@playdrop/playdrop-cli 0.12.2 → 0.12.4

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.
@@ -39,6 +39,8 @@ exports.assertWorkerProjectVersionBumped = assertWorkerProjectVersionBumped;
39
39
  exports.buildAgentFailureCode = buildAgentFailureCode;
40
40
  exports.describeAgentFailureForEvent = describeAgentFailureForEvent;
41
41
  exports.createLocalPlaydropShim = createLocalPlaydropShim;
42
+ exports.parseCodexModelCatalog = parseCodexModelCatalog;
43
+ exports.parseClaudeModelCatalog = parseClaudeModelCatalog;
42
44
  exports.buildWorkerSetupActions = buildWorkerSetupActions;
43
45
  exports.readActivePersonalWorkerRuntimeState = readActivePersonalWorkerRuntimeState;
44
46
  exports.parseLaunchAgentProgramArguments = parseLaunchAgentProgramArguments;
@@ -99,6 +101,7 @@ Object.defineProperty(exports, "readEnvBoolean", { enumerable: true, get: functi
99
101
  Object.defineProperty(exports, "runLoggedProcess", { enumerable: true, get: function () { return runtime_2.runLoggedProcess; } });
100
102
  const DEFAULT_POLL_INTERVAL_MS = 1000;
101
103
  const HEARTBEAT_INTERVAL_MS = 10000;
104
+ const WORKER_CAPABILITY_REFRESH_INTERVAL_MS = 10 * 60000;
102
105
  const INSTRUMENT_HEARTBEAT_INTERVAL_MS = 1000;
103
106
  const TASK_HEARTBEAT_TRANSIENT_FAILURE_LIMIT = 6;
104
107
  const DEFAULT_WORKER_MAX_PARALLEL_TASKS = 5;
@@ -973,12 +976,24 @@ function resolvePlaydropPluginRoot(input = {}) {
973
976
  ...candidateVersionDirectories(node_path_1.default.join(homeDir, '.codex', 'plugins', 'cache', 'playdrop', 'playdrop')),
974
977
  ...candidateVersionDirectories(node_path_1.default.join(homeDir, '.codex', 'plugins', 'cache', 'olivier-local-plugins', 'playdrop')),
975
978
  ];
979
+ let firstCandidateError = null;
976
980
  for (const candidate of candidates) {
977
981
  if (!looksLikePlaydropPluginRoot(candidate)) {
978
982
  continue;
979
983
  }
980
- readPlaydropPluginStagingManifest(candidate);
981
- return candidate;
984
+ try {
985
+ readPlaydropPluginStagingManifest(candidate);
986
+ return candidate;
987
+ }
988
+ catch (error) {
989
+ firstCandidateError ?? (firstCandidateError = error);
990
+ continue;
991
+ }
992
+ }
993
+ if (firstCandidateError) {
994
+ throw firstCandidateError instanceof Error
995
+ ? firstCandidateError
996
+ : new Error(String(firstCandidateError));
982
997
  }
983
998
  throw new Error(`playdrop_plugin_staging_manifest_missing: install or update the PlayDrop plugin so ${PLAYDROP_PLUGIN_STAGING_MANIFEST_PATH} is available.`);
984
999
  }
@@ -1320,29 +1335,8 @@ function probePlaywrightChromium() {
1320
1335
  }
1321
1336
  return { version, chromiumInstalled: true, executablePath };
1322
1337
  }
1323
- function readWorkerModelList(agent, envName) {
1324
- const raw = node_process_1.default.env[envName];
1325
- if (typeof raw !== 'string' || raw.trim().length === 0) {
1326
- return [...types_1.AGENT_TASK_MODEL_VALUES_BY_AGENT[agent]];
1327
- }
1328
- const values = raw
1329
- .split(',')
1330
- .map((entry) => entry.trim())
1331
- .filter(Boolean)
1332
- .map((entry) => (0, types_1.normalizeAgentTaskModel)(agent, entry));
1333
- const unsupported = raw
1334
- .split(',')
1335
- .map((entry) => entry.trim())
1336
- .filter(Boolean)
1337
- .filter((entry) => !(0, types_1.normalizeAgentTaskModel)(agent, entry));
1338
- if (unsupported.length > 0) {
1339
- throw new Error(`worker_preflight_unsupported_model_list:${envName}:${unsupported.join(',')}`);
1340
- }
1341
- const unique = Array.from(new Set(values));
1342
- if (unique.length === 0 || unique.some((model) => !model || model.length > 120)) {
1343
- throw new Error(`worker_preflight_invalid_model_list:${envName}`);
1344
- }
1345
- return unique;
1338
+ function normalizeDiscoveredModels(agent, models) {
1339
+ return Array.from(new Set(models.map((model) => (0, types_1.normalizeAgentTaskModel)(agent, model)).filter(Boolean)));
1346
1340
  }
1347
1341
  function parseEnvLine(line) {
1348
1342
  const trimmed = line.trim();
@@ -1388,45 +1382,51 @@ function loadWorkerEnvFile(envName, cwd = node_process_1.default.cwd()) {
1388
1382
  return envPath;
1389
1383
  }
1390
1384
  function buildWorkerCapabilities(input) {
1391
- const codexVersion = typeof input === 'string' ? input : input.codexVersion?.trim() || null;
1392
- const codexAuthenticated = typeof input === 'string' ? true : input.codexAuthenticated === true;
1393
- const claudeVersion = typeof input === 'string' ? null : input.claudeVersion?.trim() || null;
1394
- const claudeAuthenticated = typeof input === 'string' ? false : input.claudeAuthenticated === true;
1395
- const cursorVersion = typeof input === 'string' ? null : input.cursorVersion?.trim() || null;
1396
- const cursorAuthenticated = typeof input === 'string' ? false : input.cursorAuthenticated === true;
1397
- const npmVersion = typeof input === 'string' ? null : input.npmVersion?.trim() || null;
1398
- const rawPlaywright = typeof input === 'string' ? null : input.playwright ?? null;
1385
+ const codexVersion = input.codexVersion?.trim() || null;
1386
+ const codexAuthenticated = input.codexAuthenticated === true;
1387
+ const codexModels = normalizeDiscoveredModels('CODEX', input.codexModels ?? []);
1388
+ const claudeVersion = input.claudeVersion?.trim() || null;
1389
+ const claudeAuthenticated = input.claudeAuthenticated === true;
1390
+ const claudeModels = normalizeDiscoveredModels('CLAUDE_CODE', input.claudeModels ?? []);
1391
+ const cursorVersion = input.cursorVersion?.trim() || null;
1392
+ const cursorAuthenticated = input.cursorAuthenticated === true;
1393
+ const npmVersion = input.npmVersion?.trim() || null;
1394
+ const rawPlaywright = input.playwright ?? null;
1399
1395
  const playwright = rawPlaywright
1400
1396
  ? { version: rawPlaywright.version, chromiumInstalled: rawPlaywright.chromiumInstalled }
1401
1397
  : null;
1402
- const maxParallelTasks = typeof input === 'string'
1403
- ? DEFAULT_WORKER_MAX_PARALLEL_TASKS
1404
- : input.maxParallelTasks ?? DEFAULT_WORKER_MAX_PARALLEL_TASKS;
1405
- const runningTaskCount = typeof input === 'string' ? 0 : input.runningTaskCount ?? 0;
1398
+ const maxParallelTasks = input.maxParallelTasks ?? DEFAULT_WORKER_MAX_PARALLEL_TASKS;
1399
+ const runningTaskCount = input.runningTaskCount ?? 0;
1406
1400
  const degradedReasons = [];
1407
1401
  const agents = [];
1408
1402
  if (codexVersion) {
1409
1403
  if (!codexAuthenticated) {
1410
1404
  degradedReasons.push('codex_not_authenticated');
1411
1405
  }
1406
+ if (codexModels.length === 0) {
1407
+ degradedReasons.push('codex_models_unavailable');
1408
+ }
1412
1409
  agents.push({
1413
1410
  agent: 'CODEX',
1414
1411
  cliVersion: codexVersion,
1415
1412
  authenticated: codexAuthenticated,
1416
- models: readWorkerModelList('CODEX', 'PLAYDROP_WORKER_CODEX_MODELS'),
1417
- ready: codexAuthenticated,
1413
+ models: codexModels,
1414
+ ready: codexAuthenticated && codexModels.length > 0,
1418
1415
  });
1419
1416
  }
1420
1417
  if (claudeVersion) {
1421
1418
  if (!claudeAuthenticated) {
1422
1419
  degradedReasons.push('claude_not_authenticated');
1423
1420
  }
1421
+ if (claudeModels.length === 0) {
1422
+ degradedReasons.push('claude_models_unavailable');
1423
+ }
1424
1424
  agents.push({
1425
1425
  agent: 'CLAUDE_CODE',
1426
1426
  cliVersion: claudeVersion,
1427
1427
  authenticated: claudeAuthenticated,
1428
- models: readWorkerModelList('CLAUDE_CODE', 'PLAYDROP_WORKER_CLAUDE_MODELS'),
1429
- ready: claudeAuthenticated,
1428
+ models: claudeModels,
1429
+ ready: claudeAuthenticated && claudeModels.length > 0,
1430
1430
  });
1431
1431
  }
1432
1432
  if (cursorVersion) {
@@ -1761,7 +1761,7 @@ function resolveCodexModel(model) {
1761
1761
  if (!normalized) {
1762
1762
  throw new Error('agent_task_assignment_model_missing');
1763
1763
  }
1764
- const effortMatch = normalized.match(/^(.*)-(low|medium|high|extra-high|xhigh|max)$/);
1764
+ const effortMatch = normalized.match(/^(.*?)-(extra-high|low|medium|high|xhigh|max)$/);
1765
1765
  if (effortMatch?.[1] && effortMatch[2]) {
1766
1766
  const effort = effortMatch[2] === 'extra-high' ? 'xhigh' : effortMatch[2];
1767
1767
  return { model: effortMatch[1], reasoningEffort: effort };
@@ -2105,6 +2105,61 @@ function probeCodexInstallation() {
2105
2105
  }
2106
2106
  return { codexVersion: match[1] };
2107
2107
  }
2108
+ function parseCodexModelCatalog(output) {
2109
+ const jsonStart = output.indexOf('{');
2110
+ const jsonEnd = output.lastIndexOf('}');
2111
+ if (jsonStart < 0 || jsonEnd <= jsonStart) {
2112
+ throw new Error('worker_preflight_codex_model_catalog_invalid');
2113
+ }
2114
+ let parsed;
2115
+ try {
2116
+ parsed = JSON.parse(output.slice(jsonStart, jsonEnd + 1));
2117
+ }
2118
+ catch {
2119
+ throw new Error('worker_preflight_codex_model_catalog_invalid');
2120
+ }
2121
+ const models = typeof parsed === 'object' && parsed !== null && Array.isArray(parsed.models)
2122
+ ? parsed.models
2123
+ : null;
2124
+ if (!models) {
2125
+ throw new Error('worker_preflight_codex_model_catalog_invalid');
2126
+ }
2127
+ const discovered = [];
2128
+ for (const value of models) {
2129
+ if (typeof value !== 'object' || value === null) {
2130
+ continue;
2131
+ }
2132
+ const model = value;
2133
+ const slug = typeof model.slug === 'string' ? model.slug.trim() : '';
2134
+ if (!slug || model.visibility !== 'list') {
2135
+ continue;
2136
+ }
2137
+ const levels = Array.isArray(model.supported_reasoning_levels)
2138
+ ? model.supported_reasoning_levels
2139
+ .map((entry) => typeof entry === 'object' && entry !== null ? entry.effort : null)
2140
+ .filter((effort) => typeof effort === 'string' && Boolean(effort.trim()))
2141
+ : [];
2142
+ if (levels.length === 0) {
2143
+ discovered.push(slug);
2144
+ continue;
2145
+ }
2146
+ for (const effort of levels) {
2147
+ discovered.push(`${slug}-${effort.trim()}`);
2148
+ }
2149
+ }
2150
+ const normalized = normalizeDiscoveredModels('CODEX', discovered);
2151
+ if (normalized.length === 0) {
2152
+ throw new Error('worker_preflight_codex_model_catalog_empty');
2153
+ }
2154
+ return normalized;
2155
+ }
2156
+ function probeCodexModels() {
2157
+ const result = (0, shellProbe_1.runShell)('codex debug models');
2158
+ if (result.exitCode !== 0) {
2159
+ throw new Error(`worker_preflight_codex_model_catalog_failed: "codex debug models" exited with code ${result.exitCode}. Output: ${result.output.slice(0, 200)}`);
2160
+ }
2161
+ return parseCodexModelCatalog(result.output);
2162
+ }
2108
2163
  function probeClaudeInstallation() {
2109
2164
  const probe = (0, shellProbe_1.runShell)((0, shellProbe_1.buildCommandPathProbe)('claude'));
2110
2165
  if (probe.exitCode !== 0 || !probe.output.trim()) {
@@ -2123,6 +2178,71 @@ function probeClaudeInstallation() {
2123
2178
  }
2124
2179
  return { claudeVersion: match[1] };
2125
2180
  }
2181
+ function parseClaudeModelCatalog(output) {
2182
+ let parsed;
2183
+ try {
2184
+ parsed = JSON.parse(output);
2185
+ }
2186
+ catch {
2187
+ throw new Error('worker_preflight_claude_model_catalog_invalid');
2188
+ }
2189
+ const result = typeof parsed === 'object' && parsed !== null && typeof parsed.result === 'string'
2190
+ ? parsed.result
2191
+ : '';
2192
+ const available = /Available:\s*([^\n]+)/.exec(result)?.[1]?.trim() ?? '';
2193
+ if (!available) {
2194
+ throw new Error('worker_preflight_claude_model_catalog_invalid');
2195
+ }
2196
+ const discovered = available
2197
+ .replace(/\.$/, '')
2198
+ .split(/,|\bor\b/)
2199
+ .map((entry) => entry.trim())
2200
+ .filter((entry) => entry && entry !== 'a full model ID');
2201
+ const normalized = normalizeDiscoveredModels('CLAUDE_CODE', discovered);
2202
+ if (normalized.length === 0) {
2203
+ throw new Error('worker_preflight_claude_model_catalog_empty');
2204
+ }
2205
+ return normalized;
2206
+ }
2207
+ function probeClaudeModels() {
2208
+ const result = (0, shellProbe_1.runShell)('claude --print --output-format json --max-turns 1 "/model"');
2209
+ if (result.exitCode !== 0) {
2210
+ throw new Error(`worker_preflight_claude_model_catalog_failed: "claude /model" exited with code ${result.exitCode}. Output: ${result.output.slice(0, 200)}`);
2211
+ }
2212
+ return parseClaudeModelCatalog(result.output);
2213
+ }
2214
+ function probeReadyWorkerCapabilities(input) {
2215
+ const codexVersion = probeCodexInstallation()?.codexVersion ?? null;
2216
+ const claudeVersion = probeClaudeInstallation()?.claudeVersion ?? null;
2217
+ const cursorVersion = probeCursorInstallation()?.cursorVersion ?? null;
2218
+ const capabilities = buildWorkerCapabilities({
2219
+ codexVersion,
2220
+ codexAuthenticated: codexVersion ? readCodexAuthenticated() : false,
2221
+ codexModels: codexVersion ? probeCodexModels() : [],
2222
+ claudeVersion,
2223
+ claudeAuthenticated: claudeVersion ? readClaudeAuthenticated() : false,
2224
+ claudeModels: claudeVersion ? probeClaudeModels() : [],
2225
+ cursorVersion,
2226
+ cursorAuthenticated: cursorVersion ? readCursorAuthenticated() : false,
2227
+ npmVersion: input.npmVersion,
2228
+ playwright: input.playwright,
2229
+ maxParallelTasks: input.maxParallelTasks,
2230
+ runningTaskCount: 0,
2231
+ });
2232
+ if (input.pluginVersion) {
2233
+ capabilities.pluginVersion = input.pluginVersion;
2234
+ for (const agent of capabilities.agents) {
2235
+ agent.pluginVersion = input.pluginVersion;
2236
+ }
2237
+ }
2238
+ if (!capabilities.ready) {
2239
+ const reasons = Array.isArray(capabilities.degradedReasons) && capabilities.degradedReasons.length > 0
2240
+ ? capabilities.degradedReasons.join(', ')
2241
+ : 'no ready agent';
2242
+ throw new Error(`worker_preflight_failed: ${reasons}`);
2243
+ }
2244
+ return capabilities;
2245
+ }
2126
2246
  function probeCursorInstallation() {
2127
2247
  if (!(0, runtime_1.readEnvBoolean)('PLAYDROP_WORKER_ENABLE_CURSOR', false)) {
2128
2248
  return null;
@@ -2190,7 +2310,9 @@ function readWorkerTargetUpdateFailure() {
2190
2310
  function collectWorkerLocalStatus() {
2191
2311
  const degradedReasons = [];
2192
2312
  let codexVersion = null;
2313
+ let codexModels = [];
2193
2314
  let claudeVersion = null;
2315
+ let claudeModels = [];
2194
2316
  let cursorVersion = null;
2195
2317
  let npmVersion = null;
2196
2318
  let playwright = null;
@@ -2200,12 +2322,14 @@ function collectWorkerLocalStatus() {
2200
2322
  let targetUpdateFailure = null;
2201
2323
  try {
2202
2324
  codexVersion = probeCodexInstallation()?.codexVersion ?? null;
2325
+ codexModels = codexVersion ? probeCodexModels() : [];
2203
2326
  }
2204
2327
  catch (error) {
2205
2328
  degradedReasons.push(errorCode(error));
2206
2329
  }
2207
2330
  try {
2208
2331
  claudeVersion = probeClaudeInstallation()?.claudeVersion ?? null;
2332
+ claudeModels = claudeVersion ? probeClaudeModels() : [];
2209
2333
  }
2210
2334
  catch (error) {
2211
2335
  degradedReasons.push(errorCode(error));
@@ -2250,8 +2374,10 @@ function collectWorkerLocalStatus() {
2250
2374
  capabilities = buildWorkerCapabilities({
2251
2375
  codexVersion,
2252
2376
  codexAuthenticated: codexVersion ? readCodexAuthenticated() : false,
2377
+ codexModels,
2253
2378
  claudeVersion,
2254
2379
  claudeAuthenticated: claudeVersion ? readClaudeAuthenticated() : false,
2380
+ claudeModels,
2255
2381
  cursorVersion,
2256
2382
  cursorAuthenticated: cursorVersion ? readCursorAuthenticated() : false,
2257
2383
  npmVersion,
@@ -2404,7 +2530,6 @@ function writeManagedWorkerUpdatePolicyFile(input) {
2404
2530
  minimumCliVersion: input.updatePolicy.minimumCliVersion,
2405
2531
  targetPluginVersion: input.updatePolicy.targetPluginVersion,
2406
2532
  minimumPluginVersion: input.updatePolicy.minimumPluginVersion,
2407
- managedAgentCliVersions: input.updatePolicy.managedAgentCliVersions ?? {},
2408
2533
  requiredWrapperContractVersion: input.updatePolicy.requiredWrapperContractVersion,
2409
2534
  }, null, 2));
2410
2535
  }
@@ -2557,6 +2682,7 @@ async function writeManagedWorkerWrapper(input) {
2557
2682
  const script = `#!/bin/sh
2558
2683
  set -eu
2559
2684
 
2685
+ export PATH="${node_path_1.default.join(node_os_1.default.homedir(), '.playdrop', 'worker', 'bin')}:${node_path_1.default.join(node_os_1.default.homedir(), '.local', 'bin')}:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin"
2560
2686
  export PLAYDROP_WORKER_WRAPPER_CONTRACT_VERSION="${WORKER_WRAPPER_CONTRACT_VERSION}"
2561
2687
  POLICY_FILE="${policyFile}"
2562
2688
  FAILURE_FILE="${failureFile}"
@@ -2760,55 +2886,7 @@ if [ "$TARGET_UPDATE_FAILED" -eq 0 ]; then
2760
2886
  rm -f "$FAILURE_FILE"
2761
2887
  fi
2762
2888
 
2763
- node - "$POLICY_FILE" <<'NODE' || fail_converge "$?" "worker_update_agent_cli_install_failed"
2764
- const childProcess = require("child_process");
2765
- const fs = require("fs");
2766
-
2767
- const policyFile = process.argv[2];
2768
- if (!policyFile || !fs.existsSync(policyFile)) {
2769
- process.exit(0);
2770
- }
2771
- const policy = JSON.parse(fs.readFileSync(policyFile, "utf8"));
2772
- const versions = policy && policy.managedAgentCliVersions && typeof policy.managedAgentCliVersions === "object"
2773
- ? policy.managedAgentCliVersions
2774
- : {};
2775
- const packages = {
2776
- CODEX: "@openai/codex",
2777
- CLAUDE_CODE: "@anthropic-ai/claude-code",
2778
- };
2779
- const aliases = {
2780
- CODEX: "CODEX",
2781
- OPENAI_CODEX: "CODEX",
2782
- CLAUDE: "CLAUDE_CODE",
2783
- CLAUDE_CODE: "CLAUDE_CODE",
2784
- CURSOR: "CURSOR_COMPOSER",
2785
- CURSOR_COMPOSER: "CURSOR_COMPOSER",
2786
- };
2787
- for (const [rawKey, rawVersion] of Object.entries(versions)) {
2788
- const version = typeof rawVersion === "string" ? rawVersion.trim() : "";
2789
- if (!/^\\d+\\.\\d+\\.\\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(version)) {
2790
- throw new Error(\`worker_update_agent_cli_version_invalid:\${rawKey}\`);
2791
- }
2792
- const normalizedKey = aliases[String(rawKey).trim().toUpperCase().replace(/[-\\s]+/g, "_")];
2793
- if (normalizedKey === "CURSOR_COMPOSER") {
2794
- throw new Error("worker_update_cursor_cli_auto_update_unsupported");
2795
- }
2796
- const packageName = normalizedKey ? packages[normalizedKey] : null;
2797
- if (!packageName) {
2798
- throw new Error(\`worker_update_agent_cli_unknown:\${rawKey}\`);
2799
- }
2800
- const result = childProcess.spawnSync(
2801
- "npm",
2802
- ["install", "-g", "--no-audit", "--no-fund", \`\${packageName}@\${version}\`],
2803
- { stdio: "inherit" },
2804
- );
2805
- if (result.status !== 0) {
2806
- throw new Error(\`worker_update_agent_cli_install_failed:\${normalizedKey}\`);
2807
- }
2808
- }
2809
- NODE
2810
-
2811
- exec playdrop worker start --env ${shellQuote(input.env)} --name ${shellQuote(input.workerName)}
2889
+ exec /usr/bin/caffeinate -dimsu playdrop worker start --env ${shellQuote(input.env)} --name ${shellQuote(input.workerName)}
2812
2890
  `;
2813
2891
  await (0, promises_1.writeFile)(wrapperPath, script, 'utf8');
2814
2892
  await (0, promises_1.chmod)(wrapperPath, 0o755);
@@ -2864,8 +2942,9 @@ function launchManagedWorker(label, plistPath) {
2864
2942
  throw new Error('worker_setup_launchagent_uid_unavailable');
2865
2943
  }
2866
2944
  const domain = `gui/${uid}`;
2945
+ (0, shellProbe_1.runShell)(`launchctl bootout ${shellQuote(`${domain}/${label}`)}`);
2867
2946
  const bootstrap = (0, shellProbe_1.runShell)(`launchctl bootstrap ${shellQuote(domain)} ${shellQuote(plistPath)}`);
2868
- if (bootstrap.exitCode !== 0 && !/already|exists|service name is already registered/i.test(bootstrap.output)) {
2947
+ if (bootstrap.exitCode !== 0) {
2869
2948
  throw new Error(`worker_setup_launchagent_start_failed: launchctl bootstrap exited with code ${bootstrap.exitCode}. ${bootstrap.output.slice(0, 300)}`);
2870
2949
  }
2871
2950
  const kickstart = (0, shellProbe_1.runShell)(`launchctl kickstart -k ${shellQuote(`${domain}/${label}`)}`);
@@ -3247,12 +3326,6 @@ async function startWorker(options = {}) {
3247
3326
  throw new Error('worker_session_invalid: the stored session did not resolve to a user. Run "playdrop auth login" and retry.');
3248
3327
  }
3249
3328
  const target = resolveWorkerExecutionTargetFromRole(me.user.role);
3250
- const codexVersion = probeCodexInstallation()?.codexVersion ?? null;
3251
- const claudeVersion = probeClaudeInstallation()?.claudeVersion ?? null;
3252
- const cursorVersion = probeCursorInstallation()?.cursorVersion ?? null;
3253
- const codexAuthenticated = codexVersion ? readCodexAuthenticated() : false;
3254
- const claudeAuthenticated = claudeVersion ? readClaudeAuthenticated() : false;
3255
- const cursorAuthenticated = cursorVersion ? readCursorAuthenticated() : false;
3256
3329
  const npmVersion = probeNpmVersion();
3257
3330
  const playwright = probePlaywrightChromium();
3258
3331
  const playdropPluginRoot = resolvePlaydropPluginRoot();
@@ -3260,30 +3333,13 @@ async function startWorker(options = {}) {
3260
3333
  const workerKey = (0, config_1.getOrCreateWorkerKey)();
3261
3334
  const workerName = options.name?.trim() || `${username} - ${node_os_1.default.hostname()}`;
3262
3335
  const maxParallelTasks = (0, runtime_1.readPositiveEnvInt)('PLAYDROP_WORKER_MAX_PARALLEL_TASKS', DEFAULT_WORKER_MAX_PARALLEL_TASKS);
3263
- const capabilities = buildWorkerCapabilities({
3264
- codexVersion,
3265
- codexAuthenticated,
3266
- claudeVersion,
3267
- claudeAuthenticated,
3268
- cursorVersion,
3269
- cursorAuthenticated,
3336
+ let capabilities = probeReadyWorkerCapabilities({
3270
3337
  npmVersion,
3271
3338
  playwright,
3272
3339
  maxParallelTasks,
3273
- runningTaskCount: 0,
3340
+ pluginVersion: playdropPluginVersion,
3274
3341
  });
3275
- if (playdropPluginVersion) {
3276
- capabilities.pluginVersion = playdropPluginVersion;
3277
- for (const agent of capabilities.agents) {
3278
- agent.pluginVersion = playdropPluginVersion;
3279
- }
3280
- }
3281
- if (!capabilities.ready) {
3282
- const reasons = Array.isArray(capabilities.degradedReasons) && capabilities.degradedReasons.length > 0
3283
- ? capabilities.degradedReasons.join(', ')
3284
- : 'no ready agent';
3285
- throw new Error(`worker_preflight_failed: ${reasons}`);
3286
- }
3342
+ let lastCapabilityRefreshAt = Date.now();
3287
3343
  writeWorkerRuntimeState({
3288
3344
  env,
3289
3345
  workerKey,
@@ -3924,6 +3980,17 @@ async function startWorker(options = {}) {
3924
3980
  if (sessionExpired) {
3925
3981
  throw new Error(exports.WORKER_SESSION_EXPIRED_MESSAGE);
3926
3982
  }
3983
+ if (activeTaskRuns.size === 0
3984
+ && Date.now() - lastCapabilityRefreshAt >= WORKER_CAPABILITY_REFRESH_INTERVAL_MS) {
3985
+ capabilities = probeReadyWorkerCapabilities({
3986
+ npmVersion,
3987
+ playwright,
3988
+ maxParallelTasks,
3989
+ pluginVersion: playdropPluginVersion,
3990
+ });
3991
+ lastCapabilityRefreshAt = Date.now();
3992
+ console.log('Worker refreshed installed agent CLI versions and model catalogs.');
3993
+ }
3927
3994
  if (activeTaskRuns.size >= maxParallelTasks) {
3928
3995
  await sleep(DEFAULT_POLL_INTERVAL_MS);
3929
3996
  continue;
@@ -4130,6 +4197,7 @@ async function uploadTask(options = {}) {
4130
4197
  const published = await (0, upload_1.publishWorkerAppProject)({
4131
4198
  client: ctx.client,
4132
4199
  taskId: taskContext.taskId,
4200
+ taskToken: taskContext.taskToken,
4133
4201
  kind: taskContext.kind,
4134
4202
  executionTarget: taskContext.target,
4135
4203
  expectedAppName: taskContext.outputAppName ?? undefined,
package/dist/index.js CHANGED
@@ -120,6 +120,7 @@ function registerLoginCommand(parent) {
120
120
  .option('--password <password>', 'Password for direct login')
121
121
  .option('--key <apiKey>', 'API key for direct login')
122
122
  .option('--handoff-token <token>', 'Native app handoff token for direct login')
123
+ .option('--handoff-token-stdin', 'Read a native app handoff token from stdin')
123
124
  .option('--json', 'Output JSON')
124
125
  .action(async (opts) => {
125
126
  await (0, login_1.login)(opts.env, {
@@ -127,6 +128,7 @@ function registerLoginCommand(parent) {
127
128
  password: opts.password,
128
129
  key: opts.key,
129
130
  handoffToken: opts.handoffToken,
131
+ handoffTokenStdin: opts.handoffTokenStdin,
130
132
  json: opts.json,
131
133
  });
132
134
  });
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.12.2",
2
+ "version": "0.12.4",
3
3
  "build": 1,
4
4
  "runtimeSdkVersion": "0.10.0",
5
5
  "clients": {
@@ -141,14 +141,7 @@ export declare const AGENT_TASK_PROMPT_MIN_LENGTH = 4;
141
141
  export declare const AGENT_TASK_PROMPT_MAX_LENGTH = 100000;
142
142
  export declare const AGENT_RUNTIME_VALUES: readonly ["CODEX", "CLAUDE_CODE", "CURSOR_COMPOSER"];
143
143
  export type AgentRuntime = typeof AGENT_RUNTIME_VALUES[number];
144
- export declare const AGENT_TASK_CODEX_MODEL_VALUES: readonly ["gpt-5.5-low", "gpt-5.5-medium", "gpt-5.5-high", "gpt-5.5-extra-high"];
145
- export type AgentTaskCodexModel = typeof AGENT_TASK_CODEX_MODEL_VALUES[number];
146
- export declare const AGENT_TASK_CLAUDE_CODE_MODEL_VALUES: readonly ["claude-haiku-4-5", "claude-sonnet-4-6", "claude-opus-4-8"];
147
- export type AgentTaskClaudeCodeModel = typeof AGENT_TASK_CLAUDE_CODE_MODEL_VALUES[number];
148
- export declare const AGENT_TASK_CURSOR_COMPOSER_MODEL_VALUES: readonly ["cursor-composer-latest"];
149
- export type AgentTaskCursorComposerModel = typeof AGENT_TASK_CURSOR_COMPOSER_MODEL_VALUES[number];
150
- export declare const AGENT_TASK_MODEL_VALUES_BY_AGENT: Record<AgentRuntime, readonly string[]>;
151
- export declare function normalizeAgentTaskModel(agent: AgentRuntime, model: string): string | null;
144
+ export declare function normalizeAgentTaskModel(_agent: AgentRuntime, model: string): string | null;
152
145
  export declare const AGENT_TASK_EFFORT_VALUES: readonly ["simple", "standard", "complex"];
153
146
  export type AgentTaskEffort = typeof AGENT_TASK_EFFORT_VALUES[number];
154
147
  export declare const AGENT_TASK_EFFORT_KIND_VALUES: readonly ["BUILD_COMPLEXITY", "CHANGE_MAGNITUDE", "REVIEW_DEPTH"];
@@ -178,7 +171,6 @@ export interface AgentAttemptResult {
178
171
  export interface AgentTaskAgentPreference extends AgentAttemptOption {
179
172
  reason?: string | null;
180
173
  }
181
- export declare const AGENT_ATTEMPT_PLAN_DEFAULTS: Record<AgentTaskKind, AgentAttemptOption[]>;
182
174
  export declare const AGENT_TASK_EVENT_KIND_VALUES: readonly ["progress", "agent_message", "system", "catalogue_preview"];
183
175
  export type AgentTaskEventKind = typeof AGENT_TASK_EVENT_KIND_VALUES[number];
184
176
  export declare const AGENT_TASK_TRANSCRIPT_STREAM_VALUES: readonly ["stdout", "stderr", "combined"];
@@ -660,7 +652,6 @@ export interface AgentWorkerUpdatePolicyResponse {
660
652
  targetPluginVersion: string | null;
661
653
  minimumPluginVersion: string | null;
662
654
  requiredWrapperContractVersion: number;
663
- managedAgentCliVersions?: Record<string, string>;
664
655
  }
665
656
  export interface WorkerUpdateIntentRequest {
666
657
  workerKey: string;
@@ -1691,6 +1682,14 @@ export interface ReviewerAppAccessTokenResponse extends AppAccessTokenResponse {
1691
1682
  reviewer: true;
1692
1683
  };
1693
1684
  }
1685
+ export interface CaptureAppAccessTokenResponse extends AppAccessTokenResponse {
1686
+ player: {
1687
+ id: number;
1688
+ username: string;
1689
+ displayName: string;
1690
+ capture: true;
1691
+ };
1692
+ }
1694
1693
  export interface DevPlayerSummary {
1695
1694
  userId: number;
1696
1695
  slot: number;