@archstone/cli 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,6 +25,9 @@ archstone serve path/to/manifest
25
25
 
26
26
  # Replay a binding's golden fixture against the live backend; detect provider drift
27
27
  archstone verify path/to/manifest
28
+
29
+ # Compile a manifest to a standalone IR artifact (archstone.ir.json by default)
30
+ archstone build path/to/manifest [--out path]
28
31
  ```
29
32
 
30
33
  A manifest directory contains `capabilities.yaml`, `*.capability.yaml`, `*.resource.yaml`,
package/dist/index.js CHANGED
@@ -1,9 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
+ import { writeFileSync } from "fs";
5
+ import { resolve } from "path";
6
+ import { createServer } from "http";
4
7
  import { load } from "@archstone/schema";
5
8
  import { validateSemantics, compile } from "@archstone/compiler";
6
- import { Registry, serveStdio, runVerify } from "@archstone/runtime";
9
+ import { Registry, buildRegistry, serveStdio, runVerify } from "@archstone/runtime";
10
+ import { createHttpHandler } from "@archstone/runtime/http";
7
11
  function runApply(dir) {
8
12
  const res = load(dir);
9
13
  console.log(`
@@ -44,17 +48,85 @@ archstone apply ${dir}
44
48
  console.log("");
45
49
  process.exit(ok ? 0 : 1);
46
50
  }
51
+ function runBuild(dir, outPath) {
52
+ const res = load(dir);
53
+ const diags = validateSemantics(res);
54
+ const errors = diags.filter((d) => d.severity === "error");
55
+ const ok = res.ok && errors.length === 0;
56
+ if (!ok) {
57
+ console.error(`archstone build ${dir}: manifest invalid \u2014 run 'archstone apply ${dir}' for details`);
58
+ for (const i of res.issues) console.error(` - ${i.file}: ${i.message}`);
59
+ for (const d of errors) console.error(` - ${d.message}`);
60
+ process.exit(1);
61
+ }
62
+ const ir = compile(res);
63
+ const stripped = { ...ir, tools: ir.tools.map(({ contract: _contract, ...t }) => t) };
64
+ const outFile = resolve(process.cwd(), outPath ?? "archstone.ir.json");
65
+ writeFileSync(outFile, `${JSON.stringify(stripped, null, 2)}
66
+ `);
67
+ console.log(`archstone build ${dir} \u2192 ${outFile} (${stripped.tools.length} tool(s))`);
68
+ process.exit(0);
69
+ }
70
+ function runServeHttp(dir, port, token) {
71
+ if (!token) {
72
+ console.error(
73
+ "archstone serve --http: bearer token required \u2014 set ARCHSTONE_HTTP_TOKEN or pass --token <value>"
74
+ );
75
+ process.exit(1);
76
+ }
77
+ const built = buildRegistry(dir);
78
+ if (!built.ok || !built.registry) {
79
+ console.error(`archstone: cannot serve '${dir}' \u2014 manifest invalid:`);
80
+ for (const i of built.issues) console.error(` - ${i.file}: ${i.message}`);
81
+ for (const d of built.diagnostics.filter((x) => x.severity === "error")) console.error(` - ${d.message}`);
82
+ process.exit(1);
83
+ }
84
+ const handler = createHttpHandler(built.registry, { bearerToken: token });
85
+ const server = createServer((req, res) => {
86
+ void handleHttpRequest(handler, req, res);
87
+ });
88
+ server.listen(port, () => {
89
+ console.error(`archstone: serving MCP over HTTP on http://localhost:${port}/ (bearer-token gated)`);
90
+ });
91
+ }
92
+ async function handleHttpRequest(handler, req, res) {
93
+ const chunks = [];
94
+ for await (const chunk of req) chunks.push(chunk);
95
+ const headers = new Headers();
96
+ for (const [key, value] of Object.entries(req.headers)) {
97
+ if (value !== void 0) headers.set(key, Array.isArray(value) ? value.join(", ") : value);
98
+ }
99
+ const hasBody = req.method !== "GET" && req.method !== "HEAD" && chunks.length > 0;
100
+ const request = new Request(`http://${req.headers.host ?? "localhost"}${req.url ?? "/"}`, {
101
+ method: req.method ?? "GET",
102
+ headers,
103
+ body: hasBody ? Buffer.concat(chunks) : void 0
104
+ });
105
+ const response = await handler(request);
106
+ res.statusCode = response.status;
107
+ response.headers.forEach((value, key) => res.setHeader(key, value));
108
+ res.end(response.body ? Buffer.from(await response.arrayBuffer()) : void 0);
109
+ }
47
110
  var HEALTH_ICON = { green: "\u{1F7E2}", yellow: "\u{1F7E1}", red: "\u{1F534}" };
48
- async function runVerifyCmd(dir) {
111
+ async function runVerifyCmd(dir, json) {
49
112
  const res = load(dir);
50
113
  const diags = validateSemantics(res);
51
- const ok = res.ok && !diags.some((d) => d.severity === "error");
114
+ const errors = diags.filter((d) => d.severity === "error");
115
+ const ok = res.ok && errors.length === 0;
52
116
  if (!ok) {
53
- console.error(`archstone verify ${dir}: manifest invalid \u2014 run 'archstone apply ${dir}' for details`);
117
+ if (json) {
118
+ console.log(JSON.stringify({ error: "manifest_invalid", issues: res.issues, errors }));
119
+ } else {
120
+ console.error(`archstone verify ${dir}: manifest invalid \u2014 run 'archstone apply ${dir}' for details`);
121
+ }
54
122
  process.exit(2);
55
123
  }
56
124
  const registry = new Registry(compile(res));
57
125
  const reports = await runVerify(registry.listCapabilities(), dir, registry.ir.resources);
126
+ if (json) {
127
+ console.log(JSON.stringify({ results: reports }));
128
+ process.exit(reports.some((r) => r.status === "red") ? 1 : 0);
129
+ }
58
130
  console.log(`
59
131
  archstone verify ${dir}
60
132
  `);
@@ -68,21 +140,49 @@ archstone verify ${dir}
68
140
  console.log("");
69
141
  process.exit(reports.some((r) => r.status === "red") ? 1 : 0);
70
142
  }
143
+ function flagArg(argv, name) {
144
+ const idx = argv.indexOf(name);
145
+ return { value: idx !== -1 ? argv[idx + 1] : void 0, idx };
146
+ }
71
147
  async function main() {
72
- const [cmd, dir] = process.argv.slice(2);
148
+ const argv = process.argv.slice(2);
149
+ const json = argv.includes("--json");
150
+ const http = argv.includes("--http");
151
+ const out = flagArg(argv, "--out");
152
+ const port = flagArg(argv, "--port");
153
+ const token = flagArg(argv, "--token");
154
+ const consumed = /* @__PURE__ */ new Set();
155
+ for (const f of [out, port, token]) {
156
+ if (f.idx !== -1) {
157
+ consumed.add(f.idx);
158
+ consumed.add(f.idx + 1);
159
+ }
160
+ }
161
+ const positional = argv.filter((a, i) => !consumed.has(i) && a !== "--json" && a !== "--http");
162
+ const [cmd, dir] = positional;
73
163
  if (cmd === "apply" && dir) {
74
164
  runApply(dir);
75
165
  return;
76
166
  }
167
+ if (cmd === "serve" && dir && http) {
168
+ runServeHttp(dir, Number(port.value ?? 8787), token.value ?? process.env.ARCHSTONE_HTTP_TOKEN);
169
+ return;
170
+ }
77
171
  if (cmd === "serve" && dir) {
78
172
  await serveStdio(dir);
79
173
  return;
80
174
  }
81
175
  if (cmd === "verify" && dir) {
82
- await runVerifyCmd(dir);
176
+ await runVerifyCmd(dir, json);
177
+ return;
178
+ }
179
+ if (cmd === "build" && dir) {
180
+ runBuild(dir, out.value);
83
181
  return;
84
182
  }
85
- console.error("usage: archstone <apply|serve|verify> <manifest-dir>");
183
+ console.error(
184
+ "usage: archstone <apply|serve|verify|build> <manifest-dir> [--json] [--out path]\n archstone serve --http <manifest-dir> [--port <n>] [--token <value>]\n bearer token: --token <value>, or the ARCHSTONE_HTTP_TOKEN env var (required \u2014 never serves open)"
185
+ );
86
186
  process.exit(2);
87
187
  }
88
188
  main();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n// @archstone/cli — `archstone apply` (#1) + `archstone serve` (#7) + `archstone verify` (#18-20)\n//\n// apply: parse → shape-validate (#2) → semantic-validate (#3) → compile IR (#4)\n// → index Registry (#5), and REPORT (human output, exits).\n// serve: build the registry and expose it as an MCP server over stdio (#7),\n// so Claude/Cursor/ChatGPT can discover and invoke the tools. Blocks.\n// verify: replay each bound capability's golden fixture against the LIVE backend\n// and report a per-binding health status (ADD-18). The only command that\n// makes a network call outside a real MCP invocation — on demand, never\n// scheduled by Archstone itself (wire it into your own CI/cron).\n\nimport { load } from \"@archstone/schema\";\nimport { validateSemantics, compile } from \"@archstone/compiler\";\nimport { Registry, serveStdio, runVerify, type HealthStatus } from \"@archstone/runtime\";\n\nfunction runApply(dir: string): void {\n const res = load(dir);\n console.log(`\\narchstone apply ${dir}\\n`);\n\n if (res.capabilities) {\n const c = res.capabilities;\n console.log(` company ${c.company.name ?? c.company.id} (${c.company.id})`);\n console.log(` providers ${c.providers.join(\", \")}`);\n console.log(` declared ${c.capabilities.length} capabilities`);\n }\n console.log(` loaded ${res.capabilityDocs.length} capability docs, ${res.bindings.length} bindings`);\n for (const d of res.capabilityDocs) {\n console.log(` ✓ ${d.capability.id} [${d.capability.effect}] → ${d.capability.provider ?? \"?\"}`);\n }\n\n // Shape (schema) issues from #2 — \"valid shapes\" is not \"deployable\".\n if (res.issues.length > 0) {\n console.log(`\\n ✗ ${res.issues.length} shape issue(s):`);\n for (const i of res.issues) console.log(` - ${i.file}: ${i.message}`);\n } else {\n console.log(`\\n ✓ shapes valid`);\n }\n\n // Semantic pass (#3) — cross-file resolution; errors block, warnings inform.\n const diags = validateSemantics(res);\n const errors = diags.filter((d) => d.severity === \"error\");\n const warnings = diags.filter((d) => d.severity === \"warning\");\n console.log(` semantic ${errors.length} error(s), ${warnings.length} warning(s)`);\n for (const d of errors) console.log(` ✗ ${d.message}`);\n for (const d of warnings) console.log(` ⚠ ${d.message}`);\n\n const ok = res.ok && errors.length === 0;\n\n // Compile to IR (#4) + index into the Registry (#5) — only when valid enough to emit.\n if (ok) {\n const registry = new Registry(compile(res));\n const invocable = registry.listCapabilities().filter((t) => t.connector).length;\n console.log(` registry IR v${registry.ir.version} — ${registry.size} capabilities, ${invocable} invocable (bound)`);\n console.log(`\\n → run 'archstone serve ${dir}' to expose ${invocable} tool(s) to an AI agent over MCP`);\n }\n\n console.log(\"\");\n process.exit(ok ? 0 : 1);\n}\n\nconst HEALTH_ICON: Record<HealthStatus, string> = { green: \"🟢\", yellow: \"🟡\", red: \"🔴\" };\n\nasync function runVerifyCmd(dir: string): Promise<void> {\n const res = load(dir);\n const diags = validateSemantics(res);\n const ok = res.ok && !diags.some((d) => d.severity === \"error\");\n if (!ok) {\n console.error(`archstone verify ${dir}: manifest invalid — run 'archstone apply ${dir}' for details`);\n process.exit(2);\n }\n\n const registry = new Registry(compile(res));\n const reports = await runVerify(registry.listCapabilities(), dir, registry.ir.resources);\n\n console.log(`\\narchstone verify ${dir}\\n`);\n if (reports.length === 0) {\n console.log(\" (no bindings declare a contract: — nothing to verify)\\n\");\n process.exit(0);\n }\n for (const r of reports) {\n console.log(` ${HEALTH_ICON[r.status]} ${r.capabilityId} — ${r.detail}`);\n }\n console.log(\"\");\n process.exit(reports.some((r) => r.status === \"red\") ? 1 : 0);\n}\n\nasync function main(): Promise<void> {\n const [cmd, dir] = process.argv.slice(2);\n\n if (cmd === \"apply\" && dir) {\n runApply(dir);\n return;\n }\n if (cmd === \"serve\" && dir) {\n await serveStdio(dir); // blocks on the stdio transport\n return;\n }\n if (cmd === \"verify\" && dir) {\n await runVerifyCmd(dir);\n return;\n }\n\n console.error(\"usage: archstone <apply|serve|verify> <manifest-dir>\");\n process.exit(2);\n}\n\nmain();\n"],"mappings":";;;AAYA,SAAS,YAAY;AACrB,SAAS,mBAAmB,eAAe;AAC3C,SAAS,UAAU,YAAY,iBAAoC;AAEnE,SAAS,SAAS,KAAmB;AACnC,QAAM,MAAM,KAAK,GAAG;AACpB,UAAQ,IAAI;AAAA,kBAAqB,GAAG;AAAA,CAAI;AAExC,MAAI,IAAI,cAAc;AACpB,UAAM,IAAI,IAAI;AACd,YAAQ,IAAI,gBAAgB,EAAE,QAAQ,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG;AAC9E,YAAQ,IAAI,gBAAgB,EAAE,UAAU,KAAK,IAAI,CAAC,EAAE;AACpD,YAAQ,IAAI,gBAAgB,EAAE,aAAa,MAAM,eAAe;AAAA,EAClE;AACA,UAAQ,IAAI,gBAAgB,IAAI,eAAe,MAAM,qBAAqB,IAAI,SAAS,MAAM,WAAW;AACxG,aAAW,KAAK,IAAI,gBAAgB;AAClC,YAAQ,IAAI,cAAS,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,MAAM,YAAO,EAAE,WAAW,YAAY,GAAG,EAAE;AAAA,EACpG;AAGA,MAAI,IAAI,OAAO,SAAS,GAAG;AACzB,YAAQ,IAAI;AAAA,WAAS,IAAI,OAAO,MAAM,kBAAkB;AACxD,eAAW,KAAK,IAAI,OAAQ,SAAQ,IAAI,SAAS,EAAE,IAAI,KAAK,EAAE,OAAO,EAAE;AAAA,EACzE,OAAO;AACL,YAAQ,IAAI;AAAA,sBAAoB;AAAA,EAClC;AAGA,QAAM,QAAQ,kBAAkB,GAAG;AACnC,QAAM,SAAS,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO;AACzD,QAAM,WAAW,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS;AAC7D,UAAQ,IAAI,gBAAgB,OAAO,MAAM,cAAc,SAAS,MAAM,aAAa;AACnF,aAAW,KAAK,OAAQ,SAAQ,IAAI,cAAS,EAAE,OAAO,EAAE;AACxD,aAAW,KAAK,SAAU,SAAQ,IAAI,cAAS,EAAE,OAAO,EAAE;AAE1D,QAAM,KAAK,IAAI,MAAM,OAAO,WAAW;AAGvC,MAAI,IAAI;AACN,UAAM,WAAW,IAAI,SAAS,QAAQ,GAAG,CAAC;AAC1C,UAAM,YAAY,SAAS,iBAAiB,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE;AACzE,YAAQ,IAAI,oBAAoB,SAAS,GAAG,OAAO,WAAM,SAAS,IAAI,kBAAkB,SAAS,oBAAoB;AACrH,YAAQ,IAAI;AAAA,gCAA8B,GAAG,eAAe,SAAS,kCAAkC;AAAA,EACzG;AAEA,UAAQ,IAAI,EAAE;AACd,UAAQ,KAAK,KAAK,IAAI,CAAC;AACzB;AAEA,IAAM,cAA4C,EAAE,OAAO,aAAM,QAAQ,aAAM,KAAK,YAAK;AAEzF,eAAe,aAAa,KAA4B;AACtD,QAAM,MAAM,KAAK,GAAG;AACpB,QAAM,QAAQ,kBAAkB,GAAG;AACnC,QAAM,KAAK,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,MAAM,EAAE,aAAa,OAAO;AAC9D,MAAI,CAAC,IAAI;AACP,YAAQ,MAAM,oBAAoB,GAAG,kDAA6C,GAAG,eAAe;AACpG,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,WAAW,IAAI,SAAS,QAAQ,GAAG,CAAC;AAC1C,QAAM,UAAU,MAAM,UAAU,SAAS,iBAAiB,GAAG,KAAK,SAAS,GAAG,SAAS;AAEvF,UAAQ,IAAI;AAAA,mBAAsB,GAAG;AAAA,CAAI;AACzC,MAAI,QAAQ,WAAW,GAAG;AACxB,YAAQ,IAAI,gEAA2D;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,aAAW,KAAK,SAAS;AACvB,YAAQ,IAAI,KAAK,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,YAAY,WAAM,EAAE,MAAM,EAAE;AAAA,EAC1E;AACA,UAAQ,IAAI,EAAE;AACd,UAAQ,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,IAAI,CAAC;AAC9D;AAEA,eAAe,OAAsB;AACnC,QAAM,CAAC,KAAK,GAAG,IAAI,QAAQ,KAAK,MAAM,CAAC;AAEvC,MAAI,QAAQ,WAAW,KAAK;AAC1B,aAAS,GAAG;AACZ;AAAA,EACF;AACA,MAAI,QAAQ,WAAW,KAAK;AAC1B,UAAM,WAAW,GAAG;AACpB;AAAA,EACF;AACA,MAAI,QAAQ,YAAY,KAAK;AAC3B,UAAM,aAAa,GAAG;AACtB;AAAA,EACF;AAEA,UAAQ,MAAM,sDAAsD;AACpE,UAAQ,KAAK,CAAC;AAChB;AAEA,KAAK;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n// @archstone/cli — `archstone apply` (#1) + `archstone serve` (#7, + `--http` ADD-0008 #29)\n// + `archstone verify` (#18-20) + `archstone build` (ADD-0008 #27)\n//\n// apply: parse → shape-validate (#2) → semantic-validate (#3) → compile IR (#4)\n// → index Registry (#5), and REPORT (human output, exits).\n// serve: build the registry and expose it as an MCP server over stdio (#7),\n// so Claude/Cursor/ChatGPT can discover and invoke the tools. Blocks.\n// serve --http: same registry, served over real Streamable-HTTP instead of stdio —\n// `@archstone/runtime/http`'s createHttpHandler (Web-standard Request/Response,\n// bearer-token gated, shared with @archstone/agent/mcp's mcpHandler(), ADD-0008 D-3)\n// behind a thin Node-http adapter. Blocks.\n// verify: replay each bound capability's golden fixture against the LIVE backend\n// and report a per-binding health status (ADD-18). The only command that\n// makes a network call outside a real MCP invocation — on demand, never\n// scheduled by Archstone itself (wire it into your own CI/cron).\n// build: run the same compile pipeline as `apply`, strip each tool's `contract`\n// (D-8 — the fingerprint/golden-fixture path is meaningless once the fixture\n// file isn't shipping), and write the IR as a standalone JSON artifact —\n// the substrate `@archstone/agent`'s `fromIR()` will consume (RFC-0008).\n\nimport { writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { load } from \"@archstone/schema\";\nimport { validateSemantics, compile, type IR } from \"@archstone/compiler\";\nimport { Registry, buildRegistry, serveStdio, runVerify, type HealthStatus } from \"@archstone/runtime\";\nimport { createHttpHandler } from \"@archstone/runtime/http\";\n\nfunction runApply(dir: string): void {\n const res = load(dir);\n console.log(`\\narchstone apply ${dir}\\n`);\n\n if (res.capabilities) {\n const c = res.capabilities;\n console.log(` company ${c.company.name ?? c.company.id} (${c.company.id})`);\n console.log(` providers ${c.providers.join(\", \")}`);\n console.log(` declared ${c.capabilities.length} capabilities`);\n }\n console.log(` loaded ${res.capabilityDocs.length} capability docs, ${res.bindings.length} bindings`);\n for (const d of res.capabilityDocs) {\n console.log(` ✓ ${d.capability.id} [${d.capability.effect}] → ${d.capability.provider ?? \"?\"}`);\n }\n\n // Shape (schema) issues from #2 — \"valid shapes\" is not \"deployable\".\n if (res.issues.length > 0) {\n console.log(`\\n ✗ ${res.issues.length} shape issue(s):`);\n for (const i of res.issues) console.log(` - ${i.file}: ${i.message}`);\n } else {\n console.log(`\\n ✓ shapes valid`);\n }\n\n // Semantic pass (#3) — cross-file resolution; errors block, warnings inform.\n const diags = validateSemantics(res);\n const errors = diags.filter((d) => d.severity === \"error\");\n const warnings = diags.filter((d) => d.severity === \"warning\");\n console.log(` semantic ${errors.length} error(s), ${warnings.length} warning(s)`);\n for (const d of errors) console.log(` ✗ ${d.message}`);\n for (const d of warnings) console.log(` ⚠ ${d.message}`);\n\n const ok = res.ok && errors.length === 0;\n\n // Compile to IR (#4) + index into the Registry (#5) — only when valid enough to emit.\n if (ok) {\n const registry = new Registry(compile(res));\n const invocable = registry.listCapabilities().filter((t) => t.connector).length;\n console.log(` registry IR v${registry.ir.version} — ${registry.size} capabilities, ${invocable} invocable (bound)`);\n console.log(`\\n → run 'archstone serve ${dir}' to expose ${invocable} tool(s) to an AI agent over MCP`);\n }\n\n console.log(\"\");\n process.exit(ok ? 0 : 1);\n}\n\nfunction runBuild(dir: string, outPath: string | undefined): void {\n const res = load(dir);\n const diags = validateSemantics(res);\n const errors = diags.filter((d) => d.severity === \"error\");\n const ok = res.ok && errors.length === 0;\n\n if (!ok) {\n console.error(`archstone build ${dir}: manifest invalid — run 'archstone apply ${dir}' for details`);\n for (const i of res.issues) console.error(` - ${i.file}: ${i.message}`);\n for (const d of errors) console.error(` - ${d.message}`);\n process.exit(1);\n }\n\n const ir = compile(res);\n // D-8: the artifact ships with no code alongside it — the fingerprint + golden-fixture\n // path have no meaning without the fixture file / `archstone verify`, so strip `contract`\n // from every tool before writing.\n const stripped: IR = { ...ir, tools: ir.tools.map(({ contract: _contract, ...t }) => t) };\n\n const outFile = resolve(process.cwd(), outPath ?? \"archstone.ir.json\");\n writeFileSync(outFile, `${JSON.stringify(stripped, null, 2)}\\n`);\n console.log(`archstone build ${dir} → ${outFile} (${stripped.tools.length} tool(s))`);\n process.exit(0);\n}\n\nfunction runServeHttp(dir: string, port: number, token: string | undefined): void {\n // Rule #7 / ADD-0008 R-5: fail closed before touching the network — a missing token is a\n // startup error, never a silently-open endpoint. `--token` wins over the env var if both\n // are set; createHttpHandler itself would also throw on empty, but checking here first\n // gives a CLI-appropriate error message instead of an uncaught exception.\n if (!token) {\n console.error(\n \"archstone serve --http: bearer token required — set ARCHSTONE_HTTP_TOKEN or pass --token <value>\",\n );\n process.exit(1);\n }\n\n const built = buildRegistry(dir);\n if (!built.ok || !built.registry) {\n console.error(`archstone: cannot serve '${dir}' — manifest invalid:`);\n for (const i of built.issues) console.error(` - ${i.file}: ${i.message}`);\n for (const d of built.diagnostics.filter((x) => x.severity === \"error\")) console.error(` - ${d.message}`);\n process.exit(1);\n }\n\n const handler = createHttpHandler(built.registry, { bearerToken: token });\n const server = createServer((req, res) => {\n void handleHttpRequest(handler, req, res);\n });\n server.listen(port, () => {\n console.error(`archstone: serving MCP over HTTP on http://localhost:${port}/ (bearer-token gated)`);\n });\n}\n\n// D-3's \"~20-line wrapper\": Node's http.IncomingMessage/ServerResponse <-> Web-standard\n// Request/Response, so createHttpHandler (already Web-standard, shared with\n// @archstone/agent/mcp's mcpHandler()) can serve real Node HTTP traffic without a second\n// transport implementation. CLI-level plumbing only — HTTP itself still lives in\n// providers/rest for business-backend calls; this adapter never touches a backend.\nasync function handleHttpRequest(\n handler: (request: Request) => Promise<Response>,\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> {\n const chunks: Buffer[] = [];\n for await (const chunk of req) chunks.push(chunk as Buffer);\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value !== undefined) headers.set(key, Array.isArray(value) ? value.join(\", \") : value);\n }\n const hasBody = req.method !== \"GET\" && req.method !== \"HEAD\" && chunks.length > 0;\n const request = new Request(`http://${req.headers.host ?? \"localhost\"}${req.url ?? \"/\"}`, {\n method: req.method ?? \"GET\",\n headers,\n body: hasBody ? Buffer.concat(chunks) : undefined,\n });\n\n const response = await handler(request);\n res.statusCode = response.status;\n response.headers.forEach((value, key) => res.setHeader(key, value));\n res.end(response.body ? Buffer.from(await response.arrayBuffer()) : undefined);\n}\n\nconst HEALTH_ICON: Record<HealthStatus, string> = { green: \"🟢\", yellow: \"🟡\", red: \"🔴\" };\n\nasync function runVerifyCmd(dir: string, json: boolean): Promise<void> {\n const res = load(dir);\n const diags = validateSemantics(res);\n const errors = diags.filter((d) => d.severity === \"error\");\n const ok = res.ok && errors.length === 0;\n if (!ok) {\n if (json) {\n // ADD-20 D-2: this shape is strictly disjoint from the `{results}` shape below —\n // never add a shared \"envelope\" field (e.g. `ok`) to either.\n console.log(JSON.stringify({ error: \"manifest_invalid\", issues: res.issues, errors }));\n } else {\n console.error(`archstone verify ${dir}: manifest invalid — run 'archstone apply ${dir}' for details`);\n }\n process.exit(2);\n }\n\n const registry = new Registry(compile(res));\n const reports = await runVerify(registry.listCapabilities(), dir, registry.ir.resources);\n\n if (json) {\n // ADD-20 D-2: strictly disjoint from the `{error, issues, errors}` shape above.\n console.log(JSON.stringify({ results: reports }));\n process.exit(reports.some((r) => r.status === \"red\") ? 1 : 0);\n }\n\n console.log(`\\narchstone verify ${dir}\\n`);\n if (reports.length === 0) {\n console.log(\" (no bindings declare a contract: — nothing to verify)\\n\");\n process.exit(0);\n }\n for (const r of reports) {\n console.log(` ${HEALTH_ICON[r.status]} ${r.capabilityId} — ${r.detail}`);\n }\n console.log(\"\");\n process.exit(reports.some((r) => r.status === \"red\") ? 1 : 0);\n}\n\n/** Value of a `--name value` flag pair, plus the index it was found at (-1 if absent) —\n * used both to read the value and to exclude both tokens from the positional args. */\nfunction flagArg(argv: string[], name: string): { value?: string; idx: number } {\n const idx = argv.indexOf(name);\n return { value: idx !== -1 ? argv[idx + 1] : undefined, idx };\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n const json = argv.includes(\"--json\");\n const http = argv.includes(\"--http\");\n const out = flagArg(argv, \"--out\");\n const port = flagArg(argv, \"--port\");\n const token = flagArg(argv, \"--token\");\n\n const consumed = new Set<number>();\n for (const f of [out, port, token]) {\n if (f.idx !== -1) {\n consumed.add(f.idx);\n consumed.add(f.idx + 1);\n }\n }\n const positional = argv.filter((a, i) => !consumed.has(i) && a !== \"--json\" && a !== \"--http\");\n const [cmd, dir] = positional;\n\n if (cmd === \"apply\" && dir) {\n runApply(dir);\n return;\n }\n if (cmd === \"serve\" && dir && http) {\n // Bearer token: --token wins over ARCHSTONE_HTTP_TOKEN if both are set (Rule #7 —\n // required, never defaults open).\n runServeHttp(dir, Number(port.value ?? 8787), token.value ?? process.env.ARCHSTONE_HTTP_TOKEN);\n return; // blocks on the HTTP server\n }\n if (cmd === \"serve\" && dir) {\n await serveStdio(dir); // blocks on the stdio transport\n return;\n }\n if (cmd === \"verify\" && dir) {\n await runVerifyCmd(dir, json);\n return;\n }\n if (cmd === \"build\" && dir) {\n runBuild(dir, out.value);\n return;\n }\n\n console.error(\n \"usage: archstone <apply|serve|verify|build> <manifest-dir> [--json] [--out path]\\n\" +\n \" archstone serve --http <manifest-dir> [--port <n>] [--token <value>]\\n\" +\n \" bearer token: --token <value>, or the ARCHSTONE_HTTP_TOKEN env var (required — never serves open)\",\n );\n process.exit(2);\n}\n\nmain();\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archstone/cli",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "CLI (#1): archstone apply — wires the pipeline (parse -> validate -> IR -> runtime).",
@@ -22,9 +22,9 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@archstone/compiler": "0.2.2",
26
- "@archstone/runtime": "0.2.2",
27
- "@archstone/schema": "0.2.2"
25
+ "@archstone/compiler": "0.3.0",
26
+ "@archstone/runtime": "0.3.0",
27
+ "@archstone/schema": "0.3.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "tsup": "^8.5.1"