@playdrop/playdrop-cli 0.12.6 → 0.12.8
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/upload.js +0 -12
- package/dist/commands/captureListing.d.ts +0 -2
- package/dist/commands/captureListing.js +0 -15
- package/dist/commands/login.d.ts +1 -13
- package/dist/commands/login.js +4 -59
- package/dist/commands/upgrade.d.ts +0 -1
- package/dist/commands/upgrade.js +1 -2
- package/dist/commands/worker/runtime.d.ts +8 -1
- package/dist/commands/worker/runtime.js +60 -17
- package/dist/commands/worker.d.ts +63 -11
- package/dist/commands/worker.js +1201 -901
- package/dist/index.js +4 -6
- package/node_modules/@playdrop/config/client-meta.json +2 -2
- package/node_modules/@playdrop/types/dist/api.d.ts +7 -14
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.d.ts +0 -2
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/package.json +1 -1
package/config/client-meta.json
CHANGED
package/dist/apps/upload.js
CHANGED
|
@@ -100,13 +100,6 @@ function readMediaCaptureProof(input) {
|
|
|
100
100
|
if (!binding || typeof binding !== 'object' || Array.isArray(binding)) {
|
|
101
101
|
throw new Error(`agent_task_media_capture_report_invalid: ${reportPath} is missing binding.`);
|
|
102
102
|
}
|
|
103
|
-
const contentHash = typeof binding.contentHash === 'string' ? binding.contentHash.trim().toLowerCase() : '';
|
|
104
|
-
if (!isSha256Hex(contentHash)) {
|
|
105
|
-
throw new Error(`agent_task_media_capture_report_invalid: ${reportPath} binding.contentHash must be a SHA-256 hex string.`);
|
|
106
|
-
}
|
|
107
|
-
if (contentHash !== input.artifacts.bundleHash) {
|
|
108
|
-
throw new Error(`agent_task_media_capture_build_mismatch: capture report contentHash does not match the bundle being uploaded for ${input.task.name}. Rerun "playdrop project capture" after the final build changes.`);
|
|
109
|
-
}
|
|
110
103
|
const rawArtifactHashes = binding.artifactHashes;
|
|
111
104
|
if (!rawArtifactHashes || typeof rawArtifactHashes !== 'object' || Array.isArray(rawArtifactHashes)) {
|
|
112
105
|
throw new Error(`agent_task_media_capture_report_invalid: ${reportPath} binding.artifactHashes must be an object.`);
|
|
@@ -160,7 +153,6 @@ function readMediaCaptureProof(input) {
|
|
|
160
153
|
throw new Error(`agent_task_media_capture_surface_missing: listing.captureReport did not capture ${missingSurfaces.join(', ')} for ${input.task.name}.`);
|
|
161
154
|
}
|
|
162
155
|
return {
|
|
163
|
-
contentHash,
|
|
164
156
|
artifactHashes,
|
|
165
157
|
capturedSurfaces,
|
|
166
158
|
};
|
|
@@ -433,12 +425,8 @@ async function uploadAppVersion(client, task, artifacts, options) {
|
|
|
433
425
|
if (options?.mediaCaptureRequired === true
|
|
434
426
|
&& task.hostingMode !== 'EXTERNAL'
|
|
435
427
|
&& !task.externalUrl) {
|
|
436
|
-
if (!artifacts) {
|
|
437
|
-
throw new Error(`agent_task_media_capture_build_missing: ${task.name} has no hosted build artifacts to bind capture media to.`);
|
|
438
|
-
}
|
|
439
428
|
const mediaCaptureProof = readMediaCaptureProof({
|
|
440
429
|
task,
|
|
441
|
-
artifacts,
|
|
442
430
|
preparedSessionFiles,
|
|
443
431
|
});
|
|
444
432
|
await client.recordAppUploadMediaCapture(creatorUsername, task.name, sessionId, mediaCaptureProof);
|
|
@@ -11,7 +11,6 @@ type CaptureListingOptions = {
|
|
|
11
11
|
audio?: boolean;
|
|
12
12
|
outputDir?: string;
|
|
13
13
|
keepRaw?: boolean;
|
|
14
|
-
contentHash?: string;
|
|
15
14
|
};
|
|
16
15
|
type ParsedCaptureListingOptions = {
|
|
17
16
|
targetArg?: string;
|
|
@@ -24,7 +23,6 @@ type ParsedCaptureListingOptions = {
|
|
|
24
23
|
audio: boolean;
|
|
25
24
|
outputDir: string | null;
|
|
26
25
|
keepRaw: boolean;
|
|
27
|
-
contentHash: string | null;
|
|
28
26
|
explicitDimensions: boolean;
|
|
29
27
|
};
|
|
30
28
|
type CaptureRect = {
|
|
@@ -77,7 +77,6 @@ const MAX_FPS = 120;
|
|
|
77
77
|
const MIN_SUPPORTED_MACOS_MAJOR = 26;
|
|
78
78
|
const MIN_SUPPORTED_MACOS_MINOR = 4;
|
|
79
79
|
const LISTING_CAPTURE_HOOK_TIMEOUT_MS = 60000;
|
|
80
|
-
const SHA256_HEX_REGEX = /^[a-f0-9]{64}$/i;
|
|
81
80
|
const LISTING_CAPTURE_MOBILE_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1';
|
|
82
81
|
const SURFACE_CAPTURE_DIMENSIONS = {
|
|
83
82
|
DESKTOP: { width: 1280, height: 720 },
|
|
@@ -157,12 +156,6 @@ function parseCaptureListingOptions(targetArg, options = {}) {
|
|
|
157
156
|
maximum: durationSeconds,
|
|
158
157
|
});
|
|
159
158
|
const outputDir = options.outputDir?.trim() ? (0, node_path_1.resolve)(process.cwd(), options.outputDir.trim()) : null;
|
|
160
|
-
const contentHash = typeof options.contentHash === 'string' && options.contentHash.trim().length > 0
|
|
161
|
-
? options.contentHash.trim().toLowerCase()
|
|
162
|
-
: null;
|
|
163
|
-
if (contentHash && !SHA256_HEX_REGEX.test(contentHash)) {
|
|
164
|
-
throw new Error('content_hash_invalid');
|
|
165
|
-
}
|
|
166
159
|
return {
|
|
167
160
|
targetArg,
|
|
168
161
|
appName: options.app?.trim() || undefined,
|
|
@@ -174,7 +167,6 @@ function parseCaptureListingOptions(targetArg, options = {}) {
|
|
|
174
167
|
audio: Boolean(options.audio),
|
|
175
168
|
outputDir,
|
|
176
169
|
keepRaw: Boolean(options.keepRaw),
|
|
177
|
-
contentHash,
|
|
178
170
|
explicitDimensions,
|
|
179
171
|
};
|
|
180
172
|
}
|
|
@@ -640,12 +632,6 @@ function formatCommandError(error) {
|
|
|
640
632
|
suggestions: [],
|
|
641
633
|
};
|
|
642
634
|
}
|
|
643
|
-
if (error.message === 'content_hash_invalid') {
|
|
644
|
-
return {
|
|
645
|
-
message: 'The --content-hash value must be a 64-character SHA-256 hex string.',
|
|
646
|
-
suggestions: [],
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
635
|
if (error.message === 'listing_preview_support_missing') {
|
|
650
636
|
return {
|
|
651
637
|
message: 'This game does not declare preview support, so listing capture cannot run.',
|
|
@@ -1095,7 +1081,6 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1095
1081
|
captures,
|
|
1096
1082
|
warnings: allWarnings,
|
|
1097
1083
|
binding: {
|
|
1098
|
-
contentHash: parsedOptions.contentHash,
|
|
1099
1084
|
artifactHashes,
|
|
1100
1085
|
capturedSurfaces,
|
|
1101
1086
|
},
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Readable } from 'stream';
|
|
2
1
|
import { resetOpenBrowserForTests, setOpenBrowserForTests } from '../browser';
|
|
3
2
|
export { resetOpenBrowserForTests, setOpenBrowserForTests, };
|
|
4
3
|
type LoginOptions = {
|
|
@@ -6,17 +5,6 @@ type LoginOptions = {
|
|
|
6
5
|
password?: string;
|
|
7
6
|
key?: string;
|
|
8
7
|
handoffToken?: string;
|
|
9
|
-
handoffTokenStdin?: boolean;
|
|
10
8
|
json?: boolean;
|
|
11
9
|
};
|
|
12
|
-
|
|
13
|
-
stdin?: Readable;
|
|
14
|
-
};
|
|
15
|
-
export declare const HANDOFF_TOKEN_STDIN_MAX_BYTES: number;
|
|
16
|
-
type HandoffTokenStdinErrorCode = 'empty' | 'too_large' | 'read_failed';
|
|
17
|
-
export declare class HandoffTokenStdinError extends Error {
|
|
18
|
-
readonly code: HandoffTokenStdinErrorCode;
|
|
19
|
-
constructor(code: HandoffTokenStdinErrorCode);
|
|
20
|
-
}
|
|
21
|
-
export declare function readHandoffTokenFromStdin(input?: Readable): Promise<string>;
|
|
22
|
-
export declare function login(env: string, options?: LoginOptions, runtime?: LoginRuntime): Promise<void>;
|
|
10
|
+
export declare function login(env: string, options?: LoginOptions): Promise<void>;
|
package/dist/commands/login.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.readHandoffTokenFromStdin = readHandoffTokenFromStdin;
|
|
3
|
+
exports.setOpenBrowserForTests = exports.resetOpenBrowserForTests = void 0;
|
|
5
4
|
exports.login = login;
|
|
6
5
|
const types_1 = require("@playdrop/types");
|
|
7
6
|
const config_1 = require("../config");
|
|
@@ -13,40 +12,6 @@ Object.defineProperty(exports, "resetOpenBrowserForTests", { enumerable: true, g
|
|
|
13
12
|
Object.defineProperty(exports, "setOpenBrowserForTests", { enumerable: true, get: function () { return browser_1.setOpenBrowserForTests; } });
|
|
14
13
|
const output_1 = require("../output");
|
|
15
14
|
const messages_1 = require("../messages");
|
|
16
|
-
exports.HANDOFF_TOKEN_STDIN_MAX_BYTES = 8 * 1024;
|
|
17
|
-
class HandoffTokenStdinError extends Error {
|
|
18
|
-
constructor(code) {
|
|
19
|
-
super(`handoff_token_stdin_${code}`);
|
|
20
|
-
this.name = 'HandoffTokenStdinError';
|
|
21
|
-
this.code = code;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
exports.HandoffTokenStdinError = HandoffTokenStdinError;
|
|
25
|
-
async function readHandoffTokenFromStdin(input = process.stdin) {
|
|
26
|
-
const chunks = [];
|
|
27
|
-
let byteCount = 0;
|
|
28
|
-
try {
|
|
29
|
-
for await (const chunk of input) {
|
|
30
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
|
|
31
|
-
byteCount += buffer.byteLength;
|
|
32
|
-
if (byteCount > exports.HANDOFF_TOKEN_STDIN_MAX_BYTES) {
|
|
33
|
-
throw new HandoffTokenStdinError('too_large');
|
|
34
|
-
}
|
|
35
|
-
chunks.push(buffer);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
if (error instanceof HandoffTokenStdinError) {
|
|
40
|
-
throw error;
|
|
41
|
-
}
|
|
42
|
-
throw new HandoffTokenStdinError('read_failed');
|
|
43
|
-
}
|
|
44
|
-
const token = Buffer.concat(chunks, byteCount).toString('utf8').trim();
|
|
45
|
-
if (!token) {
|
|
46
|
-
throw new HandoffTokenStdinError('empty');
|
|
47
|
-
}
|
|
48
|
-
return token;
|
|
49
|
-
}
|
|
50
15
|
function sleep(ms) {
|
|
51
16
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
52
17
|
}
|
|
@@ -228,7 +193,7 @@ async function loginInBrowser(env, baseUrl, options = {}) {
|
|
|
228
193
|
], { command: 'login' });
|
|
229
194
|
process.exitCode = 1;
|
|
230
195
|
}
|
|
231
|
-
async function login(env, options = {}
|
|
196
|
+
async function login(env, options = {}) {
|
|
232
197
|
const envConfig = (0, environment_1.resolveEnvironmentConfig)(env);
|
|
233
198
|
if (!envConfig) {
|
|
234
199
|
const choices = (0, environment_1.formatEnvironmentList)();
|
|
@@ -245,13 +210,11 @@ async function login(env, options = {}, runtime = {}) {
|
|
|
245
210
|
const argvHandoffToken = options.handoffToken?.trim();
|
|
246
211
|
const hasApiKeyArgument = options.key !== undefined;
|
|
247
212
|
const hasHandoffTokenArgument = options.handoffToken !== undefined;
|
|
248
|
-
const readsHandoffTokenFromStdin = options.handoffTokenStdin === true;
|
|
249
213
|
const hasDirectCredentials = Boolean(username || password);
|
|
250
214
|
const explicitLoginMethods = [
|
|
251
215
|
hasApiKeyArgument ? 'key' : null,
|
|
252
216
|
hasDirectCredentials ? 'credentials' : null,
|
|
253
217
|
hasHandoffTokenArgument ? 'handoff' : null,
|
|
254
|
-
readsHandoffTokenFromStdin ? 'handoff-stdin' : null,
|
|
255
218
|
].filter(Boolean);
|
|
256
219
|
if (explicitLoginMethods.length > 1) {
|
|
257
220
|
(0, messages_1.printErrorWithHelp)('Choose exactly one login method.', [
|
|
@@ -259,7 +222,6 @@ async function login(env, options = {}, runtime = {}) {
|
|
|
259
222
|
'Use "--username" with "--password" for direct login.',
|
|
260
223
|
'Use "--key" by itself for API key login.',
|
|
261
224
|
'Use "--handoff-token" by itself for native app handoff login.',
|
|
262
|
-
'Use "--handoff-token-stdin" by itself to read a native app handoff token from stdin.',
|
|
263
225
|
], { command: 'login' });
|
|
264
226
|
process.exitCode = 1;
|
|
265
227
|
return;
|
|
@@ -282,30 +244,13 @@ async function login(env, options = {}, runtime = {}) {
|
|
|
282
244
|
process.exitCode = 1;
|
|
283
245
|
return;
|
|
284
246
|
}
|
|
285
|
-
let handoffToken = argvHandoffToken;
|
|
286
|
-
if (readsHandoffTokenFromStdin) {
|
|
287
|
-
try {
|
|
288
|
-
handoffToken = await readHandoffTokenFromStdin(runtime.stdin ?? process.stdin);
|
|
289
|
-
}
|
|
290
|
-
catch (error) {
|
|
291
|
-
const inputError = error instanceof HandoffTokenStdinError ? error : null;
|
|
292
|
-
const message = inputError?.code === 'too_large'
|
|
293
|
-
? `Native app handoff input exceeded the ${exports.HANDOFF_TOKEN_STDIN_MAX_BYTES}-byte limit.`
|
|
294
|
-
: inputError?.code === 'empty'
|
|
295
|
-
? 'Native app handoff input was empty.'
|
|
296
|
-
: 'Could not read native app handoff input from stdin.';
|
|
297
|
-
(0, messages_1.printErrorWithHelp)(message, ['Request a fresh handoff token from the PlayDrop app and retry.'], { command: 'login' });
|
|
298
|
-
process.exitCode = 1;
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
247
|
try {
|
|
303
248
|
if (apiKey) {
|
|
304
249
|
await loginWithKey(env, envConfig.apiBase, apiKey, { json: options.json });
|
|
305
250
|
return;
|
|
306
251
|
}
|
|
307
|
-
if (
|
|
308
|
-
await loginWithHandoff(env, envConfig.apiBase,
|
|
252
|
+
if (argvHandoffToken) {
|
|
253
|
+
await loginWithHandoff(env, envConfig.apiBase, argvHandoffToken, { json: options.json });
|
|
309
254
|
return;
|
|
310
255
|
}
|
|
311
256
|
if (username && password) {
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -49,7 +49,6 @@ function formatWorkerUpdateState(worker) {
|
|
|
49
49
|
`update=${worker.updateState ?? 'unknown'}`,
|
|
50
50
|
`ready=${worker.ready === true ? 'true' : worker.ready === false ? 'false' : 'unknown'}`,
|
|
51
51
|
`cli=${worker.cliVersion ?? 'unknown'}`,
|
|
52
|
-
`plugin=${worker.pluginVersion ?? 'unknown'}`,
|
|
53
52
|
].join(' ');
|
|
54
53
|
}
|
|
55
54
|
async function observeManagedWorkerUpdate(input) {
|
|
@@ -69,7 +68,7 @@ async function observeManagedWorkerUpdate(input) {
|
|
|
69
68
|
lastState = state;
|
|
70
69
|
}
|
|
71
70
|
if (worker.updateState === 'UPDATE_BLOCKED' || worker.lifecycleState === 'DEGRADED' || worker.ready === false) {
|
|
72
|
-
throw new Error(`playdrop_update_worker_failed: ${state}. Next action: run "playdrop worker status --json" on the worker host and inspect the wrapper failure marker under
|
|
71
|
+
throw new Error(`playdrop_update_worker_failed: ${state}. Next action: run "playdrop worker status --json" on the worker host and inspect the wrapper failure marker under the configured worker state directory.`);
|
|
73
72
|
}
|
|
74
73
|
if (worker.activeUpdateIntentId !== input.intentId
|
|
75
74
|
&& worker.lifecycleState === 'READY'
|
|
@@ -72,7 +72,10 @@ export declare function buildClaudeExecArgs(input: {
|
|
|
72
72
|
denyReadRoots?: string[] | null;
|
|
73
73
|
pluginDir?: string | null;
|
|
74
74
|
}): string[];
|
|
75
|
-
export declare function buildClaudePermissionSettings(
|
|
75
|
+
export declare function buildClaudePermissionSettings(input?: {
|
|
76
|
+
workspaceDir?: string | null;
|
|
77
|
+
denyReadRoots?: string[];
|
|
78
|
+
}): {
|
|
76
79
|
permissions: {
|
|
77
80
|
allow: string[];
|
|
78
81
|
deny: string[];
|
|
@@ -86,5 +89,9 @@ export declare function buildWorkerChildEnv(input: {
|
|
|
86
89
|
envName: string;
|
|
87
90
|
eventDir?: string;
|
|
88
91
|
devPort?: number;
|
|
92
|
+
homeDir?: string;
|
|
93
|
+
codexHomeDir?: string;
|
|
94
|
+
claudeConfigDir?: string;
|
|
95
|
+
playwrightBrowsersPath?: string;
|
|
89
96
|
baseEnv?: NodeJS.ProcessEnv;
|
|
90
97
|
}): NodeJS.ProcessEnv;
|
|
@@ -440,6 +440,9 @@ function buildCodexExecArgs(input) {
|
|
|
440
440
|
const args = [
|
|
441
441
|
'exec',
|
|
442
442
|
'--json',
|
|
443
|
+
'--ephemeral',
|
|
444
|
+
'--ignore-user-config',
|
|
445
|
+
'--ignore-rules',
|
|
443
446
|
'--sandbox',
|
|
444
447
|
sandboxMode,
|
|
445
448
|
'--cd',
|
|
@@ -471,16 +474,22 @@ function buildClaudeExecArgs(input) {
|
|
|
471
474
|
if (!model) {
|
|
472
475
|
throw new Error('agent_task_assignment_model_missing');
|
|
473
476
|
}
|
|
474
|
-
const permissionSettings = buildClaudePermissionSettings(
|
|
477
|
+
const permissionSettings = buildClaudePermissionSettings({
|
|
478
|
+
workspaceDir: input.workspaceDir,
|
|
479
|
+
denyReadRoots: input.denyReadRoots ?? [],
|
|
480
|
+
});
|
|
475
481
|
const permissionMode = input.chrome ? 'bypassPermissions' : 'acceptEdits';
|
|
476
482
|
const tools = input.chrome
|
|
477
483
|
? [...CLAUDE_BASE_TOOL_NAMES, ...CLAUDE_CHROME_TOOL_NAMES].join(',')
|
|
478
484
|
: CLAUDE_BASE_TOOL_NAMES.join(',');
|
|
485
|
+
const allowedPermissionRules = buildClaudeAllowedPermissionRules(input.workspaceDir);
|
|
479
486
|
const allowedTools = input.chrome
|
|
480
|
-
? [...
|
|
481
|
-
:
|
|
487
|
+
? [...allowedPermissionRules, ...CLAUDE_CHROME_TOOL_NAMES]
|
|
488
|
+
: allowedPermissionRules;
|
|
482
489
|
const args = [
|
|
483
490
|
'-p',
|
|
491
|
+
'--safe-mode',
|
|
492
|
+
'--strict-mcp-config',
|
|
484
493
|
'--model',
|
|
485
494
|
model,
|
|
486
495
|
'--output-format',
|
|
@@ -543,17 +552,12 @@ const CLAUDE_CHROME_TOOL_NAMES = [
|
|
|
543
552
|
'mcp__claude-in-chrome__tabs_create_mcp',
|
|
544
553
|
'mcp__claude-in-chrome__upload_image',
|
|
545
554
|
];
|
|
546
|
-
const
|
|
547
|
-
'Read',
|
|
548
|
-
'Edit',
|
|
549
|
-
'Write',
|
|
555
|
+
const CLAUDE_BUILD_PERMISSION_RULES = [
|
|
550
556
|
'Glob',
|
|
551
557
|
'Grep',
|
|
552
|
-
'Bash',
|
|
553
558
|
'Bash(playdrop *)',
|
|
554
559
|
'Bash(./bin/playdrop *)',
|
|
555
560
|
'Bash(bin/playdrop *)',
|
|
556
|
-
'Bash(/Users/*/PlayDropWorker/tasks/*/bin/playdrop *)',
|
|
557
561
|
'Bash(node *)',
|
|
558
562
|
'Bash(npm *)',
|
|
559
563
|
'Bash(mkdir *)',
|
|
@@ -563,12 +567,7 @@ const CLAUDE_ALLOWED_PERMISSION_RULES = [
|
|
|
563
567
|
'Bash(sips *)',
|
|
564
568
|
'Bash(ls *)',
|
|
565
569
|
'Bash(pwd)',
|
|
566
|
-
'Bash(cat *)',
|
|
567
|
-
'Bash(head *)',
|
|
568
|
-
'Bash(tail *)',
|
|
569
570
|
'Bash(rg *)',
|
|
570
|
-
'Bash(grep *)',
|
|
571
|
-
'Bash(find *)',
|
|
572
571
|
'Bash(which *)',
|
|
573
572
|
'Bash(wc *)',
|
|
574
573
|
'Bash(stat *)',
|
|
@@ -587,11 +586,27 @@ function normalizeClaudePermissionPath(value) {
|
|
|
587
586
|
}
|
|
588
587
|
return node_path_1.default.resolve(trimmed).replace(/\\/g, '/').replace(/\/+$/, '');
|
|
589
588
|
}
|
|
590
|
-
function
|
|
589
|
+
function buildClaudeAllowedPermissionRules(workspaceDir) {
|
|
590
|
+
const workspaceRoot = typeof workspaceDir === 'string' && workspaceDir.trim()
|
|
591
|
+
? normalizeClaudePermissionPath(workspaceDir)
|
|
592
|
+
: null;
|
|
593
|
+
if (!workspaceRoot) {
|
|
594
|
+
throw new Error('worker_claude_workspace_missing');
|
|
595
|
+
}
|
|
596
|
+
const absoluteWorkspacePattern = `//${workspaceRoot.replace(/^\/+/, '')}/**`;
|
|
597
|
+
return [
|
|
598
|
+
`Read(${absoluteWorkspacePattern})`,
|
|
599
|
+
`Edit(${absoluteWorkspacePattern})`,
|
|
600
|
+
`Write(${absoluteWorkspacePattern})`,
|
|
601
|
+
`Bash(${workspaceRoot}/bin/playdrop *)`,
|
|
602
|
+
...CLAUDE_BUILD_PERMISSION_RULES,
|
|
603
|
+
];
|
|
604
|
+
}
|
|
605
|
+
function buildClaudePermissionSettings(input = {}) {
|
|
591
606
|
return {
|
|
592
607
|
permissions: {
|
|
593
|
-
allow:
|
|
594
|
-
deny: buildClaudeDeniedPermissionRules(denyReadRoots),
|
|
608
|
+
allow: buildClaudeAllowedPermissionRules(input.workspaceDir),
|
|
609
|
+
deny: buildClaudeDeniedPermissionRules(input.denyReadRoots ?? []),
|
|
595
610
|
},
|
|
596
611
|
};
|
|
597
612
|
}
|
|
@@ -624,6 +639,34 @@ function buildWorkerChildEnv(input) {
|
|
|
624
639
|
child[key] = value;
|
|
625
640
|
}
|
|
626
641
|
}
|
|
642
|
+
if (input.homeDir !== undefined) {
|
|
643
|
+
const homeDir = input.homeDir.trim();
|
|
644
|
+
if (!homeDir || !node_path_1.default.isAbsolute(homeDir)) {
|
|
645
|
+
throw new Error('invalid_worker_child_home_dir');
|
|
646
|
+
}
|
|
647
|
+
child.HOME = homeDir;
|
|
648
|
+
}
|
|
649
|
+
if (input.codexHomeDir !== undefined) {
|
|
650
|
+
const codexHomeDir = input.codexHomeDir.trim();
|
|
651
|
+
if (!codexHomeDir || !node_path_1.default.isAbsolute(codexHomeDir)) {
|
|
652
|
+
throw new Error('invalid_worker_child_codex_home_dir');
|
|
653
|
+
}
|
|
654
|
+
child.CODEX_HOME = codexHomeDir;
|
|
655
|
+
}
|
|
656
|
+
if (input.claudeConfigDir !== undefined) {
|
|
657
|
+
const claudeConfigDir = input.claudeConfigDir.trim();
|
|
658
|
+
if (!claudeConfigDir || !node_path_1.default.isAbsolute(claudeConfigDir)) {
|
|
659
|
+
throw new Error('invalid_worker_child_claude_config_dir');
|
|
660
|
+
}
|
|
661
|
+
child.CLAUDE_CONFIG_DIR = claudeConfigDir;
|
|
662
|
+
}
|
|
663
|
+
if (input.playwrightBrowsersPath !== undefined) {
|
|
664
|
+
const playwrightBrowsersPath = input.playwrightBrowsersPath.trim();
|
|
665
|
+
if (!playwrightBrowsersPath || !node_path_1.default.isAbsolute(playwrightBrowsersPath)) {
|
|
666
|
+
throw new Error('invalid_worker_playwright_browsers_path');
|
|
667
|
+
}
|
|
668
|
+
child.PLAYWRIGHT_BROWSERS_PATH = playwrightBrowsersPath;
|
|
669
|
+
}
|
|
627
670
|
child.PATH = typeof base.PATH === 'string' && base.PATH
|
|
628
671
|
? `${input.binDir}${node_path_1.default.delimiter}${base.PATH}`
|
|
629
672
|
: input.binDir;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
-
import { type AgentExecutionTarget, type AgentRuntime, type AgentTaskKind, type AgentTaskNextStepSuggestion, type AgentTaskResponse, type AgentWorkerCapabilities, type AgentWorkerResponse, type WorkerAgentTaskAssignmentResponse, type WorkerAgentTaskBaseSourceResponse, type WorkerAgentTaskWorkspaceFileResponse, type WorkerClaimAgentTaskResponse } from '@playdrop/types';
|
|
2
|
+
import { type AgentExecutionTarget, type AgentRuntime, type AgentTaskKind, type AgentTaskNextStepSuggestion, type AgentTaskResponse, type AgentWorkerCapabilities, type AgentWorkerResponse, type AgentWorkerUpdatePolicyResponse, type WorkerAgentTaskAssignmentResponse, type WorkerAgentTaskBaseSourceResponse, type WorkerAgentTaskWorkspaceFileResponse, type WorkerClaimAgentTaskResponse } from '@playdrop/types';
|
|
3
|
+
import { loadConfig } from '../config';
|
|
3
4
|
import { type WorkerPlaydropAssetRequirement } from './upload';
|
|
4
5
|
import { type LoggedProcessResult, type LoggedProcessTranscriptChunk } from './worker/runtime';
|
|
5
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';
|
|
@@ -11,6 +12,8 @@ type WorkerStartOptions = {
|
|
|
11
12
|
once?: boolean;
|
|
12
13
|
name?: string;
|
|
13
14
|
superviseStdin?: boolean;
|
|
15
|
+
supervisor?: string;
|
|
16
|
+
devPluginWorkingTree?: string;
|
|
14
17
|
};
|
|
15
18
|
type WorkerStatusOptions = {
|
|
16
19
|
env?: string;
|
|
@@ -33,12 +36,6 @@ type WorkerLocalStatus = {
|
|
|
33
36
|
chromiumInstalled?: boolean;
|
|
34
37
|
executablePath?: string;
|
|
35
38
|
} | null;
|
|
36
|
-
plugin: {
|
|
37
|
-
ready: boolean;
|
|
38
|
-
root: string | null;
|
|
39
|
-
version: string | null;
|
|
40
|
-
error: string | null;
|
|
41
|
-
};
|
|
42
39
|
agents: AgentWorkerCapabilities['agents'];
|
|
43
40
|
capabilities: AgentWorkerCapabilities | null;
|
|
44
41
|
setupActions: WorkerSetupAction[];
|
|
@@ -57,6 +54,7 @@ type WorkerStatusPayload = {
|
|
|
57
54
|
reachable: boolean;
|
|
58
55
|
authenticated: boolean;
|
|
59
56
|
worker: AgentWorkerResponse | null;
|
|
57
|
+
updatePolicy?: AgentWorkerUpdatePolicyResponse;
|
|
60
58
|
error?: string;
|
|
61
59
|
};
|
|
62
60
|
};
|
|
@@ -164,19 +162,25 @@ export declare function assertTaskUploadResultMatchesContext(input: {
|
|
|
164
162
|
};
|
|
165
163
|
}): void;
|
|
166
164
|
export declare function stageAssignmentWorkspaceFiles(workspaceDir: string, files: readonly WorkerAgentTaskWorkspaceFileResponse[]): Promise<void>;
|
|
167
|
-
export declare function resolvePlaydropPluginRoot(input?: {
|
|
168
|
-
homeDir?: string;
|
|
169
|
-
env?: NodeJS.ProcessEnv;
|
|
170
|
-
}): string;
|
|
171
165
|
export declare function stagePlaydropPluginReferences(input: {
|
|
172
166
|
workspaceDir: string;
|
|
173
167
|
pluginRoot: string;
|
|
174
168
|
kind: AgentTaskKind;
|
|
175
169
|
}): Promise<string[]>;
|
|
170
|
+
export declare function stageCodexTaskSkills(input: {
|
|
171
|
+
workspaceDir: string;
|
|
172
|
+
stagedPluginPaths: readonly string[];
|
|
173
|
+
}): Promise<string[]>;
|
|
174
|
+
export declare function assertAssignmentPluginBundleExtractionBounds(buffer: Buffer): void;
|
|
176
175
|
export declare function stageAssignmentPluginBundle(input: {
|
|
177
176
|
workspaceDir: string;
|
|
178
177
|
pluginBundle: NonNullable<WorkerAgentTaskAssignmentResponse['pluginBundle']>;
|
|
179
178
|
}): Promise<string>;
|
|
179
|
+
export declare function resolveDevPluginWorkingTree(input: {
|
|
180
|
+
workingTree?: string;
|
|
181
|
+
env: string;
|
|
182
|
+
nodeEnv?: string;
|
|
183
|
+
}): string | null;
|
|
180
184
|
export declare function discoverWorkerProjectRoot(workspaceDir: string): string;
|
|
181
185
|
export declare function readWorkerTaskState(): WorkerTaskState | null;
|
|
182
186
|
export declare function buildWorkerHealthAlertText(input: WorkerHealthAlertInput): string;
|
|
@@ -186,6 +190,7 @@ export declare function isWorkerTransientNetworkError(error: unknown): boolean;
|
|
|
186
190
|
export declare function classifyWorkerHeartbeatError(error: unknown): 'session_expired' | 'lease_invalid' | 'transient_network' | null;
|
|
187
191
|
export declare function assertWorkerClaimErrorRetryable(error: unknown): void;
|
|
188
192
|
export declare function resolvePlaydropAssetRequirementFromText(value: unknown): WorkerPlaydropAssetRequirement | null;
|
|
193
|
+
export declare function resolvePlaywrightBrowsersPathFromExecutable(executablePath: string): string;
|
|
189
194
|
export declare function loadWorkerEnvFile(envName: string | undefined, cwd?: string): string | null;
|
|
190
195
|
export declare function buildWorkerCapabilities(input: {
|
|
191
196
|
codexVersion?: string | null;
|
|
@@ -227,6 +232,28 @@ export declare function assertWorkerProjectVersionBumped(input: {
|
|
|
227
232
|
projectVersion: string;
|
|
228
233
|
appName: string;
|
|
229
234
|
}): void;
|
|
235
|
+
export declare function prepareCodexRunHomes(input: {
|
|
236
|
+
workspaceDir: string;
|
|
237
|
+
workerUsername: string;
|
|
238
|
+
envName: string;
|
|
239
|
+
sourceCodexHome?: string;
|
|
240
|
+
playdropConfig?: ReturnType<typeof loadConfig>;
|
|
241
|
+
}): Promise<{
|
|
242
|
+
homeDir: string;
|
|
243
|
+
codexHomeDir: string;
|
|
244
|
+
}>;
|
|
245
|
+
export declare function prepareClaudeRunConfig(input: {
|
|
246
|
+
workspaceDir: string;
|
|
247
|
+
workerUsername: string;
|
|
248
|
+
envName: string;
|
|
249
|
+
sourceCredentialsPath?: string;
|
|
250
|
+
playdropConfig?: ReturnType<typeof loadConfig>;
|
|
251
|
+
}): Promise<{
|
|
252
|
+
homeDir: string;
|
|
253
|
+
claudeConfigDir: string;
|
|
254
|
+
}>;
|
|
255
|
+
export declare function removeAgentRunCredentials(workspaceDir: string): Promise<void>;
|
|
256
|
+
export declare function removeStaleAgentRunCredentials(workerHomeDir?: string): Promise<void>;
|
|
230
257
|
export declare function buildAgentFailureCode(agent: AgentRuntime, result: LoggedProcessResult): string;
|
|
231
258
|
export declare function describeAgentFailureForEvent(errorCode: string): string;
|
|
232
259
|
export declare function createLocalPlaydropShim(input: {
|
|
@@ -245,6 +272,7 @@ export declare function buildWorkerSetupActions(input: {
|
|
|
245
272
|
executablePath?: string;
|
|
246
273
|
} | null;
|
|
247
274
|
}): WorkerSetupAction[];
|
|
275
|
+
export declare function resolveWorkerStateDir(): string;
|
|
248
276
|
type WorkerRuntimeState = {
|
|
249
277
|
schemaVersion: 1;
|
|
250
278
|
pid: number;
|
|
@@ -254,6 +282,30 @@ type WorkerRuntimeState = {
|
|
|
254
282
|
target: AgentExecutionTarget;
|
|
255
283
|
startedAt: string;
|
|
256
284
|
};
|
|
285
|
+
export type WorkerSupervisorType = 'app' | 'terminal';
|
|
286
|
+
export type WorkerSupervisorLockOwner = {
|
|
287
|
+
schemaVersion: 1;
|
|
288
|
+
supervisorType: WorkerSupervisorType;
|
|
289
|
+
pid: number;
|
|
290
|
+
processStartIdentity: string;
|
|
291
|
+
workerName: string;
|
|
292
|
+
createdAt: string;
|
|
293
|
+
};
|
|
294
|
+
export type WorkerSupervisorLock = {
|
|
295
|
+
file: string;
|
|
296
|
+
owner: WorkerSupervisorLockOwner;
|
|
297
|
+
};
|
|
298
|
+
export declare function resolveWorkerSupervisorLockFile(input?: {
|
|
299
|
+
workerHomeDirectory?: string;
|
|
300
|
+
}): string;
|
|
301
|
+
export declare function parseLinuxProcessStartIdentity(stat: string): string | null;
|
|
302
|
+
export declare function removeStaleWorkerSupervisorLockIfUnchanged(file: string, observedHolder: WorkerSupervisorLockOwner): boolean;
|
|
303
|
+
export declare function acquireWorkerSupervisorLock(input: {
|
|
304
|
+
supervisorType: WorkerSupervisorType;
|
|
305
|
+
workerName: string;
|
|
306
|
+
file?: string;
|
|
307
|
+
}): WorkerSupervisorLock;
|
|
308
|
+
export declare function releaseWorkerSupervisorLock(lock: WorkerSupervisorLock): void;
|
|
257
309
|
export declare function readActivePersonalWorkerRuntimeState(): WorkerRuntimeState | null;
|
|
258
310
|
export declare function parseLaunchAgentProgramArguments(plist: string): string[];
|
|
259
311
|
export declare function assertLaunchAgentPlistCompatible(input: {
|