@zackbart/connecta 0.15.1 → 0.16.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 (332) hide show
  1. package/AGENTS.md +28 -15
  2. package/CHANGELOG.md +466 -0
  3. package/README.md +19 -4
  4. package/bin/connecta.mjs +44 -3
  5. package/dist/access-tokens.d.ts +0 -1
  6. package/dist/access-tokens.js +0 -1
  7. package/dist/activity.d.ts +50 -2
  8. package/dist/activity.js +34 -1
  9. package/dist/apps-shell.d.ts +0 -1
  10. package/dist/apps-shell.js +0 -1
  11. package/dist/auth/bearer.d.ts +0 -1
  12. package/dist/auth/bearer.js +0 -1
  13. package/dist/auth/clerk.d.ts +0 -1
  14. package/dist/auth/clerk.js +45 -9
  15. package/dist/auth/downstream-oauth.d.ts +0 -1
  16. package/dist/auth/downstream-oauth.js +0 -1
  17. package/dist/call-admission.d.ts +0 -1
  18. package/dist/call-admission.js +0 -1
  19. package/dist/catalog-drift.d.ts +99 -0
  20. package/dist/catalog-drift.js +251 -0
  21. package/dist/catalog-fingerprint.d.ts +0 -1
  22. package/dist/catalog-fingerprint.js +0 -1
  23. package/dist/catalog-limits.d.ts +0 -1
  24. package/dist/catalog-limits.js +0 -1
  25. package/dist/catalog-service.d.ts +0 -1
  26. package/dist/catalog-service.js +0 -1
  27. package/dist/catalog.d.ts +0 -1
  28. package/dist/catalog.js +0 -1
  29. package/dist/concurrency.d.ts +0 -1
  30. package/dist/concurrency.js +0 -1
  31. package/dist/connector-scope.d.ts +0 -1
  32. package/dist/connector-scope.js +0 -1
  33. package/dist/connectors/api.d.ts +29 -21
  34. package/dist/connectors/api.js +45 -19
  35. package/dist/connectors/guarded-fetch.d.ts +106 -0
  36. package/dist/connectors/guarded-fetch.js +289 -0
  37. package/dist/connectors/remote-mcp.d.ts +0 -1
  38. package/dist/connectors/remote-mcp.js +0 -1
  39. package/dist/credentials.d.ts +0 -1
  40. package/dist/credentials.js +0 -1
  41. package/dist/errors.d.ts +0 -1
  42. package/dist/errors.js +0 -1
  43. package/dist/execute.d.ts +0 -1
  44. package/dist/execute.js +0 -1
  45. package/dist/executor-admission.d.ts +11 -1
  46. package/dist/executor-admission.js +28 -1
  47. package/dist/executor-result.d.ts +0 -1
  48. package/dist/executor-result.js +0 -1
  49. package/dist/executors/quickjs-child.d.ts +0 -1
  50. package/dist/executors/quickjs-child.js +0 -1
  51. package/dist/executors/quickjs-protocol.d.ts +0 -1
  52. package/dist/executors/quickjs-protocol.js +0 -1
  53. package/dist/executors/quickjs-runtime.d.ts +0 -1
  54. package/dist/executors/quickjs-runtime.js +0 -1
  55. package/dist/executors/quickjs.d.ts +0 -1
  56. package/dist/executors/quickjs.js +2 -1
  57. package/dist/favicon.d.ts +0 -1
  58. package/dist/favicon.js +0 -1
  59. package/dist/index.d.ts +2 -3
  60. package/dist/index.js +24 -7
  61. package/dist/invocation.d.ts +0 -1
  62. package/dist/invocation.js +0 -1
  63. package/dist/json-schema.d.ts +0 -1
  64. package/dist/json-schema.js +0 -1
  65. package/dist/mcp-result.d.ts +0 -1
  66. package/dist/mcp-result.js +0 -1
  67. package/dist/meta-tools.d.ts +0 -1
  68. package/dist/meta-tools.js +0 -1
  69. package/dist/node.d.ts +0 -1
  70. package/dist/node.js +0 -1
  71. package/dist/operator-ui/generated.d.ts +2 -3
  72. package/dist/operator-ui/generated.js +3 -4
  73. package/dist/operator-ui/model.d.ts +10 -1
  74. package/dist/operator-ui/model.js +0 -1
  75. package/dist/operator-ui/view.d.ts +167 -0
  76. package/dist/operator-ui/view.js +270 -0
  77. package/dist/providers/cloudflare.d.ts +0 -1
  78. package/dist/providers/cloudflare.js +250 -276
  79. package/dist/providers/linear.d.ts +26 -5
  80. package/dist/providers/linear.js +32 -61
  81. package/dist/providers/mixpanel.d.ts +35 -4
  82. package/dist/providers/mixpanel.js +53 -79
  83. package/dist/providers/notion.d.ts +0 -1
  84. package/dist/providers/notion.js +87 -42
  85. package/dist/providers/stripe.d.ts +13 -1
  86. package/dist/providers/stripe.js +31 -60
  87. package/dist/registry.d.ts +34 -2
  88. package/dist/registry.js +85 -5
  89. package/dist/retired-toolkits.d.ts +0 -1
  90. package/dist/retired-toolkits.js +0 -1
  91. package/dist/routes/access-tokens.d.ts +0 -1
  92. package/dist/routes/access-tokens.js +0 -1
  93. package/dist/routes/activity.d.ts +0 -1
  94. package/dist/routes/activity.js +0 -1
  95. package/dist/routes/credentials.d.ts +0 -1
  96. package/dist/routes/credentials.js +0 -1
  97. package/dist/routes/mcp.d.ts +0 -1
  98. package/dist/routes/mcp.js +0 -1
  99. package/dist/routes/oauth.d.ts +0 -1
  100. package/dist/routes/oauth.js +0 -1
  101. package/dist/routes/shared.d.ts +2 -1
  102. package/dist/routes/shared.js +0 -1
  103. package/dist/routes/ui.d.ts +0 -1
  104. package/dist/routes/ui.js +0 -1
  105. package/dist/server.d.ts +0 -1
  106. package/dist/server.js +11 -1
  107. package/dist/skills.d.ts +0 -1
  108. package/dist/skills.js +0 -1
  109. package/dist/storage/file.d.ts +0 -1
  110. package/dist/storage/file.js +0 -1
  111. package/dist/storage/memory.d.ts +0 -1
  112. package/dist/storage/memory.js +0 -1
  113. package/dist/timeout.d.ts +0 -1
  114. package/dist/timeout.js +0 -1
  115. package/dist/tool-safety.d.ts +0 -1
  116. package/dist/tool-safety.js +0 -1
  117. package/dist/types.d.ts +45 -1
  118. package/dist/types.js +0 -1
  119. package/dist/ui.d.ts +0 -1
  120. package/dist/ui.js +13 -137
  121. package/dist/validate.d.ts +13 -15
  122. package/dist/validate.js +24 -11
  123. package/dist/version.d.ts +1 -2
  124. package/dist/version.js +1 -2
  125. package/documentation/architecture.md +186 -5
  126. package/documentation/auth.md +11 -0
  127. package/documentation/call-admission.md +142 -6
  128. package/documentation/cloudflare.md +58 -8
  129. package/documentation/code-mode.md +12 -12
  130. package/documentation/connector-guides.md +172 -5
  131. package/documentation/connectors.md +89 -0
  132. package/documentation/linear.md +20 -2
  133. package/documentation/mcp-ui-design.md +2 -2
  134. package/documentation/mixpanel.md +42 -9
  135. package/documentation/notion.md +16 -0
  136. package/documentation/operations.md +283 -5
  137. package/documentation/operator-ui.md +120 -5
  138. package/documentation/provider-audit.md +160 -0
  139. package/documentation/provider-conventions.md +631 -0
  140. package/documentation/request-admission.md +167 -5
  141. package/documentation/rich-output-design.md +1 -2
  142. package/documentation/stripe.md +15 -0
  143. package/ethos.md +13 -8
  144. package/examples/worker/README.md +96 -12
  145. package/examples/worker/src/index.ts +46 -1
  146. package/examples/worker/wrangler.jsonc +14 -1
  147. package/package.json +4 -5
  148. package/templates/node/.dockerignore +10 -0
  149. package/templates/node/.env.example +40 -3
  150. package/templates/node/AGENTS.md +11 -1
  151. package/templates/node/Dockerfile +49 -0
  152. package/templates/node/README.md +117 -2
  153. package/templates/node/docker-compose.yml +39 -0
  154. package/templates/node/package.json +1 -1
  155. package/templates/node/src/file-activity.ts +182 -0
  156. package/templates/node/src/index.ts +80 -4
  157. package/assets/connecta-clay-hero.png +0 -0
  158. package/dist/access-tokens.d.ts.map +0 -1
  159. package/dist/access-tokens.js.map +0 -1
  160. package/dist/activity.d.ts.map +0 -1
  161. package/dist/activity.js.map +0 -1
  162. package/dist/apps-shell.d.ts.map +0 -1
  163. package/dist/apps-shell.js.map +0 -1
  164. package/dist/auth/bearer.d.ts.map +0 -1
  165. package/dist/auth/bearer.js.map +0 -1
  166. package/dist/auth/clerk.d.ts.map +0 -1
  167. package/dist/auth/clerk.js.map +0 -1
  168. package/dist/auth/downstream-oauth.d.ts.map +0 -1
  169. package/dist/auth/downstream-oauth.js.map +0 -1
  170. package/dist/call-admission.d.ts.map +0 -1
  171. package/dist/call-admission.js.map +0 -1
  172. package/dist/catalog-fingerprint.d.ts.map +0 -1
  173. package/dist/catalog-fingerprint.js.map +0 -1
  174. package/dist/catalog-limits.d.ts.map +0 -1
  175. package/dist/catalog-limits.js.map +0 -1
  176. package/dist/catalog-service.d.ts.map +0 -1
  177. package/dist/catalog-service.js.map +0 -1
  178. package/dist/catalog.d.ts.map +0 -1
  179. package/dist/catalog.js.map +0 -1
  180. package/dist/concurrency.d.ts.map +0 -1
  181. package/dist/concurrency.js.map +0 -1
  182. package/dist/connector-scope.d.ts.map +0 -1
  183. package/dist/connector-scope.js.map +0 -1
  184. package/dist/connectors/api.d.ts.map +0 -1
  185. package/dist/connectors/api.js.map +0 -1
  186. package/dist/connectors/remote-mcp.d.ts.map +0 -1
  187. package/dist/connectors/remote-mcp.js.map +0 -1
  188. package/dist/credentials.d.ts.map +0 -1
  189. package/dist/credentials.js.map +0 -1
  190. package/dist/errors.d.ts.map +0 -1
  191. package/dist/errors.js.map +0 -1
  192. package/dist/execute.d.ts.map +0 -1
  193. package/dist/execute.js.map +0 -1
  194. package/dist/executor-admission.d.ts.map +0 -1
  195. package/dist/executor-admission.js.map +0 -1
  196. package/dist/executor-result.d.ts.map +0 -1
  197. package/dist/executor-result.js.map +0 -1
  198. package/dist/executors/quickjs-child.d.ts.map +0 -1
  199. package/dist/executors/quickjs-child.js.map +0 -1
  200. package/dist/executors/quickjs-protocol.d.ts.map +0 -1
  201. package/dist/executors/quickjs-protocol.js.map +0 -1
  202. package/dist/executors/quickjs-runtime.d.ts.map +0 -1
  203. package/dist/executors/quickjs-runtime.js.map +0 -1
  204. package/dist/executors/quickjs.d.ts.map +0 -1
  205. package/dist/executors/quickjs.js.map +0 -1
  206. package/dist/favicon.d.ts.map +0 -1
  207. package/dist/favicon.js.map +0 -1
  208. package/dist/index.d.ts.map +0 -1
  209. package/dist/index.js.map +0 -1
  210. package/dist/invocation.d.ts.map +0 -1
  211. package/dist/invocation.js.map +0 -1
  212. package/dist/json-schema.d.ts.map +0 -1
  213. package/dist/json-schema.js.map +0 -1
  214. package/dist/mcp-result.d.ts.map +0 -1
  215. package/dist/mcp-result.js.map +0 -1
  216. package/dist/meta-tools.d.ts.map +0 -1
  217. package/dist/meta-tools.js.map +0 -1
  218. package/dist/node.d.ts.map +0 -1
  219. package/dist/node.js.map +0 -1
  220. package/dist/operator-ui/generated.d.ts.map +0 -1
  221. package/dist/operator-ui/generated.js.map +0 -1
  222. package/dist/operator-ui/model.d.ts.map +0 -1
  223. package/dist/operator-ui/model.js.map +0 -1
  224. package/dist/providers/cloudflare.d.ts.map +0 -1
  225. package/dist/providers/cloudflare.js.map +0 -1
  226. package/dist/providers/linear.d.ts.map +0 -1
  227. package/dist/providers/linear.js.map +0 -1
  228. package/dist/providers/mixpanel.d.ts.map +0 -1
  229. package/dist/providers/mixpanel.js.map +0 -1
  230. package/dist/providers/notion.d.ts.map +0 -1
  231. package/dist/providers/notion.js.map +0 -1
  232. package/dist/providers/stripe.d.ts.map +0 -1
  233. package/dist/providers/stripe.js.map +0 -1
  234. package/dist/registry.d.ts.map +0 -1
  235. package/dist/registry.js.map +0 -1
  236. package/dist/retired-toolkits.d.ts.map +0 -1
  237. package/dist/retired-toolkits.js.map +0 -1
  238. package/dist/routes/access-tokens.d.ts.map +0 -1
  239. package/dist/routes/access-tokens.js.map +0 -1
  240. package/dist/routes/activity.d.ts.map +0 -1
  241. package/dist/routes/activity.js.map +0 -1
  242. package/dist/routes/credentials.d.ts.map +0 -1
  243. package/dist/routes/credentials.js.map +0 -1
  244. package/dist/routes/mcp.d.ts.map +0 -1
  245. package/dist/routes/mcp.js.map +0 -1
  246. package/dist/routes/oauth.d.ts.map +0 -1
  247. package/dist/routes/oauth.js.map +0 -1
  248. package/dist/routes/shared.d.ts.map +0 -1
  249. package/dist/routes/shared.js.map +0 -1
  250. package/dist/routes/ui.d.ts.map +0 -1
  251. package/dist/routes/ui.js.map +0 -1
  252. package/dist/server.d.ts.map +0 -1
  253. package/dist/server.js.map +0 -1
  254. package/dist/skills.d.ts.map +0 -1
  255. package/dist/skills.js.map +0 -1
  256. package/dist/storage/file.d.ts.map +0 -1
  257. package/dist/storage/file.js.map +0 -1
  258. package/dist/storage/memory.d.ts.map +0 -1
  259. package/dist/storage/memory.js.map +0 -1
  260. package/dist/timeout.d.ts.map +0 -1
  261. package/dist/timeout.js.map +0 -1
  262. package/dist/tool-safety.d.ts.map +0 -1
  263. package/dist/tool-safety.js.map +0 -1
  264. package/dist/types.d.ts.map +0 -1
  265. package/dist/types.js.map +0 -1
  266. package/dist/ui.d.ts.map +0 -1
  267. package/dist/ui.js.map +0 -1
  268. package/dist/validate.d.ts.map +0 -1
  269. package/dist/validate.js.map +0 -1
  270. package/dist/version.d.ts.map +0 -1
  271. package/dist/version.js.map +0 -1
  272. package/examples/node/README.md +0 -52
  273. package/examples/node/src/index.ts +0 -71
  274. package/src/access-tokens.ts +0 -289
  275. package/src/activity.ts +0 -234
  276. package/src/apps-shell.ts +0 -370
  277. package/src/auth/bearer.ts +0 -61
  278. package/src/auth/clerk.ts +0 -553
  279. package/src/auth/downstream-oauth.ts +0 -575
  280. package/src/call-admission.ts +0 -521
  281. package/src/catalog-fingerprint.ts +0 -52
  282. package/src/catalog-limits.ts +0 -11
  283. package/src/catalog-service.ts +0 -1153
  284. package/src/catalog.ts +0 -790
  285. package/src/concurrency.ts +0 -39
  286. package/src/connector-scope.ts +0 -66
  287. package/src/connectors/api.ts +0 -159
  288. package/src/connectors/remote-mcp.ts +0 -1090
  289. package/src/credentials.ts +0 -428
  290. package/src/errors.ts +0 -364
  291. package/src/execute.ts +0 -1406
  292. package/src/executor-admission.ts +0 -328
  293. package/src/executor-result.ts +0 -107
  294. package/src/executors/quickjs-child.ts +0 -172
  295. package/src/executors/quickjs-protocol.ts +0 -99
  296. package/src/executors/quickjs-runtime.ts +0 -470
  297. package/src/executors/quickjs.ts +0 -776
  298. package/src/favicon.ts +0 -53
  299. package/src/index.ts +0 -666
  300. package/src/invocation.ts +0 -655
  301. package/src/json-schema.ts +0 -11
  302. package/src/mcp-result.ts +0 -43
  303. package/src/meta-tools.ts +0 -1607
  304. package/src/node.ts +0 -206
  305. package/src/operator-ui/browser.css +0 -423
  306. package/src/operator-ui/browser.ts +0 -1281
  307. package/src/operator-ui/generated.ts +0 -4
  308. package/src/operator-ui/model.ts +0 -111
  309. package/src/providers/cloudflare.ts +0 -3999
  310. package/src/providers/linear.ts +0 -302
  311. package/src/providers/mixpanel.ts +0 -229
  312. package/src/providers/notion.ts +0 -1879
  313. package/src/providers/stripe.ts +0 -307
  314. package/src/registry.ts +0 -1085
  315. package/src/retired-toolkits.ts +0 -14
  316. package/src/routes/access-tokens.ts +0 -115
  317. package/src/routes/activity.ts +0 -215
  318. package/src/routes/credentials.ts +0 -254
  319. package/src/routes/mcp.ts +0 -429
  320. package/src/routes/oauth.ts +0 -322
  321. package/src/routes/shared.ts +0 -247
  322. package/src/routes/ui.ts +0 -152
  323. package/src/server.ts +0 -195
  324. package/src/skills.ts +0 -271
  325. package/src/storage/file.ts +0 -123
  326. package/src/storage/memory.ts +0 -44
  327. package/src/timeout.ts +0 -84
  328. package/src/tool-safety.ts +0 -15
  329. package/src/types.ts +0 -523
  330. package/src/ui.ts +0 -744
  331. package/src/validate.ts +0 -387
  332. package/src/version.ts +0 -7
@@ -1,289 +0,0 @@
1
- import type { AuthResult, InboundAuth, KVStorage } from "./types.js";
2
-
3
- const TOKEN_PREFIX = "cta_";
4
- const TOKEN_BYTES = 32;
5
- const TOKEN_VALUE_RE = /^cta_[A-Za-z0-9_-]{43}$/;
6
- const RECORD_PREFIX = "access-token:v1:record:";
7
- const LOOKUP_PREFIX = "access-token:v1:lookup:";
8
- const MAX_NAME_CHARACTERS = 80;
9
- const DEFAULT_MAX_ACTIVE = 100;
10
- const MAX_CONFIGURED_ACTIVE = 1_000;
11
- const encoder = new TextEncoder();
12
-
13
- interface StoredAccessToken {
14
- version: 1;
15
- id: string;
16
- name: string;
17
- tokenHash: string;
18
- tokenPrefix: string;
19
- createdAt: string;
20
- createdBy: string;
21
- revokedAt?: string;
22
- revokedBy?: string;
23
- }
24
-
25
- interface TokenLookup {
26
- version: 1;
27
- id: string;
28
- }
29
-
30
- export interface AccessTokenMetadata {
31
- id: string;
32
- name: string;
33
- tokenPrefix: string;
34
- createdAt: string;
35
- revokedAt?: string;
36
- }
37
-
38
- export interface CreatedAccessToken {
39
- token: string;
40
- accessToken: AccessTokenMetadata;
41
- }
42
-
43
- function recordKey(id: string): string {
44
- return `${RECORD_PREFIX}${id}`;
45
- }
46
-
47
- function lookupKey(hash: string): string {
48
- return `${LOOKUP_PREFIX}${hash}`;
49
- }
50
-
51
- function bytesToBase64Url(bytes: Uint8Array): string {
52
- let binary = "";
53
- for (const byte of bytes) binary += String.fromCharCode(byte);
54
- return btoa(binary)
55
- .replaceAll("+", "-")
56
- .replaceAll("/", "_")
57
- .replace(/=+$/u, "");
58
- }
59
-
60
- function bytesToHex(bytes: Uint8Array): string {
61
- return [...bytes]
62
- .map((byte) => byte.toString(16).padStart(2, "0"))
63
- .join("");
64
- }
65
-
66
- async function hashToken(token: string): Promise<string> {
67
- return bytesToHex(
68
- new Uint8Array(await crypto.subtle.digest("SHA-256", encoder.encode(token))),
69
- );
70
- }
71
-
72
- function normalizeName(value: unknown): string {
73
- if (typeof value !== "string") {
74
- throw new Error("Token name must be a string");
75
- }
76
- const compact = value.replace(/\s+/gu, " ").trim();
77
- if (!compact) throw new Error("Token name cannot be empty");
78
- if (Array.from(compact).length > MAX_NAME_CHARACTERS) {
79
- throw new Error(
80
- `Token name cannot exceed ${MAX_NAME_CHARACTERS} characters`,
81
- );
82
- }
83
- return compact;
84
- }
85
-
86
- function parseRecord(raw: string): StoredAccessToken {
87
- try {
88
- const value = JSON.parse(raw) as Partial<StoredAccessToken>;
89
- if (
90
- value.version !== 1 ||
91
- typeof value.id !== "string" ||
92
- !/^[0-9a-f-]{36}$/u.test(value.id) ||
93
- typeof value.name !== "string" ||
94
- typeof value.tokenHash !== "string" ||
95
- !/^[0-9a-f]{64}$/u.test(value.tokenHash) ||
96
- typeof value.tokenPrefix !== "string" ||
97
- typeof value.createdAt !== "string" ||
98
- typeof value.createdBy !== "string" ||
99
- (value.revokedAt !== undefined &&
100
- typeof value.revokedAt !== "string") ||
101
- (value.revokedBy !== undefined &&
102
- typeof value.revokedBy !== "string")
103
- ) {
104
- throw new Error("invalid token record");
105
- }
106
- return value as StoredAccessToken;
107
- } catch {
108
- throw new Error("Stored access token metadata is invalid or corrupted");
109
- }
110
- }
111
-
112
- function parseLookup(raw: string): TokenLookup | null {
113
- try {
114
- const value = JSON.parse(raw) as Partial<TokenLookup>;
115
- return value.version === 1 && typeof value.id === "string"
116
- ? { version: 1, id: value.id }
117
- : null;
118
- } catch {
119
- return null;
120
- }
121
- }
122
-
123
- function metadata(record: StoredAccessToken): AccessTokenMetadata {
124
- return {
125
- id: record.id,
126
- name: record.name,
127
- tokenPrefix: record.tokenPrefix,
128
- createdAt: record.createdAt,
129
- ...(record.revokedAt ? { revokedAt: record.revokedAt } : {}),
130
- };
131
- }
132
-
133
- function unauthorized(): AuthResult {
134
- return {
135
- ok: false,
136
- response: new Response(JSON.stringify({ error: "unauthorized" }), {
137
- status: 401,
138
- headers: {
139
- "Content-Type": "application/json",
140
- "WWW-Authenticate": "Bearer",
141
- },
142
- }),
143
- };
144
- }
145
-
146
- /**
147
- * Deployment-scoped personal access tokens. Secret material is never
148
- * recoverable: authentication indexes a SHA-256 digest of a random 256-bit
149
- * token, while separately enumerable metadata powers operator management.
150
- */
151
- export class AccessTokenManager {
152
- readonly auth: InboundAuth;
153
- private readonly maxActive: number;
154
-
155
- constructor(
156
- private readonly storage: KVStorage,
157
- options: { maxActive?: number } = {},
158
- ) {
159
- if (!storage.list) {
160
- throw new Error(
161
- "accessTokens requires a storage adapter that implements list(prefix)",
162
- );
163
- }
164
- const maxActive = options.maxActive ?? DEFAULT_MAX_ACTIVE;
165
- if (
166
- !Number.isInteger(maxActive) ||
167
- maxActive < 1 ||
168
- maxActive > MAX_CONFIGURED_ACTIVE
169
- ) {
170
- throw new Error(
171
- `accessTokens.maxActive must be a whole number from 1 to ${MAX_CONFIGURED_ACTIVE}`,
172
- );
173
- }
174
- this.maxActive = maxActive;
175
- this.auth = {
176
- kind: "access_token",
177
- activityActorNamespace: "connecta:access-tokens:v1",
178
- activityActorLabel: async (id) => {
179
- try {
180
- return (await this.read(id))?.name;
181
- } catch {
182
- return undefined;
183
- }
184
- },
185
- authorize: (request) => this.authorize(request),
186
- };
187
- }
188
-
189
- private async read(id: string): Promise<StoredAccessToken | null> {
190
- const raw = await this.storage.get(recordKey(id));
191
- return raw ? parseRecord(raw) : null;
192
- }
193
-
194
- async list(): Promise<AccessTokenMetadata[]> {
195
- const keys = await this.storage.list!(RECORD_PREFIX);
196
- const records = await Promise.all(
197
- keys.map(async (key) => {
198
- const raw = await this.storage.get(key);
199
- return raw ? parseRecord(raw) : null;
200
- }),
201
- );
202
- return records
203
- .filter((record): record is StoredAccessToken => Boolean(record))
204
- .sort((a, b) => b.createdAt.localeCompare(a.createdAt))
205
- .map(metadata);
206
- }
207
-
208
- async create(name: unknown, createdBy: string): Promise<CreatedAccessToken> {
209
- const normalizedName = normalizeName(name);
210
- const active = (await this.list()).filter((token) => !token.revokedAt);
211
- if (active.length >= this.maxActive) {
212
- throw new Error(
213
- `This deployment already has the maximum of ${this.maxActive} active access tokens`,
214
- );
215
- }
216
- const secretBytes = crypto.getRandomValues(new Uint8Array(TOKEN_BYTES));
217
- const token = TOKEN_PREFIX + bytesToBase64Url(secretBytes);
218
- const hash = await hashToken(token);
219
- if (await this.storage.get(lookupKey(hash))) {
220
- throw new Error("Access token collision; create another token");
221
- }
222
- const record: StoredAccessToken = {
223
- version: 1,
224
- id: crypto.randomUUID(),
225
- name: normalizedName,
226
- tokenHash: hash,
227
- tokenPrefix: token.slice(0, 12),
228
- createdAt: new Date().toISOString(),
229
- createdBy,
230
- };
231
- await this.storage.set(recordKey(record.id), JSON.stringify(record));
232
- try {
233
- await this.storage.set(
234
- lookupKey(hash),
235
- JSON.stringify({ version: 1, id: record.id } satisfies TokenLookup),
236
- );
237
- } catch (error) {
238
- await this.storage.delete(recordKey(record.id)).catch(() => {});
239
- throw error;
240
- }
241
- return { token, accessToken: metadata(record) };
242
- }
243
-
244
- async rename(
245
- id: string,
246
- name: unknown,
247
- ): Promise<AccessTokenMetadata | null> {
248
- const record = await this.read(id);
249
- if (!record) return null;
250
- record.name = normalizeName(name);
251
- await this.storage.set(recordKey(id), JSON.stringify(record));
252
- return metadata(record);
253
- }
254
-
255
- async revoke(
256
- id: string,
257
- revokedBy: string,
258
- ): Promise<AccessTokenMetadata | null> {
259
- const record = await this.read(id);
260
- if (!record) return null;
261
- if (!record.revokedAt) {
262
- // Admission disappears first. A metadata-write failure may leave the UI
263
- // calling the record active, but can never leave a token labelled
264
- // revoked while its lookup still admits requests.
265
- await this.storage.delete(lookupKey(record.tokenHash));
266
- record.revokedAt = new Date().toISOString();
267
- record.revokedBy = revokedBy;
268
- await this.storage.set(recordKey(id), JSON.stringify(record));
269
- }
270
- return metadata(record);
271
- }
272
-
273
- private async authorize(request: Request): Promise<AuthResult> {
274
- const header = request.headers.get("authorization") ?? "";
275
- const match = /^Bearer\s+(.+)$/iu.exec(header);
276
- const token = match?.[1];
277
- if (!token || !TOKEN_VALUE_RE.test(token)) return unauthorized();
278
- const hash = await hashToken(token);
279
- const lookupRaw = await this.storage.get(lookupKey(hash));
280
- if (!lookupRaw) return unauthorized();
281
- const lookup = parseLookup(lookupRaw);
282
- if (!lookup) return unauthorized();
283
- const record = await this.read(lookup.id);
284
- if (!record || record.revokedAt || record.tokenHash !== hash) {
285
- return unauthorized();
286
- }
287
- return { ok: true, subjectId: record.id };
288
- }
289
- }
package/src/activity.ts DELETED
@@ -1,234 +0,0 @@
1
- import { boundedEchoText } from "./errors.js";
2
- import type { Logger } from "./types.js";
3
-
4
- /**
5
- * How long an identity field may be before the store stops believing it.
6
- *
7
- * `connectorId` and `toolName` are ordinarily operator- and connector-authored,
8
- * and 128 bytes is far past any real one. But an address that resolved to
9
- * nothing is recorded *as written*, which puts a caller-authored string in both
10
- * fields — and "payload-free by construction" has to mean the event type has
11
- * nowhere to put a payload, not merely that connecta declines to. A 40 KB
12
- * invented connector id is a payload wearing an id's clothing.
13
- *
14
- * Clamped rather than dropped: the invented id is precisely what an operator
15
- * needs to see, and its first 128 bytes identify the mistake as well as all
16
- * 40,000 would. The `…` marker keeps a clamped value from reading as a real one.
17
- */
18
- const MAX_ACTIVITY_NAME_BYTES = 128;
19
-
20
- /** Two names and the dot between them. */
21
- const MAX_ACTIVITY_ADDRESS_BYTES = MAX_ACTIVITY_NAME_BYTES * 2 + 1;
22
-
23
- export type ActivityCallSource =
24
- | "call_tool"
25
- | "call_destructive_tool"
26
- // Read-only history. Nothing emits `batch_call` since issue #273 removed the
27
- // tool, but activity storage is append-only: rows written by older
28
- // deployments are still read back — the D1 example maps a stored row straight
29
- // into this type — and an operator's timeline should not have to lie about
30
- // where a call came from. Never widen this member back into a live source.
31
- | "batch_call"
32
- | "execute_code";
33
-
34
- export type ActivityOutcome =
35
- | "success"
36
- | "error"
37
- | "timeout"
38
- | "cancelled";
39
-
40
- export type AgentFriction =
41
- | "tool_not_found"
42
- | "schema_retry"
43
- | "destructive_reroute"
44
- | "auth_required"
45
- | "result_too_large";
46
-
47
- /** Coarse recovery class derived without inspecting payloads or error prose. */
48
- export function agentFrictionForCode(
49
- code: string | undefined,
50
- ): AgentFriction | undefined {
51
- switch (code) {
52
- case "unknown_address":
53
- case "unknown_tool":
54
- case "ambiguous_tool_alias":
55
- return "tool_not_found";
56
- case "invalid_args":
57
- return "schema_retry";
58
- case "destructive_tool_requires_approval":
59
- return "destructive_reroute";
60
- case "auth_required":
61
- return "auth_required";
62
- case "result_too_large":
63
- return "result_too_large";
64
- default:
65
- return undefined;
66
- }
67
- }
68
-
69
- /**
70
- * Authenticated identity attached to an activity event. `id` is intentionally
71
- * optional: open deployments and shared bearer tokens cannot honestly identify
72
- * a person.
73
- */
74
- export interface ActivityActor {
75
- kind: string;
76
- id?: string;
77
- /**
78
- * Stable, non-secret identity-directory namespace supplied by the admitting
79
- * auth provider. It lets authorized reads resolve ids through the same
80
- * provider rather than another provider that happens to share `kind`.
81
- */
82
- namespace?: string;
83
- }
84
-
85
- /**
86
- * Privacy-minimal history of one resolved downstream connector call.
87
- *
88
- * Arguments, results, generated code, search text, and raw errors are excluded
89
- * by construction. Deployments that need a safe human summary can add a
90
- * separate, explicit connector-level feature later.
91
- */
92
- export interface ToolCallActivityEvent {
93
- schemaVersion: 1;
94
- id: string;
95
- occurredAt: string;
96
- requestId: string;
97
- actor: ActivityActor;
98
- connectorId: string;
99
- toolName: string;
100
- address: string;
101
- source: ActivityCallSource;
102
- outcome: ActivityOutcome;
103
- durationMs: number;
104
- attempts: number;
105
- /** Set only when the call actually failed; a truncated success has none. */
106
- errorCode?: string;
107
- /**
108
- * Payload-free recovery class. Usually derived from `errorCode`, but it can
109
- * also stand alone: a result too large to return inline is friction for the
110
- * agent while remaining an `outcome: "success"` call with no error code.
111
- */
112
- friction?: AgentFriction;
113
- serverName: string;
114
- serverVersion: string;
115
- deploymentId?: string;
116
- }
117
-
118
- export interface ActivityPage {
119
- events: ToolCallActivityEvent[];
120
- nextCursor?: string;
121
- }
122
-
123
- /** Display-only actor returned by the authenticated activity read API. */
124
- export interface ActivityReadActor extends ActivityActor {
125
- label?: string;
126
- }
127
-
128
- export type ActivityReadEvent = Omit<ToolCallActivityEvent, "actor"> & {
129
- actor: ActivityReadActor;
130
- };
131
-
132
- export interface ActivityReadPage {
133
- events: ActivityReadEvent[];
134
- nextCursor?: string;
135
- }
136
-
137
- /** Write-only deployments can implement only this small, vendor-neutral seam. */
138
- export interface ActivitySink {
139
- record(event: ToolCallActivityEvent): void | Promise<void>;
140
- }
141
-
142
- /** Optional read side used by Connecta's authenticated Activity UI. */
143
- export interface ActivityReader {
144
- list(options: { cursor?: string; limit: number }): Promise<ActivityPage>;
145
- }
146
-
147
- export interface ActivityStore extends ActivitySink {
148
- list?: ActivityReader["list"];
149
- }
150
-
151
- /** Reader implementations throw this for an opaque cursor they cannot decode. */
152
- export class InvalidActivityCursorError extends Error {
153
- override name = "InvalidActivityCursorError";
154
-
155
- constructor() {
156
- super("invalid activity cursor");
157
- }
158
- }
159
-
160
- export type ActivityReadGate = (
161
- actor: ActivityActor,
162
- ) => boolean | Promise<boolean>;
163
-
164
- /** Request-scoped context shared by direct, batch, and code-mode call paths. */
165
- export interface ActivityRequestContext {
166
- sink: ActivitySink;
167
- actor: ActivityActor;
168
- requestId: string;
169
- serverInfo: { name: string; version: string };
170
- deploymentId?: string;
171
- defer?: (promise: Promise<unknown>) => void;
172
- logger: Logger;
173
- }
174
-
175
- export type ActivityEventInput = Pick<
176
- ToolCallActivityEvent,
177
- | "connectorId"
178
- | "toolName"
179
- | "address"
180
- | "source"
181
- | "outcome"
182
- | "durationMs"
183
- | "attempts"
184
- | "errorCode"
185
- | "friction"
186
- >;
187
-
188
- /**
189
- * Best-effort by design: activity storage can never change a tool result.
190
- * Workers attach async sinks to waitUntil; synchronous sinks such as Analytics
191
- * Engine complete inline; Node promises remain detached from the response.
192
- */
193
- export function recordToolActivity(
194
- context: ActivityRequestContext | undefined,
195
- input: ActivityEventInput,
196
- ): void {
197
- if (!context) return;
198
- // A caller-supplied class wins because it knows something the code table
199
- // cannot: friction that belongs to a call which did not fail.
200
- const friction = input.friction ?? agentFrictionForCode(input.errorCode);
201
- const event: ToolCallActivityEvent = {
202
- schemaVersion: 1,
203
- id: crypto.randomUUID(),
204
- occurredAt: new Date().toISOString(),
205
- requestId: context.requestId,
206
- actor: context.actor,
207
- connectorId: boundedEchoText(input.connectorId, MAX_ACTIVITY_NAME_BYTES),
208
- toolName: boundedEchoText(input.toolName, MAX_ACTIVITY_NAME_BYTES),
209
- address: boundedEchoText(input.address, MAX_ACTIVITY_ADDRESS_BYTES),
210
- source: input.source,
211
- outcome: input.outcome,
212
- durationMs: Math.max(0, Math.trunc(input.durationMs)),
213
- attempts: Math.max(1, Math.trunc(input.attempts)),
214
- ...(input.errorCode ? { errorCode: input.errorCode } : {}),
215
- ...(friction ? { friction } : {}),
216
- serverName: context.serverInfo.name,
217
- serverVersion: context.serverInfo.version,
218
- ...(context.deploymentId
219
- ? { deploymentId: context.deploymentId }
220
- : {}),
221
- };
222
- try {
223
- const result = context.sink.record(event);
224
- if (!result || typeof (result as Promise<unknown>).then !== "function") {
225
- return;
226
- }
227
- const pending = Promise.resolve(result).catch((error) => {
228
- context.logger.warn("[connecta] activity record failed", error);
229
- });
230
- if (context.defer) context.defer(pending);
231
- } catch (error) {
232
- context.logger.warn("[connecta] activity record failed", error);
233
- }
234
- }