@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
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
- }