@zackbart/connecta 0.25.0 → 0.26.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 +6 -5
- package/CHANGELOG.md +387 -0
- package/README.md +32 -15
- package/SECURITY.md +6 -5
- package/bin/connecta.mjs +5 -1
- package/dist/activity.d.ts +10 -3
- package/dist/activity.js +10 -1
- package/dist/artifacts/connector.d.ts +55 -0
- package/dist/artifacts/connector.js +724 -0
- package/dist/artifacts/document.js +80 -0
- package/dist/artifacts/guide.js +140 -0
- package/dist/artifacts/html-scan.js +329 -0
- package/dist/artifacts/json.js +68 -0
- package/dist/artifacts/kv-store.d.ts +20 -0
- package/dist/artifacts/kv-store.js +140 -0
- package/dist/artifacts/markdown.js +478 -0
- package/dist/artifacts/module.d.ts +37 -0
- package/dist/artifacts/module.js +60 -0
- package/dist/artifacts/operations.d.ts +195 -0
- package/dist/artifacts/operations.js +944 -0
- package/dist/artifacts/refresh.d.ts +53 -0
- package/dist/artifacts/refresh.js +235 -0
- package/dist/artifacts/routes.js +217 -0
- package/dist/artifacts/types.d.ts +214 -0
- package/dist/artifacts/types.js +22 -0
- package/dist/artifacts/validate.d.ts +62 -0
- package/dist/artifacts/validate.js +653 -0
- package/dist/artifacts.d.ts +6 -0
- package/dist/artifacts.js +9 -0
- package/dist/auth/downstream-oauth.js +397 -48
- package/dist/auth/oauth-start-reset.js +20 -0
- package/dist/branding.d.ts +87 -0
- package/dist/branding.js +170 -1
- package/dist/catalog-service.js +39 -29
- package/dist/catalog.js +42 -8
- package/dist/connector-access.d.ts +18 -4
- package/dist/connector-access.js +65 -20
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +19 -0
- package/dist/connectors/remote-mcp.js +48 -9
- package/dist/errors.d.ts +16 -1
- package/dist/errors.js +28 -0
- package/dist/execute.js +431 -75
- package/dist/executor-admission.d.ts +11 -1
- package/dist/executor-admission.js +79 -14
- package/dist/executors/quickjs.js +4 -2
- package/dist/index.d.ts +62 -6
- package/dist/index.js +230 -6
- package/dist/invocation.js +56 -10
- package/dist/meta-tools.js +7 -2
- package/dist/module-contracts.d.ts +21 -1
- package/dist/oauth-callback-outcome.js +43 -18
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +62 -5
- package/dist/operator-ui/view.js +372 -28
- package/dist/page-styles.js +7 -0
- package/dist/program-source.js +116 -0
- package/dist/registry.d.ts +42 -6
- package/dist/registry.js +164 -60
- package/dist/resumable.js +1291 -0
- package/dist/routes/activity.js +6 -69
- package/dist/routes/actor-labels.js +114 -0
- package/dist/routes/credentials.js +27 -5
- package/dist/routes/mcp.js +120 -59
- package/dist/routes/oauth-management.js +87 -20
- package/dist/routes/oauth.js +54 -87
- package/dist/routes/operator.js +1 -0
- package/dist/routes/shared.d.ts +23 -1
- package/dist/routes/shared.js +20 -2
- package/dist/routes/ui-data.js +2 -1
- package/dist/routes/ui.js +19 -4
- package/dist/run-journal.js +347 -0
- package/dist/runtime/shared-read.js +106 -0
- package/dist/server.js +30 -2
- package/dist/skills.js +30 -11
- package/dist/timeout.js +26 -0
- package/dist/tool-safety.d.ts +35 -0
- package/dist/tool-safety.js +31 -0
- package/dist/types.d.ts +23 -0
- package/dist/ui.d.ts +17 -5
- package/dist/ui.js +51 -68
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +214 -27
- package/documentation/auth.md +186 -2
- package/documentation/code-mode.md +236 -63
- package/documentation/meta-tools.md +55 -29
- package/ethos.md +12 -10
- package/examples/worker/AGENTS.md +6 -0
- package/examples/worker/README.md +174 -10
- package/examples/worker/src/d1-activity-row.ts +14 -2
- package/examples/worker/src/d1-activity.ts +4 -2
- package/examples/worker/src/index.ts +36 -1
- package/examples/worker/src/r2-artifact-blobs.ts +29 -0
- package/examples/worker/wrangler.jsonc +5 -1
- package/package.json +6 -2
- package/templates/node/AGENTS.md +9 -2
- package/templates/node/README.md +31 -3
- package/templates/node/package.json +1 -1
- package/templates/node/src/index.ts +27 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Connector, ToolDef } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The one fail-closed classification shared by discovery and invocation.
|
|
4
|
+
*
|
|
5
|
+
* A tool is read-only only when it says so without also saying it is
|
|
6
|
+
* destructive. Missing, false, and contradictory annotations all require the
|
|
7
|
+
* approval-visible call path.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isExplicitlyReadOnly(definition: ToolDef): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* A deployment's approval exemptions, resolved once at construction from
|
|
12
|
+
* `execute.approval` (#566). Config is the only source: nothing an agent, the
|
|
13
|
+
* operator UI, or a downstream catalog says can add an entry.
|
|
14
|
+
*/
|
|
15
|
+
export interface ApprovalPolicy {
|
|
16
|
+
/** Connector id → the default for that connector's tools. */
|
|
17
|
+
readonly connectors: ReadonlyMap<string, "never" | "ask">;
|
|
18
|
+
/** Canonical `connector.tool` address → that tool's own setting. */
|
|
19
|
+
readonly tools: ReadonlyMap<string, "never" | "ask">;
|
|
20
|
+
}
|
|
21
|
+
export declare const NO_EXEMPTIONS: ApprovalPolicy;
|
|
22
|
+
/**
|
|
23
|
+
* Whether a program may call this tool without pausing for approval.
|
|
24
|
+
*
|
|
25
|
+
* Only a tool that is *not* explicitly read-only can be exempt — a read-only
|
|
26
|
+
* tool needs no exemption, and reporting one would blur the two classes an
|
|
27
|
+
* exemption must never merge. The most specific setting wins: the tool's
|
|
28
|
+
* address, then its connector's entry, then the connector's own default,
|
|
29
|
+
* then `"ask"`. Annotations play no part beyond that first test: a downstream
|
|
30
|
+
* that could annotate its way out of approval would be granting itself the
|
|
31
|
+
* capability this exists to keep in config. Exempt never means read-only:
|
|
32
|
+
* discovery keeps it in the approval-required class, and `call_tool` still
|
|
33
|
+
* refuses it.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isApprovalExempt(policy: ApprovalPolicy, connector: Pick<Connector, "id" | "approval">, toolName: string, definition: ToolDef): boolean;
|
package/dist/tool-safety.js
CHANGED
|
@@ -9,3 +9,34 @@ export function isExplicitlyReadOnly(definition) {
|
|
|
9
9
|
return (definition.annotations?.readOnlyHint === true &&
|
|
10
10
|
definition.annotations?.destructiveHint !== true);
|
|
11
11
|
}
|
|
12
|
+
export const NO_EXEMPTIONS = {
|
|
13
|
+
connectors: new Map(),
|
|
14
|
+
tools: new Map(),
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Whether a program may call this tool without pausing for approval.
|
|
18
|
+
*
|
|
19
|
+
* Only a tool that is *not* explicitly read-only can be exempt — a read-only
|
|
20
|
+
* tool needs no exemption, and reporting one would blur the two classes an
|
|
21
|
+
* exemption must never merge. The most specific setting wins: the tool's
|
|
22
|
+
* address, then its connector's entry, then the connector's own default,
|
|
23
|
+
* then `"ask"`. Annotations play no part beyond that first test: a downstream
|
|
24
|
+
* that could annotate its way out of approval would be granting itself the
|
|
25
|
+
* capability this exists to keep in config. Exempt never means read-only:
|
|
26
|
+
* discovery keeps it in the approval-required class, and `call_tool` still
|
|
27
|
+
* refuses it.
|
|
28
|
+
*/
|
|
29
|
+
export function isApprovalExempt(policy, connector, toolName, definition) {
|
|
30
|
+
if (isExplicitlyReadOnly(definition))
|
|
31
|
+
return false;
|
|
32
|
+
// This tool starts another program. Letting a program call it while holding
|
|
33
|
+
// the only executor permit would deadlock; manual refresh is a top-level
|
|
34
|
+
// action, even though other artifact writes are exempt inside programs.
|
|
35
|
+
if (connector.id === "artifacts" && toolName === "run_refresh")
|
|
36
|
+
return false;
|
|
37
|
+
const setting = policy.tools.get(`${connector.id}.${toolName}`) ??
|
|
38
|
+
policy.connectors.get(connector.id) ??
|
|
39
|
+
connector.approval ??
|
|
40
|
+
"ask";
|
|
41
|
+
return setting === "never";
|
|
42
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -149,6 +149,11 @@ export interface ConnectorCredentialConfig {
|
|
|
149
149
|
}
|
|
150
150
|
export interface CredentialTestResult {
|
|
151
151
|
ok: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Detail for the deployment's log. The operator page shows fixed copy for
|
|
154
|
+
* `ok` and never this text, since a downstream's reply quoted here can
|
|
155
|
+
* quote the credential it rejected.
|
|
156
|
+
*/
|
|
152
157
|
message?: string;
|
|
153
158
|
}
|
|
154
159
|
export interface ConnectorContext {
|
|
@@ -212,6 +217,11 @@ export interface ConnectorStatus {
|
|
|
212
217
|
state: ConnectorStatusState;
|
|
213
218
|
/** When state === "auth_required", the URL the operator should open. */
|
|
214
219
|
authorizationUrl?: string;
|
|
220
|
+
/**
|
|
221
|
+
* Set by a non-forced `startAuth` that handed back a still-recent pending
|
|
222
|
+
* authorization URL instead of starting a new flow. Absent otherwise.
|
|
223
|
+
*/
|
|
224
|
+
authorizationReused?: boolean;
|
|
215
225
|
message?: string;
|
|
216
226
|
/**
|
|
217
227
|
* Drift observed the last time this connector served a catalog refresh *in
|
|
@@ -240,6 +250,17 @@ export interface Connector {
|
|
|
240
250
|
title?: string;
|
|
241
251
|
/** How call_tool wraps results. "mcp" passes the content array through; anything else is JSON-wrapped. */
|
|
242
252
|
kind?: "mcp" | "api";
|
|
253
|
+
/**
|
|
254
|
+
* This connector's own default for its tools that are not explicitly
|
|
255
|
+
* read-only, inside `execute_code`: `"never"` lets a program call them
|
|
256
|
+
* without pausing for approval. Reserved for connectors connecta ships
|
|
257
|
+
* whose writes are cheap to undo (the planned artifacts connector, whose
|
|
258
|
+
* every write is a new immutable version); a deployment's own connectors
|
|
259
|
+
* leave it unset and exempt through `execute.approval`, which overrides
|
|
260
|
+
* this either way. Never read from a downstream catalog — a server cannot
|
|
261
|
+
* exempt its own tools — and never makes a tool read-only anywhere else.
|
|
262
|
+
*/
|
|
263
|
+
approval?: "never";
|
|
243
264
|
description?: string;
|
|
244
265
|
/**
|
|
245
266
|
* Max inline result size (bytes) for this connector's tools before
|
|
@@ -394,6 +415,8 @@ export interface AdmissionSnapshot {
|
|
|
394
415
|
maxQueueSize: number;
|
|
395
416
|
queueTimeoutMs: number;
|
|
396
417
|
retryAfterMs: number;
|
|
418
|
+
/** Hard admitted-request lifetime, present only on the /mcp pool. */
|
|
419
|
+
maxDurationMs?: number;
|
|
397
420
|
active: number;
|
|
398
421
|
queued: number;
|
|
399
422
|
closed: boolean;
|
package/dist/ui.d.ts
CHANGED
|
@@ -2,18 +2,21 @@ import type { OperatorSurface } from "./module-contracts.js";
|
|
|
2
2
|
import type { CredentialVault } from "./credential-contract.js";
|
|
3
3
|
import { type DeferredWork } from "./connector-scope.js";
|
|
4
4
|
import { type CredentialManagementCapability, type UiData, type UiProblem, type UiToolSafety } from "./operator-ui/model.js";
|
|
5
|
+
import { type ApprovalPolicy } from "./tool-safety.js";
|
|
5
6
|
import type { RegistryView } from "./registry.js";
|
|
6
7
|
import type { ConnectaBranding, Connector, ConnectorStatus, ToolDef, UiAuthConfig } from "./types.js";
|
|
7
8
|
export { filterUiConnectors, type CredentialManagementCapability, type UiConnector, type UiData, } from "./operator-ui/model.js";
|
|
8
9
|
export { CONNECTA_FAVICON_SVG, resolveBranding, isSafeHttpUrl, isSafeHttpsUrl, isSafeIconHref } from "./branding.js";
|
|
9
|
-
export type OperatorPage = "connections" | "activity";
|
|
10
|
+
export type OperatorPage = "connections" | "activity" | "artifacts" | "artifact";
|
|
10
11
|
export declare function operatorPageForPath(path: string): OperatorPage | undefined;
|
|
11
12
|
export declare function operatorPageTitle(page: OperatorPage, configuredTitle: string): string;
|
|
12
13
|
/**
|
|
13
|
-
* The badge a tool earns, from the
|
|
14
|
-
* here so the page reports the rule rather than restating it.
|
|
14
|
+
* The badge a tool earns, from the predicates core enforces with. Computed
|
|
15
|
+
* here so the page reports the rule rather than restating it. `exempt` is the
|
|
16
|
+
* config exemption (#566), which `isApprovalExempt` already refuses to grant
|
|
17
|
+
* a read-only tool.
|
|
15
18
|
*/
|
|
16
|
-
export declare function uiToolSafety(definition: ToolDef): UiToolSafety;
|
|
19
|
+
export declare function uiToolSafety(definition: ToolDef, exempt?: boolean): UiToolSafety;
|
|
17
20
|
/**
|
|
18
21
|
* The fix-prompt key for a connector that is not usable. Chosen from the
|
|
19
22
|
* status state and the connector's declared auth shape — configuration, never
|
|
@@ -40,12 +43,21 @@ export declare function buildUiData(registry: RegistryView, baseUrl: string, ser
|
|
|
40
43
|
mayManage?: (id: string) => boolean;
|
|
41
44
|
timeoutMs?: number;
|
|
42
45
|
signal?: AbortSignal;
|
|
46
|
+
approval?: ApprovalPolicy;
|
|
43
47
|
}): Promise<UiData>;
|
|
44
48
|
/**
|
|
45
49
|
* Every operator page serves this same data-free shell. Connector, credential,
|
|
46
50
|
* and activity data arrives only through the authenticated `/ui/*` APIs.
|
|
47
51
|
*/
|
|
48
|
-
export declare function renderUiHtml(uiAuth?: UiAuthConfig, mcpUrl?: string, branding?: ConnectaBranding, nonce?: string, page?: OperatorPage
|
|
52
|
+
export declare function renderUiHtml(uiAuth?: UiAuthConfig, mcpUrl?: string, branding?: ConnectaBranding, nonce?: string, page?: OperatorPage, options?: {
|
|
53
|
+
/** Where the Connections page lives, when this shell is on another origin. */
|
|
54
|
+
homeUrl?: string;
|
|
55
|
+
/**
|
|
56
|
+
* The origin that serves `/favicon.*`, when this shell is on another one:
|
|
57
|
+
* an artifact host answers those paths with 404. See `PageLayout.iconOrigin`.
|
|
58
|
+
*/
|
|
59
|
+
iconOrigin?: string | undefined;
|
|
60
|
+
}): string;
|
|
49
61
|
/** Mount the connection UI without enabling any storage or activity module. */
|
|
50
62
|
export declare function operatorUi(options?: {
|
|
51
63
|
branding?: ConnectaBranding;
|
package/dist/ui.js
CHANGED
|
@@ -6,7 +6,7 @@ import { runEdge } from "./runtime/run.js";
|
|
|
6
6
|
import { isExplicitlyReadOnly } from "./tool-safety.js";
|
|
7
7
|
import { OPERATOR_UI_CSS, OPERATOR_UI_SCRIPT, } from "./operator-ui/generated.js";
|
|
8
8
|
export { filterUiConnectors, } from "./operator-ui/model.js";
|
|
9
|
-
import {
|
|
9
|
+
import { escapeHtml, isSafeHttpsUrl, notFoundResponse, renderPage, resolveBranding, } from "./branding.js";
|
|
10
10
|
export { CONNECTA_FAVICON_SVG, resolveBranding, isSafeHttpUrl, isSafeHttpsUrl, isSafeIconHref } from "./branding.js";
|
|
11
11
|
/**
|
|
12
12
|
* A JS string literal safe to inline in a script element. Escaping `/` keeps
|
|
@@ -18,23 +18,39 @@ function stringForInlineScript(value) {
|
|
|
18
18
|
const OPERATOR_PAGE_LABELS = {
|
|
19
19
|
connections: "Connections",
|
|
20
20
|
activity: "Activity",
|
|
21
|
+
artifacts: "Artifacts",
|
|
22
|
+
artifact: "Artifact",
|
|
21
23
|
};
|
|
24
|
+
/** `/artifacts/<id>` and its snapshots, `/artifacts/<id>/v/<version>`. */
|
|
25
|
+
const ARTIFACT_PAGE = /^\/artifacts\/[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?(?:\/v\/\d{1,9})?$/;
|
|
22
26
|
export function operatorPageForPath(path) {
|
|
23
27
|
if (path === "/")
|
|
24
28
|
return "connections";
|
|
25
29
|
if (path === "/activity")
|
|
26
30
|
return "activity";
|
|
31
|
+
if (path === "/artifacts")
|
|
32
|
+
return "artifacts";
|
|
33
|
+
if (ARTIFACT_PAGE.test(path))
|
|
34
|
+
return "artifact";
|
|
27
35
|
return undefined;
|
|
28
36
|
}
|
|
37
|
+
/** Everything artifact pages own: shells, their API, and the frame. */
|
|
38
|
+
function isArtifactPath(path) {
|
|
39
|
+
return path === "/artifacts" || path.startsWith("/artifacts/");
|
|
40
|
+
}
|
|
29
41
|
export function operatorPageTitle(page, configuredTitle) {
|
|
30
42
|
return `${OPERATOR_PAGE_LABELS[page]} — ${configuredTitle}`;
|
|
31
43
|
}
|
|
32
44
|
/**
|
|
33
|
-
* The badge a tool earns, from the
|
|
34
|
-
* here so the page reports the rule rather than restating it.
|
|
45
|
+
* The badge a tool earns, from the predicates core enforces with. Computed
|
|
46
|
+
* here so the page reports the rule rather than restating it. `exempt` is the
|
|
47
|
+
* config exemption (#566), which `isApprovalExempt` already refuses to grant
|
|
48
|
+
* a read-only tool.
|
|
35
49
|
*/
|
|
36
|
-
export function uiToolSafety(definition) {
|
|
37
|
-
|
|
50
|
+
export function uiToolSafety(definition, exempt = false) {
|
|
51
|
+
if (isExplicitlyReadOnly(definition))
|
|
52
|
+
return "runs_in_programs";
|
|
53
|
+
return exempt ? "exempt" : "needs_approval";
|
|
38
54
|
}
|
|
39
55
|
/**
|
|
40
56
|
* The fix-prompt key for a connector that is not usable. Chosen from the
|
|
@@ -82,13 +98,6 @@ export async function buildUiData(registry, baseUrl, serverInfo, credentialVault
|
|
|
82
98
|
...detailOptions,
|
|
83
99
|
}));
|
|
84
100
|
}
|
|
85
|
-
function escapeHtmlAttr(value) {
|
|
86
|
-
return value
|
|
87
|
-
.replaceAll("&", "&")
|
|
88
|
-
.replaceAll('"', """)
|
|
89
|
-
.replaceAll("<", "<")
|
|
90
|
-
.replaceAll(">", ">");
|
|
91
|
-
}
|
|
92
101
|
function jsonForInlineScript(value) {
|
|
93
102
|
return JSON.stringify(value)
|
|
94
103
|
.replaceAll("<", "\\u003c")
|
|
@@ -99,7 +108,7 @@ function jsonForInlineScript(value) {
|
|
|
99
108
|
* Every operator page serves this same data-free shell. Connector, credential,
|
|
100
109
|
* and activity data arrives only through the authenticated `/ui/*` APIs.
|
|
101
110
|
*/
|
|
102
|
-
export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding, nonce, page = "connections") {
|
|
111
|
+
export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding, nonce, page = "connections", options = {}) {
|
|
103
112
|
const clerk = uiAuth?.kind === "clerk" ? uiAuth : undefined;
|
|
104
113
|
// The Clerk loader's origin. A value that fails the gate is dropped rather
|
|
105
114
|
// than escaped into the page: the loader tag is simply not emitted, the gate
|
|
@@ -129,81 +138,46 @@ export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding, nonce, page = "c
|
|
|
129
138
|
}
|
|
130
139
|
: (uiAuth ?? { kind: "bearer" });
|
|
131
140
|
const brand = resolveBranding(branding);
|
|
132
|
-
const title = operatorPageTitle(page, brand.pageTitle);
|
|
133
141
|
// When an operator shell ships a nonce-based CSP, every script it emits must
|
|
134
142
|
// carry that nonce to run; without a nonce the markup is unchanged.
|
|
135
143
|
const nonceAttr = nonce ? ` nonce="${nonce}"` : "";
|
|
136
|
-
// Top-left corner. With an owner set it reads "<owner> <product>"; without
|
|
137
|
-
// one the product label stands alone. Either half links out when the
|
|
138
|
-
// matching URL is configured.
|
|
139
|
-
const owner = brand.ownerName
|
|
140
|
-
? brand.ownerUrl
|
|
141
|
-
? `<a class="brand navlink" href="${escapeHtmlAttr(brand.ownerUrl)}">${escapeHtmlAttr(brand.ownerName)}</a>`
|
|
142
|
-
: `<span class="brand">${escapeHtmlAttr(brand.ownerName)}</span>`
|
|
143
|
-
: brand.productUrl
|
|
144
|
-
? `<a class="brand navlink" href="${escapeHtmlAttr(brand.productUrl)}">${escapeHtmlAttr(brand.productName)}</a>`
|
|
145
|
-
: `<span class="brand">${escapeHtmlAttr(brand.productName)}</span>`;
|
|
146
|
-
const product = brand.ownerName
|
|
147
|
-
? brand.productUrl
|
|
148
|
-
? `<a class="product navlink" href="${escapeHtmlAttr(brand.productUrl)}">${escapeHtmlAttr(brand.productName)}</a>`
|
|
149
|
-
: `<span class="product">${escapeHtmlAttr(brand.productName)}</span>`
|
|
150
|
-
: "";
|
|
151
144
|
const clerkScript = clerk && clerkScriptOrigin
|
|
152
|
-
? `<script${nonceAttr} crossorigin="anonymous" data-clerk-publishable-key="${
|
|
145
|
+
? `<script${nonceAttr} crossorigin="anonymous" data-clerk-publishable-key="${escapeHtml(clerk.publishableKey)}" src="${escapeHtml(clerkScriptOrigin)}/npm/@clerk/clerk-js@6/dist/clerk.browser.js"></script>`
|
|
153
146
|
: "";
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
<meta name="description" content="${escapeHtmlAttr(brand.description)}">
|
|
167
|
-
<link rel="icon" href="${escapeHtmlAttr(brand.faviconHref)}" type="image/svg+xml">
|
|
168
|
-
<link rel="shortcut icon" href="/favicon.ico">
|
|
169
|
-
<title>${escapeHtmlAttr(title)}</title>
|
|
170
|
-
${clerkScript}
|
|
171
|
-
<style>${OPERATOR_UI_CSS}${themeCss(brand.theme)}</style>
|
|
172
|
-
</head>
|
|
173
|
-
<body>
|
|
174
|
-
<a class="skip-link" href="#operatorContent">Skip to operator page</a>
|
|
175
|
-
<header class="masthead shell">
|
|
176
|
-
<div class="masthead-inner">
|
|
177
|
-
<div class="mast-nav">
|
|
178
|
-
${owner}
|
|
179
|
-
${product}
|
|
180
|
-
</div>
|
|
181
|
-
<div id="operatorNav"></div>
|
|
182
|
-
</div>
|
|
183
|
-
</header>
|
|
184
|
-
|
|
147
|
+
// The shared layout owns the head, scheme, theme, and masthead; the shell
|
|
148
|
+
// adds its own bundle (which already imports the shared tokens), the nav
|
|
149
|
+
// mount, and the app.
|
|
150
|
+
return renderPage(branding, {
|
|
151
|
+
title: operatorPageTitle(page, brand.pageTitle),
|
|
152
|
+
uiMounted: true,
|
|
153
|
+
iconOrigin: options.iconOrigin,
|
|
154
|
+
styles: OPERATOR_UI_CSS,
|
|
155
|
+
head: clerkScript,
|
|
156
|
+
skipTo: { id: "operatorContent", label: "Skip to operator page" },
|
|
157
|
+
mastheadEnd: '<div id="operatorNav"></div>',
|
|
158
|
+
body: `
|
|
185
159
|
<main id="operatorContent" class="page shell" tabindex="-1">
|
|
186
160
|
<div class="lead">
|
|
187
161
|
<h1>${OPERATOR_PAGE_LABELS[page]}</h1>
|
|
188
162
|
<div class="lead-copy">
|
|
189
|
-
<p>${
|
|
163
|
+
<p>${escapeHtml(brand.description)}</p>
|
|
190
164
|
<noscript><p class="msg">The operator pages need JavaScript. Nothing else here
|
|
191
165
|
does — agents reach this deployment through <span class="mono">/mcp</span>.</p></noscript>
|
|
192
166
|
</div>
|
|
193
167
|
</div>
|
|
194
168
|
</main>
|
|
195
|
-
|
|
196
|
-
|
|
169
|
+
`,
|
|
170
|
+
tail: `<script${nonceAttr}>
|
|
197
171
|
const AUTH = ${jsonForInlineScript(auth)};
|
|
198
172
|
const MCP_URL = ${jsonForInlineScript(mcpUrl)};
|
|
199
173
|
const INITIAL_PAGE = ${jsonForInlineScript(page)};
|
|
174
|
+
const HOME_URL = ${jsonForInlineScript(options.homeUrl ?? "/")};
|
|
200
175
|
const TITLE_SUFFIX = ${jsonForInlineScript(brand.pageTitle)};
|
|
201
176
|
const PRODUCT_NAME = ${stringForInlineScript(brand.productName)};
|
|
202
177
|
const PRODUCT_DESCRIPTION = ${stringForInlineScript(brand.description)};
|
|
203
178
|
const PRODUCT_OPERATOR_LABEL = ${stringForInlineScript(brand.productName + " operator")};
|
|
204
|
-
${OPERATOR_UI_SCRIPT}</script
|
|
205
|
-
|
|
206
|
-
</html>`;
|
|
179
|
+
${OPERATOR_UI_SCRIPT}</script>`,
|
|
180
|
+
});
|
|
207
181
|
}
|
|
208
182
|
function ownsOperatorPath(reserved, path) {
|
|
209
183
|
if (path === "/activity")
|
|
@@ -226,8 +200,17 @@ export function operatorUi(options = {}) {
|
|
|
226
200
|
// route here, and no activity module, runs for a path this surface does
|
|
227
201
|
// not own. The Activity page is the one addition; the server reserves
|
|
228
202
|
// it only when history is readable, and routeUi checks the same thing.
|
|
229
|
-
|
|
203
|
+
// Artifact pages are the other: they exist only beside the module.
|
|
204
|
+
const artifacts = context.opts.artifactsModule;
|
|
205
|
+
const artifactPath = Boolean(artifacts) && isArtifactPath(context.path);
|
|
206
|
+
if (!artifactPath && !ownsOperatorPath(reservedPaths, context.path))
|
|
230
207
|
return null;
|
|
208
|
+
if (artifactPath && !operatorPageForPath(context.path)) {
|
|
209
|
+
// The pages' JSON API and the sandboxed frame; the module owns both,
|
|
210
|
+
// and this bundle imports none of it.
|
|
211
|
+
return (await artifacts?.handle(context)) ??
|
|
212
|
+
notFoundResponse(context.request, context.opts);
|
|
213
|
+
}
|
|
231
214
|
const routes = [
|
|
232
215
|
...(context.opts.credentialVault ? [routeCredentials] : []),
|
|
233
216
|
routeOAuthManagement,
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED