@zackbart/connecta 0.28.1 → 0.29.0
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/CHANGELOG.md +788 -5
- package/README.md +62 -42
- package/bin/connecta.mjs +110 -139
- package/bin/meta-tool-names.d.mts +2 -0
- package/bin/meta-tool-names.mjs +9 -0
- package/bin/version.mjs +3 -0
- package/dist/access-tokens.js +53 -63
- package/dist/activity-request.js +8 -0
- package/dist/activity.d.ts +76 -42
- package/dist/activity.js +110 -42
- package/dist/auth/clerk-sdk/client.d.ts +65 -0
- package/dist/auth/clerk-sdk/client.js +5 -0
- package/dist/auth/clerk-sdk/generated.js +1845 -0
- package/dist/auth/clerk-transport.js +19 -0
- package/dist/auth/clerk.d.ts +17 -7
- package/dist/auth/clerk.js +189 -107
- package/dist/auth/cloudflare-access.js +25 -26
- package/dist/auth/downstream-client-metadata.d.ts +29 -0
- package/dist/auth/downstream-client-metadata.js +133 -0
- package/dist/auth/downstream-oauth.js +1584 -1580
- package/dist/auth/downstream-revocation.js +106 -0
- package/dist/auth/oauth-token-response.js +231 -0
- package/dist/auth/oauth-v2-migration.js +116 -0
- package/dist/auth/static-oauth.js +93 -71
- package/dist/auth-elicitation.js +283 -0
- package/dist/branding.d.ts +8 -16
- package/dist/branding.js +26 -59
- package/dist/byte-read-response.js +39 -0
- package/dist/call-admission.js +12 -16
- package/dist/catalog-cache.js +739 -0
- package/dist/catalog-drift.d.ts +50 -15
- package/dist/catalog-drift.js +365 -128
- package/dist/catalog-fingerprint.js +2 -4
- package/dist/catalog-freshness.js +13 -0
- package/dist/catalog-limits.js +2 -2
- package/dist/catalog-service.js +368 -224
- package/dist/catalog.js +43 -84
- package/dist/concurrency.js +3 -4
- package/dist/config-defaults.d.ts +39 -0
- package/dist/config-defaults.js +46 -0
- package/dist/config-schema.d.ts +181 -0
- package/dist/config-schema.js +435 -0
- package/dist/config-value-sources.js +102 -0
- package/dist/config.d.ts +631 -0
- package/dist/config.js +503 -0
- package/dist/connector-access.d.ts +1 -0
- package/dist/connector-access.js +13 -15
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +15 -6
- package/dist/connector-scope.js +1 -1
- package/dist/connectors/api-connector.d.ts +59 -7
- package/dist/connectors/api-connector.js +106 -19
- package/dist/connectors/api.js +4 -1
- package/dist/connectors/guarded-fetch.d.ts +144 -0
- package/dist/connectors/guarded-fetch.js +84 -35
- package/dist/connectors/negotiation-cache.js +46 -0
- package/dist/connectors/option-shapes.js +27 -0
- package/dist/connectors/remote-mcp.d.ts +30 -4
- package/dist/connectors/remote-mcp.js +1774 -320
- package/dist/connectors/resource-uri.js +335 -0
- package/dist/credential-contract.d.ts +10 -0
- package/dist/credential-rules.js +1 -3
- package/dist/credential-url.js +108 -0
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +27 -18
- package/dist/d1.d.ts +130 -0
- package/dist/d1.js +290 -0
- package/dist/describe-config.d.ts +165 -0
- package/dist/describe-config.js +315 -0
- package/dist/described.js +106 -0
- package/dist/downstream-input-context.js +119 -0
- package/dist/downstream-input.js +323 -0
- package/dist/errors.d.ts +97 -20
- package/dist/errors.js +158 -80
- package/dist/execute.js +453 -305
- package/dist/executor-admission.d.ts +1 -0
- package/dist/executor-admission.js +11 -15
- package/dist/executor-contract.d.ts +20 -0
- package/dist/executor-contract.js +48 -0
- package/dist/executor-result.js +24 -9
- package/dist/executors/quickjs-child.js +3 -4
- package/dist/executors/quickjs-runtime.js +147 -59
- package/dist/executors/quickjs.js +29 -35
- package/dist/favicon.js +1 -2
- package/dist/fix-prompt.js +1 -3
- package/dist/guest-runtime.js +188 -0
- package/dist/guest-types.d.ts +176 -0
- package/dist/guest-types.js +1 -0
- package/dist/html-security.js +24 -0
- package/dist/identity.js +1 -3
- package/dist/inbound-credential.js +52 -0
- package/dist/index.d.ts +24 -256
- package/dist/index.js +65 -478
- package/dist/invocation-auth.js +69 -0
- package/dist/invocation.js +230 -73
- package/dist/json-schema.d.ts +1 -1
- package/dist/mcp-client-context.js +35 -0
- package/dist/mcp-result.js +11 -7
- package/dist/meta-output.js +76 -0
- package/dist/meta-tool-names.d.ts +2 -0
- package/dist/meta-tool-names.js +3 -0
- package/dist/meta-tools.js +285 -209
- package/dist/module-contracts.d.ts +8 -23
- package/dist/node.d.ts +0 -2
- package/dist/node.js +2 -2
- package/dist/oauth-callback-outcome.js +1 -3
- package/dist/oauth-handoff.js +117 -0
- package/dist/oauth-sealing.js +20 -0
- package/dist/operator-record.d.ts +137 -0
- package/dist/operator-record.js +420 -0
- package/dist/operator-ui/assets.js +31 -0
- package/dist/operator-ui/contract.d.ts +72 -0
- package/dist/operator-ui/contract.js +1 -0
- package/dist/operator-ui/fix-prompts.d.ts +18 -0
- package/dist/operator-ui/generated.js +5 -4
- package/dist/operator-ui/model.d.ts +12 -87
- package/dist/operator-ui/model.js +2 -9
- package/dist/operator-ui/setup-commands.js +1 -3
- package/dist/operator-ui/view.d.ts +323 -0
- package/dist/operator-ui/view.js +62 -189
- package/dist/page-styles.js +2 -3
- package/dist/program-source.js +14 -2
- package/dist/{exempt-writes.js → program-writes.js} +55 -18
- package/dist/provider.d.ts +142 -0
- package/dist/provider.js +187 -0
- package/dist/providers/_shared/google/result-size.js +57 -0
- package/dist/providers/_shared/google/service-account.d.ts +58 -0
- package/dist/providers/_shared/google/service-account.js +500 -0
- package/dist/providers/_shared/google/workspace.d.ts +276 -0
- package/dist/providers/_shared/google/workspace.js +518 -0
- package/dist/providers/{basecamp.d.ts → basecamp/index.d.ts} +6 -28
- package/dist/providers/basecamp/index.js +997 -0
- package/dist/providers/basecamp/skill.generated.js +9 -0
- package/dist/providers/{breeze.d.ts → breeze/index.d.ts} +2 -2
- package/dist/providers/{breeze.js → breeze/index.js} +184 -140
- package/dist/providers/breeze/skill.generated.js +9 -0
- package/dist/providers/{ccb.d.ts → ccb/index.d.ts} +2 -2
- package/dist/providers/{ccb.js → ccb/index.js} +271 -116
- package/dist/providers/ccb/skill.generated.js +9 -0
- package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
- package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
- package/dist/providers/cloudflare/skill.generated.js +10 -0
- package/dist/providers/docs/index.d.ts +54 -0
- package/dist/providers/docs/index.js +1237 -0
- package/dist/providers/docs/skill.generated.js +9 -0
- package/dist/providers/drive/index.d.ts +53 -0
- package/dist/providers/drive/index.js +1523 -0
- package/dist/providers/drive/skill.generated.js +9 -0
- package/dist/providers/forms/index.d.ts +54 -0
- package/dist/providers/forms/index.js +1217 -0
- package/dist/providers/forms/skill.generated.js +11 -0
- package/dist/providers/github/auth.d.ts +32 -0
- package/dist/providers/github/auth.js +361 -0
- package/dist/providers/github/hosted.js +218 -0
- package/dist/providers/github/index.d.ts +13 -0
- package/dist/providers/github/index.js +140 -0
- package/dist/providers/github/key.js +100 -0
- package/dist/providers/github/rest.js +321 -0
- package/dist/providers/github/scope.d.ts +29 -0
- package/dist/providers/github/scope.js +124 -0
- package/dist/providers/github/skill.generated.js +8 -0
- package/dist/providers/gmail/index.d.ts +51 -0
- package/dist/providers/gmail/index.js +1436 -0
- package/dist/providers/gmail/skill.generated.js +9 -0
- package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
- package/dist/providers/linear/index.js +352 -0
- package/dist/providers/linear/skill.generated.js +8 -0
- package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
- package/dist/providers/mixpanel/index.js +416 -0
- package/dist/providers/mixpanel/skill.generated.js +9 -0
- package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
- package/dist/providers/{notion.js → notion/index.js} +226 -239
- package/dist/providers/notion/skill.generated.js +10 -0
- package/dist/providers/{overflow.d.ts → overflow/index.d.ts} +2 -2
- package/dist/providers/{overflow.js → overflow/index.js} +74 -111
- package/dist/providers/overflow/skill.generated.js +8 -0
- package/dist/providers/{planning-center.d.ts → planning-center/index.d.ts} +2 -2
- package/dist/providers/{planning-center.js → planning-center/index.js} +264 -104
- package/dist/providers/planning-center/skill.generated.js +10 -0
- package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
- package/dist/providers/revenuecat/index.js +557 -0
- package/dist/providers/revenuecat/skill.generated.js +11 -0
- package/dist/providers/sheets/index.d.ts +54 -0
- package/dist/providers/sheets/index.js +1237 -0
- package/dist/providers/sheets/skill.generated.js +10 -0
- package/dist/providers/slides/index.d.ts +53 -0
- package/dist/providers/slides/index.js +2645 -0
- package/dist/providers/slides/skill.generated.js +9 -0
- package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
- package/dist/providers/stripe/index.js +266 -0
- package/dist/providers/stripe/skill.generated.js +11 -0
- package/dist/providers/{tithely.d.ts → tithely/index.d.ts} +2 -2
- package/dist/providers/{tithely.js → tithely/index.js} +60 -91
- package/dist/providers/tithely/skill.generated.js +8 -0
- package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
- package/dist/providers/vercel/index.js +1038 -0
- package/dist/providers/vercel/mcp-ownership.js +212 -0
- package/dist/providers/vercel/skill.generated.js +11 -0
- package/dist/registry.d.ts +108 -171
- package/dist/registry.js +513 -925
- package/dist/request-state.js +21 -0
- package/dist/resource-read.js +100 -0
- package/dist/result-shapes.js +17 -49
- package/dist/retired-toolkits.js +1 -1
- package/dist/routes/access-tokens.js +3 -6
- package/dist/routes/activity-context.js +21 -0
- package/dist/routes/activity-disclosure.js +40 -0
- package/dist/routes/activity.js +59 -21
- package/dist/routes/actor-labels.js +3 -50
- package/dist/routes/connect.js +221 -0
- package/dist/routes/credentials.js +15 -27
- package/dist/routes/mcp.js +356 -130
- package/dist/routes/oauth-client-metadata.js +15 -0
- package/dist/routes/oauth-management.js +45 -107
- package/dist/routes/oauth.js +110 -57
- package/dist/routes/operator.js +3 -3
- package/dist/routes/shared.d.ts +25 -64
- package/dist/routes/shared.js +160 -80
- package/dist/routes/skills.js +30 -0
- package/dist/routes/ui-config.js +225 -0
- package/dist/routes/ui-data.js +40 -35
- package/dist/routes/ui.js +66 -56
- package/dist/runtime/admission.js +4 -2
- package/dist/runtime/call-admission.js +3 -1
- package/dist/runtime/connector-scope.js +1 -4
- package/dist/runtime/run.js +10 -39
- package/dist/runtime/services.js +12 -117
- package/dist/runtime/shared-read.js +1 -3
- package/dist/runtime/storage.js +8 -1
- package/dist/sent-secrets.d.ts +52 -0
- package/dist/sent-secrets.js +474 -0
- package/dist/server.js +28 -71
- package/dist/skills.js +406 -174
- package/dist/sqlite.d.ts +48 -0
- package/dist/sqlite.js +166 -0
- package/dist/storage/keys.js +428 -0
- package/dist/storage/memory.js +7 -0
- package/dist/storage/sql.d.ts +66 -0
- package/dist/storage/sql.js +534 -0
- package/dist/timeout.js +4 -2
- package/dist/tool-name.js +17 -0
- package/dist/tool-safety.d.ts +9 -35
- package/dist/tool-safety.js +20 -40
- package/dist/types.d.ts +320 -54
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +7 -19
- package/dist/ui.js +76 -90
- package/dist/url-safety.js +3 -6
- package/dist/usage-guide.js +137 -0
- package/dist/validate.d.ts +7 -0
- package/dist/validate.js +309 -47
- package/dist/version.d.ts +1 -7
- package/dist/version.js +3 -7
- package/dist/worker.d.ts +11 -0
- package/dist/worker.js +434 -0
- package/documentation/architecture.md +664 -231
- package/documentation/auth.md +936 -546
- package/documentation/code-mode.md +501 -312
- package/documentation/meta-tools.md +449 -200
- package/documentation/operator-tests.md +93 -0
- package/documentation/provider-migration-0.29.md +60 -0
- package/examples/worker/AGENTS.md +34 -12
- package/examples/worker/README.md +286 -255
- package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
- package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
- package/examples/worker/src/connecta.config.ts +151 -0
- package/examples/worker/src/index.ts +14 -197
- package/examples/worker/wrangler.jsonc +15 -35
- package/package.json +120 -54
- package/templates/node/.dockerignore +1 -2
- package/templates/node/.env.example +20 -21
- package/templates/node/AGENTS.md +21 -17
- package/templates/node/Dockerfile +3 -5
- package/templates/node/README.md +133 -58
- package/templates/node/docker-compose.yml +11 -12
- package/templates/node/package.json +4 -2
- package/templates/node/src/connecta.config.ts +119 -0
- package/templates/node/src/index.ts +9 -154
- package/templates/node/src/provision-token.ts +17 -0
- package/AGENTS.md +0 -170
- package/dist/artifacts/connector.d.ts +0 -58
- package/dist/artifacts/connector.js +0 -730
- package/dist/artifacts/document.js +0 -86
- package/dist/artifacts/guide.js +0 -140
- package/dist/artifacts/html-scan.js +0 -329
- package/dist/artifacts/json.js +0 -78
- package/dist/artifacts/kv-store.d.ts +0 -20
- package/dist/artifacts/kv-store.js +0 -140
- package/dist/artifacts/markdown.js +0 -478
- package/dist/artifacts/module.d.ts +0 -37
- package/dist/artifacts/module.js +0 -66
- package/dist/artifacts/operations.d.ts +0 -197
- package/dist/artifacts/operations.js +0 -1002
- package/dist/artifacts/refresh.d.ts +0 -55
- package/dist/artifacts/refresh.js +0 -236
- package/dist/artifacts/routes.js +0 -206
- package/dist/artifacts/types.d.ts +0 -214
- package/dist/artifacts/types.js +0 -22
- package/dist/artifacts/validate.d.ts +0 -62
- package/dist/artifacts/validate.js +0 -651
- package/dist/artifacts.d.ts +0 -6
- package/dist/artifacts.js +0 -9
- package/dist/auth/bearer.d.ts +0 -11
- package/dist/auth/bearer.js +0 -50
- package/dist/providers/basecamp.js +0 -445
- package/dist/providers/linear.js +0 -217
- package/dist/providers/mixpanel.js +0 -247
- package/dist/providers/revenuecat.js +0 -317
- package/dist/providers/stripe.js +0 -253
- package/dist/providers/vercel.js +0 -1253
- package/dist/storage/file.d.ts +0 -15
- package/dist/storage/file.js +0 -455
- package/ethos.md +0 -93
- package/examples/worker/src/cloudflare-kv.ts +0 -43
- package/examples/worker/src/d1-activity-row.ts +0 -155
- package/examples/worker/src/d1-activity.ts +0 -147
- package/examples/worker/src/d1-storage.ts +0 -138
- package/examples/worker/src/r2-artifact-blobs.ts +0 -29
- package/templates/node/src/file-activity.ts +0 -182
package/dist/operator-ui/view.js
CHANGED
|
@@ -1,16 +1,37 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Everything the operator app knows, and every rule for changing it, with no
|
|
3
|
+
* DOM in sight. The components render this and nothing else, so the questions
|
|
4
|
+
* that matter — what a page shows while it loads, what an identity change
|
|
5
|
+
* erases, whether a value may become an href — are answered by functions a test
|
|
6
|
+
* can call directly rather than by reading rendered markup back out of a
|
|
7
|
+
* browser.
|
|
8
|
+
*/
|
|
9
|
+
const REGISTRATION_LABELS = {
|
|
10
|
+
cimd: "Client metadata document (CIMD)",
|
|
11
|
+
dcr: "Dynamic registration (DCR)",
|
|
12
|
+
static: "Pre-registered client",
|
|
13
|
+
};
|
|
14
|
+
export function registrationPathLabel(path) {
|
|
15
|
+
return REGISTRATION_LABELS[path];
|
|
16
|
+
}
|
|
17
|
+
/** Pages the nav lists. */
|
|
2
18
|
export const OPERATOR_PAGES = [
|
|
19
|
+
"overview",
|
|
3
20
|
"connections",
|
|
4
|
-
"
|
|
21
|
+
"tools",
|
|
22
|
+
"access",
|
|
5
23
|
"activity",
|
|
6
|
-
"
|
|
24
|
+
"config",
|
|
7
25
|
];
|
|
8
26
|
export const PAGE_META = {
|
|
27
|
+
overview: { path: "/", label: "Overview" },
|
|
28
|
+
connector: { path: "/connectors", label: "Connector" },
|
|
29
|
+
tools: { path: "/tools", label: "Tools" },
|
|
30
|
+
access: { path: "/access", label: "Access" },
|
|
31
|
+
config: { path: "/config", label: "Config" },
|
|
9
32
|
tokens: { path: "/tokens", label: "Access tokens" },
|
|
10
|
-
connections: { path: "/", label: "
|
|
33
|
+
connections: { path: "/connectors", label: "Connectors" },
|
|
11
34
|
activity: { path: "/activity", label: "Activity" },
|
|
12
|
-
artifacts: { path: "/artifacts", label: "Artifacts" },
|
|
13
|
-
artifact: { path: "/artifacts", label: "Artifact" },
|
|
14
35
|
};
|
|
15
36
|
/**
|
|
16
37
|
* What each page is for, in one line under its heading — on the gate as well
|
|
@@ -21,46 +42,20 @@ export function pageDescription(page, productDescription) {
|
|
|
21
42
|
if (page === "activity") {
|
|
22
43
|
return "Every connector tool call, by who made it and how it ended. Arguments and results are never stored.";
|
|
23
44
|
}
|
|
24
|
-
if (isArtifactPage(page)) {
|
|
25
|
-
return "Pages agents published for the team. Each one keeps every version.";
|
|
26
|
-
}
|
|
27
45
|
return productDescription;
|
|
28
46
|
}
|
|
29
47
|
/** What the gate says while the session is checked, page by page. */
|
|
30
|
-
export function checkingCopy(
|
|
31
|
-
if (page === "artifact")
|
|
32
|
-
return "Loading artifact…";
|
|
33
|
-
if (page === "artifacts")
|
|
34
|
-
return "Loading artifacts…";
|
|
48
|
+
export function checkingCopy() {
|
|
35
49
|
return "Checking your session…";
|
|
36
50
|
}
|
|
37
51
|
export function pageForPath(path) {
|
|
38
|
-
if (path
|
|
39
|
-
return "
|
|
52
|
+
if (/^\/connectors\/[a-z0-9_-]+$/.test(path))
|
|
53
|
+
return "connector";
|
|
54
|
+
if (path === "/tokens")
|
|
55
|
+
return "tokens";
|
|
40
56
|
const match = OPERATOR_PAGES.find((page) => PAGE_META[page].path === path);
|
|
41
57
|
return match ?? "connections";
|
|
42
58
|
}
|
|
43
|
-
/** Artifact pages load their own data and never ask `/ui/data` whether to open. */
|
|
44
|
-
export function isArtifactPage(page) {
|
|
45
|
-
return page === "artifacts" || page === "artifact";
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Where a viewer's frame gets its page: `/artifacts/<id>` or a snapshot,
|
|
49
|
-
* `/artifacts/<id>/v/<version>?d=<name>:<version>…`, mapped to its API call.
|
|
50
|
-
*/
|
|
51
|
-
export function artifactViewRequest(pathname, search) {
|
|
52
|
-
const match = /^\/artifacts\/([a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?)(?:\/v\/(\d{1,9}))?$/.exec(pathname);
|
|
53
|
-
if (!match?.[1])
|
|
54
|
-
return undefined;
|
|
55
|
-
const params = new URLSearchParams();
|
|
56
|
-
if (match[2]) {
|
|
57
|
-
params.set("v", match[2]);
|
|
58
|
-
for (const pin of new URLSearchParams(search).getAll("d"))
|
|
59
|
-
params.append("d", pin);
|
|
60
|
-
}
|
|
61
|
-
const query = params.toString();
|
|
62
|
-
return `/artifacts/_api/view/${match[1]}${query ? `?${query}` : ""}`;
|
|
63
|
-
}
|
|
64
59
|
export function info(message) {
|
|
65
60
|
return { message, tone: "info" };
|
|
66
61
|
}
|
|
@@ -78,6 +73,8 @@ const REFUSED_COPY = {
|
|
|
78
73
|
*/
|
|
79
74
|
export function oauthDoneNotice(action, answer, opened = true) {
|
|
80
75
|
if (action === "oauth_disconnect") {
|
|
76
|
+
if (answer?.code === "oauth_revocation_failed")
|
|
77
|
+
return info("Disconnected locally. Provider revocation could not be confirmed. Revoke the grant in the provider's console.");
|
|
81
78
|
return info("Disconnected. Connect again whenever you are ready.");
|
|
82
79
|
}
|
|
83
80
|
if (answer?.state === "ok")
|
|
@@ -100,8 +97,7 @@ export function credentialTestNotice(connectorId, answer) {
|
|
|
100
97
|
* checked, not trusted.
|
|
101
98
|
*/
|
|
102
99
|
export function refusedNotice(action, connectorId, problem) {
|
|
103
|
-
if (action === "credential_test" &&
|
|
104
|
-
(problem === "credential_required" || problem === "credential_mismatch")) {
|
|
100
|
+
if (action === "credential_test" && (problem === "credential_required" || problem === "credential_mismatch")) {
|
|
105
101
|
return failure(PROBLEM_COPY[problem], { kind: problem, connectorId });
|
|
106
102
|
}
|
|
107
103
|
return failure(REFUSED_COPY[action], {
|
|
@@ -166,18 +162,14 @@ export function loadFailureCopy(failure, productName) {
|
|
|
166
162
|
};
|
|
167
163
|
}
|
|
168
164
|
/** A collection page's load failure: fixed words, whatever the route said. */
|
|
169
|
-
export function collectionFailureCopy(
|
|
165
|
+
export function collectionFailureCopy(_collection, facts, productName) {
|
|
170
166
|
if (facts.kind === "network") {
|
|
171
167
|
return `Couldn't reach ${productName}. Check your connection, then retry.`;
|
|
172
168
|
}
|
|
173
169
|
if (facts.kind === "session")
|
|
174
170
|
return "Your session has ended. Sign in again to see this page.";
|
|
175
171
|
if (facts.kind === "forbidden" || facts.status === 404) {
|
|
176
|
-
|
|
177
|
-
return "There is no artifact here, or this identity can't open it.";
|
|
178
|
-
return collection === "activity"
|
|
179
|
-
? "Activity isn't available to this identity."
|
|
180
|
-
: "Artifacts aren't available to this identity.";
|
|
172
|
+
return "Activity isn't available to this identity.";
|
|
181
173
|
}
|
|
182
174
|
return "The deployment answered with an error. Retry in a moment; if it keeps failing, the deployment's log has the reason.";
|
|
183
175
|
}
|
|
@@ -226,6 +218,9 @@ export function initialState(page) {
|
|
|
226
218
|
function identityScopedState() {
|
|
227
219
|
return {
|
|
228
220
|
data: null,
|
|
221
|
+
contract: null,
|
|
222
|
+
contractPhase: "idle",
|
|
223
|
+
health: "loading",
|
|
229
224
|
connectorFilter: "",
|
|
230
225
|
oauthNotice: null,
|
|
231
226
|
oauthNoticeFor: null,
|
|
@@ -248,13 +243,6 @@ function identityScopedState() {
|
|
|
248
243
|
activityEvents: [],
|
|
249
244
|
activityCursor: null,
|
|
250
245
|
activitySearch: "",
|
|
251
|
-
artifactPhase: "idle",
|
|
252
|
-
artifactNotice: null,
|
|
253
|
-
artifactRows: [],
|
|
254
|
-
artifactCursor: null,
|
|
255
|
-
artifactQuery: "",
|
|
256
|
-
artifactArchived: false,
|
|
257
|
-
artifactView: null,
|
|
258
246
|
};
|
|
259
247
|
}
|
|
260
248
|
/**
|
|
@@ -310,117 +298,22 @@ export function connectorStatusLabel(status, problem) {
|
|
|
310
298
|
return "Loading details";
|
|
311
299
|
if (status === "ok")
|
|
312
300
|
return "Connected";
|
|
301
|
+
if (status === "credential_required")
|
|
302
|
+
return "Credential needed";
|
|
313
303
|
if (status === "auth_required") {
|
|
314
304
|
return problem === "credential_required" ? "Credential needed" : "Authorization needed";
|
|
315
305
|
}
|
|
316
306
|
return "Unavailable";
|
|
317
307
|
}
|
|
318
|
-
export function toolCountLabel(count) {
|
|
319
|
-
return `${count} ${count === 1 ? "tool" : "tools"}`;
|
|
320
|
-
}
|
|
321
308
|
export function connectorStatusTone(status) {
|
|
322
309
|
if (status === "ok")
|
|
323
310
|
return "ok";
|
|
324
|
-
if (status === "auth_required")
|
|
311
|
+
if (status === "auth_required" || status === "credential_required")
|
|
325
312
|
return "warn";
|
|
326
313
|
if (status === "loading")
|
|
327
314
|
return "neutral";
|
|
328
315
|
return "danger";
|
|
329
316
|
}
|
|
330
|
-
export function summarizeConnectors(connectors) {
|
|
331
|
-
const summary = {
|
|
332
|
-
total: connectors.length,
|
|
333
|
-
connected: 0,
|
|
334
|
-
attention: 0,
|
|
335
|
-
credentials: 0,
|
|
336
|
-
unavailable: 0,
|
|
337
|
-
loading: 0,
|
|
338
|
-
tools: 0,
|
|
339
|
-
drifting: 0,
|
|
340
|
-
};
|
|
341
|
-
for (const connector of connectors) {
|
|
342
|
-
if (connector.status === "ok")
|
|
343
|
-
summary.connected += 1;
|
|
344
|
-
else if (connector.status === "auth_required") {
|
|
345
|
-
if (connector.problem === "credential_required")
|
|
346
|
-
summary.credentials += 1;
|
|
347
|
-
else
|
|
348
|
-
summary.attention += 1;
|
|
349
|
-
}
|
|
350
|
-
else if (connector.status === "loading")
|
|
351
|
-
summary.loading += 1;
|
|
352
|
-
else
|
|
353
|
-
summary.unavailable += 1;
|
|
354
|
-
summary.tools += connector.toolCount || 0;
|
|
355
|
-
if (driftState(connector.catalogDrift) === "warning")
|
|
356
|
-
summary.drifting += 1;
|
|
357
|
-
}
|
|
358
|
-
return summary;
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* The one line above the list. Connected and tool counts are always present;
|
|
362
|
-
* the two counts an operator may have to act on appear only when they are not
|
|
363
|
-
* zero, so a healthy deployment stays short.
|
|
364
|
-
*/
|
|
365
|
-
export function connectorSummaryParts(summary) {
|
|
366
|
-
// Nothing has answered yet: "0 connected · 0 tools" would be a claim, and
|
|
367
|
-
// a false one.
|
|
368
|
-
if (summary.total > 0 && summary.loading === summary.total) {
|
|
369
|
-
return [
|
|
370
|
-
{
|
|
371
|
-
text: `Checking ${summary.total} connector${summary.total === 1 ? "" : "s"}…`,
|
|
372
|
-
tone: "neutral",
|
|
373
|
-
},
|
|
374
|
-
];
|
|
375
|
-
}
|
|
376
|
-
return [
|
|
377
|
-
{ text: `${summary.connected} connected`, tone: "neutral" },
|
|
378
|
-
...(summary.attention
|
|
379
|
-
? [
|
|
380
|
-
{
|
|
381
|
-
text: `${summary.attention} need${summary.attention === 1 ? "s" : ""} authorization`,
|
|
382
|
-
tone: "warn",
|
|
383
|
-
},
|
|
384
|
-
]
|
|
385
|
-
: []),
|
|
386
|
-
...(summary.credentials
|
|
387
|
-
? [
|
|
388
|
-
{
|
|
389
|
-
text: `${summary.credentials} need${summary.credentials === 1 ? "s" : ""} a credential`,
|
|
390
|
-
tone: "warn",
|
|
391
|
-
},
|
|
392
|
-
]
|
|
393
|
-
: []),
|
|
394
|
-
...(summary.unavailable
|
|
395
|
-
? [{ text: `${summary.unavailable} unavailable`, tone: "danger" }]
|
|
396
|
-
: []),
|
|
397
|
-
{ text: toolCountLabel(summary.tools), tone: "neutral" },
|
|
398
|
-
...(summary.loading
|
|
399
|
-
? [{ text: `${summary.loading} still loading`, tone: "neutral" }]
|
|
400
|
-
: []),
|
|
401
|
-
];
|
|
402
|
-
}
|
|
403
|
-
/**
|
|
404
|
-
* The badge for each call path, keyed by the server's classification so each
|
|
405
|
-
* state is a row here rather than a branch in a component.
|
|
406
|
-
*/
|
|
407
|
-
export const TOOL_SAFETY_BADGE = {
|
|
408
|
-
runs_in_programs: {
|
|
409
|
-
label: "runs in programs",
|
|
410
|
-
tone: "ok",
|
|
411
|
-
title: "Explicitly read-only: execute_code programs may call it without asking.",
|
|
412
|
-
},
|
|
413
|
-
exempt: {
|
|
414
|
-
label: "exempt from approval",
|
|
415
|
-
tone: "neutral",
|
|
416
|
-
title: "Not read-only, but this deployment's config lets programs call it without asking. Each call still counts against the write budget and appears in activity; call_tool still refuses it.",
|
|
417
|
-
},
|
|
418
|
-
needs_approval: {
|
|
419
|
-
label: "asks for approval",
|
|
420
|
-
tone: "warn",
|
|
421
|
-
title: "Not explicitly read-only: programs refuse it, and it runs through call_destructive_tool, where the host asks first.",
|
|
422
|
-
},
|
|
423
|
-
};
|
|
424
317
|
/**
|
|
425
318
|
* What the page says about a connector that is not usable, one fixed sentence
|
|
426
319
|
* per problem kind the server classified. This is the only account of a
|
|
@@ -439,7 +332,7 @@ const PROBLEM_COPY = {
|
|
|
439
332
|
catalog_failed: "Connected, but its tool catalog could not be loaded, so none of its tools are served.",
|
|
440
333
|
};
|
|
441
334
|
export function problemCopy(problem) {
|
|
442
|
-
return problem ? PROBLEM_COPY[problem] ?? null : null;
|
|
335
|
+
return problem ? (PROBLEM_COPY[problem] ?? null) : null;
|
|
443
336
|
}
|
|
444
337
|
/**
|
|
445
338
|
* A problem someone can fix by authorizing is a warning, matching the amber
|
|
@@ -447,16 +340,10 @@ export function problemCopy(problem) {
|
|
|
447
340
|
* error. The two used to share the red box, so the row contradicted itself.
|
|
448
341
|
*/
|
|
449
342
|
export function problemTone(problem) {
|
|
450
|
-
return problem === "oauth_required" ||
|
|
451
|
-
problem === "credential_required" ||
|
|
452
|
-
problem === "auth_required"
|
|
343
|
+
return problem === "oauth_required" || problem === "credential_required" || problem === "auth_required"
|
|
453
344
|
? "warn"
|
|
454
345
|
: "danger";
|
|
455
346
|
}
|
|
456
|
-
/** Who owns this connector's downstream credentials, in two words. */
|
|
457
|
-
export function authScopeLabel(scope) {
|
|
458
|
-
return scope === "personal" ? "personal auth" : "shared auth";
|
|
459
|
-
}
|
|
460
347
|
/**
|
|
461
348
|
* What this identity may do with a connector, in one line. Being able to use it
|
|
462
349
|
* is implied by seeing it at all, so the sentence covers authentication, which
|
|
@@ -472,7 +359,7 @@ export function permissionLabel(connector) {
|
|
|
472
359
|
return "Authentication for this connection is managed by your deployment.";
|
|
473
360
|
}
|
|
474
361
|
/**
|
|
475
|
-
* The
|
|
362
|
+
* The categories, in the order an operator should read them: what the
|
|
476
363
|
* deployment refuses to call, what it can no longer call, what contradicts a
|
|
477
364
|
* vetted verdict, and what changed shape underneath a reviewed schema. Counts
|
|
478
365
|
* only — a name or a schema on this path would be the payload leak the whole
|
|
@@ -483,8 +370,9 @@ const DRIFT_CATEGORIES = [
|
|
|
483
370
|
{ key: "unservedTools", label: "Unserved" },
|
|
484
371
|
{ key: "annotationConflicts", label: "Annotation conflicts" },
|
|
485
372
|
{ key: "schemaChanges", label: "Schema changes" },
|
|
373
|
+
{ key: "droppedTools", label: "Dropped tool names" },
|
|
486
374
|
];
|
|
487
|
-
|
|
375
|
+
function driftTotal(drift) {
|
|
488
376
|
if (!drift)
|
|
489
377
|
return 0;
|
|
490
378
|
return DRIFT_CATEGORIES.reduce((sum, { key }) => sum + (drift[key] || 0), 0);
|
|
@@ -498,7 +386,7 @@ export function driftState(drift) {
|
|
|
498
386
|
export function driftCounts(drift) {
|
|
499
387
|
if (!drift)
|
|
500
388
|
return [];
|
|
501
|
-
return DRIFT_CATEGORIES.map(({ key, label }) => ({
|
|
389
|
+
return DRIFT_CATEGORIES.filter(({ key }) => key !== "droppedTools" || drift.droppedTools !== undefined).map(({ key, label }) => ({
|
|
502
390
|
key,
|
|
503
391
|
label,
|
|
504
392
|
count: drift[key] || 0,
|
|
@@ -595,7 +483,9 @@ function activityMatches(event, query) {
|
|
|
595
483
|
activityOutcomeBadge(event.outcome).label,
|
|
596
484
|
activityDetail(event),
|
|
597
485
|
actorKindLabel(event.actor?.kind),
|
|
598
|
-
].some((value) => String(value ?? "")
|
|
486
|
+
].some((value) => String(value ?? "")
|
|
487
|
+
.toLowerCase()
|
|
488
|
+
.includes(q));
|
|
599
489
|
}
|
|
600
490
|
export function filterActivity(events, query) {
|
|
601
491
|
return events.filter((event) => activityMatches(event, query));
|
|
@@ -604,20 +494,15 @@ export function filterActivity(events, query) {
|
|
|
604
494
|
export function activitySummary(events) {
|
|
605
495
|
if (events.length === 0)
|
|
606
496
|
return "";
|
|
607
|
-
const
|
|
608
|
-
|
|
497
|
+
const calls = events.filter((event) => event.kind !== "catalog_drift");
|
|
498
|
+
const changes = events.length - calls.length;
|
|
499
|
+
const tools = new Set(calls.map((event) => event.address)).size;
|
|
500
|
+
return `${calls.length} loaded call${calls.length === 1 ? "" : "s"} · ${tools} tool${tools === 1 ? "" : "s"}${changes ? ` · ${changes} catalog change${changes === 1 ? "" : "s"}` : ""}`;
|
|
609
501
|
}
|
|
610
502
|
// A pause and an approval are neither success nor failure: each gets its own
|
|
611
503
|
// class, and the stylesheet paints neither as an error. Nothing has emitted
|
|
612
504
|
// either since issue #672 removed program pauses, but older rows carry them.
|
|
613
|
-
const ACTIVITY_OUTCOMES = [
|
|
614
|
-
"success",
|
|
615
|
-
"error",
|
|
616
|
-
"timeout",
|
|
617
|
-
"cancelled",
|
|
618
|
-
"paused",
|
|
619
|
-
"approved",
|
|
620
|
-
];
|
|
505
|
+
const ACTIVITY_OUTCOMES = ["success", "error", "timeout", "cancelled", "paused", "approved"];
|
|
621
506
|
export function activityOutcomeClass(outcome) {
|
|
622
507
|
return ACTIVITY_OUTCOMES.includes(outcome) ? outcome : "error";
|
|
623
508
|
}
|
|
@@ -665,6 +550,8 @@ function reasonLabel(code) {
|
|
|
665
550
|
}
|
|
666
551
|
/** The one-line detail under an address: where it ran, retries, and why it stalled. */
|
|
667
552
|
export function activityDetail(event) {
|
|
553
|
+
if (event.kind === "catalog_drift")
|
|
554
|
+
return "Catalog changed";
|
|
668
555
|
const parts = [SOURCE_LABELS[event.source] ?? event.source];
|
|
669
556
|
if (event.approval === "tool")
|
|
670
557
|
parts.push("approved for the rest of the run");
|
|
@@ -672,23 +559,13 @@ export function activityDetail(event) {
|
|
|
672
559
|
parts.push("approved for this call");
|
|
673
560
|
if (event.attempts > 1)
|
|
674
561
|
parts.push(`${event.attempts} attempts`);
|
|
675
|
-
const reasons = [event.friction, event.errorCode]
|
|
676
|
-
.filter((code) => Boolean(code))
|
|
677
|
-
.map(reasonLabel);
|
|
562
|
+
const reasons = [event.friction, event.errorCode].filter((code) => Boolean(code)).map(reasonLabel);
|
|
678
563
|
// The friction class and the code often say the same thing in two
|
|
679
564
|
// vocabularies ("auth_required" twice); once is enough.
|
|
680
565
|
for (const reason of new Set(reasons))
|
|
681
566
|
parts.push(reason);
|
|
682
567
|
return parts.join(" · ");
|
|
683
568
|
}
|
|
684
|
-
/**
|
|
685
|
-
* An artifact's last refresh, when it is worth a badge. Only a failure is: a
|
|
686
|
-
* success is what "Current data" already says, and a superseded run is
|
|
687
|
-
* nobody's concern.
|
|
688
|
-
*/
|
|
689
|
-
export function artifactRefreshBadge(last) {
|
|
690
|
-
return last?.status === "failed" ? { label: "Refresh failed", tone: "danger" } : null;
|
|
691
|
-
}
|
|
692
569
|
/**
|
|
693
570
|
* A stored credential that cannot be used, by cause. The payload's `error`
|
|
694
571
|
* beside it is not shown: it can carry a vault's own words.
|
|
@@ -704,12 +581,8 @@ export function credentialProblemCopy(problem) {
|
|
|
704
581
|
export function credentialStateLabel(credential) {
|
|
705
582
|
if (!credential.configured)
|
|
706
583
|
return "not configured";
|
|
707
|
-
const masked = credential.fields?.length
|
|
708
|
-
|
|
709
|
-
: `configured · ••••${credential.lastFour ?? ""}`;
|
|
710
|
-
return credential.updatedAt
|
|
711
|
-
? `${masked} · updated ${formatDate(credential.updatedAt)}`
|
|
712
|
-
: masked;
|
|
584
|
+
const masked = credential.fields?.length ? "configured" : `configured · ••••${credential.lastFour ?? ""}`;
|
|
585
|
+
return credential.updatedAt ? `${masked} · updated ${formatDate(credential.updatedAt)}` : masked;
|
|
713
586
|
}
|
|
714
587
|
/** Gate copy for each browser-auth shape, so the sign-in state is never a blank page. */
|
|
715
588
|
export function gateCopy(kind, signedIn) {
|
package/dist/page-styles.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Generated by scripts/build-operator-ui.mjs. Do not edit.
|
|
2
|
-
// Source: src/operator-ui/tokens.css and src/operator-ui/page.css
|
|
3
|
-
// operator UI's browser.css imports too, so every page reads one token layer.
|
|
2
|
+
// Source: src/operator-ui/tokens.css and src/operator-ui/page.css.
|
|
4
3
|
/** Custom properties and both palettes; styles no element. */
|
|
5
|
-
export const TOKENS_CSS = ":root{color-scheme:light;--accent: #
|
|
4
|
+
export const TOKENS_CSS = ":root{color-scheme:light;--accent: #5263e8;--radius: 6px;--sans: \"Inter Variable\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--mono: ui-monospace, \"SF Mono\", Menlo, Monaco, \"Cascadia Code\", Consolas, monospace;--bg: #ffffff;--surface: #ffffff;--surface-2: #f7f8fa;--border: #e8eaee;--border-strong: #cdd2da;--text: #20232b;--muted: #69707e;--ok: #12734a;--warn: #9a5b06;--danger: #bb3226;--on-accent: #ffffff;--link: var(--accent);--tint: color-mix(in srgb, var(--accent) 8%, var(--surface));--shell: 68rem;--pad: 1.25rem;--gap: 1rem;--space-1: .25rem;--space-2: .5rem;--space-3: .75rem;--space-4: 1rem;--space-6: 1.5rem;--space-8: 2rem;--text-xs: .75rem;--text-sm: .8125rem;--text-base: .875rem;--sidebar-width: 14rem}@media(prefers-color-scheme:dark){html:not([data-scheme=light]){color-scheme:dark}}html[data-scheme=dark]{color-scheme:dark}@media(prefers-color-scheme:dark){html:not([data-scheme=light]){--bg: #111216;--surface: #18191e;--surface-2: #202127;--border: #2a2b33;--border-strong: #3b3d48;--text: #e6eaf1;--muted: #97a1b2;--ok: #4cc48c;--warn: #e2a33f;--danger: #f4776c;--link: color-mix(in srgb, var(--accent) 55%, #ffffff);--tint: color-mix(in srgb, var(--accent) 16%, var(--surface))}}html[data-scheme=dark]{--bg: #111216;--surface: #18191e;--surface-2: #202127;--border: #2a2b33;--border-strong: #3b3d48;--text: #e6eaf1;--muted: #97a1b2;--ok: #4cc48c;--warn: #e2a33f;--danger: #f4776c;--link: color-mix(in srgb, var(--accent) 55%, #ffffff);--tint: color-mix(in srgb, var(--accent) 16%, var(--surface))}";
|
|
6
5
|
/** Base typography, the shell, masthead, buttons, badges, and messages. */
|
|
7
6
|
export const PAGE_CSS = "*{box-sizing:border-box}html{background:var(--bg);color:var(--text);font-family:var(--sans);font-size:16px;line-height:1.5;-webkit-font-smoothing:antialiased}body{margin:0;min-height:100vh}:is(h1,h2,h3,p,ul,ol){margin:0;padding:0}:is(h1,h2,h3){font-weight:600;line-height:1.3}h1{font-size:1.5rem;letter-spacing:-.01em}h2{font-size:1rem}:is(ul,ol){list-style:none}a{color:var(--link);text-decoration-thickness:1px;text-underline-offset:2px}button,input{font:inherit}:is(a,button,input,summary):focus-visible{border-radius:calc(var(--radius) / 2);outline:2px solid var(--link);outline-offset:2px}.skip-link{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);left:var(--pad);padding:.5rem .75rem;position:fixed;top:-4rem;z-index:10}.skip-link:focus{top:var(--pad)}.shell{margin:0 auto;max-width:var(--shell);padding-left:var(--pad);padding-right:var(--pad)}.cap,.meta{color:var(--muted);font-size:.875rem}.mono{font-family:var(--mono);font-size:.8125rem}.visually-hidden{clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}.masthead{align-items:center;background:var(--surface);border-bottom:1px solid var(--border);display:flex;gap:var(--gap);justify-content:space-between;max-width:none;padding-bottom:.75rem;padding-top:.75rem;position:sticky;top:0;z-index:5}.masthead-inner{align-items:center;display:flex;gap:var(--gap);justify-content:space-between;margin:0 auto;max-width:var(--shell);width:100%}.mast-nav,.mast-actions,.page-nav,.session-actions{align-items:center;display:flex;flex-wrap:wrap;gap:.25rem}.mast-nav,.mast-actions{gap:var(--gap)}.navlink{background:none;border:0;border-radius:calc(var(--radius) - 2px);color:var(--muted);cursor:pointer;font-size:.9375rem;padding:.35rem .6rem;text-decoration:none}.navlink:hover{background:var(--surface-2);color:var(--text)}.navlink[aria-current=page]{background:var(--tint);color:var(--link);font-weight:500}.brand,.product{padding-left:0;padding-right:0;text-decoration:none}.brand{color:var(--text);font-weight:600}.brand:hover,.product:hover{background:none}.product{color:var(--muted)}.page{padding-bottom:4rem;padding-top:2rem}.lead{margin-bottom:1.5rem}.lead-copy{color:var(--muted);display:grid;gap:.5rem;margin-top:.35rem}.lead-copy p{max-width:60ch}.btn{background:var(--surface);border:1px solid var(--border-strong);border-radius:calc(var(--radius) - 2px);color:var(--text);cursor:pointer;display:inline-flex;align-items:center;font-size:.875rem;gap:.35rem;padding:.35rem .7rem;text-decoration:none;white-space:nowrap}.btn:hover{background:var(--surface-2)}.btn:disabled{cursor:not-allowed;opacity:.55}.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}.btn.primary:hover{background:color-mix(in srgb,var(--accent) 88%,black)}.btn.danger{color:var(--danger)}.btn.danger:hover{background:color-mix(in srgb,var(--danger) 10%,var(--surface))}.btn.quiet{background:none;border-color:var(--border);color:var(--muted)}.btn.quiet:hover{background:var(--surface-2);color:var(--text)}.badge{align-items:center;background:var(--surface-2);border-radius:999px;color:var(--muted);display:inline-flex;font-size:.75rem;gap:.3rem;line-height:1.6;padding:.1rem .5rem;white-space:nowrap}.badge.ok{background:color-mix(in srgb,var(--ok) 12%,var(--surface));color:var(--ok)}.badge.warn{background:color-mix(in srgb,var(--warn) 14%,var(--surface));color:var(--warn)}.badge.danger{background:color-mix(in srgb,var(--danger) 12%,var(--surface));color:var(--danger)}.badge.accent{background:var(--tint);color:var(--link)}.msg{background:color-mix(in srgb,var(--danger) 8%,var(--surface));border:1px solid color-mix(in srgb,var(--danger) 28%,var(--surface));border-radius:calc(var(--radius) - 2px);color:var(--danger);font-size:.875rem;padding:.5rem .75rem}.status-page{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);display:grid;gap:.75rem;margin:1.5rem auto 0;max-width:34rem;padding:1.5rem}.status-head{align-items:center;display:flex;gap:.6rem}.status-mark{align-items:center;background:var(--surface-2);border-radius:50%;color:var(--muted);display:inline-flex;flex:none;height:2rem;justify-content:center;width:2rem}.status-mark svg{height:1.25rem;width:1.25rem}.status-mark.ok{background:color-mix(in srgb,var(--ok) 14%,var(--surface));color:var(--ok)}.status-mark.danger{background:color-mix(in srgb,var(--danger) 12%,var(--surface));color:var(--danger)}.status-label{color:var(--muted);font-size:.875rem;font-weight:600}.status-label.ok{color:var(--ok)}.status-label.danger{color:var(--danger)}.status-page h1{overflow-wrap:anywhere}.status-copy{color:var(--muted)}.status-actions{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.25rem}.status-details{border-top:1px solid var(--border);color:var(--muted);font-size:.875rem;margin-top:.25rem;padding-top:.75rem}.status-details>summary{cursor:pointer;width:fit-content}.status-details>p{margin-top:.5rem}.status-details pre{background:var(--surface-2);border:1px solid var(--border);border-radius:calc(var(--radius) / 2);color:var(--text);font-family:var(--mono);font-size:.75rem;line-height:1.55;margin:.5rem 0 0;max-height:18rem;overflow:auto;overflow-wrap:anywhere;padding:.5rem .6rem;white-space:pre-wrap}@media(max-width:40rem){:root{--pad: 1rem}.status-page{margin-top:0;padding:1.25rem var(--pad)}}";
|
package/dist/program-source.js
CHANGED
|
@@ -14,8 +14,20 @@ const WORD = /[\w$\\\u0080-\uffff]/;
|
|
|
14
14
|
* Any other word is an identifier or a literal, which a `/` divides.
|
|
15
15
|
*/
|
|
16
16
|
const REGEX_AFTER_WORD = new Set([
|
|
17
|
-
"await",
|
|
18
|
-
"
|
|
17
|
+
"await",
|
|
18
|
+
"case",
|
|
19
|
+
"delete",
|
|
20
|
+
"do",
|
|
21
|
+
"else",
|
|
22
|
+
"in",
|
|
23
|
+
"instanceof",
|
|
24
|
+
"new",
|
|
25
|
+
"of",
|
|
26
|
+
"return",
|
|
27
|
+
"throw",
|
|
28
|
+
"typeof",
|
|
29
|
+
"void",
|
|
30
|
+
"yield",
|
|
19
31
|
]);
|
|
20
32
|
/**
|
|
21
33
|
* Remove the statement terminators after a program's arrow expression:
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
// `destructive_tool_requires_approval` before it is validated or sent (E4),
|
|
5
|
-
// so the host's prompt on `call_destructive_tool` stays the only approval.
|
|
6
|
-
// An exempt write is still a write, though, and it gets what a write needs:
|
|
7
|
-
// its own budget, a dispatch the run waits for rather than aborts, and a
|
|
8
|
-
// result that never hides a write whose outcome is unknown.
|
|
9
|
-
//
|
|
10
|
-
// Web-API only, like everything reachable from the root entry.
|
|
1
|
+
// Budget and outcome accounting for writes dispatched by trusted programs.
|
|
2
|
+
// Await each dispatched write once; never conceal an unknown outcome.
|
|
3
|
+
import { CONFIG_DEFAULTS } from "./config-defaults.js";
|
|
11
4
|
import { Effect } from "effect";
|
|
12
5
|
import { jsonResult } from "./meta-tools.js";
|
|
13
6
|
/** Writes one program may send (`execute.maxWrites`), unless configured. */
|
|
14
|
-
export const DEFAULT_MAX_WRITES =
|
|
7
|
+
export const DEFAULT_MAX_WRITES = CONFIG_DEFAULTS.execute.maxWrites;
|
|
15
8
|
/**
|
|
16
9
|
* Whether a write landed, as far as anyone can know.
|
|
17
10
|
*
|
|
@@ -20,9 +13,9 @@ export const DEFAULT_MAX_WRITES = 10;
|
|
|
20
13
|
* not sending it could leave it undone. That is a dispatched call that timed
|
|
21
14
|
* out, was cancelled, found the service unavailable, or failed any way that
|
|
22
15
|
* is not a verdict. A refusal code (`REFUSALS`) or a downstream tool's own
|
|
23
|
-
* `isError` is an answer, so it is `failed
|
|
24
|
-
*
|
|
25
|
-
*
|
|
16
|
+
* `isError` is an answer, so it is `failed`. Response text does not decide
|
|
17
|
+
* whether an outcome is unknown. A call that was never dispatched is
|
|
18
|
+
* `failed`: nothing was sent.
|
|
26
19
|
* `result_processing_failed` means the downstream call completed.
|
|
27
20
|
*/
|
|
28
21
|
export function classifyWriteOutcome(outcome) {
|
|
@@ -51,6 +44,8 @@ export function classifyWriteOutcome(outcome) {
|
|
|
51
44
|
*/
|
|
52
45
|
const REFUSALS = new Set([
|
|
53
46
|
"auth_required",
|
|
47
|
+
"downstream_oauth_required",
|
|
48
|
+
"provider_permission_denied",
|
|
54
49
|
"invalid_args",
|
|
55
50
|
"not_found",
|
|
56
51
|
"rate_limited",
|
|
@@ -59,7 +54,7 @@ const REFUSALS = new Set([
|
|
|
59
54
|
]);
|
|
60
55
|
/** What a dispatched write's outcome says about whether it landed. */
|
|
61
56
|
export function writeStateOf(outcome) {
|
|
62
|
-
|
|
57
|
+
const state = classifyWriteOutcome(outcome.ok
|
|
63
58
|
? { ok: true, dispatched: outcome.dispatched }
|
|
64
59
|
: {
|
|
65
60
|
ok: false,
|
|
@@ -67,19 +62,44 @@ export function writeStateOf(outcome) {
|
|
|
67
62
|
error: outcome.error,
|
|
68
63
|
...(outcome.answered !== undefined ? { answered: outcome.answered } : {}),
|
|
69
64
|
});
|
|
65
|
+
return state === "unknown" && !outcome.ok && outcome.error.uncertainCall
|
|
66
|
+
? {
|
|
67
|
+
state,
|
|
68
|
+
uncertainCall: outcome.error.uncertainCall,
|
|
69
|
+
...(outcome.error.details?.stage !== undefined
|
|
70
|
+
? {
|
|
71
|
+
deadline: {
|
|
72
|
+
...(outcome.error.details.operation !== undefined
|
|
73
|
+
? { operation: outcome.error.details.operation }
|
|
74
|
+
: {}),
|
|
75
|
+
stage: outcome.error.details.stage,
|
|
76
|
+
...(outcome.error.details.elapsedMs !== undefined
|
|
77
|
+
? { elapsedMs: outcome.error.details.elapsedMs }
|
|
78
|
+
: {}),
|
|
79
|
+
...(outcome.error.details.deadlineMs !== undefined
|
|
80
|
+
? { deadlineMs: outcome.error.details.deadlineMs }
|
|
81
|
+
: {}),
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
: {}),
|
|
85
|
+
}
|
|
86
|
+
: state;
|
|
70
87
|
}
|
|
71
88
|
/**
|
|
72
|
-
* The
|
|
89
|
+
* The trusted-pool writes one program sends. The play closes this when the
|
|
73
90
|
* program settles — a write that reaches the gate after is not sent — and
|
|
74
91
|
* drains what is on the wire before the run's scope aborts it, so the write
|
|
75
92
|
* finishes and its activity says how it did. The result never hides a write
|
|
76
93
|
* whose outcome is unknown, and a program that fails after writing reports
|
|
77
94
|
* the counts.
|
|
78
95
|
*/
|
|
79
|
-
export class
|
|
96
|
+
export class ProgramWrites {
|
|
80
97
|
closed = false;
|
|
81
98
|
inFlight = new Set();
|
|
82
99
|
states = [];
|
|
100
|
+
uncertainCalls = [];
|
|
101
|
+
uncertainCallsTruncated = false;
|
|
102
|
+
deadlines = [];
|
|
83
103
|
/** Whether the program has settled: a write gated now is not sent. */
|
|
84
104
|
get isClosed() {
|
|
85
105
|
return this.closed;
|
|
@@ -95,10 +115,19 @@ export class ExemptWrites {
|
|
|
95
115
|
});
|
|
96
116
|
this.inFlight.add(settled);
|
|
97
117
|
let recorded = false;
|
|
98
|
-
return (
|
|
118
|
+
return (completion) => {
|
|
99
119
|
if (recorded)
|
|
100
120
|
return;
|
|
101
121
|
recorded = true;
|
|
122
|
+
const state = typeof completion === "string" ? completion : completion.state;
|
|
123
|
+
if (typeof completion !== "string") {
|
|
124
|
+
if (this.uncertainCalls.length < 10)
|
|
125
|
+
this.uncertainCalls.push(completion.uncertainCall);
|
|
126
|
+
else
|
|
127
|
+
this.uncertainCallsTruncated = true;
|
|
128
|
+
if (completion.deadline && this.deadlines.length < 10)
|
|
129
|
+
this.deadlines.push(completion.deadline);
|
|
130
|
+
}
|
|
102
131
|
this.states.push(state);
|
|
103
132
|
this.inFlight.delete(settled);
|
|
104
133
|
done();
|
|
@@ -124,6 +153,14 @@ export class ExemptWrites {
|
|
|
124
153
|
code: "write_outcome_unknown",
|
|
125
154
|
message: "A write this program sent has no known outcome, so that is the result rather than what the program returned. It will not be sent again. Check its target before doing anything that depends on it.",
|
|
126
155
|
retryable: false,
|
|
156
|
+
...(this.deadlines.length > 0 ? { details: this.deadlines[0] } : {}),
|
|
157
|
+
...(this.deadlines.length > 1 ? { timeouts: this.deadlines } : {}),
|
|
158
|
+
...(this.uncertainCalls.length === 1
|
|
159
|
+
? { uncertainCall: this.uncertainCalls[0] }
|
|
160
|
+
: this.uncertainCalls.length > 1
|
|
161
|
+
? { uncertainCalls: this.uncertainCalls }
|
|
162
|
+
: {}),
|
|
163
|
+
...(this.uncertainCallsTruncated ? { uncertainCallsTruncated: true } : {}),
|
|
127
164
|
writes,
|
|
128
165
|
});
|
|
129
166
|
}
|