@playcraft/cli 0.1.17 → 0.1.19
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/workspace-runtime/codex/app-server.js +24 -62
- package/dist/workspace-runtime/codex/loopback.js +3 -0
- package/dist/workspace-runtime/persistence/journal.js +1 -26
- package/dist/workspace-runtime/persistence/sqlite-worker.js +2 -13
- package/dist/workspace-runtime/server/dispatch.js +115 -413
- package/dist/workspace-runtime/server/execution-group.js +0 -3
- package/dist/workspace-runtime/server/http.js +5 -58
- package/dist/workspace-runtime/workspaces/files.js +2 -33
- package/dist/workspace-runtime/workspaces/git.js +67 -81
- package/package.json +3 -3
- package/dist/workspace-runtime/codex/transcripts.js +0 -94
- package/dist/workspace-runtime/workspaces/checkpoint-saves.js +0 -70
- package/dist/workspace-runtime/workspaces/file-snapshot.js +0 -354
|
@@ -8,7 +8,6 @@ import { portableCodexConfig, writePortableCodexCatalog } from './config-toml.js
|
|
|
8
8
|
import { createJsonRpcStdioClient } from './jsonrpc-stdio.js';
|
|
9
9
|
import { projectNativeThread } from '@playcraft/common/cloud-workspace/runtime';
|
|
10
10
|
import { collectedEvent, NativePreparationError } from './native-adapter.js';
|
|
11
|
-
import { exportTranscript } from './transcripts.js';
|
|
12
11
|
import { readProjectSkillsLockFile } from '@playcraft/common/project-skills';
|
|
13
12
|
import { freezeWorkspaceSkills } from '../workspaces/skills.js';
|
|
14
13
|
function asRecord(value) {
|
|
@@ -75,18 +74,6 @@ export function createCodexAppServerAdapter(options) {
|
|
|
75
74
|
release();
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
|
-
function transcriptHome() {
|
|
79
|
-
if (!launcherSocket)
|
|
80
|
-
return { homeDir };
|
|
81
|
-
return {
|
|
82
|
-
homeDir,
|
|
83
|
-
launcher: {
|
|
84
|
-
socketPath: launcherSocket,
|
|
85
|
-
runtimeEpoch: executionEpoch,
|
|
86
|
-
cwd: options.config.workspaceMode === 'multi' ? '/home/playcraft-dev' : options.config.workspaceRoot,
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
77
|
async function ensureStarted() {
|
|
91
78
|
if (closed)
|
|
92
79
|
throw new Error('codex_not_started');
|
|
@@ -382,44 +369,6 @@ export function createCodexAppServerAdapter(options) {
|
|
|
382
369
|
throw new Error('codex_not_started');
|
|
383
370
|
return { pid: next.pid, startedAt: next.startedAt };
|
|
384
371
|
},
|
|
385
|
-
async canContinueThread(input) {
|
|
386
|
-
return withPreparation(async () => {
|
|
387
|
-
await ensureStarted();
|
|
388
|
-
if (!rpc)
|
|
389
|
-
return false;
|
|
390
|
-
try {
|
|
391
|
-
const existing = threads.get(input.threadId);
|
|
392
|
-
if (existing && existing.sessionId !== input.sessionId)
|
|
393
|
-
return false;
|
|
394
|
-
if (existing?.turnActive)
|
|
395
|
-
return false;
|
|
396
|
-
const prepared = await lockedThreadConfig(input.cwd ?? '', input.modelConfigId, input.sessionId);
|
|
397
|
-
if (existing?.needsRecreate)
|
|
398
|
-
return false;
|
|
399
|
-
if (existing?.loaded) {
|
|
400
|
-
if (existing.skillsHash !== prepared.skillsHash || existing.discoveryHash !== prepared.discoveryHash) {
|
|
401
|
-
if (!existing.hasNativeHistory)
|
|
402
|
-
return false;
|
|
403
|
-
await restartForSkills(input.threadId, prepared.modelConfigId);
|
|
404
|
-
}
|
|
405
|
-
prepared.activate();
|
|
406
|
-
return threads.get(input.threadId)?.sessionId === input.sessionId;
|
|
407
|
-
}
|
|
408
|
-
const result = await rpc.request('thread/resume', { threadId: input.threadId, cwd: input.cwd,
|
|
409
|
-
...prepared.params }, 30_000);
|
|
410
|
-
const resumedId = result.thread?.id ?? result.threadId;
|
|
411
|
-
if (resumedId !== input.threadId)
|
|
412
|
-
return false;
|
|
413
|
-
prepared.activate();
|
|
414
|
-
threads.set(input.threadId, { sessionId: input.sessionId, cwd: input.cwd, skillsHash: prepared.skillsHash,
|
|
415
|
-
discoveryHash: prepared.discoveryHash, modelConfigId: prepared.modelConfigId, hasNativeHistory: true, loaded: true });
|
|
416
|
-
return true;
|
|
417
|
-
}
|
|
418
|
-
catch {
|
|
419
|
-
return false;
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
},
|
|
423
372
|
async readThread(input) {
|
|
424
373
|
try {
|
|
425
374
|
await ensureStarted();
|
|
@@ -436,17 +385,6 @@ export function createCodexAppServerAdapter(options) {
|
|
|
436
385
|
return undefined;
|
|
437
386
|
}
|
|
438
387
|
},
|
|
439
|
-
exportThread(input) {
|
|
440
|
-
return exportTranscript(transcriptHome(), input.threadId);
|
|
441
|
-
},
|
|
442
|
-
async captureNativeCheckpoint(input) {
|
|
443
|
-
const thread = threads.get(input.threadId);
|
|
444
|
-
if (!thread || thread.sessionId !== input.sessionId)
|
|
445
|
-
return undefined;
|
|
446
|
-
if (!thread.lastTurnId)
|
|
447
|
-
return { threadId: input.threadId };
|
|
448
|
-
return { threadId: input.threadId, nativeDurableTurnId: thread.lastTurnId };
|
|
449
|
-
},
|
|
450
388
|
async start() {
|
|
451
389
|
await ensureStarted();
|
|
452
390
|
},
|
|
@@ -470,6 +408,30 @@ export function createCodexAppServerAdapter(options) {
|
|
|
470
408
|
return { threadId, modelConfigId: prepared.modelConfigId };
|
|
471
409
|
});
|
|
472
410
|
},
|
|
411
|
+
async resumeThread(input) {
|
|
412
|
+
return withPreparation(async () => {
|
|
413
|
+
await ensureStarted();
|
|
414
|
+
if (!rpc)
|
|
415
|
+
throw new Error('codex_not_started');
|
|
416
|
+
const prepared = await lockedThreadConfig(input.cwd, input.modelConfigId, input.sessionId);
|
|
417
|
+
let resumedId;
|
|
418
|
+
try {
|
|
419
|
+
const resumed = await rpc.request('thread/resume', {
|
|
420
|
+
threadId: input.threadId, cwd: input.cwd, ...prepared.params,
|
|
421
|
+
}, 30_000);
|
|
422
|
+
resumedId = resumed.thread?.id;
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
return undefined;
|
|
426
|
+
}
|
|
427
|
+
if (resumedId !== input.threadId)
|
|
428
|
+
return undefined;
|
|
429
|
+
prepared.activate();
|
|
430
|
+
threads.set(input.threadId, { sessionId: input.sessionId, cwd: input.cwd, skillsHash: prepared.skillsHash,
|
|
431
|
+
discoveryHash: prepared.discoveryHash, modelConfigId: prepared.modelConfigId, hasNativeHistory: true, loaded: true });
|
|
432
|
+
return { modelConfigId: prepared.modelConfigId };
|
|
433
|
+
});
|
|
434
|
+
},
|
|
473
435
|
async startTurn(input) {
|
|
474
436
|
return withPreparation(async () => {
|
|
475
437
|
await ensureStarted();
|
|
@@ -26,6 +26,9 @@ export function createLoopbackNativeAdapter() {
|
|
|
26
26
|
threads.set(threadId, { sessionId: input.sessionId, items: [] });
|
|
27
27
|
return { threadId };
|
|
28
28
|
},
|
|
29
|
+
async resumeThread(input) {
|
|
30
|
+
return threads.get(input.threadId)?.sessionId === input.sessionId ? {} : undefined;
|
|
31
|
+
},
|
|
29
32
|
async startTurn(input) {
|
|
30
33
|
const thread = threads.get(input.threadId);
|
|
31
34
|
if (!thread || thread.sessionId !== input.sessionId)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
|
-
import { workspaceRegistrationSchema,
|
|
4
|
+
import { workspaceRegistrationSchema, operationRecordSchema, pendingSourceEventSchema, replayPinResultSchema, retainedRangeHasGap, sourceAckSchema, sourceEventKey, sourceEventReceiptSchema, terminalRecordSchema, } from '@playcraft/common/cloud-workspace/runtime';
|
|
5
5
|
import { SqliteWorkerClient } from './sqlite-client.js';
|
|
6
6
|
export const RUNTIME_STATE_FILE = 'runtime-state.sqlite';
|
|
7
7
|
export const LEGACY_JOURNAL_FILE = 'journal.json';
|
|
@@ -116,21 +116,6 @@ export class RuntimeJournal {
|
|
|
116
116
|
listTerminals(workspaceId) {
|
|
117
117
|
return Object.values(this.snapshot.terminals).filter((item) => !workspaceId || item.workspaceId === workspaceId);
|
|
118
118
|
}
|
|
119
|
-
getCheckpoint(checkpointId) {
|
|
120
|
-
return this.snapshot.checkpoints[checkpointId];
|
|
121
|
-
}
|
|
122
|
-
fileCheckpointId() {
|
|
123
|
-
return this.snapshot.fileCheckpointId;
|
|
124
|
-
}
|
|
125
|
-
consistentCheckpointId() {
|
|
126
|
-
return this.snapshot.consistentCheckpointId;
|
|
127
|
-
}
|
|
128
|
-
async setFileCheckpointId(checkpointId) {
|
|
129
|
-
await this.run('setCheckpointId', { field: 'file', id: checkpointId });
|
|
130
|
-
}
|
|
131
|
-
async setConsistentCheckpointId(checkpointId) {
|
|
132
|
-
await this.run('setCheckpointId', { field: 'consistent', id: checkpointId });
|
|
133
|
-
}
|
|
134
119
|
listManagedWrites(workspaceId) {
|
|
135
120
|
return Object.values(this.snapshot.managedWrites).filter((item) => !workspaceId || item.workspaceId === workspaceId);
|
|
136
121
|
}
|
|
@@ -266,11 +251,6 @@ export class RuntimeJournal {
|
|
|
266
251
|
await this.run('upsertRecord', { bucket: 'terminals', key: parsed.terminalId, body: parsed });
|
|
267
252
|
return parsed;
|
|
268
253
|
}
|
|
269
|
-
async putCheckpoint(checkpoint) {
|
|
270
|
-
const parsed = checkpointRecordSchema.parse(checkpoint);
|
|
271
|
-
await this.run('upsertRecord', { bucket: 'checkpoints', key: parsed.checkpointId, body: parsed });
|
|
272
|
-
return parsed;
|
|
273
|
-
}
|
|
274
254
|
async putFileRevision(path, record, workspaceId) {
|
|
275
255
|
if (workspaceId) {
|
|
276
256
|
await this.run('upsertRecord', { bucket: `workspace_files:${workspaceId}`, key: path, body: record });
|
|
@@ -278,10 +258,6 @@ export class RuntimeJournal {
|
|
|
278
258
|
}
|
|
279
259
|
await this.run('upsertRecord', { bucket: 'file_revisions', key: path, body: record });
|
|
280
260
|
}
|
|
281
|
-
async replaceFileRevisions(records, workspaceId) {
|
|
282
|
-
const entries = Object.entries(records).map(([key, body]) => ({ key, body }));
|
|
283
|
-
await this.run('replaceBucket', { bucket: workspaceId ? `workspace_files:${workspaceId}` : 'file_revisions', entries });
|
|
284
|
-
}
|
|
285
261
|
async markDestroyed() {
|
|
286
262
|
await this.run('setDestroyed', {});
|
|
287
263
|
}
|
|
@@ -367,7 +343,6 @@ function emptySnapshot() {
|
|
|
367
343
|
events: {},
|
|
368
344
|
sessions: {},
|
|
369
345
|
terminals: {},
|
|
370
|
-
checkpoints: {},
|
|
371
346
|
fileRevisions: {},
|
|
372
347
|
approvals: {},
|
|
373
348
|
managedWrites: {},
|
|
@@ -12,7 +12,7 @@ import { chmodSync, mkdirSync } from 'node:fs';
|
|
|
12
12
|
import { dirname } from 'node:path';
|
|
13
13
|
import { parentPort } from 'node:worker_threads';
|
|
14
14
|
import Database from 'better-sqlite3-pinned';
|
|
15
|
-
import { hashOperationPayload, operationRecordSchema, pendingSourceEventSchema,
|
|
15
|
+
import { hashOperationPayload, operationRecordSchema, pendingSourceEventSchema, terminalRecordSchema, workspaceRegistrationSchema, transitionOperation, canTransitionTurn, } from '@playcraft/common/cloud-workspace/runtime';
|
|
16
16
|
const SCHEMA_VERSION = 1;
|
|
17
17
|
const MIN_SQLITE = [3, 51, 3];
|
|
18
18
|
const BINDING = 'better-sqlite3@13.0.3';
|
|
@@ -68,9 +68,6 @@ function dispatch(type, payload) {
|
|
|
68
68
|
case 'setDestroyed':
|
|
69
69
|
setMeta(database, 'destroyed', '1');
|
|
70
70
|
return;
|
|
71
|
-
case 'setCheckpointId':
|
|
72
|
-
setMeta(database, body.field === 'consistent' ? 'consistent_checkpoint_id' : 'file_checkpoint_id', String(body.id));
|
|
73
|
-
return;
|
|
74
71
|
case 'noteExecutionGroup':
|
|
75
72
|
setMeta(database, 'execution_group', JSON.stringify({ ...(executionGroup(database)), ...asRecord(body.group) }));
|
|
76
73
|
return;
|
|
@@ -298,7 +295,6 @@ function importLegacy(database, legacy) {
|
|
|
298
295
|
return typeof session.sessionId === 'string' && typeof session.threadId === 'string';
|
|
299
296
|
});
|
|
300
297
|
importBucket(database, 'terminals', asRecord(snapshot.terminals), (value) => terminalRecordSchema.safeParse(value).success);
|
|
301
|
-
importBucket(database, 'checkpoints', asRecord(snapshot.checkpoints), (value) => checkpointRecordSchema.safeParse(value).success);
|
|
302
298
|
importBucket(database, 'approvals', asRecord(snapshot.approvals), (value) => isApproval(value));
|
|
303
299
|
importBucket(database, 'managed_writes', asRecord(snapshot.managedWrites), () => true);
|
|
304
300
|
importBucket(database, 'file_revisions', asRecord(snapshot.fileRevisions), () => true);
|
|
@@ -307,11 +303,6 @@ function importLegacy(database, legacy) {
|
|
|
307
303
|
for (const [workspaceId, files] of Object.entries(workspaceFiles)) {
|
|
308
304
|
importBucket(database, `workspace_files:${workspaceId}`, asRecord(files), () => true);
|
|
309
305
|
}
|
|
310
|
-
if (typeof snapshot.fileCheckpointId === 'string')
|
|
311
|
-
setMeta(database, 'file_checkpoint_id', snapshot.fileCheckpointId);
|
|
312
|
-
if (typeof snapshot.consistentCheckpointId === 'string') {
|
|
313
|
-
setMeta(database, 'consistent_checkpoint_id', snapshot.consistentCheckpointId);
|
|
314
|
-
}
|
|
315
306
|
setMeta(database, 'accepted_lease_fence', String(Number(snapshot.acceptedLeaseFence) || 0));
|
|
316
307
|
setMeta(database, 'destroyed', snapshot.destroyed === true ? '1' : '0');
|
|
317
308
|
setMeta(database, 'runtime_epoch', String(runtimeEpoch));
|
|
@@ -441,6 +432,7 @@ function bindTurn(database, body) {
|
|
|
441
432
|
turnBlock: undefined,
|
|
442
433
|
activeTurnId: String(body.turnId),
|
|
443
434
|
threadId: String(body.threadId),
|
|
435
|
+
recoveryContextPending: undefined,
|
|
444
436
|
});
|
|
445
437
|
}
|
|
446
438
|
const threadId = String(body.threadId);
|
|
@@ -701,13 +693,10 @@ function dump(database) {
|
|
|
701
693
|
events: eventsObject(database),
|
|
702
694
|
sessions: bucketObject(database, 'sessions'),
|
|
703
695
|
terminals: bucketObject(database, 'terminals'),
|
|
704
|
-
checkpoints: bucketObject(database, 'checkpoints'),
|
|
705
696
|
fileRevisions: bucketObject(database, 'file_revisions'),
|
|
706
697
|
approvals: bucketObject(database, 'approvals'),
|
|
707
698
|
managedWrites: bucketObject(database, 'managed_writes'),
|
|
708
699
|
workspaceDependencies: bucketObject(database, 'workspace_dependencies'),
|
|
709
|
-
fileCheckpointId: metaString(database, 'file_checkpoint_id') || undefined,
|
|
710
|
-
consistentCheckpointId: metaString(database, 'consistent_checkpoint_id') || undefined,
|
|
711
700
|
executionGroup: executionGroup(database),
|
|
712
701
|
runtimeEpoch: metaNumber(database, 'runtime_epoch'),
|
|
713
702
|
lastSourceSequence: currentCounter(database),
|