From de27cf6b7ecd49a7276d4dd6b5de4115f73870cd Mon Sep 17 00:00:00 2001 From: Kellen Busby Date: Fri, 7 Aug 2026 12:15:48 -0700 Subject: [PATCH 1/3] pr-visual-recap-reusable: accept CLAUDE_CODE_OAUTH_TOKEN for the claude backend Reusable workflows can only receive secrets declared in their workflow_call contract, so callers had no way to bill recaps to a Claude subscription even though the Claude Code CLI already honors CLAUDE_CODE_OAUTH_TOKEN. Declare the token as an optional secret, give the gate a presence-only signal for it so the claude backend passes on either credential, and pass it into both Claude Code invocations. Empty passthrough values are unset before the CLI runs so an unconfigured secret is not mistaken for the chosen auth path. --- .../workflows/pr-visual-recap-reusable.yml | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-visual-recap-reusable.yml b/.github/workflows/pr-visual-recap-reusable.yml index 3f72908f04..b3328027d1 100644 --- a/.github/workflows/pr-visual-recap-reusable.yml +++ b/.github/workflows/pr-visual-recap-reusable.yml @@ -12,6 +12,8 @@ name: PR Visual Recap (reusable) # secrets: # PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} # ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} +# # …or bill the claude backend to a Claude subscription instead: +# # CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} # # IMPORTANT: callers must trigger on the same pull_request event types as this # file declares in its on.workflow_call section (opened, synchronize, reopened, @@ -110,6 +112,12 @@ on: # Required for the default claude backend; optional when agent=codex. ANTHROPIC_API_KEY: required: false + # Alternative to ANTHROPIC_API_KEY for the claude backend: a Claude Code + # subscription OAuth token minted with `claude setup-token`, which bills + # the run to the subscription instead of API credits. Set exactly one — + # with both configured the billing path is whichever the CLI prefers. + CLAUDE_CODE_OAUTH_TOKEN: + required: false # Required when agent=codex; ignored otherwise. OPENAI_API_KEY: required: false @@ -152,6 +160,7 @@ jobs: # Presence-only signals — never expose secret VALUES to the gate. HAS_PLAN: ${{ secrets.PLAN_RECAP_TOKEN != '' }} HAS_ANTHROPIC: ${{ secrets.ANTHROPIC_API_KEY != '' }} + HAS_CLAUDE_OAUTH: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN != '' }} HAS_OPENAI: ${{ secrets.OPENAI_API_KEY != '' }} HAS_COMPATIBLE: ${{ secrets.VISUAL_RECAP_API_KEY != '' }} AGENT: ${{ inputs.agent }} @@ -230,7 +239,7 @@ jobs: } else if (agent === 'codex') { if (process.env.HAS_OPENAI !== 'true') reasons.push('OPENAI_API_KEY not configured (codex backend)'); } else if (agent === 'claude') { - if (process.env.HAS_ANTHROPIC !== 'true') reasons.push('ANTHROPIC_API_KEY not configured (claude backend)'); + if (process.env.HAS_ANTHROPIC !== 'true' && process.env.HAS_CLAUDE_OAUTH !== 'true') reasons.push('neither ANTHROPIC_API_KEY nor CLAUDE_CODE_OAUTH_TOKEN configured (claude backend)'); } else { if (process.env.HAS_COMPATIBLE !== 'true') reasons.push('VISUAL_RECAP_API_KEY not configured (openai-compatible backend)'); if (!(process.env.VISUAL_RECAP_MODEL || '').trim()) reasons.push('VISUAL_RECAP_MODEL is required (openai-compatible backend)'); @@ -588,8 +597,14 @@ jobs: continue-on-error: true env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} run: | set -uo pipefail + # A secret the caller never configured still arrives here as an empty + # env var. Drop those so the CLI picks the credential that is actually + # set instead of treating an empty one as the chosen auth path. + [ -n "${ANTHROPIC_API_KEY:-}" ] || unset ANTHROPIC_API_KEY + [ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ] || unset CLAUDE_CODE_OAUTH_TOKEN CLAUDE_ALLOWED_TOOLS="Read,Write,Bash(git diff:*)" CLAUDE_ARGS=(-p "$(cat recap-prompt.md)" --allowedTools "$CLAUDE_ALLOWED_TOOLS" --permission-mode dontAsk --output-format json) CLAUDE_ARGS+=(--model "${VISUAL_RECAP_MODEL:-claude-sonnet-5}") @@ -755,8 +770,11 @@ jobs: continue-on-error: true env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} run: | set -uo pipefail + [ -n "${ANTHROPIC_API_KEY:-}" ] || unset ANTHROPIC_API_KEY + [ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ] || unset CLAUDE_CODE_OAUTH_TOKEN CLAUDE_ALLOWED_TOOLS="Read,Write" CLAUDE_ARGS=(-p "$(cat recap-repair-prompt.md)" --allowedTools "$CLAUDE_ALLOWED_TOOLS" --permission-mode dontAsk --output-format json) CLAUDE_ARGS+=(--model "${VISUAL_RECAP_MODEL:-claude-sonnet-5}") From 1c4cecb6dc9d71ca79f3cfa67d8e8f5e9aeb7642 Mon Sep 17 00:00:00 2001 From: Kellen Busby Date: Fri, 7 Aug 2026 12:28:17 -0700 Subject: [PATCH 2/3] recap: carry the Claude subscription token through the installer and docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reusable workflow now accepts CLAUDE_CODE_OAUTH_TOKEN, so the copy and fork workflows, the caller template recap setup writes, and the gate twin the CLI evaluates all need the same alternative or they disagree about what a working repo looks like — recap doctor would report ANTHROPIC_API_KEY missing on a repo whose recaps run fine. recapRequiredSecrets now returns interchangeable secret names rather than one name per backend, so setup pushes whichever credential is available locally and doctor accepts either, warning only when both are configured. --- .changeset/recap-claude-oauth-token.md | 6 + .github/workflows/pr-visual-recap-fork.yml | 12 +- .github/workflows/pr-visual-recap.yml | 12 +- .../content/locales/ar-SA/pr-visual-recap.mdx | 12 +- .../content/locales/de-DE/pr-visual-recap.mdx | 12 +- .../content/locales/es-ES/pr-visual-recap.mdx | 12 +- .../content/locales/fr-FR/pr-visual-recap.mdx | 12 +- .../content/locales/hi-IN/pr-visual-recap.mdx | 12 +- .../content/locales/ja-JP/pr-visual-recap.mdx | 12 +- .../content/locales/ko-KR/pr-visual-recap.mdx | 12 +- .../content/locales/pt-BR/pr-visual-recap.mdx | 12 +- .../content/locales/zh-CN/pr-visual-recap.mdx | 12 +- .../content/locales/zh-TW/pr-visual-recap.mdx | 12 +- .../core/docs/content/pr-visual-recap.mdx | 22 ++-- .../src/pr-visual-recap-workflow.spec.ts | 16 +++ packages/recap-cli/src/recap-gate.spec.ts | 20 ++++ packages/recap-cli/src/recap.ts | 109 +++++++++++++----- 17 files changed, 238 insertions(+), 79 deletions(-) create mode 100644 .changeset/recap-claude-oauth-token.md diff --git a/.changeset/recap-claude-oauth-token.md b/.changeset/recap-claude-oauth-token.md new file mode 100644 index 0000000000..493d9ecf72 --- /dev/null +++ b/.changeset/recap-claude-oauth-token.md @@ -0,0 +1,6 @@ +--- +"@agent-native/recap-cli": minor +"@agent-native/core": patch +--- + +Accept `CLAUDE_CODE_OAUTH_TOKEN` as an alternative to `ANTHROPIC_API_KEY` for the PR visual recap claude backend, so recaps can bill a Claude subscription instead of API credits. `recapRequiredSecrets` now returns interchangeable secret names, and `recap setup` / `recap doctor` accept either credential. diff --git a/.github/workflows/pr-visual-recap-fork.yml b/.github/workflows/pr-visual-recap-fork.yml index a24248bc8d..aa139fbdb5 100644 --- a/.github/workflows/pr-visual-recap-fork.yml +++ b/.github/workflows/pr-visual-recap-fork.yml @@ -65,6 +65,7 @@ jobs: # Presence-only signals — never expose secret VALUES to the gate. HAS_PLAN: ${{ secrets.PLAN_RECAP_TOKEN != '' }} HAS_ANTHROPIC: ${{ secrets.ANTHROPIC_API_KEY != '' }} + HAS_CLAUDE_OAUTH: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN != '' }} HAS_OPENAI: ${{ secrets.OPENAI_API_KEY != '' }} HAS_COMPATIBLE: ${{ secrets.VISUAL_RECAP_API_KEY != '' }} AGENT: ${{ env.VISUAL_RECAP_AGENT }} @@ -196,7 +197,7 @@ jobs: } else if (agent === 'codex') { if (process.env.HAS_OPENAI !== 'true') reasons.push('OPENAI_API_KEY not configured (codex backend)'); } else if (agent === 'claude') { - if (process.env.HAS_ANTHROPIC !== 'true') reasons.push('ANTHROPIC_API_KEY not configured (claude backend)'); + if (process.env.HAS_ANTHROPIC !== 'true' && process.env.HAS_CLAUDE_OAUTH !== 'true') reasons.push('neither ANTHROPIC_API_KEY nor CLAUDE_CODE_OAUTH_TOKEN configured (claude backend)'); } else { if (process.env.HAS_COMPATIBLE !== 'true') reasons.push('VISUAL_RECAP_API_KEY not configured (openai-compatible backend)'); if (!(process.env.VISUAL_RECAP_MODEL || '').trim()) reasons.push('VISUAL_RECAP_MODEL is required (openai-compatible backend)'); @@ -579,8 +580,14 @@ jobs: continue-on-error: true env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} run: | set -uo pipefail + # A secret the repo never configured still arrives here as an empty + # env var. Drop those so the CLI picks the credential that is actually + # set instead of treating an empty one as the chosen auth path. + [ -n "${ANTHROPIC_API_KEY:-}" ] || unset ANTHROPIC_API_KEY + [ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ] || unset CLAUDE_CODE_OAUTH_TOKEN CLAUDE_ALLOWED_TOOLS="Read,Write,Bash(git diff:*)" CLAUDE_ARGS=(-p "$(cat recap-prompt.md)" --allowedTools "$CLAUDE_ALLOWED_TOOLS" --permission-mode dontAsk --output-format json) CLAUDE_ARGS+=(--model "${VISUAL_RECAP_MODEL:-claude-sonnet-5}") @@ -746,8 +753,11 @@ jobs: continue-on-error: true env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} run: | set -uo pipefail + [ -n "${ANTHROPIC_API_KEY:-}" ] || unset ANTHROPIC_API_KEY + [ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ] || unset CLAUDE_CODE_OAUTH_TOKEN CLAUDE_ALLOWED_TOOLS="Read,Write" CLAUDE_ARGS=(-p "$(cat recap-repair-prompt.md)" --allowedTools "$CLAUDE_ALLOWED_TOOLS" --permission-mode dontAsk --output-format json) CLAUDE_ARGS+=(--model "${VISUAL_RECAP_MODEL:-claude-sonnet-5}") diff --git a/.github/workflows/pr-visual-recap.yml b/.github/workflows/pr-visual-recap.yml index 9466885421..303eeccf18 100644 --- a/.github/workflows/pr-visual-recap.yml +++ b/.github/workflows/pr-visual-recap.yml @@ -46,6 +46,7 @@ jobs: # Presence-only signals — never expose secret VALUES to the gate. HAS_PLAN: ${{ secrets.PLAN_RECAP_TOKEN != '' }} HAS_ANTHROPIC: ${{ secrets.ANTHROPIC_API_KEY != '' }} + HAS_CLAUDE_OAUTH: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN != '' }} HAS_OPENAI: ${{ secrets.OPENAI_API_KEY != '' }} HAS_COMPATIBLE: ${{ secrets.VISUAL_RECAP_API_KEY != '' }} AGENT: ${{ env.VISUAL_RECAP_AGENT }} @@ -125,7 +126,7 @@ jobs: } else if (agent === 'codex') { if (process.env.HAS_OPENAI !== 'true') reasons.push('OPENAI_API_KEY not configured (codex backend)'); } else if (agent === 'claude') { - if (process.env.HAS_ANTHROPIC !== 'true') reasons.push('ANTHROPIC_API_KEY not configured (claude backend)'); + if (process.env.HAS_ANTHROPIC !== 'true' && process.env.HAS_CLAUDE_OAUTH !== 'true') reasons.push('neither ANTHROPIC_API_KEY nor CLAUDE_CODE_OAUTH_TOKEN configured (claude backend)'); } else { if (process.env.HAS_COMPATIBLE !== 'true') reasons.push('VISUAL_RECAP_API_KEY not configured (openai-compatible backend)'); if (!(process.env.VISUAL_RECAP_MODEL || '').trim()) reasons.push('VISUAL_RECAP_MODEL is required (openai-compatible backend)'); @@ -522,8 +523,14 @@ jobs: continue-on-error: true env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} run: | set -uo pipefail + # A secret the repo never configured still arrives here as an empty + # env var. Drop those so the CLI picks the credential that is actually + # set instead of treating an empty one as the chosen auth path. + [ -n "${ANTHROPIC_API_KEY:-}" ] || unset ANTHROPIC_API_KEY + [ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ] || unset CLAUDE_CODE_OAUTH_TOKEN CLAUDE_ALLOWED_TOOLS="Read,Write,Bash(git diff:*)" CLAUDE_ARGS=(-p "$(cat recap-prompt.md)" --allowedTools "$CLAUDE_ALLOWED_TOOLS" --permission-mode dontAsk --output-format json) CLAUDE_ARGS+=(--model "${VISUAL_RECAP_MODEL:-claude-sonnet-5}") @@ -694,8 +701,11 @@ jobs: continue-on-error: true env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} run: | set -uo pipefail + [ -n "${ANTHROPIC_API_KEY:-}" ] || unset ANTHROPIC_API_KEY + [ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ] || unset CLAUDE_CODE_OAUTH_TOKEN CLAUDE_ALLOWED_TOOLS="Read,Write" CLAUDE_ARGS=(-p "$(cat recap-repair-prompt.md)" --allowedTools "$CLAUDE_ALLOWED_TOOLS" --permission-mode dontAsk --output-format json) CLAUDE_ARGS+=(--model "${VISUAL_RECAP_MODEL:-claude-sonnet-5}") diff --git a/packages/core/docs/content/locales/ar-SA/pr-visual-recap.mdx b/packages/core/docs/content/locales/ar-SA/pr-visual-recap.mdx index f0e504407a..a6d171438a 100644 --- a/packages/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +++ b/packages/core/docs/content/locales/ar-SA/pr-visual-recap.mdx @@ -156,6 +156,8 @@ npx @agent-native/recap-cli@latest recap doctor إذا لم يتم تعيين المتغير، يستخدم الإجراء `claude`. +تصادق واجهة `claude` الخلفية باستخدام أيٍّ من بيانات الاعتماد: يحتسب `ANTHROPIC_API_KEY` تكلفة الرموز على حساب Anthropic API الخاص بك، بينما يحتسب `CLAUDE_CODE_OAUTH_TOKEN` — الذي يُنشأ محليًا بالأمر `claude setup-token` على اشتراك Claude — تكلفة التشغيل على ذلك الاشتراك. عيّن واحدًا فقط؛ فإذا ضُبط كلاهما، فسيعتمد مسار الفوترة على ما تفضّله واجهة Claude Code CLI. + ## النموذج والمنطق خارج الواجهة الخلفية، يوجد متغيران في المستودع يضبطان _كيف_ يعمل الوكيل: @@ -190,10 +192,11 @@ npx @agent-native/recap-cli@latest recap doctor ### الأسرار (مطلوب اثنان فقط) -| سرية | الغرض | -| ------------------- | ---------------------------------------------------------------------------------------------------------------------- | -| `PLAN_RECAP_TOKEN` | رمز مميز قابل للإلغاء تم سكه بواسطة `npx @agent-native/core@latest connect`. يسمح بنشر خطة الملخّص وتحميل لقطة الشاشة. | -| `ANTHROPIC_API_KEY` | مفتاح LLM للواجهة الخلفية الافتراضية لرمز Claude. | +| سرية | الغرض | +| ------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `PLAN_RECAP_TOKEN` | رمز مميز قابل للإلغاء تم سكه بواسطة `npx @agent-native/core@latest connect`. يسمح بنشر خطة الملخّص وتحميل لقطة الشاشة. | +| `ANTHROPIC_API_KEY` | مفتاح LLM للواجهة الخلفية الافتراضية لرمز Claude. | +| `CLAUDE_CODE_OAUTH_TOKEN` | بديل عن `ANTHROPIC_API_KEY`: رمز اشتراك Claude يُنشأ بالأمر `claude setup-token`. عيّن أحدهما فقط وليس كليهما. | **الفرق: استخدم رمزًا مميزًا لخدمة المؤسسة.** الرمز المميز الشخصي مرتبط بالشخص من قام بسكها - إذا تركوا المؤسسة أو أبطلوا رموزهم المميزة، فسيتم استخدام كل عملية إعادة شراء @@ -494,6 +497,7 @@ jobs: secrets: PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL }} with: diff --git a/packages/core/docs/content/locales/de-DE/pr-visual-recap.mdx b/packages/core/docs/content/locales/de-DE/pr-visual-recap.mdx index 36830d79c0..1dc93c4ae1 100644 --- a/packages/core/docs/content/locales/de-DE/pr-visual-recap.mdx +++ b/packages/core/docs/content/locales/de-DE/pr-visual-recap.mdx @@ -156,6 +156,8 @@ Wählen Sie mit der Repository-Variable `VISUAL_RECAP_AGENT` aus, welcher Codier Wenn die Variable nicht gesetzt ist, verwendet die Aktion `claude`. +Das `claude`-Backend authentifiziert sich mit einem der beiden Anmeldedaten: `ANTHROPIC_API_KEY` rechnet Tokens über Ihr Anthropic-API-Konto ab, während `CLAUDE_CODE_OAUTH_TOKEN` — lokal mit `claude setup-token` auf einem Claude-Abonnement erstellt — den Lauf stattdessen über dieses Abonnement abrechnet. Setzen Sie genau eines; sind beide konfiguriert, entscheidet die Claude-Code-CLI über den Abrechnungsweg. + ## Modell und Argumentation Über das Backend hinaus optimieren zwei Repository-Variablen, _wie_ der Agent ausgeführt wird: @@ -190,10 +192,11 @@ Legen Sie diese in den **Einstellungen → Geheimnisse und Variablen → Actions ### Geheimnisse (nur zwei erforderlich) -| Geheimnis | Zweck | -| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `PLAN_RECAP_TOKEN` | Widerruflicher Token, geprägt von `npx @agent-native/core@latest connect`. Autorisiert die Veröffentlichung des Zusammenfassungsplans und des Screenshot-Uploads. | -| `ANTHROPIC_API_KEY` | Der LLM-Schlüssel für das Standard-Claude-Code-Backend. | +| Geheimnis | Zweck | +| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PLAN_RECAP_TOKEN` | Widerruflicher Token, geprägt von `npx @agent-native/core@latest connect`. Autorisiert die Veröffentlichung des Zusammenfassungsplans und des Screenshot-Uploads. | +| `ANTHROPIC_API_KEY` | Der LLM-Schlüssel für das Standard-Claude-Code-Backend. | +| `CLAUDE_CODE_OAUTH_TOKEN` | Alternative zu `ANTHROPIC_API_KEY`: ein Claude-Abonnement-Token aus `claude setup-token`. Setzen Sie das eine oder das andere, nicht beides. | **Teams: Verwenden Sie ein Organisationsdienst-Token.** Ein persönliches Token ist an die Person gebunden Wer hat es geprägt – wenn sie die Organisation verlassen oder ihre Token widerrufen, jedes Repo mit @@ -494,6 +497,7 @@ jobs: secrets: PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL }} with: diff --git a/packages/core/docs/content/locales/es-ES/pr-visual-recap.mdx b/packages/core/docs/content/locales/es-ES/pr-visual-recap.mdx index a340e6f92e..a130cdb11b 100644 --- a/packages/core/docs/content/locales/es-ES/pr-visual-recap.mdx +++ b/packages/core/docs/content/locales/es-ES/pr-visual-recap.mdx @@ -156,6 +156,8 @@ Elija qué agente de codificación ejecuta la habilidad con la variable de repos Si la variable no está configurada, la acción utiliza `claude`. +El backend `claude` se autentica con cualquiera de las dos credenciales: `ANTHROPIC_API_KEY` factura los tokens a su cuenta de la API de Anthropic, mientras que `CLAUDE_CODE_OAUTH_TOKEN` —generado localmente con `claude setup-token` en una suscripción de Claude— factura la ejecución a esa suscripción. Configure exactamente una; si ambas están configuradas, la vía de facturación será la que prefiera la CLI de Claude Code. + ## Modelo y razonamiento Más allá del backend, dos variables del repositorio ajustan _cómo_ se ejecuta el agente: @@ -190,10 +192,11 @@ Configúrelos en **Configuración → Secretos y variables → Actions** de su r ### Secretos (solo se requieren dos) -| Secreto | Propósito | -| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `PLAN_RECAP_TOKEN` | Token revocable acuñado por `npx @agent-native/core@latest connect`. Autoriza la publicación del plan de resumen y la carga de la captura de pantalla. | -| `ANTHROPIC_API_KEY` | La clave LLM para el backend predeterminado del código Claude. | +| Secreto | Propósito | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `PLAN_RECAP_TOKEN` | Token revocable acuñado por `npx @agent-native/core@latest connect`. Autoriza la publicación del plan de resumen y la carga de la captura de pantalla. | +| `ANTHROPIC_API_KEY` | La clave LLM para el backend predeterminado del código Claude. | +| `CLAUDE_CODE_OAUTH_TOKEN` | Alternativa a `ANTHROPIC_API_KEY`: un token de suscripción de Claude generado con `claude setup-token`. Configure una u otra, no ambas. | **Equipos: utilice un token de servicio de la organización.** Un token personal está vinculado a la persona quién lo acuñó: si abandonan la organización o revocan sus tokens, cada repositorio lo usará @@ -494,6 +497,7 @@ jobs: secrets: PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL }} with: diff --git a/packages/core/docs/content/locales/fr-FR/pr-visual-recap.mdx b/packages/core/docs/content/locales/fr-FR/pr-visual-recap.mdx index c89f5ed1c3..cf56550b91 100644 --- a/packages/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +++ b/packages/core/docs/content/locales/fr-FR/pr-visual-recap.mdx @@ -156,6 +156,8 @@ Choisissez quel agent de codage exécute la compétence avec la variable du réf Si la variable n'est pas définie, l'action utilise `claude`. +Le backend `claude` s'authentifie avec l'un ou l'autre identifiant : `ANTHROPIC_API_KEY` facture les jetons sur votre compte API Anthropic, tandis que `CLAUDE_CODE_OAUTH_TOKEN` — créé localement avec `claude setup-token` sur un abonnement Claude — impute l'exécution à cet abonnement. N'en définissez qu'un seul ; si les deux sont configurés, la voie de facturation dépend de celle que la CLI Claude Code privilégie. + ## Modèle et raisonnement Au-delà du backend, deux variables du référentiel ajustent _comment_ l'agent s'exécute : @@ -190,10 +192,11 @@ Définissez-les dans **Paramètres → Secrets et variables → Actions** de vot ### Secrets (seulement deux requis) -| Secret | Objectif | -| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `PLAN_RECAP_TOKEN` | Jeton révocable émis par `npx @agent-native/core@latest connect`. Autorise la publication du plan récapitulatif et le téléchargement de la capture d'écran. | -| `ANTHROPIC_API_KEY` | La clé LLM pour le backend du code Claude par défaut. | +| Secret | Objectif | +| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PLAN_RECAP_TOKEN` | Jeton révocable émis par `npx @agent-native/core@latest connect`. Autorise la publication du plan récapitulatif et le téléchargement de la capture d'écran. | +| `ANTHROPIC_API_KEY` | La clé LLM pour le backend du code Claude par défaut. | +| `CLAUDE_CODE_OAUTH_TOKEN` | Alternative à `ANTHROPIC_API_KEY` : un jeton d'abonnement Claude créé avec `claude setup-token`. Définissez l'un ou l'autre, pas les deux. | **Équipes : utilisez un jeton de service d'organisation.** Un jeton personnel est lié à la personne qui l'a créé – s'il quitte l'organisation ou révoque ses jetons, chaque dépôt utilisant @@ -495,6 +498,7 @@ jobs: secrets: PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL }} with: diff --git a/packages/core/docs/content/locales/hi-IN/pr-visual-recap.mdx b/packages/core/docs/content/locales/hi-IN/pr-visual-recap.mdx index af0e9abf6c..1db06af7f2 100644 --- a/packages/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +++ b/packages/core/docs/content/locales/hi-IN/pr-visual-recap.mdx @@ -156,6 +156,8 @@ npx @agent-native/recap-cli@latest recap doctor यदि वेरिएबल अनसेट है, तो कार्रवाई `claude` का उपयोग करती है। +`claude` बैकएंड इनमें से किसी भी क्रेडेंशियल से प्रमाणित होता है: `ANTHROPIC_API_KEY` टोकन का बिल आपके Anthropic API खाते में जोड़ता है, जबकि Claude सब्सक्रिप्शन पर `claude setup-token` से स्थानीय रूप से बनाया गया `CLAUDE_CODE_OAUTH_TOKEN` उस रन का बिल उसी सब्सक्रिप्शन में जोड़ता है। इनमें से ठीक एक ही सेट करें; दोनों कॉन्फ़िगर होने पर बिलिंग किस रास्ते जाएगी यह Claude Code CLI की पसंद पर निर्भर करता है। + ## मॉडल और तर्क बैकएंड से परे, दो रिपॉजिटरी वैरिएबल एजेंट को चलाने के तरीके को ट्यून करते हैं: @@ -190,10 +192,11 @@ npx @agent-native/recap-cli@latest recap doctor ### रहस्य (केवल दो आवश्यक) -| गुप्त | उद्देश्य | -| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -| `PLAN_RECAP_TOKEN` | `npx @agent-native/core@latest connect` द्वारा प्रतिसंहरणीय टोकन ढाला गया। पुनर्कथन योजना और स्क्रीनशॉट अपलोड को प्रकाशित करने के लिए अधिकृत करता है। | -| `ANTHROPIC_API_KEY` | डिफ़ॉल्ट Claude कोड बैकएंड के लिए LLM कुंजी। | +| गुप्त | उद्देश्य | +| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PLAN_RECAP_TOKEN` | `npx @agent-native/core@latest connect` द्वारा प्रतिसंहरणीय टोकन ढाला गया। पुनर्कथन योजना और स्क्रीनशॉट अपलोड को प्रकाशित करने के लिए अधिकृत करता है। | +| `ANTHROPIC_API_KEY` | डिफ़ॉल्ट Claude कोड बैकएंड के लिए LLM कुंजी। | +| `CLAUDE_CODE_OAUTH_TOKEN` | `ANTHROPIC_API_KEY` का विकल्प: `claude setup-token` से बनाया गया Claude सब्सक्रिप्शन टोकन। दोनों नहीं, कोई एक सेट करें। | **टीमें: एक संगठन सेवा टोकन का उपयोग करें।** एक व्यक्तिगत टोकन व्यक्ति से जुड़ा होता है इसे किसने बनाया - यदि वे संगठन छोड़ देते हैं या अपने टोकन रद्द कर देते हैं, तो प्रत्येक रेपो का उपयोग किया जाता है @@ -495,6 +498,7 @@ jobs: secrets: PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL }} with: diff --git a/packages/core/docs/content/locales/ja-JP/pr-visual-recap.mdx b/packages/core/docs/content/locales/ja-JP/pr-visual-recap.mdx index fb10f550de..07f3b420b4 100644 --- a/packages/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +++ b/packages/core/docs/content/locales/ja-JP/pr-visual-recap.mdx @@ -156,6 +156,8 @@ npx @agent-native/recap-cli@latest recap doctor 変数が設定されていない場合、アクションは `claude` を使用します。 +`claude` バックエンドはどちらの資格情報でも認証できます。`ANTHROPIC_API_KEY` は Anthropic API アカウントにトークン料金が課金され、Claude サブスクリプションで `claude setup-token` を実行してローカルに発行した `CLAUDE_CODE_OAUTH_TOKEN` は実行分をそのサブスクリプションに課金します。どちらか一方だけを設定してください。両方を設定した場合、どちらで課金されるかは Claude Code CLI の選択次第になります。 + ## モデルと推論 バックエンドを超えて、2 つのリポジトリ変数がエージェントの実行方法を調整します。 @@ -190,10 +192,11 @@ npx @agent-native/recap-cli@latest recap doctor ### シークレット (必須は 2 つだけ) -| 秘密 | 目的 | -| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -| `PLAN_RECAP_TOKEN` | `npx @agent-native/core@latest connect` によって作成された取り消し可能なトークン。まとめ計画の公開とスクリーンショットのアップロードを承認します。 | -| `ANTHROPIC_API_KEY` | デフォルトの Claude コード バックエンドの LLM キー。 | +| 秘密 | 目的 | +| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PLAN_RECAP_TOKEN` | `npx @agent-native/core@latest connect` によって作成された取り消し可能なトークン。まとめ計画の公開とスクリーンショットのアップロードを承認します。 | +| `ANTHROPIC_API_KEY` | デフォルトの Claude コード バックエンドの LLM キー。 | +| `CLAUDE_CODE_OAUTH_TOKEN` | `ANTHROPIC_API_KEY` の代替。`claude setup-token` で発行する Claude サブスクリプションのトークン。どちらか一方のみを設定します。 | **Teams: 組織サービス トークンを使用します。** 個人トークンは個人にバインドされます 誰が作成したか — 彼らが組織を離れるかトークンを取り消した場合、すべてのリポジトリは @@ -492,6 +495,7 @@ jobs: secrets: PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL }} with: diff --git a/packages/core/docs/content/locales/ko-KR/pr-visual-recap.mdx b/packages/core/docs/content/locales/ko-KR/pr-visual-recap.mdx index 7addef554b..ffcf1b6b2e 100644 --- a/packages/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +++ b/packages/core/docs/content/locales/ko-KR/pr-visual-recap.mdx @@ -156,6 +156,8 @@ npx @agent-native/recap-cli@latest recap doctor 변수가 설정되지 않은 경우 작업은 `claude`를 사용합니다. +`claude` 백엔드는 두 자격 증명 중 하나로 인증합니다. `ANTHROPIC_API_KEY`는 Anthropic API 계정에 토큰 요금을 청구하고, Claude 구독에서 `claude setup-token`으로 로컬에서 발급한 `CLAUDE_CODE_OAUTH_TOKEN`은 해당 실행을 그 구독에 청구합니다. 둘 중 하나만 설정하세요. 둘 다 설정하면 어느 쪽으로 청구될지는 Claude Code CLI의 선택에 따릅니다. + ## 모델 및 추론 백엔드 외에도 두 개의 저장소 변수가 에이전트 실행 *방법*을 조정합니다. @@ -190,10 +192,11 @@ npx @agent-native/recap-cli@latest recap doctor ### 비밀번호(2개만 필요) -| 비밀 | 목적 | -| ------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `PLAN_RECAP_TOKEN` | `npx @agent-native/core@latest connect`가 발행한 취소 가능한 토큰입니다. 요약 계획 게시 및 스크린샷 업로드를 승인합니다. | -| `ANTHROPIC_API_KEY` | 기본 Claude 코드 백엔드에 대한 LLM 키입니다. | +| 비밀 | 목적 | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `PLAN_RECAP_TOKEN` | `npx @agent-native/core@latest connect`가 발행한 취소 가능한 토큰입니다. 요약 계획 게시 및 스크린샷 업로드를 승인합니다. | +| `ANTHROPIC_API_KEY` | 기본 Claude 코드 백엔드에 대한 LLM 키입니다. | +| `CLAUDE_CODE_OAUTH_TOKEN` | `ANTHROPIC_API_KEY`의 대안: `claude setup-token`으로 발급한 Claude 구독 토큰. 둘 중 하나만 설정합니다. | **팀: 조직 서비스 토큰을 사용합니다.** 개인 토큰은 개인에게 귀속됩니다. 만든 사람 - 조직을 떠나거나 토큰을 취소하면 모든 저장소는 @@ -494,6 +497,7 @@ jobs: secrets: PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL }} with: diff --git a/packages/core/docs/content/locales/pt-BR/pr-visual-recap.mdx b/packages/core/docs/content/locales/pt-BR/pr-visual-recap.mdx index bc43d0044b..9af5ec2c43 100644 --- a/packages/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +++ b/packages/core/docs/content/locales/pt-BR/pr-visual-recap.mdx @@ -156,6 +156,8 @@ Escolha qual agente de codificação executa a habilidade com a variável de rep Se a variável não estiver definida, a ação usará `claude`. +O backend `claude` se autentica com qualquer uma das credenciais: `ANTHROPIC_API_KEY` cobra os tokens na sua conta da API da Anthropic, enquanto `CLAUDE_CODE_OAUTH_TOKEN` — gerado localmente com `claude setup-token` em uma assinatura Claude — cobra a execução nessa assinatura. Defina exatamente uma; com as duas configuradas, o caminho de cobrança será o que a CLI do Claude Code preferir. + ## Modelo e raciocínio Além do back-end, duas variáveis de repositório ajustam _como_ o agente é executado: @@ -190,10 +192,11 @@ Defina-os em **Configurações → Segredos e variáveis → Actions** do seu re ### Segredos (são necessários apenas dois) -| Segredo | Propósito | -| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -| `PLAN_RECAP_TOKEN` | Token revogável cunhado por `npx @agent-native/core@latest connect`. Autoriza a publicação do plano de recapitulação e do upload da captura de tela. | -| `ANTHROPIC_API_KEY` | A chave LLM para o back-end padrão do código Claude. | +| Segredo | Propósito | +| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PLAN_RECAP_TOKEN` | Token revogável cunhado por `npx @agent-native/core@latest connect`. Autoriza a publicação do plano de recapitulação e do upload da captura de tela. | +| `ANTHROPIC_API_KEY` | A chave LLM para o back-end padrão do código Claude. | +| `CLAUDE_CODE_OAUTH_TOKEN` | Alternativa ao `ANTHROPIC_API_KEY`: um token de assinatura Claude gerado com `claude setup-token`. Defina uma ou outra, não as duas. | **Equipes: use um token de serviço organizacional.** Um token pessoal está vinculado à pessoa quem o cunhou — se eles deixarem a organização ou revogarem seus tokens, todos os repositórios usarão @@ -494,6 +497,7 @@ jobs: secrets: PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL }} with: diff --git a/packages/core/docs/content/locales/zh-CN/pr-visual-recap.mdx b/packages/core/docs/content/locales/zh-CN/pr-visual-recap.mdx index 93ed90a904..6b217ac388 100644 --- a/packages/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +++ b/packages/core/docs/content/locales/zh-CN/pr-visual-recap.mdx @@ -156,6 +156,8 @@ npx @agent-native/recap-cli@latest recap doctor 如果变量未设置,则操作使用 `claude`。 +`claude` 后端可使用其中任一凭据进行身份验证:`ANTHROPIC_API_KEY` 按令牌计入你的 Anthropic API 账户,而通过 `claude setup-token` 在 Claude 订阅上本地生成的 `CLAUDE_CODE_OAUTH_TOKEN` 则将本次运行计入该订阅。请只设置其中一个;如果两者都配置,计费方式取决于 Claude Code CLI 的选择。 + ## 模型和推理 除了后端之外,两个存储库变量还可以调整代理的运行方式: @@ -190,10 +192,11 @@ npx @agent-native/recap-cli@latest recap doctor ### 秘密(只需两个) -| 秘密 | 目的 | -| ------------------- | ----------------------------------------------------------------------------------------- | -| `PLAN_RECAP_TOKEN` | 由 `npx @agent-native/core@latest connect` 铸造的可撤销代币。授权发布回顾计划和截图上传。 | -| `ANTHROPIC_API_KEY` | 默认 Claude 代码后端的 LLM 密钥。 | +| 秘密 | 目的 | +| ------------------------- | -------------------------------------------------------------------------------------------------------------- | +| `PLAN_RECAP_TOKEN` | 由 `npx @agent-native/core@latest connect` 铸造的可撤销代币。授权发布回顾计划和截图上传。 | +| `ANTHROPIC_API_KEY` | 默认 Claude 代码后端的 LLM 密钥。 | +| `CLAUDE_CODE_OAUTH_TOKEN` | `ANTHROPIC_API_KEY` 的替代方案:通过 `claude setup-token` 生成的 Claude 订阅令牌。二者只设其一,不要同时设置。 | **团队:使用组织服务令牌。**个人令牌与人员绑定 谁铸造了它 - 如果他们离开组织或撤销他们的代币,每个存储库都会使用 @@ -492,6 +495,7 @@ jobs: secrets: PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL }} with: diff --git a/packages/core/docs/content/locales/zh-TW/pr-visual-recap.mdx b/packages/core/docs/content/locales/zh-TW/pr-visual-recap.mdx index 24f43ba3ce..858e18dc75 100644 --- a/packages/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +++ b/packages/core/docs/content/locales/zh-TW/pr-visual-recap.mdx @@ -156,6 +156,8 @@ npx @agent-native/recap-cli@latest recap doctor 如果變數未設定,則操作使用 `claude`。 +`claude` 後端可使用其中任一憑證進行驗證:`ANTHROPIC_API_KEY` 會將權杖用量計入你的 Anthropic API 帳戶,而透過 `claude setup-token` 在 Claude 訂閱上於本機產生的 `CLAUDE_CODE_OAUTH_TOKEN` 則會將這次執行計入該訂閱。請只設定其中一個;若兩者都設定,計費方式取決於 Claude Code CLI 的選擇。 + ## 模型和推理 除了後端之外,兩個儲存庫變數還可以調整代理的執行方式: @@ -190,10 +192,11 @@ npx @agent-native/recap-cli@latest recap doctor ### 秘密(只需兩個) -| 秘密 | 目的 | -| ------------------- | ----------------------------------------------------------------------------------------- | -| `PLAN_RECAP_TOKEN` | 由 `npx @agent-native/core@latest connect` 鑄造的可撤銷代幣。授權發布回顧計畫和截圖上傳。 | -| `ANTHROPIC_API_KEY` | 預設 Claude 程式碼後端的 LLM 金鑰。 | +| 秘密 | 目的 | +| ------------------------- | -------------------------------------------------------------------------------------------------------- | +| `PLAN_RECAP_TOKEN` | 由 `npx @agent-native/core@latest connect` 鑄造的可撤銷代幣。授權發布回顧計畫和截圖上傳。 | +| `ANTHROPIC_API_KEY` | 預設 Claude 程式碼後端的 LLM 金鑰。 | +| `CLAUDE_CODE_OAUTH_TOKEN` | `ANTHROPIC_API_KEY` 的替代方案:以 `claude setup-token` 產生的 Claude 訂閱權杖。兩者擇一,不要同時設定。 | **團隊:使用組織服務權杖。**個人權杖與人員綁定 誰鑄造了它 - 如果他們離開組織或撤銷他們的代幣,每個儲存庫都會使用 @@ -492,6 +495,7 @@ jobs: secrets: PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL }} with: diff --git a/packages/core/docs/content/pr-visual-recap.mdx b/packages/core/docs/content/pr-visual-recap.mdx index 100a5e2203..54ebb1c449 100644 --- a/packages/core/docs/content/pr-visual-recap.mdx +++ b/packages/core/docs/content/pr-visual-recap.mdx @@ -174,14 +174,16 @@ runner. Choose which coding agent runs the skill with the `VISUAL_RECAP_AGENT` repository variable: -| `VISUAL_RECAP_AGENT` | Coding agent | Required API key | Required variables | -| -------------------- | --------------------------------------- | ---------------------- | --------------------------------------------- | -| `claude` _(default)_ | Claude Code CLI | `ANTHROPIC_API_KEY` | — | -| `codex` | OpenAI Codex CLI | `OPENAI_API_KEY` | — | -| `openai-compatible` | Agent-Native Code + compatible provider | `VISUAL_RECAP_API_KEY` | `VISUAL_RECAP_BASE_URL`, `VISUAL_RECAP_MODEL` | +| `VISUAL_RECAP_AGENT` | Coding agent | Required API key | Required variables | +| -------------------- | --------------------------------------- | ------------------------------------------------ | --------------------------------------------- | +| `claude` _(default)_ | Claude Code CLI | `ANTHROPIC_API_KEY` or `CLAUDE_CODE_OAUTH_TOKEN` | — | +| `codex` | OpenAI Codex CLI | `OPENAI_API_KEY` | — | +| `openai-compatible` | Agent-Native Code + compatible provider | `VISUAL_RECAP_API_KEY` | `VISUAL_RECAP_BASE_URL`, `VISUAL_RECAP_MODEL` | If the variable is unset, the action uses `claude`. +The `claude` backend authenticates with either credential: `ANTHROPIC_API_KEY` bills tokens to your Anthropic API account, while `CLAUDE_CODE_OAUTH_TOKEN` — minted locally with `claude setup-token` on a Claude subscription — bills the run to that subscription instead. Set exactly one; with both configured the billing path is whichever the Claude Code CLI prefers. + ## Model and reasoning Beyond the backend, these repository variables tune how the agent runs: @@ -220,10 +222,11 @@ Set these in your repository's **Settings → Secrets and variables → Actions* ### Secrets for the default backend -| Secret | Purpose | -| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| `PLAN_RECAP_TOKEN` | Revocable token minted by `npx @agent-native/core@latest connect`. Authorizes publishing the recap plan and the screenshot upload. | -| `ANTHROPIC_API_KEY` | The LLM key for the default Claude Code backend. | +| Secret | Purpose | +| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| `PLAN_RECAP_TOKEN` | Revocable token minted by `npx @agent-native/core@latest connect`. Authorizes publishing the recap plan and the screenshot upload. | +| `ANTHROPIC_API_KEY` | The LLM key for the default Claude Code backend. | +| `CLAUDE_CODE_OAUTH_TOKEN` | Alternative to `ANTHROPIC_API_KEY`: a Claude subscription token from `claude setup-token`. Set one or the other, not both. | **Teams: use an org service token.** A personal token is bound to the person who minted it — if they leave the org or revoke their tokens, every repo using @@ -527,6 +530,7 @@ jobs: secrets: PLAN_RECAP_TOKEN: ${{ secrets.PLAN_RECAP_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} VISUAL_RECAP_API_KEY: ${{ secrets.VISUAL_RECAP_API_KEY }} PLAN_RECAP_APP_URL: ${{ secrets.PLAN_RECAP_APP_URL }} diff --git a/packages/recap-cli/src/pr-visual-recap-workflow.spec.ts b/packages/recap-cli/src/pr-visual-recap-workflow.spec.ts index 41168bf91f..3ac4035802 100644 --- a/packages/recap-cli/src/pr-visual-recap-workflow.spec.ts +++ b/packages/recap-cli/src/pr-visual-recap-workflow.spec.ts @@ -31,6 +31,22 @@ describe("the recap installer workflow", () => { ); }); + // A caller that passes a secret the reusable contract does not declare fails + // the whole run at startup, so the template and the contract move together. + it("lets reusable callers pass a Claude subscription token", () => { + const reusable = readFileSync( + path.join(repoRoot, ".github/workflows/pr-visual-recap-reusable.yml"), + "utf8", + ); + + expect(reusable).toContain( + " CLAUDE_CODE_OAUTH_TOKEN:\n required: false", + ); + expect(buildReusableCallerWorkflow()).toContain( + "CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}", + ); + }); + it("wakes labeled events when reusable callers configure labels directly", () => { const workflow = buildReusableCallerWorkflow({ requiredLabels: "visual recap", diff --git a/packages/recap-cli/src/recap-gate.spec.ts b/packages/recap-cli/src/recap-gate.spec.ts index eb9d9e041c..be60991549 100644 --- a/packages/recap-cli/src/recap-gate.spec.ts +++ b/packages/recap-cli/src/recap-gate.spec.ts @@ -59,4 +59,24 @@ describe("evaluateRecapGate", () => { expect(decision.run).toBe(true); expect(decision.reasons).toEqual([]); }); + + it("runs the claude backend on a subscription OAuth token alone", () => { + const decision = evaluateRecapGate( + validGateInput({ hasAnthropic: false, hasClaudeOauth: true }), + ); + + expect(decision.run).toBe(true); + expect(decision.reasons).toEqual([]); + }); + + it("skips the claude backend when neither credential is configured", () => { + const decision = evaluateRecapGate( + validGateInput({ hasAnthropic: false, hasClaudeOauth: false }), + ); + + expect(decision.run).toBe(false); + expect(decision.reasons).toContain( + "neither ANTHROPIC_API_KEY nor CLAUDE_CODE_OAUTH_TOKEN configured (claude backend)", + ); + }); }); diff --git a/packages/recap-cli/src/recap.ts b/packages/recap-cli/src/recap.ts index 84e251af52..440528a06d 100644 --- a/packages/recap-cli/src/recap.ts +++ b/packages/recap-cli/src/recap.ts @@ -103,6 +103,7 @@ export const PR_VISUAL_RECAP_SETUP: string[] = [ " PLAN_RECAP_TOKEN — bearer token from `npx @agent-native/core@latest connect`", " ANTHROPIC_API_KEY — the LLM key for the default Claude Code backend", "Optional (only if you change defaults):", + " CLAUDE_CODE_OAUTH_TOKEN (secret) — instead of ANTHROPIC_API_KEY, bill the claude backend to a Claude subscription; mint it with `claude setup-token` and set exactly one of the two", " OPENAI_API_KEY (secret) + VISUAL_RECAP_AGENT=codex (variable) — use Codex instead of Claude", " VISUAL_RECAP_API_KEY (secret) + VISUAL_RECAP_AGENT=openai-compatible + VISUAL_RECAP_BASE_URL (variable) — use DeepSeek, Kimi, or any OpenAI-compatible API", " VISUAL_RECAP_MODEL (variable, required for openai-compatible) — provider model id; optional override for Claude/Codex", @@ -230,6 +231,7 @@ export function buildReusableCallerWorkflow( ` secrets:\n` + ` PLAN_RECAP_TOKEN: \${{ secrets.PLAN_RECAP_TOKEN }}\n` + ` ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}\n` + + ` CLAUDE_CODE_OAUTH_TOKEN: \${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}\n` + ` OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}\n` + ` VISUAL_RECAP_API_KEY: \${{ secrets.VISUAL_RECAP_API_KEY }}\n` + ` PLAN_RECAP_APP_URL: \${{ secrets.PLAN_RECAP_APP_URL }}\n` + @@ -321,14 +323,18 @@ export function normalizeRecapAgent(value: string | undefined): RecapAgent { ); } -export function recapRequiredSecrets(agent: RecapAgent): string[] { +export function recapRequiredSecrets( + agent: RecapAgent, +): RecapSecretRequirement[] { return [ - "PLAN_RECAP_TOKEN", + { names: ["PLAN_RECAP_TOKEN"] }, agent === "codex" - ? "OPENAI_API_KEY" + ? { names: ["OPENAI_API_KEY"] } : agent === "openai-compatible" - ? "VISUAL_RECAP_API_KEY" - : "ANTHROPIC_API_KEY", + ? { names: ["VISUAL_RECAP_API_KEY"] } + : // Either credential authenticates the Claude Code CLI: the API key + // bills tokens, the OAuth token bills a Claude subscription. + { names: ["ANTHROPIC_API_KEY", "CLAUDE_CODE_OAUTH_TOKEN"] }, ]; } @@ -578,13 +584,23 @@ export interface RecapSetupPlan { repo?: string; workflowPath: string; workflowExists: boolean; - requiredSecrets: string[]; + requiredSecrets: RecapSecretRequirement[]; requiredVariables: readonly RecapVariableRequirement[]; variableProblems: RecapVariableProblem[]; variableValues: Record; secretValues: Record; } +/** + * One credential the workflow needs. `names` holds interchangeable secrets in + * the order `recap setup` prefers them — the claude backend authenticates with + * either an API key or a subscription OAuth token, so demanding a specific one + * would report a working repo as misconfigured. + */ +export interface RecapSecretRequirement { + names: [string, ...string[]]; +} + export interface RecapVariableRequirement { name: | "VISUAL_RECAP_BASE_URL" @@ -777,12 +793,21 @@ export function buildRecapSetupPlan(input: { : []; const planToken = envValue(env, "PLAN_RECAP_TOKEN") ?? planTokenFromLocalStore(appUrl); - const llmSecretName = - agent === "codex" - ? "OPENAI_API_KEY" - : agent === "openai-compatible" - ? "VISUAL_RECAP_API_KEY" - : "ANTHROPIC_API_KEY"; + // Take the first name each requirement has a local value for, so setup pushes + // one credential per requirement instead of also writing an empty alternative. + const secretValues: Record = { + PLAN_RECAP_TOKEN: planToken, + PLAN_RECAP_APP_URL: appUrl === DEFAULT_RECAP_APP_URL ? undefined : appUrl, + }; + for (const requirement of requiredSecrets) { + for (const name of requirement.names) { + const value = envValue(env, name); + if (value) { + secretValues[name] = value; + break; + } + } + } const variableValues: Record = {}; if (agent !== "claude") variableValues.VISUAL_RECAP_AGENT = agent; for (const key of [ @@ -837,11 +862,7 @@ export function buildRecapSetupPlan(input: { requiredVariables, variableProblems, variableValues, - secretValues: { - PLAN_RECAP_TOKEN: planToken, - [llmSecretName]: envValue(env, llmSecretName), - PLAN_RECAP_APP_URL: appUrl === DEFAULT_RECAP_APP_URL ? undefined : appUrl, - }, + secretValues, }; } @@ -941,11 +962,16 @@ function runSetup(args: Record): void { } else { lines.push(""); lines.push("GitHub secrets/variables:"); - const secretNames = [ + const secretRequirements: RecapSecretRequirement[] = [ ...plan.requiredSecrets, - ...(plan.secretValues.PLAN_RECAP_APP_URL ? ["PLAN_RECAP_APP_URL"] : []), + ...(plan.secretValues.PLAN_RECAP_APP_URL + ? [{ names: ["PLAN_RECAP_APP_URL"] as [string] }] + : []), ]; - for (const name of secretNames) { + for (const requirement of secretRequirements) { + const name = + requirement.names.find((candidate) => plan.secretValues[candidate]) ?? + requirement.names[0]; const status = setGithubSecret( name, plan.secretValues[name], @@ -966,6 +992,11 @@ function runSetup(args: Record): void { lines.push( ` Or set manually: ${commandForMissingSecret(name, repo)}`, ); + for (const alternative of requirement.names.slice(1)) { + lines.push( + ` Alternative: ${commandForMissingSecret(alternative, repo)}`, + ); + } } else { lines.push(` ${name}: could not set with gh.`); lines.push(` Set manually: ${commandForMissingSecret(name, repo)}`); @@ -1085,13 +1116,26 @@ function runDoctor(args: Record): void { lines.push("[missing] Could not read GitHub Actions secrets with gh."); lines.push(" Run gh auth status, or pass --repo owner/name."); } else { - for (const name of plan.requiredSecrets) { - if (secretNames.has(name)) { - lines.push(`[ok] GitHub secret configured: ${name}.`); - } else { + for (const requirement of plan.requiredSecrets) { + const configured = requirement.names.filter((name) => + secretNames.has(name), + ); + if (configured.length === 0) { ok = false; - lines.push(`[missing] GitHub secret missing: ${name}.`); - lines.push(` Set it with: ${commandForMissingSecret(name, repo)}`); + lines.push( + `[missing] GitHub secret missing: ${requirement.names.join(" or ")}.`, + ); + lines.push( + ` Set it with: ${commandForMissingSecret(requirement.names[0], repo)}`, + ); + continue; + } + lines.push(`[ok] GitHub secret configured: ${configured.join(", ")}.`); + if (configured.length > 1) { + lines.push( + `[warn] Interchangeable credentials both configured: ${configured.join(", ")}.`, + ); + lines.push(" Remove all but one so the billing path is unambiguous."); } } } @@ -1383,6 +1427,10 @@ export function sanitizeAgentFailureSummary( ) .replace(/PLAN_RECAP_TOKEN=([^\s]+)/g, "PLAN_RECAP_TOKEN=[redacted]") .replace(/ANTHROPIC_API_KEY=([^\s]+)/g, "ANTHROPIC_API_KEY=[redacted]") + .replace( + /CLAUDE_CODE_OAUTH_TOKEN=([^\s]+)/g, + "CLAUDE_CODE_OAUTH_TOKEN=[redacted]", + ) .replace(/OPENAI_API_KEY=([^\s]+)/g, "OPENAI_API_KEY=[redacted]"); const sanitizedLines = value @@ -4015,6 +4063,8 @@ export interface RecapGateInput { hasPlan: boolean; /** ANTHROPIC_API_KEY present. */ hasAnthropic: boolean; + /** CLAUDE_CODE_OAUTH_TOKEN present — the subscription-billed claude credential. */ + hasClaudeOauth?: boolean; /** OPENAI_API_KEY present. */ hasOpenai: boolean; /** VISUAL_RECAP_API_KEY present for OpenAI-compatible backends. */ @@ -4165,8 +4215,10 @@ export function evaluateRecapGate(input: RecapGateInput): { if (!input.hasOpenai) reasons.push("OPENAI_API_KEY not configured (codex backend)"); } else if (agent === "claude") { - if (!input.hasAnthropic) - reasons.push("ANTHROPIC_API_KEY not configured (claude backend)"); + if (!input.hasAnthropic && !input.hasClaudeOauth) + reasons.push( + "neither ANTHROPIC_API_KEY nor CLAUDE_CODE_OAUTH_TOKEN configured (claude backend)", + ); } else { if (!input.hasOpenaiCompatible) reasons.push( @@ -4324,6 +4376,7 @@ async function runGate(): Promise { repositoryPrivate, hasPlan: process.env.HAS_PLAN === "true", hasAnthropic: process.env.HAS_ANTHROPIC === "true", + hasClaudeOauth: process.env.HAS_CLAUDE_OAUTH === "true", hasOpenai: process.env.HAS_OPENAI === "true", hasOpenaiCompatible: process.env.HAS_COMPATIBLE === "true", agentRaw: process.env.AGENT, From a4dd86bbf95e601c1c3f769afa0d3747184338f0 Mon Sep 17 00:00:00 2001 From: Kellen Busby Date: Sat, 8 Aug 2026 14:35:59 -0700 Subject: [PATCH 3/3] recap: update core recap assertions for the grouped secret requirement shape The core CLI re-exports @agent-native/recap-cli, and its spec still asserted the flat string[] result and the old claude-backend gate message. Update both and cover the OAuth-only gate path in the suite that actually gates CI. --- packages/core/src/cli/recap.spec.ts | 37 +++++++++++++++++++---------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/packages/core/src/cli/recap.spec.ts b/packages/core/src/cli/recap.spec.ts index c017b5f128..e87c92b8e0 100644 --- a/packages/core/src/cli/recap.spec.ts +++ b/packages/core/src/cli/recap.spec.ts @@ -1001,16 +1001,16 @@ describe("recap setup planning", () => { it("selects required secrets for each backend", () => { expect(recapRequiredSecrets("claude")).toEqual([ - "PLAN_RECAP_TOKEN", - "ANTHROPIC_API_KEY", + { names: ["PLAN_RECAP_TOKEN"] }, + { names: ["ANTHROPIC_API_KEY", "CLAUDE_CODE_OAUTH_TOKEN"] }, ]); expect(recapRequiredSecrets("codex")).toEqual([ - "PLAN_RECAP_TOKEN", - "OPENAI_API_KEY", + { names: ["PLAN_RECAP_TOKEN"] }, + { names: ["OPENAI_API_KEY"] }, ]); expect(recapRequiredSecrets("openai-compatible")).toEqual([ - "PLAN_RECAP_TOKEN", - "VISUAL_RECAP_API_KEY", + { names: ["PLAN_RECAP_TOKEN"] }, + { names: ["VISUAL_RECAP_API_KEY"] }, ]); }); @@ -1109,7 +1109,10 @@ describe("recap setup planning", () => { repo: "BuilderIO/example", workflowPath: path.join(".github", "workflows", "pr-visual-recap.yml"), workflowExists: true, - requiredSecrets: ["PLAN_RECAP_TOKEN", "OPENAI_API_KEY"], + requiredSecrets: [ + { names: ["PLAN_RECAP_TOKEN"] }, + { names: ["OPENAI_API_KEY"] }, + ], variableValues: { VISUAL_RECAP_AGENT: "codex", VISUAL_RECAP_MODEL: "gpt-5.6-sol", @@ -1144,8 +1147,8 @@ describe("recap setup planning", () => { expect(plan.agent).toBe("openai-compatible"); expect(plan.requiredSecrets).toEqual([ - "PLAN_RECAP_TOKEN", - "VISUAL_RECAP_API_KEY", + { names: ["PLAN_RECAP_TOKEN"] }, + { names: ["VISUAL_RECAP_API_KEY"] }, ]); expect(plan.requiredVariables).toEqual([ { @@ -2441,14 +2444,24 @@ describe("recap gate decision", () => { expect(result.reasons).toContain("PLAN_RECAP_TOKEN not configured"); }); - it("skips when the claude backend's ANTHROPIC_API_KEY is missing", () => { - const result = evaluateRecapGate(ok({ hasAnthropic: false })); + it("skips when the claude backend has neither credential", () => { + const result = evaluateRecapGate( + ok({ hasAnthropic: false, hasClaudeOauth: false }), + ); expect(result.run).toBe(false); expect(result.reasons).toContain( - "ANTHROPIC_API_KEY not configured (claude backend)", + "neither ANTHROPIC_API_KEY nor CLAUDE_CODE_OAUTH_TOKEN configured (claude backend)", ); }); + it("runs the claude backend on a subscription OAuth token alone", () => { + const result = evaluateRecapGate( + ok({ hasAnthropic: false, hasClaudeOauth: true }), + ); + expect(result.run).toBe(true); + expect(result.reasons).toEqual([]); + }); + it("skips when the codex backend's OPENAI_API_KEY is missing", () => { const result = evaluateRecapGate( ok({ agentRaw: "codex", hasOpenai: false }),