@zackbart/connecta 0.15.1 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +40 -20
- package/CHANGELOG.md +653 -0
- package/README.md +25 -5
- package/bin/connecta.mjs +44 -3
- package/dist/access-tokens.d.ts +0 -1
- package/dist/access-tokens.js +0 -1
- package/dist/activity.d.ts +50 -2
- package/dist/activity.js +34 -1
- package/dist/apps-shell.d.ts +0 -1
- package/dist/apps-shell.js +0 -1
- package/dist/auth/bearer.d.ts +0 -1
- package/dist/auth/bearer.js +0 -1
- package/dist/auth/clerk.d.ts +0 -1
- package/dist/auth/clerk.js +45 -9
- package/dist/auth/downstream-oauth.d.ts +0 -1
- package/dist/auth/downstream-oauth.js +0 -1
- package/dist/call-admission.d.ts +0 -1
- package/dist/call-admission.js +0 -1
- package/dist/catalog-drift.d.ts +99 -0
- package/dist/catalog-drift.js +251 -0
- package/dist/catalog-fingerprint.d.ts +0 -1
- package/dist/catalog-fingerprint.js +0 -1
- package/dist/catalog-limits.d.ts +0 -1
- package/dist/catalog-limits.js +0 -1
- package/dist/catalog-service.d.ts +0 -1
- package/dist/catalog-service.js +41 -5
- package/dist/catalog.d.ts +11 -1
- package/dist/catalog.js +20 -1
- package/dist/concurrency.d.ts +0 -1
- package/dist/concurrency.js +0 -1
- package/dist/connector-scope.d.ts +0 -1
- package/dist/connector-scope.js +0 -1
- package/dist/connectors/api.d.ts +29 -21
- package/dist/connectors/api.js +45 -19
- package/dist/connectors/guarded-fetch.d.ts +106 -0
- package/dist/connectors/guarded-fetch.js +289 -0
- package/dist/connectors/remote-mcp.d.ts +0 -1
- package/dist/connectors/remote-mcp.js +0 -1
- package/dist/credentials.d.ts +0 -1
- package/dist/credentials.js +0 -1
- package/dist/errors.d.ts +30 -3
- package/dist/errors.js +1 -1
- package/dist/execute.d.ts +0 -1
- package/dist/execute.js +0 -1
- package/dist/executor-admission.d.ts +11 -1
- package/dist/executor-admission.js +28 -1
- package/dist/executor-result.d.ts +0 -1
- package/dist/executor-result.js +0 -1
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-child.js +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -1
- package/dist/executors/quickjs-protocol.js +0 -1
- package/dist/executors/quickjs-runtime.d.ts +0 -1
- package/dist/executors/quickjs-runtime.js +0 -1
- package/dist/executors/quickjs.d.ts +0 -1
- package/dist/executors/quickjs.js +2 -1
- package/dist/favicon.d.ts +0 -1
- package/dist/favicon.js +0 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +24 -7
- package/dist/invocation.d.ts +0 -1
- package/dist/invocation.js +0 -1
- package/dist/json-schema.d.ts +0 -1
- package/dist/json-schema.js +0 -1
- package/dist/mcp-result.d.ts +0 -1
- package/dist/mcp-result.js +0 -1
- package/dist/meta-tools.d.ts +0 -1
- package/dist/meta-tools.js +0 -1
- package/dist/node.d.ts +0 -1
- package/dist/node.js +0 -1
- package/dist/operator-ui/generated.d.ts +2 -3
- package/dist/operator-ui/generated.js +3 -4
- package/dist/operator-ui/model.d.ts +10 -1
- package/dist/operator-ui/model.js +0 -1
- package/dist/operator-ui/view.d.ts +167 -0
- package/dist/operator-ui/view.js +270 -0
- package/dist/providers/cloudflare.d.ts +0 -1
- package/dist/providers/cloudflare.js +261 -299
- package/dist/providers/linear.d.ts +26 -5
- package/dist/providers/linear.js +32 -61
- package/dist/providers/mixpanel.d.ts +35 -4
- package/dist/providers/mixpanel.js +53 -79
- package/dist/providers/notion.d.ts +0 -1
- package/dist/providers/notion.js +87 -42
- package/dist/providers/stripe.d.ts +13 -1
- package/dist/providers/stripe.js +31 -60
- package/dist/registry.d.ts +34 -2
- package/dist/registry.js +85 -5
- package/dist/retired-toolkits.d.ts +0 -1
- package/dist/retired-toolkits.js +0 -1
- package/dist/routes/access-tokens.d.ts +0 -1
- package/dist/routes/access-tokens.js +0 -1
- package/dist/routes/activity.d.ts +0 -1
- package/dist/routes/activity.js +0 -1
- package/dist/routes/credentials.d.ts +0 -1
- package/dist/routes/credentials.js +0 -1
- package/dist/routes/mcp.d.ts +0 -1
- package/dist/routes/mcp.js +0 -1
- package/dist/routes/oauth.d.ts +0 -1
- package/dist/routes/oauth.js +0 -1
- package/dist/routes/shared.d.ts +2 -1
- package/dist/routes/shared.js +0 -1
- package/dist/routes/ui.d.ts +0 -1
- package/dist/routes/ui.js +0 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.js +11 -1
- package/dist/skills.d.ts +0 -1
- package/dist/skills.js +0 -1
- package/dist/storage/file.d.ts +0 -1
- package/dist/storage/file.js +0 -1
- package/dist/storage/memory.d.ts +0 -1
- package/dist/storage/memory.js +0 -1
- package/dist/timeout.d.ts +0 -1
- package/dist/timeout.js +0 -1
- package/dist/tool-safety.d.ts +0 -1
- package/dist/tool-safety.js +0 -1
- package/dist/types.d.ts +45 -1
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +0 -1
- package/dist/ui.js +13 -137
- package/dist/validate.d.ts +13 -15
- package/dist/validate.js +24 -11
- package/dist/version.d.ts +1 -2
- package/dist/version.js +1 -2
- package/documentation/architecture.md +186 -5
- package/documentation/auth.md +11 -0
- package/documentation/call-admission.md +142 -6
- package/documentation/cloudflare.md +92 -10
- package/documentation/code-first-exploration.md +2 -2
- package/documentation/code-mode.md +14 -14
- package/documentation/connector-guides.md +172 -5
- package/documentation/connectors.md +89 -0
- package/documentation/linear.md +20 -2
- package/documentation/mcp-ui-design.md +2 -2
- package/documentation/meta-tools.md +7 -1
- package/documentation/mixpanel.md +42 -9
- package/documentation/notion.md +23 -2
- package/documentation/operations.md +325 -5
- package/documentation/operator-ui.md +120 -5
- package/documentation/provider-audit.md +160 -0
- package/documentation/provider-conventions.md +667 -0
- package/documentation/request-admission.md +167 -5
- package/documentation/rich-output-design.md +1 -2
- package/documentation/stripe.md +15 -0
- package/documentation/upgrading.md +454 -0
- package/ethos.md +14 -9
- package/examples/worker/README.md +102 -12
- package/examples/worker/src/index.ts +46 -1
- package/examples/worker/wrangler.jsonc +14 -1
- package/package.json +10 -6
- package/templates/node/.dockerignore +10 -0
- package/templates/node/.env.example +40 -3
- package/templates/node/AGENTS.md +16 -1
- package/templates/node/Dockerfile +49 -0
- package/templates/node/README.md +117 -2
- package/templates/node/docker-compose.yml +39 -0
- package/templates/node/package.json +1 -1
- package/templates/node/src/file-activity.ts +182 -0
- package/templates/node/src/index.ts +80 -4
- package/assets/connecta-clay-hero.png +0 -0
- package/dist/access-tokens.d.ts.map +0 -1
- package/dist/access-tokens.js.map +0 -1
- package/dist/activity.d.ts.map +0 -1
- package/dist/activity.js.map +0 -1
- package/dist/apps-shell.d.ts.map +0 -1
- package/dist/apps-shell.js.map +0 -1
- package/dist/auth/bearer.d.ts.map +0 -1
- package/dist/auth/bearer.js.map +0 -1
- package/dist/auth/clerk.d.ts.map +0 -1
- package/dist/auth/clerk.js.map +0 -1
- package/dist/auth/downstream-oauth.d.ts.map +0 -1
- package/dist/auth/downstream-oauth.js.map +0 -1
- package/dist/call-admission.d.ts.map +0 -1
- package/dist/call-admission.js.map +0 -1
- package/dist/catalog-fingerprint.d.ts.map +0 -1
- package/dist/catalog-fingerprint.js.map +0 -1
- package/dist/catalog-limits.d.ts.map +0 -1
- package/dist/catalog-limits.js.map +0 -1
- package/dist/catalog-service.d.ts.map +0 -1
- package/dist/catalog-service.js.map +0 -1
- package/dist/catalog.d.ts.map +0 -1
- package/dist/catalog.js.map +0 -1
- package/dist/concurrency.d.ts.map +0 -1
- package/dist/concurrency.js.map +0 -1
- package/dist/connector-scope.d.ts.map +0 -1
- package/dist/connector-scope.js.map +0 -1
- package/dist/connectors/api.d.ts.map +0 -1
- package/dist/connectors/api.js.map +0 -1
- package/dist/connectors/remote-mcp.d.ts.map +0 -1
- package/dist/connectors/remote-mcp.js.map +0 -1
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/execute.d.ts.map +0 -1
- package/dist/execute.js.map +0 -1
- package/dist/executor-admission.d.ts.map +0 -1
- package/dist/executor-admission.js.map +0 -1
- package/dist/executor-result.d.ts.map +0 -1
- package/dist/executor-result.js.map +0 -1
- package/dist/executors/quickjs-child.d.ts.map +0 -1
- package/dist/executors/quickjs-child.js.map +0 -1
- package/dist/executors/quickjs-protocol.d.ts.map +0 -1
- package/dist/executors/quickjs-protocol.js.map +0 -1
- package/dist/executors/quickjs-runtime.d.ts.map +0 -1
- package/dist/executors/quickjs-runtime.js.map +0 -1
- package/dist/executors/quickjs.d.ts.map +0 -1
- package/dist/executors/quickjs.js.map +0 -1
- package/dist/favicon.d.ts.map +0 -1
- package/dist/favicon.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/invocation.d.ts.map +0 -1
- package/dist/invocation.js.map +0 -1
- package/dist/json-schema.d.ts.map +0 -1
- package/dist/json-schema.js.map +0 -1
- package/dist/mcp-result.d.ts.map +0 -1
- package/dist/mcp-result.js.map +0 -1
- package/dist/meta-tools.d.ts.map +0 -1
- package/dist/meta-tools.js.map +0 -1
- package/dist/node.d.ts.map +0 -1
- package/dist/node.js.map +0 -1
- package/dist/operator-ui/generated.d.ts.map +0 -1
- package/dist/operator-ui/generated.js.map +0 -1
- package/dist/operator-ui/model.d.ts.map +0 -1
- package/dist/operator-ui/model.js.map +0 -1
- package/dist/providers/cloudflare.d.ts.map +0 -1
- package/dist/providers/cloudflare.js.map +0 -1
- package/dist/providers/linear.d.ts.map +0 -1
- package/dist/providers/linear.js.map +0 -1
- package/dist/providers/mixpanel.d.ts.map +0 -1
- package/dist/providers/mixpanel.js.map +0 -1
- package/dist/providers/notion.d.ts.map +0 -1
- package/dist/providers/notion.js.map +0 -1
- package/dist/providers/stripe.d.ts.map +0 -1
- package/dist/providers/stripe.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/retired-toolkits.d.ts.map +0 -1
- package/dist/retired-toolkits.js.map +0 -1
- package/dist/routes/access-tokens.d.ts.map +0 -1
- package/dist/routes/access-tokens.js.map +0 -1
- package/dist/routes/activity.d.ts.map +0 -1
- package/dist/routes/activity.js.map +0 -1
- package/dist/routes/credentials.d.ts.map +0 -1
- package/dist/routes/credentials.js.map +0 -1
- package/dist/routes/mcp.d.ts.map +0 -1
- package/dist/routes/mcp.js.map +0 -1
- package/dist/routes/oauth.d.ts.map +0 -1
- package/dist/routes/oauth.js.map +0 -1
- package/dist/routes/shared.d.ts.map +0 -1
- package/dist/routes/shared.js.map +0 -1
- package/dist/routes/ui.d.ts.map +0 -1
- package/dist/routes/ui.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js.map +0 -1
- package/dist/storage/file.d.ts.map +0 -1
- package/dist/storage/file.js.map +0 -1
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js.map +0 -1
- package/dist/timeout.d.ts.map +0 -1
- package/dist/timeout.js.map +0 -1
- package/dist/tool-safety.d.ts.map +0 -1
- package/dist/tool-safety.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/ui.d.ts.map +0 -1
- package/dist/ui.js.map +0 -1
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/examples/node/README.md +0 -52
- package/examples/node/src/index.ts +0 -71
- package/src/access-tokens.ts +0 -289
- package/src/activity.ts +0 -234
- package/src/apps-shell.ts +0 -370
- package/src/auth/bearer.ts +0 -61
- package/src/auth/clerk.ts +0 -553
- package/src/auth/downstream-oauth.ts +0 -575
- package/src/call-admission.ts +0 -521
- package/src/catalog-fingerprint.ts +0 -52
- package/src/catalog-limits.ts +0 -11
- package/src/catalog-service.ts +0 -1153
- package/src/catalog.ts +0 -790
- package/src/concurrency.ts +0 -39
- package/src/connector-scope.ts +0 -66
- package/src/connectors/api.ts +0 -159
- package/src/connectors/remote-mcp.ts +0 -1090
- package/src/credentials.ts +0 -428
- package/src/errors.ts +0 -364
- package/src/execute.ts +0 -1406
- package/src/executor-admission.ts +0 -328
- package/src/executor-result.ts +0 -107
- package/src/executors/quickjs-child.ts +0 -172
- package/src/executors/quickjs-protocol.ts +0 -99
- package/src/executors/quickjs-runtime.ts +0 -470
- package/src/executors/quickjs.ts +0 -776
- package/src/favicon.ts +0 -53
- package/src/index.ts +0 -666
- package/src/invocation.ts +0 -655
- package/src/json-schema.ts +0 -11
- package/src/mcp-result.ts +0 -43
- package/src/meta-tools.ts +0 -1607
- package/src/node.ts +0 -206
- package/src/operator-ui/browser.css +0 -423
- package/src/operator-ui/browser.ts +0 -1281
- package/src/operator-ui/generated.ts +0 -4
- package/src/operator-ui/model.ts +0 -111
- package/src/providers/cloudflare.ts +0 -3999
- package/src/providers/linear.ts +0 -302
- package/src/providers/mixpanel.ts +0 -229
- package/src/providers/notion.ts +0 -1879
- package/src/providers/stripe.ts +0 -307
- package/src/registry.ts +0 -1085
- package/src/retired-toolkits.ts +0 -14
- package/src/routes/access-tokens.ts +0 -115
- package/src/routes/activity.ts +0 -215
- package/src/routes/credentials.ts +0 -254
- package/src/routes/mcp.ts +0 -429
- package/src/routes/oauth.ts +0 -322
- package/src/routes/shared.ts +0 -247
- package/src/routes/ui.ts +0 -152
- package/src/server.ts +0 -195
- package/src/skills.ts +0 -271
- package/src/storage/file.ts +0 -123
- package/src/storage/memory.ts +0 -44
- package/src/timeout.ts +0 -84
- package/src/tool-safety.ts +0 -15
- package/src/types.ts +0 -523
- package/src/ui.ts +0 -744
- package/src/validate.ts +0 -387
- package/src/version.ts +0 -7
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
export const OPERATOR_PAGES = [
|
|
2
|
+
"connections",
|
|
3
|
+
"credentials",
|
|
4
|
+
"tokens",
|
|
5
|
+
"activity",
|
|
6
|
+
];
|
|
7
|
+
export const PAGE_META = {
|
|
8
|
+
connections: { path: "/", label: "Connections" },
|
|
9
|
+
credentials: { path: "/credentials", label: "Credentials" },
|
|
10
|
+
tokens: { path: "/tokens", label: "Access tokens" },
|
|
11
|
+
activity: { path: "/activity", label: "Activity" },
|
|
12
|
+
};
|
|
13
|
+
export function pageForPath(path) {
|
|
14
|
+
const match = OPERATOR_PAGES.find((page) => PAGE_META[page].path === path);
|
|
15
|
+
return match ?? "connections";
|
|
16
|
+
}
|
|
17
|
+
export function info(message) {
|
|
18
|
+
return { message, tone: "info" };
|
|
19
|
+
}
|
|
20
|
+
export function failure(message) {
|
|
21
|
+
return { message, tone: "error" };
|
|
22
|
+
}
|
|
23
|
+
export function initialState(page) {
|
|
24
|
+
return {
|
|
25
|
+
page,
|
|
26
|
+
generation: 0,
|
|
27
|
+
session: "loading",
|
|
28
|
+
gate: null,
|
|
29
|
+
refreshing: false,
|
|
30
|
+
pendingFocus: null,
|
|
31
|
+
...identityScopedState(),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Every field that belongs to one operator identity. Split out because the only
|
|
36
|
+
* safe way to change identity is to replace all of them at once: a field left
|
|
37
|
+
* behind here is one identity's data on another identity's screen.
|
|
38
|
+
*/
|
|
39
|
+
function identityScopedState() {
|
|
40
|
+
return {
|
|
41
|
+
data: null,
|
|
42
|
+
connectorFilter: "",
|
|
43
|
+
oauthNotice: null,
|
|
44
|
+
oauthBusy: null,
|
|
45
|
+
credentialNotice: null,
|
|
46
|
+
credentialEditing: null,
|
|
47
|
+
credentialBusy: null,
|
|
48
|
+
tokenPhase: "idle",
|
|
49
|
+
tokenNotice: null,
|
|
50
|
+
tokens: [],
|
|
51
|
+
createdToken: null,
|
|
52
|
+
tokenRenaming: null,
|
|
53
|
+
tokenBusy: false,
|
|
54
|
+
activityPhase: "idle",
|
|
55
|
+
activityNotice: null,
|
|
56
|
+
activityEvents: [],
|
|
57
|
+
activityCursor: null,
|
|
58
|
+
activitySearch: "",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Drop to the gate and forget the previous operator. Bumping the generation is
|
|
63
|
+
* what makes the drop stick: work already in flight for the old identity
|
|
64
|
+
* resolves into a state that no longer accepts it.
|
|
65
|
+
*/
|
|
66
|
+
export function resetIdentity(state, gate = null) {
|
|
67
|
+
return {
|
|
68
|
+
...state,
|
|
69
|
+
generation: state.generation + 1,
|
|
70
|
+
session: "gated",
|
|
71
|
+
gate,
|
|
72
|
+
refreshing: false,
|
|
73
|
+
pendingFocus: null,
|
|
74
|
+
...identityScopedState(),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Leaving a page closes what should not survive it: a one-time secret, a half
|
|
79
|
+
* typed credential, and the notices that answered the page just left.
|
|
80
|
+
*/
|
|
81
|
+
export function withPage(state, page) {
|
|
82
|
+
return {
|
|
83
|
+
...state,
|
|
84
|
+
page,
|
|
85
|
+
createdToken: null,
|
|
86
|
+
tokenRenaming: null,
|
|
87
|
+
tokenNotice: null,
|
|
88
|
+
credentialEditing: null,
|
|
89
|
+
credentialNotice: null,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export function credentialUnavailableCopy(capability) {
|
|
93
|
+
if (capability === "no_slots") {
|
|
94
|
+
return "No connectors declare operator-managed credential slots. Connector credentials remain configuration-as-code until a slot is declared.";
|
|
95
|
+
}
|
|
96
|
+
if (capability === "vault_not_configured") {
|
|
97
|
+
return "Credential storage is not configured. Set credentials.encryptionKey before managing connector credentials here.";
|
|
98
|
+
}
|
|
99
|
+
return "Credential management requires an eligible Clerk operator. Bearer-authenticated sessions can inspect connections but cannot manage stored credentials.";
|
|
100
|
+
}
|
|
101
|
+
export function accessTokenUnavailableCopy(capability) {
|
|
102
|
+
if (capability === "not_configured") {
|
|
103
|
+
return "Access tokens are not configured for this deployment. Add accessTokens to the deployment configuration to enable them.";
|
|
104
|
+
}
|
|
105
|
+
return "Access token management requires an eligible Clerk operator. A Bearer token can connect to MCP, but it cannot create or revoke other tokens.";
|
|
106
|
+
}
|
|
107
|
+
export function connectorStatusLabel(status) {
|
|
108
|
+
if (status === "ok")
|
|
109
|
+
return "Connected";
|
|
110
|
+
if (status === "auth_required")
|
|
111
|
+
return "Authorization needed";
|
|
112
|
+
return "Unavailable";
|
|
113
|
+
}
|
|
114
|
+
export function toolCountLabel(count) {
|
|
115
|
+
return `${count} ${count === 1 ? "tool" : "tools"}`;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The four categories, in the order an operator should read them: what the
|
|
119
|
+
* deployment refuses to call, what it can no longer call, what contradicts a
|
|
120
|
+
* vetted verdict, and what changed shape underneath a reviewed schema. Counts
|
|
121
|
+
* only — a name or a schema on this path would be the payload leak the whole
|
|
122
|
+
* drift model exists to avoid.
|
|
123
|
+
*/
|
|
124
|
+
const DRIFT_CATEGORIES = [
|
|
125
|
+
{ key: "unclassifiedTools", label: "Unclassified" },
|
|
126
|
+
{ key: "unservedTools", label: "Unserved" },
|
|
127
|
+
{ key: "annotationConflicts", label: "Annotation conflicts" },
|
|
128
|
+
{ key: "schemaChanges", label: "Schema changes" },
|
|
129
|
+
];
|
|
130
|
+
export function driftTotal(drift) {
|
|
131
|
+
if (!drift)
|
|
132
|
+
return 0;
|
|
133
|
+
return DRIFT_CATEGORIES.reduce((sum, { key }) => sum + (drift[key] || 0), 0);
|
|
134
|
+
}
|
|
135
|
+
export function driftState(drift) {
|
|
136
|
+
if (!drift)
|
|
137
|
+
return "unavailable";
|
|
138
|
+
return driftTotal(drift) > 0 ? "warning" : "clean";
|
|
139
|
+
}
|
|
140
|
+
/** Every category with its count, so a clean report still shows its zeros. */
|
|
141
|
+
export function driftCounts(drift) {
|
|
142
|
+
if (!drift)
|
|
143
|
+
return [];
|
|
144
|
+
return DRIFT_CATEGORIES.map(({ key, label }) => ({
|
|
145
|
+
key,
|
|
146
|
+
label,
|
|
147
|
+
count: drift[key] || 0,
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
/** One line naming the state and when it was observed. Never what drifted. */
|
|
151
|
+
export function driftSummary(drift) {
|
|
152
|
+
const state = driftState(drift);
|
|
153
|
+
if (state === "unavailable") {
|
|
154
|
+
return "No catalog refresh observed yet in this runtime.";
|
|
155
|
+
}
|
|
156
|
+
const observed = formatDate(drift?.observedAt);
|
|
157
|
+
const when = observed ? ` · observed ${observed}` : "";
|
|
158
|
+
if (state === "clean")
|
|
159
|
+
return `Matches the reviewed manifest${when}`;
|
|
160
|
+
const total = driftTotal(drift);
|
|
161
|
+
return `${total} difference${total === 1 ? "" : "s"} from the reviewed manifest${when}`;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Only http/https may become a clickable href — the browser half of the gate
|
|
165
|
+
* `src/ui.ts` applies before an authorizationUrl is ever serialized. A hostile
|
|
166
|
+
* downstream that gets a `javascript:` URL past one of them still meets the
|
|
167
|
+
* other, and the caller renders inert text instead of a link.
|
|
168
|
+
*/
|
|
169
|
+
export function safeHttpHref(url) {
|
|
170
|
+
if (!url)
|
|
171
|
+
return null;
|
|
172
|
+
try {
|
|
173
|
+
const protocol = new URL(url).protocol;
|
|
174
|
+
return protocol === "http:" || protocol === "https:" ? url : null;
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/** Locale timestamp, or "" for anything that is not a readable date. */
|
|
181
|
+
export function formatDate(value) {
|
|
182
|
+
if (!value)
|
|
183
|
+
return "";
|
|
184
|
+
const date = new Date(value);
|
|
185
|
+
return Number.isNaN(date.valueOf()) ? "" : date.toLocaleString();
|
|
186
|
+
}
|
|
187
|
+
export function actorLabel(actor) {
|
|
188
|
+
if (!actor?.kind)
|
|
189
|
+
return "unknown";
|
|
190
|
+
if (actor.label)
|
|
191
|
+
return `${actor.kind} · ${actor.label}`;
|
|
192
|
+
return actor.id ? `${actor.kind} · ${actor.id}` : actor.kind;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The stable id behind a friendly label, shown only when a label or namespace
|
|
196
|
+
* could otherwise make two different people look like one.
|
|
197
|
+
*/
|
|
198
|
+
export function actorStableId(actor) {
|
|
199
|
+
if (!actor?.id)
|
|
200
|
+
return null;
|
|
201
|
+
if (!actor.label && !actor.namespace)
|
|
202
|
+
return null;
|
|
203
|
+
return actor.namespace ? `${actor.namespace} · ${actor.id}` : actor.id;
|
|
204
|
+
}
|
|
205
|
+
function activityMatches(event, query) {
|
|
206
|
+
const q = query.trim().toLowerCase();
|
|
207
|
+
if (!q)
|
|
208
|
+
return true;
|
|
209
|
+
return [
|
|
210
|
+
event.address,
|
|
211
|
+
event.connectorId,
|
|
212
|
+
event.toolName,
|
|
213
|
+
event.source,
|
|
214
|
+
event.outcome,
|
|
215
|
+
event.errorCode,
|
|
216
|
+
event.friction,
|
|
217
|
+
event.actor?.kind,
|
|
218
|
+
event.actor?.id,
|
|
219
|
+
event.actor?.namespace,
|
|
220
|
+
event.actor?.label,
|
|
221
|
+
].some((value) => String(value ?? "").toLowerCase().includes(q));
|
|
222
|
+
}
|
|
223
|
+
export function filterActivity(events, query) {
|
|
224
|
+
return events.filter((event) => activityMatches(event, query));
|
|
225
|
+
}
|
|
226
|
+
/** Counts only. What the deployment ran, never what it sent or received. */
|
|
227
|
+
export function activitySummary(events) {
|
|
228
|
+
if (events.length === 0)
|
|
229
|
+
return "Arguments and results are never stored.";
|
|
230
|
+
const tools = new Set(events.map((event) => event.address)).size;
|
|
231
|
+
return `${events.length} loaded call${events.length === 1 ? "" : "s"} · ${tools} tool${tools === 1 ? "" : "s"} · no arguments or results stored`;
|
|
232
|
+
}
|
|
233
|
+
const ACTIVITY_OUTCOMES = ["success", "error", "timeout", "cancelled"];
|
|
234
|
+
export function activityOutcomeClass(outcome) {
|
|
235
|
+
return ACTIVITY_OUTCOMES.includes(outcome) ? outcome : "error";
|
|
236
|
+
}
|
|
237
|
+
/** The one-line detail under an address: source, retries, and friction. */
|
|
238
|
+
export function activityDetail(event) {
|
|
239
|
+
const parts = [event.source];
|
|
240
|
+
if (event.attempts > 1)
|
|
241
|
+
parts.push(`${event.attempts} attempts`);
|
|
242
|
+
if (event.friction)
|
|
243
|
+
parts.push(event.friction);
|
|
244
|
+
// The friction class and the code coincide for auth_required and
|
|
245
|
+
// result_too_large. Printing "· auth_required · auth_required" says nothing
|
|
246
|
+
// twice, so the coarse class stands in for both when they agree.
|
|
247
|
+
if (event.errorCode && event.errorCode !== event.friction) {
|
|
248
|
+
parts.push(event.errorCode);
|
|
249
|
+
}
|
|
250
|
+
return parts.join(" · ");
|
|
251
|
+
}
|
|
252
|
+
export function credentialStateLabel(credential) {
|
|
253
|
+
if (!credential.configured)
|
|
254
|
+
return "not configured";
|
|
255
|
+
const masked = credential.fields?.length
|
|
256
|
+
? "configured"
|
|
257
|
+
: `configured · ••••${credential.lastFour ?? ""}`;
|
|
258
|
+
return credential.updatedAt
|
|
259
|
+
? `${masked} · updated ${formatDate(credential.updatedAt)}`
|
|
260
|
+
: masked;
|
|
261
|
+
}
|
|
262
|
+
/** Gate copy for the two inbound-auth shapes, so the sign-in state is never a blank page. */
|
|
263
|
+
export function gateCopy(kind, signedIn) {
|
|
264
|
+
if (kind !== "clerk") {
|
|
265
|
+
return "Paste an operator bearer token to open this page. Nothing is requested until you do.";
|
|
266
|
+
}
|
|
267
|
+
return signedIn
|
|
268
|
+
? "Signed in with Clerk, but this account cannot open deployment-wide operator pages."
|
|
269
|
+
: "Sign in with Clerk to open this operator page.";
|
|
270
|
+
}
|