@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
@@ -8,22 +8,216 @@
8
8
  * (b) the key authenticates against that pod.
9
9
  * (c) the configured workspace (and project) actually exist on THIS pod —
10
10
  * via `GET /orient`, which is the pod's own answer to "what's valid here".
11
- * (d) prints the active lens (pod, workspace, project) so the user sees
11
+ * (d) the Intelligence Service is reachable — the dependency that capture,
12
+ * import and all AI structuring run through. Without it doctor once
13
+ * printed "All checks passed" while every import was 502-ing.
14
+ * (e) the connection broker answers — and when it does not, WHY: broker
15
+ * kind, relay credential presence + expiry, control-plane issuer trust
16
+ * and the owner's identity link, with the one fix for that reason.
17
+ * (f) prints the active lens (pod, workspace, project) so the user sees
12
18
  * exactly where they're pointed.
13
19
  *
14
- * Degrades gracefully on older pods (missing /orient, no release/status route)
15
- * — a check that can't run says so and moves on, it never throws.
20
+ * Degrades gracefully on older pods (missing /orient, no dependency probe) — a
21
+ * check that can't run says so and moves on, it never throws. Crucially, a
22
+ * check that could not run reports "could not determine" and is NOT green: an
23
+ * unknown is not a pass. Only a PROVEN failure sets a non-zero exit code.
16
24
  */
17
25
  import chalk from "chalk";
18
- import { log, banner } from "../utils/logger.js";
19
- import { getActivePodConfig, listPodProfiles, checkPodHealth } from "../lib/pod.js";
26
+ import { log } from "../utils/logger.js";
27
+ import { getActivePodConfig, getActiveWorkspaceId, getActiveProjectId, listPodProfiles, checkPodHealth } from "../lib/pod.js";
20
28
  import { resolveHubConfig, hubGet, HubError } from "../lib/hub-client.js";
29
+ import { formatKeySource, resolveKeySource } from "../lib/key-source.js";
30
+ import { resolveLensProvenance } from "../lib/describe-lens.js";
31
+ import { samePodOrigin } from "../lib/project-ref.js";
32
+ import { fetchKeyState } from "../lib/key-state.js";
33
+ import { describeKeyState } from "../lib/key-diagnosis.js";
34
+ import { isFixable } from "../lib/key-doctor.js";
35
+ import { connectionLines } from "../lib/agent-connections.js";
36
+ import { thisComputerInstanceIds } from "../lib/machine-instance.js";
37
+ import { tildify } from "../lib/key-locations.js";
38
+ import { cliBuild, formatCliBuild } from "../lib/cli-build.js";
39
+ import os from "node:os";
40
+ import path from "node:path";
41
+ import { unwrapList } from "../lib/unwrapList.js";
42
+ /**
43
+ * Each agent with a live connection, and the computers it is connected from —
44
+ * one line per agent. Agents with none are ONE summary line (OMIT, with the
45
+ * door to the full list). A failed read says so — never an empty list; an older
46
+ * pod that sends no connections is "cannot tell".
47
+ */
48
+ async function printAgentConnections(cfg) {
49
+ let rows;
50
+ try {
51
+ rows = unwrapList(await hubGet("/agent-users?mine=1", {}, cfg));
52
+ }
53
+ catch (err) {
54
+ console.log(` ${chalk.yellow("?")} Could not read your agents' connections: ${err instanceof Error ? err.message : String(err)}`);
55
+ return;
56
+ }
57
+ if (rows.length === 0) {
58
+ log.dim(" No agents on this pod for you yet — connect one: synap connect");
59
+ return;
60
+ }
61
+ const live = [];
62
+ let idle = 0;
63
+ let olderPod = false;
64
+ for (const row of rows) {
65
+ // Keys are minted with the SURFACE's hash: compare against every surface
66
+ // minting under this agentType, and only when the pod named the type.
67
+ const mine = row.agentType ? thisComputerInstanceIds(row.agentType) : null;
68
+ const lines = connectionLines(row.connections, { isThisComputer: (id) => !!mine?.has(id) });
69
+ if (lines === null)
70
+ olderPod = true;
71
+ else if (lines.length === 0)
72
+ idle++;
73
+ else
74
+ live.push({ name: row.name ?? row.agentType ?? "Agent", lines });
75
+ }
76
+ const width = Math.min(18, Math.max(0, ...live.map((r) => r.name.length)) + 2);
77
+ for (const r of live)
78
+ console.log(` ${r.name.padEnd(width)}${r.lines.join(chalk.dim(" · "))}`);
79
+ if (idle)
80
+ console.log(chalk.dim(` +${idle} agent${idle === 1 ? "" : "s"} with no live connection — synap agents list`));
81
+ if (olderPod)
82
+ console.log(chalk.dim(" (this pod predates per-computer connections — update it to see them)"));
83
+ }
84
+ /**
85
+ * Read the Intelligence-Service entry out of `GET /api/hub/health/dependencies`.
86
+ *
87
+ * Keys off the pod's real contract: `{ dependencies: [{ name:
88
+ * "intelligence-service", state: "reachable"|"unreachable"|"unresolved",
89
+ * reachable: boolean, reason?, endpoint?, httpStatus?, latencyMs? }] }`.
90
+ *
91
+ * Anything this cannot positively recognise degrades to `unknown` — the CLI
92
+ * ships independently of the pod, so a future field rename must surface as
93
+ * "could not determine", never as a silent green.
94
+ */
95
+ /**
96
+ * Describe which lens the pod resolved the IS under, from its `resolution`
97
+ * echo. Silent when the pod did not say — never guess a lens.
98
+ */
99
+ function readLens(resolution) {
100
+ if (!resolution || typeof resolution !== "object")
101
+ return "";
102
+ const ws = resolution.workspaceId;
103
+ if (typeof ws === "string" && ws)
104
+ return ` (checked as workspace ${ws})`;
105
+ if (ws === null)
106
+ return " (checked pod-level)";
107
+ return "";
108
+ }
109
+ export function readIntelligenceHealth(payload) {
110
+ if (!payload || typeof payload !== "object") {
111
+ return { state: "unknown", detail: "the pod returned no dependency payload" };
112
+ }
113
+ const root = payload;
114
+ const deps = root.dependencies;
115
+ if (!Array.isArray(deps)) {
116
+ return { state: "unknown", detail: "the dependency report carried no `dependencies` array" };
117
+ }
118
+ // The pod echoes back the lens it actually resolved under. Report it: a
119
+ // pod-level "reachable" does not prove the workspace the user imports into
120
+ // routes to a live service, and vice versa.
121
+ const lens = readLens(root.resolution);
122
+ const entry = deps.find((d) => Boolean(d) && typeof d === "object" && d.name === "intelligence-service");
123
+ if (!entry) {
124
+ return {
125
+ state: "unknown",
126
+ detail: "the pod's dependency report listed no intelligence-service entry",
127
+ };
128
+ }
129
+ const reason = typeof entry.reason === "string" ? entry.reason : undefined;
130
+ const where = typeof entry.endpoint === "string" ? entry.endpoint : undefined;
131
+ const suffix = reason ? ` — ${reason}` : "";
132
+ switch (entry.state) {
133
+ case "reachable": {
134
+ const ms = typeof entry.latencyMs === "number" ? ` in ${entry.latencyMs}ms` : "";
135
+ return {
136
+ state: "up",
137
+ detail: `intelligence-service answered${where ? ` at ${where}` : ""}${ms}${lens}`,
138
+ };
139
+ }
140
+ case "unreachable":
141
+ return {
142
+ state: "down",
143
+ detail: `intelligence-service${where ? ` at ${where}` : ""} did not answer${lens}${suffix}`,
144
+ };
145
+ case "unresolved":
146
+ // Determinate, not undetermined: the pod positively told us resolution
147
+ // failed, and a real capture hits that same failure. It is a proven
148
+ // break with a different CAUSE than "the service is down".
149
+ return {
150
+ state: "unresolved",
151
+ detail: `the pod could not determine which Intelligence Service it routes to${lens}${suffix}`,
152
+ };
153
+ default:
154
+ // No readable `state`. Fall back to the boolean mirror ONLY when it is a
155
+ // real boolean — never treat a missing field as healthy.
156
+ if (entry.reachable === true)
157
+ return { state: "up", detail: "intelligence-service answered" };
158
+ if (entry.reachable === false) {
159
+ return { state: "down", detail: `intelligence-service did not answer${suffix}` };
160
+ }
161
+ return { state: "unknown", detail: "the intelligence-service entry carried no readable state" };
162
+ }
163
+ }
164
+ const IS_CHECK_NAME = "Intelligence Service reachable";
165
+ /**
166
+ * Turn an IS verdict into a check row. Names the CONSEQUENCE, not just the
167
+ * dependency: the incident this fixes had a green doctor while every capture /
168
+ * import was 502-ing, so "IS unreachable" alone does not tell the user that the
169
+ * command they are about to run cannot work.
170
+ */
171
+ export function intelligenceCheck(verdict) {
172
+ const consequence = "capture / import / AI structuring WILL FAIL (files import as 0 entities, reported degraded)";
173
+ switch (verdict.state) {
174
+ case "up":
175
+ return { name: IS_CHECK_NAME, ok: true, detail: verdict.detail };
176
+ case "down":
177
+ return {
178
+ name: IS_CHECK_NAME,
179
+ ok: false,
180
+ detail: `${verdict.detail} — ${consequence}`,
181
+ fix: "The pod is up but its Intelligence Service is not. Restart/redeploy the IS, then re-run: synap doctor",
182
+ };
183
+ case "unresolved":
184
+ return {
185
+ name: IS_CHECK_NAME,
186
+ ok: false,
187
+ detail: `${verdict.detail} — ${consequence}`,
188
+ // A different fault from "down": nothing to restart, the routing itself
189
+ // is empty/misconfigured, so the fix points at configuration.
190
+ fix: "No Intelligence Service is configured for this pod/workspace. Check the pod's intelligence-service registry, then re-run: synap doctor",
191
+ };
192
+ default:
193
+ return {
194
+ name: IS_CHECK_NAME,
195
+ ok: false,
196
+ unknown: true,
197
+ detail: `could not determine — ${verdict.detail}. If it is down, ${consequence}.`,
198
+ // `import --dry-run` calls POST /capture/structure and stops before
199
+ // /capture/execute — it exercises the exact door that 502s when the IS
200
+ // is down, and writes nothing. (`capture` has no --dry-run.)
201
+ fix: "This pod build may predate GET /api/hub/health/dependencies. Probe by hand (writes nothing): synap import <a-file.md> --dry-run",
202
+ };
203
+ }
204
+ }
21
205
  export async function doctor(opts = {}) {
22
206
  const checks = [];
23
207
  const activeName = listPodProfiles().find((p) => p.active)?.name ?? "default";
208
+ // Resolve lens FIRST so `--pod` / env / profile agree on URL + workspace
209
+ // (host check used to use only active profile → `synap --pod team doctor`
210
+ // reported perso's host while Active Lens showed team).
211
+ let cfgResolved;
212
+ try {
213
+ cfgResolved = await resolveHubConfig();
214
+ }
215
+ catch {
216
+ /* fall through to profile/env */
217
+ }
24
218
  // ── (a) Host resolves — the pod is actually served at this URL ───────────
25
219
  const localConfig = getActivePodConfig();
26
- const podUrl = localConfig?.podUrl ?? process.env.SYNAP_POD_URL;
220
+ const podUrl = cfgResolved?.podUrl ?? localConfig?.podUrl ?? process.env.SYNAP_POD_URL;
27
221
  if (!podUrl) {
28
222
  checks.push({
29
223
  name: "Pod URL configured",
@@ -34,7 +228,9 @@ export async function doctor(opts = {}) {
34
228
  return finish(checks, opts, { podUrl: undefined, workspaceId: undefined, projectId: undefined });
35
229
  }
36
230
  let hostServed = false;
231
+ const names = {};
37
232
  const health = await checkPodHealth(podUrl);
233
+ names.podVersion = health.version;
38
234
  if (health.healthy) {
39
235
  hostServed = true;
40
236
  checks.push({
@@ -56,7 +252,6 @@ export async function doctor(opts = {}) {
56
252
  }
57
253
  // ── (b) Key authenticates against this pod ────────────────────────────────
58
254
  let keyValid = false;
59
- let cfgResolved;
60
255
  if (!hostServed) {
61
256
  checks.push({
62
257
  name: "API key authenticates",
@@ -67,7 +262,7 @@ export async function doctor(opts = {}) {
67
262
  }
68
263
  else {
69
264
  try {
70
- const cfg = await resolveHubConfig();
265
+ const cfg = cfgResolved ?? (await resolveHubConfig());
71
266
  cfgResolved = cfg;
72
267
  await hubGet("/users/me", {}, cfg);
73
268
  keyValid = true;
@@ -75,11 +270,13 @@ export async function doctor(opts = {}) {
75
270
  }
76
271
  catch (err) {
77
272
  const is401 = err instanceof HubError ? err.status === 401 : false;
273
+ // Say WHY — awaiting approval, replaced, expired — never a guess.
274
+ const why = is401 && cfgResolved ? describeKeyState(await fetchKeyState(cfgResolved.podUrl, cfgResolved.apiKey)) : null;
78
275
  checks.push({
79
276
  name: "API key authenticates",
80
277
  ok: false,
81
- detail: is401 ? "the pod rejected this key (expired or revoked)" : `could not verify (${err instanceof Error ? err.message : String(err)})`,
82
- fix: `Run: synap login --reconnect ${activeName}`,
278
+ detail: is401 ? `the pod rejected this key: it ${why}` : `could not verify (${err instanceof Error ? err.message : String(err)})`,
279
+ fix: is401 ? "See the key health below — run: synap doctor --fix" : `Run: synap login --reconnect ${activeName}`,
83
280
  });
84
281
  }
85
282
  }
@@ -102,6 +299,7 @@ export async function doctor(opts = {}) {
102
299
  if (cfgResolved.workspaceId) {
103
300
  const match = workspaces.find((w) => w.id === cfgResolved.workspaceId);
104
301
  workspaceOk = Boolean(match);
302
+ names.workspace = match?.name;
105
303
  checks.push({
106
304
  name: "Configured workspace exists on this pod",
107
305
  ok: workspaceOk,
@@ -121,6 +319,7 @@ export async function doctor(opts = {}) {
121
319
  if (cfgResolved.projectId) {
122
320
  const match = projects.find((p) => p.id === cfgResolved.projectId);
123
321
  projectOk = Boolean(match);
322
+ names.project = match?.name;
124
323
  checks.push({
125
324
  name: "Configured project exists on this pod",
126
325
  ok: projectOk,
@@ -143,47 +342,473 @@ export async function doctor(opts = {}) {
143
342
  });
144
343
  }
145
344
  }
345
+ // ── (d) The Intelligence Service the capture/import pipeline depends on ───
346
+ // Doctor used to stop at config coherence and print "All checks passed"
347
+ // while POST /api/hub/capture/structure was 502-ing — a diagnostic that
348
+ // reports all-clear over a dead core dependency actively misdirects.
349
+ if (!keyValid || !cfgResolved) {
350
+ checks.push({
351
+ name: "Intelligence Service reachable",
352
+ ok: false,
353
+ unknown: true,
354
+ detail: "could not determine — the key did not authenticate, so the pod's dependency probe was not called",
355
+ fix: "Fix the API key check above first.",
356
+ });
357
+ }
358
+ else {
359
+ checks.push(intelligenceCheck(await probeIntelligence(cfgResolved)));
360
+ }
361
+ // ── (e) The connection broker, and why it fails when it does ─────────────
362
+ if (!keyValid || !cfgResolved) {
363
+ checks.push({
364
+ name: BROKER_CHECK_NAME,
365
+ ok: false,
366
+ unknown: true,
367
+ detail: "could not determine — the key did not authenticate",
368
+ fix: "Fix the API key check above first.",
369
+ });
370
+ }
371
+ else {
372
+ const [providers, diagnostics] = await Promise.all([
373
+ probeBrokerProviders(cfgResolved),
374
+ probeBrokerDiagnostics(cfgResolved),
375
+ ]);
376
+ checks.push(brokerCheck(providers, diagnostics));
377
+ }
378
+ // ── (f) Every key configured for this pod, and why each one fails ───────
379
+ let keyHealth;
380
+ if (hostServed) {
381
+ const { checkKeyHealth, keyProblems } = await import("../lib/key-doctor.js");
382
+ keyHealth = await checkKeyHealth(cfgResolved?.podUrl ?? podUrl);
383
+ const problems = keyProblems(keyHealth);
384
+ const failing = problems.filter((d) => d.severity === "error");
385
+ const unverifiable = problems.filter((d) => d.code === "unverifiable");
386
+ const why = [...new Set(unverifiable.map((d) => (d.key && keyHealth.states.get(d.key)?.error) || "no answer"))].join("; ");
387
+ checks.push({
388
+ name: "Keys on this computer",
389
+ ok: problems.length === 0,
390
+ unknown: problems.length > 0 && failing.length === 0 && problems.every((d) => d.code === "unverifiable"),
391
+ detail: problems.length === 0
392
+ ? `${keyHealth.locations.length} configured cop${keyHealth.locations.length === 1 ? "y" : "ies"}, all working`
393
+ : unverifiable.length === problems.length
394
+ ? `${unverifiable.length} key${unverifiable.length === 1 ? "" : "s"} could not be checked (${why})`
395
+ : `${problems.length} problem${problems.length === 1 ? "" : "s"}`,
396
+ // Only a problem --fix can act on earns the --fix hint.
397
+ fix: problems.some(isFixable) ? "Run: synap doctor --fix" : undefined,
398
+ });
399
+ }
146
400
  await finish(checks, opts, {
147
- podUrl,
401
+ // Prefer the pod resolveHubConfig actually hit — health-check URL can lag
402
+ // the active profile when the shell is env-pinned to another pod.
403
+ podUrl: cfgResolved?.podUrl ?? podUrl,
148
404
  workspaceId: cfgResolved?.workspaceId,
149
405
  projectId: cfgResolved?.projectId,
150
- });
406
+ ...names,
407
+ }, keyHealth, cfgResolved);
151
408
  }
152
- async function finish(checks, opts, lens) {
409
+ /**
410
+ * Call the pod's dependency probe. Every failure mode that is not a positive
411
+ * "the IS is down" answer maps to `unknown` — an older pod that 404s the door
412
+ * must never read as healthy.
413
+ */
414
+ async function probeIntelligence(cfg) {
415
+ try {
416
+ // Pass the configured workspace so the pod resolves the IS exactly as a
417
+ // capture FROM THAT WORKSPACE would (resolution is capability → workspace →
418
+ // user → pod default). A pod-level probe could pass while the workspace the
419
+ // user actually imports into routes to a dead service.
420
+ return readIntelligenceHealth(await hubGet("/health/dependencies", { workspaceId: cfg.workspaceId }, cfg));
421
+ }
422
+ catch (err) {
423
+ if (err instanceof HubError && err.status === 404) {
424
+ return {
425
+ state: "unknown",
426
+ detail: "this pod does not expose GET /api/hub/health/dependencies (older build)",
427
+ };
428
+ }
429
+ if (err instanceof HubError && err.status === 403) {
430
+ return {
431
+ state: "unknown",
432
+ detail: "this key lacks the hub-protocol.read scope needed to read the dependency probe",
433
+ };
434
+ }
435
+ // The probe itself failing is not proof the IS is down — say only that.
436
+ return {
437
+ state: "unknown",
438
+ detail: `the dependency probe did not answer (${err instanceof Error ? err.message : String(err)})`,
439
+ };
440
+ }
441
+ }
442
+ const BROKER_CHECK_NAME = "Connection broker";
443
+ export function readProvidersEnvelope(payload) {
444
+ if (!payload || typeof payload !== "object") {
445
+ return { state: "unknown", detail: "the providers door returned no payload" };
446
+ }
447
+ const p = payload;
448
+ if (p.nangoStatus === "ok")
449
+ return { state: "ok" };
450
+ if (p.nangoStatus === "error") {
451
+ return {
452
+ state: "error",
453
+ reason: typeof p.nangoError?.reason === "string" ? p.nangoError.reason : "unknown",
454
+ message: typeof p.nangoError?.message === "string" ? p.nangoError.message : undefined,
455
+ };
456
+ }
457
+ return { state: "unknown", detail: "the providers door carried no readable nangoStatus" };
458
+ }
459
+ export function readBrokerDiagnostics(payload) {
460
+ const r = payload;
461
+ const readable = !!r &&
462
+ typeof r.cpIssuer?.present === "boolean" &&
463
+ typeof r.cpIssuer?.hasSourceConfigWrite === "boolean" &&
464
+ typeof r.ownerIdentityLink?.present === "boolean" &&
465
+ typeof r.relayCredential?.present === "boolean" &&
466
+ typeof r.broker?.kind === "string";
467
+ if (!readable) {
468
+ return { state: "unknown", detail: "the broker diagnostics payload was not readable" };
469
+ }
470
+ return { state: "read", report: r };
471
+ }
472
+ /** The one fix for a broker fault, sharpened by the trust diagnostics when readable. */
473
+ export function brokerFixHint(reason, diagnostics, message) {
474
+ if (reason === "broker-credential-missing") {
475
+ if (diagnostics.state !== "read") {
476
+ return "The pod holds no usable relay key from its control plane. Its trust state is unreadable here (needs a pod owner/admin key on a pod serving /connectors/broker-diagnostics) — check the control plane log for the relay-key delivery refusal, then rotate the pod's relay key from the control plane.";
477
+ }
478
+ return credentialMissingHint(diagnostics.report);
479
+ }
480
+ switch (reason) {
481
+ case "unauthenticated":
482
+ return "The broker rejected this pod's credential. Rotate the pod's relay key from the control plane (or, for a self-brokered pod, re-save its Nango key).";
483
+ case "unreachable":
484
+ return "The broker did not answer. Check that the control plane / Nango host is up and reachable from the pod.";
485
+ case "vault-unreadable":
486
+ return "The pod could not decrypt its broker credential. Check VAULT_SERVER_KEY on the pod.";
487
+ case "vault-unresolved":
488
+ return "The pod holds a relay key from its control plane but cannot read it from its vault. Rotate the pod's relay key from the control plane — it re-delivers a readable key.";
489
+ case "db-unavailable":
490
+ return "The pod could not read its database. Check the pod's health and logs.";
491
+ default:
492
+ return message ?? `The broker reported ${reason}.`;
493
+ }
494
+ }
495
+ function credentialMissingHint(r) {
496
+ if (!r.cpIssuer.present) {
497
+ return "The pod has no control plane issuer, so it cannot accept a relay key. Check CONTROL_PLANE_URL on the pod and restart it, then rotate the relay key from the control plane.";
498
+ }
499
+ if (r.cpIssuer.status !== "approved" || !r.cpIssuer.hasSourceConfigWrite) {
500
+ return "Approve the Synap Control Plane issuer with 'Configure data sources' (source-config:write) in pod-admin → Trust & Keys → Trusted issuers, then rotate the relay key from the control plane.";
501
+ }
502
+ if (!r.ownerIdentityLink.present) {
503
+ return "The pod owner's control plane identity is not linked on this pod, so relay-key delivery is refused. Link it (sign in to this pod through the control plane), then rotate the relay key from the control plane.";
504
+ }
505
+ return "Trust is in place but no valid relay key has been delivered. Rotate the pod's relay key from the control plane.";
506
+ }
507
+ /** Human summary of the trust facts; "unknown" for any part that could not be read. */
508
+ function describeBrokerTrust(diagnostics) {
509
+ if (diagnostics.state !== "read") {
510
+ return `broker kind unknown; credential unknown; issuer unknown; owner link unknown (${diagnostics.detail})`;
511
+ }
512
+ const r = diagnostics.report;
513
+ const credential = !r.relayCredential.present
514
+ ? "credential missing"
515
+ : r.relayCredential.resolvable === false
516
+ ? "credential present but unreadable"
517
+ : `credential present, valid until ${r.relayCredential.validUntil ?? "unknown"}`;
518
+ if (r.broker.kind !== "control-plane") {
519
+ return `broker ${r.broker.kind}`;
520
+ }
521
+ const issuer = !r.cpIssuer.present
522
+ ? "CP issuer missing"
523
+ : r.cpIssuer.status === "approved" && r.cpIssuer.hasSourceConfigWrite
524
+ ? "CP issuer ok"
525
+ : `CP issuer ${r.cpIssuer.status ?? "unknown"}${r.cpIssuer.hasSourceConfigWrite ? "" : " without source-config:write"}`;
526
+ const link = r.ownerIdentityLink.present ? "owner link ok" : "owner link missing";
527
+ return `broker ${r.broker.kind}; ${credential}; ${issuer}; ${link}`;
528
+ }
529
+ export function brokerCheck(providers, diagnostics) {
530
+ const trust = describeBrokerTrust(diagnostics);
531
+ if (providers.state === "unknown") {
532
+ return {
533
+ name: BROKER_CHECK_NAME,
534
+ ok: false,
535
+ unknown: true,
536
+ detail: `could not determine — ${providers.detail}; ${trust}`,
537
+ fix: "This pod build may predate GET /api/hub/connectors/providers, or the key lacks hub-protocol.read.",
538
+ };
539
+ }
540
+ if (providers.state === "not-configured") {
541
+ return {
542
+ name: BROKER_CHECK_NAME,
543
+ ok: true,
544
+ detail: "no connection broker is configured on this pod — connectors are unavailable",
545
+ };
546
+ }
547
+ if (providers.state === "ok") {
548
+ return { name: BROKER_CHECK_NAME, ok: true, detail: `broker answered; ${trust}` };
549
+ }
550
+ return {
551
+ name: BROKER_CHECK_NAME,
552
+ ok: false,
553
+ detail: `${providers.reason}; ${trust}`,
554
+ fix: brokerFixHint(providers.reason, diagnostics, providers.message),
555
+ };
556
+ }
557
+ async function probeBrokerProviders(cfg) {
558
+ try {
559
+ return readProvidersEnvelope(await hubGet("/connectors/providers", {}, cfg));
560
+ }
561
+ catch (err) {
562
+ const body = err instanceof HubError ? err.body : undefined;
563
+ if (err instanceof HubError && err.status === 503 && body?.error === "Nango not configured") {
564
+ return { state: "not-configured" };
565
+ }
566
+ return {
567
+ state: "unknown",
568
+ detail: `the providers door did not answer (${err instanceof Error ? err.message : String(err)})`,
569
+ };
570
+ }
571
+ }
572
+ async function probeBrokerDiagnostics(cfg) {
573
+ try {
574
+ return readBrokerDiagnostics(await hubGet("/connectors/broker-diagnostics", {}, cfg));
575
+ }
576
+ catch (err) {
577
+ if (err instanceof HubError && err.status === 404) {
578
+ return { state: "unknown", detail: "this pod does not expose broker diagnostics (older build)" };
579
+ }
580
+ if (err instanceof HubError && err.status === 403) {
581
+ return { state: "unknown", detail: "broker diagnostics need a pod owner/admin key" };
582
+ }
583
+ return {
584
+ state: "unknown",
585
+ detail: `broker diagnostics did not answer (${err instanceof Error ? err.message : String(err)})`,
586
+ };
587
+ }
588
+ }
589
+ /**
590
+ * Build the same provenance rungs as `synap lens` (session › directory › env ›
591
+ * global), pod-qualified against the active pod so a cross-pod directory lens
592
+ * is not claimed as the origin.
593
+ */
594
+ async function activeLensProvenance(podUrl) {
595
+ const { getClaudeSessionId, resolveActiveLensForPod } = await import("../lib/session-lens.js");
596
+ const { resolveDirectoryLensForPod, effectiveDirectoryLens } = await import("../lib/directory-lens.js");
597
+ const session = getClaudeSessionId();
598
+ const sessionLens = resolveActiveLensForPod(podUrl);
599
+ const dir = resolveDirectoryLensForPod(podUrl);
600
+ const rungs = [
601
+ { source: "session", detail: session ? session.slice(0, 8) : undefined, ...(sessionLens ?? {}) },
602
+ { source: "directory", detail: dir?.file, ...(dir ? effectiveDirectoryLens(dir.lens) : {}) },
603
+ {
604
+ source: "env",
605
+ detail: "SYNAP_WORKSPACE_ID",
606
+ workspaceId: process.env.SYNAP_WORKSPACE_ID,
607
+ projectId: process.env.SYNAP_PROJECT_ID,
608
+ },
609
+ {
610
+ source: "global",
611
+ detail: "~/.synap/config.json",
612
+ workspaceId: getActiveWorkspaceId(),
613
+ projectId: getActiveProjectId(),
614
+ },
615
+ ];
616
+ return resolveLensProvenance(rungs);
617
+ }
618
+ /** A diagnosis for JSON output — never a key's plaintext. */
619
+ function publicDiagnosis(d) {
620
+ return {
621
+ code: d.code,
622
+ severity: d.severity,
623
+ surface: d.surface,
624
+ title: d.title,
625
+ lines: d.lines,
626
+ command: d.command,
627
+ action: d.action,
628
+ approvalUrl: d.approvalUrl,
629
+ locations: d.locations.map((l) => l.label),
630
+ };
631
+ }
632
+ /** One word per check for the collapsed "✓ Pod · key · …" line. */
633
+ const SHORT_NAME = {
634
+ "Pod URL configured": "pod URL",
635
+ "Pod host resolves": "Pod",
636
+ "API key authenticates": "key",
637
+ "Configured workspace exists on this pod": "workspace",
638
+ "Configured project exists on this pod": "project",
639
+ "Workspace + project exist on this pod": "workspace",
640
+ [IS_CHECK_NAME]: "intelligence",
641
+ [BROKER_CHECK_NAME]: "broker",
642
+ "Keys on this computer": "agent keys",
643
+ };
644
+ const UUID = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi;
645
+ const ISO = /\b(\d{4}-\d{2}-\d{2})T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?/g;
646
+ const INTERNAL_URL = /\s*(?:at\s+)?\bhttps?:\/\/(?:localhost|127\.\d+\.\d+\.\d+|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|172\.(?:1[6-9]|2\d|3[01])\.\d+\.\d+|[a-z0-9-]+(?::\d+)?(?=[\s/)]|$))[^\s)]*/gi;
647
+ /**
648
+ * Default output never shows internal addresses, ids or raw timestamps — they
649
+ * are noise to a person and stay one `-v` away. A parenthetical that only
650
+ * carried an id ("(checked as workspace <uuid>)") goes entirely.
651
+ */
652
+ export function forDisplay(text, verbose = false) {
653
+ if (verbose)
654
+ return text;
655
+ return text
656
+ .replace(/\s*\([^()]*\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b[^()]*\)/gi, "")
657
+ .replace(UUID, "…")
658
+ .replace(INTERNAL_URL, "")
659
+ .replace(ISO, "$1")
660
+ .replace(/\s{2,}/g, " ")
661
+ .trim();
662
+ }
663
+ /** Where a lens field came from, short: "./.synap/lens.json", "SYNAP_WORKSPACE_ID", "this Claude session". */
664
+ function lensOrigin(p) {
665
+ if (!p)
666
+ return undefined;
667
+ if (p.source === "session")
668
+ return "this Claude session";
669
+ if (p.source === "env")
670
+ return p.detail ?? "environment";
671
+ if (p.source === "global")
672
+ return "~/.synap/config.json";
673
+ if (p.detail) {
674
+ const rel = path.relative(process.cwd(), p.detail);
675
+ return rel && !rel.startsWith("..") && !path.isAbsolute(rel) ? `./${rel}` : tildify(p.detail, os.homedir());
676
+ }
677
+ return p.source;
678
+ }
679
+ async function finish(checks, opts, lens, keyHealth, hubCfg) {
680
+ // Green requires every check to have VERIFIED something. An undetermined
681
+ // check suppresses green (it is not a pass) but does not fail the run — only
682
+ // a proven failure sets a non-zero exit code.
153
683
  const allOk = checks.every((c) => c.ok);
684
+ const anyFailed = checks.some((c) => !c.ok && !c.unknown);
685
+ const anyUnknown = checks.some((c) => c.unknown);
154
686
  const activeName = listPodProfiles().find((p) => p.active)?.name ?? "default";
687
+ const keySource = resolveKeySource();
688
+ const provenance = await activeLensProvenance(lens.podUrl);
155
689
  if (opts.json) {
156
690
  console.log(JSON.stringify({
157
691
  ok: allOk,
692
+ undetermined: anyUnknown,
158
693
  checks,
159
- lens: { pod: activeName, podUrl: lens.podUrl, workspaceId: lens.workspaceId, projectId: lens.projectId },
694
+ keyHealth: keyHealth ? keyHealth.diagnoses.map(publicDiagnosis) : null,
695
+ keysNotChecked: keyHealth
696
+ ? {
697
+ unreadable: keyHealth.unchecked ?? [],
698
+ notSentToThisPod: (keyHealth.notSent ?? []).map((l) => l.label),
699
+ }
700
+ : null,
701
+ cli: cliBuild(),
702
+ lens: {
703
+ pod: activeName,
704
+ podUrl: lens.podUrl,
705
+ workspaceId: lens.workspaceId,
706
+ projectId: lens.projectId,
707
+ keySource,
708
+ provenance,
709
+ },
160
710
  }, null, 2));
161
- if (!allOk)
711
+ if (anyFailed)
162
712
  process.exitCode = 1;
163
713
  return;
164
714
  }
165
- banner();
166
- log.heading("Coherence Preflight");
167
- for (const c of checks) {
168
- const mark = c.ok ? chalk.green("✓") : chalk.red("✗");
169
- console.log(` ${mark} ${chalk.bold(c.name)}`);
170
- log.dim(` ${c.detail}`);
171
- if (!c.ok && c.fix)
172
- log.dim(` → ${c.fix}`);
715
+ const v = !!opts.verbose;
716
+ const show = (t) => forDisplay(t, v);
717
+ // ── Header: which pod, which lens — names, not ids ──────────────────────
718
+ const lensPodName = (lens.podUrl &&
719
+ listPodProfiles().find((p) => p.config.podUrl.replace(/\/$/, "") === lens.podUrl.replace(/\/$/, ""))?.name) ||
720
+ activeName;
721
+ let host = lens.podUrl ?? "no pod";
722
+ try {
723
+ if (lens.podUrl)
724
+ host = new URL(lens.podUrl).host;
725
+ }
726
+ catch {
727
+ /* keep raw */
173
728
  }
174
729
  log.blank();
175
- log.heading("Active Lens");
176
- console.log(` Pod : ${chalk.bold(activeName)}${lens.podUrl ? ` ${chalk.dim(lens.podUrl)}` : ""}`);
177
- console.log(` Workspace : ${lens.workspaceId ? lens.workspaceId : chalk.dim("none (pod-wide)")}`);
178
- console.log(` Project : ${lens.projectId ? lens.projectId : chalk.dim("none")}`);
730
+ console.log(`${chalk.bold("synap doctor")} ${chalk.dim("·")} ${lensPodName} ${chalk.dim("·")} ${host}${lens.podVersion ? chalk.dim(` (v${lens.podVersion})`) : ""}`);
731
+ const wsName = lens.workspaceId ? lens.workspace ?? (v ? lens.workspaceId : "a workspace this pod does not list") : "pod-wide";
732
+ const lensText = lens.projectId ? `${wsName} › ${lens.project ?? (v ? lens.projectId : "a project this pod does not list")}` : wsName;
733
+ const origin = lensOrigin(provenance.project ?? provenance.workspace);
734
+ console.log(`${chalk.dim("Lens")} ${lensText}${origin && (lens.workspaceId || lens.projectId) ? chalk.dim(` (from ${origin})`) : ""}`);
735
+ // Which build is talking: a stale CLI is the first thing to rule out.
736
+ console.log(`${chalk.dim("CLI")} ${formatCliBuild(cliBuild())}`);
737
+ if (v) {
738
+ console.log(`${chalk.dim("Key")} ${formatKeySource(keySource)}`);
739
+ if (lens.podUrl)
740
+ console.log(`${chalk.dim("URL")} ${lens.podUrl}`);
741
+ }
179
742
  log.blank();
180
- if (allOk) {
181
- log.success("All checks passed.");
743
+ // ── Checks: passes collapse to one line, failures/unknowns in full ───────
744
+ const keyRow = checks.find((c) => c.name === "Keys on this computer");
745
+ const printRow = (c) => {
746
+ const mark = c.ok ? chalk.green("✓") : c.unknown ? chalk.yellow("?") : chalk.red("✗");
747
+ console.log(`${mark} ${chalk.bold(c.name)} — ${show(c.detail)}`);
748
+ if (!c.ok && c.fix)
749
+ console.log(chalk.dim(` → ${show(c.fix)}`));
750
+ };
751
+ const passed = checks.filter((c) => c.ok);
752
+ if (v)
753
+ for (const c of checks)
754
+ printRow(c);
755
+ else if (passed.length) {
756
+ const words = [...new Set(passed.map((c) => SHORT_NAME[c.name] ?? c.name))];
757
+ console.log(`${chalk.green("✓")} ${words.join(" · ")}${chalk.dim(" (details with -v)")}`);
182
758
  }
183
- else {
184
- log.warn("One or more checks failed — see the fixes above.");
185
- process.exitCode = 1;
759
+ if (!v)
760
+ for (const c of checks.filter((c) => !c.ok && c !== keyRow))
761
+ printRow(c);
762
+ // The key row's problems are the key-health lines themselves — no second copy.
763
+ if (keyHealth) {
764
+ const { printKeyHealth, fixKeyHealth, keyProblems } = await import("../lib/key-doctor.js");
765
+ printKeyHealth(keyHealth, { fixHint: !opts.fix, verbose: v, showOk: v, indent: "" });
766
+ if (opts.fix && keyProblems(keyHealth).some(isFixable)) {
767
+ log.blank();
768
+ console.log(chalk.bold("Fixing"));
769
+ const outcomes = await fixKeyHealth(keyHealth);
770
+ for (const o of outcomes)
771
+ console.log(` ${o.ok ? chalk.green("✓") : chalk.red("✗")} ${o.message}`);
772
+ // Every problem fixed AND verified live over MCP: the key row is green now.
773
+ if (keyRow && outcomes.length > 0 && outcomes.every((o) => o.ok)) {
774
+ keyRow.ok = true;
775
+ keyRow.unknown = false;
776
+ keyRow.detail = "fixed and verified (MCP initialize)";
777
+ }
778
+ }
186
779
  }
780
+ // ── One agent, several computers ─────────────────────────────────────────
781
+ if (hubCfg) {
782
+ log.blank();
783
+ console.log(chalk.bold("Connected agents"));
784
+ await printAgentConnections(hubCfg);
785
+ }
786
+ // Env pin beats the active profile in resolveHubConfig — same divergence
787
+ // `synap pods list` already names. Surface it here so doctor alone is enough
788
+ // to catch "profile says perso, every write goes to team".
789
+ const envPod = process.env.SYNAP_POD_URL?.trim();
790
+ const activeProfile = listPodProfiles().find((p) => p.active);
791
+ if (envPod && activeProfile && !samePodOrigin(envPod, activeProfile.config.podUrl)) {
792
+ log.blank();
793
+ log.warn(`SYNAP_POD_URL overrides the active profile — commands are talking to ${envPod}, not ${activeProfile.config.podUrl}.`);
794
+ log.dim(" The env var wins over the ● active profile. Unset it (or open a fresh shell) for the profile to apply.");
795
+ }
796
+ // ── Verdict: the last line. Recomputed: --fix may have turned keys green.
797
+ const finalOk = checks.every((c) => c.ok);
798
+ const finalFailed = checks.some((c) => !c.ok && !c.unknown);
187
799
  log.blank();
800
+ if (finalOk) {
801
+ console.log(chalk.green("✓ All checks passed."));
802
+ }
803
+ else if (finalFailed) {
804
+ const n = checks.filter((c) => !c.ok && !c.unknown).length;
805
+ console.log(chalk.red(`✗ ${n} check${n === 1 ? "" : "s"} failed — the actions above fix ${n === 1 ? "it" : "them"}.`));
806
+ process.exitCode = 1;
807
+ }
808
+ else {
809
+ // Not green: something could not be determined. Saying "all clear" here is
810
+ // the failure mode this command exists to avoid.
811
+ console.log(chalk.yellow("? Nothing failed, but not everything could be checked — see the ? lines."));
812
+ }
188
813
  }
189
814
  //# sourceMappingURL=doctor.js.map