From 3c9a62562183a006d95b5318eba63f2a39d5b9d1 Mon Sep 17 00:00:00 2001 From: claude-code-best Date: Sat, 20 Jun 2026 10:21:31 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=88=A0=E9=99=A4=203=20=E4=B8=AA?= =?UTF-8?q?=E5=AD=A4=E7=AB=8B=E8=AF=8A=E6=96=AD=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - scripts/verify-autofix-pr.ts: 一次性 autofix-pr 验证脚本,全仓零引用 - scripts/smoke-test-commands.ts: 开发期冒烟测试脚本,无任何 import - scripts/probe-subscription-endpoints.ts: 手动 endpoint 探针,无引用 均不在 package.json scripts、build.ts、vite.config.ts、CI workflows 中。 Co-Authored-By: glm-5.2 --- scripts/probe-subscription-endpoints.ts | 137 ----------------- scripts/smoke-test-commands.ts | 186 ------------------------ scripts/verify-autofix-pr.ts | 40 ----- 3 files changed, 363 deletions(-) delete mode 100644 scripts/probe-subscription-endpoints.ts delete mode 100644 scripts/smoke-test-commands.ts delete mode 100644 scripts/verify-autofix-pr.ts diff --git a/scripts/probe-subscription-endpoints.ts b/scripts/probe-subscription-endpoints.ts deleted file mode 100644 index 8bb647517..000000000 --- a/scripts/probe-subscription-endpoints.ts +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env bun -/** - * Probe what /v1/* endpoints the subscription OAuth bearer can actually reach. - * - * Goal: ground-truth the auth-plane question. Some endpoints in the v2.1.123 - * binary's reverse-engineered list might still accept subscription bearer - * tokens even though the binary itself only invokes them with workspace API - * keys. The only way to know is to actually call them and read the status. - * - * Strategy: send a low-risk GET to each candidate, record status + body - * preview. Never POST/DELETE/PATCH (could create/destroy real resources). - * - * Run: bun --feature AUTOFIX_PR scripts/probe-subscription-endpoints.ts - */ - -import { getOauthConfig } from '../src/constants/oauth.ts' -import { - getOAuthHeaders, - prepareApiRequest, -} from '../src/utils/teleport/api.ts' -import { enableConfigs } from '../src/utils/config.ts' - -// fork's config layer is gated; main entry calls enableConfigs() before any -// reads. We bypass the entry point so we have to flip the gate ourselves. -enableConfigs() - -// Endpoints harvested from `grep -aoE "/v1/[a-z_]+(/[a-z_-]+)*" claude.exe` -const CANDIDATES: Array<{ path: string; betas: string[] }> = [ - // Subscription plane (known-good baseline) - { path: '/v1/code/triggers', betas: ['ccr-triggers-2026-01-30'] }, - { path: '/v1/code/sessions', betas: [] }, - { path: '/v1/code/github/import-token', betas: [] }, - { path: '/v1/sessions', betas: [] }, - - // Workspace plane suspects (the user wants ground-truth) - { - path: '/v1/agents', - betas: ['', 'managed-agents-2026-04-01', 'agents-2026-04-01'], - }, - { - path: '/v1/vaults', - betas: ['', 'managed-agents-2026-04-01', 'vaults-2026-04-01'], - }, - { path: '/v1/memory_stores', betas: ['', 'managed-agents-2026-04-01'] }, - { path: '/v1/mcp_servers', betas: ['', 'managed-agents-2026-04-01'] }, - { path: '/v1/projects', betas: [''] }, - { path: '/v1/environments', betas: [''] }, - { path: '/v1/environment_providers', betas: [''] }, - { path: '/v1/skills', betas: ['', 'skills-2025-10-02'], query: '?beta=true' }, - - // Misc - { path: '/v1/models', betas: [''] }, - { path: '/v1/files', betas: [''] }, - { path: '/v1/oauth/hello', betas: [''] }, - { path: '/v1/messages/count_tokens', betas: [''] }, - - // Workspace fact-check - { path: '/v1/certs', betas: [''] }, - { path: '/v1/logs', betas: [''] }, - { path: '/v1/traces', betas: [''] }, - { path: '/v1/security/advisories/bulk', betas: [''] }, - { path: '/v1/feedback', betas: [''] }, -] as Array<{ path: string; betas: string[]; query?: string }> - -async function probe( - baseUrl: string, - accessToken: string, - orgUUID: string, - candidate: { path: string; betas: string[]; query?: string }, -): Promise { - for (const beta of candidate.betas) { - const headers: Record = { - ...getOAuthHeaders(accessToken), - 'x-organization-uuid': orgUUID, - } - if (beta) headers['anthropic-beta'] = beta - const url = `${baseUrl}${candidate.path}${candidate.query ?? ''}` - let status = 0 - let body = '' - try { - const res = await fetch(url, { - method: 'GET', - headers, - signal: AbortSignal.timeout(8000), - }) - status = res.status - body = (await res.text()).slice(0, 240).replace(/\s+/g, ' ').trim() - } catch (e: unknown) { - body = `(network) ${e instanceof Error ? e.message : String(e)}` - } - const betaLabel = beta || '' - const verdict = - status >= 200 && status < 300 - ? 'OK' - : status === 401 - ? 'AUTH' - : status === 403 - ? 'FORBID' - : status === 404 - ? 'NF' - : status === 400 - ? 'BAD' - : status === 0 - ? 'NET' - : `${status}` - const padded = candidate.path.padEnd(38) - const betaPad = betaLabel.padEnd(34) - console.log( - ` ${verdict.padEnd(6)} ${padded} ${betaPad} ${body.slice(0, 110)}`, - ) - } -} - -async function main(): Promise { - console.log( - '=== Probe subscription OAuth bearer against /v1/* candidates ===\n', - ) - const { accessToken, orgUUID } = await prepareApiRequest() - const baseUrl = getOauthConfig().BASE_API_URL - const { origin: baseOrigin } = new URL(baseUrl) - console.log(`base: ${baseOrigin}`) - console.log(`orgUUID: ${orgUUID.slice(0, 4)}…\n`) - console.log( - ' STATUS PATH BETA HEADER RESPONSE PREVIEW', - ) - console.log( - ' ------ ------------------------------------ ---------------------------------- ---------------------------------------------', - ) - for (const c of CANDIDATES) { - await probe(baseUrl, accessToken, orgUUID, c) - } - console.log( - '\nLegend: OK=2xx AUTH=401 FORBID=403 NF=404 BAD=400 NET=network/timeout =other', - ) -} - -await main() diff --git a/scripts/smoke-test-commands.ts b/scripts/smoke-test-commands.ts deleted file mode 100644 index 8a9ad27c1..000000000 --- a/scripts/smoke-test-commands.ts +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env bun -/** - * Smoke-test all newly-restored commands by actually loading and invoking - * them (no mocks). Each command must: - * 1. Have isEnabled() === true - * 2. Have isHidden === false - * 3. load() resolve to a callable - * 4. call() return a non-empty result without throwing - * - * Run with: bun --feature AUTOFIX_PR scripts/smoke-test-commands.ts - * - * NOTE: enableConfigs() must be called BEFORE any command index.ts is - * imported. Several commands evaluate `getGlobalConfig().workspaceApiKey` - * at module-load time (PR-5 dual-source isHidden), and getGlobalConfig - * throws "Config accessed before allowed" until enableConfigs runs. The - * real dev/build entry calls this from main.tsx; bypassing main means we - * have to invoke it ourselves. - */ -// NOTE: This bypasses the REPL — local-jsx commands that need React/Ink -// context will fail with informative messages. That's expected and we mark -// those PARTIAL. -import { enableConfigs } from '../src/utils/config.ts' -enableConfigs() - -type CmdSpec = { - mod: string - name: string - sample?: string - type: string - /** Set true when this command's isHidden depends on env var (e.g. workspace - * API key for /vault) — smoke test should pass even when isHidden is true. */ - hiddenWithoutEnv?: boolean - /** Override which export to import. Default: `default ?? mod[name]`. - * Use this for double-registered commands (e.g. /context, /break-cache) that - * expose separate interactive + non-interactive entries; the non-interactive - * one is the right target for a Node-only smoke run. */ - exportName?: string -} - -const COMMANDS: CmdSpec[] = [ - { mod: '../src/commands/env/index.ts', name: 'env', type: 'local' }, - { - mod: '../src/commands/debug-tool-call/index.ts', - name: 'debug-tool-call', - type: 'local', - }, - { - mod: '../src/commands/perf-issue/index.ts', - name: 'perf-issue', - type: 'local', - }, - // break-cache is double-registered: default export is the interactive - // (local-jsx) variant which is disabled outside the REPL. Test the - // non-interactive named export here instead. - { - mod: '../src/commands/break-cache/index.ts', - name: 'break-cache', - type: 'local', - exportName: 'breakCacheNonInteractive', - }, - { mod: '../src/commands/share/index.ts', name: 'share', type: 'local' }, - { mod: '../src/commands/issue/index.ts', name: 'issue', type: 'local' }, - { - mod: '../src/commands/teleport/index.ts', - name: 'teleport', - sample: '', - type: 'local-jsx', - }, - { - mod: '../src/commands/autofix-pr/index.ts', - name: 'autofix-pr', - sample: 'stop', - type: 'local-jsx', - }, - { - mod: '../src/commands/onboarding/index.ts', - name: 'onboarding', - sample: 'status', - type: 'local-jsx', - }, - // These 3 are isHidden when ANTHROPIC_API_KEY isn't set (PR-1 dynamic gating). - { - mod: '../src/commands/agents-platform/index.ts', - name: 'agents-platform', - sample: 'list', - type: 'local-jsx', - hiddenWithoutEnv: true, - }, - { - mod: '../src/commands/memory-stores/index.ts', - name: 'memory-stores', - sample: 'list', - type: 'local-jsx', - hiddenWithoutEnv: true, - }, - { - mod: '../src/commands/schedule/index.ts', - name: 'schedule', - sample: 'list', - type: 'local-jsx', - }, -] - -async function smoke( - spec: CmdSpec, -): Promise<{ name: string; ok: boolean; note: string }> { - try { - const mod = await import(spec.mod) - const cmd = spec.exportName - ? mod[spec.exportName] - : (mod.default ?? mod[spec.name]) - if (!cmd) return { name: spec.name, ok: false, note: 'no default export' } - if (cmd.name !== spec.name) { - return { name: spec.name, ok: false, note: `name mismatch: ${cmd.name}` } - } - if (cmd.isHidden) { - // Commands with env-var-gated visibility (e.g. ANTHROPIC_API_KEY) are - // expected to be hidden when the env var is unset. Treat that as pass - // with an informative note rather than fail. - if (spec.hiddenWithoutEnv) { - return { - name: spec.name, - ok: true, - note: 'isHidden=true (env-gated, set ANTHROPIC_API_KEY to enable)', - } - } - return { name: spec.name, ok: false, note: 'isHidden=true' } - } - const enabled = cmd.isEnabled?.() ?? true - if (!enabled) - return { name: spec.name, ok: false, note: 'isEnabled()=false' } - if (cmd.type !== spec.type) { - return { name: spec.name, ok: false, note: `type mismatch: ${cmd.type}` } - } - if (!cmd.load) return { name: spec.name, ok: false, note: 'no load()' } - const loaded = await cmd.load() - if (typeof loaded.call !== 'function') { - return { - name: spec.name, - ok: false, - note: 'load() did not return { call }', - } - } - if (cmd.type === 'local') { - const result = await loaded.call(spec.sample ?? '', null) - const valLen = result?.value?.length ?? 0 - if (valLen < 10) { - return { - name: spec.name, - ok: false, - note: `result too short (${valLen} chars)`, - } - } - return { name: spec.name, ok: true, note: `${valLen} chars output` } - } - // local-jsx commands need a real React context; we just check load() works. - return { - name: spec.name, - ok: true, - note: 'load() ok (local-jsx, REPL needed for full call)', - } - } catch (e: unknown) { - return { - name: spec.name, - ok: false, - note: e instanceof Error ? e.message.slice(0, 80) : String(e), - } - } -} - -async function main() { - console.log('=== Command smoke test ===\n') - let pass = 0 - let fail = 0 - for (const spec of COMMANDS) { - const r = await smoke(spec) - const tag = r.ok ? '✓' : '✗' - console.log(` ${tag} /${r.name.padEnd(18)} ${r.note}`) - if (r.ok) pass++ - else fail++ - } - console.log(`\nTotal: ${pass} pass, ${fail} fail`) - process.exit(fail === 0 ? 0 : 1) -} - -await main() diff --git a/scripts/verify-autofix-pr.ts b/scripts/verify-autofix-pr.ts deleted file mode 100644 index fc86f0f26..000000000 --- a/scripts/verify-autofix-pr.ts +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bun -// One-shot verification: import the autofix-pr command exactly the way -// commands.ts does, and dump its registration shape + isEnabled() result. -// Run with: bun --feature AUTOFIX_PR scripts/verify-autofix-pr.ts - -import autofixPr from '../src/commands/autofix-pr/index.ts' - -console.log('=== /autofix-pr Command Registration ===') -console.log('name: ', autofixPr.name) -console.log('type: ', autofixPr.type) -console.log('description: ', autofixPr.description) -console.log('argumentHint: ', autofixPr.argumentHint) -console.log('isHidden: ', autofixPr.isHidden) -console.log('bridgeSafe: ', autofixPr.bridgeSafe) -console.log('isEnabled(): ', autofixPr.isEnabled?.()) -console.log() -console.log('Bridge invocation validation:') -const cases: Array<[string, string]> = [ - ['', 'empty (should reject)'], - ['stop', 'stop (should accept)'], - ['off', 'off (should accept)'], - ['386', 'PR# (should accept)'], - ['anthropics/claude-code#999', 'cross-repo (should accept)'], - ['fix the typo', 'freeform (should reject for bridge)'], -] -for (const [arg, label] of cases) { - const err = autofixPr.getBridgeInvocationError?.(arg) - console.log(` ${label.padEnd(35)} → ${err ?? 'OK (no error)'}`) -} -console.log() -console.log('=== Verdict ===') -const enabled = autofixPr.isEnabled?.() -const visible = !autofixPr.isHidden && enabled -console.log(`Visible in slash menu: ${visible ? 'YES ✓' : 'NO ✗'}`) -if (!visible) { - console.log(' - isEnabled():', enabled) - console.log(' - isHidden: ', autofixPr.isHidden) - console.log(' Hint: ensure FEATURE_AUTOFIX_PR=1 or AUTOFIX_PR is in') - console.log(' DEFAULT_BUILD_FEATURES (scripts/defines.ts).') -}