@playdrop/playdrop-cli 0.11.8 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/config/client-meta.json +2 -2
  2. package/dist/apps/build.d.ts +1 -1
  3. package/dist/apps/build.js +4 -6
  4. package/dist/apps/index.d.ts +4 -5
  5. package/dist/apps/index.js +14 -16
  6. package/dist/apps/loadCheck.d.ts +76 -0
  7. package/dist/apps/loadCheck.js +874 -0
  8. package/dist/apps/upload.d.ts +3 -2
  9. package/dist/apps/upload.js +112 -9
  10. package/dist/assetSpecs.js +2 -5
  11. package/dist/catalogue-utils.d.ts +1 -0
  12. package/dist/catalogue-utils.js +1 -0
  13. package/dist/catalogue.d.ts +3 -1
  14. package/dist/catalogue.js +19 -3
  15. package/dist/commands/browse.js +2 -2
  16. package/dist/commands/build.js +4 -1
  17. package/dist/commands/captureListing.d.ts +30 -1
  18. package/dist/commands/captureListing.js +308 -125
  19. package/dist/commands/check.d.ts +8 -0
  20. package/dist/commands/check.js +188 -0
  21. package/dist/commands/create.js +17 -15
  22. package/dist/commands/createRemixContent.js +2 -2
  23. package/dist/commands/detail.js +2 -2
  24. package/dist/commands/dev.js +9 -12
  25. package/dist/commands/devServer.d.ts +2 -2
  26. package/dist/commands/devServer.js +3 -4
  27. package/dist/commands/marketing.js +10 -11
  28. package/dist/commands/play.js +2 -2
  29. package/dist/commands/review.d.ts +20 -0
  30. package/dist/commands/review.js +112 -0
  31. package/dist/commands/upload-content.d.ts +1 -1
  32. package/dist/commands/upload-content.js +1 -1
  33. package/dist/commands/upload.d.ts +2 -1
  34. package/dist/commands/upload.js +9 -52
  35. package/dist/commands/validate.js +4 -161
  36. package/dist/commands/worker/runtime.d.ts +1 -0
  37. package/dist/commands/worker/runtime.js +48 -5
  38. package/dist/commands/worker.d.ts +5 -6
  39. package/dist/commands/worker.js +42 -92
  40. package/dist/config.js +1 -2
  41. package/dist/index.js +33 -49
  42. package/dist/playwright.d.ts +5 -0
  43. package/dist/playwright.js +60 -1
  44. package/node_modules/@playdrop/api-client/dist/client.d.ts +5 -3
  45. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  46. package/node_modules/@playdrop/api-client/dist/client.js +10 -27
  47. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +1 -1
  48. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
  49. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +2 -1
  50. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
  51. package/node_modules/@playdrop/api-client/dist/domains/apps.js +11 -0
  52. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +1 -1
  53. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
  54. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +3 -1
  55. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -1
  56. package/node_modules/@playdrop/api-client/dist/domains/auth.js +22 -0
  57. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +1 -1
  58. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
  59. package/node_modules/@playdrop/api-client/dist/index.d.ts +7 -6
  60. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  61. package/node_modules/@playdrop/api-client/dist/index.js +11 -6
  62. package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js +0 -7
  63. package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js.map +1 -1
  64. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js +2 -46
  65. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js.map +1 -1
  66. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js +2 -22
  67. package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js.map +1 -1
  68. package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.js.map +1 -1
  69. package/node_modules/@playdrop/boxel-core/dist/src/serialization.js +1 -1
  70. package/node_modules/@playdrop/boxel-core/dist/src/serialization.js.map +1 -1
  71. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.js.map +1 -1
  72. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js +2 -2
  73. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js.map +1 -1
  74. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js +0 -11
  75. package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js.map +1 -1
  76. package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.js.map +1 -1
  77. package/node_modules/@playdrop/boxel-core/dist/src/validation.d.ts +1 -1
  78. package/node_modules/@playdrop/boxel-core/dist/src/validation.js +0 -20
  79. package/node_modules/@playdrop/boxel-core/dist/src/validation.js.map +1 -1
  80. package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.d.ts +2 -2
  81. package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.js +2 -12
  82. package/node_modules/@playdrop/boxel-three/dist/src/voxels/faces.js +1 -125
  83. package/node_modules/@playdrop/config/client-meta.json +2 -2
  84. package/node_modules/@playdrop/config/dist/src/client-meta.d.ts +8 -0
  85. package/node_modules/@playdrop/config/dist/src/client-meta.d.ts.map +1 -0
  86. package/node_modules/@playdrop/config/dist/src/client-meta.js +51 -0
  87. package/node_modules/@playdrop/config/dist/src/deployment.d.ts.map +1 -1
  88. package/node_modules/@playdrop/config/dist/src/deployment.js +2 -32
  89. package/node_modules/@playdrop/config/dist/src/index.d.ts +2 -2
  90. package/node_modules/@playdrop/config/dist/src/index.d.ts.map +1 -1
  91. package/node_modules/@playdrop/config/dist/src/index.js +3 -29
  92. package/node_modules/@playdrop/config/dist/src/runtime-service-origins.d.ts +0 -1
  93. package/node_modules/@playdrop/config/dist/src/runtime-service-origins.d.ts.map +1 -1
  94. package/node_modules/@playdrop/config/dist/src/runtime-service-origins.js +21 -5
  95. package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts +2 -2
  96. package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts.map +1 -1
  97. package/node_modules/@playdrop/config/dist/src/server/fastify.js +0 -1
  98. package/node_modules/@playdrop/config/dist/test/runtime-service-origins.test.js +8 -0
  99. package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
  100. package/node_modules/@playdrop/types/dist/api.d.ts +25 -24
  101. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  102. package/node_modules/@playdrop/types/dist/api.js +5 -10
  103. package/node_modules/@playdrop/types/dist/version.d.ts +14 -0
  104. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  105. package/package.json +1 -1
  106. package/dist/apps/launchCheck.d.ts +0 -39
  107. package/dist/apps/launchCheck.js +0 -414
  108. package/dist/captureRuntime.d.ts +0 -73
  109. package/dist/captureRuntime.js +0 -1021
  110. package/dist/commands/capture.d.ts +0 -14
  111. package/dist/commands/capture.js +0 -550
  112. package/dist/commands/captureRemote.d.ts +0 -17
  113. package/dist/commands/captureRemote.js +0 -394
  114. package/node_modules/@playdrop/types/dist/entity.d.ts +0 -31
  115. package/node_modules/@playdrop/types/dist/entity.d.ts.map +0 -1
  116. package/node_modules/@playdrop/types/dist/entity.js +0 -67
@@ -3,7 +3,7 @@ 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.WORKER_CONTEXT_COMMAND_NOT_ALLOWED_MESSAGE = exports.WORKER_SESSION_EXPIRED_MESSAGE = exports.runLoggedProcess = exports.readCodexRolloutTokenUsageForThread = exports.readCodexSandboxMode = exports.readEnvBoolean = exports.extractCodexTokensUsed = exports.extractCodexThreadId = exports.extractCodexRolloutTokenUsage = exports.extractAgentTokenUsage = exports.buildWorkerChildEnv = exports.buildClaudePermissionSettings = exports.buildClaudeExecArgs = exports.buildCodexExecArgs = exports.assertWorkerTokenUsageWithinCap = exports.DEFAULT_WORKER_TOKEN_CAP = exports.DEFAULT_CODEX_TIMEOUT_MS = void 0;
6
+ exports.WORKER_CONTEXT_COMMAND_NOT_ALLOWED_MESSAGE = exports.WORKER_SESSION_EXPIRED_MESSAGE = exports.runLoggedProcess = exports.readEnvBoolean = exports.readCodexSandboxMode = exports.readCodexRolloutTokenUsageForThread = exports.extractCodexTokensUsed = exports.extractCodexThreadId = exports.extractCodexRolloutTokenUsage = exports.extractAgentTokenUsage = exports.DEFAULT_WORKER_TOKEN_CAP = exports.DEFAULT_CODEX_TIMEOUT_MS = exports.buildWorkerChildEnv = exports.buildCodexExecArgs = exports.buildClaudePermissionSettings = exports.buildClaudeExecArgs = exports.assertWorkerTokenUsageWithinCap = void 0;
7
7
  exports.allocateWorkerDevPort = allocateWorkerDevPort;
8
8
  exports.isWorkerContextCommandAllowed = isWorkerContextCommandAllowed;
9
9
  exports.resolveWorkerExecutionTargetFromRole = resolveWorkerExecutionTargetFromRole;
@@ -55,40 +55,40 @@ exports.readReviewEvidenceFiles = readReviewEvidenceFiles;
55
55
  exports.submitReviewTask = submitReviewTask;
56
56
  exports.submitEvalTask = submitEvalTask;
57
57
  exports.failTask = failTask;
58
- const promises_1 = require("node:fs/promises");
58
+ const types_1 = require("@playdrop/types");
59
+ const fflate_1 = require("fflate");
60
+ const node_crypto_1 = require("node:crypto");
59
61
  const node_fs_1 = require("node:fs");
62
+ const promises_1 = require("node:fs/promises");
63
+ const node_module_1 = require("node:module");
60
64
  const node_os_1 = __importDefault(require("node:os"));
61
65
  const node_path_1 = __importDefault(require("node:path"));
62
66
  const node_process_1 = __importDefault(require("node:process"));
63
- const node_module_1 = require("node:module");
64
- const node_crypto_1 = require("node:crypto");
65
- const fflate_1 = require("fflate");
66
- const types_1 = require("@playdrop/types");
67
+ const clientInfo_1 = require("../clientInfo");
67
68
  const commandContext_1 = require("../commandContext");
68
69
  const config_1 = require("../config");
69
- const clientInfo_1 = require("../clientInfo");
70
70
  const messages_1 = require("../messages");
71
71
  const output_1 = require("../output");
72
72
  const shellProbe_1 = require("../shellProbe");
73
- const upload_1 = require("./upload");
74
- const review_1 = require("./review");
75
73
  const agents_1 = require("./agents");
74
+ const review_1 = require("./review");
75
+ const upload_1 = require("./upload");
76
76
  const runtime_1 = require("./worker/runtime");
77
77
  var runtime_2 = require("./worker/runtime");
78
- Object.defineProperty(exports, "DEFAULT_CODEX_TIMEOUT_MS", { enumerable: true, get: function () { return runtime_2.DEFAULT_CODEX_TIMEOUT_MS; } });
79
- Object.defineProperty(exports, "DEFAULT_WORKER_TOKEN_CAP", { enumerable: true, get: function () { return runtime_2.DEFAULT_WORKER_TOKEN_CAP; } });
80
78
  Object.defineProperty(exports, "assertWorkerTokenUsageWithinCap", { enumerable: true, get: function () { return runtime_2.assertWorkerTokenUsageWithinCap; } });
81
- Object.defineProperty(exports, "buildCodexExecArgs", { enumerable: true, get: function () { return runtime_2.buildCodexExecArgs; } });
82
79
  Object.defineProperty(exports, "buildClaudeExecArgs", { enumerable: true, get: function () { return runtime_2.buildClaudeExecArgs; } });
83
80
  Object.defineProperty(exports, "buildClaudePermissionSettings", { enumerable: true, get: function () { return runtime_2.buildClaudePermissionSettings; } });
81
+ Object.defineProperty(exports, "buildCodexExecArgs", { enumerable: true, get: function () { return runtime_2.buildCodexExecArgs; } });
84
82
  Object.defineProperty(exports, "buildWorkerChildEnv", { enumerable: true, get: function () { return runtime_2.buildWorkerChildEnv; } });
83
+ Object.defineProperty(exports, "DEFAULT_CODEX_TIMEOUT_MS", { enumerable: true, get: function () { return runtime_2.DEFAULT_CODEX_TIMEOUT_MS; } });
84
+ Object.defineProperty(exports, "DEFAULT_WORKER_TOKEN_CAP", { enumerable: true, get: function () { return runtime_2.DEFAULT_WORKER_TOKEN_CAP; } });
85
85
  Object.defineProperty(exports, "extractAgentTokenUsage", { enumerable: true, get: function () { return runtime_2.extractAgentTokenUsage; } });
86
86
  Object.defineProperty(exports, "extractCodexRolloutTokenUsage", { enumerable: true, get: function () { return runtime_2.extractCodexRolloutTokenUsage; } });
87
87
  Object.defineProperty(exports, "extractCodexThreadId", { enumerable: true, get: function () { return runtime_2.extractCodexThreadId; } });
88
88
  Object.defineProperty(exports, "extractCodexTokensUsed", { enumerable: true, get: function () { return runtime_2.extractCodexTokensUsed; } });
89
- Object.defineProperty(exports, "readEnvBoolean", { enumerable: true, get: function () { return runtime_2.readEnvBoolean; } });
90
- Object.defineProperty(exports, "readCodexSandboxMode", { enumerable: true, get: function () { return runtime_2.readCodexSandboxMode; } });
91
89
  Object.defineProperty(exports, "readCodexRolloutTokenUsageForThread", { enumerable: true, get: function () { return runtime_2.readCodexRolloutTokenUsageForThread; } });
90
+ Object.defineProperty(exports, "readCodexSandboxMode", { enumerable: true, get: function () { return runtime_2.readCodexSandboxMode; } });
91
+ Object.defineProperty(exports, "readEnvBoolean", { enumerable: true, get: function () { return runtime_2.readEnvBoolean; } });
92
92
  Object.defineProperty(exports, "runLoggedProcess", { enumerable: true, get: function () { return runtime_2.runLoggedProcess; } });
93
93
  const DEFAULT_POLL_INTERVAL_MS = 1000;
94
94
  const HEARTBEAT_INTERVAL_MS = 10000;
@@ -120,11 +120,14 @@ const WORKER_CONTEXT_ALLOWED_COMMANDS = [
120
120
  ['task', 'submit-eval'],
121
121
  ['task', 'fail'],
122
122
  ['review', 'validate-result'],
123
+ ['review', 'list-windows'],
124
+ ['review', 'screenshot'],
123
125
  ['review', 'compose-evidence'],
124
126
  ['review', 'rating-card'],
125
127
  ['project', 'validate'],
126
128
  ['project', 'build'],
127
129
  ['project', 'dev'],
130
+ ['project', 'check'],
128
131
  ['project', 'capture'],
129
132
  ['browse'],
130
133
  ['search'],
@@ -138,7 +141,7 @@ const WORKER_CONTEXT_ALLOWED_COMMANDS = [
138
141
  ['ai', 'jobs', 'detail'],
139
142
  ['ai', 'generations'],
140
143
  ];
141
- exports.WORKER_CONTEXT_COMMAND_NOT_ALLOWED_MESSAGE = 'worker_context_command_not_allowed: inside a PlayDrop worker task only task progress, task claim-slug, task upload/done/fail, assigned app scaffolding, project validation/build/dev/capture, read-only help/catalogue/documentation lookup, and PlayDrop AI generation are permitted.';
144
+ exports.WORKER_CONTEXT_COMMAND_NOT_ALLOWED_MESSAGE = 'worker_context_command_not_allowed: inside a PlayDrop worker task only task progress, task claim-slug, task upload/done/fail, assigned app scaffolding, project validation/build/dev/check/capture, read-only help/catalogue/documentation lookup, and PlayDrop AI generation are permitted.';
142
145
  function allocateWorkerDevPort(input) {
143
146
  const basePort = input.basePort ?? DEFAULT_WORKER_DEV_PORT_BASE;
144
147
  const maxParallelTasks = input.maxParallelTasks ?? DEFAULT_WORKER_MAX_PARALLEL_TASKS;
@@ -727,15 +730,6 @@ async function readTaskNextStepsFileForCommand(input) {
727
730
  return NEXT_STEPS_READ_FAILED;
728
731
  }
729
732
  }
730
- function isFinalOwnerRouteLaunchCheckFailure(error) {
731
- const message = error instanceof Error ? error.message : String(error);
732
- return message.startsWith('hosted_launch_check_failed:')
733
- && message.includes('final owner play route');
734
- }
735
- function formatTerminalFinalOwnerRouteFailure(error) {
736
- const message = error instanceof Error ? error.message : String(error);
737
- return `agent_task_final_owner_route_failed: final owner play route validation failed after the task version was created. Task uploads have a fixed outputVersion and cannot be retried by bumping catalogue.json. ${message}`;
738
- }
739
733
  function assertTaskUploadResultMatchesContext(input) {
740
734
  if (input.uploadResult.taskId !== input.taskContext.taskId) {
741
735
  throw new Error('task_upload_result_task_mismatch');
@@ -1077,11 +1071,6 @@ function discoverWorkerProjectRoot(workspaceDir) {
1077
1071
  }
1078
1072
  return roots[0];
1079
1073
  }
1080
- function writeWorkerTaskState(state) {
1081
- const file = workerStateFilePath();
1082
- (0, node_fs_1.mkdirSync)(node_path_1.default.dirname(file), { recursive: true });
1083
- (0, node_fs_1.writeFileSync)(file, JSON.stringify(state, null, 2));
1084
- }
1085
1074
  function readWorkerTaskState() {
1086
1075
  const file = workerStateFilePath();
1087
1076
  let raw;
@@ -1097,9 +1086,6 @@ function readWorkerTaskState() {
1097
1086
  }
1098
1087
  return parsed;
1099
1088
  }
1100
- function clearWorkerTaskState() {
1101
- (0, node_fs_1.rmSync)(workerStateFilePath(), { force: true });
1102
- }
1103
1089
  function isWorkerSlackAlertEnabled() {
1104
1090
  const normalized = node_process_1.default.env.PLAYDROP_WORKER_SLACK_ALERTS?.trim().toLowerCase();
1105
1091
  return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
@@ -1408,7 +1394,6 @@ function buildWorkerCapabilities(input) {
1408
1394
  ? DEFAULT_WORKER_MAX_PARALLEL_TASKS
1409
1395
  : input.maxParallelTasks ?? DEFAULT_WORKER_MAX_PARALLEL_TASKS;
1410
1396
  const runningTaskCount = typeof input === 'string' ? 0 : input.runningTaskCount ?? 0;
1411
- const providerAccountLabel = typeof input === 'string' ? null : input.providerAccountLabel?.trim() || null;
1412
1397
  const degradedReasons = [];
1413
1398
  const agents = [];
1414
1399
  if (codexVersion) {
@@ -1461,7 +1446,6 @@ function buildWorkerCapabilities(input) {
1461
1446
  agents,
1462
1447
  ready: agents.some((agent) => agent.ready === true),
1463
1448
  degradedReasons,
1464
- ...(providerAccountLabel ? { providerAccountLabel } : {}),
1465
1449
  wrapperContractVersion: (0, runtime_1.readPositiveEnvInt)('PLAYDROP_WORKER_WRAPPER_CONTRACT_VERSION', WORKER_WRAPPER_CONTRACT_VERSION),
1466
1450
  ...(npmVersion ? { npmVersion } : {}),
1467
1451
  ...(playwright ? { playwright } : {}),
@@ -1861,6 +1845,7 @@ async function runClaude(input) {
1861
1845
  workspaceDir: input.workspaceDir,
1862
1846
  denyReadRoots,
1863
1847
  pluginDir: input.playdropPluginRoot,
1848
+ chrome: input.enableChrome,
1864
1849
  }),
1865
1850
  cwd: input.workspaceDir,
1866
1851
  env: (0, runtime_1.buildWorkerChildEnv)({
@@ -1957,13 +1942,6 @@ function collectObservedPlaydropSkillPaths(output, availablePaths) {
1957
1942
  }
1958
1943
  return [...observed].sort();
1959
1944
  }
1960
- function readProviderAccountLabel(target) {
1961
- const label = node_process_1.default.env.PLAYDROP_WORKER_AGENT_ACCOUNT_LABEL?.trim() || null;
1962
- if (target === 'FIRST_PARTY' && !label) {
1963
- throw new Error('missing_playdrop_worker_agent_account_label');
1964
- }
1965
- return label;
1966
- }
1967
1945
  function resolveTelemetryStatus(value) {
1968
1946
  if (value === 'QUEUED') {
1969
1947
  throw new Error('invalid_agent_task_run_status');
@@ -1983,7 +1961,6 @@ async function recordAgentRunTelemetry(input) {
1983
1961
  requestedModel: input.assignment.model,
1984
1962
  resolvedRuntimeModel: input.resolvedRuntimeModel,
1985
1963
  reasoningEffort: input.reasoningEffort,
1986
- providerAccountLabel: input.providerAccountLabel,
1987
1964
  status: resolveTelemetryStatus(input.status),
1988
1965
  exitCode: input.result.exitCode,
1989
1966
  signal: input.result.signal,
@@ -2264,7 +2241,6 @@ function collectWorkerLocalStatus() {
2264
2241
  playwright,
2265
2242
  runningTaskCount: 0,
2266
2243
  maxParallelTasks: (0, runtime_1.readPositiveEnvInt)('PLAYDROP_WORKER_MAX_PARALLEL_TASKS', DEFAULT_WORKER_MAX_PARALLEL_TASKS),
2267
- providerAccountLabel: node_process_1.default.env.PLAYDROP_WORKER_AGENT_ACCOUNT_LABEL?.trim() || null,
2268
2244
  });
2269
2245
  if (pluginVersion) {
2270
2246
  capabilities.pluginVersion = pluginVersion;
@@ -2418,13 +2394,6 @@ function writeManagedWorkerUpdatePolicyFile(input) {
2418
2394
  function shellQuote(value) {
2419
2395
  return `'${value.replace(/'/g, `'\\''`)}'`;
2420
2396
  }
2421
- function validatePackageVersion(value, label) {
2422
- const version = typeof value === 'string' ? value.trim() : '';
2423
- if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(version)) {
2424
- throw new Error(`${label}_invalid:${version || 'missing'}`);
2425
- }
2426
- return version;
2427
- }
2428
2397
  function verifyNpmGlobalInstallPreconditions() {
2429
2398
  const prefixProbe = (0, shellProbe_1.runShell)('npm prefix -g');
2430
2399
  const prefix = prefixProbe.output.trim().split(/\r?\n/)[0]?.trim() ?? '';
@@ -3108,6 +3077,9 @@ async function fetchTaskDetail(client, target, taskId) {
3108
3077
  }
3109
3078
  return await client.getAgentTask(taskId);
3110
3079
  }
3080
+ function throwWorkerRuntimeError(error) {
3081
+ throw error;
3082
+ }
3111
3083
  async function startWorker(options = {}) {
3112
3084
  loadWorkerEnvFile(options.env);
3113
3085
  await (0, commandContext_1.withEnvironment)('worker start', 'Starting the PlayDrop worker', async ({ client, env }) => {
@@ -3126,7 +3098,6 @@ async function startWorker(options = {}) {
3126
3098
  throw new Error('worker_session_invalid: the stored session did not resolve to a user. Run "playdrop auth login" and retry.');
3127
3099
  }
3128
3100
  const target = resolveWorkerExecutionTargetFromRole(me.user.role);
3129
- const providerAccountLabel = readProviderAccountLabel(target);
3130
3101
  const codexVersion = probeCodexInstallation()?.codexVersion ?? null;
3131
3102
  const claudeVersion = probeClaudeInstallation()?.claudeVersion ?? null;
3132
3103
  const cursorVersion = probeCursorInstallation()?.cursorVersion ?? null;
@@ -3151,7 +3122,6 @@ async function startWorker(options = {}) {
3151
3122
  playwright,
3152
3123
  maxParallelTasks,
3153
3124
  runningTaskCount: 0,
3154
- providerAccountLabel,
3155
3125
  });
3156
3126
  if (playdropPluginVersion) {
3157
3127
  capabilities.pluginVersion = playdropPluginVersion;
@@ -3305,7 +3275,6 @@ async function startWorker(options = {}) {
3305
3275
  let availableSkillPaths = [];
3306
3276
  const observedSkillPaths = new Set();
3307
3277
  let assignment = null;
3308
- let providerAccountLabel = null;
3309
3278
  let resolvedRuntimeModel = task.model;
3310
3279
  let reasoningEffort = null;
3311
3280
  let agentStartedAt = null;
@@ -3328,7 +3297,6 @@ async function startWorker(options = {}) {
3328
3297
  status,
3329
3298
  resolvedRuntimeModel,
3330
3299
  reasoningEffort,
3331
- providerAccountLabel,
3332
3300
  availableSkillPaths,
3333
3301
  observedSkillPaths: [...observedSkillPaths],
3334
3302
  startedAt: agentStartedAt ?? completedAt,
@@ -3341,7 +3309,6 @@ async function startWorker(options = {}) {
3341
3309
  if (!assignment) {
3342
3310
  throw new Error('agent_task_claim_missing_task_assignment');
3343
3311
  }
3344
- providerAccountLabel = readProviderAccountLabel(assignment.target);
3345
3312
  const codexModel = assignment.agent === 'CODEX' ? resolveCodexModel(assignment.model) : null;
3346
3313
  const claudeModel = assignment.agent === 'CLAUDE_CODE' ? resolveClaudeModel(assignment.model) : null;
3347
3314
  resolvedRuntimeModel = codexModel?.model ?? claudeModel?.model ?? assignment.model;
@@ -3557,6 +3524,7 @@ async function startWorker(options = {}) {
3557
3524
  binDir,
3558
3525
  eventDir,
3559
3526
  prompt,
3527
+ enableChrome: task.kind === 'GAME_REVIEW' || task.kind === 'GAME_EVAL',
3560
3528
  envName: env,
3561
3529
  taskId: task.id,
3562
3530
  attempt: taskContext.attempt,
@@ -3786,7 +3754,7 @@ async function startWorker(options = {}) {
3786
3754
  let claimBackoffMs = CLAIM_BACKOFF_BASE_MS;
3787
3755
  while (!shuttingDown && !quiescingForUpdate) {
3788
3756
  if (fatalTaskError) {
3789
- throw fatalTaskError;
3757
+ throwWorkerRuntimeError(fatalTaskError);
3790
3758
  }
3791
3759
  if (sessionExpired) {
3792
3760
  throw new Error(exports.WORKER_SESSION_EXPIRED_MESSAGE);
@@ -3845,7 +3813,7 @@ async function startWorker(options = {}) {
3845
3813
  if (options.once) {
3846
3814
  await taskRun;
3847
3815
  if (fatalTaskError) {
3848
- throw fatalTaskError;
3816
+ throwWorkerRuntimeError(fatalTaskError);
3849
3817
  }
3850
3818
  return;
3851
3819
  }
@@ -3854,7 +3822,7 @@ async function startWorker(options = {}) {
3854
3822
  await Promise.allSettled(Array.from(activeTaskRuns.values()));
3855
3823
  }
3856
3824
  if (fatalTaskError) {
3857
- throw fatalTaskError;
3825
+ throwWorkerRuntimeError(fatalTaskError);
3858
3826
  }
3859
3827
  if (sessionExpired) {
3860
3828
  throw new Error(exports.WORKER_SESSION_EXPIRED_MESSAGE);
@@ -3994,40 +3962,22 @@ async function uploadTask(options = {}) {
3994
3962
  return;
3995
3963
  }
3996
3964
  const projectDir = discoverWorkerProjectRoot(node_process_1.default.cwd());
3997
- let published;
3998
- try {
3999
- published = await (0, upload_1.publishWorkerAppProject)({
4000
- client: ctx.client,
4001
- taskId: taskContext.taskId,
4002
- kind: taskContext.kind,
4003
- expectedAppName: taskContext.outputAppName ?? undefined,
4004
- remixSourceRef: taskContext.remixSourceRef ?? null,
4005
- playdropAssetRequirement: resolvePlaydropAssetRequirementFromTaskRequest(taskContext.creatorRequest),
4006
- creatorRequest: taskContext.creatorRequest,
4007
- projectDir,
4008
- creatorUsername: taskContext.creatorUsername,
4009
- apiBase: ctx.envConfig.apiBase,
4010
- webBase: ctx.envConfig.webBase ?? null,
4011
- token: ctx.token,
4012
- user: ctx.user,
4013
- });
4014
- }
4015
- catch (error) {
4016
- if (isFinalOwnerRouteLaunchCheckFailure(error)) {
4017
- const terminalError = formatTerminalFinalOwnerRouteFailure(error);
4018
- await ctx.client.workerFailAgentTask(taskContext.taskId, {
4019
- taskToken: taskContext.taskToken,
4020
- error: terminalError,
4021
- result: {
4022
- failedBy: 'worker_upload_final_owner_route',
4023
- finalOwnerRouteFailure: true,
4024
- originalError: error instanceof Error ? error.message : String(error),
4025
- },
4026
- });
4027
- throw new Error(terminalError);
4028
- }
4029
- throw error;
4030
- }
3965
+ const published = await (0, upload_1.publishWorkerAppProject)({
3966
+ client: ctx.client,
3967
+ taskId: taskContext.taskId,
3968
+ kind: taskContext.kind,
3969
+ executionTarget: taskContext.target,
3970
+ expectedAppName: taskContext.outputAppName ?? undefined,
3971
+ remixSourceRef: taskContext.remixSourceRef ?? null,
3972
+ playdropAssetRequirement: resolvePlaydropAssetRequirementFromTaskRequest(taskContext.creatorRequest),
3973
+ creatorRequest: taskContext.creatorRequest,
3974
+ projectDir,
3975
+ creatorUsername: taskContext.creatorUsername,
3976
+ apiBase: ctx.envConfig.apiBase,
3977
+ webBase: ctx.envConfig.webBase ?? null,
3978
+ token: ctx.token,
3979
+ user: ctx.user,
3980
+ });
4031
3981
  const result = {
4032
3982
  taskId: taskContext.taskId,
4033
3983
  appId: published.appId,
package/dist/config.js CHANGED
@@ -18,8 +18,8 @@ exports.setCurrentAccount = setCurrentAccount;
18
18
  exports.removeAccountSession = removeAccountSession;
19
19
  const crypto_1 = require("crypto");
20
20
  const fs_1 = require("fs");
21
- const path_1 = require("path");
22
21
  const os_1 = __importDefault(require("os"));
22
+ const path_1 = require("path");
23
23
  const CONFIG_VERSION = 2;
24
24
  function getConfigPath() {
25
25
  const custom = process.env.PLAYDROP_CONFIG_PATH;
@@ -225,7 +225,6 @@ function setCurrentAccount(input) {
225
225
  saveConfig(next);
226
226
  return loadConfig();
227
227
  }
228
- // eslint-disable-next-line complexity
229
228
  function removeAccountSession(input = {}) {
230
229
  const cfg = loadConfig();
231
230
  const current = getCurrentAccountSession(cfg);
package/dist/index.js CHANGED
@@ -30,10 +30,9 @@ const create_1 = require("./commands/create");
30
30
  const createRemixContent_1 = require("./commands/createRemixContent");
31
31
  const dev_1 = require("./commands/dev");
32
32
  const devBrowser_1 = require("./commands/devBrowser");
33
- const capture_1 = require("./commands/capture");
33
+ const check_1 = require("./commands/check");
34
34
  const devServer_1 = require("./commands/devServer");
35
35
  const captureListing_1 = require("./commands/captureListing");
36
- const captureRemote_1 = require("./commands/captureRemote");
37
36
  const marketing_1 = require("./commands/marketing");
38
37
  const review_1 = require("./commands/review");
39
38
  const validate_1 = require("./commands/validate");
@@ -290,6 +289,23 @@ task
290
289
  await (0, worker_1.failTask)(opts);
291
290
  });
292
291
  const review = program.command('review').description('Review helper commands for PlayDrop worker tasks');
292
+ review
293
+ .command('list-windows')
294
+ .description('List native capture windows for review evidence')
295
+ .requiredOption('--pid <pid>', 'Browser process id')
296
+ .action(async (opts) => {
297
+ await (0, review_1.listReviewCaptureWindows)(opts);
298
+ });
299
+ review
300
+ .command('screenshot')
301
+ .description('Capture a native screenshot for review evidence')
302
+ .requiredOption('--pid <pid>', 'Browser process id')
303
+ .option('--window-id <id>', 'Native capture window id')
304
+ .requiredOption('--out <path>', 'Output PNG path')
305
+ .option('--metadata <path>', 'Output metadata JSON path')
306
+ .action(async (opts) => {
307
+ await (0, review_1.captureReviewScreenshot)(opts);
308
+ });
293
309
  review
294
310
  .command('validate-result')
295
311
  .description('Validate a canonical game review result')
@@ -986,37 +1002,24 @@ project
986
1002
  player: opts.player,
987
1003
  });
988
1004
  });
989
- const projectCapture = project.command('capture').description('Run a Playwright capture');
990
- projectCapture
991
- .argument('[target]')
1005
+ project
1006
+ .command('check [target]')
1007
+ .description('Run the deterministic hosted game load-check in installed Chrome')
992
1008
  .option('--app <name>', 'App name when the target is a workspace')
993
- .option('--timeout <seconds>', 'Capture timeout in seconds')
994
- .option('--log-level <level>', 'debug, info, warn, or error')
1009
+ .option('--timeout <seconds>', 'Load-check timeout in seconds')
995
1010
  .option('--screenshot <path>', 'Screenshot output path')
996
- .option('--surface <surface>', 'desktop, mobile-landscape, or mobile-portrait')
997
- .option('--dev-auth <mode>', 'anonymous, viewer, or player')
998
- .option('--player <slot>', '1, 2, 3, or 4 when --dev-auth player is selected')
999
- .option('--reset-dev-player <mode>', 'before, after, before-and-after, or never')
1000
- .option('--username <username>', 'Override saved login username for this run')
1001
- .option('--password <password>', 'Override saved login password for this run')
1002
- .action(async (target, opts) => {
1003
- const timeoutSeconds = opts.timeout !== undefined ? Number.parseFloat(String(opts.timeout)) : undefined;
1004
- await (0, capture_1.capture)(target, {
1005
- timeoutSeconds,
1006
- appName: opts.app,
1007
- logLevel: opts.logLevel,
1008
- screenshotPath: opts.screenshot,
1009
- surfaceTarget: opts.surface,
1010
- devAuth: opts.devAuth,
1011
- player: opts.player,
1012
- resetDevPlayer: opts.resetDevPlayer,
1013
- username: opts.username,
1014
- password: opts.password,
1011
+ .option('--actions <path>', 'JSON action script to dispatch after the game frame is ready')
1012
+ .action(async (target, opts = {}) => {
1013
+ await (0, check_1.check)(target, {
1014
+ app: opts.app,
1015
+ timeout: opts.timeout,
1016
+ screenshot: opts.screenshot,
1017
+ actions: opts.actions,
1015
1018
  });
1016
1019
  });
1020
+ const projectCapture = project.command('capture').description('Record game media with the native macOS recorder');
1017
1021
  projectCapture
1018
- .command('listing [target]', { hidden: true })
1019
- .description('Record private listing video for a hosted app')
1022
+ .argument('[target]')
1020
1023
  .option('--app <name>', 'App name when the target is a workspace')
1021
1024
  .option('--duration <seconds>', 'Recording duration in seconds')
1022
1025
  .option('--width <pixels>', 'Final gameplay width in pixels')
@@ -1026,6 +1029,7 @@ projectCapture
1026
1029
  .option('--audio', 'Capture gameplay audio')
1027
1030
  .option('--output-dir <path>', 'Output directory for the capture artifacts')
1028
1031
  .option('--keep-raw', 'Keep the raw intermediate recording')
1032
+ .option('--content-hash <sha256>', 'Uploaded build content hash to bind this capture to')
1029
1033
  .action(async (target, opts) => {
1030
1034
  await (0, captureListing_1.captureListing)(target, {
1031
1035
  app: opts.app,
@@ -1037,27 +1041,7 @@ projectCapture
1037
1041
  audio: opts.audio,
1038
1042
  outputDir: opts.outputDir,
1039
1043
  keepRaw: opts.keepRaw,
1040
- });
1041
- });
1042
- projectCapture
1043
- .command('remote <url>')
1044
- .description('Run a Playwright capture against a published URL')
1045
- .option('--timeout <seconds>', 'Readiness timeout in seconds')
1046
- .option('--settle <seconds>', 'Post-ready settle delay before screenshot in seconds')
1047
- .option('--screenshot <path>', 'Screenshot output path')
1048
- .option('--log <path>', 'Log output path')
1049
- .option('--expected-url <url>', 'Expected final URL after redirects')
1050
- .option('--actions <path>', 'JSON action file with click, press, and wait steps before screenshot')
1051
- .option('--viewport <size>', 'Viewport size as WIDTHxHEIGHT')
1052
- .option('--surface <surface>', 'Browser surface preset: desktop, mobile-landscape, or mobile-portrait')
1053
- .option('--fps-sample-ms <milliseconds>', 'Sample frame rate for this many milliseconds after capture')
1054
- .option('--username <username>', 'Login username')
1055
- .option('--password <password>', 'Login password')
1056
- .option('--login-url <url>', 'Explicit login URL')
1057
- .action(async (url, _opts, command) => {
1058
- await (0, captureRemote_1.captureRemote)(url, {
1059
- ...(command.parent?.opts() ?? {}),
1060
- ...command.opts(),
1044
+ contentHash: opts.contentHash,
1061
1045
  });
1062
1046
  });
1063
1047
  const projectMarketing = project.command('marketing').description('Prepare local marketing captures and assets');
@@ -1,6 +1,7 @@
1
1
  import type { Browser, BrowserContext, BrowserContextOptions, Page } from 'playwright-core';
2
2
  export declare const HEADLESS_CHROMIUM_ARGS: readonly ["--no-sandbox", "--disable-web-security", "--use-gl=angle", "--use-angle=swiftshader", "--enable-unsafe-swiftshader", "--ignore-gpu-blocklist"];
3
3
  export declare const INTERACTIVE_CHROMIUM_ARGS: readonly ["--no-sandbox", "--disable-web-security", "--ignore-gpu-blocklist"];
4
+ export declare const INSTALLED_CHROME_ARGS: readonly ["--no-sandbox", "--disable-web-security", "--ignore-gpu-blocklist"];
4
5
  export declare const CHROMIUM_ARGS: readonly ["--no-sandbox", "--disable-web-security", "--use-gl=angle", "--use-angle=swiftshader", "--enable-unsafe-swiftshader", "--ignore-gpu-blocklist"];
5
6
  type PlaywrightModule = {
6
7
  chromium: {
@@ -8,11 +9,13 @@ type PlaywrightModule = {
8
9
  headless?: boolean;
9
10
  args?: readonly string[];
10
11
  chromiumSandbox?: boolean;
12
+ channel?: string;
11
13
  }): Promise<Browser>;
12
14
  launchPersistentContext?(userDataDir: string, options: BrowserContextOptions & {
13
15
  headless?: boolean;
14
16
  args?: readonly string[];
15
17
  chromiumSandbox?: boolean;
18
+ channel?: string;
16
19
  }): Promise<BrowserContext>;
17
20
  };
18
21
  };
@@ -21,11 +24,13 @@ type PlaydropBrowserContextOptions = BrowserContextOptions & {
21
24
  };
22
25
  export declare function setPlaywrightLoader(loader: (() => Promise<PlaywrightModule>) | null): void;
23
26
  export declare function createPlaywrightLaunchError(tool: string, error: unknown): Error;
27
+ export declare function createInstalledChromeLaunchError(tool: string, error: unknown): Error;
24
28
  type PageCallback<T> = (context: {
25
29
  browser: Browser;
26
30
  context: BrowserContext;
27
31
  page: Page;
28
32
  }) => Promise<T>;
29
33
  export declare function withChromiumPage<T>(callback: PageCallback<T>, options?: PlaydropBrowserContextOptions): Promise<T>;
34
+ export declare function withInstalledChromePage<T>(callback: PageCallback<T>, options?: PlaydropBrowserContextOptions): Promise<T>;
30
35
  export declare function launchPersistentChromiumContext(userDataDir: string, options?: PlaydropBrowserContextOptions): Promise<BrowserContext>;
31
36
  export {};
@@ -33,13 +33,16 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.CHROMIUM_ARGS = exports.INTERACTIVE_CHROMIUM_ARGS = exports.HEADLESS_CHROMIUM_ARGS = void 0;
36
+ exports.CHROMIUM_ARGS = exports.INSTALLED_CHROME_ARGS = exports.INTERACTIVE_CHROMIUM_ARGS = exports.HEADLESS_CHROMIUM_ARGS = void 0;
37
37
  exports.setPlaywrightLoader = setPlaywrightLoader;
38
38
  exports.createPlaywrightLaunchError = createPlaywrightLaunchError;
39
+ exports.createInstalledChromeLaunchError = createInstalledChromeLaunchError;
39
40
  exports.withChromiumPage = withChromiumPage;
41
+ exports.withInstalledChromePage = withInstalledChromePage;
40
42
  exports.launchPersistentChromiumContext = launchPersistentChromiumContext;
41
43
  const node_url_1 = require("node:url");
42
44
  const TOOL_NAME = 'playdrop';
45
+ const INSTALLED_CHROME_CHANNEL = 'chrome';
43
46
  const COMMON_CHROMIUM_ARGS = [
44
47
  '--no-sandbox',
45
48
  '--disable-web-security',
@@ -57,6 +60,8 @@ exports.INTERACTIVE_CHROMIUM_ARGS = [
57
60
  ...COMMON_CHROMIUM_ARGS,
58
61
  '--ignore-gpu-blocklist',
59
62
  ];
63
+ // Deterministic project checks must exercise the installed Chrome GPU path.
64
+ exports.INSTALLED_CHROME_ARGS = exports.INTERACTIVE_CHROMIUM_ARGS;
60
65
  // Backward-compatible alias for older imports.
61
66
  exports.CHROMIUM_ARGS = exports.HEADLESS_CHROMIUM_ARGS;
62
67
  let customLoader = null;
@@ -101,6 +106,19 @@ function createPlaywrightLaunchError(tool, error) {
101
106
  }
102
107
  return error instanceof Error ? error : new Error(message);
103
108
  }
109
+ function createInstalledChromeLaunchError(tool, error) {
110
+ const message = error && typeof error === 'object' && 'message' in error ? String(error.message) : String(error);
111
+ return new Error([
112
+ `[${tool}] Failed to launch installed Google Chrome.`,
113
+ '',
114
+ 'Required setup:',
115
+ ' 1. Install Google Chrome for this macOS user.',
116
+ ' 2. Run from a logged-in GUI session with WindowServer access.',
117
+ ' 3. If invoked over SSH, keep keychain unlock and the command in the same SSH session.',
118
+ '',
119
+ `Original error: ${message}`,
120
+ ].join('\n'));
121
+ }
104
122
  async function grantAutomationLocalNetworkAccess(context, origin) {
105
123
  if (!origin?.trim()) {
106
124
  return;
@@ -153,6 +171,47 @@ async function withChromiumPage(callback, options = {}) {
153
171
  }
154
172
  }
155
173
  }
174
+ async function withInstalledChromePage(callback, options = {}) {
175
+ const { chromium } = await loadPlaywright();
176
+ let browser;
177
+ try {
178
+ browser = await chromium.launch({
179
+ channel: INSTALLED_CHROME_CHANNEL,
180
+ headless: false,
181
+ args: [...exports.INSTALLED_CHROME_ARGS],
182
+ chromiumSandbox: false,
183
+ });
184
+ }
185
+ catch (error) {
186
+ throw createInstalledChromeLaunchError(TOOL_NAME, error);
187
+ }
188
+ let context = null;
189
+ try {
190
+ const { automationOrigin, ...contextOptions } = options;
191
+ context = await browser.newContext(contextOptions);
192
+ await grantAutomationLocalNetworkAccess(context, automationOrigin);
193
+ const page = await context.newPage();
194
+ return await callback({ browser, context, page });
195
+ }
196
+ finally {
197
+ if (context) {
198
+ try {
199
+ await context.close();
200
+ }
201
+ catch (closeError) {
202
+ const message = closeError instanceof Error ? closeError.message : String(closeError);
203
+ console.warn(`[${TOOL_NAME}] Failed to close Playwright context: ${message}`);
204
+ }
205
+ }
206
+ try {
207
+ await browser.close();
208
+ }
209
+ catch (closeError) {
210
+ const message = closeError instanceof Error ? closeError.message : String(closeError);
211
+ console.warn(`[${TOOL_NAME}] Failed to close Playwright browser: ${message}`);
212
+ }
213
+ }
214
+ }
156
215
  async function launchPersistentChromiumContext(userDataDir, options = {}) {
157
216
  const { chromium } = await loadPlaywright();
158
217
  if (typeof chromium.launchPersistentContext !== 'function') {