@shogo-ai/cli 1.4.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/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ import {
2
+ AGENT_NAME_PATTERN,
3
+ buildSyncPayload,
4
+ normalizeToolEntry,
5
+ runDeploy,
6
+ validateManifest
7
+ } from "./chunk-YYO73V5H.js";
8
+ import {
9
+ NodeMissingError,
10
+ PlatformPackageManager,
11
+ _resetUnixNodeCache,
12
+ isNodeAvailableOnUnix,
13
+ isNodeAvailableOnWindows,
14
+ pkg,
15
+ resolveBinInvocation
16
+ } from "./chunk-UB4RE2UC.js";
17
+ export {
18
+ AGENT_NAME_PATTERN,
19
+ NodeMissingError,
20
+ PlatformPackageManager,
21
+ _resetUnixNodeCache,
22
+ buildSyncPayload,
23
+ isNodeAvailableOnUnix,
24
+ isNodeAvailableOnWindows,
25
+ normalizeToolEntry,
26
+ pkg,
27
+ resolveBinInvocation,
28
+ runDeploy,
29
+ validateManifest
30
+ };
31
+ //# sourceMappingURL=index.js.map
package/dist/pkg.cjs ADDED
@@ -0,0 +1,457 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/pkg.ts
21
+ var pkg_exports = {};
22
+ __export(pkg_exports, {
23
+ NodeMissingError: () => NodeMissingError,
24
+ PlatformPackageManager: () => PlatformPackageManager,
25
+ _resetUnixNodeCache: () => _resetUnixNodeCache,
26
+ isNodeAvailableOnUnix: () => isNodeAvailableOnUnix,
27
+ isNodeAvailableOnWindows: () => isNodeAvailableOnWindows,
28
+ pkg: () => pkg,
29
+ resolveBinInvocation: () => resolveBinInvocation
30
+ });
31
+ module.exports = __toCommonJS(pkg_exports);
32
+ var import_node_child_process = require("child_process");
33
+ var import_node_fs = require("fs");
34
+ var import_node_path = require("path");
35
+ var NodeMissingError = class extends Error {
36
+ constructor() {
37
+ super(
38
+ "Shogo Desktop on Windows requires Node.js 20+ to run project sandboxes. npm.cmd was not found on PATH and is not present at C:\\Program Files\\nodejs\\. Install Node.js from https://nodejs.org/ (LTS recommended), then restart Shogo."
39
+ );
40
+ this.code = "NODE_NOT_INSTALLED";
41
+ this.name = "NodeMissingError";
42
+ }
43
+ };
44
+ var WINDOWS_NODE_DIR = "C:\\Program Files\\nodejs";
45
+ function isNodeAvailableOnWindows(pathEnv = process.env.PATH) {
46
+ if (process.platform !== "win32") return true;
47
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(WINDOWS_NODE_DIR, "npm.cmd"))) return true;
48
+ if (!pathEnv) return false;
49
+ for (const dir of pathEnv.split(";")) {
50
+ if (dir && (0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "npm.cmd"))) return true;
51
+ }
52
+ return false;
53
+ }
54
+ var _unixNodeAvailable = null;
55
+ function isNodeAvailableOnUnix(pathEnv = process.env.PATH) {
56
+ if (process.platform === "win32") return false;
57
+ if (pathEnv === process.env.PATH && _unixNodeAvailable !== null) return _unixNodeAvailable;
58
+ let found = false;
59
+ if (pathEnv) {
60
+ for (const dir of pathEnv.split(":")) {
61
+ if (!dir) continue;
62
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "node"))) {
63
+ found = true;
64
+ break;
65
+ }
66
+ }
67
+ }
68
+ if (pathEnv === process.env.PATH) _unixNodeAvailable = found;
69
+ return found;
70
+ }
71
+ function _resetUnixNodeCache() {
72
+ _unixNodeAvailable = null;
73
+ }
74
+ function resolveBinInvocation(workspaceDir, binName) {
75
+ const binDir = (0, import_node_path.join)(workspaceDir, "node_modules", ".bin");
76
+ const isWindows = process.platform === "win32";
77
+ const candidates = isWindows ? [(0, import_node_path.join)(binDir, `${binName}.CMD`), (0, import_node_path.join)(binDir, `${binName}.cmd`), (0, import_node_path.join)(binDir, `${binName}.exe`)] : [(0, import_node_path.join)(binDir, binName)];
78
+ const shim = candidates.find((p) => (0, import_node_fs.existsSync)(p));
79
+ if (!shim) return null;
80
+ if (isWindows) return { cmd: shim, argsPrefix: [] };
81
+ if (isNodeAvailableOnUnix()) return { cmd: shim, argsPrefix: [] };
82
+ try {
83
+ const st = (0, import_node_fs.lstatSync)(shim);
84
+ if (!st.isSymbolicLink()) return { cmd: shim, argsPrefix: [] };
85
+ const target = (0, import_node_fs.readlinkSync)(shim);
86
+ const jsEntry = (0, import_node_path.isAbsolute)(target) ? target : (0, import_node_path.resolve)((0, import_node_path.dirname)(shim), target);
87
+ if (!(0, import_node_fs.existsSync)(jsEntry)) return { cmd: shim, argsPrefix: [] };
88
+ const bun = process.env.SHOGO_BUN_PATH || "bun";
89
+ return { cmd: bun, argsPrefix: [jsEntry] };
90
+ } catch {
91
+ return { cmd: shim, argsPrefix: [] };
92
+ }
93
+ }
94
+ var IS_WINDOWS = process.platform === "win32";
95
+ var DEFAULT_INSTALL_TIMEOUT = parseInt(
96
+ process.env.SHOGO_INSTALL_TIMEOUT_MS || (IS_WINDOWS ? "300000" : "300000"),
97
+ 10
98
+ );
99
+ var DEFAULT_EXEC_TIMEOUT = 6e4;
100
+ var PlatformPackageManager = class {
101
+ constructor() {
102
+ this.isWindows = IS_WINDOWS;
103
+ }
104
+ /** Resolved path to the bun binary — prefers SHOGO_BUN_PATH (set by desktop app) over bare `bun`. */
105
+ get bunBinary() {
106
+ return process.env.SHOGO_BUN_PATH || "bun";
107
+ }
108
+ shellOpt() {
109
+ return IS_WINDOWS ? true : void 0;
110
+ }
111
+ spawnEnv(base) {
112
+ const source = base ?? process.env;
113
+ const env = {};
114
+ let pathValue = "";
115
+ for (const [k, v] of Object.entries(source)) {
116
+ if (v === void 0) continue;
117
+ if (IS_WINDOWS && k.toLowerCase() === "path") {
118
+ if (v) pathValue = v;
119
+ continue;
120
+ }
121
+ env[k] = v;
122
+ }
123
+ if (IS_WINDOWS) {
124
+ const nodePath = "C:\\Program Files\\nodejs";
125
+ env.PATH = pathValue.includes(nodePath) ? pathValue : `${nodePath};${pathValue}`;
126
+ }
127
+ return env;
128
+ }
129
+ // ---------------------------------------------------------------------------
130
+ // Install — synchronous
131
+ // ---------------------------------------------------------------------------
132
+ installSync(cwd, opts) {
133
+ const timeout = opts?.timeout ?? DEFAULT_INSTALL_TIMEOUT;
134
+ const stdio = opts?.stdio ?? "pipe";
135
+ const env = this.spawnEnv(opts?.env);
136
+ if (IS_WINDOWS) {
137
+ if (isNodeAvailableOnWindows(env.PATH)) {
138
+ try {
139
+ (0, import_node_child_process.execSync)("npm.cmd install --loglevel=error", {
140
+ cwd,
141
+ timeout,
142
+ stdio,
143
+ env,
144
+ shell: "cmd.exe"
145
+ });
146
+ return;
147
+ } catch (err) {
148
+ const wrapped = wrapWindowsNpmError(err);
149
+ if (!wrapped) throw err;
150
+ }
151
+ }
152
+ this.installSyncBunCopyfile(cwd, opts);
153
+ return;
154
+ }
155
+ const bun = this.bunBinary;
156
+ if (opts?.frozen) {
157
+ try {
158
+ (0, import_node_child_process.execSync)(`"${bun}" install --frozen-lockfile 2>&1`, { cwd, timeout, stdio, env });
159
+ return;
160
+ } catch {
161
+ }
162
+ }
163
+ (0, import_node_child_process.execSync)(`"${bun}" install`, { cwd, timeout, stdio, env });
164
+ }
165
+ /**
166
+ * Windows-only fallback path: `bun install [--frozen-lockfile]
167
+ * --backend=copyfile`. Used when npm.cmd isn't available on PATH
168
+ * (typically: Shogo Desktop bundles bun but the user didn't install
169
+ * Node.js separately).
170
+ *
171
+ * The `--backend=copyfile` flag forces bun to copy package contents
172
+ * into `node_modules/` instead of hardlinking from
173
+ * `~/.bun/install/cache`. Hardlinks are bun's default for speed but
174
+ * fail in confusing ways on Windows (the destination directory gets
175
+ * created but ends up empty, and downstream `require('webidl-conversions')`
176
+ * etc. then crash with MODULE_NOT_FOUND). See bun#10327 / #28653.
177
+ */
178
+ installSyncBunCopyfile(cwd, opts) {
179
+ const timeout = opts?.timeout ?? DEFAULT_INSTALL_TIMEOUT;
180
+ const stdio = opts?.stdio ?? "pipe";
181
+ const env = this.spawnEnv(opts?.env);
182
+ const bun = this.bunBinary;
183
+ console.warn(
184
+ "[platform-pkg] Node.js / npm.cmd not found on PATH \u2014 falling back to `bun install --backend=copyfile` (Shogo Desktop bundle path). This is slower than hardlink but avoids the Windows extract bug."
185
+ );
186
+ if (opts?.frozen) {
187
+ try {
188
+ (0, import_node_child_process.execSync)(`"${bun}" install --frozen-lockfile --backend=copyfile`, {
189
+ cwd,
190
+ timeout,
191
+ stdio,
192
+ env
193
+ });
194
+ return;
195
+ } catch {
196
+ }
197
+ }
198
+ (0, import_node_child_process.execSync)(`"${bun}" install --backend=copyfile`, { cwd, timeout, stdio, env });
199
+ }
200
+ // ---------------------------------------------------------------------------
201
+ // Install — async with spawn (used by RuntimeManager)
202
+ // ---------------------------------------------------------------------------
203
+ installAsync(cwd, opts) {
204
+ if (IS_WINDOWS) {
205
+ const env = this.spawnEnv(opts?.env);
206
+ if (isNodeAvailableOnWindows(env.PATH)) {
207
+ return this.installAsyncWindowsNpm(cwd, opts).catch((err) => {
208
+ if (err instanceof NodeMissingError) {
209
+ return this.installAsyncBunCopyfile(cwd, opts);
210
+ }
211
+ throw err;
212
+ });
213
+ }
214
+ return this.installAsyncBunCopyfile(cwd, opts);
215
+ }
216
+ return this.installAsyncBun(cwd, opts);
217
+ }
218
+ installAsyncWindowsNpm(cwd, opts) {
219
+ const timeout = opts?.timeout ?? DEFAULT_INSTALL_TIMEOUT;
220
+ const env = this.spawnEnv(opts?.env);
221
+ const cmd = "npm.cmd";
222
+ const args = ["install", "--loglevel=error"];
223
+ return new Promise((resolve, reject) => {
224
+ const proc = (0, import_node_child_process.spawn)(cmd, args, {
225
+ cwd,
226
+ stdio: "pipe",
227
+ env,
228
+ shell: true
229
+ });
230
+ const timer = setTimeout(() => {
231
+ proc.kill();
232
+ reject(new Error(`${cmd} install timed out after ${timeout / 1e3}s`));
233
+ }, timeout);
234
+ let stderr = "";
235
+ proc.stderr?.on("data", (d) => {
236
+ stderr += d.toString();
237
+ });
238
+ proc.on("exit", (code) => {
239
+ clearTimeout(timer);
240
+ if (code === 0) return resolve();
241
+ if (/not recognized as an internal or external command/i.test(stderr)) {
242
+ return reject(new NodeMissingError());
243
+ }
244
+ reject(new Error(`${cmd} install exited with code ${code}
245
+ ${stderr}`));
246
+ });
247
+ proc.on("error", (err) => {
248
+ clearTimeout(timer);
249
+ if (err.code === "ENOENT") return reject(new NodeMissingError());
250
+ reject(err);
251
+ });
252
+ });
253
+ }
254
+ installAsyncBunCopyfile(cwd, opts) {
255
+ console.warn(
256
+ "[platform-pkg] Node.js / npm.cmd not found on PATH \u2014 falling back to `bun install --backend=copyfile` (Shogo Desktop bundle path)."
257
+ );
258
+ return this.installAsyncBun(cwd, opts, ["--backend=copyfile"]);
259
+ }
260
+ installAsyncBun(cwd, opts, extraArgs = []) {
261
+ const timeout = opts?.timeout ?? DEFAULT_INSTALL_TIMEOUT;
262
+ const env = this.spawnEnv(opts?.env);
263
+ const bun = this.bunBinary;
264
+ const baseArgs = ["install", ...extraArgs];
265
+ const frozenArgs = opts?.frozen ? ["install", "--frozen-lockfile", ...extraArgs] : null;
266
+ const runOnce = (args) => new Promise((resolve, reject) => {
267
+ const proc = (0, import_node_child_process.spawn)(bun, args, {
268
+ cwd,
269
+ stdio: "pipe",
270
+ env
271
+ });
272
+ const timer = setTimeout(() => {
273
+ proc.kill();
274
+ reject(new Error(`bun ${args.join(" ")} timed out after ${timeout / 1e3}s`));
275
+ }, timeout);
276
+ let stderr = "";
277
+ proc.stderr?.on("data", (d) => {
278
+ stderr += d.toString();
279
+ });
280
+ proc.stdout?.on("data", () => {
281
+ });
282
+ proc.on("exit", (code) => {
283
+ clearTimeout(timer);
284
+ resolve({ ok: code === 0, stderr });
285
+ });
286
+ proc.on("error", (err) => {
287
+ clearTimeout(timer);
288
+ reject(err);
289
+ });
290
+ });
291
+ const run = async () => {
292
+ if (frozenArgs) {
293
+ const first = await runOnce(frozenArgs);
294
+ if (first.ok) return;
295
+ }
296
+ const second = await runOnce(baseArgs);
297
+ if (!second.ok) {
298
+ throw new Error(`bun install failed: ${second.stderr}`);
299
+ }
300
+ };
301
+ return run();
302
+ }
303
+ // ---------------------------------------------------------------------------
304
+ // Exec — run an npx/bunx tool synchronously
305
+ // ---------------------------------------------------------------------------
306
+ execToolSync(tool, args, cwd, opts) {
307
+ const timeout = opts?.timeout ?? DEFAULT_EXEC_TIMEOUT;
308
+ const stdio = opts?.stdio ?? "pipe";
309
+ const env = this.spawnEnv(opts?.env);
310
+ const argStr = args.length > 0 ? ` ${args.join(" ")}` : "";
311
+ const bun = this.bunBinary;
312
+ const cmd = IS_WINDOWS ? `npx ${tool}${argStr}` : `"${bun}" x ${opts?.useBunFlag ? "--bun " : ""}${tool}${argStr}`;
313
+ return (0, import_node_child_process.execSync)(cmd, {
314
+ cwd,
315
+ timeout,
316
+ stdio,
317
+ env,
318
+ // shellOpt() returns `true` on Windows so Node/Bun pick the platform
319
+ // default shell from `process.env.ComSpec`. ExecSyncOptions only
320
+ // narrowly types `shell` as `string | undefined`, but the runtime
321
+ // accepts a boolean and the cross-platform behavior depends on it
322
+ // (see shellOpt() doc).
323
+ shell: this.shellOpt(),
324
+ encoding: "utf-8"
325
+ });
326
+ }
327
+ // ---------------------------------------------------------------------------
328
+ // Exec — async variant that DOES NOT block the JS event loop.
329
+ //
330
+ // The sync variant above is fine for one-shot project scaffolding
331
+ // (template.copy.ts) where blocking is intentional. Inside the runtime's
332
+ // hot path (PreviewManager.runPrismaIfNeeded → /pool/assign) it caused
333
+ // a measured ~4.7s freeze of /pool/assign in staging because prisma's
334
+ // child process held the event loop for the full duration. Always
335
+ // prefer this variant in long-lived services.
336
+ // ---------------------------------------------------------------------------
337
+ async execToolAsync(tool, args, cwd, opts) {
338
+ const timeout = opts?.timeout ?? DEFAULT_EXEC_TIMEOUT;
339
+ const stdio = opts?.stdio ?? "pipe";
340
+ const env = this.spawnEnv(opts?.env);
341
+ let cmd;
342
+ let argv;
343
+ let useShell;
344
+ if (IS_WINDOWS) {
345
+ const argStr = args.length > 0 ? ` ${args.join(" ")}` : "";
346
+ cmd = `npx ${tool}${argStr}`;
347
+ argv = [];
348
+ useShell = true;
349
+ } else {
350
+ cmd = this.bunBinary;
351
+ argv = ["x"];
352
+ if (opts?.useBunFlag) argv.push("--bun");
353
+ argv.push(tool, ...args);
354
+ useShell = false;
355
+ }
356
+ return new Promise((resolvePromise, rejectPromise) => {
357
+ const proc = (0, import_node_child_process.spawn)(cmd, argv, {
358
+ cwd,
359
+ env,
360
+ stdio,
361
+ shell: useShell ? this.shellOpt() : false
362
+ });
363
+ let stdout = "";
364
+ let stderr = "";
365
+ proc.stdout?.on("data", (chunk) => {
366
+ stdout += chunk.toString();
367
+ });
368
+ proc.stderr?.on("data", (chunk) => {
369
+ stderr += chunk.toString();
370
+ });
371
+ const timer = setTimeout(() => {
372
+ try {
373
+ proc.kill("SIGKILL");
374
+ } catch {
375
+ }
376
+ rejectPromise(
377
+ new Error(`${tool} ${args.join(" ")} timed out after ${timeout}ms`)
378
+ );
379
+ }, timeout);
380
+ proc.on("error", (err) => {
381
+ clearTimeout(timer);
382
+ rejectPromise(err);
383
+ });
384
+ proc.on("exit", (code) => {
385
+ clearTimeout(timer);
386
+ if (code === 0) {
387
+ resolvePromise(stdout);
388
+ } else {
389
+ const errMsg = stderr.trim() || stdout.trim() || `exit ${code}`;
390
+ rejectPromise(new Error(`${tool} ${args.join(" ")} failed: ${errMsg}`));
391
+ }
392
+ });
393
+ });
394
+ }
395
+ // ---------------------------------------------------------------------------
396
+ // Prisma convenience wrappers
397
+ // ---------------------------------------------------------------------------
398
+ prismaGenerate(cwd, opts) {
399
+ this.execToolSync("prisma", ["generate"], cwd, {
400
+ timeout: opts?.timeout ?? 3e4,
401
+ stdio: opts?.stdio,
402
+ env: opts?.env
403
+ });
404
+ }
405
+ prismaDbPush(cwd, opts) {
406
+ const args = ["db", "push"];
407
+ if (opts?.acceptDataLoss) args.push("--accept-data-loss");
408
+ this.execToolSync("prisma", args, cwd, {
409
+ timeout: opts?.timeout ?? DEFAULT_EXEC_TIMEOUT,
410
+ stdio: opts?.stdio,
411
+ env: opts?.env
412
+ });
413
+ }
414
+ // Async siblings of the prisma wrappers above. Use these from any
415
+ // long-running service (runtime, agent gateway, preview manager) so a
416
+ // 1-3s prisma child process doesn't freeze /health, /pool/assign, or
417
+ // other concurrent HTTP work. Same arg shape, same timeout defaults.
418
+ async prismaGenerateAsync(cwd, opts) {
419
+ await this.execToolAsync("prisma", ["generate"], cwd, {
420
+ timeout: opts?.timeout ?? 3e4,
421
+ stdio: opts?.stdio,
422
+ env: opts?.env
423
+ });
424
+ }
425
+ async prismaDbPushAsync(cwd, opts) {
426
+ const args = ["db", "push"];
427
+ if (opts?.acceptDataLoss) args.push("--accept-data-loss");
428
+ await this.execToolAsync("prisma", args, cwd, {
429
+ timeout: opts?.timeout ?? DEFAULT_EXEC_TIMEOUT,
430
+ stdio: opts?.stdio,
431
+ env: opts?.env
432
+ });
433
+ }
434
+ };
435
+ function wrapWindowsNpmError(err) {
436
+ if (!IS_WINDOWS) return null;
437
+ const blob = `${err?.stderr?.toString?.() ?? ""}
438
+ ${err?.stdout?.toString?.() ?? ""}
439
+ ${err?.message ?? ""}`;
440
+ if (/not recognized as an internal or external command/i.test(blob)) {
441
+ return new NodeMissingError();
442
+ }
443
+ if (err?.code === "ENOENT") return new NodeMissingError();
444
+ return null;
445
+ }
446
+ var pkg = new PlatformPackageManager();
447
+ // Annotate the CommonJS export names for ESM import in node:
448
+ 0 && (module.exports = {
449
+ NodeMissingError,
450
+ PlatformPackageManager,
451
+ _resetUnixNodeCache,
452
+ isNodeAvailableOnUnix,
453
+ isNodeAvailableOnWindows,
454
+ pkg,
455
+ resolveBinInvocation
456
+ });
457
+ //# sourceMappingURL=pkg.cjs.map
package/dist/pkg.d.cts ADDED
@@ -0,0 +1,139 @@
1
+ import { StdioOptions } from 'node:child_process';
2
+
3
+ /**
4
+ * PlatformPackageManager — centralises Windows vs Unix differences for
5
+ * package-manager operations (install, exec, prisma, etc.).
6
+ *
7
+ * On Windows Bun 1.x's default hardlink backend can leave empty package
8
+ * directories in `node_modules/` (most visibly under transitive deps
9
+ * like `whatwg-url` → `webidl-conversions`, which then crashes any
10
+ * `expo export` / Vite build that imports them). We mitigate by
11
+ * preferring `npm.cmd` for installs when Node.js is on PATH; if it
12
+ * isn't (Shogo Desktop ships its own bun but not Node), we fall back
13
+ * to `bun install --backend=copyfile` which sidesteps the hardlink
14
+ * path entirely. The trade-off is a slower install vs. a half-broken
15
+ * `node_modules` that fails downstream commands without explanation.
16
+ *
17
+ * Lifted into the SDK from `@shogo/shared-runtime` (was AGPL) under MIT
18
+ * so the SDK CLI (`bin/shogo.ts`) and userland tooling can use it without
19
+ * dragging an AGPL runtime dependency into the published `@shogo-ai/sdk`
20
+ * package. Shogo Technologies, Inc. holds copyright; the file is
21
+ * dependency-free (only `node:child_process`, `node:fs`, `node:path`).
22
+ */
23
+
24
+ interface PkgInstallOptions {
25
+ timeout?: number;
26
+ stdio?: StdioOptions;
27
+ env?: NodeJS.ProcessEnv;
28
+ /** Attempt --frozen-lockfile first, fall back to plain install (bun only). */
29
+ frozen?: boolean;
30
+ }
31
+ /** Error thrown when the Windows Node.js prerequisite is missing. */
32
+ declare class NodeMissingError extends Error {
33
+ readonly code: "NODE_NOT_INSTALLED";
34
+ constructor();
35
+ }
36
+ /**
37
+ * True if npm.cmd can be located via PATH or the standard Windows install dir.
38
+ * Cheap — only a single existsSync + PATH walk — so safe to call from hot paths.
39
+ * Always returns true on non-Windows (bun is used there).
40
+ */
41
+ declare function isNodeAvailableOnWindows(pathEnv?: string | undefined): boolean;
42
+ /**
43
+ * True if a `node` binary is on PATH (Unix-style search). The Shogo
44
+ * Desktop API process is spawned by Electron with a PATH inherited
45
+ * from `launchctl`, which on macOS frequently EXCLUDES the user's
46
+ * shell-managed paths (Homebrew, nvm, asdf). `process.env.PATH` is
47
+ * what child spawns see when they exec a `#!/usr/bin/env node` shim,
48
+ * so this is the right thing to check.
49
+ *
50
+ * Cached for the lifetime of the process — node-install state doesn't
51
+ * change between project opens, and re-walking PATH on every spawn
52
+ * adds up.
53
+ *
54
+ * Pass an explicit `pathEnv` only from tests; production callers should
55
+ * use the default.
56
+ */
57
+ declare function isNodeAvailableOnUnix(pathEnv?: string | undefined): boolean;
58
+ /** Reset the cached node-availability probe — only used by tests. */
59
+ declare function _resetUnixNodeCache(): void;
60
+ /**
61
+ * Resolve a `node_modules/.bin/<name>` invocation that works whether
62
+ * or not a system `node` is on PATH.
63
+ *
64
+ * Why this exists: npm publishes its `.bin/<tool>` shims with
65
+ * `#!/usr/bin/env node` at the top of the underlying JS file. The
66
+ * kernel reads the shebang and tries to exec `/usr/bin/env node`;
67
+ * if `node` isn't on PATH the exec fails with `env: node: No such
68
+ * file or directory` and the spawn exits 127. Shogo Desktop ships
69
+ * the bundled `bun` runtime but does not bundle Node.js, and many
70
+ * end-user macOS PATHs (launchctl-default) don't expose any
71
+ * shell-installed node either — so every `spawn(.bin/vite, ...)`
72
+ * fails on first use. Observed in main.log:
73
+ * [CanvasBuildManager] Build error: env: node: No such file or directory
74
+ * [preview-manager] Vite build --watch exited (code=127, signal=null)
75
+ *
76
+ * Returns `null` if the shim doesn't exist (caller should treat
77
+ * this exactly like the historical `existsSync(viteBin)` no-op
78
+ * path). Otherwise returns `{ cmd, argsPrefix }`:
79
+ * - On Windows, or when `node` is on PATH: the shim itself with
80
+ * no prefix args (preserves the current behavior — fastest path).
81
+ * - When node is missing on Unix: `readlinkSync` resolves the shim
82
+ * to its underlying `.../bin/<tool>.js`, and we route through
83
+ * the bundled `bun` (`{ cmd: bunBinary, argsPrefix: [jsEntry] }`),
84
+ * which executes the JS directly and treats the shebang as a
85
+ * comment. Bun's CLI compatibility is a superset of node for
86
+ * the bundler tools we ship (vite, expo) — same exit codes,
87
+ * same flag parsing.
88
+ */
89
+ declare function resolveBinInvocation(workspaceDir: string, binName: string): {
90
+ cmd: string;
91
+ argsPrefix: string[];
92
+ } | null;
93
+ interface PkgExecOptions {
94
+ timeout?: number;
95
+ stdio?: StdioOptions;
96
+ env?: NodeJS.ProcessEnv;
97
+ /** Use `bunx --bun` instead of plain `bunx` (needed for some tools). */
98
+ useBunFlag?: boolean;
99
+ }
100
+ declare class PlatformPackageManager {
101
+ readonly isWindows: boolean;
102
+ /** Resolved path to the bun binary — prefers SHOGO_BUN_PATH (set by desktop app) over bare `bun`. */
103
+ get bunBinary(): string;
104
+ private shellOpt;
105
+ private spawnEnv;
106
+ installSync(cwd: string, opts?: PkgInstallOptions): void;
107
+ /**
108
+ * Windows-only fallback path: `bun install [--frozen-lockfile]
109
+ * --backend=copyfile`. Used when npm.cmd isn't available on PATH
110
+ * (typically: Shogo Desktop bundles bun but the user didn't install
111
+ * Node.js separately).
112
+ *
113
+ * The `--backend=copyfile` flag forces bun to copy package contents
114
+ * into `node_modules/` instead of hardlinking from
115
+ * `~/.bun/install/cache`. Hardlinks are bun's default for speed but
116
+ * fail in confusing ways on Windows (the destination directory gets
117
+ * created but ends up empty, and downstream `require('webidl-conversions')`
118
+ * etc. then crash with MODULE_NOT_FOUND). See bun#10327 / #28653.
119
+ */
120
+ private installSyncBunCopyfile;
121
+ installAsync(cwd: string, opts?: PkgInstallOptions): Promise<void>;
122
+ private installAsyncWindowsNpm;
123
+ private installAsyncBunCopyfile;
124
+ private installAsyncBun;
125
+ execToolSync(tool: string, args: string[], cwd: string, opts?: PkgExecOptions): string;
126
+ execToolAsync(tool: string, args: string[], cwd: string, opts?: PkgExecOptions): Promise<string>;
127
+ prismaGenerate(cwd: string, opts?: PkgExecOptions): void;
128
+ prismaDbPush(cwd: string, opts?: PkgExecOptions & {
129
+ acceptDataLoss?: boolean;
130
+ }): void;
131
+ prismaGenerateAsync(cwd: string, opts?: PkgExecOptions): Promise<void>;
132
+ prismaDbPushAsync(cwd: string, opts?: PkgExecOptions & {
133
+ acceptDataLoss?: boolean;
134
+ }): Promise<void>;
135
+ }
136
+ /** Singleton — most consumers just need one. */
137
+ declare const pkg: PlatformPackageManager;
138
+
139
+ export { NodeMissingError, type PkgExecOptions, type PkgInstallOptions, PlatformPackageManager, _resetUnixNodeCache, isNodeAvailableOnUnix, isNodeAvailableOnWindows, pkg, resolveBinInvocation };