Skip to content

fix: release default web run port and align codex defaults#72

Merged
ymkiux merged 3 commits intoSakuraByteCore:mainfrom
SurviveM:fix/release-default-web-port-and-align-codex-defaults
Apr 6, 2026
Merged

fix: release default web run port and align codex defaults#72
ymkiux merged 3 commits intoSakuraByteCore:mainfrom
SurviveM:fix/release-default-web-port-and-align-codex-defaults

Conversation

@SurviveM
Copy link
Copy Markdown
Collaborator

@SurviveM SurviveM commented Apr 6, 2026

Summary

  • Release the default web run port before starting the server to avoid stale listeners blocking startup
  • Add CLI coverage for fuser, lsof, and process-scan port cleanup fallbacks
  • Align Codex default reasoning effort to medium across the CLI template and web UI defaults
  • Extend unit and e2e coverage for the revised startup and config behavior

Tests

  • node tests/unit/run.mjs
  • node tests/e2e/run.js

Summary by CodeRabbit

  • New Features

    • Automatic port conflict resolution on startup to free the default web port before launching the UI.
  • Changes

    • Default model reasoning effort changed to "medium" across UI and startup behavior.
    • Reasoning-effort selection labels updated to mark "medium" as the default.
    • Legacy default template no longer contains a model reasoning effort setting.
  • Tests

    • Expanded tests for port-release behavior, reasoning-effort handling, and startup ordering.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

📝 Walkthrough

Walkthrough

Added OS-specific logic to detect and forcibly free the default web port before starting the Web UI; changed default model reasoning effort from "high" to "medium" across app state, startup normalization, UI labels, config template, and tests.

Changes

Cohort / File(s) Summary
Port release & startup
cli.js, tests/unit/web-run-host.test.mjs
Added releaseRunPortIfNeeded(port, host, deps = {}) with OS-specific flows (Windows: netstat/powershell/taskkill; non-Windows: lsof/fuser/ps/SIGKILL). cmdStart() now calls it after resolving port/host. Extensive unit tests verify branches, fallbacks, PID filtering, and call order.
Default reasoning effort changes
web-ui/app.js, web-ui/modules/app.methods.startup-claude.mjs, web-ui/partials/index/panel-config-codex.html, tests/unit/web-ui-behavior-parity.test.mjs
Default modelReasoningEffort changed from 'high' to 'medium'; startup now normalizes and restricts accepted values (`low
Config template & e2e test
cli.js, tests/e2e/test-config.js
Removed model_reasoning_effort = "high" from EMPTY_CONFIG_FALLBACK_TEMPLATE; e2e test updated to assert no model_reasoning_effort = ... line exists in restored legacy template.
Provider/status handling test
tests/unit/provider-share-command.test.mjs
Added tests ensuring loadAll maps unsupported modelReasoningEffort values to 'medium' and preserves supported 'xhigh'.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI (cmdStart)
    participant OS as OS (lsof / netstat / fuser / ps / taskkill)
    participant Server as WebServer (createWebServer)
    CLI->>CLI: resolveWebPort(port, host)
    CLI->>OS: releaseRunPortIfNeeded(port, host)
    alt default port path
        OS->>OS: enumerate listeners (lsof/fuser/netstat)
        OS->>OS: filter for managed "codexmate run"/"cli.js run" PIDs
        OS->>OS: terminate matching PIDs (SIGKILL / taskkill)
        OS-->>CLI: {attempted:true, released:true, pids:[...]}
    else non-default port or nothing to release
        OS-->>CLI: {attempted:false, released:false, pids:[], reason:'non-default-port'}
    end
    CLI->>Server: createWebServer(...)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • ymkiux
  • awsl233777

Poem

🐰 I hopped through ports to clear the way,
Kicked sleepy PIDs so servers may play,
From high to medium the settings now sing,
A calmer start, and a brighter spring. 🌷

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: releasing the default web run port and aligning codex defaults to medium reasoning effort.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/unit/web-run-host.test.mjs (1)

300-302: Reduce fragility in cmdStart ordering assertion.

This regex is tightly coupled to exact declaration forms (const/let) and nearby statements, so harmless refactors can break the test. Consider narrowing the assertion to the ordering contract only.

♻️ Suggested stabilization diff
 test('cmdStart releases the resolved port before creating the web server', () => {
-    assert.match(cliContent, /const port = resolveWebPort\(\);\s*const host = resolveWebHost\(options\);\s*releaseRunPortIfNeeded\(port\);\s*let serverHandle = createWebServer\(/s);
+    assert.match(
+        cliContent,
+        /releaseRunPortIfNeeded\(port\);\s*(?:const|let)\s+serverHandle\s*=\s*createWebServer\(/s
+    );
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/web-run-host.test.mjs` around lines 300 - 302, The test's regex is
too fragile because it depends on exact declaration keywords and surrounding
lines; update the assertion in the 'cmdStart releases the resolved port before
creating the web server' test so it only asserts ordering of resolveWebPort,
releaseRunPortIfNeeded, and createWebServer. For example, replace the strict
regex with either a relaxed regex that checks the three symbols appear in
sequence (e.g. match resolveWebPort(...) then releaseRunPortIfNeeded(...) then
createWebServer(...), allowing arbitrary text between), or compute string
indices of "resolveWebPort(", "releaseRunPortIfNeeded(", and "createWebServer("
on cliContent and assert their index order (resolve < release < create); keep
references to the functions
cmdStart/resolveWebPort/releaseRunPortIfNeeded/createWebServer to locate the
code under test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli.js`:
- Around line 280-333: The code currently calls runCommand('fuser', ['-k',
`${numericPort}/tcp`]) immediately; instead, change the non-Windows path to
first run runCommand('lsof', ['-ti', `tcp:${numericPort}`]) (use
lsofResult.stdout to populate seenPids), then verify those PIDs (skip currentPid
and check process identity where applicable via addManagedRunPidsFromPs or ps
-ef) and only then kill each PID explicitly (using process.kill or the existing
killProcess(pid, 'SIGKILL') call) and set released accordingly; update
references to fuserResult/lsofResult/seenPids/runCommand/addManagedRunPidsFromPs
so fuser -k is no longer destructive and is either removed or only used as a
last-resort after verification.

In `@web-ui/modules/app.methods.startup-claude.mjs`:
- Around line 40-43: The code sets this.modelReasoningEffort to the raw
normalized string or 'medium' only when empty, allowing invalid non-empty
values; update the assignment to validate the normalized effort against an
allowed set (e.g., ['low','medium','high']) before assigning. After computing
effort from statusRes.modelReasoningEffort (trim().toLowerCase()), check if
allowedIncludes(effort) and assign it, otherwise assign the default 'medium' to
this.modelReasoningEffort; reference the variables
statusRes.modelReasoningEffort and the property this.modelReasoningEffort in
your change.

---

Nitpick comments:
In `@tests/unit/web-run-host.test.mjs`:
- Around line 300-302: The test's regex is too fragile because it depends on
exact declaration keywords and surrounding lines; update the assertion in the
'cmdStart releases the resolved port before creating the web server' test so it
only asserts ordering of resolveWebPort, releaseRunPortIfNeeded, and
createWebServer. For example, replace the strict regex with either a relaxed
regex that checks the three symbols appear in sequence (e.g. match
resolveWebPort(...) then releaseRunPortIfNeeded(...) then createWebServer(...),
allowing arbitrary text between), or compute string indices of
"resolveWebPort(", "releaseRunPortIfNeeded(", and "createWebServer(" on
cliContent and assert their index order (resolve < release < create); keep
references to the functions
cmdStart/resolveWebPort/releaseRunPortIfNeeded/createWebServer to locate the
code under test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2683965f-57c0-4011-a296-c913baf2ca42

📥 Commits

Reviewing files that changed from the base of the PR and between c45ced3 and 04af762.

📒 Files selected for processing (7)
  • cli.js
  • tests/e2e/test-config.js
  • tests/unit/web-run-host.test.mjs
  • tests/unit/web-ui-behavior-parity.test.mjs
  • web-ui/app.js
  • web-ui/modules/app.methods.startup-claude.mjs
  • web-ui/partials/index/panel-config-codex.html
📜 Review details
🔇 Additional comments (9)
web-ui/partials/index/panel-config-codex.html (1)

81-82: Default label update is consistent with the new baseline.

The selector text now correctly marks medium as default and no longer marks high, matching the rest of this PR.

web-ui/app.js (1)

36-36: App-state default change looks correct.

Initializing modelReasoningEffort to medium is aligned with the updated UI and test expectations.

tests/e2e/test-config.js (1)

411-414: Legacy template assertion is a good regression guard.

This check correctly protects the “no explicit model_reasoning_effort in restored legacy defaults” behavior.

tests/unit/web-ui-behavior-parity.test.mjs (1)

108-108: Parity harness default update is correct.

Using medium here keeps baseline behavior aligned with current app startup defaults.

tests/unit/web-run-host.test.mjs (5)

121-125: Good extraction hook for isolated function testing.

This keeps the new releaseRunPortIfNeeded logic testable without requiring full CLI execution context.


161-181: No-op coverage for non-default ports is solid.

The assertion on both return shape and zero subprocess calls is precise and valuable.


183-207: Happy-path fuser coverage is clear and complete.

You’re validating command invocation, parsed PID result, and user-facing log output in one place.


209-247: Fallback to lsof is well validated.

The test correctly proves fallback order and kill behavior when fuser is unavailable.


249-298: ps fallback test correctly protects current process.

This is a strong guard against self-termination regressions while still validating managed-process cleanup.

@ymkiux
Copy link
Copy Markdown
Collaborator

ymkiux commented Apr 6, 2026

@coderabbitai 如果我直接合并会带来回归性异常吗

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect releaseRunPortIfNeeded and cmdStart in cli.js
rg -n "releaseRunPortIfNeeded|DEFAULT_WEB_PORT|cmdStart|EMPTY_CONFIG_FALLBACK" --type js -A 5 -B 2 | head -200

Length of output: 4240


🏁 Script executed:

#!/bin/bash
# Get the full implementation of releaseRunPortIfNeeded
sed -n '226,368p' cli.js

Length of output: 5160


🏁 Script executed:

#!/bin/bash
# Look at the actual unit test for releaseRunPortIfNeeded
cat tests/unit/web-run-host.test.mjs | grep -A 200 "releaseRunPortIfNeeded"

Length of output: 14795


@ymkiux 根据对代码的详细审查,以下是合并风险评估:


🔍 回归风险分析

✅ 低风险 / 可接受的变更

变更项 影响范围 结论
model_reasoning_effort 默认值 'high''medium' 仅影响未设置该项的新用户或空配置用户;已有配置文件的用户不受影响 行为变更但属有意为之
UI 标签调整(medium(默认) 纯展示层变更 无风险
EMPTY_CONFIG_FALLBACK_TEMPLATE 移除 model_reasoning_effort = "high" 新生成的配置不含该字段,系统将使用 'medium' 默认值 与上述变更一致,无风险

⚠️ 需注意的潜在风险

releaseRunPortIfNeeded 的 fallback 行为

该函数逻辑如下:

  1. 首先用 lsof -ti tcp:3737 获取占用端口的候选 PID
  2. ps -ef 过滤出属于 codexmate run / cli.js run 的进程
  3. lsof 不可用或端口上无 PID,回退到扫描全部 codexmate run / cli.js run 进程(不再限制端口
当 lsof 找不到任何 PID 时 → 直接扫描 ps,杀掉所有 codexmate run 进程

潜在问题:如果用户同时运行了另一个使用自定义端口(非 3737)的 codexmate run 实例,在上述 fallback 路径下,它可能被误杀。

影响条件(需同时满足):

  • lsof 未安装或端口 3737 上无进程
  • 同时有另一个 codexmate run 实例在其他端口运行

📋 整体结论

对于大多数用户,可以安全合并。 测试覆盖完整,与实际实现一致(代码使用 lsof+ps,测试也如此)。

唯一值得关注的边缘情况是多实例场景下的 fallback kill 行为,但这是极少数使用场景。如果你的用户群中存在同时运行多个 codexmate run 的场景,建议在 fallback 路径中加入端口验证再 kill,否则当前 PR 可以合并。

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
cli.js (1)

336-353: ⚠️ Potential issue | 🔴 Critical

Don't promote a failed port lookup into “kill every managed run”.

When seenPids is still empty here, the ps -ef fallback is no longer port-scoped. The later kill loop will then SIGKILL any other codexmate run / cli.js run process it finds, including instances using a different port. ps should only validate PIDs that were already tied to numericPort; if no port owner was identified, skip cleanup and let the normal bind failure surface.

🛠️ Safer fallback
         if (!(lsofResult && lsofResult.error) && candidatePids.size > 0) {
             const managedPsResult = readPsResult();
             if (!(managedPsResult && managedPsResult.error)) {
                 addManagedRunPidsFromPs(managedPsResult.stdout, candidatePids);
             }
         }
-        if (killProcess && seenPids.size === 0) {
-            const managedPsResult = readPsResult();
-            if (!(managedPsResult && managedPsResult.error)) {
-                addManagedRunPidsFromPs(managedPsResult.stdout);
-            }
-        }
+        // `ps` output alone does not prove port ownership. If no port-scoped PID
+        // was found, leave cleanup to the normal EADDRINUSE path instead of
+        // killing unrelated `codexmate run` processes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli.js` around lines 336 - 353, The current fallback calls readPsResult() and
addManagedRunPidsFromPs(managedPsResult.stdout) when seenPids is empty, which
can add PIDs not tied to numericPort and lead to killing unrelated processes;
change the logic so the ps-based parsing only accepts PIDs explicitly associated
with numericPort (either by enhancing addManagedRunPidsFromPs to filter by
numericPort or by parsing managedPsResult.stdout here and only adding
port-matching PIDs), and if no port-owner PIDs are identified leave seenPids
empty and skip the kill loop entirely (so killProcess/currentPid/released logic
never runs for non-port-scoped results).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli.js`:
- Around line 227-230: The releaseRunPortIfNeeded function currently only takes
port and may force-kill every PID listening on that port on Windows; change its
signature to accept host as well, and before invoking taskkill in the
Windows-specific branch verify the candidate PID actually corresponds to the
same listener (match the socket owner/address via existing process identity
check used elsewhere in the file) and skip killing PIDs that are bound to a
different interface or that aren’t the codexmate process; update any callers
(e.g., the places referenced around the other ranges) to pass the host through
so the function can scope cleanup correctly.

---

Duplicate comments:
In `@cli.js`:
- Around line 336-353: The current fallback calls readPsResult() and
addManagedRunPidsFromPs(managedPsResult.stdout) when seenPids is empty, which
can add PIDs not tied to numericPort and lead to killing unrelated processes;
change the logic so the ps-based parsing only accepts PIDs explicitly associated
with numericPort (either by enhancing addManagedRunPidsFromPs to filter by
numericPort or by parsing managedPsResult.stdout here and only adding
port-matching PIDs), and if no port-owner PIDs are identified leave seenPids
empty and skip the kill loop entirely (so killProcess/currentPid/released logic
never runs for non-port-scoped results).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 64a6597c-4a51-44ec-88b1-8835f136359d

📥 Commits

Reviewing files that changed from the base of the PR and between 04af762 and b99e012.

📒 Files selected for processing (4)
  • cli.js
  • tests/unit/provider-share-command.test.mjs
  • tests/unit/web-run-host.test.mjs
  • web-ui/modules/app.methods.startup-claude.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • web-ui/modules/app.methods.startup-claude.mjs
  • tests/unit/web-run-host.test.mjs
📜 Review details
🔇 Additional comments (1)
tests/unit/provider-share-command.test.mjs (1)

760-839: Good coverage for reasoning-effort normalization paths.

This validates both critical outcomes: unsupported values fallback to 'medium', while allowed 'xhigh' is preserved.

@ymkiux
Copy link
Copy Markdown
Collaborator

ymkiux commented Apr 6, 2026

@coderabbitai 如果我直接合并会带来回归性异常吗

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

Tip

For best results, initiate chat on the files or code changes.

@ymkiux 这是对上一条分析的重申,结论不变:

对绝大多数用户,可以安全合并,不会引发回归性异常。

唯一的边缘风险仍是 releaseRunPortIfNeeded 的 fallback 路径:当 lsof 不可用且端口 3737 上无进程时,会扫描全部 codexmate run 进程并 kill,可能误杀同时运行在其他端口的实例。该场景需同时满足以下条件才会触发:

  1. 系统未安装 lsof(或端口 3737 当时无进程)
  2. 用户恰好同时运行了另一个使用自定义端口的 codexmate run 实例

如果你的用户群不存在多实例并发场景,直接合并即可。如果存在,建议在 fallback kill 前加上端口二次验证,再合并。


🧠 Learnings used
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli.js`:
- Around line 412-440: The cleanup code uses lsof/fuser and
candidatePids/seenPids to kill by port only, ignoring the requested host, so
killProcess(currentPid) may terminate other instances bound to different
addresses; update the non-Windows path (the runCommand/lsof/fuser logic that
populates candidatePids, the call sites readPsResult and
addManagedRunPidsFromPs, and the loop that iterates seenPids before calling
killProcess) to verify each candidate's local listener address matches the
requested host (or skip if verification is impossible) before adding to seenPids
or issuing SIGKILL; i.e., after obtaining PIDs from lsof/fuser/readPsResult,
query the listener address for each PID (or use lsof output flags to include
address) and filter candidatePids so only processes bound to the same host:port
are left, otherwise do not perform cleanup.
- Around line 281-293: The isMatchingWindowsListenerAddress matcher currently
ignores wildcard listeners (listenerHost '0.0.0.0' or '::') when comparing
against a concrete normalizedHost, allowing a wildcard listener like
'0.0.0.0:3737' to be treated as non-conflicting with '--host localhost' or
'--host 127.0.0.1'; update isMatchingWindowsListenerAddress to treat wildcard
listener hosts as matching any concrete normalizedHost (and still handle the
special 'localhost'↔'127.0.0.1'/'::1' mapping), i.e. after extracting
listenerHost and ensuring normalizedHost exists, return true if listenerHost is
'0.0.0.0' or '::' (or if normalizedHost is those values keep existing behavior),
otherwise preserve the existing localhost and normalized comparisons using
extractListenerHost, normalizedHost and normalizeListenerHost.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2e5e5e9d-3194-44d3-850f-d973c864f4ec

📥 Commits

Reviewing files that changed from the base of the PR and between b99e012 and eb3ed90.

📒 Files selected for processing (2)
  • cli.js
  • tests/unit/web-run-host.test.mjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/web-run-host.test.mjs
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
🔇 Additional comments (1)
cli.js (1)

11080-11080: Right place to trigger the cleanup.

Doing the port-release step before createWebServer() is the correct ordering for this flow.

Comment on lines +281 to +293
const isMatchingWindowsListenerAddress = (localAddress) => {
const listenerHost = extractListenerHost(localAddress);
if (!listenerHost || !normalizedHost) {
return false;
}
if (normalizedHost === 'localhost') {
return listenerHost === '127.0.0.1' || listenerHost === '::1';
}
if (normalizedHost === '0.0.0.0' || normalizedHost === '::') {
return listenerHost === normalizedHost;
}
return listenerHost === normalizeListenerHost(normalizedHost);
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Match wildcard listeners against host-specific Windows starts.

This matcher never treats 0.0.0.0:3737 / [::]:3737 listeners as conflicts for host-specific starts. A stale wildcard codexmate run will therefore survive a later --host localhost, --host 127.0.0.1, or other concrete-address start even though it still owns the port, so the cleanup can still fall through to EADDRINUSE.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli.js` around lines 281 - 293, The isMatchingWindowsListenerAddress matcher
currently ignores wildcard listeners (listenerHost '0.0.0.0' or '::') when
comparing against a concrete normalizedHost, allowing a wildcard listener like
'0.0.0.0:3737' to be treated as non-conflicting with '--host localhost' or
'--host 127.0.0.1'; update isMatchingWindowsListenerAddress to treat wildcard
listener hosts as matching any concrete normalizedHost (and still handle the
special 'localhost'↔'127.0.0.1'/'::1' mapping), i.e. after extracting
listenerHost and ensuring normalizedHost exists, return true if listenerHost is
'0.0.0.0' or '::' (or if normalizedHost is those values keep existing behavior),
otherwise preserve the existing localhost and normalized comparisons using
extractListenerHost, normalizedHost and normalizeListenerHost.

Comment on lines +412 to +440
const lsofResult = runCommand(
'lsof',
['-ti', `tcp:${numericPort}`],
{ stdoutPidSet: candidatePids, stderrPidSet: null }
);
const shouldTryFuser = !!(lsofResult && lsofResult.error && lsofResult.error.code === 'ENOENT');
if (shouldTryFuser && candidatePids.size === 0) {
runCommand(
'fuser',
[`${numericPort}/tcp`],
{ stdoutPidSet: candidatePids, stderrPidSet: candidatePids }
);
}
if (candidatePids.size > 0) {
const managedPsResult = readPsResult();
if (!(managedPsResult && managedPsResult.error)) {
addManagedRunPidsFromPs(managedPsResult.stdout, candidatePids);
}
}
}

if (processRef.platform !== 'win32' && killProcess && !released && seenPids.size > 0) {
for (const pid of seenPids) {
if (pid === currentPid) {
continue;
}
try {
killProcess(pid, 'SIGKILL');
released = true;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Unix cleanup still kills by port instead of by requested listener.

The non-Windows path ignores host entirely: lsof/fuser collect every managed codexmate run on port 3737, then Line 433 SIGKILLs all verified matches. That can terminate another instance bound to a different specific address even when the requested --host would coexist. Filter Unix candidates by local listener address before they reach seenPids, or skip cleanup when the listener address cannot be verified.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli.js` around lines 412 - 440, The cleanup code uses lsof/fuser and
candidatePids/seenPids to kill by port only, ignoring the requested host, so
killProcess(currentPid) may terminate other instances bound to different
addresses; update the non-Windows path (the runCommand/lsof/fuser logic that
populates candidatePids, the call sites readPsResult and
addManagedRunPidsFromPs, and the loop that iterates seenPids before calling
killProcess) to verify each candidate's local listener address matches the
requested host (or skip if verification is impossible) before adding to seenPids
or issuing SIGKILL; i.e., after obtaining PIDs from lsof/fuser/readPsResult,
query the listener address for each PID (or use lsof output flags to include
address) and filter candidatePids so only processes bound to the same host:port
are left, otherwise do not perform cleanup.

@ymkiux ymkiux merged commit beea32d into SakuraByteCore:main Apr 6, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants