Compare commits
100 Commits
v0.1.7
...
cleanup-in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd8d0ef0fa | ||
|
|
c3c60b7bab | ||
|
|
27e6505bfd | ||
|
|
cdbe016e6f | ||
|
|
bd4daa3ee7 | ||
|
|
5be5387096 | ||
|
|
897ef2002e | ||
|
|
ab3c46a591 | ||
|
|
03dff274a1 | ||
|
|
bffd43056f | ||
|
|
c52245fc0a | ||
|
|
365bd3102d | ||
|
|
3df635c24d | ||
|
|
2031c67d46 | ||
|
|
694c242865 | ||
|
|
fb221baa21 | ||
|
|
e5c9a6f629 | ||
|
|
70cfa61582 | ||
|
|
afed73fa5a | ||
|
|
c735233f92 | ||
|
|
8ce09ae743 | ||
|
|
931ee96f5a | ||
|
|
c1e5e363cd | ||
|
|
b0d796e5c3 | ||
|
|
6987a54a71 | ||
|
|
fb32e3f829 | ||
|
|
59ab2701f7 | ||
|
|
7668abaed0 | ||
|
|
36d1c45954 | ||
|
|
116cc8e6bd | ||
|
|
19c00e67ed | ||
|
|
7c0ea68b65 | ||
|
|
f3a984dde1 | ||
|
|
72c6e97094 | ||
|
|
f3ab727ec2 | ||
|
|
29edece72f | ||
|
|
6181050811 | ||
|
|
0fd0026a76 | ||
|
|
6919d774f2 | ||
|
|
aa69e85795 | ||
|
|
66bbb75836 | ||
|
|
2c6ec0119e | ||
|
|
74a25d01a6 | ||
|
|
7cf4c88ab8 | ||
|
|
f68b9aa57d | ||
|
|
20d1ee8427 | ||
|
|
089a42fc07 | ||
|
|
f5b20fc517 | ||
|
|
184ec250fd | ||
|
|
43deb49c2c | ||
|
|
0e7a2446c7 | ||
|
|
63ad0196d6 | ||
|
|
32046e9b40 | ||
|
|
7bd7d0f54d | ||
|
|
cdf4bad95b | ||
|
|
4158214895 | ||
|
|
a6ed57d0f4 | ||
|
|
7b68eb1acb | ||
|
|
84950642ae | ||
|
|
a287597273 | ||
|
|
1cd4164062 | ||
|
|
47c53a18e8 | ||
|
|
cf90457428 | ||
|
|
5e77d82620 | ||
|
|
11d9660a80 | ||
|
|
1a57335d74 | ||
|
|
7bc903d875 | ||
|
|
4c22de2585 | ||
|
|
63daf33b48 | ||
|
|
2ee43d7ee8 | ||
|
|
3581d3f83f | ||
|
|
4a4394bb65 | ||
|
|
b4aa27183d | ||
|
|
96b9e0235b | ||
|
|
7095abb837 | ||
|
|
8501786852 | ||
|
|
37d4c21739 | ||
|
|
a43023705b | ||
|
|
73db9b5fd3 | ||
|
|
2b5cf9f0c1 | ||
|
|
4237a72b92 | ||
|
|
942d09ca9c | ||
|
|
ac4efae870 | ||
|
|
4c6adf4774 | ||
|
|
ff124dcdfb | ||
|
|
8e8671fc51 | ||
|
|
4c1ba35aa1 | ||
|
|
5baee3b491 | ||
|
|
43ba2cbfae | ||
|
|
5c25ac4e9a | ||
|
|
84ac06bac9 | ||
|
|
c66b859342 | ||
|
|
1709f5c098 | ||
|
|
5d6443799a | ||
|
|
3ef09f911e | ||
|
|
6f4aa02123 | ||
|
|
b65921e8c3 | ||
|
|
0fe8551d33 | ||
|
|
6319df02f0 | ||
|
|
0c88dea247 |
250
.env.example
Normal file
250
.env.example
Normal 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
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal 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.
|
||||||
27
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal 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
21
.github/pull_request_template.md
vendored
Normal 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:
|
||||||
3
.github/workflows/pr-checks.yml
vendored
3
.github/workflows/pr-checks.yml
vendored
@@ -6,6 +6,9 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
smoke-and-tests:
|
smoke-and-tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,5 +3,6 @@ dist/
|
|||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
|
!.env.example
|
||||||
.openclaude-profile.json
|
.openclaude-profile.json
|
||||||
reports/
|
reports/
|
||||||
|
|||||||
162
ANDROID_INSTALL.md
Normal file
162
ANDROID_INSTALL.md
Normal 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 ~200–400MB. 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 (6K–12K 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
126
CODE_OF_CONDUCT.md
Normal 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
119
CONTRIBUTING.md
Normal 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
29
LICENSE
Normal 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
329
README.md
@@ -1,64 +1,60 @@
|
|||||||
# OpenClaude
|
# 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:
|
### Install
|
||||||
|
|
||||||
- [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:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install -g @gitlawb/openclaude
|
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
|
```bash
|
||||||
openclaude
|
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
|
macOS / Linux:
|
||||||
|
|
||||||
```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
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install -g @gitlawb/openclaude
|
|
||||||
|
|
||||||
export CLAUDE_CODE_USE_OPENAI=1
|
export CLAUDE_CODE_USE_OPENAI=1
|
||||||
export OPENAI_API_KEY=sk-your-key-here
|
export OPENAI_API_KEY=sk-your-key-here
|
||||||
export OPENAI_MODEL=gpt-4o
|
export OPENAI_MODEL=gpt-4o
|
||||||
@@ -66,135 +62,216 @@ export OPENAI_MODEL=gpt-4o
|
|||||||
openclaude
|
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)
|
- [Non-Technical Setup](docs/non-technical-setup.md)
|
||||||
- On Windows: [Windows Quick Start](docs/quick-start-windows.md)
|
- [Windows Quick Start](docs/quick-start-windows.md)
|
||||||
- On macOS or Linux: [macOS / Linux Quick Start](docs/quick-start-mac-linux.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
|
| Provider | Setup Path | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
Best default if you already have an OpenAI API key.
|
| 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 |
|
||||||
### Ollama
|
| GitHub Models | `/onboard-github` | Interactive onboarding with saved credentials |
|
||||||
|
| Codex | `/provider` | Uses existing Codex credentials when available |
|
||||||
Best if you want to run models locally on your own machine.
|
| Ollama | `/provider` or env vars | Local inference with no API key |
|
||||||
|
| Atomic Chat | advanced setup | Local Apple Silicon backend |
|
||||||
### Codex
|
| Bedrock / Vertex / Foundry | env vars | Additional provider integrations for supported environments |
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What Works
|
## What Works
|
||||||
|
|
||||||
- **All tools**: Bash, FileRead, FileWrite, FileEdit, Glob, Grep, WebFetch, WebSearch, Agent, MCP, LSP, NotebookEdit, Tasks
|
- Tool-driven coding workflows
|
||||||
- **Streaming**: Real-time token streaming
|
Bash, file read/write/edit, grep, glob, agents, tasks, MCP, and slash commands
|
||||||
- **Tool calling**: Multi-step tool chains (the model calls tools, gets results, continues)
|
- Streaming responses
|
||||||
- **Images**: Base64 and URL images passed to vision models
|
Real-time token output and tool progress
|
||||||
- **Slash commands**: /commit, /review, /compact, /diff, /doctor, etc.
|
- Tool calling
|
||||||
- **Sub-agents**: AgentTool spawns sub-agents using the same provider
|
Multi-step tool loops with model calls, tool execution, and follow-up responses
|
||||||
- **Memory**: Persistent memory system
|
- Images
|
||||||
|
URL and base64 image inputs for providers that support vision
|
||||||
## What's Different
|
- Provider profiles
|
||||||
|
Guided setup plus saved `.openclaude-profile.json` support
|
||||||
- **No thinking mode**: Anthropic's extended thinking is disabled (OpenAI models use different reasoning)
|
- Local and remote model backends
|
||||||
- **No prompt caching**: Anthropic-specific cache headers are skipped
|
Cloud APIs, local servers, and Apple Silicon local inference
|
||||||
- **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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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.
|
||||||
|
|
||||||
```
|
- Anthropic-specific features may not exist on other providers
|
||||||
Claude Code Tool System
|
- Tool quality depends heavily on the selected model
|
||||||
|
|
- Smaller local models can struggle with long multi-step tool flows
|
||||||
v
|
- Some providers impose lower output caps than the CLI defaults, and OpenClaude adapts where possible
|
||||||
Anthropic SDK interface (duck-typed)
|
|
||||||
|
|
|
||||||
v
|
|
||||||
openaiShim.ts <-- translates formats
|
|
||||||
|
|
|
||||||
v
|
|
||||||
OpenAI Chat Completions API
|
|
||||||
|
|
|
||||||
v
|
|
||||||
Any compatible model
|
|
||||||
```
|
|
||||||
|
|
||||||
It translates:
|
For best results, use models with strong tool/function calling support.
|
||||||
- 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.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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 |
|
### Configuration
|
||||||
|-------|-------------|-------------|-------|
|
|
||||||
| 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 |
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
```
|
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.
|
||||||
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
|
>**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.
|
||||||
src/utils/model/providers.ts — Added 'openai' provider type
|
|
||||||
src/utils/model/configs.ts — Added openai model mappings
|
For Anthropic-native backends (Anthropic/Vertex/Foundry) and Codex responses, OpenClaude keeps the native provider web search behavior.
|
||||||
src/utils/model/model.ts — Respects OPENAI_MODEL for defaults
|
|
||||||
src/utils/auth.ts — Recognizes OpenAI as valid 3P provider
|
`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
|
## 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
69
SECURITY.md
Normal 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
|
||||||
87
bun.lock
87
bun.lock
@@ -13,6 +13,7 @@
|
|||||||
"@anthropic-ai/vertex-sdk": "0.14.4",
|
"@anthropic-ai/vertex-sdk": "0.14.4",
|
||||||
"@commander-js/extra-typings": "12.1.0",
|
"@commander-js/extra-typings": "12.1.0",
|
||||||
"@growthbook/growthbook": "1.6.5",
|
"@growthbook/growthbook": "1.6.5",
|
||||||
|
"@mendable/firecrawl-js": "4.18.1",
|
||||||
"@modelcontextprotocol/sdk": "1.29.0",
|
"@modelcontextprotocol/sdk": "1.29.0",
|
||||||
"@opentelemetry/api": "1.9.1",
|
"@opentelemetry/api": "1.9.1",
|
||||||
"@opentelemetry/api-logs": "0.214.0",
|
"@opentelemetry/api-logs": "0.214.0",
|
||||||
@@ -35,7 +36,8 @@
|
|||||||
"cli-highlight": "2.1.11",
|
"cli-highlight": "2.1.11",
|
||||||
"code-excerpt": "4.0.0",
|
"code-excerpt": "4.0.0",
|
||||||
"commander": "12.1.0",
|
"commander": "12.1.0",
|
||||||
"diff": "7.0.0",
|
"diff": "8.0.3",
|
||||||
|
"duck-duck-scrape": "^2.2.7",
|
||||||
"emoji-regex": "10.6.0",
|
"emoji-regex": "10.6.0",
|
||||||
"env-paths": "3.0.0",
|
"env-paths": "3.0.0",
|
||||||
"execa": "9.6.1",
|
"execa": "9.6.1",
|
||||||
@@ -48,7 +50,7 @@
|
|||||||
"ignore": "7.0.5",
|
"ignore": "7.0.5",
|
||||||
"indent-string": "5.0.0",
|
"indent-string": "5.0.0",
|
||||||
"jsonc-parser": "3.3.1",
|
"jsonc-parser": "3.3.1",
|
||||||
"lodash-es": "4.17.23",
|
"lodash-es": "4.18.0",
|
||||||
"lru-cache": "11.2.7",
|
"lru-cache": "11.2.7",
|
||||||
"marked": "15.0.12",
|
"marked": "15.0.12",
|
||||||
"p-map": "7.0.4",
|
"p-map": "7.0.4",
|
||||||
@@ -59,6 +61,7 @@
|
|||||||
"react-compiler-runtime": "1.0.0",
|
"react-compiler-runtime": "1.0.0",
|
||||||
"react-reconciler": "0.33.0",
|
"react-reconciler": "0.33.0",
|
||||||
"semver": "7.7.4",
|
"semver": "7.7.4",
|
||||||
|
"sharp": "^0.34.5",
|
||||||
"shell-quote": "1.8.3",
|
"shell-quote": "1.8.3",
|
||||||
"signal-exit": "4.1.0",
|
"signal-exit": "4.1.0",
|
||||||
"stack-utils": "2.0.6",
|
"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=="],
|
"@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=="],
|
"@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=="],
|
"@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=="],
|
"@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=="],
|
"@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=="],
|
"@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=="],
|
"@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=="],
|
"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=="],
|
"dijkstrajs": ["dijkstrajs@1.0.3", "", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="],
|
||||||
|
|
||||||
"dom-mutator": ["dom-mutator@0.6.0", "", {}, "sha512-iCt9o0aYfXMUkz/43ZOAUFQYotjGB+GNbYJiJdz4TgXkyToXbbRy5S6FbTp72lRBtfpUMwEc1KmpFEU4CZeoNg=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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-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=="],
|
"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": ["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": ["undici@7.24.6", "", {}, "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA=="],
|
||||||
|
|
||||||
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
"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=="],
|
"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/@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=="],
|
"@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=="],
|
"@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=="],
|
"@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=="],
|
"@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=="],
|
"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=="],
|
"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=="],
|
"parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ bun run hardening:strict
|
|||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
- `doctor:runtime` fails fast if `CLAUDE_CODE_USE_OPENAI=1` with a placeholder key or a missing key for non-local providers.
|
- `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`.
|
- Codex profiles validate `CODEX_API_KEY` or the Codex CLI auth file and probe `POST /responses` instead of `GET /models`.
|
||||||
|
|
||||||
## Provider Launch Profiles
|
## Provider Launch Profiles
|
||||||
|
|||||||
@@ -66,6 +66,33 @@ openclaude
|
|||||||
|
|
||||||
No API key is needed for Ollama local models.
|
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
|
## 4. If `openclaude` Is Not Found
|
||||||
|
|
||||||
Close the terminal, open a new one, and try again:
|
Close the terminal, open a new one, and try again:
|
||||||
@@ -89,6 +116,14 @@ Check the basics:
|
|||||||
- make sure Ollama is running
|
- make sure Ollama is running
|
||||||
- make sure the model was pulled successfully
|
- 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
|
## 6. Updating OpenClaude
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -66,6 +66,33 @@ openclaude
|
|||||||
|
|
||||||
No API key is needed for Ollama local models.
|
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
|
## 4. If `openclaude` Is Not Found
|
||||||
|
|
||||||
Close PowerShell, open a new one, and try again:
|
Close PowerShell, open a new one, and try again:
|
||||||
@@ -89,6 +116,14 @@ Check the basics:
|
|||||||
- make sure Ollama is running
|
- make sure Ollama is running
|
||||||
- make sure the model was pulled successfully
|
- 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
|
## 6. Updating OpenClaude
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
|||||||
@@ -49,6 +49,18 @@ def normalize_ollama_model(model_name: str) -> str:
|
|||||||
return model_name
|
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]:
|
def anthropic_to_ollama_messages(messages: list[dict]) -> list[dict]:
|
||||||
ollama_messages = []
|
ollama_messages = []
|
||||||
for msg in 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})
|
ollama_messages.append({"role": role, "content": content})
|
||||||
elif isinstance(content, list):
|
elif isinstance(content, list):
|
||||||
text_parts = []
|
text_parts = []
|
||||||
|
image_parts = []
|
||||||
for block in content:
|
for block in content:
|
||||||
if isinstance(block, dict):
|
if isinstance(block, dict):
|
||||||
if block.get("type") == "text":
|
if block.get("type") == "text":
|
||||||
text_parts.append(block.get("text", ""))
|
text_parts.append(block.get("text", ""))
|
||||||
elif block.get("type") == "image":
|
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):
|
elif isinstance(block, str):
|
||||||
text_parts.append(block)
|
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
|
return ollama_messages
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -34,6 +34,8 @@
|
|||||||
"test:provider-recommendation": "bun test src/utils/providerRecommendation.test.ts src/utils/providerProfile.test.ts",
|
"test:provider-recommendation": "bun test src/utils/providerRecommendation.test.ts src/utils/providerProfile.test.ts",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"smoke": "bun run build && node dist/cli.mjs --version",
|
"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",
|
"test:provider": "bun test src/services/api/*.test.ts src/utils/context.test.ts",
|
||||||
"doctor:runtime": "bun run scripts/system-check.ts",
|
"doctor:runtime": "bun run scripts/system-check.ts",
|
||||||
"doctor:runtime:json": "bun run scripts/system-check.ts --json",
|
"doctor:runtime:json": "bun run scripts/system-check.ts --json",
|
||||||
@@ -51,6 +53,7 @@
|
|||||||
"@anthropic-ai/vertex-sdk": "0.14.4",
|
"@anthropic-ai/vertex-sdk": "0.14.4",
|
||||||
"@commander-js/extra-typings": "12.1.0",
|
"@commander-js/extra-typings": "12.1.0",
|
||||||
"@growthbook/growthbook": "1.6.5",
|
"@growthbook/growthbook": "1.6.5",
|
||||||
|
"@mendable/firecrawl-js": "4.18.1",
|
||||||
"@modelcontextprotocol/sdk": "1.29.0",
|
"@modelcontextprotocol/sdk": "1.29.0",
|
||||||
"@opentelemetry/api": "1.9.1",
|
"@opentelemetry/api": "1.9.1",
|
||||||
"@opentelemetry/api-logs": "0.214.0",
|
"@opentelemetry/api-logs": "0.214.0",
|
||||||
@@ -73,7 +76,8 @@
|
|||||||
"cli-highlight": "2.1.11",
|
"cli-highlight": "2.1.11",
|
||||||
"code-excerpt": "4.0.0",
|
"code-excerpt": "4.0.0",
|
||||||
"commander": "12.1.0",
|
"commander": "12.1.0",
|
||||||
"diff": "7.0.0",
|
"diff": "8.0.3",
|
||||||
|
"duck-duck-scrape": "^2.2.7",
|
||||||
"emoji-regex": "10.6.0",
|
"emoji-regex": "10.6.0",
|
||||||
"env-paths": "3.0.0",
|
"env-paths": "3.0.0",
|
||||||
"execa": "9.6.1",
|
"execa": "9.6.1",
|
||||||
@@ -86,7 +90,7 @@
|
|||||||
"ignore": "7.0.5",
|
"ignore": "7.0.5",
|
||||||
"indent-string": "5.0.0",
|
"indent-string": "5.0.0",
|
||||||
"jsonc-parser": "3.3.1",
|
"jsonc-parser": "3.3.1",
|
||||||
"lodash-es": "4.17.23",
|
"lodash-es": "4.18.0",
|
||||||
"lru-cache": "11.2.7",
|
"lru-cache": "11.2.7",
|
||||||
"marked": "15.0.12",
|
"marked": "15.0.12",
|
||||||
"p-map": "7.0.4",
|
"p-map": "7.0.4",
|
||||||
@@ -97,6 +101,7 @@
|
|||||||
"react-compiler-runtime": "1.0.0",
|
"react-compiler-runtime": "1.0.0",
|
||||||
"react-reconciler": "0.33.0",
|
"react-reconciler": "0.33.0",
|
||||||
"semver": "7.7.4",
|
"semver": "7.7.4",
|
||||||
|
"sharp": "^0.34.5",
|
||||||
"shell-quote": "1.8.3",
|
"shell-quote": "1.8.3",
|
||||||
"signal-exit": "4.1.0",
|
"signal-exit": "4.1.0",
|
||||||
"stack-utils": "2.0.6",
|
"stack-utils": "2.0.6",
|
||||||
@@ -137,7 +142,7 @@
|
|||||||
"ollama",
|
"ollama",
|
||||||
"gemini"
|
"gemini"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "SEE LICENSE FILE",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,7 +158,6 @@ export async function handleBgFlag() { throw new Error("Background sessions are
|
|||||||
'modifiers-napi',
|
'modifiers-napi',
|
||||||
'url-handler-napi',
|
'url-handler-napi',
|
||||||
'color-diff-napi',
|
'color-diff-napi',
|
||||||
'sharp',
|
|
||||||
'@anthropic-ai/mcpb',
|
'@anthropic-ai/mcpb',
|
||||||
'@ant/claude-for-chrome-mcp',
|
'@ant/claude-for-chrome-mcp',
|
||||||
'@anthropic-ai/sandbox-runtime',
|
'@anthropic-ai/sandbox-runtime',
|
||||||
@@ -275,6 +274,8 @@ export const SeverityNumber = {};
|
|||||||
'@opentelemetry/sdk-logs',
|
'@opentelemetry/sdk-logs',
|
||||||
'@opentelemetry/sdk-metrics',
|
'@opentelemetry/sdk-metrics',
|
||||||
'@opentelemetry/semantic-conventions',
|
'@opentelemetry/semantic-conventions',
|
||||||
|
// Native image processing
|
||||||
|
'sharp',
|
||||||
// Cloud provider SDKs
|
// Cloud provider SDKs
|
||||||
'@aws-sdk/client-bedrock',
|
'@aws-sdk/client-bedrock',
|
||||||
'@aws-sdk/client-bedrock-runtime',
|
'@aws-sdk/client-bedrock-runtime',
|
||||||
|
|||||||
@@ -197,6 +197,19 @@ export function classifyFetchError() { return 'disabled'; }
|
|||||||
'components/FeedbackSurvey/submitTranscriptShare': `
|
'components/FeedbackSurvey/submitTranscriptShare': `
|
||||||
export async function submitTranscriptShare() { return { success: false }; }
|
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 = {
|
export const noTelemetryPlugin: BunPlugin = {
|
||||||
@@ -205,9 +218,7 @@ export const noTelemetryPlugin: BunPlugin = {
|
|||||||
for (const [modulePath, contents] of Object.entries(stubs)) {
|
for (const [modulePath, contents] of Object.entries(stubs)) {
|
||||||
// Build regex that matches the resolved file path on any OS
|
// Build regex that matches the resolved file path on any OS
|
||||||
// e.g. "services/analytics/growthbook" → /services[/\\]analytics[/\\]growthbook\.(ts|js)$/
|
// e.g. "services/analytics/growthbook" → /services[/\\]analytics[/\\]growthbook\.(ts|js)$/
|
||||||
const escaped = modulePath
|
const escaped = escapeForResolvedPathRegex(modulePath)
|
||||||
.replace(/\//g, '[/\\\\]')
|
|
||||||
.replace(/\./g, '\\.')
|
|
||||||
const filter = new RegExp(`${escaped}\\.(ts|js)$`)
|
const filter = new RegExp(`${escaped}\\.(ts|js)$`)
|
||||||
|
|
||||||
build.onLoad({ filter }, () => ({
|
build.onLoad({ filter }, () => ({
|
||||||
|
|||||||
@@ -120,23 +120,18 @@ function applyFastFlags(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|
||||||
function printSummary(profile: ProviderProfile, env: NodeJS.ProcessEnv): void {
|
function printSummary(profile: ProviderProfile): void {
|
||||||
console.log(`Launching profile: ${profile}`)
|
console.log(`Launching profile: ${profile}`)
|
||||||
if (profile === 'gemini') {
|
if (profile === 'gemini') {
|
||||||
console.log(`GEMINI_MODEL=${env.GEMINI_MODEL}`)
|
console.log('Using configured Gemini provider settings.')
|
||||||
console.log(`GEMINI_API_KEY_SET=${Boolean(env.GEMINI_API_KEY)}`)
|
|
||||||
} else if (profile === 'codex') {
|
} else if (profile === 'codex') {
|
||||||
console.log(`OPENAI_BASE_URL=${env.OPENAI_BASE_URL}`)
|
console.log('Using configured Codex/OpenAI-compatible provider settings.')
|
||||||
console.log(`OPENAI_MODEL=${env.OPENAI_MODEL}`)
|
|
||||||
console.log(`CODEX_API_KEY_SET=${Boolean(resolveCodexApiCredentials(env).apiKey)}`)
|
|
||||||
} else if (profile === 'atomic-chat') {
|
} else if (profile === 'atomic-chat') {
|
||||||
console.log(`OPENAI_BASE_URL=${env.OPENAI_BASE_URL}`)
|
console.log('Using configured Atomic Chat provider settings.')
|
||||||
console.log(`OPENAI_MODEL=${env.OPENAI_MODEL}`)
|
} else if (profile === 'ollama') {
|
||||||
console.log('OPENAI_API_KEY_SET=false (local provider, no key required)')
|
console.log('Using configured Ollama provider settings.')
|
||||||
} else {
|
} else {
|
||||||
console.log(`OPENAI_BASE_URL=${env.OPENAI_BASE_URL}`)
|
console.log('Using configured OpenAI-compatible provider settings.')
|
||||||
console.log(`OPENAI_MODEL=${env.OPENAI_MODEL}`)
|
|
||||||
console.log(`OPENAI_API_KEY_SET=${Boolean(env.OPENAI_API_KEY)}`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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)
|
const doctorCode = await runProcess('bun', ['run', 'scripts/system-check.ts'], env)
|
||||||
if (doctorCode !== 0) {
|
if (doctorCode !== 0) {
|
||||||
|
|||||||
42
scripts/system-check.test.ts
Normal file
42
scripts/system-check.test.ts
Normal 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.',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -58,6 +58,31 @@ function parseOptions(argv: string[]): CliOptions {
|
|||||||
return options
|
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 {
|
function checkNodeVersion(): CheckResult {
|
||||||
const raw = process.versions.node
|
const raw = process.versions.node
|
||||||
const major = Number(raw.split('.')[0] ?? '0')
|
const major = Number(raw.split('.')[0] ?? '0')
|
||||||
@@ -284,6 +309,7 @@ async function checkBaseUrlReachability(): Promise<CheckResult> {
|
|||||||
headers['chatgpt-account-id'] = credentials.accountId
|
headers['chatgpt-account-id'] = credentials.accountId
|
||||||
}
|
}
|
||||||
headers['Content-Type'] = 'application/json'
|
headers['Content-Type'] = 'application/json'
|
||||||
|
headers.originator = 'openclaude'
|
||||||
method = 'POST'
|
method = 'POST'
|
||||||
body = JSON.stringify({
|
body = JSON.stringify({
|
||||||
model: request.resolvedModel,
|
model: request.resolvedModel,
|
||||||
@@ -315,7 +341,17 @@ async function checkBaseUrlReachability(): Promise<CheckResult> {
|
|||||||
return pass('Provider reachability', `Reached ${endpoint} (status ${response.status}).`)
|
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) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const message = error instanceof Error ? error.message : String(error)
|
||||||
return fail('Provider reachability', `Failed to reach ${endpoint}: ${message}`)
|
return fail('Provider reachability', `Failed to reach ${endpoint}: ${message}`)
|
||||||
@@ -430,6 +466,7 @@ function writeJsonReport(
|
|||||||
options: CliOptions,
|
options: CliOptions,
|
||||||
results: CheckResult[],
|
results: CheckResult[],
|
||||||
): void {
|
): void {
|
||||||
|
const envSummary = serializeSafeEnvSummary()
|
||||||
const payload = {
|
const payload = {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
cwd: process.cwd(),
|
cwd: process.cwd(),
|
||||||
@@ -438,12 +475,24 @@ function writeJsonReport(
|
|||||||
passed: results.filter(result => result.ok).length,
|
passed: results.filter(result => result.ok).length,
|
||||||
failed: results.filter(result => !result.ok).length,
|
failed: results.filter(result => !result.ok).length,
|
||||||
},
|
},
|
||||||
env: serializeSafeEnvSummary(),
|
env: envSummary,
|
||||||
results,
|
results,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.json) {
|
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) {
|
if (options.outFile) {
|
||||||
@@ -491,6 +540,8 @@ async function main(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await main()
|
if (import.meta.main) {
|
||||||
|
await main()
|
||||||
|
}
|
||||||
|
|
||||||
export {}
|
export {}
|
||||||
|
|||||||
46
scripts/verify-no-phone-home.sh
Normal file
46
scripts/verify-no-phone-home.sh
Normal 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
|
||||||
43
scripts/verify-no-phone-home.ts
Normal file
43
scripts/verify-no-phone-home.ts
Normal 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)
|
||||||
@@ -228,9 +228,14 @@ class SmartRouter:
|
|||||||
return min(available, key=lambda p: p.score(self.strategy))
|
return min(available, key=lambda p: p.score(self.strategy))
|
||||||
|
|
||||||
def get_model_for_provider(
|
def get_model_for_provider(
|
||||||
self, provider: Provider, claude_model: str
|
self,
|
||||||
|
provider: Provider,
|
||||||
|
claude_model: str,
|
||||||
|
is_large_request: bool = False,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Map a Claude model name to the provider's actual model."""
|
"""Map a Claude model name to the provider's actual model."""
|
||||||
|
if is_large_request:
|
||||||
|
return provider.big_model
|
||||||
is_large = any(
|
is_large = any(
|
||||||
keyword in claude_model.lower()
|
keyword in claude_model.lower()
|
||||||
for keyword in ["opus", "sonnet", "large", "big"]
|
for keyword in ["opus", "sonnet", "large", "big"]
|
||||||
@@ -289,7 +294,11 @@ class SmartRouter:
|
|||||||
)
|
)
|
||||||
|
|
||||||
provider = min(available, key=lambda p: p.score(self.strategy))
|
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(
|
logger.debug(
|
||||||
f"SmartRouter: routing to {provider.name}/{model} "
|
f"SmartRouter: routing to {provider.name}/{model} "
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import type { AttributionState } from './utils/commitAttribution.js'
|
|||||||
import { getGlobalConfig } from './utils/config.js'
|
import { getGlobalConfig } from './utils/config.js'
|
||||||
import { getCwd } from './utils/cwd.js'
|
import { getCwd } from './utils/cwd.js'
|
||||||
import { isBareMode, isEnvTruthy } from './utils/envUtils.js'
|
import { isBareMode, isEnvTruthy } from './utils/envUtils.js'
|
||||||
|
import { logForDebugging } from './utils/debug.js'
|
||||||
import { getFastModeState } from './utils/fastMode.js'
|
import { getFastModeState } from './utils/fastMode.js'
|
||||||
import {
|
import {
|
||||||
type FileHistoryState,
|
type FileHistoryState,
|
||||||
@@ -695,9 +696,11 @@ export class QueryEngine {
|
|||||||
// progress are now recorded inline (their switch cases below), but
|
// progress are now recorded inline (their switch cases below), but
|
||||||
// this flush still matters for the preservedSegment tail walk.
|
// this flush still matters for the preservedSegment tail walk.
|
||||||
// If the SDK subprocess restarts before then (claude-desktop kills
|
// If the SDK subprocess restarts before then (claude-desktop kills
|
||||||
// between turns), tailUuid points to a never-written message →
|
// between turns), tailUuid can point to a never-written message. In
|
||||||
// applyPreservedSegmentRelinks fails its tail→head walk → returns
|
// that case strip preservedSegment before transcript persistence so
|
||||||
// without pruning → resume loads full pre-compact history.
|
// resume falls back to ordinary boundary pruning instead of relying on
|
||||||
|
// broken relink metadata.
|
||||||
|
let transcriptMessage = message
|
||||||
if (
|
if (
|
||||||
persistSession &&
|
persistSession &&
|
||||||
message.type === 'system' &&
|
message.type === 'system' &&
|
||||||
@@ -710,10 +713,21 @@ export class QueryEngine {
|
|||||||
)
|
)
|
||||||
if (tailIdx !== -1) {
|
if (tailIdx !== -1) {
|
||||||
await recordTranscript(this.mutableMessages.slice(0, 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) {
|
if (persistSession) {
|
||||||
// Fire-and-forget for assistant messages. claude.ts yields one
|
// Fire-and-forget for assistant messages. claude.ts yields one
|
||||||
// assistant message per content block, then mutates the last
|
// assistant message per content block, then mutates the last
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { randomBytes } from 'crypto'
|
import { randomInt } from 'crypto'
|
||||||
import type { AppState } from './state/AppState.js'
|
import type { AppState } from './state/AppState.js'
|
||||||
import type { AgentId } from './types/ids.js'
|
import type { AgentId } from './types/ids.js'
|
||||||
import { getTaskOutputPath } from './utils/task/diskOutput.js'
|
import { getTaskOutputPath } from './utils/task/diskOutput.js'
|
||||||
@@ -97,10 +97,9 @@ const TASK_ID_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz'
|
|||||||
|
|
||||||
export function generateTaskId(type: TaskType): string {
|
export function generateTaskId(type: TaskType): string {
|
||||||
const prefix = getTaskIdPrefix(type)
|
const prefix = getTaskIdPrefix(type)
|
||||||
const bytes = randomBytes(8)
|
|
||||||
let id = prefix
|
let id = prefix
|
||||||
for (let i = 0; i < 8; i++) {
|
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
|
return id
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,6 +176,8 @@ export type ToolUseContext = {
|
|||||||
querySource?: QuerySource
|
querySource?: QuerySource
|
||||||
/** Optional callback to get the latest tools (e.g., after MCP servers connect mid-query) */
|
/** Optional callback to get the latest tools (e.g., after MCP servers connect mid-query) */
|
||||||
refreshTools?: () => Tools
|
refreshTools?: () => Tools
|
||||||
|
/** Per-agent provider override from agentRouting config */
|
||||||
|
providerOverride?: { model: string; baseURL: string; apiKey: string }
|
||||||
}
|
}
|
||||||
abortController: AbortController
|
abortController: AbortController
|
||||||
readFileState: FileStateCache
|
readFileState: FileStateCache
|
||||||
|
|||||||
85
src/bridge/sessionRunner.test.ts
Normal file
85
src/bridge/sessionRunner.test.ts
Normal 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')
|
||||||
|
})
|
||||||
@@ -16,6 +16,69 @@ import type {
|
|||||||
const MAX_ACTIVITIES = 10
|
const MAX_ACTIVITIES = 10
|
||||||
const MAX_STDERR_LINES = 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.
|
* Sanitize a session ID for use in file names.
|
||||||
* Strips any characters that could cause path traversal (e.g. `../`, `/`)
|
* Strips any characters that could cause path traversal (e.g. `../`, `/`)
|
||||||
@@ -303,24 +366,12 @@ export function createSessionSpawner(deps: SessionSpawnerDeps): SessionSpawner {
|
|||||||
: []),
|
: []),
|
||||||
]
|
]
|
||||||
|
|
||||||
const env: NodeJS.ProcessEnv = {
|
const env = buildChildEnv(deps.env, {
|
||||||
...deps.env,
|
accessToken: opts.accessToken,
|
||||||
// Strip the bridge's OAuth token so the child CC process uses
|
useCcrV2: opts.useCcrV2,
|
||||||
// the session access token for inference instead.
|
workerEpoch: opts.workerEpoch,
|
||||||
CLAUDE_CODE_OAUTH_TOKEN: undefined,
|
sandbox: deps.sandbox,
|
||||||
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),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
deps.onDebug(
|
deps.onDebug(
|
||||||
`[bridge:session] Spawning sessionId=${opts.sessionId} sdkUrl=${opts.sdkUrl} accessToken=${opts.accessToken ? 'present' : 'MISSING'}`,
|
`[bridge:session] Spawning sessionId=${opts.sessionId} sdkUrl=${opts.sdkUrl} accessToken=${opts.accessToken ? 'present' : 'MISSING'}`,
|
||||||
|
|||||||
36
src/bridge/workSecret.test.ts
Normal file
36
src/bridge/workSecret.test.ts
Normal 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')
|
||||||
|
})
|
||||||
@@ -39,8 +39,8 @@ export function decodeWorkSecret(secret: string): WorkSecret {
|
|||||||
* and /v1/ for production (Envoy rewrites /v1/ → /v2/).
|
* and /v1/ for production (Envoy rewrites /v1/ → /v2/).
|
||||||
*/
|
*/
|
||||||
export function buildSdkUrl(apiBaseUrl: string, sessionId: string): string {
|
export function buildSdkUrl(apiBaseUrl: string, sessionId: string): string {
|
||||||
const isLocalhost =
|
const hostname = new URL(apiBaseUrl).hostname
|
||||||
apiBaseUrl.includes('localhost') || apiBaseUrl.includes('127.0.0.1')
|
const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1'
|
||||||
const protocol = isLocalhost ? 'ws' : 'wss'
|
const protocol = isLocalhost ? 'ws' : 'wss'
|
||||||
const version = isLocalhost ? 'v2' : 'v1'
|
const version = isLocalhost ? 'v2' : 'v1'
|
||||||
const host = apiBaseUrl.replace(/^https?:\/\//, '').replace(/\/+$/, '')
|
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
@@ -116,7 +116,6 @@ export async function autoModeCritiqueHandler(options: {
|
|||||||
querySource: 'auto_mode_critique',
|
querySource: 'auto_mode_critique',
|
||||||
model,
|
model,
|
||||||
system: CRITIQUE_SYSTEM_PROMPT,
|
system: CRITIQUE_SYSTEM_PROMPT,
|
||||||
skipSystemPromptPrefix: true,
|
|
||||||
max_tokens: 4096,
|
max_tokens: 4096,
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,5 @@
|
|||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
|
import { getAPIProvider } from 'src/utils/model/providers.js'
|
||||||
import { logEvent } from 'src/services/analytics/index.js'
|
import { logEvent } from 'src/services/analytics/index.js'
|
||||||
import {
|
import {
|
||||||
getLatestVersion,
|
getLatestVersion,
|
||||||
@@ -28,6 +29,19 @@ import { gte } from 'src/utils/semver.js'
|
|||||||
import { getInitialSettings } from 'src/utils/settings/settings.js'
|
import { getInitialSettings } from 'src/utils/settings/settings.js'
|
||||||
|
|
||||||
export async function update() {
|
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', {})
|
logEvent('tengu_update_check', {})
|
||||||
writeToStdout(`Current version: ${MACRO.VERSION}\n`)
|
writeToStdout(`Current version: ${MACRO.VERSION}\n`)
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import config from './commands/config/index.js'
|
|||||||
import { context, contextNonInteractive } from './commands/context/index.js'
|
import { context, contextNonInteractive } from './commands/context/index.js'
|
||||||
import cost from './commands/cost/index.js'
|
import cost from './commands/cost/index.js'
|
||||||
import diff from './commands/diff/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 ctx_viz from './commands/ctx_viz/index.js'
|
||||||
import doctor from './commands/doctor/index.js'
|
import doctor from './commands/doctor/index.js'
|
||||||
import onboardGithub from './commands/onboard-github/index.js'
|
import onboardGithub from './commands/onboard-github/index.js'
|
||||||
@@ -274,6 +275,7 @@ const COMMANDS = memoize((): Command[] => [
|
|||||||
contextNonInteractive,
|
contextNonInteractive,
|
||||||
cost,
|
cost,
|
||||||
diff,
|
diff,
|
||||||
|
dream,
|
||||||
doctor,
|
doctor,
|
||||||
effort,
|
effort,
|
||||||
exit,
|
exit,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -9,4 +9,3 @@ export async function call(onDone: LocalJSXCommandOnDone, context: ToolUseContex
|
|||||||
const tools = getTools(permissionContext);
|
const tools = getTools(permissionContext);
|
||||||
return <AgentsMenu tools={tools} onExit={onDone} />;
|
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
@@ -4,4 +4,3 @@ import type { LocalJSXCommandCall } from '../../types/command.js';
|
|||||||
export const call: LocalJSXCommandCall = async (onDone, context) => {
|
export const call: LocalJSXCommandCall = async (onDone, context) => {
|
||||||
return <Settings onClose={onDone} context={context} defaultTab="Config" />;
|
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
@@ -6,4 +6,3 @@ export async function call(onDone: (result?: string, options?: {
|
|||||||
}) => void): Promise<React.ReactNode> {
|
}) => void): Promise<React.ReactNode> {
|
||||||
return <DesktopHandoff onDone={onDone} />;
|
return <DesktopHandoff onDone={onDone} />;
|
||||||
}
|
}
|
||||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwiRGVza3RvcEhhbmRvZmYiLCJjYWxsIiwib25Eb25lIiwicmVzdWx0Iiwib3B0aW9ucyIsImRpc3BsYXkiLCJQcm9taXNlIiwiUmVhY3ROb2RlIl0sInNvdXJjZXMiOlsiZGVza3RvcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBDb21tYW5kUmVzdWx0RGlzcGxheSB9IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgRGVza3RvcEhhbmRvZmYgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0Rlc2t0b3BIYW5kb2ZmLmpzJ1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiAoXG4gICAgcmVzdWx0Pzogc3RyaW5nLFxuICAgIG9wdGlvbnM/OiB7IGRpc3BsYXk/OiBDb21tYW5kUmVzdWx0RGlzcGxheSB9LFxuICApID0+IHZvaWQsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICByZXR1cm4gPERlc2t0b3BIYW5kb2ZmIG9uRG9uZT17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixjQUFjQyxvQkFBb0IsUUFBUSxtQkFBbUI7QUFDN0QsU0FBU0MsY0FBYyxRQUFRLG9DQUFvQztBQUVuRSxPQUFPLGVBQWVDLElBQUlBLENBQ3hCQyxNQUFNLEVBQUUsQ0FDTkMsTUFBZSxDQUFSLEVBQUUsTUFBTSxFQUNmQyxPQUE0QyxDQUFwQyxFQUFFO0VBQUVDLE9BQU8sQ0FBQyxFQUFFTixvQkFBb0I7QUFBQyxDQUFDLEVBQzVDLEdBQUcsSUFBSSxDQUNWLEVBQUVPLE9BQU8sQ0FBQ1IsS0FBSyxDQUFDUyxTQUFTLENBQUMsQ0FBQztFQUMxQixPQUFPLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDTCxNQUFNLENBQUMsR0FBRztBQUMzQyIsImlnbm9yZUxpc3QiOltdfQ==
|
|
||||||
@@ -6,4 +6,3 @@ export const call: LocalJSXCommandCall = async (onDone, context) => {
|
|||||||
} = await import('../../components/diff/DiffDialog.js');
|
} = await import('../../components/diff/DiffDialog.js');
|
||||||
return <DiffDialog messages={context.messages} onDone={onDone} />;
|
return <DiffDialog messages={context.messages} onDone={onDone} />;
|
||||||
};
|
};
|
||||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjYWxsIiwib25Eb25lIiwiY29udGV4dCIsIkRpZmZEaWFsb2ciLCJtZXNzYWdlcyJdLCJzb3VyY2VzIjpbImRpZmYudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDYWxsIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcblxuZXhwb3J0IGNvbnN0IGNhbGw6IExvY2FsSlNYQ29tbWFuZENhbGwgPSBhc3luYyAob25Eb25lLCBjb250ZXh0KSA9PiB7XG4gIGNvbnN0IHsgRGlmZkRpYWxvZyB9ID0gYXdhaXQgaW1wb3J0KCcuLi8uLi9jb21wb25lbnRzL2RpZmYvRGlmZkRpYWxvZy5qcycpXG4gIHJldHVybiA8RGlmZkRpYWxvZyBtZXNzYWdlcz17Y29udGV4dC5tZXNzYWdlc30gb25Eb25lPXtvbkRvbmV9IC8+XG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBRyxNQUFBQyxDQUFPQyxNQUFNLEVBQUVDLE9BQU8sS0FBSztFQUNsRSxNQUFNO0lBQUVDO0VBQVcsQ0FBQyxHQUFHLE1BQU0sTUFBTSxDQUFDLHFDQUFxQyxDQUFDO0VBQzFFLE9BQU8sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUNELE9BQU8sQ0FBQ0UsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUNILE1BQU0sQ0FBQyxHQUFHO0FBQ25FLENBQUMiLCJpZ25vcmVMaXN0IjpbXX0=
|
|
||||||
@@ -4,4 +4,3 @@ import type { LocalJSXCommandCall } from '../../types/command.js';
|
|||||||
export const call: LocalJSXCommandCall = (onDone, _context, _args) => {
|
export const call: LocalJSXCommandCall = (onDone, _context, _args) => {
|
||||||
return Promise.resolve(<Doctor onDone={onDone} />);
|
return Promise.resolve(<Doctor onDone={onDone} />);
|
||||||
};
|
};
|
||||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkRvY3RvciIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjYWxsIiwib25Eb25lIiwiX2NvbnRleHQiLCJfYXJncyIsIlByb21pc2UiLCJyZXNvbHZlIl0sInNvdXJjZXMiOlsiZG9jdG9yLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBEb2N0b3IgfSBmcm9tICcuLi8uLi9zY3JlZW5zL0RvY3Rvci5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kQ2FsbCB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5cbmV4cG9ydCBjb25zdCBjYWxsOiBMb2NhbEpTWENvbW1hbmRDYWxsID0gKG9uRG9uZSwgX2NvbnRleHQsIF9hcmdzKSA9PiB7XG4gIHJldHVybiBQcm9taXNlLnJlc29sdmUoPERvY3RvciBvbkRvbmU9e29uRG9uZX0gLz4pXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLE1BQU0sUUFBUSx5QkFBeUI7QUFDaEQsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBR0MsQ0FBQ0MsTUFBTSxFQUFFQyxRQUFRLEVBQUVDLEtBQUssS0FBSztFQUNwRSxPQUFPQyxPQUFPLENBQUNDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQ0osTUFBTSxDQUFDLEdBQUcsQ0FBQztBQUNwRCxDQUFDIiwiaWdub3JlTGlzdCI6W119
|
|
||||||
68
src/commands/dream/dream.ts
Normal file
68
src/commands/dream/dream.ts
Normal 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
|
||||||
1
src/commands/dream/index.ts
Normal file
1
src/commands/dream/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default } from './dream.js'
|
||||||
File diff suppressed because one or more lines are too long
@@ -30,4 +30,3 @@ export async function call(onDone: LocalJSXCommandOnDone): Promise<React.ReactNo
|
|||||||
await gracefulShutdown(0, 'prompt_input_exit');
|
await gracefulShutdown(0, 'prompt_input_exit');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwic3Bhd25TeW5jIiwic2FtcGxlIiwiUmVhY3QiLCJFeGl0RmxvdyIsIkxvY2FsSlNYQ29tbWFuZE9uRG9uZSIsImlzQmdTZXNzaW9uIiwiZ3JhY2VmdWxTaHV0ZG93biIsImdldEN1cnJlbnRXb3JrdHJlZVNlc3Npb24iLCJHT09EQllFX01FU1NBR0VTIiwiZ2V0UmFuZG9tR29vZGJ5ZU1lc3NhZ2UiLCJjYWxsIiwib25Eb25lIiwiUHJvbWlzZSIsIlJlYWN0Tm9kZSIsInN0ZGlvIiwic2hvd1dvcmt0cmVlIl0sInNvdXJjZXMiOlsiZXhpdC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZmVhdHVyZSB9IGZyb20gJ2J1bjpidW5kbGUnXG5pbXBvcnQgeyBzcGF3blN5bmMgfSBmcm9tICdjaGlsZF9wcm9jZXNzJ1xuaW1wb3J0IHNhbXBsZSBmcm9tICdsb2Rhc2gtZXMvc2FtcGxlLmpzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBFeGl0RmxvdyB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvRXhpdEZsb3cuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5pbXBvcnQgeyBpc0JnU2Vzc2lvbiB9IGZyb20gJy4uLy4uL3V0aWxzL2NvbmN1cnJlbnRTZXNzaW9ucy5qcydcbmltcG9ydCB7IGdyYWNlZnVsU2h1dGRvd24gfSBmcm9tICcuLi8uLi91dGlscy9ncmFjZWZ1bFNodXRkb3duLmpzJ1xuaW1wb3J0IHsgZ2V0Q3VycmVudFdvcmt0cmVlU2Vzc2lvbiB9IGZyb20gJy4uLy4uL3V0aWxzL3dvcmt0cmVlLmpzJ1xuXG5jb25zdCBHT09EQllFX01FU1NBR0VTID0gWydHb29kYnllIScsICdTZWUgeWEhJywgJ0J5ZSEnLCAnQ2F0Y2ggeW91IGxhdGVyISddXG5cbmZ1bmN0aW9uIGdldFJhbmRvbUdvb2RieWVNZXNzYWdlKCk6IHN0cmluZyB7XG4gIHJldHVybiBzYW1wbGUoR09PREJZRV9NRVNTQUdFUykgPz8gJ0dvb2RieWUhJ1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICAvLyBJbnNpZGUgYSBgY2xhdWRlIC0tYmdgIHRtdXggc2Vzc2lvbjogZGV0YWNoIGluc3RlYWQgb2Yga2lsbC4gVGhlIFJFUExcbiAgLy8ga2VlcHMgcnVubmluZzsgYGNsYXVkZSBhdHRhY2hgIGNhbiByZWNvbm5lY3QuIENvdmVycyAvZXhpdCwgL3F1aXQsXG4gIC8vIGN0cmwrYywgY3RybCtkIOKAlCBhbGwgZnVubmVsIHRocm91Z2ggaGVyZSB2aWEgUkVQTCdzIGhhbmRsZUV4aXQuXG4gIGlmIChmZWF0dXJlKCdCR19TRVNTSU9OUycpICYmIGlzQmdTZXNzaW9uKCkpIHtcbiAgICBvbkRvbmUoKVxuICAgIHNwYXduU3luYygndG11eCcsIFsnZGV0YWNoLWNsaWVudCddLCB7IHN0ZGlvOiAnaWdub3JlJyB9KVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBjb25zdCBzaG93V29ya3RyZWUgPSBnZXRDdXJyZW50V29ya3RyZWVTZXNzaW9uKCkgIT09IG51bGxcblxuICBpZiAoc2hvd1dvcmt0cmVlKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxFeGl0Rmxvd1xuICAgICAgICBzaG93V29ya3RyZWU9e3Nob3dXb3JrdHJlZX1cbiAgICAgICAgb25Eb25lPXtvbkRvbmV9XG4gICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBvbkRvbmUoKX1cbiAgICAgIC8+XG4gICAgKVxuICB9XG5cbiAgb25Eb25lKGdldFJhbmRvbUdvb2RieWVNZXNzYWdlKCkpXG4gIGF3YWl0IGdyYWNlZnVsU2h1dGRvd24oMCwgJ3Byb21wdF9pbnB1dF9leGl0JylcbiAgcmV0dXJuIG51bGxcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsU0FBU0MsU0FBUyxRQUFRLGVBQWU7QUFDekMsT0FBT0MsTUFBTSxNQUFNLHFCQUFxQjtBQUN4QyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFFBQVEsUUFBUSw4QkFBOEI7QUFDdkQsY0FBY0MscUJBQXFCLFFBQVEsd0JBQXdCO0FBQ25FLFNBQVNDLFdBQVcsUUFBUSxtQ0FBbUM7QUFDL0QsU0FBU0MsZ0JBQWdCLFFBQVEsaUNBQWlDO0FBQ2xFLFNBQVNDLHlCQUF5QixRQUFRLHlCQUF5QjtBQUVuRSxNQUFNQyxnQkFBZ0IsR0FBRyxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLGtCQUFrQixDQUFDO0FBRTVFLFNBQVNDLHVCQUF1QkEsQ0FBQSxDQUFFLEVBQUUsTUFBTSxDQUFDO0VBQ3pDLE9BQU9SLE1BQU0sQ0FBQ08sZ0JBQWdCLENBQUMsSUFBSSxVQUFVO0FBQy9DO0FBRUEsT0FBTyxlQUFlRSxJQUFJQSxDQUN4QkMsTUFBTSxFQUFFUCxxQkFBcUIsQ0FDOUIsRUFBRVEsT0FBTyxDQUFDVixLQUFLLENBQUNXLFNBQVMsQ0FBQyxDQUFDO0VBQzFCO0VBQ0E7RUFDQTtFQUNBLElBQUlkLE9BQU8sQ0FBQyxhQUFhLENBQUMsSUFBSU0sV0FBVyxDQUFDLENBQUMsRUFBRTtJQUMzQ00sTUFBTSxDQUFDLENBQUM7SUFDUlgsU0FBUyxDQUFDLE1BQU0sRUFBRSxDQUFDLGVBQWUsQ0FBQyxFQUFFO01BQUVjLEtBQUssRUFBRTtJQUFTLENBQUMsQ0FBQztJQUN6RCxPQUFPLElBQUk7RUFDYjtFQUVBLE1BQU1DLFlBQVksR0FBR1IseUJBQXlCLENBQUMsQ0FBQyxLQUFLLElBQUk7RUFFekQsSUFBSVEsWUFBWSxFQUFFO0lBQ2hCLE9BQ0UsQ0FBQyxRQUFRLENBQ1AsWUFBWSxDQUFDLENBQUNBLFlBQVksQ0FBQyxDQUMzQixNQUFNLENBQUMsQ0FBQ0osTUFBTSxDQUFDLENBQ2YsUUFBUSxDQUFDLENBQUMsTUFBTUEsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUN6QjtFQUVOO0VBRUFBLE1BQU0sQ0FBQ0YsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO0VBQ2pDLE1BQU1ILGdCQUFnQixDQUFDLENBQUMsRUFBRSxtQkFBbUIsQ0FBQztFQUM5QyxPQUFPLElBQUk7QUFDYiIsImlnbm9yZUxpc3QiOltdfQ==
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -14,4 +14,3 @@ export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXComma
|
|||||||
onDone(success ? 'Login successful' : 'Login interrupted');
|
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
@@ -22,4 +22,3 @@ export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXComma
|
|||||||
const initialDescription = args || '';
|
const initialDescription = args || '';
|
||||||
return renderFeedbackComponent(onDone, context.abortController.signal, context.messages, initialDescription);
|
return renderFeedbackComponent(onDone, context.abortController.signal, context.messages, initialDescription);
|
||||||
}
|
}
|
||||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwiTG9jYWxKU1hDb21tYW5kQ29udGV4dCIsIkZlZWRiYWNrIiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiTWVzc2FnZSIsInJlbmRlckZlZWRiYWNrQ29tcG9uZW50Iiwib25Eb25lIiwicmVzdWx0Iiwib3B0aW9ucyIsImRpc3BsYXkiLCJhYm9ydFNpZ25hbCIsIkFib3J0U2lnbmFsIiwibWVzc2FnZXMiLCJpbml0aWFsRGVzY3JpcHRpb24iLCJiYWNrZ3JvdW5kVGFza3MiLCJ0YXNrSWQiLCJ0eXBlIiwiaWRlbnRpdHkiLCJhZ2VudElkIiwiUmVhY3ROb2RlIiwiY2FsbCIsImNvbnRleHQiLCJhcmdzIiwiUHJvbWlzZSIsImFib3J0Q29udHJvbGxlciIsInNpZ25hbCJdLCJzb3VyY2VzIjpbImZlZWRiYWNrLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHtcbiAgQ29tbWFuZFJlc3VsdERpc3BsYXksXG4gIExvY2FsSlNYQ29tbWFuZENvbnRleHQsXG59IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgRmVlZGJhY2sgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0ZlZWRiYWNrLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRPbkRvbmUgfSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuaW1wb3J0IHR5cGUgeyBNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdHlwZXMvbWVzc2FnZS5qcydcblxuLy8gU2hhcmVkIGZ1bmN0aW9uIHRvIHJlbmRlciB0aGUgRmVlZGJhY2sgY29tcG9uZW50XG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyRmVlZGJhY2tDb21wb25lbnQoXG4gIG9uRG9uZTogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXkgfSxcbiAgKSA9PiB2b2lkLFxuICBhYm9ydFNpZ25hbDogQWJvcnRTaWduYWwsXG4gIG1lc3NhZ2VzOiBNZXNzYWdlW10sXG4gIGluaXRpYWxEZXNjcmlwdGlvbjogc3RyaW5nID0gJycsXG4gIGJhY2tncm91bmRUYXNrczoge1xuICAgIFt0YXNrSWQ6IHN0cmluZ106IHtcbiAgICAgIHR5cGU6IHN0cmluZ1xuICAgICAgaWRlbnRpdHk/OiB7IGFnZW50SWQ6IHN0cmluZyB9XG4gICAgICBtZXNzYWdlcz86IE1lc3NhZ2VbXVxuICAgIH1cbiAgfSA9IHt9LFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgcmV0dXJuIChcbiAgICA8RmVlZGJhY2tcbiAgICAgIGFib3J0U2lnbmFsPXthYm9ydFNpZ25hbH1cbiAgICAgIG1lc3NhZ2VzPXttZXNzYWdlc31cbiAgICAgIGluaXRpYWxEZXNjcmlwdGlvbj17aW5pdGlhbERlc2NyaXB0aW9ufVxuICAgICAgb25Eb25lPXtvbkRvbmV9XG4gICAgICBiYWNrZ3JvdW5kVGFza3M9e2JhY2tncm91bmRUYXNrc31cbiAgICAvPlxuICApXG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjYWxsKFxuICBvbkRvbmU6IExvY2FsSlNYQ29tbWFuZE9uRG9uZSxcbiAgY29udGV4dDogTG9jYWxKU1hDb21tYW5kQ29udGV4dCxcbiAgYXJncz86IHN0cmluZyxcbik6IFByb21pc2U8UmVhY3QuUmVhY3ROb2RlPiB7XG4gIGNvbnN0IGluaXRpYWxEZXNjcmlwdGlvbiA9IGFyZ3MgfHwgJydcbiAgcmV0dXJuIHJlbmRlckZlZWRiYWNrQ29tcG9uZW50KFxuICAgIG9uRG9uZSxcbiAgICBjb250ZXh0LmFib3J0Q29udHJvbGxlci5zaWduYWwsXG4gICAgY29udGV4dC5tZXNzYWdlcyxcbiAgICBpbml0aWFsRGVzY3JpcHRpb24sXG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixjQUNFQyxvQkFBb0IsRUFDcEJDLHNCQUFzQixRQUNqQixtQkFBbUI7QUFDMUIsU0FBU0MsUUFBUSxRQUFRLDhCQUE4QjtBQUN2RCxjQUFjQyxxQkFBcUIsUUFBUSx3QkFBd0I7QUFDbkUsY0FBY0MsT0FBTyxRQUFRLHdCQUF3Qjs7QUFFckQ7QUFDQSxPQUFPLFNBQVNDLHVCQUF1QkEsQ0FDckNDLE1BQU0sRUFBRSxDQUNOQyxNQUFlLENBQVIsRUFBRSxNQUFNLEVBQ2ZDLE9BQTRDLENBQXBDLEVBQUU7RUFBRUMsT0FBTyxDQUFDLEVBQUVULG9CQUFvQjtBQUFDLENBQUMsRUFDNUMsR0FBRyxJQUFJLEVBQ1RVLFdBQVcsRUFBRUMsV0FBVyxFQUN4QkMsUUFBUSxFQUFFUixPQUFPLEVBQUUsRUFDbkJTLGtCQUFrQixFQUFFLE1BQU0sR0FBRyxFQUFFLEVBQy9CQyxlQUFlLEVBQUU7RUFDZixDQUFDQyxNQUFNLEVBQUUsTUFBTSxDQUFDLEVBQUU7SUFDaEJDLElBQUksRUFBRSxNQUFNO0lBQ1pDLFFBQVEsQ0FBQyxFQUFFO01BQUVDLE9BQU8sRUFBRSxNQUFNO0lBQUMsQ0FBQztJQUM5Qk4sUUFBUSxDQUFDLEVBQUVSLE9BQU8sRUFBRTtFQUN0QixDQUFDO0FBQ0gsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUNQLEVBQUVMLEtBQUssQ0FBQ29CLFNBQVMsQ0FBQztFQUNqQixPQUNFLENBQUMsUUFBUSxDQUNQLFdBQVcsQ0FBQyxDQUFDVCxXQUFXLENBQUMsQ0FDekIsUUFBUSxDQUFDLENBQUNFLFFBQVEsQ0FBQyxDQUNuQixrQkFBa0IsQ0FBQyxDQUFDQyxrQkFBa0IsQ0FBQyxDQUN2QyxNQUFNLENBQUMsQ0FBQ1AsTUFBTSxDQUFDLENBQ2YsZUFBZSxDQUFDLENBQUNRLGVBQWUsQ0FBQyxHQUNqQztBQUVOO0FBRUEsT0FBTyxlQUFlTSxJQUFJQSxDQUN4QmQsTUFBTSxFQUFFSCxxQkFBcUIsRUFDN0JrQixPQUFPLEVBQUVwQixzQkFBc0IsRUFDL0JxQixJQUFhLENBQVIsRUFBRSxNQUFNLENBQ2QsRUFBRUMsT0FBTyxDQUFDeEIsS0FBSyxDQUFDb0IsU0FBUyxDQUFDLENBQUM7RUFDMUIsTUFBTU4sa0JBQWtCLEdBQUdTLElBQUksSUFBSSxFQUFFO0VBQ3JDLE9BQU9qQix1QkFBdUIsQ0FDNUJDLE1BQU0sRUFDTmUsT0FBTyxDQUFDRyxlQUFlLENBQUNDLE1BQU0sRUFDOUJKLE9BQU8sQ0FBQ1QsUUFBUSxFQUNoQkMsa0JBQ0YsQ0FBQztBQUNIIiwiaWdub3JlTGlzdCI6W119
|
|
||||||
@@ -8,4 +8,3 @@ export const call: LocalJSXCommandCall = async (onDone, {
|
|||||||
}) => {
|
}) => {
|
||||||
return <HelpV2 commands={commands} onClose={onDone} />;
|
return <HelpV2 commands={commands} onClose={onDone} />;
|
||||||
};
|
};
|
||||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkhlbHBWMiIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjYWxsIiwib25Eb25lIiwib3B0aW9ucyIsImNvbW1hbmRzIl0sInNvdXJjZXMiOlsiaGVscC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBIZWxwVjIgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0hlbHBWMi9IZWxwVjIuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZENhbGwgfSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuXG5leHBvcnQgY29uc3QgY2FsbDogTG9jYWxKU1hDb21tYW5kQ2FsbCA9IGFzeW5jIChcbiAgb25Eb25lLFxuICB7IG9wdGlvbnM6IHsgY29tbWFuZHMgfSB9LFxuKSA9PiB7XG4gIHJldHVybiA8SGVscFYyIGNvbW1hbmRzPXtjb21tYW5kc30gb25DbG9zZT17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLE1BQU0sUUFBUSxtQ0FBbUM7QUFDMUQsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBRyxNQUFBQyxDQUN2Q0MsTUFBTSxFQUNOO0VBQUVDLE9BQU8sRUFBRTtJQUFFQztFQUFTO0FBQUUsQ0FBQyxLQUN0QjtFQUNILE9BQU8sQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUNBLFFBQVEsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDRixNQUFNLENBQUMsR0FBRztBQUN4RCxDQUFDIiwiaWdub3JlTGlzdCI6W119
|
|
||||||
@@ -10,4 +10,3 @@ export const call: LocalJSXCommandCall = async (onDone, context) => {
|
|||||||
const toolNames = getTools(permissionContext).map(tool => tool.name);
|
const toolNames = getTools(permissionContext).map(tool => tool.name);
|
||||||
return <HooksConfigMenu toolNames={toolNames} onExit={onDone} />;
|
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
@@ -12,4 +12,3 @@ export function CheckGitHubStep() {
|
|||||||
}
|
}
|
||||||
return t0;
|
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
48
src/commands/install-github-app/repoSlug.test.ts
Normal file
48
src/commands/install-github-app/repoSlug.test.ts
Normal 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,
|
||||||
|
)
|
||||||
|
})
|
||||||
54
src/commands/install-github-app/repoSlug.ts
Normal file
54
src/commands/install-github-app/repoSlug.ts
Normal 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
@@ -4,4 +4,3 @@ export async function call(onDone: LocalJSXCommandOnDone): Promise<undefined> {
|
|||||||
display: 'system'
|
display: 'system'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJMb2NhbEpTWENvbW1hbmRPbkRvbmUiLCJjYWxsIiwib25Eb25lIiwiUHJvbWlzZSIsImRpc3BsYXkiXSwic291cmNlcyI6WyJvdXRwdXQtc3R5bGUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kT25Eb25lIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwob25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUpOiBQcm9taXNlPHVuZGVmaW5lZD4ge1xuICBvbkRvbmUoXG4gICAgJy9vdXRwdXQtc3R5bGUgaGFzIGJlZW4gZGVwcmVjYXRlZC4gVXNlIC9jb25maWcgdG8gY2hhbmdlIHlvdXIgb3V0cHV0IHN0eWxlLCBvciBzZXQgaXQgaW4geW91ciBzZXR0aW5ncyBmaWxlLiBDaGFuZ2VzIHRha2UgZWZmZWN0IG9uIHRoZSBuZXh0IHNlc3Npb24uJyxcbiAgICB7IGRpc3BsYXk6ICdzeXN0ZW0nIH0sXG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsY0FBY0EscUJBQXFCLFFBQVEsd0JBQXdCO0FBRW5FLE9BQU8sZUFBZUMsSUFBSUEsQ0FBQ0MsTUFBTSxFQUFFRixxQkFBcUIsQ0FBQyxFQUFFRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7RUFDNUVELE1BQU0sQ0FDSix1SkFBdUosRUFDdko7SUFBRUUsT0FBTyxFQUFFO0VBQVMsQ0FDdEIsQ0FBQztBQUNIIiwiaWdub3JlTGlzdCI6W119
|
|
||||||
@@ -21,4 +21,3 @@ export async function call(onDone: LocalJSXCommandOnDone): Promise<React.ReactNo
|
|||||||
});
|
});
|
||||||
return <Passes onDone={onDone} />;
|
return <Passes onDone={onDone} />;
|
||||||
}
|
}
|
||||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlBhc3NlcyIsImxvZ0V2ZW50IiwiZ2V0Q2FjaGVkUmVtYWluaW5nUGFzc2VzIiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiZ2V0R2xvYmFsQ29uZmlnIiwic2F2ZUdsb2JhbENvbmZpZyIsImNhbGwiLCJvbkRvbmUiLCJQcm9taXNlIiwiUmVhY3ROb2RlIiwiY29uZmlnIiwiaXNGaXJzdFZpc2l0IiwiaGFzVmlzaXRlZFBhc3NlcyIsInJlbWFpbmluZyIsImN1cnJlbnQiLCJwYXNzZXNMYXN0U2VlblJlbWFpbmluZyIsImlzX2ZpcnN0X3Zpc2l0Il0sInNvdXJjZXMiOlsicGFzc2VzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IFBhc3NlcyB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvUGFzc2VzL1Bhc3Nlcy5qcydcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnLi4vLi4vc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgZ2V0Q2FjaGVkUmVtYWluaW5nUGFzc2VzIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvYXBpL3JlZmVycmFsLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRPbkRvbmUgfSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuaW1wb3J0IHsgZ2V0R2xvYmFsQ29uZmlnLCBzYXZlR2xvYmFsQ29uZmlnIH0gZnJvbSAnLi4vLi4vdXRpbHMvY29uZmlnLmpzJ1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICAvLyBNYXJrIHRoYXQgdXNlciBoYXMgdmlzaXRlZCAvcGFzc2VzIHNvIHdlIHN0b3Agc2hvd2luZyB0aGUgdXBzZWxsXG4gIGNvbnN0IGNvbmZpZyA9IGdldEdsb2JhbENvbmZpZygpXG4gIGNvbnN0IGlzRmlyc3RWaXNpdCA9ICFjb25maWcuaGFzVmlzaXRlZFBhc3Nlc1xuICBpZiAoaXNGaXJzdFZpc2l0KSB7XG4gICAgY29uc3QgcmVtYWluaW5nID0gZ2V0Q2FjaGVkUmVtYWluaW5nUGFzc2VzKClcbiAgICBzYXZlR2xvYmFsQ29uZmlnKGN1cnJlbnQgPT4gKHtcbiAgICAgIC4uLmN1cnJlbnQsXG4gICAgICBoYXNWaXNpdGVkUGFzc2VzOiB0cnVlLFxuICAgICAgcGFzc2VzTGFzdFNlZW5SZW1haW5pbmc6IHJlbWFpbmluZyA/PyBjdXJyZW50LnBhc3Nlc0xhc3RTZWVuUmVtYWluaW5nLFxuICAgIH0pKVxuICB9XG4gIGxvZ0V2ZW50KCd0ZW5ndV9ndWVzdF9wYXNzZXNfdmlzaXRlZCcsIHsgaXNfZmlyc3RfdmlzaXQ6IGlzRmlyc3RWaXNpdCB9KVxuICByZXR1cm4gPFBhc3NlcyBvbkRvbmU9e29uRG9uZX0gLz5cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxNQUFNLFFBQVEsbUNBQW1DO0FBQzFELFNBQVNDLFFBQVEsUUFBUSxtQ0FBbUM7QUFDNUQsU0FBU0Msd0JBQXdCLFFBQVEsZ0NBQWdDO0FBQ3pFLGNBQWNDLHFCQUFxQixRQUFRLHdCQUF3QjtBQUNuRSxTQUFTQyxlQUFlLEVBQUVDLGdCQUFnQixRQUFRLHVCQUF1QjtBQUV6RSxPQUFPLGVBQWVDLElBQUlBLENBQ3hCQyxNQUFNLEVBQUVKLHFCQUFxQixDQUM5QixFQUFFSyxPQUFPLENBQUNULEtBQUssQ0FBQ1UsU0FBUyxDQUFDLENBQUM7RUFDMUI7RUFDQSxNQUFNQyxNQUFNLEdBQUdOLGVBQWUsQ0FBQyxDQUFDO0VBQ2hDLE1BQU1PLFlBQVksR0FBRyxDQUFDRCxNQUFNLENBQUNFLGdCQUFnQjtFQUM3QyxJQUFJRCxZQUFZLEVBQUU7SUFDaEIsTUFBTUUsU0FBUyxHQUFHWCx3QkFBd0IsQ0FBQyxDQUFDO0lBQzVDRyxnQkFBZ0IsQ0FBQ1MsT0FBTyxLQUFLO01BQzNCLEdBQUdBLE9BQU87TUFDVkYsZ0JBQWdCLEVBQUUsSUFBSTtNQUN0QkcsdUJBQXVCLEVBQUVGLFNBQVMsSUFBSUMsT0FBTyxDQUFDQztJQUNoRCxDQUFDLENBQUMsQ0FBQztFQUNMO0VBQ0FkLFFBQVEsQ0FBQyw0QkFBNEIsRUFBRTtJQUFFZSxjQUFjLEVBQUVMO0VBQWEsQ0FBQyxDQUFDO0VBQ3hFLE9BQU8sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUNKLE1BQU0sQ0FBQyxHQUFHO0FBQ25DIiwiaWdub3JlTGlzdCI6W119
|
|
||||||
@@ -7,4 +7,3 @@ export const call: LocalJSXCommandCall = async (onDone, context) => {
|
|||||||
context.setMessages(prev => [...prev, createPermissionRetryMessage(commands)]);
|
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
@@ -29,4 +29,3 @@ export function PluginTrustWarning() {
|
|||||||
}
|
}
|
||||||
return t2;
|
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
Reference in New Issue
Block a user