@zackbart/connecta 0.23.0 → 0.24.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 +5 -0
- package/CHANGELOG.md +53 -0
- package/README.md +18 -10
- package/dist/activity-friction.d.ts +3 -0
- package/dist/activity-friction.js +19 -0
- package/dist/activity.d.ts +11 -2
- package/dist/activity.js +15 -19
- package/dist/auth/downstream-oauth.d.ts +2 -1
- package/dist/auth/downstream-oauth.js +10 -1
- package/dist/branding.d.ts +67 -0
- package/dist/branding.js +176 -0
- package/dist/connectors/remote-mcp.js +3 -5
- package/dist/credential-contract.d.ts +24 -0
- package/dist/credential-contract.js +1 -0
- package/dist/credential-rules.d.ts +85 -0
- package/dist/credential-rules.js +107 -0
- package/dist/credentials.d.ts +4 -100
- package/dist/credentials.js +3 -107
- package/dist/execute.d.ts +6 -0
- package/dist/execute.js +17 -7
- package/dist/index.d.ts +35 -56
- package/dist/index.js +34 -58
- package/dist/invocation.js +19 -4
- package/dist/meta-tools.d.ts +4 -0
- package/dist/meta-tools.js +8 -4
- package/dist/module-contracts.d.ts +19 -0
- package/dist/module-contracts.js +1 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +6 -3
- package/dist/operator-ui/view.d.ts +2 -18
- package/dist/operator-ui/view.js +3 -20
- package/dist/registry.d.ts +4 -1
- package/dist/registry.js +4 -6
- package/dist/routes/activity.js +1 -1
- package/dist/routes/credentials.js +5 -2
- package/dist/routes/mcp.js +13 -3
- package/dist/routes/oauth-management.d.ts +2 -0
- package/dist/routes/oauth-management.js +108 -0
- package/dist/routes/oauth.d.ts +0 -1
- package/dist/routes/oauth.js +21 -121
- package/dist/routes/shared.d.ts +23 -17
- package/dist/routes/shared.js +48 -44
- package/dist/routes/ui.js +36 -33
- package/dist/server.js +6 -26
- package/dist/types.d.ts +2 -0
- package/dist/ui.d.ts +15 -70
- package/dist/ui.js +176 -317
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +26 -17
- package/documentation/auth.md +61 -106
- package/documentation/cloudflare.md +1 -1
- package/documentation/code-mode.md +2 -2
- package/documentation/connectors.md +1 -1
- package/documentation/linear.md +1 -1
- package/documentation/meta-tools.md +6 -4
- package/documentation/mixpanel.md +1 -1
- package/documentation/notion.md +2 -2
- package/documentation/operations.md +14 -14
- package/documentation/operator-ui.md +82 -104
- package/documentation/optional-modules-upgrade.md +243 -0
- package/documentation/provider-conventions.md +5 -3
- package/documentation/revenuecat.md +1 -1
- package/documentation/storage-and-credentials.md +59 -40
- package/documentation/stripe.md +1 -1
- package/documentation/upgrading.md +33 -4
- package/ethos.md +22 -30
- package/examples/worker/AGENTS.md +3 -1
- package/examples/worker/README.md +68 -84
- package/examples/worker/src/d1-activity.ts +1 -1
- package/examples/worker/src/index.ts +11 -6
- package/package.json +17 -1
- package/templates/node/AGENTS.md +8 -6
- package/templates/node/README.md +56 -67
- package/templates/node/package.json +1 -1
- package/templates/node/src/file-activity.ts +1 -1
- package/templates/node/src/index.ts +11 -12
- package/dist/access-tokens.d.ts +0 -31
- package/dist/access-tokens.js +0 -236
- package/dist/routes/access-tokens.d.ts +0 -6
- package/dist/routes/access-tokens.js +0 -83
package/dist/routes/shared.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { identityStorageKey, validIdentityReference } from "../identity.js";
|
|
2
|
-
import { operatorPageForPath } from "../ui.js";
|
|
3
2
|
export { msg } from "../errors.js";
|
|
4
3
|
export function privateJson(body, init = {}) {
|
|
5
4
|
const headers = new Headers(init.headers);
|
|
@@ -36,7 +35,9 @@ export async function authorize(request, baseUrl, auth, runtimeContext, identity
|
|
|
36
35
|
const identity = { actor, interactive: false };
|
|
37
36
|
let connectorIds = "all";
|
|
38
37
|
try {
|
|
39
|
-
connectorIds = await identityConfig
|
|
38
|
+
connectorIds = identityConfig?.connectorAccess ? await identityConfig.connectorAccess(identity) : "all";
|
|
39
|
+
if (connectorIds !== "all" && (!Array.isArray(connectorIds) || !connectorIds.every(id => typeof id === "string" && /^[a-z0-9_-]+$/.test(id))))
|
|
40
|
+
throw new Error("invalid connector permission");
|
|
40
41
|
}
|
|
41
42
|
catch {
|
|
42
43
|
return {
|
|
@@ -44,7 +45,7 @@ export async function authorize(request, baseUrl, auth, runtimeContext, identity
|
|
|
44
45
|
response: privateJson({ error: "identity access resolution failed" }, { status: 403 }),
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
|
-
return { ok: true, actor, identity, connectorIds, operator: false };
|
|
48
|
+
return { ok: true, actor, identity, connectorIds, operator: false, credentialAdministration: "none", personalConnection: "none" };
|
|
48
49
|
}
|
|
49
50
|
let lastResponse = null;
|
|
50
51
|
for (const provider of auth) {
|
|
@@ -74,14 +75,26 @@ export async function authorize(request, baseUrl, auth, runtimeContext, identity
|
|
|
74
75
|
interactive,
|
|
75
76
|
};
|
|
76
77
|
let operator = interactive;
|
|
78
|
+
let credentialAdministration = "none";
|
|
79
|
+
let personalConnection = "none";
|
|
77
80
|
let connectorIds = "all";
|
|
78
81
|
try {
|
|
79
|
-
if (identityConfig?.
|
|
82
|
+
if (identityConfig?.activityAccess) {
|
|
80
83
|
operator = interactive && principal
|
|
81
|
-
? await identityConfig.
|
|
84
|
+
? await identityConfig.activityAccess(principal)
|
|
82
85
|
: false;
|
|
83
86
|
}
|
|
84
|
-
connectorIds = await identityConfig
|
|
87
|
+
connectorIds = identityConfig?.connectorAccess ? await identityConfig.connectorAccess(identity) : "all";
|
|
88
|
+
if (interactive) {
|
|
89
|
+
credentialAdministration = identityConfig?.credentialAdministration ? await identityConfig.credentialAdministration(identity) : "none";
|
|
90
|
+
personalConnection = principal && identityConfig?.personalConnection ? await identityConfig.personalConnection(identity) : "none";
|
|
91
|
+
}
|
|
92
|
+
if (typeof operator !== "boolean")
|
|
93
|
+
throw new Error("invalid activity permission");
|
|
94
|
+
for (const permission of [connectorIds, credentialAdministration, personalConnection]) {
|
|
95
|
+
if (permission !== "all" && permission !== "none" && (!Array.isArray(permission) || !permission.every(id => typeof id === "string" && /^[a-z0-9_-]+$/.test(id))))
|
|
96
|
+
throw new Error("invalid identity permission");
|
|
97
|
+
}
|
|
85
98
|
}
|
|
86
99
|
catch {
|
|
87
100
|
return {
|
|
@@ -100,6 +113,8 @@ export async function authorize(request, baseUrl, auth, runtimeContext, identity
|
|
|
100
113
|
? { principalKey: await identityStorageKey(principal) }
|
|
101
114
|
: {}),
|
|
102
115
|
connectorIds,
|
|
116
|
+
credentialAdministration,
|
|
117
|
+
personalConnection,
|
|
103
118
|
operator,
|
|
104
119
|
...(operator ? { uiAdminEligible: true } : {}),
|
|
105
120
|
};
|
|
@@ -118,34 +133,6 @@ export async function authorize(request, baseUrl, auth, runtimeContext, identity
|
|
|
118
133
|
}),
|
|
119
134
|
};
|
|
120
135
|
}
|
|
121
|
-
export async function authorizeUiAdmin(request, baseUrl, auth, purpose = "credential management", runtimeContext, identityConfig) {
|
|
122
|
-
// Operator mutation is intentionally narrower than /mcp and /ui/data: only
|
|
123
|
-
// an interactive provider may admit it. A static bearer token is useful
|
|
124
|
-
// for headless tool calls but must not become a deployment-admin key.
|
|
125
|
-
//
|
|
126
|
-
// Every interactive provider gets a turn, the way the /mcp gate does.
|
|
127
|
-
// Stopping at the first would make admission depend on config order: a failed gate or
|
|
128
|
-
// missing user may simply mean a later provider is the one meant to admit.
|
|
129
|
-
// The last refusal is returned if none do.
|
|
130
|
-
const authz = await authorizeUiIdentity(request, baseUrl, auth, purpose, runtimeContext, identityConfig);
|
|
131
|
-
if (!authz.ok)
|
|
132
|
-
return authz;
|
|
133
|
-
if (!authz.operator || !authz.actor.id) {
|
|
134
|
-
return {
|
|
135
|
-
ok: false,
|
|
136
|
-
response: privateJson({ error: `${purpose} requires operator access` }, { status: 403 }),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
return {
|
|
140
|
-
ok: true,
|
|
141
|
-
userId: authz.identity.principal?.id ?? authz.actor.id,
|
|
142
|
-
...(authz.identity.principal
|
|
143
|
-
? { principal: authz.identity.principal }
|
|
144
|
-
: {}),
|
|
145
|
-
...(authz.principalKey ? { principalKey: authz.principalKey } : {}),
|
|
146
|
-
connectorIds: authz.connectorIds,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
136
|
export async function authorizeUiIdentity(request, baseUrl, auth, purpose, runtimeContext, identityConfig) {
|
|
150
137
|
const providers = auth.filter((candidate) => candidate.interactiveOperator);
|
|
151
138
|
if (providers.length === 0) {
|
|
@@ -181,25 +168,42 @@ export function isSameOrigin(request, baseUrl) {
|
|
|
181
168
|
return false;
|
|
182
169
|
}
|
|
183
170
|
}
|
|
184
|
-
export function withSecurityHeaders(response, requestUrl,
|
|
171
|
+
export function withSecurityHeaders(response, requestUrl, _path) {
|
|
185
172
|
const headers = new Headers(response.headers);
|
|
186
173
|
headers.set("X-Content-Type-Options", "nosniff");
|
|
187
174
|
headers.set("Referrer-Policy", "no-referrer");
|
|
188
175
|
if (requestUrl.protocol === "https:") {
|
|
189
176
|
headers.set("Strict-Transport-Security", "max-age=31536000");
|
|
190
177
|
}
|
|
191
|
-
if (operatorPageForPath(path) || path === "/ui") {
|
|
192
|
-
// Operator HTML responses ship their own nonce-based script CSP (which
|
|
193
|
-
// already includes frame-ancestors 'none'); only fall back to the
|
|
194
|
-
// framing-only directive when no CSP is present (for example redirects).
|
|
195
|
-
if (!headers.has("Content-Security-Policy")) {
|
|
196
|
-
headers.set("Content-Security-Policy", "frame-ancestors 'none'");
|
|
197
|
-
}
|
|
198
|
-
headers.set("X-Frame-Options", "DENY");
|
|
199
|
-
}
|
|
200
178
|
return new Response(response.body, {
|
|
201
179
|
status: response.status,
|
|
202
180
|
statusText: response.statusText,
|
|
203
181
|
headers,
|
|
204
182
|
});
|
|
205
183
|
}
|
|
184
|
+
/** Unknown configured ids refuse the complete view, including management rights. */
|
|
185
|
+
export function validateAuthPermissions(authz, registry) {
|
|
186
|
+
for (const value of [
|
|
187
|
+
authz.connectorIds,
|
|
188
|
+
authz.credentialAdministration,
|
|
189
|
+
authz.personalConnection,
|
|
190
|
+
]) {
|
|
191
|
+
if (value === "all" || value === "none")
|
|
192
|
+
continue;
|
|
193
|
+
if (!Array.isArray(value) || value.some(id => !registry.getConnector(id))) {
|
|
194
|
+
throw new Error("invalid identity permission connector ids");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
export function mayManageConnector(authz, connector) {
|
|
199
|
+
if (!authz.identity.interactive)
|
|
200
|
+
return false;
|
|
201
|
+
if (authz.connectorIds !== "all" && !authz.connectorIds.includes(connector.id)) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
const permission = connector.authScope === "personal"
|
|
205
|
+
? authz.personalConnection
|
|
206
|
+
: authz.credentialAdministration;
|
|
207
|
+
return permission === "all" ||
|
|
208
|
+
(permission !== "none" && permission.includes(connector.id));
|
|
209
|
+
}
|
package/dist/routes/ui.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { CONNECTA_VERSION } from "../version.js";
|
|
1
2
|
import { CONNECTA_FAVICON_ICO } from "../favicon.js";
|
|
2
|
-
import { buildUiData, CONNECTA_FAVICON_SVG,
|
|
3
|
-
import { authorize, msg, privateJson, } from "./shared.js";
|
|
3
|
+
import { buildUiData, CONNECTA_FAVICON_SVG, operatorPageForPath, renderUiHtml, } from "../ui.js";
|
|
4
|
+
import { authorize, mayManageConnector, validateAuthPermissions, msg, privateJson, } from "./shared.js";
|
|
4
5
|
/**
|
|
5
6
|
* Headers that make an operator-supplied favicon body inert on this origin.
|
|
6
7
|
* The SVG route is the sharp one: `image/svg+xml` is an *active* content type,
|
|
@@ -67,7 +68,7 @@ export async function routeUi(context) {
|
|
|
67
68
|
});
|
|
68
69
|
}
|
|
69
70
|
const operatorPage = operatorPageForPath(path);
|
|
70
|
-
if (operatorPage) {
|
|
71
|
+
if (operatorPage && (operatorPage !== "activity" || opts.activity?.list)) {
|
|
71
72
|
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
72
73
|
return privateJson({ error: "method not allowed" }, { status: 405 });
|
|
73
74
|
}
|
|
@@ -97,13 +98,17 @@ export async function routeUi(context) {
|
|
|
97
98
|
},
|
|
98
99
|
});
|
|
99
100
|
}
|
|
100
|
-
|
|
101
|
+
const detail = /^\/ui\/connectors\/([a-z0-9_-]+)$/.exec(path);
|
|
102
|
+
if (path !== "/ui/data" && !detail)
|
|
101
103
|
return null;
|
|
104
|
+
if (request.method !== "GET")
|
|
105
|
+
return privateJson({ error: "method not allowed" }, { status: 405 });
|
|
102
106
|
const authz = await authorize(request, baseUrl, opts.auth, runtimeContext, opts.identity);
|
|
103
107
|
if (!authz.ok)
|
|
104
108
|
return authz.response;
|
|
105
109
|
let registry;
|
|
106
110
|
try {
|
|
111
|
+
validateAuthPermissions(authz, opts.registry);
|
|
107
112
|
registry = opts.registry.scoped({
|
|
108
113
|
connectorIds: authz.connectorIds,
|
|
109
114
|
...(authz.subjectKey ? { subjectKey: authz.subjectKey } : {}),
|
|
@@ -113,33 +118,31 @@ export async function routeUi(context) {
|
|
|
113
118
|
catch (error) {
|
|
114
119
|
return privateJson({ error: msg(error) }, { status: 403 });
|
|
115
120
|
}
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
: opts.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
(!opts.identity?.operatorAccess || eligibleOperator), credentialManagement, defer, interactiveManager, opts.discoveryConcurrency, accessTokenManagement, personalManager ? authz.principalKey : undefined);
|
|
144
|
-
return privateJson(data);
|
|
121
|
+
const visible = registry.listConnectors();
|
|
122
|
+
const mayManage = (id) => { const connector = registry.getConnector(id); return Boolean(connector && mayManageConnector(authz, connector)); };
|
|
123
|
+
const permissions = (connector) => ({
|
|
124
|
+
use: true,
|
|
125
|
+
manageSharedAuth: connector.authScope !== "personal" && mayManage(connector.id),
|
|
126
|
+
connectPersonal: connector.authScope === "personal" && mayManage(connector.id),
|
|
127
|
+
});
|
|
128
|
+
const activityEnabled = Boolean(opts.activity?.list) && authz.operator;
|
|
129
|
+
const credentialManagement = visible.some(c => c.credential && mayManage(c.id))
|
|
130
|
+
? opts.credentialVault ? "available" : "vault_not_configured"
|
|
131
|
+
: authz.identity.interactive && !visible.some(c => c.credential) ? "no_slots" : "requires_operator";
|
|
132
|
+
if (detail) {
|
|
133
|
+
const connector = registry.getConnector(detail[1]);
|
|
134
|
+
if (!connector)
|
|
135
|
+
return privateJson({ error: "unknown connector" }, { status: 404 });
|
|
136
|
+
const one = opts.registry.scoped({ connectorIds: [connector.id], ...(authz.subjectKey ? { subjectKey: authz.subjectKey } : {}), ...(authz.principalKey ? { principalKey: authz.principalKey } : {}) });
|
|
137
|
+
const data = await buildUiData(one, baseUrl, opts.serverInfo, opts.credentialVault, activityEnabled, credentialManagement, defer, false, 1, authz.principalKey, { mayManage, timeoutMs: opts.probeTimeoutMs ?? 30_000, signal: request.signal });
|
|
138
|
+
return privateJson({ ...data.connectors[0], permissions: permissions(connector) });
|
|
139
|
+
}
|
|
140
|
+
return privateJson({
|
|
141
|
+
serverInfo: opts.serverInfo,
|
|
142
|
+
connectaVersion: CONNECTA_VERSION,
|
|
143
|
+
activityEnabled,
|
|
144
|
+
credentialManagement,
|
|
145
|
+
oauthManagement: visible.some(c => mayManage(c.id)),
|
|
146
|
+
connectors: visible.map(c => ({ id: c.id, ...(c.title ? { title: c.title } : {}), ...(c.description ? { description: c.description } : {}), authScope: c.authScope ?? "shared", status: "loading", toolCount: 0, tools: [], oauth: Boolean(c.startAuth && c.disconnectAuth), permissions: permissions(c) })),
|
|
147
|
+
});
|
|
145
148
|
}
|
package/dist/server.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { isAdmittingExecutor } from "./executor-admission.js";
|
|
2
|
-
import { routeAccessTokens } from "./routes/access-tokens.js";
|
|
3
|
-
import { routeActivity } from "./routes/activity.js";
|
|
4
|
-
import { routeCredentials } from "./routes/credentials.js";
|
|
5
2
|
import { createMcpRoute, MCP_CORS_HEADERS } from "./routes/mcp.js";
|
|
6
|
-
import { routeOAuthCallback,
|
|
3
|
+
import { routeOAuthCallback, } from "./routes/oauth.js";
|
|
7
4
|
import { withSecurityHeaders, } from "./routes/shared.js";
|
|
8
|
-
import { routeUi } from "./routes/ui.js";
|
|
9
5
|
/**
|
|
10
6
|
* Build the Web-standard fetch handler.
|
|
11
7
|
*
|
|
@@ -59,15 +55,9 @@ export function createFetchHandler(opts) {
|
|
|
59
55
|
};
|
|
60
56
|
const route = async () => {
|
|
61
57
|
// Private mutations own OPTIONS so they never inherit wildcard CORS.
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
return
|
|
65
|
-
const credentials = await routeCredentials(context);
|
|
66
|
-
if (credentials)
|
|
67
|
-
return credentials;
|
|
68
|
-
const oauthManagement = await routeOAuthManagement(context);
|
|
69
|
-
if (oauthManagement)
|
|
70
|
-
return oauthManagement;
|
|
58
|
+
const uiResponse = await opts.ui?.handle(context);
|
|
59
|
+
if (uiResponse)
|
|
60
|
+
return uiResponse;
|
|
71
61
|
if (request.method === "OPTIONS") {
|
|
72
62
|
for (const provider of auth) {
|
|
73
63
|
if (provider.handleMetadata) {
|
|
@@ -123,12 +113,8 @@ export function createFetchHandler(opts) {
|
|
|
123
113
|
},
|
|
124
114
|
reservedRoutes: [
|
|
125
115
|
"/health",
|
|
126
|
-
|
|
127
|
-
"/
|
|
128
|
-
"/tokens",
|
|
129
|
-
"/activity",
|
|
130
|
-
"/ui",
|
|
131
|
-
"/ui/*",
|
|
116
|
+
...(opts.ui?.reservedPaths ?? []),
|
|
117
|
+
...(opts.ui && opts.activity?.list ? ["/activity"] : []),
|
|
132
118
|
],
|
|
133
119
|
},
|
|
134
120
|
...(opts.deploymentInfo ? { deployment: opts.deploymentInfo } : {}),
|
|
@@ -137,12 +123,6 @@ export function createFetchHandler(opts) {
|
|
|
137
123
|
const oauthCallback = await routeOAuthCallback(context);
|
|
138
124
|
if (oauthCallback)
|
|
139
125
|
return oauthCallback;
|
|
140
|
-
const ui = await routeUi(context);
|
|
141
|
-
if (ui)
|
|
142
|
-
return ui;
|
|
143
|
-
const activity = await routeActivity(context);
|
|
144
|
-
if (activity)
|
|
145
|
-
return activity;
|
|
146
126
|
const mcp = await routeMcp(context);
|
|
147
127
|
if (mcp)
|
|
148
128
|
return mcp;
|
package/dist/types.d.ts
CHANGED
|
@@ -132,6 +132,8 @@ export interface CredentialTestResult {
|
|
|
132
132
|
message?: string;
|
|
133
133
|
}
|
|
134
134
|
export interface ConnectorContext {
|
|
135
|
+
/** Explicit downstream consent initiation, never set by status/catalog/calls. */
|
|
136
|
+
allowAuthorization?: boolean;
|
|
135
137
|
/** Storage namespaced to this connector. */
|
|
136
138
|
storage: KVStorage;
|
|
137
139
|
logger: Logger;
|
package/dist/ui.d.ts
CHANGED
|
@@ -1,75 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { OperatorSurface } from "./module-contracts.js";
|
|
2
|
+
import type { CredentialVault } from "./credential-contract.js";
|
|
2
3
|
import { type DeferredWork } from "./connector-scope.js";
|
|
3
|
-
import { type CredentialManagementCapability, type
|
|
4
|
+
import { type CredentialManagementCapability, type UiData } from "./operator-ui/model.js";
|
|
4
5
|
import type { RegistryView } from "./registry.js";
|
|
5
6
|
import type { ConnectaBranding, UiAuthConfig } from "./types.js";
|
|
6
|
-
export { filterUiConnectors, type
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
interface ResolvedBranding {
|
|
10
|
-
productName: string;
|
|
11
|
-
productUrl?: string;
|
|
12
|
-
ownerName?: string;
|
|
13
|
-
ownerUrl?: string;
|
|
14
|
-
description: string;
|
|
15
|
-
/** Browser tab title and page meta name. */
|
|
16
|
-
pageTitle: string;
|
|
17
|
-
/** href for the page's icon link. */
|
|
18
|
-
faviconHref: string;
|
|
19
|
-
themeColor: string;
|
|
20
|
-
}
|
|
21
|
-
export declare function resolveBranding(branding?: ConnectaBranding): ResolvedBranding;
|
|
22
|
-
/**
|
|
23
|
-
* Names of the branding URLs the operator set that failed their gate and were
|
|
24
|
-
* replaced by a default. Lives beside the gates so the startup warning cannot
|
|
25
|
-
* drift from them, and takes `unknown` fields for the same reason
|
|
26
|
-
* `resolveBranding` does — a warning helper must never throw.
|
|
27
|
-
*/
|
|
28
|
-
export declare function droppedBrandingUrls(branding?: ConnectaBranding): string[];
|
|
29
|
-
export declare function isSafeHttpUrl(url: unknown): boolean;
|
|
30
|
-
/**
|
|
31
|
-
* True for values allowed in the page's `<link rel="icon" href>`: an absolute
|
|
32
|
-
* `http(s)` URL (an icon the operator hosts elsewhere) or a path rooted at this
|
|
33
|
-
* origin. The relative carve-out is deliberate rather than accidental — the
|
|
34
|
-
* default href is the relative `/favicon.svg`, which `isSafeHttpUrl` alone would
|
|
35
|
-
* reject — and it is kept narrow on both ends.
|
|
36
|
-
*
|
|
37
|
-
* Root-relative only, because operator and OAuth callback pages sit at
|
|
38
|
-
* different depths and a document-relative path would resolve differently.
|
|
39
|
-
*
|
|
40
|
-
* "Root-relative" is enforced structurally: exactly one leading `/` followed by
|
|
41
|
-
* a character that is neither `/` nor `\`. Both of those would make the value an
|
|
42
|
-
* authority (`//host`, and `/\host` because the URL parser folds `\` to `/` in
|
|
43
|
-
* special schemes), pointing at an origin this server does not control. The test
|
|
44
|
-
* runs on a copy with tab/newline/CR removed, since the parser strips those
|
|
45
|
-
* anywhere and `/\t/host` would otherwise slip through as single-slash. The
|
|
46
|
-
* origin comparison that follows is defense in depth, not the authority check —
|
|
47
|
-
* on its own it would accept an authority that happened to equal the probe host.
|
|
48
|
-
*/
|
|
49
|
-
export declare function isSafeIconHref(href: unknown): boolean;
|
|
50
|
-
/** Absolute HTTPS gate for the `UiAuthConfig` URL fields documented in types.ts. */
|
|
51
|
-
export declare function isSafeHttpsUrl(url: unknown): boolean;
|
|
52
|
-
/**
|
|
53
|
-
* Names of the `uiAuth` URLs an inbound-auth provider supplied that failed their
|
|
54
|
-
* gate. Lives beside the gate for the same reason `droppedBrandingUrls` does: the
|
|
55
|
-
* startup warning cannot then drift from what rendering actually drops. Every
|
|
56
|
-
* field is read defensively rather than trusted, because a custom `InboundAuth`
|
|
57
|
-
* is untyped at a JS call site — `isSafeHttpsUrl` takes `unknown`, and a
|
|
58
|
-
* `uiAuth` that is not the clerk shape is reported as nothing to warn about.
|
|
59
|
-
*
|
|
60
|
-
* `frontendApiUrl` is required, so anything that fails its gate is a drop.
|
|
61
|
-
* `signInUrl` and `signUpUrl` are optional, so only a value the operator
|
|
62
|
-
* *supplied* and the gate then rejected is worth a warning — an unset field
|
|
63
|
-
* took no default away from anyone. `isSetUrlValue` decides that, the same way
|
|
64
|
-
* and for the same reasons it decides it for the branding URLs: a warning that
|
|
65
|
-
* fires for one and not the other would be reporting on the field rather than
|
|
66
|
-
* on the operator's intent. Rendering is not consulted for this: it drops on
|
|
67
|
-
* the gate alone, and a blank string fails that gate too — it is simply not
|
|
68
|
-
* *reported*, because a blank is indistinguishable from leaving the field
|
|
69
|
-
* alone.
|
|
70
|
-
*/
|
|
71
|
-
export declare function droppedUiAuthUrls(uiAuth?: UiAuthConfig): string[];
|
|
72
|
-
export type OperatorPage = "connections" | "credentials" | "tokens" | "activity";
|
|
7
|
+
export { filterUiConnectors, type CredentialManagementCapability, type UiConnector, type UiData, } from "./operator-ui/model.js";
|
|
8
|
+
export { CONNECTA_FAVICON_SVG, resolveBranding, isSafeHttpUrl, isSafeHttpsUrl, isSafeIconHref } from "./branding.js";
|
|
9
|
+
export type OperatorPage = "connections" | "activity";
|
|
73
10
|
export declare function operatorPageForPath(path: string): OperatorPage | undefined;
|
|
74
11
|
export declare function operatorPageTitle(page: OperatorPage, configuredTitle: string): string;
|
|
75
12
|
export declare function credentialManagementCapability(input: {
|
|
@@ -85,9 +22,17 @@ export declare function credentialManagementCapability(input: {
|
|
|
85
22
|
export declare function buildUiData(registry: RegistryView, baseUrl: string, serverInfo: {
|
|
86
23
|
name: string;
|
|
87
24
|
version: string;
|
|
88
|
-
}, credentialVault?: CredentialVault, activityEnabled?: boolean, credentialManagement?: CredentialManagementCapability, defer?: DeferredWork, oauthManagement?: boolean, discoveryConcurrency?: number,
|
|
25
|
+
}, credentialVault?: CredentialVault, activityEnabled?: boolean, credentialManagement?: CredentialManagementCapability, defer?: DeferredWork, oauthManagement?: boolean, discoveryConcurrency?: number, personalCredentialOwner?: string, detailOptions?: {
|
|
26
|
+
mayManage?: (id: string) => boolean;
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
signal?: AbortSignal;
|
|
29
|
+
}): Promise<UiData>;
|
|
89
30
|
/**
|
|
90
31
|
* Every operator page serves this same data-free shell. Connector, credential,
|
|
91
32
|
* and activity data arrives only through the authenticated `/ui/*` APIs.
|
|
92
33
|
*/
|
|
93
34
|
export declare function renderUiHtml(uiAuth?: UiAuthConfig, mcpUrl?: string, branding?: ConnectaBranding, nonce?: string, page?: OperatorPage): string;
|
|
35
|
+
/** Mount the connection UI without enabling any storage or activity module. */
|
|
36
|
+
export declare function operatorUi(options?: {
|
|
37
|
+
branding?: ConnectaBranding;
|
|
38
|
+
}): OperatorSurface;
|