@fulldivevr/workcord-cli 3.0.3 → 3.0.5

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.
@@ -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 adapters = pickAdapters(await checkAdapters(detected, opts.adapter, home), "auto", home);
91
+ const usable = await checkAdapters(detected, opts.adapter, home);
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,8 +184,8 @@ function collect(value, previous) {
183
184
  }
184
185
  const RUNNERS = { claude: runClaude, codex: runCodex };
185
186
  /**
186
- * Prints the binary each adapter uses and drops one that cannot run tasks, so it is
187
- * never advertised to the server. A Codex older than `app-server` exits at once.
187
+ * Prints the binary each adapter uses. A Codex that cannot answer `app-server`
188
+ * fails the start, since a legacy Codex CLI cannot run tasks at all.
188
189
  */
189
190
  export async function checkAdapters(installed, choice, home, log = (line) => process.stderr.write(line + "\n"), env = process.env) {
190
191
  const usable = {};
@@ -197,10 +198,13 @@ export async function checkAdapters(installed, choice, home, log = (line) => pro
197
198
  log(`ERROR: ${probe.message}`);
198
199
  if (probe.stderr)
199
200
  log(`machine: codex said: ${probe.stderr.replace(/\s+/g, " ").slice(-300)}`);
200
- continue;
201
+ throw new Error("codex cannot run tasks, see the ERROR line above.");
201
202
  }
203
+ usable.codex = binary;
204
+ log(`machine: codex at ${usable.codex}`);
205
+ continue;
202
206
  }
203
- else if (name === "claude" && (await binaryVersion(binary, childEnv(env, home))) === "unknown") {
207
+ if (name === "claude" && (await binaryVersion(binary, childEnv(env, home))) === "unknown") {
204
208
  log(`ERROR: claude at ${binary} does not answer "claude --version". Reinstall it: npm i -g @anthropic-ai/claude-code@latest`);
205
209
  continue;
206
210
  }
@@ -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) {
@@ -1,4 +1,5 @@
1
1
  import { access, constants } 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,17 +13,32 @@ 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; when one exists it is the codex, ahead of 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
+ }
15
29
  /** Absolute path of each installed agent CLI, found the way a shell would find it. */
16
- export async function detectAdapters(env = process.env) {
30
+ export async function detectAdapters(env = process.env, codexDesktop = codexDesktopPaths()) {
17
31
  const found = {};
18
- const dirs = String(env.PATH || "").split(delimiter).filter(Boolean);
19
- for (const name of ADAPTER_NAMES) {
20
- for (const dir of dirs) {
21
- const candidate = join(dir, name);
22
- if (await access(candidate, constants.X_OK).then(() => true, () => false)) {
23
- found[name] = candidate;
24
- break;
25
- }
32
+ for (const candidate of onPath("claude", env)) {
33
+ if (await isExecutable(candidate)) {
34
+ found.claude = candidate;
35
+ break;
36
+ }
37
+ }
38
+ for (const candidate of [...codexDesktop, ...onPath("codex", env)]) {
39
+ if (await isExecutable(candidate)) {
40
+ found.codex = candidate;
41
+ break;
26
42
  }
27
43
  }
28
44
  return found;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fulldivevr/workcord-cli",
3
- "version": "3.0.3",
3
+ "version": "3.0.5",
4
4
  "description": "Agent-first CLI using persistent Hermes tenant chat, with advanced API diagnostics.",
5
5
  "private": false,
6
6
  "license": "UNLICENSED",