diff --git a/.env.example b/.env.example index 7101d14..ea41e23 100644 --- a/.env.example +++ b/.env.example @@ -29,7 +29,7 @@ ANTHROPIC_API_KEY=your-api-key-here # Model Tier Overrides (Anthropic API / OAuth / Custom Base URL / Bedrock) # ============================================================================= # Override which model is used for each tier. Defaults are used if not set. -# Optional for direct Anthropic and custom base URL modes. Required for Bedrock/Vertex. +# Optional for direct Anthropic and custom base URL modes. Required for Bedrock. # ANTHROPIC_SMALL_MODEL=... # Small tier (default: claude-haiku-4-5-20251001) # ANTHROPIC_MEDIUM_MODEL=... # Medium tier (default: claude-sonnet-4-6) # ANTHROPIC_LARGE_MODEL=... # Large tier (default: claude-opus-4-8) @@ -47,20 +47,3 @@ ANTHROPIC_API_KEY=your-api-key-here # CLAUDE_CODE_USE_BEDROCK=1 # AWS_REGION=us-east-1 # AWS_BEARER_TOKEN_BEDROCK=your-bearer-token - -# ============================================================================= -# OPTION 4: Google Vertex AI -# ============================================================================= -# https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models -# Requires a GCP service account with roles/aiplatform.user. -# Download the SA key JSON from GCP Console (IAM > Service Accounts > Keys). -# Requires the model tier overrides above to be set with Vertex AI model IDs. -# Example Vertex AI model IDs: -# ANTHROPIC_SMALL_MODEL=claude-haiku-4-5@20251001 -# ANTHROPIC_MEDIUM_MODEL=claude-sonnet-4-6 -# ANTHROPIC_LARGE_MODEL=claude-opus-4-8 - -# CLAUDE_CODE_USE_VERTEX=1 -# CLOUD_ML_REGION=us-east5 -# ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id -# GOOGLE_APPLICATION_CREDENTIALS=./credentials/google-sa-key.json diff --git a/CLAUDE.md b/CLAUDE.md index 160d378..9691f4c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -147,7 +147,7 @@ Durable workflow orchestration with crash recovery, queryable progress, intellig ### Supporting Systems - **Configuration** — YAML configs in `apps/worker/configs/` with JSON Schema validation (`config-schema.json`). Supports auth settings (MFA/TOTP), URL/code rule scoping (`rules.avoid`/`rules.focus`), run-scope steering (`vuln_classes`, `exploit`), free-form `rules_of_engagement`, and post-hoc `report` filters (`min_severity`, `min_confidence`, `guidance`). `code_path` avoid rules are enforced via the `@gotgenes/pi-permission-system` extension: `apps/worker/src/temporal/activities.ts:syncCodePathDenyRules` writes a global `path` deny config once per workflow (`apps/worker/src/ai/settings-writer.ts:writeCodePathPermissionConfig`), and the executor loads the extension when that config is present (`apps/worker/src/ai/claude-executor.ts`), so denies fire across every tool and child `task` session. `vuln_classes`/`exploit` scope is locked into `session.json` on first run; resumes with a different scope fail fast (`persistOrValidateRunScope`). Credential resolution — local mode: env vars → `./.env`; npx mode: env vars → `~/.shannon/config.toml` (via `shn setup`) - **Prompts** — Per-phase templates in `apps/worker/prompts/` with variable substitution (`{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`). Shared partials in `apps/worker/prompts/shared/` via `apps/worker/src/services/prompt-manager.ts`, including `_code-path-rules.txt` (focus/avoid `[FILE]`/`[GLOB]` routing) and `_rules-of-engagement.txt` (free-text engagement rules). When `exploit: false`, `apps/worker/src/services/findings-renderer.ts` deterministically converts each `*_exploitation_queue.json` into a `*_findings.md` for report assembly — no LLM in the loop -- **Agent Harness (pi)** — Uses the **pi harness** (`@earendil-works/pi-coding-agent`, requires Node ≥ 22.19) via `apps/worker/src/ai/claude-executor.ts` (`runClaudePrompt` → `createAgentSession`, retry disabled so Temporal owns retry). Models resolve through pi-ai in `apps/worker/src/ai/models.ts` (Anthropic / Bedrock / Vertex / custom base URL via `ModelRegistry`+`AuthStorage`). pi ships no JSON-schema output or `Task`/`TodoWrite` built-ins, so structured queues are captured via a `submit_exploitation_queue` custom tool (`apps/worker/src/ai/queue-schemas.ts`), and `task` (read-only child sessions) + `todo_write` are provided as custom tools (`apps/worker/src/ai/tools.ts`); the per-phase MCP collectors are pi custom tools (TypeBox `defineTool` in `apps/worker/src/mcp-server/`). Thinking level defaults to `medium`; disable per-scan via `CLAUDE_ADAPTIVE_THINKING=false` (→ `off`) or set `CLAUDE_THINKING_LEVEL` (env) / `core.adaptive_thinking = false` (npx TOML). Browser automation via `playwright-cli` with session isolation (`-s=`). TOTP generation via `generate-totp` CLI tool. Login flow template at `apps/worker/prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth. On authenticated whitebox scans, the `validate-authentication` preflight performs the single real login and saves the browser session to `auth-state.json` in the per-session audit directory (path from `authStateFile()` in `apps/worker/src/audit/utils.ts`, derived from `generateAuditPath()`). The validation activity (`apps/worker/src/services/validate-authentication.ts`) removes any stale file from a prior run before the agent runs and verifies the file parses and contains cookies or storage before the preflight is marked complete; `logWorkflowComplete` deletes it when the workflow ends so authenticated cookies don't sit on disk between scans. Agent prompts opt in to session reuse by `@include(shared/_shared-session.txt)` before their `` block — the partial restores the session and falls through to the full login flow if verification fails. `vuln-auth`/`exploit-auth` omit the include and own their own login +- **Agent Harness (pi)** — Uses the **pi harness** (`@earendil-works/pi-coding-agent`, requires Node ≥ 22.19) via `apps/worker/src/ai/claude-executor.ts` (`runClaudePrompt` → `createAgentSession`, retry disabled so Temporal owns retry). Models resolve through pi-ai in `apps/worker/src/ai/models.ts` (Anthropic / Bedrock / custom base URL via `ModelRegistry`+`AuthStorage`). pi ships no JSON-schema output or `Task`/`TodoWrite` built-ins, so structured queues are captured via a `submit_exploitation_queue` custom tool (`apps/worker/src/ai/queue-schemas.ts`), and `task` (read-only child sessions) + `todo_write` are provided as custom tools (`apps/worker/src/ai/tools.ts`); the per-phase MCP collectors are pi custom tools (TypeBox `defineTool` in `apps/worker/src/mcp-server/`). Thinking level defaults to `medium`; disable per-scan via `CLAUDE_ADAPTIVE_THINKING=false` (→ `off`) or set `CLAUDE_THINKING_LEVEL` (env) / `core.adaptive_thinking = false` (npx TOML). Browser automation via `playwright-cli` with session isolation (`-s=`). TOTP generation via `generate-totp` CLI tool. Login flow template at `apps/worker/prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth. On authenticated whitebox scans, the `validate-authentication` preflight performs the single real login and saves the browser session to `auth-state.json` in the per-session audit directory (path from `authStateFile()` in `apps/worker/src/audit/utils.ts`, derived from `generateAuditPath()`). The validation activity (`apps/worker/src/services/validate-authentication.ts`) removes any stale file from a prior run before the agent runs and verifies the file parses and contains cookies or storage before the preflight is marked complete; `logWorkflowComplete` deletes it when the workflow ends so authenticated cookies don't sit on disk between scans. Agent prompts opt in to session reuse by `@include(shared/_shared-session.txt)` before their `` block — the partial restores the session and falls through to the full login flow if verification fails. `vuln-auth`/`exploit-auth` omit the include and own their own login - **Audit System** — Crash-safe append-only logging in `workspaces/{hostname}_{sessionId}/`. Tracks session metrics, per-agent logs, prompts, and deliverables. WorkflowLogger (`apps/worker/src/audit/workflow-logger.ts`) provides unified human-readable per-workflow logs, backed by LogStream (`apps/worker/src/audit/log-stream.ts`) shared stream primitive - **Deliverables** — Saved to `deliverables/` in the target repo via the `save-deliverable` CLI script (`apps/worker/src/scripts/save-deliverable.ts`) - **Workspaces & Resume** — Named workspaces via `-w ` or auto-named from URL+timestamp. Resume detects completed agents via `session.json`. `loadResumeState()` in `apps/worker/src/temporal/activities.ts` validates deliverable existence, restores git checkpoints, and cleans up incomplete deliverables. Workspace listing via `apps/worker/src/temporal/workspaces.ts` diff --git a/README.md b/README.md index 95de07e..0ebc242 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Sample Shannon Lite penetration test reports from intentionally vulnerable appli - **Docker** - required for the worker container. - **Node.js 18+** - required for the recommended `npx` workflow. -- **AI provider credentials** - Anthropic is recommended; AWS Bedrock, Google Vertex AI, and compatible proxy setups are documented separately. +- **AI provider credentials** - Anthropic is recommended; AWS Bedrock and compatible proxy setups are documented separately. ### Run Shannon Lite @@ -194,7 +194,7 @@ Use these guides for operational detail: | --- | --- | | [Source build and CLI commands](docs/development.md) | Cloning, building, common commands, output paths, and local development. | | [Configuration](docs/configuration.md) | Authenticated testing, login flows, rules of engagement, report filters, and rate-limit settings. | -| [AI providers](docs/ai-providers.md) | Anthropic, AWS Bedrock, Google Vertex AI, and custom Anthropic-compatible endpoints. | +| [AI providers](docs/ai-providers.md) | Anthropic, AWS Bedrock, and custom Anthropic-compatible endpoints. | | [Platforms and networking](docs/platforms.md) | Windows/WSL2, Linux, macOS, Docker networking, local apps, and custom hostnames. | | [Workspaces and resuming](docs/workspaces.md) | Naming workspaces, resuming interrupted scans, and workspace storage. | | [Safety and limitations](docs/safety.md) | Authorized-use requirements, non-production guidance, mutative effects, cost, and model caveats. | diff --git a/apps/cli/src/commands/setup.ts b/apps/cli/src/commands/setup.ts index f0758ee..2177c77 100644 --- a/apps/cli/src/commands/setup.ts +++ b/apps/cli/src/commands/setup.ts @@ -5,7 +5,6 @@ * then persists everything to ~/.shannon/config.toml with 0o600 permissions. */ -import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import * as p from '@clack/prompts'; @@ -13,7 +12,7 @@ import { type ShannonConfig, saveConfig } from '../config/writer.js'; const SHANNON_HOME = path.join(os.homedir(), '.shannon'); -type Provider = 'anthropic' | 'custom_base_url' | 'bedrock' | 'vertex'; +type Provider = 'anthropic' | 'custom_base_url' | 'bedrock'; export async function setup(): Promise { p.intro('Shannon Setup'); @@ -25,7 +24,6 @@ export async function setup(): Promise { { value: 'anthropic' as const, label: 'Claude Direct', hint: 'recommended' }, { value: 'custom_base_url' as const, label: 'Custom Base URL', hint: 'proxies, gateways' }, { value: 'bedrock' as const, label: 'Claude via AWS Bedrock' }, - { value: 'vertex' as const, label: 'Claude via Google Vertex AI' }, ], }); if (p.isCancel(provider)) return cancelAndExit(); @@ -51,8 +49,6 @@ async function setupProvider(provider: Provider): Promise { return setupCustomBaseUrl(); case 'bedrock': return setupBedrock(); - case 'vertex': - return setupVertex(); } } @@ -213,75 +209,6 @@ async function setupBedrock(): Promise { }; } -async function setupVertex(): Promise { - // 1. Collect region and project ID - const region = await p.text({ - message: 'Google Cloud region', - placeholder: 'us-east5', - validate: required('Region is required'), - }); - if (p.isCancel(region)) return cancelAndExit(); - - const projectId = await p.text({ - message: 'GCP Project ID', - validate: required('Project ID is required'), - }); - if (p.isCancel(projectId)) return cancelAndExit(); - - // 2. File picker for service account key - p.log.info('Select the path to your GCP Service Account JSON key file.'); - const keySourcePath = await p.path({ - message: 'Service Account JSON key file', - validate: (value) => { - if (!value) return 'Path is required'; - if (!fs.existsSync(value)) return 'File not found'; - if (!value.endsWith('.json')) return 'Must be a .json file'; - return undefined; - }, - }); - if (p.isCancel(keySourcePath)) return cancelAndExit(); - - // 3. Copy key to ~/.shannon/ and lock permissions - const destPath = path.join(SHANNON_HOME, 'google-sa-key.json'); - fs.mkdirSync(SHANNON_HOME, { recursive: true }); - fs.copyFileSync(keySourcePath, destPath); - fs.chmodSync(destPath, 0o600); - p.log.success(`Key copied to ${destPath} (permissions: 0600)`); - - // 4. Model tiers - const models = await p.group({ - small: () => - p.text({ - message: 'Small model ID', - placeholder: 'claude-haiku-4-5@20251001', - validate: required('Small model ID is required'), - }), - medium: () => - p.text({ - message: 'Medium model ID', - placeholder: 'claude-sonnet-4-6', - validate: required('Medium model ID is required'), - }), - large: () => - p.text({ - message: 'Large model ID', - placeholder: 'claude-opus-4-8', - validate: required('Large model ID is required'), - }), - }); - if (p.isCancel(models)) return cancelAndExit(); - - return { - vertex: { - use: true, - region, - project_id: projectId, - key_path: destPath, - }, - models: { small: models.small, medium: models.medium, large: models.large }, - }; -} - // === Helpers === async function maybePromptAdaptiveThinking(config: ShannonConfig): Promise { diff --git a/apps/cli/src/commands/start.ts b/apps/cli/src/commands/start.ts index 6a78a5e..88325ea 100644 --- a/apps/cli/src/commands/start.ts +++ b/apps/cli/src/commands/start.ts @@ -10,7 +10,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { ensureImage, ensureInfra, randomSuffix, spawnWorker } from '../docker.js'; import { buildEnvFlags, loadEnv, validateCredentials } from '../env.js'; -import { getCredentialsPath, getWorkspacesDir, initHome } from '../home.js'; +import { getWorkspacesDir, initHome } from '../home.js'; import { isLocal } from '../mode.js'; import { resolveConfig, resolveRepo } from '../paths.js'; import { displaySplash } from '../splash.js'; @@ -78,13 +78,6 @@ export async function start(args: StartArgs): Promise { } fs.mkdirSync(path.join(repo.hostPath, '.playwright'), { recursive: true }); - const credentialsPath = getCredentialsPath(); - const hasCredentials = fs.existsSync(credentialsPath); - - if (hasCredentials) { - process.env.GOOGLE_APPLICATION_CREDENTIALS = '/app/credentials/google-sa-key.json'; - } - // 10. Resolve output directory const outputDir = args.output ? path.resolve(args.output) : undefined; if (outputDir) { @@ -107,7 +100,6 @@ export async function start(args: StartArgs): Promise { containerName, envFlags: buildEnvFlags(), ...(config && { config }), - ...(hasCredentials && { credentials: credentialsPath }), ...(promptsDir && { promptsDir }), ...(outputDir && { outputDir }), workspace, diff --git a/apps/cli/src/config/resolver.ts b/apps/cli/src/config/resolver.ts index 9b746c2..d196033 100644 --- a/apps/cli/src/config/resolver.ts +++ b/apps/cli/src/config/resolver.ts @@ -36,12 +36,6 @@ const CONFIG_MAP: readonly ConfigMapping[] = [ { env: 'AWS_REGION', toml: 'bedrock.region', type: 'string' }, { env: 'AWS_BEARER_TOKEN_BEDROCK', toml: 'bedrock.token', type: 'string' }, - // Vertex - { env: 'CLAUDE_CODE_USE_VERTEX', toml: 'vertex.use', type: 'boolean' }, - { env: 'CLOUD_ML_REGION', toml: 'vertex.region', type: 'string' }, - { env: 'ANTHROPIC_VERTEX_PROJECT_ID', toml: 'vertex.project_id', type: 'string' }, - { env: 'GOOGLE_APPLICATION_CREDENTIALS', toml: 'vertex.key_path', type: 'string' }, - // Custom Base URL { env: 'ANTHROPIC_BASE_URL', toml: 'custom_base_url.base_url', type: 'string' }, { env: 'ANTHROPIC_AUTH_TOKEN', toml: 'custom_base_url.auth_token', type: 'string' }, @@ -154,20 +148,10 @@ function validateProviderFields(config: TOMLConfig, provider: string, errors: st validateModelTiers(config, 'bedrock', errors); break; } - - case 'vertex': { - const required = ['use', 'region', 'project_id', 'key_path']; - const missing = required.filter((k) => !keys.includes(k)); - if (missing.length > 0) { - errors.push(`[vertex] missing required keys: ${missing.join(', ')}`); - } - validateModelTiers(config, 'vertex', errors); - break; - } } } -/** Bedrock and Vertex require a [models] section with all three tiers. */ +/** Bedrock requires a [models] section with all three tiers. */ function validateModelTiers(config: TOMLConfig, provider: string, errors: string[]): void { const models = config.models as Record | undefined; if (!models || typeof models !== 'object') { @@ -227,7 +211,7 @@ function validateConfig(config: TOMLConfig): string[] { } // 4. Only one provider section allowed (ignore empty sections) - const PROVIDER_SECTIONS = ['anthropic', 'custom_base_url', 'bedrock', 'vertex'] as const; + const PROVIDER_SECTIONS = ['anthropic', 'custom_base_url', 'bedrock'] as const; const present = PROVIDER_SECTIONS.filter((s) => { const section = config[s]; return section && typeof section === 'object' && Object.keys(section).length > 0; diff --git a/apps/cli/src/config/writer.ts b/apps/cli/src/config/writer.ts index bf26d47..0b4b55f 100644 --- a/apps/cli/src/config/writer.ts +++ b/apps/cli/src/config/writer.ts @@ -12,7 +12,6 @@ export interface ShannonConfig { anthropic?: { api_key?: string; oauth_token?: string }; custom_base_url?: { base_url?: string; auth_token?: string }; bedrock?: { use?: boolean; region?: string; token?: string }; - vertex?: { use?: boolean; region?: string; project_id?: string; key_path?: string }; models?: { small?: string; medium?: string; large?: string }; } diff --git a/apps/cli/src/docker.ts b/apps/cli/src/docker.ts index caef82c..c41d03a 100644 --- a/apps/cli/src/docker.ts +++ b/apps/cli/src/docker.ts @@ -236,7 +236,6 @@ export interface WorkerOptions { containerName: string; envFlags: string[]; config?: { hostPath: string; containerPath: string }; - credentials?: string; promptsDir?: string; outputDir?: string; workspace: string; @@ -291,11 +290,6 @@ export function spawnWorker(opts: WorkerOptions): ChildProcess { args.push('-v', `${opts.outputDir}:/app/output`); } - // Mount credentials file to fixed container path - if (opts.credentials) { - args.push('-v', `${opts.credentials}:/app/credentials/google-sa-key.json:ro`); - } - // Environment args.push(...opts.envFlags); diff --git a/apps/cli/src/env.ts b/apps/cli/src/env.ts index 2190838..6b0f740 100644 --- a/apps/cli/src/env.ts +++ b/apps/cli/src/env.ts @@ -18,10 +18,6 @@ const FORWARD_VARS = [ 'CLAUDE_CODE_USE_BEDROCK', 'AWS_REGION', 'AWS_BEARER_TOKEN_BEDROCK', - 'CLAUDE_CODE_USE_VERTEX', - 'CLOUD_ML_REGION', - 'ANTHROPIC_VERTEX_PROJECT_ID', - 'GOOGLE_APPLICATION_CREDENTIALS', 'ANTHROPIC_SMALL_MODEL', 'ANTHROPIC_MEDIUM_MODEL', 'ANTHROPIC_LARGE_MODEL', @@ -62,7 +58,7 @@ export function buildEnvFlags(): string[] { interface CredentialValidation { valid: boolean; error?: string; - mode: 'api-key' | 'oauth' | 'custom-base-url' | 'bedrock' | 'vertex'; + mode: 'api-key' | 'oauth' | 'custom-base-url' | 'bedrock'; } /** Check if a custom Anthropic-compatible base URL is configured. */ @@ -77,7 +73,6 @@ function detectProviders(): string[] { if (process.env.CLAUDE_CODE_OAUTH_TOKEN) providers.push('Anthropic OAuth'); if (isCustomBaseUrlConfigured()) providers.push('Custom Base URL'); if (process.env.CLAUDE_CODE_USE_BEDROCK === '1') providers.push('AWS Bedrock'); - if (process.env.CLAUDE_CODE_USE_VERTEX === '1') providers.push('Google Vertex'); return providers; } @@ -120,29 +115,6 @@ export function validateCredentials(): CredentialValidation { } return { valid: true, mode: 'bedrock' }; } - if (process.env.CLAUDE_CODE_USE_VERTEX === '1') { - const missing: string[] = []; - if (!process.env.CLOUD_ML_REGION) missing.push('CLOUD_ML_REGION'); - if (!process.env.ANTHROPIC_VERTEX_PROJECT_ID) missing.push('ANTHROPIC_VERTEX_PROJECT_ID'); - if (!process.env.ANTHROPIC_SMALL_MODEL) missing.push('ANTHROPIC_SMALL_MODEL'); - if (!process.env.ANTHROPIC_MEDIUM_MODEL) missing.push('ANTHROPIC_MEDIUM_MODEL'); - if (!process.env.ANTHROPIC_LARGE_MODEL) missing.push('ANTHROPIC_LARGE_MODEL'); - if (missing.length > 0) { - return { - valid: false, - mode: 'vertex', - error: `Vertex AI mode requires: ${missing.join(', ')}`, - }; - } - if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) { - return { - valid: false, - mode: 'vertex', - error: 'Vertex AI mode requires GOOGLE_APPLICATION_CREDENTIALS', - }; - } - return { valid: true, mode: 'vertex' }; - } const hint = getMode() === 'local' diff --git a/apps/cli/src/home.ts b/apps/cli/src/home.ts index b8c979e..e6d61b6 100644 --- a/apps/cli/src/home.ts +++ b/apps/cli/src/home.ts @@ -1,7 +1,7 @@ /** * Shannon state directory management. * - * Local mode (cloned repo): uses ./workspaces/, ./credentials/ + * Local mode (cloned repo): uses ./workspaces/ * NPX mode: uses ~/.shannon/workspaces/, ~/.shannon/ */ @@ -20,32 +20,14 @@ export function getWorkspacesDir(): string { return getMode() === 'local' ? path.resolve('workspaces') : path.join(SHANNON_HOME, 'workspaces'); } -/** - * Resolve the Vertex credentials file path. - * - * Checks GOOGLE_APPLICATION_CREDENTIALS env var first (may be set by TOML resolver), - * then falls back to mode-appropriate default location. - */ -export function getCredentialsPath(): string { - const envPath = process.env.GOOGLE_APPLICATION_CREDENTIALS; - if (envPath && fs.existsSync(envPath)) return path.resolve(envPath); - - if (getMode() === 'local') { - return path.resolve('credentials', 'google-sa-key.json'); - } - - return path.join(SHANNON_HOME, 'google-sa-key.json'); -} - /** * Initialize state directories. - * Local mode: creates ./workspaces/ and ./credentials/ + * Local mode: creates ./workspaces/ * NPX mode: creates ~/.shannon/workspaces/ */ export function initHome(): void { if (getMode() === 'local') { fs.mkdirSync(path.resolve('workspaces'), { recursive: true }); - fs.mkdirSync(path.resolve('credentials'), { recursive: true }); } else { fs.mkdirSync(path.join(SHANNON_HOME, 'workspaces'), { recursive: true }); } diff --git a/apps/worker/src/ai/models.ts b/apps/worker/src/ai/models.ts index 65088d6..8ce0220 100644 --- a/apps/worker/src/ai/models.ts +++ b/apps/worker/src/ai/models.ts @@ -14,12 +14,12 @@ * * Users override per tier via ANTHROPIC_SMALL_MODEL / ANTHROPIC_MEDIUM_MODEL / * ANTHROPIC_LARGE_MODEL, which works across all providers (Anthropic, Bedrock, - * Vertex, custom base URL). + * custom base URL). * * Resolution returns a pi `Model` object via `ModelRegistry.find`, plus the * `thinkingLevel` and an `AuthStorage` primed with runtime credentials. Bedrock - * and Vertex authenticate from the process environment (the AWS_ and GOOGLE_ vars - * the CLI forwards), so they need no runtime API key. + * authenticates from the process environment (the AWS_ vars the CLI forwards), so + * it needs no runtime API key. */ import type { ThinkingLevel } from '@earendil-works/pi-agent-core'; @@ -40,8 +40,6 @@ function piProviderId(providerConfig?: ProviderConfig): string { switch (providerConfig?.providerType) { case 'bedrock': return 'amazon-bedrock'; - case 'vertex': - return 'google-vertex'; default: // 'anthropic_api', 'custom_base_url', or unset all resolve to the anthropic // provider; custom_base_url overrides baseUrl/auth below. @@ -94,8 +92,8 @@ export interface ModelSelection { * → ANTHROPIC_API_KEY env. OAuth (CLAUDE_CODE_OAUTH_TOKEN) is read from env by pi. * - Custom base URL (custom_base_url): the auth token is set as the anthropic * runtime key and the model's baseUrl is overridden. - * - Bedrock / Vertex: authenticate from the process environment (the AWS_ and - * GOOGLE_ vars), no runtime key needed. + * - Bedrock: authenticates from the process environment (the AWS_ vars), no + * runtime key needed. */ export function resolveModelSelection( registryFactory: (authStorage: AuthStorage) => ModelRegistry, diff --git a/apps/worker/src/services/preflight.ts b/apps/worker/src/services/preflight.ts index 174ad4e..d129063 100644 --- a/apps/worker/src/services/preflight.ts +++ b/apps/worker/src/services/preflight.ts @@ -15,7 +15,7 @@ * 1. Repository path exists and contains .git * 2. Config file parses and validates (if provided) * 3. code_path rules match real entries in the repo (filesystem only) - * 4. Credentials validate via Claude Agent SDK query (API key, OAuth, Bedrock, or Vertex AI) + * 4. Credentials validate via a minimal pi session (API key, OAuth, or Bedrock) * 5. Target URL resolves, is not link-local (cloud metadata), and is reachable (DNS + HTTP) */ @@ -405,62 +405,11 @@ async function validateCredentials( return ok(undefined); } - // 3. Vertex AI mode — validate required GCP credentials are present - if (process.env.CLAUDE_CODE_USE_VERTEX === '1') { - const required = [ - 'CLOUD_ML_REGION', - 'ANTHROPIC_VERTEX_PROJECT_ID', - 'ANTHROPIC_SMALL_MODEL', - 'ANTHROPIC_MEDIUM_MODEL', - 'ANTHROPIC_LARGE_MODEL', - ]; - const missing = required.filter((v) => !process.env[v]); - if (missing.length > 0) { - return err( - new PentestError( - `Vertex AI mode requires the following env vars in .env: ${missing.join(', ')}`, - 'config', - false, - { missing }, - ErrorCode.AUTH_FAILED, - ), - ); - } - // Validate service account credentials file is accessible - const credPath = process.env.GOOGLE_APPLICATION_CREDENTIALS; - if (!credPath) { - return err( - new PentestError( - 'Vertex AI mode requires GOOGLE_APPLICATION_CREDENTIALS pointing to a service account key JSON file', - 'config', - false, - {}, - ErrorCode.AUTH_FAILED, - ), - ); - } - try { - await fs.access(credPath); - } catch { - return err( - new PentestError( - `Service account key file not found at: ${credPath}`, - 'config', - false, - { credPath }, - ErrorCode.AUTH_FAILED, - ), - ); - } - logger.info('Vertex AI credentials OK'); - return ok(undefined); - } - - // 4. Check that at least one credential is present + // 3. Check that at least one credential is present if (!process.env.ANTHROPIC_API_KEY && !process.env.CLAUDE_CODE_OAUTH_TOKEN && !process.env.ANTHROPIC_AUTH_TOKEN) { return err( new PentestError( - 'No API credentials found. Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env (or use CLAUDE_CODE_USE_BEDROCK=1 for AWS Bedrock, or CLAUDE_CODE_USE_VERTEX=1 for Google Vertex AI)', + 'No API credentials found. Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env (or use CLAUDE_CODE_USE_BEDROCK=1 for AWS Bedrock)', 'config', false, {}, @@ -469,7 +418,7 @@ async function validateCredentials( ); } - // 5. Validate via a minimal pi session + // 4. Validate via a minimal pi session const authType = process.env.CLAUDE_CODE_OAUTH_TOKEN ? 'OAuth token' : 'API key'; logger.info(`Validating ${authType} via pi...`); const probe = await probeCredentialsWithPi(authType); @@ -606,7 +555,7 @@ async function validateTargetUrl(targetUrl: string, logger: ActivityLogger): Pro * 1. Repository path exists and contains .git * 2. Config file parses and validates (if configPath provided) * 3. code_path rules match at least one entry in the repo (skipped without config) - * 4. Credentials validate (API key, OAuth, Bedrock, or Vertex AI) + * 4. Credentials validate (API key, OAuth, or Bedrock) * 5. Target URL is reachable from the container * * Returns on first failure. diff --git a/apps/worker/src/temporal/activities.ts b/apps/worker/src/temporal/activities.ts index e4281e9..4931478 100644 --- a/apps/worker/src/temporal/activities.ts +++ b/apps/worker/src/temporal/activities.ts @@ -459,7 +459,7 @@ export async function runReportAgent(input: ActivityInput): Promise; diff --git a/docs/ai-providers.md b/docs/ai-providers.md index 3a6dbd0..591e485 100644 --- a/docs/ai-providers.md +++ b/docs/ai-providers.md @@ -1,6 +1,6 @@ # AI Providers -Shannon Lite works best with Claude models. Anthropic API keys are recommended for most users, and Shannon Lite also supports AWS Bedrock, Google Vertex AI, and custom Anthropic-compatible endpoints. +Shannon Lite works best with Claude models. Anthropic API keys are recommended for most users, and Shannon Lite also supports AWS Bedrock and custom Anthropic-compatible endpoints. ## Anthropic @@ -59,38 +59,6 @@ Shannon Lite uses three model tiers: Set `ANTHROPIC_SMALL_MODEL`, `ANTHROPIC_MEDIUM_MODEL`, and `ANTHROPIC_LARGE_MODEL` to Bedrock model IDs available in your region. -## Google Vertex AI - -Create a service account with the `roles/aiplatform.user` role in the GCP Console, then download a JSON key file. - -Run `npx @keygraph/shannon setup` and select **Google Vertex AI**. The wizard prompts for region, project ID, service account key file path, and model IDs. The key file is copied to `~/.shannon/google-sa-key.json`. - -Or export environment variables directly: - -```bash -export CLAUDE_CODE_USE_VERTEX=1 -export CLOUD_ML_REGION=us-east5 -export ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id -export GOOGLE_APPLICATION_CREDENTIALS=/path/to/your-sa-key.json -export ANTHROPIC_SMALL_MODEL=claude-haiku-4-5@20251001 -export ANTHROPIC_MEDIUM_MODEL=claude-sonnet-4-6 -export ANTHROPIC_LARGE_MODEL=claude-opus-4-8 -``` - -Source-build `.env` equivalent: - -```bash -CLAUDE_CODE_USE_VERTEX=1 -CLOUD_ML_REGION=us-east5 -ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id -GOOGLE_APPLICATION_CREDENTIALS=./credentials/google-sa-key.json -ANTHROPIC_SMALL_MODEL=claude-haiku-4-5@20251001 -ANTHROPIC_MEDIUM_MODEL=claude-sonnet-4-6 -ANTHROPIC_LARGE_MODEL=claude-opus-4-8 -``` - -Set `CLOUD_ML_REGION=global` for global endpoints, or use a specific region like `us-east5`. Some models may not be available on global endpoints. - ## Custom Base URL Shannon Lite supports pointing the SDK at an Anthropic-compatible endpoint with `ANTHROPIC_BASE_URL`. For proxy-based routing, use an LLM proxy such as LiteLLM configured to expose an Anthropic-compatible endpoint.