@vellumai/assistant 0.8.0 → 0.8.1

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 (692) hide show
  1. package/AGENTS.md +11 -0
  2. package/Dockerfile +5 -4
  3. package/README.md +2 -2
  4. package/docker-entrypoint.sh +16 -0
  5. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  6. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  7. package/eslint.config.mjs +12 -0
  8. package/knip.json +2 -1
  9. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  10. package/openapi.yaml +4847 -1698
  11. package/package.json +3 -1
  12. package/scripts/generate-openapi.ts +52 -4
  13. package/scripts/sync-llm-catalog.ts +165 -0
  14. package/scripts/sync-web-search-catalog.ts +107 -0
  15. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  17. package/src/__tests__/anthropic-provider.test.ts +92 -2
  18. package/src/__tests__/app-control-flow.test.ts +7 -0
  19. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  20. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  22. package/src/__tests__/btw-routes.test.ts +1 -0
  23. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  25. package/src/__tests__/channel-policy.test.ts +12 -0
  26. package/src/__tests__/checker.test.ts +89 -0
  27. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
  28. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  29. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  30. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  31. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  32. package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
  33. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  34. package/src/__tests__/config-schema.test.ts +14 -3
  35. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  36. package/src/__tests__/config-set-route.test.ts +198 -0
  37. package/src/__tests__/config-watcher.test.ts +6 -0
  38. package/src/__tests__/contacts-tools.test.ts +51 -199
  39. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  40. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  41. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  42. package/src/__tests__/context-search-fanout.test.ts +20 -157
  43. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  44. package/src/__tests__/context-search-types.test.ts +7 -2
  45. package/src/__tests__/context-window-manager.test.ts +389 -1
  46. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  47. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  48. package/src/__tests__/conversation-error.test.ts +38 -0
  49. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  50. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  51. package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
  52. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  54. package/src/__tests__/conversation-process-callsite.test.ts +21 -1
  55. package/src/__tests__/conversation-runtime-assembly.test.ts +4 -4
  56. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  57. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  58. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  59. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  60. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  61. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  62. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  63. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  64. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  65. package/src/__tests__/filing-service.test.ts +23 -3
  66. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  67. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  68. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  69. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -8
  70. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  71. package/src/__tests__/heartbeat-service.test.ts +50 -233
  72. package/src/__tests__/history-repair.test.ts +89 -0
  73. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  74. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  75. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  76. package/src/__tests__/host-browser-routes.test.ts +325 -33
  77. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  78. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  79. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  80. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  81. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  82. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  83. package/src/__tests__/install-skill-routing.test.ts +2 -2
  84. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +15 -0
  85. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  86. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  87. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  88. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  89. package/src/__tests__/llm-resolver.test.ts +46 -0
  90. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  91. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  92. package/src/__tests__/mcp-cli.test.ts +182 -220
  93. package/src/__tests__/mcp-health-check.test.ts +56 -27
  94. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  95. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  96. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  97. package/src/__tests__/oauth-cli.test.ts +38 -2009
  98. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  99. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  100. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  102. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  103. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  104. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  105. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  106. package/src/__tests__/plugin-types.test.ts +13 -11
  107. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  108. package/src/__tests__/profile-entry-status.test.ts +43 -0
  109. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  110. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  111. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  112. package/src/__tests__/relay-server.test.ts +118 -0
  113. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  114. package/src/__tests__/schedule-retry.test.ts +56 -4
  115. package/src/__tests__/schedule-routes.test.ts +104 -0
  116. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  117. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  118. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  119. package/src/__tests__/scheduler-wake.test.ts +0 -63
  120. package/src/__tests__/secret-allowlist.test.ts +1 -0
  121. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  122. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  123. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  124. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  125. package/src/__tests__/skill-load-tool.test.ts +2 -4
  126. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  127. package/src/__tests__/suggestion-routes.test.ts +3 -3
  128. package/src/__tests__/sync-message-contract.test.ts +63 -0
  129. package/src/__tests__/task-scheduler.test.ts +88 -23
  130. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  131. package/src/__tests__/usage-cli.test.ts +11 -73
  132. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  133. package/src/__tests__/vercel-config.test.ts +168 -0
  134. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  135. package/src/__tests__/web-search.test.ts +303 -2
  136. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  137. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  138. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
  139. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  140. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  141. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  142. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  143. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  144. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  145. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  146. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  147. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  148. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  149. package/src/acp/resolve-agent.test.ts +25 -0
  150. package/src/acp/resolve-agent.ts +13 -2
  151. package/src/acp/session-manager.ts +14 -0
  152. package/src/approvals/guardian-request-resolvers.ts +32 -87
  153. package/src/calls/relay-server.ts +35 -0
  154. package/src/calls/relay-setup-router.ts +36 -0
  155. package/src/calls/types.ts +1 -0
  156. package/src/calls/voice-session-bridge.ts +23 -4
  157. package/src/channels/config.ts +14 -1
  158. package/src/channels/types.ts +1 -0
  159. package/src/cli/AGENTS.md +164 -4
  160. package/src/cli/__tests__/notifications.test.ts +54 -0
  161. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  162. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  163. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  164. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  165. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  166. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  167. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  168. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  169. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  170. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  171. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  172. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  173. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  174. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  175. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  176. package/src/cli/commands/__tests__/status.test.ts +249 -0
  177. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  178. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  179. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  180. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  181. package/src/cli/commands/attachment.ts +8 -3
  182. package/src/cli/commands/audit.ts +95 -64
  183. package/src/cli/commands/auth.ts +61 -58
  184. package/src/cli/commands/avatar.ts +276 -390
  185. package/src/cli/commands/backup.ts +409 -505
  186. package/src/cli/commands/bash.ts +9 -5
  187. package/src/cli/commands/browser.ts +28 -9
  188. package/src/cli/commands/cache.ts +9 -4
  189. package/src/cli/commands/changelog.ts +414 -0
  190. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  191. package/src/cli/commands/clients.ts +8 -3
  192. package/src/cli/commands/completions.ts +9 -9
  193. package/src/cli/commands/config.ts +102 -72
  194. package/src/cli/commands/contacts.ts +575 -696
  195. package/src/cli/commands/conversations-defer.ts +17 -69
  196. package/src/cli/commands/conversations-import.ts +90 -253
  197. package/src/cli/commands/conversations.ts +346 -436
  198. package/src/cli/commands/credential-execution.ts +9 -6
  199. package/src/cli/commands/credentials.ts +456 -736
  200. package/src/cli/commands/domain.ts +128 -206
  201. package/src/cli/commands/email.ts +606 -794
  202. package/src/cli/commands/gateway.ts +8 -1
  203. package/src/cli/commands/image-generation.ts +157 -205
  204. package/src/cli/commands/inference-providers.ts +352 -0
  205. package/src/cli/commands/inference-session.ts +415 -0
  206. package/src/cli/commands/inference.ts +87 -65
  207. package/src/cli/commands/keys.ts +8 -3
  208. package/src/cli/commands/mcp.ts +103 -287
  209. package/src/cli/commands/memory-v2.ts +162 -516
  210. package/src/cli/commands/notifications.ts +33 -7
  211. package/src/cli/commands/oauth/apps.ts +292 -261
  212. package/src/cli/commands/oauth/connect.ts +176 -297
  213. package/src/cli/commands/oauth/disconnect.ts +16 -215
  214. package/src/cli/commands/oauth/index.ts +49 -45
  215. package/src/cli/commands/oauth/mode.ts +43 -199
  216. package/src/cli/commands/oauth/ping.ts +17 -125
  217. package/src/cli/commands/oauth/providers.ts +732 -921
  218. package/src/cli/commands/oauth/request.ts +60 -350
  219. package/src/cli/commands/oauth/shared.ts +11 -121
  220. package/src/cli/commands/oauth/status.ts +31 -121
  221. package/src/cli/commands/oauth/token.ts +13 -55
  222. package/src/cli/commands/pending.ts +19 -10
  223. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  224. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  225. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  226. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  227. package/src/cli/commands/platform/connect.ts +16 -80
  228. package/src/cli/commands/platform/disconnect.ts +14 -112
  229. package/src/cli/commands/platform/index.ts +177 -246
  230. package/src/cli/commands/routes.ts +153 -336
  231. package/src/cli/commands/sequence.ts +316 -360
  232. package/src/cli/commands/skills.ts +449 -671
  233. package/src/cli/commands/status.ts +58 -37
  234. package/src/cli/commands/stt.ts +94 -262
  235. package/src/cli/commands/task.ts +14 -40
  236. package/src/cli/commands/trust.ts +8 -3
  237. package/src/cli/commands/tts.ts +162 -167
  238. package/src/cli/commands/ui.ts +35 -42
  239. package/src/cli/commands/usage.ts +188 -126
  240. package/src/cli/commands/watchers.ts +8 -3
  241. package/src/cli/commands/webhooks.ts +99 -193
  242. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  243. package/src/cli/lib/daemon-credential-client.ts +4 -5
  244. package/src/cli/lib/nested-value.ts +44 -0
  245. package/src/cli/lib/open-browser.ts +36 -0
  246. package/src/cli/lib/register-command.ts +19 -0
  247. package/src/cli/lib/time-ago.ts +34 -0
  248. package/src/cli/program.ts +2 -4
  249. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  250. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  251. package/src/cli/utils/conversation-id.ts +30 -0
  252. package/src/cli/utils/parse-duration.ts +41 -0
  253. package/src/config/acp-defaults.test.ts +5 -1
  254. package/src/config/acp-defaults.ts +11 -4
  255. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  256. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  258. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  259. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  260. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  261. package/src/config/bundled-tool-registry.ts +0 -2
  262. package/src/config/feature-flag-registry.json +16 -0
  263. package/src/config/llm-resolver.ts +16 -1
  264. package/src/config/loader.ts +76 -14
  265. package/src/config/raw-config-utils.ts +2 -30
  266. package/src/config/schema.ts +4 -0
  267. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  268. package/src/config/schemas/call-site-catalog.ts +29 -7
  269. package/src/config/schemas/llm-request-logs.ts +57 -0
  270. package/src/config/schemas/llm.ts +52 -2
  271. package/src/config/schemas/memory-retrospective.ts +48 -0
  272. package/src/config/schemas/memory-v2.ts +32 -1
  273. package/src/config/schemas/memory.ts +4 -0
  274. package/src/config/schemas/services.ts +15 -12
  275. package/src/config/seed-inference-profiles.ts +195 -134
  276. package/src/contacts/contact-store.ts +0 -61
  277. package/src/context/window-manager.ts +191 -5
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +79 -0
  279. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  280. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  281. package/src/daemon/approval-generators.ts +23 -29
  282. package/src/daemon/config-watcher.ts +2 -0
  283. package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
  284. package/src/daemon/conversation-agent-loop.ts +127 -97
  285. package/src/daemon/conversation-error.ts +21 -0
  286. package/src/daemon/conversation-lifecycle.ts +46 -5
  287. package/src/daemon/conversation-process.ts +36 -19
  288. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  289. package/src/daemon/conversation-slash.ts +175 -23
  290. package/src/daemon/conversation-store.ts +17 -10
  291. package/src/daemon/conversation-surfaces.ts +76 -12
  292. package/src/daemon/conversation-tool-setup.ts +24 -14
  293. package/src/daemon/conversation.ts +48 -9
  294. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  295. package/src/daemon/guardian-action-generators.ts +7 -22
  296. package/src/daemon/handlers/config-model.ts +8 -126
  297. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  298. package/src/daemon/handlers/config-vercel.ts +3 -1
  299. package/src/daemon/handlers/skills.ts +84 -5
  300. package/src/daemon/history-repair.ts +33 -6
  301. package/src/daemon/host-app-control-proxy.ts +44 -19
  302. package/src/daemon/host-bash-proxy.ts +85 -158
  303. package/src/daemon/host-browser-proxy.ts +96 -35
  304. package/src/daemon/host-proxy-base.ts +13 -1
  305. package/src/daemon/host-proxy-preactivation.ts +25 -1
  306. package/src/daemon/identity-helpers.ts +19 -0
  307. package/src/daemon/lifecycle.ts +42 -43
  308. package/src/daemon/meet-host-supervisor.ts +15 -15
  309. package/src/daemon/memory-v2-startup.ts +9 -2
  310. package/src/daemon/message-protocol.ts +6 -0
  311. package/src/daemon/message-types/bookmarks.ts +18 -0
  312. package/src/daemon/message-types/conversations.ts +12 -9
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/sync.ts +60 -0
  315. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  316. package/src/daemon/pkb-reminder-builder.ts +21 -7
  317. package/src/daemon/process-message.ts +56 -23
  318. package/src/daemon/server.ts +23 -18
  319. package/src/daemon/shutdown-handlers.ts +0 -2
  320. package/src/daemon/tool-setup-types.ts +9 -0
  321. package/src/daemon/tool-side-effects.ts +6 -4
  322. package/src/daemon/wake-target-adapter.ts +11 -0
  323. package/src/export/transcript-formatter.ts +61 -2
  324. package/src/filing/filing-service.ts +40 -53
  325. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  326. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  327. package/src/heartbeat/heartbeat-service.ts +148 -127
  328. package/src/home/__tests__/feed-types.test.ts +63 -131
  329. package/src/home/__tests__/feed-writer.test.ts +77 -278
  330. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  331. package/src/home/feed-types.ts +19 -73
  332. package/src/home/feed-writer.ts +25 -156
  333. package/src/home/post-connect-feed.ts +1 -3
  334. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  335. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  336. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  337. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  338. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  339. package/src/ipc/assistant-server.ts +55 -6
  340. package/src/ipc/cli-client.ts +370 -50
  341. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  342. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  343. package/src/ipc/skill-routes/events.ts +30 -3
  344. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  345. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  346. package/src/live-voice/live-voice-session-manager.ts +11 -4
  347. package/src/live-voice/live-voice-session.ts +14 -6
  348. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  349. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  350. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  351. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  352. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  353. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  354. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  355. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  356. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  357. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  358. package/src/memory/bookmark-crud.ts +179 -0
  359. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  360. package/src/memory/context-search/agent-protocol.ts +5 -1
  361. package/src/memory/context-search/agent-runner.ts +60 -85
  362. package/src/memory/context-search/limits.ts +1 -4
  363. package/src/memory/context-search/search.ts +23 -113
  364. package/src/memory/context-search/sources/conversations.ts +18 -6
  365. package/src/memory/context-search/sources/memory-v2.ts +39 -14
  366. package/src/memory/context-search/sources/memory.ts +7 -0
  367. package/src/memory/context-search/sources/workspace.ts +13 -10
  368. package/src/memory/context-search/types.ts +1 -1
  369. package/src/memory/conversation-bootstrap.ts +11 -0
  370. package/src/memory/conversation-crud.ts +312 -10
  371. package/src/memory/conversation-queries.ts +9 -5
  372. package/src/memory/conversation-title-service.ts +1 -0
  373. package/src/memory/conversation-types.ts +16 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +2 -1
  376. package/src/memory/embedding-runtime-manager.ts +1 -2
  377. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  378. package/src/memory/graph/conversation-graph-memory.ts +76 -5
  379. package/src/memory/graph/extraction.ts +4 -0
  380. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  381. package/src/memory/graph/tool-handlers.ts +17 -7
  382. package/src/memory/graph/tools.ts +44 -5
  383. package/src/memory/indexer.ts +17 -0
  384. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +13 -15
  385. package/src/memory/jobs/embed-concept-page.ts +45 -9
  386. package/src/memory/jobs-store.ts +51 -1
  387. package/src/memory/jobs-worker.ts +52 -3
  388. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  389. package/src/memory/llm-request-log-source-local.ts +26 -0
  390. package/src/memory/llm-request-log-source.ts +97 -0
  391. package/src/memory/llm-request-log-store.ts +1 -1
  392. package/src/memory/memory-retrospective-constants.ts +13 -0
  393. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  394. package/src/memory/memory-retrospective-job.ts +351 -0
  395. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  396. package/src/memory/memory-retrospective-state.ts +162 -0
  397. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  398. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  399. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  400. package/src/memory/message-content.ts +38 -1
  401. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  402. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  403. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  404. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  405. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  406. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  407. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  408. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  409. package/src/memory/migrations/243-provider-connections.ts +68 -0
  410. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  411. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  412. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  413. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  414. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  415. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  416. package/src/memory/migrations/index.ts +7 -0
  417. package/src/memory/published-pages-store.ts +16 -0
  418. package/src/memory/schema/bookmarks.ts +38 -0
  419. package/src/memory/schema/conversations.ts +2 -0
  420. package/src/memory/schema/index.ts +2 -0
  421. package/src/memory/schema/inference.ts +29 -0
  422. package/src/memory/schema/memory-core.ts +9 -0
  423. package/src/memory/search/semantic.ts +1 -4
  424. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  425. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  426. package/src/memory/v2/__tests__/activation.test.ts +11 -4
  427. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  428. package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
  429. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  430. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  431. package/src/memory/v2/__tests__/injection.test.ts +628 -10
  432. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  433. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  434. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  435. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  436. package/src/memory/v2/__tests__/qdrant.test.ts +72 -0
  437. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  438. package/src/memory/v2/__tests__/router.test.ts +516 -0
  439. package/src/memory/v2/__tests__/sim.test.ts +45 -1
  440. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  441. package/src/memory/v2/__tests__/static-context.test.ts +7 -22
  442. package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
  443. package/src/memory/v2/activation-store.ts +34 -5
  444. package/src/memory/v2/activation.ts +40 -27
  445. package/src/memory/v2/backfill-jobs.ts +17 -84
  446. package/src/memory/v2/consolidation-job.ts +85 -78
  447. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  448. package/src/memory/v2/injection.ts +440 -109
  449. package/src/memory/v2/migration.ts +117 -20
  450. package/src/memory/v2/page-index.ts +191 -0
  451. package/src/memory/v2/page-store.ts +3 -0
  452. package/src/memory/v2/prompts/consolidation.ts +9 -7
  453. package/src/memory/v2/prompts/router.ts +192 -0
  454. package/src/memory/v2/qdrant.ts +100 -87
  455. package/src/memory/v2/reranker.ts +14 -7
  456. package/src/memory/v2/router.ts +322 -0
  457. package/src/memory/v2/sim.ts +25 -12
  458. package/src/memory/v2/skill-store.ts +118 -29
  459. package/src/memory/v2/static-context.ts +16 -9
  460. package/src/memory/v2/sweep-job.ts +122 -96
  461. package/src/memory/v2/types.ts +10 -6
  462. package/src/memory/validation.ts +13 -0
  463. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  464. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  465. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  466. package/src/notifications/adapters/platform.ts +171 -0
  467. package/src/notifications/conversation-pairing.ts +2 -2
  468. package/src/notifications/copy-composer.ts +15 -0
  469. package/src/notifications/destination-resolver.ts +21 -0
  470. package/src/notifications/emit-signal.ts +28 -1
  471. package/src/notifications/home-feed-side-effect.ts +111 -0
  472. package/src/notifications/signal.ts +5 -0
  473. package/src/permissions/checker.ts +12 -0
  474. package/src/permissions/ipc-risk-types.ts +2 -0
  475. package/src/plugin-api/index.ts +13 -0
  476. package/src/plugin-api/package.json +12 -0
  477. package/src/plugin-api/types.ts +62 -0
  478. package/src/plugins/defaults/injectors.ts +19 -3
  479. package/src/plugins/external-plugin-loader.ts +294 -0
  480. package/src/plugins/types.ts +46 -30
  481. package/src/plugins/user-loader.ts +64 -41
  482. package/src/proactive-artifact/job.test.ts +12 -4
  483. package/src/proactive-artifact/job.ts +4 -0
  484. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  485. package/src/proactive-artifact/trigger-state.ts +4 -0
  486. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  487. package/src/prompts/system-prompt.ts +22 -1
  488. package/src/prompts/update-bulletin-job.ts +61 -73
  489. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  490. package/src/providers/__tests__/inference.test.ts +288 -0
  491. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  492. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  493. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  494. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  495. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  496. package/src/providers/anthropic/client.ts +95 -26
  497. package/src/providers/call-site-routing.ts +94 -16
  498. package/src/providers/connection-resolution.ts +163 -0
  499. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  500. package/src/providers/inference/adapter-factory.ts +173 -0
  501. package/src/providers/inference/auth.ts +112 -0
  502. package/src/providers/inference/backfill.ts +196 -0
  503. package/src/providers/inference/connections.ts +356 -0
  504. package/src/providers/inference/resolve-auth.ts +65 -0
  505. package/src/providers/model-catalog.ts +104 -6
  506. package/src/providers/openai/responses-provider.ts +4 -2
  507. package/src/providers/provider-env-vars.ts +17 -7
  508. package/src/providers/provider-secret-catalog.ts +49 -30
  509. package/src/providers/provider-send-message.ts +41 -20
  510. package/src/providers/registry.ts +143 -159
  511. package/src/providers/retry.ts +18 -10
  512. package/src/providers/search-provider-catalog.ts +121 -0
  513. package/src/runtime/AGENTS.md +18 -5
  514. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  515. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  516. package/src/runtime/actor-trust-resolver.ts +32 -10
  517. package/src/runtime/agent-wake.ts +35 -6
  518. package/src/runtime/assistant-event-hub.ts +3 -85
  519. package/src/runtime/auth/route-policy.ts +303 -8
  520. package/src/runtime/auth/same-actor.ts +2 -0
  521. package/src/runtime/background-job-runner.ts +339 -0
  522. package/src/runtime/btw-sidechain.ts +1 -0
  523. package/src/runtime/http-router.ts +36 -1
  524. package/src/runtime/http-server.ts +31 -5
  525. package/src/runtime/http-types.ts +2 -0
  526. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  527. package/src/runtime/middleware/request-logger.ts +62 -1
  528. package/src/runtime/pre-first-message-gate.ts +83 -0
  529. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  530. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  531. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  532. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  533. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  534. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  535. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  536. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
  537. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  538. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  539. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  540. package/src/runtime/routes/acp-routes.ts +10 -8
  541. package/src/runtime/routes/app-management-routes.ts +228 -3
  542. package/src/runtime/routes/approval-routes.ts +0 -18
  543. package/src/runtime/routes/audit-routes.ts +43 -0
  544. package/src/runtime/routes/auth-routes.ts +72 -0
  545. package/src/runtime/routes/avatar-routes.ts +273 -20
  546. package/src/runtime/routes/backup-routes.ts +406 -2
  547. package/src/runtime/routes/bookmark-routes.ts +154 -0
  548. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  549. package/src/runtime/routes/contact-routes.ts +0 -160
  550. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  551. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  552. package/src/runtime/routes/conversation-query-routes.ts +334 -86
  553. package/src/runtime/routes/conversation-routes.ts +31 -10
  554. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  555. package/src/runtime/routes/credential-routes.ts +540 -0
  556. package/src/runtime/routes/debug-routes.ts +2 -2
  557. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  558. package/src/runtime/routes/domain-routes.ts +167 -0
  559. package/src/runtime/routes/email-routes.ts +603 -0
  560. package/src/runtime/routes/errors.ts +2 -2
  561. package/src/runtime/routes/events-routes.ts +192 -0
  562. package/src/runtime/routes/home-feed-routes.ts +6 -78
  563. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  564. package/src/runtime/routes/host-browser-routes.ts +103 -22
  565. package/src/runtime/routes/http-adapter.ts +2 -0
  566. package/src/runtime/routes/identity-routes.ts +5 -0
  567. package/src/runtime/routes/image-generation-routes.ts +99 -0
  568. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  569. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  570. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  571. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
  572. package/src/runtime/routes/index.ts +36 -0
  573. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  574. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  575. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  576. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  577. package/src/runtime/routes/inference-send-routes.ts +115 -0
  578. package/src/runtime/routes/integrations/twilio.ts +1 -0
  579. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  580. package/src/runtime/routes/memory-v2-routes.ts +13 -398
  581. package/src/runtime/routes/notification-routes.ts +2 -0
  582. package/src/runtime/routes/oauth-apps.ts +112 -7
  583. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  584. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  585. package/src/runtime/routes/oauth-providers.ts +298 -8
  586. package/src/runtime/routes/platform-routes.ts +336 -0
  587. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  588. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  589. package/src/runtime/routes/playground/state.ts +2 -1
  590. package/src/runtime/routes/publish-routes.ts +221 -0
  591. package/src/runtime/routes/schedule-routes.ts +82 -0
  592. package/src/runtime/routes/sequence-routes.ts +291 -0
  593. package/src/runtime/routes/settings-routes.ts +2 -10
  594. package/src/runtime/routes/skills-routes.ts +31 -1
  595. package/src/runtime/routes/stt-routes.ts +240 -3
  596. package/src/runtime/routes/surface-action-routes.ts +43 -7
  597. package/src/runtime/routes/tts-routes.ts +67 -0
  598. package/src/runtime/routes/types.ts +32 -0
  599. package/src/runtime/routes/user-routes-cli.ts +243 -0
  600. package/src/runtime/routes/webhook-routes.ts +165 -0
  601. package/src/runtime/sync/resource-sync-events.ts +25 -0
  602. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  603. package/src/runtime/sync/sync-publisher.ts +21 -0
  604. package/src/schedule/scheduler.ts +200 -123
  605. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  606. package/src/security/secret-patterns.ts +3 -0
  607. package/src/sequence/engine.ts +38 -40
  608. package/src/subagent/manager.ts +20 -15
  609. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  610. package/src/tools/browser/browser-execution.ts +15 -4
  611. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  612. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  613. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  614. package/src/tools/browser/cdp-client/factory.ts +66 -5
  615. package/src/tools/browser/runtime-check.ts +77 -0
  616. package/src/tools/memory/register.test.ts +3 -3
  617. package/src/tools/memory/register.ts +9 -1
  618. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  619. package/src/tools/network/web-search.ts +280 -37
  620. package/src/tools/permission-checker.ts +13 -5
  621. package/src/tools/subagent/spawn.ts +3 -3
  622. package/src/tools/terminal/shell.ts +44 -0
  623. package/src/usage/attribution.ts +3 -2
  624. package/src/util/pricing.ts +86 -160
  625. package/src/watcher/__tests__/engine.test.ts +301 -0
  626. package/src/watcher/constants.ts +7 -0
  627. package/src/watcher/engine.ts +90 -90
  628. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  629. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  630. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  631. package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
  632. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  633. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  634. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  635. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  636. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  637. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  638. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  639. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  640. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  641. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  642. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  643. package/src/workspace/migrations/registry.ts +22 -0
  644. package/src/workspace/migrations/runner.ts +13 -2
  645. package/src/workspace/migrations/types.ts +13 -3
  646. package/src/workspace/provider-commit-message-generator.ts +3 -2
  647. package/src/__tests__/context-search-pkb-source.test.ts +0 -498
  648. package/src/__tests__/credentials-cli.test.ts +0 -1225
  649. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  650. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  651. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  652. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  653. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  654. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  655. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  656. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  657. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  658. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  659. package/src/cli/commands/autonomy.ts +0 -365
  660. package/src/cli/commands/memory.ts +0 -424
  661. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
  662. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  663. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  664. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  665. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  666. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  667. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  668. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  669. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  670. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  671. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  672. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  673. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  674. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  675. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  676. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  677. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  678. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  679. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  680. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  681. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  682. package/src/home/assistant-feed-authoring.ts +0 -135
  683. package/src/home/emit-feed-event.ts +0 -169
  684. package/src/home/feed-scheduler.ts +0 -281
  685. package/src/home/platform-gmail-digest.ts +0 -163
  686. package/src/home/rewrite-command-preview.ts +0 -66
  687. package/src/home/rewrite-feed-title.ts +0 -58
  688. package/src/home/rollup-producer.ts +0 -426
  689. package/src/memory/admin.ts +0 -326
  690. package/src/memory/context-search/sources/pkb.ts +0 -476
  691. package/src/memory/graph/compaction.ts +0 -299
  692. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -1,40 +1,64 @@
1
1
  import { type Command } from "commander";
2
2
 
3
- import { loadConfig } from "../../../config/loader.js";
4
- import {
5
- deleteApp,
6
- deleteConnection,
7
- deleteProvider,
8
- disconnectOAuthProvider,
9
- getProvider,
10
- listApps,
11
- listConnections,
12
- listProviders,
13
- registerProvider,
14
- updateProvider,
15
- } from "../../../oauth/oauth-store.js";
16
- import {
17
- type SerializedProvider,
18
- serializeProvider,
19
- } from "../../../oauth/provider-serializer.js";
20
- import { isProviderVisible } from "../../../oauth/provider-visibility.js";
21
- import { SEEDED_PROVIDER_KEYS } from "../../../oauth/seed-providers.js";
3
+ import { cliIpcCall, exitFromIpcResult } from "../../../ipc/cli-client.js";
4
+ import { registerCommand } from "../../lib/register-command.js";
22
5
  import { getCliLogger } from "../../logger.js";
23
6
  import { shouldOutputJson, writeOutput } from "../../output.js";
24
7
 
25
8
  const log = getCliLogger("cli");
26
9
 
27
- const LOOPBACK_CALLBACK_PATH = "/oauth/callback";
10
+ // ---------------------------------------------------------------------------
11
+ // Types
12
+ // ---------------------------------------------------------------------------
13
+
14
+ interface SerializedProvider {
15
+ providerKey: string;
16
+ displayName?: string | null;
17
+ description?: string | null;
18
+ supportsManagedMode?: boolean;
19
+ managedServiceIsPaid?: boolean;
20
+ defaultScopes?: string[];
21
+ authUrl?: string;
22
+ tokenUrl?: string;
23
+ refreshUrl?: string | null;
24
+ dashboardUrl?: string | null;
25
+ appType?: string | null;
26
+ requiresClientSecret?: boolean;
27
+ clientIdPlaceholder?: string | null;
28
+ scopeSeparator?: string;
29
+ tokenEndpointAuthMethod?: string | null;
30
+ tokenExchangeBodyFormat?: string | null;
31
+ extraParams?: unknown;
32
+ redirectUri?: string | null;
33
+ baseUrl?: string | null;
34
+ userinfoUrl?: string | null;
35
+ pingUrl?: string | null;
36
+ pingMethod?: string | null;
37
+ pingHeaders?: unknown;
38
+ pingBody?: unknown;
39
+ revokeUrl?: string | null;
40
+ revokeBodyTemplate?: unknown;
41
+ loopbackPort?: number | null;
42
+ injectionTemplates?: unknown;
43
+ identityUrl?: string | null;
44
+ identityMethod?: string | null;
45
+ identityHeaders?: unknown;
46
+ identityBody?: unknown;
47
+ identityResponsePaths?: string[] | null;
48
+ identityFormat?: string | null;
49
+ identityOkField?: string | null;
50
+ availableScopes?: unknown;
51
+ setupNotes?: string[] | unknown;
52
+ featureFlag?: string | null;
53
+ logoUrl?: string | null;
54
+ createdAt?: string;
55
+ updatedAt?: string;
56
+ }
28
57
 
29
58
  // ---------------------------------------------------------------------------
30
59
  // Text formatting helpers (non-JSON output)
31
60
  // ---------------------------------------------------------------------------
32
61
 
33
- /**
34
- * Format available scopes for text output.
35
- * Returns a single-line string for URLs, or a multi-line bullet list for
36
- * structured scope arrays.
37
- */
38
62
  function formatAvailableScopes(
39
63
  availableScopes: unknown,
40
64
  indent: string = " ",
@@ -55,7 +79,6 @@ function formatAvailableScopes(
55
79
  return null;
56
80
  }
57
81
 
58
- /** Render a single provider as a concise summary line for `list`. */
59
82
  function formatProviderSummary(p: SerializedProvider): string {
60
83
  const name = p.displayName ?? p.providerKey;
61
84
  const desc = p.description ? ` — ${p.description}` : "";
@@ -71,7 +94,6 @@ function formatProviderSummary(p: SerializedProvider): string {
71
94
  );
72
95
  }
73
96
 
74
- /** Format a JSON value as indented text for `get` detail output. */
75
97
  function formatJsonValue(value: unknown, indent: string = " "): string {
76
98
  const json = JSON.stringify(value, null, 2);
77
99
  return json
@@ -80,7 +102,6 @@ function formatJsonValue(value: unknown, indent: string = " "): string {
80
102
  .join("\n");
81
103
  }
82
104
 
83
- /** Render a single provider as structured text for `get` with all fields. */
84
105
  function formatProviderDetail(p: SerializedProvider): string {
85
106
  const lines: string[] = [];
86
107
  const name = p.displayName ?? p.providerKey;
@@ -159,11 +180,7 @@ function formatProviderDetail(p: SerializedProvider): string {
159
180
 
160
181
  /**
161
182
  * Resolve a logo URL from CLI flags, enforcing mutual exclusion between
162
- * --logo-url and --logo-simpleicons-slug. Returns:
163
- * - `undefined` when neither flag is set (caller should leave the field unchanged)
164
- * - `null` when `--logo-url ""` is passed (clear the stored value)
165
- * - a non-empty string URL otherwise
166
- * Throws when both flags are set simultaneously.
183
+ * --logo-url and --logo-simpleicons-slug.
167
184
  */
168
185
  function resolveLogoUrlFromFlags(opts: {
169
186
  logoUrl?: string;
@@ -182,51 +199,22 @@ function resolveLogoUrlFromFlags(opts: {
182
199
  return `https://cdn.simpleicons.org/${encodeURIComponent(slug)}`;
183
200
  }
184
201
  if (opts.logoUrl !== undefined) {
185
- // Trim whitespace so copy-paste-padded URLs don't fail to parse on the
186
- // client. Empty string (after trimming) clears the stored value
187
- // (matches --revoke-url semantics documented in the `update` command
188
- // help text).
189
202
  const trimmed = opts.logoUrl.trim();
190
203
  return trimmed === "" ? null : trimmed;
191
204
  }
192
205
  return undefined;
193
206
  }
194
207
 
195
- /**
196
- * Resolve the redirect URI for a provider based on its loopback port.
197
- *
198
- * Resolves the loopback redirect URI for display purposes. Gateway
199
- * redirect URIs are resolved dynamically at connect time.
200
- */
201
- function resolveRedirectUri(loopbackPort: number | null): string | null {
202
- if (!loopbackPort) {
203
- // No fixed port — loopback still works at runtime with an OS-assigned
204
- // port, but we can't predict the redirect URI ahead of time. Return
205
- // a sentinel so callers know the transport is loopback-dynamic rather
206
- // than unsupported.
207
- return "http://localhost:<dynamic>/oauth/callback";
208
- }
209
- return `http://localhost:${loopbackPort}${LOOPBACK_CALLBACK_PATH}`;
210
- }
211
-
212
- /** Serialize a provider row with the CLI-resolved redirect URI. */
213
- function parseProviderRow(row: ReturnType<typeof getProvider>) {
214
- if (!row) return row;
215
- return serializeProvider(row, {
216
- redirectUri: resolveRedirectUri(row.loopbackPort),
217
- });
218
- }
219
-
220
208
  export function registerProviderCommands(oauth: Command): void {
221
- const providers = oauth
222
- .command("providers")
223
- .description(
209
+ registerCommand(oauth, {
210
+ name: "providers",
211
+ transport: "ipc",
212
+ description:
224
213
  "Fetch configured OAuth providers and register custom providers of your own",
225
- );
226
-
227
- providers.addHelpText(
228
- "after",
229
- `
214
+ build: (providers) => {
215
+ providers.addHelpText(
216
+ "after",
217
+ `
230
218
  Providers define the protocol-level configuration for an OAuth integration:
231
219
  authorization URL, token URL, default scopes, and other endpoint details.
232
220
 
@@ -234,26 +222,26 @@ They are seeded on startup for built-in integrations (e.g. Google, Slack,
234
222
  GitHub) but can also be registered dynamically via the "register" subcommand.
235
223
 
236
224
  Each provider is identified by a provider key (e.g. "google").`,
237
- );
225
+ );
238
226
 
239
- // ---------------------------------------------------------------------------
240
- // providers list
241
- // ---------------------------------------------------------------------------
227
+ // -----------------------------------------------------------------------
228
+ // providers list
229
+ // -----------------------------------------------------------------------
242
230
 
243
- providers
244
- .command("list")
245
- .description("List all registered OAuth providers")
246
- .option(
247
- "--provider-key <key>",
248
- 'Filter by provider key substring (case-insensitive). Comma-separated values are OR\'d (e.g. "google,slack")',
249
- )
250
- .option(
251
- "--supports-managed",
252
- "Only show providers that support managed mode",
253
- )
254
- .addHelpText(
255
- "after",
256
- `
231
+ providers
232
+ .command("list")
233
+ .description("List all registered OAuth providers")
234
+ .option(
235
+ "--provider-key <key>",
236
+ 'Filter by provider key substring (case-insensitive). Comma-separated values are OR\'d (e.g. "google,slack")',
237
+ )
238
+ .option(
239
+ "--supports-managed",
240
+ "Only show providers that support managed mode",
241
+ )
242
+ .addHelpText(
243
+ "after",
244
+ `
257
245
  Returns registered OAuth providers, including both built-in providers
258
246
  seeded at startup and any dynamically registered via "providers register".
259
247
 
@@ -273,67 +261,76 @@ Examples:
273
261
  $ assistant oauth providers list --provider-key notion --json
274
262
  $ assistant oauth providers list --supports-managed
275
263
  $ assistant oauth providers list --supports-managed --json`,
276
- )
277
- .action(
278
- (
279
- opts: { providerKey?: string; supportsManaged?: boolean },
280
- cmd: Command,
281
- ) => {
282
- try {
283
- const config = loadConfig();
284
- let allProviders = listProviders();
285
- allProviders = allProviders.filter((r) =>
286
- isProviderVisible(r, config),
287
- );
288
- let rows = allProviders.map(parseProviderRow);
289
-
290
- if (opts.providerKey) {
291
- const needles = opts.providerKey
292
- .split(",")
293
- .map((n) => n.trim().toLowerCase())
294
- .filter(Boolean);
295
- rows = rows.filter(
296
- (r) =>
297
- r &&
298
- needles.some((needle) =>
299
- r.providerKey.toLowerCase().includes(needle),
300
- ),
301
- );
302
- }
303
-
304
- if (opts.supportsManaged) {
305
- rows = rows.filter((r) => r && r.supportsManagedMode);
306
- }
264
+ )
265
+ .action(
266
+ async (
267
+ opts: { providerKey?: string; supportsManaged?: boolean },
268
+ cmd: Command,
269
+ ) => {
270
+ const queryParams: Record<string, string> = {};
271
+ if (opts.supportsManaged) {
272
+ queryParams.supports_managed_mode = "true";
273
+ }
274
+ const r = await cliIpcCall<{
275
+ providers: Array<Record<string, unknown>>;
276
+ }>("oauth_providers_get", {
277
+ queryParams,
278
+ });
307
279
 
308
- if (shouldOutputJson(cmd)) {
309
- writeOutput(cmd, rows);
310
- } else {
311
- const validRows = rows.filter(
312
- (r): r is NonNullable<typeof r> => r != null,
313
- );
314
- const lines = validRows.map(formatProviderSummary);
315
- process.stdout.write(
316
- `${validRows.length} provider(s):\n\n${lines.join("\n\n")}\n`,
280
+ if (!r.ok) return exitFromIpcResult(r);
281
+
282
+ // The route returns snake_case summaries; map to camelCase for
283
+ // display consistency with the existing CLI contract.
284
+ let rows: SerializedProvider[] = (r.result?.providers ?? []).map(
285
+ (p) => ({
286
+ providerKey: p.provider_key as string,
287
+ displayName: p.display_name as string | null,
288
+ description: p.description as string | null,
289
+ supportsManagedMode: p.supports_managed_mode as boolean,
290
+ managedServiceIsPaid: p.managed_service_is_paid as boolean,
291
+ requiresClientSecret: p.requires_client_secret as boolean,
292
+ logoUrl: p.logo_url as string | null,
293
+ dashboardUrl: p.dashboard_url as string | null,
294
+ clientIdPlaceholder: p.client_id_placeholder as string | null,
295
+ featureFlag: p.feature_flag as string | null,
296
+ }),
317
297
  );
318
- }
319
- } catch (err) {
320
- const message = err instanceof Error ? err.message : String(err);
321
- writeOutput(cmd, { ok: false, error: message });
322
- process.exitCode = 1;
323
- }
324
- },
325
- );
326
298
 
327
- // ---------------------------------------------------------------------------
328
- // providers get <provider-key>
329
- // ---------------------------------------------------------------------------
299
+ if (opts.providerKey) {
300
+ const needles = opts.providerKey
301
+ .split(",")
302
+ .map((n) => n.trim().toLowerCase())
303
+ .filter(Boolean);
304
+ rows = rows.filter(
305
+ (r) =>
306
+ r &&
307
+ needles.some((needle) =>
308
+ r.providerKey.toLowerCase().includes(needle),
309
+ ),
310
+ );
311
+ }
330
312
 
331
- providers
332
- .command("get <provider-key>")
333
- .description("Show details of a specific OAuth provider")
334
- .addHelpText(
335
- "after",
336
- `
313
+ if (shouldOutputJson(cmd)) {
314
+ writeOutput(cmd, rows);
315
+ } else {
316
+ const lines = rows.map(formatProviderSummary);
317
+ process.stdout.write(
318
+ `${rows.length} provider(s):\n\n${lines.join("\n\n")}\n`,
319
+ );
320
+ }
321
+ },
322
+ );
323
+
324
+ // -----------------------------------------------------------------------
325
+ // providers get <provider-key>
326
+ // -----------------------------------------------------------------------
327
+
328
+ providers
329
+ .command("get <provider-key>")
330
+ .description("Show details of a specific OAuth provider")
331
+ .addHelpText(
332
+ "after",
333
+ `
337
334
  Arguments:
338
335
  provider-key Provider key (e.g. "google").
339
336
  Must match the key used during registration or seeding.
@@ -345,184 +342,175 @@ if the provider key is not found.
345
342
  Examples:
346
343
  $ assistant oauth providers get google
347
344
  $ assistant oauth providers get twitter --json`,
348
- )
349
- .action((provider: string, _opts: unknown, cmd: Command) => {
350
- try {
351
- const row = getProvider(provider);
352
-
353
- if (!row) {
354
- writeOutput(cmd, {
355
- ok: false,
356
- error: `Provider not found: "${provider}". Run 'assistant oauth providers list' to see all registered providers. To register a custom provider, run 'assistant oauth providers register --help'.`,
357
- });
358
- process.exitCode = 1;
359
- return;
360
- }
361
-
362
- if (!isProviderVisible(row, loadConfig())) {
363
- writeOutput(cmd, {
364
- ok: false,
365
- error: `Provider not found: "${provider}". Run 'assistant oauth providers list' to see all registered providers. To register a custom provider, run 'assistant oauth providers register --help'.`,
366
- });
367
- process.exitCode = 1;
368
- return;
369
- }
370
-
371
- const parsed = parseProviderRow(row);
372
- if (shouldOutputJson(cmd)) {
373
- writeOutput(cmd, parsed);
374
- } else if (parsed) {
375
- process.stdout.write(formatProviderDetail(parsed) + "\n");
376
- }
377
- } catch (err) {
378
- const message = err instanceof Error ? err.message : String(err);
379
- writeOutput(cmd, { ok: false, error: message });
380
- process.exitCode = 1;
381
- }
382
- });
345
+ )
346
+ .action(async (provider: string, _opts: unknown, cmd: Command) => {
347
+ const r = await cliIpcCall<{ provider: SerializedProvider }>(
348
+ "oauth_providers_by_providerKey_get",
349
+ { pathParams: { providerKey: provider } },
350
+ );
383
351
 
384
- // ---------------------------------------------------------------------------
385
- // providers register
386
- // ---------------------------------------------------------------------------
352
+ if (!r.ok) {
353
+ if (r.statusCode === 404) {
354
+ writeOutput(cmd, {
355
+ ok: false,
356
+ error: `Provider not found: "${provider}". Run 'assistant oauth providers list' to see all registered providers. To register a custom provider, run 'assistant oauth providers register --help'.`,
357
+ });
358
+ process.exitCode = 1;
359
+ return;
360
+ }
361
+ return exitFromIpcResult(r);
362
+ }
387
363
 
388
- providers
389
- .command("register")
390
- .description("Register a new OAuth provider configuration")
391
- .requiredOption(
392
- "--provider-key <key>",
393
- "Unique provider key (e.g. \"custom-service\"). Must not collide with an existing key from 'assistant oauth providers list'.",
394
- )
395
- .requiredOption(
396
- "--auth-url <url>",
397
- "OAuth authorization endpoint URL (e.g. https://accounts.example.com/o/oauth2/auth)",
398
- )
399
- .requiredOption(
400
- "--token-url <url>",
401
- "OAuth token endpoint URL (e.g. https://oauth2.example.com/token)",
402
- )
403
- .option(
404
- "--refresh-url <url>",
405
- "OAuth token refresh endpoint URL. Defaults to --token-url when omitted. Set this when the provider uses a different endpoint for the refresh_token grant than for the authorization_code grant.",
406
- )
407
- .option("--base-url <url>", "API base URL for the service")
408
- .option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
409
- .option(
410
- "--scopes <scopes>",
411
- 'Comma-separated default scopes (e.g. "read,write,profile")',
412
- )
413
- .option(
414
- "--scope-separator <sep>",
415
- 'Separator used to join scopes in the authorize URL (default: " "). Use "," for providers like Linear that expect comma-separated scopes.',
416
- )
417
- .option(
418
- "--token-auth-method <method>",
419
- 'How the client authenticates at the token endpoint: "client_secret_post" or "client_secret_basic"',
420
- )
421
- .option(
422
- "--token-exchange-body-format <format>",
423
- 'Body encoding for the token exchange request: "form" (application/x-www-form-urlencoded, default) or "json" (application/json)',
424
- "form",
425
- )
426
- .option(
427
- "--ping-url <url>",
428
- 'Health-check endpoint URL for token validation (e.g. "https://api.example.com/user"). Used by "assistant oauth ping" to verify a stored token.',
429
- )
430
- .option(
431
- "--ping-method <method>",
432
- "HTTP method for the ping endpoint: GET (default) or POST",
433
- )
434
- .option(
435
- "--ping-headers <json>",
436
- 'JSON object of extra headers for the ping request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
437
- )
438
- .option(
439
- "--ping-body <json>",
440
- 'JSON body to send with the ping request (e.g. \'{"query":"{ viewer { id } }"}\')',
441
- )
442
- .option(
443
- "--revoke-url <url>",
444
- 'OAuth token revocation endpoint URL. Called best-effort during disconnect to invalidate the access token upstream (e.g. "https://oauth2.googleapis.com/revoke"). When omitted, disconnect is local-only — the upstream token is left valid until it naturally expires.',
445
- )
446
- .option(
447
- "--revoke-body-template <json>",
448
- 'JSON object body template for the revoke request, supporting {access_token} and {client_id} substitution (e.g. \'{"token":"{access_token}","client_id":"{client_id}"}\'). The body is form-encoded and POSTed to --revoke-url.',
449
- )
450
- .option(
451
- "--display-name <name>",
452
- "Human-readable display name for the provider",
453
- )
454
- .option("--description <text>", "Short description of the provider")
455
- .option(
456
- "--dashboard-url <url>",
457
- "URL to the provider's developer console / dashboard",
458
- )
459
- .option(
460
- "--logo-url <url>",
461
- "URL to the provider's logo image (SVG or PNG). Mutually exclusive with --logo-simpleicons-slug.",
462
- )
463
- .option(
464
- "--logo-simpleicons-slug <slug>",
465
- 'Simple Icons slug (e.g. "notion", "linear"). Resolves to https://cdn.simpleicons.org/<slug>. Mutually exclusive with --logo-url.',
466
- )
467
- .option(
468
- "--client-id-placeholder <text>",
469
- "Placeholder text shown in the client ID input field",
470
- )
471
- .option(
472
- "--no-client-secret",
473
- "Mark this provider as not requiring a client secret (default: required)",
474
- )
475
- .option(
476
- "--loopback-port <port>",
477
- "Fixed port for the local OAuth callback server (e.g. 17322). When set, the redirect URI is http://localhost:<port>/oauth/callback",
478
- )
479
- .option(
480
- "--injection-templates <json>",
481
- 'JSON array of token injection templates each with hostPattern, injectionType, headerName, valuePrefix (e.g. \'[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]\')',
482
- )
483
- .option(
484
- "--app-type <type>",
485
- 'What the provider calls its OAuth apps (e.g. "OAuth App", "Desktop app", "Public integration")',
486
- )
487
- .option(
488
- "--identity-url <url>",
489
- "Identity verification endpoint URL called after OAuth to identify the connected account",
490
- )
491
- .option(
492
- "--identity-method <method>",
493
- "HTTP method for the identity endpoint: GET (default) or POST",
494
- )
495
- .option(
496
- "--identity-headers <json>",
497
- 'JSON object of extra headers for the identity request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
498
- )
499
- .option(
500
- "--identity-body <body>",
501
- 'JSON body to send with the identity request (e.g. \'{"query":"{ viewer { email } }"}\')',
502
- )
503
- .option(
504
- "--identity-response-paths <paths>",
505
- 'Comma-separated dot-notation paths to extract identity from the response (e.g. "email,name,person.email")',
506
- )
507
- .option(
508
- "--identity-format <template>",
509
- 'Format template for the extracted identity using ${pathName} tokens from --identity-response-paths (e.g. "@${login}" or "@${user} (${team})")',
510
- )
511
- .option(
512
- "--identity-ok-field <field>",
513
- 'Dot-notation path to a boolean field that must be truthy for the response to be considered valid (e.g. "ok")',
514
- )
515
- .option(
516
- "--setup-notes <json>",
517
- 'JSON array of setup instruction notes shown during guided setup (e.g. \'["Enable the Gmail API","Add test users"]\')',
518
- )
519
- .option(
520
- "--available-scopes <value>",
521
- "Available scopes: either a JSON array of {scope, description?} objects or a URL to the provider scope docs",
522
- )
523
- .addHelpText(
524
- "after",
525
- `
364
+ const parsed = r.result?.provider;
365
+ if (shouldOutputJson(cmd)) {
366
+ writeOutput(cmd, parsed);
367
+ } else if (parsed) {
368
+ process.stdout.write(formatProviderDetail(parsed) + "\n");
369
+ }
370
+ });
371
+
372
+ // -----------------------------------------------------------------------
373
+ // providers register
374
+ // -----------------------------------------------------------------------
375
+
376
+ providers
377
+ .command("register")
378
+ .description("Register a new OAuth provider configuration")
379
+ .requiredOption(
380
+ "--provider-key <key>",
381
+ "Unique provider key (e.g. \"custom-service\"). Must not collide with an existing key from 'assistant oauth providers list'.",
382
+ )
383
+ .requiredOption(
384
+ "--auth-url <url>",
385
+ "OAuth authorization endpoint URL (e.g. https://accounts.example.com/o/oauth2/auth)",
386
+ )
387
+ .requiredOption(
388
+ "--token-url <url>",
389
+ "OAuth token endpoint URL (e.g. https://oauth2.example.com/token)",
390
+ )
391
+ .option(
392
+ "--refresh-url <url>",
393
+ "OAuth token refresh endpoint URL. Defaults to --token-url when omitted.",
394
+ )
395
+ .option("--base-url <url>", "API base URL for the service")
396
+ .option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
397
+ .option(
398
+ "--scopes <scopes>",
399
+ 'Comma-separated default scopes (e.g. "read,write,profile")',
400
+ )
401
+ .option(
402
+ "--scope-separator <sep>",
403
+ 'Separator used to join scopes in the authorize URL (default: " ").',
404
+ )
405
+ .option(
406
+ "--token-auth-method <method>",
407
+ 'How the client authenticates at the token endpoint: "client_secret_post" or "client_secret_basic"',
408
+ )
409
+ .option(
410
+ "--token-exchange-body-format <format>",
411
+ 'Body encoding for the token exchange request: "form" (default) or "json"',
412
+ "form",
413
+ )
414
+ .option(
415
+ "--ping-url <url>",
416
+ "Health-check endpoint URL for token validation",
417
+ )
418
+ .option(
419
+ "--ping-method <method>",
420
+ "HTTP method for the ping endpoint: GET (default) or POST",
421
+ )
422
+ .option(
423
+ "--ping-headers <json>",
424
+ "JSON object of extra headers for the ping request",
425
+ )
426
+ .option(
427
+ "--ping-body <json>",
428
+ "JSON body to send with the ping request",
429
+ )
430
+ .option(
431
+ "--revoke-url <url>",
432
+ "OAuth token revocation endpoint URL",
433
+ )
434
+ .option(
435
+ "--revoke-body-template <json>",
436
+ "JSON object body template for the revoke request",
437
+ )
438
+ .option(
439
+ "--display-name <name>",
440
+ "Human-readable display name for the provider",
441
+ )
442
+ .option("--description <text>", "Short description of the provider")
443
+ .option(
444
+ "--dashboard-url <url>",
445
+ "URL to the provider's developer console / dashboard",
446
+ )
447
+ .option(
448
+ "--logo-url <url>",
449
+ "URL to the provider's logo image. Mutually exclusive with --logo-simpleicons-slug.",
450
+ )
451
+ .option(
452
+ "--logo-simpleicons-slug <slug>",
453
+ 'Simple Icons slug (e.g. "notion"). Mutually exclusive with --logo-url.',
454
+ )
455
+ .option(
456
+ "--client-id-placeholder <text>",
457
+ "Placeholder text shown in the client ID input field",
458
+ )
459
+ .option(
460
+ "--no-client-secret",
461
+ "Mark this provider as not requiring a client secret",
462
+ )
463
+ .option(
464
+ "--loopback-port <port>",
465
+ "Fixed port for the local OAuth callback server",
466
+ )
467
+ .option(
468
+ "--injection-templates <json>",
469
+ "JSON array of token injection templates",
470
+ )
471
+ .option(
472
+ "--app-type <type>",
473
+ 'What the provider calls its OAuth apps (e.g. "OAuth App")',
474
+ )
475
+ .option(
476
+ "--identity-url <url>",
477
+ "Identity verification endpoint URL",
478
+ )
479
+ .option(
480
+ "--identity-method <method>",
481
+ "HTTP method for the identity endpoint: GET (default) or POST",
482
+ )
483
+ .option(
484
+ "--identity-headers <json>",
485
+ "JSON object of extra headers for the identity request",
486
+ )
487
+ .option(
488
+ "--identity-body <body>",
489
+ "JSON body to send with the identity request",
490
+ )
491
+ .option(
492
+ "--identity-response-paths <paths>",
493
+ "Comma-separated dot-notation paths to extract identity from the response",
494
+ )
495
+ .option(
496
+ "--identity-format <template>",
497
+ "Format template for the extracted identity",
498
+ )
499
+ .option(
500
+ "--identity-ok-field <field>",
501
+ "Dot-notation path to a boolean field that must be truthy for the response to be valid",
502
+ )
503
+ .option(
504
+ "--setup-notes <json>",
505
+ "JSON array of setup instruction notes shown during guided setup",
506
+ )
507
+ .option(
508
+ "--available-scopes <value>",
509
+ "Available scopes: either a JSON array of {scope, description?} objects or a URL",
510
+ )
511
+ .addHelpText(
512
+ "after",
513
+ `
526
514
  Registers a new OAuth provider configuration in the local store for custom
527
515
  integrations not covered by the built-in provider seeds. The provider key
528
516
  must be unique — if it collides with an existing key, the command fails.
@@ -532,10 +520,6 @@ On success, returns the full provider row including generated timestamps.
532
520
  After registering, create an OAuth app with 'assistant oauth apps create'
533
521
  and then connect with 'assistant oauth connect <provider-key>'.
534
522
 
535
- Token injection templates control how the OAuth access token is injected
536
- into outgoing HTTP requests matched by host pattern. Identity config
537
- defines how the assistant verifies the connected account after OAuth.
538
-
539
523
  Examples:
540
524
  $ assistant oauth providers register \\
541
525
  --provider-key custom-api \\
@@ -545,536 +529,418 @@ Examples:
545
529
  --provider-key my-service \\
546
530
  --auth-url https://my-service.com/auth \\
547
531
  --token-url https://my-service.com/token \\
548
- --scopes read,write --json
549
- $ assistant oauth providers register \\
550
- --provider-key my-graphql-api \\
551
- --auth-url https://example.com/auth \\
552
- --token-url https://example.com/token \\
553
- --ping-url https://example.com/graphql \\
554
- --ping-method POST \\
555
- --ping-body '{"query":"{ viewer { id } }"}'
556
- $ assistant oauth providers register \\
557
- --provider-key linear-custom \\
558
- --auth-url https://linear.app/oauth/authorize \\
559
- --token-url https://api.linear.app/oauth/token \\
560
- --scopes read,write \\
561
- --scope-separator ","
562
- $ assistant oauth providers register \\
563
- --provider-key split-grants \\
564
- --auth-url https://example.com/oauth/authorize \\
565
- --token-url https://example.com/oauth/token \\
566
- --refresh-url https://example.com/oauth/refresh
567
- $ assistant oauth providers register \\
568
- --provider-key my-api \\
569
- --auth-url https://example.com/auth \\
570
- --token-url https://example.com/token \\
571
- --loopback-port 17400 \\
572
- --injection-templates '[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]' \\
573
- --identity-url https://api.example.com/me \\
574
- --identity-response-paths email,name
575
- $ assistant oauth providers register \\
576
- --provider-key custom-revokable \\
577
- --auth-url https://example.com/oauth/authorize \\
578
- --token-url https://example.com/oauth/token \\
579
- --revoke-url https://example.com/oauth/revoke \\
580
- --revoke-body-template '{"token":"{access_token}","client_id":"{client_id}"}'
581
- $ assistant oauth providers register \\
582
- --provider-key notion-custom \\
583
- --auth-url https://api.notion.com/v1/oauth/authorize \\
584
- --token-url https://api.notion.com/v1/oauth/token \\
585
- --token-exchange-body-format json \\
586
- --logo-simpleicons-slug notion`,
587
- )
588
- .action(
589
- (
590
- opts: {
591
- providerKey: string;
592
- authUrl: string;
593
- tokenUrl: string;
594
- refreshUrl?: string;
595
- baseUrl?: string;
596
- userinfoUrl?: string;
597
- scopes?: string;
598
- scopeSeparator?: string;
599
- tokenAuthMethod?: string;
600
- tokenExchangeBodyFormat?: string;
601
- pingUrl?: string;
602
- pingMethod?: string;
603
- pingHeaders?: string;
604
- pingBody?: string;
605
- revokeUrl?: string;
606
- revokeBodyTemplate?: string;
607
- displayName?: string;
608
- description?: string;
609
- dashboardUrl?: string;
610
- logoUrl?: string;
611
- logoSimpleiconsSlug?: string;
612
- clientIdPlaceholder?: string;
613
- clientSecret: boolean;
614
- loopbackPort?: string;
615
- injectionTemplates?: string;
616
- appType?: string;
617
- identityUrl?: string;
618
- identityMethod?: string;
619
- identityHeaders?: string;
620
- identityBody?: string;
621
- identityResponsePaths?: string;
622
- identityFormat?: string;
623
- identityOkField?: string;
624
- setupNotes?: string;
625
- availableScopes?: string;
626
- },
627
- cmd: Command,
628
- ) => {
629
- try {
630
- const resolvedLogoUrl = resolveLogoUrlFromFlags(opts);
631
- if (resolvedLogoUrl === null) {
632
- throw new Error(
633
- "Cannot clear logo_url with empty --logo-url during registration. Omit the flag instead.",
634
- );
635
- }
636
-
637
- const row = registerProvider({
638
- provider: opts.providerKey,
639
- authorizeUrl: opts.authUrl,
640
- tokenExchangeUrl: opts.tokenUrl,
641
- refreshUrl: opts.refreshUrl,
642
- baseUrl: opts.baseUrl,
643
- userinfoUrl: opts.userinfoUrl,
644
- defaultScopes: opts.scopes ? opts.scopes.split(",") : [],
645
- availableScopes: opts.availableScopes
646
- ? opts.availableScopes.startsWith("http")
647
- ? opts.availableScopes
648
- : JSON.parse(opts.availableScopes)
649
- : undefined,
650
- scopeSeparator: opts.scopeSeparator,
651
- tokenEndpointAuthMethod: opts.tokenAuthMethod,
652
- tokenExchangeBodyFormat: opts.tokenExchangeBodyFormat,
653
- pingUrl: opts.pingUrl,
654
- pingMethod: opts.pingMethod,
655
- pingHeaders: opts.pingHeaders
656
- ? JSON.parse(opts.pingHeaders)
657
- : undefined,
658
- pingBody: opts.pingBody ? JSON.parse(opts.pingBody) : undefined,
659
- revokeUrl: opts.revokeUrl,
660
- revokeBodyTemplate: opts.revokeBodyTemplate
661
- ? JSON.parse(opts.revokeBodyTemplate)
662
- : undefined,
663
- displayLabel: opts.displayName,
664
- description: opts.description,
665
- dashboardUrl: opts.dashboardUrl,
666
- logoUrl: resolvedLogoUrl,
667
- clientIdPlaceholder: opts.clientIdPlaceholder,
668
- requiresClientSecret: opts.clientSecret ? 1 : 0,
669
- loopbackPort: opts.loopbackPort
670
- ? parseInt(opts.loopbackPort, 10)
671
- : undefined,
672
- injectionTemplates: opts.injectionTemplates
673
- ? JSON.parse(opts.injectionTemplates)
674
- : undefined,
675
- appType: opts.appType,
676
- identityUrl: opts.identityUrl,
677
- identityMethod: opts.identityMethod,
678
- identityHeaders: opts.identityHeaders
679
- ? JSON.parse(opts.identityHeaders)
680
- : undefined,
681
- identityBody: opts.identityBody
682
- ? JSON.parse(opts.identityBody)
683
- : undefined,
684
- identityResponsePaths: opts.identityResponsePaths
685
- ? opts.identityResponsePaths.split(",")
686
- : undefined,
687
- identityFormat: opts.identityFormat,
688
- identityOkField: opts.identityOkField,
689
- setupNotes: opts.setupNotes
690
- ? JSON.parse(opts.setupNotes)
691
- : undefined,
692
- });
693
-
694
- writeOutput(cmd, parseProviderRow(row));
695
- } catch (err) {
696
- let message = err instanceof Error ? err.message : String(err);
697
- if (message.includes("already exists")) {
698
- message += ` Run 'assistant oauth providers list' to see existing providers, or choose a different --provider-key.`;
699
- }
700
- writeOutput(cmd, { ok: false, error: message });
701
- process.exitCode = 1;
702
- }
703
- },
704
- );
705
-
706
- // ---------------------------------------------------------------------------
707
- // providers update <provider-key>
708
- // ---------------------------------------------------------------------------
532
+ --scopes read,write --json`,
533
+ )
534
+ .action(
535
+ async (
536
+ opts: {
537
+ providerKey: string;
538
+ authUrl: string;
539
+ tokenUrl: string;
540
+ refreshUrl?: string;
541
+ baseUrl?: string;
542
+ userinfoUrl?: string;
543
+ scopes?: string;
544
+ scopeSeparator?: string;
545
+ tokenAuthMethod?: string;
546
+ tokenExchangeBodyFormat?: string;
547
+ pingUrl?: string;
548
+ pingMethod?: string;
549
+ pingHeaders?: string;
550
+ pingBody?: string;
551
+ revokeUrl?: string;
552
+ revokeBodyTemplate?: string;
553
+ displayName?: string;
554
+ description?: string;
555
+ dashboardUrl?: string;
556
+ logoUrl?: string;
557
+ logoSimpleiconsSlug?: string;
558
+ clientIdPlaceholder?: string;
559
+ clientSecret: boolean;
560
+ loopbackPort?: string;
561
+ injectionTemplates?: string;
562
+ appType?: string;
563
+ identityUrl?: string;
564
+ identityMethod?: string;
565
+ identityHeaders?: string;
566
+ identityBody?: string;
567
+ identityResponsePaths?: string;
568
+ identityFormat?: string;
569
+ identityOkField?: string;
570
+ setupNotes?: string;
571
+ availableScopes?: string;
572
+ },
573
+ cmd: Command,
574
+ ) => {
575
+ try {
576
+ const resolvedLogoUrl = resolveLogoUrlFromFlags(opts);
577
+ if (resolvedLogoUrl === null) {
578
+ throw new Error(
579
+ "Cannot clear logo_url with empty --logo-url during registration. Omit the flag instead.",
580
+ );
581
+ }
582
+
583
+ const body: Record<string, unknown> = {
584
+ provider_key: opts.providerKey,
585
+ auth_url: opts.authUrl,
586
+ token_url: opts.tokenUrl,
587
+ };
588
+
589
+ if (opts.refreshUrl !== undefined)
590
+ body.refresh_url = opts.refreshUrl;
591
+ if (opts.baseUrl !== undefined) body.base_url = opts.baseUrl;
592
+ if (opts.userinfoUrl !== undefined)
593
+ body.userinfo_url = opts.userinfoUrl;
594
+ body.default_scopes = opts.scopes ? opts.scopes.split(",") : [];
595
+ if (opts.scopeSeparator !== undefined)
596
+ body.scope_separator = opts.scopeSeparator;
597
+ if (opts.tokenAuthMethod !== undefined)
598
+ body.token_endpoint_auth_method = opts.tokenAuthMethod;
599
+ if (opts.tokenExchangeBodyFormat !== undefined)
600
+ body.token_exchange_body_format = opts.tokenExchangeBodyFormat;
601
+ if (opts.pingUrl !== undefined) body.ping_url = opts.pingUrl;
602
+ if (opts.pingMethod !== undefined)
603
+ body.ping_method = opts.pingMethod;
604
+ if (opts.pingHeaders !== undefined)
605
+ body.ping_headers = JSON.parse(opts.pingHeaders);
606
+ if (opts.pingBody !== undefined)
607
+ body.ping_body = JSON.parse(opts.pingBody);
608
+ if (opts.revokeUrl !== undefined) body.revoke_url = opts.revokeUrl;
609
+ if (opts.revokeBodyTemplate !== undefined)
610
+ body.revoke_body_template = JSON.parse(opts.revokeBodyTemplate);
611
+ if (opts.displayName !== undefined)
612
+ body.display_name = opts.displayName;
613
+ if (opts.description !== undefined)
614
+ body.description = opts.description;
615
+ if (opts.dashboardUrl !== undefined)
616
+ body.dashboard_url = opts.dashboardUrl;
617
+ if (resolvedLogoUrl !== undefined)
618
+ body.logo_url = resolvedLogoUrl;
619
+ if (opts.clientIdPlaceholder !== undefined)
620
+ body.client_id_placeholder = opts.clientIdPlaceholder;
621
+ body.requires_client_secret = opts.clientSecret;
622
+ if (opts.loopbackPort !== undefined)
623
+ body.loopback_port = parseInt(opts.loopbackPort, 10);
624
+ if (opts.injectionTemplates !== undefined)
625
+ body.injection_templates = JSON.parse(opts.injectionTemplates);
626
+ if (opts.appType !== undefined) body.app_type = opts.appType;
627
+ if (opts.identityUrl !== undefined)
628
+ body.identity_url = opts.identityUrl;
629
+ if (opts.identityMethod !== undefined)
630
+ body.identity_method = opts.identityMethod;
631
+ if (opts.identityHeaders !== undefined)
632
+ body.identity_headers = JSON.parse(opts.identityHeaders);
633
+ if (opts.identityBody !== undefined)
634
+ body.identity_body = JSON.parse(opts.identityBody);
635
+ if (opts.identityResponsePaths !== undefined)
636
+ body.identity_response_paths =
637
+ opts.identityResponsePaths.split(",");
638
+ if (opts.identityFormat !== undefined)
639
+ body.identity_format = opts.identityFormat;
640
+ if (opts.identityOkField !== undefined)
641
+ body.identity_ok_field = opts.identityOkField;
642
+ if (opts.setupNotes !== undefined)
643
+ body.setup_notes = JSON.parse(opts.setupNotes);
644
+ if (opts.availableScopes !== undefined) {
645
+ body.available_scopes = opts.availableScopes.startsWith("http")
646
+ ? opts.availableScopes
647
+ : JSON.parse(opts.availableScopes);
648
+ }
649
+
650
+ const r = await cliIpcCall<{ provider: SerializedProvider }>(
651
+ "oauth_providers_post",
652
+ { body },
653
+ );
709
654
 
710
- providers
711
- .command("update <provider-key>")
712
- .description("Update an existing custom OAuth provider configuration")
713
- .option(
714
- "--auth-url <url>",
715
- "OAuth authorization endpoint URL (e.g. https://accounts.example.com/o/oauth2/auth)",
716
- )
717
- .option(
718
- "--token-url <url>",
719
- "OAuth token endpoint URL (e.g. https://oauth2.example.com/token)",
720
- )
721
- .option(
722
- "--refresh-url <url>",
723
- "OAuth token refresh endpoint URL. Defaults to --token-url when omitted. Set this when the provider uses a different endpoint for the refresh_token grant than for the authorization_code grant.",
724
- )
725
- .option("--base-url <url>", "API base URL for the service")
726
- .option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
727
- .option(
728
- "--scopes <scopes>",
729
- 'Comma-separated default scopes (e.g. "read,write,profile")',
730
- )
731
- .option(
732
- "--scope-separator <sep>",
733
- 'Separator used to join scopes in the authorize URL (default: " "). Use "," for providers like Linear that expect comma-separated scopes.',
734
- )
735
- .option(
736
- "--token-auth-method <method>",
737
- 'How the client authenticates at the token endpoint: "client_secret_post" or "client_secret_basic"',
738
- )
739
- .option(
740
- "--token-exchange-body-format <format>",
741
- 'Body encoding for the token exchange request: "form" (application/x-www-form-urlencoded, default) or "json" (application/json)',
742
- )
743
- .option(
744
- "--ping-url <url>",
745
- 'Health-check endpoint URL for token validation (e.g. "https://api.example.com/user"). Used by "assistant oauth ping" to verify a stored token.',
746
- )
747
- .option(
748
- "--ping-method <method>",
749
- "HTTP method for the ping endpoint: GET (default) or POST",
750
- )
751
- .option(
752
- "--ping-headers <json>",
753
- 'JSON object of extra headers for the ping request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
754
- )
755
- .option(
756
- "--ping-body <json>",
757
- 'JSON body to send with the ping request (e.g. \'{"query":"{ viewer { id } }"}\')',
758
- )
759
- .option(
760
- "--revoke-url <url>",
761
- "OAuth token revocation endpoint URL. Called best-effort during disconnect to invalidate the access token upstream. Pass an empty string to clear.",
762
- )
763
- .option(
764
- "--revoke-body-template <json>",
765
- "JSON object body template for the revoke request, supporting {access_token} and {client_id} substitution. Pass an empty string to clear.",
766
- )
767
- .option(
768
- "--display-name <name>",
769
- "Human-readable display name for the provider",
770
- )
771
- .option("--description <text>", "Short description of the provider")
772
- .option(
773
- "--dashboard-url <url>",
774
- "URL to the provider's developer console / dashboard",
775
- )
776
- .option(
777
- "--logo-url <url>",
778
- "URL to the provider's logo image (SVG or PNG). Mutually exclusive with --logo-simpleicons-slug.",
779
- )
780
- .option(
781
- "--logo-simpleicons-slug <slug>",
782
- 'Simple Icons slug (e.g. "notion", "linear"). Resolves to https://cdn.simpleicons.org/<slug>. Mutually exclusive with --logo-url.',
783
- )
784
- .option(
785
- "--client-id-placeholder <text>",
786
- "Placeholder text shown in the client ID input field",
787
- )
788
- .option(
789
- "--no-client-secret",
790
- "Mark this provider as not requiring a client secret (default: required)",
791
- )
792
- .option(
793
- "--loopback-port <port>",
794
- "Fixed port for the local OAuth callback server (e.g. 17322). When set, the redirect URI is http://localhost:<port>/oauth/callback",
795
- )
796
- .option(
797
- "--injection-templates <json>",
798
- 'JSON array of token injection templates — each with hostPattern, injectionType, headerName, valuePrefix (e.g. \'[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]\')',
799
- )
800
- .option(
801
- "--app-type <type>",
802
- 'What the provider calls its OAuth apps (e.g. "OAuth App", "Desktop app", "Public integration")',
803
- )
804
- .option(
805
- "--identity-url <url>",
806
- "Identity verification endpoint URL — called after OAuth to identify the connected account",
807
- )
808
- .option(
809
- "--identity-method <method>",
810
- "HTTP method for the identity endpoint: GET (default) or POST",
811
- )
812
- .option(
813
- "--identity-headers <json>",
814
- 'JSON object of extra headers for the identity request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
815
- )
816
- .option(
817
- "--identity-body <body>",
818
- 'JSON body to send with the identity request (e.g. \'{"query":"{ viewer { email } }"}\')',
819
- )
820
- .option(
821
- "--identity-response-paths <paths>",
822
- 'Comma-separated dot-notation paths to extract identity from the response (e.g. "email,name,person.email")',
823
- )
824
- .option(
825
- "--identity-format <template>",
826
- 'Format template for the extracted identity using ${pathName} tokens from --identity-response-paths (e.g. "@${login}" or "@${user} (${team})")',
827
- )
828
- .option(
829
- "--identity-ok-field <field>",
830
- 'Dot-notation path to a boolean field that must be truthy for the response to be considered valid (e.g. "ok")',
831
- )
832
- .option(
833
- "--setup-notes <json>",
834
- 'JSON array of setup instruction notes shown during guided setup (e.g. \'["Enable the Gmail API","Add test users"]\')',
835
- )
836
- .option(
837
- "--available-scopes <value>",
838
- "Available scopes: either a JSON array of {scope, description?} objects or a URL to the provider scope docs",
839
- )
840
- .addHelpText(
841
- "after",
842
- `
655
+ if (!r.ok) {
656
+ let message = r.error ?? "Unknown error";
657
+ if (message.includes("already exists")) {
658
+ message += ` Run 'assistant oauth providers list' to see existing providers, or choose a different --provider-key.`;
659
+ }
660
+ writeOutput(cmd, { ok: false, error: message });
661
+ process.exitCode = 1;
662
+ return;
663
+ }
664
+
665
+ writeOutput(cmd, r.result?.provider);
666
+ } catch (err) {
667
+ const message = err instanceof Error ? err.message : String(err);
668
+ writeOutput(cmd, { ok: false, error: message });
669
+ process.exitCode = 1;
670
+ }
671
+ },
672
+ );
673
+
674
+ // -----------------------------------------------------------------------
675
+ // providers update <provider-key>
676
+ // -----------------------------------------------------------------------
677
+
678
+ providers
679
+ .command("update <provider-key>")
680
+ .description("Update an existing custom OAuth provider configuration")
681
+ .option(
682
+ "--auth-url <url>",
683
+ "OAuth authorization endpoint URL",
684
+ )
685
+ .option(
686
+ "--token-url <url>",
687
+ "OAuth token endpoint URL",
688
+ )
689
+ .option(
690
+ "--refresh-url <url>",
691
+ "OAuth token refresh endpoint URL",
692
+ )
693
+ .option("--base-url <url>", "API base URL for the service")
694
+ .option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
695
+ .option(
696
+ "--scopes <scopes>",
697
+ 'Comma-separated default scopes (e.g. "read,write,profile")',
698
+ )
699
+ .option(
700
+ "--scope-separator <sep>",
701
+ 'Separator used to join scopes in the authorize URL',
702
+ )
703
+ .option(
704
+ "--token-auth-method <method>",
705
+ 'How the client authenticates at the token endpoint',
706
+ )
707
+ .option(
708
+ "--token-exchange-body-format <format>",
709
+ 'Body encoding for the token exchange request: "form" or "json"',
710
+ )
711
+ .option("--ping-url <url>", "Health-check endpoint URL")
712
+ .option(
713
+ "--ping-method <method>",
714
+ "HTTP method for the ping endpoint: GET (default) or POST",
715
+ )
716
+ .option("--ping-headers <json>", "JSON object of extra headers for the ping request")
717
+ .option("--ping-body <json>", "JSON body for the ping request")
718
+ .option(
719
+ "--revoke-url <url>",
720
+ "OAuth token revocation endpoint URL. Pass empty string to clear.",
721
+ )
722
+ .option(
723
+ "--revoke-body-template <json>",
724
+ "JSON object body template for the revoke request. Pass empty string to clear.",
725
+ )
726
+ .option("--display-name <name>", "Human-readable display name")
727
+ .option("--description <text>", "Short description")
728
+ .option("--dashboard-url <url>", "Developer console / dashboard URL")
729
+ .option(
730
+ "--logo-url <url>",
731
+ "URL to the provider's logo image. Mutually exclusive with --logo-simpleicons-slug.",
732
+ )
733
+ .option(
734
+ "--logo-simpleicons-slug <slug>",
735
+ 'Simple Icons slug. Mutually exclusive with --logo-url.',
736
+ )
737
+ .option("--client-id-placeholder <text>", "Placeholder for client ID input")
738
+ .option("--no-client-secret", "Mark as not requiring a client secret")
739
+ .option("--loopback-port <port>", "Fixed port for the local OAuth callback server")
740
+ .option("--injection-templates <json>", "JSON array of token injection templates")
741
+ .option("--app-type <type>", "What the provider calls its OAuth apps")
742
+ .option("--identity-url <url>", "Identity verification endpoint URL")
743
+ .option("--identity-method <method>", "HTTP method for identity endpoint")
744
+ .option("--identity-headers <json>", "JSON object of extra headers for identity request")
745
+ .option("--identity-body <body>", "JSON body for identity request")
746
+ .option("--identity-response-paths <paths>", "Comma-separated dot-notation paths")
747
+ .option("--identity-format <template>", "Format template for extracted identity")
748
+ .option("--identity-ok-field <field>", "Dot-notation path to a boolean ok field")
749
+ .option("--setup-notes <json>", "JSON array of setup instruction notes")
750
+ .option("--available-scopes <value>", "Available scopes: JSON array or URL")
751
+ .addHelpText(
752
+ "after",
753
+ `
843
754
  Arguments:
844
755
  provider-key Provider key to update (e.g. "custom-api").
845
756
  Run 'assistant oauth providers list' to see all registered providers.
846
757
 
847
758
  Only the fields you specify are updated — all other fields remain unchanged.
848
759
  Built-in providers (e.g. "google", "slack") cannot be updated; they are
849
- managed by the system and reset on startup. To create a custom provider with
850
- different settings, use 'assistant oauth providers register'.
851
-
852
- Token injection templates control how the OAuth access token is injected
853
- into outgoing HTTP requests matched by host pattern. Identity config
854
- defines how the assistant verifies the connected account after OAuth.
760
+ managed by the system and reset on startup.
855
761
 
856
762
  Examples:
857
763
  $ assistant oauth providers update custom-api --display-name "My Custom API"
858
764
  $ assistant oauth providers update custom-api --scopes read,write --auth-url https://new.example.com/auth
859
765
  $ assistant oauth providers update custom-api --ping-url https://api.example.com/me --json
860
- $ assistant oauth providers update custom-api --scope-separator ","
861
- $ assistant oauth providers update custom-api --refresh-url https://example.com/oauth/refresh
862
- $ assistant oauth providers update custom-api \\
863
- --identity-url https://api.example.com/me \\
864
- --identity-response-paths email,name
865
- $ assistant oauth providers update custom-api \\
866
- --injection-templates '[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]'
867
- $ assistant oauth providers update custom-api \\
868
- --revoke-url https://api.example.com/oauth/revoke \\
869
- --revoke-body-template '{"token":"{access_token}"}'
870
- $ assistant oauth providers update custom-api --logo-simpleicons-slug notion
871
766
  $ assistant oauth providers update custom-api --logo-url ""`,
872
- )
873
- .action(
874
- (
875
- provider: string,
876
- opts: {
877
- authUrl?: string;
878
- tokenUrl?: string;
879
- refreshUrl?: string;
880
- baseUrl?: string;
881
- userinfoUrl?: string;
882
- scopes?: string;
883
- scopeSeparator?: string;
884
- tokenAuthMethod?: string;
885
- tokenExchangeBodyFormat?: string;
886
- pingUrl?: string;
887
- pingMethod?: string;
888
- pingHeaders?: string;
889
- pingBody?: string;
890
- revokeUrl?: string;
891
- revokeBodyTemplate?: string;
892
- displayName?: string;
893
- description?: string;
894
- dashboardUrl?: string;
895
- logoUrl?: string;
896
- logoSimpleiconsSlug?: string;
897
- clientIdPlaceholder?: string;
898
- clientSecret: boolean;
899
- loopbackPort?: string;
900
- injectionTemplates?: string;
901
- appType?: string;
902
- identityUrl?: string;
903
- identityMethod?: string;
904
- identityHeaders?: string;
905
- identityBody?: string;
906
- identityResponsePaths?: string;
907
- identityFormat?: string;
908
- identityOkField?: string;
909
- setupNotes?: string;
910
- availableScopes?: string;
911
- },
912
- cmd: Command,
913
- ) => {
914
- try {
915
- // Verify provider exists
916
- const existing = getProvider(provider);
917
- if (!existing) {
918
- writeOutput(cmd, {
919
- ok: false,
920
- error: `Provider "${provider}" not found. Run 'assistant oauth providers list' to see all registered providers.`,
921
- });
922
- process.exitCode = 1;
923
- return;
924
- }
925
-
926
- if (!isProviderVisible(existing, loadConfig())) {
927
- writeOutput(cmd, {
928
- ok: false,
929
- error: `Provider "${provider}" not found. Run 'assistant oauth providers list' to see all registered providers.`,
930
- });
931
- process.exitCode = 1;
932
- return;
933
- }
934
-
935
- // Block updates to built-in providers
936
- if (SEEDED_PROVIDER_KEYS.has(provider)) {
937
- writeOutput(cmd, {
938
- ok: false,
939
- error: `Cannot update built-in provider "${provider}". Built-in providers are managed by the system and reset on startup. To create a custom provider with different settings, use 'assistant oauth providers register --provider-key <your-custom-key> ...'`,
940
- });
941
- process.exitCode = 1;
942
- return;
943
- }
944
-
945
- // Build params object from provided options, omitting undefined values
946
- const params: Record<string, unknown> = {};
947
-
948
- if (opts.authUrl !== undefined) params.authorizeUrl = opts.authUrl;
949
- if (opts.tokenUrl !== undefined)
950
- params.tokenExchangeUrl = opts.tokenUrl;
951
- if (opts.refreshUrl !== undefined)
952
- params.refreshUrl = opts.refreshUrl;
953
- if (opts.baseUrl !== undefined) params.baseUrl = opts.baseUrl;
954
- if (opts.userinfoUrl !== undefined)
955
- params.userinfoUrl = opts.userinfoUrl;
956
- if (opts.scopes !== undefined)
957
- params.defaultScopes = opts.scopes.split(",");
958
- if (opts.scopeSeparator !== undefined)
959
- params.scopeSeparator = opts.scopeSeparator;
960
- if (opts.tokenAuthMethod !== undefined)
961
- params.tokenEndpointAuthMethod = opts.tokenAuthMethod;
962
- if (opts.tokenExchangeBodyFormat !== undefined)
963
- params.tokenExchangeBodyFormat = opts.tokenExchangeBodyFormat;
964
- if (opts.pingUrl !== undefined) params.pingUrl = opts.pingUrl;
965
- if (opts.pingMethod !== undefined)
966
- params.pingMethod = opts.pingMethod;
967
- if (opts.pingHeaders !== undefined)
968
- params.pingHeaders = JSON.parse(opts.pingHeaders);
969
- if (opts.pingBody !== undefined)
970
- params.pingBody = JSON.parse(opts.pingBody);
971
- if (opts.revokeUrl !== undefined) {
972
- // Empty string means "clear" — normalize to null so the stored
973
- // value matches the "disabled" semantics documented in the help
974
- // text. `updateProvider`'s Partial type accepts `string | null`
975
- // for this field so drizzle writes `null` to clear the column.
976
- params.revokeUrl = opts.revokeUrl === "" ? null : opts.revokeUrl;
977
- }
978
- if (opts.revokeBodyTemplate !== undefined) {
979
- // Empty string means "clear" — normalize to null to match --revoke-url's
980
- // empty-string-clear semantics documented in the help text. The
981
- // updateProvider type accepts `Record<string, string> | null` for this.
982
- params.revokeBodyTemplate =
983
- opts.revokeBodyTemplate === ""
984
- ? null
985
- : JSON.parse(opts.revokeBodyTemplate);
986
- }
987
- if (opts.displayName !== undefined)
988
- params.displayLabel = opts.displayName;
989
- if (opts.description !== undefined)
990
- params.description = opts.description;
991
- if (opts.dashboardUrl !== undefined)
992
- params.dashboardUrl = opts.dashboardUrl;
993
- if (opts.clientIdPlaceholder !== undefined)
994
- params.clientIdPlaceholder = opts.clientIdPlaceholder;
995
-
996
- const resolvedLogoUrl = resolveLogoUrlFromFlags(opts);
997
- if (resolvedLogoUrl !== undefined) {
998
- params.logoUrl = resolvedLogoUrl;
999
- }
1000
-
1001
- // Handle the negated --no-client-* flag: Commander defaults
1002
- // opts.clientSecret to true; the negated form sets it to false.
1003
- // Use getOptionValueSource to detect explicit user intent.
1004
- if (cmd.getOptionValueSource("clientSecret") === "cli") {
1005
- params.requiresClientSecret = opts.clientSecret ? 1 : 0;
1006
- }
767
+ )
768
+ .action(
769
+ async (
770
+ provider: string,
771
+ opts: {
772
+ authUrl?: string;
773
+ tokenUrl?: string;
774
+ refreshUrl?: string;
775
+ baseUrl?: string;
776
+ userinfoUrl?: string;
777
+ scopes?: string;
778
+ scopeSeparator?: string;
779
+ tokenAuthMethod?: string;
780
+ tokenExchangeBodyFormat?: string;
781
+ pingUrl?: string;
782
+ pingMethod?: string;
783
+ pingHeaders?: string;
784
+ pingBody?: string;
785
+ revokeUrl?: string;
786
+ revokeBodyTemplate?: string;
787
+ displayName?: string;
788
+ description?: string;
789
+ dashboardUrl?: string;
790
+ logoUrl?: string;
791
+ logoSimpleiconsSlug?: string;
792
+ clientIdPlaceholder?: string;
793
+ clientSecret: boolean;
794
+ loopbackPort?: string;
795
+ injectionTemplates?: string;
796
+ appType?: string;
797
+ identityUrl?: string;
798
+ identityMethod?: string;
799
+ identityHeaders?: string;
800
+ identityBody?: string;
801
+ identityResponsePaths?: string;
802
+ identityFormat?: string;
803
+ identityOkField?: string;
804
+ setupNotes?: string;
805
+ availableScopes?: string;
806
+ },
807
+ cmd: Command,
808
+ ) => {
809
+ try {
810
+ const body: Record<string, unknown> = {};
811
+
812
+ if (opts.authUrl !== undefined) body.auth_url = opts.authUrl;
813
+ if (opts.tokenUrl !== undefined) body.token_url = opts.tokenUrl;
814
+ if (opts.refreshUrl !== undefined)
815
+ body.refresh_url = opts.refreshUrl;
816
+ if (opts.baseUrl !== undefined) body.base_url = opts.baseUrl;
817
+ if (opts.userinfoUrl !== undefined)
818
+ body.userinfo_url = opts.userinfoUrl;
819
+ if (opts.scopes !== undefined)
820
+ body.default_scopes = opts.scopes.split(",");
821
+ if (opts.scopeSeparator !== undefined)
822
+ body.scope_separator = opts.scopeSeparator;
823
+ if (opts.tokenAuthMethod !== undefined)
824
+ body.token_endpoint_auth_method = opts.tokenAuthMethod;
825
+ if (opts.tokenExchangeBodyFormat !== undefined)
826
+ body.token_exchange_body_format = opts.tokenExchangeBodyFormat;
827
+ if (opts.pingUrl !== undefined) body.ping_url = opts.pingUrl;
828
+ if (opts.pingMethod !== undefined)
829
+ body.ping_method = opts.pingMethod;
830
+ if (opts.pingHeaders !== undefined)
831
+ body.ping_headers = JSON.parse(opts.pingHeaders);
832
+ if (opts.pingBody !== undefined)
833
+ body.ping_body = JSON.parse(opts.pingBody);
834
+ if (opts.revokeUrl !== undefined) {
835
+ body.revoke_url =
836
+ opts.revokeUrl === "" ? null : opts.revokeUrl;
837
+ }
838
+ if (opts.revokeBodyTemplate !== undefined) {
839
+ body.revoke_body_template =
840
+ opts.revokeBodyTemplate === ""
841
+ ? null
842
+ : JSON.parse(opts.revokeBodyTemplate);
843
+ }
844
+ if (opts.displayName !== undefined)
845
+ body.display_name = opts.displayName;
846
+ if (opts.description !== undefined)
847
+ body.description = opts.description;
848
+ if (opts.dashboardUrl !== undefined)
849
+ body.dashboard_url = opts.dashboardUrl;
850
+ if (opts.clientIdPlaceholder !== undefined)
851
+ body.client_id_placeholder = opts.clientIdPlaceholder;
852
+
853
+ const resolvedLogoUrl = resolveLogoUrlFromFlags(opts);
854
+ if (resolvedLogoUrl !== undefined) {
855
+ body.logo_url = resolvedLogoUrl;
856
+ }
857
+
858
+ if (cmd.getOptionValueSource("clientSecret") === "cli") {
859
+ body.requires_client_secret = opts.clientSecret;
860
+ }
861
+
862
+ if (opts.loopbackPort !== undefined)
863
+ body.loopback_port = parseInt(opts.loopbackPort, 10);
864
+ if (opts.injectionTemplates !== undefined)
865
+ body.injection_templates = JSON.parse(opts.injectionTemplates);
866
+ if (opts.appType !== undefined) body.app_type = opts.appType;
867
+ if (opts.identityUrl !== undefined)
868
+ body.identity_url = opts.identityUrl;
869
+ if (opts.identityMethod !== undefined)
870
+ body.identity_method = opts.identityMethod;
871
+ if (opts.identityHeaders !== undefined)
872
+ body.identity_headers = JSON.parse(opts.identityHeaders);
873
+ if (opts.identityBody !== undefined)
874
+ body.identity_body = JSON.parse(opts.identityBody);
875
+ if (opts.identityResponsePaths !== undefined)
876
+ body.identity_response_paths =
877
+ opts.identityResponsePaths.split(",");
878
+ if (opts.identityFormat !== undefined)
879
+ body.identity_format = opts.identityFormat;
880
+ if (opts.identityOkField !== undefined)
881
+ body.identity_ok_field = opts.identityOkField;
882
+ if (opts.setupNotes !== undefined)
883
+ body.setup_notes = JSON.parse(opts.setupNotes);
884
+ if (opts.availableScopes !== undefined) {
885
+ if (opts.availableScopes === "") {
886
+ body.available_scopes = null;
887
+ } else {
888
+ body.available_scopes =
889
+ opts.availableScopes.startsWith("http")
890
+ ? opts.availableScopes
891
+ : JSON.parse(opts.availableScopes);
892
+ }
893
+ }
894
+
895
+ if (Object.keys(body).length === 0) {
896
+ writeOutput(cmd, {
897
+ ok: false,
898
+ error:
899
+ "Nothing to update. Provide at least one option to change (e.g. --auth-url, --scopes, --display-name). Run 'assistant oauth providers update --help' for all options.",
900
+ });
901
+ process.exitCode = 1;
902
+ return;
903
+ }
904
+
905
+ const r = await cliIpcCall<{ provider: SerializedProvider }>(
906
+ "oauth_providers_by_providerKey_patch",
907
+ { pathParams: { providerKey: provider }, body },
908
+ );
1007
909
 
1008
- if (opts.loopbackPort !== undefined)
1009
- params.loopbackPort = parseInt(opts.loopbackPort, 10);
1010
- if (opts.injectionTemplates !== undefined)
1011
- params.injectionTemplates = JSON.parse(opts.injectionTemplates);
1012
- if (opts.appType !== undefined) params.appType = opts.appType;
1013
- if (opts.identityUrl !== undefined)
1014
- params.identityUrl = opts.identityUrl;
1015
- if (opts.identityMethod !== undefined)
1016
- params.identityMethod = opts.identityMethod;
1017
- if (opts.identityHeaders !== undefined)
1018
- params.identityHeaders = JSON.parse(opts.identityHeaders);
1019
- if (opts.identityBody !== undefined)
1020
- params.identityBody = JSON.parse(opts.identityBody);
1021
- if (opts.identityResponsePaths !== undefined)
1022
- params.identityResponsePaths =
1023
- opts.identityResponsePaths.split(",");
1024
- if (opts.identityFormat !== undefined)
1025
- params.identityFormat = opts.identityFormat;
1026
- if (opts.identityOkField !== undefined)
1027
- params.identityOkField = opts.identityOkField;
1028
- if (opts.setupNotes !== undefined)
1029
- params.setupNotes = JSON.parse(opts.setupNotes);
1030
- if (opts.availableScopes !== undefined) {
1031
- if (opts.availableScopes === "") {
1032
- params.availableScopes = null;
1033
- } else {
1034
- params.availableScopes = opts.availableScopes.startsWith("http")
1035
- ? opts.availableScopes
1036
- : JSON.parse(opts.availableScopes);
910
+ if (!r.ok) {
911
+ writeOutput(cmd, {
912
+ ok: false,
913
+ error: r.error ?? "Unknown error",
914
+ });
915
+ process.exitCode = 1;
916
+ return;
917
+ }
918
+
919
+ writeOutput(cmd, r.result?.provider);
920
+ } catch (err) {
921
+ const message = err instanceof Error ? err.message : String(err);
922
+ writeOutput(cmd, { ok: false, error: message });
923
+ process.exitCode = 1;
1037
924
  }
1038
- }
1039
-
1040
- // Check if any fields were actually provided
1041
- if (Object.keys(params).length === 0) {
1042
- writeOutput(cmd, {
1043
- ok: false,
1044
- error:
1045
- "Nothing to update. Provide at least one option to change (e.g. --auth-url, --scopes, --display-name). Run 'assistant oauth providers update --help' for all options.",
1046
- });
1047
- process.exitCode = 1;
1048
- return;
1049
- }
1050
-
1051
- const row = updateProvider(provider, params);
925
+ },
926
+ );
1052
927
 
1053
- writeOutput(cmd, parseProviderRow(row));
1054
- } catch (err) {
1055
- const message = err instanceof Error ? err.message : String(err);
1056
- writeOutput(cmd, { ok: false, error: message });
1057
- process.exitCode = 1;
1058
- }
1059
- },
1060
- );
928
+ // -----------------------------------------------------------------------
929
+ // providers delete <provider-key>
930
+ // -----------------------------------------------------------------------
1061
931
 
1062
- // ---------------------------------------------------------------------------
1063
- // providers delete <provider-key>
1064
- // ---------------------------------------------------------------------------
1065
-
1066
- providers
1067
- .command("delete <provider-key>")
1068
- .description(
1069
- "Delete a custom OAuth provider and optionally its associated apps and connections",
1070
- )
1071
- .option(
1072
- "--force",
1073
- "Cascade-delete all associated apps and connections before removing the provider",
1074
- )
1075
- .addHelpText(
1076
- "after",
1077
- `
932
+ providers
933
+ .command("delete <provider-key>")
934
+ .description(
935
+ "Delete a custom OAuth provider and optionally its associated apps and connections",
936
+ )
937
+ .option(
938
+ "--force",
939
+ "Cascade-delete all associated apps and connections before removing the provider",
940
+ )
941
+ .addHelpText(
942
+ "after",
943
+ `
1078
944
  Arguments:
1079
945
  provider-key Provider key to delete (e.g. "custom-api").
1080
946
  Run 'assistant oauth providers list' to see registered providers.
@@ -1092,92 +958,37 @@ Examples:
1092
958
  $ assistant oauth providers delete custom-api
1093
959
  $ assistant oauth providers delete custom-api --force
1094
960
  $ assistant oauth providers delete custom-api --force --json`,
1095
- )
1096
- .action(
1097
- async (provider: string, opts: { force?: boolean }, cmd: Command) => {
1098
- try {
1099
- const providerRow = getProvider(provider);
1100
- if (!providerRow) {
1101
- writeOutput(cmd, {
1102
- ok: false,
1103
- error: `Provider not found: "${provider}". Run 'assistant oauth providers list' to see all registered providers.`,
1104
- });
1105
- process.exitCode = 1;
1106
- return;
1107
- }
1108
-
1109
- if (!isProviderVisible(providerRow, loadConfig())) {
1110
- writeOutput(cmd, {
1111
- ok: false,
1112
- error: `Provider not found: "${provider}". Run 'assistant oauth providers list' to see all registered providers.`,
1113
- });
1114
- process.exitCode = 1;
1115
- return;
1116
- }
1117
-
1118
- if (SEEDED_PROVIDER_KEYS.has(provider) && !opts.force) {
1119
- log.info(
1120
- `Note: "${provider}" is a built-in provider and will be re-created on next startup.`,
1121
- );
1122
- }
1123
-
1124
- const dependentApps = listApps().filter(
1125
- (a) => a.provider === provider,
1126
- );
1127
- const dependentConnections = listConnections(provider);
1128
- const appCount = dependentApps.length;
1129
- const connCount = dependentConnections.length;
1130
-
1131
- if ((appCount > 0 || connCount > 0) && !opts.force) {
1132
- writeOutput(cmd, {
1133
- ok: false,
1134
- error: `Cannot delete provider "${provider}": ${appCount} app(s) and ${connCount} connection(s) depend on it. Use --force to cascade-delete all dependent apps and connections, or remove them manually first with 'assistant oauth apps delete' and 'assistant oauth disconnect'.`,
961
+ )
962
+ .action(
963
+ async (provider: string, opts: { force?: boolean }, cmd: Command) => {
964
+ const r = await cliIpcCall<{
965
+ ok: boolean;
966
+ deleted: {
967
+ provider: number;
968
+ apps: number;
969
+ connections: number;
970
+ };
971
+ }>("oauth_providers_by_providerKey_delete", {
972
+ pathParams: { providerKey: provider },
973
+ body: { force: opts.force ?? false },
1135
974
  });
1136
- process.exitCode = 1;
1137
- return;
1138
- }
1139
-
1140
- // Warn about built-in providers when --force is used
1141
- if (SEEDED_PROVIDER_KEYS.has(provider) && opts.force) {
1142
- log.info(
1143
- `Note: "${provider}" is a built-in provider and will be re-created on next startup.`,
1144
- );
1145
- }
1146
975
 
1147
- // Cascade-delete connections first, then apps, then the provider.
1148
- // Use disconnectOAuthProvider to clean up OAuth tokens from secure
1149
- // storage in addition to deleting the connection DB row.
1150
- for (const conn of dependentConnections) {
1151
- const result = await disconnectOAuthProvider(
1152
- provider,
1153
- undefined,
1154
- conn.id as string,
1155
- );
1156
- if (result === "error") {
1157
- log.info(
1158
- `Warning: failed to clean up tokens for connection ${conn.id} — deleting connection row to continue cascade.`,
1159
- );
1160
- deleteConnection(conn.id);
976
+ if (!r.ok) {
977
+ writeOutput(cmd, {
978
+ ok: false,
979
+ error: r.error ?? "Unknown error",
980
+ });
981
+ process.exitCode = 1;
982
+ return;
1161
983
  }
1162
- }
1163
- for (const app of dependentApps) {
1164
- await deleteApp(app.id);
1165
- }
1166
- deleteProvider(provider);
1167
984
 
1168
- if (!shouldOutputJson(cmd)) {
1169
- log.info(`Deleted provider: ${provider}`);
1170
- }
985
+ if (!shouldOutputJson(cmd)) {
986
+ log.info(`Deleted provider: ${provider}`);
987
+ }
1171
988
 
1172
- writeOutput(cmd, {
1173
- ok: true,
1174
- deleted: { provider: 1, apps: appCount, connections: connCount },
1175
- });
1176
- } catch (err) {
1177
- const message = err instanceof Error ? err.message : String(err);
1178
- writeOutput(cmd, { ok: false, error: message });
1179
- process.exitCode = 1;
1180
- }
1181
- },
1182
- );
989
+ writeOutput(cmd, r.result);
990
+ },
991
+ );
992
+ },
993
+ });
1183
994
  }