Compare commits

...

100 Commits

Author SHA1 Message Date
anandh8x
bd8d0ef0fa Remove embedded source map directives from tracked sources
Inline base64 source maps had been checked into tracked src files. This strips those comments from the repository without changing runtime behavior or adding ongoing guardrails, per the requested one-time cleanup scope.

Constraint: Keep this change limited to tracked source cleanup only
Rejected: Add CI/source verification guard | user requested one-time cleanup only
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: If these directives reappear, fix the producing transform instead of reintroducing repo-side cleanup code
Tested: rg -n "sourceMappingURL" ., bun run smoke, bun run verify:privacy, bun run test:provider, npm run test:provider-recommendation
Not-tested: bun run typecheck (repository has many pre-existing unrelated failures)
2026-04-04 13:38:49 +05:30
Yakout
c3c60b7bab fix: OAuth tokens secure storage for Windows & Linux (#215)
* fix: OAuth tokens secure storage for Windows & Linux

* fix: OAuth tokens secure storage for Windows & Linux #215

* fix: OAuth tokens secure storage for Windows & Linux #215

* fix: OAuth tokens secure storage for Windows & Linux #215
2026-04-04 14:26:56 +08:00
KRATOS
27e6505bfd hardening: isolate third-party paths and clean external-build metadata (#311)
* hardening: isolate third-party paths and clean external-build metadata

* fix: restore external feedback flow and make privacy check portable
2026-04-04 14:22:33 +08:00
Vasanth T
cdbe016e6f fix: apply provider flag before startup banner (#322) 2026-04-04 14:19:02 +08:00
KRATOS
bd4daa3ee7 cleanup: remove dead USER_TYPE fast mode and setup branches (#315) 2026-04-04 14:15:28 +08:00
Anandan
5be5387096 Clear a tiny message-component batch of unused React imports (#321)
This sixth pass continues issue #314 with a message-focused micro-batch: three tiny message components that each only surfaced a single unused React import warning. The batch stays intentionally minimal and behavior-neutral.

Constraint: Keep pass 6 limited to one-line message-component cleanup with the same warning shape
Rejected: Mix in broader message component cleanup or unrelated typing fixes | would dilute the micro-pass and expand review scope unnecessarily
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Continue splitting by path + warning shape when easy one-line cleanups are available
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)

Co-authored-by: anandh8x <test@example.com>
2026-04-04 14:13:01 +08:00
Anandan
897ef2002e Trim a small LogoV2 batch of unused React imports (#320)
This fifth cleanup pass follows issue #314 with another homogeneous, low-risk slice: three LogoV2 notice/upsell components that each only surfaced a single unused React import warning. Removing just those imports keeps the series moving without mixing in broader cleanup categories.

Constraint: Keep pass 5 to files with the same single-warning pattern
Rejected: Fold in unrelated LogoV2 files with wider warnings or other cleanup shapes | would make the pass less uniform and less reviewable
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Continue preferring single-pattern micro-passes when the compiler output presents them clearly
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)

Co-authored-by: anandh8x <test@example.com>
2026-04-04 14:12:35 +08:00
Anandan
ab3c46a591 Remove a tiny batch of unused React imports (#319)
This fourth pass continues issue #314 with the smallest cleanup slice so far: six files that each surfaced the same single warning for an unused React import. Keeping the batch this focused improves review speed and removes noise without changing behavior or widening the cleanup scope.

Constraint: Limit this pass to single-warning files only so the intent stays obvious
Rejected: Combine this with broader typing or props-alias cleanup | unnecessary scope expansion for an already reviewable one-line pattern
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep using ultra-small homogeneous passes whenever the compiler output exposes them; they are cheap to review and easy to revert
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)

Co-authored-by: anandh8x <test@example.com>
2026-04-04 14:12:18 +08:00
Anandan
03dff274a1 Peel off another tiny display-component cleanup batch (#318)
This third pass continues the issue #314 cleanup series with four very small display-oriented components. The changes only remove unused React imports and reconnect existing Props aliases or parameter types where the files were otherwise surfacing straightforward compiler noise.

Constraint: Keep the pass limited to trivial display components with uniform low-risk cleanup shape
Rejected: Mix in files with unrelated missing-module or broader logic noise | weakens review focus and muddies verification
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Prefer these tiny homogeneous slices while the repo still has wider baseline no-unused noise
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)

Co-authored-by: anandh8x <test@example.com>
2026-04-04 14:11:44 +08:00
Anandan
bffd43056f Trim another narrow batch of dialog cleanup noise (#317)
This follow-up pass continues the phased unused-code cleanup from issue #314 with four dialog components that shared the same low-risk pattern: avoidable unused imports, dormant Props aliases, and untyped callback plumbing that only existed as compiler noise. The changes keep behavior intact while reducing the next layer of cleanup friction.

Constraint: Keep the second pass narrowly scoped and homogeneous so it stays easy to review beside PR #316
Rejected: Fold in EffortCallout and wider component cleanup at the same time | larger surface area and less uniform risk profile
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Continue batching future passes by shared cleanup pattern; leave broader refactors and compatibility placeholders for separate PRs
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)

Co-authored-by: anandh8x <test@example.com>
2026-04-04 14:11:08 +08:00
KRATOS
c52245fc0a fix: restore image paste and image tool-result handling (#308) 2026-04-04 14:10:26 +08:00
Anandan
365bd3102d Reduce low-risk unused-symbol noise in core components (#316)
This first cleanup pass removes clearly unused imports and dead locals from a small set of components, and reconnects a few existing Props aliases to their component signatures so they stop surfacing as avoidable noise. The scope stays intentionally narrow to make the follow-up cleanup series easier to review and lower risk.

Constraint: Follow issue #314 with a components-only, low-risk first pass
Rejected: Broader sweep across commands/hooks/utils in the same PR | too much review surface for an initial cleanup pass
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep subsequent unused-code cleanups narrowly batched; treat signature/compatibility placeholders separately from straightforward import/alias cleanup
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (baseline repo noise remains outside this narrow pass)

Co-authored-by: anandh8x <test@example.com>
2026-04-04 14:09:00 +08:00
Anandan
3df635c24d Remove a tiny PromptInput batch of unused React imports (#323)
This seventh pass continues issue #314 with a two-file PromptInput micro-batch. Both files shared the same single-warning pattern for an unused React import, so the cleanup stays extremely small, behavior-neutral, and easy to review.

Constraint: Keep pass 7 to the exact two-file PromptInput pair with the same warning shape
Rejected: Fold in broader PromptInput cleanup or unrelated warnings | would expand scope beyond a simple micro-pass
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Continue harvesting paired file/test micro-batches when the compiler output exposes them cleanly
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)

Co-authored-by: anandh8x <test@example.com>
2026-04-04 14:08:30 +08:00
Christian Schimetschka
2031c67d46 refactor: improve error response for non-available models (#298) 2026-04-04 10:15:27 +08:00
pr0ln
694c242865 Reduce resume OOM risk and fix update-config skill init (#304)
* Reduce resume transcript memory pressure

* Fix update-config bundled skill schema generation

---------

Co-authored-by: pr0ln <pr0ln@pr0lnui-Macmini.local>
2026-04-04 10:15:01 +08:00
pr0ln
fb221baa21 Limit auto-mode classifier transcript growth (#277)
* Limit auto-mode classifier transcript growth

* Release persisted tool results from transcript state

---------

Co-authored-by: pr0ln <pr0ln@pr0lnui-Macmini.local>
2026-04-04 09:24:14 +08:00
Meetpatel006
e5c9a6f629 Enable Free DDG WebSearch For Non-Claude Models (#234)
* added duck duck go for websearch tools that allowed free searching

* update readme

* Replace @phukon/duckduckgo-search with duck-duck-scrape and fix Firecrawl routing priority, and add DDG error handling

* refactor: streamline DuckDuckGo search fallback to use Firecrawl directly on rate limit

* docs: update README to clarify DuckDuckGo web search fallback and its limitations with TOS
2026-04-04 09:21:54 +08:00
step325
70cfa61582 fix: disable experimental API betas by default, reduce side query token usage, standardize Headers type (#281)
* fix: disable experimental API betas by default to prevent 500 errors

Tool search (defer_loading), global cache scope, and context management
betas require internal Anthropic server-side support. External accounts
receive 500 Internal Server Error when these are sent.

Set CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=true by default in the CLI
entrypoint. Users with internal access can opt back in with =false.

Also includes: cache key stability fixes (Sonnet 1M latch, system-before-
messages key ordering, resume fingerprint isMeta skip), sideQuery default
cleanup, and /dream command.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: standardize API headers to Headers type and enable tengu feature flags by default

* fix: address PR review — dream lock, MCP betas guard, redundant Partial

- Call recordConsolidation() programmatically in /dream instead of
  delegating to model prompt (unreliable)
- Add CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS guard to MCP entrypoint
  (was only in CLI entrypoint, causing 500s in MCP server mode)
- Remove redundant ? markers from SecretValueSource Partial<{}> type

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 01:40:07 +08:00
KRATOS
afed73fa5a fix: resolve keyboard input freeze on Windows and Mac at startup (#285)
Three compounding issues caused keyboard input to appear frozen or drop
characters on startup, particularly on Windows CMD/PowerShell and Mac
terminal environments.

Issue 1 — earlyInput disabled (cli.tsx):
The rebrand commit gated startCapturingEarlyInput() behind an opt-in
env flag (OPENCLAUDE_ENABLE_EARLY_INPUT=1), which meant any characters
typed before React finished mounting were silently dropped. Users who
type immediately after launch saw an empty input box with no indication
their keystrokes were lost. Flipped to an opt-out flag
(OPENCLAUDE_DISABLE_EARLY_INPUT=1) so early input capture is on by
default, matching the original upstream behaviour.

Issue 2 — stdin.resume() called before listener attached (App.tsx):
stdin.resume() put the stream into flowing mode before the data/readable
listener was registered. Any input arriving in that gap was queued and
delivered in a burst when the listener connected, which could flood
React's scheduler and stall input processing. Moved resume() to after
the listener is attached so the stream only flows once the handler is
ready.

Issue 3 — AnimatedAsterisk fires ~60 React re-renders in 3s (AnimatedAsterisk.tsx):
The startup screen colour sweep animation runs at 50ms intervals for
3000ms total. Each tick triggers a full re-render of the startup screen
subtree, which competes with stdin event processing in React's microtask
queue. On Windows, where the event loop scheduler is slower, this
reliably caused typing to lag or freeze for the first few seconds after
launch. The animation is now skipped on Windows (process.platform ===
'win32'), showing the icon in its settled state immediately. Mac and
Linux are unaffected.

Closes #228, #220, #205
2026-04-04 01:34:41 +08:00
KRATOS
c735233f92 fix: change default config dir from ~/.claude to ~/.openclaude (#280)
Prevents collision with existing Claude Code installations that already
use ~/.claude for their own config, settings, and project data.

Migration compatibility: if ~/.openclaude does not yet exist but ~/.claude
does, the legacy path is kept automatically so existing openclaude users
don't lose their data on upgrade. New installs go straight to ~/.openclaude.

Users who need an explicit path can set CLAUDE_CONFIG_DIR.

Fixes #184

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 23:50:36 +08:00
KRATOS
8ce09ae743 fix: disable cache_control injection for third-party providers (#276)
getPromptCachingEnabled() returned true for all providers including
Azure Foundry, OpenAI, Gemini, and GitHub. This caused cache_control
blocks to be injected into every request sent to 3P endpoints.

Azure Foundry's strict Responsible AI content filter treats unexpected
Anthropic-specific fields (cache_control: { type: "ephemeral" }) as
a jailbreak signal and rejects the request with a 400 error — even
for a simple prompt like "hi".

Fix: return false early when provider is not firstParty, bedrock, or
vertex — the only providers that understand and support prompt caching.

Fixes #273
Related: #267 (Finding 1)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 22:53:14 +08:00
Vasanth T
931ee96f5a security: address remaining code scanning alerts (#253) 2026-04-03 22:46:53 +08:00
KRATOS
c1e5e363cd fix: resolve keyboard freeze via sync render path and stable useAppState selectors (#266)
* fix: resolve keyboard freeze via sync render path and stable useAppState selectors

Two compounding React 19 defects caused keyboard input to freeze after
MCP notifications or rapid state updates:

Defect 2 (ink.tsx): The render() path used async updateContainer, which
batches updates across scheduler ticks. Keyboard events dispatched mid-render
drained faster than React processed them, causing input to appear frozen.
Fixed by switching to updateContainerSync + flushSyncWork (same pattern
already used in the unmount path).

Defect 4 (AppState.tsx): useAppState and useAppStateMaybeOutsideOfProvider
used React Compiler _c cache invalidation tied to selector identity. Inline
arrow selectors (new reference each render) invalidated the cache every cycle,
producing a new `get` function. useSyncExternalStore treats a new `get` as a
tearing signal, re-syncing state and re-rendering — causing a loop that
starved the input handler. Fixed with useRef + useCallback(fn, []) to give
useSyncExternalStore a permanently stable snapshot reference.

Note: AppState.tsx is React Compiler output. The _c bypass for these two
hooks is intentional — compiler cache invalidation on inline selectors is
the root cause of the tearing loop.

All 200 tests pass. Build and smoke test verified.

Closes #77, #220, #228

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: update selector refs during render instead of useLayoutEffect

Addresses review feedback on PR #266. The previous useLayoutEffect approach
updated selectorRef.current after the render phase, meaning a changed selector
(e.g. s => s.tasks[attachment.taskId] when taskId changes) would still read
stale data during the render it changed in.

Fix: assign selectorRef.current and storeRef.current directly during render
before useSyncExternalStore calls get(). Ref mutation during render is safe
here — it's synchronous and happens before the snapshot is read. get()
identity stays stable via useCallback(fn, []) so useSyncExternalStore never
sees a new subscription function and won't trigger re-render loops.

This is the standard pattern used by zustand and jotai for selector stability.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 22:33:16 +08:00
sooth
b0d796e5c3 fix: harden resume after compaction failures (#195)
* fix: harden resume after compaction failures

* test: cover resume compaction safeguards

* fix: address resume safeguard review findings
2026-04-03 22:31:06 +08:00
Vasanth T
6987a54a71 feat(vscode): redesign control center (#236)
* feat(vscode): redesign control center

* fix(vscode): keep launch target messaging honest
2026-04-03 21:58:25 +08:00
JasonVon
fb32e3f829 feat: per-agent model routing — route different agents to different providers (#238)
* feat: add agentModels and agentRouting to SettingsSchema

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add agentRouting module for per-agent provider resolution

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: thread providerOverride through OpenAI shim for per-agent routing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: getAnthropicClient accepts providerOverride for agent routing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: thread providerOverride through Options and queryModel calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: thread providerOverride through query loop and ToolUseContext

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: resolve agent routing in runAgent and inject providerOverride

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add Agent Routing configuration guide to README

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add unit tests for resolveAgentProvider + plaintext api_key note

- 15 tests covering priority chain (name > subagentType > default > null)
- normalize() case-insensitive and hyphen/underscore equivalence
- Edge cases: null settings, missing config sections, non-existent model
- README note about api_key stored in plaintext

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* security: address code review — SSRF, credential leak, key collision

- base_url schema now uses z.string().url() for SSRF mitigation
- Strip auth headers (Authorization, x-api-key, api-key) from
  defaultHeaders when providerOverride is active, preventing
  Anthropic credentials from leaking to third-party endpoints
- Warn on duplicate normalized routing keys to prevent silent shadowing
- providerOverride.apiKey is never logged (verified via grep)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: 冯俊辉 <fengjunhui@shiyanjia.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 21:47:26 +08:00
Vasanth T
59ab2701f7 docs: add community standard files (#257) 2026-04-03 18:58:59 +05:30
Seunghyeok Lee
7668abaed0 restore public type shims (#109)
Co-authored-by: Seunghyeok Lee <seunghyeok2915@users.noreply.github.com>
2026-04-03 21:20:37 +08:00
Shivam singh
36d1c45954 fix(retry): prevent retries on quota-exhausted 429 errors (#249)
* fix(retry): prevent retries on quota-exhausted 429 errors

- Detect hard quota exhaustion (e.g. 'limit: 0')
- Short-circuit retry loop with CannotRetryError
- Align shouldRetry() to avoid inconsistent behavior

This prevents unnecessary retries and improves error clarity.

* fix(retry): prevent retries on quota-exhausted 429 errors

* fix(retry): prevent retries on quota-exhausted 429 errors
2026-04-03 21:20:17 +08:00
Anandan
116cc8e6bd Route third-party first-run setup into the provider wizard (#261)
The login picker previously sent third-party users to a dead-end info screen
that only mentioned env vars. This change reuses the existing provider wizard
from the login flow so first-run setup can continue without requiring slash
command access first.

Constraint: The existing provider setup logic must remain the single source of truth
Rejected: Build a separate third-party auth wizard in ConsoleOAuthFlow | would duplicate provider setup behavior and drift over time
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep third-party onboarding routed through ProviderWizard unless the provider command flow is intentionally redesigned
Tested: bun test src/components/ConsoleOAuthFlow.test.tsx src/commands/provider/provider.test.tsx
Tested: tsc --noEmit via project diagnostics
Not-tested: Live gh-authenticated push and PR creation path

Co-authored-by: anandh8x <test@example.com>
2026-04-03 21:18:00 +08:00
KRATOS
19c00e67ed feat: expose flicker-free mode as a /config toggle (closes #260) (#265)
Add flickerFreeMode to GlobalConfig so external users can enable
fullscreen alt-screen mode via /config instead of having to set
the CLAUDE_CODE_NO_FLICKER=1 env var manually.

Priority order in isFullscreenEnvEnabled():
  CLAUDE_CODE_NO_FLICKER=0  → always off (env wins)
  CLAUDE_CODE_NO_FLICKER=1  → always on (env wins)
  tmux -CC detected         → off (terminal safety guard)
  config flickerFreeMode    → user preference (new)
  USER_TYPE=ant             → internal default

The env var still takes full precedence so existing scripts and
automation are unaffected. The new setting only activates when
flickerFreeMode is explicitly set in config.
2026-04-03 21:17:38 +08:00
Vasanth T
7c0ea68b65 fix: address code scanning alerts (#240) 2026-04-03 14:52:35 +05:30
KRATOS
f3a984dde1 fix(security-review): Handle null shell output (#231)
Normalize shell command stdout and stderr before the prompt-shell path and shared tool-result mappers use string operations. This prevents /security-review from crashing when a shell tool returns null output fields and adds regression coverage for both direct mapper calls and prompt generation.

Fixes #165

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-03 10:14:28 +02:00
Brendan
72c6e97094 fix: route ask-user-question footer actions through useInput (#229) 2026-04-03 10:14:17 +02:00
Preetham
f3ab727ec2 Added LM Studio provider setup guide (#227)
* ## PR: Add LM Studio Provider Support

### Summary
Adds comprehensive LM Studio integration to openclaude, following the same pattern as the existing Ollama provider. LM Studio is a popular local LLM inference tool that exposes an OpenAI-compatible API.

### Changes (4 files, 672 insertions)

**New Files:**
- `lmstudio_provider.py` (377 lines) - Full provider implementation with:
  - Health check functions (`check_lmstudio_running`)
  - Model listing (`list_lmstudio_models`)
  - Chat completion (`lmstudio_chat`)
  - Streaming support (`lmstudio_chat_stream`)
  - Comprehensive docstring with setup instructions, troubleshooting, and model recommendations

- `test_lmstudio_provider.py` (227 lines) - Complete test suite with 12 passing tests covering:
  - API URL construction
  - Server health checks
  - Model listing
  - Chat completion functionality

**Modified Files:**
- `docs/quick-start-mac-linux.md` (+34 lines) - Added Option D: LM Studio with setup instructions and troubleshooting
- `docs/quick-start-windows.md` (+34 lines) - Added Option D: LM Studio with PowerShell syntax and troubleshooting

### Key Features
- No API key required (local inference)
- Default port: 1234 (LM Studio's standard)
- OpenAI-compatible API integration
- Consistent with existing provider patterns (Ollama, Atomic Chat)
- All tests passing (12/12)

### Usage
```bash
export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_BASE_URL=http://localhost:1234/v1
export OPENAI_MODEL=your-model-name
openclaude
```

* made pr as doc only pr for lm studio

* LM studio recent ui changes fixes in doc
2026-04-03 12:45:57 +08:00
Vasanth T
29edece72f docs: refresh repository README (#226) 2026-04-03 11:39:26 +08:00
Vasanth T
6181050811 chore: patch dependabot vulnerabilities (#225) 2026-04-03 11:34:09 +08:00
Adam Solomon
0fd0026a76 feat: (Extension of #175) added cross-platform system-wide environment variable setup guide for all providers (#185)
* added Instructions to env example to allow openclaude to be used system wide

* added suggested .env.example changes

I added the suggested .env.example changes suggested earlier within the pr thread
2026-04-03 11:27:14 +08:00
KRATOS
6919d774f2 fix: custom OPENAI_BASE_URL always wins over Codex model alias detection (#222)
* feat: add --provider CLI flag for multi-provider support

Adds a --provider flag that maps friendly provider names to the
environment variables the codebase uses for provider detection.
No more manual env-var configuration — users can now simply run:

  openclaude --provider openai --model gpt-4o
  openclaude --provider gemini --model gemini-2.0-flash
  openclaude --provider ollama --model llama3.2
  openclaude --provider bedrock
  openclaude --provider vertex

Implementation details:
- providerFlag.ts: core logic — maps provider names to env vars,
  uses ??= so explicit env vars always win over the flag defaults
- providerFlag.test.ts: 18 tests covering all 7 providers,
  error messages, model passthrough, and env-var precedence
- cli.tsx: early fast-path (mirrors --bare pattern) — sets env
  vars before Commander option-building and module constants run
- main.tsx: adds --provider to Commander option chain for --help

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: custom OPENAI_BASE_URL always wins over Codex model alias detection

When OPENAI_MODEL=gpt-5.4 (or gpt-5.4-mini) and a custom OPENAI_BASE_URL
is set (Azure, OpenRouter, etc), the transport was incorrectly forced to
codex_responses because gpt-5.4 is in CODEX_ALIAS_MODELS. This caused
requests to be sent with Codex auth instead of the user's API key,
resulting in 401 Unauthorized errors.

Fix: only use codex_responses when the base URL is explicitly the Codex
endpoint, OR when no custom base URL is set and the model is a Codex
alias. An explicit OPENAI_BASE_URL always takes priority over model-name
based Codex detection.

Verified locally: gpt-5.4 via OpenRouter now correctly shows
Provider=OpenRouter, Endpoint=https://openrouter.ai/api/v1 instead of
routing to chatgpt.com/backend-api/codex.

Fixes #200, #203

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 11:11:10 +08:00
Vasanth T
aa69e85795 fix: correct prompt identity branding (#224) 2026-04-03 11:06:26 +08:00
Kevin Codex
66bbb75836 Merge pull request #221 from gnanam1990/fix/keyboard-freeze-mcp-notifications
fix: prevent keyboard freeze when MCP notification effects fire
2026-04-03 10:27:11 +08:00
gnanam1990
2c6ec0119e fix: prevent keyboard freeze when MCP notification effects fire
React 19 requires `supportsMicrotasks: true` in the reconciler host
config so it can flush state updates from passive effects via
queueMicrotask. Without this, state updates triggered inside
useMcpConnectivityStatus were silently dropped, corrupting React's
internal executionContext and causing all keyboard input to freeze
after the "N MCP server(s) need auth" notification appeared.

Root cause (three-part fix):

1. reconciler.ts: declare supportsMicrotasks + scheduleMicrotask so
   React 19 schedules passive-effect flushes correctly.

2. useMcpConnectivityStatus.tsx: wrap the MCP auth notification effect
   in try/catch so any unexpected throw does not propagate into
   flushPassiveEffects and permanently corrupt executionContext.

3. notifications.tsx: wrap addNotification, removeNotification, and
   processQueue in try/catch for the same reason — these are called
   from 12+ notification hooks across passive effects.

Also fixes a pre-existing test isolation bug in context.test.ts where
assigning `undefined` to process.env produced the string "undefined",
polluting the env for subsequent test files.

Resolves: #169, #205, #77
2026-04-03 07:41:53 +05:30
Kevin Codex
74a25d01a6 Merge pull request #206 from alamnahin/feat/ollama-image-passthrough
feat(ollama): pass Anthropic base64 image blocks to Ollama images payload
2026-04-03 10:06:08 +08:00
Kevin Codex
7cf4c88ab8 docs: add security policy 2026-04-03 09:40:17 +08:00
Kevin Codex
f68b9aa57d Create SECURITY.md 2026-04-03 09:17:21 +08:00
Kevin Codex
20d1ee8427 Merge pull request #207 from alamnahin/feat/router-large-request-modeling
fix(router): use large message size when selecting models
2026-04-03 08:58:29 +08:00
Kevin Codex
089a42fc07 Merge pull request #211 from joetam/fix-image-paste-stubs
fix linux clipboard image paste for jpeg/gif/webp
2026-04-03 08:55:50 +08:00
jmt
f5b20fc517 fix: make clipboard images pasteable in OpenClaude
Images in the clipboard could fail to become pasted image attachments in OpenClaude. User-facing symptom: paste would detect that an image existed, but nothing would appear in the prompt, and bundled builds could also fail while converting BMP clipboard images into a format OpenClaude can send to the model.

Linux clipboard image paste had drifted between detection and extraction. checkImage accepted png/jpeg/jpg/gif/webp/bmp, but saveImage only tried image/png and image/bmp. When the clipboard advertised a JPEG, GIF, or WebP image, OpenClaude concluded that an image was present and then failed to write the temp screenshot file, so the paste path returned null and nothing was inserted into the prompt.

Bundled OpenClaude builds had a second failure mode. The build replaces image-processor-napi and sharp with explicit stub modules in bundled mode. getImageProcessor() treated those stubs as real processors, so BMP clipboard images reached sharp(imageBuffer).png() and then failed before they could be converted into a pasteable PNG for OpenClaude.

Keep the Linux clipboard commands generated from one MIME type list and reject __stub-marked image processors up front instead of failing in the middle of image paste.
2026-04-02 15:51:49 -07:00
Md.Nahin Alam
184ec250fd test(router): scope FAKE_KEY via pytest monkeypatch fixture 2026-04-03 04:18:20 +06:00
Md.Nahin Alam
43deb49c2c fix(router): use large request size for model selection 2026-04-03 03:45:33 +06:00
Md.Nahin Alam
0e7a2446c7 feat(ollama): pass base64 image blocks through to Ollama payload 2026-04-03 03:29:00 +06:00
Kevin Codex
63ad0196d6 Merge pull request #172 from devNull-bootloader/main
Add OpenClaude VS Code extension with terminal UI and control center
2026-04-03 02:25:03 +08:00
Kevin Codex
32046e9b40 Merge pull request #191 from BrainSlugs83/security/pin-firecrawl-js-dependency
security: pin @mendable/firecrawl-js to exact version
2026-04-03 02:19:17 +08:00
Mikey
7bd7d0f54d security: pin @mendable/firecrawl-js to exact version
Pins @mendable/firecrawl-js from ^4.18.1 to 4.18.1, consistent with
the pinning policy established in #102.
2026-04-02 11:07:54 -07:00
Kevin Codex
cdf4bad95b Merge pull request #182 from wrenchpilot/development
Enhance local provider URL validation for private IP addresses
2026-04-03 01:56:20 +08:00
Urvish Lanje
4158214895 Merge pull request #3 from devNull-bootloader/feat/initial-vscode-extension
fix: address review feedback for launcher behavior and links
2026-04-02 19:53:21 +02:00
Kevin Codex
a6ed57d0f4 Merge pull request #161 from auriti/fix/block-update-for-3p-providers
fix: block update command for 3P providers, align thinking block handling
2026-04-03 01:52:54 +08:00
James Shawn Carnley
7b68eb1acb Enhance local provider URL detection for IPv6 and loopback ranges 2026-04-02 13:46:10 -04:00
Kevin Codex
84950642ae Merge pull request #168 from firecrawl/add-firecrawl
feat: add Firecrawl backend for WebSearch and WebFetch
2026-04-03 01:43:25 +08:00
Kevin Codex
a287597273 Merge pull request #162 from auriti/fix/provider-aware-error-messages
fix: provider-aware error messages and skip Anthropic key approval for 3P
2026-04-03 01:42:15 +08:00
Kevin Codex
1cd4164062 Merge pull request #159 from Ghoul07-bit/main
Android Termux installation guide
2026-04-03 01:17:17 +08:00
Kevin Codex
47c53a18e8 Merge pull request #174 from gnanam1990/feat/provider-aware-rate-limit
feat: provider-aware rate limit reset delay for OpenAI/GitHub/Codex providers
2026-04-03 01:16:58 +08:00
Urvish Lanje
cf90457428 fix: address review feedback for launcher behavior and links
- point all repository links to Gitlawb/openclaude
- make shim opt-in by default to preserve Anthropic-first flow
- add command availability check with first-run install guidance
- render runtime and shim state dynamically in control center
- make command palette shortcut hint platform-aware
2026-04-02 17:14:42 +00:00
James Shawn Carnley
5e77d82620 Merge branch 'Gitlawb:main' into development 2026-04-02 12:55:59 -04:00
Kevin Codex
11d9660a80 Merge pull request #157 from erdemozyol/fix/status-tab-highlight
fix: refresh tab highlight on horizontal navigation
2026-04-03 00:55:33 +08:00
Kevin Codex
1a57335d74 Merge pull request #160 from auriti/fix/shim-ids-azure-safety
fix: crypto.randomUUID for IDs, Azure Foundry detection, safety filter visibility
2026-04-03 00:54:49 +08:00
Kevin Codex
7bc903d875 Merge pull request #156 from auriti/fix/model-lookup-and-llama-context
fix: deterministic prefix matching and correct Llama 3.x context windows
2026-04-03 00:53:42 +08:00
Kevin Codex
4c22de2585 Merge pull request #179 from gnanam1990/fix/gemini-routing
fix: route CLAUDE_CODE_USE_GEMINI through OpenAI-compatible shim
2026-04-03 00:50:21 +08:00
Leonardo Grigorio
63daf33b48 docs: add Firecrawl section to README 2026-04-02 13:47:59 -03:00
James Shawn Carnley
2ee43d7ee8 Merge branch 'Gitlawb:main' into development 2026-04-02 12:43:24 -04:00
Kevin Codex
3581d3f83f Merge pull request #142 from skfallin/fix/anthropic-schema-format
Strip incompatible JSON Schema keywords from tool schemas
2026-04-03 00:26:45 +08:00
James Shawn Carnley
4a4394bb65 feat: enhance local provider URL validation to include private IPv4 and IPv6 addresses 2026-04-02 12:26:23 -04:00
gnanam1990
b4aa27183d fix: route CLAUDE_CODE_USE_GEMINI through OpenAI-compatible shim
The Gemini provider uses Google's OpenAI-compatible endpoint
(generativelanguage.googleapis.com/v1beta/openai) but the client
routing condition in client.ts only checked CLAUDE_CODE_USE_OPENAI
and CLAUDE_CODE_USE_GITHUB — CLAUDE_CODE_USE_GEMINI was missing.

This caused every Gemini request to fall through to the Anthropic
client path. Since ANTHROPIC_API_KEY is not set when using Gemini,
the Anthropic SDK threw:

  "Could not resolve authentication method. Expected either apiKey
   or authToken to be set."

Fix: add CLAUDE_CODE_USE_GEMINI to the OpenAI shim routing condition
so Gemini requests correctly reach createOpenAIShimClient(), which
maps GEMINI_API_KEY → OPENAI_API_KEY and sets OPENAI_BASE_URL to
the Google endpoint.

Closes #176

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 21:51:26 +05:30
Kevin Codex
96b9e0235b Merge pull request #177 from gnanam1990/feat/env-example
feat: add .env.example with all provider configurations
2026-04-03 00:16:38 +08:00
gnanam1990
7095abb837 feat: add .env.example with all provider configurations
New contributors had to hunt through README and source files to find
required environment variables. This adds a single reference file at
repo root covering all supported providers with placeholder values,
inline comments, and sensible defaults.

Providers covered:
- Anthropic (default)
- OpenAI
- Google Gemini
- GitHub Models
- Ollama (local)
- AWS Bedrock
- Google Vertex AI

Also includes optional tuning vars: CLAUDE_CODE_MAX_RETRIES,
CLAUDE_CODE_UNATTENDED_RETRY, OPENCLAUDE_ENABLE_EXTENDED_KEYS,
OPENCLAUDE_DISABLE_CO_AUTHORED_BY, API_TIMEOUT_MS, CLAUDE_DEBUG.

Updated .gitignore to add !.env.example exception so the template
is not suppressed by the existing .env.* rule.

Closes #175

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 21:43:49 +05:30
gnanam1990
8501786852 feat: provider-aware rate limit reset delay
Previously getRateLimitResetDelayMs only read the Anthropic-specific
'anthropic-ratelimit-unified-reset' header (Unix timestamp), returning
null for every other provider. This meant OpenAI, GitHub, and Codex
users in persistent retry mode (CLAUDE_CODE_UNATTENDED_RETRY=1) always
fell back to dumb exponential backoff even when the server included an
exact reset time in the response headers.

This change makes the function provider-aware:

- firstParty (Anthropic): existing behaviour preserved — reads
  'anthropic-ratelimit-unified-reset' Unix timestamp
- openai / codex / github: reads 'x-ratelimit-reset-requests' and
  'x-ratelimit-reset-tokens' (OpenAI relative duration strings like
  "1s", "6m0s", "1h30m0s"), picks the larger of the two so retries
  don't fire before both token and request limits have reset
- bedrock / vertex / foundry / gemini: returns null (no standard
  reset header for these providers)

Adds parseOpenAIDuration() as an exported helper to convert OpenAI's
duration format into milliseconds.

16 new tests covering all provider paths and edge cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 21:30:05 +05:30
skfallin
37d4c21739 fix: make schema sanitization provider-specific 2026-04-02 17:57:42 +02:00
Urvish Lanje
a43023705b Merge pull request #2 from devNull-bootloader/feat/initial-vscode-extension
Initial VS Code Extension for OpenClaude
2026-04-02 17:54:40 +02:00
Kevin Codex
73db9b5fd3 Merge pull request #163 from erdemozyol/feat/codex-status-usage
Add Codex usage to /status
2026-04-02 23:54:07 +08:00
Urvish Lanje
2b5cf9f0c1 feat: initial VS Code extension for OpenClaude
Introduce OpenClaude as a first-class VS Code extension with:

- Built-in Control Center sidebar for seamless workflow integration
- Terminal-first design with authentic monospace UI and ASCII styling
- Quick-launch buttons for OpenClaude terminal, repository access, and command palette
- Status display showing runtime and OpenAI shim configuration
- Dark theme optimized for focus and extended development sessions
- Proper extension manifest with activation events and contribution points
- Debug configuration for local development

This extension provides developers with direct access to OpenClaude
without leaving VS Code, enabling a tighter integration with the editor.
2026-04-02 15:50:56 +00:00
Kevin Codex
4237a72b92 Merge pull request #170 from gnanam1990/fix/security-issue-42
security: fix 5 findings from issue #42 — env leak, ant gate, depth DoS, URL parse, CA cert
2026-04-02 23:38:53 +08:00
gnanam1990
942d09ca9c security: fix 5 findings from issue #42 — env leak, ant gate, depth DoS, URL parse, CA cert
Finding 1 [CRITICAL] — sessionRunner leaks full process.env to child
Extract buildChildEnv() with an explicit allowlist of safe OS/runtime vars.
Child process no longer inherits ANTHROPIC_API_KEY, OPENAI_API_KEY, DB
credentials, or any other secret present in the parent shell environment.
Only CLAUDE_CODE_* bridge vars, PATH, HOME, and standard OS env are passed.

Finding 2 [HIGH] — USER_TYPE=ant activatable by external users
Add isAntEmployee() -> false constant in src/utils/buildConfig.ts.
Replace all three direct process.env.USER_TYPE === 'ant' checks in
setup.ts and onChangeAppState.ts so no external user can activate
Anthropic-internal code paths (commit attribution, system prompt clearing,
dangerously-skip-permissions bypass) by setting USER_TYPE in their shell.

Finding 3 [HIGH] — memoryScan.ts unlimited directory walk
Add MAX_DEPTH=3 guard on readdir({ recursive: true }) results.
Deep or symlink-looped memory directories no longer cause an unbounded
blocking walk before the MAX_MEMORY_FILES cap takes effect.

Finding 5 [HIGH] — buildSdkUrl uses string.includes for protocol detection
Replace apiBaseUrl.includes('localhost') with new URL(apiBaseUrl).hostname
comparison so a remote URL containing 'localhost' in its path no longer
incorrectly gets ws:// (unencrypted) instead of wss://.

Finding 6 [HIGH] — upstream proxy writes unvalidated CA cert to disk
Add isValidPemContent() validation before writeFile in the CA cert download
path. A compromised proxy sending non-PEM data (HTML, JSON, scripts) is now
rejected before it can be appended to the system CA bundle.

Each fix is covered by new unit tests (25 tests across 5 new test files).
All 52 tests pass. Build verified clean on v0.1.7.

Fixes #42

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 21:04:10 +05:30
Leonardo Grigorio
ac4efae870 feat: add Firecrawl backend for WebSearch and WebFetch tools
WebSearch is currently disabled for all non-Anthropic providers (OpenAI
shim, DeepSeek, Ollama, etc.) because those providers have no native
search backend. This adds Firecrawl as a fallback that activates when
FIRECRAWL_API_KEY is set, unlocking web search for every model
openclaude supports.

WebFetch uses basic HTTP + Turndown for HTML-to-markdown conversion,
which fails silently on JS-rendered SPAs and bot-protected pages.
Firecrawl scrape replaces the fetch layer when FIRECRAWL_API_KEY is set,
returning clean markdown that handles dynamic content correctly.

Changes:
- WebSearchTool: add runFirecrawlSearch() using @mendable/firecrawl-js,
  respects allowed_domains (post-filter) and blocked_domains (-site: operators),
  includes result snippets alongside links. shouldUseFirecrawl() ensures
  firstParty/Vertex/Foundry/Codex providers keep their native backends.
- WebFetchTool: add scrapeWithFirecrawl(), drops into the existing
  applyPromptToMarkdown() pipeline so prompt processing is unchanged.
- Remove "Web search is only available in the US" restriction from
  prompt when Firecrawl is active (it works globally).
2026-04-02 12:18:20 -03:00
Urvish Lanje
4c6adf4774 Merge pull request #1 from devNull-bootloader/copilot/create-vscode-extension-openclaude
Add sleek terminal-style VS Code extension for OpenClaude
2026-04-02 17:13:02 +02:00
copilot-swe-agent[bot]
ff124dcdfb fix: use cryptographic nonce for extension webview CSP
Agent-Logs-Url: https://github.com/devNull-bootloader/openclaude/sessions/30a4694d-1125-4280-a593-74b5e3da601e

Co-authored-by: devNull-bootloader <189463177+devNull-bootloader@users.noreply.github.com>
2026-04-02 15:08:22 +00:00
copilot-swe-agent[bot]
8e8671fc51 feat: add visual OpenClaude control center UI in VS Code extension
Agent-Logs-Url: https://github.com/devNull-bootloader/openclaude/sessions/30a4694d-1125-4280-a593-74b5e3da601e

Co-authored-by: devNull-bootloader <189463177+devNull-bootloader@users.noreply.github.com>
2026-04-02 15:07:20 +00:00
Leonardo Grigorio
4c1ba35aa1 Revert "docs: add MCP servers guide with Firecrawl as featured example"
This reverts commit 5baee3b491.
2026-04-02 12:02:42 -03:00
Leonardo Grigorio
5baee3b491 docs: add MCP servers guide with Firecrawl as featured example
Adds docs/mcp-servers.md — the first documentation on how to configure
MCP servers in OpenClaude. Covers .mcp.json setup, the Firecrawl MCP
server for web scraping and search, available tools, and a pattern for
adding multiple servers.
2026-04-02 12:01:54 -03:00
copilot-swe-agent[bot]
43ba2cbfae feat: add VS Code extension with terminal launcher and custom theme
Agent-Logs-Url: https://github.com/devNull-bootloader/openclaude/sessions/5c0e9230-42be-4cce-a5d6-e85d665ea72a

Co-authored-by: devNull-bootloader <189463177+devNull-bootloader@users.noreply.github.com>
2026-04-02 14:58:36 +00:00
erdemozyol
5c25ac4e9a Add Codex usage to /status 2026-04-02 17:37:07 +03:00
erdemozyol
84ac06bac9 fix: show display version in status 2026-04-02 17:28:34 +03:00
Juan Camilo
c66b859342 fix: provider-aware error messages and skip Anthropic key approval for 3P
1. errors.ts: Add getCustomOffSwitchMessage() that returns a
   provider-neutral message for 3P users instead of the hardcoded
   "Opus is experiencing high load, please use /model to switch to
   Sonnet" which is misleading for OpenAI/Gemini/Ollama users.
   The original constant is preserved for backward-compatible string
   matching in error handlers.

2. Onboarding.tsx: Skip the "approve API key" step when a 3P provider
   is active. Previously, having ANTHROPIC_API_KEY in the environment
   (e.g., from a previous Anthropic setup) triggered an irrelevant
   Anthropic key approval UI even when using Gemini or OpenAI.
2026-04-02 16:23:12 +02:00
Juan Camilo
1709f5c098 fix: block update command for 3P providers, align thinking block handling
1. cli/update.ts: Block the update command for third-party providers.
   The update mechanism downloads from Anthropic's GCS bucket, which
   would silently replace the OpenClaude build (with the OpenAI shim)
   with the upstream Claude Code binary (without it). Now shows an
   actionable message directing users to rebuild from source.

2. codexShim.ts: Filter thinking blocks from assistant history, matching
   the openaiShim behavior. Without this, thinking blocks were included
   as plain text in assistant messages for the Codex transport but
   excluded for the OpenAI transport — causing inconsistent history
   when switching providers mid-session.
2026-04-02 16:18:10 +02:00
Juan Camilo
5d6443799a fix: crypto.randomUUID for IDs, Azure Foundry detection, safety filter visibility
Three targeted fixes:

1. Replace Math.random() with crypto.randomUUID() for message and tool
   call IDs in both openaiShim.ts and codexShim.ts. Math.random() is
   not cryptographically secure and predictable in seeded environments.

2. Anchor Azure endpoint detection to parsed hostname instead of raw
   URL regex. Adds support for Azure AI Foundry (services.ai.azure.com)
   alongside existing cognitiveservices and openai Azure endpoints.
   Prevents SSRF-style bypass via path segments.

3. Surface content safety filter blocks to the user. When Gemini or
   Azure returns finish_reason 'content_filter' or 'safety', emit a
   visible text block '[Content blocked by provider safety filter]'
   instead of silently returning empty/truncated content with
   stop_reason 'end_turn'. Applied to both streaming and non-streaming.
2026-04-02 16:14:35 +02:00
Ghoul07-bit
3ef09f911e Create ANDROID_INSTALL.md
Installation Guide to run OpenClaude on andriod
2026-04-02 15:10:20 +01:00
erdemozyol
6f4aa02123 fix: refresh tab highlight on horizontal navigation 2026-04-02 16:58:45 +03:00
Juan Camilo
b65921e8c3 fix: deterministic prefix matching and correct Llama 3.x context windows
Two fixes in openaiContextWindows.ts:

1. Sort lookup keys by length descending in lookupByModel() so the most
   specific prefix always wins. Without this, 'gpt-4-turbo-preview'
   could match 'gpt-4' (8k) instead of 'gpt-4-turbo' (128k) depending
   on V8's object key iteration order.

2. Update Llama 3.1/3.2/3.3 context windows from 8,192 to 128,000.
   These models support 128k context natively (Meta official specs).
   The previous 8k value was Ollama's default num_ctx, not the model's
   actual capability, causing premature auto-compact warnings.
2026-04-02 15:50:52 +02:00
skfallin
0fe8551d33 Merge branch 'main' into fix/anthropic-schema-format 2026-04-02 15:50:16 +02:00
skfallin
6319df02f0 Merge upstream/main into fix/anthropic-schema-format 2026-04-02 15:42:28 +02:00
skfallin
0c88dea247 Strip incompatible JSON Schema keywords from tool schemas 2026-04-02 13:50:47 +02:00
710 changed files with 10586 additions and 1963 deletions

250
.env.example Normal file
View File

@@ -0,0 +1,250 @@
# =============================================================================
# OpenClaude Environment Configuration
# =============================================================================
# Copy this file to .env and fill in your values:
# cp .env.example .env
#
# Only set the variables for the provider you want to use.
# All other sections can be left commented out.
# =============================================================================
# =============================================================================
# SYSTEM-WIDE SETUP (OPTIONAL)
# =============================================================================
# Instead of using a .env file per project, you can set these variables
# system-wide so OpenClaude works from any directory on your machine.
#
# STEP 1: Pick your provider variables from the list below.
# STEP 2: Set them using the method for your OS (see further down).
#
# ── Provider variables ───────────────────────────────────────────────
#
# Option 1 — Anthropic:
# ANTHROPIC_API_KEY=sk-ant-your-key-here
# ANTHROPIC_MODEL=claude-sonnet-4-5 (optional)
# ANTHROPIC_BASE_URL=https://api.anthropic.com (optional)
#
# Option 2 — OpenAI:
# CLAUDE_CODE_USE_OPENAI=1
# OPENAI_API_KEY=sk-your-key-here
# OPENAI_MODEL=gpt-4o
# OPENAI_BASE_URL=https://api.openai.com/v1 (optional)
#
# Option 3 — Google Gemini:
# CLAUDE_CODE_USE_GEMINI=1
# GEMINI_API_KEY=your-gemini-key-here
# GEMINI_MODEL=gemini-2.0-flash
# GEMINI_BASE_URL=https://generativelanguage.googleapis.com (optional)
#
# Option 4 — GitHub Models:
# CLAUDE_CODE_USE_GITHUB=1
# GITHUB_TOKEN=ghp_your-token-here
#
# Option 5 — Ollama (local):
# CLAUDE_CODE_USE_OPENAI=1
# OPENAI_BASE_URL=http://localhost:11434/v1
# OPENAI_API_KEY=ollama
# OPENAI_MODEL=llama3.2
#
# Option 6 — LM Studio (local):
# CLAUDE_CODE_USE_OPENAI=1
# OPENAI_BASE_URL=http://localhost:1234/v1
# OPENAI_MODEL=your-model-id-here
# OPENAI_API_KEY=lmstudio (optional)
#
# Option 7 — AWS Bedrock (may also need: aws configure):
# CLAUDE_CODE_USE_BEDROCK=1
# AWS_REGION=us-east-1
# AWS_DEFAULT_REGION=us-east-1
# AWS_BEARER_TOKEN_BEDROCK=your-bearer-token-here
# ANTHROPIC_BEDROCK_BASE_URL=https://bedrock-runtime.us-east-1.amazonaws.com
#
# Option 8 — Google Vertex AI:
# CLAUDE_CODE_USE_VERTEX=1
# ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id
# CLOUD_ML_REGION=us-east5
# GOOGLE_CLOUD_PROJECT=your-gcp-project-id
#
# ── How to set variables on each OS ──────────────────────────────────
#
# macOS (zsh):
# 1. Open: nano ~/.zshrc
# 2. Add each variable as: export VAR_NAME=value
# 3. Save and reload: source ~/.zshrc
#
# Linux (bash):
# 1. Open: nano ~/.bashrc
# 2. Add each variable as: export VAR_NAME=value
# 3. Save and reload: source ~/.bashrc
#
# Windows (PowerShell):
# Run for each variable:
# [System.Environment]::SetEnvironmentVariable('VAR_NAME', 'value', 'User')
# Then restart your terminal.
#
# Windows (Command Prompt):
# Run for each variable:
# setx VAR_NAME value
# Then restart your terminal.
#
# Windows (GUI):
# Settings > System > About > Advanced System Settings >
# Environment Variables > under "User variables" click New,
# then add each variable.
#
# ── Important notes ──────────────────────────────────────────────────
#
# LOCAL SERVERS: If using LM Studio or Ollama, the server MUST be
# running with a model loaded before you launch OpenClaude —
# otherwise you'll get connection errors.
#
# SWITCHING PROVIDERS: To temporarily switch, unset the relevant
# variables in your current terminal session:
#
# macOS / Linux:
# unset VAR_NAME
# # e.g.: unset CLAUDE_CODE_USE_OPENAI OPENAI_BASE_URL OPENAI_MODEL
#
# Windows (PowerShell — current session only):
# Remove-Item Env:VAR_NAME
#
# To permanently remove a variable on Windows:
# [System.Environment]::SetEnvironmentVariable('VAR_NAME', $null, 'User')
#
# LOAD ORDER:
# Shell and system environment variables are inherited by the process.
# Project .env files are only used if your launcher or shell loads them
# before starting OpenClaude.
# COMPATIBILITY:
# System-wide variables work regardless of how you run OpenClaude:
# npx, global npm install, bun run, or node directly. Any process
# launched from your terminal inherits your shell's environment.
#
# REMINDER: Make sure .env is in your .gitignore to avoid committing secrets.
# =============================================================================
# =============================================================================
# PROVIDER SELECTION — uncomment ONE block below
# =============================================================================
# -----------------------------------------------------------------------------
# Option 1: Anthropic (default — no provider flag needed)
# -----------------------------------------------------------------------------
ANTHROPIC_API_KEY=sk-ant-your-key-here
# Override the default model (optional)
# ANTHROPIC_MODEL=claude-sonnet-4-5
# Use a custom Anthropic-compatible endpoint (optional)
# ANTHROPIC_BASE_URL=https://api.anthropic.com
# -----------------------------------------------------------------------------
# Option 2: OpenAI
# -----------------------------------------------------------------------------
# CLAUDE_CODE_USE_OPENAI=1
# OPENAI_API_KEY=sk-your-key-here
# OPENAI_MODEL=gpt-4o
# Use a custom OpenAI-compatible endpoint (optional — defaults to api.openai.com)
# OPENAI_BASE_URL=https://api.openai.com/v1
# -----------------------------------------------------------------------------
# Option 3: Google Gemini
# -----------------------------------------------------------------------------
# CLAUDE_CODE_USE_GEMINI=1
# GEMINI_API_KEY=your-gemini-key-here
# GEMINI_MODEL=gemini-2.0-flash
# Use a custom Gemini endpoint (optional)
# GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
# -----------------------------------------------------------------------------
# Option 4: GitHub Models
# -----------------------------------------------------------------------------
# CLAUDE_CODE_USE_GITHUB=1
# GITHUB_TOKEN=ghp_your-token-here
# -----------------------------------------------------------------------------
# Option 5: Ollama (local models)
# -----------------------------------------------------------------------------
# CLAUDE_CODE_USE_OPENAI=1
# OPENAI_BASE_URL=http://localhost:11434/v1
# OPENAI_API_KEY=ollama
# OPENAI_MODEL=llama3.2
# -----------------------------------------------------------------------------
# Option 6: LM Studio (local models)
# -----------------------------------------------------------------------------
# LM Studio exposes an OpenAI-compatible API, so we use the OpenAI provider.
# Make sure LM Studio is running with the Developer server enabled
# (Developer tab > toggle server ON).
#
# Steps:
# 1. Download and install LM Studio from https://lmstudio.ai
# 2. Search for and download a model (e.g. any coding or instruct model)
# 3. Load the model and start the Developer server
# 4. Set OPENAI_MODEL to the model ID shown in LM Studio's Developer tab
#
# The default server URL is http://localhost:1234 — change the port below
# if you've configured a different one in LM Studio.
#
# OPENAI_API_KEY is optional — LM Studio runs locally and ignores it.
# Some clients require a non-empty value; if you get auth errors, set it
# to any dummy value (e.g. "lmstudio").
#
# CLAUDE_CODE_USE_OPENAI=1
# OPENAI_BASE_URL=http://localhost:1234/v1
# OPENAI_MODEL=your-model-id-here
# -----------------------------------------------------------------------------
# Option 7: AWS Bedrock
# -----------------------------------------------------------------------------
# You may also need AWS CLI credentials configured (run: aws configure)
# or have AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY set in your
# environment in addition to the variables below.
#
# CLAUDE_CODE_USE_BEDROCK=1
# AWS_REGION=us-east-1
# AWS_DEFAULT_REGION=us-east-1
# AWS_BEARER_TOKEN_BEDROCK=your-bearer-token-here
# ANTHROPIC_BEDROCK_BASE_URL=https://bedrock-runtime.us-east-1.amazonaws.com
# -----------------------------------------------------------------------------
# Option 8: Google Vertex AI
# -----------------------------------------------------------------------------
# CLAUDE_CODE_USE_VERTEX=1
# ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id
# CLOUD_ML_REGION=us-east5
# GOOGLE_CLOUD_PROJECT=your-gcp-project-id
# =============================================================================
# OPTIONAL TUNING
# =============================================================================
# Max number of API retries on failure (default: 10)
# CLAUDE_CODE_MAX_RETRIES=10
# Enable persistent retry mode for unattended/CI sessions
# Retries 429/529 indefinitely with smart backoff
# CLAUDE_CODE_UNATTENDED_RETRY=1
# Enable extended key reporting (Kitty keyboard protocol)
# Useful for iTerm2, WezTerm, Ghostty if modifier keys feel off
# OPENCLAUDE_ENABLE_EXTENDED_KEYS=1
# Disable "Co-authored-by" line in git commits made by OpenClaude
# OPENCLAUDE_DISABLE_CO_AUTHORED_BY=1
# Custom timeout for API requests in milliseconds (default: varies)
# API_TIMEOUT_MS=60000
# Enable debug logging
# CLAUDE_DEBUG=1

41
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,41 @@
---
name: Bug report
about: Report a reproducible problem in OpenClaude
title: ""
labels: ""
assignees: ""
---
## Summary
What is broken?
## Steps to Reproduce
1.
2.
3.
## Expected Behavior
What should have happened?
## Actual Behavior
What happened instead?
## Environment
- OpenClaude version:
- OS:
- Terminal:
- Provider:
- Model:
## Logs / Screenshots
Paste the exact error output or attach screenshots if useful.
## Additional Context
Anything else maintainers should know?

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: OpenClaude Discussions
url: https://github.com/Gitlawb/openclaude/discussions
about: Use Discussions for setup help, questions, ideas, and community conversation.

View File

@@ -0,0 +1,27 @@
---
name: Feature request
about: Suggest an improvement or new capability for OpenClaude
title: ""
labels: ""
assignees: ""
---
## Summary
What would you like OpenClaude to do?
## Problem
What problem does this solve for you?
## Proposed Direction
Describe the smallest useful version of the feature if possible.
## Alternatives Considered
What are you doing today instead?
## Additional Context
Examples, screenshots, related projects, or prior art.

21
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,21 @@
## Summary
- what changed
- why it changed
## Impact
- user-facing impact:
- developer/maintainer impact:
## Testing
- [ ] `bun run build`
- [ ] `bun run smoke`
- [ ] focused tests:
## Notes
- provider/model path tested:
- screenshots attached (if UI changed):
- follow-up work or known limitations:

View File

@@ -6,6 +6,9 @@ on:
branches:
- main
permissions:
contents: read
jobs:
smoke-and-tests:
runs-on: ubuntu-latest

1
.gitignore vendored
View File

@@ -3,5 +3,6 @@ dist/
*.tsbuildinfo
.env
.env.*
!.env.example
.openclaude-profile.json
reports/

162
ANDROID_INSTALL.md Normal file
View File

@@ -0,0 +1,162 @@
# OpenClaude on Android (Termux)
A complete guide to running OpenClaude on Android using Termux + proot Ubuntu.
---
## Prerequisites
- Android phone with ~700MB free storage
- [Termux](https://f-droid.org/en/packages/com.termux/) installed from **F-Droid** (not Play Store)
- An [OpenRouter](https://openrouter.ai) API key (free, no credit card required)
---
## Why This Setup?
OpenClaude requires [Bun](https://bun.sh) to build, and Bun does not support Android natively. The workaround is running a real Ubuntu environment inside Termux via `proot-distro`, where Bun's Linux binary works correctly.
---
## Installation
### Step 1 — Update Termux
```bash
pkg update && pkg upgrade
```
Press `N` or Enter for any config file conflict prompts.
### Step 2 — Install dependencies
```bash
pkg install nodejs-lts git proot-distro
```
Verify Node.js:
```bash
node --version # should be v20+
```
### Step 3 — Clone OpenClaude
```bash
git clone https://github.com/Gitlawb/openclaude.git
cd openclaude
npm install
npm link
```
### Step 4 — Install Ubuntu via proot
```bash
proot-distro install ubuntu
```
This downloads ~200400MB. Wait for it to complete.
### Step 5 — Install Bun inside Ubuntu
```bash
proot-distro login ubuntu
curl -fsSL https://bun.sh/install | bash
source ~/.bashrc
bun --version # should show 1.3.11+
```
### Step 6 — Build OpenClaude
```bash
cd /data/data/com.termux/files/home/openclaude
bun run build
```
You should see:
```
✓ Built openclaude v0.1.6 → dist/cli.mjs
```
### Step 7 — Save env vars permanently
Still inside Ubuntu, add your OpenRouter config to `.bashrc`:
```bash
echo 'export CLAUDE_CODE_USE_OPENAI=1' >> ~/.bashrc
echo 'export OPENAI_API_KEY=your_openrouter_key_here' >> ~/.bashrc
echo 'export OPENAI_BASE_URL=https://openrouter.ai/api/v1' >> ~/.bashrc
echo 'export OPENAI_MODEL=qwen/qwen3.6-plus-preview:free' >> ~/.bashrc
source ~/.bashrc
```
Replace `your_openrouter_key_here` with your actual key from [openrouter.ai/keys](https://openrouter.ai/keys).
### Step 8 — Run OpenClaude
```bash
node dist/cli.mjs
```
Select **3** (3rd-party platform) at the login screen. Your env vars will be detected automatically.
---
## Restarting After Closing Termux
Every time you reopen Termux after killing it, run:
```bash
proot-distro login ubuntu
cd /data/data/com.termux/files/home/openclaude
node dist/cli.mjs
```
---
## Recommended Free Model
**`qwen/qwen3.6-plus-preview:free`** — Best free model on OpenRouter as of April 2026.
- 1M token context window
- Beats Claude 4.5 Opus on Terminal-Bench 2.0 agentic coding (61.6 vs 59.3)
- Built-in chain-of-thought reasoning
- Native tool use and function calling
- $0/M tokens (preview period)
> ⚠️ Free status may change when the preview period ends. Check [openrouter.ai](https://openrouter.ai/qwen/qwen3.6-plus-preview:free) for current pricing.
---
## Alternative Free Models (OpenRouter)
| Model ID | Context | Notes |
|---|---|---|
| `qwen/qwen3-coder:free` | 262K | Best for pure coding tasks |
| `openai/gpt-oss-120b:free` | 131K | OpenAI open model, strong tool calling |
| `nvidia/nemotron-3-super-120b-a12b:free` | 262K | Hybrid MoE, good general use |
| `meta-llama/llama-3.3-70b-instruct:free` | 66K | Reliable, widely tested |
Switch models anytime:
```bash
export OPENAI_MODEL=qwen/qwen3-coder:free
node dist/cli.mjs
```
---
## Why Not Groq or Cerebras?
Both were tested and fail due to OpenClaude's large system prompt (~50K tokens):
- **Groq free tier**: TPM limits too low (6K12K tokens/min)
- **Cerebras free tier**: TPM limits exceeded, even on `llama3.1-8b`
OpenRouter free models have no TPM restrictions — only 20 req/min and 200 req/day.
---
## Tips
- **Don't swipe Termux away** from recent apps mid-session — use the home button to minimize instead.
- The Ubuntu environment persists between Termux sessions; your build and config are saved.
- Run `bun run build` again only if you pull updates to the OpenClaude repo.

126
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,126 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and maintainers pledge to make participation in
our community a harassment-free experience for everyone, regardless of age,
body size, visible or invisible disability, ethnicity, sex characteristics,
gender identity and expression, level of experience, education, socio-economic
status, nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for
moderation decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official email address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the project maintainers through the repository maintainers or
security/community contact paths available in the repository.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/),
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq).

119
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,119 @@
# Contributing to OpenClaude
Thanks for contributing.
OpenClaude is a fast-moving open-source coding-agent CLI with support for multiple providers, local backends, MCP, and a terminal-first workflow. The best contributions here are focused, well-tested, and easy to review.
## Before You Start
- Search existing [issues](https://github.com/Gitlawb/openclaude/issues) and [discussions](https://github.com/Gitlawb/openclaude/discussions) before opening a new thread.
- Use issues for confirmed bugs and actionable feature work.
- Use discussions for setup help, ideas, and general community conversation.
- For larger changes, open an issue first so the scope is clear before implementation.
- For security reports, follow [SECURITY.md](SECURITY.md).
## Local Setup
Install dependencies:
```bash
bun install
```
Build the CLI:
```bash
bun run build
```
Smoke test:
```bash
bun run smoke
```
Run the app locally:
```bash
bun run dev
```
If you are working on provider setup or saved profiles, useful commands include:
```bash
bun run profile:init
bun run dev:profile
```
## Development Workflow
- Keep PRs focused on one problem or feature.
- Avoid mixing unrelated cleanup into the same change.
- Preserve existing repo patterns unless the change is intentionally refactoring them.
- Add or update tests when the change affects behavior.
- Update docs when setup, commands, or user-facing behavior changes.
## Validation
At minimum, run the most relevant checks for your change.
Common checks:
```bash
bun run build
bun run smoke
```
Focused tests:
```bash
bun test ./path/to/test-file.test.ts
```
When working on provider/runtime setup, this can also help:
```bash
bun run doctor:runtime
```
## Pull Requests
Good PRs usually include:
- a short explanation of what changed
- why it changed
- the user or developer impact
- the exact checks you ran
If the PR touches UI, terminal presentation, or the VS Code extension, include screenshots when useful.
If the PR changes provider behavior, mention which provider path was tested.
## Code Style
- Follow the existing code style in the touched files.
- Prefer small, readable changes over broad rewrites.
- Do not reformat unrelated files just because they are nearby.
- Keep comments useful and concise.
## Provider Changes
OpenClaude supports multiple provider paths. If you change provider logic:
- be explicit about which providers are affected
- avoid breaking third-party providers while fixing first-party behavior
- test the exact provider/model path you changed when possible
- call out any limitations or follow-up work in the PR description
## Community
Please be respectful and constructive with other contributors.
Maintainers may ask for:
- narrower scope
- focused follow-up PRs
- stronger validation
- docs updates for behavior changes
That is normal and helps keep the project reviewable as it grows.

29
LICENSE Normal file
View File

@@ -0,0 +1,29 @@
NOTICE
This repository contains code derived from Anthropic's Claude Code CLI.
The original Claude Code source is proprietary software:
Copyright (c) Anthropic PBC. All rights reserved.
Subject to Anthropic's Commercial Terms of Service.
Modifications and additions by OpenClaude contributors are offered under
the MIT License where legally permissible:
MIT License
Copyright (c) 2026 OpenClaude contributors (modifications only)
Permission is hereby granted, free of charge, to any person obtaining
a copy of the modifications made by OpenClaude contributors, to deal
in those modifications without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the modifications.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
The underlying derived code remains subject to Anthropic's copyright.
This project does not have Anthropic's authorization to distribute
their proprietary source. Users and contributors should evaluate their
own legal position.

329
README.md
View File

@@ -1,64 +1,60 @@
# OpenClaude
Use Claude Code with **any LLM** — not just Claude.
OpenClaude is an open-source coding-agent CLI that works with more than one model provider.
OpenClaude is a fork of the [Claude Code source leak](https://gitlawb.com/node/repos/z6MkgKkb/instructkr-claude-code) (exposed via npm source maps on March 31, 2026). We added an OpenAI-compatible provider shim so you can plug in GPT-4o, DeepSeek, Gemini, Llama, Mistral, or any model that speaks the OpenAI chat completions API. It now also supports the ChatGPT Codex backend for `codexplan` and `codexspark`, and local inference via [Atomic Chat](https://atomic.chat/) on Apple Silicon.
Use OpenAI-compatible APIs, Gemini, GitHub Models, Codex, Ollama, Atomic Chat, and other supported backends while keeping the same terminal-first workflow: prompts, tools, agents, MCP, slash commands, and streaming output.
All of Claude Code's tools work — bash, file read/write/edit, grep, glob, agents, tasks, MCP — just powered by whatever model you choose.
## Why OpenClaude
- Use one CLI across cloud and local model providers
- Save provider profiles inside the app with `/provider`
- Run locally with Ollama or Atomic Chat
- Keep core coding-agent workflows: bash, file tools, grep, glob, agents, tasks, MCP, and web tools
## Provenance & Legal Notice
OpenClaude is derived from Anthropic's Claude Code CLI source code, which was
inadvertently exposed in March 2026 through a packaging error in npm. The
original Claude Code source is proprietary software owned by Anthropic PBC.
This project adds multi-provider support, strips telemetry, and adapts the
codebase for open use. It is not an authorized fork or open-source release
by Anthropic.
**"Claude" and "Claude Code" are trademarks of Anthropic PBC.**
Contributors should be aware that the legal status of distributing code
derived from Anthropic's proprietary source is unresolved. See the LICENSE
file for details.
---
## Start Here
## Quick Start
If you are new to terminals or just want the easiest path, start with the beginner guides:
- [Non-Technical Setup](docs/non-technical-setup.md)
- [Windows Quick Start](docs/quick-start-windows.md)
- [macOS / Linux Quick Start](docs/quick-start-mac-linux.md)
If you want source builds, Bun workflows, profile launchers, or full provider examples, use:
- [Advanced Setup](docs/advanced-setup.md)
---
## Beginner Install
For most users, install the npm package:
### Install
```bash
npm install -g @gitlawb/openclaude
```
The package name is `@gitlawb/openclaude`, but the command you run is:
If the npm install path later reports `ripgrep not found`, install ripgrep system-wide and confirm `rg --version` works in the same terminal before starting OpenClaude.
### Start
```bash
openclaude
```
If you install via npm and later see `ripgrep not found`, install ripgrep system-wide and confirm `rg --version` works in the same terminal before starting OpenClaude.
Inside OpenClaude:
---
- run `/provider` for guided setup of OpenAI-compatible, Gemini, Ollama, or Codex profiles
- run `/onboard-github` for GitHub Models setup
## Fastest Setup
### Fastest OpenAI setup
### Windows PowerShell
```powershell
npm install -g @gitlawb/openclaude
$env:CLAUDE_CODE_USE_OPENAI="1"
$env:OPENAI_API_KEY="sk-your-key-here"
$env:OPENAI_MODEL="gpt-4o"
openclaude
```
### macOS / Linux
macOS / Linux:
```bash
npm install -g @gitlawb/openclaude
export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_API_KEY=sk-your-key-here
export OPENAI_MODEL=gpt-4o
@@ -66,135 +62,216 @@ export OPENAI_MODEL=gpt-4o
openclaude
```
That is enough to start with OpenAI.
Windows PowerShell:
```powershell
$env:CLAUDE_CODE_USE_OPENAI="1"
$env:OPENAI_API_KEY="sk-your-key-here"
$env:OPENAI_MODEL="gpt-4o"
openclaude
```
### Fastest local Ollama setup
macOS / Linux:
```bash
export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_BASE_URL=http://localhost:11434/v1
export OPENAI_MODEL=qwen2.5-coder:7b
openclaude
```
Windows PowerShell:
```powershell
$env:CLAUDE_CODE_USE_OPENAI="1"
$env:OPENAI_BASE_URL="http://localhost:11434/v1"
$env:OPENAI_MODEL="qwen2.5-coder:7b"
openclaude
```
---
## Choose Your Guide
## Setup Guides
### Beginner
Beginner-friendly guides:
- Want the easiest setup with copy-paste steps: [Non-Technical Setup](docs/non-technical-setup.md)
- On Windows: [Windows Quick Start](docs/quick-start-windows.md)
- On macOS or Linux: [macOS / Linux Quick Start](docs/quick-start-mac-linux.md)
- [Non-Technical Setup](docs/non-technical-setup.md)
- [Windows Quick Start](docs/quick-start-windows.md)
- [macOS / Linux Quick Start](docs/quick-start-mac-linux.md)
### Advanced
Advanced and source-build guides:
- Want source builds, Bun, local profiles, runtime checks, or more provider choices: [Advanced Setup](docs/advanced-setup.md)
- [Advanced Setup](docs/advanced-setup.md)
- [Android Install](ANDROID_INSTALL.md)
---
## Common Beginner Choices
## Supported Providers
### OpenAI
Best default if you already have an OpenAI API key.
### Ollama
Best if you want to run models locally on your own machine.
### Codex
Best if you already use the Codex CLI or ChatGPT Codex backend.
### Atomic Chat
Best if you want local inference on Apple Silicon with Atomic Chat. See [Advanced Setup](docs/advanced-setup.md).
| Provider | Setup Path | Notes |
| --- | --- | --- |
| OpenAI-compatible | `/provider` or env vars | Works with OpenAI, OpenRouter, DeepSeek, Groq, Mistral, LM Studio, and compatible local `/v1` servers |
| Gemini | `/provider` or env vars | Google Gemini support through the runtime provider layer |
| GitHub Models | `/onboard-github` | Interactive onboarding with saved credentials |
| Codex | `/provider` | Uses existing Codex credentials when available |
| Ollama | `/provider` or env vars | Local inference with no API key |
| Atomic Chat | advanced setup | Local Apple Silicon backend |
| Bedrock / Vertex / Foundry | env vars | Additional provider integrations for supported environments |
---
## What Works
- **All tools**: Bash, FileRead, FileWrite, FileEdit, Glob, Grep, WebFetch, WebSearch, Agent, MCP, LSP, NotebookEdit, Tasks
- **Streaming**: Real-time token streaming
- **Tool calling**: Multi-step tool chains (the model calls tools, gets results, continues)
- **Images**: Base64 and URL images passed to vision models
- **Slash commands**: /commit, /review, /compact, /diff, /doctor, etc.
- **Sub-agents**: AgentTool spawns sub-agents using the same provider
- **Memory**: Persistent memory system
## What's Different
- **No thinking mode**: Anthropic's extended thinking is disabled (OpenAI models use different reasoning)
- **No prompt caching**: Anthropic-specific cache headers are skipped
- **No beta features**: Anthropic-specific beta headers are ignored
- **Token limits**: Defaults to 32K max output — some models may cap lower, which is handled gracefully
- Tool-driven coding workflows
Bash, file read/write/edit, grep, glob, agents, tasks, MCP, and slash commands
- Streaming responses
Real-time token output and tool progress
- Tool calling
Multi-step tool loops with model calls, tool execution, and follow-up responses
- Images
URL and base64 image inputs for providers that support vision
- Provider profiles
Guided setup plus saved `.openclaude-profile.json` support
- Local and remote model backends
Cloud APIs, local servers, and Apple Silicon local inference
---
## How It Works
## Provider Notes
The shim (`src/services/api/openaiShim.ts`) sits between Claude Code and the LLM API:
OpenClaude supports multiple providers, but behavior is not identical across all of them.
```
Claude Code Tool System
|
v
Anthropic SDK interface (duck-typed)
|
v
openaiShim.ts <-- translates formats
|
v
OpenAI Chat Completions API
|
v
Any compatible model
```
- Anthropic-specific features may not exist on other providers
- Tool quality depends heavily on the selected model
- Smaller local models can struggle with long multi-step tool flows
- Some providers impose lower output caps than the CLI defaults, and OpenClaude adapts where possible
It translates:
- Anthropic message blocks → OpenAI messages
- Anthropic tool_use/tool_result → OpenAI function calls
- OpenAI SSE streaming → Anthropic stream events
- Anthropic system prompt arrays → OpenAI system messages
The rest of Claude Code doesn't know it's talking to a different model.
For best results, use models with strong tool/function calling support.
---
## Model Quality Notes
## Agent Routing
Not all models are equal at agentic tool use. Here's a rough guide:
Route different agents to different AI providers within the same session. Useful for cost optimization (cheap model for code review, powerful model for complex coding) or leveraging model strengths.
| Model | Tool Calling | Code Quality | Speed |
|-------|-------------|-------------|-------|
| GPT-4o | Excellent | Excellent | Fast |
| DeepSeek-V3 | Great | Great | Fast |
| Gemini 2.0 Flash | Great | Good | Very Fast |
| Llama 3.3 70B | Good | Good | Medium |
| Mistral Large | Good | Good | Fast |
| GPT-4o-mini | Good | Good | Very Fast |
| Qwen 2.5 72B | Good | Good | Medium |
| Smaller models (<7B) | Limited | Limited | Very Fast |
### Configuration
For best results, use models with strong function/tool calling support.
Add to `~/.claude/settings.json`:
```json
{
"agentModels": {
"deepseek-chat": {
"base_url": "https://api.deepseek.com/v1",
"api_key": "sk-your-key"
},
"gpt-4o": {
"base_url": "https://api.openai.com/v1",
"api_key": "sk-your-key"
}
},
"agentRouting": {
"Explore": "deepseek-chat",
"Plan": "gpt-4o",
"general-purpose": "gpt-4o",
"frontend-dev": "deepseek-chat",
"default": "gpt-4o"
}
}
```
### How It Works
- **agentModels**: Maps model names to OpenAI-compatible API endpoints
- **agentRouting**: Maps agent types or team member names to model names
- **Priority**: `name` > `subagent_type` > `"default"` > global provider
- **Matching**: Case-insensitive, hyphen/underscore equivalent (`general-purpose` = `general_purpose`)
- **Teams**: Team members are routed by their `name` — no extra config needed
When no routing match is found, the global provider (env vars) is used as fallback.
> **Note:** `api_key` values in `settings.json` are stored in plaintext. Keep this file private and do not commit it to version control.
---
## Files Changed from Original
## Web Search and Fetch
```
src/services/api/openaiShim.ts — NEW: OpenAI-compatible API shim (724 lines)
src/services/api/client.ts — Routes to shim when CLAUDE_CODE_USE_OPENAI=1
src/utils/model/providers.ts — Added 'openai' provider type
src/utils/model/configs.ts — Added openai model mappings
src/utils/model/model.ts — Respects OPENAI_MODEL for defaults
src/utils/auth.ts — Recognizes OpenAI as valid 3P provider
By default, `WebSearch` now works on non-Anthropic models using DuckDuckGo. This gives GPT-4o, DeepSeek, Gemini, Ollama, and other OpenAI-compatible providers a free web search path out of the box.
>**Note:** DuckDuckGo fallback works by scraping search results and may be rate-limited, blocked, or subject to DuckDuckGo's Terms of Service. If you want a more reliable supported option, configure Firecrawl.
For Anthropic-native backends (Anthropic/Vertex/Foundry) and Codex responses, OpenClaude keeps the native provider web search behavior.
`WebFetch` works but uses basic HTTP plus HTML-to-markdown conversion. That fails on JavaScript-rendered pages (React, Next.js, Vue SPAs) and sites that block plain HTTP requests.
Set a [Firecrawl](https://firecrawl.dev) API key if you want Firecrawl-powered search/fetch behavior:
```bash
export FIRECRAWL_API_KEY=your-key-here
```
6 files changed. 786 lines added. Zero dependencies added.
With Firecrawl enabled:
- `WebSearch` can use Firecrawl's search API (while DuckDuckGo remains the default free path for non-Claude models)
- `WebFetch` uses Firecrawl's scrape endpoint instead of raw HTTP, handling JS-rendered pages correctly
Free tier at [firecrawl.dev](https://firecrawl.dev) includes 500 credits. The key is optional.
---
## Origin
## Source Build
This is a fork of [instructkr/claude-code](https://gitlawb.com/node/repos/z6MkgKkb/instructkr-claude-code), which mirrored the Claude Code source snapshot that became publicly accessible through an npm source map exposure on March 31, 2026.
```bash
bun install
bun run build
node dist/cli.mjs
```
The original Claude Code source is the property of Anthropic. This repository is not affiliated with or endorsed by Anthropic.
Helpful commands:
- `bun run dev`
- `bun run smoke`
- `bun run doctor:runtime`
---
## VS Code Extension
The repo includes a VS Code extension in [`vscode-extension/openclaude-vscode`](vscode-extension/openclaude-vscode) for OpenClaude launch integration and theme support.
---
## Security
If you believe you found a security issue, see [SECURITY.md](SECURITY.md).
---
## Contributing
Contributions are welcome.
For larger changes, open an issue first so the scope is clear before implementation. Helpful validation commands include:
- `bun run build`
- `bun run smoke`
- focused `bun test ...` runs for touched areas
---
## Disclaimer
OpenClaude is an independent community project and is not affiliated with, endorsed by, or sponsored by Anthropic.
"Claude" and "Claude Code" are trademarks of Anthropic.
---
## License
This repository is provided for educational and research purposes. The original source code is subject to Anthropic's terms. The OpenAI shim additions are public domain.
MIT

69
SECURITY.md Normal file
View File

@@ -0,0 +1,69 @@
# Security Policy
## Supported Versions
Open Claude is currently maintained on the latest `main` branch and the latest
npm release only.
| Version | Supported |
| ------- | --------- |
| Latest release | :white_check_mark: |
| Older releases | :x: |
| Unreleased forks / modified builds | :x: |
Security fixes are generally released in the next patch version and may also be
landed directly on `main` before a package release is published.
## Reporting a Vulnerability
If you believe you have found a security vulnerability in Open Claude, please
report it privately.
Preferred reporting channel:
- GitHub Security Advisories / private vulnerability reporting for this
repository
Please include:
- a clear description of the issue
- affected version, commit, or environment
- reproduction steps or a proof of concept
- impact assessment
- any suggested remediation, if available
Please do **not** open a public issue for an unpatched vulnerability.
## Response Process
Our general goals are:
- initial triage acknowledgment within 7 days
- follow-up after validation when we can reproduce the issue
- coordinated disclosure after a fix is available
Severity, exploitability, and maintenance bandwidth may affect timelines.
## Disclosure and CVEs
Valid reports may be fixed privately first and disclosed after a patch is
available.
If a report is accepted and the issue is significant enough to warrant formal
tracking, we may publish a GitHub Security Advisory and request or assign a CVE
through the appropriate channel. CVE issuance is not guaranteed for every
report.
## Scope
This policy applies to:
- the Open Claude source code in this repository
- official release artifacts published from this repository
- the `@gitlawb/openclaude` npm package
This policy does not cover:
- third-party model providers, endpoints, or hosted services
- local misconfiguration on the reporter's machine
- vulnerabilities in unofficial forks, mirrors, or downstream repackages

View File

@@ -13,6 +13,7 @@
"@anthropic-ai/vertex-sdk": "0.14.4",
"@commander-js/extra-typings": "12.1.0",
"@growthbook/growthbook": "1.6.5",
"@mendable/firecrawl-js": "4.18.1",
"@modelcontextprotocol/sdk": "1.29.0",
"@opentelemetry/api": "1.9.1",
"@opentelemetry/api-logs": "0.214.0",
@@ -35,7 +36,8 @@
"cli-highlight": "2.1.11",
"code-excerpt": "4.0.0",
"commander": "12.1.0",
"diff": "7.0.0",
"diff": "8.0.3",
"duck-duck-scrape": "^2.2.7",
"emoji-regex": "10.6.0",
"env-paths": "3.0.0",
"execa": "9.6.1",
@@ -48,7 +50,7 @@
"ignore": "7.0.5",
"indent-string": "5.0.0",
"jsonc-parser": "3.3.1",
"lodash-es": "4.17.23",
"lodash-es": "4.18.0",
"lru-cache": "11.2.7",
"marked": "15.0.12",
"p-map": "7.0.4",
@@ -59,6 +61,7 @@
"react-compiler-runtime": "1.0.0",
"react-reconciler": "0.33.0",
"semver": "7.7.4",
"sharp": "^0.34.5",
"shell-quote": "1.8.3",
"signal-exit": "4.1.0",
"stack-utils": "2.0.6",
@@ -175,6 +178,8 @@
"@commander-js/extra-typings": ["@commander-js/extra-typings@12.1.0", "", { "peerDependencies": { "commander": "~12.1.0" } }, "sha512-wf/lwQvWAA0goIghcb91dQYpkLBcyhOhQNqG/VgWhnKzgt+UOMvra7EX/2fv70arm5RW+PUHoQHHDa6/p77Eqg=="],
"@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
"@growthbook/growthbook": ["@growthbook/growthbook@1.6.5", "", { "dependencies": { "dom-mutator": "^0.6.0" } }, "sha512-mUaMsgeUTpRIUOTn33EUXHRK6j7pxBjwqH4WpQyq+pukjd1AIzWlEa6w7i6bInJUcweGgP2beXZmaP6b6UPn7A=="],
"@grpc/grpc-js": ["@grpc/grpc-js@1.14.3", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA=="],
@@ -183,8 +188,60 @@
"@hono/node-server": ["@hono/node-server@1.19.12", "", { "peerDependencies": { "hono": "^4" } }, "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw=="],
"@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
"@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="],
"@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="],
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="],
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
"@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="],
"@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="],
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="],
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="],
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="],
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
"@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="],
"@mendable/firecrawl-js": ["@mendable/firecrawl-js@4.18.1", "", { "dependencies": { "axios": "1.14.0", "firecrawl": "4.16.0", "typescript-event-target": "^1.1.1", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.0" } }, "sha512-NfmJv+xcHoZthj8I3NP/8KAgO8EWcvOcTvCAvszxqs7/6sCs1CRss6Tum6RycZNSwJkr5RzQossN89IlixRfng=="],
"@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="],
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
@@ -433,12 +490,16 @@
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
"diff": ["diff@7.0.0", "", {}, "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw=="],
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="],
"dijkstrajs": ["dijkstrajs@1.0.3", "", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="],
"dom-mutator": ["dom-mutator@0.6.0", "", {}, "sha512-iCt9o0aYfXMUkz/43ZOAUFQYotjGB+GNbYJiJdz4TgXkyToXbbRy5S6FbTp72lRBtfpUMwEc1KmpFEU4CZeoNg=="],
"duck-duck-scrape": ["duck-duck-scrape@2.2.7", "", { "dependencies": { "html-entities": "^2.3.3", "needle": "^3.2.0" } }, "sha512-BEcglwnfx5puJl90KQfX+Q2q5vCguqyMpZcSRPBWk8OY55qWwV93+E+7DbIkrGDW4qkqPfUvtOUdi0lXz6lEMQ=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
@@ -495,6 +556,8 @@
"find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
"firecrawl": ["firecrawl@4.16.0", "", { "dependencies": { "axios": "^1.13.5", "typescript-event-target": "^1.1.1", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.0" } }, "sha512-7SJ/FWhZBtW2gTCE/BsvU+gbfIpfTq+D9IH82l9MacauLVptaY6EdYAhrK3YSMC9yr5NxvxRcpZKcXG/nqjiiQ=="],
"follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="],
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
@@ -543,6 +606,8 @@
"hono": ["hono@4.12.9", "", {}, "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA=="],
"html-entities": ["html-entities@2.6.0", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="],
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
@@ -591,7 +656,7 @@
"locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
"lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
"lodash-es": ["lodash-es@4.18.0", "", {}, "sha512-koAgswPPA+UTaPN64Etp+PGP+WT6oqOS2NMi5yDkMaiGw9qY4VxQbQF0mtKMyr4BlTznWyzePV5UpECTJQmSUA=="],
"lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
@@ -617,6 +682,8 @@
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
"needle": ["needle@3.5.0", "", { "dependencies": { "iconv-lite": "^0.6.3", "sax": "^1.2.4" }, "bin": { "needle": "bin/needle" } }, "sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w=="],
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
@@ -701,6 +768,8 @@
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
@@ -713,6 +782,8 @@
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
"sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
@@ -767,6 +838,8 @@
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"typescript-event-target": ["typescript-event-target@1.1.2", "", {}, "sha512-TvkrTUpv7gCPlcnSoEwUVUBwsdheKm+HF5u2tPAKubkIGMfovdSizCTaZRY/NhR8+Ijy8iZZUapbVQAsNrkFrw=="],
"undici": ["undici@7.24.6", "", {}, "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA=="],
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
@@ -817,6 +890,8 @@
"zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
"@anthropic-ai/sandbox-runtime/lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
"@aws-crypto/crc32/@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@aws-crypto/crc32/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
@@ -1007,6 +1082,8 @@
"@aws-sdk/xml-builder/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@grpc/proto-loader/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
"@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="],
@@ -1237,6 +1314,8 @@
"gaxios/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
"needle/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],

View File

@@ -194,7 +194,7 @@ bun run hardening:strict
Notes:
- `doctor:runtime` fails fast if `CLAUDE_CODE_USE_OPENAI=1` with a placeholder key or a missing key for non-local providers.
- Local providers such as `http://localhost:11434/v1` and `http://127.0.0.1:1337/v1` can run without `OPENAI_API_KEY`.
- Local providers such as `http://localhost:11434/v1`, `http://10.0.0.1:11434/v1`, and `http://127.0.0.1:1337/v1` can run without `OPENAI_API_KEY`.
- Codex profiles validate `CODEX_API_KEY` or the Codex CLI auth file and probe `POST /responses` instead of `GET /models`.
## Provider Launch Profiles

View File

@@ -66,6 +66,33 @@ openclaude
No API key is needed for Ollama local models.
### Option D: LM Studio
Install LM Studio first from:
- `https://lmstudio.ai/`
Then in LM Studio:
1. Download a model (e.g., Llama 3.1 8B, Mistral 7B)
2. Go to the "Developer" tab
3. Select your model and enable the server via the toggle
Then run:
```bash
export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_BASE_URL=http://localhost:1234/v1
export OPENAI_MODEL=your-model-name
# export OPENAI_API_KEY=lmstudio # optional: some users need a dummy key
openclaude
```
Replace `your-model-name` with the model name shown in LM Studio.
No API key is needed for LM Studio local models (but uncomment the `OPENAI_API_KEY` line if you hit auth errors).
## 4. If `openclaude` Is Not Found
Close the terminal, open a new one, and try again:
@@ -89,6 +116,14 @@ Check the basics:
- make sure Ollama is running
- make sure the model was pulled successfully
### For LM Studio
- make sure LM Studio is installed
- make sure LM Studio is running
- make sure the server is enabled (toggle on in the "Developer" tab)
- make sure a model is loaded in LM Studio
- make sure the model name matches what you set in `OPENAI_MODEL`
## 6. Updating OpenClaude
```bash

View File

@@ -66,6 +66,33 @@ openclaude
No API key is needed for Ollama local models.
### Option D: LM Studio
Install LM Studio first from:
- `https://lmstudio.ai/`
Then in LM Studio:
1. Download a model (e.g., Llama 3.1 8B, Mistral 7B)
2. Go to the "Developer" tab
3. Select your model and enable the server via the toggle
Then run:
```powershell
$env:CLAUDE_CODE_USE_OPENAI="1"
$env:OPENAI_BASE_URL="http://localhost:1234/v1"
$env:OPENAI_MODEL="your-model-name"
# $env:OPENAI_API_KEY="lmstudio" # optional: some users need a dummy key
openclaude
```
Replace `your-model-name` with the model name shown in LM Studio.
No API key is needed for LM Studio local models (but uncomment the `OPENAI_API_KEY` line if you hit auth errors).
## 4. If `openclaude` Is Not Found
Close PowerShell, open a new one, and try again:
@@ -89,6 +116,14 @@ Check the basics:
- make sure Ollama is running
- make sure the model was pulled successfully
### For LM Studio
- make sure LM Studio is installed
- make sure LM Studio is running
- make sure the server is enabled (toggle on in the "Developer" tab)
- make sure a model is loaded in LM Studio
- make sure the model name matches what you set in `OPENAI_MODEL`
## 6. Updating OpenClaude
```powershell

View File

@@ -49,6 +49,18 @@ def normalize_ollama_model(model_name: str) -> str:
return model_name
def _extract_ollama_image_data(block: dict) -> str | None:
source = block.get("source")
if not isinstance(source, dict):
return None
if source.get("type") != "base64":
return None
data = source.get("data")
if isinstance(data, str) and data:
return data
return None
def anthropic_to_ollama_messages(messages: list[dict]) -> list[dict]:
ollama_messages = []
for msg in messages:
@@ -58,15 +70,23 @@ def anthropic_to_ollama_messages(messages: list[dict]) -> list[dict]:
ollama_messages.append({"role": role, "content": content})
elif isinstance(content, list):
text_parts = []
image_parts = []
for block in content:
if isinstance(block, dict):
if block.get("type") == "text":
text_parts.append(block.get("text", ""))
elif block.get("type") == "image":
text_parts.append("[image]")
image_data = _extract_ollama_image_data(block)
if image_data:
image_parts.append(image_data)
else:
text_parts.append("[image]")
elif isinstance(block, str):
text_parts.append(block)
ollama_messages.append({"role": role, "content": "\n".join(text_parts)})
ollama_message = {"role": role, "content": "\n".join(text_parts)}
if image_parts:
ollama_message["images"] = image_parts
ollama_messages.append(ollama_message)
return ollama_messages

View File

@@ -34,6 +34,8 @@
"test:provider-recommendation": "bun test src/utils/providerRecommendation.test.ts src/utils/providerProfile.test.ts",
"typecheck": "tsc --noEmit",
"smoke": "bun run build && node dist/cli.mjs --version",
"verify:privacy": "bun run scripts/verify-no-phone-home.ts",
"build:verified": "bun run build && bun run verify:privacy",
"test:provider": "bun test src/services/api/*.test.ts src/utils/context.test.ts",
"doctor:runtime": "bun run scripts/system-check.ts",
"doctor:runtime:json": "bun run scripts/system-check.ts --json",
@@ -51,6 +53,7 @@
"@anthropic-ai/vertex-sdk": "0.14.4",
"@commander-js/extra-typings": "12.1.0",
"@growthbook/growthbook": "1.6.5",
"@mendable/firecrawl-js": "4.18.1",
"@modelcontextprotocol/sdk": "1.29.0",
"@opentelemetry/api": "1.9.1",
"@opentelemetry/api-logs": "0.214.0",
@@ -73,7 +76,8 @@
"cli-highlight": "2.1.11",
"code-excerpt": "4.0.0",
"commander": "12.1.0",
"diff": "7.0.0",
"diff": "8.0.3",
"duck-duck-scrape": "^2.2.7",
"emoji-regex": "10.6.0",
"env-paths": "3.0.0",
"execa": "9.6.1",
@@ -86,7 +90,7 @@
"ignore": "7.0.5",
"indent-string": "5.0.0",
"jsonc-parser": "3.3.1",
"lodash-es": "4.17.23",
"lodash-es": "4.18.0",
"lru-cache": "11.2.7",
"marked": "15.0.12",
"p-map": "7.0.4",
@@ -97,6 +101,7 @@
"react-compiler-runtime": "1.0.0",
"react-reconciler": "0.33.0",
"semver": "7.7.4",
"sharp": "^0.34.5",
"shell-quote": "1.8.3",
"signal-exit": "4.1.0",
"stack-utils": "2.0.6",
@@ -137,7 +142,7 @@
"ollama",
"gemini"
],
"license": "MIT",
"license": "SEE LICENSE FILE",
"publishConfig": {
"access": "public"
}

View File

@@ -158,7 +158,6 @@ export async function handleBgFlag() { throw new Error("Background sessions are
'modifiers-napi',
'url-handler-napi',
'color-diff-napi',
'sharp',
'@anthropic-ai/mcpb',
'@ant/claude-for-chrome-mcp',
'@anthropic-ai/sandbox-runtime',
@@ -275,6 +274,8 @@ export const SeverityNumber = {};
'@opentelemetry/sdk-logs',
'@opentelemetry/sdk-metrics',
'@opentelemetry/semantic-conventions',
// Native image processing
'sharp',
// Cloud provider SDKs
'@aws-sdk/client-bedrock',
'@aws-sdk/client-bedrock-runtime',

View File

@@ -197,6 +197,19 @@ export function classifyFetchError() { return 'disabled'; }
'components/FeedbackSurvey/submitTranscriptShare': `
export async function submitTranscriptShare() { return { success: false }; }
`,
// ─── Internal employee logging (not needed in the external build) ─────
'services/internalLogging': `
export async function logPermissionContextForAnts() {}
export const getContainerId = async () => null;
`,
}
function escapeForResolvedPathRegex(modulePath: string): string {
return modulePath
.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
.replace(/\//g, '[/\\\\]')
}
export const noTelemetryPlugin: BunPlugin = {
@@ -205,9 +218,7 @@ export const noTelemetryPlugin: BunPlugin = {
for (const [modulePath, contents] of Object.entries(stubs)) {
// Build regex that matches the resolved file path on any OS
// e.g. "services/analytics/growthbook" → /services[/\\]analytics[/\\]growthbook\.(ts|js)$/
const escaped = modulePath
.replace(/\//g, '[/\\\\]')
.replace(/\./g, '\\.')
const escaped = escapeForResolvedPathRegex(modulePath)
const filter = new RegExp(`${escaped}\\.(ts|js)$`)
build.onLoad({ filter }, () => ({

View File

@@ -120,23 +120,18 @@ function applyFastFlags(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
return env
}
function printSummary(profile: ProviderProfile, env: NodeJS.ProcessEnv): void {
function printSummary(profile: ProviderProfile): void {
console.log(`Launching profile: ${profile}`)
if (profile === 'gemini') {
console.log(`GEMINI_MODEL=${env.GEMINI_MODEL}`)
console.log(`GEMINI_API_KEY_SET=${Boolean(env.GEMINI_API_KEY)}`)
console.log('Using configured Gemini provider settings.')
} else if (profile === 'codex') {
console.log(`OPENAI_BASE_URL=${env.OPENAI_BASE_URL}`)
console.log(`OPENAI_MODEL=${env.OPENAI_MODEL}`)
console.log(`CODEX_API_KEY_SET=${Boolean(resolveCodexApiCredentials(env).apiKey)}`)
console.log('Using configured Codex/OpenAI-compatible provider settings.')
} else if (profile === 'atomic-chat') {
console.log(`OPENAI_BASE_URL=${env.OPENAI_BASE_URL}`)
console.log(`OPENAI_MODEL=${env.OPENAI_MODEL}`)
console.log('OPENAI_API_KEY_SET=false (local provider, no key required)')
console.log('Using configured Atomic Chat provider settings.')
} else if (profile === 'ollama') {
console.log('Using configured Ollama provider settings.')
} else {
console.log(`OPENAI_BASE_URL=${env.OPENAI_BASE_URL}`)
console.log(`OPENAI_MODEL=${env.OPENAI_MODEL}`)
console.log(`OPENAI_API_KEY_SET=${Boolean(env.OPENAI_API_KEY)}`)
console.log('Using configured OpenAI-compatible provider settings.')
}
}
@@ -231,7 +226,7 @@ async function main(): Promise<void> {
}
}
printSummary(profile, env)
printSummary(profile)
const doctorCode = await runProcess('bun', ['run', 'scripts/system-check.ts'], env)
if (doctorCode !== 0) {

View File

@@ -0,0 +1,42 @@
import { describe, expect, test } from 'bun:test'
import { formatReachabilityFailureDetail } from './system-check.ts'
describe('formatReachabilityFailureDetail', () => {
test('returns generic failure detail for non-codex transport', () => {
const detail = formatReachabilityFailureDetail(
'https://api.openai.com/v1/models',
429,
'{"error":"rate_limit"}',
{
transport: 'chat_completions',
requestedModel: 'gpt-4o',
resolvedModel: 'gpt-4o',
},
)
expect(detail).toBe(
'Unexpected status 429 from https://api.openai.com/v1/models. Body: {"error":"rate_limit"}',
)
})
test('adds alias/entitlement hint for codex model support 400s', () => {
const detail = formatReachabilityFailureDetail(
'https://chatgpt.com/backend-api/codex/responses',
400,
'{"detail":"The \\"gpt-5.3-codex-spark\\" model is not supported when using Codex with a ChatGPT account."}',
{
transport: 'codex_responses',
requestedModel: 'codexspark',
resolvedModel: 'gpt-5.3-codex-spark',
},
)
expect(detail).toContain(
'model alias "codexspark" resolved to "gpt-5.3-codex-spark"',
)
expect(detail).toContain(
'Try "codexplan" or another entitled Codex model.',
)
})
})

View File

@@ -58,6 +58,31 @@ function parseOptions(argv: string[]): CliOptions {
return options
}
export function formatReachabilityFailureDetail(
endpoint: string,
status: number,
responseBody: string,
request: {
transport: string
requestedModel: string
resolvedModel: string
},
): string {
const compactBody = responseBody.trim().replace(/\s+/g, ' ').slice(0, 240)
const base = `Unexpected status ${status} from ${endpoint}.`
const bodySuffix = compactBody ? ` Body: ${compactBody}` : ''
if (request.transport !== 'codex_responses' || status !== 400) {
return `${base}${bodySuffix}`
}
if (!/not supported.*chatgpt account/i.test(responseBody)) {
return `${base}${bodySuffix}`
}
return `${base}${bodySuffix} Hint: model alias "${request.requestedModel}" resolved to "${request.resolvedModel}", which this ChatGPT account does not currently allow. Try "codexplan" or another entitled Codex model.`
}
function checkNodeVersion(): CheckResult {
const raw = process.versions.node
const major = Number(raw.split('.')[0] ?? '0')
@@ -284,6 +309,7 @@ async function checkBaseUrlReachability(): Promise<CheckResult> {
headers['chatgpt-account-id'] = credentials.accountId
}
headers['Content-Type'] = 'application/json'
headers.originator = 'openclaude'
method = 'POST'
body = JSON.stringify({
model: request.resolvedModel,
@@ -315,7 +341,17 @@ async function checkBaseUrlReachability(): Promise<CheckResult> {
return pass('Provider reachability', `Reached ${endpoint} (status ${response.status}).`)
}
return fail('Provider reachability', `Unexpected status ${response.status} from ${endpoint}.`)
const responseBody = await response.text().catch(() => '')
const detail = formatReachabilityFailureDetail(
endpoint,
response.status,
responseBody,
request,
)
return fail(
'Provider reachability',
detail,
)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return fail('Provider reachability', `Failed to reach ${endpoint}: ${message}`)
@@ -430,6 +466,7 @@ function writeJsonReport(
options: CliOptions,
results: CheckResult[],
): void {
const envSummary = serializeSafeEnvSummary()
const payload = {
timestamp: new Date().toISOString(),
cwd: process.cwd(),
@@ -438,12 +475,24 @@ function writeJsonReport(
passed: results.filter(result => result.ok).length,
failed: results.filter(result => !result.ok).length,
},
env: serializeSafeEnvSummary(),
env: envSummary,
results,
}
if (options.json) {
console.log(JSON.stringify(payload, null, 2))
console.log(
JSON.stringify(
{
timestamp: payload.timestamp,
cwd: payload.cwd,
summary: payload.summary,
env: '[redacted in console JSON output; use --out-file for the full report]',
results: payload.results,
},
null,
2,
),
)
}
if (options.outFile) {
@@ -491,6 +540,8 @@ async function main(): Promise<void> {
}
}
await main()
if (import.meta.main) {
await main()
}
export {}

View File

@@ -0,0 +1,46 @@
#!/bin/bash
set -euo pipefail
DIST="dist/cli.mjs"
if [ ! -f "$DIST" ]; then
echo "ERROR: $DIST not found. Run 'bun run build' first."
exit 1
fi
EXIT=0
BANNED=(
"datadoghq.com"
"api/event_logging/batch"
"api/claude_code/metrics"
"getKubernetesNamespace"
"/var/run/secrets/kubernetes"
"/proc/self/mountinfo"
"tengu_internal_record_permission_context"
)
echo "Checking $DIST for banned patterns..."
echo ""
for pattern in "${BANNED[@]}"; do
COUNT=$(grep -F -c "$pattern" "$DIST" 2>/dev/null || true)
COUNT=${COUNT:-0}
if [ "$COUNT" -gt 0 ]; then
echo " FAIL: '$pattern' found ($COUNT occurrences)"
EXIT=1
else
echo " PASS: '$pattern' not found"
fi
done
echo ""
if [ "$EXIT" -eq 0 ]; then
echo "✓ All checks passed — no banned patterns in build output"
else
echo "✗ FAILED — banned patterns found in build output"
fi
exit $EXIT

View File

@@ -0,0 +1,43 @@
import { existsSync, readFileSync } from 'node:fs'
const DIST = 'dist/cli.mjs'
const BANNED_PATTERNS = [
'datadoghq.com',
'api/event_logging/batch',
'api/claude_code/metrics',
'getKubernetesNamespace',
'/var/run/secrets/kubernetes',
'/proc/self/mountinfo',
'tengu_internal_record_permission_context',
] as const
if (!existsSync(DIST)) {
console.error(`ERROR: ${DIST} not found. Run 'bun run build' first.`)
process.exit(1)
}
const contents = readFileSync(DIST, 'utf8')
let exitCode = 0
console.log(`Checking ${DIST} for banned patterns...`)
console.log('')
for (const pattern of BANNED_PATTERNS) {
const count = contents.split(pattern).length - 1
if (count > 0) {
console.log(` FAIL: '${pattern}' found (${count} occurrences)`)
exitCode = 1
} else {
console.log(` PASS: '${pattern}' not found`)
}
}
console.log('')
if (exitCode === 0) {
console.log('✓ All checks passed — no banned patterns in build output')
} else {
console.log('✗ FAILED — banned patterns found in build output')
}
process.exit(exitCode)

View File

@@ -228,9 +228,14 @@ class SmartRouter:
return min(available, key=lambda p: p.score(self.strategy))
def get_model_for_provider(
self, provider: Provider, claude_model: str
self,
provider: Provider,
claude_model: str,
is_large_request: bool = False,
) -> str:
"""Map a Claude model name to the provider's actual model."""
if is_large_request:
return provider.big_model
is_large = any(
keyword in claude_model.lower()
for keyword in ["opus", "sonnet", "large", "big"]
@@ -289,7 +294,11 @@ class SmartRouter:
)
provider = min(available, key=lambda p: p.score(self.strategy))
model = self.get_model_for_provider(provider, claude_model)
model = self.get_model_for_provider(
provider,
claude_model,
is_large_request=large,
)
logger.debug(
f"SmartRouter: routing to {provider.name}/{model} "

View File

@@ -46,6 +46,7 @@ import type { AttributionState } from './utils/commitAttribution.js'
import { getGlobalConfig } from './utils/config.js'
import { getCwd } from './utils/cwd.js'
import { isBareMode, isEnvTruthy } from './utils/envUtils.js'
import { logForDebugging } from './utils/debug.js'
import { getFastModeState } from './utils/fastMode.js'
import {
type FileHistoryState,
@@ -695,9 +696,11 @@ export class QueryEngine {
// progress are now recorded inline (their switch cases below), but
// this flush still matters for the preservedSegment tail walk.
// If the SDK subprocess restarts before then (claude-desktop kills
// between turns), tailUuid points to a never-written message
// applyPreservedSegmentRelinks fails its tail→head walk → returns
// without pruning → resume loads full pre-compact history.
// between turns), tailUuid can point to a never-written message. In
// that case strip preservedSegment before transcript persistence so
// resume falls back to ordinary boundary pruning instead of relying on
// broken relink metadata.
let transcriptMessage = message
if (
persistSession &&
message.type === 'system' &&
@@ -710,10 +713,21 @@ export class QueryEngine {
)
if (tailIdx !== -1) {
await recordTranscript(this.mutableMessages.slice(0, tailIdx + 1))
} else {
transcriptMessage = {
...message,
compactMetadata: {
...message.compactMetadata,
preservedSegment: undefined,
},
}
logForDebugging(
`[QueryEngine] stripped preservedSegment before transcript write; missing tail ${tailUuid}`,
)
}
}
}
messages.push(message)
messages.push(transcriptMessage)
if (persistSession) {
// Fire-and-forget for assistant messages. claude.ts yields one
// assistant message per content block, then mutates the last

View File

@@ -1,4 +1,4 @@
import { randomBytes } from 'crypto'
import { randomInt } from 'crypto'
import type { AppState } from './state/AppState.js'
import type { AgentId } from './types/ids.js'
import { getTaskOutputPath } from './utils/task/diskOutput.js'
@@ -97,10 +97,9 @@ const TASK_ID_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz'
export function generateTaskId(type: TaskType): string {
const prefix = getTaskIdPrefix(type)
const bytes = randomBytes(8)
let id = prefix
for (let i = 0; i < 8; i++) {
id += TASK_ID_ALPHABET[bytes[i]! % TASK_ID_ALPHABET.length]
id += TASK_ID_ALPHABET[randomInt(TASK_ID_ALPHABET.length)]!
}
return id
}

View File

@@ -176,6 +176,8 @@ export type ToolUseContext = {
querySource?: QuerySource
/** Optional callback to get the latest tools (e.g., after MCP servers connect mid-query) */
refreshTools?: () => Tools
/** Per-agent provider override from agentRouting config */
providerOverride?: { model: string; baseURL: string; apiKey: string }
}
abortController: AbortController
readFileState: FileStateCache

View File

@@ -0,0 +1,85 @@
import { expect, test } from 'bun:test'
import { buildChildEnv } from './sessionRunner.ts'
// Finding #42-1: sessionRunner spreads the full parent process.env into the
// child process environment, leaking API keys, DB credentials, proxy secrets.
// Only CLAUDE_CODE_OAUTH_TOKEN was stripped. Fix: explicit allowlist.
const baseOpts = {
accessToken: 'test-access-token',
useCcrV2: false as const,
}
test('buildChildEnv does not leak ANTHROPIC_API_KEY to child', () => {
const parentEnv = {
PATH: '/usr/bin',
HOME: '/home/user',
ANTHROPIC_API_KEY: 'sk-ant-secret-key',
CLAUDE_CODE_SESSION_ACCESS_TOKEN: 'will-be-overwritten',
}
const env = buildChildEnv(parentEnv, baseOpts)
expect(env.ANTHROPIC_API_KEY).toBeUndefined()
})
test('buildChildEnv does not leak OPENAI_API_KEY to child', () => {
const parentEnv = {
PATH: '/usr/bin',
HOME: '/home/user',
OPENAI_API_KEY: 'sk-openai-secret',
}
const env = buildChildEnv(parentEnv, baseOpts)
expect(env.OPENAI_API_KEY).toBeUndefined()
})
test('buildChildEnv does not leak arbitrary secrets to child', () => {
const parentEnv = {
PATH: '/usr/bin',
HOME: '/home/user',
DB_PASSWORD: 'super-secret',
AWS_SECRET_ACCESS_KEY: 'aws-secret',
GITHUB_TOKEN: 'ghp_token',
}
const env = buildChildEnv(parentEnv, baseOpts)
expect(env.DB_PASSWORD).toBeUndefined()
expect(env.AWS_SECRET_ACCESS_KEY).toBeUndefined()
expect(env.GITHUB_TOKEN).toBeUndefined()
})
test('buildChildEnv includes PATH and HOME from parent', () => {
const parentEnv = {
PATH: '/usr/bin:/usr/local/bin',
HOME: '/home/user',
ANTHROPIC_API_KEY: 'sk-secret',
}
const env = buildChildEnv(parentEnv, baseOpts)
expect(env.PATH).toBe('/usr/bin:/usr/local/bin')
expect(env.HOME).toBe('/home/user')
})
test('buildChildEnv sets CLAUDE_CODE_SESSION_ACCESS_TOKEN from opts', () => {
const env = buildChildEnv({ PATH: '/usr/bin' }, { ...baseOpts, accessToken: 'my-token' })
expect(env.CLAUDE_CODE_SESSION_ACCESS_TOKEN).toBe('my-token')
})
test('buildChildEnv sets CLAUDE_CODE_ENVIRONMENT_KIND to bridge', () => {
const env = buildChildEnv({ PATH: '/usr/bin' }, baseOpts)
expect(env.CLAUDE_CODE_ENVIRONMENT_KIND).toBe('bridge')
})
test('buildChildEnv does not pass CLAUDE_CODE_OAUTH_TOKEN to child', () => {
const parentEnv = {
PATH: '/usr/bin',
CLAUDE_CODE_OAUTH_TOKEN: 'oauth-token-to-strip',
}
const env = buildChildEnv(parentEnv, baseOpts)
expect(env.CLAUDE_CODE_OAUTH_TOKEN).toBeUndefined()
})
test('buildChildEnv sets CCR v2 vars when useCcrV2 is true', () => {
const env = buildChildEnv(
{ PATH: '/usr/bin' },
{ accessToken: 'tok', useCcrV2: true, workerEpoch: 42 },
)
expect(env.CLAUDE_CODE_USE_CCR_V2).toBe('1')
expect(env.CLAUDE_CODE_WORKER_EPOCH).toBe('42')
})

View File

@@ -16,6 +16,69 @@ import type {
const MAX_ACTIVITIES = 10
const MAX_STDERR_LINES = 10
/**
* Safe OS and runtime variables that the child process needs to function.
* Everything else (API keys, DB passwords, proxy secrets, etc.) must not
* be inherited — the child authenticates via CLAUDE_CODE_SESSION_ACCESS_TOKEN.
*/
const CHILD_ENV_ALLOWLIST = new Set([
// System / shell
'PATH', 'HOME', 'USERPROFILE', 'HOMEPATH', 'HOMEDRIVE',
'USERNAME', 'USER', 'LOGNAME',
'TEMP', 'TMP', 'TMPDIR',
'SYSTEMROOT', 'SYSTEMDRIVE', 'COMSPEC', 'WINDIR',
'LANG', 'LC_ALL', 'LC_CTYPE',
// Node.js runtime
'NODE_OPTIONS', 'NODE_PATH', 'NODE_ENV',
// OpenClaude session / bridge (non-secret)
'CLAUDE_CODE_ENVIRONMENT_KIND',
'CLAUDE_CODE_FORCE_SANDBOX',
'CLAUDE_CODE_BUBBLEWRAP',
'CLAUDE_CODE_ENTRYPOINT',
'CLAUDE_CODE_COORDINATOR_MODE',
'CLAUDE_CODE_PERMISSIONS_VERSION',
'CLAUDE_CODE_PERMISSIONS_SETTING',
// Display / terminal
'TERM', 'COLORTERM', 'FORCE_COLOR', 'NO_COLOR',
])
type BuildChildEnvOpts = {
accessToken: string
useCcrV2: boolean
workerEpoch?: number
sandbox?: boolean
}
/**
* Build the environment for the child CC process from an explicit allowlist.
* This prevents the parent's API keys and credentials from leaking to the child.
*/
export function buildChildEnv(
parentEnv: NodeJS.ProcessEnv,
opts: BuildChildEnvOpts,
): NodeJS.ProcessEnv {
// Start from allowlisted parent vars only
const env: NodeJS.ProcessEnv = {}
for (const key of Object.keys(parentEnv)) {
if (CHILD_ENV_ALLOWLIST.has(key)) {
env[key] = parentEnv[key]
}
}
// Bridge-required overrides
env.CLAUDE_CODE_OAUTH_TOKEN = undefined // explicitly strip
env.CLAUDE_CODE_ENVIRONMENT_KIND = 'bridge'
if (opts.sandbox) env.CLAUDE_CODE_FORCE_SANDBOX = '1'
env.CLAUDE_CODE_SESSION_ACCESS_TOKEN = opts.accessToken
env.CLAUDE_CODE_POST_FOR_SESSION_INGRESS_V2 = '1'
if (opts.useCcrV2) {
env.CLAUDE_CODE_USE_CCR_V2 = '1'
env.CLAUDE_CODE_WORKER_EPOCH = String(opts.workerEpoch)
}
return env
}
/**
* Sanitize a session ID for use in file names.
* Strips any characters that could cause path traversal (e.g. `../`, `/`)
@@ -303,24 +366,12 @@ export function createSessionSpawner(deps: SessionSpawnerDeps): SessionSpawner {
: []),
]
const env: NodeJS.ProcessEnv = {
...deps.env,
// Strip the bridge's OAuth token so the child CC process uses
// the session access token for inference instead.
CLAUDE_CODE_OAUTH_TOKEN: undefined,
CLAUDE_CODE_ENVIRONMENT_KIND: 'bridge',
...(deps.sandbox && { CLAUDE_CODE_FORCE_SANDBOX: '1' }),
CLAUDE_CODE_SESSION_ACCESS_TOKEN: opts.accessToken,
// v1: HybridTransport (WS reads + POST writes) to Session-Ingress.
// Harmless in v2 mode — transportUtils checks CLAUDE_CODE_USE_CCR_V2 first.
CLAUDE_CODE_POST_FOR_SESSION_INGRESS_V2: '1',
// v2: SSETransport + CCRClient to CCR's /v1/code/sessions/* endpoints.
// Same env vars environment-manager sets in the container path.
...(opts.useCcrV2 && {
CLAUDE_CODE_USE_CCR_V2: '1',
CLAUDE_CODE_WORKER_EPOCH: String(opts.workerEpoch),
}),
}
const env = buildChildEnv(deps.env, {
accessToken: opts.accessToken,
useCcrV2: opts.useCcrV2,
workerEpoch: opts.workerEpoch,
sandbox: deps.sandbox,
})
deps.onDebug(
`[bridge:session] Spawning sessionId=${opts.sessionId} sdkUrl=${opts.sdkUrl} accessToken=${opts.accessToken ? 'present' : 'MISSING'}`,

View File

@@ -0,0 +1,36 @@
import { expect, test } from 'bun:test'
import { buildSdkUrl } from './workSecret.ts'
// Finding #42-5: buildSdkUrl uses string.includes() on the full URL,
// so a remote URL containing "localhost" in its path gets ws:// (unencrypted).
test('buildSdkUrl uses wss for remote URL that contains localhost in path', () => {
const url = buildSdkUrl('https://remote.example.com/proxy/localhost/api', 'sess-1')
expect(url).toContain('wss://')
expect(url).not.toContain('ws://')
})
test('buildSdkUrl uses ws for actual localhost hostname', () => {
const url = buildSdkUrl('http://localhost:8080', 'sess-1')
expect(url).toContain('ws://')
})
test('buildSdkUrl uses ws for 127.0.0.1 hostname', () => {
const url = buildSdkUrl('http://127.0.0.1:3000', 'sess-1')
expect(url).toContain('ws://')
})
test('buildSdkUrl uses wss for regular remote hostname', () => {
const url = buildSdkUrl('https://api.example.com', 'sess-1')
expect(url).toContain('wss://')
})
test('buildSdkUrl uses v2 path for localhost', () => {
const url = buildSdkUrl('http://localhost:8080', 'sess-abc')
expect(url).toContain('/v2/session_ingress/ws/sess-abc')
})
test('buildSdkUrl uses v1 path for remote', () => {
const url = buildSdkUrl('https://api.example.com', 'sess-abc')
expect(url).toContain('/v1/session_ingress/ws/sess-abc')
})

View File

@@ -39,8 +39,8 @@ export function decodeWorkSecret(secret: string): WorkSecret {
* and /v1/ for production (Envoy rewrites /v1/ → /v2/).
*/
export function buildSdkUrl(apiBaseUrl: string, sessionId: string): string {
const isLocalhost =
apiBaseUrl.includes('localhost') || apiBaseUrl.includes('127.0.0.1')
const hostname = new URL(apiBaseUrl).hostname
const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1'
const protocol = isLocalhost ? 'ws' : 'wss'
const version = isLocalhost ? 'v2' : 'v1'
const host = apiBaseUrl.replace(/^https?:\/\//, '').replace(/\/+$/, '')

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -116,7 +116,6 @@ export async function autoModeCritiqueHandler(options: {
querySource: 'auto_mode_critique',
model,
system: CRITIQUE_SYSTEM_PROMPT,
skipSystemPromptPrefix: true,
max_tokens: 4096,
messages: [
{

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,5 @@
import chalk from 'chalk'
import { getAPIProvider } from 'src/utils/model/providers.js'
import { logEvent } from 'src/services/analytics/index.js'
import {
getLatestVersion,
@@ -28,6 +29,19 @@ import { gte } from 'src/utils/semver.js'
import { getInitialSettings } from 'src/utils/settings/settings.js'
export async function update() {
// Block updates for third-party providers. The update mechanism downloads
// from Anthropic's distribution bucket, which would silently replace the
// OpenClaude build (with the OpenAI shim) with the upstream Claude Code
// binary (without it).
if (getAPIProvider() !== 'firstParty') {
writeToStdout(
chalk.yellow('Auto-update is not available for third-party provider builds.\n') +
'To update, pull the latest source from the repository and rebuild:\n' +
' git pull && bun install && bun run build\n',
)
return
}
logEvent('tengu_update_check', {})
writeToStdout(`Current version: ${MACRO.VERSION}\n`)

View File

@@ -17,6 +17,7 @@ import config from './commands/config/index.js'
import { context, contextNonInteractive } from './commands/context/index.js'
import cost from './commands/cost/index.js'
import diff from './commands/diff/index.js'
import dream from './commands/dream/index.js'
import ctx_viz from './commands/ctx_viz/index.js'
import doctor from './commands/doctor/index.js'
import onboardGithub from './commands/onboard-github/index.js'
@@ -274,6 +275,7 @@ const COMMANDS = memoize((): Command[] => [
contextNonInteractive,
cost,
diff,
dream,
doctor,
effort,
exit,

File diff suppressed because one or more lines are too long

View File

@@ -9,4 +9,3 @@ export async function call(onDone: LocalJSXCommandOnDone, context: ToolUseContex
const tools = getTools(permissionContext);
return <AgentsMenu tools={tools} onExit={onDone} />;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkFnZW50c01lbnUiLCJUb29sVXNlQ29udGV4dCIsImdldFRvb2xzIiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiY2FsbCIsIm9uRG9uZSIsImNvbnRleHQiLCJQcm9taXNlIiwiUmVhY3ROb2RlIiwiYXBwU3RhdGUiLCJnZXRBcHBTdGF0ZSIsInBlcm1pc3Npb25Db250ZXh0IiwidG9vbFBlcm1pc3Npb25Db250ZXh0IiwidG9vbHMiXSwic291cmNlcyI6WyJhZ2VudHMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQWdlbnRzTWVudSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvYWdlbnRzL0FnZW50c01lbnUuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xVc2VDb250ZXh0IH0gZnJvbSAnLi4vLi4vVG9vbC5qcydcbmltcG9ydCB7IGdldFRvb2xzIH0gZnJvbSAnLi4vLi4vdG9vbHMuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjYWxsKFxuICBvbkRvbmU6IExvY2FsSlNYQ29tbWFuZE9uRG9uZSxcbiAgY29udGV4dDogVG9vbFVzZUNvbnRleHQsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICBjb25zdCBhcHBTdGF0ZSA9IGNvbnRleHQuZ2V0QXBwU3RhdGUoKVxuICBjb25zdCBwZXJtaXNzaW9uQ29udGV4dCA9IGFwcFN0YXRlLnRvb2xQZXJtaXNzaW9uQ29udGV4dFxuICBjb25zdCB0b29scyA9IGdldFRvb2xzKHBlcm1pc3Npb25Db250ZXh0KVxuXG4gIHJldHVybiA8QWdlbnRzTWVudSB0b29scz17dG9vbHN9IG9uRXhpdD17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFVBQVUsUUFBUSx1Q0FBdUM7QUFDbEUsY0FBY0MsY0FBYyxRQUFRLGVBQWU7QUFDbkQsU0FBU0MsUUFBUSxRQUFRLGdCQUFnQjtBQUN6QyxjQUFjQyxxQkFBcUIsUUFBUSx3QkFBd0I7QUFFbkUsT0FBTyxlQUFlQyxJQUFJQSxDQUN4QkMsTUFBTSxFQUFFRixxQkFBcUIsRUFDN0JHLE9BQU8sRUFBRUwsY0FBYyxDQUN4QixFQUFFTSxPQUFPLENBQUNSLEtBQUssQ0FBQ1MsU0FBUyxDQUFDLENBQUM7RUFDMUIsTUFBTUMsUUFBUSxHQUFHSCxPQUFPLENBQUNJLFdBQVcsQ0FBQyxDQUFDO0VBQ3RDLE1BQU1DLGlCQUFpQixHQUFHRixRQUFRLENBQUNHLHFCQUFxQjtFQUN4RCxNQUFNQyxLQUFLLEdBQUdYLFFBQVEsQ0FBQ1MsaUJBQWlCLENBQUM7RUFFekMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQ0UsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUNSLE1BQU0sQ0FBQyxHQUFHO0FBQ3JEIiwiaWdub3JlTGlzdCI6W119

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,4 +4,3 @@ import type { LocalJSXCommandCall } from '../../types/command.js';
export const call: LocalJSXCommandCall = async (onDone, context) => {
return <Settings onClose={onDone} context={context} defaultTab="Config" />;
};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlNldHRpbmdzIiwiTG9jYWxKU1hDb21tYW5kQ2FsbCIsImNhbGwiLCJvbkRvbmUiLCJjb250ZXh0Il0sInNvdXJjZXMiOlsiY29uZmlnLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IFNldHRpbmdzIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9TZXR0aW5ncy9TZXR0aW5ncy5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kQ2FsbCB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5cbmV4cG9ydCBjb25zdCBjYWxsOiBMb2NhbEpTWENvbW1hbmRDYWxsID0gYXN5bmMgKG9uRG9uZSwgY29udGV4dCkgPT4ge1xuICByZXR1cm4gPFNldHRpbmdzIG9uQ2xvc2U9e29uRG9uZX0gY29udGV4dD17Y29udGV4dH0gZGVmYXVsdFRhYj1cIkNvbmZpZ1wiIC8+XG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsUUFBUSxRQUFRLHVDQUF1QztBQUNoRSxjQUFjQyxtQkFBbUIsUUFBUSx3QkFBd0I7QUFFakUsT0FBTyxNQUFNQyxJQUFJLEVBQUVELG1CQUFtQixHQUFHLE1BQUFDLENBQU9DLE1BQU0sRUFBRUMsT0FBTyxLQUFLO0VBQ2xFLE9BQU8sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUNELE1BQU0sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDQyxPQUFPLENBQUMsQ0FBQyxVQUFVLENBQUMsUUFBUSxHQUFHO0FBQzVFLENBQUMiLCJpZ25vcmVMaXN0IjpbXX0=

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -6,4 +6,3 @@ export async function call(onDone: (result?: string, options?: {
}) => void): Promise<React.ReactNode> {
return <DesktopHandoff onDone={onDone} />;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwiRGVza3RvcEhhbmRvZmYiLCJjYWxsIiwib25Eb25lIiwicmVzdWx0Iiwib3B0aW9ucyIsImRpc3BsYXkiLCJQcm9taXNlIiwiUmVhY3ROb2RlIl0sInNvdXJjZXMiOlsiZGVza3RvcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBDb21tYW5kUmVzdWx0RGlzcGxheSB9IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgRGVza3RvcEhhbmRvZmYgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0Rlc2t0b3BIYW5kb2ZmLmpzJ1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiAoXG4gICAgcmVzdWx0Pzogc3RyaW5nLFxuICAgIG9wdGlvbnM/OiB7IGRpc3BsYXk/OiBDb21tYW5kUmVzdWx0RGlzcGxheSB9LFxuICApID0+IHZvaWQsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICByZXR1cm4gPERlc2t0b3BIYW5kb2ZmIG9uRG9uZT17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixjQUFjQyxvQkFBb0IsUUFBUSxtQkFBbUI7QUFDN0QsU0FBU0MsY0FBYyxRQUFRLG9DQUFvQztBQUVuRSxPQUFPLGVBQWVDLElBQUlBLENBQ3hCQyxNQUFNLEVBQUUsQ0FDTkMsTUFBZSxDQUFSLEVBQUUsTUFBTSxFQUNmQyxPQUE0QyxDQUFwQyxFQUFFO0VBQUVDLE9BQU8sQ0FBQyxFQUFFTixvQkFBb0I7QUFBQyxDQUFDLEVBQzVDLEdBQUcsSUFBSSxDQUNWLEVBQUVPLE9BQU8sQ0FBQ1IsS0FBSyxDQUFDUyxTQUFTLENBQUMsQ0FBQztFQUMxQixPQUFPLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDTCxNQUFNLENBQUMsR0FBRztBQUMzQyIsImlnbm9yZUxpc3QiOltdfQ==

View File

@@ -6,4 +6,3 @@ export const call: LocalJSXCommandCall = async (onDone, context) => {
} = await import('../../components/diff/DiffDialog.js');
return <DiffDialog messages={context.messages} onDone={onDone} />;
};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjYWxsIiwib25Eb25lIiwiY29udGV4dCIsIkRpZmZEaWFsb2ciLCJtZXNzYWdlcyJdLCJzb3VyY2VzIjpbImRpZmYudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDYWxsIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcblxuZXhwb3J0IGNvbnN0IGNhbGw6IExvY2FsSlNYQ29tbWFuZENhbGwgPSBhc3luYyAob25Eb25lLCBjb250ZXh0KSA9PiB7XG4gIGNvbnN0IHsgRGlmZkRpYWxvZyB9ID0gYXdhaXQgaW1wb3J0KCcuLi8uLi9jb21wb25lbnRzL2RpZmYvRGlmZkRpYWxvZy5qcycpXG4gIHJldHVybiA8RGlmZkRpYWxvZyBtZXNzYWdlcz17Y29udGV4dC5tZXNzYWdlc30gb25Eb25lPXtvbkRvbmV9IC8+XG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBRyxNQUFBQyxDQUFPQyxNQUFNLEVBQUVDLE9BQU8sS0FBSztFQUNsRSxNQUFNO0lBQUVDO0VBQVcsQ0FBQyxHQUFHLE1BQU0sTUFBTSxDQUFDLHFDQUFxQyxDQUFDO0VBQzFFLE9BQU8sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUNELE9BQU8sQ0FBQ0UsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUNILE1BQU0sQ0FBQyxHQUFHO0FBQ25FLENBQUMiLCJpZ25vcmVMaXN0IjpbXX0=

View File

@@ -4,4 +4,3 @@ import type { LocalJSXCommandCall } from '../../types/command.js';
export const call: LocalJSXCommandCall = (onDone, _context, _args) => {
return Promise.resolve(<Doctor onDone={onDone} />);
};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkRvY3RvciIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjYWxsIiwib25Eb25lIiwiX2NvbnRleHQiLCJfYXJncyIsIlByb21pc2UiLCJyZXNvbHZlIl0sInNvdXJjZXMiOlsiZG9jdG9yLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBEb2N0b3IgfSBmcm9tICcuLi8uLi9zY3JlZW5zL0RvY3Rvci5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kQ2FsbCB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5cbmV4cG9ydCBjb25zdCBjYWxsOiBMb2NhbEpTWENvbW1hbmRDYWxsID0gKG9uRG9uZSwgX2NvbnRleHQsIF9hcmdzKSA9PiB7XG4gIHJldHVybiBQcm9taXNlLnJlc29sdmUoPERvY3RvciBvbkRvbmU9e29uRG9uZX0gLz4pXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLE1BQU0sUUFBUSx5QkFBeUI7QUFDaEQsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBR0MsQ0FBQ0MsTUFBTSxFQUFFQyxRQUFRLEVBQUVDLEtBQUssS0FBSztFQUNwRSxPQUFPQyxPQUFPLENBQUNDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQ0osTUFBTSxDQUFDLEdBQUcsQ0FBQztBQUNwRCxDQUFDIiwiaWdub3JlTGlzdCI6W119

View File

@@ -0,0 +1,68 @@
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs'
import type { Command } from '../../commands.js'
import { isAutoMemoryEnabled, getAutoMemPath } from '../../memdir/paths.js'
import { getProjectDir } from '../../utils/sessionStorage.js'
import { getOriginalCwd, getSessionId } from '../../bootstrap/state.js'
import { buildConsolidationPrompt } from '../../services/autoDream/consolidationPrompt.js'
import {
readLastConsolidatedAt,
listSessionsTouchedSince,
recordConsolidation,
} from '../../services/autoDream/consolidationLock.js'
const command = {
type: 'prompt',
name: 'dream',
description:
'Run memory consolidation — synthesize recent sessions into durable memories',
isEnabled: () => isAutoMemoryEnabled(),
progressMessage: 'consolidating memories',
contentLength: 0,
source: 'builtin',
async getPromptForCommand(): Promise<ContentBlockParam[]> {
const memoryRoot = getAutoMemPath()
const transcriptDir = getProjectDir(getOriginalCwd())
let lastAt: number
try {
lastAt = await readLastConsolidatedAt()
} catch {
lastAt = 0
}
let sessionIds: string[]
try {
sessionIds = await listSessionsTouchedSince(lastAt)
} catch {
sessionIds = []
}
const currentSession = getSessionId()
sessionIds = sessionIds.filter(id => id !== currentSession)
if (sessionIds.length === 0) {
sessionIds = [currentSession]
}
const hoursSince =
lastAt > 0
? `${((Date.now() - lastAt) / 3_600_000).toFixed(1)}h ago`
: 'never'
const extra = `
**Manually triggered by user via /dream.**
Sessions since last consolidation (${sessionIds.length}, last run: ${hoursSince}):
${sessionIds.map(id => `- ${id}`).join('\n')}`
const prompt = buildConsolidationPrompt(memoryRoot, transcriptDir, extra)
// Record consolidation timestamp programmatically so auto-dream
// knows when the last manual run happened.
await recordConsolidation()
return [{ type: 'text', text: prompt }]
},
} satisfies Command
export default command

View File

@@ -0,0 +1 @@
export { default } from './dream.js'

File diff suppressed because one or more lines are too long

View File

@@ -30,4 +30,3 @@ export async function call(onDone: LocalJSXCommandOnDone): Promise<React.ReactNo
await gracefulShutdown(0, 'prompt_input_exit');
return null;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwic3Bhd25TeW5jIiwic2FtcGxlIiwiUmVhY3QiLCJFeGl0RmxvdyIsIkxvY2FsSlNYQ29tbWFuZE9uRG9uZSIsImlzQmdTZXNzaW9uIiwiZ3JhY2VmdWxTaHV0ZG93biIsImdldEN1cnJlbnRXb3JrdHJlZVNlc3Npb24iLCJHT09EQllFX01FU1NBR0VTIiwiZ2V0UmFuZG9tR29vZGJ5ZU1lc3NhZ2UiLCJjYWxsIiwib25Eb25lIiwiUHJvbWlzZSIsIlJlYWN0Tm9kZSIsInN0ZGlvIiwic2hvd1dvcmt0cmVlIl0sInNvdXJjZXMiOlsiZXhpdC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZmVhdHVyZSB9IGZyb20gJ2J1bjpidW5kbGUnXG5pbXBvcnQgeyBzcGF3blN5bmMgfSBmcm9tICdjaGlsZF9wcm9jZXNzJ1xuaW1wb3J0IHNhbXBsZSBmcm9tICdsb2Rhc2gtZXMvc2FtcGxlLmpzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBFeGl0RmxvdyB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvRXhpdEZsb3cuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5pbXBvcnQgeyBpc0JnU2Vzc2lvbiB9IGZyb20gJy4uLy4uL3V0aWxzL2NvbmN1cnJlbnRTZXNzaW9ucy5qcydcbmltcG9ydCB7IGdyYWNlZnVsU2h1dGRvd24gfSBmcm9tICcuLi8uLi91dGlscy9ncmFjZWZ1bFNodXRkb3duLmpzJ1xuaW1wb3J0IHsgZ2V0Q3VycmVudFdvcmt0cmVlU2Vzc2lvbiB9IGZyb20gJy4uLy4uL3V0aWxzL3dvcmt0cmVlLmpzJ1xuXG5jb25zdCBHT09EQllFX01FU1NBR0VTID0gWydHb29kYnllIScsICdTZWUgeWEhJywgJ0J5ZSEnLCAnQ2F0Y2ggeW91IGxhdGVyISddXG5cbmZ1bmN0aW9uIGdldFJhbmRvbUdvb2RieWVNZXNzYWdlKCk6IHN0cmluZyB7XG4gIHJldHVybiBzYW1wbGUoR09PREJZRV9NRVNTQUdFUykgPz8gJ0dvb2RieWUhJ1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICAvLyBJbnNpZGUgYSBgY2xhdWRlIC0tYmdgIHRtdXggc2Vzc2lvbjogZGV0YWNoIGluc3RlYWQgb2Yga2lsbC4gVGhlIFJFUExcbiAgLy8ga2VlcHMgcnVubmluZzsgYGNsYXVkZSBhdHRhY2hgIGNhbiByZWNvbm5lY3QuIENvdmVycyAvZXhpdCwgL3F1aXQsXG4gIC8vIGN0cmwrYywgY3RybCtkIOKAlCBhbGwgZnVubmVsIHRocm91Z2ggaGVyZSB2aWEgUkVQTCdzIGhhbmRsZUV4aXQuXG4gIGlmIChmZWF0dXJlKCdCR19TRVNTSU9OUycpICYmIGlzQmdTZXNzaW9uKCkpIHtcbiAgICBvbkRvbmUoKVxuICAgIHNwYXduU3luYygndG11eCcsIFsnZGV0YWNoLWNsaWVudCddLCB7IHN0ZGlvOiAnaWdub3JlJyB9KVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBjb25zdCBzaG93V29ya3RyZWUgPSBnZXRDdXJyZW50V29ya3RyZWVTZXNzaW9uKCkgIT09IG51bGxcblxuICBpZiAoc2hvd1dvcmt0cmVlKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxFeGl0Rmxvd1xuICAgICAgICBzaG93V29ya3RyZWU9e3Nob3dXb3JrdHJlZX1cbiAgICAgICAgb25Eb25lPXtvbkRvbmV9XG4gICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBvbkRvbmUoKX1cbiAgICAgIC8+XG4gICAgKVxuICB9XG5cbiAgb25Eb25lKGdldFJhbmRvbUdvb2RieWVNZXNzYWdlKCkpXG4gIGF3YWl0IGdyYWNlZnVsU2h1dGRvd24oMCwgJ3Byb21wdF9pbnB1dF9leGl0JylcbiAgcmV0dXJuIG51bGxcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsU0FBU0MsU0FBUyxRQUFRLGVBQWU7QUFDekMsT0FBT0MsTUFBTSxNQUFNLHFCQUFxQjtBQUN4QyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFFBQVEsUUFBUSw4QkFBOEI7QUFDdkQsY0FBY0MscUJBQXFCLFFBQVEsd0JBQXdCO0FBQ25FLFNBQVNDLFdBQVcsUUFBUSxtQ0FBbUM7QUFDL0QsU0FBU0MsZ0JBQWdCLFFBQVEsaUNBQWlDO0FBQ2xFLFNBQVNDLHlCQUF5QixRQUFRLHlCQUF5QjtBQUVuRSxNQUFNQyxnQkFBZ0IsR0FBRyxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLGtCQUFrQixDQUFDO0FBRTVFLFNBQVNDLHVCQUF1QkEsQ0FBQSxDQUFFLEVBQUUsTUFBTSxDQUFDO0VBQ3pDLE9BQU9SLE1BQU0sQ0FBQ08sZ0JBQWdCLENBQUMsSUFBSSxVQUFVO0FBQy9DO0FBRUEsT0FBTyxlQUFlRSxJQUFJQSxDQUN4QkMsTUFBTSxFQUFFUCxxQkFBcUIsQ0FDOUIsRUFBRVEsT0FBTyxDQUFDVixLQUFLLENBQUNXLFNBQVMsQ0FBQyxDQUFDO0VBQzFCO0VBQ0E7RUFDQTtFQUNBLElBQUlkLE9BQU8sQ0FBQyxhQUFhLENBQUMsSUFBSU0sV0FBVyxDQUFDLENBQUMsRUFBRTtJQUMzQ00sTUFBTSxDQUFDLENBQUM7SUFDUlgsU0FBUyxDQUFDLE1BQU0sRUFBRSxDQUFDLGVBQWUsQ0FBQyxFQUFFO01BQUVjLEtBQUssRUFBRTtJQUFTLENBQUMsQ0FBQztJQUN6RCxPQUFPLElBQUk7RUFDYjtFQUVBLE1BQU1DLFlBQVksR0FBR1IseUJBQXlCLENBQUMsQ0FBQyxLQUFLLElBQUk7RUFFekQsSUFBSVEsWUFBWSxFQUFFO0lBQ2hCLE9BQ0UsQ0FBQyxRQUFRLENBQ1AsWUFBWSxDQUFDLENBQUNBLFlBQVksQ0FBQyxDQUMzQixNQUFNLENBQUMsQ0FBQ0osTUFBTSxDQUFDLENBQ2YsUUFBUSxDQUFDLENBQUMsTUFBTUEsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUN6QjtFQUVOO0VBRUFBLE1BQU0sQ0FBQ0YsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO0VBQ2pDLE1BQU1ILGdCQUFnQixDQUFDLENBQUMsRUFBRSxtQkFBbUIsQ0FBQztFQUM5QyxPQUFPLElBQUk7QUFDYiIsImlnbm9yZUxpc3QiOltdfQ==

File diff suppressed because one or more lines are too long

View File

@@ -14,4 +14,3 @@ export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXComma
onDone(success ? 'Login successful' : 'Login interrupted');
}} />;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxvY2FsSlNYQ29tbWFuZENvbnRleHQiLCJMb2NhbEpTWENvbW1hbmRPbkRvbmUiLCJMb2dpbiIsInJ1bkV4dHJhVXNhZ2UiLCJjYWxsIiwib25Eb25lIiwiY29udGV4dCIsIlByb21pc2UiLCJSZWFjdE5vZGUiLCJyZXN1bHQiLCJ0eXBlIiwidmFsdWUiLCJzdWNjZXNzIiwib25DaGFuZ2VBUElLZXkiXSwic291cmNlcyI6WyJleHRyYS11c2FnZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDb250ZXh0IH0gZnJvbSAnLi4vLi4vY29tbWFuZHMuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5pbXBvcnQgeyBMb2dpbiB9IGZyb20gJy4uL2xvZ2luL2xvZ2luLmpzJ1xuaW1wb3J0IHsgcnVuRXh0cmFVc2FnZSB9IGZyb20gJy4vZXh0cmEtdXNhZ2UtY29yZS5qcydcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwoXG4gIG9uRG9uZTogTG9jYWxKU1hDb21tYW5kT25Eb25lLFxuICBjb250ZXh0OiBMb2NhbEpTWENvbW1hbmRDb250ZXh0LFxuKTogUHJvbWlzZTxSZWFjdC5SZWFjdE5vZGUgfCBudWxsPiB7XG4gIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHJ1bkV4dHJhVXNhZ2UoKVxuXG4gIGlmIChyZXN1bHQudHlwZSA9PT0gJ21lc3NhZ2UnKSB7XG4gICAgb25Eb25lKHJlc3VsdC52YWx1ZSlcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8TG9naW5cbiAgICAgIHN0YXJ0aW5nTWVzc2FnZT17XG4gICAgICAgICdTdGFydGluZyBuZXcgbG9naW4gZm9sbG93aW5nIC9leHRyYS11c2FnZS4gRXhpdCB3aXRoIEN0cmwtQyB0byB1c2UgZXhpc3RpbmcgYWNjb3VudC4nXG4gICAgICB9XG4gICAgICBvbkRvbmU9e3N1Y2Nlc3MgPT4ge1xuICAgICAgICBjb250ZXh0Lm9uQ2hhbmdlQVBJS2V5KClcbiAgICAgICAgb25Eb25lKHN1Y2Nlc3MgPyAnTG9naW4gc3VjY2Vzc2Z1bCcgOiAnTG9naW4gaW50ZXJydXB0ZWQnKVxuICAgICAgfX1cbiAgICAvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLGNBQWNDLHNCQUFzQixRQUFRLG1CQUFtQjtBQUMvRCxjQUFjQyxxQkFBcUIsUUFBUSx3QkFBd0I7QUFDbkUsU0FBU0MsS0FBSyxRQUFRLG1CQUFtQjtBQUN6QyxTQUFTQyxhQUFhLFFBQVEsdUJBQXVCO0FBRXJELE9BQU8sZUFBZUMsSUFBSUEsQ0FDeEJDLE1BQU0sRUFBRUoscUJBQXFCLEVBQzdCSyxPQUFPLEVBQUVOLHNCQUFzQixDQUNoQyxFQUFFTyxPQUFPLENBQUNSLEtBQUssQ0FBQ1MsU0FBUyxHQUFHLElBQUksQ0FBQyxDQUFDO0VBQ2pDLE1BQU1DLE1BQU0sR0FBRyxNQUFNTixhQUFhLENBQUMsQ0FBQztFQUVwQyxJQUFJTSxNQUFNLENBQUNDLElBQUksS0FBSyxTQUFTLEVBQUU7SUFDN0JMLE1BQU0sQ0FBQ0ksTUFBTSxDQUFDRSxLQUFLLENBQUM7SUFDcEIsT0FBTyxJQUFJO0VBQ2I7RUFFQSxPQUNFLENBQUMsS0FBSyxDQUNKLGVBQWUsQ0FBQyxDQUNkLHNGQUNGLENBQUMsQ0FDRCxNQUFNLENBQUMsQ0FBQ0MsT0FBTyxJQUFJO0lBQ2pCTixPQUFPLENBQUNPLGNBQWMsQ0FBQyxDQUFDO0lBQ3hCUixNQUFNLENBQUNPLE9BQU8sR0FBRyxrQkFBa0IsR0FBRyxtQkFBbUIsQ0FBQztFQUM1RCxDQUFDLENBQUMsR0FDRjtBQUVOIiwiaWdub3JlTGlzdCI6W119

File diff suppressed because one or more lines are too long

View File

@@ -22,4 +22,3 @@ export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXComma
const initialDescription = args || '';
return renderFeedbackComponent(onDone, context.abortController.signal, context.messages, initialDescription);
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwiTG9jYWxKU1hDb21tYW5kQ29udGV4dCIsIkZlZWRiYWNrIiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiTWVzc2FnZSIsInJlbmRlckZlZWRiYWNrQ29tcG9uZW50Iiwib25Eb25lIiwicmVzdWx0Iiwib3B0aW9ucyIsImRpc3BsYXkiLCJhYm9ydFNpZ25hbCIsIkFib3J0U2lnbmFsIiwibWVzc2FnZXMiLCJpbml0aWFsRGVzY3JpcHRpb24iLCJiYWNrZ3JvdW5kVGFza3MiLCJ0YXNrSWQiLCJ0eXBlIiwiaWRlbnRpdHkiLCJhZ2VudElkIiwiUmVhY3ROb2RlIiwiY2FsbCIsImNvbnRleHQiLCJhcmdzIiwiUHJvbWlzZSIsImFib3J0Q29udHJvbGxlciIsInNpZ25hbCJdLCJzb3VyY2VzIjpbImZlZWRiYWNrLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHtcbiAgQ29tbWFuZFJlc3VsdERpc3BsYXksXG4gIExvY2FsSlNYQ29tbWFuZENvbnRleHQsXG59IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgRmVlZGJhY2sgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0ZlZWRiYWNrLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRPbkRvbmUgfSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuaW1wb3J0IHR5cGUgeyBNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdHlwZXMvbWVzc2FnZS5qcydcblxuLy8gU2hhcmVkIGZ1bmN0aW9uIHRvIHJlbmRlciB0aGUgRmVlZGJhY2sgY29tcG9uZW50XG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyRmVlZGJhY2tDb21wb25lbnQoXG4gIG9uRG9uZTogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXkgfSxcbiAgKSA9PiB2b2lkLFxuICBhYm9ydFNpZ25hbDogQWJvcnRTaWduYWwsXG4gIG1lc3NhZ2VzOiBNZXNzYWdlW10sXG4gIGluaXRpYWxEZXNjcmlwdGlvbjogc3RyaW5nID0gJycsXG4gIGJhY2tncm91bmRUYXNrczoge1xuICAgIFt0YXNrSWQ6IHN0cmluZ106IHtcbiAgICAgIHR5cGU6IHN0cmluZ1xuICAgICAgaWRlbnRpdHk/OiB7IGFnZW50SWQ6IHN0cmluZyB9XG4gICAgICBtZXNzYWdlcz86IE1lc3NhZ2VbXVxuICAgIH1cbiAgfSA9IHt9LFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgcmV0dXJuIChcbiAgICA8RmVlZGJhY2tcbiAgICAgIGFib3J0U2lnbmFsPXthYm9ydFNpZ25hbH1cbiAgICAgIG1lc3NhZ2VzPXttZXNzYWdlc31cbiAgICAgIGluaXRpYWxEZXNjcmlwdGlvbj17aW5pdGlhbERlc2NyaXB0aW9ufVxuICAgICAgb25Eb25lPXtvbkRvbmV9XG4gICAgICBiYWNrZ3JvdW5kVGFza3M9e2JhY2tncm91bmRUYXNrc31cbiAgICAvPlxuICApXG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjYWxsKFxuICBvbkRvbmU6IExvY2FsSlNYQ29tbWFuZE9uRG9uZSxcbiAgY29udGV4dDogTG9jYWxKU1hDb21tYW5kQ29udGV4dCxcbiAgYXJncz86IHN0cmluZyxcbik6IFByb21pc2U8UmVhY3QuUmVhY3ROb2RlPiB7XG4gIGNvbnN0IGluaXRpYWxEZXNjcmlwdGlvbiA9IGFyZ3MgfHwgJydcbiAgcmV0dXJuIHJlbmRlckZlZWRiYWNrQ29tcG9uZW50KFxuICAgIG9uRG9uZSxcbiAgICBjb250ZXh0LmFib3J0Q29udHJvbGxlci5zaWduYWwsXG4gICAgY29udGV4dC5tZXNzYWdlcyxcbiAgICBpbml0aWFsRGVzY3JpcHRpb24sXG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixjQUNFQyxvQkFBb0IsRUFDcEJDLHNCQUFzQixRQUNqQixtQkFBbUI7QUFDMUIsU0FBU0MsUUFBUSxRQUFRLDhCQUE4QjtBQUN2RCxjQUFjQyxxQkFBcUIsUUFBUSx3QkFBd0I7QUFDbkUsY0FBY0MsT0FBTyxRQUFRLHdCQUF3Qjs7QUFFckQ7QUFDQSxPQUFPLFNBQVNDLHVCQUF1QkEsQ0FDckNDLE1BQU0sRUFBRSxDQUNOQyxNQUFlLENBQVIsRUFBRSxNQUFNLEVBQ2ZDLE9BQTRDLENBQXBDLEVBQUU7RUFBRUMsT0FBTyxDQUFDLEVBQUVULG9CQUFvQjtBQUFDLENBQUMsRUFDNUMsR0FBRyxJQUFJLEVBQ1RVLFdBQVcsRUFBRUMsV0FBVyxFQUN4QkMsUUFBUSxFQUFFUixPQUFPLEVBQUUsRUFDbkJTLGtCQUFrQixFQUFFLE1BQU0sR0FBRyxFQUFFLEVBQy9CQyxlQUFlLEVBQUU7RUFDZixDQUFDQyxNQUFNLEVBQUUsTUFBTSxDQUFDLEVBQUU7SUFDaEJDLElBQUksRUFBRSxNQUFNO0lBQ1pDLFFBQVEsQ0FBQyxFQUFFO01BQUVDLE9BQU8sRUFBRSxNQUFNO0lBQUMsQ0FBQztJQUM5Qk4sUUFBUSxDQUFDLEVBQUVSLE9BQU8sRUFBRTtFQUN0QixDQUFDO0FBQ0gsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUNQLEVBQUVMLEtBQUssQ0FBQ29CLFNBQVMsQ0FBQztFQUNqQixPQUNFLENBQUMsUUFBUSxDQUNQLFdBQVcsQ0FBQyxDQUFDVCxXQUFXLENBQUMsQ0FDekIsUUFBUSxDQUFDLENBQUNFLFFBQVEsQ0FBQyxDQUNuQixrQkFBa0IsQ0FBQyxDQUFDQyxrQkFBa0IsQ0FBQyxDQUN2QyxNQUFNLENBQUMsQ0FBQ1AsTUFBTSxDQUFDLENBQ2YsZUFBZSxDQUFDLENBQUNRLGVBQWUsQ0FBQyxHQUNqQztBQUVOO0FBRUEsT0FBTyxlQUFlTSxJQUFJQSxDQUN4QmQsTUFBTSxFQUFFSCxxQkFBcUIsRUFDN0JrQixPQUFPLEVBQUVwQixzQkFBc0IsRUFDL0JxQixJQUFhLENBQVIsRUFBRSxNQUFNLENBQ2QsRUFBRUMsT0FBTyxDQUFDeEIsS0FBSyxDQUFDb0IsU0FBUyxDQUFDLENBQUM7RUFDMUIsTUFBTU4sa0JBQWtCLEdBQUdTLElBQUksSUFBSSxFQUFFO0VBQ3JDLE9BQU9qQix1QkFBdUIsQ0FDNUJDLE1BQU0sRUFDTmUsT0FBTyxDQUFDRyxlQUFlLENBQUNDLE1BQU0sRUFDOUJKLE9BQU8sQ0FBQ1QsUUFBUSxFQUNoQkMsa0JBQ0YsQ0FBQztBQUNIIiwiaWdub3JlTGlzdCI6W119

View File

@@ -8,4 +8,3 @@ export const call: LocalJSXCommandCall = async (onDone, {
}) => {
return <HelpV2 commands={commands} onClose={onDone} />;
};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkhlbHBWMiIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjYWxsIiwib25Eb25lIiwib3B0aW9ucyIsImNvbW1hbmRzIl0sInNvdXJjZXMiOlsiaGVscC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBIZWxwVjIgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0hlbHBWMi9IZWxwVjIuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZENhbGwgfSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuXG5leHBvcnQgY29uc3QgY2FsbDogTG9jYWxKU1hDb21tYW5kQ2FsbCA9IGFzeW5jIChcbiAgb25Eb25lLFxuICB7IG9wdGlvbnM6IHsgY29tbWFuZHMgfSB9LFxuKSA9PiB7XG4gIHJldHVybiA8SGVscFYyIGNvbW1hbmRzPXtjb21tYW5kc30gb25DbG9zZT17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLE1BQU0sUUFBUSxtQ0FBbUM7QUFDMUQsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBRyxNQUFBQyxDQUN2Q0MsTUFBTSxFQUNOO0VBQUVDLE9BQU8sRUFBRTtJQUFFQztFQUFTO0FBQUUsQ0FBQyxLQUN0QjtFQUNILE9BQU8sQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUNBLFFBQVEsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDRixNQUFNLENBQUMsR0FBRztBQUN4RCxDQUFDIiwiaWdub3JlTGlzdCI6W119

View File

@@ -10,4 +10,3 @@ export const call: LocalJSXCommandCall = async (onDone, context) => {
const toolNames = getTools(permissionContext).map(tool => tool.name);
return <HooksConfigMenu toolNames={toolNames} onExit={onDone} />;
};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkhvb2tzQ29uZmlnTWVudSIsImxvZ0V2ZW50IiwiZ2V0VG9vbHMiLCJMb2NhbEpTWENvbW1hbmRDYWxsIiwiY2FsbCIsIm9uRG9uZSIsImNvbnRleHQiLCJhcHBTdGF0ZSIsImdldEFwcFN0YXRlIiwicGVybWlzc2lvbkNvbnRleHQiLCJ0b29sUGVybWlzc2lvbkNvbnRleHQiLCJ0b29sTmFtZXMiLCJtYXAiLCJ0b29sIiwibmFtZSJdLCJzb3VyY2VzIjpbImhvb2tzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEhvb2tzQ29uZmlnTWVudSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvaG9va3MvSG9va3NDb25maWdNZW51LmpzJ1xuaW1wb3J0IHsgbG9nRXZlbnQgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9hbmFseXRpY3MvaW5kZXguanMnXG5pbXBvcnQgeyBnZXRUb29scyB9IGZyb20gJy4uLy4uL3Rvb2xzLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDYWxsIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcblxuZXhwb3J0IGNvbnN0IGNhbGw6IExvY2FsSlNYQ29tbWFuZENhbGwgPSBhc3luYyAob25Eb25lLCBjb250ZXh0KSA9PiB7XG4gIGxvZ0V2ZW50KCd0ZW5ndV9ob29rc19jb21tYW5kJywge30pXG4gIGNvbnN0IGFwcFN0YXRlID0gY29udGV4dC5nZXRBcHBTdGF0ZSgpXG4gIGNvbnN0IHBlcm1pc3Npb25Db250ZXh0ID0gYXBwU3RhdGUudG9vbFBlcm1pc3Npb25Db250ZXh0XG4gIGNvbnN0IHRvb2xOYW1lcyA9IGdldFRvb2xzKHBlcm1pc3Npb25Db250ZXh0KS5tYXAodG9vbCA9PiB0b29sLm5hbWUpXG4gIHJldHVybiA8SG9va3NDb25maWdNZW51IHRvb2xOYW1lcz17dG9vbE5hbWVzfSBvbkV4aXQ9e29uRG9uZX0gLz5cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxlQUFlLFFBQVEsMkNBQTJDO0FBQzNFLFNBQVNDLFFBQVEsUUFBUSxtQ0FBbUM7QUFDNUQsU0FBU0MsUUFBUSxRQUFRLGdCQUFnQjtBQUN6QyxjQUFjQyxtQkFBbUIsUUFBUSx3QkFBd0I7QUFFakUsT0FBTyxNQUFNQyxJQUFJLEVBQUVELG1CQUFtQixHQUFHLE1BQUFDLENBQU9DLE1BQU0sRUFBRUMsT0FBTyxLQUFLO0VBQ2xFTCxRQUFRLENBQUMscUJBQXFCLEVBQUUsQ0FBQyxDQUFDLENBQUM7RUFDbkMsTUFBTU0sUUFBUSxHQUFHRCxPQUFPLENBQUNFLFdBQVcsQ0FBQyxDQUFDO0VBQ3RDLE1BQU1DLGlCQUFpQixHQUFHRixRQUFRLENBQUNHLHFCQUFxQjtFQUN4RCxNQUFNQyxTQUFTLEdBQUdULFFBQVEsQ0FBQ08saUJBQWlCLENBQUMsQ0FBQ0csR0FBRyxDQUFDQyxJQUFJLElBQUlBLElBQUksQ0FBQ0MsSUFBSSxDQUFDO0VBQ3BFLE9BQU8sQ0FBQyxlQUFlLENBQUMsU0FBUyxDQUFDLENBQUNILFNBQVMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDTixNQUFNLENBQUMsR0FBRztBQUNsRSxDQUFDIiwiaWdub3JlTGlzdCI6W119

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -12,4 +12,3 @@ export function CheckGitHubStep() {
}
return t0;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJDaGVja0dpdEh1YlN0ZXAiLCIkIiwiX2MiLCJ0MCIsIlN5bWJvbCIsImZvciJdLCJzb3VyY2VzIjpbIkNoZWNrR2l0SHViU3RlcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcblxuZXhwb3J0IGZ1bmN0aW9uIENoZWNrR2l0SHViU3RlcCgpIHtcbiAgcmV0dXJuIDxUZXh0PkNoZWNraW5nIEdpdEh1YiBDTEkgaW5zdGFsbGF0aW9u4oCmPC9UZXh0PlxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsSUFBSSxRQUFRLGNBQWM7QUFFbkMsT0FBTyxTQUFBQyxnQkFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBRixDQUFBLFFBQUFHLE1BQUEsQ0FBQUMsR0FBQTtJQUNFRixFQUFBLElBQUMsSUFBSSxDQUFDLGlDQUFpQyxFQUF0QyxJQUFJLENBQXlDO0lBQUFGLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBQUEsT0FBOUNFLEVBQThDO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0=

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,48 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { extractGitHubRepoSlug } from './repoSlug.ts'
test('keeps owner/repo input as-is', () => {
assert.equal(extractGitHubRepoSlug('Gitlawb/openclaude'), 'Gitlawb/openclaude')
})
test('extracts slug from https GitHub URLs', () => {
assert.equal(
extractGitHubRepoSlug('https://github.com/Gitlawb/openclaude'),
'Gitlawb/openclaude',
)
assert.equal(
extractGitHubRepoSlug('https://www.github.com/Gitlawb/openclaude.git'),
'Gitlawb/openclaude',
)
})
test('extracts slug from ssh GitHub URLs', () => {
assert.equal(
extractGitHubRepoSlug('git@github.com:Gitlawb/openclaude.git'),
'Gitlawb/openclaude',
)
assert.equal(
extractGitHubRepoSlug('ssh://git@github.com/Gitlawb/openclaude'),
'Gitlawb/openclaude',
)
})
test('rejects malformed or non-GitHub URLs', () => {
assert.equal(extractGitHubRepoSlug('https://gitlab.com/Gitlawb/openclaude'), null)
assert.equal(extractGitHubRepoSlug('https://github.com/Gitlawb'), null)
assert.equal(extractGitHubRepoSlug('not actually github.com/Gitlawb/openclaude'), null)
assert.equal(
extractGitHubRepoSlug('https://evil.example/?next=github.com/Gitlawb/openclaude'),
null,
)
assert.equal(
extractGitHubRepoSlug('https://github.com.evil.example/Gitlawb/openclaude'),
null,
)
assert.equal(
extractGitHubRepoSlug('https://example.com/github.com/Gitlawb/openclaude'),
null,
)
})

View File

@@ -0,0 +1,54 @@
export function extractGitHubRepoSlug(value: string): string | null {
const trimmed = value.trim()
const slugMatch = trimmed.match(
/^(?<owner>[^/:\s]+)\/(?<repo>[^/\s]+?)(?:\.git)?\/?$/i,
)
if (slugMatch?.groups?.owner && slugMatch.groups.repo) {
return `${slugMatch.groups.owner}/${slugMatch.groups.repo}`.replace(
/\.git$/i,
'',
)
}
const shorthandUrlMatch = trimmed.match(
/^(?:https?:\/\/)?(?:www\.)?github\.com\/(?<owner>[^/:\s]+)\/(?<repo>[^/\s]+?)(?:\.git)?\/?$/i,
)
if (shorthandUrlMatch?.groups?.owner && shorthandUrlMatch.groups.repo) {
return `${shorthandUrlMatch.groups.owner}/${shorthandUrlMatch.groups.repo}`.replace(
/\.git$/i,
'',
)
}
const sshMatch = trimmed.match(
/^(?:git@|ssh:\/\/git@)(?:www\.)?github\.com[:/](?<owner>[^/:\s]+)\/(?<repo>[^/\s]+?)(?:\.git)?\/?$/i,
)
if (sshMatch?.groups?.owner && sshMatch.groups.repo) {
return `${sshMatch.groups.owner}/${sshMatch.groups.repo}`
}
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
return null
}
try {
const parsed = new URL(trimmed)
const hostname = parsed.hostname.toLowerCase()
if (hostname !== 'github.com' && hostname !== 'www.github.com') {
return null
}
const segments = parsed.pathname
.replace(/^\/+|\/+$/g, '')
.split('/')
.filter(Boolean)
if (segments.length < 2) {
return null
}
return `${segments[0]}/${segments[1]}`.replace(/\.git$/i, '')
} catch {
return null
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,4 +4,3 @@ export async function call(onDone: LocalJSXCommandOnDone): Promise<undefined> {
display: 'system'
});
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJMb2NhbEpTWENvbW1hbmRPbkRvbmUiLCJjYWxsIiwib25Eb25lIiwiUHJvbWlzZSIsImRpc3BsYXkiXSwic291cmNlcyI6WyJvdXRwdXQtc3R5bGUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kT25Eb25lIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwob25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUpOiBQcm9taXNlPHVuZGVmaW5lZD4ge1xuICBvbkRvbmUoXG4gICAgJy9vdXRwdXQtc3R5bGUgaGFzIGJlZW4gZGVwcmVjYXRlZC4gVXNlIC9jb25maWcgdG8gY2hhbmdlIHlvdXIgb3V0cHV0IHN0eWxlLCBvciBzZXQgaXQgaW4geW91ciBzZXR0aW5ncyBmaWxlLiBDaGFuZ2VzIHRha2UgZWZmZWN0IG9uIHRoZSBuZXh0IHNlc3Npb24uJyxcbiAgICB7IGRpc3BsYXk6ICdzeXN0ZW0nIH0sXG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsY0FBY0EscUJBQXFCLFFBQVEsd0JBQXdCO0FBRW5FLE9BQU8sZUFBZUMsSUFBSUEsQ0FBQ0MsTUFBTSxFQUFFRixxQkFBcUIsQ0FBQyxFQUFFRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7RUFDNUVELE1BQU0sQ0FDSix1SkFBdUosRUFDdko7SUFBRUUsT0FBTyxFQUFFO0VBQVMsQ0FDdEIsQ0FBQztBQUNIIiwiaWdub3JlTGlzdCI6W119

View File

@@ -21,4 +21,3 @@ export async function call(onDone: LocalJSXCommandOnDone): Promise<React.ReactNo
});
return <Passes onDone={onDone} />;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlBhc3NlcyIsImxvZ0V2ZW50IiwiZ2V0Q2FjaGVkUmVtYWluaW5nUGFzc2VzIiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiZ2V0R2xvYmFsQ29uZmlnIiwic2F2ZUdsb2JhbENvbmZpZyIsImNhbGwiLCJvbkRvbmUiLCJQcm9taXNlIiwiUmVhY3ROb2RlIiwiY29uZmlnIiwiaXNGaXJzdFZpc2l0IiwiaGFzVmlzaXRlZFBhc3NlcyIsInJlbWFpbmluZyIsImN1cnJlbnQiLCJwYXNzZXNMYXN0U2VlblJlbWFpbmluZyIsImlzX2ZpcnN0X3Zpc2l0Il0sInNvdXJjZXMiOlsicGFzc2VzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IFBhc3NlcyB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvUGFzc2VzL1Bhc3Nlcy5qcydcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnLi4vLi4vc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgZ2V0Q2FjaGVkUmVtYWluaW5nUGFzc2VzIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvYXBpL3JlZmVycmFsLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRPbkRvbmUgfSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuaW1wb3J0IHsgZ2V0R2xvYmFsQ29uZmlnLCBzYXZlR2xvYmFsQ29uZmlnIH0gZnJvbSAnLi4vLi4vdXRpbHMvY29uZmlnLmpzJ1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICAvLyBNYXJrIHRoYXQgdXNlciBoYXMgdmlzaXRlZCAvcGFzc2VzIHNvIHdlIHN0b3Agc2hvd2luZyB0aGUgdXBzZWxsXG4gIGNvbnN0IGNvbmZpZyA9IGdldEdsb2JhbENvbmZpZygpXG4gIGNvbnN0IGlzRmlyc3RWaXNpdCA9ICFjb25maWcuaGFzVmlzaXRlZFBhc3Nlc1xuICBpZiAoaXNGaXJzdFZpc2l0KSB7XG4gICAgY29uc3QgcmVtYWluaW5nID0gZ2V0Q2FjaGVkUmVtYWluaW5nUGFzc2VzKClcbiAgICBzYXZlR2xvYmFsQ29uZmlnKGN1cnJlbnQgPT4gKHtcbiAgICAgIC4uLmN1cnJlbnQsXG4gICAgICBoYXNWaXNpdGVkUGFzc2VzOiB0cnVlLFxuICAgICAgcGFzc2VzTGFzdFNlZW5SZW1haW5pbmc6IHJlbWFpbmluZyA/PyBjdXJyZW50LnBhc3Nlc0xhc3RTZWVuUmVtYWluaW5nLFxuICAgIH0pKVxuICB9XG4gIGxvZ0V2ZW50KCd0ZW5ndV9ndWVzdF9wYXNzZXNfdmlzaXRlZCcsIHsgaXNfZmlyc3RfdmlzaXQ6IGlzRmlyc3RWaXNpdCB9KVxuICByZXR1cm4gPFBhc3NlcyBvbkRvbmU9e29uRG9uZX0gLz5cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxNQUFNLFFBQVEsbUNBQW1DO0FBQzFELFNBQVNDLFFBQVEsUUFBUSxtQ0FBbUM7QUFDNUQsU0FBU0Msd0JBQXdCLFFBQVEsZ0NBQWdDO0FBQ3pFLGNBQWNDLHFCQUFxQixRQUFRLHdCQUF3QjtBQUNuRSxTQUFTQyxlQUFlLEVBQUVDLGdCQUFnQixRQUFRLHVCQUF1QjtBQUV6RSxPQUFPLGVBQWVDLElBQUlBLENBQ3hCQyxNQUFNLEVBQUVKLHFCQUFxQixDQUM5QixFQUFFSyxPQUFPLENBQUNULEtBQUssQ0FBQ1UsU0FBUyxDQUFDLENBQUM7RUFDMUI7RUFDQSxNQUFNQyxNQUFNLEdBQUdOLGVBQWUsQ0FBQyxDQUFDO0VBQ2hDLE1BQU1PLFlBQVksR0FBRyxDQUFDRCxNQUFNLENBQUNFLGdCQUFnQjtFQUM3QyxJQUFJRCxZQUFZLEVBQUU7SUFDaEIsTUFBTUUsU0FBUyxHQUFHWCx3QkFBd0IsQ0FBQyxDQUFDO0lBQzVDRyxnQkFBZ0IsQ0FBQ1MsT0FBTyxLQUFLO01BQzNCLEdBQUdBLE9BQU87TUFDVkYsZ0JBQWdCLEVBQUUsSUFBSTtNQUN0QkcsdUJBQXVCLEVBQUVGLFNBQVMsSUFBSUMsT0FBTyxDQUFDQztJQUNoRCxDQUFDLENBQUMsQ0FBQztFQUNMO0VBQ0FkLFFBQVEsQ0FBQyw0QkFBNEIsRUFBRTtJQUFFZSxjQUFjLEVBQUVMO0VBQWEsQ0FBQyxDQUFDO0VBQ3hFLE9BQU8sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUNKLE1BQU0sQ0FBQyxHQUFHO0FBQ25DIiwiaWdub3JlTGlzdCI6W119

View File

@@ -7,4 +7,3 @@ export const call: LocalJSXCommandCall = async (onDone, context) => {
context.setMessages(prev => [...prev, createPermissionRetryMessage(commands)]);
}} />;
};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlBlcm1pc3Npb25SdWxlTGlzdCIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjcmVhdGVQZXJtaXNzaW9uUmV0cnlNZXNzYWdlIiwiY2FsbCIsIm9uRG9uZSIsImNvbnRleHQiLCJjb21tYW5kcyIsInNldE1lc3NhZ2VzIiwicHJldiJdLCJzb3VyY2VzIjpbInBlcm1pc3Npb25zLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IFBlcm1pc3Npb25SdWxlTGlzdCB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvcGVybWlzc2lvbnMvcnVsZXMvUGVybWlzc2lvblJ1bGVMaXN0LmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDYWxsIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcbmltcG9ydCB7IGNyZWF0ZVBlcm1pc3Npb25SZXRyeU1lc3NhZ2UgfSBmcm9tICcuLi8uLi91dGlscy9tZXNzYWdlcy5qcydcblxuZXhwb3J0IGNvbnN0IGNhbGw6IExvY2FsSlNYQ29tbWFuZENhbGwgPSBhc3luYyAob25Eb25lLCBjb250ZXh0KSA9PiB7XG4gIHJldHVybiAoXG4gICAgPFBlcm1pc3Npb25SdWxlTGlzdFxuICAgICAgb25FeGl0PXtvbkRvbmV9XG4gICAgICBvblJldHJ5RGVuaWFscz17Y29tbWFuZHMgPT4ge1xuICAgICAgICBjb250ZXh0LnNldE1lc3NhZ2VzKHByZXYgPT4gW1xuICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgY3JlYXRlUGVybWlzc2lvblJldHJ5TWVzc2FnZShjb21tYW5kcyksXG4gICAgICAgIF0pXG4gICAgICB9fVxuICAgIC8+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxrQkFBa0IsUUFBUSwwREFBMEQ7QUFDN0YsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBQ2pFLFNBQVNDLDRCQUE0QixRQUFRLHlCQUF5QjtBQUV0RSxPQUFPLE1BQU1DLElBQUksRUFBRUYsbUJBQW1CLEdBQUcsTUFBQUUsQ0FBT0MsTUFBTSxFQUFFQyxPQUFPLEtBQUs7RUFDbEUsT0FDRSxDQUFDLGtCQUFrQixDQUNqQixNQUFNLENBQUMsQ0FBQ0QsTUFBTSxDQUFDLENBQ2YsY0FBYyxDQUFDLENBQUNFLFFBQVEsSUFBSTtJQUMxQkQsT0FBTyxDQUFDRSxXQUFXLENBQUNDLElBQUksSUFBSSxDQUMxQixHQUFHQSxJQUFJLEVBQ1BOLDRCQUE0QixDQUFDSSxRQUFRLENBQUMsQ0FDdkMsQ0FBQztFQUNKLENBQUMsQ0FBQyxHQUNGO0FBRU4sQ0FBQyIsImlnbm9yZUxpc3QiOltdfQ==

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -29,4 +29,3 @@ export function PluginTrustWarning() {
}
return t2;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJCb3giLCJUZXh0IiwiZ2V0UGx1Z2luVHJ1c3RNZXNzYWdlIiwiUGx1Z2luVHJ1c3RXYXJuaW5nIiwiJCIsIl9jIiwidDAiLCJTeW1ib2wiLCJmb3IiLCJjdXN0b21NZXNzYWdlIiwidDEiLCJ3YXJuaW5nIiwidDIiXSwic291cmNlcyI6WyJQbHVnaW5UcnVzdFdhcm5pbmcudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmaWd1cmVzIGZyb20gJ2ZpZ3VyZXMnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IGdldFBsdWdpblRydXN0TWVzc2FnZSB9IGZyb20gJy4uLy4uL3V0aWxzL3BsdWdpbnMvbWFya2V0cGxhY2VIZWxwZXJzLmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gUGx1Z2luVHJ1c3RXYXJuaW5nKCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGN1c3RvbU1lc3NhZ2UgPSBnZXRQbHVnaW5UcnVzdE1lc3NhZ2UoKVxuICByZXR1cm4gKFxuICAgIDxCb3ggbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgIDxUZXh0IGNvbG9yPVwiY2xhdWRlXCI+e2ZpZ3VyZXMud2FybmluZ30gPC9UZXh0PlxuICAgICAgPFRleHQgZGltQ29sb3IgaXRhbGljPlxuICAgICAgICBNYWtlIHN1cmUgeW91IHRydXN0IGEgcGx1Z2luIGJlZm9yZSBpbnN0YWxsaW5nLCB1cGRhdGluZywgb3IgdXNpbmcgaXQuXG4gICAgICAgIEFudGhyb3BpYyBkb2VzIG5vdCBjb250cm9sIHdoYXQgTUNQIHNlcnZlcnMsIGZpbGVzLCBvciBvdGhlciBzb2Z0d2FyZVxuICAgICAgICBhcmUgaW5jbHVkZWQgaW4gcGx1Z2lucyBhbmQgY2Fubm90IHZlcmlmeSB0aGF0IHRoZXkgd2lsbCB3b3JrIGFzXG4gICAgICAgIGludGVuZGVkIG9yIHRoYXQgdGhleSB3b24mYXBvczt0IGNoYW5nZS4gU2VlIGVhY2ggcGx1Z2luJmFwb3M7cyBob21lcGFnZVxuICAgICAgICBmb3IgbW9yZSBpbmZvcm1hdGlvbi57Y3VzdG9tTWVzc2FnZSA/IGAgJHtjdXN0b21NZXNzYWdlfWAgOiAnJ31cbiAgICAgIDwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLFNBQVNDLHFCQUFxQixRQUFRLDJDQUEyQztBQUVqRixPQUFPLFNBQUFDLG1CQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFGLENBQUEsUUFBQUcsTUFBQSxDQUFBQyxHQUFBO0lBQ2lCRixFQUFBLEdBQUFKLHFCQUFxQixDQUFDLENBQUM7SUFBQUUsQ0FBQSxNQUFBRSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBRixDQUFBO0VBQUE7RUFBN0MsTUFBQUssYUFBQSxHQUFzQkgsRUFBdUI7RUFBQSxJQUFBSSxFQUFBO0VBQUEsSUFBQU4sQ0FBQSxRQUFBRyxNQUFBLENBQUFDLEdBQUE7SUFHekNFLEVBQUEsSUFBQyxJQUFJLENBQU8sS0FBUSxDQUFSLFFBQVEsQ0FBRSxDQUFBWixPQUFPLENBQUFhLE9BQU8sQ0FBRSxDQUFDLEVBQXRDLElBQUksQ0FBeUM7SUFBQVAsQ0FBQSxNQUFBTSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBTixDQUFBO0VBQUE7RUFBQSxJQUFBUSxFQUFBO0VBQUEsSUFBQVIsQ0FBQSxRQUFBRyxNQUFBLENBQUFDLEdBQUE7SUFEaERJLEVBQUEsSUFBQyxHQUFHLENBQWUsWUFBQyxDQUFELEdBQUMsQ0FDbEIsQ0FBQUYsRUFBNkMsQ0FDN0MsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBTixLQUFLLENBQUMsQ0FBQyxrU0FLRSxDQUFBRCxhQUFhLEdBQWIsSUFBb0JBLGFBQWEsRUFBTyxHQUF4QyxFQUF1QyxDQUMvRCxFQU5DLElBQUksQ0FPUCxFQVRDLEdBQUcsQ0FTRTtJQUFBTCxDQUFBLE1BQUFRLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFSLENBQUE7RUFBQTtFQUFBLE9BVE5RLEVBU007QUFBQSIsImlnbm9yZUxpc3QiOltdfQ==

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More