elanous 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5080-1613752891fcadb4.js → 2984-977cfa339fcd65a5.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8142-011e1f474efb8b33.js → 3190-e9282753e7ebde85.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3995-bee512a7063036ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 4240-7153881b0b377306.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +25 -0
- package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3403.4bc97b4066945a94.js → 5329.8b7e4bca684b6eec.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5791-bb82743fa6299562.js → 763-7d0e1913b5074e98.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7633.0654fe6ea259c1ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → 8d0d9faf-97887ff99713b84e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 91ff6dda-3b33d42d20657429.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +1 -0
- package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-e5bea36f183b25e6.js → webpack-37b8bf78a3c6eec5.js} +1 -1
- package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +1 -0
- package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → qMI0jnlZaBjjm4aIDYNGM}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +29 -27
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +29 -27
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +29 -27
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +26 -24
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +26 -24
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +26 -24
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +29 -27
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +29 -27
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +29 -27
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/icons/elanous-16.png +0 -0
- package/apps/pwa/out/icons/elanous-180.png +0 -0
- package/apps/pwa/out/icons/elanous-192.png +0 -0
- package/apps/pwa/out/icons/elanous-32.png +0 -0
- package/apps/pwa/out/icons/elanous-512.png +0 -0
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +26 -24
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +29 -27
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +29 -27
- package/apps/pwa/out/manifest.webmanifest +5 -2
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +26 -24
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +26 -24
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +29 -27
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +29 -27
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +27 -25
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +29 -27
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +26 -24
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +29 -27
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +29 -27
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +29 -27
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +29 -27
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +29 -27
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +26 -24
- package/apps/pwa/out/sw-strategies.js +44 -0
- package/apps/pwa/out/sw.js +6 -44
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +29 -27
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +29 -27
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +29 -27
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +29 -27
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +29 -27
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +29 -27
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +29 -27
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +29 -27
- package/catalog/resources.yaml +6 -6
- package/docs/goal-context/steward-directives-2026-09-28.md +35 -37
- package/graphs/landing/landing-heal.yaml +1 -1
- package/graphs/release/recipes.yaml +11 -1
- package/graphs/release/release-loop.yaml +9 -2
- package/graphs/video/README.md +1 -0
- package/graphs/video/explainer-line.yaml +58 -0
- package/package.json +3 -1
- package/plugins/card-followup/README.md +33 -0
- package/plugins/card-followup/examples/card-sample.json +7 -0
- package/plugins/card-followup/graphs/card-followup.yaml +16 -0
- package/plugins/card-followup/graphs/recipes.yaml +12 -0
- package/plugins/card-followup/graphs/run-step.ts +172 -0
- package/plugins/card-followup/plugin.json +25 -0
- package/plugins/geo-check/README.md +13 -0
- package/plugins/geo-check/examples/brand-sample.json +8 -0
- package/plugins/geo-check/graphs/geo-check.yaml +20 -0
- package/plugins/geo-check/graphs/recipes.yaml +18 -0
- package/plugins/geo-check/graphs/run-step.ts +177 -0
- package/plugins/geo-check/plugin.json +24 -0
- package/plugins/job-coach/.codex-plugin/plugin.json +18 -1
- package/plugins/job-coach/README.md +42 -0
- package/plugins/job-coach/connectors/ncs/server.ts +4 -3
- package/plugins/job-coach/graphs/gap-judge.ts +44 -0
- package/plugins/job-coach/graphs/recipes.yaml +27 -0
- package/plugins/job-coach/graphs/report-enterprise.yaml +28 -0
- package/plugins/job-coach/graphs/run-step.ts +211 -17
- package/plugins/job-coach/plugin.json +16 -3
- package/plugins/job-coach/skills/enterprise-ai-fit/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-competency/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-needs/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-roadmap/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-task-analysis/SKILL.md +8 -0
- package/plugins/job-coach/skills/interview-to-profile/SKILL.md +18 -1
- package/scripts/backup/elanous-backup.sh +4 -1
- package/scripts/build-docs-site.ts +4 -3
- package/scripts/coord-channel-watch.sh +23 -4
- package/scripts/coord-post.sh +49 -12
- package/scripts/coord-tracks.json +5 -5
- package/scripts/daemon-port-baseline.txt +1 -2
- package/scripts/eval-bot-vs-grok.ts +79 -0
- package/scripts/eval-grok-live-baseline.ts +36 -0
- package/scripts/fixtures/front-route-corpus.sample.json +17 -0
- package/scripts/fleet/node-setup.sh +46 -7
- package/scripts/landing-heal/must-fix-match.ts +26 -0
- package/scripts/landing-heal/run-mission.ts +114 -20
- package/scripts/lib/android-test-device.ts +32 -0
- package/scripts/lib/front-route-measurement.ts +131 -0
- package/scripts/lib/tui-sim-keys.ts +42 -0
- package/scripts/measure-front-route-classifier.ts +56 -0
- package/scripts/media/ocr-leak-scan.sh +21 -0
- package/scripts/public-export.ts +2 -3
- package/scripts/public-import-pr.ts +80 -6
- package/scripts/publish-internal-dist.ts +2 -3
- package/scripts/red-sweep-diff.ts +1 -1
- package/scripts/release-loop/auto-approve-node.ts +72 -0
- package/scripts/release-loop/checklist-gate-node.ts +35 -0
- package/scripts/release-loop/gate-diff.ts +29 -0
- package/scripts/release-loop/gate-node.ts +366 -45
- package/scripts/release-loop/publish-node.ts +7 -3
- package/scripts/release-loop/release-docs.ts +20 -13
- package/scripts/release-loop/shard-plan.ts +47 -0
- package/scripts/release-loop/test-durations.ts +63 -0
- package/scripts/release-loop/upgrade-node.ts +3 -2
- package/scripts/test-audit-coverage-per-file.sh +16 -0
- package/scripts/test-audit-meaningless.ts +88 -0
- package/scripts/test-audit-overlap-and-weak.ts +73 -0
- package/scripts/test-deterministic.ts +27 -5
- package/scripts/tui-sim.ts +59 -14
- package/scripts/ux/bump-installed.sh +20 -0
- package/scripts/ux/lib/cdp.ts +72 -0
- package/scripts/ux/record-page.ts +53 -0
- package/skills/explainer-video/SKILL.md +117 -0
- package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -0
- package/skills/explainer-video/engine/build.mjs +431 -0
- package/skills/explainer-video/engine/reel.mjs +128 -0
- package/skills/explainer-video/engine/reel.sh +8 -0
- package/skills/explainer-video/engine/vo.ts +79 -0
- package/skills/explainer-video/examples/elanous-0.2.5/script.json +155 -0
- package/src/agent/nested-agent-env.ts +1 -0
- package/src/agent-mission/driver.ts +95 -16
- package/src/agent-mission/mission-cli.ts +6 -0
- package/src/agent-mission/resource-ladder.ts +73 -25
- package/src/auth/acp-token.ts +16 -0
- package/src/auth/github-app-token.ts +112 -0
- package/src/autopilot/gh-rest-fallback.ts +177 -0
- package/src/autopilot/mission-origin.ts +20 -4
- package/src/autopilot/mission-phase-track.ts +11 -1
- package/src/autopilot/pr-manager.ts +51 -4
- package/src/autopilot/track-agent.ts +3 -3
- package/src/autopilot/track-courier.ts +2 -2
- package/src/boot/acp-server.ts +2 -16
- package/src/boot/daemon-tools/web-search.ts +2 -1
- package/src/boot/ws-bridge.ts +11 -0
- package/src/chat/index.ts +37 -8
- package/src/chat/log-scroll-key.ts +12 -0
- package/src/chat/submit-trace.ts +30 -0
- package/src/cli/agent-cli.ts +7 -2
- package/src/cli/decisions-cli.ts +94 -0
- package/src/cli/doctor-cli.ts +40 -54
- package/src/cli/doctor-readiness.ts +16 -24
- package/src/cli/intake-cli.ts +1 -1
- package/src/cli/mcp-call.ts +9 -0
- package/src/cli/mcp-gateway-cli.ts +7 -3
- package/src/cli/nexus-ios-bind.ts +25 -8
- package/src/cli/phone-link.ts +117 -0
- package/src/cli/plugin-cli.ts +136 -11
- package/src/cli/pr-lineage.ts +2 -1
- package/src/cli/private-ledger-commands.ts +31 -0
- package/src/cli/pwa-start.ts +3 -0
- package/src/cli/release-cli.ts +97 -7
- package/src/cli/self-update.ts +7 -4
- package/src/cli/start-cli.ts +33 -14
- package/src/dashboard/acp-boot.ts +3 -0
- package/src/dashboard/index.ts +62 -13
- package/src/dashboard/input/no-provider-reply.ts +13 -0
- package/src/dashboard/slash-runtime/unknown-slash-reply.ts +78 -0
- package/src/dashboard/stale-install-check.ts +77 -0
- package/src/decisions/decision-ledger.ts +404 -0
- package/src/domains/outbound-alert.ts +13 -7
- package/src/domains/telegram-kind-route.ts +21 -0
- package/src/execution-loop/dispatch-task.ts +139 -0
- package/src/execution-loop/gate-decision-summary.ts +29 -0
- package/src/execution-loop/gates.ts +197 -0
- package/src/field/field-media.ts +245 -0
- package/src/graph-kinds/installed-plugin-nodes.ts +95 -0
- package/src/graph-kinds/plugin-nodes.ts +10 -2
- package/src/graph-runner/runner.ts +35 -5
- package/src/grounding/token.ts +12 -6
- package/src/harness/harness-cli-command.ts +68 -9
- package/src/harness/harness-pod-dispatch.ts +8 -2
- package/src/harness/harness-stop.ts +147 -18
- package/src/harness/self-send-decide.ts +26 -17
- package/src/harness/self-send-target.ts +22 -1
- package/src/harness/worktree-branch-prefix.ts +4 -3
- package/src/index.ts +50 -13
- package/src/instance/resolve.ts +6 -0
- package/src/intake-plane/collect-telegram-saved.ts +5 -0
- package/src/intake-plane/digest.ts +11 -2
- package/src/live/detail-switch.ts +8 -3
- package/src/llm/usable-llm.ts +32 -0
- package/src/loops/registry.ts +27 -5
- package/src/market/publish.ts +59 -3
- package/src/native-tool-catalog.ts +2 -2
- package/src/nexus/api/field-uploads.ts +143 -0
- package/src/nexus/api/graph-approvals.ts +174 -0
- package/src/nexus/api/harness-api.ts +219 -5
- package/src/nexus/api/http-server.ts +69 -6
- package/src/nexus/api/log-fabric.ts +10 -1
- package/src/nexus/api/mcp-oauth-hint.ts +10 -9
- package/src/nexus/api/merge-approvals.ts +2 -1
- package/src/nexus/api/meta-api.ts +14 -0
- package/src/nexus/api/plugins-credentials-api.ts +47 -0
- package/src/nexus/api/plugins-market.ts +87 -3
- package/src/nexus/api/pod-credential-api.ts +68 -14
- package/src/nexus/api/public-routes.ts +7 -0
- package/src/nexus/api/trace.ts +57 -6
- package/src/nexus/boot/register-mcp-clients.ts +99 -14
- package/src/nexus/index.ts +33 -32
- package/src/nexus/outbound/router.ts +1 -1
- package/src/notify/llm-budget-alert.ts +119 -0
- package/src/onboarding.ts +25 -2
- package/src/plugins/core/host.ts +14 -0
- package/src/plugins/install/market-fetch.ts +4 -3
- package/src/plugins/install/plugin-credentials.ts +106 -0
- package/src/plugins/install/plugin-install.ts +163 -32
- package/src/plugins/maker/plugin-maker.ts +208 -0
- package/src/prompt-library/universal-preamble.ts +8 -2
- package/src/relay/channel.ts +184 -0
- package/src/relay/mux.ts +182 -0
- package/src/relay/test-vectors.json +29 -0
- package/src/release-loop/checklist.ts +217 -0
- package/src/release-loop/release-note.ts +6 -3
- package/src/seat-address/seat-address.ts +34 -0
- package/src/self-dev/dev-cli.ts +40 -20
- package/src/self-dev/dev-pipeline.ts +77 -1
- package/src/self-implement/gate-baseline.ts +3 -1
- package/src/self-implement/goal-author.ts +18 -1
- package/src/self-implement/host-regate.ts +6 -2
- package/src/self-implement/orchestrator.ts +7 -2
- package/src/self-implement/run-ledger-compact.ts +1 -1
- package/src/self-implement/seams.ts +4 -2
- package/src/session/index.ts +23 -10
- package/src/skills/tools/dev-harness.ts +42 -0
- package/src/steward/steward-cards.ts +45 -0
- package/src/steward/triage.ts +50 -14
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +61 -3
- package/src/task-orchestrator/surfaces/pod-bun-cache.ts +18 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +36 -6
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +39 -9
- package/src/task-orchestrator/surfaces/pod-source-bundle.ts +16 -1
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +51 -13
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -28
- package/src/telegram-report.ts +12 -6
- package/src/telegram.ts +108 -1
- package/src/tui-client/acp-transport-ws-client.ts +72 -5
- package/src/tui.ts +3 -2
- package/src/user-config.ts +32 -2
- package/src/util/spawn-sync-output.ts +15 -0
- package/src/version/code-revision.ts +1 -1
- package/src/version/packed-revision.json +1 -1
- package/src/video-pipeline/recipes/explainer.ts +213 -0
- package/src/video-pipeline/walk-line.ts +3 -2
- package/src/web-search/free.ts +77 -0
- package/src/web-search/index.ts +2 -0
- package/src/workflow-runtime/deps-bridge.ts +4 -3
- package/src/workflow-runtime/plugin-kind-node.ts +32 -4
- package/src/workflow-runtime/types.ts +2 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1191-7b13a24117a689fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-3fcae5186c64fe6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
- package/apps/pwa/out/_next/static/chunks/319-d37df1b6e0cbeff4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4836-3c93795ab834381e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6090-1bd6077591603947.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7457-f5cb1cc8db9f7453.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-0059d82bdb4812bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-52467f244755917f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-21c3261af6ab377c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1991445b8ea86274.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-4ebfaee2369fe248.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-9fa879ebf5959096.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ca7c3cc305c3285a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9951930add3d2051.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-4472ddf1e2458e69.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4de08d4b766f134b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7a975e2b9db1dfb7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-49270714ef35bb96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-33fbdf353e2a5982.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-259f122e2dd64317.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-65b82726753658e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4b8df3748dc06285.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-112f7e477bda77fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-542a5c0006da637c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-63ffb347831262ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-98e4e359d97263b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-97e24ac675ce576e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ac6391f26391d89d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f45c9d45ffc43bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-36995b9cb5d98218.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-0c795b577988d63f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b4311e2f71c6735a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-eb2381074df20dba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-b01336e64eba01ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7af7f21c1f2858ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-a98ecd364586c89b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-57f0502dfbfa089b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-3467d2e1abd8a14a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-7842ca23d53ba682.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-ae9ac882912b0198.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-3a0d72e3f783bcef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-f3b39fe8358c2a97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-5a22af3181ac2afb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-1457627bb302a22c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/css/e66b7f5efc8df3f3.css +0 -1
- /package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → qMI0jnlZaBjjm4aIDYNGM}/_ssgManifest.js +0 -0
|
@@ -6,6 +6,7 @@ import { getElanousConfigDir } from '../elanous-config-dir.js';
|
|
|
6
6
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
7
7
|
import { issueMcpPat, listMcpPats, revokeMcpPat } from '../mcp-gateway/pat-store.js';
|
|
8
8
|
import { startMcpGateway } from '../mcp-gateway/gateway.js';
|
|
9
|
+
import { resolveDaemonEndpoint } from '../nexus/daemon-endpoint.js';
|
|
9
10
|
|
|
10
11
|
function privateTokenFile(path: string): string {
|
|
11
12
|
const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
@@ -47,12 +48,15 @@ export function registerMcpGatewayCommands(mcp: Command): void {
|
|
|
47
48
|
.description('Owner-PAT authenticated public MCP relay to the scoped NEXUS endpoint')
|
|
48
49
|
.option('--host <host>', 'Bind host', '127.0.0.1')
|
|
49
50
|
.option('--port <port>', 'Bind port', '31482')
|
|
50
|
-
.
|
|
51
|
+
.option('--nexus-url <url>', 'NEXUS base URL. Default: the current daemon (elanous nexus show).')
|
|
51
52
|
.option('--public-url <url>', 'Public HTTPS origin used in protected resource metadata')
|
|
52
53
|
.option('--nexus-token-file <path>', 'Private file holding the mcp-public NEXUS token; use - for stdin')
|
|
53
|
-
.action(safeAction(async (opts: { host: string; port: string; nexusUrl
|
|
54
|
+
.action(safeAction(async (opts: { host: string; port: string; nexusUrl?: string; publicUrl?: string; nexusTokenFile?: string }) => {
|
|
54
55
|
const port = Number(opts.port);
|
|
55
56
|
if (!Number.isSafeInteger(port) || port < 1 || port > 65535) throw new Error('invalid gateway port');
|
|
57
|
+
// 데몬 주소는 짐작하지 않는다 — 통합관제(daemon-endpoint)에 묻고, 모르면 멈춘다(대표 09-28).
|
|
58
|
+
const nexusUrl = opts.nexusUrl ?? resolveDaemonEndpoint()?.baseUrl;
|
|
59
|
+
if (!nexusUrl) throw new Error('daemon address unknown — pass --nexus-url or start the daemon (elanous nexus show)');
|
|
56
60
|
if (!opts.publicUrl) throw new Error('--public-url <url> required for protected-resource metadata');
|
|
57
61
|
if (!opts.nexusTokenFile) throw new Error('--nexus-token-file <path|-> required (no token argv)');
|
|
58
62
|
if (opts.nexusTokenFile === '-' && process.stdin.isTTY) throw new Error('pipe the nexus token on stdin');
|
|
@@ -66,7 +70,7 @@ export function registerMcpGatewayCommands(mcp: Command): void {
|
|
|
66
70
|
if (!stat.isFile() || (stat.mode & 0o077) !== 0) throw new Error('PAT store must be a private regular file (0600)');
|
|
67
71
|
} finally { closeSync(fd); }
|
|
68
72
|
listMcpPats(patRoot);
|
|
69
|
-
const server = startMcpGateway({ host: opts.host, port, nexusUrl
|
|
73
|
+
const server = startMcpGateway({ host: opts.host, port, nexusUrl, nexusToken, patRoot, publicUrl: opts.publicUrl });
|
|
70
74
|
process.stderr.write(`MCP gateway listening on ${server.hostname}:${server.port}\n`);
|
|
71
75
|
}));
|
|
72
76
|
|
|
@@ -59,6 +59,9 @@ export interface IosBindOpts {
|
|
|
59
59
|
readTokenFn?: () => string | null;
|
|
60
60
|
/** Test seam — host fallback (production daily driver 의 default). */
|
|
61
61
|
hostFallback?: string;
|
|
62
|
+
/** Test seam — `defaults write` 가 쓸 도메인. 기본은 설치된 앱 «컨테이너»의 plist 경로다.
|
|
63
|
+
* `spawnSyncFn` 만 주입된 시험은 bundle id 그대로(실 `xcrun` 을 부르지 않는다). */
|
|
64
|
+
resolveDomainFn?: (bundleId: string) => string;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
export interface IosBindResult {
|
|
@@ -90,6 +93,16 @@ function defaultReadToken(): string | null {
|
|
|
90
93
|
}
|
|
91
94
|
}
|
|
92
95
|
|
|
96
|
+
/** 앱이 실제로 읽는 도메인 — 설치된 앱의 데이터 컨테이너 plist(확장자 없이).
|
|
97
|
+
* ⚠️ `defaults write <bundleId>` 는 시뮬레이터 «전역» 도메인에 쓴다. 앱은 자기 컨테이너 plist 를 읽으므로,
|
|
98
|
+
* 앱이 한 번이라도 설정을 저장한 뒤로는 그 값이 이긴다(2026-09-30 실측: 연결 링크로 받은 만료 임시 토큰이
|
|
99
|
+
* 컨테이너에 남아 ios-bind 가 «✓» 를 찍고도 앱은 401 을 받았다). 컨테이너를 못 찾으면 bundle id 로 폴백한다. */
|
|
100
|
+
function defaultResolveDomain(bundleId: string): string {
|
|
101
|
+
const r = defaultSpawnSync('xcrun', ['simctl', 'get_app_container', 'booted', bundleId, 'data'], { stdio: 'pipe' });
|
|
102
|
+
const dir = r.status === 0 ? String(r.stdout ?? '').trim() : '';
|
|
103
|
+
return dir ? join(dir, 'Library', 'Preferences', bundleId) : bundleId;
|
|
104
|
+
}
|
|
105
|
+
|
|
93
106
|
/** UserDefaults write 1 회. spawnSync exit code 0 = 성공. */
|
|
94
107
|
function defaultsWrite(
|
|
95
108
|
spawnFn: typeof defaultSpawnSync,
|
|
@@ -119,6 +132,8 @@ export function runNexusIosBind(opts: IosBindOpts = {}): IosBindResult {
|
|
|
119
132
|
const readToken = opts.readTokenFn ?? defaultReadToken;
|
|
120
133
|
const hostFallback = opts.hostFallback ?? DEFAULT_HOST_FALLBACK;
|
|
121
134
|
const dryRun = opts.dryRun ?? false;
|
|
135
|
+
const resolveDomain = opts.resolveDomainFn
|
|
136
|
+
?? (opts.spawnSyncFn || dryRun ? (id: string) => id : defaultResolveDomain);
|
|
122
137
|
|
|
123
138
|
// 1) daemon 현재 host/port — override 가 우선 · 없으면 runtime sidecar ·
|
|
124
139
|
// port 가 없으면 현재 우주의 daemon endpoint 를 조회한다.
|
|
@@ -150,20 +165,22 @@ export function runNexusIosBind(opts: IosBindOpts = {}): IosBindResult {
|
|
|
150
165
|
const tokenRaw = opts.noToken ? null : readToken();
|
|
151
166
|
const token = tokenRaw ?? '';
|
|
152
167
|
|
|
153
|
-
// 3) UserDefaults write 3 회 (host · port · token)
|
|
168
|
+
// 3) UserDefaults write 3 회 (host · port · token) — 앱 컨테이너 도메인에
|
|
169
|
+
const domain = resolveDomain(bundleId);
|
|
154
170
|
const lines: string[] = [];
|
|
155
|
-
|
|
171
|
+
if (domain !== bundleId) lines.push(` domain 앱 컨테이너 (${bundleId}) — 앱이 실행 중이면 끄고 다시 켜야 읽는다`);
|
|
172
|
+
const hostResult = defaultsWrite(spawnFn, domain, 'nexusHost', 'string', host, dryRun);
|
|
156
173
|
lines.push(` host ${hostResult.ok ? '✓' : '✗'} ${hostResult.cmd}`);
|
|
157
|
-
const portResult = defaultsWrite(spawnFn,
|
|
174
|
+
const portResult = defaultsWrite(spawnFn, domain, 'nexusPort', 'int', String(port), dryRun);
|
|
158
175
|
lines.push(` port ${portResult.ok ? '✓' : '✗'} ${portResult.cmd}`);
|
|
159
176
|
|
|
160
177
|
let tokenInjected = false;
|
|
161
178
|
let tokenLength = 0;
|
|
162
179
|
if (!opts.noToken && token.length > 0) {
|
|
163
|
-
const tokenResult = defaultsWrite(spawnFn,
|
|
180
|
+
const tokenResult = defaultsWrite(spawnFn, domain, 'bearerToken', 'string', token, dryRun);
|
|
164
181
|
tokenInjected = tokenResult.ok;
|
|
165
182
|
tokenLength = token.length;
|
|
166
|
-
lines.push(` token ${tokenResult.ok ? '✓' : '✗'} xcrun simctl spawn booted defaults write ${
|
|
183
|
+
lines.push(` token ${tokenResult.ok ? '✓' : '✗'} xcrun simctl spawn booted defaults write ${domain} bearerToken <${tokenLength}-char redacted>`);
|
|
167
184
|
} else if (opts.noToken) {
|
|
168
185
|
lines.push(` token — (skipped · --no-token)`);
|
|
169
186
|
} else {
|
|
@@ -173,19 +190,19 @@ export function runNexusIosBind(opts: IosBindOpts = {}): IosBindResult {
|
|
|
173
190
|
let asciiKeyboardOk = true;
|
|
174
191
|
if (opts.asciiKeyboard !== undefined) {
|
|
175
192
|
const value = opts.asciiKeyboard ? 'true' : 'false';
|
|
176
|
-
const r = defaultsWrite(spawnFn,
|
|
193
|
+
const r = defaultsWrite(spawnFn, domain, 'asciiKeyboard', 'bool', value, dryRun);
|
|
177
194
|
asciiKeyboardOk = r.ok;
|
|
178
195
|
lines.push(` ascii ${r.ok ? '✓' : '✗'} ${r.cmd}`);
|
|
179
196
|
}
|
|
180
197
|
|
|
181
198
|
let seedPromptOk = true;
|
|
182
199
|
if (opts.seedPrompt !== undefined) {
|
|
183
|
-
const r = defaultsWrite(spawnFn,
|
|
200
|
+
const r = defaultsWrite(spawnFn, domain, 'seedPrompt', 'string', opts.seedPrompt, dryRun);
|
|
184
201
|
seedPromptOk = r.ok;
|
|
185
202
|
const preview = opts.seedPrompt.length > 24
|
|
186
203
|
? opts.seedPrompt.slice(0, 24) + '…'
|
|
187
204
|
: opts.seedPrompt;
|
|
188
|
-
lines.push(` seed ${r.ok ? '✓' : '✗'} xcrun simctl spawn booted defaults write ${
|
|
205
|
+
lines.push(` seed ${r.ok ? '✓' : '✗'} xcrun simctl spawn booted defaults write ${domain} seedPrompt "${preview}" (${opts.seedPrompt.length} chars)`);
|
|
189
206
|
}
|
|
190
207
|
|
|
191
208
|
const allOk = hostResult.ok && portResult.ok
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// `elanous phone link` — 휴대폰 앱을 이 Mac 의 넥서스에 «한 번에» 붙이는 연결 링크(⊕ 터미널 QR).
|
|
2
|
+
// 대표 2026-09-30: 아이폰 첫 화면이 개발용 연결 카드라 복잡하다 → 앱은 링크 하나로 온보딩한다.
|
|
3
|
+
// 링크 = `elanous://connect?host=…&port=…&tls=1|0&token=…` — 앱이 `.onOpenURL` 로 받아 저장하고 바로 연결한다.
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { debug } from '../debug/log.js';
|
|
6
|
+
|
|
7
|
+
export interface PhoneEndpoint { host: string; port: number; tls: boolean }
|
|
8
|
+
|
|
9
|
+
/** REST/PWA 기준 URL(예 `https://mbp.x.ts.net:31415/v1/`) → 앱이 쓰는 host·port·tls. 못 읽으면 null. */
|
|
10
|
+
export function endpointFromBaseUrl(base: string): PhoneEndpoint | null {
|
|
11
|
+
let u: URL;
|
|
12
|
+
try { u = new URL(base); } catch { return null; }
|
|
13
|
+
if (u.protocol !== 'http:' && u.protocol !== 'https:') return null;
|
|
14
|
+
const tls = u.protocol === 'https:';
|
|
15
|
+
const port = u.port ? Number(u.port) : tls ? 443 : 80;
|
|
16
|
+
if (!u.hostname || !Number.isInteger(port) || port < 1 || port > 65_535) return null;
|
|
17
|
+
return { host: u.hostname, port, tls };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** 앱이 여는 연결 링크. 토큰은 URL 인코딩한다. */
|
|
21
|
+
export function buildPhoneConnectLink(ep: PhoneEndpoint, token: string): string {
|
|
22
|
+
const q = new URLSearchParams({ host: ep.host, port: String(ep.port), tls: ep.tls ? '1' : '0', token });
|
|
23
|
+
return `elanous://connect?${q.toString()}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 어느 주소를 휴대폰에 줄까 — 기본은 tailnet(휴대폰이 닿는다) · `local` 이면 루프백(같은 Mac 의 시뮬레이터). */
|
|
27
|
+
export function pickPhoneBase(urls: { loopback: string; tailnet?: string }, opts: { local?: boolean } = {}): { base: string; kind: 'tailnet' | 'loopback' } {
|
|
28
|
+
if (!opts.local && urls.tailnet) return { base: urls.tailnet, kind: 'tailnet' };
|
|
29
|
+
return { base: urls.loopback, kind: 'loopback' };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** USB 로 꽂힌 안드로이드에 링크를 «직접» 건넨다 — `adb reverse` 로 폰의 127.0.0.1:<port> 를 이 Mac 으로 잇고
|
|
33
|
+
* `am start` 로 elanous://connect 를 연다. 토큰은 화면·로그에 찍지 않는다(adb 인자로만 간다). */
|
|
34
|
+
export function androidDeliverySteps(link: string, port: number, serial?: string): string[][] {
|
|
35
|
+
const dev = serial ? ['-s', serial] : [];
|
|
36
|
+
return [
|
|
37
|
+
[...dev, 'reverse', `tcp:${port}`, `tcp:${port}`],
|
|
38
|
+
[...dev, 'shell', 'am', 'start', '-a', 'android.intent.action.VIEW', '-d', `'${link}'`, 'com.elanvitalai.elanous.android'],
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface RunPhoneLinkOpts {
|
|
43
|
+
local?: boolean;
|
|
44
|
+
/** USB 안드로이드에 adb 로 바로 건넨다(루프백 ⊕ adb reverse). 문자열이면 기기 serial. */
|
|
45
|
+
android?: boolean | string;
|
|
46
|
+
temp?: boolean;
|
|
47
|
+
ttl?: string;
|
|
48
|
+
qr?: boolean;
|
|
49
|
+
out?: { log: (s: string) => void; error: (s: string) => void };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function runPhoneLink(opts: RunPhoneLinkOpts = {}): Promise<number> {
|
|
53
|
+
const out = opts.out ?? { log: (s: string) => console.log(s), error: (s: string) => console.error(s) };
|
|
54
|
+
const { runNexusShow } = await import('./nexus-show.js');
|
|
55
|
+
const silent = { log: () => {}, error: () => {} };
|
|
56
|
+
const shown = await runNexusShow({ format: 'json', out: silent });
|
|
57
|
+
if (!shown.urls) {
|
|
58
|
+
out.error('✗ 넥서스가 떠 있지 않다 — 먼저 `elanous nexus run` (또는 `elanous nexus status` 로 확인).');
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
const picked = pickPhoneBase(shown.urls.rest, { local: opts.local || !!opts.android });
|
|
62
|
+
const ep = endpointFromBaseUrl(picked.base);
|
|
63
|
+
if (!ep) { out.error(`✗ 넥서스 주소를 읽지 못했다: ${picked.base}`); return 1; }
|
|
64
|
+
|
|
65
|
+
let token: string;
|
|
66
|
+
let tokenNote: string;
|
|
67
|
+
if (opts.temp) {
|
|
68
|
+
const { issueTempToken, parseTtl } = await import('../auth/temp-tokens.js');
|
|
69
|
+
const issued = issueTempToken({ ttlMs: parseTtl(opts.ttl ?? '24h'), label: 'phone' });
|
|
70
|
+
token = issued.token;
|
|
71
|
+
tokenNote = `단기 토큰 · ${issued.expiresAt} 까지 · 폐기: elanous token revoke ${issued.id}`;
|
|
72
|
+
} else {
|
|
73
|
+
const { loadEnvelope } = await import('../auth/token-store.js');
|
|
74
|
+
const env = loadEnvelope();
|
|
75
|
+
if (!env?.active) { out.error('✗ 소유자 토큰이 없다 — `elanous token rotate` 로 한 번 만든 뒤 다시.'); return 1; }
|
|
76
|
+
token = env.active;
|
|
77
|
+
tokenNote = '소유자 토큰(설정의 Bearer 와 같다) · 바꾸려면 elanous token rotate';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const link = buildPhoneConnectLink(ep, token);
|
|
81
|
+
try { debug.log('phone.link', 'issued', { kind: picked.kind, tls: ep.tls, temp: !!opts.temp }); } catch { /* 관측 실패가 발급을 막지 않는다 */ }
|
|
82
|
+
|
|
83
|
+
if (opts.android) {
|
|
84
|
+
const serial = typeof opts.android === 'string' ? opts.android : undefined;
|
|
85
|
+
const lanEp = { ...ep, host: '127.0.0.1', tls: false };
|
|
86
|
+
const androidLink = buildPhoneConnectLink(lanEp, token);
|
|
87
|
+
for (const args of androidDeliverySteps(androidLink, lanEp.port, serial)) {
|
|
88
|
+
const r = spawnSync('adb', args, { encoding: 'utf8' });
|
|
89
|
+
if (r.status !== 0) {
|
|
90
|
+
// ⛔ 인자에 토큰이 있으므로 명령을 그대로 찍지 않는다 — 단계 이름과 adb 의 말만.
|
|
91
|
+
const why = (r.error?.message ?? r.stderr ?? '').trim().split('\n')[0] ?? '';
|
|
92
|
+
out.error(`✗ adb ${args.includes('reverse') ? 'reverse' : 'am start'} 실패 — ${why || `exit ${r.status}`}`);
|
|
93
|
+
out.error(' USB 디버깅 허용 · `adb devices` 에 기기가 «device» 로 보이는지 · 기기가 여럿이면 --android <serial>.');
|
|
94
|
+
return 1;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
try { debug.log('phone.link', 'android-delivered', { port: lanEp.port, serial: !!serial, temp: !!opts.temp }); } catch { /* 관측 실패가 연결을 막지 않는다 */ }
|
|
98
|
+
out.log(`✓ 안드로이드 앱에 연결 링크를 건넸다 — 폰에서 앱이 열리고 바로 붙는다 (127.0.0.1:${lanEp.port} ⇄ 이 Mac · adb reverse).`);
|
|
99
|
+
out.error('⚠ USB 를 뽑으면 끊긴다. 계속 쓰려면 폰의 Tailscale 을 켜고 `elanous phone link` 의 QR 을 찍는다.');
|
|
100
|
+
out.error(`토큰: ${tokenNote}`);
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
out.log('휴대폰에서 이 링크를 열면 앱이 바로 연결된다 (카메라로 QR · AirDrop · 메모 붙여넣기):');
|
|
105
|
+
out.log('');
|
|
106
|
+
out.log(link);
|
|
107
|
+
out.log('');
|
|
108
|
+
if (opts.qr !== false) {
|
|
109
|
+
const qr = spawnSync('qrencode', ['-t', 'ANSIUTF8', link], { encoding: 'utf8' });
|
|
110
|
+
if (qr.status === 0 && qr.stdout) out.log(qr.stdout);
|
|
111
|
+
}
|
|
112
|
+
out.error(`주소: ${ep.tls ? 'https' : 'http'}://${ep.host}:${ep.port} (${picked.kind === 'tailnet' ? 'tailnet — 휴대폰이 같은 tailnet 에 있어야 한다' : '루프백 — 이 Mac 의 시뮬레이터 전용'})`);
|
|
113
|
+
if (picked.kind === 'loopback' && !opts.local) out.error('⚠ tailnet 주소가 없다 — 실제 휴대폰에서는 닿지 않는다. `elanous nexus show` 로 tailnet 을 확인하라.');
|
|
114
|
+
out.error(`토큰: ${tokenNote}`);
|
|
115
|
+
out.error('⚠ 이 링크는 이 Mac 을 조작할 수 있는 토큰을 담는다 — 남에게 보내거나 공개 캡처에 싣지 않는다.');
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
package/src/cli/plugin-cli.ts
CHANGED
|
@@ -5,10 +5,13 @@ import { join } from 'node:path';
|
|
|
5
5
|
import { existsSync, readFileSync } from 'node:fs';
|
|
6
6
|
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
7
7
|
import { userConfigPath } from '../user-config.js';
|
|
8
|
-
import { debug } from '../debug/log.js';
|
|
8
|
+
import { debug, redactSecretText } from '../debug/log.js';
|
|
9
|
+
import { addMarket, listMarkets, MarketFetchError, updateMarket } from '../plugins/install/market-fetch.js';
|
|
9
10
|
import { emitDecision } from '../live/detail-switch.js';
|
|
10
11
|
import { OFFICIAL_INDEX_KEYS } from '../market/official-keys.js';
|
|
11
12
|
import { installPlugin, listInstalledPlugins, PluginInstallError, removePlugin, type InstallEvent } from '../plugins/install/plugin-install.js';
|
|
13
|
+
import { credentialStatus, setPluginCredentials } from '../plugins/install/plugin-credentials.js';
|
|
14
|
+
import { makePlugin } from '../plugins/maker/plugin-maker.js';
|
|
12
15
|
|
|
13
16
|
function keys(): ReadonlyArray<{ keyId: string; publicKey: string }> {
|
|
14
17
|
const configPath = userConfigPath();
|
|
@@ -32,43 +35,125 @@ async function askConsent(capabilities: string[], json: boolean): Promise<boolea
|
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
function outputEvent(event: InstallEvent, json: boolean): void {
|
|
38
|
+
function outputEvent(event: InstallEvent, json: boolean, nodeCount: { value: number }): void {
|
|
36
39
|
// Connector field names are useful; values and secret-field metadata are not needed here.
|
|
37
40
|
const safe = event.event === 'credentials'
|
|
38
41
|
? { event: 'credentials', connectors: event.connectors.map(({ id, fields }) => ({ id, fields: fields.map(({ name }) => name) })) }
|
|
39
42
|
: event;
|
|
40
43
|
debug.log('plugin.cli', event.event, { event: event.event });
|
|
44
|
+
if (event.event === 'registered') nodeCount.value = event.nodes.length;
|
|
41
45
|
if (json) stdout.write(JSON.stringify(safe) + '\n');
|
|
42
46
|
else if (event.event === 'credentials') {
|
|
43
47
|
if (event.connectors.length) console.log(`Connectors: ${event.connectors.map(connector => connector.id).join(', ')} — configure via elanous connector settings (no secrets requested here)`);
|
|
44
|
-
} else if (event.event === 'done') console.log(`Installed ${event.plugin}@${event.version}`);
|
|
48
|
+
} else if (event.event === 'done') console.log(`Installed ${event.plugin}@${event.version} (${nodeCount.value} nodes)`);
|
|
45
49
|
else if (event.event === 'consent' && event.required) console.log(`Permissions: ${event.capabilities.join(', ')}`);
|
|
46
50
|
}
|
|
47
51
|
|
|
52
|
+
/** Only our own install/market errors carry a cause (fixed wording that may name the user's own spec); URLs and secrets are masked; any other error stays generic. */
|
|
53
|
+
function safeCause(error: unknown): string {
|
|
54
|
+
if (!(error instanceof PluginInstallError || error instanceof MarketFetchError)) return 'unexpected error';
|
|
55
|
+
return redactSecretText(error.message.replace(/https?:\/\/[^\s"'<>]+/gi, '[redacted URL]'));
|
|
56
|
+
}
|
|
57
|
+
|
|
48
58
|
export function registerPluginCommands(program: Command): void {
|
|
49
59
|
const plugin = program.command('plugin').description('Install and manage Elanous plugins');
|
|
60
|
+
plugin.command('make <request>').description('Write, validate and install a local graph plugin from one request')
|
|
61
|
+
.option('--name <slug>', 'Local plugin name')
|
|
62
|
+
.option('--dir <parent>', 'Parent directory (default: instance plugins-local)')
|
|
63
|
+
.option('--run', 'Run the installed graph')
|
|
64
|
+
.option('--input <json>', 'JSON input for --run')
|
|
65
|
+
.option('--json', 'Print the result as JSON')
|
|
66
|
+
.action(async (request: string, opts: { name?: string; dir?: string; run?: boolean; input?: string; json?: boolean }) => {
|
|
67
|
+
try {
|
|
68
|
+
const result = await makePlugin({ request, name: opts.name, parentDir: opts.dir, run: opts.run,
|
|
69
|
+
...(opts.input === undefined ? {} : { input: JSON.parse(opts.input) as unknown }) });
|
|
70
|
+
if (opts.json) stdout.write(JSON.stringify(result) + '\n');
|
|
71
|
+
else {
|
|
72
|
+
console.log(`뼈대 ${result.timings.scaffold}ms · 작성 ${result.timings.write}ms`);
|
|
73
|
+
console.log(`검증 오류 ${result.errors.length} · ${result.timings.validate}ms${result.timings.repair === undefined ? '' : ` · 수리 ${result.timings.repair}ms`}`);
|
|
74
|
+
console.log(`설치 ${result.timings.install === 0 && result.status === 'failed' ? '미실행' : result.status === 'failed' && result.timings.run === undefined ? '실패' : '완료'} · ${result.timings.install}ms (${result.dir})`);
|
|
75
|
+
if (opts.run) console.log(`실행 ${result.runStatus ?? '미실행'} · ${result.timings.run ?? 0}ms`);
|
|
76
|
+
for (const error of result.errors) console.error(error);
|
|
77
|
+
}
|
|
78
|
+
if (result.status === 'failed') process.exitCode = 1;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
+
if (opts.json) stdout.write(JSON.stringify({ status: 'failed', errors: [message] }) + '\n');
|
|
82
|
+
else console.error(`plugin make failed: ${message}`);
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
50
86
|
plugin.command('add <spec>').description('Install a local path, pinned git plugin or signed market plugin')
|
|
51
87
|
.option('--yes', 'Accept requested capabilities')
|
|
52
88
|
.option('--json', 'NDJSON progress events')
|
|
53
89
|
.option('--allow-unsigned', 'Allow unsigned marketplace indices')
|
|
54
|
-
.
|
|
90
|
+
.option('--refresh', 'Refresh the signed marketplace index before installing')
|
|
91
|
+
.action(async (spec: string, opts: { yes?: boolean; json?: boolean; allowUnsigned?: boolean; refresh?: boolean }) => {
|
|
55
92
|
try {
|
|
56
93
|
const root = elanousStateRoot();
|
|
94
|
+
const nodeCount = { value: 0 };
|
|
57
95
|
await installPlugin(spec, {
|
|
58
96
|
root, marketDir: join(root, 'plugins', 'markets'), trustedKeys: keys(),
|
|
59
|
-
yes: opts.yes, allowUnsigned: opts.allowUnsigned,
|
|
60
|
-
onEvent: event => outputEvent(event, !!opts.json),
|
|
97
|
+
yes: opts.yes, allowUnsigned: opts.allowUnsigned, refresh: opts.refresh,
|
|
98
|
+
onEvent: event => outputEvent(event, !!opts.json, nodeCount),
|
|
61
99
|
consent: capabilities => askConsent(capabilities, !!opts.json),
|
|
62
100
|
});
|
|
63
101
|
emitDecision({ kind: 'ROUTE', what: 'plugin add', reason: 'plugin installed', purpose: 'make plugin available', target: 'plugin host' });
|
|
64
102
|
} catch (error) {
|
|
65
103
|
const reason = error instanceof PluginInstallError ? error.reason : 'io';
|
|
66
|
-
// Installer errors may embed an arbitrary source path or git URL; never echo them or config secrets.
|
|
67
104
|
const detail = reason === 'consent-denied' ? 'plugin capabilities require consent' : `plugin installation ${reason} error`;
|
|
68
|
-
|
|
105
|
+
const cause = safeCause(error);
|
|
106
|
+
debug.log('plugin.cli', 'failed', { reason, cause }, { level: 'error' });
|
|
69
107
|
emitDecision({ kind: 'ESCALATE', what: 'plugin add', reason, purpose: 'report installation failure', target: 'user' });
|
|
70
|
-
if (opts.json) stdout.write(JSON.stringify({ event: 'failed', reason, detail }) + '\n');
|
|
71
|
-
else console.error(`plugin add failed: ${detail}`);
|
|
108
|
+
if (opts.json) stdout.write(JSON.stringify({ event: 'failed', reason, detail, ...(reason === 'consent-denied' ? {} : { cause }) }) + '\n');
|
|
109
|
+
else console.error(`plugin add failed: ${detail}${reason === 'consent-denied' ? '' : `: ${cause}`}`);
|
|
110
|
+
process.exitCode = 1;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
const market = plugin.command('market').description('Manage signed plugin marketplaces');
|
|
114
|
+
market.command('add <name> <url>').description('Register a marketplace URL')
|
|
115
|
+
.action((name: string, url: string) => {
|
|
116
|
+
try {
|
|
117
|
+
const added = addMarket(name, url);
|
|
118
|
+
debug.log('plugin.cli', 'market.add', { name: added.name });
|
|
119
|
+
console.log(`Added market ${added.name} (${added.url})`);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error(`plugin market add failed: ${safeCause(error)}`);
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
market.command('list').description('List configured marketplaces').option('--json', 'JSON array')
|
|
126
|
+
.action((opts: { json?: boolean }) => {
|
|
127
|
+
try {
|
|
128
|
+
const markets = listMarkets();
|
|
129
|
+
debug.log('plugin.cli', 'market.list', { count: markets.length });
|
|
130
|
+
if (opts.json) stdout.write(JSON.stringify(markets) + '\n');
|
|
131
|
+
else for (const entry of markets) console.log(`${entry.name} (${entry.url})`);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (opts.json) stdout.write(JSON.stringify({ event: 'failed', cause: safeCause(error) }) + '\n');
|
|
134
|
+
else console.error(`plugin market list failed: ${safeCause(error)}`);
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
market.command('update [name]').description('Refresh signed marketplace indices')
|
|
139
|
+
.action(async (name?: string) => {
|
|
140
|
+
try {
|
|
141
|
+
const markets = listMarkets();
|
|
142
|
+
const selected = name ? markets.filter(entry => entry.name === name) : markets;
|
|
143
|
+
if (!selected.length) throw new Error('market not configured');
|
|
144
|
+
const root = elanousStateRoot();
|
|
145
|
+
for (const entry of selected) {
|
|
146
|
+
try {
|
|
147
|
+
const result = await updateMarket(entry.name, { root, marketDir: join(root, 'plugins', 'markets') });
|
|
148
|
+
debug.log('plugin.cli', 'market.update', { name: result.market.name, sequence: result.index.sequence });
|
|
149
|
+
console.log(`Updated market ${result.market.name} (sequence ${result.index.sequence})`);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.error(`plugin market update ${entry.name} failed: ${safeCause(error)}`);
|
|
152
|
+
process.exitCode = 1;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error(`plugin market update failed: ${safeCause(error)}`);
|
|
72
157
|
process.exitCode = 1;
|
|
73
158
|
}
|
|
74
159
|
});
|
|
@@ -83,12 +168,52 @@ export function registerPluginCommands(program: Command): void {
|
|
|
83
168
|
const installed = listInstalledPlugins(root).filter(item => registered.has(`${item.market}/${item.name}/${item.version}`));
|
|
84
169
|
debug.log('plugin.cli', 'list', { count: installed.length });
|
|
85
170
|
if (opts.json) stdout.write(JSON.stringify(installed) + '\n');
|
|
86
|
-
else for (const item of installed) console.log(`${item.name}@${item.version} (${item.market})`);
|
|
171
|
+
else for (const item of installed) console.log(`${item.name}@${item.version} (${item.market})${item.installedAt ? ` — installed ${item.installedAt}` : ''}`);
|
|
87
172
|
} catch {
|
|
88
173
|
console.error('plugin list failed: invalid plugin ledger');
|
|
89
174
|
process.exitCode = 1;
|
|
90
175
|
}
|
|
91
176
|
});
|
|
177
|
+
plugin.command('credentials <plugin>').description('Show or update installed plugin credential status')
|
|
178
|
+
.option('--set <NAME=VALUE>', 'Set a declared field', (value: string, values: string[]) => [...values, value], [] as string[])
|
|
179
|
+
.option('--unset <NAME>', 'Remove a declared field', (value: string, values: string[]) => [...values, value], [] as string[])
|
|
180
|
+
.option('--stdin <NAME>', 'Read one credential value from standard input')
|
|
181
|
+
.option('--json', 'JSON status')
|
|
182
|
+
.action(async (name: string, opts: { set: string[]; unset: string[]; stdin?: string; json?: boolean }) => {
|
|
183
|
+
try {
|
|
184
|
+
const root = elanousStateRoot();
|
|
185
|
+
const fields: Record<string, string | null> = Object.create(null);
|
|
186
|
+
for (const assignment of opts.set) {
|
|
187
|
+
const equal = assignment.indexOf('=');
|
|
188
|
+
if (equal < 1) throw new Error('invalid credential assignment');
|
|
189
|
+
fields[assignment.slice(0, equal)] = assignment.slice(equal + 1);
|
|
190
|
+
}
|
|
191
|
+
for (const field of opts.unset) fields[field] = null;
|
|
192
|
+
if (opts.stdin !== undefined) {
|
|
193
|
+
let input = '';
|
|
194
|
+
for await (const chunk of stdin) {
|
|
195
|
+
input += chunk.toString();
|
|
196
|
+
if (input.includes('\n')) break;
|
|
197
|
+
}
|
|
198
|
+
if (!input) throw new Error('credential stdin is empty');
|
|
199
|
+
fields[opts.stdin] = input.split(/\r?\n/, 1)[0]!;
|
|
200
|
+
}
|
|
201
|
+
if (Object.keys(fields).length > 0) {
|
|
202
|
+
setPluginCredentials(name, fields, root);
|
|
203
|
+
debug.log('plugin.credentials', 'set', { plugin: name, fields: Object.keys(fields), count: Object.keys(fields).length });
|
|
204
|
+
}
|
|
205
|
+
const status = credentialStatus(name, root);
|
|
206
|
+
if (opts.json) stdout.write(JSON.stringify(status) + '\n');
|
|
207
|
+
else for (const field of status.fields) console.log(`${field.name}\t${field.env}\t${field.set ? 'set' : 'unset'}`);
|
|
208
|
+
// The web app's PUT reloads MCP clients itself; a CLI write reaches an already-running daemon only after a reload.
|
|
209
|
+
// stderr in both modes, so `--json` stdout stays one JSON line.
|
|
210
|
+
if (Object.keys(fields).length > 0) console.error('A running daemon applies this to plugin MCP servers after `elanous mcp reload` (the web app does it for you).');
|
|
211
|
+
} catch {
|
|
212
|
+
if (opts.json) stdout.write(JSON.stringify({ error: 'plugin credentials failed' }) + '\n');
|
|
213
|
+
else console.error('plugin credentials failed');
|
|
214
|
+
process.exitCode = 1;
|
|
215
|
+
}
|
|
216
|
+
});
|
|
92
217
|
plugin.command('remove <name>').description('Remove installed plugin versions')
|
|
93
218
|
.action((name: string) => {
|
|
94
219
|
try {
|
package/src/cli/pr-lineage.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const SELF_IMPL_PREFIX = 'self-impl/';
|
|
2
2
|
const LINEAGE_HASH_SUFFIX = /-[0-9a-f]{7,8}$/i;
|
|
3
|
+
const RUN_SUFFIX = /-r[a-z0-9]{6}$/i;
|
|
3
4
|
/**
|
|
4
5
|
* Formal new-generation segment after `self-impl/`: dash-delimited `-goalid-<id>-`.
|
|
5
6
|
* ⛔ A substring like `mygoalid-foo` is not that segment — it has no leading `-`/`^` boundary.
|
|
@@ -9,7 +10,7 @@ const GOAL_ID_SEGMENT = /(?:^|-)goalid-([^/-]+)-/;
|
|
|
9
10
|
/** Lineage slug of a harness branch: `self-impl/<slug>-<7–8 hex>`, or null when the name is not that shape. */
|
|
10
11
|
export function branchLineageSlug(branch: string): string | null {
|
|
11
12
|
if (!branch.startsWith(SELF_IMPL_PREFIX)) return null;
|
|
12
|
-
const rest = branch.slice(SELF_IMPL_PREFIX.length);
|
|
13
|
+
const rest = branch.slice(SELF_IMPL_PREFIX.length).replace(RUN_SUFFIX, '');
|
|
13
14
|
const hash = LINEAGE_HASH_SUFFIX.exec(rest);
|
|
14
15
|
return hash ? rest.slice(0, rest.length - hash[0].length) : rest;
|
|
15
16
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
|
|
5
|
+
// Private local ledgers (decisions · directives) are absent from the public export;
|
|
6
|
+
// JS-only distributions still register them. Kept out of src/index.ts so the `self`
|
|
7
|
+
// block stays free of direct sink registration (see self-cli-sink-surface.test.ts).
|
|
8
|
+
export function registerPrivateLedgerCommands(program: Command): void {
|
|
9
|
+
if (existsSync(resolve(import.meta.dir, 'decisions-cli.ts')) || existsSync(resolve(import.meta.dir, 'decisions-cli.js'))) {
|
|
10
|
+
program.command('decisions').description('대표 결정 로컬 원장').allowUnknownOption().allowExcessArguments(true)
|
|
11
|
+
.action(async (_opts: unknown, command: Command) => {
|
|
12
|
+
try { await (await import('../domains/standalone-log-sink.js')).registerStandaloneLogSink('decisions'); }
|
|
13
|
+
catch { /* Logging must not prevent recording a decision. */ }
|
|
14
|
+
const privateDecisionsModule: string = './decisions-cli.js';
|
|
15
|
+
const { registerDecisionsCommands } = await import(privateDecisionsModule) as { registerDecisionsCommands: (program: Command) => void };
|
|
16
|
+
const privateProgram = new Command();
|
|
17
|
+
registerDecisionsCommands(privateProgram);
|
|
18
|
+
await privateProgram.parseAsync(['decisions', ...command.args], { from: 'user' });
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (existsSync(resolve(import.meta.dir, 'directives-cli.ts')) || existsSync(resolve(import.meta.dir, 'directives-cli.js'))) {
|
|
22
|
+
program.command('directives').description('대표 지시 로컬 색인·검색').allowUnknownOption().allowExcessArguments(true)
|
|
23
|
+
.action(async (_opts: unknown, command: Command) => {
|
|
24
|
+
const privateDirectivesModule: string = './directives-cli.js';
|
|
25
|
+
const { registerDirectivesCommands } = await import(privateDirectivesModule) as { registerDirectivesCommands: (program: Command) => void };
|
|
26
|
+
const privateProgram = new Command();
|
|
27
|
+
registerDirectivesCommands(privateProgram);
|
|
28
|
+
await privateProgram.parseAsync(['directives', ...command.args], { from: 'user' });
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/cli/pwa-start.ts
CHANGED
|
@@ -126,6 +126,8 @@ export interface PwaStartOpts {
|
|
|
126
126
|
* MCP-client boot is skipped (PR1 2026-05-13). Default undefined =
|
|
127
127
|
* leave the decision to the daemon-side `mcp.enabled` config. */
|
|
128
128
|
mcpEnabled?: boolean;
|
|
129
|
+
/** Forward an explicit --no-auth opt-out to the detached Nexus child. */
|
|
130
|
+
noAuth?: boolean;
|
|
129
131
|
out?: { log: (s: string) => void; error: (s: string) => void };
|
|
130
132
|
// ─── Test seams ─────────────────────────────────────────────
|
|
131
133
|
bgLaunchFn?: (opts: {
|
|
@@ -237,6 +239,7 @@ function collectForwardArgs(opts: PwaStartOpts, httpPort: number): string[] {
|
|
|
237
239
|
// those gates only matter on the parent (build runs before fork).
|
|
238
240
|
if (opts.watch && (opts.mode ?? 'static') === 'static') args.push('--watch');
|
|
239
241
|
if (opts.mcpEnabled === false) args.push('--no-mcp');
|
|
242
|
+
if (opts.noAuth === true) args.push('--no-auth');
|
|
240
243
|
return args;
|
|
241
244
|
}
|
|
242
245
|
|