@capybara-hq/kernel 0.0.0-stage → 0.1.0-rc.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 (637) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +20 -2
  4. package/bootstrap.json +26 -0
  5. package/dist/admin/admin.controller.d.ts +139 -0
  6. package/dist/admin/admin.controller.js +238 -0
  7. package/dist/admin/extensions.controller.d.ts +100 -0
  8. package/dist/admin/extensions.controller.js +226 -0
  9. package/dist/admin/setup.controller.d.ts +36 -0
  10. package/dist/admin/setup.controller.js +106 -0
  11. package/dist/admin/setup.service.d.ts +47 -0
  12. package/dist/admin/setup.service.js +90 -0
  13. package/dist/agents/agents.controller.d.ts +205 -0
  14. package/dist/agents/agents.controller.js +144 -0
  15. package/dist/agents/agents.service.d.ts +177 -0
  16. package/dist/agents/agents.service.js +677 -0
  17. package/dist/agents/definition.d.ts +198 -0
  18. package/dist/agents/definition.js +152 -0
  19. package/dist/agents/models.controller.d.ts +13 -0
  20. package/dist/agents/models.controller.js +31 -0
  21. package/dist/agents/templates.controller.d.ts +15 -0
  22. package/dist/agents/templates.controller.js +37 -0
  23. package/dist/agents/templates.d.ts +83 -0
  24. package/dist/agents/templates.js +173 -0
  25. package/dist/catalog/admin-catalog.controller.d.ts +143 -0
  26. package/dist/catalog/admin-catalog.controller.js +245 -0
  27. package/dist/catalog/catalog.service.d.ts +430 -0
  28. package/dist/catalog/catalog.service.js +882 -0
  29. package/dist/catalog/claude/agents.d.ts +27 -0
  30. package/dist/catalog/claude/agents.js +270 -0
  31. package/dist/catalog/claude/commands.d.ts +50 -0
  32. package/dist/catalog/claude/commands.js +179 -0
  33. package/dist/catalog/claude/manifest.d.ts +69 -0
  34. package/dist/catalog/claude/manifest.js +134 -0
  35. package/dist/catalog/claude/marketplace.d.ts +35 -0
  36. package/dist/catalog/claude/marketplace.js +194 -0
  37. package/dist/catalog/claude/mcp.d.ts +33 -0
  38. package/dist/catalog/claude/mcp.js +226 -0
  39. package/dist/catalog/claude/report.d.ts +86 -0
  40. package/dist/catalog/claude/report.js +121 -0
  41. package/dist/catalog/claude/translate.d.ts +56 -0
  42. package/dist/catalog/claude/translate.js +857 -0
  43. package/dist/catalog/cli.d.ts +46 -0
  44. package/dist/catalog/cli.js +211 -0
  45. package/dist/catalog/git-source.d.ts +127 -0
  46. package/dist/catalog/git-source.js +520 -0
  47. package/dist/catalog/marketplace-fetch.d.ts +99 -0
  48. package/dist/catalog/marketplace-fetch.js +203 -0
  49. package/dist/catalog/marketplaces.controller.d.ts +97 -0
  50. package/dist/catalog/marketplaces.controller.js +197 -0
  51. package/dist/catalog/marketplaces.service.d.ts +340 -0
  52. package/dist/catalog/marketplaces.service.js +1217 -0
  53. package/dist/catalog/org-catalog.controller.d.ts +128 -0
  54. package/dist/catalog/org-catalog.controller.js +277 -0
  55. package/dist/catalog/org-catalog.service.d.ts +246 -0
  56. package/dist/catalog/org-catalog.service.js +625 -0
  57. package/dist/catalog/plugin-import.d.ts +436 -0
  58. package/dist/catalog/plugin-import.js +1069 -0
  59. package/dist/catalog/plugin-install.d.ts +447 -0
  60. package/dist/catalog/plugin-install.js +1333 -0
  61. package/dist/catalog/plugin-package.d.ts +143 -0
  62. package/dist/catalog/plugin-package.js +382 -0
  63. package/dist/catalog/plugin-upgrade.d.ts +222 -0
  64. package/dist/catalog/plugin-upgrade.js +1219 -0
  65. package/dist/catalog/requests.service.d.ts +162 -0
  66. package/dist/catalog/requests.service.js +504 -0
  67. package/dist/catalog/schema-path.d.ts +7 -0
  68. package/dist/catalog/schema-path.js +63 -0
  69. package/dist/catalog/upgrade-targets.d.ts +59 -0
  70. package/dist/catalog/upgrade-targets.js +90 -0
  71. package/dist/catalog/versions.d.ts +31 -0
  72. package/dist/catalog/versions.js +44 -0
  73. package/dist/channels/actions.d.ts +37 -0
  74. package/dist/channels/actions.js +121 -0
  75. package/dist/channels/cards.d.ts +44 -0
  76. package/dist/channels/cards.js +156 -0
  77. package/dist/channels/connections.controller.d.ts +59 -0
  78. package/dist/channels/connections.controller.js +115 -0
  79. package/dist/channels/connections.service.d.ts +187 -0
  80. package/dist/channels/connections.service.js +534 -0
  81. package/dist/channels/features.d.ts +12 -0
  82. package/dist/channels/features.js +22 -0
  83. package/dist/channels/inbound.d.ts +120 -0
  84. package/dist/channels/inbound.js +332 -0
  85. package/dist/channels/outbound.d.ts +140 -0
  86. package/dist/channels/outbound.js +1126 -0
  87. package/dist/channels/phrases.d.ts +100 -0
  88. package/dist/channels/phrases.js +81 -0
  89. package/dist/channels/supervisor.d.ts +182 -0
  90. package/dist/channels/supervisor.js +625 -0
  91. package/dist/channels/threads.d.ts +29 -0
  92. package/dist/channels/threads.js +104 -0
  93. package/dist/channels/webhook-url.d.ts +16 -0
  94. package/dist/channels/webhook-url.js +34 -0
  95. package/dist/channels/webhook.controller.d.ts +19 -0
  96. package/dist/channels/webhook.controller.js +72 -0
  97. package/dist/channels/webhook.d.ts +73 -0
  98. package/dist/channels/webhook.js +274 -0
  99. package/dist/config/docs.d.ts +9 -0
  100. package/dist/config/docs.js +85 -0
  101. package/dist/config/env.d.ts +6 -0
  102. package/dist/config/env.js +73 -0
  103. package/dist/config/fields.d.ts +42 -0
  104. package/dist/config/fields.js +214 -0
  105. package/dist/config/file-source.d.ts +3 -0
  106. package/dist/config/file-source.js +31 -0
  107. package/dist/config/load.d.ts +20 -0
  108. package/dist/config/load.js +98 -0
  109. package/dist/config/nacos-source.d.ts +31 -0
  110. package/dist/config/nacos-source.js +229 -0
  111. package/dist/config/schema.d.ts +237 -0
  112. package/dist/config/schema.js +1192 -0
  113. package/dist/config/sources.d.ts +18 -0
  114. package/dist/config/sources.js +59 -0
  115. package/dist/config/types.d.ts +9 -0
  116. package/dist/config/types.js +7 -0
  117. package/dist/conversations/artifacts.d.ts +52 -0
  118. package/dist/conversations/artifacts.js +137 -0
  119. package/dist/conversations/attachments.controller.d.ts +28 -0
  120. package/dist/conversations/attachments.controller.js +96 -0
  121. package/dist/conversations/attachments.service.d.ts +180 -0
  122. package/dist/conversations/attachments.service.js +578 -0
  123. package/dist/conversations/conversations.controller.d.ts +288 -0
  124. package/dist/conversations/conversations.controller.js +607 -0
  125. package/dist/conversations/conversations.service.d.ts +321 -0
  126. package/dist/conversations/conversations.service.js +754 -0
  127. package/dist/conversations/event-log.d.ts +122 -0
  128. package/dist/conversations/event-log.js +259 -0
  129. package/dist/conversations/events.d.ts +856 -0
  130. package/dist/conversations/events.js +639 -0
  131. package/dist/conversations/files.d.ts +92 -0
  132. package/dist/conversations/files.js +213 -0
  133. package/dist/conversations/outputs-read.d.ts +34 -0
  134. package/dist/conversations/outputs-read.js +37 -0
  135. package/dist/conversations/queue.d.ts +169 -0
  136. package/dist/conversations/queue.js +527 -0
  137. package/dist/conversations/sandbox-status.d.ts +21 -0
  138. package/dist/conversations/sandbox-status.js +24 -0
  139. package/dist/conversations/selection.d.ts +46 -0
  140. package/dist/conversations/selection.js +75 -0
  141. package/dist/conversations/titles.d.ts +93 -0
  142. package/dist/conversations/titles.js +423 -0
  143. package/dist/ctx/bus.d.ts +16 -0
  144. package/dist/ctx/bus.js +101 -0
  145. package/dist/ctx/create-ctx.d.ts +109 -0
  146. package/dist/ctx/create-ctx.js +592 -0
  147. package/dist/ctx/kv.d.ts +4 -0
  148. package/dist/ctx/kv.js +31 -0
  149. package/dist/ctx/lock.d.ts +4 -0
  150. package/dist/ctx/lock.js +39 -0
  151. package/dist/ctx/namespace.d.ts +6 -0
  152. package/dist/ctx/namespace.js +12 -0
  153. package/dist/ctx/queue.d.ts +25 -0
  154. package/dist/ctx/queue.js +151 -0
  155. package/dist/ctx/secrets.d.ts +45 -0
  156. package/dist/ctx/secrets.js +175 -0
  157. package/dist/db/kernel-schema.d.ts +1122 -0
  158. package/dist/db/kernel-schema.js +5 -0
  159. package/dist/db/migrator.d.ts +28 -0
  160. package/dist/db/migrator.js +140 -0
  161. package/dist/db/pool.d.ts +21 -0
  162. package/dist/db/pool.js +28 -0
  163. package/dist/db/sql-statements.d.ts +34 -0
  164. package/dist/db/sql-statements.js +168 -0
  165. package/dist/errors.d.ts +211 -0
  166. package/dist/errors.js +213 -0
  167. package/dist/evals/budget.d.ts +61 -0
  168. package/dist/evals/budget.js +181 -0
  169. package/dist/evals/diff.d.ts +55 -0
  170. package/dist/evals/diff.js +133 -0
  171. package/dist/evals/judge.d.ts +53 -0
  172. package/dist/evals/judge.js +149 -0
  173. package/dist/evals/plan.d.ts +47 -0
  174. package/dist/evals/plan.js +77 -0
  175. package/dist/evals/reconstruct.d.ts +137 -0
  176. package/dist/evals/reconstruct.js +322 -0
  177. package/dist/evals/recorded-tools.d.ts +54 -0
  178. package/dist/evals/recorded-tools.js +167 -0
  179. package/dist/evals/run-replay.d.ts +86 -0
  180. package/dist/evals/run-replay.js +502 -0
  181. package/dist/evals/runs.controller.d.ts +100 -0
  182. package/dist/evals/runs.controller.js +199 -0
  183. package/dist/evals/runs.service.d.ts +275 -0
  184. package/dist/evals/runs.service.js +836 -0
  185. package/dist/evals/sets.controller.d.ts +61 -0
  186. package/dist/evals/sets.controller.js +183 -0
  187. package/dist/evals/sets.service.d.ts +128 -0
  188. package/dist/evals/sets.service.js +558 -0
  189. package/dist/evals/snapshot.d.ts +76 -0
  190. package/dist/evals/snapshot.js +207 -0
  191. package/dist/evals/step-replay.d.ts +113 -0
  192. package/dist/evals/step-replay.js +181 -0
  193. package/dist/evals/trajectory.controller.d.ts +41 -0
  194. package/dist/evals/trajectory.controller.js +88 -0
  195. package/dist/evals/trajectory.d.ts +144 -0
  196. package/dist/evals/trajectory.js +338 -0
  197. package/dist/evals/trajectory.service.d.ts +108 -0
  198. package/dist/evals/trajectory.service.js +323 -0
  199. package/dist/evals/worker.d.ts +101 -0
  200. package/dist/evals/worker.js +790 -0
  201. package/dist/extensions/admin-ops.d.ts +105 -0
  202. package/dist/extensions/admin-ops.js +353 -0
  203. package/dist/extensions/admin-view.d.ts +79 -0
  204. package/dist/extensions/admin-view.js +327 -0
  205. package/dist/extensions/approvals.d.ts +119 -0
  206. package/dist/extensions/approvals.js +205 -0
  207. package/dist/extensions/blobs.d.ts +45 -0
  208. package/dist/extensions/blobs.js +160 -0
  209. package/dist/extensions/bootstrap.d.ts +176 -0
  210. package/dist/extensions/bootstrap.js +535 -0
  211. package/dist/extensions/cli.d.ts +61 -0
  212. package/dist/extensions/cli.js +165 -0
  213. package/dist/extensions/config-json-schema.d.ts +9 -0
  214. package/dist/extensions/config-json-schema.js +73 -0
  215. package/dist/extensions/deployment-config.d.ts +138 -0
  216. package/dist/extensions/deployment-config.js +394 -0
  217. package/dist/extensions/enabled.d.ts +31 -0
  218. package/dist/extensions/enabled.js +55 -0
  219. package/dist/extensions/hooks.d.ts +161 -0
  220. package/dist/extensions/hooks.js +425 -0
  221. package/dist/extensions/host.d.ts +577 -0
  222. package/dist/extensions/host.js +2071 -0
  223. package/dist/extensions/inflight.d.ts +69 -0
  224. package/dist/extensions/inflight.js +277 -0
  225. package/dist/extensions/installs.d.ts +166 -0
  226. package/dist/extensions/installs.js +253 -0
  227. package/dist/extensions/instance.d.ts +167 -0
  228. package/dist/extensions/instance.js +329 -0
  229. package/dist/extensions/ledger.d.ts +30 -0
  230. package/dist/extensions/ledger.js +50 -0
  231. package/dist/extensions/lifecycle.d.ts +80 -0
  232. package/dist/extensions/lifecycle.js +152 -0
  233. package/dist/extensions/migrate-lock.d.ts +51 -0
  234. package/dist/extensions/migrate-lock.js +100 -0
  235. package/dist/extensions/org-config.controller.d.ts +24 -0
  236. package/dist/extensions/org-config.controller.js +59 -0
  237. package/dist/extensions/org-config.d.ts +169 -0
  238. package/dist/extensions/org-config.js +520 -0
  239. package/dist/extensions/package.d.ts +115 -0
  240. package/dist/extensions/package.js +246 -0
  241. package/dist/extensions/permission-set.d.ts +117 -0
  242. package/dist/extensions/permission-set.js +184 -0
  243. package/dist/extensions/permission-view.d.ts +194 -0
  244. package/dist/extensions/permission-view.js +284 -0
  245. package/dist/extensions/registry.d.ts +24 -0
  246. package/dist/extensions/registry.js +56 -0
  247. package/dist/extensions/resolve.d.ts +12 -0
  248. package/dist/extensions/resolve.js +71 -0
  249. package/dist/extensions/sources.d.ts +69 -0
  250. package/dist/extensions/sources.js +120 -0
  251. package/dist/extensions/sync.d.ts +40 -0
  252. package/dist/extensions/sync.js +242 -0
  253. package/dist/files/previewers.controller.d.ts +46 -0
  254. package/dist/files/previewers.controller.js +105 -0
  255. package/dist/files/previewers.d.ts +100 -0
  256. package/dist/files/previewers.js +250 -0
  257. package/dist/http/api.module.d.ts +114 -0
  258. package/dist/http/api.module.js +448 -0
  259. package/dist/http/app.d.ts +77 -0
  260. package/dist/http/app.js +220 -0
  261. package/dist/http/error.filter.d.ts +21 -0
  262. package/dist/http/error.filter.js +85 -0
  263. package/dist/http/gateway.module.d.ts +15 -0
  264. package/dist/http/gateway.module.js +13 -0
  265. package/dist/http/health.controller.d.ts +26 -0
  266. package/dist/http/health.controller.js +79 -0
  267. package/dist/http/nest-logger.d.ts +20 -0
  268. package/dist/http/nest-logger.js +28 -0
  269. package/dist/http/plugin-dispatch.d.ts +68 -0
  270. package/dist/http/plugin-dispatch.js +200 -0
  271. package/dist/http/tokens.d.ts +131 -0
  272. package/dist/http/tokens.js +131 -0
  273. package/dist/http/zod.pipe.d.ts +8 -0
  274. package/dist/http/zod.pipe.js +14 -0
  275. package/dist/identity/accounts.d.ts +93 -0
  276. package/dist/identity/accounts.js +367 -0
  277. package/dist/identity/api-tokens.d.ts +73 -0
  278. package/dist/identity/api-tokens.js +197 -0
  279. package/dist/identity/audit.d.ts +23 -0
  280. package/dist/identity/audit.js +41 -0
  281. package/dist/identity/auth.controller.d.ts +40 -0
  282. package/dist/identity/auth.controller.js +130 -0
  283. package/dist/identity/auth.guard.d.ts +41 -0
  284. package/dist/identity/auth.guard.js +162 -0
  285. package/dist/identity/binding.d.ts +47 -0
  286. package/dist/identity/binding.js +84 -0
  287. package/dist/identity/cookies.d.ts +16 -0
  288. package/dist/identity/cookies.js +32 -0
  289. package/dist/identity/credential-check.d.ts +17 -0
  290. package/dist/identity/credential-check.js +24 -0
  291. package/dist/identity/enterprise-admin.d.ts +14 -0
  292. package/dist/identity/enterprise-admin.js +27 -0
  293. package/dist/identity/external.d.ts +106 -0
  294. package/dist/identity/external.js +337 -0
  295. package/dist/identity/hash.d.ts +2 -0
  296. package/dist/identity/hash.js +3 -0
  297. package/dist/identity/login.d.ts +6 -0
  298. package/dist/identity/login.js +20 -0
  299. package/dist/identity/me.controller.d.ts +61 -0
  300. package/dist/identity/me.controller.js +159 -0
  301. package/dist/identity/passwords.d.ts +6 -0
  302. package/dist/identity/passwords.js +37 -0
  303. package/dist/identity/principal.d.ts +56 -0
  304. package/dist/identity/principal.js +38 -0
  305. package/dist/identity/rate-limit.d.ts +55 -0
  306. package/dist/identity/rate-limit.js +121 -0
  307. package/dist/identity/sessions.d.ts +84 -0
  308. package/dist/identity/sessions.js +306 -0
  309. package/dist/identity/settings.d.ts +121 -0
  310. package/dist/identity/settings.js +248 -0
  311. package/dist/identity/sso.controller.d.ts +59 -0
  312. package/dist/identity/sso.controller.js +192 -0
  313. package/dist/identity/sso.d.ts +83 -0
  314. package/dist/identity/sso.js +202 -0
  315. package/dist/inbox/inbox.service.d.ts +43 -0
  316. package/dist/inbox/inbox.service.js +57 -0
  317. package/dist/index.d.ts +69 -0
  318. package/dist/index.js +67 -0
  319. package/dist/kernel.d.ts +259 -0
  320. package/dist/kernel.js +1326 -0
  321. package/dist/log-config.d.ts +9 -0
  322. package/dist/log-config.js +27 -0
  323. package/dist/log-context.d.ts +32 -0
  324. package/dist/log-context.js +40 -0
  325. package/dist/log-loki.d.ts +34 -0
  326. package/dist/log-loki.js +145 -0
  327. package/dist/log.d.ts +79 -0
  328. package/dist/log.js +303 -0
  329. package/dist/models/prices.d.ts +53 -0
  330. package/dist/models/prices.js +121 -0
  331. package/dist/models/purposes.controller.d.ts +19 -0
  332. package/dist/models/purposes.controller.js +56 -0
  333. package/dist/models/purposes.d.ts +140 -0
  334. package/dist/models/purposes.js +267 -0
  335. package/dist/net/proxy.d.ts +44 -0
  336. package/dist/net/proxy.js +162 -0
  337. package/dist/net/public-address.d.ts +60 -0
  338. package/dist/net/public-address.js +272 -0
  339. package/dist/net/safe-fetch.d.ts +38 -0
  340. package/dist/net/safe-fetch.js +140 -0
  341. package/dist/orgs/directory-sync.controller.d.ts +14 -0
  342. package/dist/orgs/directory-sync.controller.js +40 -0
  343. package/dist/orgs/directory-sync.d.ts +166 -0
  344. package/dist/orgs/directory-sync.js +558 -0
  345. package/dist/orgs/invitations.controller.d.ts +58 -0
  346. package/dist/orgs/invitations.controller.js +137 -0
  347. package/dist/orgs/invitations.service.d.ts +80 -0
  348. package/dist/orgs/invitations.service.js +217 -0
  349. package/dist/orgs/members.service.d.ts +63 -0
  350. package/dist/orgs/members.service.js +234 -0
  351. package/dist/orgs/offboarding.d.ts +141 -0
  352. package/dist/orgs/offboarding.js +398 -0
  353. package/dist/orgs/org-lock.d.ts +32 -0
  354. package/dist/orgs/org-lock.js +46 -0
  355. package/dist/orgs/org.guard.d.ts +35 -0
  356. package/dist/orgs/org.guard.js +95 -0
  357. package/dist/orgs/orgs.controller.d.ts +78 -0
  358. package/dist/orgs/orgs.controller.js +192 -0
  359. package/dist/orgs/orgs.service.d.ts +61 -0
  360. package/dist/orgs/orgs.service.js +209 -0
  361. package/dist/orgs/roles.d.ts +13 -0
  362. package/dist/orgs/roles.js +58 -0
  363. package/dist/orgs/settings.controller.d.ts +20 -0
  364. package/dist/orgs/settings.controller.js +53 -0
  365. package/dist/orgs/settings.service.d.ts +120 -0
  366. package/dist/orgs/settings.service.js +178 -0
  367. package/dist/packages/npm.d.ts +50 -0
  368. package/dist/packages/npm.js +218 -0
  369. package/dist/packages/provenance.d.ts +47 -0
  370. package/dist/packages/provenance.js +147 -0
  371. package/dist/packages/tgz.d.ts +76 -0
  372. package/dist/packages/tgz.js +225 -0
  373. package/dist/runtime/abort.d.ts +32 -0
  374. package/dist/runtime/abort.js +71 -0
  375. package/dist/runtime/checkpointer.d.ts +32 -0
  376. package/dist/runtime/checkpointer.js +243 -0
  377. package/dist/runtime/context/build.d.ts +254 -0
  378. package/dist/runtime/context/build.js +693 -0
  379. package/dist/runtime/context/claude-tools.d.ts +11 -0
  380. package/dist/runtime/context/claude-tools.js +24 -0
  381. package/dist/runtime/context/compaction.d.ts +26 -0
  382. package/dist/runtime/context/compaction.js +195 -0
  383. package/dist/runtime/context/pairing.d.ts +11 -0
  384. package/dist/runtime/context/pairing.js +82 -0
  385. package/dist/runtime/context/reminders.d.ts +5 -0
  386. package/dist/runtime/context/reminders.js +11 -0
  387. package/dist/runtime/context/skills.d.ts +105 -0
  388. package/dist/runtime/context/skills.js +283 -0
  389. package/dist/runtime/context/tokens.d.ts +19 -0
  390. package/dist/runtime/context/tokens.js +104 -0
  391. package/dist/runtime/conversation-lock.d.ts +40 -0
  392. package/dist/runtime/conversation-lock.js +97 -0
  393. package/dist/runtime/hook-calls.d.ts +84 -0
  394. package/dist/runtime/hook-calls.js +136 -0
  395. package/dist/runtime/langgraph-loop.d.ts +52 -0
  396. package/dist/runtime/langgraph-loop.js +247 -0
  397. package/dist/runtime/loop.d.ts +93 -0
  398. package/dist/runtime/loop.js +7 -0
  399. package/dist/runtime/maintenance.d.ts +80 -0
  400. package/dist/runtime/maintenance.js +148 -0
  401. package/dist/runtime/media-cache.d.ts +21 -0
  402. package/dist/runtime/media-cache.js +51 -0
  403. package/dist/runtime/model-call.d.ts +176 -0
  404. package/dist/runtime/model-call.js +1049 -0
  405. package/dist/runtime/providers.d.ts +86 -0
  406. package/dist/runtime/providers.js +224 -0
  407. package/dist/runtime/quota.d.ts +29 -0
  408. package/dist/runtime/quota.js +70 -0
  409. package/dist/runtime/run-finish.d.ts +45 -0
  410. package/dist/runtime/run-finish.js +69 -0
  411. package/dist/runtime/run-queue.d.ts +121 -0
  412. package/dist/runtime/run-queue.js +493 -0
  413. package/dist/runtime/run-worker.d.ts +138 -0
  414. package/dist/runtime/run-worker.js +806 -0
  415. package/dist/runtime/stream.controller.d.ts +34 -0
  416. package/dist/runtime/stream.controller.js +122 -0
  417. package/dist/runtime/stream.d.ts +63 -0
  418. package/dist/runtime/stream.js +262 -0
  419. package/dist/runtime/subagents/child.d.ts +80 -0
  420. package/dist/runtime/subagents/child.js +225 -0
  421. package/dist/runtime/subagents/definition.d.ts +103 -0
  422. package/dist/runtime/subagents/definition.js +392 -0
  423. package/dist/runtime/subagents/dispatch.d.ts +69 -0
  424. package/dist/runtime/subagents/dispatch.js +502 -0
  425. package/dist/runtime/subagents/limits.d.ts +23 -0
  426. package/dist/runtime/subagents/limits.js +34 -0
  427. package/dist/runtime/subagents/org-agents.d.ts +50 -0
  428. package/dist/runtime/subagents/org-agents.js +70 -0
  429. package/dist/runtime/subagents/preamble.d.ts +7 -0
  430. package/dist/runtime/subagents/preamble.js +11 -0
  431. package/dist/runtime/subagents/progress.d.ts +26 -0
  432. package/dist/runtime/subagents/progress.js +36 -0
  433. package/dist/runtime/subagents/roster.d.ts +55 -0
  434. package/dist/runtime/subagents/roster.js +111 -0
  435. package/dist/runtime/subagents/skill-fork.d.ts +123 -0
  436. package/dist/runtime/subagents/skill-fork.js +259 -0
  437. package/dist/runtime/usage-report.d.ts +163 -0
  438. package/dist/runtime/usage-report.js +551 -0
  439. package/dist/runtime/waits.d.ts +233 -0
  440. package/dist/runtime/waits.js +883 -0
  441. package/dist/sandbox/leases.d.ts +169 -0
  442. package/dist/sandbox/leases.js +515 -0
  443. package/dist/sandbox/lifecycle.d.ts +21 -0
  444. package/dist/sandbox/lifecycle.js +68 -0
  445. package/dist/sandbox/sandbox.service.d.ts +249 -0
  446. package/dist/sandbox/sandbox.service.js +908 -0
  447. package/dist/skills/catalog.d.ts +149 -0
  448. package/dist/skills/catalog.js +405 -0
  449. package/dist/skills/changes.controller.d.ts +56 -0
  450. package/dist/skills/changes.controller.js +136 -0
  451. package/dist/skills/changes.service.d.ts +175 -0
  452. package/dist/skills/changes.service.js +805 -0
  453. package/dist/skills/invocable.controller.d.ts +23 -0
  454. package/dist/skills/invocable.controller.js +50 -0
  455. package/dist/skills/invocation-keys.d.ts +23 -0
  456. package/dist/skills/invocation-keys.js +44 -0
  457. package/dist/skills/invoke.d.ts +135 -0
  458. package/dist/skills/invoke.js +286 -0
  459. package/dist/skills/package.d.ts +87 -0
  460. package/dist/skills/package.js +227 -0
  461. package/dist/skills/skills.controller.d.ts +40 -0
  462. package/dist/skills/skills.controller.js +110 -0
  463. package/dist/skills/skills.service.d.ts +192 -0
  464. package/dist/skills/skills.service.js +567 -0
  465. package/dist/storage/prefix.d.ts +36 -0
  466. package/dist/storage/prefix.js +104 -0
  467. package/dist/storage/purge.d.ts +27 -0
  468. package/dist/storage/purge.js +75 -0
  469. package/dist/storage/storage.service.d.ts +44 -0
  470. package/dist/storage/storage.service.js +145 -0
  471. package/dist/text.d.ts +13 -0
  472. package/dist/text.js +13 -0
  473. package/dist/tools/approvals.controller.d.ts +53 -0
  474. package/dist/tools/approvals.controller.js +128 -0
  475. package/dist/tools/approvals.service.d.ts +172 -0
  476. package/dist/tools/approvals.service.js +568 -0
  477. package/dist/tools/breaker.d.ts +28 -0
  478. package/dist/tools/breaker.js +72 -0
  479. package/dist/tools/builtins/agent.d.ts +81 -0
  480. package/dist/tools/builtins/agent.js +223 -0
  481. package/dist/tools/builtins/ask-user.d.ts +28 -0
  482. package/dist/tools/builtins/ask-user.js +139 -0
  483. package/dist/tools/builtins/read-tool-output.d.ts +18 -0
  484. package/dist/tools/builtins/read-tool-output.js +108 -0
  485. package/dist/tools/builtins/sandbox/bash.d.ts +16 -0
  486. package/dist/tools/builtins/sandbox/bash.js +137 -0
  487. package/dist/tools/builtins/sandbox/common.d.ts +116 -0
  488. package/dist/tools/builtins/sandbox/common.js +306 -0
  489. package/dist/tools/builtins/sandbox/deliver.d.ts +5 -0
  490. package/dist/tools/builtins/sandbox/deliver.js +180 -0
  491. package/dist/tools/builtins/sandbox/edit.d.ts +4 -0
  492. package/dist/tools/builtins/sandbox/edit.js +98 -0
  493. package/dist/tools/builtins/sandbox/glob-match.d.ts +25 -0
  494. package/dist/tools/builtins/sandbox/glob-match.js +240 -0
  495. package/dist/tools/builtins/sandbox/glob.d.ts +4 -0
  496. package/dist/tools/builtins/sandbox/glob.js +104 -0
  497. package/dist/tools/builtins/sandbox/grep.d.ts +9 -0
  498. package/dist/tools/builtins/sandbox/grep.js +133 -0
  499. package/dist/tools/builtins/sandbox/index.d.ts +12 -0
  500. package/dist/tools/builtins/sandbox/index.js +27 -0
  501. package/dist/tools/builtins/sandbox/names.d.ts +7 -0
  502. package/dist/tools/builtins/sandbox/names.js +20 -0
  503. package/dist/tools/builtins/sandbox/read.d.ts +8 -0
  504. package/dist/tools/builtins/sandbox/read.js +121 -0
  505. package/dist/tools/builtins/sandbox/write.d.ts +4 -0
  506. package/dist/tools/builtins/sandbox/write.js +88 -0
  507. package/dist/tools/builtins/skill-load.d.ts +53 -0
  508. package/dist/tools/builtins/skill-load.js +446 -0
  509. package/dist/tools/builtins/skill-write.d.ts +59 -0
  510. package/dist/tools/builtins/skill-write.js +244 -0
  511. package/dist/tools/builtins/tool-search.d.ts +31 -0
  512. package/dist/tools/builtins/tool-search.js +171 -0
  513. package/dist/tools/connector-content.controller.d.ts +34 -0
  514. package/dist/tools/connector-content.controller.js +78 -0
  515. package/dist/tools/connector-content.d.ts +104 -0
  516. package/dist/tools/connector-content.js +267 -0
  517. package/dist/tools/connectors.controller.d.ts +210 -0
  518. package/dist/tools/connectors.controller.js +234 -0
  519. package/dist/tools/connectors.service.d.ts +303 -0
  520. package/dist/tools/connectors.service.js +1102 -0
  521. package/dist/tools/credentials.d.ts +139 -0
  522. package/dist/tools/credentials.js +351 -0
  523. package/dist/tools/drift.d.ts +141 -0
  524. package/dist/tools/drift.js +539 -0
  525. package/dist/tools/elicitation.d.ts +81 -0
  526. package/dist/tools/elicitation.js +266 -0
  527. package/dist/tools/endpoint.d.ts +18 -0
  528. package/dist/tools/endpoint.js +115 -0
  529. package/dist/tools/info.d.ts +67 -0
  530. package/dist/tools/info.js +113 -0
  531. package/dist/tools/naming.d.ts +27 -0
  532. package/dist/tools/naming.js +47 -0
  533. package/dist/tools/oauth/discovery.d.ts +65 -0
  534. package/dist/tools/oauth/discovery.js +307 -0
  535. package/dist/tools/oauth/flow.d.ts +63 -0
  536. package/dist/tools/oauth/flow.js +237 -0
  537. package/dist/tools/oauth/oauth.controller.d.ts +31 -0
  538. package/dist/tools/oauth/oauth.controller.js +95 -0
  539. package/dist/tools/oauth/tokens.d.ts +111 -0
  540. package/dist/tools/oauth/tokens.js +258 -0
  541. package/dist/tools/outputs.d.ts +61 -0
  542. package/dist/tools/outputs.js +156 -0
  543. package/dist/tools/pipeline.d.ts +322 -0
  544. package/dist/tools/pipeline.js +1236 -0
  545. package/dist/tools/policy.d.ts +60 -0
  546. package/dist/tools/policy.js +236 -0
  547. package/dist/tools/questions.controller.d.ts +24 -0
  548. package/dist/tools/questions.controller.js +58 -0
  549. package/dist/tools/questions.service.d.ts +103 -0
  550. package/dist/tools/questions.service.js +295 -0
  551. package/dist/tools/redact.d.ts +20 -0
  552. package/dist/tools/redact.js +60 -0
  553. package/dist/tools/registry.d.ts +246 -0
  554. package/dist/tools/registry.js +481 -0
  555. package/dist/tools/resources.d.ts +16 -0
  556. package/dist/tools/resources.js +76 -0
  557. package/dist/tools/specs.d.ts +14 -0
  558. package/dist/tools/specs.js +31 -0
  559. package/dist/tools/validate.d.ts +20 -0
  560. package/dist/tools/validate.js +183 -0
  561. package/dist/tools/view.d.ts +43 -0
  562. package/dist/tools/view.js +225 -0
  563. package/dist/ui/embed-token.d.ts +31 -0
  564. package/dist/ui/embed-token.js +132 -0
  565. package/dist/ui/registry.d.ts +58 -0
  566. package/dist/ui/registry.js +90 -0
  567. package/dist/ui/ui-stream.d.ts +37 -0
  568. package/dist/ui/ui-stream.js +162 -0
  569. package/dist/ui/views.controller.d.ts +81 -0
  570. package/dist/ui/views.controller.js +261 -0
  571. package/dist/ui/views.d.ts +155 -0
  572. package/dist/ui/views.js +306 -0
  573. package/dist/version.d.ts +5 -0
  574. package/dist/version.js +6 -0
  575. package/package.json +69 -3
  576. package/src/migrations/0001_init.sql +8 -0
  577. package/src/migrations/0002_secrets.sql +8 -0
  578. package/src/migrations/0003_identity.sql +117 -0
  579. package/src/migrations/0004_plugin_org_config_nullable.sql +3 -0
  580. package/src/migrations/0005_agents.sql +27 -0
  581. package/src/migrations/0006_conversations.sql +37 -0
  582. package/src/migrations/0007_checkpoints.sql +26 -0
  583. package/src/migrations/0008_runs_trigger_seq.sql +3 -0
  584. package/src/migrations/0009_runs_active_unique.sql +4 -0
  585. package/src/migrations/0010_tools.sql +125 -0
  586. package/src/migrations/0011_approvals_spec.sql +10 -0
  587. package/src/migrations/0012_run_waits.sql +24 -0
  588. package/src/migrations/0013_app_tools_source.sql +4 -0
  589. package/src/migrations/0014_tool_specs.sql +8 -0
  590. package/src/migrations/0015_tool_drift.sql +20 -0
  591. package/src/migrations/0016_plugin_permissions.sql +11 -0
  592. package/src/migrations/0017_identity_sources.sql +6 -0
  593. package/src/migrations/0018_channels.sql +74 -0
  594. package/src/migrations/0019_identity_trust.sql +17 -0
  595. package/src/migrations/0020_channel_card_lookup.sql +10 -0
  596. package/src/migrations/0021_org_storage_purge.sql +9 -0
  597. package/src/migrations/0022_attachments.sql +29 -0
  598. package/src/migrations/0023_sandboxes.sql +37 -0
  599. package/src/migrations/0024_skills.sql +46 -0
  600. package/src/migrations/0025_skills_catalog_idx.sql +6 -0
  601. package/src/migrations/0026_skill_changes.sql +34 -0
  602. package/src/migrations/0027_model_prices.sql +18 -0
  603. package/src/migrations/0028_evals.sql +105 -0
  604. package/src/migrations/0029_model_purposes.sql +18 -0
  605. package/src/migrations/0030_plugin_installs.sql +73 -0
  606. package/src/migrations/0031_plugin_snapshot_source.sql +6 -0
  607. package/src/migrations/0032_plugin_deployment_configs.sql +10 -0
  608. package/src/migrations/0033_rename_apps_to_connectors.sql +25 -0
  609. package/src/migrations/0034_directory.sql +49 -0
  610. package/src/migrations/0035_catalogs.sql +40 -0
  611. package/src/migrations/0036_app_import_staging.sql +17 -0
  612. package/src/migrations/0037_directory_installs.sql +92 -0
  613. package/src/migrations/0038_channel_inbound_mode.sql +6 -0
  614. package/src/migrations/0039_channel_card_pending.sql +8 -0
  615. package/src/migrations/0040_usage_report_sweep.sql +10 -0
  616. package/src/migrations/0041_rename_plugins_to_extensions.sql +38 -0
  617. package/src/migrations/0042_rename_directory_to_catalog.sql +80 -0
  618. package/src/migrations/0043_rename_app_installs_to_plugin_installs.sql +20 -0
  619. package/src/migrations/0044_catalog_one_kind.sql +90 -0
  620. package/src/migrations/0045_plugin_dependencies.sql +9 -0
  621. package/src/migrations/0046_git_sources.sql +25 -0
  622. package/src/migrations/0047_marketplace_entry_heads.sql +7 -0
  623. package/src/migrations/0048_marketplace_entry_trees.sql +8 -0
  624. package/src/migrations/0049_plugin_upgrades.sql +37 -0
  625. package/src/migrations/0050_bootstrap.sql +53 -0
  626. package/src/migrations/0051_claude_compat.sql +26 -0
  627. package/src/migrations/0052_conversations_account_agent_idx.sql +5 -0
  628. package/src/migrations/0053_subagents.sql +73 -0
  629. package/src/migrations/0054_subagent_wait_expiry.sql +7 -0
  630. package/src/migrations/0055_subagent_parent_indexes.sql +10 -0
  631. package/src/migrations/0056_approval_reason.sql +3 -0
  632. package/src/migrations/0057_conversation_titles.sql +11 -0
  633. package/src/migrations/0058_conversation_selection.sql +4 -0
  634. package/src/migrations/0059_conversation_queue.sql +22 -0
  635. package/src/migrations/0060_file_previewers.sql +10 -0
  636. package/src/migrations/0061_queue_selection.sql +5 -0
  637. package/src/migrations/0062_queued_skill.sql +8 -0
@@ -0,0 +1,1102 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { ToolError, } from "@capybara-hq/plugin-sdk";
3
+ import { sql } from "kysely";
4
+ import { z } from "zod";
5
+ import { assertItemInstallable } from "../catalog/requests.service.js";
6
+ import { ApiError } from "../errors.js";
7
+ import { audit } from "../identity/audit.js";
8
+ import { assertCustomAllowed } from "../identity/settings.js";
9
+ import { lockOrgAndAuthorize } from "../orgs/org-lock.js";
10
+ import { CredentialMissing, isOAuth, } from "./credentials.js";
11
+ import { snapshot, toolIssue, } from "./drift.js";
12
+ import { endpointCredentialIssues, endpointView } from "./endpoint.js";
13
+ import { deriveSlug, isValidSlug, SLUG_RULE, toolKey } from "./naming.js";
14
+ import { checkOAuthUrl, discoverMcp, OAuthSetupError, oauthHttpOptions, pickAuthMethod, registerClient, } from "./oauth/discovery.js";
15
+ import { callbackUrl, publicUrlRequired } from "./oauth/flow.js";
16
+ import { OAuthGrantError, OAuthUnavailable, tokenRequest, } from "./oauth/tokens.js";
17
+ import { policyFromAnnotations } from "./registry.js";
18
+ import { recordToolSpecs } from "./specs.js";
19
+ /** 预览时没给 slug、也推导不出时,拼 key 用的占位 slug。 */
20
+ const PREVIEW_SLUG = "app";
21
+ /** 预览里 inspect 失败的说明里带上的 ToolError 消息最多字符数。 */
22
+ const MAX_INSPECT_DETAIL = 500;
23
+ const notFound = () => new ApiError("connector.not_found", "Connector not found");
24
+ const fail = (path, message) => new ApiError("validation_failed", "Request validation failed", [{ path, message }]);
25
+ const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
26
+ /** OAuth 连接器在安装时可带的客户端(手填 client / 客户端凭据流)。 */
27
+ const OAuthClientCredential = z.strictObject({
28
+ clientId: z.string().min(1).max(500),
29
+ clientSecret: z.string().min(1).max(4000).optional(),
30
+ });
31
+ /** 发现 / 注册 / 取 token 的失败 → 502 `connector.connect_failed`,details 带原因码(向导据 `dcr_unsupported` 改为手填 client)。 */
32
+ function connectFailed(e) {
33
+ if (e instanceof OAuthSetupError) {
34
+ return new ApiError("connector.connect_failed", e.message, [
35
+ { reason: e.reason, message: e.message },
36
+ ]);
37
+ }
38
+ if (e instanceof OAuthGrantError) {
39
+ const message = `The authorization server rejected the client credentials (${e.error})`;
40
+ return new ApiError("connector.connect_failed", message, [{ reason: "token_failed", message }]);
41
+ }
42
+ const message = e instanceof OAuthUnavailable ? e.message : "Could not complete OAuth setup with the connector";
43
+ return new ApiError("connector.connect_failed", message, [{ reason: "token_failed", message }]);
44
+ }
45
+ /**
46
+ * 解析请求里的 OAuth 客户端 `{ clientId, clientSecret? }`(安装时的 `credential`、轮换时的组织凭据);
47
+ * 错误只带路径与英文说明,不回显值。
48
+ */
49
+ function parseOAuthClient(credential, requireSecret, prefix = "credential") {
50
+ if (credential === undefined || credential === null) {
51
+ throw fail(prefix, "Provide the OAuth client registered with the authorization server ({ clientId, clientSecret })");
52
+ }
53
+ const r = OAuthClientCredential.safeParse(credential);
54
+ if (!r.success) {
55
+ throw new ApiError("validation_failed", "Invalid credential", r.error.issues.map((i) => ({
56
+ path: [prefix, ...i.path.map(String)].filter(Boolean).join("."),
57
+ message: i.message,
58
+ })));
59
+ }
60
+ if (requireSecret && r.data.clientSecret === undefined) {
61
+ throw fail(prefix ? `${prefix}.clientSecret` : "clientSecret", "A client secret is required for the client credentials flow");
62
+ }
63
+ return r.data;
64
+ }
65
+ /** 无注解 = 既没有 readOnlyHint 也没有 destructiveHint。 */
66
+ const unannotated = (a) => a?.readOnlyHint === undefined && a?.destructiveHint === undefined;
67
+ /**
68
+ * 连接器(远端服务实例)与其工具快照、凭据。写操作都在组织行锁内复核发起人权限并写审计
69
+ * (detail 只有凭据类型与归属,没有值)。连接器会话按操作打开、用完即关,不在进程内缓存。
70
+ */
71
+ export class ConnectorsService {
72
+ db;
73
+ registry;
74
+ host;
75
+ credentials;
76
+ config;
77
+ drift;
78
+ constructor(db, registry, host, credentials, config, drift) {
79
+ this.db = db;
80
+ this.registry = registry;
81
+ this.host = host;
82
+ this.credentials = credentials;
83
+ this.config = config;
84
+ this.drift = drift;
85
+ }
86
+ /**
87
+ * 校验连接器存在、端点按其 schema 合法、不含凭据样式的头 / query 参数 / URL 用户信息(端点对成员可见,
88
+ * 凭据须走认证设置);返回解析后的端点。
89
+ */
90
+ parseEndpoint(connector, endpoint) {
91
+ const c = this.registry.findConnector(connector);
92
+ if (!c) {
93
+ const kinds = this.registry.connectorKinds();
94
+ throw fail("connector", `Unknown connector "${connector}"; installed connectors: ${kinds.length > 0 ? kinds.join(", ") : "none"}`);
95
+ }
96
+ const r = c.endpointSchema.safeParse(endpoint);
97
+ if (!r.success) {
98
+ throw new ApiError("validation_failed", "Request validation failed", r.error.issues.map((i) => ({
99
+ path: ["endpoint", ...i.path.map(String)].join("."),
100
+ message: i.message,
101
+ })));
102
+ }
103
+ const leaks = endpointCredentialIssues(r.data);
104
+ if (leaks.length > 0)
105
+ throw new ApiError("validation_failed", "Request validation failed", leaks);
106
+ return r.data;
107
+ }
108
+ async slugTaken(db, orgId, slug) {
109
+ const row = await db
110
+ .selectFrom("connectors")
111
+ .select("id")
112
+ .where("org_id", "=", orgId)
113
+ .where("slug", "=", slug)
114
+ .where("deleted_at", "is", null)
115
+ .executeTakeFirst();
116
+ return row !== undefined;
117
+ }
118
+ resolveSlug(input) {
119
+ if (input.slug !== undefined) {
120
+ if (!isValidSlug(input.slug))
121
+ throw fail("slug", SLUG_RULE);
122
+ return input.slug;
123
+ }
124
+ const derived = deriveSlug(input.name);
125
+ if (!derived) {
126
+ throw fail("slug", `Cannot derive a slug from the name; provide one. ${SLUG_RULE}`);
127
+ }
128
+ return derived;
129
+ }
130
+ /**
131
+ * OAuth 连接器安装前的准备(A16):授权码流按 `discovery` 取端点(MCP 发现或手填)、按 `client` 动态注册或用请求里
132
+ * 手填的 client(不做首次 listTools);客户端凭据流用请求里的 client 取一次组织 token。
133
+ * 外连失败一律 502 `connector.connect_failed`(details 带原因码),什么都不写。
134
+ */
135
+ async prepareOAuth(auth, endpoint, credential, publicUrl) {
136
+ const http = oauthHttpOptions(this.config());
137
+ const parseClient = (requireSecret) => parseOAuthClient(credential, requireSecret);
138
+ const checked = (path, url) => {
139
+ try {
140
+ checkOAuthUrl(url, http);
141
+ }
142
+ catch (e) {
143
+ throw fail(path, e instanceof Error ? e.message : "Invalid URL");
144
+ }
145
+ return url;
146
+ };
147
+ if (auth.type === "oauth_client") {
148
+ const manual = parseClient(true);
149
+ const client = {
150
+ clientId: manual.clientId,
151
+ ...(manual.clientSecret !== undefined && { clientSecret: manual.clientSecret }),
152
+ authMethod: auth.tokenAuthMethod ?? "client_secret_basic",
153
+ tokenEndpoint: checked("auth.tokenUrl", auth.tokenUrl),
154
+ ...(auth.scopes && { scopes: auth.scopes }),
155
+ };
156
+ try {
157
+ const tokens = await tokenRequest(client, { grant_type: "client_credentials", scope: auth.scopes?.join(" ") }, http);
158
+ return { client, tokens };
159
+ }
160
+ catch (e) {
161
+ throw connectFailed(e);
162
+ }
163
+ }
164
+ const manual = auth.client === "manual" ? parseClient(false) : undefined;
165
+ if (!manual && credential !== undefined && credential !== null) {
166
+ throw fail("credential", "With client dcr the client is registered automatically; omit credential or use client manual");
167
+ }
168
+ let meta;
169
+ let endpoints;
170
+ if (auth.discovery === "mcp") {
171
+ const url = isObj(endpoint) && typeof endpoint.url === "string" ? endpoint.url : undefined;
172
+ if (url === undefined)
173
+ throw fail("endpoint.url", "MCP OAuth discovery needs an endpoint URL");
174
+ try {
175
+ meta = await discoverMcp(url, http);
176
+ }
177
+ catch (e) {
178
+ throw connectFailed(e);
179
+ }
180
+ endpoints = {
181
+ authorizationEndpoint: meta.authorizationEndpoint,
182
+ tokenEndpoint: meta.tokenEndpoint,
183
+ resource: meta.resource,
184
+ ...(meta.scopes && { scopes: meta.scopes }),
185
+ };
186
+ }
187
+ else {
188
+ endpoints = {
189
+ authorizationEndpoint: checked("auth.authorizationUrl", auth.authorizationUrl ?? ""),
190
+ tokenEndpoint: checked("auth.tokenUrl", auth.tokenUrl ?? ""),
191
+ };
192
+ }
193
+ const scopes = auth.scopes ?? endpoints.scopes;
194
+ if (manual) {
195
+ return {
196
+ client: {
197
+ clientId: manual.clientId,
198
+ ...(manual.clientSecret !== undefined && { clientSecret: manual.clientSecret }),
199
+ authMethod: manual.clientSecret === undefined
200
+ ? "none"
201
+ : (auth.tokenAuthMethod ?? pickAuthMethod(meta?.authMethods, true)),
202
+ authorizationEndpoint: endpoints.authorizationEndpoint,
203
+ tokenEndpoint: endpoints.tokenEndpoint,
204
+ ...(endpoints.resource !== undefined && { resource: endpoints.resource }),
205
+ ...(meta && { issuer: meta.issuer }),
206
+ ...(scopes && { scopes }),
207
+ },
208
+ };
209
+ }
210
+ if (!meta)
211
+ throw fail("auth.client", "Dynamic client registration requires discovery mcp");
212
+ try {
213
+ const client = await registerClient({ ...meta, ...(scopes && { scopes }) }, callbackUrl(publicUrl), http, auth.tokenAuthMethod);
214
+ return { client };
215
+ }
216
+ catch (e) {
217
+ throw connectFailed(e);
218
+ }
219
+ }
220
+ /**
221
+ * The checks of {@link create} that need neither a connection nor the database: the slug, the
222
+ * endpoint against the connector's schema, the credential shape, the OAuth client and the OAuth
223
+ * URL policy. `create` runs them first; a plugin install runs them for every template before it
224
+ * creates anything, so bad input is a 400 rather than a failed install. Returns what they parsed.
225
+ */
226
+ checkCreateInput(actorAccountId, input) {
227
+ const slug = this.resolveSlug(input);
228
+ const endpoint = this.parseEndpoint(input.connector, input.endpoint);
229
+ if (this.host.manifests().some((m) => m.name === slug)) {
230
+ throw fail("slug", `Slug "${slug}" is the name of an installed plugin; choose another`);
231
+ }
232
+ const auth = input.auth;
233
+ const credential = input.credential ?? undefined;
234
+ if (!isOAuth(auth)) {
235
+ if (credential === undefined)
236
+ return { slug, endpoint };
237
+ if (auth.type === "none")
238
+ throw fail("credential", "Auth type none takes no credential");
239
+ const owner = auth.ownership === "org" ? { kind: "org" } : { kind: "user", accountId: actorAccountId };
240
+ this.credentials.validate(auth, owner, credential);
241
+ return { slug, endpoint, owner, secret: credential };
242
+ }
243
+ if (!this.config().http.publicUrl)
244
+ throw publicUrlRequired();
245
+ const http = oauthHttpOptions(this.config());
246
+ const checked = (path, url) => {
247
+ try {
248
+ checkOAuthUrl(url, http);
249
+ }
250
+ catch (e) {
251
+ throw fail(path, e instanceof Error ? e.message : "Invalid URL");
252
+ }
253
+ };
254
+ if (auth.type === "oauth_client") {
255
+ parseOAuthClient(credential, true);
256
+ checked("auth.tokenUrl", auth.tokenUrl);
257
+ return { slug, endpoint };
258
+ }
259
+ if (auth.client === "manual")
260
+ parseOAuthClient(credential, false);
261
+ else if (credential !== undefined) {
262
+ throw fail("credential", "With client dcr the client is registered automatically; omit credential or use client manual");
263
+ }
264
+ if (auth.discovery === "mcp") {
265
+ if (!isObj(endpoint) || typeof endpoint.url !== "string") {
266
+ throw fail("endpoint.url", "MCP OAuth discovery needs an endpoint URL");
267
+ }
268
+ return { slug, endpoint };
269
+ }
270
+ checked("auth.authorizationUrl", auth.authorizationUrl ?? "");
271
+ checked("auth.tokenUrl", auth.tokenUrl ?? "");
272
+ if (auth.client !== "manual") {
273
+ throw fail("auth.client", "Dynamic client registration requires discovery mcp");
274
+ }
275
+ return { slug, endpoint };
276
+ }
277
+ /**
278
+ * 安装连接器(A4):校验 → 用请求里的凭据(尚未落库)连接并 listTools → 一个事务内写连接器、工具快照、
279
+ * 凭据与审计。连接失败时什么都不写。组织凭据型连接器的 `credential` 是组织凭据,个人型是创建者自己的。
280
+ * OAuth(A16,要求 `http.publicUrl`):授权码连接器不做首次 listTools(首个成员连接后刷新),只存客户端;
281
+ * 客户端凭据连接器用安装时取到的组织 token listTools,存客户端与 token。
282
+ */
283
+ async create(orgId, actorAccountId, input, actorTokenId) {
284
+ // 自建被禁止时先拒绝(事务里再复核一次):不该为此触发外连
285
+ if (!input.template)
286
+ await assertCustomAllowed(this.db, "customConnectors");
287
+ const { slug, endpoint, owner, secret } = this.checkCreateInput(actorAccountId, input);
288
+ const id = randomUUID();
289
+ const auth = input.auth;
290
+ const publicUrl = this.config().http.publicUrl;
291
+ // 先做不需要连接的检查:冲突的 slug 不该触发一次外连
292
+ if (await this.slugTaken(this.db, orgId, slug)) {
293
+ throw new ApiError("connector.slug_taken", "Connector slug is already taken");
294
+ }
295
+ const oauth = isOAuth(auth)
296
+ ? await this.prepareOAuth(auth, endpoint, input.credential, publicUrl)
297
+ : undefined;
298
+ let rows = [];
299
+ let issues = [];
300
+ if (auth.type !== "oauth_code") {
301
+ const listed = await this.drift.listTools(input.connector, endpoint, async () => {
302
+ if (auth.type === "none")
303
+ return { headers: {} };
304
+ if (oauth?.tokens) {
305
+ return this.credentials.injectionFor(id, auth, { token: oauth.tokens.access_token });
306
+ }
307
+ if (!secret)
308
+ throw new CredentialMissing(id, auth.ownership);
309
+ return this.credentials.injectionFor(id, auth, secret);
310
+ }, { orgId, slug });
311
+ ({ rows, issues } = snapshot(slug, listed.specs, listed.resources));
312
+ }
313
+ return this.db.transaction().execute(async (t) => {
314
+ await lockOrgAndAuthorize(t, orgId, actorAccountId, "connectors.manage");
315
+ if (input.template) {
316
+ await assertItemInstallable(t, orgId, input.template.itemId, input.template.approvedBy ?? input.template.itemId);
317
+ }
318
+ else
319
+ await assertCustomAllowed(t, "customConnectors");
320
+ if (await this.slugTaken(t, orgId, slug)) {
321
+ throw new ApiError("connector.slug_taken", "Connector slug is already taken");
322
+ }
323
+ await t
324
+ .insertInto("connectors")
325
+ .values({
326
+ id,
327
+ org_id: orgId,
328
+ slug,
329
+ name: input.name,
330
+ connector: input.connector,
331
+ endpoint: JSON.stringify(endpoint),
332
+ auth: JSON.stringify(auth),
333
+ created_by: actorAccountId,
334
+ catalog_item_id: input.template?.itemId ?? null,
335
+ catalog_version: input.template?.version ?? null,
336
+ // 授权码连接器安装时没有快照:模板的默认策略留到首个快照时应用
337
+ template_tool_policies: input.template?.defaultToolPolicies
338
+ ? JSON.stringify(input.template.defaultToolPolicies)
339
+ : null,
340
+ // 授权码连接器安装时不拍快照(A16):留空,首个快照按安装处理
341
+ ...(auth.type !== "oauth_code" && { tools_synced_at: sql `now()` }),
342
+ })
343
+ .execute();
344
+ await this.insertTools(t, id, rows, input.template?.defaultToolPolicies);
345
+ if (secret && owner)
346
+ await this.credentials.setStatic(orgId, id, auth, owner, secret, t);
347
+ if (oauth) {
348
+ await this.credentials.setOAuthClient(orgId, id, oauth.client, t);
349
+ if (oauth.tokens) {
350
+ await this.credentials.setOAuthTokens(orgId, id, { kind: "org" }, oauth.tokens, t);
351
+ }
352
+ }
353
+ await audit(t, {
354
+ actorAccountId,
355
+ ...(actorTokenId !== undefined && { actorTokenId }),
356
+ orgId,
357
+ action: "connector.created",
358
+ target: id,
359
+ detail: {
360
+ slug,
361
+ connector: input.connector,
362
+ auth: { type: auth.type, ...(auth.type !== "none" && { ownership: auth.ownership }) },
363
+ tools: rows.length,
364
+ credentialProvided: secret !== undefined || oauth !== undefined,
365
+ ...(input.template && {
366
+ catalog: {
367
+ itemId: input.template.itemId,
368
+ version: input.template.version,
369
+ ...(input.template.templateId !== undefined && {
370
+ templateId: input.template.templateId,
371
+ }),
372
+ },
373
+ }),
374
+ },
375
+ });
376
+ return { connector: await this.detail(t, orgId, id, actorAccountId), issues };
377
+ });
378
+ }
379
+ /**
380
+ * Checks a template update before anything is written (an org plugin upgrade): the endpoint
381
+ * against the connector's schema, and with `resetCredentials` the new credential's shape, or for
382
+ * OAuth the client setup (discovery, dynamic registration or the client credentials token; an
383
+ * outbound failure -> 502 `connector.connect_failed`). Writes nothing.
384
+ */
385
+ async prepareTemplateUpdate(actorAccountId, connector, input) {
386
+ const endpoint = this.parseEndpoint(connector, input.endpoint);
387
+ const out = { input, endpoint };
388
+ if (!input.resetCredentials)
389
+ return out;
390
+ const auth = input.auth;
391
+ const credential = input.credential ?? undefined;
392
+ if (!isOAuth(auth)) {
393
+ if (credential === undefined)
394
+ return out;
395
+ if (auth.type === "none")
396
+ throw fail("credential", "Auth type none takes no credential");
397
+ const owner = auth.ownership === "org" ? { kind: "org" } : { kind: "user", accountId: actorAccountId };
398
+ this.credentials.validate(auth, owner, credential);
399
+ return { ...out, owner, secret: credential };
400
+ }
401
+ const publicUrl = this.config().http.publicUrl;
402
+ if (!publicUrl)
403
+ throw publicUrlRequired();
404
+ return { ...out, oauth: await this.prepareOAuth(auth, endpoint, credential, publicUrl) };
405
+ }
406
+ /**
407
+ * Applies a prepared template update in the caller's transaction (the caller holds the org row
408
+ * lock and checked the actor): the endpoint, auth and template version of the connector, the
409
+ * template's default policies (`template_tool_policies`) and the policies the org did not set
410
+ * (`policy_source` `template` or `derived`): a tool the new template names takes its policy
411
+ * (`template`), an unnamed `template` tool returns to the policy derived from its annotations,
412
+ * unnamed `derived` tools and the org's own (`admin`) stay (spec §10). Tool definitions are untouched (the next refresh takes the new endpoint through drift review). With `resetCredentials` the credentials are replaced.
413
+ * Audits `connector.template_applied`. Deleted / missing -> `connector.not_found`.
414
+ */
415
+ async applyTemplateIn(t, orgId, connectorId, actorAccountId, p, actorTokenId) {
416
+ await this.row(t, orgId, connectorId, true);
417
+ const { input } = p;
418
+ const policies = input.template.defaultToolPolicies;
419
+ await t
420
+ .updateTable("connectors")
421
+ .set({
422
+ endpoint: JSON.stringify(p.endpoint),
423
+ auth: JSON.stringify(input.auth),
424
+ catalog_item_id: input.template.itemId,
425
+ catalog_version: input.template.version,
426
+ template_tool_policies: policies ? JSON.stringify(policies) : null,
427
+ updated_at: sql `now()`,
428
+ })
429
+ .where("id", "=", connectorId)
430
+ .execute();
431
+ // Tools at a default the org did not set (template or derived): a tool the new template names
432
+ // takes its policy (`template`); an unnamed `template` one returns to the derived policy;
433
+ // unnamed derived ones and the org's own (`admin`) stay
434
+ const atDefault = await t
435
+ .selectFrom("connector_tools")
436
+ .select(["name", "spec", "default_policy", "policy_source"])
437
+ .where("connector_id", "=", connectorId)
438
+ .where("policy_source", "in", ["template", "derived"])
439
+ .forUpdate()
440
+ .execute();
441
+ const policyChanges = {};
442
+ for (const tool of atDefault) {
443
+ const named = policies !== undefined && Object.hasOwn(policies, tool.name);
444
+ if (!named && tool.policy_source === "derived")
445
+ continue;
446
+ const next = named
447
+ ? policies[tool.name]
448
+ : policyFromAnnotations(tool.spec.annotations);
449
+ await t
450
+ .updateTable("connector_tools")
451
+ .set({ default_policy: next, policy_source: named ? "template" : "derived" })
452
+ .where("connector_id", "=", connectorId)
453
+ .where("name", "=", tool.name)
454
+ .execute();
455
+ if (next !== tool.default_policy) {
456
+ policyChanges[tool.name] = { from: tool.default_policy, to: next };
457
+ }
458
+ }
459
+ if (input.resetCredentials) {
460
+ await this.credentials.clearConnector(orgId, connectorId, t);
461
+ if (p.secret && p.owner) {
462
+ await this.credentials.setStatic(orgId, connectorId, input.auth, p.owner, p.secret, t);
463
+ }
464
+ if (p.oauth) {
465
+ await this.credentials.setOAuthClient(orgId, connectorId, p.oauth.client, t);
466
+ if (p.oauth.tokens) {
467
+ await this.credentials.setOAuthTokens(orgId, connectorId, { kind: "org" }, p.oauth.tokens, t);
468
+ }
469
+ }
470
+ }
471
+ await audit(t, {
472
+ actorAccountId,
473
+ ...(actorTokenId !== undefined && { actorTokenId }),
474
+ orgId,
475
+ action: "connector.template_applied",
476
+ target: connectorId,
477
+ detail: {
478
+ catalog: {
479
+ itemId: input.template.itemId,
480
+ version: input.template.version,
481
+ ...(input.template.templateId !== undefined && {
482
+ templateId: input.template.templateId,
483
+ }),
484
+ },
485
+ auth: {
486
+ type: input.auth.type,
487
+ ...(input.auth.type !== "none" && { ownership: input.auth.ownership }),
488
+ },
489
+ credentialsReset: input.resetCredentials,
490
+ credentialProvided: p.secret !== undefined || p.oauth !== undefined,
491
+ policies: policyChanges,
492
+ },
493
+ });
494
+ }
495
+ /**
496
+ * Previews a connector (Ruling A20, for the install wizard): after checking the connector, the
497
+ * endpoint (credential pattern check included) and the credential shape, reads the endpoint
498
+ * description with `connector.inspect` (auth hints, operations), and when it can authenticate
499
+ * (static credentials / a token from the client credentials flow) or auth is none, also lists the
500
+ * tools, builds keys from the slug and derives default policies and issues. Writes nothing (no
501
+ * rows, no audit); the OAuth authorization code flow lists no tools (no dynamic registration).
502
+ * Any outbound failure is 502 `connector.connect_failed`, the message holding only the
503
+ * connector's English explanation (redacted by the injected credentials) or the error class name.
504
+ * `customConnectors: deny` -> `catalog.custom_disabled`.
505
+ */
506
+ async preview(orgId, input) {
507
+ // 部署禁止组织自建连接器时也不替组织去连任意端点(目录安装不走预览)
508
+ await assertCustomAllowed(this.db, "customConnectors");
509
+ const endpoint = this.parseEndpoint(input.connector, input.endpoint);
510
+ const connector = this.registry.findConnector(input.connector);
511
+ if (!connector)
512
+ throw fail("connector", `Unknown connector "${input.connector}"`);
513
+ let slug = PREVIEW_SLUG;
514
+ if (input.slug !== undefined) {
515
+ if (!isValidSlug(input.slug))
516
+ throw fail("slug", SLUG_RULE);
517
+ slug = input.slug;
518
+ }
519
+ else if (input.name !== undefined) {
520
+ slug = deriveSlug(input.name) ?? PREVIEW_SLUG;
521
+ }
522
+ // 先校验凭据形状,再做任何外连
523
+ const auth = input.auth;
524
+ const credential = input.credential ?? undefined;
525
+ let inject;
526
+ let oauthClient = false;
527
+ if (auth === undefined) {
528
+ if (credential !== undefined && credential !== null) {
529
+ throw fail("credential", "Provide auth together with credential");
530
+ }
531
+ }
532
+ else if (auth.type === "none") {
533
+ if (credential !== undefined && credential !== null) {
534
+ throw fail("credential", "Auth type none takes no credential");
535
+ }
536
+ inject = async () => ({ headers: {} });
537
+ }
538
+ else if (!isOAuth(auth)) {
539
+ if (credential !== undefined && credential !== null) {
540
+ // 预览按「安装者自己的凭据」校验归属:组织型是组织凭据,个人型是自己的
541
+ const owner = auth.ownership === "org" ? { kind: "org" } : { kind: "user", accountId: "preview" };
542
+ this.credentials.validate(auth, owner, credential);
543
+ const secret = credential;
544
+ inject = async () => this.credentials.injectionFor("preview", auth, secret);
545
+ }
546
+ }
547
+ else if (auth.type === "oauth_client" && credential !== undefined && credential !== null) {
548
+ oauthClient = true;
549
+ }
550
+ const timeoutMs = this.config().tools.timeoutMs;
551
+ let inspection;
552
+ if (connector.inspect) {
553
+ try {
554
+ inspection = await connector.inspect({ endpoint, signal: AbortSignal.timeout(timeoutMs) });
555
+ }
556
+ catch (e) {
557
+ const message = e instanceof ToolError
558
+ ? `Could not read the connector description: ${e.message.slice(0, MAX_INSPECT_DETAIL)}`
559
+ : `Could not read the connector description (${e instanceof Error ? e.name : "Error"})`;
560
+ throw new ApiError("connector.connect_failed", message);
561
+ }
562
+ }
563
+ if (oauthClient && auth?.type === "oauth_client") {
564
+ const oauth = await this.prepareOAuth(auth, endpoint, credential, this.config().http.publicUrl ?? "");
565
+ const token = oauth.tokens?.access_token;
566
+ if (token)
567
+ inject = async () => this.credentials.injectionFor("preview", auth, { token });
568
+ }
569
+ let tools = null;
570
+ let issues = [];
571
+ if (inject) {
572
+ const listed = await this.drift.listTools(input.connector, endpoint, inject, {
573
+ orgId,
574
+ preview: true,
575
+ });
576
+ const snap = snapshot(slug, listed.specs, listed.resources);
577
+ issues = snap.issues;
578
+ tools = snap.rows.map((r) => ({
579
+ name: r.name,
580
+ key: toolKey(slug, r.name),
581
+ description: r.spec.description,
582
+ inputSchema: r.spec.inputSchema,
583
+ annotations: r.spec.annotations ?? null,
584
+ defaultPolicy: policyFromAnnotations(r.spec.annotations),
585
+ issue: r.issue,
586
+ source: r.source,
587
+ }));
588
+ }
589
+ return {
590
+ tools,
591
+ issues,
592
+ authHints: inspection?.authHints ?? [],
593
+ ...(inspection?.operations !== undefined && { operations: inspection.operations }),
594
+ };
595
+ }
596
+ async insertTools(t, connectorId, rows, templatePolicies) {
597
+ if (rows.length === 0)
598
+ return;
599
+ await recordToolSpecs(t, rows);
600
+ const fromTemplate = (name) => templatePolicies && Object.hasOwn(templatePolicies, name)
601
+ ? templatePolicies[name]
602
+ : undefined;
603
+ await t
604
+ .insertInto("connector_tools")
605
+ .values(rows.map((r) => ({
606
+ connector_id: connectorId,
607
+ name: r.name,
608
+ spec: JSON.stringify(r.spec),
609
+ spec_hash: r.specHash,
610
+ status: r.issue ? "disabled" : "enabled",
611
+ default_policy: fromTemplate(r.name) ?? policyFromAnnotations(r.spec.annotations),
612
+ ...(fromTemplate(r.name) !== undefined && { policy_source: "template" }),
613
+ source: r.source,
614
+ // 安装即复核
615
+ ...(!r.issue && { reviewed_at: sql `now()` }),
616
+ })))
617
+ .execute();
618
+ }
619
+ /** 读本组织未删除的连接器行;不存在 → `connector.not_found`。`lock` 时 FOR UPDATE。 */
620
+ async row(db, orgId, connectorId, lock = false) {
621
+ let q = db
622
+ .selectFrom("connectors")
623
+ .selectAll()
624
+ .where("id", "=", connectorId)
625
+ .where("org_id", "=", orgId)
626
+ .where("deleted_at", "is", null);
627
+ if (lock)
628
+ q = q.forUpdate();
629
+ const r = await q.executeTakeFirst();
630
+ if (!r)
631
+ throw notFound();
632
+ return r;
633
+ }
634
+ summary(r) {
635
+ return {
636
+ id: r.id,
637
+ slug: r.slug,
638
+ name: r.name,
639
+ connector: r.connector,
640
+ // 大段文本(如 OpenAPI 文档)不回显全文,见 endpoint.ts
641
+ endpoint: endpointView(r.endpoint),
642
+ auth: r.auth,
643
+ status: r.status,
644
+ createdBy: r.created_by,
645
+ createdAt: r.created_at,
646
+ updatedAt: r.updated_at,
647
+ };
648
+ }
649
+ async detail(db, orgId, connectorId, viewerAccountId) {
650
+ const r = await this.row(db, orgId, connectorId);
651
+ const tools = await db
652
+ .selectFrom("connector_tools")
653
+ .select([
654
+ "name",
655
+ "spec",
656
+ "spec_hash",
657
+ "status",
658
+ "default_policy",
659
+ "source",
660
+ "policy_source",
661
+ "missing_since",
662
+ "reviewed_at",
663
+ "pending_spec",
664
+ "pending_spec_hash",
665
+ "pending_source",
666
+ ])
667
+ .where("connector_id", "=", connectorId)
668
+ .orderBy("name")
669
+ .execute();
670
+ const s = this.summary(r);
671
+ const auth = s.auth;
672
+ const status = await this.credentials.status(orgId, connectorId, auth, viewerAccountId, db === this.db ? undefined : db);
673
+ return {
674
+ ...s,
675
+ tools: tools.map((t) => {
676
+ const spec = t.spec;
677
+ const next = t.pending_spec;
678
+ return {
679
+ name: t.name,
680
+ key: toolKey(r.slug, t.name),
681
+ description: spec.description,
682
+ inputSchema: spec.inputSchema,
683
+ annotations: spec.annotations ?? null,
684
+ connectorData: spec.connectorData ?? null,
685
+ status: t.status,
686
+ defaultPolicy: t.default_policy,
687
+ specHash: t.spec_hash,
688
+ issue: toolIssue(r.slug, next ?? spec),
689
+ source: t.source,
690
+ policySource: t.policy_source,
691
+ missing: t.missing_since !== null,
692
+ reviewedAt: t.reviewed_at,
693
+ pending: next && t.pending_spec_hash !== null
694
+ ? {
695
+ description: next.description,
696
+ inputSchema: next.inputSchema,
697
+ annotations: next.annotations ?? null,
698
+ connectorData: next.connectorData ?? null,
699
+ specHash: t.pending_spec_hash,
700
+ source: t.pending_source ?? t.source,
701
+ }
702
+ : null,
703
+ };
704
+ }),
705
+ credential: { ownership: auth.type === "none" ? null : auth.ownership, status },
706
+ };
707
+ }
708
+ /** 本组织未删除的连接器(按创建时间)。 */
709
+ async list(orgId) {
710
+ const rows = await this.db
711
+ .selectFrom("connectors")
712
+ .selectAll()
713
+ .where("org_id", "=", orgId)
714
+ .where("deleted_at", "is", null)
715
+ .orderBy("created_at")
716
+ .orderBy("id")
717
+ .execute();
718
+ return rows.map((r) => this.summary(r));
719
+ }
720
+ async get(orgId, connectorId, viewerAccountId) {
721
+ return this.detail(this.db, orgId, connectorId, viewerAccountId);
722
+ }
723
+ /** 改名、启停连接器、工具启停与默认策略、批量把无注解工具改为 allow(`connectors.manage`)。 */
724
+ async update(orgId, connectorId, actorAccountId, patch, actorTokenId) {
725
+ return this.db.transaction().execute(async (t) => {
726
+ await lockOrgAndAuthorize(t, orgId, actorAccountId, "connectors.manage");
727
+ const app = await this.row(t, orgId, connectorId, true);
728
+ const current = await t
729
+ .selectFrom("connector_tools")
730
+ .select([
731
+ "name",
732
+ "spec",
733
+ "status",
734
+ "default_policy",
735
+ "missing_since",
736
+ "pending_spec_hash",
737
+ "reviewed_at",
738
+ ])
739
+ .where("connector_id", "=", connectorId)
740
+ .forUpdate()
741
+ .execute();
742
+ const byName = new Map(current.map((c) => [c.name, c]));
743
+ const toolChanges = Object.entries(patch.tools ?? {});
744
+ const problems = [];
745
+ for (const [name, change] of toolChanges) {
746
+ const c = byName.get(name);
747
+ if (!c) {
748
+ problems.push({ path: `tools.${name}`, message: "No such tool in this connector" });
749
+ continue;
750
+ }
751
+ if (change.status === "enabled") {
752
+ const path = `tools.${name}.status`;
753
+ // 待复核(新增或定义变了)的工具只能经复核接口启用:这里启用会绕过复核、或把旧定义暴露给模型
754
+ if (c.status === "pending_review" || c.pending_spec_hash !== null) {
755
+ problems.push({
756
+ path,
757
+ message: `Tool is pending review; approve it with POST /connectors/${connectorId}/tools/${name}/review`,
758
+ });
759
+ continue;
760
+ }
761
+ if (c.missing_since !== null) {
762
+ problems.push({ path, message: "The connector no longer provides this tool" });
763
+ continue;
764
+ }
765
+ // 从未复核过的工具(如被拒绝的新工具):启用等于跳过复核,只能经复核接口批准
766
+ if (c.reviewed_at === null) {
767
+ problems.push({
768
+ path,
769
+ message: `Tool has never been reviewed; approve it with POST /connectors/${connectorId}/tools/${name}/review`,
770
+ });
771
+ continue;
772
+ }
773
+ const issue = toolIssue(app.slug, c.spec);
774
+ if (issue)
775
+ problems.push({ path, message: issue });
776
+ }
777
+ }
778
+ if (problems.length > 0) {
779
+ throw new ApiError("validation_failed", "Request validation failed", problems);
780
+ }
781
+ if (patch.name !== undefined || patch.status !== undefined) {
782
+ await t
783
+ .updateTable("connectors")
784
+ .set({
785
+ ...(patch.name !== undefined && { name: patch.name }),
786
+ ...(patch.status !== undefined && { status: patch.status }),
787
+ updated_at: sql `now()`,
788
+ })
789
+ .where("id", "=", connectorId)
790
+ .execute();
791
+ }
792
+ const allowed = [];
793
+ if (patch.allowUnannotated) {
794
+ for (const c of current) {
795
+ const explicit = patch.tools?.[c.name]?.defaultPolicy !== undefined;
796
+ if (!explicit &&
797
+ c.default_policy === "ask" &&
798
+ unannotated(c.spec.annotations)) {
799
+ allowed.push(c.name);
800
+ }
801
+ }
802
+ if (allowed.length > 0) {
803
+ await t
804
+ .updateTable("connector_tools")
805
+ .set({ default_policy: "allow", policy_source: "admin" })
806
+ .where("connector_id", "=", connectorId)
807
+ .where("name", "in", allowed)
808
+ .execute();
809
+ }
810
+ }
811
+ for (const [name, change] of toolChanges) {
812
+ if (change.status === undefined && change.defaultPolicy === undefined)
813
+ continue;
814
+ await t
815
+ .updateTable("connector_tools")
816
+ .set({
817
+ ...(change.status !== undefined && { status: change.status }),
818
+ ...(change.defaultPolicy !== undefined && {
819
+ default_policy: change.defaultPolicy,
820
+ policy_source: "admin",
821
+ }),
822
+ })
823
+ .where("connector_id", "=", connectorId)
824
+ .where("name", "=", name)
825
+ .execute();
826
+ }
827
+ await audit(t, {
828
+ actorAccountId,
829
+ ...(actorTokenId !== undefined && { actorTokenId }),
830
+ orgId,
831
+ action: "connector.updated",
832
+ target: connectorId,
833
+ detail: {
834
+ ...(patch.name !== undefined && { name: { from: app.name, to: patch.name } }),
835
+ ...(patch.status !== undefined && { status: { from: app.status, to: patch.status } }),
836
+ ...(toolChanges.length > 0 && { tools: Object.fromEntries(toolChanges) }),
837
+ ...(allowed.length > 0 && { allowedUnannotated: allowed }),
838
+ },
839
+ });
840
+ return this.detail(t, orgId, connectorId, actorAccountId);
841
+ });
842
+ }
843
+ /** 软删除,并删除该连接器的全部凭据(`connectors.manage`)。 */
844
+ async remove(orgId, connectorId, actorAccountId, actorTokenId) {
845
+ await this.db.transaction().execute(async (t) => {
846
+ await lockOrgAndAuthorize(t, orgId, actorAccountId, "connectors.manage");
847
+ await this.removeIn(t, orgId, connectorId, actorAccountId, actorTokenId);
848
+ });
849
+ }
850
+ /**
851
+ * 在调用方事务里软删除连接器(调用方已锁组织行并按需复核角色;应用安装的回滚与卸载用):删全部凭据与授权,
852
+ * 审计 `connector.deleted`(`reason` 并进 detail)。已删除 / 不存在 → `connector.not_found`。
853
+ */
854
+ async removeIn(t, orgId, connectorId, actorAccountId, actorTokenId, reason) {
855
+ const app = await this.row(t, orgId, connectorId, true);
856
+ await t
857
+ .updateTable("connectors")
858
+ .set({ deleted_at: sql `now()`, updated_at: sql `now()` })
859
+ .where("id", "=", connectorId)
860
+ .execute();
861
+ await this.credentials.clearConnector(orgId, connectorId, t);
862
+ // 授权绑定连接器 id(软删除不触发外键级联):一并删掉,不留给任何后来的连接器
863
+ await t.deleteFrom("tool_grants").where("connector_id", "=", connectorId).execute();
864
+ await t
865
+ .deleteFrom("conversation_tool_grants")
866
+ .where("connector_id", "=", connectorId)
867
+ .execute();
868
+ await audit(t, {
869
+ actorAccountId,
870
+ ...(actorTokenId !== undefined && { actorTokenId }),
871
+ orgId,
872
+ action: "connector.deleted",
873
+ target: connectorId,
874
+ detail: { slug: app.slug, ...(reason !== undefined && { reason }) },
875
+ });
876
+ }
877
+ /**
878
+ * 重新 listTools 并按漂移检测的规则比对写入(见 drift.ts):新增 / 定义变化的工具待复核(旧定义保留供对比),
879
+ * 消失的工具停用;连接器从未拍过快照时首个快照直接启用。
880
+ * 组织凭据型连接器用组织凭据,个人型用发起人自己的凭据(缺 → `credential.missing`)。OAuth 连接器收到 401 时
881
+ * 在刷新锁内刷新 token 后重试一次。`trigger: "first_connect"`:OAuth 授权码连接器首个成员连接后由连接流程调用
882
+ * (A16),只要求 `connectors.use`。审计 `connector.tools_refreshed`(变化的工具名,不含定义)。
883
+ */
884
+ async refresh(orgId, connectorId, actorAccountId, actorTokenId, opts = {}) {
885
+ const app = await this.row(this.db, orgId, connectorId);
886
+ const auth = app.auth;
887
+ const { specs, resources } = await this.drift.listTools(app.connector, app.endpoint, () => this.credentials.injection(orgId, connectorId, auth, actorAccountId), { orgId, connectorId }, (last) => this.credentials.onUnauthorized(orgId, connectorId, auth, actorAccountId, last));
888
+ const { rows, issues } = snapshot(app.slug, specs, resources);
889
+ return this.db.transaction().execute(async (t) => {
890
+ await lockOrgAndAuthorize(t, orgId, actorAccountId, opts.trigger === "first_connect" ? "connectors.use" : "connectors.manage");
891
+ const locked = await this.row(t, orgId, connectorId, true);
892
+ const changes = await this.drift.apply(t, locked, rows);
893
+ // 首个快照(授权码连接器首个成员连接后):按目录模板的 defaultToolPolicies 设默认策略(policy_source 'template')
894
+ const templatePolicies = locked.template_tool_policies;
895
+ if (locked.tools_synced_at === null && templatePolicies) {
896
+ for (const [name, policy] of Object.entries(templatePolicies)) {
897
+ await t
898
+ .updateTable("connector_tools")
899
+ .set({ default_policy: policy, policy_source: "template" })
900
+ .where("connector_id", "=", connectorId)
901
+ .where("name", "=", name)
902
+ .where("policy_source", "=", "derived")
903
+ .execute();
904
+ }
905
+ }
906
+ await audit(t, {
907
+ actorAccountId,
908
+ ...(actorTokenId !== undefined && { actorTokenId }),
909
+ orgId,
910
+ action: "connector.tools_refreshed",
911
+ target: connectorId,
912
+ detail: { ...changes, ...(opts.trigger && { trigger: opts.trigger }) },
913
+ });
914
+ return {
915
+ connector: await this.detail(t, orgId, connectorId, actorAccountId),
916
+ issues,
917
+ changes,
918
+ };
919
+ });
920
+ }
921
+ /**
922
+ * 复核一个待复核的工具(`connectors.manage`,spec 7.3)。`approve`:采用待复核的新定义(若有)并启用,
923
+ * `reviewed_at` 记为现在;默认策略按新定义的注解重新推导,除非管理员设过(`policy_source = admin`)且来源没变。
924
+ * `reject`:停用,丢弃待复核的新定义、保留旧定义;从未复核过的工具被拒后 `reviewed_at` 仍为空(仍可再批准)。
925
+ * 工具不存在 → 404 `connector.tool_not_found`;不可复核(已复核且无待复核定义,或已消失)→ 409 `connector.tool_not_pending`;新定义不合规(如 key 不合法)不能通过 → `validation_failed`。
926
+ * 采用新定义即换了指纹:旧的「以后都允许」/「本会话都允许」授权按指纹比对自然失效(D8)。审计 `connector.tool_reviewed`。
927
+ */
928
+ async reviewTool(orgId, connectorId, name, action, actorAccountId, actorTokenId) {
929
+ return this.db.transaction().execute(async (t) => {
930
+ await lockOrgAndAuthorize(t, orgId, actorAccountId, "connectors.manage");
931
+ const app = await this.row(t, orgId, connectorId, true);
932
+ const tool = await t
933
+ .selectFrom("connector_tools")
934
+ .select([
935
+ "status",
936
+ "source",
937
+ "spec",
938
+ "policy_source",
939
+ "pending_spec",
940
+ "pending_spec_hash",
941
+ "pending_source",
942
+ "reviewed_at",
943
+ "missing_since",
944
+ ])
945
+ .where("connector_id", "=", connectorId)
946
+ .where("name", "=", name)
947
+ .forUpdate()
948
+ .executeTakeFirst();
949
+ if (!tool)
950
+ throw new ApiError("connector.tool_not_found", "Tool not found in this connector");
951
+ // 可复核:待复核、有待复核的新定义,或从未复核过(如被拒绝的新工具,仍可再批准);已消失的不可复核
952
+ const reviewable = tool.status === "pending_review" ||
953
+ tool.pending_spec_hash !== null ||
954
+ tool.reviewed_at === null;
955
+ if (!reviewable || tool.missing_since !== null) {
956
+ throw new ApiError("connector.tool_not_pending", "Tool is not pending review");
957
+ }
958
+ const next = (tool.pending_spec ?? tool.spec);
959
+ const nextSource = tool.pending_source ?? tool.source;
960
+ if (action === "approve") {
961
+ const issue = toolIssue(app.slug, next);
962
+ if (issue)
963
+ throw fail("action", issue);
964
+ }
965
+ const rederive = tool.policy_source === "derived" || nextSource !== tool.source;
966
+ await t
967
+ .updateTable("connector_tools")
968
+ .set({
969
+ ...(action === "approve"
970
+ ? {
971
+ status: "enabled",
972
+ ...(tool.pending_spec_hash !== null && {
973
+ spec: JSON.stringify(next),
974
+ spec_hash: tool.pending_spec_hash,
975
+ source: nextSource,
976
+ }),
977
+ ...(rederive && {
978
+ default_policy: policyFromAnnotations(next.annotations),
979
+ policy_source: "derived",
980
+ }),
981
+ }
982
+ : { status: "disabled" }),
983
+ pending_spec: null,
984
+ pending_spec_hash: null,
985
+ pending_source: null,
986
+ // 拒绝从未复核过的工具不算「复核通过过」:reviewed_at 保持为空(之后不能经 PATCH 启用)
987
+ ...((action === "approve" || tool.reviewed_at !== null) && {
988
+ reviewed_at: sql `now()`,
989
+ }),
990
+ })
991
+ .where("connector_id", "=", connectorId)
992
+ .where("name", "=", name)
993
+ .execute();
994
+ await audit(t, {
995
+ actorAccountId,
996
+ ...(actorTokenId !== undefined && { actorTokenId }),
997
+ orgId,
998
+ action: "connector.tool_reviewed",
999
+ target: connectorId,
1000
+ detail: { tool: name, action },
1001
+ });
1002
+ return this.detail(t, orgId, connectorId, actorAccountId);
1003
+ });
1004
+ }
1005
+ /**
1006
+ * 写凭据(组织凭据:`connectors.manage`;个人凭据:`connectors.use`,只写发起人自己的那一条)。
1007
+ * 形状与归属不符 → `validation_failed`。审计只记类型与归属。
1008
+ */
1009
+ async setCredential(orgId, connectorId, actorAccountId, ownership, value, actorTokenId) {
1010
+ if (ownership === "org") {
1011
+ const app = await this.row(this.db, orgId, connectorId);
1012
+ const auth = app.auth;
1013
+ if (isOAuth(auth)) {
1014
+ await this.rotateOAuthClient(orgId, connectorId, actorAccountId, auth, value, actorTokenId);
1015
+ return;
1016
+ }
1017
+ }
1018
+ await this.db.transaction().execute(async (t) => {
1019
+ await lockOrgAndAuthorize(t, orgId, actorAccountId, ownership === "org" ? "connectors.manage" : "connectors.use");
1020
+ const app = await this.row(t, orgId, connectorId);
1021
+ const auth = app.auth;
1022
+ const owner = ownership === "org" ? { kind: "org" } : { kind: "user", accountId: actorAccountId };
1023
+ this.credentials.validate(auth, owner, value);
1024
+ await this.credentials.setStatic(orgId, connectorId, auth, owner, value, t);
1025
+ await audit(t, {
1026
+ actorAccountId,
1027
+ ...(actorTokenId !== undefined && { actorTokenId }),
1028
+ orgId,
1029
+ action: "connector.credential.set",
1030
+ target: connectorId,
1031
+ detail: { type: auth.type, ownership },
1032
+ });
1033
+ });
1034
+ }
1035
+ /**
1036
+ * 轮换 OAuth 客户端(Ruling A18,组织凭据接口,`connectors.manage`):值为 `{ clientId, clientSecret }`。
1037
+ * 客户端凭据连接器先用新客户端取一次 token 验证(失败 → 502 `connector.connect_failed`,什么都不改),再替换客户端并以
1038
+ * 新 token 替换缓存的组织 token;授权码连接器只限手填 client,只替换客户端(成员的 token 保留,之后刷新若得到
1039
+ * invalid_client 会作废)。审计只记类型与归属。
1040
+ */
1041
+ async rotateOAuthClient(orgId, connectorId, actorAccountId, auth, value, actorTokenId) {
1042
+ if (auth.type === "oauth_code" && auth.client !== "manual") {
1043
+ throw fail("auth.client", "The OAuth client of this connector was registered automatically and cannot be replaced");
1044
+ }
1045
+ const next = parseOAuthClient(value, auth.type === "oauth_client", "");
1046
+ const current = await this.credentials.oauthClient(orgId, connectorId);
1047
+ if (!current) {
1048
+ throw new ApiError("connector.connect_failed", "The connector has no registered OAuth client; reinstall it");
1049
+ }
1050
+ const client = { ...current, clientId: next.clientId };
1051
+ delete client.clientSecret;
1052
+ if (next.clientSecret !== undefined) {
1053
+ client.clientSecret = next.clientSecret;
1054
+ client.authMethod =
1055
+ auth.tokenAuthMethod ??
1056
+ (current.authMethod === "none" ? "client_secret_basic" : current.authMethod);
1057
+ }
1058
+ else {
1059
+ client.authMethod = "none";
1060
+ }
1061
+ let tokens;
1062
+ if (auth.type === "oauth_client") {
1063
+ try {
1064
+ tokens = await tokenRequest(client, { grant_type: "client_credentials", scope: (auth.scopes ?? client.scopes)?.join(" ") }, oauthHttpOptions(this.config()));
1065
+ }
1066
+ catch (e) {
1067
+ throw connectFailed(e);
1068
+ }
1069
+ }
1070
+ await this.db.transaction().execute(async (t) => {
1071
+ await lockOrgAndAuthorize(t, orgId, actorAccountId, "connectors.manage");
1072
+ await this.row(t, orgId, connectorId, true);
1073
+ await this.credentials.setOAuthClient(orgId, connectorId, client, t);
1074
+ if (tokens)
1075
+ await this.credentials.setOAuthTokens(orgId, connectorId, { kind: "org" }, tokens, t);
1076
+ await audit(t, {
1077
+ actorAccountId,
1078
+ ...(actorTokenId !== undefined && { actorTokenId }),
1079
+ orgId,
1080
+ action: "connector.credential.set",
1081
+ target: connectorId,
1082
+ detail: { type: auth.type, ownership: "org" },
1083
+ });
1084
+ });
1085
+ }
1086
+ /** 删除发起人自己的个人凭据(`connectors.use`);本来就没有也成功。 */
1087
+ async clearMyCredential(orgId, connectorId, actorAccountId, actorTokenId) {
1088
+ await this.db.transaction().execute(async (t) => {
1089
+ await lockOrgAndAuthorize(t, orgId, actorAccountId, "connectors.use");
1090
+ const app = await this.row(t, orgId, connectorId);
1091
+ await this.credentials.clear(orgId, connectorId, { kind: "user", accountId: actorAccountId }, t);
1092
+ await audit(t, {
1093
+ actorAccountId,
1094
+ ...(actorTokenId !== undefined && { actorTokenId }),
1095
+ orgId,
1096
+ action: "connector.credential.cleared",
1097
+ target: connectorId,
1098
+ detail: { type: app.auth.type, ownership: "personal" },
1099
+ });
1100
+ });
1101
+ }
1102
+ }