@zackbart/connecta 0.19.0 → 0.21.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 +107 -0
- package/README.md +3 -1
- package/bin/connecta.mjs +23 -6
- package/dist/apps-shell.d.ts +10 -12
- package/dist/apps-shell.js +29 -220
- package/dist/auth/bearer.d.ts +2 -2
- package/dist/auth/bearer.js +2 -2
- package/dist/auth/clerk.js +1 -0
- package/dist/auth/cloudflare-access.d.ts +8 -0
- package/dist/auth/cloudflare-access.js +66 -0
- package/dist/execute.d.ts +0 -7
- package/dist/execute.js +20 -123
- package/dist/index.d.ts +2 -2
- package/dist/index.js +110 -69
- package/dist/invocation.d.ts +1 -1
- package/dist/meta-tools.d.ts +0 -1
- package/dist/meta-tools.js +10 -495
- package/dist/operator-ui/generated.d.ts +2 -2
- package/dist/operator-ui/generated.js +1 -1
- package/dist/operator-ui/model.d.ts +3 -3
- package/dist/operator-ui/view.d.ts +1 -1
- package/dist/operator-ui/view.js +6 -3
- package/dist/routes/access-tokens.d.ts +1 -1
- package/dist/routes/access-tokens.js +2 -2
- package/dist/routes/activity.js +2 -2
- package/dist/routes/credentials.js +1 -1
- package/dist/routes/mcp.js +1 -1
- package/dist/routes/oauth.js +1 -1
- package/dist/routes/shared.d.ts +4 -4
- package/dist/routes/shared.js +10 -10
- package/dist/routes/ui.js +12 -9
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +11 -8
- package/dist/types.d.ts +37 -22
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +7 -4
- package/documentation/auth.md +71 -7
- package/documentation/code-mode.md +23 -23
- package/documentation/meta-tools.md +26 -50
- package/documentation/operations.md +36 -21
- package/documentation/operator-ui.md +21 -5
- package/documentation/provider-conventions.md +3 -4
- package/documentation/upgrading.md +106 -8
- package/ethos.md +3 -4
- package/examples/worker/README.md +52 -32
- package/examples/worker/src/index.ts +32 -38
- package/examples/worker/wrangler.jsonc +12 -4
- package/package.json +5 -1
- package/templates/node/package.json +1 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
function identityString(identity, field) {
|
|
2
|
+
const value = identity[field];
|
|
3
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
4
|
+
}
|
|
5
|
+
function unauthorized() {
|
|
6
|
+
return {
|
|
7
|
+
ok: false,
|
|
8
|
+
response: Response.json({ error: "Cloudflare Access authentication required" }, { status: 401 }),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Trust the identity Cloudflare Access attached to this direct Worker
|
|
13
|
+
* invocation. Access has already validated the browser session, Managed OAuth
|
|
14
|
+
* token, or service-token headers before the Worker runs; this adapter does
|
|
15
|
+
* not accept or parse a caller-supplied JWT.
|
|
16
|
+
*/
|
|
17
|
+
export function cloudflareAccessAuth() {
|
|
18
|
+
return {
|
|
19
|
+
kind: "cloudflare-access",
|
|
20
|
+
interactiveOperator: true,
|
|
21
|
+
activityActorNamespace: "cloudflare-access",
|
|
22
|
+
uiAuth: { kind: "cloudflare-access" },
|
|
23
|
+
async authorize(_request, _baseUrl, runtimeContext) {
|
|
24
|
+
const access = runtimeContext?.access;
|
|
25
|
+
if (!access)
|
|
26
|
+
return unauthorized();
|
|
27
|
+
let identity;
|
|
28
|
+
try {
|
|
29
|
+
identity = await access.getIdentity();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return unauthorized();
|
|
33
|
+
}
|
|
34
|
+
if (!identity) {
|
|
35
|
+
// Access service-token policies authenticate the request and attach
|
|
36
|
+
// ctx.access, but getIdentity() is a user-identity API and returns
|
|
37
|
+
// undefined. Cloudflare strips the service-token headers before the
|
|
38
|
+
// Worker, so the Access application audience is the only trusted,
|
|
39
|
+
// stable service attribution available without parsing a JWT.
|
|
40
|
+
return { ok: true, subjectId: access.aud };
|
|
41
|
+
}
|
|
42
|
+
const userId = identityString(identity, "user_uuid") ??
|
|
43
|
+
identityString(identity, "email");
|
|
44
|
+
const commonName = identityString(identity, "common_name");
|
|
45
|
+
const serviceTokenId = identityString(identity, "service_token_id");
|
|
46
|
+
if (identity.service_token_status === true ||
|
|
47
|
+
serviceTokenId ||
|
|
48
|
+
(!userId && commonName)) {
|
|
49
|
+
const subjectId = serviceTokenId ?? commonName;
|
|
50
|
+
return subjectId
|
|
51
|
+
? { ok: true, subjectId }
|
|
52
|
+
: {
|
|
53
|
+
ok: false,
|
|
54
|
+
response: Response.json({ error: "Cloudflare Access service identity required" }, { status: 403 }),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (!userId) {
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
response: Response.json({ error: "Cloudflare Access user identity required" }, { status: 403 }),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return { ok: true, userId, subjectId: userId };
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
package/dist/execute.d.ts
CHANGED
|
@@ -91,14 +91,8 @@ export type EmittedBlock = {
|
|
|
91
91
|
data: string;
|
|
92
92
|
mimeType: string;
|
|
93
93
|
};
|
|
94
|
-
interface UiReadBinding {
|
|
95
|
-
address: string;
|
|
96
|
-
fixedArgs: Record<string, unknown>;
|
|
97
|
-
viewArgs: string[];
|
|
98
|
-
}
|
|
99
94
|
interface UiPayload {
|
|
100
95
|
html: string;
|
|
101
|
-
reads?: Record<string, UiReadBinding>;
|
|
102
96
|
}
|
|
103
97
|
/**
|
|
104
98
|
* Request-local collection for `connecta.emit` and `connecta.ui`. Budgets fail
|
|
@@ -136,7 +130,6 @@ export declare class EmitCollector {
|
|
|
136
130
|
* complaint about its type would send the author to fix the wrong thing.
|
|
137
131
|
*/
|
|
138
132
|
acceptUi(...values: unknown[]): void;
|
|
139
|
-
acceptValidatedUi(payload: UiPayload): void;
|
|
140
133
|
private assertUiVacant;
|
|
141
134
|
private acceptUiPayload;
|
|
142
135
|
}
|
package/dist/execute.js
CHANGED
|
@@ -6,8 +6,7 @@ import { guardExecuteResultValue, MAX_EXECUTE_LOG_CHARS, truncateExecuteText, }
|
|
|
6
6
|
import { ExecutorAdmissionError, ExecutorExecutionError, isAdmittingExecutor, } from "./executor-admission.js";
|
|
7
7
|
import { boundedEchoText, classifyCallError, msg } from "./errors.js";
|
|
8
8
|
import { InvocationFailure, InvocationService, } from "./invocation.js";
|
|
9
|
-
import { hasConnectorGuides } from "./skills.js";
|
|
10
|
-
import { isExplicitlyReadOnly } from "./tool-safety.js";
|
|
9
|
+
import { connectorGuide, connectorGuideRequired, connectorSkillName, hasConnectorGuides, } from "./skills.js";
|
|
11
10
|
/** Keep one model-written program from amplifying into an unbounded fan-out. */
|
|
12
11
|
const EXECUTE_MAX_HOST_CALLS = 20;
|
|
13
12
|
export const EXECUTE_MAX_BATCH_CALLS = 10;
|
|
@@ -149,11 +148,7 @@ function requireEmittedBlock(raw) {
|
|
|
149
148
|
}
|
|
150
149
|
return raw;
|
|
151
150
|
}
|
|
152
|
-
const UI_SHAPE_HINT = "connecta.ui accepts exactly one
|
|
153
|
-
const MAX_UI_READ_BINDINGS = 32;
|
|
154
|
-
const MAX_UI_VIEW_ARGS = 32;
|
|
155
|
-
const UI_READ_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
156
|
-
const FORBIDDEN_UI_KEY = new Set(["__proto__", "constructor", "prototype"]);
|
|
151
|
+
const UI_SHAPE_HINT = "connecta.ui accepts exactly one argument: a non-empty string of HTML";
|
|
157
152
|
/** What the argument was, named the way the emit validator names a bad field. */
|
|
158
153
|
function describeUiArgument(raw) {
|
|
159
154
|
if (raw === null)
|
|
@@ -168,9 +163,9 @@ function describeUiArgument(raw) {
|
|
|
168
163
|
return `${/^[aeiou]/.test(kind) ? "an" : "a"} ${kind}`;
|
|
169
164
|
}
|
|
170
165
|
/**
|
|
171
|
-
* Strict U1
|
|
172
|
-
*
|
|
173
|
-
* block
|
|
166
|
+
* Strict U1 validation. There is no options parameter and no sugar form, for
|
|
167
|
+
* M1's reason: sugar is how a one-shape contract grows hair. An options bag or
|
|
168
|
+
* an MCP block object is just a non-string, and fails as one.
|
|
174
169
|
*/
|
|
175
170
|
function requireUiHtml(raw) {
|
|
176
171
|
if (typeof raw !== "string" || raw.length === 0) {
|
|
@@ -178,80 +173,11 @@ function requireUiHtml(raw) {
|
|
|
178
173
|
}
|
|
179
174
|
return raw;
|
|
180
175
|
}
|
|
181
|
-
function requireRecord(raw, label) {
|
|
182
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
183
|
-
throw guestFailure("invalid_args", `${label} must be an object`);
|
|
184
|
-
}
|
|
185
|
-
return raw;
|
|
186
|
-
}
|
|
187
|
-
function requireExactKeys(value, allowed, label) {
|
|
188
|
-
const extras = Object.keys(value).filter((key) => !allowed.includes(key));
|
|
189
|
-
if (extras.length > 0) {
|
|
190
|
-
throw guestFailure("invalid_args", `${label} carries unsupported field(s) ${extras.map((key) => JSON.stringify(key)).join(", ")}`);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
function requireUiReadKey(raw, label) {
|
|
194
|
-
if (typeof raw !== "string" ||
|
|
195
|
-
raw.length === 0 ||
|
|
196
|
-
raw.length > 128 ||
|
|
197
|
-
FORBIDDEN_UI_KEY.has(raw)) {
|
|
198
|
-
throw guestFailure("invalid_args", `${label} must be a non-empty string of at most 128 characters and cannot be __proto__, constructor, or prototype`);
|
|
199
|
-
}
|
|
200
|
-
return raw;
|
|
201
|
-
}
|
|
202
|
-
function requireUiReads(raw) {
|
|
203
|
-
const record = requireRecord(raw, "connecta.ui options.reads");
|
|
204
|
-
const names = Object.keys(record);
|
|
205
|
-
if (names.length === 0 || names.length > MAX_UI_READ_BINDINGS) {
|
|
206
|
-
throw guestFailure("invalid_args", `connecta.ui options.reads must contain from 1 through ${MAX_UI_READ_BINDINGS} named bindings`);
|
|
207
|
-
}
|
|
208
|
-
const reads = Object.create(null);
|
|
209
|
-
for (const name of names) {
|
|
210
|
-
if (!UI_READ_NAME.test(name) || FORBIDDEN_UI_KEY.has(name)) {
|
|
211
|
-
throw guestFailure("invalid_args", `connecta.ui read binding name ${JSON.stringify(name)} must match ${UI_READ_NAME}`);
|
|
212
|
-
}
|
|
213
|
-
const value = requireRecord(record[name], `connecta.ui read binding ${JSON.stringify(name)}`);
|
|
214
|
-
requireExactKeys(value, ["address", "fixedArgs", "viewArgs"], `connecta.ui read binding ${JSON.stringify(name)}`);
|
|
215
|
-
if (typeof value.address !== "string" || value.address.length === 0) {
|
|
216
|
-
throw guestFailure("invalid_args", `connecta.ui read binding ${JSON.stringify(name)} address must be a non-empty string`);
|
|
217
|
-
}
|
|
218
|
-
const fixedArgs = value.fixedArgs === undefined
|
|
219
|
-
? {}
|
|
220
|
-
: requireRecord(value.fixedArgs, `connecta.ui read binding ${JSON.stringify(name)} fixedArgs`);
|
|
221
|
-
const rawViewArgs = value.viewArgs ?? [];
|
|
222
|
-
if (!Array.isArray(rawViewArgs) || rawViewArgs.length > MAX_UI_VIEW_ARGS) {
|
|
223
|
-
throw guestFailure("invalid_args", `connecta.ui read binding ${JSON.stringify(name)} viewArgs must be an array of at most ${MAX_UI_VIEW_ARGS} strings`);
|
|
224
|
-
}
|
|
225
|
-
const viewArgs = rawViewArgs.map((key) => requireUiReadKey(key, `connecta.ui read binding ${JSON.stringify(name)} viewArgs entry`));
|
|
226
|
-
if (new Set(viewArgs).size !== viewArgs.length) {
|
|
227
|
-
throw guestFailure("invalid_args", `connecta.ui read binding ${JSON.stringify(name)} viewArgs must not repeat a key`);
|
|
228
|
-
}
|
|
229
|
-
for (const key of viewArgs) {
|
|
230
|
-
if (Object.prototype.hasOwnProperty.call(fixedArgs, key)) {
|
|
231
|
-
throw guestFailure("invalid_args", `connecta.ui read binding ${JSON.stringify(name)} view argument ${JSON.stringify(key)} cannot override a fixed argument`);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
reads[name] = {
|
|
235
|
-
address: value.address,
|
|
236
|
-
fixedArgs,
|
|
237
|
-
viewArgs,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
return reads;
|
|
241
|
-
}
|
|
242
176
|
function requireUiPayload(values) {
|
|
243
|
-
if (values.length !== 1
|
|
177
|
+
if (values.length !== 1) {
|
|
244
178
|
throw guestFailure("invalid_args", `${UI_SHAPE_HINT}; got ${values.length} arguments`);
|
|
245
179
|
}
|
|
246
|
-
|
|
247
|
-
if (values.length === 1)
|
|
248
|
-
return { html };
|
|
249
|
-
const options = requireRecord(values[1], "connecta.ui options");
|
|
250
|
-
requireExactKeys(options, ["reads"], "connecta.ui options");
|
|
251
|
-
if (!Object.prototype.hasOwnProperty.call(options, "reads")) {
|
|
252
|
-
throw guestFailure("invalid_args", "connecta.ui options must contain reads");
|
|
253
|
-
}
|
|
254
|
-
return { html, reads: requireUiReads(options.reads) };
|
|
180
|
+
return { html: requireUiHtml(values[0]) };
|
|
255
181
|
}
|
|
256
182
|
/**
|
|
257
183
|
* Request-local collection for `connecta.emit` and `connecta.ui`. Budgets fail
|
|
@@ -309,10 +235,6 @@ export class EmitCollector {
|
|
|
309
235
|
this.assertUiVacant();
|
|
310
236
|
this.acceptUiPayload(requireUiPayload(values));
|
|
311
237
|
}
|
|
312
|
-
acceptValidatedUi(payload) {
|
|
313
|
-
this.assertUiVacant();
|
|
314
|
-
this.acceptUiPayload(payload);
|
|
315
|
-
}
|
|
316
238
|
assertUiVacant() {
|
|
317
239
|
if (this.ui) {
|
|
318
240
|
throw guestFailure("invalid_args", "connecta.ui accepts at most one payload per run: a view was already accepted and stands");
|
|
@@ -595,36 +517,6 @@ export async function buildSandboxProviders(registry, baseUrl, logger, activity,
|
|
|
595
517
|
const callNamespace = async (connectorId, toolAlias, args) => {
|
|
596
518
|
return called("call", () => invocation.invokeToolAlias(String(connectorId), String(toolAlias), sanitizeIdentifier, args ?? {}, invocationContext()));
|
|
597
519
|
};
|
|
598
|
-
/**
|
|
599
|
-
* A read binding is admitted while the program still owns the request. The
|
|
600
|
-
* shell later calls the ordinary `call_tool`, which repeats this same
|
|
601
|
-
* fail-closed check against the then-current catalog; validating here keeps
|
|
602
|
-
* a typo or destructive address from producing a view whose controls can
|
|
603
|
-
* never work, while validation at use keeps a stale view from retaining old
|
|
604
|
-
* authority.
|
|
605
|
-
*/
|
|
606
|
-
const validateUiReads = async (payload) => {
|
|
607
|
-
if (!payload.reads)
|
|
608
|
-
return payload;
|
|
609
|
-
const reads = Object.create(null);
|
|
610
|
-
for (const [name, binding] of Object.entries(payload.reads)) {
|
|
611
|
-
const resolution = await catalog.resolveTool(binding.address, limits.signal !== undefined ? { signal: limits.signal } : {});
|
|
612
|
-
if (!resolution.ok) {
|
|
613
|
-
throw new InvocationFailure({
|
|
614
|
-
...resolution.error,
|
|
615
|
-
message: `connecta.ui read binding ${JSON.stringify(name)} could not resolve ${JSON.stringify(binding.address)}: ${resolution.error.message}`,
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
if (!isExplicitlyReadOnly(resolution.resolved.definition)) {
|
|
619
|
-
throw guestFailure("destructive_tool_requires_approval", `connecta.ui read binding ${JSON.stringify(name)} refuses ${JSON.stringify(binding.address)}: the tool is not explicitly read-only`);
|
|
620
|
-
}
|
|
621
|
-
reads[name] = {
|
|
622
|
-
...binding,
|
|
623
|
-
address: `${resolution.resolved.connector.id}.${resolution.resolved.toolName}`,
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
return { html: payload.html, reads };
|
|
627
|
-
};
|
|
628
520
|
const fns = {
|
|
629
521
|
__callNamespace: callNamespace,
|
|
630
522
|
call: (address, args) => callAddress(address, args),
|
|
@@ -646,8 +538,7 @@ export async function buildSandboxProviders(registry, baseUrl, logger, activity,
|
|
|
646
538
|
if (!limits.emitCollector) {
|
|
647
539
|
throw guestFailure("unavailable", "connecta.ui is unavailable: no emission collector was configured for this execution", true);
|
|
648
540
|
}
|
|
649
|
-
|
|
650
|
-
limits.emitCollector.acceptValidatedUi(payload);
|
|
541
|
+
limits.emitCollector.acceptUi(...values);
|
|
651
542
|
},
|
|
652
543
|
batch: async (calls) => {
|
|
653
544
|
const started = Date.now();
|
|
@@ -965,9 +856,15 @@ function connectorInventory(connectors) {
|
|
|
965
856
|
return `${prefix}none.`;
|
|
966
857
|
const entries = connectors.map((connector) => {
|
|
967
858
|
const shortcut = sanitizeIdentifier(connector.id);
|
|
968
|
-
|
|
859
|
+
const address = shortcut === connector.id
|
|
969
860
|
? connector.id
|
|
970
861
|
: `${connector.id} (shortcut ${shortcut})`;
|
|
862
|
+
if (!connectorGuide(connector))
|
|
863
|
+
return address;
|
|
864
|
+
const requirement = connectorGuideRequired(connector)
|
|
865
|
+
? "required guide"
|
|
866
|
+
: "guide";
|
|
867
|
+
return `${address} (${requirement} ${connectorSkillName(connector.id)})`;
|
|
971
868
|
});
|
|
972
869
|
const shown = [];
|
|
973
870
|
for (let index = 0; index < entries.length; index++) {
|
|
@@ -988,18 +885,18 @@ function connectorInventory(connectors) {
|
|
|
988
885
|
return `${prefix}${shown.join(", ")}.`;
|
|
989
886
|
return `${prefix}${shown.join(", ")}${shown.length > 0 ? "; " : ""}+${omitted} more.`;
|
|
990
887
|
}
|
|
991
|
-
const executeDescription = (emitBudgets, connectorGuides, connectors) => `Choose the route before discovery.
|
|
888
|
+
const executeDescription = (emitBudgets, connectorGuides, connectors) => `Choose the route before discovery. A known address uses call_tool. Unknown-address and wider read-only work use exactly one execute_code call that discovers, calls, and returns the answer. Finish in that program; don't return catalog matches for a later call. Only readOnlyHint: true tools are available. Limits: ${EXECUTE_MAX_HOST_CALLS} host calls per run, ${EXECUTE_MAX_BATCH_CALLS} per batch, ${EXECUTE_HOST_CALL_TIMEOUT_MS / 1_000}-second host deadline.
|
|
992
889
|
|
|
993
890
|
${connectorInventory(connectors)}
|
|
994
891
|
|
|
995
|
-
Write one plain-JavaScript async arrow function. Use only:
|
|
892
|
+
Fetch required guides named above before executing. Write one plain-JavaScript async arrow function. Use only:
|
|
996
893
|
- <connectorId>.<toolName>(args) for a sanitized shortcut, or connecta.call(address, args) for a canonical address.
|
|
997
|
-
- connecta.search(args)
|
|
894
|
+
- connecta.search(args) returns { tools }; connecta.describe(args) returns { tools }; use entry key lists. connecta.batch(calls) accepts canonical connector addresses only.
|
|
998
895
|
- connecta.emit(block) — { type: "text", text } or { type: "image" | "audio", data (base64), mimeType }. Success-only; ${emitBudgets.maxBlocks} blocks/${emitBudgets.maxBytes} bytes; invalid/over-budget throws.
|
|
999
|
-
- connecta.ui(html
|
|
896
|
+
- connecta.ui(html) for one display-only, success-only view; return the same summary the HTML renders.
|
|
1000
897
|
- console.log(...) — captured.
|
|
1001
898
|
|
|
1002
|
-
|
|
899
|
+
No portable ambient capabilities. Return JSON; reduce large results before truncation. Build arguments from required input keys and schemas, never descriptions or output keys. Fetch skills({ name: "usage" }) only when this is insufficient or repair is needed; it has full rules, examples${connectorGuides ? ", guide handling" : ""}, and runtime details.`;
|
|
1003
900
|
/** Register the execute_code meta-tool. Only called when an executor is configured. */
|
|
1004
901
|
export function registerExecuteTool(server, registry, ctx) {
|
|
1005
902
|
// Resolved once so the description and the collector cannot disagree about
|
package/dist/index.d.ts
CHANGED
|
@@ -132,7 +132,7 @@ export interface ConnectaConfig {
|
|
|
132
132
|
credentials?: ConnectaCredentialsConfig;
|
|
133
133
|
/**
|
|
134
134
|
* Named, revocable Bearer tokens for MCP clients. Creation and mutation
|
|
135
|
-
* require an eligible
|
|
135
|
+
* require an eligible interactive operator; token secrets are returned once.
|
|
136
136
|
*/
|
|
137
137
|
accessTokens?: ConnectaAccessTokensConfig;
|
|
138
138
|
/** Tool-catalog caching, persistence, stale fallback, and probe deadlines. */
|
|
@@ -192,6 +192,6 @@ export { CONNECTA_VERSION } from "./version.js";
|
|
|
192
192
|
export type { Registry } from "./registry.js";
|
|
193
193
|
export type { RemoteMcpOptions, RemoteMcpAuth, RemoteMcpRedirectPolicy, } from "./connectors/remote-mcp.js";
|
|
194
194
|
export type { ApiOptions, ApiTool } from "./connectors/api.js";
|
|
195
|
-
export type { CatalogDriftCounts, CatalogDriftReport, Connector, ConnectorCallAdmissionInput, ConnectorCallAdmissionPolicy, ConnectorCallAdmissionRule, ConnectorRollingWindowBudget, ConnectaBranding, ConnectorCredentialAccess, ConnectorCredentialConfig, ConnectorCredentialFieldConfig, ConnectorCredentialValues, ConnectorContext, ConnectorUsageGuide, ConnectorStatus, CredentialTestResult, AdmittingExecutor, AdmissionSnapshot, ExecuteResult, Executor, ExecutorLease, ExecutorProvider, InboundAuth, UiAuthConfig, AuthResult, JsonSchema, KVStorage, Logger, ToolDef, ToolAnnotations, } from "./types.js";
|
|
195
|
+
export type { CatalogDriftCounts, CatalogDriftReport, Connector, ConnectorCallAdmissionInput, ConnectorCallAdmissionPolicy, ConnectorCallAdmissionRule, ConnectorRollingWindowBudget, ConnectaBranding, ConnectorCredentialAccess, ConnectorCredentialConfig, ConnectorCredentialFieldConfig, ConnectorCredentialValues, ConnectorContext, ConnectorUsageGuide, ConnectorStatus, CredentialTestResult, AdmittingExecutor, AdmissionSnapshot, ExecuteResult, Executor, ExecutorLease, ExecutorProvider, InboundAuth, InboundAuthRuntimeContext, UiAuthConfig, AuthResult, JsonSchema, KVStorage, Logger, ToolDef, ToolAnnotations, } from "./types.js";
|
|
196
196
|
export type { ActivityActor, ActivityCallSource, ActivityOutcome, ActivityPage, ActivityReadActor, ActivityReadEvent, ActivityReader, ActivityReadGate, ActivityReadPage, ActivitySink, ActivityStore, AgentFriction, CatalogDriftActivityEvent, ToolCallActivityEvent, } from "./activity.js";
|
|
197
197
|
export { InvalidActivityCursorError } from "./activity.js";
|
package/dist/index.js
CHANGED
|
@@ -42,71 +42,116 @@ function normalizeAuth(auth) {
|
|
|
42
42
|
return rank(a) - rank(b);
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
45
|
+
const admissionPoolSchema = {
|
|
46
|
+
concurrency: null,
|
|
47
|
+
maxQueueSize: null,
|
|
48
|
+
queueTimeoutMs: null,
|
|
49
|
+
retryAfterMs: null,
|
|
50
|
+
};
|
|
51
|
+
const CONFIG_SCHEMA = {
|
|
52
|
+
connectors: null,
|
|
53
|
+
auth: null,
|
|
54
|
+
storage: null,
|
|
55
|
+
publicUrl: null,
|
|
56
|
+
activity: {
|
|
57
|
+
store: null,
|
|
58
|
+
readGate: null,
|
|
59
|
+
deploymentId: null,
|
|
60
|
+
},
|
|
61
|
+
credentials: {
|
|
62
|
+
encryptionKey: null,
|
|
63
|
+
},
|
|
64
|
+
accessTokens: {
|
|
65
|
+
maxActive: null,
|
|
66
|
+
},
|
|
67
|
+
discovery: {
|
|
68
|
+
concurrency: null,
|
|
69
|
+
catalogTtlSeconds: null,
|
|
70
|
+
persistCatalog: null,
|
|
71
|
+
staleCatalogSeconds: null,
|
|
72
|
+
probeTimeoutMs: null,
|
|
73
|
+
},
|
|
74
|
+
calls: {
|
|
75
|
+
defaultTimeoutMs: null,
|
|
76
|
+
maxResultBytes: null,
|
|
77
|
+
},
|
|
78
|
+
execute: {
|
|
79
|
+
maxEmittedBytes: null,
|
|
80
|
+
maxEmittedBlocks: null,
|
|
81
|
+
},
|
|
82
|
+
admission: {
|
|
83
|
+
requests: admissionPoolSchema,
|
|
84
|
+
code: admissionPoolSchema,
|
|
85
|
+
},
|
|
86
|
+
branding: {
|
|
87
|
+
productName: null,
|
|
88
|
+
productUrl: null,
|
|
89
|
+
ownerName: null,
|
|
90
|
+
ownerUrl: null,
|
|
91
|
+
description: null,
|
|
92
|
+
pageTitle: null,
|
|
93
|
+
favicon: {
|
|
94
|
+
svg: null,
|
|
95
|
+
ico: null,
|
|
96
|
+
href: null,
|
|
97
|
+
},
|
|
98
|
+
themeColor: null,
|
|
99
|
+
},
|
|
100
|
+
logger: null,
|
|
101
|
+
serverInfo: {
|
|
102
|
+
name: null,
|
|
103
|
+
version: null,
|
|
104
|
+
title: null,
|
|
105
|
+
websiteUrl: null,
|
|
106
|
+
icons: [
|
|
107
|
+
{
|
|
108
|
+
src: null,
|
|
109
|
+
mimeType: null,
|
|
110
|
+
sizes: null,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
deploymentInfo: null,
|
|
115
|
+
executor: null,
|
|
116
|
+
};
|
|
117
|
+
function unknownOptionPaths(value, path, schema) {
|
|
118
|
+
if (schema === null)
|
|
119
|
+
return [];
|
|
120
|
+
if (Array.isArray(schema)) {
|
|
121
|
+
if (!Array.isArray(value))
|
|
122
|
+
return [];
|
|
123
|
+
return value.flatMap((entry, index) => unknownOptionPaths(entry, `${path}[${index}]`, schema[0]));
|
|
101
124
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
125
|
+
if (typeof value !== "object" || value === null)
|
|
126
|
+
return [];
|
|
127
|
+
const known = new Set(Object.keys(schema));
|
|
128
|
+
const unknown = Reflect.ownKeys(value)
|
|
129
|
+
.filter((key) => typeof key !== "string" || !known.has(key))
|
|
130
|
+
.map((key) => `${path}.${String(key)}`)
|
|
131
|
+
.sort();
|
|
132
|
+
if (unknown.length > 0)
|
|
133
|
+
return unknown;
|
|
134
|
+
for (const [key, childSchema] of Object.entries(schema)) {
|
|
135
|
+
unknown.push(...unknownOptionPaths(value[key], `${path}.${key}`, childSchema));
|
|
105
136
|
}
|
|
106
|
-
|
|
137
|
+
return unknown;
|
|
138
|
+
}
|
|
139
|
+
function rejectUnknownOptions(paths) {
|
|
140
|
+
if (paths.length === 0)
|
|
107
141
|
return;
|
|
108
|
-
throw new Error(
|
|
109
|
-
|
|
142
|
+
throw new Error(`Unknown Connecta configuration option${paths.length === 1 ? "" : "s"}:\n` +
|
|
143
|
+
paths.map((path) => `- ${path}`).join("\n"));
|
|
144
|
+
}
|
|
145
|
+
/** Reject JavaScript typos and removed options before construction does work. */
|
|
146
|
+
function assertKnownConfig(config) {
|
|
147
|
+
rejectUnknownOptions(unknownOptionPaths(config, "ConnectaConfig", CONFIG_SCHEMA));
|
|
148
|
+
const activity = config.activity;
|
|
149
|
+
if (activity !== undefined &&
|
|
150
|
+
(typeof activity !== "object" ||
|
|
151
|
+
activity === null ||
|
|
152
|
+
typeof activity.store?.record !== "function")) {
|
|
153
|
+
throw new Error("ConnectaConfig.activity.store must implement record(event)");
|
|
154
|
+
}
|
|
110
155
|
}
|
|
111
156
|
/**
|
|
112
157
|
* One-time construction warnings for deployment shapes that run fine but are
|
|
@@ -193,11 +238,7 @@ function warnInsecureConfig(config, inboundAuth, logger) {
|
|
|
193
238
|
}
|
|
194
239
|
}
|
|
195
240
|
export function createConnecta(config) {
|
|
196
|
-
|
|
197
|
-
if (hasOwn(config, "surface")) {
|
|
198
|
-
throw new Error("ConnectaConfig.surface was removed in issue #273. Remove it; connecta " +
|
|
199
|
-
"now serves one seven-tool surface.");
|
|
200
|
-
}
|
|
241
|
+
assertKnownConfig(config);
|
|
201
242
|
if (!config.executor) {
|
|
202
243
|
throw new Error("ConnectaConfig.executor is required. Configure quickJsExecutor() from " +
|
|
203
244
|
'"@zackbart/connecta/quickjs" on Node, or ' +
|
|
@@ -221,8 +262,8 @@ export function createConnecta(config) {
|
|
|
221
262
|
? new AccessTokenManager(storage, config.accessTokens)
|
|
222
263
|
: undefined;
|
|
223
264
|
if (accessTokens &&
|
|
224
|
-
!configuredAuth.some((provider) => provider.
|
|
225
|
-
throw new Error("accessTokens requires
|
|
265
|
+
!configuredAuth.some((provider) => provider.interactiveOperator)) {
|
|
266
|
+
throw new Error("accessTokens requires an interactive operator auth provider: only an eligible " +
|
|
226
267
|
"operator may create, rename, or revoke deployment access tokens");
|
|
227
268
|
}
|
|
228
269
|
const serverInfo = {
|
package/dist/invocation.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export interface InvocationContext<T> {
|
|
|
57
57
|
requestSignal?: AbortSignal;
|
|
58
58
|
unwrapResult?: boolean;
|
|
59
59
|
/**
|
|
60
|
-
* Caller-owned result policy. MCP
|
|
60
|
+
* Caller-owned result policy. MCP applies result paging here; code mode
|
|
61
61
|
* normally accepts the already-unwrapped value unchanged.
|
|
62
62
|
*/
|
|
63
63
|
processResult?: (value: unknown, resolved: ResolvedCatalogTool) => T | Promise<T>;
|
package/dist/meta-tools.d.ts
CHANGED
|
@@ -46,7 +46,6 @@ type ResultMode = "mcp" | "value";
|
|
|
46
46
|
export interface CallArgs {
|
|
47
47
|
address: string;
|
|
48
48
|
args?: Record<string, unknown>;
|
|
49
|
-
fields?: string[];
|
|
50
49
|
resultMode?: ResultMode;
|
|
51
50
|
timeoutMs?: number;
|
|
52
51
|
/** Retries after the first attempt; honored only for safely annotated tools. */
|