Build workspace agent, capability graph, and versioned cutovers

This commit is contained in:
Timothy J. Aveni
2026-09-06 11:12:04 -07:00
parent c4d42a0ac5
commit ce793cc54f
55 changed files with 5600 additions and 2529 deletions
+43 -9
View File
@@ -2,21 +2,23 @@
import { readFile } from "node:fs/promises";
import process from "node:process";
import { compileCapabilitySource } from "./parser.js";
import { compileCapabilityResourceSource, compileCapabilitySource } from "./parser.js";
import { capabilityId } from "../capability-model/types.js";
import { compileWorkspaceRevision } from "../capability-model/validation.js";
const usage = `usage: quixos-capability-compile [--check]
const usage = `usage: quixos-capability-compile [--check] [--resource]
[--workspace-id ID] [--workspace-revision-id ID]
[--source-root-commit GIT_REV] <workspace.qx | ->
Parses, lowers, and validates a Quixos capability workspace. By default the
checked semantic workspace revision is written as JSON. --check emits no JSON.
Parses, lowers, and validates a Quixos capability workspace. --resource instead
expects one standalone interface or package document. By default the checked
semantic workspace revision or resource is written as JSON. --check emits no JSON.
The identity overrides instantiate a checked built-in/template assembly for one
real workspace; both must be supplied together.`;
const parseArgs = (args: string[]) => {
let checkOnly = false;
let resource = false;
let workspaceId: string | undefined;
let workspaceRevisionId: string | undefined;
let sourceRootCommit: string | undefined;
@@ -24,6 +26,7 @@ const parseArgs = (args: string[]) => {
for (let index = 0; index < args.length; index += 1) {
const argument = args[index]!;
if (argument === "--check") checkOnly = true;
else if (argument === "--resource") resource = true;
else if (argument === "--workspace-id") workspaceId = args[++index];
else if (argument === "--workspace-revision-id") workspaceRevisionId = args[++index];
else if (argument === "--source-root-commit") sourceRootCommit = args[++index];
@@ -31,10 +34,14 @@ const parseArgs = (args: string[]) => {
else if (argument.startsWith("-")) throw new Error(usage);
else positional.push(argument);
}
if (positional.length !== 1 || Boolean(workspaceId) !== Boolean(workspaceRevisionId)) {
if (
positional.length !== 1
|| Boolean(workspaceId) !== Boolean(workspaceRevisionId)
|| (resource && Boolean(workspaceId || workspaceRevisionId || sourceRootCommit))
) {
throw new Error(usage);
}
return { checkOnly, workspaceId, workspaceRevisionId, sourceRootCommit, fileName: positional[0]! };
return { checkOnly, resource, workspaceId, workspaceRevisionId, sourceRootCommit, fileName: positional[0]! };
};
const main = async () => {
@@ -45,6 +52,7 @@ const main = async () => {
}
const {
checkOnly,
resource,
workspaceId,
workspaceRevisionId,
sourceRootCommit,
@@ -59,9 +67,16 @@ const main = async () => {
process.stdin.on("error", reject);
})
: await readFile(fileName, "utf8");
const result = compileCapabilitySource(source, fileName);
if (!result.ok) {
for (const diagnostic of result.diagnostics) {
const reportDiagnostics = (diagnostics: readonly {
fileName: string;
line: number;
column: number;
phase: string;
code: string;
message: string;
path?: string;
}[]) => {
for (const diagnostic of diagnostics) {
const location = diagnostic.line
? `${diagnostic.fileName}:${diagnostic.line}:${diagnostic.column + 1}`
: `${diagnostic.fileName}${diagnostic.path ? `:${diagnostic.path}` : ""}`;
@@ -70,6 +85,25 @@ const main = async () => {
);
}
process.exitCode = 1;
};
if (resource) {
const result = compileCapabilityResourceSource(source, {
source: {
repository: "https://compiler.invalid/resource.git",
commit: "0000000000000000000000000000000000000000",
},
fileName,
});
if (!result.ok) {
reportDiagnostics(result.diagnostics);
return;
}
if (!checkOnly) process.stdout.write(`${JSON.stringify(result.resource, null, 2)}\n`);
return;
}
const result = compileCapabilitySource(source, fileName);
if (!result.ok) {
reportDiagnostics(result.diagnostics);
return;
}
const workspace = workspaceId && workspaceRevisionId