@jsonstudio/rcc 0.89.683 → 0.89.912

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 (215) hide show
  1. package/README.md +44 -0
  2. package/dist/build-info.js +2 -2
  3. package/dist/cli.js +164 -116
  4. package/dist/cli.js.map +1 -1
  5. package/dist/client/anthropic/anthropic-protocol-client.js +42 -1
  6. package/dist/client/anthropic/anthropic-protocol-client.js.map +1 -1
  7. package/dist/client/gemini-cli/gemini-cli-protocol-client.js +4 -1
  8. package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
  9. package/dist/commands/camoufox-backfill.d.ts +2 -0
  10. package/dist/commands/camoufox-backfill.js +33 -0
  11. package/dist/commands/camoufox-backfill.js.map +1 -0
  12. package/dist/commands/camoufox-fp.d.ts +2 -0
  13. package/dist/commands/camoufox-fp.js +86 -0
  14. package/dist/commands/camoufox-fp.js.map +1 -0
  15. package/dist/commands/oauth.d.ts +2 -0
  16. package/dist/commands/oauth.js +170 -0
  17. package/dist/commands/oauth.js.map +1 -0
  18. package/dist/commands/provider-update.js +439 -2
  19. package/dist/commands/provider-update.js.map +1 -1
  20. package/dist/commands/quota-status.d.ts +2 -0
  21. package/dist/commands/quota-status.js +80 -0
  22. package/dist/commands/quota-status.js.map +1 -0
  23. package/dist/commands/token-daemon.js +12 -1
  24. package/dist/commands/token-daemon.js.map +1 -1
  25. package/dist/config/provider-v2-loader.d.ts +16 -0
  26. package/dist/config/provider-v2-loader.js +84 -0
  27. package/dist/config/provider-v2-loader.js.map +1 -0
  28. package/dist/config/routecodex-config-loader.js +27 -4
  29. package/dist/config/routecodex-config-loader.js.map +1 -1
  30. package/dist/config/system-prompts/codex-cli.txt +1 -0
  31. package/dist/config/virtual-router-builder.d.ts +9 -0
  32. package/dist/config/virtual-router-builder.js +34 -0
  33. package/dist/config/virtual-router-builder.js.map +1 -0
  34. package/dist/config/virtual-router-types.d.ts +25 -0
  35. package/dist/config/virtual-router-types.js +30 -0
  36. package/dist/config/virtual-router-types.js.map +1 -0
  37. package/dist/manager/index.d.ts +10 -0
  38. package/dist/manager/index.js +27 -0
  39. package/dist/manager/index.js.map +1 -0
  40. package/dist/manager/modules/health/index.d.ts +22 -0
  41. package/dist/manager/modules/health/index.js +82 -0
  42. package/dist/manager/modules/health/index.js.map +1 -0
  43. package/dist/manager/modules/quota/index.d.ts +57 -0
  44. package/dist/manager/modules/quota/index.js +426 -0
  45. package/dist/manager/modules/quota/index.js.map +1 -0
  46. package/dist/manager/modules/routing/index.d.ts +17 -0
  47. package/dist/manager/modules/routing/index.js +61 -0
  48. package/dist/manager/modules/routing/index.js.map +1 -0
  49. package/dist/manager/modules/token/index.d.ts +10 -0
  50. package/dist/manager/modules/token/index.js +58 -0
  51. package/dist/manager/modules/token/index.js.map +1 -0
  52. package/dist/manager/storage/base-store.d.ts +6 -0
  53. package/dist/manager/storage/base-store.js +2 -0
  54. package/dist/manager/storage/base-store.js.map +1 -0
  55. package/dist/manager/storage/file-store.d.ts +25 -0
  56. package/dist/manager/storage/file-store.js +117 -0
  57. package/dist/manager/storage/file-store.js.map +1 -0
  58. package/dist/manager/types.d.ts +9 -0
  59. package/dist/manager/types.js +2 -0
  60. package/dist/manager/types.js.map +1 -0
  61. package/dist/message-center/index.d.ts +5 -0
  62. package/dist/message-center/index.js +6 -0
  63. package/dist/message-center/index.js.map +1 -0
  64. package/dist/message-center/message-center.d.ts +93 -0
  65. package/dist/message-center/message-center.js +189 -0
  66. package/dist/message-center/message-center.js.map +1 -0
  67. package/dist/providers/auth/antigravity-userinfo-helper.d.ts +2 -0
  68. package/dist/providers/auth/antigravity-userinfo-helper.js +102 -0
  69. package/dist/providers/auth/antigravity-userinfo-helper.js.map +1 -1
  70. package/dist/providers/auth/iflow-cookie-auth.d.ts +27 -0
  71. package/dist/providers/auth/iflow-cookie-auth.js +209 -0
  72. package/dist/providers/auth/iflow-cookie-auth.js.map +1 -0
  73. package/dist/providers/auth/oauth-lifecycle.js +29 -22
  74. package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
  75. package/dist/providers/auth/token-scanner/index.js +16 -1
  76. package/dist/providers/auth/token-scanner/index.js.map +1 -1
  77. package/dist/providers/core/config/camoufox-launcher.d.ts +16 -0
  78. package/dist/providers/core/config/camoufox-launcher.js +314 -0
  79. package/dist/providers/core/config/camoufox-launcher.js.map +1 -0
  80. package/dist/providers/core/config/oauth-flows.d.ts +9 -0
  81. package/dist/providers/core/config/oauth-flows.js +50 -19
  82. package/dist/providers/core/config/oauth-flows.js.map +1 -1
  83. package/dist/providers/core/config/provider-oauth-configs.d.ts +6 -0
  84. package/dist/providers/core/config/provider-oauth-configs.js +12 -0
  85. package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
  86. package/dist/providers/core/config/service-profiles.js +26 -3
  87. package/dist/providers/core/config/service-profiles.js.map +1 -1
  88. package/dist/providers/core/runtime/antigravity-quota-client.d.ts +10 -0
  89. package/dist/providers/core/runtime/antigravity-quota-client.js +88 -0
  90. package/dist/providers/core/runtime/antigravity-quota-client.js.map +1 -0
  91. package/dist/providers/core/runtime/base-provider.d.ts +2 -1
  92. package/dist/providers/core/runtime/base-provider.js +93 -34
  93. package/dist/providers/core/runtime/base-provider.js.map +1 -1
  94. package/dist/providers/core/runtime/gemini-cli-http-provider.js +42 -10
  95. package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
  96. package/dist/providers/core/runtime/http-request-executor.js +24 -0
  97. package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
  98. package/dist/providers/core/runtime/http-transport-provider.d.ts +0 -3
  99. package/dist/providers/core/runtime/http-transport-provider.js +32 -136
  100. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  101. package/dist/providers/core/runtime/provider-error-classifier.js +18 -10
  102. package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
  103. package/dist/providers/core/runtime/rate-limit-manager.d.ts +6 -0
  104. package/dist/providers/core/runtime/rate-limit-manager.js +23 -0
  105. package/dist/providers/core/runtime/rate-limit-manager.js.map +1 -1
  106. package/dist/providers/core/runtime/responses-provider.js +17 -19
  107. package/dist/providers/core/runtime/responses-provider.js.map +1 -1
  108. package/dist/providers/core/strategies/oauth-auth-code-flow.d.ts +1 -0
  109. package/dist/providers/core/strategies/oauth-auth-code-flow.js +3 -2
  110. package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
  111. package/dist/providers/core/strategies/oauth-device-flow.d.ts +1 -0
  112. package/dist/providers/core/strategies/oauth-device-flow.js +3 -2
  113. package/dist/providers/core/strategies/oauth-device-flow.js.map +1 -1
  114. package/dist/providers/core/strategies/oauth-hybrid-flow.d.ts +1 -0
  115. package/dist/providers/core/strategies/oauth-hybrid-flow.js +3 -2
  116. package/dist/providers/core/strategies/oauth-hybrid-flow.js.map +1 -1
  117. package/dist/providers/core/utils/http-client.js +43 -1
  118. package/dist/providers/core/utils/http-client.js.map +1 -1
  119. package/dist/providers/mock/mock-provider-runtime.js +4 -4
  120. package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
  121. package/dist/providers/profile/provider-profile-loader.js +13 -1
  122. package/dist/providers/profile/provider-profile-loader.js.map +1 -1
  123. package/dist/providers/profile/provider-profile.d.ts +5 -0
  124. package/dist/scripts/camoufox/gen-fingerprint-env.py +171 -0
  125. package/dist/scripts/camoufox/launch-auth.mjs +617 -0
  126. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.d.ts +3 -0
  127. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +138 -0
  128. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -0
  129. package/dist/server/runtime/http-server/daemon-admin/providers-handler.d.ts +3 -0
  130. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +166 -0
  131. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -0
  132. package/dist/server/runtime/http-server/daemon-admin/quota-handler.d.ts +3 -0
  133. package/dist/server/runtime/http-server/daemon-admin/quota-handler.js +109 -0
  134. package/dist/server/runtime/http-server/daemon-admin/quota-handler.js.map +1 -0
  135. package/dist/server/runtime/http-server/daemon-admin/status-handler.d.ts +3 -0
  136. package/dist/server/runtime/http-server/daemon-admin/status-handler.js +43 -0
  137. package/dist/server/runtime/http-server/daemon-admin/status-handler.js.map +1 -0
  138. package/dist/server/runtime/http-server/daemon-admin-routes.d.ts +19 -0
  139. package/dist/server/runtime/http-server/daemon-admin-routes.js +27 -0
  140. package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -0
  141. package/dist/server/runtime/http-server/executor-provider.d.ts +1 -0
  142. package/dist/server/runtime/http-server/executor-provider.js +26 -0
  143. package/dist/server/runtime/http-server/executor-provider.js.map +1 -1
  144. package/dist/server/runtime/http-server/executor-response.d.ts +16 -0
  145. package/dist/server/runtime/http-server/executor-response.js +164 -0
  146. package/dist/server/runtime/http-server/executor-response.js.map +1 -0
  147. package/dist/server/runtime/http-server/index.d.ts +6 -0
  148. package/dist/server/runtime/http-server/index.js +121 -53
  149. package/dist/server/runtime/http-server/index.js.map +1 -1
  150. package/dist/server/runtime/http-server/request-executor.d.ts +3 -0
  151. package/dist/server/runtime/http-server/request-executor.js +73 -21
  152. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  153. package/dist/server/runtime/http-server/routes.d.ts +5 -0
  154. package/dist/server/runtime/http-server/routes.js +45 -1
  155. package/dist/server/runtime/http-server/routes.js.map +1 -1
  156. package/dist/server/runtime/http-server/types.d.ts +1 -0
  157. package/dist/server/utils/client-connection-state.d.ts +8 -0
  158. package/dist/server/utils/client-connection-state.js +52 -0
  159. package/dist/server/utils/client-connection-state.js.map +1 -0
  160. package/dist/server/utils/request-id-manager.js +21 -3
  161. package/dist/server/utils/request-id-manager.js.map +1 -1
  162. package/dist/token-daemon/history-store.d.ts +2 -0
  163. package/dist/token-daemon/history-store.js +6 -2
  164. package/dist/token-daemon/history-store.js.map +1 -1
  165. package/dist/token-daemon/index.js +36 -5
  166. package/dist/token-daemon/index.js.map +1 -1
  167. package/dist/token-daemon/leader-lock.d.ts +11 -0
  168. package/dist/token-daemon/leader-lock.js +79 -0
  169. package/dist/token-daemon/leader-lock.js.map +1 -0
  170. package/dist/token-daemon/message-bus-integrator.d.ts +98 -0
  171. package/dist/token-daemon/message-bus-integrator.js +144 -0
  172. package/dist/token-daemon/message-bus-integrator.js.map +1 -0
  173. package/dist/token-daemon/provider-registry.d.ts +22 -0
  174. package/dist/token-daemon/provider-registry.js +201 -0
  175. package/dist/token-daemon/provider-registry.js.map +1 -0
  176. package/dist/token-daemon/token-daemon.d.ts +8 -0
  177. package/dist/token-daemon/token-daemon.js +196 -11
  178. package/dist/token-daemon/token-daemon.js.map +1 -1
  179. package/dist/token-portal/local-token-portal.d.ts +1 -0
  180. package/dist/token-portal/local-token-portal.js +18 -0
  181. package/dist/token-portal/local-token-portal.js.map +1 -1
  182. package/dist/token-portal/render.js +1 -0
  183. package/dist/token-portal/render.js.map +1 -1
  184. package/dist/tools/error-log.d.ts +31 -0
  185. package/dist/tools/error-log.js +117 -0
  186. package/dist/tools/error-log.js.map +1 -0
  187. package/dist/tools/stats-request-events.d.ts +2 -0
  188. package/dist/tools/stats-request-events.js +16 -0
  189. package/dist/tools/stats-request-events.js.map +1 -0
  190. package/dist/tools/stats-usage.d.ts +31 -0
  191. package/dist/tools/stats-usage.js +206 -0
  192. package/dist/tools/stats-usage.js.map +1 -0
  193. package/package.json +9 -4
  194. package/scripts/analyze-codex-error-failures.mjs +111 -0
  195. package/scripts/analyze-usage-estimate.mjs +240 -0
  196. package/scripts/camoufox/gen-fingerprint-env.py +171 -0
  197. package/scripts/camoufox/launch-auth.mjs +617 -0
  198. package/scripts/classify-codex-samples.mjs +251 -0
  199. package/scripts/cleanup-codex-error-samples.mjs +88 -0
  200. package/scripts/compare-codex-rccx.mjs +268 -0
  201. package/scripts/copy-compat-assets.mjs +18 -0
  202. package/scripts/install-release.sh +1 -1
  203. package/scripts/local-replay-openai-response.mjs +1 -2
  204. package/scripts/pack-mode.mjs +16 -6
  205. package/scripts/replay-codex-sample.mjs +24 -2
  206. package/scripts/responses-compare-server.mjs +119 -0
  207. package/scripts/tests/apply-patch-loop.mjs +266 -7
  208. package/scripts/tests/exec-command-loop.mjs +165 -0
  209. package/scripts/tool-classification-report.ts +281 -0
  210. package/scripts/verification/samples/openai-chat-list-local-files.json +1 -1
  211. package/scripts/verify-apply-patch.mjs +28 -17
  212. package/scripts/verify-codex-error-samples.mjs +102 -0
  213. package/scripts/verify-e2e-toolcall.mjs +71 -4
  214. package/scripts/virtual-router-shadow-v2-real.mjs +143 -0
  215. package/scripts/virtual-router-shadow-v2.mjs +122 -0
@@ -0,0 +1,164 @@
1
+ import { mapProviderProtocol, asRecord } from './provider-utils.js';
2
+ import { extractAnthropicToolAliasMap } from './anthropic-tool-alias.js';
3
+ import { convertProviderResponse as bridgeConvertProviderResponse, createSnapshotRecorder as bridgeCreateSnapshotRecorder } from '../../../modules/llmswitch/bridge.js';
4
+ import { applyClientConnectionStateToContext } from '../../utils/client-connection-state.js';
5
+ function extractClientModelId(metadata, originalRequest) {
6
+ const candidates = [
7
+ metadata.clientModelId,
8
+ metadata.originalModelId,
9
+ (metadata.target && typeof metadata.target === 'object'
10
+ ? metadata.target.clientModelId
11
+ : undefined),
12
+ originalRequest && typeof originalRequest === 'object'
13
+ ? originalRequest.model
14
+ : undefined,
15
+ originalRequest && typeof originalRequest === 'object'
16
+ ? originalRequest.originalModelId
17
+ : undefined
18
+ ];
19
+ for (const candidate of candidates) {
20
+ if (typeof candidate === 'string' && candidate.trim()) {
21
+ return candidate.trim();
22
+ }
23
+ }
24
+ return undefined;
25
+ }
26
+ export async function convertProviderResponseIfNeeded(options, deps) {
27
+ if (options.processMode === 'passthrough') {
28
+ return options.response;
29
+ }
30
+ const entry = (options.entryEndpoint || '').toLowerCase();
31
+ const needsAnthropicConversion = entry.includes('/v1/messages');
32
+ const needsResponsesConversion = entry.includes('/v1/responses');
33
+ const needsChatConversion = entry.includes('/v1/chat/completions');
34
+ if (!needsAnthropicConversion && !needsResponsesConversion && !needsChatConversion) {
35
+ return options.response;
36
+ }
37
+ const body = options.response.body;
38
+ if (!body || typeof body !== 'object') {
39
+ return options.response;
40
+ }
41
+ try {
42
+ const providerProtocol = mapProviderProtocol(options.providerType);
43
+ const metadataBag = asRecord(options.pipelineMetadata);
44
+ const aliasMap = extractAnthropicToolAliasMap(metadataBag);
45
+ const originalModelId = extractClientModelId(metadataBag ?? {}, options.originalRequest);
46
+ const baseContext = {
47
+ ...(metadataBag ?? {})
48
+ };
49
+ if (typeof metadataBag?.routeName === 'string') {
50
+ baseContext.routeId = metadataBag.routeName;
51
+ }
52
+ baseContext.requestId = options.requestId;
53
+ baseContext.entryEndpoint = options.entryEndpoint || entry;
54
+ baseContext.providerProtocol = providerProtocol;
55
+ baseContext.originalModelId = originalModelId;
56
+ applyClientConnectionStateToContext(metadataBag, baseContext);
57
+ const adapterContext = baseContext;
58
+ if (aliasMap) {
59
+ adapterContext.anthropicToolNameMap = aliasMap;
60
+ }
61
+ const stageRecorder = await bridgeCreateSnapshotRecorder(adapterContext, typeof adapterContext.entryEndpoint === 'string'
62
+ ? adapterContext.entryEndpoint
63
+ : entry);
64
+ const providerInvoker = async (invokeOptions) => {
65
+ if (invokeOptions.routeHint) {
66
+ const carrier = invokeOptions.payload;
67
+ const existingMeta = carrier.metadata && typeof carrier.metadata === 'object'
68
+ ? carrier.metadata
69
+ : {};
70
+ carrier.metadata = {
71
+ ...existingMeta,
72
+ routeHint: existingMeta.routeHint ?? invokeOptions.routeHint
73
+ };
74
+ }
75
+ const nestedInput = {
76
+ entryEndpoint: options.entryEndpoint || entry,
77
+ method: 'POST',
78
+ requestId: options.requestId,
79
+ headers: {},
80
+ query: {},
81
+ body: invokeOptions.payload,
82
+ metadata: {
83
+ ...(metadataBag ?? {}),
84
+ direction: 'request',
85
+ stage: 'provider.invoke'
86
+ }
87
+ };
88
+ const nestedResult = await deps.executeNested(nestedInput);
89
+ const nestedBody = nestedResult.body && typeof nestedResult.body === 'object'
90
+ ? nestedResult.body
91
+ : nestedResult;
92
+ return { providerResponse: nestedBody };
93
+ };
94
+ const reenterPipeline = async (reenterOpts) => {
95
+ const nestedEntry = reenterOpts.entryEndpoint || options.entryEndpoint || entry;
96
+ const nestedExtra = asRecord(reenterOpts.metadata) ?? {};
97
+ const nestedEntryLower = nestedEntry.toLowerCase();
98
+ const nestedMetadata = {
99
+ ...(metadataBag ?? {}),
100
+ ...nestedExtra,
101
+ entryEndpoint: nestedEntry,
102
+ direction: 'request',
103
+ stage: 'inbound'
104
+ };
105
+ if (nestedEntryLower.includes('/v1/chat/completions')) {
106
+ nestedMetadata.providerProtocol = 'openai-chat';
107
+ }
108
+ else if (nestedEntryLower.includes('/v1/responses')) {
109
+ nestedMetadata.providerProtocol = 'openai-responses';
110
+ }
111
+ else if (nestedEntryLower.includes('/v1/messages')) {
112
+ nestedMetadata.providerProtocol = 'anthropic-messages';
113
+ }
114
+ const nestedInput = {
115
+ entryEndpoint: nestedEntry,
116
+ method: 'POST',
117
+ requestId: reenterOpts.requestId,
118
+ headers: {},
119
+ query: {},
120
+ body: reenterOpts.body,
121
+ metadata: nestedMetadata
122
+ };
123
+ const nestedResult = await deps.executeNested(nestedInput);
124
+ const nestedBody = nestedResult.body && typeof nestedResult.body === 'object'
125
+ ? nestedResult.body
126
+ : undefined;
127
+ return { body: nestedBody };
128
+ };
129
+ const converted = await bridgeConvertProviderResponse({
130
+ providerProtocol,
131
+ providerResponse: body,
132
+ context: adapterContext,
133
+ entryEndpoint: options.entryEndpoint || entry,
134
+ wantsStream: options.wantsStream,
135
+ providerInvoker,
136
+ stageRecorder,
137
+ reenterPipeline
138
+ });
139
+ if (converted.__sse_responses) {
140
+ return {
141
+ ...options.response,
142
+ body: { __sse_responses: converted.__sse_responses }
143
+ };
144
+ }
145
+ return {
146
+ ...options.response,
147
+ body: converted.body ?? body
148
+ };
149
+ }
150
+ catch (error) {
151
+ const err = error;
152
+ const message = err instanceof Error ? err.message : String(err ?? 'Unknown error');
153
+ const providerProtocol = mapProviderProtocol(options.providerType);
154
+ const isSseConvertFailure = typeof message === 'string' &&
155
+ message.toLowerCase().includes('failed to convert sse payload');
156
+ if (providerProtocol === 'gemini-chat' && isSseConvertFailure) {
157
+ console.error('[RequestExecutor] Fatal SSE decode error for Gemini provider, bubbling as HTTP error', error);
158
+ throw error;
159
+ }
160
+ console.error('[RequestExecutor] Failed to convert provider response via llmswitch-core', error);
161
+ throw error;
162
+ }
163
+ }
164
+ //# sourceMappingURL=executor-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor-response.js","sourceRoot":"","sources":["../../../../src/server/runtime/http-server/executor-response.ts"],"names":[],"mappings":"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"}
@@ -37,9 +37,15 @@ export declare class RouteCodexHttpServer {
37
37
  private errorHandlingShim;
38
38
  private routeErrorHub;
39
39
  private readonly coloredLogger;
40
+ private managerDaemon;
40
41
  constructor(config: ServerConfigV2);
41
42
  private resolveVirtualRouterInput;
42
43
  private getModuleDependencies;
44
+ /**
45
+ * Register Daemon Admin UI route.
46
+ * Serves docs/daemon-admin-ui.html as a static page; localhost-only.
47
+ */
48
+ private registerDaemonAdminUiRoute;
43
49
  private getErrorHandlingShim;
44
50
  private createDebugCenterShim;
45
51
  private updateProviderProfiles;
@@ -15,7 +15,6 @@ import { attachProviderRuntimeMetadata } from '../../../providers/core/runtime/p
15
15
  import { extractAnthropicToolAliasMap } from './anthropic-tool-alias.js';
16
16
  import { AuthFileResolver } from '../../../config/auth-file-resolver.js';
17
17
  import { buildProviderProfiles } from '../../../providers/profile/provider-profile-loader.js';
18
- import { emitProviderError } from '../../../providers/core/utils/provider-error-reporter.js';
19
18
  import { isStageLoggingEnabled, logPipelineStage } from '../../utils/stage-logger.js';
20
19
  import { registerDefaultMiddleware } from './middleware.js';
21
20
  import { registerHttpRoutes, registerOAuthPortalRoute } from './routes.js';
@@ -28,6 +27,11 @@ import { writeClientSnapshot } from '../../../providers/core/utils/snapshot-writ
28
27
  import { createServerColoredLogger } from './colored-logger.js';
29
28
  import { formatValueForConsole } from '../../../utils/logger.js';
30
29
  import { QuietErrorHandlingCenter } from '../../../error-handling/quiet-error-handling-center.js';
30
+ import { hasVirtualRouterSeriesCooldown } from './executor-provider.js';
31
+ import { ManagerDaemon } from '../../../manager/index.js';
32
+ import { HealthManagerModule } from '../../../manager/modules/health/index.js';
33
+ import { RoutingStateManagerModule } from '../../../manager/modules/routing/index.js';
34
+ import { TokenManagerModule } from '../../../manager/modules/token/index.js';
31
35
  /**
32
36
  * RouteCodex Server V2
33
37
  *
@@ -56,6 +60,7 @@ export class RouteCodexHttpServer {
56
60
  errorHandlingShim = null;
57
61
  routeErrorHub = null;
58
62
  coloredLogger = createServerColoredLogger();
63
+ managerDaemon = null;
59
64
  constructor(config) {
60
65
  this.config = config;
61
66
  this.app = express();
@@ -77,6 +82,7 @@ export class RouteCodexHttpServer {
77
82
  // This ensures OAuth Portal is available when providers check token validity
78
83
  registerDefaultMiddleware(this.app);
79
84
  registerOAuthPortalRoute(this.app);
85
+ this.registerDaemonAdminUiRoute();
80
86
  console.log('[RouteCodexHttpServer] OAuth Portal route registered (early initialization)');
81
87
  console.log('[RouteCodexHttpServer] Initialized (pipeline=hub)');
82
88
  }
@@ -96,6 +102,35 @@ export class RouteCodexHttpServer {
96
102
  }
97
103
  return this.moduleDependencies;
98
104
  }
105
+ /**
106
+ * Register Daemon Admin UI route.
107
+ * Serves docs/daemon-admin-ui.html as a static page; localhost-only.
108
+ */
109
+ registerDaemonAdminUiRoute() {
110
+ this.app.get('/daemon/admin', async (req, res) => {
111
+ try {
112
+ const ip = req.socket?.remoteAddress || '';
113
+ const allowed = ip === '127.0.0.1' || ip === '::1' || ip === '::ffff:127.0.0.1';
114
+ if (!allowed) {
115
+ res.status(403).json({ error: { message: 'forbidden', code: 'forbidden' } });
116
+ return;
117
+ }
118
+ const filePath = new URL('../../../../docs/daemon-admin-ui.html', import.meta.url);
119
+ const fs = await import('node:fs/promises');
120
+ const html = await fs.readFile(filePath, 'utf8');
121
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
122
+ res.send(html);
123
+ }
124
+ catch (error) {
125
+ const message = error instanceof Error ? error.message : String(error);
126
+ res.status(500).json({
127
+ error: {
128
+ message: `Daemon admin UI not available: ${message}`
129
+ }
130
+ });
131
+ }
132
+ });
133
+ }
99
134
  getErrorHandlingShim() {
100
135
  if (!this.errorHandlingShim) {
101
136
  this.errorHandlingShim = {
@@ -307,6 +342,26 @@ export class RouteCodexHttpServer {
307
342
  // 初始化错误处理
308
343
  await this.errorHandling.initialize();
309
344
  await this.initializeRouteErrorHub();
345
+ // 初始化 ManagerDaemon 骨架(当前模块为占位实现,不改变行为)
346
+ if (!this.managerDaemon) {
347
+ const serverId = `${this.config.server.host}:${this.config.server.port}`;
348
+ const daemon = new ManagerDaemon({ serverId });
349
+ daemon.registerModule(new TokenManagerModule());
350
+ daemon.registerModule(new RoutingStateManagerModule());
351
+ daemon.registerModule(new HealthManagerModule());
352
+ // Quota manager(当前仅用于 antigravity/gemini-cli 等需要配额信息的 Provider)
353
+ try {
354
+ const mod = (await import('../../../manager/modules/quota/index.js'));
355
+ if (typeof mod.QuotaManagerModule === 'function') {
356
+ daemon.registerModule(new mod.QuotaManagerModule());
357
+ }
358
+ }
359
+ catch {
360
+ // 可选模块,缺失时忽略
361
+ }
362
+ await daemon.start();
363
+ this.managerDaemon = daemon;
364
+ }
310
365
  // registerDefaultMiddleware and registerOAuthPortalRoute already called in constructor
311
366
  // Register remaining HTTP routes
312
367
  registerHttpRoutes({
@@ -314,7 +369,23 @@ export class RouteCodexHttpServer {
314
369
  config: this.config,
315
370
  buildHandlerContext: () => this.buildHandlerContext(),
316
371
  getPipelineReady: () => this.isPipelineReady(),
317
- handleError: (error, context) => this.handleError(error, context)
372
+ handleError: (error, context) => this.handleError(error, context),
373
+ getHealthSnapshot: () => {
374
+ const healthModule = this.managerDaemon?.getModule('health');
375
+ return healthModule?.getCurrentSnapshot() ?? null;
376
+ },
377
+ getRoutingState: (sessionId) => {
378
+ const routingModule = this.managerDaemon?.getModule('routing');
379
+ const store = routingModule?.getRoutingStateStore();
380
+ if (!store) {
381
+ return null;
382
+ }
383
+ const key = sessionId && sessionId.trim() ? `session:${sessionId.trim()}` : '';
384
+ return key ? store.loadSync(key) : null;
385
+ },
386
+ getManagerDaemon: () => this.managerDaemon,
387
+ getVirtualRouterArtifacts: () => this.currentRouterArtifacts,
388
+ getServerId: () => `${this.config.server.host}:${this.config.server.port}`
318
389
  });
319
390
  this._isInitialized = true;
320
391
  console.log('[RouteCodexHttpServer] Initialization completed successfully');
@@ -355,6 +426,15 @@ export class RouteCodexHttpServer {
355
426
  await this.disposeProviders();
356
427
  }
357
428
  catch { /* ignore */ }
429
+ try {
430
+ if (this.managerDaemon) {
431
+ await this.managerDaemon.stop();
432
+ this.managerDaemon = null;
433
+ }
434
+ }
435
+ catch {
436
+ // ignore manager shutdown failures
437
+ }
358
438
  await this.errorHandling.destroy();
359
439
  console.log('[RouteCodexHttpServer] Server stopped');
360
440
  resolve();
@@ -433,8 +513,21 @@ export class RouteCodexHttpServer {
433
513
  const bootstrapArtifacts = await this.bootstrapVirtualRouter(routerInput);
434
514
  this.currentRouterArtifacts = bootstrapArtifacts;
435
515
  const hubCtor = await this.ensureHubPipelineCtor();
516
+ const hubConfig = {
517
+ virtualRouter: bootstrapArtifacts.config
518
+ };
519
+ const healthModule = this.managerDaemon?.getModule('health');
520
+ const healthStore = healthModule?.getHealthStore();
521
+ if (healthStore) {
522
+ hubConfig.healthStore = healthStore;
523
+ }
524
+ const routingModule = this.managerDaemon?.getModule('routing');
525
+ const routingStateStore = routingModule?.getRoutingStateStore();
526
+ if (routingStateStore) {
527
+ hubConfig.routingStateStore = routingStateStore;
528
+ }
436
529
  if (!this.hubPipeline) {
437
- this.hubPipeline = new hubCtor({ virtualRouter: bootstrapArtifacts.config });
530
+ this.hubPipeline = new hubCtor(hubConfig);
438
531
  }
439
532
  else {
440
533
  this.hubPipeline.updateVirtualRouterConfig(bootstrapArtifacts.config);
@@ -512,6 +605,7 @@ export class RouteCodexHttpServer {
512
605
  const auth = runtime.auth || { type: 'apikey' };
513
606
  const authRecord = auth;
514
607
  const authType = this.normalizeAuthType(auth.type);
608
+ const rawType = typeof auth.rawType === 'string' ? auth.rawType.trim().toLowerCase() : '';
515
609
  const pickString = (...candidates) => {
516
610
  for (const candidate of candidates) {
517
611
  if (typeof candidate === 'string') {
@@ -543,6 +637,9 @@ export class RouteCodexHttpServer {
543
637
  return undefined;
544
638
  };
545
639
  if (authType === 'apikey') {
640
+ if (rawType === 'iflow-cookie') {
641
+ return { ...auth, type: 'apikey', rawType: auth.rawType ?? 'iflow-cookie' };
642
+ }
546
643
  const value = await this.resolveApiKeyValue(runtime, auth);
547
644
  return { ...auth, type: 'apikey', value };
548
645
  }
@@ -630,8 +727,9 @@ export class RouteCodexHttpServer {
630
727
  const pipelineLabel = 'hub';
631
728
  let iterationMetadata = initialMetadata;
632
729
  let followupTriggered = false;
633
- const maxAttemptsRaw = process.env.ROUTECODEX_PROVIDER_RETRY_MAX || process.env.RCC_PROVIDER_RETRY_MAX;
634
- const maxAttempts = Math.max(1, Number.isFinite(Number(maxAttemptsRaw)) ? Number(maxAttemptsRaw) || 3 : 3);
730
+ // Provider 级别不再在单个 HTTP 请求内执行重复尝试,
731
+ // 429/配额/熔断逻辑统一交由 llmswitch-core VirtualRouter 处理。
732
+ const maxAttempts = 1;
635
733
  let attempt = 0;
636
734
  const originalBodySnapshot = this.cloneRequestPayload(input.body);
637
735
  const excludedProviderKeys = new Set();
@@ -782,53 +880,6 @@ export class RouteCodexHttpServer {
782
880
  model: providerModel,
783
881
  providerLabel
784
882
  });
785
- const runtimeMetadata = {
786
- requestId: input.requestId,
787
- providerKey: target.providerKey,
788
- providerId: handle.providerId,
789
- providerType: handle.providerType,
790
- providerProtocol,
791
- routeName: pipelineResult.routingDecision?.routeName,
792
- pipelineId: target.providerKey,
793
- runtimeKey,
794
- target
795
- };
796
- runtimeMetadata.providerFamily = handle.providerFamily;
797
- emitProviderError({
798
- error,
799
- stage: 'provider.send',
800
- runtime: runtimeMetadata,
801
- dependencies: this.getModuleDependencies()
802
- });
803
- const err = error;
804
- const statusCode = typeof err?.status === 'number'
805
- ? err.status
806
- : typeof err?.statusCode === 'number'
807
- ? err.statusCode
808
- : typeof err?.response?.status === 'number'
809
- ? err.response.status
810
- : undefined;
811
- const retryable = err?.retryable === true;
812
- // 对可恢复的 429 错误在同一 HTTP 请求内执行透明重试:
813
- // - 依赖 providerErrorCenter 已更新 VirtualRouter 的健康状态(短冷静期);
814
- // - 将当前 providerKey 记录到本次请求的排除列表中,下一轮选路时显式跳过;
815
- // - 再次调用 HubPipeline 时,让 VirtualRouter 在最新健康状态 + 排除集下选择其它可用 key;
816
- // - 若超过最大重试次数或错误不可恢复,则将错误抛出,由上层 HTTP 处理。
817
- if (retryable && statusCode === 429 && attempt < maxAttempts) {
818
- this.logStage('provider.send.retry', input.requestId, {
819
- providerKey: target.providerKey,
820
- attempt,
821
- status: statusCode
822
- });
823
- // 本次请求内,将当前 providerKey 标记为已尝试,下一轮路由会自动跳过。
824
- if (typeof target.providerKey === 'string' && target.providerKey.trim()) {
825
- excludedProviderKeys.add(target.providerKey.trim());
826
- }
827
- // 下一轮沿用当前 pipeline metadata(包含路由决策等),让 VirtualRouter
828
- // 在最新健康状态下重新选择 provider。
829
- iterationMetadata = { ...mergedMetadata };
830
- continue;
831
- }
832
883
  throw error;
833
884
  }
834
885
  }
@@ -880,7 +931,7 @@ export class RouteCodexHttpServer {
880
931
  : inboundOriginator;
881
932
  const routeHint = this.extractRouteHint(input) ?? userMeta.routeHint;
882
933
  const processMode = userMeta.processMode || 'chat';
883
- return {
934
+ const metadata = {
884
935
  ...userMeta,
885
936
  entryEndpoint: input.entryEndpoint,
886
937
  processMode,
@@ -891,6 +942,23 @@ export class RouteCodexHttpServer {
891
942
  ...(resolvedUserAgent ? { userAgent: resolvedUserAgent } : {}),
892
943
  ...(resolvedOriginator ? { clientOriginator: resolvedOriginator } : {})
893
944
  };
945
+ // 在 Host 入口统一解析会话标识,后续 HubPipeline / servertool 等模块仅依赖
946
+ // sessionId / conversationId 字段,不再重复解析 clientHeaders。
947
+ try {
948
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
949
+ const { extractSessionIdentifiersFromMetadata } = require('../../../../sharedmodule/llmswitch-core/dist/conversion/hub/pipeline/session-identifiers.js');
950
+ const identifiers = extractSessionIdentifiersFromMetadata(metadata);
951
+ if (identifiers.sessionId) {
952
+ metadata.sessionId = identifiers.sessionId;
953
+ }
954
+ if (identifiers.conversationId) {
955
+ metadata.conversationId = identifiers.conversationId;
956
+ }
957
+ }
958
+ catch {
959
+ // best-effort:解析失败时不影响主流程
960
+ }
961
+ return metadata;
894
962
  }
895
963
  extractHeaderValue(headers, name) {
896
964
  if (!headers) {