@bash0816/claude-code 2.1.219 → 2.1.220-2
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
|
@@ -571,6 +571,33 @@
|
|
|
571
571
|
"entry_end_offset": 265451802,
|
|
572
572
|
"tarball_integrity": "sha512-G5yLidk2u6djL5PPuBYXVwVhTsZY5zLoZwh1Jnrd/IRyYb0P0x4gKTzVvGlsGepkBrOp0H2k/KuicyNPrXt57Q==",
|
|
573
573
|
"tarball_sha256": "cf4ae671f08b91ac75ea63c8497e7ff4e3bdbe522b0f83bff2de162d44e0eb15",
|
|
574
|
+
"status": "termux_verified"
|
|
575
|
+
},
|
|
576
|
+
"2.1.220": {
|
|
577
|
+
"wrapper_spec": "@anthropic-ai/claude-code@2.1.220",
|
|
578
|
+
"native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.220",
|
|
579
|
+
"entry_js_offset": 243831156,
|
|
580
|
+
"entry_end_offset": 265457701,
|
|
581
|
+
"tarball_integrity": "sha512-VHFI8mKruIntKn7eq81sbyS19/KWmQcmJQsS/C+j9M/E+w0s4UytgsL7DADPjBE/GByNiKoRtLYDMntCjRlOdA==",
|
|
582
|
+
"tarball_sha256": "e38454d73576a08a2e707f26539d73fc9ef33e890228ca5c58a2bbe810ac884d",
|
|
583
|
+
"status": "termux_verified"
|
|
584
|
+
},
|
|
585
|
+
"2.1.220-1": {
|
|
586
|
+
"wrapper_spec": "@anthropic-ai/claude-code@2.1.220",
|
|
587
|
+
"native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.220",
|
|
588
|
+
"entry_js_offset": 243831156,
|
|
589
|
+
"entry_end_offset": 265457701,
|
|
590
|
+
"tarball_integrity": "sha512-VHFI8mKruIntKn7eq81sbyS19/KWmQcmJQsS/C+j9M/E+w0s4UytgsL7DADPjBE/GByNiKoRtLYDMntCjRlOdA==",
|
|
591
|
+
"tarball_sha256": "e38454d73576a08a2e707f26539d73fc9ef33e890228ca5c58a2bbe810ac884d",
|
|
592
|
+
"status": "offset_discovered"
|
|
593
|
+
},
|
|
594
|
+
"2.1.220-2": {
|
|
595
|
+
"wrapper_spec": "@anthropic-ai/claude-code@2.1.220",
|
|
596
|
+
"native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.220",
|
|
597
|
+
"entry_js_offset": 243831156,
|
|
598
|
+
"entry_end_offset": 265457701,
|
|
599
|
+
"tarball_integrity": "sha512-VHFI8mKruIntKn7eq81sbyS19/KWmQcmJQsS/C+j9M/E+w0s4UytgsL7DADPjBE/GByNiKoRtLYDMntCjRlOdA==",
|
|
600
|
+
"tarball_sha256": "e38454d73576a08a2e707f26539d73fc9ef33e890228ca5c58a2bbe810ac884d",
|
|
574
601
|
"status": "offset_discovered"
|
|
575
602
|
}
|
|
576
603
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 1,
|
|
3
3
|
"package_name": "@bash0816/claude-code",
|
|
4
|
-
"latest_audited_version": "2.1.
|
|
5
|
-
"latest_candidate_version": "2.1.
|
|
6
|
-
"previous_stable_version": "2.1.
|
|
4
|
+
"latest_audited_version": "2.1.220",
|
|
5
|
+
"latest_candidate_version": "2.1.220-2",
|
|
6
|
+
"previous_stable_version": "2.1.219",
|
|
7
7
|
"stable_pinned_version": "2.1.193",
|
|
8
8
|
"manifest_url": "https://raw.githubusercontent.com/bash0816/ClaudeCode-Termux/main/config/claude-termux-release-manifest.json"
|
|
9
9
|
}
|
|
@@ -623,6 +623,7 @@ async function main() {
|
|
|
623
623
|
|
|
624
624
|
const originalArgv = process.argv.slice();
|
|
625
625
|
const originalExit = process.exit;
|
|
626
|
+
const originalKill = process.kill;
|
|
626
627
|
const originalBun = process.versions.bun;
|
|
627
628
|
const hadGlobalBun = Object.prototype.hasOwnProperty.call(globalThis, 'Bun');
|
|
628
629
|
const originalGlobalBun = globalThis.Bun;
|
|
@@ -636,6 +637,12 @@ async function main() {
|
|
|
636
637
|
function onAsyncError(error) {
|
|
637
638
|
asyncErrors.push(error);
|
|
638
639
|
}
|
|
640
|
+
const printWaitMs = Number(process.env.CLAUDE_TERMUX_PRINT_WAIT_MS || 5000);
|
|
641
|
+
async function waitForPrintFlush() {
|
|
642
|
+
if (Number.isFinite(printWaitMs) && printWaitMs > 0) {
|
|
643
|
+
await new Promise(resolve => setTimeout(resolve, printWaitMs));
|
|
644
|
+
}
|
|
645
|
+
}
|
|
639
646
|
|
|
640
647
|
try {
|
|
641
648
|
process.once('uncaughtException', onAsyncError);
|
|
@@ -694,21 +701,26 @@ async function main() {
|
|
|
694
701
|
globalThis.__claudeBun = globalThis.__claudeBunShim;
|
|
695
702
|
globalThis.Bun = globalThis.__claudeBunShim;
|
|
696
703
|
process.argv = ['node', extractedFile, ...argv];
|
|
704
|
+
let lastExitAttemptCode = 0;
|
|
697
705
|
process.exit = code => {
|
|
698
|
-
|
|
706
|
+
lastExitAttemptCode = code ?? 0;
|
|
707
|
+
throw new RequestedExit(lastExitAttemptCode);
|
|
708
|
+
};
|
|
709
|
+
process.kill = (pid, signal) => {
|
|
710
|
+
if (pid === process.pid && (signal === 'SIGKILL' || signal === 9)) {
|
|
711
|
+
throw new RequestedExit(lastExitAttemptCode);
|
|
712
|
+
}
|
|
713
|
+
return originalKill.call(process, pid, signal);
|
|
699
714
|
};
|
|
700
|
-
|
|
701
|
-
const printWaitMs = Number(process.env.CLAUDE_TERMUX_PRINT_WAIT_MS || 5000);
|
|
702
715
|
const moduleLike = { exports: {} };
|
|
703
716
|
const maybePromise = fn(moduleLike.exports, fakeRequire, moduleLike, extractedFile, workdir);
|
|
704
717
|
if (maybePromise && typeof maybePromise.then === 'function') await maybePromise;
|
|
705
|
-
|
|
706
|
-
await new Promise(resolve => setTimeout(resolve, printWaitMs));
|
|
707
|
-
}
|
|
718
|
+
await waitForPrintFlush();
|
|
708
719
|
if (asyncErrors.length > 0) throw asyncErrors[0];
|
|
709
720
|
} catch (error) {
|
|
710
721
|
if (error instanceof RequestedExit) {
|
|
711
722
|
process.exitCode = error.code;
|
|
723
|
+
await waitForPrintFlush();
|
|
712
724
|
return;
|
|
713
725
|
}
|
|
714
726
|
throw error;
|
|
@@ -722,6 +734,7 @@ async function main() {
|
|
|
722
734
|
}
|
|
723
735
|
process.argv = originalArgv;
|
|
724
736
|
process.exit = originalExit;
|
|
737
|
+
process.kill = originalKill;
|
|
725
738
|
try {
|
|
726
739
|
if (originalBun === undefined) {
|
|
727
740
|
delete process.versions.bun;
|
|
@@ -758,6 +771,7 @@ else
|
|
|
758
771
|
_bootstrap=$(mktemp "${TERMUX_TMPDIR}/claude-bootstrap.XXXXXX.js")
|
|
759
772
|
trap 'rm -f "$_bootstrap"' EXIT HUP INT TERM
|
|
760
773
|
export CLAUDE_TERMUX_TUI="${_tui}"
|
|
774
|
+
export CLAUDE_TERMUX_PRINT_MODE="${_pf}"
|
|
761
775
|
cat <<'NODE' > "$_bootstrap"
|
|
762
776
|
const fs = require('fs');
|
|
763
777
|
const path = require('path');
|
|
@@ -1316,6 +1330,7 @@ async function main() {
|
|
|
1316
1330
|
|
|
1317
1331
|
const originalArgv = process.argv.slice();
|
|
1318
1332
|
const originalExit = process.exit;
|
|
1333
|
+
const originalKill = process.kill;
|
|
1319
1334
|
const originalBun = process.versions.bun;
|
|
1320
1335
|
const hadGlobalBun = Object.prototype.hasOwnProperty.call(globalThis, 'Bun');
|
|
1321
1336
|
const originalGlobalBun = globalThis.Bun;
|
|
@@ -1329,6 +1344,13 @@ async function main() {
|
|
|
1329
1344
|
function onAsyncError(error) {
|
|
1330
1345
|
asyncErrors.push(error);
|
|
1331
1346
|
}
|
|
1347
|
+
async function waitForPrintFlushIfNeeded() {
|
|
1348
|
+
if (process.env.CLAUDE_TERMUX_PRINT_MODE !== '1') return;
|
|
1349
|
+
const printWaitMs = Number(process.env.CLAUDE_TERMUX_PRINT_WAIT_MS || 5000);
|
|
1350
|
+
if (Number.isFinite(printWaitMs) && printWaitMs > 0) {
|
|
1351
|
+
await new Promise(resolve => setTimeout(resolve, printWaitMs));
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1332
1354
|
|
|
1333
1355
|
try {
|
|
1334
1356
|
process.once('uncaughtException', onAsyncError);
|
|
@@ -1387,17 +1409,27 @@ async function main() {
|
|
|
1387
1409
|
globalThis.__claudeBun = globalThis.__claudeBunShim;
|
|
1388
1410
|
globalThis.Bun = globalThis.__claudeBunShim;
|
|
1389
1411
|
process.argv = ['node', extractedFile, ...argv];
|
|
1412
|
+
let lastExitAttemptCode = 0;
|
|
1390
1413
|
process.exit = code => {
|
|
1391
|
-
|
|
1414
|
+
lastExitAttemptCode = code ?? 0;
|
|
1415
|
+
throw new RequestedExit(lastExitAttemptCode);
|
|
1416
|
+
};
|
|
1417
|
+
process.kill = (pid, signal) => {
|
|
1418
|
+
if (pid === process.pid && (signal === 'SIGKILL' || signal === 9)) {
|
|
1419
|
+
throw new RequestedExit(lastExitAttemptCode);
|
|
1420
|
+
}
|
|
1421
|
+
return originalKill.call(process, pid, signal);
|
|
1392
1422
|
};
|
|
1393
1423
|
|
|
1394
1424
|
const moduleLike = { exports: {} };
|
|
1395
1425
|
const maybePromise = fn(moduleLike.exports, fakeRequire, moduleLike, extractedFile, workdir);
|
|
1396
1426
|
if (maybePromise && typeof maybePromise.then === 'function') await maybePromise;
|
|
1427
|
+
await waitForPrintFlushIfNeeded();
|
|
1397
1428
|
if (asyncErrors.length > 0) throw asyncErrors[0];
|
|
1398
1429
|
} catch (error) {
|
|
1399
1430
|
if (error instanceof RequestedExit) {
|
|
1400
1431
|
process.exitCode = error.code;
|
|
1432
|
+
await waitForPrintFlushIfNeeded();
|
|
1401
1433
|
return;
|
|
1402
1434
|
}
|
|
1403
1435
|
throw error;
|
|
@@ -1406,6 +1438,7 @@ async function main() {
|
|
|
1406
1438
|
process.removeListener('unhandledRejection', onAsyncError);
|
|
1407
1439
|
process.argv = originalArgv;
|
|
1408
1440
|
process.exit = originalExit;
|
|
1441
|
+
process.kill = originalKill;
|
|
1409
1442
|
process.once('exit', () => {
|
|
1410
1443
|
if (extractedFile) {
|
|
1411
1444
|
try {
|
|
@@ -191,12 +191,32 @@ test('print path does not defer cleanup to exit', () => {
|
|
|
191
191
|
test('bootstrap path defers cleanup to exit', () => {
|
|
192
192
|
const bootstrapBlock = extractBlock('cat <<\'NODE\' > "$_bootstrap"', '\n export ENABLE_CLAUDEAI_MCP_SERVERS=');
|
|
193
193
|
|
|
194
|
-
assert.equal(bootstrapBlock.includes('CLAUDE_TERMUX_PRINT_WAIT_MS'), false);
|
|
195
|
-
assert.equal(bootstrapBlock.includes('setTimeout(resolve, printWaitMs)'), false);
|
|
196
194
|
assert.equal(bootstrapBlock.includes('process.once(\'exit\''), true);
|
|
197
195
|
assert.equal(bootstrapBlock.includes('process.removeListener(\'uncaughtException\''), true);
|
|
198
196
|
});
|
|
199
197
|
|
|
198
|
+
test('bootstrap branch exports CLAUDE_TERMUX_PRINT_MODE before invoking node', () => {
|
|
199
|
+
const bootstrapShellRegion = extractFunction(
|
|
200
|
+
script,
|
|
201
|
+
'export CLAUDE_TERMUX_TUI="${_tui}"',
|
|
202
|
+
'cat <<\'NODE\' > "$_bootstrap"',
|
|
203
|
+
);
|
|
204
|
+
assert.equal(bootstrapShellRegion.includes('export CLAUDE_TERMUX_PRINT_MODE="${_pf}"'), true);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('helper and bootstrap wait for print flush, including the RequestedExit path', () => {
|
|
208
|
+
const helperBlock = extractBlock('cat <<\'NODE\' > "$_helper"', '\n export ENABLE_CLAUDEAI_MCP_SERVERS=');
|
|
209
|
+
const bootstrapBlock = extractBlock('cat <<\'NODE\' > "$_bootstrap"', '\n export ENABLE_CLAUDEAI_MCP_SERVERS=');
|
|
210
|
+
|
|
211
|
+
assert.equal(helperBlock.includes('async function waitForPrintFlush()'), true);
|
|
212
|
+
assert.equal(helperBlock.includes('await waitForPrintFlush();\n if (asyncErrors.length > 0) throw asyncErrors[0];'), true);
|
|
213
|
+
assert.equal(helperBlock.includes('process.exitCode = error.code;\n await waitForPrintFlush();\n return;'), true);
|
|
214
|
+
|
|
215
|
+
assert.equal(bootstrapBlock.includes('async function waitForPrintFlushIfNeeded()'), true);
|
|
216
|
+
assert.equal(bootstrapBlock.includes('await waitForPrintFlushIfNeeded();\n if (asyncErrors.length > 0) throw asyncErrors[0];'), true);
|
|
217
|
+
assert.equal(bootstrapBlock.includes('process.exitCode = error.code;\n await waitForPrintFlushIfNeeded();\n return;'), true);
|
|
218
|
+
});
|
|
219
|
+
|
|
200
220
|
test('entry extraction uses a process-unique filename', () => {
|
|
201
221
|
const helperBlock = extractBlock('cat <<\'NODE\' > "$_helper"', '\n export ENABLE_CLAUDEAI_MCP_SERVERS=');
|
|
202
222
|
const bootstrapBlock = extractBlock('cat <<\'NODE\' > "$_bootstrap"', '\n export ENABLE_CLAUDEAI_MCP_SERVERS=');
|
|
@@ -544,3 +564,173 @@ test('tarball contents match the workspace runner and test file', { skip: !fs.ex
|
|
|
544
564
|
test('CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC is never forced (regression guard)', () => {
|
|
545
565
|
assert.equal(script.includes('CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC'), false);
|
|
546
566
|
});
|
|
567
|
+
|
|
568
|
+
function buildScenarioFixtureSource() {
|
|
569
|
+
const typeofBun = Array.from({ length: 7 }, () => 'typeof Bun').join('; ');
|
|
570
|
+
const bunProps = Array.from({ length: 42 }, (_, i) => `Bun.p${i}`).join('; ');
|
|
571
|
+
return `function(exports, require, module, __filename, __dirname) {
|
|
572
|
+
${typeofBun}; typeof globalThis.Bun; globalThis.Bun; ${bunProps};
|
|
573
|
+
npmInstallDeprecated:!0; npmInstallDeprecated:!0;
|
|
574
|
+
const scenario = process.env.TEST_SCENARIO;
|
|
575
|
+
if (scenario === 'sync-exit') { process.stdout.write('ok'); process.exit(0); return; }
|
|
576
|
+
if (scenario === 'async-exit') {
|
|
577
|
+
setTimeout(() => { process.stdout.write('ok'); }, 100).unref();
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
if (scenario === 'self-sigkill-fallback-string') {
|
|
581
|
+
try {
|
|
582
|
+
process.exit(17);
|
|
583
|
+
} catch (e) {
|
|
584
|
+
process.kill(process.pid, 'SIGKILL');
|
|
585
|
+
}
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (scenario === 'self-sigkill-fallback-numeric') {
|
|
589
|
+
try {
|
|
590
|
+
process.exit(17);
|
|
591
|
+
} catch (e) {
|
|
592
|
+
process.kill(process.pid, 9);
|
|
593
|
+
}
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
if (scenario === 'self-sigkill-fallback-no-code') {
|
|
597
|
+
try {
|
|
598
|
+
process.exit();
|
|
599
|
+
} catch (e) {
|
|
600
|
+
process.kill(process.pid, 'SIGKILL');
|
|
601
|
+
}
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (scenario === 'other-process-kill') {
|
|
605
|
+
const cp = require('child_process');
|
|
606
|
+
const child = cp.spawn('node', ['-e', 'setTimeout(() => {}, 5000)']);
|
|
607
|
+
const childPid = child.pid;
|
|
608
|
+
process.kill(childPid, 0);
|
|
609
|
+
child.kill();
|
|
610
|
+
process.stdout.write('ok');
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
process.stdout.write('ok');
|
|
614
|
+
}`;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function runScenario({ printMode, stdinInherit, scenario }) {
|
|
618
|
+
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-stdin-test-'));
|
|
619
|
+
const sourceBin = path.join(tmpBase, 'fake-source.js');
|
|
620
|
+
const fixtureSource = buildScenarioFixtureSource();
|
|
621
|
+
fs.writeFileSync(sourceBin, fixtureSource, 'utf8');
|
|
622
|
+
const entryJsOffset = 0;
|
|
623
|
+
const entryEndOffset = Buffer.byteLength(fixtureSource, 'utf8');
|
|
624
|
+
const workdir = path.join(tmpBase, 'workdir');
|
|
625
|
+
fs.mkdirSync(workdir, { recursive: true });
|
|
626
|
+
|
|
627
|
+
const env = {
|
|
628
|
+
...process.env,
|
|
629
|
+
SOURCE_BIN: sourceBin,
|
|
630
|
+
WORKDIR: workdir,
|
|
631
|
+
ENTRY_JS_OFFSET: String(entryJsOffset),
|
|
632
|
+
ENTRY_END_OFFSET: String(entryEndOffset),
|
|
633
|
+
CURRENT_CLAUDE_VERSION: '2.1.220',
|
|
634
|
+
CLAUDE_TERMUX_PACKAGE_DIR: path.join(__dirname, '..'),
|
|
635
|
+
MAGI_ENV: '1',
|
|
636
|
+
CLAUDE_TERMUX_PRINT_WAIT_MS: '300',
|
|
637
|
+
TMPDIR: tmpBase,
|
|
638
|
+
TEST_SCENARIO: scenario,
|
|
639
|
+
};
|
|
640
|
+
if (stdinInherit) env.CLAUDE_TERMUX_STDIN = 'inherit';
|
|
641
|
+
else delete env.CLAUDE_TERMUX_STDIN;
|
|
642
|
+
|
|
643
|
+
const args = printMode ? ['-p', 'x'] : [];
|
|
644
|
+
const start = Date.now();
|
|
645
|
+
const result = child_process.spawnSync('sh', [scriptPath, ...args], {
|
|
646
|
+
env,
|
|
647
|
+
input: 'test input\n',
|
|
648
|
+
encoding: 'utf8',
|
|
649
|
+
timeout: 10000,
|
|
650
|
+
});
|
|
651
|
+
const elapsedMs = Date.now() - start;
|
|
652
|
+
fs.rmSync(tmpBase, { recursive: true, force: true });
|
|
653
|
+
return { ...result, elapsedMs };
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
test('helper and bootstrap intercept process.kill(self, SIGKILL) statically', () => {
|
|
657
|
+
const helperBlock = extractBlock('cat <<\'NODE\' > "$_helper"', '\n export ENABLE_CLAUDEAI_MCP_SERVERS=');
|
|
658
|
+
const bootstrapBlock = extractBlock('cat <<\'NODE\' > "$_bootstrap"', '\n export ENABLE_CLAUDEAI_MCP_SERVERS=');
|
|
659
|
+
|
|
660
|
+
// Check helper branch
|
|
661
|
+
const helperOriginalKillIdx = helperBlock.indexOf('const originalKill = process.kill;');
|
|
662
|
+
assert.ok(helperOriginalKillIdx > -1, 'helper: missing originalKill declaration');
|
|
663
|
+
|
|
664
|
+
const helperProcessKillIdx = helperBlock.indexOf('process.kill = (pid, signal) => {', helperOriginalKillIdx);
|
|
665
|
+
assert.ok(helperProcessKillIdx > helperOriginalKillIdx, 'helper: process.kill override must come after originalKill declaration');
|
|
666
|
+
|
|
667
|
+
const helperRestoreIdx = helperBlock.indexOf('process.kill = originalKill;', helperProcessKillIdx);
|
|
668
|
+
assert.ok(helperRestoreIdx > helperProcessKillIdx, 'helper: process.kill restoration must come after override');
|
|
669
|
+
|
|
670
|
+
// Verify SIGKILL check within the override
|
|
671
|
+
const helperKillOverrideEnd = helperBlock.indexOf('};', helperProcessKillIdx);
|
|
672
|
+
const helperKillOverride = helperBlock.slice(helperProcessKillIdx, helperKillOverrideEnd);
|
|
673
|
+
assert.ok(helperKillOverride.includes('signal === \'SIGKILL\''), 'helper: process.kill must check for SIGKILL string');
|
|
674
|
+
assert.ok(helperKillOverride.includes('signal === 9'), 'helper: process.kill must check for SIGKILL numeric value');
|
|
675
|
+
assert.ok(helperKillOverride.includes('throw new RequestedExit'), 'helper: process.kill must throw RequestedExit for self SIGKILL');
|
|
676
|
+
|
|
677
|
+
// Check bootstrap branch
|
|
678
|
+
const bootstrapOriginalKillIdx = bootstrapBlock.indexOf('const originalKill = process.kill;');
|
|
679
|
+
assert.ok(bootstrapOriginalKillIdx > -1, 'bootstrap: missing originalKill declaration');
|
|
680
|
+
|
|
681
|
+
const bootstrapProcessKillIdx = bootstrapBlock.indexOf('process.kill = (pid, signal) => {', bootstrapOriginalKillIdx);
|
|
682
|
+
assert.ok(bootstrapProcessKillIdx > bootstrapOriginalKillIdx, 'bootstrap: process.kill override must come after originalKill declaration');
|
|
683
|
+
|
|
684
|
+
const bootstrapRestoreIdx = bootstrapBlock.indexOf('process.kill = originalKill;', bootstrapProcessKillIdx);
|
|
685
|
+
assert.ok(bootstrapRestoreIdx > bootstrapProcessKillIdx, 'bootstrap: process.kill restoration must come after override');
|
|
686
|
+
|
|
687
|
+
// Verify SIGKILL check within the override
|
|
688
|
+
const bootstrapKillOverrideEnd = bootstrapBlock.indexOf('};', bootstrapProcessKillIdx);
|
|
689
|
+
const bootstrapKillOverride = bootstrapBlock.slice(bootstrapProcessKillIdx, bootstrapKillOverrideEnd);
|
|
690
|
+
assert.ok(bootstrapKillOverride.includes('signal === \'SIGKILL\''), 'bootstrap: process.kill must check for SIGKILL string');
|
|
691
|
+
assert.ok(bootstrapKillOverride.includes('signal === 9'), 'bootstrap: process.kill must check for SIGKILL numeric value');
|
|
692
|
+
assert.ok(bootstrapKillOverride.includes('throw new RequestedExit'), 'bootstrap: process.kill must throw RequestedExit for self SIGKILL');
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
test('helper branch (CLI -p, no stdin inherit): normal/sync-exit/async-exit all produce output', () => {
|
|
696
|
+
for (const scenario of ['normal', 'sync-exit', 'async-exit']) {
|
|
697
|
+
const r = runScenario({ printMode: true, stdinInherit: false, scenario });
|
|
698
|
+
assert.ok((r.stdout || '').includes('ok'), `scenario=${scenario} stdout=${r.stdout} stderr=${r.stderr}`);
|
|
699
|
+
assert.equal(r.status, 0, `scenario=${scenario} status=${r.status} stderr=${r.stderr}`);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
test('bootstrap branch (-p + CLAUDE_TERMUX_STDIN=inherit): normal/sync-exit/async-exit all produce output', () => {
|
|
704
|
+
for (const scenario of ['normal', 'sync-exit', 'async-exit']) {
|
|
705
|
+
const r = runScenario({ printMode: true, stdinInherit: true, scenario });
|
|
706
|
+
assert.ok((r.stdout || '').includes('ok'), `scenario=${scenario} stdout=${r.stdout} stderr=${r.stderr}`);
|
|
707
|
+
assert.equal(r.status, 0, `scenario=${scenario} status=${r.status} stderr=${r.stderr}`);
|
|
708
|
+
if (scenario === 'async-exit') {
|
|
709
|
+
assert.ok(r.elapsedMs >= 250, `expected wait >= 250ms, got ${r.elapsedMs}ms`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
test('helper branch intercepts self-directed SIGKILL (string signal) and exits with proper code', () => {
|
|
715
|
+
const r = runScenario({ printMode: true, stdinInherit: false, scenario: 'self-sigkill-fallback-string' });
|
|
716
|
+
assert.equal(r.status, 17, `expected status 17, got ${r.status}; stderr=${r.stderr}`);
|
|
717
|
+
assert.ok(!r.signal, `expected clean exit (no signal), but got signal: ${r.signal}`);
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
test('helper branch intercepts self-directed SIGKILL (numeric signal 9) and exits with proper code', () => {
|
|
721
|
+
const r = runScenario({ printMode: true, stdinInherit: false, scenario: 'self-sigkill-fallback-numeric' });
|
|
722
|
+
assert.equal(r.status, 17, `expected status 17, got ${r.status}; stderr=${r.stderr}`);
|
|
723
|
+
assert.ok(!r.signal, `expected clean exit (no signal), but got signal: ${r.signal}`);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
test('helper branch intercepts self-directed SIGKILL with process.exit() (no code) and defaults to 0', () => {
|
|
727
|
+
const r = runScenario({ printMode: true, stdinInherit: false, scenario: 'self-sigkill-fallback-no-code' });
|
|
728
|
+
assert.equal(r.status, 0, `expected status 0, got ${r.status}; stderr=${r.stderr}`);
|
|
729
|
+
assert.ok(!r.signal, `expected clean exit (no signal), but got signal: ${r.signal}`);
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
test('helper branch allows process.kill to other processes (signal 0, passthrough)', () => {
|
|
733
|
+
const r = runScenario({ printMode: true, stdinInherit: false, scenario: 'other-process-kill' });
|
|
734
|
+
assert.ok((r.stdout || '').includes('ok'), `expected ok output, got stdout=${r.stdout} stderr=${r.stderr}`);
|
|
735
|
+
assert.equal(r.status, 0, `expected status 0, got ${r.status}; stderr=${r.stderr}`);
|
|
736
|
+
});
|