@playdrop/playdrop-cli 0.5.5 → 0.5.6
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/README.md +2 -1
- package/config/client-meta.json +4 -4
- package/dist/apps/upload.js +226 -80
- package/dist/assetSpecs.d.ts +1 -0
- package/dist/assetSpecs.js +22 -1
- package/dist/assets/model-artifacts.d.ts +2 -2
- package/dist/assets/model-artifacts.js +1 -1
- package/dist/captureRuntime.d.ts +1 -0
- package/dist/captureRuntime.js +3 -2
- package/dist/catalogue.d.ts +33 -8
- package/dist/catalogue.js +364 -46
- package/dist/commandContext.d.ts +5 -1
- package/dist/commandContext.js +90 -29
- package/dist/commands/browse.d.ts +3 -0
- package/dist/commands/browse.js +90 -17
- package/dist/commands/build.js +1 -1
- package/dist/commands/capture.d.ts +3 -0
- package/dist/commands/capture.js +121 -9
- package/dist/commands/captureListing.d.ts +2 -0
- package/dist/commands/captureListing.js +157 -61
- package/dist/commands/create.js +6 -28
- package/dist/commands/createRemixContent.js +4 -26
- package/dist/commands/creations.js +2 -3
- package/dist/commands/detail.js +24 -2
- package/dist/commands/dev.d.ts +8 -1
- package/dist/commands/dev.js +180 -2
- package/dist/commands/devRuntimeAssets.d.ts +34 -0
- package/dist/commands/devRuntimeAssets.js +308 -0
- package/dist/commands/devServer.d.ts +11 -0
- package/dist/commands/devServer.js +196 -13
- package/dist/commands/init.js +6 -24
- package/dist/commands/search.d.ts +4 -0
- package/dist/commands/search.js +68 -11
- package/dist/commands/upload-content.d.ts +3 -3
- package/dist/commands/upload-content.js +19 -38
- package/dist/commands/upload.js +67 -77
- package/dist/commands/validate.js +13 -20
- package/dist/commands/whoami.js +23 -26
- package/dist/devAuth.d.ts +16 -0
- package/dist/devAuth.js +60 -0
- package/dist/index.js +22 -4
- package/dist/taskSelection.js +4 -3
- package/dist/taskUtils.d.ts +2 -2
- package/dist/taskUtils.js +1 -1
- package/dist/uploadLog.d.ts +1 -1
- package/node_modules/@playdrop/ai-client/package.json +1 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts +43 -114
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.js +22 -0
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +11 -19
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +116 -106
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +2 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.js +13 -0
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +5 -5
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/payments.js +8 -8
- package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/search.js +24 -2
- package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts +13 -1
- package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/tags.js +52 -0
- package/node_modules/@playdrop/api-client/dist/index.d.ts +25 -29
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +23 -8
- package/node_modules/@playdrop/api-client/package.json +1 -1
- package/node_modules/@playdrop/boxel-core/package.json +1 -1
- package/node_modules/@playdrop/boxel-three/package.json +1 -1
- package/node_modules/@playdrop/config/client-meta.json +4 -4
- package/node_modules/@playdrop/config/package.json +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +124 -3
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +23 -0
- package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts +24 -0
- package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/app-capability-filters.js +72 -0
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts +3 -2
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset.d.ts +2 -3
- package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset.js +1 -1
- package/node_modules/@playdrop/types/dist/index.d.ts +2 -0
- package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.js +2 -0
- package/node_modules/@playdrop/types/dist/owned-assets.d.ts +21 -0
- package/node_modules/@playdrop/types/dist/owned-assets.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/owned-assets.js +35 -0
- package/node_modules/@playdrop/types/dist/player-meta.d.ts +28 -0
- package/node_modules/@playdrop/types/dist/player-meta.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.d.ts +111 -1
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.js +3 -0
- package/node_modules/@playdrop/types/package.json +1 -1
- package/node_modules/@playdrop/vox-three/package.json +1 -1
- package/package.json +1 -1
|
@@ -7,8 +7,11 @@ exports.DEV_ROUTER_PORT = void 0;
|
|
|
7
7
|
exports.parseMountConflictError = parseMountConflictError;
|
|
8
8
|
exports.buildLocalDevAppUrl = buildLocalDevAppUrl;
|
|
9
9
|
exports.ensureDevRouterRunning = ensureDevRouterRunning;
|
|
10
|
+
exports.updateMountedDevRuntimeAssetManifest = updateMountedDevRuntimeAssetManifest;
|
|
10
11
|
exports.startDevServer = startDevServer;
|
|
11
12
|
exports.isDevServerAvailable = isDevServerAvailable;
|
|
13
|
+
exports.createDevRouterServer = createDevRouterServer;
|
|
14
|
+
exports.listenDevRouterServer = listenDevRouterServer;
|
|
12
15
|
exports.runDevRouterServer = runDevRouterServer;
|
|
13
16
|
const node_http_1 = __importDefault(require("node:http"));
|
|
14
17
|
const node_fs_1 = require("node:fs");
|
|
@@ -18,6 +21,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
18
21
|
exports.DEV_ROUTER_PORT = 8888;
|
|
19
22
|
const DEV_ROUTER_HOST = '127.0.0.1';
|
|
20
23
|
const CONTROL_PREFIX = '/_playdrop';
|
|
24
|
+
const DEV_ROUTER_PROTOCOL_VERSION = 3;
|
|
21
25
|
const CONTENT_TYPE_BY_EXTENSION = {
|
|
22
26
|
'.html': 'text/html; charset=utf-8',
|
|
23
27
|
'.js': 'application/javascript; charset=utf-8',
|
|
@@ -128,6 +132,19 @@ function cleanupStaleRouterMounts() {
|
|
|
128
132
|
}
|
|
129
133
|
}
|
|
130
134
|
}
|
|
135
|
+
function findRouterMountByCreatorAndApp(creatorUsername, appName) {
|
|
136
|
+
let match = null;
|
|
137
|
+
for (const mount of routerMountsById.values()) {
|
|
138
|
+
if (mount.creatorUsername !== creatorUsername || mount.appName !== appName) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (match) {
|
|
142
|
+
throw new Error(`ambiguous_mount_lookup:${creatorUsername}/${appName}`);
|
|
143
|
+
}
|
|
144
|
+
match = mount;
|
|
145
|
+
}
|
|
146
|
+
return match;
|
|
147
|
+
}
|
|
131
148
|
function resolveStaticPath(staticRoot, rawRelativePath) {
|
|
132
149
|
const normalizedRelativePath = (0, node_path_1.normalize)(rawRelativePath.replace(/^\/+/, ''));
|
|
133
150
|
if (!normalizedRelativePath || normalizedRelativePath === '.' || normalizedRelativePath.startsWith('..')) {
|
|
@@ -159,6 +176,13 @@ function parseMountPath(pathname) {
|
|
|
159
176
|
assetPath: rest.map((entry) => decodeURIComponent(entry)).join('/'),
|
|
160
177
|
};
|
|
161
178
|
}
|
|
179
|
+
function getOwnedRuntimeAssetFile(mount, ownedAssetName, role) {
|
|
180
|
+
const files = mount.runtimeAssetManifest?.ownedAssetFiles?.[ownedAssetName];
|
|
181
|
+
if (!files) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
return files[role.trim().toLowerCase()] ?? null;
|
|
185
|
+
}
|
|
162
186
|
async function readJsonBody(req) {
|
|
163
187
|
const chunks = [];
|
|
164
188
|
for await (const chunk of req) {
|
|
@@ -192,7 +216,7 @@ async function fetchRouterJson(path, init = {}, port = exports.DEV_ROUTER_PORT)
|
|
|
192
216
|
}
|
|
193
217
|
return await response.json();
|
|
194
218
|
}
|
|
195
|
-
async function
|
|
219
|
+
async function fetchRouterHealth(port = exports.DEV_ROUTER_PORT, timeoutMs = 400) {
|
|
196
220
|
const controller = new AbortController();
|
|
197
221
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
198
222
|
timeout.unref?.();
|
|
@@ -201,18 +225,34 @@ async function isRouterHealthy(port = exports.DEV_ROUTER_PORT, timeoutMs = 400)
|
|
|
201
225
|
method: 'GET',
|
|
202
226
|
signal: controller.signal,
|
|
203
227
|
});
|
|
204
|
-
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
const payload = await response.json().catch(() => null);
|
|
232
|
+
if (!payload || typeof payload !== 'object' || payload.status !== 'ok') {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
return payload;
|
|
205
236
|
}
|
|
206
237
|
catch {
|
|
207
|
-
return
|
|
238
|
+
return null;
|
|
208
239
|
}
|
|
209
240
|
finally {
|
|
210
241
|
clearTimeout(timeout);
|
|
211
242
|
}
|
|
212
243
|
}
|
|
244
|
+
function isCompatibleRouterHealth(health) {
|
|
245
|
+
return health.protocolVersion === DEV_ROUTER_PROTOCOL_VERSION
|
|
246
|
+
&& health.capabilities?.runtimeAssetManifest === true
|
|
247
|
+
&& health.capabilities?.runtimeAssetManifestLookup === true;
|
|
248
|
+
}
|
|
213
249
|
async function ensureDevRouterRunning(port = exports.DEV_ROUTER_PORT) {
|
|
214
|
-
|
|
215
|
-
|
|
250
|
+
const existingHealth = await fetchRouterHealth(port);
|
|
251
|
+
if (existingHealth) {
|
|
252
|
+
if (isCompatibleRouterHealth(existingHealth)) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
throw new Error(`dev_router_incompatible:port_${port}`);
|
|
216
256
|
}
|
|
217
257
|
const cliEntrypoint = getCliEntrypointPath();
|
|
218
258
|
const child = (0, node_child_process_1.spawn)(process.execPath, [cliEntrypoint, 'project', '_dev-router', 'serve'], {
|
|
@@ -226,7 +266,8 @@ async function ensureDevRouterRunning(port = exports.DEV_ROUTER_PORT) {
|
|
|
226
266
|
child.unref();
|
|
227
267
|
const deadline = Date.now() + 5000;
|
|
228
268
|
while (Date.now() < deadline) {
|
|
229
|
-
|
|
269
|
+
const health = await fetchRouterHealth(port, 250);
|
|
270
|
+
if (health && isCompatibleRouterHealth(health)) {
|
|
230
271
|
return;
|
|
231
272
|
}
|
|
232
273
|
await new Promise((resolveTimeout) => setTimeout(resolveTimeout, 150));
|
|
@@ -271,8 +312,24 @@ async function unregisterDevMount(mountId, port = exports.DEV_ROUTER_PORT) {
|
|
|
271
312
|
throw new Error(`mount_unregister_failed:${response.status}`);
|
|
272
313
|
}
|
|
273
314
|
}
|
|
315
|
+
async function updateMountedDevRuntimeAssetManifest(input) {
|
|
316
|
+
const port = input.port ?? exports.DEV_ROUTER_PORT;
|
|
317
|
+
const response = await fetch(`http://${DEV_ROUTER_HOST}:${port}${CONTROL_PREFIX}/apps/dev/${encodeURIComponent(input.creatorUsername)}/${encodeURIComponent(input.appName)}/runtime-assets`, {
|
|
318
|
+
method: 'POST',
|
|
319
|
+
headers: { 'Content-Type': 'application/json' },
|
|
320
|
+
body: JSON.stringify({
|
|
321
|
+
runtimeAssetManifest: input.runtimeAssetManifest ?? null,
|
|
322
|
+
}),
|
|
323
|
+
});
|
|
324
|
+
if (!response.ok) {
|
|
325
|
+
const payload = await response.json().catch(() => null);
|
|
326
|
+
throw new Error(typeof payload?.error === 'string'
|
|
327
|
+
? payload.error
|
|
328
|
+
: `runtime_asset_manifest_update_failed:${response.status}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
274
331
|
async function startDevServer(options) {
|
|
275
|
-
const { appName, appType, creatorUsername, htmlPath, port, projectInfo } = options;
|
|
332
|
+
const { appName, appType, creatorUsername, htmlPath, port, projectInfo, runtimeAssetManifest } = options;
|
|
276
333
|
const ownerPid = process.pid;
|
|
277
334
|
let closing = false;
|
|
278
335
|
const devProcess = spawnDevScript(projectInfo);
|
|
@@ -301,6 +358,7 @@ async function startDevServer(options) {
|
|
|
301
358
|
staticRoot,
|
|
302
359
|
repoRoot,
|
|
303
360
|
ownerPid,
|
|
361
|
+
runtimeAssetManifest: runtimeAssetManifest ?? null,
|
|
304
362
|
}, port);
|
|
305
363
|
mountId = registeredMount.mountId;
|
|
306
364
|
appUrl = registeredMount.appUrl;
|
|
@@ -355,7 +413,8 @@ async function isDevServerAvailable(input, timeoutMs = 1000) {
|
|
|
355
413
|
clearTimeout(timeout);
|
|
356
414
|
}
|
|
357
415
|
}
|
|
358
|
-
|
|
416
|
+
function createDevRouterServer(initialPort = exports.DEV_ROUTER_PORT) {
|
|
417
|
+
let activePort = initialPort;
|
|
359
418
|
// eslint-disable-next-line complexity
|
|
360
419
|
const handleRequest = async (req, res) => {
|
|
361
420
|
const method = req.method || 'GET';
|
|
@@ -369,16 +428,86 @@ async function runDevRouterServer(port = Number(process.env.PLAYDROP_DEV_ROUTER_
|
|
|
369
428
|
res.end();
|
|
370
429
|
return;
|
|
371
430
|
}
|
|
372
|
-
let
|
|
431
|
+
let requestUrl;
|
|
373
432
|
try {
|
|
374
|
-
|
|
433
|
+
requestUrl = new URL(req.url || '/', `http://${DEV_ROUTER_HOST}:${activePort}`);
|
|
375
434
|
}
|
|
376
435
|
catch {
|
|
377
436
|
respondWithText(res, method, 400, 'Invalid URL path');
|
|
378
437
|
return;
|
|
379
438
|
}
|
|
439
|
+
const pathname = decodeURIComponent(requestUrl.pathname);
|
|
440
|
+
const searchParams = requestUrl.searchParams;
|
|
380
441
|
if (pathname === `${CONTROL_PREFIX}/health` && method === 'GET') {
|
|
381
|
-
sendJson(res, 200, {
|
|
442
|
+
sendJson(res, 200, {
|
|
443
|
+
status: 'ok',
|
|
444
|
+
protocolVersion: DEV_ROUTER_PROTOCOL_VERSION,
|
|
445
|
+
capabilities: {
|
|
446
|
+
runtimeAssetManifest: true,
|
|
447
|
+
runtimeAssetManifestLookup: true,
|
|
448
|
+
},
|
|
449
|
+
});
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (pathname.startsWith(`${CONTROL_PREFIX}/apps/dev/`) && pathname.endsWith('/runtime-assets') && method === 'GET') {
|
|
453
|
+
cleanupStaleRouterMounts();
|
|
454
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
455
|
+
if (segments.length !== 6) {
|
|
456
|
+
sendJson(res, 404, { error: 'runtime_asset_manifest_not_found' });
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const creatorUsername = decodeURIComponent(segments[3] ?? '');
|
|
460
|
+
const appName = decodeURIComponent(segments[4] ?? '');
|
|
461
|
+
if (!creatorUsername || !appName) {
|
|
462
|
+
sendJson(res, 400, { error: 'invalid_runtime_asset_manifest_lookup' });
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
let mount;
|
|
466
|
+
try {
|
|
467
|
+
mount = findRouterMountByCreatorAndApp(creatorUsername, appName);
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
const message = error instanceof Error ? error.message : 'ambiguous_mount_lookup';
|
|
471
|
+
sendJson(res, 409, { error: message });
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
if (!mount?.runtimeAssetManifest) {
|
|
475
|
+
sendJson(res, 404, { error: 'runtime_asset_manifest_not_found' });
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
sendJson(res, 200, mount.runtimeAssetManifest.response);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
if (pathname.startsWith(`${CONTROL_PREFIX}/apps/dev/`) && pathname.endsWith('/runtime-assets') && method === 'POST') {
|
|
482
|
+
cleanupStaleRouterMounts();
|
|
483
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
484
|
+
if (segments.length !== 6) {
|
|
485
|
+
sendJson(res, 404, { error: 'runtime_asset_manifest_not_found' });
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const creatorUsername = decodeURIComponent(segments[3] ?? '');
|
|
489
|
+
const appName = decodeURIComponent(segments[4] ?? '');
|
|
490
|
+
if (!creatorUsername || !appName) {
|
|
491
|
+
sendJson(res, 400, { error: 'invalid_runtime_asset_manifest_lookup' });
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
let mount;
|
|
495
|
+
try {
|
|
496
|
+
mount = findRouterMountByCreatorAndApp(creatorUsername, appName);
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
const message = error instanceof Error ? error.message : 'ambiguous_mount_lookup';
|
|
500
|
+
sendJson(res, 409, { error: message });
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (!mount) {
|
|
504
|
+
sendJson(res, 404, { error: 'runtime_asset_manifest_not_found' });
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
const body = await readJsonBody(req);
|
|
508
|
+
mount.runtimeAssetManifest = body.runtimeAssetManifest ?? null;
|
|
509
|
+
mount.updatedAt = Date.now();
|
|
510
|
+
sendJson(res, 200, { ok: true });
|
|
382
511
|
return;
|
|
383
512
|
}
|
|
384
513
|
if (pathname === `${CONTROL_PREFIX}/mounts` && method === 'POST') {
|
|
@@ -414,9 +543,10 @@ async function runDevRouterServer(port = Number(process.env.PLAYDROP_DEV_ROUTER_
|
|
|
414
543
|
}
|
|
415
544
|
existingMount.ownerPid = ownerPid;
|
|
416
545
|
existingMount.updatedAt = Date.now();
|
|
546
|
+
existingMount.runtimeAssetManifest = body.runtimeAssetManifest ?? null;
|
|
417
547
|
sendJson(res, 200, {
|
|
418
548
|
mountId: existingMount.id,
|
|
419
|
-
appUrl: buildLocalDevAppUrl({ creatorUsername, appType, appName, port }),
|
|
549
|
+
appUrl: buildLocalDevAppUrl({ creatorUsername, appType, appName, port: activePort }),
|
|
420
550
|
creatorUsername,
|
|
421
551
|
appType,
|
|
422
552
|
appName,
|
|
@@ -436,12 +566,13 @@ async function runDevRouterServer(port = Number(process.env.PLAYDROP_DEV_ROUTER_
|
|
|
436
566
|
repoRoot,
|
|
437
567
|
ownerPid,
|
|
438
568
|
updatedAt: Date.now(),
|
|
569
|
+
runtimeAssetManifest: body.runtimeAssetManifest ?? null,
|
|
439
570
|
};
|
|
440
571
|
routerMountsById.set(mount.id, mount);
|
|
441
572
|
routerMountIdsByKey.set(key, mount.id);
|
|
442
573
|
sendJson(res, 200, {
|
|
443
574
|
mountId: mount.id,
|
|
444
|
-
appUrl: buildLocalDevAppUrl({ creatorUsername, appType, appName, port }),
|
|
575
|
+
appUrl: buildLocalDevAppUrl({ creatorUsername, appType, appName, port: activePort }),
|
|
445
576
|
creatorUsername,
|
|
446
577
|
appType,
|
|
447
578
|
appName,
|
|
@@ -494,6 +625,40 @@ async function runDevRouterServer(port = Number(process.env.PLAYDROP_DEV_ROUTER_
|
|
|
494
625
|
respondWithText(res, method, 404, 'Not found');
|
|
495
626
|
return;
|
|
496
627
|
}
|
|
628
|
+
if (parsedMountPath.assetPath === '_playdrop/runtime-assets') {
|
|
629
|
+
if (!mount.runtimeAssetManifest) {
|
|
630
|
+
sendJson(res, 500, { error: 'runtime_asset_manifest_not_registered' });
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
sendJson(res, 200, mount.runtimeAssetManifest.response);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
if (parsedMountPath.assetPath.startsWith('_playdrop/runtime-assets/files/')) {
|
|
637
|
+
const ownedAssetName = parsedMountPath.assetPath.slice('_playdrop/runtime-assets/files/'.length).trim();
|
|
638
|
+
const role = (searchParams.get('role') || 'primary').trim().toLowerCase();
|
|
639
|
+
if (!ownedAssetName || !role) {
|
|
640
|
+
respondWithText(res, method, 400, 'Invalid runtime asset file request');
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
const ownedFile = getOwnedRuntimeAssetFile(mount, ownedAssetName, role);
|
|
644
|
+
if (!ownedFile) {
|
|
645
|
+
respondWithText(res, method, 404, 'Not found');
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
try {
|
|
649
|
+
const stats = (0, node_fs_1.statSync)(ownedFile.absolutePath);
|
|
650
|
+
if (!stats.isFile()) {
|
|
651
|
+
respondWithText(res, method, 404, 'Not found');
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
const content = (0, node_fs_1.readFileSync)(ownedFile.absolutePath);
|
|
655
|
+
respondWithBuffer(res, method, 200, ownedFile.contentType || resolveContentType(ownedFile.absolutePath), content);
|
|
656
|
+
}
|
|
657
|
+
catch {
|
|
658
|
+
respondWithText(res, method, 404, 'Not found');
|
|
659
|
+
}
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
497
662
|
if (parsedMountPath.assetPath === 'index.html') {
|
|
498
663
|
try {
|
|
499
664
|
const freshHtml = (0, node_fs_1.readFileSync)(mount.htmlPath);
|
|
@@ -525,6 +690,15 @@ async function runDevRouterServer(port = Number(process.env.PLAYDROP_DEV_ROUTER_
|
|
|
525
690
|
const server = node_http_1.default.createServer((req, res) => {
|
|
526
691
|
void handleRequest(req, res);
|
|
527
692
|
});
|
|
693
|
+
server.on('listening', () => {
|
|
694
|
+
const address = server.address();
|
|
695
|
+
if (address && typeof address === 'object' && typeof address.port === 'number') {
|
|
696
|
+
activePort = address.port;
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
return server;
|
|
700
|
+
}
|
|
701
|
+
async function listenDevRouterServer(server, port = exports.DEV_ROUTER_PORT) {
|
|
528
702
|
await new Promise((resolveListen, rejectListen) => {
|
|
529
703
|
const onError = (error) => {
|
|
530
704
|
server.off('listening', onListening);
|
|
@@ -538,6 +712,15 @@ async function runDevRouterServer(port = Number(process.env.PLAYDROP_DEV_ROUTER_
|
|
|
538
712
|
server.once('listening', onListening);
|
|
539
713
|
server.listen(port, DEV_ROUTER_HOST);
|
|
540
714
|
});
|
|
715
|
+
const address = server.address();
|
|
716
|
+
if (!address || typeof address !== 'object' || typeof address.port !== 'number') {
|
|
717
|
+
throw new Error('dev_router_port_unavailable');
|
|
718
|
+
}
|
|
719
|
+
return address.port;
|
|
720
|
+
}
|
|
721
|
+
async function runDevRouterServer(port = Number(process.env.PLAYDROP_DEV_ROUTER_PORT) || exports.DEV_ROUTER_PORT) {
|
|
722
|
+
const server = createDevRouterServer(port);
|
|
723
|
+
await listenDevRouterServer(server, port);
|
|
541
724
|
await new Promise(() => {
|
|
542
725
|
// Keep the router alive until the process exits.
|
|
543
726
|
});
|
package/dist/commands/init.js
CHANGED
|
@@ -10,10 +10,9 @@ const promises_1 = __importDefault(require("node:readline/promises"));
|
|
|
10
10
|
const node_process_1 = require("node:process");
|
|
11
11
|
const node_child_process_1 = require("node:child_process");
|
|
12
12
|
const types_1 = require("@playdrop/types");
|
|
13
|
-
const config_1 = require("../config");
|
|
14
13
|
const apiClient_1 = require("../apiClient");
|
|
14
|
+
const commandContext_1 = require("../commandContext");
|
|
15
15
|
const http_1 = require("../http");
|
|
16
|
-
const environment_1 = require("../environment");
|
|
17
16
|
const messages_1 = require("../messages");
|
|
18
17
|
const clientInfo_1 = require("../clientInfo");
|
|
19
18
|
const DEFAULT_CATALOGUE = '{}\n';
|
|
@@ -197,31 +196,14 @@ async function init(targetPath, options = {}) {
|
|
|
197
196
|
(0, node_fs_1.mkdirSync)(resolved, { recursive: true });
|
|
198
197
|
const cataloguePath = (0, node_path_1.join)(resolved, 'catalogue.json');
|
|
199
198
|
const catalogueExisted = (0, node_fs_1.existsSync)(cataloguePath);
|
|
200
|
-
const
|
|
201
|
-
if (!
|
|
202
|
-
(0, messages_1.printLoginRequired)('Bootstrapping a Playdrop project', 'project init');
|
|
203
|
-
process.exitCode = 1;
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
if (!cfg.env) {
|
|
207
|
-
(0, messages_1.printConfigEnvironmentMissing)('project init');
|
|
208
|
-
process.exitCode = 1;
|
|
199
|
+
const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project init', 'Bootstrapping a Playdrop project', { workspacePath: resolved });
|
|
200
|
+
if (!ctx) {
|
|
209
201
|
return null;
|
|
210
202
|
}
|
|
211
|
-
const
|
|
212
|
-
const envConfig = (0, environment_1.resolveEnvironmentConfig)(preferredEnv);
|
|
213
|
-
if (!envConfig) {
|
|
214
|
-
const choices = (0, environment_1.formatEnvironmentList)();
|
|
215
|
-
(0, messages_1.printUnknownEnvironment)(preferredEnv || '', choices, 'project init');
|
|
216
|
-
process.exitCode = 1;
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
if (envConfig.allowInsecureRequests) {
|
|
220
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
221
|
-
}
|
|
203
|
+
const { envConfig, token } = ctx;
|
|
222
204
|
let bootstrap = null;
|
|
223
205
|
try {
|
|
224
|
-
bootstrap = await fetchBootstrap(envConfig.apiBase,
|
|
206
|
+
bootstrap = await fetchBootstrap(envConfig.apiBase, token);
|
|
225
207
|
}
|
|
226
208
|
catch (error) {
|
|
227
209
|
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
@@ -240,7 +222,7 @@ async function init(targetPath, options = {}) {
|
|
|
240
222
|
const agentsExisted = (0, node_fs_1.existsSync)(agentsPath);
|
|
241
223
|
let username = null;
|
|
242
224
|
try {
|
|
243
|
-
username = await resolveUsername(envConfig.apiBase,
|
|
225
|
+
username = await resolveUsername(envConfig.apiBase, token);
|
|
244
226
|
}
|
|
245
227
|
catch (error) {
|
|
246
228
|
if (error instanceof http_1.CLIUnsupportedClientError) {
|
package/dist/commands/search.js
CHANGED
|
@@ -7,6 +7,7 @@ const errors_1 = require("../errors");
|
|
|
7
7
|
const messages_1 = require("../messages");
|
|
8
8
|
const output_1 = require("../output");
|
|
9
9
|
const refs_1 = require("../refs");
|
|
10
|
+
const CREATOR_USERNAME_FILTER_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
10
11
|
function parseSearchKind(raw) {
|
|
11
12
|
if (!raw || raw.trim().length === 0) {
|
|
12
13
|
return 'all';
|
|
@@ -123,12 +124,6 @@ function buildCountsSuffix(item) {
|
|
|
123
124
|
if (typeof item.item.viewCount === 'number') {
|
|
124
125
|
parts.push(`view ${formatCountValue(item.item.viewCount)}`);
|
|
125
126
|
}
|
|
126
|
-
if (typeof item.item.downloadCount === 'number') {
|
|
127
|
-
parts.push(`download ${formatCountValue(item.item.downloadCount)}`);
|
|
128
|
-
}
|
|
129
|
-
if (typeof item.item.playCount === 'number') {
|
|
130
|
-
parts.push(`play ${formatCountValue(item.item.playCount)}`);
|
|
131
|
-
}
|
|
132
127
|
}
|
|
133
128
|
if (item.kind === 'asset-pack' && typeof item.item.downloadCount === 'number') {
|
|
134
129
|
parts.push(`download ${formatCountValue(item.item.downloadCount)}`);
|
|
@@ -206,7 +201,11 @@ function buildSearchRequest(input) {
|
|
|
206
201
|
page: 1,
|
|
207
202
|
pageSize: input.limit + input.offset,
|
|
208
203
|
...(input.sort ? { sort: input.sort } : {}),
|
|
204
|
+
...(input.creatorUsername ? { creatorUsername: input.creatorUsername } : {}),
|
|
209
205
|
appType: input.appType,
|
|
206
|
+
auth: input.auth,
|
|
207
|
+
controller: input.controller,
|
|
208
|
+
surface: input.surface,
|
|
210
209
|
assetCategory: input.assetCategory,
|
|
211
210
|
assetSubcategory: input.assetSubcategory,
|
|
212
211
|
assetSpec: input.assetSpec,
|
|
@@ -219,8 +218,27 @@ function buildSearchRequest(input) {
|
|
|
219
218
|
}
|
|
220
219
|
async function search(query, options = {}) {
|
|
221
220
|
const trimmedQuery = typeof query === 'string' ? query.trim() : '';
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
const creatorUsername = normalizeOptionalString(options.creator)?.toLowerCase();
|
|
222
|
+
if (creatorUsername && !CREATOR_USERNAME_FILTER_REGEX.test(creatorUsername)) {
|
|
223
|
+
(0, messages_1.printErrorWithHelp)('The --creator value is invalid.', ['Use a creator username like playdrop or olivier-dev.'], { command: 'search' });
|
|
224
|
+
process.exitCode = 1;
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const hasScopeFilters = Boolean(creatorUsername)
|
|
228
|
+
|| Boolean(options.appType)
|
|
229
|
+
|| Boolean(options.auth)
|
|
230
|
+
|| Boolean(options.controller)
|
|
231
|
+
|| Boolean(options.surface)
|
|
232
|
+
|| Boolean(options.assetCategory)
|
|
233
|
+
|| Boolean(options.assetSubcategory)
|
|
234
|
+
|| Boolean(options.assetSpec)
|
|
235
|
+
|| Boolean(options.assetSpecOwner)
|
|
236
|
+
|| Boolean(options.assetSpecName)
|
|
237
|
+
|| Boolean(options.packContainsCategory)
|
|
238
|
+
|| Boolean(options.packContainsSubcategory)
|
|
239
|
+
|| Array.isArray(options.tag) && options.tag.length > 0;
|
|
240
|
+
if (!trimmedQuery && !hasScopeFilters) {
|
|
241
|
+
(0, messages_1.printErrorWithHelp)('A search query or filter is required.', ['Example: playdrop search "city builder"', 'Example: playdrop search --creator playdrop --kind app'], { command: 'search' });
|
|
224
242
|
process.exitCode = 1;
|
|
225
243
|
return;
|
|
226
244
|
}
|
|
@@ -244,7 +262,7 @@ async function search(query, options = {}) {
|
|
|
244
262
|
}
|
|
245
263
|
const sort = parseSearchSort(options.sort);
|
|
246
264
|
if (sort === null) {
|
|
247
|
-
(0, messages_1.printErrorWithHelp)(`Sort "${options.sort ?? ''}" is not supported.`, ['Use one of: relevance,
|
|
265
|
+
(0, messages_1.printErrorWithHelp)(`Sort "${options.sort ?? ''}" is not supported.`, ['Use one of: relevance, views, likes, used, newest, updated, downloads, remixes, comments, assets, apps, alpha.'], { command: 'search' });
|
|
248
266
|
process.exitCode = 1;
|
|
249
267
|
return;
|
|
250
268
|
}
|
|
@@ -254,6 +272,30 @@ async function search(query, options = {}) {
|
|
|
254
272
|
process.exitCode = 1;
|
|
255
273
|
return;
|
|
256
274
|
}
|
|
275
|
+
const auth = options.auth === undefined ? undefined : (0, types_1.parseAppAuthFilter)(options.auth);
|
|
276
|
+
if (options.auth !== undefined && !auth) {
|
|
277
|
+
(0, messages_1.printErrorWithHelp)(`Auth filter "${options.auth}" is not supported.`, ['Use one of: required, optional, none.'], { command: 'search' });
|
|
278
|
+
process.exitCode = 1;
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const controller = options.controller === undefined ? undefined : (0, types_1.parseAppControllerFilter)(options.controller);
|
|
282
|
+
if (options.controller !== undefined && !controller) {
|
|
283
|
+
(0, messages_1.printErrorWithHelp)(`Controller filter "${options.controller}" is not supported.`, ['Use one of: required, optional, none.'], { command: 'search' });
|
|
284
|
+
process.exitCode = 1;
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const surface = options.surface === undefined ? undefined : (0, types_1.parseAppSurfaceFilter)(options.surface);
|
|
288
|
+
if (options.surface !== undefined && !surface) {
|
|
289
|
+
(0, messages_1.printErrorWithHelp)(`Surface filter "${options.surface}" is not supported.`, ['Use one of: desktop, mobile-portrait, mobile-landscape.'], { command: 'search' });
|
|
290
|
+
process.exitCode = 1;
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const hasCapabilityFilters = Boolean(auth || controller || surface);
|
|
294
|
+
if (hasCapabilityFilters && kind !== 'all' && kind !== 'app') {
|
|
295
|
+
(0, messages_1.printErrorWithHelp)('Capability filters only support app search.', ['Use --kind app, or omit --kind to search across app results only.'], { command: 'search' });
|
|
296
|
+
process.exitCode = 1;
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
257
299
|
const assetCategory = parseAssetCategory(options.assetCategory);
|
|
258
300
|
if (assetCategory === null) {
|
|
259
301
|
(0, messages_1.printErrorWithHelp)(`Asset category "${options.assetCategory}" is not supported.`, ['Use a canonical asset category like IMAGE, VIDEO, AUDIO, SPRITESHEET, or MODEL_3D.'], { command: 'search' });
|
|
@@ -278,6 +320,17 @@ async function search(query, options = {}) {
|
|
|
278
320
|
process.exitCode = 1;
|
|
279
321
|
return;
|
|
280
322
|
}
|
|
323
|
+
if (hasCapabilityFilters && (assetCategory
|
|
324
|
+
|| assetSubcategory
|
|
325
|
+
|| normalizeOptionalString(options.assetSpec)
|
|
326
|
+
|| normalizeOptionalString(options.assetSpecOwner)
|
|
327
|
+
|| normalizeOptionalString(options.assetSpecName)
|
|
328
|
+
|| packContainsCategory
|
|
329
|
+
|| packContainsSubcategory)) {
|
|
330
|
+
(0, messages_1.printErrorWithHelp)('Capability filters cannot be combined with asset or pack search filters.', ['Use app search filters only when passing --auth, --surface, or --controller.'], { command: 'search' });
|
|
331
|
+
process.exitCode = 1;
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
281
334
|
const tags = normalizeTagFilters(options.tag);
|
|
282
335
|
if (tags === null) {
|
|
283
336
|
(0, messages_1.printErrorWithHelp)('The --tag value is invalid.', ['Use canonical refs like theme/pirate or visual-style/pixel-art.', 'Pass at most 5 unique tag refs.'], { command: 'search' });
|
|
@@ -289,10 +342,14 @@ async function search(query, options = {}) {
|
|
|
289
342
|
const response = await client.search(buildSearchRequest({
|
|
290
343
|
trimmedQuery,
|
|
291
344
|
kind,
|
|
345
|
+
...(creatorUsername ? { creatorUsername } : {}),
|
|
292
346
|
limit,
|
|
293
347
|
offset,
|
|
294
348
|
sort: sort ?? undefined,
|
|
295
349
|
appType: appType,
|
|
350
|
+
auth: auth ?? undefined,
|
|
351
|
+
controller: controller ?? undefined,
|
|
352
|
+
surface: surface ?? undefined,
|
|
296
353
|
assetCategory: assetCategory ?? undefined,
|
|
297
354
|
assetSubcategory: assetSubcategory ?? undefined,
|
|
298
355
|
assetSpec: normalizeOptionalString(options.assetSpec),
|
|
@@ -317,11 +374,11 @@ async function search(query, options = {}) {
|
|
|
317
374
|
return;
|
|
318
375
|
}
|
|
319
376
|
if (items.length === 0) {
|
|
320
|
-
console.log(`No results found for "${trimmedQuery}".`);
|
|
377
|
+
console.log(trimmedQuery ? `No results found for "${trimmedQuery}".` : 'No results found.');
|
|
321
378
|
console.log('Next: adjust your filters or run "playdrop browse" to explore available content.');
|
|
322
379
|
return;
|
|
323
380
|
}
|
|
324
|
-
console.log(`Search results for "${trimmedQuery}":\n`);
|
|
381
|
+
console.log(trimmedQuery ? `Search results for "${trimmedQuery}":\n` : 'Search results:\n');
|
|
325
382
|
for (const [index, item] of items.entries()) {
|
|
326
383
|
const displayName = item.item.displayName || item.item.name;
|
|
327
384
|
console.log(`${index + 1}. [${item.kind}] ${item.ref} | ${displayName} | @${item.item.creatorUsername} | ${itemSummary(item)}${buildCountsSuffix(item)}${buildTagSuffix(item.item.tags)}`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ApiClient } from '@playdrop/api-client';
|
|
2
2
|
import { type AssetPackUploadedLocalAsset } from '@playdrop/types';
|
|
3
|
-
import type { AssetPackTask, AssetSpecTask, AssetTask,
|
|
3
|
+
import type { AssetPackTask, AssetSpecTask, AssetTask, OwnedAssetTask, PackOwnedAssetTask } from '../catalogue';
|
|
4
4
|
import type { CliTask } from '../taskUtils';
|
|
5
5
|
export type CurrentUserRole = 'USER' | 'CREATOR' | 'ADMIN' | null;
|
|
6
6
|
export type UploadedAssetInfo = {
|
|
@@ -70,11 +70,11 @@ export declare function parseUnversionedAssetTaskRef(rawRef: string, fallbackCre
|
|
|
70
70
|
name: string;
|
|
71
71
|
} | null;
|
|
72
72
|
export declare function buildAssetPackUploadPlans(tasks: CliTask[], defaultCreator: string, currentUserRole: CurrentUserRole): PackUploadPlanningResult;
|
|
73
|
-
export declare function uploadAssetTask(client: ApiClient, task: AssetTask |
|
|
73
|
+
export declare function uploadAssetTask(client: ApiClient, task: AssetTask | OwnedAssetTask, sourceAppVersionId?: number, creatorUsername?: string, options?: {
|
|
74
74
|
clearTags?: boolean;
|
|
75
75
|
}): Promise<UploadedAssetInfo>;
|
|
76
76
|
export declare function uploadAssetSpecTask(client: ApiClient, task: AssetSpecTask, creatorUsername?: string): Promise<UploadedAssetSpecInfo>;
|
|
77
|
-
export declare function uploadAssetPackTask(client: ApiClient, task: AssetPackTask, creatorUsername: string, uploadedAssets: Map<string, UploadedAssetInfo>, localAssetTasks:
|
|
77
|
+
export declare function uploadAssetPackTask(client: ApiClient, task: AssetPackTask, creatorUsername: string, uploadedAssets: Map<string, UploadedAssetInfo>, localAssetTasks: PackOwnedAssetTask[], uploadKeyByAssetKey: Map<string, string>, targetCreatorUsername?: string, options?: {
|
|
78
78
|
clearTags?: boolean;
|
|
79
79
|
}): Promise<UploadedPackInfo>;
|
|
80
80
|
export {};
|