@bash0816/claude-code 2.1.241 → 2.1.248
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/claude +20 -2
- package/config/claude-native-audited-versions.json +30 -0
- package/config/claude-termux-release-manifest.json +3 -3
- package/lib/bunfs-child-process-guard.mjs +18 -0
- package/lib/bunfs-child-process-guard.test.js +113 -0
- package/lib/bunfs-esm-loader.mjs +167 -0
- package/lib/bunfs-esm-loader.test.js +488 -0
- package/lib/bunfs-extract.js +205 -0
- package/lib/bunfs-extract.test.js +265 -0
- package/lib/bunfs-vm-guard.mjs +116 -0
- package/lib/bunfs-vm-guard.test.js +142 -0
- package/lib/bunfs-ws-stub.mjs +11 -0
- package/lib/bunfs-yaml-shim.mjs +91 -0
- package/lib/native-validators.js +90 -0
- package/lib/native-validators.test.js +160 -0
- package/lib/prepare-native.js +5 -41
- package/lib/termux-run-claude-native.sh +154 -4
- package/lib/termux-run-claude-native.test.js +3 -3
- package/package.json +2 -2
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const packageDir = path.resolve(__dirname, '..');
|
|
8
|
+
|
|
9
|
+
function verifyTarball(file, audited, version) {
|
|
10
|
+
const buf = fs.readFileSync(file);
|
|
11
|
+
if (audited.tarball_sha256) {
|
|
12
|
+
const sha256 = crypto.createHash('sha256').update(buf).digest('hex');
|
|
13
|
+
if (sha256 !== audited.tarball_sha256) {
|
|
14
|
+
throw new Error(`tarball sha256 mismatch for ${version}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (audited.tarball_integrity) {
|
|
18
|
+
const sha512 = `sha512-${crypto.createHash('sha512').update(buf).digest('base64')}`;
|
|
19
|
+
if (sha512 !== audited.tarball_integrity) {
|
|
20
|
+
throw new Error(`tarball integrity mismatch for ${version}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (audited.tarball_size !== undefined && Number(audited.tarball_size) !== buf.length) {
|
|
24
|
+
throw new Error(`tarball size mismatch for ${version}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function validateOffsets(file, audited, version) {
|
|
29
|
+
if (audited.entry_format === 'esm-chunked') {
|
|
30
|
+
validateEsmChunkedOffsets(file, audited, version);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
validateLegacyCjsOffsets(file, audited, version);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function validateLegacyCjsOffsets(file, audited, version) {
|
|
37
|
+
const buf = fs.readFileSync(file);
|
|
38
|
+
const start = Number(audited.entry_js_offset);
|
|
39
|
+
const end = Number(audited.entry_end_offset);
|
|
40
|
+
const startMarker = Buffer.from('function(exports, require, module, __filename, __dirname) {// Claude Code is a Beta product');
|
|
41
|
+
const endMarker = Buffer.from('/$bunfs/root/image-processor.js');
|
|
42
|
+
|
|
43
|
+
if (!(start > 0 && end > start && end <= buf.length)) {
|
|
44
|
+
throw new Error(`invalid audited offsets for ${version}`);
|
|
45
|
+
}
|
|
46
|
+
if (!buf.subarray(start, start + startMarker.length).equals(startMarker)) {
|
|
47
|
+
throw new Error(`audited start offset validation failed for ${version}`);
|
|
48
|
+
}
|
|
49
|
+
if (!buf.subarray(end, end + endMarker.length).equals(endMarker)) {
|
|
50
|
+
throw new Error(`audited end offset validation failed for ${version}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function validateEsmChunkedOffsets(file, audited, version) {
|
|
55
|
+
// 371MB超のバイナリ全体をreadFileSyncしない(実機でOOM確認済み)。
|
|
56
|
+
// discoverModuleGraphは範囲readSyncのみでトレイラー・モジュールテーブルを検証する。
|
|
57
|
+
const { discoverModuleGraph, readEntryContentPrefix } = require(path.join(packageDir, 'lib', 'bunfs-extract.js'));
|
|
58
|
+
const graph = discoverModuleGraph(file);
|
|
59
|
+
try {
|
|
60
|
+
if (!(graph.numModules > 0)) {
|
|
61
|
+
throw new Error(`esm-chunked module graph is empty for ${version}`);
|
|
62
|
+
}
|
|
63
|
+
if (graph.entryName !== '/$bunfs/root/cli') {
|
|
64
|
+
throw new Error(`esm-chunked entry module name mismatch for ${version}: ${graph.entryName}`);
|
|
65
|
+
}
|
|
66
|
+
if (!Object.prototype.hasOwnProperty.call(audited, 'cycle_hoists')) {
|
|
67
|
+
throw new Error(`esm-chunked audited metadata for ${version} is missing cycle_hoists field`);
|
|
68
|
+
}
|
|
69
|
+
if (audited.num_modules !== undefined && graph.numModules !== audited.num_modules) {
|
|
70
|
+
throw new Error(`esm-chunked num_modules mismatch for ${version}: expected ${audited.num_modules}, got ${graph.numModules}`);
|
|
71
|
+
}
|
|
72
|
+
if (audited.byte_count !== undefined && graph.byteCount !== audited.byte_count) {
|
|
73
|
+
throw new Error(`esm-chunked byte_count mismatch for ${version}: expected ${audited.byte_count}, got ${graph.byteCount}`);
|
|
74
|
+
}
|
|
75
|
+
const prefix = readEntryContentPrefix(graph.fd, graph.entryModule, 256).toString('utf8');
|
|
76
|
+
const codeStart = prefix.replace(/^(\s*\/\/[^\n]*\n)+/, '').replace(/^\(/, '');
|
|
77
|
+
if (codeStart.startsWith('function(exports, require, module, __filename, __dirname) {')) {
|
|
78
|
+
throw new Error(`entry module for ${version} is legacy-cjs wrapped, but audited entry_format is esm-chunked`);
|
|
79
|
+
}
|
|
80
|
+
} finally {
|
|
81
|
+
fs.closeSync(graph.fd);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = {
|
|
86
|
+
validateEsmChunkedOffsets,
|
|
87
|
+
validateLegacyCjsOffsets,
|
|
88
|
+
validateOffsets,
|
|
89
|
+
verifyTarball,
|
|
90
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const os = require('node:os');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
validateEsmChunkedOffsets,
|
|
11
|
+
} = require('./native-validators.js');
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
discoverModuleGraph,
|
|
15
|
+
} = require('./bunfs-extract.js');
|
|
16
|
+
|
|
17
|
+
const TRAILER = '\n---- Bun! ----\n';
|
|
18
|
+
|
|
19
|
+
// StandaloneModuleGraphの最小合成バイナリを構築する。
|
|
20
|
+
// レイアウト: [preamble padding][module contents][module table][Offsets(32byte)][trailer]
|
|
21
|
+
function buildSyntheticBinary({ modules, entryPointId, corruptTrailer = false, preamblePadding = 64 }) {
|
|
22
|
+
const nameBuffers = modules.map((m) => Buffer.from(m.name, 'utf8'));
|
|
23
|
+
const contentBuffers = modules.map((m) => Buffer.from(m.content ?? '', 'utf8'));
|
|
24
|
+
|
|
25
|
+
const dataParts = [];
|
|
26
|
+
const nameOffsets = [];
|
|
27
|
+
const contOffsets = [];
|
|
28
|
+
let cursor = 0;
|
|
29
|
+
for (let i = 0; i < modules.length; i += 1) {
|
|
30
|
+
nameOffsets.push(cursor);
|
|
31
|
+
dataParts.push(nameBuffers[i]);
|
|
32
|
+
cursor += nameBuffers[i].length;
|
|
33
|
+
}
|
|
34
|
+
for (let i = 0; i < modules.length; i += 1) {
|
|
35
|
+
contOffsets.push(cursor);
|
|
36
|
+
dataParts.push(contentBuffers[i]);
|
|
37
|
+
cursor += contentBuffers[i].length;
|
|
38
|
+
}
|
|
39
|
+
const byteCountBeforeTable = cursor;
|
|
40
|
+
|
|
41
|
+
const MODULE_TABLE_ENTRY_SIZE = 52;
|
|
42
|
+
const modTable = Buffer.alloc(MODULE_TABLE_ENTRY_SIZE * modules.length);
|
|
43
|
+
for (let i = 0; i < modules.length; i += 1) {
|
|
44
|
+
const base = i * MODULE_TABLE_ENTRY_SIZE;
|
|
45
|
+
modTable.writeUInt32LE(nameOffsets[i], base);
|
|
46
|
+
modTable.writeUInt32LE(nameBuffers[i].length, base + 4);
|
|
47
|
+
modTable.writeUInt32LE(contOffsets[i], base + 8);
|
|
48
|
+
modTable.writeUInt32LE(contentBuffers[i].length, base + 12);
|
|
49
|
+
modTable[base + 49] = modules[i].loader ?? 1; // 1 = js
|
|
50
|
+
}
|
|
51
|
+
const modulesOffset = byteCountBeforeTable;
|
|
52
|
+
const modulesLength = modTable.length;
|
|
53
|
+
const byteCount = byteCountBeforeTable + modulesLength;
|
|
54
|
+
|
|
55
|
+
const offsetsBuf = Buffer.alloc(32);
|
|
56
|
+
offsetsBuf.writeBigUInt64LE(BigInt(byteCount), 0);
|
|
57
|
+
offsetsBuf.writeUInt32LE(modulesOffset, 8);
|
|
58
|
+
offsetsBuf.writeUInt32LE(modulesLength, 12);
|
|
59
|
+
offsetsBuf.writeUInt32LE(entryPointId, 16);
|
|
60
|
+
|
|
61
|
+
const trailerBuf = Buffer.from(corruptTrailer ? '\n---- NOT BUN ----\n' : TRAILER, 'utf8');
|
|
62
|
+
|
|
63
|
+
return Buffer.concat([
|
|
64
|
+
Buffer.alloc(preamblePadding),
|
|
65
|
+
...dataParts,
|
|
66
|
+
modTable,
|
|
67
|
+
offsetsBuf,
|
|
68
|
+
trailerBuf,
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function writeTempBinary(buf) {
|
|
73
|
+
const file = path.join(os.tmpdir(), `native-validators-test-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.bin`);
|
|
74
|
+
fs.writeFileSync(file, buf);
|
|
75
|
+
return file;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
test('validateEsmChunkedOffsets accepts matching num_modules and byte_count', () => {
|
|
79
|
+
const buf = buildSyntheticBinary({
|
|
80
|
+
modules: [
|
|
81
|
+
{ name: '/$bunfs/root/cli', content: 'export default 1;' },
|
|
82
|
+
],
|
|
83
|
+
entryPointId: 0,
|
|
84
|
+
});
|
|
85
|
+
const file = writeTempBinary(buf);
|
|
86
|
+
try {
|
|
87
|
+
const graph = discoverModuleGraph(file);
|
|
88
|
+
try {
|
|
89
|
+
const audited = {
|
|
90
|
+
num_modules: graph.numModules,
|
|
91
|
+
byte_count: graph.byteCount,
|
|
92
|
+
entry_format: 'esm-chunked',
|
|
93
|
+
cycle_hoists: [],
|
|
94
|
+
};
|
|
95
|
+
assert.doesNotThrow(() => validateEsmChunkedOffsets(file, audited, '9.9.9'));
|
|
96
|
+
} finally {
|
|
97
|
+
fs.closeSync(graph.fd);
|
|
98
|
+
}
|
|
99
|
+
} finally {
|
|
100
|
+
fs.rmSync(file, { force: true });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('validateEsmChunkedOffsets rejects mismatched num_modules', () => {
|
|
105
|
+
const buf = buildSyntheticBinary({
|
|
106
|
+
modules: [
|
|
107
|
+
{ name: '/$bunfs/root/cli', content: 'export default 1;' },
|
|
108
|
+
],
|
|
109
|
+
entryPointId: 0,
|
|
110
|
+
});
|
|
111
|
+
const file = writeTempBinary(buf);
|
|
112
|
+
try {
|
|
113
|
+
const graph = discoverModuleGraph(file);
|
|
114
|
+
try {
|
|
115
|
+
const audited = {
|
|
116
|
+
num_modules: graph.numModules + 1, // intentionally wrong
|
|
117
|
+
byte_count: graph.byteCount,
|
|
118
|
+
entry_format: 'esm-chunked',
|
|
119
|
+
cycle_hoists: [],
|
|
120
|
+
};
|
|
121
|
+
assert.throws(
|
|
122
|
+
() => validateEsmChunkedOffsets(file, audited, '9.9.9'),
|
|
123
|
+
/num_modules mismatch/,
|
|
124
|
+
);
|
|
125
|
+
} finally {
|
|
126
|
+
fs.closeSync(graph.fd);
|
|
127
|
+
}
|
|
128
|
+
} finally {
|
|
129
|
+
fs.rmSync(file, { force: true });
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('validateEsmChunkedOffsets rejects mismatched byte_count', () => {
|
|
134
|
+
const buf = buildSyntheticBinary({
|
|
135
|
+
modules: [
|
|
136
|
+
{ name: '/$bunfs/root/cli', content: 'export default 1;' },
|
|
137
|
+
],
|
|
138
|
+
entryPointId: 0,
|
|
139
|
+
});
|
|
140
|
+
const file = writeTempBinary(buf);
|
|
141
|
+
try {
|
|
142
|
+
const graph = discoverModuleGraph(file);
|
|
143
|
+
try {
|
|
144
|
+
const audited = {
|
|
145
|
+
num_modules: graph.numModules,
|
|
146
|
+
byte_count: graph.byteCount + 1, // intentionally wrong
|
|
147
|
+
entry_format: 'esm-chunked',
|
|
148
|
+
cycle_hoists: [],
|
|
149
|
+
};
|
|
150
|
+
assert.throws(
|
|
151
|
+
() => validateEsmChunkedOffsets(file, audited, '9.9.9'),
|
|
152
|
+
/byte_count mismatch/,
|
|
153
|
+
);
|
|
154
|
+
} finally {
|
|
155
|
+
fs.closeSync(graph.fd);
|
|
156
|
+
}
|
|
157
|
+
} finally {
|
|
158
|
+
fs.rmSync(file, { force: true });
|
|
159
|
+
}
|
|
160
|
+
});
|
package/lib/prepare-native.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const cp = require('child_process');
|
|
5
|
-
const crypto = require('crypto');
|
|
6
5
|
const fs = require('fs');
|
|
7
6
|
const os = require('os');
|
|
8
7
|
const path = require('path');
|
|
@@ -16,6 +15,8 @@ const curlRetries = process.env.CLAUDE_TERMUX_FETCH_RETRIES || '4';
|
|
|
16
15
|
const curlConnectTimeout = process.env.CLAUDE_TERMUX_FETCH_CONNECT_TIMEOUT || '20';
|
|
17
16
|
const curlMaxTime = process.env.CLAUDE_TERMUX_FETCH_MAX_TIME || '300';
|
|
18
17
|
|
|
18
|
+
const { validateOffsets, verifyTarball } = require(path.join(packageDir, 'lib', 'native-validators.js'));
|
|
19
|
+
|
|
19
20
|
if (!item) {
|
|
20
21
|
console.error(`Unsupported audited Claude Code version: ${version}`);
|
|
21
22
|
process.exit(1);
|
|
@@ -27,7 +28,7 @@ const nativeDest = path.join(versionDir, 'app', 'node_modules', '@anthropic-ai',
|
|
|
27
28
|
const sourceBin = path.join(nativeDest, 'claude');
|
|
28
29
|
|
|
29
30
|
if (fs.existsSync(sourceBin)) {
|
|
30
|
-
validateOffsets(sourceBin, item);
|
|
31
|
+
validateOffsets(sourceBin, item, version);
|
|
31
32
|
process.exit(0);
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -36,7 +37,7 @@ const packDir = fs.mkdtempSync(path.join(os.tmpdir(), `claude-code-${version}-`)
|
|
|
36
37
|
|
|
37
38
|
try {
|
|
38
39
|
const tgzPath = fetchNativeTarball(item.native_spec, packDir);
|
|
39
|
-
verifyTarball(tgzPath, item);
|
|
40
|
+
verifyTarball(tgzPath, item, version);
|
|
40
41
|
|
|
41
42
|
const extractDir = path.join(packDir, 'native');
|
|
42
43
|
fs.mkdirSync(extractDir, { recursive: true });
|
|
@@ -45,7 +46,7 @@ try {
|
|
|
45
46
|
fs.rmSync(nativeDest, { recursive: true, force: true });
|
|
46
47
|
fs.mkdirSync(path.dirname(nativeDest), { recursive: true });
|
|
47
48
|
fs.cpSync(path.join(extractDir, 'package'), nativeDest, { recursive: true });
|
|
48
|
-
validateOffsets(sourceBin, item);
|
|
49
|
+
validateOffsets(sourceBin, item, version);
|
|
49
50
|
} finally {
|
|
50
51
|
fs.rmSync(packDir, { recursive: true, force: true });
|
|
51
52
|
}
|
|
@@ -107,40 +108,3 @@ function fetchNativeTarball(spec, packDir) {
|
|
|
107
108
|
}
|
|
108
109
|
return path.join(packDir, packEntry.filename);
|
|
109
110
|
}
|
|
110
|
-
|
|
111
|
-
function verifyTarball(file, audited) {
|
|
112
|
-
const buf = fs.readFileSync(file);
|
|
113
|
-
if (audited.tarball_sha256) {
|
|
114
|
-
const sha256 = crypto.createHash('sha256').update(buf).digest('hex');
|
|
115
|
-
if (sha256 !== audited.tarball_sha256) {
|
|
116
|
-
throw new Error(`tarball sha256 mismatch for ${version}`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
if (audited.tarball_integrity) {
|
|
120
|
-
const sha512 = `sha512-${crypto.createHash('sha512').update(buf).digest('base64')}`;
|
|
121
|
-
if (sha512 !== audited.tarball_integrity) {
|
|
122
|
-
throw new Error(`tarball integrity mismatch for ${version}`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (audited.tarball_size !== undefined && Number(audited.tarball_size) !== buf.length) {
|
|
126
|
-
throw new Error(`tarball size mismatch for ${version}`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function validateOffsets(file, audited) {
|
|
131
|
-
const buf = fs.readFileSync(file);
|
|
132
|
-
const start = Number(audited.entry_js_offset);
|
|
133
|
-
const end = Number(audited.entry_end_offset);
|
|
134
|
-
const startMarker = Buffer.from('function(exports, require, module, __filename, __dirname) {// Claude Code is a Beta product');
|
|
135
|
-
const endMarker = Buffer.from('/$bunfs/root/image-processor.js');
|
|
136
|
-
|
|
137
|
-
if (!(start > 0 && end > start && end <= buf.length)) {
|
|
138
|
-
throw new Error(`invalid audited offsets for ${version}`);
|
|
139
|
-
}
|
|
140
|
-
if (!buf.subarray(start, start + startMarker.length).equals(startMarker)) {
|
|
141
|
-
throw new Error(`audited start offset validation failed for ${version}`);
|
|
142
|
-
}
|
|
143
|
-
if (!buf.subarray(end, end + endMarker.length).equals(endMarker)) {
|
|
144
|
-
throw new Error(`audited end offset validation failed for ${version}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
@@ -3,8 +3,14 @@ set -eu
|
|
|
3
3
|
|
|
4
4
|
SOURCE_BIN="${SOURCE_BIN:?SOURCE_BIN is required}"
|
|
5
5
|
WORKDIR="${WORKDIR:-${HOME}/.claude-termux-native-package/launcher-workdir}"
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
ENTRY_FORMAT="${ENTRY_FORMAT:-legacy-cjs}"
|
|
7
|
+
if [ "${ENTRY_FORMAT}" = "esm-chunked" ]; then
|
|
8
|
+
ENTRY_JS_OFFSET="${ENTRY_JS_OFFSET:-0}"
|
|
9
|
+
ENTRY_END_OFFSET="${ENTRY_END_OFFSET:-0}"
|
|
10
|
+
else
|
|
11
|
+
ENTRY_JS_OFFSET="${ENTRY_JS_OFFSET:?ENTRY_JS_OFFSET is required}"
|
|
12
|
+
ENTRY_END_OFFSET="${ENTRY_END_OFFSET:?ENTRY_END_OFFSET is required}"
|
|
13
|
+
fi
|
|
8
14
|
CURRENT_CLAUDE_VERSION="${CURRENT_CLAUDE_VERSION:?CURRENT_CLAUDE_VERSION is required}"
|
|
9
15
|
CLAUDE_TERMUX_PACKAGE_DIR="${CLAUDE_TERMUX_PACKAGE_DIR:?CLAUDE_TERMUX_PACKAGE_DIR is required}"
|
|
10
16
|
TERMUX_TMPDIR="${TMPDIR:-/data/data/com.termux/files/usr/tmp}"
|
|
@@ -45,6 +51,7 @@ export SSL_CERT_FILE
|
|
|
45
51
|
export DISABLE_AUTOUPDATER
|
|
46
52
|
export SOURCE_BIN
|
|
47
53
|
export WORKDIR
|
|
54
|
+
export ENTRY_FORMAT
|
|
48
55
|
export ENTRY_JS_OFFSET
|
|
49
56
|
export ENTRY_END_OFFSET
|
|
50
57
|
export CURRENT_CLAUDE_VERSION
|
|
@@ -656,7 +663,71 @@ function rewriteNativeChunkSource(source) {
|
|
|
656
663
|
return patched;
|
|
657
664
|
}
|
|
658
665
|
|
|
659
|
-
async function
|
|
666
|
+
async function esmChunkedMain() {
|
|
667
|
+
const { prepareProcessOwnedDir } = require(path.join(process.env.CLAUDE_TERMUX_PACKAGE_DIR, 'lib', 'bunfs-extract.js'));
|
|
668
|
+
const { registerHooks } = require('node:module');
|
|
669
|
+
const { pathToFileURL } = require('node:url');
|
|
670
|
+
|
|
671
|
+
const { ownedDir, entryRelPath } = prepareProcessOwnedDir(sourceBin, workdir);
|
|
672
|
+
const libDir = path.join(process.env.CLAUDE_TERMUX_PACKAGE_DIR, 'lib');
|
|
673
|
+
|
|
674
|
+
globalThis.__claudeYaml = createYamlShim();
|
|
675
|
+
globalThis.Bun = {
|
|
676
|
+
version: '1.1.8',
|
|
677
|
+
stringWidth,
|
|
678
|
+
wrapAnsi,
|
|
679
|
+
stripANSI,
|
|
680
|
+
hash: stableHash,
|
|
681
|
+
which: (cmd) => {
|
|
682
|
+
try {
|
|
683
|
+
return require('child_process').execFileSync('which', [String(cmd)], { encoding: 'utf8' }).trim() || null;
|
|
684
|
+
} catch { return null; }
|
|
685
|
+
},
|
|
686
|
+
gc: () => {},
|
|
687
|
+
YAML: globalThis.__claudeYaml,
|
|
688
|
+
};
|
|
689
|
+
Object.defineProperty(process.versions, 'bun', { value: '1.1.8', configurable: true });
|
|
690
|
+
globalThis.__claudeBunShim = globalThis.Bun;
|
|
691
|
+
globalThis.__claudeBun = globalThis.Bun;
|
|
692
|
+
|
|
693
|
+
let cycleHoists = [];
|
|
694
|
+
try {
|
|
695
|
+
const auditedVersions = require(path.join(process.env.CLAUDE_TERMUX_PACKAGE_DIR, 'config', 'claude-native-audited-versions.json'));
|
|
696
|
+
const entry = auditedVersions.versions?.[process.env.CURRENT_CLAUDE_VERSION];
|
|
697
|
+
if (entry && Array.isArray(entry.cycle_hoists)) {
|
|
698
|
+
cycleHoists = entry.cycle_hoists;
|
|
699
|
+
}
|
|
700
|
+
} catch {
|
|
701
|
+
// 読み込み失敗時は空配列のまま(fail-closed、既存の同期require経路にフォールバック)
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const loaderMod = require(path.join(libDir, 'bunfs-esm-loader.mjs'));
|
|
705
|
+
loaderMod.initialize({
|
|
706
|
+
processOwnedDir: ownedDir,
|
|
707
|
+
sourceBin,
|
|
708
|
+
childProcessGuardPath: path.join(libDir, 'bunfs-child-process-guard.mjs'),
|
|
709
|
+
vmGuardPath: path.join(libDir, 'bunfs-vm-guard.mjs'),
|
|
710
|
+
wsStubPath: path.join(libDir, 'bunfs-ws-stub.mjs'),
|
|
711
|
+
cycleHoists,
|
|
712
|
+
});
|
|
713
|
+
registerHooks({ resolve: loaderMod.resolve, load: loaderMod.load });
|
|
714
|
+
|
|
715
|
+
const entryUrl = pathToFileURL(path.join(ownedDir, entryRelPath)).href;
|
|
716
|
+
|
|
717
|
+
// 2.1.245実チャンクのエントリは、内部のmain相当処理をトップレベルでawaitせず
|
|
718
|
+
// fire-and-forgetで起動する(Bunランタイム前提の実装)。そのためawait import()は
|
|
719
|
+
// 内部の非同期処理が完了する前に解決してしまい、legacy-cjs経路のような
|
|
720
|
+
// process.exitパッチ+ここでの強制exit呼び出しを行うと、まだ実行中の内部処理を
|
|
721
|
+
// 強制終了させ出力が失われる(実機で確認済み)。process.exit/killは一切パッチせず、
|
|
722
|
+
// 実際のCLIコードが自ら呼ぶprocess.exit()に任せてNodeの自然なイベントループ終了を
|
|
723
|
+
// 待つ(この関数はawait import()完了後、何もせずreturnするだけでよい)。
|
|
724
|
+
// 同じ理由で、ここでglobalThis.Bun/__claudeYamlを削除するcleanupも行わない
|
|
725
|
+
// (fire-and-forgetの内部処理がimport()解決後も継続してBunを参照するため、
|
|
726
|
+
// 早期に消すと実機で"Bun is not defined"を引き起こす。プロセス終了まで残す)。
|
|
727
|
+
await import(entryUrl);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
async function legacyCjsMain() {
|
|
660
731
|
let extractedFile;
|
|
661
732
|
extractedFile = ensureEntryFile();
|
|
662
733
|
const code = fs.readFileSync(extractedFile, 'utf8');
|
|
@@ -1000,6 +1071,10 @@ async function main() {
|
|
|
1000
1071
|
}
|
|
1001
1072
|
}
|
|
1002
1073
|
|
|
1074
|
+
function main() {
|
|
1075
|
+
return process.env.ENTRY_FORMAT === 'esm-chunked' ? esmChunkedMain() : legacyCjsMain();
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1003
1078
|
main().catch(error => {
|
|
1004
1079
|
if (error && error.code === 'CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED') {
|
|
1005
1080
|
console.error(BLOCK_MESSAGE);
|
|
@@ -1613,7 +1688,71 @@ function rewriteNativeChunkSource(source) {
|
|
|
1613
1688
|
return patched;
|
|
1614
1689
|
}
|
|
1615
1690
|
|
|
1616
|
-
async function
|
|
1691
|
+
async function esmChunkedMain() {
|
|
1692
|
+
const { prepareProcessOwnedDir } = require(path.join(process.env.CLAUDE_TERMUX_PACKAGE_DIR, 'lib', 'bunfs-extract.js'));
|
|
1693
|
+
const { registerHooks } = require('node:module');
|
|
1694
|
+
const { pathToFileURL } = require('node:url');
|
|
1695
|
+
|
|
1696
|
+
const { ownedDir, entryRelPath } = prepareProcessOwnedDir(sourceBin, workdir);
|
|
1697
|
+
const libDir = path.join(process.env.CLAUDE_TERMUX_PACKAGE_DIR, 'lib');
|
|
1698
|
+
|
|
1699
|
+
globalThis.__claudeYaml = createYamlShim();
|
|
1700
|
+
globalThis.Bun = {
|
|
1701
|
+
version: '1.1.8',
|
|
1702
|
+
stringWidth,
|
|
1703
|
+
wrapAnsi,
|
|
1704
|
+
stripANSI,
|
|
1705
|
+
hash: stableHash,
|
|
1706
|
+
which: (cmd) => {
|
|
1707
|
+
try {
|
|
1708
|
+
return require('child_process').execFileSync('which', [String(cmd)], { encoding: 'utf8' }).trim() || null;
|
|
1709
|
+
} catch { return null; }
|
|
1710
|
+
},
|
|
1711
|
+
gc: () => {},
|
|
1712
|
+
YAML: globalThis.__claudeYaml,
|
|
1713
|
+
};
|
|
1714
|
+
Object.defineProperty(process.versions, 'bun', { value: '1.1.8', configurable: true });
|
|
1715
|
+
globalThis.__claudeBunShim = globalThis.Bun;
|
|
1716
|
+
globalThis.__claudeBun = globalThis.Bun;
|
|
1717
|
+
|
|
1718
|
+
let cycleHoists = [];
|
|
1719
|
+
try {
|
|
1720
|
+
const auditedVersions = require(path.join(process.env.CLAUDE_TERMUX_PACKAGE_DIR, 'config', 'claude-native-audited-versions.json'));
|
|
1721
|
+
const entry = auditedVersions.versions?.[process.env.CURRENT_CLAUDE_VERSION];
|
|
1722
|
+
if (entry && Array.isArray(entry.cycle_hoists)) {
|
|
1723
|
+
cycleHoists = entry.cycle_hoists;
|
|
1724
|
+
}
|
|
1725
|
+
} catch {
|
|
1726
|
+
// 読み込み失敗時は空配列のまま(fail-closed、既存の同期require経路にフォールバック)
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
const loaderMod = require(path.join(libDir, 'bunfs-esm-loader.mjs'));
|
|
1730
|
+
loaderMod.initialize({
|
|
1731
|
+
processOwnedDir: ownedDir,
|
|
1732
|
+
sourceBin,
|
|
1733
|
+
childProcessGuardPath: path.join(libDir, 'bunfs-child-process-guard.mjs'),
|
|
1734
|
+
vmGuardPath: path.join(libDir, 'bunfs-vm-guard.mjs'),
|
|
1735
|
+
wsStubPath: path.join(libDir, 'bunfs-ws-stub.mjs'),
|
|
1736
|
+
cycleHoists,
|
|
1737
|
+
});
|
|
1738
|
+
registerHooks({ resolve: loaderMod.resolve, load: loaderMod.load });
|
|
1739
|
+
|
|
1740
|
+
const entryUrl = pathToFileURL(path.join(ownedDir, entryRelPath)).href;
|
|
1741
|
+
|
|
1742
|
+
// 2.1.245実チャンクのエントリは、内部のmain相当処理をトップレベルでawaitせず
|
|
1743
|
+
// fire-and-forgetで起動する(Bunランタイム前提の実装)。そのためawait import()は
|
|
1744
|
+
// 内部の非同期処理が完了する前に解決してしまい、legacy-cjs経路のような
|
|
1745
|
+
// process.exitパッチ+ここでの強制exit呼び出しを行うと、まだ実行中の内部処理を
|
|
1746
|
+
// 強制終了させ出力が失われる(実機で確認済み)。process.exit/killは一切パッチせず、
|
|
1747
|
+
// 実際のCLIコードが自ら呼ぶprocess.exit()に任せてNodeの自然なイベントループ終了を
|
|
1748
|
+
// 待つ(この関数はawait import()完了後、何もせずreturnするだけでよい)。
|
|
1749
|
+
// 同じ理由で、ここでglobalThis.Bun/__claudeYamlを削除するcleanupも行わない
|
|
1750
|
+
// (fire-and-forgetの内部処理がimport()解決後も継続してBunを参照するため、
|
|
1751
|
+
// 早期に消すと実機で"Bun is not defined"を引き起こす。プロセス終了まで残す)。
|
|
1752
|
+
await import(entryUrl);
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
async function legacyCjsMain() {
|
|
1617
1756
|
let extractedFile;
|
|
1618
1757
|
extractedFile = ensureEntryFile();
|
|
1619
1758
|
const code = fs.readFileSync(extractedFile, 'utf8');
|
|
@@ -1961,8 +2100,19 @@ async function main() {
|
|
|
1961
2100
|
}
|
|
1962
2101
|
}
|
|
1963
2102
|
|
|
2103
|
+
function main() {
|
|
2104
|
+
return process.env.ENTRY_FORMAT === 'esm-chunked' ? esmChunkedMain() : legacyCjsMain();
|
|
2105
|
+
}
|
|
2106
|
+
|
|
1964
2107
|
main()
|
|
1965
2108
|
.then(() => {
|
|
2109
|
+
// esm-chunked経路は内部のfire-and-forget非同期処理が実際のprocess.exit()を
|
|
2110
|
+
// 自ら呼ぶまでNodeのイベントループを生かしておく必要があるため、TUIモードと
|
|
2111
|
+
// 同様にここでは強制exitしない(実機で確認済み: 強制exitすると--helpの出力等が
|
|
2112
|
+
// 完了前に打ち切られる)。
|
|
2113
|
+
if (process.env.ENTRY_FORMAT === 'esm-chunked') {
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
1966
2116
|
if (process.env.CLAUDE_TERMUX_TUI === '1' && process.exitCode === undefined) {
|
|
1967
2117
|
return;
|
|
1968
2118
|
}
|
|
@@ -81,7 +81,7 @@ function loadHelperApi() {
|
|
|
81
81
|
const rewriteSource = extractFunction(
|
|
82
82
|
helperBlock,
|
|
83
83
|
'function rewriteNativeChunkSource(source) {',
|
|
84
|
-
'\n\nasync function
|
|
84
|
+
'\n\nasync function esmChunkedMain() {',
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
const context = vm.createContext({ module: { exports: {} }, exports: {}, fs, path, process });
|
|
@@ -113,12 +113,12 @@ test('helper and bootstrap rewrite helpers stay identical', () => {
|
|
|
113
113
|
const helperRewrite = extractFunction(
|
|
114
114
|
helperBlock,
|
|
115
115
|
'function rewriteNativeChunkSource(source) {',
|
|
116
|
-
'\n\nasync function
|
|
116
|
+
'\n\nasync function esmChunkedMain() {',
|
|
117
117
|
);
|
|
118
118
|
const bootstrapRewrite = extractFunction(
|
|
119
119
|
bootstrapBlock,
|
|
120
120
|
'function rewriteNativeChunkSource(source) {',
|
|
121
|
-
'\n\nasync function
|
|
121
|
+
'\n\nasync function esmChunkedMain() {',
|
|
122
122
|
);
|
|
123
123
|
const helperWrapAnsi = extractFunction(
|
|
124
124
|
helperBlock,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bash0816/claude-code",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.248",
|
|
4
4
|
"description": "Unofficial Termux-native Claude Code wrapper with audited native replay",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"LICENSE"
|
|
16
16
|
],
|
|
17
17
|
"engines": {
|
|
18
|
-
"node": ">=
|
|
18
|
+
"node": ">=22.15.0 <23.0.0 || >=23.5.0"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
|
21
21
|
"claude-code",
|