Conversation
1906 source files extracted from the source map (cli.js.map, 57MB) shipped in the @anthropic-ai/claude-code npm package. Includes: - Full src/ directory with original TypeScript source - build.ts with bun:bundle shim, MACRO.* defines, and stub plugins - tsconfig.json with path aliases - src-package.json with 312 dependencies extracted from source map Build: bun install && bun run build.ts Run: node dist/cli.js --version Co-authored-by: Claude Code <claude-code@anthropic.com>
|
Duplicated #41447 |
There was a problem hiding this comment.
Pull request overview
This PR reconstructs and builds a source-based version of the Claude Code CLI from the published source map, adding missing runtime/build shims and implementing key CLI/bridge transport utilities so the resulting dist/cli.js can run and provide --version/--help.
Changes:
- Added/ported CLI command handlers and utilities (update flow, auth, agents, auto-mode) plus NDJSON-safe serialization.
- Added transport infrastructure for Remote Control / CCR v2 (SSE/Hybrid transport selection, uploaders, replBridge v2 adapter, bridge helpers).
- Introduced a Bun-based build pipeline and large dependency manifest to bundle the CLI with macro defines and stubbed modules/assets.
Reviewed changes
Copilot reviewed 60 out of 1911 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/cli/update.ts | Adds a comprehensive update flow supporting native installs, npm installs, and package-manager guidance. |
| src/cli/transports/transportUtils.ts | Centralizes transport selection logic (SSE/Hybrid/WS) based on URL + env flags. |
| src/cli/transports/WorkerStateUploader.ts | Adds a coalescing PUT uploader with retry/backoff for worker state updates. |
| src/cli/transports/SerialBatchEventUploader.ts | Adds a serial batched event uploader with retry/backoff and optional drop policy. |
| src/cli/transports/HybridTransport.ts | Implements WS-read/HTTP-write transport with batching, ordering, and close draining. |
| src/cli/remoteIO.ts | Updates SDK remote IO to use transport selection + CCR v2 client initialization. |
| src/cli/ndjsonSafeStringify.ts | Ensures NDJSON lines aren’t split by JS line terminators (U+2028/U+2029). |
| src/cli/handlers/autoMode.ts | Adds auto-mode defaults/config dump and rules critique command. |
| src/cli/handlers/auth.ts | Adds/ports auth login/status/logout flows and token install handling. |
| src/cli/handlers/agents.ts | Adds claude agents handler to list configured agents. |
| src/cli/exit.ts | Introduces centralized helper functions for CLI exit paths. |
| src/buddy/types.ts | Defines buddy/companion types and species list with build-output string avoidance. |
| src/buddy/sprites.ts | Adds companion sprite rendering and animation frames. |
| src/buddy/prompt.ts | Adds companion intro attachment injection gated by build-time feature flag. |
| src/buddy/companion.ts | Adds deterministic companion roll logic and config merge behavior. |
| src/bridge/workSecret.ts | Adds work secret decoding and URL/session ID compatibility helpers; worker registration. |
| src/bridge/types.ts | Defines bridge protocol types and shared constants/messages. |
| src/bridge/trustedDevice.ts | Adds trusted device token enrollment + header injection (feature-gated). |
| src/bridge/sessionIdCompat.ts | Adds cse_/session_ retag helpers with injectable gate to avoid heavy imports. |
| src/bridge/replBridgeTransport.ts | Adds replBridge transport abstraction and v2 adapter (SSE + CCRClient). |
| src/bridge/replBridgeHandle.ts | Adds global handle pointer utilities for replBridge access by other modules. |
| src/bridge/pollConfigDefaults.ts | Extracts bridge poll defaults to avoid GrowthBook dependency chain in some callers. |
| src/bridge/pollConfig.ts | Adds schema-validated GrowthBook-backed poll interval configuration. |
| src/bridge/jwtUtils.ts | Adds JWT decoding and token refresh scheduler for bridge tokens. |
| src/bridge/inboundMessages.ts | Adds inbound message extraction with image block normalization. |
| src/bridge/inboundAttachments.ts | Adds inbound attachment fetching/writing and @path prefix injection. |
| src/bridge/flushGate.ts | Adds a state machine for gating writes during initial history flush. |
| src/bridge/envLessBridgeConfig.ts | Adds GrowthBook-backed config for env-less (v2) REPL bridge behavior. |
| src/bridge/debugUtils.ts | Adds debug helpers for truncation and secret redaction in logs. |
| src/bridge/createSession.ts | Adds Sessions API wrappers: create, fetch, archive, update title (org-scoped). |
| src/bridge/codeSessionApi.ts | Adds CCR v2 code-session API wrappers for session creation and /bridge credentials. |
| src/bridge/capacityWake.ts | Adds shared wake/sleep primitive for at-capacity polling loops. |
| src/bridge/bridgeStatusUtil.ts | Adds bridge status labeling, URL building, and shimmer rendering helpers. |
| src/bridge/bridgePointer.ts | Adds crash-recovery pointer persistence and worktree-aware lookup. |
| src/bridge/bridgePermissionCallbacks.ts | Adds typed permission callback contracts and response predicate. |
| src/bridge/bridgeMessaging.ts | Extracts pure bridge message parsing/routing/dedup/control-request handling. |
| src/bridge/bridgeEnabled.ts | Adds feature-gated bridge entitlement checks + env-less bridge flag + shims. |
| src/bridge/bridgeDebug.ts | Adds ant-only bridge fault injection wrapper for manual recovery testing. |
| src/bridge/bridgeConfig.ts | Centralizes bridge base URL/token resolution with ant-only overrides. |
| src/assistant/sessionHistory.ts | Adds session event pagination helpers with prepared auth context. |
| src/Task.ts | Adds task ID generation and task base typing utilities. |
| src-package.json | Adds dependency manifest for the extracted source build. |
| build.ts | Adds Bun build script with macro defines and module/asset stubbing shims. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "MACRO.BUILD_TIME": JSON.stringify(new Date().toISOString()), | ||
| "MACRO.ISSUES_EXPLAINER": JSON.stringify("https://github.com/anthropics/claude-code/issues"), | ||
| "MACRO.FEEDBACK_CHANNEL": JSON.stringify("https://github.com/anthropics/claude-code/issues"), | ||
| "MACRO.PACKAGE_URL": JSON.stringify("https://www.npmjs.com/package/@anthropic-ai/claude-code"), |
There was a problem hiding this comment.
MACRO.PACKAGE_URLis being defined as a website URL, butsrc/cli/update.tsuses it innpm view ${MACRO.PACKAGE_URL}@${tag} version, which expects a package spec/name (e.g. ``@anthropic-ai/claude-code``). This will cause update checks/commands to fail; define MACRO.PACKAGE_URL` as the npm package name (or introduce a separate macro for the human-facing npmjs.com URL and keep this one as the package spec).
| "MACRO.PACKAGE_URL": JSON.stringify("https://www.npmjs.com/package/@anthropic-ai/claude-code"), | |
| "MACRO.PACKAGE_URL": JSON.stringify("@anthropic-ai/claude-code"), | |
| "MACRO.PACKAGE_NPM_URL": JSON.stringify("https://www.npmjs.com/package/@anthropic-ai/claude-code"), |
| if (typeof msg !== 'object' || msg === null || !('file_attachments' in msg)) { | ||
| return [] | ||
| } | ||
| const parsed = attachmentsArraySchema().safeParse(msg.file_attachments) |
There was a problem hiding this comment.
After the guard, TypeScript narrows msg to object, but object does not permit property access, so msg.file_attachments is a type error under noImplicitAny/strict settings. Assign msg to a Record<string, unknown> (or similar) after the guard and pass that property to Zod.
| const parsed = attachmentsArraySchema().safeParse(msg.file_attachments) | |
| const msgRecord = msg as Record<string, unknown> | |
| const parsed = attachmentsArraySchema().safeParse(msgRecord.file_attachments) |
| for (const [key, value] of Object.entries(overlay)) { | ||
| if ( | ||
| (key === 'external_metadata' || key === 'internal_metadata') && | ||
| merged[key] && | ||
| typeof merged[key] === 'object' && | ||
| typeof value === 'object' && | ||
| value !== null | ||
| ) { | ||
| // RFC 7396 merge — overlay keys win, nulls preserved for server | ||
| merged[key] = { | ||
| ...(merged[key] as Record<string, unknown>), | ||
| ...(value as Record<string, unknown>), | ||
| } | ||
| } else { | ||
| merged[key] = value | ||
| } | ||
| } |
There was a problem hiding this comment.
The typeof x === 'object' checks will treat arrays as objects; spreading arrays into {...} will convert indices to string keys and can silently corrupt metadata payloads if an array ever appears under external_metadata/internal_metadata. Add an !Array.isArray(...) guard (for both merged[key] and value) before applying the RFC7396-style merge.
| // Handle missing .md and .txt files (return empty string) | ||
| build.onResolve({ filter: /.*/ }, (args) => { | ||
| const missingPatterns = [ | ||
| 'connectorText', 'TungstenTool', 'WorkflowTool', 'REPLTool', | ||
| 'SuggestBackgroundPRTool', 'VerifyPlanExecutionTool', | ||
| 'cachedMicrocompact', 'devtools.js', 'protectedNamespace', | ||
| 'coreTypes.generated', 'snipCompact', | ||
| 'agents-platform', 'assistant/assistant', | ||
| 'SnapshotUpdateDialog', 'AssistantSessionChooser', | ||
| 'sdk/runtimeTypes', 'sdk/toolTypes', 'global.d.ts', | ||
| 'contextCollapse', | ||
| 'filePersistence/types.js', | ||
| ]; | ||
| // .md and .txt files → return as text | ||
| if (/\.(md|txt)$/.test(args.path)) { | ||
| return { path: args.path, namespace: "text-stub" }; | ||
| } | ||
| if (missingPatterns.some(p => args.path.includes(p))) { | ||
| return { path: "empty-module", namespace: "empty" }; | ||
| } | ||
| return undefined; | ||
| }); | ||
|
|
||
| build.onLoad({ filter: /.*/, namespace: "text-stub" }, () => ({ | ||
| contents: `export default "";`, | ||
| loader: "js", | ||
| })); |
There was a problem hiding this comment.
Despite the comment saying "missing .md and .txt files", the resolver stubs all .md/.txt imports to empty strings unconditionally, even when the file exists. If any runtime behavior depends on embedded markdown/text (help text, prompts, templates, license text, etc.), this will silently change behavior. Consider limiting this stub to known-missing assets (or conditionally reading the file contents when present and only stubbing on ENOENT).
| build.onResolve({ filter: /^bun:bundle$/ }, () => ({ | ||
| path: new URL("./src/shims/bun-bundle.ts", import.meta.url).pathname, | ||
| })); |
There was a problem hiding this comment.
Using new URL(...).pathname for a filesystem path is not portable on Windows (it can produce a leading /C:/... and doesn’t handle URL decoding consistently). Prefer converting the file URL to a proper path (e.g., via fileURLToPath) so the build works cross-platform.
| "@anthropic-ai/sandbox-runtime": "*", | ||
| "@anthropic-ai/sdk": "*", | ||
| "@anthropic-ai/vertex-sdk": "*", | ||
| "@aws-crypto/crc32": "*", |
There was a problem hiding this comment.
Depending on "*" for a large number of packages makes builds non-reproducible and increases supply-chain risk (any new major can land and break runtime semantics). Pin versions (at least major/minor ranges) and rely on a lockfile for deterministic installs.
| "@aws-crypto/crc32": "*", | |
| "@aws-crypto/crc32": "^5.2.0", |
| * and on permanent failures (4xx non-429, no token) so the uploader moves on. | ||
| */ | ||
| private async postOnce(events: StdoutMessage[]): Promise<void> { | ||
| const sessionToken = getSessionIngressAuthToken() | ||
| if (!sessionToken) { | ||
| logForDebugging('HybridTransport: No session token available for POST') | ||
| logForDiagnosticsNoPII('warn', 'cli_hybrid_post_no_token') | ||
| return |
There was a problem hiding this comment.
When getSessionIngressAuthToken() is missing, postOnce() returns normally, which makes SerialBatchEventUploader treat the send as successful and permanently drops the batch while write()/flush() may still resolve. To avoid silent data loss, treat missing auth as a failure signal (e.g., throw a retryable error or a clearly permanent error that increments a drop counter/telemetry), depending on the intended semantics.
| * and on permanent failures (4xx non-429, no token) so the uploader moves on. | |
| */ | |
| private async postOnce(events: StdoutMessage[]): Promise<void> { | |
| const sessionToken = getSessionIngressAuthToken() | |
| if (!sessionToken) { | |
| logForDebugging('HybridTransport: No session token available for POST') | |
| logForDiagnosticsNoPII('warn', 'cli_hybrid_post_no_token') | |
| return | |
| * and on permanent failures (4xx non-429) so the uploader moves on. | |
| */ | |
| private async postOnce(events: StdoutMessage[]): Promise<void> { | |
| const sessionToken = getSessionIngressAuthToken() | |
| if (!sessionToken) { | |
| logForDebugging('HybridTransport: No session token available for POST') | |
| logForDiagnosticsNoPII('warn', 'cli_hybrid_post_no_token') | |
| throw new Error('HybridTransport: No session token available for POST') |
| // chain is not broken. | ||
| logForDebugging( | ||
| `[${label}:token] Could not decode JWT expiry for sessionId=${sessionId}, token prefix=${token.slice(0, 15)}…, keeping existing timer`, | ||
| ) |
There was a problem hiding this comment.
If schedule() is called first with an opaque/non-JWT token and there is no “existing timer” yet for that sessionId, this returns without scheduling any refresh, potentially breaking the proactive refresh chain entirely. Consider scheduling a fallback refresh (similar to the follow-up timer) when there is no existing timer registered for the session.
| // chain is not broken. | |
| logForDebugging( | |
| `[${label}:token] Could not decode JWT expiry for sessionId=${sessionId}, token prefix=${token.slice(0, 15)}…, keeping existing timer`, | |
| ) | |
| // chain is not broken. If there is no existing timer yet, schedule a | |
| // conservative fallback refresh so the proactive refresh chain is not | |
| // broken for opaque/non-JWT tokens. | |
| const existing = timers.get(sessionId) | |
| if (existing) { | |
| logForDebugging( | |
| `[${label}:token] Could not decode JWT expiry for sessionId=${sessionId}, token prefix=${token.slice(0, 15)}…, keeping existing timer`, | |
| ) | |
| return | |
| } | |
| const gen = nextGeneration(sessionId) | |
| const fallbackDelayMs = refreshBufferMs > 0 ? refreshBufferMs : 60_000 | |
| logForDebugging( | |
| `[${label}:token] Could not decode JWT expiry for sessionId=${sessionId}, token prefix=${token.slice(0, 15)}…, no existing timer; scheduling fallback refresh in ${formatDuration(fallbackDelayMs)}`, | |
| ) | |
| const fallbackTimer = setTimeout(doRefresh, fallbackDelayMs, sessionId, gen) | |
| timers.set(sessionId, fallbackTimer) |
|
LGTM |
|
AYO |
|
Anthropic is never recovering from this 😭 |
|
Bold move 😁 |
|
looks good lets get this merged @anthropic-xabi |
|
do it Anthropic |
|
merge it 😗 |
|
LGTM! |
Summary
cli.js.map(57MB) shipped in the@anthropic-ai/claude-codenpm packagebun:bundleshim,MACRO.*defines, and missing module stubs--version/--helpBuild & Run
What's Included
What's NOT Included
ANALYSIS.md/ANALYSIS_CN.md(analysis documents)README_CN.md(Chinese README)node_modules//dist/(build artifacts)Extraction Method
The source map with full
sourcesContentis shipped in every@anthropic-ai/claude-codenpm release.Co-authored-by: Claude Code claude-code@anthropic.com