@bash0816/claude-code 2.1.161 → 2.1.177-1
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
CHANGED
|
@@ -37,7 +37,7 @@ npm が `claude` bin link を管理する状態になれば、通常の `npm ins
|
|
|
37
37
|
Latest audited version / 最新監査済み版:
|
|
38
38
|
|
|
39
39
|
```sh
|
|
40
|
-
npm install -g @bash0816/claude-code@2.1.161
|
|
40
|
+
npm install -g @bash0816/claude-code@2.1.161-2
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
## Update / 更新
|
|
@@ -54,6 +54,14 @@ claude update
|
|
|
54
54
|
npm install -g @bash0816/claude-code@<latest_audited_version>
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
If you already have the repo's `2.1.161` release or the official upstream
|
|
58
|
+
`2.1.165` installed, install `2.1.159-13` explicitly. `claude update` will not
|
|
59
|
+
roll a newer installed terminal back to this repo's current audited release.
|
|
60
|
+
|
|
61
|
+
この repo の `2.1.161` release または official upstream の `2.1.165` を
|
|
62
|
+
すでに入れている端末では、`2.1.159-13` を明示 install してください。
|
|
63
|
+
`claude update` だけでは、より新しい端末をこの repo の現在の audited release に戻しません。
|
|
64
|
+
|
|
57
65
|
Normal launch also checks the same manifest with a short timeout and prints a notice when a newer audited version is available.
|
|
58
66
|
|
|
59
67
|
通常起動時も、同じ manifest を短い timeout で確認し、新しい監査済み version があれば通知します。
|
|
@@ -229,6 +229,26 @@
|
|
|
229
229
|
"status": "termux_verified"
|
|
230
230
|
},
|
|
231
231
|
"2.1.161": {
|
|
232
|
+
"wrapper_spec": "@anthropic-ai/claude-code@2.1.161",
|
|
233
|
+
"native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.161",
|
|
234
|
+
"entry_js_offset": 225551924,
|
|
235
|
+
"entry_end_offset": 241503454,
|
|
236
|
+
"tarball_integrity": "sha512-rLEzd8/q4UnTLDSAd3eYgog+G0iZ8y6K8yh0gSxf82d7uOkypYaBUkbmFENuHuzIzCIf9Yy/utCJfp6kQFjUow==",
|
|
237
|
+
"tarball_sha256": "e87d973bbe58593e06c101196f6417f7d950bf777cb9f1a8ff002ebf987ea066",
|
|
238
|
+
"tarball_size": 74314760,
|
|
239
|
+
"status": "offset_discovered"
|
|
240
|
+
},
|
|
241
|
+
"2.1.161-1": {
|
|
242
|
+
"wrapper_spec": "@anthropic-ai/claude-code@2.1.161",
|
|
243
|
+
"native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.161",
|
|
244
|
+
"entry_js_offset": 225551924,
|
|
245
|
+
"entry_end_offset": 241503454,
|
|
246
|
+
"tarball_integrity": "sha512-rLEzd8/q4UnTLDSAd3eYgog+G0iZ8y6K8yh0gSxf82d7uOkypYaBUkbmFENuHuzIzCIf9Yy/utCJfp6kQFjUow==",
|
|
247
|
+
"tarball_sha256": "e87d973bbe58593e06c101196f6417f7d950bf777cb9f1a8ff002ebf987ea066",
|
|
248
|
+
"tarball_size": 74314760,
|
|
249
|
+
"status": "reverted"
|
|
250
|
+
},
|
|
251
|
+
"2.1.161-2": {
|
|
232
252
|
"wrapper_spec": "@anthropic-ai/claude-code@2.1.161",
|
|
233
253
|
"native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.161",
|
|
234
254
|
"entry_js_offset": 225551924,
|
|
@@ -237,6 +257,33 @@
|
|
|
237
257
|
"tarball_sha256": "e87d973bbe58593e06c101196f6417f7d950bf777cb9f1a8ff002ebf987ea066",
|
|
238
258
|
"tarball_size": 74314760,
|
|
239
259
|
"status": "termux_verified"
|
|
260
|
+
},
|
|
261
|
+
"2.1.165": {
|
|
262
|
+
"wrapper_spec": "@anthropic-ai/claude-code@2.1.165",
|
|
263
|
+
"native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.165",
|
|
264
|
+
"entry_js_offset": 226798932,
|
|
265
|
+
"entry_end_offset": 242980856,
|
|
266
|
+
"tarball_integrity": "sha512-uGwKxRQGTxh3BZGnX096CLf/2us5lUFnk4KerWhG4WCpRiEUSu4TUDojp/S3SUzA//FpM57fViUW07XL3T19lw==",
|
|
267
|
+
"tarball_sha256": "6dc6d84624bedaf5ff0948b178fdfc9c2724d8051cb00e70559e292055f7155e",
|
|
268
|
+
"status": "reverted"
|
|
269
|
+
},
|
|
270
|
+
"2.1.177": {
|
|
271
|
+
"wrapper_spec": "@anthropic-ai/claude-code@2.1.177",
|
|
272
|
+
"native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.177",
|
|
273
|
+
"entry_js_offset": 231509988,
|
|
274
|
+
"entry_end_offset": 248542413,
|
|
275
|
+
"tarball_integrity": "sha512-qb0K+/xwaZFz9spBUcDX3vNgEFTSNdbDze8AOpU4R9weVrwBY9zAO/OPqxC9qNhb5ZIRnJAZLKiICEeEUHowzA==",
|
|
276
|
+
"tarball_sha256": "dc6f28ab95d8748528a92e207d0f2d1dcd173855d18f11b8f82c5827bbdcb234",
|
|
277
|
+
"status": "offset_discovered"
|
|
278
|
+
},
|
|
279
|
+
"2.1.177-1": {
|
|
280
|
+
"wrapper_spec": "@anthropic-ai/claude-code@2.1.177",
|
|
281
|
+
"native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.177",
|
|
282
|
+
"entry_js_offset": 231509988,
|
|
283
|
+
"entry_end_offset": 248542413,
|
|
284
|
+
"tarball_integrity": "sha512-qb0K+/xwaZFz9spBUcDX3vNgEFTSNdbDze8AOpU4R9weVrwBY9zAO/OPqxC9qNhb5ZIRnJAZLKiICEeEUHowzA==",
|
|
285
|
+
"tarball_sha256": "dc6f28ab95d8748528a92e207d0f2d1dcd173855d18f11b8f82c5827bbdcb234",
|
|
286
|
+
"status": "offset_discovered"
|
|
240
287
|
}
|
|
241
288
|
}
|
|
242
289
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 1,
|
|
3
3
|
"package_name": "@bash0816/claude-code",
|
|
4
|
-
"latest_audited_version": "2.1.161",
|
|
5
|
-
"latest_candidate_version": "2.1.
|
|
4
|
+
"latest_audited_version": "2.1.161-2",
|
|
5
|
+
"latest_candidate_version": "2.1.177-1",
|
|
6
6
|
"previous_stable_version": "2.1.159-13",
|
|
7
7
|
"manifest_url": "https://raw.githubusercontent.com/bash0816/ClaudeCode-Termux/main/config/claude-termux-release-manifest.json"
|
|
8
8
|
}
|
|
@@ -49,7 +49,7 @@ for _a in "$@"; do
|
|
|
49
49
|
done
|
|
50
50
|
|
|
51
51
|
if [ "$_pf" = "1" ] && [ "${CLAUDE_TERMUX_STDIN:-}" != "inherit" ]; then
|
|
52
|
-
_helper=$(mktemp "${
|
|
52
|
+
_helper=$(mktemp "${TERMUX_TMPDIR}/claude-helper.XXXXXX.js")
|
|
53
53
|
trap 'rm -f "$_helper"' EXIT HUP INT TERM
|
|
54
54
|
cat <<'NODE' > "$_helper"
|
|
55
55
|
const fs = require('fs');
|
|
@@ -73,8 +73,39 @@ class RequestedExit extends Error {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
function cleanupStaleEntryFiles(currentWorkdir = workdir, currentEntryJsOffset = entryJsOffset, currentEntryEndOffset = entryEndOffset, now = Date.now()) {
|
|
77
|
+
const prefix = `cli.${currentEntryJsOffset}.${currentEntryEndOffset}.`;
|
|
78
|
+
const suffix = '.bare-path.js';
|
|
79
|
+
const maxAgeMs = 24 * 60 * 60 * 1000;
|
|
80
|
+
let entries;
|
|
81
|
+
try {
|
|
82
|
+
entries = fs.readdirSync(currentWorkdir, { withFileTypes: true });
|
|
83
|
+
} catch {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
if (!entry.isFile() && !entry.isSymbolicLink()) continue;
|
|
88
|
+
if (!entry.name.startsWith(prefix) || !entry.name.endsWith(suffix)) continue;
|
|
89
|
+
const filePath = path.join(currentWorkdir, entry.name);
|
|
90
|
+
let stats;
|
|
91
|
+
try {
|
|
92
|
+
stats = fs.statSync(filePath);
|
|
93
|
+
} catch {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (Number.isFinite(stats.mtimeMs) && now - stats.mtimeMs < maxAgeMs) continue;
|
|
97
|
+
try {
|
|
98
|
+
fs.rmSync(filePath, { force: true });
|
|
99
|
+
} catch {}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
76
103
|
function ensureEntryFile() {
|
|
77
|
-
|
|
104
|
+
cleanupStaleEntryFiles();
|
|
105
|
+
const extractedFile = path.join(
|
|
106
|
+
workdir,
|
|
107
|
+
`cli.${entryJsOffset}.${entryEndOffset}.${process.pid}.${Date.now().toString(36)}.${Math.random().toString(36).slice(2)}.bare-path.js`,
|
|
108
|
+
);
|
|
78
109
|
const len = entryEndOffset - entryJsOffset;
|
|
79
110
|
if (!(len > 0)) throw new Error('invalid replay offsets');
|
|
80
111
|
|
|
@@ -87,15 +118,165 @@ function ensureEntryFile() {
|
|
|
87
118
|
return extractedFile;
|
|
88
119
|
}
|
|
89
120
|
|
|
121
|
+
function isFullWidthCodePoint(codePoint) {
|
|
122
|
+
return Number.isFinite(codePoint) && (
|
|
123
|
+
codePoint >= 0x1100 && (
|
|
124
|
+
codePoint <= 0x115f ||
|
|
125
|
+
codePoint === 0x2329 ||
|
|
126
|
+
codePoint === 0x232a ||
|
|
127
|
+
(codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
|
|
128
|
+
(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
|
|
129
|
+
(codePoint >= 0xf900 && codePoint <= 0xfaff) ||
|
|
130
|
+
(codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
|
|
131
|
+
(codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
|
|
132
|
+
(codePoint >= 0xff00 && codePoint <= 0xff60) ||
|
|
133
|
+
(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
|
|
134
|
+
(codePoint >= 0x1f300 && codePoint <= 0x1f6ff) ||
|
|
135
|
+
(codePoint >= 0x1fa70 && codePoint <= 0x1faff) ||
|
|
136
|
+
(codePoint >= 0x1f900 && codePoint <= 0x1f9ff) ||
|
|
137
|
+
(codePoint >= 0x20000 && codePoint <= 0x3fffd)
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function graphemeWidth(grapheme) {
|
|
143
|
+
let width = 0;
|
|
144
|
+
const symbols = Array.from(String(grapheme ?? ''));
|
|
145
|
+
const codePoints = symbols.map(symbol => symbol.codePointAt(0)).filter(codePoint => Number.isFinite(codePoint));
|
|
146
|
+
if (codePoints.length === 0) return 0;
|
|
147
|
+
if (codePoints.length > 1 && codePoints.every(codePoint => codePoint >= 0x1f1e6 && codePoint <= 0x1f1ff)) {
|
|
148
|
+
return 2;
|
|
149
|
+
}
|
|
150
|
+
if (codePoints.includes(0x20e3) || codePoints.includes(0x200d)) {
|
|
151
|
+
return 2;
|
|
152
|
+
}
|
|
153
|
+
if (codePoints.includes(0xfe0f) || codePoints.some(codePoint => codePoint >= 0x2600 && codePoint <= 0x27bf)) {
|
|
154
|
+
return 2;
|
|
155
|
+
}
|
|
156
|
+
for (const symbol of symbols) {
|
|
157
|
+
const codePoint = symbol.codePointAt(0);
|
|
158
|
+
if (codePoint === undefined || codePoint === 0) continue;
|
|
159
|
+
if (codePoint < 32 || (codePoint >= 0x7f && codePoint < 0xa0)) continue;
|
|
160
|
+
if (codePoint === 0x200d || codePoint === 0xfe0f) continue;
|
|
161
|
+
if (/\p{M}/u.test(symbol)) continue;
|
|
162
|
+
if (isFullWidthCodePoint(codePoint)) return 2;
|
|
163
|
+
width = 1;
|
|
164
|
+
}
|
|
165
|
+
return width;
|
|
166
|
+
}
|
|
167
|
+
|
|
90
168
|
function stringWidth(value) {
|
|
91
|
-
const text =
|
|
169
|
+
const text = stripANSI(value);
|
|
92
170
|
if (typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function') {
|
|
93
171
|
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
|
94
172
|
let width = 0;
|
|
95
|
-
for (const
|
|
173
|
+
for (const segment of segmenter.segment(text)) width += graphemeWidth(segment.segment);
|
|
96
174
|
return width;
|
|
97
175
|
}
|
|
98
|
-
return Array.from(text).
|
|
176
|
+
return Array.from(text).reduce((width, symbol) => width + graphemeWidth(symbol), 0);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function stripANSI(value) {
|
|
180
|
+
return String(value ?? '')
|
|
181
|
+
.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '')
|
|
182
|
+
.replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, '');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function wrapAnsi(value, columns, options = {}) {
|
|
186
|
+
const text = String(value ?? '');
|
|
187
|
+
const width = Number(columns);
|
|
188
|
+
const hard = options.hard !== false;
|
|
189
|
+
const trim = options.trim === true;
|
|
190
|
+
const wordWrap = options.wordWrap !== false;
|
|
191
|
+
if (!Number.isFinite(width) || width <= 0) return trim ? text.trimEnd() : text;
|
|
192
|
+
|
|
193
|
+
const ansiPattern = /\x1B\[[0-?]*[ -/]*[@-~]|\x1B\][^\x07]*(?:\x07|\x1B\\)/g;
|
|
194
|
+
const segmenter = typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function'
|
|
195
|
+
? new Intl.Segmenter('en', { granularity: 'grapheme' })
|
|
196
|
+
: null;
|
|
197
|
+
const splitVisible = (chunk) => {
|
|
198
|
+
if (chunk === '') return [];
|
|
199
|
+
if (!segmenter) return Array.from(chunk);
|
|
200
|
+
return Array.from(segmenter.segment(chunk), part => part.segment);
|
|
201
|
+
};
|
|
202
|
+
const splitByGrapheme = hard || !wordWrap;
|
|
203
|
+
let result = '';
|
|
204
|
+
let currentWidth = 0;
|
|
205
|
+
let lastIndex = 0;
|
|
206
|
+
const appendVisible = (chunk) => {
|
|
207
|
+
const tokens = splitByGrapheme ? splitVisible(chunk) : (chunk.match(/\s+|[^\s]+/gu) || []);
|
|
208
|
+
for (const token of tokens) {
|
|
209
|
+
if (token === '\n') {
|
|
210
|
+
if (trim) result = result.replace(/[ \t]+$/g, '');
|
|
211
|
+
result += token;
|
|
212
|
+
currentWidth = 0;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const tokenWidth = stringWidth(token);
|
|
216
|
+
const isWhitespace = /^\s+$/u.test(token);
|
|
217
|
+
if (trim && isWhitespace && currentWidth === 0) continue;
|
|
218
|
+
if (currentWidth > 0 && currentWidth + tokenWidth > width) {
|
|
219
|
+
if (!splitByGrapheme && !isWhitespace) {
|
|
220
|
+
result = result.replace(/[ \t]+$/g, '');
|
|
221
|
+
result += '\n';
|
|
222
|
+
currentWidth = 0;
|
|
223
|
+
} else {
|
|
224
|
+
for (const piece of splitVisible(token)) {
|
|
225
|
+
const pieceWidth = stringWidth(piece);
|
|
226
|
+
if (currentWidth > 0 && currentWidth + pieceWidth > width) {
|
|
227
|
+
if (trim) result = result.replace(/[ \t]+$/g, '');
|
|
228
|
+
result += '\n';
|
|
229
|
+
currentWidth = 0;
|
|
230
|
+
}
|
|
231
|
+
if (trim && /^\s+$/u.test(piece) && currentWidth === 0) continue;
|
|
232
|
+
result += piece;
|
|
233
|
+
currentWidth += pieceWidth;
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
result += token;
|
|
239
|
+
currentWidth += tokenWidth;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
for (const match of text.matchAll(ansiPattern)) {
|
|
243
|
+
appendVisible(text.slice(lastIndex, match.index ?? 0));
|
|
244
|
+
result += match[0];
|
|
245
|
+
lastIndex = (match.index ?? 0) + match[0].length;
|
|
246
|
+
}
|
|
247
|
+
appendVisible(text.slice(lastIndex));
|
|
248
|
+
return trim ? result.replace(/[ \t]+$/gm, '') : result;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function stableHash(value, seed) {
|
|
252
|
+
const text = String(value ?? '');
|
|
253
|
+
let hash = 2166136261;
|
|
254
|
+
if (seed !== undefined) {
|
|
255
|
+
const seedText = String(seed ?? '');
|
|
256
|
+
for (let i = 0; i < seedText.length; i += 1) {
|
|
257
|
+
hash ^= seedText.charCodeAt(i);
|
|
258
|
+
hash = Math.imul(hash, 16777619);
|
|
259
|
+
}
|
|
260
|
+
hash ^= 0x9e3779b9;
|
|
261
|
+
hash = Math.imul(hash, 16777619);
|
|
262
|
+
}
|
|
263
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
264
|
+
hash ^= text.charCodeAt(i);
|
|
265
|
+
hash = Math.imul(hash, 16777619);
|
|
266
|
+
}
|
|
267
|
+
return hash >>> 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function replaceRequired(source, pattern, replacement, label, expectedCount) {
|
|
271
|
+
const text = String(source);
|
|
272
|
+
const matches = text.match(pattern);
|
|
273
|
+
if (!matches || matches.length === 0) {
|
|
274
|
+
throw new Error(`rewriteNativeChunkSource: missing ${label}`);
|
|
275
|
+
}
|
|
276
|
+
if (expectedCount !== undefined && matches.length !== expectedCount) {
|
|
277
|
+
throw new Error(`rewriteNativeChunkSource: unexpected ${label} count ${matches.length}`);
|
|
278
|
+
}
|
|
279
|
+
return text.replace(pattern, replacement);
|
|
99
280
|
}
|
|
100
281
|
|
|
101
282
|
function parseScalar(value) {
|
|
@@ -244,6 +425,9 @@ function createFakeRequire(realRequire) {
|
|
|
244
425
|
writable: true,
|
|
245
426
|
});
|
|
246
427
|
}
|
|
428
|
+
if (context.Bun && globalThis.__claudeYaml) {
|
|
429
|
+
context.Bun.YAML = globalThis.__claudeYaml;
|
|
430
|
+
}
|
|
247
431
|
} catch {}
|
|
248
432
|
return context;
|
|
249
433
|
}
|
|
@@ -344,25 +528,60 @@ function createFakeRequire(realRequire) {
|
|
|
344
528
|
};
|
|
345
529
|
}
|
|
346
530
|
|
|
347
|
-
|
|
348
|
-
const
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
531
|
+
function rewriteNativeChunkSource(source) {
|
|
532
|
+
const rawPrefix = 'function(exports, require, module, __filename, __dirname) {';
|
|
533
|
+
const injectedPrefix = rawPrefix + 'var __claudeBun = globalThis.__claudeBunShim;';
|
|
534
|
+
let patched = String(source);
|
|
535
|
+
patched = replaceRequired(
|
|
536
|
+
patched,
|
|
537
|
+
/^function\(exports, require, module, __filename, __dirname\) \{(?:var __claudeBun = globalThis\.__claudeBunShim;)?/,
|
|
538
|
+
injectedPrefix,
|
|
539
|
+
'module wrapper prefix',
|
|
540
|
+
1,
|
|
541
|
+
);
|
|
542
|
+
patched = replaceRequired(
|
|
543
|
+
patched,
|
|
354
544
|
/\btypeof Bun\b/g,
|
|
355
545
|
'typeof __claudeBun',
|
|
356
|
-
|
|
546
|
+
'typeof Bun',
|
|
547
|
+
3,
|
|
548
|
+
);
|
|
549
|
+
patched = replaceRequired(
|
|
550
|
+
patched,
|
|
551
|
+
/\btypeof globalThis\.Bun\b/g,
|
|
552
|
+
'typeof globalThis.__claudeBun',
|
|
553
|
+
'typeof globalThis.Bun',
|
|
554
|
+
1,
|
|
555
|
+
);
|
|
556
|
+
patched = replaceRequired(
|
|
557
|
+
patched,
|
|
558
|
+
/\bglobalThis\.Bun\b/g,
|
|
559
|
+
'globalThis.__claudeBun',
|
|
560
|
+
'globalThis.Bun',
|
|
561
|
+
1,
|
|
562
|
+
);
|
|
563
|
+
patched = replaceRequired(
|
|
564
|
+
patched,
|
|
357
565
|
/\bBun\./g,
|
|
358
566
|
'__claudeBun.',
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
567
|
+
'Bun property access',
|
|
568
|
+
33,
|
|
569
|
+
);
|
|
570
|
+
patched = replaceRequired(
|
|
571
|
+
patched,
|
|
572
|
+
/\bnpmInstallDeprecated:!0\b/g,
|
|
573
|
+
'npmInstallDeprecated:!1',
|
|
574
|
+
'npmInstallDeprecated flag',
|
|
575
|
+
1,
|
|
365
576
|
);
|
|
577
|
+
return patched;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
async function main() {
|
|
581
|
+
let extractedFile;
|
|
582
|
+
extractedFile = ensureEntryFile();
|
|
583
|
+
const code = fs.readFileSync(extractedFile, 'utf8');
|
|
584
|
+
const patchedCode = rewriteNativeChunkSource(code);
|
|
366
585
|
const fn = eval('(' + patchedCode.replace(/\)\s*$/, '') + ')');
|
|
367
586
|
|
|
368
587
|
const originalArgv = process.argv.slice();
|
|
@@ -389,6 +608,9 @@ async function main() {
|
|
|
389
608
|
globalThis.Bun = {
|
|
390
609
|
version: '1.1.8',
|
|
391
610
|
stringWidth,
|
|
611
|
+
wrapAnsi,
|
|
612
|
+
stripANSI,
|
|
613
|
+
hash: stableHash,
|
|
392
614
|
which: (cmd) => {
|
|
393
615
|
try {
|
|
394
616
|
return _realChild.execFileSync('which', [String(cmd)], { encoding: 'utf8' }).trim() || null;
|
|
@@ -424,9 +646,9 @@ async function main() {
|
|
|
424
646
|
gte: (a, b) => _cmp(a, b) >= 0,
|
|
425
647
|
lt: (a, b) => _cmp(a, b) < 0,
|
|
426
648
|
lte: (a, b) => _cmp(a, b) <= 0,
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
649
|
+
};
|
|
650
|
+
})(),
|
|
651
|
+
YAML: globalThis.__claudeYaml,
|
|
430
652
|
};
|
|
431
653
|
Object.assign(globalThis.__claudeBunShim, globalThis.Bun);
|
|
432
654
|
if (typeof globalThis.__claudeBunShim.gc !== 'function') {
|
|
@@ -439,10 +661,13 @@ async function main() {
|
|
|
439
661
|
throw new RequestedExit(code);
|
|
440
662
|
};
|
|
441
663
|
|
|
664
|
+
const printWaitMs = Number(process.env.CLAUDE_TERMUX_PRINT_WAIT_MS || 5000);
|
|
442
665
|
const moduleLike = { exports: {} };
|
|
443
666
|
const maybePromise = fn(moduleLike.exports, fakeRequire, moduleLike, extractedFile, workdir);
|
|
444
667
|
if (maybePromise && typeof maybePromise.then === 'function') await maybePromise;
|
|
445
|
-
|
|
668
|
+
if (Number.isFinite(printWaitMs) && printWaitMs > 0) {
|
|
669
|
+
await new Promise(resolve => setTimeout(resolve, printWaitMs));
|
|
670
|
+
}
|
|
446
671
|
if (asyncErrors.length > 0) throw asyncErrors[0];
|
|
447
672
|
} catch (error) {
|
|
448
673
|
if (error instanceof RequestedExit) {
|
|
@@ -453,6 +678,11 @@ async function main() {
|
|
|
453
678
|
} finally {
|
|
454
679
|
process.removeListener('uncaughtException', onAsyncError);
|
|
455
680
|
process.removeListener('unhandledRejection', onAsyncError);
|
|
681
|
+
if (extractedFile) {
|
|
682
|
+
try {
|
|
683
|
+
fs.rmSync(extractedFile, { force: true });
|
|
684
|
+
} catch {}
|
|
685
|
+
}
|
|
456
686
|
process.argv = originalArgv;
|
|
457
687
|
process.exit = originalExit;
|
|
458
688
|
try {
|
|
@@ -482,13 +712,14 @@ NODE
|
|
|
482
712
|
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-1}"
|
|
483
713
|
export ENABLE_CLAUDEAI_MCP_SERVERS="${ENABLE_CLAUDEAI_MCP_SERVERS:-0}"
|
|
484
714
|
export CLAUDE_CODE_SIMPLE="${CLAUDE_CODE_SIMPLE:-0}"
|
|
715
|
+
export DISABLE_INSTALLATION_CHECKS="${DISABLE_INSTALLATION_CHECKS:-true}"
|
|
485
716
|
node "$_helper" "$@" </dev/null
|
|
486
717
|
_status=$?
|
|
487
718
|
rm -f "$_helper"
|
|
488
719
|
trap - EXIT HUP INT TERM
|
|
489
720
|
exit "$_status"
|
|
490
721
|
else
|
|
491
|
-
_bootstrap=$(mktemp "${
|
|
722
|
+
_bootstrap=$(mktemp "${TERMUX_TMPDIR}/claude-bootstrap.XXXXXX.js")
|
|
492
723
|
trap 'rm -f "$_bootstrap"' EXIT HUP INT TERM
|
|
493
724
|
cat <<'NODE' > "$_bootstrap"
|
|
494
725
|
const fs = require('fs');
|
|
@@ -512,8 +743,39 @@ class RequestedExit extends Error {
|
|
|
512
743
|
}
|
|
513
744
|
}
|
|
514
745
|
|
|
746
|
+
function cleanupStaleEntryFiles(currentWorkdir = workdir, currentEntryJsOffset = entryJsOffset, currentEntryEndOffset = entryEndOffset, now = Date.now()) {
|
|
747
|
+
const prefix = `cli.${currentEntryJsOffset}.${currentEntryEndOffset}.`;
|
|
748
|
+
const suffix = '.bare-path.js';
|
|
749
|
+
const maxAgeMs = 24 * 60 * 60 * 1000;
|
|
750
|
+
let entries;
|
|
751
|
+
try {
|
|
752
|
+
entries = fs.readdirSync(currentWorkdir, { withFileTypes: true });
|
|
753
|
+
} catch {
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
for (const entry of entries) {
|
|
757
|
+
if (!entry.isFile() && !entry.isSymbolicLink()) continue;
|
|
758
|
+
if (!entry.name.startsWith(prefix) || !entry.name.endsWith(suffix)) continue;
|
|
759
|
+
const filePath = path.join(currentWorkdir, entry.name);
|
|
760
|
+
let stats;
|
|
761
|
+
try {
|
|
762
|
+
stats = fs.statSync(filePath);
|
|
763
|
+
} catch {
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
if (Number.isFinite(stats.mtimeMs) && now - stats.mtimeMs < maxAgeMs) continue;
|
|
767
|
+
try {
|
|
768
|
+
fs.rmSync(filePath, { force: true });
|
|
769
|
+
} catch {}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
515
773
|
function ensureEntryFile() {
|
|
516
|
-
|
|
774
|
+
cleanupStaleEntryFiles();
|
|
775
|
+
const extractedFile = path.join(
|
|
776
|
+
workdir,
|
|
777
|
+
`cli.${entryJsOffset}.${entryEndOffset}.${process.pid}.${Date.now().toString(36)}.${Math.random().toString(36).slice(2)}.bare-path.js`,
|
|
778
|
+
);
|
|
517
779
|
const len = entryEndOffset - entryJsOffset;
|
|
518
780
|
if (!(len > 0)) throw new Error('invalid replay offsets');
|
|
519
781
|
|
|
@@ -526,15 +788,165 @@ function ensureEntryFile() {
|
|
|
526
788
|
return extractedFile;
|
|
527
789
|
}
|
|
528
790
|
|
|
791
|
+
function isFullWidthCodePoint(codePoint) {
|
|
792
|
+
return Number.isFinite(codePoint) && (
|
|
793
|
+
codePoint >= 0x1100 && (
|
|
794
|
+
codePoint <= 0x115f ||
|
|
795
|
+
codePoint === 0x2329 ||
|
|
796
|
+
codePoint === 0x232a ||
|
|
797
|
+
(codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
|
|
798
|
+
(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
|
|
799
|
+
(codePoint >= 0xf900 && codePoint <= 0xfaff) ||
|
|
800
|
+
(codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
|
|
801
|
+
(codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
|
|
802
|
+
(codePoint >= 0xff00 && codePoint <= 0xff60) ||
|
|
803
|
+
(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
|
|
804
|
+
(codePoint >= 0x1f300 && codePoint <= 0x1f6ff) ||
|
|
805
|
+
(codePoint >= 0x1fa70 && codePoint <= 0x1faff) ||
|
|
806
|
+
(codePoint >= 0x1f900 && codePoint <= 0x1f9ff) ||
|
|
807
|
+
(codePoint >= 0x20000 && codePoint <= 0x3fffd)
|
|
808
|
+
)
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function graphemeWidth(grapheme) {
|
|
813
|
+
let width = 0;
|
|
814
|
+
const symbols = Array.from(String(grapheme ?? ''));
|
|
815
|
+
const codePoints = symbols.map(symbol => symbol.codePointAt(0)).filter(codePoint => Number.isFinite(codePoint));
|
|
816
|
+
if (codePoints.length === 0) return 0;
|
|
817
|
+
if (codePoints.length > 1 && codePoints.every(codePoint => codePoint >= 0x1f1e6 && codePoint <= 0x1f1ff)) {
|
|
818
|
+
return 2;
|
|
819
|
+
}
|
|
820
|
+
if (codePoints.includes(0x20e3) || codePoints.includes(0x200d)) {
|
|
821
|
+
return 2;
|
|
822
|
+
}
|
|
823
|
+
if (codePoints.includes(0xfe0f) || codePoints.some(codePoint => codePoint >= 0x2600 && codePoint <= 0x27bf)) {
|
|
824
|
+
return 2;
|
|
825
|
+
}
|
|
826
|
+
for (const symbol of symbols) {
|
|
827
|
+
const codePoint = symbol.codePointAt(0);
|
|
828
|
+
if (codePoint === undefined || codePoint === 0) continue;
|
|
829
|
+
if (codePoint < 32 || (codePoint >= 0x7f && codePoint < 0xa0)) continue;
|
|
830
|
+
if (codePoint === 0x200d || codePoint === 0xfe0f) continue;
|
|
831
|
+
if (/\p{M}/u.test(symbol)) continue;
|
|
832
|
+
if (isFullWidthCodePoint(codePoint)) return 2;
|
|
833
|
+
width = 1;
|
|
834
|
+
}
|
|
835
|
+
return width;
|
|
836
|
+
}
|
|
837
|
+
|
|
529
838
|
function stringWidth(value) {
|
|
530
|
-
const text =
|
|
839
|
+
const text = stripANSI(value);
|
|
531
840
|
if (typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function') {
|
|
532
841
|
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
|
533
842
|
let width = 0;
|
|
534
|
-
for (const
|
|
843
|
+
for (const segment of segmenter.segment(text)) width += graphemeWidth(segment.segment);
|
|
535
844
|
return width;
|
|
536
845
|
}
|
|
537
|
-
return Array.from(text).
|
|
846
|
+
return Array.from(text).reduce((width, symbol) => width + graphemeWidth(symbol), 0);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function stripANSI(value) {
|
|
850
|
+
return String(value ?? '')
|
|
851
|
+
.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '')
|
|
852
|
+
.replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, '');
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
function wrapAnsi(value, columns, options = {}) {
|
|
856
|
+
const text = String(value ?? '');
|
|
857
|
+
const width = Number(columns);
|
|
858
|
+
const hard = options.hard !== false;
|
|
859
|
+
const trim = options.trim === true;
|
|
860
|
+
const wordWrap = options.wordWrap !== false;
|
|
861
|
+
if (!Number.isFinite(width) || width <= 0) return trim ? text.trimEnd() : text;
|
|
862
|
+
|
|
863
|
+
const ansiPattern = /\x1B\[[0-?]*[ -/]*[@-~]|\x1B\][^\x07]*(?:\x07|\x1B\\)/g;
|
|
864
|
+
const segmenter = typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function'
|
|
865
|
+
? new Intl.Segmenter('en', { granularity: 'grapheme' })
|
|
866
|
+
: null;
|
|
867
|
+
const splitVisible = (chunk) => {
|
|
868
|
+
if (chunk === '') return [];
|
|
869
|
+
if (!segmenter) return Array.from(chunk);
|
|
870
|
+
return Array.from(segmenter.segment(chunk), part => part.segment);
|
|
871
|
+
};
|
|
872
|
+
const splitByGrapheme = hard || !wordWrap;
|
|
873
|
+
let result = '';
|
|
874
|
+
let currentWidth = 0;
|
|
875
|
+
let lastIndex = 0;
|
|
876
|
+
const appendVisible = (chunk) => {
|
|
877
|
+
const tokens = splitByGrapheme ? splitVisible(chunk) : (chunk.match(/\s+|[^\s]+/gu) || []);
|
|
878
|
+
for (const token of tokens) {
|
|
879
|
+
if (token === '\n') {
|
|
880
|
+
if (trim) result = result.replace(/[ \t]+$/g, '');
|
|
881
|
+
result += token;
|
|
882
|
+
currentWidth = 0;
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
const tokenWidth = stringWidth(token);
|
|
886
|
+
const isWhitespace = /^\s+$/u.test(token);
|
|
887
|
+
if (trim && isWhitespace && currentWidth === 0) continue;
|
|
888
|
+
if (currentWidth > 0 && currentWidth + tokenWidth > width) {
|
|
889
|
+
if (!splitByGrapheme && !isWhitespace) {
|
|
890
|
+
result = result.replace(/[ \t]+$/g, '');
|
|
891
|
+
result += '\n';
|
|
892
|
+
currentWidth = 0;
|
|
893
|
+
} else {
|
|
894
|
+
for (const piece of splitVisible(token)) {
|
|
895
|
+
const pieceWidth = stringWidth(piece);
|
|
896
|
+
if (currentWidth > 0 && currentWidth + pieceWidth > width) {
|
|
897
|
+
if (trim) result = result.replace(/[ \t]+$/g, '');
|
|
898
|
+
result += '\n';
|
|
899
|
+
currentWidth = 0;
|
|
900
|
+
}
|
|
901
|
+
if (trim && /^\s+$/u.test(piece) && currentWidth === 0) continue;
|
|
902
|
+
result += piece;
|
|
903
|
+
currentWidth += pieceWidth;
|
|
904
|
+
}
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
result += token;
|
|
909
|
+
currentWidth += tokenWidth;
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
for (const match of text.matchAll(ansiPattern)) {
|
|
913
|
+
appendVisible(text.slice(lastIndex, match.index ?? 0));
|
|
914
|
+
result += match[0];
|
|
915
|
+
lastIndex = (match.index ?? 0) + match[0].length;
|
|
916
|
+
}
|
|
917
|
+
appendVisible(text.slice(lastIndex));
|
|
918
|
+
return trim ? result.replace(/[ \t]+$/gm, '') : result;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function stableHash(value, seed) {
|
|
922
|
+
const text = String(value ?? '');
|
|
923
|
+
let hash = 2166136261;
|
|
924
|
+
if (seed !== undefined) {
|
|
925
|
+
const seedText = String(seed ?? '');
|
|
926
|
+
for (let i = 0; i < seedText.length; i += 1) {
|
|
927
|
+
hash ^= seedText.charCodeAt(i);
|
|
928
|
+
hash = Math.imul(hash, 16777619);
|
|
929
|
+
}
|
|
930
|
+
hash ^= 0x9e3779b9;
|
|
931
|
+
hash = Math.imul(hash, 16777619);
|
|
932
|
+
}
|
|
933
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
934
|
+
hash ^= text.charCodeAt(i);
|
|
935
|
+
hash = Math.imul(hash, 16777619);
|
|
936
|
+
}
|
|
937
|
+
return hash >>> 0;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function replaceRequired(source, pattern, replacement, label, expectedCount) {
|
|
941
|
+
const text = String(source);
|
|
942
|
+
const matches = text.match(pattern);
|
|
943
|
+
if (!matches || matches.length === 0) {
|
|
944
|
+
throw new Error(`rewriteNativeChunkSource: missing ${label}`);
|
|
945
|
+
}
|
|
946
|
+
if (expectedCount !== undefined && matches.length !== expectedCount) {
|
|
947
|
+
throw new Error(`rewriteNativeChunkSource: unexpected ${label} count ${matches.length}`);
|
|
948
|
+
}
|
|
949
|
+
return text.replace(pattern, replacement);
|
|
538
950
|
}
|
|
539
951
|
|
|
540
952
|
function parseScalar(value) {
|
|
@@ -633,6 +1045,63 @@ function createFakeRequire(realRequire) {
|
|
|
633
1045
|
const realChild = realRequire('child_process');
|
|
634
1046
|
const realVm = realRequire('vm');
|
|
635
1047
|
|
|
1048
|
+
function injectBunIntoContext(context) {
|
|
1049
|
+
if (!context || typeof context !== 'object') return context;
|
|
1050
|
+
try {
|
|
1051
|
+
if (!Object.prototype.hasOwnProperty.call(context, '__claudeYaml')) {
|
|
1052
|
+
Object.defineProperty(context, '__claudeYaml', {
|
|
1053
|
+
value: globalThis.__claudeYaml,
|
|
1054
|
+
configurable: true,
|
|
1055
|
+
writable: true,
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
if (!Object.prototype.hasOwnProperty.call(context, '__claudeBunShim')) {
|
|
1059
|
+
Object.defineProperty(context, '__claudeBunShim', {
|
|
1060
|
+
value: globalThis.__claudeBunShim,
|
|
1061
|
+
configurable: true,
|
|
1062
|
+
writable: true,
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
if (!Object.prototype.hasOwnProperty.call(context, '__claudeBun')) {
|
|
1066
|
+
Object.defineProperty(context, '__claudeBun', {
|
|
1067
|
+
value: globalThis.__claudeBunShim,
|
|
1068
|
+
configurable: true,
|
|
1069
|
+
writable: true,
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
if (Object.prototype.hasOwnProperty.call(context, 'Bun')) {
|
|
1073
|
+
if (context.Bun && typeof context.Bun === 'object' && context.Bun !== globalThis.Bun) {
|
|
1074
|
+
try {
|
|
1075
|
+
context.Bun = globalThis.Bun;
|
|
1076
|
+
} catch {
|
|
1077
|
+
Object.defineProperty(context, 'Bun', {
|
|
1078
|
+
value: globalThis.Bun,
|
|
1079
|
+
configurable: true,
|
|
1080
|
+
writable: true,
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
if (!context.Bun || typeof context.Bun !== 'object') {
|
|
1085
|
+
Object.defineProperty(context, 'Bun', {
|
|
1086
|
+
value: globalThis.Bun,
|
|
1087
|
+
configurable: true,
|
|
1088
|
+
writable: true,
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
} else {
|
|
1092
|
+
Object.defineProperty(context, 'Bun', {
|
|
1093
|
+
value: globalThis.Bun,
|
|
1094
|
+
configurable: true,
|
|
1095
|
+
writable: true,
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
if (context.Bun && globalThis.__claudeYaml) {
|
|
1099
|
+
context.Bun.YAML = globalThis.__claudeYaml;
|
|
1100
|
+
}
|
|
1101
|
+
} catch {}
|
|
1102
|
+
return context;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
636
1105
|
function rewriteArgs(args) {
|
|
637
1106
|
if (
|
|
638
1107
|
Array.isArray(args) &&
|
|
@@ -729,25 +1198,60 @@ function createFakeRequire(realRequire) {
|
|
|
729
1198
|
};
|
|
730
1199
|
}
|
|
731
1200
|
|
|
732
|
-
|
|
733
|
-
const
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
1201
|
+
function rewriteNativeChunkSource(source) {
|
|
1202
|
+
const rawPrefix = 'function(exports, require, module, __filename, __dirname) {';
|
|
1203
|
+
const injectedPrefix = rawPrefix + 'var __claudeBun = globalThis.__claudeBunShim;';
|
|
1204
|
+
let patched = String(source);
|
|
1205
|
+
patched = replaceRequired(
|
|
1206
|
+
patched,
|
|
1207
|
+
/^function\(exports, require, module, __filename, __dirname\) \{(?:var __claudeBun = globalThis\.__claudeBunShim;)?/,
|
|
1208
|
+
injectedPrefix,
|
|
1209
|
+
'module wrapper prefix',
|
|
1210
|
+
1,
|
|
1211
|
+
);
|
|
1212
|
+
patched = replaceRequired(
|
|
1213
|
+
patched,
|
|
739
1214
|
/\btypeof Bun\b/g,
|
|
740
1215
|
'typeof __claudeBun',
|
|
741
|
-
|
|
1216
|
+
'typeof Bun',
|
|
1217
|
+
3,
|
|
1218
|
+
);
|
|
1219
|
+
patched = replaceRequired(
|
|
1220
|
+
patched,
|
|
1221
|
+
/\btypeof globalThis\.Bun\b/g,
|
|
1222
|
+
'typeof globalThis.__claudeBun',
|
|
1223
|
+
'typeof globalThis.Bun',
|
|
1224
|
+
1,
|
|
1225
|
+
);
|
|
1226
|
+
patched = replaceRequired(
|
|
1227
|
+
patched,
|
|
1228
|
+
/\bglobalThis\.Bun\b/g,
|
|
1229
|
+
'globalThis.__claudeBun',
|
|
1230
|
+
'globalThis.Bun',
|
|
1231
|
+
1,
|
|
1232
|
+
);
|
|
1233
|
+
patched = replaceRequired(
|
|
1234
|
+
patched,
|
|
742
1235
|
/\bBun\./g,
|
|
743
1236
|
'__claudeBun.',
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
1237
|
+
'Bun property access',
|
|
1238
|
+
33,
|
|
1239
|
+
);
|
|
1240
|
+
patched = replaceRequired(
|
|
1241
|
+
patched,
|
|
1242
|
+
/\bnpmInstallDeprecated:!0\b/g,
|
|
1243
|
+
'npmInstallDeprecated:!1',
|
|
1244
|
+
'npmInstallDeprecated flag',
|
|
1245
|
+
1,
|
|
750
1246
|
);
|
|
1247
|
+
return patched;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
async function main() {
|
|
1251
|
+
let extractedFile;
|
|
1252
|
+
extractedFile = ensureEntryFile();
|
|
1253
|
+
const code = fs.readFileSync(extractedFile, 'utf8');
|
|
1254
|
+
const patchedCode = rewriteNativeChunkSource(code);
|
|
751
1255
|
const fn = eval('(' + patchedCode.replace(/\)\s*$/, '') + ')');
|
|
752
1256
|
|
|
753
1257
|
const originalArgv = process.argv.slice();
|
|
@@ -774,6 +1278,9 @@ async function main() {
|
|
|
774
1278
|
globalThis.Bun = {
|
|
775
1279
|
version: '1.1.8',
|
|
776
1280
|
stringWidth,
|
|
1281
|
+
wrapAnsi,
|
|
1282
|
+
stripANSI,
|
|
1283
|
+
hash: stableHash,
|
|
777
1284
|
which: (cmd) => {
|
|
778
1285
|
try {
|
|
779
1286
|
return _realChild.execFileSync('which', [String(cmd)], { encoding: 'utf8' }).trim() || null;
|
|
@@ -810,8 +1317,8 @@ async function main() {
|
|
|
810
1317
|
lt: (a, b) => _cmp(a, b) < 0,
|
|
811
1318
|
lte: (a, b) => _cmp(a, b) <= 0,
|
|
812
1319
|
};
|
|
813
|
-
|
|
814
|
-
|
|
1320
|
+
})(),
|
|
1321
|
+
YAML: globalThis.__claudeYaml,
|
|
815
1322
|
};
|
|
816
1323
|
Object.assign(globalThis.__claudeBunShim, globalThis.Bun);
|
|
817
1324
|
if (typeof globalThis.__claudeBunShim.gc !== 'function') {
|
|
@@ -827,8 +1334,6 @@ async function main() {
|
|
|
827
1334
|
const moduleLike = { exports: {} };
|
|
828
1335
|
const maybePromise = fn(moduleLike.exports, fakeRequire, moduleLike, extractedFile, workdir);
|
|
829
1336
|
if (maybePromise && typeof maybePromise.then === 'function') await maybePromise;
|
|
830
|
-
const _waitMs = Number(process.env.CLAUDE_TERMUX_PRINT_WAIT_MS || 200);
|
|
831
|
-
await new Promise(resolve => setTimeout(resolve, _waitMs));
|
|
832
1337
|
if (asyncErrors.length > 0) throw asyncErrors[0];
|
|
833
1338
|
} catch (error) {
|
|
834
1339
|
if (error instanceof RequestedExit) {
|
|
@@ -841,17 +1346,25 @@ async function main() {
|
|
|
841
1346
|
process.removeListener('unhandledRejection', onAsyncError);
|
|
842
1347
|
process.argv = originalArgv;
|
|
843
1348
|
process.exit = originalExit;
|
|
844
|
-
|
|
845
|
-
if (
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
1349
|
+
process.once('exit', () => {
|
|
1350
|
+
if (extractedFile) {
|
|
1351
|
+
try {
|
|
1352
|
+
fs.rmSync(extractedFile, { force: true });
|
|
1353
|
+
} catch {}
|
|
849
1354
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1355
|
+
try {
|
|
1356
|
+
if (originalBun === undefined) {
|
|
1357
|
+
delete process.versions.bun;
|
|
1358
|
+
} else {
|
|
1359
|
+
Object.defineProperty(process.versions, 'bun', { value: originalBun, configurable: true });
|
|
1360
|
+
}
|
|
1361
|
+
} catch {}
|
|
1362
|
+
delete globalThis.__claudeYaml;
|
|
1363
|
+
delete globalThis.__claudeBunShim;
|
|
1364
|
+
delete globalThis.__claudeBun;
|
|
1365
|
+
if (hadGlobalBun) globalThis.Bun = originalGlobalBun;
|
|
1366
|
+
else delete globalThis.Bun;
|
|
1367
|
+
});
|
|
855
1368
|
}
|
|
856
1369
|
}
|
|
857
1370
|
|
|
@@ -866,6 +1379,7 @@ main().catch(error => {
|
|
|
866
1379
|
NODE
|
|
867
1380
|
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-1}"
|
|
868
1381
|
export ENABLE_CLAUDEAI_MCP_SERVERS="${ENABLE_CLAUDEAI_MCP_SERVERS:-0}"
|
|
1382
|
+
export DISABLE_INSTALLATION_CHECKS="${DISABLE_INSTALLATION_CHECKS:-true}"
|
|
869
1383
|
node "$_bootstrap" "$@"
|
|
870
1384
|
_status=$?
|
|
871
1385
|
rm -f "$_bootstrap"
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
const child_process = require('child_process');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const vm = require('vm');
|
|
11
|
+
|
|
12
|
+
const scriptPath = path.join(__dirname, 'termux-run-claude-native.sh');
|
|
13
|
+
const script = fs.readFileSync(scriptPath, 'utf8');
|
|
14
|
+
|
|
15
|
+
function extractBlock(marker, trailer) {
|
|
16
|
+
const start = script.indexOf(marker);
|
|
17
|
+
assert.notEqual(start, -1, `missing marker: ${marker}`);
|
|
18
|
+
const bodyStart = script.indexOf('\n', start);
|
|
19
|
+
assert.notEqual(bodyStart, -1, `missing body start: ${marker}`);
|
|
20
|
+
const end = script.indexOf(trailer, bodyStart + 1);
|
|
21
|
+
assert.notEqual(end, -1, `missing trailer: ${trailer}`);
|
|
22
|
+
return script.slice(bodyStart + 1, end);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function extractFunction(block, startName, endName) {
|
|
26
|
+
const start = block.indexOf(startName);
|
|
27
|
+
assert.notEqual(start, -1, `missing function: ${startName}`);
|
|
28
|
+
const end = block.indexOf(endName, start);
|
|
29
|
+
assert.notEqual(end, -1, `missing end marker: ${endName}`);
|
|
30
|
+
return block.slice(start, end).trimEnd();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function loadHelperApi() {
|
|
34
|
+
const helperBlock = extractBlock('cat <<\'NODE\' > "$_helper"', '\n export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=');
|
|
35
|
+
const replaceSource = extractFunction(
|
|
36
|
+
helperBlock,
|
|
37
|
+
'function replaceRequired(source, pattern, replacement, label, expectedCount) {',
|
|
38
|
+
'\n\nfunction parseScalar(value) {',
|
|
39
|
+
);
|
|
40
|
+
const fullWidthSource = extractFunction(
|
|
41
|
+
helperBlock,
|
|
42
|
+
'function isFullWidthCodePoint(codePoint) {',
|
|
43
|
+
'\n\nfunction graphemeWidth(grapheme) {',
|
|
44
|
+
);
|
|
45
|
+
const graphemeWidthSource = extractFunction(
|
|
46
|
+
helperBlock,
|
|
47
|
+
'function graphemeWidth(grapheme) {',
|
|
48
|
+
'\n\nfunction stringWidth(value) {',
|
|
49
|
+
);
|
|
50
|
+
const stringWidthSource = extractFunction(
|
|
51
|
+
helperBlock,
|
|
52
|
+
'function stringWidth(value) {',
|
|
53
|
+
'\n\nfunction stripANSI(value) {',
|
|
54
|
+
);
|
|
55
|
+
const stripAnsiSource = extractFunction(
|
|
56
|
+
helperBlock,
|
|
57
|
+
'function stripANSI(value) {',
|
|
58
|
+
'\n\nfunction wrapAnsi(value, columns, options = {}) {',
|
|
59
|
+
);
|
|
60
|
+
const cleanupSource = extractFunction(
|
|
61
|
+
helperBlock,
|
|
62
|
+
'function cleanupStaleEntryFiles(currentWorkdir = workdir, currentEntryJsOffset = entryJsOffset, currentEntryEndOffset = entryEndOffset, now = Date.now()) {',
|
|
63
|
+
'\n\nfunction ensureEntryFile() {',
|
|
64
|
+
);
|
|
65
|
+
const wrapAnsiSource = extractFunction(
|
|
66
|
+
helperBlock,
|
|
67
|
+
'function wrapAnsi(value, columns, options = {}) {',
|
|
68
|
+
'\n\nfunction stableHash(value, seed) {',
|
|
69
|
+
);
|
|
70
|
+
const stableHashSource = extractFunction(
|
|
71
|
+
helperBlock,
|
|
72
|
+
'function stableHash(value, seed) {',
|
|
73
|
+
'\n\nfunction replaceRequired(source, pattern, replacement, label, expectedCount) {',
|
|
74
|
+
);
|
|
75
|
+
const rewriteSource = extractFunction(
|
|
76
|
+
helperBlock,
|
|
77
|
+
'function rewriteNativeChunkSource(source) {',
|
|
78
|
+
'\n\nasync function main() {',
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const context = vm.createContext({ module: { exports: {} }, exports: {}, fs, path });
|
|
82
|
+
vm.runInContext(
|
|
83
|
+
`${replaceSource}\n${cleanupSource}\n${fullWidthSource}\n${graphemeWidthSource}\n${stripAnsiSource}\n${stringWidthSource}\n${wrapAnsiSource}\n${stableHashSource}\n${rewriteSource}\nmodule.exports = { replaceRequired, cleanupStaleEntryFiles, isFullWidthCodePoint, graphemeWidth, stripANSI, stringWidth, wrapAnsi, stableHash, rewriteNativeChunkSource };`,
|
|
84
|
+
context,
|
|
85
|
+
);
|
|
86
|
+
return context.module.exports;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function sha256(filePath) {
|
|
90
|
+
return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
test('helper and bootstrap rewrite helpers stay identical', () => {
|
|
94
|
+
const helperBlock = extractBlock('cat <<\'NODE\' > "$_helper"', '\n export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=');
|
|
95
|
+
const bootstrapBlock = extractBlock('cat <<\'NODE\' > "$_bootstrap"', '\n export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=');
|
|
96
|
+
|
|
97
|
+
const helperReplace = extractFunction(
|
|
98
|
+
helperBlock,
|
|
99
|
+
'function replaceRequired(source, pattern, replacement, label, expectedCount) {',
|
|
100
|
+
'\n\nfunction parseScalar(value) {',
|
|
101
|
+
);
|
|
102
|
+
const bootstrapReplace = extractFunction(
|
|
103
|
+
bootstrapBlock,
|
|
104
|
+
'function replaceRequired(source, pattern, replacement, label, expectedCount) {',
|
|
105
|
+
'\n\nfunction parseScalar(value) {',
|
|
106
|
+
);
|
|
107
|
+
const helperRewrite = extractFunction(
|
|
108
|
+
helperBlock,
|
|
109
|
+
'function rewriteNativeChunkSource(source) {',
|
|
110
|
+
'\n\nasync function main() {',
|
|
111
|
+
);
|
|
112
|
+
const bootstrapRewrite = extractFunction(
|
|
113
|
+
bootstrapBlock,
|
|
114
|
+
'function rewriteNativeChunkSource(source) {',
|
|
115
|
+
'\n\nasync function main() {',
|
|
116
|
+
);
|
|
117
|
+
const helperWrapAnsi = extractFunction(
|
|
118
|
+
helperBlock,
|
|
119
|
+
'function wrapAnsi(value, columns, options = {}) {',
|
|
120
|
+
'\n\nfunction stableHash(value, seed) {',
|
|
121
|
+
);
|
|
122
|
+
const helperCleanup = extractFunction(
|
|
123
|
+
helperBlock,
|
|
124
|
+
'function cleanupStaleEntryFiles(currentWorkdir = workdir, currentEntryJsOffset = entryJsOffset, currentEntryEndOffset = entryEndOffset, now = Date.now()) {',
|
|
125
|
+
'\n\nfunction ensureEntryFile() {',
|
|
126
|
+
);
|
|
127
|
+
const bootstrapWrapAnsi = extractFunction(
|
|
128
|
+
bootstrapBlock,
|
|
129
|
+
'function wrapAnsi(value, columns, options = {}) {',
|
|
130
|
+
'\n\nfunction stableHash(value, seed) {',
|
|
131
|
+
);
|
|
132
|
+
const bootstrapCleanup = extractFunction(
|
|
133
|
+
bootstrapBlock,
|
|
134
|
+
'function cleanupStaleEntryFiles(currentWorkdir = workdir, currentEntryJsOffset = entryJsOffset, currentEntryEndOffset = entryEndOffset, now = Date.now()) {',
|
|
135
|
+
'\n\nfunction ensureEntryFile() {',
|
|
136
|
+
);
|
|
137
|
+
const helperStableHash = extractFunction(
|
|
138
|
+
helperBlock,
|
|
139
|
+
'function stableHash(value, seed) {',
|
|
140
|
+
'\n\nfunction replaceRequired(source, pattern, replacement, label, expectedCount) {',
|
|
141
|
+
);
|
|
142
|
+
const bootstrapStableHash = extractFunction(
|
|
143
|
+
bootstrapBlock,
|
|
144
|
+
'function stableHash(value, seed) {',
|
|
145
|
+
'\n\nfunction replaceRequired(source, pattern, replacement, label, expectedCount) {',
|
|
146
|
+
);
|
|
147
|
+
const helperStringWidth = extractFunction(
|
|
148
|
+
helperBlock,
|
|
149
|
+
'function isFullWidthCodePoint(codePoint) {',
|
|
150
|
+
'\n\nfunction graphemeWidth(grapheme) {',
|
|
151
|
+
) + '\n\n' + extractFunction(
|
|
152
|
+
helperBlock,
|
|
153
|
+
'function graphemeWidth(grapheme) {',
|
|
154
|
+
'\n\nfunction stringWidth(value) {',
|
|
155
|
+
) + '\n\n' + extractFunction(
|
|
156
|
+
helperBlock,
|
|
157
|
+
'function stringWidth(value) {',
|
|
158
|
+
'\n\nfunction stripANSI(value) {',
|
|
159
|
+
);
|
|
160
|
+
const bootstrapStringWidth = extractFunction(
|
|
161
|
+
bootstrapBlock,
|
|
162
|
+
'function isFullWidthCodePoint(codePoint) {',
|
|
163
|
+
'\n\nfunction graphemeWidth(grapheme) {',
|
|
164
|
+
) + '\n\n' + extractFunction(
|
|
165
|
+
bootstrapBlock,
|
|
166
|
+
'function graphemeWidth(grapheme) {',
|
|
167
|
+
'\n\nfunction stringWidth(value) {',
|
|
168
|
+
) + '\n\n' + extractFunction(
|
|
169
|
+
bootstrapBlock,
|
|
170
|
+
'function stringWidth(value) {',
|
|
171
|
+
'\n\nfunction stripANSI(value) {',
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
assert.equal(helperReplace, bootstrapReplace);
|
|
175
|
+
assert.equal(helperRewrite, bootstrapRewrite);
|
|
176
|
+
assert.equal(helperWrapAnsi, bootstrapWrapAnsi);
|
|
177
|
+
assert.equal(helperCleanup, bootstrapCleanup);
|
|
178
|
+
assert.equal(helperStableHash, bootstrapStableHash);
|
|
179
|
+
assert.equal(helperStringWidth, bootstrapStringWidth);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('print path does not defer cleanup to exit', () => {
|
|
183
|
+
const helperBlock = extractBlock('cat <<\'NODE\' > "$_helper"', '\n export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=');
|
|
184
|
+
|
|
185
|
+
assert.equal(helperBlock.includes('CLAUDE_TERMUX_PRINT_WAIT_MS'), true);
|
|
186
|
+
assert.equal(helperBlock.includes('setTimeout(resolve, printWaitMs)'), true);
|
|
187
|
+
assert.equal(helperBlock.includes('process.once(\'exit\''), false);
|
|
188
|
+
assert.equal(helperBlock.includes('process.removeListener(\'uncaughtException\''), true);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('bootstrap path defers cleanup to exit', () => {
|
|
192
|
+
const bootstrapBlock = extractBlock('cat <<\'NODE\' > "$_bootstrap"', '\n export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=');
|
|
193
|
+
|
|
194
|
+
assert.equal(bootstrapBlock.includes('CLAUDE_TERMUX_PRINT_WAIT_MS'), false);
|
|
195
|
+
assert.equal(bootstrapBlock.includes('setTimeout(resolve, printWaitMs)'), false);
|
|
196
|
+
assert.equal(bootstrapBlock.includes('process.once(\'exit\''), true);
|
|
197
|
+
assert.equal(bootstrapBlock.includes('process.removeListener(\'uncaughtException\''), true);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('entry extraction uses a process-unique filename', () => {
|
|
201
|
+
const helperBlock = extractBlock('cat <<\'NODE\' > "$_helper"', '\n export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=');
|
|
202
|
+
const bootstrapBlock = extractBlock('cat <<\'NODE\' > "$_bootstrap"', '\n export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=');
|
|
203
|
+
|
|
204
|
+
assert.equal(helperBlock.includes('process.pid'), true);
|
|
205
|
+
assert.equal(helperBlock.includes('Math.random'), true);
|
|
206
|
+
assert.equal(bootstrapBlock.includes('process.pid'), true);
|
|
207
|
+
assert.equal(bootstrapBlock.includes('Math.random'), true);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test('replaceRequired enforces the expected replacement count', () => {
|
|
211
|
+
const { replaceRequired } = loadHelperApi();
|
|
212
|
+
|
|
213
|
+
assert.equal(replaceRequired('abc abc', /abc/g, 'x', 'abc', 2), 'x x');
|
|
214
|
+
assert.throws(
|
|
215
|
+
() => replaceRequired('abc abc', /abc/g, 'x', 'abc', 1),
|
|
216
|
+
/unexpected abc count 2/,
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test('stringWidth treats full-width and combining text as expected', () => {
|
|
221
|
+
const { stringWidth } = loadHelperApi();
|
|
222
|
+
|
|
223
|
+
assert.equal(stringWidth('abc'), 3);
|
|
224
|
+
assert.equal(stringWidth('あ'), 2);
|
|
225
|
+
assert.equal(stringWidth('a\u0301'), 1);
|
|
226
|
+
assert.equal(stringWidth('🙂'), 2);
|
|
227
|
+
assert.equal(stringWidth('🚀'), 2);
|
|
228
|
+
assert.equal(stringWidth('🚗'), 2);
|
|
229
|
+
assert.equal(stringWidth('❤️'), 2);
|
|
230
|
+
assert.equal(stringWidth('☕️'), 2);
|
|
231
|
+
assert.equal(stringWidth('🇯🇵'), 2);
|
|
232
|
+
assert.equal(stringWidth('#️⃣'), 2);
|
|
233
|
+
assert.equal(stringWidth('👨👩👧👦'), 2);
|
|
234
|
+
assert.equal(stringWidth('\u001b[31mあ\u001b[0m'), 2);
|
|
235
|
+
assert.equal(stringWidth('\u001b[36m🇯🇵\u001b[0m'), 2);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test('stableHash incorporates the optional seed', () => {
|
|
239
|
+
const { stableHash } = loadHelperApi();
|
|
240
|
+
|
|
241
|
+
assert.equal(stableHash('abc', 123), stableHash('abc', 123));
|
|
242
|
+
assert.notEqual(stableHash('abc', 123), stableHash('abc', 456));
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test('wrapAnsi respects soft wrap, trim, and no-wrap options', () => {
|
|
246
|
+
const { wrapAnsi } = loadHelperApi();
|
|
247
|
+
|
|
248
|
+
assert.equal(wrapAnsi('abc def', 4, { hard: false }), 'abc\ndef');
|
|
249
|
+
assert.equal(wrapAnsi(' abc', 10, { trim: true }), 'abc');
|
|
250
|
+
assert.equal(wrapAnsi('abc ', 10, { trim: true }), 'abc');
|
|
251
|
+
assert.equal(wrapAnsi('abc def', 3, { wordWrap: false }).includes('\n'), true);
|
|
252
|
+
assert.equal(wrapAnsi('ab\u001b[31mcd', 3).includes('\n'), true);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test('cleanupStaleEntryFiles removes only stale extracted files for the same offsets', () => {
|
|
256
|
+
const { cleanupStaleEntryFiles } = loadHelperApi();
|
|
257
|
+
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-termux-'));
|
|
258
|
+
const tmpdir = fs.mkdtempSync(path.join(tmpRoot, 'work-'));
|
|
259
|
+
const stale = path.join(tmpdir, 'cli.11.22.123.old.bare-path.js');
|
|
260
|
+
const fresh = path.join(tmpdir, 'cli.11.22.456.new.bare-path.js');
|
|
261
|
+
const otherOffsets = path.join(tmpdir, 'cli.33.44.999.old.bare-path.js');
|
|
262
|
+
try {
|
|
263
|
+
fs.writeFileSync(stale, 'stale');
|
|
264
|
+
fs.writeFileSync(fresh, 'fresh');
|
|
265
|
+
fs.writeFileSync(otherOffsets, 'other');
|
|
266
|
+
const oldTime = new Date(Date.now() - (2 * 24 * 60 * 60 * 1000));
|
|
267
|
+
fs.utimesSync(stale, oldTime, oldTime);
|
|
268
|
+
fs.utimesSync(otherOffsets, oldTime, oldTime);
|
|
269
|
+
|
|
270
|
+
cleanupStaleEntryFiles(tmpdir, 11, 22, Date.now());
|
|
271
|
+
|
|
272
|
+
assert.equal(fs.existsSync(stale), false);
|
|
273
|
+
assert.equal(fs.existsSync(fresh), true);
|
|
274
|
+
assert.equal(fs.existsSync(otherOffsets), true);
|
|
275
|
+
} finally {
|
|
276
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
function buildSyntheticBundleSource() {
|
|
281
|
+
const typeofBun = Array.from({ length: 3 }, () => 'typeof Bun').join('; ');
|
|
282
|
+
const bunProps = Array.from({ length: 33 }, (_, index) => `Bun.p${index}`).join('; ');
|
|
283
|
+
return `function(exports, require, module, __filename, __dirname) { ${typeofBun}; typeof globalThis.Bun; globalThis.Bun; ${bunProps}; npmInstallDeprecated:!0 }`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
test('rewriteNativeChunkSource rewrites the synthetic bundle slice', () => {
|
|
287
|
+
const { rewriteNativeChunkSource } = loadHelperApi();
|
|
288
|
+
const source = buildSyntheticBundleSource();
|
|
289
|
+
const patched = rewriteNativeChunkSource(source);
|
|
290
|
+
|
|
291
|
+
assert.match(patched, /var __claudeBun = globalThis\.__claudeBunShim;/);
|
|
292
|
+
assert.match(patched, /typeof __claudeBun/);
|
|
293
|
+
assert.match(patched, /typeof globalThis\.__claudeBun/);
|
|
294
|
+
assert.match(patched, /globalThis\.__claudeBun/);
|
|
295
|
+
assert.match(patched, /__claudeBun\./);
|
|
296
|
+
assert.match(patched, /npmInstallDeprecated:!1/);
|
|
297
|
+
assert.doesNotMatch(patched, /npmInstallDeprecated:!0/);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test('rewriteNativeChunkSource rejects unexpected replacement counts', () => {
|
|
301
|
+
const { rewriteNativeChunkSource } = loadHelperApi();
|
|
302
|
+
const source = buildSyntheticBundleSource().replace('Bun.p30;', '');
|
|
303
|
+
|
|
304
|
+
assert.throws(
|
|
305
|
+
() => rewriteNativeChunkSource(source),
|
|
306
|
+
/unexpected Bun property access count 32/,
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const tarballPath = path.join(__dirname, '..', 'bash0816-claude-code-2.1.177.tgz');
|
|
311
|
+
|
|
312
|
+
test('tarball contents match the workspace runner and test file', { skip: !fs.existsSync(tarballPath) }, () => {
|
|
313
|
+
const tarRunner = child_process.execFileSync('tar', ['-xOf', tarballPath, 'package/lib/termux-run-claude-native.sh']);
|
|
314
|
+
const tarTest = child_process.execFileSync('tar', ['-xOf', tarballPath, 'package/lib/termux-run-claude-native.test.js']);
|
|
315
|
+
|
|
316
|
+
const worktreeRunner = fs.readFileSync(path.join(__dirname, 'termux-run-claude-native.sh'));
|
|
317
|
+
const worktreeTest = fs.readFileSync(path.join(__dirname, 'termux-run-claude-native.test.js'));
|
|
318
|
+
|
|
319
|
+
assert.equal(crypto.createHash('sha256').update(tarRunner).digest('hex'), crypto.createHash('sha256').update(worktreeRunner).digest('hex'));
|
|
320
|
+
assert.equal(crypto.createHash('sha256').update(tarTest).digest('hex'), crypto.createHash('sha256').update(worktreeTest).digest('hex'));
|
|
321
|
+
assert.equal(tarRunner.length, worktreeRunner.length);
|
|
322
|
+
assert.equal(tarTest.length, worktreeTest.length);
|
|
323
|
+
});
|