@synap-core/cli 1.11.0 → 1.13.0

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 (533) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +40 -198
  3. package/dist/build-info.json +6 -0
  4. package/dist/commands/agent-run.js +3 -2
  5. package/dist/commands/agent-run.js.map +1 -1
  6. package/dist/commands/agents.d.ts +32 -17
  7. package/dist/commands/agents.js +203 -237
  8. package/dist/commands/agents.js.map +1 -1
  9. package/dist/commands/app.d.ts +143 -0
  10. package/dist/commands/app.js +524 -0
  11. package/dist/commands/app.js.map +1 -0
  12. package/dist/commands/automate.d.ts +21 -0
  13. package/dist/commands/automate.js +42 -0
  14. package/dist/commands/automate.js.map +1 -0
  15. package/dist/commands/automation-schema.test.d.ts +16 -0
  16. package/dist/commands/automation-schema.test.js +77 -0
  17. package/dist/commands/automation-schema.test.js.map +1 -0
  18. package/dist/commands/automation.d.ts +12 -0
  19. package/dist/commands/automation.js +116 -66
  20. package/dist/commands/automation.js.map +1 -1
  21. package/dist/commands/box.d.ts +42 -0
  22. package/dist/commands/box.js +89 -0
  23. package/dist/commands/box.js.map +1 -0
  24. package/dist/commands/bridge-setup.d.ts +14 -1
  25. package/dist/commands/bridge-setup.js +395 -129
  26. package/dist/commands/bridge-setup.js.map +1 -1
  27. package/dist/commands/cap-connections.d.ts +4 -4
  28. package/dist/commands/cap-connections.js +14 -14
  29. package/dist/commands/cap-connections.js.map +1 -1
  30. package/dist/commands/capability.d.ts +3 -1
  31. package/dist/commands/capability.js +249 -176
  32. package/dist/commands/capability.js.map +1 -1
  33. package/dist/commands/catalog.d.ts +275 -0
  34. package/dist/commands/catalog.js +481 -0
  35. package/dist/commands/catalog.js.map +1 -0
  36. package/dist/commands/cell.d.ts +155 -8
  37. package/dist/commands/cell.js +416 -40
  38. package/dist/commands/cell.js.map +1 -1
  39. package/dist/commands/centrality.js +1 -13
  40. package/dist/commands/centrality.js.map +1 -1
  41. package/dist/commands/close-questions.d.ts +27 -0
  42. package/dist/commands/close-questions.js +49 -0
  43. package/dist/commands/close-questions.js.map +1 -0
  44. package/dist/commands/close-questions.test.d.ts +1 -0
  45. package/dist/commands/close-questions.test.js +52 -0
  46. package/dist/commands/close-questions.test.js.map +1 -0
  47. package/dist/commands/connect.d.ts +36 -13
  48. package/dist/commands/connect.js +68 -167
  49. package/dist/commands/connect.js.map +1 -1
  50. package/dist/commands/connections.d.ts +3 -2
  51. package/dist/commands/connections.js +95 -25
  52. package/dist/commands/connections.js.map +1 -1
  53. package/dist/commands/context.js +79 -44
  54. package/dist/commands/context.js.map +1 -1
  55. package/dist/commands/data-extra.d.ts +34 -0
  56. package/dist/commands/data-extra.js +203 -25
  57. package/dist/commands/data-extra.js.map +1 -1
  58. package/dist/commands/data.d.ts +120 -2
  59. package/dist/commands/data.js +295 -21
  60. package/dist/commands/data.js.map +1 -1
  61. package/dist/commands/diagnose.d.ts +66 -4
  62. package/dist/commands/diagnose.js +285 -28
  63. package/dist/commands/diagnose.js.map +1 -1
  64. package/dist/commands/doc.d.ts +11 -4
  65. package/dist/commands/doc.js +39 -14
  66. package/dist/commands/doc.js.map +1 -1
  67. package/dist/commands/doctor.d.ts +133 -3
  68. package/dist/commands/doctor.js +761 -41
  69. package/dist/commands/doctor.js.map +1 -1
  70. package/dist/commands/explain.js +5 -5
  71. package/dist/commands/explain.js.map +1 -1
  72. package/dist/commands/hooks.d.ts +19 -0
  73. package/dist/commands/hooks.js +70 -0
  74. package/dist/commands/hooks.js.map +1 -0
  75. package/dist/commands/import.d.ts +393 -13
  76. package/dist/commands/import.js +1150 -54
  77. package/dist/commands/import.js.map +1 -1
  78. package/dist/commands/init.d.ts +34 -9
  79. package/dist/commands/init.js +481 -981
  80. package/dist/commands/init.js.map +1 -1
  81. package/dist/commands/keys.d.ts +1 -0
  82. package/dist/commands/keys.js +15 -2
  83. package/dist/commands/keys.js.map +1 -1
  84. package/dist/commands/knowledge.d.ts +22 -6
  85. package/dist/commands/knowledge.js +121 -57
  86. package/dist/commands/knowledge.js.map +1 -1
  87. package/dist/commands/lens.d.ts +37 -1
  88. package/dist/commands/lens.js +194 -72
  89. package/dist/commands/lens.js.map +1 -1
  90. package/dist/commands/market-authoring.d.ts +29 -2
  91. package/dist/commands/market-authoring.js +348 -7
  92. package/dist/commands/market-authoring.js.map +1 -1
  93. package/dist/commands/market-official.d.ts +58 -0
  94. package/dist/commands/market-official.js +84 -0
  95. package/dist/commands/market-official.js.map +1 -0
  96. package/dist/commands/market.d.ts +400 -16
  97. package/dist/commands/market.js +1802 -146
  98. package/dist/commands/market.js.map +1 -1
  99. package/dist/commands/mcp.d.ts +60 -25
  100. package/dist/commands/mcp.js +251 -64
  101. package/dist/commands/mcp.js.map +1 -1
  102. package/dist/commands/observe.category.test.d.ts +1 -0
  103. package/dist/commands/observe.category.test.js +59 -0
  104. package/dist/commands/observe.category.test.js.map +1 -0
  105. package/dist/commands/observe.d.ts +3 -0
  106. package/dist/commands/observe.js +7 -5
  107. package/dist/commands/observe.js.map +1 -1
  108. package/dist/commands/observe.title.test.d.ts +1 -0
  109. package/dist/commands/observe.title.test.js +37 -0
  110. package/dist/commands/observe.title.test.js.map +1 -0
  111. package/dist/commands/open.d.ts +39 -9
  112. package/dist/commands/open.js +80 -25
  113. package/dist/commands/open.js.map +1 -1
  114. package/dist/commands/playbook.d.ts +42 -0
  115. package/dist/commands/playbook.js +171 -0
  116. package/dist/commands/playbook.js.map +1 -0
  117. package/dist/commands/pod.js +1 -1
  118. package/dist/commands/pod.js.map +1 -1
  119. package/dist/commands/pods.d.ts +11 -1
  120. package/dist/commands/pods.js +129 -131
  121. package/dist/commands/pods.js.map +1 -1
  122. package/dist/commands/proposals-actions.d.ts +62 -0
  123. package/dist/commands/proposals-actions.js +143 -8
  124. package/dist/commands/proposals-actions.js.map +1 -1
  125. package/dist/commands/proposals-await.d.ts +68 -0
  126. package/dist/commands/proposals-await.js +248 -0
  127. package/dist/commands/proposals-await.js.map +1 -0
  128. package/dist/commands/proton-setup.d.ts +73 -0
  129. package/dist/commands/proton-setup.js +579 -0
  130. package/dist/commands/proton-setup.js.map +1 -0
  131. package/dist/commands/providers.d.ts +23 -0
  132. package/dist/commands/providers.js +126 -1
  133. package/dist/commands/providers.js.map +1 -1
  134. package/dist/commands/raycast.js +6 -2
  135. package/dist/commands/raycast.js.map +1 -1
  136. package/dist/commands/rule.d.ts +150 -0
  137. package/dist/commands/rule.js +447 -0
  138. package/dist/commands/rule.js.map +1 -0
  139. package/dist/commands/session-feed-wait.d.ts +48 -0
  140. package/dist/commands/session-feed-wait.js +261 -0
  141. package/dist/commands/session-feed-wait.js.map +1 -0
  142. package/dist/commands/session-post.d.ts +40 -0
  143. package/dist/commands/session-post.js +96 -0
  144. package/dist/commands/session-post.js.map +1 -0
  145. package/dist/commands/session-wait.d.ts +116 -0
  146. package/dist/commands/session-wait.js +224 -0
  147. package/dist/commands/session-wait.js.map +1 -0
  148. package/dist/commands/sessions.d.ts +54 -0
  149. package/dist/commands/sessions.js +501 -58
  150. package/dist/commands/sessions.js.map +1 -1
  151. package/dist/commands/skill-manage.d.ts +21 -0
  152. package/dist/commands/skill-manage.js +136 -19
  153. package/dist/commands/skill-manage.js.map +1 -1
  154. package/dist/commands/skills-commands.d.ts +2 -0
  155. package/dist/commands/skills-commands.js +9 -1
  156. package/dist/commands/skills-commands.js.map +1 -1
  157. package/dist/commands/skills-commands.test.d.ts +1 -0
  158. package/dist/commands/skills-commands.test.js +57 -0
  159. package/dist/commands/skills-commands.test.js.map +1 -0
  160. package/dist/commands/skills.js +16 -0
  161. package/dist/commands/skills.js.map +1 -1
  162. package/dist/commands/status.d.ts +1 -4
  163. package/dist/commands/status.js +6 -35
  164. package/dist/commands/status.js.map +1 -1
  165. package/dist/commands/statusline.backoff.test.d.ts +13 -0
  166. package/dist/commands/statusline.backoff.test.js +100 -0
  167. package/dist/commands/statusline.backoff.test.js.map +1 -0
  168. package/dist/commands/statusline.js +16 -9
  169. package/dist/commands/statusline.js.map +1 -1
  170. package/dist/commands/sync-status.d.ts +12 -0
  171. package/dist/commands/sync-status.js +125 -0
  172. package/dist/commands/sync-status.js.map +1 -0
  173. package/dist/commands/templates-retire.d.ts +34 -0
  174. package/dist/commands/templates-retire.js +192 -0
  175. package/dist/commands/templates-retire.js.map +1 -0
  176. package/dist/commands/tools.js +6 -21
  177. package/dist/commands/tools.js.map +1 -1
  178. package/dist/commands/tracks.d.ts +112 -0
  179. package/dist/commands/tracks.js +543 -0
  180. package/dist/commands/tracks.js.map +1 -0
  181. package/dist/commands/update.d.ts +3 -11
  182. package/dist/commands/update.js +5 -134
  183. package/dist/commands/update.js.map +1 -1
  184. package/dist/commands/upload.d.ts +41 -3
  185. package/dist/commands/upload.js +120 -18
  186. package/dist/commands/upload.js.map +1 -1
  187. package/dist/commands/vendor.d.ts +28 -0
  188. package/dist/commands/vendor.js +110 -0
  189. package/dist/commands/vendor.js.map +1 -0
  190. package/dist/commands/versions.js +6 -0
  191. package/dist/commands/versions.js.map +1 -1
  192. package/dist/commands/view.js +10 -5
  193. package/dist/commands/view.js.map +1 -1
  194. package/dist/commands/whoami.d.ts +30 -3
  195. package/dist/commands/whoami.js +186 -23
  196. package/dist/commands/whoami.js.map +1 -1
  197. package/dist/commands/worker-accounts.d.ts +149 -0
  198. package/dist/commands/worker-accounts.js +339 -0
  199. package/dist/commands/worker-accounts.js.map +1 -0
  200. package/dist/commands/worker-accounts.test.d.ts +1 -0
  201. package/dist/commands/worker-accounts.test.js +224 -0
  202. package/dist/commands/worker-accounts.test.js.map +1 -0
  203. package/dist/commands/worker.d.ts +331 -0
  204. package/dist/commands/worker.js +1067 -0
  205. package/dist/commands/worker.js.map +1 -0
  206. package/dist/commands/worker.test.d.ts +1 -0
  207. package/dist/commands/worker.test.js +427 -0
  208. package/dist/commands/worker.test.js.map +1 -0
  209. package/dist/commands/workspace-ops.d.ts +35 -0
  210. package/dist/commands/workspace-ops.js +153 -0
  211. package/dist/commands/workspace-ops.js.map +1 -0
  212. package/dist/index.d.ts +6 -6
  213. package/dist/index.js +1043 -186
  214. package/dist/index.js.map +1 -1
  215. package/dist/lib/agent-connections.d.ts +31 -0
  216. package/dist/lib/agent-connections.js +33 -0
  217. package/dist/lib/agent-connections.js.map +1 -0
  218. package/dist/lib/agent-detect.d.ts +27 -0
  219. package/dist/lib/agent-detect.js +71 -0
  220. package/dist/lib/agent-detect.js.map +1 -0
  221. package/dist/lib/agent-door.d.ts +136 -0
  222. package/dist/lib/agent-door.js +429 -0
  223. package/dist/lib/agent-door.js.map +1 -0
  224. package/dist/lib/agents-config.d.ts +2 -0
  225. package/dist/lib/agents-config.js +4 -9
  226. package/dist/lib/agents-config.js.map +1 -1
  227. package/dist/lib/approval-poll.d.ts +31 -2
  228. package/dist/lib/approval-poll.js +20 -7
  229. package/dist/lib/approval-poll.js.map +1 -1
  230. package/dist/lib/atomic-file.d.ts +48 -0
  231. package/dist/lib/atomic-file.js +129 -0
  232. package/dist/lib/atomic-file.js.map +1 -0
  233. package/dist/lib/auth.d.ts +17 -11
  234. package/dist/lib/auth.js +47 -64
  235. package/dist/lib/auth.js.map +1 -1
  236. package/dist/lib/batch-approval.d.ts +85 -0
  237. package/dist/lib/batch-approval.js +170 -0
  238. package/dist/lib/batch-approval.js.map +1 -0
  239. package/dist/lib/box/egress.d.ts +82 -0
  240. package/dist/lib/box/egress.js +264 -0
  241. package/dist/lib/box/egress.js.map +1 -0
  242. package/dist/lib/box/evidence.d.ts +86 -0
  243. package/dist/lib/box/evidence.js +121 -0
  244. package/dist/lib/box/evidence.js.map +1 -0
  245. package/dist/lib/box/job-server.d.ts +72 -0
  246. package/dist/lib/box/job-server.js +246 -0
  247. package/dist/lib/box/job-server.js.map +1 -0
  248. package/dist/lib/box/provisioner.d.ts +83 -0
  249. package/dist/lib/box/provisioner.js +312 -0
  250. package/dist/lib/box/provisioner.js.map +1 -0
  251. package/dist/lib/box/proxmox.d.ts +112 -0
  252. package/dist/lib/box/proxmox.js +211 -0
  253. package/dist/lib/box/proxmox.js.map +1 -0
  254. package/dist/lib/box/wire.d.ts +98 -0
  255. package/dist/lib/box/wire.js +70 -0
  256. package/dist/lib/box/wire.js.map +1 -0
  257. package/dist/lib/capture-lane.d.ts +6 -5
  258. package/dist/lib/capture-lane.js +6 -5
  259. package/dist/lib/capture-lane.js.map +1 -1
  260. package/dist/lib/capture-structure.d.ts +230 -11
  261. package/dist/lib/capture-structure.js +251 -13
  262. package/dist/lib/capture-structure.js.map +1 -1
  263. package/dist/lib/catalog-templates.d.ts +59 -0
  264. package/dist/lib/catalog-templates.js +126 -0
  265. package/dist/lib/catalog-templates.js.map +1 -0
  266. package/dist/lib/cli-build.d.ts +19 -0
  267. package/dist/lib/cli-build.js +37 -0
  268. package/dist/lib/cli-build.js.map +1 -0
  269. package/dist/lib/client-header.test.d.ts +1 -0
  270. package/dist/lib/client-header.test.js +85 -0
  271. package/dist/lib/client-header.test.js.map +1 -0
  272. package/dist/lib/conversation-headers.d.ts +33 -0
  273. package/dist/lib/conversation-headers.js +69 -0
  274. package/dist/lib/conversation-headers.js.map +1 -0
  275. package/dist/lib/cp-packages.d.ts +167 -11
  276. package/dist/lib/cp-packages.js +253 -21
  277. package/dist/lib/cp-packages.js.map +1 -1
  278. package/dist/lib/credential-class.d.ts +144 -0
  279. package/dist/lib/credential-class.js +138 -0
  280. package/dist/lib/credential-class.js.map +1 -0
  281. package/dist/lib/describe-lens.d.ts +35 -0
  282. package/dist/lib/describe-lens.js +40 -0
  283. package/dist/lib/describe-lens.js.map +1 -1
  284. package/dist/lib/device-connect.d.ts +125 -0
  285. package/dist/lib/device-connect.js +256 -0
  286. package/dist/lib/device-connect.js.map +1 -0
  287. package/dist/lib/directory-lens.d.ts +99 -0
  288. package/dist/lib/directory-lens.js +187 -0
  289. package/dist/lib/directory-lens.js.map +1 -0
  290. package/dist/lib/exporter-coverage.d.ts +71 -0
  291. package/dist/lib/exporter-coverage.js +97 -0
  292. package/dist/lib/exporter-coverage.js.map +1 -0
  293. package/dist/lib/goal-words.d.ts +38 -0
  294. package/dist/lib/goal-words.js +112 -0
  295. package/dist/lib/goal-words.js.map +1 -0
  296. package/dist/lib/hub-client.d.ts +57 -15
  297. package/dist/lib/hub-client.js +396 -82
  298. package/dist/lib/hub-client.js.map +1 -1
  299. package/dist/lib/init-connect.d.ts +41 -0
  300. package/dist/lib/init-connect.js +75 -0
  301. package/dist/lib/init-connect.js.map +1 -0
  302. package/dist/lib/init-exit.d.ts +24 -0
  303. package/dist/lib/init-exit.js +41 -0
  304. package/dist/lib/init-exit.js.map +1 -0
  305. package/dist/lib/init-flow.d.ts +25 -0
  306. package/dist/lib/init-flow.js +40 -0
  307. package/dist/lib/init-flow.js.map +1 -0
  308. package/dist/lib/init-handoff.d.ts +24 -0
  309. package/dist/lib/init-handoff.js +26 -0
  310. package/dist/lib/init-handoff.js.map +1 -0
  311. package/dist/lib/init-output.d.ts +18 -0
  312. package/dist/lib/init-output.js +43 -0
  313. package/dist/lib/init-output.js.map +1 -0
  314. package/dist/lib/init-pod.d.ts +45 -0
  315. package/dist/lib/init-pod.js +36 -0
  316. package/dist/lib/init-pod.js.map +1 -0
  317. package/dist/lib/installed.d.ts +210 -11
  318. package/dist/lib/installed.js +234 -36
  319. package/dist/lib/installed.js.map +1 -1
  320. package/dist/lib/key-approval.d.ts +57 -0
  321. package/dist/lib/key-approval.js +117 -0
  322. package/dist/lib/key-approval.js.map +1 -0
  323. package/dist/lib/key-diagnosis.d.ts +56 -0
  324. package/dist/lib/key-diagnosis.js +227 -0
  325. package/dist/lib/key-diagnosis.js.map +1 -0
  326. package/dist/lib/key-doctor.d.ts +136 -0
  327. package/dist/lib/key-doctor.js +286 -0
  328. package/dist/lib/key-doctor.js.map +1 -0
  329. package/dist/lib/key-locations.d.ts +120 -0
  330. package/dist/lib/key-locations.js +326 -0
  331. package/dist/lib/key-locations.js.map +1 -0
  332. package/dist/lib/key-source.d.ts +68 -0
  333. package/dist/lib/key-source.js +109 -0
  334. package/dist/lib/key-source.js.map +1 -0
  335. package/dist/lib/key-state.d.ts +70 -0
  336. package/dist/lib/key-state.js +115 -0
  337. package/dist/lib/key-state.js.map +1 -0
  338. package/dist/lib/kind-package.d.ts +181 -0
  339. package/dist/lib/kind-package.js +679 -0
  340. package/dist/lib/kind-package.js.map +1 -0
  341. package/dist/lib/knowledge-title.d.ts +25 -0
  342. package/dist/lib/knowledge-title.js +47 -0
  343. package/dist/lib/knowledge-title.js.map +1 -0
  344. package/dist/lib/knowledge-title.test.d.ts +1 -0
  345. package/dist/lib/knowledge-title.test.js +34 -0
  346. package/dist/lib/knowledge-title.test.js.map +1 -0
  347. package/dist/lib/machine-instance.d.ts +64 -0
  348. package/dist/lib/machine-instance.js +142 -0
  349. package/dist/lib/machine-instance.js.map +1 -0
  350. package/dist/lib/mcp-bridge.d.ts +49 -0
  351. package/dist/lib/mcp-bridge.js +180 -0
  352. package/dist/lib/mcp-bridge.js.map +1 -0
  353. package/dist/lib/mcp-bridge.test.d.ts +1 -0
  354. package/dist/lib/mcp-bridge.test.js +118 -0
  355. package/dist/lib/mcp-bridge.test.js.map +1 -0
  356. package/dist/lib/mcp-call.d.ts +30 -0
  357. package/dist/lib/mcp-call.js +107 -0
  358. package/dist/lib/mcp-call.js.map +1 -0
  359. package/dist/lib/mcp-snippets.d.ts +53 -0
  360. package/dist/lib/mcp-snippets.js +82 -0
  361. package/dist/lib/mcp-snippets.js.map +1 -0
  362. package/dist/lib/next-steps.d.ts +8 -0
  363. package/dist/lib/next-steps.js +31 -1
  364. package/dist/lib/next-steps.js.map +1 -1
  365. package/dist/lib/openclaw.js +3 -2
  366. package/dist/lib/openclaw.js.map +1 -1
  367. package/dist/lib/package-wire.d.ts +179 -0
  368. package/dist/lib/package-wire.js +68 -0
  369. package/dist/lib/package-wire.js.map +1 -0
  370. package/dist/lib/pair-link.d.ts +38 -0
  371. package/dist/lib/pair-link.js +68 -0
  372. package/dist/lib/pair-link.js.map +1 -0
  373. package/dist/lib/pod-moved.d.ts +70 -0
  374. package/dist/lib/pod-moved.js +146 -0
  375. package/dist/lib/pod-moved.js.map +1 -0
  376. package/dist/lib/pod-reach.d.ts +39 -0
  377. package/dist/lib/pod-reach.js +117 -0
  378. package/dist/lib/pod-reach.js.map +1 -0
  379. package/dist/lib/pod-reach.test.d.ts +1 -0
  380. package/dist/lib/pod-reach.test.js +67 -0
  381. package/dist/lib/pod-reach.test.js.map +1 -0
  382. package/dist/lib/pod-wide.d.ts +40 -0
  383. package/dist/lib/pod-wide.js +46 -0
  384. package/dist/lib/pod-wide.js.map +1 -0
  385. package/dist/lib/pod.d.ts +74 -112
  386. package/dist/lib/pod.js +146 -415
  387. package/dist/lib/pod.js.map +1 -1
  388. package/dist/lib/project-ref.d.ts +9 -3
  389. package/dist/lib/project-ref.js +10 -4
  390. package/dist/lib/project-ref.js.map +1 -1
  391. package/dist/lib/register-claude-code-mcp.test.d.ts +1 -0
  392. package/dist/lib/register-claude-code-mcp.test.js +78 -0
  393. package/dist/lib/register-claude-code-mcp.test.js.map +1 -0
  394. package/dist/lib/review-credential.d.ts +119 -0
  395. package/dist/lib/review-credential.js +149 -0
  396. package/dist/lib/review-credential.js.map +1 -0
  397. package/dist/lib/safe-open.d.ts +49 -0
  398. package/dist/lib/safe-open.js +119 -0
  399. package/dist/lib/safe-open.js.map +1 -0
  400. package/dist/lib/session-feed.d.ts +185 -0
  401. package/dist/lib/session-feed.js +257 -0
  402. package/dist/lib/session-feed.js.map +1 -0
  403. package/dist/lib/session-lens.d.ts +40 -1
  404. package/dist/lib/session-lens.js +54 -4
  405. package/dist/lib/session-lens.js.map +1 -1
  406. package/dist/lib/superwhisper-import.d.ts +8 -0
  407. package/dist/lib/superwhisper-import.js +14 -7
  408. package/dist/lib/superwhisper-import.js.map +1 -1
  409. package/dist/lib/superwhisper-title.test.d.ts +1 -0
  410. package/dist/lib/superwhisper-title.test.js +18 -0
  411. package/dist/lib/superwhisper-title.test.js.map +1 -0
  412. package/dist/lib/targets.d.ts +152 -10
  413. package/dist/lib/targets.js +974 -325
  414. package/dist/lib/targets.js.map +1 -1
  415. package/dist/lib/template-file.d.ts +4 -0
  416. package/dist/lib/template-file.js.map +1 -1
  417. package/dist/lib/template-lifecycle.d.ts +217 -0
  418. package/dist/lib/template-lifecycle.js +433 -0
  419. package/dist/lib/template-lifecycle.js.map +1 -0
  420. package/dist/lib/template-steps.d.ts +88 -0
  421. package/dist/lib/template-steps.js +118 -0
  422. package/dist/lib/template-steps.js.map +1 -0
  423. package/dist/lib/time-ago.d.ts +2 -0
  424. package/dist/lib/time-ago.js +15 -0
  425. package/dist/lib/time-ago.js.map +1 -0
  426. package/dist/lib/wake-hook-settings.d.ts +57 -0
  427. package/dist/lib/wake-hook-settings.js +153 -0
  428. package/dist/lib/wake-hook-settings.js.map +1 -0
  429. package/dist/lib/wake-state.d.ts +65 -0
  430. package/dist/lib/wake-state.js +122 -0
  431. package/dist/lib/wake-state.js.map +1 -0
  432. package/dist/utils/logger.js +5 -2
  433. package/dist/utils/logger.js.map +1 -1
  434. package/package.json +26 -17
  435. package/skills/README.md +13 -11
  436. package/skills/agent-os/SKILL.md +214 -99
  437. package/skills/manifest.json +1 -1
  438. package/skills/onboard/SKILL.md +55 -26
  439. package/skills/synap/README.md +5 -8
  440. package/skills/synap/SKILL.md +1149 -289
  441. package/skills/synap/_order.txt +7 -0
  442. package/skills/synap/_skill.yaml +1 -1
  443. package/skills/synap/automations.md +15 -0
  444. package/skills/synap/capture.md +5 -0
  445. package/skills/synap/cli-operations.md +24 -18
  446. package/skills/synap/code-skills.md +70 -0
  447. package/skills/synap/concepts.md +36 -0
  448. package/skills/synap/content-os.md +10 -1
  449. package/skills/synap/creative-loop.md +15 -10
  450. package/skills/synap/crm.md +21 -52
  451. package/skills/synap/document-embeds.md +143 -47
  452. package/skills/synap/escalation-ladder.md +20 -10
  453. package/skills/synap/focus-sessions.md +56 -21
  454. package/skills/synap/from-intent.md +158 -0
  455. package/skills/synap/governance-rules.md +5 -5
  456. package/skills/synap/governance.md +10 -7
  457. package/skills/synap/hand-off.md +33 -0
  458. package/skills/synap/inline-patterns.md +15 -7
  459. package/skills/synap/lenses.md +34 -16
  460. package/skills/synap/linking.md +42 -41
  461. package/skills/synap/mcp-remote-servers.md +64 -0
  462. package/skills/synap/mental-model.md +17 -15
  463. package/skills/synap/operating-mode-rules.md +2 -2
  464. package/skills/synap/processes.md +118 -0
  465. package/skills/synap/quick-reference.md +7 -2
  466. package/skills/synap/reading.md +3 -1
  467. package/skills/synap/reflexes.md +12 -42
  468. package/skills/synap/viewframe-cells.md +38 -5
  469. package/skills/synap/work-flow.md +1 -1
  470. package/skills/synap/work-model.md +110 -0
  471. package/skills/synap/worked-examples.md +4 -5
  472. package/skills/synap/workspace-design.md +15 -11
  473. package/skills/synap/writes.md +105 -25
  474. package/skills/synap/writing-titles.md +63 -0
  475. package/skills/synap-app/SKILL.md +149 -67
  476. package/skills/synap-app/_skill.yaml +1 -1
  477. package/skills/synap-app/creating-the-base-app.md +12 -7
  478. package/skills/synap-app/governance.md +27 -16
  479. package/skills/synap-app/install-commands.md +18 -9
  480. package/skills/synap-app/overview.md +16 -9
  481. package/skills/synap-app/using-the-sdk.md +75 -25
  482. package/skills/synap-design/SKILL.md +474 -0
  483. package/skills/synap-design/_order.txt +4 -0
  484. package/skills/synap-design/_skill.yaml +21 -0
  485. package/skills/synap-design/loop.md +134 -0
  486. package/skills/synap-design/overview.md +44 -0
  487. package/skills/synap-design/principles.md +130 -0
  488. package/skills/synap-design/system.md +133 -0
  489. package/skills/synap-market/SKILL.md +306 -16
  490. package/skills/synap-market/_skill.yaml +1 -1
  491. package/skills/synap-market/author-and-publish.md +145 -8
  492. package/skills/synap-market/governance-and-catalog.md +7 -0
  493. package/skills/synap-market/install.md +79 -1
  494. package/skills/synap-market/overview.md +74 -6
  495. package/skills/synap-schema/SKILL.md +172 -10
  496. package/skills/synap-schema/_order.txt +2 -0
  497. package/skills/synap-schema/_skill.yaml +4 -2
  498. package/skills/synap-schema/common-mistakes.md +4 -2
  499. package/skills/synap-schema/discover-profiles.md +1 -1
  500. package/skills/synap-schema/entity-scope.md +15 -3
  501. package/skills/synap-schema/extend-first.md +29 -0
  502. package/skills/synap-schema/extend-vs-create.md +1 -1
  503. package/skills/synap-schema/intent-vocabulary-ssot.md +111 -0
  504. package/skills/synap-schema/property-types.md +1 -1
  505. package/skills/synap-schema/relations.md +1 -1
  506. package/skills/synap-ui/SKILL.md +19 -12
  507. package/skills/synap-ui/_skill.yaml +1 -1
  508. package/skills/synap-ui/ai-companion.md +1 -1
  509. package/skills/synap-ui/arranging-bento.md +2 -0
  510. package/skills/synap-ui/bento-layouts.md +10 -10
  511. package/skills/synap-ui/bento-recipes.md +313 -543
  512. package/skills/synap-ui/four-builds.md +5 -0
  513. package/skills/synap-ui/view-types.md +2 -2
  514. package/skills/synap-ui/widget-catalog.md +22 -278
  515. package/templates/automations/bookmark-enrichment.automation.json +290 -13
  516. package/templates/automations/url-bookmark-capture.automation.json +73 -11
  517. package/templates/box/runner.py +206 -0
  518. package/templates/skills/authoring-playbooks/SKILL.md +6 -3
  519. package/templates/skills/authoring-playbooks/reference/stages-contract.md +15 -0
  520. package/templates/skills/authoring-playbooks/reference/worked-example.md +3 -0
  521. package/dist/commands/infra.d.ts +0 -21
  522. package/dist/commands/infra.js +0 -262
  523. package/dist/commands/infra.js.map +0 -1
  524. package/node_modules/@synap/hub-rest-client/dist/index.cjs +0 -1261
  525. package/node_modules/@synap/hub-rest-client/dist/index.d.cts +0 -1647
  526. package/node_modules/@synap/hub-rest-client/dist/index.d.ts +0 -1647
  527. package/node_modules/@synap/hub-rest-client/dist/index.js +0 -1231
  528. package/node_modules/@synap/hub-rest-client/package.json +0 -45
  529. package/node_modules/@synap/hub-rest-client/src/client.ts +0 -1631
  530. package/node_modules/@synap/hub-rest-client/src/errors.ts +0 -30
  531. package/node_modules/@synap/hub-rest-client/src/index.ts +0 -153
  532. package/node_modules/@synap/hub-rest-client/src/setup.ts +0 -133
  533. package/node_modules/@synap/hub-rest-client/src/types.ts +0 -1299
@@ -1,11 +1,52 @@
1
- import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
1
+ import { existsSync, readFileSync, unlinkSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { getActivePodConfig, getActiveWorkspaceId, getActiveWorkspaceIdForPod, findPodNameByUrl, getActiveProjectId, listPodProfiles, getPodOverride, getSurfaceAgentKey } from "./pod.js";
3
+ import { getActivePodConfig, getActiveWorkspaceId, getActiveWorkspaceIdForPod, findPodNameByUrl, getActiveProjectBinding, getActiveProjectId, listPodProfiles, getPodOverride, getSurfaceAgentKey, } from "./pod.js";
4
4
  import { resolveAgentOverride } from "./agents-config.js";
5
- import { resolveActiveLens } from "./session-lens.js";
6
- import { HubRestClient } from "@synap/hub-rest-client";
5
+ import { resolveActiveLensForPod, getClaudeSessionId, writeLens, clearLensField, } from "./session-lens.js";
6
+ import { clientHeader, conversationHeaders } from "./conversation-headers.js";
7
+ import { effectiveDirectoryLens, resolveDirectoryLensForPod, writeDirectoryLens, clearDirectoryLensField, } from "./directory-lens.js";
8
+ import { preferClaudeCodeSurfaceKey } from "./key-source.js";
9
+ import { samePodOrigin } from "./project-ref.js";
10
+ import { PodMovedError, podFetch } from "./pod-moved.js";
11
+ import { HubRestClient } from "@synap-core/hub-rest-client";
7
12
  import { log } from "../utils/logger.js";
8
13
  import chalk from "chalk";
14
+ /**
15
+ * Resolve a projectId safe to send to `podUrl`.
16
+ *
17
+ * Order: pod-qualified lens → directory lens → env project when co-located with
18
+ * this pod (trustEnvProject — same model as SYNAP_WORKSPACE_ID under env rung) →
19
+ * ActiveProjectBinding for this pod → legacy activeProjectId only when the
20
+ * active profile is this pod.
21
+ *
22
+ * Write paths should consume `resolveHubConfig().projectId` rather than bare
23
+ * `getActiveProjectId()` / env.
24
+ */
25
+ function resolveProjectIdForPod(podUrl, opts) {
26
+ if (opts.lensProjectId)
27
+ return opts.lensProjectId;
28
+ if (opts.dirLensProjectId)
29
+ return opts.dirLensProjectId;
30
+ // Env pin co-located with env pod (connect --pin-project, Claude settings.env).
31
+ if (opts.trustEnvProject && opts.envProject)
32
+ return opts.envProject;
33
+ const profiles = listPodProfiles();
34
+ const binding = getActiveProjectBinding();
35
+ if (binding) {
36
+ const boundPod = profiles.find((x) => x.name === binding.podName);
37
+ if (boundPod && samePodOrigin(boundPod.config.podUrl, podUrl)) {
38
+ return binding.projectId;
39
+ }
40
+ // Binding is for another pod — do not send it.
41
+ return undefined;
42
+ }
43
+ // Legacy: activeProjectId with no binding — only when active profile is this pod.
44
+ const active = getActivePodConfig();
45
+ if (active && samePodOrigin(active.podUrl, podUrl)) {
46
+ return getActiveProjectId() || undefined;
47
+ }
48
+ return undefined;
49
+ }
9
50
  /**
10
51
  * A failed Hub call, carrying the structured facts callers need to branch on.
11
52
  *
@@ -173,6 +214,33 @@ function readErrorBody(status, rawBody) {
173
214
  const oneLine = trimmed.replace(/\s+/g, " ");
174
215
  return { detail: oneLine.length > 200 ? `${oneLine.slice(0, 200)}…` : oneLine };
175
216
  }
217
+ // tRPC batch errors are array-shaped: `[{ error: { message, code, data: { httpStatus, ... } } }]`
218
+ // (superjson may additionally nest the real payload one level deeper under
219
+ // `error.json`). Unwrap to the first entry's error object before falling
220
+ // through to the normal object-shaped handling below, so a tRPC failure
221
+ // surfaces its real `message` instead of the generic status phrase.
222
+ if (Array.isArray(parsed)) {
223
+ const first = parsed[0];
224
+ const err = first?.error;
225
+ if (err && typeof err === "object") {
226
+ const json = err.json;
227
+ parsed = json ?? err;
228
+ }
229
+ }
230
+ else if (parsed &&
231
+ typeof parsed === "object" &&
232
+ "error" in parsed) {
233
+ // Single tRPC-shaped error body: `{ error: { message, ... } }` or
234
+ // `{ error: { json: { message, ... } } }`. Only unwrap when `error` is an
235
+ // OBJECT — a Hub-REST body's `error` is a plain string category
236
+ // (`{ error: "...", detail: "..." }`) and must fall through unchanged.
237
+ const errVal = parsed.error;
238
+ if (errVal && typeof errVal === "object") {
239
+ const errObj = errVal;
240
+ const json = errObj.json;
241
+ parsed = json ?? errObj;
242
+ }
243
+ }
176
244
  if (parsed && typeof parsed === "object") {
177
245
  const o = parsed;
178
246
  const code = typeof o.code === "string" ? o.code : undefined;
@@ -210,44 +278,48 @@ function hubError(method, path, status, rawBody, suffix, ctx) {
210
278
  workspaceId: ctx?.workspaceId,
211
279
  });
212
280
  }
213
- /** Where to send users who hit a wall. */
281
+ /**
282
+ * Where to send users who hit a wall. The support door is the ONE customer
283
+ * support entry point shared by every Synap surface (`from=cli` tells support
284
+ * which surface the person came from); Discord stays as the community option.
285
+ */
286
+ export const SUPPORT_URL = "https://synap.live/support?from=cli";
214
287
  const DISCORD_INVITE_URL = "https://discord.gg/xhRdQ7hG5h";
215
288
  /** Wrap a URL in an OSC-8 terminal hyperlink so `label` is clickable. */
216
289
  function osc8Link(url, label) {
217
290
  return `\x1b]8;;${url}\x1b\\${label}\x1b]8;;\x1b\\`;
218
291
  }
219
292
  /**
220
- * Once-per-process guard for the Discord help footer. A single CLI invocation
293
+ * Once-per-process guard for the help footer. A single CLI invocation
221
294
  * runs one command; printing the footer at most once keeps it a footer, not a
222
295
  * per-error/per-line spam.
223
296
  */
224
- let _discordHelpShown = false;
297
+ let _helpShown = false;
225
298
  /**
226
- * Append a one-line "need help?" footer with a clickable Discord invite. Prints
227
- * to stderr (same stream as the error it follows, so `2>/dev/null` hides both)
228
- * and only once per process. The invite is an OSC-8 hyperlink on an interactive
229
- * terminal, a plain URL when the stream is redirected/piped.
299
+ * Append a one-line "need help?" footer naming the support door (open a ticket)
300
+ * and the Discord invite. Prints to stderr (same stream as the error it
301
+ * follows, so `2>/dev/null` hides both) and only once per process. Each URL is
302
+ * an OSC-8 hyperlink on an interactive terminal, a plain URL when the stream is
303
+ * redirected/piped.
230
304
  */
231
- export function discordHelpLine() {
232
- if (_discordHelpShown)
305
+ export function helpFooterLine() {
306
+ if (_helpShown)
233
307
  return;
234
- _discordHelpShown = true;
235
- const link = process.stderr.isTTY
236
- ? osc8Link(DISCORD_INVITE_URL, DISCORD_INVITE_URL)
237
- : DISCORD_INVITE_URL;
238
- console.error(chalk.dim(` Need help? Join our Discord: ${link}`));
308
+ _helpShown = true;
309
+ const link = (url) => (process.stderr.isTTY ? osc8Link(url, url) : url);
310
+ console.error(chalk.dim(` Need help? Open a ticket: ${link(SUPPORT_URL)} (or join our Discord: ${link(DISCORD_INVITE_URL)})`));
239
311
  }
240
312
  /**
241
313
  * Print a failed Hub call: its humanized message plus a hint for the classes
242
314
  * where the cause is not obvious from the message alone. Lives here rather than
243
315
  * in utils/logger so the logger stays domain-agnostic and the taxonomy and its
244
- * presentation sit in one file. Always closes with the one-time Discord footer.
316
+ * presentation sit in one file. Always closes with the one-time help footer.
245
317
  */
246
318
  export function renderHubError(err) {
247
319
  renderHubErrorBody(err);
248
320
  // ONE central place every failed Hub command flows through — append the help
249
321
  // footer here so it prints once per failed command (guarded), not per line.
250
- discordHelpLine();
322
+ helpFooterLine();
251
323
  }
252
324
  function renderHubErrorBody(err) {
253
325
  // Hints use log.hint (stderr), not log.dim (stdout): a hint belongs to the
@@ -258,11 +330,70 @@ function renderHubErrorBody(err) {
258
330
  log.hint(err.hint);
259
331
  return;
260
332
  }
333
+ if (err instanceof PodMovedError) {
334
+ // One line: what happened and what to run.
335
+ log.error(err.message);
336
+ return;
337
+ }
261
338
  if (!(err instanceof HubError)) {
262
339
  log.error(err instanceof Error ? err.message : String(err));
263
340
  return;
264
341
  }
342
+ // Connection-class failure — the execute door returns 424 with a machine
343
+ // `errorClass` (+ `providerRef`) precisely so it SURVIVES the pod's 5xx egress
344
+ // sanitizer. This is client-ACTIONABLE (reconnect), not a server fault, so name
345
+ // the cause + the exact fix instead of the generic status hint. Mirrors the
346
+ // browser's P1 self-heal reconnect chip.
347
+ const errBody = err.body;
348
+ const errorClass = errBody?.errorClass;
349
+ if (errorClass === "auth" || errorClass === "no_connection") {
350
+ const provider = errBody?.providerRef
351
+ ? chalk.bold(errBody.providerRef)
352
+ : "this capability's";
353
+ log.error(`The ${provider} connection needs reconnecting — its access has expired or was revoked.`);
354
+ log.hint('Reconnect it: synap cap connect "<capability>" --reconnect');
355
+ log.hint("Find the capability name with: synap cap list");
356
+ return;
357
+ }
358
+ if (errorClass === "target_missing") {
359
+ log.error(err.message);
360
+ log.hint("The workspace or target this run needs no longer exists on this pod. Run: synap orient");
361
+ return;
362
+ }
363
+ // SETUP-REQUIRED failure — the APPLY door (`POST /capabilities/apply`) returns
364
+ // a DUCK-TYPED body (412 for `no_connection`, 400 for `missing_field`) with
365
+ // `failureClass` (+ `missingFields` / `connection`) so an install that needs a
366
+ // human before it can apply survives as an actionable message. This is a
367
+ // client-ACTIONABLE, NON-retryable state — name the missing step instead of the
368
+ // generic status phrase below (a bare 412/400 tells the user nothing to do).
369
+ const setupBody = err.body;
370
+ const failureClass = setupBody?.failureClass;
371
+ if (failureClass === "no_connection") {
372
+ const provider = setupBody?.connection?.provider;
373
+ log.error(provider
374
+ ? `The "${provider}" provider isn't connected yet — connect it first (synap cap connect "${provider}").`
375
+ : 'A required provider isn\'t connected yet — connect it first (synap cap connect "<provider>").');
376
+ return;
377
+ }
378
+ if (failureClass === "missing_field") {
379
+ const missingFields = Array.isArray(setupBody?.missingFields)
380
+ ? setupBody.missingFields.filter((field) => typeof field === "string")
381
+ : [];
382
+ log.error(missingFields.length > 0
383
+ ? `Missing required value(s): ${missingFields.join(", ")}`
384
+ : "Missing required value(s).");
385
+ return;
386
+ }
265
387
  log.error(err.message);
388
+ // A 500 carries an `errorId` (the egress sanitizer logs the real cause under
389
+ // it server-side) — surface it so "quote errorId when reporting it" is
390
+ // actionable rather than a dead end.
391
+ const errorId = typeof err.body?.errorId === "string"
392
+ ? err.body.errorId
393
+ : undefined;
394
+ if (err.status >= 500 && errorId) {
395
+ log.hint(`errorId: ${errorId}`);
396
+ }
266
397
  // A 500 whose body carried a specific cause (detail/message) is already in
267
398
  // `err.message` above — do NOT then claim "internal error, not in this
268
399
  // command": a workspace/template creation failure is often an actionable
@@ -284,9 +415,15 @@ function renderHubErrorBody(err) {
284
415
  // active workspace left over from ANOTHER pod (a stale `synap use` after a
285
416
  // pod switch) being sent to a pod it doesn't belong to.
286
417
  if (err.workspaceId && err.podUrl) {
418
+ // State the FACT (what was sent where), then the two causes WITHOUT
419
+ // picking one — this code cannot tell them apart, and asserting the
420
+ // cross-pod story is wrong whenever the saved workspace simply no longer
421
+ // exists on its own pod (dogfood: `pods.team.workspaceId` pointed at a
422
+ // deleted workspace; the hint blamed a pod mix-up that had not happened).
423
+ // `synap doctor` is the only thing here that actually CHECKS, so it leads.
287
424
  log.hint(`Sent workspace ${err.workspaceId} to ${err.podUrl}.`);
288
- log.hint("That workspace likely belongs to a different pod than the one you're calling.");
289
- log.hint("Fix: pass --pod <name> to target the workspace's pod, or run `synap pods use <name>` to align your active pod + workspace. See valid workspaces with `synap orient`.");
425
+ log.hint("That workspace is not accessible on this pod — either it lives on a different pod, or it no longer exists here.");
426
+ log.hint("Run: synap doctor (checks which of the two it is), then: synap orient (see valid workspaces) and synap use <id>.");
290
427
  }
291
428
  else {
292
429
  log.hint("Workspace not found or not accessible on this pod — run: synap orient (see valid workspaces), or: synap doctor");
@@ -337,14 +474,22 @@ export async function resolveHubConfig(opts) {
337
474
  // Beats the env vars deliberately — that is the whole point of the flag.
338
475
  const podOverride = getPodOverride();
339
476
  if (podOverride) {
477
+ // Prefer cross-pod-safe resolution (binding + "not another pod's default")
478
+ // over the profile's baked `workspaceId`, which can drift to a foreign UUID
479
+ // after dual-pod work (dogfood: perso profile held team CRM ws → 403).
480
+ const overrideName = findPodNameByUrl(podOverride.podUrl);
481
+ const safeWs = getActiveWorkspaceIdForPod(overrideName) ||
482
+ // Only fall back to profile default when it isn't known to belong elsewhere
483
+ // (getActiveWorkspaceIdForPod already applied that rule against the profile).
484
+ undefined;
340
485
  return {
341
486
  podUrl: podOverride.podUrl,
342
487
  apiKey: podOverride.hubApiKey,
343
- // A profile's agentUserId may be empty; "cli" makes resolveUserId fetch /users/me.
344
- userId: podOverride.agentUserId || "cli",
345
- // Use the TARGET pod's own workspace — NOT this session's lens, which
346
- // belongs to the default pod and would be meaningless on the target.
347
- workspaceId: podOverride.workspaceId || undefined,
488
+ // "cli": resolveUserId asks the pod who this key acts as (see HubConfig.userId).
489
+ userId: "cli",
490
+ agentUserId: podOverride.agentUserId || undefined,
491
+ workspaceId: safeWs,
492
+ projectId: resolveProjectIdForPod(podOverride.podUrl, {}),
348
493
  };
349
494
  }
350
495
  // 1. Explicit flags (escape hatch)
@@ -365,7 +510,13 @@ export async function resolveHubConfig(opts) {
365
510
  return {
366
511
  podUrl,
367
512
  apiKey: agentOverride.apiKey,
368
- userId: agentOverride.podName ?? "agent",
513
+ // The acting user is asked of the pod ("cli" → /users/me): an agent key
514
+ // acts as its linked human, and the pod 403s the agent's own id.
515
+ userId: "cli",
516
+ // The AGENT's own id, recorded with its key — for `synap worker` to
517
+ // recognise its own room posts (resolveAgentUserId). Never the pod
518
+ // profile name (it once was, and the worker resumed itself on them).
519
+ agentUserId: agentOverride.agentUserId || undefined,
369
520
  scopes: envScopes ? envScopes.split(",") : undefined,
370
521
  };
371
522
  }
@@ -376,7 +527,13 @@ export async function resolveHubConfig(opts) {
376
527
  const envScopes = process.env.SYNAP_KEY_SCOPES;
377
528
  const envWorkspace = process.env.SYNAP_WORKSPACE_ID;
378
529
  const envProject = process.env.SYNAP_PROJECT_ID;
379
- if (envPod && envKey && envUser) {
530
+ // Pod + key are enough. SYNAP_USER_ID used to be required too, so a shell
531
+ // with just the pair (`SYNAP_POD_URL=… SYNAP_HUB_API_KEY=… synap app
532
+ // connect`, the documented way to try another key) fell through to "No pod
533
+ // configured" (2026-10-20). Unset, userId is "cli" and resolveUserId asks
534
+ // the pod (/users/me) — the same fallback --pod and SYNAP_AGENT use. The
535
+ // claude-code surface-key preference below still applies.
536
+ if (envPod && envKey) {
380
537
  // Surface-bound resolution guard: if this pod has a `claude-code` surface
381
538
  // key pinned to it (via `synap connect --target=claude-code`) and the
382
539
  // ambient SYNAP_HUB_API_KEY doesn't match it, the session inherited a
@@ -384,28 +541,53 @@ export async function resolveHubConfig(opts) {
384
541
  // the pinned key so a leaked/inherited env var can't silently redefine
385
542
  // which agent this session acts as. Conservative: only applies here, in
386
543
  // step 3 — steps 0-2 (--pod, explicit flags, SYNAP_AGENT) still win
387
- // outright, untouched.
388
- const surfaceKey = getSurfaceAgentKey("claude-code");
389
- let apiKey = envKey;
390
- let userId = envUser;
391
- if (surfaceKey && surfaceKey.podUrl === envPod && surfaceKey.hubApiKey && surfaceKey.hubApiKey !== envKey) {
544
+ // outright, untouched. Preference logic lives in key-source.ts so whoami
545
+ // reports the same rule (no drift).
546
+ const preferred = preferClaudeCodeSurfaceKey({
547
+ envPod,
548
+ envKey,
549
+ envUser,
550
+ surface: getSurfaceAgentKey("claude-code", envPod),
551
+ });
552
+ if (preferred.usedSurface) {
392
553
  process.stderr.write(`[synap] warning: ambient SYNAP_HUB_API_KEY does not match the claude-code surface key pinned to ${envPod} — using the pinned key instead of the inherited one.\n`);
393
- apiKey = surfaceKey.hubApiKey;
394
- userId = surfaceKey.agentUserId || envUser;
395
554
  }
555
+ const apiKey = preferred.apiKey;
556
+ const userId = preferred.userId;
557
+ const agentUserId = preferred.agentUserId;
558
+ // Per-Claude-session lens wins over the global env var, so concurrent
559
+ // sessions can each be scoped to a different workspace — but ONLY when the
560
+ // lens belongs to THIS pod. A lens stamped with another pod is ignored so
561
+ // the pod-qualified fallback below applies (the "Access denied to
562
+ // workspace" 403 after `synap pods use`).
563
+ const lens = resolveActiveLensForPod(envPod);
564
+ // …and the per-DIRECTORY lens sits directly beneath it: the durable default
565
+ // for this working tree, overridden by the session lens when one is set.
566
+ // Pod-qualified through the same door, for the same reason — a directory
567
+ // lens outlives many `synap pods use` switches, so it is MORE exposed to
568
+ // the cross-pod 403 than the session lens is.
569
+ const dirLens = resolveDirectoryLensForPod(envPod)?.lens;
396
570
  return {
397
571
  podUrl: envPod,
398
572
  apiKey,
399
573
  userId,
400
- // Per-Claude-session lens wins over the global env var, so concurrent
401
- // sessions can each be scoped to a different workspace. The global-config
402
- // fallback is resolved AGAINST THIS POD (envPod) — never send a workspace
403
- // that belongs to a different pod (the "Access denied to workspace" 403).
404
- workspaceId: resolveActiveLens()?.workspaceId ||
574
+ agentUserId,
575
+ // The global-config fallback is resolved AGAINST THIS POD (envPod) —
576
+ // never send a workspace that belongs to a different pod.
577
+ workspaceId: lens?.workspaceId ||
578
+ dirLens?.workspaceId ||
405
579
  envWorkspace ||
406
580
  getActiveWorkspaceIdForPod(findPodNameByUrl(envPod)),
407
- // Project is a peer lens, threaded exactly like workspaceId.
408
- projectId: resolveActiveLens()?.projectId || envProject || getActiveProjectId(),
581
+ // Project is a peer lens. Lens/dir-lens are already pod-qualified;
582
+ // env/global active project only when ActiveProjectBinding matches this pod.
583
+ // Write paths must use this resolveHubConfig output — never bare
584
+ // getActiveProjectId() / SYNAP_PROJECT_ID without the pod check.
585
+ projectId: resolveProjectIdForPod(envPod, {
586
+ lensProjectId: lens?.projectId,
587
+ dirLensProjectId: dirLens?.projectId,
588
+ envProject,
589
+ trustEnvProject: true,
590
+ }),
409
591
  scopes: envScopes ? envScopes.split(",") : undefined,
410
592
  };
411
593
  }
@@ -413,40 +595,144 @@ export async function resolveHubConfig(opts) {
413
595
  const config = getActivePodConfig();
414
596
  if (!config)
415
597
  throw new Error("No pod configured. Run: synap pods add");
598
+ // Same pod-qualification as step 3: a lens stamped with another pod must not
599
+ // short-circuit `getActiveWorkspaceId()`, which IS cross-pod-safe.
600
+ const activeLens = resolveActiveLensForPod(config.podUrl);
601
+ const activeDirLens = resolveDirectoryLensForPod(config.podUrl)?.lens;
416
602
  return {
417
603
  podUrl: config.podUrl,
418
604
  apiKey: config.hubApiKey,
419
- userId: config.agentUserId,
420
- workspaceId: resolveActiveLens()?.workspaceId || getActiveWorkspaceId(),
421
- projectId: resolveActiveLens()?.projectId || getActiveProjectId(),
605
+ // "cli": resolveUserId asks the pod. A profile's agentUserId is the key's
606
+ // principal (a computer or agent key), not who the pod acts as.
607
+ userId: "cli",
608
+ agentUserId: config.agentUserId || undefined,
609
+ // session lens › directory lens › global config. (No env rung here: this
610
+ // branch is only reached when SYNAP_POD_URL is unset.)
611
+ workspaceId: activeLens?.workspaceId || activeDirLens?.workspaceId || getActiveWorkspaceId(),
612
+ // Project: same pod-qualification as step 3 (no bare getActiveProjectId).
613
+ projectId: resolveProjectIdForPod(config.podUrl, {
614
+ lensProjectId: activeLens?.projectId,
615
+ dirLensProjectId: activeDirLens?.projectId,
616
+ }),
422
617
  };
423
618
  }
424
- const SESSION_FILE = ".synap-session";
425
- /** Read the active session ID for this terminal from CWD or env var. */
426
- export function readActiveSessionId() {
619
+ // ─── Active focus session ─────────────────────────────────────────────────────
620
+ //
621
+ // ONE store, two rungs of the SAME lens mechanism:
622
+ //
623
+ // env SYNAP_SESSION_ID › session lens (per Claude tab) › directory lens
624
+ //
625
+ // The directory rung supplies a session only OUTSIDE a Claude Code conversation
626
+ // (`effectiveDirectoryLens`): inside one, the session belongs to the
627
+ // conversation, never to the working tree.
628
+ //
629
+ // The retired legacy store was a per-CWD `.synap-session` file that the session
630
+ // lens already silently shadowed — two concurrent "current session" stores for
631
+ // one concept. It is now read exactly once, to migrate (below).
632
+ /** Legacy per-CWD session file — RETIRED as a store; read once to migrate. */
633
+ const LEGACY_SESSION_FILE = ".synap-session";
634
+ let legacySessionMigrationChecked = false;
635
+ /**
636
+ * One-time migration of a pre-existing `.synap-session` into the DIRECTORY lens.
637
+ *
638
+ * COMPAT DECISION — read-and-migrate, not a permanent read-only fallback. A
639
+ * fallback would keep two stores alive indefinitely, which is exactly the
640
+ * divergence being removed. Migrating is faithful: `.synap-session` was
641
+ * per-working-tree and durable, and the directory lens has precisely that
642
+ * lifetime and scope — so an already-attached session survives the upgrade for
643
+ * every terminal in that tree. The file is deleted afterwards so it can never
644
+ * shadow (or be shadowed by) the lens again.
645
+ *
646
+ * No `podUrl` is stamped: the legacy file carried no pod, and stamping one here
647
+ * would falsely pod-qualify whatever workspace/project the lens already holds.
648
+ */
649
+ function migrateLegacySessionFile() {
650
+ if (legacySessionMigrationChecked)
651
+ return;
652
+ legacySessionMigrationChecked = true;
653
+ const filePath = join(process.cwd(), LEGACY_SESSION_FILE);
654
+ if (!existsSync(filePath))
655
+ return;
656
+ let id;
657
+ try {
658
+ id = readFileSync(filePath, "utf8").trim() || undefined;
659
+ }
660
+ catch {
661
+ return;
662
+ }
663
+ try {
664
+ if (id)
665
+ writeDirectoryLens({ focusSessionId: id });
666
+ unlinkSync(filePath);
667
+ }
668
+ catch {
669
+ return; // best-effort — a read-only tree simply keeps the stale file
670
+ }
671
+ process.stderr.write(`[synap] .synap-session is deprecated${id ? ` — session ${id.slice(0, 8)}… migrated into .synap/lens.json` : " — removed (empty)"}.\n`);
672
+ }
673
+ /**
674
+ * The focus session every hub call should be tagged with.
675
+ *
676
+ * Both lens rungs are pod-qualified: a focus-session id is POD-LOCAL, so a lens
677
+ * stamped with another pod must not leak into `X-Session-Id`. (Lenses written
678
+ * before the stamp existed have no `podUrl` and match leniently — see
679
+ * `lensMatchesPod`.)
680
+ */
681
+ export function resolveActiveSessionId(podUrl) {
427
682
  const fromEnv = process.env.SYNAP_SESSION_ID;
428
683
  if (fromEnv)
429
684
  return fromEnv;
430
- const filePath = join(process.cwd(), SESSION_FILE);
431
- if (existsSync(filePath)) {
432
- return readFileSync(filePath, "utf8").trim() || undefined;
433
- }
434
- return undefined;
685
+ migrateLegacySessionFile();
686
+ const fromSessionLens = resolveActiveLensForPod(podUrl)?.focusSessionId;
687
+ if (fromSessionLens)
688
+ return fromSessionLens;
689
+ const dir = resolveDirectoryLensForPod(podUrl);
690
+ return dir ? effectiveDirectoryLens(dir.lens).focusSessionId : undefined;
435
691
  }
436
- /** Attach a session to this terminal — writes .synap-session in CWD. */
437
- export function writeActiveSessionId(sessionId) {
438
- writeFileSync(join(process.cwd(), SESSION_FILE), sessionId, "utf8");
692
+ /**
693
+ * Attach a focus session to this terminal.
694
+ *
695
+ * Inside Claude Code the SESSION lens wins — per-tab isolation is the point
696
+ * (tab A on session X, tab B on session Y, same directory). Outside it there is
697
+ * no `CLAUDE_CODE_SESSION_ID` to key on, so the DIRECTORY lens is the
698
+ * legitimate fallback: the same per-working-tree scope the retired
699
+ * `.synap-session` had. `attach` therefore never silently no-ops.
700
+ */
701
+ export function attachActiveSessionId(sessionId) {
702
+ const claudeSession = getClaudeSessionId();
703
+ if (claudeSession) {
704
+ writeLens(claudeSession, { focusSessionId: sessionId });
705
+ return "session-lens";
706
+ }
707
+ writeDirectoryLens({ focusSessionId: sessionId });
708
+ return "directory-lens";
439
709
  }
440
- /** Detach the active session from this terminal — removes .synap-session. */
441
- export function clearActiveSessionId() {
442
- const filePath = join(process.cwd(), SESSION_FILE);
443
- if (existsSync(filePath))
444
- unlinkSync(filePath);
710
+ /**
711
+ * Detach — clear the focus session from BOTH lens rungs, and return whatever
712
+ * was active. Clearing only the top rung would "detach" into the lower one,
713
+ * which reads as the command failing.
714
+ */
715
+ export function detachActiveSessionId(podUrl) {
716
+ const previous = resolveActiveSessionId(podUrl);
717
+ const claudeSession = getClaudeSessionId();
718
+ if (claudeSession)
719
+ clearLensField(claudeSession, "focusSessionId");
720
+ clearDirectoryLensField("focusSessionId");
721
+ return previous;
445
722
  }
446
- function sessionHeaders() {
447
- // Per-Claude-session lens wins; fall back to the legacy per-CWD .synap-session.
448
- const id = resolveActiveLens()?.focusSessionId || readActiveSessionId();
449
- return id ? { "X-Session-Id": id } : {};
723
+ /**
724
+ * Per-request headers: which client calls (`synap-cli/<version>`), the focus
725
+ * session this terminal is attached to, and the
726
+ * Claude Code conversation the call comes from (`conversation-headers.ts`) — so
727
+ * the work it lands can be traced back to, and reopened in, that conversation.
728
+ */
729
+ function sessionHeaders(cfg) {
730
+ const id = resolveActiveSessionId(cfg?.podUrl);
731
+ return {
732
+ ...clientHeader("synap-cli"),
733
+ ...conversationHeaders(getClaudeSessionId()),
734
+ ...(id ? { "X-Session-Id": id } : {}),
735
+ };
450
736
  }
451
737
  /** Max 429 retries for bulk import (each waits up to the rate-limit window). */
452
738
  const HUB_429_MAX_RETRIES = 12;
@@ -530,9 +816,11 @@ async function hubFetchWithRetry(url, init, label, timeoutMs) {
530
816
  await acquireHubSlot();
531
817
  let res;
532
818
  try {
533
- res = await fetch(url, init);
819
+ res = await podFetch(url, init);
534
820
  }
535
821
  catch (err) {
822
+ if (err instanceof PodMovedError)
823
+ throw err;
536
824
  throw networkError(err, url, labelMethod, labelPath, timeoutMs);
537
825
  }
538
826
  if (res.status !== 429)
@@ -548,22 +836,36 @@ async function hubFetchWithRetry(url, init, label, timeoutMs) {
548
836
  await sleep(wait);
549
837
  }
550
838
  }
551
- export async function hubGet(path, params, cfg) {
839
+ export async function hubGet(path, params, cfg,
840
+ // Default 15s suits reads. A long-poll door (`/answers/wait`) holds the
841
+ // request open for its own `timeoutSeconds`; its caller passes more.
842
+ timeoutMs = 15_000) {
552
843
  const url = new URL(`${cfg.podUrl}/api/hub${path}`);
553
844
  for (const [k, v] of Object.entries(params)) {
554
845
  if (v !== undefined)
555
846
  url.searchParams.set(k, String(v));
556
847
  }
557
848
  const res = await hubFetchWithRetry(url.toString(), {
558
- headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
559
- signal: AbortSignal.timeout(15_000),
560
- }, `GET ${path}`, 15_000);
849
+ headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
850
+ signal: AbortSignal.timeout(timeoutMs),
851
+ }, `GET ${path}`, timeoutMs);
561
852
  if (!res.ok) {
562
853
  const body = await res.text().catch(() => "");
563
854
  throw hubError("GET", path, res.status, body, undefined, cfg);
564
855
  }
565
856
  return res.json();
566
857
  }
858
+ /**
859
+ * The workspace a failing request ACTUALLY carried. A caller may override the
860
+ * configured lens per-command (e.g. `--workspace <id>`), and naming the config's
861
+ * workspace in that error sends the reader chasing the wrong id.
862
+ */
863
+ function errorWorkspace(body, cfg) {
864
+ const sent = body && typeof body === "object" && "workspaceId" in body
865
+ ? body.workspaceId
866
+ : undefined;
867
+ return typeof sent === "string" && sent.length > 0 ? sent : cfg.workspaceId;
868
+ }
567
869
  export async function hubPost(path, body, cfg,
568
870
  // Default 15s suits CRUD doors. Capability runs invoke external provider APIs
569
871
  // (often several sequential calls) and legitimately take longer — callers
@@ -574,14 +876,17 @@ timeoutMs = 15_000) {
574
876
  headers: {
575
877
  Authorization: `Bearer ${cfg.apiKey}`,
576
878
  "Content-Type": "application/json",
577
- ...sessionHeaders(),
879
+ ...sessionHeaders(cfg),
578
880
  },
579
881
  body: JSON.stringify(body),
580
882
  signal: AbortSignal.timeout(timeoutMs),
581
883
  }, `POST ${path}`, timeoutMs);
582
884
  if (!res.ok) {
583
885
  const bodyText = await res.text().catch(() => "");
584
- throw hubError("POST", path, res.status, bodyText, undefined, cfg);
886
+ throw hubError("POST", path, res.status, bodyText, undefined, {
887
+ podUrl: cfg.podUrl,
888
+ workspaceId: errorWorkspace(body, cfg),
889
+ });
585
890
  }
586
891
  return res.json();
587
892
  }
@@ -595,7 +900,7 @@ timeoutMs = 15_000) {
595
900
  export async function hubPostMultipart(path, form, cfg, timeoutMs = 120_000) {
596
901
  const res = await hubFetchWithRetry(`${cfg.podUrl}/api/hub${path}`, {
597
902
  method: "POST",
598
- headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
903
+ headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
599
904
  body: form,
600
905
  signal: AbortSignal.timeout(timeoutMs),
601
906
  }, `POST multipart ${path}`, timeoutMs);
@@ -617,14 +922,16 @@ export async function hubPostMultipartRetryable(path, buildForm, cfg, timeoutMs
617
922
  const url = `${cfg.podUrl}/api/hub${path}`;
618
923
  let res;
619
924
  try {
620
- res = await fetch(url, {
925
+ res = await podFetch(url, {
621
926
  method: "POST",
622
- headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
927
+ headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
623
928
  body: form,
624
929
  signal: AbortSignal.timeout(timeoutMs),
625
930
  });
626
931
  }
627
932
  catch (err) {
933
+ if (err instanceof PodMovedError)
934
+ throw err;
628
935
  throw networkError(err, url, "POST multipart", path, timeoutMs);
629
936
  }
630
937
  if (res.status !== 429) {
@@ -649,19 +956,24 @@ export async function hubPatch(path, body, cfg) {
649
956
  const url = `${cfg.podUrl}/api/hub${path}`;
650
957
  let res;
651
958
  try {
652
- res = await fetch(url, {
959
+ res = await podFetch(url, {
653
960
  method: "PATCH",
654
- headers: { Authorization: `Bearer ${cfg.apiKey}`, "Content-Type": "application/json", ...sessionHeaders() },
961
+ headers: { Authorization: `Bearer ${cfg.apiKey}`, "Content-Type": "application/json", ...sessionHeaders(cfg) },
655
962
  body: JSON.stringify(body),
656
963
  signal: AbortSignal.timeout(15_000),
657
964
  });
658
965
  }
659
966
  catch (err) {
967
+ if (err instanceof PodMovedError)
968
+ throw err;
660
969
  throw networkError(err, url, "PATCH", path, 15_000);
661
970
  }
662
971
  if (!res.ok) {
663
972
  const bodyText = await res.text().catch(() => "");
664
- throw hubError("PATCH", path, res.status, bodyText, undefined, cfg);
973
+ throw hubError("PATCH", path, res.status, bodyText, undefined, {
974
+ podUrl: cfg.podUrl,
975
+ workspaceId: errorWorkspace(body, cfg),
976
+ });
665
977
  }
666
978
  return res.json();
667
979
  }
@@ -669,13 +981,15 @@ export async function hubDelete(path, cfg) {
669
981
  const url = `${cfg.podUrl}/api/hub${path}`;
670
982
  let res;
671
983
  try {
672
- res = await fetch(url, {
984
+ res = await podFetch(url, {
673
985
  method: "DELETE",
674
- headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
986
+ headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
675
987
  signal: AbortSignal.timeout(15_000),
676
988
  });
677
989
  }
678
990
  catch (err) {
991
+ if (err instanceof PodMovedError)
992
+ throw err;
679
993
  throw networkError(err, url, "DELETE", path, 15_000);
680
994
  }
681
995
  if (!res.ok) {
@@ -696,5 +1010,5 @@ export function makeHubClient(cfg) {
696
1010
  workspaceId: cfg.workspaceId,
697
1011
  });
698
1012
  }
699
- export { HubRestClient } from "@synap/hub-rest-client";
1013
+ export { HubRestClient } from "@synap-core/hub-rest-client";
700
1014
  //# sourceMappingURL=hub-client.js.map