@playdrop/playdrop-cli 0.12.3 → 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.
- package/config/client-meta.json +1 -1
- package/dist/appUrls.d.ts +9 -0
- package/dist/appUrls.js +29 -0
- package/dist/apps/index.d.ts +2 -0
- package/dist/apps/index.js +2 -0
- package/dist/apps/loadCheck.d.ts +4 -0
- package/dist/apps/loadCheck.js +48 -13
- package/dist/apps/upload.d.ts +2 -0
- package/dist/apps/upload.js +1 -0
- package/dist/commands/captureListing.d.ts +3 -1
- package/dist/commands/captureListing.js +24 -8
- package/dist/commands/check.js +40 -26
- package/dist/commands/create.js +47 -2
- package/dist/commands/devShared.d.ts +5 -0
- package/dist/commands/devShared.js +9 -0
- package/dist/commands/taskCaptureSession.d.ts +4 -0
- package/dist/commands/taskCaptureSession.js +21 -0
- package/dist/commands/upload.d.ts +1 -0
- package/dist/commands/upload.js +16 -1
- package/dist/commands/worker.d.ts +5 -1
- package/dist/commands/worker.js +187 -119
- package/node_modules/@playdrop/config/client-meta.json +1 -1
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +9 -10
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +3 -60
- package/package.json +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/game-ideas.d.ts +0 -46
- package/node_modules/@playdrop/api-client/dist/domains/game-ideas.d.ts.map +0 -1
- package/node_modules/@playdrop/api-client/dist/domains/game-ideas.js +0 -177
- package/node_modules/@playdrop/api-client/dist/domains/music.d.ts +0 -27
- package/node_modules/@playdrop/api-client/dist/domains/music.d.ts.map +0 -1
- package/node_modules/@playdrop/api-client/dist/domains/music.js +0 -96
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AppResponse } from '@playdrop/types';
|
|
2
|
+
import type { TaskCaptureSession } from '../appUrls';
|
|
3
|
+
import type { ResolvedWorkspaceAuthConfig } from '../workspaceAuth';
|
|
4
|
+
export declare function resolveTaskCaptureSession(workspaceAuth: ResolvedWorkspaceAuthConfig | null, app: AppResponse): TaskCaptureSession | null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveTaskCaptureSession = resolveTaskCaptureSession;
|
|
4
|
+
function resolveTaskCaptureSession(workspaceAuth, app) {
|
|
5
|
+
const taskId = workspaceAuth?.config.taskId;
|
|
6
|
+
const taskToken = workspaceAuth?.config.taskToken?.trim() ?? '';
|
|
7
|
+
if (taskId === undefined && !taskToken) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
if (!Number.isInteger(taskId) || Number(taskId) <= 0 || !taskToken) {
|
|
11
|
+
throw new Error('worker_capture_session_incomplete');
|
|
12
|
+
}
|
|
13
|
+
if (!Number.isInteger(app.id) || Number(app.id) <= 0) {
|
|
14
|
+
throw new Error('registered_app_id_missing');
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
appId: Number(app.id),
|
|
18
|
+
taskId: Number(taskId),
|
|
19
|
+
taskToken,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -9,6 +9,7 @@ export declare function upload(pathOrName: string, options?: UploadCommandOption
|
|
|
9
9
|
export type WorkerAppPublishInput = {
|
|
10
10
|
client: ApiClient;
|
|
11
11
|
taskId: number;
|
|
12
|
+
taskToken: string;
|
|
12
13
|
kind: 'NEW_GAME' | 'REMIX_GAME' | 'GAME_UPDATE';
|
|
13
14
|
executionTarget?: AgentExecutionTarget;
|
|
14
15
|
expectedAppName?: string | null;
|
package/dist/commands/upload.js
CHANGED
|
@@ -1225,6 +1225,11 @@ const WORKER_SUPERVISOR_SOURCE_EXCLUDES = [
|
|
|
1225
1225
|
'.playdrop-task-events/',
|
|
1226
1226
|
'bin/playdrop',
|
|
1227
1227
|
];
|
|
1228
|
+
const WORKER_SUPERVISOR_BUNDLE_EXCLUDES = [
|
|
1229
|
+
...WORKER_SUPERVISOR_SOURCE_EXCLUDES,
|
|
1230
|
+
'assets/marketing/',
|
|
1231
|
+
'listing/',
|
|
1232
|
+
];
|
|
1228
1233
|
const MIN_HERO_PORTRAIT_WIDTH = 512;
|
|
1229
1234
|
const MIN_HERO_PORTRAIT_HEIGHT = 768;
|
|
1230
1235
|
const MIN_HERO_LANDSCAPE_WIDTH = 768;
|
|
@@ -2273,7 +2278,7 @@ async function publishWorkerAppProject(input) {
|
|
|
2273
2278
|
...appTask,
|
|
2274
2279
|
versionVisibility: 'PRIVATE',
|
|
2275
2280
|
sourceArchiveExcludeRelativeFiles: WORKER_SUPERVISOR_SOURCE_EXCLUDES,
|
|
2276
|
-
bundleArchiveExcludeRelativeFiles:
|
|
2281
|
+
bundleArchiveExcludeRelativeFiles: WORKER_SUPERVISOR_BUNDLE_EXCLUDES,
|
|
2277
2282
|
};
|
|
2278
2283
|
if (input.kind === 'REMIX_GAME') {
|
|
2279
2284
|
const expectedRemixRef = input.remixSourceRef?.trim() ?? '';
|
|
@@ -2331,6 +2336,15 @@ async function publishWorkerAppProject(input) {
|
|
|
2331
2336
|
if (typeof app.id !== 'number' || !Number.isInteger(app.id) || app.id <= 0) {
|
|
2332
2337
|
throw new Error(`App "${task.name}" registration did not return an app id.`);
|
|
2333
2338
|
}
|
|
2339
|
+
const taskToken = input.taskToken.trim();
|
|
2340
|
+
if (!taskToken) {
|
|
2341
|
+
throw new Error('agent_task_token_missing');
|
|
2342
|
+
}
|
|
2343
|
+
const captureSession = {
|
|
2344
|
+
appId: app.id,
|
|
2345
|
+
taskId: input.taskId,
|
|
2346
|
+
taskToken,
|
|
2347
|
+
};
|
|
2334
2348
|
if (input.kind === 'NEW_GAME' || input.kind === 'REMIX_GAME') {
|
|
2335
2349
|
assertNewGameListingScreenshots(task);
|
|
2336
2350
|
assertTaskPlaytestEvidenceManifest(task);
|
|
@@ -2346,6 +2360,7 @@ async function publishWorkerAppProject(input) {
|
|
|
2346
2360
|
ensureRegisteredAppShell: true,
|
|
2347
2361
|
agentTaskId: input.taskId,
|
|
2348
2362
|
mediaCaptureRequired: input.executionTarget === 'FIRST_PARTY',
|
|
2363
|
+
captureSession,
|
|
2349
2364
|
});
|
|
2350
2365
|
if (!uploadResult.versionCreated || !uploadResult.version) {
|
|
2351
2366
|
throw new Error(`App "${task.name}" upload did not return a created version.`);
|
|
@@ -187,11 +187,13 @@ export declare function classifyWorkerHeartbeatError(error: unknown): 'session_e
|
|
|
187
187
|
export declare function assertWorkerClaimErrorRetryable(error: unknown): void;
|
|
188
188
|
export declare function resolvePlaydropAssetRequirementFromText(value: unknown): WorkerPlaydropAssetRequirement | null;
|
|
189
189
|
export declare function loadWorkerEnvFile(envName: string | undefined, cwd?: string): string | null;
|
|
190
|
-
export declare function buildWorkerCapabilities(input:
|
|
190
|
+
export declare function buildWorkerCapabilities(input: {
|
|
191
191
|
codexVersion?: string | null;
|
|
192
192
|
codexAuthenticated?: boolean | null;
|
|
193
|
+
codexModels?: string[] | null;
|
|
193
194
|
claudeVersion?: string | null;
|
|
194
195
|
claudeAuthenticated?: boolean | null;
|
|
196
|
+
claudeModels?: string[] | null;
|
|
195
197
|
cursorVersion?: string | null;
|
|
196
198
|
cursorAuthenticated?: boolean | null;
|
|
197
199
|
npmVersion?: string | null;
|
|
@@ -233,6 +235,8 @@ export declare function createLocalPlaydropShim(input: {
|
|
|
233
235
|
attempt: number;
|
|
234
236
|
devPort: number;
|
|
235
237
|
}): Promise<string>;
|
|
238
|
+
export declare function parseCodexModelCatalog(output: string): string[];
|
|
239
|
+
export declare function parseClaudeModelCatalog(output: string): string[];
|
|
236
240
|
export declare function buildWorkerSetupActions(input: {
|
|
237
241
|
degradedReasons: string[];
|
|
238
242
|
playwright?: {
|
package/dist/commands/worker.js
CHANGED
|
@@ -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
|
-
|
|
981
|
-
|
|
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
|
|
1324
|
-
|
|
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 =
|
|
1392
|
-
const codexAuthenticated =
|
|
1393
|
-
const
|
|
1394
|
-
const
|
|
1395
|
-
const
|
|
1396
|
-
const
|
|
1397
|
-
const
|
|
1398
|
-
const
|
|
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 =
|
|
1403
|
-
|
|
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:
|
|
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:
|
|
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(/^(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
3340
|
+
pluginVersion: playdropPluginVersion,
|
|
3274
3341
|
});
|
|
3275
|
-
|
|
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,
|