@cf-vibesdk/sdk 0.0.1

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/dist/index.js ADDED
@@ -0,0 +1,501 @@
1
+ // @bun
2
+ // src/http.ts
3
+ class HttpClient {
4
+ opts;
5
+ cachedAccessToken = null;
6
+ constructor(opts) {
7
+ this.opts = opts;
8
+ }
9
+ get baseUrl() {
10
+ return this.opts.baseUrl.replace(/\/$/, "");
11
+ }
12
+ get fetchFn() {
13
+ return this.opts.fetchFn ?? fetch;
14
+ }
15
+ getToken() {
16
+ return this.opts.token ?? this.cachedAccessToken?.token;
17
+ }
18
+ async ensureAccessToken() {
19
+ if (this.opts.token)
20
+ return this.opts.token;
21
+ if (!this.opts.apiKey)
22
+ return;
23
+ const now = Date.now();
24
+ const skewMs = 30000;
25
+ if (this.cachedAccessToken && this.cachedAccessToken.expiresAtMs - skewMs > now) {
26
+ return this.cachedAccessToken.token;
27
+ }
28
+ const url = `${this.baseUrl}/api/auth/exchange-api-key`;
29
+ const resp = await this.fetchFn(url, {
30
+ method: "POST",
31
+ headers: {
32
+ "Content-Type": "application/json",
33
+ Authorization: `Bearer ${this.opts.apiKey}`
34
+ }
35
+ });
36
+ if (!resp.ok) {
37
+ const text = await resp.text().catch(() => "");
38
+ throw new Error(`HTTP ${resp.status} for /api/auth/exchange-api-key: ${text || resp.statusText}`);
39
+ }
40
+ const parsed = await resp.json();
41
+ if (!parsed.success) {
42
+ throw new Error(parsed.error.message);
43
+ }
44
+ const expiresAtMs = Date.parse(parsed.data.expiresAt);
45
+ this.cachedAccessToken = { token: parsed.data.accessToken, expiresAtMs };
46
+ return parsed.data.accessToken;
47
+ }
48
+ async headers(extra) {
49
+ const headers = new Headers({
50
+ ...this.opts.defaultHeaders,
51
+ ...extra
52
+ });
53
+ const token = await this.ensureAccessToken();
54
+ if (token)
55
+ headers.set("Authorization", `Bearer ${token}`);
56
+ return headers;
57
+ }
58
+ async fetchJson(path, init) {
59
+ const url = `${this.baseUrl}${path}`;
60
+ const resp = await this.fetchFn(url, init);
61
+ if (!resp.ok) {
62
+ const text = await resp.text().catch(() => "");
63
+ throw new Error(`HTTP ${resp.status} for ${path}: ${text || resp.statusText}`);
64
+ }
65
+ return await resp.json();
66
+ }
67
+ async fetchRaw(path, init) {
68
+ const url = `${this.baseUrl}${path}`;
69
+ const resp = await this.fetchFn(url, init);
70
+ if (!resp.ok) {
71
+ const text = await resp.text().catch(() => "");
72
+ throw new Error(`HTTP ${resp.status} for ${path}: ${text || resp.statusText}`);
73
+ }
74
+ return resp;
75
+ }
76
+ }
77
+
78
+ // src/ndjson.ts
79
+ async function* parseNdjsonStream(stream) {
80
+ const reader = stream.getReader();
81
+ const decoder = new TextDecoder;
82
+ let buffer = "";
83
+ while (true) {
84
+ const { value, done } = await reader.read();
85
+ if (done)
86
+ break;
87
+ buffer += decoder.decode(value, { stream: true });
88
+ while (true) {
89
+ const newlineIndex = buffer.indexOf(`
90
+ `);
91
+ if (newlineIndex === -1)
92
+ break;
93
+ const line = buffer.slice(0, newlineIndex).trim();
94
+ buffer = buffer.slice(newlineIndex + 1);
95
+ if (!line)
96
+ continue;
97
+ yield JSON.parse(line);
98
+ }
99
+ }
100
+ const tail = buffer.trim();
101
+ if (tail) {
102
+ yield JSON.parse(tail);
103
+ }
104
+ }
105
+
106
+ // src/emitter.ts
107
+ class TypedEmitter {
108
+ listeners = new Map;
109
+ anyListeners = new Set;
110
+ on(event, cb) {
111
+ const set = this.listeners.get(event) ?? new Set;
112
+ set.add(cb);
113
+ this.listeners.set(event, set);
114
+ return () => this.off(event, cb);
115
+ }
116
+ off(event, cb) {
117
+ const set = this.listeners.get(event);
118
+ set?.delete(cb);
119
+ if (set && set.size === 0)
120
+ this.listeners.delete(event);
121
+ }
122
+ onAny(cb) {
123
+ this.anyListeners.add(cb);
124
+ return () => {
125
+ this.anyListeners.delete(cb);
126
+ };
127
+ }
128
+ emit(event, payload) {
129
+ for (const cb of this.anyListeners)
130
+ cb(event, payload);
131
+ const set = this.listeners.get(event);
132
+ if (!set)
133
+ return;
134
+ for (const cb of set)
135
+ cb(payload);
136
+ }
137
+ }
138
+
139
+ // src/ws.ts
140
+ function toWsCloseEvent(ev) {
141
+ return {
142
+ code: typeof ev.code === "number" ? ev.code : 1000,
143
+ reason: typeof ev.reason === "string" ? ev.reason : ""
144
+ };
145
+ }
146
+ function createAgentConnection(url, options = {}) {
147
+ const emitter = new TypedEmitter;
148
+ const headers = { ...options.headers ?? {} };
149
+ if (options.origin)
150
+ headers.Origin = options.origin;
151
+ const ws = options.webSocketFactory ? options.webSocketFactory(url, undefined, headers) : new WebSocket(url);
152
+ if (ws.addEventListener) {
153
+ ws.addEventListener("open", () => {
154
+ emitter.emit("ws:open", undefined);
155
+ });
156
+ ws.addEventListener("close", (ev) => {
157
+ emitter.emit("ws:close", toWsCloseEvent(ev));
158
+ });
159
+ ws.addEventListener("error", (ev) => {
160
+ emitter.emit("ws:error", { error: ev });
161
+ });
162
+ ws.addEventListener("message", (ev) => {
163
+ handleMessage(ev.data);
164
+ });
165
+ } else if (ws.on) {
166
+ ws.on("open", () => emitter.emit("ws:open", undefined));
167
+ ws.on("close", (code, reason) => {
168
+ emitter.emit("ws:close", {
169
+ code: typeof code === "number" ? code : 1000,
170
+ reason: typeof reason === "string" ? reason : ""
171
+ });
172
+ });
173
+ ws.on("error", (error) => emitter.emit("ws:error", { error }));
174
+ ws.on("message", (data) => handleMessage(data));
175
+ }
176
+ function handleMessage(data) {
177
+ try {
178
+ const parsed = JSON.parse(String(data));
179
+ emitter.emit("ws:message", parsed);
180
+ switch (parsed.type) {
181
+ case "agent_connected":
182
+ emitter.emit("connected", parsed);
183
+ break;
184
+ case "conversation_response":
185
+ case "conversation_state":
186
+ emitter.emit("conversation", parsed);
187
+ break;
188
+ case "phase_generating":
189
+ case "phase_generated":
190
+ case "phase_implementing":
191
+ case "phase_implemented":
192
+ case "phase_validating":
193
+ case "phase_validated":
194
+ emitter.emit("phase", parsed);
195
+ break;
196
+ case "file_chunk_generated":
197
+ case "file_generated":
198
+ case "file_generating":
199
+ case "file_regenerating":
200
+ case "file_regenerated":
201
+ emitter.emit("file", parsed);
202
+ break;
203
+ case "deployment_completed":
204
+ case "deployment_started":
205
+ case "deployment_failed":
206
+ emitter.emit("preview", parsed);
207
+ break;
208
+ case "error":
209
+ emitter.emit("error", { error: String(parsed.error ?? "Unknown error") });
210
+ break;
211
+ default:
212
+ break;
213
+ }
214
+ } catch (error) {
215
+ emitter.emit("ws:error", { error });
216
+ }
217
+ }
218
+ function send(msg) {
219
+ ws.send(JSON.stringify(msg));
220
+ }
221
+ function close() {
222
+ ws.close();
223
+ }
224
+ async function waitFor(event, predicate, timeoutMs = 60000) {
225
+ return new Promise((resolve, reject) => {
226
+ const timeout = setTimeout(() => {
227
+ unsub();
228
+ reject(new Error(`Timeout waiting for event: ${String(event)}`));
229
+ }, timeoutMs);
230
+ const unsub = emitter.on(event, (payload) => {
231
+ if (predicate && !predicate(payload))
232
+ return;
233
+ clearTimeout(timeout);
234
+ unsub();
235
+ resolve(payload);
236
+ });
237
+ });
238
+ }
239
+ return {
240
+ send,
241
+ close,
242
+ on: (event, cb) => emitter.on(event, cb),
243
+ onAny: (cb) => emitter.onAny(cb),
244
+ waitFor
245
+ };
246
+ }
247
+
248
+ // src/session.ts
249
+ class BuildSession {
250
+ clientOptions;
251
+ init;
252
+ agentId;
253
+ websocketUrl;
254
+ behaviorType;
255
+ projectType;
256
+ connection = null;
257
+ constructor(clientOptions, start, init = {}) {
258
+ this.clientOptions = clientOptions;
259
+ this.init = init;
260
+ this.agentId = start.agentId;
261
+ this.websocketUrl = start.websocketUrl;
262
+ this.behaviorType = start.behaviorType;
263
+ this.projectType = start.projectType;
264
+ }
265
+ isConnected() {
266
+ return this.connection !== null;
267
+ }
268
+ connect(options = {}) {
269
+ if (this.connection)
270
+ return this.connection;
271
+ const origin = options.origin ?? this.clientOptions.websocketOrigin;
272
+ const webSocketFactory = options.webSocketFactory ?? this.clientOptions.webSocketFactory;
273
+ const headers = { ...options.headers ?? {} };
274
+ const token = this.init.getAuthToken?.();
275
+ if (token && !headers.Authorization) {
276
+ headers.Authorization = `Bearer ${token}`;
277
+ }
278
+ const connectOptions = {
279
+ ...options,
280
+ ...origin ? { origin } : {},
281
+ ...Object.keys(headers).length ? { headers } : {},
282
+ ...webSocketFactory ? { webSocketFactory } : {}
283
+ };
284
+ this.connection = createAgentConnection(this.websocketUrl, connectOptions);
285
+ const credentials = options.credentials ?? this.init.defaultCredentials;
286
+ if (credentials) {
287
+ this.connection.on("ws:open", () => {
288
+ this.connection?.send({
289
+ type: "session_init",
290
+ credentials
291
+ });
292
+ });
293
+ }
294
+ return this.connection;
295
+ }
296
+ startGeneration() {
297
+ this.assertConnected();
298
+ this.connection.send({ type: "generate_all" });
299
+ }
300
+ stop() {
301
+ this.assertConnected();
302
+ this.connection.send({ type: "stop_generation" });
303
+ }
304
+ followUp(message, options) {
305
+ this.assertConnected();
306
+ this.connection.send({
307
+ type: "user_suggestion",
308
+ message,
309
+ images: options?.images
310
+ });
311
+ }
312
+ requestConversationState() {
313
+ this.assertConnected();
314
+ this.connection.send({ type: "get_conversation_state" });
315
+ }
316
+ deployPreview() {
317
+ this.assertConnected();
318
+ this.connection.send({ type: "preview" });
319
+ }
320
+ deployCloudflare() {
321
+ this.assertConnected();
322
+ this.connection.send({ type: "deploy" });
323
+ }
324
+ async waitUntilReady(options = {}) {
325
+ this.assertConnected();
326
+ const timeoutMs = options.timeoutMs ?? 120000;
327
+ const behavior = this.behaviorType;
328
+ if (behavior === "phasic") {
329
+ await this.connection.waitFor("phase", (payload) => payload.type === "phase_implementing", timeoutMs);
330
+ return;
331
+ }
332
+ await this.connection.waitFor("conversation", (payload) => payload.type === "conversation_response", timeoutMs);
333
+ }
334
+ on = (event, cb) => {
335
+ this.assertConnected();
336
+ return this.connection.on(event, cb);
337
+ };
338
+ onAny = (cb) => {
339
+ this.assertConnected();
340
+ return this.connection.onAny(cb);
341
+ };
342
+ onMessageType(type, cb) {
343
+ this.assertConnected();
344
+ return this.connection.on("ws:message", (msg) => {
345
+ if (msg.type === type)
346
+ cb(msg);
347
+ });
348
+ }
349
+ async waitForMessageType(type, timeoutMs) {
350
+ this.assertConnected();
351
+ return await this.connection.waitFor("ws:message", (msg) => msg.type === type, timeoutMs);
352
+ }
353
+ close() {
354
+ this.connection?.close();
355
+ this.connection = null;
356
+ }
357
+ assertConnected() {
358
+ if (!this.connection) {
359
+ throw new Error("BuildSession is not connected. Call session.connect() first.");
360
+ }
361
+ }
362
+ }
363
+
364
+ // src/client.ts
365
+ function toQueryString(query) {
366
+ const params = new URLSearchParams;
367
+ for (const [k, v] of Object.entries(query)) {
368
+ if (v === undefined)
369
+ continue;
370
+ params.set(k, String(v));
371
+ }
372
+ const s = params.toString();
373
+ return s ? `?${s}` : "";
374
+ }
375
+
376
+ class VibeClient {
377
+ options;
378
+ http;
379
+ constructor(options) {
380
+ this.options = options;
381
+ this.http = new HttpClient(options);
382
+ }
383
+ get baseUrl() {
384
+ return this.http.baseUrl;
385
+ }
386
+ async build(prompt, options = {}) {
387
+ const body = {
388
+ query: prompt,
389
+ language: options.language,
390
+ frameworks: options.frameworks,
391
+ selectedTemplate: options.selectedTemplate,
392
+ behaviorType: options.behaviorType,
393
+ projectType: options.projectType,
394
+ images: options.images,
395
+ credentials: options.credentials
396
+ };
397
+ const resp = await this.http.fetchRaw("/api/agent", {
398
+ method: "POST",
399
+ headers: await this.http.headers({ "Content-Type": "application/json" }),
400
+ body: JSON.stringify(body)
401
+ });
402
+ if (!resp.body) {
403
+ throw new Error("Missing response body from /api/agent");
404
+ }
405
+ let start = null;
406
+ for await (const obj of parseNdjsonStream(resp.body)) {
407
+ if (!start) {
408
+ start = obj;
409
+ continue;
410
+ }
411
+ const o = obj;
412
+ if (typeof o.chunk === "string") {
413
+ options.onBlueprintChunk?.(o.chunk);
414
+ }
415
+ }
416
+ if (!start) {
417
+ throw new Error("No start event received from /api/agent");
418
+ }
419
+ const session = new BuildSession(this.options, start, {
420
+ getAuthToken: () => this.http.getToken(),
421
+ ...options.credentials ? { defaultCredentials: options.credentials } : {}
422
+ });
423
+ if (options.autoConnect ?? true) {
424
+ session.connect();
425
+ if (options.autoGenerate ?? true) {
426
+ session.startGeneration();
427
+ }
428
+ }
429
+ return session;
430
+ }
431
+ async connect(agentId, options = {}) {
432
+ const data = await this.http.fetchJson(`/api/agent/${agentId}/connect`, { method: "GET", headers: await this.http.headers() });
433
+ if (!data.success) {
434
+ throw new Error(data.error.message);
435
+ }
436
+ const start = {
437
+ agentId: data.data.agentId,
438
+ websocketUrl: data.data.websocketUrl
439
+ };
440
+ return new BuildSession(this.options, start, {
441
+ getAuthToken: () => this.http.getToken(),
442
+ ...options.credentials ? { defaultCredentials: options.credentials } : {}
443
+ });
444
+ }
445
+ apps = {
446
+ listPublic: async (query = {}) => {
447
+ const qs = toQueryString({
448
+ limit: query.limit,
449
+ page: query.page,
450
+ sort: query.sort,
451
+ order: query.order,
452
+ period: query.period,
453
+ framework: query.framework,
454
+ search: query.search
455
+ });
456
+ return this.http.fetchJson(`/api/apps/public${qs}`, { method: "GET", headers: await this.http.headers() });
457
+ },
458
+ listMine: async () => {
459
+ return this.http.fetchJson("/api/apps", {
460
+ method: "GET",
461
+ headers: await this.http.headers()
462
+ });
463
+ },
464
+ get: async (appId) => {
465
+ return this.http.fetchJson(`/api/apps/${appId}`, {
466
+ method: "GET",
467
+ headers: await this.http.headers()
468
+ });
469
+ },
470
+ getGitCloneToken: async (appId) => {
471
+ return this.http.fetchJson(`/api/apps/${appId}/git/token`, {
472
+ method: "POST",
473
+ headers: await this.http.headers({ "Content-Type": "application/json" })
474
+ });
475
+ }
476
+ };
477
+ }
478
+ // src/agentic.ts
479
+ class AgenticClient extends VibeClient {
480
+ constructor(options) {
481
+ super(options);
482
+ }
483
+ async build(prompt, options = {}) {
484
+ return super.build(prompt, { ...options, behaviorType: options.behaviorType ?? "agentic" });
485
+ }
486
+ }
487
+ // src/phasic.ts
488
+ class PhasicClient extends VibeClient {
489
+ constructor(options) {
490
+ super(options);
491
+ }
492
+ async build(prompt, options = {}) {
493
+ return super.build(prompt, { ...options, behaviorType: options.behaviorType ?? "phasic" });
494
+ }
495
+ }
496
+ export {
497
+ VibeClient,
498
+ PhasicClient,
499
+ BuildSession,
500
+ AgenticClient
501
+ };
package/dist/node.d.ts ADDED
@@ -0,0 +1,67 @@
1
+ // Generated by dts-bundle-generator v9.5.1
2
+
3
+ type CredentialsPayload = {
4
+ providers?: Record<string, {
5
+ apiKey: string;
6
+ }>;
7
+ aiGateway?: {
8
+ baseUrl: string;
9
+ token: string;
10
+ };
11
+ };
12
+ declare const SUPPORTED_IMAGE_MIME_TYPES: readonly [
13
+ "image/png",
14
+ "image/jpeg",
15
+ "image/webp"
16
+ ];
17
+ type SupportedImageMimeType = typeof SUPPORTED_IMAGE_MIME_TYPES[number];
18
+ interface ImageAttachment {
19
+ /** Unique identifier for this attachment */
20
+ id: string;
21
+ /** Original filename */
22
+ filename: string;
23
+ /** MIME type of the image */
24
+ mimeType: SupportedImageMimeType;
25
+ /** Base64-encoded image data (without data URL prefix) */
26
+ base64Data: string;
27
+ /** Size of the original file in bytes */
28
+ size?: number;
29
+ /** Optional dimensions if available */
30
+ dimensions?: {
31
+ width: number;
32
+ height: number;
33
+ };
34
+ }
35
+ type BehaviorType = "phasic" | "agentic";
36
+ type ProjectType = "app" | "workflow" | "presentation" | "general";
37
+ interface CodeGenArgs {
38
+ query: string;
39
+ language?: string;
40
+ frameworks?: string[];
41
+ selectedTemplate?: string;
42
+ behaviorType?: BehaviorType;
43
+ projectType?: ProjectType;
44
+ images?: ImageAttachment[];
45
+ /** Optional ephemeral credentials (BYOK / gateway override) for sdk */
46
+ credentials?: CredentialsPayload;
47
+ }
48
+ type Credentials = NonNullable<CodeGenArgs["credentials"]>;
49
+ type WebSocketLike = {
50
+ send: (data: string) => void;
51
+ close: () => void;
52
+ addEventListener?: (type: "open" | "close" | "error" | "message", listener: (event: unknown) => void) => void;
53
+ on?: (type: string, listener: (...args: unknown[]) => void) => void;
54
+ };
55
+ type AgentConnectionOptions = {
56
+ /** Override Origin header for WS (browser-like security). */
57
+ origin?: string;
58
+ /** Optional per-connection ephemeral credentials (used by `session_init`). */
59
+ credentials?: Credentials;
60
+ /** Additional headers (Node/Bun via ws factory). */
61
+ headers?: Record<string, string>;
62
+ /** Optional WebSocket factory for Node/Bun runtimes. */
63
+ webSocketFactory?: (url: string, protocols?: string | string[], headers?: Record<string, string>) => WebSocketLike;
64
+ };
65
+ export declare function createNodeWebSocketFactory(): NonNullable<AgentConnectionOptions["webSocketFactory"]>;
66
+
67
+ export {};
package/dist/node.js ADDED
@@ -0,0 +1,12 @@
1
+ // @bun
2
+ // src/node.ts
3
+ import WebSocket from "ws";
4
+ function createNodeWebSocketFactory() {
5
+ return (url, protocols, headers) => {
6
+ const ws = headers ? new WebSocket(url, protocols, { headers }) : new WebSocket(url, protocols);
7
+ return ws;
8
+ };
9
+ }
10
+ export {
11
+ createNodeWebSocketFactory
12
+ };
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@cf-vibesdk/sdk",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "default": "./dist/index.js"
9
+ },
10
+ "./node": {
11
+ "types": "./dist/node.d.ts",
12
+ "default": "./dist/node.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "scripts": {
22
+ "build": "rm -rf ./dist && bun build ./src/index.ts ./src/node.ts --outdir ./dist --target bun",
23
+ "bundle-types": "dts-bundle-generator --export-referenced-types false --project ./tsconfig.protocol.json -o ./dist/index.d.ts ./src/index.ts && dts-bundle-generator --export-referenced-types false --project ./tsconfig.protocol.json -o ./dist/node.d.ts ./src/node.ts",
24
+ "typecheck": "tsc -p ./tsconfig.json --noEmit",
25
+ "test": "bun test",
26
+ "example:cli": "bun ./src/example/cli.ts"
27
+ },
28
+ "dependencies": {
29
+ "ws": "^8.18.3"
30
+ },
31
+ "devDependencies": {
32
+ "@types/ws": "^8.18.1",
33
+ "dts-bundle-generator": "^9.5.1",
34
+ "typescript": "^5.9.3"
35
+ }
36
+ }
37
+