Skip to content

Commit 50fed3c

Browse files
committed
fix: port to current Perry toolchain (macOS)
The source targeted an older custom Perry fork; upstream @perryts/perry (0.5.112x+) removed its bespoke helpers and tightened FFI/runtime semantics, so the macOS build crashed on launch. Port off the removed/changed surfaces (rebased onto the Windows-port work on main): - fs.isDirectory removed -> src/fs-compat.ts (statSync().isDirectory()) - child_process.spawnBackground removed -> src/process-compat.ts; spawn with { detached:true }, platform-neutral (caller supplies the shell; no re-wrap), logFile redirected via fd, /dev/null|NUL discards - execSync/spawnSync now return Buffers -> execText/spawnText wrappers in process-compat force { encoding:'utf8' }; all call sites renamed - cross-device sync crypto intrinsics removed -> stubbed inline in sync-host/guest (sync disabled, deltas pass through) - LSP FFI manifest: string params i64 -> "string" - render.ts: define dropped t3/t5 timing vars; import find-bar match getters (unbound identifier reads now throw ReferenceError) IDE launches: workbench, explorer, editor, tree-sitter highlighting. Build requires PERRY_ALLOW_PERRY_FEATURES=1. See PERRY-MIGRATION-NOTES.md.
1 parent b921bde commit 50fed3c

24 files changed

Lines changed: 297 additions & 166 deletions

PERRY-MIGRATION-NOTES.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Perry toolchain migration (macOS)
2+
3+
Hone was originally built against a custom Perry fork. Upstream `@perryts/perry`
4+
(0.5.112x+) removed that fork's bespoke helpers and tightened FFI/runtime
5+
semantics, so the source needed porting. Summary of what changed and why, for
6+
anyone touching these areas (incl. the Windows port).
7+
8+
## Compat shims (new files)
9+
- `src/fs-compat.ts``isDirectory()` (fork-only `fs` export, removed) reimplemented
10+
via `statSync().isDirectory()`.
11+
- `src/process-compat.ts` — three shims:
12+
- `execText` / `spawnText``execSync`/`spawnSync` now return a **Buffer** unless
13+
`{ encoding: 'utf8' }` is passed (Node semantics). The codebase treats results as
14+
strings everywhere, so all call sites route through these wrappers.
15+
- `spawnBackground` — fork-only `child_process` helper, removed. Reimplemented over
16+
`spawn` with `{ detached: true }`. **Platform-neutral:** the caller supplies the
17+
shell (`shellBin`/`shellArg`), so the shim does NOT re-wrap; a real `logFile` is
18+
redirected via an opened fd, `/dev/null`/`NUL`/empty discards.
19+
20+
All `execSync(`/`spawnSync(` call sites were renamed to `execText(`/`spawnText(`;
21+
`isDirectory`/`spawnBackground` imports repointed to the shims. Native
22+
`execSync`/`spawnSync` are imported only inside `process-compat.ts`.
23+
24+
## FFI manifest types (native libs)
25+
Upstream Perry strictly checks `i64` params and rejects NaN-boxed strings (the old
26+
fork extracted the pointer leniently). String params must be typed `"string"`, and
27+
string-pointer returns (`Rust -> i64`) typed `"i64_str"`. Fixed in
28+
`native/lsp/package.json` here, and in the `hone-editor` / `hone-terminal` repos.
29+
Find them by scanning Rust for `*const u8` params (incl. `pub unsafe extern`).
30+
31+
## Other
32+
- Cross-device sync used custom crypto intrinsics (x25519/aes-gcm/hkdf), now gone —
33+
**stubbed inline** in `sync-host.ts`/`sync-guest.ts`; sync is disabled (deltas pass
34+
through as plaintext). Restore by reimplementing over Node `crypto`.
35+
- Unbound identifier reads now throw `ReferenceError` (old Perry read them as
36+
`undefined`). Run `npx tsc --noEmit | grep 'TS2304\|TS2552'` after any port to catch
37+
the whole class. (Fixed: dropped `t3`/`t5` timing vars + a missing find-bar import
38+
in render.ts.)
39+
40+
## Building
41+
Requires `PERRY_ALLOW_PERRY_FEATURES=1` (native-library allowlist gate), or add
42+
`"perry": { "allow": { "nativeLibrary": ["*"] } }` to package.json. Debug a runtime
43+
crash with `--debug-symbols` + `lldb -o "break set -n exit" -o run -o bt` — Perry's
44+
throw handler prints then exits without unwinding, so the crashing stack is intact.

native/lsp/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
"abiVersion": "0.5",
1414
"module": "@honeide/lsp-bridge",
1515
"functions": [
16-
{ "name": "hone_lsp_start", "params": ["i64", "i64", "i64"], "returns": "f64" },
17-
{ "name": "hone_lsp_send", "params": ["f64", "i64"], "returns": "f64" },
16+
{ "name": "hone_lsp_start", "params": ["string", "string", "string"], "returns": "f64" },
17+
{ "name": "hone_lsp_send", "params": ["f64", "string"], "returns": "f64" },
1818
{ "name": "hone_lsp_poll", "params": ["f64"], "returns": "i64" },
1919
{ "name": "hone_lsp_is_alive", "params": ["f64"], "returns": "f64" },
2020
{ "name": "hone_lsp_stop", "params": ["f64"], "returns": "f64" }

src/fs-compat.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { statSync } from 'fs';
2+
3+
// Perry's `fs` module dropped its top-level `isDirectory(path)` helper
4+
// (upstream @perryts/perry). Shim it via statSync so existing call sites
5+
// keep their "false for missing/non-dir" semantics.
6+
export function isDirectory(path: string): boolean {
7+
try {
8+
const st = statSync(path);
9+
return st.isDirectory();
10+
} catch (e: any) {
11+
return false;
12+
}
13+
}

src/process-compat.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { spawn, execSync, spawnSync } from 'child_process';
2+
import { openSync } from 'fs';
3+
4+
// Compatibility shims for upstream @perryts/perry, which diverged from the
5+
// custom fork hone was originally built against:
6+
//
7+
// - execSync/spawnSync now follow Node semantics and return a Buffer unless
8+
// `{ encoding: 'utf8' }` is passed. The codebase treats the result as a
9+
// string everywhere, so route every call through these wrappers.
10+
// - child_process.spawnBackground (a fork-only helper) was removed; reimplement
11+
// it over the standard `spawn` with { detached: true }.
12+
13+
export function execText(cmd: string): string {
14+
return execSync(cmd, { encoding: 'utf8' }) as unknown as string;
15+
}
16+
17+
export function spawnText(cmd: string, args: string[]): { stdout: string; stderr: string; status: number } {
18+
return spawnSync(cmd, args, { encoding: 'utf8' }) as unknown as { stdout: string; stderr: string; status: number };
19+
}
20+
21+
// Fire-and-forget background spawn. The CALLER supplies the platform shell
22+
// (e.g. cmd.exe /c vs /bin/sh -c), so we do NOT re-wrap in a shell here —
23+
// `command`/`args` are spawned directly, which keeps this cross-platform.
24+
//
25+
// The third argument matches the old signature: a string (output-redirect
26+
// target — '/dev/null' or 'NUL' mean discard) or an options object
27+
// { cwd?, logFile? }. When a real logFile is given, output is redirected to it
28+
// via an opened fd (no shell needed); otherwise output is discarded.
29+
export function spawnBackground(command: string, args: string[], options?: string | object): { pid: number; handleId: number } {
30+
let logFile = '';
31+
let cwd = '';
32+
if (typeof options === 'string') {
33+
const s = options as string;
34+
if (s.length > 0 && s !== '/dev/null' && s !== 'NUL') logFile = s;
35+
} else if (options !== null && options !== undefined && typeof options === 'object') {
36+
const o: any = options;
37+
if (o.logFile !== undefined && o.logFile !== null) logFile = o.logFile;
38+
if (o.cwd !== undefined && o.cwd !== null) cwd = o.cwd;
39+
}
40+
41+
let stdio: any = 'ignore';
42+
if (logFile.length > 0) {
43+
const fd: any = openSync(logFile, 'a');
44+
stdio = ['ignore', fd, fd];
45+
}
46+
47+
const spawnOpts: any = { detached: true, stdio: stdio };
48+
if (cwd.length > 0) spawnOpts.cwd = cwd;
49+
50+
const child: any = spawn(command, args, spawnOpts);
51+
let pid = 0;
52+
if (child !== null && child !== undefined && child.pid !== undefined && child.pid !== null) {
53+
pid = child.pid;
54+
}
55+
try { child.unref(); } catch (e: any) {}
56+
57+
return { pid: pid, handleId: 0 };
58+
}

src/workbench/paths.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*/
1010

1111
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
12-
import { execSync } from 'child_process';
12+
import { execText } from '../process-compat';
1313

1414
declare const __platform__: number;
1515

@@ -91,7 +91,7 @@ export function getHomeDir(): string {
9191
if (envHome && envHome.length > 0) { _homeDir = envHome; return _homeDir; }
9292
} catch (_e: any) {}
9393
try {
94-
const result = execSync('/bin/echo $HOME') as unknown as string;
94+
const result = execText('/bin/echo $HOME') as unknown as string;
9595
const dir = trimNewline(result);
9696
if (dir.length > 0) { _homeDir = dir; return _homeDir; }
9797
} catch (e: any) {}
@@ -105,7 +105,7 @@ export function getHomeDir(): string {
105105
if (envUP && envUP.length > 0) { _homeDir = envUP; return _homeDir; }
106106
} catch (_e: any) {}
107107
try {
108-
const result = execSync('echo %USERPROFILE%') as unknown as string;
108+
const result = execText('echo %USERPROFILE%') as unknown as string;
109109
const dir = trimNewline(result);
110110
if (dir.length > 0) { _homeDir = dir; return _homeDir; }
111111
} catch (e: any) {}
@@ -267,13 +267,13 @@ export function getCwd(): string {
267267
// that prints the working directory when called with no args).
268268
if (__platform__ === 3) {
269269
try {
270-
const result = execSync('cd') as unknown as string;
270+
const result = execText('cd') as unknown as string;
271271
const dir = trimNewline(result);
272272
if (dir.length > 0) return dir;
273273
} catch (e: any) {}
274274
} else {
275275
try {
276-
const result = execSync('pwd') as unknown as string;
276+
const result = execText('pwd') as unknown as string;
277277
const dir = trimNewline(result);
278278
if (dir.length > 0) return dir;
279279
} catch (e: any) {}

src/workbench/render.ts

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ import {
5353
} from './theme/theme-colors';
5454
import type { LayoutMode } from '../platform';
5555
import { getWorkbenchSettings, updateSettings, onSettingsChange, getSettingsVersion, getLastOpenTabs, getLastActiveTab, getLastPinnedTabs, applyWorkspaceOverlay, setNumberSetting } from './settings';
56-
import { readFileSync, writeFileSync, readdirSync, isDirectory, existsSync, unlinkSync } from 'fs';
56+
import { readFileSync, writeFileSync, readdirSync, existsSync, unlinkSync } from 'fs';
5757
import { join } from 'path';
58-
import { spawnBackground } from 'child_process';
59-
import { execSync, spawnSync } from 'child_process';
58+
import { execText, spawnText, spawnBackground } from '../process-compat';
59+
import { isDirectory } from '../fs-compat';
6060
import { spawn } from 'perry/thread';
6161
import { getTempDir, getCwd, getHomeDir, getAppDataDir } from './paths';
6262
import { getPlatformContext, isWebPlatform } from '../platform';
@@ -151,7 +151,7 @@ import { initWorkspaceTrust, isWorkspaceTrusted, trustWorkspace, revokeWorkspace
151151
import { renderReferencesPeek, showReferencesFromJson, setReferencesJumpHandler } from './views/references-peek/references-peek';
152152
import { renderTasksPanel, runDefaultBuildTask, runTaskByLabel, setTasksWorkspaceRoot, setTasksAppDataDir, setOnTaskRunStart, setOnTaskRunDone } from './views/tasks/tasks-panel';
153153
import { initRecentItems, addRecentFile, addRecentFolder, getRecentPath, getRecentType } from './views/recent/recent-store';
154-
import { createFindBar, setFindEditorCallbacks, showFindBar, showFindBarWithReplace, hideFindBar, isFindBarVisible } from './views/find/find-bar';
154+
import { createFindBar, setFindEditorCallbacks, showFindBar, showFindBarWithReplace, hideFindBar, isFindBarVisible, getFindMatchCount, getFindCurrentMatch, getFindMatchLine, getFindMatchCol, getFindMatchLen } from './views/find/find-bar';
155155
import { setLspWorkspaceRoot, initLspBridge, triggerDiagnostics, getCompletions, setDiagnosticsStatusUpdater } from './views/lsp/lsp-bridge';
156156
import { setDiagnosticsFileOpener } from './views/lsp/diagnostics-panel';
157157
import { createAutocompletePopup, setAutocompleteAcceptHandler } from './views/lsp/autocomplete-popup';
@@ -489,7 +489,7 @@ function toggleSidebarLocation(): void {
489489
function copyBranchNameToClipboard(): void {
490490
if (workspaceRoot.length === 0) return;
491491
try {
492-
const r = spawnSync('git', ['-C', workspaceRoot, 'rev-parse', '--abbrev-ref', 'HEAD']);
492+
const r = spawnText('git', ['-C', workspaceRoot, 'rev-parse', '--abbrev-ref', 'HEAD']);
493493
if (r.status !== 0) return;
494494
let s = r.stdout;
495495
let end = s.length;
@@ -1839,7 +1839,7 @@ function promptCloseDirtyTabMacos(filePath: string): number {
18391839
script += ' return "2"\n';
18401840
script += 'end try\n';
18411841
try {
1842-
const r = spawnSync('osascript', ['-e', script]);
1842+
const r = spawnText('osascript', ['-e', script]);
18431843
if (r.status !== 0) return 2;
18441844
const out = r.stdout.length > 0 ? r.stdout.charAt(0) : '';
18451845
if (out === '0') return 0;
@@ -1859,7 +1859,7 @@ function promptCloseDirtyTabWindows(filePath: string): number {
18591859
ps += '\'Save changes to ' + name + '?\',\'Hone\',\'YesNoCancel\',\'Question\');';
18601860
ps += 'if ($r -eq \'Yes\') { [Console]::Out.Write(\'0\') } elseif ($r -eq \'No\') { [Console]::Out.Write(\'1\') } else { [Console]::Out.Write(\'2\') }';
18611861
try {
1862-
const r = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', ps]);
1862+
const r = spawnText('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', ps]);
18631863
if (r.status !== 0) return 2;
18641864
const out = r.stdout.length > 0 ? r.stdout.charAt(0) : '';
18651865
if (out === '0') return 0;
@@ -2055,7 +2055,7 @@ function promptForRenameMacos(): string {
20552055
script += ' return ""\n';
20562056
script += 'end try\n';
20572057
try {
2058-
const r = spawnSync('osascript', ['-e', script]);
2058+
const r = spawnText('osascript', ['-e', script]);
20592059
if (r.status !== 0) return '';
20602060
let out = r.stdout;
20612061
let end = out.length;
@@ -2074,7 +2074,7 @@ function promptForRenameWindows(): string {
20742074
ps = '[void][System.Reflection.Assembly]::LoadWithPartialName(\'Microsoft.VisualBasic\');' + ps;
20752075
ps += '[Console]::Out.Write($r)';
20762076
try {
2077-
const r = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', ps]);
2077+
const r = spawnText('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', ps]);
20782078
if (r.status !== 0) return '';
20792079
let out = r.stdout;
20802080
let end = out.length;
@@ -2421,12 +2421,12 @@ function onGenerateCommitMessageImpl(): void {
24212421
// Prefer staged diff; fall back to working tree if nothing is staged.
24222422
let diff = '';
24232423
try {
2424-
const r = spawnSync('git', ['-C', workspaceRoot, 'diff', '--cached', '--no-color', '--stat=80,80']);
2424+
const r = spawnText('git', ['-C', workspaceRoot, 'diff', '--cached', '--no-color', '--stat=80,80']);
24252425
if (r.status === 0) diff = r.stdout;
24262426
} catch (_e: any) {}
24272427
if (diff.length < 5) {
24282428
try {
2429-
const r = spawnSync('git', ['-C', workspaceRoot, 'diff', '--no-color', '--stat=80,80']);
2429+
const r = spawnText('git', ['-C', workspaceRoot, 'diff', '--no-color', '--stat=80,80']);
24302430
if (r.status === 0) diff = r.stdout;
24312431
} catch (_e: any) {}
24322432
}
@@ -2437,12 +2437,12 @@ function onGenerateCommitMessageImpl(): void {
24372437
// Include the patch body too, capped at ~6KB so we don't blow the context.
24382438
let body = '';
24392439
try {
2440-
const r = spawnSync('git', ['-C', workspaceRoot, 'diff', '--cached', '--no-color', '-U2']);
2440+
const r = spawnText('git', ['-C', workspaceRoot, 'diff', '--cached', '--no-color', '-U2']);
24412441
if (r.status === 0) body = r.stdout;
24422442
} catch (_e: any) {}
24432443
if (body.length < 5) {
24442444
try {
2445-
const r = spawnSync('git', ['-C', workspaceRoot, 'diff', '--no-color', '-U2']);
2445+
const r = spawnText('git', ['-C', workspaceRoot, 'diff', '--no-color', '-U2']);
24462446
if (r.status === 0) body = r.stdout;
24472447
} catch (_e: any) {}
24482448
}
@@ -2478,18 +2478,18 @@ function onGeneratePRDescriptionImpl(): void {
24782478
// Resolve the base ref.
24792479
let baseRef = '';
24802480
try {
2481-
const r = spawnSync('git', ['-C', workspaceRoot, 'rev-parse', '--verify', 'main']);
2481+
const r = spawnText('git', ['-C', workspaceRoot, 'rev-parse', '--verify', 'main']);
24822482
if (r.status === 0) baseRef = 'main';
24832483
} catch (_e: any) {}
24842484
if (baseRef.length === 0) {
24852485
try {
2486-
const r = spawnSync('git', ['-C', workspaceRoot, 'rev-parse', '--verify', 'master']);
2486+
const r = spawnText('git', ['-C', workspaceRoot, 'rev-parse', '--verify', 'master']);
24872487
if (r.status === 0) baseRef = 'master';
24882488
} catch (_e: any) {}
24892489
}
24902490
if (baseRef.length === 0) {
24912491
try {
2492-
const r = spawnSync('git', ['-C', workspaceRoot, 'rev-parse', '--abbrev-ref', 'origin/HEAD']);
2492+
const r = spawnText('git', ['-C', workspaceRoot, 'rev-parse', '--abbrev-ref', 'origin/HEAD']);
24932493
if (r.status === 0) {
24942494
// Output is e.g. "origin/main\n" — slice off the "origin/" prefix.
24952495
let s = r.stdout;
@@ -2508,7 +2508,7 @@ function onGeneratePRDescriptionImpl(): void {
25082508
// Current branch — used in the prompt header.
25092509
let branch = '';
25102510
try {
2511-
const r = spawnSync('git', ['-C', workspaceRoot, 'rev-parse', '--abbrev-ref', 'HEAD']);
2511+
const r = spawnText('git', ['-C', workspaceRoot, 'rev-parse', '--abbrev-ref', 'HEAD']);
25122512
if (r.status === 0) {
25132513
branch = r.stdout;
25142514
let end = branch.length;
@@ -2533,7 +2533,7 @@ function onGeneratePRDescriptionImpl(): void {
25332533

25342534
let log = '';
25352535
try {
2536-
const r = spawnSync('git', ['-C', workspaceRoot, 'log', '--no-color', '--pretty=%h %s', range]);
2536+
const r = spawnText('git', ['-C', workspaceRoot, 'log', '--no-color', '--pretty=%h %s', range]);
25372537
if (r.status === 0) log = r.stdout;
25382538
} catch (_e: any) {}
25392539
if (log.length < 1) {
@@ -2543,13 +2543,13 @@ function onGeneratePRDescriptionImpl(): void {
25432543

25442544
let stat = '';
25452545
try {
2546-
const r = spawnSync('git', ['-C', workspaceRoot, 'diff', '--no-color', '--stat=80,80', range]);
2546+
const r = spawnText('git', ['-C', workspaceRoot, 'diff', '--no-color', '--stat=80,80', range]);
25472547
if (r.status === 0) stat = r.stdout;
25482548
} catch (_e: any) {}
25492549

25502550
let body = '';
25512551
try {
2552-
const r = spawnSync('git', ['-C', workspaceRoot, 'diff', '--no-color', '-U2', range]);
2552+
const r = spawnText('git', ['-C', workspaceRoot, 'diff', '--no-color', '-U2', range]);
25532553
if (r.status === 0) body = r.stdout;
25542554
} catch (_e: any) {}
25552555
if (body.length > 8000) body = body.slice(0, 8000) + '\n[…truncated]';
@@ -3089,11 +3089,13 @@ function displayFileContent(filePath: string): void {
30893089
const t1 = Date.now();
30903090
updateBreadcrumb();
30913091
const t2 = Date.now();
3092+
const t3 = Date.now();
30923093
updateStatusBarLanguageImpl(filePath);
30933094
const t4 = Date.now();
30943095
if (editorReady < 1) return;
30953096
const lang = detectLanguage(filePath);
30963097
editorInstance.setLanguage(lang);
3098+
const t5 = Date.now();
30973099
const content = safeReadFile(filePath);
30983100
// Strip a leading UTF-8 BOM (U+FEFF) before it reaches the editor. Windows
30993101
// tools (Notepad, older Visual Studio, PowerShell `Out-File`/`>` default)
@@ -3909,7 +3911,7 @@ function syncInlineBlame(): void {
39093911
const range = String(lineNum) + ',' + String(lineNum);
39103912
let output = '';
39113913
try {
3912-
const r = spawnSync('git', ['blame', '-L', range, '--porcelain', '--', filePath]);
3914+
const r = spawnText('git', ['blame', '-L', range, '--porcelain', '--', filePath]);
39133915
if (r.status === 0) output = r.stdout;
39143916
} catch (e) {
39153917
return '';
@@ -5740,7 +5742,7 @@ function onClaudeRelayRequestFromGuest(guestId: string, prompt: string, wsRoot:
57405742
let claudeBin = '';
57415743
try {
57425744
const cmd = __platform__ === 3 ? 'where' : 'which';
5743-
const r = spawnSync(cmd, ['claude']);
5745+
const r = spawnText(cmd, ['claude']);
57445746
if (r.status === 0 && r.stdout.length > 0) {
57455747
for (let i = 0; i < r.stdout.length; i++) {
57465748
const ch = r.stdout.charCodeAt(i);
@@ -5768,9 +5770,9 @@ function onClaudeRelayRequestFromGuest(guestId: string, prompt: string, wsRoot:
57685770
try {
57695771
const pidStr = String(claudeRelayPid);
57705772
if (__platform__ === 3) {
5771-
spawnSync('taskkill', ['/F', '/PID', pidStr]);
5773+
spawnText('taskkill', ['/F', '/PID', pidStr]);
57725774
} else {
5773-
spawnSync('kill', [pidStr]);
5775+
spawnText('kill', [pidStr]);
57745776
}
57755777
} catch (e) {}
57765778
}
@@ -5909,11 +5911,11 @@ function claudeRelayPollTick(): void {
59095911
try {
59105912
const pidStr = String(claudeRelayPid);
59115913
if (__platform__ === 3) {
5912-
const r = spawnSync('tasklist', ['/FI', 'PID eq ' + pidStr, '/NH']);
5914+
const r = spawnText('tasklist', ['/FI', 'PID eq ' + pidStr, '/NH']);
59135915
if (r.status !== 0) gone = 1;
59145916
else if (r.stdout.length >= 5 && r.stdout.charCodeAt(0) === 73 && r.stdout.charCodeAt(1) === 78) gone = 1;
59155917
} else {
5916-
const r = spawnSync('kill', ['-0', pidStr]);
5918+
const r = spawnText('kill', ['-0', pidStr]);
59175919
if (r.status !== 0) gone = 1;
59185920
}
59195921
} catch (e) {
@@ -6148,9 +6150,9 @@ function onClaudeRelayStopFromGuest(guestId: string, sessionId: string): void {
61486150
try {
61496151
const pidStr = String(claudeRelayPid);
61506152
if (__platform__ === 3) {
6151-
spawnSync('taskkill', ['/F', '/PID', pidStr]);
6153+
spawnText('taskkill', ['/F', '/PID', pidStr]);
61526154
} else {
6153-
spawnSync('kill', [pidStr]);
6155+
spawnText('kill', [pidStr]);
61546156
}
61556157
} catch (e) {}
61566158
claudeRelayPid = 0;

0 commit comments

Comments
 (0)