@playdrop/playdrop-cli 0.12.2 → 0.12.4
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/appUrls.d.ts +9 -0
- package/dist/appUrls.js +29 -0
- package/dist/apps/index.d.ts +2 -0
- package/dist/apps/index.js +2 -0
- package/dist/apps/loadCheck.d.ts +4 -0
- package/dist/apps/loadCheck.js +48 -13
- package/dist/apps/upload.d.ts +2 -0
- package/dist/apps/upload.js +1 -0
- package/dist/clientInfo.js +0 -11
- package/dist/commands/captureListing.d.ts +3 -1
- package/dist/commands/captureListing.js +24 -8
- package/dist/commands/check.js +40 -26
- package/dist/commands/create.js +47 -2
- package/dist/commands/devShared.d.ts +5 -0
- package/dist/commands/devShared.js +9 -0
- package/dist/commands/login.d.ts +13 -1
- package/dist/commands/login.js +72 -5
- package/dist/commands/taskCaptureSession.d.ts +4 -0
- package/dist/commands/taskCaptureSession.js +21 -0
- package/dist/commands/upload.d.ts +1 -0
- package/dist/commands/upload.js +16 -1
- package/dist/commands/worker.d.ts +5 -1
- package/dist/commands/worker.js +187 -119
- package/dist/index.js +2 -0
- package/node_modules/@playdrop/config/client-meta.json +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +9 -10
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +3 -60
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ exports.createDevError = createDevError;
|
|
|
4
4
|
exports.isNetworkError = isNetworkError;
|
|
5
5
|
exports.fetchDevUser = fetchDevUser;
|
|
6
6
|
exports.fetchDevUsername = fetchDevUsername;
|
|
7
|
+
exports.resolveDevCreatorUsername = resolveDevCreatorUsername;
|
|
7
8
|
exports.assertAppRegistered = assertAppRegistered;
|
|
8
9
|
exports.resolveDevTarget = resolveDevTarget;
|
|
9
10
|
exports.findProjectInfo = findProjectInfo;
|
|
@@ -54,6 +55,14 @@ async function fetchDevUsername(client) {
|
|
|
54
55
|
const user = await fetchDevUser(client);
|
|
55
56
|
return user.username;
|
|
56
57
|
}
|
|
58
|
+
async function resolveDevCreatorUsername(input) {
|
|
59
|
+
const workspaceOwnerUsername = input.workspaceOwnerUsername?.trim() ?? '';
|
|
60
|
+
if (workspaceOwnerUsername) {
|
|
61
|
+
return workspaceOwnerUsername;
|
|
62
|
+
}
|
|
63
|
+
const accountUsername = input.accountUsername?.trim() ?? '';
|
|
64
|
+
return accountUsername || await fetchDevUsername(input.client);
|
|
65
|
+
}
|
|
57
66
|
async function assertAppRegistered(client, creatorUsername, appName) {
|
|
58
67
|
const response = await client.fetchAppBySlug(creatorUsername, appName);
|
|
59
68
|
if (!response?.app) {
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Readable } from 'stream';
|
|
1
2
|
import { resetOpenBrowserForTests, setOpenBrowserForTests } from '../browser';
|
|
2
3
|
export { resetOpenBrowserForTests, setOpenBrowserForTests, };
|
|
3
4
|
type LoginOptions = {
|
|
@@ -5,6 +6,17 @@ type LoginOptions = {
|
|
|
5
6
|
password?: string;
|
|
6
7
|
key?: string;
|
|
7
8
|
handoffToken?: string;
|
|
9
|
+
handoffTokenStdin?: boolean;
|
|
8
10
|
json?: boolean;
|
|
9
11
|
};
|
|
10
|
-
|
|
12
|
+
type LoginRuntime = {
|
|
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>;
|
package/dist/commands/login.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.setOpenBrowserForTests = exports.resetOpenBrowserForTests = void 0;
|
|
3
|
+
exports.HandoffTokenStdinError = exports.HANDOFF_TOKEN_STDIN_MAX_BYTES = exports.setOpenBrowserForTests = exports.resetOpenBrowserForTests = void 0;
|
|
4
|
+
exports.readHandoffTokenFromStdin = readHandoffTokenFromStdin;
|
|
4
5
|
exports.login = login;
|
|
5
6
|
const types_1 = require("@playdrop/types");
|
|
6
7
|
const config_1 = require("../config");
|
|
@@ -12,6 +13,40 @@ Object.defineProperty(exports, "resetOpenBrowserForTests", { enumerable: true, g
|
|
|
12
13
|
Object.defineProperty(exports, "setOpenBrowserForTests", { enumerable: true, get: function () { return browser_1.setOpenBrowserForTests; } });
|
|
13
14
|
const output_1 = require("../output");
|
|
14
15
|
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
|
+
}
|
|
15
50
|
function sleep(ms) {
|
|
16
51
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
17
52
|
}
|
|
@@ -193,7 +228,7 @@ async function loginInBrowser(env, baseUrl, options = {}) {
|
|
|
193
228
|
], { command: 'login' });
|
|
194
229
|
process.exitCode = 1;
|
|
195
230
|
}
|
|
196
|
-
async function login(env, options = {}) {
|
|
231
|
+
async function login(env, options = {}, runtime = {}) {
|
|
197
232
|
const envConfig = (0, environment_1.resolveEnvironmentConfig)(env);
|
|
198
233
|
if (!envConfig) {
|
|
199
234
|
const choices = (0, environment_1.formatEnvironmentList)();
|
|
@@ -207,12 +242,16 @@ async function login(env, options = {}) {
|
|
|
207
242
|
const username = options.username?.trim();
|
|
208
243
|
const password = options.password;
|
|
209
244
|
const apiKey = options.key?.trim();
|
|
210
|
-
const
|
|
245
|
+
const argvHandoffToken = options.handoffToken?.trim();
|
|
246
|
+
const hasApiKeyArgument = options.key !== undefined;
|
|
247
|
+
const hasHandoffTokenArgument = options.handoffToken !== undefined;
|
|
248
|
+
const readsHandoffTokenFromStdin = options.handoffTokenStdin === true;
|
|
211
249
|
const hasDirectCredentials = Boolean(username || password);
|
|
212
250
|
const explicitLoginMethods = [
|
|
213
|
-
|
|
251
|
+
hasApiKeyArgument ? 'key' : null,
|
|
214
252
|
hasDirectCredentials ? 'credentials' : null,
|
|
215
|
-
|
|
253
|
+
hasHandoffTokenArgument ? 'handoff' : null,
|
|
254
|
+
readsHandoffTokenFromStdin ? 'handoff-stdin' : null,
|
|
216
255
|
].filter(Boolean);
|
|
217
256
|
if (explicitLoginMethods.length > 1) {
|
|
218
257
|
(0, messages_1.printErrorWithHelp)('Choose exactly one login method.', [
|
|
@@ -220,6 +259,7 @@ async function login(env, options = {}) {
|
|
|
220
259
|
'Use "--username" with "--password" for direct login.',
|
|
221
260
|
'Use "--key" by itself for API key login.',
|
|
222
261
|
'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.',
|
|
223
263
|
], { command: 'login' });
|
|
224
264
|
process.exitCode = 1;
|
|
225
265
|
return;
|
|
@@ -232,6 +272,33 @@ async function login(env, options = {}) {
|
|
|
232
272
|
process.exitCode = 1;
|
|
233
273
|
return;
|
|
234
274
|
}
|
|
275
|
+
if (hasApiKeyArgument && !apiKey) {
|
|
276
|
+
(0, messages_1.printErrorWithHelp)('The API key cannot be empty.', ['Provide a non-empty value to "--key".'], { command: 'login' });
|
|
277
|
+
process.exitCode = 1;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (hasHandoffTokenArgument && !argvHandoffToken) {
|
|
281
|
+
(0, messages_1.printErrorWithHelp)('The native app handoff token cannot be empty.', ['Request a fresh handoff token from the PlayDrop app and retry.'], { command: 'login' });
|
|
282
|
+
process.exitCode = 1;
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
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
|
+
}
|
|
235
302
|
try {
|
|
236
303
|
if (apiKey) {
|
|
237
304
|
await loginWithKey(env, envConfig.apiBase, apiKey, { json: options.json });
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AppResponse } from '@playdrop/types';
|
|
2
|
+
import type { TaskCaptureSession } from '../appUrls';
|
|
3
|
+
import type { ResolvedWorkspaceAuthConfig } from '../workspaceAuth';
|
|
4
|
+
export declare function resolveTaskCaptureSession(workspaceAuth: ResolvedWorkspaceAuthConfig | null, app: AppResponse): TaskCaptureSession | null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveTaskCaptureSession = resolveTaskCaptureSession;
|
|
4
|
+
function resolveTaskCaptureSession(workspaceAuth, app) {
|
|
5
|
+
const taskId = workspaceAuth?.config.taskId;
|
|
6
|
+
const taskToken = workspaceAuth?.config.taskToken?.trim() ?? '';
|
|
7
|
+
if (taskId === undefined && !taskToken) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
if (!Number.isInteger(taskId) || Number(taskId) <= 0 || !taskToken) {
|
|
11
|
+
throw new Error('worker_capture_session_incomplete');
|
|
12
|
+
}
|
|
13
|
+
if (!Number.isInteger(app.id) || Number(app.id) <= 0) {
|
|
14
|
+
throw new Error('registered_app_id_missing');
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
appId: Number(app.id),
|
|
18
|
+
taskId: Number(taskId),
|
|
19
|
+
taskToken,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -9,6 +9,7 @@ export declare function upload(pathOrName: string, options?: UploadCommandOption
|
|
|
9
9
|
export type WorkerAppPublishInput = {
|
|
10
10
|
client: ApiClient;
|
|
11
11
|
taskId: number;
|
|
12
|
+
taskToken: string;
|
|
12
13
|
kind: 'NEW_GAME' | 'REMIX_GAME' | 'GAME_UPDATE';
|
|
13
14
|
executionTarget?: AgentExecutionTarget;
|
|
14
15
|
expectedAppName?: string | null;
|
package/dist/commands/upload.js
CHANGED
|
@@ -1225,6 +1225,11 @@ const WORKER_SUPERVISOR_SOURCE_EXCLUDES = [
|
|
|
1225
1225
|
'.playdrop-task-events/',
|
|
1226
1226
|
'bin/playdrop',
|
|
1227
1227
|
];
|
|
1228
|
+
const WORKER_SUPERVISOR_BUNDLE_EXCLUDES = [
|
|
1229
|
+
...WORKER_SUPERVISOR_SOURCE_EXCLUDES,
|
|
1230
|
+
'assets/marketing/',
|
|
1231
|
+
'listing/',
|
|
1232
|
+
];
|
|
1228
1233
|
const MIN_HERO_PORTRAIT_WIDTH = 512;
|
|
1229
1234
|
const MIN_HERO_PORTRAIT_HEIGHT = 768;
|
|
1230
1235
|
const MIN_HERO_LANDSCAPE_WIDTH = 768;
|
|
@@ -2273,7 +2278,7 @@ async function publishWorkerAppProject(input) {
|
|
|
2273
2278
|
...appTask,
|
|
2274
2279
|
versionVisibility: 'PRIVATE',
|
|
2275
2280
|
sourceArchiveExcludeRelativeFiles: WORKER_SUPERVISOR_SOURCE_EXCLUDES,
|
|
2276
|
-
bundleArchiveExcludeRelativeFiles:
|
|
2281
|
+
bundleArchiveExcludeRelativeFiles: WORKER_SUPERVISOR_BUNDLE_EXCLUDES,
|
|
2277
2282
|
};
|
|
2278
2283
|
if (input.kind === 'REMIX_GAME') {
|
|
2279
2284
|
const expectedRemixRef = input.remixSourceRef?.trim() ?? '';
|
|
@@ -2331,6 +2336,15 @@ async function publishWorkerAppProject(input) {
|
|
|
2331
2336
|
if (typeof app.id !== 'number' || !Number.isInteger(app.id) || app.id <= 0) {
|
|
2332
2337
|
throw new Error(`App "${task.name}" registration did not return an app id.`);
|
|
2333
2338
|
}
|
|
2339
|
+
const taskToken = input.taskToken.trim();
|
|
2340
|
+
if (!taskToken) {
|
|
2341
|
+
throw new Error('agent_task_token_missing');
|
|
2342
|
+
}
|
|
2343
|
+
const captureSession = {
|
|
2344
|
+
appId: app.id,
|
|
2345
|
+
taskId: input.taskId,
|
|
2346
|
+
taskToken,
|
|
2347
|
+
};
|
|
2334
2348
|
if (input.kind === 'NEW_GAME' || input.kind === 'REMIX_GAME') {
|
|
2335
2349
|
assertNewGameListingScreenshots(task);
|
|
2336
2350
|
assertTaskPlaytestEvidenceManifest(task);
|
|
@@ -2346,6 +2360,7 @@ async function publishWorkerAppProject(input) {
|
|
|
2346
2360
|
ensureRegisteredAppShell: true,
|
|
2347
2361
|
agentTaskId: input.taskId,
|
|
2348
2362
|
mediaCaptureRequired: input.executionTarget === 'FIRST_PARTY',
|
|
2363
|
+
captureSession,
|
|
2349
2364
|
});
|
|
2350
2365
|
if (!uploadResult.versionCreated || !uploadResult.version) {
|
|
2351
2366
|
throw new Error(`App "${task.name}" upload did not return a created version.`);
|
|
@@ -187,11 +187,13 @@ export declare function classifyWorkerHeartbeatError(error: unknown): 'session_e
|
|
|
187
187
|
export declare function assertWorkerClaimErrorRetryable(error: unknown): void;
|
|
188
188
|
export declare function resolvePlaydropAssetRequirementFromText(value: unknown): WorkerPlaydropAssetRequirement | null;
|
|
189
189
|
export declare function loadWorkerEnvFile(envName: string | undefined, cwd?: string): string | null;
|
|
190
|
-
export declare function buildWorkerCapabilities(input:
|
|
190
|
+
export declare function buildWorkerCapabilities(input: {
|
|
191
191
|
codexVersion?: string | null;
|
|
192
192
|
codexAuthenticated?: boolean | null;
|
|
193
|
+
codexModels?: string[] | null;
|
|
193
194
|
claudeVersion?: string | null;
|
|
194
195
|
claudeAuthenticated?: boolean | null;
|
|
196
|
+
claudeModels?: string[] | null;
|
|
195
197
|
cursorVersion?: string | null;
|
|
196
198
|
cursorAuthenticated?: boolean | null;
|
|
197
199
|
npmVersion?: string | null;
|
|
@@ -233,6 +235,8 @@ export declare function createLocalPlaydropShim(input: {
|
|
|
233
235
|
attempt: number;
|
|
234
236
|
devPort: number;
|
|
235
237
|
}): Promise<string>;
|
|
238
|
+
export declare function parseCodexModelCatalog(output: string): string[];
|
|
239
|
+
export declare function parseClaudeModelCatalog(output: string): string[];
|
|
236
240
|
export declare function buildWorkerSetupActions(input: {
|
|
237
241
|
degradedReasons: string[];
|
|
238
242
|
playwright?: {
|