@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/LICENSE +21 -0
- package/README.md +18 -0
- package/dist/chunk-UB4RE2UC.js +427 -0
- package/dist/chunk-YYO73V5H.js +227 -0
- package/dist/deploy.cjs +255 -0
- package/dist/deploy.d.cts +168 -0
- package/dist/deploy.d.ts +168 -0
- package/dist/deploy.js +15 -0
- package/dist/index.cjs +688 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +31 -0
- package/dist/pkg.cjs +457 -0
- package/dist/pkg.d.cts +139 -0
- package/dist/pkg.d.ts +139 -0
- package/dist/pkg.js +19 -0
- package/package.json +65 -0
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 };
|