@jsonstudio/rcc 0.89.683 → 0.89.873

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.
Files changed (191) hide show
  1. package/dist/build-info.js +2 -2
  2. package/dist/cli.js +164 -116
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client/anthropic/anthropic-protocol-client.js +42 -1
  5. package/dist/client/anthropic/anthropic-protocol-client.js.map +1 -1
  6. package/dist/client/gemini-cli/gemini-cli-protocol-client.js +4 -1
  7. package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
  8. package/dist/commands/camoufox-backfill.d.ts +2 -0
  9. package/dist/commands/camoufox-backfill.js +33 -0
  10. package/dist/commands/camoufox-backfill.js.map +1 -0
  11. package/dist/commands/camoufox-fp.d.ts +2 -0
  12. package/dist/commands/camoufox-fp.js +86 -0
  13. package/dist/commands/camoufox-fp.js.map +1 -0
  14. package/dist/commands/oauth.d.ts +2 -0
  15. package/dist/commands/oauth.js +170 -0
  16. package/dist/commands/oauth.js.map +1 -0
  17. package/dist/commands/provider-update.js +439 -2
  18. package/dist/commands/provider-update.js.map +1 -1
  19. package/dist/commands/quota-status.d.ts +2 -0
  20. package/dist/commands/quota-status.js +80 -0
  21. package/dist/commands/quota-status.js.map +1 -0
  22. package/dist/commands/token-daemon.js +12 -1
  23. package/dist/commands/token-daemon.js.map +1 -1
  24. package/dist/config/provider-v2-loader.d.ts +16 -0
  25. package/dist/config/provider-v2-loader.js +84 -0
  26. package/dist/config/provider-v2-loader.js.map +1 -0
  27. package/dist/config/routecodex-config-loader.js +27 -4
  28. package/dist/config/routecodex-config-loader.js.map +1 -1
  29. package/dist/config/system-prompts/codex-cli.txt +1 -0
  30. package/dist/config/virtual-router-builder.d.ts +9 -0
  31. package/dist/config/virtual-router-builder.js +34 -0
  32. package/dist/config/virtual-router-builder.js.map +1 -0
  33. package/dist/config/virtual-router-types.d.ts +25 -0
  34. package/dist/config/virtual-router-types.js +30 -0
  35. package/dist/config/virtual-router-types.js.map +1 -0
  36. package/dist/manager/index.d.ts +10 -0
  37. package/dist/manager/index.js +27 -0
  38. package/dist/manager/index.js.map +1 -0
  39. package/dist/manager/modules/health/index.d.ts +22 -0
  40. package/dist/manager/modules/health/index.js +82 -0
  41. package/dist/manager/modules/health/index.js.map +1 -0
  42. package/dist/manager/modules/quota/index.d.ts +57 -0
  43. package/dist/manager/modules/quota/index.js +426 -0
  44. package/dist/manager/modules/quota/index.js.map +1 -0
  45. package/dist/manager/modules/routing/index.d.ts +17 -0
  46. package/dist/manager/modules/routing/index.js +61 -0
  47. package/dist/manager/modules/routing/index.js.map +1 -0
  48. package/dist/manager/modules/token/index.d.ts +10 -0
  49. package/dist/manager/modules/token/index.js +58 -0
  50. package/dist/manager/modules/token/index.js.map +1 -0
  51. package/dist/manager/storage/base-store.d.ts +6 -0
  52. package/dist/manager/storage/base-store.js +2 -0
  53. package/dist/manager/storage/base-store.js.map +1 -0
  54. package/dist/manager/storage/file-store.d.ts +25 -0
  55. package/dist/manager/storage/file-store.js +117 -0
  56. package/dist/manager/storage/file-store.js.map +1 -0
  57. package/dist/manager/types.d.ts +9 -0
  58. package/dist/manager/types.js +2 -0
  59. package/dist/manager/types.js.map +1 -0
  60. package/dist/message-center/index.d.ts +5 -0
  61. package/dist/message-center/index.js +6 -0
  62. package/dist/message-center/index.js.map +1 -0
  63. package/dist/message-center/message-center.d.ts +93 -0
  64. package/dist/message-center/message-center.js +189 -0
  65. package/dist/message-center/message-center.js.map +1 -0
  66. package/dist/providers/auth/antigravity-userinfo-helper.d.ts +2 -0
  67. package/dist/providers/auth/antigravity-userinfo-helper.js +102 -0
  68. package/dist/providers/auth/antigravity-userinfo-helper.js.map +1 -1
  69. package/dist/providers/auth/iflow-cookie-auth.d.ts +27 -0
  70. package/dist/providers/auth/iflow-cookie-auth.js +209 -0
  71. package/dist/providers/auth/iflow-cookie-auth.js.map +1 -0
  72. package/dist/providers/auth/oauth-lifecycle.js +29 -22
  73. package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
  74. package/dist/providers/auth/token-scanner/index.js +16 -1
  75. package/dist/providers/auth/token-scanner/index.js.map +1 -1
  76. package/dist/providers/core/config/camoufox-launcher.d.ts +16 -0
  77. package/dist/providers/core/config/camoufox-launcher.js +314 -0
  78. package/dist/providers/core/config/camoufox-launcher.js.map +1 -0
  79. package/dist/providers/core/config/oauth-flows.d.ts +9 -0
  80. package/dist/providers/core/config/oauth-flows.js +50 -19
  81. package/dist/providers/core/config/oauth-flows.js.map +1 -1
  82. package/dist/providers/core/config/provider-oauth-configs.d.ts +6 -0
  83. package/dist/providers/core/config/provider-oauth-configs.js +12 -0
  84. package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
  85. package/dist/providers/core/config/service-profiles.js +26 -3
  86. package/dist/providers/core/config/service-profiles.js.map +1 -1
  87. package/dist/providers/core/runtime/antigravity-quota-client.d.ts +10 -0
  88. package/dist/providers/core/runtime/antigravity-quota-client.js +88 -0
  89. package/dist/providers/core/runtime/antigravity-quota-client.js.map +1 -0
  90. package/dist/providers/core/runtime/base-provider.d.ts +2 -1
  91. package/dist/providers/core/runtime/base-provider.js +93 -34
  92. package/dist/providers/core/runtime/base-provider.js.map +1 -1
  93. package/dist/providers/core/runtime/gemini-cli-http-provider.js +42 -10
  94. package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
  95. package/dist/providers/core/runtime/http-request-executor.js +24 -0
  96. package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
  97. package/dist/providers/core/runtime/http-transport-provider.d.ts +0 -3
  98. package/dist/providers/core/runtime/http-transport-provider.js +32 -136
  99. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  100. package/dist/providers/core/runtime/provider-error-classifier.js +18 -10
  101. package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
  102. package/dist/providers/core/runtime/rate-limit-manager.d.ts +6 -0
  103. package/dist/providers/core/runtime/rate-limit-manager.js +23 -0
  104. package/dist/providers/core/runtime/rate-limit-manager.js.map +1 -1
  105. package/dist/providers/core/strategies/oauth-auth-code-flow.d.ts +1 -0
  106. package/dist/providers/core/strategies/oauth-auth-code-flow.js +3 -2
  107. package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
  108. package/dist/providers/core/strategies/oauth-device-flow.d.ts +1 -0
  109. package/dist/providers/core/strategies/oauth-device-flow.js +3 -2
  110. package/dist/providers/core/strategies/oauth-device-flow.js.map +1 -1
  111. package/dist/providers/core/strategies/oauth-hybrid-flow.d.ts +1 -0
  112. package/dist/providers/core/strategies/oauth-hybrid-flow.js +3 -2
  113. package/dist/providers/core/strategies/oauth-hybrid-flow.js.map +1 -1
  114. package/dist/providers/core/utils/http-client.js +43 -1
  115. package/dist/providers/core/utils/http-client.js.map +1 -1
  116. package/dist/providers/mock/mock-provider-runtime.js +4 -4
  117. package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
  118. package/dist/providers/profile/provider-profile-loader.js +13 -1
  119. package/dist/providers/profile/provider-profile-loader.js.map +1 -1
  120. package/dist/providers/profile/provider-profile.d.ts +5 -0
  121. package/dist/scripts/camoufox/gen-fingerprint-env.py +171 -0
  122. package/dist/scripts/camoufox/launch-auth.mjs +617 -0
  123. package/dist/server/runtime/http-server/executor-provider.d.ts +1 -0
  124. package/dist/server/runtime/http-server/executor-provider.js +26 -0
  125. package/dist/server/runtime/http-server/executor-provider.js.map +1 -1
  126. package/dist/server/runtime/http-server/executor-response.d.ts +16 -0
  127. package/dist/server/runtime/http-server/executor-response.js +164 -0
  128. package/dist/server/runtime/http-server/executor-response.js.map +1 -0
  129. package/dist/server/runtime/http-server/index.d.ts +1 -0
  130. package/dist/server/runtime/http-server/index.js +88 -53
  131. package/dist/server/runtime/http-server/index.js.map +1 -1
  132. package/dist/server/runtime/http-server/request-executor.js +5 -19
  133. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  134. package/dist/server/runtime/http-server/routes.d.ts +2 -0
  135. package/dist/server/runtime/http-server/routes.js +33 -1
  136. package/dist/server/runtime/http-server/routes.js.map +1 -1
  137. package/dist/server/runtime/http-server/types.d.ts +1 -0
  138. package/dist/server/utils/client-connection-state.d.ts +8 -0
  139. package/dist/server/utils/client-connection-state.js +52 -0
  140. package/dist/server/utils/client-connection-state.js.map +1 -0
  141. package/dist/server/utils/request-id-manager.js +21 -3
  142. package/dist/server/utils/request-id-manager.js.map +1 -1
  143. package/dist/token-daemon/history-store.d.ts +2 -0
  144. package/dist/token-daemon/history-store.js +6 -2
  145. package/dist/token-daemon/history-store.js.map +1 -1
  146. package/dist/token-daemon/index.js +36 -5
  147. package/dist/token-daemon/index.js.map +1 -1
  148. package/dist/token-daemon/leader-lock.d.ts +11 -0
  149. package/dist/token-daemon/leader-lock.js +79 -0
  150. package/dist/token-daemon/leader-lock.js.map +1 -0
  151. package/dist/token-daemon/message-bus-integrator.d.ts +98 -0
  152. package/dist/token-daemon/message-bus-integrator.js +144 -0
  153. package/dist/token-daemon/message-bus-integrator.js.map +1 -0
  154. package/dist/token-daemon/provider-registry.d.ts +22 -0
  155. package/dist/token-daemon/provider-registry.js +201 -0
  156. package/dist/token-daemon/provider-registry.js.map +1 -0
  157. package/dist/token-daemon/token-daemon.d.ts +8 -0
  158. package/dist/token-daemon/token-daemon.js +196 -11
  159. package/dist/token-daemon/token-daemon.js.map +1 -1
  160. package/dist/token-portal/local-token-portal.d.ts +1 -0
  161. package/dist/token-portal/local-token-portal.js +18 -0
  162. package/dist/token-portal/local-token-portal.js.map +1 -1
  163. package/dist/token-portal/render.js +1 -0
  164. package/dist/token-portal/render.js.map +1 -1
  165. package/dist/tools/error-log.d.ts +31 -0
  166. package/dist/tools/error-log.js +117 -0
  167. package/dist/tools/error-log.js.map +1 -0
  168. package/dist/tools/stats-request-events.d.ts +2 -0
  169. package/dist/tools/stats-request-events.js +16 -0
  170. package/dist/tools/stats-request-events.js.map +1 -0
  171. package/dist/tools/stats-usage.d.ts +31 -0
  172. package/dist/tools/stats-usage.js +206 -0
  173. package/dist/tools/stats-usage.js.map +1 -0
  174. package/package.json +8 -4
  175. package/scripts/analyze-codex-error-failures.mjs +109 -0
  176. package/scripts/camoufox/gen-fingerprint-env.py +171 -0
  177. package/scripts/camoufox/launch-auth.mjs +617 -0
  178. package/scripts/classify-codex-samples.mjs +251 -0
  179. package/scripts/cleanup-codex-error-samples.mjs +88 -0
  180. package/scripts/compare-codex-rccx.mjs +268 -0
  181. package/scripts/copy-compat-assets.mjs +18 -0
  182. package/scripts/install-release.sh +1 -1
  183. package/scripts/local-replay-openai-response.mjs +1 -2
  184. package/scripts/pack-mode.mjs +16 -6
  185. package/scripts/replay-codex-sample.mjs +24 -2
  186. package/scripts/responses-compare-server.mjs +119 -0
  187. package/scripts/verify-apply-patch.mjs +28 -17
  188. package/scripts/verify-codex-error-samples.mjs +99 -0
  189. package/scripts/verify-e2e-toolcall.mjs +19 -4
  190. package/scripts/virtual-router-shadow-v2-real.mjs +143 -0
  191. package/scripts/virtual-router-shadow-v2.mjs +122 -0
@@ -1,6 +1,6 @@
1
1
  export const buildInfo = {
2
2
  mode: 'release',
3
- version: '0.89.683',
4
- buildTime: '2026-01-04T14:19:20.587Z'
3
+ version: '0.89.873',
4
+ buildTime: '2026-01-10T03:15:26.920Z'
5
5
  };
6
6
  //# sourceMappingURL=build-info.js.map
package/dist/cli.js CHANGED
@@ -118,84 +118,64 @@ program
118
118
  .description('RouteCodex CLI - Multi-provider OpenAI proxy server and Claude Code interface')
119
119
  .version(cliVersion);
120
120
  async function ensureTokenDaemonAutoStart() {
121
+ // Token 刷新逻辑已经在服务器进程内通过 ManagerDaemon/TokenManagerModule 执行。
122
+ // 为避免重复启动独立的 token-daemon 进程,这里不再自动拉起后台守护,仅保留显式 CLI 命令。
123
+ const disabledEnv = String(process.env.ROUTECODEX_TOKEN_DAEMON_DISABLED || process.env.RCC_TOKEN_DAEMON_DISABLED || '')
124
+ .trim()
125
+ .toLowerCase();
126
+ if (disabledEnv !== '1' && disabledEnv !== 'true' && disabledEnv !== 'yes') {
127
+ logger.info('Token manager is now integrated into the server process; automatic external token-daemon auto-start is disabled.');
128
+ }
129
+ }
130
+ async function stopTokenDaemonIfRunning() {
121
131
  try {
122
- const disabledEnv = String(process.env.ROUTECODEX_TOKEN_DAEMON_DISABLED || process.env.RCC_TOKEN_DAEMON_DISABLED || '')
123
- .trim()
124
- .toLowerCase();
125
- if (disabledEnv === '1' || disabledEnv === 'true' || disabledEnv === 'yes') {
126
- logger.info('Token daemon auto-start disabled via env (ROUTECODEX_TOKEN_DAEMON_DISABLED/RCC_TOKEN_DAEMON_DISABLED)');
132
+ if (!fs.existsSync(TOKEN_DAEMON_PID_FILE)) {
127
133
  return;
128
134
  }
129
- let existingPid = null;
135
+ const txt = fs.readFileSync(TOKEN_DAEMON_PID_FILE, 'utf8');
136
+ const parsed = Number(String(txt || '').trim());
137
+ if (!Number.isFinite(parsed) || parsed <= 0) {
138
+ return;
139
+ }
140
+ const pid = parsed;
141
+ let running = false;
130
142
  try {
131
- if (fs.existsSync(TOKEN_DAEMON_PID_FILE)) {
132
- const txt = fs.readFileSync(TOKEN_DAEMON_PID_FILE, 'utf8');
133
- const parsed = Number(String(txt || '').trim());
134
- if (Number.isFinite(parsed) && parsed > 0) {
135
- existingPid = parsed;
136
- }
137
- }
143
+ process.kill(pid, 0);
144
+ running = true;
138
145
  }
139
146
  catch {
140
- existingPid = null;
147
+ running = false;
141
148
  }
142
- const waitForProcessExit = async (pid, timeoutMs) => {
143
- const deadline = Date.now() + timeoutMs;
144
- while (Date.now() < deadline) {
145
- try {
146
- process.kill(pid, 0);
147
- }
148
- catch {
149
- return;
150
- }
151
- await new Promise((resolve) => setTimeout(resolve, 100));
152
- }
153
- };
154
- if (existingPid) {
155
- let running = false;
156
- try {
157
- process.kill(existingPid, 0);
158
- running = true;
159
- }
160
- catch {
161
- running = false;
162
- }
163
- if (running) {
164
- logger.info(`Restarting token daemon to refresh environment (pid=${existingPid})`);
165
- try {
166
- process.kill(existingPid, 'SIGTERM');
167
- }
168
- catch {
169
- // ignore
170
- }
171
- await waitForProcessExit(existingPid, 2000);
172
- }
149
+ if (!running) {
173
150
  try {
174
151
  fs.unlinkSync(TOKEN_DAEMON_PID_FILE);
175
152
  }
176
- catch {
177
- // ignore
178
- }
153
+ catch { /* ignore */ }
154
+ return;
179
155
  }
180
- const nodeBin = process.execPath;
181
- const cliEntry = path.resolve(__dirname, 'cli.js');
182
- const args = [cliEntry, 'token-daemon', 'start'];
183
- const { spawn } = await import('child_process');
184
- const child = spawn(nodeBin, args, {
185
- stdio: 'ignore',
186
- detached: true,
187
- env: { ...process.env }
188
- });
189
156
  try {
190
- child.unref();
157
+ process.kill(pid, 'SIGTERM');
191
158
  }
192
159
  catch {
193
160
  // ignore
194
161
  }
195
- logger.info('Token daemon auto-started in background');
162
+ const deadline = Date.now() + 2000;
163
+ while (Date.now() < deadline) {
164
+ try {
165
+ process.kill(pid, 0);
166
+ }
167
+ catch {
168
+ break;
169
+ }
170
+ await new Promise(resolve => setTimeout(resolve, 100));
171
+ }
172
+ try {
173
+ fs.unlinkSync(TOKEN_DAEMON_PID_FILE);
174
+ }
175
+ catch { /* ignore */ }
196
176
  }
197
- catch (error) {
198
- logger.debug(`Failed to auto-start token daemon: ${error instanceof Error ? error.message : String(error)}`);
177
+ catch {
178
+ // best-effort: failures here must not break CLI shutdown
199
179
  }
200
180
  }
201
181
  // Provider command group - update models and generate minimal provider config
@@ -204,12 +184,36 @@ try {
204
184
  program.addCommand(createProviderUpdateCommand());
205
185
  }
206
186
  catch { /* optional: command not available in some builds */ }
187
+ // Camoufox fingerprint debug command (optional)
188
+ try {
189
+ const { createCamoufoxFpCommand } = await import('./commands/camoufox-fp.js');
190
+ program.addCommand(createCamoufoxFpCommand());
191
+ }
192
+ catch { /* optional */ }
193
+ // Camoufox fingerprint backfill command (optional)
194
+ try {
195
+ const { createCamoufoxBackfillCommand } = await import('./commands/camoufox-backfill.js');
196
+ program.addCommand(createCamoufoxBackfillCommand());
197
+ }
198
+ catch { /* optional */ }
207
199
  // Token daemon command group - manage OAuth tokens
208
200
  try {
209
201
  const { createTokenDaemonCommand } = await import('./commands/token-daemon.js');
210
202
  program.addCommand(createTokenDaemonCommand());
211
203
  }
212
204
  catch { /* optional: command not available in some builds */ }
205
+ // Quota status command - inspect daemon-managed quota snapshot
206
+ try {
207
+ const { createQuotaStatusCommand } = await import('./commands/quota-status.js');
208
+ program.addCommand(createQuotaStatusCommand());
209
+ }
210
+ catch { /* optional */ }
211
+ // OAuth command - force re-auth for a specific token (Camoufox-aware when enabled)
212
+ try {
213
+ const { createOauthCommand } = await import('./commands/oauth.js');
214
+ program.addCommand(createOauthCommand());
215
+ }
216
+ catch { /* optional: command not available in some builds */ }
213
217
  // Validate command - auto start server then run E2E checks
214
218
  try {
215
219
  const { createValidateCommand } = await import('./commands/validate.js');
@@ -796,6 +800,9 @@ program
796
800
  catch { /* ignore */ }
797
801
  }
798
802
  }
803
+ if (IS_DEV_PACKAGE) {
804
+ await stopTokenDaemonIfRunning();
805
+ }
799
806
  // Ensure parent exits even if child fails to exit
800
807
  try {
801
808
  process.exit(0);
@@ -1072,37 +1079,54 @@ program
1072
1079
  .action(async () => {
1073
1080
  const spinner = await createSpinner('Stopping RouteCodex server...');
1074
1081
  try {
1075
- // Resolve config path and port
1076
- const configPath = path.join(homedir(), '.routecodex', 'config.json');
1077
- // Check if config exists
1078
- if (!fs.existsSync(configPath)) {
1079
- spinner.fail(`Configuration file not found: ${configPath}`);
1080
- logger.error('Cannot determine server port without configuration file');
1081
- logger.info('Please create a configuration file first:');
1082
- logger.info(' rcc config init');
1083
- process.exit(1);
1084
- }
1085
- // Load configuration to get port
1086
- let config;
1087
- try {
1088
- const configContent = fs.readFileSync(configPath, 'utf8');
1089
- config = JSON.parse(configContent);
1090
- }
1091
- catch (error) {
1092
- spinner.fail('Failed to parse configuration file');
1093
- logger.error(`Invalid JSON in configuration file: ${configPath}`);
1094
- process.exit(1);
1082
+ let resolvedPort;
1083
+ if (IS_DEV_PACKAGE) {
1084
+ const envPort = Number(process.env.ROUTECODEX_PORT || process.env.RCC_PORT || NaN);
1085
+ if (!Number.isNaN(envPort) && envPort > 0) {
1086
+ logger.info(`Using port ${envPort} from environment (ROUTECODEX_PORT/RCC_PORT) [dev package: routecodex]`);
1087
+ resolvedPort = envPort;
1088
+ }
1089
+ else {
1090
+ resolvedPort = DEFAULT_DEV_PORT;
1091
+ logger.info(`Using dev default port ${resolvedPort} (routecodex dev package)`);
1092
+ }
1095
1093
  }
1096
- const port = (config?.httpserver?.port ?? config?.server?.port ?? config?.port);
1097
- if (!port || typeof port !== 'number' || port <= 0) {
1098
- spinner.fail('Invalid or missing port configuration');
1099
- logger.error('Configuration file must specify a valid port number');
1100
- process.exit(1);
1094
+ else {
1095
+ // Resolve config path and port
1096
+ const configPath = path.join(homedir(), '.routecodex', 'config.json');
1097
+ // Check if config exists
1098
+ if (!fs.existsSync(configPath)) {
1099
+ spinner.fail(`Configuration file not found: ${configPath}`);
1100
+ logger.error('Cannot determine server port without configuration file');
1101
+ logger.info('Please create a configuration file first:');
1102
+ logger.info(' rcc config init');
1103
+ process.exit(1);
1104
+ }
1105
+ // Load configuration to get port
1106
+ let config;
1107
+ try {
1108
+ const configContent = fs.readFileSync(configPath, 'utf8');
1109
+ config = JSON.parse(configContent);
1110
+ }
1111
+ catch (error) {
1112
+ spinner.fail('Failed to parse configuration file');
1113
+ logger.error(`Invalid JSON in configuration file: ${configPath}`);
1114
+ process.exit(1);
1115
+ }
1116
+ const port = (config?.httpserver?.port ?? config?.server?.port ?? config?.port);
1117
+ if (!port || typeof port !== 'number' || port <= 0) {
1118
+ spinner.fail('Invalid or missing port configuration');
1119
+ logger.error('Configuration file must specify a valid port number');
1120
+ process.exit(1);
1121
+ }
1122
+ resolvedPort = port;
1101
1123
  }
1102
- const resolvedPort = port;
1103
1124
  const pids = findListeningPids(resolvedPort);
1104
1125
  if (!pids.length) {
1105
1126
  spinner.succeed(`No server listening on ${resolvedPort}.`);
1127
+ if (IS_DEV_PACKAGE) {
1128
+ await stopTokenDaemonIfRunning();
1129
+ }
1106
1130
  return;
1107
1131
  }
1108
1132
  for (const pid of pids) {
@@ -1115,6 +1139,9 @@ program
1115
1139
  while (Date.now() < deadline) {
1116
1140
  if (findListeningPids(resolvedPort).length === 0) {
1117
1141
  spinner.succeed(`Stopped server on ${resolvedPort}.`);
1142
+ if (IS_DEV_PACKAGE) {
1143
+ await stopTokenDaemonIfRunning();
1144
+ }
1118
1145
  return;
1119
1146
  }
1120
1147
  await sleep(100);
@@ -1129,6 +1156,9 @@ program
1129
1156
  }
1130
1157
  }
1131
1158
  spinner.succeed(`Force stopped server on ${resolvedPort}.`);
1159
+ if (IS_DEV_PACKAGE) {
1160
+ await stopTokenDaemonIfRunning();
1161
+ }
1132
1162
  }
1133
1163
  catch (e) {
1134
1164
  spinner.fail(`Failed to stop: ${e.message}`);
@@ -1146,34 +1176,51 @@ program
1146
1176
  .action(async (options) => {
1147
1177
  const spinner = await createSpinner('Restarting RouteCodex server...');
1148
1178
  try {
1149
- // Resolve config path
1150
- const configPath = options.config || path.join(homedir(), '.routecodex', 'config.json');
1151
- // Check if config exists
1152
- if (!fs.existsSync(configPath)) {
1153
- spinner.fail(`Configuration file not found: ${configPath}`);
1154
- logger.error('Cannot determine server port without configuration file');
1155
- logger.info('Please create a configuration file first:');
1156
- logger.info(' rcc config init');
1157
- process.exit(1);
1158
- }
1159
- // Load configuration to get port
1160
- let config;
1161
- try {
1162
- const configContent = fs.readFileSync(configPath, 'utf8');
1163
- config = JSON.parse(configContent);
1164
- }
1165
- catch (error) {
1166
- spinner.fail('Failed to parse configuration file');
1167
- logger.error(`Invalid JSON in configuration file: ${configPath}`);
1168
- process.exit(1);
1179
+ let resolvedPort;
1180
+ let resolvedHost = LOCAL_HOSTS.LOCALHOST;
1181
+ if (IS_DEV_PACKAGE) {
1182
+ const envPort = Number(process.env.ROUTECODEX_PORT || process.env.RCC_PORT || NaN);
1183
+ if (!Number.isNaN(envPort) && envPort > 0) {
1184
+ logger.info(`Using port ${envPort} from environment (ROUTECODEX_PORT/RCC_PORT) [dev package: routecodex]`);
1185
+ resolvedPort = envPort;
1186
+ }
1187
+ else {
1188
+ resolvedPort = DEFAULT_DEV_PORT;
1189
+ logger.info(`Using dev default port ${resolvedPort} (routecodex dev package)`);
1190
+ }
1169
1191
  }
1170
- const port = (config?.httpserver?.port ?? config?.server?.port ?? config?.port);
1171
- if (!port || typeof port !== 'number' || port <= 0) {
1172
- spinner.fail('Invalid or missing port configuration');
1173
- logger.error('Configuration file must specify a valid port number');
1174
- process.exit(1);
1192
+ else {
1193
+ // Resolve config path
1194
+ const configPath = options.config || path.join(homedir(), '.routecodex', 'config.json');
1195
+ // Check if config exists
1196
+ if (!fs.existsSync(configPath)) {
1197
+ spinner.fail(`Configuration file not found: ${configPath}`);
1198
+ logger.error('Cannot determine server port without configuration file');
1199
+ logger.info('Please create a configuration file first:');
1200
+ logger.info(' rcc config init');
1201
+ process.exit(1);
1202
+ }
1203
+ // Load configuration to get port
1204
+ let config;
1205
+ try {
1206
+ const configContent = fs.readFileSync(configPath, 'utf8');
1207
+ config = JSON.parse(configContent);
1208
+ }
1209
+ catch (error) {
1210
+ spinner.fail('Failed to parse configuration file');
1211
+ logger.error(`Invalid JSON in configuration file: ${configPath}`);
1212
+ process.exit(1);
1213
+ }
1214
+ const port = (config?.httpserver?.port ?? config?.server?.port ?? config?.port);
1215
+ if (!port || typeof port !== 'number' || port <= 0) {
1216
+ spinner.fail('Invalid or missing port configuration');
1217
+ logger.error('Configuration file must specify a valid port number');
1218
+ process.exit(1);
1219
+ }
1220
+ resolvedPort = port;
1221
+ resolvedHost =
1222
+ (config?.httpserver?.host || config?.server?.host || config?.host || LOCAL_HOSTS.LOCALHOST);
1175
1223
  }
1176
- const resolvedPort = port;
1177
1224
  // Stop current instance (if any)
1178
1225
  const pids = findListeningPids(resolvedPort);
1179
1226
  if (pids.length) {
@@ -1221,8 +1268,7 @@ program
1221
1268
  fs.writeFileSync(path.join(homedir(), '.routecodex', 'server.cli.pid'), String(child.pid ?? ''), 'utf8');
1222
1269
  }
1223
1270
  catch (error) { /* ignore */ }
1224
- const host = (config?.httpserver?.host || config?.server?.host || config?.host || LOCAL_HOSTS.LOCALHOST);
1225
- spinner.succeed(`RouteCodex server restarting on ${host}:${resolvedPort}`);
1271
+ spinner.succeed(`RouteCodex server restarting on ${resolvedHost}:${resolvedPort}`);
1226
1272
  logger.info(`Server will run on port: ${resolvedPort}`);
1227
1273
  logger.info('Press Ctrl+C to stop the server');
1228
1274
  const shutdown = async (sig) => {
@@ -1622,7 +1668,9 @@ async function ensurePortAvailable(port, parentSpinner, opts = {}) {
1622
1668
  }
1623
1669
  function findListeningPids(port) {
1624
1670
  try {
1625
- const result = spawnSync('lsof', ['-tiTCP', `:${port}`, '-sTCP:LISTEN'], { encoding: 'utf8' });
1671
+ // macOS/BSD lsof expects either "-i TCP:port" or "-tiTCP:port" as a single argument.
1672
+ // Use the compact form to avoid treating ":port" as a filename.
1673
+ const result = spawnSync('lsof', [`-tiTCP:${port}`, '-sTCP:LISTEN'], { encoding: 'utf8' });
1626
1674
  if (result.error) {
1627
1675
  logger.warning(`lsof not available to inspect port usage: ${result.error.message}`);
1628
1676
  return [];