@creatornader/agent-bridge 0.2.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 (74) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +216 -0
  3. package/README.md +338 -0
  4. package/SKILL.md +24 -0
  5. package/bin/CLAUDE.md +11 -0
  6. package/bin/agent-bridge +7 -0
  7. package/clients/claude-code.json +20 -0
  8. package/clients/claude-desktop.json +20 -0
  9. package/clients/codex.json +20 -0
  10. package/clients/generic-mcp.json +13 -0
  11. package/clients/openclaw.json +13 -0
  12. package/dist/bridge-domain-BcnBeHOn.d.ts +97 -0
  13. package/dist/bridge-service-CfqMcMAn.d.ts +28 -0
  14. package/dist/bridge-store-BuGjxTn4.d.ts +61 -0
  15. package/dist/bridge.d.ts +102 -0
  16. package/dist/bridge.js +83 -0
  17. package/dist/bridge.js.map +1 -0
  18. package/dist/chunk-4KAOBC5X.js +773 -0
  19. package/dist/chunk-4KAOBC5X.js.map +1 -0
  20. package/dist/chunk-6MAZ6YLZ.js +146 -0
  21. package/dist/chunk-6MAZ6YLZ.js.map +1 -0
  22. package/dist/chunk-AXL2I7FH.js +423 -0
  23. package/dist/chunk-AXL2I7FH.js.map +1 -0
  24. package/dist/chunk-BV6W4AJK.js +211 -0
  25. package/dist/chunk-BV6W4AJK.js.map +1 -0
  26. package/dist/chunk-CKSKUBDQ.js +153 -0
  27. package/dist/chunk-CKSKUBDQ.js.map +1 -0
  28. package/dist/chunk-JOUBDRYX.js +272 -0
  29. package/dist/chunk-JOUBDRYX.js.map +1 -0
  30. package/dist/chunk-JTC5YURG.js +49 -0
  31. package/dist/chunk-JTC5YURG.js.map +1 -0
  32. package/dist/chunk-KVR4JBRF.js +80 -0
  33. package/dist/chunk-KVR4JBRF.js.map +1 -0
  34. package/dist/chunk-O26W5QHS.js +409 -0
  35. package/dist/chunk-O26W5QHS.js.map +1 -0
  36. package/dist/chunk-WEULPMUY.js +988 -0
  37. package/dist/chunk-WEULPMUY.js.map +1 -0
  38. package/dist/chunk-YD6YJATN.js +48 -0
  39. package/dist/chunk-YD6YJATN.js.map +1 -0
  40. package/dist/cli.d.ts +3 -0
  41. package/dist/cli.js +670 -0
  42. package/dist/cli.js.map +1 -0
  43. package/dist/client-config.d.ts +30 -0
  44. package/dist/client-config.js +10 -0
  45. package/dist/client-config.js.map +1 -0
  46. package/dist/client-runtime.d.ts +15 -0
  47. package/dist/client-runtime.js +15 -0
  48. package/dist/client-runtime.js.map +1 -0
  49. package/dist/gateway-BULozv3K.d.ts +54 -0
  50. package/dist/gateway-main.d.ts +2 -0
  51. package/dist/gateway-main.js +105 -0
  52. package/dist/gateway-main.js.map +1 -0
  53. package/dist/gateway.d.ts +4 -0
  54. package/dist/gateway.js +10 -0
  55. package/dist/gateway.js.map +1 -0
  56. package/dist/index.d.ts +2 -0
  57. package/dist/index.js +18 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/server.d.ts +34 -0
  60. package/dist/server.js +19 -0
  61. package/dist/server.js.map +1 -0
  62. package/dist/sqlite.d.ts +156 -0
  63. package/dist/sqlite.js +24 -0
  64. package/dist/sqlite.js.map +1 -0
  65. package/llms.txt +39 -0
  66. package/package.json +104 -0
  67. package/sql/migrations/001_schema_state.sql +34 -0
  68. package/sql/migrations/002_workspaces_agents_credentials.sql +77 -0
  69. package/sql/migrations/003_messages_receipts_deliveries.sql +116 -0
  70. package/sql/migrations/004_message_query_indexes.sql +31 -0
  71. package/sql/migrations/005_delivery_events_presence.sql +79 -0
  72. package/sql/migrations/006_legacy_shared_context_import.sql +139 -0
  73. package/sql/migrations/007_runtime_role.sql +59 -0
  74. package/sql/setup.sql +68 -0
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ BridgeService
4
+ } from "./chunk-CKSKUBDQ.js";
5
+ import {
6
+ BridgeValidationError
7
+ } from "./chunk-BV6W4AJK.js";
8
+
9
+ // src/gateway.ts
10
+ import { randomUUID } from "crypto";
11
+ import { createServer } from "http";
12
+
13
+ // src/gateway-auth.ts
14
+ import { createHash, timingSafeEqual } from "crypto";
15
+ function hashCredential(token) {
16
+ return createHash("sha256").update(token, "utf8").digest("hex");
17
+ }
18
+ var PostgresCredentialResolver = class {
19
+ constructor(db) {
20
+ this.db = db;
21
+ }
22
+ async resolve(token, signal) {
23
+ if (signal?.aborted) throw signal.reason;
24
+ const hash = hashCredential(token);
25
+ const result = await this.db.query(
26
+ `SELECT credential.id, credential.workspace_id, agent.principal
27
+ FROM agent_bridge.credentials credential
28
+ JOIN agent_bridge.agents agent
29
+ ON agent.id=credential.agent_id AND agent.workspace_id=credential.workspace_id
30
+ JOIN agent_bridge.workspaces workspace ON workspace.id=credential.workspace_id
31
+ WHERE credential.token_hash=$1 AND credential.revoked_at IS NULL
32
+ AND (credential.expires_at IS NULL OR credential.expires_at>now())
33
+ AND agent.disabled_at IS NULL AND workspace.disabled_at IS NULL
34
+ LIMIT 1`,
35
+ [hash]
36
+ );
37
+ const row = result.rows[0];
38
+ if (signal?.aborted) throw signal.reason;
39
+ return row ? { id: row.id, principal: { workspace: row.workspace_id, agent: row.principal } } : null;
40
+ }
41
+ };
42
+ function bearerToken(value) {
43
+ if (!value?.startsWith("Bearer ")) return null;
44
+ const token = value.slice(7);
45
+ if (!token || token.length > 4096 || /\s/.test(token)) return null;
46
+ return token;
47
+ }
48
+ function safeTokenHashEqual(token, expectedHash) {
49
+ const actual = Buffer.from(hashCredential(token));
50
+ const expected = Buffer.from(expectedHash);
51
+ return actual.length === expected.length && timingSafeEqual(actual, expected);
52
+ }
53
+
54
+ // src/gateway.ts
55
+ var jsonHeaders = { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" };
56
+ function send(res, status, value, requestId) {
57
+ if (res.writableEnded) return;
58
+ res.writeHead(status, { ...jsonHeaders, "x-request-id": requestId });
59
+ res.end(JSON.stringify(value));
60
+ }
61
+ function failure(res, status, code, requestId) {
62
+ send(res, status, { error: { code, requestId } }, requestId);
63
+ }
64
+ async function body(req, limit, signal) {
65
+ return new Promise((resolve, reject) => {
66
+ const chunks = [];
67
+ let size = 0;
68
+ const cleanup = () => {
69
+ req.off("data", onData);
70
+ req.off("end", onEnd);
71
+ req.off("error", onError);
72
+ signal.removeEventListener("abort", onAbort);
73
+ };
74
+ const finish = (callback) => {
75
+ cleanup();
76
+ callback();
77
+ };
78
+ const onData = (chunk) => {
79
+ const buffer = Buffer.from(chunk);
80
+ size += buffer.length;
81
+ if (size > limit) {
82
+ finish(() => reject(Object.assign(new Error("body too large"), {
83
+ status: 413,
84
+ code: "body_too_large"
85
+ })));
86
+ req.resume();
87
+ return;
88
+ }
89
+ chunks.push(buffer);
90
+ };
91
+ const onEnd = () => finish(() => {
92
+ if (!size) {
93
+ resolve({});
94
+ return;
95
+ }
96
+ try {
97
+ const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
98
+ if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") throw new Error();
99
+ resolve(parsed);
100
+ } catch {
101
+ reject(Object.assign(new Error("invalid JSON"), { status: 400, code: "malformed_json" }));
102
+ }
103
+ });
104
+ const onError = (error) => finish(() => reject(error));
105
+ const onAbort = () => {
106
+ finish(() => reject(signal.reason));
107
+ req.resume();
108
+ };
109
+ req.on("data", onData);
110
+ req.on("end", onEnd);
111
+ req.on("error", onError);
112
+ signal.addEventListener("abort", onAbort, { once: true });
113
+ if (signal.aborted) onAbort();
114
+ });
115
+ }
116
+ function numberParam(value, name) {
117
+ if (value === null) return void 0;
118
+ const result = Number(value);
119
+ if (!Number.isSafeInteger(result)) throw new BridgeValidationError(`${name} is invalid`);
120
+ return result;
121
+ }
122
+ function active(signal) {
123
+ if (signal.aborted) throw signal.reason;
124
+ }
125
+ function createGateway(options) {
126
+ const service = new BridgeService(options.store);
127
+ const metrics = { requests: 0, errors: 0, timeouts: 0, authFailures: 0 };
128
+ const limit = options.bodyLimitBytes ?? 128 * 1024;
129
+ const deadline = options.requestDeadlineMs ?? 1e4;
130
+ const origins = new Set(options.allowedOrigins ?? []);
131
+ return createServer((req, res) => {
132
+ const requestId = randomUUID();
133
+ const abort = new AbortController();
134
+ let mutationStarted = false;
135
+ metrics.requests += 1;
136
+ const timer = setTimeout(() => {
137
+ abort.abort(new Error("request deadline exceeded"));
138
+ metrics.timeouts += 1;
139
+ metrics.errors += 1;
140
+ res.once("finish", () => {
141
+ if (!req.complete) req.destroy();
142
+ });
143
+ failure(
144
+ res,
145
+ 504,
146
+ mutationStarted ? "mutation_outcome_unknown" : "request_timeout",
147
+ requestId
148
+ );
149
+ }, deadline);
150
+ void (async () => {
151
+ const url = new URL(req.url ?? "/", "http://gateway.invalid");
152
+ if (req.method === "GET" && url.pathname === "/readyz") {
153
+ let ready = false;
154
+ try {
155
+ ready = await (options.ready?.() ?? options.store.initialize().then(() => true));
156
+ } catch {
157
+ ready = false;
158
+ }
159
+ send(res, ready ? 200 : 503, { status: ready ? "ready" : "not_ready" }, requestId);
160
+ return;
161
+ }
162
+ const origin = req.headers.origin;
163
+ if (origin && !origins.has(origin)) {
164
+ failure(res, 403, "origin_forbidden", requestId);
165
+ return;
166
+ }
167
+ const token = bearerToken(req.headers.authorization);
168
+ const credential = token ? await options.credentials.resolve(token, abort.signal) : null;
169
+ active(abort.signal);
170
+ if (!credential) {
171
+ metrics.authFailures += 1;
172
+ failure(res, 401, "unauthorized", requestId);
173
+ return;
174
+ }
175
+ if (req.method === "GET" && url.pathname === "/metrics") {
176
+ res.writeHead(200, { ...jsonHeaders, "content-type": "text/plain; version=0.0.4", "x-request-id": requestId });
177
+ res.end(Object.entries(metrics).map(([key, value]) => `agent_bridge_gateway_${key}_total ${value}`).join("\n") + "\n");
178
+ return;
179
+ }
180
+ const instanceHeader = req.headers["x-agent-bridge-instance"];
181
+ const principal = {
182
+ ...credential.principal,
183
+ instance: Array.isArray(instanceHeader) ? instanceHeader[0] : instanceHeader
184
+ };
185
+ if (req.method === "GET" && url.pathname === "/v2/status") {
186
+ if (!options.store.diagnostics) {
187
+ send(res, 200, { schemaVersion: "postgres-v2", deliverySupported: false, pending: null, claimed: null, retrying: null, dead: null, principal: credential.principal }, requestId);
188
+ } else {
189
+ send(res, 200, { ...await options.store.diagnostics(principal), principal: credential.principal }, requestId);
190
+ }
191
+ return;
192
+ }
193
+ if (req.method === "POST" && req.headers["content-type"]?.split(";", 1)[0] !== "application/json") {
194
+ failure(res, 415, "unsupported_media_type", requestId);
195
+ return;
196
+ }
197
+ if (req.method === "POST" && url.pathname === "/v2/messages") {
198
+ const input = await body(req, limit, abort.signal);
199
+ active(abort.signal);
200
+ mutationStarted = true;
201
+ send(res, 201, await service.publish(principal, input), requestId);
202
+ return;
203
+ }
204
+ if (req.method === "GET" && url.pathname === "/v2/history") {
205
+ const types = url.searchParams.getAll("type");
206
+ send(res, 200, await service.history(principal, {
207
+ cursor: url.searchParams.get("cursor") ?? void 0,
208
+ limit: numberParam(url.searchParams.get("limit"), "limit"),
209
+ types: types.length ? types : void 0,
210
+ includeExpired: url.searchParams.get("includeExpired") === "true",
211
+ source: url.searchParams.get("source") ?? void 0,
212
+ since: url.searchParams.get("since") ?? void 0,
213
+ unacknowledgedBy: url.searchParams.get("unacknowledgedBy") ?? void 0,
214
+ threadId: url.searchParams.get("threadId") ?? void 0,
215
+ latest: url.searchParams.get("latest") === "true"
216
+ }), requestId);
217
+ return;
218
+ }
219
+ if (req.method === "POST" && url.pathname === "/v2/receipts") {
220
+ const input = await body(req, limit, abort.signal);
221
+ active(abort.signal);
222
+ mutationStarted = true;
223
+ send(res, 200, { recorded: await service.acknowledge(principal, input.messageIds) }, requestId);
224
+ return;
225
+ }
226
+ if (req.method === "POST" && url.pathname === "/v2/deliveries/claim") {
227
+ const input = await body(req, limit, abort.signal);
228
+ active(abort.signal);
229
+ mutationStarted = true;
230
+ send(res, 200, await service.claim(principal, input), requestId);
231
+ return;
232
+ }
233
+ if (req.method === "POST" && url.pathname === "/v2/presence/heartbeat") {
234
+ const input = await body(req, limit, abort.signal);
235
+ active(abort.signal);
236
+ mutationStarted = true;
237
+ send(res, 200, await service.heartbeat(principal, input), requestId);
238
+ return;
239
+ }
240
+ if (req.method === "GET" && url.pathname === "/v2/presence") {
241
+ send(res, 200, { agents: await service.presence(principal) }, requestId);
242
+ return;
243
+ }
244
+ const match = url.pathname.match(/^\/v2\/deliveries\/([^/]+)\/(extend|ack|nack)$/);
245
+ if (req.method === "POST" && match) {
246
+ const input = await body(req, limit, abort.signal);
247
+ active(abort.signal);
248
+ mutationStarted = true;
249
+ const [, id, action] = match;
250
+ const result = action === "extend" ? await service.extend(principal, id, input.leaseToken, input.leaseMs) : action === "ack" ? await service.ack(principal, id, input.leaseToken) : await service.nack(principal, id, input.leaseToken, input.error, input.dead, input.retryPolicy);
251
+ if (!result) failure(res, 409, "lease_conflict", requestId);
252
+ else send(res, 200, result, requestId);
253
+ return;
254
+ }
255
+ failure(res, 404, "not_found", requestId);
256
+ })().catch((error) => {
257
+ if (res.writableEnded) return;
258
+ metrics.errors += 1;
259
+ if (error instanceof BridgeValidationError) failure(res, 400, error.code, requestId);
260
+ else failure(res, error?.status ?? 500, error?.code ?? "internal_error", requestId);
261
+ }).finally(() => clearTimeout(timer));
262
+ });
263
+ }
264
+
265
+ export {
266
+ hashCredential,
267
+ PostgresCredentialResolver,
268
+ bearerToken,
269
+ safeTokenHashEqual,
270
+ createGateway
271
+ };
272
+ //# sourceMappingURL=chunk-JOUBDRYX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/gateway.ts","../src/gateway-auth.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { BridgeValidationError, type MessageDraft } from \"./bridge-domain.js\";\nimport { BridgeService } from \"./bridge-service.js\";\nimport type { BridgeStore } from \"./bridge-store.js\";\nimport { bearerToken, type CredentialResolver } from \"./gateway-auth.js\";\n\nexport interface GatewayOptions {\n store: BridgeStore;\n credentials: CredentialResolver;\n allowedOrigins?: string[];\n bodyLimitBytes?: number;\n requestDeadlineMs?: number;\n ready?: () => Promise<boolean>;\n}\n\ntype Metrics = { requests: number; errors: number; timeouts: number; authFailures: number };\nconst jsonHeaders = { \"content-type\": \"application/json; charset=utf-8\", \"cache-control\": \"no-store\" };\n\nfunction send(res: ServerResponse, status: number, value: unknown, requestId: string): void {\n if (res.writableEnded) return;\n res.writeHead(status, { ...jsonHeaders, \"x-request-id\": requestId });\n res.end(JSON.stringify(value));\n}\n\nfunction failure(res: ServerResponse, status: number, code: string, requestId: string): void {\n send(res, status, { error: { code, requestId } }, requestId);\n}\n\nasync function body(\n req: IncomingMessage,\n limit: number,\n signal: AbortSignal,\n): Promise<Record<string, any>> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n const cleanup = () => {\n req.off(\"data\", onData);\n req.off(\"end\", onEnd);\n req.off(\"error\", onError);\n signal.removeEventListener(\"abort\", onAbort);\n };\n const finish = (callback: () => void) => {\n cleanup();\n callback();\n };\n const onData = (chunk: Buffer | string) => {\n const buffer = Buffer.from(chunk);\n size += buffer.length;\n if (size > limit) {\n finish(() => reject(Object.assign(new Error(\"body too large\"), {\n status: 413,\n code: \"body_too_large\",\n })));\n req.resume();\n return;\n }\n chunks.push(buffer);\n };\n const onEnd = () => finish(() => {\n if (!size) {\n resolve({});\n return;\n }\n try {\n const parsed = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n if (!parsed || Array.isArray(parsed) || typeof parsed !== \"object\") throw new Error();\n resolve(parsed);\n } catch {\n reject(Object.assign(new Error(\"invalid JSON\"), { status: 400, code: \"malformed_json\" }));\n }\n });\n const onError = (error: Error) => finish(() => reject(error));\n const onAbort = () => {\n finish(() => reject(signal.reason));\n req.resume();\n };\n req.on(\"data\", onData);\n req.on(\"end\", onEnd);\n req.on(\"error\", onError);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n if (signal.aborted) onAbort();\n });\n}\n\nfunction numberParam(value: string | null, name: string): number | undefined {\n if (value === null) return undefined;\n const result = Number(value);\n if (!Number.isSafeInteger(result)) throw new BridgeValidationError(`${name} is invalid`);\n return result;\n}\n\nfunction active(signal: AbortSignal): void {\n if (signal.aborted) throw signal.reason;\n}\n\nexport function createGateway(options: GatewayOptions) {\n const service = new BridgeService(options.store);\n const metrics: Metrics = { requests: 0, errors: 0, timeouts: 0, authFailures: 0 };\n const limit = options.bodyLimitBytes ?? 128 * 1024;\n const deadline = options.requestDeadlineMs ?? 10_000;\n const origins = new Set(options.allowedOrigins ?? []);\n\n return createServer((req, res) => {\n const requestId = randomUUID();\n const abort = new AbortController();\n let mutationStarted = false;\n metrics.requests += 1;\n const timer = setTimeout(() => {\n abort.abort(new Error(\"request deadline exceeded\"));\n metrics.timeouts += 1; metrics.errors += 1;\n res.once(\"finish\", () => {\n if (!req.complete) req.destroy();\n });\n failure(\n res,\n 504,\n mutationStarted ? \"mutation_outcome_unknown\" : \"request_timeout\",\n requestId,\n );\n }, deadline);\n\n void (async () => {\n const url = new URL(req.url ?? \"/\", \"http://gateway.invalid\");\n if (req.method === \"GET\" && url.pathname === \"/readyz\") {\n let ready = false;\n try {\n ready = await (options.ready?.() ?? options.store.initialize().then(() => true));\n } catch {\n ready = false;\n }\n send(res, ready ? 200 : 503, { status: ready ? \"ready\" : \"not_ready\" }, requestId);\n return;\n }\n const origin = req.headers.origin;\n if (origin && !origins.has(origin)) {\n failure(res, 403, \"origin_forbidden\", requestId); return;\n }\n const token = bearerToken(req.headers.authorization);\n const credential = token ? await options.credentials.resolve(token, abort.signal) : null;\n active(abort.signal);\n if (!credential) {\n metrics.authFailures += 1;\n failure(res, 401, \"unauthorized\", requestId); return;\n }\n if (req.method === \"GET\" && url.pathname === \"/metrics\") {\n res.writeHead(200, { ...jsonHeaders, \"content-type\": \"text/plain; version=0.0.4\", \"x-request-id\": requestId });\n res.end(Object.entries(metrics).map(([key, value]) => `agent_bridge_gateway_${key}_total ${value}`).join(\"\\n\") + \"\\n\");\n return;\n }\n const instanceHeader = req.headers[\"x-agent-bridge-instance\"];\n const principal = {\n ...credential.principal,\n instance: Array.isArray(instanceHeader) ? instanceHeader[0] : instanceHeader,\n };\n\n if (req.method === \"GET\" && url.pathname === \"/v2/status\") {\n if (!options.store.diagnostics) {\n send(res, 200, { schemaVersion: \"postgres-v2\", deliverySupported: false, pending: null, claimed: null, retrying: null, dead: null, principal: credential.principal }, requestId);\n } else {\n send(res, 200, { ...await options.store.diagnostics(principal), principal: credential.principal }, requestId);\n }\n return;\n }\n\n if (req.method === \"POST\" && req.headers[\"content-type\"]?.split(\";\", 1)[0] !== \"application/json\") {\n failure(res, 415, \"unsupported_media_type\", requestId); return;\n }\n\n if (req.method === \"POST\" && url.pathname === \"/v2/messages\") {\n const input = await body(req, limit, abort.signal) as unknown as MessageDraft;\n active(abort.signal);\n mutationStarted = true;\n send(res, 201, await service.publish(principal, input), requestId); return;\n }\n if (req.method === \"GET\" && url.pathname === \"/v2/history\") {\n const types = url.searchParams.getAll(\"type\");\n send(res, 200, await service.history(principal, {\n cursor: url.searchParams.get(\"cursor\") ?? undefined,\n limit: numberParam(url.searchParams.get(\"limit\"), \"limit\"),\n types: types.length ? types : undefined,\n includeExpired: url.searchParams.get(\"includeExpired\") === \"true\",\n source: url.searchParams.get(\"source\") ?? undefined,\n since: url.searchParams.get(\"since\") ?? undefined,\n unacknowledgedBy: url.searchParams.get(\"unacknowledgedBy\") ?? undefined,\n threadId: url.searchParams.get(\"threadId\") ?? undefined,\n latest: url.searchParams.get(\"latest\") === \"true\",\n }), requestId); return;\n }\n if (req.method === \"POST\" && url.pathname === \"/v2/receipts\") {\n const input = await body(req, limit, abort.signal);\n active(abort.signal);\n mutationStarted = true;\n send(res, 200, { recorded: await service.acknowledge(principal, input.messageIds) }, requestId); return;\n }\n if (req.method === \"POST\" && url.pathname === \"/v2/deliveries/claim\") {\n const input = await body(req, limit, abort.signal);\n active(abort.signal);\n mutationStarted = true;\n send(res, 200, await service.claim(principal, input), requestId); return;\n }\n if (req.method === \"POST\" && url.pathname === \"/v2/presence/heartbeat\") {\n const input = await body(req, limit, abort.signal);\n active(abort.signal);\n mutationStarted = true;\n send(res, 200, await service.heartbeat(principal, input), requestId); return;\n }\n if (req.method === \"GET\" && url.pathname === \"/v2/presence\") {\n send(res, 200, { agents: await service.presence(principal) }, requestId); return;\n }\n const match = url.pathname.match(/^\\/v2\\/deliveries\\/([^/]+)\\/(extend|ack|nack)$/);\n if (req.method === \"POST\" && match) {\n const input = await body(req, limit, abort.signal);\n active(abort.signal);\n mutationStarted = true;\n const [, id, action] = match;\n const result = action === \"extend\"\n ? await service.extend(principal, id, input.leaseToken, input.leaseMs)\n : action === \"ack\"\n ? await service.ack(principal, id, input.leaseToken)\n : await service.nack(principal, id, input.leaseToken, input.error, input.dead, input.retryPolicy);\n if (!result) failure(res, 409, \"lease_conflict\", requestId);\n else send(res, 200, result, requestId);\n return;\n }\n failure(res, 404, \"not_found\", requestId);\n })().catch((error: any) => {\n if (res.writableEnded) return;\n metrics.errors += 1;\n if (error instanceof BridgeValidationError) failure(res, 400, error.code, requestId);\n else failure(res, error?.status ?? 500, error?.code ?? \"internal_error\", requestId);\n }).finally(() => clearTimeout(timer));\n });\n}\n","import { createHash, timingSafeEqual } from \"node:crypto\";\nimport type { BridgePrincipal } from \"./bridge-domain.js\";\nimport type { PgQueryable } from \"./postgres-bridge-store.js\";\n\nexport interface AuthenticatedCredential {\n id: string;\n principal: BridgePrincipal;\n}\n\nexport interface CredentialResolver {\n resolve(token: string, signal?: AbortSignal): Promise<AuthenticatedCredential | null>;\n}\n\nexport function hashCredential(token: string): string {\n return createHash(\"sha256\").update(token, \"utf8\").digest(\"hex\");\n}\n\nexport class PostgresCredentialResolver implements CredentialResolver {\n constructor(private readonly db: PgQueryable) {}\n\n async resolve(token: string, signal?: AbortSignal): Promise<AuthenticatedCredential | null> {\n if (signal?.aborted) throw signal.reason;\n const hash = hashCredential(token);\n const result = await this.db.query<{\n id: string; workspace_id: string; principal: string;\n }>(\n `SELECT credential.id, credential.workspace_id, agent.principal\n FROM agent_bridge.credentials credential\n JOIN agent_bridge.agents agent\n ON agent.id=credential.agent_id AND agent.workspace_id=credential.workspace_id\n JOIN agent_bridge.workspaces workspace ON workspace.id=credential.workspace_id\n WHERE credential.token_hash=$1 AND credential.revoked_at IS NULL\n AND (credential.expires_at IS NULL OR credential.expires_at>now())\n AND agent.disabled_at IS NULL AND workspace.disabled_at IS NULL\n LIMIT 1`,\n [hash],\n );\n const row = result.rows[0];\n if (signal?.aborted) throw signal.reason;\n return row ? { id: row.id, principal: { workspace: row.workspace_id, agent: row.principal } } : null;\n }\n}\n\nexport function bearerToken(value: string | undefined): string | null {\n if (!value?.startsWith(\"Bearer \")) return null;\n const token = value.slice(7);\n if (!token || token.length > 4096 || /\\s/.test(token)) return null;\n return token;\n}\n\nexport function safeTokenHashEqual(token: string, expectedHash: string): boolean {\n const actual = Buffer.from(hashCredential(token));\n const expected = Buffer.from(expectedHash);\n return actual.length === expected.length && timingSafeEqual(actual, expected);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ HttpBridgeStore,
4
+ LegacySupabaseRestStore
5
+ } from "./chunk-AXL2I7FH.js";
6
+ import {
7
+ SQLiteBridgeStore,
8
+ SQLiteEdgeStore,
9
+ SyncingBridgeStore
10
+ } from "./chunk-WEULPMUY.js";
11
+ import {
12
+ BridgeService
13
+ } from "./chunk-CKSKUBDQ.js";
14
+
15
+ // src/client-runtime.ts
16
+ import { chmodSync, mkdirSync } from "fs";
17
+ import { basename, dirname } from "path";
18
+ function createStore(config) {
19
+ if (config.provider === "local") {
20
+ if (config.databasePath !== ":memory:") {
21
+ const directory = dirname(config.databasePath);
22
+ mkdirSync(directory, { recursive: true, mode: 448 });
23
+ if (basename(directory) === ".agent-bridge") chmodSync(directory, 448);
24
+ }
25
+ return new SQLiteBridgeStore(config.databasePath);
26
+ }
27
+ if (config.provider === "gateway") {
28
+ const directory = dirname(config.edgeDatabasePath);
29
+ mkdirSync(directory, { recursive: true, mode: 448 });
30
+ if (basename(directory) === ".agent-bridge") chmodSync(directory, 448);
31
+ const remote = new HttpBridgeStore({ baseUrl: config.url, token: config.credential, principal: config.principal });
32
+ const edge = new SQLiteEdgeStore(config.edgeDatabasePath, { endpoint: config.url, principal: config.principal });
33
+ return new SyncingBridgeStore(edge, remote, config.principal);
34
+ }
35
+ return new LegacySupabaseRestStore(config.url, config.credential);
36
+ }
37
+ async function createClientRuntime(config) {
38
+ const store = createStore(config);
39
+ await store.initialize();
40
+ return { config, store, service: new BridgeService(store), close: async () => {
41
+ await store.close?.();
42
+ } };
43
+ }
44
+
45
+ export {
46
+ createStore,
47
+ createClientRuntime
48
+ };
49
+ //# sourceMappingURL=chunk-JTC5YURG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client-runtime.ts"],"sourcesContent":["import { chmodSync, mkdirSync } from \"node:fs\";\nimport { basename, dirname } from \"node:path\";\nimport { BridgeService } from \"./bridge-service.js\";\nimport type { BridgeStore } from \"./bridge-store.js\";\nimport { HttpBridgeStore } from \"./http-bridge-store.js\";\nimport { SQLiteEdgeStore } from \"./sqlite-edge-store.js\";\nimport { SyncingBridgeStore } from \"./syncing-bridge-store.js\";\nimport { LegacySupabaseRestStore } from \"./legacy-supabase-store.js\";\nimport { SQLiteBridgeStore } from \"./sqlite-bridge-store.js\";\nimport type { ClientConfig } from \"./client-config.js\";\n\nexport interface ClientRuntime { config: ClientConfig; store: BridgeStore; service: BridgeService; close(): Promise<void>; }\n\nexport function createStore(config: ClientConfig): BridgeStore {\n if (config.provider === \"local\") {\n if (config.databasePath !== \":memory:\") {\n const directory = dirname(config.databasePath);\n mkdirSync(directory, { recursive: true, mode: 0o700 });\n if (basename(directory) === \".agent-bridge\") chmodSync(directory, 0o700);\n }\n return new SQLiteBridgeStore(config.databasePath);\n }\n if (config.provider === \"gateway\") {\n const directory = dirname(config.edgeDatabasePath);\n mkdirSync(directory, { recursive: true, mode: 0o700 });\n if (basename(directory) === \".agent-bridge\") chmodSync(directory, 0o700);\n const remote = new HttpBridgeStore({ baseUrl: config.url!, token: config.credential!, principal: config.principal });\n const edge = new SQLiteEdgeStore(config.edgeDatabasePath, { endpoint: config.url!, principal: config.principal });\n return new SyncingBridgeStore(edge, remote, config.principal);\n }\n return new LegacySupabaseRestStore(config.url!, config.credential!);\n}\n\nexport async function createClientRuntime(config: ClientConfig): Promise<ClientRuntime> {\n const store = createStore(config);\n await store.initialize();\n return { config, store, service: new BridgeService(store), close: async () => { await store.close?.(); } };\n}\n"],"mappings":";;;;;;;;;;;;;;;AAAA,SAAS,WAAW,iBAAiB;AACrC,SAAS,UAAU,eAAe;AAY3B,SAAS,YAAY,QAAmC;AAC7D,MAAI,OAAO,aAAa,SAAS;AAC/B,QAAI,OAAO,iBAAiB,YAAY;AACtC,YAAM,YAAY,QAAQ,OAAO,YAAY;AAC7C,gBAAU,WAAW,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACrD,UAAI,SAAS,SAAS,MAAM,gBAAiB,WAAU,WAAW,GAAK;AAAA,IACzE;AACA,WAAO,IAAI,kBAAkB,OAAO,YAAY;AAAA,EAClD;AACA,MAAI,OAAO,aAAa,WAAW;AACjC,UAAM,YAAY,QAAQ,OAAO,gBAAgB;AACjD,cAAU,WAAW,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACrD,QAAI,SAAS,SAAS,MAAM,gBAAiB,WAAU,WAAW,GAAK;AACvE,UAAM,SAAS,IAAI,gBAAgB,EAAE,SAAS,OAAO,KAAM,OAAO,OAAO,YAAa,WAAW,OAAO,UAAU,CAAC;AACnH,UAAM,OAAO,IAAI,gBAAgB,OAAO,kBAAkB,EAAE,UAAU,OAAO,KAAM,WAAW,OAAO,UAAU,CAAC;AAChH,WAAO,IAAI,mBAAmB,MAAM,QAAQ,OAAO,SAAS;AAAA,EAC9D;AACA,SAAO,IAAI,wBAAwB,OAAO,KAAM,OAAO,UAAW;AACpE;AAEA,eAAsB,oBAAoB,QAA8C;AACtF,QAAM,QAAQ,YAAY,MAAM;AAChC,QAAM,MAAM,WAAW;AACvB,SAAO,EAAE,QAAQ,OAAO,SAAS,IAAI,cAAc,KAAK,GAAG,OAAO,YAAY;AAAE,UAAM,MAAM,QAAQ;AAAA,EAAG,EAAE;AAC3G;","names":[]}
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/client-config.ts
4
+ import { existsSync, readFileSync } from "fs";
5
+ import { createHash } from "crypto";
6
+ import { homedir } from "os";
7
+ import { join } from "path";
8
+ function pathComponent(value) {
9
+ const readable = value.replace(/[^A-Za-z0-9._-]+/g, "_").slice(0, 48) || "_";
10
+ const suffix = createHash("sha256").update(value).digest("hex").slice(0, 12);
11
+ return `${readable}-${suffix}`;
12
+ }
13
+ function clean(value) {
14
+ return value?.trim() || void 0;
15
+ }
16
+ function unquote(value) {
17
+ const input = value.trim();
18
+ if (input.length > 1 && (input[0] === '"' && input[input.length - 1] === '"' || input[0] === "'" && input[input.length - 1] === "'")) return input.slice(1, -1);
19
+ return input;
20
+ }
21
+ function readClientConfigFile(path) {
22
+ if (!existsSync(path)) return {};
23
+ const result = {};
24
+ for (const raw of readFileSync(path, "utf8").split(/\r?\n/)) {
25
+ const line = raw.trim();
26
+ if (!line || line.startsWith("#")) continue;
27
+ const separator = line.indexOf("=");
28
+ if (separator > 0) result[line.slice(0, separator).trim()] = unquote(line.slice(separator + 1));
29
+ }
30
+ return result;
31
+ }
32
+ function resolveClientConfig(env = process.env, explicitAgent) {
33
+ const home = clean(env.HOME) ?? homedir();
34
+ const configPath = clean(env.AGENT_BRIDGE_CONFIG) ?? join(home, ".agent-bridge", "config");
35
+ const file = readClientConfigFile(configPath);
36
+ const value = (key) => clean(env[key]) ?? clean(file[key]);
37
+ const rawProvider = value("AGENT_BRIDGE_PROVIDER") ?? (value("AGENT_BRIDGE_TOKEN") ? "gateway" : value("AGENT_BRIDGE_URL") ? "legacy-supabase" : "local");
38
+ const provider = rawProvider === "legacy" || rawProvider === "supabase" ? "legacy-supabase" : rawProvider;
39
+ if (!["local", "gateway", "legacy-supabase"].includes(provider)) throw new Error(`Unsupported AGENT_BRIDGE_PROVIDER: ${rawProvider}`);
40
+ const configuredAgent = clean(env.AGENT_BRIDGE_AGENT);
41
+ const requestedAgent = clean(explicitAgent);
42
+ if (configuredAgent && requestedAgent && configuredAgent !== requestedAgent) {
43
+ throw new Error(`source must match AGENT_BRIDGE_AGENT (${configuredAgent}); got ${requestedAgent}`);
44
+ }
45
+ const agent = requestedAgent ?? configuredAgent;
46
+ if (!agent) throw new Error("AGENT_BRIDGE_AGENT is required");
47
+ const url = value("AGENT_BRIDGE_URL");
48
+ const credential = provider === "gateway" ? value("AGENT_BRIDGE_TOKEN") : value("AGENT_BRIDGE_KEY");
49
+ if (provider !== "local" && (!url || !credential)) throw new Error(`${provider} requires AGENT_BRIDGE_URL and ${provider === "gateway" ? "AGENT_BRIDGE_TOKEN" : "AGENT_BRIDGE_KEY"}`);
50
+ const workspace = value("AGENT_BRIDGE_WORKSPACE") ?? (provider === "legacy-supabase" ? "*" : "default");
51
+ const instance = clean(env.AGENT_BRIDGE_INSTANCE);
52
+ const databasePath = value("AGENT_BRIDGE_DB") ?? join(home, ".agent-bridge", "bridge.sqlite3");
53
+ const edgeDatabasePath = value("AGENT_BRIDGE_EDGE_DB") ?? join(home, ".agent-bridge", "edge.sqlite3");
54
+ const cursorScope = createHash("sha256").update(`${provider}\0${provider === "local" ? databasePath : url}`).digest("hex").slice(0, 16);
55
+ return {
56
+ provider,
57
+ principal: { workspace, agent, instance },
58
+ url,
59
+ credential,
60
+ databasePath,
61
+ edgeDatabasePath,
62
+ cursorPath: value("AGENT_BRIDGE_CURSOR") ?? join(
63
+ home,
64
+ ".agent-bridge",
65
+ "cursors",
66
+ pathComponent(provider),
67
+ cursorScope,
68
+ pathComponent(workspace),
69
+ pathComponent(agent),
70
+ pathComponent(instance ?? "default")
71
+ ),
72
+ configPath
73
+ };
74
+ }
75
+
76
+ export {
77
+ readClientConfigFile,
78
+ resolveClientConfig
79
+ };
80
+ //# sourceMappingURL=chunk-KVR4JBRF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client-config.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { createHash } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { BridgePrincipal } from \"./bridge-domain.js\";\n\nexport type ClientProvider = \"local\" | \"gateway\" | \"legacy-supabase\";\n\nexport interface ClientEnvironment extends NodeJS.ProcessEnv {\n AGENT_BRIDGE_PROVIDER?: string;\n AGENT_BRIDGE_URL?: string;\n AGENT_BRIDGE_KEY?: string;\n AGENT_BRIDGE_TOKEN?: string;\n AGENT_BRIDGE_AGENT?: string;\n AGENT_BRIDGE_WORKSPACE?: string;\n AGENT_BRIDGE_INSTANCE?: string;\n AGENT_BRIDGE_DB?: string;\n AGENT_BRIDGE_EDGE_DB?: string;\n AGENT_BRIDGE_CONFIG?: string;\n AGENT_BRIDGE_CURSOR?: string;\n}\n\nexport interface ClientConfig {\n provider: ClientProvider;\n principal: BridgePrincipal;\n url?: string;\n credential?: string;\n databasePath: string;\n edgeDatabasePath: string;\n cursorPath: string;\n configPath: string;\n}\n\nfunction pathComponent(value: string): string {\n const readable = value.replace(/[^A-Za-z0-9._-]+/g, \"_\").slice(0, 48) || \"_\";\n const suffix = createHash(\"sha256\").update(value).digest(\"hex\").slice(0, 12);\n return `${readable}-${suffix}`;\n}\n\nfunction clean(value: string | undefined): string | undefined {\n return value?.trim() || undefined;\n}\n\nfunction unquote(value: string): string {\n const input = value.trim();\n if (input.length > 1 && ((input[0] === '\"' && input[input.length - 1] === '\"') || (input[0] === \"'\" && input[input.length - 1] === \"'\"))) return input.slice(1, -1);\n return input;\n}\n\nexport function readClientConfigFile(path: string): Record<string, string> {\n if (!existsSync(path)) return {};\n const result: Record<string, string> = {};\n for (const raw of readFileSync(path, \"utf8\").split(/\\r?\\n/)) {\n const line = raw.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const separator = line.indexOf(\"=\");\n if (separator > 0) result[line.slice(0, separator).trim()] = unquote(line.slice(separator + 1));\n }\n return result;\n}\n\nexport function resolveClientConfig(\n env: ClientEnvironment = process.env,\n explicitAgent?: string,\n): ClientConfig {\n const home = clean(env.HOME) ?? homedir();\n const configPath = clean(env.AGENT_BRIDGE_CONFIG) ?? join(home, \".agent-bridge\", \"config\");\n const file = readClientConfigFile(configPath);\n const value = (key: keyof ClientEnvironment) => clean(env[key]) ?? clean(file[key]);\n const rawProvider = value(\"AGENT_BRIDGE_PROVIDER\") ?? (value(\"AGENT_BRIDGE_TOKEN\") ? \"gateway\" : value(\"AGENT_BRIDGE_URL\") ? \"legacy-supabase\" : \"local\");\n const provider = rawProvider === \"legacy\" || rawProvider === \"supabase\" ? \"legacy-supabase\" : rawProvider;\n if (!([\"local\", \"gateway\", \"legacy-supabase\"] as string[]).includes(provider)) throw new Error(`Unsupported AGENT_BRIDGE_PROVIDER: ${rawProvider}`);\n // Identity belongs to the active client process. A shared config file must\n // never make one runtime impersonate the client that initialized the bridge.\n const configuredAgent = clean(env.AGENT_BRIDGE_AGENT);\n const requestedAgent = clean(explicitAgent);\n if (configuredAgent && requestedAgent && configuredAgent !== requestedAgent) {\n throw new Error(`source must match AGENT_BRIDGE_AGENT (${configuredAgent}); got ${requestedAgent}`);\n }\n const agent = requestedAgent ?? configuredAgent;\n if (!agent) throw new Error(\"AGENT_BRIDGE_AGENT is required\");\n const url = value(\"AGENT_BRIDGE_URL\");\n const credential = provider === \"gateway\" ? value(\"AGENT_BRIDGE_TOKEN\") : value(\"AGENT_BRIDGE_KEY\");\n if (provider !== \"local\" && (!url || !credential)) throw new Error(`${provider} requires AGENT_BRIDGE_URL and ${provider === \"gateway\" ? \"AGENT_BRIDGE_TOKEN\" : \"AGENT_BRIDGE_KEY\"}`);\n const workspace = value(\"AGENT_BRIDGE_WORKSPACE\") ?? (provider === \"legacy-supabase\" ? \"*\" : \"default\");\n const instance = clean(env.AGENT_BRIDGE_INSTANCE);\n const databasePath = value(\"AGENT_BRIDGE_DB\") ?? join(home, \".agent-bridge\", \"bridge.sqlite3\");\n const edgeDatabasePath = value(\"AGENT_BRIDGE_EDGE_DB\") ?? join(home, \".agent-bridge\", \"edge.sqlite3\");\n const cursorScope = createHash(\"sha256\")\n .update(`${provider}\\0${provider === \"local\" ? databasePath : url}`)\n .digest(\"hex\")\n .slice(0, 16);\n return {\n provider: provider as ClientProvider,\n principal: { workspace, agent, instance },\n url,\n credential,\n databasePath,\n edgeDatabasePath,\n cursorPath: value(\"AGENT_BRIDGE_CURSOR\") ?? join(\n home,\n \".agent-bridge\",\n \"cursors\",\n pathComponent(provider),\n cursorScope,\n pathComponent(workspace),\n pathComponent(agent),\n pathComponent(instance ?? \"default\"),\n ),\n configPath,\n };\n}\n"],"mappings":"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","names":[]}