@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.
- package/README.md +27 -0
- package/dist/build-info.js +2 -2
- package/dist/build-info.js.map +1 -1
- package/dist/cli/commands/claude.js +1 -0
- package/dist/cli/commands/claude.js.map +1 -1
- package/dist/cli/commands/codex.js +1 -0
- package/dist/cli/commands/codex.js.map +1 -1
- package/dist/cli/commands/guardian-daemon.d.ts +2 -0
- package/dist/cli/commands/guardian-daemon.js +299 -0
- package/dist/cli/commands/guardian-daemon.js.map +1 -0
- package/dist/cli/commands/init/camoufox.js +1 -1
- package/dist/cli/commands/init/camoufox.js.map +1 -1
- package/dist/cli/commands/launcher/types.d.ts +6 -0
- package/dist/cli/commands/launcher-kernel.js +456 -109
- package/dist/cli/commands/launcher-kernel.js.map +1 -1
- package/dist/cli/commands/port.js +28 -8
- package/dist/cli/commands/port.js.map +1 -1
- package/dist/cli/commands/restart.d.ts +4 -0
- package/dist/cli/commands/restart.js +91 -42
- package/dist/cli/commands/restart.js.map +1 -1
- package/dist/cli/commands/start-types.d.ts +4 -0
- package/dist/cli/commands/start.js +108 -65
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/stop.d.ts +3 -0
- package/dist/cli/commands/stop.js +30 -63
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/config/init-provider-catalog.js +8 -3
- package/dist/cli/config/init-provider-catalog.js.map +1 -1
- package/dist/cli/guardian/client.d.ts +38 -0
- package/dist/cli/guardian/client.js +237 -0
- package/dist/cli/guardian/client.js.map +1 -0
- package/dist/cli/guardian/paths.d.ts +7 -0
- package/dist/cli/guardian/paths.js +13 -0
- package/dist/cli/guardian/paths.js.map +1 -0
- package/dist/cli/guardian/types.d.ts +30 -0
- package/dist/cli/guardian/types.js +2 -0
- package/dist/cli/guardian/types.js.map +1 -0
- package/dist/cli/register/guardian-daemon-command.d.ts +2 -0
- package/dist/cli/register/guardian-daemon-command.js +5 -0
- package/dist/cli/register/guardian-daemon-command.js.map +1 -0
- package/dist/cli/server/port-utils.js +57 -1
- package/dist/cli/server/port-utils.js.map +1 -1
- package/dist/cli.js +48 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/oauth.js +6 -6
- package/dist/commands/oauth.js.map +1 -1
- package/dist/config/routecodex-config-loader.js +66 -1
- package/dist/config/routecodex-config-loader.js.map +1 -1
- package/dist/config/virtual-router-builder.js +18 -0
- package/dist/config/virtual-router-builder.js.map +1 -1
- package/dist/config/virtual-router-types.js +20 -5
- package/dist/config/virtual-router-types.js.map +1 -1
- package/dist/daemon-admin-ui/assets/index-C8vP_c5E.js +15 -0
- package/dist/daemon-admin-ui/assets/index-DjIoHmNv.css +1 -0
- package/dist/daemon-admin-ui/index.html +13 -0
- package/dist/docs/daemon-admin-ui.html +328 -57
- package/dist/index.d.ts +9 -0
- package/dist/index.js +268 -10
- package/dist/index.js.map +1 -1
- package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.d.ts +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.js +36 -0
- package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.js.map +1 -1
- package/dist/manager/modules/quota/provider-quota-daemon.events.js +50 -1
- package/dist/manager/modules/quota/provider-quota-daemon.events.js.map +1 -1
- package/dist/providers/auth/antigravity-user-agent.js +78 -31
- package/dist/providers/auth/antigravity-user-agent.js.map +1 -1
- package/dist/providers/auth/gemini-cli-userinfo-helper.js +94 -63
- package/dist/providers/auth/gemini-cli-userinfo-helper.js.map +1 -1
- package/dist/providers/auth/iflow-userinfo-helper.js +1 -1
- package/dist/providers/auth/iflow-userinfo-helper.js.map +1 -1
- package/dist/providers/auth/oauth-error-message.d.ts +1 -0
- package/dist/providers/auth/oauth-error-message.js +44 -0
- package/dist/providers/auth/oauth-error-message.js.map +1 -0
- package/dist/providers/auth/oauth-lifecycle/error-detection.js +42 -8
- package/dist/providers/auth/oauth-lifecycle/error-detection.js.map +1 -1
- package/dist/providers/auth/oauth-lifecycle/token-io.d.ts +1 -0
- package/dist/providers/auth/oauth-lifecycle/token-io.js +12 -0
- package/dist/providers/auth/oauth-lifecycle/token-io.js.map +1 -1
- package/dist/providers/auth/oauth-lifecycle.js +502 -87
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/auth/oauth-repair-cooldown.js +2 -7
- package/dist/providers/auth/oauth-repair-cooldown.js.map +1 -1
- package/dist/providers/auth/oauth-repair-env.js +3 -5
- package/dist/providers/auth/oauth-repair-env.js.map +1 -1
- package/dist/providers/auth/oauth-utils/error-extraction.js +42 -8
- package/dist/providers/auth/oauth-utils/error-extraction.js.map +1 -1
- package/dist/providers/core/config/camoufox-actions.d.ts +31 -0
- package/dist/providers/core/config/camoufox-actions.js +461 -0
- package/dist/providers/core/config/camoufox-actions.js.map +1 -0
- package/dist/providers/core/config/camoufox-launcher.d.ts +3 -0
- package/dist/providers/core/config/camoufox-launcher.js +518 -160
- package/dist/providers/core/config/camoufox-launcher.js.map +1 -1
- package/dist/providers/core/config/oauth-flows.js +6 -44
- package/dist/providers/core/config/oauth-flows.js.map +1 -1
- package/dist/providers/core/config/provider-oauth-configs.js +51 -7
- package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
- package/dist/providers/core/runtime/provider-error-classifier.js +32 -15
- package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
- package/dist/providers/core/runtime/provider-family-profile-utils.js +1 -1
- package/dist/providers/core/runtime/provider-family-profile-utils.js.map +1 -1
- package/dist/providers/core/runtime/provider-response-postprocessor.js +61 -14
- package/dist/providers/core/runtime/provider-response-postprocessor.js.map +1 -1
- package/dist/providers/core/strategies/oauth-auth-code-flow.d.ts +1 -0
- package/dist/providers/core/strategies/oauth-auth-code-flow.js +124 -19
- package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
- package/dist/providers/core/strategies/oauth-device-flow.js +6 -3
- package/dist/providers/core/strategies/oauth-device-flow.js.map +1 -1
- package/dist/providers/profile/families/iflow-profile.js +83 -10
- package/dist/providers/profile/families/iflow-profile.js.map +1 -1
- package/dist/scripts/camoufox/launch-auth.mjs +112 -5
- package/dist/server/handlers/config-admin-handler.js +9 -2
- package/dist/server/handlers/config-admin-handler.js.map +1 -1
- package/dist/server/handlers/handler-utils.js +3 -12
- package/dist/server/handlers/handler-utils.js.map +1 -1
- package/dist/server/handlers/logging.js +3 -4
- package/dist/server/handlers/logging.js.map +1 -1
- package/dist/server/runtime/http-server/clock-client-reaper.js +3 -26
- package/dist/server/runtime/http-server/clock-client-reaper.js.map +1 -1
- package/dist/server/runtime/http-server/clock-client-registry-utils.d.ts +4 -0
- package/dist/server/runtime/http-server/clock-client-registry-utils.js +74 -16
- package/dist/server/runtime/http-server/clock-client-registry-utils.js.map +1 -1
- package/dist/server/runtime/http-server/clock-client-registry.d.ts +15 -0
- package/dist/server/runtime/http-server/clock-client-registry.js +300 -6
- package/dist/server/runtime/http-server/clock-client-registry.js.map +1 -1
- package/dist/server/runtime/http-server/clock-client-routes.js +49 -19
- package/dist/server/runtime/http-server/clock-client-routes.js.map +1 -1
- package/dist/server/runtime/http-server/clock-daemon-log-throttle.d.ts +16 -0
- package/dist/server/runtime/http-server/clock-daemon-log-throttle.js +49 -0
- package/dist/server/runtime/http-server/clock-daemon-log-throttle.js.map +1 -1
- package/dist/server/runtime/http-server/clock-scope-resolution.d.ts +14 -0
- package/dist/server/runtime/http-server/clock-scope-resolution.js +212 -0
- package/dist/server/runtime/http-server/clock-scope-resolution.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js +5 -3
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/control-handler.js +104 -15
- package/dist/server/runtime/http-server/daemon-admin/control-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +2 -2
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.d.ts +24 -0
- package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.js +316 -70
- package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +190 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/routing-policy.js +18 -29
- package/dist/server/runtime/http-server/daemon-admin/routing-policy.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js +2 -0
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin-routes.d.ts +8 -1
- package/dist/server/runtime/http-server/daemon-admin-routes.js +30 -0
- package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
- package/dist/server/runtime/http-server/executor/client-injection-flow.d.ts +14 -0
- package/dist/server/runtime/http-server/executor/client-injection-flow.js +287 -0
- package/dist/server/runtime/http-server/executor/client-injection-flow.js.map +1 -0
- package/dist/server/runtime/http-server/executor/index.d.ts +1 -1
- package/dist/server/runtime/http-server/executor/index.js +1 -1
- package/dist/server/runtime/http-server/executor/index.js.map +1 -1
- package/dist/server/runtime/http-server/executor/provider-response-converter.js +236 -62
- package/dist/server/runtime/http-server/executor/provider-response-converter.js.map +1 -1
- package/dist/server/runtime/http-server/executor/request-executor-core-utils.d.ts +1 -0
- package/dist/server/runtime/http-server/executor/request-executor-core-utils.js +12 -0
- package/dist/server/runtime/http-server/executor/request-executor-core-utils.js.map +1 -1
- package/dist/server/runtime/http-server/executor/request-retry-helpers.js +16 -12
- package/dist/server/runtime/http-server/executor/request-retry-helpers.js.map +1 -1
- package/dist/server/runtime/http-server/executor/sse-error-handler.d.ts +1 -0
- package/dist/server/runtime/http-server/executor/sse-error-handler.js +13 -2
- package/dist/server/runtime/http-server/executor/sse-error-handler.js.map +1 -1
- package/dist/server/runtime/http-server/executor/usage-aggregator.d.ts +0 -12
- package/dist/server/runtime/http-server/executor/usage-aggregator.js +84 -88
- package/dist/server/runtime/http-server/executor/usage-aggregator.js.map +1 -1
- package/dist/server/runtime/http-server/executor-metadata.js +328 -7
- package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
- package/dist/server/runtime/http-server/executor-response.d.ts +1 -0
- package/dist/server/runtime/http-server/executor-response.js +52 -58
- package/dist/server/runtime/http-server/executor-response.js.map +1 -1
- package/dist/server/runtime/http-server/http-server-bootstrap.js +50 -6
- package/dist/server/runtime/http-server/http-server-bootstrap.js.map +1 -1
- package/dist/server/runtime/http-server/http-server-clock-daemon.d.ts +1 -0
- package/dist/server/runtime/http-server/http-server-clock-daemon.js +186 -44
- package/dist/server/runtime/http-server/http-server-clock-daemon.js.map +1 -1
- package/dist/server/runtime/http-server/http-server-lifecycle.js +4 -4
- package/dist/server/runtime/http-server/http-server-lifecycle.js.map +1 -1
- package/dist/server/runtime/http-server/hub-shadow-compare.js +1 -1
- package/dist/server/runtime/http-server/hub-shadow-compare.js.map +1 -1
- package/dist/server/runtime/http-server/index.d.ts +1 -0
- package/dist/server/runtime/http-server/index.js +1 -0
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/middleware.js +82 -4
- package/dist/server/runtime/http-server/middleware.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.js +6 -5
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.d.ts +2 -1
- package/dist/server/runtime/http-server/routes.js +4 -2
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/session-dir.js +12 -1
- package/dist/server/runtime/http-server/session-dir.js.map +1 -1
- package/dist/server/runtime/http-server/stats-manager.d.ts +35 -0
- package/dist/server/runtime/http-server/stats-manager.js +269 -21
- package/dist/server/runtime/http-server/stats-manager.js.map +1 -1
- package/dist/server/runtime/http-server/stopmessage-scope-rebind.d.ts +13 -0
- package/dist/server/runtime/http-server/stopmessage-scope-rebind.js +168 -0
- package/dist/server/runtime/http-server/stopmessage-scope-rebind.js.map +1 -0
- package/dist/server/runtime/http-server/tmux-session-probe.d.ts +10 -0
- package/dist/server/runtime/http-server/tmux-session-probe.js +97 -0
- package/dist/server/runtime/http-server/tmux-session-probe.js.map +1 -1
- package/dist/server-lifecycle/port-utils.d.ts +2 -1
- package/dist/server-lifecycle/port-utils.js +84 -4
- package/dist/server-lifecycle/port-utils.js.map +1 -1
- package/dist/token-daemon/index.d.ts +1 -0
- package/dist/token-daemon/index.js +17 -12
- package/dist/token-daemon/index.js.map +1 -1
- package/dist/utils/clock-client-token.d.ts +2 -1
- package/dist/utils/clock-client-token.js +52 -8
- package/dist/utils/clock-client-token.js.map +1 -1
- package/dist/utils/clock-scope-trace.d.ts +11 -0
- package/dist/utils/clock-scope-trace.js +41 -0
- package/dist/utils/clock-scope-trace.js.map +1 -0
- package/dist/utils/llms-engine-shadow.js +1 -1
- package/dist/utils/llms-engine-shadow.js.map +1 -1
- package/docs/DAEMON_CONTROL_PLANE.md +1 -0
- package/docs/ROUTING_POLICY_SCHEMA.md +4 -2
- package/docs/daemon-admin-ui.html +328 -57
- package/docs/design/servertool-stopmessage-lifecycle.md +109 -0
- package/docs/exec-command-guard-policy.example.v1.json +7 -1
- package/docs/providers/antigravity-gemini-provider-compat.md +2 -2
- package/package.json +21 -5
- package/scripts/build-core.mjs +12 -0
- package/scripts/camoufox/launch-auth.mjs +112 -5
- package/scripts/ci/repo-sanity.mjs +1 -0
- package/scripts/install-global.sh +6 -0
- package/scripts/install-verify.mjs +33 -16
- package/scripts/run-bg.sh +226 -43
- package/scripts/run-fg-gtimeout.sh +158 -14
- package/scripts/tests/blackbox-rcc-vs-routecodex-antigravity.mjs +3 -3
- package/scripts/tests/ci-jest.mjs +9 -1
- package/scripts/triage-errorsamples.mjs +216 -0
- package/scripts/verify-codex-error-samples.mjs +92 -15
- package/scripts/verify-install-e2e.mjs +57 -27
|
@@ -5,7 +5,15 @@ import path from 'node:path';
|
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { logOAuthDebug } from '../../auth/oauth-logger.js';
|
|
8
|
+
import { CAMO_CLICK_TARGETS, clickCamoGoogleAccountByHint, hasCamoGoogleSignInPrompt, clickCamoGoogleSignInBySelector, clickCamoTarget, ensureCamoProfile, getActiveCamoPageUrl, gotoCamoUrl, startCamoSession, setDefaultCamoProfile } from './camoufox-actions.js';
|
|
8
9
|
const activeLaunchers = new Set();
|
|
10
|
+
let lastCamoufoxLaunchFailureReason = null;
|
|
11
|
+
function setCamoufoxLaunchFailureReason(reason) {
|
|
12
|
+
lastCamoufoxLaunchFailureReason = reason && reason.trim().length > 0 ? reason.trim() : null;
|
|
13
|
+
}
|
|
14
|
+
export function getLastCamoufoxLaunchFailureReason() {
|
|
15
|
+
return lastCamoufoxLaunchFailureReason;
|
|
16
|
+
}
|
|
9
17
|
function registerLauncher(child) {
|
|
10
18
|
const handle = { child };
|
|
11
19
|
activeLaunchers.add(handle);
|
|
@@ -160,6 +168,47 @@ function getProviderFamily(provider) {
|
|
|
160
168
|
}
|
|
161
169
|
return rawProvider;
|
|
162
170
|
}
|
|
171
|
+
function isDisabledFlag(value) {
|
|
172
|
+
const raw = String(value || '').trim().toLowerCase();
|
|
173
|
+
return raw === '0' || raw === 'false' || raw === 'no' || raw === 'off';
|
|
174
|
+
}
|
|
175
|
+
export function shouldPreferCamoCliForOAuth(provider) {
|
|
176
|
+
if (isDisabledFlag(process.env.ROUTECODEX_OAUTH_CAMO_CLI) || isDisabledFlag(process.env.RCC_OAUTH_CAMO_CLI)) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
void provider;
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
function resolveCamoCliCommand() {
|
|
183
|
+
const configured = String(process.env.ROUTECODEX_CAMO_CLI_PATH || process.env.RCC_CAMO_CLI_PATH || '').trim();
|
|
184
|
+
return configured || 'camo';
|
|
185
|
+
}
|
|
186
|
+
function runCamoCliCheck() {
|
|
187
|
+
try {
|
|
188
|
+
const result = spawnSync(resolveCamoCliCommand(), ['--help'], {
|
|
189
|
+
stdio: 'ignore'
|
|
190
|
+
});
|
|
191
|
+
const code = result.error?.code;
|
|
192
|
+
if (code === 'ENOENT') {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
return result.status === 0;
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
export function shouldRepairCamoufoxFingerprintForOAuth(providerFamily, navigatorPlatform, hostPlatform = process.platform) {
|
|
202
|
+
const family = String(providerFamily || '').trim().toLowerCase();
|
|
203
|
+
const platform = String(navigatorPlatform || '').trim().toLowerCase();
|
|
204
|
+
if (hostPlatform !== 'darwin') {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
if (platform !== 'win32') {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
return family === 'gemini' || family === 'iflow' || family === 'qwen';
|
|
211
|
+
}
|
|
163
212
|
export function sanitizeCamouConfigForOAuth(provider, fingerprintEnv) {
|
|
164
213
|
const env = { ...(fingerprintEnv || {}) };
|
|
165
214
|
const raw = env.CAMOU_CONFIG_1;
|
|
@@ -180,8 +229,12 @@ export function sanitizeCamouConfigForOAuth(provider, fingerprintEnv) {
|
|
|
180
229
|
return env;
|
|
181
230
|
}
|
|
182
231
|
const family = getProviderFamily(provider);
|
|
183
|
-
if (family === 'iflow'
|
|
184
|
-
|
|
232
|
+
if (family === 'iflow') {
|
|
233
|
+
// iFlow OAuth 页面在部分指纹下会因 header 覆盖导致页面乱码;OAuth 场景统一移除该覆盖。
|
|
234
|
+
delete parsed['headers.Accept-Encoding'];
|
|
235
|
+
if (Object.prototype.hasOwnProperty.call(parsed, 'timezone')) {
|
|
236
|
+
delete parsed.timezone;
|
|
237
|
+
}
|
|
185
238
|
env.CAMOU_CONFIG_1 = JSON.stringify(parsed);
|
|
186
239
|
}
|
|
187
240
|
return env;
|
|
@@ -317,7 +370,7 @@ function runCamoufoxPathCheck() {
|
|
|
317
370
|
return execution.result?.status === 0;
|
|
318
371
|
}
|
|
319
372
|
export function isCamoufoxAvailable() {
|
|
320
|
-
return
|
|
373
|
+
return runCamoCliCheck();
|
|
321
374
|
}
|
|
322
375
|
function installCamoufox() {
|
|
323
376
|
const execution = runPythonWithLaunchers((launcher) => [...launcher.argsPrefix, '-m', 'pip', 'install', '--user', '-U', 'camoufox'], { stdio: 'inherit' });
|
|
@@ -331,31 +384,11 @@ function installCamoufox() {
|
|
|
331
384
|
return execution.result.status === 0;
|
|
332
385
|
}
|
|
333
386
|
function ensureCamoufoxInstalled() {
|
|
334
|
-
if (
|
|
387
|
+
if (runCamoCliCheck()) {
|
|
335
388
|
return true;
|
|
336
389
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
'1')
|
|
340
|
-
.trim()
|
|
341
|
-
.toLowerCase();
|
|
342
|
-
const autoInstallEnabled = autoInstallRaw !== '0' && autoInstallRaw !== 'false' && autoInstallRaw !== 'no';
|
|
343
|
-
if (!autoInstallEnabled) {
|
|
344
|
-
logOAuthDebug('[OAuth] Camoufox: auto-install disabled; camoufox not available');
|
|
345
|
-
return false;
|
|
346
|
-
}
|
|
347
|
-
console.warn('[OAuth] Camoufox not detected. Installing via Python launcher ...');
|
|
348
|
-
logOAuthDebug('[OAuth] Camoufox: python module not available, attempting install...');
|
|
349
|
-
const installed = installCamoufox();
|
|
350
|
-
if (!installed) {
|
|
351
|
-
logOAuthDebug('[OAuth] Camoufox: auto-install failed or Python launcher unavailable');
|
|
352
|
-
return false;
|
|
353
|
-
}
|
|
354
|
-
const ready = runCamoufoxPathCheck();
|
|
355
|
-
if (!ready) {
|
|
356
|
-
logOAuthDebug('[OAuth] Camoufox: install completed but camoufox path still unresolved');
|
|
357
|
-
}
|
|
358
|
-
return ready;
|
|
390
|
+
logOAuthDebug('[OAuth] camo-cli not available');
|
|
391
|
+
return false;
|
|
359
392
|
}
|
|
360
393
|
export function ensureCamoufoxInstalledForInit() {
|
|
361
394
|
return ensureCamoufoxInstalled();
|
|
@@ -488,8 +521,9 @@ function ensureFingerprintEnv(profileId, provider, alias) {
|
|
|
488
521
|
}
|
|
489
522
|
const scriptPath = resolveGenFingerprintScriptPath();
|
|
490
523
|
if (!scriptPath) {
|
|
491
|
-
logOAuthDebug('[OAuth] Camoufox: fingerprint generator script not found;
|
|
492
|
-
|
|
524
|
+
logOAuthDebug('[OAuth] Camoufox: fingerprint generator script not found; creating minimal fingerprint env');
|
|
525
|
+
const osPolicy = computeOsPolicy(provider, alias);
|
|
526
|
+
return writeMinimalFingerprintEnv(profileId, osPolicy);
|
|
493
527
|
}
|
|
494
528
|
const osPolicy = computeOsPolicy(provider, alias);
|
|
495
529
|
const fpRoot = getFingerprintRoot();
|
|
@@ -545,163 +579,487 @@ export function ensureCamoufoxFingerprintForToken(provider, alias) {
|
|
|
545
579
|
// fingerprint generation failures are non-fatal for token scanning
|
|
546
580
|
}
|
|
547
581
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
582
|
+
function isTruthyFlag(value) {
|
|
583
|
+
const raw = String(value || '').trim().toLowerCase();
|
|
584
|
+
return raw === '1' || raw === 'true' || raw === 'yes' || raw === 'on';
|
|
585
|
+
}
|
|
586
|
+
function parsePositiveInt(value, fallbackValue) {
|
|
587
|
+
const parsed = Number.parseInt(String(value || '').trim(), 10);
|
|
588
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallbackValue;
|
|
589
|
+
}
|
|
590
|
+
function isTokenPortalUrl(rawUrl) {
|
|
591
|
+
if (!rawUrl || typeof rawUrl !== 'string') {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
try {
|
|
595
|
+
const parsed = new URL(rawUrl);
|
|
596
|
+
const pathName = parsed.pathname || '';
|
|
597
|
+
return pathName === '/token-auth/demo' || pathName.startsWith('/token-auth/demo/');
|
|
598
|
+
}
|
|
599
|
+
catch {
|
|
600
|
+
return rawUrl.includes('/token-auth/demo');
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function resolvePortalOauthUrl(rawUrl) {
|
|
604
|
+
if (!isTokenPortalUrl(rawUrl)) {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
try {
|
|
608
|
+
const parsed = new URL(rawUrl);
|
|
609
|
+
const oauthUrl = parsed.searchParams.get('oauthUrl');
|
|
610
|
+
if (!oauthUrl || typeof oauthUrl !== 'string') {
|
|
611
|
+
return null;
|
|
556
612
|
}
|
|
557
|
-
|
|
613
|
+
const normalized = oauthUrl.trim();
|
|
614
|
+
return normalized || null;
|
|
615
|
+
}
|
|
616
|
+
catch {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
async function maybeAdvanceTokenPortal(options) {
|
|
621
|
+
if (!isTokenPortalUrl(options.launchUrl)) {
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
const autoMode = String(process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE || '').trim();
|
|
625
|
+
const autoModeEnabled = autoMode.length > 0;
|
|
626
|
+
if (!autoModeEnabled) {
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
const activeUrl = getActiveCamoPageUrl(options.actionContext);
|
|
630
|
+
if (activeUrl && !isTokenPortalUrl(activeUrl)) {
|
|
631
|
+
logOAuthDebug(`[OAuth] camo-cli portal advance skipped (active page is non-portal): ${activeUrl}`);
|
|
632
|
+
return true;
|
|
633
|
+
}
|
|
634
|
+
if (isTruthyFlag(process.env.ROUTECODEX_CAMOUFOX_OPEN_ONLY) || isTruthyFlag(process.env.RCC_CAMOUFOX_OPEN_ONLY)) {
|
|
635
|
+
logOAuthDebug('[OAuth] camo-cli portal advance skipped (open-only mode)');
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
const retryCount = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_PORTAL_CLICK_RETRIES, autoModeEnabled ? 8 : 2);
|
|
639
|
+
const retryDelayMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_PORTAL_CLICK_RETRY_DELAY_MS, 350);
|
|
640
|
+
return clickCamoTarget(options.actionContext, CAMO_CLICK_TARGETS.tokenPortalContinue, {
|
|
641
|
+
retries: retryCount,
|
|
642
|
+
retryDelayMs,
|
|
643
|
+
required: autoModeEnabled
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
function isIflowOAuthUrl(rawUrl) {
|
|
647
|
+
if (!rawUrl || typeof rawUrl !== 'string') {
|
|
558
648
|
return false;
|
|
559
649
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
650
|
+
try {
|
|
651
|
+
const parsed = new URL(rawUrl);
|
|
652
|
+
return /(?:^|\.)iflow\.cn$/i.test(parsed.hostname) && parsed.pathname === '/oauth';
|
|
653
|
+
}
|
|
654
|
+
catch {
|
|
655
|
+
return rawUrl.includes('iflow.cn/oauth');
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
function isQwenAuthorizeUrl(rawUrl) {
|
|
659
|
+
if (!rawUrl || typeof rawUrl !== 'string') {
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
662
|
+
try {
|
|
663
|
+
const parsed = new URL(rawUrl);
|
|
664
|
+
return /(?:^|\.)chat\.qwen\.ai$/i.test(parsed.hostname) && parsed.pathname.startsWith('/authorize');
|
|
665
|
+
}
|
|
666
|
+
catch {
|
|
667
|
+
return rawUrl.includes('chat.qwen.ai/authorize');
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
function isGoogleAuthUrl(rawUrl) {
|
|
671
|
+
if (!rawUrl || typeof rawUrl !== 'string') {
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
try {
|
|
675
|
+
const parsed = new URL(rawUrl);
|
|
676
|
+
if (!/(?:^|\.)accounts\.google\.com$/i.test(parsed.hostname)) {
|
|
677
|
+
return false;
|
|
564
678
|
}
|
|
565
|
-
|
|
566
|
-
|
|
679
|
+
return parsed.pathname.includes('/signin/') || parsed.pathname.includes('/oauth');
|
|
680
|
+
}
|
|
681
|
+
catch {
|
|
682
|
+
return rawUrl.includes('accounts.google.com');
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
async function maybeAdvanceQwenAuthorization(options) {
|
|
686
|
+
const provider = String(options.provider || '').trim().toLowerCase();
|
|
687
|
+
if (provider !== 'qwen') {
|
|
688
|
+
return true;
|
|
689
|
+
}
|
|
690
|
+
let activeUrl = getActiveCamoPageUrl(options.actionContext);
|
|
691
|
+
if (activeUrl && !isQwenAuthorizeUrl(activeUrl) && isTokenPortalUrl(activeUrl)) {
|
|
692
|
+
const settleTimeoutMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_QWEN_OAUTH_SETTLE_MS, 8000);
|
|
693
|
+
const pollIntervalMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_QWEN_OAUTH_POLL_MS, 250);
|
|
694
|
+
const deadline = Date.now() + settleTimeoutMs;
|
|
695
|
+
while (Date.now() <= deadline) {
|
|
696
|
+
const currentUrl = getActiveCamoPageUrl(options.actionContext);
|
|
697
|
+
if (currentUrl) {
|
|
698
|
+
activeUrl = currentUrl;
|
|
699
|
+
if (isQwenAuthorizeUrl(currentUrl) || isGoogleAuthUrl(currentUrl)) {
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (!activeUrl || !isQwenAuthorizeUrl(activeUrl)) {
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
const autoMode = String(process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE || '').trim().toLowerCase();
|
|
710
|
+
const autoModeEnabled = autoMode === 'qwen';
|
|
711
|
+
if (!autoModeEnabled) {
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
const retryCount = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_QWEN_CLICK_RETRIES, autoModeEnabled ? 6 : 2);
|
|
715
|
+
const retryDelayMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_QWEN_CLICK_RETRY_DELAY_MS, 350);
|
|
716
|
+
// Try direct confirm first; if not present, fall back to Google entry button.
|
|
717
|
+
await clickCamoTarget(options.actionContext, CAMO_CLICK_TARGETS.qwenAuthorizeConfirm, {
|
|
718
|
+
retries: retryCount,
|
|
719
|
+
retryDelayMs,
|
|
720
|
+
required: false
|
|
721
|
+
});
|
|
722
|
+
await clickCamoTarget(options.actionContext, CAMO_CLICK_TARGETS.qwenGoogleContinue, {
|
|
723
|
+
retries: retryCount,
|
|
724
|
+
retryDelayMs,
|
|
725
|
+
required: false
|
|
726
|
+
});
|
|
727
|
+
return true;
|
|
728
|
+
}
|
|
729
|
+
async function maybeAdvanceIflowAccountSelection(options) {
|
|
730
|
+
const provider = String(options.provider || '').trim().toLowerCase();
|
|
731
|
+
if (provider !== 'iflow') {
|
|
732
|
+
return true;
|
|
733
|
+
}
|
|
734
|
+
const autoMode = String(process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE || '').trim().toLowerCase();
|
|
735
|
+
const autoModeEnabled = autoMode === 'iflow';
|
|
736
|
+
if (!autoModeEnabled) {
|
|
737
|
+
return true;
|
|
738
|
+
}
|
|
739
|
+
let activeUrl = getActiveCamoPageUrl(options.actionContext);
|
|
740
|
+
if (activeUrl && !isIflowOAuthUrl(activeUrl) && isTokenPortalUrl(activeUrl)) {
|
|
741
|
+
const settleTimeoutMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_IFLOW_OAUTH_SETTLE_MS, 7000);
|
|
742
|
+
const pollIntervalMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_IFLOW_OAUTH_POLL_MS, 250);
|
|
743
|
+
const waited = await waitForIflowOAuthPage({
|
|
744
|
+
actionContext: options.actionContext,
|
|
745
|
+
settleTimeoutMs,
|
|
746
|
+
pollIntervalMs
|
|
747
|
+
});
|
|
748
|
+
if (!waited) {
|
|
749
|
+
logOAuthDebug(`[OAuth] camo-cli iflow account advance skipped (iflow oauth page not ready): ${activeUrl || 'n/a'}`);
|
|
750
|
+
return true;
|
|
751
|
+
}
|
|
752
|
+
activeUrl = waited;
|
|
753
|
+
}
|
|
754
|
+
if (!activeUrl || !isIflowOAuthUrl(activeUrl)) {
|
|
755
|
+
return true;
|
|
756
|
+
}
|
|
757
|
+
logOAuthDebug(`[OAuth] camo-cli iflow oauth page ready: ${activeUrl}`);
|
|
758
|
+
const retryCount = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_IFLOW_ACCOUNT_CLICK_RETRIES, autoModeEnabled ? 6 : 2);
|
|
759
|
+
const retryDelayMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_IFLOW_ACCOUNT_CLICK_RETRY_DELAY_MS, 400);
|
|
760
|
+
return clickCamoTarget(options.actionContext, CAMO_CLICK_TARGETS.iflowAccountSelect, {
|
|
761
|
+
retries: retryCount,
|
|
762
|
+
retryDelayMs,
|
|
763
|
+
required: autoModeEnabled
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
async function waitForGoogleAuthPage(options) {
|
|
767
|
+
const timeoutMs = options.settleTimeoutMs > 0 ? options.settleTimeoutMs : 7000;
|
|
768
|
+
const intervalMs = options.pollIntervalMs > 0 ? options.pollIntervalMs : 250;
|
|
769
|
+
const deadline = Date.now() + timeoutMs;
|
|
770
|
+
let lastUrl = null;
|
|
771
|
+
while (Date.now() <= deadline) {
|
|
772
|
+
const activeUrl = getActiveCamoPageUrl(options.actionContext);
|
|
773
|
+
if (activeUrl) {
|
|
774
|
+
lastUrl = activeUrl;
|
|
775
|
+
if (isGoogleAuthUrl(activeUrl)) {
|
|
776
|
+
return activeUrl;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
780
|
+
}
|
|
781
|
+
return isGoogleAuthUrl(lastUrl || '') ? lastUrl : null;
|
|
782
|
+
}
|
|
783
|
+
async function waitForGoogleSignInPrompt(options) {
|
|
784
|
+
const timeoutMs = options.settleTimeoutMs > 0 ? options.settleTimeoutMs : 12_000;
|
|
785
|
+
const intervalMs = options.pollIntervalMs > 0 ? options.pollIntervalMs : 400;
|
|
786
|
+
const deadline = Date.now() + timeoutMs;
|
|
787
|
+
let lastUrl = null;
|
|
788
|
+
while (Date.now() <= deadline) {
|
|
789
|
+
const activeUrl = getActiveCamoPageUrl(options.actionContext);
|
|
790
|
+
if (activeUrl) {
|
|
791
|
+
lastUrl = activeUrl;
|
|
792
|
+
if (!isGoogleAuthUrl(activeUrl)) {
|
|
793
|
+
return { activeUrl, promptDetected: false };
|
|
794
|
+
}
|
|
795
|
+
if (hasCamoGoogleSignInPrompt(options.actionContext)) {
|
|
796
|
+
return { activeUrl, promptDetected: true };
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
800
|
+
}
|
|
801
|
+
return {
|
|
802
|
+
activeUrl: lastUrl,
|
|
803
|
+
promptDetected: !!lastUrl && isGoogleAuthUrl(lastUrl) && hasCamoGoogleSignInPrompt(options.actionContext)
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
async function maybeAdvanceGoogleAuth(options) {
|
|
807
|
+
const provider = String(options.provider || '').trim().toLowerCase();
|
|
808
|
+
if (provider !== 'gemini-cli' && provider !== 'antigravity' && provider !== 'qwen') {
|
|
809
|
+
return true;
|
|
810
|
+
}
|
|
811
|
+
const autoMode = String(process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE || '').trim().toLowerCase();
|
|
812
|
+
const autoModeEnabled = autoMode === 'gemini' || autoMode === 'antigravity' || autoMode === 'qwen';
|
|
813
|
+
if (!autoModeEnabled) {
|
|
814
|
+
return true;
|
|
815
|
+
}
|
|
816
|
+
let activeUrl = getActiveCamoPageUrl(options.actionContext);
|
|
817
|
+
const needsGooglePageWait = !!activeUrl && !isGoogleAuthUrl(activeUrl) && (isQwenAuthorizeUrl(activeUrl) || isTokenPortalUrl(activeUrl));
|
|
818
|
+
if (needsGooglePageWait) {
|
|
819
|
+
const settleTimeoutMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_GOOGLE_OAUTH_SETTLE_MS, 7000);
|
|
820
|
+
const pollIntervalMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_GOOGLE_OAUTH_POLL_MS, 250);
|
|
821
|
+
activeUrl = await waitForGoogleAuthPage({
|
|
822
|
+
actionContext: options.actionContext,
|
|
823
|
+
settleTimeoutMs,
|
|
824
|
+
pollIntervalMs
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
if (!activeUrl || !isGoogleAuthUrl(activeUrl)) {
|
|
828
|
+
return true;
|
|
829
|
+
}
|
|
830
|
+
const retryCount = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_GOOGLE_CLICK_RETRIES, autoModeEnabled ? 4 : 2);
|
|
831
|
+
const retryDelayMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_GOOGLE_CLICK_RETRY_DELAY_MS, 350);
|
|
832
|
+
const signInRetryCount = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_GOOGLE_SIGNIN_RETRIES, autoModeEnabled ? 12 : retryCount);
|
|
833
|
+
const signInRetryDelayMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_GOOGLE_SIGNIN_RETRY_DELAY_MS, autoModeEnabled ? 500 : retryDelayMs);
|
|
834
|
+
const accountHint = String(process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT ||
|
|
835
|
+
process.env.RCC_CAMOUFOX_ACCOUNT_TEXT ||
|
|
836
|
+
options.alias ||
|
|
837
|
+
'').trim();
|
|
838
|
+
const hasEmailHint = accountHint.includes('@');
|
|
839
|
+
if (accountHint) {
|
|
840
|
+
const hintClicked = await clickCamoGoogleAccountByHint(options.actionContext, accountHint, {
|
|
841
|
+
retries: retryCount,
|
|
842
|
+
retryDelayMs,
|
|
843
|
+
required: autoModeEnabled && hasEmailHint
|
|
844
|
+
});
|
|
845
|
+
if (!hintClicked) {
|
|
846
|
+
return false;
|
|
567
847
|
}
|
|
568
|
-
|
|
848
|
+
}
|
|
849
|
+
// If we matched a concrete email hint, do not run generic account fallback.
|
|
850
|
+
if (!hasEmailHint) {
|
|
851
|
+
const accountFallbackClicked = await clickCamoTarget(options.actionContext, CAMO_CLICK_TARGETS.googleAccountSelect, {
|
|
852
|
+
retries: retryCount,
|
|
853
|
+
retryDelayMs,
|
|
854
|
+
required: autoModeEnabled
|
|
855
|
+
});
|
|
856
|
+
if (!accountFallbackClicked) {
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
const signInUrl = getActiveCamoPageUrl(options.actionContext);
|
|
861
|
+
if (!signInUrl) {
|
|
862
|
+
logOAuthDebug('[OAuth] camo-cli google sign-in step failed: active page unavailable (session may be closed)');
|
|
569
863
|
return false;
|
|
570
864
|
}
|
|
865
|
+
if (!isGoogleAuthUrl(signInUrl)) {
|
|
866
|
+
logOAuthDebug(`[OAuth] camo-cli google sign-in step skipped (active page is non-google): ${signInUrl || 'n/a'}`);
|
|
867
|
+
return true;
|
|
868
|
+
}
|
|
869
|
+
const strictRequired = provider === 'gemini-cli' || provider === 'antigravity';
|
|
870
|
+
const signInSettleMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_GOOGLE_SIGNIN_SETTLE_MS, strictRequired ? 16_000 : 8_000);
|
|
871
|
+
const signInPollMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_GOOGLE_SIGNIN_POLL_MS, 400);
|
|
872
|
+
const signInProbe = await waitForGoogleSignInPrompt({
|
|
873
|
+
actionContext: options.actionContext,
|
|
874
|
+
settleTimeoutMs: signInSettleMs,
|
|
875
|
+
pollIntervalMs: signInPollMs
|
|
876
|
+
});
|
|
877
|
+
if (signInProbe.promptDetected) {
|
|
878
|
+
const signInClicked = await clickCamoGoogleSignInBySelector(options.actionContext, {
|
|
879
|
+
retries: signInRetryCount,
|
|
880
|
+
retryDelayMs: signInRetryDelayMs,
|
|
881
|
+
required: true
|
|
882
|
+
});
|
|
883
|
+
if (!signInClicked) {
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
else if (strictRequired && signInProbe.activeUrl && isGoogleAuthUrl(signInProbe.activeUrl)) {
|
|
888
|
+
logOAuthDebug(`[OAuth] camo-cli google sign-in prompt not observed on strict provider=${provider} url=${signInProbe.activeUrl}`);
|
|
889
|
+
return false;
|
|
890
|
+
}
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
async function waitForTokenPortalPage(options) {
|
|
894
|
+
const timeoutMs = options.settleTimeoutMs > 0 ? options.settleTimeoutMs : 4000;
|
|
895
|
+
const intervalMs = options.pollIntervalMs > 0 ? options.pollIntervalMs : 250;
|
|
896
|
+
const deadline = Date.now() + timeoutMs;
|
|
897
|
+
while (Date.now() <= deadline) {
|
|
898
|
+
const activeUrl = getActiveCamoPageUrl(options.actionContext);
|
|
899
|
+
if (activeUrl && isTokenPortalUrl(activeUrl)) {
|
|
900
|
+
return true;
|
|
901
|
+
}
|
|
902
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
903
|
+
}
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
async function waitForIflowOAuthPage(options) {
|
|
907
|
+
const timeoutMs = options.settleTimeoutMs > 0 ? options.settleTimeoutMs : 7000;
|
|
908
|
+
const intervalMs = options.pollIntervalMs > 0 ? options.pollIntervalMs : 250;
|
|
909
|
+
const deadline = Date.now() + timeoutMs;
|
|
910
|
+
let lastUrl = null;
|
|
911
|
+
while (Date.now() <= deadline) {
|
|
912
|
+
const activeUrl = getActiveCamoPageUrl(options.actionContext);
|
|
913
|
+
if (activeUrl) {
|
|
914
|
+
lastUrl = activeUrl;
|
|
915
|
+
if (isIflowOAuthUrl(activeUrl)) {
|
|
916
|
+
return activeUrl;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
920
|
+
}
|
|
921
|
+
return isIflowOAuthUrl(lastUrl || '') ? lastUrl : null;
|
|
922
|
+
}
|
|
923
|
+
export async function openAuthInCamoufox(options) {
|
|
924
|
+
setCamoufoxLaunchFailureReason(null);
|
|
925
|
+
logOAuthDebug(`[OAuth] Camoufox: launch requested url=${options.url} provider=${options.provider ?? ''} alias=${options.alias ?? ''}`);
|
|
571
926
|
const url = options.url;
|
|
572
927
|
if (!url || typeof url !== 'string') {
|
|
573
928
|
logOAuthDebug('[OAuth] Camoufox: invalid or empty URL; falling back to default browser');
|
|
929
|
+
setCamoufoxLaunchFailureReason('invalid_launch_url');
|
|
574
930
|
return false;
|
|
575
931
|
}
|
|
576
932
|
const launchUrl = applyGoogleLocaleHint(url);
|
|
577
|
-
const
|
|
933
|
+
const derivedProfileId = buildProfileId(options.provider, options.alias);
|
|
934
|
+
const explicitProfileId = options.profileId && options.profileId.trim().length > 0
|
|
578
935
|
? options.profileId.trim()
|
|
579
|
-
:
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const autoRepairRaw = String(process.env.ROUTECODEX_CAMOUFOX_AUTO_REPAIR_OS_MISMATCH ||
|
|
587
|
-
process.env.RCC_CAMOUFOX_AUTO_REPAIR_OS_MISMATCH ||
|
|
588
|
-
'1')
|
|
589
|
-
.trim()
|
|
590
|
-
.toLowerCase();
|
|
591
|
-
const autoRepairEnabled = autoRepairRaw !== '0' && autoRepairRaw !== 'false' && autoRepairRaw !== 'no';
|
|
592
|
-
const family = getProviderFamily(options.provider);
|
|
593
|
-
const isGoogleFlow = family === 'gemini';
|
|
594
|
-
if (autoRepairEnabled && isGoogleFlow && process.platform === 'darwin') {
|
|
595
|
-
const fpPath = getFingerprintPath(profileId);
|
|
596
|
-
const existingEnv = loadFingerprintEnv(profileId);
|
|
597
|
-
const rawCfg = existingEnv?.CAMOU_CONFIG_1;
|
|
598
|
-
if (rawCfg) {
|
|
599
|
-
try {
|
|
600
|
-
const cfg = JSON.parse(rawCfg);
|
|
601
|
-
const platform = typeof cfg['navigator.platform'] === 'string' ? String(cfg['navigator.platform']) : '';
|
|
602
|
-
const wantsRepair = platform.toLowerCase() === 'win32';
|
|
603
|
-
if (wantsRepair) {
|
|
604
|
-
const backupPath = `${fpPath}.bak.${Date.now()}`;
|
|
605
|
-
console.warn(`[OAuth] Camoufox fingerprint OS mismatch (host=macos, fp=${platform}). ` +
|
|
606
|
-
`Repairing to macos fingerprint for profileId=${profileId} (backup=${backupPath}).`);
|
|
607
|
-
try {
|
|
608
|
-
fs.renameSync(fpPath, backupPath);
|
|
609
|
-
}
|
|
610
|
-
catch {
|
|
611
|
-
// If backup fails, do not delete/overwrite; just continue with existing fingerprint.
|
|
612
|
-
console.warn('[OAuth] Camoufox fingerprint repair skipped (backup failed).');
|
|
613
|
-
}
|
|
614
|
-
const prevForcedOs = process.env.ROUTECODEX_CAMOUFOX_FORCE_OS;
|
|
615
|
-
process.env.ROUTECODEX_CAMOUFOX_FORCE_OS = 'macos';
|
|
616
|
-
try {
|
|
617
|
-
// Re-generate fingerprint file for this profileId (best-effort).
|
|
618
|
-
void ensureFingerprintEnv(profileId, options.provider, options.alias);
|
|
619
|
-
}
|
|
620
|
-
finally {
|
|
621
|
-
if (prevForcedOs === undefined) {
|
|
622
|
-
delete process.env.ROUTECODEX_CAMOUFOX_FORCE_OS;
|
|
623
|
-
}
|
|
624
|
-
else {
|
|
625
|
-
process.env.ROUTECODEX_CAMOUFOX_FORCE_OS = prevForcedOs;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
catch {
|
|
631
|
-
// ignore parse errors
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
}
|
|
936
|
+
: '';
|
|
937
|
+
const hasProviderAlias = String(options.provider || '').trim().length > 0 || String(options.alias || '').trim().length > 0;
|
|
938
|
+
const profileId = hasProviderAlias
|
|
939
|
+
? derivedProfileId
|
|
940
|
+
: (explicitProfileId || derivedProfileId);
|
|
941
|
+
if (hasProviderAlias && explicitProfileId && explicitProfileId !== derivedProfileId) {
|
|
942
|
+
logOAuthDebug(`[OAuth] Camoufox profile override ignored: explicit=${explicitProfileId} derived=${derivedProfileId}`);
|
|
635
943
|
}
|
|
636
|
-
|
|
637
|
-
|
|
944
|
+
// Ensure profile directory exists ahead of launch so that fingerprint/profile data can be persisted per token.
|
|
945
|
+
const profileDir = ensureCamoufoxProfileDir(options.provider, options.alias);
|
|
946
|
+
const profileRoot = getProfileRoot();
|
|
947
|
+
const fingerprintRoot = getFingerprintRoot();
|
|
948
|
+
if (!shouldPreferCamoCliForOAuth(options.provider)) {
|
|
949
|
+
logOAuthDebug('[OAuth] camo-cli launch skipped (provider disabled)');
|
|
950
|
+
setCamoufoxLaunchFailureReason('provider_disabled');
|
|
951
|
+
return false;
|
|
638
952
|
}
|
|
639
|
-
const
|
|
640
|
-
const
|
|
641
|
-
const
|
|
953
|
+
const devMode = String(process.env.ROUTECODEX_CAMOUFOX_DEV_MODE || '').trim().toLowerCase();
|
|
954
|
+
const headless = !(devMode === '1' || devMode === 'true' || devMode === 'yes' || devMode === 'on');
|
|
955
|
+
const camoCommand = resolveCamoCliCommand();
|
|
642
956
|
try {
|
|
643
|
-
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
957
|
+
console.log(`[OAuth] Camoufox launcher spawn via camo-cli: profileId=${profileId} headless=${headless ? '1' : '0'}`);
|
|
958
|
+
const fingerprintEnvRaw = ensureFingerprintEnv(profileId, options.provider, options.alias);
|
|
959
|
+
const fingerprintEnv = sanitizeCamouConfigForOAuth(options.provider, fingerprintEnvRaw);
|
|
960
|
+
const localeEnv = resolveCamoufoxLocaleEnv();
|
|
961
|
+
const sharedEnv = {
|
|
962
|
+
...process.env,
|
|
963
|
+
...localeEnv,
|
|
964
|
+
...fingerprintEnv,
|
|
965
|
+
WEBAUTO_PATHS_PROFILES: profileRoot,
|
|
966
|
+
WEBAUTO_PATHS_FINGERPRINTS: fingerprintRoot,
|
|
967
|
+
BROWSER_PROFILE_ID: profileId,
|
|
968
|
+
BROWSER_PROFILE_DIR: profileDir,
|
|
969
|
+
BROWSER_INITIAL_URL: launchUrl
|
|
970
|
+
};
|
|
971
|
+
const actionContext = {
|
|
972
|
+
camoCommand,
|
|
973
|
+
profileId,
|
|
974
|
+
env: sharedEnv
|
|
975
|
+
};
|
|
976
|
+
if (!ensureCamoProfile(actionContext)) {
|
|
977
|
+
setCamoufoxLaunchFailureReason('profile_create_failed');
|
|
978
|
+
return false;
|
|
979
|
+
}
|
|
980
|
+
// camo currently resolves session routing by default profile in some paths.
|
|
981
|
+
if (!setDefaultCamoProfile(actionContext)) {
|
|
982
|
+
setCamoufoxLaunchFailureReason('profile_default_set_failed');
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
if (!startCamoSession(actionContext, headless)) {
|
|
986
|
+
setCamoufoxLaunchFailureReason('session_start_failed');
|
|
987
|
+
return false;
|
|
988
|
+
}
|
|
989
|
+
// Ensure we always navigate to the requested URL even when the session already exists.
|
|
990
|
+
let effectiveLaunchUrl = launchUrl;
|
|
991
|
+
let gotoOk = gotoCamoUrl(actionContext, effectiveLaunchUrl);
|
|
992
|
+
if (!gotoOk) {
|
|
993
|
+
const isPortalLaunch = isTokenPortalUrl(launchUrl);
|
|
994
|
+
if (!headless) {
|
|
995
|
+
logOAuthDebug('[OAuth] camo-cli goto returned non-zero in headful mode; keep session for manual portal/open-url continuation');
|
|
996
|
+
gotoOk = true;
|
|
660
997
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
if (
|
|
670
|
-
|
|
998
|
+
if (isPortalLaunch) {
|
|
999
|
+
const settleTimeoutMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_PORTAL_GOTO_SETTLE_MS, 4000);
|
|
1000
|
+
const pollIntervalMs = parsePositiveInt(process.env.ROUTECODEX_CAMOUFOX_PORTAL_GOTO_POLL_MS, 250);
|
|
1001
|
+
const portalSettled = await waitForTokenPortalPage({
|
|
1002
|
+
actionContext,
|
|
1003
|
+
settleTimeoutMs,
|
|
1004
|
+
pollIntervalMs
|
|
1005
|
+
});
|
|
1006
|
+
if (portalSettled) {
|
|
1007
|
+
logOAuthDebug('[OAuth] camo-cli portal goto returned non-zero but active page settled to portal; continue with portal flow');
|
|
1008
|
+
gotoOk = true;
|
|
671
1009
|
}
|
|
672
|
-
settled = true;
|
|
673
|
-
clearTimeout(timer);
|
|
674
|
-
resolve(value);
|
|
675
|
-
};
|
|
676
|
-
const timer = setTimeout(() => settle(true), quickCheckMs);
|
|
677
|
-
if (typeof timer.unref === 'function') {
|
|
678
|
-
timer.unref();
|
|
679
1010
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
if (
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
settle(true);
|
|
1011
|
+
if (!gotoOk) {
|
|
1012
|
+
const fallbackOauthUrl = resolvePortalOauthUrl(launchUrl);
|
|
1013
|
+
if (fallbackOauthUrl && headless) {
|
|
1014
|
+
logOAuthDebug(`[OAuth] camo-cli portal goto failed; fallback to direct oauthUrl=${fallbackOauthUrl}`);
|
|
1015
|
+
effectiveLaunchUrl = fallbackOauthUrl;
|
|
1016
|
+
gotoOk = gotoCamoUrl(actionContext, effectiveLaunchUrl);
|
|
687
1017
|
}
|
|
688
|
-
});
|
|
689
|
-
});
|
|
690
|
-
if (!ok) {
|
|
691
|
-
try {
|
|
692
|
-
console.warn('[OAuth] Camoufox launcher exited early; falling back to system browser.');
|
|
693
|
-
}
|
|
694
|
-
catch {
|
|
695
|
-
// ignore
|
|
696
1018
|
}
|
|
697
1019
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
1020
|
+
if (!gotoOk) {
|
|
1021
|
+
setCamoufoxLaunchFailureReason('goto_failed');
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
1024
|
+
const portalAdvanced = await maybeAdvanceTokenPortal({
|
|
1025
|
+
launchUrl,
|
|
1026
|
+
actionContext
|
|
1027
|
+
});
|
|
1028
|
+
if (!portalAdvanced) {
|
|
1029
|
+
setCamoufoxLaunchFailureReason('element_not_found:token_portal_continue');
|
|
1030
|
+
return false;
|
|
1031
|
+
}
|
|
1032
|
+
const iflowAdvanced = await maybeAdvanceIflowAccountSelection({
|
|
1033
|
+
provider: options.provider,
|
|
1034
|
+
actionContext
|
|
1035
|
+
});
|
|
1036
|
+
if (!iflowAdvanced) {
|
|
1037
|
+
setCamoufoxLaunchFailureReason('element_not_found:iflow_account_select');
|
|
1038
|
+
return false;
|
|
1039
|
+
}
|
|
1040
|
+
const qwenAdvanced = await maybeAdvanceQwenAuthorization({
|
|
1041
|
+
provider: options.provider,
|
|
1042
|
+
actionContext
|
|
1043
|
+
});
|
|
1044
|
+
if (!qwenAdvanced) {
|
|
1045
|
+
setCamoufoxLaunchFailureReason('element_not_found:qwen_authorization');
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
1048
|
+
const googleAdvanced = await maybeAdvanceGoogleAuth({
|
|
1049
|
+
provider: options.provider,
|
|
1050
|
+
alias: options.alias,
|
|
1051
|
+
actionContext
|
|
1052
|
+
});
|
|
1053
|
+
if (!googleAdvanced) {
|
|
1054
|
+
setCamoufoxLaunchFailureReason('element_not_found:google_auth_step');
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
setCamoufoxLaunchFailureReason(null);
|
|
1058
|
+
return true;
|
|
702
1059
|
}
|
|
703
1060
|
catch (error) {
|
|
704
|
-
|
|
1061
|
+
setCamoufoxLaunchFailureReason(`exception:${error instanceof Error ? error.message : String(error)}`);
|
|
1062
|
+
logOAuthDebug(`[OAuth] camo-cli launch failed - ${error instanceof Error ? error.message : String(error)}`);
|
|
705
1063
|
return false;
|
|
706
1064
|
}
|
|
707
1065
|
}
|