@cf-vibesdk/sdk 0.0.7 → 0.0.9
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 +98 -1
- package/dist/index.d.ts +115 -0
- package/dist/index.js +191 -11
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -96,13 +96,21 @@ const session = await client.build('Build a weather dashboard', {
|
|
|
96
96
|
|
|
97
97
|
### `client.connect(agentId)`
|
|
98
98
|
|
|
99
|
-
Connect to an existing app session.
|
|
99
|
+
Connect to an existing app session. State is automatically restored from the agent, including:
|
|
100
|
+
- Phase timeline with completion status
|
|
101
|
+
- Generated files
|
|
102
|
+
- Agent metadata (query, projectName, behaviorType, etc.)
|
|
100
103
|
|
|
101
104
|
```ts
|
|
102
105
|
const session = await client.connect('agent-id-here', {
|
|
103
106
|
credentials: { ... }, // Optional
|
|
104
107
|
});
|
|
105
108
|
await session.connect();
|
|
109
|
+
|
|
110
|
+
// State is now seeded from the agent
|
|
111
|
+
console.log('Original query:', session.state.get().query);
|
|
112
|
+
console.log('Phases:', session.phases.list());
|
|
113
|
+
console.log('Files:', session.files.listPaths());
|
|
106
114
|
```
|
|
107
115
|
|
|
108
116
|
## App Management
|
|
@@ -215,6 +223,71 @@ const snapshot = session.files.snapshot(); // { 'src/App.tsx': '...', ... }
|
|
|
215
223
|
const tree = session.files.tree(); // Nested file tree structure
|
|
216
224
|
```
|
|
217
225
|
|
|
226
|
+
### Phase Timeline
|
|
227
|
+
|
|
228
|
+
Access the full phase timeline for phasic builds. The timeline is automatically seeded when connecting to an existing agent and updated as phases progress.
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
// Get all phases
|
|
232
|
+
const phases = session.phases.list();
|
|
233
|
+
// [{ id: 'phase-0', name: 'Core Setup', status: 'completed', files: [...] }, ...]
|
|
234
|
+
|
|
235
|
+
// Get current active phase
|
|
236
|
+
const current = session.phases.current();
|
|
237
|
+
if (current) {
|
|
238
|
+
console.log(`Working on: ${current.name}`);
|
|
239
|
+
console.log(`Status: ${current.status}`); // 'generating' | 'implementing' | 'validating'
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Get completed phases
|
|
243
|
+
const done = session.phases.completed();
|
|
244
|
+
console.log(`Progress: ${done.length}/${session.phases.count()}`);
|
|
245
|
+
|
|
246
|
+
// Check if all phases are done
|
|
247
|
+
if (session.phases.allCompleted()) {
|
|
248
|
+
console.log('Build complete!');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Get phase by ID
|
|
252
|
+
const phase = session.phases.get('phase-0');
|
|
253
|
+
|
|
254
|
+
// Subscribe to phase changes
|
|
255
|
+
const unsubscribe = session.phases.onChange((event) => {
|
|
256
|
+
console.log(`Phase ${event.type}:`, event.phase.name);
|
|
257
|
+
console.log(`Status: ${event.phase.status}`);
|
|
258
|
+
console.log(`Total phases: ${event.allPhases.length}`);
|
|
259
|
+
});
|
|
260
|
+
// Later: unsubscribe();
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The `onChange` callback receives a `PhaseTimelineEvent`:
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
type PhaseTimelineEvent = {
|
|
267
|
+
type: 'added' | 'updated'; // New phase vs status/file change
|
|
268
|
+
phase: PhaseInfo; // The affected phase
|
|
269
|
+
allPhases: PhaseInfo[]; // All phases after this change
|
|
270
|
+
};
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Each phase contains:
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
type PhaseInfo = {
|
|
277
|
+
id: string; // 'phase-0', 'phase-1', etc.
|
|
278
|
+
name: string; // 'Core Setup', 'Authentication', etc.
|
|
279
|
+
description: string; // What the phase accomplishes
|
|
280
|
+
status: PhaseStatus; // 'pending' | 'generating' | 'implementing' | 'validating' | 'completed' | 'cancelled'
|
|
281
|
+
files: PhaseFile[]; // Files in this phase
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
type PhaseFile = {
|
|
285
|
+
path: string; // 'src/App.tsx'
|
|
286
|
+
purpose: string; // 'Main application component'
|
|
287
|
+
status: PhaseFileStatus; // 'pending' | 'generating' | 'completed' | 'cancelled'
|
|
288
|
+
};
|
|
289
|
+
```
|
|
290
|
+
|
|
218
291
|
### State
|
|
219
292
|
|
|
220
293
|
```ts
|
|
@@ -226,6 +299,16 @@ console.log(state.phase); // { status: 'idle' | 'generating' | ... }
|
|
|
226
299
|
console.log(state.preview); // Preview deployment state
|
|
227
300
|
console.log(state.cloudflare); // Cloudflare deployment state
|
|
228
301
|
|
|
302
|
+
// Phase timeline (array of all phases)
|
|
303
|
+
console.log(state.phases); // [{ id, name, status, files }, ...]
|
|
304
|
+
|
|
305
|
+
// Agent metadata (seeded from agent_connected)
|
|
306
|
+
console.log(state.behaviorType); // 'phasic' | 'agentic'
|
|
307
|
+
console.log(state.projectType); // 'app' | 'workflow' | etc.
|
|
308
|
+
console.log(state.query); // Original user prompt
|
|
309
|
+
console.log(state.projectName); // Project name from blueprint
|
|
310
|
+
console.log(state.shouldBeGenerating); // Whether agent is actively generating
|
|
311
|
+
|
|
229
312
|
// Subscribe to changes
|
|
230
313
|
session.state.onChange((next, prev) => {
|
|
231
314
|
console.log('State changed:', next);
|
|
@@ -324,10 +407,24 @@ All types are exported:
|
|
|
324
407
|
|
|
325
408
|
```ts
|
|
326
409
|
import type {
|
|
410
|
+
// Client & Session
|
|
327
411
|
VibeClientOptions,
|
|
328
412
|
BuildOptions,
|
|
329
413
|
BuildSession,
|
|
330
414
|
SessionState,
|
|
415
|
+
SessionFiles,
|
|
416
|
+
SessionPhases,
|
|
417
|
+
|
|
418
|
+
// Phase Timeline
|
|
419
|
+
PhaseInfo,
|
|
420
|
+
PhaseFile,
|
|
421
|
+
PhaseStatus,
|
|
422
|
+
PhaseFileStatus,
|
|
423
|
+
PhaseEventType,
|
|
424
|
+
PhaseTimelineEvent,
|
|
425
|
+
PhaseTimelineChangeType,
|
|
426
|
+
|
|
427
|
+
// API
|
|
331
428
|
ApiResponse,
|
|
332
429
|
AppDetails,
|
|
333
430
|
Credentials,
|
package/dist/index.d.ts
CHANGED
|
@@ -5568,6 +5568,8 @@ export type AgentEventMap = {
|
|
|
5568
5568
|
error: {
|
|
5569
5569
|
error: string;
|
|
5570
5570
|
};
|
|
5571
|
+
/** Emitted when the phase timeline changes (phase added or updated). */
|
|
5572
|
+
phases: PhaseTimelineEvent;
|
|
5571
5573
|
};
|
|
5572
5574
|
/**
|
|
5573
5575
|
* URL provider for WebSocket connections.
|
|
@@ -5613,6 +5615,62 @@ export type PhaseEventType = "phase_generating" | "phase_generated" | "phase_imp
|
|
|
5613
5615
|
export type WaitForPhaseOptions = WaitOptions & {
|
|
5614
5616
|
type: PhaseEventType;
|
|
5615
5617
|
};
|
|
5618
|
+
/**
|
|
5619
|
+
* Status of a file within a phase.
|
|
5620
|
+
*/
|
|
5621
|
+
export type PhaseFileStatus = "pending" | "generating" | "completed" | "cancelled";
|
|
5622
|
+
/**
|
|
5623
|
+
* A file concept within a phase, with its generation status.
|
|
5624
|
+
* Extends platform's FileConceptType with status tracking.
|
|
5625
|
+
*/
|
|
5626
|
+
export type PhaseFile = Pick<FileConceptType, "path" | "purpose"> & {
|
|
5627
|
+
status: PhaseFileStatus;
|
|
5628
|
+
};
|
|
5629
|
+
/**
|
|
5630
|
+
* Status of a phase in the timeline.
|
|
5631
|
+
*/
|
|
5632
|
+
export type PhaseStatus = "pending" | "generating" | "implementing" | "validating" | "completed" | "cancelled";
|
|
5633
|
+
/**
|
|
5634
|
+
* A phase in the build timeline with its files and status.
|
|
5635
|
+
* Extends platform's PhaseConceptType with SDK-specific fields.
|
|
5636
|
+
*/
|
|
5637
|
+
export type PhaseInfo = Pick<PhaseConceptType, "name" | "description"> & {
|
|
5638
|
+
/** Unique identifier for this phase (e.g., "phase-0", "phase-1"). */
|
|
5639
|
+
id: string;
|
|
5640
|
+
/** Current status of the phase. */
|
|
5641
|
+
status: PhaseStatus;
|
|
5642
|
+
/** Files in this phase with their generation status. */
|
|
5643
|
+
files: PhaseFile[];
|
|
5644
|
+
};
|
|
5645
|
+
/**
|
|
5646
|
+
* High-level API for accessing phase timeline data.
|
|
5647
|
+
*/
|
|
5648
|
+
export type SessionPhases = {
|
|
5649
|
+
/** Get all phases in the timeline. */
|
|
5650
|
+
list: () => PhaseInfo[];
|
|
5651
|
+
/** Get the currently active phase (first non-completed phase), or undefined. */
|
|
5652
|
+
current: () => PhaseInfo | undefined;
|
|
5653
|
+
/** Get all completed phases. */
|
|
5654
|
+
completed: () => PhaseInfo[];
|
|
5655
|
+
/** Get a phase by its id (e.g., "phase-0"). */
|
|
5656
|
+
get: (id: string) => PhaseInfo | undefined;
|
|
5657
|
+
/** Get the total count of phases. */
|
|
5658
|
+
count: () => number;
|
|
5659
|
+
/** Check if all phases are completed. */
|
|
5660
|
+
allCompleted: () => boolean;
|
|
5661
|
+
};
|
|
5662
|
+
/**
|
|
5663
|
+
* Event emitted when the phase timeline changes.
|
|
5664
|
+
*/
|
|
5665
|
+
export type PhaseTimelineChangeType = "added" | "updated";
|
|
5666
|
+
export type PhaseTimelineEvent = {
|
|
5667
|
+
/** Type of change: 'added' for new phase, 'updated' for status/file changes. */
|
|
5668
|
+
type: PhaseTimelineChangeType;
|
|
5669
|
+
/** The phase that was added or updated. */
|
|
5670
|
+
phase: PhaseInfo;
|
|
5671
|
+
/** All phases in the timeline after this change. */
|
|
5672
|
+
allPhases: PhaseInfo[];
|
|
5673
|
+
};
|
|
5616
5674
|
export type SessionDeployable = {
|
|
5617
5675
|
files: number;
|
|
5618
5676
|
reason: "generation_complete" | "phase_validated";
|
|
@@ -5697,15 +5755,48 @@ export type SessionState = {
|
|
|
5697
5755
|
preview: PreviewDeploymentState;
|
|
5698
5756
|
cloudflare: CloudflareDeploymentState;
|
|
5699
5757
|
lastError?: string;
|
|
5758
|
+
/** Full phase timeline with status and files for each phase. */
|
|
5759
|
+
phases: PhaseInfo[];
|
|
5760
|
+
/** Behavior type of the agent (phasic or agentic). */
|
|
5761
|
+
behaviorType?: BehaviorType$1;
|
|
5762
|
+
/** Project type (app, workflow, presentation, general). */
|
|
5763
|
+
projectType?: ProjectType$1;
|
|
5764
|
+
/** Original user query that started this build. */
|
|
5765
|
+
query?: string;
|
|
5766
|
+
/** Whether the agent should be actively generating. */
|
|
5767
|
+
shouldBeGenerating?: boolean;
|
|
5768
|
+
/** Project name from the blueprint. */
|
|
5769
|
+
projectName?: string;
|
|
5700
5770
|
};
|
|
5701
5771
|
export declare class SessionStateStore {
|
|
5702
5772
|
private state;
|
|
5703
5773
|
private emitter;
|
|
5704
5774
|
get(): SessionState;
|
|
5705
5775
|
onChange(cb: (next: SessionState, prev: SessionState) => void): () => void;
|
|
5776
|
+
/**
|
|
5777
|
+
* Subscribe to phase timeline changes.
|
|
5778
|
+
* Fires when a phase is added or when a phase's status/files change.
|
|
5779
|
+
*/
|
|
5780
|
+
onPhaseChange(cb: (event: PhaseTimelineEvent) => void): () => void;
|
|
5706
5781
|
setConnection(state: ConnectionState): void;
|
|
5707
5782
|
applyWsMessage(msg: AgentWsServerMessage): void;
|
|
5783
|
+
/**
|
|
5784
|
+
* Update the status of a file in the phase timeline.
|
|
5785
|
+
*/
|
|
5786
|
+
private updateFileStatus;
|
|
5787
|
+
/**
|
|
5788
|
+
* Update an existing phase or add a new one to the timeline.
|
|
5789
|
+
*/
|
|
5790
|
+
private updateOrAddPhase;
|
|
5708
5791
|
private setState;
|
|
5792
|
+
/**
|
|
5793
|
+
* Compare old and new phases arrays and emit change events.
|
|
5794
|
+
*/
|
|
5795
|
+
private emitPhaseChanges;
|
|
5796
|
+
/**
|
|
5797
|
+
* Check if a phase has meaningfully changed (status or file statuses).
|
|
5798
|
+
*/
|
|
5799
|
+
private hasPhaseChanged;
|
|
5709
5800
|
clear(): void;
|
|
5710
5801
|
}
|
|
5711
5802
|
type WorkspaceChange = {
|
|
@@ -5775,6 +5866,30 @@ export declare class BuildSession {
|
|
|
5775
5866
|
readonly workspace: WorkspaceStore;
|
|
5776
5867
|
readonly state: SessionStateStore;
|
|
5777
5868
|
readonly files: SessionFiles;
|
|
5869
|
+
/**
|
|
5870
|
+
* High-level API for accessing the phase timeline.
|
|
5871
|
+
* Phases are seeded from agent_connected and updated on phase events.
|
|
5872
|
+
*/
|
|
5873
|
+
readonly phases: {
|
|
5874
|
+
/** Get all phases in the timeline. */
|
|
5875
|
+
list: () => PhaseInfo[];
|
|
5876
|
+
/** Get the currently active phase (first non-completed phase), or undefined. */
|
|
5877
|
+
current: () => PhaseInfo | undefined;
|
|
5878
|
+
/** Get all completed phases. */
|
|
5879
|
+
completed: () => PhaseInfo[];
|
|
5880
|
+
/** Get a phase by its id (e.g., "phase-0"). */
|
|
5881
|
+
get: (id: string) => PhaseInfo | undefined;
|
|
5882
|
+
/** Get the total count of phases. */
|
|
5883
|
+
count: () => number;
|
|
5884
|
+
/** Check if all phases are completed. */
|
|
5885
|
+
allCompleted: () => boolean;
|
|
5886
|
+
/**
|
|
5887
|
+
* Subscribe to phase timeline changes.
|
|
5888
|
+
* Fires when a phase is added or when a phase's status/files change.
|
|
5889
|
+
* @returns Unsubscribe function.
|
|
5890
|
+
*/
|
|
5891
|
+
onChange: (cb: (event: PhaseTimelineEvent) => void) => (() => void);
|
|
5892
|
+
};
|
|
5778
5893
|
readonly wait: {
|
|
5779
5894
|
generationStarted: (options?: WaitOptions) => Promise<{
|
|
5780
5895
|
type: "generation_started";
|
package/dist/index.js
CHANGED
|
@@ -227,7 +227,8 @@ var INITIAL_STATE = {
|
|
|
227
227
|
generation: { status: "idle" },
|
|
228
228
|
phase: { status: "idle" },
|
|
229
229
|
preview: { status: "idle" },
|
|
230
|
-
cloudflare: { status: "idle" }
|
|
230
|
+
cloudflare: { status: "idle" },
|
|
231
|
+
phases: []
|
|
231
232
|
};
|
|
232
233
|
function extractPhaseInfo(msg) {
|
|
233
234
|
const phase = msg?.phase;
|
|
@@ -236,6 +237,51 @@ function extractPhaseInfo(msg) {
|
|
|
236
237
|
description: phase?.description
|
|
237
238
|
};
|
|
238
239
|
}
|
|
240
|
+
function extractPhaseFiles(msg) {
|
|
241
|
+
const phase = msg?.phase;
|
|
242
|
+
return phase?.files;
|
|
243
|
+
}
|
|
244
|
+
function isPhasicState(state) {
|
|
245
|
+
return state.behaviorType === "phasic" && "generatedPhases" in state;
|
|
246
|
+
}
|
|
247
|
+
function buildPhaseTimelineFromState(state, generatedFilesMap) {
|
|
248
|
+
if (!isPhasicState(state))
|
|
249
|
+
return [];
|
|
250
|
+
const isActivelyGenerating = state.shouldBeGenerating === true;
|
|
251
|
+
return state.generatedPhases.map((phase, index) => {
|
|
252
|
+
let status;
|
|
253
|
+
if (phase.completed) {
|
|
254
|
+
status = "completed";
|
|
255
|
+
} else if (!isActivelyGenerating) {
|
|
256
|
+
status = "cancelled";
|
|
257
|
+
} else {
|
|
258
|
+
status = "generating";
|
|
259
|
+
}
|
|
260
|
+
const files = phase.files.map((f) => {
|
|
261
|
+
const fileExists = f.path in generatedFilesMap;
|
|
262
|
+
let fileStatus;
|
|
263
|
+
if (fileExists) {
|
|
264
|
+
fileStatus = "completed";
|
|
265
|
+
} else if (!isActivelyGenerating) {
|
|
266
|
+
fileStatus = "cancelled";
|
|
267
|
+
} else {
|
|
268
|
+
fileStatus = "pending";
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
path: f.path,
|
|
272
|
+
purpose: f.purpose,
|
|
273
|
+
status: fileStatus
|
|
274
|
+
};
|
|
275
|
+
});
|
|
276
|
+
return {
|
|
277
|
+
id: `phase-${index}`,
|
|
278
|
+
name: phase.name,
|
|
279
|
+
description: phase.description,
|
|
280
|
+
status,
|
|
281
|
+
files
|
|
282
|
+
};
|
|
283
|
+
});
|
|
284
|
+
}
|
|
239
285
|
|
|
240
286
|
class SessionStateStore {
|
|
241
287
|
state = INITIAL_STATE;
|
|
@@ -246,6 +292,9 @@ class SessionStateStore {
|
|
|
246
292
|
onChange(cb) {
|
|
247
293
|
return this.emitter.on("change", ({ prev, next }) => cb(next, prev));
|
|
248
294
|
}
|
|
295
|
+
onPhaseChange(cb) {
|
|
296
|
+
return this.emitter.on("phaseChange", cb);
|
|
297
|
+
}
|
|
249
298
|
setConnection(state) {
|
|
250
299
|
this.setState({ connection: state });
|
|
251
300
|
}
|
|
@@ -303,47 +352,90 @@ class SessionStateStore {
|
|
|
303
352
|
}
|
|
304
353
|
case "file_generating": {
|
|
305
354
|
const m = msg;
|
|
306
|
-
this.
|
|
355
|
+
const phasesWithGenerating = this.updateFileStatus(m.filePath, "generating");
|
|
356
|
+
this.setState({ currentFile: m.filePath, phases: phasesWithGenerating });
|
|
307
357
|
break;
|
|
308
358
|
}
|
|
309
359
|
case "file_generated": {
|
|
360
|
+
const m = msg;
|
|
361
|
+
const filePath = m.file?.filePath;
|
|
310
362
|
const prev = this.state.generation;
|
|
363
|
+
const phasesWithCompleted = filePath ? this.updateFileStatus(filePath, "completed") : this.state.phases;
|
|
311
364
|
if (prev.status === "running" || prev.status === "stopped") {
|
|
312
365
|
this.setState({
|
|
313
366
|
generation: { ...prev, filesGenerated: prev.filesGenerated + 1 },
|
|
314
|
-
currentFile: undefined
|
|
367
|
+
currentFile: undefined,
|
|
368
|
+
phases: phasesWithCompleted
|
|
315
369
|
});
|
|
370
|
+
} else {
|
|
371
|
+
this.setState({ phases: phasesWithCompleted, currentFile: undefined });
|
|
316
372
|
}
|
|
317
373
|
break;
|
|
318
374
|
}
|
|
319
375
|
case "phase_generating": {
|
|
320
376
|
const m = msg;
|
|
321
|
-
|
|
377
|
+
const phaseInfo = extractPhaseInfo(m);
|
|
378
|
+
const phaseFiles = extractPhaseFiles(m);
|
|
379
|
+
const phases = this.updateOrAddPhase(phaseInfo, "generating", phaseFiles);
|
|
380
|
+
this.setState({
|
|
381
|
+
phase: { status: "generating", ...phaseInfo },
|
|
382
|
+
phases
|
|
383
|
+
});
|
|
322
384
|
break;
|
|
323
385
|
}
|
|
324
386
|
case "phase_generated": {
|
|
325
387
|
const m = msg;
|
|
326
|
-
|
|
388
|
+
const phaseInfo = extractPhaseInfo(m);
|
|
389
|
+
const phaseFiles = extractPhaseFiles(m);
|
|
390
|
+
const phases = this.updateOrAddPhase(phaseInfo, "implementing", phaseFiles);
|
|
391
|
+
this.setState({
|
|
392
|
+
phase: { status: "generated", ...phaseInfo },
|
|
393
|
+
phases
|
|
394
|
+
});
|
|
327
395
|
break;
|
|
328
396
|
}
|
|
329
397
|
case "phase_implementing": {
|
|
330
398
|
const m = msg;
|
|
331
|
-
|
|
399
|
+
const phaseInfo = extractPhaseInfo(m);
|
|
400
|
+
const phaseFiles = extractPhaseFiles(m);
|
|
401
|
+
const phases = this.updateOrAddPhase(phaseInfo, "implementing", phaseFiles);
|
|
402
|
+
this.setState({
|
|
403
|
+
phase: { status: "implementing", ...phaseInfo },
|
|
404
|
+
phases
|
|
405
|
+
});
|
|
332
406
|
break;
|
|
333
407
|
}
|
|
334
408
|
case "phase_implemented": {
|
|
335
409
|
const m = msg;
|
|
336
|
-
|
|
410
|
+
const phaseInfo = extractPhaseInfo(m);
|
|
411
|
+
const phaseFiles = extractPhaseFiles(m);
|
|
412
|
+
const phases = this.updateOrAddPhase(phaseInfo, "validating", phaseFiles);
|
|
413
|
+
this.setState({
|
|
414
|
+
phase: { status: "implemented", ...phaseInfo },
|
|
415
|
+
phases
|
|
416
|
+
});
|
|
337
417
|
break;
|
|
338
418
|
}
|
|
339
419
|
case "phase_validating": {
|
|
340
420
|
const m = msg;
|
|
341
|
-
|
|
421
|
+
const phaseInfo = extractPhaseInfo(m);
|
|
422
|
+
const phaseFiles = extractPhaseFiles(m);
|
|
423
|
+
const phases = this.updateOrAddPhase(phaseInfo, "validating", phaseFiles);
|
|
424
|
+
this.setState({
|
|
425
|
+
phase: { status: "validating", ...phaseInfo },
|
|
426
|
+
phases
|
|
427
|
+
});
|
|
342
428
|
break;
|
|
343
429
|
}
|
|
344
430
|
case "phase_validated": {
|
|
345
431
|
const m = msg;
|
|
346
|
-
|
|
432
|
+
const phaseInfo = extractPhaseInfo(m);
|
|
433
|
+
const phaseFiles = extractPhaseFiles(m);
|
|
434
|
+
const phases = this.updateOrAddPhase(phaseInfo, "completed", phaseFiles);
|
|
435
|
+
this.setState({
|
|
436
|
+
phase: { status: "validated", ...phaseInfo },
|
|
437
|
+
phases
|
|
438
|
+
});
|
|
347
439
|
break;
|
|
348
440
|
}
|
|
349
441
|
case "deployment_started": {
|
|
@@ -394,9 +486,24 @@ class SessionStateStore {
|
|
|
394
486
|
}
|
|
395
487
|
case "agent_connected": {
|
|
396
488
|
const m = msg;
|
|
489
|
+
const agentState = m.state;
|
|
397
490
|
const previewUrl = m.previewUrl;
|
|
398
|
-
|
|
399
|
-
|
|
491
|
+
const phases = buildPhaseTimelineFromState(agentState, agentState.generatedFilesMap ?? {});
|
|
492
|
+
let generation = this.state.generation;
|
|
493
|
+
if (agentState.shouldBeGenerating) {
|
|
494
|
+
const filesGenerated = Object.keys(agentState.generatedFilesMap ?? {}).length;
|
|
495
|
+
generation = { status: "running", filesGenerated };
|
|
496
|
+
}
|
|
497
|
+
this.setState({
|
|
498
|
+
previewUrl,
|
|
499
|
+
phases,
|
|
500
|
+
generation,
|
|
501
|
+
behaviorType: agentState.behaviorType,
|
|
502
|
+
projectType: agentState.projectType,
|
|
503
|
+
query: agentState.query,
|
|
504
|
+
shouldBeGenerating: agentState.shouldBeGenerating,
|
|
505
|
+
projectName: agentState.projectName
|
|
506
|
+
});
|
|
400
507
|
break;
|
|
401
508
|
}
|
|
402
509
|
case "error": {
|
|
@@ -408,11 +515,75 @@ class SessionStateStore {
|
|
|
408
515
|
break;
|
|
409
516
|
}
|
|
410
517
|
}
|
|
518
|
+
updateFileStatus(filePath, status) {
|
|
519
|
+
return this.state.phases.map((phase) => ({
|
|
520
|
+
...phase,
|
|
521
|
+
files: phase.files.map((f) => f.path === filePath ? { ...f, status } : f)
|
|
522
|
+
}));
|
|
523
|
+
}
|
|
524
|
+
updateOrAddPhase(phaseInfo, status, phaseFiles) {
|
|
525
|
+
const phases = [...this.state.phases];
|
|
526
|
+
const existingIndex = phases.findIndex((p) => p.name === phaseInfo.name);
|
|
527
|
+
const files = (phaseFiles ?? []).map((f) => ({
|
|
528
|
+
path: f.path,
|
|
529
|
+
purpose: f.purpose,
|
|
530
|
+
status: status === "completed" ? "completed" : "pending"
|
|
531
|
+
}));
|
|
532
|
+
if (existingIndex >= 0) {
|
|
533
|
+
phases[existingIndex] = {
|
|
534
|
+
...phases[existingIndex],
|
|
535
|
+
status,
|
|
536
|
+
description: phaseInfo.description ?? phases[existingIndex].description,
|
|
537
|
+
files: files.length > 0 ? files : phases[existingIndex].files
|
|
538
|
+
};
|
|
539
|
+
} else if (phaseInfo.name) {
|
|
540
|
+
phases.push({
|
|
541
|
+
id: `phase-${phases.length}`,
|
|
542
|
+
name: phaseInfo.name,
|
|
543
|
+
description: phaseInfo.description ?? "",
|
|
544
|
+
status,
|
|
545
|
+
files
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
return phases;
|
|
549
|
+
}
|
|
411
550
|
setState(patch) {
|
|
412
551
|
const prev = this.state;
|
|
413
552
|
const next = { ...prev, ...patch };
|
|
414
553
|
this.state = next;
|
|
415
554
|
this.emitter.emit("change", { prev, next });
|
|
555
|
+
if (patch.phases && patch.phases !== prev.phases) {
|
|
556
|
+
this.emitPhaseChanges(prev.phases, patch.phases);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
emitPhaseChanges(prevPhases, nextPhases) {
|
|
560
|
+
for (const phase of nextPhases) {
|
|
561
|
+
const prevPhase = prevPhases.find((p) => p.id === phase.id);
|
|
562
|
+
if (!prevPhase) {
|
|
563
|
+
this.emitter.emit("phaseChange", {
|
|
564
|
+
type: "added",
|
|
565
|
+
phase,
|
|
566
|
+
allPhases: nextPhases
|
|
567
|
+
});
|
|
568
|
+
} else if (this.hasPhaseChanged(prevPhase, phase)) {
|
|
569
|
+
this.emitter.emit("phaseChange", {
|
|
570
|
+
type: "updated",
|
|
571
|
+
phase,
|
|
572
|
+
allPhases: nextPhases
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
hasPhaseChanged(prev, next) {
|
|
578
|
+
if (prev.status !== next.status)
|
|
579
|
+
return true;
|
|
580
|
+
if (prev.files.length !== next.files.length)
|
|
581
|
+
return true;
|
|
582
|
+
for (let i = 0;i < prev.files.length; i++) {
|
|
583
|
+
if (prev.files[i].status !== next.files[i].status)
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
return false;
|
|
416
587
|
}
|
|
417
588
|
clear() {
|
|
418
589
|
this.state = INITIAL_STATE;
|
|
@@ -804,6 +975,15 @@ class BuildSession {
|
|
|
804
975
|
snapshot: () => this.workspace.snapshot(),
|
|
805
976
|
tree: () => buildFileTree(this.workspace.paths())
|
|
806
977
|
};
|
|
978
|
+
phases = {
|
|
979
|
+
list: () => this.state.get().phases,
|
|
980
|
+
current: () => this.state.get().phases.find((p) => p.status !== "completed" && p.status !== "cancelled"),
|
|
981
|
+
completed: () => this.state.get().phases.filter((p) => p.status === "completed"),
|
|
982
|
+
get: (id) => this.state.get().phases.find((p) => p.id === id),
|
|
983
|
+
count: () => this.state.get().phases.length,
|
|
984
|
+
allCompleted: () => this.state.get().phases.length > 0 && this.state.get().phases.every((p) => p.status === "completed"),
|
|
985
|
+
onChange: (cb) => this.state.onPhaseChange(cb)
|
|
986
|
+
};
|
|
807
987
|
wait = {
|
|
808
988
|
generationStarted: (options = {}) => this.waitForGenerationStarted(options),
|
|
809
989
|
generationComplete: (options = {}) => this.waitForGenerationComplete(options),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cf-vibesdk/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"bundle-types": "dts-bundle-generator --export-referenced-types false --no-check --project ./tsconfig.protocol.json -o ./dist/index.d.ts ./src/index.ts && bun run scripts/expand-drizzle-types.ts",
|
|
23
23
|
"typecheck": "tsc -p ./tsconfig.json --noEmit",
|
|
24
24
|
"test": "bun test test/*.test.ts",
|
|
25
|
-
"test:integration": "bun test --timeout 600000 test/integration/*.test.ts"
|
|
25
|
+
"test:integration": "bun test --timeout 600000 test/integration/*.test.ts",
|
|
26
|
+
"package": "bun run typecheck && bun run test && bun run build && bun run bundle-types"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"@cloudflare/workers-types": "^4.20241218.0",
|