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
package/src/onboarding.ts
CHANGED
|
@@ -25,13 +25,16 @@
|
|
|
25
25
|
// back-fill merge — so tests don't need to stub config paths either.
|
|
26
26
|
|
|
27
27
|
import { existsSync, statSync } from 'node:fs';
|
|
28
|
+
import { delimiter, join } from 'node:path';
|
|
29
|
+
import { spawnSync } from 'node:child_process';
|
|
30
|
+
import { isHeadlessEnv, spawnCodexLogin, type CodexLoginResult } from './acp/codex-auth.js';
|
|
28
31
|
import { getRunContext, type RunContext } from './agent/run-context.js';
|
|
29
32
|
import * as readline from 'node:readline/promises';
|
|
30
33
|
import { stdin as input, stdout as output } from 'node:process';
|
|
31
34
|
import {
|
|
32
35
|
type UserConfig, type LLMProviderName, type SkillSetName,
|
|
33
36
|
saveUserConfig, markOnboardingComplete, buildUserConfig,
|
|
34
|
-
skillSetDir, SKILL_SET_NAMES, userConfigPath, urlRoutingDefaults, devRequestRoutingDefaults,
|
|
37
|
+
skillSetDir, SKILL_SET_NAMES, userConfigPath, urlRoutingDefaults, devRequestRoutingDefaults, defaultSkillDirs, resolveSkillSources,
|
|
35
38
|
} from './user-config.js';
|
|
36
39
|
import {
|
|
37
40
|
GROK_MODEL, OPENAI_MODEL, ANTHROPIC_MODEL, LOCAL_LLM_MODEL, GEMINI_MODEL,
|
|
@@ -40,7 +43,9 @@ import {
|
|
|
40
43
|
import { CODEX_DEFAULT_MODEL } from './llm.js';
|
|
41
44
|
import { suggestSetupModel } from './model-tier/index.js';
|
|
42
45
|
import { loginWithCodex, CODEX_DEVICE_LOGIN_URL } from './oauth/codex.js';
|
|
46
|
+
import { loginWithCodexBrowser } from './oauth/codex-browser-login.js';
|
|
43
47
|
import { loadTokens } from './oauth/store.js';
|
|
48
|
+
import { browserUnavailableReason } from './oauth/browser-availability.js';
|
|
44
49
|
import { TelegramBot } from './telegram.js';
|
|
45
50
|
import { DiscordBot } from './discord.js';
|
|
46
51
|
import { pickCodexAuthMode, pickCodexModel } from './codex/setup.js';
|
|
@@ -54,6 +59,11 @@ import type {
|
|
|
54
59
|
import { showStepOr, chooseFrom } from './onboarding/io-extended.js';
|
|
55
60
|
import { askWithHelp } from './onboarding/wire-helpers.js';
|
|
56
61
|
import { stepTransition } from './onboarding/transition.js';
|
|
62
|
+
import { unattendedSetupHint } from './onboarding/entry-hints.js';
|
|
63
|
+
import { continueHereLines } from './onboarding/continue-here.js';
|
|
64
|
+
import type { NexusShowResult } from './cli/nexus-show.js';
|
|
65
|
+
import type { TailscaleProbe } from './nexus/onboarding/tailscale-probe.js';
|
|
66
|
+
import type { IssuedTempToken } from './auth/temp-tokens.js';
|
|
57
67
|
import {
|
|
58
68
|
askValidated,
|
|
59
69
|
validateIntList,
|
|
@@ -151,6 +161,8 @@ export interface WizardIO {
|
|
|
151
161
|
* and `ask` otherwise. */
|
|
152
162
|
askSecret?(prompt: string, field?: string): Promise<string>;
|
|
153
163
|
print(text: string): void;
|
|
164
|
+
/** Synthetic, unattended IO (never issue or display a connection token). */
|
|
165
|
+
nonInteractive?: boolean;
|
|
154
166
|
/** Called once after a successful config save. */
|
|
155
167
|
complete?(): void;
|
|
156
168
|
close(): void;
|
|
@@ -325,6 +337,108 @@ export interface LocalProbeDeps {
|
|
|
325
337
|
|
|
326
338
|
export interface GrokOnboardingDeps {
|
|
327
339
|
resolveCredential?: () => GrokCredential | null;
|
|
340
|
+
/** Subscription detection for step 1 (OB1) — tests inject; default = `detectProviders`. */
|
|
341
|
+
detectProviders?: () => Promise<import('./llm/provider-detect.js').DetectedProvider[]>;
|
|
342
|
+
/** OB1 — does the codex CLI already hold a usable login (~/.codex/auth.json)? Tests inject. */
|
|
343
|
+
hasCodexCliLogin?: () => boolean;
|
|
344
|
+
loadCodexTokens?: typeof loadTokens;
|
|
345
|
+
/** Browser-first OAuth test seams. Defaults use the local environment. */
|
|
346
|
+
codexEnv?: NodeJS.ProcessEnv;
|
|
347
|
+
isHeadless?: (env: NodeJS.ProcessEnv) => boolean;
|
|
348
|
+
loginWithCodexBrowser?: typeof loginWithCodexBrowser;
|
|
349
|
+
spawnCodexLogin?: (opts: { deviceAuth: false; codexPath: string }) => Promise<CodexLoginResult>;
|
|
350
|
+
loginWithCodex?: typeof loginWithCodex;
|
|
351
|
+
/** Execute local browser/clipboard commands without a shell. Returns false when unavailable. */
|
|
352
|
+
runAuthCommand?: (command: string, args: string[], input?: string) => boolean;
|
|
353
|
+
authPlatform?: NodeJS.Platform;
|
|
354
|
+
/** OSC52 fallback, when clipboard commands are unavailable. */
|
|
355
|
+
writeAuthTerminal?: (data: string) => boolean;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function pathCodexBinary(env: NodeJS.ProcessEnv): string | null {
|
|
359
|
+
const binary = process.platform === 'win32' ? 'codex.exe' : 'codex';
|
|
360
|
+
for (const dir of (env.PATH ?? '').split(delimiter)) {
|
|
361
|
+
if (!dir) continue;
|
|
362
|
+
const candidate = join(dir, binary);
|
|
363
|
+
try { if (existsSync(candidate) && statSync(candidate).isFile()) return candidate; }
|
|
364
|
+
catch { /* skip inaccessible PATH entries */ }
|
|
365
|
+
}
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function runAuthCommand(command: string, args: string[], input?: string): boolean {
|
|
370
|
+
try {
|
|
371
|
+
const result = spawnSync(command, args, {
|
|
372
|
+
input,
|
|
373
|
+
stdio: [input === undefined ? 'ignore' : 'pipe', 'ignore', 'ignore'],
|
|
374
|
+
timeout: 3_000,
|
|
375
|
+
});
|
|
376
|
+
return result.status === 0 && !result.error;
|
|
377
|
+
} catch { return false; }
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function assistCodexDeviceLogin(
|
|
381
|
+
io: WizardIO,
|
|
382
|
+
url: string,
|
|
383
|
+
code: string,
|
|
384
|
+
deps: GrokOnboardingDeps,
|
|
385
|
+
): void {
|
|
386
|
+
const env = deps.codexEnv ?? process.env;
|
|
387
|
+
const platform = deps.authPlatform ?? process.platform;
|
|
388
|
+
const execute = deps.runAuthCommand ?? runAuthCommand;
|
|
389
|
+
const local = !(deps.isHeadless ?? isHeadlessEnv)(env);
|
|
390
|
+
const unavailable = browserUnavailableReason(env, platform);
|
|
391
|
+
if (unavailable) {
|
|
392
|
+
io.print(` ${unavailable}`);
|
|
393
|
+
debug.log('browser.open', 'skipped', { reason: unavailable.includes('ssh') ? 'ssh' : 'no-display' });
|
|
394
|
+
} else if (local) {
|
|
395
|
+
try {
|
|
396
|
+
if (platform === 'darwin') execute('open', [url]);
|
|
397
|
+
else if (platform === 'linux' || platform === 'freebsd' || platform === 'openbsd') execute('xdg-open', [url]);
|
|
398
|
+
} catch { /* browser is optional; URL remains visible */ }
|
|
399
|
+
}
|
|
400
|
+
let copied = false;
|
|
401
|
+
for (const [command, args] of platform === 'darwin'
|
|
402
|
+
? [['pbcopy', []]] as Array<[string, string[]]>
|
|
403
|
+
: platform === 'linux' || platform === 'freebsd' || platform === 'openbsd'
|
|
404
|
+
? [['wl-copy', []], ['xclip', ['-selection', 'clipboard']]] as Array<[string, string[]]>
|
|
405
|
+
: []) {
|
|
406
|
+
try { if (execute(command, args, code)) { copied = true; break; } }
|
|
407
|
+
catch { /* try the next clipboard */ }
|
|
408
|
+
}
|
|
409
|
+
if (!copied) {
|
|
410
|
+
try {
|
|
411
|
+
const write = deps.writeAuthTerminal ?? ((data: string) => {
|
|
412
|
+
if (!output.isTTY) return false;
|
|
413
|
+
output.write(data);
|
|
414
|
+
return true;
|
|
415
|
+
});
|
|
416
|
+
copied = write(`\x1b]52;c;${Buffer.from(code).toString('base64')}\x07`);
|
|
417
|
+
} catch { /* terminals without OSC52 still show the code */ }
|
|
418
|
+
}
|
|
419
|
+
if (copied) io.print(' 코드를 복사했습니다.');
|
|
420
|
+
if (local) notifyDeviceCode(code, copied, platform, execute);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/** OB5c — on a desktop, raise an OS notification with the code so the person can paste it without
|
|
424
|
+
* hunting for the terminal. Missing notifier (or headless) is skipped silently; the code never goes to logs. */
|
|
425
|
+
function notifyDeviceCode(
|
|
426
|
+
code: string,
|
|
427
|
+
copied: boolean,
|
|
428
|
+
platform: NodeJS.Platform,
|
|
429
|
+
execute: (command: string, args: string[], input?: string) => boolean,
|
|
430
|
+
): void {
|
|
431
|
+
const message = copied ? `인증 코드 ${code} — 클립보드에 복사됨 · 붙여넣기만` : `인증 코드 ${code}`;
|
|
432
|
+
let notified = false;
|
|
433
|
+
try {
|
|
434
|
+
if (platform === 'darwin') {
|
|
435
|
+
const quoted = (s: string) => `"${s.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
436
|
+
notified = execute('osascript', ['-e', `display notification ${quoted(message)} with title "Elanous"`]);
|
|
437
|
+
} else if (platform === 'linux' || platform === 'freebsd' || platform === 'openbsd') {
|
|
438
|
+
notified = execute('notify-send', ['Elanous', message]);
|
|
439
|
+
}
|
|
440
|
+
} catch { /* no notifier — the code stays on screen */ }
|
|
441
|
+
debug.log('onboarding.codex', 'device-code-notify', { platform, copied, notified });
|
|
328
442
|
}
|
|
329
443
|
|
|
330
444
|
/** Default probe — calls into the local-LLM manager's quad-probe
|
|
@@ -425,7 +539,18 @@ async function askLLM(
|
|
|
425
539
|
excerpt: getMessages().setupStepLLMExcerpt,
|
|
426
540
|
severity: 'required',
|
|
427
541
|
});
|
|
428
|
-
|
|
542
|
+
// OB1 (10-01): show subscriptions the machine already has and preselect one — the old default was the env-only
|
|
543
|
+
// «Auto-detect», so a ChatGPT/Codex or Grok subscriber pressing Enter got a setup that found nothing.
|
|
544
|
+
let detected: import('./llm/provider-detect.js').DetectedProvider[] = [];
|
|
545
|
+
try {
|
|
546
|
+
const detect = grokDeps.detectProviders ?? (async () => (await import('./llm/provider-detect.js')).detectProviders());
|
|
547
|
+
detected = await detect();
|
|
548
|
+
} catch { /* detection is advisory — the list still works */ }
|
|
549
|
+
const { subscriptionHint } = await import('./onboarding/subscription-hint.js');
|
|
550
|
+
const hint = subscriptionHint(detected);
|
|
551
|
+
for (const found of hint.found) io.print(found.line);
|
|
552
|
+
const configured = current.provider && current.provider !== 'auto' ? current.provider : undefined;
|
|
553
|
+
const currentIdx = PROVIDER_CHOICES.findIndex(c => c.key === (configured ?? hint.preferred));
|
|
429
554
|
const providerOptions: ChoiceOption<ProviderChoice | WizardBackValue>[] =
|
|
430
555
|
PROVIDER_CHOICES.map((c, i) => ({
|
|
431
556
|
key: String(i + 1),
|
|
@@ -448,7 +573,7 @@ async function askLLM(
|
|
|
448
573
|
&& (grokDeps.resolveCredential ?? resolveGrokCredential)()?.kind === 'subscription';
|
|
449
574
|
if (grokSubscription) io.print(' Found grok subscription (~/.grok/auth.json)');
|
|
450
575
|
if (choice.key === 'auto') {
|
|
451
|
-
io.print(' → auto mode:
|
|
576
|
+
io.print(' → auto mode: uses a subscription login it finds (ChatGPT/Codex ~/.codex · Grok ~/.grok) first, then XAI/OPENAI/ANTHROPIC/LOCAL_LLM env vars.');
|
|
452
577
|
return out;
|
|
453
578
|
}
|
|
454
579
|
|
|
@@ -456,26 +581,60 @@ async function askLLM(
|
|
|
456
581
|
// Keeps onboarding and `elanous codex setup` in sync — one picker, one
|
|
457
582
|
// curated model catalog, one source of truth for provider defaults.
|
|
458
583
|
if (choice.key === 'openai-codex') {
|
|
459
|
-
const mode = await pickCodexAuthMode(io
|
|
584
|
+
const mode = await pickCodexAuthMode(io, {
|
|
585
|
+
...(grokDeps.hasCodexCliLogin ? { hasCodexCliLogin: grokDeps.hasCodexCliLogin } : {}),
|
|
586
|
+
...(grokDeps.loadCodexTokens ? { loadTokens: grokDeps.loadCodexTokens } : {}),
|
|
587
|
+
});
|
|
460
588
|
if (mode === 'oauth') {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
589
|
+
const env = grokDeps.codexEnv ?? process.env;
|
|
590
|
+
let signedIn = false;
|
|
591
|
+
const why = browserUnavailableReason(env, grokDeps.authPlatform ?? process.platform);
|
|
592
|
+
if (why) {
|
|
593
|
+
io.print(` ${why}`);
|
|
594
|
+
debug.log('browser.open', 'skipped', { reason: why.includes('ssh') ? 'ssh' : 'no-display' });
|
|
595
|
+
}
|
|
596
|
+
if (!why && !(grokDeps.isHeadless ?? isHeadlessEnv)(env)) {
|
|
597
|
+
// Full-screen IO retains print() messages across redraws. Keep this
|
|
598
|
+
// waiting hint transient so it cannot reappear on the device-code screen.
|
|
599
|
+
if (io.showStep) output.write(' 브라우저에서 로그인을 마치세요 — 끝나면 자동으로 이어집니다.\n');
|
|
600
|
+
else io.print(' 브라우저에서 로그인을 마치세요 — 끝나면 자동으로 이어집니다.');
|
|
601
|
+
try {
|
|
602
|
+
await (grokDeps.loginWithCodexBrowser ?? loginWithCodexBrowser)();
|
|
603
|
+
signedIn = true;
|
|
604
|
+
} catch { /* try PATH Codex, then device code */ }
|
|
605
|
+
if (!signedIn) {
|
|
606
|
+
const codexPath = pathCodexBinary(env);
|
|
607
|
+
if (codexPath) {
|
|
608
|
+
try {
|
|
609
|
+
const result = await (grokDeps.spawnCodexLogin ?? spawnCodexLogin)({ deviceAuth: false, codexPath });
|
|
610
|
+
signedIn = result.ok;
|
|
611
|
+
} catch { /* device code remains available */ }
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (signedIn) io.print(' → ChatGPT/Codex 로그인을 씁니다.');
|
|
615
|
+
else io.print(' 브라우저 로그인이 안 됐습니다 — 코드로 이어갑니다');
|
|
616
|
+
}
|
|
617
|
+
if (!signedIn) {
|
|
618
|
+
io.print(' 코드로 로그인합니다…');
|
|
619
|
+
try {
|
|
620
|
+
await (grokDeps.loginWithCodex ?? loginWithCodex)({
|
|
621
|
+
onProgress: (p) => {
|
|
622
|
+
if (p.type === 'user_code') {
|
|
623
|
+
io.print('');
|
|
624
|
+
io.print(` 1) 아무 브라우저에서 열기: ${p.loginUrl}`);
|
|
625
|
+
io.print(` 2) 코드 입력: ${p.userCode}`);
|
|
626
|
+
io.print('');
|
|
627
|
+
assistCodexDeviceLogin(io, p.loginUrl ?? CODEX_DEVICE_LOGIN_URL, p.userCode ?? '', grokDeps);
|
|
628
|
+
io.print(' 로그인을 기다리는 중…');
|
|
629
|
+
}
|
|
630
|
+
if (p.type === 'saved') io.print(' 로그인했습니다. 저장했습니다.');
|
|
631
|
+
},
|
|
632
|
+
});
|
|
633
|
+
} catch (err: any) {
|
|
634
|
+
io.print(` ! OAuth failed: ${err?.message ?? err}`);
|
|
635
|
+
io.print(` Navigate to ${CODEX_DEVICE_LOGIN_URL} manually,`);
|
|
636
|
+
io.print(` or re-run \`elanous login openai-codex\` / \`elanous codex setup\` later.`);
|
|
637
|
+
}
|
|
479
638
|
}
|
|
480
639
|
} else if (mode === 'apikey') {
|
|
481
640
|
// PR-Δ14 — askValidated catches blank/short/whitespace before
|
|
@@ -493,6 +652,8 @@ async function askLLM(
|
|
|
493
652
|
);
|
|
494
653
|
if (key.trim()) out.apiKey = key.trim();
|
|
495
654
|
else if (current.apiKey) out.apiKey = current.apiKey;
|
|
655
|
+
} else if (mode === 'oauth-keep') {
|
|
656
|
+
io.print(' → using your existing ChatGPT/Codex login.');
|
|
496
657
|
} else {
|
|
497
658
|
io.print(' → skipped. Run `elanous codex setup` or `elanous login openai-codex` later.');
|
|
498
659
|
}
|
|
@@ -1163,6 +1324,13 @@ export interface RunWizardOpts {
|
|
|
1163
1324
|
grokDeps?: GrokOnboardingDeps;
|
|
1164
1325
|
/** Internal ownership transfer for wrappers that explicitly complete IO. */
|
|
1165
1326
|
deferComplete?: boolean;
|
|
1327
|
+
/** Post-save connections; injection keeps wizard tests off the real daemon/token store. */
|
|
1328
|
+
continueHereDeps?: {
|
|
1329
|
+
showNexus?: () => Promise<Pick<NexusShowResult, 'urls' | 'status' | 'instance'>>;
|
|
1330
|
+
probeTailscale?: () => Promise<TailscaleProbe>;
|
|
1331
|
+
issueToken?: () => Pick<IssuedTempToken, 'token'>;
|
|
1332
|
+
renderQr?: (link: string) => string | undefined;
|
|
1333
|
+
};
|
|
1166
1334
|
}
|
|
1167
1335
|
|
|
1168
1336
|
/** Pick the default IO. TTY → `fullScreenIO` (Phase 5: cleared
|
|
@@ -1206,7 +1374,7 @@ export class OnboardingRefusedError extends Error {
|
|
|
1206
1374
|
export function handleOnboardingRefusal(error: unknown, entrance: 'agent' | 'other'): boolean {
|
|
1207
1375
|
if (!(error instanceof OnboardingRefusedError) || error.code !== 'onboarding-refused') return false;
|
|
1208
1376
|
if (entrance === 'agent' && error.reason === 'non-tty') {
|
|
1209
|
-
console.error(
|
|
1377
|
+
console.error(`elanous agent needs a configured LLM. Run \`elanous onboarding\` in a terminal, or ${unattendedSetupHint()} for unattended setup.`);
|
|
1210
1378
|
} else {
|
|
1211
1379
|
console.error(error.message);
|
|
1212
1380
|
}
|
|
@@ -1231,7 +1399,7 @@ function refuseInteractiveOnboardingIfNeeded(opts: RunWizardOpts, path: string):
|
|
|
1231
1399
|
if (nonTTY) {
|
|
1232
1400
|
throw new OnboardingRefusedError(
|
|
1233
1401
|
'대화형 온보딩은 stdin TTY가 있는 자리에서만 실행할 수 있다. '
|
|
1234
|
-
+
|
|
1402
|
+
+ `무인 설정은 ${unattendedSetupHint()}를 사용하라.`,
|
|
1235
1403
|
'non-tty',
|
|
1236
1404
|
);
|
|
1237
1405
|
}
|
|
@@ -1375,14 +1543,107 @@ export async function runOnboarding(opts: RunWizardOpts = {}): Promise<UserConfi
|
|
|
1375
1543
|
io.print('');
|
|
1376
1544
|
io.print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
1377
1545
|
io.print(` ${m.setupComplete}`);
|
|
1546
|
+
// ER1 — first-run consent notice (default on · 대표 10-01): what is sent and how to turn it off.
|
|
1547
|
+
io.print(' 오류가 나면 운영팀에 자동으로 알립니다(오류 내용·버전·무작위 설치 ID — 이름·메일 없이). 끄기: elanous config set errorReports.enabled false');
|
|
1378
1548
|
io.print(` Provider : ${marked.llm.provider}${marked.llm.model ? ` (${marked.llm.model})` : ''}`);
|
|
1379
|
-
|
|
1380
|
-
|
|
1549
|
+
// EN5 — show what the loader will actually read (resolveSkillSources), not the raw preset fields.
|
|
1550
|
+
const skillSources = resolveSkillSources(marked).filter((source) => source.enabled);
|
|
1551
|
+
io.print(` Skills : ${marked.skills.activeSet} — ${skillSources.length} dir(s)`);
|
|
1552
|
+
for (const source of skillSources) io.print(` ${source.path}${source.kind === 'preset' ? '' : ` (${source.id})`}`);
|
|
1553
|
+
// EN9 — skills the person already has in another agent: one line, one command (registers in place, no copy).
|
|
1554
|
+
try {
|
|
1555
|
+
const { CONNECT_AGENTS, planConnect } = await import('./skills/connect.js');
|
|
1556
|
+
const active = defaultSkillDirs(marked);
|
|
1557
|
+
for (const agent of CONNECT_AGENTS) {
|
|
1558
|
+
const plan = planConnect(agent, active);
|
|
1559
|
+
if (plan.state === 'new') io.print(` 쓰던 스킬 ${plan.skills.length}개 찾음(${agent}) — 가져오려면: elanous connect ${agent}`);
|
|
1560
|
+
}
|
|
1561
|
+
} catch { /* advisory */ }
|
|
1562
|
+
// EN13 — a bot the person already runs elsewhere: one line, one command (values never shown).
|
|
1563
|
+
try {
|
|
1564
|
+
const { channelImportHint } = await import('./channel-import/run.js');
|
|
1565
|
+
const hint = channelImportHint();
|
|
1566
|
+
if (hint) io.print(` ${hint}`);
|
|
1567
|
+
} catch { /* advisory */ }
|
|
1381
1568
|
io.print(` Obsidian : ${marked.obsidian.vault}`);
|
|
1382
1569
|
io.print(` Telegram : ${marked.telegram.enabled ? 'enabled' : 'disabled'}`);
|
|
1383
1570
|
io.print(` Discord : ${marked.discord.enabled ? 'enabled' : 'disabled'}`);
|
|
1384
1571
|
io.print(` ${i18nFormat(m.setupRerunHint, { cmd: 'elanous setup' })}`);
|
|
1385
1572
|
io.print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
1573
|
+
|
|
1574
|
+
const interactive = !io.nonInteractive && !!(opts.io || input.isTTY);
|
|
1575
|
+
let shown: Pick<NexusShowResult, 'urls' | 'status' | 'instance'> | undefined;
|
|
1576
|
+
try {
|
|
1577
|
+
const showNexus = opts.continueHereDeps?.showNexus ?? (async () => {
|
|
1578
|
+
const { runNexusShow } = await import('./cli/nexus-show.js');
|
|
1579
|
+
return runNexusShow({ format: 'json', out: { log: () => {}, error: () => {} } });
|
|
1580
|
+
});
|
|
1581
|
+
shown = await showNexus();
|
|
1582
|
+
} catch { /* Config was saved; daemon discovery is advisory. */ }
|
|
1583
|
+
const daemonRunning = shown?.status === 'unregistered'
|
|
1584
|
+
|| (shown?.status === 'registered' && shown.instance?.alive === true);
|
|
1585
|
+
const pwaUrl = daemonRunning ? shown?.urls?.pwa.tailnet ?? shown?.urls?.pwa.loopback : undefined;
|
|
1586
|
+
let phoneLink: string | undefined;
|
|
1587
|
+
if (interactive && daemonRunning && shown?.urls?.rest) {
|
|
1588
|
+
try {
|
|
1589
|
+
const { pickPhoneBase, endpointFromBaseUrl, buildPhoneConnectLink } = await import('./cli/phone-link.js');
|
|
1590
|
+
const picked = pickPhoneBase(shown.urls.rest);
|
|
1591
|
+
const endpoint = endpointFromBaseUrl(picked.base);
|
|
1592
|
+
if (endpoint) {
|
|
1593
|
+
const issued = opts.continueHereDeps?.issueToken
|
|
1594
|
+
? opts.continueHereDeps.issueToken()
|
|
1595
|
+
: await (async () => {
|
|
1596
|
+
const { issueTempToken, parseTtl } = await import('./auth/temp-tokens.js');
|
|
1597
|
+
return issueTempToken({ ttlMs: parseTtl('24h'), label: 'phone' });
|
|
1598
|
+
})();
|
|
1599
|
+
phoneLink = buildPhoneConnectLink(endpoint, issued.token);
|
|
1600
|
+
try { debug.log('onboarding.continue-here', 'phone-link-issued', { kind: picked.kind, tls: endpoint.tls, temp: true }); }
|
|
1601
|
+
catch { /* Logging must not interrupt setup. */ }
|
|
1602
|
+
}
|
|
1603
|
+
} catch { /* No token/link on failure; show the manual command instead. */ }
|
|
1604
|
+
}
|
|
1605
|
+
let tailscale: { host?: string } | undefined;
|
|
1606
|
+
let installed = false;
|
|
1607
|
+
let alive = false;
|
|
1608
|
+
let probeTimer: ReturnType<typeof setTimeout> | undefined;
|
|
1609
|
+
try {
|
|
1610
|
+
const probe = opts.continueHereDeps?.probeTailscale ?? (async () => {
|
|
1611
|
+
const { probeTailscale } = await import('./nexus/onboarding/tailscale-probe.js');
|
|
1612
|
+
return probeTailscale({ timeoutMs: 2000 });
|
|
1613
|
+
});
|
|
1614
|
+
const status = await Promise.race([
|
|
1615
|
+
probe(),
|
|
1616
|
+
new Promise<undefined>(resolve => { probeTimer = setTimeout(() => resolve(undefined), 2000); }),
|
|
1617
|
+
]);
|
|
1618
|
+
if (status) {
|
|
1619
|
+
installed = status.installed;
|
|
1620
|
+
alive = status.alive;
|
|
1621
|
+
// A bare hostname has no HTTPS certificate — offer the command without an address.
|
|
1622
|
+
if (installed && alive) tailscale = status.magicDnsHost ? { host: status.magicDnsHost } : {};
|
|
1623
|
+
}
|
|
1624
|
+
} catch { /* Tailscale discovery is advisory. */ }
|
|
1625
|
+
finally { if (probeTimer) clearTimeout(probeTimer); }
|
|
1626
|
+
try { debug.log('onboarding.tailscale', 'probed', { installed, alive, offered: !!tailscale }); }
|
|
1627
|
+
catch { /* Logging must not interrupt setup. */ }
|
|
1628
|
+
const directions = continueHereLines({ daemonRunning, pwaUrl, phoneLink, telegramEnabled: marked.telegram.enabled, interactive, tailscale });
|
|
1629
|
+
if (io.nonInteractive) {
|
|
1630
|
+
for (const line of directions) process.stdout.write(`${line}\n`);
|
|
1631
|
+
} else {
|
|
1632
|
+
io.print('');
|
|
1633
|
+
for (const line of directions) io.print(line);
|
|
1634
|
+
}
|
|
1635
|
+
if (interactive && phoneLink) {
|
|
1636
|
+
try {
|
|
1637
|
+
const renderQr = opts.continueHereDeps?.renderQr ?? ((link: string) => {
|
|
1638
|
+
const qr = spawnSync('qrencode', ['-t', 'ANSIUTF8', link], { encoding: 'utf8' });
|
|
1639
|
+
return qr.status === 0 ? qr.stdout : undefined;
|
|
1640
|
+
});
|
|
1641
|
+
const qr = renderQr(phoneLink);
|
|
1642
|
+
if (qr) io.print(qr);
|
|
1643
|
+
} catch { /* qrencode is optional, exactly as with phone link. */ }
|
|
1644
|
+
}
|
|
1645
|
+
try { debug.log('onboarding.continue-here', 'shown', { daemonRunning, interactive, phoneLinkIssued: !!phoneLink, telegramEnabled: marked.telegram.enabled }); }
|
|
1646
|
+
catch { /* Logging must not interrupt setup. */ }
|
|
1386
1647
|
if (!opts.deferComplete) io.complete?.();
|
|
1387
1648
|
return marked;
|
|
1388
1649
|
} finally {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// PS1 — 페르소나 프리셋(MK 문면 · persona-presets/<id>.yaml)을 «내 페르소나»로 만든다.
|
|
2
|
+
// 용어: «페르소나»(대표 10-01) — 사용자가 고르고 꾸미는 AI 동료. ⚠️ `SessionSurfaceId`(이름만 Surface 인 캐릭터 축) ·
|
|
3
|
+
// `modelTier.persona`(모델 등급 축)와는 «다른 것»이다 — 여기서 만드는 것은 기존 페르소나 저장소(`resolveStatePersonaDir()`,
|
|
4
|
+
// Discord·PWA 가 이미 읽는 `<id>.yaml`)의 한 파일이다. 프리셋 원본은 고치지 않는다.
|
|
5
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { parse, stringify } from 'yaml';
|
|
8
|
+
|
|
9
|
+
export interface PersonaPreset {
|
|
10
|
+
personaId: string;
|
|
11
|
+
displayName: string;
|
|
12
|
+
names: string[];
|
|
13
|
+
role: string;
|
|
14
|
+
title?: string | null;
|
|
15
|
+
oneLine: string;
|
|
16
|
+
voice: { rule: string; examples: string[] };
|
|
17
|
+
tasks: Array<{ when: string; what: string }>;
|
|
18
|
+
tools: Array<{ name: string; from: string; optional?: boolean }>;
|
|
19
|
+
firstQuestions: string[];
|
|
20
|
+
askBefore?: string[];
|
|
21
|
+
doesNot: string[];
|
|
22
|
+
forWhom: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface PresetCommon { loop_start?: string; always_ask?: string[]; memory?: string; title?: string }
|
|
26
|
+
|
|
27
|
+
/** `persona-presets/` shipped with this package (src/persona → ../../persona-presets). */
|
|
28
|
+
export function presetsDir(): string {
|
|
29
|
+
return join(import.meta.dir, '..', '..', 'persona-presets');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function loadPresetIndex(dir = presetsDir()): { order: string[]; common: PresetCommon } {
|
|
33
|
+
const index = parse(readFileSync(join(dir, '_index.yaml'), 'utf8')) as { presets?: string[]; common?: PresetCommon };
|
|
34
|
+
return { order: index.presets ?? [], common: index.common ?? {} };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Presets in the index order; files not in the index come last (sorted). */
|
|
38
|
+
export function loadPresets(dir = presetsDir()): PersonaPreset[] {
|
|
39
|
+
if (!existsSync(dir)) return [];
|
|
40
|
+
const { order } = loadPresetIndex(dir);
|
|
41
|
+
const files = readdirSync(dir).filter((f) => f.endsWith('.yaml') && !f.startsWith('_')).map((f) => f.slice(0, -5));
|
|
42
|
+
const ids = [...order.filter((id) => files.includes(id)), ...files.filter((id) => !order.includes(id)).sort()];
|
|
43
|
+
return ids.map((id) => parse(readFileSync(join(dir, `${id}.yaml`), 'utf8')) as PersonaPreset);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Find by id or by any of its names (case-insensitive). */
|
|
47
|
+
export function findPreset(presets: readonly PersonaPreset[], query: string): PersonaPreset | undefined {
|
|
48
|
+
const q = query.trim().toLowerCase();
|
|
49
|
+
return presets.find((p) => p.personaId === q) ?? presets.find((p) => p.names.some((n) => n.toLowerCase() === q) || p.role === query.trim());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The persona's working instructions, assembled from the preset (and the shared rules). */
|
|
53
|
+
export function presetSystemPrompt(p: PersonaPreset, common: PresetCommon, name: string): string {
|
|
54
|
+
const lines = [
|
|
55
|
+
`너는 «${name}», ${p.role}다. ${p.oneLine}`,
|
|
56
|
+
'',
|
|
57
|
+
`말투: ${p.voice.rule}`,
|
|
58
|
+
...p.voice.examples.map((e) => ` 예) ${e}`),
|
|
59
|
+
'',
|
|
60
|
+
'맡은 일:',
|
|
61
|
+
...p.tasks.map((t) => `- ${t.when}: ${t.what}`),
|
|
62
|
+
'',
|
|
63
|
+
`처음 만나면 묻는다: ${p.firstQuestions.join(' / ')}`,
|
|
64
|
+
`먼저 확인을 받는다: ${[...(p.askBefore ?? []), ...(common.always_ask ?? [])].join(' · ')}`,
|
|
65
|
+
'하지 않는다:',
|
|
66
|
+
...p.doesNot.map((d) => `- ${d}`),
|
|
67
|
+
];
|
|
68
|
+
if (common.loop_start) lines.push('', `시작 방식: ${common.loop_start}`);
|
|
69
|
+
return lines.join('\n');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** ASCII id for the profile file: from --name when it is latin, else the preset id; `-2`, `-3`… when taken. */
|
|
73
|
+
export function choosePersonaId(preset: PersonaPreset, name: string | undefined, taken: ReadonlySet<string>): string {
|
|
74
|
+
const fromName = name?.toLowerCase().normalize('NFKD').replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
75
|
+
const base = fromName && /^[a-z0-9]/.test(fromName) ? fromName.slice(0, 32) : preset.personaId;
|
|
76
|
+
if (!taken.has(base)) return base;
|
|
77
|
+
for (let i = 2; ; i++) if (!taken.has(`${base}-${i}`)) return `${base}-${i}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The profile YAML written to the persona store (the existing loader reads it; `preset` records where it came from). */
|
|
81
|
+
export function presetToProfileYaml(p: PersonaPreset, common: PresetCommon, opts: { personaId: string; name?: string; title?: string }): string {
|
|
82
|
+
const name = opts.name?.trim() || p.names[0] || p.displayName;
|
|
83
|
+
const title = opts.title ?? p.title ?? undefined;
|
|
84
|
+
const profile = {
|
|
85
|
+
personaId: opts.personaId,
|
|
86
|
+
displayName: opts.name?.trim() ? `${opts.name.trim()} · ${p.role}` : p.displayName,
|
|
87
|
+
description: p.oneLine,
|
|
88
|
+
systemPrompt: presetSystemPrompt(p, common, name),
|
|
89
|
+
preset: { id: p.personaId, role: p.role, ...(title ? { title } : {}), tools: p.tools.map((t) => t.name), firstQuestions: p.firstQuestions },
|
|
90
|
+
};
|
|
91
|
+
return `# 프리셋 «${p.personaId}» 에서 만든 페르소나 — elanous persona add (원본: persona-presets/${p.personaId}.yaml)\n${stringify(profile, { lineWidth: 0 })}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Short block for `persona show` and `persona list`. */
|
|
95
|
+
export function describePreset(p: PersonaPreset): string[] {
|
|
96
|
+
return [
|
|
97
|
+
`${p.displayName} — ${p.role}${p.title ? ` (${p.title})` : ''}`,
|
|
98
|
+
` ${p.oneLine}`,
|
|
99
|
+
` 누구에게: ${p.forWhom}`,
|
|
100
|
+
` 맡는 일: ${p.tasks.map((t) => `${t.when} ${t.what}`).join(' / ')}`,
|
|
101
|
+
` 쓰는 도구: ${p.tools.map((t) => (t.optional ? `${t.name}(선택 · 따로 설치)` : t.name)).join(', ')}`,
|
|
102
|
+
` 처음 묻는 것: ${p.firstQuestions.join(' / ')}`,
|
|
103
|
+
` 하지 않는 것: ${p.doesNot.join(' / ')}`,
|
|
104
|
+
];
|
|
105
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
2
|
+
import { resolve, sep } from 'node:path';
|
|
3
|
+
import { parse as parseYaml } from 'yaml';
|
|
4
|
+
import { debug } from '../../debug/log.js';
|
|
5
|
+
import { listNodeKinds } from '../../graph-kinds/registry.js';
|
|
6
|
+
import { loadPluginNodes } from '../../graph-kinds/plugin-nodes.js';
|
|
7
|
+
import { loadPluginManifestFromDir } from '../core/manifest.js';
|
|
8
|
+
import { installPlugin, type InstalledPlugin } from '../install/plugin-install.js';
|
|
9
|
+
import { addNode, type AddNodeOptions, type AddNodeResult } from './node-maker.js';
|
|
10
|
+
|
|
11
|
+
export interface AddAndInstallNodeOptions extends Omit<AddNodeOptions, 'deps'> {
|
|
12
|
+
/** Accept the plugin's requested capabilities without asking (same meaning as `plugin add --yes`). */
|
|
13
|
+
yes?: boolean;
|
|
14
|
+
deps?: {
|
|
15
|
+
addNode?: typeof addNode;
|
|
16
|
+
installPlugin?: typeof installPlugin;
|
|
17
|
+
consent?: (capabilities: string[]) => boolean | Promise<boolean>;
|
|
18
|
+
sync?: (installed: InstalledPlugin) => void | Promise<void>;
|
|
19
|
+
listNodeKinds?: typeof listNodeKinds;
|
|
20
|
+
codex?: NonNullable<AddNodeOptions['deps']>['codex'];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AddAndInstallNodeResult extends Omit<AddNodeResult, 'status' | 'timings'> {
|
|
25
|
+
status: 'installed' | 'failed';
|
|
26
|
+
plugin?: string;
|
|
27
|
+
installedPath?: string;
|
|
28
|
+
timings: AddNodeResult['timings'] & { install: number; sync: number };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function installedNodeManifest(installed: InstalledPlugin, kind: string) {
|
|
32
|
+
const { manifest, inferred } = loadPluginManifestFromDir(installed.path, { id: installed.name });
|
|
33
|
+
if (inferred || manifest.id !== installed.name || manifest.version !== installed.version) {
|
|
34
|
+
throw new Error(`installed plugin manifest mismatch: ${installed.name}@${installed.version}`);
|
|
35
|
+
}
|
|
36
|
+
const root = realpathSync(installed.path);
|
|
37
|
+
const matches = (manifest.contributes.nodes ?? []).some(path => {
|
|
38
|
+
const file = realpathSync(resolve(root, path));
|
|
39
|
+
if (!path.startsWith('./') || !file.startsWith(`${root}${sep}`)) throw new Error(`unsafe installed node path: ${path}`);
|
|
40
|
+
const node: unknown = parseYaml(readFileSync(file, 'utf8'));
|
|
41
|
+
return node !== null && typeof node === 'object' && !Array.isArray(node)
|
|
42
|
+
&& (node as Record<string, unknown>).kind === kind
|
|
43
|
+
&& (node as Record<string, unknown>).graph === 'workflow';
|
|
44
|
+
});
|
|
45
|
+
if (!matches) throw new Error(`installed node not found: ${installed.name}:${kind}`);
|
|
46
|
+
return manifest;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function addAndInstallNode({ dir, request, kind, yes, deps }: AddAndInstallNodeOptions): Promise<AddAndInstallNodeResult> {
|
|
50
|
+
const added = await (deps?.addNode ?? addNode)({ dir, request, kind,
|
|
51
|
+
...(deps?.codex ? { deps: { codex: deps.codex } } : {}),
|
|
52
|
+
});
|
|
53
|
+
const result: AddAndInstallNodeResult = {
|
|
54
|
+
...added, status: 'failed', timings: { ...added.timings, install: 0, sync: 0 },
|
|
55
|
+
};
|
|
56
|
+
if (added.status !== 'added') return result;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const startedInstall = performance.now();
|
|
60
|
+
let installed: InstalledPlugin;
|
|
61
|
+
try {
|
|
62
|
+
installed = await (deps?.installPlugin ?? installPlugin)(added.dir, {
|
|
63
|
+
...(yes === undefined ? {} : { yes }),
|
|
64
|
+
consent: deps?.consent ?? (() => false),
|
|
65
|
+
});
|
|
66
|
+
} finally {
|
|
67
|
+
result.timings.install = Math.round(performance.now() - startedInstall);
|
|
68
|
+
debug.log('plugin.node-install', 'install', { ms: result.timings.install });
|
|
69
|
+
}
|
|
70
|
+
result.plugin = installed.name;
|
|
71
|
+
result.installedPath = installed.path;
|
|
72
|
+
const startedSync = performance.now();
|
|
73
|
+
try {
|
|
74
|
+
const manifest = installedNodeManifest(installed, added.kind);
|
|
75
|
+
if (deps?.sync) await deps.sync(installed);
|
|
76
|
+
else {
|
|
77
|
+
const loaded = loadPluginNodes(installed.path, manifest);
|
|
78
|
+
if (loaded.errors.length) throw new Error(`node sync failed: ${loaded.errors.join('; ')}`);
|
|
79
|
+
}
|
|
80
|
+
const registered = (deps?.listNodeKinds ?? listNodeKinds)('workflow')
|
|
81
|
+
.some(entry => entry.plugin === installed.name && entry.kind === `${installed.name}:${added.kind}`);
|
|
82
|
+
if (!registered) throw new Error(`node kind not registered: ${installed.name}:${added.kind}`);
|
|
83
|
+
result.status = 'installed';
|
|
84
|
+
} finally {
|
|
85
|
+
result.timings.sync = Math.round(performance.now() - startedSync);
|
|
86
|
+
debug.log('plugin.node-install', 'sync', { ms: result.timings.sync, status: result.status });
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
result.errors.push(error instanceof Error ? error.message : String(error));
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|