@opentag/dispatcher 0.1.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.
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # @opentag/dispatcher
2
+
3
+ Embeddable dispatcher service for OpenTag.
4
+
5
+ Use this package when you want to host the OpenTag dispatcher inside another Node or Hono-compatible service instead of running `@opentag/dispatcher-app`.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @opentag/dispatcher
11
+ ```
12
+
13
+ ## Exports
14
+
15
+ - `createDispatcherApp`: creates the Hono app that exposes the OpenTag dispatcher API.
16
+ - `createGitHubCallbackSink`: posts callback messages to GitHub issue or PR comments.
17
+ - `createSlackCallbackSink`: posts callback messages to Slack threads through `chat.postMessage`.
18
+ - `createCompositeCallbackSink`: fans callback delivery out to multiple sinks.
19
+ - `CallbackMessage`, `CallbackSink`: callback delivery contracts.
20
+
21
+ ## Example
22
+
23
+ ```ts
24
+ import {
25
+ createCompositeCallbackSink,
26
+ createDispatcherApp,
27
+ createGitHubCallbackSink,
28
+ createSlackCallbackSink
29
+ } from "@opentag/dispatcher";
30
+
31
+ export const dispatcher = createDispatcherApp({
32
+ databasePath: "opentag.db",
33
+ pairingToken: process.env.OPENTAG_PAIRING_TOKEN,
34
+ callbackSink: createCompositeCallbackSink([
35
+ createGitHubCallbackSink({ token: process.env.OPENTAG_GITHUB_TOKEN }),
36
+ createSlackCallbackSink({ botToken: process.env.OPENTAG_SLACK_BOT_TOKEN })
37
+ ])
38
+ });
39
+ ```
40
+
41
+ ## API Shape
42
+
43
+ The app exposes `/healthz` and `/v1/*` dispatcher endpoints for runners, repository bindings, Slack channel bindings, runs, progress, heartbeats, completion, and audit event lookup.
44
+
45
+ When `pairingToken` is set, every `/v1/*` endpoint requires:
46
+
47
+ ```text
48
+ Authorization: Bearer <pairingToken>
49
+ ```
50
+
51
+ ## Stability
52
+
53
+ The Hono app factory and callback sink interfaces are public API. Individual HTTP endpoint semantics should remain backward compatible within a major version.
@@ -0,0 +1,12 @@
1
+ import type { CallbackSink } from "./server.js";
2
+ export type FetchLike = typeof fetch;
3
+ export declare function createGitHubCallbackSink(input: {
4
+ token?: string;
5
+ fetchImpl?: FetchLike;
6
+ }): CallbackSink;
7
+ export declare function createSlackCallbackSink(input: {
8
+ botToken?: string;
9
+ fetchImpl?: FetchLike;
10
+ }): CallbackSink;
11
+ export declare function createCompositeCallbackSink(sinks: CallbackSink[]): CallbackSink;
12
+ //# sourceMappingURL=callbacks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callbacks.d.ts","sourceRoot":"","sources":["../src/callbacks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC;AAErC,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,YAAY,CAwBvG;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,YAAY,CA+BzG;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,YAAY,CAQ/E"}
@@ -0,0 +1,3 @@
1
+ export * from "./callbacks.js";
2
+ export * from "./server.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,295 @@
1
+ // src/callbacks.ts
2
+ import { parseSlackThreadKey } from "@opentag/slack";
3
+ function createGitHubCallbackSink(input) {
4
+ const fetchImpl = input.fetchImpl ?? fetch;
5
+ return {
6
+ async deliver(message) {
7
+ if (message.provider !== "github") return;
8
+ if (!input.token) return;
9
+ const response = await fetchImpl(message.uri, {
10
+ method: "POST",
11
+ headers: {
12
+ accept: "application/vnd.github+json",
13
+ authorization: `Bearer ${input.token}`,
14
+ "content-type": "application/json",
15
+ "x-github-api-version": "2022-11-28"
16
+ },
17
+ body: JSON.stringify({ body: message.body })
18
+ });
19
+ if (!response.ok) {
20
+ throw new Error(`deliver GitHub callback failed: ${response.status} ${await response.text()}`);
21
+ }
22
+ }
23
+ };
24
+ }
25
+ function createSlackCallbackSink(input) {
26
+ const fetchImpl = input.fetchImpl ?? fetch;
27
+ return {
28
+ async deliver(message) {
29
+ if (message.provider !== "slack") return;
30
+ if (!input.botToken) return;
31
+ const thread = parseSlackThreadKey(message.threadKey ?? "");
32
+ const response = await fetchImpl(message.uri, {
33
+ method: "POST",
34
+ headers: {
35
+ authorization: `Bearer ${input.botToken}`,
36
+ "content-type": "application/json"
37
+ },
38
+ body: JSON.stringify({
39
+ channel: thread.channelId,
40
+ text: message.body,
41
+ thread_ts: thread.threadTs
42
+ })
43
+ });
44
+ if (!response.ok) {
45
+ throw new Error(`deliver Slack callback failed: ${response.status} ${await response.text()}`);
46
+ }
47
+ const body = await response.json();
48
+ if (body.ok === false) {
49
+ throw new Error(`deliver Slack callback failed: ${body.error ?? "unknown_error"}`);
50
+ }
51
+ }
52
+ };
53
+ }
54
+ function createCompositeCallbackSink(sinks) {
55
+ return {
56
+ async deliver(message) {
57
+ for (const sink of sinks) {
58
+ await sink.deliver(message);
59
+ }
60
+ }
61
+ };
62
+ }
63
+
64
+ // src/server.ts
65
+ import { OpenTagEventSchema, OpenTagRunResultSchema } from "@opentag/core";
66
+ import { renderAcknowledgement, renderFinalResult, renderProgress } from "@opentag/github";
67
+ import { createOpenTagRepository, migrateSchema } from "@opentag/store";
68
+ import Database from "better-sqlite3";
69
+ import { drizzle } from "drizzle-orm/better-sqlite3";
70
+ import { Hono } from "hono";
71
+ import { z } from "zod";
72
+ var CreateRunnerSchema = z.object({
73
+ runnerId: z.string().min(1),
74
+ name: z.string().min(1)
75
+ });
76
+ var CreateRepoBindingSchema = z.object({
77
+ provider: z.string().min(1),
78
+ owner: z.string().min(1),
79
+ repo: z.string().min(1),
80
+ runnerId: z.string().min(1),
81
+ workspacePath: z.string().min(1).optional(),
82
+ defaultExecutor: z.string().min(1).optional(),
83
+ allowedActors: z.array(z.string().min(1)).optional()
84
+ });
85
+ var CreateSlackChannelBindingSchema = z.object({
86
+ teamId: z.string().min(1),
87
+ channelId: z.string().min(1),
88
+ owner: z.string().min(1),
89
+ repo: z.string().min(1)
90
+ });
91
+ var CreateRunSchema = z.object({
92
+ runId: z.string().min(1),
93
+ event: OpenTagEventSchema
94
+ });
95
+ var CompleteRunSchema = z.object({
96
+ result: OpenTagRunResultSchema
97
+ });
98
+ var ProgressSchema = z.object({
99
+ type: z.string().min(1).optional(),
100
+ message: z.string().min(1),
101
+ at: z.string().datetime().optional()
102
+ });
103
+ function repoKeyFromEvent(event) {
104
+ const owner = event.metadata["owner"];
105
+ const repo = event.metadata["repo"];
106
+ if (typeof owner !== "string" || typeof repo !== "string") return null;
107
+ return {
108
+ provider: typeof event.metadata["repoProvider"] === "string" ? event.metadata["repoProvider"] : "github",
109
+ owner,
110
+ repo
111
+ };
112
+ }
113
+ function isWriteCapable(event) {
114
+ return event.permissions.some((permission) => ["repo:write", "pr:create", "pr:update"].includes(permission.scope));
115
+ }
116
+ function actorIsAllowed(event, allowedActors) {
117
+ if (!allowedActors?.length) return true;
118
+ return allowedActors.includes(event.actor.handle ?? "") || allowedActors.includes(event.actor.providerUserId);
119
+ }
120
+ var noopCallbackSink = {
121
+ async deliver() {
122
+ return;
123
+ }
124
+ };
125
+ async function deliverAndAudit(input) {
126
+ await input.sink.deliver(input.message);
127
+ await input.repo.appendRunEvent({
128
+ runId: input.message.runId,
129
+ type: `callback.${input.message.kind}.delivered`,
130
+ payload: input.message
131
+ });
132
+ }
133
+ function isAuthorized(request, pairingToken) {
134
+ if (!pairingToken) return true;
135
+ return request.headers.get("authorization") === `Bearer ${pairingToken}`;
136
+ }
137
+ function createDispatcherApp(input) {
138
+ const sqlite = new Database(input.databasePath);
139
+ migrateSchema(sqlite);
140
+ const repo = createOpenTagRepository(drizzle(sqlite));
141
+ const app = new Hono();
142
+ const callbackSink = input.callbackSink ?? noopCallbackSink;
143
+ app.get("/healthz", (c) => c.json({ ok: true }));
144
+ app.use("/v1/*", async (c, next) => {
145
+ if (!isAuthorized(c.req.raw, input.pairingToken)) {
146
+ return c.json({ error: "unauthorized" }, 401);
147
+ }
148
+ await next();
149
+ });
150
+ app.post("/v1/runners", async (c) => {
151
+ const parsed = CreateRunnerSchema.parse(await c.req.json());
152
+ await repo.registerRunner(parsed);
153
+ return c.json({ ok: true }, 201);
154
+ });
155
+ app.post("/v1/repo-bindings", async (c) => {
156
+ const parsed = CreateRepoBindingSchema.parse(await c.req.json());
157
+ await repo.createRepoBinding({
158
+ provider: parsed.provider,
159
+ owner: parsed.owner,
160
+ repo: parsed.repo,
161
+ runnerId: parsed.runnerId,
162
+ ...parsed.workspacePath ? { workspacePath: parsed.workspacePath } : {},
163
+ ...parsed.defaultExecutor ? { defaultExecutor: parsed.defaultExecutor } : {},
164
+ ...parsed.allowedActors?.length ? { allowedActors: parsed.allowedActors } : {}
165
+ });
166
+ return c.json({ ok: true }, 201);
167
+ });
168
+ app.get("/v1/repo-bindings/:provider/:owner/:repo", async (c) => {
169
+ const binding = await repo.getRepoBinding({
170
+ provider: c.req.param("provider"),
171
+ owner: c.req.param("owner"),
172
+ repo: c.req.param("repo")
173
+ });
174
+ if (!binding) return c.json({ error: "repo_binding_not_found" }, 404);
175
+ return c.json({ binding });
176
+ });
177
+ app.post("/v1/slack-channel-bindings", async (c) => {
178
+ const parsed = CreateSlackChannelBindingSchema.parse(await c.req.json());
179
+ await repo.createSlackChannelBinding(parsed);
180
+ return c.json({ ok: true }, 201);
181
+ });
182
+ app.get("/v1/slack-channel-bindings/:teamId/:channelId", async (c) => {
183
+ const binding = await repo.getSlackChannelBinding({
184
+ teamId: c.req.param("teamId"),
185
+ channelId: c.req.param("channelId")
186
+ });
187
+ if (!binding) return c.json({ error: "slack_channel_binding_not_found" }, 404);
188
+ return c.json({ binding });
189
+ });
190
+ app.post("/v1/runs", async (c) => {
191
+ const parsed = CreateRunSchema.parse(await c.req.json());
192
+ const repoKey = repoKeyFromEvent(parsed.event);
193
+ if (!repoKey) {
194
+ return c.json({ error: "repo_context_missing" }, 422);
195
+ }
196
+ const binding = await repo.getRepoBinding(repoKey);
197
+ if (!binding) {
198
+ return c.json({ error: "repo_not_bound" }, 403);
199
+ }
200
+ if (isWriteCapable(parsed.event) && !actorIsAllowed(parsed.event, binding.allowedActors)) {
201
+ return c.json({ error: "actor_not_allowed_for_write" }, 403);
202
+ }
203
+ const run = await repo.createRun({ id: parsed.runId, event: parsed.event });
204
+ await deliverAndAudit({
205
+ repo,
206
+ sink: callbackSink,
207
+ message: {
208
+ runId: run.id,
209
+ kind: "acknowledgement",
210
+ provider: parsed.event.callback.provider,
211
+ uri: parsed.event.callback.uri,
212
+ body: renderAcknowledgement(run.id),
213
+ ...parsed.event.callback.threadKey ? { threadKey: parsed.event.callback.threadKey } : {}
214
+ }
215
+ });
216
+ return c.json({ run }, 201);
217
+ });
218
+ app.post("/v1/runners/:runnerId/claim", async (c) => {
219
+ const claimed = await repo.claimNextRun({ runnerId: c.req.param("runnerId"), leaseSeconds: 60 });
220
+ if (!claimed) return c.body(null, 204);
221
+ return c.json(claimed, 200);
222
+ });
223
+ app.post("/v1/runners/:runnerId/runs/:runId/heartbeat", async (c) => {
224
+ const ok = await repo.heartbeat({ runnerId: c.req.param("runnerId"), runId: c.req.param("runId") });
225
+ if (!ok) return c.json({ error: "run_not_claimed_by_runner" }, 404);
226
+ return c.json({ ok: true });
227
+ });
228
+ app.post("/v1/runs/:runId/running", async (c) => {
229
+ const body = z.object({ executor: z.string().min(1) }).parse(await c.req.json());
230
+ await repo.markRunning({ runId: c.req.param("runId"), executor: body.executor });
231
+ return c.json({ ok: true });
232
+ });
233
+ app.post("/v1/runs/:runId/progress", async (c) => {
234
+ const runId = c.req.param("runId");
235
+ const body = ProgressSchema.parse(await c.req.json());
236
+ const stored = await repo.getRun({ runId });
237
+ if (!stored) return c.json({ error: "run_not_found" }, 404);
238
+ await repo.recordProgress({
239
+ runId,
240
+ message: body.message,
241
+ ...body.type ? { type: body.type } : {},
242
+ ...body.at ? { at: body.at } : {}
243
+ });
244
+ await deliverAndAudit({
245
+ repo,
246
+ sink: callbackSink,
247
+ message: {
248
+ runId,
249
+ kind: "progress",
250
+ provider: stored.event.callback.provider,
251
+ uri: stored.event.callback.uri,
252
+ body: renderProgress({ runId, message: body.message }),
253
+ ...stored.event.callback.threadKey ? { threadKey: stored.event.callback.threadKey } : {}
254
+ }
255
+ });
256
+ return c.json({ ok: true });
257
+ });
258
+ app.post("/v1/runs/:runId/complete", async (c) => {
259
+ const runId = c.req.param("runId");
260
+ const parsed = CompleteRunSchema.parse(await c.req.json());
261
+ const stored = await repo.getRun({ runId });
262
+ if (!stored) return c.json({ error: "run_not_found" }, 404);
263
+ await repo.completeRun({ runId, result: parsed.result });
264
+ await deliverAndAudit({
265
+ repo,
266
+ sink: callbackSink,
267
+ message: {
268
+ runId,
269
+ kind: "final",
270
+ provider: stored.event.callback.provider,
271
+ uri: stored.event.callback.uri,
272
+ body: renderFinalResult(parsed.result),
273
+ ...stored.event.callback.threadKey ? { threadKey: stored.event.callback.threadKey } : {}
274
+ }
275
+ });
276
+ return c.json({ ok: true });
277
+ });
278
+ app.get("/v1/runs/:runId", async (c) => {
279
+ const stored = await repo.getRun({ runId: c.req.param("runId") });
280
+ if (!stored) return c.json({ error: "run_not_found" }, 404);
281
+ return c.json(stored);
282
+ });
283
+ app.get("/v1/runs/:runId/events", async (c) => {
284
+ const events = await repo.listRunEvents({ runId: c.req.param("runId") });
285
+ return c.json({ events });
286
+ });
287
+ return app;
288
+ }
289
+ export {
290
+ createCompositeCallbackSink,
291
+ createDispatcherApp,
292
+ createGitHubCallbackSink,
293
+ createSlackCallbackSink
294
+ };
295
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/callbacks.ts","../src/server.ts"],"sourcesContent":["import { parseSlackThreadKey } from \"@opentag/slack\";\nimport type { CallbackMessage, CallbackSink } from \"./server.js\";\n\nexport type FetchLike = typeof fetch;\n\nexport function createGitHubCallbackSink(input: { token?: string; fetchImpl?: FetchLike }): CallbackSink {\n const fetchImpl = input.fetchImpl ?? fetch;\n\n return {\n async deliver(message: CallbackMessage): Promise<void> {\n if (message.provider !== \"github\") return;\n if (!input.token) return;\n\n const response = await fetchImpl(message.uri, {\n method: \"POST\",\n headers: {\n accept: \"application/vnd.github+json\",\n authorization: `Bearer ${input.token}`,\n \"content-type\": \"application/json\",\n \"x-github-api-version\": \"2022-11-28\"\n },\n body: JSON.stringify({ body: message.body })\n });\n\n if (!response.ok) {\n throw new Error(`deliver GitHub callback failed: ${response.status} ${await response.text()}`);\n }\n }\n };\n}\n\nexport function createSlackCallbackSink(input: { botToken?: string; fetchImpl?: FetchLike }): CallbackSink {\n const fetchImpl = input.fetchImpl ?? fetch;\n\n return {\n async deliver(message: CallbackMessage): Promise<void> {\n if (message.provider !== \"slack\") return;\n if (!input.botToken) return;\n\n const thread = parseSlackThreadKey(message.threadKey ?? \"\");\n const response = await fetchImpl(message.uri, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${input.botToken}`,\n \"content-type\": \"application/json\"\n },\n body: JSON.stringify({\n channel: thread.channelId,\n text: message.body,\n thread_ts: thread.threadTs\n })\n });\n\n if (!response.ok) {\n throw new Error(`deliver Slack callback failed: ${response.status} ${await response.text()}`);\n }\n const body = (await response.json()) as { ok?: boolean; error?: string };\n if (body.ok === false) {\n throw new Error(`deliver Slack callback failed: ${body.error ?? \"unknown_error\"}`);\n }\n }\n };\n}\n\nexport function createCompositeCallbackSink(sinks: CallbackSink[]): CallbackSink {\n return {\n async deliver(message: CallbackMessage): Promise<void> {\n for (const sink of sinks) {\n await sink.deliver(message);\n }\n }\n };\n}\n","import { OpenTagEventSchema, OpenTagRunResultSchema } from \"@opentag/core\";\nimport { renderAcknowledgement, renderFinalResult, renderProgress } from \"@opentag/github\";\nimport { createOpenTagRepository, migrateSchema } from \"@opentag/store\";\nimport Database from \"better-sqlite3\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { Hono } from \"hono\";\nimport { z } from \"zod\";\n\nconst CreateRunnerSchema = z.object({\n runnerId: z.string().min(1),\n name: z.string().min(1)\n});\n\nconst CreateRepoBindingSchema = z.object({\n provider: z.string().min(1),\n owner: z.string().min(1),\n repo: z.string().min(1),\n runnerId: z.string().min(1),\n workspacePath: z.string().min(1).optional(),\n defaultExecutor: z.string().min(1).optional(),\n allowedActors: z.array(z.string().min(1)).optional()\n});\n\nconst CreateSlackChannelBindingSchema = z.object({\n teamId: z.string().min(1),\n channelId: z.string().min(1),\n owner: z.string().min(1),\n repo: z.string().min(1)\n});\n\nconst CreateRunSchema = z.object({\n runId: z.string().min(1),\n event: OpenTagEventSchema\n});\n\nconst CompleteRunSchema = z.object({\n result: OpenTagRunResultSchema\n});\n\nconst ProgressSchema = z.object({\n type: z.string().min(1).optional(),\n message: z.string().min(1),\n at: z.string().datetime().optional()\n});\n\nfunction repoKeyFromEvent(event: z.infer<typeof OpenTagEventSchema>): { provider: string; owner: string; repo: string } | null {\n const owner = event.metadata[\"owner\"];\n const repo = event.metadata[\"repo\"];\n if (typeof owner !== \"string\" || typeof repo !== \"string\") return null;\n return {\n provider: typeof event.metadata[\"repoProvider\"] === \"string\" ? (event.metadata[\"repoProvider\"] as string) : \"github\",\n owner,\n repo\n };\n}\n\nfunction isWriteCapable(event: z.infer<typeof OpenTagEventSchema>): boolean {\n return event.permissions.some((permission) => [\"repo:write\", \"pr:create\", \"pr:update\"].includes(permission.scope));\n}\n\nfunction actorIsAllowed(event: z.infer<typeof OpenTagEventSchema>, allowedActors: string[] | undefined): boolean {\n if (!allowedActors?.length) return true;\n return allowedActors.includes(event.actor.handle ?? \"\") || allowedActors.includes(event.actor.providerUserId);\n}\n\nexport type CallbackMessage = {\n runId: string;\n kind: \"acknowledgement\" | \"progress\" | \"final\";\n provider: \"github\" | \"slack\" | \"lark\" | \"webhook\";\n uri: string;\n body: string;\n threadKey?: string;\n};\n\nexport type CallbackSink = {\n deliver(message: CallbackMessage): Promise<void>;\n};\n\nconst noopCallbackSink: CallbackSink = {\n async deliver() {\n return;\n }\n};\n\nasync function deliverAndAudit(input: {\n repo: ReturnType<typeof createOpenTagRepository>;\n sink: CallbackSink;\n message: CallbackMessage;\n}): Promise<void> {\n await input.sink.deliver(input.message);\n await input.repo.appendRunEvent({\n runId: input.message.runId,\n type: `callback.${input.message.kind}.delivered`,\n payload: input.message\n });\n}\n\nfunction isAuthorized(request: Request, pairingToken: string | undefined): boolean {\n if (!pairingToken) return true;\n return request.headers.get(\"authorization\") === `Bearer ${pairingToken}`;\n}\n\nexport function createDispatcherApp(input: { databasePath: string; callbackSink?: CallbackSink; pairingToken?: string }) {\n const sqlite = new Database(input.databasePath);\n migrateSchema(sqlite);\n const repo = createOpenTagRepository(drizzle(sqlite));\n const app = new Hono();\n const callbackSink = input.callbackSink ?? noopCallbackSink;\n\n app.get(\"/healthz\", (c) => c.json({ ok: true }));\n\n app.use(\"/v1/*\", async (c, next) => {\n if (!isAuthorized(c.req.raw, input.pairingToken)) {\n return c.json({ error: \"unauthorized\" }, 401);\n }\n await next();\n });\n\n app.post(\"/v1/runners\", async (c) => {\n const parsed = CreateRunnerSchema.parse(await c.req.json());\n await repo.registerRunner(parsed);\n return c.json({ ok: true }, 201);\n });\n\n app.post(\"/v1/repo-bindings\", async (c) => {\n const parsed = CreateRepoBindingSchema.parse(await c.req.json());\n await repo.createRepoBinding({\n provider: parsed.provider,\n owner: parsed.owner,\n repo: parsed.repo,\n runnerId: parsed.runnerId,\n ...(parsed.workspacePath ? { workspacePath: parsed.workspacePath } : {}),\n ...(parsed.defaultExecutor ? { defaultExecutor: parsed.defaultExecutor } : {}),\n ...(parsed.allowedActors?.length ? { allowedActors: parsed.allowedActors } : {})\n });\n return c.json({ ok: true }, 201);\n });\n\n app.get(\"/v1/repo-bindings/:provider/:owner/:repo\", async (c) => {\n const binding = await repo.getRepoBinding({\n provider: c.req.param(\"provider\"),\n owner: c.req.param(\"owner\"),\n repo: c.req.param(\"repo\")\n });\n if (!binding) return c.json({ error: \"repo_binding_not_found\" }, 404);\n return c.json({ binding });\n });\n\n app.post(\"/v1/slack-channel-bindings\", async (c) => {\n const parsed = CreateSlackChannelBindingSchema.parse(await c.req.json());\n await repo.createSlackChannelBinding(parsed);\n return c.json({ ok: true }, 201);\n });\n\n app.get(\"/v1/slack-channel-bindings/:teamId/:channelId\", async (c) => {\n const binding = await repo.getSlackChannelBinding({\n teamId: c.req.param(\"teamId\"),\n channelId: c.req.param(\"channelId\")\n });\n if (!binding) return c.json({ error: \"slack_channel_binding_not_found\" }, 404);\n return c.json({ binding });\n });\n\n app.post(\"/v1/runs\", async (c) => {\n const parsed = CreateRunSchema.parse(await c.req.json());\n const repoKey = repoKeyFromEvent(parsed.event);\n if (!repoKey) {\n return c.json({ error: \"repo_context_missing\" }, 422);\n }\n const binding = await repo.getRepoBinding(repoKey);\n if (!binding) {\n return c.json({ error: \"repo_not_bound\" }, 403);\n }\n if (isWriteCapable(parsed.event) && !actorIsAllowed(parsed.event, binding.allowedActors)) {\n return c.json({ error: \"actor_not_allowed_for_write\" }, 403);\n }\n\n const run = await repo.createRun({ id: parsed.runId, event: parsed.event });\n await deliverAndAudit({\n repo,\n sink: callbackSink,\n message: {\n runId: run.id,\n kind: \"acknowledgement\",\n provider: parsed.event.callback.provider,\n uri: parsed.event.callback.uri,\n body: renderAcknowledgement(run.id),\n ...(parsed.event.callback.threadKey ? { threadKey: parsed.event.callback.threadKey } : {})\n }\n });\n return c.json({ run }, 201);\n });\n\n app.post(\"/v1/runners/:runnerId/claim\", async (c) => {\n const claimed = await repo.claimNextRun({ runnerId: c.req.param(\"runnerId\"), leaseSeconds: 60 });\n if (!claimed) return c.body(null, 204);\n return c.json(claimed, 200);\n });\n\n app.post(\"/v1/runners/:runnerId/runs/:runId/heartbeat\", async (c) => {\n const ok = await repo.heartbeat({ runnerId: c.req.param(\"runnerId\"), runId: c.req.param(\"runId\") });\n if (!ok) return c.json({ error: \"run_not_claimed_by_runner\" }, 404);\n return c.json({ ok: true });\n });\n\n app.post(\"/v1/runs/:runId/running\", async (c) => {\n const body = z.object({ executor: z.string().min(1) }).parse(await c.req.json());\n await repo.markRunning({ runId: c.req.param(\"runId\"), executor: body.executor });\n return c.json({ ok: true });\n });\n\n app.post(\"/v1/runs/:runId/progress\", async (c) => {\n const runId = c.req.param(\"runId\");\n const body = ProgressSchema.parse(await c.req.json());\n const stored = await repo.getRun({ runId });\n if (!stored) return c.json({ error: \"run_not_found\" }, 404);\n\n await repo.recordProgress({\n runId,\n message: body.message,\n ...(body.type ? { type: body.type } : {}),\n ...(body.at ? { at: body.at } : {})\n });\n await deliverAndAudit({\n repo,\n sink: callbackSink,\n message: {\n runId,\n kind: \"progress\",\n provider: stored.event.callback.provider,\n uri: stored.event.callback.uri,\n body: renderProgress({ runId, message: body.message }),\n ...(stored.event.callback.threadKey ? { threadKey: stored.event.callback.threadKey } : {})\n }\n });\n return c.json({ ok: true });\n });\n\n app.post(\"/v1/runs/:runId/complete\", async (c) => {\n const runId = c.req.param(\"runId\");\n const parsed = CompleteRunSchema.parse(await c.req.json());\n const stored = await repo.getRun({ runId });\n if (!stored) return c.json({ error: \"run_not_found\" }, 404);\n\n await repo.completeRun({ runId, result: parsed.result });\n await deliverAndAudit({\n repo,\n sink: callbackSink,\n message: {\n runId,\n kind: \"final\",\n provider: stored.event.callback.provider,\n uri: stored.event.callback.uri,\n body: renderFinalResult(parsed.result),\n ...(stored.event.callback.threadKey ? { threadKey: stored.event.callback.threadKey } : {})\n }\n });\n return c.json({ ok: true });\n });\n\n app.get(\"/v1/runs/:runId\", async (c) => {\n const stored = await repo.getRun({ runId: c.req.param(\"runId\") });\n if (!stored) return c.json({ error: \"run_not_found\" }, 404);\n return c.json(stored);\n });\n\n app.get(\"/v1/runs/:runId/events\", async (c) => {\n const events = await repo.listRunEvents({ runId: c.req.param(\"runId\") });\n return c.json({ events });\n });\n\n return app;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,18 @@
1
+ import { Hono } from "hono";
2
+ export type CallbackMessage = {
3
+ runId: string;
4
+ kind: "acknowledgement" | "progress" | "final";
5
+ provider: "github" | "slack" | "lark" | "webhook";
6
+ uri: string;
7
+ body: string;
8
+ threadKey?: string;
9
+ };
10
+ export type CallbackSink = {
11
+ deliver(message: CallbackMessage): Promise<void>;
12
+ };
13
+ export declare function createDispatcherApp(input: {
14
+ databasePath: string;
15
+ callbackSink?: CallbackSink;
16
+ pairingToken?: string;
17
+ }): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
18
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AA4D5B,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,iBAAiB,GAAG,UAAU,GAAG,OAAO,CAAC;IAC/C,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD,CAAC;AA0BF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,8EA0KtH"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@opentag/dispatcher",
3
+ "version": "0.1.0",
4
+ "description": "Embeddable OpenTag dispatcher Hono app and callback sinks.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "development": "./src/index.ts",
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "keywords": [
23
+ "opentag",
24
+ "dispatcher",
25
+ "hono",
26
+ "agents",
27
+ "webhooks"
28
+ ],
29
+ "license": "Apache-2.0",
30
+ "dependencies": {
31
+ "better-sqlite3": "^11.7.0",
32
+ "drizzle-orm": "^0.45.2",
33
+ "hono": "^4.6.15",
34
+ "zod": "^3.24.1",
35
+ "@opentag/core": "0.1.0",
36
+ "@opentag/store": "0.1.0",
37
+ "@opentag/slack": "0.1.0",
38
+ "@opentag/github": "0.1.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/better-sqlite3": "^7.6.12",
42
+ "tsup": "^8.3.5",
43
+ "typescript": "^5.7.2"
44
+ },
45
+ "scripts": {
46
+ "build": "tsup && tsc -b tsconfig.json --emitDeclarationOnly --force",
47
+ "lint": "tsc --noEmit"
48
+ }
49
+ }