@playdrop/playdrop-cli 0.12.20 → 0.12.22
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/commands/worker/instrument-evidence.d.ts +1 -1
- package/dist/commands/worker/instrument-evidence.js +33 -1
- package/dist/commands/worker.js +4 -3
- package/dist/index.js +2 -2
- package/node_modules/@playdrop/config/client-meta.json +1 -1
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/instrument-evidence.js +10 -20
- package/package.json +1 -1
package/config/client-meta.json
CHANGED
|
@@ -9,7 +9,7 @@ export declare function saveInstrumentEvidenceCapture(input: {
|
|
|
9
9
|
evidenceDir: string;
|
|
10
10
|
groupId: string;
|
|
11
11
|
name: string;
|
|
12
|
-
screenshotId
|
|
12
|
+
screenshotId?: string;
|
|
13
13
|
}): Promise<InstrumentEvidenceCapture>;
|
|
14
14
|
export declare function readAndValidateInstrumentEvidenceManifest(input: {
|
|
15
15
|
workspaceDir: string;
|
|
@@ -99,6 +99,35 @@ function createWorkerInstrumentScreenshotCollector(workspaceDir) {
|
|
|
99
99
|
finish: () => chromeParser.finish() + playwrightParser.finish(),
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
|
+
function resolveLatestUnusedScreenshotId(workspaceDir, index) {
|
|
103
|
+
const used = new Set();
|
|
104
|
+
const evidenceRoot = node_path_1.default.resolve(workspaceDir);
|
|
105
|
+
const walk = (dir) => {
|
|
106
|
+
for (const entry of (0, node_fs_1.existsSync)(dir) ? (0, node_fs_1.readdirSync)(dir, { withFileTypes: true }) : []) {
|
|
107
|
+
const entryPath = node_path_1.default.join(dir, entry.name);
|
|
108
|
+
if (entry.isDirectory()) {
|
|
109
|
+
walk(entryPath);
|
|
110
|
+
}
|
|
111
|
+
else if (entry.name === exports.INSTRUMENT_EVIDENCE_MANIFEST_FILE) {
|
|
112
|
+
const manifest = readJsonRecord(entryPath, 'invalid_instrument_evidence_manifest');
|
|
113
|
+
for (const capture of Object.values(manifest.captures ?? {})) {
|
|
114
|
+
if (capture?.screenshotId) {
|
|
115
|
+
used.add(capture.screenshotId);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
walk(evidenceRoot);
|
|
122
|
+
const candidates = Object.values(index.screenshots)
|
|
123
|
+
.filter((screenshot) => !used.has(screenshot.screenshotId))
|
|
124
|
+
.sort((a, b) => a.capturedAt.localeCompare(b.capturedAt));
|
|
125
|
+
const latest = candidates.at(-1);
|
|
126
|
+
if (!latest) {
|
|
127
|
+
throw new Error('instrument_screenshot_none_available');
|
|
128
|
+
}
|
|
129
|
+
return latest.screenshotId;
|
|
130
|
+
}
|
|
102
131
|
function normalizeEvidenceName(value) {
|
|
103
132
|
const normalized = value.trim();
|
|
104
133
|
if (!types_1.INSTRUMENT_EVIDENCE_NAMES.includes(normalized)) {
|
|
@@ -131,8 +160,11 @@ function readManifestIfPresent(evidenceDir) {
|
|
|
131
160
|
async function saveInstrumentEvidenceCapture(input) {
|
|
132
161
|
const evidenceName = normalizeEvidenceName(input.name);
|
|
133
162
|
const groupId = normalizeGroupId(input.groupId);
|
|
134
|
-
const screenshotId = input.screenshotId.trim();
|
|
135
163
|
const index = readScreenshotIndex(input.workspaceDir);
|
|
164
|
+
const requestedId = input.screenshotId?.trim() ?? '';
|
|
165
|
+
// With no id, persist the capture the judge just took: the most recent one not already used as
|
|
166
|
+
// evidence. The judge never has to track ids, and a failed capture simply is not there to pick.
|
|
167
|
+
const screenshotId = requestedId || resolveLatestUnusedScreenshotId(input.workspaceDir, index);
|
|
136
168
|
const screenshot = index.screenshots[screenshotId];
|
|
137
169
|
if (!screenshot) {
|
|
138
170
|
throw new Error(`instrument_screenshot_not_found:${screenshotId}`);
|
package/dist/commands/worker.js
CHANGED
|
@@ -4892,9 +4892,10 @@ async function saveTaskEvidence(options) {
|
|
|
4892
4892
|
}
|
|
4893
4893
|
const evidenceDir = options.evidenceDir?.trim();
|
|
4894
4894
|
const name = options.name?.trim();
|
|
4895
|
+
// --screenshot-id is optional: with no id the worker persists the capture the agent just took.
|
|
4895
4896
|
const screenshotId = options.screenshotId?.trim();
|
|
4896
4897
|
const groupId = options.group?.trim() || (taskContext.kind === 'GAME_REVIEW' ? 'review' : '');
|
|
4897
|
-
if (!evidenceDir || !name || !
|
|
4898
|
+
if (!evidenceDir || !name || !groupId) {
|
|
4898
4899
|
throw new Error('task_evidence_options_required');
|
|
4899
4900
|
}
|
|
4900
4901
|
const capture = await (0, instrument_evidence_1.saveInstrumentEvidenceCapture)({
|
|
@@ -4902,9 +4903,9 @@ async function saveTaskEvidence(options) {
|
|
|
4902
4903
|
evidenceDir,
|
|
4903
4904
|
groupId,
|
|
4904
4905
|
name,
|
|
4905
|
-
screenshotId,
|
|
4906
|
+
...(screenshotId ? { screenshotId } : {}),
|
|
4906
4907
|
});
|
|
4907
|
-
(0, output_1.printSuccess)(`Saved ${capture.name} evidence from
|
|
4908
|
+
(0, output_1.printSuccess)(`Saved ${capture.name} evidence from screenshot ${capture.screenshotId} to ${capture.fileName}.`);
|
|
4908
4909
|
}
|
|
4909
4910
|
const INSTRUMENT_ERROR_REASONS = new Set([
|
|
4910
4911
|
'auth_state',
|
package/dist/index.js
CHANGED
|
@@ -287,9 +287,9 @@ task
|
|
|
287
287
|
});
|
|
288
288
|
task
|
|
289
289
|
.command('evidence')
|
|
290
|
-
.description('Persist
|
|
290
|
+
.description('Persist the screenshot you just took as task evidence')
|
|
291
291
|
.requiredOption('--name <name>', 'Evidence state: first-frame, core, win, or loss')
|
|
292
|
-
.
|
|
292
|
+
.option('--screenshot-id <id>', 'Screenshot id; omit to persist the capture you just took')
|
|
293
293
|
.requiredOption('--evidence-dir <path>', 'Evidence output directory')
|
|
294
294
|
.option('--group <id>', 'Evidence group id; required for GAME_EVAL')
|
|
295
295
|
.action(async (opts) => {
|
|
@@ -30,7 +30,6 @@ export declare const PLAYWRIGHT_MCP_SCREENSHOT_TOOL = "mcp__playwright__browser_
|
|
|
30
30
|
export declare const PLAYWRIGHT_MCP_CONTROLLED_TAB_ID = 1;
|
|
31
31
|
export declare const PLAYWRIGHT_MCP_SCREENSHOT_BUDGET = 100;
|
|
32
32
|
export declare function isInstrumentScreenshotId(value: string): boolean;
|
|
33
|
-
export declare function normalizePlaywrightScreenshotId(value: unknown): string | null;
|
|
34
33
|
export declare class ClaudeChromeScreenshotStreamParser {
|
|
35
34
|
private readonly onScreenshot;
|
|
36
35
|
private buffer;
|
|
@@ -46,9 +45,10 @@ export declare class ClaudeChromeScreenshotStreamParser {
|
|
|
46
45
|
export declare class PlaywrightMcpScreenshotStreamParser {
|
|
47
46
|
private readonly onScreenshot;
|
|
48
47
|
private buffer;
|
|
49
|
-
private readonly
|
|
48
|
+
private readonly pendingScreenshotToolUseIds;
|
|
50
49
|
private readonly handledToolUseIds;
|
|
51
50
|
private screenshotCallCount;
|
|
51
|
+
private sequence;
|
|
52
52
|
constructor(onScreenshot: (screenshot: ClaudeChromeScreenshot) => void);
|
|
53
53
|
push(chunk: string): number;
|
|
54
54
|
finish(): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument-evidence.d.ts","sourceRoot":"","sources":["../src/instrument-evidence.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,iDAK5B,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhF,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,YAAY,GAAG,WAAW,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,YAAY,GAAG,WAAW,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,sBAAsB,EAAE,yBAAyB,CAAC,CAAC,CAAC;CAC9E,CAAC;AAMF,eAAO,MAAM,8BAA8B,6CAA6C,CAAC;AACzF,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAGlD,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAKpD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/D;
|
|
1
|
+
{"version":3,"file":"instrument-evidence.d.ts","sourceRoot":"","sources":["../src/instrument-evidence.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,iDAK5B,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhF,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,YAAY,GAAG,WAAW,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,YAAY,GAAG,WAAW,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,sBAAsB,EAAE,yBAAyB,CAAC,CAAC,CAAC;CAC9E,CAAC;AAMF,eAAO,MAAM,8BAA8B,6CAA6C,CAAC;AACzF,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAGlD,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAKpD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/D;AAiDD,qBAAa,kCAAkC;IAKjC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAJzC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAA+B;IACzE,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqB;gBAE7B,YAAY,EAAE,CAAC,UAAU,EAAE,sBAAsB,KAAK,IAAI;IAEvF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAqB3B,MAAM,IAAI,MAAM;IAMhB,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,iBAAiB;CAiE1B;AAMD,qBAAa,mCAAmC;IAOlC,OAAO,CAAC,QAAQ,CAAC,YAAY;IANzC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAqB;IACjE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;IACvD,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAK;gBAEQ,YAAY,EAAE,CAAC,UAAU,EAAE,sBAAsB,KAAK,IAAI;IAEvF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAqB3B,MAAM,IAAI,MAAM;IAMhB,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,iBAAiB;CA+C1B"}
|
|
@@ -23,8 +23,7 @@ __export(instrument_evidence_exports, {
|
|
|
23
23
|
PLAYWRIGHT_MCP_SCREENSHOT_BUDGET: () => PLAYWRIGHT_MCP_SCREENSHOT_BUDGET,
|
|
24
24
|
PLAYWRIGHT_MCP_SCREENSHOT_TOOL: () => PLAYWRIGHT_MCP_SCREENSHOT_TOOL,
|
|
25
25
|
PlaywrightMcpScreenshotStreamParser: () => PlaywrightMcpScreenshotStreamParser,
|
|
26
|
-
isInstrumentScreenshotId: () => isInstrumentScreenshotId
|
|
27
|
-
normalizePlaywrightScreenshotId: () => normalizePlaywrightScreenshotId
|
|
26
|
+
isInstrumentScreenshotId: () => isInstrumentScreenshotId
|
|
28
27
|
});
|
|
29
28
|
module.exports = __toCommonJS(instrument_evidence_exports);
|
|
30
29
|
const INSTRUMENT_EVIDENCE_NAMES = [
|
|
@@ -39,14 +38,10 @@ const MAX_STREAM_LINE_LENGTH = 20 * 1024 * 1024;
|
|
|
39
38
|
const PLAYWRIGHT_MCP_SCREENSHOT_TOOL = "mcp__playwright__browser_take_screenshot";
|
|
40
39
|
const PLAYWRIGHT_MCP_CONTROLLED_TAB_ID = 1;
|
|
41
40
|
const PLAYWRIGHT_MCP_SCREENSHOT_BUDGET = 100;
|
|
42
|
-
const PLAYWRIGHT_SCREENSHOT_ID_PATTERN = /^[
|
|
41
|
+
const PLAYWRIGHT_SCREENSHOT_ID_PATTERN = /^pw-[1-9][0-9]*$/;
|
|
43
42
|
function isInstrumentScreenshotId(value) {
|
|
44
43
|
return SCREENSHOT_ID_PATTERN.test(value) || PLAYWRIGHT_SCREENSHOT_ID_PATTERN.test(value);
|
|
45
44
|
}
|
|
46
|
-
function normalizePlaywrightScreenshotId(value) {
|
|
47
|
-
const id = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
48
|
-
return PLAYWRIGHT_SCREENSHOT_ID_PATTERN.test(id) ? id : null;
|
|
49
|
-
}
|
|
50
45
|
function asRecord(value) {
|
|
51
46
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
52
47
|
}
|
|
@@ -228,9 +223,10 @@ class ClaudeChromeScreenshotStreamParser {
|
|
|
228
223
|
class PlaywrightMcpScreenshotStreamParser {
|
|
229
224
|
onScreenshot;
|
|
230
225
|
buffer = "";
|
|
231
|
-
|
|
226
|
+
pendingScreenshotToolUseIds = /* @__PURE__ */ new Set();
|
|
232
227
|
handledToolUseIds = /* @__PURE__ */ new Set();
|
|
233
228
|
screenshotCallCount = 0;
|
|
229
|
+
sequence = 0;
|
|
234
230
|
constructor(onScreenshot) {
|
|
235
231
|
this.onScreenshot = onScreenshot;
|
|
236
232
|
}
|
|
@@ -298,12 +294,7 @@ class PlaywrightMcpScreenshotStreamParser {
|
|
|
298
294
|
if (this.screenshotCallCount > PLAYWRIGHT_MCP_SCREENSHOT_BUDGET) {
|
|
299
295
|
throw new Error(`playwright_screenshot_budget_exceeded:${PLAYWRIGHT_MCP_SCREENSHOT_BUDGET}`);
|
|
300
296
|
}
|
|
301
|
-
|
|
302
|
-
const filename = normalizePlaywrightScreenshotId(input?.["filename"]);
|
|
303
|
-
if (!filename) {
|
|
304
|
-
continue;
|
|
305
|
-
}
|
|
306
|
-
this.pendingScreenshotFilenames.set(id, filename);
|
|
297
|
+
this.pendingScreenshotToolUseIds.add(id);
|
|
307
298
|
}
|
|
308
299
|
}
|
|
309
300
|
recordToolResults(payload) {
|
|
@@ -316,12 +307,11 @@ class PlaywrightMcpScreenshotStreamParser {
|
|
|
316
307
|
continue;
|
|
317
308
|
}
|
|
318
309
|
const toolUseId = typeof toolResult["tool_use_id"] === "string" ? toolResult["tool_use_id"].trim() : "";
|
|
319
|
-
|
|
320
|
-
if (!screenshotId || this.handledToolUseIds.has(toolUseId)) {
|
|
310
|
+
if (!this.pendingScreenshotToolUseIds.has(toolUseId) || this.handledToolUseIds.has(toolUseId)) {
|
|
321
311
|
continue;
|
|
322
312
|
}
|
|
323
313
|
this.handledToolUseIds.add(toolUseId);
|
|
324
|
-
this.
|
|
314
|
+
this.pendingScreenshotToolUseIds.delete(toolUseId);
|
|
325
315
|
const resultContent = Array.isArray(toolResult["content"]) ? toolResult["content"] : [];
|
|
326
316
|
const images = resultContent.map(asRecord).filter((item) => item?.["type"] === "image");
|
|
327
317
|
if (images.length === 0) {
|
|
@@ -336,8 +326,9 @@ class PlaywrightMcpScreenshotStreamParser {
|
|
|
336
326
|
if (mediaType !== "image/jpeg" && mediaType !== "image/png" || !dataBase64) {
|
|
337
327
|
throw new Error(`invalid_playwright_screenshot_payload:${toolUseId}`);
|
|
338
328
|
}
|
|
329
|
+
this.sequence += 1;
|
|
339
330
|
this.onScreenshot({
|
|
340
|
-
screenshotId
|
|
331
|
+
screenshotId: `pw-${this.sequence}`,
|
|
341
332
|
tabId: PLAYWRIGHT_MCP_CONTROLLED_TAB_ID,
|
|
342
333
|
toolUseId,
|
|
343
334
|
mediaType,
|
|
@@ -358,6 +349,5 @@ class PlaywrightMcpScreenshotStreamParser {
|
|
|
358
349
|
PLAYWRIGHT_MCP_SCREENSHOT_BUDGET,
|
|
359
350
|
PLAYWRIGHT_MCP_SCREENSHOT_TOOL,
|
|
360
351
|
PlaywrightMcpScreenshotStreamParser,
|
|
361
|
-
isInstrumentScreenshotId
|
|
362
|
-
normalizePlaywrightScreenshotId
|
|
352
|
+
isInstrumentScreenshotId
|
|
363
353
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playdrop/playdrop-cli",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.22",
|
|
4
4
|
"description": "Official Playdrop CLI for publishing browser games, creator apps, and AI-generated game assets on playdrop.ai",
|
|
5
5
|
"homepage": "https://www.playdrop.ai",
|
|
6
6
|
"repository": {
|