@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.
- package/dist/client/agents-client.d.ts +26 -0
- package/dist/client/agents-client.d.ts.map +1 -0
- package/dist/client/agents-client.js +19 -0
- package/dist/client/agents-client.js.map +1 -0
- package/dist/client/auth-client.d.ts +205 -0
- package/dist/client/auth-client.d.ts.map +1 -0
- package/dist/client/auth-client.js +59 -0
- package/dist/client/auth-client.js.map +1 -0
- package/dist/client/backend-client.d.ts +39 -0
- package/dist/client/backend-client.d.ts.map +1 -0
- package/dist/client/backend-client.js +22 -0
- package/dist/client/backend-client.js.map +1 -0
- package/dist/client/backup-client.d.ts +56 -0
- package/dist/client/backup-client.d.ts.map +1 -0
- package/dist/client/backup-client.js +12 -0
- package/dist/client/backup-client.js.map +1 -0
- package/dist/client/capabilities-client.d.ts +35 -0
- package/dist/client/capabilities-client.d.ts.map +1 -0
- package/dist/client/capabilities-client.js +19 -0
- package/dist/client/capabilities-client.js.map +1 -0
- package/dist/client/channels-client.d.ts +7 -0
- package/dist/client/channels-client.d.ts.map +1 -0
- package/dist/client/channels-client.js +10 -0
- package/dist/client/channels-client.js.map +1 -0
- package/dist/client/config-client.d.ts +59 -0
- package/dist/client/config-client.d.ts.map +1 -0
- package/dist/client/config-client.js +18 -0
- package/dist/client/config-client.js.map +1 -0
- package/dist/client/connectors-client.d.ts +225 -0
- package/dist/client/connectors-client.d.ts.map +1 -0
- package/dist/client/connectors-client.js +31 -0
- package/dist/client/connectors-client.js.map +1 -0
- package/dist/client/conversations-client.d.ts +399 -0
- package/dist/client/conversations-client.d.ts.map +1 -0
- package/dist/client/conversations-client.js +120 -0
- package/dist/client/conversations-client.js.map +1 -0
- package/dist/client/hooks-client.d.ts +113 -0
- package/dist/client/hooks-client.d.ts.map +1 -0
- package/dist/client/hooks-client.js +25 -0
- package/dist/client/hooks-client.js.map +1 -0
- package/dist/{client.d.ts → client/index.d.ts} +2235 -2666
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +79 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/interactions-client.d.ts +52 -0
- package/dist/client/interactions-client.d.ts.map +1 -0
- package/dist/client/interactions-client.js +84 -0
- package/dist/client/interactions-client.js.map +1 -0
- package/dist/client/login-client.d.ts +56 -0
- package/dist/client/login-client.d.ts.map +1 -0
- package/dist/client/login-client.js +103 -0
- package/dist/client/login-client.js.map +1 -0
- package/dist/client/manifest-client.d.ts +354 -0
- package/dist/client/manifest-client.d.ts.map +1 -0
- package/dist/client/manifest-client.js +76 -0
- package/dist/client/manifest-client.js.map +1 -0
- package/dist/client/marketplace-client.d.ts +274 -0
- package/dist/client/marketplace-client.d.ts.map +1 -0
- package/dist/client/marketplace-client.js +46 -0
- package/dist/client/marketplace-client.js.map +1 -0
- package/dist/client/mcp-config-schema-client.d.ts +5 -0
- package/dist/client/mcp-config-schema-client.d.ts.map +1 -0
- package/dist/client/mcp-config-schema-client.js +9 -0
- package/dist/client/mcp-config-schema-client.js.map +1 -0
- package/dist/client/notifications-client.d.ts +27 -0
- package/dist/client/notifications-client.d.ts.map +1 -0
- package/dist/client/notifications-client.js +11 -0
- package/dist/client/notifications-client.js.map +1 -0
- package/dist/client/observability-client.d.ts +103 -0
- package/dist/client/observability-client.d.ts.map +1 -0
- package/dist/client/observability-client.js +43 -0
- package/dist/client/observability-client.js.map +1 -0
- package/dist/client/plugins-client.d.ts +17 -0
- package/dist/client/plugins-client.d.ts.map +1 -0
- package/dist/client/plugins-client.js +9 -0
- package/dist/client/plugins-client.js.map +1 -0
- package/dist/client/policy-client.d.ts +37 -0
- package/dist/client/policy-client.d.ts.map +1 -0
- package/dist/client/policy-client.js +26 -0
- package/dist/client/policy-client.js.map +1 -0
- package/dist/client/presets-client.d.ts +335 -0
- package/dist/client/presets-client.d.ts.map +1 -0
- package/dist/client/presets-client.js +38 -0
- package/dist/client/presets-client.js.map +1 -0
- package/dist/client/profiles-client.d.ts +102 -0
- package/dist/client/profiles-client.d.ts.map +1 -0
- package/dist/client/profiles-client.js +37 -0
- package/dist/client/profiles-client.js.map +1 -0
- package/dist/client/schedules-client.d.ts +28 -0
- package/dist/client/schedules-client.d.ts.map +1 -0
- package/dist/client/schedules-client.js +13 -0
- package/dist/client/schedules-client.js.map +1 -0
- package/dist/client/settings-schema-client.d.ts +23 -0
- package/dist/client/settings-schema-client.d.ts.map +1 -0
- package/dist/client/settings-schema-client.js +9 -0
- package/dist/client/settings-schema-client.js.map +1 -0
- package/dist/client/states-client.d.ts +471 -0
- package/dist/client/states-client.d.ts.map +1 -0
- package/dist/client/states-client.js +144 -0
- package/dist/client/states-client.js.map +1 -0
- package/dist/client/storage-client.d.ts +40 -0
- package/dist/client/storage-client.d.ts.map +1 -0
- package/dist/client/storage-client.js +43 -0
- package/dist/client/storage-client.js.map +1 -0
- package/dist/client/sub-mcp-client.d.ts +17 -0
- package/dist/client/sub-mcp-client.d.ts.map +1 -0
- package/dist/client/sub-mcp-client.js +14 -0
- package/dist/client/sub-mcp-client.js.map +1 -0
- package/dist/client/system-client.d.ts +11 -0
- package/dist/client/system-client.d.ts.map +1 -0
- package/dist/client/system-client.js +13 -0
- package/dist/client/system-client.js.map +1 -0
- package/dist/client/templates-client.d.ts +29 -0
- package/dist/client/templates-client.d.ts.map +1 -0
- package/dist/client/templates-client.js +21 -0
- package/dist/client/templates-client.js.map +1 -0
- package/dist/client/tool-extensions-client.d.ts +54 -0
- package/dist/client/tool-extensions-client.d.ts.map +1 -0
- package/dist/client/tool-extensions-client.js +21 -0
- package/dist/client/tool-extensions-client.js.map +1 -0
- package/dist/client/tool-meta-client.d.ts +69 -0
- package/dist/client/tool-meta-client.d.ts.map +1 -0
- package/dist/client/tool-meta-client.js +38 -0
- package/dist/client/tool-meta-client.js.map +1 -0
- package/dist/client/tools-client.d.ts +83 -0
- package/dist/client/tools-client.d.ts.map +1 -0
- package/dist/client/tools-client.js +38 -0
- package/dist/client/tools-client.js.map +1 -0
- package/dist/client/transport.d.ts +13 -0
- package/dist/client/transport.d.ts.map +1 -0
- package/dist/client/transport.js +10 -0
- package/dist/client/transport.js.map +1 -0
- package/dist/client/trigger-links-client.d.ts +44 -0
- package/dist/client/trigger-links-client.d.ts.map +1 -0
- package/dist/client/trigger-links-client.js +30 -0
- package/dist/client/trigger-links-client.js.map +1 -0
- package/dist/client/web-entry-gate-client.d.ts +34 -0
- package/dist/client/web-entry-gate-client.d.ts.map +1 -0
- package/dist/client/web-entry-gate-client.js +25 -0
- package/dist/client/web-entry-gate-client.js.map +1 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +33 -17
- package/dist/http.js.map +1 -1
- package/dist/schemas/api-keys.d.ts +34 -0
- package/dist/schemas/api-keys.d.ts.map +1 -0
- package/dist/schemas/api-keys.js +22 -0
- package/dist/schemas/api-keys.js.map +1 -0
- package/dist/schemas/auth-routes.d.ts +78 -0
- package/dist/schemas/auth-routes.d.ts.map +1 -0
- package/dist/schemas/auth-routes.js +42 -0
- package/dist/schemas/auth-routes.js.map +1 -0
- package/dist/schemas/backup.d.ts +78 -0
- package/dist/schemas/backup.d.ts.map +1 -0
- package/dist/schemas/backup.js +33 -0
- package/dist/schemas/backup.js.map +1 -0
- package/dist/schemas/capabilities.d.ts +99 -0
- package/dist/schemas/capabilities.d.ts.map +1 -0
- package/dist/schemas/capabilities.js +74 -0
- package/dist/schemas/capabilities.js.map +1 -0
- package/dist/schemas/channels.d.ts +7 -0
- package/dist/schemas/channels.d.ts.map +1 -0
- package/dist/schemas/channels.js +9 -0
- package/dist/schemas/channels.js.map +1 -0
- package/dist/schemas/config.d.ts +17 -0
- package/dist/schemas/config.d.ts.map +1 -0
- package/dist/schemas/config.js +17 -0
- package/dist/schemas/config.js.map +1 -0
- package/dist/schemas/connectors.d.ts +383 -0
- package/dist/schemas/connectors.d.ts.map +1 -0
- package/dist/schemas/connectors.js +122 -0
- package/dist/schemas/connectors.js.map +1 -0
- package/dist/schemas/conversations.d.ts +600 -0
- package/dist/schemas/conversations.d.ts.map +1 -0
- package/dist/schemas/conversations.js +208 -0
- package/dist/schemas/conversations.js.map +1 -0
- package/dist/schemas/extensions.d.ts +44 -0
- package/dist/schemas/extensions.d.ts.map +1 -0
- package/dist/schemas/extensions.js +29 -0
- package/dist/schemas/extensions.js.map +1 -0
- package/dist/schemas/fleet.d.ts +271 -0
- package/dist/schemas/fleet.d.ts.map +1 -0
- package/dist/schemas/fleet.js +132 -0
- package/dist/schemas/fleet.js.map +1 -0
- package/dist/schemas/hooks.d.ts +120 -0
- package/dist/schemas/hooks.d.ts.map +1 -0
- package/dist/schemas/hooks.js +44 -0
- package/dist/schemas/hooks.js.map +1 -0
- package/dist/schemas/index.d.ts +38 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +38 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/interactions.d.ts +156 -0
- package/dist/schemas/interactions.d.ts.map +1 -0
- package/dist/schemas/interactions.js +124 -0
- package/dist/schemas/interactions.js.map +1 -0
- package/dist/schemas/login.d.ts +86 -0
- package/dist/schemas/login.d.ts.map +1 -0
- package/dist/schemas/login.js +54 -0
- package/dist/schemas/login.js.map +1 -0
- package/dist/schemas/manifest-mcp.d.ts +125 -0
- package/dist/schemas/manifest-mcp.d.ts.map +1 -0
- package/dist/schemas/manifest-mcp.js +61 -0
- package/dist/schemas/manifest-mcp.js.map +1 -0
- package/dist/schemas/marketplace.d.ts +708 -0
- package/dist/schemas/marketplace.d.ts.map +1 -0
- package/dist/schemas/marketplace.js +387 -0
- package/dist/schemas/marketplace.js.map +1 -0
- package/dist/schemas/notifications.d.ts +61 -0
- package/dist/schemas/notifications.d.ts.map +1 -0
- package/dist/schemas/notifications.js +38 -0
- package/dist/schemas/notifications.js.map +1 -0
- package/dist/schemas/observability.d.ts +114 -0
- package/dist/schemas/observability.d.ts.map +1 -0
- package/dist/schemas/observability.js +74 -0
- package/dist/schemas/observability.js.map +1 -0
- package/dist/schemas/policy.d.ts +58 -0
- package/dist/schemas/policy.d.ts.map +1 -0
- package/dist/schemas/policy.js +32 -0
- package/dist/schemas/policy.js.map +1 -0
- package/dist/schemas/presets.d.ts +258 -0
- package/dist/schemas/presets.d.ts.map +1 -0
- package/dist/schemas/presets.js +95 -0
- package/dist/schemas/presets.js.map +1 -0
- package/dist/schemas/profiles.d.ts +180 -0
- package/dist/schemas/profiles.d.ts.map +1 -0
- package/dist/schemas/profiles.js +100 -0
- package/dist/schemas/profiles.js.map +1 -0
- package/dist/schemas/roles.d.ts +125 -0
- package/dist/schemas/roles.d.ts.map +1 -0
- package/dist/schemas/roles.js +46 -0
- package/dist/schemas/roles.js.map +1 -0
- package/dist/schemas/schedules.d.ts +25 -0
- package/dist/schemas/schedules.d.ts.map +1 -0
- package/dist/schemas/schedules.js +24 -0
- package/dist/schemas/schedules.js.map +1 -0
- package/dist/schemas/served.d.ts +626 -0
- package/dist/schemas/served.d.ts.map +1 -0
- package/dist/schemas/served.js +28 -0
- package/dist/schemas/served.js.map +1 -0
- package/dist/schemas/settings-schema.d.ts +23 -0
- package/dist/schemas/settings-schema.d.ts.map +1 -0
- package/dist/schemas/settings-schema.js +25 -0
- package/dist/schemas/settings-schema.js.map +1 -0
- package/dist/schemas/shared.d.ts +20 -0
- package/dist/schemas/shared.d.ts.map +1 -0
- package/dist/schemas/shared.js +20 -0
- package/dist/schemas/shared.js.map +1 -0
- package/dist/schemas/states.d.ts +499 -0
- package/dist/schemas/states.d.ts.map +1 -0
- package/dist/schemas/states.js +220 -0
- package/dist/schemas/states.js.map +1 -0
- package/dist/schemas/storage.d.ts +37 -0
- package/dist/schemas/storage.d.ts.map +1 -0
- package/dist/schemas/storage.js +24 -0
- package/dist/schemas/storage.js.map +1 -0
- package/dist/schemas/studio-plugins.d.ts +30 -0
- package/dist/schemas/studio-plugins.d.ts.map +1 -0
- package/dist/schemas/studio-plugins.js +20 -0
- package/dist/schemas/studio-plugins.js.map +1 -0
- package/dist/schemas/sub-mcp.d.ts +29 -0
- package/dist/schemas/sub-mcp.d.ts.map +1 -0
- package/dist/schemas/sub-mcp.js +22 -0
- package/dist/schemas/sub-mcp.js.map +1 -0
- package/dist/schemas/system-kinds.d.ts +122 -0
- package/dist/schemas/system-kinds.d.ts.map +1 -0
- package/dist/schemas/system-kinds.js +40 -0
- package/dist/schemas/system-kinds.js.map +1 -0
- package/dist/schemas/templates.d.ts +26 -0
- package/dist/schemas/templates.d.ts.map +1 -0
- package/dist/schemas/templates.js +11 -0
- package/dist/schemas/templates.js.map +1 -0
- package/dist/schemas/tool-meta.d.ts +64 -0
- package/dist/schemas/tool-meta.d.ts.map +1 -0
- package/dist/schemas/tool-meta.js +44 -0
- package/dist/schemas/tool-meta.js.map +1 -0
- package/dist/schemas/tools.d.ts +62 -0
- package/dist/schemas/tools.d.ts.map +1 -0
- package/dist/schemas/tools.js +53 -0
- package/dist/schemas/tools.js.map +1 -0
- package/dist/schemas/trigger-links.d.ts +71 -0
- package/dist/schemas/trigger-links.d.ts.map +1 -0
- package/dist/schemas/trigger-links.js +46 -0
- package/dist/schemas/trigger-links.js.map +1 -0
- package/dist/schemas/web-entry.d.ts +45 -0
- package/dist/schemas/web-entry.d.ts.map +1 -0
- package/dist/schemas/web-entry.js +36 -0
- package/dist/schemas/web-entry.js.map +1 -0
- package/dist/tool-runs.d.ts +4 -4
- package/package.json +1 -1
- package/src/client/agents-client.ts +23 -0
- package/src/client/auth-client.ts +144 -0
- package/src/client/backend-client.ts +25 -0
- package/src/client/backup-client.ts +16 -0
- package/src/client/capabilities-client.ts +22 -0
- package/src/client/channels-client.ts +11 -0
- package/src/client/config-client.ts +21 -0
- package/src/client/connectors-client.ts +52 -0
- package/src/client/conversations-client.ts +240 -0
- package/src/client/hooks-client.ts +44 -0
- package/src/client/index.ts +122 -0
- package/src/client/interactions-client.ts +89 -0
- package/src/client/login-client.ts +109 -0
- package/src/client/manifest-client.ts +118 -0
- package/src/client/marketplace-client.ts +122 -0
- package/src/client/mcp-config-schema-client.ts +11 -0
- package/src/client/notifications-client.ts +13 -0
- package/src/client/observability-client.ts +82 -0
- package/src/client/plugins-client.ts +11 -0
- package/src/client/policy-client.ts +41 -0
- package/src/client/presets-client.ts +124 -0
- package/src/client/profiles-client.ts +51 -0
- package/src/client/schedules-client.ts +22 -0
- package/src/client/settings-schema-client.ts +11 -0
- package/src/client/states-client.ts +292 -0
- package/src/client/storage-client.ts +63 -0
- package/src/client/sub-mcp-client.ts +17 -0
- package/src/client/system-client.ts +16 -0
- package/src/client/templates-client.ts +28 -0
- package/src/client/tool-extensions-client.ts +24 -0
- package/src/client/tool-meta-client.ts +65 -0
- package/src/client/tools-client.ts +66 -0
- package/src/client/transport.ts +18 -0
- package/src/client/trigger-links-client.ts +51 -0
- package/src/client/web-entry-gate-client.ts +45 -0
- package/src/http.ts +48 -24
- package/src/schemas/api-keys.ts +26 -0
- package/src/schemas/auth-routes.ts +48 -0
- package/src/schemas/backup.ts +42 -0
- package/src/schemas/capabilities.ts +87 -0
- package/src/schemas/channels.ts +11 -0
- package/src/schemas/config.ts +17 -0
- package/src/schemas/connectors.ts +138 -0
- package/src/schemas/conversations.ts +256 -0
- package/src/schemas/extensions.ts +34 -0
- package/src/schemas/fleet.ts +153 -0
- package/src/schemas/hooks.ts +56 -0
- package/src/schemas/index.ts +37 -0
- package/src/schemas/interactions.ts +146 -0
- package/src/schemas/login.ts +62 -0
- package/src/schemas/manifest-mcp.ts +69 -0
- package/src/schemas/marketplace.ts +447 -0
- package/src/schemas/notifications.ts +42 -0
- package/src/schemas/observability.ts +86 -0
- package/src/schemas/policy.ts +37 -0
- package/src/schemas/presets.ts +113 -0
- package/src/schemas/profiles.ts +119 -0
- package/src/schemas/roles.ts +57 -0
- package/src/schemas/schedules.ts +27 -0
- package/src/schemas/served.ts +49 -0
- package/src/schemas/settings-schema.ts +31 -0
- package/src/schemas/shared.ts +21 -0
- package/src/schemas/states.ts +272 -0
- package/src/schemas/storage.ts +31 -0
- package/src/schemas/studio-plugins.ts +21 -0
- package/src/schemas/sub-mcp.ts +24 -0
- package/src/schemas/system-kinds.ts +46 -0
- package/src/schemas/templates.ts +11 -0
- package/src/schemas/tool-meta.ts +51 -0
- package/src/schemas/tools.ts +61 -0
- package/src/schemas/trigger-links.ts +54 -0
- package/src/schemas/web-entry.ts +46 -0
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -920
- package/dist/client.js.map +0 -1
- package/dist/schemas.d.ts +0 -5172
- package/dist/schemas.d.ts.map +0 -1
- package/dist/schemas.js +0 -2268
- package/dist/schemas.js.map +0 -1
- package/src/client.ts +0 -1771
- package/src/schemas.ts +0 -2760
package/src/client.ts
DELETED
|
@@ -1,1771 +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 {
|
|
8
|
-
type ApiConfig,
|
|
9
|
-
type RequestOptions,
|
|
10
|
-
apiDownload,
|
|
11
|
-
apiRequest,
|
|
12
|
-
apiText,
|
|
13
|
-
encodeSegment,
|
|
14
|
-
extractError,
|
|
15
|
-
isUnsafePathSegment,
|
|
16
|
-
} from './http';
|
|
17
|
-
import { ApiError, ApiLoginFailedError, ApiUnauthorizedError } from './errors';
|
|
18
|
-
import { readSseFrames, sseOpenToken } from './sse';
|
|
19
|
-
import { agentList, streamAgentRun, streamAuthoredAgentRun } from './agents';
|
|
20
|
-
import { getToolRun, listToolRuns, submitToolRun, type SubmitToolRunArgs } from './tool-runs';
|
|
21
|
-
import * as s from './schemas';
|
|
22
|
-
|
|
23
|
-
export interface RunToolArgs {
|
|
24
|
-
readonly tool: string;
|
|
25
|
-
readonly kwargs?: Record<string, unknown>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Identify one app tool for a live-registry op (`POST /api/tools/reload`,
|
|
30
|
-
* `POST /api/tools/remove`): its `kind` (the reloader kind a plugin registered,
|
|
31
|
-
* e.g. `example_tool`) and `name`. `targets` optionally restricts the fleet
|
|
32
|
-
* fan-out to specific workers; omit it to reach every worker.
|
|
33
|
-
*/
|
|
34
|
-
export interface ToolAdminArgs {
|
|
35
|
-
readonly kind: string;
|
|
36
|
-
readonly name: string;
|
|
37
|
-
readonly targets?: string[];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Names to add to / remove from the manifest `api_tools` include and exclude
|
|
42
|
-
* lists (`POST /api/api-tools`). Each field is a bare name list; the server
|
|
43
|
-
* refuses a body whose four lists are all empty.
|
|
44
|
-
*/
|
|
45
|
-
export interface ApiToolsListsBody {
|
|
46
|
-
readonly include_add?: string[];
|
|
47
|
-
readonly include_remove?: string[];
|
|
48
|
-
readonly exclude_add?: string[];
|
|
49
|
-
readonly exclude_remove?: string[];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface StartConnectArgs {
|
|
53
|
-
readonly provider_id: string;
|
|
54
|
-
readonly alias: string;
|
|
55
|
-
readonly enabled_sub_services: string[];
|
|
56
|
-
readonly config_values?: Record<string, string>;
|
|
57
|
-
readonly return_url?: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Body for creating or editing an API key (POST/PUT `/api/auth/api-keys`). */
|
|
61
|
-
export interface ApiKeyBody {
|
|
62
|
-
readonly user_id: string;
|
|
63
|
-
readonly description: string;
|
|
64
|
-
readonly scopes: string[];
|
|
65
|
-
// `null` is an explicit clear on edit (PATCH-style PUT): absent preserves the
|
|
66
|
-
// stored policy data, `null` wipes it. Create never sends `null`.
|
|
67
|
-
readonly policy_data?: Record<string, unknown> | null;
|
|
68
|
-
// The access-control condition: an authored templated-text value (inline content
|
|
69
|
-
// or a stored template id), or `null` to explicitly clear it on edit.
|
|
70
|
-
readonly condition?: s.TemplatedText | null;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Body for one-time claim-link creation (POST `/api/auth/claim-links`). The
|
|
75
|
-
* `api_key` is a raw key the caller holds (the just-minted key, client-side
|
|
76
|
-
* exactly once); `ttl_seconds` optionally overrides the default lifetime (the
|
|
77
|
-
* server caps it at its ceiling).
|
|
78
|
-
*/
|
|
79
|
-
export interface ClaimLinkBody {
|
|
80
|
-
readonly api_key: string;
|
|
81
|
-
readonly ttl_seconds?: number | null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Body for the fail-closed jq guard (POST `/api/auth/validate-condition`). The guard
|
|
86
|
-
* compile-checks the inline jq `condition` the author typed. `sample_context` is a
|
|
87
|
-
* `JqAuthContext`-shaped sample; when present the guard also evaluates the condition
|
|
88
|
-
* against it and returns the boolean `result` (else `result` is `null`).
|
|
89
|
-
*/
|
|
90
|
-
export interface ValidateConditionBody {
|
|
91
|
-
readonly condition?: string;
|
|
92
|
-
readonly sample_context?: Record<string, unknown>;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Body for binding a webhook verifier to a topic
|
|
97
|
-
* (PUT `/api/hooks/topics/{topic}/verifier`). `verifier` is a registered verifier
|
|
98
|
-
* NAME, resolved against the live registry at bind time (an unknown name is a loud
|
|
99
|
-
* 400). `config` is that verifier's config object — it carries a `secret_env` name,
|
|
100
|
-
* never a secret value — and defaults to `{}` when omitted.
|
|
101
|
-
*/
|
|
102
|
-
export interface TopicVerifierBody {
|
|
103
|
-
readonly verifier: string;
|
|
104
|
-
readonly config?: Record<string, unknown>;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Body for minting a trigger link (POST `/api/hooks/trigger-links`). `ttl_seconds`
|
|
109
|
-
* is REQUIRED and nullable — a positive int is a timed link, `null` is a permanent
|
|
110
|
-
* link (`0`/negative is a loud server 400); it is always sent EXPLICITLY (a dropped
|
|
111
|
-
* key 400s server-side). `name` is optional (the server generates one when omitted);
|
|
112
|
-
* `tool_kwargs` is the optional per-link params merged last into every fire.
|
|
113
|
-
*/
|
|
114
|
-
export interface TriggerLinkCreateBody {
|
|
115
|
-
readonly topic: string;
|
|
116
|
-
readonly name?: string;
|
|
117
|
-
readonly execution_key: string;
|
|
118
|
-
// The link is a token door; this is its only auth knob (token → token+api_key).
|
|
119
|
-
readonly require_api_key: boolean;
|
|
120
|
-
readonly ttl_seconds: number | null;
|
|
121
|
-
readonly tool_kwargs?: Record<string, unknown> | null;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Body for minting a web entry code (POST
|
|
126
|
-
* `/api/channels/web/gates/{identity}/codes`). Both fields are optional-nullable
|
|
127
|
-
* and sent EXPLICITLY: `label` is a human tag (`null` for none); `expires_at` is
|
|
128
|
-
* an ISO-8601 instant the code dies at (`null` for a code that never expires).
|
|
129
|
-
*/
|
|
130
|
-
export interface WebEntryCodeMintBody {
|
|
131
|
-
readonly label: string | null;
|
|
132
|
-
readonly expires_at: string | null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** Body for adding a URL to a scope (POST `/api/auth/scopes`). */
|
|
136
|
-
export interface AddUrlToScopeBody {
|
|
137
|
-
readonly scope_id: string;
|
|
138
|
-
readonly url: string;
|
|
139
|
-
readonly pattern?: string;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Body for creating a role (POST `/api/auth/roles`). `base_tier` is the security
|
|
144
|
-
* tier the role inherits (`editor`/`viewer` — `admin` is reserved); its jq is
|
|
145
|
-
* resolved server-side, never authored here. `grants` is the editable per-tag
|
|
146
|
-
* access-level map (feature-group tag → `none`/`read`/`write`); it defaults to
|
|
147
|
-
* empty (every group `none`, fail-closed) when omitted.
|
|
148
|
-
*/
|
|
149
|
-
export interface RoleCreateBody {
|
|
150
|
-
readonly name: string;
|
|
151
|
-
readonly description?: string;
|
|
152
|
-
readonly base_tier: string;
|
|
153
|
-
readonly grants?: Record<string, s.GrantLevel>;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Body for editing a role (PUT `/api/auth/roles/{name}`). Both fields are
|
|
158
|
-
* omit-means-KEEP: an absent `grants` preserves the stored map (never a silent
|
|
159
|
-
* wipe), an absent `description` preserves the stored description. The base-tier jq
|
|
160
|
-
* is seed-fixed and not editable here.
|
|
161
|
-
*/
|
|
162
|
-
export interface RoleUpdateBody {
|
|
163
|
-
readonly grants?: Record<string, s.GrantLevel>;
|
|
164
|
-
readonly description?: string;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Body for pinning a route public (POST `/api/auth/public-routes`). `pattern` is an
|
|
169
|
-
* optional regex the AC verifier full-matches request paths against, mapping every
|
|
170
|
-
* match to the `url` key (so a mount's whole subtree can be pinned in one call).
|
|
171
|
-
*/
|
|
172
|
-
export interface PinRoutePublicBody {
|
|
173
|
-
readonly url: string;
|
|
174
|
-
readonly pattern?: string;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Body for creating a preset (POST `/api/presets`). `description` is the bound
|
|
179
|
-
* tool's LLM-facing docstring — REQUIRED non-empty on every create path (the door
|
|
180
|
-
* rejects an empty one with a 422); it is never the tool_meta `display_name`.
|
|
181
|
-
* `extensions` is the combos list — the create flow OMITS it when there are no
|
|
182
|
-
* combos (the route rejects an explicit `extensions: []`). `output_schema` is the
|
|
183
|
-
* optional author-set output JSON Schema; `input_schema` is the optional author-set
|
|
184
|
-
* input JSON Schema, read by the router as a top-level version field. Each combo
|
|
185
|
-
* element is a bare extension name or a `{ name, config }` mapping. Categorization
|
|
186
|
-
* tags live in the tool_meta overlay, written after a successful create — never in
|
|
187
|
-
* this body.
|
|
188
|
-
*/
|
|
189
|
-
export interface CreatePresetBody {
|
|
190
|
-
readonly name: string;
|
|
191
|
-
readonly base_tool: string;
|
|
192
|
-
readonly description: string;
|
|
193
|
-
readonly fixed_kwargs?: Record<string, unknown>;
|
|
194
|
-
readonly extensions?: readonly s.PresetExtensionElement[][];
|
|
195
|
-
readonly output_schema?: Record<string, unknown> | null;
|
|
196
|
-
readonly input_schema?: Record<string, unknown> | null;
|
|
197
|
-
// The optional door-layer state binding applied around every run of this preset.
|
|
198
|
-
readonly state_binding?: s.StateBinding | null;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Body for saving a new preset version (POST `/api/presets/{name}/versions`). At
|
|
203
|
-
* least one field must be provided (an empty body is a loud 400). Each field's
|
|
204
|
-
* sentinel is uniform: omitted carries the active version's value forward; an
|
|
205
|
-
* explicit `[]` clears `extensions`, and an explicit `null` clears `output_schema`
|
|
206
|
-
* or `input_schema`. `input_schema` is the optional author-set input JSON Schema and
|
|
207
|
-
* follows the same carry-forward sentinel as `output_schema`. `state_binding` is the
|
|
208
|
-
* optional door-layer binding and follows the same sentinel: omitted carries the active
|
|
209
|
-
* one forward, an explicit `null` clears it. `description` carries forward when omitted;
|
|
210
|
-
* an explicit non-empty string sets it (the API rejects an explicit empty one).
|
|
211
|
-
* Categorization tags are not a version field — they live in the tool_meta overlay.
|
|
212
|
-
*/
|
|
213
|
-
export interface SavePresetVersionBody {
|
|
214
|
-
readonly fixed_kwargs?: Record<string, unknown>;
|
|
215
|
-
readonly description?: string;
|
|
216
|
-
readonly extensions?: readonly s.PresetExtensionElement[][];
|
|
217
|
-
readonly output_schema?: Record<string, unknown> | null;
|
|
218
|
-
readonly input_schema?: Record<string, unknown> | null;
|
|
219
|
-
readonly state_binding?: s.StateBinding | null;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Body for the dry-run validate door (POST `/api/presets/validate`). `name` is
|
|
224
|
-
* required and selects the verdict mode server-side: CREATE when no preset named
|
|
225
|
-
* `name` exists (then `base_tool` is required), VERSION when one does (the version
|
|
226
|
-
* fields are read under save-version semantics). The draft is sent exactly as the
|
|
227
|
-
* matching write would send it.
|
|
228
|
-
*/
|
|
229
|
-
export interface ValidatePresetBody {
|
|
230
|
-
readonly name: string;
|
|
231
|
-
readonly base_tool?: string;
|
|
232
|
-
readonly description?: string;
|
|
233
|
-
readonly fixed_kwargs?: Record<string, unknown>;
|
|
234
|
-
readonly extensions?: readonly s.PresetExtensionElement[][];
|
|
235
|
-
readonly output_schema?: Record<string, unknown> | null;
|
|
236
|
-
readonly input_schema?: Record<string, unknown> | null;
|
|
237
|
-
// The optional door-layer state binding to dry-run — validated (never mounted) by the
|
|
238
|
-
// same checks the write would run. In VERSION mode it follows the save-version sentinel:
|
|
239
|
-
// omitted carries the active binding forward, an explicit `null` clears it.
|
|
240
|
-
readonly state_binding?: s.StateBinding | null;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Merge-patch body for a tool's overlay row (PATCH
|
|
245
|
-
* `/api/tool-meta/tools/{tool_name}`). Only the fields PRESENT are written; a
|
|
246
|
-
* present-null clears (`hidden: null` writes the tri-state "defer to the plugin
|
|
247
|
-
* declaration" state, `display_name: null` clears the label, `folder_id: null`
|
|
248
|
-
* unfiles); a present `tags` array replaces the whole set. Absent fields are left
|
|
249
|
-
* untouched — there is no full-row replace, so a display-name+tags editor sends
|
|
250
|
-
* ONLY those two keys with no risk to `folder_id`/`hidden`. At least one field must
|
|
251
|
-
* be present. A blank/whitespace-only `display_name` is rejected by the API (422) —
|
|
252
|
-
* callers map an emptied label to `null`, never `""`. A present `badges` array
|
|
253
|
-
* replaces the whole overlay set (INFORMATIONAL labels, never an enforced gate).
|
|
254
|
-
*/
|
|
255
|
-
export interface ToolMetaPatch {
|
|
256
|
-
readonly display_name?: string | null;
|
|
257
|
-
readonly folder_id?: string | null;
|
|
258
|
-
readonly tags?: readonly string[];
|
|
259
|
-
readonly badges?: readonly string[];
|
|
260
|
-
readonly hidden?: boolean | null;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Body for a storage upload (POST `/api/storage/resources`). Exactly ONE of
|
|
265
|
-
* `content_text` (stored verbatim) or `content_base64` (a base64 string decoded to
|
|
266
|
-
* bytes) supplies the content for `id`; uploading an existing id overwrites it
|
|
267
|
-
* (provider passthrough semantics).
|
|
268
|
-
*/
|
|
269
|
-
export interface StorageUploadBody {
|
|
270
|
-
readonly id: string;
|
|
271
|
-
readonly content_text?: string;
|
|
272
|
-
readonly content_base64?: string;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* The thread-list filter set (GET `/api/conversations/{route}/threads` query
|
|
277
|
-
* params, alongside `page`/`pageSize`). `status` narrows to threads whose newest
|
|
278
|
-
* record sits in one delivery state; `address` is a SUBSTRING match over the
|
|
279
|
-
* client address. Both omitted when undefined — an unfiltered listing.
|
|
280
|
-
*/
|
|
281
|
-
export interface ConversationThreadFilters {
|
|
282
|
-
readonly status?: s.ConversationDeliveryStatus;
|
|
283
|
-
readonly address?: string;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* One transcript read (GET `/api/conversations/{route}/transcript` query params).
|
|
288
|
-
* `order` is not a default the caller may skip: `asc` reads the thread from its
|
|
289
|
-
* beginning and `desc` from its newest end, and which one a screen wants is a
|
|
290
|
-
* decision that screen must make. `q` narrows the page to records whose text
|
|
291
|
-
* matches it (a SUBSTRING), omitted for an unfiltered read.
|
|
292
|
-
*/
|
|
293
|
-
export interface ConversationTranscriptQuery {
|
|
294
|
-
readonly routeName: string;
|
|
295
|
-
readonly threadId: string;
|
|
296
|
-
readonly page: number;
|
|
297
|
-
readonly pageSize: number;
|
|
298
|
-
readonly order: s.TranscriptOrder;
|
|
299
|
-
readonly q?: string;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* A route-scoped message search (GET
|
|
304
|
-
* `/api/conversations/{route}/messages/search` query params). `q` is the required
|
|
305
|
-
* needle — a substring over record text, across every thread on the route; `page`
|
|
306
|
-
* is supplied per-request by the infinite query.
|
|
307
|
-
*/
|
|
308
|
-
export interface ConversationMessageSearchQuery {
|
|
309
|
-
readonly routeName: string;
|
|
310
|
-
readonly q: string;
|
|
311
|
-
readonly page: number;
|
|
312
|
-
readonly pageSize: number;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Body for sending a message into a thread (POST
|
|
317
|
-
* `/api/conversations/{route}/thread/messages`). `thread_id` names the thread
|
|
318
|
-
* (an api-door address, passed as a value); `text` is the message. `address` is
|
|
319
|
-
* an optional override the server otherwise derives from the thread, omitted
|
|
320
|
-
* when undefined.
|
|
321
|
-
*/
|
|
322
|
-
export interface ConversationThreadMessageBody {
|
|
323
|
-
readonly thread_id: string;
|
|
324
|
-
readonly text: string;
|
|
325
|
-
readonly address?: string;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/** The observability-metrics window + bucket size (GET query params). */
|
|
329
|
-
export interface MetricsQuery {
|
|
330
|
-
readonly from?: string;
|
|
331
|
-
readonly to?: string;
|
|
332
|
-
readonly granularity?: string;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* The observability runs filter. List/dict-valued params (`tags`) are
|
|
337
|
-
* JSON-encoded into the query string; scalars are sent verbatim. Shared by
|
|
338
|
-
* `listRuns` and the `exportRuns` download.
|
|
339
|
-
*/
|
|
340
|
-
export interface RunsQuery {
|
|
341
|
-
readonly from?: string;
|
|
342
|
-
readonly to?: string;
|
|
343
|
-
readonly tags?: string[];
|
|
344
|
-
readonly status?: 'error' | 'success';
|
|
345
|
-
readonly minCost?: number;
|
|
346
|
-
readonly maxCost?: number;
|
|
347
|
-
readonly minTokens?: number;
|
|
348
|
-
readonly maxTokens?: number;
|
|
349
|
-
readonly minLatencyMs?: number;
|
|
350
|
-
readonly maxLatencyMs?: number;
|
|
351
|
-
readonly sort?: string;
|
|
352
|
-
readonly dir?: 'asc' | 'desc';
|
|
353
|
-
readonly page?: number;
|
|
354
|
-
readonly pageSize?: number;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* The marketplace search filter (GET `/api/marketplace/search` query params).
|
|
359
|
-
* `tags` is sent as REPEATED `tags` params, one per value; `page` is supplied
|
|
360
|
-
* per-request by the infinite query, never from the URL. `tier`, `namespace`,
|
|
361
|
-
* `contract`, and `page_size` are accepted by the route but the v1 UI sends none
|
|
362
|
-
* of them (every v1 row is official/tai42; the page size stays the server
|
|
363
|
-
* default) — they exist for contract completeness.
|
|
364
|
-
*/
|
|
365
|
-
export interface MarketplaceSearchQuery {
|
|
366
|
-
readonly q?: string;
|
|
367
|
-
readonly kind?: string;
|
|
368
|
-
readonly category?: string;
|
|
369
|
-
readonly tags?: string[];
|
|
370
|
-
readonly namespace?: string;
|
|
371
|
-
readonly tier?: string;
|
|
372
|
-
readonly contract?: string;
|
|
373
|
-
readonly sort?: 'downloads' | 'updated' | 'name' | 'relevance';
|
|
374
|
-
readonly page?: number;
|
|
375
|
-
readonly page_size?: number;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Body for installing / updating a plugin. `ref` is `namespace/name`.
|
|
380
|
-
*
|
|
381
|
-
* `env` / `secret_keys` satisfy an mcp-server entry's required `!ENV` markers in
|
|
382
|
-
* the same transaction that writes the entry: `env` supplies the values,
|
|
383
|
-
* `secret_keys` marks which land in the secret band. Both omitted for a plain
|
|
384
|
-
* install (no required markers).
|
|
385
|
-
*
|
|
386
|
-
* `route_mounts` maps a route-carrying item name to the base prefix its routes
|
|
387
|
-
* mount under (the default or the operator's remap); `accept_public_routes`
|
|
388
|
-
* explicitly consents to the routes served without authentication. Omitted for a
|
|
389
|
-
* plugin that registers no routes.
|
|
390
|
-
*/
|
|
391
|
-
export interface MarketplaceInstallBody {
|
|
392
|
-
readonly ref: string;
|
|
393
|
-
readonly version?: string;
|
|
394
|
-
readonly env?: Record<string, string>;
|
|
395
|
-
readonly secret_keys?: string[];
|
|
396
|
-
readonly route_mounts?: Record<string, string>;
|
|
397
|
-
readonly accept_public_routes?: boolean;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Body for previewing an install / update (`POST /api/marketplace/install/preview`):
|
|
402
|
-
* resolves the spec and computes the routes, collisions, and public rows WITHOUT
|
|
403
|
-
* side effects. `route_mounts` applies base overrides so the operator sees the
|
|
404
|
-
* effect of a remap before committing.
|
|
405
|
-
*/
|
|
406
|
-
export interface MarketplaceInstallPreviewBody {
|
|
407
|
-
readonly ref: string;
|
|
408
|
-
readonly version?: string;
|
|
409
|
-
readonly route_mounts?: Record<string, string>;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/** Body for uninstalling a plugin. */
|
|
413
|
-
export interface MarketplaceUninstallBody {
|
|
414
|
-
readonly ref: string;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Encode a possibly-nested storage id / directory path for use in a URL path,
|
|
419
|
-
* percent-encoding each segment while preserving the `/` separators. A storage id
|
|
420
|
-
* can be a nested path (e.g. `a/b c.txt`); encoding each segment independently keeps
|
|
421
|
-
* the slashes structural and escapes everything else (spaces, `#`, `?`, …).
|
|
422
|
-
*
|
|
423
|
-
* Dot segments (`.`, `..`), empty segments (a leading/trailing/doubled `/`, or an
|
|
424
|
-
* empty id) are REJECTED loudly rather than encoded: the browser's WHATWG URL parser
|
|
425
|
-
* removes dot-segments from a relative request path before it is sent — collapsing
|
|
426
|
-
* `..` even when percent-encoded — so such an id can never be faithfully transmitted.
|
|
427
|
-
* Left to encode, it would silently retarget the request at an unrelated same-origin
|
|
428
|
-
* route. The server enforces the same relative-path rule; failing fast here keeps a
|
|
429
|
-
* malformed id from ever leaving the client.
|
|
430
|
-
*/
|
|
431
|
-
function encodePath(id: string): string {
|
|
432
|
-
const segments = id.split('/');
|
|
433
|
-
if (segments.some(isUnsafePathSegment)) {
|
|
434
|
-
throw new Error("storage path must be a relative path with no empty, '.', or '..' segment");
|
|
435
|
-
}
|
|
436
|
-
return segments.map(encodeURIComponent).join('/');
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/** Flatten a `RunsQuery` to transport params, JSON-encoding the `tags` list. */
|
|
440
|
-
function runsQueryToParams(query: RunsQuery): Required<RequestOptions>['query'] {
|
|
441
|
-
return {
|
|
442
|
-
from: query.from,
|
|
443
|
-
to: query.to,
|
|
444
|
-
tags: query.tags === undefined ? undefined : JSON.stringify(query.tags),
|
|
445
|
-
status: query.status,
|
|
446
|
-
minCost: query.minCost,
|
|
447
|
-
maxCost: query.maxCost,
|
|
448
|
-
minTokens: query.minTokens,
|
|
449
|
-
maxTokens: query.maxTokens,
|
|
450
|
-
minLatencyMs: query.minLatencyMs,
|
|
451
|
-
maxLatencyMs: query.maxLatencyMs,
|
|
452
|
-
sort: query.sort,
|
|
453
|
-
dir: query.dir,
|
|
454
|
-
page: query.page,
|
|
455
|
-
pageSize: query.pageSize,
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Whether a login-method target is a safe same-origin relative API path. The
|
|
461
|
-
* login screen is the one page where a misconfigured/compromised manifest could
|
|
462
|
-
* redirect CREDENTIALS off-origin, so a `submit_path`/`href` MUST start `/api/`
|
|
463
|
-
* — anything else (absolute URL, protocol-relative `//host`, missing prefix) is
|
|
464
|
-
* rejected. The `/api/` prefix already excludes `//`-prefixed values; the
|
|
465
|
-
* explicit double-slash clause is kept for readability.
|
|
466
|
-
*/
|
|
467
|
-
export function isSafeApiPath(path: string): boolean {
|
|
468
|
-
return path.startsWith('/api/') && !path.startsWith('//');
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
/**
|
|
472
|
-
* Map a transport error from a login submission onto an `ApiLoginFailedError`
|
|
473
|
-
* (rendered inline on the login page) or rethrow it unchanged (5xx, schema
|
|
474
|
-
* drift, network — the page's generic failure branch surfaces it loudly).
|
|
475
|
-
*
|
|
476
|
-
* A transport 401 arrives as `ApiUnauthorizedError` before any body is read, so
|
|
477
|
-
* its message is generic by construction; the domain 4xx statuses carry the
|
|
478
|
-
* server's informative envelope text (rate-limit, bootstrap-already-initialized,
|
|
479
|
-
* password-policy, or the claim exchange's uniform "unknown or already used"
|
|
480
|
-
* 404) straight through to the inline surface.
|
|
481
|
-
*/
|
|
482
|
-
function asLoginError(error: unknown): never {
|
|
483
|
-
if (error instanceof ApiUnauthorizedError) {
|
|
484
|
-
throw new ApiLoginFailedError('Sign-in failed — check your credentials.', 401);
|
|
485
|
-
}
|
|
486
|
-
if (
|
|
487
|
-
error instanceof ApiError &&
|
|
488
|
-
(error.status === 400 ||
|
|
489
|
-
error.status === 403 ||
|
|
490
|
-
error.status === 404 ||
|
|
491
|
-
error.status === 409 ||
|
|
492
|
-
error.status === 422 ||
|
|
493
|
-
error.status === 429)
|
|
494
|
-
) {
|
|
495
|
-
throw new ApiLoginFailedError(error.message, error.status);
|
|
496
|
-
}
|
|
497
|
-
throw error;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
/**
|
|
501
|
-
* Body for the combined env+manifest secret op (POST `/api/mcp-config/secret-env`).
|
|
502
|
-
* The server generates the env key from `value` (shaping the name from `key_hint`),
|
|
503
|
-
* then writes the env value and the literal `!ENV ${KEY}` manifest leaf at
|
|
504
|
-
* `manifest_pointer` atomically. `manifest_pointer`'s head segment MUST be `mcp`
|
|
505
|
-
* (the server refuses any other with a loud 400).
|
|
506
|
-
*/
|
|
507
|
-
export interface SetMcpSecretEnvBody {
|
|
508
|
-
readonly value: string;
|
|
509
|
-
readonly key_hint: string;
|
|
510
|
-
readonly manifest_pointer: string;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
/**
|
|
514
|
-
* The client-facing write body for a state declaration (`PUT /api/states/{name}`), the
|
|
515
|
-
* wire shape of `tai42_contract.states.StateDeclaration` minus the platform-computed
|
|
516
|
-
* `effective_schema`/`regimes` (those are read-only, refused on a write). `schema` is the
|
|
517
|
-
* `TemplatedText | dict` union: an inline JSON-schema object, or a stored template reference
|
|
518
|
-
* (`id` + render `kwargs`) the platform renders to the schema. `retention_days` is `null` to
|
|
519
|
-
* keep records forever.
|
|
520
|
-
*/
|
|
521
|
-
export interface StateDeclarationBody {
|
|
522
|
-
readonly name: string;
|
|
523
|
-
readonly description?: string;
|
|
524
|
-
readonly schema: s.TemplatedText | Record<string, unknown>;
|
|
525
|
-
readonly subject_kinds: readonly string[];
|
|
526
|
-
readonly default_subject_kind: string;
|
|
527
|
-
readonly retention_days?: number | null;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
/**
|
|
531
|
-
* A state-template document write body (`PUT /api/state-templates/{name}`, the platform half).
|
|
532
|
-
* `schema` is the `TemplatedText | dict` union — an inline fragment schema, or a stored
|
|
533
|
-
* template reference the platform renders to the fragment.
|
|
534
|
-
*/
|
|
535
|
-
export interface StateTemplateBody {
|
|
536
|
-
readonly name: string;
|
|
537
|
-
readonly description?: string;
|
|
538
|
-
readonly parameters?: Record<string, unknown>;
|
|
539
|
-
readonly schema: s.TemplatedText | Record<string, unknown>;
|
|
540
|
-
readonly regimes?: Record<string, unknown>[];
|
|
541
|
-
readonly declarations?: Record<string, unknown> | null;
|
|
542
|
-
readonly trace?: Record<string, unknown>;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
/** A template attach / re-attach body (`PUT|PATCH /api/states/{name}/attachments/{template}`). */
|
|
546
|
-
export interface StateAttachmentBody {
|
|
547
|
-
readonly path?: string[];
|
|
548
|
-
readonly parameters?: Record<string, unknown>;
|
|
549
|
-
readonly declarations?: Record<string, unknown>;
|
|
550
|
-
// Reconcile directive for a re-attach that would orphan open records: retry with
|
|
551
|
-
// `{ orphans: "close", resolution: "<name>" }` to close them (the server refuses
|
|
552
|
-
// without it). Omitted on a first attach.
|
|
553
|
-
readonly options?: Record<string, unknown>;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
/** The four-part subject a record route addresses in its path. */
|
|
557
|
-
export interface StateSubjectRef {
|
|
558
|
-
readonly target_kind: string;
|
|
559
|
-
readonly target_name: string;
|
|
560
|
-
readonly kind: string;
|
|
561
|
-
readonly key: string;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
/** A keyset page request: an optional size and the opaque cursor a prior page returned. */
|
|
565
|
-
export interface StatePageQuery {
|
|
566
|
-
readonly limit?: number;
|
|
567
|
-
readonly cursor?: string;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
/**
|
|
571
|
-
* The subject-addressed record path: `/api/states/{name}/records/{target_kind}/
|
|
572
|
-
* {target_name}/{kind}/{key}` — four path segments, each percent-encoded, so a key
|
|
573
|
-
* carrying a slash or a colon never escapes its segment.
|
|
574
|
-
*/
|
|
575
|
-
function stateRecordPath(name: string, subject: StateSubjectRef): string {
|
|
576
|
-
return (
|
|
577
|
-
`/api/states/${encodeSegment(name)}/records/` +
|
|
578
|
-
`${encodeSegment(subject.target_kind)}/${encodeSegment(subject.target_name)}/` +
|
|
579
|
-
`${encodeSegment(subject.kind)}/${encodeSegment(subject.key)}`
|
|
580
|
-
);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
/**
|
|
584
|
-
* Encode a template jq's declared params as JSON query values: the record `template-jq`
|
|
585
|
-
* route reads each `?<param>=<json>` as a JSON-decoded argument, so a string, number,
|
|
586
|
-
* array or object round-trips faithfully. An `undefined` value is dropped (an
|
|
587
|
-
* omitted optional param).
|
|
588
|
-
*/
|
|
589
|
-
function encodeJqParams(params?: Record<string, unknown>): Record<string, string> | undefined {
|
|
590
|
-
if (params === undefined) return undefined;
|
|
591
|
-
const query: Record<string, string> = {};
|
|
592
|
-
for (const [key, value] of Object.entries(params)) {
|
|
593
|
-
if (value === undefined) continue;
|
|
594
|
-
query[key] = JSON.stringify(value);
|
|
595
|
-
}
|
|
596
|
-
return query;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
export function createApiClient(config: ApiConfig) {
|
|
600
|
-
const req = <S extends Parameters<typeof apiRequest>[2]>(
|
|
601
|
-
path: string,
|
|
602
|
-
schema: S,
|
|
603
|
-
options?: Parameters<typeof apiRequest>[3],
|
|
604
|
-
) => apiRequest(config, path, schema, options);
|
|
605
|
-
|
|
606
|
-
return {
|
|
607
|
-
// The configured API base — the exact `baseUrl` this client was constructed
|
|
608
|
-
// with (`''` for a same-origin deployment). Read-only: features that must
|
|
609
|
-
// compose an ABSOLUTE API URL (a QR-encoded public path the API origin must
|
|
610
|
-
// receive, not the SPA origin) read it instead of assuming same-origin. The
|
|
611
|
-
// transport itself still joins it per request; this only surfaces the value.
|
|
612
|
-
baseUrl: config.baseUrl ?? '',
|
|
613
|
-
|
|
614
|
-
// -- tools ---------------------------------------------------------------
|
|
615
|
-
listTools: (signal?: AbortSignal) => req('/api/tools', s.toolNames, { signal }),
|
|
616
|
-
getToolSchema: (name: string, signal?: AbortSignal) =>
|
|
617
|
-
req(`/api/tools/${encodeSegment(name)}/schema`, s.toolSchema, { signal }),
|
|
618
|
-
getAllToolSchemas: (signal?: AbortSignal) =>
|
|
619
|
-
req('/api/tools-schema', s.allToolSchemas, { signal }),
|
|
620
|
-
runTool: (args: RunToolArgs, signal?: AbortSignal) =>
|
|
621
|
-
req('/api/run-tool', s.runToolResult, {
|
|
622
|
-
method: 'POST',
|
|
623
|
-
// The backend's `/api/run-tool` door reads `{tool_name, arguments}` (the
|
|
624
|
-
// shape `read_tool_call` enforces and the background `/api/tool-runs`
|
|
625
|
-
// submit already sends). The SPA keeps `tool`/`kwargs` as its ergonomic
|
|
626
|
-
// arg names; map them onto the wire fields here.
|
|
627
|
-
body: { tool_name: args.tool, arguments: args.kwargs ?? {} },
|
|
628
|
-
signal,
|
|
629
|
-
}),
|
|
630
|
-
|
|
631
|
-
// -- app tool live-registry admin ----------------------------------------
|
|
632
|
-
// Re-register or remove one app tool by kind+name, applied on this worker and
|
|
633
|
-
// broadcast to the fleet (all workers, or only `targets`); both return the bare
|
|
634
|
-
// per-worker `fleetResult` the shared fleet-report handler renders. Both are
|
|
635
|
-
// fenced + destructive server-side, so a refusal surfaces as a loud error.
|
|
636
|
-
reloadTool: (args: ToolAdminArgs) =>
|
|
637
|
-
req('/api/tools/reload', s.fleetResult, {
|
|
638
|
-
method: 'POST',
|
|
639
|
-
body: { kind: args.kind, name: args.name, targets: args.targets ?? null },
|
|
640
|
-
}),
|
|
641
|
-
removeTool: (args: ToolAdminArgs) =>
|
|
642
|
-
req('/api/tools/remove', s.fleetResult, {
|
|
643
|
-
method: 'POST',
|
|
644
|
-
body: { kind: args.kind, name: args.name, targets: args.targets ?? null },
|
|
645
|
-
}),
|
|
646
|
-
|
|
647
|
-
// -- background tool runs ------------------------------------------------
|
|
648
|
-
submitToolRun: (args: SubmitToolRunArgs, signal?: AbortSignal) =>
|
|
649
|
-
submitToolRun(config, args, signal),
|
|
650
|
-
getToolRun: (runId: string, signal?: AbortSignal) => getToolRun(config, runId, signal),
|
|
651
|
-
listToolRuns: (toolName: string, signal?: AbortSignal) =>
|
|
652
|
-
listToolRuns(config, toolName, signal),
|
|
653
|
-
|
|
654
|
-
// -- tool tags (additive map feeding the ToolPicker tag grouping) --------
|
|
655
|
-
listToolTags: (signal?: AbortSignal) => req('/api/tools/tags', s.toolTags, { signal }),
|
|
656
|
-
|
|
657
|
-
// -- presets -------------------------------------------------------------
|
|
658
|
-
// Every preset is store-backed and versioned; the list is the single
|
|
659
|
-
// management population (the presets plus the conflicted quarantined rows).
|
|
660
|
-
listPresets: (signal?: AbortSignal) => req('/api/presets', s.presetList, { signal }),
|
|
661
|
-
createPreset: (body: CreatePresetBody) =>
|
|
662
|
-
req('/api/presets', s.presetRecord, { method: 'POST', body }),
|
|
663
|
-
getPreset: (name: string, signal?: AbortSignal) =>
|
|
664
|
-
req(`/api/presets/${encodeSegment(name)}`, s.presetDetail, { signal }),
|
|
665
|
-
listPresetVersions: (name: string, signal?: AbortSignal) =>
|
|
666
|
-
req(`/api/presets/${encodeSegment(name)}/versions`, s.presetVersionList, { signal }),
|
|
667
|
-
getPresetVersion: (name: string, version: number, signal?: AbortSignal) =>
|
|
668
|
-
req(
|
|
669
|
-
`/api/presets/${encodeSegment(name)}/versions/${encodeSegment(version)}`,
|
|
670
|
-
s.presetVersion,
|
|
671
|
-
{ signal },
|
|
672
|
-
),
|
|
673
|
-
savePresetVersion: (name: string, body: SavePresetVersionBody) =>
|
|
674
|
-
req(`/api/presets/${encodeSegment(name)}/versions`, s.presetVersion, {
|
|
675
|
-
method: 'POST',
|
|
676
|
-
body,
|
|
677
|
-
}),
|
|
678
|
-
rollbackPreset: (name: string, version: number) =>
|
|
679
|
-
req(`/api/presets/${encodeSegment(name)}/rollback`, s.presetRollback, {
|
|
680
|
-
method: 'POST',
|
|
681
|
-
body: { version },
|
|
682
|
-
}),
|
|
683
|
-
deletePreset: (name: string) =>
|
|
684
|
-
req(`/api/presets/${encodeSegment(name)}`, s.presetDeleted, { method: 'DELETE' }),
|
|
685
|
-
renamePreset: (name: string, newName: string) =>
|
|
686
|
-
req(`/api/presets/${encodeSegment(name)}/rename`, s.presetRenamed, {
|
|
687
|
-
method: 'POST',
|
|
688
|
-
body: { new_name: newName },
|
|
689
|
-
}),
|
|
690
|
-
// The presets whose active composition names this one as a tool — the referees
|
|
691
|
-
// a rename would strand. A rename preflight reads this to warn before the 409.
|
|
692
|
-
getPresetReferees: (name: string, signal?: AbortSignal) =>
|
|
693
|
-
req(`/api/presets/${encodeSegment(name)}/referees`, s.presetReferees, { signal }),
|
|
694
|
-
// The dry-run verdict for a draft (create OR version, mode-resolved server-side):
|
|
695
|
-
// `valid` plus the server's verbatim `error` message. Both verdicts are HTTP 200.
|
|
696
|
-
validatePreset: (body: ValidatePresetBody) =>
|
|
697
|
-
req('/api/presets/validate', s.presetValidation, { method: 'POST', body }),
|
|
698
|
-
// Replace one version's `tags` annotation — labels on an immutable body, so no
|
|
699
|
-
// rebind. Returns the re-annotated identity + new tag list.
|
|
700
|
-
setPresetVersionTags: (name: string, version: number, tags: readonly string[]) =>
|
|
701
|
-
req(
|
|
702
|
-
`/api/presets/${encodeSegment(name)}/versions/${encodeSegment(version)}/tags`,
|
|
703
|
-
s.presetVersionTags,
|
|
704
|
-
{ method: 'PUT', body: { tags } },
|
|
705
|
-
),
|
|
706
|
-
|
|
707
|
-
// -- states --------------------------------------------------------------
|
|
708
|
-
// A state is a declared JSON document, one per subject. The list is the
|
|
709
|
-
// management population; a single state read composes the declaration with its
|
|
710
|
-
// effective schema, regimes and attachments. Every record route addresses its subject
|
|
711
|
-
// as four percent-encoded path segments (see `stateRecordPath`).
|
|
712
|
-
listStates: (signal?: AbortSignal) => req('/api/states', s.stateList, { signal }),
|
|
713
|
-
getState: (name: string, signal?: AbortSignal) =>
|
|
714
|
-
req(`/api/states/${encodeSegment(name)}`, s.stateDetail, { signal }),
|
|
715
|
-
// A plain declaration upsert — no `replace` flag. With records present the server
|
|
716
|
-
// accepts additive schema changes and refuses with a 409 any change that removes or
|
|
717
|
-
// alters an existing field, or a subject-kind removal that would strand records.
|
|
718
|
-
putState: (name: string, body: StateDeclarationBody) =>
|
|
719
|
-
req(`/api/states/${encodeSegment(name)}`, s.stateDetail, {
|
|
720
|
-
method: 'PUT',
|
|
721
|
-
body,
|
|
722
|
-
}),
|
|
723
|
-
deleteState: (name: string) =>
|
|
724
|
-
req(`/api/states/${encodeSegment(name)}`, s.stateDeleted, { method: 'DELETE' }),
|
|
725
|
-
getStateStats: (name: string, signal?: AbortSignal) =>
|
|
726
|
-
req(`/api/states/${encodeSegment(name)}/stats`, s.stateStats, { signal }),
|
|
727
|
-
// -- state template attachments ------------------------------------------
|
|
728
|
-
listStateAttachments: (name: string, signal?: AbortSignal) =>
|
|
729
|
-
req(`/api/states/${encodeSegment(name)}/attachments`, s.stateAttachmentList, { signal }),
|
|
730
|
-
getStateAttachment: (name: string, template: string, signal?: AbortSignal) =>
|
|
731
|
-
req(
|
|
732
|
-
`/api/states/${encodeSegment(name)}/attachments/${encodeSegment(template)}`,
|
|
733
|
-
s.stateAttachment,
|
|
734
|
-
{ signal },
|
|
735
|
-
),
|
|
736
|
-
attachStateTemplate: (name: string, template: string, body: StateAttachmentBody) =>
|
|
737
|
-
req(
|
|
738
|
-
`/api/states/${encodeSegment(name)}/attachments/${encodeSegment(template)}`,
|
|
739
|
-
s.stateAttached,
|
|
740
|
-
{ method: 'PUT', body },
|
|
741
|
-
),
|
|
742
|
-
patchStateAttachment: (name: string, template: string, body: StateAttachmentBody) =>
|
|
743
|
-
req(
|
|
744
|
-
`/api/states/${encodeSegment(name)}/attachments/${encodeSegment(template)}`,
|
|
745
|
-
s.stateAttachmentUpdated,
|
|
746
|
-
{ method: 'PATCH', body },
|
|
747
|
-
),
|
|
748
|
-
detachStateTemplate: (name: string, template: string) =>
|
|
749
|
-
req(
|
|
750
|
-
`/api/states/${encodeSegment(name)}/attachments/${encodeSegment(template)}`,
|
|
751
|
-
s.stateDetached,
|
|
752
|
-
{ method: 'DELETE' },
|
|
753
|
-
),
|
|
754
|
-
// -- state subjects + records -------------------------------------------
|
|
755
|
-
listStateSubjects: (
|
|
756
|
-
name: string,
|
|
757
|
-
params: { kind?: string } & StatePageQuery,
|
|
758
|
-
signal?: AbortSignal,
|
|
759
|
-
) =>
|
|
760
|
-
req(`/api/states/${encodeSegment(name)}/subjects`, s.subjectPage, {
|
|
761
|
-
signal,
|
|
762
|
-
query: { kind: params.kind, limit: params.limit, cursor: params.cursor },
|
|
763
|
-
}),
|
|
764
|
-
// The content search body is a single JSONB containment document (`filters`); the
|
|
765
|
-
// server refuses an empty one, so the UI validates a non-empty object first.
|
|
766
|
-
searchStateRecords: (
|
|
767
|
-
name: string,
|
|
768
|
-
body: { filters: Record<string, unknown> } & StatePageQuery,
|
|
769
|
-
signal?: AbortSignal,
|
|
770
|
-
) =>
|
|
771
|
-
req(`/api/states/${encodeSegment(name)}/records/search`, s.recordSearchPage, {
|
|
772
|
-
method: 'POST',
|
|
773
|
-
body,
|
|
774
|
-
signal,
|
|
775
|
-
}),
|
|
776
|
-
// A subject with no document yet reads as `null` (a 200 `{data: null}`, the record
|
|
777
|
-
// page's Create path) — never an error, so the schema is nullable.
|
|
778
|
-
getStateRecord: (name: string, subject: StateSubjectRef, signal?: AbortSignal) =>
|
|
779
|
-
req(stateRecordPath(name, subject), s.stateRecord.nullable(), { signal }),
|
|
780
|
-
putStateRecord: (name: string, subject: StateSubjectRef, data: Record<string, unknown>) =>
|
|
781
|
-
req(stateRecordPath(name, subject), s.stateRecord, { method: 'PUT', body: data }),
|
|
782
|
-
patchStateRecord: (name: string, subject: StateSubjectRef, data: Record<string, unknown>) =>
|
|
783
|
-
req(stateRecordPath(name, subject), s.stateRecord, { method: 'PATCH', body: data }),
|
|
784
|
-
applyStateRecord: (
|
|
785
|
-
name: string,
|
|
786
|
-
subject: StateSubjectRef,
|
|
787
|
-
ops: readonly Record<string, unknown>[],
|
|
788
|
-
) =>
|
|
789
|
-
req(`${stateRecordPath(name, subject)}/deltas`, s.applyResult, {
|
|
790
|
-
method: 'POST',
|
|
791
|
-
body: { ops },
|
|
792
|
-
}),
|
|
793
|
-
// Evaluate a template jq over the subject: each declared param is JSON-encoded into
|
|
794
|
-
// the query string (`?<param>=<json>`); the response is `{name, purpose, value}`. An
|
|
795
|
-
// `attachment.name` jq is percent-encoded as one path segment.
|
|
796
|
-
evalTemplateJq: (
|
|
797
|
-
name: string,
|
|
798
|
-
subject: StateSubjectRef,
|
|
799
|
-
jqName: string,
|
|
800
|
-
params?: Record<string, unknown>,
|
|
801
|
-
signal?: AbortSignal,
|
|
802
|
-
) =>
|
|
803
|
-
req(
|
|
804
|
-
`${stateRecordPath(name, subject)}/template-jq/${encodeSegment(jqName)}`,
|
|
805
|
-
s.templateJqResult,
|
|
806
|
-
{ signal, query: encodeJqParams(params) },
|
|
807
|
-
),
|
|
808
|
-
// Apply an `update`-purpose template jq to the subject: `input` is the jq's argument,
|
|
809
|
-
// `op_id` its idempotency key (a replay is a no-op). The jq lands its op batch through
|
|
810
|
-
// the apply chokepoint. An `attachment.name` jq is percent-encoded as one path segment.
|
|
811
|
-
applyTemplateJq: (
|
|
812
|
-
name: string,
|
|
813
|
-
subject: StateSubjectRef,
|
|
814
|
-
jqName: string,
|
|
815
|
-
body?: { input?: unknown; op_id?: string },
|
|
816
|
-
) =>
|
|
817
|
-
req(
|
|
818
|
-
`${stateRecordPath(name, subject)}/template-jq/${encodeSegment(jqName)}`,
|
|
819
|
-
s.templateJqApplyResult,
|
|
820
|
-
{ method: 'POST', body: body ?? {} },
|
|
821
|
-
),
|
|
822
|
-
deleteStateRecord: (name: string, subject: StateSubjectRef) =>
|
|
823
|
-
req(stateRecordPath(name, subject), s.recordErased, { method: 'DELETE' }),
|
|
824
|
-
// `mode` is required server-side (`switch` drops, `merge` combines); the fold returns
|
|
825
|
-
// a report, not the surviving record.
|
|
826
|
-
foldStateRecord: (
|
|
827
|
-
name: string,
|
|
828
|
-
subject: StateSubjectRef,
|
|
829
|
-
into: StateSubjectRef,
|
|
830
|
-
mode: 'switch' | 'merge',
|
|
831
|
-
) =>
|
|
832
|
-
req(`${stateRecordPath(name, subject)}/fold`, s.stateFoldReport, {
|
|
833
|
-
method: 'POST',
|
|
834
|
-
body: { into, mode },
|
|
835
|
-
}),
|
|
836
|
-
listStateWrites: (
|
|
837
|
-
name: string,
|
|
838
|
-
subject: StateSubjectRef,
|
|
839
|
-
params?: StatePageQuery,
|
|
840
|
-
signal?: AbortSignal,
|
|
841
|
-
) =>
|
|
842
|
-
req(`${stateRecordPath(name, subject)}/writes`, s.writesPage, {
|
|
843
|
-
signal,
|
|
844
|
-
query: { limit: params?.limit, cursor: params?.cursor },
|
|
845
|
-
}),
|
|
846
|
-
stateConsumers: (name: string, signal?: AbortSignal) =>
|
|
847
|
-
req(`/api/states/${encodeSegment(name)}/consumers`, s.stateConsumers, { signal }),
|
|
848
|
-
// -- state-template documents (top-level sibling collection) ---------------
|
|
849
|
-
listStateTemplates: (signal?: AbortSignal) =>
|
|
850
|
-
req('/api/state-templates', s.stateTemplateList, { signal }),
|
|
851
|
-
getStateTemplate: (name: string, signal?: AbortSignal) =>
|
|
852
|
-
req(`/api/state-templates/${encodeSegment(name)}`, s.stateTemplateDocument, { signal }),
|
|
853
|
-
putStateTemplate: (name: string, body: StateTemplateBody, replace?: boolean) =>
|
|
854
|
-
req(`/api/state-templates/${encodeSegment(name)}`, s.stateTemplateDocument, {
|
|
855
|
-
method: 'PUT',
|
|
856
|
-
body,
|
|
857
|
-
query: replace === true ? { replace: 'true' } : undefined,
|
|
858
|
-
}),
|
|
859
|
-
deleteStateTemplate: (name: string) =>
|
|
860
|
-
req(`/api/state-templates/${encodeSegment(name)}`, s.stateTemplateDeleted, {
|
|
861
|
-
method: 'DELETE',
|
|
862
|
-
}),
|
|
863
|
-
// -- state retention -----------------------------------------------------
|
|
864
|
-
pruneStateRetention: () =>
|
|
865
|
-
req('/api/state-retention/prune', s.stateRetentionPruned, { method: 'POST', body: {} }),
|
|
866
|
-
|
|
867
|
-
// -- tool_meta (the organizational overlay) ------------------------------
|
|
868
|
-
// The folder tree + per-tool overlay rows in one read; the features merge the
|
|
869
|
-
// rows against the live tool list and native tags client-side.
|
|
870
|
-
listToolMeta: (signal?: AbortSignal) => req('/api/tool-meta', s.toolMetaOverlay, { signal }),
|
|
871
|
-
// Merge-patch a tool's overlay row (create it if absent). `patch` carries ONLY
|
|
872
|
-
// the fields to change — a present-null clears, an absent field is untouched,
|
|
873
|
-
// and a present `tags` array replaces the set.
|
|
874
|
-
upsertToolMeta: (toolName: string, patch: ToolMetaPatch) =>
|
|
875
|
-
req(`/api/tool-meta/tools/${encodeSegment(toolName)}`, s.toolMetaRecord, {
|
|
876
|
-
method: 'PATCH',
|
|
877
|
-
body: patch,
|
|
878
|
-
}),
|
|
879
|
-
deleteToolMeta: (toolName: string) =>
|
|
880
|
-
req(`/api/tool-meta/tools/${encodeSegment(toolName)}`, s.toolMetaDeleted, {
|
|
881
|
-
method: 'DELETE',
|
|
882
|
-
}),
|
|
883
|
-
createFolder: (name: string, parentId: string | null = null) =>
|
|
884
|
-
req('/api/tool-meta/folders', s.folderRecord, {
|
|
885
|
-
method: 'POST',
|
|
886
|
-
body: { name, parent_id: parentId },
|
|
887
|
-
}),
|
|
888
|
-
renameFolder: (folderId: string, name: string) =>
|
|
889
|
-
req(`/api/tool-meta/folders/${encodeSegment(folderId)}/rename`, s.folderRecord, {
|
|
890
|
-
method: 'POST',
|
|
891
|
-
body: { name },
|
|
892
|
-
}),
|
|
893
|
-
// `null` re-parents to the root.
|
|
894
|
-
moveFolder: (folderId: string, parentId: string | null) =>
|
|
895
|
-
req(`/api/tool-meta/folders/${encodeSegment(folderId)}/move`, s.folderRecord, {
|
|
896
|
-
method: 'POST',
|
|
897
|
-
body: { parent_id: parentId },
|
|
898
|
-
}),
|
|
899
|
-
deleteFolder: (folderId: string) =>
|
|
900
|
-
req(`/api/tool-meta/folders/${encodeSegment(folderId)}`, s.folderDeleted, {
|
|
901
|
-
method: 'DELETE',
|
|
902
|
-
}),
|
|
903
|
-
|
|
904
|
-
// -- storage -------------------------------------------------------------
|
|
905
|
-
// The content store a storage-provider plugin exposes. Dead by default:
|
|
906
|
-
// `getStorageInfo` reports `present: false` (a 200 empty state) and every other
|
|
907
|
-
// door answers a loud 501 when no provider is installed. Ids may be nested
|
|
908
|
-
// paths, so each is `encodePath`-encoded (per-segment, `/` preserved).
|
|
909
|
-
getStorageInfo: (signal?: AbortSignal) => req('/api/storage', s.storageInfo, { signal }),
|
|
910
|
-
listStorageResources: (signal?: AbortSignal) =>
|
|
911
|
-
req('/api/storage/resources', s.storageResourceList, { signal }),
|
|
912
|
-
statStorageResource: (id: string, signal?: AbortSignal) =>
|
|
913
|
-
req(`/api/storage/resources/${encodePath(id)}/stat`, s.storageResourceStat, { signal }),
|
|
914
|
-
downloadStorageResource: (id: string, signal?: AbortSignal): Promise<Blob> =>
|
|
915
|
-
apiDownload(config, `/api/storage/resources/${encodePath(id)}/content`, { signal }),
|
|
916
|
-
uploadStorageResource: (body: StorageUploadBody) =>
|
|
917
|
-
req('/api/storage/resources', s.storageResourceStored, { method: 'POST', body }),
|
|
918
|
-
deleteStorageResource: (id: string) =>
|
|
919
|
-
req(`/api/storage/resources/${encodePath(id)}`, s.storageResourceDeleted, {
|
|
920
|
-
method: 'DELETE',
|
|
921
|
-
}),
|
|
922
|
-
deleteStorageDir: (path: string) =>
|
|
923
|
-
req(`/api/storage/dirs/${encodePath(path)}`, s.storageDirDeleted, { method: 'DELETE' }),
|
|
924
|
-
|
|
925
|
-
// -- backend identity + fleet (worker bus) -------------------------------
|
|
926
|
-
// Backend IDENTITY (`getBackendInfo`, kept on `/api/backend`) is distinct from
|
|
927
|
-
// the FLEET doors, which ride the app's worker bus and need no backend at all.
|
|
928
|
-
// `getBackendInfo` reports `present: false` (a 200 empty state) when no backend
|
|
929
|
-
// plugin is registered. `listFleetWorkers` reads the live bus presence census
|
|
930
|
-
// (every subscribed worker, ASGI + backend-runtime) and never fabricates an empty
|
|
931
|
-
// fleet — a census read that fails surfaces as a loud 500.
|
|
932
|
-
getBackendInfo: (signal?: AbortSignal) => req('/api/backend', s.backendInfo, { signal }),
|
|
933
|
-
listFleetWorkers: (signal?: AbortSignal) =>
|
|
934
|
-
req('/api/fleet/workers', s.fleetWorkers, { signal }),
|
|
935
|
-
// Soft-restart the fleet: `targets` names the workers to reload, or `null` for
|
|
936
|
-
// the whole fleet. The serving worker applies locally then broadcasts; a 200
|
|
937
|
-
// response carries the per-worker fleet report, so workers that did not converge
|
|
938
|
-
// are named, not hidden. A failed local apply instead re-raises as a loud error
|
|
939
|
-
// (the transport throws with the failure message), not a returned report.
|
|
940
|
-
reloadFleetConfig: (targets: string[] | null) =>
|
|
941
|
-
req('/api/fleet/reload-config', s.fleetReloadResult, { method: 'POST', body: { targets } }),
|
|
942
|
-
|
|
943
|
-
// -- extensions ----------------------------------------------------------
|
|
944
|
-
listExtensions: (signal?: AbortSignal) => req('/api/extensions', s.extensions, { signal }),
|
|
945
|
-
// A MANIFEST-provided tool's current combos + the extension catalog. (A preset
|
|
946
|
-
// tool's extensions live on the preset spec, read via `getPreset`.)
|
|
947
|
-
getToolExtensions: (name: string, signal?: AbortSignal) =>
|
|
948
|
-
req(`/api/tools/${encodeSegment(name)}/extensions`, s.toolExtensions, { signal }),
|
|
949
|
-
// Author ALL of a MANIFEST tool's combos at once (the full lossless list; `[]`
|
|
950
|
-
// clears). Returns the apply result: the local reload (`status` + `env_keys`) plus
|
|
951
|
-
// the mode-wrapped fleet `fanout` of the reload it broadcast, from which the shared
|
|
952
|
-
// fleet-report handler surfaces any failed propagation.
|
|
953
|
-
setToolExtensions: (name: string, combos: readonly s.PresetExtensionElement[][]) =>
|
|
954
|
-
req(`/api/tools/${encodeSegment(name)}/extensions`, s.toolExtensionsApplyResult, {
|
|
955
|
-
method: 'POST',
|
|
956
|
-
body: { combos },
|
|
957
|
-
}),
|
|
958
|
-
|
|
959
|
-
// -- templates -----------------------------------------------------------
|
|
960
|
-
listTemplates: (signal?: AbortSignal) => req('/api/templates', s.templateNames, { signal }),
|
|
961
|
-
getTemplate: (templateId: string) =>
|
|
962
|
-
req('/api/template', s.templateDetail, { method: 'POST', body: { template_id: templateId } }),
|
|
963
|
-
uploadTemplate: (path: string, content: string) =>
|
|
964
|
-
req('/api/upload-template', s.templateUploaded, { method: 'POST', body: { path, content } }),
|
|
965
|
-
deleteTemplate: (path: string) =>
|
|
966
|
-
req('/api/delete-template', s.templateDeleted, { method: 'POST', body: { path } }),
|
|
967
|
-
// Delete every stored template under a directory prefix. Unlike the idempotent
|
|
968
|
-
// single delete, the server 404s a prefix matching nothing and 400s the template
|
|
969
|
-
// root; those surface as loud errors, never a faked success.
|
|
970
|
-
deleteTemplateDir: (path: string) =>
|
|
971
|
-
req('/api/delete-template-dir', s.templateDirDeleted, { method: 'POST', body: { path } }),
|
|
972
|
-
// Render one authored text — inline `content` OR a stored template `id`, plus its
|
|
973
|
-
// render `kwargs` — and parse the rendered output. The value is the shared
|
|
974
|
-
// `TemplatedText` (exactly one source); the route wraps it under `text`.
|
|
975
|
-
renderTemplate: (text: s.TemplatedText) =>
|
|
976
|
-
req('/api/render-template', s.templateRendered, { method: 'POST', body: { text } }),
|
|
977
|
-
clearTemplatesCache: () =>
|
|
978
|
-
req('/api/clear-templates-cache', s.templateCacheCleared, { method: 'POST' }),
|
|
979
|
-
|
|
980
|
-
// -- manifest / mcp ------------------------------------------------------
|
|
981
|
-
getManifest: (signal?: AbortSignal) => req('/api/manifest', s.manifestView, { signal }),
|
|
982
|
-
setMcpConfig: (mcp: unknown[]) =>
|
|
983
|
-
req('/api/mcp-config', s.reloadConfigResult, { method: 'POST', body: { mcp } }),
|
|
984
|
-
getMcpStatus: (signal?: AbortSignal) => req('/api/mcp-status', s.mcpStatus, { signal }),
|
|
985
|
-
reloadMcp: (title: string) =>
|
|
986
|
-
req(`/api/mcp-status/${encodeSegment(title)}/reload`, s.mcpReloadResult, {
|
|
987
|
-
method: 'POST',
|
|
988
|
-
}),
|
|
989
|
-
|
|
990
|
-
// Granular manifest-section entry edits — add/replace or remove ONE entry by
|
|
991
|
-
// title, leaving the rest of the section untouched (the surgical counterpart to
|
|
992
|
-
// the whole-section `setMcpConfig`). Each crosses the config pipeline and returns
|
|
993
|
-
// the shared apply-result (`status` + `env_keys` + fleet `fanout`). `replace` lets
|
|
994
|
-
// an entry whose title already exists swap in; without it a collision is refused.
|
|
995
|
-
addToolsEntries: (entries: readonly unknown[], replace = false) =>
|
|
996
|
-
req('/api/tools-config/entries', s.reloadConfigResult, {
|
|
997
|
-
method: 'POST',
|
|
998
|
-
body: { entries, replace },
|
|
999
|
-
}),
|
|
1000
|
-
removeToolsEntry: (title: string) =>
|
|
1001
|
-
req(`/api/tools-config/entries/${encodeSegment(title)}`, s.reloadConfigResult, {
|
|
1002
|
-
method: 'DELETE',
|
|
1003
|
-
}),
|
|
1004
|
-
addAgentsEntries: (entries: readonly unknown[], replace = false) =>
|
|
1005
|
-
req('/api/agents-config/entries', s.reloadConfigResult, {
|
|
1006
|
-
method: 'POST',
|
|
1007
|
-
body: { entries, replace },
|
|
1008
|
-
}),
|
|
1009
|
-
removeAgentsEntry: (title: string) =>
|
|
1010
|
-
req(`/api/agents-config/entries/${encodeSegment(title)}`, s.reloadConfigResult, {
|
|
1011
|
-
method: 'DELETE',
|
|
1012
|
-
}),
|
|
1013
|
-
// Edit the manifest `api_tools` include/exclude name lists by delta (add/remove
|
|
1014
|
-
// names on either list). Absent fields default to empty; the server refuses a body
|
|
1015
|
-
// whose four lists are all empty. Returns the shared apply-result.
|
|
1016
|
-
updateApiTools: (body: ApiToolsListsBody) =>
|
|
1017
|
-
req('/api/api-tools', s.reloadConfigResult, {
|
|
1018
|
-
method: 'POST',
|
|
1019
|
-
body: {
|
|
1020
|
-
include_add: body.include_add ?? [],
|
|
1021
|
-
include_remove: body.include_remove ?? [],
|
|
1022
|
-
exclude_add: body.exclude_add ?? [],
|
|
1023
|
-
exclude_remove: body.exclude_remove ?? [],
|
|
1024
|
-
},
|
|
1025
|
-
}),
|
|
1026
|
-
|
|
1027
|
-
// -- failed-MCP health ---------------------------------------------------
|
|
1028
|
-
// The MCP servers skipped by the viability check, plus their remediation ops. The
|
|
1029
|
-
// LIST rides the fleet fan-out primitive (each worker's failed roster arrives as
|
|
1030
|
-
// its own report payload; `failedMcpsFromReport` unions them). Reload-all re-probes
|
|
1031
|
-
// every failed server; deregister detaches one server's tools by title. Each op
|
|
1032
|
-
// returns the bare per-worker `fleetResult`.
|
|
1033
|
-
listFailedMcps: (signal?: AbortSignal) =>
|
|
1034
|
-
req('/api/mcp-status/failed', s.fleetResult, { signal }),
|
|
1035
|
-
reloadFailedMcps: () =>
|
|
1036
|
-
req('/api/mcp-status/reload-failed', s.fleetResult, {
|
|
1037
|
-
method: 'POST',
|
|
1038
|
-
body: { targets: null },
|
|
1039
|
-
}),
|
|
1040
|
-
deregisterMcp: (title: string) =>
|
|
1041
|
-
req(`/api/mcp-status/${encodeSegment(title)}/deregister`, s.fleetResult, {
|
|
1042
|
-
method: 'POST',
|
|
1043
|
-
body: { targets: null },
|
|
1044
|
-
}),
|
|
1045
|
-
|
|
1046
|
-
// -- sub-mcp -------------------------------------------------------------
|
|
1047
|
-
listSubMcp: (signal?: AbortSignal) => req('/api/sub-mcp', s.subMcpList, { signal }),
|
|
1048
|
-
// `transport` is optional at the client boundary (the server defaults it to
|
|
1049
|
-
// `http`); when omitted, JSON.stringify drops the undefined key from the body.
|
|
1050
|
-
createSubMcp: (slug: string, tools: string[], transport?: string) =>
|
|
1051
|
-
req('/api/sub-mcp', s.subMcpCreated, { method: 'POST', body: { slug, tools, transport } }),
|
|
1052
|
-
deleteSubMcp: (slug: string) =>
|
|
1053
|
-
req(`/api/sub-mcp/${encodeSegment(slug)}`, s.subMcpRemoved, { method: 'DELETE' }),
|
|
1054
|
-
|
|
1055
|
-
// -- config --------------------------------------------------------------
|
|
1056
|
-
getEnvConfig: (signal?: AbortSignal) => req('/api/config/env', s.envConfig, { signal }),
|
|
1057
|
-
setEnvConfig: (env: Record<string, string>) =>
|
|
1058
|
-
req('/api/config/env', s.reloadConfigResult, { method: 'POST', body: env }),
|
|
1059
|
-
getConfigMode: (signal?: AbortSignal) => req('/api/config/mode', s.configMode, { signal }),
|
|
1060
|
-
// The LOCAL soft-restart door (distinct from the System page's fleet door
|
|
1061
|
-
// `/api/fleet/reload-config`): refresh env from the config manager, reset the
|
|
1062
|
-
// settings caches, and re-initialize from the manifest — applied on the serving
|
|
1063
|
-
// worker and broadcast to the fleet (all workers, or only `targets`; `null` = all).
|
|
1064
|
-
// Admin-`fenced`; the 200 body is the bare per-worker fleet report so an
|
|
1065
|
-
// unconverged sibling is visible, never hidden.
|
|
1066
|
-
reloadConfig: (targets: string[] | null = null) =>
|
|
1067
|
-
req('/api/config/reload', s.fleetReloadResult, { method: 'POST', body: { targets } }),
|
|
1068
|
-
|
|
1069
|
-
// -- settings profiles ---------------------------------------------------
|
|
1070
|
-
// The admin-only `/api/config/profiles/*` surface. Bodies/diff/versions carry
|
|
1071
|
-
// REAL secret values (secret/fenced routes); masking is CLIENT-SIDE.
|
|
1072
|
-
listSettingsProfiles: (signal?: AbortSignal) =>
|
|
1073
|
-
req('/api/config/profiles', s.settingsProfileList, { signal }),
|
|
1074
|
-
getSettingsProfile: (name: string, signal?: AbortSignal) =>
|
|
1075
|
-
req(`/api/config/profiles/${encodeSegment(name)}`, s.settingsProfileBody, { signal }),
|
|
1076
|
-
putSettingsProfile: (name: string, body: s.SettingsProfileBody) =>
|
|
1077
|
-
req(`/api/config/profiles/${encodeSegment(name)}`, s.settingsProfileSaved, {
|
|
1078
|
-
method: 'PUT',
|
|
1079
|
-
body,
|
|
1080
|
-
}),
|
|
1081
|
-
deleteSettingsProfile: (name: string) =>
|
|
1082
|
-
req(`/api/config/profiles/${encodeSegment(name)}`, s.settingsProfileDeleted, {
|
|
1083
|
-
method: 'DELETE',
|
|
1084
|
-
}),
|
|
1085
|
-
// Diff of the SAVED profile vs the stored env — server-computed, no request body.
|
|
1086
|
-
diffSettingsProfile: (name: string) =>
|
|
1087
|
-
req(`/api/config/profiles/${encodeSegment(name)}/diff`, s.settingsProfileDiff, {
|
|
1088
|
-
method: 'POST',
|
|
1089
|
-
}),
|
|
1090
|
-
// Apply the SAVED profile (full-replace of the profile-managed band). Fenced,
|
|
1091
|
-
// destructive → the dedicated `profile_apply_response` report.
|
|
1092
|
-
applySettingsProfile: (name: string) =>
|
|
1093
|
-
req(`/api/config/profiles/${encodeSegment(name)}/apply`, s.profileApplyResponse, {
|
|
1094
|
-
method: 'POST',
|
|
1095
|
-
}),
|
|
1096
|
-
listSettingsProfileVersions: (name: string, signal?: AbortSignal) =>
|
|
1097
|
-
req(`/api/config/profiles/${encodeSegment(name)}/versions`, s.settingsProfileVersionList, {
|
|
1098
|
-
signal,
|
|
1099
|
-
}),
|
|
1100
|
-
getSettingsProfileVersion: (name: string, version: number, signal?: AbortSignal) =>
|
|
1101
|
-
req(
|
|
1102
|
-
`/api/config/profiles/${encodeSegment(name)}/versions/${encodeSegment(version)}`,
|
|
1103
|
-
s.settingsProfileVersion,
|
|
1104
|
-
{ signal },
|
|
1105
|
-
),
|
|
1106
|
-
rollbackSettingsProfile: (name: string, version: number) =>
|
|
1107
|
-
req(`/api/config/profiles/${encodeSegment(name)}/rollback`, s.settingsProfileRollback, {
|
|
1108
|
-
method: 'POST',
|
|
1109
|
-
body: { version },
|
|
1110
|
-
}),
|
|
1111
|
-
|
|
1112
|
-
// -- combined env+manifest op + preserved manifest -----------------------
|
|
1113
|
-
// The SecretRefField server half: one atomic env-value + `!ENV` manifest
|
|
1114
|
-
// write, returning the shared apply-result shape (reload + fleet fanout).
|
|
1115
|
-
setMcpSecretEnv: (body: SetMcpSecretEnvBody) =>
|
|
1116
|
-
req('/api/mcp-config/secret-env', s.reloadConfigResult, { method: 'POST', body }),
|
|
1117
|
-
// The PRESERVED manifest (`!ENV` markers intact, no resolved secrets) — the
|
|
1118
|
-
// connectors page's McpServersSection reads this so round-trips never inline
|
|
1119
|
-
// resolved values.
|
|
1120
|
-
getManifestPreserved: (signal?: AbortSignal) =>
|
|
1121
|
-
req('/api/manifest/preserved', s.manifestView, { signal }),
|
|
1122
|
-
// The manifest MCP section's `!ENV ${VAR[:default]}` marker refs — NAMES and
|
|
1123
|
-
// set/unset BOOLEANS only, never values. Drives the McpServersSection env-refs
|
|
1124
|
-
// checklist on the connectors page.
|
|
1125
|
-
getMcpEnvRefs: (signal?: AbortSignal) =>
|
|
1126
|
-
req('/api/manifest/mcp-env-refs', s.mcpEnvRefs, { signal }),
|
|
1127
|
-
|
|
1128
|
-
// -- connectors ----------------------------------------------------------
|
|
1129
|
-
listProviders: (signal?: AbortSignal) =>
|
|
1130
|
-
req('/api/connectors/providers', s.providers, { signal }),
|
|
1131
|
-
listConnections: (signal?: AbortSignal) =>
|
|
1132
|
-
req('/api/connectors/connections', s.connectionsList, { signal }),
|
|
1133
|
-
getConnection: (id: string, signal?: AbortSignal) =>
|
|
1134
|
-
req(`/api/connectors/connections/${encodeSegment(id)}`, s.connectionView, { signal }),
|
|
1135
|
-
startConnect: (args: StartConnectArgs) =>
|
|
1136
|
-
req('/api/connectors/connections/start', s.startConnectResult, {
|
|
1137
|
-
method: 'POST',
|
|
1138
|
-
body: args,
|
|
1139
|
-
}),
|
|
1140
|
-
disconnect: (id: string) =>
|
|
1141
|
-
req(`/api/connectors/connections/${encodeSegment(id)}`, s.disconnectResult, {
|
|
1142
|
-
method: 'DELETE',
|
|
1143
|
-
}),
|
|
1144
|
-
reconnect: (id: string, enabled_sub_services: string[], return_url?: string) =>
|
|
1145
|
-
req(`/api/connectors/connections/${encodeSegment(id)}/reconnect`, s.reconnectResult, {
|
|
1146
|
-
method: 'POST',
|
|
1147
|
-
body: { enabled_sub_services, return_url },
|
|
1148
|
-
}),
|
|
1149
|
-
patchSubServices: (id: string, enabled_sub_services: string[], return_url?: string) =>
|
|
1150
|
-
req(
|
|
1151
|
-
`/api/connectors/connections/${encodeSegment(id)}/sub-services`,
|
|
1152
|
-
s.patchSubServicesResult,
|
|
1153
|
-
{
|
|
1154
|
-
method: 'PATCH',
|
|
1155
|
-
body: { enabled_sub_services, return_url },
|
|
1156
|
-
},
|
|
1157
|
-
),
|
|
1158
|
-
completeOAuth: (state: string, code: string, error?: string) =>
|
|
1159
|
-
req('/api/connectors/oauth/complete', s.oauthCompleteResult, {
|
|
1160
|
-
method: 'POST',
|
|
1161
|
-
body: { state, code, error },
|
|
1162
|
-
}),
|
|
1163
|
-
|
|
1164
|
-
// -- studio plugin registry ---------------------------------------------
|
|
1165
|
-
listStudioPlugins: (signal?: AbortSignal) =>
|
|
1166
|
-
req('/api/plugins', s.studioPluginRegistry, { signal }),
|
|
1167
|
-
|
|
1168
|
-
// -- interactions --------------------------------------------------------
|
|
1169
|
-
// One page of the pending interactions (the tail-only stream's paged base). The
|
|
1170
|
-
// door filters by audience before paging — the SAME filter the stream applies —
|
|
1171
|
-
// so `total` is the caller's own count, and every terminal frame it later receives
|
|
1172
|
-
// refers to a question within that `total`.
|
|
1173
|
-
listInteractions: (page: number, pageSize: number, signal?: AbortSignal) =>
|
|
1174
|
-
req('/api/interactions', s.interactionsPage, { signal, query: { page, pageSize } }),
|
|
1175
|
-
answerInteraction: (interactionId: string, answer: unknown) =>
|
|
1176
|
-
req(`/api/interactions/${encodeSegment(interactionId)}/answer`, s.interactionAnswered, {
|
|
1177
|
-
method: 'POST',
|
|
1178
|
-
body: { answer },
|
|
1179
|
-
}),
|
|
1180
|
-
// Withdraw a pending ask WITHOUT answering it (a bodyless POST): the question is
|
|
1181
|
-
// resolved as cancelled and the flow that asked never resumes. Terminal like an
|
|
1182
|
-
// answer, so the loud failure mappings mirror it — a 409 (already answered, so no
|
|
1183
|
-
// longer cancellable) surfaces as `ApiConflictError`, a 404 (unknown/already gone)
|
|
1184
|
-
// and a 403 (not this caller's audience) as `ApiError` — all via the shared
|
|
1185
|
-
// `apiRequest` status handling.
|
|
1186
|
-
cancelInteraction: (interactionId: string) =>
|
|
1187
|
-
req(`/api/interactions/${encodeSegment(interactionId)}/cancel`, s.interactionCancelled, {
|
|
1188
|
-
method: 'POST',
|
|
1189
|
-
}),
|
|
1190
|
-
|
|
1191
|
-
// -- channels --------------------------------------------------------------
|
|
1192
|
-
// The installed channel-plugin names (delivery media for ask_user questions).
|
|
1193
|
-
listChannels: (signal?: AbortSignal) => req('/api/channels', s.channels, { signal }),
|
|
1194
|
-
|
|
1195
|
-
// -- conversations ---------------------------------------------------------
|
|
1196
|
-
// The stored routing rows, each with its `callback_secret` withheld — the
|
|
1197
|
-
// conversation monitor's route picker.
|
|
1198
|
-
listConversationRoutes: (signal?: AbortSignal) =>
|
|
1199
|
-
req('/api/conversations', s.conversationRoutes, { signal }),
|
|
1200
|
-
// Create or REPLACE a route (an UPSERT: the same door is the create and the
|
|
1201
|
-
// edit path). `route_name` rides the URL path AND the body; the door rejects a
|
|
1202
|
-
// disagreeing pair, so both come from the one create object. The reply carries
|
|
1203
|
-
// the freshly-minted `callback_secret` for an `api`-door row, returned ONCE.
|
|
1204
|
-
createOrReplaceConversationRoute: (route: s.ConversationRouteCreate) =>
|
|
1205
|
-
req(`/api/conversations/${encodeSegment(route.route_name)}`, s.conversationRouteWritten, {
|
|
1206
|
-
method: 'POST',
|
|
1207
|
-
body: route,
|
|
1208
|
-
}),
|
|
1209
|
-
// Delete a route by name (destructive + authority-changing server-side): it
|
|
1210
|
-
// drops the routing row and reclaims the thread indexes it owned.
|
|
1211
|
-
deleteConversationRoute: (routeName: string) =>
|
|
1212
|
-
req(`/api/conversations/${encodeSegment(routeName)}`, s.conversationRouteDeleted, {
|
|
1213
|
-
method: 'DELETE',
|
|
1214
|
-
}),
|
|
1215
|
-
// One route's threads, newest activity first. Admin-only server-side: a
|
|
1216
|
-
// listing spans every caller on the route, so a scoped session gets a 403.
|
|
1217
|
-
listConversationThreads: (
|
|
1218
|
-
routeName: string,
|
|
1219
|
-
page: number,
|
|
1220
|
-
pageSize: number,
|
|
1221
|
-
filters: ConversationThreadFilters = {},
|
|
1222
|
-
signal?: AbortSignal,
|
|
1223
|
-
) =>
|
|
1224
|
-
req(`/api/conversations/${encodeSegment(routeName)}/threads`, s.conversationThreadsPage, {
|
|
1225
|
-
signal,
|
|
1226
|
-
query: { page, pageSize, status: filters.status, address: filters.address },
|
|
1227
|
-
}),
|
|
1228
|
-
// One thread's transcript, caller-scoped, read in the asked-for direction.
|
|
1229
|
-
// The `thread_id` rides the QUERY STRING, never the path: it embeds the api
|
|
1230
|
-
// door's `{principal}/{end user}` address with the principal already
|
|
1231
|
-
// percent-encoded, which a path segment would have to encode a second time
|
|
1232
|
-
// and the server would then decode once too few. A query value is decoded
|
|
1233
|
-
// exactly once whatever it holds, so it is passed through verbatim.
|
|
1234
|
-
readConversationTranscript: (query: ConversationTranscriptQuery, signal?: AbortSignal) =>
|
|
1235
|
-
req(
|
|
1236
|
-
`/api/conversations/${encodeSegment(query.routeName)}/transcript`,
|
|
1237
|
-
s.conversationTranscriptPage,
|
|
1238
|
-
{
|
|
1239
|
-
signal,
|
|
1240
|
-
query: {
|
|
1241
|
-
thread_id: query.threadId,
|
|
1242
|
-
page: query.page,
|
|
1243
|
-
pageSize: query.pageSize,
|
|
1244
|
-
order: query.order,
|
|
1245
|
-
q: query.q,
|
|
1246
|
-
},
|
|
1247
|
-
},
|
|
1248
|
-
),
|
|
1249
|
-
// Search every record on a route by text, across threads, newest first. The
|
|
1250
|
-
// needle rides the QUERY (`q`); paging is the infinite query's own window. The
|
|
1251
|
-
// page's `truncated` flag says the match set outran the door's scan cap.
|
|
1252
|
-
searchConversationMessages: (query: ConversationMessageSearchQuery, signal?: AbortSignal) =>
|
|
1253
|
-
req(
|
|
1254
|
-
`/api/conversations/${encodeSegment(query.routeName)}/messages/search`,
|
|
1255
|
-
s.conversationMessageSearchPage,
|
|
1256
|
-
{ signal, query: { q: query.q, page: query.page, pageSize: query.pageSize } },
|
|
1257
|
-
),
|
|
1258
|
-
// Send a human message into a thread. The `thread_id` rides the BODY (a POST,
|
|
1259
|
-
// never a path segment) — same encode-once reasoning as the transcript read.
|
|
1260
|
-
sendConversationThreadMessage: (routeName: string, body: ConversationThreadMessageBody) =>
|
|
1261
|
-
req(
|
|
1262
|
-
`/api/conversations/${encodeSegment(routeName)}/thread/messages`,
|
|
1263
|
-
s.conversationThreadMessageSent,
|
|
1264
|
-
{ method: 'POST', body },
|
|
1265
|
-
),
|
|
1266
|
-
// Read a thread's reply mode (`agent`/`manual`) and where it comes from. The
|
|
1267
|
-
// `thread_id` rides the QUERY value, encoded once, as the transcript read does.
|
|
1268
|
-
getConversationThreadMode: (routeName: string, threadId: string, signal?: AbortSignal) =>
|
|
1269
|
-
req(
|
|
1270
|
-
`/api/conversations/${encodeSegment(routeName)}/thread/mode`,
|
|
1271
|
-
s.conversationThreadModeState,
|
|
1272
|
-
{ signal, query: { thread_id: threadId } },
|
|
1273
|
-
),
|
|
1274
|
-
// Flip a thread's reply mode. The `thread_id` rides the BODY (a PUT).
|
|
1275
|
-
setConversationThreadMode: (
|
|
1276
|
-
routeName: string,
|
|
1277
|
-
threadId: string,
|
|
1278
|
-
mode: s.ConversationThreadMode,
|
|
1279
|
-
) =>
|
|
1280
|
-
req(
|
|
1281
|
-
`/api/conversations/${encodeSegment(routeName)}/thread/mode`,
|
|
1282
|
-
s.conversationThreadModeState,
|
|
1283
|
-
{ method: 'PUT', body: { thread_id: threadId, mode } },
|
|
1284
|
-
),
|
|
1285
|
-
|
|
1286
|
-
// -- conversation target configs -------------------------------------------
|
|
1287
|
-
// The stored per-target presentation configs (each `(target_kind, target_name)`
|
|
1288
|
-
// pair's multichannel flag + first-contact greeting). Admin config surface.
|
|
1289
|
-
listConversationConfigs: (signal?: AbortSignal) =>
|
|
1290
|
-
req('/api/conversation-configs', s.conversationConfigs, { signal }),
|
|
1291
|
-
// One config by its `(target_kind, target_name)` key. 404 when unknown.
|
|
1292
|
-
getConversationConfig: (
|
|
1293
|
-
targetKind: s.ConversationTargetKind,
|
|
1294
|
-
targetName: string,
|
|
1295
|
-
signal?: AbortSignal,
|
|
1296
|
-
) =>
|
|
1297
|
-
req(
|
|
1298
|
-
`/api/conversation-configs/${encodeSegment(targetKind)}/${encodeSegment(targetName)}`,
|
|
1299
|
-
s.targetConversationConfig,
|
|
1300
|
-
{ signal },
|
|
1301
|
-
),
|
|
1302
|
-
// Create or REPLACE a config (an UPSERT: the same door is the create and the
|
|
1303
|
-
// edit path). The `(target_kind, target_name)` key rides the URL path; the door
|
|
1304
|
-
// rejects a body whose key disagrees, so both come from the one config object.
|
|
1305
|
-
setConversationConfig: (config: s.TargetConversationConfig) =>
|
|
1306
|
-
req(
|
|
1307
|
-
`/api/conversation-configs/${encodeSegment(config.target_kind)}/${encodeSegment(config.target_name)}`,
|
|
1308
|
-
s.conversationConfigWritten,
|
|
1309
|
-
{ method: 'PUT', body: config },
|
|
1310
|
-
),
|
|
1311
|
-
// Delete a config by its key. 404 when the key is unknown.
|
|
1312
|
-
deleteConversationConfig: (targetKind: s.ConversationTargetKind, targetName: string) =>
|
|
1313
|
-
req(
|
|
1314
|
-
`/api/conversation-configs/${encodeSegment(targetKind)}/${encodeSegment(targetName)}`,
|
|
1315
|
-
s.conversationConfigDeleted,
|
|
1316
|
-
{ method: 'DELETE' },
|
|
1317
|
-
),
|
|
1318
|
-
|
|
1319
|
-
// -- conversation admin ----------------------------------------------------
|
|
1320
|
-
// Every answer record whose delivery ended `failed`, across every route and
|
|
1321
|
-
// caller. Admin-only server-side; not a paged window (returns `{items, total}`).
|
|
1322
|
-
listFailedConversationMessages: (signal?: AbortSignal) =>
|
|
1323
|
-
req('/api/conversations/messages/failed', s.conversationFailedMessages, { signal }),
|
|
1324
|
-
// Forget ONE thread on a route (destructive): its checkpoint, answer records and
|
|
1325
|
-
// thread indexes. The `thread_id` rides the QUERY value, encoded once — it embeds
|
|
1326
|
-
// the api door's percent-encoded `{principal}/{end user}` address, which a path
|
|
1327
|
-
// segment would encode a second time. `removed` counts the records this call
|
|
1328
|
-
// cleared; a valid id on its own route always succeeds, never a 404.
|
|
1329
|
-
deleteConversationThread: (routeName: string, threadId: string) =>
|
|
1330
|
-
req(`/api/conversations/${encodeSegment(routeName)}/thread`, s.conversationThreadDeleted, {
|
|
1331
|
-
method: 'DELETE',
|
|
1332
|
-
query: { thread_id: threadId },
|
|
1333
|
-
}),
|
|
1334
|
-
// Erase a LINKED person ENTIRELY (GDPR, wide blast radius): its aggregated thread
|
|
1335
|
-
// and checkpoint, its person row, and every address→person index mapping, across
|
|
1336
|
-
// EVERY route it wrote under. The `person_id` is a uuid4 with no percent-encoded
|
|
1337
|
-
// principal, so it rides the PATH cleanly. `erased` says whether this call removed
|
|
1338
|
-
// the person row (idempotent — a retry answers `false`, never a 404).
|
|
1339
|
-
deleteConversationPerson: (personId: string) =>
|
|
1340
|
-
req(`/api/conversations/persons/${encodeSegment(personId)}`, s.conversationPersonDeleted, {
|
|
1341
|
-
method: 'DELETE',
|
|
1342
|
-
}),
|
|
1343
|
-
|
|
1344
|
-
// -- channel-web entry gate ------------------------------------------------
|
|
1345
|
-
// A web route's entry gate, keyed by its `our_identity`: read the flag + its
|
|
1346
|
-
// live codes, flip the flag, mint a code (raw returned ONCE), revoke a code by
|
|
1347
|
-
// its hash id. The conversation monitor's first route mutations.
|
|
1348
|
-
getWebEntryGate: (identity: string, signal?: AbortSignal) =>
|
|
1349
|
-
req(`/api/channels/web/gates/${encodeSegment(identity)}`, s.webEntryGate, { signal }),
|
|
1350
|
-
setWebEntryGate: (identity: string, enabled: boolean) =>
|
|
1351
|
-
req(`/api/channels/web/gates/${encodeSegment(identity)}`, s.webEntryGateState, {
|
|
1352
|
-
method: 'PUT',
|
|
1353
|
-
body: { enabled },
|
|
1354
|
-
}),
|
|
1355
|
-
// Mint a code. `label`/`expires_at` are sent EXPLICITLY (including `null`). The
|
|
1356
|
-
// raw `code` rides the reply ONCE (composed into the chat URL client-side).
|
|
1357
|
-
mintWebEntryCode: (identity: string, body: WebEntryCodeMintBody) =>
|
|
1358
|
-
req(`/api/channels/web/gates/${encodeSegment(identity)}/codes`, s.webEntryCodeMinted, {
|
|
1359
|
-
method: 'POST',
|
|
1360
|
-
body: { label: body.label, expires_at: body.expires_at },
|
|
1361
|
-
}),
|
|
1362
|
-
// Revoke a code by its hash id (immediate + durable). 404 when the id is unknown.
|
|
1363
|
-
revokeWebEntryCode: (identity: string, codeId: string) =>
|
|
1364
|
-
req(
|
|
1365
|
-
`/api/channels/web/gates/${encodeSegment(identity)}/codes/${encodeSegment(codeId)}`,
|
|
1366
|
-
s.webEntryCodeRevoked,
|
|
1367
|
-
{ method: 'DELETE' },
|
|
1368
|
-
),
|
|
1369
|
-
|
|
1370
|
-
// -- notifications ---------------------------------------------------------
|
|
1371
|
-
// The internal notifications sink, newest-first — the messages the `notify_user`
|
|
1372
|
-
// operation records with no channel, so they surface only in this inbox.
|
|
1373
|
-
listNotifications: (signal?: AbortSignal) =>
|
|
1374
|
-
req('/api/notifications', s.notifications, { signal }),
|
|
1375
|
-
|
|
1376
|
-
// -- agents --------------------------------------------------------------
|
|
1377
|
-
listAgents: (signal?: AbortSignal) => req('/api/agents', agentList, { signal }),
|
|
1378
|
-
// The authorable subset (agents whose `spec_runnable` is true) — the compose
|
|
1379
|
-
// UI's base-agent picker. An empty `items` list ⇒ no authoring is possible.
|
|
1380
|
-
listSpecRunnableAgents: (signal?: AbortSignal) =>
|
|
1381
|
-
req('/api/agents/spec-runnable', agentList, { signal }),
|
|
1382
|
-
// The streaming run: opens POST /api/agents/{name}/runs and yields parsed
|
|
1383
|
-
// agent events. The auto-form's built value is the POST body.
|
|
1384
|
-
streamAgentRun: (name: string, input: unknown, signal?: AbortSignal) =>
|
|
1385
|
-
streamAgentRun(config, name, input, signal),
|
|
1386
|
-
// The streaming run of an AUTHORED agent (POST /api/agents/authored/{name}/runs).
|
|
1387
|
-
// The body carries only the non-baked ToolInput fields; the baked spec is
|
|
1388
|
-
// resolved server-side. Reuses the same SSE parser (never EventSource).
|
|
1389
|
-
streamAuthoredAgentRun: (name: string, input: unknown, signal?: AbortSignal) =>
|
|
1390
|
-
streamAuthoredAgentRun(config, name, input, signal),
|
|
1391
|
-
|
|
1392
|
-
// -- hooks --------------------------------------------------------
|
|
1393
|
-
listHooks: (topic?: string, signal?: AbortSignal) =>
|
|
1394
|
-
req('/api/hooks', s.hookList, { signal, query: topic === undefined ? undefined : { topic } }),
|
|
1395
|
-
registerHook: (params: s.HookRegister) =>
|
|
1396
|
-
req('/api/hooks', s.hookRegistered, { method: 'POST', body: params }),
|
|
1397
|
-
unregisterHook: (name: string) =>
|
|
1398
|
-
req(`/api/hooks/${encodeSegment(name)}`, s.hookRemoved, { method: 'DELETE' }),
|
|
1399
|
-
// The registered webhook-verifier catalog — the only source the bind picker
|
|
1400
|
-
// reads (names only; an empty registry is a valid empty list).
|
|
1401
|
-
listHookVerifiers: (signal?: AbortSignal) =>
|
|
1402
|
-
req('/api/hooks/verifiers', s.hookVerifiers, { signal }),
|
|
1403
|
-
// Bind a verifier to a topic (locks its otherwise-open public ingress). A PUT
|
|
1404
|
-
// replaces any existing binding; an unknown verifier name is a loud 400.
|
|
1405
|
-
setTopicVerifier: (topic: string, body: TopicVerifierBody) =>
|
|
1406
|
-
req(`/api/hooks/topics/${encodeSegment(topic)}/verifier`, s.topicVerifierSet, {
|
|
1407
|
-
method: 'PUT',
|
|
1408
|
-
body,
|
|
1409
|
-
}),
|
|
1410
|
-
// Unbind a topic's verifier (re-opens its public ingress). 404 when no binding.
|
|
1411
|
-
deleteTopicVerifier: (topic: string) =>
|
|
1412
|
-
req(`/api/hooks/topics/${encodeSegment(topic)}/verifier`, s.topicVerifierRemoved, {
|
|
1413
|
-
method: 'DELETE',
|
|
1414
|
-
}),
|
|
1415
|
-
|
|
1416
|
-
// -- hooks: trigger links -------------------------------------------------
|
|
1417
|
-
// Mint a trigger link for a topic. `ttl_seconds` is sent EXPLICITLY
|
|
1418
|
-
// (including `null` for a permanent link) — the server requires the key present
|
|
1419
|
-
// and 400s a dropped one; `tool_kwargs` is omitted when undefined. The raw
|
|
1420
|
-
// `token` rides the response ONCE (composed into the QR URL client-side).
|
|
1421
|
-
createTriggerLink: (body: TriggerLinkCreateBody) =>
|
|
1422
|
-
req('/api/hooks/trigger-links', s.triggerLinkCreated, {
|
|
1423
|
-
method: 'POST',
|
|
1424
|
-
body: {
|
|
1425
|
-
topic: body.topic,
|
|
1426
|
-
name: body.name,
|
|
1427
|
-
execution_key: body.execution_key,
|
|
1428
|
-
require_api_key: body.require_api_key,
|
|
1429
|
-
ttl_seconds: body.ttl_seconds,
|
|
1430
|
-
tool_kwargs: body.tool_kwargs,
|
|
1431
|
-
},
|
|
1432
|
-
}),
|
|
1433
|
-
// Every live trigger-link record (hash prefix only — never a raw token).
|
|
1434
|
-
listTriggerLinks: (signal?: AbortSignal) =>
|
|
1435
|
-
req('/api/hooks/trigger-links', s.triggerLinkList, { signal }),
|
|
1436
|
-
// Revoke a link by name (immediate + durable). 404 when the name is unknown.
|
|
1437
|
-
deleteTriggerLink: (name: string) =>
|
|
1438
|
-
req(`/api/hooks/trigger-links/${encodeSegment(name)}`, s.triggerLinkDeleted, {
|
|
1439
|
-
method: 'DELETE',
|
|
1440
|
-
}),
|
|
1441
|
-
|
|
1442
|
-
// -- mcp config schema (connectors McpServersSection form) ---------------
|
|
1443
|
-
getMcpConfigSchema: (signal?: AbortSignal) =>
|
|
1444
|
-
req('/api/mcp-config/schema', s.jsonSchema, { signal }),
|
|
1445
|
-
|
|
1446
|
-
// -- settings schema -----------------------------------------------------
|
|
1447
|
-
getSettingsSchema: (signal?: AbortSignal) =>
|
|
1448
|
-
req('/api/config/settings-schema', s.settingsSchema, { signal }),
|
|
1449
|
-
|
|
1450
|
-
// -- auth: scopes --------------------------------------------------------
|
|
1451
|
-
listScopes: (signal?: AbortSignal) => req('/api/auth/scopes', s.authScopes, { signal }),
|
|
1452
|
-
addUrlToScope: (body: AddUrlToScopeBody) =>
|
|
1453
|
-
req('/api/auth/scopes', s.addUrlToScopeResult, { method: 'POST', body }),
|
|
1454
|
-
removeUrlFromScope: (body: { url: string }) =>
|
|
1455
|
-
req('/api/auth/scopes/urls', s.removeUrlFromScopeResult, { method: 'DELETE', body }),
|
|
1456
|
-
removeScope: (scopeId: string) =>
|
|
1457
|
-
req(`/api/auth/scopes/${encodeSegment(scopeId)}`, s.removeScopeResult, {
|
|
1458
|
-
method: 'DELETE',
|
|
1459
|
-
}),
|
|
1460
|
-
// The app's HTTP route catalog with each route's current mapping — the only
|
|
1461
|
-
// source of the mapper's unassigned-routes bucket (`mapped: null` entries).
|
|
1462
|
-
listAuthRoutes: (signal?: AbortSignal) => req('/api/auth/routes', s.authRoutes, { signal }),
|
|
1463
|
-
// The urls pinned to the public marker; pinning/unpinning is the dedicated
|
|
1464
|
-
// public-routes writer, never the scope machinery.
|
|
1465
|
-
listPublicRoutes: (signal?: AbortSignal) =>
|
|
1466
|
-
req('/api/auth/public-routes', s.publicRoutes, { signal }),
|
|
1467
|
-
pinRoutePublic: (body: PinRoutePublicBody) =>
|
|
1468
|
-
req('/api/auth/public-routes', s.pinPublicResult, { method: 'POST', body }),
|
|
1469
|
-
// Unpin a public url. A url that is absent or scope-mapped is a loud 404.
|
|
1470
|
-
unpinPublicRoute: (url: string) =>
|
|
1471
|
-
req('/api/auth/public-routes', s.unpinPublicResult, { method: 'DELETE', body: { url } }),
|
|
1472
|
-
|
|
1473
|
-
// -- auth: roles (editable per-tag RBAC) ---------------------------------
|
|
1474
|
-
// The seeded + operator-authored roles as full bodies (base-tier ceiling +
|
|
1475
|
-
// editable per-tag grant map). ADMIN-ONLY — the route is `secret` (a listing
|
|
1476
|
-
// exposes each role's raw base-tier jq); a non-admin projection never reaches it.
|
|
1477
|
-
listRoles: (signal?: AbortSignal) => req('/api/auth/roles', s.roleList, { signal }),
|
|
1478
|
-
// Create an operator-authored role. The server validates the grant map + base
|
|
1479
|
-
// tier and 409s on a name collision; the reserved `admin` role cannot be created.
|
|
1480
|
-
createRole: (body: RoleCreateBody) =>
|
|
1481
|
-
req('/api/auth/roles', s.roleBody, { method: 'POST', body }),
|
|
1482
|
-
// Edit a role's per-tag grant map + description (omit-means-keep). The reserved
|
|
1483
|
-
// `admin` role and any `allow_all` role are block-downgrade guarded server-side.
|
|
1484
|
-
updateRole: (name: string, body: RoleUpdateBody) =>
|
|
1485
|
-
req(`/api/auth/roles/${encodeSegment(name)}`, s.roleBody, { method: 'PUT', body }),
|
|
1486
|
-
// Delete a role. The reserved `admin` role is undeletable; a role still assigned
|
|
1487
|
-
// to any principal is a loud 409.
|
|
1488
|
-
deleteRole: (name: string) =>
|
|
1489
|
-
req(`/api/auth/roles/${encodeSegment(name)}`, s.roleDeleted, { method: 'DELETE' }),
|
|
1490
|
-
// A role's append-only version history + who/action/before→after audit trail.
|
|
1491
|
-
// ADMIN-ONLY (`secret`).
|
|
1492
|
-
listRoleVersions: (name: string, signal?: AbortSignal) =>
|
|
1493
|
-
req(`/api/auth/roles/${encodeSegment(name)}/versions`, s.roleVersions, { signal }),
|
|
1494
|
-
// Re-point a role's active version to a prior one (LIVE — holders follow on
|
|
1495
|
-
// their next request); returns the re-pointed role body.
|
|
1496
|
-
rollbackRole: (name: string, version: number) =>
|
|
1497
|
-
req(`/api/auth/roles/${encodeSegment(name)}/rollback`, s.roleBody, {
|
|
1498
|
-
method: 'POST',
|
|
1499
|
-
body: { version },
|
|
1500
|
-
}),
|
|
1501
|
-
|
|
1502
|
-
// -- auth: api keys ------------------------------------------------------
|
|
1503
|
-
listTokensPayload: (signal?: AbortSignal) =>
|
|
1504
|
-
req('/api/auth/tokens-payload', s.tokensPayload, { signal }),
|
|
1505
|
-
createApiKey: (body: ApiKeyBody) =>
|
|
1506
|
-
req('/api/auth/api-keys', s.createdApiKey, { method: 'POST', body }),
|
|
1507
|
-
editApiKey: (userId: string, body: Omit<ApiKeyBody, 'user_id'>) =>
|
|
1508
|
-
req(`/api/auth/api-keys/${encodeSegment(userId)}`, s.editApiKeyResult, {
|
|
1509
|
-
method: 'PUT',
|
|
1510
|
-
body,
|
|
1511
|
-
}),
|
|
1512
|
-
revokeApiKey: (userId: string) =>
|
|
1513
|
-
req(`/api/auth/api-keys/${encodeSegment(userId)}`, s.revokeApiKeyResult, {
|
|
1514
|
-
method: 'DELETE',
|
|
1515
|
-
}),
|
|
1516
|
-
// Mint a one-time claim link carrying a raw key to another device (the QR
|
|
1517
|
-
// onboarding leg). The token rides the URL fragment; the server returns a
|
|
1518
|
-
// relative `claim_path` and the raw token ONCE.
|
|
1519
|
-
createClaimLink: (body: ClaimLinkBody) =>
|
|
1520
|
-
req('/api/auth/claim-links', s.claimLinkCreated, { method: 'POST', body }),
|
|
1521
|
-
|
|
1522
|
-
// -- auth: the caller's capability projection ----------------------------
|
|
1523
|
-
// The derived (path, method) surface + tools/agents the caller can reach
|
|
1524
|
-
// right now — the projection the Studio filters its nav and pages on.
|
|
1525
|
-
getMe: (signal?: AbortSignal) => req('/api/auth/me', s.meProjection, { signal }),
|
|
1526
|
-
|
|
1527
|
-
// -- login: the public sign-in aggregator --------------------------------
|
|
1528
|
-
// PUBLIC by design: `apiRequest` attaches `x-api-key` only when a token
|
|
1529
|
-
// exists, so the unauthenticated call carries no auth header — no transport
|
|
1530
|
-
// change needed.
|
|
1531
|
-
getLoginMethods: (options?: { signal?: AbortSignal }) =>
|
|
1532
|
-
req('/api/login/methods', s.loginMethods, { signal: options?.signal }),
|
|
1533
|
-
// Submit a `form` method's field VALUES as a flat top-level JSON body (the
|
|
1534
|
-
// server's request models take the declared field names top-level). `path`
|
|
1535
|
-
// is re-checked with the same relative-`/api/` guard the renderer uses — an
|
|
1536
|
-
// off-origin path is a PROGRAMMING error (loud `Error`), not a login failure.
|
|
1537
|
-
// A refused credential becomes an inline `ApiLoginFailedError`.
|
|
1538
|
-
submitLoginForm: async (path: string, values: Record<string, string>) => {
|
|
1539
|
-
if (!isSafeApiPath(path)) {
|
|
1540
|
-
throw new Error(
|
|
1541
|
-
`login submit_path must be a relative /api/ path; received ${JSON.stringify(path)}`,
|
|
1542
|
-
);
|
|
1543
|
-
}
|
|
1544
|
-
try {
|
|
1545
|
-
return await req(path, s.loginResult, { method: 'POST', body: values });
|
|
1546
|
-
} catch (error) {
|
|
1547
|
-
return asLoginError(error);
|
|
1548
|
-
}
|
|
1549
|
-
},
|
|
1550
|
-
// Exchange a one-time SSO hand-back code for a session (never a token in the
|
|
1551
|
-
// URL). An expired/reused code is a login failure, surfaced inline.
|
|
1552
|
-
exchangeSsoCode: async (code: string) => {
|
|
1553
|
-
try {
|
|
1554
|
-
return await req('/api/login/sso/exchange', s.loginResult, {
|
|
1555
|
-
method: 'POST',
|
|
1556
|
-
body: { code },
|
|
1557
|
-
});
|
|
1558
|
-
} catch (error) {
|
|
1559
|
-
return asLoginError(error);
|
|
1560
|
-
}
|
|
1561
|
-
},
|
|
1562
|
-
// Exchange a one-time claim token (read from the login URL fragment, never a
|
|
1563
|
-
// query param) for a session. PUBLIC. The token is single-use and burns
|
|
1564
|
-
// server-side; a used/unknown/expired token answers the uniform 404, which —
|
|
1565
|
-
// like the SSO leg — maps to an inline `ApiLoginFailedError`, not a global
|
|
1566
|
-
// unauthorized redirect. The caller latches the exchange so it fires once.
|
|
1567
|
-
claimLogin: async (body: { token: string }) => {
|
|
1568
|
-
try {
|
|
1569
|
-
return await req('/api/login/claim', s.loginResult, { method: 'POST', body });
|
|
1570
|
-
} catch (error) {
|
|
1571
|
-
return asLoginError(error);
|
|
1572
|
-
}
|
|
1573
|
-
},
|
|
1574
|
-
// Whether this deployment can mint API keys locally (the settings key-create
|
|
1575
|
-
// UI gates on `mintable`). AUTHED.
|
|
1576
|
-
getAuthCapabilities: (signal?: AbortSignal) =>
|
|
1577
|
-
req('/api/auth/capabilities', s.authCapabilities, { signal }),
|
|
1578
|
-
|
|
1579
|
-
// -- auth: session logout ------------------------------------------------
|
|
1580
|
-
// The single authed logout dispatcher — revokes the caller's accounts
|
|
1581
|
-
// session server-side before the shell clears local state. A plain `sk-` key
|
|
1582
|
-
// has nothing to revoke and answers 404 (surfaced as an `ApiError`, quiet at
|
|
1583
|
-
// the call site); a 5xx/network failure is a real revoke failure the shell
|
|
1584
|
-
// surfaces loudly.
|
|
1585
|
-
logout: () => req('/api/auth/logout', s.logoutResult, { method: 'POST' }),
|
|
1586
|
-
|
|
1587
|
-
// -- auth: policy authoring + versioning ---------------------------------
|
|
1588
|
-
// The fail-closed jq guard: compiles (and, with `sample_context`, evaluates) a
|
|
1589
|
-
// condition without persisting anything, so a broken condition is caught at
|
|
1590
|
-
// author time instead of locking a key out at enforcement. A broken condition
|
|
1591
|
-
// is a 400 the transport throws — never a false-positive `ok`.
|
|
1592
|
-
validateCondition: (body: ValidateConditionBody) =>
|
|
1593
|
-
req('/api/auth/validate-condition', s.validateConditionResult, { method: 'POST', body }),
|
|
1594
|
-
// The user's append-only policy version history (each row's `is_current`
|
|
1595
|
-
// derived from the active pointer). SECRET-ADJACENT — the body carries the raw
|
|
1596
|
-
// condition, so its fixtures are hand-authored.
|
|
1597
|
-
listPolicyVersions: (userId: string, signal?: AbortSignal) =>
|
|
1598
|
-
req(`/api/auth/api-keys/${encodeSegment(userId)}/policy/versions`, s.policyVersionList, {
|
|
1599
|
-
signal,
|
|
1600
|
-
}),
|
|
1601
|
-
// Re-point the enforced policy to a prior version; returns the re-pointed
|
|
1602
|
-
// active version. Enforcement follows on cache invalidation.
|
|
1603
|
-
rollbackPolicy: (userId: string, version: number) =>
|
|
1604
|
-
req(`/api/auth/api-keys/${encodeSegment(userId)}/policy/rollback`, s.policyRollback, {
|
|
1605
|
-
method: 'POST',
|
|
1606
|
-
body: { version },
|
|
1607
|
-
}),
|
|
1608
|
-
|
|
1609
|
-
// -- backup --------------------------------------------------------------
|
|
1610
|
-
listBackupSections: (signal?: AbortSignal) =>
|
|
1611
|
-
req('/api/backup/sections', s.backupSections, { signal }),
|
|
1612
|
-
exportBackup: (sections: string[], signal?: AbortSignal): Promise<Blob> =>
|
|
1613
|
-
apiDownload(config, '/api/backup/export', { method: 'POST', body: { sections }, signal }),
|
|
1614
|
-
importBackup: (body: { document: unknown; sections: string[] }) =>
|
|
1615
|
-
req('/api/backup/import', s.backupImportReport, { method: 'POST', body }),
|
|
1616
|
-
|
|
1617
|
-
// -- schedules -----------------------------------------------------------
|
|
1618
|
-
listSchedules: (signal?: AbortSignal) => req('/api/schedules', s.scheduleList, { signal }),
|
|
1619
|
-
getServerDateTime: (signal?: AbortSignal) =>
|
|
1620
|
-
req('/api/schedules/server-datetime', s.serverDateTime, { signal }),
|
|
1621
|
-
addSchedule: (body: {
|
|
1622
|
-
tool_name: string;
|
|
1623
|
-
tool_kwargs: Record<string, unknown>;
|
|
1624
|
-
schedule_kwargs: Record<string, unknown>;
|
|
1625
|
-
// The optional door-layer state binding applied around every fire of this schedule.
|
|
1626
|
-
state_binding?: s.StateBinding | null;
|
|
1627
|
-
}) => req('/api/schedules', s.jsonValue, { method: 'POST', body }),
|
|
1628
|
-
deleteSchedule: (name: string) =>
|
|
1629
|
-
req(`/api/schedules/${encodeSegment(name)}`, s.jsonValue, { method: 'DELETE' }),
|
|
1630
|
-
|
|
1631
|
-
// -- observability -------------------------------------------------------
|
|
1632
|
-
// The monitoring-backend dashboard rollups (`GET /api/observability/metrics`),
|
|
1633
|
-
// a JSON aggregate distinct from any raw process-metrics text endpoint.
|
|
1634
|
-
getObservabilityMetrics: (params: MetricsQuery = {}, signal?: AbortSignal) =>
|
|
1635
|
-
req('/api/observability/metrics', s.dashboardMetrics, {
|
|
1636
|
-
query: { from: params.from, to: params.to, granularity: params.granularity },
|
|
1637
|
-
signal,
|
|
1638
|
-
}),
|
|
1639
|
-
listRuns: (params: RunsQuery = {}, signal?: AbortSignal) =>
|
|
1640
|
-
req('/api/observability/runs', s.runsPage, { query: runsQueryToParams(params), signal }),
|
|
1641
|
-
getRunTrace: (traceId: string, signal?: AbortSignal) =>
|
|
1642
|
-
req(`/api/observability/runs/${encodeSegment(traceId)}/trace`, s.runTrace, { signal }),
|
|
1643
|
-
exportTrace: (traceId: string, signal?: AbortSignal): Promise<Blob> =>
|
|
1644
|
-
apiDownload(config, `/api/observability/runs/${encodeSegment(traceId)}/trace/export`, {
|
|
1645
|
-
signal,
|
|
1646
|
-
}),
|
|
1647
|
-
exportRuns: (
|
|
1648
|
-
params: RunsQuery & { format: 'csv' | 'json' },
|
|
1649
|
-
signal?: AbortSignal,
|
|
1650
|
-
): Promise<Blob> =>
|
|
1651
|
-
apiDownload(config, '/api/observability/runs/export', {
|
|
1652
|
-
query: { ...runsQueryToParams(params), format: params.format },
|
|
1653
|
-
signal,
|
|
1654
|
-
}),
|
|
1655
|
-
|
|
1656
|
-
// -- marketplace ---------------------------------------------------------
|
|
1657
|
-
// Browse/search proxy the registry through the app (one origin, one auth);
|
|
1658
|
-
// install/uninstall/update mutate the RUNNING app (pip install + manifest
|
|
1659
|
-
// patch + reload) and return synchronous receipts.
|
|
1660
|
-
searchMarketplace: (query: MarketplaceSearchQuery = {}, signal?: AbortSignal) =>
|
|
1661
|
-
req('/api/marketplace/search', s.marketplaceSearchPage, {
|
|
1662
|
-
query: {
|
|
1663
|
-
q: query.q,
|
|
1664
|
-
kind: query.kind,
|
|
1665
|
-
category: query.category,
|
|
1666
|
-
tags: query.tags,
|
|
1667
|
-
namespace: query.namespace,
|
|
1668
|
-
tier: query.tier,
|
|
1669
|
-
contract: query.contract,
|
|
1670
|
-
sort: query.sort,
|
|
1671
|
-
page: query.page,
|
|
1672
|
-
page_size: query.page_size,
|
|
1673
|
-
},
|
|
1674
|
-
signal,
|
|
1675
|
-
}),
|
|
1676
|
-
getMarketplacePlugin: (namespace: string, name: string, signal?: AbortSignal) =>
|
|
1677
|
-
req(
|
|
1678
|
-
`/api/marketplace/plugins/${encodeSegment(namespace)}/${encodeSegment(name)}`,
|
|
1679
|
-
s.marketplacePluginDetail,
|
|
1680
|
-
{ signal },
|
|
1681
|
-
),
|
|
1682
|
-
listMarketplaceCategories: (signal?: AbortSignal) =>
|
|
1683
|
-
req('/api/marketplace/categories', s.marketplaceCategories, { signal }),
|
|
1684
|
-
listMarketplaceKinds: (signal?: AbortSignal) =>
|
|
1685
|
-
req('/api/marketplace/kinds', s.marketplaceKinds, { signal }),
|
|
1686
|
-
listInstalledMarketplacePlugins: (signal?: AbortSignal) =>
|
|
1687
|
-
req('/api/marketplace/installed', s.marketplaceInstalled, { signal }),
|
|
1688
|
-
// Preview resolves the spec and computes routes/collisions/public rows with
|
|
1689
|
-
// NO side effects, so the install dialog can show absolute paths, block on a
|
|
1690
|
-
// collision, and surface the public routes before the operator commits.
|
|
1691
|
-
previewMarketplaceInstall: (body: MarketplaceInstallPreviewBody, signal?: AbortSignal) =>
|
|
1692
|
-
req('/api/marketplace/install/preview', s.marketplaceInstallPreview, {
|
|
1693
|
-
method: 'POST',
|
|
1694
|
-
body,
|
|
1695
|
-
signal,
|
|
1696
|
-
}),
|
|
1697
|
-
installMarketplacePlugin: (body: MarketplaceInstallBody) =>
|
|
1698
|
-
req('/api/marketplace/install', s.marketplaceInstallResult, { method: 'POST', body }),
|
|
1699
|
-
uninstallMarketplacePlugin: (body: MarketplaceUninstallBody) =>
|
|
1700
|
-
req('/api/marketplace/uninstall', s.marketplaceUninstallResult, { method: 'POST', body }),
|
|
1701
|
-
updateMarketplacePlugin: (body: MarketplaceInstallBody) =>
|
|
1702
|
-
req('/api/marketplace/update', s.marketplaceInstallResult, { method: 'POST', body }),
|
|
1703
|
-
// Upgrade EVERY installed plugin to its newest compatible version in one
|
|
1704
|
-
// pass; the response is the per-plugin outcome readout.
|
|
1705
|
-
upgradeAllMarketplacePlugins: () =>
|
|
1706
|
-
req('/api/marketplace/upgrade-all', s.marketplaceUpgradeAllResult, { method: 'POST' }),
|
|
1707
|
-
getMarketplaceAdvisories: (signal?: AbortSignal) =>
|
|
1708
|
-
req('/api/marketplace/advisories', s.marketplaceAdvisories, { signal }),
|
|
1709
|
-
|
|
1710
|
-
// -- system (plain-text ops endpoint) ------------------------------------
|
|
1711
|
-
getHealth: (signal?: AbortSignal) => apiText(config, '/health', { signal }),
|
|
1712
|
-
|
|
1713
|
-
// The pluggable-kind status table (`GET /api/system/kinds`) — a JSON `{data}`
|
|
1714
|
-
// envelope, one row per kind with its active/default/off state.
|
|
1715
|
-
getSystemKinds: (signal?: AbortSignal) =>
|
|
1716
|
-
req('/api/system/kinds', s.kindStatusList, { signal }),
|
|
1717
|
-
|
|
1718
|
-
/**
|
|
1719
|
-
* Open the authed interactions SSE stream. The stream is TAIL-ONLY: its cursor
|
|
1720
|
-
* is the stream tail at connect, so it carries no backlog and no
|
|
1721
|
-
* `interaction.backlog_done` frame — the pending base comes from
|
|
1722
|
-
* `listInteractions`. The caller drives reconnect and refetches that base on
|
|
1723
|
-
* each (re)connect. Returns the raw frame iterator; the interactions feature
|
|
1724
|
-
* maps frames to typed events.
|
|
1725
|
-
*
|
|
1726
|
-
* `lastEventId` (the id of the last frame the caller saw) is sent as the
|
|
1727
|
-
* `Last-Event-ID` header so the server resumes AFTER it and replays any frame —
|
|
1728
|
-
* including a card's `answered` — that landed during a disconnect gap. It is
|
|
1729
|
-
* also sent as a `?last_event_id=` query param so a proxy that strips the header
|
|
1730
|
-
* still resumes; the server reads either. Omitted on a first connect.
|
|
1731
|
-
*/
|
|
1732
|
-
streamInteractions: async (signal?: AbortSignal, lastEventId?: string) => {
|
|
1733
|
-
const doFetch = config.fetch ?? globalThis.fetch;
|
|
1734
|
-
const token = config.getToken();
|
|
1735
|
-
const headers: Record<string, string> = { accept: 'text/event-stream' };
|
|
1736
|
-
if (token) headers['x-api-key'] = token;
|
|
1737
|
-
if (lastEventId) headers['last-event-id'] = lastEventId;
|
|
1738
|
-
// Distinct-URL per open — see the canonical constraint on `sseOpenToken`.
|
|
1739
|
-
const resume = lastEventId ? `&last_event_id=${encodeURIComponent(lastEventId)}` : '';
|
|
1740
|
-
const url = `${config.baseUrl ?? ''}/api/interactions/stream?_=${sseOpenToken()}${resume}`;
|
|
1741
|
-
const response = await doFetch(url, {
|
|
1742
|
-
headers,
|
|
1743
|
-
signal,
|
|
1744
|
-
});
|
|
1745
|
-
if (response.status === 401) throw new ApiUnauthorizedError();
|
|
1746
|
-
// A non-ok stream open (5xx/4xx) still carries a body, which would parse to
|
|
1747
|
-
// zero frames and look like a silently-empty stream — surface it loudly so
|
|
1748
|
-
// the caller shows an error instead of hanging on a reconnect loop. Read the
|
|
1749
|
-
// `{ "error" }` envelope so a load-bearing server message (e.g. the OFF
|
|
1750
|
-
// store's 501 remediation line) surfaces verbatim instead of collapsing to
|
|
1751
|
-
// the bare status text.
|
|
1752
|
-
if (!response.ok) {
|
|
1753
|
-
let payload: unknown;
|
|
1754
|
-
try {
|
|
1755
|
-
payload = await response.json();
|
|
1756
|
-
} catch {
|
|
1757
|
-
throw new ApiError(response.statusText || 'stream failed', response.status);
|
|
1758
|
-
}
|
|
1759
|
-
const { message, code } = extractError(payload);
|
|
1760
|
-
throw new ApiError(
|
|
1761
|
-
message ?? (response.statusText || 'stream failed'),
|
|
1762
|
-
response.status,
|
|
1763
|
-
code,
|
|
1764
|
-
);
|
|
1765
|
-
}
|
|
1766
|
-
return readSseFrames(response, signal);
|
|
1767
|
-
},
|
|
1768
|
-
} as const;
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
export type ApiClient = ReturnType<typeof createApiClient>;
|