@playdrop/playdrop-cli 0.12.37 → 0.12.39
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 +2 -2
- package/dist/apps/build.d.ts +3 -1
- package/dist/apps/build.js +4 -0
- package/dist/apps/loadCheck.d.ts +9 -2
- package/dist/apps/loadCheck.js +167 -32
- package/dist/apps/surfaceProfiles.d.ts +10 -0
- package/dist/apps/surfaceProfiles.js +64 -0
- package/dist/apps/upload.js +2 -0
- package/dist/catalogue.d.ts +7 -1
- package/dist/catalogue.js +27 -0
- package/dist/commands/check.d.ts +1 -0
- package/dist/commands/check.js +68 -2
- package/dist/commands/upload.d.ts +1 -0
- package/dist/commands/upload.js +117 -22
- package/dist/commands/worker/runtime.d.ts +6 -1
- package/dist/commands/worker/runtime.js +20 -3
- package/dist/commands/worker.d.ts +29 -3
- package/dist/commands/worker.js +114 -15
- package/dist/index.js +2 -0
- package/node_modules/@playdrop/config/client-meta.json +2 -2
- package/node_modules/@playdrop/types/dist/api.d.ts +23 -1
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts +49 -0
- package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/app-playtest-tape.js +242 -0
- package/node_modules/@playdrop/types/dist/index.d.ts +1 -0
- package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.js +2 -0
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts +3 -0
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/instrument-evidence.js +56 -5
- package/node_modules/@playdrop/types/dist/version.d.ts +5 -0
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/commands/upload.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.resolveUploadOwner = resolveUploadOwner;
|
|
|
4
4
|
exports.resolveDefaultUploadCreator = resolveDefaultUploadCreator;
|
|
5
5
|
exports.upload = upload;
|
|
6
6
|
exports.resolveWorkerPlaydropAssetRequirement = resolveWorkerPlaydropAssetRequirement;
|
|
7
|
+
exports.assertAgentNewGamePlaytestContract = assertAgentNewGamePlaytestContract;
|
|
7
8
|
exports.assertTaskPlaytestEvidenceManifest = assertTaskPlaytestEvidenceManifest;
|
|
8
9
|
exports.assertWorkerAppLocalUploadPreflight = assertWorkerAppLocalUploadPreflight;
|
|
9
10
|
exports.publishWorkerAppProject = publishWorkerAppProject;
|
|
@@ -1222,6 +1223,22 @@ function resolveWorkerPlaydropAssetRequirement(value) {
|
|
|
1222
1223
|
}
|
|
1223
1224
|
return null;
|
|
1224
1225
|
}
|
|
1226
|
+
function assertAgentNewGamePlaytestContract(task) {
|
|
1227
|
+
if (!task.primarySurface) {
|
|
1228
|
+
throw new Error('agent_task_primary_surface_required: NEW_GAME catalogue.json must declare primarySurface.');
|
|
1229
|
+
}
|
|
1230
|
+
if (!task.surfaceTargets.includes(task.primarySurface)) {
|
|
1231
|
+
throw new Error(`agent_task_primary_surface_not_supported:${task.primarySurface}`);
|
|
1232
|
+
}
|
|
1233
|
+
if (!task.playtestTapes) {
|
|
1234
|
+
throw new Error('agent_task_playtest_tapes_required: NEW_GAME catalogue.json must declare one playtest tape per supported surface.');
|
|
1235
|
+
}
|
|
1236
|
+
for (const surface of task.surfaceTargets) {
|
|
1237
|
+
if (!task.playtestTapes[surface]) {
|
|
1238
|
+
throw new Error(`agent_task_playtest_tape_missing:${surface}`);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1225
1242
|
const WORKER_SUPERVISOR_SOURCE_EXCLUDES = [
|
|
1226
1243
|
'PLAYDROP_TASK.md',
|
|
1227
1244
|
'AGENTS.md',
|
|
@@ -1670,6 +1687,70 @@ function readPlaytestEvidenceManifest(task) {
|
|
|
1670
1687
|
throw new Error(formatPlaytestEvidenceManifestError(`playtest-evidence.json must be valid JSON. ${reason}`));
|
|
1671
1688
|
}
|
|
1672
1689
|
}
|
|
1690
|
+
function assertGreyboxReport(task) {
|
|
1691
|
+
const reportPath = (0, node_path_1.join)(task.projectDir, 'greybox-report.json');
|
|
1692
|
+
if (!(0, node_fs_1.existsSync)(reportPath)) {
|
|
1693
|
+
throw new Error('agent_task_greybox_report_missing: greybox-report.json must prove the prototype and final start, agency, and restart checks.');
|
|
1694
|
+
}
|
|
1695
|
+
let report;
|
|
1696
|
+
try {
|
|
1697
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(reportPath, 'utf8'));
|
|
1698
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
1699
|
+
throw new Error('report must be a JSON object');
|
|
1700
|
+
}
|
|
1701
|
+
report = parsed;
|
|
1702
|
+
}
|
|
1703
|
+
catch (error) {
|
|
1704
|
+
throw new Error(`agent_task_greybox_report_invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
1705
|
+
}
|
|
1706
|
+
if (report.schemaVersion !== 1 || Object.keys(report).some((key) => !['schemaVersion', 'prototype', 'final'].includes(key))) {
|
|
1707
|
+
throw new Error('agent_task_greybox_report_invalid: expected schemaVersion 1 with only prototype and final sections.');
|
|
1708
|
+
}
|
|
1709
|
+
for (const sectionName of ['prototype', 'final']) {
|
|
1710
|
+
const rawSection = report[sectionName];
|
|
1711
|
+
if (!rawSection || typeof rawSection !== 'object' || Array.isArray(rawSection)) {
|
|
1712
|
+
throw new Error(`agent_task_greybox_report_invalid: ${sectionName} must contain start, agency, and restart.`);
|
|
1713
|
+
}
|
|
1714
|
+
const section = rawSection;
|
|
1715
|
+
if (Object.keys(section).length !== 3
|
|
1716
|
+
|| Object.keys(section).some((key) => !['start', 'agency', 'restart'].includes(key))) {
|
|
1717
|
+
throw new Error(`agent_task_greybox_report_invalid: ${sectionName} must contain exactly start, agency, and restart.`);
|
|
1718
|
+
}
|
|
1719
|
+
for (const checkName of ['start', 'agency', 'restart']) {
|
|
1720
|
+
const rawCheck = section[checkName];
|
|
1721
|
+
if (!rawCheck || typeof rawCheck !== 'object' || Array.isArray(rawCheck)) {
|
|
1722
|
+
throw new Error(`agent_task_greybox_report_invalid: ${sectionName}.${checkName} must be an object.`);
|
|
1723
|
+
}
|
|
1724
|
+
const check = rawCheck;
|
|
1725
|
+
const expectedKeys = checkName === 'agency'
|
|
1726
|
+
? ['passed', 'normalInput', 'controlCondition', 'observation']
|
|
1727
|
+
: ['passed', 'observation'];
|
|
1728
|
+
if (Object.keys(check).length !== expectedKeys.length
|
|
1729
|
+
|| Object.keys(check).some((key) => !expectedKeys.includes(key))
|
|
1730
|
+
|| check.passed !== true
|
|
1731
|
+
|| typeof check.observation !== 'string'
|
|
1732
|
+
|| !check.observation.trim()
|
|
1733
|
+
|| (checkName === 'agency' && (typeof check.normalInput !== 'string'
|
|
1734
|
+
|| !check.normalInput.trim()
|
|
1735
|
+
|| typeof check.controlCondition !== 'string'
|
|
1736
|
+
|| !check.controlCondition.trim()))) {
|
|
1737
|
+
throw new Error(`agent_task_greybox_report_invalid: ${sectionName}.${checkName} must be complete and passing.`);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
async function collectUploadPreflightErrors(checks) {
|
|
1743
|
+
const errors = [];
|
|
1744
|
+
for (const check of checks) {
|
|
1745
|
+
try {
|
|
1746
|
+
await check();
|
|
1747
|
+
}
|
|
1748
|
+
catch (error) {
|
|
1749
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
return errors;
|
|
1753
|
+
}
|
|
1673
1754
|
const PLAYTEST_EVIDENCE_MANIFEST_HELP = 'Expected shape: {"version":1,"entries":[{"environment":"local","url":"http://localhost:8080/...","surface":"desktop","checkedAt":"2026-07-05T00:00:00.000Z","captures":["evidence/input.png","evidence/win.png","evidence/loss.png"],"actions":["press ArrowRight","complete the final round","allow health to reach zero"],"statesObserved":["player moved right","victory overlay","defeat overlay"],"consoleErrors":[]}],"proof":{"primaryInput":{"action":"press ArrowRight","observed":"player moved right","capture":"evidence/input.png"},"win":{"action":"complete the final round","observed":"victory overlay appeared","capture":"evidence/win.png"},"loss":{"action":"allow health to reach zero","observed":"defeat overlay appeared","capture":"evidence/loss.png"}}}. NEW_GAME proof requires three distinct captures from a current non-final entry. The uploader appends hashes and the final published-route entry automatically.';
|
|
1674
1755
|
function formatMissingPlaytestEvidenceManifestError(message) {
|
|
1675
1756
|
return `agent_task_playtest_manifest_missing: ${message} ${PLAYTEST_EVIDENCE_MANIFEST_HELP}`;
|
|
@@ -1816,30 +1897,44 @@ async function assertWorkerAppLocalUploadPreflight(input) {
|
|
|
1816
1897
|
if (input.kind === 'GAME_UPDATE') {
|
|
1817
1898
|
return assertTaskPlaytestEvidenceManifest(input.task);
|
|
1818
1899
|
}
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
(
|
|
1900
|
+
let playtestProof = null;
|
|
1901
|
+
const errors = await collectUploadPreflightErrors([
|
|
1902
|
+
() => assertGreyboxReport(input.task),
|
|
1903
|
+
() => (0, listingPreflight_1.assertAgentGameHeroListingPreflight)(input.task),
|
|
1904
|
+
...(input.kind === 'NEW_GAME' ? [() => (0, listingPreflight_1.assertAgentNewGameArtDirectionPreflight)(input.task)] : []),
|
|
1905
|
+
...(input.kind === 'NEW_GAME' ? [() => assertAgentNewGamePlaytestContract(input.task)] : []),
|
|
1906
|
+
() => {
|
|
1907
|
+
if (!appTaskSatisfiesPlaydropAssetRequirement(input.task, input.playdropAssetRequirement)) {
|
|
1908
|
+
throw new Error('agent_task_playdrop_asset_dependency_missing: the creator asked for PlayDrop assets, but catalogue.json does not declare a matching uses.assets, uses.packs, or ownedAssets dependency.');
|
|
1909
|
+
}
|
|
1910
|
+
},
|
|
1911
|
+
() => (0, ownedRuntimeImageValidation_1.assertOwnedRuntimeImagesAreValid)(input.task),
|
|
1912
|
+
() => assertRequestedPlaydropAssetsAreUsedAtRuntime({
|
|
1913
|
+
task: input.task,
|
|
1914
|
+
requirement: input.playdropAssetRequirement,
|
|
1915
|
+
creatorRequest: input.creatorRequest,
|
|
1916
|
+
}),
|
|
1917
|
+
() => assertNewGameDeclaredAssetsAreUsedAtRuntime({
|
|
1918
|
+
creatorUsername: input.creatorUsername?.trim() ?? '',
|
|
1919
|
+
task: input.task,
|
|
1920
|
+
}),
|
|
1921
|
+
() => (0, listingPreflight_1.assertAgentGameScreenshotListingPreflight)(input.task),
|
|
1922
|
+
() => (0, listingPreflight_1.assertAgentGameCaptureReportPreflight)(input.task, {
|
|
1923
|
+
requireCaptureReport: input.executionTarget === 'FIRST_PARTY',
|
|
1924
|
+
}),
|
|
1925
|
+
() => {
|
|
1926
|
+
playtestProof = assertTaskPlaytestEvidenceManifest(input.task, {
|
|
1927
|
+
requireOutcomeProof: input.kind === 'NEW_GAME',
|
|
1928
|
+
});
|
|
1929
|
+
},
|
|
1930
|
+
]);
|
|
1931
|
+
if (errors.length === 1) {
|
|
1932
|
+
throw new Error(errors[0]);
|
|
1822
1933
|
}
|
|
1823
|
-
if (
|
|
1824
|
-
throw new Error(
|
|
1934
|
+
if (errors.length > 1) {
|
|
1935
|
+
throw new Error(`agent_task_upload_preflight_failed:\n${errors.map((error, index) => `${index + 1}. ${error}`).join('\n')}`);
|
|
1825
1936
|
}
|
|
1826
|
-
|
|
1827
|
-
assertRequestedPlaydropAssetsAreUsedAtRuntime({
|
|
1828
|
-
task: input.task,
|
|
1829
|
-
requirement: input.playdropAssetRequirement,
|
|
1830
|
-
creatorRequest: input.creatorRequest,
|
|
1831
|
-
});
|
|
1832
|
-
await assertNewGameDeclaredAssetsAreUsedAtRuntime({
|
|
1833
|
-
creatorUsername: input.creatorUsername?.trim() ?? '',
|
|
1834
|
-
task: input.task,
|
|
1835
|
-
});
|
|
1836
|
-
(0, listingPreflight_1.assertAgentGameScreenshotListingPreflight)(input.task);
|
|
1837
|
-
(0, listingPreflight_1.assertAgentGameCaptureReportPreflight)(input.task, {
|
|
1838
|
-
requireCaptureReport: input.executionTarget === 'FIRST_PARTY',
|
|
1839
|
-
});
|
|
1840
|
-
return assertTaskPlaytestEvidenceManifest(input.task, {
|
|
1841
|
-
requireOutcomeProof: input.kind === 'NEW_GAME',
|
|
1842
|
-
});
|
|
1937
|
+
return playtestProof;
|
|
1843
1938
|
}
|
|
1844
1939
|
// Task upload path used by "playdrop task upload": same internal pipeline as
|
|
1845
1940
|
// "playdrop project publish", but always as one PRIVATE draft version for the
|
|
@@ -77,7 +77,12 @@ export declare function buildClaudeExecArgs(input: {
|
|
|
77
77
|
pluginDir?: string | null;
|
|
78
78
|
}): string[];
|
|
79
79
|
export declare const PLAYWRIGHT_MCP_VERSION = "0.0.78";
|
|
80
|
-
export
|
|
80
|
+
export type WorkerInstrumentSurface = 'DESKTOP' | 'MOBILE_LANDSCAPE' | 'MOBILE_PORTRAIT';
|
|
81
|
+
export declare function buildPlaywrightMcpCommand(surface?: WorkerInstrumentSurface | null): {
|
|
82
|
+
command: string;
|
|
83
|
+
args: string[];
|
|
84
|
+
};
|
|
85
|
+
export declare function buildPlaywrightMcpServerConfig(surface?: WorkerInstrumentSurface | null): Record<string, unknown>;
|
|
81
86
|
export declare function buildClaudePermissionSettings(input?: {
|
|
82
87
|
workspaceDir?: string | null;
|
|
83
88
|
denyReadRoots?: string[];
|
|
@@ -17,6 +17,7 @@ exports.runLoggedProcess = runLoggedProcess;
|
|
|
17
17
|
exports.buildCodexExecArgs = buildCodexExecArgs;
|
|
18
18
|
exports.readCodexSandboxMode = readCodexSandboxMode;
|
|
19
19
|
exports.buildClaudeExecArgs = buildClaudeExecArgs;
|
|
20
|
+
exports.buildPlaywrightMcpCommand = buildPlaywrightMcpCommand;
|
|
20
21
|
exports.buildPlaywrightMcpServerConfig = buildPlaywrightMcpServerConfig;
|
|
21
22
|
exports.buildClaudePermissionSettings = buildClaudePermissionSettings;
|
|
22
23
|
exports.buildClaudeDeniedPermissionRules = buildClaudeDeniedPermissionRules;
|
|
@@ -25,6 +26,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
25
26
|
const promises_1 = require("node:fs/promises");
|
|
26
27
|
const node_os_1 = __importDefault(require("node:os"));
|
|
27
28
|
const node_path_1 = __importDefault(require("node:path"));
|
|
29
|
+
const surfaceProfiles_1 = require("../../apps/surfaceProfiles");
|
|
28
30
|
const CHILD_KILL_GRACE_MS = 10000;
|
|
29
31
|
exports.DEFAULT_CODEX_TIMEOUT_MS = 2 * 60 * 60 * 1000;
|
|
30
32
|
exports.DEFAULT_WORKER_TOKEN_CAP = 20000000;
|
|
@@ -647,12 +649,27 @@ const CLAUDE_BASE_TOOL_NAMES = [
|
|
|
647
649
|
// never through @latest drift (see the 2026-07-13 auto-update incident).
|
|
648
650
|
exports.PLAYWRIGHT_MCP_VERSION = '0.0.78';
|
|
649
651
|
const PLAYWRIGHT_MCP_ALLOWED_TOOL_RULE = 'mcp__playwright';
|
|
650
|
-
function
|
|
652
|
+
function buildPlaywrightMcpCommand(surface) {
|
|
653
|
+
const surfaceArgs = surface ? (0, surfaceProfiles_1.buildPlaywrightMcpSurfaceArgs)(surface) : [];
|
|
654
|
+
return {
|
|
655
|
+
command: 'npx',
|
|
656
|
+
args: [
|
|
657
|
+
'-y',
|
|
658
|
+
`@playwright/mcp@${exports.PLAYWRIGHT_MCP_VERSION}`,
|
|
659
|
+
'--browser',
|
|
660
|
+
'chrome',
|
|
661
|
+
'--isolated',
|
|
662
|
+
...surfaceArgs,
|
|
663
|
+
],
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function buildPlaywrightMcpServerConfig(surface) {
|
|
667
|
+
const server = buildPlaywrightMcpCommand(surface);
|
|
651
668
|
return {
|
|
652
669
|
mcpServers: {
|
|
653
670
|
playwright: {
|
|
654
|
-
command:
|
|
655
|
-
args:
|
|
671
|
+
command: server.command,
|
|
672
|
+
args: server.args,
|
|
656
673
|
},
|
|
657
674
|
},
|
|
658
675
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
-
import { type AgentExecutionTarget, type AgentRuntime, type AgentTaskNextStepSuggestion, type AgentTaskResponse, type AgentWorkerCapabilities, type AgentWorkerResponse, type AgentWorkerUpdatePolicyResponse, type WorkerTaskAssignmentV2, type WorkerTaskPluginV2, type WorkerClaimAgentTaskResponse } from '@playdrop/types';
|
|
2
|
+
import { type AgentExecutionTarget, type AgentRuntime, type AgentTaskKind, type AgentTaskNextStepSuggestion, type AgentTaskResponse, type AgentWorkerCapabilities, type AgentWorkerResponse, type AgentWorkerUpdatePolicyResponse, type JsonValue, type WorkerTaskAssignmentV2, type WorkerTaskPluginV2, type WorkerClaimAgentTaskResponse } from '@playdrop/types';
|
|
3
3
|
import { loadConfig } from '../config';
|
|
4
|
+
import { runShell } from '../shellProbe';
|
|
4
5
|
import { type WorkerPlaydropAssetRequirement } from './upload';
|
|
5
|
-
import { type LoggedProcessResult, type LoggedProcessTranscriptChunk } from './worker/runtime';
|
|
6
|
-
export { assertWorkerTokenUsageWithinCap, buildClaudeExecArgs, buildClaudePermissionSettings, buildCodexExecArgs, buildWorkerChildEnv, DEFAULT_CODEX_TIMEOUT_MS, DEFAULT_WORKER_TOKEN_CAP, extractAgentTokenUsage, extractCodexRolloutTokenUsage, extractCodexThreadId, extractCodexTokensUsed, readCodexRolloutTokenUsageForThread, readCodexSandboxMode, readEnvBoolean, runLoggedProcess } from './worker/runtime';
|
|
6
|
+
import { type WorkerInstrumentSurface, type LoggedProcessResult, type LoggedProcessTranscriptChunk } from './worker/runtime';
|
|
7
|
+
export { assertWorkerTokenUsageWithinCap, buildClaudeExecArgs, buildClaudePermissionSettings, buildCodexExecArgs, buildPlaywrightMcpCommand, buildWorkerChildEnv, DEFAULT_CODEX_TIMEOUT_MS, DEFAULT_WORKER_TOKEN_CAP, extractAgentTokenUsage, extractCodexRolloutTokenUsage, extractCodexThreadId, extractCodexTokensUsed, readCodexRolloutTokenUsageForThread, readCodexSandboxMode, readEnvBoolean, runLoggedProcess } from './worker/runtime';
|
|
7
8
|
export type { AgentTokenUsage, LoggedProcessResult, LoggedProcessTranscriptChunk } from './worker/runtime';
|
|
8
9
|
export declare const WORKER_SESSION_EXPIRED_MESSAGE = "worker session expired: run \"playdrop auth login\" and start the worker again";
|
|
9
10
|
export declare const 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.";
|
|
@@ -126,6 +127,24 @@ type WorkerTaskState = {
|
|
|
126
127
|
leaseExpiresAt: string | null;
|
|
127
128
|
claimedAt: string;
|
|
128
129
|
};
|
|
130
|
+
type WorkerTaskContextFile = {
|
|
131
|
+
taskId: number;
|
|
132
|
+
attempt: number;
|
|
133
|
+
taskToken: string;
|
|
134
|
+
taskType: string;
|
|
135
|
+
metadata: Record<string, JsonValue>;
|
|
136
|
+
kind: AgentTaskKind;
|
|
137
|
+
creatorUsername: string;
|
|
138
|
+
creatorRequest: string;
|
|
139
|
+
target: AgentExecutionTarget;
|
|
140
|
+
outputAppName: string | null;
|
|
141
|
+
outputVersion: string;
|
|
142
|
+
remixSourceRef?: string | null;
|
|
143
|
+
allowedTemplateKeys?: string[];
|
|
144
|
+
reviewAppVersionId?: number | null;
|
|
145
|
+
devPort: number;
|
|
146
|
+
env: string;
|
|
147
|
+
};
|
|
129
148
|
type WorkerCodexModel = {
|
|
130
149
|
model: string;
|
|
131
150
|
reasoningEffort: string;
|
|
@@ -147,6 +166,7 @@ export declare function claimBackoffDelayMs(currentMs: number, random?: () => nu
|
|
|
147
166
|
export declare function resolveWorkerHomeDir(): string;
|
|
148
167
|
export declare const WORKER_TASK_WORKSPACE_RETENTION = 20;
|
|
149
168
|
export declare function pruneWorkerTaskWorkspaces(incomingWorkspaceDir: string, retention?: number): Promise<string[]>;
|
|
169
|
+
export declare function resolveWorkerInstrumentSurface(taskContext: Pick<WorkerTaskContextFile, 'kind' | 'metadata'>): WorkerInstrumentSurface | null;
|
|
150
170
|
export declare function hasAgentTaskUploadedArtifact(task: Pick<AgentTaskResponse, 'appId' | 'appVersionId'>): boolean;
|
|
151
171
|
export declare function appendWorkerTranscriptChunks(input: {
|
|
152
172
|
client: Pick<ApiClient, 'workerAppendAgentTaskTranscriptChunks'>;
|
|
@@ -176,6 +196,7 @@ export declare function assertAssignmentPluginBundleExtractionBounds(buffer: Buf
|
|
|
176
196
|
export declare function stageAssignmentPluginBundle(input: {
|
|
177
197
|
workspaceDir: string;
|
|
178
198
|
pluginBundle: WorkerTaskPluginV2;
|
|
199
|
+
taskKind?: AgentTaskKind;
|
|
179
200
|
}): Promise<string>;
|
|
180
201
|
export declare function resolveDevPluginWorkingTree(input: {
|
|
181
202
|
workingTree?: string;
|
|
@@ -328,6 +349,11 @@ export declare function assertLaunchAgentPlistCompatible(input: {
|
|
|
328
349
|
wrapperPath: string;
|
|
329
350
|
canonicalPlistPath: string;
|
|
330
351
|
}): void;
|
|
352
|
+
export declare function launchManagedWorker(label: string, plistPath: string, options: {
|
|
353
|
+
registrationChanged: boolean;
|
|
354
|
+
runCommand?: typeof runShell;
|
|
355
|
+
uid?: number;
|
|
356
|
+
}): void;
|
|
331
357
|
export declare function showWorkerStatus(options?: WorkerStatusOptions): Promise<WorkerStatusPayload>;
|
|
332
358
|
export declare function setupWorker(options?: WorkerSetupOptions): Promise<void>;
|
|
333
359
|
export declare function instrumentTaskChromeTitleMatchers(assignment: WorkerTaskAssignmentV2): string[];
|
package/dist/commands/worker.js
CHANGED
|
@@ -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_TASK_WORKSPACE_RETENTION = 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;
|
|
6
|
+
exports.WORKER_TASK_WORKSPACE_RETENTION = 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.buildPlaywrightMcpCommand = 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;
|
|
@@ -12,6 +12,7 @@ exports.nextClaimBackoffMs = nextClaimBackoffMs;
|
|
|
12
12
|
exports.claimBackoffDelayMs = claimBackoffDelayMs;
|
|
13
13
|
exports.resolveWorkerHomeDir = resolveWorkerHomeDir;
|
|
14
14
|
exports.pruneWorkerTaskWorkspaces = pruneWorkerTaskWorkspaces;
|
|
15
|
+
exports.resolveWorkerInstrumentSurface = resolveWorkerInstrumentSurface;
|
|
15
16
|
exports.hasAgentTaskUploadedArtifact = hasAgentTaskUploadedArtifact;
|
|
16
17
|
exports.appendWorkerTranscriptChunks = appendWorkerTranscriptChunks;
|
|
17
18
|
exports.readTaskNextStepsFile = readTaskNextStepsFile;
|
|
@@ -65,6 +66,7 @@ exports.withTaskUploadLock = withTaskUploadLock;
|
|
|
65
66
|
exports.readActivePersonalWorkerRuntimeState = readActivePersonalWorkerRuntimeState;
|
|
66
67
|
exports.parseLaunchAgentProgramArguments = parseLaunchAgentProgramArguments;
|
|
67
68
|
exports.assertLaunchAgentPlistCompatible = assertLaunchAgentPlistCompatible;
|
|
69
|
+
exports.launchManagedWorker = launchManagedWorker;
|
|
68
70
|
exports.showWorkerStatus = showWorkerStatus;
|
|
69
71
|
exports.setupWorker = setupWorker;
|
|
70
72
|
exports.instrumentTaskChromeTitleMatchers = instrumentTaskChromeTitleMatchers;
|
|
@@ -109,6 +111,7 @@ Object.defineProperty(exports, "assertWorkerTokenUsageWithinCap", { enumerable:
|
|
|
109
111
|
Object.defineProperty(exports, "buildClaudeExecArgs", { enumerable: true, get: function () { return runtime_2.buildClaudeExecArgs; } });
|
|
110
112
|
Object.defineProperty(exports, "buildClaudePermissionSettings", { enumerable: true, get: function () { return runtime_2.buildClaudePermissionSettings; } });
|
|
111
113
|
Object.defineProperty(exports, "buildCodexExecArgs", { enumerable: true, get: function () { return runtime_2.buildCodexExecArgs; } });
|
|
114
|
+
Object.defineProperty(exports, "buildPlaywrightMcpCommand", { enumerable: true, get: function () { return runtime_2.buildPlaywrightMcpCommand; } });
|
|
112
115
|
Object.defineProperty(exports, "buildWorkerChildEnv", { enumerable: true, get: function () { return runtime_2.buildWorkerChildEnv; } });
|
|
113
116
|
Object.defineProperty(exports, "DEFAULT_CODEX_TIMEOUT_MS", { enumerable: true, get: function () { return runtime_2.DEFAULT_CODEX_TIMEOUT_MS; } });
|
|
114
117
|
Object.defineProperty(exports, "DEFAULT_WORKER_TOKEN_CAP", { enumerable: true, get: function () { return runtime_2.DEFAULT_WORKER_TOKEN_CAP; } });
|
|
@@ -148,6 +151,7 @@ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
|
148
151
|
exports.WORKER_SESSION_EXPIRED_MESSAGE = 'worker session expired: run "playdrop auth login" and start the worker again';
|
|
149
152
|
const WORKER_CONTEXT_ALLOWED_COMMANDS = [
|
|
150
153
|
['task', 'context'],
|
|
154
|
+
['task', 'help'],
|
|
151
155
|
['task', 'report'],
|
|
152
156
|
['task', 'report-catalogue'],
|
|
153
157
|
['task', 'claim-slug'],
|
|
@@ -170,6 +174,7 @@ const WORKER_CONTEXT_ALLOWED_COMMANDS = [
|
|
|
170
174
|
['search'],
|
|
171
175
|
['detail'],
|
|
172
176
|
['versions', 'browse'],
|
|
177
|
+
['tags', 'browse'],
|
|
173
178
|
['documentation', 'browse'],
|
|
174
179
|
['documentation', 'read'],
|
|
175
180
|
['help'],
|
|
@@ -567,6 +572,48 @@ function buildWorkerTaskContextV2(assignment) {
|
|
|
567
572
|
target: assignment.task.executionTarget,
|
|
568
573
|
});
|
|
569
574
|
}
|
|
575
|
+
function normalizeWorkerInstrumentSurface(value) {
|
|
576
|
+
return value === 'DESKTOP' || value === 'MOBILE_LANDSCAPE' || value === 'MOBILE_PORTRAIT'
|
|
577
|
+
? value
|
|
578
|
+
: null;
|
|
579
|
+
}
|
|
580
|
+
function resolveWorkerInstrumentSurface(taskContext) {
|
|
581
|
+
const playdrop = taskContext.metadata.playdrop;
|
|
582
|
+
if (!playdrop || typeof playdrop !== 'object' || Array.isArray(playdrop)) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
const metadata = playdrop;
|
|
586
|
+
if (taskContext.kind === 'GAME_REVIEW') {
|
|
587
|
+
const reviewTarget = metadata.reviewTarget;
|
|
588
|
+
if (!reviewTarget || typeof reviewTarget !== 'object' || Array.isArray(reviewTarget)) {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
return normalizeWorkerInstrumentSurface(reviewTarget.primarySurface);
|
|
592
|
+
}
|
|
593
|
+
if (taskContext.kind === 'GAME_EVAL') {
|
|
594
|
+
const evalTarget = metadata.evalTarget;
|
|
595
|
+
if (!evalTarget || typeof evalTarget !== 'object' || Array.isArray(evalTarget)) {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
const targets = evalTarget.targets;
|
|
599
|
+
if (!Array.isArray(targets) || targets.length === 0) {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
const surfaces = new Set();
|
|
603
|
+
for (const target of targets) {
|
|
604
|
+
if (!target || typeof target !== 'object' || Array.isArray(target)) {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
const surface = normalizeWorkerInstrumentSurface(target.primarySurface);
|
|
608
|
+
if (!surface) {
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
surfaces.add(surface);
|
|
612
|
+
}
|
|
613
|
+
return surfaces.size === 1 ? Array.from(surfaces)[0] ?? null : null;
|
|
614
|
+
}
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
570
617
|
function workerTaskContextPath(workspaceDir) {
|
|
571
618
|
return node_path_1.default.join(workspaceDir, '.playdrop', 'task.json');
|
|
572
619
|
}
|
|
@@ -1062,9 +1109,24 @@ function readAgentBundleManifest(extractedRoot) {
|
|
|
1062
1109
|
if (!destinations.has('CLAUDE.md')) {
|
|
1063
1110
|
throw new Error('agent_bundle_claude_file_missing');
|
|
1064
1111
|
}
|
|
1112
|
+
const taskAgentOverlays = manifest.workspace.taskAgentOverlays;
|
|
1113
|
+
if (taskAgentOverlays !== undefined) {
|
|
1114
|
+
if (!taskAgentOverlays || typeof taskAgentOverlays !== 'object' || Array.isArray(taskAgentOverlays)) {
|
|
1115
|
+
throw new Error('agent_bundle_task_agent_overlays_invalid');
|
|
1116
|
+
}
|
|
1117
|
+
for (const [rawKind, rawSource] of Object.entries(taskAgentOverlays)) {
|
|
1118
|
+
if (!WORKER_SUPPORTED_KINDS.includes(rawKind)) {
|
|
1119
|
+
throw new Error(`agent_bundle_task_agent_overlay_kind_invalid:${rawKind}`);
|
|
1120
|
+
}
|
|
1121
|
+
const source = normalizeAgentBundlePath(extractedRoot, rawSource, 'agent_bundle_task_agent_overlay_source_invalid');
|
|
1122
|
+
if (!isFile(source)) {
|
|
1123
|
+
throw new Error(`agent_bundle_task_agent_overlay_source_missing:${rawSource ?? ''}`);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1065
1127
|
return { manifest, pluginRoot };
|
|
1066
1128
|
}
|
|
1067
|
-
async function stageAgentBundleWorkspaceFiles(extractedRoot, workspaceDir) {
|
|
1129
|
+
async function stageAgentBundleWorkspaceFiles(extractedRoot, workspaceDir, taskKind) {
|
|
1068
1130
|
const resolved = readAgentBundleManifest(extractedRoot);
|
|
1069
1131
|
for (const entry of resolved.manifest.workspace.files) {
|
|
1070
1132
|
const source = normalizeAgentBundlePath(extractedRoot, entry.source, 'agent_bundle_workspace_source_invalid');
|
|
@@ -1072,6 +1134,16 @@ async function stageAgentBundleWorkspaceFiles(extractedRoot, workspaceDir) {
|
|
|
1072
1134
|
await (0, promises_1.mkdir)(node_path_1.default.dirname(destination), { recursive: true });
|
|
1073
1135
|
await (0, promises_1.copyFile)(source, destination);
|
|
1074
1136
|
}
|
|
1137
|
+
const overlaySource = taskKind
|
|
1138
|
+
? resolved.manifest.workspace.taskAgentOverlays?.[taskKind]
|
|
1139
|
+
: null;
|
|
1140
|
+
if (overlaySource) {
|
|
1141
|
+
const agentsPath = resolveWorkspaceFileDestination(workspaceDir, 'AGENTS.md');
|
|
1142
|
+
const overlayPath = normalizeAgentBundlePath(extractedRoot, overlaySource, 'agent_bundle_task_agent_overlay_source_invalid');
|
|
1143
|
+
const base = (0, node_fs_1.readFileSync)(agentsPath, 'utf8').trimEnd();
|
|
1144
|
+
const overlay = (0, node_fs_1.readFileSync)(overlayPath, 'utf8').trim();
|
|
1145
|
+
await (0, promises_1.writeFile)(agentsPath, `${base}\n\n${overlay}\n`, 'utf8');
|
|
1146
|
+
}
|
|
1075
1147
|
return resolved.pluginRoot;
|
|
1076
1148
|
}
|
|
1077
1149
|
async function stageAssignmentPluginBundle(input) {
|
|
@@ -1127,7 +1199,7 @@ async function stageAssignmentPluginBundle(input) {
|
|
|
1127
1199
|
await (0, promises_1.mkdir)(node_path_1.default.dirname(destination), { recursive: true });
|
|
1128
1200
|
await (0, promises_1.writeFile)(destination, Buffer.from(content));
|
|
1129
1201
|
}
|
|
1130
|
-
return await stageAgentBundleWorkspaceFiles(pluginRoot, input.workspaceDir);
|
|
1202
|
+
return await stageAgentBundleWorkspaceFiles(pluginRoot, input.workspaceDir, input.taskKind);
|
|
1131
1203
|
}
|
|
1132
1204
|
function resolveDevPluginWorkingTree(input) {
|
|
1133
1205
|
const workingTree = input.workingTree?.trim();
|
|
@@ -1946,8 +2018,22 @@ async function runCodex(input) {
|
|
|
1946
2018
|
env: pluginEnv,
|
|
1947
2019
|
});
|
|
1948
2020
|
}
|
|
2021
|
+
if (input.enablePlaywrightMcp) {
|
|
2022
|
+
const server = (0, runtime_1.buildPlaywrightMcpCommand)(input.instrumentSurface);
|
|
2023
|
+
await execFileAsync('codex', ['mcp', 'add', 'playwright', '--', server.command, ...server.args], {
|
|
2024
|
+
cwd: input.workspaceDir,
|
|
2025
|
+
env: {
|
|
2026
|
+
...node_process_1.default.env,
|
|
2027
|
+
HOME: homeDir,
|
|
2028
|
+
CODEX_HOME: codexHomeDir,
|
|
2029
|
+
},
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
1949
2032
|
let observedCodexThreadId = null;
|
|
1950
2033
|
let codexThreadProbe = '';
|
|
2034
|
+
const screenshotCollector = input.enablePlaywrightMcp
|
|
2035
|
+
? (0, instrument_evidence_1.createWorkerInstrumentScreenshotCollector)(input.workspaceDir)
|
|
2036
|
+
: null;
|
|
1951
2037
|
const result = await (0, runtime_1.runLoggedProcess)({
|
|
1952
2038
|
command: 'codex',
|
|
1953
2039
|
args: (0, runtime_1.buildCodexExecArgs)({
|
|
@@ -1981,9 +2067,13 @@ async function runCodex(input) {
|
|
|
1981
2067
|
}
|
|
1982
2068
|
await input.onTranscriptChunks(chunks);
|
|
1983
2069
|
},
|
|
2070
|
+
...(screenshotCollector
|
|
2071
|
+
? { onStdoutText: (text) => screenshotCollector.push(text) > 0 }
|
|
2072
|
+
: {}),
|
|
1984
2073
|
transcriptFlushIntervalMs: runtime_1.DEFAULT_TRANSCRIPT_FLUSH_INTERVAL_MS,
|
|
1985
2074
|
onChild: input.onChild,
|
|
1986
2075
|
});
|
|
2076
|
+
screenshotCollector?.finish();
|
|
1987
2077
|
observedCodexThreadId = observedCodexThreadId
|
|
1988
2078
|
?? (0, runtime_1.extractCodexThreadId)(`${result.stdout}\n${result.stderr}\n${result.outputTail}`);
|
|
1989
2079
|
if (!observedCodexThreadId) {
|
|
@@ -2026,7 +2116,7 @@ async function runClaude(input) {
|
|
|
2026
2116
|
if (input.enablePlaywrightMcp) {
|
|
2027
2117
|
playwrightMcpConfigPath = node_path_1.default.join(input.workspaceDir, '.playdrop', 'claude-run', 'playwright-mcp.json');
|
|
2028
2118
|
(0, node_fs_1.mkdirSync)(node_path_1.default.dirname(playwrightMcpConfigPath), { recursive: true });
|
|
2029
|
-
(0, node_fs_1.writeFileSync)(playwrightMcpConfigPath, `${JSON.stringify((0, runtime_1.buildPlaywrightMcpServerConfig)(), null, 2)}\n`, 'utf8');
|
|
2119
|
+
(0, node_fs_1.writeFileSync)(playwrightMcpConfigPath, `${JSON.stringify((0, runtime_1.buildPlaywrightMcpServerConfig)(input.instrumentSurface), null, 2)}\n`, 'utf8');
|
|
2030
2120
|
}
|
|
2031
2121
|
const result = await (0, runtime_1.runLoggedProcess)({
|
|
2032
2122
|
command: 'claude',
|
|
@@ -3381,21 +3471,26 @@ async function writeManagedWorkerLaunchAgent(input) {
|
|
|
3381
3471
|
</dict>
|
|
3382
3472
|
</plist>
|
|
3383
3473
|
`;
|
|
3474
|
+
const previousPlist = (0, node_fs_1.existsSync)(plistPath) ? (0, node_fs_1.readFileSync)(plistPath, 'utf8') : null;
|
|
3475
|
+
const registrationChanged = previousPlist !== plist;
|
|
3384
3476
|
await (0, promises_1.writeFile)(plistPath, plist, 'utf8');
|
|
3385
|
-
return { label, plistPath };
|
|
3477
|
+
return { label, plistPath, registrationChanged };
|
|
3386
3478
|
}
|
|
3387
|
-
function launchManagedWorker(label, plistPath) {
|
|
3388
|
-
const uid = typeof node_process_1.default.getuid === 'function' ? node_process_1.default.getuid() : null;
|
|
3479
|
+
function launchManagedWorker(label, plistPath, options) {
|
|
3480
|
+
const uid = options.uid ?? (typeof node_process_1.default.getuid === 'function' ? node_process_1.default.getuid() : null);
|
|
3389
3481
|
if (uid === null) {
|
|
3390
3482
|
throw new Error('worker_setup_launchagent_uid_unavailable');
|
|
3391
3483
|
}
|
|
3484
|
+
const runCommand = options.runCommand ?? shellProbe_1.runShell;
|
|
3392
3485
|
const domain = `gui/${uid}`;
|
|
3393
|
-
(
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3486
|
+
if (options.registrationChanged) {
|
|
3487
|
+
runCommand(`launchctl bootout ${shellQuote(`${domain}/${label}`)}`);
|
|
3488
|
+
const bootstrap = runCommand(`launchctl bootstrap ${shellQuote(domain)} ${shellQuote(plistPath)}`);
|
|
3489
|
+
if (bootstrap.exitCode !== 0) {
|
|
3490
|
+
throw new Error(`worker_setup_launchagent_start_failed: launchctl bootstrap exited with code ${bootstrap.exitCode}. ${bootstrap.output.slice(0, 300)}`);
|
|
3491
|
+
}
|
|
3397
3492
|
}
|
|
3398
|
-
const kickstart = (
|
|
3493
|
+
const kickstart = runCommand(`launchctl kickstart -k ${shellQuote(`${domain}/${label}`)}`);
|
|
3399
3494
|
if (kickstart.exitCode !== 0) {
|
|
3400
3495
|
throw new Error(`worker_setup_launchagent_start_failed: launchctl kickstart exited with code ${kickstart.exitCode}. ${kickstart.output.slice(0, 300)}`);
|
|
3401
3496
|
}
|
|
@@ -3532,7 +3627,7 @@ async function setupWorker(options = {}) {
|
|
|
3532
3627
|
}
|
|
3533
3628
|
verifyNpmGlobalInstallPreconditions();
|
|
3534
3629
|
const { wrapperPath, policyFile } = await writeManagedWorkerWrapper({ env, workerName, workerKey });
|
|
3535
|
-
const { label, plistPath } = await writeManagedWorkerLaunchAgent({ env, workerName, wrapperPath });
|
|
3630
|
+
const { label, plistPath, registrationChanged } = await writeManagedWorkerLaunchAgent({ env, workerName, wrapperPath });
|
|
3536
3631
|
const status = await resolveWorkerStatusPayload({ env });
|
|
3537
3632
|
const activeTaskCount = status.server.worker?.activeTaskCount ?? 0;
|
|
3538
3633
|
const shouldStart = options.start !== false;
|
|
@@ -3550,7 +3645,7 @@ async function setupWorker(options = {}) {
|
|
|
3550
3645
|
return;
|
|
3551
3646
|
}
|
|
3552
3647
|
if (shouldStart || options.restart) {
|
|
3553
|
-
launchManagedWorker(label, plistPath);
|
|
3648
|
+
launchManagedWorker(label, plistPath, { registrationChanged });
|
|
3554
3649
|
}
|
|
3555
3650
|
(0, output_1.printSuccess)('Managed worker setup complete.', [
|
|
3556
3651
|
`Wrapper: ${wrapperPath}`,
|
|
@@ -4027,11 +4122,12 @@ async function startWorker(options = {}) {
|
|
|
4027
4122
|
};
|
|
4028
4123
|
await stageAssignmentWorkspaceV2(workspaceDir, assignment.workspace);
|
|
4029
4124
|
const assignmentPluginRoot = devPluginWorkingTree
|
|
4030
|
-
? await stageAgentBundleWorkspaceFiles(devPluginWorkingTree, workspaceDir)
|
|
4125
|
+
? await stageAgentBundleWorkspaceFiles(devPluginWorkingTree, workspaceDir, task.kind)
|
|
4031
4126
|
: assignment.plugin
|
|
4032
4127
|
? await stageAssignmentPluginBundle({
|
|
4033
4128
|
workspaceDir,
|
|
4034
4129
|
pluginBundle: assignment.plugin,
|
|
4130
|
+
taskKind: task.kind,
|
|
4035
4131
|
})
|
|
4036
4132
|
: null;
|
|
4037
4133
|
availableSkillPaths = listPluginSkillPaths(assignmentPluginRoot);
|
|
@@ -4118,6 +4214,8 @@ async function startWorker(options = {}) {
|
|
|
4118
4214
|
devPort,
|
|
4119
4215
|
codexModel: codexModel ?? resolveCodexModel(assignment.agent.model),
|
|
4120
4216
|
taskPluginRoot: assignmentPluginRoot,
|
|
4217
|
+
enablePlaywrightMcp: assignment.agent.browser === 'PLAYWRIGHT',
|
|
4218
|
+
instrumentSurface: resolveWorkerInstrumentSurface(taskContext),
|
|
4121
4219
|
imagePaths: imageAttachments.map((attachment) => attachment.path),
|
|
4122
4220
|
onTranscriptChunks: async (chunks) => {
|
|
4123
4221
|
await appendTranscriptChunks(chunks);
|
|
@@ -4135,6 +4233,7 @@ async function startWorker(options = {}) {
|
|
|
4135
4233
|
prompt,
|
|
4136
4234
|
enableChrome: assignment.agent.browser === 'CHROME',
|
|
4137
4235
|
enablePlaywrightMcp: assignment.agent.browser === 'PLAYWRIGHT',
|
|
4236
|
+
instrumentSurface: resolveWorkerInstrumentSurface(taskContext),
|
|
4138
4237
|
envName: env,
|
|
4139
4238
|
workerUsername: username,
|
|
4140
4239
|
taskId: task.id,
|
package/dist/index.js
CHANGED
|
@@ -1021,12 +1021,14 @@ project
|
|
|
1021
1021
|
.option('--timeout <seconds>', 'Load-check timeout in seconds')
|
|
1022
1022
|
.option('--screenshot <path>', 'Screenshot output path')
|
|
1023
1023
|
.option('--actions <path>', 'JSON action script to dispatch after the game frame is ready')
|
|
1024
|
+
.option('--tape <surface>', 'Run matched idle and catalogue playtest-tape captures on a declared surface')
|
|
1024
1025
|
.action(async (target, opts = {}) => {
|
|
1025
1026
|
await (0, check_1.check)(target, {
|
|
1026
1027
|
app: opts.app,
|
|
1027
1028
|
timeout: opts.timeout,
|
|
1028
1029
|
screenshot: opts.screenshot,
|
|
1029
1030
|
actions: opts.actions,
|
|
1031
|
+
tape: opts.tape,
|
|
1030
1032
|
});
|
|
1031
1033
|
});
|
|
1032
1034
|
const projectCapture = project.command('capture').description('Record game media with the native macOS recorder');
|