mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-09-18 23:20:36 +08:00
Compare commits
65
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6dac6f121 | ||
|
|
044a1036f6 | ||
|
|
a551ae4682 | ||
|
|
12f457aad8 | ||
|
|
625ab08afd | ||
|
|
8dfb31d8a5 | ||
|
|
6ee201f023 | ||
|
|
b4d6741327 | ||
|
|
4e5d8b13ca | ||
|
|
5d5c10a4f3 | ||
|
|
632a368e81 | ||
|
|
4c682d8b65 | ||
|
|
38ec876110 | ||
|
|
0d2971c794 | ||
|
|
c68f82cb11 | ||
|
|
78758edf84 | ||
|
|
c3d45e8e94 | ||
|
|
931e620273 | ||
|
|
905d4eb99a | ||
|
|
5fb899572b | ||
|
|
c3bf66dbc2 | ||
|
|
3943183052 | ||
|
|
65f29cf68e | ||
|
|
1c8b699d43 | ||
|
|
ff49ec5fd6 | ||
|
|
25474bfe8b | ||
|
|
b47fd721da | ||
|
|
c26cb6427d | ||
|
|
657fb7c9c9 | ||
|
|
2ff1acb3ee | ||
|
|
b2fdd80112 | ||
|
|
26ddc358fe | ||
|
|
398370690e | ||
|
|
6cad158a17 | ||
|
|
0f1fe5ef85 | ||
|
|
6e2bd52842 | ||
|
|
3534c326a5 | ||
|
|
6685b26dfb | ||
|
|
5150ea9643 | ||
|
|
eb8baa46af | ||
|
|
f328a5c889 | ||
|
|
b15d4751a6 | ||
|
|
d5db8208f9 | ||
|
|
d8af4e9f01 | ||
|
|
f37c786ad3 | ||
|
|
21b0f0f9aa | ||
|
|
27f549ae64 | ||
|
|
263993d836 | ||
|
|
85133eeab2 | ||
|
|
1eddb334cf | ||
|
|
0432df8bfe | ||
|
|
ba026a3e56 | ||
|
|
c95e735eb1 | ||
|
|
58dbe8ed68 | ||
|
|
c281e17d7f | ||
|
|
408a40e7c2 | ||
|
|
bee87b3258 | ||
|
|
32156b120b | ||
|
|
7225f045c6 | ||
|
|
88c168b39e | ||
|
|
e7b588b6ea | ||
|
|
094bd24d57 | ||
|
|
3ac52d0da9 | ||
|
|
0ee1beea58 | ||
|
|
ff9acae588 |
@@ -45,7 +45,7 @@ TASK OVERVIEW:
|
||||
- If you find similar issues using ./scripts/gh.sh search, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
|
||||
|
||||
5. Apply the selected labels:
|
||||
- Use `./scripts/edit-issue-labels.sh --issue NUMBER --add-label LABEL1 --add-label LABEL2` to apply your selected labels
|
||||
- Use `./scripts/edit-issue-labels.sh --add-label LABEL1 --add-label LABEL2` to apply your selected labels (issue number is read from the workflow event)
|
||||
- DO NOT post any comments explaining your decision
|
||||
- DO NOT communicate directly with users
|
||||
- If no labels are clearly applicable, do not apply any labels
|
||||
|
||||
@@ -25,4 +25,4 @@ jobs:
|
||||
prompt: "/review-pr REPO: ${{ github.repository }} PR_NUMBER: ${{ github.event.pull_request.number }}"
|
||||
claude_args: |
|
||||
--allowedTools "mcp__github_inline_comment__create_inline_comment"
|
||||
--model "claude-opus-4-6"
|
||||
--model "claude-opus-4-7"
|
||||
|
||||
@@ -36,4 +36,4 @@ jobs:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_args: |
|
||||
--allowedTools "Bash(bun install),Bash(bun test:*),Bash(bun run format),Bash(bun typecheck)"
|
||||
--model "claude-opus-4-6"
|
||||
--model "claude-opus-4-7"
|
||||
|
||||
@@ -20,6 +20,8 @@ jobs:
|
||||
|
||||
- name: Run Claude Code for Issue Triage
|
||||
uses: anthropics/claude-code-action@main
|
||||
env:
|
||||
CLAUDE_CODE_SCRIPT_CAPS: '{"edit-issue-labels.sh":2}'
|
||||
with:
|
||||
prompt: "/label-issue REPO: ${{ github.repository }} ISSUE_NUMBER: ${{ github.event.issue.number }}"
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
|
||||
@@ -27,6 +27,8 @@ jobs:
|
||||
with:
|
||||
prompt: "List all available tools"
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
# Explicitly include project so .mcp.json is discovered regardless of the event-gated default
|
||||
setting_sources: "user,project"
|
||||
env:
|
||||
# Change to test directory so it finds .mcp.json
|
||||
CLAUDE_WORKING_DIR: ${{ github.workspace }}/base-action/test/mcp-test
|
||||
@@ -108,7 +110,11 @@ jobs:
|
||||
with:
|
||||
prompt: "List all available tools"
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
mcp_config: '{"mcpServers":{"test-server":{"type":"stdio","command":"bun","args":["simple-mcp-server.ts"],"env":{}}}}'
|
||||
# mcp_config input was removed; pass via claude_args. Pin setting_sources to "user"
|
||||
# so .mcp.json is NOT auto-discovered — this proves the flag itself works.
|
||||
setting_sources: "user"
|
||||
claude_args: >-
|
||||
--mcp-config '{"mcpServers":{"test-server":{"type":"stdio","command":"bun","args":["simple-mcp-server.ts"],"env":{}}}}'
|
||||
env:
|
||||
# Change to test directory so bun can find the MCP server script
|
||||
CLAUDE_WORKING_DIR: ${{ github.workspace }}/base-action/test/mcp-test
|
||||
|
||||
+112
-6
@@ -32,7 +32,16 @@ inputs:
|
||||
required: false
|
||||
default: ""
|
||||
allowed_non_write_users:
|
||||
description: "Comma-separated list of usernames to allow without write permissions, or '*' to allow all users. Only works when github_token input is provided. WARNING: Use with extreme caution - this bypasses security checks and should only be used for workflows with very limited permissions (e.g., issue labeling)."
|
||||
description: |
|
||||
Comma-separated list of usernames to allow without write permissions, or '*' to allow all users.
|
||||
Only works when github_token input is provided. WARNING: Use with extreme caution - this
|
||||
bypasses security checks and should only be used for workflows with very limited permissions
|
||||
(e.g., issue labeling).
|
||||
|
||||
SECURITY: Processing untrusted content exposes the workflow to prompt injection. When this
|
||||
input is set, Claude does a best-effort scrub of Anthropic, cloud, and GitHub Actions secrets
|
||||
from subprocess environments. This reduces but does not eliminate prompt injection risk -
|
||||
only use for workflows with very limited permissions and validate all outputs.
|
||||
required: false
|
||||
default: ""
|
||||
include_comments_by_actor:
|
||||
@@ -53,6 +62,10 @@ inputs:
|
||||
description: "Claude Code settings as JSON string or path to settings JSON file"
|
||||
required: false
|
||||
default: ""
|
||||
setting_sources:
|
||||
description: "Comma-separated list of setting sources to load (user, project, local). When unset, the action applies 'user,project,local' at runtime for PR contexts where .claude/ is restored from the base branch; for other contexts it applies the same event-gated default as base-action. Set to 'user' to ignore in-repo settings entirely."
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
# Auth configuration
|
||||
anthropic_api_key:
|
||||
@@ -164,7 +177,7 @@ runs:
|
||||
steps:
|
||||
- name: Install Bun
|
||||
if: inputs.path_to_bun_executable == ''
|
||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # https://github.com/oven-sh/setup-bun/releases/tag/v2.1.2
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # https://github.com/oven-sh/setup-bun/releases/tag/v2.2.0
|
||||
with:
|
||||
bun-version: 1.3.6
|
||||
token: ${{ inputs.github_token || github.token }}
|
||||
@@ -186,11 +199,56 @@ runs:
|
||||
cd ${GITHUB_ACTION_PATH}
|
||||
bun install --production
|
||||
|
||||
- name: Install subprocess isolation dependencies
|
||||
# Install subprocess isolation dependencies when processing content from non-write users.
|
||||
# Best-effort: skips on non-Linux or when sudo/apt unavailable (self-hosted runners).
|
||||
if: ${{ inputs.allowed_non_write_users != '' && runner.os == 'Linux' }}
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "${CLAUDE_CODE_SUBPROCESS_ENV_SCRUB:-}" = "0" ]; then
|
||||
echo "Subprocess isolation opted out via CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=0"
|
||||
exit 0
|
||||
fi
|
||||
if command -v apt-get >/dev/null && command -v sudo >/dev/null; then
|
||||
for i in 1 2 3; do
|
||||
sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends bubblewrap socat && break
|
||||
echo "apt-get attempt $i failed, retrying..."
|
||||
sleep 5
|
||||
done
|
||||
fi
|
||||
# Ubuntu 24.04+ restricts unprivileged user namespaces via AppArmor.
|
||||
# The sysctl doesn't exist on older kernels — that's fine.
|
||||
if [ -f /proc/sys/kernel/apparmor_restrict_unprivileged_userns ] && command -v sudo >/dev/null; then
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
fi
|
||||
|
||||
- name: Pin bun binary for post-steps
|
||||
if: ${{ inputs.allowed_non_write_users != '' }}
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
run: |
|
||||
# Keep a copy of the bun binary alongside the action's own files so
|
||||
# post-steps use the same version that was on PATH at action start.
|
||||
mkdir -p "$GITHUB_ACTION_PATH/bin"
|
||||
cp "$(command -v bun)" "$GITHUB_ACTION_PATH/bin/bun"
|
||||
|
||||
- name: Prepend system bin dirs to PATH
|
||||
if: ${{ inputs.allowed_non_write_users != '' && runner.os != 'Windows' }}
|
||||
continue-on-error: true
|
||||
shell: /bin/bash --noprofile --norc -e -o pipefail {0}
|
||||
run: |
|
||||
echo "/usr/bin" >> "$GITHUB_PATH"
|
||||
echo "/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Run Claude Code Action
|
||||
id: run
|
||||
shell: bash
|
||||
run: |
|
||||
bun run ${GITHUB_ACTION_PATH}/src/entrypoints/run.ts
|
||||
bun --no-env-file \
|
||||
--config="${GITHUB_ACTION_PATH}/bunfig.toml" \
|
||||
--tsconfig-override="${GITHUB_ACTION_PATH}/tsconfig.json" \
|
||||
run ${GITHUB_ACTION_PATH}/src/entrypoints/run.ts
|
||||
env:
|
||||
# Prepare inputs
|
||||
MODE: ${{ inputs.mode }}
|
||||
@@ -204,6 +262,8 @@ runs:
|
||||
OVERRIDE_GITHUB_TOKEN: ${{ inputs.github_token }}
|
||||
ALLOWED_BOTS: ${{ inputs.allowed_bots }}
|
||||
ALLOWED_NON_WRITE_USERS: ${{ inputs.allowed_non_write_users }}
|
||||
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB: ${{ env.CLAUDE_CODE_SUBPROCESS_ENV_SCRUB || (inputs.allowed_non_write_users != '' && '1') || '' }}
|
||||
CLAUDE_CODE_SCRIPT_CAPS: ${{ env.CLAUDE_CODE_SCRIPT_CAPS || '' }}
|
||||
INCLUDE_COMMENTS_BY_ACTOR: ${{ inputs.include_comments_by_actor }}
|
||||
EXCLUDE_COMMENTS_BY_ACTOR: ${{ inputs.exclude_comments_by_actor }}
|
||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||
@@ -223,6 +283,7 @@ runs:
|
||||
# Base-action inputs
|
||||
INPUT_PROMPT_FILE: ${{ runner.temp }}/claude-prompts/claude-prompt.txt
|
||||
INPUT_SETTINGS: ${{ inputs.settings }}
|
||||
INPUT_SETTING_SOURCES: ${{ inputs.setting_sources }}
|
||||
INPUT_EXPERIMENTAL_SLASH_COMMANDS_DIR: ${{ github.action_path }}/slash-commands
|
||||
INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
||||
INPUT_PATH_TO_BUN_EXECUTABLE: ${{ inputs.path_to_bun_executable }}
|
||||
@@ -270,6 +331,15 @@ runs:
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: ${{ env.ANTHROPIC_DEFAULT_HAIKU_MODEL }}
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: ${{ env.ANTHROPIC_DEFAULT_OPUS_MODEL }}
|
||||
|
||||
# MCP configuration — these env vars are read directly from process.env by the
|
||||
# Claude CLI subprocess. They must be listed explicitly here because this step's
|
||||
# env: block shadows the calling workflow's job-level env vars (GitHub Actions
|
||||
# composite action behavior). Set these in your workflow's job-level env: or via
|
||||
# a prior step that writes to $GITHUB_ENV.
|
||||
MCP_TIMEOUT: ${{ env.MCP_TIMEOUT }}
|
||||
MCP_TOOL_TIMEOUT: ${{ env.MCP_TOOL_TIMEOUT }}
|
||||
MAX_MCP_OUTPUT_TOKENS: ${{ env.MAX_MCP_OUTPUT_TOKENS }}
|
||||
|
||||
# Telemetry configuration
|
||||
CLAUDE_CODE_ENABLE_TELEMETRY: ${{ env.CLAUDE_CODE_ENABLE_TELEMETRY }}
|
||||
OTEL_METRICS_EXPORTER: ${{ env.OTEL_METRICS_EXPORTER }}
|
||||
@@ -281,11 +351,42 @@ runs:
|
||||
OTEL_LOGS_EXPORT_INTERVAL: ${{ env.OTEL_LOGS_EXPORT_INTERVAL }}
|
||||
OTEL_RESOURCE_ATTRIBUTES: ${{ env.OTEL_RESOURCE_ATTRIBUTES }}
|
||||
|
||||
- name: Re-prepend system bin dirs to PATH
|
||||
if: ${{ always() && inputs.allowed_non_write_users != '' && runner.os != 'Windows' }}
|
||||
continue-on-error: true
|
||||
shell: /bin/bash --noprofile --norc -e -o pipefail {0}
|
||||
env:
|
||||
BASH_ENV: ""
|
||||
LD_PRELOAD: ""
|
||||
LD_LIBRARY_PATH: ""
|
||||
NODE_OPTIONS: ""
|
||||
DYLD_INSERT_LIBRARIES: ""
|
||||
DYLD_PRELOAD: ""
|
||||
DYLD_LIBRARY_PATH: ""
|
||||
DYLD_FRAMEWORK_PATH: ""
|
||||
run: |
|
||||
echo "/usr/bin" >> "$GITHUB_PATH"
|
||||
echo "/bin" >> "$GITHUB_PATH"
|
||||
{
|
||||
echo "BASH_ENV="
|
||||
echo "LD_PRELOAD="
|
||||
echo "LD_LIBRARY_PATH="
|
||||
echo "DYLD_INSERT_LIBRARIES="
|
||||
echo "DYLD_PRELOAD="
|
||||
echo "DYLD_LIBRARY_PATH="
|
||||
echo "DYLD_FRAMEWORK_PATH="
|
||||
} >> "$GITHUB_ENV"
|
||||
|
||||
- name: Cleanup SSH signing key
|
||||
if: always() && inputs.ssh_signing_key != ''
|
||||
shell: bash
|
||||
run: |
|
||||
bun run ${GITHUB_ACTION_PATH}/src/entrypoints/cleanup-ssh-signing.ts
|
||||
BUN_BIN="${GITHUB_ACTION_PATH}/bin/bun"
|
||||
[ -x "$BUN_BIN" ] || BUN_BIN="bun"
|
||||
"$BUN_BIN" --no-env-file \
|
||||
--config="${GITHUB_ACTION_PATH}/bunfig.toml" \
|
||||
--tsconfig-override="${GITHUB_ACTION_PATH}/tsconfig.json" \
|
||||
run ${GITHUB_ACTION_PATH}/src/entrypoints/cleanup-ssh-signing.ts
|
||||
|
||||
- name: Post buffered inline comments
|
||||
if: always() && inputs.classify_inline_comments != 'false'
|
||||
@@ -297,10 +398,15 @@ runs:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
|
||||
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
||||
run: |
|
||||
bun run ${GITHUB_ACTION_PATH}/src/entrypoints/post-buffered-inline-comments.ts
|
||||
BUN_BIN="${GITHUB_ACTION_PATH}/bin/bun"
|
||||
[ -x "$BUN_BIN" ] || BUN_BIN="bun"
|
||||
"$BUN_BIN" --no-env-file \
|
||||
--config="${GITHUB_ACTION_PATH}/bunfig.toml" \
|
||||
--tsconfig-override="${GITHUB_ACTION_PATH}/tsconfig.json" \
|
||||
run ${GITHUB_ACTION_PATH}/src/entrypoints/post-buffered-inline-comments.ts
|
||||
|
||||
- name: Revoke app token
|
||||
if: always() && inputs.github_token == '' && steps.run.outputs.skipped_due_to_workflow_validation_mismatch != 'true'
|
||||
if: always() && inputs.github_token == '' && steps.run.outputs.github_token != '' && steps.run.outputs.skipped_due_to_workflow_validation_mismatch != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
curl -L \
|
||||
|
||||
@@ -94,6 +94,7 @@ Add the following to your workflow file:
|
||||
| `max_turns` | Maximum number of conversation turns (default: no limit) | No | '' |
|
||||
| `mcp_config` | Path to the MCP configuration JSON file, or MCP configuration JSON string | No | '' |
|
||||
| `settings` | Path to Claude Code settings JSON file, or settings JSON string | No | '' |
|
||||
| `setting_sources` | Comma-separated setting sources to load (`user`, `project`, `local`). Project/local merge permissions additively. | No | event-dependent (see below) |
|
||||
| `system_prompt` | Override system prompt | No | '' |
|
||||
| `append_system_prompt` | Append to system prompt | No | '' |
|
||||
| `claude_env` | Custom environment variables to pass to Claude Code execution (YAML multiline format) | No | '' |
|
||||
@@ -111,6 +112,8 @@ Add the following to your workflow file:
|
||||
|
||||
\*\*`show_full_output` is automatically enabled when GitHub Actions debug mode is active. See [security documentation](../docs/security.md#️-full-output-security-warning) for important security considerations.
|
||||
|
||||
`setting_sources` defaults to `user,project,local` for most events. Under `pull_request_target`, `workflow_run`, and `issue_comment` it defaults to `user` only; set it explicitly if you want project/local settings to load for those events.
|
||||
|
||||
## Outputs
|
||||
|
||||
| Output | Description |
|
||||
|
||||
@@ -18,6 +18,10 @@ inputs:
|
||||
description: "Claude Code settings as JSON string or path to settings JSON file"
|
||||
required: false
|
||||
default: ""
|
||||
setting_sources:
|
||||
description: "Comma-separated list of setting sources to load (user, project, local). Defaults to 'user,project,local'; under pull_request_target/workflow_run/issue_comment, defaults to 'user' only. Project/local settings additively merge permissions with allowed_tools — set explicitly to control which sources load."
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
# Action settings
|
||||
claude_args:
|
||||
@@ -97,7 +101,7 @@ runs:
|
||||
|
||||
- name: Install Bun
|
||||
if: inputs.path_to_bun_executable == ''
|
||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # https://github.com/oven-sh/setup-bun/releases/tag/v2.1.2
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # https://github.com/oven-sh/setup-bun/releases/tag/v2.2.0
|
||||
with:
|
||||
bun-version: 1.3.6
|
||||
|
||||
@@ -124,7 +128,7 @@ runs:
|
||||
PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
||||
run: |
|
||||
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
|
||||
CLAUDE_CODE_VERSION="2.1.81"
|
||||
CLAUDE_CODE_VERSION="2.1.118"
|
||||
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
|
||||
for attempt in 1 2 3; do
|
||||
echo "Installation attempt $attempt..."
|
||||
@@ -165,6 +169,7 @@ runs:
|
||||
INPUT_PROMPT: ${{ inputs.prompt }}
|
||||
INPUT_PROMPT_FILE: ${{ inputs.prompt_file }}
|
||||
INPUT_SETTINGS: ${{ inputs.settings }}
|
||||
INPUT_SETTING_SOURCES: ${{ inputs.setting_sources }}
|
||||
INPUT_CLAUDE_ARGS: ${{ inputs.claude_args }}
|
||||
INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
||||
INPUT_PATH_TO_BUN_EXECUTABLE: ${{ inputs.path_to_bun_executable }}
|
||||
|
||||
+204
-32
@@ -6,7 +6,7 @@
|
||||
"name": "@anthropic-ai/claude-code-base-action",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.81",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.118",
|
||||
"shell-quote": "^1.8.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -27,41 +27,33 @@
|
||||
|
||||
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.81", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-CBeebgibBEN/DWOQGZN67vhuTG55RbI1hlsFSSoZ4uA/Io3lw04eHTE2ISCmdbqyJaefYTt6GKZei1nP0TQMNw=="],
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.118", "", { "dependencies": { "@anthropic-ai/sdk": "^0.81.0", "@modelcontextprotocol/sdk": "^1.29.0" }, "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.118", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.118", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.118", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.118" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-OfxCTzmfqvctpTLd3CP+UrpC0JdhYcJp12rD+SK29k+9+hrbblCrLobvhdWpTuYFejTPJuiLVsbHxq0BkEuELQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.118", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RudnoBekv0c9CPL0EeMc4RqDe4Pb7tdz/2oxa5EYqaajXNRlYtTvru9q7wq7Zvp40JQ24hz38swOTJ7PkW7G/g=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.118", "", { "os": "darwin", "cpu": "x64" }, "sha512-Hf/H46uElpfygALlb4KZR2EuyyJRe7jBuWa+TDA4jmAHVblNfwkVyaCp8s61hZINB3kAmXdLdM81VI+xwruWzA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.118", "", { "os": "linux", "cpu": "arm64" }, "sha512-lwMXnweJKpzESezJFM8mngRxJfaq/N0gqyFXBm5bOYaPIZnlGlP3h1JMKsJeqC4neLVGbe5a3Hq4T22Rr7OoAA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.118", "", { "os": "linux", "cpu": "arm64" }, "sha512-gSuZS8GM8MZuklzAJS8VCCjqK2UJJeerV+JpVYzXNMelotq4sXUg2dp17VbjCJ1jhUC9u1gpzlQDWkmYrXCbOg=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.2.118", "", { "os": "linux", "cpu": "x64" }, "sha512-m0KBbwN9s0+hQwAPzeUFvegrEqoT9EOC+Vz3vr4dd9FcZyvKZE0yiv9S7YbFp1ZKWDQmppmvpcB+9eME7WQ0yA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.118", "", { "os": "linux", "cpu": "x64" }, "sha512-36lG1F9IsuNBV7AzJY98z8KwryoWZCeEtMzgZL7614zPBhZGBsziQUZEBm2Eu7FVWbRQmYv6BL52+gffpkM4Gw=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.118", "", { "os": "win32", "cpu": "arm64" }, "sha512-o30/SL084+a8wJ+5cgKM1BflxiBUEy+xEcEpZPW+zCFtiqY0b1Pr+K35ECsbKBrv+w5/0Byp4/CvCkP15Otsgw=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.2.118", "", { "os": "win32", "cpu": "x64" }, "sha512-TSqsVBUaZGgYMkjCZckXhPvmJDTS7C6VAl4IOeMVNB/oPINVFaobtVagjYvY0BFnlDCOzz6sb8puafHwcm7qQA=="],
|
||||
|
||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.81.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-D4K5PvEV6wPiRtVlVsJHIUhHAmOZ6IT/I9rKlTf84gR7GyyAurPJK7z9BOf/AZqC5d1DhYQGJNKRmV+q8dGhgw=="],
|
||||
|
||||
"@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="],
|
||||
|
||||
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
||||
|
||||
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
|
||||
"@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="],
|
||||
|
||||
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
|
||||
|
||||
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
|
||||
|
||||
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
|
||||
|
||||
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
|
||||
|
||||
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
|
||||
|
||||
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
|
||||
|
||||
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
|
||||
|
||||
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
|
||||
|
||||
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
|
||||
|
||||
@@ -71,22 +63,202 @@
|
||||
|
||||
"@types/shell-quote": ["@types/shell-quote@1.7.5", "", {}, "sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw=="],
|
||||
|
||||
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
|
||||
"ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
|
||||
|
||||
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
||||
|
||||
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
|
||||
|
||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||
|
||||
"content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="],
|
||||
|
||||
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
||||
|
||||
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||
|
||||
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
||||
|
||||
"cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
||||
|
||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||
|
||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
|
||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||
|
||||
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
||||
|
||||
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
||||
|
||||
"eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
|
||||
|
||||
"eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],
|
||||
|
||||
"express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
||||
|
||||
"express-rate-limit": ["express-rate-limit@8.3.1", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
||||
|
||||
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
||||
|
||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||
|
||||
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"hono": ["hono@4.12.9", "", {}, "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA=="],
|
||||
|
||||
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||
|
||||
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="],
|
||||
|
||||
"json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
||||
|
||||
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
||||
|
||||
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
|
||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"path-to-regexp": ["path-to-regexp@8.4.0", "", {}, "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg=="],
|
||||
|
||||
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
|
||||
|
||||
"prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="],
|
||||
|
||||
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||
|
||||
"qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="],
|
||||
|
||||
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||
|
||||
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
|
||||
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
|
||||
|
||||
"serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
|
||||
|
||||
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="],
|
||||
|
||||
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||
|
||||
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
||||
|
||||
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
||||
|
||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||
|
||||
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
|
||||
|
||||
"tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="],
|
||||
|
||||
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
||||
|
||||
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||
|
||||
"undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
|
||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
|
||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||
|
||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||
|
||||
"zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.81",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.118",
|
||||
"shell-quote": "^1.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -11,6 +11,14 @@ async function run() {
|
||||
try {
|
||||
validateEnvironmentVariables();
|
||||
|
||||
// The composite action's "Install Claude Code" step writes the binary to
|
||||
// ~/.local/bin/claude. Pass that path explicitly so the Agent SDK doesn't
|
||||
// fall back to its bundled platform package, which bun may resolve to the
|
||||
// wrong libc variant on Linux.
|
||||
const claudeExecutable =
|
||||
process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE ||
|
||||
`${process.env.HOME}/.local/bin/claude`;
|
||||
|
||||
await setupClaudeCodeSettings(
|
||||
process.env.INPUT_SETTINGS,
|
||||
undefined, // homeDir
|
||||
@@ -20,7 +28,7 @@ async function run() {
|
||||
await installPlugins(
|
||||
process.env.INPUT_PLUGIN_MARKETPLACES,
|
||||
process.env.INPUT_PLUGINS,
|
||||
process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE,
|
||||
claudeExecutable,
|
||||
);
|
||||
|
||||
const promptConfig = await preparePrompt({
|
||||
@@ -38,9 +46,9 @@ async function run() {
|
||||
appendSystemPrompt: process.env.INPUT_APPEND_SYSTEM_PROMPT,
|
||||
fallbackModel: process.env.INPUT_FALLBACK_MODEL,
|
||||
model: process.env.ANTHROPIC_MODEL,
|
||||
pathToClaudeCodeExecutable:
|
||||
process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE,
|
||||
pathToClaudeCodeExecutable: claudeExecutable,
|
||||
showFullOutput: process.env.INPUT_SHOW_FULL_OUTPUT,
|
||||
settingSources: process.env.INPUT_SETTING_SOURCES,
|
||||
});
|
||||
|
||||
// Set outputs for the standalone base-action
|
||||
|
||||
@@ -79,6 +79,20 @@ function mergeMcpConfigs(configValues: string[]): string {
|
||||
return JSON.stringify(merged);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip comment lines from a shell argument string.
|
||||
* Lines whose first non-whitespace character is `#` are removed entirely.
|
||||
* Inline `#` within a line (e.g. inside a quoted value) is left untouched
|
||||
* because shell-quote handles quoting — we only need to remove full comment lines
|
||||
* before shell-quote sees them.
|
||||
*/
|
||||
function stripShellComments(input: string): string {
|
||||
return input
|
||||
.split("\n")
|
||||
.filter((line) => !line.trim().startsWith("#"))
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse claudeArgs string into extraArgs record for SDK pass-through
|
||||
* The SDK/CLI will handle --mcp-config, --json-schema, etc.
|
||||
@@ -92,7 +106,7 @@ function parseClaudeArgsToExtraArgs(
|
||||
if (!claudeArgs?.trim()) return {};
|
||||
|
||||
const result: Record<string, string | null> = {};
|
||||
const args = parseShellArgs(claudeArgs).filter(
|
||||
const args = parseShellArgs(stripShellComments(claudeArgs)).filter(
|
||||
(arg): arg is string => typeof arg === "string",
|
||||
);
|
||||
|
||||
@@ -215,6 +229,12 @@ export function parseSdkOptions(options: ClaudeOptions): ParsedSdkOptions {
|
||||
// Set the entrypoint for Claude Code to identify this as the GitHub Action
|
||||
env.CLAUDE_CODE_ENTRYPOINT = "claude-code-github-action";
|
||||
|
||||
// Remove OIDC token request variables so Claude cannot mint new tokens.
|
||||
// These are only needed by the action itself (via @actions/core.getIDToken()),
|
||||
// not by the Claude session.
|
||||
delete env.ACTIONS_ID_TOKEN_REQUEST_URL;
|
||||
delete env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
|
||||
|
||||
// Build system prompt option - default to claude_code preset
|
||||
let systemPrompt: SdkOptions["systemPrompt"];
|
||||
if (options.systemPrompt) {
|
||||
@@ -251,13 +271,22 @@ export function parseSdkOptions(options: ClaudeOptions): ParsedSdkOptions {
|
||||
extraArgs,
|
||||
env,
|
||||
|
||||
// Load settings from sources - prefer user's --setting-sources if provided, otherwise use all sources
|
||||
// This ensures users can override the default behavior (e.g., --setting-sources user to avoid in-repo configs)
|
||||
settingSources: extraArgs["setting-sources"]
|
||||
? (extraArgs["setting-sources"].split(
|
||||
",",
|
||||
) as SdkOptions["settingSources"])
|
||||
: ["user", "project", "local"],
|
||||
// Setting sources precedence: direct input > --setting-sources in claude_args > default.
|
||||
// The default is supplied by the caller (the wrapper action passes
|
||||
// ["user","project","local"]); base-action applies an event-gated default of ["user"]
|
||||
// under pull_request_target/workflow_run/issue_comment and ["user","project","local"]
|
||||
// otherwise. Both action.yml files leave the YAML default empty so that
|
||||
// --setting-sources in claude_args is reachable when the input is not set.
|
||||
settingSources: (options.settingSources
|
||||
? options.settingSources.split(",").map((s) => s.trim())
|
||||
: extraArgs["setting-sources"]
|
||||
? extraArgs["setting-sources"].split(",").map((s) => s.trim())
|
||||
: (options.defaultSettingSources ??
|
||||
(process.env.GITHUB_EVENT_NAME === "pull_request_target" ||
|
||||
process.env.GITHUB_EVENT_NAME === "workflow_run" ||
|
||||
process.env.GITHUB_EVENT_NAME === "issue_comment"
|
||||
? ["user"]
|
||||
: ["user", "project", "local"]))) as SdkOptions["settingSources"],
|
||||
};
|
||||
|
||||
// Remove setting-sources from extraArgs to avoid passing it twice
|
||||
|
||||
@@ -151,7 +151,7 @@ export async function runClaudeWithSdk(
|
||||
|
||||
console.log(`Running Claude with prompt from file: ${promptPath}`);
|
||||
// Log SDK options without env (which could contain sensitive data)
|
||||
const { env, ...optionsToLog } = sdkOptions;
|
||||
const { env, extraArgs, ...optionsToLog } = sdkOptions;
|
||||
console.log("SDK options:", JSON.stringify(optionsToLog, null, 2));
|
||||
|
||||
const messages: SDKMessage[] = [];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { runClaudeWithSdk } from "./run-claude-sdk";
|
||||
import type { ClaudeRunResult } from "./run-claude-sdk";
|
||||
import { parseSdkOptions } from "./parse-sdk-options";
|
||||
import type { Options as SdkOptions } from "@anthropic-ai/claude-agent-sdk";
|
||||
|
||||
export type ClaudeOptions = {
|
||||
claudeArgs?: string;
|
||||
@@ -14,6 +15,8 @@ export type ClaudeOptions = {
|
||||
appendSystemPrompt?: string;
|
||||
fallbackModel?: string;
|
||||
showFullOutput?: string;
|
||||
settingSources?: string;
|
||||
defaultSettingSources?: SdkOptions["settingSources"];
|
||||
};
|
||||
|
||||
export async function runClaude(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import { describe, test, expect, afterEach } from "bun:test";
|
||||
import { parseSdkOptions } from "../src/parse-sdk-options";
|
||||
import type { ClaudeOptions } from "../src/run-claude";
|
||||
|
||||
@@ -313,6 +313,40 @@ describe("parseSdkOptions", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("shell comment stripping", () => {
|
||||
test("should parse flags before and after a comment line", () => {
|
||||
const options: ClaudeOptions = {
|
||||
claudeArgs: "--model 'claude-haiku'\n# comment\n--allowed-tools 'Edit'",
|
||||
};
|
||||
|
||||
const result = parseSdkOptions(options);
|
||||
|
||||
expect(result.sdkOptions.extraArgs?.["model"]).toBe("claude-haiku");
|
||||
expect(result.sdkOptions.allowedTools).toEqual(["Edit"]);
|
||||
});
|
||||
|
||||
test("should parse flags correctly when no comments are present", () => {
|
||||
const options: ClaudeOptions = {
|
||||
claudeArgs: "--model 'claude-haiku'",
|
||||
};
|
||||
|
||||
const result = parseSdkOptions(options);
|
||||
|
||||
expect(result.sdkOptions.extraArgs?.["model"]).toBe("claude-haiku");
|
||||
});
|
||||
|
||||
test("should not strip inline # that appears inside a quoted value", () => {
|
||||
const options: ClaudeOptions = {
|
||||
claudeArgs: "--model 'claude-haiku' --prompt 'use color #ff0000'",
|
||||
};
|
||||
|
||||
const result = parseSdkOptions(options);
|
||||
|
||||
expect(result.sdkOptions.extraArgs?.["model"]).toBe("claude-haiku");
|
||||
expect(result.sdkOptions.extraArgs?.["prompt"]).toBe("use color #ff0000");
|
||||
});
|
||||
});
|
||||
|
||||
describe("environment variables passthrough", () => {
|
||||
test("should include OTEL environment variables in sdkOptions.env", () => {
|
||||
// Set up test environment variables
|
||||
@@ -366,5 +400,151 @@ describe("parseSdkOptions", () => {
|
||||
"claude-code-github-action",
|
||||
);
|
||||
});
|
||||
|
||||
test("should strip ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN from env", () => {
|
||||
const originalEnv = { ...process.env };
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_URL =
|
||||
"https://token.actions.githubusercontent.com";
|
||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = "secret-token-value";
|
||||
|
||||
try {
|
||||
const options: ClaudeOptions = {};
|
||||
const result = parseSdkOptions(options);
|
||||
|
||||
expect(
|
||||
result.sdkOptions.env?.ACTIONS_ID_TOKEN_REQUEST_URL,
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
result.sdkOptions.env?.ACTIONS_ID_TOKEN_REQUEST_TOKEN,
|
||||
).toBeUndefined();
|
||||
} finally {
|
||||
process.env = originalEnv;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("settingSources", () => {
|
||||
const originalEventName = process.env.GITHUB_EVENT_NAME;
|
||||
afterEach(() => {
|
||||
if (originalEventName === undefined) {
|
||||
delete process.env.GITHUB_EVENT_NAME;
|
||||
} else {
|
||||
process.env.GITHUB_EVENT_NAME = originalEventName;
|
||||
}
|
||||
});
|
||||
|
||||
test("should default to ['user','project','local'] for non-gated events", () => {
|
||||
process.env.GITHUB_EVENT_NAME = "push";
|
||||
const result = parseSdkOptions({});
|
||||
|
||||
expect(result.sdkOptions.settingSources).toEqual([
|
||||
"user",
|
||||
"project",
|
||||
"local",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should default to ['user'] under pull_request_target", () => {
|
||||
process.env.GITHUB_EVENT_NAME = "pull_request_target";
|
||||
const result = parseSdkOptions({});
|
||||
|
||||
expect(result.sdkOptions.settingSources).toEqual(["user"]);
|
||||
});
|
||||
|
||||
test("should default to ['user'] under workflow_run", () => {
|
||||
process.env.GITHUB_EVENT_NAME = "workflow_run";
|
||||
const result = parseSdkOptions({});
|
||||
|
||||
expect(result.sdkOptions.settingSources).toEqual(["user"]);
|
||||
});
|
||||
|
||||
test("should default to ['user'] under issue_comment", () => {
|
||||
process.env.GITHUB_EVENT_NAME = "issue_comment";
|
||||
const result = parseSdkOptions({});
|
||||
|
||||
expect(result.sdkOptions.settingSources).toEqual(["user"]);
|
||||
});
|
||||
|
||||
test("should use direct settingSources input when provided", () => {
|
||||
const options: ClaudeOptions = {
|
||||
settingSources: "user,project,local",
|
||||
};
|
||||
const result = parseSdkOptions(options);
|
||||
|
||||
expect(result.sdkOptions.settingSources).toEqual([
|
||||
"user",
|
||||
"project",
|
||||
"local",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should use --setting-sources from claudeArgs when no direct input", () => {
|
||||
const options: ClaudeOptions = {
|
||||
claudeArgs: "--setting-sources user,project",
|
||||
};
|
||||
const result = parseSdkOptions(options);
|
||||
|
||||
expect(result.sdkOptions.settingSources).toEqual(["user", "project"]);
|
||||
expect(result.sdkOptions.extraArgs?.["setting-sources"]).toBeUndefined();
|
||||
});
|
||||
|
||||
test("direct input should take precedence over claudeArgs", () => {
|
||||
const options: ClaudeOptions = {
|
||||
settingSources: "user",
|
||||
claudeArgs: "--setting-sources user,project,local",
|
||||
};
|
||||
const result = parseSdkOptions(options);
|
||||
|
||||
expect(result.sdkOptions.settingSources).toEqual(["user"]);
|
||||
});
|
||||
|
||||
test("should trim whitespace in comma-separated values", () => {
|
||||
const options: ClaudeOptions = {
|
||||
settingSources: "user, project , local",
|
||||
};
|
||||
const result = parseSdkOptions(options);
|
||||
|
||||
expect(result.sdkOptions.settingSources).toEqual([
|
||||
"user",
|
||||
"project",
|
||||
"local",
|
||||
]);
|
||||
});
|
||||
|
||||
test("explicit defaultSettingSources overrides the event-gated default", () => {
|
||||
process.env.GITHUB_EVENT_NAME = "pull_request_target";
|
||||
const options: ClaudeOptions = {
|
||||
defaultSettingSources: ["user", "project", "local"],
|
||||
};
|
||||
const result = parseSdkOptions(options);
|
||||
|
||||
expect(result.sdkOptions.settingSources).toEqual([
|
||||
"user",
|
||||
"project",
|
||||
"local",
|
||||
]);
|
||||
});
|
||||
|
||||
test("--setting-sources in claudeArgs should win over defaultSettingSources", () => {
|
||||
const options: ClaudeOptions = {
|
||||
claudeArgs: "--setting-sources user",
|
||||
defaultSettingSources: ["user", "project", "local"],
|
||||
};
|
||||
const result = parseSdkOptions(options);
|
||||
|
||||
expect(result.sdkOptions.settingSources).toEqual(["user"]);
|
||||
});
|
||||
|
||||
test("empty-string settingSources falls through to claudeArgs then default", () => {
|
||||
// YAML default: "" — INPUT_SETTING_SOURCES is "" when the user doesn't set the input
|
||||
const options: ClaudeOptions = {
|
||||
settingSources: "",
|
||||
claudeArgs: "--setting-sources user,project",
|
||||
defaultSettingSources: ["user", "project", "local"],
|
||||
};
|
||||
const result = parseSdkOptions(options);
|
||||
|
||||
expect(result.sdkOptions.settingSources).toEqual(["user", "project"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.81",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.118",
|
||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||
"@octokit/graphql": "^8.2.2",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
@@ -37,41 +37,31 @@
|
||||
|
||||
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.81", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-CBeebgibBEN/DWOQGZN67vhuTG55RbI1hlsFSSoZ4uA/Io3lw04eHTE2ISCmdbqyJaefYTt6GKZei1nP0TQMNw=="],
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.118", "", { "dependencies": { "@anthropic-ai/sdk": "^0.81.0", "@modelcontextprotocol/sdk": "^1.29.0" }, "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.118", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.118", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.118", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.118" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-OfxCTzmfqvctpTLd3CP+UrpC0JdhYcJp12rD+SK29k+9+hrbblCrLobvhdWpTuYFejTPJuiLVsbHxq0BkEuELQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.118", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RudnoBekv0c9CPL0EeMc4RqDe4Pb7tdz/2oxa5EYqaajXNRlYtTvru9q7wq7Zvp40JQ24hz38swOTJ7PkW7G/g=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.118", "", { "os": "darwin", "cpu": "x64" }, "sha512-Hf/H46uElpfygALlb4KZR2EuyyJRe7jBuWa+TDA4jmAHVblNfwkVyaCp8s61hZINB3kAmXdLdM81VI+xwruWzA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.118", "", { "os": "linux", "cpu": "arm64" }, "sha512-lwMXnweJKpzESezJFM8mngRxJfaq/N0gqyFXBm5bOYaPIZnlGlP3h1JMKsJeqC4neLVGbe5a3Hq4T22Rr7OoAA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.118", "", { "os": "linux", "cpu": "arm64" }, "sha512-gSuZS8GM8MZuklzAJS8VCCjqK2UJJeerV+JpVYzXNMelotq4sXUg2dp17VbjCJ1jhUC9u1gpzlQDWkmYrXCbOg=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.2.118", "", { "os": "linux", "cpu": "x64" }, "sha512-m0KBbwN9s0+hQwAPzeUFvegrEqoT9EOC+Vz3vr4dd9FcZyvKZE0yiv9S7YbFp1ZKWDQmppmvpcB+9eME7WQ0yA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.118", "", { "os": "linux", "cpu": "x64" }, "sha512-36lG1F9IsuNBV7AzJY98z8KwryoWZCeEtMzgZL7614zPBhZGBsziQUZEBm2Eu7FVWbRQmYv6BL52+gffpkM4Gw=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.118", "", { "os": "win32", "cpu": "arm64" }, "sha512-o30/SL084+a8wJ+5cgKM1BflxiBUEy+xEcEpZPW+zCFtiqY0b1Pr+K35ECsbKBrv+w5/0Byp4/CvCkP15Otsgw=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.2.118", "", { "os": "win32", "cpu": "x64" }, "sha512-TSqsVBUaZGgYMkjCZckXhPvmJDTS7C6VAl4IOeMVNB/oPINVFaobtVagjYvY0BFnlDCOzz6sb8puafHwcm7qQA=="],
|
||||
|
||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.81.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-D4K5PvEV6wPiRtVlVsJHIUhHAmOZ6IT/I9rKlTf84gR7GyyAurPJK7z9BOf/AZqC5d1DhYQGJNKRmV+q8dGhgw=="],
|
||||
|
||||
"@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="],
|
||||
|
||||
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
||||
|
||||
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
|
||||
|
||||
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
|
||||
|
||||
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
|
||||
|
||||
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
|
||||
|
||||
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
|
||||
|
||||
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
|
||||
|
||||
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
|
||||
|
||||
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
|
||||
|
||||
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
|
||||
|
||||
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
|
||||
"@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="],
|
||||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.16.0", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-8ofX7gkZcLj9H9rSd50mCgm3SSF8C7XoclxJuLoV0Cz3rEQ1tv9MZRYYvJtm9n1BiEQQMzSmE/w2AEkNacLYfg=="],
|
||||
|
||||
@@ -113,6 +103,8 @@
|
||||
|
||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||
|
||||
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"before-after-hook": ["before-after-hook@2.2.3", "", {}, "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="],
|
||||
@@ -183,6 +175,8 @@
|
||||
|
||||
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
||||
|
||||
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
||||
|
||||
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
||||
|
||||
"finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="],
|
||||
@@ -209,20 +203,30 @@
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"hono": ["hono@4.12.9", "", {}, "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA=="],
|
||||
|
||||
"http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||
|
||||
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="],
|
||||
|
||||
"json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||
|
||||
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
||||
@@ -269,6 +273,8 @@
|
||||
|
||||
"raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="],
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
|
||||
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
@@ -299,6 +305,8 @@
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
|
||||
|
||||
"tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="],
|
||||
|
||||
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
||||
@@ -327,6 +335,8 @@
|
||||
|
||||
"zod-to-json-schema": ["zod-to-json-schema@3.24.6", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
|
||||
|
||||
"@octokit/core/@octokit/graphql": ["@octokit/graphql@7.1.1", "", { "dependencies": { "@octokit/request": "^8.4.1", "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" } }, "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g=="],
|
||||
|
||||
"@octokit/core/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
||||
@@ -359,12 +369,24 @@
|
||||
|
||||
"accepts/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||
|
||||
"ajv-formats/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
|
||||
|
||||
"express/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||
|
||||
"send/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||
|
||||
"type-is/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.3.1", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
||||
|
||||
"@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
||||
|
||||
"@octokit/endpoint/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
||||
@@ -403,12 +425,24 @@
|
||||
|
||||
"accepts/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"express/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"type-is/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/express/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/raw-body/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"@octokit/plugin-request-log/@octokit/core/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@10.1.4", "", { "dependencies": { "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA=="],
|
||||
|
||||
"@octokit/rest/@octokit/core/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@10.1.4", "", { "dependencies": { "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA=="],
|
||||
@@ -416,5 +450,15 @@
|
||||
"@octokit/rest/@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
||||
|
||||
"@octokit/rest/@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/express/body-parser/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/express/body-parser/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/express/body-parser/qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/express/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@modelcontextprotocol/sdk/raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Intentionally minimal. action.yml pins --config to this file so bun resolves
|
||||
# its runtime config from the action directory rather than the workspace.
|
||||
@@ -13,6 +13,9 @@
|
||||
- Accepts either a comma-separated list of specific usernames or `*` to allow all users
|
||||
- **Should be used with extreme caution** as it bypasses the primary security mechanism of this action
|
||||
- Is designed for automation workflows where user permissions are already restricted by the workflow's permission scope
|
||||
- When set, Claude does a best-effort scrub of Anthropic, cloud, and GitHub Actions secrets from subprocess environments. On Linux runners with bubblewrap available, subprocesses additionally run with PID-namespace isolation. This reduces but does not eliminate prompt injection risk — keep workflow permissions minimal and validate all outputs. Set `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB: 0` in your workflow or job `env:` block to opt out.
|
||||
- Optionally set `CLAUDE_CODE_SCRIPT_CAPS` in your workflow `env:` block to limit how many times Claude can call specific scripts per run. Value is JSON: `{"script-name.sh": maxCalls}`. Example: `CLAUDE_CODE_SCRIPT_CAPS: '{"edit-issue-labels.sh":2}'` allows at most 2 calls to `edit-issue-labels.sh`. Useful for write-capable helper scripts.
|
||||
- When using `allowed_non_write_users`, always pass `github_token: ${{ secrets.GITHUB_TOKEN }}`. The auto-generated workflow token is scoped to the job's declared permissions and expires when the job completes. **Do not use a personal access token** — a static token does not rotate between runs and could be partially or fully recovered over time via prompt injection. Restricting allowed tools via `claude_args` reduces the rate of recovery but may not eliminate the risk. We recommend restricting allowed tools (e.g. `claude_args: '--allowedTools "Bash(gh issue view:*)"'`) to the minimum required when using `allowed_non_write_users`.
|
||||
- **Token Permissions**: The GitHub app receives only a short-lived token scoped specifically to the repository it's operating in
|
||||
- **No Cross-Repository Access**: Each action invocation is limited to the repository where it was triggered
|
||||
- **Limited Scope**: The token cannot access other repositories or perform actions beyond the configured permissions
|
||||
@@ -31,6 +34,8 @@ This design ensures that users retain full control over what pull requests are c
|
||||
|
||||
**Beware of potential hidden markdown when tagging Claude on untrusted content.** External contributors may include hidden instructions through HTML comments, invisible characters, hidden attributes, or other techniques. The action sanitizes content by stripping HTML comments, invisible characters, markdown image alt text, hidden HTML attributes, and HTML entities, but new bypass techniques may emerge. We recommend reviewing the raw content of all input coming from external contributors before allowing Claude to process it.
|
||||
|
||||
On public repos, you can also use `include_comments_by_actor` to allowlist which users' comments are passed to Claude, reducing exposure to untrusted input. Use `exclude_comments_by_actor` to filter out noisy bot comments (e.g., `dependabot[bot]`, `renovate[bot]`). If an actor matches both lists, exclusion takes priority. See [Usage](./usage.md) for details.
|
||||
|
||||
## GitHub App Permissions
|
||||
|
||||
The [Claude Code GitHub app](https://github.com/apps/claude) requests the following permissions:
|
||||
|
||||
+2
-1
@@ -421,7 +421,8 @@ jobs:
|
||||
- `./scripts/gh.sh label list` to see available labels
|
||||
|
||||
Based on your analysis, add the appropriate labels using:
|
||||
`./scripts/edit-issue-labels.sh --issue [number] --add-label "label1" --add-label "label2"`
|
||||
`./scripts/edit-issue-labels.sh --add-label "label1" --add-label "label2"`
|
||||
(the issue number is read automatically from the workflow event)
|
||||
|
||||
If it appears to be a duplicate, post a comment mentioning the original issue.
|
||||
|
||||
|
||||
@@ -76,6 +76,8 @@ jobs:
|
||||
| `ssh_signing_key` | SSH private key for signing commits. Enables signed commits with full git CLI support (rebasing, etc.). See [Security](./security.md#commit-signing) | No | "" |
|
||||
| `bot_id` | GitHub user ID to use for git operations (defaults to Claude's bot ID). Required with `ssh_signing_key` for verified commits | No | `41898282` |
|
||||
| `bot_name` | GitHub username to use for git operations (defaults to Claude's bot name). Required with `ssh_signing_key` for verified commits | No | `claude[bot]` |
|
||||
| `include_comments_by_actor` | Comma-separated list of actor usernames to INCLUDE in comments. Supports the `*[bot]` wildcard to match all bot accounts. Empty (default) includes all actors | No | "" |
|
||||
| `exclude_comments_by_actor` | Comma-separated list of actor usernames to EXCLUDE from comments. Supports the `*[bot]` wildcard to match all bot accounts. If an actor matches both lists, exclusion takes priority | No | "" |
|
||||
| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots. **⚠️ On public repos with `'*'`, external Apps may be able to invoke this action.** See [Security](./security.md) | No | "" |
|
||||
| `allowed_non_write_users` | **⚠️ RISKY**: Comma-separated list of usernames to allow without write permissions, or '\*' for all users. Only works with `github_token` input. See [Security](./security.md) | No | "" |
|
||||
| `path_to_claude_code_executable` | Optional path to a custom Claude Code executable. Skips automatic installation. Useful for Nix, custom containers, or specialized environments | No | "" |
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
name: Auto Fix CI Failures
|
||||
|
||||
# ⚠️ SECURITY NOTE
|
||||
#
|
||||
# This workflow checks out the PR branch and runs build/test commands
|
||||
# (npm, bun, etc.) against it with elevated permissions (contents:write,
|
||||
# id-token:write). This means code from the PR branch executes in a
|
||||
# trusted context with access to secrets and the ability to push to the
|
||||
# repository.
|
||||
#
|
||||
# Only use this workflow in repositories where everyone with write access
|
||||
# is fully trusted with these permissions. Do not use this in repositories
|
||||
# that accept contributions from untrusted or semi-trusted collaborators.
|
||||
#
|
||||
# The pull_requests[0] check below limits this to same-repo PRs (fork PRs
|
||||
# are excluded), but anyone who can push a branch to this repository can
|
||||
# control what code runs here.
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["CI"]
|
||||
@@ -35,10 +51,14 @@ jobs:
|
||||
|
||||
- name: Create fix branch
|
||||
id: branch
|
||||
env:
|
||||
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
run: |
|
||||
BRANCH_NAME="claude-auto-fix-ci-${{ github.event.workflow_run.head_branch }}-${{ github.run_id }}"
|
||||
SAFE_BRANCH=$(printf '%s' "$HEAD_BRANCH" | tr -cd 'a-zA-Z0-9/_.-')
|
||||
BRANCH_NAME="claude-auto-fix-ci-${SAFE_BRANCH}-${RUN_ID}"
|
||||
git checkout -b "$BRANCH_NAME"
|
||||
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Get CI failure details
|
||||
id: failure_details
|
||||
|
||||
@@ -53,6 +53,8 @@ jobs:
|
||||
fromJSON(steps.detect.outputs.structured_output).confidence >= 0.7
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
WORKFLOW_NAME: ${{ github.event.workflow_run.name }}
|
||||
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||
run: |
|
||||
OUTPUT='${{ steps.detect.outputs.structured_output }}'
|
||||
CONFIDENCE=$(echo "$OUTPUT" | jq -r '.confidence')
|
||||
@@ -63,8 +65,7 @@ jobs:
|
||||
echo ""
|
||||
echo "Triggering automatic retry..."
|
||||
|
||||
gh workflow run "${{ github.event.workflow_run.name }}" \
|
||||
--ref "${{ github.event.workflow_run.head_branch }}"
|
||||
gh workflow run "$WORKFLOW_NAME" --ref "$HEAD_BRANCH"
|
||||
|
||||
# Low confidence flaky detection - skip retry
|
||||
- name: Low confidence detection
|
||||
@@ -83,13 +84,14 @@ jobs:
|
||||
if: github.event.workflow_run.event == 'pull_request'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||
run: |
|
||||
OUTPUT='${{ steps.detect.outputs.structured_output }}'
|
||||
IS_FLAKY=$(echo "$OUTPUT" | jq -r '.is_flaky')
|
||||
CONFIDENCE=$(echo "$OUTPUT" | jq -r '.confidence')
|
||||
SUMMARY=$(echo "$OUTPUT" | jq -r '.summary')
|
||||
|
||||
pr_number=$(gh pr list --head "${{ github.event.workflow_run.head_branch }}" --json number --jq '.[0].number')
|
||||
pr_number=$(gh pr list --head "$HEAD_BRANCH" --json number --jq '.[0].number')
|
||||
|
||||
if [ -n "$pr_number" ]; then
|
||||
if [ "$IS_FLAKY" = "true" ]; then
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.81",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.118",
|
||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||
"@octokit/graphql": "^8.2.2",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Edits labels on a GitHub issue.
|
||||
# Usage: ./scripts/edit-issue-labels.sh --issue 123 --add-label bug --add-label needs-triage --remove-label untriaged
|
||||
# Usage: ./scripts/edit-issue-labels.sh --add-label bug --add-label needs-triage --remove-label untriaged
|
||||
#
|
||||
# The issue number is read from the workflow event payload.
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ISSUE=""
|
||||
# Read from event payload so the issue number is bound to the triggering event
|
||||
ISSUE=$(jq -r '.issue.number // empty' "${GITHUB_EVENT_PATH:?GITHUB_EVENT_PATH not set}")
|
||||
if ! [[ "$ISSUE" =~ ^[0-9]+$ ]]; then
|
||||
echo "Error: no issue number in event payload" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ADD_LABELS=()
|
||||
REMOVE_LABELS=()
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--issue)
|
||||
ISSUE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--add-label)
|
||||
ADD_LABELS+=("$2")
|
||||
shift 2
|
||||
@@ -26,20 +30,12 @@ while [[ $# -gt 0 ]]; do
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Error: unknown argument (only --add-label and --remove-label are accepted)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate issue number
|
||||
if [[ -z "$ISSUE" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ "$ISSUE" =~ ^[0-9]+$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ${#ADD_LABELS[@]} -eq 0 && ${#REMOVE_LABELS[@]} -eq 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
+26
-11
@@ -43,10 +43,16 @@ import type { ClaudeRunResult } from "../../base-action/src/run-claude-sdk";
|
||||
|
||||
/**
|
||||
* Install Claude Code CLI, handling retry logic and custom executable paths.
|
||||
* Returns the absolute path to the claude executable.
|
||||
*/
|
||||
async function installClaudeCode(): Promise<void> {
|
||||
async function installClaudeCode(): Promise<string> {
|
||||
const customExecutable = process.env.PATH_TO_CLAUDE_CODE_EXECUTABLE;
|
||||
if (customExecutable) {
|
||||
if (/[\x00-\x1f\x7f]/.test(customExecutable)) {
|
||||
throw new Error(
|
||||
"PATH_TO_CLAUDE_CODE_EXECUTABLE contains control characters (e.g. newlines), which is not allowed",
|
||||
);
|
||||
}
|
||||
console.log(`Using custom Claude Code executable: ${customExecutable}`);
|
||||
const claudeDir = dirname(customExecutable);
|
||||
// Add to PATH by appending to GITHUB_PATH
|
||||
@@ -56,10 +62,10 @@ async function installClaudeCode(): Promise<void> {
|
||||
}
|
||||
// Also add to current process PATH
|
||||
process.env.PATH = `${claudeDir}:${process.env.PATH}`;
|
||||
return;
|
||||
return customExecutable;
|
||||
}
|
||||
|
||||
const claudeCodeVersion = "2.1.81";
|
||||
const claudeCodeVersion = "2.1.118";
|
||||
console.log(`Installing Claude Code v${claudeCodeVersion}...`);
|
||||
|
||||
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||
@@ -88,7 +94,7 @@ async function installClaudeCode(): Promise<void> {
|
||||
await appendFile(githubPath, `${homeBin}\n`);
|
||||
}
|
||||
process.env.PATH = `${homeBin}:${process.env.PATH}`;
|
||||
return;
|
||||
return `${homeBin}/claude`;
|
||||
} catch (error) {
|
||||
if (attempt === 3) {
|
||||
throw new Error(
|
||||
@@ -99,6 +105,7 @@ async function installClaudeCode(): Promise<void> {
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
}
|
||||
}
|
||||
throw new Error("unreachable");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,7 +222,7 @@ async function run() {
|
||||
prepareCompleted = true;
|
||||
|
||||
// Phase 2: Install Claude Code CLI
|
||||
await installClaudeCode();
|
||||
const claudeExecutable = await installClaudeCode();
|
||||
|
||||
// Phase 3: Run Claude (import base-action directly)
|
||||
// Set env vars needed by the base-action code
|
||||
@@ -229,11 +236,12 @@ async function run() {
|
||||
// Restore them from the base branch before the CLI reads them.
|
||||
//
|
||||
// We read pull_request.base.ref from the payload directly because agent
|
||||
// mode's branchInfo.baseBranch defaults to "main" rather than the PR's
|
||||
// actual target (agent/index.ts). For issue_comment on a PR the payload
|
||||
// mode's branchInfo.baseBranch defaults to the repo's default branch rather
|
||||
// than the PR's actual target (agent/index.ts). For issue_comment on a PR the payload
|
||||
// lacks base.ref, so we fall back to the mode-provided value — tag mode
|
||||
// fetches it from GraphQL; agent mode on issue_comment is an edge case
|
||||
// that at worst restores from the wrong trusted branch (still secure).
|
||||
let configRestoredFromBase = false;
|
||||
if (isEntityContext(context) && context.isPR) {
|
||||
let restoreBase = baseBranch;
|
||||
if (
|
||||
@@ -246,6 +254,7 @@ async function run() {
|
||||
}
|
||||
if (restoreBase) {
|
||||
restoreConfigFromBase(restoreBase);
|
||||
configRestoredFromBase = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +263,7 @@ async function run() {
|
||||
await installPlugins(
|
||||
process.env.INPUT_PLUGIN_MARKETPLACES,
|
||||
process.env.INPUT_PLUGINS,
|
||||
process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE,
|
||||
claudeExecutable,
|
||||
);
|
||||
|
||||
const promptFile =
|
||||
@@ -269,9 +278,15 @@ async function run() {
|
||||
claudeArgs: prepareResult.claudeArgs,
|
||||
appendSystemPrompt: process.env.APPEND_SYSTEM_PROMPT,
|
||||
model: process.env.ANTHROPIC_MODEL,
|
||||
pathToClaudeCodeExecutable:
|
||||
process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE,
|
||||
pathToClaudeCodeExecutable: claudeExecutable,
|
||||
showFullOutput: process.env.INPUT_SHOW_FULL_OUTPUT,
|
||||
settingSources: process.env.INPUT_SETTING_SOURCES,
|
||||
// Only assert that project/local config is safe to load when it was actually
|
||||
// restored from the base branch above. Otherwise leave undefined so
|
||||
// parseSdkOptions applies its event-gated default.
|
||||
defaultSettingSources: configRestoredFromBase
|
||||
? ["user", "project", "local"]
|
||||
: undefined,
|
||||
});
|
||||
|
||||
claudeSuccess = claudeResult.conclusion === "success";
|
||||
@@ -312,7 +327,7 @@ async function run() {
|
||||
commentId,
|
||||
githubToken,
|
||||
claudeBranch,
|
||||
baseBranch: baseBranch || "main",
|
||||
baseBranch: baseBranch || context.repository.default_branch || "main",
|
||||
triggerUsername: context.actor,
|
||||
context,
|
||||
octokit,
|
||||
|
||||
@@ -253,7 +253,8 @@ async function run() {
|
||||
commentId: parseInt(process.env.CLAUDE_COMMENT_ID!),
|
||||
githubToken,
|
||||
claudeBranch: process.env.CLAUDE_BRANCH,
|
||||
baseBranch: process.env.BASE_BRANCH || "main",
|
||||
baseBranch:
|
||||
process.env.BASE_BRANCH || context.repository.default_branch || "main",
|
||||
triggerUsername: process.env.TRIGGER_USERNAME,
|
||||
context,
|
||||
octokit,
|
||||
|
||||
@@ -12,6 +12,13 @@ export const PR_QUERY = `
|
||||
baseRefName
|
||||
headRefName
|
||||
headRefOid
|
||||
isCrossRepository
|
||||
headRepository {
|
||||
owner {
|
||||
login
|
||||
}
|
||||
name
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
lastEditedAt
|
||||
|
||||
@@ -79,6 +79,7 @@ type BaseContext = {
|
||||
owner: string;
|
||||
repo: string;
|
||||
full_name: string;
|
||||
default_branch?: string;
|
||||
};
|
||||
actor: string;
|
||||
inputs: {
|
||||
@@ -140,6 +141,7 @@ export function parseGitHubContext(): GitHubContext {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
full_name: `${context.repo.owner}/${context.repo.repo}`,
|
||||
default_branch: context.payload.repository?.default_branch,
|
||||
},
|
||||
actor: context.actor,
|
||||
inputs: {
|
||||
|
||||
@@ -299,7 +299,7 @@ export async function fetchGitHubData({
|
||||
includeCommentsByActor,
|
||||
excludeCommentsByActor,
|
||||
);
|
||||
reviewData = pullRequest.reviews || [];
|
||||
reviewData = pullRequest.reviews || { nodes: [] };
|
||||
|
||||
console.log(`Successfully fetched PR #${prNumber} data`);
|
||||
} else {
|
||||
|
||||
@@ -28,7 +28,7 @@ function extractFirstLabel(githubData: FetchDataResult): string | undefined {
|
||||
*
|
||||
* Valid branch names:
|
||||
* - Start with alphanumeric character (not dash, to prevent option injection)
|
||||
* - Contain only alphanumeric, forward slash, hyphen, underscore, or period
|
||||
* - Contain only alphanumeric, forward slash, hyphen, underscore, period, or hash (#)
|
||||
* - Do not start or end with a period
|
||||
* - Do not end with a slash
|
||||
* - Do not contain '..' (path traversal)
|
||||
@@ -58,12 +58,16 @@ export function validateBranchName(branchName: string): void {
|
||||
);
|
||||
}
|
||||
|
||||
// Strict whitelist pattern: alphanumeric start, then alphanumeric/slash/hyphen/underscore/period
|
||||
const validPattern = /^[a-zA-Z0-9][a-zA-Z0-9/_.-]*$/;
|
||||
// Strict whitelist pattern: alphanumeric start, then alphanumeric/slash/hyphen/underscore/period/hash/plus.
|
||||
// # is valid per git-check-ref-format and commonly used in branch names like "fix/#123-description".
|
||||
// + is valid per git-check-ref-format and generated by Claude Code's EnterWorktree tool when
|
||||
// converting worktree names containing "/" (e.g. "feat/foo" becomes "worktree-feat+foo").
|
||||
// All git calls use execFileSync (not shell interpolation), so neither # nor + carries injection risk.
|
||||
const validPattern = /^[a-zA-Z0-9][a-zA-Z0-9/_.#+-]*$/;
|
||||
|
||||
if (!validPattern.test(branchName)) {
|
||||
throw new Error(
|
||||
`Invalid branch name: "${branchName}". Branch names must start with an alphanumeric character and contain only alphanumeric characters, forward slashes, hyphens, underscores, or periods.`,
|
||||
`Invalid branch name: "${branchName}". Branch names must start with an alphanumeric character and contain only alphanumeric characters, forward slashes, hyphens, underscores, periods, hashes (#), or plus signs (+).`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,7 +122,7 @@ export function validateBranchName(branchName: string): void {
|
||||
* @param args - Git command arguments (e.g., ["checkout", "branch-name"])
|
||||
*/
|
||||
function execGit(args: string[]): void {
|
||||
execFileSync("git", args, { stdio: "inherit" });
|
||||
execFileSync("git", args, { stdio: "inherit", env: process.env });
|
||||
}
|
||||
|
||||
export type BranchInfo = {
|
||||
@@ -164,9 +168,23 @@ export async function setupBranch(
|
||||
// Validate branch names before use to prevent command injection
|
||||
validateBranchName(branchName);
|
||||
|
||||
// Execute git commands to checkout PR branch (dynamic depth based on PR size)
|
||||
// Using execFileSync instead of shell template literals for security
|
||||
execGit(["fetch", "origin", `--depth=${fetchDepth}`, branchName]);
|
||||
// For cross-repository (fork) PRs, fetch via the pull ref since the
|
||||
// branch only exists on the fork's remote, not on origin.
|
||||
if (prData.isCrossRepository) {
|
||||
console.log(
|
||||
`PR #${entityNumber} is from a fork, fetching via refs/pull/${entityNumber}/head...`,
|
||||
);
|
||||
execGit([
|
||||
"fetch",
|
||||
"origin",
|
||||
`--depth=${fetchDepth}`,
|
||||
`pull/${entityNumber}/head:${branchName}`,
|
||||
]);
|
||||
} else {
|
||||
// Execute git commands to checkout PR branch (dynamic depth based on PR size)
|
||||
// Using execFileSync instead of shell template literals for security
|
||||
execGit(["fetch", "origin", `--depth=${fetchDepth}`, branchName]);
|
||||
}
|
||||
execGit(["checkout", branchName, "--"]);
|
||||
|
||||
console.log(`Successfully checked out PR branch for PR #${entityNumber}`);
|
||||
|
||||
@@ -51,11 +51,34 @@ export async function configureGitAuth(
|
||||
console.log("No existing authentication headers to remove");
|
||||
}
|
||||
|
||||
// Update the remote URL to include the token for authentication
|
||||
console.log("Updating remote URL with authentication...");
|
||||
const remoteUrl = `https://x-access-token:${githubToken}@${serverUrl.host}/${context.repository.owner}/${context.repository.repo}.git`;
|
||||
await $`git remote set-url origin ${remoteUrl}`;
|
||||
console.log("✓ Updated remote URL with authentication token");
|
||||
if (process.env.ALLOWED_NON_WRITE_USERS) {
|
||||
// When processing content from non-write users, use a credential helper
|
||||
// instead of embedding the token in the remote URL. The helper script reads
|
||||
// from GH_TOKEN at auth time, so .git/config stays token-free. Written as a
|
||||
// file to avoid shell-escaping the helper body; placed under
|
||||
// GITHUB_ACTION_PATH so it sits alongside the action source.
|
||||
console.log("Configuring git credential helper...");
|
||||
process.env.GH_TOKEN = githubToken;
|
||||
const helperPath = join(
|
||||
process.env.GITHUB_ACTION_PATH || homedir(),
|
||||
".git-credential-gh-token",
|
||||
);
|
||||
await writeFile(
|
||||
helperPath,
|
||||
'#!/bin/sh\necho username=x-access-token\necho password="$GH_TOKEN"\n',
|
||||
{ mode: 0o700 },
|
||||
);
|
||||
const cleanUrl = `https://${serverUrl.host}/${context.repository.owner}/${context.repository.repo}.git`;
|
||||
await $`git remote set-url origin ${cleanUrl}`;
|
||||
await $`git config credential.helper ${helperPath}`;
|
||||
console.log("✓ Configured credential helper");
|
||||
} else {
|
||||
// Update the remote URL to include the token for authentication
|
||||
console.log("Updating remote URL with authentication...");
|
||||
const remoteUrl = `https://x-access-token:${githubToken}@${serverUrl.host}/${context.repository.owner}/${context.repository.repo}.git`;
|
||||
await $`git remote set-url origin ${remoteUrl}`;
|
||||
console.log("✓ Updated remote URL with authentication token");
|
||||
}
|
||||
|
||||
console.log("Git authentication configured successfully");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { execFileSync } from "child_process";
|
||||
import { rmSync } from "fs";
|
||||
import { cpSync, existsSync, rmSync } from "fs";
|
||||
|
||||
// Paths that are both PR-controllable and read from cwd at CLI startup.
|
||||
//
|
||||
@@ -15,6 +15,9 @@ const SENSITIVE_PATHS = [
|
||||
".claude.json",
|
||||
".gitmodules",
|
||||
".ripgreprc",
|
||||
"CLAUDE.md",
|
||||
"CLAUDE.local.md",
|
||||
".husky",
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -44,19 +47,46 @@ export function restoreConfigFromBase(baseBranch: string): void {
|
||||
`Restoring ${SENSITIVE_PATHS.join(", ")} from origin/${baseBranch} (PR head is untrusted)`,
|
||||
);
|
||||
|
||||
// Fetch base first — if this fails we haven't touched the workspace and the
|
||||
// caller sees a clean error.
|
||||
execFileSync("git", ["fetch", "origin", baseBranch, "--depth=1"], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
// Snapshot every PR-authored sensitive path into .claude-pr/ before deletion
|
||||
// so review agents can inspect what the PR changes without those files ever
|
||||
// being executed. Captured before the security delete so it reflects the
|
||||
// PR-authored version.
|
||||
rmSync(".claude-pr", { recursive: true, force: true });
|
||||
for (const p of SENSITIVE_PATHS) {
|
||||
if (existsSync(p)) {
|
||||
cpSync(p, `.claude-pr/${p}`, { recursive: true });
|
||||
}
|
||||
}
|
||||
if (existsSync(".claude-pr")) {
|
||||
console.log(
|
||||
"Preserved PR's sensitive paths → .claude-pr/ for review agents (not executed)",
|
||||
);
|
||||
}
|
||||
|
||||
// Delete PR-controlled versions. If the restore below fails for a given path,
|
||||
// that path stays deleted — the safe fallback (no attacker-controlled config).
|
||||
// A bare `git checkout` alone wouldn't remove files the PR added, so nuke first.
|
||||
// Delete PR-controlled versions BEFORE fetching so the attacker-controlled
|
||||
// .gitmodules is absent during the network operation. If git reads .gitmodules
|
||||
// during fetch (fetch.recurseSubmodules=on-demand, the git default), it will
|
||||
// attempt to fetch submodule objects and block on credential prompts in CI —
|
||||
// causing an indefinite hang. Deleting first closes that window.
|
||||
//
|
||||
// If the restore below fails for a given path, that path stays deleted —
|
||||
// the safe fallback (no attacker-controlled config). A bare `git checkout`
|
||||
// alone wouldn't remove files the PR added, so nuke first.
|
||||
for (const p of SENSITIVE_PATHS) {
|
||||
rmSync(p, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
// --no-recurse-submodules: explicitly suppress submodule fetching regardless of
|
||||
// fetch.recurseSubmodules config. Defense-in-depth alongside the delete above.
|
||||
execFileSync(
|
||||
"git",
|
||||
["fetch", "origin", baseBranch, "--depth=1", "--no-recurse-submodules"],
|
||||
{
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
},
|
||||
);
|
||||
|
||||
for (const p of SENSITIVE_PATHS) {
|
||||
try {
|
||||
execFileSync("git", ["checkout", `origin/${baseBranch}`, "--", p], {
|
||||
|
||||
+6
-2
@@ -141,10 +141,14 @@ export async function setupGitHubToken(): Promise<string> {
|
||||
const permissions = parseAdditionalPermissions();
|
||||
|
||||
console.log("Exchanging OIDC token for app token...");
|
||||
const appToken = await retryWithBackoff(() =>
|
||||
exchangeForAppToken(oidcToken, permissions),
|
||||
const appToken = await retryWithBackoff(
|
||||
() => exchangeForAppToken(oidcToken, permissions),
|
||||
{
|
||||
shouldRetry: (error) => !(error instanceof WorkflowValidationSkipError),
|
||||
},
|
||||
);
|
||||
console.log("App token successfully obtained");
|
||||
core.setSecret(appToken);
|
||||
|
||||
console.log("Using GITHUB_TOKEN from OIDC");
|
||||
return appToken;
|
||||
|
||||
@@ -57,6 +57,13 @@ export type GitHubPullRequest = {
|
||||
baseRefName: string;
|
||||
headRefName: string;
|
||||
headRefOid: string;
|
||||
isCrossRepository: boolean;
|
||||
headRepository: {
|
||||
owner: {
|
||||
login: string;
|
||||
};
|
||||
name: string;
|
||||
} | null;
|
||||
createdAt: string;
|
||||
updatedAt?: string;
|
||||
lastEditedAt?: string;
|
||||
|
||||
@@ -85,15 +85,15 @@ export async function prepareAgentMode({
|
||||
|
||||
// Check for branch info from environment variables (useful for auto-fix workflows)
|
||||
const claudeBranch = process.env.CLAUDE_BRANCH || undefined;
|
||||
const baseBranch =
|
||||
process.env.BASE_BRANCH || context.inputs.baseBranch || "main";
|
||||
const defaultBranch = context.repository.default_branch || "main";
|
||||
const baseBranch = context.inputs.baseBranch || defaultBranch;
|
||||
|
||||
// Detect current branch from GitHub environment
|
||||
const currentBranch =
|
||||
claudeBranch ||
|
||||
process.env.GITHUB_HEAD_REF ||
|
||||
process.env.GITHUB_REF_NAME ||
|
||||
"main";
|
||||
defaultBranch;
|
||||
|
||||
// Get our GitHub MCP servers config
|
||||
const ourMcpConfig = await prepareMcpConfig({
|
||||
|
||||
@@ -3,6 +3,7 @@ export type RetryOptions = {
|
||||
initialDelayMs?: number;
|
||||
maxDelayMs?: number;
|
||||
backoffFactor?: number;
|
||||
shouldRetry?: (error: Error) => boolean;
|
||||
};
|
||||
|
||||
export async function retryWithBackoff<T>(
|
||||
@@ -14,6 +15,7 @@ export async function retryWithBackoff<T>(
|
||||
initialDelayMs = 5000,
|
||||
maxDelayMs = 20000,
|
||||
backoffFactor = 2,
|
||||
shouldRetry,
|
||||
} = options;
|
||||
|
||||
let delayMs = initialDelayMs;
|
||||
@@ -27,6 +29,11 @@ export async function retryWithBackoff<T>(
|
||||
lastError = error instanceof Error ? error : new Error(String(error));
|
||||
console.error(`Attempt ${attempt} failed:`, lastError.message);
|
||||
|
||||
if (shouldRetry && !shouldRetry(lastError)) {
|
||||
console.error("Error is not retryable, giving up immediately");
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
if (attempt < maxAttempts) {
|
||||
console.log(`Retrying in ${delayMs / 1000} seconds...`);
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
|
||||
@@ -27,6 +27,8 @@ describe("generatePrompt", () => {
|
||||
baseRefName: "main",
|
||||
headRefName: "feature-branch",
|
||||
headRefOid: "abc123",
|
||||
isCrossRepository: false,
|
||||
headRepository: { owner: { login: "testowner" }, name: "testrepo" },
|
||||
commits: {
|
||||
totalCount: 2,
|
||||
nodes: [
|
||||
|
||||
@@ -1006,6 +1006,8 @@ describe("fetchGitHubData integration with time filtering", () => {
|
||||
baseRefName: "main",
|
||||
headRefName: "feature",
|
||||
headRefOid: "abc123",
|
||||
isCrossRepository: false,
|
||||
headRepository: { owner: { login: "testowner" }, name: "testrepo" },
|
||||
createdAt: "2024-01-15T10:00:00Z",
|
||||
updatedAt: "2024-01-15T12:30:00Z", // Edited after trigger
|
||||
lastEditedAt: "2024-01-15T12:30:00Z", // Edited after trigger
|
||||
|
||||
@@ -24,6 +24,8 @@ describe("formatContext", () => {
|
||||
baseRefName: "main",
|
||||
headRefName: "feature/test",
|
||||
headRefOid: "abc123",
|
||||
isCrossRepository: false,
|
||||
headRepository: { owner: { login: "testowner" }, name: "testrepo" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
additions: 50,
|
||||
deletions: 30,
|
||||
|
||||
@@ -36,6 +36,7 @@ const defaultRepository = {
|
||||
owner: "test-owner",
|
||||
repo: "test-repo",
|
||||
full_name: "test-owner/test-repo",
|
||||
default_branch: "main",
|
||||
};
|
||||
|
||||
type MockContextOverrides = Omit<Partial<ParsedGitHubContext>, "inputs"> & {
|
||||
|
||||
@@ -88,7 +88,7 @@ describe("Agent Mode", () => {
|
||||
expect(result.claudeArgs).toBe("--model claude-sonnet-4 --max-turns 10");
|
||||
expect(result.claudeArgs).not.toContain("--mcp-config");
|
||||
|
||||
// Verify return structure - should use "main" as fallback when no env vars set
|
||||
// Verify return structure - should fall back to repository.default_branch when no env vars set
|
||||
expect(result).toEqual({
|
||||
commentId: undefined,
|
||||
branchInfo: {
|
||||
@@ -108,6 +108,60 @@ describe("Agent Mode", () => {
|
||||
process.env.GITHUB_REF_NAME = originalRefName;
|
||||
});
|
||||
|
||||
test("prepare falls back to repository.default_branch when not 'main'", async () => {
|
||||
const contextWithDevelop = createMockAutomationContext({
|
||||
eventName: "workflow_dispatch",
|
||||
repository: {
|
||||
owner: "test-owner",
|
||||
repo: "test-repo",
|
||||
full_name: "test-owner/test-repo",
|
||||
default_branch: "develop",
|
||||
},
|
||||
});
|
||||
|
||||
// Save and clear env vars that would otherwise override the fallback
|
||||
const originalClaudeBranch = process.env.CLAUDE_BRANCH;
|
||||
const originalHeadRef = process.env.GITHUB_HEAD_REF;
|
||||
const originalRefName = process.env.GITHUB_REF_NAME;
|
||||
delete process.env.CLAUDE_BRANCH;
|
||||
delete process.env.GITHUB_HEAD_REF;
|
||||
delete process.env.GITHUB_REF_NAME;
|
||||
|
||||
const mockOctokit = {
|
||||
rest: {
|
||||
users: {
|
||||
getAuthenticated: mock(() =>
|
||||
Promise.resolve({
|
||||
data: { login: "test-user", id: 12345, type: "User" },
|
||||
}),
|
||||
),
|
||||
getByUsername: mock(() =>
|
||||
Promise.resolve({
|
||||
data: { login: "test-user", id: 12345, type: "User" },
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
} as any;
|
||||
|
||||
const result = await prepareAgentMode({
|
||||
context: contextWithDevelop,
|
||||
octokit: mockOctokit,
|
||||
githubToken: "test-token",
|
||||
});
|
||||
|
||||
expect(result.branchInfo.baseBranch).toBe("develop");
|
||||
expect(result.branchInfo.currentBranch).toBe("develop");
|
||||
|
||||
// Restore env vars
|
||||
if (originalClaudeBranch !== undefined)
|
||||
process.env.CLAUDE_BRANCH = originalClaudeBranch;
|
||||
if (originalHeadRef !== undefined)
|
||||
process.env.GITHUB_HEAD_REF = originalHeadRef;
|
||||
if (originalRefName !== undefined)
|
||||
process.env.GITHUB_REF_NAME = originalRefName;
|
||||
});
|
||||
|
||||
test("prepare rejects bot actors without allowed_bots", async () => {
|
||||
const contextWithPrompts = createMockAutomationContext({
|
||||
eventName: "workflow_dispatch",
|
||||
|
||||
@@ -17,6 +17,8 @@ describe("pull_request_target event support", () => {
|
||||
baseRefName: "main",
|
||||
headRefName: "feature-branch",
|
||||
headRefOid: "abc123",
|
||||
isCrossRepository: false,
|
||||
headRepository: { owner: { login: "testowner" }, name: "testrepo" },
|
||||
commits: {
|
||||
totalCount: 2,
|
||||
nodes: [
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test";
|
||||
import { retryWithBackoff } from "../src/utils/retry";
|
||||
|
||||
describe("retryWithBackoff", () => {
|
||||
let originalConsoleLog: typeof console.log;
|
||||
let originalConsoleError: typeof console.error;
|
||||
|
||||
beforeEach(() => {
|
||||
originalConsoleLog = console.log;
|
||||
originalConsoleError = console.error;
|
||||
console.log = mock(() => {});
|
||||
console.error = mock(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
console.log = originalConsoleLog;
|
||||
console.error = originalConsoleError;
|
||||
});
|
||||
|
||||
it("returns the result on first success", async () => {
|
||||
const result = await retryWithBackoff(() => Promise.resolve("ok"), {
|
||||
maxAttempts: 3,
|
||||
initialDelayMs: 1,
|
||||
});
|
||||
expect(result).toBe("ok");
|
||||
});
|
||||
|
||||
it("retries on failure and succeeds", async () => {
|
||||
let attempt = 0;
|
||||
const result = await retryWithBackoff(
|
||||
() => {
|
||||
attempt++;
|
||||
if (attempt < 3) throw new Error("transient");
|
||||
return Promise.resolve("recovered");
|
||||
},
|
||||
{ maxAttempts: 3, initialDelayMs: 1 },
|
||||
);
|
||||
expect(result).toBe("recovered");
|
||||
expect(attempt).toBe(3);
|
||||
});
|
||||
|
||||
it("throws after exhausting all attempts", async () => {
|
||||
await expect(
|
||||
retryWithBackoff(() => Promise.reject(new Error("permanent")), {
|
||||
maxAttempts: 2,
|
||||
initialDelayMs: 1,
|
||||
}),
|
||||
).rejects.toThrow("permanent");
|
||||
});
|
||||
|
||||
it("stops retrying immediately when shouldRetry returns false", async () => {
|
||||
class NonRetryableError extends Error {
|
||||
constructor() {
|
||||
super("non-retryable");
|
||||
this.name = "NonRetryableError";
|
||||
}
|
||||
}
|
||||
|
||||
let attempts = 0;
|
||||
await expect(
|
||||
retryWithBackoff(
|
||||
() => {
|
||||
attempts++;
|
||||
throw new NonRetryableError();
|
||||
},
|
||||
{
|
||||
maxAttempts: 3,
|
||||
initialDelayMs: 1,
|
||||
shouldRetry: (error) => !(error instanceof NonRetryableError),
|
||||
},
|
||||
),
|
||||
).rejects.toThrow("non-retryable");
|
||||
expect(attempts).toBe(1);
|
||||
});
|
||||
|
||||
it("continues retrying when shouldRetry returns true", async () => {
|
||||
let attempts = 0;
|
||||
await expect(
|
||||
retryWithBackoff(
|
||||
() => {
|
||||
attempts++;
|
||||
throw new Error("retryable");
|
||||
},
|
||||
{
|
||||
maxAttempts: 3,
|
||||
initialDelayMs: 1,
|
||||
shouldRetry: () => true,
|
||||
},
|
||||
),
|
||||
).rejects.toThrow("retryable");
|
||||
expect(attempts).toBe(3);
|
||||
});
|
||||
|
||||
it("preserves the original error when shouldRetry aborts", async () => {
|
||||
class SpecificError extends Error {
|
||||
code = 401;
|
||||
constructor() {
|
||||
super("unauthorized");
|
||||
this.name = "SpecificError";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await retryWithBackoff(
|
||||
() => {
|
||||
throw new SpecificError();
|
||||
},
|
||||
{
|
||||
maxAttempts: 3,
|
||||
initialDelayMs: 1,
|
||||
shouldRetry: (error) => !(error instanceof SpecificError),
|
||||
},
|
||||
);
|
||||
expect.unreachable("should have thrown");
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(SpecificError);
|
||||
expect((error as SpecificError).code).toBe(401);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -36,6 +36,25 @@ describe("validateBranchName", () => {
|
||||
expect(() => validateBranchName("refs/heads/main")).not.toThrow();
|
||||
expect(() => validateBranchName("bugfix/JIRA-1234")).not.toThrow();
|
||||
});
|
||||
|
||||
it("should accept branch names containing # (git-valid, common in issue-linked branches)", () => {
|
||||
// Reported in #1137: branches like "put-back-arm64-#2" were rejected
|
||||
expect(() => validateBranchName("put-back-arm64-#2")).not.toThrow();
|
||||
expect(() =>
|
||||
validateBranchName("feature/#123-description"),
|
||||
).not.toThrow();
|
||||
expect(() => validateBranchName("fix/issue-#42")).not.toThrow();
|
||||
});
|
||||
|
||||
it("should accept branch names containing + (generated by Claude Code EnterWorktree)", () => {
|
||||
// EnterWorktree converts "/" in worktree names to "+" when generating branch names.
|
||||
// e.g. EnterWorktree("feat/skill-consolidation") → branch "worktree-feat+skill-consolidation"
|
||||
expect(() =>
|
||||
validateBranchName("worktree-feat+skill-consolidation"),
|
||||
).not.toThrow();
|
||||
expect(() => validateBranchName("fix+issue-123")).not.toThrow();
|
||||
expect(() => validateBranchName("feature+new-thing")).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("command injection attempts", () => {
|
||||
|
||||
Reference in New Issue
Block a user