@zackbart/connecta 0.15.1 → 0.16.1

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 (335) hide show
  1. package/AGENTS.md +40 -20
  2. package/CHANGELOG.md +653 -0
  3. package/README.md +25 -5
  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 +41 -5
  27. package/dist/catalog.d.ts +11 -1
  28. package/dist/catalog.js +20 -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 +30 -3
  42. package/dist/errors.js +1 -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 +261 -299
  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 +92 -10
  129. package/documentation/code-first-exploration.md +2 -2
  130. package/documentation/code-mode.md +14 -14
  131. package/documentation/connector-guides.md +172 -5
  132. package/documentation/connectors.md +89 -0
  133. package/documentation/linear.md +20 -2
  134. package/documentation/mcp-ui-design.md +2 -2
  135. package/documentation/meta-tools.md +7 -1
  136. package/documentation/mixpanel.md +42 -9
  137. package/documentation/notion.md +23 -2
  138. package/documentation/operations.md +325 -5
  139. package/documentation/operator-ui.md +120 -5
  140. package/documentation/provider-audit.md +160 -0
  141. package/documentation/provider-conventions.md +667 -0
  142. package/documentation/request-admission.md +167 -5
  143. package/documentation/rich-output-design.md +1 -2
  144. package/documentation/stripe.md +15 -0
  145. package/documentation/upgrading.md +454 -0
  146. package/ethos.md +14 -9
  147. package/examples/worker/README.md +102 -12
  148. package/examples/worker/src/index.ts +46 -1
  149. package/examples/worker/wrangler.jsonc +14 -1
  150. package/package.json +10 -6
  151. package/templates/node/.dockerignore +10 -0
  152. package/templates/node/.env.example +40 -3
  153. package/templates/node/AGENTS.md +16 -1
  154. package/templates/node/Dockerfile +49 -0
  155. package/templates/node/README.md +117 -2
  156. package/templates/node/docker-compose.yml +39 -0
  157. package/templates/node/package.json +1 -1
  158. package/templates/node/src/file-activity.ts +182 -0
  159. package/templates/node/src/index.ts +80 -4
  160. package/assets/connecta-clay-hero.png +0 -0
  161. package/dist/access-tokens.d.ts.map +0 -1
  162. package/dist/access-tokens.js.map +0 -1
  163. package/dist/activity.d.ts.map +0 -1
  164. package/dist/activity.js.map +0 -1
  165. package/dist/apps-shell.d.ts.map +0 -1
  166. package/dist/apps-shell.js.map +0 -1
  167. package/dist/auth/bearer.d.ts.map +0 -1
  168. package/dist/auth/bearer.js.map +0 -1
  169. package/dist/auth/clerk.d.ts.map +0 -1
  170. package/dist/auth/clerk.js.map +0 -1
  171. package/dist/auth/downstream-oauth.d.ts.map +0 -1
  172. package/dist/auth/downstream-oauth.js.map +0 -1
  173. package/dist/call-admission.d.ts.map +0 -1
  174. package/dist/call-admission.js.map +0 -1
  175. package/dist/catalog-fingerprint.d.ts.map +0 -1
  176. package/dist/catalog-fingerprint.js.map +0 -1
  177. package/dist/catalog-limits.d.ts.map +0 -1
  178. package/dist/catalog-limits.js.map +0 -1
  179. package/dist/catalog-service.d.ts.map +0 -1
  180. package/dist/catalog-service.js.map +0 -1
  181. package/dist/catalog.d.ts.map +0 -1
  182. package/dist/catalog.js.map +0 -1
  183. package/dist/concurrency.d.ts.map +0 -1
  184. package/dist/concurrency.js.map +0 -1
  185. package/dist/connector-scope.d.ts.map +0 -1
  186. package/dist/connector-scope.js.map +0 -1
  187. package/dist/connectors/api.d.ts.map +0 -1
  188. package/dist/connectors/api.js.map +0 -1
  189. package/dist/connectors/remote-mcp.d.ts.map +0 -1
  190. package/dist/connectors/remote-mcp.js.map +0 -1
  191. package/dist/credentials.d.ts.map +0 -1
  192. package/dist/credentials.js.map +0 -1
  193. package/dist/errors.d.ts.map +0 -1
  194. package/dist/errors.js.map +0 -1
  195. package/dist/execute.d.ts.map +0 -1
  196. package/dist/execute.js.map +0 -1
  197. package/dist/executor-admission.d.ts.map +0 -1
  198. package/dist/executor-admission.js.map +0 -1
  199. package/dist/executor-result.d.ts.map +0 -1
  200. package/dist/executor-result.js.map +0 -1
  201. package/dist/executors/quickjs-child.d.ts.map +0 -1
  202. package/dist/executors/quickjs-child.js.map +0 -1
  203. package/dist/executors/quickjs-protocol.d.ts.map +0 -1
  204. package/dist/executors/quickjs-protocol.js.map +0 -1
  205. package/dist/executors/quickjs-runtime.d.ts.map +0 -1
  206. package/dist/executors/quickjs-runtime.js.map +0 -1
  207. package/dist/executors/quickjs.d.ts.map +0 -1
  208. package/dist/executors/quickjs.js.map +0 -1
  209. package/dist/favicon.d.ts.map +0 -1
  210. package/dist/favicon.js.map +0 -1
  211. package/dist/index.d.ts.map +0 -1
  212. package/dist/index.js.map +0 -1
  213. package/dist/invocation.d.ts.map +0 -1
  214. package/dist/invocation.js.map +0 -1
  215. package/dist/json-schema.d.ts.map +0 -1
  216. package/dist/json-schema.js.map +0 -1
  217. package/dist/mcp-result.d.ts.map +0 -1
  218. package/dist/mcp-result.js.map +0 -1
  219. package/dist/meta-tools.d.ts.map +0 -1
  220. package/dist/meta-tools.js.map +0 -1
  221. package/dist/node.d.ts.map +0 -1
  222. package/dist/node.js.map +0 -1
  223. package/dist/operator-ui/generated.d.ts.map +0 -1
  224. package/dist/operator-ui/generated.js.map +0 -1
  225. package/dist/operator-ui/model.d.ts.map +0 -1
  226. package/dist/operator-ui/model.js.map +0 -1
  227. package/dist/providers/cloudflare.d.ts.map +0 -1
  228. package/dist/providers/cloudflare.js.map +0 -1
  229. package/dist/providers/linear.d.ts.map +0 -1
  230. package/dist/providers/linear.js.map +0 -1
  231. package/dist/providers/mixpanel.d.ts.map +0 -1
  232. package/dist/providers/mixpanel.js.map +0 -1
  233. package/dist/providers/notion.d.ts.map +0 -1
  234. package/dist/providers/notion.js.map +0 -1
  235. package/dist/providers/stripe.d.ts.map +0 -1
  236. package/dist/providers/stripe.js.map +0 -1
  237. package/dist/registry.d.ts.map +0 -1
  238. package/dist/registry.js.map +0 -1
  239. package/dist/retired-toolkits.d.ts.map +0 -1
  240. package/dist/retired-toolkits.js.map +0 -1
  241. package/dist/routes/access-tokens.d.ts.map +0 -1
  242. package/dist/routes/access-tokens.js.map +0 -1
  243. package/dist/routes/activity.d.ts.map +0 -1
  244. package/dist/routes/activity.js.map +0 -1
  245. package/dist/routes/credentials.d.ts.map +0 -1
  246. package/dist/routes/credentials.js.map +0 -1
  247. package/dist/routes/mcp.d.ts.map +0 -1
  248. package/dist/routes/mcp.js.map +0 -1
  249. package/dist/routes/oauth.d.ts.map +0 -1
  250. package/dist/routes/oauth.js.map +0 -1
  251. package/dist/routes/shared.d.ts.map +0 -1
  252. package/dist/routes/shared.js.map +0 -1
  253. package/dist/routes/ui.d.ts.map +0 -1
  254. package/dist/routes/ui.js.map +0 -1
  255. package/dist/server.d.ts.map +0 -1
  256. package/dist/server.js.map +0 -1
  257. package/dist/skills.d.ts.map +0 -1
  258. package/dist/skills.js.map +0 -1
  259. package/dist/storage/file.d.ts.map +0 -1
  260. package/dist/storage/file.js.map +0 -1
  261. package/dist/storage/memory.d.ts.map +0 -1
  262. package/dist/storage/memory.js.map +0 -1
  263. package/dist/timeout.d.ts.map +0 -1
  264. package/dist/timeout.js.map +0 -1
  265. package/dist/tool-safety.d.ts.map +0 -1
  266. package/dist/tool-safety.js.map +0 -1
  267. package/dist/types.d.ts.map +0 -1
  268. package/dist/types.js.map +0 -1
  269. package/dist/ui.d.ts.map +0 -1
  270. package/dist/ui.js.map +0 -1
  271. package/dist/validate.d.ts.map +0 -1
  272. package/dist/validate.js.map +0 -1
  273. package/dist/version.d.ts.map +0 -1
  274. package/dist/version.js.map +0 -1
  275. package/examples/node/README.md +0 -52
  276. package/examples/node/src/index.ts +0 -71
  277. package/src/access-tokens.ts +0 -289
  278. package/src/activity.ts +0 -234
  279. package/src/apps-shell.ts +0 -370
  280. package/src/auth/bearer.ts +0 -61
  281. package/src/auth/clerk.ts +0 -553
  282. package/src/auth/downstream-oauth.ts +0 -575
  283. package/src/call-admission.ts +0 -521
  284. package/src/catalog-fingerprint.ts +0 -52
  285. package/src/catalog-limits.ts +0 -11
  286. package/src/catalog-service.ts +0 -1153
  287. package/src/catalog.ts +0 -790
  288. package/src/concurrency.ts +0 -39
  289. package/src/connector-scope.ts +0 -66
  290. package/src/connectors/api.ts +0 -159
  291. package/src/connectors/remote-mcp.ts +0 -1090
  292. package/src/credentials.ts +0 -428
  293. package/src/errors.ts +0 -364
  294. package/src/execute.ts +0 -1406
  295. package/src/executor-admission.ts +0 -328
  296. package/src/executor-result.ts +0 -107
  297. package/src/executors/quickjs-child.ts +0 -172
  298. package/src/executors/quickjs-protocol.ts +0 -99
  299. package/src/executors/quickjs-runtime.ts +0 -470
  300. package/src/executors/quickjs.ts +0 -776
  301. package/src/favicon.ts +0 -53
  302. package/src/index.ts +0 -666
  303. package/src/invocation.ts +0 -655
  304. package/src/json-schema.ts +0 -11
  305. package/src/mcp-result.ts +0 -43
  306. package/src/meta-tools.ts +0 -1607
  307. package/src/node.ts +0 -206
  308. package/src/operator-ui/browser.css +0 -423
  309. package/src/operator-ui/browser.ts +0 -1281
  310. package/src/operator-ui/generated.ts +0 -4
  311. package/src/operator-ui/model.ts +0 -111
  312. package/src/providers/cloudflare.ts +0 -3999
  313. package/src/providers/linear.ts +0 -302
  314. package/src/providers/mixpanel.ts +0 -229
  315. package/src/providers/notion.ts +0 -1879
  316. package/src/providers/stripe.ts +0 -307
  317. package/src/registry.ts +0 -1085
  318. package/src/retired-toolkits.ts +0 -14
  319. package/src/routes/access-tokens.ts +0 -115
  320. package/src/routes/activity.ts +0 -215
  321. package/src/routes/credentials.ts +0 -254
  322. package/src/routes/mcp.ts +0 -429
  323. package/src/routes/oauth.ts +0 -322
  324. package/src/routes/shared.ts +0 -247
  325. package/src/routes/ui.ts +0 -152
  326. package/src/server.ts +0 -195
  327. package/src/skills.ts +0 -271
  328. package/src/storage/file.ts +0 -123
  329. package/src/storage/memory.ts +0 -44
  330. package/src/timeout.ts +0 -84
  331. package/src/tool-safety.ts +0 -15
  332. package/src/types.ts +0 -523
  333. package/src/ui.ts +0 -744
  334. package/src/validate.ts +0 -387
  335. package/src/version.ts +0 -7
package/src/node.ts DELETED
@@ -1,206 +0,0 @@
1
- import { createServer } from "node:http";
2
- import type { IncomingMessage, Server, ServerResponse } from "node:http";
3
- import { Readable } from "node:stream";
4
- import { pipeline } from "node:stream/promises";
5
- import type { Connecta } from "./index.js";
6
-
7
- export { fileStorage } from "./storage/file.js";
8
- export type { FileStorageOptions } from "./storage/file.js";
9
-
10
- /** 10 MiB. Tool arguments are JSON; nothing legitimate approaches this. */
11
- const DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024;
12
-
13
- export interface ListenOptions {
14
- port: number;
15
- /** Interface to bind. Defaults to Node's own (all interfaces). */
16
- host?: string;
17
- /** Reject request bodies larger than this with 413. Default 10 MiB. */
18
- maxBodyBytes?: number;
19
- /**
20
- * Stop accepting connections on SIGTERM/SIGINT, let in-flight requests
21
- * finish, drain deferred work, then exit. Default true — Docker sends
22
- * SIGTERM on every `compose up` recreate and Node's default is to die
23
- * mid-request. Set false to install your own handlers.
24
- */
25
- gracefulShutdown?: boolean;
26
- /** Shutdown deadline before forcing exit. Default 10s (Docker's own grace). */
27
- shutdownTimeoutMs?: number;
28
- }
29
-
30
- const BODY_TOO_LARGE = Symbol("body-too-large");
31
-
32
- async function toRequest(
33
- req: IncomingMessage,
34
- maxBodyBytes: number,
35
- signal: AbortSignal,
36
- ): Promise<Request | typeof BODY_TOO_LARGE> {
37
- const host = req.headers.host ?? "localhost";
38
- const proto =
39
- (req.headers["x-forwarded-proto"] as string | undefined) ?? "http";
40
- const url = `${proto}://${host}${req.url ?? "/"}`;
41
- const method = req.method ?? "GET";
42
- const headers = new Headers();
43
- for (const [key, value] of Object.entries(req.headers)) {
44
- if (value == null) continue;
45
- headers.set(key, Array.isArray(value) ? value.join(", ") : value);
46
- }
47
- let body: Uint8Array | undefined;
48
- if (method !== "GET" && method !== "HEAD") {
49
- const chunks: Buffer[] = [];
50
- let size = 0;
51
- for await (const chunk of req) {
52
- size += (chunk as Buffer).length;
53
- // Stop reading as soon as the cap is passed rather than buffering the
54
- // rest of a body we have already decided to reject.
55
- if (size > maxBodyBytes) return BODY_TOO_LARGE;
56
- chunks.push(chunk as Buffer);
57
- }
58
- if (chunks.length) body = new Uint8Array(Buffer.concat(chunks));
59
- }
60
- return new Request(url, {
61
- method,
62
- headers,
63
- body,
64
- signal,
65
- // Required by Node's fetch when sending a body stream.
66
- ...(body ? { duplex: "half" } : {}),
67
- } as RequestInit);
68
- }
69
-
70
- async function writeResponse(
71
- res: ServerResponse,
72
- response: Response,
73
- ): Promise<void> {
74
- res.statusCode = response.status;
75
- response.headers.forEach((value, key) => res.setHeader(key, value));
76
- if (!response.body) {
77
- res.end();
78
- return;
79
- }
80
- // Stream rather than buffer: a connector-served route may return a large
81
- // proxied file body, and materializing it would hold the whole thing in
82
- // memory before the first byte reaches the client.
83
- await pipeline(
84
- Readable.fromWeb(response.body as Parameters<typeof Readable.fromWeb>[0]),
85
- res,
86
- );
87
- }
88
-
89
- /**
90
- * Serve a Connecta over node:http. Thin adapter: IncomingMessage → Request,
91
- * Response → ServerResponse, plus the process-level concerns a long-running
92
- * container needs (body cap, response streaming, graceful shutdown). No
93
- * dependencies beyond node core.
94
- */
95
- export function listen(
96
- connecta: Connecta,
97
- portOrOptions: number | ListenOptions,
98
- ): Server {
99
- const opts: ListenOptions =
100
- typeof portOrOptions === "number" ? { port: portOrOptions } : portOrOptions;
101
- const maxBodyBytes = opts.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
102
-
103
- // Node has no ExecutionContext, so deferred work (activity sinks) would
104
- // otherwise be an untracked floating promise. Track it here and drain it on
105
- // shutdown so both runtimes behave the same.
106
- const pending = new Set<Promise<unknown>>();
107
- const ctx = {
108
- waitUntil(promise: Promise<unknown>): void {
109
- pending.add(promise);
110
- void promise.finally(() => pending.delete(promise));
111
- },
112
- };
113
-
114
- const server = createServer((req, res) => {
115
- void (async () => {
116
- const controller = new AbortController();
117
- const abortRequest = () => {
118
- if (!controller.signal.aborted) {
119
- controller.abort(new Error("HTTP client disconnected."));
120
- }
121
- };
122
- const abortIncompleteRequest = () => {
123
- if (!req.complete) abortRequest();
124
- };
125
- const abortIncompleteResponse = () => {
126
- if (!res.writableFinished) abortRequest();
127
- };
128
- req.once("aborted", abortRequest);
129
- req.once("close", abortIncompleteRequest);
130
- res.once("close", abortIncompleteResponse);
131
- try {
132
- const request = await toRequest(
133
- req,
134
- maxBodyBytes,
135
- controller.signal,
136
- );
137
- if (request === BODY_TOO_LARGE) {
138
- res.statusCode = 413;
139
- // The request stream was abandoned mid-body, so this connection
140
- // cannot be reused — say so rather than leaving the client to
141
- // discover it as a reset.
142
- res.setHeader("Connection", "close");
143
- res.end("Payload Too Large");
144
- return;
145
- }
146
- const response = await connecta.fetch(request, undefined, ctx);
147
- await writeResponse(res, response);
148
- } catch (error) {
149
- // A client that deliberately went away can make either the request
150
- // handler or response pipeline reject. The request's abort signal is
151
- // the authoritative classification: there is no caller left to answer,
152
- // and logging an expected disconnect would turn cancellation into an
153
- // error-log spam vector.
154
- if (controller.signal.aborted) return;
155
- // A headless deployment has nothing else to go on; never swallow this.
156
- console.error("[connecta] request failed", error);
157
- if (res.headersSent) {
158
- // Mid-stream failure: appending an error string here would corrupt
159
- // the partial body the client has already begun reading. Cutting
160
- // the connection is the only honest signal left.
161
- res.destroy();
162
- return;
163
- }
164
- res.statusCode = 500;
165
- res.end("Internal Server Error");
166
- } finally {
167
- req.removeListener("aborted", abortRequest);
168
- req.removeListener("close", abortIncompleteRequest);
169
- res.removeListener("close", abortIncompleteResponse);
170
- }
171
- })();
172
- });
173
-
174
- if (opts.gracefulShutdown !== false) {
175
- const timeoutMs = opts.shutdownTimeoutMs ?? 10_000;
176
- let shuttingDown = false;
177
- const shutdown = (signal: string): void => {
178
- if (shuttingDown) return;
179
- shuttingDown = true;
180
- console.info(`[connecta] ${signal} received, draining`);
181
- const forced = setTimeout(() => {
182
- console.warn("[connecta] shutdown timed out, exiting");
183
- process.exit(1);
184
- }, timeoutMs);
185
- forced.unref?.();
186
- // Stop queued/running sandbox work now, not after server.close has
187
- // waited for the very requests those children are holding open.
188
- const executorClose = connecta.close();
189
- // close() alone waits on idle keep-alive sockets, so a single browser
190
- // tab would stall every shutdown until the force-exit deadline.
191
- server.closeIdleConnections();
192
- server.close(() => {
193
- void Promise.allSettled([...pending, executorClose]).then(() => {
194
- clearTimeout(forced);
195
- process.exit(0);
196
- });
197
- });
198
- };
199
- process.on("SIGTERM", () => shutdown("SIGTERM"));
200
- process.on("SIGINT", () => shutdown("SIGINT"));
201
- }
202
-
203
- if (opts.host) server.listen(opts.port, opts.host);
204
- else server.listen(opts.port);
205
- return server;
206
- }
@@ -1,423 +0,0 @@
1
- :root {
2
- color-scheme: light;
3
- --ink: #000;
4
- --paper: #fff;
5
- --rule: #ccc;
6
- --muted: #666;
7
- --trace: #f5f5f5;
8
- --shell: 70rem;
9
- --pad: 1rem;
10
- --gap: 1.5rem;
11
- --sans: "Helvetica Neue", Helvetica, Arial, sans-serif;
12
- --mono: ui-monospace, "SF Mono", Menlo, Monaco, "Cascadia Code", Consolas,
13
- monospace;
14
- }
15
- * { border-radius: 0; box-sizing: border-box; }
16
- html {
17
- background: var(--paper);
18
- color: var(--ink);
19
- font-family: var(--sans);
20
- font-size: 16px;
21
- line-height: 1.5;
22
- -webkit-font-smoothing: antialiased;
23
- text-rendering: optimizeLegibility;
24
- }
25
- body { margin: 0; min-height: 100vh; }
26
- ::selection { background: var(--ink); color: var(--paper); }
27
- :is(h1, h2, h3, p, ul, ol) { margin: 0; padding: 0; }
28
- :is(h1, h2, h3) { font-size: inherit; font-weight: 400; }
29
- :is(ul, ol) { list-style: none; }
30
- a { color: inherit; }
31
- button, input { font: inherit; }
32
- button {
33
- background: none;
34
- border: 0;
35
- color: inherit;
36
- cursor: pointer;
37
- margin: 0;
38
- padding: 0;
39
- text-align: left;
40
- }
41
- button:disabled { cursor: wait; opacity: .5; }
42
- input {
43
- background: var(--paper);
44
- border: 1px solid var(--rule);
45
- color: var(--ink);
46
- min-height: 2rem;
47
- padding: .2rem .5rem;
48
- }
49
- input:focus-visible { outline-offset: -1px; }
50
- :is(a, button, input, summary):focus-visible {
51
- outline: 1px solid var(--ink);
52
- }
53
- :is(a, button, summary):focus-visible { outline-offset: 2px; }
54
- .skip-link {
55
- background: var(--paper);
56
- left: var(--pad);
57
- padding: .5rem;
58
- position: fixed;
59
- top: -4rem;
60
- z-index: 10;
61
- }
62
- .skip-link:focus { top: var(--pad); }
63
-
64
- .shell {
65
- margin: 0 auto;
66
- max-width: var(--shell);
67
- padding-left: var(--pad);
68
- padding-right: var(--pad);
69
- }
70
- .pgrid {
71
- column-gap: var(--gap);
72
- display: grid;
73
- grid-template-columns: repeat(3, minmax(0, 1fr));
74
- row-gap: 1rem;
75
- }
76
- .pcap { grid-column: 1; }
77
- .pbody { grid-column: 2 / -1; min-width: 0; }
78
- .cap, .meta { color: var(--muted); font-size: .9em; }
79
- .mono { font-family: var(--mono); font-size: .78rem; }
80
- .hidden { display: none !important; }
81
- .visually-hidden {
82
- clip: rect(0 0 0 0);
83
- clip-path: inset(50%);
84
- height: 1px;
85
- overflow: hidden;
86
- position: absolute;
87
- white-space: nowrap;
88
- width: 1px;
89
- }
90
-
91
- .masthead {
92
- align-items: start;
93
- padding-bottom: var(--pad);
94
- padding-top: var(--pad);
95
- }
96
- .brand {
97
- font-weight: 500;
98
- grid-column: 1;
99
- text-decoration: none;
100
- }
101
- .mast-nav {
102
- display: flex;
103
- gap: var(--gap);
104
- grid-column: 2 / -1;
105
- justify-content: space-between;
106
- min-width: 0;
107
- }
108
- .mast-actions {
109
- display: flex;
110
- gap: var(--gap);
111
- justify-content: flex-end;
112
- min-width: 0;
113
- }
114
- .page-nav,
115
- .session-actions {
116
- display: flex;
117
- flex-wrap: wrap;
118
- gap: .5rem var(--gap);
119
- }
120
- .mast-actions :is(a, button) {
121
- align-items: center;
122
- display: inline-flex;
123
- min-height: 2rem;
124
- }
125
- .navlink,
126
- .linklike {
127
- text-decoration: underline;
128
- text-decoration-thickness: 1.5px;
129
- text-underline-offset: .22em;
130
- }
131
- .navlink { text-decoration-color: transparent; }
132
- .navlink:hover,
133
- .navlink:focus-visible,
134
- .navlink[aria-current="page"] { text-decoration-color: currentColor; }
135
- .linklike { text-decoration-color: currentColor; }
136
- .linklike:hover,
137
- .linklike:focus-visible { text-decoration-color: transparent; }
138
-
139
- .page { padding-bottom: 5rem; }
140
- .lead { margin-top: 6rem; }
141
- .section,
142
- .section + .section { margin-top: 3rem; }
143
- .lead-copy,
144
- .body-copy { max-width: 34em; }
145
- .lead-copy > * + *,
146
- .body-copy > * + * { margin-top: 1.5rem; }
147
- .row,
148
- .actions {
149
- align-items: center;
150
- display: flex;
151
- flex-wrap: wrap;
152
- gap: var(--gap);
153
- }
154
- .row input { flex: 1; min-width: 12rem; }
155
-
156
- .gate-actions { margin-top: 1.5rem; }
157
- #err { margin-top: 1.5rem; text-decoration: underline; }
158
-
159
- .endpoint { border-bottom: 1px solid var(--rule); border-top: 1px solid var(--rule); }
160
- .endpoint-row {
161
- align-items: baseline;
162
- display: flex;
163
- gap: var(--gap);
164
- min-width: 0;
165
- padding: .75rem 0;
166
- }
167
- .endpoint-row code {
168
- flex: 1;
169
- min-width: 0;
170
- overflow-x: auto;
171
- white-space: nowrap;
172
- }
173
- .endpoint-row button { flex: none; }
174
-
175
- .connector-tools { border-bottom: 1px solid var(--rule); }
176
- .toolbar { margin-bottom: 1.5rem; }
177
- .toolbar input { flex-basis: 18rem; }
178
- #notice { margin-bottom: .75rem; }
179
- #notice:empty { display: none; }
180
- #notice:not(:empty) { text-decoration: underline; }
181
- #oauthNotice { margin-bottom: .75rem; }
182
- #oauthNotice:empty { display: none; }
183
- .error-notice, .msg { text-decoration: underline; }
184
- .card,
185
- .credential-card,
186
- .activity-item {
187
- padding-left: 1.25rem;
188
- position: relative;
189
- }
190
- .card::before,
191
- .credential-card::before,
192
- .activity-item::before {
193
- background: var(--rule);
194
- bottom: 0;
195
- content: "";
196
- left: .25rem;
197
- position: absolute;
198
- top: 0;
199
- width: 1px;
200
- }
201
- .card { border-top: 1px solid var(--rule); padding-bottom: .75rem; padding-top: .75rem; }
202
- .connector-head {
203
- display: grid;
204
- gap: var(--gap);
205
- grid-template-columns: minmax(0, 2fr) minmax(10rem, 1fr);
206
- }
207
- .connector-title {
208
- align-items: baseline;
209
- display: flex;
210
- gap: .5rem;
211
- }
212
- .connector-title .dot,
213
- .activity-stamp .dot {
214
- margin-left: -1.25rem;
215
- }
216
- .activity-stamp {
217
- align-items: baseline;
218
- display: flex;
219
- gap: .75rem;
220
- }
221
- .card h2 { overflow-wrap: anywhere; }
222
- .connector-state { text-align: right; }
223
- .dot {
224
- background: var(--paper);
225
- border: 1px solid var(--ink);
226
- display: inline-block;
227
- flex: none;
228
- height: .5rem;
229
- width: .5rem;
230
- z-index: 1;
231
- }
232
- .dot.ok { background: var(--ink); }
233
- .dot.auth_required {
234
- background: linear-gradient(90deg, var(--ink) 50%, var(--paper) 50%);
235
- }
236
- .connector-description { margin-top: .25rem; max-width: 40rem; }
237
- .connector-message,
238
- .connector-auth { margin-top: .75rem; }
239
- .credential-ledger { border-bottom: 1px solid var(--rule); }
240
- .credential-card { border-top: 1px solid var(--rule); padding-bottom: .75rem; padding-top: .75rem; }
241
- .credential-head {
242
- align-items: baseline;
243
- display: flex;
244
- flex-wrap: wrap;
245
- gap: .25rem var(--gap);
246
- justify-content: space-between;
247
- }
248
- .credential-copy { margin-top: .25rem; max-width: 40rem; }
249
- .credential-field-summary {
250
- border-top: 1px solid var(--rule);
251
- margin-top: .75rem;
252
- }
253
- .credential-field-summary > div {
254
- border-bottom: 1px solid var(--rule);
255
- display: flex;
256
- flex-wrap: wrap;
257
- gap: .25rem var(--gap);
258
- justify-content: space-between;
259
- padding: .5rem 0;
260
- }
261
- .credential-actions { display: flex; flex-wrap: wrap; gap: var(--gap); margin-top: .75rem; }
262
- .credential-actions button,
263
- .credential-form button,
264
- .activity-controls button,
265
- .activity-more {
266
- align-items: center;
267
- display: inline-flex;
268
- min-height: 2.75rem;
269
- }
270
- .credential-form {
271
- align-items: center;
272
- display: flex;
273
- flex-wrap: wrap;
274
- gap: .75rem var(--gap);
275
- margin-top: .75rem;
276
- }
277
- .credential-form > input { flex: 1 1 18rem; }
278
- .credential-fields { display: grid; flex: 1 1 100%; gap: .75rem; }
279
- .credential-field {
280
- align-items: center;
281
- display: grid;
282
- gap: var(--gap);
283
- grid-template-columns: minmax(9rem, 12rem) 1fr;
284
- }
285
- .credential-field input { min-width: 0; width: 100%; }
286
- .danger { text-decoration-style: double; }
287
-
288
- .token-create {
289
- border-bottom: 1px solid var(--rule);
290
- border-top: 1px solid var(--rule);
291
- padding: .75rem 0;
292
- }
293
- .token-create > label {
294
- display: block;
295
- margin-bottom: .5rem;
296
- }
297
- .token-create input { flex: 1 1 18rem; }
298
- .token-create button,
299
- .token-card button,
300
- .token-reveal button {
301
- align-items: center;
302
- display: inline-flex;
303
- min-height: 2.75rem;
304
- }
305
- .token-reveal {
306
- background: var(--ink);
307
- color: var(--paper);
308
- margin-top: 1.5rem;
309
- padding: 1rem 1.25rem;
310
- }
311
- .token-reveal .meta,
312
- .token-reveal .cap { color: #bbb; }
313
- .token-reveal-head,
314
- .token-card-head {
315
- align-items: baseline;
316
- display: flex;
317
- flex-wrap: wrap;
318
- gap: .25rem var(--gap);
319
- justify-content: space-between;
320
- }
321
- .token-secret {
322
- border-bottom: 1px solid #555;
323
- border-top: 1px solid #555;
324
- margin-top: .75rem;
325
- }
326
- .token-secret code {
327
- color: var(--paper);
328
- user-select: all;
329
- }
330
- .token-ledger {
331
- border-bottom: 1px solid var(--rule);
332
- margin-top: 1.5rem;
333
- }
334
- .token-card {
335
- border-top: 1px solid var(--rule);
336
- padding: .75rem 0 .75rem 1.25rem;
337
- position: relative;
338
- }
339
- .token-card::before {
340
- background: var(--ink);
341
- bottom: 0;
342
- content: "";
343
- left: .25rem;
344
- position: absolute;
345
- top: 0;
346
- width: 1px;
347
- }
348
- .token-card.revoked { color: var(--muted); }
349
- .token-card.revoked::before { background: var(--rule); }
350
-
351
- details { margin-top: .75rem; }
352
- summary { cursor: pointer; list-style: none; width: max-content; }
353
- summary::-webkit-details-marker { display: none; }
354
- .tool-list { border-bottom: 1px solid var(--rule); margin-top: .5rem; }
355
- .tool {
356
- border-top: 1px solid var(--rule);
357
- display: grid;
358
- gap: .25rem var(--gap);
359
- grid-template-columns: minmax(12rem, 1fr) minmax(0, 2fr);
360
- padding: .5rem 0;
361
- }
362
- .tool code { font-family: var(--mono); font-size: .78rem; overflow-wrap: anywhere; }
363
- .tool .td { color: var(--muted); font-size: .9em; }
364
- .empty { border-top: 1px solid var(--rule); padding: .75rem 0; }
365
-
366
- .activity-copy { margin-bottom: 1.5rem; }
367
- .activity-controls { margin-bottom: 1.5rem; }
368
- .activity-controls input { flex: 1 1 18rem; }
369
- #activityNotice { margin-bottom: .75rem; }
370
- .activity-ledger { border-bottom: 1px solid var(--rule); }
371
- .activity-item {
372
- border-top: 1px solid var(--rule);
373
- display: grid;
374
- gap: .25rem var(--gap);
375
- grid-template-columns: minmax(9rem, .85fr) minmax(12rem, 1.4fr) minmax(8rem, .9fr);
376
- padding-bottom: .75rem;
377
- padding-top: .75rem;
378
- }
379
- .activity-time,
380
- .activity-actor,
381
- .activity-detail { color: var(--muted); font-size: .82rem; }
382
- .activity-actor-id { color: var(--muted); margin-top: .1rem; }
383
- .activity-address { font-family: var(--mono); font-size: .78rem; overflow-wrap: anywhere; }
384
- .activity-outcome { font-size: .9em; }
385
- .activity-item.error .activity-outcome,
386
- .activity-item.timeout .activity-outcome,
387
- .activity-item.cancelled .activity-outcome { text-decoration: underline; }
388
- .activity-empty { border-top: 1px solid var(--rule); padding: .75rem 0; }
389
- .activity-more { margin-top: .75rem; }
390
- .unavailable {
391
- background: var(--trace);
392
- border-bottom: 1px solid var(--rule);
393
- border-top: 1px solid var(--rule);
394
- padding: .75rem;
395
- }
396
-
397
- @media (prefers-reduced-motion: reduce) {
398
- html:focus-within { scroll-behavior: auto; }
399
- }
400
-
401
- @media (max-width: 36.99rem) {
402
- .pgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
403
- .pcap,
404
- .pbody { grid-column: 1 / -1; }
405
- .masthead .brand { grid-column: 1; }
406
- .mast-nav { grid-column: 1 / -1; grid-row: 2; justify-content: flex-start; }
407
- .product { display: none; }
408
- .mast-actions {
409
- align-items: flex-start;
410
- flex-direction: column;
411
- font-size: .875rem;
412
- gap: .25rem;
413
- }
414
- .lead { margin-top: 4rem; }
415
- .section,
416
- .section + .section { margin-top: 2.5rem; }
417
- .connector-head,
418
- .tool,
419
- .activity-item { grid-template-columns: 1fr; }
420
- .connector-state { text-align: left; }
421
- .credential-field { align-items: start; grid-template-columns: 1fr; gap: .25rem; }
422
- input { min-height: 2.75rem; }
423
- }