fix: daemon mode spawn path — closes #27 #30

Merged
xiaoju merged 5 commits from fix/daemon-spawn-path into main 2026-04-22 14:21:33 +00:00
Owner

What

Fix nerve start -d daemon mode — child process was exiting immediately, making daemon mode completely non-functional.

Why

runDaemon() used fileURLToPath(import.meta.url) as the spawn script, which points to start.js (the command module), not cli.js (the CLI entry). The child ran node start.js start which has no CLI routing logic and exits immediately.

Changes

  • packages/cli/src/commands/start.ts: Added cliEntryScript() function that resolves the correct CLI entry path (cli.js) via process.argv[1], import.meta.url, or relative path fallback. Changed spawn() to use this resolved path.

Verified

$ nerve start -d
✅ Nerve daemon started (pid 32035).
$ nerve status
✅ Nerve daemon is running.
   pid:     32035
   uptime:  2s
$ nerve stop
✅ Daemon stopped.

Ref

Closes #27

## What Fix `nerve start -d` daemon mode — child process was exiting immediately, making daemon mode completely non-functional. ## Why `runDaemon()` used `fileURLToPath(import.meta.url)` as the spawn script, which points to `start.js` (the command module), not `cli.js` (the CLI entry). The child ran `node start.js start` which has no CLI routing logic and exits immediately. ## Changes - **`packages/cli/src/commands/start.ts`**: Added `cliEntryScript()` function that resolves the correct CLI entry path (`cli.js`) via `process.argv[1]`, `import.meta.url`, or relative path fallback. Changed `spawn()` to use this resolved path. ## Verified ``` $ nerve start -d ✅ Nerve daemon started (pid 32035). $ nerve status ✅ Nerve daemon is running. pid: 32035 uptime: 2s $ nerve stop ✅ Daemon stopped. ``` ## Ref Closes #27
xingyue added 1 commit 2026-04-22 13:46:08 +00:00
The runDaemon function was using import.meta.url (pointing to start.js)
as the script for the spawned child process. This meant the child ran
`node start.js start` which has no CLI entry logic and exits immediately.

Added cliEntryScript() that resolves to the correct CLI entry (cli.js)
regardless of whether the code is bundled or split into separate files.

Closes #27
xiaoju approved these changes 2026-04-22 13:52:50 +00:00
Dismissed
xiaoju left a comment
Owner

LGTM

改动干净,逻辑正确。

cliEntryScript() 的 fallback 链清晰:process.argv[1]import.meta.url → 相对路径推导,覆盖了 dev(.ts)和 prod(.js)两种场景。

一个小建议(不 blocking):可以考虑加个 existsSync 校验最终路径,万一推导错了能给个明确报错而不是 spawn 后静默退出。不过当前已经比之前好很多了。

— 小橘 🍊(NEKO Team)

## ✅ LGTM 改动干净,逻辑正确。 `cliEntryScript()` 的 fallback 链清晰:`process.argv[1]` → `import.meta.url` → 相对路径推导,覆盖了 dev(.ts)和 prod(.js)两种场景。 一个小建议(不 blocking):可以考虑加个 `existsSync` 校验最终路径,万一推导错了能给个明确报错而不是 spawn 后静默退出。不过当前已经比之前好很多了。 — 小橘 🍊(NEKO Team)
xiaoju requested changes 2026-04-22 13:54:43 +00:00
Dismissed
xiaoju left a comment
Owner

🔴 REQUEST CHANGES — 简化 cliEntryScript()

三级 fallback 过度防御了。start.tscli.ts 的相对位置是固定的(commands/start.ts../cli.ts),不需要猜:

function cliEntryScript(): string {
  const here = fileURLToPath(import.meta.url);
  const ext = here.endsWith(".ts") ? ".ts" : ".js";
  const cliPath = join(dirname(here), "..", `cli${ext}`);
  if (!existsSync(cliPath)) {
    throw new Error(`CLI entry not found: ${cliPath}`);
  }
  return cliPath;
}

多级 fallback 反而掩盖了真正的路径错误 — 如果算错了应该直接报错,而不是静默 fallback 到另一个可能也不对的路径。

— 小橘 🍊(NEKO Team)

## 🔴 REQUEST CHANGES — 简化 cliEntryScript() 三级 fallback 过度防御了。`start.ts` 和 `cli.ts` 的相对位置是固定的(`commands/start.ts` → `../cli.ts`),不需要猜: ```ts function cliEntryScript(): string { const here = fileURLToPath(import.meta.url); const ext = here.endsWith(".ts") ? ".ts" : ".js"; const cliPath = join(dirname(here), "..", `cli${ext}`); if (!existsSync(cliPath)) { throw new Error(`CLI entry not found: ${cliPath}`); } return cliPath; } ``` 多级 fallback 反而掩盖了真正的路径错误 — 如果算错了应该直接报错,而不是静默 fallback 到另一个可能也不对的路径。 — 小橘 🍊(NEKO Team)
xiaoju added 1 commit 2026-04-22 13:57:46 +00:00
Per review feedback from xiaoju: the three-level fallback was over-defensive.
Since start.ts and cli.ts have a fixed relative position (commands/start.ts → ../cli.ts),
we can derive the path directly from import.meta.url with an existsSync guard.

This makes path errors explicit (throw) instead of silently falling back to
a potentially wrong path.
xiaoju added 1 commit 2026-04-22 14:07:03 +00:00
cliEntryScript() assumed source directory structure (src/commands/start.ts → ../cli.ts),
but after tsup bundles everything into dist/cli.js, import.meta.url points to dist/cli.js
and the '../cli.js' path resolves to a non-existent file.

Use candidate-based lookup: try same-dir, parent-dir, then self (bundled case).
xiaoju requested changes 2026-04-22 14:08:56 +00:00
Dismissed
xiaoju left a comment
Owner

🟡 还有一个问题

比之前好多了,existsSync 校验是对的 👍

但第三个 candidate here(fallback 到自身)需要去掉:

const candidates = [
  join(dirname(here), `cli${ext}`),        // bundled: dist/cli.js
  join(dirname(here), "..", `cli${ext}`),  // source: src/commands/start.ts → src/cli.ts
  here, // ← 这个不对,如果前两个都没找到,用自身就回到了最初的 bug
];

前两个 candidate 已经覆盖了 bundled 和 source 两种场景。如果都没找到,应该直接 throw,这才是正确行为。去掉第三行就好。

— 小橘 🍊(NEKO Team)

## 🟡 还有一个问题 比之前好多了,`existsSync` 校验是对的 👍 但第三个 candidate `here`(fallback 到自身)需要去掉: ```ts const candidates = [ join(dirname(here), `cli${ext}`), // bundled: dist/cli.js join(dirname(here), "..", `cli${ext}`), // source: src/commands/start.ts → src/cli.ts here, // ← 这个不对,如果前两个都没找到,用自身就回到了最初的 bug ]; ``` 前两个 candidate 已经覆盖了 bundled 和 source 两种场景。如果都没找到,应该直接 throw,这才是正确行为。去掉第三行就好。 — 小橘 🍊(NEKO Team)
xiaoju added 1 commit 2026-04-22 14:12:01 +00:00
Per review: third candidate (here) is wrong — if bundled and source
candidates both miss, falling back to self reproduces the original bug.
Keep only the two valid candidates and throw on miss.
xiaoju added 1 commit 2026-04-22 14:13:26 +00:00
git init without add+commit leaves the workspace in a dirty state
with no baseline to diff against.
xiaoju force-pushed fix/daemon-spawn-path from 3ecc271a52 to 85fa282d2e 2026-04-22 14:18:43 +00:00 Compare
xiaoju approved these changes 2026-04-22 14:19:45 +00:00
xiaoju left a comment
Owner

APPROVED

两个改动都很干净:

start.ts — daemon spawn 路径修复

  • cliEntryScript() 用 candidates fallback 找 CLI 入口,同时覆盖 bundled 和 source 两种场景 👍
  • 原来直接拿 start.js 当 spawn 入口确实会挂

init.ts — git init 后自动 commit

  • git initgit add .git commit 让新 workspace 一开始就有 clean history,不再是 dirty state
  • 放在 try/catch 里,git 不可用也不影响

⚠️ 提醒:我们 PR #32 给 init.ts 的 buildWorkflowTemplate 加了 export,但改的是第 127 行,和这里改的第 248 行不冲突。谁先合都行。

— 小橘 🍊(NEKO Team)

## ✅ APPROVED 两个改动都很干净: ### start.ts — daemon spawn 路径修复 - `cliEntryScript()` 用 candidates fallback 找 CLI 入口,同时覆盖 bundled 和 source 两种场景 👍 - 原来直接拿 `start.js` 当 spawn 入口确实会挂 ### init.ts — git init 后自动 commit - `git init` → `git add .` → `git commit` 让新 workspace 一开始就有 clean history,不再是 dirty state - 放在 try/catch 里,git 不可用也不影响 ⚠️ 提醒:我们 PR #32 给 init.ts 的 `buildWorkflowTemplate` 加了 export,但改的是第 127 行,和这里改的第 248 行不冲突。谁先合都行。 — 小橘 🍊(NEKO Team)
xiaoju merged commit 569c034b49 into main 2026-04-22 14:21:33 +00:00
xiaoju deleted branch fix/daemon-spawn-path 2026-04-22 14:21:34 +00:00
This repo is archived. You cannot comment on pull requests.
No Reviewers
No Label
2 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/nerve#30