@cana-ai/sdk 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.
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Cana Apps SDK — programmatic access.
3
+ *
4
+ * import { Cana } from "@cana-ai/sdk";
5
+ * const app = Cana.app("app_abc123");
6
+ * const session = await app.start({ token: "eyJhbGc…" });
7
+ * session.send("Hello");
8
+ * session.on("delta", ({ text }) => process.stdout.write(text));
9
+ * session.on("done", () => console.log("\\n[done]"));
10
+ *
11
+ * The SDK is intentionally framework-agnostic — no React, no DOM
12
+ * dependencies. It's the same wire protocol the embed.js widget
13
+ * speaks; if a customer wants to drive their own UI they import this.
14
+ */
15
+ export interface AppOptions {
16
+ /** Override the API base. Defaults to https://cana.build (api is path-routed under the same host). */
17
+ apiBase?: string;
18
+ /** When the token expires (401), call this to mint a fresh one. */
19
+ refreshToken?: () => Promise<string>;
20
+ }
21
+ export interface StartOptions {
22
+ token: string;
23
+ /** Resume an existing session (skips POST /sessions). */
24
+ sessionId?: string;
25
+ /** Pre-emitted attachments — server may reject if file uploads disabled. */
26
+ attachments?: unknown[];
27
+ }
28
+ export interface SessionConfig {
29
+ sessionId: string;
30
+ variantId: string | null;
31
+ experimentId?: string;
32
+ }
33
+ export type SessionEvent = {
34
+ type: "delta";
35
+ text: string;
36
+ } | {
37
+ type: "tool_use";
38
+ toolCallId: string;
39
+ toolName: string;
40
+ input: unknown;
41
+ } | {
42
+ type: "tool_result";
43
+ toolCallId: string;
44
+ content: string;
45
+ isError?: boolean;
46
+ } | {
47
+ type: "done";
48
+ usage: {
49
+ inputTokens: number;
50
+ outputTokens: number;
51
+ };
52
+ pausedNow: boolean;
53
+ } | {
54
+ type: "error";
55
+ reason: string;
56
+ message?: string;
57
+ };
58
+ type EventHandler<E extends SessionEvent["type"]> = (ev: Extract<SessionEvent, {
59
+ type: E;
60
+ }>) => void;
61
+ declare class Session {
62
+ readonly sessionId: string;
63
+ readonly variantId: string | null;
64
+ readonly experimentId: string | undefined;
65
+ private app;
66
+ private listeners;
67
+ private abort;
68
+ constructor(app: App, cfg: SessionConfig);
69
+ on<E extends SessionEvent["type"]>(event: E, handler: EventHandler<E>): this;
70
+ off<E extends SessionEvent["type"]>(event: E, handler: EventHandler<E>): this;
71
+ private emit;
72
+ /** Send a user message + stream the model's reply. */
73
+ send(content: string, opts?: {
74
+ idempotencyKey?: string;
75
+ }): Promise<void>;
76
+ /** Abort an in-flight send. */
77
+ end(): void;
78
+ private consumeStream;
79
+ private parseEventBlock;
80
+ }
81
+ export declare class App {
82
+ readonly id: string;
83
+ readonly apiBase: string;
84
+ private token;
85
+ private refreshToken?;
86
+ constructor(id: string, opts?: AppOptions);
87
+ getToken(): string;
88
+ setToken(token: string): void;
89
+ /** Public-safe app config (branding + welcome + suggested prompts). */
90
+ config(): Promise<Record<string, unknown>>;
91
+ /** Start a session (or resume if `sessionId` provided). */
92
+ start(opts: StartOptions): Promise<Session>;
93
+ /** Internal — try the configured refresh callback and re-set the token. */
94
+ refreshIfPossible(): Promise<boolean>;
95
+ }
96
+ /** Namespace export — matches `Cana.app(id)` documented in the spec. */
97
+ export declare const Cana: {
98
+ app(id: string, opts?: AppOptions): App;
99
+ };
100
+ export type { Session };
101
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Cana Apps SDK — programmatic access.
3
+ *
4
+ * import { Cana } from "@cana-ai/sdk";
5
+ * const app = Cana.app("app_abc123");
6
+ * const session = await app.start({ token: "eyJhbGc…" });
7
+ * session.send("Hello");
8
+ * session.on("delta", ({ text }) => process.stdout.write(text));
9
+ * session.on("done", () => console.log("\\n[done]"));
10
+ *
11
+ * The SDK is intentionally framework-agnostic — no React, no DOM
12
+ * dependencies. It's the same wire protocol the embed.js widget
13
+ * speaks; if a customer wants to drive their own UI they import this.
14
+ */
15
+ // Default to the production cana host. The api is path-routed at
16
+ // `${origin}/api/v1/...` via ingress — there is no separate
17
+ // `api.cana.build` subdomain. Override via `apiBase` for staging /
18
+ // self-hosted deployments.
19
+ const DEFAULT_API_BASE = "https://cana.build";
20
+ class Session {
21
+ sessionId;
22
+ variantId;
23
+ experimentId;
24
+ app;
25
+ listeners = new Map();
26
+ abort = null;
27
+ constructor(app, cfg) {
28
+ this.app = app;
29
+ this.sessionId = cfg.sessionId;
30
+ this.variantId = cfg.variantId;
31
+ this.experimentId = cfg.experimentId;
32
+ }
33
+ on(event, handler) {
34
+ const arr = this.listeners.get(event) ?? [];
35
+ arr.push(handler);
36
+ this.listeners.set(event, arr);
37
+ return this;
38
+ }
39
+ off(event, handler) {
40
+ const arr = this.listeners.get(event);
41
+ if (arr) {
42
+ const i = arr.indexOf(handler);
43
+ if (i >= 0)
44
+ arr.splice(i, 1);
45
+ }
46
+ return this;
47
+ }
48
+ emit(ev) {
49
+ const arr = this.listeners.get(ev.type);
50
+ if (!arr)
51
+ return;
52
+ for (const h of arr) {
53
+ try {
54
+ h(ev);
55
+ }
56
+ catch (err) {
57
+ console.error("[cana-sdk] handler threw:", err);
58
+ }
59
+ }
60
+ }
61
+ /** Send a user message + stream the model's reply. */
62
+ async send(content, opts) {
63
+ const ctrl = new AbortController();
64
+ this.abort = ctrl;
65
+ const headers = {
66
+ "Authorization": `Bearer ${this.app.getToken()}`,
67
+ "Content-Type": "application/json",
68
+ "Cana-Client": "sdk@0.1.0",
69
+ };
70
+ if (opts?.idempotencyKey)
71
+ headers["Idempotency-Key"] = opts.idempotencyKey;
72
+ let res;
73
+ try {
74
+ res = await fetch(`${this.app.apiBase}/api/v1/apps/${this.app.id}/messages`, {
75
+ method: "POST",
76
+ headers,
77
+ body: JSON.stringify({ sessionId: this.sessionId, content }),
78
+ signal: ctrl.signal,
79
+ });
80
+ }
81
+ catch (err) {
82
+ this.emit({ type: "error", reason: "network", message: err.message });
83
+ return;
84
+ }
85
+ if (res.status === 401) {
86
+ // Token expired/invalid — try a refresh + retry once.
87
+ const ok = await this.app.refreshIfPossible();
88
+ if (ok)
89
+ return this.send(content, opts);
90
+ this.emit({ type: "error", reason: res.headers.get("Cana-Reason") ?? "token_invalid" });
91
+ return;
92
+ }
93
+ if (!res.ok) {
94
+ let body = {};
95
+ try {
96
+ body = await res.json();
97
+ }
98
+ catch { /* not JSON */ }
99
+ this.emit({
100
+ type: "error",
101
+ reason: res.headers.get("Cana-Reason") ?? body.error ?? `http_${res.status}`,
102
+ message: body.message,
103
+ });
104
+ return;
105
+ }
106
+ if (!res.body) {
107
+ this.emit({ type: "error", reason: "empty_stream" });
108
+ return;
109
+ }
110
+ await this.consumeStream(res.body);
111
+ }
112
+ /** Abort an in-flight send. */
113
+ end() {
114
+ if (this.abort) {
115
+ this.abort.abort();
116
+ this.abort = null;
117
+ }
118
+ }
119
+ async consumeStream(body) {
120
+ const reader = body.getReader();
121
+ const decoder = new TextDecoder();
122
+ let buffer = "";
123
+ while (true) {
124
+ const { value, done } = await reader.read();
125
+ if (done)
126
+ break;
127
+ buffer += decoder.decode(value, { stream: true });
128
+ let idx;
129
+ while ((idx = buffer.indexOf("\n\n")) !== -1) {
130
+ const raw = buffer.slice(0, idx);
131
+ buffer = buffer.slice(idx + 2);
132
+ this.parseEventBlock(raw);
133
+ }
134
+ }
135
+ }
136
+ parseEventBlock(block) {
137
+ if (!block.trim() || block.startsWith(":"))
138
+ return; // heartbeat / comment
139
+ let event = "message";
140
+ let data = "";
141
+ for (const line of block.split("\n")) {
142
+ if (line.startsWith("event:"))
143
+ event = line.slice(6).trim();
144
+ else if (line.startsWith("data:"))
145
+ data += line.slice(5).trim();
146
+ }
147
+ if (!data)
148
+ return;
149
+ let parsed;
150
+ try {
151
+ parsed = JSON.parse(data);
152
+ }
153
+ catch {
154
+ return;
155
+ }
156
+ switch (event) {
157
+ case "cana.app.delta":
158
+ this.emit({ type: "delta", text: parsed.text ?? "" });
159
+ break;
160
+ case "cana.app.tool_use":
161
+ this.emit({ type: "tool_use", toolCallId: parsed.toolCallId, toolName: parsed.toolName, input: parsed.input });
162
+ break;
163
+ case "cana.app.tool_result":
164
+ this.emit({ type: "tool_result", toolCallId: parsed.toolCallId, content: parsed.content, isError: parsed.isError });
165
+ break;
166
+ case "cana.app.done":
167
+ this.emit({ type: "done", usage: parsed.usage ?? { inputTokens: 0, outputTokens: 0 }, pausedNow: !!parsed.pausedNow });
168
+ break;
169
+ case "cana.app.error":
170
+ this.emit({ type: "error", reason: parsed.reason ?? "unknown_error", message: parsed.message });
171
+ break;
172
+ }
173
+ }
174
+ }
175
+ export class App {
176
+ id;
177
+ apiBase;
178
+ token = null;
179
+ refreshToken;
180
+ constructor(id, opts = {}) {
181
+ this.id = id;
182
+ this.apiBase = opts.apiBase ?? DEFAULT_API_BASE;
183
+ this.refreshToken = opts.refreshToken;
184
+ }
185
+ getToken() {
186
+ if (!this.token)
187
+ throw new Error("Call app.start({ token }) first.");
188
+ return this.token;
189
+ }
190
+ setToken(token) {
191
+ this.token = token;
192
+ }
193
+ /** Public-safe app config (branding + welcome + suggested prompts). */
194
+ async config() {
195
+ const res = await fetch(`${this.apiBase}/api/v1/apps/${this.id}/config`);
196
+ if (!res.ok)
197
+ throw new Error(`Failed to load app config (${res.status})`);
198
+ return res.json();
199
+ }
200
+ /** Start a session (or resume if `sessionId` provided). */
201
+ async start(opts) {
202
+ this.token = opts.token;
203
+ if (opts.sessionId) {
204
+ return new Session(this, {
205
+ sessionId: opts.sessionId,
206
+ variantId: null,
207
+ });
208
+ }
209
+ const res = await fetch(`${this.apiBase}/api/v1/apps/${this.id}/sessions`, {
210
+ method: "POST",
211
+ headers: {
212
+ "Authorization": `Bearer ${this.token}`,
213
+ "Content-Type": "application/json",
214
+ "Cana-Client": "sdk@0.1.0",
215
+ },
216
+ body: JSON.stringify({}),
217
+ });
218
+ if (!res.ok) {
219
+ const reason = res.headers.get("Cana-Reason") ?? "session_start_failed";
220
+ throw new Error(`Cana session failed: ${reason}`);
221
+ }
222
+ const data = (await res.json());
223
+ return new Session(this, data);
224
+ }
225
+ /** Internal — try the configured refresh callback and re-set the token. */
226
+ async refreshIfPossible() {
227
+ if (!this.refreshToken)
228
+ return false;
229
+ try {
230
+ this.token = await this.refreshToken();
231
+ return true;
232
+ }
233
+ catch {
234
+ return false;
235
+ }
236
+ }
237
+ }
238
+ /** Namespace export — matches `Cana.app(id)` documented in the spec. */
239
+ export const Cana = {
240
+ app(id, opts) {
241
+ return new App(id, opts);
242
+ },
243
+ };
244
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * `CanaClient` — the OpenAI-compat SDK's main entry point.
3
+ *
4
+ * import { CanaClient } from "@cana-ai/sdk/openai";
5
+ *
6
+ * const client = new CanaClient({
7
+ * appId: "app_abc",
8
+ * signingKey: process.env.CANA_APP_KEY!,
9
+ * apiBase: "https://cana.build", // optional
10
+ * });
11
+ *
12
+ * // 1. Plain chat completion
13
+ * const c = await client.chat.completions.create({
14
+ * model: "openai/gpt-4o",
15
+ * messages: [{ role: "user", content: "Hello" }],
16
+ * });
17
+ *
18
+ * // 2. Streaming
19
+ * const s = await client.chat.completions.create({ ..., stream: true });
20
+ * for await (const chunk of s) {
21
+ * process.stdout.write(chunk.choices[0]?.delta.content ?? "");
22
+ * }
23
+ *
24
+ * // 3. Server-hosted MCP tools — discover + auto-dispatch
25
+ * const mcpTools = await client.mcp.tools(["search", "shopify"]);
26
+ * const r = await client.runWithMcp({
27
+ * model: "anthropic/claude-haiku-4-5-20251001",
28
+ * messages: […],
29
+ * mcp: ["search", "shopify"],
30
+ * maxRounds: 5,
31
+ * });
32
+ *
33
+ * // 4. Zod helper for typed structured output via response_format
34
+ * const Person = z.object({ name: z.string(), age: z.number() });
35
+ * const { result } = await client.chat.completions.generate({
36
+ * model: "openai/gpt-4o",
37
+ * messages: […],
38
+ * schema: Person,
39
+ * });
40
+ * // result: { name: string; age: number }
41
+ */
42
+ import { type ChatCompletion, type ChatCompletionChunk, type ChatCompletionRequest, type ChatMessage, type ChatTool } from "./types.js";
43
+ export interface CanaClientOptions {
44
+ appId: string;
45
+ /** App signing key — `csk_live_…`. Never log this. */
46
+ signingKey: string;
47
+ /** Override the API base. Defaults to https://cana.build. */
48
+ apiBase?: string;
49
+ /** Override the global fetch (test injection). */
50
+ fetch?: typeof fetch;
51
+ }
52
+ export declare class CanaClient {
53
+ private readonly appId;
54
+ private readonly signingKey;
55
+ private readonly apiBase;
56
+ private readonly fetchImpl;
57
+ readonly chat: {
58
+ completions: {
59
+ create<T extends ChatCompletionRequest>(request: T): Promise<T extends {
60
+ stream: true;
61
+ } ? AsyncIterable<ChatCompletionChunk> : ChatCompletion>;
62
+ /** Convenience: structured output via `response_format` + Zod
63
+ * parser. The schema is converted to JSON Schema via the
64
+ * optional `zod-to-json-schema` peer dep; if unavailable,
65
+ * caller must pre-convert. Returns the typed parsed object. */
66
+ generate<T>(args: {
67
+ model: string;
68
+ messages: ChatMessage[];
69
+ schema: {
70
+ parse(value: unknown): T;
71
+ } & {
72
+ _def?: any;
73
+ };
74
+ schemaName?: string;
75
+ temperature?: number;
76
+ max_tokens?: number;
77
+ }): Promise<{
78
+ result: T;
79
+ raw: ChatCompletion;
80
+ }>;
81
+ };
82
+ };
83
+ readonly mcp: {
84
+ tools(connectors: string[]): Promise<{
85
+ tools: ChatTool[];
86
+ dropped: string[];
87
+ }>;
88
+ execute(args: {
89
+ name: string;
90
+ arguments?: Record<string, unknown>;
91
+ }): Promise<{
92
+ result: unknown;
93
+ isError: boolean;
94
+ durationMs: number;
95
+ }>;
96
+ };
97
+ constructor(options: CanaClientOptions);
98
+ private chatCompletionsCreate;
99
+ private chatCompletionsGenerate;
100
+ private mcpToolsFetch;
101
+ private mcpExecuteOne;
102
+ runWithMcp(args: {
103
+ model: string;
104
+ messages: ChatMessage[];
105
+ /** Server-hosted MCP connector names to expose. */
106
+ mcp?: string[];
107
+ /** Additional client-defined tools to pass through to the
108
+ * model alongside the MCP tools. The loop EXITS as soon as the
109
+ * model calls one of these — the caller is responsible for
110
+ * dispatching them and continuing the loop on their side. */
111
+ extraTools?: ChatTool[];
112
+ maxRounds?: number;
113
+ temperature?: number;
114
+ max_tokens?: number;
115
+ }): Promise<{
116
+ message: ChatMessage;
117
+ messages: ChatMessage[];
118
+ rounds: number;
119
+ }>;
120
+ private signedFetch;
121
+ }
122
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/openai/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAIH,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,EAChB,KAAK,QAAQ,EACd,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAID,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IAEzC,SAAgB,IAAI,EAAE;QACpB,WAAW,EAAE;YACX,MAAM,CAAC,CAAC,SAAS,qBAAqB,EACpC,OAAO,EAAE,CAAC,GACT,OAAO,CAAC,CAAC,SAAS;gBAAE,MAAM,EAAE,IAAI,CAAA;aAAE,GAAG,aAAa,CAAC,mBAAmB,CAAC,GAAG,cAAc,CAAC,CAAC;YAC7F;;;4EAGgE;YAChE,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE;gBAChB,KAAK,EAAE,MAAM,CAAC;gBACd,QAAQ,EAAE,WAAW,EAAE,CAAC;gBAExB,MAAM,EAAE;oBAAE,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,CAAC,CAAA;iBAAE,GAAG;oBAAE,IAAI,CAAC,EAAE,GAAG,CAAA;iBAAE,CAAC;gBACtD,UAAU,CAAC,EAAE,MAAM,CAAC;gBACpB,WAAW,CAAC,EAAE,MAAM,CAAC;gBACrB,UAAU,CAAC,EAAE,MAAM,CAAC;aACrB,GAAG,OAAO,CAAC;gBAAE,MAAM,EAAE,CAAC,CAAC;gBAAC,GAAG,EAAE,cAAc,CAAA;aAAE,CAAC,CAAC;SACjD,CAAC;KACH,CAAC;IAEF,SAAgB,GAAG,EAAE;QACnB,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;YAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;YAAC,OAAO,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAC;QAC/E,OAAO,CAAC,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,GAAG,OAAO,CAAC;YAC5E,MAAM,EAAE,OAAO,CAAC;YAChB,OAAO,EAAE,OAAO,CAAC;YACjB,UAAU,EAAE,MAAM,CAAC;SACpB,CAAC,CAAC;KACJ,CAAC;gBAEU,OAAO,EAAE,iBAAiB;YAuBxB,qBAAqB;YAiBrB,uBAAuB;YAiCvB,aAAa;YAOb,aAAa;IAkBrB,UAAU,CAAC,IAAI,EAAE;QACrB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,WAAW,EAAE,CAAC;QACxB,mDAAmD;QACnD,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;QACf;;;sEAG8D;QAC9D,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;QACxB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,WAAW,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;YAgEhE,WAAW;CAqB1B"}
@@ -0,0 +1,264 @@
1
+ /**
2
+ * `CanaClient` — the OpenAI-compat SDK's main entry point.
3
+ *
4
+ * import { CanaClient } from "@cana-ai/sdk/openai";
5
+ *
6
+ * const client = new CanaClient({
7
+ * appId: "app_abc",
8
+ * signingKey: process.env.CANA_APP_KEY!,
9
+ * apiBase: "https://cana.build", // optional
10
+ * });
11
+ *
12
+ * // 1. Plain chat completion
13
+ * const c = await client.chat.completions.create({
14
+ * model: "openai/gpt-4o",
15
+ * messages: [{ role: "user", content: "Hello" }],
16
+ * });
17
+ *
18
+ * // 2. Streaming
19
+ * const s = await client.chat.completions.create({ ..., stream: true });
20
+ * for await (const chunk of s) {
21
+ * process.stdout.write(chunk.choices[0]?.delta.content ?? "");
22
+ * }
23
+ *
24
+ * // 3. Server-hosted MCP tools — discover + auto-dispatch
25
+ * const mcpTools = await client.mcp.tools(["search", "shopify"]);
26
+ * const r = await client.runWithMcp({
27
+ * model: "anthropic/claude-haiku-4-5-20251001",
28
+ * messages: […],
29
+ * mcp: ["search", "shopify"],
30
+ * maxRounds: 5,
31
+ * });
32
+ *
33
+ * // 4. Zod helper for typed structured output via response_format
34
+ * const Person = z.object({ name: z.string(), age: z.number() });
35
+ * const { result } = await client.chat.completions.generate({
36
+ * model: "openai/gpt-4o",
37
+ * messages: […],
38
+ * schema: Person,
39
+ * });
40
+ * // result: { name: string; age: number }
41
+ */
42
+ import { signRequest } from "./sign.js";
43
+ import { parseChunkStream } from "./streaming.js";
44
+ import { CanaApiError, } from "./types.js";
45
+ const DEFAULT_BASE = "https://cana.build";
46
+ export class CanaClient {
47
+ appId;
48
+ signingKey;
49
+ apiBase;
50
+ fetchImpl;
51
+ chat;
52
+ mcp;
53
+ constructor(options) {
54
+ if (!options.appId)
55
+ throw new Error("CanaClient: appId is required");
56
+ if (!options.signingKey)
57
+ throw new Error("CanaClient: signingKey is required");
58
+ this.appId = options.appId;
59
+ this.signingKey = options.signingKey;
60
+ this.apiBase = (options.apiBase ?? DEFAULT_BASE).replace(/\/$/, "");
61
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
62
+ this.chat = {
63
+ completions: {
64
+ create: (req) => this.chatCompletionsCreate(req),
65
+ generate: (args) => this.chatCompletionsGenerate(args),
66
+ },
67
+ };
68
+ this.mcp = {
69
+ tools: (connectors) => this.mcpToolsFetch(connectors),
70
+ execute: (args) => this.mcpExecuteOne(args),
71
+ };
72
+ }
73
+ // ─── chat/completions ───────────────────────────────────────
74
+ async chatCompletionsCreate(req) {
75
+ const body = JSON.stringify(req);
76
+ const res = await this.signedFetch({
77
+ path: "/openai/chat/completions",
78
+ body,
79
+ accept: req.stream ? "text/event-stream" : "application/json",
80
+ });
81
+ if (!res.ok)
82
+ await throwApiError(res);
83
+ if (req.stream) {
84
+ if (!res.body)
85
+ throw new Error("streaming response has no body");
86
+ return parseChunkStream(res.body);
87
+ }
88
+ return (await res.json());
89
+ }
90
+ async chatCompletionsGenerate(args) {
91
+ const jsonSchema = await zodLikeToJsonSchema(args.schema);
92
+ const raw = (await this.chatCompletionsCreate({
93
+ model: args.model,
94
+ messages: args.messages,
95
+ response_format: {
96
+ type: "json_schema",
97
+ json_schema: { name: args.schemaName ?? "Result", strict: true, schema: jsonSchema },
98
+ },
99
+ stream: false,
100
+ temperature: args.temperature,
101
+ max_tokens: args.max_tokens,
102
+ }));
103
+ const content = raw.choices[0]?.message.content;
104
+ if (typeof content !== "string")
105
+ throw new Error("generate(): no string content in response");
106
+ let parsed;
107
+ try {
108
+ parsed = JSON.parse(content);
109
+ }
110
+ catch {
111
+ throw new Error("generate(): model returned non-JSON content despite response_format");
112
+ }
113
+ const result = args.schema.parse(parsed);
114
+ return { result, raw };
115
+ }
116
+ // ─── mcp.* ──────────────────────────────────────────────────
117
+ async mcpToolsFetch(connectors) {
118
+ const body = JSON.stringify({ connectors });
119
+ const res = await this.signedFetch({ path: "/openai/mcp/tools", body });
120
+ if (!res.ok)
121
+ await throwApiError(res);
122
+ return (await res.json());
123
+ }
124
+ async mcpExecuteOne(args) {
125
+ const body = JSON.stringify(args);
126
+ const res = await this.signedFetch({ path: "/openai/mcp/execute", body });
127
+ if (!res.ok)
128
+ await throwApiError(res);
129
+ return (await res.json());
130
+ }
131
+ // ─── runWithMcp convenience loop ────────────────────────────
132
+ //
133
+ // Discovers MCP tools, runs chat/completions, on `tool_calls`
134
+ // dispatches any `mcp__*` named call via /openai/mcp/execute,
135
+ // appends as a `role:"tool"` message, and continues. Non-MCP
136
+ // tool_calls are surfaced to the caller by exiting the loop with
137
+ // the assistant message carrying those tool_calls.
138
+ //
139
+ // maxRounds caps the loop (default 5). Returns the final assistant
140
+ // message + the cumulative conversation.
141
+ async runWithMcp(args) {
142
+ const maxRounds = args.maxRounds ?? 5;
143
+ let { tools: mcpTools } = args.mcp && args.mcp.length > 0
144
+ ? await this.mcpToolsFetch(args.mcp)
145
+ : { tools: [] };
146
+ const tools = [...mcpTools, ...(args.extraTools ?? [])];
147
+ const convo = [...args.messages];
148
+ let round = 0;
149
+ while (round < maxRounds) {
150
+ round++;
151
+ const completion = (await this.chatCompletionsCreate({
152
+ model: args.model,
153
+ messages: convo,
154
+ tools: tools.length > 0 ? tools : undefined,
155
+ stream: false,
156
+ temperature: args.temperature,
157
+ max_tokens: args.max_tokens,
158
+ }));
159
+ const choice = completion.choices[0];
160
+ if (!choice) {
161
+ throw new Error("runWithMcp: upstream returned no choices");
162
+ }
163
+ const message = choice.message;
164
+ convo.push(message);
165
+ // No tool_calls — final answer. Exit.
166
+ if (!message.tool_calls || message.tool_calls.length === 0) {
167
+ return { message, messages: convo, rounds: round };
168
+ }
169
+ // Any non-mcp tool call → exit and hand back to caller. The
170
+ // caller dispatches in their own loop.
171
+ const hasNonMcp = message.tool_calls.some((tc) => !tc.function.name.startsWith("mcp__"));
172
+ if (hasNonMcp) {
173
+ return { message, messages: convo, rounds: round };
174
+ }
175
+ // All MCP — dispatch sequentially. Append each tool_result.
176
+ for (const tc of message.tool_calls) {
177
+ let parsedArgs;
178
+ try {
179
+ parsedArgs = JSON.parse(tc.function.arguments);
180
+ }
181
+ catch {
182
+ parsedArgs = {};
183
+ }
184
+ const result = await this.mcpExecuteOne({
185
+ name: tc.function.name,
186
+ arguments: parsedArgs,
187
+ });
188
+ convo.push({
189
+ role: "tool",
190
+ tool_call_id: tc.id,
191
+ content: typeof result.result === "string" ? result.result : JSON.stringify(result.result),
192
+ });
193
+ }
194
+ }
195
+ // Loop cap reached → return the latest assistant message.
196
+ const last = convo[convo.length - 1] ?? convo[0];
197
+ if (!last)
198
+ throw new Error("runWithMcp: no messages in conversation");
199
+ return { message: last, messages: convo, rounds: round };
200
+ }
201
+ // ─── lower-level: signed fetch ──────────────────────────────
202
+ async signedFetch(args) {
203
+ const url = `${this.apiBase}/api/v1/apps/${this.appId}${args.path}`;
204
+ const { signatureHeader } = await signRequest({
205
+ signingKey: this.signingKey,
206
+ rawBody: args.body,
207
+ });
208
+ return this.fetchImpl(url, {
209
+ method: "POST",
210
+ headers: {
211
+ "Content-Type": "application/json",
212
+ "X-Cana-App-Signature": signatureHeader,
213
+ Accept: args.accept ?? "application/json",
214
+ },
215
+ body: args.body,
216
+ });
217
+ }
218
+ }
219
+ // ─── helpers ──────────────────────────────────────────────────
220
+ async function throwApiError(res) {
221
+ const text = await res.text();
222
+ let code = "internal";
223
+ let message = `request failed with HTTP ${res.status}`;
224
+ let retryable = res.status >= 500;
225
+ try {
226
+ const parsed = JSON.parse(text);
227
+ if (parsed.error) {
228
+ code = parsed.error.code ?? code;
229
+ message = parsed.error.message ?? message;
230
+ retryable = parsed.error.retryable ?? retryable;
231
+ }
232
+ }
233
+ catch { /* keep defaults */ }
234
+ const retryAfter = res.headers.get("retry-after");
235
+ throw new CanaApiError({
236
+ code,
237
+ message,
238
+ status: res.status,
239
+ retryable,
240
+ retryAfterSec: retryAfter ? Number(retryAfter) : undefined,
241
+ });
242
+ }
243
+ /**
244
+ * Best-effort Zod → JSON Schema bridge. Uses the optional peer-dep
245
+ * `zod-to-json-schema` if installed; throws a clear error if not.
246
+ * Allows consumers that DON'T use structured output to skip the dep.
247
+ */
248
+ async function zodLikeToJsonSchema(schema) {
249
+ try {
250
+ const mod = await import(
251
+ /* @vite-ignore */
252
+ /* webpackIgnore: true */
253
+ "zod-to-json-schema");
254
+ const convert = mod.default
255
+ ?? mod.zodToJsonSchema;
256
+ if (!convert)
257
+ throw new Error("zod-to-json-schema exported an unexpected shape");
258
+ return convert(schema);
259
+ }
260
+ catch (err) {
261
+ throw new Error(`generate() requires \`zod-to-json-schema\` to be installed (peer dep). Original error: ${err.message}`);
262
+ }
263
+ }
264
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/openai/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `@cana-ai/sdk/openai` — OpenAI-compatible proxy client for `/apps`.
3
+ *
4
+ * Distribution:
5
+ * - npm: `import { CanaClient } from "@cana-ai/sdk/openai"`
6
+ * - browser script tag:
7
+ * <script src="https://cana.build/sdk/openai.js"></script>
8
+ * <script>
9
+ * const c = new CanaOpenAI.CanaClient({ appId, signingKey });
10
+ * c.chat.completions.create({...}).then(console.log);
11
+ * </script>
12
+ *
13
+ * Web Crypto only — works on Node 18+, Cloudflare Workers, Deno,
14
+ * modern browsers. Zero `node:crypto` dependency at compile time.
15
+ *
16
+ * See ./client.ts for usage examples.
17
+ */
18
+ export { CanaClient, type CanaClientOptions } from "./client.js";
19
+ export { signRequest } from "./sign.js";
20
+ export { parseChunkStream } from "./streaming.js";
21
+ export { CanaApiError, type ChatMessage, type ChatToolCall, type ChatTool, type ChatToolChoice, type ChatCompletion, type ChatCompletionChunk, type ChatCompletionRequest, type ResponseFormat, type ResponseFormatJsonSchema, type CanaErrorBody, } from "./types.js";
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/openai/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EACL,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,wBAAwB,EAC7B,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `@cana-ai/sdk/openai` — OpenAI-compatible proxy client for `/apps`.
3
+ *
4
+ * Distribution:
5
+ * - npm: `import { CanaClient } from "@cana-ai/sdk/openai"`
6
+ * - browser script tag:
7
+ * <script src="https://cana.build/sdk/openai.js"></script>
8
+ * <script>
9
+ * const c = new CanaOpenAI.CanaClient({ appId, signingKey });
10
+ * c.chat.completions.create({...}).then(console.log);
11
+ * </script>
12
+ *
13
+ * Web Crypto only — works on Node 18+, Cloudflare Workers, Deno,
14
+ * modern browsers. Zero `node:crypto` dependency at compile time.
15
+ *
16
+ * See ./client.ts for usage examples.
17
+ */
18
+ export { CanaClient } from "./client.js";
19
+ export { signRequest } from "./sign.js";
20
+ export { parseChunkStream } from "./streaming.js";
21
+ export { CanaApiError, } from "./types.js";
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/openai/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAA0B,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EACL,YAAY,GAWb,MAAM,YAAY,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * HMAC-SHA256 signing helper for the OpenAI-compat proxy.
3
+ *
4
+ * Uses Web Crypto only — works natively on Node 18+, Cloudflare
5
+ * Workers, Deno, modern browsers. No `node:crypto` import, so the SDK
6
+ * has zero Node-typings dependency at compile time.
7
+ *
8
+ * Canonical signed string (matches the route's `verifyIssuerSignature`):
9
+ *
10
+ * `${unixMs}.${rawBodyUtf8}`
11
+ *
12
+ * Header format (matches the route's parser):
13
+ *
14
+ * `X-Cana-App-Signature: t=<unix-ms>,v1=<hex>`
15
+ */
16
+ /** Returns the signed string + header value for a given raw body. */
17
+ export declare function signRequest(args: {
18
+ signingKey: string;
19
+ rawBody: string;
20
+ /** Override clock for tests. Defaults to Date.now(). */
21
+ timestampMs?: number;
22
+ }): Promise<{
23
+ signatureHeader: string;
24
+ timestamp: number;
25
+ signatureHex: string;
26
+ }>;
27
+ //# sourceMappingURL=sign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../../src/openai/sign.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAsBH,qEAAqE;AACrE,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAQhF"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * HMAC-SHA256 signing helper for the OpenAI-compat proxy.
3
+ *
4
+ * Uses Web Crypto only — works natively on Node 18+, Cloudflare
5
+ * Workers, Deno, modern browsers. No `node:crypto` import, so the SDK
6
+ * has zero Node-typings dependency at compile time.
7
+ *
8
+ * Canonical signed string (matches the route's `verifyIssuerSignature`):
9
+ *
10
+ * `${unixMs}.${rawBodyUtf8}`
11
+ *
12
+ * Header format (matches the route's parser):
13
+ *
14
+ * `X-Cana-App-Signature: t=<unix-ms>,v1=<hex>`
15
+ */
16
+ /** Compute the HMAC-SHA256 hex digest of `data` keyed by `secret`. */
17
+ async function hmacSha256Hex(secret, data) {
18
+ const subtle = globalThis.crypto?.subtle;
19
+ if (!subtle) {
20
+ throw new Error("@cana-ai/sdk/openai requires Web Crypto (globalThis.crypto.subtle). Supported on Node 18+, Workers, Deno, modern browsers.");
21
+ }
22
+ const enc = new TextEncoder();
23
+ const key = await subtle.importKey("raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
24
+ const buf = await subtle.sign("HMAC", key, enc.encode(data));
25
+ return Array.from(new Uint8Array(buf), (b) => b.toString(16).padStart(2, "0")).join("");
26
+ }
27
+ /** Returns the signed string + header value for a given raw body. */
28
+ export async function signRequest(args) {
29
+ const t = args.timestampMs ?? Date.now();
30
+ const sig = await hmacSha256Hex(args.signingKey, `${t}.${args.rawBody}`);
31
+ return {
32
+ signatureHeader: `t=${t},v1=${sig}`,
33
+ timestamp: t,
34
+ signatureHex: sig,
35
+ };
36
+ }
37
+ //# sourceMappingURL=sign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign.js","sourceRoot":"","sources":["../../src/openai/sign.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,sEAAsE;AACtE,KAAK,UAAU,aAAa,CAAC,MAAc,EAAE,IAAY;IACvD,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,4HAA4H,CAC7H,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,SAAS,CAChC,KAAK,EACL,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAClB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EACjC,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAC;IACF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAClG,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAKjC;IACC,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACzE,OAAO;QACL,eAAe,EAAE,KAAK,CAAC,OAAO,GAAG,EAAE;QACnC,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,GAAG;KAClB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * SSE parser for the OpenAI Chat Completions chunk stream.
3
+ *
4
+ * The route emits verbatim `data: { ... }\n\n` chunks terminated by
5
+ * `data: [DONE]\n\n` — the OpenAI Chat Completions spec. This parser
6
+ * is intentionally minimal: it pulls `data:` payloads out of the
7
+ * `\n\n`-delimited frames, JSON-parses each one (skipping `[DONE]`),
8
+ * and yields them as OpenAI ChatCompletionChunk objects.
9
+ *
10
+ * Handles SSE comment lines (`: ping`) by ignoring them.
11
+ *
12
+ * The reader is a `ReadableStreamDefaultReader<Uint8Array>` from
13
+ * `Response.body.getReader()` so it works in any modern fetch env
14
+ * (Node 18+ / Workers / Deno / browser).
15
+ */
16
+ import type { ChatCompletionChunk } from "./types.js";
17
+ export declare function parseChunkStream(body: ReadableStream<Uint8Array>): AsyncGenerator<ChatCompletionChunk>;
18
+ //# sourceMappingURL=streaming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../../src/openai/streaming.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,wBAAuB,gBAAgB,CACrC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAC/B,cAAc,CAAC,mBAAmB,CAAC,CAgCrC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * SSE parser for the OpenAI Chat Completions chunk stream.
3
+ *
4
+ * The route emits verbatim `data: { ... }\n\n` chunks terminated by
5
+ * `data: [DONE]\n\n` — the OpenAI Chat Completions spec. This parser
6
+ * is intentionally minimal: it pulls `data:` payloads out of the
7
+ * `\n\n`-delimited frames, JSON-parses each one (skipping `[DONE]`),
8
+ * and yields them as OpenAI ChatCompletionChunk objects.
9
+ *
10
+ * Handles SSE comment lines (`: ping`) by ignoring them.
11
+ *
12
+ * The reader is a `ReadableStreamDefaultReader<Uint8Array>` from
13
+ * `Response.body.getReader()` so it works in any modern fetch env
14
+ * (Node 18+ / Workers / Deno / browser).
15
+ */
16
+ export async function* parseChunkStream(body) {
17
+ const reader = body.getReader();
18
+ const dec = new TextDecoder();
19
+ let buffer = "";
20
+ for (;;) {
21
+ const { value, done } = await reader.read();
22
+ if (done)
23
+ break;
24
+ buffer += dec.decode(value, { stream: true });
25
+ let idx;
26
+ while ((idx = buffer.indexOf("\n\n")) >= 0) {
27
+ const frame = buffer.slice(0, idx);
28
+ buffer = buffer.slice(idx + 2);
29
+ // SSE comment-only frame (heartbeat) → skip.
30
+ if (frame.startsWith(":"))
31
+ continue;
32
+ const dataLines = [];
33
+ for (const line of frame.split("\n")) {
34
+ if (line.startsWith("data:"))
35
+ dataLines.push(line.slice(5).trimStart());
36
+ // `event:` and `id:` are ignored — OpenAI chat completions
37
+ // doesn't use them on chunks.
38
+ }
39
+ if (dataLines.length === 0)
40
+ continue;
41
+ const data = dataLines.join("\n");
42
+ if (data === "[DONE]")
43
+ return;
44
+ try {
45
+ yield JSON.parse(data);
46
+ }
47
+ catch {
48
+ // Malformed JSON in a `data:` frame — skip and continue.
49
+ // The upstream provider's bug shouldn't kill the loop.
50
+ continue;
51
+ }
52
+ }
53
+ }
54
+ }
55
+ //# sourceMappingURL=streaming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.js","sourceRoot":"","sources":["../../src/openai/streaming.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,gBAAgB,CACrC,IAAgC;IAEhC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;IAC9B,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,SAAS,CAAC;QACR,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,IAAI;YAAE,MAAM;QAChB,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,IAAI,GAAW,CAAC;QAChB,OAAO,CAAC,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YACnC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YAC/B,6CAA6C;YAC7C,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YACpC,MAAM,SAAS,GAAa,EAAE,CAAC;YAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;oBAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;gBACxE,2DAA2D;gBAC3D,8BAA8B;YAChC,CAAC;YACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACrC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,IAAI,KAAK,QAAQ;gBAAE,OAAO;YAC9B,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAwB,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC;gBACP,yDAAyD;gBACzD,uDAAuD;gBACvD,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Minimal OpenAI Chat Completions type surface — the SDK doesn't
3
+ * re-implement OpenAI's full schema, just enough for typed
4
+ * convenience. Callers using their own OpenAI types are free to
5
+ * cast.
6
+ */
7
+ export interface ChatMessage {
8
+ role: "system" | "user" | "assistant" | "tool";
9
+ content: string | null | Array<{
10
+ type: "text";
11
+ text: string;
12
+ } | {
13
+ type: "image_url";
14
+ image_url: {
15
+ url: string;
16
+ };
17
+ }>;
18
+ tool_calls?: ChatToolCall[];
19
+ tool_call_id?: string;
20
+ name?: string;
21
+ }
22
+ export interface ChatToolCall {
23
+ id: string;
24
+ type: "function";
25
+ function: {
26
+ name: string;
27
+ arguments: string;
28
+ };
29
+ }
30
+ export interface ChatTool {
31
+ type: "function";
32
+ function: {
33
+ name: string;
34
+ description?: string;
35
+ parameters: Record<string, unknown>;
36
+ };
37
+ }
38
+ export type ChatToolChoice = "auto" | "none" | "required" | {
39
+ type: "function";
40
+ function: {
41
+ name: string;
42
+ };
43
+ };
44
+ export interface ResponseFormatJsonSchema {
45
+ type: "json_schema";
46
+ json_schema: {
47
+ name: string;
48
+ strict?: boolean;
49
+ schema: Record<string, unknown>;
50
+ };
51
+ }
52
+ export type ResponseFormat = {
53
+ type: "text";
54
+ } | {
55
+ type: "json_object";
56
+ } | ResponseFormatJsonSchema;
57
+ export interface ChatCompletionRequest {
58
+ model: string;
59
+ messages: ChatMessage[];
60
+ tools?: ChatTool[];
61
+ tool_choice?: ChatToolChoice;
62
+ response_format?: ResponseFormat;
63
+ stream?: boolean;
64
+ max_tokens?: number;
65
+ temperature?: number;
66
+ top_p?: number;
67
+ stop?: string | string[];
68
+ }
69
+ export interface ChatCompletion {
70
+ id: string;
71
+ object: "chat.completion";
72
+ created: number;
73
+ model: string;
74
+ choices: Array<{
75
+ index: number;
76
+ message: ChatMessage;
77
+ finish_reason: "stop" | "length" | "tool_calls" | "content_filter" | null;
78
+ }>;
79
+ usage?: {
80
+ prompt_tokens: number;
81
+ completion_tokens: number;
82
+ total_tokens: number;
83
+ };
84
+ }
85
+ export interface ChatCompletionChunk {
86
+ id: string;
87
+ object: "chat.completion.chunk";
88
+ created: number;
89
+ model: string;
90
+ choices: Array<{
91
+ index: number;
92
+ delta: Partial<ChatMessage>;
93
+ finish_reason: "stop" | "length" | "tool_calls" | "content_filter" | null;
94
+ }>;
95
+ usage?: {
96
+ prompt_tokens: number;
97
+ completion_tokens: number;
98
+ total_tokens: number;
99
+ };
100
+ }
101
+ /** Server-side error shape — matches the route's `makeErrorBody`. */
102
+ export interface CanaErrorBody {
103
+ error: {
104
+ code: string;
105
+ message: string;
106
+ retryable: boolean;
107
+ };
108
+ }
109
+ export declare class CanaApiError extends Error {
110
+ readonly code: string;
111
+ readonly status: number;
112
+ readonly retryable: boolean;
113
+ readonly retryAfterSec: number | undefined;
114
+ constructor(args: {
115
+ code: string;
116
+ message: string;
117
+ status: number;
118
+ retryable: boolean;
119
+ retryAfterSec?: number;
120
+ });
121
+ }
122
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/openai/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Minimal OpenAI Chat Completions type surface — the SDK doesn't
3
+ * re-implement OpenAI's full schema, just enough for typed
4
+ * convenience. Callers using their own OpenAI types are free to
5
+ * cast.
6
+ */
7
+ export class CanaApiError extends Error {
8
+ code;
9
+ status;
10
+ retryable;
11
+ retryAfterSec;
12
+ constructor(args) {
13
+ super(args.message);
14
+ this.name = "CanaApiError";
15
+ this.code = args.code;
16
+ this.status = args.status;
17
+ this.retryable = args.retryable;
18
+ this.retryAfterSec = args.retryAfterSec;
19
+ }
20
+ }
21
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/openai/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkFH,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrB,IAAI,CAAS;IACb,MAAM,CAAS;IACf,SAAS,CAAU;IACnB,aAAa,CAAqB;IAClD,YAAY,IAAmG;QAC7G,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC;IAC1C,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@cana-ai/sdk",
3
+ "version": "0.2.0",
4
+ "description": "Cana Apps SDK — programmatic access for embedders who drive their own UI.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./openai": {
15
+ "types": "./dist/openai/index.d.ts",
16
+ "import": "./dist/openai/index.js"
17
+ }
18
+ },
19
+ "files": ["dist"],
20
+ "scripts": {
21
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
22
+ "dev": "tsc -p tsconfig.json --watch",
23
+ "prepublishOnly": "pnpm run build",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest"
26
+ },
27
+ "peerDependencies": {
28
+ "zod-to-json-schema": "^3.0.0"
29
+ },
30
+ "peerDependenciesMeta": {
31
+ "zod-to-json-schema": { "optional": true }
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "^5.8.3",
35
+ "vitest": "^3.0.0",
36
+ "zod": "^3.24.1",
37
+ "zod-to-json-schema": "^3.24.0"
38
+ }
39
+ }