@playdrop/playdrop-cli 0.12.10 → 0.12.12

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.12.7",
2
+ "version": "0.12.8",
3
3
  "build": 2,
4
4
  "runtimeSdkVersion": "0.12.5",
5
5
  "runtimeSdkBuild": 2,
@@ -1,3 +1,8 @@
1
+ export declare function resolveNpmInstallInvocation(platform?: NodeJS.Platform, nodeExecutable?: string): {
2
+ command: string;
3
+ args: string[];
4
+ };
5
+ export declare function runNpmInstall(projectDir: string): boolean;
1
6
  type CreateOptions = {
2
7
  template?: string;
3
8
  remix?: string;
@@ -3,6 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveNpmInstallInvocation = resolveNpmInstallInvocation;
7
+ exports.runNpmInstall = runNpmInstall;
6
8
  exports.createAssetSpecProject = createAssetSpecProject;
7
9
  exports.create = create;
8
10
  const types_1 = require("@playdrop/types");
@@ -22,6 +24,7 @@ const messages_1 = require("../messages");
22
24
  const loadCheck_1 = require("../apps/loadCheck");
23
25
  const devShared_1 = require("./devShared");
24
26
  const init_1 = require("./init");
27
+ const npmProcess_1 = require("./npmProcess");
25
28
  const taskCaptureSession_1 = require("./taskCaptureSession");
26
29
  const CATALOGUE_FILENAME = 'catalogue.json';
27
30
  const LEGACY_CATALOGUE_VERSION_KEY = ['schema', 'Version'].join('');
@@ -186,9 +189,12 @@ async function promptInstallDependencies(projectDir) {
186
189
  rl.close();
187
190
  return /^y(es)?$/i.test(answer.trim());
188
191
  }
192
+ function resolveNpmInstallInvocation(platform = process.platform, nodeExecutable = process.execPath) {
193
+ return (0, npmProcess_1.resolveNpmInvocation)(['install'], platform, nodeExecutable);
194
+ }
189
195
  function runNpmInstall(projectDir) {
190
- const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
191
- const result = (0, node_child_process_1.spawnSync)(npmCommand, ['install'], {
196
+ const invocation = resolveNpmInstallInvocation();
197
+ const result = (0, node_child_process_1.spawnSync)(invocation.command, invocation.args, {
192
198
  cwd: projectDir,
193
199
  stdio: 'inherit',
194
200
  });
@@ -34,6 +34,7 @@ export declare function buildLocalDevAppUrl(input: {
34
34
  }): string;
35
35
  export declare function terminateChildProcessTree(child: ChildProcess | null, timeoutMs?: number): Promise<void>;
36
36
  export declare function ensureDevRouterRunning(port?: number): Promise<void>;
37
+ export declare function resolveDevRouterWorkingDirectory(cliEntrypoint: string): string;
37
38
  export declare function updateMountedDevRuntimeAssetManifest(input: {
38
39
  creatorUsername: string;
39
40
  appName: string;
@@ -8,6 +8,7 @@ exports.parseMountConflictError = parseMountConflictError;
8
8
  exports.buildLocalDevAppUrl = buildLocalDevAppUrl;
9
9
  exports.terminateChildProcessTree = terminateChildProcessTree;
10
10
  exports.ensureDevRouterRunning = ensureDevRouterRunning;
11
+ exports.resolveDevRouterWorkingDirectory = resolveDevRouterWorkingDirectory;
11
12
  exports.updateMountedDevRuntimeAssetManifest = updateMountedDevRuntimeAssetManifest;
12
13
  exports.startDevServer = startDevServer;
13
14
  exports.isDevServerAvailable = isDevServerAvailable;
@@ -19,6 +20,7 @@ const node_crypto_1 = require("node:crypto");
19
20
  const node_fs_1 = require("node:fs");
20
21
  const node_http_1 = __importDefault(require("node:http"));
21
22
  const node_path_1 = require("node:path");
23
+ const npmProcess_1 = require("./npmProcess");
22
24
  exports.DEV_ROUTER_PORT = 8888;
23
25
  const DEV_ROUTER_HOST = '127.0.0.1';
24
26
  const CONTROL_PREFIX = '/_playdrop';
@@ -248,8 +250,8 @@ function spawnDevScript(projectInfo) {
248
250
  if (!projectInfo.projectDir || !projectInfo.packageJson || typeof projectInfo.packageJson.scripts?.dev !== 'string') {
249
251
  return null;
250
252
  }
251
- const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
252
- const child = (0, node_child_process_1.spawn)(npmCommand, ['run', 'dev'], {
253
+ const npm = (0, npmProcess_1.resolveNpmInvocation)(['run', 'dev']);
254
+ const child = (0, node_child_process_1.spawn)(npm.command, npm.args, {
253
255
  cwd: projectInfo.projectDir,
254
256
  stdio: 'inherit',
255
257
  env: { ...process.env },
@@ -341,25 +343,52 @@ async function waitForProcessGroupExit(pid, timeoutMs) {
341
343
  }
342
344
  return !isProcessGroupAlive(pid);
343
345
  }
346
+ async function terminateWindowsProcessTree(pid, timeoutMs) {
347
+ const windowsRoot = process.env.SystemRoot?.trim() || process.env.WINDIR?.trim() || 'C:\\Windows';
348
+ const taskkillPath = (0, node_path_1.join)(windowsRoot, 'System32', 'taskkill.exe');
349
+ await new Promise((resolvePromise) => {
350
+ let settled = false;
351
+ const taskkill = (0, node_child_process_1.spawn)(taskkillPath, ['/PID', String(pid), '/T', '/F'], {
352
+ stdio: 'ignore',
353
+ windowsHide: true,
354
+ });
355
+ const settle = () => {
356
+ if (settled) {
357
+ return;
358
+ }
359
+ settled = true;
360
+ clearTimeout(timeout);
361
+ resolvePromise();
362
+ };
363
+ const timeout = setTimeout(() => {
364
+ taskkill.kill();
365
+ settle();
366
+ }, timeoutMs);
367
+ timeout.unref?.();
368
+ taskkill.once('error', settle);
369
+ taskkill.once('close', settle);
370
+ });
371
+ }
344
372
  async function terminateChildProcessTree(child, timeoutMs = 2000) {
345
373
  const pid = child?.pid;
346
374
  if (!child || !pid || !isPidAlive(pid)) {
347
375
  return;
348
376
  }
349
- const useGroup = process.platform !== 'win32';
350
- signalProcess(pid, 'SIGTERM', useGroup);
351
- const exited = useGroup
352
- ? await waitForProcessGroupExit(pid, timeoutMs)
353
- : await waitForChildExit(child, timeoutMs);
354
- if (exited) {
377
+ if (process.platform === 'win32') {
378
+ await terminateWindowsProcessTree(pid, timeoutMs);
379
+ if (isPidAlive(pid)) {
380
+ signalProcess(pid, 'SIGKILL', false);
381
+ await waitForChildExit(child, timeoutMs);
382
+ }
355
383
  return;
356
384
  }
357
- signalProcess(pid, 'SIGKILL', useGroup);
358
- if (useGroup) {
359
- await waitForProcessGroupExit(pid, timeoutMs);
385
+ signalProcess(pid, 'SIGTERM', true);
386
+ const exited = await waitForProcessGroupExit(pid, timeoutMs);
387
+ if (exited) {
360
388
  return;
361
389
  }
362
- await waitForChildExit(child, timeoutMs);
390
+ signalProcess(pid, 'SIGKILL', true);
391
+ await waitForProcessGroupExit(pid, timeoutMs);
363
392
  }
364
393
  async function fetchRouterJson(path, init = {}, port = exports.DEV_ROUTER_PORT) {
365
394
  const response = await fetch(`http://${DEV_ROUTER_HOST}:${port}${path}`, init);
@@ -416,6 +445,7 @@ async function ensureDevRouterRunning(port = exports.DEV_ROUTER_PORT) {
416
445
  const childEnv = { ...process.env };
417
446
  delete childEnv.PLAYDROP_WORKER_CONTEXT;
418
447
  const child = (0, node_child_process_1.spawn)(process.execPath, [cliEntrypoint, 'project', '_dev-router', 'serve'], {
448
+ cwd: resolveDevRouterWorkingDirectory(cliEntrypoint),
419
449
  detached: true,
420
450
  stdio: 'ignore',
421
451
  env: {
@@ -434,6 +464,9 @@ async function ensureDevRouterRunning(port = exports.DEV_ROUTER_PORT) {
434
464
  }
435
465
  throw new Error('dev_router_start_failed');
436
466
  }
467
+ function resolveDevRouterWorkingDirectory(cliEntrypoint) {
468
+ return (0, node_path_1.dirname)((0, node_path_1.resolve)(cliEntrypoint));
469
+ }
437
470
  async function registerDevMount(input, port = exports.DEV_ROUTER_PORT) {
438
471
  const response = await fetch(`http://${DEV_ROUTER_HOST}:${port}${CONTROL_PREFIX}/mounts`, {
439
472
  method: 'POST',
@@ -0,0 +1,4 @@
1
+ export declare function resolveNpmInvocation(args: string[], platform?: NodeJS.Platform, nodeExecutable?: string): {
2
+ command: string;
3
+ args: string[];
4
+ };
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveNpmInvocation = resolveNpmInvocation;
7
+ const node_path_1 = require("node:path");
8
+ const node_process_1 = __importDefault(require("node:process"));
9
+ function resolveNpmInvocation(args, platform = node_process_1.default.platform, nodeExecutable = node_process_1.default.execPath) {
10
+ if (platform === 'win32') {
11
+ return {
12
+ command: nodeExecutable,
13
+ args: [
14
+ node_path_1.win32.join(node_path_1.win32.dirname(nodeExecutable), 'node_modules', 'npm', 'bin', 'npm-cli.js'),
15
+ ...args,
16
+ ],
17
+ };
18
+ }
19
+ return { command: 'npm', args };
20
+ }
@@ -266,6 +266,7 @@ export declare function createLocalPlaydropShim(input: {
266
266
  export declare function parseCodexModelCatalog(output: string): string[];
267
267
  export declare function parseClaudeModelCatalog(output: string): string[];
268
268
  export declare function claudeModelProbeReportsSelection(output: string, expectedDisplayName: string): boolean;
269
+ export declare function probeWorkerProviderIndependently<T>(probe: () => T, unavailable: T): T;
269
270
  export declare function buildWorkerSetupActions(input: {
270
271
  degradedReasons: string[];
271
272
  playwright?: {
@@ -301,6 +302,7 @@ export declare function resolveWorkerSupervisorLockFile(input?: {
301
302
  workerHomeDirectory?: string;
302
303
  }): string;
303
304
  export declare function parseLinuxProcessStartIdentity(stat: string): string | null;
305
+ export declare function parseWindowsProcessStartIdentity(output: string): string | null;
304
306
  export declare function removeStaleWorkerSupervisorLockIfUnchanged(file: string, observedHolder: WorkerSupervisorLockOwner): boolean;
305
307
  export declare function acquireWorkerSupervisorLock(input: {
306
308
  supervisorType: WorkerSupervisorType;
@@ -50,10 +50,12 @@ exports.createLocalPlaydropShim = createLocalPlaydropShim;
50
50
  exports.parseCodexModelCatalog = parseCodexModelCatalog;
51
51
  exports.parseClaudeModelCatalog = parseClaudeModelCatalog;
52
52
  exports.claudeModelProbeReportsSelection = claudeModelProbeReportsSelection;
53
+ exports.probeWorkerProviderIndependently = probeWorkerProviderIndependently;
53
54
  exports.buildWorkerSetupActions = buildWorkerSetupActions;
54
55
  exports.resolveWorkerStateDir = resolveWorkerStateDir;
55
56
  exports.resolveWorkerSupervisorLockFile = resolveWorkerSupervisorLockFile;
56
57
  exports.parseLinuxProcessStartIdentity = parseLinuxProcessStartIdentity;
58
+ exports.parseWindowsProcessStartIdentity = parseWindowsProcessStartIdentity;
57
59
  exports.removeStaleWorkerSupervisorLockIfUnchanged = removeStaleWorkerSupervisorLockIfUnchanged;
58
60
  exports.acquireWorkerSupervisorLock = acquireWorkerSupervisorLock;
59
61
  exports.releaseWorkerSupervisorLock = releaseWorkerSupervisorLock;
@@ -2327,18 +2329,35 @@ async function createLocalPlaydropShim(input) {
2327
2329
  const binDir = node_path_1.default.join(input.workspaceDir, 'bin');
2328
2330
  await (0, promises_1.mkdir)(binDir, { recursive: true });
2329
2331
  const cliEntrypoint = node_path_1.default.resolve(node_process_1.default.argv[1] ?? node_path_1.default.join(__dirname, '..', 'index.js'));
2330
- const shimPath = node_path_1.default.join(binDir, 'playdrop');
2331
- await (0, promises_1.writeFile)(shimPath, [
2332
- '#!/bin/sh',
2333
- 'export PLAYDROP_WORKER_CONTEXT=1',
2334
- `export PLAYDROP_WORKER_TASK_ID=${JSON.stringify(String(input.taskId))}`,
2335
- `export PLAYDROP_WORKER_TASK_ATTEMPT=${JSON.stringify(String(input.attempt))}`,
2336
- `export PLAYDROP_WORKER_TASK_DEV_PORT=${JSON.stringify(String(input.devPort))}`,
2337
- `export PLAYDROP_DEV_ROUTER_PORT=${JSON.stringify(String(input.devPort))}`,
2338
- `exec ${JSON.stringify(node_process_1.default.execPath)} ${JSON.stringify(cliEntrypoint)} "$@"`,
2339
- '',
2340
- ].join('\n'), 'utf8');
2341
- await (0, promises_1.chmod)(shimPath, 0o755);
2332
+ const isWindows = node_process_1.default.platform === 'win32';
2333
+ const shimPath = node_path_1.default.join(binDir, isWindows ? 'playdrop.cmd' : 'playdrop');
2334
+ const shim = isWindows
2335
+ ? [
2336
+ '@echo off',
2337
+ 'setlocal',
2338
+ 'set "PLAYDROP_WORKER_CONTEXT=1"',
2339
+ `set "PLAYDROP_WORKER_TASK_ID=${input.taskId}"`,
2340
+ `set "PLAYDROP_WORKER_TASK_ATTEMPT=${input.attempt}"`,
2341
+ `set "PLAYDROP_WORKER_TASK_DEV_PORT=${input.devPort}"`,
2342
+ `set "PLAYDROP_DEV_ROUTER_PORT=${input.devPort}"`,
2343
+ `"${node_process_1.default.execPath}" "${cliEntrypoint}" %*`,
2344
+ 'exit /b %ERRORLEVEL%',
2345
+ '',
2346
+ ]
2347
+ : [
2348
+ '#!/bin/sh',
2349
+ 'export PLAYDROP_WORKER_CONTEXT=1',
2350
+ `export PLAYDROP_WORKER_TASK_ID=${JSON.stringify(String(input.taskId))}`,
2351
+ `export PLAYDROP_WORKER_TASK_ATTEMPT=${JSON.stringify(String(input.attempt))}`,
2352
+ `export PLAYDROP_WORKER_TASK_DEV_PORT=${JSON.stringify(String(input.devPort))}`,
2353
+ `export PLAYDROP_DEV_ROUTER_PORT=${JSON.stringify(String(input.devPort))}`,
2354
+ `exec ${JSON.stringify(node_process_1.default.execPath)} ${JSON.stringify(cliEntrypoint)} "$@"`,
2355
+ '',
2356
+ ];
2357
+ await (0, promises_1.writeFile)(shimPath, shim.join(isWindows ? '\r\n' : '\n'), 'utf8');
2358
+ if (!isWindows) {
2359
+ await (0, promises_1.chmod)(shimPath, 0o755);
2360
+ }
2342
2361
  return binDir;
2343
2362
  }
2344
2363
  function probeCodexInstallation() {
@@ -2485,17 +2504,31 @@ function probeClaudeModels() {
2485
2504
  }
2486
2505
  return normalizeDiscoveredModels('CLAUDE_CODE', discovered);
2487
2506
  }
2507
+ function probeWorkerProviderIndependently(probe, unavailable) {
2508
+ try {
2509
+ return probe();
2510
+ }
2511
+ catch {
2512
+ return unavailable;
2513
+ }
2514
+ }
2488
2515
  function probeReadyWorkerCapabilities(input) {
2489
- const codexVersion = probeCodexInstallation()?.codexVersion ?? null;
2490
- const claudeVersion = probeClaudeInstallation()?.claudeVersion ?? null;
2491
- const cursorVersion = probeCursorInstallation()?.cursorVersion ?? null;
2516
+ const codexVersion = probeWorkerProviderIndependently(() => probeCodexInstallation()?.codexVersion ?? null, null);
2517
+ const claudeVersion = probeWorkerProviderIndependently(() => probeClaudeInstallation()?.claudeVersion ?? null, null);
2518
+ const cursorVersion = probeWorkerProviderIndependently(() => probeCursorInstallation()?.cursorVersion ?? null, null);
2519
+ const codexModels = codexVersion
2520
+ ? probeWorkerProviderIndependently(probeCodexModels, [])
2521
+ : [];
2522
+ const claudeModels = claudeVersion
2523
+ ? probeWorkerProviderIndependently(probeClaudeModels, [])
2524
+ : [];
2492
2525
  const capabilities = buildWorkerCapabilities({
2493
2526
  codexVersion,
2494
2527
  codexAuthenticated: codexVersion ? readCodexAuthenticated() : false,
2495
- codexModels: codexVersion ? probeCodexModels() : [],
2528
+ codexModels,
2496
2529
  claudeVersion,
2497
2530
  claudeAuthenticated: claudeVersion ? readClaudeAuthenticated() : false,
2498
- claudeModels: claudeVersion ? probeClaudeModels() : [],
2531
+ claudeModels,
2499
2532
  cursorVersion,
2500
2533
  cursorAuthenticated: cursorVersion ? readCursorAuthenticated() : false,
2501
2534
  npmVersion: input.npmVersion,
@@ -2719,6 +2752,10 @@ function parseLinuxProcessStartIdentity(stat) {
2719
2752
  const startTimeTicks = fieldsAfterCommand[19];
2720
2753
  return /^\d+$/.test(startTimeTicks ?? '') ? `linux:${startTimeTicks}` : null;
2721
2754
  }
2755
+ function parseWindowsProcessStartIdentity(output) {
2756
+ const ticks = output.split(/\r?\n/).map((line) => line.trim()).find((line) => /^\d{10,}$/.test(line));
2757
+ return /^\d{10,}$/.test(ticks ?? '') ? `win32:${ticks}` : null;
2758
+ }
2722
2759
  function readProcessStartIdentity(pid) {
2723
2760
  if (!Number.isInteger(pid) || pid <= 0) {
2724
2761
  return null;
@@ -2736,6 +2773,12 @@ function readProcessStartIdentity(pid) {
2736
2773
  const identity = result.output.trim().replace(/\s+/g, ' ');
2737
2774
  return result.exitCode === 0 && identity ? identity : null;
2738
2775
  }
2776
+ if (node_process_1.default.platform === 'win32') {
2777
+ const script = `(Get-Process -Id ${pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`;
2778
+ const encodedScript = Buffer.from(script, 'utf16le').toString('base64');
2779
+ const result = (0, shellProbe_1.runShell)(`powershell.exe -NoLogo -NoProfile -NonInteractive -EncodedCommand ${encodedScript}`, 'win32');
2780
+ return result.exitCode === 0 ? parseWindowsProcessStartIdentity(result.output) : null;
2781
+ }
2739
2782
  throw new Error(`worker_supervisor_process_identity_unsupported: ${node_process_1.default.platform}`);
2740
2783
  }
2741
2784
  function readWorkerSupervisorLockOwner(file) {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.12.7",
2
+ "version": "0.12.8",
3
3
  "build": 2,
4
4
  "runtimeSdkVersion": "0.12.5",
5
5
  "runtimeSdkBuild": 2,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playdrop/playdrop-cli",
3
- "version": "0.12.10",
3
+ "version": "0.12.12",
4
4
  "description": "Official Playdrop CLI for publishing browser games, creator apps, and AI-generated game assets on playdrop.ai",
5
5
  "homepage": "https://www.playdrop.ai",
6
6
  "repository": {