@jsonstudio/rcc 0.89.2195 → 0.89.2239

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 (55) hide show
  1. package/dist/build-info.js +2 -2
  2. package/dist/cli/commands/init.js +15 -1
  3. package/dist/cli/commands/init.js.map +1 -1
  4. package/dist/cli/commands/start-utils.js +1 -1
  5. package/dist/cli/commands/start.js +76 -6
  6. package/dist/cli/commands/start.js.map +1 -1
  7. package/dist/cli/commands/stop.js +4 -1
  8. package/dist/cli/commands/stop.js.map +1 -1
  9. package/dist/cli/config/init-config.js +15 -1
  10. package/dist/cli/config/init-config.js.map +1 -1
  11. package/dist/cli/config/init-provider-catalog.js +5 -2
  12. package/dist/cli/config/init-provider-catalog.js.map +1 -1
  13. package/dist/cli/server/port-utils.js +63 -51
  14. package/dist/cli/server/port-utils.js.map +1 -1
  15. package/dist/commands/provider-update.js +12 -0
  16. package/dist/commands/provider-update.js.map +1 -1
  17. package/dist/providers/auth/qwen-userinfo-helper.js +18 -3
  18. package/dist/providers/auth/qwen-userinfo-helper.js.map +1 -1
  19. package/dist/providers/auth/tokenfile-auth.d.ts +1 -0
  20. package/dist/providers/auth/tokenfile-auth.js +15 -9
  21. package/dist/providers/auth/tokenfile-auth.js.map +1 -1
  22. package/dist/providers/core/config/service-profiles.js +13 -4
  23. package/dist/providers/core/config/service-profiles.js.map +1 -1
  24. package/dist/providers/core/runtime/http-transport-provider.d.ts +1 -0
  25. package/dist/providers/core/runtime/http-transport-provider.js +60 -1
  26. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  27. package/dist/providers/profile/families/qwen-profile.js +203 -0
  28. package/dist/providers/profile/families/qwen-profile.js.map +1 -1
  29. package/dist/server/handlers/handler-utils.js +3 -5
  30. package/dist/server/handlers/handler-utils.js.map +1 -1
  31. package/dist/server/runtime/http-server/clock-client-reaper.d.ts +1 -0
  32. package/dist/server/runtime/http-server/clock-client-reaper.js +30 -1
  33. package/dist/server/runtime/http-server/clock-client-reaper.js.map +1 -1
  34. package/dist/server/runtime/http-server/executor/provider-response-utils.js +5 -0
  35. package/dist/server/runtime/http-server/executor/provider-response-utils.js.map +1 -1
  36. package/dist/server/runtime/http-server/executor/request-executor-core-utils.d.ts +1 -0
  37. package/dist/server/runtime/http-server/executor/request-executor-core-utils.js +48 -0
  38. package/dist/server/runtime/http-server/executor/request-executor-core-utils.js.map +1 -1
  39. package/dist/server/runtime/http-server/executor/request-retry-helpers.js +9 -1
  40. package/dist/server/runtime/http-server/executor/request-retry-helpers.js.map +1 -1
  41. package/dist/server/runtime/http-server/executor-response.js +9 -1
  42. package/dist/server/runtime/http-server/executor-response.js.map +1 -1
  43. package/dist/server/runtime/http-server/http-server-bootstrap.js +5 -0
  44. package/dist/server/runtime/http-server/http-server-bootstrap.js.map +1 -1
  45. package/dist/server/runtime/http-server/http-server-clock-daemon.js +14 -1
  46. package/dist/server/runtime/http-server/http-server-clock-daemon.js.map +1 -1
  47. package/dist/server/runtime/http-server/request-executor.js +21 -3
  48. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  49. package/dist/utils/managed-server-pids.js +44 -6
  50. package/dist/utils/managed-server-pids.js.map +1 -1
  51. package/package.json +4 -3
  52. package/scripts/cleanup-stale-server-pids.mjs +142 -0
  53. package/scripts/install-global.sh +2 -0
  54. package/scripts/verify-e2e-toolcall.mjs +12 -1
  55. package/scripts/verify-install-e2e.mjs +12 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsonstudio/rcc",
3
- "version": "0.89.2195",
3
+ "version": "0.89.2239",
4
4
  "description": "Multi-provider OpenAI proxy server with anthropic/responses/chat support (release)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "scripts": {
32
32
  "build": "npm run llmswitch:ensure && node scripts/build-core.mjs && node scripts/vendor-core.mjs && npm run clean && node scripts/gen-build-info.mjs && tsc && node scripts/copy-compat-assets.mjs && node scripts/copy-modules-config.mjs && npm run fix:cli-permission",
33
- "build:dev": "bash -lc 'export BUILD_MODE=dev; npm run build && npm run fix:cli-permission && npm run test:unified-hub-shadow && npm run verify:e2e-toolcall && npm run verify:apply-patch && npm run verify:apply-patch-regressions && npm run verify:exec-command && npm run test:routing-instructions && npm run test:cli && npm run install:global && npm run mock:regressions && npm run test:blackbox-antigravity-parity && npm run verify:errorsamples && npm run verify:e2e-gemini-followup-sample'",
33
+ "build:dev": "bash -lc 'export BUILD_MODE=dev; npm run cleanup:stale-server-pids || true; trap \"npm run cleanup:stale-server-pids >/dev/null 2>&1 || true\" EXIT; npm run build && npm run fix:cli-permission && npm run test:unified-hub-shadow && npm run verify:e2e-toolcall && npm run verify:apply-patch && npm run verify:apply-patch-regressions && npm run verify:exec-command && npm run test:routing-instructions && npm run test:cli && npm run install:global && npm run mock:regressions && npm run test:blackbox-antigravity-parity && npm run verify:errorsamples && npm run verify:e2e-gemini-followup-sample'",
34
34
  "build:min": "npm run llmswitch:ensure && node scripts/build-core.mjs && node scripts/vendor-core.mjs && npm run clean && node scripts/gen-build-info.mjs && tsc && node scripts/copy-compat-assets.mjs && node scripts/copy-modules-config.mjs && npm run fix:cli-permission",
35
35
  "prepack": "echo skip-prepack",
36
36
  "postbuild": "node scripts/ensure-cli-executable.mjs",
@@ -63,6 +63,7 @@
63
63
  "lint:fix": "ESLINT_USE_FLAT_CONFIG=1 eslint \"src/**/*.ts\" --fix --no-cache",
64
64
  "lint:strict": "ESLINT_USE_FLAT_CONFIG=1 eslint \"src/**/*.ts\" --max-warnings 0 --no-cache",
65
65
  "clean": "rm -rf dist coverage",
66
+ "cleanup:stale-server-pids": "node scripts/cleanup-stale-server-pids.mjs --quiet",
66
67
  "prebuild": "npm run verify:repo-sanity && echo skip-lint",
67
68
  "prepare": "",
68
69
  "postinstall": "node scripts/ensure-cli-executable.mjs",
@@ -150,7 +151,7 @@
150
151
  },
151
152
  "dependencies": {
152
153
  "@anthropic-ai/sdk": "^0.65.0",
153
- "@jsonstudio/llms": "0.6.2125",
154
+ "@jsonstudio/llms": "^0.6.2172",
154
155
  "@lmstudio/sdk": "^1.5.0",
155
156
  "@radix-ui/react-switch": "^1.2.6",
156
157
  "@types/socket.io": "^3.0.1",
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import { spawnSync } from 'node:child_process';
6
+
7
+ const quiet = process.argv.includes('--quiet');
8
+ const routeCodexHome = path.join(os.homedir(), '.routecodex');
9
+
10
+ function log(message) {
11
+ if (!quiet) {
12
+ console.log(`[cleanup:server-pids] ${message}`);
13
+ }
14
+ }
15
+
16
+ function isPidAlive(pid) {
17
+ if (!Number.isFinite(pid) || pid <= 0) {
18
+ return false;
19
+ }
20
+ try {
21
+ process.kill(pid, 0);
22
+ return true;
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+
28
+ function readProcessCommand(pid) {
29
+ if (process.platform === 'win32') {
30
+ return '';
31
+ }
32
+ try {
33
+ const result = spawnSync('ps', ['-p', String(pid), '-o', 'command='], { encoding: 'utf8' });
34
+ if (result.error || Number(result.status ?? 0) !== 0) {
35
+ return '';
36
+ }
37
+ return String(result.stdout || '').trim().toLowerCase();
38
+ } catch {
39
+ return '';
40
+ }
41
+ }
42
+
43
+ function isTrustedRouteCodexCommand(command) {
44
+ const normalized = String(command || '').trim().toLowerCase();
45
+ if (!normalized) {
46
+ return false;
47
+ }
48
+ if (normalized.includes('routecodex/dist/index.js')) {
49
+ return true;
50
+ }
51
+ if (normalized.includes('routecodex/dist/cli.js')) {
52
+ return true;
53
+ }
54
+ if (normalized.includes('@jsonstudio/rcc') && normalized.includes('/dist/index.js')) {
55
+ return true;
56
+ }
57
+ if (normalized.includes('jsonstudio-rcc') && normalized.includes('/dist/index.js')) {
58
+ return true;
59
+ }
60
+ return false;
61
+ }
62
+
63
+ function isPidListeningOnPort(pid, port) {
64
+ if (process.platform === 'win32') {
65
+ return true;
66
+ }
67
+ try {
68
+ const result = spawnSync('lsof', ['-nP', `-iTCP:${port}`, '-sTCP:LISTEN', '-t'], { encoding: 'utf8' });
69
+ if (result.error) {
70
+ return true;
71
+ }
72
+ if (Number(result.status ?? 0) !== 0) {
73
+ return false;
74
+ }
75
+ const pids = String(result.stdout || '')
76
+ .split(/\r?\n/)
77
+ .map((entry) => Number.parseInt(entry.trim(), 10))
78
+ .filter((entry) => Number.isFinite(entry) && entry > 0);
79
+ return pids.includes(pid);
80
+ } catch {
81
+ return true;
82
+ }
83
+ }
84
+
85
+ function cleanupPidFile(filePath) {
86
+ const fileName = path.basename(filePath);
87
+ const match = fileName.match(/^server-(\d+)\.pid$/i);
88
+ const filePort = match ? Number.parseInt(match[1], 10) : null;
89
+ let raw = '';
90
+ try {
91
+ raw = fs.readFileSync(filePath, 'utf8');
92
+ } catch {
93
+ return { removed: false, reason: 'unreadable' };
94
+ }
95
+ const pid = Number.parseInt(String(raw || '').trim(), 10);
96
+ if (!Number.isFinite(pid) || pid <= 0) {
97
+ fs.rmSync(filePath, { force: true });
98
+ return { removed: true, reason: 'invalid_pid' };
99
+ }
100
+ if (!isPidAlive(pid)) {
101
+ fs.rmSync(filePath, { force: true });
102
+ return { removed: true, reason: 'pid_not_alive' };
103
+ }
104
+
105
+ const command = readProcessCommand(pid);
106
+ if (command && !isTrustedRouteCodexCommand(command)) {
107
+ fs.rmSync(filePath, { force: true });
108
+ return { removed: true, reason: 'pid_not_routecodex' };
109
+ }
110
+ if (Number.isFinite(filePort) && filePort > 0 && !isPidListeningOnPort(pid, filePort)) {
111
+ fs.rmSync(filePath, { force: true });
112
+ return { removed: true, reason: 'pid_not_listening_on_port' };
113
+ }
114
+
115
+ return { removed: false, reason: `kept:${fileName}:${pid}` };
116
+ }
117
+
118
+ function main() {
119
+ if (!fs.existsSync(routeCodexHome)) {
120
+ return;
121
+ }
122
+ const entries = fs.readdirSync(routeCodexHome);
123
+ const candidates = entries
124
+ .filter((name) => /^server-\d+\.pid$/i.test(name) || name === 'server.cli.pid')
125
+ .map((name) => path.join(routeCodexHome, name));
126
+
127
+ let removed = 0;
128
+ let kept = 0;
129
+ for (const filePath of candidates) {
130
+ const result = cleanupPidFile(filePath);
131
+ if (result.removed) {
132
+ removed += 1;
133
+ log(`removed ${path.basename(filePath)} (${result.reason})`);
134
+ } else {
135
+ kept += 1;
136
+ log(`kept ${path.basename(filePath)} (${result.reason})`);
137
+ }
138
+ }
139
+ log(`done removed=${removed} kept=${kept}`);
140
+ }
141
+
142
+ main();
@@ -244,11 +244,13 @@ cleanup_old_install() {
244
244
  main() {
245
245
  check_node
246
246
  cleanup_old_install
247
+ node scripts/cleanup-stale-server-pids.mjs --quiet || true
247
248
  build_project
248
249
  global_install
249
250
  link_global_llms_dev
250
251
  verify_install
251
252
  verify_server_health
253
+ node scripts/cleanup-stale-server-pids.mjs --quiet || true
252
254
 
253
255
  echo ""
254
256
  echo "🎉 ć…šć±€ćź‰èŁ…ćźŒæˆ!"
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { spawn } from 'node:child_process';
2
+ import { spawn, spawnSync } from 'node:child_process';
3
3
  import process from 'node:process';
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
@@ -33,6 +33,16 @@ const AGENTS_INSTRUCTIONS = (() => {
33
33
  }
34
34
  })();
35
35
 
36
+ function cleanupStaleServerPidFiles() {
37
+ try {
38
+ spawnSync('node', [path.join(__dirname, 'cleanup-stale-server-pids.mjs'), '--quiet'], {
39
+ stdio: 'ignore'
40
+ });
41
+ } catch {
42
+ // ignore cleanup failures
43
+ }
44
+ }
45
+
36
46
  function readServerApiKeyFromConfig(configPath) {
37
47
  try {
38
48
  const raw = fs.readFileSync(configPath, 'utf8');
@@ -102,6 +112,7 @@ async function main() {
102
112
  await runGeminiCliStartupCheck();
103
113
  } finally {
104
114
  shutdown();
115
+ cleanupStaleServerPidFiles();
105
116
  if (resolved.tempDir) {
106
117
  try {
107
118
  fs.rmSync(resolved.tempDir, { recursive: true, force: true });
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { spawn } from 'node:child_process';
2
+ import { spawn, spawnSync } from 'node:child_process';
3
3
  import { once } from 'node:events';
4
4
  import { setTimeout as sleep } from 'node:timers/promises';
5
5
  import http from 'node:http';
@@ -22,6 +22,16 @@ function resolveRoutecodexBinary() {
22
22
  return 'routecodex';
23
23
  }
24
24
 
25
+ function cleanupStaleServerPidFiles() {
26
+ try {
27
+ spawnSync('node', [path.join(process.cwd(), 'scripts', 'cleanup-stale-server-pids.mjs'), '--quiet'], {
28
+ stdio: 'ignore'
29
+ });
30
+ } catch {
31
+ // ignore cleanup failures
32
+ }
33
+ }
34
+
25
35
  async function waitForHealth(timeoutMs = 60000) {
26
36
  const start = Date.now();
27
37
  while (Date.now() - start < timeoutMs) {
@@ -165,6 +175,7 @@ async function main() {
165
175
  if (mockServer) {
166
176
  await mockServer.close();
167
177
  }
178
+ cleanupStaleServerPidFiles();
168
179
  }
169
180
  }
170
181