@jsonstudio/rcc 0.89.2239 → 0.90.1

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 (237) hide show
  1. package/README.md +27 -0
  2. package/dist/build-info.js +2 -2
  3. package/dist/build-info.js.map +1 -1
  4. package/dist/cli/commands/claude.js +1 -0
  5. package/dist/cli/commands/claude.js.map +1 -1
  6. package/dist/cli/commands/codex.js +1 -0
  7. package/dist/cli/commands/codex.js.map +1 -1
  8. package/dist/cli/commands/guardian-daemon.d.ts +2 -0
  9. package/dist/cli/commands/guardian-daemon.js +299 -0
  10. package/dist/cli/commands/guardian-daemon.js.map +1 -0
  11. package/dist/cli/commands/init/camoufox.js +1 -1
  12. package/dist/cli/commands/init/camoufox.js.map +1 -1
  13. package/dist/cli/commands/launcher/types.d.ts +6 -0
  14. package/dist/cli/commands/launcher-kernel.js +456 -109
  15. package/dist/cli/commands/launcher-kernel.js.map +1 -1
  16. package/dist/cli/commands/port.js +28 -8
  17. package/dist/cli/commands/port.js.map +1 -1
  18. package/dist/cli/commands/restart.d.ts +4 -0
  19. package/dist/cli/commands/restart.js +91 -42
  20. package/dist/cli/commands/restart.js.map +1 -1
  21. package/dist/cli/commands/start-types.d.ts +4 -0
  22. package/dist/cli/commands/start.js +108 -65
  23. package/dist/cli/commands/start.js.map +1 -1
  24. package/dist/cli/commands/stop.d.ts +3 -0
  25. package/dist/cli/commands/stop.js +30 -63
  26. package/dist/cli/commands/stop.js.map +1 -1
  27. package/dist/cli/config/init-provider-catalog.js +8 -3
  28. package/dist/cli/config/init-provider-catalog.js.map +1 -1
  29. package/dist/cli/guardian/client.d.ts +38 -0
  30. package/dist/cli/guardian/client.js +237 -0
  31. package/dist/cli/guardian/client.js.map +1 -0
  32. package/dist/cli/guardian/paths.d.ts +7 -0
  33. package/dist/cli/guardian/paths.js +13 -0
  34. package/dist/cli/guardian/paths.js.map +1 -0
  35. package/dist/cli/guardian/types.d.ts +30 -0
  36. package/dist/cli/guardian/types.js +2 -0
  37. package/dist/cli/guardian/types.js.map +1 -0
  38. package/dist/cli/register/guardian-daemon-command.d.ts +2 -0
  39. package/dist/cli/register/guardian-daemon-command.js +5 -0
  40. package/dist/cli/register/guardian-daemon-command.js.map +1 -0
  41. package/dist/cli/server/port-utils.js +57 -1
  42. package/dist/cli/server/port-utils.js.map +1 -1
  43. package/dist/cli.js +48 -0
  44. package/dist/cli.js.map +1 -1
  45. package/dist/commands/oauth.js +6 -6
  46. package/dist/commands/oauth.js.map +1 -1
  47. package/dist/config/routecodex-config-loader.js +66 -1
  48. package/dist/config/routecodex-config-loader.js.map +1 -1
  49. package/dist/config/virtual-router-builder.js +18 -0
  50. package/dist/config/virtual-router-builder.js.map +1 -1
  51. package/dist/config/virtual-router-types.js +20 -5
  52. package/dist/config/virtual-router-types.js.map +1 -1
  53. package/dist/daemon-admin-ui/assets/index-C8vP_c5E.js +15 -0
  54. package/dist/daemon-admin-ui/assets/index-DjIoHmNv.css +1 -0
  55. package/dist/daemon-admin-ui/index.html +13 -0
  56. package/dist/docs/daemon-admin-ui.html +328 -57
  57. package/dist/index.d.ts +9 -0
  58. package/dist/index.js +268 -10
  59. package/dist/index.js.map +1 -1
  60. package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.d.ts +1 -0
  61. package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.js +36 -0
  62. package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.js.map +1 -1
  63. package/dist/manager/modules/quota/provider-quota-daemon.events.js +50 -1
  64. package/dist/manager/modules/quota/provider-quota-daemon.events.js.map +1 -1
  65. package/dist/providers/auth/antigravity-user-agent.js +78 -31
  66. package/dist/providers/auth/antigravity-user-agent.js.map +1 -1
  67. package/dist/providers/auth/gemini-cli-userinfo-helper.js +94 -63
  68. package/dist/providers/auth/gemini-cli-userinfo-helper.js.map +1 -1
  69. package/dist/providers/auth/iflow-userinfo-helper.js +1 -1
  70. package/dist/providers/auth/iflow-userinfo-helper.js.map +1 -1
  71. package/dist/providers/auth/oauth-error-message.d.ts +1 -0
  72. package/dist/providers/auth/oauth-error-message.js +44 -0
  73. package/dist/providers/auth/oauth-error-message.js.map +1 -0
  74. package/dist/providers/auth/oauth-lifecycle/error-detection.js +42 -8
  75. package/dist/providers/auth/oauth-lifecycle/error-detection.js.map +1 -1
  76. package/dist/providers/auth/oauth-lifecycle/token-io.d.ts +1 -0
  77. package/dist/providers/auth/oauth-lifecycle/token-io.js +12 -0
  78. package/dist/providers/auth/oauth-lifecycle/token-io.js.map +1 -1
  79. package/dist/providers/auth/oauth-lifecycle.js +502 -87
  80. package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
  81. package/dist/providers/auth/oauth-repair-cooldown.js +2 -7
  82. package/dist/providers/auth/oauth-repair-cooldown.js.map +1 -1
  83. package/dist/providers/auth/oauth-repair-env.js +3 -5
  84. package/dist/providers/auth/oauth-repair-env.js.map +1 -1
  85. package/dist/providers/auth/oauth-utils/error-extraction.js +42 -8
  86. package/dist/providers/auth/oauth-utils/error-extraction.js.map +1 -1
  87. package/dist/providers/core/config/camoufox-actions.d.ts +31 -0
  88. package/dist/providers/core/config/camoufox-actions.js +461 -0
  89. package/dist/providers/core/config/camoufox-actions.js.map +1 -0
  90. package/dist/providers/core/config/camoufox-launcher.d.ts +3 -0
  91. package/dist/providers/core/config/camoufox-launcher.js +518 -160
  92. package/dist/providers/core/config/camoufox-launcher.js.map +1 -1
  93. package/dist/providers/core/config/oauth-flows.js +6 -44
  94. package/dist/providers/core/config/oauth-flows.js.map +1 -1
  95. package/dist/providers/core/config/provider-oauth-configs.js +51 -7
  96. package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
  97. package/dist/providers/core/runtime/provider-error-classifier.js +32 -15
  98. package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
  99. package/dist/providers/core/runtime/provider-family-profile-utils.js +1 -1
  100. package/dist/providers/core/runtime/provider-family-profile-utils.js.map +1 -1
  101. package/dist/providers/core/runtime/provider-response-postprocessor.js +61 -14
  102. package/dist/providers/core/runtime/provider-response-postprocessor.js.map +1 -1
  103. package/dist/providers/core/strategies/oauth-auth-code-flow.d.ts +1 -0
  104. package/dist/providers/core/strategies/oauth-auth-code-flow.js +124 -19
  105. package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
  106. package/dist/providers/core/strategies/oauth-device-flow.js +6 -3
  107. package/dist/providers/core/strategies/oauth-device-flow.js.map +1 -1
  108. package/dist/providers/profile/families/iflow-profile.js +83 -10
  109. package/dist/providers/profile/families/iflow-profile.js.map +1 -1
  110. package/dist/scripts/camoufox/launch-auth.mjs +112 -5
  111. package/dist/server/handlers/config-admin-handler.js +9 -2
  112. package/dist/server/handlers/config-admin-handler.js.map +1 -1
  113. package/dist/server/handlers/handler-utils.js +3 -12
  114. package/dist/server/handlers/handler-utils.js.map +1 -1
  115. package/dist/server/handlers/logging.js +3 -4
  116. package/dist/server/handlers/logging.js.map +1 -1
  117. package/dist/server/runtime/http-server/clock-client-reaper.js +3 -26
  118. package/dist/server/runtime/http-server/clock-client-reaper.js.map +1 -1
  119. package/dist/server/runtime/http-server/clock-client-registry-utils.d.ts +4 -0
  120. package/dist/server/runtime/http-server/clock-client-registry-utils.js +74 -16
  121. package/dist/server/runtime/http-server/clock-client-registry-utils.js.map +1 -1
  122. package/dist/server/runtime/http-server/clock-client-registry.d.ts +15 -0
  123. package/dist/server/runtime/http-server/clock-client-registry.js +300 -6
  124. package/dist/server/runtime/http-server/clock-client-registry.js.map +1 -1
  125. package/dist/server/runtime/http-server/clock-client-routes.js +49 -19
  126. package/dist/server/runtime/http-server/clock-client-routes.js.map +1 -1
  127. package/dist/server/runtime/http-server/clock-daemon-log-throttle.d.ts +16 -0
  128. package/dist/server/runtime/http-server/clock-daemon-log-throttle.js +49 -0
  129. package/dist/server/runtime/http-server/clock-daemon-log-throttle.js.map +1 -1
  130. package/dist/server/runtime/http-server/clock-scope-resolution.d.ts +14 -0
  131. package/dist/server/runtime/http-server/clock-scope-resolution.js +212 -0
  132. package/dist/server/runtime/http-server/clock-scope-resolution.js.map +1 -0
  133. package/dist/server/runtime/http-server/daemon-admin/auth-handler.js +5 -3
  134. package/dist/server/runtime/http-server/daemon-admin/auth-handler.js.map +1 -1
  135. package/dist/server/runtime/http-server/daemon-admin/control-handler.js +104 -15
  136. package/dist/server/runtime/http-server/daemon-admin/control-handler.js.map +1 -1
  137. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +2 -2
  138. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
  139. package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.d.ts +24 -0
  140. package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.js +316 -70
  141. package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.js.map +1 -1
  142. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +190 -1
  143. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
  144. package/dist/server/runtime/http-server/daemon-admin/routing-policy.js +18 -29
  145. package/dist/server/runtime/http-server/daemon-admin/routing-policy.js.map +1 -1
  146. package/dist/server/runtime/http-server/daemon-admin/stats-handler.js +2 -0
  147. package/dist/server/runtime/http-server/daemon-admin/stats-handler.js.map +1 -1
  148. package/dist/server/runtime/http-server/daemon-admin-routes.d.ts +8 -1
  149. package/dist/server/runtime/http-server/daemon-admin-routes.js +30 -0
  150. package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
  151. package/dist/server/runtime/http-server/executor/client-injection-flow.d.ts +14 -0
  152. package/dist/server/runtime/http-server/executor/client-injection-flow.js +287 -0
  153. package/dist/server/runtime/http-server/executor/client-injection-flow.js.map +1 -0
  154. package/dist/server/runtime/http-server/executor/index.d.ts +1 -1
  155. package/dist/server/runtime/http-server/executor/index.js +1 -1
  156. package/dist/server/runtime/http-server/executor/index.js.map +1 -1
  157. package/dist/server/runtime/http-server/executor/provider-response-converter.js +236 -62
  158. package/dist/server/runtime/http-server/executor/provider-response-converter.js.map +1 -1
  159. package/dist/server/runtime/http-server/executor/request-executor-core-utils.d.ts +1 -0
  160. package/dist/server/runtime/http-server/executor/request-executor-core-utils.js +12 -0
  161. package/dist/server/runtime/http-server/executor/request-executor-core-utils.js.map +1 -1
  162. package/dist/server/runtime/http-server/executor/request-retry-helpers.js +16 -12
  163. package/dist/server/runtime/http-server/executor/request-retry-helpers.js.map +1 -1
  164. package/dist/server/runtime/http-server/executor/sse-error-handler.d.ts +1 -0
  165. package/dist/server/runtime/http-server/executor/sse-error-handler.js +13 -2
  166. package/dist/server/runtime/http-server/executor/sse-error-handler.js.map +1 -1
  167. package/dist/server/runtime/http-server/executor/usage-aggregator.d.ts +0 -12
  168. package/dist/server/runtime/http-server/executor/usage-aggregator.js +84 -88
  169. package/dist/server/runtime/http-server/executor/usage-aggregator.js.map +1 -1
  170. package/dist/server/runtime/http-server/executor-metadata.js +328 -7
  171. package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
  172. package/dist/server/runtime/http-server/executor-response.d.ts +1 -0
  173. package/dist/server/runtime/http-server/executor-response.js +52 -58
  174. package/dist/server/runtime/http-server/executor-response.js.map +1 -1
  175. package/dist/server/runtime/http-server/http-server-bootstrap.js +50 -6
  176. package/dist/server/runtime/http-server/http-server-bootstrap.js.map +1 -1
  177. package/dist/server/runtime/http-server/http-server-clock-daemon.d.ts +1 -0
  178. package/dist/server/runtime/http-server/http-server-clock-daemon.js +186 -44
  179. package/dist/server/runtime/http-server/http-server-clock-daemon.js.map +1 -1
  180. package/dist/server/runtime/http-server/http-server-lifecycle.js +4 -4
  181. package/dist/server/runtime/http-server/http-server-lifecycle.js.map +1 -1
  182. package/dist/server/runtime/http-server/hub-shadow-compare.js +1 -1
  183. package/dist/server/runtime/http-server/hub-shadow-compare.js.map +1 -1
  184. package/dist/server/runtime/http-server/index.d.ts +1 -0
  185. package/dist/server/runtime/http-server/index.js +1 -0
  186. package/dist/server/runtime/http-server/index.js.map +1 -1
  187. package/dist/server/runtime/http-server/middleware.js +82 -4
  188. package/dist/server/runtime/http-server/middleware.js.map +1 -1
  189. package/dist/server/runtime/http-server/request-executor.js +6 -5
  190. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  191. package/dist/server/runtime/http-server/routes.d.ts +2 -1
  192. package/dist/server/runtime/http-server/routes.js +4 -2
  193. package/dist/server/runtime/http-server/routes.js.map +1 -1
  194. package/dist/server/runtime/http-server/session-dir.js +12 -1
  195. package/dist/server/runtime/http-server/session-dir.js.map +1 -1
  196. package/dist/server/runtime/http-server/stats-manager.d.ts +35 -0
  197. package/dist/server/runtime/http-server/stats-manager.js +269 -21
  198. package/dist/server/runtime/http-server/stats-manager.js.map +1 -1
  199. package/dist/server/runtime/http-server/stopmessage-scope-rebind.d.ts +13 -0
  200. package/dist/server/runtime/http-server/stopmessage-scope-rebind.js +168 -0
  201. package/dist/server/runtime/http-server/stopmessage-scope-rebind.js.map +1 -0
  202. package/dist/server/runtime/http-server/tmux-session-probe.d.ts +10 -0
  203. package/dist/server/runtime/http-server/tmux-session-probe.js +97 -0
  204. package/dist/server/runtime/http-server/tmux-session-probe.js.map +1 -1
  205. package/dist/server-lifecycle/port-utils.d.ts +2 -1
  206. package/dist/server-lifecycle/port-utils.js +84 -4
  207. package/dist/server-lifecycle/port-utils.js.map +1 -1
  208. package/dist/token-daemon/index.d.ts +1 -0
  209. package/dist/token-daemon/index.js +17 -12
  210. package/dist/token-daemon/index.js.map +1 -1
  211. package/dist/utils/clock-client-token.d.ts +2 -1
  212. package/dist/utils/clock-client-token.js +52 -8
  213. package/dist/utils/clock-client-token.js.map +1 -1
  214. package/dist/utils/clock-scope-trace.d.ts +11 -0
  215. package/dist/utils/clock-scope-trace.js +41 -0
  216. package/dist/utils/clock-scope-trace.js.map +1 -0
  217. package/dist/utils/llms-engine-shadow.js +1 -1
  218. package/dist/utils/llms-engine-shadow.js.map +1 -1
  219. package/docs/DAEMON_CONTROL_PLANE.md +1 -0
  220. package/docs/ROUTING_POLICY_SCHEMA.md +4 -2
  221. package/docs/daemon-admin-ui.html +328 -57
  222. package/docs/design/servertool-stopmessage-lifecycle.md +109 -0
  223. package/docs/exec-command-guard-policy.example.v1.json +7 -1
  224. package/docs/providers/antigravity-gemini-provider-compat.md +2 -2
  225. package/package.json +21 -5
  226. package/scripts/build-core.mjs +12 -0
  227. package/scripts/camoufox/launch-auth.mjs +112 -5
  228. package/scripts/ci/repo-sanity.mjs +1 -0
  229. package/scripts/install-global.sh +6 -0
  230. package/scripts/install-verify.mjs +33 -16
  231. package/scripts/run-bg.sh +226 -43
  232. package/scripts/run-fg-gtimeout.sh +158 -14
  233. package/scripts/tests/blackbox-rcc-vs-routecodex-antigravity.mjs +3 -3
  234. package/scripts/tests/ci-jest.mjs +9 -1
  235. package/scripts/triage-errorsamples.mjs +216 -0
  236. package/scripts/verify-codex-error-samples.mjs +92 -15
  237. package/scripts/verify-install-e2e.mjs +57 -27
package/scripts/run-bg.sh CHANGED
@@ -5,8 +5,7 @@
5
5
  # Notes:
6
6
  # - Always backgrounds the command using nohup and &
7
7
  # - If timeout_seconds > 0, a watchdog will terminate the process after the timeout
8
- # - Detects an existing RouteCodex server listener on the target port. With --replace, it will terminate
9
- # the existing listener (regardless of worktree/global install). Without --replace, it exits non‑zero.
8
+ # - Only manages processes via pid file with command validation; refuses to kill unknown listeners
10
9
 
11
10
  set -euo pipefail
12
11
 
@@ -17,6 +16,7 @@ fi
17
16
 
18
17
  REPLACE=0
19
18
  TARGET_PORT=""
19
+ PRESTART_TRUSTED_LISTENERS=""
20
20
 
21
21
  # Parse optional flags until --
22
22
  while [[ $# -gt 0 ]]; do
@@ -35,30 +35,15 @@ done
35
35
  CMD_STR=${1:-}
36
36
  TIMEOUT_SEC=${2:-0}
37
37
 
38
- if [[ -z "${CMD_STR}" ]]; then
38
+ if [[ -z "${CMD_STR:-}" ]]; then
39
39
  echo "Command string is empty" >&2
40
40
  exit 2
41
41
  fi
42
42
 
43
- cleanup_existing_servers() {
44
- local killed=0
45
- if [[ "${CMD_STR}" == *"dist/index.js"* || "${CMD_STR}" == *"routecodex"* ]]; then
46
- echo "[run-bg] ensuring no previous RouteCodex server is running" >&2
47
- pkill -f "/opt/homebrew/lib/node_modules/routecodex/dist/index.js" 2>/dev/null && killed=1 || true
48
- pkill -f "$(pwd)/dist/index.js" 2>/dev/null && killed=1 || true
49
- pkill -f "routecodex/dist/index.js" 2>/dev/null && killed=1 || true
50
- if [[ "${killed}" -eq 1 ]]; then
51
- sleep 1
52
- fi
53
- fi
54
- }
55
-
56
43
  detect_port() {
57
- local p="${TARGET_PORT}"
44
+ local p="${TARGET_PORT:-}"
58
45
  if [[ -n "$p" ]]; then echo "$p"; return; fi
59
- # Prefer env
60
46
  if [[ -n "${ROUTECODEX_PORT:-}" ]]; then echo "${ROUTECODEX_PORT}"; return; fi
61
- # Try user config
62
47
  local cfg="$HOME/.routecodex/config.json"
63
48
  if command -v jq >/dev/null 2>&1 && [[ -f "$cfg" ]]; then
64
49
  p=$(jq -r '.port // empty' "$cfg" 2>/dev/null || true)
@@ -67,47 +52,245 @@ detect_port() {
67
52
  echo 5520
68
53
  }
69
54
 
70
- ensure_singleton() {
71
- local port=$(detect_port)
72
- # Check existing listener on port
73
- local pids
74
- pids=$(lsof -t -nP -iTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)
75
- if [[ -n "$pids" ]]; then
76
- if [[ "$REPLACE" -eq 1 ]]; then
77
- echo "[run-bg] port $port is in use by PIDs: $pids; attempting graceful replace" >&2
78
- # First send TERM
79
- while read -r pid; do
80
- [[ -z "$pid" ]] && continue
81
- kill -TERM "$pid" 2>/dev/null || true
82
- done <<< "$pids"
55
+ # Returns 0 when command is a trusted RouteCodex server command.
56
+ is_routecodex_process() {
57
+ local pid="$1"
58
+ local cmd
59
+ cmd=$(ps -o command= -p "$pid" 2>/dev/null || echo "")
60
+ local normalized
61
+ normalized=$(echo "$cmd" | tr '[:upper:]' '[:lower:]')
62
+
63
+ if [[ "$normalized" == *"routecodex/dist/index.js"* ]]; then
64
+ return 0
65
+ fi
66
+ if [[ "$normalized" == *"@jsonstudio/rcc"* && "$normalized" == *"/dist/index.js"* ]]; then
67
+ return 0
68
+ fi
69
+ if [[ "$normalized" == *"jsonstudio-rcc"* && "$normalized" == *"/dist/index.js"* ]]; then
70
+ return 0
71
+ fi
72
+ return 1
73
+ }
74
+
75
+ is_routecodex_server_command() {
76
+ local normalized
77
+ normalized=$(echo "${1:-}" | tr '[:upper:]' '[:lower:]')
78
+ if [[ "$normalized" == *"dist/index.js"* ]]; then
79
+ return 0
80
+ fi
81
+ return 1
82
+ }
83
+
84
+ list_listener_pids_by_port() {
85
+ local port="$1"
86
+ if ! command -v lsof >/dev/null 2>&1; then
87
+ return 0
88
+ fi
89
+ local raw
90
+ raw=$(lsof -t -nP -iTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)
91
+ if [[ -z "${raw:-}" ]]; then
92
+ return 0
93
+ fi
94
+ echo "$raw" | awk 'NF>0 && !seen[$1]++ { print $1 }'
95
+ }
96
+
97
+ stop_routecodex_pid() {
98
+ local pid="$1"
99
+ if [[ -z "${pid:-}" ]]; then
100
+ return 0
101
+ fi
102
+ if ! kill -0 "$pid" 2>/dev/null; then
103
+ return 0
104
+ fi
105
+ if ! is_routecodex_process "$pid"; then
106
+ echo "[run-bg] WARNING: refusing to stop non-RouteCodex PID $pid" >&2
107
+ return 1
108
+ fi
109
+
110
+ kill -TERM "$pid" 2>/dev/null || true
111
+ sleep 1
112
+ if kill -0 "$pid" 2>/dev/null; then
113
+ if is_routecodex_process "$pid"; then
114
+ kill -KILL "$pid" 2>/dev/null || true
83
115
  sleep 1
84
- # Force kill survivors
85
- pids=$(lsof -t -nP -iTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)
86
- if [[ -n "$pids" ]]; then
87
- while read -r pid; do
88
- [[ -z "$pid" ]] && continue
89
- kill -KILL "$pid" 2>/dev/null || true
90
- done <<< "$pids"
91
- sleep 1
92
- fi
93
116
  else
94
- echo "[run-bg] detected existing listener on port $port (PIDs: $pids). Use --replace to replace it." >&2
117
+ echo "[run-bg] WARNING: PID $pid changed owner before force kill, skip" >&2
118
+ fi
119
+ fi
120
+ return 0
121
+ }
122
+
123
+ stop_managed_server() {
124
+ local port="$1"
125
+ local pid_file="$HOME/.routecodex/server-${port}.pid"
126
+
127
+ if [[ ! -f "$pid_file" ]]; then
128
+ return 0
129
+ fi
130
+
131
+ local pid
132
+ pid=$(cat "$pid_file" 2>/dev/null || echo "")
133
+
134
+ if [[ -z "$pid" ]]; then
135
+ rm -f "$pid_file"
136
+ return 0
137
+ fi
138
+
139
+ if ! kill -0 "$pid" 2>/dev/null; then
140
+ rm -f "$pid_file"
141
+ return 0
142
+ fi
143
+
144
+ # 验证进程归属
145
+ if ! is_routecodex_process "$pid"; then
146
+ echo "[run-bg] WARNING: PID $pid from pid file is not a RouteCodex process, refusing to kill" >&2
147
+ rm -f "$pid_file"
148
+ return 1
149
+ fi
150
+
151
+ echo "[run-bg] stopping managed server PID $pid on port $port" >&2
152
+ stop_routecodex_pid "$pid" || true
153
+
154
+ rm -f "$pid_file"
155
+ }
156
+
157
+ capture_prestart_trusted_listeners() {
158
+ local listener_pids="$1"
159
+ PRESTART_TRUSTED_LISTENERS=""
160
+ if [[ -z "${listener_pids:-}" ]]; then
161
+ return 0
162
+ fi
163
+ while read -r lp; do
164
+ [[ -z "${lp:-}" ]] && continue
165
+ if is_routecodex_process "$lp"; then
166
+ PRESTART_TRUSTED_LISTENERS="${PRESTART_TRUSTED_LISTENERS}${PRESTART_TRUSTED_LISTENERS:+ }$lp"
167
+ fi
168
+ done <<< "$listener_pids"
169
+ }
170
+
171
+ wait_routecodex_server_ready() {
172
+ local port="$1"
173
+ local pid="$2"
174
+ local excluded_pids="${3:-}"
175
+ local attempts=20
176
+ while [[ "$attempts" -gt 0 ]]; do
177
+ if ! kill -0 "$pid" 2>/dev/null; then
178
+ return 1
179
+ fi
180
+ local listener_pids
181
+ listener_pids=$(list_listener_pids_by_port "$port")
182
+ if [[ -n "${listener_pids:-}" ]]; then
183
+ while read -r lp; do
184
+ [[ -z "${lp:-}" ]] && continue
185
+ if [[ "$lp" == "$pid" ]]; then
186
+ return 0
187
+ fi
188
+ if is_routecodex_process "$lp"; then
189
+ case " ${excluded_pids} " in
190
+ *" ${lp} "*) ;;
191
+ *) return 0 ;;
192
+ esac
193
+ fi
194
+ done <<< "$listener_pids"
195
+ fi
196
+ sleep 0.2
197
+ attempts=$((attempts - 1))
198
+ done
199
+ return 1
200
+ }
201
+
202
+ ensure_singleton() {
203
+ local port
204
+ port=$(detect_port)
205
+ local pid_file="$HOME/.routecodex/server-${port}.pid"
206
+ local listener_pids
207
+ listener_pids=$(list_listener_pids_by_port "$port")
208
+ capture_prestart_trusted_listeners "$listener_pids"
209
+
210
+ if [[ -n "${listener_pids:-}" ]]; then
211
+ local trusted_pids=""
212
+ local foreign_pids=""
213
+ while read -r lp; do
214
+ [[ -z "${lp:-}" ]] && continue
215
+ if is_routecodex_process "$lp"; then
216
+ trusted_pids="${trusted_pids}${trusted_pids:+ }$lp"
217
+ else
218
+ foreign_pids="${foreign_pids}${foreign_pids:+ }$lp"
219
+ fi
220
+ done <<< "$listener_pids"
221
+
222
+ if [[ -n "${foreign_pids:-}" ]]; then
223
+ echo "[run-bg] port $port is occupied by non-RouteCodex listener(s): ${foreign_pids}. Refusing to stop listener." >&2
95
224
  exit 9
96
225
  fi
226
+
227
+ if [[ "$REPLACE" -ne 1 ]]; then
228
+ if is_routecodex_server_command "${CMD_STR}"; then
229
+ echo "[run-bg] RouteCodex server already listening on port $port (PID(s): ${trusted_pids:-unknown}); auto-replacing trusted server listener." >&2
230
+ REPLACE=1
231
+ else
232
+ echo "[run-bg] RouteCodex server already listening on port $port (PID(s): ${trusted_pids:-unknown}). Use --replace to replace it." >&2
233
+ exit 9
234
+ fi
235
+ fi
236
+
237
+ if [[ -f "$pid_file" ]]; then
238
+ stop_managed_server "$port" || true
239
+ else
240
+ echo "[run-bg] replacing trusted RouteCodex listener(s) on port $port (PID(s): ${trusted_pids})" >&2
241
+ while read -r tp; do
242
+ [[ -z "${tp:-}" ]] && continue
243
+ stop_routecodex_pid "$tp" || true
244
+ done <<< "$trusted_pids"
245
+ fi
246
+ fi
247
+
248
+ if [[ -f "$pid_file" ]]; then
249
+ local pid
250
+ pid=$(cat "$pid_file" 2>/dev/null || echo "")
251
+
252
+ if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
253
+ # 验证进程归属
254
+ if is_routecodex_process "$pid"; then
255
+ if [[ "$REPLACE" -eq 1 ]]; then
256
+ echo "[run-bg] replacing managed server PID $pid on port $port" >&2
257
+ stop_managed_server "$port"
258
+ else
259
+ echo "[run-bg] managed server already running on port $port (PID $pid). Use --replace to stop it." >&2
260
+ exit 9
261
+ fi
262
+ else
263
+ echo "[run-bg] WARNING: PID $pid from pid file is not a RouteCodex process, cleaning stale pid file" >&2
264
+ rm -f "$pid_file"
265
+ fi
266
+ else
267
+ rm -f "$pid_file"
268
+ fi
97
269
  fi
98
270
  }
99
271
 
100
- cleanup_existing_servers
101
272
  ensure_singleton
102
273
 
103
274
  ts=$(date +%s)
104
275
  log_file="/tmp/routecodex-bg-${ts}.log"
276
+ port=$(detect_port)
105
277
 
106
278
  echo "[run-bg] starting: ${CMD_STR} (log: ${log_file})" >&2
107
279
  nohup bash -lc "${CMD_STR}" >"${log_file}" 2>&1 &
108
280
  pid=$!
109
281
  echo "[run-bg] started pid=${pid}" >&2
110
282
 
283
+ if is_routecodex_server_command "${CMD_STR}"; then
284
+ if ! wait_routecodex_server_ready "$port" "$pid" "${PRESTART_TRUSTED_LISTENERS:-}"; then
285
+ echo "[run-bg] ERROR: RouteCodex server failed to become ready on port ${port} (pid=${pid})." >&2
286
+ if [[ -f "$log_file" ]]; then
287
+ echo "[run-bg] recent log tail:" >&2
288
+ tail -n 40 "$log_file" >&2 || true
289
+ fi
290
+ exit 1
291
+ fi
292
+ fi
293
+
111
294
  if [[ "${TIMEOUT_SEC}" =~ ^[0-9]+$ && ${TIMEOUT_SEC} -gt 0 ]]; then
112
295
  (
113
296
  sleep "${TIMEOUT_SEC}" || true
@@ -4,6 +4,7 @@
4
4
  # bash scripts/run-fg-gtimeout.sh <timeout_seconds> [--replace] [--port <port>] -- '<command>'
5
5
  # Notes:
6
6
  # - Uses gtimeout when available; otherwise falls back to a manual killer
7
+ # - Only manages processes via pid file with command validation; refuses to kill unknown listeners
7
8
 
8
9
  set -euo pipefail
9
10
 
@@ -28,13 +29,13 @@ while [[ $# -gt 0 ]]; do
28
29
  done
29
30
 
30
31
  CMD_STR=${1:-}
31
- if [[ -z "${CMD_STR}" ]]; then
32
+ if [[ -z "${CMD_STR:-}" ]]; then
32
33
  echo "Command string is empty" >&2
33
34
  exit 2
34
35
  fi
35
36
 
36
37
  detect_port() {
37
- local p="${TARGET_PORT}"
38
+ local p="${TARGET_PORT:-}"
38
39
  if [[ -n "$p" ]]; then echo "$p"; return; fi
39
40
  if [[ -n "${ROUTECODEX_PORT:-}" ]]; then echo "${ROUTECODEX_PORT}"; return; fi
40
41
  local cfg="$HOME/.routecodex/config.json"
@@ -45,22 +46,165 @@ detect_port() {
45
46
  echo 5520
46
47
  }
47
48
 
48
- ensure_singleton() {
49
- local port=$(detect_port)
50
- local pids
51
- pids=$(lsof -t -nP -iTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)
52
- if [[ -n "$pids" ]]; then
53
- if [[ "$REPLACE" -eq 1 ]]; then
54
- echo "[run-fg] port $port is in use by PIDs: $pids; attempting graceful replace" >&2
55
- while read -r pid; do kill -TERM "$pid" 2>/dev/null || true; done <<< "$pids"
56
- sleep 1
57
- pids=$(lsof -t -nP -iTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)
58
- if [[ -n "$pids" ]]; then while read -r pid; do kill -KILL "$pid" 2>/dev/null || true; done <<< "$pids"; fi
49
+ # Returns 0 when command is a trusted RouteCodex server command.
50
+ is_routecodex_process() {
51
+ local pid="$1"
52
+ local cmd
53
+ cmd=$(ps -o command= -p "$pid" 2>/dev/null || echo "")
54
+ local normalized
55
+ normalized=$(echo "$cmd" | tr '[:upper:]' '[:lower:]')
56
+
57
+ if [[ "$normalized" == *"routecodex/dist/index.js"* ]]; then
58
+ return 0
59
+ fi
60
+ if [[ "$normalized" == *"@jsonstudio/rcc"* && "$normalized" == *"/dist/index.js"* ]]; then
61
+ return 0
62
+ fi
63
+ if [[ "$normalized" == *"jsonstudio-rcc"* && "$normalized" == *"/dist/index.js"* ]]; then
64
+ return 0
65
+ fi
66
+ return 1
67
+ }
68
+
69
+ list_listener_pids_by_port() {
70
+ local port="$1"
71
+ if ! command -v lsof >/dev/null 2>&1; then
72
+ return 0
73
+ fi
74
+ local raw
75
+ raw=$(lsof -t -nP -iTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)
76
+ if [[ -z "${raw:-}" ]]; then
77
+ return 0
78
+ fi
79
+ echo "$raw" | awk 'NF>0 && !seen[$1]++ { print $1 }'
80
+ }
81
+
82
+ stop_routecodex_pid() {
83
+ local pid="$1"
84
+ if [[ -z "${pid:-}" ]]; then
85
+ return 0
86
+ fi
87
+ if ! kill -0 "$pid" 2>/dev/null; then
88
+ return 0
89
+ fi
90
+ if ! is_routecodex_process "$pid"; then
91
+ echo "[run-fg] WARNING: refusing to stop non-RouteCodex PID $pid" >&2
92
+ return 1
93
+ fi
94
+
95
+ kill -TERM "$pid" 2>/dev/null || true
96
+ sleep 1
97
+ if kill -0 "$pid" 2>/dev/null; then
98
+ if is_routecodex_process "$pid"; then
99
+ kill -KILL "$pid" 2>/dev/null || true
59
100
  sleep 1
60
101
  else
61
- echo "[run-fg] detected existing listener on port $port (PIDs: $pids). Use --replace to replace it." >&2
102
+ echo "[run-fg] WARNING: PID $pid changed owner before force kill, skip" >&2
103
+ fi
104
+ fi
105
+ return 0
106
+ }
107
+
108
+ stop_managed_server() {
109
+ local port="$1"
110
+ local pid_file="$HOME/.routecodex/server-${port}.pid"
111
+
112
+ if [[ ! -f "$pid_file" ]]; then
113
+ return 0
114
+ fi
115
+
116
+ local pid
117
+ pid=$(cat "$pid_file" 2>/dev/null || echo "")
118
+
119
+ if [[ -z "$pid" ]]; then
120
+ rm -f "$pid_file"
121
+ return 0
122
+ fi
123
+
124
+ if ! kill -0 "$pid" 2>/dev/null; then
125
+ rm -f "$pid_file"
126
+ return 0
127
+ fi
128
+
129
+ # 验证进程归属
130
+ if ! is_routecodex_process "$pid"; then
131
+ echo "[run-fg] WARNING: PID $pid from pid file is not a RouteCodex process, refusing to kill" >&2
132
+ rm -f "$pid_file"
133
+ return 1
134
+ fi
135
+
136
+ echo "[run-fg] stopping managed server PID $pid on port $port" >&2
137
+ stop_routecodex_pid "$pid" || true
138
+
139
+ rm -f "$pid_file"
140
+ }
141
+
142
+ ensure_singleton() {
143
+ local port
144
+ port=$(detect_port)
145
+ local pid_file="$HOME/.routecodex/server-${port}.pid"
146
+ local listener_pids
147
+ listener_pids=$(list_listener_pids_by_port "$port")
148
+
149
+ if [[ -n "${listener_pids:-}" ]]; then
150
+ local trusted_pids=""
151
+ local foreign_pids=""
152
+ while read -r lp; do
153
+ [[ -z "${lp:-}" ]] && continue
154
+ if is_routecodex_process "$lp"; then
155
+ trusted_pids="${trusted_pids}${trusted_pids:+ }$lp"
156
+ else
157
+ foreign_pids="${foreign_pids}${foreign_pids:+ }$lp"
158
+ fi
159
+ done <<< "$listener_pids"
160
+
161
+ if [[ -n "${foreign_pids:-}" ]]; then
162
+ echo "[run-fg] port $port is occupied by non-RouteCodex listener(s): ${foreign_pids}. Refusing to stop listener." >&2
62
163
  exit 9
63
164
  fi
165
+
166
+ if [[ "$REPLACE" -ne 1 ]]; then
167
+ if [[ "$(echo "${CMD_STR:-}" | tr '[:upper:]' '[:lower:]')" == *"dist/index.js"* ]]; then
168
+ echo "[run-fg] RouteCodex server already listening on port $port (PID(s): ${trusted_pids:-unknown}); auto-replacing trusted server listener." >&2
169
+ REPLACE=1
170
+ else
171
+ echo "[run-fg] RouteCodex server already listening on port $port (PID(s): ${trusted_pids:-unknown}). Use --replace to replace it." >&2
172
+ exit 9
173
+ fi
174
+ fi
175
+
176
+ if [[ -f "$pid_file" ]]; then
177
+ stop_managed_server "$port" || true
178
+ else
179
+ echo "[run-fg] replacing trusted RouteCodex listener(s) on port $port (PID(s): ${trusted_pids})" >&2
180
+ while read -r tp; do
181
+ [[ -z "${tp:-}" ]] && continue
182
+ stop_routecodex_pid "$tp" || true
183
+ done <<< "$trusted_pids"
184
+ fi
185
+ fi
186
+
187
+ if [[ -f "$pid_file" ]]; then
188
+ local pid
189
+ pid=$(cat "$pid_file" 2>/dev/null || echo "")
190
+
191
+ if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
192
+ # 验证进程归属
193
+ if is_routecodex_process "$pid"; then
194
+ if [[ "$REPLACE" -eq 1 ]]; then
195
+ echo "[run-fg] replacing managed server PID $pid on port $port" >&2
196
+ stop_managed_server "$port"
197
+ else
198
+ echo "[run-fg] managed server already running on port $port (PID $pid). Use --replace to stop it." >&2
199
+ exit 9
200
+ fi
201
+ else
202
+ echo "[run-fg] WARNING: PID $pid from pid file is not a RouteCodex process, cleaning stale pid file" >&2
203
+ rm -f "$pid_file"
204
+ fi
205
+ else
206
+ rm -f "$pid_file"
207
+ fi
64
208
  fi
65
209
  }
66
210
 
@@ -541,9 +541,9 @@ async function runOnceBlackbox(opts) {
541
541
  // Keep blackbox deterministic/hermetic: do not hit Antigravity auto-updater from CI.
542
542
  ROUTECODEX_ANTIGRAVITY_UA_DISABLE_REMOTE: '1',
543
543
  RCC_ANTIGRAVITY_UA_DISABLE_REMOTE: '1',
544
- // Also pin UA version so we don't rely on hardcoded fallbacks in tests.
545
- ROUTECODEX_ANTIGRAVITY_UA_VERSION: '1.11.9',
546
- RCC_ANTIGRAVITY_UA_VERSION: '1.11.9',
544
+ // Also pin UA version so the blackbox run stays deterministic.
545
+ ROUTECODEX_ANTIGRAVITY_UA_VERSION: '4.1.24',
546
+ RCC_ANTIGRAVITY_UA_VERSION: '4.1.24',
547
547
  ...(antigravityApiBase
548
548
  ? {
549
549
  ROUTECODEX_ANTIGRAVITY_API_BASE: antigravityApiBase,
@@ -90,8 +90,16 @@ const cliTests = [
90
90
  'tests/cli/stop-command.spec.ts'
91
91
  ];
92
92
 
93
+ const webuiTests = [
94
+ 'tests/frontend/webui-app.utils.spec.ts',
95
+ 'tests/frontend/webui-app.render.spec.tsx',
96
+ 'tests/frontend/webui-app.integration.spec.tsx',
97
+ 'tests/frontend/webui-app.pages.spec.tsx',
98
+ 'tests/frontend/webui-app.edge.spec.tsx'
99
+ ];
100
+
93
101
  const wantsCoverage = process.argv.includes('--coverage') || process.env.ROUTECODEX_CI_COVERAGE === '1';
94
- const allTests = [...routingInstructionTests, ...cliTests];
102
+ const allTests = [...routingInstructionTests, ...cliTests, ...webuiTests];
95
103
 
96
104
  const jestBin = path.join(process.cwd(), 'node_modules', 'jest', 'bin', 'jest.js');
97
105