elanous 0.2.5 → 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 +5 -4
- 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/{7527.943753b6ed829f09.js → 1883.c91a2648af4842fd.js} +1 -1
- 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/{8918-482b30ce2a8ebf4c.js → 2984-977cfa339fcd65a5.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/{4853-35fdbe6bdbcdc0fc.js → 3165-95515ca5f299a120.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3190-e9282753e7ebde85.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5391-8793f88b03b30161.js → 3995-bee512a7063036ce.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9704-3221c102623c8d73.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/5329.8b7e4bca684b6eec.js +5 -0
- 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/{5348-bbf838e119661eee.js → 763-7d0e1913b5074e98.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{8617.465ca22a319c95b3.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/{9238.2cd5f47f4bf7a2c3.js → 8250.4a2c5553a07cdf47.js} +1 -1
- 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/{4daa2798-1363d725c79f3593.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/{f6f3850d-7f324c6566002113.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-84cd419494e16b0f.js → page-c3890e90bd232732.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{06ef2eba-d1b99ac6d2ea663c.js → beb5cf10-9adcc10b83930090.js} +1 -1
- 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-2bf59c720fe9542c.js → webpack-37b8bf78a3c6eec5.js} +1 -1
- package/apps/pwa/out/_next/static/{ktMqfrBbf8BMGac0jqLrb → qMI0jnlZaBjjm4aIDYNGM}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +21 -21
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +21 -21
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +21 -21
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +20 -20
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +20 -20
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +20 -20
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +21 -21
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +21 -21
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +21 -21
- 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 +20 -20
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +21 -21
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +21 -21
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +20 -20
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +20 -20
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +21 -21
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +21 -21
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +21 -21
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +21 -21
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +20 -20
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +21 -21
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +21 -21
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +21 -21
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +21 -21
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +21 -21
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +20 -20
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +21 -21
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +21 -21
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +21 -21
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +21 -21
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +21 -21
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +21 -21
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +21 -21
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +21 -21
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +3 -1
- package/package.json +1 -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/README.md +30 -0
- package/scripts/coord-tracks.json +2 -2
- package/scripts/daemon-port-baseline.txt +1 -2
- package/scripts/lib/android-test-device.ts +32 -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 +46 -13
- package/scripts/release-loop/shard-plan.ts +47 -0
- package/scripts/ux/record-page.ts +4 -1
- package/skills/explainer-video/SKILL.md +18 -0
- package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -1
- package/skills/explainer-video/engine/build.mjs +1 -1
- 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 +58 -16
- package/src/autopilot/mission-origin.ts +20 -4
- package/src/autopilot/mission-phase-track.ts +11 -1
- package/src/autopilot/track-agent.ts +3 -3
- package/src/autopilot/track-courier.ts +2 -2
- package/src/cli/nexus-ios-bind.ts +25 -8
- package/src/cli/phone-link.ts +117 -0
- package/src/cli/plugin-cli.ts +27 -0
- package/src/cli/private-ledger-commands.ts +31 -0
- package/src/cli/release-cli.ts +14 -11
- package/src/domains/outbound-alert.ts +5 -5
- package/src/field/field-media.ts +245 -0
- package/src/index.ts +18 -24
- package/src/nexus/api/field-uploads.ts +143 -0
- package/src/nexus/api/graph-approvals.ts +50 -1
- package/src/nexus/api/harness-api.ts +219 -5
- package/src/nexus/api/http-server.ts +9 -0
- package/src/notify/llm-budget-alert.ts +119 -0
- package/src/plugins/maker/plugin-maker.ts +208 -0
- 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 +28 -2
- package/src/seat-address/seat-address.ts +34 -0
- package/src/self-implement/run-ledger-compact.ts +1 -1
- package/src/session/index.ts +23 -10
- package/src/task-orchestrator/surfaces/pod-bun-cache.ts +18 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +12 -3
- package/src/telegram.ts +108 -1
- package/src/user-config.ts +4 -0
- 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/apps/pwa/out/_next/static/chunks/1413-7179a49332db5a80.js +0 -1
- package/apps/pwa/out/_next/static/chunks/150-25c277137a9638ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1640-bc99ec23bbe29d12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1751.50b96ea9c37b49b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2300-de059d0ab2a16630.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2684-05b0a81e75298c16.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3026-2ed7e317d9b02102.js +0 -25
- package/apps/pwa/out/_next/static/chunks/3611-77496ada38cd6358.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3717-68957c4dc9ceec90.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3929-46ba9af6f9d895cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4015-62c5a3744698a8da.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4217-3c4317a7905f9eeb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4240-3230f230fae2d7d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4271-e53e075ebd48593b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4377.3b125fef79576dfd.js +0 -5
- package/apps/pwa/out/_next/static/chunks/4440-18bb160d95819f4e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5007-6a3b30e23ff68a6e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5244-12b424c42070f5ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5611-df00af3e4e4a61bb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5890-dca2321773be829a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6123-37c9dc57ddaa7ab0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6588-b7975304a302ab91.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6604-c03961962b6273b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7109-cf8c87e968e83a65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7178-30f77877525a2efb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7262-871563fc9663730b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7294-f0eeebb92aa93459.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7429-881af44deed6958f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/764-a01e858525a190c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7868-e35d5f7985ecd3f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8320-51484af7b01fdfa5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9131-52f5fad51cceca04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-70c404c7bb6d7f0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3ffd80166b1050aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f89e9a303515a033.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-4f403cd060442c95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-0ac5d053f0444d21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-4bcc63e272ae8205.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-880734bd07d62882.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-72bd384898d88955.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1018bbc9586ecae5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-de86f8c38076dd7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-70c404c7bb6d7f0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-cd6447196d8bafe6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-e733630c47fa102b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d1c1a8357b161875.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-60c514128e81d787.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8b637b7e325e13c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-bccce0babceae30f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-63301d11dbe73836.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-a8cf0dc472060db2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-02fba54881ccfa49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-d5e8e90f0038f8de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-b1ef6824f494466a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d589bb0f233b1009.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c9bbaebef4cda059.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-3639ce7916b92fed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7450ae7478955653.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-70c404c7bb6d7f0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b41ca4abfd618452.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-b333dd79ebee3fa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-8be1227492793655.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-6e03e9371b552e0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-911411e9fca18d03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-792170e649b37088.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c40463466937bc91.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-509c798b80384536.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-1cac2aa21d77c417.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-9626799d9b4a2cd1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-361487d2da603917.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-607f5beb94cb2e07.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-d0222d219e29dddf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-a26980a31126decc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-edb2b37decf23e4d.js +0 -1
- /package/apps/pwa/out/_next/static/{ktMqfrBbf8BMGac0jqLrb → qMI0jnlZaBjjm4aIDYNGM}/_ssgManifest.js +0 -0
|
@@ -13,20 +13,36 @@ import { getUserConfig } from '../user-config.js';
|
|
|
13
13
|
import { resolveChannelBotToken } from '../channel-bot-token.js';
|
|
14
14
|
import { join } from 'node:path';
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
channel: 'telegram' | 'pwa' | 'voice' | 'cli' | 'api' | 'tui';
|
|
16
|
+
interface MissionOriginBase {
|
|
18
17
|
/** 텔레그램: 답장할 chat id. */
|
|
19
18
|
chatId?: number;
|
|
20
19
|
/** 텔레그램: 수신 봇 식별(botId=토큰 prefix). 발송 시 config 에서 토큰 해석. */
|
|
21
20
|
botId?: string;
|
|
22
|
-
/** 텔레그램 스레드(포럼) — 있으면 답장에 붙임. */
|
|
23
|
-
threadId?: number;
|
|
24
21
|
/** HITL 승인/거절 버튼을 단 알림 메시지의 message_id. 크로스서피스 싱크용 —
|
|
25
22
|
* PWA/텔레그램 어느 쪽에서 해소돼도 이 좌표(chatId+botId+이 id)로 텔레그램 메시지를
|
|
26
23
|
* edit("승인됨/거절됨")하고 버튼을 제거한다. 버튼 발송 성공 시에만 기록. */
|
|
27
24
|
hitlMessageId?: number;
|
|
28
25
|
}
|
|
29
26
|
|
|
27
|
+
export type MissionOrigin = MissionOriginBase & (
|
|
28
|
+
| {
|
|
29
|
+
channel: 'telegram' | 'pwa' | 'voice' | 'cli' | 'api' | 'tui';
|
|
30
|
+
/** 텔레그램 스레드(포럼) — 있으면 답장에 붙임. */
|
|
31
|
+
threadId?: number;
|
|
32
|
+
channelId?: never;
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
channel: 'discord';
|
|
36
|
+
/** Discord origin channel (snowflake). */
|
|
37
|
+
channelId: string;
|
|
38
|
+
/** Discord thread (snowflake), if the request came from a thread.
|
|
39
|
+
* ⛔ `threadId` 가 아니다 — 텔레그램 `threadId?: number` 와 같은 이름이면 유니온에서 `string|number` 로 넓어져
|
|
40
|
+
* 텔레그램 발송부 20여 곳의 타입이 깨진다(2026-10-01 PWA 빌드 실패 · #22246). */
|
|
41
|
+
discordThreadId?: string;
|
|
42
|
+
threadId?: never;
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
|
|
30
46
|
function originDir(): string {
|
|
31
47
|
// core 미션 fabric = autopilot/ (conatus/ 는 투자 customer 네임스페이스·대표 정정 2026-07-11).
|
|
32
48
|
// [ISO-3] ELANOUS_STATE_DIR 존중 — 격리 테스트 데몬은 운영 origin 을 못 본다.
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
|
|
4
|
-
type TrackEntry = { id: string; mark: string; owns: string[] };
|
|
4
|
+
type TrackEntry = { id: string; mark: string; owns: string[]; alias?: string };
|
|
5
|
+
|
|
6
|
+
// Seat ids became two letters on 09-30 (#22085) with the old one-letter id kept as `alias`.
|
|
7
|
+
// A track named either way is the same seat — posts and inputs still carry the old letter during the switch.
|
|
8
|
+
export function findTrack(idOrAlias: string, registry: TrackEntry[] = loadTrackRegistry()): TrackEntry | undefined {
|
|
9
|
+
return registry.find((track) => track.id === idOrAlias || track.alias === idOrAlias);
|
|
10
|
+
}
|
|
11
|
+
export function trackPostIds(idOrAlias: string, registry: TrackEntry[] = loadTrackRegistry()): string[] {
|
|
12
|
+
const track = findTrack(idOrAlias, registry);
|
|
13
|
+
return [...new Set([idOrAlias, ...(track ? [track.id, ...(track.alias ? [track.alias] : [])] : [])])];
|
|
14
|
+
}
|
|
5
15
|
|
|
6
16
|
export function loadTrackRegistry(path = resolve(import.meta.dir, '../../scripts/coord-tracks.json')): TrackEntry[] {
|
|
7
17
|
try {
|
|
@@ -6,7 +6,7 @@ import { debug, redactSecretText } from '../debug/log.js';
|
|
|
6
6
|
import { getOrCreatePersonaSession } from '../session/index.js';
|
|
7
7
|
import { getUserConfig } from '../user-config.js';
|
|
8
8
|
import { decideBudget, readBudgetInputsLive } from '../self-implement/budget-gate.js';
|
|
9
|
-
import {
|
|
9
|
+
import { findTrack } from './mission-phase-track.js';
|
|
10
10
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
11
11
|
|
|
12
12
|
export type TrackAgentInput = { missionId: string; taskId: string; title: string; prompt: string; track: string };
|
|
@@ -45,7 +45,7 @@ const forbidden = /\b(?:delete|remove|deploy|publish|release|restart|reboot|merg
|
|
|
45
45
|
let running = 0;
|
|
46
46
|
|
|
47
47
|
function context(input: TrackAgentInput, repo: string): string {
|
|
48
|
-
const track =
|
|
48
|
+
const track = findTrack(input.track);
|
|
49
49
|
if (!track) throw new Error('unknown track');
|
|
50
50
|
const docs = resolve(repo, 'docs');
|
|
51
51
|
let handoff = '';
|
|
@@ -94,7 +94,7 @@ export async function decideTrackAction(input: TrackAgentInput, deps: RunnerDeps
|
|
|
94
94
|
export async function executeTrackAction(decision: TrackAgentDecision, input: TrackAgentInput, deps: RunnerDeps = {}): Promise<TrackAgentResult> {
|
|
95
95
|
if (decision.action !== 'say') return { status: 'held', detail: decision.reason ?? '사람 확인' };
|
|
96
96
|
if (forbidden.test(`${input.title}\n${input.prompt}`)) return { status: 'held', detail: '금지 작업 — 사람 확인' };
|
|
97
|
-
if (!
|
|
97
|
+
if (!findTrack(input.track)) return { status: 'held', detail: '트랙 미정 — 사람 확인' };
|
|
98
98
|
try {
|
|
99
99
|
const checkBudget = deps.checkBudget ?? (async () => {
|
|
100
100
|
const action = decideBudget(await readBudgetInputsLive()).action;
|
|
@@ -6,7 +6,7 @@ import { join, resolve } from 'node:path';
|
|
|
6
6
|
import { debug, redactSecretText } from '../debug/log.js';
|
|
7
7
|
import { TaskStore } from '../task-orchestrator/store.js';
|
|
8
8
|
import { getUserConfig, type UserConfig } from '../user-config.js';
|
|
9
|
-
import { inferPhaseTrack, loadTrackRegistry } from './mission-phase-track.js';
|
|
9
|
+
import { inferPhaseTrack, loadTrackRegistry, trackPostIds } from './mission-phase-track.js';
|
|
10
10
|
import type { TrackAgentInput, TrackAgentRunner } from './track-agent.js';
|
|
11
11
|
|
|
12
12
|
type CourierConfig = NonNullable<NonNullable<UserConfig['autopilot']>['trackCourier']>;
|
|
@@ -66,7 +66,7 @@ export async function trackAlive(track: string, deps: CourierDeps = {}): Promise
|
|
|
66
66
|
const comments = await (deps.listComments ?? (() => defaultComments(config.channelPr!, now, minutesFor(config))))();
|
|
67
67
|
if (!Array.isArray(comments)) throw new Error('comments response is not an array');
|
|
68
68
|
const timestamps = comments
|
|
69
|
-
.filter((c) => c && typeof c.body === 'string' && c.body.startsWith(`**[${
|
|
69
|
+
.filter((c) => c && typeof c.body === 'string' && trackPostIds(track).some((id) => c.body.startsWith(`**[${id}]**`)))
|
|
70
70
|
.map((c) => Date.parse(c.created_at))
|
|
71
71
|
.filter((at) => Number.isFinite(at) && at <= now);
|
|
72
72
|
if (!timestamps.length) return { alive: false, lastPostAt: null, reason: 'no-post' };
|
|
@@ -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
|
@@ -11,6 +11,7 @@ import { emitDecision } from '../live/detail-switch.js';
|
|
|
11
11
|
import { OFFICIAL_INDEX_KEYS } from '../market/official-keys.js';
|
|
12
12
|
import { installPlugin, listInstalledPlugins, PluginInstallError, removePlugin, type InstallEvent } from '../plugins/install/plugin-install.js';
|
|
13
13
|
import { credentialStatus, setPluginCredentials } from '../plugins/install/plugin-credentials.js';
|
|
14
|
+
import { makePlugin } from '../plugins/maker/plugin-maker.js';
|
|
14
15
|
|
|
15
16
|
function keys(): ReadonlyArray<{ keyId: string; publicKey: string }> {
|
|
16
17
|
const configPath = userConfigPath();
|
|
@@ -56,6 +57,32 @@ function safeCause(error: unknown): string {
|
|
|
56
57
|
|
|
57
58
|
export function registerPluginCommands(program: Command): void {
|
|
58
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
|
+
});
|
|
59
86
|
plugin.command('add <spec>').description('Install a local path, pinned git plugin or signed market plugin')
|
|
60
87
|
.option('--yes', 'Accept requested capabilities')
|
|
61
88
|
.option('--json', 'NDJSON progress events')
|
|
@@ -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/release-cli.ts
CHANGED
|
@@ -22,7 +22,8 @@ import { effectiveInstanceRoot, releaseLedgerRoot } from '../instance/resolve.js
|
|
|
22
22
|
import { getElanousConfigDirOverride } from '../elanous-config-dir.js';
|
|
23
23
|
import { envLiteral } from '../platform/env-literal.js';
|
|
24
24
|
import { userConfigPath } from '../user-config.js';
|
|
25
|
-
import { addItem, devVersion, listChecklist, removeItem, seedFromRoadmap, setItem, summarizeChecklist, type ChecklistStatus } from '../release-loop/checklist.js';
|
|
25
|
+
import { addItem, devVersion, listChecklist, removeItem, seedFromRoadmap, setItem, summarizeChecklist, type ChecklistStatus, type ChecklistDisposition } from '../release-loop/checklist.js';
|
|
26
|
+
import { writeStdoutJson } from './stdout-json.js';
|
|
26
27
|
|
|
27
28
|
export const DEFAULT_PUBLIC_REPO = 'ElanvitalAI/elanous';
|
|
28
29
|
const SEMVER = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
|
|
@@ -466,11 +467,11 @@ function checklistVersion(input: string | undefined, codenames: Record<string, s
|
|
|
466
467
|
return resolved;
|
|
467
468
|
}
|
|
468
469
|
|
|
469
|
-
function checklistOutput(v: string, codenames: Record<string, string>, mode: 'list' | 'status', json?: boolean): void {
|
|
470
|
+
async function checklistOutput(v: string, codenames: Record<string, string>, mode: 'list' | 'status', json?: boolean): Promise<void> {
|
|
470
471
|
const data = listChecklist(v);
|
|
471
472
|
const summary = summarizeChecklist(data);
|
|
472
473
|
const result = { ...data, codename: codenames[v] ?? '', ...summary };
|
|
473
|
-
if (json) {
|
|
474
|
+
if (json) { await writeStdoutJson(`${JSON.stringify(result)}\n`); return; }
|
|
474
475
|
console.log(`${v}${result.codename ? ` (${result.codename})` : ''} · 공개 ${data.released || '없음'} · 개발 ${data.dev}`);
|
|
475
476
|
if (mode === 'list') {
|
|
476
477
|
for (const item of data.items) console.log(`${({ green: '🟢', yellow: '🟡', red: '🔴', done: '✅' } as const)[item.status]} ${item.id} ${item.title}${item.owner ? ` · ${item.owner}` : ''}`);
|
|
@@ -492,26 +493,28 @@ export function registerReleaseCommands(program: Command): void {
|
|
|
492
493
|
return { version: checklistVersion(opts.version, codenames), codenames, json: opts.json };
|
|
493
494
|
};
|
|
494
495
|
const withContext = (cmd: Command) => cmd.option('--version <v>', '판 또는 별칭').option('--json', '결과 JSON');
|
|
495
|
-
withContext(checklist.command('list').description('모든 칸')).action((_opts: unknown, cmd: Command) => {
|
|
496
|
+
withContext(checklist.command('list').description('모든 칸')).action(async (_opts: unknown, cmd: Command) => {
|
|
496
497
|
const { version, codenames, json } = context(cmd);
|
|
497
|
-
checklistOutput(version, codenames, 'list', json);
|
|
498
|
+
await checklistOutput(version, codenames, 'list', json);
|
|
498
499
|
});
|
|
499
|
-
withContext(checklist.command('status').description('상태 요약')).action((_opts: unknown, cmd: Command) => {
|
|
500
|
+
withContext(checklist.command('status').description('상태 요약')).action(async (_opts: unknown, cmd: Command) => {
|
|
500
501
|
const { version, codenames, json } = context(cmd);
|
|
501
|
-
checklistOutput(version, codenames, 'status', json);
|
|
502
|
+
await checklistOutput(version, codenames, 'status', json);
|
|
502
503
|
});
|
|
503
504
|
withContext(checklist.command('add <id> <title>').description('칸 추가')).option('--owner <owner>', '담당').action((id: string, title: string, opts: { owner?: string }, cmd: Command) => {
|
|
504
505
|
const { version, json } = context(cmd);
|
|
505
506
|
const data = addItem(version, { id, title, ...(opts.owner !== undefined ? { owner: opts.owner } : {}) });
|
|
506
507
|
if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 추가`);
|
|
507
508
|
});
|
|
508
|
-
withContext(checklist.command('set <id>').description('칸
|
|
509
|
+
withContext(checklist.command('set <id>').description('칸 상태·근거·담당·처분 갱신'))
|
|
509
510
|
.option('--status <status>', 'green|yellow|red|done').option('--evidence <evidence>', '근거').option('--owner <owner>', '담당')
|
|
510
|
-
.
|
|
511
|
+
.option('--disposition <disposition>', 'move|known-issue|block')
|
|
512
|
+
.action((id: string, opts: { status?: string; evidence?: string; owner?: string; disposition?: string }, cmd: Command) => {
|
|
511
513
|
const { version, json } = context(cmd);
|
|
512
514
|
if (opts.status !== undefined && !['green', 'yellow', 'red', 'done'].includes(opts.status)) throw new Error(`잘못된 상태: ${opts.status}`);
|
|
513
|
-
if (opts.
|
|
514
|
-
|
|
515
|
+
if (opts.disposition !== undefined && !['move', 'known-issue', 'block'].includes(opts.disposition)) throw new Error(`잘못된 처분: ${opts.disposition}`);
|
|
516
|
+
if (opts.status === undefined && opts.evidence === undefined && opts.owner === undefined && opts.disposition === undefined) throw new Error('갱신할 칸을 지정하라');
|
|
517
|
+
const data = setItem(version, id, { ...(opts.status !== undefined ? { status: opts.status as ChecklistStatus } : {}), ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}), ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.disposition !== undefined ? { disposition: opts.disposition as ChecklistDisposition } : {}) }, process.env.ELANOUS_TRACK || 'cli');
|
|
515
518
|
if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 갱신`);
|
|
516
519
|
});
|
|
517
520
|
withContext(checklist.command('rm <id>').description('칸 삭제')).action((id: string, _opts: unknown, cmd: Command) => {
|
|
@@ -116,10 +116,10 @@ function deferOutbound(text: string, kind: string, origin?: MissionOrigin | null
|
|
|
116
116
|
mkdirSync(dirname(path), { recursive: true });
|
|
117
117
|
const rec = { ts: new Date().toISOString(), kind, text, ...(origin ? { origin } : {}) };
|
|
118
118
|
appendFileSync(path, JSON.stringify(rec) + '\n');
|
|
119
|
-
console.
|
|
119
|
+
console.error(`[outbound] 야간 무음(00:00~06:30 KST) — 보류 적재 (${kind})`);
|
|
120
120
|
logSend('deferred', kind, { reason: 'quiet-hours' }); // 밀림 적재 관측
|
|
121
121
|
} catch (e) {
|
|
122
|
-
console.
|
|
122
|
+
console.error(`[outbound] 보류 적재 실패 — 콘솔 출력\n${text}`, e instanceof Error ? e.message : '');
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -238,7 +238,7 @@ export function sendOutbound(text: string, kind = 'alert', origin?: MissionOrigi
|
|
|
238
238
|
return true; // 보류 = 수락 (호출측 재시도/에러 루프 방지)
|
|
239
239
|
}
|
|
240
240
|
const bypass = inQuietHours();
|
|
241
|
-
if (bypass) console.
|
|
241
|
+
if (bypass) console.error('[outbound] 야간 무음 우회 — 최근 사용자 활동(깨어있음) → 즉시 발송 + 보류분 flush');
|
|
242
242
|
try { flushDeferred(); } catch { /* fail-soft */ }
|
|
243
243
|
// ★ 발송 관측(대표 지시) — 발송 시각·mode·밀림(burst) 판정을 logs.db 에. burst=최근 2분 5건+
|
|
244
244
|
// (몰려 나가는 중 = 밀림 의심). `elanous logs --category outbound.send` 로 실시간/밀림 구분.
|
|
@@ -275,7 +275,7 @@ export function classifyDaemonResponse(j: unknown): DaemonPathClass {
|
|
|
275
275
|
function logDaemonPath(classification: DaemonPathClass, kind: string, extra: Record<string, unknown> = {}): void {
|
|
276
276
|
try { debug.log('outbound.send', 'daemon-path', { classification, kind, ...extra }); } catch { /* fail-soft */ }
|
|
277
277
|
if (classification === 'ok') return;
|
|
278
|
-
try { console.
|
|
278
|
+
try { console.error(`[outbound] daemon-path ${classification}`); } catch { /* fail-soft */ }
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
/** elanous `/v1/outbound` 우선 → 실패 시 텔레그램 직접. 성공 경로 반환(원장 중복방지용). */
|
|
@@ -334,6 +334,6 @@ function sendTelegramDirect(text: string, kind?: string): boolean {
|
|
|
334
334
|
tok = tok || env.TELEGRAM_BOT_TOKEN || '';
|
|
335
335
|
chat = chat || env.TELEGRAM_CHAT_ID || '';
|
|
336
336
|
}
|
|
337
|
-
if (!tok || !chat) { console.
|
|
337
|
+
if (!tok || !chat) { console.error('[outbound] 토큰/chat 미설정 — 콘솔 출력\n' + text); return false; }
|
|
338
338
|
return sendTelegramRaw(tok, chat, text);
|
|
339
339
|
}
|