elanous 0.2.6 → 0.2.8
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/.env.example +10 -7
- package/README.md +4 -0
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3995-bee512a7063036ce.js → 2375-cb6debea56cce4fb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +25 -0
- package/apps/pwa/out/_next/static/chunks/{8d0d9faf-97887ff99713b84e.js → 2ea96958-e8c7e112de50b067.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5329.8b7e4bca684b6eec.js → 3403.4bc97b4066945a94.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +1 -0
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7633.0654fe6ea259c1ee.js → 4323.c5dfb6f503304775.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4836-3084c58c1725a40b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2984-977cfa339fcd65a5.js → 5080-1613752891fcadb4.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{763-7d0e1913b5074e98.js → 5791-bb82743fa6299562.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4240-7153881b0b377306.js → 6062-73e68343a9814f6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7032-f4ef698518fba61c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{91ff6dda-3b33d42d20657429.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8894-7317998f6cb97d8c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9454-a3e0953307216c58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9aa47b0bc6d570ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f4d2ecbbae6088d6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-2053e49930c82748.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-d4b6df905f185055.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a70ba27c33003dd0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-186fc6952f027acc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-18166f885d831d3e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-1c9d6cfe68b121c2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-cbbe03c0da7b081a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c108ff1fdb665c97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-147a328753d983e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-799395a9df7d95aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-6a8158b71d1fe9d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-9567560270857531.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-e7ed3d010153d818.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-c0c0d19d51342d3e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2e002ad8faa43291.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-b672e02e4bec8deb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-1ba4a5d857276334.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-9db2226f38c542fb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-70ee65f5f7a151dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-0bb8e43203efb23b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-092e1bcf27d751cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-eb52580c4b75b905.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7464c1987dd76e6d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-e573437b0a1c728f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-294300625e49e4d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-085847b088675978.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-9fe9d38c36999eb4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-d8ab4d1489223459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-6141f67f531d8b6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-295a02b1026b8255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-1eb9a0e1e054209d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-82efa177a30f3741.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d0be51cec4f62606.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-2e367075ffdbc062.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-647ed2b66c1d49f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ba058c56e38f2792.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-37b8bf78a3c6eec5.js → webpack-e5bea36f183b25e6.js} +1 -1
- package/apps/pwa/out/_next/static/css/5ef72a618a8994f3.css +1 -0
- package/apps/pwa/out/_next/static/lywc8m_HDzH9MKSDQhyEj/_buildManifest.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -0
- package/apps/pwa/out/consult/index.txt +32 -0
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -0
- package/apps/pwa/out/exec/index.txt +32 -0
- package/apps/pwa/out/field/index.html +1 -0
- package/apps/pwa/out/field/index.txt +32 -0
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/bin/elanous.cjs +107 -0
- package/graphs/field/field-feed.yaml +24 -0
- package/graphs/field/recipes.yaml +12 -0
- package/graphs/landing/landing-heal.yaml +3 -1
- package/graphs/landing/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +10 -0
- package/graphs/release/release-loop.yaml +10 -3
- package/graphs/test-diet/recipes.yaml +9 -0
- package/graphs/test-diet/test-diet.yaml +22 -0
- package/package.json +6 -5
- package/persona-presets/_index.yaml +10 -0
- package/persona-presets/_schema.json +146 -0
- package/persona-presets/dana.yaml +26 -0
- package/persona-presets/haru.yaml +27 -0
- package/persona-presets/kai.yaml +27 -0
- package/persona-presets/leo.yaml +27 -0
- package/persona-presets/mira.yaml +30 -0
- package/persona-presets/nari.yaml +27 -0
- package/persona-presets/sora.yaml +27 -0
- package/persona-presets/yuna.yaml +27 -0
- package/plugins/card-followup/README.md +13 -4
- package/plugins/card-followup/graphs/card-followup.yaml +3 -1
- package/plugins/card-followup/graphs/recipes.yaml +5 -1
- package/plugins/card-followup/graphs/run-step.ts +122 -18
- package/plugins/card-followup/graphs/sales-strategy.ts +173 -0
- package/plugins/card-followup/plugin.json +1 -1
- package/plugins/doc-draft/README.md +13 -0
- package/plugins/doc-draft/examples/brief-sample.json +8 -0
- package/plugins/doc-draft/graphs/doc-draft.yaml +14 -0
- package/plugins/doc-draft/graphs/recipes.yaml +9 -0
- package/plugins/doc-draft/graphs/run-step.ts +113 -0
- package/plugins/doc-draft/plugin.json +24 -0
- package/plugins/geo-check/README.md +3 -1
- package/plugins/geo-check/graphs/run-step.ts +58 -13
- package/plugins/geo-check/plugin.json +1 -1
- package/plugins/job-coach/.codex-plugin/plugin.json +1 -1
- package/plugins/job-coach/README.md +1 -1
- package/plugins/job-coach/graphs/run-step.ts +2 -2
- package/plugins/job-coach/plugin.json +1 -1
- package/plugins/job-coach/plugin.ts +1 -1
- package/scripts/affinity/endcard.sh +3 -2
- package/scripts/affinity/reframe.sh +3 -1
- package/scripts/ci-public-export-leak-gate.ts +48 -0
- package/scripts/codex-quota-alert.ts +122 -258
- package/scripts/coord-tracks.json +4 -4
- package/scripts/field-feed/deliver-node.ts +57 -0
- package/scripts/field-feed/feed-node.ts +67 -0
- package/scripts/field-feed/lib.ts +164 -0
- package/scripts/field-feed/reel-node.ts +24 -0
- package/scripts/field-feed/start.ts +62 -0
- package/scripts/fixtures/vflow-sample.ndjson +6 -0
- package/scripts/install.sh +109 -21
- package/scripts/landing-heal/clean-checkout.ts +114 -0
- package/scripts/landing-heal/run-mission.ts +38 -3
- package/scripts/lib/pwa-scenarios/scenarios.ts +2 -1
- package/scripts/persona-presets/lint.ts +79 -0
- package/scripts/public-export.ts +8 -4
- package/scripts/release-loop/baseline-from-logs.ts +92 -0
- package/scripts/release-loop/checklist-gate-node.ts +2 -2
- package/scripts/release-loop/env-known-failures.ts +31 -0
- package/scripts/release-loop/gate-node.ts +108 -22
- package/scripts/release-loop/known-issues-node.ts +159 -0
- package/scripts/release-loop/npm-publish-node.ts +124 -0
- package/scripts/release-loop/ops-upgrade-node.ts +95 -0
- package/scripts/release-loop/publish-node.ts +23 -1
- package/scripts/release-loop/release-readiness.ts +58 -0
- package/scripts/release-loop/shard-plan.ts +17 -0
- package/scripts/release-loop/unattended-release.ts +93 -0
- package/scripts/storyboard/storyboard.ts +167 -0
- package/scripts/test-diet/lib.ts +88 -0
- package/scripts/test-diet/node.ts +102 -0
- package/scripts/ux/measure-first-text.ts +102 -0
- package/scripts/vflow-collect.ts +20 -7
- package/scripts/vflow-search.ts +73 -0
- package/scripts/video-free-line-test-walker.ts +36 -0
- package/scripts/video-pipeline.ts +2 -2
- package/skills/explainer-video/SKILL.md +12 -9
- package/skills/explainer-video/engine/build.mjs +1 -1
- package/skills/explainer-video/engine/reel.mjs +138 -31
- package/skills/explainer-video/engine/reel.sh +2 -1
- package/skills/explainer-video/examples/elanous-0.2.5/script.json +1 -1
- package/skills/grill-me/SKILL.md +1 -1
- package/skills/omni-digest/docs/diagrams/noncode-terminal-promotion-path-2026-08-13.mmd +1 -1
- package/src/a2a/server.ts +2 -0
- package/src/acp/auth-methods.ts +30 -0
- package/src/acp/elanous-aware-peers.ts +30 -0
- package/src/acp/server.ts +96 -20
- package/src/agent/monad-agent-turn.ts +25 -6
- package/src/agent/shared-app-tools.ts +5 -3
- package/src/agent-substrate/review-intent.ts +6 -2
- package/src/agent-substrate/shard-boundary-intent.ts +70 -0
- package/src/auth/github-app-token.ts +4 -1
- package/src/autopilot/track-agent-shadow.ts +151 -0
- package/src/autopilot/track-agent.ts +23 -3
- package/src/boot/acp-server-help.ts +16 -0
- package/src/boot/daemon-prompt-turn.ts +31 -10
- package/src/boot/daemon-tools/index.ts +7 -1
- package/src/budget/codex-credit-pace.ts +84 -0
- package/src/budget/codex-credit-plan.ts +154 -0
- package/src/budget/codex-reset-credit-state.ts +2 -0
- package/src/channel-import/detect.ts +191 -0
- package/src/channel-import/run.ts +111 -0
- package/src/chat/chat-cli.ts +4 -1
- package/src/chat/short-question.ts +40 -0
- package/src/cli/agent-cli.ts +24 -15
- package/src/cli/ask-bare.ts +42 -0
- package/src/cli/cli-user-error.ts +25 -0
- package/src/cli/codex-credit-plan-view.ts +25 -0
- package/src/cli/config-test-sync.ts +4 -0
- package/src/cli/decisions-cli.ts +13 -3
- package/src/cli/dependence-gauge.ts +237 -0
- package/src/cli/doctor-bundle.ts +128 -0
- package/src/cli/doctor-cli.ts +41 -1
- package/src/cli/doctor-local-llm.ts +95 -0
- package/src/cli/doctor-modes.ts +48 -0
- package/src/cli/import-cli.ts +60 -0
- package/src/cli/intake-cli.ts +12 -2
- package/src/cli/intake-source-cli.ts +58 -0
- package/src/cli/machine-cli.ts +23 -0
- package/src/cli/mcp-login.ts +13 -4
- package/src/cli/msg-cli.ts +137 -0
- package/src/cli/persona-cli.ts +116 -0
- package/src/cli/plugin-cli.ts +25 -0
- package/src/cli/pr-cli.ts +36 -0
- package/src/cli/provider-cli.ts +5 -0
- package/src/cli/release-cli.ts +50 -8
- package/src/cli/release-run-if-ready.ts +48 -0
- package/src/cli/setup-cli.ts +27 -6
- package/src/cli/setup-recommend.ts +8 -0
- package/src/cli/skills-cli.ts +173 -0
- package/src/cli/start-cli.ts +13 -4
- package/src/cli/usage-cli.ts +11 -1
- package/src/codex/setup.ts +37 -16
- package/src/dashboard/index.ts +18 -2
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -2
- package/src/dashboard/slash-runtime/help-from-registry.ts +4 -2
- package/src/decisions/decision-cards.ts +291 -0
- package/src/decisions/decision-ledger.ts +5 -2
- package/src/decisions/discord-decision-cards.ts +133 -0
- package/src/decisions/telegram-decision-cards.ts +100 -0
- package/src/directives/version-at.ts +77 -0
- package/src/discord.ts +73 -6
- package/src/domains/core-tools.ts +37 -14
- package/src/domains/outbound-alert.ts +94 -12
- package/src/domains/repeated-failure.ts +1 -1
- package/src/error-report/report.ts +200 -0
- package/src/exec-requests/planner.ts +146 -0
- package/src/exec-requests/runner.ts +378 -0
- package/src/exec-requests/store.ts +78 -0
- package/src/field/field-feed-auto.ts +48 -0
- package/src/field/field-media.ts +8 -3
- package/src/field/field-reel.ts +203 -0
- package/src/grok/credential.ts +7 -5
- package/src/harness/aside-run-status.ts +33 -0
- package/src/harness/browser-verify.ts +48 -17
- package/src/harness/harness-cli-command.ts +25 -3
- package/src/harness/image-load-state.ts +21 -0
- package/src/harness/process-garbage.ts +45 -0
- package/src/hooks/error-report.ts +210 -0
- package/src/hooks/receiver.ts +41 -1
- package/src/import/apply.ts +128 -0
- package/src/import/plan.ts +197 -0
- package/src/index.ts +168 -21
- package/src/input-core/text-entry.ts +13 -5
- package/src/instance/leader.ts +13 -1
- package/src/intake-plane/collect-github.ts +5 -3
- package/src/intake-plane/collect-telegram-saved.ts +15 -3
- package/src/intake-plane/discord-seat-work.ts +56 -0
- package/src/intake-plane/intake-sources.ts +202 -0
- package/src/intake-plane/items.ts +28 -12
- package/src/intake-plane/seat-answer.ts +96 -0
- package/src/intake-plane/seat-doc-route.ts +221 -0
- package/src/intake-plane/submit-intake-work.ts +9 -1
- package/src/intake-plane/telegram-seat-work.ts +72 -0
- package/src/llm/model-defaults.ts +24 -1
- package/src/llm.ts +5 -4
- package/src/machines/machine-ledger.ts +127 -0
- package/src/market/pack-internal-refs.ts +28 -0
- package/src/market/publish.ts +31 -4
- package/src/maturity/feature-maturity.ts +65 -0
- package/src/model-tier/turn-tier.ts +24 -0
- package/src/msg/msg-store.ts +149 -0
- package/src/native-tool-catalog.ts +2 -1
- package/src/nexus/api/chat-fast-path-config.ts +28 -0
- package/src/nexus/api/consult-requests.ts +128 -0
- package/src/nexus/api/discord-trigger-bot.ts +3 -0
- package/src/nexus/api/error-reports.ts +108 -0
- package/src/nexus/api/exec-requests.ts +51 -0
- package/src/nexus/api/field-uploads.ts +40 -3
- package/src/nexus/api/graph-approvals-feed.ts +140 -0
- package/src/nexus/api/graph-approvals.ts +71 -4
- package/src/nexus/api/http-server.ts +82 -8
- package/src/nexus/api/outbound-report.ts +7 -0
- package/src/nexus/api/running-runs-background.ts +68 -0
- package/src/nexus/api/running-runs-child.ts +6 -0
- package/src/nexus/api/seat-requests.ts +254 -0
- package/src/nexus/api/share-captures.ts +91 -0
- package/src/nexus/api/skill-problems.ts +56 -0
- package/src/nexus/api/terminals.ts +24 -8
- package/src/nexus/api/trace-run-scan.ts +37 -0
- package/src/nexus/api/trace.ts +19 -3
- package/src/nexus/index.ts +28 -1
- package/src/nexus/setup-status.ts +11 -7
- package/src/notify/llm-budget-alert.ts +16 -10
- package/src/oauth/browser-availability.ts +14 -0
- package/src/oauth/codex-account-rotation.ts +26 -4
- package/src/oauth/codex-account-store.ts +20 -2
- package/src/oauth/codex-browser-login.ts +173 -0
- package/src/oauth/codex-quota-policy.ts +16 -0
- package/src/oauth/codex.ts +4 -1
- package/src/oauth/fallback-chain.ts +5 -5
- package/src/oauth/store.ts +42 -5
- package/src/onboarding/continue-here.ts +34 -0
- package/src/onboarding/entry-hints.ts +8 -0
- package/src/onboarding/full-screen-io.ts +35 -3
- package/src/onboarding/help/skills.md +1 -1
- package/src/onboarding/non-interactive.ts +1 -0
- package/src/onboarding/subscription-hint.ts +27 -0
- package/src/onboarding/web-first.ts +90 -0
- package/src/onboarding.ts +287 -26
- package/src/persona/presets.ts +105 -0
- package/src/plugins/maker/node-install.ts +92 -0
- package/src/plugins/maker/node-maker.ts +139 -0
- package/src/plugins/maker/plugin-maker.ts +19 -7
- package/src/policy/llm-policy.ts +268 -0
- package/src/prompt-library/session-guidance.ts +7 -1
- package/src/prompt-library/universal-preamble.ts +2 -2
- package/src/relay/relay-cli.ts +20 -0
- package/src/relay/relay-server.ts +220 -0
- package/src/release-loop/checklist.ts +37 -13
- package/src/self-dev/decompose.ts +1 -0
- package/src/self-dev/dev-pipeline.ts +6 -3
- package/src/self-dev/orchestrate.ts +18 -11
- package/src/self-dev/run-record-mirror.ts +44 -0
- package/src/self-dev/run-store.ts +12 -3
- package/src/self-implement/harness-policy-gates.ts +4 -1
- package/src/self-implement/report-origin.ts +12 -1
- package/src/session/chat.ts +17 -5
- package/src/shell-env-bootstrap.ts +44 -7
- package/src/skills/connect-claude-plugin.ts +142 -0
- package/src/skills/connect.ts +70 -0
- package/src/skills/index.ts +29 -1
- package/src/skills/repair.ts +124 -0
- package/src/skills/runner.ts +11 -2
- package/src/skills/shared-agent-skill-roots.ts +25 -0
- package/src/skills/trigger-extract.ts +1 -1
- package/src/steward/failure-streak.ts +78 -0
- package/src/steward/steward-cards.ts +24 -0
- package/src/steward/triage-budget.ts +127 -0
- package/src/steward/triage-plan.ts +75 -0
- package/src/steward/triage.ts +244 -42
- package/src/task-cards/card-store.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-command-job.ts +16 -8
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +68 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +78 -4
- package/src/telegram-commands.ts +13 -0
- package/src/telegram.ts +72 -2
- package/src/tool-runtime/elanous-skills-list-runtime.ts +3 -3
- package/src/tool-runtime/self-cognition-runtimes.ts +160 -2
- package/src/user-config.ts +189 -15
- package/src/version/packed-revision.json +1 -1
- package/src/video-pipeline/recipes/character.ts +42 -5
- package/src/video-pipeline/recipes/ffmpeg.ts +1 -1
- package/src/video-pipeline/recipes/free-line.ts +16 -3
- package/src/video-pipeline/recipes/remote.ts +7 -1
- package/src/video-pipeline/vflow-reference.ts +109 -0
- package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3190-e9282753e7ebde85.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +0 -25
- package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +0 -1
- package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +0 -1
- package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +0 -1
- package/apps/pwa/out/_next/static/qMI0jnlZaBjjm4aIDYNGM/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{qMI0jnlZaBjjm4aIDYNGM → lywc8m_HDzH9MKSDQhyEj}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { closeSync, existsSync, lstatSync, mkdtempSync, mkdirSync, openSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { homedir, platform, arch } from 'node:os';
|
|
4
|
+
import { join, resolve, sep } from 'node:path';
|
|
5
|
+
import { getElanousConfigDir } from '../elanous-config-dir.js';
|
|
6
|
+
import { resolveCurrentInstance } from '../instance/current.js';
|
|
7
|
+
import { LogStore, logsDbPath, type LogStoreRow } from '../mss/logging/log-store.js';
|
|
8
|
+
import { cliVersion, codeRevision, packageVersion } from '../version/code-revision.js';
|
|
9
|
+
import { runDoctor, defaultReadInstallPrefix, type DoctorOptions, type DoctorReport } from './doctor-cli.js';
|
|
10
|
+
|
|
11
|
+
const REDACTED = '<redacted>';
|
|
12
|
+
|
|
13
|
+
/** Recursively redact named secrets and secret-shaped text before serialization. */
|
|
14
|
+
export function redactForBundle(value: unknown, home: string = homedir(), redactNamedFields: boolean = true): unknown {
|
|
15
|
+
if (typeof value === 'string') {
|
|
16
|
+
let text = value;
|
|
17
|
+
if (home && home !== sep) {
|
|
18
|
+
// Match the home as a path component, including JSON-escaped slashes.
|
|
19
|
+
const escaped = home.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
20
|
+
text = text.replace(new RegExp(`${escaped}(?=[/\\\\]|$)`, 'g'), '~');
|
|
21
|
+
}
|
|
22
|
+
// By kind, not by the last example found: headers that carry credentials, credentials inside URLs,
|
|
23
|
+
// known token shapes, and any `name=value` / `"name": "value"` whose name says it is a credential.
|
|
24
|
+
return text
|
|
25
|
+
.replace(/\b((?:Proxy-)?Authorization\s*:\s*)[^\r\n"']+/gi, `$1${REDACTED}`)
|
|
26
|
+
.replace(/\b((?:Set-)?Cookie\s*:\s*)[^\r\n"']+/gi, `$1${REDACTED}`)
|
|
27
|
+
.replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@"'<>]+@/gi, `$1${REDACTED}@`)
|
|
28
|
+
.replace(/(["']?(?:[\w-]*(?:token|key|secret|password|passwd|pwd|cookie|session|credential|auth))["']?\s*[:=]\s*["']?)[^\s"',}&]+/gi, `$1${REDACTED}`)
|
|
29
|
+
.replace(/\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]{8,}/g, `$1 ${REDACTED}`)
|
|
30
|
+
.replace(/\b(?:sk-[A-Za-z0-9_-]+|gh[pousr]_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+|glpat-[A-Za-z0-9_-]+|xox[a-z]-[A-Za-z0-9-]+|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35})\b/g, REDACTED)
|
|
31
|
+
.replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, REDACTED);
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(value)) return value.map((entry) => redactForBundle(entry, home, redactNamedFields));
|
|
34
|
+
if (value && typeof value === 'object') {
|
|
35
|
+
const result: Record<string, unknown> = Object.create(null);
|
|
36
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
37
|
+
const safeKey = redactForBundle(key, home) as string;
|
|
38
|
+
result[safeKey] = redactNamedFields && /(?:token|key|secret|password|passwd|pwd|authorization|cookie|session|credential|auth)/i.test(key)
|
|
39
|
+
? REDACTED : redactForBundle(entry, home, redactNamedFields);
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface DoctorBundleOptions {
|
|
47
|
+
outDir: string;
|
|
48
|
+
now?: Date;
|
|
49
|
+
/** Read-only doctor seams for isolated tests. */
|
|
50
|
+
doctorOptions?: DoctorOptions;
|
|
51
|
+
home?: string;
|
|
52
|
+
configDir?: string;
|
|
53
|
+
logDbPath?: string;
|
|
54
|
+
doctorReport?: DoctorReport;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface DoctorBundleResult { path: string; files: string[]; bytes: number }
|
|
58
|
+
|
|
59
|
+
/** Bundle only generated, redacted entries. Never put an unprocessed source file into tar. */
|
|
60
|
+
export function buildDoctorBundle({ outDir, now = new Date(), doctorOptions = {}, home = homedir(), configDir = getElanousConfigDir(), logDbPath = logsDbPath(), doctorReport }: DoctorBundleOptions): DoctorBundleResult {
|
|
61
|
+
const instant = now.toISOString();
|
|
62
|
+
const stamp = instant.replace(/[-:]/g, '').slice(0, 15).replace('T', '-');
|
|
63
|
+
mkdirSync(outDir, { recursive: true, mode: 0o700 });
|
|
64
|
+
const destination = resolve(outDir, `elanous-diagnostics-${stamp}.tar.gz`);
|
|
65
|
+
if (existsSync(destination)) throw new Error('diagnostics archive already exists');
|
|
66
|
+
const work = mkdtempSync(join(outDir, '.elanous-diagnostics-'));
|
|
67
|
+
const files: string[] = [];
|
|
68
|
+
const put = (name: string, data: unknown): void => {
|
|
69
|
+
writeFileSync(join(work, name), JSON.stringify(redactForBundle(data, home, name === 'config.redacted.json' || name === 'onboarding.json'), null, 2) + '\n', { mode: 0o600 });
|
|
70
|
+
files.push(name);
|
|
71
|
+
};
|
|
72
|
+
try {
|
|
73
|
+
const report = doctorReport ?? runDoctor(doctorOptions);
|
|
74
|
+
const universe = resolveCurrentInstance({ cwd: () => process.cwd() });
|
|
75
|
+
const packageRoot = resolve(import.meta.dir, '../..');
|
|
76
|
+
const installPrefix = doctorOptions.readInstallPrefix?.() ?? defaultReadInstallPrefix(packageRoot, existsSync);
|
|
77
|
+
let logs: LogStoreRow[] = [];
|
|
78
|
+
let logStatus: 'ok' | 'no-store' = 'no-store';
|
|
79
|
+
if (existsSync(logDbPath)) {
|
|
80
|
+
try {
|
|
81
|
+
const store = LogStore.openReadOnly(logDbPath);
|
|
82
|
+
try {
|
|
83
|
+
logs = store.query({ sinceMs: now.getTime() - 86_400_000, untilMs: now.getTime(), limit: 500 });
|
|
84
|
+
logStatus = 'ok';
|
|
85
|
+
} finally { store.close(); }
|
|
86
|
+
} catch { /* Unreadable store: export no unverified log bytes. */ }
|
|
87
|
+
}
|
|
88
|
+
put('summary.json', {
|
|
89
|
+
version: packageVersion(), sha: codeRevision() ?? 'unknown', cliVersion: cliVersion(),
|
|
90
|
+
os: platform(), arch: arch(), node: process.version, bun: typeof Bun === 'undefined' ? null : Bun.version,
|
|
91
|
+
installPath: installPrefix ?? packageRoot,
|
|
92
|
+
universe: { kind: universe.kind, root: universe.root }, createdAt: instant, logs: logStatus,
|
|
93
|
+
});
|
|
94
|
+
put('doctor.json', report);
|
|
95
|
+
let config: unknown = {};
|
|
96
|
+
const configPath = doctorOptions.configPath ?? join(configDir, 'config.json');
|
|
97
|
+
try {
|
|
98
|
+
if (lstatSync(configPath).isFile()) config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
99
|
+
} catch { config = { status: 'unavailable' }; }
|
|
100
|
+
put('config.redacted.json', config);
|
|
101
|
+
put('credentials.json', report.credentials.map(({ name, resolved, source }) => ({ name, present: resolved, source })));
|
|
102
|
+
writeFileSync(join(work, 'logs-recent.jsonl'), logs.map((row) => {
|
|
103
|
+
const entry = row as { data: string | null };
|
|
104
|
+
let data = entry.data;
|
|
105
|
+
if (data) {
|
|
106
|
+
try { data = JSON.stringify(redactForBundle(JSON.parse(data), home)); }
|
|
107
|
+
catch { /* Non-JSON log bodies are redacted as plain text below. */ }
|
|
108
|
+
}
|
|
109
|
+
return JSON.stringify(redactForBundle({ ...row, data }, home, false));
|
|
110
|
+
}).join('\n') + (logs.length ? '\n' : ''), { mode: 0o600 });
|
|
111
|
+
files.push('logs-recent.jsonl');
|
|
112
|
+
const onboardingPath = join(configDir, 'onboarding.json');
|
|
113
|
+
try {
|
|
114
|
+
if (lstatSync(onboardingPath).isFile()) put('onboarding.json', JSON.parse(readFileSync(onboardingPath, 'utf8')));
|
|
115
|
+
} catch { /* An absent or unreadable optional record is not exported. */ }
|
|
116
|
+
const archiveFd = openSync(destination, 'wx', 0o600);
|
|
117
|
+
let tarSucceeded = false;
|
|
118
|
+
try {
|
|
119
|
+
const tar = spawnSync('tar', ['-czf', '-', '-C', work, ...files], { stdio: ['ignore', archiveFd, 'pipe'], timeout: 30_000 });
|
|
120
|
+
tarSucceeded = !tar.error && tar.status === 0;
|
|
121
|
+
} finally {
|
|
122
|
+
closeSync(archiveFd);
|
|
123
|
+
if (!tarSucceeded) rmSync(destination, { force: true });
|
|
124
|
+
}
|
|
125
|
+
if (!tarSucceeded) throw new Error('could not create diagnostics archive');
|
|
126
|
+
return { path: destination, files, bytes: statSync(destination).size };
|
|
127
|
+
} finally { rmSync(work, { recursive: true, force: true }); }
|
|
128
|
+
}
|
package/src/cli/doctor-cli.ts
CHANGED
|
@@ -34,6 +34,10 @@ import { ADVISABLE_DOCTOR_FIX_IDS, adviseDoctorFixes, type DoctorAdvice } from '
|
|
|
34
34
|
import { gitInstallPlan } from './git-install-plan.js';
|
|
35
35
|
import { detectDistroFamily, parseOsRelease } from './doctor-distro.js';
|
|
36
36
|
import { checkPythonEnv } from './python-cli.js';
|
|
37
|
+
import { describeModes, formatModeLine, type DoctorMode } from './doctor-modes.js';
|
|
38
|
+
import { formatDoctorLocalLlm, probeDoctorLocalLlm, type DoctorLocalLlm } from './doctor-local-llm.js';
|
|
39
|
+
import type { LlmInventory } from '../llm/local-manager/types.js';
|
|
40
|
+
import { buildDoctorBundle } from './doctor-bundle.js';
|
|
37
41
|
|
|
38
42
|
function defaultCheckPythonEnv(): { status: 'ok' | 'fixable' | 'manual'; evidence: string; remedy?: string } {
|
|
39
43
|
const c = checkPythonEnv(false);
|
|
@@ -99,6 +103,10 @@ export interface DoctorReport {
|
|
|
99
103
|
externalCommands: DoctorExternalCommand[];
|
|
100
104
|
requiredMissing?: string[];
|
|
101
105
|
capabilitySummary?: DoctorCapabilitySummary;
|
|
106
|
+
/** Read-only configuration and chat-use status. Present on a successful report. */
|
|
107
|
+
modes?: DoctorMode[];
|
|
108
|
+
/** Local LLM inventory; CLI probes separately so synchronous credential reports stay read-only and unchanged. */
|
|
109
|
+
localLlm?: DoctorLocalLlm;
|
|
102
110
|
/** F1 readiness. Present on a successful report. Absent when the report itself failed. */
|
|
103
111
|
readiness?: ReadinessReport;
|
|
104
112
|
reason?: string;
|
|
@@ -183,6 +191,8 @@ export interface DoctorCliDeps extends DoctorOptions, Pick<DoctorFixDeps, 'home'
|
|
|
183
191
|
resolveAdviceProvider?: (config: UserConfig) => { provider: string; model: string; auth: string };
|
|
184
192
|
confirmAdvice?: (message: string) => Promise<boolean>;
|
|
185
193
|
adviceInteractive?: boolean;
|
|
194
|
+
/** Local-manager inventory seam; default calls getInventory(). */
|
|
195
|
+
localLlmInventory?: () => Promise<LlmInventory>;
|
|
186
196
|
}
|
|
187
197
|
|
|
188
198
|
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
|
|
@@ -1044,6 +1054,7 @@ export function runDoctor(options: DoctorOptions = {}): DoctorReport {
|
|
|
1044
1054
|
externalCommands: externalCommands.commands,
|
|
1045
1055
|
requiredMissing,
|
|
1046
1056
|
capabilitySummary: summarizeDoctorCapabilities(credentials),
|
|
1057
|
+
modes: describeModes(resolutionOptions.userConfig),
|
|
1047
1058
|
readiness: evaluateReadiness(readiness),
|
|
1048
1059
|
retiredConfigKeys: findRetiredConfigKeysInFile(options.configPath, readFile),
|
|
1049
1060
|
...(resourceMetadata.available ? {} : { catalogMetadataUnavailable: true }),
|
|
@@ -1136,6 +1147,8 @@ export function formatDoctorReport(report: DoctorReport, options: { credentials?
|
|
|
1136
1147
|
}),
|
|
1137
1148
|
...(report.externalCommandsCatalogUnavailable ? [`External commands catalog unavailable: ${report.externalCommandsCatalogReason ?? 'catalog/external-commands.yaml is unavailable.'}`] : []),
|
|
1138
1149
|
...formatCapabilitySummary(capabilitySummary),
|
|
1150
|
+
...(report.modes ? ['모드:', ...report.modes.map((row) => ` ${formatModeLine(row)}`)] : []),
|
|
1151
|
+
...(report.localLlm ? formatDoctorLocalLlm(report.localLlm) : []),
|
|
1139
1152
|
].join('\n');
|
|
1140
1153
|
}
|
|
1141
1154
|
|
|
@@ -1203,12 +1216,37 @@ export function registerDoctorCommand(program: Command, deps: DoctorCliDeps = {}
|
|
|
1203
1216
|
.description('Reports whether credentials resolve and where each resolution comes from')
|
|
1204
1217
|
.option('--json', 'structured output')
|
|
1205
1218
|
.option('--credentials', 'show per-credential details in human-readable output')
|
|
1219
|
+
.option('--bundle', 'write a redacted diagnostics archive for support')
|
|
1220
|
+
.option('--out <dir>', 'output directory for --bundle')
|
|
1206
1221
|
.option('--fix', 'show repairs; git installation asks on a TTY (other repairs require --yes)')
|
|
1207
1222
|
.option('--advise', 'ask the configured LLM to rank catalog repairs (read-only advice)')
|
|
1208
1223
|
.option('--yes', 'apply planned doctor repairs (requires --fix)')
|
|
1209
1224
|
.option('--sudo', 'also run the planned sudo install lines — only where `sudo -n true` works (requires --fix --yes)')
|
|
1210
1225
|
.option('--restart', 'restart the nexus service when it runs a different version than this installed copy, then verify it (requires --fix --yes · interrupts bots, terminals and running turns)')
|
|
1211
|
-
.action(async (opts: { json?: boolean; credentials?: boolean; fix?: boolean; advise?: boolean; yes?: boolean; sudo?: boolean; restart?: boolean }) => {
|
|
1226
|
+
.action(async (opts: { json?: boolean; credentials?: boolean; bundle?: boolean; out?: string; fix?: boolean; advise?: boolean; yes?: boolean; sudo?: boolean; restart?: boolean }) => {
|
|
1227
|
+
if (opts.bundle) {
|
|
1228
|
+
if (opts.fix || opts.advise || opts.yes || opts.sudo || opts.restart) {
|
|
1229
|
+
err.error('--bundle cannot be combined with repair or advice options');
|
|
1230
|
+
setExitCode(1);
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
try {
|
|
1234
|
+
// Same report as `doctor --json`, local LLM section included, so support sees what the user saw.
|
|
1235
|
+
const doctorReport = runDoctor(deps);
|
|
1236
|
+
if (doctorReport.ok) doctorReport.localLlm = await probeDoctorLocalLlm(deps.localLlmInventory);
|
|
1237
|
+
const result = buildDoctorBundle({ outDir: opts.out ?? process.cwd(), doctorOptions: deps, doctorReport });
|
|
1238
|
+
out.log(opts.json ? JSON.stringify(result) : [`Diagnostics: ${result.path} (${result.bytes} bytes)`, 'Contents:', ...result.files.map((file) => ` ${file}`)].join('\n'));
|
|
1239
|
+
} catch {
|
|
1240
|
+
err.error('Could not create diagnostics archive.');
|
|
1241
|
+
setExitCode(1);
|
|
1242
|
+
}
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
if (opts.out !== undefined) {
|
|
1246
|
+
err.error('--out requires --bundle');
|
|
1247
|
+
setExitCode(1);
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1212
1250
|
if (opts.yes && !opts.fix) {
|
|
1213
1251
|
err.error('--yes requires --fix');
|
|
1214
1252
|
setExitCode(1);
|
|
@@ -1235,6 +1273,7 @@ export function registerDoctorCommand(program: Command, deps: DoctorCliDeps = {}
|
|
|
1235
1273
|
setExitCode(1);
|
|
1236
1274
|
return;
|
|
1237
1275
|
}
|
|
1276
|
+
report.localLlm = await probeDoctorLocalLlm(deps.localLlmInventory);
|
|
1238
1277
|
if (!opts.fix && !opts.advise) {
|
|
1239
1278
|
out.log(opts.json ? JSON.stringify(report, null, 2) : formatDoctorReport(report, { credentials: opts.credentials }));
|
|
1240
1279
|
if (report.requiredMissing?.length) setExitCode(1);
|
|
@@ -1353,6 +1392,7 @@ export function registerDoctorCommand(program: Command, deps: DoctorCliDeps = {}
|
|
|
1353
1392
|
: undefined;
|
|
1354
1393
|
// PATH cannot change in this process; re-probe readiness rather than treating a saved block as PATH=ok.
|
|
1355
1394
|
const currentReport = results || gitInstall?.ran ? runDoctor(deps) : report;
|
|
1395
|
+
if (currentReport.ok) currentReport.localLlm = report.localLlm;
|
|
1356
1396
|
if (gitInstall?.ran && gitInstall.ok && currentReport.requiredMissing?.includes('git')) {
|
|
1357
1397
|
gitInstall.detail = 'git init 성공 · 현재 doctor PATH 에서 git 을 찾지 못했습니다. 새 셸에서 다시 확인하세요.';
|
|
1358
1398
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { debug } from '../debug/log.js';
|
|
2
|
+
import { probeDocker } from '../llm/local-manager/docker-probe.js';
|
|
3
|
+
import { probeLmstudio } from '../llm/local-manager/lmstudio-probe.js';
|
|
4
|
+
import { probeMlx } from '../llm/local-manager/mlx-probe.js';
|
|
5
|
+
import { listNodes } from '../llm/local-manager/node-registry.js';
|
|
6
|
+
import { probeOllama } from '../llm/local-manager/ollama-probe.js';
|
|
7
|
+
import type { LlmInventory, LlmModel, LlmNode, LlmRuntime } from '../llm/local-manager/types.js';
|
|
8
|
+
|
|
9
|
+
export interface DoctorLocalLlm {
|
|
10
|
+
nodes: { kind: 'ollama' | 'lmstudio' | 'mlx' | 'docker'; baseUrl: string; reachable: boolean; models: string[] }[];
|
|
11
|
+
timedOut: boolean;
|
|
12
|
+
/** Inventory lookup failed; absence of servers was not established. */
|
|
13
|
+
failed?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const kinds = ['ollama', 'lmstudio', 'mlx', 'docker'] as const;
|
|
17
|
+
type Kind = (typeof kinds)[number];
|
|
18
|
+
|
|
19
|
+
const baseUrlFields: Record<Kind, 'ollamaBaseUrl' | 'lmstudioBaseUrl' | 'mlxBaseUrl' | 'dockerBaseUrl'> = {
|
|
20
|
+
ollama: 'ollamaBaseUrl', lmstudio: 'lmstudioBaseUrl', mlx: 'mlxBaseUrl', docker: 'dockerBaseUrl',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type Probe = (node: { id: string; isLocal: boolean }, deps: { timeoutMs?: number }) => Promise<{ reachable: boolean; models: readonly LlmModel[]; warnings: readonly string[] }>;
|
|
24
|
+
|
|
25
|
+
/** Doctor shows only this machine, so it probes only the local node — the full inventory also probes every remote
|
|
26
|
+
* fleet node over ssh and took ~7 s cold, past the 3 s cap («시간 초과» with LM Studio running · 10-01). */
|
|
27
|
+
export async function localInventory(deps: {
|
|
28
|
+
nodes?: () => readonly LlmNode[];
|
|
29
|
+
probes?: Record<'lmstudio' | 'ollama' | 'mlx' | 'docker', Probe>;
|
|
30
|
+
now?: () => number;
|
|
31
|
+
} = {}): Promise<LlmInventory> {
|
|
32
|
+
const probes = deps.probes ?? { lmstudio: probeLmstudio, ollama: probeOllama, mlx: probeMlx, docker: probeDocker } as Record<'lmstudio' | 'ollama' | 'mlx' | 'docker', Probe>;
|
|
33
|
+
const read = deps.nodes ?? listNodes;
|
|
34
|
+
const nodes: LlmNode[] = [];
|
|
35
|
+
const models: LlmModel[] = [];
|
|
36
|
+
const warnings: string[] = [];
|
|
37
|
+
for (const node of read().filter((n) => n.isLocal)) {
|
|
38
|
+
const seed = { id: node.id, isLocal: true };
|
|
39
|
+
const order = ['lmstudio', 'ollama', 'mlx', 'docker'] as const;
|
|
40
|
+
const results = await Promise.all(order.map((kind) => probes[kind](seed, { timeoutMs: 2500 })));
|
|
41
|
+
const runtimes: LlmRuntime[] = order.filter((_, i) => results[i]!.reachable);
|
|
42
|
+
for (const [i, r] of results.entries()) { models.push(...r.models); for (const w of r.warnings) warnings.push(`${node.id}:${order[i]}:${w}`); }
|
|
43
|
+
// Probes record base URLs in the registry on success — read the node back for them.
|
|
44
|
+
const fresh = read().find((n) => n.id === node.id) ?? node;
|
|
45
|
+
nodes.push({ ...fresh, reachable: runtimes.length > 0, runtimes });
|
|
46
|
+
}
|
|
47
|
+
return { nodes, models, at: (deps.now ?? Date.now)(), cached: false, warnings };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function probeDoctorLocalLlm(inventory: () => Promise<LlmInventory> = () => localInventory(), timeoutMs = 3000): Promise<DoctorLocalLlm> {
|
|
51
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
52
|
+
let result: DoctorLocalLlm;
|
|
53
|
+
try {
|
|
54
|
+
const snapshot = await Promise.race([
|
|
55
|
+
Promise.resolve().then(inventory),
|
|
56
|
+
new Promise<null>((resolve) => { timer = setTimeout(() => resolve(null), timeoutMs); }),
|
|
57
|
+
]);
|
|
58
|
+
result = snapshot === null
|
|
59
|
+
? { nodes: [], timedOut: true }
|
|
60
|
+
: {
|
|
61
|
+
nodes: snapshot.nodes.filter((node) => node.isLocal).flatMap((node) => kinds.flatMap((kind) => {
|
|
62
|
+
const baseUrl = node[baseUrlFields[kind]];
|
|
63
|
+
if (!node.runtimes.includes(kind) && !baseUrl) return [];
|
|
64
|
+
return [{
|
|
65
|
+
kind,
|
|
66
|
+
baseUrl: baseUrl ?? '',
|
|
67
|
+
reachable: node.reachable === true && node.runtimes.includes(kind),
|
|
68
|
+
models: snapshot.models.filter((model) => model.nodeId === node.id && model.runtime === kind).map((model) => model.id),
|
|
69
|
+
}];
|
|
70
|
+
})),
|
|
71
|
+
timedOut: false,
|
|
72
|
+
};
|
|
73
|
+
} catch {
|
|
74
|
+
result = { nodes: [], timedOut: false, failed: true };
|
|
75
|
+
} finally {
|
|
76
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
77
|
+
}
|
|
78
|
+
debug.log('doctor.local-llm', 'probed', {
|
|
79
|
+
nodes: result.nodes.length,
|
|
80
|
+
reachable: result.nodes.filter((node) => node.reachable).length,
|
|
81
|
+
models: result.nodes.reduce((total, node) => total + node.models.length, 0),
|
|
82
|
+
timedOut: result.timedOut,
|
|
83
|
+
});
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function formatDoctorLocalLlm(result: DoctorLocalLlm): string[] {
|
|
88
|
+
if (result.timedOut) return ['Local LLM:', ' 시간 초과'];
|
|
89
|
+
if (result.failed) return ['Local LLM:', ' 로컬 LLM 인벤토리를 조회하지 못했다'];
|
|
90
|
+
if (result.nodes.length === 0) return ['Local LLM:', ' 로컬 LLM 서버를 찾지 못했다 — Ollama(:11434) · LM Studio(:1234) 를 띄우면 여기 보인다'];
|
|
91
|
+
return [
|
|
92
|
+
'Local LLM:',
|
|
93
|
+
...result.nodes.map((node) => ` ${node.kind} · ${node.baseUrl} · ${node.reachable ? '닿음' : '안 닿음'} · 모델 ${node.models.length}개${node.models.length ? ` · ${node.models.slice(0, 5).join(', ')}` : ''}`),
|
|
94
|
+
];
|
|
95
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { DEFAULT_MODEL_TIER, isModelTier, type ModelTier } from '../model-tier/types.js';
|
|
2
|
+
|
|
3
|
+
export interface DoctorMode {
|
|
4
|
+
id: 'model-tier' | 'auto-route' | 'fast-mode';
|
|
5
|
+
value: ModelTier | boolean | 'none';
|
|
6
|
+
source: 'config' | 'default' | 'none';
|
|
7
|
+
usedByChat: boolean;
|
|
8
|
+
note: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Report configured switches without changing them or implying they affect chat turns. */
|
|
12
|
+
export function describeModes(cfg: { modelTier?: { llm?: unknown }; llm?: { autoRoute?: { enabled?: unknown } } }): DoctorMode[] {
|
|
13
|
+
const tier = cfg.modelTier?.llm;
|
|
14
|
+
const autoRoute = cfg.llm?.autoRoute?.enabled;
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
id: 'model-tier',
|
|
18
|
+
value: isModelTier(tier) ? tier : DEFAULT_MODEL_TIER,
|
|
19
|
+
source: isModelTier(tier) ? 'config' : 'default',
|
|
20
|
+
usedByChat: false,
|
|
21
|
+
note: '대화 턴 모델 선택에 쓰이지 않는다(표시·전환 계획만)',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'auto-route',
|
|
25
|
+
value: typeof autoRoute === 'boolean' ? autoRoute : false,
|
|
26
|
+
source: typeof autoRoute === 'boolean' ? 'config' : 'default',
|
|
27
|
+
usedByChat: false,
|
|
28
|
+
note: '오토파일럿 턴만 읽는다 · 켜기 = elanous config set llm.autoRoute.enabled true',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'fast-mode',
|
|
32
|
+
value: 'none',
|
|
33
|
+
source: 'none',
|
|
34
|
+
usedByChat: false,
|
|
35
|
+
note: '스위치 없음(분류기 호출부 0)',
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function formatModeLine(row: DoctorMode): string {
|
|
41
|
+
const label = {
|
|
42
|
+
'model-tier': 'smart·등급',
|
|
43
|
+
'auto-route': 'smart·자동 라우팅',
|
|
44
|
+
'fast-mode': '빠른 모드',
|
|
45
|
+
}[row.id];
|
|
46
|
+
const source = { config: '설정', default: '기본', none: '없음' }[row.source];
|
|
47
|
+
return `${label}: ${String(row.value)}(${source}) · 대화에 ${row.usedByChat ? '쓰임' : '안 쓰임'} — ${row.note}`;
|
|
48
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// EN4 — `elanous import`: one read-only plan over the agents you already use. Applying stays with the sub-commands each item names.
|
|
2
|
+
import type { Command } from 'commander';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { debug } from '../debug/log.js';
|
|
5
|
+
import { formatImportPlan, planImport, type ImportPlan } from '../import/plan.js';
|
|
6
|
+
import { applyImport, formatApplied, IMPORT_MODES, undoImport, type ImportMode } from '../import/apply.js';
|
|
7
|
+
import { dirname, join } from 'node:path';
|
|
8
|
+
|
|
9
|
+
export async function buildImportPlan(home = homedir()): Promise<ImportPlan> {
|
|
10
|
+
const { defaultSkillDirs, getUserConfig } = await import('../user-config.js');
|
|
11
|
+
const cfg = getUserConfig();
|
|
12
|
+
const plan = planImport({
|
|
13
|
+
home,
|
|
14
|
+
...(process.env.CODEX_HOME ? { codexHome: process.env.CODEX_HOME } : {}),
|
|
15
|
+
activeSkillDirs: defaultSkillDirs(cfg),
|
|
16
|
+
mcpServerIds: (cfg.mcp?.servers ?? []).map((server) => server.id),
|
|
17
|
+
});
|
|
18
|
+
for (const item of plan.items) debug.log('import.plan', 'item', { source: item.source, kind: item.kind, status: item.status });
|
|
19
|
+
debug.log('import.plan', 'planned', { sources: plan.sources.filter((s) => s.detected).map((s) => s.id), items: plan.items.length });
|
|
20
|
+
return plan;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function registerImportCommand(program: Command): void {
|
|
24
|
+
program.command('import')
|
|
25
|
+
.description('Show what elanous can bring over from Claude Code, Codex, ~/.agents, OpenClaw and Hermes — a plan first; `--apply` registers skills in place and adds MCP switched off, `--undo` puts everything back')
|
|
26
|
+
.option('--json', 'Print the plan (or the apply result) as JSON')
|
|
27
|
+
.option('--apply', 'Apply the plan: skills in place (ref) or as link/copy · MCP switched off · secrets and hooks only listed')
|
|
28
|
+
.option('--mode <mode>', `How skills come over: ${IMPORT_MODES.join('|')} (default ref — nothing copied)`)
|
|
29
|
+
.option('--pick <ids>', 'Only these item ids (comma-separated, see --json)')
|
|
30
|
+
.option('--undo', 'Undo the last --apply: restore the config bytes and remove links/copies')
|
|
31
|
+
.action(async (opts: { json?: boolean; apply?: boolean; mode?: string; pick?: string; undo?: boolean }) => {
|
|
32
|
+
const { userConfigPath } = await import('../user-config.js');
|
|
33
|
+
const importRoot = join(dirname(userConfigPath()), 'import');
|
|
34
|
+
if (opts.undo) {
|
|
35
|
+
const undone = undoImport(importRoot);
|
|
36
|
+
const { resetSkillIndex } = await import('../skills/index.js');
|
|
37
|
+
resetSkillIndex();
|
|
38
|
+
debug.log('import.apply', 'undone', { found: undone !== null, created: undone?.created.length ?? 0 });
|
|
39
|
+
if (opts.json) console.log(JSON.stringify({ undone: undone !== null }));
|
|
40
|
+
else console.log(undone ? `되돌렸습니다 — 설정을 ${undone.at} 이전으로 돌리고, 만든 것 ${undone.created.length}개를 지웠습니다.` : '되돌릴 가져오기가 없습니다.');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const plan = await buildImportPlan();
|
|
44
|
+
if (!opts.apply) { console.log(opts.json ? JSON.stringify(plan, null, 2) : formatImportPlan(plan)); return; }
|
|
45
|
+
if (opts.mode !== undefined && !IMPORT_MODES.includes(opts.mode as ImportMode)) throw new Error(`--mode 는 ${IMPORT_MODES.join('|')} 중 하나다(받은 값: ${opts.mode})`);
|
|
46
|
+
const { getUserConfig, saveUserConfig } = await import('../user-config.js');
|
|
47
|
+
const cfg = getUserConfig();
|
|
48
|
+
const rawMcp = (cfg.raw.mcp && typeof cfg.raw.mcp === 'object' && !Array.isArray(cfg.raw.mcp) ? cfg.raw.mcp : {}) as Record<string, unknown>;
|
|
49
|
+
const manifest = applyImport(plan, { ...(opts.mode ? { mode: opts.mode as ImportMode } : {}), ...(opts.pick ? { pick: opts.pick.split(',').map((id) => id.trim()).filter(Boolean) } : {}) }, {
|
|
50
|
+
home: homedir(), configPath: userConfigPath(), importRoot,
|
|
51
|
+
sources: cfg.skills.sources ?? [],
|
|
52
|
+
rawMcpServers: (Array.isArray(rawMcp.servers) ? rawMcp.servers : []) as Array<Record<string, unknown>>,
|
|
53
|
+
save: (sources, servers) => saveUserConfig({ ...cfg, raw: { ...cfg.raw, mcp: { ...rawMcp, servers } }, skills: { ...cfg.skills, sources } }),
|
|
54
|
+
});
|
|
55
|
+
const { resetSkillIndex } = await import('../skills/index.js');
|
|
56
|
+
resetSkillIndex();
|
|
57
|
+
debug.log('import.apply', 'applied', { mode: manifest.mode, applied: manifest.applied.length, kept: manifest.kept.length, created: manifest.created.length });
|
|
58
|
+
console.log(opts.json ? JSON.stringify({ mode: manifest.mode, applied: manifest.applied, kept: manifest.kept, created: manifest.created.length }, null, 2) : formatApplied(manifest));
|
|
59
|
+
});
|
|
60
|
+
}
|
package/src/cli/intake-cli.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { writeStdoutFully } from './stdout-flush.js';
|
|
|
11
11
|
import { runGitCommand } from '../git-fs/runner.js';
|
|
12
12
|
import { debug } from '../debug/log.js';
|
|
13
13
|
import { getUserConfig } from '../user-config.js';
|
|
14
|
+
import { registerIntakeSourceCommands } from './intake-source-cli.js';
|
|
14
15
|
|
|
15
16
|
export interface IntakeToTasksCliDeps {
|
|
16
17
|
root: string;
|
|
@@ -91,6 +92,7 @@ export function resolveIntakeCheckRoot(
|
|
|
91
92
|
export function registerIntakeCommands(program: Command): void {
|
|
92
93
|
// ── intake check — 바깥 사실을 elanous 현재와 대조 (태스크 등록 없음) ──
|
|
93
94
|
const intakeCmd = program.command('intake').description('바깥 사실·문서를 elanous 현재와 대조하거나 태스크로 받는다');
|
|
95
|
+
registerIntakeSourceCommands(intakeCmd);
|
|
94
96
|
intakeCmd.hook('preAction', async () => {
|
|
95
97
|
try {
|
|
96
98
|
const { registerStandaloneLogSink } = await import('../domains/standalone-log-sink.js');
|
|
@@ -217,7 +219,9 @@ export function registerIntakeCommands(program: Command): void {
|
|
|
217
219
|
const { readPipedStdin } = await import('./piped-stdin.js');
|
|
218
220
|
const text = opts.file ? readFileSync(opts.file, 'utf8') : (await readPipedStdin()) ?? '';
|
|
219
221
|
const { raws, bad } = parseRawIntakeJsonl(text);
|
|
220
|
-
|
|
222
|
+
let result: ReturnType<typeof ingestIntakeItems>;
|
|
223
|
+
try { result = ingestIntakeItems(effectiveInstanceRoot(), opts.source as (typeof INTAKE_SOURCES)[number], raws); }
|
|
224
|
+
catch (error) { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 2; return; }
|
|
221
225
|
const out = { source: opts.source, inputLines: raws.length + bad, badInputLines: bad, ...result };
|
|
222
226
|
if (opts.json) await writeStdoutFully(JSON.stringify(out, null, 2));
|
|
223
227
|
else console.log(`흡수 원장 · ${opts.source}: 새 ${result.added} · 합침 ${result.merged} · 이미 끝난 것 ${result.seen} · 버림 ${result.skipped}${bad ? ` · 깨진 입력 ${bad}` : ''}${result.badLines ? ` · 깨진 원장 줄 ${result.badLines}` : ''}`);
|
|
@@ -228,14 +232,20 @@ export function registerIntakeCommands(program: Command): void {
|
|
|
228
232
|
.description('흡수 원장 항목 보기 (최근 본 순)')
|
|
229
233
|
.option('--status <status>', 'new | queued | absorbed | checked | routed | discarded | deferred')
|
|
230
234
|
.option('--source <source>', '입력원으로 거르기')
|
|
235
|
+
.option('--seat <id>', '자리 id (별칭 허용; 자리 없는 옛 항목은 제외)')
|
|
231
236
|
.option('--limit <n>', '최대 줄 수 (기본 30)')
|
|
232
237
|
.option('--json', '구조화 출력')
|
|
233
|
-
.action(async (opts: { status?: string; source?: string; limit?: string; json?: boolean }) => {
|
|
238
|
+
.action(async (opts: { status?: string; source?: string; seat?: string; limit?: string; json?: boolean }) => {
|
|
234
239
|
const { listIntakeItems } = await import('../intake-plane/items.js');
|
|
235
240
|
const { effectiveInstanceRoot } = await import('../instance/resolve.js');
|
|
241
|
+
const { canonicalSeat } = await import('../intake-plane/intake-sources.js');
|
|
242
|
+
let seat: string | undefined;
|
|
243
|
+
try { seat = opts.seat === undefined ? undefined : canonicalSeat(opts.seat); }
|
|
244
|
+
catch (error) { console.error((error as Error).message); process.exitCode = 2; return; }
|
|
236
245
|
const all = listIntakeItems(effectiveInstanceRoot(), {
|
|
237
246
|
...(opts.status ? { status: opts.status as never } : {}),
|
|
238
247
|
...(opts.source ? { source: opts.source as never } : {}),
|
|
248
|
+
...(seat ? { seat } : {}),
|
|
239
249
|
});
|
|
240
250
|
const limit = Math.max(1, Number(opts.limit ?? 30) || 30);
|
|
241
251
|
const shown = all.slice(0, limit);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
4
|
+
import { addSource, canonicalSeat, dueSources, expiredSource, listSources, removeSource, runDue, type SourceKind } from '../intake-plane/intake-sources.js';
|
|
5
|
+
|
|
6
|
+
export function registerIntakeSourceCommands(intake: Command, root: () => string = effectiveInstanceRoot): void {
|
|
7
|
+
const source = intake.command('source').description('자리별 흡수 원천과 주기 등록·조회·실행');
|
|
8
|
+
const fail = (error: unknown) => { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 2; };
|
|
9
|
+
source.command('add')
|
|
10
|
+
.requiredOption('--seat <id>', '자리 id 또는 별칭')
|
|
11
|
+
.requiredOption('--kind <kind>', 'github-query | rss | command')
|
|
12
|
+
.requiredOption('--spec <spec>', 'GitHub 질의 · 피드 URL · 명령')
|
|
13
|
+
.requiredOption('--every <cadence>', 'Nh | Nd | Nw')
|
|
14
|
+
.option('--until <date>', '만료 시작 날짜 YYYY-MM-DD (UTC)')
|
|
15
|
+
.option('--why <reason>', '등록 이유')
|
|
16
|
+
.option('--id <id>', '원천 id')
|
|
17
|
+
.action((opts: { seat: string; kind: string; spec: string; every: string; until?: string; why?: string; id?: string }) => {
|
|
18
|
+
try {
|
|
19
|
+
const added = addSource({ id: opts.id ?? `src-${randomUUID().slice(0, 8)}`, seat: opts.seat, kind: opts.kind as SourceKind,
|
|
20
|
+
spec: opts.spec, every: opts.every, ...(opts.until ? { until: opts.until } : {}), ...(opts.why ? { why: opts.why } : {}) }, root());
|
|
21
|
+
console.log(`${added.id} ${added.seat} ${added.kind} ${added.every}`);
|
|
22
|
+
} catch (error) { fail(error); }
|
|
23
|
+
});
|
|
24
|
+
source.command('list')
|
|
25
|
+
.option('--seat <id>', '자리 id 또는 별칭')
|
|
26
|
+
.option('--json', 'JSON 출력')
|
|
27
|
+
.action((opts: { seat?: string; json?: boolean }) => {
|
|
28
|
+
try {
|
|
29
|
+
const now = new Date();
|
|
30
|
+
const sources = listSources({ seat: opts.seat }, root()).map((entry) => ({ ...entry, expired: expiredSource(entry, now) }));
|
|
31
|
+
if (opts.json) console.log(JSON.stringify(sources));
|
|
32
|
+
else for (const entry of sources) console.log(`${entry.id} ${entry.seat} ${entry.kind} ${entry.every} ${entry.expired ? '만료' : '활성'} ${entry.spec}`);
|
|
33
|
+
} catch (error) { fail(error); }
|
|
34
|
+
});
|
|
35
|
+
source.command('remove <id>').action((id: string) => {
|
|
36
|
+
try { if (!removeSource(id, root())) { console.error(`원천 id 없음: ${id}`); process.exitCode = 2; return; } console.log(`removed ${id}`); }
|
|
37
|
+
catch (error) { fail(error); }
|
|
38
|
+
});
|
|
39
|
+
source.command('run-due')
|
|
40
|
+
.option('--seat <id>', '자리 id 또는 별칭')
|
|
41
|
+
.option('--dry-run', '기한 원천 목록만 · 수집·원장·마지막 실행 시각 무변')
|
|
42
|
+
.option('--json', 'JSON 출력')
|
|
43
|
+
.action(async (opts: { seat?: string; dryRun?: boolean; json?: boolean }) => {
|
|
44
|
+
try {
|
|
45
|
+
const seat = opts.seat === undefined ? undefined : canonicalSeat(opts.seat);
|
|
46
|
+
if (opts.dryRun) {
|
|
47
|
+
const sources = dueSources(new Date(), root()).filter((entry) => seat === undefined || entry.seat === seat);
|
|
48
|
+
if (opts.json) console.log(JSON.stringify(sources));
|
|
49
|
+
else for (const entry of sources) console.log(`${entry.id} ${entry.seat} ${entry.kind} ${entry.every}`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const result = await runDue({ seat, deps: { stateDir: root() } });
|
|
53
|
+
if (opts.json) console.log(JSON.stringify(result));
|
|
54
|
+
else for (const row of result.ran) console.log(`${row.id} ${row.items}${row.error ? ` 실패: ${row.error}` : ''}`);
|
|
55
|
+
if (result.ran.some((row) => row.error)) process.exitCode = 1;
|
|
56
|
+
} catch (error) { fail(error); }
|
|
57
|
+
});
|
|
58
|
+
}
|
package/src/cli/machine-cli.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { loadMachineLedger, machineMarkdownPath, renderLedgerMarkdown, setMachineField } from '../machines/machine-ledger.js';
|
|
2
4
|
import { MACHINE_NAME, resolveMachineName } from '../roles/machine-name.js';
|
|
3
5
|
import { readMachineProfile, seatRank, validSeatRank, writeMachineProfile } from '../roles/machine-profile.js';
|
|
4
6
|
|
|
@@ -28,6 +30,27 @@ export function registerMachineCommands(program: Command, root?: string): void {
|
|
|
28
30
|
writeMachineProfile({ id, duties, seats }, root);
|
|
29
31
|
console.log(`machine · ${id} · ${duties.join(',') || '-'} · ${Object.entries(seats).map(([seat, value]) => `${seat}:${value.rank}`).join(',') || '-'}`);
|
|
30
32
|
});
|
|
33
|
+
machine.command('render').description('YAML 원장에서 Markdown 생성·검증')
|
|
34
|
+
.option('--check', 'Markdown 과 YAML 동기화 검증')
|
|
35
|
+
.action((opts: { check?: boolean }) => {
|
|
36
|
+
const markdown = renderLedgerMarkdown(loadMachineLedger(process.cwd()));
|
|
37
|
+
const path = machineMarkdownPath(process.cwd());
|
|
38
|
+
if (opts.check) {
|
|
39
|
+
let current: string;
|
|
40
|
+
try { current = readFileSync(path, 'utf8'); } catch { throw new Error(`machine ledger Markdown missing: ${path}`); }
|
|
41
|
+
if (current !== markdown) throw new Error(`machine ledger Markdown stale: ${path}`);
|
|
42
|
+
console.log('machine render --check OK');
|
|
43
|
+
} else {
|
|
44
|
+
writeFileSync(path, markdown, 'utf8');
|
|
45
|
+
console.log(`machine render · ${path}`);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
machine.command('ledger-set').description('YAML 보유 자원 필드 수정')
|
|
49
|
+
.argument('<id>').argument('<field>').argument('<value>')
|
|
50
|
+
.action((id: string, field: string, value: string) => {
|
|
51
|
+
setMachineField(process.cwd(), id, field, value);
|
|
52
|
+
console.log(`machine ledger-set · ${id} · ${field}`);
|
|
53
|
+
});
|
|
31
54
|
machine.command('show').option('--json', 'JSON 출력')
|
|
32
55
|
.action((opts: { json?: boolean }) => {
|
|
33
56
|
const resolved = resolveMachineName({ root });
|
package/src/cli/mcp-login.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from '../mcp/mcp-oauth.js';
|
|
13
13
|
import { parseWwwAuthenticate } from '../mcp/client.js';
|
|
14
14
|
import { debug } from '../debug/log.js';
|
|
15
|
+
import { browserUnavailableReason } from '../oauth/browser-availability.js';
|
|
15
16
|
|
|
16
17
|
const CALLBACK_PATH = '/oauth/callback';
|
|
17
18
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
@@ -31,6 +32,8 @@ export interface McpLoginOpts {
|
|
|
31
32
|
readConfigFn?: () => { mcp?: { servers: McpServerSpec[] } };
|
|
32
33
|
fetch?: McpOAuthFetch;
|
|
33
34
|
openBrowser?: (url: string) => Promise<void>;
|
|
35
|
+
browserEnv?: NodeJS.ProcessEnv;
|
|
36
|
+
browserPlatform?: NodeJS.Platform;
|
|
34
37
|
createListener?: (handler: (req: IncomingMessage, res: ServerResponse) => void) => Server;
|
|
35
38
|
/** 발견한 issuer/tokenEndpoint 를 config 에 되쓰는 자리. 테스트 심. */
|
|
36
39
|
persistDiscoveryFn?: (input: PersistDiscoveryInput) => PersistDiscoveryResult;
|
|
@@ -91,10 +94,16 @@ export async function runMcpLogin(opts: McpLoginOpts): Promise<McpLoginResult> {
|
|
|
91
94
|
void callbackPromise.catch(() => undefined);
|
|
92
95
|
out.log(`Open this URL to authorize '${opts.serverId}':`);
|
|
93
96
|
out.log(authorization.request.url);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
const why = browserUnavailableReason(opts.browserEnv ?? process.env, opts.browserPlatform ?? process.platform);
|
|
98
|
+
if (why) {
|
|
99
|
+
out.log(why);
|
|
100
|
+
debug.log('browser.open', 'skipped', { reason: why.includes('ssh') ? 'ssh' : 'no-display' });
|
|
101
|
+
} else {
|
|
102
|
+
try {
|
|
103
|
+
await (opts.openBrowser ?? openDefaultBrowser)(authorization.request.url);
|
|
104
|
+
} catch {
|
|
105
|
+
out.error('Could not open the default browser; open the URL above manually.');
|
|
106
|
+
}
|
|
98
107
|
}
|
|
99
108
|
const callback = await callbackPromise;
|
|
100
109
|
await exchangeAuthorizationCode(authorization.metadata, authorization.request, callback, {
|