@zackbart/connecta 0.31.0 → 0.33.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.
Files changed (169) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +30 -20
  3. package/bin/connecta.mjs +25 -6
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.d.ts +167 -23
  6. package/dist/access-tokens.js +676 -140
  7. package/dist/agent-identity.js +47 -0
  8. package/dist/auth/cloudflare-access.js +13 -5
  9. package/dist/auth-elicitation.js +1 -1
  10. package/dist/call-admission-context.js +15 -0
  11. package/dist/call-admission.js +30 -43
  12. package/dist/call-recovery.js +6 -2
  13. package/dist/catalog-service.js +7 -2
  14. package/dist/config-value-sources.js +14 -9
  15. package/dist/config.d.ts +58 -78
  16. package/dist/config.js +143 -47
  17. package/dist/connector-access.d.ts +4 -33
  18. package/dist/connector-access.js +15 -99
  19. package/dist/connector-caller.d.ts +7 -1
  20. package/dist/connector-caller.js +3 -2
  21. package/dist/connectors/api-connector.d.ts +27 -4
  22. package/dist/connectors/api-connector.js +2 -0
  23. package/dist/connectors/guarded-fetch.d.ts +9 -0
  24. package/dist/connectors/guarded-fetch.js +7 -0
  25. package/dist/connectors/remote-mcp.js +2 -0
  26. package/dist/describe-config.d.ts +32 -7
  27. package/dist/describe-config.js +9 -18
  28. package/dist/errors.d.ts +1 -1
  29. package/dist/errors.js +1 -0
  30. package/dist/execute.js +16 -14
  31. package/dist/index.d.ts +4 -3
  32. package/dist/index.js +30 -10
  33. package/dist/invocation.js +12 -7
  34. package/dist/membership.d.ts +51 -0
  35. package/dist/membership.js +121 -0
  36. package/dist/meta-tools.js +17 -19
  37. package/dist/module-contracts.d.ts +23 -6
  38. package/dist/operator-record.d.ts +2 -2
  39. package/dist/operator-record.js +2 -2
  40. package/dist/operator-ui/agents.d.ts +74 -0
  41. package/dist/operator-ui/agents.js +104 -0
  42. package/dist/operator-ui/contract.d.ts +151 -29
  43. package/dist/operator-ui/endpoints.js +192 -0
  44. package/dist/operator-ui/fix-prompts.js +4 -4
  45. package/dist/operator-ui/generated.js +5 -4
  46. package/dist/operator-ui/member-page.js +112 -0
  47. package/dist/operator-ui/model.d.ts +15 -7
  48. package/dist/operator-ui/view.d.ts +84 -19
  49. package/dist/operator-ui/view.js +83 -21
  50. package/dist/page-styles.js +1 -1
  51. package/dist/program-writes.js +2 -2
  52. package/dist/provider.d.ts +63 -11
  53. package/dist/provider.js +46 -7
  54. package/dist/providers/_shared/google/workspace.d.ts +53 -16
  55. package/dist/providers/_shared/google/workspace.js +31 -9
  56. package/dist/providers/_shared/rest/tools.js +2 -2
  57. package/dist/providers/_shared/rest/value-safety.d.ts +50 -0
  58. package/dist/providers/_shared/rest/value-safety.js +699 -7
  59. package/dist/providers/breeze/skill.generated.js +2 -2
  60. package/dist/providers/ccb/index.js +1 -1
  61. package/dist/providers/cloudflare/index.js +3 -3
  62. package/dist/providers/cloudflare/rest.js +25 -3
  63. package/dist/providers/cloudflare/skill.generated.js +2 -2
  64. package/dist/providers/cloudflare/value-safety.js +32 -0
  65. package/dist/providers/docs/index.d.ts +1 -1
  66. package/dist/providers/docs/index.js +5 -5
  67. package/dist/providers/docs/skill.generated.js +2 -2
  68. package/dist/providers/drive/index.d.ts +1 -1
  69. package/dist/providers/drive/index.js +4 -4
  70. package/dist/providers/drive/skill.generated.js +2 -2
  71. package/dist/providers/forms/index.d.ts +1 -1
  72. package/dist/providers/forms/index.js +5 -5
  73. package/dist/providers/forms/skill.generated.js +2 -2
  74. package/dist/providers/github/skill.generated.js +2 -2
  75. package/dist/providers/gmail/index.d.ts +1 -1
  76. package/dist/providers/gmail/index.js +279 -26
  77. package/dist/providers/gmail/skill.generated.js +2 -2
  78. package/dist/providers/linear/index.js +1 -1
  79. package/dist/providers/mixpanel/index.d.ts +27 -12
  80. package/dist/providers/mixpanel/index.js +58 -6
  81. package/dist/providers/mixpanel/operations.js +20 -0
  82. package/dist/providers/mixpanel/rest.js +603 -0
  83. package/dist/providers/mixpanel/skill.generated.js +3 -2
  84. package/dist/providers/mixpanel/value-safety.js +16 -0
  85. package/dist/providers/notion/index.d.ts +7 -0
  86. package/dist/providers/notion/index.js +116 -11
  87. package/dist/providers/notion/rest.js +183 -24
  88. package/dist/providers/notion/skill.generated.js +4 -4
  89. package/dist/providers/notion/value-safety.js +30 -5
  90. package/dist/providers/overflow/index.js +1 -1
  91. package/dist/providers/planning-center/index.d.ts +27 -0
  92. package/dist/providers/planning-center/index.js +287 -25
  93. package/dist/providers/planning-center/skill.generated.js +24 -4
  94. package/dist/providers/sheets/index.d.ts +4 -1
  95. package/dist/providers/sheets/index.js +9 -6
  96. package/dist/providers/sheets/skill.generated.js +2 -2
  97. package/dist/providers/slides/index.d.ts +1 -1
  98. package/dist/providers/slides/index.js +6 -6
  99. package/dist/providers/slides/skill.generated.js +2 -2
  100. package/dist/providers/stripe/rest.js +9 -2
  101. package/dist/providers/stripe/skill.generated.js +2 -2
  102. package/dist/providers/stripe/value-safety.js +22 -0
  103. package/dist/providers/vercel/index.js +2 -2
  104. package/dist/providers/vercel/rest.js +20 -2
  105. package/dist/providers/vercel/skill.generated.js +2 -2
  106. package/dist/providers/vercel/value-safety.js +22 -0
  107. package/dist/providers/wispr-flow/index.d.ts +14 -0
  108. package/dist/providers/wispr-flow/index.js +117 -0
  109. package/dist/providers/wispr-flow/skill.generated.js +8 -0
  110. package/dist/registry.d.ts +2 -3
  111. package/dist/registry.js +8 -9
  112. package/dist/routes/access-tokens.js +82 -75
  113. package/dist/routes/activity-disclosure.js +17 -18
  114. package/dist/routes/activity.js +34 -54
  115. package/dist/routes/actor-labels.js +33 -4
  116. package/dist/routes/agent-path.js +43 -0
  117. package/dist/routes/connect.js +16 -12
  118. package/dist/routes/credentials.js +16 -40
  119. package/dist/routes/mcp.js +155 -85
  120. package/dist/routes/oauth-management.js +11 -5
  121. package/dist/routes/oauth.js +3 -9
  122. package/dist/routes/operator.js +25 -17
  123. package/dist/routes/shared.d.ts +60 -20
  124. package/dist/routes/shared.js +187 -96
  125. package/dist/routes/ui-config.js +437 -97
  126. package/dist/routes/ui-data.js +2 -2
  127. package/dist/routes/ui.js +9 -16
  128. package/dist/runtime/call-admission.js +9 -0
  129. package/dist/server.js +8 -0
  130. package/dist/skills.js +5 -4
  131. package/dist/storage/keys.js +9 -1
  132. package/dist/tool-safety.js +13 -5
  133. package/dist/types.d.ts +12 -1
  134. package/dist/ui.d.ts +2 -2
  135. package/dist/ui.js +17 -15
  136. package/dist/usage-guide.js +3 -3
  137. package/dist/version.d.ts +1 -1
  138. package/dist/version.js +1 -1
  139. package/dist/workspace-address.js +9 -0
  140. package/dist/workspace-subject.js +23 -0
  141. package/documentation/README.md +46 -22
  142. package/documentation/architecture.md +378 -58
  143. package/documentation/auth.md +806 -215
  144. package/documentation/code-mode.md +93 -81
  145. package/documentation/deploying.md +128 -26
  146. package/documentation/integrating.md +145 -22
  147. package/documentation/meta-tools.md +58 -46
  148. package/documentation/migration-0.33.md +344 -0
  149. package/documentation/operating.md +14 -9
  150. package/documentation/operator-tests.md +117 -31
  151. package/documentation/operator-ui.md +326 -55
  152. package/documentation/provider-auth-migration.md +42 -27
  153. package/documentation/provider-migration-0.29.md +3 -3
  154. package/examples/worker/AGENTS.md +35 -8
  155. package/examples/worker/README.md +92 -53
  156. package/examples/worker/src/connecta.config.ts +41 -20
  157. package/examples/worker/wrangler.jsonc +3 -0
  158. package/package.json +7 -2
  159. package/templates/node/.env.example +3 -0
  160. package/templates/node/AGENTS.md +33 -10
  161. package/templates/node/Dockerfile +2 -2
  162. package/templates/node/README.md +71 -44
  163. package/templates/node/docker-compose.yml +3 -2
  164. package/templates/node/package.json +4 -3
  165. package/templates/node/src/adopt-token.ts +23 -0
  166. package/templates/node/src/connecta.config.ts +33 -12
  167. package/templates/node/src/rotate-token.ts +17 -0
  168. package/dist/tool-safety.d.ts +0 -9
  169. package/templates/node/src/provision-token.ts +0 -17
@@ -0,0 +1,192 @@
1
+ /** How many chips an endpoint row shows before "+N more". */
2
+ const ACCESS_CHIP_LIMIT = 4;
3
+ function initialsOf(text) {
4
+ const words = text
5
+ .replace(/^user_/, "")
6
+ .split(/[\s._@-]+/)
7
+ .filter(Boolean);
8
+ const letters = words.length > 1 ? words[0][0] + words[1][0] : (words[0] ?? text).slice(0, 2);
9
+ return letters.toUpperCase();
10
+ }
11
+ function hueOf(key) {
12
+ let hash = 0;
13
+ for (const char of key)
14
+ hash = (hash * 31 + char.charCodeAt(0)) >>> 0;
15
+ return hash % 6;
16
+ }
17
+ /** A member as a chip. Labels are null on Access deployments, where the raw id shows instead. */
18
+ function accessChip(member) {
19
+ if (member.kind === "agent") {
20
+ const key = `agent:${member.name}`;
21
+ return { key, kind: "agent", text: key, initials: "", hue: hueOf(key) };
22
+ }
23
+ const key = `${member.namespace}:${member.id}`;
24
+ const text = member.label ?? member.id;
25
+ return { key, kind: "human", text, initials: initialsOf(text), hue: hueOf(key) };
26
+ }
27
+ /** Chips for one endpoint in contract order, people first, without repeats. */
28
+ export function accessChips(members) {
29
+ const unique = new Map();
30
+ for (const chip of members.map(accessChip))
31
+ if (!unique.has(chip.key))
32
+ unique.set(chip.key, chip);
33
+ const chips = [...unique.values()];
34
+ return [...chips.filter((chip) => chip.kind === "human"), ...chips.filter((chip) => chip.kind === "agent")];
35
+ }
36
+ /** The chips a collapsed row shows, and how many the "+N more" button holds back. */
37
+ export function visibleChips(chips, expanded) {
38
+ if (expanded || chips.length <= ACCESS_CHIP_LIMIT)
39
+ return { shown: chips, hidden: 0 };
40
+ return { shown: chips.slice(0, ACCESS_CHIP_LIMIT), hidden: chips.length - ACCESS_CHIP_LIMIT };
41
+ }
42
+ /** Matrix cells for one endpoint, keyed by connector id. */
43
+ function cellsFor(contract, pool) {
44
+ return new Map(contract.matrix.filter((cell) => cell.pool === pool).map((cell) => [cell.connectorId, cell]));
45
+ }
46
+ /** Plain /mcp declares no tool list; its matrix cells are its grants. */
47
+ function rootGrants(contract) {
48
+ return contract.matrix
49
+ .filter((cell) => cell.pool === null)
50
+ .map((cell) => ({
51
+ connectorId: cell.connectorId,
52
+ tools: cell.tools === "all" ? "all" : cell.tools.map((name) => ({ name, requireReadOnly: false })),
53
+ }));
54
+ }
55
+ /** Intersect declared grants with final live verdicts, in config order. */
56
+ function endpointGroups(contract, grants, readOnly, pool) {
57
+ const cells = cellsFor(contract, pool);
58
+ return (contract.config?.connectors ?? []).flatMap((config) => {
59
+ const live = contract.live.connectors.find((c) => c.id === config.id);
60
+ const grant = grants.find((g) => g.connectorId === config.id);
61
+ if (!live || !grant)
62
+ return [];
63
+ const tools = live.tools.filter((tool) => {
64
+ if (readOnly && tool.classification !== "read")
65
+ return false;
66
+ return (grant.tools === "all" ||
67
+ grant.tools.some((g) => g.name === tool.name && (!g.requireReadOnly || tool.classification === "read")));
68
+ });
69
+ // A granted connector with no complete catalog still needs a visible fix.
70
+ return [{ config, live, tools, runsAs: cells.get(config.id)?.runsAs ?? null }];
71
+ });
72
+ }
73
+ const toolCount = (groups) => groups.reduce((n, group) => n + group.tools.length, 0);
74
+ /** One row for plain /mcp, then one per pool, in contract order. Maintainer view only. */
75
+ export function endpointRows(contract) {
76
+ if (contract.role !== "maintainer")
77
+ return [];
78
+ const rows = [];
79
+ if (contract.root) {
80
+ const groups = endpointGroups(contract, rootGrants(contract), contract.root.readOnly, null);
81
+ rows.push({
82
+ pool: null,
83
+ label: "Default",
84
+ path: contract.root.path,
85
+ agentPath: null,
86
+ readOnly: contract.root.readOnly,
87
+ toolCount: toolCount(groups),
88
+ groups,
89
+ access: accessChips(contract.root.maintainers),
90
+ });
91
+ }
92
+ for (const pool of contract.pools) {
93
+ const groups = endpointGroups(contract, pool.tools, pool.readOnly, pool.name);
94
+ rows.push({
95
+ pool: pool.name,
96
+ label: pool.name,
97
+ path: pool.humanPath,
98
+ agentPath: pool.members.some((member) => member.kind === "agent") ? pool.agentPath : null,
99
+ readOnly: pool.readOnly,
100
+ toolCount: toolCount(groups),
101
+ groups,
102
+ access: accessChips(pool.members),
103
+ });
104
+ }
105
+ return rows;
106
+ }
107
+ /** A calendar day, without the time: when a shared credential was saved. */
108
+ function formatDay(value) {
109
+ const date = new Date(value);
110
+ return Number.isNaN(date.valueOf()) ? "" : date.toLocaleDateString(undefined, { dateStyle: "medium" });
111
+ }
112
+ /** Where a shared credential lives; a vault or OAuth date when the contract has one. */
113
+ function sharedSourceLabel(source, date) {
114
+ switch (source) {
115
+ case "vault":
116
+ return date ? `saved ${date}` : "saved";
117
+ case "oauth":
118
+ return date ? `connected ${date}` : "connected";
119
+ case "missing":
120
+ return "not configured";
121
+ case "config":
122
+ return "set in deployment";
123
+ case "unauthenticated":
124
+ return "no credential needed";
125
+ }
126
+ }
127
+ /** Whose account a connector acts with on one endpoint, in one line. */
128
+ export function runsAsLabel(runsAs, day = formatDay) {
129
+ switch (runsAs.kind) {
130
+ case "shared":
131
+ return `Shared · ${sharedSourceLabel(runsAs.source, runsAs.savedAt ? day(runsAs.savedAt) : "")}`;
132
+ case "personal":
133
+ return `Personal · ${runsAs.connectedMembers} of ${runsAs.members} connected`;
134
+ case "workspace":
135
+ return `Workspace · people: self · agents: ${runsAs.agents ?? "none"}`;
136
+ case "workspaceFixed":
137
+ return `Everyone acts as ${runsAs.subject}`;
138
+ }
139
+ }
140
+ export function matrixColumns(contract) {
141
+ if (contract.role !== "maintainer")
142
+ return [];
143
+ return [
144
+ ...(contract.root ? [{ pool: null, path: contract.root.path, readOnly: contract.root.readOnly }] : []),
145
+ ...contract.pools.map((pool) => ({ pool: pool.name, path: pool.humanPath, readOnly: pool.readOnly })),
146
+ ];
147
+ }
148
+ /** What one connector × endpoint cell holds, straight from the contract's matrix. */
149
+ export function matrixCell(contract, connectorId, column) {
150
+ const cell = contract.matrix.find((c) => c.connectorId === connectorId && c.pool === column.pool);
151
+ if (!cell)
152
+ return { kind: "none" };
153
+ const live = contract.live.connectors.find((c) => c.id === connectorId)?.tools ?? [];
154
+ const tools = cell.tools === "all"
155
+ ? live.map(({ name, classification }) => ({ name, classification }))
156
+ : cell.tools.map((name) => ({ name, classification: live.find((t) => t.name === name)?.classification ?? null }));
157
+ return { kind: cell.tools === "all" ? "all" : "some", tools, runsAs: cell.runsAs };
158
+ }
159
+ /** The cell's visible text: "all", a count, or a dash. */
160
+ export function matrixCellText(cell) {
161
+ if (cell.kind === "none")
162
+ return "—";
163
+ return cell.kind === "all" ? "all" : String(cell.tools.length);
164
+ }
165
+ /** The cell's accessible name, e.g. "GitHub on /support: 3 tools". */
166
+ export function matrixCellLabel(connector, column, cell) {
167
+ const where = `${connector} on ${column.path}`;
168
+ if (cell.kind === "none")
169
+ return `${where}: no tools`;
170
+ const what = cell.kind === "all" ? "all tools" : `${cell.tools.length} ${cell.tools.length === 1 ? "tool" : "tools"}`;
171
+ return `${where}: ${what}${column.readOnly ? ", read-only" : ""}`;
172
+ }
173
+ /** The connector's subject for the matrix's Runs as column: the /mcp cell, else its first pool's. */
174
+ export function connectorRunsAs(contract, connectorId) {
175
+ const cells = contract.matrix.filter((cell) => cell.connectorId === connectorId);
176
+ const root = cells.find((cell) => cell.pool === null);
177
+ if (root)
178
+ return root.runsAs;
179
+ for (const pool of contract.pools) {
180
+ const cell = cells.find((c) => c.pool === pool.name);
181
+ if (cell)
182
+ return cell.runsAs;
183
+ }
184
+ return null;
185
+ }
186
+ /** Who may use plain /mcp, for Settings: each maintainer by label, else by raw id. */
187
+ export function rootAccessSentence(root) {
188
+ const names = root.maintainers.map((member) => (member.kind === "human" ? (member.label ?? member.id) : member.name));
189
+ return names.length
190
+ ? `Only maintainers can use ${root.path}: ${names.join(", ")}.`
191
+ : `Only maintainers can use ${root.path}, and none are listed.`;
192
+ }
@@ -19,8 +19,8 @@ const CATALOGUE = {
19
19
  credential_required: {
20
20
  problem: "A connector with an operator-managed credential slot has no usable credential stored.",
21
21
  steps: [
22
- "An operator with credential administration can add the credential on the operator page; that needs no code change.",
23
- "If nobody can, grant credential administration through the deployment's identity config (credentialAdministration for shared auth, personalConnection for personal auth), which is denied by default.",
22
+ "With a credential vault configured, a maintainer adds a shared credential on the operator page, and a pool member saves their own personal one from their view of it; neither needs a code change. Without a vault, configure one first.",
23
+ "If nobody can, list the person in the deployment's maintainers for shared auth, or in the members of a pool containing the connector for personal auth.",
24
24
  "Check that the connector's credential declaration (label and fields) matches what the downstream actually needs.",
25
25
  ],
26
26
  },
@@ -57,9 +57,9 @@ const CATALOGUE = {
57
57
  oauth_action_failed: {
58
58
  problem: "Restarting or disconnecting a connector's downstream OAuth from the operator page failed.",
59
59
  steps: [
60
- "Check that the connector implements startAuth and disconnectAuth, and that the storage holding its OAuth state is writable.",
60
+ "Check that the connector implements the hooks the action needs (startAuth, finishAuth, and verifyState to connect; disconnectAuth to disconnect), and that the storage holding its OAuth state is writable.",
61
61
  "Check the connector's OAuth client configuration, including the authorization server it discovers or is given.",
62
- "Confirm the operator has the config-derived permission for this action (credentialAdministration for shared auth, personalConnection for personal auth).",
62
+ "Confirm the person is a maintainer for shared auth, or a member of a pool containing the connector for personal auth.",
63
63
  ],
64
64
  },
65
65
  catalog_failed: {