@playdrop/playdrop-cli 0.10.20 → 0.10.21
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 +5 -1
- package/dist/apps/build.js +3 -2
- package/dist/commands/agents.js +30 -3
- package/dist/commands/clients.js +98 -1
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.js +50 -0
- package/dist/commands/generation.js +3 -0
- package/dist/commands/worker/runtime.js +6 -0
- package/dist/commands/worker.d.ts +16 -0
- package/dist/commands/worker.js +72 -4
- package/dist/commands/workspaces.js +2 -1
- package/node_modules/@playdrop/config/client-meta.json +5 -1
- package/node_modules/@playdrop/config/dist/test/validateClientEnvironment.test.js +39 -0
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +6 -2
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +2 -1
- package/package.json +5 -5
package/config/client-meta.json
CHANGED
package/dist/apps/build.js
CHANGED
|
@@ -33,9 +33,10 @@ const projectBuildCache = new Map();
|
|
|
33
33
|
const typescriptSourceCache = new Map();
|
|
34
34
|
async function runNpmScript(projectDir, script) {
|
|
35
35
|
await (0, promises_1.mkdir)(projectDir, { recursive: true });
|
|
36
|
-
const npmCommand = process.platform === 'win32' ? '
|
|
36
|
+
const npmCommand = process.platform === 'win32' ? process.env.ComSpec || 'cmd.exe' : 'npm';
|
|
37
|
+
const npmArgs = process.platform === 'win32' ? ['/d', '/s', '/c', `npm run ${script}`] : ['run', script];
|
|
37
38
|
await new Promise((resolveRun, rejectRun) => {
|
|
38
|
-
const child = (0, node_child_process_1.spawn)(npmCommand,
|
|
39
|
+
const child = (0, node_child_process_1.spawn)(npmCommand, npmArgs, {
|
|
39
40
|
cwd: projectDir,
|
|
40
41
|
stdio: 'inherit',
|
|
41
42
|
env: { ...process.env },
|
package/dist/commands/agents.js
CHANGED
|
@@ -88,11 +88,16 @@ async function detectCli(command, runShell) {
|
|
|
88
88
|
if (pathProbe.exitCode !== 0 || pathProbe.output.trim().length === 0) {
|
|
89
89
|
return { installed: false };
|
|
90
90
|
}
|
|
91
|
+
const path = pathProbe.output.split('\n')[0]?.trim();
|
|
91
92
|
const versionProbe = await runShell(`${command} --version`);
|
|
93
|
+
const version = versionProbe.output.split('\n')[0]?.trim() || undefined;
|
|
94
|
+
if (versionProbe.exitCode !== 0 && !/\d+\.\d+\.\d+/.test(versionProbe.output)) {
|
|
95
|
+
return { installed: false, path };
|
|
96
|
+
}
|
|
92
97
|
return {
|
|
93
98
|
installed: true,
|
|
94
|
-
path
|
|
95
|
-
version
|
|
99
|
+
path,
|
|
100
|
+
version,
|
|
96
101
|
};
|
|
97
102
|
}
|
|
98
103
|
function detectApp(appNames, homeDir, appExists) {
|
|
@@ -285,6 +290,9 @@ function printAgentsStatus(status) {
|
|
|
285
290
|
}
|
|
286
291
|
}
|
|
287
292
|
function runCodexPluginAction(action, runCommand) {
|
|
293
|
+
if (!ensureAgentCliUsable('codex', runCommand)) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
288
296
|
const marketplaceCommand = action === 'install-plugin'
|
|
289
297
|
? ['plugin', 'marketplace', 'add', PLAYDROP_PLUGIN_MARKETPLACE]
|
|
290
298
|
: ['plugin', 'marketplace', 'upgrade', 'playdrop'];
|
|
@@ -340,6 +348,9 @@ function runCodexPluginAction(action, runCommand) {
|
|
|
340
348
|
console.log('Open Codex and run /plugins, then install or enable PlayDrop.');
|
|
341
349
|
}
|
|
342
350
|
function runClaudePluginAction(action, runCommand) {
|
|
351
|
+
if (!ensureAgentCliUsable('claude', runCommand)) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
343
354
|
if (action === 'install-plugin') {
|
|
344
355
|
const marketplace = runCommand('claude', ['plugin', 'marketplace', 'add', PLAYDROP_PLUGIN_MARKETPLACE]);
|
|
345
356
|
if (marketplace.status !== 0) {
|
|
@@ -379,6 +390,21 @@ function runClaudePluginAction(action, runCommand) {
|
|
|
379
390
|
console.log('Updated the PlayDrop plugin for Claude.');
|
|
380
391
|
console.log('Run /reload-plugins in Claude Code or restart Claude Code to apply it.');
|
|
381
392
|
}
|
|
393
|
+
function ensureAgentCliUsable(command, runCommand) {
|
|
394
|
+
const result = runCommand(command, ['--version']);
|
|
395
|
+
const output = commandOutput(result).trim();
|
|
396
|
+
if (result.status === 0 || /\d+\.\d+\.\d+/.test(output)) {
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
const label = command === 'codex' ? 'Codex' : 'Claude';
|
|
400
|
+
console.error(`${label} CLI is missing or not runnable.`);
|
|
401
|
+
console.error(`${command} --version failed with code ${result.status ?? 'unknown'}.`);
|
|
402
|
+
if (output) {
|
|
403
|
+
console.error(`Output: ${output}`);
|
|
404
|
+
}
|
|
405
|
+
process.exitCode = 1;
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
382
408
|
function readInstalledPlayDropPlugin(command, runCommand) {
|
|
383
409
|
const list = runCommand(command, ['plugin', 'list', '--json']);
|
|
384
410
|
if (list.status !== 0) {
|
|
@@ -477,9 +503,10 @@ async function runLoginShell(command) {
|
|
|
477
503
|
}
|
|
478
504
|
function runCommandSync(command, args) {
|
|
479
505
|
const result = (0, node_child_process_1.spawnSync)(command, args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
506
|
+
const spawnError = result.error instanceof Error ? result.error.message : '';
|
|
480
507
|
return {
|
|
481
508
|
status: result.status,
|
|
482
509
|
stdout: result.stdout ?? '',
|
|
483
|
-
stderr: result.stderr ?? '',
|
|
510
|
+
stderr: [result.stderr ?? '', spawnError].filter(Boolean).join('\n'),
|
|
484
511
|
};
|
|
485
512
|
}
|
package/dist/commands/clients.js
CHANGED
|
@@ -17,6 +17,9 @@ const output_1 = require("../output");
|
|
|
17
17
|
const versionCompare_1 = require("../versionCompare");
|
|
18
18
|
const MAC_APP_PATH = '/Applications/PlayDrop.app';
|
|
19
19
|
const MAC_CLIENT_LATEST_URL = 'https://www.playdrop.ai/api/mac-client/latest';
|
|
20
|
+
const WINDOWS_CLIENT_EXECUTABLE = 'PlayDrop.exe';
|
|
21
|
+
const WINDOWS_CLIENT_INSTALL_HINT = 'Install or update PlayDrop for Windows with the Windows installer or the in-app updater.';
|
|
22
|
+
const WINDOWS_CLIENT_PATH_ENV = 'PLAYDROP_WINDOWS_CLIENT_PATH';
|
|
20
23
|
async function showClientStatus(options = {}) {
|
|
21
24
|
const status = await getClientStatus(options);
|
|
22
25
|
if (options.json) {
|
|
@@ -29,6 +32,9 @@ async function showClientStatus(options = {}) {
|
|
|
29
32
|
}
|
|
30
33
|
async function getClientStatus(options = {}) {
|
|
31
34
|
const platform = options.platform ?? process.platform;
|
|
35
|
+
if (platform === 'win32') {
|
|
36
|
+
return getWindowsClientStatus(options);
|
|
37
|
+
}
|
|
32
38
|
if (platform !== 'darwin') {
|
|
33
39
|
return {
|
|
34
40
|
platform,
|
|
@@ -65,6 +71,24 @@ async function updateClient(options = {}) {
|
|
|
65
71
|
}
|
|
66
72
|
async function launchClient(options = {}) {
|
|
67
73
|
const platform = options.platform ?? process.platform;
|
|
74
|
+
if (platform === 'win32') {
|
|
75
|
+
const status = await getWindowsClientStatus(options);
|
|
76
|
+
if (!status.installed || !status.appPath) {
|
|
77
|
+
console.error('PlayDrop for Windows is not installed.');
|
|
78
|
+
console.error(WINDOWS_CLIENT_INSTALL_HINT);
|
|
79
|
+
process.exitCode = 1;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const runCommand = options.runCommand ?? runCommandSync;
|
|
83
|
+
const result = runCommand(status.appPath, []);
|
|
84
|
+
if (result.status !== 0) {
|
|
85
|
+
console.error(`Failed to launch PlayDrop from ${status.appPath}.`);
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
console.log('Launched PlayDrop.');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
68
92
|
if (platform !== 'darwin') {
|
|
69
93
|
printUnsupportedPlatform(platform);
|
|
70
94
|
return;
|
|
@@ -87,6 +111,12 @@ async function launchClient(options = {}) {
|
|
|
87
111
|
}
|
|
88
112
|
async function installOrUpdateClient(action, options) {
|
|
89
113
|
const platform = options.platform ?? process.platform;
|
|
114
|
+
if (platform === 'win32') {
|
|
115
|
+
console.error(`PlayDrop for Windows ${action} is not available through the CLI yet.`);
|
|
116
|
+
console.error(WINDOWS_CLIENT_INSTALL_HINT);
|
|
117
|
+
process.exitCode = 1;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
90
120
|
if (platform !== 'darwin') {
|
|
91
121
|
printUnsupportedPlatform(platform);
|
|
92
122
|
return;
|
|
@@ -129,7 +159,12 @@ function printClientStatus(status) {
|
|
|
129
159
|
}
|
|
130
160
|
if (!status.installed) {
|
|
131
161
|
console.log('PlayDrop client: missing');
|
|
132
|
-
|
|
162
|
+
if (status.nextAction) {
|
|
163
|
+
console.log(`Run: ${status.nextAction}`);
|
|
164
|
+
}
|
|
165
|
+
else if (status.reason) {
|
|
166
|
+
console.log(status.reason);
|
|
167
|
+
}
|
|
133
168
|
return;
|
|
134
169
|
}
|
|
135
170
|
const version = status.version ? ` ${status.version}` : '';
|
|
@@ -141,6 +176,68 @@ function printClientStatus(status) {
|
|
|
141
176
|
console.log('Run: playdrop clients update');
|
|
142
177
|
}
|
|
143
178
|
}
|
|
179
|
+
async function getWindowsClientStatus(options) {
|
|
180
|
+
const appPath = resolveWindowsClientPath(options.appPath);
|
|
181
|
+
if (!appPath) {
|
|
182
|
+
return {
|
|
183
|
+
platform: 'win32',
|
|
184
|
+
supported: true,
|
|
185
|
+
status: 'missing',
|
|
186
|
+
installed: false,
|
|
187
|
+
reason: WINDOWS_CLIENT_INSTALL_HINT,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const bundle = readWindowsClientInfo(appPath);
|
|
191
|
+
return {
|
|
192
|
+
platform: 'win32',
|
|
193
|
+
supported: true,
|
|
194
|
+
status: 'installed',
|
|
195
|
+
installed: true,
|
|
196
|
+
appPath,
|
|
197
|
+
version: bundle.version,
|
|
198
|
+
build: bundle.build,
|
|
199
|
+
updateAvailable: false,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function resolveWindowsClientPath(explicitPath) {
|
|
203
|
+
const candidates = windowsClientPathCandidates(explicitPath);
|
|
204
|
+
return candidates.find((candidate) => (0, node_fs_1.existsSync)(candidate));
|
|
205
|
+
}
|
|
206
|
+
function windowsClientPathCandidates(explicitPath) {
|
|
207
|
+
const candidates = [];
|
|
208
|
+
const add = (value) => {
|
|
209
|
+
if (value && value.trim().length > 0) {
|
|
210
|
+
candidates.push(value.trim());
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
add(explicitPath);
|
|
214
|
+
add(process.env[WINDOWS_CLIENT_PATH_ENV]);
|
|
215
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
216
|
+
if (localAppData) {
|
|
217
|
+
add((0, node_path_1.join)(localAppData, 'PlayDrop.Windows', 'current', WINDOWS_CLIENT_EXECUTABLE));
|
|
218
|
+
add((0, node_path_1.join)(localAppData, 'PlayDrop', 'current', WINDOWS_CLIENT_EXECUTABLE));
|
|
219
|
+
}
|
|
220
|
+
add(process.env.ProgramFiles ? (0, node_path_1.join)(process.env.ProgramFiles, 'PlayDrop', WINDOWS_CLIENT_EXECUTABLE) : undefined);
|
|
221
|
+
add(process.env['ProgramFiles(x86)'] ? (0, node_path_1.join)(process.env['ProgramFiles(x86)'], 'PlayDrop', WINDOWS_CLIENT_EXECUTABLE) : undefined);
|
|
222
|
+
return [...new Set(candidates)];
|
|
223
|
+
}
|
|
224
|
+
function readWindowsClientInfo(appPath) {
|
|
225
|
+
for (const versionPath of [
|
|
226
|
+
(0, node_path_1.join)((0, node_path_1.dirname)(appPath), 'sq.version'),
|
|
227
|
+
(0, node_path_1.join)((0, node_path_1.dirname)((0, node_path_1.dirname)(appPath)), 'sq.version'),
|
|
228
|
+
]) {
|
|
229
|
+
try {
|
|
230
|
+
const version = (0, node_fs_1.readFileSync)(versionPath, 'utf8').trim();
|
|
231
|
+
if (version.length > 0) {
|
|
232
|
+
return { version };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// Development and portable builds may not have a Velopack version file.
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return {};
|
|
240
|
+
}
|
|
144
241
|
async function fetchLatestMacClientManifest() {
|
|
145
242
|
try {
|
|
146
243
|
const response = await fetch(MAC_CLIENT_LATEST_URL, { headers: { Accept: 'application/json' } });
|
|
@@ -22,6 +22,14 @@ interface CliHealth {
|
|
|
22
22
|
updateStatus: 'current' | 'update-available' | 'unknown';
|
|
23
23
|
updateAvailable?: boolean;
|
|
24
24
|
nextAction?: string;
|
|
25
|
+
command?: CliCommandHealth;
|
|
26
|
+
}
|
|
27
|
+
interface CliCommandHealth {
|
|
28
|
+
available: boolean;
|
|
29
|
+
path?: string;
|
|
30
|
+
npmGlobalBin?: string;
|
|
31
|
+
npmShimPath?: string;
|
|
32
|
+
issue?: 'not-on-path' | 'shim-missing';
|
|
25
33
|
}
|
|
26
34
|
export interface DoctorReport {
|
|
27
35
|
cli: CliHealth;
|
package/dist/commands/doctor.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.showDoctor = showDoctor;
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
5
6
|
const clientInfo_1 = require("../clientInfo");
|
|
6
7
|
const config_1 = require("../config");
|
|
7
8
|
const output_1 = require("../output");
|
|
@@ -61,11 +62,13 @@ async function getAccountHealth() {
|
|
|
61
62
|
}
|
|
62
63
|
async function getCliHealth() {
|
|
63
64
|
const currentVersion = (0, clientInfo_1.getCliVersion)();
|
|
65
|
+
const command = getCliCommandHealth();
|
|
64
66
|
const result = (0, shellProbe_1.runShell)('npm view @playdrop/playdrop-cli version --json');
|
|
65
67
|
if (result.exitCode !== 0 || !result.output.trim()) {
|
|
66
68
|
return {
|
|
67
69
|
version: (0, clientInfo_1.getCliVersionLabel)(),
|
|
68
70
|
updateStatus: 'unknown',
|
|
71
|
+
command,
|
|
69
72
|
};
|
|
70
73
|
}
|
|
71
74
|
const latestVersion = parseNpmVersion(result.output);
|
|
@@ -73,6 +76,7 @@ async function getCliHealth() {
|
|
|
73
76
|
return {
|
|
74
77
|
version: (0, clientInfo_1.getCliVersionLabel)(),
|
|
75
78
|
updateStatus: 'unknown',
|
|
79
|
+
command,
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
82
|
const updateAvailable = (0, versionCompare_1.firstVersionIsNewer)(latestVersion, currentVersion);
|
|
@@ -82,8 +86,34 @@ async function getCliHealth() {
|
|
|
82
86
|
updateStatus: updateAvailable ? 'update-available' : 'current',
|
|
83
87
|
updateAvailable,
|
|
84
88
|
nextAction: updateAvailable ? 'playdrop update' : undefined,
|
|
89
|
+
command,
|
|
85
90
|
};
|
|
86
91
|
}
|
|
92
|
+
function getCliCommandHealth(platform = process.platform) {
|
|
93
|
+
const pathProbe = (0, shellProbe_1.runShell)((0, shellProbe_1.buildCommandPathProbe)('playdrop', platform), platform);
|
|
94
|
+
if (pathProbe.exitCode === 0 && pathProbe.output.trim()) {
|
|
95
|
+
return {
|
|
96
|
+
available: true,
|
|
97
|
+
path: pathProbe.output.split('\n')[0]?.trim(),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (platform !== 'win32') {
|
|
101
|
+
return { available: false, issue: 'not-on-path' };
|
|
102
|
+
}
|
|
103
|
+
const npmGlobalBin = resolveNpmGlobalBin(platform);
|
|
104
|
+
const npmShimPath = npmGlobalBin ? (0, node_path_1.join)(npmGlobalBin, 'playdrop.cmd') : undefined;
|
|
105
|
+
const shimExists = npmShimPath ? (0, node_fs_1.existsSync)(npmShimPath) : false;
|
|
106
|
+
return {
|
|
107
|
+
available: false,
|
|
108
|
+
npmGlobalBin,
|
|
109
|
+
npmShimPath: shimExists ? npmShimPath : undefined,
|
|
110
|
+
issue: shimExists ? 'not-on-path' : 'shim-missing',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function resolveNpmGlobalBin(platform) {
|
|
114
|
+
const prefix = (0, shellProbe_1.runShell)('npm config get prefix', platform).output.split('\n')[0]?.trim();
|
|
115
|
+
return prefix && prefix.length > 0 ? prefix : undefined;
|
|
116
|
+
}
|
|
87
117
|
function parseNpmVersion(output) {
|
|
88
118
|
const trimmed = output.trim();
|
|
89
119
|
try {
|
|
@@ -164,6 +194,7 @@ function printDoctorReport(report) {
|
|
|
164
194
|
? ', current'
|
|
165
195
|
: ', update status unknown';
|
|
166
196
|
console.log(`PlayDrop CLI: ${report.cli.version}${cliUpdate}`);
|
|
197
|
+
printCliCommandDoctorStatus(report.cli.command);
|
|
167
198
|
console.log(`Account: ${report.account.loggedIn ? report.account.identity ?? 'logged in' : 'not logged in'}`);
|
|
168
199
|
console.log(`Node: ${report.tools.node.installed ? report.tools.node.version ?? 'installed' : 'missing'}`);
|
|
169
200
|
console.log(`npm: ${report.tools.npm.installed ? report.tools.npm.version ?? 'installed' : 'missing'}`);
|
|
@@ -192,6 +223,22 @@ function printDoctorReport(report) {
|
|
|
192
223
|
}
|
|
193
224
|
}
|
|
194
225
|
}
|
|
226
|
+
function printCliCommandDoctorStatus(command) {
|
|
227
|
+
if (!command) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (command.available) {
|
|
231
|
+
console.log(`CLI command: ${command.path ?? 'available'}`);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
console.log('CLI command: not on PATH');
|
|
235
|
+
if (command.npmShimPath) {
|
|
236
|
+
console.log(` Direct npm shim: ${command.npmShimPath}`);
|
|
237
|
+
}
|
|
238
|
+
if (command.npmGlobalBin) {
|
|
239
|
+
console.log(` Add ${command.npmGlobalBin} to PATH for new PowerShell sessions.`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
195
242
|
function printNativeClientDoctorStatus(status) {
|
|
196
243
|
if (!status.supported) {
|
|
197
244
|
console.log(`Native client: unavailable on ${status.platform}`);
|
|
@@ -199,6 +246,9 @@ function printNativeClientDoctorStatus(status) {
|
|
|
199
246
|
}
|
|
200
247
|
if (!status.installed) {
|
|
201
248
|
console.log('Native client: missing');
|
|
249
|
+
if (status.reason) {
|
|
250
|
+
console.log(` ${status.reason}`);
|
|
251
|
+
}
|
|
202
252
|
return;
|
|
203
253
|
}
|
|
204
254
|
const statusLabel = status.updateAvailable ? 'update available' : 'up to date';
|
|
@@ -419,6 +419,9 @@ function resolveImageAttachment(value, optionName) {
|
|
|
419
419
|
return undefined;
|
|
420
420
|
}
|
|
421
421
|
const trimmed = value.trim();
|
|
422
|
+
if ((0, node_path_1.isAbsolute)(trimmed)) {
|
|
423
|
+
return resolveLocalImageAttachment(optionName, trimmed);
|
|
424
|
+
}
|
|
422
425
|
try {
|
|
423
426
|
const parsed = new URL(trimmed);
|
|
424
427
|
if (parsed.protocol !== 'https:') {
|
|
@@ -525,6 +525,12 @@ function normalizeClaudePermissionPath(value) {
|
|
|
525
525
|
if (!trimmed) {
|
|
526
526
|
return null;
|
|
527
527
|
}
|
|
528
|
+
if (/^\/[^/]/.test(trimmed)) {
|
|
529
|
+
return node_path_1.default.posix.resolve(trimmed).replace(/\/+$/, '');
|
|
530
|
+
}
|
|
531
|
+
if (/^[A-Za-z]:[\\/]/.test(trimmed) || /^\\\\/.test(trimmed)) {
|
|
532
|
+
return node_path_1.default.win32.resolve(trimmed).replace(/\\/g, '/').replace(/\/+$/, '');
|
|
533
|
+
}
|
|
528
534
|
return node_path_1.default.resolve(trimmed).replace(/\\/g, '/').replace(/\/+$/, '');
|
|
529
535
|
}
|
|
530
536
|
function buildClaudePermissionSettings(denyReadRoots = []) {
|
|
@@ -31,6 +31,7 @@ type WorkerLocalStatus = {
|
|
|
31
31
|
playwright: {
|
|
32
32
|
version?: string;
|
|
33
33
|
chromiumInstalled?: boolean;
|
|
34
|
+
executablePath?: string;
|
|
34
35
|
} | null;
|
|
35
36
|
plugin: {
|
|
36
37
|
ready: boolean;
|
|
@@ -40,6 +41,13 @@ type WorkerLocalStatus = {
|
|
|
40
41
|
};
|
|
41
42
|
agents: AgentWorkerCapabilities['agents'];
|
|
42
43
|
capabilities: AgentWorkerCapabilities | null;
|
|
44
|
+
setupActions: WorkerSetupAction[];
|
|
45
|
+
};
|
|
46
|
+
type WorkerSetupAction = {
|
|
47
|
+
reason: string;
|
|
48
|
+
label: string;
|
|
49
|
+
command?: string;
|
|
50
|
+
message: string;
|
|
43
51
|
};
|
|
44
52
|
type WorkerStatusPayload = {
|
|
45
53
|
schemaVersion: 1;
|
|
@@ -205,6 +213,14 @@ export declare function createLocalPlaydropShim(input: {
|
|
|
205
213
|
attempt: number;
|
|
206
214
|
devPort: number;
|
|
207
215
|
}): Promise<string>;
|
|
216
|
+
export declare function buildWorkerSetupActions(input: {
|
|
217
|
+
degradedReasons: string[];
|
|
218
|
+
playwright?: {
|
|
219
|
+
version?: string;
|
|
220
|
+
chromiumInstalled?: boolean;
|
|
221
|
+
executablePath?: string;
|
|
222
|
+
} | null;
|
|
223
|
+
}): WorkerSetupAction[];
|
|
208
224
|
type WorkerRuntimeState = {
|
|
209
225
|
schemaVersion: 1;
|
|
210
226
|
pid: number;
|
package/dist/commands/worker.js
CHANGED
|
@@ -38,6 +38,7 @@ exports.assertWorkerProjectVersionBumped = assertWorkerProjectVersionBumped;
|
|
|
38
38
|
exports.buildAgentFailureCode = buildAgentFailureCode;
|
|
39
39
|
exports.describeAgentFailureForEvent = describeAgentFailureForEvent;
|
|
40
40
|
exports.createLocalPlaydropShim = createLocalPlaydropShim;
|
|
41
|
+
exports.buildWorkerSetupActions = buildWorkerSetupActions;
|
|
41
42
|
exports.readActivePersonalWorkerRuntimeState = readActivePersonalWorkerRuntimeState;
|
|
42
43
|
exports.parseLaunchAgentProgramArguments = parseLaunchAgentProgramArguments;
|
|
43
44
|
exports.assertLaunchAgentPlistCompatible = assertLaunchAgentPlistCompatible;
|
|
@@ -1181,9 +1182,9 @@ function probePlaywrightChromium() {
|
|
|
1181
1182
|
const version = typeof packageJson.version === 'string' && packageJson.version.trim() ? packageJson.version.trim() : 'unknown';
|
|
1182
1183
|
const chromiumInstalled = (0, node_fs_1.existsSync)(executablePath);
|
|
1183
1184
|
if (!chromiumInstalled) {
|
|
1184
|
-
|
|
1185
|
+
return { version, chromiumInstalled: false, executablePath };
|
|
1185
1186
|
}
|
|
1186
|
-
return { version, chromiumInstalled };
|
|
1187
|
+
return { version, chromiumInstalled: true, executablePath };
|
|
1187
1188
|
}
|
|
1188
1189
|
function readWorkerModelList(agent, envName) {
|
|
1189
1190
|
const raw = node_process_1.default.env[envName];
|
|
@@ -1260,7 +1261,10 @@ function buildWorkerCapabilities(input) {
|
|
|
1260
1261
|
const cursorVersion = typeof input === 'string' ? null : input.cursorVersion?.trim() || null;
|
|
1261
1262
|
const cursorAuthenticated = typeof input === 'string' ? false : input.cursorAuthenticated === true;
|
|
1262
1263
|
const npmVersion = typeof input === 'string' ? null : input.npmVersion?.trim() || null;
|
|
1263
|
-
const
|
|
1264
|
+
const rawPlaywright = typeof input === 'string' ? null : input.playwright ?? null;
|
|
1265
|
+
const playwright = rawPlaywright
|
|
1266
|
+
? { version: rawPlaywright.version, chromiumInstalled: rawPlaywright.chromiumInstalled }
|
|
1267
|
+
: null;
|
|
1264
1268
|
const maxParallelTasks = typeof input === 'string'
|
|
1265
1269
|
? DEFAULT_WORKER_MAX_PARALLEL_TASKS
|
|
1266
1270
|
: input.maxParallelTasks ?? DEFAULT_WORKER_MAX_PARALLEL_TASKS;
|
|
@@ -1954,6 +1958,9 @@ function probeCodexInstallation() {
|
|
|
1954
1958
|
const versionResult = (0, shellProbe_1.runShell)('codex --version');
|
|
1955
1959
|
const match = /(\d+\.\d+\.\d+\S*)/.exec(versionResult.output);
|
|
1956
1960
|
if (!match?.[1]) {
|
|
1961
|
+
if (versionResult.exitCode !== 0) {
|
|
1962
|
+
return null;
|
|
1963
|
+
}
|
|
1957
1964
|
throw new Error(`codex_version_check_failed: "codex --version" exited with code ${versionResult.exitCode} and reported no version. Output: ${versionResult.output.slice(0, 200)}`);
|
|
1958
1965
|
}
|
|
1959
1966
|
if (versionResult.exitCode !== 0) {
|
|
@@ -1970,7 +1977,13 @@ function probeClaudeInstallation() {
|
|
|
1970
1977
|
}
|
|
1971
1978
|
const versionResult = (0, shellProbe_1.runShell)('claude --version');
|
|
1972
1979
|
const match = /(\d+\.\d+\.\d+\S*)/.exec(versionResult.output);
|
|
1973
|
-
if (!match?.[1]
|
|
1980
|
+
if (!match?.[1]) {
|
|
1981
|
+
if (versionResult.exitCode !== 0) {
|
|
1982
|
+
return null;
|
|
1983
|
+
}
|
|
1984
|
+
throw new Error(`claude_version_check_failed: "claude --version" exited with code ${versionResult.exitCode} and reported no version. Output: ${versionResult.output.slice(0, 200)}`);
|
|
1985
|
+
}
|
|
1986
|
+
if (versionResult.exitCode !== 0) {
|
|
1974
1987
|
throw new Error(`claude_version_check_failed: "claude --version" exited with code ${versionResult.exitCode} and reported no version. Output: ${versionResult.output.slice(0, 200)}`);
|
|
1975
1988
|
}
|
|
1976
1989
|
return { claudeVersion: match[1] };
|
|
@@ -2076,6 +2089,9 @@ function collectWorkerLocalStatus() {
|
|
|
2076
2089
|
}
|
|
2077
2090
|
try {
|
|
2078
2091
|
playwright = probePlaywrightChromium();
|
|
2092
|
+
if (!playwright.chromiumInstalled) {
|
|
2093
|
+
degradedReasons.push('worker_preflight_chromium_missing');
|
|
2094
|
+
}
|
|
2079
2095
|
}
|
|
2080
2096
|
catch (error) {
|
|
2081
2097
|
degradedReasons.push(errorCode(error));
|
|
@@ -2143,8 +2159,53 @@ function collectWorkerLocalStatus() {
|
|
|
2143
2159
|
},
|
|
2144
2160
|
agents: capabilities?.agents ?? [],
|
|
2145
2161
|
capabilities,
|
|
2162
|
+
setupActions: buildWorkerSetupActions({
|
|
2163
|
+
degradedReasons: uniqueReasons,
|
|
2164
|
+
playwright,
|
|
2165
|
+
}),
|
|
2146
2166
|
};
|
|
2147
2167
|
}
|
|
2168
|
+
function buildWorkerSetupActions(input) {
|
|
2169
|
+
const reasons = new Set(input.degradedReasons);
|
|
2170
|
+
const actions = [];
|
|
2171
|
+
if (reasons.has('worker_preflight_chromium_missing')) {
|
|
2172
|
+
const version = input.playwright?.version && input.playwright.version !== 'unknown'
|
|
2173
|
+
? `@${input.playwright.version}`
|
|
2174
|
+
: '';
|
|
2175
|
+
actions.push({
|
|
2176
|
+
reason: 'worker_preflight_chromium_missing',
|
|
2177
|
+
label: 'Install Playwright Chromium',
|
|
2178
|
+
command: `npx playwright${version} install chromium`,
|
|
2179
|
+
message: input.playwright?.executablePath
|
|
2180
|
+
? `Chromium is required for PlayDrop launch checks. Expected browser path: ${input.playwright.executablePath}.`
|
|
2181
|
+
: 'Chromium is required for PlayDrop launch checks.',
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
if (reasons.has('worker_preflight_playwright_missing')) {
|
|
2185
|
+
actions.push({
|
|
2186
|
+
reason: 'worker_preflight_playwright_missing',
|
|
2187
|
+
label: 'Repair the PlayDrop CLI install',
|
|
2188
|
+
command: 'npm install -g @playdrop/playdrop-cli@latest',
|
|
2189
|
+
message: 'The PlayDrop CLI package could not load playwright-core from its installed dependencies.',
|
|
2190
|
+
});
|
|
2191
|
+
}
|
|
2192
|
+
if (reasons.has('playdrop_plugin_staging_manifest_missing')) {
|
|
2193
|
+
actions.push({
|
|
2194
|
+
reason: 'playdrop_plugin_staging_manifest_missing',
|
|
2195
|
+
label: 'Install or update the PlayDrop agent plugin',
|
|
2196
|
+
command: 'playdrop agents status --json',
|
|
2197
|
+
message: 'Run the CLI-reported nextAction.command for a supported local agent so playdrop-worker-staging.json is available.',
|
|
2198
|
+
});
|
|
2199
|
+
}
|
|
2200
|
+
if (reasons.has('agent_cli_not_found')) {
|
|
2201
|
+
actions.push({
|
|
2202
|
+
reason: 'agent_cli_not_found',
|
|
2203
|
+
label: 'Install a supported agent CLI',
|
|
2204
|
+
message: 'Install and authenticate Codex or Claude Code, then rerun playdrop agents status --json.',
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
return actions;
|
|
2208
|
+
}
|
|
2148
2209
|
function getWorkerStateDir() {
|
|
2149
2210
|
return node_path_1.default.join(node_os_1.default.homedir(), '.playdrop', 'worker');
|
|
2150
2211
|
}
|
|
@@ -2782,6 +2843,13 @@ function printWorkerStatus(payload) {
|
|
|
2782
2843
|
if (payload.local.degradedReasons.length > 0) {
|
|
2783
2844
|
console.log(` Reasons: ${payload.local.degradedReasons.join(', ')}`);
|
|
2784
2845
|
}
|
|
2846
|
+
if (payload.local.setupActions.length > 0) {
|
|
2847
|
+
console.log(' Setup actions:');
|
|
2848
|
+
for (const action of payload.local.setupActions) {
|
|
2849
|
+
console.log(` ${action.label}${action.command ? `: ${action.command}` : ''}`);
|
|
2850
|
+
console.log(` ${action.message}`);
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2785
2853
|
if (!payload.server.authenticated) {
|
|
2786
2854
|
console.log('Server worker: not authenticated');
|
|
2787
2855
|
}
|
|
@@ -307,7 +307,8 @@ function isMacProtectedWorkspaceRoot(rootPath, homeDir = node_os_1.default.homed
|
|
|
307
307
|
return protectedRoots.some((protectedRoot) => absoluteRoot === protectedRoot || isPathInside(absoluteRoot, protectedRoot));
|
|
308
308
|
}
|
|
309
309
|
function isPathInside(candidate, parent) {
|
|
310
|
-
|
|
310
|
+
const relativePath = (0, node_path_1.relative)(parent, candidate);
|
|
311
|
+
return Boolean(relativePath) && !relativePath.startsWith('..') && !(0, node_path_1.isAbsolute)(relativePath);
|
|
311
312
|
}
|
|
312
313
|
function isAllowedProtectedPath(candidate, allowedProtectedRoots) {
|
|
313
314
|
return allowedProtectedRoots.some((allowedRoot) => candidate === allowedRoot || isPathInside(candidate, allowedRoot));
|
|
@@ -80,6 +80,45 @@ function runtime(overrides = {}) {
|
|
|
80
80
|
const result = (0, index_1.validateClientEnvironment)(info);
|
|
81
81
|
strict_1.default.equal(result.ok, true);
|
|
82
82
|
});
|
|
83
|
+
(0, node_test_1.default)('accepts windows-playdrop as a first-class Windows client identity', () => {
|
|
84
|
+
const meta = (0, index_1.loadClientMeta)();
|
|
85
|
+
const windowsRule = meta.clients['windows-playdrop'];
|
|
86
|
+
strict_1.default.ok(windowsRule, 'windows-playdrop metadata must be explicit');
|
|
87
|
+
const requiredVersion = windowsRule.minimumVersion ?? meta.version;
|
|
88
|
+
const requiredBuild = windowsRule.minimumBuild ?? meta.build;
|
|
89
|
+
const info = runtime({
|
|
90
|
+
client: 'windows-playdrop',
|
|
91
|
+
clientVersion: requiredVersion,
|
|
92
|
+
clientBuild: requiredBuild,
|
|
93
|
+
platform: 'windows',
|
|
94
|
+
platformVersion: '10.0.22631',
|
|
95
|
+
});
|
|
96
|
+
const result = (0, index_1.validateClientEnvironment)(info);
|
|
97
|
+
strict_1.default.equal(result.ok, true);
|
|
98
|
+
});
|
|
99
|
+
(0, node_test_1.default)('enforces the windows-playdrop alpha build floor', () => {
|
|
100
|
+
const meta = (0, index_1.loadClientMeta)();
|
|
101
|
+
const windowsRule = meta.clients['windows-playdrop'];
|
|
102
|
+
strict_1.default.ok(windowsRule?.minimumVersion, 'windows-playdrop requires an explicit minimum version');
|
|
103
|
+
const requiredBuild = windowsRule.minimumBuild;
|
|
104
|
+
if (typeof requiredBuild !== 'number') {
|
|
105
|
+
strict_1.default.fail('windows-playdrop requires an explicit minimum build');
|
|
106
|
+
}
|
|
107
|
+
const info = runtime({
|
|
108
|
+
client: 'windows-playdrop',
|
|
109
|
+
clientVersion: windowsRule.minimumVersion,
|
|
110
|
+
clientBuild: Math.max(requiredBuild - 1, 0),
|
|
111
|
+
platform: 'windows',
|
|
112
|
+
platformVersion: '10.0.22631',
|
|
113
|
+
});
|
|
114
|
+
const result = (0, index_1.validateClientEnvironment)(info);
|
|
115
|
+
strict_1.default.equal(result.ok, false);
|
|
116
|
+
if (!result.ok) {
|
|
117
|
+
strict_1.default.equal(result.code, 'unsupported_client_build');
|
|
118
|
+
strict_1.default.equal(result.requiredVersion, windowsRule.minimumVersion);
|
|
119
|
+
strict_1.default.equal(result.requiredBuild, requiredBuild);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
83
122
|
(0, node_test_1.default)('keeps accepting the legacy Apple client id during rollout', () => {
|
|
84
123
|
const meta = (0, index_1.loadClientMeta)();
|
|
85
124
|
const requiredVersion = meta.clients['all']?.minimumVersion ?? meta.version;
|