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.
Files changed (455) hide show
  1. package/README.md +7 -6
  2. package/apps/pwa/out/404/index.html +1 -1
  3. package/apps/pwa/out/404.html +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/{5080-1613752891fcadb4.js → 2984-977cfa339fcd65a5.js} +3 -3
  9. package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/{8142-011e1f474efb8b33.js → 3190-e9282753e7ebde85.js} +1 -1
  11. package/apps/pwa/out/_next/static/chunks/3995-bee512a7063036ce.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 4240-7153881b0b377306.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +25 -0
  16. package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{3403.4bc97b4066945a94.js → 5329.8b7e4bca684b6eec.js} +2 -2
  19. package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/{5791-bb82743fa6299562.js → 763-7d0e1913b5074e98.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7633.0654fe6ea259c1ee.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → 8d0d9faf-97887ff99713b84e.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 91ff6dda-3b33d42d20657429.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/{webpack-e5bea36f183b25e6.js → webpack-37b8bf78a3c6eec5.js} +1 -1
  91. package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +1 -0
  92. package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → qMI0jnlZaBjjm4aIDYNGM}/_buildManifest.js +1 -1
  93. package/apps/pwa/out/approvals/index.html +1 -1
  94. package/apps/pwa/out/approvals/index.txt +29 -27
  95. package/apps/pwa/out/autopilot/index.html +1 -1
  96. package/apps/pwa/out/autopilot/index.txt +29 -27
  97. package/apps/pwa/out/board/index.html +1 -1
  98. package/apps/pwa/out/board/index.txt +29 -27
  99. package/apps/pwa/out/botlab/index.html +1 -1
  100. package/apps/pwa/out/botlab/index.txt +26 -24
  101. package/apps/pwa/out/bots/index.html +1 -1
  102. package/apps/pwa/out/bots/index.txt +26 -24
  103. package/apps/pwa/out/chat/index.html +1 -1
  104. package/apps/pwa/out/chat/index.txt +26 -24
  105. package/apps/pwa/out/control/index.html +1 -1
  106. package/apps/pwa/out/control/index.txt +29 -27
  107. package/apps/pwa/out/design-check/index.html +1 -1
  108. package/apps/pwa/out/design-check/index.txt +29 -27
  109. package/apps/pwa/out/editor/index.html +1 -1
  110. package/apps/pwa/out/editor/index.txt +29 -27
  111. package/apps/pwa/out/healthz +1 -1
  112. package/apps/pwa/out/icons/elanous-16.png +0 -0
  113. package/apps/pwa/out/icons/elanous-180.png +0 -0
  114. package/apps/pwa/out/icons/elanous-192.png +0 -0
  115. package/apps/pwa/out/icons/elanous-32.png +0 -0
  116. package/apps/pwa/out/icons/elanous-512.png +0 -0
  117. package/apps/pwa/out/index.html +1 -1
  118. package/apps/pwa/out/index.txt +26 -24
  119. package/apps/pwa/out/intake/index.html +1 -1
  120. package/apps/pwa/out/intake/index.txt +29 -27
  121. package/apps/pwa/out/live/index.html +1 -1
  122. package/apps/pwa/out/live/index.txt +29 -27
  123. package/apps/pwa/out/manifest.webmanifest +5 -2
  124. package/apps/pwa/out/market/index.html +1 -1
  125. package/apps/pwa/out/market/index.txt +26 -24
  126. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  127. package/apps/pwa/out/missions/placeholder/index.txt +26 -24
  128. package/apps/pwa/out/morning/index.html +1 -1
  129. package/apps/pwa/out/morning/index.txt +29 -27
  130. package/apps/pwa/out/observatory/index.html +1 -1
  131. package/apps/pwa/out/observatory/index.txt +29 -27
  132. package/apps/pwa/out/reflection/index.html +1 -1
  133. package/apps/pwa/out/reflection/index.txt +27 -25
  134. package/apps/pwa/out/scheduler/index.html +1 -1
  135. package/apps/pwa/out/scheduler/index.txt +29 -27
  136. package/apps/pwa/out/sessions/index.html +1 -1
  137. package/apps/pwa/out/sessions/index.txt +26 -24
  138. package/apps/pwa/out/settings/devices/index.html +1 -1
  139. package/apps/pwa/out/settings/devices/index.txt +29 -27
  140. package/apps/pwa/out/settings/index.html +1 -1
  141. package/apps/pwa/out/settings/index.txt +29 -27
  142. package/apps/pwa/out/setup/done/index.html +1 -1
  143. package/apps/pwa/out/setup/done/index.txt +29 -27
  144. package/apps/pwa/out/setup/index.html +1 -1
  145. package/apps/pwa/out/setup/index.txt +29 -27
  146. package/apps/pwa/out/share/index.html +1 -1
  147. package/apps/pwa/out/share/index.txt +29 -27
  148. package/apps/pwa/out/showroom/index.html +1 -1
  149. package/apps/pwa/out/showroom/index.txt +26 -24
  150. package/apps/pwa/out/sw-strategies.js +44 -0
  151. package/apps/pwa/out/sw.js +6 -44
  152. package/apps/pwa/out/tasks/index.html +1 -1
  153. package/apps/pwa/out/tasks/index.txt +29 -27
  154. package/apps/pwa/out/term/index.html +1 -1
  155. package/apps/pwa/out/term/index.txt +29 -27
  156. package/apps/pwa/out/trace/index.html +1 -1
  157. package/apps/pwa/out/trace/index.txt +29 -27
  158. package/apps/pwa/out/vault/index.html +1 -1
  159. package/apps/pwa/out/vault/index.txt +29 -27
  160. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  161. package/apps/pwa/out/workflows/chat-ui/index.txt +29 -27
  162. package/apps/pwa/out/workflows/index.html +1 -1
  163. package/apps/pwa/out/workflows/index.txt +29 -27
  164. package/apps/pwa/out/workspace/index.html +1 -1
  165. package/apps/pwa/out/workspace/index.txt +29 -27
  166. package/apps/pwa/out/worktrees/index.html +1 -1
  167. package/apps/pwa/out/worktrees/index.txt +29 -27
  168. package/catalog/resources.yaml +6 -6
  169. package/docs/goal-context/steward-directives-2026-09-28.md +35 -37
  170. package/graphs/landing/landing-heal.yaml +1 -1
  171. package/graphs/release/recipes.yaml +11 -1
  172. package/graphs/release/release-loop.yaml +9 -2
  173. package/graphs/video/README.md +1 -0
  174. package/graphs/video/explainer-line.yaml +58 -0
  175. package/package.json +3 -1
  176. package/plugins/card-followup/README.md +33 -0
  177. package/plugins/card-followup/examples/card-sample.json +7 -0
  178. package/plugins/card-followup/graphs/card-followup.yaml +16 -0
  179. package/plugins/card-followup/graphs/recipes.yaml +12 -0
  180. package/plugins/card-followup/graphs/run-step.ts +172 -0
  181. package/plugins/card-followup/plugin.json +25 -0
  182. package/plugins/geo-check/README.md +13 -0
  183. package/plugins/geo-check/examples/brand-sample.json +8 -0
  184. package/plugins/geo-check/graphs/geo-check.yaml +20 -0
  185. package/plugins/geo-check/graphs/recipes.yaml +18 -0
  186. package/plugins/geo-check/graphs/run-step.ts +177 -0
  187. package/plugins/geo-check/plugin.json +24 -0
  188. package/plugins/job-coach/.codex-plugin/plugin.json +18 -1
  189. package/plugins/job-coach/README.md +42 -0
  190. package/plugins/job-coach/connectors/ncs/server.ts +4 -3
  191. package/plugins/job-coach/graphs/gap-judge.ts +44 -0
  192. package/plugins/job-coach/graphs/recipes.yaml +27 -0
  193. package/plugins/job-coach/graphs/report-enterprise.yaml +28 -0
  194. package/plugins/job-coach/graphs/run-step.ts +211 -17
  195. package/plugins/job-coach/plugin.json +16 -3
  196. package/plugins/job-coach/skills/enterprise-ai-fit/SKILL.md +8 -0
  197. package/plugins/job-coach/skills/enterprise-competency/SKILL.md +8 -0
  198. package/plugins/job-coach/skills/enterprise-needs/SKILL.md +8 -0
  199. package/plugins/job-coach/skills/enterprise-roadmap/SKILL.md +8 -0
  200. package/plugins/job-coach/skills/enterprise-task-analysis/SKILL.md +8 -0
  201. package/plugins/job-coach/skills/interview-to-profile/SKILL.md +18 -1
  202. package/scripts/backup/elanous-backup.sh +4 -1
  203. package/scripts/build-docs-site.ts +4 -3
  204. package/scripts/coord-channel-watch.sh +23 -4
  205. package/scripts/coord-post.sh +49 -12
  206. package/scripts/coord-tracks.json +5 -5
  207. package/scripts/daemon-port-baseline.txt +1 -2
  208. package/scripts/eval-bot-vs-grok.ts +79 -0
  209. package/scripts/eval-grok-live-baseline.ts +36 -0
  210. package/scripts/fixtures/front-route-corpus.sample.json +17 -0
  211. package/scripts/fleet/node-setup.sh +46 -7
  212. package/scripts/landing-heal/must-fix-match.ts +26 -0
  213. package/scripts/landing-heal/run-mission.ts +114 -20
  214. package/scripts/lib/android-test-device.ts +32 -0
  215. package/scripts/lib/front-route-measurement.ts +131 -0
  216. package/scripts/lib/tui-sim-keys.ts +42 -0
  217. package/scripts/measure-front-route-classifier.ts +56 -0
  218. package/scripts/media/ocr-leak-scan.sh +21 -0
  219. package/scripts/public-export.ts +2 -3
  220. package/scripts/public-import-pr.ts +80 -6
  221. package/scripts/publish-internal-dist.ts +2 -3
  222. package/scripts/red-sweep-diff.ts +1 -1
  223. package/scripts/release-loop/auto-approve-node.ts +72 -0
  224. package/scripts/release-loop/checklist-gate-node.ts +35 -0
  225. package/scripts/release-loop/gate-diff.ts +29 -0
  226. package/scripts/release-loop/gate-node.ts +366 -45
  227. package/scripts/release-loop/publish-node.ts +7 -3
  228. package/scripts/release-loop/release-docs.ts +20 -13
  229. package/scripts/release-loop/shard-plan.ts +47 -0
  230. package/scripts/release-loop/test-durations.ts +63 -0
  231. package/scripts/release-loop/upgrade-node.ts +3 -2
  232. package/scripts/test-audit-coverage-per-file.sh +16 -0
  233. package/scripts/test-audit-meaningless.ts +88 -0
  234. package/scripts/test-audit-overlap-and-weak.ts +73 -0
  235. package/scripts/test-deterministic.ts +27 -5
  236. package/scripts/tui-sim.ts +59 -14
  237. package/scripts/ux/bump-installed.sh +20 -0
  238. package/scripts/ux/lib/cdp.ts +72 -0
  239. package/scripts/ux/record-page.ts +53 -0
  240. package/skills/explainer-video/SKILL.md +117 -0
  241. package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -0
  242. package/skills/explainer-video/engine/build.mjs +431 -0
  243. package/skills/explainer-video/engine/reel.mjs +128 -0
  244. package/skills/explainer-video/engine/reel.sh +8 -0
  245. package/skills/explainer-video/engine/vo.ts +79 -0
  246. package/skills/explainer-video/examples/elanous-0.2.5/script.json +155 -0
  247. package/src/agent/nested-agent-env.ts +1 -0
  248. package/src/agent-mission/driver.ts +95 -16
  249. package/src/agent-mission/mission-cli.ts +6 -0
  250. package/src/agent-mission/resource-ladder.ts +73 -25
  251. package/src/auth/acp-token.ts +16 -0
  252. package/src/auth/github-app-token.ts +112 -0
  253. package/src/autopilot/gh-rest-fallback.ts +177 -0
  254. package/src/autopilot/mission-origin.ts +20 -4
  255. package/src/autopilot/mission-phase-track.ts +11 -1
  256. package/src/autopilot/pr-manager.ts +51 -4
  257. package/src/autopilot/track-agent.ts +3 -3
  258. package/src/autopilot/track-courier.ts +2 -2
  259. package/src/boot/acp-server.ts +2 -16
  260. package/src/boot/daemon-tools/web-search.ts +2 -1
  261. package/src/boot/ws-bridge.ts +11 -0
  262. package/src/chat/index.ts +37 -8
  263. package/src/chat/log-scroll-key.ts +12 -0
  264. package/src/chat/submit-trace.ts +30 -0
  265. package/src/cli/agent-cli.ts +7 -2
  266. package/src/cli/decisions-cli.ts +94 -0
  267. package/src/cli/doctor-cli.ts +40 -54
  268. package/src/cli/doctor-readiness.ts +16 -24
  269. package/src/cli/intake-cli.ts +1 -1
  270. package/src/cli/mcp-call.ts +9 -0
  271. package/src/cli/mcp-gateway-cli.ts +7 -3
  272. package/src/cli/nexus-ios-bind.ts +25 -8
  273. package/src/cli/phone-link.ts +117 -0
  274. package/src/cli/plugin-cli.ts +136 -11
  275. package/src/cli/pr-lineage.ts +2 -1
  276. package/src/cli/private-ledger-commands.ts +31 -0
  277. package/src/cli/pwa-start.ts +3 -0
  278. package/src/cli/release-cli.ts +97 -7
  279. package/src/cli/self-update.ts +7 -4
  280. package/src/cli/start-cli.ts +33 -14
  281. package/src/dashboard/acp-boot.ts +3 -0
  282. package/src/dashboard/index.ts +62 -13
  283. package/src/dashboard/input/no-provider-reply.ts +13 -0
  284. package/src/dashboard/slash-runtime/unknown-slash-reply.ts +78 -0
  285. package/src/dashboard/stale-install-check.ts +77 -0
  286. package/src/decisions/decision-ledger.ts +404 -0
  287. package/src/domains/outbound-alert.ts +13 -7
  288. package/src/domains/telegram-kind-route.ts +21 -0
  289. package/src/execution-loop/dispatch-task.ts +139 -0
  290. package/src/execution-loop/gate-decision-summary.ts +29 -0
  291. package/src/execution-loop/gates.ts +197 -0
  292. package/src/field/field-media.ts +245 -0
  293. package/src/graph-kinds/installed-plugin-nodes.ts +95 -0
  294. package/src/graph-kinds/plugin-nodes.ts +10 -2
  295. package/src/graph-runner/runner.ts +35 -5
  296. package/src/grounding/token.ts +12 -6
  297. package/src/harness/harness-cli-command.ts +68 -9
  298. package/src/harness/harness-pod-dispatch.ts +8 -2
  299. package/src/harness/harness-stop.ts +147 -18
  300. package/src/harness/self-send-decide.ts +26 -17
  301. package/src/harness/self-send-target.ts +22 -1
  302. package/src/harness/worktree-branch-prefix.ts +4 -3
  303. package/src/index.ts +50 -13
  304. package/src/instance/resolve.ts +6 -0
  305. package/src/intake-plane/collect-telegram-saved.ts +5 -0
  306. package/src/intake-plane/digest.ts +11 -2
  307. package/src/live/detail-switch.ts +8 -3
  308. package/src/llm/usable-llm.ts +32 -0
  309. package/src/loops/registry.ts +27 -5
  310. package/src/market/publish.ts +59 -3
  311. package/src/native-tool-catalog.ts +2 -2
  312. package/src/nexus/api/field-uploads.ts +143 -0
  313. package/src/nexus/api/graph-approvals.ts +174 -0
  314. package/src/nexus/api/harness-api.ts +219 -5
  315. package/src/nexus/api/http-server.ts +69 -6
  316. package/src/nexus/api/log-fabric.ts +10 -1
  317. package/src/nexus/api/mcp-oauth-hint.ts +10 -9
  318. package/src/nexus/api/merge-approvals.ts +2 -1
  319. package/src/nexus/api/meta-api.ts +14 -0
  320. package/src/nexus/api/plugins-credentials-api.ts +47 -0
  321. package/src/nexus/api/plugins-market.ts +87 -3
  322. package/src/nexus/api/pod-credential-api.ts +68 -14
  323. package/src/nexus/api/public-routes.ts +7 -0
  324. package/src/nexus/api/trace.ts +57 -6
  325. package/src/nexus/boot/register-mcp-clients.ts +99 -14
  326. package/src/nexus/index.ts +33 -32
  327. package/src/nexus/outbound/router.ts +1 -1
  328. package/src/notify/llm-budget-alert.ts +119 -0
  329. package/src/onboarding.ts +25 -2
  330. package/src/plugins/core/host.ts +14 -0
  331. package/src/plugins/install/market-fetch.ts +4 -3
  332. package/src/plugins/install/plugin-credentials.ts +106 -0
  333. package/src/plugins/install/plugin-install.ts +163 -32
  334. package/src/plugins/maker/plugin-maker.ts +208 -0
  335. package/src/prompt-library/universal-preamble.ts +8 -2
  336. package/src/relay/channel.ts +184 -0
  337. package/src/relay/mux.ts +182 -0
  338. package/src/relay/test-vectors.json +29 -0
  339. package/src/release-loop/checklist.ts +217 -0
  340. package/src/release-loop/release-note.ts +6 -3
  341. package/src/seat-address/seat-address.ts +34 -0
  342. package/src/self-dev/dev-cli.ts +40 -20
  343. package/src/self-dev/dev-pipeline.ts +77 -1
  344. package/src/self-implement/gate-baseline.ts +3 -1
  345. package/src/self-implement/goal-author.ts +18 -1
  346. package/src/self-implement/host-regate.ts +6 -2
  347. package/src/self-implement/orchestrator.ts +7 -2
  348. package/src/self-implement/run-ledger-compact.ts +1 -1
  349. package/src/self-implement/seams.ts +4 -2
  350. package/src/session/index.ts +23 -10
  351. package/src/skills/tools/dev-harness.ts +42 -0
  352. package/src/steward/steward-cards.ts +45 -0
  353. package/src/steward/triage.ts +50 -14
  354. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +61 -3
  355. package/src/task-orchestrator/surfaces/pod-bun-cache.ts +18 -0
  356. package/src/task-orchestrator/surfaces/pod-command-job.ts +36 -6
  357. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +39 -9
  358. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +16 -1
  359. package/src/task-orchestrator/surfaces/pod-source-receive.ts +51 -13
  360. package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -28
  361. package/src/telegram-report.ts +12 -6
  362. package/src/telegram.ts +108 -1
  363. package/src/tui-client/acp-transport-ws-client.ts +72 -5
  364. package/src/tui.ts +3 -2
  365. package/src/user-config.ts +32 -2
  366. package/src/util/spawn-sync-output.ts +15 -0
  367. package/src/version/code-revision.ts +1 -1
  368. package/src/version/packed-revision.json +1 -1
  369. package/src/video-pipeline/recipes/explainer.ts +213 -0
  370. package/src/video-pipeline/walk-line.ts +3 -2
  371. package/src/web-search/free.ts +77 -0
  372. package/src/web-search/index.ts +2 -0
  373. package/src/workflow-runtime/deps-bridge.ts +4 -3
  374. package/src/workflow-runtime/plugin-kind-node.ts +32 -4
  375. package/src/workflow-runtime/types.ts +2 -1
  376. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  377. package/apps/pwa/out/_next/static/chunks/1191-7b13a24117a689fa.js +0 -1
  378. package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
  379. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  380. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  381. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  382. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  383. package/apps/pwa/out/_next/static/chunks/2375-3fcae5186c64fe6b.js +0 -1
  384. package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +0 -1
  385. package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
  386. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  387. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  388. package/apps/pwa/out/_next/static/chunks/319-d37df1b6e0cbeff4.js +0 -1
  389. package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
  390. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  391. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  392. package/apps/pwa/out/_next/static/chunks/4836-3c93795ab834381e.js +0 -1
  393. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  394. package/apps/pwa/out/_next/static/chunks/6090-1bd6077591603947.js +0 -1
  395. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  396. package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
  397. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  398. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  399. package/apps/pwa/out/_next/static/chunks/7457-f5cb1cc8db9f7453.js +0 -1
  400. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  401. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  402. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  403. package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +0 -1
  404. package/apps/pwa/out/_next/static/chunks/8519-0059d82bdb4812bf.js +0 -1
  405. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  406. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  407. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  408. package/apps/pwa/out/_next/static/chunks/9740-52467f244755917f.js +0 -1
  409. package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +0 -1
  410. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  412. package/apps/pwa/out/_next/static/chunks/app/approvals/page-21c3261af6ab377c.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1991445b8ea86274.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/app/board/page-4ebfaee2369fe248.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/app/botlab/page-9fa879ebf5959096.js +0 -1
  416. package/apps/pwa/out/_next/static/chunks/app/bots/page-ca7c3cc305c3285a.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/app/chat/page-9951930add3d2051.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/app/control/page-4472ddf1e2458e69.js +0 -1
  419. package/apps/pwa/out/_next/static/chunks/app/design-check/page-4de08d4b766f134b.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/app/editor/page-7a975e2b9db1dfb7.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  422. package/apps/pwa/out/_next/static/chunks/app/intake/page-49270714ef35bb96.js +0 -1
  423. package/apps/pwa/out/_next/static/chunks/app/layout-33fbdf353e2a5982.js +0 -1
  424. package/apps/pwa/out/_next/static/chunks/app/live/page-259f122e2dd64317.js +0 -1
  425. package/apps/pwa/out/_next/static/chunks/app/market/page-65b82726753658e1.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4b8df3748dc06285.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/app/morning/page-112f7e477bda77fd.js +0 -1
  428. package/apps/pwa/out/_next/static/chunks/app/not-found-542a5c0006da637c.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/app/observatory/page-63ffb347831262ea.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/app/page-98e4e359d97263b4.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/app/reflection/page-97e24ac675ce576e.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ac6391f26391d89d.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f45c9d45ffc43bd.js +0 -1
  434. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-36995b9cb5d98218.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/app/settings/page-0c795b577988d63f.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b4311e2f71c6735a.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/app/setup/page-eb2381074df20dba.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/app/share/page-b01336e64eba01ae.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/app/showroom/page-7af7f21c1f2858ef.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/app/tasks/page-a98ecd364586c89b.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/app/term/page-57f0502dfbfa089b.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/app/trace/page-3467d2e1abd8a14a.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/app/vault/page-7842ca23d53ba682.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-ae9ac882912b0198.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/app/workflows/page-3a0d72e3f783bcef.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/app/workspace/page-f3b39fe8358c2a97.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-5a22af3181ac2afb.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/main-app-1457627bb302a22c.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  454. package/apps/pwa/out/_next/static/css/e66b7f5efc8df3f3.css +0 -1
  455. /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
- .requiredOption('--nexus-url <url>', 'NEXUS base URL')
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: string; publicUrl?: string; nexusTokenFile?: string }) => {
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: opts.nexusUrl, nexusToken, patRoot, publicUrl: opts.publicUrl });
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
- const hostResult = defaultsWrite(spawnFn, bundleId, 'nexusHost', 'string', host, dryRun);
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, bundleId, 'nexusPort', 'int', String(port), dryRun);
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, bundleId, 'bearerToken', 'string', token, dryRun);
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 ${bundleId} bearerToken <${tokenLength}-char redacted>`);
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, bundleId, 'asciiKeyboard', 'bool', value, dryRun);
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, bundleId, 'seedPrompt', 'string', opts.seedPrompt, dryRun);
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 ${bundleId} seedPrompt "${preview}" (${opts.seedPrompt.length} chars)`);
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
+ }
@@ -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
- .action(async (spec: string, opts: { yes?: boolean; json?: boolean; allowUnsigned?: boolean }) => {
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
- debug.log('plugin.cli', 'failed', { reason }, { level: 'error' });
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 {
@@ -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
+ }
@@ -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