@cf-vibesdk/sdk 0.0.9 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -2
- package/dist/index.d.ts +26 -0
- package/dist/index.js +55 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -94,6 +94,53 @@ const session = await client.build('Build a weather dashboard', {
|
|
|
94
94
|
});
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
#### Blueprint Streaming
|
|
98
|
+
|
|
99
|
+
The server streams blueprint chunks as the AI generates the project plan. By default, `build()` waits for all chunks before returning:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
// Default behavior: waits for blueprint to complete
|
|
103
|
+
const session = await client.build('Build a todo app', {
|
|
104
|
+
onBlueprintChunk: (chunk) => {
|
|
105
|
+
// Called in real-time as chunks arrive
|
|
106
|
+
blueprintText += chunk;
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
// Session returned after all blueprint chunks received
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
For faster startup, set `waitForBlueprint: false` to return immediately and stream chunks in the background:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
const session = await client.build('Build a todo app', {
|
|
116
|
+
waitForBlueprint: false, // Return immediately after start event
|
|
117
|
+
onBlueprintChunk: (chunk) => {
|
|
118
|
+
// Still called in real-time as chunks arrive
|
|
119
|
+
blueprintText += chunk;
|
|
120
|
+
},
|
|
121
|
+
onBlueprintError: (error) => {
|
|
122
|
+
// Called if streaming fails (session is auto-closed)
|
|
123
|
+
console.error('Blueprint streaming failed:', error);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
// Session returned immediately, blueprint streams in background
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Use the `BlueprintStreamParser` utility to convert chunks to readable Markdown:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { BlueprintStreamParser } from '@cf-vibesdk/sdk';
|
|
133
|
+
|
|
134
|
+
const parser = new BlueprintStreamParser();
|
|
135
|
+
const session = await client.build('Build a todo app', {
|
|
136
|
+
waitForBlueprint: false,
|
|
137
|
+
onBlueprintChunk: (chunk) => {
|
|
138
|
+
const markdown = parser.append(chunk);
|
|
139
|
+
console.log(markdown); // Rendered as Markdown
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
97
144
|
### `client.connect(agentId)`
|
|
98
145
|
|
|
99
146
|
Connect to an existing app session. State is automatically restored from the agent, including:
|
|
@@ -322,14 +369,14 @@ session.workspace.onChange((change) => {
|
|
|
322
369
|
|
|
323
370
|
## WebSocket Reliability
|
|
324
371
|
|
|
325
|
-
Connections automatically reconnect with exponential backoff.
|
|
372
|
+
Connections automatically reconnect with exponential backoff. The first reconnect attempt is **immediate** (0ms delay) for fast recovery from brief network blips, followed by exponential backoff starting at 200ms.
|
|
326
373
|
|
|
327
374
|
```ts
|
|
328
375
|
// Custom retry config
|
|
329
376
|
await session.connect({
|
|
330
377
|
retry: {
|
|
331
378
|
enabled: true, // Default: true
|
|
332
|
-
initialDelayMs:
|
|
379
|
+
initialDelayMs: 200, // Default: 200 (used from 2nd attempt onward)
|
|
333
380
|
maxDelayMs: 30000, // Default: 30000
|
|
334
381
|
maxRetries: 10, // Default: Infinity
|
|
335
382
|
},
|
|
@@ -339,6 +386,35 @@ await session.connect({
|
|
|
339
386
|
await session.connect({ retry: { enabled: false } });
|
|
340
387
|
```
|
|
341
388
|
|
|
389
|
+
### Auto-Preview on Reconnect
|
|
390
|
+
|
|
391
|
+
When connecting to an **existing app** (via `client.connect()`), the SDK automatically sends a `preview` message on connect and reconnect to ensure the preview deployment stays active:
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
// Existing app - auto-preview is enabled by default
|
|
395
|
+
const session = await client.connect('agent-id');
|
|
396
|
+
await session.connect(); // Sends preview message automatically
|
|
397
|
+
|
|
398
|
+
// Override for new builds or disable
|
|
399
|
+
await session.connect({ autoRequestPreview: false });
|
|
400
|
+
|
|
401
|
+
// Force enable for new builds
|
|
402
|
+
const session = await client.build('Build a todo app');
|
|
403
|
+
await session.connect({ autoRequestPreview: true });
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Reconnect Events
|
|
407
|
+
|
|
408
|
+
```ts
|
|
409
|
+
session.on('ws:reconnecting', ({ attempt, delayMs, reason }) => {
|
|
410
|
+
console.log(`Reconnecting (attempt ${attempt}, delay ${delayMs}ms, reason: ${reason})`);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
session.on('ws:reconnected', () => {
|
|
414
|
+
console.log('Reconnected successfully');
|
|
415
|
+
});
|
|
416
|
+
```
|
|
417
|
+
|
|
342
418
|
## HTTP Retry
|
|
343
419
|
|
|
344
420
|
HTTP requests automatically retry on 5xx errors.
|
package/dist/index.d.ts
CHANGED
|
@@ -5453,7 +5453,21 @@ type CodeGenArgs$1 = CodeGenArgs;
|
|
|
5453
5453
|
export type BuildOptions = Omit<CodeGenArgs$1, "query"> & {
|
|
5454
5454
|
autoConnect?: boolean;
|
|
5455
5455
|
autoGenerate?: boolean;
|
|
5456
|
+
/**
|
|
5457
|
+
* Called for each blueprint chunk as it streams from the server.
|
|
5458
|
+
*/
|
|
5456
5459
|
onBlueprintChunk?: (chunk: string) => void;
|
|
5460
|
+
/**
|
|
5461
|
+
* Called if blueprint streaming fails. The session will be closed automatically.
|
|
5462
|
+
* Only relevant when `waitForBlueprint` is false.
|
|
5463
|
+
*/
|
|
5464
|
+
onBlueprintError?: (error: Error) => void;
|
|
5465
|
+
/**
|
|
5466
|
+
* If true (default), `build()` waits for all blueprint chunks before returning.
|
|
5467
|
+
* If false, `build()` returns immediately after receiving the start event,
|
|
5468
|
+
* and blueprint chunks stream in the background via `onBlueprintChunk`.
|
|
5469
|
+
*/
|
|
5470
|
+
waitForBlueprint?: boolean;
|
|
5457
5471
|
};
|
|
5458
5472
|
type TemplateFiles = Record<string, string>;
|
|
5459
5473
|
export type BuildStartEvent = {
|
|
@@ -5554,6 +5568,7 @@ export type AgentEventMap = {
|
|
|
5554
5568
|
delayMs: number;
|
|
5555
5569
|
reason: "close" | "error";
|
|
5556
5570
|
};
|
|
5571
|
+
"ws:reconnected": undefined;
|
|
5557
5572
|
"ws:raw": {
|
|
5558
5573
|
raw: unknown;
|
|
5559
5574
|
};
|
|
@@ -5849,12 +5864,19 @@ type WaitUntilReadyOptions = WaitOptions;
|
|
|
5849
5864
|
type BuildSessionConnectOptions = Omit<AgentConnectionOptions, "credentials"> & {
|
|
5850
5865
|
/** If true (default), send `get_conversation_state` on socket open. */
|
|
5851
5866
|
autoRequestConversationState?: boolean;
|
|
5867
|
+
/**
|
|
5868
|
+
* If true, send `preview` message on connect and reconnect to ensure preview exists.
|
|
5869
|
+
* Defaults to true for existing apps (via `client.connect()`), false for new builds.
|
|
5870
|
+
*/
|
|
5871
|
+
autoRequestPreview?: boolean;
|
|
5852
5872
|
/** Credentials to send via session_init after connection. */
|
|
5853
5873
|
credentials?: Credentials;
|
|
5854
5874
|
};
|
|
5855
5875
|
type BuildSessionInit = {
|
|
5856
5876
|
httpClient: HttpClient;
|
|
5857
5877
|
defaultCredentials?: Credentials;
|
|
5878
|
+
/** True if this session is for an existing app (via client.connect). */
|
|
5879
|
+
isExistingApp?: boolean;
|
|
5858
5880
|
};
|
|
5859
5881
|
export declare class BuildSession {
|
|
5860
5882
|
private init;
|
|
@@ -5948,6 +5970,10 @@ export declare class VibeClient {
|
|
|
5948
5970
|
get baseUrl(): string;
|
|
5949
5971
|
/**
|
|
5950
5972
|
* Creates a new agent/app from a prompt and returns a BuildSession.
|
|
5973
|
+
*
|
|
5974
|
+
* By default, waits for all blueprint chunks before returning. Set
|
|
5975
|
+
* `waitForBlueprint: false` to return immediately after the start event,
|
|
5976
|
+
* with blueprint chunks streaming in the background.
|
|
5951
5977
|
*/
|
|
5952
5978
|
build(prompt: string, options?: BuildOptions): Promise<BuildSession>;
|
|
5953
5979
|
/** Connect to an existing agent/app by id. */
|
package/dist/index.js
CHANGED
|
@@ -409,7 +409,7 @@ class SessionStateStore {
|
|
|
409
409
|
const m = msg;
|
|
410
410
|
const phaseInfo = extractPhaseInfo(m);
|
|
411
411
|
const phaseFiles = extractPhaseFiles(m);
|
|
412
|
-
const phases = this.updateOrAddPhase(phaseInfo, "
|
|
412
|
+
const phases = this.updateOrAddPhase(phaseInfo, "completed", phaseFiles);
|
|
413
413
|
this.setState({
|
|
414
414
|
phase: { status: "implemented", ...phaseInfo },
|
|
415
415
|
phases
|
|
@@ -431,7 +431,7 @@ class SessionStateStore {
|
|
|
431
431
|
const m = msg;
|
|
432
432
|
const phaseInfo = extractPhaseInfo(m);
|
|
433
433
|
const phaseFiles = extractPhaseFiles(m);
|
|
434
|
-
const phases = this.updateOrAddPhase(phaseInfo, "
|
|
434
|
+
const phases = this.updateOrAddPhase(phaseInfo, "validating", phaseFiles);
|
|
435
435
|
this.setState({
|
|
436
436
|
phase: { status: "validated", ...phaseInfo },
|
|
437
437
|
phases
|
|
@@ -618,7 +618,7 @@ async function withTimeout(promise, ms, message = "Operation timed out") {
|
|
|
618
618
|
// src/ws.ts
|
|
619
619
|
var WS_RETRY_DEFAULTS = {
|
|
620
620
|
enabled: true,
|
|
621
|
-
initialDelayMs:
|
|
621
|
+
initialDelayMs: 200,
|
|
622
622
|
maxDelayMs: 30000,
|
|
623
623
|
maxRetries: Infinity
|
|
624
624
|
};
|
|
@@ -630,6 +630,7 @@ function createAgentConnection(getUrl, options = {}) {
|
|
|
630
630
|
let closedByUser = false;
|
|
631
631
|
let reconnectAttempts = 0;
|
|
632
632
|
let reconnectTimer = null;
|
|
633
|
+
let hasConnectedBefore = false;
|
|
633
634
|
const pendingSends = [];
|
|
634
635
|
const maxPendingSends = 1000;
|
|
635
636
|
function clearReconnectTimer() {
|
|
@@ -655,22 +656,34 @@ function createAgentConnection(getUrl, options = {}) {
|
|
|
655
656
|
return;
|
|
656
657
|
if (reconnectTimer)
|
|
657
658
|
return;
|
|
658
|
-
const delayMs = computeBackoffMs(reconnectAttempts, retryCfg);
|
|
659
|
+
const delayMs = reconnectAttempts === 0 ? 0 : computeBackoffMs(reconnectAttempts, retryCfg);
|
|
659
660
|
emitter.emit("ws:reconnecting", {
|
|
660
661
|
attempt: reconnectAttempts + 1,
|
|
661
662
|
delayMs,
|
|
662
663
|
reason
|
|
663
664
|
});
|
|
664
665
|
reconnectAttempts += 1;
|
|
665
|
-
|
|
666
|
-
reconnectTimer =
|
|
667
|
-
|
|
668
|
-
|
|
666
|
+
if (delayMs === 0) {
|
|
667
|
+
reconnectTimer = setTimeout(() => {
|
|
668
|
+
reconnectTimer = null;
|
|
669
|
+
connectNow();
|
|
670
|
+
}, 0);
|
|
671
|
+
} else {
|
|
672
|
+
reconnectTimer = setTimeout(() => {
|
|
673
|
+
reconnectTimer = null;
|
|
674
|
+
connectNow();
|
|
675
|
+
}, delayMs);
|
|
676
|
+
}
|
|
669
677
|
}
|
|
670
678
|
function onOpen() {
|
|
679
|
+
const isReconnect = hasConnectedBefore;
|
|
680
|
+
hasConnectedBefore = true;
|
|
671
681
|
isOpen = true;
|
|
672
682
|
reconnectAttempts = 0;
|
|
673
683
|
emitter.emit("ws:open", undefined);
|
|
684
|
+
if (isReconnect) {
|
|
685
|
+
emitter.emit("ws:reconnected", undefined);
|
|
686
|
+
}
|
|
674
687
|
flushPendingSends();
|
|
675
688
|
}
|
|
676
689
|
function onClose(e) {
|
|
@@ -1005,7 +1018,7 @@ class BuildSession {
|
|
|
1005
1018
|
async connect(options = {}) {
|
|
1006
1019
|
if (this.connection)
|
|
1007
1020
|
return this.connection;
|
|
1008
|
-
const { autoRequestConversationState, credentials, ...connectionOptions } = options;
|
|
1021
|
+
const { autoRequestConversationState, autoRequestPreview, credentials, ...connectionOptions } = options;
|
|
1009
1022
|
const getUrl = async () => {
|
|
1010
1023
|
const { ticket } = await this.init.httpClient.getWsTicket(this.agentId);
|
|
1011
1024
|
const base = this.websocketUrl;
|
|
@@ -1026,6 +1039,7 @@ class BuildSession {
|
|
|
1026
1039
|
});
|
|
1027
1040
|
const sessionCredentials = credentials ?? this.init.defaultCredentials;
|
|
1028
1041
|
const shouldRequestConversationState = autoRequestConversationState ?? true;
|
|
1042
|
+
const shouldRequestPreview = autoRequestPreview ?? (this.init.isExistingApp ?? false);
|
|
1029
1043
|
this.connection.on("ws:open", () => {
|
|
1030
1044
|
if (sessionCredentials) {
|
|
1031
1045
|
this.connection?.send({
|
|
@@ -1036,6 +1050,14 @@ class BuildSession {
|
|
|
1036
1050
|
if (shouldRequestConversationState) {
|
|
1037
1051
|
this.connection?.send({ type: "get_conversation_state" });
|
|
1038
1052
|
}
|
|
1053
|
+
if (shouldRequestPreview) {
|
|
1054
|
+
this.connection?.send({ type: "preview" });
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
this.connection.on("ws:reconnected", () => {
|
|
1058
|
+
if (shouldRequestPreview) {
|
|
1059
|
+
this.connection?.send({ type: "preview" });
|
|
1060
|
+
}
|
|
1039
1061
|
});
|
|
1040
1062
|
return this.connection;
|
|
1041
1063
|
}
|
|
@@ -1198,24 +1220,35 @@ class VibeClient {
|
|
|
1198
1220
|
if (!resp.body) {
|
|
1199
1221
|
throw new Error("Missing response body from /api/agent");
|
|
1200
1222
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
start = obj;
|
|
1205
|
-
continue;
|
|
1206
|
-
}
|
|
1207
|
-
const o = obj;
|
|
1208
|
-
if (typeof o.chunk === "string") {
|
|
1209
|
-
options.onBlueprintChunk?.(o.chunk);
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
if (!start) {
|
|
1223
|
+
const iterator = parseNdjsonStream(resp.body)[Symbol.asyncIterator]();
|
|
1224
|
+
const { value: start, done } = await iterator.next();
|
|
1225
|
+
if (done || !start) {
|
|
1213
1226
|
throw new Error("No start event received from /api/agent");
|
|
1214
1227
|
}
|
|
1215
1228
|
const session = new BuildSession(start, {
|
|
1216
1229
|
httpClient: this.http,
|
|
1217
1230
|
...options.credentials ? { defaultCredentials: options.credentials } : {}
|
|
1218
1231
|
});
|
|
1232
|
+
const waitForBlueprint = options.waitForBlueprint ?? true;
|
|
1233
|
+
const processChunks = async () => {
|
|
1234
|
+
let result = await iterator.next();
|
|
1235
|
+
while (!result.done) {
|
|
1236
|
+
const obj = result.value;
|
|
1237
|
+
if (typeof obj.chunk === "string") {
|
|
1238
|
+
options.onBlueprintChunk?.(obj.chunk);
|
|
1239
|
+
}
|
|
1240
|
+
result = await iterator.next();
|
|
1241
|
+
}
|
|
1242
|
+
};
|
|
1243
|
+
if (waitForBlueprint) {
|
|
1244
|
+
await processChunks();
|
|
1245
|
+
} else {
|
|
1246
|
+
processChunks().catch((error) => {
|
|
1247
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1248
|
+
options.onBlueprintError?.(err);
|
|
1249
|
+
session.close();
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1219
1252
|
if (options.autoConnect ?? true) {
|
|
1220
1253
|
await session.connect();
|
|
1221
1254
|
if (options.autoGenerate ?? true) {
|
|
@@ -1235,6 +1268,7 @@ class VibeClient {
|
|
|
1235
1268
|
};
|
|
1236
1269
|
return new BuildSession(start, {
|
|
1237
1270
|
httpClient: this.http,
|
|
1271
|
+
isExistingApp: true,
|
|
1238
1272
|
...options.credentials ? { defaultCredentials: options.credentials } : {}
|
|
1239
1273
|
});
|
|
1240
1274
|
}
|