@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
|
@@ -35,23 +35,17 @@ function controlsFor(manage, connection) {
|
|
|
35
35
|
}
|
|
36
36
|
return [];
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* A few words under the account's name, only where the badge alone would
|
|
40
|
+
* leave something out: a saved credential that no longer fits, or one saved
|
|
41
|
+
* but not working.
|
|
42
|
+
*/
|
|
38
43
|
function summaryFor(manage, connection) {
|
|
39
|
-
if (manage.kind
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return "Your saved credential no longer fits. Replace it.";
|
|
45
|
-
return connection.state === "ok" ? "Your credential is saved." : "Your credential is saved, but it isn't working.";
|
|
46
|
-
}
|
|
47
|
-
if (connection.state === "ok")
|
|
48
|
-
return "Connected with your account.";
|
|
49
|
-
if (connection.state === "auth_required") {
|
|
50
|
-
return manage.kind === "oauth" && manage.actions.includes("connect")
|
|
51
|
-
? "Not connected. Its tools won't work for you until you connect."
|
|
52
|
-
: "Not connected.";
|
|
53
|
-
}
|
|
54
|
-
return "Unavailable right now.";
|
|
44
|
+
if (manage.kind !== "credential" || !credentialStored(manage))
|
|
45
|
+
return null;
|
|
46
|
+
if (connection.problem === "credential_mismatch")
|
|
47
|
+
return "Saved credential no longer fits";
|
|
48
|
+
return connection.state === "ok" ? null : "Saved, but not working";
|
|
55
49
|
}
|
|
56
50
|
const toolCount = (connector) => connector.tools.length;
|
|
57
51
|
export function memberPage(contract) {
|
|
@@ -94,19 +88,10 @@ export function toolCountLabel(count) {
|
|
|
94
88
|
/** The in-page question before a member's action that takes something away. */
|
|
95
89
|
export function memberConfirmCopy(action, name) {
|
|
96
90
|
if (action === "oauth_restart") {
|
|
97
|
-
return {
|
|
98
|
-
question: `Reconnect ${name}? Your current connection stops working until you finish authorizing in the new tab.`,
|
|
99
|
-
confirm: "Reconnect",
|
|
100
|
-
};
|
|
91
|
+
return { question: `Reconnect ${name}? It stops working until you finish in the new tab.`, confirm: "Reconnect" };
|
|
101
92
|
}
|
|
102
93
|
if (action === "oauth_disconnect") {
|
|
103
|
-
return {
|
|
104
|
-
question: `Disconnect your ${name} account? Its tools stop working for you until you connect again.`,
|
|
105
|
-
confirm: "Disconnect",
|
|
106
|
-
};
|
|
94
|
+
return { question: `Disconnect your ${name} account? Its tools stop working for you.`, confirm: "Disconnect" };
|
|
107
95
|
}
|
|
108
|
-
return {
|
|
109
|
-
question: `Clear your ${name} credential? Its tools stop working for you until you add one again.`,
|
|
110
|
-
confirm: "Clear",
|
|
111
|
-
};
|
|
96
|
+
return { question: `Clear your ${name} credential? Its tools stop working for you.`, confirm: "Clear" };
|
|
112
97
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { endpointRows } from "./endpoints.js";
|
|
2
|
+
import { joinList } from "./connection-map.js";
|
|
3
|
+
import { connectorVerdict } from "./view.js";
|
|
4
|
+
const PROBLEM_TITLE = {
|
|
5
|
+
connector_unavailable: "is unavailable",
|
|
6
|
+
oauth_required: "needs authorization",
|
|
7
|
+
credential_required: "needs a credential",
|
|
8
|
+
auth_required: "needs a secret in the deployment",
|
|
9
|
+
credential_mismatch: "can't use its saved credential",
|
|
10
|
+
catalog_failed: "couldn't load its tools",
|
|
11
|
+
};
|
|
12
|
+
function affects(paths) {
|
|
13
|
+
return paths.length ? `Affects ${joinList(paths)}` : "No endpoint serves it";
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* One row per connector that is not healthy, failing first, then one per
|
|
17
|
+
* agent that sits in an endpoint without a working token. Each row carries
|
|
18
|
+
* the fix the rest of the UI already offers for it.
|
|
19
|
+
*/
|
|
20
|
+
export function attentionItems(contract, options = {}) {
|
|
21
|
+
if (contract.role !== "maintainer")
|
|
22
|
+
return [];
|
|
23
|
+
const rows = endpointRows(contract);
|
|
24
|
+
const connectors = [];
|
|
25
|
+
for (const config of contract.config?.connectors ?? []) {
|
|
26
|
+
const live = contract.live.connectors.find((c) => c.id === config.id);
|
|
27
|
+
if (!live)
|
|
28
|
+
continue;
|
|
29
|
+
const signals = options.signals?.[config.id] ?? {};
|
|
30
|
+
const verdict = connectorVerdict(live, signals);
|
|
31
|
+
if ((verdict.tone !== "warn" && verdict.tone !== "danger") || !verdict.action)
|
|
32
|
+
continue;
|
|
33
|
+
const title = config.title ?? config.id;
|
|
34
|
+
const id = encodeURIComponent(config.id);
|
|
35
|
+
const paths = rows.filter((row) => row.groups.some((group) => group.config.id === config.id)).map((r) => r.path);
|
|
36
|
+
const connection = `/connectors/${id}#connection`;
|
|
37
|
+
const base = {
|
|
38
|
+
key: `connector:${config.id}`,
|
|
39
|
+
kind: "connector",
|
|
40
|
+
tone: verdict.tone,
|
|
41
|
+
subject: title,
|
|
42
|
+
action: { label: verdict.action, href: connection, ariaLabel: `${verdict.action} ${title}` },
|
|
43
|
+
href: connection,
|
|
44
|
+
};
|
|
45
|
+
// The server's problem code outranks a failed read on this page: the row
|
|
46
|
+
// keeps its fix prompt and says the details didn't load. "Unavailable"
|
|
47
|
+
// beside a failed read is the page's own (see connectorVerdict).
|
|
48
|
+
const reported = live.problem ?? (live.status === "error" ? "connector_unavailable" : undefined);
|
|
49
|
+
const problem = signals.failed && reported === "connector_unavailable" ? undefined : reported;
|
|
50
|
+
if (problem) {
|
|
51
|
+
connectors.push({
|
|
52
|
+
...base,
|
|
53
|
+
problem: PROBLEM_TITLE[problem],
|
|
54
|
+
body: signals.failed ? `Details didn't load · ${affects(paths)}` : affects(paths),
|
|
55
|
+
fix: { kind: problem, connectorId: config.id },
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
else if (signals.failed) {
|
|
59
|
+
connectors.push({
|
|
60
|
+
...base,
|
|
61
|
+
problem: "didn't load on this page",
|
|
62
|
+
body: affects(paths),
|
|
63
|
+
fix: null,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
else if (live.status !== "ok") {
|
|
67
|
+
connectors.push({
|
|
68
|
+
...base,
|
|
69
|
+
problem: verdict.label === "Authorization needed" ? "needs authorization" : "isn't connected",
|
|
70
|
+
body: affects(paths),
|
|
71
|
+
fix: null,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Healthy but drifted: the live tool list differs from the reviewed one.
|
|
76
|
+
connectors.push({
|
|
77
|
+
...base,
|
|
78
|
+
problem: "has a changed tool list",
|
|
79
|
+
body: affects(paths),
|
|
80
|
+
fix: { kind: "catalog_drift", connectorId: config.id },
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const agents = contract.agents
|
|
85
|
+
.filter((agent) => agent.pools.length && agent.token.state !== "issued")
|
|
86
|
+
.map((agent) => {
|
|
87
|
+
// An agent connects only at a pool's agent URL.
|
|
88
|
+
const paths = agent.pools.map((name) => contract.pools.find((pool) => pool.name === name)?.agentPath ?? `/agent/${name}`);
|
|
89
|
+
const revoked = agent.token.state === "revoked";
|
|
90
|
+
return {
|
|
91
|
+
key: `agent:${agent.name}`,
|
|
92
|
+
kind: "agent",
|
|
93
|
+
tone: "warn",
|
|
94
|
+
subject: agent.name,
|
|
95
|
+
problem: revoked ? "has a revoked token" : "has no token",
|
|
96
|
+
body: `Can't connect at ${joinList(paths)}`,
|
|
97
|
+
action: options.tokensPage
|
|
98
|
+
? { label: "Issue token", href: "/tokens", ariaLabel: `Issue a token for ${agent.name}` }
|
|
99
|
+
: null,
|
|
100
|
+
href: options.tokensPage ? "/tokens" : null,
|
|
101
|
+
fix: null,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
return [
|
|
105
|
+
...connectors.filter((item) => item.tone === "danger"),
|
|
106
|
+
...connectors.filter((item) => item.tone === "warn"),
|
|
107
|
+
...agents,
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
/** The numbers above the list, counted from the same map the page draws. */
|
|
111
|
+
export function overviewHeadline(contract, map, items, signals = {}) {
|
|
112
|
+
const bar = (contract.config?.connectors ?? []).flatMap((config) => {
|
|
113
|
+
const live = contract.live.connectors.find((c) => c.id === config.id);
|
|
114
|
+
if (!live)
|
|
115
|
+
return [];
|
|
116
|
+
const health = connectorVerdict(live, signals[config.id]);
|
|
117
|
+
return [{ id: config.id, title: config.title ?? config.id, tone: health.tone, label: health.label }];
|
|
118
|
+
});
|
|
119
|
+
const healthy = bar.filter((entry) => entry.tone === "ok").length;
|
|
120
|
+
const warn = bar.filter((entry) => entry.tone === "warn").length;
|
|
121
|
+
const danger = bar.filter((entry) => entry.tone === "danger").length;
|
|
122
|
+
const count = (kind) => map.nodes.filter((node) => node.kind === kind).length;
|
|
123
|
+
return {
|
|
124
|
+
connectors: bar.length,
|
|
125
|
+
healthy,
|
|
126
|
+
warn,
|
|
127
|
+
danger,
|
|
128
|
+
bar,
|
|
129
|
+
endpoints: count("endpoint"),
|
|
130
|
+
readOnlyEndpoints: map.nodes.filter((node) => node.kind === "endpoint" && node.readOnly).length,
|
|
131
|
+
people: count("person"),
|
|
132
|
+
agents: contract.agents.length,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -4,16 +4,7 @@ import type { CredentialManagementCapability, UiConnector, UiData, UiProblem } f
|
|
|
4
4
|
import type { FixPromptKind } from "./fix-prompts.js";
|
|
5
5
|
import type { AgentSlot } from "./agents.js";
|
|
6
6
|
export declare function registrationPathLabel(path: NonNullable<UiConnector["registrationPath"]>): string;
|
|
7
|
-
|
|
8
|
-
* A connector's source column: the maintained provider with the
|
|
9
|
-
* implementation it uses, since a dual provider's `auth` selects either the
|
|
10
|
-
* vendor's hosted MCP server or Connecta's REST connector.
|
|
11
|
-
*/
|
|
12
|
-
export declare function connectorSourceLabel(source: {
|
|
13
|
-
kind: string;
|
|
14
|
-
provider?: string;
|
|
15
|
-
}): string;
|
|
16
|
-
export type OperatorPage = "endpoints" | "settings" | "connector" | "tokens" | "connections" | "activity";
|
|
7
|
+
export type OperatorPage = "overview" | "endpoints" | "settings" | "connector" | "tokens" | "connections" | "activity";
|
|
17
8
|
/** Pages the nav lists, in dashboard order. */
|
|
18
9
|
export declare const OPERATOR_PAGES: readonly OperatorPage[];
|
|
19
10
|
export declare const PAGE_META: Readonly<Record<OperatorPage, {
|
|
@@ -134,7 +125,7 @@ export declare function credentialTestNotice(connectorId: string, answer: {
|
|
|
134
125
|
*/
|
|
135
126
|
export declare function refusedNotice(action: DownstreamAction, connectorId: string, problem?: unknown): Notice;
|
|
136
127
|
/** An action whose permission or target changed before it was sent. */
|
|
137
|
-
export declare const STALE_COPY = "This changed before
|
|
128
|
+
export declare const STALE_COPY = "This changed before sending, so nothing was sent. Try again.";
|
|
138
129
|
/**
|
|
139
130
|
* Every row action's failure, in one voice. The page's own failures — a
|
|
140
131
|
* lapsed session, a denied identity, a dropped connection — read the same for
|
|
@@ -327,12 +318,31 @@ export declare function problemCopy(problem: UiProblem | undefined): string | nu
|
|
|
327
318
|
* error. The two used to share the red box, so the row contradicted itself.
|
|
328
319
|
*/
|
|
329
320
|
export declare function problemTone(problem: UiProblem): "warn" | "danger";
|
|
321
|
+
/** What the page knows about a connector beyond its live status: catalog drift and a failed detail read. */
|
|
322
|
+
export interface ConnectorSignals {
|
|
323
|
+
drift?: boolean;
|
|
324
|
+
failed?: boolean;
|
|
325
|
+
}
|
|
326
|
+
/** One connector's health everywhere it is shown: badge tone, status word, and the one action beside it. */
|
|
327
|
+
export interface ConnectorVerdict {
|
|
328
|
+
tone: "ok" | "warn" | "danger" | "neutral";
|
|
329
|
+
label: string;
|
|
330
|
+
/** The link to the Connection tab, or null when the connector is fine. */
|
|
331
|
+
action: "Connect" | "Replace key" | "Inspect" | "Review" | null;
|
|
332
|
+
}
|
|
330
333
|
/**
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
+
* The verdict the Connectors list, the Overview, Endpoints and a connector's
|
|
335
|
+
* page all show. The server's problem code comes first, then a failed detail
|
|
336
|
+
* read on this page, then a non-ok status, then catalog drift. A problem
|
|
337
|
+
* outranks the status, so a connector whose catalog failed is never
|
|
338
|
+
* "Connected". A failed read marks its connector `connector_unavailable` in
|
|
339
|
+
* the store, so that one code beside a failed read is the page's own and
|
|
340
|
+
* reads "Couldn't load" instead of blaming the downstream.
|
|
334
341
|
*/
|
|
335
|
-
export declare function
|
|
342
|
+
export declare function connectorVerdict(live: {
|
|
343
|
+
status: string;
|
|
344
|
+
problem?: UiProblem;
|
|
345
|
+
}, signals?: ConnectorSignals): ConnectorVerdict;
|
|
336
346
|
/**
|
|
337
347
|
* Hosted-provider catalog drift, as an operator reads it
|
|
338
348
|
* ([#343](https://github.com/zackbart/connecta/issues/343)).
|
|
@@ -378,6 +388,8 @@ export declare function activityOutcomeBadge(outcome: string): {
|
|
|
378
388
|
label: string;
|
|
379
389
|
tone: Tone;
|
|
380
390
|
};
|
|
391
|
+
/** Why a call stalled or failed, in words; "" when nothing was recorded. */
|
|
392
|
+
export declare function activityReason(event: UiActivityEvent): string;
|
|
381
393
|
/** The one-line detail under an address: where it ran, retries, and why it stalled. */
|
|
382
394
|
export declare function activityDetail(event: UiActivityEvent): string;
|
|
383
395
|
/**
|
|
@@ -385,13 +397,6 @@ export declare function activityDetail(event: UiActivityEvent): string;
|
|
|
385
397
|
* beside it is not shown: it can carry a vault's own words.
|
|
386
398
|
*/
|
|
387
399
|
export declare function credentialProblemCopy(problem: string | undefined): string;
|
|
388
|
-
export declare function credentialStateLabel(credential: {
|
|
389
|
-
configured: boolean | null;
|
|
390
|
-
problem?: string;
|
|
391
|
-
fields?: unknown[];
|
|
392
|
-
lastFour?: string;
|
|
393
|
-
updatedAt?: string;
|
|
394
|
-
}): string;
|
|
395
400
|
/** Gate copy for each browser-auth shape, so the sign-in state is never a blank page. */
|
|
396
401
|
export declare function gateCopy(kind: string, signedIn: boolean): string;
|
|
397
402
|
export {};
|