magenta-canon 0.1.2 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magenta-canon",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "A verifiable MCP accountability gateway for AI-agent tool calls: allows authorized calls, blocks unauthorized calls, records both, and produces cryptographic evidence anyone can verify.",
package/scripts/demo.mjs CHANGED
@@ -127,17 +127,23 @@ const capture = (cmd, args, opts = {}) =>
127
127
  const isWindows = process.platform === "win32";
128
128
  const runDriver = (configPath, env) =>
129
129
  new Promise((resolve) => {
130
- const ch = spawn("node", ["scripts/mcp-demo-drive.mjs", configPath], {
130
+ // Resolve the driver as an absolute, spawn-safe native path (cwd-independent
131
+ // and correct under the npm-cache _npx path on Windows).
132
+ const driverPath = path.join(ROOT, "scripts", "mcp-demo-drive.mjs");
133
+ const ch = spawn(process.execPath, [driverPath, configPath], {
131
134
  // `detached` only buys us a process group on POSIX; skip it on Windows.
132
- cwd: ROOT, detached: !isWindows, stdio: ["ignore", "pipe", "ignore"], env,
135
+ // Capture stderr too, so a child failure surfaces a real cause instead of
136
+ // an opaque "MCP driver failed".
137
+ cwd: ROOT, detached: !isWindows, stdio: ["ignore", "pipe", "pipe"], env,
133
138
  });
134
- let out = "";
139
+ let out = "", err = "";
135
140
  ch.stdout.on("data", (d) => (out += d));
141
+ ch.stderr.on("data", (d) => (err += d));
136
142
  const cleanup = () => killChildTree(ch);
137
- const watchdog = setTimeout(() => { cleanup(); resolve({ code: 124, out }); }, 30_000);
143
+ const watchdog = setTimeout(() => { cleanup(); resolve({ code: 124, out, err }); }, 30_000);
138
144
  ch.on("exit", (code) => {
139
145
  clearTimeout(watchdog);
140
- setTimeout(() => { cleanup(); resolve({ code: code ?? 0, out }); }, 150);
146
+ setTimeout(() => { cleanup(); resolve({ code: code ?? 0, out, err }); }, 150);
141
147
  });
142
148
  });
143
149
 
@@ -199,6 +205,15 @@ async function main() {
199
205
  // ephemeral control plane. Semantics (capabilities, tool map) are unchanged.
200
206
  const base = JSON.parse(readFileSync(path.join(ROOT, "examples/magenta-gateway.demo.config.json"), "utf8"));
201
207
  base.controlPlane = { url: URL, internalKey: INTERNAL_KEY };
208
+ // Make the downstream command absolute and launch it with the current Node
209
+ // (process.execPath) rather than a bare "node" + relative arg — cwd-independent
210
+ // and correct under the npm-cache _npx path, including on Windows.
211
+ if (base.downstream && Array.isArray(base.downstream.args)) {
212
+ base.downstream.command = process.execPath;
213
+ base.downstream.args = base.downstream.args.map((a) =>
214
+ a.endsWith(".mjs") || a.endsWith(".js") ? path.join(ROOT, a) : a,
215
+ );
216
+ }
202
217
  writeFileSync(F.config, JSON.stringify(base, null, 2));
203
218
 
204
219
  const drive = await runDriver(F.config, {
@@ -208,8 +223,8 @@ async function main() {
208
223
  MAGENTA_TSX_CLI: TSX_CLI,
209
224
  PATH: `${path.join(ROOT, "node_modules", ".bin")}${path.delimiter}${process.env.PATH}`,
210
225
  });
211
- if (drive.code !== 0) die("MCP driver failed.", drive.out);
212
- if (!/HANDSHAKE_OK/.test(drive.out)) die("MCP handshake did not complete.", drive.out);
226
+ if (drive.code !== 0) die("MCP driver failed.", [drive.out, drive.err].filter(Boolean).join("\n"));
227
+ if (!/HANDSHAKE_OK/.test(drive.out)) die("MCP handshake did not complete.", [drive.out, drive.err].filter(Boolean).join("\n"));
213
228
  for (const line of drive.out.trim().split("\n")) {
214
229
  if (!line.trim()) continue;
215
230
  const colored = line.startsWith("ALLOWED") ? grn(line)
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { spawn } from "node:child_process";
10
10
  import readline from "node:readline";
11
+ import { siblingPath } from "./win-paths.mjs";
11
12
 
12
13
  const cfg = process.argv[2];
13
14
  if (!cfg) { console.error("usage: node scripts/mcp-demo-drive.mjs <config>"); process.exit(2); }
@@ -15,7 +16,9 @@ if (!cfg) { console.error("usage: node scripts/mcp-demo-drive.mjs <config>"); pr
15
16
  // Launch the gateway. When MAGENTA_TSX_CLI is provided (by the demo orchestrator,
16
17
  // incl. the installed-package CLI) run it as `node <tsxCli> mcp-gateway.ts`;
17
18
  // otherwise fall back to `npx tsx` for the documented manual flow.
18
- const gatewayPath = new URL("./mcp-gateway.ts", import.meta.url).pathname;
19
+ // NOTE: use fileURLToPath (via siblingPath), NOT URL.pathname — the latter yields
20
+ // `/C:/…` on Windows, which tsx/node cannot open (broke step 3/7 on Windows).
21
+ const gatewayPath = siblingPath(import.meta.url, "./mcp-gateway.ts");
19
22
  const gw = process.env.MAGENTA_TSX_CLI
20
23
  ? spawn(process.execPath, [process.env.MAGENTA_TSX_CLI, gatewayPath, cfg], { stdio: ["pipe", "pipe", "inherit"] })
21
24
  : spawn("npx", ["tsx", gatewayPath, cfg], { stdio: ["pipe", "pipe", "inherit"] });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Windows-safe path helpers for the demo's child-process wiring.
3
+ *
4
+ * The MCP demo driver spawns the gateway script by resolving a sibling file from
5
+ * `import.meta.url`. The naive `new URL("./x.ts", import.meta.url).pathname`
6
+ * returns a POSIX-style string with a spurious leading slash before the drive
7
+ * letter on Windows (e.g. `/C:/Users/.../x.ts`), which Node/tsx cannot open —
8
+ * this broke `npx magenta-canon demo` at step 3/7 on Windows. `fileURLToPath`
9
+ * produces the correct native path on every platform (`C:\Users\...\x.ts` on
10
+ * Windows, `/home/.../x.ts` on POSIX).
11
+ *
12
+ * Pure; no process/verifier/witness/evidence/gateway semantics.
13
+ */
14
+ import { fileURLToPath } from "node:url";
15
+
16
+ /** Resolve a sibling file next to `metaUrl` as a spawn-safe, native absolute path. */
17
+ export function siblingPath(metaUrl, name) {
18
+ return fileURLToPath(new URL(name, metaUrl));
19
+ }