@synap-core/cli 1.11.0 → 1.12.2

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 (452) hide show
  1. package/README.md +23 -198
  2. package/dist/build-info.json +6 -0
  3. package/dist/commands/agent-run.js +3 -2
  4. package/dist/commands/agent-run.js.map +1 -1
  5. package/dist/commands/agents.d.ts +33 -17
  6. package/dist/commands/agents.js +203 -237
  7. package/dist/commands/agents.js.map +1 -1
  8. package/dist/commands/app.d.ts +143 -0
  9. package/dist/commands/app.js +523 -0
  10. package/dist/commands/app.js.map +1 -0
  11. package/dist/commands/automate.d.ts +21 -0
  12. package/dist/commands/automate.js +42 -0
  13. package/dist/commands/automate.js.map +1 -0
  14. package/dist/commands/automation-schema.test.d.ts +16 -0
  15. package/dist/commands/automation-schema.test.js +77 -0
  16. package/dist/commands/automation-schema.test.js.map +1 -0
  17. package/dist/commands/automation.d.ts +12 -0
  18. package/dist/commands/automation.js +80 -35
  19. package/dist/commands/automation.js.map +1 -1
  20. package/dist/commands/bridge-setup.d.ts +14 -1
  21. package/dist/commands/bridge-setup.js +392 -128
  22. package/dist/commands/bridge-setup.js.map +1 -1
  23. package/dist/commands/cap-connections.d.ts +4 -4
  24. package/dist/commands/cap-connections.js +14 -14
  25. package/dist/commands/cap-connections.js.map +1 -1
  26. package/dist/commands/capability.d.ts +2 -0
  27. package/dist/commands/capability.js +218 -168
  28. package/dist/commands/capability.js.map +1 -1
  29. package/dist/commands/catalog.d.ts +275 -0
  30. package/dist/commands/catalog.js +481 -0
  31. package/dist/commands/catalog.js.map +1 -0
  32. package/dist/commands/cell.d.ts +155 -8
  33. package/dist/commands/cell.js +416 -40
  34. package/dist/commands/cell.js.map +1 -1
  35. package/dist/commands/centrality.js +1 -13
  36. package/dist/commands/centrality.js.map +1 -1
  37. package/dist/commands/connect.d.ts +38 -13
  38. package/dist/commands/connect.js +68 -167
  39. package/dist/commands/connect.js.map +1 -1
  40. package/dist/commands/connections.d.ts +3 -2
  41. package/dist/commands/connections.js +95 -25
  42. package/dist/commands/connections.js.map +1 -1
  43. package/dist/commands/context.js +7 -1
  44. package/dist/commands/context.js.map +1 -1
  45. package/dist/commands/data-extra.d.ts +34 -0
  46. package/dist/commands/data-extra.js +193 -23
  47. package/dist/commands/data-extra.js.map +1 -1
  48. package/dist/commands/data.d.ts +120 -2
  49. package/dist/commands/data.js +293 -20
  50. package/dist/commands/data.js.map +1 -1
  51. package/dist/commands/diagnose.d.ts +66 -4
  52. package/dist/commands/diagnose.js +285 -28
  53. package/dist/commands/diagnose.js.map +1 -1
  54. package/dist/commands/doc.d.ts +11 -4
  55. package/dist/commands/doc.js +39 -14
  56. package/dist/commands/doc.js.map +1 -1
  57. package/dist/commands/doctor.d.ts +112 -3
  58. package/dist/commands/doctor.js +657 -32
  59. package/dist/commands/doctor.js.map +1 -1
  60. package/dist/commands/explain.js +5 -5
  61. package/dist/commands/explain.js.map +1 -1
  62. package/dist/commands/import.d.ts +368 -13
  63. package/dist/commands/import.js +1064 -54
  64. package/dist/commands/import.js.map +1 -1
  65. package/dist/commands/init.d.ts +20 -9
  66. package/dist/commands/init.js +433 -973
  67. package/dist/commands/init.js.map +1 -1
  68. package/dist/commands/keys.d.ts +1 -0
  69. package/dist/commands/keys.js +15 -2
  70. package/dist/commands/keys.js.map +1 -1
  71. package/dist/commands/knowledge.d.ts +22 -6
  72. package/dist/commands/knowledge.js +117 -55
  73. package/dist/commands/knowledge.js.map +1 -1
  74. package/dist/commands/lens.d.ts +37 -1
  75. package/dist/commands/lens.js +192 -71
  76. package/dist/commands/lens.js.map +1 -1
  77. package/dist/commands/market-authoring.d.ts +29 -2
  78. package/dist/commands/market-authoring.js +348 -7
  79. package/dist/commands/market-authoring.js.map +1 -1
  80. package/dist/commands/market-official.d.ts +58 -0
  81. package/dist/commands/market-official.js +84 -0
  82. package/dist/commands/market-official.js.map +1 -0
  83. package/dist/commands/market.d.ts +400 -16
  84. package/dist/commands/market.js +1802 -146
  85. package/dist/commands/market.js.map +1 -1
  86. package/dist/commands/mcp.d.ts +60 -25
  87. package/dist/commands/mcp.js +251 -64
  88. package/dist/commands/mcp.js.map +1 -1
  89. package/dist/commands/open.d.ts +39 -9
  90. package/dist/commands/open.js +80 -25
  91. package/dist/commands/open.js.map +1 -1
  92. package/dist/commands/playbook.d.ts +42 -0
  93. package/dist/commands/playbook.js +162 -0
  94. package/dist/commands/playbook.js.map +1 -0
  95. package/dist/commands/pod.js +1 -1
  96. package/dist/commands/pod.js.map +1 -1
  97. package/dist/commands/pods.d.ts +5 -0
  98. package/dist/commands/pods.js +97 -116
  99. package/dist/commands/pods.js.map +1 -1
  100. package/dist/commands/proposals-actions.d.ts +62 -0
  101. package/dist/commands/proposals-actions.js +143 -8
  102. package/dist/commands/proposals-actions.js.map +1 -1
  103. package/dist/commands/proposals-await.d.ts +68 -0
  104. package/dist/commands/proposals-await.js +248 -0
  105. package/dist/commands/proposals-await.js.map +1 -0
  106. package/dist/commands/proton-setup.d.ts +73 -0
  107. package/dist/commands/proton-setup.js +577 -0
  108. package/dist/commands/proton-setup.js.map +1 -0
  109. package/dist/commands/providers.d.ts +23 -0
  110. package/dist/commands/providers.js +126 -1
  111. package/dist/commands/providers.js.map +1 -1
  112. package/dist/commands/raycast.js +6 -2
  113. package/dist/commands/raycast.js.map +1 -1
  114. package/dist/commands/rule.d.ts +150 -0
  115. package/dist/commands/rule.js +447 -0
  116. package/dist/commands/rule.js.map +1 -0
  117. package/dist/commands/session-wait.d.ts +111 -0
  118. package/dist/commands/session-wait.js +209 -0
  119. package/dist/commands/session-wait.js.map +1 -0
  120. package/dist/commands/sessions.d.ts +52 -0
  121. package/dist/commands/sessions.js +478 -58
  122. package/dist/commands/sessions.js.map +1 -1
  123. package/dist/commands/skill-manage.d.ts +21 -0
  124. package/dist/commands/skill-manage.js +136 -19
  125. package/dist/commands/skill-manage.js.map +1 -1
  126. package/dist/commands/skills.js +16 -0
  127. package/dist/commands/skills.js.map +1 -1
  128. package/dist/commands/status.d.ts +1 -4
  129. package/dist/commands/status.js +5 -34
  130. package/dist/commands/status.js.map +1 -1
  131. package/dist/commands/statusline.backoff.test.d.ts +13 -0
  132. package/dist/commands/statusline.backoff.test.js +100 -0
  133. package/dist/commands/statusline.backoff.test.js.map +1 -0
  134. package/dist/commands/statusline.js +16 -9
  135. package/dist/commands/statusline.js.map +1 -1
  136. package/dist/commands/sync-status.d.ts +12 -0
  137. package/dist/commands/sync-status.js +125 -0
  138. package/dist/commands/sync-status.js.map +1 -0
  139. package/dist/commands/templates-retire.d.ts +34 -0
  140. package/dist/commands/templates-retire.js +192 -0
  141. package/dist/commands/templates-retire.js.map +1 -0
  142. package/dist/commands/tools.js +6 -21
  143. package/dist/commands/tools.js.map +1 -1
  144. package/dist/commands/tracks.d.ts +51 -0
  145. package/dist/commands/tracks.js +291 -0
  146. package/dist/commands/tracks.js.map +1 -0
  147. package/dist/commands/update.d.ts +3 -11
  148. package/dist/commands/update.js +5 -134
  149. package/dist/commands/update.js.map +1 -1
  150. package/dist/commands/upload.d.ts +41 -3
  151. package/dist/commands/upload.js +120 -18
  152. package/dist/commands/upload.js.map +1 -1
  153. package/dist/commands/vendor.d.ts +28 -0
  154. package/dist/commands/vendor.js +110 -0
  155. package/dist/commands/vendor.js.map +1 -0
  156. package/dist/commands/versions.js +6 -0
  157. package/dist/commands/versions.js.map +1 -1
  158. package/dist/commands/view.js +10 -5
  159. package/dist/commands/view.js.map +1 -1
  160. package/dist/commands/whoami.d.ts +23 -3
  161. package/dist/commands/whoami.js +169 -23
  162. package/dist/commands/whoami.js.map +1 -1
  163. package/dist/commands/worker.d.ts +122 -0
  164. package/dist/commands/worker.js +435 -0
  165. package/dist/commands/worker.js.map +1 -0
  166. package/dist/commands/worker.test.d.ts +1 -0
  167. package/dist/commands/worker.test.js +116 -0
  168. package/dist/commands/worker.test.js.map +1 -0
  169. package/dist/commands/workspace-ops.d.ts +35 -0
  170. package/dist/commands/workspace-ops.js +153 -0
  171. package/dist/commands/workspace-ops.js.map +1 -0
  172. package/dist/index.d.ts +6 -6
  173. package/dist/index.js +900 -180
  174. package/dist/index.js.map +1 -1
  175. package/dist/lib/agent-connections.d.ts +31 -0
  176. package/dist/lib/agent-connections.js +33 -0
  177. package/dist/lib/agent-connections.js.map +1 -0
  178. package/dist/lib/agent-detect.d.ts +27 -0
  179. package/dist/lib/agent-detect.js +71 -0
  180. package/dist/lib/agent-detect.js.map +1 -0
  181. package/dist/lib/agent-door.d.ts +138 -0
  182. package/dist/lib/agent-door.js +426 -0
  183. package/dist/lib/agent-door.js.map +1 -0
  184. package/dist/lib/agents-config.d.ts +2 -0
  185. package/dist/lib/agents-config.js +4 -9
  186. package/dist/lib/agents-config.js.map +1 -1
  187. package/dist/lib/approval-poll.d.ts +31 -2
  188. package/dist/lib/approval-poll.js +20 -7
  189. package/dist/lib/approval-poll.js.map +1 -1
  190. package/dist/lib/atomic-file.d.ts +48 -0
  191. package/dist/lib/atomic-file.js +129 -0
  192. package/dist/lib/atomic-file.js.map +1 -0
  193. package/dist/lib/auth.d.ts +14 -9
  194. package/dist/lib/auth.js +44 -61
  195. package/dist/lib/auth.js.map +1 -1
  196. package/dist/lib/batch-approval.d.ts +85 -0
  197. package/dist/lib/batch-approval.js +170 -0
  198. package/dist/lib/batch-approval.js.map +1 -0
  199. package/dist/lib/capture-lane.d.ts +6 -5
  200. package/dist/lib/capture-lane.js +6 -5
  201. package/dist/lib/capture-lane.js.map +1 -1
  202. package/dist/lib/capture-structure.d.ts +230 -11
  203. package/dist/lib/capture-structure.js +251 -13
  204. package/dist/lib/capture-structure.js.map +1 -1
  205. package/dist/lib/catalog-templates.d.ts +59 -0
  206. package/dist/lib/catalog-templates.js +126 -0
  207. package/dist/lib/catalog-templates.js.map +1 -0
  208. package/dist/lib/cli-build.d.ts +19 -0
  209. package/dist/lib/cli-build.js +37 -0
  210. package/dist/lib/cli-build.js.map +1 -0
  211. package/dist/lib/conversation-headers.d.ts +18 -0
  212. package/dist/lib/conversation-headers.js +32 -0
  213. package/dist/lib/conversation-headers.js.map +1 -0
  214. package/dist/lib/cp-packages.d.ts +167 -11
  215. package/dist/lib/cp-packages.js +253 -21
  216. package/dist/lib/cp-packages.js.map +1 -1
  217. package/dist/lib/credential-class.d.ts +117 -0
  218. package/dist/lib/credential-class.js +116 -0
  219. package/dist/lib/credential-class.js.map +1 -0
  220. package/dist/lib/describe-lens.d.ts +35 -0
  221. package/dist/lib/describe-lens.js +40 -0
  222. package/dist/lib/describe-lens.js.map +1 -1
  223. package/dist/lib/device-connect.d.ts +125 -0
  224. package/dist/lib/device-connect.js +256 -0
  225. package/dist/lib/device-connect.js.map +1 -0
  226. package/dist/lib/directory-lens.d.ts +99 -0
  227. package/dist/lib/directory-lens.js +187 -0
  228. package/dist/lib/directory-lens.js.map +1 -0
  229. package/dist/lib/exporter-coverage.d.ts +71 -0
  230. package/dist/lib/exporter-coverage.js +97 -0
  231. package/dist/lib/exporter-coverage.js.map +1 -0
  232. package/dist/lib/hub-client.d.ts +29 -8
  233. package/dist/lib/hub-client.js +328 -55
  234. package/dist/lib/hub-client.js.map +1 -1
  235. package/dist/lib/init-connect.d.ts +41 -0
  236. package/dist/lib/init-connect.js +75 -0
  237. package/dist/lib/init-connect.js.map +1 -0
  238. package/dist/lib/init-exit.d.ts +24 -0
  239. package/dist/lib/init-exit.js +41 -0
  240. package/dist/lib/init-exit.js.map +1 -0
  241. package/dist/lib/init-flow.d.ts +25 -0
  242. package/dist/lib/init-flow.js +40 -0
  243. package/dist/lib/init-flow.js.map +1 -0
  244. package/dist/lib/init-handoff.d.ts +16 -0
  245. package/dist/lib/init-handoff.js +19 -0
  246. package/dist/lib/init-handoff.js.map +1 -0
  247. package/dist/lib/init-output.d.ts +18 -0
  248. package/dist/lib/init-output.js +43 -0
  249. package/dist/lib/init-output.js.map +1 -0
  250. package/dist/lib/init-pod.d.ts +45 -0
  251. package/dist/lib/init-pod.js +36 -0
  252. package/dist/lib/init-pod.js.map +1 -0
  253. package/dist/lib/installed.d.ts +210 -11
  254. package/dist/lib/installed.js +234 -36
  255. package/dist/lib/installed.js.map +1 -1
  256. package/dist/lib/key-approval.d.ts +57 -0
  257. package/dist/lib/key-approval.js +117 -0
  258. package/dist/lib/key-approval.js.map +1 -0
  259. package/dist/lib/key-diagnosis.d.ts +56 -0
  260. package/dist/lib/key-diagnosis.js +227 -0
  261. package/dist/lib/key-diagnosis.js.map +1 -0
  262. package/dist/lib/key-doctor.d.ts +136 -0
  263. package/dist/lib/key-doctor.js +286 -0
  264. package/dist/lib/key-doctor.js.map +1 -0
  265. package/dist/lib/key-locations.d.ts +120 -0
  266. package/dist/lib/key-locations.js +326 -0
  267. package/dist/lib/key-locations.js.map +1 -0
  268. package/dist/lib/key-source.d.ts +66 -0
  269. package/dist/lib/key-source.js +106 -0
  270. package/dist/lib/key-source.js.map +1 -0
  271. package/dist/lib/key-state.d.ts +70 -0
  272. package/dist/lib/key-state.js +115 -0
  273. package/dist/lib/key-state.js.map +1 -0
  274. package/dist/lib/kind-package.d.ts +181 -0
  275. package/dist/lib/kind-package.js +679 -0
  276. package/dist/lib/kind-package.js.map +1 -0
  277. package/dist/lib/knowledge-title.d.ts +24 -0
  278. package/dist/lib/knowledge-title.js +46 -0
  279. package/dist/lib/knowledge-title.js.map +1 -0
  280. package/dist/lib/knowledge-title.test.d.ts +1 -0
  281. package/dist/lib/knowledge-title.test.js +34 -0
  282. package/dist/lib/knowledge-title.test.js.map +1 -0
  283. package/dist/lib/machine-instance.d.ts +64 -0
  284. package/dist/lib/machine-instance.js +142 -0
  285. package/dist/lib/machine-instance.js.map +1 -0
  286. package/dist/lib/mcp-bridge.d.ts +42 -0
  287. package/dist/lib/mcp-bridge.js +147 -0
  288. package/dist/lib/mcp-bridge.js.map +1 -0
  289. package/dist/lib/mcp-bridge.test.d.ts +1 -0
  290. package/dist/lib/mcp-bridge.test.js +90 -0
  291. package/dist/lib/mcp-bridge.test.js.map +1 -0
  292. package/dist/lib/mcp-call.d.ts +30 -0
  293. package/dist/lib/mcp-call.js +107 -0
  294. package/dist/lib/mcp-call.js.map +1 -0
  295. package/dist/lib/mcp-snippets.d.ts +53 -0
  296. package/dist/lib/mcp-snippets.js +82 -0
  297. package/dist/lib/mcp-snippets.js.map +1 -0
  298. package/dist/lib/next-steps.d.ts +8 -0
  299. package/dist/lib/next-steps.js +31 -1
  300. package/dist/lib/next-steps.js.map +1 -1
  301. package/dist/lib/package-wire.d.ts +179 -0
  302. package/dist/lib/package-wire.js +68 -0
  303. package/dist/lib/package-wire.js.map +1 -0
  304. package/dist/lib/pair-link.d.ts +38 -0
  305. package/dist/lib/pair-link.js +68 -0
  306. package/dist/lib/pair-link.js.map +1 -0
  307. package/dist/lib/pod-wide.d.ts +40 -0
  308. package/dist/lib/pod-wide.js +46 -0
  309. package/dist/lib/pod-wide.js.map +1 -0
  310. package/dist/lib/pod.d.ts +66 -112
  311. package/dist/lib/pod.js +134 -412
  312. package/dist/lib/pod.js.map +1 -1
  313. package/dist/lib/project-ref.d.ts +9 -3
  314. package/dist/lib/project-ref.js +10 -4
  315. package/dist/lib/project-ref.js.map +1 -1
  316. package/dist/lib/review-credential.d.ts +119 -0
  317. package/dist/lib/review-credential.js +147 -0
  318. package/dist/lib/review-credential.js.map +1 -0
  319. package/dist/lib/safe-open.d.ts +49 -0
  320. package/dist/lib/safe-open.js +119 -0
  321. package/dist/lib/safe-open.js.map +1 -0
  322. package/dist/lib/session-lens.d.ts +40 -1
  323. package/dist/lib/session-lens.js +54 -4
  324. package/dist/lib/session-lens.js.map +1 -1
  325. package/dist/lib/superwhisper-import.d.ts +8 -0
  326. package/dist/lib/superwhisper-import.js +14 -7
  327. package/dist/lib/superwhisper-import.js.map +1 -1
  328. package/dist/lib/superwhisper-title.test.d.ts +1 -0
  329. package/dist/lib/superwhisper-title.test.js +18 -0
  330. package/dist/lib/superwhisper-title.test.js.map +1 -0
  331. package/dist/lib/targets.d.ts +134 -10
  332. package/dist/lib/targets.js +962 -258
  333. package/dist/lib/targets.js.map +1 -1
  334. package/dist/lib/template-file.d.ts +4 -0
  335. package/dist/lib/template-file.js.map +1 -1
  336. package/dist/lib/template-lifecycle.d.ts +217 -0
  337. package/dist/lib/template-lifecycle.js +433 -0
  338. package/dist/lib/template-lifecycle.js.map +1 -0
  339. package/dist/lib/template-steps.d.ts +88 -0
  340. package/dist/lib/template-steps.js +118 -0
  341. package/dist/lib/template-steps.js.map +1 -0
  342. package/dist/lib/time-ago.d.ts +2 -0
  343. package/dist/lib/time-ago.js +15 -0
  344. package/dist/lib/time-ago.js.map +1 -0
  345. package/dist/utils/logger.js +5 -2
  346. package/dist/utils/logger.js.map +1 -1
  347. package/package.json +23 -17
  348. package/skills/README.md +12 -10
  349. package/skills/agent-os/SKILL.md +214 -99
  350. package/skills/manifest.json +1 -1
  351. package/skills/onboard/SKILL.md +55 -26
  352. package/skills/synap/README.md +5 -8
  353. package/skills/synap/SKILL.md +1086 -283
  354. package/skills/synap/_order.txt +7 -0
  355. package/skills/synap/_skill.yaml +1 -1
  356. package/skills/synap/automations.md +15 -0
  357. package/skills/synap/capture.md +5 -0
  358. package/skills/synap/cli-operations.md +24 -18
  359. package/skills/synap/concepts.md +28 -0
  360. package/skills/synap/content-os.md +10 -1
  361. package/skills/synap/creative-loop.md +15 -10
  362. package/skills/synap/crm.md +21 -52
  363. package/skills/synap/document-embeds.md +141 -47
  364. package/skills/synap/escalation-ladder.md +20 -10
  365. package/skills/synap/focus-sessions.md +47 -21
  366. package/skills/synap/from-intent.md +142 -0
  367. package/skills/synap/governance-rules.md +4 -4
  368. package/skills/synap/governance.md +10 -7
  369. package/skills/synap/hand-off.md +33 -0
  370. package/skills/synap/inline-patterns.md +15 -7
  371. package/skills/synap/lenses.md +33 -15
  372. package/skills/synap/linking.md +41 -40
  373. package/skills/synap/mcp-remote-servers.md +64 -0
  374. package/skills/synap/mental-model.md +17 -15
  375. package/skills/synap/operating-mode-rules.md +1 -1
  376. package/skills/synap/processes.md +118 -0
  377. package/skills/synap/quick-reference.md +5 -1
  378. package/skills/synap/reading.md +3 -1
  379. package/skills/synap/reflexes.md +12 -42
  380. package/skills/synap/viewframe-cells.md +38 -5
  381. package/skills/synap/work-flow.md +1 -1
  382. package/skills/synap/work-model.md +96 -0
  383. package/skills/synap/worked-examples.md +4 -5
  384. package/skills/synap/workspace-design.md +14 -10
  385. package/skills/synap/writes.md +97 -24
  386. package/skills/synap/writing-titles.md +63 -0
  387. package/skills/synap-app/SKILL.md +2 -2
  388. package/skills/synap-app/_skill.yaml +1 -1
  389. package/skills/synap-app/install-commands.md +1 -1
  390. package/skills/synap-design/SKILL.md +474 -0
  391. package/skills/synap-design/_order.txt +4 -0
  392. package/skills/synap-design/_skill.yaml +21 -0
  393. package/skills/synap-design/loop.md +134 -0
  394. package/skills/synap-design/overview.md +44 -0
  395. package/skills/synap-design/principles.md +130 -0
  396. package/skills/synap-design/system.md +133 -0
  397. package/skills/synap-market/SKILL.md +306 -16
  398. package/skills/synap-market/_skill.yaml +1 -1
  399. package/skills/synap-market/author-and-publish.md +145 -8
  400. package/skills/synap-market/governance-and-catalog.md +7 -0
  401. package/skills/synap-market/install.md +79 -1
  402. package/skills/synap-market/overview.md +74 -6
  403. package/skills/synap-schema/SKILL.md +172 -10
  404. package/skills/synap-schema/_order.txt +2 -0
  405. package/skills/synap-schema/_skill.yaml +4 -2
  406. package/skills/synap-schema/common-mistakes.md +4 -2
  407. package/skills/synap-schema/discover-profiles.md +1 -1
  408. package/skills/synap-schema/entity-scope.md +15 -3
  409. package/skills/synap-schema/extend-first.md +29 -0
  410. package/skills/synap-schema/extend-vs-create.md +1 -1
  411. package/skills/synap-schema/intent-vocabulary-ssot.md +111 -0
  412. package/skills/synap-schema/property-types.md +1 -1
  413. package/skills/synap-schema/relations.md +1 -1
  414. package/skills/synap-ui/SKILL.md +19 -12
  415. package/skills/synap-ui/_skill.yaml +1 -1
  416. package/skills/synap-ui/ai-companion.md +1 -1
  417. package/skills/synap-ui/arranging-bento.md +2 -0
  418. package/skills/synap-ui/bento-layouts.md +10 -10
  419. package/skills/synap-ui/bento-recipes.md +313 -543
  420. package/skills/synap-ui/four-builds.md +5 -0
  421. package/skills/synap-ui/view-types.md +2 -2
  422. package/skills/synap-ui/widget-catalog.md +22 -278
  423. package/templates/automations/bookmark-enrichment.automation.json +290 -13
  424. package/templates/automations/url-bookmark-capture.automation.json +73 -11
  425. package/templates/skills/authoring-playbooks/SKILL.md +6 -3
  426. package/templates/skills/authoring-playbooks/reference/stages-contract.md +15 -0
  427. package/templates/skills/authoring-playbooks/reference/worked-example.md +3 -0
  428. package/dist/commands/infra.d.ts +0 -21
  429. package/dist/commands/infra.js +0 -262
  430. package/dist/commands/infra.js.map +0 -1
  431. package/dist/commands/template.d.ts +0 -42
  432. package/dist/commands/template.js +0 -191
  433. package/dist/commands/template.js.map +0 -1
  434. package/dist/lib/browser-auth.d.ts +0 -35
  435. package/dist/lib/browser-auth.js +0 -170
  436. package/dist/lib/browser-auth.js.map +0 -1
  437. package/dist/lib/openclaw.d.ts +0 -32
  438. package/dist/lib/openclaw.js +0 -164
  439. package/dist/lib/openclaw.js.map +0 -1
  440. package/dist/lib/seed.d.ts +0 -13
  441. package/dist/lib/seed.js +0 -135
  442. package/dist/lib/seed.js.map +0 -1
  443. package/node_modules/@synap/hub-rest-client/dist/index.cjs +0 -1261
  444. package/node_modules/@synap/hub-rest-client/dist/index.d.cts +0 -1647
  445. package/node_modules/@synap/hub-rest-client/dist/index.d.ts +0 -1647
  446. package/node_modules/@synap/hub-rest-client/dist/index.js +0 -1231
  447. package/node_modules/@synap/hub-rest-client/package.json +0 -45
  448. package/node_modules/@synap/hub-rest-client/src/client.ts +0 -1631
  449. package/node_modules/@synap/hub-rest-client/src/errors.ts +0 -30
  450. package/node_modules/@synap/hub-rest-client/src/index.ts +0 -153
  451. package/node_modules/@synap/hub-rest-client/src/setup.ts +0 -133
  452. package/node_modules/@synap/hub-rest-client/src/types.ts +0 -1299
@@ -0,0 +1,143 @@
1
+ /**
2
+ * `synap app init` / `synap app connect` — the App Connect flow (APP-CONNECT-SPEC §1/§5).
3
+ *
4
+ * An app repo carries a `synap.app.json` manifest at its root. `init` writes it
5
+ * (asks name, pod, requests) and registers the app on the pod, writing the pod's
6
+ * assigned `public_id` back into the file. `connect` files ONE proposal naming
7
+ * the app and its requested reach, waits for the human to approve it, mints the
8
+ * app's key, and drops it into the deploy target declared by `deploy`.
9
+ *
10
+ * Naming (locked by the spec): the object is an Application, the authorization a
11
+ * grant, the bearer an API key. Never "connection".
12
+ *
13
+ * Auth: the SAME pod key every other command uses (`ensurePodKey` / `login()`).
14
+ * The manifest names its pod, so we first look for a saved profile for THAT pod
15
+ * (the rule `ensurePodKey` uses), then fall back to the active pod's key when it
16
+ * is the same pod. No second auth mechanism is invented.
17
+ */
18
+ import { HubRestClient } from "@synap-core/hub-rest-client";
19
+ export declare const MANIFEST_FILENAME = "synap.app.json";
20
+ /** v1 supports ONLY "specific" mode (spec §8 puts global/OAuth out of scope). */
21
+ export type AppMode = "specific";
22
+ export interface AppRequest {
23
+ /** W1 grant grammar: `entity.<qualifier>.<action>`, `entity.*.create`, `*`. */
24
+ permission: string;
25
+ /** A workspace NAME — the pod resolves it to an id (unknown ⇒ register/connect fails loud). */
26
+ workspace: string;
27
+ }
28
+ export type DeployTarget = "vercel" | "env";
29
+ export interface DeploySpec {
30
+ /** `vercel` → `vercel env add …`; `env` → write `.env.local`. */
31
+ target: DeployTarget;
32
+ /** Which env var receives which value. `key` is required. */
33
+ env: {
34
+ key?: string;
35
+ podUrl?: string;
36
+ workspaceId?: string;
37
+ };
38
+ }
39
+ export interface AppManifest {
40
+ version: 1;
41
+ /** Pod-assigned `public_id` (`app_<uuid>`) — the app's `client_id`. ABSENT until first register. */
42
+ id?: string;
43
+ name: string;
44
+ description?: string;
45
+ logoUrl: string | null;
46
+ mode: AppMode;
47
+ pod: string;
48
+ requests: AppRequest[];
49
+ deploy: DeploySpec;
50
+ }
51
+ /**
52
+ * Parse + validate a manifest. Every failure names the field and the fix —
53
+ * a malformed manifest is a visible error, never a silent default.
54
+ */
55
+ export declare function parseManifest(raw: string): AppManifest;
56
+ /** Serialize in the spec's field order, omitting absent optionals. Round-trips through parseManifest. */
57
+ export declare function serializeManifest(m: AppManifest): string;
58
+ export declare function manifestPath(root?: string): string;
59
+ /** Read + parse the manifest. A missing file names `synap app init`; a broken one names the field. */
60
+ export declare function readManifest(file?: string): AppManifest;
61
+ export declare function writeManifest(file: string, m: AppManifest): void;
62
+ export interface DeployEnvValues {
63
+ apiKey: string;
64
+ podUrl: string;
65
+ workspaceId?: string;
66
+ }
67
+ /**
68
+ * Map the minted values onto the env vars the manifest declares. Pure: the
69
+ * single place that decides which var receives which value. Only declared vars
70
+ * are written; a declared `workspaceId` with no resolved id is a visible error
71
+ * (never a silent half-write).
72
+ */
73
+ export declare function buildEnvAssignments(deploy: DeploySpec, values: DeployEnvValues): Array<{
74
+ name: string;
75
+ value: string;
76
+ }>;
77
+ /** Quote an env value only when it would otherwise be mis-read (whitespace, #, quotes). */
78
+ export declare function quoteEnvValue(value: string): string;
79
+ /**
80
+ * Apply assignments to `.env.local` content, pure. An existing `NAME=…` line
81
+ * (optionally `export NAME=…`) is replaced in place; otherwise the var is
82
+ * appended. Idempotent: running twice leaves one line per var.
83
+ */
84
+ export declare function applyEnvContent(existing: string, assignments: Array<{
85
+ name: string;
86
+ value: string;
87
+ }>): string;
88
+ /**
89
+ * Write one var into a Vercel project (production) idempotently: remove any
90
+ * existing value first (`vercel env add` refuses a duplicate name), then add.
91
+ */
92
+ export declare function writeVercelEnv(name: string, value: string): void;
93
+ /** Write the assignments into the target the manifest declares. Idempotent. */
94
+ export declare function writeDeployTarget(deploy: DeploySpec, assignments: Array<{
95
+ name: string;
96
+ value: string;
97
+ }>, root?: string): string;
98
+ /** The `apps` record the pod returns. `public_id` is the manifest's `id` (client_id). */
99
+ export interface RegisteredApp {
100
+ id: string;
101
+ public_id: string;
102
+ name: string;
103
+ description?: string | null;
104
+ logo_url?: string | null;
105
+ mode?: string;
106
+ }
107
+ export interface RegisterAppInput {
108
+ name: string;
109
+ description?: string;
110
+ logoUrl?: string | null;
111
+ mode: AppMode;
112
+ }
113
+ export interface ConnectResult {
114
+ proposalId: string;
115
+ reviewUrl: string;
116
+ }
117
+ export interface AppKeyResult {
118
+ apiKey: string;
119
+ keyId?: string;
120
+ }
121
+ /**
122
+ * The App Connect Hub routes. A thin subclass of `HubRestClient` — it reuses
123
+ * the shared request loop (retries, timeouts, Idempotency-Key) through the
124
+ * protected `request()` door rather than hand-rolling fetch.
125
+ */
126
+ export declare class AppConnectClient extends HubRestClient {
127
+ /** POST /api/hub/apps — register/upsert, idempotent by name+owner. Returns the app. */
128
+ registerApp(app: RegisterAppInput): Promise<RegisteredApp>;
129
+ /** POST /api/hub/apps/:id/connect — files ONE proposal naming the app and its reach. */
130
+ connectApp(publicId: string, requests: AppRequest[]): Promise<ConnectResult>;
131
+ /** POST /api/hub/apps/:id/key — after approval; mints (rotates) the app key, plaintext ONCE. */
132
+ mintAppKey(publicId: string): Promise<AppKeyResult>;
133
+ }
134
+ /**
135
+ * Write `synap.app.json` at the repo root and register the app on the pod,
136
+ * writing the pod-assigned id back into the file.
137
+ */
138
+ export declare function appInit(): Promise<void>;
139
+ /** File the proposal, print the review URL, wait for approval, mint + deploy the key. */
140
+ export declare function appConnect(): Promise<void>;
141
+ /** Entry points used by the commander registration — surface pod failures through the one door. */
142
+ export declare function appInitCommand(): Promise<void>;
143
+ export declare function appConnectCommand(): Promise<void>;
@@ -0,0 +1,523 @@
1
+ /**
2
+ * `synap app init` / `synap app connect` — the App Connect flow (APP-CONNECT-SPEC §1/§5).
3
+ *
4
+ * An app repo carries a `synap.app.json` manifest at its root. `init` writes it
5
+ * (asks name, pod, requests) and registers the app on the pod, writing the pod's
6
+ * assigned `public_id` back into the file. `connect` files ONE proposal naming
7
+ * the app and its requested reach, waits for the human to approve it, mints the
8
+ * app's key, and drops it into the deploy target declared by `deploy`.
9
+ *
10
+ * Naming (locked by the spec): the object is an Application, the authorization a
11
+ * grant, the bearer an API key. Never "connection".
12
+ *
13
+ * Auth: the SAME pod key every other command uses (`ensurePodKey` / `login()`).
14
+ * The manifest names its pod, so we first look for a saved profile for THAT pod
15
+ * (the rule `ensurePodKey` uses), then fall back to the active pod's key when it
16
+ * is the same pod. No second auth mechanism is invented.
17
+ */
18
+ import { execFileSync } from "node:child_process";
19
+ import fs from "node:fs";
20
+ import { writeSecretFile } from "../lib/atomic-file.js";
21
+ import path from "node:path";
22
+ import * as readline from "node:readline/promises";
23
+ import { stdin as input, stdout as output } from "node:process";
24
+ import chalk from "chalk";
25
+ import { HubRestClient } from "@synap-core/hub-rest-client";
26
+ import { log } from "../utils/logger.js";
27
+ import { renderHubError, resolveHubConfig } from "../lib/hub-client.js";
28
+ import { pollForApproval } from "../lib/approval-poll.js";
29
+ import { listPodProfiles } from "../lib/pod.js";
30
+ import { samePodOrigin } from "../lib/project-ref.js";
31
+ import { classifyProposalStatus, statusOf } from "./proposals-await.js";
32
+ // ─── Manifest (APP-CONNECT-SPEC §1) ────────────────────────────────────────────
33
+ export const MANIFEST_FILENAME = "synap.app.json";
34
+ function optionalString(v) {
35
+ return typeof v === "string" && v.trim() ? v.trim() : undefined;
36
+ }
37
+ /**
38
+ * Parse + validate a manifest. Every failure names the field and the fix —
39
+ * a malformed manifest is a visible error, never a silent default.
40
+ */
41
+ export function parseManifest(raw) {
42
+ let obj;
43
+ try {
44
+ obj = JSON.parse(raw);
45
+ }
46
+ catch (err) {
47
+ throw new Error(`${MANIFEST_FILENAME} is not valid JSON: ${err.message}`);
48
+ }
49
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
50
+ throw new Error(`${MANIFEST_FILENAME} must be a JSON object.`);
51
+ }
52
+ const m = obj;
53
+ if (m.version !== 1) {
54
+ throw new Error(`Unsupported ${MANIFEST_FILENAME} version ${JSON.stringify(m.version)} — expected 1.`);
55
+ }
56
+ const name = optionalString(m.name);
57
+ if (!name)
58
+ throw new Error(`${MANIFEST_FILENAME} is missing a "name".`);
59
+ if (m.mode !== undefined && m.mode !== "specific") {
60
+ throw new Error(`Unsupported mode ${JSON.stringify(m.mode)} — v1 supports only "specific".`);
61
+ }
62
+ const pod = optionalString(m.pod);
63
+ if (!pod)
64
+ throw new Error(`${MANIFEST_FILENAME} is missing a "pod" URL.`);
65
+ try {
66
+ new URL(pod);
67
+ }
68
+ catch {
69
+ throw new Error(`${MANIFEST_FILENAME} "pod" is not a valid URL: ${pod}`);
70
+ }
71
+ if (!Array.isArray(m.requests) || m.requests.length === 0) {
72
+ throw new Error(`${MANIFEST_FILENAME} must list at least one entry in "requests".`);
73
+ }
74
+ const requests = m.requests.map((r, i) => {
75
+ if (!r || typeof r !== "object" || Array.isArray(r)) {
76
+ throw new Error(`requests[${i}] must be an object like { "permission": …, "workspace": … }.`);
77
+ }
78
+ const rr = r;
79
+ const permission = optionalString(rr.permission);
80
+ if (!permission)
81
+ throw new Error(`requests[${i}].permission is required.`);
82
+ const workspace = optionalString(rr.workspace);
83
+ if (!workspace)
84
+ throw new Error(`requests[${i}].workspace is required (a workspace name).`);
85
+ return { permission, workspace };
86
+ });
87
+ const deployRaw = m.deploy;
88
+ if (!deployRaw || typeof deployRaw !== "object" || Array.isArray(deployRaw)) {
89
+ throw new Error(`${MANIFEST_FILENAME} is missing a "deploy" block.`);
90
+ }
91
+ const d = deployRaw;
92
+ if (d.target !== "vercel" && d.target !== "env") {
93
+ throw new Error(`deploy.target must be "vercel" or "env" (got ${JSON.stringify(d.target)}).`);
94
+ }
95
+ const envRaw = d.env;
96
+ if (!envRaw || typeof envRaw !== "object" || Array.isArray(envRaw)) {
97
+ throw new Error(`deploy.env is required (which var receives the key, pod URL, workspace id).`);
98
+ }
99
+ const e = envRaw;
100
+ const env = {
101
+ key: optionalString(e.key),
102
+ podUrl: optionalString(e.podUrl),
103
+ workspaceId: optionalString(e.workspaceId),
104
+ };
105
+ if (!env.key) {
106
+ throw new Error(`deploy.env.key is required — the env var that receives the API key.`);
107
+ }
108
+ return {
109
+ version: 1,
110
+ id: optionalString(m.id),
111
+ name,
112
+ description: optionalString(m.description),
113
+ logoUrl: optionalString(m.logoUrl) ?? null,
114
+ mode: "specific",
115
+ pod,
116
+ requests,
117
+ deploy: { target: d.target, env },
118
+ };
119
+ }
120
+ /** Serialize in the spec's field order, omitting absent optionals. Round-trips through parseManifest. */
121
+ export function serializeManifest(m) {
122
+ const env = {};
123
+ if (m.deploy.env.key)
124
+ env.key = m.deploy.env.key;
125
+ if (m.deploy.env.podUrl)
126
+ env.podUrl = m.deploy.env.podUrl;
127
+ if (m.deploy.env.workspaceId)
128
+ env.workspaceId = m.deploy.env.workspaceId;
129
+ const out = { version: m.version };
130
+ if (m.id)
131
+ out.id = m.id;
132
+ out.name = m.name;
133
+ if (m.description)
134
+ out.description = m.description;
135
+ if (m.logoUrl)
136
+ out.logoUrl = m.logoUrl;
137
+ out.mode = m.mode;
138
+ out.pod = m.pod;
139
+ out.requests = m.requests.map((r) => ({ permission: r.permission, workspace: r.workspace }));
140
+ out.deploy = { target: m.deploy.target, env };
141
+ return JSON.stringify(out, null, 2) + "\n";
142
+ }
143
+ export function manifestPath(root = process.cwd()) {
144
+ return path.join(root, MANIFEST_FILENAME);
145
+ }
146
+ /** Read + parse the manifest. A missing file names `synap app init`; a broken one names the field. */
147
+ export function readManifest(file = manifestPath()) {
148
+ let raw;
149
+ try {
150
+ raw = fs.readFileSync(file, "utf-8");
151
+ }
152
+ catch (err) {
153
+ if (err.code === "ENOENT") {
154
+ throw new Error(`No ${MANIFEST_FILENAME} at ${file}. Create it with: synap app init`);
155
+ }
156
+ throw new Error(`Could not read ${file}: ${err.message}`);
157
+ }
158
+ return parseManifest(raw);
159
+ }
160
+ export function writeManifest(file, m) {
161
+ writeSecretFile(file, serializeManifest(m), { mode: 0o600 });
162
+ }
163
+ /**
164
+ * Map the minted values onto the env vars the manifest declares. Pure: the
165
+ * single place that decides which var receives which value. Only declared vars
166
+ * are written; a declared `workspaceId` with no resolved id is a visible error
167
+ * (never a silent half-write).
168
+ */
169
+ export function buildEnvAssignments(deploy, values) {
170
+ const out = [];
171
+ if (deploy.env.key)
172
+ out.push({ name: deploy.env.key, value: values.apiKey });
173
+ if (deploy.env.podUrl)
174
+ out.push({ name: deploy.env.podUrl, value: values.podUrl });
175
+ if (deploy.env.workspaceId) {
176
+ if (!values.workspaceId) {
177
+ throw new Error(`deploy.env.workspaceId is declared ("${deploy.env.workspaceId}") but no workspace id was resolved.`);
178
+ }
179
+ out.push({ name: deploy.env.workspaceId, value: values.workspaceId });
180
+ }
181
+ return out;
182
+ }
183
+ /** Quote an env value only when it would otherwise be mis-read (whitespace, #, quotes). */
184
+ export function quoteEnvValue(value) {
185
+ if (/[\s#"']/.test(value))
186
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
187
+ return value;
188
+ }
189
+ /**
190
+ * Apply assignments to `.env.local` content, pure. An existing `NAME=…` line
191
+ * (optionally `export NAME=…`) is replaced in place; otherwise the var is
192
+ * appended. Idempotent: running twice leaves one line per var.
193
+ */
194
+ export function applyEnvContent(existing, assignments) {
195
+ const lines = existing.length ? existing.replace(/\r\n/g, "\n").split("\n") : [];
196
+ while (lines.length && lines[lines.length - 1] === "")
197
+ lines.pop();
198
+ for (const { name, value } of assignments) {
199
+ const re = new RegExp(`^\\s*(?:export\\s+)?${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*=`);
200
+ const idx = lines.findIndex((l) => re.test(l));
201
+ const line = `${name}=${quoteEnvValue(value)}`;
202
+ if (idx >= 0)
203
+ lines[idx] = line;
204
+ else
205
+ lines.push(line);
206
+ }
207
+ return lines.length ? lines.join("\n") + "\n" : "";
208
+ }
209
+ /**
210
+ * Write one var into a Vercel project (production) idempotently: remove any
211
+ * existing value first (`vercel env add` refuses a duplicate name), then add.
212
+ */
213
+ export function writeVercelEnv(name, value) {
214
+ // Order is rm → add because `vercel env add` refuses an existing name. A
215
+ // not-found rm is the normal first-run case; any other rm failure is surfaced
216
+ // (the add below then fails loudly if the old var really is still there).
217
+ try {
218
+ execFileSync("vercel", ["env", "rm", name, "production", "-y"], {
219
+ stdio: ["ignore", "pipe", "pipe"],
220
+ });
221
+ }
222
+ catch (err) {
223
+ const stderr = String(err.stderr ?? "");
224
+ if (!/not found/i.test(stderr)) {
225
+ log.warn(`\`vercel env rm ${name} production\` failed: ${stderr.trim() || err.message}`);
226
+ }
227
+ }
228
+ try {
229
+ execFileSync("vercel", ["env", "add", name, "production"], {
230
+ input: value + "\n",
231
+ stdio: ["pipe", "pipe", "pipe"],
232
+ });
233
+ }
234
+ catch (err) {
235
+ throw new Error(`\`vercel env add ${name} production\` failed: ${err.message}\n` +
236
+ `Fix the Vercel CLI (\`vercel login\`, \`vercel link\`), then re-run \`synap app connect\`.`);
237
+ }
238
+ }
239
+ /** Write the assignments into the target the manifest declares. Idempotent. */
240
+ export function writeDeployTarget(deploy, assignments, root = process.cwd()) {
241
+ if (deploy.target === "vercel") {
242
+ for (const { name, value } of assignments)
243
+ writeVercelEnv(name, value);
244
+ return "vercel (production)";
245
+ }
246
+ const envFile = path.join(root, ".env.local");
247
+ let existing = "";
248
+ try {
249
+ existing = fs.readFileSync(envFile, "utf-8");
250
+ }
251
+ catch (err) {
252
+ if (err.code !== "ENOENT") {
253
+ throw new Error(`Could not read ${envFile}: ${err.message}`);
254
+ }
255
+ }
256
+ writeSecretFile(envFile, applyEnvContent(existing, assignments), { mode: 0o600 });
257
+ return path.relative(root, envFile) || ".env.local";
258
+ }
259
+ /**
260
+ * The App Connect Hub routes. A thin subclass of `HubRestClient` — it reuses
261
+ * the shared request loop (retries, timeouts, Idempotency-Key) through the
262
+ * protected `request()` door rather than hand-rolling fetch.
263
+ */
264
+ export class AppConnectClient extends HubRestClient {
265
+ /** POST /api/hub/apps — register/upsert, idempotent by name+owner. Returns the app. */
266
+ async registerApp(app) {
267
+ const res = await this.request("POST", "/api/hub/apps", app);
268
+ if (!res?.app?.public_id) {
269
+ throw new Error("POST /api/hub/apps returned no app.public_id — check the pod logs.");
270
+ }
271
+ return res.app;
272
+ }
273
+ /** POST /api/hub/apps/:id/connect — files ONE proposal naming the app and its reach. */
274
+ async connectApp(publicId, requests) {
275
+ const res = await this.request("POST", `/api/hub/apps/${encodeURIComponent(publicId)}/connect`, { requests });
276
+ if (!res?.proposalId) {
277
+ throw new Error("POST /apps/:id/connect returned no proposalId — check the pod logs.");
278
+ }
279
+ return res;
280
+ }
281
+ /** POST /api/hub/apps/:id/key — after approval; mints (rotates) the app key, plaintext ONCE. */
282
+ async mintAppKey(publicId) {
283
+ const res = await this.request("POST", `/api/hub/apps/${encodeURIComponent(publicId)}/key`, {});
284
+ if (!res?.apiKey) {
285
+ throw new Error("POST /apps/:id/key returned no apiKey — check the pod logs.");
286
+ }
287
+ return { apiKey: res.apiKey, keyId: res.keyId };
288
+ }
289
+ }
290
+ // ─── Auth: the existing pod key, for the manifest's pod ────────────────────────
291
+ /**
292
+ * The user's pod key for the manifest's pod. Same mechanism `ensurePodKey` uses:
293
+ * a saved profile for that exact pod, verified against `/auth/status`; otherwise
294
+ * the active pod's key when it is the same pod. Never invents a credential.
295
+ */
296
+ async function resolveAppCredentials(podUrl) {
297
+ const norm = (u) => u.replace(/\/+$/, "");
298
+ const target = norm(podUrl);
299
+ const saved = listPodProfiles().find((p) => norm(p.config.podUrl) === target);
300
+ if (saved?.config.hubApiKey) {
301
+ const res = await fetch(`${target}/api/hub/auth/status`, {
302
+ headers: { Authorization: `Bearer ${saved.config.hubApiKey}` },
303
+ signal: AbortSignal.timeout(15_000),
304
+ }).catch(() => null);
305
+ if (res?.ok) {
306
+ return { podUrl: target, apiKey: saved.config.hubApiKey, userId: saved.config.agentUserId || "cli" };
307
+ }
308
+ if (res && (res.status === 401 || res.status === 403)) {
309
+ log.warn(`Your saved key for ${target} no longer works.`);
310
+ }
311
+ else {
312
+ // A failed read is not an empty result — surface it.
313
+ throw new Error(`Could not verify your saved key for ${target} (${res ? `HTTP ${res.status}` : "unreachable"}).`);
314
+ }
315
+ }
316
+ // Fall back to the active pod's key when the manifest's pod IS the active pod
317
+ // (covers env-pinned sessions) — still the one auth mechanism.
318
+ const cfg = await resolveHubConfig();
319
+ if (samePodOrigin(cfg.podUrl, target))
320
+ return { ...cfg, podUrl: target };
321
+ throw new Error(`No pod key for ${target}. Add it with: synap pods add ${new URL(target).hostname} ${target}`);
322
+ }
323
+ // ─── shared step: register (and write the id back) ─────────────────────────────
324
+ async function registerIfNeeded(client, manifest, file) {
325
+ if (manifest.id)
326
+ return manifest;
327
+ const app = await client.registerApp({
328
+ name: manifest.name,
329
+ description: manifest.description,
330
+ logoUrl: manifest.logoUrl,
331
+ mode: manifest.mode,
332
+ });
333
+ const updated = { ...manifest, id: app.public_id };
334
+ writeManifest(file, updated);
335
+ log.success(`Registered "${manifest.name}" — id ${app.public_id} written to ${MANIFEST_FILENAME}`);
336
+ return updated;
337
+ }
338
+ // ─── prompt helpers (node:readline — @clack/prompts hangs on this pod) ─────────
339
+ async function ask(rl, question, def = "") {
340
+ const suffix = def ? chalk.dim(` [${def}]`) : "";
341
+ const answer = (await rl.question(` ${question}${suffix}: `)).trim();
342
+ return answer || def;
343
+ }
344
+ // ─── `synap app init` ──────────────────────────────────────────────────────────
345
+ /**
346
+ * Write `synap.app.json` at the repo root and register the app on the pod,
347
+ * writing the pod-assigned id back into the file.
348
+ */
349
+ export async function appInit() {
350
+ if (!process.stdin.isTTY) {
351
+ throw new Error("`synap app init` needs an interactive terminal (it asks for your app's name, pod, and permissions).\n" +
352
+ `For a non-interactive setup, write ${MANIFEST_FILENAME} by hand and run: synap app connect`);
353
+ }
354
+ const root = process.cwd();
355
+ const file = manifestPath(root);
356
+ let existing;
357
+ if (fs.existsSync(file)) {
358
+ existing = readManifest(file);
359
+ log.info(`Updating the existing ${MANIFEST_FILENAME}.`);
360
+ }
361
+ // A default pod URL for the prompt only. resolveHubConfig is entirely local
362
+ // (no network); its ONLY failure here is "No pod configured" — a legitimate
363
+ // state for a first app, so the prompt simply starts empty.
364
+ const cfg = await resolveHubConfig().catch(() => undefined);
365
+ const rl = readline.createInterface({ input, output });
366
+ let manifest;
367
+ try {
368
+ const name = await ask(rl, "App name", existing?.name ?? path.basename(root));
369
+ if (!name)
370
+ throw new Error("An app name is required.");
371
+ const pod = await ask(rl, "Pod URL", existing?.pod ?? cfg?.podUrl ?? "");
372
+ if (!pod)
373
+ throw new Error("A pod URL is required.");
374
+ try {
375
+ new URL(pod);
376
+ }
377
+ catch {
378
+ throw new Error(`That pod URL is not valid: ${pod}`);
379
+ }
380
+ log.blank();
381
+ log.info("What should this app be allowed to do?");
382
+ log.dim('Grant grammar: entity.<qualifier>.<action>, entity.*.create, or "*".');
383
+ const requests = [];
384
+ for (let i = 0; i < 20; i++) {
385
+ const def = existing?.requests[i];
386
+ const permission = await ask(rl, i === 0 ? "Requested permission" : "Another permission (leave blank to finish)", def?.permission ?? "");
387
+ if (!permission)
388
+ break;
389
+ const workspace = await ask(rl, `Workspace name for "${permission}"`, def?.workspace ?? "");
390
+ if (!workspace)
391
+ throw new Error(`A workspace name is required for "${permission}".`);
392
+ requests.push({ permission, workspace });
393
+ }
394
+ if (requests.length === 0)
395
+ throw new Error("At least one request is required.");
396
+ manifest = {
397
+ version: 1,
398
+ id: existing?.id,
399
+ name,
400
+ description: existing?.description,
401
+ logoUrl: existing?.logoUrl ?? null,
402
+ mode: "specific",
403
+ pod,
404
+ requests,
405
+ deploy: existing?.deploy ?? {
406
+ target: "env",
407
+ env: { key: "SYNAP_API_KEY", podUrl: "SYNAP_POD_URL", workspaceId: "SYNAP_WORKSPACE_ID" },
408
+ },
409
+ };
410
+ }
411
+ finally {
412
+ rl.close();
413
+ }
414
+ // Write first (so nothing is lost if the pod is unreachable), then register
415
+ // and rewrite with the assigned id.
416
+ writeManifest(file, manifest);
417
+ log.success(`Wrote ${MANIFEST_FILENAME}`);
418
+ log.dim(`Edit deploy (target + env var names) in ${MANIFEST_FILENAME} before connecting.`);
419
+ const creds = await resolveAppCredentials(manifest.pod);
420
+ const client = new AppConnectClient({ podUrl: creds.podUrl, apiKey: creds.apiKey });
421
+ const registered = await registerIfNeeded(client, manifest, file);
422
+ log.blank();
423
+ log.success(`App "${registered.name}" is registered (${registered.id}).`);
424
+ log.info("Next: " + chalk.cyan("synap app connect") + " to request access and receive the key.");
425
+ }
426
+ // ─── `synap app connect` ───────────────────────────────────────────────────────
427
+ async function resolveWorkspaceIdFor(client, requests) {
428
+ const names = [...new Set(requests.map((r) => r.workspace.trim()))];
429
+ const workspaces = await client.getWorkspaces();
430
+ const byName = new Map(workspaces.map((w) => [w.name.trim().toLowerCase(), w.id]));
431
+ const missing = names.filter((n) => !byName.get(n.toLowerCase()));
432
+ if (missing.length) {
433
+ throw new Error(`Workspace not found on this pod: ${missing.join(", ")}. ` +
434
+ `Check the name in ${MANIFEST_FILENAME} (run \`synap orient\` to list workspaces).`);
435
+ }
436
+ if (names.length > 1) {
437
+ log.warn(`This app requests ${names.length} workspaces — writing the first ("${names[0]}") to the workspace-id env var.`);
438
+ }
439
+ return byName.get(names[0].toLowerCase());
440
+ }
441
+ /** File the proposal, print the review URL, wait for approval, mint + deploy the key. */
442
+ export async function appConnect() {
443
+ const root = process.cwd();
444
+ const file = manifestPath(root);
445
+ let manifest = readManifest(file);
446
+ const creds = await resolveAppCredentials(manifest.pod);
447
+ const client = new AppConnectClient({ podUrl: creds.podUrl, apiKey: creds.apiKey });
448
+ // Register first if the manifest has no id yet (spec §5).
449
+ manifest = await registerIfNeeded(client, manifest, file);
450
+ // ── 1. File ONE proposal naming the app and its requested reach ─────────────
451
+ const { proposalId, reviewUrl } = await client.connectApp(manifest.id, manifest.requests);
452
+ log.blank();
453
+ log.heading(`Access request filed for "${manifest.name}"`);
454
+ log.info("Approve it on your phone: " + chalk.cyan(reviewUrl));
455
+ log.dim(`Proposal ${proposalId}`);
456
+ // ── 2. Poll until the human approves ────────────────────────────────────────
457
+ process.stdout.write(chalk.dim(" Waiting for approval"));
458
+ const ticker = setInterval(() => process.stdout.write(chalk.dim(".")), 2000);
459
+ try {
460
+ await pollForApproval({
461
+ url: `${creds.podUrl.replace(/\/+$/, "")}/api/hub/proposals/${proposalId}`,
462
+ headers: { Authorization: `Bearer ${creds.apiKey}` },
463
+ isApproved: (d) => classifyProposalStatus(statusOf(d)) === "approved",
464
+ isRejected: (d) => classifyProposalStatus(statusOf(d)) === "rejected",
465
+ onApproved: (d) => d,
466
+ // `classifyProposalStatus` maps rejected / expired / withdrawn /
467
+ // approval_failed to one "rejected" — say "not approved", never assert a
468
+ // rejection we cannot prove.
469
+ rejectedError: `The access request for "${manifest.name}" was not approved (rejected, expired or withdrawn). Re-run \`synap app connect\` to ask again.`,
470
+ // A timeout is NOT proof the human declined: the pod may have been
471
+ // unreachable while polling. Name both causes.
472
+ timeoutError: `Timed out waiting for approval (120s). If the pod was unreachable, check your connection; otherwise open ${reviewUrl} and approve, then re-run \`synap app connect\`.`,
473
+ });
474
+ }
475
+ finally {
476
+ clearInterval(ticker);
477
+ process.stdout.write("\n");
478
+ }
479
+ log.success("Approved.");
480
+ // ── 3. Resolve + validate everything fallible BEFORE minting — a throw after
481
+ // the mint would lose the key and a re-run would mint an orphan. ────────
482
+ const workspaceId = manifest.deploy.env.workspaceId
483
+ ? await resolveWorkspaceIdFor(client, manifest.requests)
484
+ : undefined;
485
+ buildEnvAssignments(manifest.deploy, { apiKey: "", podUrl: creds.podUrl, workspaceId });
486
+ // ── 4. Mint the app key (rotates any existing — `connect` is re-runnable) ────
487
+ const { apiKey } = await client.mintAppKey(manifest.id);
488
+ // ── 5. Deploy: write the declared env vars into the target ──────────────────
489
+ const assignments = buildEnvAssignments(manifest.deploy, {
490
+ apiKey,
491
+ podUrl: creds.podUrl,
492
+ workspaceId,
493
+ });
494
+ const where = writeDeployTarget(manifest.deploy, assignments, root);
495
+ log.blank();
496
+ log.success(`Key written to ${where}`);
497
+ for (const { name } of assignments)
498
+ log.dim(`${name}=…`);
499
+ log.dim("See or revoke this app in Pod Admin → My connections.");
500
+ log.dim(manifest.deploy.target === "vercel"
501
+ ? "Deploy with `vercel --prod` to pick up the new env vars."
502
+ : "Restart your app (or `vercel --prod`) to pick up the new env vars.");
503
+ }
504
+ /** Entry points used by the commander registration — surface pod failures through the one door. */
505
+ export async function appInitCommand() {
506
+ try {
507
+ await appInit();
508
+ }
509
+ catch (err) {
510
+ renderHubError(err);
511
+ process.exit(1);
512
+ }
513
+ }
514
+ export async function appConnectCommand() {
515
+ try {
516
+ await appConnect();
517
+ }
518
+ catch (err) {
519
+ renderHubError(err);
520
+ process.exit(1);
521
+ }
522
+ }
523
+ //# sourceMappingURL=app.js.map