opencode-anthropic-multi-account 0.2.23 → 0.2.25
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/dist/{chunk-RAX4SFCO.js → chunk-3CTML5AX.js} +5 -6
- package/dist/chunk-3CTML5AX.js.map +1 -0
- package/dist/{chunk-2SN3UVSM.js → chunk-II7N6KHL.js} +244 -113
- package/dist/chunk-II7N6KHL.js.map +1 -0
- package/dist/fingerprint-capture.d.ts +4 -2
- package/dist/fingerprint-capture.js +4 -2
- package/dist/index.js +140 -174
- package/dist/index.js.map +1 -1
- package/dist/scrub-template.d.ts +13 -3
- package/dist/scrub-template.js +1 -1
- package/package.json +7 -6
- package/dist/chunk-2SN3UVSM.js.map +0 -1
- package/dist/chunk-RAX4SFCO.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
scrubTemplate
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3CTML5AX.js";
|
|
4
4
|
|
|
5
|
-
// src/fingerprint
|
|
5
|
+
// src/claude-code/fingerprint/capture.ts
|
|
6
6
|
import { spawn } from "child_process";
|
|
7
7
|
import { createServer } from "http";
|
|
8
8
|
import { basename, dirname as dirname2, join as join2 } from "path";
|
|
@@ -17,11 +17,11 @@ import {
|
|
|
17
17
|
writeFile as writeFile2
|
|
18
18
|
} from "fs/promises";
|
|
19
19
|
|
|
20
|
-
// src/fingerprint
|
|
21
|
-
var
|
|
20
|
+
// src/claude-code/fingerprint/data.json
|
|
21
|
+
var data_default = {
|
|
22
22
|
_version: 1,
|
|
23
23
|
_schemaVersion: 1,
|
|
24
|
-
_captured: "2026-04-
|
|
24
|
+
_captured: "2026-04-24T13:28:12.469Z",
|
|
25
25
|
_source: "bundled",
|
|
26
26
|
agent_identity: "You are a Claude agent, built on Anthropic's Claude Agent SDK.",
|
|
27
27
|
system_prompt: `You are an interactive agent that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
|
|
@@ -93,6 +93,8 @@ In code: default to writing no comments. Never write multi-paragraph docstrings
|
|
|
93
93
|
- Use the Agent tool with specialized agents when the task at hand matches the agent's description. Subagents are valuable for parallelizing independent queries or for protecting the main context window from excessive results, but they should not be used excessively when not needed. Importantly, avoid duplicating work that subagents are already doing - if you delegate research to a subagent, do not also perform the same searches yourself.
|
|
94
94
|
- For broad codebase exploration or research that'll take more than 3 queries, spawn Agent with subagent_type=Explore. Otherwise use the Glob or Grep directly.
|
|
95
95
|
- When the user types \`/<skill-name>\`, invoke it via Skill. Only use skills listed in the user-invocable skills section \u2014 don't guess.
|
|
96
|
+
- When work you just finished has a natural future follow-up, end your reply with a one-line offer to \`/schedule\` a background agent to do it \u2014 name the concrete action and cadence ("Want me to /schedule an agent in 2 weeks to open a cleanup PR for the flag?"). One-time signals: a feature flag/gate/experiment/staged rollout (clean it up or ramp it), a soak window or metric to verify (query it and post results), a long-running job with an ETA (check status and report), a temp workaround/instrumentation/.skip left in (open a removal PR), a "remove once X" TODO. Recurring signals: a sweep/triage/report/queue-drain the user just did by hand, or anything "weekly"/"again"/"piling up" \u2014 offer to run it as a routine. The bar is 70%+ odds the user says yes \u2014 skip it for refactors, bug fixes with tests, docs, renames, routine dep bumps, plain feature merges, or when the user signals closure ("nothing else to do", "should be fine now"). Don't stack offers on back-to-back turns; let most tasks just be tasks.
|
|
97
|
+
- If the user asks about "ultrareview" or how to run it, explain that /ultrareview launches a multi-agent cloud review of the current branch (or /ultrareview <PR#> for a GitHub PR). It is user-triggered and billed; you cannot launch it yourself, so do not attempt to via Bash or otherwise. It needs a git repository (offer to "git init" if not in one); the no-arg form bundles the local branch and does not need a GitHub remote.
|
|
96
98
|
|
|
97
99
|
# Language
|
|
98
100
|
Always respond in Korean. Use Korean for all explanations, comments, and communications with the user. Technical terms and code identifiers should remain in their original form.
|
|
@@ -100,8 +102,6 @@ Maintain full orthographic correctness for Korean, including all required diacri
|
|
|
100
102
|
|
|
101
103
|
When working with tool results, write down any important information you might need later in your response, as the original tool result may be cleared later.
|
|
102
104
|
|
|
103
|
-
Length limits: keep text between tool calls to \u226425 words. Keep final responses to \u2264100 words unless the task requires more detail.
|
|
104
|
-
|
|
105
105
|
gitStatus: This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.
|
|
106
106
|
|
|
107
107
|
Current branch: main
|
|
@@ -779,7 +779,7 @@ Ensure your plan is complete and unambiguous:
|
|
|
779
779
|
},
|
|
780
780
|
{
|
|
781
781
|
name: "Monitor",
|
|
782
|
-
description: 'Start a background monitor that streams events from a long-running script. Each stdout line is an event \u2014 you keep working and notifications arrive in the chat. Events arrive on their own schedule and are not replies from the user, even if one lands while you\'re waiting for the user to answer a question.\n\
|
|
782
|
+
description: 'Start a background monitor that streams events from a long-running script. Each stdout line is an event \u2014 you keep working and notifications arrive in the chat. Events arrive on their own schedule and are not replies from the user, even if one lands while you\'re waiting for the user to answer a question.\n\nPick by how many notifications you need:\n- **One** ("tell me when the server is ready / the build finishes") \u2192 use **Bash with `run_in_background`** and a command that exits when the condition is true, e.g. `until grep -q "Ready in" dev.log; do sleep 0.5; done`. You get a single completion notification when it exits.\n- **One per occurrence, indefinitely** ("tell me every time an ERROR line appears") \u2192 Monitor with an unbounded command (`tail -f`, `inotifywait -m`, `while true`).\n- **One per occurrence, until a known end** ("emit each CI step result, stop when the run completes") \u2192 Monitor with a command that emits lines and then exits.\n\nYour script\'s stdout is the event stream. Each line becomes a notification. Exit ends the watch.\n\n # Each matching log line is an event\n tail -f /var/log/app.log | grep --line-buffered "ERROR"\n\n # Each file change is an event\n inotifywait -m --format \'%e %f\' /watched/dir\n\n # Poll GitHub for new PR comments and emit one line per new comment\n last=$(date -u +%Y-%m-%dT%H:%M:%SZ)\n while true; do\n now=$(date -u +%Y-%m-%dT%H:%M:%SZ)\n gh api "repos/owner/repo/issues/123/comments?since=$last" --jq \'.[] | "\\(.user.login): \\(.body)"\'\n last=$now; sleep 30\n done\n\n # Node script that emits events as they arrive (e.g. WebSocket listener)\n node watch-for-events.js\n\n # Per-occurrence with a natural end: emit each CI check as it lands, exit when the run completes\n prev=""\n while true; do\n s=$(gh pr checks 123 --json name,bucket)\n cur=$(jq -r \'.[] | select(.bucket!="pending") | "\\(.name): \\(.bucket)"\' <<<"$s" | sort)\n comm -13 <(echo "$prev") <(echo "$cur")\n prev=$cur\n jq -e \'all(.bucket!="pending")\' <<<"$s" >/dev/null && break\n sleep 30\n done\n\n**Don\'t use an unbounded command for a single notification.** `tail -f`, `inotifywait -m`, and `while true` never exit on their own, so the monitor stays armed until timeout even after the event has fired. For "tell me when X is ready," use Bash `run_in_background` with an `until` loop instead (one notification, ends in seconds). Note that `tail -f log | grep -m 1 ...` does *not* fix this: if the log goes quiet after the match, `tail` never receives SIGPIPE and the pipeline hangs anyway.\n\n**Script quality:**\n- Always use `grep --line-buffered` in pipes \u2014 without it, pipe buffering delays events by minutes.\n- In poll loops, handle transient failures (`curl ... || true`) \u2014 one failed request shouldn\'t kill the monitor.\n- Poll intervals: 30s+ for remote APIs (rate limits), 0.5-1s for local checks.\n- Write a specific `description` \u2014 it appears in every notification ("errors in deploy.log" not "watching logs").\n- Only stdout is the event stream. Stderr goes to the output file (readable via Read) but does not trigger notifications \u2014 for a command you run directly (e.g. `python train.py 2>&1 | grep --line-buffered ...`), merge stderr with `2>&1` so its failures reach your filter. (No effect on `tail -f` of an existing log \u2014 that file only contains what its writer redirected.)\n\n**Coverage \u2014 silence is not success.** When watching a job or process for an outcome, your filter must match every terminal state, not just the happy path. A monitor that greps only for the success marker stays silent through a crashloop, a hung process, or an unexpected exit \u2014 and silence looks identical to "still running." Before arming, ask: *if this process crashed right now, would my filter emit anything?* If not, widen it.\n\n # Wrong \u2014 silent on crash, hang, or any non-success exit\n tail -f run.log | grep --line-buffered "elapsed_steps="\n\n # Right \u2014 one alternation covering progress + the failure signatures you\'d act on\n tail -f run.log | grep -E --line-buffered "elapsed_steps=|Traceback|Error|FAILED|assert|Killed|OOM"\n\nFor poll loops checking job state, emit on every terminal status (`succeeded|failed|cancelled|timeout`), not just success. If you cannot confidently enumerate the failure signatures, broaden the grep alternation rather than narrow it \u2014 some extra noise is better than missing a crashloop.\n\n**Output volume**: Every stdout line is a conversation message, so the filter should be selective \u2014 but selective means "the lines you\'d act on," not "only good news." Never pipe raw logs; use `grep --line-buffered`, `awk`, or a wrapper that emits exactly the success and failure signals you care about. Monitors that produce too many events are automatically stopped; restart with a tighter filter if this happens.\n\nStdout lines within 200ms are batched into a single notification, so multiline output from a single event groups naturally.\n\nThe script runs in the same shell environment as Bash. Exit ends the watch (exit code is reported). Timeout \u2192 killed. Set `persistent: true` for session-length watches (PR monitoring, log tails) \u2014 the monitor runs until you call TaskStop or the session ends. Use TaskStop to cancel early.',
|
|
783
783
|
input_schema: {
|
|
784
784
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
785
785
|
type: "object",
|
|
@@ -889,7 +889,7 @@ If the result says the push wasn't sent, that's expected \u2014 no action needed
|
|
|
889
889
|
},
|
|
890
890
|
{
|
|
891
891
|
name: "Read",
|
|
892
|
-
description: 'Reads a file from the local filesystem. You can access any file directly by using this tool.\nAssume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.\n\nUsage:\n- The file_path parameter must be an absolute path, not a relative path\n- By default, it reads up to 2000 lines starting from the beginning of the file\n- When you already know which part of the file you need, only read that part. This can be important for larger files.\n- Results are returned using cat -n format, with line numbers starting at 1\n- This tool allows Claude Code to read images (eg PNG, JPG, etc). When reading an image file the contents are presented visually as Claude Code is a multimodal LLM.\n- This tool can read PDF files (.pdf). For large PDFs (more than 10 pages), you MUST provide the pages parameter to read specific page ranges (e.g., pages: "1-5"). Reading a large PDF without the pages parameter will fail. Maximum 20 pages per request.\n- This tool can read Jupyter notebooks (.ipynb files) and returns all cells with their outputs, combining code, text, and visualizations.\n- This tool can only read files, not directories. To
|
|
892
|
+
description: 'Reads a file from the local filesystem. You can access any file directly by using this tool.\nAssume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.\n\nUsage:\n- The file_path parameter must be an absolute path, not a relative path\n- By default, it reads up to 2000 lines starting from the beginning of the file\n- When you already know which part of the file you need, only read that part. This can be important for larger files.\n- Results are returned using cat -n format, with line numbers starting at 1\n- This tool allows Claude Code to read images (eg PNG, JPG, etc). When reading an image file the contents are presented visually as Claude Code is a multimodal LLM.\n- This tool can read PDF files (.pdf). For large PDFs (more than 10 pages), you MUST provide the pages parameter to read specific page ranges (e.g., pages: "1-5"). Reading a large PDF without the pages parameter will fail. Maximum 20 pages per request.\n- This tool can read Jupyter notebooks (.ipynb files) and returns all cells with their outputs, combining code, text, and visualizations.\n- This tool can only read files, not directories. To list files in a directory, use the registered shell tool.\n- You will regularly be asked to read screenshots. If the user provides a path to a screenshot, ALWAYS use this tool to view the file at the path. This tool will work with all temporary file paths.\n- If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.',
|
|
893
893
|
input_schema: {
|
|
894
894
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
895
895
|
type: "object",
|
|
@@ -1417,8 +1417,8 @@ IMPORTANT - Use the correct year in search queries:
|
|
|
1417
1417
|
"WebSearch",
|
|
1418
1418
|
"Write"
|
|
1419
1419
|
],
|
|
1420
|
-
anthropic_beta: "claude-code-20250219,
|
|
1421
|
-
cc_version: "2.1.
|
|
1420
|
+
anthropic_beta: "claude-code-20250219,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advisor-tool-2026-03-01,effort-2025-11-24,afk-mode-2026-01-31",
|
|
1421
|
+
cc_version: "2.1.119",
|
|
1422
1422
|
header_order: [
|
|
1423
1423
|
"Accept",
|
|
1424
1424
|
"Authorization",
|
|
@@ -1444,11 +1444,11 @@ IMPORTANT - Use the correct year in search queries:
|
|
|
1444
1444
|
],
|
|
1445
1445
|
header_values: {
|
|
1446
1446
|
accept: "application/json",
|
|
1447
|
-
"anthropic-beta": "claude-code-20250219,
|
|
1447
|
+
"anthropic-beta": "claude-code-20250219,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advisor-tool-2026-03-01,effort-2025-11-24,afk-mode-2026-01-31",
|
|
1448
1448
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
1449
1449
|
"anthropic-version": "2023-06-01",
|
|
1450
1450
|
"content-type": "application/json",
|
|
1451
|
-
"user-agent": "claude-cli/2.1.
|
|
1451
|
+
"user-agent": "claude-cli/2.1.119 (external, sdk-cli)",
|
|
1452
1452
|
"x-app": "cli",
|
|
1453
1453
|
"x-stainless-timeout": "600"
|
|
1454
1454
|
},
|
|
@@ -1466,15 +1466,22 @@ IMPORTANT - Use the correct year in search queries:
|
|
|
1466
1466
|
]
|
|
1467
1467
|
};
|
|
1468
1468
|
|
|
1469
|
-
// src/cli-version.ts
|
|
1470
|
-
import { execFileSync } from "child_process";
|
|
1471
|
-
var DEFAULT_CLI_VERSION =
|
|
1469
|
+
// src/claude-code/cli-version.ts
|
|
1470
|
+
import { execFileSync as defaultExecFileSync } from "child_process";
|
|
1471
|
+
var DEFAULT_CLI_VERSION = data_default.cc_version;
|
|
1472
1472
|
var CLI_VERSION_PATTERN = /(\d+\.\d+\.\d+)/;
|
|
1473
1473
|
var CLAUDE_VERSION_TIMEOUT_MS = 3e3;
|
|
1474
1474
|
var detectedVersion = null;
|
|
1475
|
+
var cliVersionProbe = defaultExecFileSync;
|
|
1475
1476
|
function parseCliVersion(output) {
|
|
1476
1477
|
return output.match(CLI_VERSION_PATTERN)?.[1] ?? null;
|
|
1477
1478
|
}
|
|
1479
|
+
function probeCliVersion() {
|
|
1480
|
+
return cliVersionProbe("claude", ["--version"], {
|
|
1481
|
+
encoding: "utf8",
|
|
1482
|
+
timeout: CLAUDE_VERSION_TIMEOUT_MS
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1478
1485
|
function detectCliVersion() {
|
|
1479
1486
|
if (detectedVersion !== null) {
|
|
1480
1487
|
return detectedVersion;
|
|
@@ -1485,10 +1492,7 @@ function detectCliVersion() {
|
|
|
1485
1492
|
return detectedVersion;
|
|
1486
1493
|
}
|
|
1487
1494
|
try {
|
|
1488
|
-
const output =
|
|
1489
|
-
encoding: "utf8",
|
|
1490
|
-
timeout: CLAUDE_VERSION_TIMEOUT_MS
|
|
1491
|
-
});
|
|
1495
|
+
const output = probeCliVersion();
|
|
1492
1496
|
detectedVersion = parseCliVersion(output) ?? DEFAULT_CLI_VERSION;
|
|
1493
1497
|
} catch {
|
|
1494
1498
|
detectedVersion = DEFAULT_CLI_VERSION;
|
|
@@ -1496,8 +1500,9 @@ function detectCliVersion() {
|
|
|
1496
1500
|
return detectedVersion;
|
|
1497
1501
|
}
|
|
1498
1502
|
|
|
1499
|
-
// src/oauth-config
|
|
1503
|
+
// src/claude-code/oauth-config/detect.ts
|
|
1500
1504
|
import { createHash } from "crypto";
|
|
1505
|
+
import { execFileSync as defaultExecFileSync2 } from "child_process";
|
|
1501
1506
|
import { existsSync } from "fs";
|
|
1502
1507
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
1503
1508
|
import { homedir, platform } from "os";
|
|
@@ -1513,14 +1518,14 @@ var cc_derived_defaults_default = {
|
|
|
1513
1518
|
},
|
|
1514
1519
|
oauth: {
|
|
1515
1520
|
clientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
1516
|
-
authorizeUrl: "https://claude.
|
|
1521
|
+
authorizeUrl: "https://claude.ai/oauth/authorize",
|
|
1517
1522
|
tokenUrl: "https://platform.claude.com/v1/oauth/token",
|
|
1518
|
-
scopes: "user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload",
|
|
1523
|
+
scopes: "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload",
|
|
1519
1524
|
baseApiUrl: "https://api.anthropic.com"
|
|
1520
1525
|
}
|
|
1521
1526
|
};
|
|
1522
1527
|
|
|
1523
|
-
// src/utils.ts
|
|
1528
|
+
// src/shared/utils.ts
|
|
1524
1529
|
import {
|
|
1525
1530
|
createMinimalClient,
|
|
1526
1531
|
formatWaitTime,
|
|
@@ -1530,7 +1535,7 @@ import {
|
|
|
1530
1535
|
sleep
|
|
1531
1536
|
} from "opencode-multi-account-core";
|
|
1532
1537
|
|
|
1533
|
-
// src/constants.ts
|
|
1538
|
+
// src/shared/constants.ts
|
|
1534
1539
|
import { anthropicOAuthAdapter } from "opencode-multi-account-core";
|
|
1535
1540
|
var ANTHROPIC_OAUTH_ADAPTER = anthropicOAuthAdapter;
|
|
1536
1541
|
var ANTHROPIC_CLIENT_ID = ANTHROPIC_OAUTH_ADAPTER.oauthClientId;
|
|
@@ -1543,14 +1548,14 @@ var PLAN_LABELS = ANTHROPIC_OAUTH_ADAPTER.planLabels;
|
|
|
1543
1548
|
var TOKEN_EXPIRY_BUFFER_MS = 6e4;
|
|
1544
1549
|
var TOKEN_REFRESH_TIMEOUT_MS = 3e4;
|
|
1545
1550
|
|
|
1546
|
-
// src/config.ts
|
|
1551
|
+
// src/shared/config.ts
|
|
1547
1552
|
import {
|
|
1548
1553
|
createConfigLoader
|
|
1549
1554
|
} from "opencode-multi-account-core";
|
|
1550
1555
|
var configLoader = createConfigLoader("claude-multiauth.json");
|
|
1551
1556
|
var { getConfig, loadConfig, resetConfigCache, updateConfigField } = configLoader;
|
|
1552
1557
|
|
|
1553
|
-
// src/utils.ts
|
|
1558
|
+
// src/shared/utils.ts
|
|
1554
1559
|
async function showToast(client, message, variant) {
|
|
1555
1560
|
if (getConfig().quiet_mode) return;
|
|
1556
1561
|
try {
|
|
@@ -1566,28 +1571,57 @@ function debugLog(client, message, extra) {
|
|
|
1566
1571
|
});
|
|
1567
1572
|
}
|
|
1568
1573
|
|
|
1569
|
-
// src/oauth-config
|
|
1574
|
+
// src/claude-code/oauth-config/detect.ts
|
|
1570
1575
|
var CONFIG_SCAN_WINDOW_CHARS = 4096;
|
|
1571
1576
|
var CONFIG_SCAN_LOOKBACK_CHARS = 512;
|
|
1572
|
-
var
|
|
1573
|
-
var
|
|
1577
|
+
var KNOWN_PROD_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
1578
|
+
var POLLUTED_CACHED_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
|
|
1579
|
+
var SAFE_FALLBACK_SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
|
|
1574
1580
|
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1575
1581
|
var CACHE_FILE_NAME = "anthropic-oauth-config-cache.json";
|
|
1576
1582
|
var DEFAULT_OVERRIDE_FILE_NAME = "oauth-config.override.json";
|
|
1577
1583
|
var derivedDefaults = cc_derived_defaults_default;
|
|
1578
|
-
var
|
|
1579
|
-
clientId: derivedDefaults.oauth?.clientId ||
|
|
1580
|
-
authorizeUrl: derivedDefaults.oauth?.authorizeUrl || "https://claude.
|
|
1584
|
+
var fallbackPayload = normalizeDetectedOAuthConfigPayload({
|
|
1585
|
+
clientId: derivedDefaults.oauth?.clientId || KNOWN_PROD_CLIENT_ID,
|
|
1586
|
+
authorizeUrl: derivedDefaults.oauth?.authorizeUrl || "https://claude.ai/oauth/authorize",
|
|
1581
1587
|
tokenUrl: derivedDefaults.oauth?.tokenUrl || "https://platform.claude.com/v1/oauth/token",
|
|
1582
|
-
scopes:
|
|
1583
|
-
baseApiUrl: derivedDefaults.oauth?.baseApiUrl || "https://api.anthropic.com"
|
|
1588
|
+
scopes: derivedDefaults.oauth?.scopes || SAFE_FALLBACK_SCOPES,
|
|
1589
|
+
baseApiUrl: derivedDefaults.oauth?.baseApiUrl || "https://api.anthropic.com"
|
|
1590
|
+
});
|
|
1591
|
+
var FALLBACK = {
|
|
1592
|
+
...fallbackPayload,
|
|
1584
1593
|
source: "fallback"
|
|
1585
1594
|
};
|
|
1586
|
-
function
|
|
1587
|
-
|
|
1588
|
-
|
|
1595
|
+
function hasPollutedCachedScope(scopes) {
|
|
1596
|
+
const parsedScopes = scopes.split(/\s+/).filter(Boolean);
|
|
1597
|
+
return parsedScopes.includes(POLLUTED_CACHED_SCOPE) || !parsedScopes.includes("org:create_api_key");
|
|
1598
|
+
}
|
|
1599
|
+
function filterScopesByBinaryPresence(buf, expected) {
|
|
1600
|
+
const verified = [];
|
|
1601
|
+
for (const scope of expected) {
|
|
1602
|
+
const needle = Buffer.from(`"${scope}"`);
|
|
1603
|
+
if (buf.includes(needle)) {
|
|
1604
|
+
verified.push(scope);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
return verified;
|
|
1608
|
+
}
|
|
1609
|
+
function getVerifiedCanonicalScopes(buf, fallbackScopes) {
|
|
1610
|
+
const expectedScopes = fallbackScopes.split(/\s+/).filter(Boolean);
|
|
1611
|
+
const verifiedScopes = filterScopesByBinaryPresence(buf, expectedScopes);
|
|
1612
|
+
return verifiedScopes.length === expectedScopes.length ? verifiedScopes.join(" ") : null;
|
|
1613
|
+
}
|
|
1614
|
+
function normalizeAuthorizeUrl(url) {
|
|
1615
|
+
if (url === "https://claude.com/cai/oauth/authorize") {
|
|
1616
|
+
return "https://claude.ai/oauth/authorize";
|
|
1589
1617
|
}
|
|
1590
|
-
return
|
|
1618
|
+
return url;
|
|
1619
|
+
}
|
|
1620
|
+
function normalizeDetectedOAuthConfigPayload(payload) {
|
|
1621
|
+
return {
|
|
1622
|
+
...payload,
|
|
1623
|
+
authorizeUrl: normalizeAuthorizeUrl(payload.authorizeUrl)
|
|
1624
|
+
};
|
|
1591
1625
|
}
|
|
1592
1626
|
function pickNearestScopes(block, centerIndex) {
|
|
1593
1627
|
return pickNearestValue(block, centerIndex, /SCOPES\s*:\s*"([^"]+)"/gi) || pickNearestValue(block, centerIndex, /scope[s]?\s*:\s*"([^"]+)"/gi) || null;
|
|
@@ -1611,10 +1645,12 @@ function extractCandidateBlocks(binaryText) {
|
|
|
1611
1645
|
const currentIndex = currentMatch.index ?? 0;
|
|
1612
1646
|
const previousClientIdIndex = clientIdMatches[index - 1]?.index;
|
|
1613
1647
|
const nextClientIdIndex = clientIdMatches[index + 1]?.index;
|
|
1614
|
-
const
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1648
|
+
const { start, end } = getCandidateBlockRange(
|
|
1649
|
+
currentIndex,
|
|
1650
|
+
previousClientIdIndex,
|
|
1651
|
+
nextClientIdIndex,
|
|
1652
|
+
binaryText.length
|
|
1653
|
+
);
|
|
1618
1654
|
const key = `${start}:${end}`;
|
|
1619
1655
|
if (seenRanges.has(key)) {
|
|
1620
1656
|
continue;
|
|
@@ -1627,6 +1663,19 @@ function extractCandidateBlocks(binaryText) {
|
|
|
1627
1663
|
}
|
|
1628
1664
|
return blocks;
|
|
1629
1665
|
}
|
|
1666
|
+
function midpoint(left, right) {
|
|
1667
|
+
return Math.floor((left + right) / 2);
|
|
1668
|
+
}
|
|
1669
|
+
function getCandidateBlockRange(currentIndex, previousClientIdIndex, nextClientIdIndex, textLength) {
|
|
1670
|
+
const boundedLeftEdge = currentIndex - CONFIG_SCAN_LOOKBACK_CHARS;
|
|
1671
|
+
const boundedRightEdge = currentIndex + CONFIG_SCAN_WINDOW_CHARS;
|
|
1672
|
+
const leftBoundary = previousClientIdIndex === void 0 ? boundedLeftEdge : Math.max(boundedLeftEdge, midpoint(previousClientIdIndex, currentIndex));
|
|
1673
|
+
const rightBoundary = nextClientIdIndex === void 0 ? boundedRightEdge : Math.min(boundedRightEdge, midpoint(currentIndex, nextClientIdIndex));
|
|
1674
|
+
return {
|
|
1675
|
+
start: Math.max(0, leftBoundary),
|
|
1676
|
+
end: Math.min(textLength, Math.max(currentIndex + 1, rightBoundary))
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1630
1679
|
function pickNearestValue(block, centerIndex, pattern) {
|
|
1631
1680
|
let nearestValue;
|
|
1632
1681
|
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
@@ -1665,7 +1714,7 @@ function extractCandidateFromBlock(block) {
|
|
|
1665
1714
|
clientId: clientIdMatch[1],
|
|
1666
1715
|
authorizeUrl: authorizeUrl || FALLBACK.authorizeUrl,
|
|
1667
1716
|
tokenUrl: tokenUrl || FALLBACK.tokenUrl,
|
|
1668
|
-
scopes:
|
|
1717
|
+
scopes: extractedScopes || FALLBACK.scopes,
|
|
1669
1718
|
baseApiUrl: baseApiUrl || FALLBACK.baseApiUrl
|
|
1670
1719
|
};
|
|
1671
1720
|
if (!isDetectedOAuthConfigPayload(payload)) {
|
|
@@ -1678,9 +1727,15 @@ function extractCandidateFromBlock(block) {
|
|
|
1678
1727
|
}
|
|
1679
1728
|
var memoizedConfig = null;
|
|
1680
1729
|
var detectorTestOverrides = {};
|
|
1730
|
+
function getPlatform() {
|
|
1731
|
+
return detectorTestOverrides.platform?.() ?? platform();
|
|
1732
|
+
}
|
|
1733
|
+
function fileExists(path) {
|
|
1734
|
+
return (detectorTestOverrides.existsSync ?? existsSync)(path);
|
|
1735
|
+
}
|
|
1681
1736
|
function candidatePaths() {
|
|
1682
1737
|
const home = homedir();
|
|
1683
|
-
if (
|
|
1738
|
+
if (getPlatform() === "win32") {
|
|
1684
1739
|
return [
|
|
1685
1740
|
join(home, ".local", "bin", "claude.exe"),
|
|
1686
1741
|
join(home, "AppData", "Roaming", "npm", "node_modules", "@anthropic-ai", "claude-code", "cli.js"),
|
|
@@ -1700,6 +1755,49 @@ function candidatePaths() {
|
|
|
1700
1755
|
join(home, ".claude", "local", "node_modules", "@anthropic-ai", "claude-code", "cli.mjs")
|
|
1701
1756
|
];
|
|
1702
1757
|
}
|
|
1758
|
+
function enumerateCCCandidates() {
|
|
1759
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1760
|
+
const candidates = [];
|
|
1761
|
+
const currentPlatform = getPlatform();
|
|
1762
|
+
const pathDelimiter = currentPlatform === "win32" ? ";" : ":";
|
|
1763
|
+
const pathDirs = (detectorTestOverrides.pathEnv ?? process.env.PATH ?? "").split(pathDelimiter).filter(Boolean);
|
|
1764
|
+
const pathCandidateNames = currentPlatform === "win32" ? ["claude.exe", "claude.cmd", "claude"] : ["claude"];
|
|
1765
|
+
const addCandidate = (candidatePath) => {
|
|
1766
|
+
const key = currentPlatform === "win32" ? candidatePath.toLowerCase() : candidatePath;
|
|
1767
|
+
if (seen.has(key) || !fileExists(candidatePath)) {
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
seen.add(key);
|
|
1771
|
+
candidates.push(candidatePath);
|
|
1772
|
+
};
|
|
1773
|
+
for (const dir of pathDirs) {
|
|
1774
|
+
for (const fileName of pathCandidateNames) {
|
|
1775
|
+
addCandidate(join(dir, fileName));
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
for (const candidatePath of candidatePaths()) {
|
|
1779
|
+
addCandidate(candidatePath);
|
|
1780
|
+
}
|
|
1781
|
+
return candidates;
|
|
1782
|
+
}
|
|
1783
|
+
function probeOneVersion(binPath) {
|
|
1784
|
+
const currentPlatform = getPlatform();
|
|
1785
|
+
if (currentPlatform === "win32" && /\.(cmd|bat)$/i.test(binPath) && /[&|><^"'%\r\n`$;(){}\[\]]/.test(binPath)) {
|
|
1786
|
+
return null;
|
|
1787
|
+
}
|
|
1788
|
+
try {
|
|
1789
|
+
const output = (detectorTestOverrides.execFileSync ?? defaultExecFileSync2)(binPath, ["--version"], {
|
|
1790
|
+
timeout: 2e3,
|
|
1791
|
+
encoding: "utf-8",
|
|
1792
|
+
windowsHide: true,
|
|
1793
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1794
|
+
shell: currentPlatform === "win32" && /\.(cmd|bat)$/i.test(binPath)
|
|
1795
|
+
});
|
|
1796
|
+
return output.match(/(\d+\.\d+\.\d+(?:[.\-][\w.\-]+)?)/)?.[1] ?? null;
|
|
1797
|
+
} catch {
|
|
1798
|
+
return null;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1703
1801
|
function getCachePath() {
|
|
1704
1802
|
return join(getConfigDir(), CACHE_FILE_NAME);
|
|
1705
1803
|
}
|
|
@@ -1721,13 +1819,17 @@ function isDetectedOAuthConfigPayload(value) {
|
|
|
1721
1819
|
const candidate = value;
|
|
1722
1820
|
return typeof candidate.clientId === "string" && UUID_PATTERN.test(candidate.clientId) && typeof candidate.authorizeUrl === "string" && isValidUrl(candidate.authorizeUrl) && typeof candidate.tokenUrl === "string" && isValidUrl(candidate.tokenUrl) && typeof candidate.scopes === "string" && candidate.scopes.length > 0;
|
|
1723
1821
|
}
|
|
1724
|
-
function
|
|
1822
|
+
function buildResolvedConfig(payload, source, ccPath, ccHash) {
|
|
1725
1823
|
return {
|
|
1726
|
-
...
|
|
1824
|
+
...payload,
|
|
1825
|
+
source,
|
|
1727
1826
|
...ccPath ? { ccPath } : {},
|
|
1728
1827
|
...ccHash ? { ccHash } : {}
|
|
1729
1828
|
};
|
|
1730
1829
|
}
|
|
1830
|
+
function toFallbackConfig(ccPath, ccHash) {
|
|
1831
|
+
return buildResolvedConfig(FALLBACK, "fallback", ccPath, ccHash);
|
|
1832
|
+
}
|
|
1731
1833
|
function isOverrideDisabled() {
|
|
1732
1834
|
return process.env.ANTHROPIC_MULTI_ACCOUNT_OAUTH_DISABLE_OVERRIDE === "1";
|
|
1733
1835
|
}
|
|
@@ -1741,30 +1843,44 @@ function readOverrideString(value) {
|
|
|
1741
1843
|
function getOverridePath() {
|
|
1742
1844
|
return readOverrideString(process.env.ANTHROPIC_MULTI_ACCOUNT_OAUTH_OVERRIDE_PATH) ?? getDefaultOverridePath();
|
|
1743
1845
|
}
|
|
1744
|
-
function
|
|
1846
|
+
function readOverrideRecord(value) {
|
|
1745
1847
|
if (typeof value !== "object" || value === null) {
|
|
1848
|
+
return null;
|
|
1849
|
+
}
|
|
1850
|
+
return value;
|
|
1851
|
+
}
|
|
1852
|
+
function readOverrideField(candidate, key) {
|
|
1853
|
+
const value = candidate[key];
|
|
1854
|
+
return typeof value === "string" ? readOverrideString(value) : void 0;
|
|
1855
|
+
}
|
|
1856
|
+
function readOverrideUrl(candidate, key) {
|
|
1857
|
+
const value = readOverrideField(candidate, key);
|
|
1858
|
+
return value && isValidUrl(value) ? value : void 0;
|
|
1859
|
+
}
|
|
1860
|
+
function normalizeOverride(value) {
|
|
1861
|
+
const candidate = readOverrideRecord(value);
|
|
1862
|
+
if (!candidate) {
|
|
1746
1863
|
return {};
|
|
1747
1864
|
}
|
|
1748
|
-
const candidate = value;
|
|
1749
1865
|
const normalized = {};
|
|
1750
|
-
const clientId =
|
|
1866
|
+
const clientId = readOverrideField(candidate, "clientId");
|
|
1751
1867
|
if (clientId && UUID_PATTERN.test(clientId)) {
|
|
1752
1868
|
normalized.clientId = clientId;
|
|
1753
1869
|
}
|
|
1754
|
-
const authorizeUrl =
|
|
1755
|
-
if (authorizeUrl
|
|
1756
|
-
normalized.authorizeUrl = authorizeUrl;
|
|
1870
|
+
const authorizeUrl = readOverrideUrl(candidate, "authorizeUrl");
|
|
1871
|
+
if (authorizeUrl) {
|
|
1872
|
+
normalized.authorizeUrl = normalizeAuthorizeUrl(authorizeUrl);
|
|
1757
1873
|
}
|
|
1758
|
-
const tokenUrl =
|
|
1759
|
-
if (tokenUrl
|
|
1874
|
+
const tokenUrl = readOverrideUrl(candidate, "tokenUrl");
|
|
1875
|
+
if (tokenUrl) {
|
|
1760
1876
|
normalized.tokenUrl = tokenUrl;
|
|
1761
1877
|
}
|
|
1762
|
-
const scopes =
|
|
1878
|
+
const scopes = readOverrideField(candidate, "scopes");
|
|
1763
1879
|
if (scopes) {
|
|
1764
|
-
normalized.scopes =
|
|
1880
|
+
normalized.scopes = scopes;
|
|
1765
1881
|
}
|
|
1766
|
-
const baseApiUrl =
|
|
1767
|
-
if (baseApiUrl
|
|
1882
|
+
const baseApiUrl = readOverrideUrl(candidate, "baseApiUrl");
|
|
1883
|
+
if (baseApiUrl) {
|
|
1768
1884
|
normalized.baseApiUrl = baseApiUrl;
|
|
1769
1885
|
}
|
|
1770
1886
|
return normalized;
|
|
@@ -1801,16 +1917,22 @@ async function applyManualOverride(baseConfig) {
|
|
|
1801
1917
|
};
|
|
1802
1918
|
}
|
|
1803
1919
|
function findCCBinary() {
|
|
1804
|
-
const override = process.env.
|
|
1805
|
-
if (override &&
|
|
1920
|
+
const override = process.env.ANTHROPIC_CC_PATH;
|
|
1921
|
+
if (override && fileExists(override)) {
|
|
1806
1922
|
return override;
|
|
1807
1923
|
}
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
}
|
|
1924
|
+
const candidates = enumerateCCCandidates();
|
|
1925
|
+
if (candidates.length === 0) {
|
|
1926
|
+
return null;
|
|
1812
1927
|
}
|
|
1813
|
-
|
|
1928
|
+
if (candidates.length === 1) {
|
|
1929
|
+
return candidates[0] ?? null;
|
|
1930
|
+
}
|
|
1931
|
+
const probedCandidates = candidates.map((candidatePath) => {
|
|
1932
|
+
const version = probeOneVersion(candidatePath);
|
|
1933
|
+
return version ? { path: candidatePath, version } : null;
|
|
1934
|
+
}).filter((candidate) => candidate !== null).sort((left, right) => compareVersions(right.version, left.version) ?? 0);
|
|
1935
|
+
return probedCandidates[0]?.path ?? candidates[0] ?? null;
|
|
1814
1936
|
}
|
|
1815
1937
|
async function fingerprintBinary(path) {
|
|
1816
1938
|
const binaryContents = await readFile(path);
|
|
@@ -1819,19 +1941,24 @@ async function fingerprintBinary(path) {
|
|
|
1819
1941
|
function scanBinaryForOAuthConfig(buf) {
|
|
1820
1942
|
const binaryText = buf.toString("latin1");
|
|
1821
1943
|
const candidates = extractCandidateBlocks(binaryText).map(extractCandidateFromBlock).filter((candidate) => candidate !== null).sort((left, right) => right.score - left.score);
|
|
1822
|
-
|
|
1944
|
+
const preferredCandidate = candidates.find((candidate) => candidate.payload.clientId === KNOWN_PROD_CLIENT_ID);
|
|
1945
|
+
return preferredCandidate?.payload ?? candidates[0]?.payload ?? null;
|
|
1946
|
+
}
|
|
1947
|
+
async function readRawCacheEntries() {
|
|
1948
|
+
const raw = await readFile(getCachePath(), "utf-8");
|
|
1949
|
+
const parsed = JSON.parse(raw);
|
|
1950
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.entries !== "object" || parsed.entries === null) {
|
|
1951
|
+
return {};
|
|
1952
|
+
}
|
|
1953
|
+
return parsed.entries;
|
|
1823
1954
|
}
|
|
1824
1955
|
async function loadCache() {
|
|
1825
1956
|
try {
|
|
1826
|
-
const
|
|
1827
|
-
const parsed = JSON.parse(raw);
|
|
1828
|
-
if (typeof parsed !== "object" || parsed === null || typeof parsed.entries !== "object" || parsed.entries === null) {
|
|
1829
|
-
return {};
|
|
1830
|
-
}
|
|
1957
|
+
const rawEntries = await readRawCacheEntries();
|
|
1831
1958
|
const validEntries = {};
|
|
1832
|
-
for (const [hash, value] of Object.entries(
|
|
1833
|
-
if (isDetectedOAuthConfigPayload(value)) {
|
|
1834
|
-
validEntries[hash] = value;
|
|
1959
|
+
for (const [hash, value] of Object.entries(rawEntries)) {
|
|
1960
|
+
if (isDetectedOAuthConfigPayload(value) && !hasPollutedCachedScope(value.scopes)) {
|
|
1961
|
+
validEntries[hash] = normalizeDetectedOAuthConfigPayload(value);
|
|
1835
1962
|
}
|
|
1836
1963
|
}
|
|
1837
1964
|
return validEntries;
|
|
@@ -1842,7 +1969,11 @@ async function loadCache() {
|
|
|
1842
1969
|
async function saveCache(hash, config) {
|
|
1843
1970
|
try {
|
|
1844
1971
|
const cachePath = getCachePath();
|
|
1845
|
-
const currentEntries =
|
|
1972
|
+
const currentEntries = {};
|
|
1973
|
+
try {
|
|
1974
|
+
Object.assign(currentEntries, await readRawCacheEntries());
|
|
1975
|
+
} catch {
|
|
1976
|
+
}
|
|
1846
1977
|
currentEntries[hash] = config;
|
|
1847
1978
|
await mkdir(dirname(cachePath), { recursive: true });
|
|
1848
1979
|
await writeFile(
|
|
@@ -1867,27 +1998,23 @@ async function detectOAuthConfig() {
|
|
|
1867
1998
|
const cachedEntries = await loadCache();
|
|
1868
1999
|
const cachedConfig = cachedEntries[ccHash];
|
|
1869
2000
|
if (cachedConfig) {
|
|
1870
|
-
memoizedConfig = await applyManualOverride(
|
|
1871
|
-
...cachedConfig,
|
|
1872
|
-
source: "cached",
|
|
1873
|
-
ccPath,
|
|
1874
|
-
ccHash
|
|
1875
|
-
});
|
|
2001
|
+
memoizedConfig = await applyManualOverride(buildResolvedConfig(cachedConfig, "cached", ccPath, ccHash));
|
|
1876
2002
|
return memoizedConfig;
|
|
1877
2003
|
}
|
|
1878
2004
|
const readBinaryFile = detectorTestOverrides.readBinaryFile || readFile;
|
|
1879
|
-
const
|
|
2005
|
+
const binaryBuffer = await readBinaryFile(ccPath);
|
|
2006
|
+
const scannedConfig = scanBinaryForOAuthConfig(binaryBuffer);
|
|
1880
2007
|
if (!scannedConfig) {
|
|
1881
2008
|
memoizedConfig = await applyManualOverride(toFallbackConfig(ccPath, ccHash));
|
|
1882
2009
|
return memoizedConfig;
|
|
1883
2010
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
2011
|
+
const verifiedCanonicalScopes = getVerifiedCanonicalScopes(binaryBuffer, FALLBACK.scopes);
|
|
2012
|
+
if (verifiedCanonicalScopes) {
|
|
2013
|
+
scannedConfig.scopes = verifiedCanonicalScopes;
|
|
2014
|
+
}
|
|
2015
|
+
const runtimeDetectedConfig = normalizeDetectedOAuthConfigPayload(scannedConfig);
|
|
2016
|
+
await saveCache(ccHash, runtimeDetectedConfig);
|
|
2017
|
+
memoizedConfig = await applyManualOverride(buildResolvedConfig(runtimeDetectedConfig, "detected", ccPath, ccHash));
|
|
1891
2018
|
return memoizedConfig;
|
|
1892
2019
|
} catch {
|
|
1893
2020
|
memoizedConfig = await applyManualOverride(FALLBACK);
|
|
@@ -1895,7 +2022,7 @@ async function detectOAuthConfig() {
|
|
|
1895
2022
|
}
|
|
1896
2023
|
}
|
|
1897
2024
|
|
|
1898
|
-
// src/fingerprint
|
|
2025
|
+
// src/claude-code/fingerprint/capture.ts
|
|
1899
2026
|
var CURRENT_SCHEMA_VERSION = 1;
|
|
1900
2027
|
var LIVE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
1901
2028
|
var DEFAULT_CAPTURE_TIMEOUT_MS = 1e4;
|
|
@@ -1914,15 +2041,15 @@ var STATIC_HEADER_NAMES = [
|
|
|
1914
2041
|
];
|
|
1915
2042
|
var SUPPORTED_CC_RANGE = {
|
|
1916
2043
|
min: "1.0.0",
|
|
1917
|
-
maxTested: "2.1.
|
|
2044
|
+
maxTested: "2.1.119"
|
|
1918
2045
|
};
|
|
1919
|
-
var bundledTemplate =
|
|
2046
|
+
var bundledTemplate = data_default;
|
|
1920
2047
|
var fingerprintCaptureTestOverrides = {};
|
|
1921
2048
|
function now() {
|
|
1922
2049
|
return fingerprintCaptureTestOverrides.now?.() ?? Date.now();
|
|
1923
2050
|
}
|
|
1924
2051
|
function getCachePath2() {
|
|
1925
|
-
return join2(getConfigDir(), CACHE_FILE_NAME2);
|
|
2052
|
+
return join2(fingerprintCaptureTestOverrides.getConfigDir?.() ?? getConfigDir(), CACHE_FILE_NAME2);
|
|
1926
2053
|
}
|
|
1927
2054
|
function isRecord(value) {
|
|
1928
2055
|
return typeof value === "object" && value !== null;
|
|
@@ -2298,23 +2425,26 @@ function parseVersion(version) {
|
|
|
2298
2425
|
if (!match) {
|
|
2299
2426
|
return null;
|
|
2300
2427
|
}
|
|
2301
|
-
|
|
2428
|
+
const [, major, minor, patch] = match;
|
|
2429
|
+
return [Number(major), Number(minor), Number(patch)];
|
|
2302
2430
|
}
|
|
2303
2431
|
function compareVersions(left, right) {
|
|
2304
|
-
const
|
|
2305
|
-
const
|
|
2306
|
-
if (!
|
|
2432
|
+
const leftParts = parseVersion(left);
|
|
2433
|
+
const rightParts = parseVersion(right);
|
|
2434
|
+
if (!leftParts || !rightParts) {
|
|
2307
2435
|
return null;
|
|
2308
2436
|
}
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
return diff;
|
|
2315
|
-
}
|
|
2437
|
+
const [leftMajor, leftMinor, leftPatch] = leftParts;
|
|
2438
|
+
const [rightMajor, rightMinor, rightPatch] = rightParts;
|
|
2439
|
+
const majorDiff = leftMajor - rightMajor;
|
|
2440
|
+
if (majorDiff !== 0) {
|
|
2441
|
+
return majorDiff;
|
|
2316
2442
|
}
|
|
2317
|
-
|
|
2443
|
+
const minorDiff = leftMinor - rightMinor;
|
|
2444
|
+
if (minorDiff !== 0) {
|
|
2445
|
+
return minorDiff;
|
|
2446
|
+
}
|
|
2447
|
+
return leftPatch - rightPatch;
|
|
2318
2448
|
}
|
|
2319
2449
|
function detectDrift(template, installedOverride) {
|
|
2320
2450
|
const cachedVersion = template.cc_version ?? null;
|
|
@@ -2401,9 +2531,9 @@ function resetFingerprintCaptureForTest() {
|
|
|
2401
2531
|
}
|
|
2402
2532
|
|
|
2403
2533
|
export {
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2534
|
+
getConfig,
|
|
2535
|
+
loadConfig,
|
|
2536
|
+
updateConfigField,
|
|
2407
2537
|
ANTHROPIC_OAUTH_ADAPTER,
|
|
2408
2538
|
ANTHROPIC_USAGE_ENDPOINT,
|
|
2409
2539
|
ANTHROPIC_PROFILE_ENDPOINT,
|
|
@@ -2412,9 +2542,9 @@ export {
|
|
|
2412
2542
|
PLAN_LABELS,
|
|
2413
2543
|
TOKEN_EXPIRY_BUFFER_MS,
|
|
2414
2544
|
TOKEN_REFRESH_TIMEOUT_MS,
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2545
|
+
cc_derived_defaults_default,
|
|
2546
|
+
data_default,
|
|
2547
|
+
detectCliVersion,
|
|
2418
2548
|
showToast,
|
|
2419
2549
|
debugLog,
|
|
2420
2550
|
createMinimalClient,
|
|
@@ -2431,9 +2561,10 @@ export {
|
|
|
2431
2561
|
extractTemplate,
|
|
2432
2562
|
captureLiveTemplateAsync,
|
|
2433
2563
|
refreshLiveFingerprintAsync,
|
|
2564
|
+
compareVersions,
|
|
2434
2565
|
detectDrift,
|
|
2435
2566
|
checkCCCompat,
|
|
2436
2567
|
setFingerprintCaptureTestOverridesForTest,
|
|
2437
2568
|
resetFingerprintCaptureForTest
|
|
2438
2569
|
};
|
|
2439
|
-
//# sourceMappingURL=chunk-
|
|
2570
|
+
//# sourceMappingURL=chunk-II7N6KHL.js.map
|