@addai/node 0.4.0

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.
Files changed (133) hide show
  1. package/README.md +96 -0
  2. package/dist/agent-auth.d.ts +9 -0
  3. package/dist/agent-auth.js +96 -0
  4. package/dist/attachments.d.ts +20 -0
  5. package/dist/attachments.js +164 -0
  6. package/dist/capabilities.d.ts +64 -0
  7. package/dist/capabilities.js +406 -0
  8. package/dist/claude-binary.d.ts +16 -0
  9. package/dist/claude-binary.js +145 -0
  10. package/dist/claude-config.d.ts +11 -0
  11. package/dist/claude-config.js +144 -0
  12. package/dist/claude-print.d.ts +35 -0
  13. package/dist/claude-print.js +312 -0
  14. package/dist/claude-spawn.d.ts +40 -0
  15. package/dist/claude-spawn.js +220 -0
  16. package/dist/cli.d.ts +2 -0
  17. package/dist/cli.js +137 -0
  18. package/dist/codex-binary.d.ts +4 -0
  19. package/dist/codex-binary.js +70 -0
  20. package/dist/codex-spawn.d.ts +47 -0
  21. package/dist/codex-spawn.js +320 -0
  22. package/dist/command-runner.d.ts +9 -0
  23. package/dist/command-runner.js +592 -0
  24. package/dist/config.d.ts +7 -0
  25. package/dist/config.js +38 -0
  26. package/dist/control-server.d.ts +5 -0
  27. package/dist/control-server.js +292 -0
  28. package/dist/diskguard.d.ts +96 -0
  29. package/dist/diskguard.js +435 -0
  30. package/dist/error-classify.d.ts +20 -0
  31. package/dist/error-classify.js +159 -0
  32. package/dist/events.d.ts +26 -0
  33. package/dist/events.js +97 -0
  34. package/dist/gemini-binary.d.ts +4 -0
  35. package/dist/gemini-binary.js +64 -0
  36. package/dist/gemini-spawn.d.ts +52 -0
  37. package/dist/gemini-spawn.js +369 -0
  38. package/dist/git-identity.d.ts +18 -0
  39. package/dist/git-identity.js +104 -0
  40. package/dist/grok-binary.d.ts +4 -0
  41. package/dist/grok-binary.js +70 -0
  42. package/dist/grok-spawn.d.ts +41 -0
  43. package/dist/grok-spawn.js +495 -0
  44. package/dist/guardrails.d.ts +32 -0
  45. package/dist/guardrails.js +125 -0
  46. package/dist/hang-watchdog.d.ts +17 -0
  47. package/dist/hang-watchdog.js +48 -0
  48. package/dist/harness-registry.d.ts +55 -0
  49. package/dist/harness-registry.js +180 -0
  50. package/dist/heartbeat.d.ts +13 -0
  51. package/dist/heartbeat.js +95 -0
  52. package/dist/index.d.ts +12 -0
  53. package/dist/index.js +440 -0
  54. package/dist/install.d.ts +63 -0
  55. package/dist/install.js +467 -0
  56. package/dist/jsonl-tail.d.ts +20 -0
  57. package/dist/jsonl-tail.js +192 -0
  58. package/dist/kimi-binary.d.ts +4 -0
  59. package/dist/kimi-binary.js +68 -0
  60. package/dist/kimi-spawn.d.ts +47 -0
  61. package/dist/kimi-spawn.js +319 -0
  62. package/dist/lockfile.d.ts +30 -0
  63. package/dist/lockfile.js +169 -0
  64. package/dist/mcp-config.d.ts +23 -0
  65. package/dist/mcp-config.js +133 -0
  66. package/dist/memory-capture.d.ts +20 -0
  67. package/dist/memory-capture.js +45 -0
  68. package/dist/memory-pack.d.ts +34 -0
  69. package/dist/memory-pack.js +171 -0
  70. package/dist/model-filter.d.ts +4 -0
  71. package/dist/model-filter.js +34 -0
  72. package/dist/node-health.d.ts +18 -0
  73. package/dist/node-health.js +74 -0
  74. package/dist/pairing.d.ts +20 -0
  75. package/dist/pairing.js +129 -0
  76. package/dist/paths.d.ts +9 -0
  77. package/dist/paths.js +48 -0
  78. package/dist/precompact-hook.d.ts +27 -0
  79. package/dist/precompact-hook.js +136 -0
  80. package/dist/projects.d.ts +25 -0
  81. package/dist/projects.js +255 -0
  82. package/dist/prompt-keystrokes.d.ts +3 -0
  83. package/dist/prompt-keystrokes.js +56 -0
  84. package/dist/pty-helper.d.ts +26 -0
  85. package/dist/pty-helper.js +156 -0
  86. package/dist/request-pump.d.ts +12 -0
  87. package/dist/request-pump.js +249 -0
  88. package/dist/run-retry.d.ts +28 -0
  89. package/dist/run-retry.js +81 -0
  90. package/dist/self-update.d.ts +75 -0
  91. package/dist/self-update.js +192 -0
  92. package/dist/session-runner.d.ts +130 -0
  93. package/dist/session-runner.js +2271 -0
  94. package/dist/sleep-detector.d.ts +11 -0
  95. package/dist/sleep-detector.js +32 -0
  96. package/dist/state-migrate.d.ts +8 -0
  97. package/dist/state-migrate.js +91 -0
  98. package/dist/store.d.ts +5 -0
  99. package/dist/store.js +82 -0
  100. package/dist/supabase-client.d.ts +15 -0
  101. package/dist/supabase-client.js +119 -0
  102. package/dist/time-context.d.ts +10 -0
  103. package/dist/time-context.js +71 -0
  104. package/dist/tokens.d.ts +4 -0
  105. package/dist/tokens.js +33 -0
  106. package/dist/tui/app.d.ts +35 -0
  107. package/dist/tui/app.js +82 -0
  108. package/dist/tui/console-capture.d.ts +13 -0
  109. package/dist/tui/console-capture.js +62 -0
  110. package/dist/tui/dashboard.d.ts +42 -0
  111. package/dist/tui/dashboard.js +205 -0
  112. package/dist/tui/data.d.ts +57 -0
  113. package/dist/tui/data.js +62 -0
  114. package/dist/tui/harnesses.d.ts +16 -0
  115. package/dist/tui/harnesses.js +354 -0
  116. package/dist/tui/render.d.ts +21 -0
  117. package/dist/tui/render.js +116 -0
  118. package/dist/tui/requests.d.ts +18 -0
  119. package/dist/tui/requests.js +127 -0
  120. package/dist/tui/run.d.ts +13 -0
  121. package/dist/tui/run.js +178 -0
  122. package/dist/tui/transcript.d.ts +39 -0
  123. package/dist/tui/transcript.js +234 -0
  124. package/dist/tui.d.ts +1 -0
  125. package/dist/tui.js +314 -0
  126. package/dist/types.d.ts +65 -0
  127. package/dist/types.js +3 -0
  128. package/dist/win.d.ts +67 -0
  129. package/dist/win.js +317 -0
  130. package/package.json +60 -0
  131. package/scripts/fix-pty-helper.js +28 -0
  132. package/scripts/precompact-capture.js +292 -0
  133. package/scripts/smoke-test.sh +74 -0
package/dist/win.js ADDED
@@ -0,0 +1,317 @@
1
+ "use strict";
2
+ // Windows-compatibility helpers shared by every module that discovers or
3
+ // spawns an agent CLI. On macOS every function here is either a no-op
4
+ // passthrough or the same logic the callers used before; the win32 branches
5
+ // are additive. One deliberate Linux delta: the old kimi/codex/gemini PATH
6
+ // probe (execFileSync('command', ...) with no shell) only worked on macOS
7
+ // (which ships /usr/bin/command); on Linux it always threw and discovery
8
+ // fell through to hardcoded candidates. The /bin/sh probe here works on all
9
+ // POSIX, so on Linux a PATH-resident binary now correctly wins — matching
10
+ // macOS behavior and the finders' documented intent.
11
+ //
12
+ // The three Windows realities this file encapsulates:
13
+ //
14
+ // 1. npm installs CLIs as `.cmd` shims (%APPDATA%\npm\<name>.cmd), and
15
+ // Node >=18.17/20 refuses to spawn/execFile a .cmd/.bat without
16
+ // shell:true (EINVAL — the CVE-2024-27980 fix). shell:true means
17
+ // cmd.exe, and our argv routinely carries whole chat prompts (quotes,
18
+ // %VARS%, newlines) — running that through cmd.exe is a command-
19
+ // injection hazard. So instead of a shell we RESOLVE the shim to the
20
+ // node script it wraps and run [process.execPath, script, ...args]
21
+ // directly. No cmd.exe anywhere near prompt text.
22
+ //
23
+ // 2. `command -v` / /bin/sh don't exist on Windows; PATH lookup is
24
+ // `where`, and results need .exe preferred over .cmd.
25
+ //
26
+ // 3. Creating FILE symlinks needs admin/Developer Mode; DIRECTORY links
27
+ // work unprivileged only as junctions. Renaming over an open file
28
+ // fails EPERM/EBUSY (antivirus etc.).
29
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ var desc = Object.getOwnPropertyDescriptor(m, k);
32
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
33
+ desc = { enumerable: true, get: function() { return m[k]; } };
34
+ }
35
+ Object.defineProperty(o, k2, desc);
36
+ }) : (function(o, m, k, k2) {
37
+ if (k2 === undefined) k2 = k;
38
+ o[k2] = m[k];
39
+ }));
40
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
41
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
42
+ }) : function(o, v) {
43
+ o["default"] = v;
44
+ });
45
+ var __importStar = (this && this.__importStar) || (function () {
46
+ var ownKeys = function(o) {
47
+ ownKeys = Object.getOwnPropertyNames || function (o) {
48
+ var ar = [];
49
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
50
+ return ar;
51
+ };
52
+ return ownKeys(o);
53
+ };
54
+ return function (mod) {
55
+ if (mod && mod.__esModule) return mod;
56
+ var result = {};
57
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
58
+ __setModuleDefault(result, mod);
59
+ return result;
60
+ };
61
+ })();
62
+ Object.defineProperty(exports, "__esModule", { value: true });
63
+ exports.IS_WINDOWS = void 0;
64
+ exports.whereLookup = whereLookup;
65
+ exports.npmShimCandidates = npmShimCandidates;
66
+ exports.findCliBinary = findCliBinary;
67
+ exports.resolveCliInvocation = resolveCliInvocation;
68
+ exports.killProcessTree = killProcessTree;
69
+ exports.wrapMcpCommandForPlatform = wrapMcpCommandForPlatform;
70
+ exports.safeRenameSync = safeRenameSync;
71
+ exports.linkOrCopyFile = linkOrCopyFile;
72
+ exports.linkDir = linkDir;
73
+ const fs = __importStar(require("fs"));
74
+ const os = __importStar(require("os"));
75
+ const path = __importStar(require("path"));
76
+ const child_process_1 = require("child_process");
77
+ const paths_1 = require("./paths");
78
+ exports.IS_WINDOWS = process.platform === 'win32';
79
+ function fileExists(p) {
80
+ try {
81
+ return fs.existsSync(p) && fs.statSync(p).isFile();
82
+ }
83
+ catch {
84
+ return false;
85
+ }
86
+ }
87
+ /** `where <name>` on win32. Returns the best hit (.exe > .cmd > .bat) or
88
+ * null. Extensionless hits (git-bash shims) are skipped — CreateProcess
89
+ * can't run them. */
90
+ function whereLookup(name) {
91
+ if (!exports.IS_WINDOWS)
92
+ return null;
93
+ try {
94
+ const out = (0, child_process_1.execFileSync)('where', [name], { encoding: 'utf8', timeout: 5000 });
95
+ const lines = out.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
96
+ const pick = lines.find(l => /\.exe$/i.test(l)) ??
97
+ lines.find(l => /\.cmd$/i.test(l)) ??
98
+ lines.find(l => /\.bat$/i.test(l)) ??
99
+ null;
100
+ return pick && fileExists(pick) ? pick : null;
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ /** Standard npm global-shim locations for a CLI on win32. */
107
+ function npmShimCandidates(name) {
108
+ const out = [];
109
+ const appData = process.env.APPDATA;
110
+ const localAppData = process.env.LOCALAPPDATA;
111
+ if (appData)
112
+ out.push(path.join(appData, 'npm', `${name}.cmd`));
113
+ if (localAppData)
114
+ out.push(path.join(localAppData, 'npm', `${name}.cmd`));
115
+ out.push(path.join(os.homedir(), 'AppData', 'Roaming', 'npm', `${name}.cmd`));
116
+ return out;
117
+ }
118
+ /** POSIX PATH probe via the shell (`command -v` follows aliases/functions
119
+ * to a real path; /bin/sh exists on every macOS/Linux box). */
120
+ function posixCommandV(name) {
121
+ try {
122
+ const out = (0, child_process_1.execFileSync)('/bin/sh', ['-c', `command -v ${name}`], {
123
+ encoding: 'utf8',
124
+ timeout: 5000,
125
+ }).trim();
126
+ return out && fs.existsSync(out) ? out : null;
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ }
132
+ /**
133
+ * Cross-platform CLI discovery: PATH probe first, then platform-specific
134
+ * candidate paths. Returns null when not installed — callers surface an
135
+ * install hint. NOT cached here; each finder module keeps its own cache.
136
+ */
137
+ function findCliBinary(name, opts = {}) {
138
+ if (exports.IS_WINDOWS) {
139
+ const viaWhere = whereLookup(name);
140
+ if (viaWhere)
141
+ return viaWhere;
142
+ const nodeDir = path.dirname(process.execPath);
143
+ const execSiblings = [path.join(nodeDir, `${name}.cmd`), path.join(nodeDir, `${name}.exe`)];
144
+ const toolsShims = [paths_1.RUNTIME_HOME, paths_1.LEGACY_RUNTIME_HOME].flatMap(root => {
145
+ const toolsDir = path.join(root, 'tools');
146
+ return [path.join(toolsDir, `${name}.cmd`), path.join(toolsDir, `${name}.exe`)];
147
+ });
148
+ for (const c of [...npmShimCandidates(name), ...(opts.win32Candidates ?? []), ...execSiblings, ...toolsShims]) {
149
+ if (fileExists(c))
150
+ return c;
151
+ }
152
+ return null;
153
+ }
154
+ const viaSh = posixCommandV(name);
155
+ if (viaSh)
156
+ return viaSh;
157
+ // npm -g drops bins next to the node binary itself (nvm/homebrew/asdf
158
+ // prefixes) — a dir that is often NOT on the daemon's launchd PATH.
159
+ const nodeBinSibling = path.join(path.dirname(process.execPath), name);
160
+ // The daemon's own toolchain prefix — where installs land when the
161
+ // machine's global prefix is root-owned (EACCES fallback).
162
+ // The legacy home is searched too: a box whose state dir was never
163
+ // migrated (because the new home already had its own state.json) can
164
+ // still hold harnesses installed before the rename.
165
+ const toolsBins = [paths_1.RUNTIME_HOME, paths_1.LEGACY_RUNTIME_HOME].map(root => path.join(root, 'tools', 'bin', name));
166
+ for (const c of [...(opts.posixCandidates ?? []), nodeBinSibling, ...toolsBins]) {
167
+ if (fileExists(c))
168
+ return c;
169
+ }
170
+ return null;
171
+ }
172
+ /**
173
+ * Turn a (binary, args) pair into something Node can actually spawn on this
174
+ * platform. POSIX: identity. win32: `.cmd`/`.bat` npm shims are resolved to
175
+ * the node script they wrap and run via process.execPath — never through
176
+ * cmd.exe, because args carry raw prompt text. `.exe` passes through.
177
+ *
178
+ * Throws with a descriptive message when a shim can't be resolved — callers
179
+ * already route spawn errors into a clean per-request failure.
180
+ */
181
+ function resolveCliInvocation(bin, args) {
182
+ if (!exports.IS_WINDOWS)
183
+ return { file: bin, args };
184
+ let target = bin;
185
+ // A bare name (no dir, no extension) can't be CreateProcess'd unless it's
186
+ // an .exe on PATH — try `where` to pin it down.
187
+ if (!path.isAbsolute(target) && !/\.(exe|cmd|bat)$/i.test(target)) {
188
+ const found = whereLookup(target);
189
+ if (found)
190
+ target = found;
191
+ }
192
+ if (/\.(cmd|bat)$/i.test(target)) {
193
+ const script = resolveNpmShimScript(target);
194
+ if (!script) {
195
+ throw new Error(`cannot spawn npm shim safely: ${target} — could not resolve the node script it wraps. ` +
196
+ `Install the CLI's native Windows build (an .exe) or reinstall via npm.`);
197
+ }
198
+ return { file: process.execPath, args: [script, ...args] };
199
+ }
200
+ return { file: target, args };
201
+ }
202
+ /** Parse a package-manager .cmd shim for the `…\node_modules\…js` script it
203
+ * executes. Returns the absolute script path or null. Two shim dialects:
204
+ * npm's cmd-shim writes `"%dp0%\<rel>.js"` (a variable it SETs earlier);
205
+ * pnpm/yarn-classic write `"%~dp0\<rel>.js"` (batch parameter expansion —
206
+ * note: no trailing `%`). */
207
+ function resolveNpmShimScript(shimPath) {
208
+ let body;
209
+ try {
210
+ body = fs.readFileSync(shimPath, 'utf8');
211
+ }
212
+ catch {
213
+ return null;
214
+ }
215
+ const m = body.match(/"%(?:dp0%|~dp0)[\\/]([^"]+\.(?:m|c)?js)"/i);
216
+ if (!m)
217
+ return null;
218
+ const script = path.join(path.dirname(shimPath), m[1]);
219
+ return fs.existsSync(script) ? script : null;
220
+ }
221
+ /**
222
+ * Kill an agent child process. POSIX: SIGINT (graceful — CLIs flush and
223
+ * exit). win32: `taskkill /T /F` — Node's SIGINT emulation is a hard
224
+ * TerminateProcess on the one process anyway, and /T also reaps the MCP
225
+ * server grandchildren that would otherwise be orphaned.
226
+ */
227
+ function killProcessTree(proc) {
228
+ if (exports.IS_WINDOWS && proc.pid) {
229
+ try {
230
+ (0, child_process_1.spawn)('taskkill', ['/PID', String(proc.pid), '/T', '/F'], {
231
+ stdio: 'ignore',
232
+ windowsHide: true,
233
+ }).unref();
234
+ return;
235
+ }
236
+ catch { /* fall through to plain kill */ }
237
+ }
238
+ try {
239
+ proc.kill('SIGINT');
240
+ }
241
+ catch { /* already gone */ }
242
+ }
243
+ /**
244
+ * Rewrite a stdio MCP server command for the platform. On native Windows,
245
+ * registry commands like `npx` are npm .cmd shims that the agent CLIs
246
+ * (claude.exe, codex, grok…) can't CreateProcess directly — the documented
247
+ * fix is a `cmd /c` wrapper. MCP commands + args come from the trusted
248
+ * registry (not chat text), so cmd.exe here is acceptable. `.exe` commands
249
+ * pass through untouched. POSIX: identity.
250
+ */
251
+ function wrapMcpCommandForPlatform(command, args) {
252
+ if (!exports.IS_WINDOWS)
253
+ return { command, args };
254
+ if (/\.exe$/i.test(command))
255
+ return { command, args };
256
+ return { command: 'cmd', args: ['/c', command, ...args] };
257
+ }
258
+ function sleepSync(ms) {
259
+ try {
260
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
261
+ }
262
+ catch { /* SharedArrayBuffer unavailable — skip the wait */ }
263
+ }
264
+ /**
265
+ * renameSync with win32 EPERM/EBUSY retries (antivirus / another process
266
+ * briefly holding the target open), falling back to copy+unlink. POSIX
267
+ * behaves exactly like fs.renameSync.
268
+ */
269
+ function safeRenameSync(from, to) {
270
+ for (let attempt = 0;; attempt++) {
271
+ try {
272
+ fs.renameSync(from, to);
273
+ return;
274
+ }
275
+ catch (err) {
276
+ const code = err.code;
277
+ const retryable = exports.IS_WINDOWS && (code === 'EPERM' || code === 'EBUSY' || code === 'EACCES');
278
+ if (!retryable)
279
+ throw err;
280
+ if (attempt >= 2)
281
+ break;
282
+ sleepSync(30 * (attempt + 1));
283
+ }
284
+ }
285
+ // Last resort: non-atomic replace (still better than failing the write).
286
+ fs.copyFileSync(from, to);
287
+ try {
288
+ fs.unlinkSync(from);
289
+ }
290
+ catch { /* tmp file left behind — harmless */ }
291
+ }
292
+ /**
293
+ * Bridge a host file into a sandbox dir: symlink where possible, copy where
294
+ * not (win32 file symlinks need admin/Developer Mode). maxCopyBytes guards
295
+ * against duplicating something huge when we fall back to copying.
296
+ */
297
+ function linkOrCopyFile(real, dest, maxCopyBytes = 8 * 1024 * 1024) {
298
+ try {
299
+ fs.symlinkSync(real, dest);
300
+ return;
301
+ }
302
+ catch { /* fall through */ }
303
+ try {
304
+ const st = fs.statSync(real);
305
+ if (st.size <= maxCopyBytes)
306
+ fs.copyFileSync(real, dest);
307
+ }
308
+ catch { /* unreadable — skip */ }
309
+ }
310
+ /** Directory link that works unprivileged on win32 ('junction' is ignored
311
+ * by Node on POSIX, where a normal symlink is created). */
312
+ function linkDir(real, dest) {
313
+ try {
314
+ fs.symlinkSync(real, dest, 'junction');
315
+ }
316
+ catch { /* target exists or FS refuses — caller treats as best-effort */ }
317
+ }
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@addai/node",
3
+ "version": "0.4.0",
4
+ "description": "Daemon that pairs a machine with your +Ai account and runs Claude / Codex / Kimi / Gemini agents on its behalf. Reachable via Supabase from Vault, Entity Studio, or any other +Ai surface.",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "addai",
8
+ "entity-studio",
9
+ "claude",
10
+ "codex",
11
+ "kimi",
12
+ "gemini",
13
+ "agent",
14
+ "daemon",
15
+ "supabase",
16
+ "mcp"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/just-AddAi/addai-entity-runtime.git"
21
+ },
22
+ "homepage": "https://github.com/just-AddAi/addai-entity-runtime#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/just-AddAi/addai-entity-runtime/issues"
25
+ },
26
+ "type": "commonjs",
27
+ "main": "dist/index.js",
28
+ "bin": {
29
+ "ainode": "dist/cli.js"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "scripts",
37
+ "README.md"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsc -p tsconfig.json",
41
+ "start": "node dist/cli.js",
42
+ "dev": "tsc -p tsconfig.json && node dist/cli.js",
43
+ "test": "npm run build && node --test test/*.test.mjs",
44
+ "clean": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\"",
45
+ "postinstall": "node scripts/fix-pty-helper.js",
46
+ "prepublishOnly": "npm run build"
47
+ },
48
+ "engines": {
49
+ "node": ">=18"
50
+ },
51
+ "dependencies": {
52
+ "node-pty": "^1.1.0",
53
+ "ws": "^8.21.1"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^20.0.0",
57
+ "@types/ws": "^8.18.1",
58
+ "typescript": "^5.6.0"
59
+ }
60
+ }
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ // node-pty ships POSIX prebuilds whose `spawn-helper` binary can lose its
3
+ // executable bit when the npm tarball is extracted. macOS node-pty
4
+ // posix_spawn()s that helper, so without +x every PTY spawn dies with
5
+ // "posix_spawnp failed." — see src/pty-helper.ts for the full story.
6
+ //
7
+ // This used to hunt for <pkg>/node_modules/node-pty by hand, a path that
8
+ // only exists when node-pty is NESTED. npm hoists it to the tree root under
9
+ // npx, so the fix silently did nothing on exactly the installs that needed
10
+ // it. The real logic now lives in the daemon (dist/pty-helper.js) and
11
+ // resolves the package the way Node does. The daemon also repairs at boot,
12
+ // so if dist isn't built yet (a source checkout installing before its first
13
+ // build) we exit quietly instead of duplicating the logic here.
14
+
15
+ let mod;
16
+ try {
17
+ mod = require('../dist/pty-helper.js');
18
+ } catch {
19
+ process.exit(0);
20
+ }
21
+
22
+ try {
23
+ const line = mod.describeRepair(mod.ensurePtyHelperExecutable());
24
+ if (line) process.stdout.write(`[entities-runtime] ${line}\n`);
25
+ } catch (err) {
26
+ process.stderr.write(`[entities-runtime] pty helper check failed: ${err.message}\n`);
27
+ }
28
+ process.exit(0);