@playdrop/playdrop-cli 0.12.14 → 0.12.16
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 +4 -0
- package/dist/commands/worker/runtime.js +37 -4
- package/dist/commands/worker.js +10 -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,14 @@ export declare function buildClaudeExecArgs(input: {
|
|
|
68
68
|
model: string;
|
|
69
69
|
effort?: string | null;
|
|
70
70
|
chrome?: boolean;
|
|
71
|
+
playwrightMcp?: boolean;
|
|
72
|
+
playwrightMcpConfigPath?: string | null;
|
|
71
73
|
workspaceDir?: string | null;
|
|
72
74
|
denyReadRoots?: string[] | null;
|
|
73
75
|
pluginDir?: string | null;
|
|
74
76
|
}): string[];
|
|
77
|
+
export declare const PLAYWRIGHT_MCP_VERSION = "0.0.78";
|
|
78
|
+
export declare function buildPlaywrightMcpServerConfig(): Record<string, unknown>;
|
|
75
79
|
export declare function buildClaudePermissionSettings(input?: {
|
|
76
80
|
workspaceDir?: string | null;
|
|
77
81
|
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;
|
|
@@ -488,18 +489,33 @@ function buildClaudeExecArgs(input) {
|
|
|
488
489
|
if (!model) {
|
|
489
490
|
throw new Error('agent_task_assignment_model_missing');
|
|
490
491
|
}
|
|
492
|
+
if (input.chrome && input.playwrightMcp) {
|
|
493
|
+
throw new Error('claude_exec_browser_mode_conflict');
|
|
494
|
+
}
|
|
495
|
+
const playwrightMcpConfigPath = typeof input.playwrightMcpConfigPath === 'string'
|
|
496
|
+
? input.playwrightMcpConfigPath.trim()
|
|
497
|
+
: '';
|
|
498
|
+
if (input.playwrightMcp && !playwrightMcpConfigPath) {
|
|
499
|
+
throw new Error('playwright_mcp_config_path_missing');
|
|
500
|
+
}
|
|
491
501
|
const permissionSettings = buildClaudePermissionSettings({
|
|
492
502
|
workspaceDir: input.workspaceDir,
|
|
493
503
|
denyReadRoots: input.denyReadRoots ?? [],
|
|
494
504
|
});
|
|
495
|
-
const permissionMode = input.chrome ? 'bypassPermissions' : 'acceptEdits';
|
|
505
|
+
const permissionMode = input.chrome || input.playwrightMcp ? 'bypassPermissions' : 'acceptEdits';
|
|
506
|
+
// MCP tools are deferred behind ToolSearch in the Claude CLI; without it in the toolset a
|
|
507
|
+
// session cannot reach any MCP server's tools no matter what --mcp-config provides.
|
|
496
508
|
const tools = input.chrome
|
|
497
509
|
? [...CLAUDE_BASE_TOOL_NAMES, ...CLAUDE_CHROME_TOOL_NAMES].join(',')
|
|
498
|
-
:
|
|
510
|
+
: input.playwrightMcp
|
|
511
|
+
? [...CLAUDE_BASE_TOOL_NAMES, 'ToolSearch'].join(',')
|
|
512
|
+
: CLAUDE_BASE_TOOL_NAMES.join(',');
|
|
499
513
|
const allowedPermissionRules = buildClaudeAllowedPermissionRules(input.workspaceDir);
|
|
500
514
|
const allowedTools = input.chrome
|
|
501
515
|
? [...allowedPermissionRules, ...CLAUDE_CHROME_TOOL_NAMES]
|
|
502
|
-
:
|
|
516
|
+
: input.playwrightMcp
|
|
517
|
+
? [...allowedPermissionRules, PLAYWRIGHT_MCP_ALLOWED_TOOL_RULE]
|
|
518
|
+
: allowedPermissionRules;
|
|
503
519
|
const args = [
|
|
504
520
|
'-p',
|
|
505
521
|
'--safe-mode',
|
|
@@ -525,6 +541,9 @@ function buildClaudeExecArgs(input) {
|
|
|
525
541
|
if (input.chrome) {
|
|
526
542
|
args.unshift('--chrome');
|
|
527
543
|
}
|
|
544
|
+
if (input.playwrightMcp) {
|
|
545
|
+
args.push('--mcp-config', playwrightMcpConfigPath);
|
|
546
|
+
}
|
|
528
547
|
const pluginDir = typeof input.pluginDir === 'string' ? input.pluginDir.trim() : '';
|
|
529
548
|
if (pluginDir) {
|
|
530
549
|
args.push('--plugin-dir', pluginDir);
|
|
@@ -543,6 +562,20 @@ const CLAUDE_BASE_TOOL_NAMES = [
|
|
|
543
562
|
'Glob',
|
|
544
563
|
'Grep',
|
|
545
564
|
];
|
|
565
|
+
// Pinned deliberately: agent-CLI and MCP versions on workers only change through releases,
|
|
566
|
+
// never through @latest drift (see the 2026-07-13 auto-update incident).
|
|
567
|
+
exports.PLAYWRIGHT_MCP_VERSION = '0.0.78';
|
|
568
|
+
const PLAYWRIGHT_MCP_ALLOWED_TOOL_RULE = 'mcp__playwright';
|
|
569
|
+
function buildPlaywrightMcpServerConfig() {
|
|
570
|
+
return {
|
|
571
|
+
mcpServers: {
|
|
572
|
+
playwright: {
|
|
573
|
+
command: 'npx',
|
|
574
|
+
args: ['-y', `@playwright/mcp@${exports.PLAYWRIGHT_MCP_VERSION}`, '--browser', 'chrome', '--isolated'],
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
};
|
|
578
|
+
}
|
|
546
579
|
const CLAUDE_CHROME_TOOL_NAMES = [
|
|
547
580
|
'mcp__claude-in-chrome__browser_batch',
|
|
548
581
|
'mcp__claude-in-chrome__computer',
|
package/dist/commands/worker.js
CHANGED
|
@@ -2102,9 +2102,14 @@ 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
|
+
let playwrightMcpConfigPath = null;
|
|
2109
|
+
if (input.enablePlaywrightMcp) {
|
|
2110
|
+
playwrightMcpConfigPath = node_path_1.default.join(claudeConfigDir, 'playwright-mcp.json');
|
|
2111
|
+
(0, node_fs_1.writeFileSync)(playwrightMcpConfigPath, `${JSON.stringify((0, runtime_1.buildPlaywrightMcpServerConfig)(), null, 2)}\n`, 'utf8');
|
|
2112
|
+
}
|
|
2108
2113
|
const result = await (0, runtime_1.runLoggedProcess)({
|
|
2109
2114
|
command: 'claude',
|
|
2110
2115
|
args: (0, runtime_1.buildClaudeExecArgs)({
|
|
@@ -2113,6 +2118,8 @@ async function runClaude(input) {
|
|
|
2113
2118
|
denyReadRoots,
|
|
2114
2119
|
pluginDir: input.taskPluginRoot,
|
|
2115
2120
|
chrome: input.enableChrome,
|
|
2121
|
+
playwrightMcp: input.enablePlaywrightMcp,
|
|
2122
|
+
playwrightMcpConfigPath,
|
|
2116
2123
|
}),
|
|
2117
2124
|
cwd: input.workspaceDir,
|
|
2118
2125
|
env: (0, runtime_1.buildWorkerChildEnv)({
|
|
@@ -4090,7 +4097,8 @@ async function startWorker(options = {}) {
|
|
|
4090
4097
|
binDir,
|
|
4091
4098
|
eventDir,
|
|
4092
4099
|
prompt,
|
|
4093
|
-
enableChrome:
|
|
4100
|
+
enableChrome: false,
|
|
4101
|
+
enablePlaywrightMcp: task.kind === 'GAME_REVIEW' || task.kind === 'GAME_EVAL',
|
|
4094
4102
|
envName: env,
|
|
4095
4103
|
workerUsername: username,
|
|
4096
4104
|
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.16",
|
|
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": {
|