@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.cjs ADDED
@@ -0,0 +1,688 @@
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/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AGENT_NAME_PATTERN: () => AGENT_NAME_PATTERN,
24
+ NodeMissingError: () => NodeMissingError,
25
+ PlatformPackageManager: () => PlatformPackageManager,
26
+ _resetUnixNodeCache: () => _resetUnixNodeCache,
27
+ buildSyncPayload: () => buildSyncPayload,
28
+ isNodeAvailableOnUnix: () => isNodeAvailableOnUnix,
29
+ isNodeAvailableOnWindows: () => isNodeAvailableOnWindows,
30
+ normalizeToolEntry: () => normalizeToolEntry,
31
+ pkg: () => pkg,
32
+ resolveBinInvocation: () => resolveBinInvocation,
33
+ runDeploy: () => runDeploy,
34
+ validateManifest: () => validateManifest
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/deploy.ts
39
+ var AGENT_NAME_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
40
+ var KNOWN_KEYS = /* @__PURE__ */ new Set([
41
+ "systemPrompt",
42
+ "tools",
43
+ "characterName",
44
+ "displayName",
45
+ "voiceId",
46
+ "firstMessage",
47
+ "model"
48
+ ]);
49
+ function validateManifest(raw) {
50
+ const issues = [];
51
+ if (raw == null) return { agents: {}, issues };
52
+ if (typeof raw !== "object" || Array.isArray(raw)) {
53
+ issues.push({
54
+ path: "agents",
55
+ message: "`agents` must be an object keyed by agent name"
56
+ });
57
+ return { agents: {}, issues };
58
+ }
59
+ const agents = {};
60
+ for (const [name, value] of Object.entries(raw)) {
61
+ if (!AGENT_NAME_PATTERN.test(name)) {
62
+ issues.push({
63
+ path: `agents.${name}`,
64
+ message: `agent name must match ${AGENT_NAME_PATTERN}`
65
+ });
66
+ continue;
67
+ }
68
+ if (value == null || typeof value !== "object" || Array.isArray(value)) {
69
+ issues.push({
70
+ path: `agents.${name}`,
71
+ message: "value must be an object"
72
+ });
73
+ continue;
74
+ }
75
+ const entry = {};
76
+ for (const [k, v] of Object.entries(value)) {
77
+ if (!KNOWN_KEYS.has(k)) {
78
+ issues.push({
79
+ path: `agents.${name}.${k}`,
80
+ message: `unknown field; supported: ${[...KNOWN_KEYS].join(", ")}`
81
+ });
82
+ continue;
83
+ }
84
+ if (k === "tools") {
85
+ if (!Array.isArray(v)) {
86
+ issues.push({
87
+ path: `agents.${name}.tools`,
88
+ message: "must be an array of strings or { name, description?, inputSchema? } objects"
89
+ });
90
+ continue;
91
+ }
92
+ const tools = [];
93
+ const seenNames = /* @__PURE__ */ new Set();
94
+ let valid = true;
95
+ for (let i = 0; i < v.length; i++) {
96
+ const t = v[i];
97
+ if (typeof t === "string") {
98
+ if (t.length === 0) {
99
+ issues.push({
100
+ path: `agents.${name}.tools[${i}]`,
101
+ message: "tool name must be a non-empty string"
102
+ });
103
+ valid = false;
104
+ continue;
105
+ }
106
+ if (seenNames.has(t)) {
107
+ issues.push({
108
+ path: `agents.${name}.tools[${i}]`,
109
+ message: `duplicate tool name '${t}'`
110
+ });
111
+ valid = false;
112
+ continue;
113
+ }
114
+ seenNames.add(t);
115
+ tools.push(t);
116
+ continue;
117
+ }
118
+ if (t == null || typeof t !== "object" || Array.isArray(t)) {
119
+ issues.push({
120
+ path: `agents.${name}.tools[${i}]`,
121
+ message: "tools entries must be a string or { name, description?, inputSchema? }"
122
+ });
123
+ valid = false;
124
+ continue;
125
+ }
126
+ const obj = t;
127
+ if (typeof obj.name !== "string" || obj.name.length === 0) {
128
+ issues.push({
129
+ path: `agents.${name}.tools[${i}].name`,
130
+ message: "name is required and must be a non-empty string"
131
+ });
132
+ valid = false;
133
+ continue;
134
+ }
135
+ if (seenNames.has(obj.name)) {
136
+ issues.push({
137
+ path: `agents.${name}.tools[${i}]`,
138
+ message: `duplicate tool name '${obj.name}'`
139
+ });
140
+ valid = false;
141
+ continue;
142
+ }
143
+ if (obj.description !== void 0 && typeof obj.description !== "string") {
144
+ issues.push({
145
+ path: `agents.${name}.tools[${i}].description`,
146
+ message: "description must be a string"
147
+ });
148
+ valid = false;
149
+ continue;
150
+ }
151
+ if (obj.inputSchema !== void 0 && (obj.inputSchema === null || typeof obj.inputSchema !== "object" || Array.isArray(obj.inputSchema))) {
152
+ issues.push({
153
+ path: `agents.${name}.tools[${i}].inputSchema`,
154
+ message: "inputSchema must be a JSON Schema object"
155
+ });
156
+ valid = false;
157
+ continue;
158
+ }
159
+ seenNames.add(obj.name);
160
+ const descriptor = { name: obj.name };
161
+ if (typeof obj.description === "string") {
162
+ descriptor.description = obj.description;
163
+ }
164
+ if (obj.inputSchema !== void 0) {
165
+ descriptor.inputSchema = obj.inputSchema;
166
+ }
167
+ tools.push(descriptor);
168
+ }
169
+ if (valid) entry.tools = tools;
170
+ continue;
171
+ }
172
+ if (typeof v !== "string") {
173
+ issues.push({
174
+ path: `agents.${name}.${k}`,
175
+ message: "must be a string"
176
+ });
177
+ continue;
178
+ }
179
+ ;
180
+ entry[k] = v;
181
+ }
182
+ if (entry.voiceId && !entry.firstMessage) {
183
+ issues.push({
184
+ path: `agents.${name}.firstMessage`,
185
+ message: "voiceId is set; consider providing firstMessage so the agent has a greeting"
186
+ });
187
+ }
188
+ agents[name] = entry;
189
+ }
190
+ return { agents, issues };
191
+ }
192
+ function normalizeToolEntry(entry) {
193
+ if (typeof entry === "string") return { name: entry };
194
+ const out = { name: entry.name };
195
+ if (entry.description !== void 0) out.description = entry.description;
196
+ if (entry.inputSchema !== void 0) out.inputSchema = entry.inputSchema;
197
+ return out;
198
+ }
199
+ function buildSyncPayload(opts) {
200
+ const out = { agents: {}, prune: opts.prune, dryRun: opts.dryRun };
201
+ for (const [name, entry] of Object.entries(opts.manifest)) {
202
+ const a = {};
203
+ if (entry.systemPrompt !== void 0) a.systemPrompt = entry.systemPrompt;
204
+ if (entry.tools !== void 0) {
205
+ a.tools = entry.tools.map(normalizeToolEntry);
206
+ }
207
+ if (entry.characterName !== void 0) a.characterName = entry.characterName;
208
+ if (entry.displayName !== void 0) a.displayName = entry.displayName;
209
+ if (entry.voiceId !== void 0) a.voiceId = entry.voiceId;
210
+ if (entry.firstMessage !== void 0) a.firstMessage = entry.firstMessage;
211
+ if (entry.model !== void 0) a.model = entry.model;
212
+ out.agents[name] = a;
213
+ }
214
+ return out;
215
+ }
216
+ function resolveDeployHeaders(options) {
217
+ const headers = {
218
+ "content-type": "application/json"
219
+ };
220
+ const auth = options.auth ?? (options.shogoApiKey ? { kind: "apiKey", apiKey: options.shogoApiKey } : void 0);
221
+ if (!auth) {
222
+ throw new Error(
223
+ 'runDeploy: missing auth \u2014 pass `auth: { kind: "apiKey", apiKey }` or `auth: { kind: "runtimeToken", token }`'
224
+ );
225
+ }
226
+ if (auth.kind === "apiKey") {
227
+ headers.authorization = `Bearer ${auth.apiKey}`;
228
+ } else {
229
+ headers["x-runtime-token"] = auth.token;
230
+ }
231
+ return headers;
232
+ }
233
+ async function runDeploy(options) {
234
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
235
+ if (typeof fetchImpl !== "function") {
236
+ throw new Error("runDeploy: global fetch is unavailable; pass options.fetchImpl");
237
+ }
238
+ const url = `${options.apiUrl.replace(/\/+$/, "")}/api/projects/${encodeURIComponent(options.projectId)}/agents/sync`;
239
+ const payload = buildSyncPayload({
240
+ manifest: options.manifest,
241
+ prune: options.prune,
242
+ dryRun: options.dryRun
243
+ });
244
+ const res = await fetchImpl(url, {
245
+ method: "POST",
246
+ headers: resolveDeployHeaders(options),
247
+ body: JSON.stringify(payload)
248
+ });
249
+ let body = null;
250
+ try {
251
+ body = await res.json();
252
+ } catch {
253
+ }
254
+ return { status: res.status, body };
255
+ }
256
+
257
+ // src/pkg.ts
258
+ var import_node_child_process = require("child_process");
259
+ var import_node_fs = require("fs");
260
+ var import_node_path = require("path");
261
+ var NodeMissingError = class extends Error {
262
+ constructor() {
263
+ super(
264
+ "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."
265
+ );
266
+ this.code = "NODE_NOT_INSTALLED";
267
+ this.name = "NodeMissingError";
268
+ }
269
+ };
270
+ var WINDOWS_NODE_DIR = "C:\\Program Files\\nodejs";
271
+ function isNodeAvailableOnWindows(pathEnv = process.env.PATH) {
272
+ if (process.platform !== "win32") return true;
273
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(WINDOWS_NODE_DIR, "npm.cmd"))) return true;
274
+ if (!pathEnv) return false;
275
+ for (const dir of pathEnv.split(";")) {
276
+ if (dir && (0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "npm.cmd"))) return true;
277
+ }
278
+ return false;
279
+ }
280
+ var _unixNodeAvailable = null;
281
+ function isNodeAvailableOnUnix(pathEnv = process.env.PATH) {
282
+ if (process.platform === "win32") return false;
283
+ if (pathEnv === process.env.PATH && _unixNodeAvailable !== null) return _unixNodeAvailable;
284
+ let found = false;
285
+ if (pathEnv) {
286
+ for (const dir of pathEnv.split(":")) {
287
+ if (!dir) continue;
288
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "node"))) {
289
+ found = true;
290
+ break;
291
+ }
292
+ }
293
+ }
294
+ if (pathEnv === process.env.PATH) _unixNodeAvailable = found;
295
+ return found;
296
+ }
297
+ function _resetUnixNodeCache() {
298
+ _unixNodeAvailable = null;
299
+ }
300
+ function resolveBinInvocation(workspaceDir, binName) {
301
+ const binDir = (0, import_node_path.join)(workspaceDir, "node_modules", ".bin");
302
+ const isWindows = process.platform === "win32";
303
+ 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)];
304
+ const shim = candidates.find((p) => (0, import_node_fs.existsSync)(p));
305
+ if (!shim) return null;
306
+ if (isWindows) return { cmd: shim, argsPrefix: [] };
307
+ if (isNodeAvailableOnUnix()) return { cmd: shim, argsPrefix: [] };
308
+ try {
309
+ const st = (0, import_node_fs.lstatSync)(shim);
310
+ if (!st.isSymbolicLink()) return { cmd: shim, argsPrefix: [] };
311
+ const target = (0, import_node_fs.readlinkSync)(shim);
312
+ const jsEntry = (0, import_node_path.isAbsolute)(target) ? target : (0, import_node_path.resolve)((0, import_node_path.dirname)(shim), target);
313
+ if (!(0, import_node_fs.existsSync)(jsEntry)) return { cmd: shim, argsPrefix: [] };
314
+ const bun = process.env.SHOGO_BUN_PATH || "bun";
315
+ return { cmd: bun, argsPrefix: [jsEntry] };
316
+ } catch {
317
+ return { cmd: shim, argsPrefix: [] };
318
+ }
319
+ }
320
+ var IS_WINDOWS = process.platform === "win32";
321
+ var DEFAULT_INSTALL_TIMEOUT = parseInt(
322
+ process.env.SHOGO_INSTALL_TIMEOUT_MS || (IS_WINDOWS ? "300000" : "300000"),
323
+ 10
324
+ );
325
+ var DEFAULT_EXEC_TIMEOUT = 6e4;
326
+ var PlatformPackageManager = class {
327
+ constructor() {
328
+ this.isWindows = IS_WINDOWS;
329
+ }
330
+ /** Resolved path to the bun binary — prefers SHOGO_BUN_PATH (set by desktop app) over bare `bun`. */
331
+ get bunBinary() {
332
+ return process.env.SHOGO_BUN_PATH || "bun";
333
+ }
334
+ shellOpt() {
335
+ return IS_WINDOWS ? true : void 0;
336
+ }
337
+ spawnEnv(base) {
338
+ const source = base ?? process.env;
339
+ const env = {};
340
+ let pathValue = "";
341
+ for (const [k, v] of Object.entries(source)) {
342
+ if (v === void 0) continue;
343
+ if (IS_WINDOWS && k.toLowerCase() === "path") {
344
+ if (v) pathValue = v;
345
+ continue;
346
+ }
347
+ env[k] = v;
348
+ }
349
+ if (IS_WINDOWS) {
350
+ const nodePath = "C:\\Program Files\\nodejs";
351
+ env.PATH = pathValue.includes(nodePath) ? pathValue : `${nodePath};${pathValue}`;
352
+ }
353
+ return env;
354
+ }
355
+ // ---------------------------------------------------------------------------
356
+ // Install — synchronous
357
+ // ---------------------------------------------------------------------------
358
+ installSync(cwd, opts) {
359
+ const timeout = opts?.timeout ?? DEFAULT_INSTALL_TIMEOUT;
360
+ const stdio = opts?.stdio ?? "pipe";
361
+ const env = this.spawnEnv(opts?.env);
362
+ if (IS_WINDOWS) {
363
+ if (isNodeAvailableOnWindows(env.PATH)) {
364
+ try {
365
+ (0, import_node_child_process.execSync)("npm.cmd install --loglevel=error", {
366
+ cwd,
367
+ timeout,
368
+ stdio,
369
+ env,
370
+ shell: "cmd.exe"
371
+ });
372
+ return;
373
+ } catch (err) {
374
+ const wrapped = wrapWindowsNpmError(err);
375
+ if (!wrapped) throw err;
376
+ }
377
+ }
378
+ this.installSyncBunCopyfile(cwd, opts);
379
+ return;
380
+ }
381
+ const bun = this.bunBinary;
382
+ if (opts?.frozen) {
383
+ try {
384
+ (0, import_node_child_process.execSync)(`"${bun}" install --frozen-lockfile 2>&1`, { cwd, timeout, stdio, env });
385
+ return;
386
+ } catch {
387
+ }
388
+ }
389
+ (0, import_node_child_process.execSync)(`"${bun}" install`, { cwd, timeout, stdio, env });
390
+ }
391
+ /**
392
+ * Windows-only fallback path: `bun install [--frozen-lockfile]
393
+ * --backend=copyfile`. Used when npm.cmd isn't available on PATH
394
+ * (typically: Shogo Desktop bundles bun but the user didn't install
395
+ * Node.js separately).
396
+ *
397
+ * The `--backend=copyfile` flag forces bun to copy package contents
398
+ * into `node_modules/` instead of hardlinking from
399
+ * `~/.bun/install/cache`. Hardlinks are bun's default for speed but
400
+ * fail in confusing ways on Windows (the destination directory gets
401
+ * created but ends up empty, and downstream `require('webidl-conversions')`
402
+ * etc. then crash with MODULE_NOT_FOUND). See bun#10327 / #28653.
403
+ */
404
+ installSyncBunCopyfile(cwd, opts) {
405
+ const timeout = opts?.timeout ?? DEFAULT_INSTALL_TIMEOUT;
406
+ const stdio = opts?.stdio ?? "pipe";
407
+ const env = this.spawnEnv(opts?.env);
408
+ const bun = this.bunBinary;
409
+ console.warn(
410
+ "[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."
411
+ );
412
+ if (opts?.frozen) {
413
+ try {
414
+ (0, import_node_child_process.execSync)(`"${bun}" install --frozen-lockfile --backend=copyfile`, {
415
+ cwd,
416
+ timeout,
417
+ stdio,
418
+ env
419
+ });
420
+ return;
421
+ } catch {
422
+ }
423
+ }
424
+ (0, import_node_child_process.execSync)(`"${bun}" install --backend=copyfile`, { cwd, timeout, stdio, env });
425
+ }
426
+ // ---------------------------------------------------------------------------
427
+ // Install — async with spawn (used by RuntimeManager)
428
+ // ---------------------------------------------------------------------------
429
+ installAsync(cwd, opts) {
430
+ if (IS_WINDOWS) {
431
+ const env = this.spawnEnv(opts?.env);
432
+ if (isNodeAvailableOnWindows(env.PATH)) {
433
+ return this.installAsyncWindowsNpm(cwd, opts).catch((err) => {
434
+ if (err instanceof NodeMissingError) {
435
+ return this.installAsyncBunCopyfile(cwd, opts);
436
+ }
437
+ throw err;
438
+ });
439
+ }
440
+ return this.installAsyncBunCopyfile(cwd, opts);
441
+ }
442
+ return this.installAsyncBun(cwd, opts);
443
+ }
444
+ installAsyncWindowsNpm(cwd, opts) {
445
+ const timeout = opts?.timeout ?? DEFAULT_INSTALL_TIMEOUT;
446
+ const env = this.spawnEnv(opts?.env);
447
+ const cmd = "npm.cmd";
448
+ const args = ["install", "--loglevel=error"];
449
+ return new Promise((resolve, reject) => {
450
+ const proc = (0, import_node_child_process.spawn)(cmd, args, {
451
+ cwd,
452
+ stdio: "pipe",
453
+ env,
454
+ shell: true
455
+ });
456
+ const timer = setTimeout(() => {
457
+ proc.kill();
458
+ reject(new Error(`${cmd} install timed out after ${timeout / 1e3}s`));
459
+ }, timeout);
460
+ let stderr = "";
461
+ proc.stderr?.on("data", (d) => {
462
+ stderr += d.toString();
463
+ });
464
+ proc.on("exit", (code) => {
465
+ clearTimeout(timer);
466
+ if (code === 0) return resolve();
467
+ if (/not recognized as an internal or external command/i.test(stderr)) {
468
+ return reject(new NodeMissingError());
469
+ }
470
+ reject(new Error(`${cmd} install exited with code ${code}
471
+ ${stderr}`));
472
+ });
473
+ proc.on("error", (err) => {
474
+ clearTimeout(timer);
475
+ if (err.code === "ENOENT") return reject(new NodeMissingError());
476
+ reject(err);
477
+ });
478
+ });
479
+ }
480
+ installAsyncBunCopyfile(cwd, opts) {
481
+ console.warn(
482
+ "[platform-pkg] Node.js / npm.cmd not found on PATH \u2014 falling back to `bun install --backend=copyfile` (Shogo Desktop bundle path)."
483
+ );
484
+ return this.installAsyncBun(cwd, opts, ["--backend=copyfile"]);
485
+ }
486
+ installAsyncBun(cwd, opts, extraArgs = []) {
487
+ const timeout = opts?.timeout ?? DEFAULT_INSTALL_TIMEOUT;
488
+ const env = this.spawnEnv(opts?.env);
489
+ const bun = this.bunBinary;
490
+ const baseArgs = ["install", ...extraArgs];
491
+ const frozenArgs = opts?.frozen ? ["install", "--frozen-lockfile", ...extraArgs] : null;
492
+ const runOnce = (args) => new Promise((resolve, reject) => {
493
+ const proc = (0, import_node_child_process.spawn)(bun, args, {
494
+ cwd,
495
+ stdio: "pipe",
496
+ env
497
+ });
498
+ const timer = setTimeout(() => {
499
+ proc.kill();
500
+ reject(new Error(`bun ${args.join(" ")} timed out after ${timeout / 1e3}s`));
501
+ }, timeout);
502
+ let stderr = "";
503
+ proc.stderr?.on("data", (d) => {
504
+ stderr += d.toString();
505
+ });
506
+ proc.stdout?.on("data", () => {
507
+ });
508
+ proc.on("exit", (code) => {
509
+ clearTimeout(timer);
510
+ resolve({ ok: code === 0, stderr });
511
+ });
512
+ proc.on("error", (err) => {
513
+ clearTimeout(timer);
514
+ reject(err);
515
+ });
516
+ });
517
+ const run = async () => {
518
+ if (frozenArgs) {
519
+ const first = await runOnce(frozenArgs);
520
+ if (first.ok) return;
521
+ }
522
+ const second = await runOnce(baseArgs);
523
+ if (!second.ok) {
524
+ throw new Error(`bun install failed: ${second.stderr}`);
525
+ }
526
+ };
527
+ return run();
528
+ }
529
+ // ---------------------------------------------------------------------------
530
+ // Exec — run an npx/bunx tool synchronously
531
+ // ---------------------------------------------------------------------------
532
+ execToolSync(tool, args, cwd, opts) {
533
+ const timeout = opts?.timeout ?? DEFAULT_EXEC_TIMEOUT;
534
+ const stdio = opts?.stdio ?? "pipe";
535
+ const env = this.spawnEnv(opts?.env);
536
+ const argStr = args.length > 0 ? ` ${args.join(" ")}` : "";
537
+ const bun = this.bunBinary;
538
+ const cmd = IS_WINDOWS ? `npx ${tool}${argStr}` : `"${bun}" x ${opts?.useBunFlag ? "--bun " : ""}${tool}${argStr}`;
539
+ return (0, import_node_child_process.execSync)(cmd, {
540
+ cwd,
541
+ timeout,
542
+ stdio,
543
+ env,
544
+ // shellOpt() returns `true` on Windows so Node/Bun pick the platform
545
+ // default shell from `process.env.ComSpec`. ExecSyncOptions only
546
+ // narrowly types `shell` as `string | undefined`, but the runtime
547
+ // accepts a boolean and the cross-platform behavior depends on it
548
+ // (see shellOpt() doc).
549
+ shell: this.shellOpt(),
550
+ encoding: "utf-8"
551
+ });
552
+ }
553
+ // ---------------------------------------------------------------------------
554
+ // Exec — async variant that DOES NOT block the JS event loop.
555
+ //
556
+ // The sync variant above is fine for one-shot project scaffolding
557
+ // (template.copy.ts) where blocking is intentional. Inside the runtime's
558
+ // hot path (PreviewManager.runPrismaIfNeeded → /pool/assign) it caused
559
+ // a measured ~4.7s freeze of /pool/assign in staging because prisma's
560
+ // child process held the event loop for the full duration. Always
561
+ // prefer this variant in long-lived services.
562
+ // ---------------------------------------------------------------------------
563
+ async execToolAsync(tool, args, cwd, opts) {
564
+ const timeout = opts?.timeout ?? DEFAULT_EXEC_TIMEOUT;
565
+ const stdio = opts?.stdio ?? "pipe";
566
+ const env = this.spawnEnv(opts?.env);
567
+ let cmd;
568
+ let argv;
569
+ let useShell;
570
+ if (IS_WINDOWS) {
571
+ const argStr = args.length > 0 ? ` ${args.join(" ")}` : "";
572
+ cmd = `npx ${tool}${argStr}`;
573
+ argv = [];
574
+ useShell = true;
575
+ } else {
576
+ cmd = this.bunBinary;
577
+ argv = ["x"];
578
+ if (opts?.useBunFlag) argv.push("--bun");
579
+ argv.push(tool, ...args);
580
+ useShell = false;
581
+ }
582
+ return new Promise((resolvePromise, rejectPromise) => {
583
+ const proc = (0, import_node_child_process.spawn)(cmd, argv, {
584
+ cwd,
585
+ env,
586
+ stdio,
587
+ shell: useShell ? this.shellOpt() : false
588
+ });
589
+ let stdout = "";
590
+ let stderr = "";
591
+ proc.stdout?.on("data", (chunk) => {
592
+ stdout += chunk.toString();
593
+ });
594
+ proc.stderr?.on("data", (chunk) => {
595
+ stderr += chunk.toString();
596
+ });
597
+ const timer = setTimeout(() => {
598
+ try {
599
+ proc.kill("SIGKILL");
600
+ } catch {
601
+ }
602
+ rejectPromise(
603
+ new Error(`${tool} ${args.join(" ")} timed out after ${timeout}ms`)
604
+ );
605
+ }, timeout);
606
+ proc.on("error", (err) => {
607
+ clearTimeout(timer);
608
+ rejectPromise(err);
609
+ });
610
+ proc.on("exit", (code) => {
611
+ clearTimeout(timer);
612
+ if (code === 0) {
613
+ resolvePromise(stdout);
614
+ } else {
615
+ const errMsg = stderr.trim() || stdout.trim() || `exit ${code}`;
616
+ rejectPromise(new Error(`${tool} ${args.join(" ")} failed: ${errMsg}`));
617
+ }
618
+ });
619
+ });
620
+ }
621
+ // ---------------------------------------------------------------------------
622
+ // Prisma convenience wrappers
623
+ // ---------------------------------------------------------------------------
624
+ prismaGenerate(cwd, opts) {
625
+ this.execToolSync("prisma", ["generate"], cwd, {
626
+ timeout: opts?.timeout ?? 3e4,
627
+ stdio: opts?.stdio,
628
+ env: opts?.env
629
+ });
630
+ }
631
+ prismaDbPush(cwd, opts) {
632
+ const args = ["db", "push"];
633
+ if (opts?.acceptDataLoss) args.push("--accept-data-loss");
634
+ this.execToolSync("prisma", args, cwd, {
635
+ timeout: opts?.timeout ?? DEFAULT_EXEC_TIMEOUT,
636
+ stdio: opts?.stdio,
637
+ env: opts?.env
638
+ });
639
+ }
640
+ // Async siblings of the prisma wrappers above. Use these from any
641
+ // long-running service (runtime, agent gateway, preview manager) so a
642
+ // 1-3s prisma child process doesn't freeze /health, /pool/assign, or
643
+ // other concurrent HTTP work. Same arg shape, same timeout defaults.
644
+ async prismaGenerateAsync(cwd, opts) {
645
+ await this.execToolAsync("prisma", ["generate"], cwd, {
646
+ timeout: opts?.timeout ?? 3e4,
647
+ stdio: opts?.stdio,
648
+ env: opts?.env
649
+ });
650
+ }
651
+ async prismaDbPushAsync(cwd, opts) {
652
+ const args = ["db", "push"];
653
+ if (opts?.acceptDataLoss) args.push("--accept-data-loss");
654
+ await this.execToolAsync("prisma", args, cwd, {
655
+ timeout: opts?.timeout ?? DEFAULT_EXEC_TIMEOUT,
656
+ stdio: opts?.stdio,
657
+ env: opts?.env
658
+ });
659
+ }
660
+ };
661
+ function wrapWindowsNpmError(err) {
662
+ if (!IS_WINDOWS) return null;
663
+ const blob = `${err?.stderr?.toString?.() ?? ""}
664
+ ${err?.stdout?.toString?.() ?? ""}
665
+ ${err?.message ?? ""}`;
666
+ if (/not recognized as an internal or external command/i.test(blob)) {
667
+ return new NodeMissingError();
668
+ }
669
+ if (err?.code === "ENOENT") return new NodeMissingError();
670
+ return null;
671
+ }
672
+ var pkg = new PlatformPackageManager();
673
+ // Annotate the CommonJS export names for ESM import in node:
674
+ 0 && (module.exports = {
675
+ AGENT_NAME_PATTERN,
676
+ NodeMissingError,
677
+ PlatformPackageManager,
678
+ _resetUnixNodeCache,
679
+ buildSyncPayload,
680
+ isNodeAvailableOnUnix,
681
+ isNodeAvailableOnWindows,
682
+ normalizeToolEntry,
683
+ pkg,
684
+ resolveBinInvocation,
685
+ runDeploy,
686
+ validateManifest
687
+ });
688
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1,3 @@
1
+ export { AGENT_NAME_PATTERN, AgentManifestEntry, AgentToolEntry, AgentsManifest, BuildSyncPayloadOptions, DeployAuth, RunDeployOptions, RunDeployResult, SyncPayload, ToolDescriptor, ValidatedManifest, ValidationIssue, buildSyncPayload, normalizeToolEntry, runDeploy, validateManifest } from './deploy.cjs';
2
+ export { NodeMissingError, PkgExecOptions, PkgInstallOptions, PlatformPackageManager, _resetUnixNodeCache, isNodeAvailableOnUnix, isNodeAvailableOnWindows, pkg, resolveBinInvocation } from './pkg.cjs';
3
+ import 'node:child_process';
@@ -0,0 +1,3 @@
1
+ export { AGENT_NAME_PATTERN, AgentManifestEntry, AgentToolEntry, AgentsManifest, BuildSyncPayloadOptions, DeployAuth, RunDeployOptions, RunDeployResult, SyncPayload, ToolDescriptor, ValidatedManifest, ValidationIssue, buildSyncPayload, normalizeToolEntry, runDeploy, validateManifest } from './deploy.js';
2
+ export { NodeMissingError, PkgExecOptions, PkgInstallOptions, PlatformPackageManager, _resetUnixNodeCache, isNodeAvailableOnUnix, isNodeAvailableOnWindows, pkg, resolveBinInvocation } from './pkg.js';
3
+ import 'node:child_process';