@synap-core/cli 1.12.2 → 1.13.1-next.20261102.ec3d166

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 (354) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -11
  3. package/dist/build-info.json +2 -3
  4. package/dist/commands/agents.d.ts +0 -1
  5. package/dist/commands/agents.js +13 -3
  6. package/dist/commands/agents.js.map +1 -1
  7. package/dist/commands/app.js +2 -1
  8. package/dist/commands/app.js.map +1 -1
  9. package/dist/commands/automation.js +46 -41
  10. package/dist/commands/automation.js.map +1 -1
  11. package/dist/commands/box.d.ts +42 -0
  12. package/dist/commands/box.js +89 -0
  13. package/dist/commands/box.js.map +1 -0
  14. package/dist/commands/bridge-setup.js +3 -1
  15. package/dist/commands/bridge-setup.js.map +1 -1
  16. package/dist/commands/capability.d.ts +87 -1
  17. package/dist/commands/capability.js +174 -64
  18. package/dist/commands/capability.js.map +1 -1
  19. package/dist/commands/close-questions.d.ts +27 -0
  20. package/dist/commands/close-questions.js +49 -0
  21. package/dist/commands/close-questions.js.map +1 -0
  22. package/dist/commands/close-questions.test.d.ts +1 -0
  23. package/dist/commands/close-questions.test.js +52 -0
  24. package/dist/commands/close-questions.test.js.map +1 -0
  25. package/dist/commands/connect.d.ts +1 -3
  26. package/dist/commands/connect.js +2 -2
  27. package/dist/commands/connect.js.map +1 -1
  28. package/dist/commands/context.js +72 -43
  29. package/dist/commands/context.js.map +1 -1
  30. package/dist/commands/data-extra.d.ts +13 -0
  31. package/dist/commands/data-extra.js +35 -6
  32. package/dist/commands/data-extra.js.map +1 -1
  33. package/dist/commands/data.js +2 -1
  34. package/dist/commands/data.js.map +1 -1
  35. package/dist/commands/doctor.d.ts +39 -0
  36. package/dist/commands/doctor.js +136 -14
  37. package/dist/commands/doctor.js.map +1 -1
  38. package/dist/commands/hooks.d.ts +19 -0
  39. package/dist/commands/hooks.js +70 -0
  40. package/dist/commands/hooks.js.map +1 -0
  41. package/dist/commands/import.d.ts +25 -0
  42. package/dist/commands/import.js +98 -12
  43. package/dist/commands/import.js.map +1 -1
  44. package/dist/commands/init.d.ts +14 -0
  45. package/dist/commands/init.js +63 -21
  46. package/dist/commands/init.js.map +1 -1
  47. package/dist/commands/keys-request.d.ts +48 -0
  48. package/dist/commands/keys-request.js +236 -0
  49. package/dist/commands/keys-request.js.map +1 -0
  50. package/dist/commands/keys-request.test.d.ts +1 -0
  51. package/dist/commands/keys-request.test.js +176 -0
  52. package/dist/commands/keys-request.test.js.map +1 -0
  53. package/dist/commands/knowledge.d.ts +6 -0
  54. package/dist/commands/knowledge.js +13 -3
  55. package/dist/commands/knowledge.js.map +1 -1
  56. package/dist/commands/lens.js +2 -1
  57. package/dist/commands/lens.js.map +1 -1
  58. package/dist/commands/market-authoring.js +1 -1
  59. package/dist/commands/market-authoring.js.map +1 -1
  60. package/dist/commands/market.d.ts +61 -4
  61. package/dist/commands/market.js +106 -48
  62. package/dist/commands/market.js.map +1 -1
  63. package/dist/commands/mcp.d.ts +1 -1
  64. package/dist/commands/mcp.js +14 -9
  65. package/dist/commands/mcp.js.map +1 -1
  66. package/dist/commands/observe.category.test.d.ts +1 -0
  67. package/dist/commands/observe.category.test.js +59 -0
  68. package/dist/commands/observe.category.test.js.map +1 -0
  69. package/dist/commands/observe.d.ts +3 -0
  70. package/dist/commands/observe.js +7 -5
  71. package/dist/commands/observe.js.map +1 -1
  72. package/dist/commands/observe.title.test.d.ts +1 -0
  73. package/dist/commands/observe.title.test.js +37 -0
  74. package/dist/commands/observe.title.test.js.map +1 -0
  75. package/dist/commands/playbook.js +9 -0
  76. package/dist/commands/playbook.js.map +1 -1
  77. package/dist/commands/pods.d.ts +6 -1
  78. package/dist/commands/pods.js +38 -17
  79. package/dist/commands/pods.js.map +1 -1
  80. package/dist/commands/proposals-actions.js +7 -1
  81. package/dist/commands/proposals-actions.js.map +1 -1
  82. package/dist/commands/proton-setup.js +3 -1
  83. package/dist/commands/proton-setup.js.map +1 -1
  84. package/dist/commands/providers.d.ts +22 -6
  85. package/dist/commands/providers.js +39 -32
  86. package/dist/commands/providers.js.map +1 -1
  87. package/dist/commands/raycast.d.ts +15 -0
  88. package/dist/commands/raycast.js +36 -10
  89. package/dist/commands/raycast.js.map +1 -1
  90. package/dist/commands/session-feed-wait.d.ts +48 -0
  91. package/dist/commands/session-feed-wait.js +261 -0
  92. package/dist/commands/session-feed-wait.js.map +1 -0
  93. package/dist/commands/session-post.d.ts +40 -0
  94. package/dist/commands/session-post.js +96 -0
  95. package/dist/commands/session-post.js.map +1 -0
  96. package/dist/commands/session-wait.d.ts +9 -4
  97. package/dist/commands/session-wait.js +20 -5
  98. package/dist/commands/session-wait.js.map +1 -1
  99. package/dist/commands/sessions.d.ts +2 -0
  100. package/dist/commands/sessions.js +23 -0
  101. package/dist/commands/sessions.js.map +1 -1
  102. package/dist/commands/skills-commands.d.ts +2 -0
  103. package/dist/commands/skills-commands.js +9 -1
  104. package/dist/commands/skills-commands.js.map +1 -1
  105. package/dist/commands/skills-commands.test.d.ts +1 -0
  106. package/dist/commands/skills-commands.test.js +57 -0
  107. package/dist/commands/skills-commands.test.js.map +1 -0
  108. package/dist/commands/status.js +13 -7
  109. package/dist/commands/status.js.map +1 -1
  110. package/dist/commands/statusline.backoff.test.js +13 -3
  111. package/dist/commands/statusline.backoff.test.js.map +1 -1
  112. package/dist/commands/statusline.d.ts +104 -9
  113. package/dist/commands/statusline.js +392 -148
  114. package/dist/commands/statusline.js.map +1 -1
  115. package/dist/commands/statusline.line2.test.d.ts +15 -0
  116. package/dist/commands/statusline.line2.test.js +226 -0
  117. package/dist/commands/statusline.line2.test.js.map +1 -0
  118. package/dist/commands/statusline.needs-you.test.d.ts +1 -0
  119. package/dist/commands/statusline.needs-you.test.js +17 -0
  120. package/dist/commands/statusline.needs-you.test.js.map +1 -0
  121. package/dist/commands/statusline.now.test.d.ts +10 -0
  122. package/dist/commands/statusline.now.test.js +250 -0
  123. package/dist/commands/statusline.now.test.js.map +1 -0
  124. package/dist/commands/tracks.d.ts +68 -6
  125. package/dist/commands/tracks.js +272 -16
  126. package/dist/commands/tracks.js.map +1 -1
  127. package/dist/commands/upload.js +12 -0
  128. package/dist/commands/upload.js.map +1 -1
  129. package/dist/commands/wait.d.ts +124 -0
  130. package/dist/commands/wait.js +384 -0
  131. package/dist/commands/wait.js.map +1 -0
  132. package/dist/commands/wait.test.d.ts +8 -0
  133. package/dist/commands/wait.test.js +308 -0
  134. package/dist/commands/wait.test.js.map +1 -0
  135. package/dist/commands/whoami.d.ts +7 -0
  136. package/dist/commands/whoami.js +17 -0
  137. package/dist/commands/whoami.js.map +1 -1
  138. package/dist/commands/worker-accounts.d.ts +149 -0
  139. package/dist/commands/worker-accounts.js +339 -0
  140. package/dist/commands/worker-accounts.js.map +1 -0
  141. package/dist/commands/worker-accounts.test.d.ts +1 -0
  142. package/dist/commands/worker-accounts.test.js +224 -0
  143. package/dist/commands/worker-accounts.test.js.map +1 -0
  144. package/dist/commands/worker-github-app.d.ts +63 -0
  145. package/dist/commands/worker-github-app.js +161 -0
  146. package/dist/commands/worker-github-app.js.map +1 -0
  147. package/dist/commands/worker-github-app.test.d.ts +1 -0
  148. package/dist/commands/worker-github-app.test.js +102 -0
  149. package/dist/commands/worker-github-app.test.js.map +1 -0
  150. package/dist/commands/worker-github-broker.d.ts +39 -0
  151. package/dist/commands/worker-github-broker.js +106 -0
  152. package/dist/commands/worker-github-broker.js.map +1 -0
  153. package/dist/commands/worker-github-broker.test.d.ts +1 -0
  154. package/dist/commands/worker-github-broker.test.js +90 -0
  155. package/dist/commands/worker-github-broker.test.js.map +1 -0
  156. package/dist/commands/worker-runs.test.d.ts +1 -0
  157. package/dist/commands/worker-runs.test.js +150 -0
  158. package/dist/commands/worker-runs.test.js.map +1 -0
  159. package/dist/commands/worker-worktrees.d.ts +34 -0
  160. package/dist/commands/worker-worktrees.js +87 -0
  161. package/dist/commands/worker-worktrees.js.map +1 -0
  162. package/dist/commands/worker.d.ts +406 -10
  163. package/dist/commands/worker.js +1180 -126
  164. package/dist/commands/worker.js.map +1 -1
  165. package/dist/commands/worker.test.js +588 -11
  166. package/dist/commands/worker.test.js.map +1 -1
  167. package/dist/index.js +220 -28
  168. package/dist/index.js.map +1 -1
  169. package/dist/lib/agent-door.d.ts +1 -3
  170. package/dist/lib/agent-door.js +10 -7
  171. package/dist/lib/agent-door.js.map +1 -1
  172. package/dist/lib/agents-config.d.ts +9 -0
  173. package/dist/lib/agents-config.js +20 -0
  174. package/dist/lib/agents-config.js.map +1 -1
  175. package/dist/lib/atomic-file.d.ts +20 -1
  176. package/dist/lib/atomic-file.js +100 -2
  177. package/dist/lib/atomic-file.js.map +1 -1
  178. package/dist/lib/auth.d.ts +28 -2
  179. package/dist/lib/auth.js +52 -12
  180. package/dist/lib/auth.js.map +1 -1
  181. package/dist/lib/box/egress.d.ts +82 -0
  182. package/dist/lib/box/egress.js +264 -0
  183. package/dist/lib/box/egress.js.map +1 -0
  184. package/dist/lib/box/evidence.d.ts +86 -0
  185. package/dist/lib/box/evidence.js +121 -0
  186. package/dist/lib/box/evidence.js.map +1 -0
  187. package/dist/lib/box/job-server.d.ts +72 -0
  188. package/dist/lib/box/job-server.js +250 -0
  189. package/dist/lib/box/job-server.js.map +1 -0
  190. package/dist/lib/box/provisioner.d.ts +83 -0
  191. package/dist/lib/box/provisioner.js +312 -0
  192. package/dist/lib/box/provisioner.js.map +1 -0
  193. package/dist/lib/box/proxmox.d.ts +112 -0
  194. package/dist/lib/box/proxmox.js +211 -0
  195. package/dist/lib/box/proxmox.js.map +1 -0
  196. package/dist/lib/box/wire.d.ts +98 -0
  197. package/dist/lib/box/wire.js +70 -0
  198. package/dist/lib/box/wire.js.map +1 -0
  199. package/dist/lib/bridge-invocation.test.d.ts +1 -0
  200. package/dist/lib/bridge-invocation.test.js +75 -0
  201. package/dist/lib/bridge-invocation.test.js.map +1 -0
  202. package/dist/lib/bridge-status.d.ts +144 -0
  203. package/dist/lib/bridge-status.js +227 -0
  204. package/dist/lib/bridge-status.js.map +1 -0
  205. package/dist/lib/bridge-status.test.d.ts +6 -0
  206. package/dist/lib/bridge-status.test.js +197 -0
  207. package/dist/lib/bridge-status.test.js.map +1 -0
  208. package/dist/lib/client-header.test.d.ts +1 -0
  209. package/dist/lib/client-header.test.js +103 -0
  210. package/dist/lib/client-header.test.js.map +1 -0
  211. package/dist/lib/conversation-headers.d.ts +89 -9
  212. package/dist/lib/conversation-headers.js +156 -3
  213. package/dist/lib/conversation-headers.js.map +1 -1
  214. package/dist/lib/conversation-headers.test.d.ts +1 -0
  215. package/dist/lib/conversation-headers.test.js +78 -0
  216. package/dist/lib/conversation-headers.test.js.map +1 -0
  217. package/dist/lib/credential-class.d.ts +40 -2
  218. package/dist/lib/credential-class.js +48 -6
  219. package/dist/lib/credential-class.js.map +1 -1
  220. package/dist/lib/device-connect.d.ts +45 -0
  221. package/dist/lib/device-connect.js +60 -0
  222. package/dist/lib/device-connect.js.map +1 -1
  223. package/dist/lib/exporter-coverage.d.ts +1 -1
  224. package/dist/lib/exporter-coverage.js +1 -1
  225. package/dist/lib/goal-words.d.ts +38 -0
  226. package/dist/lib/goal-words.js +115 -0
  227. package/dist/lib/goal-words.js.map +1 -0
  228. package/dist/lib/hub-client.d.ts +87 -8
  229. package/dist/lib/hub-client.js +205 -70
  230. package/dist/lib/hub-client.js.map +1 -1
  231. package/dist/lib/init-handoff.d.ts +8 -0
  232. package/dist/lib/init-handoff.js +9 -2
  233. package/dist/lib/init-handoff.js.map +1 -1
  234. package/dist/lib/key-locations.d.ts +3 -3
  235. package/dist/lib/key-locations.js +3 -39
  236. package/dist/lib/key-locations.js.map +1 -1
  237. package/dist/lib/key-source.d.ts +3 -1
  238. package/dist/lib/key-source.js +7 -4
  239. package/dist/lib/key-source.js.map +1 -1
  240. package/dist/lib/knowledge-title.d.ts +3 -2
  241. package/dist/lib/knowledge-title.js +3 -2
  242. package/dist/lib/knowledge-title.js.map +1 -1
  243. package/dist/lib/machine-instance.d.ts +7 -5
  244. package/dist/lib/machine-instance.js +7 -5
  245. package/dist/lib/machine-instance.js.map +1 -1
  246. package/dist/lib/mcp-bridge.d.ts +50 -0
  247. package/dist/lib/mcp-bridge.js +232 -11
  248. package/dist/lib/mcp-bridge.js.map +1 -1
  249. package/dist/lib/mcp-bridge.test.js +224 -4
  250. package/dist/lib/mcp-bridge.test.js.map +1 -1
  251. package/dist/lib/mcp-call.d.ts +5 -1
  252. package/dist/lib/mcp-call.js +7 -6
  253. package/dist/lib/mcp-call.js.map +1 -1
  254. package/dist/lib/mcp-connector-url.test.d.ts +1 -0
  255. package/dist/lib/mcp-connector-url.test.js +13 -0
  256. package/dist/lib/mcp-connector-url.test.js.map +1 -0
  257. package/dist/lib/mcp-snippets.d.ts +16 -0
  258. package/dist/lib/mcp-snippets.js +20 -2
  259. package/dist/lib/mcp-snippets.js.map +1 -1
  260. package/dist/lib/now-line.d.ts +66 -0
  261. package/dist/lib/now-line.js +193 -0
  262. package/dist/lib/now-line.js.map +1 -0
  263. package/dist/lib/now-line.test.d.ts +5 -0
  264. package/dist/lib/now-line.test.js +177 -0
  265. package/dist/lib/now-line.test.js.map +1 -0
  266. package/dist/lib/package-updates.d.ts +66 -0
  267. package/dist/lib/package-updates.js +76 -0
  268. package/dist/lib/package-updates.js.map +1 -0
  269. package/dist/lib/pod-moved.d.ts +70 -0
  270. package/dist/lib/pod-moved.js +146 -0
  271. package/dist/lib/pod-moved.js.map +1 -0
  272. package/dist/lib/pod-reach.d.ts +39 -0
  273. package/dist/lib/pod-reach.js +117 -0
  274. package/dist/lib/pod-reach.js.map +1 -0
  275. package/dist/lib/pod-reach.test.d.ts +1 -0
  276. package/dist/lib/pod-reach.test.js +67 -0
  277. package/dist/lib/pod-reach.test.js.map +1 -0
  278. package/dist/lib/pod.d.ts +8 -4
  279. package/dist/lib/pod.js +12 -10
  280. package/dist/lib/pod.js.map +1 -1
  281. package/dist/lib/register-claude-code-mcp.test.d.ts +1 -0
  282. package/dist/lib/register-claude-code-mcp.test.js +97 -0
  283. package/dist/lib/register-claude-code-mcp.test.js.map +1 -0
  284. package/dist/lib/review-credential.js +3 -1
  285. package/dist/lib/review-credential.js.map +1 -1
  286. package/dist/lib/safe-open.d.ts +5 -3
  287. package/dist/lib/safe-open.js +9 -8
  288. package/dist/lib/safe-open.js.map +1 -1
  289. package/dist/lib/self-host.d.ts +17 -0
  290. package/dist/lib/self-host.js +24 -0
  291. package/dist/lib/self-host.js.map +1 -0
  292. package/dist/lib/session-feed.d.ts +185 -0
  293. package/dist/lib/session-feed.js +257 -0
  294. package/dist/lib/session-feed.js.map +1 -0
  295. package/dist/lib/targets.d.ts +52 -3
  296. package/dist/lib/targets.js +150 -209
  297. package/dist/lib/targets.js.map +1 -1
  298. package/dist/lib/term.d.ts +24 -0
  299. package/dist/lib/term.js +52 -0
  300. package/dist/lib/term.js.map +1 -0
  301. package/dist/lib/term.test.d.ts +1 -0
  302. package/dist/lib/term.test.js +29 -0
  303. package/dist/lib/term.test.js.map +1 -0
  304. package/dist/lib/transcript-index.d.ts +94 -0
  305. package/dist/lib/transcript-index.js +321 -0
  306. package/dist/lib/transcript-index.js.map +1 -0
  307. package/dist/lib/transcript-index.test.d.ts +10 -0
  308. package/dist/lib/transcript-index.test.js +200 -0
  309. package/dist/lib/transcript-index.test.js.map +1 -0
  310. package/dist/lib/wait-help.d.ts +11 -0
  311. package/dist/lib/wait-help.js +25 -0
  312. package/dist/lib/wait-help.js.map +1 -0
  313. package/dist/lib/wait-state.d.ts +57 -0
  314. package/dist/lib/wait-state.js +114 -0
  315. package/dist/lib/wait-state.js.map +1 -0
  316. package/dist/lib/wake-hook-settings.d.ts +57 -0
  317. package/dist/lib/wake-hook-settings.js +153 -0
  318. package/dist/lib/wake-hook-settings.js.map +1 -0
  319. package/dist/lib/wake-state.d.ts +65 -0
  320. package/dist/lib/wake-state.js +122 -0
  321. package/dist/lib/wake-state.js.map +1 -0
  322. package/package.json +5 -2
  323. package/skills/README.md +1 -1
  324. package/skills/agent-os/SKILL.md +8 -8
  325. package/skills/synap/SKILL.md +178 -88
  326. package/skills/synap/code-skills.md +84 -0
  327. package/skills/synap/concepts.md +38 -20
  328. package/skills/synap/creative-loop.md +1 -1
  329. package/skills/synap/document-embeds.md +17 -1
  330. package/skills/synap/focus-sessions.md +17 -8
  331. package/skills/synap/from-intent.md +33 -17
  332. package/skills/synap/governance-rules.md +3 -1
  333. package/skills/synap/lenses.md +4 -4
  334. package/skills/synap/linking.md +2 -2
  335. package/skills/synap/mental-model.md +1 -1
  336. package/skills/synap/operating-mode-rules.md +1 -1
  337. package/skills/synap/quick-reference.md +2 -1
  338. package/skills/synap/reflexes.md +8 -8
  339. package/skills/synap/viewframe-cells.md +1 -1
  340. package/skills/synap/work-model.md +24 -10
  341. package/skills/synap/workspace-design.md +5 -5
  342. package/skills/synap/writes.md +16 -9
  343. package/skills/synap/writing-titles.md +7 -0
  344. package/skills/synap-app/SKILL.md +148 -66
  345. package/skills/synap-app/creating-the-base-app.md +12 -7
  346. package/skills/synap-app/governance.md +27 -16
  347. package/skills/synap-app/install-commands.md +18 -9
  348. package/skills/synap-app/overview.md +16 -9
  349. package/skills/synap-app/using-the-sdk.md +75 -25
  350. package/skills/synap-market/SKILL.md +1 -1
  351. package/skills/synap-market/governance-and-catalog.md +1 -1
  352. package/skills/synap-schema/SKILL.md +24 -21
  353. package/skills/synap-schema/intent-vocabulary-ssot.md +24 -21
  354. package/templates/box/runner.py +206 -0
@@ -4,7 +4,7 @@
4
4
  * A Capability is a named, installable PACK of what you (and your AI) can do
5
5
  * (e.g. "Google Workspace", "Discord Bot"). Tools = its connection; skills = its
6
6
  * verbs; templates = the catalog. The user only ever lists, adds, and enables a
7
- * PACK — never a bare tool or skill (CAPABILITIES-NORTH-STAR.md).
7
+ * PACK — never a bare tool or skill (CAPABILITIES-NORTH-STAR, team pod).
8
8
  *
9
9
  * A capability VERB is a runnable action backed by a skill (verbId = skill name).
10
10
  * `run` is the launcher: it parses arbitrary `--key value` flags into the skill's
@@ -17,7 +17,7 @@ import ora from "ora";
17
17
  import prompts from "prompts";
18
18
  import { openExternalUrl } from "../lib/safe-open.js";
19
19
  import { readFileSync } from "node:fs";
20
- import { resolveHubConfig, hubGet, hubPost, hubDelete, HubError, renderHubError } from "../lib/hub-client.js";
20
+ import { resolveHubConfig, hubGet, hubPost, hubPatch, hubDelete, HubError, renderHubError } from "../lib/hub-client.js";
21
21
  import { unwrapList } from "../lib/unwrapList.js";
22
22
  import { log } from "../utils/logger.js";
23
23
  // ── Helpers ───────────────────────────────────────────────────────────────────
@@ -499,8 +499,9 @@ async function connectProviderOAuth(cfg, provider, workspaceId, ctx) {
499
499
  }
500
500
  /**
501
501
  * Ensure a capability's required connection is satisfied. Provider (nango://) →
502
- * OAuth open + poll. Vault (vault://) → prompt for the key, POST /vault/secrets.
503
- * Returns true when the connection is (now) satisfied, false otherwise.
502
+ * OAuth open + poll. Vault (vault://) → rotate the linked secret in place, or
503
+ * apply the template with the key as a param. Returns true when the connection
504
+ * is (now) satisfied, false otherwise.
504
505
  */
505
506
  async function ensureConnection(cfg, workspaceId, card,
506
507
  // Force a re-auth even when the card reports `state === "connected"`. The card
@@ -518,12 +519,20 @@ forceReconnect = false) {
518
519
  // own diagnosis (which of not_declared / unauthenticated / unreachable, and
519
520
  // the remedy). Only OPENING A BROWSER is doomed, and that is already guarded
520
521
  // below. Short-circuiting on the card's cached state costs the real message.
521
- // ── Vault credential — prompt + re-apply WITH params ──────────────────────
522
- // The correct wiring: apply the template with the credential as a param, so the
523
- // governed applier stores the secret AND links it into the connection tool's
524
- // credentialRef (pod-wide, for a pod-scoped cap). A bare /vault/secrets write
525
- // creates an orphan secret that is NOT linked to the tool — the old bug.
522
+ // ── Vault credential ──────────────────────────────────────────────────────
523
+ // Every write here must land on the secret the capability's tools actually
524
+ // read. A bare /vault/secrets write creates an orphan secret NOT linked to the
525
+ // tool's credentialRef: the CLI used to fall back to it and print success
526
+ // while every run kept the old key (unipile-linkedin, 2026-10-27).
526
527
  if (conn.kind === "vault") {
528
+ // Installed with a live key → rotate THAT secret in place. The tools keep
529
+ // their vault://<id>, so the new value is what the next run reads; no
530
+ // template re-apply, so optional install params are not blanked either.
531
+ if (card.id && conn.account && conn.state === "connected") {
532
+ return rotateLinkedSecret(cfg, card, card.id, conn.account);
533
+ }
534
+ // No live key yet → apply the template WITH params: the governed applier
535
+ // stores the secret AND links it into the tool's credentialRef.
527
536
  const hasParams = (card.installParams ?? []).some((p) => p.required);
528
537
  if (hasParams) {
529
538
  const collected = await collectInstallParams(card);
@@ -531,30 +540,12 @@ forceReconnect = false) {
531
540
  return false;
532
541
  return applyTemplateWithParams(cfg, card, workspaceId, collected, "Connecting");
533
542
  }
534
- // Fallback (older pod without install-params on the card): store the secret
535
- // directly. May need manual linking if the tool's credentialRef doesn't match.
536
- const service = conn.provider ?? card.key;
537
- const answer = await prompts({
538
- type: "password",
539
- name: "value",
540
- message: `Paste the ${chalk.bold(card.name)} credential (${service})`,
541
- });
542
- const value = answer.value;
543
- if (!value) {
544
- log.dim("Cancelled — no credential entered.");
545
- return false;
546
- }
547
- const spinner = ora({ text: "Storing credential in the vault…", color: "cyan" }).start();
548
- try {
549
- await hubPost("/vault/secrets", { name: `${card.name} credential`, value, service, type: "credential", workspaceId }, cfg);
550
- spinner.succeed(chalk.green(`Stored ${chalk.bold(service)} credential.`));
551
- return true;
552
- }
553
- catch (err) {
554
- spinner.fail(chalk.red("Failed to store credential"));
555
- renderHubError(err);
556
- return false;
557
- }
543
+ log.error(`Can't connect ${card.name} from here.`);
544
+ log.dim(card.id
545
+ ? "The pod lists no install params for it and its tools point at no vault key that still exists."
546
+ : "The pod lists no install params for it.");
547
+ log.dim(`Re-install it with its key: synap cap add "${card.key}"`);
548
+ return false;
558
549
  }
559
550
  // ── Provider OAuth (nango://) — open + poll ───────────────────────────────
560
551
  const provider = conn.provider ?? card.key;
@@ -566,6 +557,36 @@ forceReconnect = false) {
566
557
  forceReauth: forceReconnect,
567
558
  });
568
559
  }
560
+ /**
561
+ * Replace the value of the vault secret a capability's tools point at
562
+ * (`secretId` = the card's `connection.account`, the id in their
563
+ * `vault://<id>`), through the capability credentials door. The row keeps its
564
+ * id, so every reference to it now resolves to the new value and no second
565
+ * secret is written.
566
+ */
567
+ async function rotateLinkedSecret(cfg, card, capabilityId, secretId) {
568
+ const answer = await prompts({
569
+ type: "password",
570
+ name: "value",
571
+ message: `Paste the new ${chalk.bold(card.name)} key`,
572
+ });
573
+ const value = answer.value;
574
+ if (!value) {
575
+ log.dim("Cancelled — no key entered. The current key is unchanged.");
576
+ return false;
577
+ }
578
+ const spinner = ora({ text: `Replacing the ${chalk.bold(card.name)} key…`, color: "cyan" }).start();
579
+ try {
580
+ await hubPatch(`/capabilities/${capabilityId}/credentials/${secretId}`, { value }, cfg);
581
+ spinner.succeed(chalk.green(`Replaced the ${chalk.bold(card.name)} key. Its tools use the new one from the next run.`));
582
+ return true;
583
+ }
584
+ catch (err) {
585
+ spinner.fail(chalk.red(`Couldn't replace the ${card.name} key — the current key is unchanged`));
586
+ renderHubError(err);
587
+ return false;
588
+ }
589
+ }
569
590
  /**
570
591
  * Prompt for a template's REQUIRED install params (masked for secrets). Returns
571
592
  * the params map, or null if the user cancelled. Empty map when nothing required.
@@ -614,9 +635,12 @@ async function applyTemplateWithParams(cfg, card, workspaceId, params, verb) {
614
635
  }
615
636
  catch (err) {
616
637
  // Non-fatal — the connection itself succeeded; surface so it isn't
617
- // silently stuck in the old "not approved" trap.
618
- log.warn(`Connected, but couldn't auto-approve tool "${t.name}": ${err.message}`);
619
- log.dim(`Approve it manually if runs fail with "Capability is not approved".`);
638
+ // silently stuck in the old "not approved" trap. Founder dogfood
639
+ // 2026-10-30 (Hetzner Cloud): this approve timed out, the old hint
640
+ // named no command, and every run was refused "installed but not
641
+ // enabled" while `cap enable` (verbs only) printed success.
642
+ log.warn(`Connected, but its access to "${t.name}" is still off: ${err.message}`);
643
+ log.dim(`Nothing it does can run until it is on. Turn it on: synap cap enable "${card.name}"`);
620
644
  }
621
645
  }
622
646
  spinner.succeed(chalk.green(`${verb === "Adding" ? "Added" : "Connected"} ${chalk.bold(card.name)}.`));
@@ -960,8 +984,12 @@ export async function capabilityEnable(name, opts) {
960
984
  if (after)
961
985
  card = after;
962
986
  }
987
+ // (a′) Its access to the service: a tool row the run gate refuses blocks
988
+ // every verb, so it is turned on before (and whatever) the verbs.
989
+ const accessOn = (card.toolsOff?.length ?? 0) === 0 || reportAccess(card, await turnOnAccess(cfg, card));
963
990
  if (card.verbs.length === 0) {
964
- log.heading(`${card.name} is on`);
991
+ if (accessOn)
992
+ log.heading(`${card.name} is on`);
965
993
  log.dim("This capability has no selectable verbs yet.");
966
994
  return;
967
995
  }
@@ -991,7 +1019,11 @@ export async function capabilityEnable(name, opts) {
991
1019
  const toEnable = card.verbs.filter((v) => selected.has(v.verbId) && !v.enabled);
992
1020
  const toDisable = card.verbs.filter((v) => !selected.has(v.verbId) && v.enabled);
993
1021
  if (toEnable.length === 0 && toDisable.length === 0) {
994
- log.success(`Nothing changed — ${card.name} is already set up that way.`);
1022
+ if (!accessOn)
1023
+ return;
1024
+ log.success((card.toolsOff?.length ?? 0) > 0
1025
+ ? `Nothing else changed — ${card.name}'s verbs are already set up that way.`
1026
+ : `Nothing changed — ${card.name} is already set up that way.`);
995
1027
  return;
996
1028
  }
997
1029
  const work = ora({ text: "Applying your selection…", color: "cyan" }).start();
@@ -1040,6 +1072,42 @@ export async function capabilityEnable(name, opts) {
1040
1072
  log.dim(`Run it: synap cap run ${example.verbId} --<param> <value>`);
1041
1073
  }
1042
1074
  }
1075
+ /**
1076
+ * Turn ON a capability's access to its service: each member tool row the pod's
1077
+ * run gate refuses (`card.toolsOff`), through the same door the install uses
1078
+ * (`POST /tools/:id/approve`). A person's step: the pod refuses an agent key,
1079
+ * and that refusal is reported with where a person fixes it, never as success.
1080
+ */
1081
+ export async function turnOnAccess(cfg, card) {
1082
+ const turnedOn = [];
1083
+ const refused = [];
1084
+ for (const t of card.toolsOff ?? []) {
1085
+ try {
1086
+ await hubPost(`/tools/${t.id}/approve`, { approved: true }, cfg);
1087
+ turnedOn.push(t.name);
1088
+ }
1089
+ catch (err) {
1090
+ refused.push({ name: t.name, reason: err.message });
1091
+ }
1092
+ }
1093
+ return { turnedOn, refused };
1094
+ }
1095
+ /** Say what `turnOnAccess` did; returns false when some access is still off. */
1096
+ function reportAccess(card, res) {
1097
+ if (res.turnedOn.length > 0) {
1098
+ log.success(`Turned on its access to ${res.turnedOn.join(", ")}`);
1099
+ }
1100
+ for (const r of res.refused) {
1101
+ log.warn(`Its access to "${r.name}" is still off: ${r.reason}`);
1102
+ }
1103
+ if (res.refused.length > 0) {
1104
+ log.dim(card.nextAction.url
1105
+ ? `Nothing it does can run until it is on. A person turns it on here: ${card.nextAction.url}`
1106
+ : "Nothing it does can run until it is on. A person turns it on in the pod admin, under Tools.");
1107
+ return false;
1108
+ }
1109
+ return true;
1110
+ }
1043
1111
  export async function capabilityConnect(name, opts) {
1044
1112
  let cfg;
1045
1113
  try {
@@ -1050,26 +1118,42 @@ export async function capabilityConnect(name, opts) {
1050
1118
  process.exit(1);
1051
1119
  }
1052
1120
  const workspaceId = requireWorkspace(opts, cfg);
1053
- // No name → discovery picker: list connectable services (Nango providers) with
1054
- // their status, pick one, run the OAuth flow. `connect <name>` still connects a
1055
- // specific capability's provider.
1121
+ // No name → discovery picker: the connectable services (Nango providers) AND
1122
+ // the installed capabilities connected by an API key (vault://, e.g. Unipile).
1123
+ // Nango never lists a keyed one, so before 28 Oct the picker hid them and
1124
+ // `--reconnect` could not reach them. Pick an OAuth service → its OAuth flow;
1125
+ // pick a keyed capability → the named path below (rotate the key in place).
1056
1126
  if (!name || !name.trim()) {
1057
1127
  const spin = ora({ text: "Fetching connectable services…", color: "cyan" }).start();
1058
- let providers;
1128
+ let providers = [];
1059
1129
  let nangoStatus;
1060
1130
  let nangoError;
1061
- try {
1062
- const res = (await hubGet("/connectors/providers", {}, cfg));
1063
- providers = res.providers ?? [];
1064
- nangoStatus = res.nangoStatus;
1065
- nangoError = res.nangoError;
1066
- spin.stop();
1131
+ let providersFailed = false;
1132
+ let keyed = [];
1133
+ let keyedFailed = false;
1134
+ const [provRes, catRes] = await Promise.allSettled([
1135
+ hubGet("/connectors/providers", {}, cfg),
1136
+ fetchCatalog(cfg, workspaceId),
1137
+ ]);
1138
+ spin.stop();
1139
+ if (provRes.status === "fulfilled") {
1140
+ providers = provRes.value.providers ?? [];
1141
+ nangoStatus = provRes.value.nangoStatus;
1142
+ nangoError = provRes.value.nangoError;
1067
1143
  }
1068
- catch (err) {
1069
- spin.stop();
1070
- renderHubError(err);
1144
+ else {
1145
+ providersFailed = true;
1146
+ renderHubError(provRes.reason);
1071
1147
  log.dim("Couldn't reach the pod's connector service.");
1072
- return;
1148
+ }
1149
+ if (catRes.status === "fulfilled") {
1150
+ keyed = catRes.value.filter((c) => c.source === "installed" && c.connection?.required === true && c.connection.kind === "vault");
1151
+ }
1152
+ else {
1153
+ // A failed read is not "no keyed capabilities": say so, and name the door that still works.
1154
+ keyedFailed = true;
1155
+ log.warn("Couldn't list the capabilities connected by an API key.");
1156
+ log.dim('Connect one by name: synap cap connect "<name>" (names: synap cap list)');
1073
1157
  }
1074
1158
  // Distinguish "couldn't check" from "checked, genuinely empty" — the same
1075
1159
  // fault-vs-absence signal the pod now returns. A guess ("may not be
@@ -1078,23 +1162,31 @@ export async function capabilityConnect(name, opts) {
1078
1162
  log.warn(`Couldn't check connectors (${nangoError?.reason ?? "unknown"}) — connection state is unknown.`);
1079
1163
  if (nangoError?.message)
1080
1164
  log.dim(nangoError.message);
1081
- return;
1165
+ providers = [];
1166
+ providersFailed = true;
1082
1167
  }
1083
- if (providers.length === 0) {
1168
+ if (providers.length === 0 && keyed.length === 0) {
1169
+ if (providersFailed || keyedFailed)
1170
+ return;
1084
1171
  log.warn("No connectable services available on this pod.");
1085
1172
  log.dim("This pod's Nango declares no integrations. A pod admin declares them in the Nango dashboard.");
1086
1173
  return;
1087
1174
  }
1175
+ const keyedConnected = (c) => c.connection?.state === "connected";
1088
1176
  const connectable = providers.filter((p) => !p.connected);
1089
1177
  const connected = providers.filter((p) => p.connected);
1178
+ const keyedOpen = keyed.filter((c) => !keyedConnected(c));
1179
+ const keyedDone = keyed.filter(keyedConnected);
1090
1180
  // --reconnect: the point is to re-auth an ALREADY-connected (but expired)
1091
- // service, so connected rows become selectable (a fresh OAuth) rather than
1092
- // disabled, and "everything already connected" is no longer a dead end.
1093
- if (connectable.length === 0 && !opts.reconnect) {
1181
+ // service, so connected rows become selectable (a fresh OAuth, or a new key)
1182
+ // rather than disabled, and "everything already connected" is no longer a dead end.
1183
+ if (connectable.length === 0 && keyedOpen.length === 0 && !opts.reconnect) {
1094
1184
  log.heading("Everything connectable here is already connected.");
1095
1185
  for (const p of connected)
1096
1186
  log.dim(` ${chalk.green("✓")} ${p.displayName ?? p.provider}`);
1097
- log.dim("Reconnect an expired one: synap cap connect --reconnect");
1187
+ for (const c of keyedDone)
1188
+ log.dim(` ${chalk.green("✓")} ${c.name}`);
1189
+ log.dim("Reconnect an expired one, or replace a key: synap cap connect --reconnect");
1098
1190
  return;
1099
1191
  }
1100
1192
  const answer = await prompts({
@@ -1102,25 +1194,41 @@ export async function capabilityConnect(name, opts) {
1102
1194
  name: "id",
1103
1195
  message: `${opts.reconnect ? "Reconnect" : "Connect"} a service ${chalk.dim(`→ ${podHostLabel(cfg)}`)}`,
1104
1196
  // Connectable first; already-connected grouped at the end — disabled in the
1105
- // normal flow, but SELECTABLE (for a forced re-auth) under --reconnect.
1197
+ // normal flow, but SELECTABLE (for a forced re-auth / a new key) under --reconnect.
1106
1198
  choices: [
1107
1199
  ...connectable.map((p) => ({
1108
1200
  title: p.displayName ?? p.provider,
1109
- value: p.id,
1201
+ value: `oauth:${p.id}`,
1202
+ })),
1203
+ ...keyedOpen.map((c) => ({
1204
+ title: c.name,
1205
+ description: "API key",
1206
+ value: `key:${c.name}`,
1110
1207
  })),
1111
1208
  ...connected.map((p) => ({
1112
1209
  title: `${p.displayName ?? p.provider} ${chalk.green("✓ connected")}`,
1113
1210
  description: opts.reconnect ? "re-authenticate" : "already connected",
1114
- value: p.id,
1211
+ value: `oauth:${p.id}`,
1212
+ disabled: !opts.reconnect,
1213
+ })),
1214
+ ...keyedDone.map((c) => ({
1215
+ title: `${c.name} ${chalk.green("✓ connected")}`,
1216
+ description: opts.reconnect ? "replace its API key" : "already connected",
1217
+ value: `key:${c.name}`,
1115
1218
  disabled: !opts.reconnect,
1116
1219
  })),
1117
1220
  ],
1118
1221
  });
1119
- if (!answer.id) {
1222
+ const picked = typeof answer.id === "string" ? answer.id : "";
1223
+ if (!picked) {
1120
1224
  log.dim("Cancelled.");
1121
1225
  return;
1122
1226
  }
1123
- const chosen = providers.find((p) => p.id === answer.id);
1227
+ if (picked.startsWith("key:")) {
1228
+ await capabilityConnect(picked.slice("key:".length), opts);
1229
+ return;
1230
+ }
1231
+ const chosen = providers.find((p) => p.id === picked.slice("oauth:".length));
1124
1232
  const ok = await connectProviderOAuth(cfg, chosen.id, workspaceId, {
1125
1233
  displayName: chosen.displayName ?? chosen.provider,
1126
1234
  forceReauth: opts.reconnect ?? false,
@@ -1158,7 +1266,9 @@ export async function capabilityConnect(name, opts) {
1158
1266
  }
1159
1267
  if (opts.reconnect) {
1160
1268
  log.heading(`Reconnecting ${card.name}`);
1161
- log.dim("Forcing a fresh sign-in — your previous access had expired or was revoked.");
1269
+ log.dim(card.connection.kind === "vault"
1270
+ ? "Replacing the key its tools use."
1271
+ : "Forcing a fresh sign-in — your previous access had expired or was revoked.");
1162
1272
  }
1163
1273
  const ok = await ensureConnection(cfg, workspaceId, card, opts.reconnect ?? false);
1164
1274
  if (!ok)