opencode-anthropic-multi-account 0.2.24 → 0.2.26
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-5PQ3VP24.js} +245 -113
- package/dist/chunk-5PQ3VP24.js.map +1 -0
- package/dist/fingerprint-capture.d.ts +4 -2
- package/dist/fingerprint-capture.js +4 -2
- package/dist/index.js +134 -171
- 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 +8 -7
- 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-28T13:59:47.421Z",
|
|
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,15 +93,16 @@ 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.
|
|
99
101
|
Maintain full orthographic correctness for Korean, including all required diacritical marks, accents, and special characters. Never substitute accented characters with their ASCII equivalents (e.g., never write "nao" for "n\xE3o", "fur" for "f\xFCr", or "loeschen" for "l\xF6schen").
|
|
100
102
|
|
|
103
|
+
# Context management
|
|
101
104
|
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
105
|
|
|
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
106
|
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
107
|
|
|
107
108
|
Current branch: main
|
|
@@ -121,7 +122,7 @@ Recent commits:
|
|
|
121
122
|
description: `Launch a new agent to handle complex, multi-step tasks. Each agent type has specific capabilities and tools available to it.
|
|
122
123
|
|
|
123
124
|
Available agent types and the tools they have access to:
|
|
124
|
-
- Explore: Fast agent
|
|
125
|
+
- Explore: Fast read-only search agent for locating code. Use it to find files by pattern (eg. "src/components/**/*.tsx"), grep for symbols or keywords (eg. "API endpoints"), or answer "where is X defined / which files reference Y." Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis \u2014 it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: "quick" for a single targeted lookup, "medium" for moderate exploration, or "very thorough" to search across multiple locations and naming conventions. (Tools: All tools except Agent, ExitPlanMode, Edit, Write, NotebookEdit)
|
|
125
126
|
- general-purpose: General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. (Tools: *)
|
|
126
127
|
- Plan: Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs. (Tools: All tools except Agent, ExitPlanMode, Edit, Write, NotebookEdit)
|
|
127
128
|
- statusline-setup: Use this agent to configure the user's Claude Code status line setting. (Tools: Read, Edit)
|
|
@@ -347,7 +348,7 @@ The agent starts with no context from this conversation, so the prompt briefs it
|
|
|
347
348
|
},
|
|
348
349
|
{
|
|
349
350
|
name: "Bash",
|
|
350
|
-
description: 'Executes a given bash command and returns its output.\n\nThe working directory persists between commands, but shell state does not. The shell environment is initialized from the user\'s profile (bash or zsh).\n\nIMPORTANT: Avoid using this tool to run `find`, `grep`, `cat`, `head`, `tail`, `sed`, `awk`, or `echo` commands, unless explicitly instructed or after you have verified that a dedicated tool cannot accomplish your task. Instead, use the appropriate dedicated tool as this will provide a much better experience for the user:\n\n - File search: Use Glob (NOT find or ls)\n - Content search: Use Grep (NOT grep or rg)\n - Read files: Use Read (NOT cat/head/tail)\n - Edit files: Use Edit (NOT sed/awk)\n - Write files: Use Write (NOT echo >/cat <<EOF)\n - Communication: Output text directly (NOT echo/printf)\nWhile the Bash tool can do similar things, it\u2019s better to use the built-in tools as they provide a better user experience and make it easier to review tool calls and give permission.\n\n# Instructions\n - If your command will create new directories or files, first use this tool to run `ls` to verify the parent directory exists and is the correct location.\n - Always quote file paths that contain spaces with double quotes in your command (e.g., cd "path with spaces/file.txt")\n - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of `cd`. You may use `cd` if the User explicitly requests it. In particular, never prepend `cd <current-directory>` to a `git` command \u2014 `git` already operates on the current working tree, and the compound triggers a permission prompt.\n - You may specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). By default, your command will timeout after 120000ms (2 minutes).\n - You can use the `run_in_background` parameter to run the command in the background. Only use this if you don\'t need the result immediately and are OK being notified when the command completes later. You do not need to check the output right away - you\'ll be notified when it finishes. You do not need to use \'&\' at the end of the command when using this parameter.\n - When issuing multiple commands:\n - If the commands are independent and can run in parallel, make multiple Bash tool calls in a single message. Example: if you need to run "git status" and "git diff", send a single message with two Bash tool calls in parallel.\n - If the commands depend on each other and must run sequentially, use a single Bash call with \'&&\' to chain them together.\n - Use \';\' only when you need to run commands sequentially but don\'t care if earlier commands fail.\n - DO NOT use newlines to separate commands (newlines are ok in quoted strings).\n - For git commands:\n - Prefer to create a new commit rather than amending an existing commit.\n - Before running destructive operations (e.g., git reset --hard, git push --force, git checkout --), consider whether there is a safer alternative that achieves the same goal. Only use destructive operations when they are truly the best approach.\n - Never skip hooks (--no-verify) or bypass signing (--no-gpg-sign, -c commit.gpgsign=false) unless the user has explicitly asked for it. If a hook fails, investigate and fix the underlying issue.\n - Avoid unnecessary `sleep` commands:\n - Do not sleep between commands that can run immediately \u2014 just run them.\n - Use the Monitor tool to stream events from a background process (each stdout line is a notification). For one-shot "wait until done," use Bash with run_in_background instead.\n - If your command is long running and you would like to be notified when it finishes \u2014 use `run_in_background`. No sleep needed.\n - Do not retry failing commands in a sleep loop \u2014 diagnose the root cause.\n - If waiting for a background task you started with `run_in_background`, you will be notified when it completes \u2014 do not poll.\n - Long leading `sleep` commands are blocked. To poll until a condition is met, use Monitor with an until-loop (e.g. `until <check>; do sleep 2; done`) \u2014 you get a notification when the loop exits. Do not chain shorter sleeps to work around the block.\n\n\n# Committing changes with git\n\nOnly create commits when requested by the user. If unclear, ask first. When the user asks you to create a new git commit, follow these steps carefully:\n\nYou can call multiple tools in a single response. When multiple independent pieces of information are requested and all commands are likely to succeed, run multiple tool calls in parallel for optimal performance. The numbered steps below indicate which commands should be batched in parallel.\n\nGit Safety Protocol:\n- NEVER update the git config\n- NEVER run destructive git commands (push --force, reset --hard, checkout ., restore ., clean -f, branch -D) unless the user explicitly requests these actions. Taking unauthorized destructive actions is unhelpful and can result in lost work, so it\'s best to ONLY run these commands when given direct instructions \n- NEVER skip hooks (--no-verify, --no-gpg-sign, etc) unless the user explicitly requests it\n- NEVER run force push to main/master, warn the user if they request it\n- CRITICAL: Always create NEW commits rather than amending, unless the user explicitly requests a git amend. When a pre-commit hook fails, the commit did NOT happen \u2014 so --amend would modify the PREVIOUS commit, which may result in destroying work or losing previous changes. Instead, after hook failure, fix the issue, re-stage, and create a NEW commit\n- When staging files, prefer adding specific files by name rather than using "git add -A" or "git add .", which can accidentally include sensitive files (.env, credentials) or large binaries\n- NEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise the user will feel that you are being too proactive\n\n1. Run the following bash commands in parallel, each using the Bash tool:\n - Run a git status command to see all untracked files. IMPORTANT: Never use the -uall flag as it can cause memory issues on large repos.\n - Run a git diff command to see both staged and unstaged changes that will be committed.\n - Run a git log command to see recent commit messages, so that you can follow this repository\'s commit message style.\n2. Analyze all staged changes (both previously staged and newly added) and draft a commit message:\n - Summarize the nature of the changes (eg. new feature, enhancement to an existing feature, bug fix, refactoring, test, docs, etc.). Ensure the message accurately reflects the changes and their purpose (i.e. "add" means a wholly new feature, "update" means an enhancement to an existing feature, "fix" means a bug fix, etc.).\n - Do not commit files that likely contain secrets (.env, credentials.json, etc). Warn the user if they specifically request to commit those files\n - Draft a concise (1-2 sentences) commit message that focuses on the "why" rather than the "what"\n - Ensure it accurately reflects the changes and their purpose\n3. Run the following commands in parallel:\n - Add relevant untracked files to the staging area.\n - Create the commit with a message ending with:\n Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>\n - Run git status after the commit completes to verify success.\n Note: git status depends on the commit completing, so run it sequentially after the commit.\n4. If the commit fails due to pre-commit hook: fix the issue and create a NEW commit\n\nImportant notes:\n- NEVER run additional commands to read or explore code, besides git bash commands\n- NEVER use the TodoWrite or Agent tools\n- DO NOT push to the remote repository unless the user explicitly asks you to do so\n- IMPORTANT: Never use git commands with the -i flag (like git rebase -i or git add -i) since they require interactive input which is not supported.\n- IMPORTANT: Do not use --no-edit with git rebase commands, as the --no-edit flag is not a valid option for git rebase.\n- If there are no changes to commit (i.e., no untracked files and no modifications), do not create an empty commit\n- In order to ensure good formatting, ALWAYS pass the commit message via a HEREDOC, a la this example:\n<example>\ngit commit -m "$(cat <<\'EOF\'\n Commit message here.\n\n Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>\n EOF\n )"\n</example>\n\n# Creating pull requests\nUse the gh command via the Bash tool for ALL GitHub-related tasks including working with issues, pull requests, checks, and releases. If given a Github URL use the gh command to get the information needed.\n\nIMPORTANT: When the user asks you to create a pull request, follow these steps carefully:\n\n1. Run the following bash commands in parallel using the Bash tool, in order to understand the current state of the branch since it diverged from the main branch:\n - Run a git status command to see all untracked files (never use -uall flag)\n - Run a git diff command to see both staged and unstaged changes that will be committed\n - Check if the current branch tracks a remote branch and is up to date with the remote, so you know if you need to push to the remote\n - Run a git log command and `git diff [base-branch]...HEAD` to understand the full commit history for the current branch (from the time it diverged from the base branch)\n2. Analyze all changes that will be included in the pull request, making sure to look at all relevant commits (NOT just the latest commit, but ALL commits that will be included in the pull request!!!), and draft a pull request title and summary:\n - Keep the PR title short (under 70 characters)\n - Use the description/body for details, not the title\n3. Run the following commands in parallel:\n - Create new branch if needed\n - Push to remote with -u flag if needed\n - Create PR using gh pr create with the format below. Use a HEREDOC to pass the body to ensure correct formatting.\n<example>\ngh pr create --title "the pr title" --body "$(cat <<\'EOF\'\n## Summary\n<1-3 bullet points>\n\n## Test plan\n[Bulleted markdown checklist of TODOs for testing the pull request...]\n\n\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)\nEOF\n)"\n</example>\n\nImportant:\n- DO NOT use the TodoWrite or Agent tools\n- Return the PR URL when you\'re done, so the user can see it\n\n# Other common operations\n- View comments on a Github PR: gh api repos/foo/bar/pulls/123/comments',
|
|
351
|
+
description: 'Executes a given bash command and returns its output.\n\nThe working directory persists between commands, but shell state does not. The shell environment is initialized from the user\'s profile (bash or zsh).\n\nIMPORTANT: Avoid using this tool to run `find`, `grep`, `cat`, `head`, `tail`, `sed`, `awk`, or `echo` commands, unless explicitly instructed or after you have verified that a dedicated tool cannot accomplish your task. Instead, use the appropriate dedicated tool as this will provide a much better experience for the user:\n\n - File search: Use Glob (NOT find or ls)\n - Content search: Use Grep (NOT grep or rg)\n - Read files: Use Read (NOT cat/head/tail)\n - Edit files: Use Edit (NOT sed/awk)\n - Write files: Use Write (NOT echo >/cat <<EOF)\n - Communication: Output text directly (NOT echo/printf)\nWhile the Bash tool can do similar things, it\u2019s better to use the built-in tools as they provide a better user experience and make it easier to review tool calls and give permission.\n\n# Instructions\n - If your command will create new directories or files, first use this tool to run `ls` to verify the parent directory exists and is the correct location.\n - Always quote file paths that contain spaces with double quotes in your command (e.g., cd "path with spaces/file.txt")\n - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of `cd`. You may use `cd` if the User explicitly requests it. In particular, never prepend `cd <current-directory>` to a `git` command \u2014 `git` already operates on the current working tree, and the compound triggers a permission prompt.\n - You may specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). By default, your command will timeout after 120000ms (2 minutes).\n - You can use the `run_in_background` parameter to run the command in the background. Only use this if you don\'t need the result immediately and are OK being notified when the command completes later. You do not need to check the output right away - you\'ll be notified when it finishes. You do not need to use \'&\' at the end of the command when using this parameter.\n - For git commands:\n - Prefer to create a new commit rather than amending an existing commit.\n - Before running destructive operations (e.g., git reset --hard, git push --force, git checkout --), consider whether there is a safer alternative that achieves the same goal. Only use destructive operations when they are truly the best approach.\n - Never skip hooks (--no-verify) or bypass signing (--no-gpg-sign, -c commit.gpgsign=false) unless the user has explicitly asked for it. If a hook fails, investigate and fix the underlying issue.\n - Avoid unnecessary `sleep` commands:\n - Do not sleep between commands that can run immediately \u2014 just run them.\n - Use the Monitor tool to stream events from a background process (each stdout line is a notification). For one-shot "wait until done," use Bash with run_in_background instead.\n - If your command is long running and you would like to be notified when it finishes \u2014 use `run_in_background`. No sleep needed.\n - Do not retry failing commands in a sleep loop \u2014 diagnose the root cause.\n - If waiting for a background task you started with `run_in_background`, you will be notified when it completes \u2014 do not poll.\n - Long leading `sleep` commands are blocked. To poll until a condition is met, use Monitor with an until-loop (e.g. `until <check>; do sleep 2; done`) \u2014 you get a notification when the loop exits. Do not chain shorter sleeps to work around the block.\n\n\n# Committing changes with git\n\nOnly create commits when requested by the user. If unclear, ask first. When the user asks you to create a new git commit, follow these steps carefully:\n\nYou can call multiple tools in a single response. When multiple independent pieces of information are requested and all commands are likely to succeed, run multiple tool calls in parallel for optimal performance. The numbered steps below indicate which commands should be batched in parallel.\n\nGit Safety Protocol:\n- NEVER update the git config\n- NEVER run destructive git commands (push --force, reset --hard, checkout ., restore ., clean -f, branch -D) unless the user explicitly requests these actions. Taking unauthorized destructive actions is unhelpful and can result in lost work, so it\'s best to ONLY run these commands when given direct instructions \n- NEVER skip hooks (--no-verify, --no-gpg-sign, etc) unless the user explicitly requests it\n- NEVER run force push to main/master, warn the user if they request it\n- CRITICAL: Always create NEW commits rather than amending, unless the user explicitly requests a git amend. When a pre-commit hook fails, the commit did NOT happen \u2014 so --amend would modify the PREVIOUS commit, which may result in destroying work or losing previous changes. Instead, after hook failure, fix the issue, re-stage, and create a NEW commit\n- When staging files, prefer adding specific files by name rather than using "git add -A" or "git add .", which can accidentally include sensitive files (.env, credentials) or large binaries\n- NEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise the user will feel that you are being too proactive\n\n1. Run the following bash commands in parallel, each using the Bash tool:\n - Run a git status command to see all untracked files. IMPORTANT: Never use the -uall flag as it can cause memory issues on large repos.\n - Run a git diff command to see both staged and unstaged changes that will be committed.\n - Run a git log command to see recent commit messages, so that you can follow this repository\'s commit message style.\n2. Analyze all staged changes (both previously staged and newly added) and draft a commit message:\n - Summarize the nature of the changes (eg. new feature, enhancement to an existing feature, bug fix, refactoring, test, docs, etc.). Ensure the message accurately reflects the changes and their purpose (i.e. "add" means a wholly new feature, "update" means an enhancement to an existing feature, "fix" means a bug fix, etc.).\n - Do not commit files that likely contain secrets (.env, credentials.json, etc). Warn the user if they specifically request to commit those files\n - Draft a concise (1-2 sentences) commit message that focuses on the "why" rather than the "what"\n - Ensure it accurately reflects the changes and their purpose\n3. Run the following commands in parallel:\n - Add relevant untracked files to the staging area.\n - Create the commit with a message ending with:\n Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>\n - Run git status after the commit completes to verify success.\n Note: git status depends on the commit completing, so run it sequentially after the commit.\n4. If the commit fails due to pre-commit hook: fix the issue and create a NEW commit\n\nImportant notes:\n- NEVER run additional commands to read or explore code, besides git bash commands\n- NEVER use the TodoWrite or Agent tools\n- DO NOT push to the remote repository unless the user explicitly asks you to do so\n- IMPORTANT: Never use git commands with the -i flag (like git rebase -i or git add -i) since they require interactive input which is not supported.\n- IMPORTANT: Do not use --no-edit with git rebase commands, as the --no-edit flag is not a valid option for git rebase.\n- If there are no changes to commit (i.e., no untracked files and no modifications), do not create an empty commit\n- In order to ensure good formatting, ALWAYS pass the commit message via a HEREDOC, a la this example:\n<example>\ngit commit -m "$(cat <<\'EOF\'\n Commit message here.\n\n Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>\n EOF\n )"\n</example>\n\n# Creating pull requests\nUse the gh command via the Bash tool for ALL GitHub-related tasks including working with issues, pull requests, checks, and releases. If given a Github URL use the gh command to get the information needed.\n\nIMPORTANT: When the user asks you to create a pull request, follow these steps carefully:\n\n1. Run the following bash commands in parallel using the Bash tool, in order to understand the current state of the branch since it diverged from the main branch:\n - Run a git status command to see all untracked files (never use -uall flag)\n - Run a git diff command to see both staged and unstaged changes that will be committed\n - Check if the current branch tracks a remote branch and is up to date with the remote, so you know if you need to push to the remote\n - Run a git log command and `git diff [base-branch]...HEAD` to understand the full commit history for the current branch (from the time it diverged from the base branch)\n2. Analyze all changes that will be included in the pull request, making sure to look at all relevant commits (NOT just the latest commit, but ALL commits that will be included in the pull request!!!), and draft a pull request title and summary:\n - Keep the PR title short (under 70 characters)\n - Use the description/body for details, not the title\n3. Run the following commands in parallel:\n - Create new branch if needed\n - Push to remote with -u flag if needed\n - Create PR using gh pr create with the format below. Use a HEREDOC to pass the body to ensure correct formatting.\n<example>\ngh pr create --title "the pr title" --body "$(cat <<\'EOF\'\n## Summary\n<1-3 bullet points>\n\n## Test plan\n[Bulleted markdown checklist of TODOs for testing the pull request...]\n\n\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)\nEOF\n)"\n</example>\n\nImportant:\n- DO NOT use the TodoWrite or Agent tools\n- Return the PR URL when you\'re done, so the user can see it\n\n# Other common operations\n- View comments on a Github PR: gh api repos/foo/bar/pulls/123/comments',
|
|
351
352
|
input_schema: {
|
|
352
353
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
353
354
|
type: "object",
|
|
@@ -779,7 +780,7 @@ Ensure your plan is complete and unambiguous:
|
|
|
779
780
|
},
|
|
780
781
|
{
|
|
781
782
|
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\
|
|
783
|
+
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
784
|
input_schema: {
|
|
784
785
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
785
786
|
type: "object",
|
|
@@ -889,7 +890,7 @@ If the result says the push wasn't sent, that's expected \u2014 no action needed
|
|
|
889
890
|
},
|
|
890
891
|
{
|
|
891
892
|
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
|
|
893
|
+
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.\n- Do NOT re-read a file you just edited to verify \u2014 Edit/Write would have errored if the change failed, and the harness tracks file state for you.',
|
|
893
894
|
input_schema: {
|
|
894
895
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
895
896
|
type: "object",
|
|
@@ -1418,7 +1419,7 @@ IMPORTANT - Use the correct year in search queries:
|
|
|
1418
1419
|
"Write"
|
|
1419
1420
|
],
|
|
1420
1421
|
anthropic_beta: "claude-code-20250219,oauth-2025-04-20,context-1m-2025-08-07,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",
|
|
1421
|
-
cc_version: "2.1.
|
|
1422
|
+
cc_version: "2.1.121",
|
|
1422
1423
|
header_order: [
|
|
1423
1424
|
"Accept",
|
|
1424
1425
|
"Authorization",
|
|
@@ -1448,7 +1449,7 @@ IMPORTANT - Use the correct year in search queries:
|
|
|
1448
1449
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
1449
1450
|
"anthropic-version": "2023-06-01",
|
|
1450
1451
|
"content-type": "application/json",
|
|
1451
|
-
"user-agent": "claude-cli/2.1.
|
|
1452
|
+
"user-agent": "claude-cli/2.1.121 (external, sdk-cli)",
|
|
1452
1453
|
"x-app": "cli",
|
|
1453
1454
|
"x-stainless-timeout": "600"
|
|
1454
1455
|
},
|
|
@@ -1466,15 +1467,22 @@ IMPORTANT - Use the correct year in search queries:
|
|
|
1466
1467
|
]
|
|
1467
1468
|
};
|
|
1468
1469
|
|
|
1469
|
-
// src/cli-version.ts
|
|
1470
|
-
import { execFileSync } from "child_process";
|
|
1471
|
-
var DEFAULT_CLI_VERSION =
|
|
1470
|
+
// src/claude-code/cli-version.ts
|
|
1471
|
+
import { execFileSync as defaultExecFileSync } from "child_process";
|
|
1472
|
+
var DEFAULT_CLI_VERSION = data_default.cc_version;
|
|
1472
1473
|
var CLI_VERSION_PATTERN = /(\d+\.\d+\.\d+)/;
|
|
1473
1474
|
var CLAUDE_VERSION_TIMEOUT_MS = 3e3;
|
|
1474
1475
|
var detectedVersion = null;
|
|
1476
|
+
var cliVersionProbe = defaultExecFileSync;
|
|
1475
1477
|
function parseCliVersion(output) {
|
|
1476
1478
|
return output.match(CLI_VERSION_PATTERN)?.[1] ?? null;
|
|
1477
1479
|
}
|
|
1480
|
+
function probeCliVersion() {
|
|
1481
|
+
return cliVersionProbe("claude", ["--version"], {
|
|
1482
|
+
encoding: "utf8",
|
|
1483
|
+
timeout: CLAUDE_VERSION_TIMEOUT_MS
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1478
1486
|
function detectCliVersion() {
|
|
1479
1487
|
if (detectedVersion !== null) {
|
|
1480
1488
|
return detectedVersion;
|
|
@@ -1485,10 +1493,7 @@ function detectCliVersion() {
|
|
|
1485
1493
|
return detectedVersion;
|
|
1486
1494
|
}
|
|
1487
1495
|
try {
|
|
1488
|
-
const output =
|
|
1489
|
-
encoding: "utf8",
|
|
1490
|
-
timeout: CLAUDE_VERSION_TIMEOUT_MS
|
|
1491
|
-
});
|
|
1496
|
+
const output = probeCliVersion();
|
|
1492
1497
|
detectedVersion = parseCliVersion(output) ?? DEFAULT_CLI_VERSION;
|
|
1493
1498
|
} catch {
|
|
1494
1499
|
detectedVersion = DEFAULT_CLI_VERSION;
|
|
@@ -1496,8 +1501,9 @@ function detectCliVersion() {
|
|
|
1496
1501
|
return detectedVersion;
|
|
1497
1502
|
}
|
|
1498
1503
|
|
|
1499
|
-
// src/oauth-config
|
|
1504
|
+
// src/claude-code/oauth-config/detect.ts
|
|
1500
1505
|
import { createHash } from "crypto";
|
|
1506
|
+
import { execFileSync as defaultExecFileSync2 } from "child_process";
|
|
1501
1507
|
import { existsSync } from "fs";
|
|
1502
1508
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
1503
1509
|
import { homedir, platform } from "os";
|
|
@@ -1513,14 +1519,14 @@ var cc_derived_defaults_default = {
|
|
|
1513
1519
|
},
|
|
1514
1520
|
oauth: {
|
|
1515
1521
|
clientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
1516
|
-
authorizeUrl: "https://claude.
|
|
1522
|
+
authorizeUrl: "https://claude.ai/oauth/authorize",
|
|
1517
1523
|
tokenUrl: "https://platform.claude.com/v1/oauth/token",
|
|
1518
|
-
scopes: "user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload",
|
|
1524
|
+
scopes: "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload",
|
|
1519
1525
|
baseApiUrl: "https://api.anthropic.com"
|
|
1520
1526
|
}
|
|
1521
1527
|
};
|
|
1522
1528
|
|
|
1523
|
-
// src/utils.ts
|
|
1529
|
+
// src/shared/utils.ts
|
|
1524
1530
|
import {
|
|
1525
1531
|
createMinimalClient,
|
|
1526
1532
|
formatWaitTime,
|
|
@@ -1530,7 +1536,7 @@ import {
|
|
|
1530
1536
|
sleep
|
|
1531
1537
|
} from "opencode-multi-account-core";
|
|
1532
1538
|
|
|
1533
|
-
// src/constants.ts
|
|
1539
|
+
// src/shared/constants.ts
|
|
1534
1540
|
import { anthropicOAuthAdapter } from "opencode-multi-account-core";
|
|
1535
1541
|
var ANTHROPIC_OAUTH_ADAPTER = anthropicOAuthAdapter;
|
|
1536
1542
|
var ANTHROPIC_CLIENT_ID = ANTHROPIC_OAUTH_ADAPTER.oauthClientId;
|
|
@@ -1543,14 +1549,14 @@ var PLAN_LABELS = ANTHROPIC_OAUTH_ADAPTER.planLabels;
|
|
|
1543
1549
|
var TOKEN_EXPIRY_BUFFER_MS = 6e4;
|
|
1544
1550
|
var TOKEN_REFRESH_TIMEOUT_MS = 3e4;
|
|
1545
1551
|
|
|
1546
|
-
// src/config.ts
|
|
1552
|
+
// src/shared/config.ts
|
|
1547
1553
|
import {
|
|
1548
1554
|
createConfigLoader
|
|
1549
1555
|
} from "opencode-multi-account-core";
|
|
1550
1556
|
var configLoader = createConfigLoader("claude-multiauth.json");
|
|
1551
1557
|
var { getConfig, loadConfig, resetConfigCache, updateConfigField } = configLoader;
|
|
1552
1558
|
|
|
1553
|
-
// src/utils.ts
|
|
1559
|
+
// src/shared/utils.ts
|
|
1554
1560
|
async function showToast(client, message, variant) {
|
|
1555
1561
|
if (getConfig().quiet_mode) return;
|
|
1556
1562
|
try {
|
|
@@ -1566,28 +1572,57 @@ function debugLog(client, message, extra) {
|
|
|
1566
1572
|
});
|
|
1567
1573
|
}
|
|
1568
1574
|
|
|
1569
|
-
// src/oauth-config
|
|
1575
|
+
// src/claude-code/oauth-config/detect.ts
|
|
1570
1576
|
var CONFIG_SCAN_WINDOW_CHARS = 4096;
|
|
1571
1577
|
var CONFIG_SCAN_LOOKBACK_CHARS = 512;
|
|
1572
|
-
var
|
|
1573
|
-
var
|
|
1578
|
+
var KNOWN_PROD_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
1579
|
+
var POLLUTED_CACHED_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
|
|
1580
|
+
var SAFE_FALLBACK_SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
|
|
1574
1581
|
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
1582
|
var CACHE_FILE_NAME = "anthropic-oauth-config-cache.json";
|
|
1576
1583
|
var DEFAULT_OVERRIDE_FILE_NAME = "oauth-config.override.json";
|
|
1577
1584
|
var derivedDefaults = cc_derived_defaults_default;
|
|
1578
|
-
var
|
|
1579
|
-
clientId: derivedDefaults.oauth?.clientId ||
|
|
1580
|
-
authorizeUrl: derivedDefaults.oauth?.authorizeUrl || "https://claude.
|
|
1585
|
+
var fallbackPayload = normalizeDetectedOAuthConfigPayload({
|
|
1586
|
+
clientId: derivedDefaults.oauth?.clientId || KNOWN_PROD_CLIENT_ID,
|
|
1587
|
+
authorizeUrl: derivedDefaults.oauth?.authorizeUrl || "https://claude.ai/oauth/authorize",
|
|
1581
1588
|
tokenUrl: derivedDefaults.oauth?.tokenUrl || "https://platform.claude.com/v1/oauth/token",
|
|
1582
|
-
scopes:
|
|
1583
|
-
baseApiUrl: derivedDefaults.oauth?.baseApiUrl || "https://api.anthropic.com"
|
|
1589
|
+
scopes: derivedDefaults.oauth?.scopes || SAFE_FALLBACK_SCOPES,
|
|
1590
|
+
baseApiUrl: derivedDefaults.oauth?.baseApiUrl || "https://api.anthropic.com"
|
|
1591
|
+
});
|
|
1592
|
+
var FALLBACK = {
|
|
1593
|
+
...fallbackPayload,
|
|
1584
1594
|
source: "fallback"
|
|
1585
1595
|
};
|
|
1586
|
-
function
|
|
1587
|
-
|
|
1588
|
-
|
|
1596
|
+
function hasPollutedCachedScope(scopes) {
|
|
1597
|
+
const parsedScopes = scopes.split(/\s+/).filter(Boolean);
|
|
1598
|
+
return parsedScopes.includes(POLLUTED_CACHED_SCOPE) || !parsedScopes.includes("org:create_api_key");
|
|
1599
|
+
}
|
|
1600
|
+
function filterScopesByBinaryPresence(buf, expected) {
|
|
1601
|
+
const verified = [];
|
|
1602
|
+
for (const scope of expected) {
|
|
1603
|
+
const needle = Buffer.from(`"${scope}"`);
|
|
1604
|
+
if (buf.includes(needle)) {
|
|
1605
|
+
verified.push(scope);
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
return verified;
|
|
1609
|
+
}
|
|
1610
|
+
function getVerifiedCanonicalScopes(buf, fallbackScopes) {
|
|
1611
|
+
const expectedScopes = fallbackScopes.split(/\s+/).filter(Boolean);
|
|
1612
|
+
const verifiedScopes = filterScopesByBinaryPresence(buf, expectedScopes);
|
|
1613
|
+
return verifiedScopes.length === expectedScopes.length ? verifiedScopes.join(" ") : null;
|
|
1614
|
+
}
|
|
1615
|
+
function normalizeAuthorizeUrl(url) {
|
|
1616
|
+
if (url === "https://claude.com/cai/oauth/authorize") {
|
|
1617
|
+
return "https://claude.ai/oauth/authorize";
|
|
1589
1618
|
}
|
|
1590
|
-
return
|
|
1619
|
+
return url;
|
|
1620
|
+
}
|
|
1621
|
+
function normalizeDetectedOAuthConfigPayload(payload) {
|
|
1622
|
+
return {
|
|
1623
|
+
...payload,
|
|
1624
|
+
authorizeUrl: normalizeAuthorizeUrl(payload.authorizeUrl)
|
|
1625
|
+
};
|
|
1591
1626
|
}
|
|
1592
1627
|
function pickNearestScopes(block, centerIndex) {
|
|
1593
1628
|
return pickNearestValue(block, centerIndex, /SCOPES\s*:\s*"([^"]+)"/gi) || pickNearestValue(block, centerIndex, /scope[s]?\s*:\s*"([^"]+)"/gi) || null;
|
|
@@ -1611,10 +1646,12 @@ function extractCandidateBlocks(binaryText) {
|
|
|
1611
1646
|
const currentIndex = currentMatch.index ?? 0;
|
|
1612
1647
|
const previousClientIdIndex = clientIdMatches[index - 1]?.index;
|
|
1613
1648
|
const nextClientIdIndex = clientIdMatches[index + 1]?.index;
|
|
1614
|
-
const
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1649
|
+
const { start, end } = getCandidateBlockRange(
|
|
1650
|
+
currentIndex,
|
|
1651
|
+
previousClientIdIndex,
|
|
1652
|
+
nextClientIdIndex,
|
|
1653
|
+
binaryText.length
|
|
1654
|
+
);
|
|
1618
1655
|
const key = `${start}:${end}`;
|
|
1619
1656
|
if (seenRanges.has(key)) {
|
|
1620
1657
|
continue;
|
|
@@ -1627,6 +1664,19 @@ function extractCandidateBlocks(binaryText) {
|
|
|
1627
1664
|
}
|
|
1628
1665
|
return blocks;
|
|
1629
1666
|
}
|
|
1667
|
+
function midpoint(left, right) {
|
|
1668
|
+
return Math.floor((left + right) / 2);
|
|
1669
|
+
}
|
|
1670
|
+
function getCandidateBlockRange(currentIndex, previousClientIdIndex, nextClientIdIndex, textLength) {
|
|
1671
|
+
const boundedLeftEdge = currentIndex - CONFIG_SCAN_LOOKBACK_CHARS;
|
|
1672
|
+
const boundedRightEdge = currentIndex + CONFIG_SCAN_WINDOW_CHARS;
|
|
1673
|
+
const leftBoundary = previousClientIdIndex === void 0 ? boundedLeftEdge : Math.max(boundedLeftEdge, midpoint(previousClientIdIndex, currentIndex));
|
|
1674
|
+
const rightBoundary = nextClientIdIndex === void 0 ? boundedRightEdge : Math.min(boundedRightEdge, midpoint(currentIndex, nextClientIdIndex));
|
|
1675
|
+
return {
|
|
1676
|
+
start: Math.max(0, leftBoundary),
|
|
1677
|
+
end: Math.min(textLength, Math.max(currentIndex + 1, rightBoundary))
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1630
1680
|
function pickNearestValue(block, centerIndex, pattern) {
|
|
1631
1681
|
let nearestValue;
|
|
1632
1682
|
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
@@ -1665,7 +1715,7 @@ function extractCandidateFromBlock(block) {
|
|
|
1665
1715
|
clientId: clientIdMatch[1],
|
|
1666
1716
|
authorizeUrl: authorizeUrl || FALLBACK.authorizeUrl,
|
|
1667
1717
|
tokenUrl: tokenUrl || FALLBACK.tokenUrl,
|
|
1668
|
-
scopes:
|
|
1718
|
+
scopes: extractedScopes || FALLBACK.scopes,
|
|
1669
1719
|
baseApiUrl: baseApiUrl || FALLBACK.baseApiUrl
|
|
1670
1720
|
};
|
|
1671
1721
|
if (!isDetectedOAuthConfigPayload(payload)) {
|
|
@@ -1678,9 +1728,15 @@ function extractCandidateFromBlock(block) {
|
|
|
1678
1728
|
}
|
|
1679
1729
|
var memoizedConfig = null;
|
|
1680
1730
|
var detectorTestOverrides = {};
|
|
1731
|
+
function getPlatform() {
|
|
1732
|
+
return detectorTestOverrides.platform?.() ?? platform();
|
|
1733
|
+
}
|
|
1734
|
+
function fileExists(path) {
|
|
1735
|
+
return (detectorTestOverrides.existsSync ?? existsSync)(path);
|
|
1736
|
+
}
|
|
1681
1737
|
function candidatePaths() {
|
|
1682
1738
|
const home = homedir();
|
|
1683
|
-
if (
|
|
1739
|
+
if (getPlatform() === "win32") {
|
|
1684
1740
|
return [
|
|
1685
1741
|
join(home, ".local", "bin", "claude.exe"),
|
|
1686
1742
|
join(home, "AppData", "Roaming", "npm", "node_modules", "@anthropic-ai", "claude-code", "cli.js"),
|
|
@@ -1700,6 +1756,49 @@ function candidatePaths() {
|
|
|
1700
1756
|
join(home, ".claude", "local", "node_modules", "@anthropic-ai", "claude-code", "cli.mjs")
|
|
1701
1757
|
];
|
|
1702
1758
|
}
|
|
1759
|
+
function enumerateCCCandidates() {
|
|
1760
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1761
|
+
const candidates = [];
|
|
1762
|
+
const currentPlatform = getPlatform();
|
|
1763
|
+
const pathDelimiter = currentPlatform === "win32" ? ";" : ":";
|
|
1764
|
+
const pathDirs = (detectorTestOverrides.pathEnv ?? process.env.PATH ?? "").split(pathDelimiter).filter(Boolean);
|
|
1765
|
+
const pathCandidateNames = currentPlatform === "win32" ? ["claude.exe", "claude.cmd", "claude"] : ["claude"];
|
|
1766
|
+
const addCandidate = (candidatePath) => {
|
|
1767
|
+
const key = currentPlatform === "win32" ? candidatePath.toLowerCase() : candidatePath;
|
|
1768
|
+
if (seen.has(key) || !fileExists(candidatePath)) {
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
seen.add(key);
|
|
1772
|
+
candidates.push(candidatePath);
|
|
1773
|
+
};
|
|
1774
|
+
for (const dir of pathDirs) {
|
|
1775
|
+
for (const fileName of pathCandidateNames) {
|
|
1776
|
+
addCandidate(join(dir, fileName));
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
for (const candidatePath of candidatePaths()) {
|
|
1780
|
+
addCandidate(candidatePath);
|
|
1781
|
+
}
|
|
1782
|
+
return candidates;
|
|
1783
|
+
}
|
|
1784
|
+
function probeOneVersion(binPath) {
|
|
1785
|
+
const currentPlatform = getPlatform();
|
|
1786
|
+
if (currentPlatform === "win32" && /\.(cmd|bat)$/i.test(binPath) && /[&|><^"'%\r\n`$;(){}\[\]]/.test(binPath)) {
|
|
1787
|
+
return null;
|
|
1788
|
+
}
|
|
1789
|
+
try {
|
|
1790
|
+
const output = (detectorTestOverrides.execFileSync ?? defaultExecFileSync2)(binPath, ["--version"], {
|
|
1791
|
+
timeout: 2e3,
|
|
1792
|
+
encoding: "utf-8",
|
|
1793
|
+
windowsHide: true,
|
|
1794
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1795
|
+
shell: currentPlatform === "win32" && /\.(cmd|bat)$/i.test(binPath)
|
|
1796
|
+
});
|
|
1797
|
+
return output.match(/(\d+\.\d+\.\d+(?:[.\-][\w.\-]+)?)/)?.[1] ?? null;
|
|
1798
|
+
} catch {
|
|
1799
|
+
return null;
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1703
1802
|
function getCachePath() {
|
|
1704
1803
|
return join(getConfigDir(), CACHE_FILE_NAME);
|
|
1705
1804
|
}
|
|
@@ -1721,13 +1820,17 @@ function isDetectedOAuthConfigPayload(value) {
|
|
|
1721
1820
|
const candidate = value;
|
|
1722
1821
|
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
1822
|
}
|
|
1724
|
-
function
|
|
1823
|
+
function buildResolvedConfig(payload, source, ccPath, ccHash) {
|
|
1725
1824
|
return {
|
|
1726
|
-
...
|
|
1825
|
+
...payload,
|
|
1826
|
+
source,
|
|
1727
1827
|
...ccPath ? { ccPath } : {},
|
|
1728
1828
|
...ccHash ? { ccHash } : {}
|
|
1729
1829
|
};
|
|
1730
1830
|
}
|
|
1831
|
+
function toFallbackConfig(ccPath, ccHash) {
|
|
1832
|
+
return buildResolvedConfig(FALLBACK, "fallback", ccPath, ccHash);
|
|
1833
|
+
}
|
|
1731
1834
|
function isOverrideDisabled() {
|
|
1732
1835
|
return process.env.ANTHROPIC_MULTI_ACCOUNT_OAUTH_DISABLE_OVERRIDE === "1";
|
|
1733
1836
|
}
|
|
@@ -1741,30 +1844,44 @@ function readOverrideString(value) {
|
|
|
1741
1844
|
function getOverridePath() {
|
|
1742
1845
|
return readOverrideString(process.env.ANTHROPIC_MULTI_ACCOUNT_OAUTH_OVERRIDE_PATH) ?? getDefaultOverridePath();
|
|
1743
1846
|
}
|
|
1744
|
-
function
|
|
1847
|
+
function readOverrideRecord(value) {
|
|
1745
1848
|
if (typeof value !== "object" || value === null) {
|
|
1849
|
+
return null;
|
|
1850
|
+
}
|
|
1851
|
+
return value;
|
|
1852
|
+
}
|
|
1853
|
+
function readOverrideField(candidate, key) {
|
|
1854
|
+
const value = candidate[key];
|
|
1855
|
+
return typeof value === "string" ? readOverrideString(value) : void 0;
|
|
1856
|
+
}
|
|
1857
|
+
function readOverrideUrl(candidate, key) {
|
|
1858
|
+
const value = readOverrideField(candidate, key);
|
|
1859
|
+
return value && isValidUrl(value) ? value : void 0;
|
|
1860
|
+
}
|
|
1861
|
+
function normalizeOverride(value) {
|
|
1862
|
+
const candidate = readOverrideRecord(value);
|
|
1863
|
+
if (!candidate) {
|
|
1746
1864
|
return {};
|
|
1747
1865
|
}
|
|
1748
|
-
const candidate = value;
|
|
1749
1866
|
const normalized = {};
|
|
1750
|
-
const clientId =
|
|
1867
|
+
const clientId = readOverrideField(candidate, "clientId");
|
|
1751
1868
|
if (clientId && UUID_PATTERN.test(clientId)) {
|
|
1752
1869
|
normalized.clientId = clientId;
|
|
1753
1870
|
}
|
|
1754
|
-
const authorizeUrl =
|
|
1755
|
-
if (authorizeUrl
|
|
1756
|
-
normalized.authorizeUrl = authorizeUrl;
|
|
1871
|
+
const authorizeUrl = readOverrideUrl(candidate, "authorizeUrl");
|
|
1872
|
+
if (authorizeUrl) {
|
|
1873
|
+
normalized.authorizeUrl = normalizeAuthorizeUrl(authorizeUrl);
|
|
1757
1874
|
}
|
|
1758
|
-
const tokenUrl =
|
|
1759
|
-
if (tokenUrl
|
|
1875
|
+
const tokenUrl = readOverrideUrl(candidate, "tokenUrl");
|
|
1876
|
+
if (tokenUrl) {
|
|
1760
1877
|
normalized.tokenUrl = tokenUrl;
|
|
1761
1878
|
}
|
|
1762
|
-
const scopes =
|
|
1879
|
+
const scopes = readOverrideField(candidate, "scopes");
|
|
1763
1880
|
if (scopes) {
|
|
1764
|
-
normalized.scopes =
|
|
1881
|
+
normalized.scopes = scopes;
|
|
1765
1882
|
}
|
|
1766
|
-
const baseApiUrl =
|
|
1767
|
-
if (baseApiUrl
|
|
1883
|
+
const baseApiUrl = readOverrideUrl(candidate, "baseApiUrl");
|
|
1884
|
+
if (baseApiUrl) {
|
|
1768
1885
|
normalized.baseApiUrl = baseApiUrl;
|
|
1769
1886
|
}
|
|
1770
1887
|
return normalized;
|
|
@@ -1801,16 +1918,22 @@ async function applyManualOverride(baseConfig) {
|
|
|
1801
1918
|
};
|
|
1802
1919
|
}
|
|
1803
1920
|
function findCCBinary() {
|
|
1804
|
-
const override = process.env.
|
|
1805
|
-
if (override &&
|
|
1921
|
+
const override = process.env.ANTHROPIC_CC_PATH;
|
|
1922
|
+
if (override && fileExists(override)) {
|
|
1806
1923
|
return override;
|
|
1807
1924
|
}
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
}
|
|
1925
|
+
const candidates = enumerateCCCandidates();
|
|
1926
|
+
if (candidates.length === 0) {
|
|
1927
|
+
return null;
|
|
1812
1928
|
}
|
|
1813
|
-
|
|
1929
|
+
if (candidates.length === 1) {
|
|
1930
|
+
return candidates[0] ?? null;
|
|
1931
|
+
}
|
|
1932
|
+
const probedCandidates = candidates.map((candidatePath) => {
|
|
1933
|
+
const version = probeOneVersion(candidatePath);
|
|
1934
|
+
return version ? { path: candidatePath, version } : null;
|
|
1935
|
+
}).filter((candidate) => candidate !== null).sort((left, right) => compareVersions(right.version, left.version) ?? 0);
|
|
1936
|
+
return probedCandidates[0]?.path ?? candidates[0] ?? null;
|
|
1814
1937
|
}
|
|
1815
1938
|
async function fingerprintBinary(path) {
|
|
1816
1939
|
const binaryContents = await readFile(path);
|
|
@@ -1819,19 +1942,24 @@ async function fingerprintBinary(path) {
|
|
|
1819
1942
|
function scanBinaryForOAuthConfig(buf) {
|
|
1820
1943
|
const binaryText = buf.toString("latin1");
|
|
1821
1944
|
const candidates = extractCandidateBlocks(binaryText).map(extractCandidateFromBlock).filter((candidate) => candidate !== null).sort((left, right) => right.score - left.score);
|
|
1822
|
-
|
|
1945
|
+
const preferredCandidate = candidates.find((candidate) => candidate.payload.clientId === KNOWN_PROD_CLIENT_ID);
|
|
1946
|
+
return preferredCandidate?.payload ?? candidates[0]?.payload ?? null;
|
|
1947
|
+
}
|
|
1948
|
+
async function readRawCacheEntries() {
|
|
1949
|
+
const raw = await readFile(getCachePath(), "utf-8");
|
|
1950
|
+
const parsed = JSON.parse(raw);
|
|
1951
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.entries !== "object" || parsed.entries === null) {
|
|
1952
|
+
return {};
|
|
1953
|
+
}
|
|
1954
|
+
return parsed.entries;
|
|
1823
1955
|
}
|
|
1824
1956
|
async function loadCache() {
|
|
1825
1957
|
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
|
-
}
|
|
1958
|
+
const rawEntries = await readRawCacheEntries();
|
|
1831
1959
|
const validEntries = {};
|
|
1832
|
-
for (const [hash, value] of Object.entries(
|
|
1833
|
-
if (isDetectedOAuthConfigPayload(value)) {
|
|
1834
|
-
validEntries[hash] = value;
|
|
1960
|
+
for (const [hash, value] of Object.entries(rawEntries)) {
|
|
1961
|
+
if (isDetectedOAuthConfigPayload(value) && !hasPollutedCachedScope(value.scopes)) {
|
|
1962
|
+
validEntries[hash] = normalizeDetectedOAuthConfigPayload(value);
|
|
1835
1963
|
}
|
|
1836
1964
|
}
|
|
1837
1965
|
return validEntries;
|
|
@@ -1842,7 +1970,11 @@ async function loadCache() {
|
|
|
1842
1970
|
async function saveCache(hash, config) {
|
|
1843
1971
|
try {
|
|
1844
1972
|
const cachePath = getCachePath();
|
|
1845
|
-
const currentEntries =
|
|
1973
|
+
const currentEntries = {};
|
|
1974
|
+
try {
|
|
1975
|
+
Object.assign(currentEntries, await readRawCacheEntries());
|
|
1976
|
+
} catch {
|
|
1977
|
+
}
|
|
1846
1978
|
currentEntries[hash] = config;
|
|
1847
1979
|
await mkdir(dirname(cachePath), { recursive: true });
|
|
1848
1980
|
await writeFile(
|
|
@@ -1867,27 +1999,23 @@ async function detectOAuthConfig() {
|
|
|
1867
1999
|
const cachedEntries = await loadCache();
|
|
1868
2000
|
const cachedConfig = cachedEntries[ccHash];
|
|
1869
2001
|
if (cachedConfig) {
|
|
1870
|
-
memoizedConfig = await applyManualOverride(
|
|
1871
|
-
...cachedConfig,
|
|
1872
|
-
source: "cached",
|
|
1873
|
-
ccPath,
|
|
1874
|
-
ccHash
|
|
1875
|
-
});
|
|
2002
|
+
memoizedConfig = await applyManualOverride(buildResolvedConfig(cachedConfig, "cached", ccPath, ccHash));
|
|
1876
2003
|
return memoizedConfig;
|
|
1877
2004
|
}
|
|
1878
2005
|
const readBinaryFile = detectorTestOverrides.readBinaryFile || readFile;
|
|
1879
|
-
const
|
|
2006
|
+
const binaryBuffer = await readBinaryFile(ccPath);
|
|
2007
|
+
const scannedConfig = scanBinaryForOAuthConfig(binaryBuffer);
|
|
1880
2008
|
if (!scannedConfig) {
|
|
1881
2009
|
memoizedConfig = await applyManualOverride(toFallbackConfig(ccPath, ccHash));
|
|
1882
2010
|
return memoizedConfig;
|
|
1883
2011
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
2012
|
+
const verifiedCanonicalScopes = getVerifiedCanonicalScopes(binaryBuffer, FALLBACK.scopes);
|
|
2013
|
+
if (verifiedCanonicalScopes) {
|
|
2014
|
+
scannedConfig.scopes = verifiedCanonicalScopes;
|
|
2015
|
+
}
|
|
2016
|
+
const runtimeDetectedConfig = normalizeDetectedOAuthConfigPayload(scannedConfig);
|
|
2017
|
+
await saveCache(ccHash, runtimeDetectedConfig);
|
|
2018
|
+
memoizedConfig = await applyManualOverride(buildResolvedConfig(runtimeDetectedConfig, "detected", ccPath, ccHash));
|
|
1891
2019
|
return memoizedConfig;
|
|
1892
2020
|
} catch {
|
|
1893
2021
|
memoizedConfig = await applyManualOverride(FALLBACK);
|
|
@@ -1895,7 +2023,7 @@ async function detectOAuthConfig() {
|
|
|
1895
2023
|
}
|
|
1896
2024
|
}
|
|
1897
2025
|
|
|
1898
|
-
// src/fingerprint
|
|
2026
|
+
// src/claude-code/fingerprint/capture.ts
|
|
1899
2027
|
var CURRENT_SCHEMA_VERSION = 1;
|
|
1900
2028
|
var LIVE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
1901
2029
|
var DEFAULT_CAPTURE_TIMEOUT_MS = 1e4;
|
|
@@ -1914,15 +2042,15 @@ var STATIC_HEADER_NAMES = [
|
|
|
1914
2042
|
];
|
|
1915
2043
|
var SUPPORTED_CC_RANGE = {
|
|
1916
2044
|
min: "1.0.0",
|
|
1917
|
-
maxTested: "2.1.
|
|
2045
|
+
maxTested: "2.1.121"
|
|
1918
2046
|
};
|
|
1919
|
-
var bundledTemplate =
|
|
2047
|
+
var bundledTemplate = data_default;
|
|
1920
2048
|
var fingerprintCaptureTestOverrides = {};
|
|
1921
2049
|
function now() {
|
|
1922
2050
|
return fingerprintCaptureTestOverrides.now?.() ?? Date.now();
|
|
1923
2051
|
}
|
|
1924
2052
|
function getCachePath2() {
|
|
1925
|
-
return join2(getConfigDir(), CACHE_FILE_NAME2);
|
|
2053
|
+
return join2(fingerprintCaptureTestOverrides.getConfigDir?.() ?? getConfigDir(), CACHE_FILE_NAME2);
|
|
1926
2054
|
}
|
|
1927
2055
|
function isRecord(value) {
|
|
1928
2056
|
return typeof value === "object" && value !== null;
|
|
@@ -2298,23 +2426,26 @@ function parseVersion(version) {
|
|
|
2298
2426
|
if (!match) {
|
|
2299
2427
|
return null;
|
|
2300
2428
|
}
|
|
2301
|
-
|
|
2429
|
+
const [, major, minor, patch] = match;
|
|
2430
|
+
return [Number(major), Number(minor), Number(patch)];
|
|
2302
2431
|
}
|
|
2303
2432
|
function compareVersions(left, right) {
|
|
2304
|
-
const
|
|
2305
|
-
const
|
|
2306
|
-
if (!
|
|
2433
|
+
const leftParts = parseVersion(left);
|
|
2434
|
+
const rightParts = parseVersion(right);
|
|
2435
|
+
if (!leftParts || !rightParts) {
|
|
2307
2436
|
return null;
|
|
2308
2437
|
}
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
return diff;
|
|
2315
|
-
}
|
|
2438
|
+
const [leftMajor, leftMinor, leftPatch] = leftParts;
|
|
2439
|
+
const [rightMajor, rightMinor, rightPatch] = rightParts;
|
|
2440
|
+
const majorDiff = leftMajor - rightMajor;
|
|
2441
|
+
if (majorDiff !== 0) {
|
|
2442
|
+
return majorDiff;
|
|
2316
2443
|
}
|
|
2317
|
-
|
|
2444
|
+
const minorDiff = leftMinor - rightMinor;
|
|
2445
|
+
if (minorDiff !== 0) {
|
|
2446
|
+
return minorDiff;
|
|
2447
|
+
}
|
|
2448
|
+
return leftPatch - rightPatch;
|
|
2318
2449
|
}
|
|
2319
2450
|
function detectDrift(template, installedOverride) {
|
|
2320
2451
|
const cachedVersion = template.cc_version ?? null;
|
|
@@ -2401,9 +2532,9 @@ function resetFingerprintCaptureForTest() {
|
|
|
2401
2532
|
}
|
|
2402
2533
|
|
|
2403
2534
|
export {
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2535
|
+
getConfig,
|
|
2536
|
+
loadConfig,
|
|
2537
|
+
updateConfigField,
|
|
2407
2538
|
ANTHROPIC_OAUTH_ADAPTER,
|
|
2408
2539
|
ANTHROPIC_USAGE_ENDPOINT,
|
|
2409
2540
|
ANTHROPIC_PROFILE_ENDPOINT,
|
|
@@ -2412,9 +2543,9 @@ export {
|
|
|
2412
2543
|
PLAN_LABELS,
|
|
2413
2544
|
TOKEN_EXPIRY_BUFFER_MS,
|
|
2414
2545
|
TOKEN_REFRESH_TIMEOUT_MS,
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2546
|
+
cc_derived_defaults_default,
|
|
2547
|
+
data_default,
|
|
2548
|
+
detectCliVersion,
|
|
2418
2549
|
showToast,
|
|
2419
2550
|
debugLog,
|
|
2420
2551
|
createMinimalClient,
|
|
@@ -2431,9 +2562,10 @@ export {
|
|
|
2431
2562
|
extractTemplate,
|
|
2432
2563
|
captureLiveTemplateAsync,
|
|
2433
2564
|
refreshLiveFingerprintAsync,
|
|
2565
|
+
compareVersions,
|
|
2434
2566
|
detectDrift,
|
|
2435
2567
|
checkCCCompat,
|
|
2436
2568
|
setFingerprintCaptureTestOverridesForTest,
|
|
2437
2569
|
resetFingerprintCaptureForTest
|
|
2438
2570
|
};
|
|
2439
|
-
//# sourceMappingURL=chunk-
|
|
2571
|
+
//# sourceMappingURL=chunk-5PQ3VP24.js.map
|