@playdrop/playdrop-cli 0.3.4-build.1 → 0.3.5-build.1

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.
Files changed (141) hide show
  1. package/README.md +60 -23
  2. package/config/client-meta.json +5 -5
  3. package/dist/apps/upload.js +5 -3
  4. package/dist/assets/model-artifacts.js +1 -1
  5. package/dist/catalogue.d.ts +6 -0
  6. package/dist/catalogue.js +38 -1
  7. package/dist/commandContext.d.ts +1 -0
  8. package/dist/commandContext.js +45 -15
  9. package/dist/commands/browse.d.ts +16 -0
  10. package/dist/commands/browse.js +370 -0
  11. package/dist/commands/build.js +4 -9
  12. package/dist/commands/capture.js +24 -24
  13. package/dist/commands/captureRemote.d.ts +11 -0
  14. package/dist/commands/captureRemote.js +90 -0
  15. package/dist/commands/comments.d.ts +14 -0
  16. package/dist/commands/comments.js +189 -0
  17. package/dist/commands/create.js +112 -72
  18. package/dist/commands/creations.d.ts +49 -0
  19. package/dist/commands/creations.js +657 -0
  20. package/dist/commands/credits.d.ts +10 -0
  21. package/dist/commands/credits.js +91 -0
  22. package/dist/commands/detail.d.ts +2 -2
  23. package/dist/commands/detail.js +148 -290
  24. package/dist/commands/dev.js +24 -24
  25. package/dist/commands/devShared.js +2 -2
  26. package/dist/commands/documentation.d.ts +4 -1
  27. package/dist/commands/documentation.js +79 -104
  28. package/dist/commands/feedback.d.ts +12 -9
  29. package/dist/commands/feedback.js +125 -257
  30. package/dist/commands/format.js +6 -13
  31. package/dist/commands/generation.d.ts +11 -0
  32. package/dist/commands/generation.js +204 -42
  33. package/dist/commands/gettingStarted.d.ts +1 -0
  34. package/dist/commands/gettingStarted.js +26 -0
  35. package/dist/commands/init.js +26 -24
  36. package/dist/commands/login.js +9 -8
  37. package/dist/commands/logout.js +2 -1
  38. package/dist/commands/notifications.d.ts +14 -0
  39. package/dist/commands/notifications.js +179 -0
  40. package/dist/commands/search.d.ts +13 -0
  41. package/dist/commands/search.js +198 -0
  42. package/dist/commands/upload.js +20 -17
  43. package/dist/commands/validate.js +15 -1
  44. package/dist/commands/versionsBrowse.d.ts +7 -0
  45. package/dist/commands/versionsBrowse.js +209 -0
  46. package/dist/commands/whoami.js +9 -8
  47. package/dist/errors.d.ts +9 -0
  48. package/dist/errors.js +52 -0
  49. package/dist/externalAssetPackValidation.d.ts +2 -0
  50. package/dist/externalAssetPackValidation.js +115 -0
  51. package/dist/http.js +1 -1
  52. package/dist/index.js +570 -630
  53. package/dist/messages.js +11 -11
  54. package/dist/output.d.ts +5 -0
  55. package/dist/output.js +45 -0
  56. package/dist/playwright.js +1 -1
  57. package/dist/refs.d.ts +18 -0
  58. package/dist/refs.js +105 -0
  59. package/node_modules/@playdrop/ai-client/dist/index.d.ts +42 -15
  60. package/node_modules/@playdrop/ai-client/dist/index.d.ts.map +1 -1
  61. package/node_modules/@playdrop/ai-client/package.json +1 -0
  62. package/node_modules/@playdrop/api-client/dist/client.d.ts +39 -27
  63. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  64. package/node_modules/@playdrop/api-client/dist/client.js +280 -1669
  65. package/node_modules/@playdrop/api-client/dist/core/errors.d.ts +9 -0
  66. package/node_modules/@playdrop/api-client/dist/core/errors.d.ts.map +1 -0
  67. package/node_modules/@playdrop/api-client/dist/core/errors.js +46 -0
  68. package/node_modules/@playdrop/api-client/dist/core/request.d.ts +27 -0
  69. package/node_modules/@playdrop/api-client/dist/core/request.d.ts.map +1 -0
  70. package/node_modules/@playdrop/api-client/dist/core/request.js +122 -0
  71. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +75 -0
  72. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -0
  73. package/node_modules/@playdrop/api-client/dist/domains/admin.js +282 -0
  74. package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts +22 -0
  75. package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts.map +1 -0
  76. package/node_modules/@playdrop/api-client/dist/domains/ai.js +15 -0
  77. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +60 -0
  78. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -0
  79. package/node_modules/@playdrop/api-client/dist/domains/apps.js +301 -0
  80. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts +59 -0
  81. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts.map +1 -0
  82. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.js +297 -0
  83. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +62 -0
  84. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -0
  85. package/node_modules/@playdrop/api-client/dist/domains/assets.js +297 -0
  86. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +28 -0
  87. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -0
  88. package/node_modules/@playdrop/api-client/dist/domains/auth.js +78 -0
  89. package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts +29 -0
  90. package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts.map +1 -0
  91. package/node_modules/@playdrop/api-client/dist/domains/comments.js +65 -0
  92. package/node_modules/@playdrop/api-client/dist/domains/me.d.ts +24 -0
  93. package/node_modules/@playdrop/api-client/dist/domains/me.d.ts.map +1 -0
  94. package/node_modules/@playdrop/api-client/dist/domains/me.js +35 -0
  95. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +37 -0
  96. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -0
  97. package/node_modules/@playdrop/api-client/dist/domains/payments.js +148 -0
  98. package/node_modules/@playdrop/api-client/dist/domains/search.d.ts +27 -0
  99. package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -0
  100. package/node_modules/@playdrop/api-client/dist/domains/search.js +65 -0
  101. package/node_modules/@playdrop/api-client/dist/index.d.ts +33 -56
  102. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  103. package/node_modules/@playdrop/api-client/dist/index.js +103 -44
  104. package/node_modules/@playdrop/api-client/package.json +3 -2
  105. package/node_modules/@playdrop/boxel-core/package.json +1 -1
  106. package/node_modules/@playdrop/boxel-three/dist/test/glb-skinned.test.js +1 -1
  107. package/node_modules/@playdrop/boxel-three/dist/test/instantiate.test.js +1 -1
  108. package/node_modules/@playdrop/boxel-three/dist/test/skinned-mesh.test.js +1 -1
  109. package/node_modules/@playdrop/boxel-three/package.json +2 -1
  110. package/node_modules/@playdrop/config/client-meta.json +5 -5
  111. package/node_modules/@playdrop/config/dist/src/constants.d.ts +5 -0
  112. package/node_modules/@playdrop/config/dist/src/constants.d.ts.map +1 -1
  113. package/node_modules/@playdrop/config/dist/src/constants.js +5 -1
  114. package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
  115. package/node_modules/@playdrop/config/package.json +1 -1
  116. package/node_modules/@playdrop/types/dist/api.d.ts +178 -17
  117. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  118. package/node_modules/@playdrop/types/dist/api.js +30 -1
  119. package/node_modules/@playdrop/types/dist/app.d.ts +0 -14
  120. package/node_modules/@playdrop/types/dist/app.d.ts.map +1 -1
  121. package/node_modules/@playdrop/types/dist/app.js +0 -10
  122. package/node_modules/@playdrop/types/dist/asset-pack.d.ts +11 -1
  123. package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
  124. package/node_modules/@playdrop/types/dist/asset.d.ts +65 -0
  125. package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
  126. package/node_modules/@playdrop/types/dist/realtime.d.ts +26 -26
  127. package/node_modules/@playdrop/types/dist/realtime.d.ts.map +1 -1
  128. package/node_modules/@playdrop/types/dist/version.d.ts +5 -0
  129. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  130. package/package.json +2 -3
  131. package/bin/playdrop-cli +0 -2
  132. package/dist/commands/asset-packs.d.ts +0 -27
  133. package/dist/commands/asset-packs.js +0 -508
  134. package/dist/commands/assets.d.ts +0 -35
  135. package/dist/commands/assets.js +0 -668
  136. package/dist/commands/list.d.ts +0 -7
  137. package/dist/commands/list.js +0 -347
  138. package/dist/commands/migrateCatalogueV2.d.ts +0 -1
  139. package/dist/commands/migrateCatalogueV2.js +0 -142
  140. package/dist/commands/versions.d.ts +0 -17
  141. package/dist/commands/versions.js +0 -384
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.showCreditBalance = showCreditBalance;
4
+ exports.browseCreditTransactions = browseCreditTransactions;
5
+ const commandContext_1 = require("../commandContext");
6
+ const errors_1 = require("../errors");
7
+ const messages_1 = require("../messages");
8
+ const output_1 = require("../output");
9
+ function parsePositiveInteger(raw, label, fallback) {
10
+ if (raw === undefined) {
11
+ return fallback;
12
+ }
13
+ const parsed = typeof raw === 'number' ? raw : Number.parseInt(String(raw), 10);
14
+ if (!Number.isInteger(parsed) || parsed < 0) {
15
+ return null;
16
+ }
17
+ if (label === 'limit' && parsed <= 0) {
18
+ return null;
19
+ }
20
+ return parsed;
21
+ }
22
+ async function showCreditBalance(options = {}) {
23
+ await (0, commandContext_1.withEnvironment)('credits balance', 'Checking your credit balance', async ({ client }) => {
24
+ try {
25
+ const response = await client.me();
26
+ const balance = response.user?.creditBalance ?? 0;
27
+ if (options.json) {
28
+ (0, output_1.printJson)({ balance });
29
+ return;
30
+ }
31
+ (0, output_1.printSuccess)(`Credit balance: ${balance}`, ['Next: run "playdrop credits transactions" to review recent credit activity.']);
32
+ }
33
+ catch (error) {
34
+ const handled = (0, errors_1.handleCommandFailure)(error, 'credits balance', 'Credit balance lookup', {
35
+ apiMessage: (apiError) => ({
36
+ problem: `Credit balance lookup failed with status ${apiError.status}.`,
37
+ suggestions: ['Run "playdrop auth login" and retry.'],
38
+ }),
39
+ });
40
+ if (!handled) {
41
+ throw error;
42
+ }
43
+ }
44
+ });
45
+ }
46
+ async function browseCreditTransactions(options = {}) {
47
+ const limit = parsePositiveInteger(options.limit, 'limit', 20);
48
+ if (limit === null) {
49
+ (0, messages_1.printErrorWithHelp)('The --limit value must be a positive integer.', ['Example: --limit 20'], { command: 'credits transactions' });
50
+ process.exitCode = 1;
51
+ return;
52
+ }
53
+ const offset = parsePositiveInteger(options.offset, 'offset', 0);
54
+ if (offset === null) {
55
+ (0, messages_1.printErrorWithHelp)('The --offset value must be zero or a positive integer.', ['Example: --offset 20'], { command: 'credits transactions' });
56
+ process.exitCode = 1;
57
+ return;
58
+ }
59
+ await (0, commandContext_1.withEnvironment)('credits transactions', 'Checking your credit history', async ({ client }) => {
60
+ try {
61
+ const response = await client.fetchCreditTransactions({ limit, offset });
62
+ const transactions = response.transactions ?? [];
63
+ if (options.json) {
64
+ (0, output_1.printJson)({ transactions });
65
+ return;
66
+ }
67
+ if (transactions.length === 0) {
68
+ console.log('No credit transactions found.');
69
+ console.log('Next: run "playdrop credits balance" to confirm your current balance.');
70
+ return;
71
+ }
72
+ console.log('Credit transactions:\n');
73
+ for (const transaction of transactions) {
74
+ console.log(`#${transaction.id} | ${transaction.kind.toLowerCase()} | ${transaction.amount} | ${transaction.status.toLowerCase()} | ${(0, output_1.formatTimestamp)(transaction.createdAt)}`);
75
+ console.log(` ${transaction.displayName}`);
76
+ }
77
+ console.log('\nNext: run "playdrop credits balance" to confirm your latest balance.');
78
+ }
79
+ catch (error) {
80
+ const handled = (0, errors_1.handleCommandFailure)(error, 'credits transactions', 'Credit transaction lookup', {
81
+ apiMessage: (apiError) => ({
82
+ problem: `Credit transaction lookup failed with status ${apiError.status}.`,
83
+ suggestions: ['Run "playdrop auth login" and retry.'],
84
+ }),
85
+ });
86
+ if (!handled) {
87
+ throw error;
88
+ }
89
+ }
90
+ });
91
+ }
@@ -1,5 +1,5 @@
1
1
  type DetailOptions = {
2
- format?: string;
2
+ json?: boolean;
3
3
  };
4
- export declare function detail(key: string | undefined, options?: DetailOptions): Promise<void>;
4
+ export declare function detail(rawRef: string | undefined, options?: DetailOptions): Promise<void>;
5
5
  export {};
@@ -1,320 +1,178 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.detail = detail;
4
- const types_1 = require("@playdrop/types");
5
- const config_1 = require("../config");
6
- const apiClient_1 = require("../apiClient");
7
- const http_1 = require("../http");
8
- const environment_1 = require("../environment");
4
+ const commandContext_1 = require("../commandContext");
5
+ const errors_1 = require("../errors");
9
6
  const messages_1 = require("../messages");
10
- const APP_TYPE_SLUGS = {
11
- game: 'GAME',
12
- template: 'TEMPLATE',
13
- tool: 'TOOL',
14
- demo: 'DEMO',
15
- };
16
- function parseFormat(raw) {
17
- if (!raw || raw.trim().length === 0) {
18
- return 'text';
19
- }
20
- const normalized = raw.trim().toLowerCase();
21
- if (normalized === 'text' || normalized === 'json') {
22
- return normalized;
23
- }
24
- return null;
7
+ const output_1 = require("../output");
8
+ const refs_1 = require("../refs");
9
+ function printRefValidationError(raw) {
10
+ (0, messages_1.printErrorWithHelp)('A canonical ref is required.', [
11
+ 'Use the format <creator>/<kind>/<name>.',
12
+ 'Kinds must be exactly app, asset, or asset-pack.',
13
+ `Example: playdrop detail playdrop/app/hangingout`,
14
+ `Example: playdrop detail playdrop/asset/astro`,
15
+ ], { command: 'detail' });
16
+ process.exitCode = 1;
25
17
  }
26
- function parseKey(key) {
27
- const trimmed = key.trim();
28
- if (!trimmed) {
29
- return null;
30
- }
31
- const segments = trimmed.split('/').map((part) => part.trim()).filter(Boolean);
32
- if (segments.length !== 3) {
33
- return null;
34
- }
35
- const [creator, typeSlug, name] = segments;
36
- if (!creator || !typeSlug || !name) {
37
- return null;
38
- }
18
+ function buildAssetSourceUrl(apiBase, ref, revision) {
19
+ const encodedCreator = encodeURIComponent(ref.creator);
20
+ const encodedName = encodeURIComponent(ref.name);
21
+ return `${apiBase}/assets/${encodedCreator}/${encodedName}/versions/${revision}/source`;
22
+ }
23
+ function buildAssetPackSourceUrl(apiBase, ref, version) {
24
+ const encodedCreator = encodeURIComponent(ref.creator);
25
+ const encodedName = encodeURIComponent(ref.name);
26
+ const encodedVersion = encodeURIComponent(version);
27
+ return `${apiBase}/asset-packs/${encodedCreator}/${encodedName}/versions/${encodedVersion}/source`;
28
+ }
29
+ function extractAssetFileUrls(asset) {
30
+ const files = asset.currentVersion?.fileManifest?.files;
31
+ if (!Array.isArray(files)) {
32
+ return [];
33
+ }
34
+ return files
35
+ .filter((entry) => typeof entry?.role === 'string' && typeof entry?.url === 'string' && entry.url.trim().length > 0)
36
+ .map((entry) => ({ role: entry.role, url: entry.url.trim() }));
37
+ }
38
+ function decorateApp(app) {
39
39
  return {
40
- creator,
41
- typeSlug: typeSlug.toLowerCase(),
42
- name,
40
+ ...app,
41
+ urls: {
42
+ play: typeof app.assetUrl === 'string' && app.assetUrl.trim().length > 0 ? app.assetUrl : null,
43
+ source: typeof app.sourceUrl === 'string' && app.sourceUrl.trim().length > 0 ? app.sourceUrl : null,
44
+ },
43
45
  };
44
46
  }
45
- function resolveKey(parsed) {
46
- const appType = APP_TYPE_SLUGS[parsed.typeSlug];
47
- if (appType) {
48
- return { kind: 'app', creator: parsed.creator, appType, name: parsed.name };
49
- }
50
- if (parsed.typeSlug === 'asset' || parsed.typeSlug === 'assets') {
51
- return { kind: 'asset', creator: parsed.creator, name: parsed.name };
52
- }
53
- if (parsed.typeSlug === 'pack' || parsed.typeSlug === 'packs' || parsed.typeSlug === 'asset-pack' || parsed.typeSlug === 'asset-packs') {
54
- return { kind: 'pack', creator: parsed.creator, name: parsed.name };
55
- }
56
- return null;
47
+ function decorateAsset(apiBase, ref, asset) {
48
+ return {
49
+ ...asset,
50
+ urls: {
51
+ source: asset.currentVersion ? buildAssetSourceUrl(apiBase, ref, asset.currentVersion.revision) : null,
52
+ files: extractAssetFileUrls(asset),
53
+ },
54
+ };
57
55
  }
58
- function normalizeCreatorUsername(username) {
59
- if (!username) {
60
- return null;
61
- }
62
- const trimmed = username.trim();
63
- return trimmed.length > 0 ? trimmed : null;
56
+ function decorateAssetPack(apiBase, ref, pack) {
57
+ return {
58
+ ...pack,
59
+ urls: {
60
+ download: typeof pack.currentVersion?.downloadUrl === 'string' && pack.currentVersion.downloadUrl.trim().length > 0
61
+ ? pack.currentVersion.downloadUrl
62
+ : null,
63
+ source: pack.currentVersion?.version ? buildAssetPackSourceUrl(apiBase, ref, pack.currentVersion.version) : null,
64
+ },
65
+ };
64
66
  }
65
- function isNetworkError(error) {
66
- if (!error || typeof error !== 'object') {
67
- return false;
68
- }
69
- const candidate = error;
70
- if (candidate instanceof TypeError) {
71
- return true;
72
- }
73
- const code = typeof candidate.code === 'string' ? candidate.code : undefined;
74
- if (code === 'ECONNREFUSED' || code === 'ENOTFOUND') {
75
- return true;
76
- }
77
- const cause = candidate.cause;
78
- if (cause && typeof cause === 'object' && typeof cause.code === 'string') {
79
- const nestedCode = cause.code;
80
- return nestedCode === 'ECONNREFUSED' || nestedCode === 'ENOTFOUND';
81
- }
82
- return false;
67
+ function printAppText(ref, app) {
68
+ console.log(`[app] ${ref}`);
69
+ console.log(`Display name: ${(0, output_1.formatOptionalValue)(app.displayName)}`);
70
+ console.log(`Creator: @${app.creatorUsername}`);
71
+ console.log(`Type: ${app.type.toLowerCase()}`);
72
+ console.log(`Description: ${(0, output_1.formatOptionalValue)(app.description)}`);
73
+ console.log(`Created: ${(0, output_1.formatTimestamp)(app.createdAt)}`);
74
+ console.log(`Updated: ${(0, output_1.formatTimestamp)(app.updatedAt)}`);
75
+ console.log(`Current version: ${(0, output_1.formatOptionalValue)(app.currentVersion)}`);
76
+ console.log(`Play URL: ${(0, output_1.formatOptionalValue)(app.urls.play)}`);
77
+ console.log(`Source URL: ${(0, output_1.formatOptionalValue)(app.urls.source)}`);
78
+ console.log('\nNext: run "playdrop versions browse ' + ref + '" to inspect version history.');
83
79
  }
84
- async function resolveCurrentUsername(client) {
85
- try {
86
- const response = await client.me();
87
- const username = response?.user?.username;
88
- if (typeof username === 'string' && username.trim().length > 0) {
89
- return username.trim();
80
+ function printAssetText(ref, asset) {
81
+ console.log(`[asset] ${ref}`);
82
+ console.log(`Display name: ${(0, output_1.formatOptionalValue)(asset.displayName)}`);
83
+ console.log(`Creator: @${asset.creatorUsername}`);
84
+ console.log(`Category: ${asset.category.toLowerCase()}`);
85
+ console.log(`Description: ${(0, output_1.formatOptionalValue)(asset.description)}`);
86
+ console.log(`Created: ${(0, output_1.formatTimestamp)(asset.createdAt)}`);
87
+ console.log(`Updated: ${(0, output_1.formatTimestamp)(asset.updatedAt)}`);
88
+ console.log(`Current revision: ${asset.currentVersion ? asset.currentVersion.revisionLabel : 'none'}`);
89
+ console.log(`Current subcategory: ${(0, output_1.formatOptionalValue)(asset.currentVersion?.subcategory)}`);
90
+ console.log(`Source URL: ${(0, output_1.formatOptionalValue)(asset.urls.source)}`);
91
+ if (asset.urls.files.length > 0) {
92
+ for (const file of asset.urls.files) {
93
+ console.log(`File URL (${file.role}): ${file.url}`);
90
94
  }
91
- return null;
92
95
  }
93
- catch (error) {
94
- if (error instanceof types_1.UnsupportedClientError) {
95
- (0, http_1.handleUnsupportedError)(error, 'Detail');
96
- }
97
- if (error instanceof http_1.CLIUnsupportedClientError) {
98
- throw error;
99
- }
100
- if (error instanceof types_1.ApiError) {
101
- throw error;
102
- }
103
- throw error;
96
+ else {
97
+ console.log('File URLs: none');
104
98
  }
99
+ console.log('\nNext: run "playdrop versions browse ' + ref + '" to inspect other revisions.');
105
100
  }
106
- function formatList(items) {
107
- if (!items || items.length === 0) {
108
- return '';
109
- }
110
- return items.join(', ');
101
+ function printAssetPackText(ref, pack) {
102
+ console.log(`[asset-pack] ${ref}`);
103
+ console.log(`Display name: ${(0, output_1.formatOptionalValue)(pack.displayName)}`);
104
+ console.log(`Creator: @${pack.creatorUsername}`);
105
+ console.log(`Description: ${(0, output_1.formatOptionalValue)(pack.description)}`);
106
+ console.log(`Created: ${(0, output_1.formatTimestamp)(pack.createdAt)}`);
107
+ console.log(`Updated: ${(0, output_1.formatTimestamp)(pack.updatedAt)}`);
108
+ console.log(`Current version: ${(0, output_1.formatOptionalValue)(pack.currentVersion?.version)}`);
109
+ console.log(`Download URL: ${(0, output_1.formatOptionalValue)(pack.urls.download)}`);
110
+ console.log(`Source URL: ${(0, output_1.formatOptionalValue)(pack.urls.source)}`);
111
+ console.log('\nNext: run "playdrop versions browse ' + ref + '" to inspect other versions.');
111
112
  }
112
- function printAppText(key, app) {
113
- console.log(`Key: ${key}`);
114
- console.log(`Type: ${app.type}`);
115
- console.log(`Creator: ${app.creatorUsername}`);
116
- console.log(`Name: ${app.name}`);
117
- console.log(`Display Name: ${app.displayName}`);
118
- console.log(`Description: ${typeof app.description === 'string' ? app.description : ''}`);
119
- console.log(`Created At: ${app.createdAt}`);
120
- console.log(`Updated At: ${app.updatedAt}`);
121
- if (Array.isArray(app.surfaceTargets) && app.surfaceTargets.length > 0) {
122
- console.log(`Surface Targets: ${formatList(app.surfaceTargets)}`);
123
- }
124
- if (typeof app.emoji === 'string' && app.emoji.trim()) {
125
- console.log(`Emoji: ${app.emoji}`);
126
- }
127
- if (typeof app.color === 'string' && app.color.trim()) {
128
- console.log(`Color: ${app.color}`);
129
- }
130
- if (typeof app.entryPoint === 'string' && app.entryPoint.trim()) {
131
- console.log(`Entry Point: ${app.entryPoint}`);
132
- }
133
- if (typeof app.assetUrl === 'string' && app.assetUrl.trim()) {
134
- console.log(`HTML URL: ${app.assetUrl}`);
135
- }
136
- if (typeof app.sourceUrl === 'string' && app.sourceUrl.trim()) {
137
- console.log(`Source URL: ${app.sourceUrl}`);
138
- }
139
- }
140
- function printAssetText(key, asset) {
141
- console.log(`Key: ${key}`);
142
- console.log('Type: ASSET');
143
- console.log(`Creator: ${asset.creatorUsername}`);
144
- console.log(`Name: ${asset.name}`);
145
- console.log(`Display Name: ${asset.displayName}`);
146
- console.log(`Description: ${typeof asset.description === 'string' ? asset.description : ''}`);
147
- console.log(`Category: ${asset.category}`);
148
- console.log(`Created At: ${asset.createdAt}`);
149
- console.log(`Updated At: ${asset.updatedAt}`);
150
- if (asset.currentVersion) {
151
- console.log(`Current Revision: r${asset.currentVersion.revision}`);
152
- console.log(`Current Subcategory: ${asset.currentVersion.subcategory}`);
153
- console.log(`Current Format: ${asset.currentVersion.format}`);
154
- console.log(`Current Visibility: ${asset.currentVersion.visibility}`);
155
- }
156
- }
157
- function printPackText(key, pack) {
158
- console.log(`Key: ${key}`);
159
- console.log('Type: PACK');
160
- console.log(`Creator: ${pack.creatorUsername}`);
161
- console.log(`Name: ${pack.name}`);
162
- console.log(`Display Name: ${pack.displayName}`);
163
- console.log(`Description: ${typeof pack.description === 'string' ? pack.description : ''}`);
164
- console.log(`Created At: ${pack.createdAt}`);
165
- console.log(`Updated At: ${pack.updatedAt}`);
166
- if (pack.currentVersion) {
167
- console.log(`Current Version: ${pack.currentVersion.version}`);
168
- console.log(`Current Visibility: ${pack.currentVersion.visibility}`);
169
- }
170
- }
171
- async function detail(key, options = {}) {
172
- if (!key || key.trim().length === 0) {
173
- (0, messages_1.printErrorWithHelp)('A key in the format creator/type/name is required.', [
174
- 'Example: playdrop/game/jumper',
175
- 'Use "playdrop-cli list" to find keys before running detail.',
176
- ], { command: 'detail' });
177
- process.exitCode = 1;
178
- return;
179
- }
180
- const cfg = (0, config_1.loadConfig)();
181
- if (!cfg.env) {
182
- (0, messages_1.printConfigEnvironmentMissing)('detail');
183
- process.exitCode = 1;
184
- return;
185
- }
186
- if (!cfg.token) {
187
- (0, messages_1.printLoginRequired)('Showing item details', 'detail');
188
- process.exitCode = 1;
189
- return;
190
- }
191
- const envConfig = (0, environment_1.resolveEnvironmentConfig)(cfg.env);
192
- if (!envConfig) {
193
- const choices = (0, environment_1.formatEnvironmentList)();
194
- (0, messages_1.printUnknownEnvironment)(cfg.env, choices, 'detail');
195
- process.exitCode = 1;
196
- return;
197
- }
198
- if (envConfig.allowInsecureRequests) {
199
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
200
- }
201
- const format = parseFormat(options.format);
202
- if (!format) {
203
- (0, messages_1.printErrorWithHelp)(`Format "${options.format ?? ''}" is not supported.`, ['Use "--format text" or "--format json".'], { command: 'detail' });
204
- process.exitCode = 1;
205
- return;
206
- }
207
- const parsedKey = parseKey(key);
208
- if (!parsedKey) {
209
- (0, messages_1.printErrorWithHelp)(`Key "${key}" is invalid.`, ['Provide keys as creator/type/name (example: playdrop/game/jumper).'], { command: 'detail' });
210
- process.exitCode = 1;
211
- return;
113
+ async function detail(rawRef, options = {}) {
114
+ let ref;
115
+ try {
116
+ ref = (0, refs_1.parseContentRef)(rawRef ?? '');
212
117
  }
213
- const resolvedKey = resolveKey(parsedKey);
214
- if (!resolvedKey) {
215
- (0, messages_1.printErrorWithHelp)(`Type "${parsedKey.typeSlug}" in the key is not supported.`, ['Supported types: game, demo, tool, template, asset, pack'], { command: 'detail' });
216
- process.exitCode = 1;
118
+ catch {
119
+ printRefValidationError(rawRef);
217
120
  return;
218
121
  }
219
- const client = (0, apiClient_1.createCliApiClient)({ baseUrl: envConfig.apiBase, token: cfg.token });
220
- let creatorUsername = resolvedKey.creator;
221
- if (creatorUsername.toLowerCase() === 'me') {
122
+ await (0, commandContext_1.withPublicEnvironment)('detail', async ({ client, envConfig }) => {
222
123
  try {
223
- const resolved = await resolveCurrentUsername(client);
224
- if (!resolved) {
225
- (0, messages_1.printErrorWithHelp)('Could not resolve your creator username from the API.', ['Retry "playdrop-cli detail" in a moment.'], { command: 'detail' });
226
- process.exitCode = 1;
227
- return;
228
- }
229
- creatorUsername = resolved;
230
- }
231
- catch (error) {
232
- if (error instanceof http_1.CLIUnsupportedClientError) {
124
+ if (ref.kind === 'app') {
125
+ const response = await client.fetchAppBySlug(ref.creator, ref.name);
126
+ const item = decorateApp(response.app);
127
+ if (options.json) {
128
+ (0, output_1.printJson)({ kind: ref.kind, ref: ref.ref, item });
129
+ return;
130
+ }
131
+ printAppText(ref.ref, item);
233
132
  return;
234
133
  }
235
- if (error instanceof types_1.ApiError) {
236
- (0, messages_1.printErrorWithHelp)(`Failed to resolve your account (status ${error.status}).`, ['Run "playdrop-cli login" to refresh your session, then try again.'], { command: 'detail' });
237
- process.exitCode = 1;
134
+ if (ref.kind === 'asset') {
135
+ const response = await client.fetchAssetBySlug(ref.creator, ref.name);
136
+ const item = decorateAsset(envConfig.apiBase, ref, response.asset);
137
+ if (options.json) {
138
+ (0, output_1.printJson)({ kind: ref.kind, ref: ref.ref, item });
139
+ return;
140
+ }
141
+ printAssetText(ref.ref, item);
238
142
  return;
239
143
  }
240
- if (isNetworkError(error)) {
241
- (0, messages_1.printNetworkIssue)('Could not reach the API to resolve your account.', 'detail');
242
- process.exitCode = 1;
144
+ const response = await client.fetchAssetPackBySlug(ref.creator, ref.name);
145
+ const item = decorateAssetPack(envConfig.apiBase, ref, response.pack);
146
+ if (options.json) {
147
+ (0, output_1.printJson)({ kind: ref.kind, ref: ref.ref, item });
243
148
  return;
244
149
  }
245
- throw error;
150
+ printAssetPackText(ref.ref, item);
246
151
  }
247
- }
248
- creatorUsername = normalizeCreatorUsername(creatorUsername) ?? '';
249
- if (!creatorUsername) {
250
- (0, messages_1.printErrorWithHelp)('Creator username cannot be empty.', ['Provide a key with a creator segment, like playdrop/game/jumper.'], { command: 'detail' });
251
- process.exitCode = 1;
252
- return;
253
- }
254
- const fullKey = `${creatorUsername}/${parsedKey.typeSlug}/${resolvedKey.name}`;
255
- try {
256
- if (resolvedKey.kind === 'app') {
257
- const response = await client.fetchAppBySlug(creatorUsername, resolvedKey.name);
258
- const app = response?.app;
259
- if (!app) {
260
- throw new Error('missing_app_payload');
261
- }
262
- if (format === 'json') {
263
- console.log(JSON.stringify({ kind: 'app', data: app }, null, 2));
264
- }
265
- else {
266
- printAppText(fullKey, app);
267
- }
268
- return;
269
- }
270
- if (resolvedKey.kind === 'asset') {
271
- const response = await client.fetchAssetBySlug(creatorUsername, resolvedKey.name);
272
- const asset = response?.asset;
273
- if (!asset) {
274
- throw new Error('missing_asset_payload');
275
- }
276
- if (format === 'json') {
277
- console.log(JSON.stringify({ kind: 'asset', data: asset }, null, 2));
278
- }
279
- else {
280
- printAssetText(fullKey, asset);
281
- }
282
- return;
283
- }
284
- const response = await client.fetchAssetPackBySlug(creatorUsername, resolvedKey.name);
285
- const pack = response?.pack;
286
- if (!pack) {
287
- throw new Error('missing_pack_payload');
288
- }
289
- if (format === 'json') {
290
- console.log(JSON.stringify({ kind: 'pack', data: pack }, null, 2));
291
- }
292
- else {
293
- printPackText(fullKey, pack);
294
- }
295
- }
296
- catch (error) {
297
- if (error instanceof http_1.CLIUnsupportedClientError) {
298
- return;
299
- }
300
- if (error instanceof types_1.UnsupportedClientError) {
301
- (0, http_1.handleUnsupportedError)(error, 'Detail');
302
- }
303
- if (error instanceof types_1.ApiError) {
304
- if (error.status === 404) {
305
- (0, messages_1.printErrorWithHelp)(`Item ${fullKey} was not found.`, ['Use "playdrop-cli list" to confirm the key.'], { command: 'detail' });
306
- }
307
- else {
308
- (0, messages_1.printErrorWithHelp)(`Request failed (status ${error.status}).`, ['Retry in a moment.'], { command: 'detail' });
152
+ catch (error) {
153
+ const handled = (0, errors_1.handleCommandFailure)(error, 'detail', 'Detail lookup', {
154
+ apiMessage: (apiError) => {
155
+ if (apiError.status === 404) {
156
+ return {
157
+ problem: `No item was found for "${ref.ref}".`,
158
+ suggestions: ['Check the ref and retry.', 'Run "playdrop browse" to explore available content.'],
159
+ };
160
+ }
161
+ if (apiError.status === 401 || apiError.status === 403) {
162
+ return {
163
+ problem: `You do not have access to "${ref.ref}".`,
164
+ suggestions: ['Run "playdrop auth login" if this is private content you own.', 'Use "playdrop help detail" for the ref format.'],
165
+ };
166
+ }
167
+ return {
168
+ problem: `Detail lookup failed with status ${apiError.status}.`,
169
+ suggestions: ['Retry in a moment.', 'Use "playdrop help detail" for usage details.'],
170
+ };
171
+ },
172
+ });
173
+ if (!handled) {
174
+ throw error;
309
175
  }
310
- process.exitCode = 1;
311
- return;
312
176
  }
313
- if (isNetworkError(error)) {
314
- (0, messages_1.printNetworkIssue)('Could not reach the API to fetch details.', 'detail');
315
- process.exitCode = 1;
316
- return;
317
- }
318
- throw error;
319
- }
177
+ });
320
178
  }