@zackbart/connecta 0.37.0 → 0.38.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 +80 -0
- package/README.md +2 -2
- package/bin/connecta.mjs +1 -39
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.js +2 -4
- package/dist/agent-identity.js +1 -1
- package/dist/auth/clerk-sdk/generated.js +2 -1
- package/dist/auth/cloudflare-access.d.ts +3 -1
- package/dist/auth/cloudflare-access.js +22 -11
- package/dist/auth/downstream-oauth.js +195 -270
- package/dist/auth/downstream-revocation.js +1 -1
- package/dist/auth/oauth-grant.js +11 -0
- package/dist/auth/oauth-token-response.js +1 -1
- package/dist/auth/vercel-agent.js +2 -1
- package/dist/auth-elicitation.js +2 -2
- package/dist/branding.d.ts +1 -15
- package/dist/branding.js +2 -2
- package/dist/bytes.js +42 -0
- package/dist/call-admission-context.js +2 -5
- package/dist/call-admission.js +5 -0
- package/dist/call-recovery.js +0 -3
- package/dist/catalog-cache.js +26 -7
- package/dist/catalog-fingerprint.js +2 -18
- package/dist/catalog-revision.js +9 -0
- package/dist/catalog-service.js +51 -20
- package/dist/config-defaults.d.ts +1 -0
- package/dist/config-defaults.js +1 -0
- package/dist/config-schema.d.ts +1 -1
- package/dist/config-schema.js +6 -2
- package/dist/config.d.ts +41 -14
- package/dist/config.js +21 -71
- package/dist/connector-caller.js +1 -1
- package/dist/connectors/api-connector.js +4 -3
- package/dist/connectors/guarded-fetch.d.ts +0 -6
- package/dist/connectors/guarded-fetch.js +1 -11
- package/dist/connectors/remote-mcp.js +93 -69
- package/dist/connectors/resource-uri.js +0 -4
- package/dist/credentials.js +3 -0
- package/dist/d1.d.ts +0 -89
- package/dist/d1.js +0 -34
- package/dist/downstream-input.js +2 -2
- package/dist/execute.js +161 -116
- package/dist/executor-result.js +2 -1
- package/dist/executors/quickjs-runtime.js +2 -25
- package/dist/executors/quickjs.js +1 -1
- package/dist/guest-runtime.js +5 -3
- package/dist/guest-types.d.ts +5 -5
- package/dist/http/client.js +1 -1
- package/dist/http/errors.js +2 -2
- package/dist/http/json-schema.js +1 -1
- package/dist/http/node-transport.js +0 -1
- package/dist/http/oauth.js +1 -1
- package/dist/http/paging.js +6 -9
- package/dist/http/provider.js +1 -1
- package/dist/http/safety.js +1 -1
- package/dist/http/services.js +1 -8
- package/dist/http/tools.js +1 -7
- package/dist/http/vendor.js +7 -7
- package/dist/identity.js +2 -6
- package/dist/index.js +1 -2
- package/dist/invocation-auth.js +2 -1
- package/dist/invocation.js +8 -2
- package/dist/meta-tools.js +27 -27
- package/dist/native-tools.js +2 -1
- package/dist/oauth-handoff.js +2 -2
- package/dist/operation-vocabulary.js +1 -1
- package/dist/operator-record.d.ts +8 -2
- package/dist/operator-record.js +18 -50
- package/dist/operator-ui/contract.d.ts +4 -2
- package/dist/operator-ui/generated.js +3 -3
- package/dist/operator-ui/model.d.ts +7 -2
- package/dist/program-writes.js +3 -8
- package/dist/provider-call.js +0 -7
- package/dist/provider.d.ts +1 -1
- package/dist/providers/_shared/google/kit.js +1 -6
- package/dist/providers/_shared/google/service-account.js +1 -3
- package/dist/providers/_shared/google/workspace.d.ts +1 -1
- package/dist/providers/_shared/guide.js +3 -0
- package/dist/providers/_shared/json.js +12 -0
- package/dist/providers/_shared/rest/compact-index.js +1 -1
- package/dist/providers/_shared/rest/kit.js +1 -3
- package/dist/providers/_shared/rest/operation-index.js +1 -3
- package/dist/providers/_shared/rest/value-safety.js +26 -28
- package/dist/providers/breeze/api.js +2 -7
- package/dist/providers/breeze/index.js +4 -2
- package/dist/providers/breeze/tools.js +3 -4
- package/dist/providers/ccb/api.js +1 -6
- package/dist/providers/ccb/index.js +2 -1
- package/dist/providers/ccb/tools.js +1 -6
- package/dist/providers/cloudflare/api.js +2 -2
- package/dist/providers/cloudflare/index.js +2 -1
- package/dist/providers/cloudflare/safety.js +3 -3
- package/dist/providers/docs/tools.js +2 -10
- package/dist/providers/drive/tools.js +12 -25
- package/dist/providers/forms/tools.js +1 -9
- package/dist/providers/gmail/tools.js +5 -21
- package/dist/providers/overflow/api.js +1 -3
- package/dist/providers/overflow/index.js +2 -1
- package/dist/providers/overflow/tools.js +3 -7
- package/dist/providers/planning-center/api.js +2 -1
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/providers/planning-center/lib/json-api.js +1 -9
- package/dist/providers/planning-center/tools.js +2 -1
- package/dist/providers/sheets/tools.js +1 -9
- package/dist/providers/slides/tools.js +4 -13
- package/dist/providers/stripe/index.js +2 -1
- package/dist/providers/stripe/safety.js +2 -2
- package/dist/providers/tithely/api.js +2 -7
- package/dist/providers/tithely/index.js +3 -2
- package/dist/providers/tithely/tools.js +7 -5
- package/dist/providers/vercel/api.js +1 -6
- package/dist/providers/vercel/lib/pin.js +1 -1
- package/dist/providers/vercel/safety.js +1 -1
- package/dist/providers/vercel/tools.js +2 -1
- package/dist/redaction-marker.js +2 -2
- package/dist/registry.d.ts +4 -1
- package/dist/registry.js +35 -22
- package/dist/request-principal.js +15 -2
- package/dist/request-state.js +2 -2
- package/dist/routes/access-tokens.js +1 -1
- package/dist/routes/activity-disclosure.js +1 -1
- package/dist/routes/connect.js +2 -2
- package/dist/routes/credentials.js +1 -1
- package/dist/routes/dispatch.js +10 -0
- package/dist/routes/mcp.js +23 -21
- package/dist/routes/oauth-management.js +1 -1
- package/dist/routes/operator.js +53 -7
- package/dist/routes/shared.d.ts +8 -8
- package/dist/routes/shared.js +22 -15
- package/dist/routes/ui-config.js +6 -6
- package/dist/routes/ui-data.js +2 -2
- package/dist/routes/ui.js +9 -4
- package/dist/runtime/call-admission.js +1 -1
- package/dist/runtime/call.js +1 -1
- package/dist/runtime/settlement.js +1 -1
- package/dist/runtime/shared-read.js +2 -15
- package/dist/sent-secrets.d.ts +14 -6
- package/dist/sent-secrets.js +231 -85
- package/dist/server.js +4 -4
- package/dist/skills.js +8 -35
- package/dist/sqlite.d.ts +0 -19
- package/dist/sqlite.js +2 -83
- package/dist/storage/keys.js +29 -124
- package/dist/storage/sql-native.js +14 -111
- package/dist/storage/sql.d.ts +1 -24
- package/dist/storage/sql.js +1 -12
- package/dist/tool-error.js +4 -0
- package/dist/types.d.ts +4 -3
- package/dist/ui.js +3 -0
- package/dist/url-safety.js +0 -4
- package/dist/usage-guide.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +1 -1
- package/documentation/README.md +6 -42
- package/documentation/architecture.md +190 -507
- package/documentation/auth.md +172 -58
- package/documentation/code-mode.md +98 -66
- package/documentation/deploying.md +19 -40
- package/documentation/integrating.md +8 -19
- package/documentation/meta-tools.md +23 -6
- package/documentation/migration-0.33.md +5 -5
- package/documentation/migration-0.34.md +2 -4
- package/documentation/migration-retired-layouts.md +74 -0
- package/documentation/operator-tests.md +19 -119
- package/documentation/operator-ui.md +46 -8
- package/documentation/provider-auth-migration.md +4 -10
- package/documentation/provider-migration-0.29.md +4 -15
- package/examples/worker/AGENTS.md +4 -6
- package/examples/worker/README.md +20 -115
- package/guides/connecting-agents.md +7 -30
- package/guides/how-it-works.md +2 -2
- package/guides/safety-and-trust.md +4 -1
- package/package.json +19 -19
- package/templates/node/README.md +9 -26
- package/templates/node/package.json +1 -1
- package/dist/auth/oauth-v2-migration.js +0 -135
- package/dist/provider-call-facts.d.ts +0 -115
- package/dist/provider-call.d.ts +0 -112
- package/dist/storage/d1-copy.js +0 -255
- package/dist/storage/sql-schema.d.ts +0 -6
- package/examples/worker/kv-to-d1.wrangler.jsonc +0 -26
- package/examples/worker/scripts/copy-kv-to-d1.mjs +0 -162
package/dist/routes/shared.js
CHANGED
|
@@ -5,6 +5,7 @@ import { isMaintainer, isMember } from "../membership.js";
|
|
|
5
5
|
import { identityStorageKey, validIdentityReference } from "../identity.js";
|
|
6
6
|
import { assertsAgents, isAgentName } from "../agent-identity.js";
|
|
7
7
|
import { authenticate } from "../auth-admission.js";
|
|
8
|
+
import { attachFailureFacts, carryFailureFacts, failureRecord, logFailure } from "../operator-record.js";
|
|
8
9
|
export { msg } from "../errors.js";
|
|
9
10
|
import { authCall, authFlow, authInvoke, authProgram, registerAuth } from "../auth/native.js";
|
|
10
11
|
import { runEdge } from "../runtime/run.js";
|
|
@@ -87,17 +88,17 @@ const providerChallengeNative = /* @__PURE__ */ registerAuth(providerChallenge,
|
|
|
87
88
|
return authFlow(function* () {
|
|
88
89
|
const privateHeaders = new Headers(response.headers);
|
|
89
90
|
privateHeaders.set("Cache-Control", "no-store");
|
|
90
|
-
response = new Response(response.body, {
|
|
91
|
+
response = carryFailureFacts(response, new Response(response.body, {
|
|
91
92
|
status: response.status,
|
|
92
93
|
statusText: response.statusText,
|
|
93
94
|
headers: privateHeaders,
|
|
94
|
-
});
|
|
95
|
+
}));
|
|
95
96
|
if (response.status !== 401)
|
|
96
97
|
return response;
|
|
97
98
|
if (!response.headers.has("WWW-Authenticate")) {
|
|
98
99
|
const headers = new Headers(response.headers);
|
|
99
100
|
headers.set("WWW-Authenticate", refusingProvider?.challenge?.(request, baseUrl) ?? "Bearer");
|
|
100
|
-
response = new Response(response.body, { status: response.status, statusText: response.statusText, headers });
|
|
101
|
+
response = carryFailureFacts(response, new Response(response.body, { status: response.status, statusText: response.statusText, headers }));
|
|
101
102
|
}
|
|
102
103
|
const path = new URL(request.url).pathname;
|
|
103
104
|
const pool = /^\/mcp\/([a-z0-9_-]+)$/.exec(path)?.[1];
|
|
@@ -113,7 +114,7 @@ const providerChallengeNative = /* @__PURE__ */ registerAuth(providerChallenge,
|
|
|
113
114
|
return response;
|
|
114
115
|
const headers = new Headers(response.headers);
|
|
115
116
|
headers.set("WWW-Authenticate", challenge);
|
|
116
|
-
return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
|
|
117
|
+
return carryFailureFacts(response, new Response(response.body, { status: response.status, statusText: response.statusText, headers }));
|
|
117
118
|
});
|
|
118
119
|
});
|
|
119
120
|
function recognizedProvider(request, auth, runtimeContext) {
|
|
@@ -162,7 +163,7 @@ const authorizeNative = /* @__PURE__ */ registerAuth(authorize, function (reques
|
|
|
162
163
|
return { ok: false, response: privateJson({ error: "authenticated user required" }, { status: 403 }) };
|
|
163
164
|
}
|
|
164
165
|
// Every caller of this function is a human route. Agents reach pools only at
|
|
165
|
-
// `/agent/<pool
|
|
166
|
+
// `/agent/<pool>`, so a workload or `cta_` credential refuses here
|
|
166
167
|
// without verification, and never falls back to a cookie, session or Access context.
|
|
167
168
|
if (machineCredential) {
|
|
168
169
|
return {
|
|
@@ -213,6 +214,12 @@ const authorizeNative = /* @__PURE__ */ registerAuth(authorize, function (reques
|
|
|
213
214
|
if (!admission.admitted)
|
|
214
215
|
return { ok: false, response: admission.response };
|
|
215
216
|
const result = admission.result;
|
|
217
|
+
if (!result.ok && result.response.status === 503 && provider.kind === "cloudflare-access") {
|
|
218
|
+
logFailure(logger, "Cloudflare Access verification unavailable", failureRecord({
|
|
219
|
+
accessContext: Boolean(runtimeContext?.access),
|
|
220
|
+
authorizationHeader: credential.kind,
|
|
221
|
+
}, result.response));
|
|
222
|
+
}
|
|
216
223
|
// Only built-in agent credential adapters assert an agent, and they never
|
|
217
224
|
// answer here. Any `agent` claim refuses like an unknown token.
|
|
218
225
|
const agent = result.ok ? result.agent : undefined;
|
|
@@ -272,10 +279,10 @@ const authorizeNative = /* @__PURE__ */ registerAuth(authorize, function (reques
|
|
|
272
279
|
ok: false,
|
|
273
280
|
response: (yield authCall(providerChallenge, [
|
|
274
281
|
lastResponse ??
|
|
275
|
-
privateJson({ error: "unauthorized" }, {
|
|
282
|
+
attachFailureFacts(privateJson({ error: "unauthorized" }, {
|
|
276
283
|
status: 401,
|
|
277
284
|
headers: { "WWW-Authenticate": "Bearer" },
|
|
278
|
-
}),
|
|
285
|
+
}), { reason: "authentication_provider_missing" }),
|
|
279
286
|
request,
|
|
280
287
|
baseUrl,
|
|
281
288
|
auth,
|
|
@@ -292,7 +299,7 @@ function agentChallenge() {
|
|
|
292
299
|
};
|
|
293
300
|
}
|
|
294
301
|
/**
|
|
295
|
-
* Admission at an agent endpoint, `/agent/<pool
|
|
302
|
+
* Admission at an agent endpoint, `/agent/<pool>`. Only the built-in
|
|
296
303
|
* agent credential adapters are consulted: Clerk and Access never run, cookies and Clerk
|
|
297
304
|
* handshake parameters are stripped, and the runtime context, which carries
|
|
298
305
|
* Access's `ctx.access` even on a bypassed path, is never passed. Anything but
|
|
@@ -369,7 +376,7 @@ export function isSameOrigin(request, baseUrl) {
|
|
|
369
376
|
return false;
|
|
370
377
|
}
|
|
371
378
|
}
|
|
372
|
-
export function withSecurityHeaders(response, requestUrl
|
|
379
|
+
export function withSecurityHeaders(response, requestUrl) {
|
|
373
380
|
const headers = response.headers.get("Content-Type")?.toLowerCase().startsWith("text/html")
|
|
374
381
|
? htmlSecurityHeaders(response.headers)
|
|
375
382
|
: new Headers(response.headers);
|
|
@@ -385,7 +392,7 @@ export function withSecurityHeaders(response, requestUrl, _path) {
|
|
|
385
392
|
});
|
|
386
393
|
}
|
|
387
394
|
/**
|
|
388
|
-
* Plain `/mcp` and every operator route admit a maintainer
|
|
395
|
+
* Plain `/mcp` and every operator route admit a maintainer. An open
|
|
389
396
|
* deployment has no inbound auth and so no maintainers: its one anonymous
|
|
390
397
|
* caller keeps the whole default endpoint, as the startup warning says.
|
|
391
398
|
*/
|
|
@@ -394,7 +401,7 @@ export function admitsMaintainer(opts, authz) {
|
|
|
394
401
|
}
|
|
395
402
|
/**
|
|
396
403
|
* The declared pool `/mcp/<name>` serves to this person, or undefined for the
|
|
397
|
-
* same 404. An agent is never admitted here, even when listed
|
|
404
|
+
* same 404. An agent is never admitted here, even when listed.
|
|
398
405
|
*/
|
|
399
406
|
export function memberPool(opts, name, identity) {
|
|
400
407
|
if (identity.agent !== undefined)
|
|
@@ -410,7 +417,7 @@ export function agentPool(opts, name, identity) {
|
|
|
410
417
|
return pool?.members.some((member) => member.kind === "agent" && isMember(member, identity)) ? pool : undefined;
|
|
411
418
|
}
|
|
412
419
|
/**
|
|
413
|
-
* The connectors of every pool this identity is a member of
|
|
420
|
+
* The connectors of every pool this identity is a member of: what a
|
|
414
421
|
* pool member may ever manage, and only their personal ones among these.
|
|
415
422
|
*/
|
|
416
423
|
export function memberConnectors(opts, identity) {
|
|
@@ -428,7 +435,7 @@ export function memberConnectors(opts, identity) {
|
|
|
428
435
|
}
|
|
429
436
|
/**
|
|
430
437
|
* Whether this identity may start, finish, or remove a connector's auth
|
|
431
|
-
*
|
|
438
|
+
* Only an interactive person ever may; an agent or any other machine
|
|
432
439
|
* credential manages nothing. Shared auth is deployment-wide, so it takes a
|
|
433
440
|
* maintainer. A personal connector takes a stable principal that is a
|
|
434
441
|
* maintainer, whose plain `/mcp` includes it, or a member of a pool that
|
|
@@ -447,12 +454,12 @@ export function mayManageConnector(opts, authz, connector) {
|
|
|
447
454
|
const pools = memberConnectors(opts, identity);
|
|
448
455
|
return pools === "all" || pools.has(connector.id);
|
|
449
456
|
}
|
|
450
|
-
/** Agent token list, issue, rotate, and revoke: an interactive maintainer only
|
|
457
|
+
/** Agent token list, issue, rotate, and revoke: an interactive maintainer only. */
|
|
451
458
|
export function managesAgentTokens(opts, authz) {
|
|
452
459
|
return (authz.identity.interactive && authz.identity.agent === undefined && isMaintainer(opts.maintainers, authz.identity));
|
|
453
460
|
}
|
|
454
461
|
/**
|
|
455
|
-
* Activity reads: a maintainer principal, interactive or not
|
|
462
|
+
* Activity reads: a maintainer principal, interactive or not. Members
|
|
456
463
|
* and agents read none, and an open deployment has no maintainer.
|
|
457
464
|
*/
|
|
458
465
|
export function readsActivity(opts, authz) {
|
package/dist/routes/ui-config.js
CHANGED
|
@@ -23,7 +23,7 @@ import { closeScope } from "../runtime/connector-scope.js";
|
|
|
23
23
|
import { withDeadlineEffect } from "../runtime/run.js";
|
|
24
24
|
import { uiProblemFor } from "../ui.js";
|
|
25
25
|
import { workspaceSubjectOf } from "../workspace-subject.js";
|
|
26
|
-
import { authorized, refuse, scopeFor, serveOperator } from "./operator.js";
|
|
26
|
+
import { authorized, operatorViewerKey, refuse, scopeFor, serveOperator, } from "./operator.js";
|
|
27
27
|
import { isMaintainer, isMember } from "../membership.js";
|
|
28
28
|
import { admitsMaintainer, mayManageConnector, privateJson, readsActivity } from "./shared.js";
|
|
29
29
|
const cachedMaintainers = new WeakSet();
|
|
@@ -338,7 +338,7 @@ function matrixFor(context, visible, rows, ownersVisible) {
|
|
|
338
338
|
function maintainerRead(context, authz) {
|
|
339
339
|
const { opts } = context;
|
|
340
340
|
return Effect.gen(function* () {
|
|
341
|
-
const registry = opts.registry.scoped(scopeFor(authz));
|
|
341
|
+
const registry = opts.registry.scoped(scopeFor(authz, opts.config.auth.length > 0));
|
|
342
342
|
const selected = context.url.searchParams.get("connector");
|
|
343
343
|
const visible = registry.listConnectors().filter((connector) => selected === null || connector.id === selected);
|
|
344
344
|
if (selected !== null && visible.length === 0)
|
|
@@ -359,7 +359,7 @@ function maintainerRead(context, authz) {
|
|
|
359
359
|
.map(([id, tools]) => [id, Object.fromEntries(Object.entries(tools).filter(([name]) => validName(name)))]));
|
|
360
360
|
// A pool owner is a Workspace mailbox address. Only an authenticated
|
|
361
361
|
// maintainer reads it; an open deployment's anonymous caller, which this
|
|
362
|
-
// route admits, gets null, the same as an unset owner
|
|
362
|
+
// route admits, gets null, the same as an unset owner.
|
|
363
363
|
const ownersVisible = opts.config.auth.length > 0 && isMaintainer(opts.maintainers, authz.identity);
|
|
364
364
|
config.pools = config.pools.map((pool) => ({
|
|
365
365
|
...pool,
|
|
@@ -442,7 +442,7 @@ function maintainerRead(context, authz) {
|
|
|
442
442
|
agents,
|
|
443
443
|
matrix,
|
|
444
444
|
connectors: [],
|
|
445
|
-
you: { interactive: authz.identity.interactive },
|
|
445
|
+
you: { interactive: authz.identity.interactive, viewerKey: yield* operatorViewerKey(context, authz) },
|
|
446
446
|
};
|
|
447
447
|
return privateJson(contract);
|
|
448
448
|
});
|
|
@@ -553,7 +553,7 @@ function memberRead(context, authz, pools) {
|
|
|
553
553
|
return Effect.gen(function* () {
|
|
554
554
|
const allIds = opts.registry.listConnectors().map((c) => c.id);
|
|
555
555
|
const ids = new Set(allIds.filter((id) => pools.some(([, pool]) => servesConnector(pool, id))));
|
|
556
|
-
const registry = opts.registry.scoped(scopeFor(authz, [...ids]));
|
|
556
|
+
const registry = opts.registry.scoped(scopeFor(authz, opts.config.auth.length > 0, [...ids]));
|
|
557
557
|
const selected = context.url.searchParams.get("connector");
|
|
558
558
|
const visible = registry
|
|
559
559
|
.listConnectors()
|
|
@@ -583,7 +583,7 @@ function memberRead(context, authz, pools) {
|
|
|
583
583
|
agents: [],
|
|
584
584
|
matrix: [],
|
|
585
585
|
connectors,
|
|
586
|
-
you: { interactive: authz.identity.interactive },
|
|
586
|
+
you: { interactive: authz.identity.interactive, viewerKey: yield* operatorViewerKey(context, authz) },
|
|
587
587
|
};
|
|
588
588
|
return privateJson(contract);
|
|
589
589
|
});
|
package/dist/routes/ui-data.js
CHANGED
|
@@ -30,10 +30,10 @@ export function uiData(registry, baseUrl, options) {
|
|
|
30
30
|
* connecta described the failure itself, that failure's typed facts. The
|
|
31
31
|
* message is never logged, whatever it looks like: a connector's own
|
|
32
32
|
* `status()` can quote a downstream error body, which can quote the
|
|
33
|
-
* secret it just rejected (INV-6).
|
|
33
|
+
* secret it just rejected (INV-6). Non-error `ok` and `per_caller` states are dropped.
|
|
34
34
|
*/
|
|
35
35
|
const logStatus = (id, state, failure, requestScope) => {
|
|
36
|
-
if (state === "ok")
|
|
36
|
+
if (state === "ok" || state === "per_caller")
|
|
37
37
|
return;
|
|
38
38
|
const logger = registry.contextFor(id, baseUrl, requestScope).logger;
|
|
39
39
|
logFailure(logger, "operator status", failureRecord({ connector: id, state }, failure), state === "auth_required" ? "info" : "warn");
|
package/dist/routes/ui.js
CHANGED
|
@@ -9,7 +9,7 @@ import { Effect } from "effect";
|
|
|
9
9
|
import { CONNECTA_VERSION } from "../version.js";
|
|
10
10
|
import { CONNECTA_FAVICON_ICO } from "../favicon.js";
|
|
11
11
|
import { CONNECTA_FAVICON_SVG, operatorPageForPath, renderUiHtml } from "../ui.js";
|
|
12
|
-
import { authorized, refuse, scopeFor, serveOperator, visibleRegistry, } from "./operator.js";
|
|
12
|
+
import { authorized, operatorViewerKey, refuse, scopeFor, serveOperator, visibleRegistry, } from "./operator.js";
|
|
13
13
|
import { managesAgentTokens, mayManageConnector, privateJson, readsActivity } from "./shared.js";
|
|
14
14
|
import { routeUiConfig, scanOperatorActivity } from "./ui-config.js";
|
|
15
15
|
import { uiData } from "./ui-data.js";
|
|
@@ -144,7 +144,7 @@ function connectorDetail(context, id) {
|
|
|
144
144
|
const connector = registry.getConnector(id);
|
|
145
145
|
if (!connector)
|
|
146
146
|
return yield* refuse("unknown connector", 404);
|
|
147
|
-
const data = yield* uiData(opts.registry.scoped(scopeFor(authz, [connector.id])), baseUrl, {
|
|
147
|
+
const data = yield* uiData(opts.registry.scoped(scopeFor(authz, opts.config.auth.length > 0, [connector.id])), baseUrl, {
|
|
148
148
|
serverInfo: opts.config.serverInfo,
|
|
149
149
|
activityContext: operatorActivityContext(context, authz),
|
|
150
150
|
credentialVault: opts.config.vault,
|
|
@@ -168,7 +168,11 @@ function connectorDetail(context, id) {
|
|
|
168
168
|
signal: request.signal,
|
|
169
169
|
timeoutError: new Error("Activity observation timed out."),
|
|
170
170
|
}).pipe(Effect.catch(() => Effect.succeed("unavailable")));
|
|
171
|
-
return privateJson({
|
|
171
|
+
return privateJson({
|
|
172
|
+
...detail,
|
|
173
|
+
viewerKey: yield* operatorViewerKey(context, authz),
|
|
174
|
+
permissions: permissions(connector),
|
|
175
|
+
});
|
|
172
176
|
});
|
|
173
177
|
}
|
|
174
178
|
/** The summary: every visible connector as "loading", with no probe at all. */
|
|
@@ -178,10 +182,11 @@ function summary(context) {
|
|
|
178
182
|
const authz = yield* authorized(context);
|
|
179
183
|
const registry = yield* visibleRegistry(context, authz);
|
|
180
184
|
const { visible, mayManage, permissions, activityEnabled, credentialManagement } = operatorView(context, authz, registry);
|
|
181
|
-
// Only maintainers reach here
|
|
185
|
+
// Only maintainers reach here; setup commands are offered for every
|
|
182
186
|
// declared pool, since they administer them all.
|
|
183
187
|
const pools = [...opts.pools.keys()];
|
|
184
188
|
return privateJson({
|
|
189
|
+
viewerKey: yield* operatorViewerKey(context, authz),
|
|
185
190
|
...(pools.length ? { pools } : {}),
|
|
186
191
|
serverInfo: opts.config.serverInfo,
|
|
187
192
|
connectaVersion: CONNECTA_VERSION,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// block: the one place outside its instance methods that may read its private
|
|
8
8
|
// bookkeeping, and one that emits nothing into the `.d.ts`.
|
|
9
9
|
//
|
|
10
|
-
// The two controllers stay separate on purpose
|
|
10
|
+
// The two controllers stay separate on purpose: request admission
|
|
11
11
|
// bounds executor work, this bounds calls to one downstream connector, and a
|
|
12
12
|
// shared queue between them was refused.
|
|
13
13
|
import { Effect } from "effect";
|
package/dist/runtime/call.js
CHANGED
|
@@ -106,7 +106,7 @@ function ended() {
|
|
|
106
106
|
return new ConnectorCallError("connector_call_failed", "This call has ended; a capability kept from it can no longer be used.", { retryable: false });
|
|
107
107
|
}
|
|
108
108
|
/** Build one call's services. Invocation calls this in the scoped attempt that owns the permit (B.1). */
|
|
109
|
-
|
|
109
|
+
function openCall(inputs) {
|
|
110
110
|
let open = true;
|
|
111
111
|
const parentSignal = inputs.parent ? Context.get(inputs.parent.services, Signal) : undefined;
|
|
112
112
|
const parents = [inputs.signal, parentSignal, inputs.requestSignal].filter((signal) => signal !== undefined);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// it was created.") when the Worker's compatibility date is 2025-06-16 or
|
|
11
11
|
// later, in releases since September 2026. The refusal throws out of the call
|
|
12
12
|
// that completed the Deferred, failing the request that released a slot, and
|
|
13
|
-
// the waiter never resumes
|
|
13
|
+
// the waiter never resumes. Awaiting a Promise around that wait is no
|
|
14
14
|
// help: the fiber waiting on the Deferred inside the Promise is resumed in the
|
|
15
15
|
// completing request just the same.
|
|
16
16
|
//
|
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// Discovery and the registry both coalesce a connector's catalog read inside
|
|
4
|
-
// one request, so a search, a describe, and the call after them cost one
|
|
5
|
-
// downstream listing. The read used to run under whichever reader asked
|
|
6
|
-
// first: its signal was the read's signal, so a short-deadline call that
|
|
7
|
-
// happened to start the read failed a search that joined it with the call's
|
|
8
|
-
// timeout, and a cancelled search cancelled a read others were still waiting
|
|
9
|
-
// on (#571).
|
|
10
|
-
//
|
|
11
|
-
// So the read owns its signal. Each reader waits under its own cancellation,
|
|
12
|
-
// and the read is cancelled only when no reader is left to want it. Nothing
|
|
13
|
-
// here is shared across requests: a SharedRead lives in one request's map and
|
|
14
|
-
// is waited on only by that request's fibers, which is why a plain count of
|
|
15
|
-
// readers is enough and no edge is needed between them.
|
|
1
|
+
// Request-local catalog reads own their signal. Each reader waits under its own
|
|
2
|
+
// cancellation; the read is cancelled only when no reader remains (INV-7).
|
|
16
3
|
import { Deferred, Effect, Exit } from "effect";
|
|
17
4
|
import { forkRequest } from "./run.js";
|
|
18
5
|
import { requestOwner } from "./request-scope.js";
|
package/dist/sent-secrets.d.ts
CHANGED
|
@@ -47,6 +47,9 @@ export declare class SentSecrets {
|
|
|
47
47
|
request(input: RequestInfo | URL, init?: RequestInit): void;
|
|
48
48
|
/** Serialized wire text about to be returned, stored, or paged, under payload rules. */
|
|
49
49
|
text(value: string): string;
|
|
50
|
+
/** An SDK response body, checked after it has been fully read so late registrations
|
|
51
|
+
* still apply. An idle set leaves the original serialization untouched. */
|
|
52
|
+
serializedAgentOutput(body: string, json: boolean): string;
|
|
50
53
|
/** A plain-data snapshot of `value` with every match removed: own enumerable data properties
|
|
51
54
|
* (an error's own fields, non-enumerable ones included), read once each. Accessors are never
|
|
52
55
|
* invoked, except an error's stack and the accessors its class defines, read once into the
|
|
@@ -154,10 +157,16 @@ export declare class SentSecrets {
|
|
|
154
157
|
* decoded views are matched together, as one lane between unmatched separators. Each decode
|
|
155
158
|
* is paid for from the call's work budget; a part the budget cannot check is withheld. */
|
|
156
159
|
private encodedLeaves;
|
|
157
|
-
/**
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
160
|
+
/** Check replacements as returned, including their placeholder seams. Numbers touched by
|
|
161
|
+
* the first scan get one chance beside the replaced strings; every later hit withholds its
|
|
162
|
+
* entire part. Each changing round therefore removes at least one remaining part. Unchanged
|
|
163
|
+
* lanes pay for no retry. Large lanes recheck changed parts and the seams touching them; an
|
|
164
|
+
* unaffordable check withholds every part it read and all original retention candidates. */
|
|
165
|
+
private settleParts;
|
|
166
|
+
/** The parts' joined text, with each part's replacement. A touched number is checked again
|
|
167
|
+
* after string replacement before withholding it whole. Tiny or short whole private values,
|
|
168
|
+
* deep matches and unchecked runs still withhold changed numbers whole. A bounded lane also
|
|
169
|
+
* loses an unfinished tail, and withholds every touched number without a second check. */
|
|
161
170
|
private safeParts;
|
|
162
171
|
/** Leaves too large to match joined within the work budget: each is matched alone, in order,
|
|
163
172
|
* and one that does not fit is withheld without spending what a smaller one needs. A run
|
|
@@ -215,7 +224,6 @@ export declare function agentOutputOperations<T extends Record<string, (...args:
|
|
|
215
224
|
/** Slot reads cover custom handlers too, including keys put in query strings. A field named
|
|
216
225
|
* as an identifier (an application or client ID, a username, a public key) is matched whole. */
|
|
217
226
|
export declare function registerCredentialValues(secrets: SentSecrets, values: import("./types.js").ConnectorCredentialValues | null): void;
|
|
218
|
-
export declare function trackCredentialReads(ctx: ConnectorContext): void;
|
|
219
227
|
/** A connector's result or error as it enters: every connector's credentials and the
|
|
220
228
|
* request's private arguments, under payload rules. */
|
|
221
229
|
export declare function redactSentSecrets<T>(ctx: ConnectorContext, value: T): T;
|
|
@@ -226,7 +234,7 @@ export declare function requestSentSecrets(ctx: ConnectorContext): SentSecrets;
|
|
|
226
234
|
* extracted from. Redact it whole first: a cut through an echoed form can leave a remnant
|
|
227
235
|
* shorter than any fragment the output boundary matches, and a normalized number or
|
|
228
236
|
* extracted substring may no longer hold the form at all. */
|
|
229
|
-
export declare
|
|
237
|
+
export declare const redactBeforeCut: typeof redactSentSecrets;
|
|
230
238
|
/** Base64, or base64url with `url`, of bytes or UTF-8 text Connecta returns to an agent: a
|
|
231
239
|
* download, an attachment, a REST body, a page cursor. Every such encoding goes through here,
|
|
232
240
|
* since no later pass reads through an encoding the output does not declare: the content is
|