@zackbart/connecta 0.33.0 → 0.34.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 +35 -0
- package/README.md +99 -128
- package/SECURITY.md +1 -1
- package/bin/version.mjs +1 -1
- package/dist/activity.d.ts +3 -0
- package/dist/branding.d.ts +7 -0
- package/dist/branding.js +76 -9
- package/dist/catalog-service.js +15 -1
- package/dist/config-value-sources.js +6 -1
- package/dist/config.d.ts +13 -2
- package/dist/config.js +36 -2
- package/dist/connector-caller.js +2 -1
- package/dist/describe-config.d.ts +3 -1
- package/dist/describe-config.js +1 -0
- package/dist/execute.js +1 -11
- package/dist/fix-prompt.js +2 -2
- package/dist/index.js +1 -0
- package/dist/membership.d.ts +2 -0
- package/dist/oauth-callback-outcome.js +2 -2
- package/dist/operator-ui/agents.d.ts +1 -1
- package/dist/operator-ui/agents.js +16 -16
- package/dist/operator-ui/config-summary.js +562 -0
- package/dist/operator-ui/connection-map.js +375 -0
- package/dist/operator-ui/endpoints.js +44 -15
- package/dist/operator-ui/fix-prompts.js +1 -1
- package/dist/operator-ui/generated.js +4 -4
- package/dist/operator-ui/member-page.js +13 -28
- package/dist/operator-ui/overview.js +134 -0
- package/dist/operator-ui/view.d.ts +27 -22
- package/dist/operator-ui/view.js +104 -88
- package/dist/page-styles.js +5 -3
- package/dist/providers/_shared/google/workspace.d.ts +6 -4
- package/dist/providers/_shared/google/workspace.js +6 -2
- package/dist/providers/ccb/index.js +1 -1
- package/dist/providers/docs/index.js +1 -1
- package/dist/providers/docs/skill.generated.js +2 -2
- package/dist/providers/drive/index.js +1 -1
- package/dist/providers/drive/skill.generated.js +2 -2
- package/dist/providers/forms/index.js +1 -1
- package/dist/providers/forms/skill.generated.js +2 -2
- package/dist/providers/gmail/index.js +1 -1
- package/dist/providers/gmail/skill.generated.js +2 -2
- package/dist/providers/mixpanel/skill.generated.js +3 -3
- package/dist/providers/notion/skill.generated.js +1 -1
- package/dist/providers/overflow/index.js +1 -1
- package/dist/providers/overflow/skill.generated.js +2 -2
- package/dist/providers/planning-center/index.js +1 -1
- package/dist/providers/planning-center/skill.generated.js +4 -4
- package/dist/providers/revenuecat/skill.generated.js +2 -2
- package/dist/providers/sheets/index.js +1 -1
- package/dist/providers/sheets/skill.generated.js +2 -2
- package/dist/providers/slides/index.js +1 -1
- package/dist/providers/slides/skill.generated.js +2 -2
- package/dist/providers/tithely/index.js +1 -1
- package/dist/providers/vercel/index.js +1 -1
- package/dist/providers/vercel/skill.generated.js +1 -1
- package/dist/registry.js +15 -0
- package/dist/routes/mcp.js +20 -13
- package/dist/routes/oauth.js +2 -2
- package/dist/skills.js +10 -0
- package/dist/storage/sql.js +5 -1
- package/dist/types.d.ts +18 -7
- package/dist/ui.js +9 -4
- package/dist/usage-guide.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/README.md +2 -2
- package/documentation/architecture.md +18 -13
- package/documentation/auth.md +183 -67
- package/documentation/code-mode.md +23 -5
- package/documentation/deploying.md +5 -1
- package/documentation/integrating.md +9 -6
- package/documentation/meta-tools.md +36 -22
- package/documentation/migration-0.33.md +21 -13
- package/documentation/migration-0.34.md +67 -0
- package/documentation/operator-tests.md +46 -20
- package/documentation/operator-ui.md +294 -104
- package/examples/worker/AGENTS.md +15 -9
- package/examples/worker/README.md +41 -14
- package/examples/worker/src/connecta.config.ts +5 -4
- package/examples/worker/wrangler.jsonc +1 -1
- package/package.json +19 -19
- package/templates/node/AGENTS.md +13 -7
- package/templates/node/README.md +14 -11
- package/templates/node/package.json +1 -1
- package/templates/node/src/connecta.config.ts +2 -2
package/dist/config.js
CHANGED
|
@@ -184,6 +184,31 @@ function assertFlag(value, path) {
|
|
|
184
184
|
if (typeof value !== "boolean")
|
|
185
185
|
throw new ConfigError(`${path} must be true or false.`);
|
|
186
186
|
}
|
|
187
|
+
/** Longest pool `description`, in characters, after trimming. */
|
|
188
|
+
const MAX_POOL_DESCRIPTION_LENGTH = 500;
|
|
189
|
+
/**
|
|
190
|
+
* A pool description is one line of prose for the model. C0 and C1 controls
|
|
191
|
+
* (including NEL), plus Unicode line and paragraph separators, are refused.
|
|
192
|
+
* A scan, as in remote-mcp.ts,
|
|
193
|
+
* because a character class over the control range trips `no-control-regex`.
|
|
194
|
+
*/
|
|
195
|
+
function hasLineBreakOrControlCharacter(value) {
|
|
196
|
+
for (let index = 0; index < value.length; index++) {
|
|
197
|
+
const code = value.charCodeAt(index);
|
|
198
|
+
if (code <= 0x1f || (code >= 0x7f && code <= 0x9f) || code === 0x2028 || code === 0x2029)
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
function assertPoolDescription(value, path) {
|
|
204
|
+
const trimmed = typeof value === "string" ? value.trim() : undefined;
|
|
205
|
+
if (trimmed === undefined ||
|
|
206
|
+
trimmed.length === 0 ||
|
|
207
|
+
[...trimmed].length > MAX_POOL_DESCRIPTION_LENGTH ||
|
|
208
|
+
hasLineBreakOrControlCharacter(trimmed)) {
|
|
209
|
+
throw new ConfigError(`${path} must be text of 1 to ${MAX_POOL_DESCRIPTION_LENGTH} characters after trimming, with no line breaks or control characters.`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
187
212
|
/** Shape only; each reference resolves against auth and agents in createConnecta. */
|
|
188
213
|
function assertMemberList(value, path) {
|
|
189
214
|
if (!Array.isArray(value)) {
|
|
@@ -217,8 +242,9 @@ const pool = {
|
|
|
217
242
|
* The Google Workspace address agents in this pool act as, on a Workspace
|
|
218
243
|
* connector whose `subject` is a mapping function. Humans always act as
|
|
219
244
|
* themselves through that mapping, and a fixed `subject` address applies to
|
|
220
|
-
* everyone. Without an owner,
|
|
221
|
-
*
|
|
245
|
+
* everyone. Without an owner, a mapping-subject Workspace connector is
|
|
246
|
+
* absent for this pool's agents, as a personal connector is; people in the
|
|
247
|
+
* pool still see it.
|
|
222
248
|
*/
|
|
223
249
|
owner: opaque({
|
|
224
250
|
check: (value, path) => {
|
|
@@ -227,6 +253,14 @@ const pool = {
|
|
|
227
253
|
}
|
|
228
254
|
},
|
|
229
255
|
}),
|
|
256
|
+
/**
|
|
257
|
+
* What this pool is for, in plain words for the model: appended to the MCP
|
|
258
|
+
* `initialize` instructions at `/mcp/<name>` and `/agent/<name>`, so a
|
|
259
|
+
* caller learns its scope ("Team tier: shared tools only."). Trimmed, 1 to
|
|
260
|
+
* 500 characters, no line breaks or control characters. It grants and hides nothing; plain
|
|
261
|
+
* `/mcp` never shows it.
|
|
262
|
+
*/
|
|
263
|
+
description: opaque({ check: assertPoolDescription }),
|
|
230
264
|
};
|
|
231
265
|
const icon = {
|
|
232
266
|
src: required(text()),
|
package/dist/connector-caller.js
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
// Workspace through domain-wide delegation (`src/providers/_shared/google/`) — which
|
|
10
10
|
// hand the identity to a deployment-config function that names the
|
|
11
11
|
// downstream subject (PRINCIPLES.md INV-3, #678), or, for an agent, use the
|
|
12
|
-
// admitted pool's configured owner (decisions/0006)
|
|
12
|
+
// admitted pool's configured owner (decisions/0006), and the scoped registry
|
|
13
|
+
// view hides the connector from an agent whose pool has none. The provider never
|
|
13
14
|
// decides whose account to open; config does, from this.
|
|
14
15
|
//
|
|
15
16
|
// Core sets it from the authorization that admitted the request, on the scoped
|
|
@@ -115,7 +115,8 @@ export interface ConnectaConfigDescription {
|
|
|
115
115
|
/**
|
|
116
116
|
* `readOnly` is resolved: the pool's own value, else the deployment's.
|
|
117
117
|
* `members` are resolved references in declaration order. `owner` is the
|
|
118
|
-
* Workspace address agents act as, or null.
|
|
118
|
+
* Workspace address agents act as, or null. `description` is the trimmed
|
|
119
|
+
* text the pool's endpoints add to their MCP instructions, or null.
|
|
119
120
|
*/
|
|
120
121
|
pools: Array<{
|
|
121
122
|
name: string;
|
|
@@ -124,6 +125,7 @@ export interface ConnectaConfigDescription {
|
|
|
124
125
|
members: DescribedMember[];
|
|
125
126
|
readOnly: boolean;
|
|
126
127
|
owner: string | null;
|
|
128
|
+
description: string | null;
|
|
127
129
|
}>;
|
|
128
130
|
modules: {
|
|
129
131
|
ui: {
|
package/dist/describe-config.js
CHANGED
|
@@ -250,6 +250,7 @@ export function describeConfig(input) {
|
|
|
250
250
|
? { kind: "agent", name: member.name }
|
|
251
251
|
: { kind: "human", namespace: member.namespace, id: member.id }),
|
|
252
252
|
owner: input.pools.get(name)?.owner ?? null,
|
|
253
|
+
description: input.pools.get(name)?.description ?? null,
|
|
253
254
|
})),
|
|
254
255
|
modules: {
|
|
255
256
|
ui: { enabled: config.ui !== undefined },
|
package/dist/execute.js
CHANGED
|
@@ -798,18 +798,8 @@ function sandboxProvider(registry, baseUrl, activity, limits, requestScope = {})
|
|
|
798
798
|
...args,
|
|
799
799
|
includeSchemas: args.includeSchemas ?? "json",
|
|
800
800
|
includeSchemaKeys: args.includeSchemaKeys !== false,
|
|
801
|
-
}));
|
|
801
|
+
}, catalogDependency));
|
|
802
802
|
boundedDiscoveryText(result, "Request a smaller limit, omit fullDescriptions, use compact schemas, or pass includeSchemaKeys: false.");
|
|
803
|
-
for (const tool of result.tools)
|
|
804
|
-
catalogAddressDependency(tool.address);
|
|
805
|
-
for (const error of result.catalogErrors)
|
|
806
|
-
catalogDependency(error.connector);
|
|
807
|
-
for (const connector of result.absence?.configuredConnectors ?? [])
|
|
808
|
-
catalogDependency(connector);
|
|
809
|
-
for (const connector of result.queryAnalysis?.configuredConnectors ?? [])
|
|
810
|
-
catalogDependency(connector);
|
|
811
|
-
if (result.queryAnalysis?.connectorScope)
|
|
812
|
-
catalogDependency(result.queryAnalysis.connectorScope);
|
|
813
803
|
return result;
|
|
814
804
|
}),
|
|
815
805
|
describe: (raw) => discovery("describe", async () => {
|
package/dist/fix-prompt.js
CHANGED
|
@@ -29,7 +29,7 @@ export function renderFixPrompt(spec, connectorId) {
|
|
|
29
29
|
"own logs (lines prefixed [connecta]) or by reproducing the failure locally. Never put a secret in source, " +
|
|
30
30
|
"a log line, or your reply: secrets belong in the deployment's environment or its credential vault.",
|
|
31
31
|
"Make the smallest change that fixes it, then verify it by redeploying and refreshing the connection on the " +
|
|
32
|
-
"operator
|
|
33
|
-
"a secret only the operator holds
|
|
32
|
+
"operator Connectors page. If the fix needs a step you cannot do yourself, such as a provider console setting " +
|
|
33
|
+
"or a secret only the operator holds, name that exact step instead.",
|
|
34
34
|
].join("\n\n");
|
|
35
35
|
}
|
package/dist/index.js
CHANGED
|
@@ -67,6 +67,7 @@ function resolvePools(pools, readOnly, registry, members) {
|
|
|
67
67
|
readOnly: pool.readOnly ?? readOnly,
|
|
68
68
|
members: Object.freeze(resolveMembers(pool.members, path, members)),
|
|
69
69
|
...(pool.owner !== undefined ? { owner: pool.owner.trim() } : {}),
|
|
70
|
+
...(pool.description !== undefined ? { description: pool.description.trim() } : {}),
|
|
70
71
|
}));
|
|
71
72
|
}
|
|
72
73
|
return resolved;
|
package/dist/membership.d.ts
CHANGED
|
@@ -25,6 +25,8 @@ export interface ResolvedPool {
|
|
|
25
25
|
members: readonly Member[];
|
|
26
26
|
/** Trimmed `owner`: the Workspace address agents in this pool act as. */
|
|
27
27
|
owner?: string;
|
|
28
|
+
/** Trimmed `description`, appended to this pool's MCP instructions. */
|
|
29
|
+
description?: string;
|
|
28
30
|
}
|
|
29
31
|
/** What resolving a reference needs to know about the deployment. */
|
|
30
32
|
export interface MemberContext {
|
|
@@ -11,7 +11,7 @@ const OUTCOMES = {
|
|
|
11
11
|
status: 400,
|
|
12
12
|
tone: "declined",
|
|
13
13
|
heading: "Authorization was declined",
|
|
14
|
-
message: "Consent was declined
|
|
14
|
+
message: "Consent was declined, so nothing was stored. Start again when ready.",
|
|
15
15
|
fix: {
|
|
16
16
|
problem: "Consent for a downstream OAuth connector was declined at the provider, so no grant was stored.",
|
|
17
17
|
steps: [
|
|
@@ -72,7 +72,7 @@ const OUTCOMES = {
|
|
|
72
72
|
fix: {
|
|
73
73
|
problem: "A provider returned an authorization code, but exchanging it for tokens failed.",
|
|
74
74
|
steps: [
|
|
75
|
-
"Check the OAuth client
|
|
75
|
+
"Check the OAuth client id and secret the connector reads from the deployment's environment, and that they belong to the OAuth app that showed the consent screen.",
|
|
76
76
|
"Check that the token endpoint and redirect URI the connector uses match the provider's registration exactly.",
|
|
77
77
|
"Find the [connecta] warning for this callback in the deployment logs; it records the exchange error the page withholds.",
|
|
78
78
|
],
|
|
@@ -62,7 +62,7 @@ export declare function slotBadge(slot: Pick<AgentSlot, "state">): {
|
|
|
62
62
|
label: string;
|
|
63
63
|
tone: Tone;
|
|
64
64
|
};
|
|
65
|
-
/** When the slot last changed,
|
|
65
|
+
/** When the slot last changed, issued or revoked (its badge says which); a dash when it never has. */
|
|
66
66
|
export declare function slotSince(slot: AgentSlot, format: (value: string) => string): string;
|
|
67
67
|
export declare function lastSeenLabel(lastSeenAt: string | null, format: (value: string) => string): string;
|
|
68
68
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { failure } from "./view.js";
|
|
2
2
|
/** Why a row's controls are disabled when its slot is not in the live list. */
|
|
3
|
-
const SLOT_UNAVAILABLE = "Token state
|
|
3
|
+
const SLOT_UNAVAILABLE = "Token state unknown. Refresh.";
|
|
4
4
|
/** Why a row's controls are disabled until a fresh list read resolves its slot. */
|
|
5
5
|
const SLOT_UNRESOLVED = "Refresh to continue";
|
|
6
6
|
export function slotActions(slot) {
|
|
@@ -53,12 +53,12 @@ export function slotBadge(slot) {
|
|
|
53
53
|
return { label: "Revoked", tone: "warn" };
|
|
54
54
|
return { label: "No token", tone: "neutral" };
|
|
55
55
|
}
|
|
56
|
-
/** When the slot last changed,
|
|
56
|
+
/** When the slot last changed, issued or revoked (its badge says which); a dash when it never has. */
|
|
57
57
|
export function slotSince(slot, format) {
|
|
58
58
|
if (slot.state === "issued")
|
|
59
|
-
return
|
|
59
|
+
return format(slot.issuedAt);
|
|
60
60
|
if (slot.state === "revoked")
|
|
61
|
-
return
|
|
61
|
+
return format(slot.revokedAt);
|
|
62
62
|
return "—";
|
|
63
63
|
}
|
|
64
64
|
export function lastSeenLabel(lastSeenAt, format) {
|
|
@@ -72,33 +72,33 @@ export function lastSeenLabel(lastSeenAt, format) {
|
|
|
72
72
|
*/
|
|
73
73
|
export function tokenFailureNotice(action, name, facts, productName) {
|
|
74
74
|
if (facts.kind === "session")
|
|
75
|
-
return failure("
|
|
75
|
+
return failure("Session ended. Sign in again.");
|
|
76
76
|
if (facts.kind === "forbidden")
|
|
77
|
-
return failure("You
|
|
77
|
+
return failure("You can't manage agent tokens.");
|
|
78
78
|
if (facts.kind === "stale") {
|
|
79
|
-
return failure(`${name}'s token changed
|
|
79
|
+
return failure(`${name}'s token changed, so nothing was sent. Try again.`);
|
|
80
80
|
}
|
|
81
81
|
if (facts.kind === "network") {
|
|
82
|
-
return failure(`Couldn't reach ${productName}. Refresh to check ${name}'s token
|
|
82
|
+
return failure(`Couldn't reach ${productName}. Refresh to check ${name}'s token.`);
|
|
83
83
|
}
|
|
84
84
|
if (facts.status === 503) {
|
|
85
85
|
return failure(action === "revoke"
|
|
86
|
-
? `
|
|
87
|
-
: `${action === "issue" ? "Issue" : "
|
|
86
|
+
? `Revoke result unknown for ${name}. Revoke again to be sure.`
|
|
87
|
+
: `${action === "issue" ? "Issue" : "Rotate"} result unknown for ${name}. No secret came back, so rotate again.`);
|
|
88
88
|
}
|
|
89
89
|
if (facts.status === 499) {
|
|
90
90
|
return failure(action === "revoke"
|
|
91
|
-
? `The request ended before ${name}'s
|
|
92
|
-
: `The request ended before ${name}'s token
|
|
91
|
+
? `The request ended before ${name}'s revoke finished. Refresh to check its token.`
|
|
92
|
+
: `The request ended before ${name}'s token arrived. Refresh to check its token.`);
|
|
93
93
|
}
|
|
94
94
|
// The route's compare-and-swap refused it: the slot moved after the list was read.
|
|
95
95
|
if (facts.status === 409 && facts.code === "slot_changed")
|
|
96
|
-
return failure("Changed since you looked
|
|
96
|
+
return failure("Changed since you looked. Refresh.");
|
|
97
97
|
if (facts.status === 409)
|
|
98
|
-
return failure(`${name}'s token changed
|
|
98
|
+
return failure(`${name}'s token changed. Reload and try again.`);
|
|
99
99
|
if (facts.status === 404)
|
|
100
100
|
return failure(`${name} is no longer declared in code. Reload the page.`);
|
|
101
101
|
return action === "revoke"
|
|
102
|
-
? failure(
|
|
103
|
-
: failure(`
|
|
102
|
+
? failure(`Couldn't revoke ${name}'s token. Reload and try again.`)
|
|
103
|
+
: failure(`Couldn't issue a token for ${name}. Reload and try again.`);
|
|
104
104
|
}
|