@turns/dev 0.0.12 → 0.0.14
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/agent-anchor.js +8 -0
- package/dist/codex-update.js +21 -4
- package/dist/harness-registry.js +31 -6
- package/dist/queue-worker.js +7 -1
- package/package.json +1 -1
package/dist/agent-anchor.js
CHANGED
|
@@ -595,6 +595,7 @@ class CodexAppServerTransport extends LineTransport {
|
|
|
595
595
|
: 'workspace-write',
|
|
596
596
|
approvalPolicy: this.anchor.permissionMode === 'bypassPermissions' ? 'never' : 'on-request',
|
|
597
597
|
};
|
|
598
|
+
const resumePath = !createSession && fs.existsSync(this.anchor.transcriptPath) ? this.anchor.transcriptPath : '';
|
|
598
599
|
const opened = createSession
|
|
599
600
|
? await this.request('thread/start', {
|
|
600
601
|
...configuration,
|
|
@@ -606,6 +607,13 @@ class CodexAppServerTransport extends LineTransport {
|
|
|
606
607
|
}, 90_000)
|
|
607
608
|
: await this.request('thread/resume', {
|
|
608
609
|
threadId: this.threadId,
|
|
610
|
+
// Codex's thread registry may not include a still-existing rollout
|
|
611
|
+
// after an upgrade. Supplying the descriptor path lets Codex resume
|
|
612
|
+
// the exact on-disk history instead of rejecting every later
|
|
613
|
+
// queued turn as "thread not found". Do not send Turns' temporary
|
|
614
|
+
// placeholder for a newly bound session: a non-empty path takes
|
|
615
|
+
// precedence over a valid thread id in the app-server protocol.
|
|
616
|
+
...(resumePath ? { path: resumePath } : {}),
|
|
609
617
|
...configuration,
|
|
610
618
|
...(this.anchor.model && this.anchor.model !== 'auto' ? { model: this.anchor.model } : {}),
|
|
611
619
|
...(this.anchor.thinkingLevel
|
package/dist/codex-update.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Machine-local Codex CLI self-update, callable by the Turns daemon. */
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import { resolveHarnessCommand } from "./harness-registry.js";
|
|
4
|
+
import { codexCommandIsAppManaged, resolveHarnessCommand } from "./harness-registry.js";
|
|
5
5
|
const VERSION_TIMEOUT_MS = 15_000;
|
|
6
6
|
const UPDATE_TIMEOUT_MS = 5 * 60_000;
|
|
7
7
|
const OUTPUT_LIMIT = 64 * 1024;
|
|
@@ -58,20 +58,28 @@ function commandFailure(result, fallback) {
|
|
|
58
58
|
}
|
|
59
59
|
export function createCodexUpdateManager(dependencies = {}) {
|
|
60
60
|
const resolveCommand = dependencies.resolveCommand || (() => resolveHarnessCommand('codex'));
|
|
61
|
+
const isAppManaged = dependencies.isAppManaged || codexCommandIsAppManaged;
|
|
61
62
|
const run = dependencies.run || runCommand;
|
|
62
63
|
let active = null;
|
|
64
|
+
const updateCommand = () => {
|
|
65
|
+
const command = resolveCommand();
|
|
66
|
+
if (isAppManaged(command)) {
|
|
67
|
+
throw new Error('Codex is managed by the macOS app. Update the Codex app to update its bundled CLI.');
|
|
68
|
+
}
|
|
69
|
+
return command;
|
|
70
|
+
};
|
|
63
71
|
const perform = async () => {
|
|
64
|
-
const beforeRun = await run(
|
|
72
|
+
const beforeRun = await run(updateCommand(), ['--version'], VERSION_TIMEOUT_MS);
|
|
65
73
|
if (beforeRun.failed || beforeRun.timedOut || beforeRun.exitCode !== 0) {
|
|
66
74
|
throw commandFailure(beforeRun, 'Codex is not installed or its version could not be read.');
|
|
67
75
|
}
|
|
68
76
|
const previousVersion = parseCodexVersion(beforeRun.output);
|
|
69
|
-
const updateRun = await run(
|
|
77
|
+
const updateRun = await run(updateCommand(), ['update'], UPDATE_TIMEOUT_MS);
|
|
70
78
|
if (updateRun.failed || updateRun.timedOut || updateRun.exitCode !== 0) {
|
|
71
79
|
throw commandFailure(updateRun, 'Codex could not update itself. Run `codex update` in a terminal on the machine.');
|
|
72
80
|
}
|
|
73
81
|
// Resolve again because the updater may replace a shim or install path.
|
|
74
|
-
const afterRun = await run(
|
|
82
|
+
const afterRun = await run(updateCommand(), ['--version'], VERSION_TIMEOUT_MS);
|
|
75
83
|
if (afterRun.failed || afterRun.timedOut || afterRun.exitCode !== 0) {
|
|
76
84
|
throw commandFailure(afterRun, 'Codex updated, but its new version could not be read.');
|
|
77
85
|
}
|
|
@@ -83,6 +91,15 @@ export function createCodexUpdateManager(dependencies = {}) {
|
|
|
83
91
|
};
|
|
84
92
|
};
|
|
85
93
|
return {
|
|
94
|
+
/** App-bundled Codex is updated with the app, not by its CLI command. */
|
|
95
|
+
available() {
|
|
96
|
+
try {
|
|
97
|
+
return !isAppManaged(resolveCommand());
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
},
|
|
86
103
|
/** Concurrent clicks share one installer process instead of racing writes. */
|
|
87
104
|
update() {
|
|
88
105
|
if (!active)
|
package/dist/harness-registry.js
CHANGED
|
@@ -269,30 +269,55 @@ function executable(candidate) {
|
|
|
269
269
|
return '';
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
|
-
export
|
|
272
|
+
export const MACOS_BUNDLED_CODEX_PATH = '/Applications/Codex.app/Contents/Resources/codex';
|
|
273
|
+
/** The app owns this executable and updates it as part of the app bundle. */
|
|
274
|
+
export function codexCommandIsAppManaged(command) {
|
|
275
|
+
let candidate = path.resolve(command);
|
|
276
|
+
try {
|
|
277
|
+
candidate = fs.realpathSync(candidate);
|
|
278
|
+
}
|
|
279
|
+
catch { }
|
|
280
|
+
return /\/Codex\.app\/Contents\/Resources\/codex$/u.test(candidate.replaceAll('\\', '/'));
|
|
281
|
+
}
|
|
282
|
+
export function resolveHarnessCommand(harness, profile = {}, dependencies = {}) {
|
|
283
|
+
const env = dependencies.env || process.env;
|
|
284
|
+
const home = dependencies.home || HOME;
|
|
273
285
|
const envName = `TURNS_${String(harness).toUpperCase().replaceAll('-', '_')}_BIN`;
|
|
274
|
-
const configured = String(
|
|
286
|
+
const configured = String(env[envName] || '').trim();
|
|
275
287
|
if (configured) {
|
|
276
288
|
const found = executable(path.resolve(configured));
|
|
277
289
|
if (found)
|
|
278
290
|
return found;
|
|
279
291
|
throw new Error(`${envName} is not executable: ${configured}`);
|
|
280
292
|
}
|
|
293
|
+
// The desktop app and its bundled CLI are released together. On macOS,
|
|
294
|
+
// prefer that known-compatible binary over independently installed npm or
|
|
295
|
+
// Homebrew copies while preserving TURNS_CODEX_BIN as the explicit escape
|
|
296
|
+
// hatch above.
|
|
297
|
+
if (harness === 'codex' && (dependencies.platform || process.platform) === 'darwin') {
|
|
298
|
+
const candidates = dependencies.bundledCodexPaths ||
|
|
299
|
+
[MACOS_BUNDLED_CODEX_PATH, path.join(home, 'Applications', 'Codex.app', 'Contents', 'Resources', 'codex')];
|
|
300
|
+
for (const candidate of candidates) {
|
|
301
|
+
const found = executable(candidate);
|
|
302
|
+
if (found)
|
|
303
|
+
return found;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
281
306
|
const command = profile.command || ({ cursor: 'cursor-agent', antigravity: 'agy' }[harness] ?? harness);
|
|
282
|
-
for (const directory of String(
|
|
307
|
+
for (const directory of String(env.PATH || '').split(path.delimiter)) {
|
|
283
308
|
const found = executable(path.join(directory, command));
|
|
284
309
|
if (found)
|
|
285
310
|
return found;
|
|
286
311
|
}
|
|
287
312
|
for (const segments of profile.homePaths || []) {
|
|
288
|
-
const found = executable(path.join(
|
|
313
|
+
const found = executable(path.join(home, ...segments));
|
|
289
314
|
if (found)
|
|
290
315
|
return found;
|
|
291
316
|
}
|
|
292
317
|
if (harness === 'cursor') {
|
|
293
318
|
for (const candidate of [
|
|
294
|
-
path.join(
|
|
295
|
-
path.join(
|
|
319
|
+
path.join(home, '.local', 'bin', 'cursor-agent'),
|
|
320
|
+
path.join(home, '.local', 'bin', 'agent'),
|
|
296
321
|
]) {
|
|
297
322
|
const found = executable(candidate);
|
|
298
323
|
if (found)
|
package/dist/queue-worker.js
CHANGED
|
@@ -1372,7 +1372,13 @@ export async function watchQueuedPrompts({ rpc, options, machine, getDescriptors
|
|
|
1372
1372
|
type: 'machine.register',
|
|
1373
1373
|
machineId: machine,
|
|
1374
1374
|
machineHostname: os.hostname(),
|
|
1375
|
-
capabilities: {
|
|
1375
|
+
capabilities: {
|
|
1376
|
+
fileSearch: true,
|
|
1377
|
+
git: true,
|
|
1378
|
+
fileRanges: true,
|
|
1379
|
+
harnessAuth: true,
|
|
1380
|
+
...(codexUpdate.available() ? { codexUpdate: true } : {}),
|
|
1381
|
+
},
|
|
1376
1382
|
workerId,
|
|
1377
1383
|
workerInstanceId,
|
|
1378
1384
|
anchorSessionIds: [...anchors.keys()],
|