@capybara-hq/kernel 0.1.0-rc.12 → 0.1.0-rc.14
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/admin/admin.schemas.d.ts +13 -0
- package/dist/agents/agent-lookup.d.ts +5 -0
- package/dist/agents/agent-lookup.js +15 -0
- package/dist/background/background.module.js +2 -2
- package/dist/background/launch-lock.d.ts +1 -1
- package/dist/background/launch-lock.js +1 -1
- package/dist/background/poll.job.d.ts +2 -2
- package/dist/background/poll.job.js +10 -10
- package/dist/background/stop.d.ts +1 -1
- package/dist/background/stop.js +3 -3
- package/dist/background/store.d.ts +5 -5
- package/dist/background/store.js +5 -5
- package/dist/background/tasks.service.d.ts +6 -6
- package/dist/background/tasks.service.js +13 -13
- package/dist/catalog/admin-catalog.controller.d.ts +13 -2
- package/dist/catalog/admin-catalog.controller.js +24 -3
- package/dist/catalog/admin-catalog.schemas.d.ts +10 -0
- package/dist/catalog/admin-catalog.schemas.js +7 -0
- package/dist/catalog/catalog-content.d.ts +9 -1
- package/dist/catalog/catalog-content.js +2 -1
- package/dist/catalog/catalog-files.d.ts +3 -2
- package/dist/catalog/catalog-files.js +8 -5
- package/dist/catalog/component-scope.service.d.ts +14 -1
- package/dist/catalog/component-scope.service.js +12 -2
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/org-catalog.controller.d.ts +5 -4
- package/dist/catalog/org-catalog.controller.js +5 -4
- package/dist/catalog/org-catalog.schemas.d.ts +18 -2
- package/dist/catalog/org-catalog.schemas.js +16 -2
- package/dist/catalog/org-catalog.service.js +8 -2
- package/dist/catalog/plugin-import.js +1 -1
- package/dist/catalog/plugin-package.js +37 -9
- package/dist/catalog/plugin-upgrade.d.ts +15 -1
- package/dist/catalog/plugin-upgrade.js +18 -5
- package/dist/channels/channels.module.js +2 -2
- package/dist/channels/connections.controller.d.ts +1 -1
- package/dist/channels/connections.controller.js +1 -1
- package/dist/channels/connections.service.d.ts +22 -5
- package/dist/channels/connections.service.js +56 -10
- package/dist/channels/inbound.d.ts +8 -8
- package/dist/channels/inbound.js +31 -31
- package/dist/channels/phrases.d.ts +27 -27
- package/dist/channels/phrases.js +27 -27
- package/dist/channels/webhook.d.ts +21 -1
- package/dist/channels/webhook.js +39 -4
- package/dist/config/docs.js +1 -1
- package/dist/config/schema.d.ts +2 -2
- package/dist/config/schema.js +21 -21
- package/dist/conversations/conversations.controller.d.ts +11 -11
- package/dist/conversations/conversations.controller.js +15 -15
- package/dist/conversations/conversations.module.d.ts +1 -1
- package/dist/conversations/conversations.module.js +7 -7
- package/dist/conversations/conversations.schemas.js +1 -1
- package/dist/conversations/conversations.service.d.ts +14 -14
- package/dist/conversations/conversations.service.js +19 -19
- package/dist/conversations/{environment-status.d.ts → device-status.d.ts} +9 -9
- package/dist/conversations/{environment-status.js → device-status.js} +18 -18
- package/dist/conversations/event-log.d.ts +1 -1
- package/dist/conversations/event-log.js +1 -1
- package/dist/conversations/events.d.ts +1 -1
- package/dist/conversations/events.js +2 -2
- package/dist/ctx/create-ctx.d.ts +26 -3
- package/dist/ctx/create-ctx.js +134 -26
- package/dist/db/kernel-schema.d.ts +37 -25
- package/dist/{environments → devices}/access.d.ts +19 -19
- package/dist/{environments → devices}/access.js +28 -28
- package/dist/devices/conversation-devices.d.ts +75 -0
- package/dist/{environments/conversation-environments.js → devices/conversation-devices.js} +71 -72
- package/dist/devices/devices.controller.d.ts +36 -0
- package/dist/{environments/environments.controller.js → devices/devices.controller.js} +37 -37
- package/dist/devices/devices.module.d.ts +5 -13
- package/dist/devices/devices.module.js +16 -29
- package/dist/devices/devices.schemas.d.ts +3 -0
- package/dist/devices/devices.schemas.js +14 -0
- package/dist/{environments/environments.service.d.ts → devices/devices.service.d.ts} +70 -70
- package/dist/{environments/environments.service.js → devices/devices.service.js} +122 -122
- package/dist/devices/dto.d.ts +13 -54
- package/dist/devices/dto.js +7 -39
- package/dist/devices/label.d.ts +11 -0
- package/dist/{environments → devices}/label.js +7 -7
- package/dist/{environments → devices}/legacy-cleanup.job.d.ts +3 -3
- package/dist/{environments → devices}/legacy-cleanup.job.js +3 -3
- package/dist/errors.d.ts +29 -25
- package/dist/errors.js +29 -25
- package/dist/evals/recorded-tools.d.ts +2 -2
- package/dist/evals/recorded-tools.js +6 -6
- package/dist/extensions/admin-ops.js +1 -0
- package/dist/extensions/admin-view.js +2 -0
- package/dist/extensions/effective-config.d.ts +6 -0
- package/dist/extensions/effective-config.js +18 -7
- package/dist/extensions/guide-places.d.ts +66 -0
- package/dist/extensions/guide-places.js +152 -0
- package/dist/extensions/host.d.ts +13 -2
- package/dist/extensions/host.js +27 -6
- package/dist/extensions/instance.d.ts +17 -2
- package/dist/extensions/instance.js +21 -0
- package/dist/extensions/ledger.d.ts +1 -1
- package/dist/extensions/permission-set.d.ts +16 -2
- package/dist/extensions/permission-set.js +17 -3
- package/dist/extensions/permission-view.d.ts +10 -1
- package/dist/extensions/plugin-guides.d.ts +207 -54
- package/dist/extensions/plugin-guides.js +436 -165
- package/dist/files/previewers.d.ts +1 -1
- package/dist/files/previewers.js +1 -1
- package/dist/http/api.module.js +16 -6
- package/dist/http/gateway-routes.d.ts +1 -1
- package/dist/http/gateway-routes.js +1 -1
- package/dist/identity/settings.d.ts +32 -0
- package/dist/identity/settings.js +26 -2
- package/dist/infra/config/kernel-config.service.js +4 -4
- package/dist/infra/lock/lock.service.d.ts +8 -0
- package/dist/infra/lock/lock.service.js +27 -0
- package/dist/infra/throttler/named-throttlers.d.ts +4 -1
- package/dist/infra/throttler/named-throttlers.js +16 -1
- package/dist/infra/throttler/throttler.guard.js +1 -1
- package/dist/kernel.module.js +3 -3
- package/dist/{devices → local-devices}/device-auth.controller.d.ts +6 -6
- package/dist/{devices → local-devices}/device-auth.controller.js +5 -5
- package/dist/{devices → local-devices}/device-auth.d.ts +20 -20
- package/dist/{devices → local-devices}/device-auth.js +51 -53
- package/dist/local-devices/device-auth.module.d.ts +7 -0
- package/dist/{devices → local-devices}/device-auth.module.js +5 -5
- package/dist/{devices → local-devices}/device-routes.d.ts +3 -3
- package/dist/{devices → local-devices}/device-routes.js +2 -2
- package/dist/local-devices/dto.d.ts +66 -0
- package/dist/local-devices/dto.js +44 -0
- package/dist/{devices → local-devices}/keys.d.ts +11 -11
- package/dist/{devices → local-devices}/keys.js +11 -11
- package/dist/{devices/device-gateway.d.ts → local-devices/local-device-gateway.d.ts} +14 -14
- package/dist/{devices/device-gateway.js → local-devices/local-device-gateway.js} +44 -44
- package/dist/{devices/device-provider.d.ts → local-devices/local-device-provider.d.ts} +15 -15
- package/dist/{devices/device-provider.js → local-devices/local-device-provider.js} +23 -23
- package/dist/local-devices/local-devices.module.d.ts +16 -0
- package/dist/local-devices/local-devices.module.js +44 -0
- package/dist/{devices → local-devices}/machines.d.ts +26 -26
- package/dist/{devices → local-devices}/machines.js +71 -71
- package/dist/{devices → local-devices}/relay.d.ts +15 -15
- package/dist/{devices → local-devices}/relay.js +55 -55
- package/dist/{devices → local-devices}/targets.d.ts +8 -8
- package/dist/{devices → local-devices}/targets.js +14 -14
- package/dist/orgs/offboarding.d.ts +6 -6
- package/dist/orgs/offboarding.js +12 -12
- package/dist/orgs/settings.service.d.ts +28 -0
- package/dist/orgs/settings.service.js +20 -1
- package/dist/overrides.d.ts +3 -1
- package/dist/overrides.js +2 -0
- package/dist/platform/exclusions.js +48 -8
- package/dist/platform/operation.js +10 -2
- package/dist/platform/ops/admin.ops.js +8 -2
- package/dist/platform/ops/connectors.ops.js +2 -0
- package/dist/platform/ops/conversations.ops.d.ts +3 -3
- package/dist/platform/ops/conversations.ops.js +9 -9
- package/dist/platform/ops/devices.ops.d.ts +26 -0
- package/dist/platform/ops/devices.ops.js +311 -0
- package/dist/platform/ops/org-settings.ops.js +2 -2
- package/dist/platform/ops/orgs.ops.js +1 -0
- package/dist/platform/ops/setup.ops.d.ts +24 -0
- package/dist/platform/ops/setup.ops.js +212 -0
- package/dist/platform/ops/ui.ops.d.ts +8 -0
- package/dist/platform/ops/ui.ops.js +41 -0
- package/dist/platform/platform-ops.module.d.ts +4 -2
- package/dist/platform/platform-ops.module.js +13 -5
- package/dist/platform/route-schemas.js +11 -3
- package/dist/roles/gateway-role.d.ts +3 -3
- package/dist/roles/gateway-role.js +4 -4
- package/dist/roles/gateway.module.d.ts +1 -1
- package/dist/roles/gateway.module.js +5 -5
- package/dist/roles/worker.module.js +1 -1
- package/dist/runtime/context/build.d.ts +1 -1
- package/dist/runtime/context/build.js +6 -6
- package/dist/runtime/context/compaction.js +1 -1
- package/dist/runtime/context/{environment-paths.d.ts → device-paths.d.ts} +11 -11
- package/dist/runtime/context/{environment-paths.js → device-paths.js} +13 -13
- package/dist/runtime/maintenance.d.ts +1 -1
- package/dist/sandbox/jobs.d.ts +13 -13
- package/dist/sandbox/jobs.js +20 -20
- package/dist/sandbox/leases.d.ts +9 -9
- package/dist/sandbox/leases.js +14 -14
- package/dist/sandbox/providers.d.ts +8 -8
- package/dist/sandbox/providers.js +8 -8
- package/dist/sandbox/sandbox.module.d.ts +1 -1
- package/dist/sandbox/sandbox.module.js +1 -1
- package/dist/sandbox/sandbox.service.d.ts +76 -76
- package/dist/sandbox/sandbox.service.js +230 -230
- package/dist/setup/kernel-checks.d.ts +91 -0
- package/dist/setup/kernel-checks.js +295 -0
- package/dist/setup/oauth-token-probe.d.ts +47 -0
- package/dist/setup/oauth-token-probe.js +108 -0
- package/dist/setup/redact-check.d.ts +18 -0
- package/dist/setup/redact-check.js +46 -0
- package/dist/setup/setup-check-registry.d.ts +86 -0
- package/dist/setup/setup-check-registry.js +75 -0
- package/dist/setup/setup-checks.controller.d.ts +28 -0
- package/dist/setup/setup-checks.controller.js +78 -0
- package/dist/setup/setup-checks.schemas.d.ts +38 -0
- package/dist/setup/setup-checks.schemas.js +43 -0
- package/dist/setup/setup-checks.service.d.ts +144 -0
- package/dist/setup/setup-checks.service.js +683 -0
- package/dist/setup/setup-guides.controller.d.ts +18 -0
- package/dist/setup/setup-guides.controller.js +48 -0
- package/dist/setup/setup-guides.schemas.d.ts +10 -0
- package/dist/setup/setup-guides.schemas.js +6 -0
- package/dist/setup/setup-guides.service.d.ts +126 -0
- package/dist/setup/setup-guides.service.js +315 -0
- package/dist/setup/setup.module.d.ts +8 -0
- package/dist/setup/setup.module.js +31 -0
- package/dist/tools/builtins/builtin-tools.d.ts +2 -2
- package/dist/tools/builtins/builtin-tools.js +5 -5
- package/dist/tools/builtins/platform.js +1 -1
- package/dist/tools/builtins/sandbox/bash.js +6 -6
- package/dist/tools/builtins/sandbox/common.d.ts +25 -25
- package/dist/tools/builtins/sandbox/common.js +51 -51
- package/dist/tools/builtins/sandbox/edit.js +2 -2
- package/dist/tools/builtins/sandbox/index.d.ts +1 -1
- package/dist/tools/builtins/sandbox/index.js +1 -1
- package/dist/tools/builtins/sandbox/task-output.d.ts +6 -6
- package/dist/tools/builtins/sandbox/task-output.js +10 -10
- package/dist/tools/builtins/sandbox/task-stop.js +2 -2
- package/dist/tools/builtins/sandbox/write.js +2 -2
- package/dist/tools/builtins/skill-load.d.ts +1 -1
- package/dist/tools/builtins/skill-load.js +2 -2
- package/dist/tools/connectors.controller.d.ts +28 -11
- package/dist/tools/connectors.controller.js +49 -14
- package/dist/tools/connectors.schemas.d.ts +4 -0
- package/dist/tools/connectors.schemas.js +4 -0
- package/dist/tools/connectors.service.d.ts +30 -2
- package/dist/tools/connectors.service.js +98 -3
- package/dist/tools/credentials.d.ts +20 -0
- package/dist/tools/credentials.js +90 -1
- package/dist/tools/oauth/tokens.d.ts +20 -0
- package/dist/tools/oauth/tokens.js +49 -24
- package/dist/tools/pipeline.js +4 -3
- package/dist/tools/registry.d.ts +2 -2
- package/dist/tools/tools.module.js +3 -3
- package/dist/ui/admin-ui.controller.d.ts +56 -0
- package/dist/ui/admin-ui.controller.js +192 -0
- package/dist/ui/arrangement.d.ts +74 -0
- package/dist/ui/arrangement.js +98 -0
- package/dist/ui/embed-token.d.ts +2 -2
- package/dist/ui/embed-token.js +30 -5
- package/dist/ui/registry.d.ts +29 -14
- package/dist/ui/registry.js +31 -10
- package/dist/ui/slots.controller.d.ts +11 -0
- package/dist/ui/slots.controller.js +25 -0
- package/dist/ui/ui-stream.d.ts +20 -3
- package/dist/ui/ui-stream.js +25 -7
- package/dist/ui/ui.schemas.d.ts +66 -0
- package/dist/ui/ui.schemas.js +65 -0
- package/dist/ui/views.controller.d.ts +35 -49
- package/dist/ui/views.controller.js +48 -70
- package/dist/ui/views.d.ts +84 -18
- package/dist/ui/views.js +201 -38
- package/package.json +3 -3
- package/src/migrations/0105_devices_rename.sql +136 -0
- package/src/migrations/0106_setup_guides_v2.sql +10 -0
- package/dist/devices/device-auth.module.d.ts +0 -7
- package/dist/environments/conversation-environments.d.ts +0 -75
- package/dist/environments/dto.d.ts +0 -25
- package/dist/environments/dto.js +0 -12
- package/dist/environments/environments.controller.d.ts +0 -36
- package/dist/environments/environments.module.d.ts +0 -8
- package/dist/environments/environments.module.js +0 -31
- package/dist/environments/environments.schemas.d.ts +0 -3
- package/dist/environments/environments.schemas.js +0 -14
- package/dist/environments/label.d.ts +0 -11
- package/dist/platform/ops/environments.ops.d.ts +0 -26
- package/dist/platform/ops/environments.ops.js +0 -311
|
@@ -68,20 +68,11 @@ const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
|
68
68
|
/** RFC 6749 §5.2 形式的错误码(只有这种串才写进日志 / 错误,不回显对方任意文本)。 */
|
|
69
69
|
const OAUTH_ERROR_RE = /^[a-z][a-z0-9_]{0,63}$/;
|
|
70
70
|
/**
|
|
71
|
-
*
|
|
72
|
-
* (Basic form-encodes them first, §2.3.1
|
|
73
|
-
*
|
|
74
|
-
* - 200 with `ok: false` (Slack reports every error this way), or with an `error` code and no
|
|
75
|
-
* `access_token` -> OAuthGrantError with that code (`ok: false` wins over an `access_token`); a
|
|
76
|
-
* transient code (`temporarily_unavailable`, `server_error`, Slack's `ratelimited`, ...) or no
|
|
77
|
-
* parsable code -> OAuthUnavailable.
|
|
78
|
-
* - Everything else (network error, timeout, 429, 408, 403 and other 4xx, a 4xx without a code,
|
|
79
|
-
* 5xx, 3xx, a malformed body) -> OAuthUnavailable: a gateway's rate limit or a WAF block is not a
|
|
80
|
-
* dead credential.
|
|
81
|
-
* - A `token_type` other than Bearer / `user` / `opts.tokenTypes` -> OAuthGrantError
|
|
82
|
-
* `unsupported_token_type`; an accepted one is stored as `Bearer`.
|
|
71
|
+
* The headers and form of a token request (RFC 6749 §3.2): client_id / secret per the client's
|
|
72
|
+
* auth method (Basic form-encodes them first, §2.3.1; otherwise `client_id`, plus
|
|
73
|
+
* `client_secret` for `client_secret_post`). Empty parameters are left out.
|
|
83
74
|
*/
|
|
84
|
-
export
|
|
75
|
+
export function tokenRequestInit(client, params) {
|
|
85
76
|
const form = new URLSearchParams();
|
|
86
77
|
for (const [k, v] of Object.entries(params))
|
|
87
78
|
if (v !== undefined && v !== "")
|
|
@@ -91,8 +82,7 @@ export async function tokenRequest(client, params, http, opts = {}) {
|
|
|
91
82
|
accept: "application/json",
|
|
92
83
|
};
|
|
93
84
|
if (client.authMethod === "client_secret_basic" && client.clientSecret !== undefined) {
|
|
94
|
-
|
|
95
|
-
headers.authorization = `Basic ${Buffer.from(`${enc(client.clientId)}:${enc(client.clientSecret)}`).toString("base64")}`;
|
|
85
|
+
headers.authorization = basicClientAuth(client.clientId, client.clientSecret);
|
|
96
86
|
}
|
|
97
87
|
else {
|
|
98
88
|
form.set("client_id", client.clientId);
|
|
@@ -100,6 +90,46 @@ export async function tokenRequest(client, params, http, opts = {}) {
|
|
|
100
90
|
form.set("client_secret", client.clientSecret);
|
|
101
91
|
}
|
|
102
92
|
}
|
|
93
|
+
return { headers, body: form };
|
|
94
|
+
}
|
|
95
|
+
/** The form encoding of a Basic client credential part (RFC 6749 §2.3.1). */
|
|
96
|
+
export const formEncode = (s) => encodeURIComponent(s).replace(/%20/g, "+");
|
|
97
|
+
/** The `Authorization` value of `client_secret_basic` (both parts form-encoded first). */
|
|
98
|
+
export const basicClientAuth = (clientId, clientSecret) => `Basic ${Buffer.from(`${formEncode(clientId)}:${formEncode(clientSecret)}`).toString("base64")}`;
|
|
99
|
+
/**
|
|
100
|
+
* The refusal code of a token endpoint answer, or null when it is not a refusal (the one parser of
|
|
101
|
+
* token refusals): a 400 / 401 whose JSON body has an RFC 6749-shaped `error`, or a 200 whose body
|
|
102
|
+
* carries `ok: false` (Slack) or an `error` and no `access_token`, with a code that is not
|
|
103
|
+
* transient. Only codes matching the RFC 6749 shape are returned, never other text of the body.
|
|
104
|
+
*/
|
|
105
|
+
export function parseTokenRefusal(status, body) {
|
|
106
|
+
const code = isObj(body) ? body.error : undefined;
|
|
107
|
+
const shaped = typeof code === "string" && OAUTH_ERROR_RE.test(code) ? code : null;
|
|
108
|
+
if (status === 400 || status === 401)
|
|
109
|
+
return shaped;
|
|
110
|
+
if (status !== 200 || !isObj(body))
|
|
111
|
+
return null;
|
|
112
|
+
if (body.ok === false || (body.access_token === undefined && body.error !== undefined)) {
|
|
113
|
+
return shaped !== null && !TRANSIENT_ERRORS.has(shaped) ? shaped : null;
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Calls the token endpoint (RFC 6749 §3.2), sending client_id / secret per the client's auth method
|
|
119
|
+
* (Basic form-encodes them first, §2.3.1).
|
|
120
|
+
* - 400 / 401 with a parsable RFC 6749 `error` code -> OAuthGrantError.
|
|
121
|
+
* - 200 with `ok: false` (Slack reports every error this way), or with an `error` code and no
|
|
122
|
+
* `access_token` -> OAuthGrantError with that code (`ok: false` wins over an `access_token`); a
|
|
123
|
+
* transient code (`temporarily_unavailable`, `server_error`, Slack's `ratelimited`, ...) or no
|
|
124
|
+
* parsable code -> OAuthUnavailable.
|
|
125
|
+
* - Everything else (network error, timeout, 429, 408, 403 and other 4xx, a 4xx without a code,
|
|
126
|
+
* 5xx, 3xx, a malformed body) -> OAuthUnavailable: a gateway's rate limit or a WAF block is not a
|
|
127
|
+
* dead credential.
|
|
128
|
+
* - A `token_type` other than Bearer / `user` / `opts.tokenTypes` -> OAuthGrantError
|
|
129
|
+
* `unsupported_token_type`; an accepted one is stored as `Bearer`.
|
|
130
|
+
*/
|
|
131
|
+
export async function tokenRequest(client, params, http, opts = {}) {
|
|
132
|
+
const { headers, body: form } = tokenRequestInit(client, params);
|
|
103
133
|
let res;
|
|
104
134
|
try {
|
|
105
135
|
res = await oauthFetch(client.tokenEndpoint, { method: "POST", headers, body: form }, http);
|
|
@@ -116,18 +146,13 @@ export async function tokenRequest(client, params, http, opts = {}) {
|
|
|
116
146
|
catch {
|
|
117
147
|
body = undefined;
|
|
118
148
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
throw new OAuthGrantError(code);
|
|
123
|
-
}
|
|
149
|
+
const refusal = parseTokenRefusal(res.status, body);
|
|
150
|
+
if (refusal !== null)
|
|
151
|
+
throw new OAuthGrantError(refusal);
|
|
124
152
|
if (res.status !== 200 || !isObj(body))
|
|
125
153
|
throw new OAuthUnavailable();
|
|
154
|
+
// an error body without a usable code (or a transient one): unavailable, not a refusal
|
|
126
155
|
if (body.ok === false || (body.access_token === undefined && body.error !== undefined)) {
|
|
127
|
-
const code = body.error;
|
|
128
|
-
if (typeof code === "string" && OAUTH_ERROR_RE.test(code) && !TRANSIENT_ERRORS.has(code)) {
|
|
129
|
-
throw new OAuthGrantError(code);
|
|
130
|
-
}
|
|
131
156
|
throw new OAuthUnavailable();
|
|
132
157
|
}
|
|
133
158
|
const { access_token, refresh_token, expires_in, token_type, scope } = body;
|
package/dist/tools/pipeline.js
CHANGED
|
@@ -207,9 +207,10 @@ let ToolPipeline = class ToolPipeline {
|
|
|
207
207
|
},
|
|
208
208
|
effective,
|
|
209
209
|
switches,
|
|
210
|
-
inlineViews: (plugin) => new Set((host?.current(plugin)?.
|
|
211
|
-
.filter((
|
|
212
|
-
.
|
|
210
|
+
inlineViews: (plugin) => new Set((host?.current(plugin)?.ui ?? [])
|
|
211
|
+
.filter((r) => r.slot.id === "conversation.tool-result.card" &&
|
|
212
|
+
r.contribution.aliasOf === undefined)
|
|
213
|
+
.map((r) => r.contribution.id)),
|
|
213
214
|
onToolsChanged: (orgId, connectorId) => {
|
|
214
215
|
void drift?.onToolsChanged(orgId, connectorId);
|
|
215
216
|
},
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { BUILTIN_TOOL_NAMES } from "./builtin-names.js";
|
|
|
14
14
|
export { canonicalJson } from "./canonical-json.js";
|
|
15
15
|
/**
|
|
16
16
|
* What a tool's `unavailable` is asked with: the org, and for a run's listing the root
|
|
17
|
-
* conversation (`conversationId`; the sandbox tools follow its
|
|
17
|
+
* conversation (`conversationId`; the sandbox tools follow its device, ruling R8), whether
|
|
18
18
|
* the run is an eval replay (`replay`: its tool calls are never executed), and a `memo` shared by
|
|
19
19
|
* the tools of one listing (one answer per question per listing).
|
|
20
20
|
*/
|
|
@@ -346,7 +346,7 @@ export declare class ToolRegistry {
|
|
|
346
346
|
forkChild?: boolean;
|
|
347
347
|
/**
|
|
348
348
|
* The root conversation of the run (a sub-agent's tree root): tools whose availability
|
|
349
|
-
* depends on the conversation's
|
|
349
|
+
* depends on the conversation's device (the sandbox tools) are listed by it.
|
|
350
350
|
*/
|
|
351
351
|
sandboxConversationId?: string;
|
|
352
352
|
/** An eval replay's listing: its calls are never executed (the sandbox tools are listed). */
|
|
@@ -9,7 +9,7 @@ import { BackgroundModule } from "../background/background.module.js";
|
|
|
9
9
|
import { ConversationAccessModule } from "../conversations/access.js";
|
|
10
10
|
import { AttachmentsModule } from "../conversations/attachments.module.js";
|
|
11
11
|
import { EventLogModule } from "../conversations/event-log.js";
|
|
12
|
-
import {
|
|
12
|
+
import { DevicesModule } from "../devices/devices.module.js";
|
|
13
13
|
import { ExtensionsModule } from "../extensions/extensions.module.js";
|
|
14
14
|
import { QueueModule } from "../infra/queue/queue.module.js";
|
|
15
15
|
import { ModelsModule } from "../models/models.module.js";
|
|
@@ -59,9 +59,9 @@ ToolsModule = __decorate([
|
|
|
59
59
|
OrgLockModule,
|
|
60
60
|
// the `Agent` tool's dispatch
|
|
61
61
|
SubagentsModule,
|
|
62
|
-
// the builtin sandbox and skill tools run in the conversation's
|
|
62
|
+
// the builtin sandbox and skill tools run in the conversation's device
|
|
63
63
|
SandboxModule,
|
|
64
|
-
|
|
64
|
+
DevicesModule,
|
|
65
65
|
// `Bash` with run_in_background records its task; TaskOutput / TaskStop finish and stop it
|
|
66
66
|
BackgroundModule,
|
|
67
67
|
// the conversation page's tool metadata reads only what the viewer may see
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { UiActionResult, UiView } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import type { FastifyReply, FastifyRequest } from "fastify";
|
|
3
|
+
import type { z } from "zod";
|
|
4
|
+
import { ApiTokenService } from "../identity/api-tokens.js";
|
|
5
|
+
import { type Principal } from "../identity/principal.js";
|
|
6
|
+
import { SessionService } from "../identity/sessions.js";
|
|
7
|
+
import { EmbedTokens } from "./embed-token.js";
|
|
8
|
+
import { ContributionParams, PlatformActionBody, PlatformViewQuery } from "./ui.schemas.js";
|
|
9
|
+
import { OrgUiStreams } from "./ui-stream.js";
|
|
10
|
+
import { type UiArrangementSlot, type UiContributionItem, UiViews } from "./views.js";
|
|
11
|
+
/**
|
|
12
|
+
* Extension UI in the admin console (UI slots spec §7, platform scope): the `platform.page`
|
|
13
|
+
* contributions, for super admins only (`@SuperAdmin`, a person: service tokens are refused), with
|
|
14
|
+
* no org. A platform contribution needs its extension running (no org enablement applies) and is
|
|
15
|
+
* given the platform's deployment config; its render args carry `scope: "platform"`, `role:
|
|
16
|
+
* "superAdmin"` and no `orgId`.
|
|
17
|
+
* - `GET /api/v1/admin/ui/contributions` → `{ items }` (`platform.page` only, base order);
|
|
18
|
+
* - `GET /api/v1/admin/ui/arrangement` → `{ slots }` over every arrangeable slot, the platform's
|
|
19
|
+
* entries only, with every running extension's contributions (hidden ones marked);
|
|
20
|
+
* - `GET /api/v1/admin/ui/views/:plugin/:id?locale=` → `{ view }`;
|
|
21
|
+
* - `POST /api/v1/admin/ui/actions/:plugin/:id` (`{ actionId, payload?, answer?, locale? }`) →
|
|
22
|
+
* `{ toast }` | `{ view }`;
|
|
23
|
+
* - `POST /api/v1/admin/ui/embeds/:plugin/:id/token` → `{ token, src, expiresAt, expiresIn }`: a
|
|
24
|
+
* platform embed token (`scope: "platform"`, `role: "superAdmin"`, no `orgId`), which
|
|
25
|
+
* `ctx.ui.verifyEmbedToken` accepts only for a `platform.page` contribution while the account is
|
|
26
|
+
* still an active super admin;
|
|
27
|
+
* - `GET /api/v1/admin/ui/stream`: the platform refresh SSE (`event: ui`, topic `platform-ui`),
|
|
28
|
+
* super admin re-checked on the `runtime.sseRecheckMs` interval.
|
|
29
|
+
* A contribution in any other slot is 404 `ui.not_found`, the same as one that does not exist.
|
|
30
|
+
*/
|
|
31
|
+
export declare class AdminUiController {
|
|
32
|
+
private readonly views;
|
|
33
|
+
private readonly streams;
|
|
34
|
+
private readonly sessions;
|
|
35
|
+
private readonly tokens;
|
|
36
|
+
private readonly embeds;
|
|
37
|
+
constructor(views: UiViews, streams: OrgUiStreams, sessions: SessionService, tokens: ApiTokenService, embeds: EmbedTokens);
|
|
38
|
+
contributions(principal: Principal): Promise<{
|
|
39
|
+
items: UiContributionItem[];
|
|
40
|
+
}>;
|
|
41
|
+
arrangement(principal: Principal): Promise<{
|
|
42
|
+
slots: UiArrangementSlot[];
|
|
43
|
+
}>;
|
|
44
|
+
view(principal: Principal, { plugin, id }: z.infer<typeof ContributionParams>, q: z.infer<typeof PlatformViewQuery>): Promise<{
|
|
45
|
+
view: UiView;
|
|
46
|
+
}>;
|
|
47
|
+
action(principal: Principal, { plugin, id }: z.infer<typeof ContributionParams>, body: z.infer<typeof PlatformActionBody>): Promise<UiActionResult>;
|
|
48
|
+
embedToken(principal: Principal, { plugin, id }: z.infer<typeof ContributionParams>): Promise<{
|
|
49
|
+
token: string;
|
|
50
|
+
src: string;
|
|
51
|
+
expiresAt: string;
|
|
52
|
+
expiresIn: number;
|
|
53
|
+
}>;
|
|
54
|
+
/** The platform refresh stream: written on the raw response (`reply.hijack()`) like the org one. */
|
|
55
|
+
stream(principal: Principal, req: FastifyRequest, reply: FastifyReply): Promise<void>;
|
|
56
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { Controller, Get, HttpCode, Inject, Post, Req, Res } from "@nestjs/common";
|
|
11
|
+
import { ApiTokenService } from "../identity/api-tokens.js";
|
|
12
|
+
import { credentialCheck } from "../identity/credential-check.js";
|
|
13
|
+
import { CurrentPrincipal, requireUser, SuperAdmin, } from "../identity/principal.js";
|
|
14
|
+
import { SessionService } from "../identity/sessions.js";
|
|
15
|
+
import { createZodDto } from "../infra/validation/create-zod-dto.js";
|
|
16
|
+
import { ZBody, ZParam, ZQuery } from "../infra/validation/zod-params.js";
|
|
17
|
+
import { EMBED_TOKEN_TTL_SECONDS, EmbedTokens } from "./embed-token.js";
|
|
18
|
+
import { ContributionParams, PlatformActionBody, PlatformViewQuery } from "./ui.schemas.js";
|
|
19
|
+
import { OrgUiStreams } from "./ui-stream.js";
|
|
20
|
+
import { BEARER_RE, embedSrc } from "./views.controller.js";
|
|
21
|
+
import { UiViews } from "./views.js";
|
|
22
|
+
class AdminUiContributionParamsDto extends createZodDto(ContributionParams, "AdminUiContributionParams") {
|
|
23
|
+
}
|
|
24
|
+
class AdminUiViewQueryDto extends createZodDto(PlatformViewQuery, "AdminUiViewQuery") {
|
|
25
|
+
}
|
|
26
|
+
class AdminUiActionBodyDto extends createZodDto(PlatformActionBody, "AdminUiActionBody") {
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Extension UI in the admin console (UI slots spec §7, platform scope): the `platform.page`
|
|
30
|
+
* contributions, for super admins only (`@SuperAdmin`, a person: service tokens are refused), with
|
|
31
|
+
* no org. A platform contribution needs its extension running (no org enablement applies) and is
|
|
32
|
+
* given the platform's deployment config; its render args carry `scope: "platform"`, `role:
|
|
33
|
+
* "superAdmin"` and no `orgId`.
|
|
34
|
+
* - `GET /api/v1/admin/ui/contributions` → `{ items }` (`platform.page` only, base order);
|
|
35
|
+
* - `GET /api/v1/admin/ui/arrangement` → `{ slots }` over every arrangeable slot, the platform's
|
|
36
|
+
* entries only, with every running extension's contributions (hidden ones marked);
|
|
37
|
+
* - `GET /api/v1/admin/ui/views/:plugin/:id?locale=` → `{ view }`;
|
|
38
|
+
* - `POST /api/v1/admin/ui/actions/:plugin/:id` (`{ actionId, payload?, answer?, locale? }`) →
|
|
39
|
+
* `{ toast }` | `{ view }`;
|
|
40
|
+
* - `POST /api/v1/admin/ui/embeds/:plugin/:id/token` → `{ token, src, expiresAt, expiresIn }`: a
|
|
41
|
+
* platform embed token (`scope: "platform"`, `role: "superAdmin"`, no `orgId`), which
|
|
42
|
+
* `ctx.ui.verifyEmbedToken` accepts only for a `platform.page` contribution while the account is
|
|
43
|
+
* still an active super admin;
|
|
44
|
+
* - `GET /api/v1/admin/ui/stream`: the platform refresh SSE (`event: ui`, topic `platform-ui`),
|
|
45
|
+
* super admin re-checked on the `runtime.sseRecheckMs` interval.
|
|
46
|
+
* A contribution in any other slot is 404 `ui.not_found`, the same as one that does not exist.
|
|
47
|
+
*/
|
|
48
|
+
let AdminUiController = class AdminUiController {
|
|
49
|
+
views;
|
|
50
|
+
streams;
|
|
51
|
+
sessions;
|
|
52
|
+
tokens;
|
|
53
|
+
embeds;
|
|
54
|
+
constructor(views, streams, sessions, tokens, embeds) {
|
|
55
|
+
this.views = views;
|
|
56
|
+
this.streams = streams;
|
|
57
|
+
this.sessions = sessions;
|
|
58
|
+
this.tokens = tokens;
|
|
59
|
+
this.embeds = embeds;
|
|
60
|
+
}
|
|
61
|
+
async contributions(principal) {
|
|
62
|
+
requireUser(principal);
|
|
63
|
+
return { items: await this.views.platformContributions() };
|
|
64
|
+
}
|
|
65
|
+
async arrangement(principal) {
|
|
66
|
+
requireUser(principal);
|
|
67
|
+
return { slots: await this.views.platformArrangementView() };
|
|
68
|
+
}
|
|
69
|
+
async view(principal, { plugin, id }, q) {
|
|
70
|
+
const p = requireUser(principal);
|
|
71
|
+
const view = await this.views.render({
|
|
72
|
+
scope: "platform",
|
|
73
|
+
accountId: p.account.id,
|
|
74
|
+
plugin,
|
|
75
|
+
id,
|
|
76
|
+
locale: q.locale,
|
|
77
|
+
});
|
|
78
|
+
return { view };
|
|
79
|
+
}
|
|
80
|
+
async action(principal, { plugin, id }, body) {
|
|
81
|
+
const p = requireUser(principal);
|
|
82
|
+
return this.views.act({ scope: "platform", accountId: p.account.id, plugin, id, locale: body.locale }, {
|
|
83
|
+
actionId: body.actionId,
|
|
84
|
+
...(body.payload !== undefined && { payload: body.payload }),
|
|
85
|
+
...(body.answer !== undefined && { answer: body.answer }),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async embedToken(principal, { plugin, id }) {
|
|
89
|
+
const p = requireUser(principal);
|
|
90
|
+
const target = await this.views.embed({
|
|
91
|
+
scope: "platform",
|
|
92
|
+
accountId: p.account.id,
|
|
93
|
+
plugin,
|
|
94
|
+
id,
|
|
95
|
+
});
|
|
96
|
+
const { token, expiresAt } = this.embeds.issue({
|
|
97
|
+
plugin: target.plugin,
|
|
98
|
+
scope: "platform",
|
|
99
|
+
accountId: p.account.id,
|
|
100
|
+
role: "superAdmin",
|
|
101
|
+
contribution: target.contribution,
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
token,
|
|
105
|
+
src: embedSrc(target.plugin, target.path),
|
|
106
|
+
expiresAt: expiresAt.toISOString(),
|
|
107
|
+
expiresIn: EMBED_TOKEN_TTL_SECONDS,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** The platform refresh stream: written on the raw response (`reply.hijack()`) like the org one. */
|
|
111
|
+
async stream(principal, req, reply) {
|
|
112
|
+
const p = requireUser(principal);
|
|
113
|
+
const res = reply.raw;
|
|
114
|
+
const handle = await this.streams.open({
|
|
115
|
+
scope: "platform",
|
|
116
|
+
viewerAccountId: p.account.id,
|
|
117
|
+
credential: credentialCheck(p, BEARER_RE.exec(req.headers.authorization ?? "")?.[1], {
|
|
118
|
+
sessions: this.sessions,
|
|
119
|
+
tokens: this.tokens,
|
|
120
|
+
}),
|
|
121
|
+
}, {
|
|
122
|
+
write: (chunk) => {
|
|
123
|
+
res.write(chunk);
|
|
124
|
+
},
|
|
125
|
+
backlog: () => res.writableLength,
|
|
126
|
+
end: () => {
|
|
127
|
+
res.end();
|
|
128
|
+
},
|
|
129
|
+
abort: () => {
|
|
130
|
+
res.destroy();
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
reply.hijack();
|
|
134
|
+
res.on("close", () => handle.close());
|
|
135
|
+
if (res.destroyed || !res.socket || res.socket.destroyed) {
|
|
136
|
+
handle.close();
|
|
137
|
+
res.destroy();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
res.writeHead(200, {
|
|
141
|
+
"content-type": "text/event-stream; charset=utf-8",
|
|
142
|
+
"cache-control": "no-cache",
|
|
143
|
+
"x-accel-buffering": "no",
|
|
144
|
+
connection: "keep-alive",
|
|
145
|
+
});
|
|
146
|
+
res.flushHeaders();
|
|
147
|
+
handle.begin();
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
__decorate([
|
|
151
|
+
Get("contributions"),
|
|
152
|
+
__param(0, CurrentPrincipal())
|
|
153
|
+
], AdminUiController.prototype, "contributions", null);
|
|
154
|
+
__decorate([
|
|
155
|
+
Get("arrangement"),
|
|
156
|
+
__param(0, CurrentPrincipal())
|
|
157
|
+
], AdminUiController.prototype, "arrangement", null);
|
|
158
|
+
__decorate([
|
|
159
|
+
Get("views/:plugin/:id"),
|
|
160
|
+
__param(0, CurrentPrincipal()),
|
|
161
|
+
__param(1, ZParam(AdminUiContributionParamsDto)),
|
|
162
|
+
__param(2, ZQuery(AdminUiViewQueryDto))
|
|
163
|
+
], AdminUiController.prototype, "view", null);
|
|
164
|
+
__decorate([
|
|
165
|
+
Post("actions/:plugin/:id"),
|
|
166
|
+
HttpCode(200),
|
|
167
|
+
__param(0, CurrentPrincipal()),
|
|
168
|
+
__param(1, ZParam(AdminUiContributionParamsDto)),
|
|
169
|
+
__param(2, ZBody(AdminUiActionBodyDto))
|
|
170
|
+
], AdminUiController.prototype, "action", null);
|
|
171
|
+
__decorate([
|
|
172
|
+
Post("embeds/:plugin/:id/token"),
|
|
173
|
+
HttpCode(200),
|
|
174
|
+
__param(0, CurrentPrincipal()),
|
|
175
|
+
__param(1, ZParam(AdminUiContributionParamsDto))
|
|
176
|
+
], AdminUiController.prototype, "embedToken", null);
|
|
177
|
+
__decorate([
|
|
178
|
+
Get("stream"),
|
|
179
|
+
__param(0, CurrentPrincipal()),
|
|
180
|
+
__param(1, Req()),
|
|
181
|
+
__param(2, Res())
|
|
182
|
+
], AdminUiController.prototype, "stream", null);
|
|
183
|
+
AdminUiController = __decorate([
|
|
184
|
+
Controller("api/v1/admin/ui"),
|
|
185
|
+
SuperAdmin(),
|
|
186
|
+
__param(0, Inject(UiViews)),
|
|
187
|
+
__param(1, Inject(OrgUiStreams)),
|
|
188
|
+
__param(2, Inject(SessionService)),
|
|
189
|
+
__param(3, Inject(ApiTokenService)),
|
|
190
|
+
__param(4, Inject(EmbedTokens))
|
|
191
|
+
], AdminUiController);
|
|
192
|
+
export { AdminUiController };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type UiSlotId } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { UiContributionItem } from "./views.js";
|
|
4
|
+
export declare const UiArrangementEntrySchema: z.ZodObject<{
|
|
5
|
+
hidden: z.ZodArray<z.ZodString>;
|
|
6
|
+
order: z.ZodArray<z.ZodString>;
|
|
7
|
+
}, z.core.$strict>;
|
|
8
|
+
export type UiArrangementEntry = z.infer<typeof UiArrangementEntrySchema>;
|
|
9
|
+
export declare const UiArrangementSchema: z.ZodRecord<z.ZodEnum<{
|
|
10
|
+
"agent.detail.section": "agent.detail.section";
|
|
11
|
+
"conversation.panel": "conversation.panel";
|
|
12
|
+
"conversation.properties.section": "conversation.properties.section";
|
|
13
|
+
"conversation.tool-result.card": "conversation.tool-result.card";
|
|
14
|
+
"file.preview": "file.preview";
|
|
15
|
+
"org.page": "org.page";
|
|
16
|
+
"org.settings.section": "org.settings.section";
|
|
17
|
+
"platform.page": "platform.page";
|
|
18
|
+
}> & z.core.$partial, z.ZodObject<{
|
|
19
|
+
hidden: z.ZodArray<z.ZodString>;
|
|
20
|
+
order: z.ZodArray<z.ZodString>;
|
|
21
|
+
}, z.core.$strict>>;
|
|
22
|
+
export type UiArrangement = z.infer<typeof UiArrangementSchema>;
|
|
23
|
+
/** The slots an org arranges: every arrangeable slot but the platform's own. */
|
|
24
|
+
export declare const ORG_ARRANGEABLE: readonly UiSlotId[];
|
|
25
|
+
/** An org's patch: a slot's entry replaces the stored one, null removes it. */
|
|
26
|
+
export declare const OrgUiArrangementPatchSchema: z.ZodRecord<z.ZodEnum<{
|
|
27
|
+
"agent.detail.section": "agent.detail.section";
|
|
28
|
+
"conversation.panel": "conversation.panel";
|
|
29
|
+
"conversation.properties.section": "conversation.properties.section";
|
|
30
|
+
"conversation.tool-result.card": "conversation.tool-result.card";
|
|
31
|
+
"file.preview": "file.preview";
|
|
32
|
+
"org.page": "org.page";
|
|
33
|
+
"org.settings.section": "org.settings.section";
|
|
34
|
+
"platform.page": "platform.page";
|
|
35
|
+
}> & z.core.$partial, z.ZodNullable<z.ZodObject<{
|
|
36
|
+
hidden: z.ZodArray<z.ZodString>;
|
|
37
|
+
order: z.ZodArray<z.ZodString>;
|
|
38
|
+
}, z.core.$strict>>>;
|
|
39
|
+
/** A deployment patch: like {@link OrgUiArrangementPatchSchema} over every arrangeable slot. */
|
|
40
|
+
export declare const UiArrangementPatchSchema: z.ZodRecord<z.ZodEnum<{
|
|
41
|
+
"agent.detail.section": "agent.detail.section";
|
|
42
|
+
"conversation.panel": "conversation.panel";
|
|
43
|
+
"conversation.properties.section": "conversation.properties.section";
|
|
44
|
+
"conversation.tool-result.card": "conversation.tool-result.card";
|
|
45
|
+
"file.preview": "file.preview";
|
|
46
|
+
"org.page": "org.page";
|
|
47
|
+
"org.settings.section": "org.settings.section";
|
|
48
|
+
"platform.page": "platform.page";
|
|
49
|
+
}> & z.core.$partial, z.ZodNullable<z.ZodObject<{
|
|
50
|
+
hidden: z.ZodArray<z.ZodString>;
|
|
51
|
+
order: z.ZodArray<z.ZodString>;
|
|
52
|
+
}, z.core.$strict>>>;
|
|
53
|
+
/** Merges a per-slot patch into the stored arrangement (null removes the slot's entry). */
|
|
54
|
+
export declare function mergeArrangement(current: UiArrangement | undefined, patch: Partial<Record<UiSlotId, UiArrangementEntry | null>>): UiArrangement;
|
|
55
|
+
/** The entry that applies to a slot: the org's if it arranged the slot, else the platform's. */
|
|
56
|
+
export declare function effectiveEntry(org: UiArrangement | undefined, platform: UiArrangement, slot: UiSlotId): {
|
|
57
|
+
entry: UiArrangementEntry;
|
|
58
|
+
source: "org" | "platform" | "none";
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Orders the items of one slot (hidden ones stay in place): grouped by anchor in the slot's anchor
|
|
62
|
+
* order, also without an entry (the lists match what the page renders per anchor); within a group
|
|
63
|
+
* the keys of `entry.order` come first in that order, the rest in base order. Unknown keys are
|
|
64
|
+
* ignored, so an order never moves an item across anchors.
|
|
65
|
+
*/
|
|
66
|
+
export declare function orderItems(items: UiContributionItem[], entry: UiArrangementEntry): UiContributionItem[];
|
|
67
|
+
/** {@link orderItems} without the hidden keys. */
|
|
68
|
+
export declare function arrange(items: UiContributionItem[], entry: UiArrangementEntry): UiContributionItem[];
|
|
69
|
+
/**
|
|
70
|
+
* Applies the per-slot entries to a mixed list: each slot's items are arranged and written back
|
|
71
|
+
* into the positions that slot occupied (a slot without an entry: its base order, grouped by
|
|
72
|
+
* anchor).
|
|
73
|
+
*/
|
|
74
|
+
export declare function arrangeBySlot(items: UiContributionItem[], entryOf: (slot: UiSlotId) => UiArrangementEntry): UiContributionItem[];
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { UI_ARRANGEABLE_SLOTS, UI_CONTRIBUTION_KEY_RE, uiSlot, } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* Arrangement of UI slots (UI slots spec §5): per slot, which contributions are hidden and in
|
|
5
|
+
* which order. "Hidden" means not listed: the access gate never reads an arrangement, so a hidden
|
|
6
|
+
* contribution's view, action and embed routes keep working for an allowed role.
|
|
7
|
+
*/
|
|
8
|
+
const Key = z.string().regex(UI_CONTRIBUTION_KEY_RE);
|
|
9
|
+
const LIST_MAX = 200;
|
|
10
|
+
export const UiArrangementEntrySchema = z
|
|
11
|
+
.object({ hidden: z.array(Key).max(LIST_MAX), order: z.array(Key).max(LIST_MAX) })
|
|
12
|
+
.strict();
|
|
13
|
+
export const UiArrangementSchema = z.partialRecord(z.enum(UI_ARRANGEABLE_SLOTS), UiArrangementEntrySchema);
|
|
14
|
+
/** The slots an org arranges: every arrangeable slot but the platform's own. */
|
|
15
|
+
export const ORG_ARRANGEABLE = UI_ARRANGEABLE_SLOTS.filter((s) => s !== "platform.page");
|
|
16
|
+
/** An org's patch: a slot's entry replaces the stored one, null removes it. */
|
|
17
|
+
export const OrgUiArrangementPatchSchema = z.partialRecord(z.enum(ORG_ARRANGEABLE), UiArrangementEntrySchema.nullable());
|
|
18
|
+
/** A deployment patch: like {@link OrgUiArrangementPatchSchema} over every arrangeable slot. */
|
|
19
|
+
export const UiArrangementPatchSchema = z.partialRecord(z.enum(UI_ARRANGEABLE_SLOTS), UiArrangementEntrySchema.nullable());
|
|
20
|
+
/** Merges a per-slot patch into the stored arrangement (null removes the slot's entry). */
|
|
21
|
+
export function mergeArrangement(current, patch) {
|
|
22
|
+
const merged = { ...current };
|
|
23
|
+
for (const [slot, entry] of Object.entries(patch)) {
|
|
24
|
+
if (entry === null)
|
|
25
|
+
delete merged[slot];
|
|
26
|
+
else if (entry !== undefined)
|
|
27
|
+
merged[slot] = entry;
|
|
28
|
+
}
|
|
29
|
+
return merged;
|
|
30
|
+
}
|
|
31
|
+
const NONE = { hidden: [], order: [] };
|
|
32
|
+
/** The entry that applies to a slot: the org's if it arranged the slot, else the platform's. */
|
|
33
|
+
export function effectiveEntry(org, platform, slot) {
|
|
34
|
+
const o = org?.[slot];
|
|
35
|
+
if (o !== undefined)
|
|
36
|
+
return { entry: o, source: "org" };
|
|
37
|
+
const p = platform[slot];
|
|
38
|
+
if (p !== undefined)
|
|
39
|
+
return { entry: p, source: "platform" };
|
|
40
|
+
return { entry: NONE, source: "none" };
|
|
41
|
+
}
|
|
42
|
+
const keyOf = (i) => `${i.plugin}/${i.id}`;
|
|
43
|
+
/**
|
|
44
|
+
* Orders the items of one slot (hidden ones stay in place): grouped by anchor in the slot's anchor
|
|
45
|
+
* order, also without an entry (the lists match what the page renders per anchor); within a group
|
|
46
|
+
* the keys of `entry.order` come first in that order, the rest in base order. Unknown keys are
|
|
47
|
+
* ignored, so an order never moves an item across anchors.
|
|
48
|
+
*/
|
|
49
|
+
export function orderItems(items, entry) {
|
|
50
|
+
const anchors = items[0] === undefined ? undefined : uiSlot(items[0].slot)?.anchors;
|
|
51
|
+
const groupOf = (i) => anchors === undefined ? 0 : Math.max(0, anchors.indexOf(i.after ?? anchors[0] ?? ""));
|
|
52
|
+
const rank = new Map(entry.order.map((k, n) => [k, n]));
|
|
53
|
+
return items
|
|
54
|
+
.map((item, base) => ({ item, base }))
|
|
55
|
+
.sort((a, b) => {
|
|
56
|
+
const g = groupOf(a.item) - groupOf(b.item);
|
|
57
|
+
if (g !== 0)
|
|
58
|
+
return g;
|
|
59
|
+
const ra = rank.get(keyOf(a.item));
|
|
60
|
+
const rb = rank.get(keyOf(b.item));
|
|
61
|
+
if (ra !== undefined && rb !== undefined)
|
|
62
|
+
return ra - rb;
|
|
63
|
+
if (ra !== undefined)
|
|
64
|
+
return -1;
|
|
65
|
+
if (rb !== undefined)
|
|
66
|
+
return 1;
|
|
67
|
+
return a.base - b.base;
|
|
68
|
+
})
|
|
69
|
+
.map((x) => x.item);
|
|
70
|
+
}
|
|
71
|
+
/** {@link orderItems} without the hidden keys. */
|
|
72
|
+
export function arrange(items, entry) {
|
|
73
|
+
const hidden = new Set(entry.hidden);
|
|
74
|
+
return orderItems(items, entry).filter((i) => !hidden.has(keyOf(i)));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Applies the per-slot entries to a mixed list: each slot's items are arranged and written back
|
|
78
|
+
* into the positions that slot occupied (a slot without an entry: its base order, grouped by
|
|
79
|
+
* anchor).
|
|
80
|
+
*/
|
|
81
|
+
export function arrangeBySlot(items, entryOf) {
|
|
82
|
+
const bySlot = new Map();
|
|
83
|
+
items.forEach((i, n) => {
|
|
84
|
+
const at = bySlot.get(i.slot);
|
|
85
|
+
if (at)
|
|
86
|
+
at.push(n);
|
|
87
|
+
else
|
|
88
|
+
bySlot.set(i.slot, [n]);
|
|
89
|
+
});
|
|
90
|
+
const out = [...items];
|
|
91
|
+
for (const [slot, at] of bySlot) {
|
|
92
|
+
const arranged = arrange(at.map((n) => items[n]), entryOf(slot));
|
|
93
|
+
at.forEach((n, j) => {
|
|
94
|
+
out[n] = arranged[j];
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return out.filter((i) => i !== undefined);
|
|
98
|
+
}
|
package/dist/ui/embed-token.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EmbedClaims } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import type { EmbedClaims, OrgEmbedClaims, PlatformEmbedClaims } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { Keyring } from "../ctx/secrets.js";
|
|
3
3
|
/** 嵌入令牌的有效期(秒)。网页在过期前 60 秒续期。 */
|
|
4
4
|
export declare const EMBED_TOKEN_TTL_SECONDS = 300;
|
|
@@ -22,7 +22,7 @@ export declare class EmbedTokens {
|
|
|
22
22
|
private readonly keys;
|
|
23
23
|
constructor(keyring: Keyring, now?: () => number);
|
|
24
24
|
/** 签发:`exp` = 现在 + {@link EMBED_TOKEN_TTL_SECONDS}。 */
|
|
25
|
-
issue(c: Omit<
|
|
25
|
+
issue(c: Omit<OrgEmbedClaims, "exp"> | Omit<PlatformEmbedClaims, "exp">): {
|
|
26
26
|
token: string;
|
|
27
27
|
claims: EmbedClaims;
|
|
28
28
|
expiresAt: Date;
|