158 lines
4.7 KiB
JavaScript
158 lines
4.7 KiB
JavaScript
import { constants as fsConstants } from "node:fs";
|
|
import { access } from "node:fs/promises";
|
|
import path from "node:path";
|
|
|
|
export const BUILDABLE_IMPLEMENTATION_STATES = Object.freeze([
|
|
"repo-entrypoint",
|
|
"static-web-wrapper",
|
|
"repo-bundle"
|
|
]);
|
|
|
|
const buildableImplementationStates = new Set(BUILDABLE_IMPLEMENTATION_STATES);
|
|
|
|
async function pathExists(repoRoot, relativePath) {
|
|
try {
|
|
await access(path.join(repoRoot, relativePath), fsConstants.R_OK);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function publishEnabledForService(service) {
|
|
return service.sourceState !== "intentionally-disabled" &&
|
|
buildableImplementationStates.has(service.implementationState);
|
|
}
|
|
|
|
export function notPublishedReasonForService(service) {
|
|
if (publishEnabledForService(service)) {
|
|
return "publish_not_run";
|
|
}
|
|
if (service.sourceState === "intentionally-disabled") {
|
|
return "disabled_intentionally";
|
|
}
|
|
if (service.implementationState === "missing-runtime-entrypoint") {
|
|
return "disabled_missing_runtime_entrypoint";
|
|
}
|
|
if (service.implementationState === "library-only") {
|
|
return "disabled_missing_executable_entrypoint";
|
|
}
|
|
return `disabled_${service.implementationState}`;
|
|
}
|
|
|
|
export function servicePublishPolicy(service) {
|
|
const publishEnabled = publishEnabledForService(service);
|
|
return {
|
|
publishEnabled,
|
|
artifactRequired: publishEnabled,
|
|
artifactScope: publishEnabled ? "required" : "disabled",
|
|
notPublishedReason: publishEnabled ? "publish_not_run" : notPublishedReasonForService(service)
|
|
};
|
|
}
|
|
|
|
export function withServicePublishPolicy(service) {
|
|
return {
|
|
...service,
|
|
...servicePublishPolicy(service)
|
|
};
|
|
}
|
|
|
|
function serviceSourceState(catalog, serviceId) {
|
|
return catalog?.services?.find((service) => service.serviceId === serviceId)?.sourceState ?? "source-present";
|
|
}
|
|
|
|
export async function resolveDevArtifactService(repoRoot, serviceId, catalog = null) {
|
|
const sourceState = serviceSourceState(catalog, serviceId);
|
|
const tsCmdPath = `cmd/${serviceId}/main.ts`;
|
|
if (await pathExists(repoRoot, tsCmdPath)) {
|
|
return withServicePublishPolicy({
|
|
serviceId,
|
|
runtimeKind: "bun-command",
|
|
entrypoint: tsCmdPath,
|
|
implementationState: "repo-entrypoint",
|
|
sourceState
|
|
});
|
|
}
|
|
|
|
const cmdPath = `cmd/${serviceId}/main.mjs`;
|
|
if (await pathExists(repoRoot, cmdPath)) {
|
|
return withServicePublishPolicy({
|
|
serviceId,
|
|
runtimeKind: "node-command",
|
|
entrypoint: cmdPath,
|
|
implementationState: "repo-entrypoint",
|
|
sourceState
|
|
});
|
|
}
|
|
|
|
if (serviceId === "hwlab-cloud-web") {
|
|
return withServicePublishPolicy({
|
|
serviceId,
|
|
runtimeKind: "cloud-web",
|
|
entrypoint: "web/hwlab-cloud-web/index.html",
|
|
implementationState: "static-web-wrapper",
|
|
sourceState
|
|
});
|
|
}
|
|
|
|
if (serviceId === "hwlab-agent-skills") {
|
|
return withServicePublishPolicy({
|
|
serviceId,
|
|
runtimeKind: "skills-bundle",
|
|
entrypoint: "skills/hwlab-agent-runtime/SKILL.md",
|
|
implementationState: "repo-bundle",
|
|
sourceState
|
|
});
|
|
}
|
|
|
|
return withServicePublishPolicy({
|
|
serviceId,
|
|
runtimeKind: "health-placeholder",
|
|
entrypoint: null,
|
|
implementationState: "missing-runtime-entrypoint",
|
|
sourceState
|
|
});
|
|
}
|
|
|
|
export async function resolveDevArtifactServices(repoRoot, serviceIds, frozenServiceIds, catalog = null) {
|
|
const unknown = serviceIds.filter((serviceId) => !frozenServiceIds.includes(serviceId));
|
|
if (unknown.length > 0) {
|
|
throw new Error(`unknown service IDs: ${unknown.join(", ")}`);
|
|
}
|
|
|
|
const services = [];
|
|
for (const serviceId of serviceIds) {
|
|
services.push(await resolveDevArtifactService(repoRoot, serviceId, catalog));
|
|
}
|
|
return services;
|
|
}
|
|
|
|
export function serviceInventoryFromServices(services) {
|
|
const requiredServiceIds = services
|
|
.filter((service) => service.artifactRequired)
|
|
.map((service) => service.serviceId);
|
|
const disabledServiceIds = services
|
|
.filter((service) => !service.artifactRequired)
|
|
.map((service) => service.serviceId);
|
|
|
|
return {
|
|
version: "v2",
|
|
serviceCount: services.length,
|
|
requiredServiceCount: requiredServiceIds.length,
|
|
disabledServiceCount: disabledServiceIds.length,
|
|
requiredServiceIds,
|
|
disabledServiceIds,
|
|
services: services.map((service) => ({
|
|
serviceId: service.serviceId,
|
|
publishEnabled: service.publishEnabled,
|
|
artifactRequired: service.artifactRequired,
|
|
artifactScope: service.artifactScope,
|
|
runtimeKind: service.runtimeKind,
|
|
implementationState: service.implementationState,
|
|
sourceState: service.sourceState,
|
|
entrypoint: service.entrypoint,
|
|
disabledReason: service.artifactRequired ? null : service.notPublishedReason
|
|
}))
|
|
};
|
|
}
|