@koda-sl/baker-bridge 0.23.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,93 @@
1
+ # @koda-sl/baker-bridge
2
+
3
+ > STATUS: WIP
4
+
5
+ Two exports:
6
+
7
+ - **`@koda-sl/baker-bridge/hono`** — HTTP server wrapping the Claude Agent SDK with SSE streaming and sync endpoints
8
+ - **`@koda-sl/baker-bridge/client`** — Lightweight client for the Baker API (zero dependencies, global `fetch` only)
9
+
10
+ ## Install
11
+
12
+ This package is published to GitHub Packages. Configure your `.npmrc` first:
13
+
14
+ ```
15
+ @koda-sl:registry=https://npm.pkg.github.com
16
+ ```
17
+
18
+ Then install:
19
+
20
+ ```bash
21
+ pnpm add @koda-sl/baker-bridge
22
+ ```
23
+
24
+ ## `@koda-sl/baker-bridge/client`
25
+
26
+ Typed client for the Baker Convex HTTP API. No Hono/Agent SDK dependencies — safe to import anywhere. Reads `BAKER_CONVEX_SITE_URL` and `BAKER_API_KEY` from env (validated at import time via `t3-env`).
27
+
28
+ ```ts
29
+ import { fetchTags } from "@koda-sl/baker-bridge/client";
30
+
31
+ const tags = await fetchTags();
32
+ // TagResponse — array of Tag objects with Convex system fields
33
+ ```
34
+
35
+ ### Environment variables
36
+
37
+ | Variable | Description |
38
+ |---|---|
39
+ | `BAKER_CONVEX_SITE_URL` | Convex site URL (e.g. `https://your-deployment.convex.site`) |
40
+ | `BAKER_API_KEY` | API key for Bearer auth (`bk_...`) |
41
+
42
+ ### Exports
43
+
44
+ - `fetchTags()` — fetch all tags for the authenticated company
45
+ - `Tag`, `TagType`, `TAG_TYPES`, `TagResponse` — type re-exports
46
+
47
+ ### Requirements
48
+
49
+ - Node 18+ (uses global `fetch`)
50
+
51
+ ## `@koda-sl/baker-bridge/hono`
52
+
53
+ HTTP server wrapping the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk).
54
+
55
+ ### CLI
56
+
57
+ ```bash
58
+ npx @koda-sl/baker-bridge
59
+ ```
60
+
61
+ ### Programmatic
62
+
63
+ ```ts
64
+ import { createServer } from "@koda-sl/baker-bridge/hono";
65
+
66
+ const server = createServer();
67
+ await server.start();
68
+ ```
69
+
70
+ ### Environment variables
71
+
72
+ | Variable | Description | Default |
73
+ |---|---|---|
74
+ | `AUTH_TOKEN` | Bearer token for auth | _(required)_ |
75
+ | `BAKER_CONVEX_SITE_URL` | Convex site URL for callbacks | _(required)_ |
76
+ | `BAKER_API_KEY` | API key for Convex callbacks | _(required)_ |
77
+ | `ANTHROPIC_API_KEY` | Anthropic API key (required by the SDK) | _(from env)_ |
78
+
79
+ ### Endpoints
80
+
81
+ | Method | Path | Auth | Description |
82
+ |--------|------|------|-------------|
83
+ | `GET` | `/health` | None | Health check — returns `{ status, projectDir }` |
84
+ | `POST` | `/message` | Bearer token | SSE streaming chat |
85
+ | `POST` | `/message/async` | Bearer token | Fire-and-forget with Convex callback |
86
+
87
+ ## Development
88
+
89
+ ```bash
90
+ # From monorepo root
91
+ pnpm --filter @koda-sl/baker-bridge dev # Watch mode with tsx
92
+ pnpm --filter @koda-sl/baker-bridge build # Compile to dist/
93
+ ```
package/dist/cli.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ declare const args: string[];
3
+ declare const command: string;
4
+ declare function printUsage(): void;
5
+ declare function main(): Promise<void>;
6
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,QAAA,MAAM,IAAI,UAAwB,CAAC;AACnC,QAAA,MAAM,OAAO,QAAU,CAAC;AAExB,iBAAS,UAAU,IAAI,IAAI,CAQ1B;AAED,iBAAe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAkBnC"}
package/dist/cli.js ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ const args = process.argv.slice(2);
4
+ const command = args[0];
5
+ function printUsage() {
6
+ console.error("Usage: baker-bridge <command>");
7
+ console.error("");
8
+ console.error("Commands:");
9
+ console.error(" hono Start the Hono HTTP server");
10
+ console.error("");
11
+ console.error("Options:");
12
+ console.error(" --help Show this help message");
13
+ }
14
+ async function main() {
15
+ if (!command || command === "--help" || command === "-h") {
16
+ printUsage();
17
+ process.exit(command ? 0 : 1);
18
+ }
19
+ if (command === "hono") {
20
+ const { createServer } = await import("./hono/server.js");
21
+ const server = createServer();
22
+ await server.start();
23
+ process.on("SIGINT", () => server.stop());
24
+ process.on("SIGTERM", () => server.stop());
25
+ return;
26
+ }
27
+ console.error(`Unknown command: ${command}`);
28
+ printUsage();
29
+ process.exit(1);
30
+ }
31
+ main().catch((error) => {
32
+ console.error("Fatal error:", error);
33
+ process.exit(1);
34
+ });
35
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;AAEA,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAExB,SAAS,UAAU;IACjB,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC/C,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC3B,OAAO,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;IAChE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1B,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;AAC9D,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACzD,UAAU,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3C,OAAO;IACT,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;IAC7C,UAAU,EAAE,CAAC;IACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { SDKMessage } from "@anthropic-ai/claude-agent-sdk";
2
+ import type { ChatAttachment } from "./types.ts";
3
+ interface StreamCallbacks {
4
+ onMessage: (msg: SDKMessage) => void;
5
+ onInputRequest: (toolUseId: string, questions: unknown) => void;
6
+ onComplete: (costUsd: number, isError: boolean, errors?: string[]) => void;
7
+ }
8
+ declare class AgentSession {
9
+ readonly threadId: string;
10
+ private sessionId;
11
+ private pendingQuestions;
12
+ private handlerCtx;
13
+ private options;
14
+ constructor(threadId: string);
15
+ sendAndStream(content: string, callbacks: StreamCallbacks, attachments?: ChatAttachment[]): Promise<void>;
16
+ resolveQuestion(toolUseId: string, answers: Record<string, string>): boolean;
17
+ }
18
+ /**
19
+ * Get or create a session for a thread. All code paths (WS, async) MUST use
20
+ * this function so every message on the same thread hits the same session.
21
+ */
22
+ export declare function getOrCreateSession(threadId: string): AgentSession;
23
+ export {};
24
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/hono/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAG1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAcjD,UAAU,eAAe;IACvB,SAAS,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAChE,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC5E;AAUD,cAAM,YAAY;IAChB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,gBAAgB,CAAsC;IAC9D,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,OAAO,CAAU;gBAEb,QAAQ,EAAE,MAAM;IAMtB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC/G,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO;CAS7E;AAsED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CASjE"}
@@ -0,0 +1,113 @@
1
+ import { query } from "@anthropic-ai/claude-agent-sdk";
2
+ import { buildAttachmentPromptSuffix, downloadAttachments } from "./attachments.js";
3
+ // ---------------------------------------------------------------------------
4
+ // AgentSession — wraps query() with resume for multi-turn
5
+ //
6
+ // One session per thread. The first message creates a fresh query().
7
+ // Subsequent messages create a new query() with `resume: sessionId`
8
+ // so the conversation context carries over between turns.
9
+ // ---------------------------------------------------------------------------
10
+ class AgentSession {
11
+ threadId;
12
+ sessionId;
13
+ pendingQuestions = new Map();
14
+ handlerCtx;
15
+ options;
16
+ constructor(threadId) {
17
+ this.threadId = threadId;
18
+ this.handlerCtx = { threadId, pendingQuestions: this.pendingQuestions, onInputRequest: null };
19
+ this.options = buildSessionOptions(this.handlerCtx);
20
+ }
21
+ async sendAndStream(content, callbacks, attachments) {
22
+ let costUsd = 0;
23
+ this.handlerCtx.onInputRequest = callbacks.onInputRequest;
24
+ try {
25
+ // Download attachments to local filesystem and append paths to prompt
26
+ let prompt = content;
27
+ if (attachments && attachments.length > 0) {
28
+ const paths = await downloadAttachments(attachments);
29
+ prompt += buildAttachmentPromptSuffix(paths);
30
+ }
31
+ const opts = this.sessionId ? { ...this.options, resume: this.sessionId } : this.options;
32
+ const q = query({ prompt, options: opts });
33
+ for await (const msg of q) {
34
+ // Capture session ID for resuming subsequent turns
35
+ if (msg.session_id) {
36
+ this.sessionId = msg.session_id;
37
+ }
38
+ callbacks.onMessage(msg);
39
+ if (msg.type === "result") {
40
+ costUsd = msg.total_cost_usd;
41
+ }
42
+ }
43
+ callbacks.onComplete(costUsd, false);
44
+ }
45
+ catch (error) {
46
+ const message = error instanceof Error ? error.message : "Unknown error";
47
+ console.error("Agent session error:", message);
48
+ callbacks.onComplete(costUsd, true, [message]);
49
+ }
50
+ }
51
+ resolveQuestion(toolUseId, answers) {
52
+ const pending = this.pendingQuestions.get(toolUseId);
53
+ if (!pending) {
54
+ return false;
55
+ }
56
+ pending.resolve(answers);
57
+ this.pendingQuestions.delete(toolUseId);
58
+ return true;
59
+ }
60
+ }
61
+ // ---------------------------------------------------------------------------
62
+ // Session registry — shared across WS and async paths
63
+ // ---------------------------------------------------------------------------
64
+ const sessions = new Map();
65
+ async function handleAskUserQuestion(input, toolUseID, ctx) {
66
+ // Notify the client about the pending question via WS
67
+ ctx.onInputRequest?.(toolUseID, input.questions);
68
+ const answers = await new Promise((resolve) => {
69
+ ctx.pendingQuestions.set(toolUseID, { resolve });
70
+ });
71
+ return {
72
+ behavior: "allow",
73
+ updatedInput: { questions: input.questions, answers },
74
+ };
75
+ }
76
+ const toolHandlers = {
77
+ AskUserQuestion: handleAskUserQuestion,
78
+ };
79
+ /**
80
+ * Build SDK options with settingSources, cwd, and canUseTool dispatcher.
81
+ * No permissionMode — the CLI routes every tool through canUseTool via
82
+ * --permission-prompt-tool stdio. Our callback returns "allow" for
83
+ * everything except AskUserQuestion (which awaits user answers).
84
+ */
85
+ function buildSessionOptions(handlerCtx) {
86
+ return {
87
+ model: "claude-sonnet-4-6",
88
+ cwd: process.cwd(),
89
+ settingSources: ["project"],
90
+ includePartialMessages: true,
91
+ canUseTool: async (toolName, input, opts) => {
92
+ const handler = toolHandlers[toolName];
93
+ if (handler) {
94
+ return await handler(input, opts.toolUseID, handlerCtx);
95
+ }
96
+ return { behavior: "allow", updatedInput: input };
97
+ },
98
+ };
99
+ }
100
+ /**
101
+ * Get or create a session for a thread. All code paths (WS, async) MUST use
102
+ * this function so every message on the same thread hits the same session.
103
+ */
104
+ export function getOrCreateSession(threadId) {
105
+ const existing = sessions.get(threadId);
106
+ if (existing) {
107
+ return existing;
108
+ }
109
+ const session = new AgentSession(threadId);
110
+ sessions.set(threadId, session);
111
+ return session;
112
+ }
113
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/hono/agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ChatAttachment } from "./types.ts";
2
+ /**
3
+ * Download attachments to the sandbox filesystem and return the local paths.
4
+ * The AI agent can then use its Read tool to view images or read documents.
5
+ */
6
+ export declare function downloadAttachments(attachments: ChatAttachment[]): Promise<string[]>;
7
+ /**
8
+ * Build prompt suffix that tells the AI about downloaded attachments.
9
+ */
10
+ export declare function buildAttachmentPromptSuffix(paths: string[]): string;
11
+ //# sourceMappingURL=attachments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../../src/hono/attachments.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA4B1F;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAMnE"}
@@ -0,0 +1,43 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ const ATTACHMENTS_DIR = "/tmp/attachments";
4
+ /**
5
+ * Download attachments to the sandbox filesystem and return the local paths.
6
+ * The AI agent can then use its Read tool to view images or read documents.
7
+ */
8
+ export async function downloadAttachments(attachments) {
9
+ if (attachments.length === 0) {
10
+ return [];
11
+ }
12
+ await mkdir(ATTACHMENTS_DIR, { recursive: true });
13
+ const paths = [];
14
+ for (const att of attachments) {
15
+ const sanitized = att.filename.replace(/[^a-zA-Z0-9._-]/g, "_");
16
+ const localPath = join(ATTACHMENTS_DIR, `${Date.now()}-${sanitized}`);
17
+ try {
18
+ const response = await fetch(att.url);
19
+ if (!response.ok) {
20
+ console.error(`Failed to download attachment ${att.filename}: ${response.status}`);
21
+ continue;
22
+ }
23
+ const buffer = Buffer.from(await response.arrayBuffer());
24
+ await writeFile(localPath, buffer);
25
+ paths.push(localPath);
26
+ }
27
+ catch (err) {
28
+ console.error(`Failed to download attachment ${att.filename}:`, err);
29
+ }
30
+ }
31
+ return paths;
32
+ }
33
+ /**
34
+ * Build prompt suffix that tells the AI about downloaded attachments.
35
+ */
36
+ export function buildAttachmentPromptSuffix(paths) {
37
+ if (paths.length === 0) {
38
+ return "";
39
+ }
40
+ const fileList = paths.map((p) => `- ${p}`).join("\n");
41
+ return `\n\nThe user attached the following files. Use the Read tool to view them:\n${fileList}`;
42
+ }
43
+ //# sourceMappingURL=attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../src/hono/attachments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAE3C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,WAA6B;IACrE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAElD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,SAAS,EAAE,CAAC,CAAC;QAEtE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACtC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,OAAO,CAAC,KAAK,CAAC,iCAAiC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBACnF,SAAS;YACX,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;YACzD,MAAM,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,iCAAiC,GAAG,CAAC,QAAQ,GAAG,EAAE,GAAG,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,2BAA2B,CAAC,KAAe;IACzD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvD,OAAO,+EAA+E,QAAQ,EAAE,CAAC;AACnG,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { postToConvex } from "./convex.ts";
2
+ import type { AsyncMessageRequest } from "./types.ts";
3
+ export { postToConvex };
4
+ export declare function processAsync(request: AsyncMessageRequest): Promise<void>;
5
+ //# sourceMappingURL=callback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../src/hono/callback.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8B9E"}
@@ -0,0 +1,30 @@
1
+ import { getOrCreateSession } from "./agent.js";
2
+ import { postToConvex } from "./convex.js";
3
+ export { postToConvex };
4
+ export async function processAsync(request) {
5
+ const { prompt, threadId, attachments } = request;
6
+ // Use the shared registry — same session across async and WS paths
7
+ const session = getOrCreateSession(threadId);
8
+ await session.sendAndStream(prompt, {
9
+ onMessage: (msg) => {
10
+ if (msg.type !== "stream_event") {
11
+ void postToConvex("/api/chat/event", { threadId, event: msg });
12
+ }
13
+ },
14
+ onInputRequest: () => {
15
+ // Input requests are posted to Convex inside canUseTool (AgentSession)
16
+ },
17
+ onComplete: async (costUsd, isError, errors) => {
18
+ const ok = await postToConvex("/api/chat/complete", {
19
+ threadId,
20
+ isError,
21
+ costUsd,
22
+ ...(errors && { errors }),
23
+ });
24
+ if (!ok) {
25
+ console.error(`Thread ${threadId} completion callback failed — message may be stuck until cron cleanup`);
26
+ }
27
+ },
28
+ }, attachments);
29
+ }
30
+ //# sourceMappingURL=callback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callback.js","sourceRoot":"","sources":["../../src/hono/callback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAA4B;IAC7D,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAClD,mEAAmE;IACnE,MAAM,OAAO,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAE7C,MAAM,OAAO,CAAC,aAAa,CACzB,MAAM,EACN;QACE,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;YACjB,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBAChC,KAAK,YAAY,CAAC,iBAAiB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;QACD,cAAc,EAAE,GAAG,EAAE;YACnB,uEAAuE;QACzE,CAAC;QACD,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;YAC7C,MAAM,EAAE,GAAG,MAAM,YAAY,CAAC,oBAAoB,EAAE;gBAClD,QAAQ;gBACR,OAAO;gBACP,OAAO;gBACP,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;aAC1B,CAAC,CAAC;YACH,IAAI,CAAC,EAAE,EAAE,CAAC;gBACR,OAAO,CAAC,KAAK,CAAC,UAAU,QAAQ,uEAAuE,CAAC,CAAC;YAC3G,CAAC;QACH,CAAC;KACF,EACD,WAAW,CACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function postToConvex(path: string, body: Record<string, unknown>, retries?: number): Promise<boolean>;
2
+ //# sourceMappingURL=convex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convex.d.ts","sourceRoot":"","sources":["../../src/hono/convex.ts"],"names":[],"mappings":"AAMA,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,SAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CA0B7G"}
@@ -0,0 +1,33 @@
1
+ import { env } from "./env.js";
2
+ function delay(ms) {
3
+ return new Promise((resolve) => setTimeout(resolve, ms));
4
+ }
5
+ export async function postToConvex(path, body, retries = 3) {
6
+ for (let attempt = 0; attempt < retries; attempt++) {
7
+ try {
8
+ const res = await fetch(`${env.BAKER_CONVEX_SITE_URL}${path}`, {
9
+ method: "POST",
10
+ headers: {
11
+ "Content-Type": "application/json",
12
+ Authorization: `Bearer ${env.BAKER_API_KEY}`,
13
+ },
14
+ body: JSON.stringify(body),
15
+ });
16
+ if (res.ok) {
17
+ return true;
18
+ }
19
+ if (attempt < retries - 1) {
20
+ await delay(1000 * 2 ** attempt);
21
+ }
22
+ }
23
+ catch (err) {
24
+ console.error(`POST to Convex ${path} attempt ${attempt + 1} failed:`, err);
25
+ if (attempt < retries - 1) {
26
+ await delay(1000 * 2 ** attempt);
27
+ }
28
+ }
29
+ }
30
+ console.error(`Failed to POST to Convex ${path} after ${retries} attempts`);
31
+ return false;
32
+ }
33
+ //# sourceMappingURL=convex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convex.js","sourceRoot":"","sources":["../../src/hono/convex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,IAA6B,EAAE,OAAO,GAAG,CAAC;IACzF,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,CAAC,qBAAqB,GAAG,IAAI,EAAE,EAAE;gBAC7D,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,aAAa,EAAE,UAAU,GAAG,CAAC,aAAa,EAAE;iBAC7C;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC3B,CAAC,CAAC;YACH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,OAAO,GAAG,OAAO,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,kBAAkB,IAAI,YAAY,OAAO,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;YAC5E,IAAI,OAAO,GAAG,OAAO,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,4BAA4B,IAAI,UAAU,OAAO,WAAW,CAAC,CAAC;IAC5E,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare const env: Readonly<{
2
+ AUTH_TOKEN: string;
3
+ BAKER_CONVEX_SITE_URL: string;
4
+ BAKER_API_KEY: string;
5
+ }>;
6
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/hono/env.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,GAAG;;;;EAOd,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { createEnv } from "@t3-oss/env-core";
2
+ import { z } from "zod";
3
+ export const env = createEnv({
4
+ server: {
5
+ AUTH_TOKEN: z.string().min(1),
6
+ BAKER_CONVEX_SITE_URL: z.url(),
7
+ BAKER_API_KEY: z.string().min(1),
8
+ },
9
+ runtimeEnv: process.env,
10
+ });
11
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/hono/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,GAAG,GAAG,SAAS,CAAC;IAC3B,MAAM,EAAE;QACN,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7B,qBAAqB,EAAE,CAAC,CAAC,GAAG,EAAE;QAC9B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;KACjC;IACD,UAAU,EAAE,OAAO,CAAC,GAAG;CACxB,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { Hono } from "hono";
2
+ export declare function createServer(): {
3
+ app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
4
+ start: () => Promise<void>;
5
+ stop: () => Promise<void>;
6
+ };
7
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/hono/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AA0B5B,wBAAgB,YAAY;;iBA8HR,OAAO,CAAC,IAAI,CAAC;gBAUd,OAAO,CAAC,IAAI,CAAC;EAiB/B"}
@@ -0,0 +1,158 @@
1
+ import { serve } from "@hono/node-server";
2
+ import { createNodeWebSocket } from "@hono/node-ws";
3
+ import { Hono } from "hono";
4
+ import { bearerAuth } from "hono/bearer-auth";
5
+ import { cors } from "hono/cors";
6
+ import { getOrCreateSession } from "./agent.js";
7
+ import { postToConvex, processAsync } from "./callback.js";
8
+ import { env } from "./env.js";
9
+ /** Relay a non-streaming event to Convex (fire-and-forget) */
10
+ function relayToConvex(threadId, message) {
11
+ if (message.type === "stream_event") {
12
+ return;
13
+ }
14
+ void postToConvex("/api/chat/event", { threadId, event: message });
15
+ }
16
+ /** Signal stream completion to Convex */
17
+ function signalComplete(threadId, costUsd, isError, errors) {
18
+ void postToConvex("/api/chat/complete", {
19
+ threadId,
20
+ isError,
21
+ costUsd,
22
+ ...(errors && { errors }),
23
+ });
24
+ }
25
+ export function createServer() {
26
+ const port = 3000;
27
+ const host = "0.0.0.0";
28
+ const app = new Hono();
29
+ const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
30
+ app.use("*", cors());
31
+ app.use("/message/*", bearerAuth({ token: env.AUTH_TOKEN }));
32
+ app.use("/answer-question", bearerAuth({ token: env.AUTH_TOKEN }));
33
+ app.get("/health", (c) => {
34
+ return c.json({ status: "ok", projectDir: process.cwd() });
35
+ });
36
+ // WebSocket endpoint — persistent bidirectional communication
37
+ app.get("/ws", upgradeWebSocket((c) => {
38
+ const token = c.req.query("token");
39
+ return {
40
+ onOpen(_event, ws) {
41
+ if (token !== env.AUTH_TOKEN) {
42
+ ws.close(4001, "Unauthorized");
43
+ }
44
+ },
45
+ onMessage(event, ws) {
46
+ let msg;
47
+ try {
48
+ msg = JSON.parse(typeof event.data === "string" ? event.data : "");
49
+ }
50
+ catch {
51
+ ws.send(JSON.stringify({ type: "error", error: "Invalid JSON" }));
52
+ return;
53
+ }
54
+ switch (msg.type) {
55
+ case "chat": {
56
+ const { threadId, content, attachments } = msg;
57
+ const session = getOrCreateSession(threadId);
58
+ session.sendAndStream(content, {
59
+ onMessage: (m) => {
60
+ try {
61
+ ws.send(JSON.stringify({ type: m.type, data: m }));
62
+ }
63
+ catch {
64
+ // WS may have closed — still relay to Convex
65
+ }
66
+ relayToConvex(threadId, m);
67
+ },
68
+ onInputRequest: (toolUseId, questions) => {
69
+ try {
70
+ ws.send(JSON.stringify({ type: "input_request", toolUseId, questions }));
71
+ }
72
+ catch {
73
+ // WS may have closed — question is still registered in pendingQuestions
74
+ }
75
+ },
76
+ onComplete: (costUsd, isError, errors) => {
77
+ try {
78
+ ws.send(JSON.stringify({ type: "result", costUsd, isError, errors }));
79
+ }
80
+ catch {
81
+ // WS may have closed — still signal Convex
82
+ }
83
+ signalComplete(threadId, costUsd, isError, errors);
84
+ },
85
+ }, attachments);
86
+ break;
87
+ }
88
+ case "answer": {
89
+ const session = getOrCreateSession(msg.threadId);
90
+ session.resolveQuestion(msg.toolUseId, msg.answers);
91
+ break;
92
+ }
93
+ default:
94
+ break;
95
+ }
96
+ },
97
+ onClose() {
98
+ // Don't destroy sessions — they persist for reconnection
99
+ },
100
+ };
101
+ }));
102
+ // Async endpoint — returns 202 immediately, processes in background
103
+ app.post("/message/async", async (c) => {
104
+ const body = await c.req.json();
105
+ if (!body.prompt) {
106
+ return c.json({ error: "prompt is required" }, 400);
107
+ }
108
+ if (!body.threadId) {
109
+ return c.json({ error: "threadId is required" }, 400);
110
+ }
111
+ processAsync(body).catch((err) => {
112
+ console.error("Async processing failed:", err);
113
+ });
114
+ return c.json({ status: "accepted" }, 202);
115
+ });
116
+ // Resolve a pending AskUserQuestion — HTTP fallback for non-WS clients
117
+ app.post("/answer-question", async (c) => {
118
+ const body = await c.req.json();
119
+ if (!body.toolUseId) {
120
+ return c.json({ error: "toolUseId is required" }, 400);
121
+ }
122
+ if (body.threadId) {
123
+ const session = getOrCreateSession(body.threadId);
124
+ if (session.resolveQuestion(body.toolUseId, body.answers ?? {})) {
125
+ return c.json({ status: "ok" });
126
+ }
127
+ }
128
+ return c.json({ error: "No pending question with that ID" }, 404);
129
+ });
130
+ let server = null;
131
+ function start() {
132
+ return new Promise((resolve) => {
133
+ server = serve({ fetch: app.fetch, port, hostname: host }, () => {
134
+ console.warn(`Agent server listening on http://${host}:${port}`);
135
+ resolve();
136
+ });
137
+ injectWebSocket(server);
138
+ });
139
+ }
140
+ function stop() {
141
+ return new Promise((resolve, reject) => {
142
+ if (!server) {
143
+ resolve();
144
+ return;
145
+ }
146
+ server.close((err) => {
147
+ if (err) {
148
+ reject(err);
149
+ }
150
+ else {
151
+ resolve();
152
+ }
153
+ });
154
+ });
155
+ }
156
+ return { app, start, stop };
157
+ }
158
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/hono/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export interface ChatAttachment {
2
+ url: string;
3
+ contentType: string;
4
+ filename: string;
5
+ }
6
+ export interface WSChatMessage {
7
+ type: "chat";
8
+ threadId: string;
9
+ content: string;
10
+ attachments?: ChatAttachment[];
11
+ }
12
+ export interface WSAnswerMessage {
13
+ type: "answer";
14
+ threadId: string;
15
+ toolUseId: string;
16
+ answers: Record<string, string>;
17
+ }
18
+ export type IncomingWSMessage = WSChatMessage | WSAnswerMessage;
19
+ export interface AsyncMessageRequest {
20
+ prompt: string;
21
+ threadId: string;
22
+ attachments?: ChatAttachment[];
23
+ }
24
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/hono/types.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,eAAe,CAAC;AAGhE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;CAChC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/hono/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@koda-sl/baker-bridge",
3
+ "version": "0.23.0",
4
+ "description": "HTTP server wrapping the Claude Agent SDK with SSE streaming and sync endpoints",
5
+ "type": "module",
6
+ "bin": {
7
+ "baker-bridge": "dist/cli.js"
8
+ },
9
+ "exports": {
10
+ "./hono": {
11
+ "types": "./dist/hono/server.d.ts",
12
+ "default": "./dist/hono/server.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "dev": "tsx watch src/cli.ts",
21
+ "start": "node dist/cli.js",
22
+ "typecheck": "tsc --noEmit",
23
+ "link:local": "bash scripts/link-local.sh",
24
+ "unlink:local": "bash scripts/unlink-local.sh"
25
+ },
26
+ "dependencies": {
27
+ "@anthropic-ai/claude-agent-sdk": "^0.2.50",
28
+ "@hono/node-server": "^1.0.0",
29
+ "@hono/node-ws": "^1.3.0",
30
+ "@t3-oss/env-core": "^0.13.10",
31
+ "hono": "^4.0.0",
32
+ "zod": "^4.3.6"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "25.3.0",
36
+ "tsx": "^4.0.0",
37
+ "typescript": "^5.9.3"
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/koda-sl/baker.git",
42
+ "directory": "packages/bridge"
43
+ },
44
+ "publishConfig": {
45
+ "registry": "https://registry.npmjs.org",
46
+ "access": "public"
47
+ }
48
+ }