@tai42/api-client 16.0.0 → 16.0.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 (370) hide show
  1. package/dist/client/agents-client.d.ts +26 -0
  2. package/dist/client/agents-client.d.ts.map +1 -0
  3. package/dist/client/agents-client.js +19 -0
  4. package/dist/client/agents-client.js.map +1 -0
  5. package/dist/client/auth-client.d.ts +205 -0
  6. package/dist/client/auth-client.d.ts.map +1 -0
  7. package/dist/client/auth-client.js +59 -0
  8. package/dist/client/auth-client.js.map +1 -0
  9. package/dist/client/backend-client.d.ts +39 -0
  10. package/dist/client/backend-client.d.ts.map +1 -0
  11. package/dist/client/backend-client.js +22 -0
  12. package/dist/client/backend-client.js.map +1 -0
  13. package/dist/client/backup-client.d.ts +56 -0
  14. package/dist/client/backup-client.d.ts.map +1 -0
  15. package/dist/client/backup-client.js +12 -0
  16. package/dist/client/backup-client.js.map +1 -0
  17. package/dist/client/capabilities-client.d.ts +35 -0
  18. package/dist/client/capabilities-client.d.ts.map +1 -0
  19. package/dist/client/capabilities-client.js +19 -0
  20. package/dist/client/capabilities-client.js.map +1 -0
  21. package/dist/client/channels-client.d.ts +7 -0
  22. package/dist/client/channels-client.d.ts.map +1 -0
  23. package/dist/client/channels-client.js +10 -0
  24. package/dist/client/channels-client.js.map +1 -0
  25. package/dist/client/config-client.d.ts +59 -0
  26. package/dist/client/config-client.d.ts.map +1 -0
  27. package/dist/client/config-client.js +18 -0
  28. package/dist/client/config-client.js.map +1 -0
  29. package/dist/client/connectors-client.d.ts +225 -0
  30. package/dist/client/connectors-client.d.ts.map +1 -0
  31. package/dist/client/connectors-client.js +31 -0
  32. package/dist/client/connectors-client.js.map +1 -0
  33. package/dist/client/conversations-client.d.ts +399 -0
  34. package/dist/client/conversations-client.d.ts.map +1 -0
  35. package/dist/client/conversations-client.js +120 -0
  36. package/dist/client/conversations-client.js.map +1 -0
  37. package/dist/client/hooks-client.d.ts +113 -0
  38. package/dist/client/hooks-client.d.ts.map +1 -0
  39. package/dist/client/hooks-client.js +25 -0
  40. package/dist/client/hooks-client.js.map +1 -0
  41. package/dist/{client.d.ts → client/index.d.ts} +2235 -2666
  42. package/dist/client/index.d.ts.map +1 -0
  43. package/dist/client/index.js +79 -0
  44. package/dist/client/index.js.map +1 -0
  45. package/dist/client/interactions-client.d.ts +52 -0
  46. package/dist/client/interactions-client.d.ts.map +1 -0
  47. package/dist/client/interactions-client.js +84 -0
  48. package/dist/client/interactions-client.js.map +1 -0
  49. package/dist/client/login-client.d.ts +56 -0
  50. package/dist/client/login-client.d.ts.map +1 -0
  51. package/dist/client/login-client.js +103 -0
  52. package/dist/client/login-client.js.map +1 -0
  53. package/dist/client/manifest-client.d.ts +354 -0
  54. package/dist/client/manifest-client.d.ts.map +1 -0
  55. package/dist/client/manifest-client.js +76 -0
  56. package/dist/client/manifest-client.js.map +1 -0
  57. package/dist/client/marketplace-client.d.ts +274 -0
  58. package/dist/client/marketplace-client.d.ts.map +1 -0
  59. package/dist/client/marketplace-client.js +46 -0
  60. package/dist/client/marketplace-client.js.map +1 -0
  61. package/dist/client/mcp-config-schema-client.d.ts +5 -0
  62. package/dist/client/mcp-config-schema-client.d.ts.map +1 -0
  63. package/dist/client/mcp-config-schema-client.js +9 -0
  64. package/dist/client/mcp-config-schema-client.js.map +1 -0
  65. package/dist/client/notifications-client.d.ts +27 -0
  66. package/dist/client/notifications-client.d.ts.map +1 -0
  67. package/dist/client/notifications-client.js +11 -0
  68. package/dist/client/notifications-client.js.map +1 -0
  69. package/dist/client/observability-client.d.ts +103 -0
  70. package/dist/client/observability-client.d.ts.map +1 -0
  71. package/dist/client/observability-client.js +43 -0
  72. package/dist/client/observability-client.js.map +1 -0
  73. package/dist/client/plugins-client.d.ts +17 -0
  74. package/dist/client/plugins-client.d.ts.map +1 -0
  75. package/dist/client/plugins-client.js +9 -0
  76. package/dist/client/plugins-client.js.map +1 -0
  77. package/dist/client/policy-client.d.ts +37 -0
  78. package/dist/client/policy-client.d.ts.map +1 -0
  79. package/dist/client/policy-client.js +26 -0
  80. package/dist/client/policy-client.js.map +1 -0
  81. package/dist/client/presets-client.d.ts +335 -0
  82. package/dist/client/presets-client.d.ts.map +1 -0
  83. package/dist/client/presets-client.js +38 -0
  84. package/dist/client/presets-client.js.map +1 -0
  85. package/dist/client/profiles-client.d.ts +102 -0
  86. package/dist/client/profiles-client.d.ts.map +1 -0
  87. package/dist/client/profiles-client.js +37 -0
  88. package/dist/client/profiles-client.js.map +1 -0
  89. package/dist/client/schedules-client.d.ts +28 -0
  90. package/dist/client/schedules-client.d.ts.map +1 -0
  91. package/dist/client/schedules-client.js +13 -0
  92. package/dist/client/schedules-client.js.map +1 -0
  93. package/dist/client/settings-schema-client.d.ts +23 -0
  94. package/dist/client/settings-schema-client.d.ts.map +1 -0
  95. package/dist/client/settings-schema-client.js +9 -0
  96. package/dist/client/settings-schema-client.js.map +1 -0
  97. package/dist/client/states-client.d.ts +471 -0
  98. package/dist/client/states-client.d.ts.map +1 -0
  99. package/dist/client/states-client.js +144 -0
  100. package/dist/client/states-client.js.map +1 -0
  101. package/dist/client/storage-client.d.ts +40 -0
  102. package/dist/client/storage-client.d.ts.map +1 -0
  103. package/dist/client/storage-client.js +43 -0
  104. package/dist/client/storage-client.js.map +1 -0
  105. package/dist/client/sub-mcp-client.d.ts +17 -0
  106. package/dist/client/sub-mcp-client.d.ts.map +1 -0
  107. package/dist/client/sub-mcp-client.js +14 -0
  108. package/dist/client/sub-mcp-client.js.map +1 -0
  109. package/dist/client/system-client.d.ts +11 -0
  110. package/dist/client/system-client.d.ts.map +1 -0
  111. package/dist/client/system-client.js +13 -0
  112. package/dist/client/system-client.js.map +1 -0
  113. package/dist/client/templates-client.d.ts +29 -0
  114. package/dist/client/templates-client.d.ts.map +1 -0
  115. package/dist/client/templates-client.js +21 -0
  116. package/dist/client/templates-client.js.map +1 -0
  117. package/dist/client/tool-extensions-client.d.ts +54 -0
  118. package/dist/client/tool-extensions-client.d.ts.map +1 -0
  119. package/dist/client/tool-extensions-client.js +21 -0
  120. package/dist/client/tool-extensions-client.js.map +1 -0
  121. package/dist/client/tool-meta-client.d.ts +69 -0
  122. package/dist/client/tool-meta-client.d.ts.map +1 -0
  123. package/dist/client/tool-meta-client.js +38 -0
  124. package/dist/client/tool-meta-client.js.map +1 -0
  125. package/dist/client/tools-client.d.ts +83 -0
  126. package/dist/client/tools-client.d.ts.map +1 -0
  127. package/dist/client/tools-client.js +38 -0
  128. package/dist/client/tools-client.js.map +1 -0
  129. package/dist/client/transport.d.ts +13 -0
  130. package/dist/client/transport.d.ts.map +1 -0
  131. package/dist/client/transport.js +10 -0
  132. package/dist/client/transport.js.map +1 -0
  133. package/dist/client/trigger-links-client.d.ts +44 -0
  134. package/dist/client/trigger-links-client.d.ts.map +1 -0
  135. package/dist/client/trigger-links-client.js +30 -0
  136. package/dist/client/trigger-links-client.js.map +1 -0
  137. package/dist/client/web-entry-gate-client.d.ts +34 -0
  138. package/dist/client/web-entry-gate-client.d.ts.map +1 -0
  139. package/dist/client/web-entry-gate-client.js +25 -0
  140. package/dist/client/web-entry-gate-client.js.map +1 -0
  141. package/dist/http.d.ts.map +1 -1
  142. package/dist/http.js +33 -17
  143. package/dist/http.js.map +1 -1
  144. package/dist/schemas/api-keys.d.ts +34 -0
  145. package/dist/schemas/api-keys.d.ts.map +1 -0
  146. package/dist/schemas/api-keys.js +22 -0
  147. package/dist/schemas/api-keys.js.map +1 -0
  148. package/dist/schemas/auth-routes.d.ts +78 -0
  149. package/dist/schemas/auth-routes.d.ts.map +1 -0
  150. package/dist/schemas/auth-routes.js +42 -0
  151. package/dist/schemas/auth-routes.js.map +1 -0
  152. package/dist/schemas/backup.d.ts +78 -0
  153. package/dist/schemas/backup.d.ts.map +1 -0
  154. package/dist/schemas/backup.js +33 -0
  155. package/dist/schemas/backup.js.map +1 -0
  156. package/dist/schemas/capabilities.d.ts +99 -0
  157. package/dist/schemas/capabilities.d.ts.map +1 -0
  158. package/dist/schemas/capabilities.js +74 -0
  159. package/dist/schemas/capabilities.js.map +1 -0
  160. package/dist/schemas/channels.d.ts +7 -0
  161. package/dist/schemas/channels.d.ts.map +1 -0
  162. package/dist/schemas/channels.js +9 -0
  163. package/dist/schemas/channels.js.map +1 -0
  164. package/dist/schemas/config.d.ts +17 -0
  165. package/dist/schemas/config.d.ts.map +1 -0
  166. package/dist/schemas/config.js +17 -0
  167. package/dist/schemas/config.js.map +1 -0
  168. package/dist/schemas/connectors.d.ts +383 -0
  169. package/dist/schemas/connectors.d.ts.map +1 -0
  170. package/dist/schemas/connectors.js +122 -0
  171. package/dist/schemas/connectors.js.map +1 -0
  172. package/dist/schemas/conversations.d.ts +600 -0
  173. package/dist/schemas/conversations.d.ts.map +1 -0
  174. package/dist/schemas/conversations.js +208 -0
  175. package/dist/schemas/conversations.js.map +1 -0
  176. package/dist/schemas/extensions.d.ts +44 -0
  177. package/dist/schemas/extensions.d.ts.map +1 -0
  178. package/dist/schemas/extensions.js +29 -0
  179. package/dist/schemas/extensions.js.map +1 -0
  180. package/dist/schemas/fleet.d.ts +271 -0
  181. package/dist/schemas/fleet.d.ts.map +1 -0
  182. package/dist/schemas/fleet.js +132 -0
  183. package/dist/schemas/fleet.js.map +1 -0
  184. package/dist/schemas/hooks.d.ts +120 -0
  185. package/dist/schemas/hooks.d.ts.map +1 -0
  186. package/dist/schemas/hooks.js +44 -0
  187. package/dist/schemas/hooks.js.map +1 -0
  188. package/dist/schemas/index.d.ts +38 -0
  189. package/dist/schemas/index.d.ts.map +1 -0
  190. package/dist/schemas/index.js +38 -0
  191. package/dist/schemas/index.js.map +1 -0
  192. package/dist/schemas/interactions.d.ts +156 -0
  193. package/dist/schemas/interactions.d.ts.map +1 -0
  194. package/dist/schemas/interactions.js +124 -0
  195. package/dist/schemas/interactions.js.map +1 -0
  196. package/dist/schemas/login.d.ts +86 -0
  197. package/dist/schemas/login.d.ts.map +1 -0
  198. package/dist/schemas/login.js +54 -0
  199. package/dist/schemas/login.js.map +1 -0
  200. package/dist/schemas/manifest-mcp.d.ts +125 -0
  201. package/dist/schemas/manifest-mcp.d.ts.map +1 -0
  202. package/dist/schemas/manifest-mcp.js +61 -0
  203. package/dist/schemas/manifest-mcp.js.map +1 -0
  204. package/dist/schemas/marketplace.d.ts +708 -0
  205. package/dist/schemas/marketplace.d.ts.map +1 -0
  206. package/dist/schemas/marketplace.js +387 -0
  207. package/dist/schemas/marketplace.js.map +1 -0
  208. package/dist/schemas/notifications.d.ts +61 -0
  209. package/dist/schemas/notifications.d.ts.map +1 -0
  210. package/dist/schemas/notifications.js +38 -0
  211. package/dist/schemas/notifications.js.map +1 -0
  212. package/dist/schemas/observability.d.ts +114 -0
  213. package/dist/schemas/observability.d.ts.map +1 -0
  214. package/dist/schemas/observability.js +74 -0
  215. package/dist/schemas/observability.js.map +1 -0
  216. package/dist/schemas/policy.d.ts +58 -0
  217. package/dist/schemas/policy.d.ts.map +1 -0
  218. package/dist/schemas/policy.js +32 -0
  219. package/dist/schemas/policy.js.map +1 -0
  220. package/dist/schemas/presets.d.ts +258 -0
  221. package/dist/schemas/presets.d.ts.map +1 -0
  222. package/dist/schemas/presets.js +95 -0
  223. package/dist/schemas/presets.js.map +1 -0
  224. package/dist/schemas/profiles.d.ts +180 -0
  225. package/dist/schemas/profiles.d.ts.map +1 -0
  226. package/dist/schemas/profiles.js +100 -0
  227. package/dist/schemas/profiles.js.map +1 -0
  228. package/dist/schemas/roles.d.ts +125 -0
  229. package/dist/schemas/roles.d.ts.map +1 -0
  230. package/dist/schemas/roles.js +46 -0
  231. package/dist/schemas/roles.js.map +1 -0
  232. package/dist/schemas/schedules.d.ts +25 -0
  233. package/dist/schemas/schedules.d.ts.map +1 -0
  234. package/dist/schemas/schedules.js +24 -0
  235. package/dist/schemas/schedules.js.map +1 -0
  236. package/dist/schemas/served.d.ts +626 -0
  237. package/dist/schemas/served.d.ts.map +1 -0
  238. package/dist/schemas/served.js +28 -0
  239. package/dist/schemas/served.js.map +1 -0
  240. package/dist/schemas/settings-schema.d.ts +23 -0
  241. package/dist/schemas/settings-schema.d.ts.map +1 -0
  242. package/dist/schemas/settings-schema.js +25 -0
  243. package/dist/schemas/settings-schema.js.map +1 -0
  244. package/dist/schemas/shared.d.ts +20 -0
  245. package/dist/schemas/shared.d.ts.map +1 -0
  246. package/dist/schemas/shared.js +20 -0
  247. package/dist/schemas/shared.js.map +1 -0
  248. package/dist/schemas/states.d.ts +499 -0
  249. package/dist/schemas/states.d.ts.map +1 -0
  250. package/dist/schemas/states.js +220 -0
  251. package/dist/schemas/states.js.map +1 -0
  252. package/dist/schemas/storage.d.ts +37 -0
  253. package/dist/schemas/storage.d.ts.map +1 -0
  254. package/dist/schemas/storage.js +24 -0
  255. package/dist/schemas/storage.js.map +1 -0
  256. package/dist/schemas/studio-plugins.d.ts +30 -0
  257. package/dist/schemas/studio-plugins.d.ts.map +1 -0
  258. package/dist/schemas/studio-plugins.js +20 -0
  259. package/dist/schemas/studio-plugins.js.map +1 -0
  260. package/dist/schemas/sub-mcp.d.ts +29 -0
  261. package/dist/schemas/sub-mcp.d.ts.map +1 -0
  262. package/dist/schemas/sub-mcp.js +22 -0
  263. package/dist/schemas/sub-mcp.js.map +1 -0
  264. package/dist/schemas/system-kinds.d.ts +122 -0
  265. package/dist/schemas/system-kinds.d.ts.map +1 -0
  266. package/dist/schemas/system-kinds.js +40 -0
  267. package/dist/schemas/system-kinds.js.map +1 -0
  268. package/dist/schemas/templates.d.ts +26 -0
  269. package/dist/schemas/templates.d.ts.map +1 -0
  270. package/dist/schemas/templates.js +11 -0
  271. package/dist/schemas/templates.js.map +1 -0
  272. package/dist/schemas/tool-meta.d.ts +64 -0
  273. package/dist/schemas/tool-meta.d.ts.map +1 -0
  274. package/dist/schemas/tool-meta.js +44 -0
  275. package/dist/schemas/tool-meta.js.map +1 -0
  276. package/dist/schemas/tools.d.ts +62 -0
  277. package/dist/schemas/tools.d.ts.map +1 -0
  278. package/dist/schemas/tools.js +53 -0
  279. package/dist/schemas/tools.js.map +1 -0
  280. package/dist/schemas/trigger-links.d.ts +71 -0
  281. package/dist/schemas/trigger-links.d.ts.map +1 -0
  282. package/dist/schemas/trigger-links.js +46 -0
  283. package/dist/schemas/trigger-links.js.map +1 -0
  284. package/dist/schemas/web-entry.d.ts +45 -0
  285. package/dist/schemas/web-entry.d.ts.map +1 -0
  286. package/dist/schemas/web-entry.js +36 -0
  287. package/dist/schemas/web-entry.js.map +1 -0
  288. package/dist/tool-runs.d.ts +4 -4
  289. package/package.json +1 -1
  290. package/src/client/agents-client.ts +23 -0
  291. package/src/client/auth-client.ts +144 -0
  292. package/src/client/backend-client.ts +25 -0
  293. package/src/client/backup-client.ts +16 -0
  294. package/src/client/capabilities-client.ts +22 -0
  295. package/src/client/channels-client.ts +11 -0
  296. package/src/client/config-client.ts +21 -0
  297. package/src/client/connectors-client.ts +52 -0
  298. package/src/client/conversations-client.ts +240 -0
  299. package/src/client/hooks-client.ts +44 -0
  300. package/src/client/index.ts +122 -0
  301. package/src/client/interactions-client.ts +89 -0
  302. package/src/client/login-client.ts +109 -0
  303. package/src/client/manifest-client.ts +118 -0
  304. package/src/client/marketplace-client.ts +122 -0
  305. package/src/client/mcp-config-schema-client.ts +11 -0
  306. package/src/client/notifications-client.ts +13 -0
  307. package/src/client/observability-client.ts +82 -0
  308. package/src/client/plugins-client.ts +11 -0
  309. package/src/client/policy-client.ts +41 -0
  310. package/src/client/presets-client.ts +124 -0
  311. package/src/client/profiles-client.ts +51 -0
  312. package/src/client/schedules-client.ts +22 -0
  313. package/src/client/settings-schema-client.ts +11 -0
  314. package/src/client/states-client.ts +292 -0
  315. package/src/client/storage-client.ts +63 -0
  316. package/src/client/sub-mcp-client.ts +17 -0
  317. package/src/client/system-client.ts +16 -0
  318. package/src/client/templates-client.ts +28 -0
  319. package/src/client/tool-extensions-client.ts +24 -0
  320. package/src/client/tool-meta-client.ts +65 -0
  321. package/src/client/tools-client.ts +66 -0
  322. package/src/client/transport.ts +18 -0
  323. package/src/client/trigger-links-client.ts +51 -0
  324. package/src/client/web-entry-gate-client.ts +45 -0
  325. package/src/http.ts +48 -24
  326. package/src/schemas/api-keys.ts +26 -0
  327. package/src/schemas/auth-routes.ts +48 -0
  328. package/src/schemas/backup.ts +42 -0
  329. package/src/schemas/capabilities.ts +87 -0
  330. package/src/schemas/channels.ts +11 -0
  331. package/src/schemas/config.ts +17 -0
  332. package/src/schemas/connectors.ts +138 -0
  333. package/src/schemas/conversations.ts +256 -0
  334. package/src/schemas/extensions.ts +34 -0
  335. package/src/schemas/fleet.ts +153 -0
  336. package/src/schemas/hooks.ts +56 -0
  337. package/src/schemas/index.ts +37 -0
  338. package/src/schemas/interactions.ts +146 -0
  339. package/src/schemas/login.ts +62 -0
  340. package/src/schemas/manifest-mcp.ts +69 -0
  341. package/src/schemas/marketplace.ts +447 -0
  342. package/src/schemas/notifications.ts +42 -0
  343. package/src/schemas/observability.ts +86 -0
  344. package/src/schemas/policy.ts +37 -0
  345. package/src/schemas/presets.ts +113 -0
  346. package/src/schemas/profiles.ts +119 -0
  347. package/src/schemas/roles.ts +57 -0
  348. package/src/schemas/schedules.ts +27 -0
  349. package/src/schemas/served.ts +49 -0
  350. package/src/schemas/settings-schema.ts +31 -0
  351. package/src/schemas/shared.ts +21 -0
  352. package/src/schemas/states.ts +272 -0
  353. package/src/schemas/storage.ts +31 -0
  354. package/src/schemas/studio-plugins.ts +21 -0
  355. package/src/schemas/sub-mcp.ts +24 -0
  356. package/src/schemas/system-kinds.ts +46 -0
  357. package/src/schemas/templates.ts +11 -0
  358. package/src/schemas/tool-meta.ts +51 -0
  359. package/src/schemas/tools.ts +61 -0
  360. package/src/schemas/trigger-links.ts +54 -0
  361. package/src/schemas/web-entry.ts +46 -0
  362. package/dist/client.d.ts.map +0 -1
  363. package/dist/client.js +0 -920
  364. package/dist/client.js.map +0 -1
  365. package/dist/schemas.d.ts +0 -5172
  366. package/dist/schemas.d.ts.map +0 -1
  367. package/dist/schemas.js +0 -2268
  368. package/dist/schemas.js.map +0 -1
  369. package/src/client.ts +0 -1771
  370. package/src/schemas.ts +0 -2760
package/dist/client.js DELETED
@@ -1,920 +0,0 @@
1
- /**
2
- * The typed client: one method per endpoint, each validating
3
- * its response against the matching zod schema. Server state flows through
4
- * TanStack Query in the features; this client is a plain typed transport with no
5
- * caching of its own.
6
- */
7
- import { apiDownload, apiRequest, apiText, encodeSegment, extractError, isUnsafePathSegment, } from './http';
8
- import { ApiError, ApiLoginFailedError, ApiUnauthorizedError } from './errors';
9
- import { readSseFrames, sseOpenToken } from './sse';
10
- import { agentList, streamAgentRun, streamAuthoredAgentRun } from './agents';
11
- import { getToolRun, listToolRuns, submitToolRun } from './tool-runs';
12
- import * as s from './schemas';
13
- /**
14
- * Encode a possibly-nested storage id / directory path for use in a URL path,
15
- * percent-encoding each segment while preserving the `/` separators. A storage id
16
- * can be a nested path (e.g. `a/b c.txt`); encoding each segment independently keeps
17
- * the slashes structural and escapes everything else (spaces, `#`, `?`, …).
18
- *
19
- * Dot segments (`.`, `..`), empty segments (a leading/trailing/doubled `/`, or an
20
- * empty id) are REJECTED loudly rather than encoded: the browser's WHATWG URL parser
21
- * removes dot-segments from a relative request path before it is sent — collapsing
22
- * `..` even when percent-encoded — so such an id can never be faithfully transmitted.
23
- * Left to encode, it would silently retarget the request at an unrelated same-origin
24
- * route. The server enforces the same relative-path rule; failing fast here keeps a
25
- * malformed id from ever leaving the client.
26
- */
27
- function encodePath(id) {
28
- const segments = id.split('/');
29
- if (segments.some(isUnsafePathSegment)) {
30
- throw new Error("storage path must be a relative path with no empty, '.', or '..' segment");
31
- }
32
- return segments.map(encodeURIComponent).join('/');
33
- }
34
- /** Flatten a `RunsQuery` to transport params, JSON-encoding the `tags` list. */
35
- function runsQueryToParams(query) {
36
- return {
37
- from: query.from,
38
- to: query.to,
39
- tags: query.tags === undefined ? undefined : JSON.stringify(query.tags),
40
- status: query.status,
41
- minCost: query.minCost,
42
- maxCost: query.maxCost,
43
- minTokens: query.minTokens,
44
- maxTokens: query.maxTokens,
45
- minLatencyMs: query.minLatencyMs,
46
- maxLatencyMs: query.maxLatencyMs,
47
- sort: query.sort,
48
- dir: query.dir,
49
- page: query.page,
50
- pageSize: query.pageSize,
51
- };
52
- }
53
- /**
54
- * Whether a login-method target is a safe same-origin relative API path. The
55
- * login screen is the one page where a misconfigured/compromised manifest could
56
- * redirect CREDENTIALS off-origin, so a `submit_path`/`href` MUST start `/api/`
57
- * — anything else (absolute URL, protocol-relative `//host`, missing prefix) is
58
- * rejected. The `/api/` prefix already excludes `//`-prefixed values; the
59
- * explicit double-slash clause is kept for readability.
60
- */
61
- export function isSafeApiPath(path) {
62
- return path.startsWith('/api/') && !path.startsWith('//');
63
- }
64
- /**
65
- * Map a transport error from a login submission onto an `ApiLoginFailedError`
66
- * (rendered inline on the login page) or rethrow it unchanged (5xx, schema
67
- * drift, network — the page's generic failure branch surfaces it loudly).
68
- *
69
- * A transport 401 arrives as `ApiUnauthorizedError` before any body is read, so
70
- * its message is generic by construction; the domain 4xx statuses carry the
71
- * server's informative envelope text (rate-limit, bootstrap-already-initialized,
72
- * password-policy, or the claim exchange's uniform "unknown or already used"
73
- * 404) straight through to the inline surface.
74
- */
75
- function asLoginError(error) {
76
- if (error instanceof ApiUnauthorizedError) {
77
- throw new ApiLoginFailedError('Sign-in failed — check your credentials.', 401);
78
- }
79
- if (error instanceof ApiError &&
80
- (error.status === 400 ||
81
- error.status === 403 ||
82
- error.status === 404 ||
83
- error.status === 409 ||
84
- error.status === 422 ||
85
- error.status === 429)) {
86
- throw new ApiLoginFailedError(error.message, error.status);
87
- }
88
- throw error;
89
- }
90
- /**
91
- * The subject-addressed record path: `/api/states/{name}/records/{target_kind}/
92
- * {target_name}/{kind}/{key}` — four path segments, each percent-encoded, so a key
93
- * carrying a slash or a colon never escapes its segment.
94
- */
95
- function stateRecordPath(name, subject) {
96
- return (`/api/states/${encodeSegment(name)}/records/` +
97
- `${encodeSegment(subject.target_kind)}/${encodeSegment(subject.target_name)}/` +
98
- `${encodeSegment(subject.kind)}/${encodeSegment(subject.key)}`);
99
- }
100
- /**
101
- * Encode a template jq's declared params as JSON query values: the record `template-jq`
102
- * route reads each `?<param>=<json>` as a JSON-decoded argument, so a string, number,
103
- * array or object round-trips faithfully. An `undefined` value is dropped (an
104
- * omitted optional param).
105
- */
106
- function encodeJqParams(params) {
107
- if (params === undefined)
108
- return undefined;
109
- const query = {};
110
- for (const [key, value] of Object.entries(params)) {
111
- if (value === undefined)
112
- continue;
113
- query[key] = JSON.stringify(value);
114
- }
115
- return query;
116
- }
117
- export function createApiClient(config) {
118
- const req = (path, schema, options) => apiRequest(config, path, schema, options);
119
- return {
120
- // The configured API base — the exact `baseUrl` this client was constructed
121
- // with (`''` for a same-origin deployment). Read-only: features that must
122
- // compose an ABSOLUTE API URL (a QR-encoded public path the API origin must
123
- // receive, not the SPA origin) read it instead of assuming same-origin. The
124
- // transport itself still joins it per request; this only surfaces the value.
125
- baseUrl: config.baseUrl ?? '',
126
- // -- tools ---------------------------------------------------------------
127
- listTools: (signal) => req('/api/tools', s.toolNames, { signal }),
128
- getToolSchema: (name, signal) => req(`/api/tools/${encodeSegment(name)}/schema`, s.toolSchema, { signal }),
129
- getAllToolSchemas: (signal) => req('/api/tools-schema', s.allToolSchemas, { signal }),
130
- runTool: (args, signal) => req('/api/run-tool', s.runToolResult, {
131
- method: 'POST',
132
- // The backend's `/api/run-tool` door reads `{tool_name, arguments}` (the
133
- // shape `read_tool_call` enforces and the background `/api/tool-runs`
134
- // submit already sends). The SPA keeps `tool`/`kwargs` as its ergonomic
135
- // arg names; map them onto the wire fields here.
136
- body: { tool_name: args.tool, arguments: args.kwargs ?? {} },
137
- signal,
138
- }),
139
- // -- app tool live-registry admin ----------------------------------------
140
- // Re-register or remove one app tool by kind+name, applied on this worker and
141
- // broadcast to the fleet (all workers, or only `targets`); both return the bare
142
- // per-worker `fleetResult` the shared fleet-report handler renders. Both are
143
- // fenced + destructive server-side, so a refusal surfaces as a loud error.
144
- reloadTool: (args) => req('/api/tools/reload', s.fleetResult, {
145
- method: 'POST',
146
- body: { kind: args.kind, name: args.name, targets: args.targets ?? null },
147
- }),
148
- removeTool: (args) => req('/api/tools/remove', s.fleetResult, {
149
- method: 'POST',
150
- body: { kind: args.kind, name: args.name, targets: args.targets ?? null },
151
- }),
152
- // -- background tool runs ------------------------------------------------
153
- submitToolRun: (args, signal) => submitToolRun(config, args, signal),
154
- getToolRun: (runId, signal) => getToolRun(config, runId, signal),
155
- listToolRuns: (toolName, signal) => listToolRuns(config, toolName, signal),
156
- // -- tool tags (additive map feeding the ToolPicker tag grouping) --------
157
- listToolTags: (signal) => req('/api/tools/tags', s.toolTags, { signal }),
158
- // -- presets -------------------------------------------------------------
159
- // Every preset is store-backed and versioned; the list is the single
160
- // management population (the presets plus the conflicted quarantined rows).
161
- listPresets: (signal) => req('/api/presets', s.presetList, { signal }),
162
- createPreset: (body) => req('/api/presets', s.presetRecord, { method: 'POST', body }),
163
- getPreset: (name, signal) => req(`/api/presets/${encodeSegment(name)}`, s.presetDetail, { signal }),
164
- listPresetVersions: (name, signal) => req(`/api/presets/${encodeSegment(name)}/versions`, s.presetVersionList, { signal }),
165
- getPresetVersion: (name, version, signal) => req(`/api/presets/${encodeSegment(name)}/versions/${encodeSegment(version)}`, s.presetVersion, { signal }),
166
- savePresetVersion: (name, body) => req(`/api/presets/${encodeSegment(name)}/versions`, s.presetVersion, {
167
- method: 'POST',
168
- body,
169
- }),
170
- rollbackPreset: (name, version) => req(`/api/presets/${encodeSegment(name)}/rollback`, s.presetRollback, {
171
- method: 'POST',
172
- body: { version },
173
- }),
174
- deletePreset: (name) => req(`/api/presets/${encodeSegment(name)}`, s.presetDeleted, { method: 'DELETE' }),
175
- renamePreset: (name, newName) => req(`/api/presets/${encodeSegment(name)}/rename`, s.presetRenamed, {
176
- method: 'POST',
177
- body: { new_name: newName },
178
- }),
179
- // The presets whose active composition names this one as a tool — the referees
180
- // a rename would strand. A rename preflight reads this to warn before the 409.
181
- getPresetReferees: (name, signal) => req(`/api/presets/${encodeSegment(name)}/referees`, s.presetReferees, { signal }),
182
- // The dry-run verdict for a draft (create OR version, mode-resolved server-side):
183
- // `valid` plus the server's verbatim `error` message. Both verdicts are HTTP 200.
184
- validatePreset: (body) => req('/api/presets/validate', s.presetValidation, { method: 'POST', body }),
185
- // Replace one version's `tags` annotation — labels on an immutable body, so no
186
- // rebind. Returns the re-annotated identity + new tag list.
187
- setPresetVersionTags: (name, version, tags) => req(`/api/presets/${encodeSegment(name)}/versions/${encodeSegment(version)}/tags`, s.presetVersionTags, { method: 'PUT', body: { tags } }),
188
- // -- states --------------------------------------------------------------
189
- // A state is a declared JSON document, one per subject. The list is the
190
- // management population; a single state read composes the declaration with its
191
- // effective schema, regimes and attachments. Every record route addresses its subject
192
- // as four percent-encoded path segments (see `stateRecordPath`).
193
- listStates: (signal) => req('/api/states', s.stateList, { signal }),
194
- getState: (name, signal) => req(`/api/states/${encodeSegment(name)}`, s.stateDetail, { signal }),
195
- // A plain declaration upsert — no `replace` flag. With records present the server
196
- // accepts additive schema changes and refuses with a 409 any change that removes or
197
- // alters an existing field, or a subject-kind removal that would strand records.
198
- putState: (name, body) => req(`/api/states/${encodeSegment(name)}`, s.stateDetail, {
199
- method: 'PUT',
200
- body,
201
- }),
202
- deleteState: (name) => req(`/api/states/${encodeSegment(name)}`, s.stateDeleted, { method: 'DELETE' }),
203
- getStateStats: (name, signal) => req(`/api/states/${encodeSegment(name)}/stats`, s.stateStats, { signal }),
204
- // -- state template attachments ------------------------------------------
205
- listStateAttachments: (name, signal) => req(`/api/states/${encodeSegment(name)}/attachments`, s.stateAttachmentList, { signal }),
206
- getStateAttachment: (name, template, signal) => req(`/api/states/${encodeSegment(name)}/attachments/${encodeSegment(template)}`, s.stateAttachment, { signal }),
207
- attachStateTemplate: (name, template, body) => req(`/api/states/${encodeSegment(name)}/attachments/${encodeSegment(template)}`, s.stateAttached, { method: 'PUT', body }),
208
- patchStateAttachment: (name, template, body) => req(`/api/states/${encodeSegment(name)}/attachments/${encodeSegment(template)}`, s.stateAttachmentUpdated, { method: 'PATCH', body }),
209
- detachStateTemplate: (name, template) => req(`/api/states/${encodeSegment(name)}/attachments/${encodeSegment(template)}`, s.stateDetached, { method: 'DELETE' }),
210
- // -- state subjects + records -------------------------------------------
211
- listStateSubjects: (name, params, signal) => req(`/api/states/${encodeSegment(name)}/subjects`, s.subjectPage, {
212
- signal,
213
- query: { kind: params.kind, limit: params.limit, cursor: params.cursor },
214
- }),
215
- // The content search body is a single JSONB containment document (`filters`); the
216
- // server refuses an empty one, so the UI validates a non-empty object first.
217
- searchStateRecords: (name, body, signal) => req(`/api/states/${encodeSegment(name)}/records/search`, s.recordSearchPage, {
218
- method: 'POST',
219
- body,
220
- signal,
221
- }),
222
- // A subject with no document yet reads as `null` (a 200 `{data: null}`, the record
223
- // page's Create path) — never an error, so the schema is nullable.
224
- getStateRecord: (name, subject, signal) => req(stateRecordPath(name, subject), s.stateRecord.nullable(), { signal }),
225
- putStateRecord: (name, subject, data) => req(stateRecordPath(name, subject), s.stateRecord, { method: 'PUT', body: data }),
226
- patchStateRecord: (name, subject, data) => req(stateRecordPath(name, subject), s.stateRecord, { method: 'PATCH', body: data }),
227
- applyStateRecord: (name, subject, ops) => req(`${stateRecordPath(name, subject)}/deltas`, s.applyResult, {
228
- method: 'POST',
229
- body: { ops },
230
- }),
231
- // Evaluate a template jq over the subject: each declared param is JSON-encoded into
232
- // the query string (`?<param>=<json>`); the response is `{name, purpose, value}`. An
233
- // `attachment.name` jq is percent-encoded as one path segment.
234
- evalTemplateJq: (name, subject, jqName, params, signal) => req(`${stateRecordPath(name, subject)}/template-jq/${encodeSegment(jqName)}`, s.templateJqResult, { signal, query: encodeJqParams(params) }),
235
- // Apply an `update`-purpose template jq to the subject: `input` is the jq's argument,
236
- // `op_id` its idempotency key (a replay is a no-op). The jq lands its op batch through
237
- // the apply chokepoint. An `attachment.name` jq is percent-encoded as one path segment.
238
- applyTemplateJq: (name, subject, jqName, body) => req(`${stateRecordPath(name, subject)}/template-jq/${encodeSegment(jqName)}`, s.templateJqApplyResult, { method: 'POST', body: body ?? {} }),
239
- deleteStateRecord: (name, subject) => req(stateRecordPath(name, subject), s.recordErased, { method: 'DELETE' }),
240
- // `mode` is required server-side (`switch` drops, `merge` combines); the fold returns
241
- // a report, not the surviving record.
242
- foldStateRecord: (name, subject, into, mode) => req(`${stateRecordPath(name, subject)}/fold`, s.stateFoldReport, {
243
- method: 'POST',
244
- body: { into, mode },
245
- }),
246
- listStateWrites: (name, subject, params, signal) => req(`${stateRecordPath(name, subject)}/writes`, s.writesPage, {
247
- signal,
248
- query: { limit: params?.limit, cursor: params?.cursor },
249
- }),
250
- stateConsumers: (name, signal) => req(`/api/states/${encodeSegment(name)}/consumers`, s.stateConsumers, { signal }),
251
- // -- state-template documents (top-level sibling collection) ---------------
252
- listStateTemplates: (signal) => req('/api/state-templates', s.stateTemplateList, { signal }),
253
- getStateTemplate: (name, signal) => req(`/api/state-templates/${encodeSegment(name)}`, s.stateTemplateDocument, { signal }),
254
- putStateTemplate: (name, body, replace) => req(`/api/state-templates/${encodeSegment(name)}`, s.stateTemplateDocument, {
255
- method: 'PUT',
256
- body,
257
- query: replace === true ? { replace: 'true' } : undefined,
258
- }),
259
- deleteStateTemplate: (name) => req(`/api/state-templates/${encodeSegment(name)}`, s.stateTemplateDeleted, {
260
- method: 'DELETE',
261
- }),
262
- // -- state retention -----------------------------------------------------
263
- pruneStateRetention: () => req('/api/state-retention/prune', s.stateRetentionPruned, { method: 'POST', body: {} }),
264
- // -- tool_meta (the organizational overlay) ------------------------------
265
- // The folder tree + per-tool overlay rows in one read; the features merge the
266
- // rows against the live tool list and native tags client-side.
267
- listToolMeta: (signal) => req('/api/tool-meta', s.toolMetaOverlay, { signal }),
268
- // Merge-patch a tool's overlay row (create it if absent). `patch` carries ONLY
269
- // the fields to change — a present-null clears, an absent field is untouched,
270
- // and a present `tags` array replaces the set.
271
- upsertToolMeta: (toolName, patch) => req(`/api/tool-meta/tools/${encodeSegment(toolName)}`, s.toolMetaRecord, {
272
- method: 'PATCH',
273
- body: patch,
274
- }),
275
- deleteToolMeta: (toolName) => req(`/api/tool-meta/tools/${encodeSegment(toolName)}`, s.toolMetaDeleted, {
276
- method: 'DELETE',
277
- }),
278
- createFolder: (name, parentId = null) => req('/api/tool-meta/folders', s.folderRecord, {
279
- method: 'POST',
280
- body: { name, parent_id: parentId },
281
- }),
282
- renameFolder: (folderId, name) => req(`/api/tool-meta/folders/${encodeSegment(folderId)}/rename`, s.folderRecord, {
283
- method: 'POST',
284
- body: { name },
285
- }),
286
- // `null` re-parents to the root.
287
- moveFolder: (folderId, parentId) => req(`/api/tool-meta/folders/${encodeSegment(folderId)}/move`, s.folderRecord, {
288
- method: 'POST',
289
- body: { parent_id: parentId },
290
- }),
291
- deleteFolder: (folderId) => req(`/api/tool-meta/folders/${encodeSegment(folderId)}`, s.folderDeleted, {
292
- method: 'DELETE',
293
- }),
294
- // -- storage -------------------------------------------------------------
295
- // The content store a storage-provider plugin exposes. Dead by default:
296
- // `getStorageInfo` reports `present: false` (a 200 empty state) and every other
297
- // door answers a loud 501 when no provider is installed. Ids may be nested
298
- // paths, so each is `encodePath`-encoded (per-segment, `/` preserved).
299
- getStorageInfo: (signal) => req('/api/storage', s.storageInfo, { signal }),
300
- listStorageResources: (signal) => req('/api/storage/resources', s.storageResourceList, { signal }),
301
- statStorageResource: (id, signal) => req(`/api/storage/resources/${encodePath(id)}/stat`, s.storageResourceStat, { signal }),
302
- downloadStorageResource: (id, signal) => apiDownload(config, `/api/storage/resources/${encodePath(id)}/content`, { signal }),
303
- uploadStorageResource: (body) => req('/api/storage/resources', s.storageResourceStored, { method: 'POST', body }),
304
- deleteStorageResource: (id) => req(`/api/storage/resources/${encodePath(id)}`, s.storageResourceDeleted, {
305
- method: 'DELETE',
306
- }),
307
- deleteStorageDir: (path) => req(`/api/storage/dirs/${encodePath(path)}`, s.storageDirDeleted, { method: 'DELETE' }),
308
- // -- backend identity + fleet (worker bus) -------------------------------
309
- // Backend IDENTITY (`getBackendInfo`, kept on `/api/backend`) is distinct from
310
- // the FLEET doors, which ride the app's worker bus and need no backend at all.
311
- // `getBackendInfo` reports `present: false` (a 200 empty state) when no backend
312
- // plugin is registered. `listFleetWorkers` reads the live bus presence census
313
- // (every subscribed worker, ASGI + backend-runtime) and never fabricates an empty
314
- // fleet — a census read that fails surfaces as a loud 500.
315
- getBackendInfo: (signal) => req('/api/backend', s.backendInfo, { signal }),
316
- listFleetWorkers: (signal) => req('/api/fleet/workers', s.fleetWorkers, { signal }),
317
- // Soft-restart the fleet: `targets` names the workers to reload, or `null` for
318
- // the whole fleet. The serving worker applies locally then broadcasts; a 200
319
- // response carries the per-worker fleet report, so workers that did not converge
320
- // are named, not hidden. A failed local apply instead re-raises as a loud error
321
- // (the transport throws with the failure message), not a returned report.
322
- reloadFleetConfig: (targets) => req('/api/fleet/reload-config', s.fleetReloadResult, { method: 'POST', body: { targets } }),
323
- // -- extensions ----------------------------------------------------------
324
- listExtensions: (signal) => req('/api/extensions', s.extensions, { signal }),
325
- // A MANIFEST-provided tool's current combos + the extension catalog. (A preset
326
- // tool's extensions live on the preset spec, read via `getPreset`.)
327
- getToolExtensions: (name, signal) => req(`/api/tools/${encodeSegment(name)}/extensions`, s.toolExtensions, { signal }),
328
- // Author ALL of a MANIFEST tool's combos at once (the full lossless list; `[]`
329
- // clears). Returns the apply result: the local reload (`status` + `env_keys`) plus
330
- // the mode-wrapped fleet `fanout` of the reload it broadcast, from which the shared
331
- // fleet-report handler surfaces any failed propagation.
332
- setToolExtensions: (name, combos) => req(`/api/tools/${encodeSegment(name)}/extensions`, s.toolExtensionsApplyResult, {
333
- method: 'POST',
334
- body: { combos },
335
- }),
336
- // -- templates -----------------------------------------------------------
337
- listTemplates: (signal) => req('/api/templates', s.templateNames, { signal }),
338
- getTemplate: (templateId) => req('/api/template', s.templateDetail, { method: 'POST', body: { template_id: templateId } }),
339
- uploadTemplate: (path, content) => req('/api/upload-template', s.templateUploaded, { method: 'POST', body: { path, content } }),
340
- deleteTemplate: (path) => req('/api/delete-template', s.templateDeleted, { method: 'POST', body: { path } }),
341
- // Delete every stored template under a directory prefix. Unlike the idempotent
342
- // single delete, the server 404s a prefix matching nothing and 400s the template
343
- // root; those surface as loud errors, never a faked success.
344
- deleteTemplateDir: (path) => req('/api/delete-template-dir', s.templateDirDeleted, { method: 'POST', body: { path } }),
345
- // Render one authored text — inline `content` OR a stored template `id`, plus its
346
- // render `kwargs` — and parse the rendered output. The value is the shared
347
- // `TemplatedText` (exactly one source); the route wraps it under `text`.
348
- renderTemplate: (text) => req('/api/render-template', s.templateRendered, { method: 'POST', body: { text } }),
349
- clearTemplatesCache: () => req('/api/clear-templates-cache', s.templateCacheCleared, { method: 'POST' }),
350
- // -- manifest / mcp ------------------------------------------------------
351
- getManifest: (signal) => req('/api/manifest', s.manifestView, { signal }),
352
- setMcpConfig: (mcp) => req('/api/mcp-config', s.reloadConfigResult, { method: 'POST', body: { mcp } }),
353
- getMcpStatus: (signal) => req('/api/mcp-status', s.mcpStatus, { signal }),
354
- reloadMcp: (title) => req(`/api/mcp-status/${encodeSegment(title)}/reload`, s.mcpReloadResult, {
355
- method: 'POST',
356
- }),
357
- // Granular manifest-section entry edits — add/replace or remove ONE entry by
358
- // title, leaving the rest of the section untouched (the surgical counterpart to
359
- // the whole-section `setMcpConfig`). Each crosses the config pipeline and returns
360
- // the shared apply-result (`status` + `env_keys` + fleet `fanout`). `replace` lets
361
- // an entry whose title already exists swap in; without it a collision is refused.
362
- addToolsEntries: (entries, replace = false) => req('/api/tools-config/entries', s.reloadConfigResult, {
363
- method: 'POST',
364
- body: { entries, replace },
365
- }),
366
- removeToolsEntry: (title) => req(`/api/tools-config/entries/${encodeSegment(title)}`, s.reloadConfigResult, {
367
- method: 'DELETE',
368
- }),
369
- addAgentsEntries: (entries, replace = false) => req('/api/agents-config/entries', s.reloadConfigResult, {
370
- method: 'POST',
371
- body: { entries, replace },
372
- }),
373
- removeAgentsEntry: (title) => req(`/api/agents-config/entries/${encodeSegment(title)}`, s.reloadConfigResult, {
374
- method: 'DELETE',
375
- }),
376
- // Edit the manifest `api_tools` include/exclude name lists by delta (add/remove
377
- // names on either list). Absent fields default to empty; the server refuses a body
378
- // whose four lists are all empty. Returns the shared apply-result.
379
- updateApiTools: (body) => req('/api/api-tools', s.reloadConfigResult, {
380
- method: 'POST',
381
- body: {
382
- include_add: body.include_add ?? [],
383
- include_remove: body.include_remove ?? [],
384
- exclude_add: body.exclude_add ?? [],
385
- exclude_remove: body.exclude_remove ?? [],
386
- },
387
- }),
388
- // -- failed-MCP health ---------------------------------------------------
389
- // The MCP servers skipped by the viability check, plus their remediation ops. The
390
- // LIST rides the fleet fan-out primitive (each worker's failed roster arrives as
391
- // its own report payload; `failedMcpsFromReport` unions them). Reload-all re-probes
392
- // every failed server; deregister detaches one server's tools by title. Each op
393
- // returns the bare per-worker `fleetResult`.
394
- listFailedMcps: (signal) => req('/api/mcp-status/failed', s.fleetResult, { signal }),
395
- reloadFailedMcps: () => req('/api/mcp-status/reload-failed', s.fleetResult, {
396
- method: 'POST',
397
- body: { targets: null },
398
- }),
399
- deregisterMcp: (title) => req(`/api/mcp-status/${encodeSegment(title)}/deregister`, s.fleetResult, {
400
- method: 'POST',
401
- body: { targets: null },
402
- }),
403
- // -- sub-mcp -------------------------------------------------------------
404
- listSubMcp: (signal) => req('/api/sub-mcp', s.subMcpList, { signal }),
405
- // `transport` is optional at the client boundary (the server defaults it to
406
- // `http`); when omitted, JSON.stringify drops the undefined key from the body.
407
- createSubMcp: (slug, tools, transport) => req('/api/sub-mcp', s.subMcpCreated, { method: 'POST', body: { slug, tools, transport } }),
408
- deleteSubMcp: (slug) => req(`/api/sub-mcp/${encodeSegment(slug)}`, s.subMcpRemoved, { method: 'DELETE' }),
409
- // -- config --------------------------------------------------------------
410
- getEnvConfig: (signal) => req('/api/config/env', s.envConfig, { signal }),
411
- setEnvConfig: (env) => req('/api/config/env', s.reloadConfigResult, { method: 'POST', body: env }),
412
- getConfigMode: (signal) => req('/api/config/mode', s.configMode, { signal }),
413
- // The LOCAL soft-restart door (distinct from the System page's fleet door
414
- // `/api/fleet/reload-config`): refresh env from the config manager, reset the
415
- // settings caches, and re-initialize from the manifest — applied on the serving
416
- // worker and broadcast to the fleet (all workers, or only `targets`; `null` = all).
417
- // Admin-`fenced`; the 200 body is the bare per-worker fleet report so an
418
- // unconverged sibling is visible, never hidden.
419
- reloadConfig: (targets = null) => req('/api/config/reload', s.fleetReloadResult, { method: 'POST', body: { targets } }),
420
- // -- settings profiles ---------------------------------------------------
421
- // The admin-only `/api/config/profiles/*` surface. Bodies/diff/versions carry
422
- // REAL secret values (secret/fenced routes); masking is CLIENT-SIDE.
423
- listSettingsProfiles: (signal) => req('/api/config/profiles', s.settingsProfileList, { signal }),
424
- getSettingsProfile: (name, signal) => req(`/api/config/profiles/${encodeSegment(name)}`, s.settingsProfileBody, { signal }),
425
- putSettingsProfile: (name, body) => req(`/api/config/profiles/${encodeSegment(name)}`, s.settingsProfileSaved, {
426
- method: 'PUT',
427
- body,
428
- }),
429
- deleteSettingsProfile: (name) => req(`/api/config/profiles/${encodeSegment(name)}`, s.settingsProfileDeleted, {
430
- method: 'DELETE',
431
- }),
432
- // Diff of the SAVED profile vs the stored env — server-computed, no request body.
433
- diffSettingsProfile: (name) => req(`/api/config/profiles/${encodeSegment(name)}/diff`, s.settingsProfileDiff, {
434
- method: 'POST',
435
- }),
436
- // Apply the SAVED profile (full-replace of the profile-managed band). Fenced,
437
- // destructive → the dedicated `profile_apply_response` report.
438
- applySettingsProfile: (name) => req(`/api/config/profiles/${encodeSegment(name)}/apply`, s.profileApplyResponse, {
439
- method: 'POST',
440
- }),
441
- listSettingsProfileVersions: (name, signal) => req(`/api/config/profiles/${encodeSegment(name)}/versions`, s.settingsProfileVersionList, {
442
- signal,
443
- }),
444
- getSettingsProfileVersion: (name, version, signal) => req(`/api/config/profiles/${encodeSegment(name)}/versions/${encodeSegment(version)}`, s.settingsProfileVersion, { signal }),
445
- rollbackSettingsProfile: (name, version) => req(`/api/config/profiles/${encodeSegment(name)}/rollback`, s.settingsProfileRollback, {
446
- method: 'POST',
447
- body: { version },
448
- }),
449
- // -- combined env+manifest op + preserved manifest -----------------------
450
- // The SecretRefField server half: one atomic env-value + `!ENV` manifest
451
- // write, returning the shared apply-result shape (reload + fleet fanout).
452
- setMcpSecretEnv: (body) => req('/api/mcp-config/secret-env', s.reloadConfigResult, { method: 'POST', body }),
453
- // The PRESERVED manifest (`!ENV` markers intact, no resolved secrets) — the
454
- // connectors page's McpServersSection reads this so round-trips never inline
455
- // resolved values.
456
- getManifestPreserved: (signal) => req('/api/manifest/preserved', s.manifestView, { signal }),
457
- // The manifest MCP section's `!ENV ${VAR[:default]}` marker refs — NAMES and
458
- // set/unset BOOLEANS only, never values. Drives the McpServersSection env-refs
459
- // checklist on the connectors page.
460
- getMcpEnvRefs: (signal) => req('/api/manifest/mcp-env-refs', s.mcpEnvRefs, { signal }),
461
- // -- connectors ----------------------------------------------------------
462
- listProviders: (signal) => req('/api/connectors/providers', s.providers, { signal }),
463
- listConnections: (signal) => req('/api/connectors/connections', s.connectionsList, { signal }),
464
- getConnection: (id, signal) => req(`/api/connectors/connections/${encodeSegment(id)}`, s.connectionView, { signal }),
465
- startConnect: (args) => req('/api/connectors/connections/start', s.startConnectResult, {
466
- method: 'POST',
467
- body: args,
468
- }),
469
- disconnect: (id) => req(`/api/connectors/connections/${encodeSegment(id)}`, s.disconnectResult, {
470
- method: 'DELETE',
471
- }),
472
- reconnect: (id, enabled_sub_services, return_url) => req(`/api/connectors/connections/${encodeSegment(id)}/reconnect`, s.reconnectResult, {
473
- method: 'POST',
474
- body: { enabled_sub_services, return_url },
475
- }),
476
- patchSubServices: (id, enabled_sub_services, return_url) => req(`/api/connectors/connections/${encodeSegment(id)}/sub-services`, s.patchSubServicesResult, {
477
- method: 'PATCH',
478
- body: { enabled_sub_services, return_url },
479
- }),
480
- completeOAuth: (state, code, error) => req('/api/connectors/oauth/complete', s.oauthCompleteResult, {
481
- method: 'POST',
482
- body: { state, code, error },
483
- }),
484
- // -- studio plugin registry ---------------------------------------------
485
- listStudioPlugins: (signal) => req('/api/plugins', s.studioPluginRegistry, { signal }),
486
- // -- interactions --------------------------------------------------------
487
- // One page of the pending interactions (the tail-only stream's paged base). The
488
- // door filters by audience before paging — the SAME filter the stream applies —
489
- // so `total` is the caller's own count, and every terminal frame it later receives
490
- // refers to a question within that `total`.
491
- listInteractions: (page, pageSize, signal) => req('/api/interactions', s.interactionsPage, { signal, query: { page, pageSize } }),
492
- answerInteraction: (interactionId, answer) => req(`/api/interactions/${encodeSegment(interactionId)}/answer`, s.interactionAnswered, {
493
- method: 'POST',
494
- body: { answer },
495
- }),
496
- // Withdraw a pending ask WITHOUT answering it (a bodyless POST): the question is
497
- // resolved as cancelled and the flow that asked never resumes. Terminal like an
498
- // answer, so the loud failure mappings mirror it — a 409 (already answered, so no
499
- // longer cancellable) surfaces as `ApiConflictError`, a 404 (unknown/already gone)
500
- // and a 403 (not this caller's audience) as `ApiError` — all via the shared
501
- // `apiRequest` status handling.
502
- cancelInteraction: (interactionId) => req(`/api/interactions/${encodeSegment(interactionId)}/cancel`, s.interactionCancelled, {
503
- method: 'POST',
504
- }),
505
- // -- channels --------------------------------------------------------------
506
- // The installed channel-plugin names (delivery media for ask_user questions).
507
- listChannels: (signal) => req('/api/channels', s.channels, { signal }),
508
- // -- conversations ---------------------------------------------------------
509
- // The stored routing rows, each with its `callback_secret` withheld — the
510
- // conversation monitor's route picker.
511
- listConversationRoutes: (signal) => req('/api/conversations', s.conversationRoutes, { signal }),
512
- // Create or REPLACE a route (an UPSERT: the same door is the create and the
513
- // edit path). `route_name` rides the URL path AND the body; the door rejects a
514
- // disagreeing pair, so both come from the one create object. The reply carries
515
- // the freshly-minted `callback_secret` for an `api`-door row, returned ONCE.
516
- createOrReplaceConversationRoute: (route) => req(`/api/conversations/${encodeSegment(route.route_name)}`, s.conversationRouteWritten, {
517
- method: 'POST',
518
- body: route,
519
- }),
520
- // Delete a route by name (destructive + authority-changing server-side): it
521
- // drops the routing row and reclaims the thread indexes it owned.
522
- deleteConversationRoute: (routeName) => req(`/api/conversations/${encodeSegment(routeName)}`, s.conversationRouteDeleted, {
523
- method: 'DELETE',
524
- }),
525
- // One route's threads, newest activity first. Admin-only server-side: a
526
- // listing spans every caller on the route, so a scoped session gets a 403.
527
- listConversationThreads: (routeName, page, pageSize, filters = {}, signal) => req(`/api/conversations/${encodeSegment(routeName)}/threads`, s.conversationThreadsPage, {
528
- signal,
529
- query: { page, pageSize, status: filters.status, address: filters.address },
530
- }),
531
- // One thread's transcript, caller-scoped, read in the asked-for direction.
532
- // The `thread_id` rides the QUERY STRING, never the path: it embeds the api
533
- // door's `{principal}/{end user}` address with the principal already
534
- // percent-encoded, which a path segment would have to encode a second time
535
- // and the server would then decode once too few. A query value is decoded
536
- // exactly once whatever it holds, so it is passed through verbatim.
537
- readConversationTranscript: (query, signal) => req(`/api/conversations/${encodeSegment(query.routeName)}/transcript`, s.conversationTranscriptPage, {
538
- signal,
539
- query: {
540
- thread_id: query.threadId,
541
- page: query.page,
542
- pageSize: query.pageSize,
543
- order: query.order,
544
- q: query.q,
545
- },
546
- }),
547
- // Search every record on a route by text, across threads, newest first. The
548
- // needle rides the QUERY (`q`); paging is the infinite query's own window. The
549
- // page's `truncated` flag says the match set outran the door's scan cap.
550
- searchConversationMessages: (query, signal) => req(`/api/conversations/${encodeSegment(query.routeName)}/messages/search`, s.conversationMessageSearchPage, { signal, query: { q: query.q, page: query.page, pageSize: query.pageSize } }),
551
- // Send a human message into a thread. The `thread_id` rides the BODY (a POST,
552
- // never a path segment) — same encode-once reasoning as the transcript read.
553
- sendConversationThreadMessage: (routeName, body) => req(`/api/conversations/${encodeSegment(routeName)}/thread/messages`, s.conversationThreadMessageSent, { method: 'POST', body }),
554
- // Read a thread's reply mode (`agent`/`manual`) and where it comes from. The
555
- // `thread_id` rides the QUERY value, encoded once, as the transcript read does.
556
- getConversationThreadMode: (routeName, threadId, signal) => req(`/api/conversations/${encodeSegment(routeName)}/thread/mode`, s.conversationThreadModeState, { signal, query: { thread_id: threadId } }),
557
- // Flip a thread's reply mode. The `thread_id` rides the BODY (a PUT).
558
- setConversationThreadMode: (routeName, threadId, mode) => req(`/api/conversations/${encodeSegment(routeName)}/thread/mode`, s.conversationThreadModeState, { method: 'PUT', body: { thread_id: threadId, mode } }),
559
- // -- conversation target configs -------------------------------------------
560
- // The stored per-target presentation configs (each `(target_kind, target_name)`
561
- // pair's multichannel flag + first-contact greeting). Admin config surface.
562
- listConversationConfigs: (signal) => req('/api/conversation-configs', s.conversationConfigs, { signal }),
563
- // One config by its `(target_kind, target_name)` key. 404 when unknown.
564
- getConversationConfig: (targetKind, targetName, signal) => req(`/api/conversation-configs/${encodeSegment(targetKind)}/${encodeSegment(targetName)}`, s.targetConversationConfig, { signal }),
565
- // Create or REPLACE a config (an UPSERT: the same door is the create and the
566
- // edit path). The `(target_kind, target_name)` key rides the URL path; the door
567
- // rejects a body whose key disagrees, so both come from the one config object.
568
- setConversationConfig: (config) => req(`/api/conversation-configs/${encodeSegment(config.target_kind)}/${encodeSegment(config.target_name)}`, s.conversationConfigWritten, { method: 'PUT', body: config }),
569
- // Delete a config by its key. 404 when the key is unknown.
570
- deleteConversationConfig: (targetKind, targetName) => req(`/api/conversation-configs/${encodeSegment(targetKind)}/${encodeSegment(targetName)}`, s.conversationConfigDeleted, { method: 'DELETE' }),
571
- // -- conversation admin ----------------------------------------------------
572
- // Every answer record whose delivery ended `failed`, across every route and
573
- // caller. Admin-only server-side; not a paged window (returns `{items, total}`).
574
- listFailedConversationMessages: (signal) => req('/api/conversations/messages/failed', s.conversationFailedMessages, { signal }),
575
- // Forget ONE thread on a route (destructive): its checkpoint, answer records and
576
- // thread indexes. The `thread_id` rides the QUERY value, encoded once — it embeds
577
- // the api door's percent-encoded `{principal}/{end user}` address, which a path
578
- // segment would encode a second time. `removed` counts the records this call
579
- // cleared; a valid id on its own route always succeeds, never a 404.
580
- deleteConversationThread: (routeName, threadId) => req(`/api/conversations/${encodeSegment(routeName)}/thread`, s.conversationThreadDeleted, {
581
- method: 'DELETE',
582
- query: { thread_id: threadId },
583
- }),
584
- // Erase a LINKED person ENTIRELY (GDPR, wide blast radius): its aggregated thread
585
- // and checkpoint, its person row, and every address→person index mapping, across
586
- // EVERY route it wrote under. The `person_id` is a uuid4 with no percent-encoded
587
- // principal, so it rides the PATH cleanly. `erased` says whether this call removed
588
- // the person row (idempotent — a retry answers `false`, never a 404).
589
- deleteConversationPerson: (personId) => req(`/api/conversations/persons/${encodeSegment(personId)}`, s.conversationPersonDeleted, {
590
- method: 'DELETE',
591
- }),
592
- // -- channel-web entry gate ------------------------------------------------
593
- // A web route's entry gate, keyed by its `our_identity`: read the flag + its
594
- // live codes, flip the flag, mint a code (raw returned ONCE), revoke a code by
595
- // its hash id. The conversation monitor's first route mutations.
596
- getWebEntryGate: (identity, signal) => req(`/api/channels/web/gates/${encodeSegment(identity)}`, s.webEntryGate, { signal }),
597
- setWebEntryGate: (identity, enabled) => req(`/api/channels/web/gates/${encodeSegment(identity)}`, s.webEntryGateState, {
598
- method: 'PUT',
599
- body: { enabled },
600
- }),
601
- // Mint a code. `label`/`expires_at` are sent EXPLICITLY (including `null`). The
602
- // raw `code` rides the reply ONCE (composed into the chat URL client-side).
603
- mintWebEntryCode: (identity, body) => req(`/api/channels/web/gates/${encodeSegment(identity)}/codes`, s.webEntryCodeMinted, {
604
- method: 'POST',
605
- body: { label: body.label, expires_at: body.expires_at },
606
- }),
607
- // Revoke a code by its hash id (immediate + durable). 404 when the id is unknown.
608
- revokeWebEntryCode: (identity, codeId) => req(`/api/channels/web/gates/${encodeSegment(identity)}/codes/${encodeSegment(codeId)}`, s.webEntryCodeRevoked, { method: 'DELETE' }),
609
- // -- notifications ---------------------------------------------------------
610
- // The internal notifications sink, newest-first — the messages the `notify_user`
611
- // operation records with no channel, so they surface only in this inbox.
612
- listNotifications: (signal) => req('/api/notifications', s.notifications, { signal }),
613
- // -- agents --------------------------------------------------------------
614
- listAgents: (signal) => req('/api/agents', agentList, { signal }),
615
- // The authorable subset (agents whose `spec_runnable` is true) — the compose
616
- // UI's base-agent picker. An empty `items` list ⇒ no authoring is possible.
617
- listSpecRunnableAgents: (signal) => req('/api/agents/spec-runnable', agentList, { signal }),
618
- // The streaming run: opens POST /api/agents/{name}/runs and yields parsed
619
- // agent events. The auto-form's built value is the POST body.
620
- streamAgentRun: (name, input, signal) => streamAgentRun(config, name, input, signal),
621
- // The streaming run of an AUTHORED agent (POST /api/agents/authored/{name}/runs).
622
- // The body carries only the non-baked ToolInput fields; the baked spec is
623
- // resolved server-side. Reuses the same SSE parser (never EventSource).
624
- streamAuthoredAgentRun: (name, input, signal) => streamAuthoredAgentRun(config, name, input, signal),
625
- // -- hooks --------------------------------------------------------
626
- listHooks: (topic, signal) => req('/api/hooks', s.hookList, { signal, query: topic === undefined ? undefined : { topic } }),
627
- registerHook: (params) => req('/api/hooks', s.hookRegistered, { method: 'POST', body: params }),
628
- unregisterHook: (name) => req(`/api/hooks/${encodeSegment(name)}`, s.hookRemoved, { method: 'DELETE' }),
629
- // The registered webhook-verifier catalog — the only source the bind picker
630
- // reads (names only; an empty registry is a valid empty list).
631
- listHookVerifiers: (signal) => req('/api/hooks/verifiers', s.hookVerifiers, { signal }),
632
- // Bind a verifier to a topic (locks its otherwise-open public ingress). A PUT
633
- // replaces any existing binding; an unknown verifier name is a loud 400.
634
- setTopicVerifier: (topic, body) => req(`/api/hooks/topics/${encodeSegment(topic)}/verifier`, s.topicVerifierSet, {
635
- method: 'PUT',
636
- body,
637
- }),
638
- // Unbind a topic's verifier (re-opens its public ingress). 404 when no binding.
639
- deleteTopicVerifier: (topic) => req(`/api/hooks/topics/${encodeSegment(topic)}/verifier`, s.topicVerifierRemoved, {
640
- method: 'DELETE',
641
- }),
642
- // -- hooks: trigger links -------------------------------------------------
643
- // Mint a trigger link for a topic. `ttl_seconds` is sent EXPLICITLY
644
- // (including `null` for a permanent link) — the server requires the key present
645
- // and 400s a dropped one; `tool_kwargs` is omitted when undefined. The raw
646
- // `token` rides the response ONCE (composed into the QR URL client-side).
647
- createTriggerLink: (body) => req('/api/hooks/trigger-links', s.triggerLinkCreated, {
648
- method: 'POST',
649
- body: {
650
- topic: body.topic,
651
- name: body.name,
652
- execution_key: body.execution_key,
653
- require_api_key: body.require_api_key,
654
- ttl_seconds: body.ttl_seconds,
655
- tool_kwargs: body.tool_kwargs,
656
- },
657
- }),
658
- // Every live trigger-link record (hash prefix only — never a raw token).
659
- listTriggerLinks: (signal) => req('/api/hooks/trigger-links', s.triggerLinkList, { signal }),
660
- // Revoke a link by name (immediate + durable). 404 when the name is unknown.
661
- deleteTriggerLink: (name) => req(`/api/hooks/trigger-links/${encodeSegment(name)}`, s.triggerLinkDeleted, {
662
- method: 'DELETE',
663
- }),
664
- // -- mcp config schema (connectors McpServersSection form) ---------------
665
- getMcpConfigSchema: (signal) => req('/api/mcp-config/schema', s.jsonSchema, { signal }),
666
- // -- settings schema -----------------------------------------------------
667
- getSettingsSchema: (signal) => req('/api/config/settings-schema', s.settingsSchema, { signal }),
668
- // -- auth: scopes --------------------------------------------------------
669
- listScopes: (signal) => req('/api/auth/scopes', s.authScopes, { signal }),
670
- addUrlToScope: (body) => req('/api/auth/scopes', s.addUrlToScopeResult, { method: 'POST', body }),
671
- removeUrlFromScope: (body) => req('/api/auth/scopes/urls', s.removeUrlFromScopeResult, { method: 'DELETE', body }),
672
- removeScope: (scopeId) => req(`/api/auth/scopes/${encodeSegment(scopeId)}`, s.removeScopeResult, {
673
- method: 'DELETE',
674
- }),
675
- // The app's HTTP route catalog with each route's current mapping — the only
676
- // source of the mapper's unassigned-routes bucket (`mapped: null` entries).
677
- listAuthRoutes: (signal) => req('/api/auth/routes', s.authRoutes, { signal }),
678
- // The urls pinned to the public marker; pinning/unpinning is the dedicated
679
- // public-routes writer, never the scope machinery.
680
- listPublicRoutes: (signal) => req('/api/auth/public-routes', s.publicRoutes, { signal }),
681
- pinRoutePublic: (body) => req('/api/auth/public-routes', s.pinPublicResult, { method: 'POST', body }),
682
- // Unpin a public url. A url that is absent or scope-mapped is a loud 404.
683
- unpinPublicRoute: (url) => req('/api/auth/public-routes', s.unpinPublicResult, { method: 'DELETE', body: { url } }),
684
- // -- auth: roles (editable per-tag RBAC) ---------------------------------
685
- // The seeded + operator-authored roles as full bodies (base-tier ceiling +
686
- // editable per-tag grant map). ADMIN-ONLY — the route is `secret` (a listing
687
- // exposes each role's raw base-tier jq); a non-admin projection never reaches it.
688
- listRoles: (signal) => req('/api/auth/roles', s.roleList, { signal }),
689
- // Create an operator-authored role. The server validates the grant map + base
690
- // tier and 409s on a name collision; the reserved `admin` role cannot be created.
691
- createRole: (body) => req('/api/auth/roles', s.roleBody, { method: 'POST', body }),
692
- // Edit a role's per-tag grant map + description (omit-means-keep). The reserved
693
- // `admin` role and any `allow_all` role are block-downgrade guarded server-side.
694
- updateRole: (name, body) => req(`/api/auth/roles/${encodeSegment(name)}`, s.roleBody, { method: 'PUT', body }),
695
- // Delete a role. The reserved `admin` role is undeletable; a role still assigned
696
- // to any principal is a loud 409.
697
- deleteRole: (name) => req(`/api/auth/roles/${encodeSegment(name)}`, s.roleDeleted, { method: 'DELETE' }),
698
- // A role's append-only version history + who/action/before→after audit trail.
699
- // ADMIN-ONLY (`secret`).
700
- listRoleVersions: (name, signal) => req(`/api/auth/roles/${encodeSegment(name)}/versions`, s.roleVersions, { signal }),
701
- // Re-point a role's active version to a prior one (LIVE — holders follow on
702
- // their next request); returns the re-pointed role body.
703
- rollbackRole: (name, version) => req(`/api/auth/roles/${encodeSegment(name)}/rollback`, s.roleBody, {
704
- method: 'POST',
705
- body: { version },
706
- }),
707
- // -- auth: api keys ------------------------------------------------------
708
- listTokensPayload: (signal) => req('/api/auth/tokens-payload', s.tokensPayload, { signal }),
709
- createApiKey: (body) => req('/api/auth/api-keys', s.createdApiKey, { method: 'POST', body }),
710
- editApiKey: (userId, body) => req(`/api/auth/api-keys/${encodeSegment(userId)}`, s.editApiKeyResult, {
711
- method: 'PUT',
712
- body,
713
- }),
714
- revokeApiKey: (userId) => req(`/api/auth/api-keys/${encodeSegment(userId)}`, s.revokeApiKeyResult, {
715
- method: 'DELETE',
716
- }),
717
- // Mint a one-time claim link carrying a raw key to another device (the QR
718
- // onboarding leg). The token rides the URL fragment; the server returns a
719
- // relative `claim_path` and the raw token ONCE.
720
- createClaimLink: (body) => req('/api/auth/claim-links', s.claimLinkCreated, { method: 'POST', body }),
721
- // -- auth: the caller's capability projection ----------------------------
722
- // The derived (path, method) surface + tools/agents the caller can reach
723
- // right now — the projection the Studio filters its nav and pages on.
724
- getMe: (signal) => req('/api/auth/me', s.meProjection, { signal }),
725
- // -- login: the public sign-in aggregator --------------------------------
726
- // PUBLIC by design: `apiRequest` attaches `x-api-key` only when a token
727
- // exists, so the unauthenticated call carries no auth header — no transport
728
- // change needed.
729
- getLoginMethods: (options) => req('/api/login/methods', s.loginMethods, { signal: options?.signal }),
730
- // Submit a `form` method's field VALUES as a flat top-level JSON body (the
731
- // server's request models take the declared field names top-level). `path`
732
- // is re-checked with the same relative-`/api/` guard the renderer uses — an
733
- // off-origin path is a PROGRAMMING error (loud `Error`), not a login failure.
734
- // A refused credential becomes an inline `ApiLoginFailedError`.
735
- submitLoginForm: async (path, values) => {
736
- if (!isSafeApiPath(path)) {
737
- throw new Error(`login submit_path must be a relative /api/ path; received ${JSON.stringify(path)}`);
738
- }
739
- try {
740
- return await req(path, s.loginResult, { method: 'POST', body: values });
741
- }
742
- catch (error) {
743
- return asLoginError(error);
744
- }
745
- },
746
- // Exchange a one-time SSO hand-back code for a session (never a token in the
747
- // URL). An expired/reused code is a login failure, surfaced inline.
748
- exchangeSsoCode: async (code) => {
749
- try {
750
- return await req('/api/login/sso/exchange', s.loginResult, {
751
- method: 'POST',
752
- body: { code },
753
- });
754
- }
755
- catch (error) {
756
- return asLoginError(error);
757
- }
758
- },
759
- // Exchange a one-time claim token (read from the login URL fragment, never a
760
- // query param) for a session. PUBLIC. The token is single-use and burns
761
- // server-side; a used/unknown/expired token answers the uniform 404, which —
762
- // like the SSO leg — maps to an inline `ApiLoginFailedError`, not a global
763
- // unauthorized redirect. The caller latches the exchange so it fires once.
764
- claimLogin: async (body) => {
765
- try {
766
- return await req('/api/login/claim', s.loginResult, { method: 'POST', body });
767
- }
768
- catch (error) {
769
- return asLoginError(error);
770
- }
771
- },
772
- // Whether this deployment can mint API keys locally (the settings key-create
773
- // UI gates on `mintable`). AUTHED.
774
- getAuthCapabilities: (signal) => req('/api/auth/capabilities', s.authCapabilities, { signal }),
775
- // -- auth: session logout ------------------------------------------------
776
- // The single authed logout dispatcher — revokes the caller's accounts
777
- // session server-side before the shell clears local state. A plain `sk-` key
778
- // has nothing to revoke and answers 404 (surfaced as an `ApiError`, quiet at
779
- // the call site); a 5xx/network failure is a real revoke failure the shell
780
- // surfaces loudly.
781
- logout: () => req('/api/auth/logout', s.logoutResult, { method: 'POST' }),
782
- // -- auth: policy authoring + versioning ---------------------------------
783
- // The fail-closed jq guard: compiles (and, with `sample_context`, evaluates) a
784
- // condition without persisting anything, so a broken condition is caught at
785
- // author time instead of locking a key out at enforcement. A broken condition
786
- // is a 400 the transport throws — never a false-positive `ok`.
787
- validateCondition: (body) => req('/api/auth/validate-condition', s.validateConditionResult, { method: 'POST', body }),
788
- // The user's append-only policy version history (each row's `is_current`
789
- // derived from the active pointer). SECRET-ADJACENT — the body carries the raw
790
- // condition, so its fixtures are hand-authored.
791
- listPolicyVersions: (userId, signal) => req(`/api/auth/api-keys/${encodeSegment(userId)}/policy/versions`, s.policyVersionList, {
792
- signal,
793
- }),
794
- // Re-point the enforced policy to a prior version; returns the re-pointed
795
- // active version. Enforcement follows on cache invalidation.
796
- rollbackPolicy: (userId, version) => req(`/api/auth/api-keys/${encodeSegment(userId)}/policy/rollback`, s.policyRollback, {
797
- method: 'POST',
798
- body: { version },
799
- }),
800
- // -- backup --------------------------------------------------------------
801
- listBackupSections: (signal) => req('/api/backup/sections', s.backupSections, { signal }),
802
- exportBackup: (sections, signal) => apiDownload(config, '/api/backup/export', { method: 'POST', body: { sections }, signal }),
803
- importBackup: (body) => req('/api/backup/import', s.backupImportReport, { method: 'POST', body }),
804
- // -- schedules -----------------------------------------------------------
805
- listSchedules: (signal) => req('/api/schedules', s.scheduleList, { signal }),
806
- getServerDateTime: (signal) => req('/api/schedules/server-datetime', s.serverDateTime, { signal }),
807
- addSchedule: (body) => req('/api/schedules', s.jsonValue, { method: 'POST', body }),
808
- deleteSchedule: (name) => req(`/api/schedules/${encodeSegment(name)}`, s.jsonValue, { method: 'DELETE' }),
809
- // -- observability -------------------------------------------------------
810
- // The monitoring-backend dashboard rollups (`GET /api/observability/metrics`),
811
- // a JSON aggregate distinct from any raw process-metrics text endpoint.
812
- getObservabilityMetrics: (params = {}, signal) => req('/api/observability/metrics', s.dashboardMetrics, {
813
- query: { from: params.from, to: params.to, granularity: params.granularity },
814
- signal,
815
- }),
816
- listRuns: (params = {}, signal) => req('/api/observability/runs', s.runsPage, { query: runsQueryToParams(params), signal }),
817
- getRunTrace: (traceId, signal) => req(`/api/observability/runs/${encodeSegment(traceId)}/trace`, s.runTrace, { signal }),
818
- exportTrace: (traceId, signal) => apiDownload(config, `/api/observability/runs/${encodeSegment(traceId)}/trace/export`, {
819
- signal,
820
- }),
821
- exportRuns: (params, signal) => apiDownload(config, '/api/observability/runs/export', {
822
- query: { ...runsQueryToParams(params), format: params.format },
823
- signal,
824
- }),
825
- // -- marketplace ---------------------------------------------------------
826
- // Browse/search proxy the registry through the app (one origin, one auth);
827
- // install/uninstall/update mutate the RUNNING app (pip install + manifest
828
- // patch + reload) and return synchronous receipts.
829
- searchMarketplace: (query = {}, signal) => req('/api/marketplace/search', s.marketplaceSearchPage, {
830
- query: {
831
- q: query.q,
832
- kind: query.kind,
833
- category: query.category,
834
- tags: query.tags,
835
- namespace: query.namespace,
836
- tier: query.tier,
837
- contract: query.contract,
838
- sort: query.sort,
839
- page: query.page,
840
- page_size: query.page_size,
841
- },
842
- signal,
843
- }),
844
- getMarketplacePlugin: (namespace, name, signal) => req(`/api/marketplace/plugins/${encodeSegment(namespace)}/${encodeSegment(name)}`, s.marketplacePluginDetail, { signal }),
845
- listMarketplaceCategories: (signal) => req('/api/marketplace/categories', s.marketplaceCategories, { signal }),
846
- listMarketplaceKinds: (signal) => req('/api/marketplace/kinds', s.marketplaceKinds, { signal }),
847
- listInstalledMarketplacePlugins: (signal) => req('/api/marketplace/installed', s.marketplaceInstalled, { signal }),
848
- // Preview resolves the spec and computes routes/collisions/public rows with
849
- // NO side effects, so the install dialog can show absolute paths, block on a
850
- // collision, and surface the public routes before the operator commits.
851
- previewMarketplaceInstall: (body, signal) => req('/api/marketplace/install/preview', s.marketplaceInstallPreview, {
852
- method: 'POST',
853
- body,
854
- signal,
855
- }),
856
- installMarketplacePlugin: (body) => req('/api/marketplace/install', s.marketplaceInstallResult, { method: 'POST', body }),
857
- uninstallMarketplacePlugin: (body) => req('/api/marketplace/uninstall', s.marketplaceUninstallResult, { method: 'POST', body }),
858
- updateMarketplacePlugin: (body) => req('/api/marketplace/update', s.marketplaceInstallResult, { method: 'POST', body }),
859
- // Upgrade EVERY installed plugin to its newest compatible version in one
860
- // pass; the response is the per-plugin outcome readout.
861
- upgradeAllMarketplacePlugins: () => req('/api/marketplace/upgrade-all', s.marketplaceUpgradeAllResult, { method: 'POST' }),
862
- getMarketplaceAdvisories: (signal) => req('/api/marketplace/advisories', s.marketplaceAdvisories, { signal }),
863
- // -- system (plain-text ops endpoint) ------------------------------------
864
- getHealth: (signal) => apiText(config, '/health', { signal }),
865
- // The pluggable-kind status table (`GET /api/system/kinds`) — a JSON `{data}`
866
- // envelope, one row per kind with its active/default/off state.
867
- getSystemKinds: (signal) => req('/api/system/kinds', s.kindStatusList, { signal }),
868
- /**
869
- * Open the authed interactions SSE stream. The stream is TAIL-ONLY: its cursor
870
- * is the stream tail at connect, so it carries no backlog and no
871
- * `interaction.backlog_done` frame — the pending base comes from
872
- * `listInteractions`. The caller drives reconnect and refetches that base on
873
- * each (re)connect. Returns the raw frame iterator; the interactions feature
874
- * maps frames to typed events.
875
- *
876
- * `lastEventId` (the id of the last frame the caller saw) is sent as the
877
- * `Last-Event-ID` header so the server resumes AFTER it and replays any frame —
878
- * including a card's `answered` — that landed during a disconnect gap. It is
879
- * also sent as a `?last_event_id=` query param so a proxy that strips the header
880
- * still resumes; the server reads either. Omitted on a first connect.
881
- */
882
- streamInteractions: async (signal, lastEventId) => {
883
- const doFetch = config.fetch ?? globalThis.fetch;
884
- const token = config.getToken();
885
- const headers = { accept: 'text/event-stream' };
886
- if (token)
887
- headers['x-api-key'] = token;
888
- if (lastEventId)
889
- headers['last-event-id'] = lastEventId;
890
- // Distinct-URL per open — see the canonical constraint on `sseOpenToken`.
891
- const resume = lastEventId ? `&last_event_id=${encodeURIComponent(lastEventId)}` : '';
892
- const url = `${config.baseUrl ?? ''}/api/interactions/stream?_=${sseOpenToken()}${resume}`;
893
- const response = await doFetch(url, {
894
- headers,
895
- signal,
896
- });
897
- if (response.status === 401)
898
- throw new ApiUnauthorizedError();
899
- // A non-ok stream open (5xx/4xx) still carries a body, which would parse to
900
- // zero frames and look like a silently-empty stream — surface it loudly so
901
- // the caller shows an error instead of hanging on a reconnect loop. Read the
902
- // `{ "error" }` envelope so a load-bearing server message (e.g. the OFF
903
- // store's 501 remediation line) surfaces verbatim instead of collapsing to
904
- // the bare status text.
905
- if (!response.ok) {
906
- let payload;
907
- try {
908
- payload = await response.json();
909
- }
910
- catch {
911
- throw new ApiError(response.statusText || 'stream failed', response.status);
912
- }
913
- const { message, code } = extractError(payload);
914
- throw new ApiError(message ?? (response.statusText || 'stream failed'), response.status, code);
915
- }
916
- return readSseFrames(response, signal);
917
- },
918
- };
919
- }
920
- //# sourceMappingURL=client.js.map