Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ packages/claude-code/config/claude-termux-release-manifest.json

**日本語:** このTermuxラッパーでは Remote Control(claude.ai/code や Claude モバイルアプリからのセッション操作)は動作しません。ラッパーが `ws`(WebSocket)モジュールを何もしない no-op スタブに置き換えているため、Remote Control が必要とする接続が確立されません。TUI 起動時に `Remote Control disconnected` と表示されることがありますが、これは想定内の挙動であり、通常の対話操作・`-p`(print mode)・その他の CLI コマンドには影響しません。

**English:** Automatic tool discovery (Tool Search) is disabled by default in this wrapper to prevent unnecessary `ToolSearch` and `WebSearch`/`WebFetch` activation during normal conversations. If you need to enable it, use `ENABLE_TOOL_SEARCH=true claude` or set `ENABLE_TOOL_SEARCH` to `force` in the `env` block of `settings.json`:

```json
{ "env": { "ENABLE_TOOL_SEARCH": "force" } }
```

**日本語:** このラッパーでは、通常の会話で不要な `ToolSearch` や `WebSearch`/`WebFetch` 活動化を防ぐため、automatic tool discovery(Tool Search)が既定で無効化されています。有効化が必要な場合は `ENABLE_TOOL_SEARCH=true claude` を使うか、`settings.json` の `env` ブロック内に `ENABLE_TOOL_SEARCH` を `force` に設定してください:

```json
{ "env": { "ENABLE_TOOL_SEARCH": "force" } }
```

## Verify / 確認

```sh
Expand Down
45 changes: 45 additions & 0 deletions packages/claude-code/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,51 @@ like `disable` or `Y` will silently turn protection off. Use exactly `0` to be s
`1`/`true`/`yes`/`on`(大文字小文字を区別しません)以外の値は全て機能を再有効化してしまうため、
`disable` や `Y` のような typo でも保護が黙って外れます。安全のため厳密に `0` を指定してください。

### Tool Search Disabled by Default / Tool Search は既定で無効化

This Termux wrapper disables the upstream's automatic tool discovery feature (`ENABLE_TOOL_SEARCH=false`
by default) to prevent unnecessary dynamic tool loading in normal conversations. Without this setting,
the model may spontaneously activate `ToolSearch` and trigger `WebSearch`/`WebFetch` calls, consuming
your conversation turn limit (`--max-turns`).

この Termux wrapper は、upstream の自動 tool discovery 機能を既定で無効化しています
(`ENABLE_TOOL_SEARCH=false`)。この設定がないと、モデルが通常の会話で自発的に `ToolSearch` を活動化させ、
`WebSearch`/`WebFetch` を呼び出し、会話の turn 上限(`--max-turns`)を消費するおそれがあります。

If you want to re-enable tool search (for example, if you explicitly use `--tools` and want the model
to load additional tools dynamically), set the variable before launching:

tool search を再有効化したい場合(例えば `--tools` を明示的に指定して、モデルが追加の tool を
動的にロードしてほしい場合)、起動前に環境変数を設定してください:

```sh
ENABLE_TOOL_SEARCH=true claude
```

or:

または:

```sh
ENABLE_TOOL_SEARCH=auto claude
```

**Note on `settings.json`:** If you want to configure this in `settings.json`, place `ENABLE_TOOL_SEARCH` in
the `env` block with the value `force`. The environment variable default (`false`) takes precedence over `true`,
so use the `env` block to force-enable it:

```json
{ "env": { "ENABLE_TOOL_SEARCH": "force" } }
```

**`settings.json` を使う場合の注意:** `settings.json` で設定する場合、`env` ブロック内に `ENABLE_TOOL_SEARCH`
を置いて、値を `force` にしてください。環境変数の既定値(`false`)が `true` より優先されるため、
force-enable するには `env` ブロックを使う必要があります:

```json
{ "env": { "ENABLE_TOOL_SEARCH": "force" } }
```

## Policy / 方針

- Only audited versions in `config/claude-native-audited-versions.json` can run.
Expand Down
69 changes: 63 additions & 6 deletions packages/claude-code/lib/bunfs-esm-loader.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,44 @@ let WS_STUB_PATH = null;

let CYCLE_HOISTS = [];

let REEXTRACT = null;
let reExtractConsecFailures = 0;
let lastReExtractMs = 0;
const MAX_CONSEC_FAILURES = 3;
const REEXTRACT_THROTTLE_MS = 3000;

function recoverMissing(realPath, now = Date.now()) {
if (existsSync(realPath)) return true;
if (!REEXTRACT || !SOURCE_BIN) return false;
if (reExtractConsecFailures >= MAX_CONSEC_FAILURES) return false;
if (now - lastReExtractMs < REEXTRACT_THROTTLE_MS) return existsSync(realPath);
lastReExtractMs = now;
try {
if (!existsSync(SOURCE_BIN)) { reExtractConsecFailures += 1; return false; }
REEXTRACT(SOURCE_BIN, PROCESS_OWNED_DIR);
console.error('[claude-code] recovered missing extracted module(s) by re-extracting from the native binary');
} catch (e) {
reExtractConsecFailures += 1;
console.error('[claude-code] re-extraction failed: ' + (e && e.message ? e.message : String(e)));
return false;
}
const ok = existsSync(realPath);
reExtractConsecFailures = ok ? 0 : reExtractConsecFailures + 1;
return ok;
}

export function initialize(data) {
PROCESS_OWNED_DIR = data.processOwnedDir;
SOURCE_BIN = data.sourceBin;
CHILD_PROCESS_GUARD_PATH = data.childProcessGuardPath;
VM_GUARD_PATH = data.vmGuardPath;
WS_STUB_PATH = data.wsStubPath;
CYCLE_HOISTS = Array.isArray(data.cycleHoists) ? data.cycleHoists : [];
REEXTRACT = typeof data.reExtract === 'function' ? data.reExtract : null;
globalThis.__bunfsRecoverMissing = recoverMissing;
// 回復状態のリセット (テスト隔離・再 initialize 対応)
reExtractConsecFailures = 0;
lastReExtractMs = 0;
}

function tryHoistCycleBreakingImports(filePath, source) {
Expand All @@ -37,7 +68,7 @@ function tryHoistCycleBreakingImports(filePath, source) {

const real = path.resolve(PROCESS_OWNED_DIR, record.targetModule);
if (path.relative(PROCESS_OWNED_DIR, real).startsWith('..')) continue;
if (!existsSync(real)) continue;
if (!existsSync(real) && !recoverMissing(real)) continue;

let varName = targetToVar.get(record.targetModule);
if (!varName) {
Expand Down Expand Up @@ -94,13 +125,27 @@ function buildImportMetaRequirePolyfillPrelude(anchorUrl) {
` throw new Error("bunfs meta-require: path escapes process-owned dir: " + id);\n` +
` }\n` +
` if (!__bunfsMetaRequireExistsSync(real)) {\n` +
` throw new Error("bunfs meta-require: missing extracted module " + id + " -> " + real);\n` +
` const _rec = (typeof globalThis.__bunfsRecoverMissing === "function") && globalThis.__bunfsRecoverMissing(real);\n` +
` if (!_rec) throw new Error("bunfs meta-require: missing extracted module " + id + " -> " + real);\n` +
` }\n` +
` const ext = __bunfsMetaRequirePath.extname(real);\n` +
` if (ext === ".md" || ext === ".txt") {\n` +
` return __bunfsMetaRequireReadFileSync(real, "utf8");\n` +
` try { return __bunfsMetaRequireReadFileSync(real, "utf8"); }\n` +
` catch (_e2) {\n` +
` if (_e2 && _e2.code === "ENOENT" && !__bunfsMetaRequireExistsSync(real) && typeof globalThis.__bunfsRecoverMissing === "function" && globalThis.__bunfsRecoverMissing(real)) {\n` +
` return __bunfsMetaRequireReadFileSync(real, "utf8");\n` +
` }\n` +
` throw _e2;\n` +
` }\n` +
` }\n` +
` try {\n` +
` return __bunfsRealRequire(real);\n` +
` } catch (_e) {\n` +
` if (!__bunfsMetaRequireExistsSync(real) && typeof globalThis.__bunfsRecoverMissing === "function" && globalThis.__bunfsRecoverMissing(real)) {\n` +
` return __bunfsRealRequire(real);\n` +
` }\n` +
` throw _e;\n` +
` }\n` +
` return __bunfsRealRequire(real);\n` +
` }\n` +
` return __bunfsRealRequire(id);\n` +
`};\n`
Expand Down Expand Up @@ -132,7 +177,7 @@ export function resolve(specifier, context, nextResolve) {
if (path.relative(PROCESS_OWNED_DIR, real).startsWith('..')) {
throw new Error(`bunfs resolve: path escapes process-owned dir: ${specifier}`);
}
if (!existsSync(real)) {
if (!existsSync(real) && !recoverMissing(real)) {
throw new Error(`bunfs resolve: missing extracted module ${specifier} -> ${real}`);
}
return { url: pathToFileURL(real).href, shortCircuit: true, format: 'module' };
Expand All @@ -146,7 +191,17 @@ export function load(url, context, nextLoad) {
return nextLoad(url, context);
}
const filePath = fileURLToPath(url);
let source = readFileSync(filePath, 'utf8');
let source;
try {
source = readFileSync(filePath, 'utf8');
} catch (e) {
// filePath 自身が消えている場合のみ回復 (エラーコードだけに依存しない)
if (e && e.code === 'ENOENT' && !existsSync(filePath) && recoverMissing(filePath)) {
source = readFileSync(filePath, 'utf8');
} else {
throw e;
}
}

let hoistedImportLine = '';
const hoistResult = tryHoistCycleBreakingImports(filePath, source);
Expand All @@ -165,3 +220,5 @@ export function load(url, context, nextLoad) {
}
return { format: 'module', source, shortCircuit: true };
}

export { recoverMissing };
Loading