@fulldivevr/workcord-cli 3.0.3 → 3.0.4
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/commands/machine.js +29 -12
- package/dist/machine/adapters/codex.js +1 -1
- package/dist/machine/agents.js +38 -10
- package/package.json +1 -1
package/dist/commands/machine.js
CHANGED
|
@@ -8,7 +8,7 @@ import { deleteMachineFile, getMachineLockPath, getMachinePath, getMachineThread
|
|
|
8
8
|
import { normalizeBaseUrl } from "../http.js";
|
|
9
9
|
import { runClaude } from "../machine/adapters/claude.js";
|
|
10
10
|
import { binaryVersion, probeCodex, runCodex } from "../machine/adapters/codex.js";
|
|
11
|
-
import { ADAPTER_NAMES, childEnv, detectAdapters } from "../machine/agents.js";
|
|
11
|
+
import { ADAPTER_NAMES, childEnv, codexCandidates, detectAdapters } from "../machine/agents.js";
|
|
12
12
|
import { handleTask, TaskContexts } from "../machine/handler.js";
|
|
13
13
|
import { pairComputer } from "../machine/pair.js";
|
|
14
14
|
import { nextRotationDelay, rotateMachineKey, ROTATE_EVERY_MS } from "../machine/rotate.js";
|
|
@@ -88,7 +88,8 @@ export function registerMachine(program) {
|
|
|
88
88
|
const detected = await detectAdapters();
|
|
89
89
|
pickAdapters(detected, opts.adapter, home);
|
|
90
90
|
// checkAdapters already narrowed the set to the chosen adapter.
|
|
91
|
-
const
|
|
91
|
+
const usable = await checkAdapters(detected, opts.adapter, home, undefined, process.env, await codexCandidates());
|
|
92
|
+
const adapters = pickAdapters(usable, "auto", home);
|
|
92
93
|
const baseUrl = credential.baseUrl || (await getBaseUrl());
|
|
93
94
|
const installationId = await readOrCreateInstallationId();
|
|
94
95
|
const release = await acquireMachineLock(getMachineLockPath());
|
|
@@ -183,24 +184,40 @@ function collect(value, previous) {
|
|
|
183
184
|
}
|
|
184
185
|
const RUNNERS = { claude: runClaude, codex: runCodex };
|
|
185
186
|
/**
|
|
186
|
-
* Prints the binary each adapter uses
|
|
187
|
-
*
|
|
187
|
+
* Prints the binary each adapter uses. Codex runs from the first candidate that
|
|
188
|
+
* answers `app-server`; an installed Codex where none does fails the start, since
|
|
189
|
+
* a legacy Codex CLI cannot run tasks at all.
|
|
188
190
|
*/
|
|
189
|
-
export async function checkAdapters(installed, choice, home, log = (line) => process.stderr.write(line + "\n"), env = process.env) {
|
|
191
|
+
export async function checkAdapters(installed, choice, home, log = (line) => process.stderr.write(line + "\n"), env = process.env, codexCandidates = installed.codex ? [installed.codex] : []) {
|
|
190
192
|
const usable = {};
|
|
191
193
|
for (const [name, binary] of Object.entries(installed)) {
|
|
192
194
|
if (!binary || (choice !== "auto" && choice !== name))
|
|
193
195
|
continue;
|
|
194
196
|
if (name === "codex") {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (probe.
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
const failed = [];
|
|
198
|
+
for (const candidate of codexCandidates) {
|
|
199
|
+
const probe = await probeCodex(candidate, env, home);
|
|
200
|
+
if (probe.ok) {
|
|
201
|
+
usable.codex = candidate;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
failed.push({ ...probe, binary: candidate });
|
|
201
205
|
}
|
|
206
|
+
if (!usable.codex) {
|
|
207
|
+
for (const probe of failed) {
|
|
208
|
+
log(`ERROR: ${probe.message}`);
|
|
209
|
+
if (probe.stderr)
|
|
210
|
+
log(`machine: codex said: ${probe.stderr.replace(/\s+/g, " ").slice(-300)}`);
|
|
211
|
+
}
|
|
212
|
+
throw new Error("codex is installed but none of its copies can run tasks, see the ERROR lines above.");
|
|
213
|
+
}
|
|
214
|
+
for (const probe of failed) {
|
|
215
|
+
log(`machine: warning: skipped codex at ${probe.binary} (version ${probe.version}), it cannot run "codex app-server"`);
|
|
216
|
+
}
|
|
217
|
+
log(`machine: codex at ${usable.codex}`);
|
|
218
|
+
continue;
|
|
202
219
|
}
|
|
203
|
-
|
|
220
|
+
if (name === "claude" && (await binaryVersion(binary, childEnv(env, home))) === "unknown") {
|
|
204
221
|
log(`ERROR: claude at ${binary} does not answer "claude --version". Reinstall it: npm i -g @anthropic-ai/claude-code@latest`);
|
|
205
222
|
continue;
|
|
206
223
|
}
|
|
@@ -52,7 +52,7 @@ export function binaryVersion(binary, env) {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
export function codexUnusableMessage(binary, version) {
|
|
55
|
-
return `codex at ${binary} (version ${version}) cannot run "codex app-server". Update it: npm i -g @openai/codex@latest`;
|
|
55
|
+
return `codex at ${binary} (version ${version}) cannot run "codex app-server". Update it: npm i -g @openai/codex@latest, or install the Codex desktop app (it bundles a current codex)`;
|
|
56
56
|
}
|
|
57
57
|
/** Starts `codex app-server` and accepts it once it answers `initialize`; a legacy Codex CLI never does. */
|
|
58
58
|
export async function probeCodex(binary, env, home, timeoutMs = PROBE_TIMEOUT_MS) {
|
package/dist/machine/agents.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { access, constants } from "node:fs/promises";
|
|
1
|
+
import { access, constants, realpath } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
2
3
|
import { delimiter, join } from "node:path";
|
|
3
4
|
export const ADAPTER_NAMES = ["claude", "codex"];
|
|
4
5
|
/**
|
|
@@ -12,19 +13,46 @@ export function childEnv(env = process.env, home) {
|
|
|
12
13
|
return inherited;
|
|
13
14
|
return { ...inherited, HOME: home, CODEX_HOME: join(home, ".codex"), CLAUDE_CONFIG_DIR: join(home, ".claude") };
|
|
14
15
|
}
|
|
16
|
+
/** Codex desktop bundles a current codex that is not on PATH. */
|
|
17
|
+
export function codexDesktopPaths(platform = process.platform, home = homedir()) {
|
|
18
|
+
if (platform !== "darwin")
|
|
19
|
+
return [];
|
|
20
|
+
return ["/Applications", join(home, "Applications")].flatMap((dir) => [
|
|
21
|
+
join(dir, "ChatGPT.app/Contents/Resources/codex-cli/bin/codex"),
|
|
22
|
+
join(dir, "Codex.app/Contents/Resources/codex"),
|
|
23
|
+
]);
|
|
24
|
+
}
|
|
25
|
+
const isExecutable = (path) => access(path, constants.X_OK).then(() => true, () => false);
|
|
26
|
+
function onPath(name, env) {
|
|
27
|
+
return String(env.PATH || "").split(delimiter).filter(Boolean).map((dir) => join(dir, name));
|
|
28
|
+
}
|
|
29
|
+
/** Every installed codex, PATH order first, then the desktop bundles, one per real file. */
|
|
30
|
+
export async function codexCandidates(env = process.env, desktop = codexDesktopPaths()) {
|
|
31
|
+
const seen = new Set();
|
|
32
|
+
const found = [];
|
|
33
|
+
for (const candidate of [...onPath("codex", env), ...desktop]) {
|
|
34
|
+
if (!(await isExecutable(candidate)))
|
|
35
|
+
continue;
|
|
36
|
+
const real = await realpath(candidate).catch(() => candidate);
|
|
37
|
+
if (seen.has(real))
|
|
38
|
+
continue;
|
|
39
|
+
seen.add(real);
|
|
40
|
+
found.push(candidate);
|
|
41
|
+
}
|
|
42
|
+
return found;
|
|
43
|
+
}
|
|
15
44
|
/** Absolute path of each installed agent CLI, found the way a shell would find it. */
|
|
16
|
-
export async function detectAdapters(env = process.env) {
|
|
45
|
+
export async function detectAdapters(env = process.env, codexDesktop = codexDesktopPaths()) {
|
|
17
46
|
const found = {};
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (await access(candidate, constants.X_OK).then(() => true, () => false)) {
|
|
23
|
-
found[name] = candidate;
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
47
|
+
for (const candidate of onPath("claude", env)) {
|
|
48
|
+
if (await isExecutable(candidate)) {
|
|
49
|
+
found.claude = candidate;
|
|
50
|
+
break;
|
|
26
51
|
}
|
|
27
52
|
}
|
|
53
|
+
const [codex] = await codexCandidates(env, codexDesktop);
|
|
54
|
+
if (codex)
|
|
55
|
+
found.codex = codex;
|
|
28
56
|
return found;
|
|
29
57
|
}
|
|
30
58
|
//# sourceMappingURL=agents.js.map
|