elanous 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/06ef2eba-d1b99ac6d2ea663c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1413-7179a49332db5a80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/150-25c277137a9638ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1640-bc99ec23bbe29d12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 1751.50b96ea9c37b49b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2300-de059d0ab2a16630.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2684-05b0a81e75298c16.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3026-2ed7e317d9b02102.js +25 -0
- package/apps/pwa/out/_next/static/chunks/3611-77496ada38cd6358.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3717-68957c4dc9ceec90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3929-46ba9af6f9d895cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4015-62c5a3744698a8da.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4217-3c4317a7905f9eeb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4240-3230f230fae2d7d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4271-e53e075ebd48593b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4377.3b125fef79576dfd.js +5 -0
- package/apps/pwa/out/_next/static/chunks/4440-18bb160d95819f4e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4853-35fdbe6bdbcdc0fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → 4daa2798-1363d725c79f3593.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5007-6a3b30e23ff68a6e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5244-12b424c42070f5ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5791-bb82743fa6299562.js → 5348-bbf838e119661eee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5391-8793f88b03b30161.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5611-df00af3e4e4a61bb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5890-dca2321773be829a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6123-37c9dc57ddaa7ab0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6588-b7975304a302ab91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6604-c03961962b6273b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7109-cf8c87e968e83a65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7178-30f77877525a2efb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7262-871563fc9663730b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7294-f0eeebb92aa93459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7429-881af44deed6958f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7527.943753b6ed829f09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/764-a01e858525a190c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7868-e35d5f7985ecd3f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8320-51484af7b01fdfa5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 8617.465ca22a319c95b3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5080-1613752891fcadb4.js → 8918-482b30ce2a8ebf4c.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9131-52f5fad51cceca04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9238.2cd5f47f4bf7a2c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 9704-3221c102623c8d73.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-70c404c7bb6d7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3ffd80166b1050aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f89e9a303515a033.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-4f403cd060442c95.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-9fa879ebf5959096.js → page-0ac5d053f0444d21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-4bcc63e272ae8205.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-880734bd07d62882.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-4472ddf1e2458e69.js → page-72bd384898d88955.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1018bbc9586ecae5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-de86f8c38076dd7b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-70c404c7bb6d7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-cd6447196d8bafe6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-e733630c47fa102b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-d1c1a8357b161875.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-60c514128e81d787.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8b637b7e325e13c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-112f7e477bda77fd.js → page-bccce0babceae30f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-63301d11dbe73836.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-a8cf0dc472060db2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-02fba54881ccfa49.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-d5e8e90f0038f8de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-b1ef6824f494466a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d589bb0f233b1009.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c9bbaebef4cda059.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-3639ce7916b92fed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7450ae7478955653.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-70c404c7bb6d7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b41ca4abfd618452.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-b333dd79ebee3fa9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-8be1227492793655.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-6e03e9371b552e0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-911411e9fca18d03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-792170e649b37088.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c40463466937bc91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-509c798b80384536.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-1cac2aa21d77c417.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-9626799d9b4a2cd1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-84cd419494e16b0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → f6f3850d-7f324c6566002113.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-361487d2da603917.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-607f5beb94cb2e07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-d0222d219e29dddf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-a26980a31126decc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-edb2b37decf23e4d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-e5bea36f183b25e6.js → webpack-2bf59c720fe9542c.js} +1 -1
- package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +1 -0
- package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → ktMqfrBbf8BMGac0jqLrb}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +29 -27
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +29 -27
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +29 -27
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +26 -24
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +26 -24
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +26 -24
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +29 -27
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +29 -27
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +29 -27
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/icons/elanous-16.png +0 -0
- package/apps/pwa/out/icons/elanous-180.png +0 -0
- package/apps/pwa/out/icons/elanous-192.png +0 -0
- package/apps/pwa/out/icons/elanous-32.png +0 -0
- package/apps/pwa/out/icons/elanous-512.png +0 -0
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +26 -24
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +29 -27
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +29 -27
- package/apps/pwa/out/manifest.webmanifest +5 -2
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +26 -24
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +26 -24
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +29 -27
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +29 -27
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +27 -25
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +29 -27
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +26 -24
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +29 -27
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +29 -27
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +29 -27
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +29 -27
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +29 -27
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +26 -24
- package/apps/pwa/out/sw-strategies.js +44 -0
- package/apps/pwa/out/sw.js +6 -44
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +29 -27
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +29 -27
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +29 -27
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +29 -27
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +29 -27
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +29 -27
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +29 -27
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +29 -27
- package/catalog/resources.yaml +6 -6
- package/docs/goal-context/steward-directives-2026-09-28.md +35 -37
- package/graphs/landing/landing-heal.yaml +1 -1
- package/graphs/release/recipes.yaml +8 -1
- package/graphs/release/release-loop.yaml +6 -1
- package/graphs/video/README.md +1 -0
- package/graphs/video/explainer-line.yaml +58 -0
- package/package.json +3 -1
- package/plugins/job-coach/.codex-plugin/plugin.json +18 -1
- package/plugins/job-coach/README.md +12 -0
- package/plugins/job-coach/connectors/ncs/server.ts +4 -3
- package/plugins/job-coach/graphs/gap-judge.ts +44 -0
- package/plugins/job-coach/graphs/recipes.yaml +27 -0
- package/plugins/job-coach/graphs/report-enterprise.yaml +28 -0
- package/plugins/job-coach/graphs/run-step.ts +211 -17
- package/plugins/job-coach/plugin.json +16 -3
- package/plugins/job-coach/skills/enterprise-ai-fit/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-competency/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-needs/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-roadmap/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-task-analysis/SKILL.md +8 -0
- package/plugins/job-coach/skills/interview-to-profile/SKILL.md +18 -1
- package/scripts/backup/elanous-backup.sh +4 -1
- package/scripts/build-docs-site.ts +4 -3
- package/scripts/coord-channel-watch.sh +23 -4
- package/scripts/coord-post.sh +49 -12
- package/scripts/coord-tracks.json +5 -5
- package/scripts/eval-bot-vs-grok.ts +79 -0
- package/scripts/eval-grok-live-baseline.ts +36 -0
- package/scripts/fixtures/front-route-corpus.sample.json +17 -0
- package/scripts/fleet/node-setup.sh +46 -7
- package/scripts/landing-heal/must-fix-match.ts +26 -0
- package/scripts/landing-heal/run-mission.ts +114 -20
- package/scripts/lib/front-route-measurement.ts +131 -0
- package/scripts/lib/tui-sim-keys.ts +42 -0
- package/scripts/measure-front-route-classifier.ts +56 -0
- package/scripts/media/ocr-leak-scan.sh +21 -0
- package/scripts/public-export.ts +2 -3
- package/scripts/public-import-pr.ts +80 -6
- package/scripts/publish-internal-dist.ts +2 -3
- package/scripts/red-sweep-diff.ts +1 -1
- package/scripts/release-loop/auto-approve-node.ts +72 -0
- package/scripts/release-loop/gate-node.ts +332 -44
- package/scripts/release-loop/publish-node.ts +7 -3
- package/scripts/release-loop/release-docs.ts +20 -13
- package/scripts/release-loop/test-durations.ts +63 -0
- package/scripts/release-loop/upgrade-node.ts +3 -2
- package/scripts/test-audit-coverage-per-file.sh +16 -0
- package/scripts/test-audit-meaningless.ts +88 -0
- package/scripts/test-audit-overlap-and-weak.ts +73 -0
- package/scripts/test-deterministic.ts +27 -5
- package/scripts/tui-sim.ts +59 -14
- package/scripts/ux/bump-installed.sh +20 -0
- package/scripts/ux/lib/cdp.ts +72 -0
- package/scripts/ux/record-page.ts +50 -0
- package/skills/explainer-video/SKILL.md +99 -0
- package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -0
- package/skills/explainer-video/engine/build.mjs +431 -0
- package/skills/explainer-video/engine/vo.ts +37 -0
- package/skills/explainer-video/examples/elanous-0.2.5/script.json +155 -0
- package/src/agent/nested-agent-env.ts +1 -0
- package/src/agent-mission/driver.ts +95 -16
- package/src/agent-mission/mission-cli.ts +6 -0
- package/src/agent-mission/resource-ladder.ts +73 -25
- package/src/auth/acp-token.ts +16 -0
- package/src/auth/github-app-token.ts +112 -0
- package/src/autopilot/gh-rest-fallback.ts +177 -0
- package/src/autopilot/pr-manager.ts +51 -4
- package/src/boot/acp-server.ts +2 -16
- package/src/boot/daemon-tools/web-search.ts +2 -1
- package/src/boot/ws-bridge.ts +11 -0
- package/src/chat/index.ts +37 -8
- package/src/chat/log-scroll-key.ts +12 -0
- package/src/chat/submit-trace.ts +30 -0
- package/src/cli/agent-cli.ts +7 -2
- package/src/cli/decisions-cli.ts +94 -0
- package/src/cli/doctor-cli.ts +40 -54
- package/src/cli/doctor-readiness.ts +16 -24
- package/src/cli/intake-cli.ts +1 -1
- package/src/cli/mcp-call.ts +9 -0
- package/src/cli/mcp-gateway-cli.ts +7 -3
- package/src/cli/plugin-cli.ts +109 -11
- package/src/cli/pr-lineage.ts +2 -1
- package/src/cli/pwa-start.ts +3 -0
- package/src/cli/release-cli.ts +94 -7
- package/src/cli/self-update.ts +7 -4
- package/src/cli/start-cli.ts +33 -14
- package/src/dashboard/acp-boot.ts +3 -0
- package/src/dashboard/index.ts +62 -13
- package/src/dashboard/input/no-provider-reply.ts +13 -0
- package/src/dashboard/slash-runtime/unknown-slash-reply.ts +78 -0
- package/src/dashboard/stale-install-check.ts +77 -0
- package/src/decisions/decision-ledger.ts +404 -0
- package/src/domains/outbound-alert.ts +8 -2
- package/src/domains/telegram-kind-route.ts +21 -0
- package/src/execution-loop/dispatch-task.ts +139 -0
- package/src/execution-loop/gate-decision-summary.ts +29 -0
- package/src/execution-loop/gates.ts +197 -0
- package/src/graph-kinds/installed-plugin-nodes.ts +95 -0
- package/src/graph-kinds/plugin-nodes.ts +10 -2
- package/src/graph-runner/runner.ts +35 -5
- package/src/grounding/token.ts +12 -6
- package/src/harness/harness-cli-command.ts +68 -9
- package/src/harness/harness-pod-dispatch.ts +8 -2
- package/src/harness/harness-stop.ts +147 -18
- package/src/harness/self-send-decide.ts +26 -17
- package/src/harness/self-send-target.ts +22 -1
- package/src/harness/worktree-branch-prefix.ts +4 -3
- package/src/index.ts +56 -13
- package/src/instance/resolve.ts +6 -0
- package/src/intake-plane/collect-telegram-saved.ts +5 -0
- package/src/intake-plane/digest.ts +11 -2
- package/src/live/detail-switch.ts +8 -3
- package/src/llm/usable-llm.ts +32 -0
- package/src/loops/registry.ts +27 -5
- package/src/market/publish.ts +59 -3
- package/src/native-tool-catalog.ts +2 -2
- package/src/nexus/api/graph-approvals.ts +125 -0
- package/src/nexus/api/http-server.ts +60 -6
- package/src/nexus/api/log-fabric.ts +10 -1
- package/src/nexus/api/mcp-oauth-hint.ts +10 -9
- package/src/nexus/api/merge-approvals.ts +2 -1
- package/src/nexus/api/meta-api.ts +14 -0
- package/src/nexus/api/plugins-credentials-api.ts +47 -0
- package/src/nexus/api/plugins-market.ts +87 -3
- package/src/nexus/api/pod-credential-api.ts +68 -14
- package/src/nexus/api/public-routes.ts +7 -0
- package/src/nexus/api/trace.ts +57 -6
- package/src/nexus/boot/register-mcp-clients.ts +99 -14
- package/src/nexus/index.ts +33 -32
- package/src/nexus/outbound/router.ts +1 -1
- package/src/onboarding.ts +25 -2
- package/src/plugins/core/host.ts +14 -0
- package/src/plugins/install/market-fetch.ts +4 -3
- package/src/plugins/install/plugin-credentials.ts +106 -0
- package/src/plugins/install/plugin-install.ts +163 -32
- package/src/prompt-library/universal-preamble.ts +8 -2
- package/src/release-loop/checklist.ts +191 -0
- package/src/release-loop/release-note.ts +6 -3
- package/src/self-dev/dev-cli.ts +40 -20
- package/src/self-dev/dev-pipeline.ts +77 -1
- package/src/self-implement/gate-baseline.ts +3 -1
- package/src/self-implement/goal-author.ts +18 -1
- package/src/self-implement/host-regate.ts +6 -2
- package/src/self-implement/orchestrator.ts +7 -2
- package/src/self-implement/seams.ts +4 -2
- package/src/skills/tools/dev-harness.ts +42 -0
- package/src/steward/steward-cards.ts +45 -0
- package/src/steward/triage.ts +50 -14
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +61 -3
- package/src/task-orchestrator/surfaces/pod-command-job.ts +27 -6
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +39 -9
- package/src/task-orchestrator/surfaces/pod-source-bundle.ts +16 -1
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +51 -13
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -28
- package/src/telegram-report.ts +12 -6
- package/src/tui-client/acp-transport-ws-client.ts +72 -5
- package/src/tui.ts +3 -2
- package/src/user-config.ts +28 -2
- package/src/util/spawn-sync-output.ts +15 -0
- package/src/version/code-revision.ts +1 -1
- package/src/version/packed-revision.json +1 -1
- package/src/web-search/free.ts +77 -0
- package/src/web-search/index.ts +2 -0
- package/src/workflow-runtime/deps-bridge.ts +4 -3
- package/src/workflow-runtime/plugin-kind-node.ts +32 -4
- package/src/workflow-runtime/types.ts +2 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1191-7b13a24117a689fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-3fcae5186c64fe6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
- package/apps/pwa/out/_next/static/chunks/319-d37df1b6e0cbeff4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4836-3c93795ab834381e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6090-1bd6077591603947.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7457-f5cb1cc8db9f7453.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-011e1f474efb8b33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-0059d82bdb4812bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-52467f244755917f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-21c3261af6ab377c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1991445b8ea86274.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-4ebfaee2369fe248.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ca7c3cc305c3285a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9951930add3d2051.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4de08d4b766f134b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7a975e2b9db1dfb7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-49270714ef35bb96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-33fbdf353e2a5982.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-259f122e2dd64317.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-65b82726753658e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4b8df3748dc06285.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-542a5c0006da637c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-63ffb347831262ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-98e4e359d97263b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-97e24ac675ce576e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ac6391f26391d89d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f45c9d45ffc43bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-36995b9cb5d98218.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-0c795b577988d63f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b4311e2f71c6735a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-eb2381074df20dba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-b01336e64eba01ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7af7f21c1f2858ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-a98ecd364586c89b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-57f0502dfbfa089b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-3467d2e1abd8a14a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-7842ca23d53ba682.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-ae9ac882912b0198.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-3a0d72e3f783bcef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-f3b39fe8358c2a97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-5a22af3181ac2afb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-1457627bb302a22c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/css/e66b7f5efc8df3f3.css +0 -1
- /package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → ktMqfrBbf8BMGac0jqLrb}/_ssgManifest.js +0 -0
package/src/cli/release-cli.ts
CHANGED
|
@@ -18,8 +18,11 @@ import { tmpdir } from 'node:os';
|
|
|
18
18
|
import { dirname, join, resolve } from 'node:path';
|
|
19
19
|
import type { Command } from 'commander';
|
|
20
20
|
import { debug } from '../debug/log.js';
|
|
21
|
-
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
21
|
+
import { effectiveInstanceRoot, releaseLedgerRoot } from '../instance/resolve.js';
|
|
22
|
+
import { getElanousConfigDirOverride } from '../elanous-config-dir.js';
|
|
22
23
|
import { envLiteral } from '../platform/env-literal.js';
|
|
24
|
+
import { userConfigPath } from '../user-config.js';
|
|
25
|
+
import { addItem, devVersion, listChecklist, removeItem, seedFromRoadmap, setItem, summarizeChecklist, type ChecklistStatus } from '../release-loop/checklist.js';
|
|
23
26
|
|
|
24
27
|
export const DEFAULT_PUBLIC_REPO = 'ElanvitalAI/elanous';
|
|
25
28
|
const SEMVER = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
|
|
@@ -257,7 +260,7 @@ export async function tagRelease(opts: TagOptions, run: Runner = defaultRunner):
|
|
|
257
260
|
return { tagged: true };
|
|
258
261
|
}
|
|
259
262
|
|
|
260
|
-
export async function publishRelease(opts: { dir: string; notesFile: string; yes?: boolean; repoRoot?: string; instanceRoot?: string; log?: (l: string) => void }, run: Runner = defaultRunner): Promise<{ published: boolean; steps: PublishStep[] }> {
|
|
263
|
+
export async function publishRelease(opts: { dir: string; notesFile: string; yes?: boolean; repoRoot?: string; instanceRoot?: string; ledgerRoot?: string; log?: (l: string) => void }, run: Runner = defaultRunner): Promise<{ published: boolean; steps: PublishStep[] }> {
|
|
261
264
|
const log = opts.log ?? ((l: string) => console.log(l));
|
|
262
265
|
const m = readManifest(opts.dir);
|
|
263
266
|
if (!existsSync(opts.notesFile)) throw new Error(`릴리스 본문 파일이 없다: ${opts.notesFile} (초안: ${m.notesDraft ?? 'release notes --from <ref>'})`);
|
|
@@ -273,11 +276,24 @@ export async function publishRelease(opts: { dir: string; notesFile: string; yes
|
|
|
273
276
|
checkSourceTag(m.version, m.sourceCommit, repoRoot, run);
|
|
274
277
|
for (const s of steps) must(run(s.command, s.args, s.cwd), s.what);
|
|
275
278
|
applySourceTag(m.version, m.sourceCommit, repoRoot, run);
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
279
|
+
// 판 기록은 이 우주 ⊕ 기계 단위 릴리스 원장(`releaseLedgerRoot()` · 다음 판 gate 가 기준선으로 읽는 자리) 두 곳에 남긴다.
|
|
280
|
+
// 공개는 이미 끝났으니 한 자리가 실패해도 다른 자리는 쓰고, 실패는 숨기지 않고 알린다(되돌릴 수 없는 단계 뒤라 던지지 않는다).
|
|
281
|
+
const body = `${JSON.stringify({ ...m, publishedAt: new Date().toISOString() }, null, 2)}\n`;
|
|
282
|
+
// 설정 디렉터리를 명시로 바꿔 끼운 경우(시험·`--config-dir`)는 그 우주만 — 다른 원장으로 넘지 않는다(gate 의 `releaseLedgerRoot` 와 같은 규칙).
|
|
283
|
+
const roots = getElanousConfigDirOverride() ? [effectiveInstanceRoot()]
|
|
284
|
+
: [...new Set([opts.instanceRoot ?? effectiveInstanceRoot(), opts.ledgerRoot ?? releaseLedgerRoot()])];
|
|
285
|
+
for (const root of roots) {
|
|
286
|
+
const record = join(root, 'release', m.version, 'release.json');
|
|
287
|
+
try {
|
|
288
|
+
mkdirSync(dirname(record), { recursive: true });
|
|
289
|
+
const temp = `${record}.${process.pid}.tmp`;
|
|
290
|
+
writeFileSync(temp, body, { mode: 0o600 });
|
|
291
|
+
chmodSync(temp, 0o600);
|
|
292
|
+
renameSync(temp, record);
|
|
293
|
+
} catch (error) {
|
|
294
|
+
log(`⚠ 판 기록을 못 남겼다: ${record} — ${error instanceof Error ? error.message : String(error)} (공개는 끝났다 · 다음 판 gate 는 기준선을 다시 스윕한다)`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
281
297
|
log(`✅ 공개: https://github.com/${m.publicRepo}/releases/tag/${m.tag} — 이어서 \`elanous release verify --version ${m.version}\``);
|
|
282
298
|
return { published: true, steps };
|
|
283
299
|
}
|
|
@@ -436,8 +452,79 @@ async function jsonAction<T>(json: boolean | undefined, body: (log: (l: string)
|
|
|
436
452
|
}
|
|
437
453
|
}
|
|
438
454
|
|
|
455
|
+
function checklistCodenames(): Record<string, string> {
|
|
456
|
+
const path = getElanousConfigDirOverride() ? join(effectiveInstanceRoot(), 'config.json') : userConfigPath();
|
|
457
|
+
if (!existsSync(path)) return {};
|
|
458
|
+
const raw = JSON.parse(readFileSync(path, 'utf8')) as { release?: { codenames?: Record<string, string> } };
|
|
459
|
+
return raw.release?.codenames ?? {};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function checklistVersion(input: string | undefined, codenames: Record<string, string>): string {
|
|
463
|
+
const v = input ?? devVersion().replace(/-dev\.\d+$/, '');
|
|
464
|
+
const resolved = isReleaseVersion(v) ? v : Object.entries(codenames).find(([, name]) => name === v)?.[0] ?? v;
|
|
465
|
+
if (!isReleaseVersion(resolved)) throw new Error(`체크리스트 판 또는 별칭이 아니다: ${v}`);
|
|
466
|
+
return resolved;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function checklistOutput(v: string, codenames: Record<string, string>, mode: 'list' | 'status', json?: boolean): void {
|
|
470
|
+
const data = listChecklist(v);
|
|
471
|
+
const summary = summarizeChecklist(data);
|
|
472
|
+
const result = { ...data, codename: codenames[v] ?? '', ...summary };
|
|
473
|
+
if (json) { console.log(JSON.stringify(result)); return; }
|
|
474
|
+
console.log(`${v}${result.codename ? ` (${result.codename})` : ''} · 공개 ${data.released || '없음'} · 개발 ${data.dev}`);
|
|
475
|
+
if (mode === 'list') {
|
|
476
|
+
for (const item of data.items) console.log(`${({ green: '🟢', yellow: '🟡', red: '🔴', done: '✅' } as const)[item.status]} ${item.id} ${item.title}${item.owner ? ` · ${item.owner}` : ''}`);
|
|
477
|
+
} else {
|
|
478
|
+
console.log(`🟢 ${summary.green} · 🟡 ${summary.yellow} · 🔴 ${summary.red} · ✅ ${summary.done}`);
|
|
479
|
+
console.log(`🔴 칸: ${summary.blocked.join(', ') || '없음'}`);
|
|
480
|
+
console.log(`담당별: ${Object.entries(summary.byOwner).map(([owner, count]) => `${owner} ${count}`).join(' · ') || '없음'}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
439
484
|
export function registerReleaseCommands(program: Command): void {
|
|
440
485
|
const release = program.command('release').description('공개 배포 한 판 — prepare(로컬) → publish(--yes) → verify (docs/manual/MANUAL-versioning-and-release-2026-09-25.md)');
|
|
486
|
+
const checklist = release.command('checklist').description('판별 확인표 조회·갱신')
|
|
487
|
+
.option('--version <v>', '판 또는 별칭(기본: package.json 의 개발판에서 -dev.N 제거)')
|
|
488
|
+
.option('--json', '결과 JSON');
|
|
489
|
+
const context = (cmd: Command) => {
|
|
490
|
+
const opts = { ...(cmd.parent?.opts() as { version?: string; json?: boolean }), ...(cmd.opts() as { version?: string; json?: boolean }) };
|
|
491
|
+
const codenames = checklistCodenames();
|
|
492
|
+
return { version: checklistVersion(opts.version, codenames), codenames, json: opts.json };
|
|
493
|
+
};
|
|
494
|
+
const withContext = (cmd: Command) => cmd.option('--version <v>', '판 또는 별칭').option('--json', '결과 JSON');
|
|
495
|
+
withContext(checklist.command('list').description('모든 칸')).action((_opts: unknown, cmd: Command) => {
|
|
496
|
+
const { version, codenames, json } = context(cmd);
|
|
497
|
+
checklistOutput(version, codenames, 'list', json);
|
|
498
|
+
});
|
|
499
|
+
withContext(checklist.command('status').description('상태 요약')).action((_opts: unknown, cmd: Command) => {
|
|
500
|
+
const { version, codenames, json } = context(cmd);
|
|
501
|
+
checklistOutput(version, codenames, 'status', json);
|
|
502
|
+
});
|
|
503
|
+
withContext(checklist.command('add <id> <title>').description('칸 추가')).option('--owner <owner>', '담당').action((id: string, title: string, opts: { owner?: string }, cmd: Command) => {
|
|
504
|
+
const { version, json } = context(cmd);
|
|
505
|
+
const data = addItem(version, { id, title, ...(opts.owner !== undefined ? { owner: opts.owner } : {}) });
|
|
506
|
+
if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 추가`);
|
|
507
|
+
});
|
|
508
|
+
withContext(checklist.command('set <id>').description('칸 상태·근거·담당 갱신'))
|
|
509
|
+
.option('--status <status>', 'green|yellow|red|done').option('--evidence <evidence>', '근거').option('--owner <owner>', '담당')
|
|
510
|
+
.action((id: string, opts: { status?: string; evidence?: string; owner?: string }, cmd: Command) => {
|
|
511
|
+
const { version, json } = context(cmd);
|
|
512
|
+
if (opts.status !== undefined && !['green', 'yellow', 'red', 'done'].includes(opts.status)) throw new Error(`잘못된 상태: ${opts.status}`);
|
|
513
|
+
if (opts.status === undefined && opts.evidence === undefined && opts.owner === undefined) throw new Error('갱신할 칸을 지정하라');
|
|
514
|
+
const data = setItem(version, id, { ...(opts.status !== undefined ? { status: opts.status as ChecklistStatus } : {}), ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}), ...(opts.owner !== undefined ? { owner: opts.owner } : {}) }, process.env.ELANOUS_TRACK || 'cli');
|
|
515
|
+
if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 갱신`);
|
|
516
|
+
});
|
|
517
|
+
withContext(checklist.command('rm <id>').description('칸 삭제')).action((id: string, _opts: unknown, cmd: Command) => {
|
|
518
|
+
const { version, json } = context(cmd);
|
|
519
|
+
const data = removeItem(version, id, process.env.ELANOUS_TRACK || 'cli');
|
|
520
|
+
if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 삭제`);
|
|
521
|
+
});
|
|
522
|
+
withContext(checklist.command('seed').description('마크다운 로드맵에서 빈 칸 들이기')).requiredOption('--from <file>', '로드맵 파일')
|
|
523
|
+
.action((opts: { from: string }, cmd: Command) => {
|
|
524
|
+
const { version, json } = context(cmd);
|
|
525
|
+
const data = seedFromRoadmap(version, readFileSync(opts.from, 'utf8'));
|
|
526
|
+
if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${version} ${data.items.length}칸`);
|
|
527
|
+
});
|
|
441
528
|
release.command('prepare')
|
|
442
529
|
.description('깨끗한 원본 → 공개본 → 공개 저장소 이력 위 커밋 → PWA → 묶음·체크섬 → 로컬 끝까지 (네트워크 쓰기 없음)')
|
|
443
530
|
.requiredOption('--version <x.y.z>', 'package.json 과 같은 버전(먼저 버전 PR 을 착지)')
|
package/src/cli/self-update.ts
CHANGED
|
@@ -235,11 +235,11 @@ export function formatUpdateNotice(result: Pick<SelfUpdateResult, 'installedVers
|
|
|
235
235
|
function defaultNotice(text: string): void {
|
|
236
236
|
// 시험에선 텔레그램으로 나가지 않는다.
|
|
237
237
|
if (process.env.NODE_ENV === 'test') return;
|
|
238
|
-
import('../domains/outbound-alert.js').then((m) => { m.sendOutbound(text, 'report'); }).catch(() => {});
|
|
238
|
+
import('../domains/outbound-alert.js').then((m) => { m.sendOutbound(text, 'ops-report'); }).catch(() => {});
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
function defaultAlert(text: string): void {
|
|
242
|
-
import('../domains/outbound-alert.js').then((m) => { m.sendOutbound(text, 'alert'); }).catch(() => {});
|
|
242
|
+
import('../domains/outbound-alert.js').then((m) => { m.sendOutbound(text, 'ops-alert'); }).catch(() => {});
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
export interface VersionPruneOutcome {
|
|
@@ -296,8 +296,11 @@ export function childPath(env: NodeJS.ProcessEnv = process.env, execPath: string
|
|
|
296
296
|
return current.split(':').includes(bunDir) ? current : [bunDir, current].filter(Boolean).join(':');
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
// bun 1.4 는 maxBuffer(기본 1MiB)를 넘는 출력을 자르고 자식을 죽인다 — 설치·빌드 로그는 그보다 클 수 있다.
|
|
300
|
+
const SPAWN_MAX_BUFFER = 64 * 1024 * 1024;
|
|
301
|
+
|
|
299
302
|
const execute = (command: string, args: string[], cwd: string, options?: { timeout?: number }) => {
|
|
300
|
-
const result = spawnSync(command, args, { cwd, encoding: 'utf8', timeout: options?.timeout, maxBuffer:
|
|
303
|
+
const result = spawnSync(command, args, { cwd, encoding: 'utf8', timeout: options?.timeout, maxBuffer: SPAWN_MAX_BUFFER, env: { ...process.env, PATH: childPath() } });
|
|
301
304
|
return { status: result.status, stdout: result.stdout ?? '', stderr: result.error?.message ?? result.stderr ?? '' };
|
|
302
305
|
};
|
|
303
306
|
|
|
@@ -395,7 +398,7 @@ export async function runReleaseUpdate(options: ReleaseUpdateOptions = {}, deps:
|
|
|
395
398
|
}
|
|
396
399
|
} catch { previous = ''; }
|
|
397
400
|
const run = deps.run ?? ((command: string, args: string[], cwd: string, input?: string, env?: NodeJS.ProcessEnv) => {
|
|
398
|
-
const result = spawnSync(command, args, { cwd, encoding: 'utf8', input, env: { ...process.env, ...env, PATH: childPath() } });
|
|
401
|
+
const result = spawnSync(command, args, { cwd, encoding: 'utf8', input, maxBuffer: SPAWN_MAX_BUFFER, env: { ...process.env, ...env, PATH: childPath() } });
|
|
399
402
|
return { status: result.status, stderr: result.error?.message ?? result.stderr ?? '' };
|
|
400
403
|
});
|
|
401
404
|
try {
|
package/src/cli/start-cli.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { spawn, type SpawnOptions } from 'node:child_process';
|
|
2
2
|
import { createInterface } from 'node:readline/promises';
|
|
3
3
|
import type { Command } from 'commander';
|
|
4
|
-
import {
|
|
4
|
+
import type { DetectedProvider } from '../llm/provider-detect.js';
|
|
5
|
+
import { resolveUsableLlm, type UsableLlm } from '../llm/usable-llm.js';
|
|
5
6
|
import { resolveDaemonEndpoint, type DaemonEndpoint, type ResolveDaemonEndpointOpts } from '../nexus/daemon-endpoint.js';
|
|
6
7
|
import { runBgLaunch } from './bg-launch.js';
|
|
7
8
|
|
|
@@ -13,10 +14,13 @@ export interface StartOptions {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export interface StartState {
|
|
16
|
-
|
|
17
|
+
/** Discovery candidates are diagnostic only; the resolved route controls the plan. */
|
|
18
|
+
providers?: readonly Pick<DetectedProvider, 'provider' | 'auth' | 'available' | 'rank'>[];
|
|
17
19
|
tty: boolean;
|
|
18
20
|
healthy: boolean;
|
|
19
21
|
discoveryFailed?: boolean;
|
|
22
|
+
/** Required selected runtime route; null means resolution failed, not an absent LLM. */
|
|
23
|
+
llm: UsableLlm | null;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
export type StartStepId = 'detect-llm' | 'login' | 'check-daemon' | 'launch-daemon' | 'open-gui' | 'open-tui';
|
|
@@ -32,11 +36,13 @@ export interface StartResult {
|
|
|
32
36
|
|
|
33
37
|
/** No I/O and no credential values: the same observations always produce the same actions. */
|
|
34
38
|
export function planStart(options: StartOptions, state: StartState): StartPlannedStep[] {
|
|
35
|
-
const available = state.
|
|
39
|
+
const available = state.llm?.usable === true;
|
|
40
|
+
// The offered login is the Codex device login: it only fixes the auto route or an explicit openai-codex.
|
|
41
|
+
const codexLoginFixes = state.llm?.provider === undefined || state.llm.provider === 'openai-codex';
|
|
36
42
|
const gui = options.gui === true || options.tui !== true;
|
|
37
43
|
return [
|
|
38
44
|
{ id: 'detect-llm', needed: true },
|
|
39
|
-
{ id: 'login', needed: !state.discoveryFailed && !available && state.tty && options.json !== true && options.login !== false },
|
|
45
|
+
{ id: 'login', needed: !state.discoveryFailed && state.llm !== null && !available && codexLoginFixes && state.tty && options.json !== true && options.login !== false },
|
|
40
46
|
{ id: 'check-daemon', needed: true },
|
|
41
47
|
{ id: 'launch-daemon', needed: !state.healthy },
|
|
42
48
|
{ id: 'open-gui', needed: gui },
|
|
@@ -45,7 +51,9 @@ export function planStart(options: StartOptions, state: StartState): StartPlanne
|
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
export interface StartDeps {
|
|
54
|
+
/** Optional diagnostic seam; never gates runtime route selection. */
|
|
48
55
|
detect?: () => Promise<DetectedProvider[]>;
|
|
56
|
+
resolveLlm?: () => UsableLlm;
|
|
49
57
|
isTty?: () => boolean;
|
|
50
58
|
confirmLogin?: () => Promise<boolean>;
|
|
51
59
|
login?: () => Promise<boolean>;
|
|
@@ -115,21 +123,25 @@ export async function runStart(options: StartOptions = {}, deps: StartDeps = {})
|
|
|
115
123
|
const output = deps.output ?? console.log;
|
|
116
124
|
const steps: StartStep[] = [];
|
|
117
125
|
const record = (id: StartStepId, status: StartStep['status'], detail: string) => steps.push({ id, status, detail });
|
|
118
|
-
let
|
|
126
|
+
let usable: UsableLlm | undefined;
|
|
119
127
|
let discoveryFailed = false;
|
|
128
|
+
if (deps.detect) {
|
|
129
|
+
try { await deps.detect(); }
|
|
130
|
+
catch { /* Diagnostic candidate probe must not gate the runtime route. */ }
|
|
131
|
+
}
|
|
120
132
|
try {
|
|
121
|
-
|
|
122
|
-
record('detect-llm', 'done', 'LLM
|
|
133
|
+
usable = (deps.resolveLlm ?? resolveUsableLlm)();
|
|
134
|
+
record('detect-llm', 'done', 'LLM route resolved');
|
|
123
135
|
} catch {
|
|
124
136
|
discoveryFailed = true;
|
|
125
|
-
record('detect-llm', 'failed', 'LLM
|
|
137
|
+
record('detect-llm', 'failed', 'LLM route could not be resolved');
|
|
126
138
|
}
|
|
127
139
|
const tty = (deps.isTty ?? (() => process.stdin.isTTY === true))();
|
|
128
140
|
let healthy = false;
|
|
129
141
|
const probeHealth = deps.health ?? (() => defaultHealth(deps));
|
|
130
142
|
try { healthy = await probeHealth(); } catch { /* No health response; try launch. */ }
|
|
131
|
-
const plan = planStart(options, {
|
|
132
|
-
let llm: StartResult['llm'] = discoveryFailed ? 'unknown' :
|
|
143
|
+
const plan = planStart(options, { tty, healthy, discoveryFailed, llm: usable ?? null });
|
|
144
|
+
let llm: StartResult['llm'] = discoveryFailed ? 'unknown' : usable?.usable ? 'available' : 'missing';
|
|
133
145
|
if (plan.find((entry) => entry.id === 'login')?.needed) {
|
|
134
146
|
let agreed = false;
|
|
135
147
|
try { agreed = await (deps.confirmLogin ?? confirmLogin)(); } catch { /* No consent. */ }
|
|
@@ -137,8 +149,12 @@ export async function runStart(options: StartOptions = {}, deps: StartDeps = {})
|
|
|
137
149
|
try {
|
|
138
150
|
if (await (deps.login ?? defaultLogin)()) {
|
|
139
151
|
try {
|
|
140
|
-
|
|
141
|
-
|
|
152
|
+
if (deps.detect) {
|
|
153
|
+
try { await deps.detect(); }
|
|
154
|
+
catch { /* Runtime route decides. */ }
|
|
155
|
+
}
|
|
156
|
+
usable = (deps.resolveLlm ?? resolveUsableLlm)();
|
|
157
|
+
llm = usable.usable ? 'available' : 'missing';
|
|
142
158
|
record('login', llm === 'available' ? 'done' : 'failed', llm === 'available' ? 'LLM login detected' : 'No usable LLM login detected');
|
|
143
159
|
} catch {
|
|
144
160
|
llm = 'unknown';
|
|
@@ -147,7 +163,8 @@ export async function runStart(options: StartOptions = {}, deps: StartDeps = {})
|
|
|
147
163
|
} else record('login', 'failed', 'LLM login did not complete');
|
|
148
164
|
} catch { record('login', 'failed', 'LLM login or discovery did not complete'); }
|
|
149
165
|
} else record('login', 'skipped', 'Login declined');
|
|
150
|
-
} else record('login', 'skipped', llm === 'available' ? 'LLM already available' : llm === 'unknown' ? 'LLM discovery unavailable; login not offered'
|
|
166
|
+
} else record('login', 'skipped', llm === 'available' ? 'LLM already available' : llm === 'unknown' ? 'LLM discovery unavailable; login not offered'
|
|
167
|
+
: usable?.provider !== undefined && usable.provider !== 'openai-codex' ? `Codex login does not configure llm.provider=${usable.provider}` : 'Interactive login unavailable');
|
|
151
168
|
record('check-daemon', healthy ? 'done' : 'failed', healthy ? 'Daemon healthy' : 'Daemon did not respond');
|
|
152
169
|
let daemon: StartResult['daemon'] = healthy ? 'running' : 'failed';
|
|
153
170
|
if (plan.find((entry) => entry.id === 'launch-daemon')?.needed) {
|
|
@@ -197,7 +214,9 @@ export async function runStart(options: StartOptions = {}, deps: StartDeps = {})
|
|
|
197
214
|
if (options.json) output(JSON.stringify(result));
|
|
198
215
|
else {
|
|
199
216
|
for (const step of steps) output(`${step.status === 'done' ? '✓' : step.status === 'failed' ? '✗' : '–'} ${step.id}: ${step.detail}`);
|
|
200
|
-
|
|
217
|
+
const selected = usable?.provider;
|
|
218
|
+
if (llm === 'missing' && selected !== undefined && selected !== 'openai-codex') output(`LLM not configured for llm.provider=${selected}; run \`elanous doctor\` for that provider's fix.`);
|
|
219
|
+
else if (llm === 'missing') output('LLM not configured; run `elanous login openai-codex` or `elanous llm detect`.');
|
|
201
220
|
if (llm === 'unknown') output('LLM status unknown; discovery failed. Run `elanous llm detect` to check configuration.');
|
|
202
221
|
}
|
|
203
222
|
return result;
|
|
@@ -83,6 +83,8 @@ export interface DashboardAcpBootDeps {
|
|
|
83
83
|
* callbacks become unused — the daemon's `runTurn` owns those.
|
|
84
84
|
* `getCwd()` is still consulted to populate `newSession(cwd)`. */
|
|
85
85
|
remote?: { url: string; token?: string; label?: string };
|
|
86
|
+
/** Notifies the dashboard when an established remote connection drops. */
|
|
87
|
+
onRemoteDisconnect?: () => void;
|
|
86
88
|
/** Tier 1 daemon-resume — when set together with `remote`,
|
|
87
89
|
* the boot flips from `DashboardSession.attach()`
|
|
88
90
|
* (which mints a new sessionId) to `attachExisting({sessionId})`
|
|
@@ -205,6 +207,7 @@ export async function bootDashboardAcpSession(
|
|
|
205
207
|
url: deps.remote!.url,
|
|
206
208
|
...(deps.remote!.token ? { token: deps.remote!.token } : {}),
|
|
207
209
|
...(deps.remote!.label ? { label: deps.remote!.label } : {}),
|
|
210
|
+
onDisconnect: () => deps.onRemoteDisconnect?.(),
|
|
208
211
|
}),
|
|
209
212
|
});
|
|
210
213
|
recordDashboardAcpBootOutcome(result);
|
package/src/dashboard/index.ts
CHANGED
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
7
7
|
import { join } from 'path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { createStaleInstallCheck, readCurrentInstalledCommit, readDashboardBootCommit, STALE_INSTALL_CHECK_INTERVAL_MS, versionedDashboardInstallDir } from './stale-install-check.js';
|
|
8
10
|
import { spawn, spawnSync } from 'node:child_process';
|
|
9
11
|
import { $ } from 'bun';
|
|
10
12
|
import { LOCAL_SKILLS_DIR, SERVICE_NAMES, DATA_DIR, OBSIDIAN_VAULT } from '../config.js';
|
|
11
13
|
import { dispatchDashboardSessionRuntimeTool } from './session-runtime-dispatch.js';
|
|
12
14
|
import { buildAdRunSetup } from '../ad-pipeline/ad-run-setup.js';
|
|
15
|
+
import { nextChatScrollOffset } from '../chat/log-scroll-key.js';
|
|
13
16
|
import type { CommandRunner } from '../ad-pipeline/higgsfield-backend.js';
|
|
14
17
|
import type { ShootRunOptions } from '../ad-pipeline/shoot-run.js';
|
|
15
18
|
import type { QcThresholds } from '../ad-pipeline/qc.js';
|
|
@@ -602,6 +605,7 @@ import {
|
|
|
602
605
|
import { createDashboardInlineSetupFlow } from './setup-inline.js';
|
|
603
606
|
import { resolveDashboardChatMainThemeCommand } from './input/chat-main-theme-command.js';
|
|
604
607
|
import { resolveDashboardChatMainLogCommand } from './input/chat-main-log-command.js';
|
|
608
|
+
import { noProviderReply } from './input/no-provider-reply.js';
|
|
605
609
|
import {
|
|
606
610
|
dashboardLogHelpLines,
|
|
607
611
|
resolveDashboardLogFilterAction,
|
|
@@ -1066,6 +1070,7 @@ import { bootDashboardHitl } from './hitl-boot.js';
|
|
|
1066
1070
|
import { attachDashboardFilePathToken } from './file-path-attachment.js';
|
|
1067
1071
|
import { bootDashboardSlashExecutor } from './slash-executor-boot.js';
|
|
1068
1072
|
import { buildDashboardSlashRegistry, type DashboardSlashContext } from './slash-runtime/index.js';
|
|
1073
|
+
import { unknownSlashReply } from './slash-runtime/unknown-slash-reply.js';
|
|
1069
1074
|
import { resolveSurfaceUx } from '../agent/surface-ux/build.js';
|
|
1070
1075
|
import type { SurfaceUx } from '../agent/surface-ux/types.js';
|
|
1071
1076
|
import { getDefaultQuestionChannels, type QuestionChannel } from '../hitl/question.js';
|
|
@@ -4239,6 +4244,36 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
4239
4244
|
} catch { /* noop */ }
|
|
4240
4245
|
}, 5_000);
|
|
4241
4246
|
(gitDirtyTimer as unknown as { unref?: () => void }).unref?.();
|
|
4247
|
+
// The running module's installed version, not the user's working directory, is the baseline.
|
|
4248
|
+
const dashboardCodeRoot = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
4249
|
+
const versionDir = versionedDashboardInstallDir(dashboardCodeRoot);
|
|
4250
|
+
const staleInstallCheck = createStaleInstallCheck({
|
|
4251
|
+
codeRoot: dashboardCodeRoot,
|
|
4252
|
+
bootCommit: versionDir ? readDashboardBootCommit(versionDir) : undefined,
|
|
4253
|
+
readInstalledCommit: readCurrentInstalledCommit,
|
|
4254
|
+
now: Date.now,
|
|
4255
|
+
});
|
|
4256
|
+
if (versionDir) {
|
|
4257
|
+
const checkInstall = (): void => {
|
|
4258
|
+
const result = staleInstallCheck.check();
|
|
4259
|
+
debug.log('dashboard.version', 'install-check', {
|
|
4260
|
+
state: result.state, bootCommit: result.bootCommit, installedCommit: result.installedCommit,
|
|
4261
|
+
});
|
|
4262
|
+
if (staleInstallCheck.shouldNotify(result)) {
|
|
4263
|
+
chatLines.push(C.warning(`elanous 가 새 판으로 바뀌었다 (${result.bootCommit!.slice(0, 7)} → ${result.installedCommit!.slice(0, 7)}) · TUI 를 다시 띄워라`));
|
|
4264
|
+
chatScrollOffset = -1;
|
|
4265
|
+
try { draw(); } catch { /* TUI torn down */ }
|
|
4266
|
+
}
|
|
4267
|
+
};
|
|
4268
|
+
checkInstall();
|
|
4269
|
+
const installCheckTimer = setInterval(checkInstall, STALE_INSTALL_CHECK_INTERVAL_MS);
|
|
4270
|
+
installCheckTimer.unref?.();
|
|
4271
|
+
} else {
|
|
4272
|
+
const result = staleInstallCheck.check();
|
|
4273
|
+
debug.log('dashboard.version', 'install-check', {
|
|
4274
|
+
state: result.state, bootCommit: result.bootCommit, installedCommit: result.installedCommit,
|
|
4275
|
+
});
|
|
4276
|
+
}
|
|
4242
4277
|
// Phase T1 — unified terminal matrix. Wraps sessionRegistry during
|
|
4243
4278
|
// the migration window: spawns flow through `matrix.spawn()` so we
|
|
4244
4279
|
// get a single `term:<N>` id space, character/transport fields,
|
|
@@ -15343,7 +15378,13 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
15343
15378
|
).persistToolOutputPreview;
|
|
15344
15379
|
acpPtyAvailable = (await import('../pty-shell/registry.js')).ptyAvailable;
|
|
15345
15380
|
const dashboardAcpBootResult = await bootDashboardAcpSession({
|
|
15346
|
-
...(opts.remote ? {
|
|
15381
|
+
...(opts.remote ? {
|
|
15382
|
+
remote: opts.remote,
|
|
15383
|
+
onRemoteDisconnect: () => {
|
|
15384
|
+
pushChatLine(C.warning(' ⚠ 데몬 연결이 끊겼다 · 입력 중인 글은 그대로다 · 다시 붙으려면 TUI 를 다시 띄워라'));
|
|
15385
|
+
draw();
|
|
15386
|
+
},
|
|
15387
|
+
} : {}),
|
|
15347
15388
|
...(opts.resumeSessionId ? { resumeSessionId: opts.resumeSessionId } : {}),
|
|
15348
15389
|
getCwd: () => getSessionCwd(),
|
|
15349
15390
|
getChatHistory: () => chat.history,
|
|
@@ -16742,6 +16783,17 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
16742
16783
|
: '/quit /clear (run `elanous setup` — no LLM provider available)',
|
|
16743
16784
|
debugLog: debug.enabled ? (event, label, payload) => debug.log(event, label, payload) : undefined,
|
|
16744
16785
|
textInputOpts: {
|
|
16786
|
+
onScrollKey: (name) => {
|
|
16787
|
+
const { rows } = termSize();
|
|
16788
|
+
const viewport = computePromptFrameLogViewportBounds(
|
|
16789
|
+
rows, computePaneH(rows), getLayoutPromptFrame(rows),
|
|
16790
|
+
);
|
|
16791
|
+
chatScrollOffset = nextChatScrollOffset(
|
|
16792
|
+
chatScrollOffset, name, Math.max(1, viewport.height),
|
|
16793
|
+
Math.max(0, chatLines.length - viewport.height),
|
|
16794
|
+
);
|
|
16795
|
+
draw();
|
|
16796
|
+
},
|
|
16745
16797
|
// TUI 부활 후속 — Esc·Esc = /rewind 픽커 (codex backtrack
|
|
16746
16798
|
// 제스처 · resolveEscEscRewind 순수 판정). 빈 버퍼에서만
|
|
16747
16799
|
// prime · 1.5s 창. 힌트는 세션 1회.
|
|
@@ -18453,17 +18505,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18453
18505
|
const handled = await pluginHost.dispatchSlash(cmdLower, args);
|
|
18454
18506
|
if (handled) { chatScrollOffset = -1; continue; }
|
|
18455
18507
|
}
|
|
18456
|
-
const
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
implement: '/harness implement <feature>',
|
|
18460
|
-
dev: '/harness dev <무엇을 왜 고칠지 한 문장>',
|
|
18461
|
-
} as Record<string, string>)[cmdLower];
|
|
18462
|
-
if (retiredHarnessCommand) {
|
|
18463
|
-
chatLines.push(C.warning(`/${cmdLower} has moved; use ${retiredHarnessCommand}`));
|
|
18464
|
-
} else {
|
|
18465
|
-
chatLines.push(C.warning(`Unknown command: /${cmdLower}`));
|
|
18466
|
-
chatLines.push(C.muted('Type /help for the list of commands.'));
|
|
18508
|
+
const replyLines = unknownSlashReply(cmdLower, dashboardSlashRegistry.names());
|
|
18509
|
+
for (const [index, line] of replyLines.entries()) {
|
|
18510
|
+
chatLines.push(index === 0 ? C.warning(line) : C.muted(line));
|
|
18467
18511
|
}
|
|
18468
18512
|
chatScrollOffset = -1;
|
|
18469
18513
|
continue; // stay in input mode
|
|
@@ -18475,7 +18519,12 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
|
|
|
18475
18519
|
|
|
18476
18520
|
// ── Q&A chat — requires at least one LLM provider ──
|
|
18477
18521
|
if (!anyProviderAvailable()) {
|
|
18478
|
-
|
|
18522
|
+
const reply = noProviderReply(input.text);
|
|
18523
|
+
for (const line of reply.lines) {
|
|
18524
|
+
chatLines.push(line.tone === 'warning' ? C.warning(line.text) : C.muted(line.text));
|
|
18525
|
+
}
|
|
18526
|
+
inputPrefixState.set(reply.prefill);
|
|
18527
|
+
nextInitial = inputPrefixState.consumeInitialText('plain');
|
|
18479
18528
|
continue; // stay in input mode
|
|
18480
18529
|
}
|
|
18481
18530
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function noProviderReply(text: string): {
|
|
2
|
+
lines: Array<{ tone: 'warning' | 'muted'; text: string }>;
|
|
3
|
+
prefill: string;
|
|
4
|
+
} {
|
|
5
|
+
const prefill = text.trim();
|
|
6
|
+
return {
|
|
7
|
+
lines: [
|
|
8
|
+
{ tone: 'warning', text: 'No LLM provider available. Run `elanous setup` or `elanous codex setup`.' },
|
|
9
|
+
{ tone: 'muted', text: '보내지 않았다 — 입력칸에 그대로 두었다 · setup 뒤 Enter' },
|
|
10
|
+
],
|
|
11
|
+
prefill,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Keep migration and retirement advice separate from the active slash registry:
|
|
2
|
+
// a retired name must never be advertised as an available command.
|
|
3
|
+
export const RETIRED_SLASH_ADVICE: Readonly<Record<string, { kind: 'moved' | 'retired'; use: string }>> = {
|
|
4
|
+
// These four entry points moved into the harness command.
|
|
5
|
+
ask: { kind: 'moved', use: '/harness ask <무엇을 왜 고칠지 한 문장>' },
|
|
6
|
+
say: { kind: 'moved', use: '/harness ask <무엇을 왜 고칠지 한 문장>' },
|
|
7
|
+
implement: { kind: 'moved', use: '/harness ask <무엇을 왜 고칠지 한 문장>' },
|
|
8
|
+
dev: { kind: 'moved', use: '/harness dev <무엇을 왜 고칠지 한 문장>' },
|
|
9
|
+
// Retired rich-UI and auxiliary commands from the slash registration tests.
|
|
10
|
+
ui: { kind: 'retired', use: '/help' }, workspace: { kind: 'retired', use: '/help' },
|
|
11
|
+
ws: { kind: 'retired', use: '/help' }, window: { kind: 'retired', use: '/help' },
|
|
12
|
+
win: { kind: 'retired', use: '/help' }, scratch: { kind: 'retired', use: '/help' },
|
|
13
|
+
sc: { kind: 'retired', use: '/help' }, 'acp-vw': { kind: 'retired', use: '/help' },
|
|
14
|
+
'claude-vw': { kind: 'retired', use: '/help' }, 'pty-pane': { kind: 'retired', use: '/help' },
|
|
15
|
+
'pty-view': { kind: 'retired', use: '/help' }, fullscreen: { kind: 'retired', use: '/help' },
|
|
16
|
+
fs: { kind: 'retired', use: '/help' }, view: { kind: 'retired', use: '/help' },
|
|
17
|
+
ctoggle: { kind: 'retired', use: '/help' }, cache: { kind: 'retired', use: '/help' },
|
|
18
|
+
perf: { kind: 'retired', use: '/help' }, 'pty-list': { kind: 'retired', use: '/help' },
|
|
19
|
+
ptys: { kind: 'retired', use: '/help' }, 'sweep-tool-results': { kind: 'retired', use: '/help' },
|
|
20
|
+
budget: { kind: 'retired', use: '/tokens' }, b: { kind: 'retired', use: '/tokens' },
|
|
21
|
+
route: { kind: 'retired', use: '/help' }, llm: { kind: 'retired', use: '/model' },
|
|
22
|
+
'browser-cdp': { kind: 'retired', use: '/help' }, bcdp: { kind: 'retired', use: '/help' },
|
|
23
|
+
dm: { kind: 'retired', use: '/help' }, surf: { kind: 'retired', use: '/help' },
|
|
24
|
+
'codex-vw': { kind: 'retired', use: '/help' }, 'skill-triggers': { kind: 'retired', use: '/help' },
|
|
25
|
+
triggers: { kind: 'retired', use: '/help' }, git: { kind: 'retired', use: '/help' },
|
|
26
|
+
memorize: { kind: 'retired', use: '/memory' }, 'mem-compact': { kind: 'retired', use: '/memory' },
|
|
27
|
+
sim: { kind: 'retired', use: '/help' }, simulator: { kind: 'retired', use: '/help' },
|
|
28
|
+
'turn-slider': { kind: 'retired', use: '/help' }, turnslider: { kind: 'retired', use: '/help' },
|
|
29
|
+
tslider: { kind: 'retired', use: '/help' }, playground: { kind: 'retired', use: '/help' },
|
|
30
|
+
pg: { kind: 'retired', use: '/help' }, branch: { kind: 'retired', use: '/help' },
|
|
31
|
+
audit: { kind: 'retired', use: '/help' }, 'substrate-stats': { kind: 'retired', use: '/help' },
|
|
32
|
+
sst: { kind: 'retired', use: '/help' }, usage: { kind: 'retired', use: '/tokens' },
|
|
33
|
+
stats: { kind: 'retired', use: '/help' }, 'codex-setup': { kind: 'retired', use: '/help' },
|
|
34
|
+
'codex-init': { kind: 'retired', use: '/help' }, hint: { kind: 'retired', use: '/help' },
|
|
35
|
+
media: { kind: 'retired', use: '/help' }, mv: { kind: 'retired', use: '/help' },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function distanceAtMostTwo(a: string, b: string): number {
|
|
39
|
+
if (Math.abs(a.length - b.length) > 2) return 3;
|
|
40
|
+
let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
41
|
+
for (let i = 1; i <= a.length; i++) {
|
|
42
|
+
const current = [i];
|
|
43
|
+
for (let j = 1; j <= b.length; j++) {
|
|
44
|
+
current[j] = Math.min(
|
|
45
|
+
current[j - 1]! + 1,
|
|
46
|
+
previous[j]! + 1,
|
|
47
|
+
previous[j - 1]! + (a[i - 1] === b[j - 1] ? 0 : 1),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (Math.min(...current) > 2) return 3;
|
|
51
|
+
previous = current;
|
|
52
|
+
}
|
|
53
|
+
return previous[b.length]!;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Plain lines: the caller applies its existing warning/muted presentation. */
|
|
57
|
+
export function unknownSlashReply(name: string, registered: readonly string[]): string[] {
|
|
58
|
+
const slashName = name.replace(/^\//, '').toLowerCase();
|
|
59
|
+
const advice = Object.hasOwn(RETIRED_SLASH_ADVICE, slashName)
|
|
60
|
+
? RETIRED_SLASH_ADVICE[slashName] : undefined;
|
|
61
|
+
if (advice?.kind === 'moved') {
|
|
62
|
+
return [`/${slashName} has moved; use ${advice.use}`];
|
|
63
|
+
}
|
|
64
|
+
if (advice) return [`/${slashName} is retired; use ${advice.use}`];
|
|
65
|
+
|
|
66
|
+
const suggestions = [...new Set(registered)]
|
|
67
|
+
.filter((candidate) => !Object.hasOwn(RETIRED_SLASH_ADVICE, candidate))
|
|
68
|
+
.map((candidate) => ({ candidate, distance: distanceAtMostTwo(slashName, candidate) }))
|
|
69
|
+
.filter(({ distance }) => distance <= 2)
|
|
70
|
+
.sort((a, b) => a.distance - b.distance || a.candidate.localeCompare(b.candidate))
|
|
71
|
+
.slice(0, 3)
|
|
72
|
+
.map(({ candidate }) => `/${candidate}`);
|
|
73
|
+
return [
|
|
74
|
+
`Unknown command: /${slashName}`,
|
|
75
|
+
...(suggestions.length ? [`Did you mean: ${suggestions.join(', ')}?`] : []),
|
|
76
|
+
'Type /help for the list of commands.',
|
|
77
|
+
];
|
|
78
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { readInstallMetadataCommit } from '../version/code-revision.js';
|
|
6
|
+
|
|
7
|
+
export type StaleInstallResult = {
|
|
8
|
+
state: 'same' | 'changed' | 'unknown' | 'not-installed';
|
|
9
|
+
bootCommit: string | undefined;
|
|
10
|
+
installedCommit: string | undefined;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const STALE_INSTALL_CHECK_INTERVAL_MS = 60_000;
|
|
14
|
+
|
|
15
|
+
/** Identify the immutable installed version containing this module, never the session cwd. */
|
|
16
|
+
export function versionedDashboardInstallDir(
|
|
17
|
+
codeRoot: string,
|
|
18
|
+
installRoot = join(homedir(), '.local', 'share', 'elanous'),
|
|
19
|
+
): string | undefined {
|
|
20
|
+
try {
|
|
21
|
+
const packageRoot = realpathSync(codeRoot);
|
|
22
|
+
if (basename(packageRoot) !== 'elanous' || basename(dirname(packageRoot)) !== 'node_modules') return undefined;
|
|
23
|
+
const versionDir = dirname(dirname(packageRoot));
|
|
24
|
+
if (dirname(versionDir) !== resolve(installRoot, 'versions')) return undefined;
|
|
25
|
+
return versionDir;
|
|
26
|
+
} catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createStaleInstallCheck(input: {
|
|
32
|
+
bootCommit: string | undefined;
|
|
33
|
+
readInstalledCommit: () => string | undefined;
|
|
34
|
+
now: () => number;
|
|
35
|
+
codeRoot?: string;
|
|
36
|
+
installRoot?: string;
|
|
37
|
+
}) {
|
|
38
|
+
const installed = input.codeRoot === undefined
|
|
39
|
+
|| versionedDashboardInstallDir(input.codeRoot, input.installRoot) !== undefined;
|
|
40
|
+
const notifiedCommits = new Set<string>();
|
|
41
|
+
let lastReadAt = -Infinity;
|
|
42
|
+
let lastResult: StaleInstallResult | undefined;
|
|
43
|
+
const check = (): StaleInstallResult => {
|
|
44
|
+
if (!installed) return { state: 'not-installed', bootCommit: input.bootCommit, installedCommit: undefined };
|
|
45
|
+
const time = input.now();
|
|
46
|
+
if (lastResult && time - lastReadAt < STALE_INSTALL_CHECK_INTERVAL_MS) return lastResult;
|
|
47
|
+
lastReadAt = time;
|
|
48
|
+
let installedCommit: string | undefined;
|
|
49
|
+
try { installedCommit = input.readInstalledCommit(); } catch { /* unreadable metadata */ }
|
|
50
|
+
const result: StaleInstallResult = {
|
|
51
|
+
state: !input.bootCommit || !installedCommit
|
|
52
|
+
? 'unknown'
|
|
53
|
+
: input.bootCommit === installedCommit ? 'same' : 'changed',
|
|
54
|
+
bootCommit: input.bootCommit,
|
|
55
|
+
installedCommit,
|
|
56
|
+
};
|
|
57
|
+
lastResult = result;
|
|
58
|
+
return result;
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
check,
|
|
62
|
+
shouldNotify(result: StaleInstallResult): boolean {
|
|
63
|
+
if (result.state !== 'changed' || !result.installedCommit || notifiedCommits.has(result.installedCommit)) return false;
|
|
64
|
+
notifiedCommits.add(result.installedCommit);
|
|
65
|
+
return true;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Only the current pointer is polled; boot metadata is read once from the immutable version. */
|
|
71
|
+
export function readDashboardBootCommit(versionDir: string): string | undefined {
|
|
72
|
+
return readInstallMetadataCommit(join(versionDir, 'install.json'));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function readCurrentInstalledCommit(installRoot = join(homedir(), '.local', 'share', 'elanous')): string | undefined {
|
|
76
|
+
return readInstallMetadataCommit(join(installRoot, 'current', 'install.json'));
|
|
77
|
+
}
|