@playdrop/playdrop-cli 0.12.13 → 0.12.15
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.js +8 -4
- package/dist/commands/worker/runtime.d.ts +3 -0
- package/dist/commands/worker/runtime.js +41 -4
- package/dist/commands/worker.js +4 -2
- package/node_modules/@playdrop/config/client-meta.json +1 -1
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts +16 -0
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/instrument-evidence.js +132 -2
- package/package.json +1 -1
package/config/client-meta.json
CHANGED
|
@@ -66,7 +66,7 @@ function extensionForMediaType(mediaType) {
|
|
|
66
66
|
}
|
|
67
67
|
function createWorkerInstrumentScreenshotCollector(workspaceDir) {
|
|
68
68
|
const resolvedWorkspace = node_path_1.default.resolve(workspaceDir);
|
|
69
|
-
const
|
|
69
|
+
const store = (screenshot) => {
|
|
70
70
|
const buffer = Buffer.from(screenshot.dataBase64, 'base64');
|
|
71
71
|
if (buffer.length <= 0) {
|
|
72
72
|
throw new Error(`invalid_claude_screenshot_payload:${screenshot.screenshotId}`);
|
|
@@ -89,10 +89,14 @@ function createWorkerInstrumentScreenshotCollector(workspaceDir) {
|
|
|
89
89
|
height: screenshot.height,
|
|
90
90
|
};
|
|
91
91
|
writeJsonAtomic(screenshotIndexPath(resolvedWorkspace), index);
|
|
92
|
-
}
|
|
92
|
+
};
|
|
93
|
+
// Both parsers watch the same stream: their tool names and id namespaces are disjoint, so a
|
|
94
|
+
// session only ever populates the store through the browser integration it actually used.
|
|
95
|
+
const chromeParser = new types_1.ClaudeChromeScreenshotStreamParser(store);
|
|
96
|
+
const playwrightParser = new types_1.PlaywrightMcpScreenshotStreamParser(store);
|
|
93
97
|
return {
|
|
94
|
-
push: (chunk) =>
|
|
95
|
-
finish: () =>
|
|
98
|
+
push: (chunk) => chromeParser.push(chunk) + playwrightParser.push(chunk),
|
|
99
|
+
finish: () => chromeParser.finish() + playwrightParser.finish(),
|
|
96
100
|
};
|
|
97
101
|
}
|
|
98
102
|
function normalizeEvidenceName(value) {
|
|
@@ -68,10 +68,13 @@ export declare function buildClaudeExecArgs(input: {
|
|
|
68
68
|
model: string;
|
|
69
69
|
effort?: string | null;
|
|
70
70
|
chrome?: boolean;
|
|
71
|
+
playwrightMcp?: boolean;
|
|
71
72
|
workspaceDir?: string | null;
|
|
72
73
|
denyReadRoots?: string[] | null;
|
|
73
74
|
pluginDir?: string | null;
|
|
74
75
|
}): string[];
|
|
76
|
+
export declare const PLAYWRIGHT_MCP_VERSION = "0.0.78";
|
|
77
|
+
export declare function buildPlaywrightMcpServerConfig(): Record<string, unknown>;
|
|
75
78
|
export declare function buildClaudePermissionSettings(input?: {
|
|
76
79
|
workspaceDir?: string | null;
|
|
77
80
|
denyReadRoots?: string[];
|
|
@@ -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.DEFAULT_TRANSCRIPT_FLUSH_INTERVAL_MS = exports.DEFAULT_CODEX_LOG_TAIL_CHARS = exports.DEFAULT_WORKER_TOKEN_CAP = exports.DEFAULT_CODEX_TIMEOUT_MS = void 0;
|
|
6
|
+
exports.PLAYWRIGHT_MCP_VERSION = exports.DEFAULT_TRANSCRIPT_FLUSH_INTERVAL_MS = exports.DEFAULT_CODEX_LOG_TAIL_CHARS = exports.DEFAULT_WORKER_TOKEN_CAP = exports.DEFAULT_CODEX_TIMEOUT_MS = void 0;
|
|
7
7
|
exports.readPositiveEnvInt = readPositiveEnvInt;
|
|
8
8
|
exports.readEnvBoolean = readEnvBoolean;
|
|
9
9
|
exports.extractAgentTokenUsage = extractAgentTokenUsage;
|
|
@@ -16,6 +16,7 @@ exports.runLoggedProcess = runLoggedProcess;
|
|
|
16
16
|
exports.buildCodexExecArgs = buildCodexExecArgs;
|
|
17
17
|
exports.readCodexSandboxMode = readCodexSandboxMode;
|
|
18
18
|
exports.buildClaudeExecArgs = buildClaudeExecArgs;
|
|
19
|
+
exports.buildPlaywrightMcpServerConfig = buildPlaywrightMcpServerConfig;
|
|
19
20
|
exports.buildClaudePermissionSettings = buildClaudePermissionSettings;
|
|
20
21
|
exports.buildClaudeDeniedPermissionRules = buildClaudeDeniedPermissionRules;
|
|
21
22
|
exports.buildWorkerChildEnv = buildWorkerChildEnv;
|
|
@@ -347,7 +348,14 @@ async function runLoggedProcess(input) {
|
|
|
347
348
|
return;
|
|
348
349
|
}
|
|
349
350
|
const chunks = pendingTranscriptChunks.splice(0, pendingTranscriptChunks.length);
|
|
350
|
-
|
|
351
|
+
try {
|
|
352
|
+
await input.onTranscriptChunks(chunks);
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
// Transcript upload is telemetry, never task-critical: requeue in order and retry with the next flush.
|
|
356
|
+
pendingTranscriptChunks.unshift(...chunks);
|
|
357
|
+
console.error(`warning: transcript chunk upload failed, will retry with next flush: ${error instanceof Error ? error.message : String(error)}`);
|
|
358
|
+
}
|
|
351
359
|
};
|
|
352
360
|
const queueTranscriptFlush = () => {
|
|
353
361
|
transcriptFlushPromise = transcriptFlushPromise.then(flushTranscriptChunks, flushTranscriptChunks);
|
|
@@ -416,6 +424,13 @@ async function runLoggedProcess(input) {
|
|
|
416
424
|
}
|
|
417
425
|
void (async () => {
|
|
418
426
|
await queueTranscriptFlush();
|
|
427
|
+
if (pendingTranscriptChunks.length > 0) {
|
|
428
|
+
// Last flush failed: retry once, then drop with a warning. Missing transcript chunks never fail the task.
|
|
429
|
+
await flushTranscriptChunks();
|
|
430
|
+
}
|
|
431
|
+
if (pendingTranscriptChunks.length > 0) {
|
|
432
|
+
console.error(`warning: dropped ${pendingTranscriptChunks.length} transcript chunk(s) after final retry; transcript is telemetry, continuing`);
|
|
433
|
+
}
|
|
419
434
|
if (transcriptFlushError) {
|
|
420
435
|
throwLoggedProcessError(transcriptFlushError);
|
|
421
436
|
}
|
|
@@ -474,18 +489,23 @@ function buildClaudeExecArgs(input) {
|
|
|
474
489
|
if (!model) {
|
|
475
490
|
throw new Error('agent_task_assignment_model_missing');
|
|
476
491
|
}
|
|
492
|
+
if (input.chrome && input.playwrightMcp) {
|
|
493
|
+
throw new Error('claude_exec_browser_mode_conflict');
|
|
494
|
+
}
|
|
477
495
|
const permissionSettings = buildClaudePermissionSettings({
|
|
478
496
|
workspaceDir: input.workspaceDir,
|
|
479
497
|
denyReadRoots: input.denyReadRoots ?? [],
|
|
480
498
|
});
|
|
481
|
-
const permissionMode = input.chrome ? 'bypassPermissions' : 'acceptEdits';
|
|
499
|
+
const permissionMode = input.chrome || input.playwrightMcp ? 'bypassPermissions' : 'acceptEdits';
|
|
482
500
|
const tools = input.chrome
|
|
483
501
|
? [...CLAUDE_BASE_TOOL_NAMES, ...CLAUDE_CHROME_TOOL_NAMES].join(',')
|
|
484
502
|
: CLAUDE_BASE_TOOL_NAMES.join(',');
|
|
485
503
|
const allowedPermissionRules = buildClaudeAllowedPermissionRules(input.workspaceDir);
|
|
486
504
|
const allowedTools = input.chrome
|
|
487
505
|
? [...allowedPermissionRules, ...CLAUDE_CHROME_TOOL_NAMES]
|
|
488
|
-
:
|
|
506
|
+
: input.playwrightMcp
|
|
507
|
+
? [...allowedPermissionRules, PLAYWRIGHT_MCP_ALLOWED_TOOL_RULE]
|
|
508
|
+
: allowedPermissionRules;
|
|
489
509
|
const args = [
|
|
490
510
|
'-p',
|
|
491
511
|
'--safe-mode',
|
|
@@ -511,6 +531,9 @@ function buildClaudeExecArgs(input) {
|
|
|
511
531
|
if (input.chrome) {
|
|
512
532
|
args.unshift('--chrome');
|
|
513
533
|
}
|
|
534
|
+
if (input.playwrightMcp) {
|
|
535
|
+
args.push('--mcp-config', JSON.stringify(buildPlaywrightMcpServerConfig()));
|
|
536
|
+
}
|
|
514
537
|
const pluginDir = typeof input.pluginDir === 'string' ? input.pluginDir.trim() : '';
|
|
515
538
|
if (pluginDir) {
|
|
516
539
|
args.push('--plugin-dir', pluginDir);
|
|
@@ -529,6 +552,20 @@ const CLAUDE_BASE_TOOL_NAMES = [
|
|
|
529
552
|
'Glob',
|
|
530
553
|
'Grep',
|
|
531
554
|
];
|
|
555
|
+
// Pinned deliberately: agent-CLI and MCP versions on workers only change through releases,
|
|
556
|
+
// never through @latest drift (see the 2026-07-13 auto-update incident).
|
|
557
|
+
exports.PLAYWRIGHT_MCP_VERSION = '0.0.78';
|
|
558
|
+
const PLAYWRIGHT_MCP_ALLOWED_TOOL_RULE = 'mcp__playwright';
|
|
559
|
+
function buildPlaywrightMcpServerConfig() {
|
|
560
|
+
return {
|
|
561
|
+
mcpServers: {
|
|
562
|
+
playwright: {
|
|
563
|
+
command: 'npx',
|
|
564
|
+
args: ['-y', `@playwright/mcp@${exports.PLAYWRIGHT_MCP_VERSION}`, '--browser', 'chrome', '--isolated'],
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
};
|
|
568
|
+
}
|
|
532
569
|
const CLAUDE_CHROME_TOOL_NAMES = [
|
|
533
570
|
'mcp__claude-in-chrome__browser_batch',
|
|
534
571
|
'mcp__claude-in-chrome__computer',
|
package/dist/commands/worker.js
CHANGED
|
@@ -2102,7 +2102,7 @@ async function runClaude(input) {
|
|
|
2102
2102
|
envName: input.envName,
|
|
2103
2103
|
});
|
|
2104
2104
|
const denyReadRoots = resolveClaudeDenyReadRoots(input.workspaceDir);
|
|
2105
|
-
const screenshotCollector = input.enableChrome
|
|
2105
|
+
const screenshotCollector = input.enableChrome || input.enablePlaywrightMcp
|
|
2106
2106
|
? (0, instrument_evidence_1.createWorkerInstrumentScreenshotCollector)(input.workspaceDir)
|
|
2107
2107
|
: null;
|
|
2108
2108
|
const result = await (0, runtime_1.runLoggedProcess)({
|
|
@@ -2113,6 +2113,7 @@ async function runClaude(input) {
|
|
|
2113
2113
|
denyReadRoots,
|
|
2114
2114
|
pluginDir: input.taskPluginRoot,
|
|
2115
2115
|
chrome: input.enableChrome,
|
|
2116
|
+
playwrightMcp: input.enablePlaywrightMcp,
|
|
2116
2117
|
}),
|
|
2117
2118
|
cwd: input.workspaceDir,
|
|
2118
2119
|
env: (0, runtime_1.buildWorkerChildEnv)({
|
|
@@ -4090,7 +4091,8 @@ async function startWorker(options = {}) {
|
|
|
4090
4091
|
binDir,
|
|
4091
4092
|
eventDir,
|
|
4092
4093
|
prompt,
|
|
4093
|
-
enableChrome:
|
|
4094
|
+
enableChrome: false,
|
|
4095
|
+
enablePlaywrightMcp: task.kind === 'GAME_REVIEW' || task.kind === 'GAME_EVAL',
|
|
4094
4096
|
envName: env,
|
|
4095
4097
|
workerUsername: username,
|
|
4096
4098
|
taskId: task.id,
|
|
@@ -26,6 +26,9 @@ export type InstrumentEvidenceManifest = {
|
|
|
26
26
|
controlledTabId: number;
|
|
27
27
|
captures: Partial<Record<InstrumentEvidenceName, InstrumentEvidenceCapture>>;
|
|
28
28
|
};
|
|
29
|
+
export declare const PLAYWRIGHT_MCP_SCREENSHOT_TOOL = "mcp__playwright__browser_take_screenshot";
|
|
30
|
+
export declare const PLAYWRIGHT_MCP_CONTROLLED_TAB_ID = 1;
|
|
31
|
+
export declare function isInstrumentScreenshotId(value: string): boolean;
|
|
29
32
|
export declare class ClaudeChromeScreenshotStreamParser {
|
|
30
33
|
private readonly onScreenshot;
|
|
31
34
|
private buffer;
|
|
@@ -38,4 +41,17 @@ export declare class ClaudeChromeScreenshotStreamParser {
|
|
|
38
41
|
private recordToolUse;
|
|
39
42
|
private recordToolResults;
|
|
40
43
|
}
|
|
44
|
+
export declare class PlaywrightMcpScreenshotStreamParser {
|
|
45
|
+
private readonly onScreenshot;
|
|
46
|
+
private buffer;
|
|
47
|
+
private readonly pendingScreenshotToolUseIds;
|
|
48
|
+
private readonly handledToolUseIds;
|
|
49
|
+
private sequence;
|
|
50
|
+
constructor(onScreenshot: (screenshot: ClaudeChromeScreenshot) => void);
|
|
51
|
+
push(chunk: string): number;
|
|
52
|
+
finish(): number;
|
|
53
|
+
private processLine;
|
|
54
|
+
private recordToolUse;
|
|
55
|
+
private recordToolResults;
|
|
56
|
+
}
|
|
41
57
|
//# sourceMappingURL=instrument-evidence.d.ts.map
|
|
@@ -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;
|
|
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,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/D;AA+CD,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;IAMlC,OAAO,CAAC,QAAQ,CAAC,YAAY;IALzC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAqB;IACjE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;IACvD,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;IAcrB,OAAO,CAAC,iBAAiB;CA8C1B"}
|
|
@@ -18,7 +18,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
18
18
|
var instrument_evidence_exports = {};
|
|
19
19
|
__export(instrument_evidence_exports, {
|
|
20
20
|
ClaudeChromeScreenshotStreamParser: () => ClaudeChromeScreenshotStreamParser,
|
|
21
|
-
INSTRUMENT_EVIDENCE_NAMES: () => INSTRUMENT_EVIDENCE_NAMES
|
|
21
|
+
INSTRUMENT_EVIDENCE_NAMES: () => INSTRUMENT_EVIDENCE_NAMES,
|
|
22
|
+
PLAYWRIGHT_MCP_CONTROLLED_TAB_ID: () => PLAYWRIGHT_MCP_CONTROLLED_TAB_ID,
|
|
23
|
+
PLAYWRIGHT_MCP_SCREENSHOT_TOOL: () => PLAYWRIGHT_MCP_SCREENSHOT_TOOL,
|
|
24
|
+
PlaywrightMcpScreenshotStreamParser: () => PlaywrightMcpScreenshotStreamParser,
|
|
25
|
+
isInstrumentScreenshotId: () => isInstrumentScreenshotId
|
|
22
26
|
});
|
|
23
27
|
module.exports = __toCommonJS(instrument_evidence_exports);
|
|
24
28
|
const INSTRUMENT_EVIDENCE_NAMES = [
|
|
@@ -30,6 +34,12 @@ const INSTRUMENT_EVIDENCE_NAMES = [
|
|
|
30
34
|
const SCREENSHOT_ID_PATTERN = /^ss_[A-Za-z0-9_-]+$/;
|
|
31
35
|
const SCREENSHOT_ID_IN_TEXT_PATTERN = /\bID:\s*(ss_[A-Za-z0-9_-]+)\b/g;
|
|
32
36
|
const MAX_STREAM_LINE_LENGTH = 20 * 1024 * 1024;
|
|
37
|
+
const PLAYWRIGHT_MCP_SCREENSHOT_TOOL = "mcp__playwright__browser_take_screenshot";
|
|
38
|
+
const PLAYWRIGHT_MCP_CONTROLLED_TAB_ID = 1;
|
|
39
|
+
const PLAYWRIGHT_SCREENSHOT_ID_PATTERN = /^pw-[1-9][0-9]*$/;
|
|
40
|
+
function isInstrumentScreenshotId(value) {
|
|
41
|
+
return SCREENSHOT_ID_PATTERN.test(value) || PLAYWRIGHT_SCREENSHOT_ID_PATTERN.test(value);
|
|
42
|
+
}
|
|
33
43
|
function asRecord(value) {
|
|
34
44
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
35
45
|
}
|
|
@@ -208,8 +218,128 @@ class ClaudeChromeScreenshotStreamParser {
|
|
|
208
218
|
return emitted;
|
|
209
219
|
}
|
|
210
220
|
}
|
|
221
|
+
class PlaywrightMcpScreenshotStreamParser {
|
|
222
|
+
onScreenshot;
|
|
223
|
+
buffer = "";
|
|
224
|
+
pendingScreenshotToolUseIds = /* @__PURE__ */ new Set();
|
|
225
|
+
handledToolUseIds = /* @__PURE__ */ new Set();
|
|
226
|
+
sequence = 0;
|
|
227
|
+
constructor(onScreenshot) {
|
|
228
|
+
this.onScreenshot = onScreenshot;
|
|
229
|
+
}
|
|
230
|
+
push(chunk) {
|
|
231
|
+
if (!chunk) {
|
|
232
|
+
return 0;
|
|
233
|
+
}
|
|
234
|
+
this.buffer += chunk;
|
|
235
|
+
if (this.buffer.length > MAX_STREAM_LINE_LENGTH && !this.buffer.includes("\n")) {
|
|
236
|
+
throw new Error("claude_stream_json_line_too_large");
|
|
237
|
+
}
|
|
238
|
+
let emitted = 0;
|
|
239
|
+
for (; ; ) {
|
|
240
|
+
const newlineIndex = this.buffer.indexOf("\n");
|
|
241
|
+
if (newlineIndex < 0) {
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
const line = this.buffer.slice(0, newlineIndex);
|
|
245
|
+
this.buffer = this.buffer.slice(newlineIndex + 1);
|
|
246
|
+
emitted += this.processLine(line);
|
|
247
|
+
}
|
|
248
|
+
return emitted;
|
|
249
|
+
}
|
|
250
|
+
finish() {
|
|
251
|
+
const line = this.buffer;
|
|
252
|
+
this.buffer = "";
|
|
253
|
+
return line.trim() ? this.processLine(line) : 0;
|
|
254
|
+
}
|
|
255
|
+
processLine(line) {
|
|
256
|
+
const trimmed = line.trim();
|
|
257
|
+
if (!trimmed) {
|
|
258
|
+
return 0;
|
|
259
|
+
}
|
|
260
|
+
let payload;
|
|
261
|
+
try {
|
|
262
|
+
const parsed = JSON.parse(trimmed);
|
|
263
|
+
const record = asRecord(parsed);
|
|
264
|
+
if (!record) {
|
|
265
|
+
throw new Error("invalid_claude_stream_json");
|
|
266
|
+
}
|
|
267
|
+
payload = record;
|
|
268
|
+
} catch (error) {
|
|
269
|
+
throw new Error(`invalid_claude_stream_json:${error instanceof Error ? error.message : String(error)}`);
|
|
270
|
+
}
|
|
271
|
+
if (payload["type"] === "assistant") {
|
|
272
|
+
this.recordToolUse(payload);
|
|
273
|
+
return 0;
|
|
274
|
+
}
|
|
275
|
+
if (payload["type"] === "user") {
|
|
276
|
+
return this.recordToolResults(payload);
|
|
277
|
+
}
|
|
278
|
+
return 0;
|
|
279
|
+
}
|
|
280
|
+
recordToolUse(payload) {
|
|
281
|
+
const message = asRecord(payload["message"]);
|
|
282
|
+
const content = Array.isArray(message?.["content"]) ? message["content"] : [];
|
|
283
|
+
for (const value of content) {
|
|
284
|
+
const item = asRecord(value);
|
|
285
|
+
const id = typeof item?.["id"] === "string" ? item["id"].trim() : "";
|
|
286
|
+
const name = typeof item?.["name"] === "string" ? item["name"].trim() : "";
|
|
287
|
+
if (item?.["type"] !== "tool_use" || !id || name !== PLAYWRIGHT_MCP_SCREENSHOT_TOOL) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
this.pendingScreenshotToolUseIds.add(id);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
recordToolResults(payload) {
|
|
294
|
+
const message = asRecord(payload["message"]);
|
|
295
|
+
const content = Array.isArray(message?.["content"]) ? message["content"] : [];
|
|
296
|
+
let emitted = 0;
|
|
297
|
+
for (const value of content) {
|
|
298
|
+
const toolResult = asRecord(value);
|
|
299
|
+
if (toolResult?.["type"] !== "tool_result") {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const toolUseId = typeof toolResult["tool_use_id"] === "string" ? toolResult["tool_use_id"].trim() : "";
|
|
303
|
+
if (!this.pendingScreenshotToolUseIds.has(toolUseId) || this.handledToolUseIds.has(toolUseId)) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
this.handledToolUseIds.add(toolUseId);
|
|
307
|
+
this.pendingScreenshotToolUseIds.delete(toolUseId);
|
|
308
|
+
const resultContent = Array.isArray(toolResult["content"]) ? toolResult["content"] : [];
|
|
309
|
+
const images = resultContent.map(asRecord).filter((item) => item?.["type"] === "image");
|
|
310
|
+
if (images.length === 0) {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (images.length > 1) {
|
|
314
|
+
throw new Error(`playwright_screenshot_multiple_images:${toolUseId}`);
|
|
315
|
+
}
|
|
316
|
+
const source = asRecord(images[0]?.["source"]);
|
|
317
|
+
const mediaType = source?.["media_type"];
|
|
318
|
+
const dataBase64 = typeof source?.["data"] === "string" ? source["data"] : "";
|
|
319
|
+
if (mediaType !== "image/jpeg" && mediaType !== "image/png" || !dataBase64) {
|
|
320
|
+
throw new Error(`invalid_playwright_screenshot_payload:${toolUseId}`);
|
|
321
|
+
}
|
|
322
|
+
this.sequence += 1;
|
|
323
|
+
this.onScreenshot({
|
|
324
|
+
screenshotId: `pw-${this.sequence}`,
|
|
325
|
+
tabId: PLAYWRIGHT_MCP_CONTROLLED_TAB_ID,
|
|
326
|
+
toolUseId,
|
|
327
|
+
mediaType,
|
|
328
|
+
dataBase64,
|
|
329
|
+
width: null,
|
|
330
|
+
height: null
|
|
331
|
+
});
|
|
332
|
+
emitted += 1;
|
|
333
|
+
}
|
|
334
|
+
return emitted;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
211
337
|
// Annotate the CommonJS export names for ESM import in node:
|
|
212
338
|
0 && (module.exports = {
|
|
213
339
|
ClaudeChromeScreenshotStreamParser,
|
|
214
|
-
INSTRUMENT_EVIDENCE_NAMES
|
|
340
|
+
INSTRUMENT_EVIDENCE_NAMES,
|
|
341
|
+
PLAYWRIGHT_MCP_CONTROLLED_TAB_ID,
|
|
342
|
+
PLAYWRIGHT_MCP_SCREENSHOT_TOOL,
|
|
343
|
+
PlaywrightMcpScreenshotStreamParser,
|
|
344
|
+
isInstrumentScreenshotId
|
|
215
345
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playdrop/playdrop-cli",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.15",
|
|
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": {
|