diff --git a/.env.example b/.env.example index 329091edfa..245533afd1 100644 --- a/.env.example +++ b/.env.example @@ -133,7 +133,7 @@ GITEA_ALLOWED_USERS= # GITEA_BOT_MENTION=archon # Server -PORT=3000 +# PORT=3090 # Default: 3090. Uncomment to override — must match between server and Vite proxy. # HOST=0.0.0.0 # Bind address (default: 0.0.0.0). Set to 127.0.0.1 to restrict to localhost only. # Cloud Deployment (for --profile cloud with Caddy reverse proxy) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d541d13da..490db677e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **Cherry-pick batch 2 from upstream (10 commits).** Selective Tier 1 picks from the upstream delta: + - `0ec74410` — Bumped `hono` to `^4.12.16` and added `@hono/node-server` `^1.19.13` override (closes upstream #1484). + - `0afbeb30` — Bumped `@anthropic-ai/claude-agent-sdk` to `0.2.121` and `@openai/codex-sdk` to `0.125.0`. Pi packages skipped (fork doesn't use Pi). + - `cbcca8c1` — Orchestrator clears stale session ID on `error_during_execution` instead of persisting the failed session ID, preventing infinite failure loops after Claude session expiry (closes upstream #1280). + - `0c5d7b12` — Orchestrator now creates `~/.archon/workspaces` before AI provider spawn so fresh-install ENOENT no longer surfaces as an incorrect "Claude binary not found" error. + - `45682bd2` — Claude provider's `hasExplicitTokens` uses `||` instead of `??` so empty-string env vars are treated as missing (upstream #1028). + - `4885ee64` — `CLAUDE_BIN_PATH` is now honored in dev mode (relevant for libc-mismatch hosts; upstream #1481). + - `ff901115` — Claude provider stops passing `--no-env-file` to the native binary in dev mode (the flag is Bun-only; upstream #1461). + - `7d067738` — CLI lazy-imports bundled skill files so non-setup commands don't crash on missing source (upstream #1394). + - `d89bc767` — Aligned PORT default to `3090` across `.env.example`, setup wizard, and JSDoc (upstream #1271). + - `301a139e` — Split `connection.test.ts` into its own batch in `@archon/core` test script to avoid mock pollution that caused `getDatabaseType()` tests to see leaked `DATABASE_URL` (upstream #1269). Also reapplied to preserve fork-only batches (`workflow-analytics`, `cron-parser`, `knowledge-writer`). + - **Bumped transitive `axios` to `^1.15.0` via root `overrides` to clear CVE-2025-62718** (NO_PROXY bypass via hostname normalization → potential SSRF). Archon pulls `axios` transitively through `@slack/bolt` and `@slack/web-api`; both semver ranges (`^1.12.0` and `^1.13.5`) accept the override cleanly, so no API surface changes. Credits @stefans71 for identifying and reporting the vulnerability in #1153. Closes #1053. - **Stale workspace symlink no longer reported as "not in a git repository" by the CLI.** When `archon workflow run` (or `--resume`) is invoked from a valid git repo whose `~/.archon/workspaces///source` symlink points somewhere else (common after moving/renaming the checkout), auto-registration fails but the repo is fine. Previously both the worktree-creation and resume paths fell through to the generic `Cannot create worktree: not in a git repository` / `Cannot resume: Not in a git repository` errors — a lie that sent users down the wrong diagnostic path. Both sites now preserve the registration error and throw `Cannot {create worktree,resume}: repository registration failed.` with the original cause and a concrete cleanup hint (`Remove the stale workspace entry at and retry`) when the failure matches the `createProjectSourceSymlink()` shape. Credits @Bortlesboat for identifying the root cause and the parser approach in #1157. Closes #1146. - **Server startup no longer marks actively-running workflows as failed.** The `failOrphanedRuns()` call has been removed from `packages/server/src/index.ts` to match the CLI precedent (`packages/cli/src/cli.ts:256-258`). Per the new CLAUDE.md principle "No Autonomous Lifecycle Mutation Across Process Boundaries", a stuck `running` row is now transitioned explicitly by the user: via the per-row Cancel/Abandon buttons on the dashboard workflow card, or `archon workflow abandon ` from the CLI. (`archon workflow cleanup` is a separate command that deletes OLD terminal runs for disk hygiene — it does not handle stuck `running` rows.) Closes #1216. diff --git a/bun.lock b/bun.lock index 8f1fcec74e..e9c0d03ff1 100644 --- a/bun.lock +++ b/bun.lock @@ -4,7 +4,7 @@ "": { "name": "archon", "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.74", + "@anthropic-ai/claude-agent-sdk": "^0.2.121", }, "devDependencies": { "@eslint/js": "^9.39.1", @@ -127,9 +127,9 @@ "name": "@archon/providers", "version": "0.3.6", "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.89", + "@anthropic-ai/claude-agent-sdk": "^0.2.121", "@archon/paths": "workspace:*", - "@openai/codex-sdk": "^0.116.0", + "@openai/codex-sdk": "^0.125.0", }, "devDependencies": { "pino": "^9", @@ -150,7 +150,7 @@ "@archon/workflows": "workspace:*", "@hono/zod-openapi": "^0.19.6", "dotenv": "^17.2.3", - "hono": "^4.11.4", + "hono": "^4.12.16", "zod": "^3.25.28", }, "devDependencies": { @@ -225,15 +225,32 @@ }, }, "overrides": { + "@hono/node-server": "^1.19.13", "axios": "^1.15.0", "test-exclude": "^7.0.1", }, "packages": { "@antfu/ni": ["@antfu/ni@25.0.0", "", { "dependencies": { "ansis": "^4.0.0", "fzf": "^0.5.2", "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" }, "bin": { "na": "bin/na.mjs", "ni": "bin/ni.mjs", "nr": "bin/nr.mjs", "nci": "bin/nci.mjs", "nlx": "bin/nlx.mjs", "nun": "bin/nun.mjs", "nup": "bin/nup.mjs" } }, "sha512-9q/yCljni37pkMr4sPrI3G4jqdIk074+iukc5aFJl7kmDCCsiJrbZ6zKxnES1Gwg+i9RcDZwvktl23puGslmvA=="], - "@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.74", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-S/SFSSbZHPL1HiQxAqCCxU3iHuE5nM+ir0OK1n0bZ+9hlVUH7OOn88AsV9s54E0c1kvH9YF4/foWH8J9kICsBw=="], + "@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.121", "", { "dependencies": { "@anthropic-ai/sdk": "^0.81.0", "@modelcontextprotocol/sdk": "^1.29.0" }, "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.121", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.121", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.121", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.121", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.121", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.121", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.121", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.121" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-hwZNYTkGLKVixd/V/OCJwfH/SdfxZXGV0m6wvy5EBq6qfB+lvJTRz/MSOSa7dHqo4/F7zJY68crEEca68Wrxpw=="], - "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.74.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-srbJV7JKsc5cQ6eVuFzjZO7UR3xEPJqPamHFIe29bs38Ij2IripoAhC0S5NslNbaFUYqBKypmmpzMTpqfHEUDw=="], + "@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.121", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zVHcXvx6Hl/glDcOCH+EyNx4KPE9cMGLk42eEBSZe014tAN5W8bwM/By08iM6dxijnpH0NQRNNEAW+BryWzuDg=="], + + "@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.121", "", { "os": "darwin", "cpu": "x64" }, "sha512-lIXdqKj+bpfDxCk/eU1F1TXNqsIsLTRrkUG/wx19WIGZ8gLUmmVSveUKGlNegTs7S6evMvuezprJzDJT4TcvPA=="], + + "@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.121", "", { "os": "linux", "cpu": "arm64" }, "sha512-AQSnJzaiFvQpUPfO1tWLvsHgb6KNar4QYEQ/5/sk1itfgr3Fx9gxTreq43wX7AXSvkBX1QlDaP1aR1sfM/g/lQ=="], + + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.121", "", { "os": "linux", "cpu": "arm64" }, "sha512-4XaGK+dRBYy7krln7BrDG0WsdE6ejUSgHjWHlUGXoubFfZUvls4GSahLcYjJBArLi4dLnxKw8zEuiQguPAIbrw=="], + + "@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.2.121", "", { "os": "linux", "cpu": "x64" }, "sha512-DJUgpm7au086WaQV/S7BGOt2M8D90spGZRizT3twYsacf1BxzK1qsXqB/Pw1lUjPy6pI107pml/TaPzWuS/Vzg=="], + + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.121", "", { "os": "linux", "cpu": "x64" }, "sha512-sQoGIgzLlBRrwizxsCV/lbaEuxXom/cfOwlDtQ2HnS1IzDDSjSf5d5pugpWItkOyXBWcHzMUu731WTTutvd/BQ=="], + + "@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.121", "", { "os": "win32", "cpu": "arm64" }, "sha512-6n/NHkHxs0/lCJX3XPADjo1EFzXBf0IwYz/nyzJGBCDJjGKmgTe0i8eYBr/hviwt1/OPeK7dmVzVSVl6EL9Azg=="], + + "@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.2.121", "", { "os": "win32", "cpu": "x64" }, "sha512-v2/R918/t94cCwc6rmbxk+UYeQPtF2oBLtQAk+cT0M60hvqmCZO2noyZx5uTp8TQncOlG4MkINIeNY2yfmWSoQ=="], + + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.81.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-D4K5PvEV6wPiRtVlVsJHIUhHAmOZ6IT/I9rKlTf84gR7GyyAurPJK7z9BOf/AZqC5d1DhYQGJNKRmV+q8dGhgw=="], "@archon/adapters": ["@archon/adapters@workspace:packages/adapters"], @@ -455,7 +472,7 @@ "@grammyjs/types": ["@grammyjs/types@3.26.0", "", {}, "sha512-jlnyfxfev/2o68HlvAGRocAXgdPPX5QabG7jZlbqC2r9DZyWBfzTlg+nu3O3Fy4EhgLWu28hZ/8wr7DsNamP9A=="], - "@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="], + "@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="], "@hono/zod-openapi": ["@hono/zod-openapi@0.19.10", "", { "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.0", "@hono/zod-validator": "^0.7.1", "openapi3-ts": "^4.5.0" }, "peerDependencies": { "hono": ">=4.3.6", "zod": ">=3.0.0" } }, "sha512-dpoS6DenvoJyvxtQ7Kd633FRZ/Qf74+4+o9s+zZI8pEqnbjdF/DtxIib08WDpCaWabMEJOL5TXpMgNEZvb7hpA=="], @@ -471,9 +488,9 @@ "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], @@ -495,9 +512,9 @@ "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], @@ -505,11 +522,11 @@ "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], "@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], @@ -517,7 +534,7 @@ "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], "@inquirer/ansi": ["@inquirer/ansi@1.0.2", "", {}, "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ=="], @@ -541,7 +558,7 @@ "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], - "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], "@mswjs/interceptors": ["@mswjs/interceptors@0.41.3", "", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "strict-event-emitter": "^0.5.1" } }, "sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA=="], @@ -587,21 +604,21 @@ "@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="], - "@openai/codex": ["@openai/codex@0.116.0", "", { "optionalDependencies": { "@openai/codex-darwin-arm64": "npm:@openai/codex@0.116.0-darwin-arm64", "@openai/codex-darwin-x64": "npm:@openai/codex@0.116.0-darwin-x64", "@openai/codex-linux-arm64": "npm:@openai/codex@0.116.0-linux-arm64", "@openai/codex-linux-x64": "npm:@openai/codex@0.116.0-linux-x64", "@openai/codex-win32-arm64": "npm:@openai/codex@0.116.0-win32-arm64", "@openai/codex-win32-x64": "npm:@openai/codex@0.116.0-win32-x64" }, "bin": { "codex": "bin/codex.js" } }, "sha512-K6q9P2ZmpnzGmpS6Ybjvsdtvu8AbJx3f/Z4KmjH1u85StSS9TWMSQB8z0PPObKMejbtiIkHwhGyEIHi4iBYjig=="], + "@openai/codex": ["@openai/codex@0.125.0", "", { "optionalDependencies": { "@openai/codex-darwin-arm64": "npm:@openai/codex@0.125.0-darwin-arm64", "@openai/codex-darwin-x64": "npm:@openai/codex@0.125.0-darwin-x64", "@openai/codex-linux-arm64": "npm:@openai/codex@0.125.0-linux-arm64", "@openai/codex-linux-x64": "npm:@openai/codex@0.125.0-linux-x64", "@openai/codex-win32-arm64": "npm:@openai/codex@0.125.0-win32-arm64", "@openai/codex-win32-x64": "npm:@openai/codex@0.125.0-win32-x64" }, "bin": { "codex": "bin/codex.js" } }, "sha512-GiE9wlgL95u/5BRirY5d3EaRLU1tu7Y1R09R8lCHHVmcQdSmhS809FdPDWH3gIYHS7ZriAPqXwJ3aLA0WKl40Q=="], - "@openai/codex-darwin-arm64": ["@openai/codex@0.116.0-darwin-arm64", "", { "os": "darwin", "cpu": "arm64" }, "sha512-WkdL083p8uMeASpg8bwV0DPGgzkm48LjN3MyU2m/YukujbiLnknAmG29O2q2rFCLm0oLSDIGUK8EnXA4ZcAF9Q=="], + "@openai/codex-darwin-arm64": ["@openai/codex@0.125.0-darwin-arm64", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gn2fHiSO0XgyHp1OSd5DWUTm66Bv9UEuipW5pVEj1E+hWZCOrdqnYttllKFWtRGj5yiKefNX3JIxONgh/ZwlOQ=="], - "@openai/codex-darwin-x64": ["@openai/codex@0.116.0-darwin-x64", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ax8uTwYSNIwGrzcNRcn0jJQhZzNcKGDbbn00Emde7gGOemjSLhRALjUaKjckAaW5xWnNqHTGdtzzPB4phNlDYg=="], + "@openai/codex-darwin-x64": ["@openai/codex@0.125.0-darwin-x64", "", { "os": "darwin", "cpu": "x64" }, "sha512-TZ5Lek2X/UXTI9LXFxzarvQaJeuTrqVh4POc7soO/8RclVnCxADnCf15sivxLd5eiFW4t0myGoeVoM4lciRiRg=="], - "@openai/codex-linux-arm64": ["@openai/codex@0.116.0-linux-arm64", "", { "os": "linux", "cpu": "arm64" }, "sha512-X7cL8rBSGDB+RSZc2FoKiqcMVeLPMmo06bkss/en4lLQsV1XG2DZI56WuXg92IOX3SjYl6Av/eOWgsb1t3UeLQ=="], + "@openai/codex-linux-arm64": ["@openai/codex@0.125.0-linux-arm64", "", { "os": "linux", "cpu": "arm64" }, "sha512-pPnJoJD6rZ2Iin0zNt/up36bO2/EOp2B+1/rPHu/lSq3PJbT3Fmnfut2kJy5LylXb7bGA2XQbtqOogZzIbnlkA=="], - "@openai/codex-linux-x64": ["@openai/codex@0.116.0-linux-x64", "", { "os": "linux", "cpu": "x64" }, "sha512-S9InOgJT3tj6uQp55NqrCA1k5tklwFaH00JdC2ElbRmxchm7ard4WxHSJZX9TiY8enj4cQoLIC04NFTUCO+/PQ=="], + "@openai/codex-linux-x64": ["@openai/codex@0.125.0-linux-x64", "", { "os": "linux", "cpu": "x64" }, "sha512-K2NTTEeBpz/G+N2x17UGWfauRt3So+ir4f+U/60l5PPnYEJB/w3YZrlXo2G9og8Dm9BqtoBAjoPV74sRv9tWWQ=="], - "@openai/codex-sdk": ["@openai/codex-sdk@0.116.0", "", { "dependencies": { "@openai/codex": "0.116.0" } }, "sha512-qrn1Pu5G1GJ9w4m/Lk3L3466ulMGG9SfyR0LPAaXdisuQI1rqgoUOuoZ4byX7cCzn0x1g2+WPc0apZgjMEK04Q=="], + "@openai/codex-sdk": ["@openai/codex-sdk@0.125.0", "", { "dependencies": { "@openai/codex": "0.125.0" } }, "sha512-1xCIHdSbQVF880nJ2aVWdPIsWZbSpKODwuP9y/gvtChDYhYfYEW0DKp2H8ZlctkzIjlzS/WzYmP6ZZPHIvs2Dg=="], - "@openai/codex-win32-arm64": ["@openai/codex@0.116.0-win32-arm64", "", { "os": "win32", "cpu": "arm64" }, "sha512-kX2oAUzkgZX9OsYpd4omv9IGf+9VWj4Vy3UtIAnQKBu1DTSzmTJmXDuDn87mkyUciSZadm2QbeqQQzm2NC0NYw=="], + "@openai/codex-win32-arm64": ["@openai/codex@0.125.0-win32-arm64", "", { "os": "win32", "cpu": "arm64" }, "sha512-zxoUakw9oIHIFrAyk400XkkLBJFA6nOym0NDq6sQ/jhdcYraKqNSRCII2nsBwZHk+/4zgUvuk52iuutgysY/rQ=="], - "@openai/codex-win32-x64": ["@openai/codex@0.116.0-win32-x64", "", { "os": "win32", "cpu": "x64" }, "sha512-6sBIMOoA9FNuxQvCCnK0P548Wqrlk3I9SMdtOCUg2zYzYU7jOF2mWS1VpRQ6R+Jvo2x50dxeJZ+W37dBmXfprw=="], + "@openai/codex-win32-x64": ["@openai/codex@0.125.0-win32-x64", "", { "os": "win32", "cpu": "x64" }, "sha512-ofpOK+OWH5QFuUZ9pTM0d/PcXUXiIP5z5DpRcE9MlucJoyOl4Zy4Nu3NcuHF4YzCkZMQb6x3j0tjDEPHKqNQzw=="], "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], @@ -1391,11 +1408,11 @@ "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], - "flatted": ["flatted@3.4.1", "", {}, "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ=="], + "flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], "flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="], - "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + "follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="], "fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="], @@ -1505,7 +1522,7 @@ "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="], - "hono": ["hono@4.12.7", "", {}, "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw=="], + "hono": ["hono@4.12.16", "", {}, "sha512-jN0ZewiNAWSe5khM3EyCmBb250+b40wWbwNILNfEvq84VREWwOIkuUsFONk/3i3nqkz7Oe1PcpM2mwQEK2L9Kg=="], "html-comment-regex": ["html-comment-regex@1.1.2", "", {}, "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ=="], @@ -1961,7 +1978,7 @@ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], + "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], "pg": ["pg@8.20.0", "", { "dependencies": { "pg-connection-string": "^2.12.0", "pg-pool": "^3.13.0", "pg-protocol": "^1.13.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA=="], @@ -2039,7 +2056,7 @@ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="], + "qs": ["qs@6.15.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], @@ -2449,7 +2466,7 @@ "@antfu/ni/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], - "@archon/providers/@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.89", "", { "dependencies": { "@anthropic-ai/sdk": "^0.74.0", "@modelcontextprotocol/sdk": "^1.27.1" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-/9W0lyBGuGHw1uu7pQafsp6BLpxfqCv1QYE0Z/eZTX6lGHht4j4Q+O3UImzjsiyEE9cGkOAwZBGAEHDEqt+QUA=="], + "@anthropic-ai/claude-agent-sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "@astrojs/markdown-remark/remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], @@ -2491,20 +2508,6 @@ "@expressive-code/plugin-shiki/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], - "@img/sharp-darwin-arm64/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], - - "@img/sharp-darwin-x64/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], - - "@img/sharp-linux-arm/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], - - "@img/sharp-linux-arm64/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], - - "@img/sharp-linux-x64/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], - - "@img/sharp-linuxmusl-arm64/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], - - "@img/sharp-linuxmusl-x64/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], - "@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], "@mdx-js/mdx/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], @@ -2517,6 +2520,10 @@ "@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "@modelcontextprotocol/sdk/hono": ["hono@4.12.7", "", {}, "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw=="], + + "@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "@redocly/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "@redocly/openapi-core/colorette": ["colorette@1.4.0", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="], @@ -2691,28 +2698,14 @@ "retext-stringify/unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + "shadcn/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], + "shadcn/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], "shadcn/execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], "shadcn/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], - "sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], - - "sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], - - "sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], - - "sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], - - "sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], - - "sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], - - "sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], - - "sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], - "sitemap/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], @@ -2813,6 +2806,10 @@ "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + "astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], "astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], @@ -2829,12 +2826,24 @@ "astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + "astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + "astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + "astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + "astro/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], "astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + "astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "cliui/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], @@ -2983,6 +2992,10 @@ "retext/unified/trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + "shadcn/@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + + "shadcn/@modelcontextprotocol/sdk/hono": ["hono@4.12.7", "", {}, "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw=="], + "shadcn/execa/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], "shadcn/execa/human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], @@ -3091,6 +3104,8 @@ "remark-parse/mdast-util-from-markdown/unist-util-stringify-position/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + "shadcn/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "shadcn/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], "telegramify-markdown/remark-gfm/mdast-util-gfm/mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@0.1.3", "", { "dependencies": { "ccount": "^1.0.0", "mdast-util-find-and-replace": "^1.1.0", "micromark": "^2.11.3" } }, "sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A=="], diff --git a/deploy/.env.example b/deploy/.env.example index 9e2d5f521f..9a0b208e74 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -46,7 +46,7 @@ TELEGRAM_BOT_TOKEN=123456789:ABC... # ============================================ # Optional # ============================================ -PORT=3000 +PORT=3000 # Docker deployment default (the included compose/Caddy configs target :3000). For local dev (no Docker), omit PORT — server and Vite proxy both default to 3090. # TELEGRAM_STREAMING_MODE=stream # DISCORD_STREAMING_MODE=batch diff --git a/package.json b/package.json index 2fceb51a72..736a97ac4c 100644 --- a/package.json +++ b/package.json @@ -49,9 +49,10 @@ }, "overrides": { "test-exclude": "^7.0.1", - "axios": "^1.15.0" + "axios": "^1.15.0", + "@hono/node-server": "^1.19.13" }, "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.74" + "@anthropic-ai/claude-agent-sdk": "^0.2.121" } } diff --git a/packages/cli/src/commands/setup.test.ts b/packages/cli/src/commands/setup.test.ts index 6d463d5fda..0ef6d3f742 100644 --- a/packages/cli/src/commands/setup.test.ts +++ b/packages/cli/src/commands/setup.test.ts @@ -150,7 +150,9 @@ CODEX_ACCOUNT_ID=account1 expect(content).toContain('# Using SQLite (default)'); expect(content).toContain('CLAUDE_USE_GLOBAL_AUTH=true'); expect(content).toContain('DEFAULT_AI_ASSISTANT=claude'); - expect(content).toContain('PORT=3000'); + // PORT is intentionally commented out — server and Vite both default to 3090 when unset (#1152). + expect(content).toContain('# PORT=3090'); + expect(content).not.toMatch(/^PORT=/m); expect(content).not.toContain('DATABASE_URL='); }); @@ -401,11 +403,11 @@ CODEX_ACCOUNT_ID=account1 }); describe('copyArchonSkill', () => { - it('should create skill files in target directory', () => { + it('should create skill files in target directory', async () => { const target = join(TEST_DIR, 'skill-target'); mkdirSync(target, { recursive: true }); - copyArchonSkill(target); + await copyArchonSkill(target); expect(existsSync(join(target, '.claude', 'skills', 'archon', 'SKILL.md'))).toBe(true); expect(existsSync(join(target, '.claude', 'skills', 'archon', 'guides', 'setup.md'))).toBe( @@ -419,11 +421,11 @@ CODEX_ACCOUNT_ID=account1 ).toBe(true); }); - it('should write non-empty content to skill files', () => { + it('should write non-empty content to skill files', async () => { const target = join(TEST_DIR, 'skill-target-content'); mkdirSync(target, { recursive: true }); - copyArchonSkill(target); + await copyArchonSkill(target); const content = readFileSync( join(target, '.claude', 'skills', 'archon', 'SKILL.md'), @@ -433,23 +435,23 @@ CODEX_ACCOUNT_ID=account1 expect(content).toContain('archon'); }); - it('should overwrite existing skill files', () => { + it('should overwrite existing skill files', async () => { const target = join(TEST_DIR, 'skill-target-overwrite'); const skillDir = join(target, '.claude', 'skills', 'archon'); mkdirSync(skillDir, { recursive: true }); writeFileSync(join(skillDir, 'SKILL.md'), 'old content'); - copyArchonSkill(target); + await copyArchonSkill(target); const content = readFileSync(join(skillDir, 'SKILL.md'), 'utf-8'); expect(content).not.toBe('old content'); }); - it('should create skill files even when target directory does not exist', () => { + it('should create skill files even when target directory does not exist', async () => { const target = join(TEST_DIR, 'non-existent-parent', 'skill-target-new'); // Do NOT pre-create target — copyArchonSkill must handle it - copyArchonSkill(target); + await copyArchonSkill(target); expect(existsSync(join(target, '.claude', 'skills', 'archon', 'SKILL.md'))).toBe(true); }); diff --git a/packages/cli/src/commands/setup.ts b/packages/cli/src/commands/setup.ts index 0235ceec3e..d913891486 100644 --- a/packages/cli/src/commands/setup.ts +++ b/packages/cli/src/commands/setup.ts @@ -24,7 +24,6 @@ import { } from '@clack/prompts'; import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; import { join, dirname } from 'path'; -import { BUNDLED_SKILL_FILES } from '../bundled-skill'; import { homedir } from 'os'; import { randomBytes } from 'crypto'; import { spawn, execSync, type ChildProcess } from 'child_process'; @@ -1290,8 +1289,12 @@ export function generateEnvContent(config: SetupConfig): string { } // Server + // PORT is intentionally omitted: both the Hono server (packages/core/src/utils/port-allocation.ts) + // and the Vite dev proxy (packages/web/vite.config.ts) default to 3090 when unset, which keeps + // them in sync. Writing a fixed PORT here risked a mismatch if ~/.archon/.env leaks a PORT that + // the Vite proxy (which only reads repo-local .env) never sees — see #1152. lines.push('# Server'); - lines.push('PORT=3000'); + lines.push('# PORT=3090 # Default: 3090. Uncomment to override.'); lines.push(''); // Concurrency @@ -1330,8 +1333,18 @@ function writeEnvFiles( * Copy the bundled Archon skill files to /.claude/skills/archon/ * * Always overwrites existing files to ensure the latest skill version is installed. + * + * The `bundled-skill` module is dynamically imported here so that its 18 top-level + * `import … with { type: 'text' }` statements only execute when this function is + * actually called. Compiled binaries (`bun build --compile`) still statically + * analyze the literal-string `import()` and embed the chunk; linked-source + * installs (`bun link`) don't touch the source skill files unless the user runs + * `archon setup`. Without this indirection, every `archon` invocation — + * including `archon --help` — fails at module load when the source skill files + * are missing from disk. */ -export function copyArchonSkill(targetPath: string): void { +export async function copyArchonSkill(targetPath: string): Promise { + const { BUNDLED_SKILL_FILES } = await import('../bundled-skill'); const skillRoot = join(targetPath, '.claude', 'skills', 'archon'); for (const [relativePath, content] of Object.entries(BUNDLED_SKILL_FILES)) { const dest = join(skillRoot, relativePath); @@ -1675,7 +1688,7 @@ export async function setupCommand(options: SetupOptions): Promise { const skillTarget = skillTargetRaw; s.start('Installing Archon skill...'); try { - copyArchonSkill(skillTarget); + await copyArchonSkill(skillTarget); } catch (err) { s.stop('Archon skill installation failed'); cancel(`Could not install skill: ${(err as NodeJS.ErrnoException).message}`); @@ -1769,7 +1782,7 @@ export async function setupCommand(options: SetupOptions): Promise { // Additional options note note( 'Other settings you can customize in ~/.archon/.env:\n' + - ' - PORT (default: 3000)\n' + + ' - PORT (default: 3090)\n' + ' - MAX_CONCURRENT_CONVERSATIONS (default: 10)\n' + ' - *_STREAMING_MODE (stream | batch per platform)\n\n' + 'These defaults work well for most users.', diff --git a/packages/core/package.json b/packages/core/package.json index 541685c3be..6a13b46fa2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -22,7 +22,7 @@ "./state/*": "./src/state/*.ts" }, "scripts": { - "test": "bun test src/handlers/command-handler.test.ts && bun test src/handlers/clone.test.ts && bun test src/db/adapters/postgres.test.ts && bun test src/db/adapters/sqlite.test.ts src/db/codebases.test.ts src/db/connection.test.ts src/db/conversations.test.ts src/db/env-vars.test.ts src/db/isolation-environments.test.ts src/db/messages.test.ts src/db/sessions.test.ts src/db/workflow-events.test.ts src/db/workflows.test.ts src/utils/defaults-copy.test.ts src/utils/worktree-sync.test.ts src/utils/conversation-lock.test.ts src/utils/credential-sanitizer.test.ts src/utils/port-allocation.test.ts src/utils/error.test.ts src/utils/error-formatter.test.ts src/utils/github-graphql.test.ts src/config/ src/state/ && bun test src/utils/path-validation.test.ts && bun test src/db/workflow-analytics.test.ts && bun test src/services/cleanup-service.test.ts && bun test src/services/title-generator.test.ts && bun test src/services/cron-parser.test.ts && bun test src/services/knowledge-writer.test.ts && bun test src/workflows/ && bun test src/operations/workflow-operations.test.ts && bun test src/operations/isolation-operations.test.ts && bun test src/orchestrator/orchestrator.test.ts && bun test src/orchestrator/orchestrator-agent.test.ts && bun test src/orchestrator/orchestrator-isolation.test.ts", + "test": "bun test src/handlers/command-handler.test.ts && bun test src/handlers/clone.test.ts && bun test src/db/adapters/postgres.test.ts && bun test src/db/connection.test.ts && bun test src/db/adapters/sqlite.test.ts src/db/codebases.test.ts src/db/conversations.test.ts src/db/env-vars.test.ts src/db/isolation-environments.test.ts src/db/messages.test.ts src/db/sessions.test.ts src/db/workflow-events.test.ts src/db/workflows.test.ts src/utils/defaults-copy.test.ts src/utils/worktree-sync.test.ts src/utils/conversation-lock.test.ts src/utils/credential-sanitizer.test.ts src/utils/port-allocation.test.ts src/utils/error.test.ts src/utils/error-formatter.test.ts src/utils/github-graphql.test.ts src/config/ src/state/ && bun test src/utils/path-validation.test.ts && bun test src/db/workflow-analytics.test.ts && bun test src/services/cleanup-service.test.ts && bun test src/services/title-generator.test.ts && bun test src/services/cron-parser.test.ts && bun test src/services/knowledge-writer.test.ts && bun test src/workflows/ && bun test src/operations/workflow-operations.test.ts && bun test src/operations/isolation-operations.test.ts && bun test src/orchestrator/orchestrator.test.ts && bun test src/orchestrator/orchestrator-agent.test.ts && bun test src/orchestrator/orchestrator-isolation.test.ts", "type-check": "bun x tsc --noEmit", "build": "echo 'No build needed - Bun runs TypeScript directly'" }, diff --git a/packages/core/src/db/sessions.test.ts b/packages/core/src/db/sessions.test.ts index 810c815dd8..476e1c3acf 100644 --- a/packages/core/src/db/sessions.test.ts +++ b/packages/core/src/db/sessions.test.ts @@ -168,7 +168,18 @@ describe('sessions', () => { ); }); - test('throws SessionNotFoundError when session does not exist', async () => { + test('sets assistant_session_id to NULL when called with null', async () => { + mockQuery.mockResolvedValueOnce(createQueryResult([], 1)); + + await updateSession('session-123', null); + + expect(mockQuery).toHaveBeenCalledWith( + 'UPDATE remote_agent_sessions SET assistant_session_id = $1 WHERE id = $2', + [null, 'session-123'] + ); + }); + + test('throws SessionNotFoundError when session does not exist (updateSession)', async () => { mockQuery.mockResolvedValueOnce(createQueryResult([], 0)); // rowCount = 0 const error = await updateSession('non-existent', 'new-session-id').catch(e => e); diff --git a/packages/core/src/db/sessions.ts b/packages/core/src/db/sessions.ts index e04f602e75..38df36b55b 100644 --- a/packages/core/src/db/sessions.ts +++ b/packages/core/src/db/sessions.ts @@ -58,7 +58,7 @@ export async function createSession(data: { return result.rows[0]; } -export async function updateSession(id: string, sessionId: string): Promise { +export async function updateSession(id: string, sessionId: string | null): Promise { const result = await pool.query( 'UPDATE remote_agent_sessions SET assistant_session_id = $1 WHERE id = $2', [sessionId, id] diff --git a/packages/core/src/orchestrator/orchestrator-agent.test.ts b/packages/core/src/orchestrator/orchestrator-agent.test.ts index 3a4a1299c9..11ff15f0ea 100644 --- a/packages/core/src/orchestrator/orchestrator-agent.test.ts +++ b/packages/core/src/orchestrator/orchestrator-agent.test.ts @@ -51,9 +51,11 @@ const mockLoadConfig = mock(() => const mockLogger = createMockLogger(); +const mockEnsureArchonWorkspacesPath = mock(() => Promise.resolve('/home/test/.archon/workspaces')); mock.module('@archon/paths', () => ({ createLogger: mock(() => mockLogger), getArchonWorkspacesPath: mock(() => '/home/test/.archon/workspaces'), + ensureArchonWorkspacesPath: mockEnsureArchonWorkspacesPath, getArchonHome: mock(() => '/home/test/.archon'), })); @@ -72,10 +74,14 @@ mock.module('../db/codebases', () => ({ createCodebase: mock(() => Promise.resolve({ id: 'new-codebase-id' })), })); +const mockUpdateSession = mock(() => Promise.resolve()); +const mockTransitionSession = mock(() => + Promise.resolve({ id: 'session-1', assistant_session_id: null }) +); mock.module('../db/sessions', () => ({ getActiveSession: mock(() => Promise.resolve(null)), - updateSession: mock(() => Promise.resolve()), - transitionSession: mock(() => Promise.resolve({ id: 'session-1', assistant_session_id: null })), + updateSession: mockUpdateSession, + transitionSession: mockTransitionSession, })); const mockParseCommand = mock( @@ -902,6 +908,7 @@ describe('discoverAllWorkflows — remote sync', () => { mockSendQuery.mockClear(); mockGetCodebaseEnvVars.mockReset(); mockLoadConfig.mockReset(); + mockEnsureArchonWorkspacesPath.mockClear(); // Reset mocks between tests in this suite and restore safe defaults mockGetOrCreateConversation.mockImplementation(() => Promise.resolve(null)); mockGetCodebase.mockImplementation(() => Promise.resolve(null)); @@ -927,6 +934,9 @@ describe('discoverAllWorkflows — remote sync', () => { expect(mockSyncWorkspace).toHaveBeenCalledWith('/repos/test-repo', undefined, { resetAfterFetch: false, }); + // Regression guard: orchestrator must resolve cwd through the ensure variant + // so the workspaces dir is created before the AI provider spawn (issue #1528). + expect(mockEnsureArchonWorkspacesPath).toHaveBeenCalled(); }); test('passes resetAfterFetch=true for managed clones', async () => { @@ -1602,3 +1612,73 @@ describe('handleMessage — workflow context injection', () => { await expect(handleMessage(platform, 'conv-1', 'Hello')).resolves.toBeUndefined(); }); }); + +// ─── Stale session ID clearing on error_during_execution ──────────────────── + +describe('stale session ID clearing on error_during_execution', () => { + beforeEach(() => { + mockUpdateSession.mockClear(); + mockTransitionSession.mockClear(); + mockGetOrCreateConversation.mockReset(); + mockGetCodebase.mockReset(); + mockSendQuery.mockReset(); + mockLogger.warn.mockClear(); + mockGetRecentWorkflowResultMessages.mockReset(); + mockGetRecentWorkflowResultMessages.mockImplementation(() => Promise.resolve([])); + mockDiscoverWorkflowsWithConfig.mockReset(); + mockDiscoverWorkflowsWithConfig.mockImplementation(() => + Promise.resolve({ workflows: [], errors: [] }) + ); + mockGetOrCreateConversation.mockImplementation(() => Promise.resolve(makeConversation())); + mockGetCodebase.mockImplementation(() => Promise.resolve(null)); + mockListCodebases.mockReset(); + mockListCodebases.mockImplementation(() => Promise.resolve([])); + }); + + test('handleStreamMode: clears session ID on error_during_execution result', async () => { + // Simulate AI returning error_during_execution with a stale session ID + mockSendQuery.mockImplementationOnce(async function* () { + yield { + type: 'result', + isError: true, + errorSubtype: 'error_during_execution', + sessionId: 'stale-session-id', + }; + }); + // transitionSession returns a session with an existing assistant_session_id + mockTransitionSession.mockResolvedValueOnce({ + id: 'session-1', + assistant_session_id: 'stale-session-id', + }); + + const platform = makePlatform(); + // Use streaming mode + (platform.getStreamingMode as ReturnType).mockReturnValue('stream'); + await handleMessage(platform, 'conv-1', 'hello'); + + // updateSession should be called with null to clear the stale session ID + expect(mockUpdateSession).toHaveBeenCalledWith('session-1', null); + }); + + test('handleBatchMode: clears session ID on error_during_execution result', async () => { + mockSendQuery.mockImplementationOnce(async function* () { + yield { + type: 'result', + isError: true, + errorSubtype: 'error_during_execution', + sessionId: 'stale-session-id', + }; + }); + mockTransitionSession.mockResolvedValueOnce({ + id: 'session-1', + assistant_session_id: 'stale-session-id', + }); + + const platform = makePlatform(); + // batch is the default from makePlatform, but be explicit + (platform.getStreamingMode as ReturnType).mockReturnValue('batch'); + await handleMessage(platform, 'conv-1', 'hello'); + + expect(mockUpdateSession).toHaveBeenCalledWith('session-1', null); + }); +}); diff --git a/packages/core/src/orchestrator/orchestrator-agent.ts b/packages/core/src/orchestrator/orchestrator-agent.ts index 292f0e0ad8..061bd5b5dd 100644 --- a/packages/core/src/orchestrator/orchestrator-agent.ts +++ b/packages/core/src/orchestrator/orchestrator-agent.ts @@ -25,7 +25,7 @@ import { formatToolCall } from '@archon/workflows/utils/tool-formatter'; import { classifyAndFormatError } from '../utils/error-formatter'; import { toError } from '../utils/error'; import { getAgentProvider, getProviderCapabilities } from '@archon/providers'; -import { getArchonHome, getArchonWorkspacesPath } from '@archon/paths'; +import { getArchonHome, getArchonWorkspacesPath, ensureArchonWorkspacesPath } from '@archon/paths'; import { syncArchonToWorktree } from '../utils/worktree-sync'; import { syncWorkspace, toRepoPath } from '@archon/git'; import type { WorkspaceSyncResult } from '@archon/git'; @@ -335,12 +335,15 @@ async function dispatchOrchestratorWorkflow( // ─── Session Helpers ──────────────────────────────────────────────────────── -async function tryPersistSessionId(sessionId: string, assistantSessionId: string): Promise { +async function tryPersistSessionId( + sessionId: string, + assistantSessionId: string | null +): Promise { try { await sessionDb.updateSession(sessionId, assistantSessionId); } catch (error) { getLog().error( - { err: error as Error, sessionId, newSessionId: assistantSessionId }, + { err: error as Error, sessionId, persistedValue: assistantSessionId }, 'session_id_persist_failed' ); } @@ -806,7 +809,7 @@ export async function handleMessage( attachedFiles, workflowContext ); - const cwd = getArchonWorkspacesPath(); + const cwd = await ensureArchonWorkspacesPath(); // 4. Update activity and get/create session await db.touchConversation(conversation.id); @@ -964,11 +967,32 @@ async function handleStreamMode( await platform.sendStructuredEvent(conversationId, msg); } } else if (msg.type === 'result') { - if (msg.sessionId) { + if (msg.isError && msg.errorSubtype === 'error_during_execution') { + getLog().warn( + { + conversationId, + errorSubtype: msg.errorSubtype, + staleSessionId: msg.sessionId, + errors: msg.errors, + stopReason: msg.stopReason, + }, + 'clearing_stale_session_id' + ); + await tryPersistSessionId(session.id, null); + newSessionId = undefined; + } else if (msg.sessionId) { newSessionId = msg.sessionId; } if (msg.isError) { - getLog().warn({ conversationId, errorSubtype: msg.errorSubtype }, 'ai_result_error'); + getLog().warn( + { + conversationId, + errorSubtype: msg.errorSubtype, + errors: msg.errors, + stopReason: msg.stopReason, + }, + 'ai_result_error' + ); const syntheticError = new Error(msg.errorSubtype ?? 'AI result error'); await platform.sendMessage(conversationId, classifyAndFormatError(syntheticError)); if (newSessionId) { @@ -1087,11 +1111,32 @@ async function handleBatchMode( getLog().debug({ toolName: msg.toolName }, 'tool_call'); } } else if (msg.type === 'result') { - if (msg.sessionId) { + if (msg.isError && msg.errorSubtype === 'error_during_execution') { + getLog().warn( + { + conversationId, + errorSubtype: msg.errorSubtype, + staleSessionId: msg.sessionId, + errors: msg.errors, + stopReason: msg.stopReason, + }, + 'clearing_stale_session_id' + ); + await tryPersistSessionId(session.id, null); + newSessionId = undefined; + } else if (msg.sessionId) { newSessionId = msg.sessionId; } if (msg.isError) { - getLog().warn({ conversationId, errorSubtype: msg.errorSubtype }, 'ai_result_error'); + getLog().warn( + { + conversationId, + errorSubtype: msg.errorSubtype, + errors: msg.errors, + stopReason: msg.stopReason, + }, + 'ai_result_error' + ); const syntheticError = new Error(msg.errorSubtype ?? 'AI result error'); await platform.sendMessage(conversationId, classifyAndFormatError(syntheticError)); if (newSessionId) { diff --git a/packages/core/src/orchestrator/orchestrator-isolation.test.ts b/packages/core/src/orchestrator/orchestrator-isolation.test.ts index 63ea6292ac..f8ca8d69b0 100644 --- a/packages/core/src/orchestrator/orchestrator-isolation.test.ts +++ b/packages/core/src/orchestrator/orchestrator-isolation.test.ts @@ -10,6 +10,7 @@ const mockLogger = createMockLogger(); mock.module('@archon/paths', () => ({ createLogger: mock(() => mockLogger), getArchonWorkspacesPath: mock(() => '/home/test/.archon/workspaces'), + ensureArchonWorkspacesPath: mock(() => Promise.resolve('/home/test/.archon/workspaces')), getArchonHome: mock(() => '/home/test/.archon'), })); diff --git a/packages/core/src/orchestrator/orchestrator.test.ts b/packages/core/src/orchestrator/orchestrator.test.ts index bd0caf3bf8..48cac3ceb0 100644 --- a/packages/core/src/orchestrator/orchestrator.test.ts +++ b/packages/core/src/orchestrator/orchestrator.test.ts @@ -12,6 +12,7 @@ const mockLogger = createMockLogger(); mock.module('@archon/paths', () => ({ createLogger: mock(() => mockLogger), getArchonWorkspacesPath: mock(() => '/home/test/.archon/workspaces'), + ensureArchonWorkspacesPath: mock(() => Promise.resolve('/home/test/.archon/workspaces')), getArchonHome: mock(() => '/home/test/.archon'), })); diff --git a/packages/core/src/utils/port-allocation.ts b/packages/core/src/utils/port-allocation.ts index efb34d3198..0ecb5b74e1 100644 --- a/packages/core/src/utils/port-allocation.ts +++ b/packages/core/src/utils/port-allocation.ts @@ -30,7 +30,7 @@ export function calculatePortOffset(path: string): number { * Get the port for the Hono server * - If PORT env var is set: use it (explicit override, validated) * - If running in worktree: auto-allocate deterministic port based on path hash - * - Otherwise: use default 3000 + * - Otherwise: use default 3090 (matches the Vite proxy fallback in packages/web/vite.config.ts) * * Note: Exits process with code 1 if PORT env var is set but invalid (not 1-65535) */ diff --git a/packages/docs-web/src/content/docs/getting-started/ai-assistants.md b/packages/docs-web/src/content/docs/getting-started/ai-assistants.md index b7eb80888f..e662087b8b 100644 --- a/packages/docs-web/src/content/docs/getting-started/ai-assistants.md +++ b/packages/docs-web/src/content/docs/getting-started/ai-assistants.md @@ -60,6 +60,8 @@ If neither is set in a compiled binary, Archon throws with install instructions The Claude Agent SDK accepts either the native compiled binary or a JS `cli.js`. +**Dev mode override:** when running from source (`bun run dev:server`), the SDK auto-resolves its bundled per-platform binary by default. Set `CLAUDE_BIN_PATH` if you need to override that — most commonly on glibc Linux where the SDK picks the musl variant first and fails to spawn. Config-file `claudeBinaryPath` is intentionally binary-mode-only (per-repo, not per-machine). + **Typical paths by install method:** | Install method | Typical executable path | diff --git a/packages/docs-web/src/content/docs/reference/security.md b/packages/docs-web/src/content/docs/reference/security.md index b3d1696e04..8c0538e18b 100644 --- a/packages/docs-web/src/content/docs/reference/security.md +++ b/packages/docs-web/src/content/docs/reference/security.md @@ -128,8 +128,8 @@ The GitHub and Gitea adapters verify webhook signatures to ensure payloads origi Archon prevents target repo `.env` from leaking into subprocesses through structural protection: -1. **Boot cleanup:** `stripCwdEnv()` removes Bun-auto-loaded CWD `.env` keys from `process.env` before any application code runs. -2. **Claude Code subprocess:** `executableArgs: ['--no-env-file']` prevents Bun from auto-loading `.env` in the Claude Code subprocess CWD. +1. **Boot cleanup:** `stripCwdEnv()` removes Bun-auto-loaded CWD `.env` keys from `process.env` before any application code runs. **This is the primary guard** — every subprocess Archon spawns inherits from the already-cleaned `process.env`. +2. **Claude Code subprocess:** when the SDK is configured to spawn a Bun-runnable JS entry point (legacy npm-installed `cli.js`/`cli.mjs`/`cli.cjs`), Archon also passes `executableArgs: ['--no-env-file']` so Bun skips its env autoload inside the spawned process. SDK 0.2.x ships per-platform native binaries instead — those don't auto-load `.env` from cwd, so the flag is unnecessary and is omitted. 3. **Bun script nodes:** `bun --no-env-file` prevents script node subprocesses from loading target repo `.env`. 4. **Bash nodes:** Not affected — bash does not auto-load `.env` files. diff --git a/packages/paths/src/archon-paths.test.ts b/packages/paths/src/archon-paths.test.ts index 734516375f..6cd56406a1 100644 --- a/packages/paths/src/archon-paths.test.ts +++ b/packages/paths/src/archon-paths.test.ts @@ -10,6 +10,7 @@ import { isDocker, getArchonHome, getArchonWorkspacesPath, + ensureArchonWorkspacesPath, getArchonWorktreesPath, getArchonConfigPath, getCommandFolderSearchPaths, @@ -546,6 +547,43 @@ describe('ensureProjectStructure', () => { }); }); +describe('ensureArchonWorkspacesPath', () => { + let tempArchonHome: string; + useEnvSnapshot(); + + beforeEach(async () => { + delete process.env.WORKSPACE_PATH; + delete process.env.ARCHON_DOCKER; + tempArchonHome = join( + tmpdir(), + `archon-paths-test-${Date.now()}-${Math.random().toString(36).slice(2)}` + ); + process.env.ARCHON_HOME = tempArchonHome; + }); + + afterEach(async () => { + await rm(tempArchonHome, { recursive: true, force: true }); + }); + + test('creates the workspaces directory when missing', async () => { + const expected = getArchonWorkspacesPath(); + expect(existsSync(expected)).toBe(false); + + const returned = await ensureArchonWorkspacesPath(); + + expect(returned).toBe(expected); + expect((await lstat(expected)).isDirectory()).toBe(true); + }); + + test('is idempotent - safe to call twice', async () => { + await ensureArchonWorkspacesPath(); + await ensureArchonWorkspacesPath(); + + const expected = getArchonWorkspacesPath(); + expect((await lstat(expected)).isDirectory()).toBe(true); + }); +}); + describe('createProjectSourceSymlink', () => { let tempArchonHome: string; let tempTarget: string; diff --git a/packages/paths/src/archon-paths.ts b/packages/paths/src/archon-paths.ts index ca8ea73774..19235f81aa 100644 --- a/packages/paths/src/archon-paths.ts +++ b/packages/paths/src/archon-paths.ts @@ -80,6 +80,16 @@ export function getArchonWorkspacesPath(): string { return join(getArchonHome(), 'workspaces'); } +/** + * Ensure the workspaces directory exists and return its path. + * Safe to call on a fresh install before any workspace is registered. + */ +export async function ensureArchonWorkspacesPath(): Promise { + const path = getArchonWorkspacesPath(); + await mkdir(path, { recursive: true }); + return path; +} + /** * Get the global worktrees directory (~/.archon/worktrees/). * Used as the legacy fallback for repos not registered under workspaces/. diff --git a/packages/paths/src/index.ts b/packages/paths/src/index.ts index 8f067cfeca..f5b0e065c4 100644 --- a/packages/paths/src/index.ts +++ b/packages/paths/src/index.ts @@ -4,6 +4,7 @@ export { isDocker, getArchonHome, getArchonWorkspacesPath, + ensureArchonWorkspacesPath, getArchonWorktreesPath, getArchonConfigPath, getCommandFolderSearchPaths, diff --git a/packages/providers/package.json b/packages/providers/package.json index 9e4e278b8e..cc0467305d 100644 --- a/packages/providers/package.json +++ b/packages/providers/package.json @@ -21,9 +21,9 @@ "type-check": "bun x tsc --noEmit" }, "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.89", + "@anthropic-ai/claude-agent-sdk": "^0.2.121", "@archon/paths": "workspace:*", - "@openai/codex-sdk": "^0.116.0" + "@openai/codex-sdk": "^0.125.0" }, "devDependencies": { "pino": "^9" diff --git a/packages/providers/src/claude/binary-resolver-dev.test.ts b/packages/providers/src/claude/binary-resolver-dev.test.ts index 2474c76d73..923490fbbd 100644 --- a/packages/providers/src/claude/binary-resolver-dev.test.ts +++ b/packages/providers/src/claude/binary-resolver-dev.test.ts @@ -1,8 +1,15 @@ /** * Tests for the Claude binary resolver in dev mode (BUNDLED_IS_BINARY=false). * Separate file because binary-mode tests mock BUNDLED_IS_BINARY=true. + * + * Dev mode normally lets the SDK resolve the binary from its bundled + * platform package. CLAUDE_BIN_PATH is honored as an escape hatch for + * environments where SDK auto-resolution picks the wrong variant — most + * notably glibc Linux hosts, where the SDK prefers the musl binary first + * and silently falls over with a misleading "not found" error. + * Config-file path is intentionally NOT honored in dev mode (still binary-only). */ -import { describe, test, expect, mock } from 'bun:test'; +import { describe, test, expect, mock, beforeEach, afterAll, spyOn } from 'bun:test'; import { createMockLogger } from '../test/mocks/logger'; mock.module('@archon/paths', () => ({ @@ -10,31 +17,68 @@ mock.module('@archon/paths', () => ({ BUNDLED_IS_BINARY: false, })); -import { resolveClaudeBinaryPath } from './binary-resolver'; +import * as resolver from './binary-resolver'; describe('resolveClaudeBinaryPath (dev mode)', () => { - test('returns undefined when BUNDLED_IS_BINARY is false', async () => { - const result = await resolveClaudeBinaryPath(); + const originalEnv = process.env.CLAUDE_BIN_PATH; + let fileExistsSpy: ReturnType | undefined; + + beforeEach(() => { + delete process.env.CLAUDE_BIN_PATH; + fileExistsSpy?.mockRestore(); + fileExistsSpy = undefined; + }); + + afterAll(() => { + if (originalEnv !== undefined) { + process.env.CLAUDE_BIN_PATH = originalEnv; + } else { + delete process.env.CLAUDE_BIN_PATH; + } + fileExistsSpy?.mockRestore(); + }); + + test('returns undefined when nothing is configured', async () => { + const result = await resolver.resolveClaudeBinaryPath(); expect(result).toBeUndefined(); }); - test('returns undefined even with config path set', async () => { - const result = await resolveClaudeBinaryPath('/some/custom/path'); + test('returns undefined when only config path is set (config is binary-mode only)', async () => { + const result = await resolver.resolveClaudeBinaryPath('/some/custom/path'); expect(result).toBeUndefined(); }); - test('returns undefined even with env var set', async () => { - const original = process.env.CLAUDE_BIN_PATH; - process.env.CLAUDE_BIN_PATH = '/some/env/path'; - try { - const result = await resolveClaudeBinaryPath(); - expect(result).toBeUndefined(); - } finally { - if (original !== undefined) { - process.env.CLAUDE_BIN_PATH = original; - } else { - delete process.env.CLAUDE_BIN_PATH; - } - } + test('honors CLAUDE_BIN_PATH env var when file exists', async () => { + process.env.CLAUDE_BIN_PATH = '/usr/local/bin/claude'; + fileExistsSpy = spyOn(resolver, 'fileExists').mockReturnValue(true); + + const result = await resolver.resolveClaudeBinaryPath(); + expect(result).toBe('/usr/local/bin/claude'); + }); + + test('throws when CLAUDE_BIN_PATH is set but file does not exist', async () => { + process.env.CLAUDE_BIN_PATH = '/nonexistent/claude'; + fileExistsSpy = spyOn(resolver, 'fileExists').mockReturnValue(false); + + await expect(resolver.resolveClaudeBinaryPath()).rejects.toThrow( + 'CLAUDE_BIN_PATH is set to "/nonexistent/claude" but the file does not exist' + ); + }); + + test('env var wins over config path in dev mode', async () => { + process.env.CLAUDE_BIN_PATH = '/env/claude'; + fileExistsSpy = spyOn(resolver, 'fileExists').mockReturnValue(true); + + const result = await resolver.resolveClaudeBinaryPath('/config/claude'); + expect(result).toBe('/env/claude'); + }); + + test('falls through to undefined when CLAUDE_BIN_PATH is the empty string', async () => { + // Pin the contract: an unset shell variable that gets exported as empty + // (e.g. `export CLAUDE_BIN_PATH=`) must behave the same as fully unset, + // not throw "file does not exist". + process.env.CLAUDE_BIN_PATH = ''; + const result = await resolver.resolveClaudeBinaryPath(); + expect(result).toBeUndefined(); }); }); diff --git a/packages/providers/src/claude/binary-resolver.ts b/packages/providers/src/claude/binary-resolver.ts index c2273d85d2..5122e8790c 100644 --- a/packages/providers/src/claude/binary-resolver.ts +++ b/packages/providers/src/claude/binary-resolver.ts @@ -6,15 +6,17 @@ * own node_modules location; in compiled binaries that path is frozen to * the build host's filesystem and does not exist on end-user machines. * - * Resolution order (binary mode only): - * 1. `CLAUDE_BIN_PATH` environment variable - * 2. `assistants.claude.claudeBinaryPath` in config - * 3. Autodetect canonical install path (native installer default) - * 4. Throw with install instructions + * Resolution order: + * 1. `CLAUDE_BIN_PATH` environment variable (honored in both modes — escape + * hatch for hosts where the SDK's per-platform binary auto-resolution + * picks the wrong variant, e.g. glibc Linux + musl SDK package) + * 2. `assistants.claude.claudeBinaryPath` in config (binary mode only) + * 3. Autodetect canonical install path (binary mode only — native installer default) + * 4. Throw with install instructions (binary mode only) * - * In dev mode (BUNDLED_IS_BINARY=false), returns undefined so the caller - * omits `pathToClaudeCodeExecutable` entirely and the SDK resolves via its - * normal node_modules lookup. + * In dev mode (BUNDLED_IS_BINARY=false), if no env var is set, returns + * undefined so the caller omits `pathToClaudeCodeExecutable` entirely and + * the SDK resolves via its normal node_modules lookup. */ import { existsSync as _existsSync } from 'node:fs'; import { homedir } from 'node:os'; @@ -53,17 +55,22 @@ const INSTALL_INSTRUCTIONS = 'See: https://archon.diy/docs/reference/configuration#claude'; /** - * Resolve the path to the Claude Code SDK's cli.js. + * Resolve the path to the Claude Code executable (native binary in SDK 0.2.x; + * legacy `cli.js` is still accepted for operators pinned to npm-installed + * SDKs that ship a JS entry point). * - * In dev mode: returns undefined (let SDK resolve via node_modules). - * In binary mode: resolves from env/config, or throws with install instructions. + * In dev mode: honors `CLAUDE_BIN_PATH` if set; otherwise returns undefined + * (let SDK resolve from its bundled per-platform native binary in + * `@anthropic-ai/claude-agent-sdk-`). + * In binary mode: resolves from env/config/autodetect, or throws with + * install instructions. */ export async function resolveClaudeBinaryPath( configClaudeBinaryPath?: string ): Promise { - if (!BUNDLED_IS_BINARY) return undefined; - - // 1. Environment variable override + // 1. Environment variable override — honored in dev mode too, so operators + // on libc mismatches (e.g. glibc host with the SDK's musl variant first in + // its resolution order) can pin a known-good binary without a compiled build. const envPath = process.env.CLAUDE_BIN_PATH; if (envPath) { if (!fileExists(envPath)) { @@ -77,6 +84,8 @@ export async function resolveClaudeBinaryPath( return envPath; } + if (!BUNDLED_IS_BINARY) return undefined; + // 2. Config file override if (configClaudeBinaryPath) { if (!fileExists(configClaudeBinaryPath)) { diff --git a/packages/providers/src/claude/provider.test.ts b/packages/providers/src/claude/provider.test.ts index 16641b1555..a5fd64380f 100644 --- a/packages/providers/src/claude/provider.test.ts +++ b/packages/providers/src/claude/provider.test.ts @@ -18,18 +18,37 @@ mock.module('@anthropic-ai/claude-agent-sdk', () => ({ import { ClaudeProvider, shouldPassNoEnvFile } from './provider'; import * as claudeModule from './provider'; +import * as binaryResolver from './binary-resolver'; describe('shouldPassNoEnvFile', () => { - test('returns true when cliPath is undefined (dev mode — SDK spawns cli.js via Bun)', () => { - expect(shouldPassNoEnvFile(undefined)).toBe(true); + test('returns false when cliPath is undefined (dev mode — SDK 0.2.x resolves a native binary)', () => { + // Pre-0.2.x the SDK shipped cli.js and dev mode = JS. Since 0.2.x the + // SDK ships per-platform native binaries via optional deps. The flag + // (a Bun runtime option) is meaningless to native binaries and gets + // rejected as `error: unknown option '--no-env-file'`. CWD .env leak + // protection comes from stripCwdEnv() at entry, not from this flag. + expect(shouldPassNoEnvFile(undefined)).toBe(false); }); - test('returns true for an explicit cli.js path (npm-installed, SDK spawns via Bun/Node)', () => { + test('returns true for an explicit cli.js path (legacy npm-installed cli.js, SDK spawns via Bun)', () => { expect( shouldPassNoEnvFile('/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js') ).toBe(true); }); + test('returns true for .mjs and .cjs paths (also Bun-runnable JS entry points)', () => { + expect(shouldPassNoEnvFile('/path/to/cli.mjs')).toBe(true); + expect(shouldPassNoEnvFile('/path/to/cli.cjs')).toBe(true); + }); + + test('returns false for non-Bun-runnable JS-adjacent extensions', () => { + // `.ts`/`.tsx`/`.jsx` are deliberately excluded — the SDK never shipped + // those as entry points, so accepting them would only widen misconfiguration. + expect(shouldPassNoEnvFile('/path/to/cli.ts')).toBe(false); + expect(shouldPassNoEnvFile('/path/to/cli.tsx')).toBe(false); + expect(shouldPassNoEnvFile('/path/to/cli.jsx')).toBe(false); + }); + test('returns false for a native binary path (curl installer, SDK execs directly)', () => { expect(shouldPassNoEnvFile('/Users/test/.local/bin/claude')).toBe(false); }); @@ -504,8 +523,10 @@ describe('ClaudeProvider', () => { const callArgs = mockQuery.mock.calls[0][0] as { options: { env: NodeJS.ProcessEnv; executableArgs?: string[] }; }; - // --no-env-file prevents Bun from auto-loading .env in subprocess CWD - expect(callArgs.options.executableArgs).toEqual(['--no-env-file']); + // executableArgs is omitted when cliPath is undefined (dev mode, SDK + // 0.2.x resolves a native binary). CWD .env leak protection comes + // from stripCwdEnv() at entry, not from the --no-env-file flag. + expect(callArgs.options.executableArgs).toBeUndefined(); expect(callArgs.options.env.CUSTOM_USER_KEY).toBe('user-trusted-value'); // Windows uses "Path" casing in spread objects and USERPROFILE instead of HOME const envPath = callArgs.options.env.PATH ?? callArgs.options.env.Path; @@ -520,6 +541,37 @@ describe('ClaudeProvider', () => { else delete process.env.CUSTOM_USER_KEY; }); + test('passes executableArgs: [--no-env-file] when cliPath ends in a Bun-runnable JS extension', async () => { + // Belt-and-suspenders integration check: the dev-mode path is exercised + // in the test above (executableArgs: undefined). This test exercises the + // legacy explicit-cli.js path through the real buildBaseClaudeOptions + // codepath, so a regression in the conditional spread would be caught. + const spy = spyOn(binaryResolver, 'resolveClaudeBinaryPath').mockResolvedValue( + '/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js' + ); + + mockQuery.mockImplementation(async function* () { + // empty + }); + + for await (const _ of client.sendQuery('test', '/workspace')) { + // consume + } + + const callArgs = mockQuery.mock.calls[0][0] as { + options: { + executableArgs?: string[]; + pathToClaudeCodeExecutable?: string; + }; + }; + expect(callArgs.options.executableArgs).toEqual(['--no-env-file']); + expect(callArgs.options.pathToClaudeCodeExecutable).toBe( + '/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js' + ); + + spy.mockRestore(); + }); + test('classifies exit code errors as crash and retries up to 3 times', async () => { const error = new Error('process exited with code 1'); mockQuery.mockImplementation(async function* () { diff --git a/packages/providers/src/claude/provider.ts b/packages/providers/src/claude/provider.ts index 7202f4e19e..8dea3ae5c4 100644 --- a/packages/providers/src/claude/provider.ts +++ b/packages/providers/src/claude/provider.ts @@ -15,8 +15,12 @@ * Binary resolution: * - In compiled binaries, `pathToClaudeCodeExecutable` is resolved from * `CLAUDE_BIN_PATH` env or `assistants.claude.claudeBinaryPath` config; - * see ./binary-resolver.ts. In dev mode the SDK resolves cli.js itself - * from node_modules. + * see ./binary-resolver.ts. In dev mode the resolver returns undefined + * and the SDK picks its bundled per-platform native binary (Mach-O/ELF/PE + * from `@anthropic-ai/claude-agent-sdk-` optional dep). Pre-0.2.x + * SDKs shipped `cli.js` in the package and dev mode resolved that JS file; + * the SDK switched to native binaries in the 0.2.x series. See + * `shouldPassNoEnvFile` for the implications on the `--no-env-file` flag. */ import { query, @@ -82,8 +86,9 @@ function normalizeClaudeUsage(usage?: { * - ~/.archon/.env loaded with override:true as the trusted source */ function buildSubprocessEnv(): NodeJS.ProcessEnv { + // Using || intentionally: empty string should be treated as missing credential const hasExplicitTokens = Boolean( - process.env.CLAUDE_CODE_OAUTH_TOKEN ?? process.env.CLAUDE_API_KEY + process.env.CLAUDE_CODE_OAUTH_TOKEN || process.env.CLAUDE_API_KEY ); const authMode = hasExplicitTokens ? 'explicit' : 'global'; getLog().info( @@ -508,31 +513,43 @@ interface ToolResultEntry { toolCallId?: string; } +/** Bun-runnable JS extensions. `.ts`/`.tsx`/`.jsx` are excluded — the SDK has + * never shipped those as entry points, so accepting them would only widen the + * surface for misconfiguration. */ +const BUN_JS_EXTENSIONS = ['.js', '.mjs', '.cjs'] as const; + /** * Decide whether the Claude subprocess should be spawned with `--no-env-file`. * - * `--no-env-file` is a Bun flag that prevents auto-loading `.env` from the - * target repo cwd into the spawned process. It only applies when the SDK - * spawns the executable via Bun/Node — i.e. when the executable is a `.js` - * file (dev mode resolves cli.js, npm-installed resolves cli.js). For a - * native Claude Code binary (curl/PowerShell installer at - * `~/.local/bin/claude`), the SDK execs the binary directly and the flag - * gets passed to the native binary, which rejects unknown options and - * exits code 1. + * `--no-env-file` is a Bun flag (consumed by the Bun runtime, not by Claude + * Code itself) that prevents auto-loading `.env` from the target repo cwd + * into the spawned process. It only does anything when the SDK spawns a + * Bun-runnable JS file via `bun cli.js …` — Bun parses the flag and skips + * its env autoload. For native Claude Code binaries the flag is meaningless + * and, worse, gets handed to the binary which rejects unknown options. + * + * The dev-mode `cliPath === undefined` path used to imply "JS executable" + * because the SDK shipped `cli.js` inside its package. SDK 0.2.x switched + * to per-platform native binaries (e.g. `@anthropic-ai/claude-agent-sdk-darwin-arm64/claude`), + * so dev mode now resolves to a native executable and the historical + * `undefined → true` heuristic is unsafe. Only return `true` when we have + * an explicit Bun-runnable JS path (`.js`/`.mjs`/`.cjs`) — i.e. when the + * operator pointed Archon at a legacy Bun/Node-runnable cli script. + * Otherwise return `false`. * - * Returning `false` for native binaries is verified safe — the native - * binary does not auto-load `.env` from CWD (probed end-to-end with - * sentinel `.env` and `.env.local` in the workflow CWD; both arrived - * UNSET in the spawned bash tool). The first-layer protection — - * `stripCwdEnv()` in `@archon/paths` (#1067) — removes CWD env keys from - * the parent process before spawn, so the subprocess inherits a clean - * env regardless of executable type. + * Safety: target-repo `.env` leaks are prevented by `stripCwdEnv()` in + * `@archon/paths` (#1067), which deletes CWD `.env` keys from + * `process.env` at every Archon entry point before any subprocess is + * spawned. The native Claude binary does not auto-load `.env` from its + * cwd either (verified end-to-end with sentinel keys). `--no-env-file` + * was belt-and-suspenders for the JS-via-Bun case only. * * Exported so the decision can be unit-tested without needing to mock * `BUNDLED_IS_BINARY` or run the full provider sendQuery pathway. */ export function shouldPassNoEnvFile(cliPath: string | undefined): boolean { - return cliPath === undefined || cliPath.endsWith('.js'); + if (cliPath === undefined) return false; + return BUN_JS_EXTENSIONS.some(ext => cliPath.endsWith(ext)); } /** @@ -550,10 +567,7 @@ function buildBaseClaudeOptions( cliPath: string | undefined ): Options { const isJsExecutable = shouldPassNoEnvFile(cliPath); - getLog().debug( - { cliPath: cliPath ?? null, isJsExecutable, passesNoEnvFile: isJsExecutable }, - 'claude.subprocess_env_file_flag' - ); + getLog().debug({ cliPath: cliPath ?? null, isJsExecutable }, 'claude.subprocess_env_file_flag'); return { cwd, diff --git a/packages/server/package.json b/packages/server/package.json index 3e9fe3d9ef..10e31874cc 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -19,7 +19,7 @@ "@archon/workflows": "workspace:*", "@hono/zod-openapi": "^0.19.6", "dotenv": "^17.2.3", - "hono": "^4.11.4", + "hono": "^4.12.16", "zod": "^3.25.28" }, "devDependencies": {