@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,209 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.browseVersions = browseVersions;
4
+ const commandContext_1 = require("../commandContext");
5
+ const errors_1 = require("../errors");
6
+ const messages_1 = require("../messages");
7
+ const output_1 = require("../output");
8
+ const refs_1 = require("../refs");
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
+ function buildAssetSourceUrl(apiBase, ref, revision) {
23
+ return `${apiBase}/assets/${encodeURIComponent(ref.creator)}/${encodeURIComponent(ref.name)}/versions/${revision}/source`;
24
+ }
25
+ function buildAssetPackSourceUrl(apiBase, ref, version) {
26
+ return `${apiBase}/asset-packs/${encodeURIComponent(ref.creator)}/${encodeURIComponent(ref.name)}/versions/${encodeURIComponent(version)}/source`;
27
+ }
28
+ function assetFileUrls(version) {
29
+ const files = version.fileManifest?.files;
30
+ if (!Array.isArray(files)) {
31
+ return [];
32
+ }
33
+ return files
34
+ .filter((entry) => typeof entry?.role === 'string' && typeof entry?.url === 'string' && entry.url.trim().length > 0)
35
+ .map((entry) => ({ role: entry.role, url: entry.url.trim() }));
36
+ }
37
+ function printEntry(entry) {
38
+ if (entry.kind === 'app') {
39
+ const currentLabel = entry.isCurrent ? ' | current' : '';
40
+ console.log(`${entry.version} | ${entry.visibility.toLowerCase()}${currentLabel} | ${(0, output_1.formatTimestamp)(entry.createdAt)}`);
41
+ console.log(` Play URL: ${(0, output_1.formatOptionalValue)(entry.urls.play)}`);
42
+ console.log(` Source URL: ${(0, output_1.formatOptionalValue)(entry.urls.source)}`);
43
+ return;
44
+ }
45
+ if (entry.kind === 'asset') {
46
+ const currentLabel = entry.isCurrent ? ' | current' : '';
47
+ console.log(`${entry.revisionLabel} | ${entry.visibility.toLowerCase()}${currentLabel} | ${entry.format.toLowerCase()} | ${entry.subcategory} | ${(0, output_1.formatTimestamp)(entry.createdAt)}`);
48
+ console.log(` Source URL: ${entry.urls.source}`);
49
+ if (entry.urls.files.length > 0) {
50
+ for (const file of entry.urls.files) {
51
+ console.log(` File URL (${file.role}): ${file.url}`);
52
+ }
53
+ }
54
+ return;
55
+ }
56
+ const currentLabel = entry.isCurrent ? ' | current' : '';
57
+ console.log(`${entry.version} | ${entry.visibility.toLowerCase()}${currentLabel} | ${(0, output_1.formatTimestamp)(entry.createdAt)}`);
58
+ console.log(` Download URL: ${(0, output_1.formatOptionalValue)(entry.urls.download)}`);
59
+ console.log(` Source URL: ${entry.urls.source}`);
60
+ }
61
+ async function browseVersions(rawRef, options = {}) {
62
+ let ref;
63
+ try {
64
+ ref = (0, refs_1.parseContentRef)(rawRef ?? '');
65
+ }
66
+ catch {
67
+ (0, messages_1.printErrorWithHelp)('A canonical ref is required.', ['Use the format <creator>/<kind>/<name>.', 'Example: playdrop versions browse playdrop/app/hangingout'], { command: 'versions browse' });
68
+ process.exitCode = 1;
69
+ return;
70
+ }
71
+ const limit = parsePositiveInteger(options.limit, 'limit', 10);
72
+ if (limit === null) {
73
+ (0, messages_1.printErrorWithHelp)('The --limit value must be a positive integer.', ['Example: --limit 10'], { command: 'versions browse' });
74
+ process.exitCode = 1;
75
+ return;
76
+ }
77
+ const offset = parsePositiveInteger(options.offset, 'offset', 0);
78
+ if (offset === null) {
79
+ (0, messages_1.printErrorWithHelp)('The --offset value must be zero or a positive integer.', ['Example: --offset 20'], { command: 'versions browse' });
80
+ process.exitCode = 1;
81
+ return;
82
+ }
83
+ await (0, commandContext_1.withPublicEnvironment)('versions browse', async ({ client, envConfig }) => {
84
+ try {
85
+ let entries = [];
86
+ let pagination;
87
+ if (ref.kind === 'app') {
88
+ const response = await client.listAppVersions(ref.creator, ref.name);
89
+ const listed = response.versions ?? [];
90
+ const selected = listed.slice(offset, offset + limit);
91
+ const details = await Promise.all(selected.map(async (version) => {
92
+ const detail = await client.getAppVersion(ref.creator, ref.name, version.version);
93
+ return {
94
+ kind: 'app',
95
+ version: version.version,
96
+ visibility: version.visibility,
97
+ createdAt: version.createdAt,
98
+ updatedAt: detail.version.updatedAt,
99
+ releaseNotes: version.releaseNotes ?? null,
100
+ isCurrent: Boolean(version.isCurrent),
101
+ urls: {
102
+ play: typeof detail.version.assetUrl === 'string' ? detail.version.assetUrl : null,
103
+ source: typeof detail.version.sourceUrl === 'string' ? detail.version.sourceUrl : null,
104
+ },
105
+ };
106
+ }));
107
+ entries = details;
108
+ pagination = {
109
+ limit,
110
+ offset,
111
+ count: entries.length,
112
+ hasMore: listed.length > offset + limit,
113
+ };
114
+ }
115
+ else if (ref.kind === 'asset') {
116
+ const [detail, response] = await Promise.all([
117
+ client.fetchAssetBySlug(ref.creator, ref.name),
118
+ client.listAssetVersions(ref.creator, ref.name, { limit, offset }),
119
+ ]);
120
+ const currentRevision = detail.asset.currentVersion?.revision ?? null;
121
+ entries = (response.versions ?? []).map((version) => ({
122
+ kind: 'asset',
123
+ revision: version.revision,
124
+ revisionLabel: version.revisionLabel,
125
+ visibility: version.visibility,
126
+ createdAt: version.createdAt,
127
+ updatedAt: version.updatedAt,
128
+ subcategory: version.subcategory,
129
+ format: version.format,
130
+ isCurrent: currentRevision === version.revision,
131
+ urls: {
132
+ source: buildAssetSourceUrl(envConfig.apiBase, ref, version.revision),
133
+ files: assetFileUrls(version),
134
+ },
135
+ }));
136
+ pagination = response.pagination ?? {
137
+ limit,
138
+ offset,
139
+ count: entries.length,
140
+ hasMore: false,
141
+ };
142
+ }
143
+ else {
144
+ const [detail, response] = await Promise.all([
145
+ client.fetchAssetPackBySlug(ref.creator, ref.name),
146
+ client.listAssetPackVersions(ref.creator, ref.name, { limit, offset }),
147
+ ]);
148
+ const currentVersion = detail.pack.currentVersion?.version ?? null;
149
+ entries = (response.versions ?? []).map((version) => ({
150
+ kind: 'asset-pack',
151
+ version: version.version,
152
+ visibility: version.visibility,
153
+ createdAt: version.createdAt,
154
+ updatedAt: version.updatedAt,
155
+ isCurrent: currentVersion === version.version,
156
+ urls: {
157
+ download: typeof version.downloadUrl === 'string' && version.downloadUrl.trim().length > 0 ? version.downloadUrl : null,
158
+ source: buildAssetPackSourceUrl(envConfig.apiBase, ref, version.version),
159
+ },
160
+ }));
161
+ pagination = response.pagination ?? {
162
+ limit,
163
+ offset,
164
+ count: entries.length,
165
+ hasMore: false,
166
+ };
167
+ }
168
+ if (options.json) {
169
+ (0, output_1.printJson)({ kind: ref.kind, ref: ref.ref, entries, pagination });
170
+ return;
171
+ }
172
+ if (entries.length === 0) {
173
+ console.log(`No versions found for "${ref.ref}".`);
174
+ console.log('Next: run "playdrop detail ' + ref.ref + '" to inspect the current item state.');
175
+ return;
176
+ }
177
+ console.log(`Versions for ${ref.ref}:\n`);
178
+ for (const entry of entries) {
179
+ printEntry(entry);
180
+ }
181
+ console.log('\nNext: use the URLs above directly, or run "playdrop detail ' + ref.ref + '" for the current item summary.');
182
+ }
183
+ catch (error) {
184
+ const handled = (0, errors_1.handleCommandFailure)(error, 'versions browse', 'Version lookup', {
185
+ apiMessage: (apiError) => {
186
+ if (apiError.status === 404) {
187
+ return {
188
+ problem: `No item was found for "${ref.ref}".`,
189
+ suggestions: ['Check the ref and retry.', 'Run "playdrop browse" to explore available content.'],
190
+ };
191
+ }
192
+ if (apiError.status === 401 || apiError.status === 403) {
193
+ return {
194
+ problem: `You do not have access to version history for "${ref.ref}".`,
195
+ suggestions: ['Run "playdrop auth login" if this is private content you own.'],
196
+ };
197
+ }
198
+ return {
199
+ problem: `Version lookup failed with status ${apiError.status}.`,
200
+ suggestions: ['Retry in a moment.', 'Use "playdrop help versions browse" for usage details.'],
201
+ };
202
+ },
203
+ });
204
+ if (!handled) {
205
+ throw error;
206
+ }
207
+ }
208
+ });
209
+ }
@@ -10,14 +10,14 @@ const messages_1 = require("../messages");
10
10
  async function whoami() {
11
11
  const cfg = (0, config_1.loadConfig)();
12
12
  if (!cfg.token || !cfg.env) {
13
- (0, messages_1.printLoginRequired)('Checking your Playdrop account status', 'whoami');
13
+ (0, messages_1.printLoginRequired)('Checking your Playdrop account status', 'auth whoami');
14
14
  process.exitCode = 1;
15
15
  return;
16
16
  }
17
17
  const envConfig = (0, environment_1.resolveEnvironmentConfig)(cfg.env);
18
18
  if (!envConfig) {
19
19
  const choices = (0, environment_1.formatEnvironmentList)();
20
- (0, messages_1.printUnknownEnvironment)(cfg.env, choices, 'whoami');
20
+ (0, messages_1.printUnknownEnvironment)(cfg.env, choices, 'auth whoami');
21
21
  process.exitCode = 1;
22
22
  return;
23
23
  }
@@ -36,9 +36,9 @@ async function whoami() {
36
36
  }
37
37
  if (unknownError instanceof types_1.ApiError) {
38
38
  (0, messages_1.printErrorWithHelp)(`Request failed with status ${unknownError.status}.`, [
39
- 'Run "playdrop-cli login" to refresh your credentials.',
40
- 'Use "playdrop-cli whoami" again after logging in to confirm your status.'
41
- ], { command: 'whoami' });
39
+ 'Run "playdrop auth login" to refresh your credentials.',
40
+ 'Use "playdrop auth whoami" again after logging in to confirm your status.'
41
+ ], { command: 'auth whoami' });
42
42
  process.exitCode = 1;
43
43
  return null;
44
44
  }
@@ -53,7 +53,7 @@ async function whoami() {
53
53
  if (e instanceof http_1.CLIUnsupportedClientError) {
54
54
  return;
55
55
  }
56
- (0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to check your account.', 'whoami');
56
+ (0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to check your account.', 'auth whoami');
57
57
  process.exitCode = 1;
58
58
  return;
59
59
  }
@@ -62,11 +62,12 @@ async function whoami() {
62
62
  const username = data.user?.username;
63
63
  if (!username) {
64
64
  (0, messages_1.printErrorWithHelp)('The Playdrop API returned an unexpected response.', [
65
- 'Retry "playdrop-cli whoami" in a moment.',
65
+ 'Retry "playdrop auth whoami" in a moment.',
66
66
  'If the problem continues, contact the Playdrop team.'
67
- ], { command: 'whoami' });
67
+ ], { command: 'auth whoami' });
68
68
  process.exitCode = 1;
69
69
  return;
70
70
  }
71
71
  console.log(`${username} (${cfg.env})`);
72
+ console.log('Next: run "playdrop getting-started" to see the recommended workflow.');
72
73
  }
@@ -0,0 +1,9 @@
1
+ import { ApiError } from '@playdrop/types';
2
+ export declare function isNetworkError(error: unknown): boolean;
3
+ export declare function handleCommandFailure(error: unknown, command: string, context: string, options?: {
4
+ networkMessage?: string;
5
+ apiMessage?: (error: ApiError) => {
6
+ problem: string;
7
+ suggestions?: string[];
8
+ };
9
+ }): boolean;
package/dist/errors.js ADDED
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isNetworkError = isNetworkError;
4
+ exports.handleCommandFailure = handleCommandFailure;
5
+ const types_1 = require("@playdrop/types");
6
+ const http_1 = require("./http");
7
+ const messages_1 = require("./messages");
8
+ function isNetworkError(error) {
9
+ if (!error || typeof error !== 'object') {
10
+ return false;
11
+ }
12
+ const candidate = error;
13
+ if (candidate instanceof TypeError) {
14
+ return true;
15
+ }
16
+ const code = typeof candidate.code === 'string' ? candidate.code : undefined;
17
+ if (code === 'ECONNREFUSED' || code === 'ENOTFOUND') {
18
+ return true;
19
+ }
20
+ const cause = candidate.cause;
21
+ if (cause && typeof cause === 'object' && typeof cause.code === 'string') {
22
+ const nestedCode = cause.code;
23
+ return nestedCode === 'ECONNREFUSED' || nestedCode === 'ENOTFOUND';
24
+ }
25
+ return false;
26
+ }
27
+ function handleCommandFailure(error, command, context, options = {}) {
28
+ if (error instanceof http_1.CLIUnsupportedClientError) {
29
+ return true;
30
+ }
31
+ if (error instanceof types_1.UnsupportedClientError) {
32
+ (0, http_1.handleUnsupportedError)(error, context);
33
+ return true;
34
+ }
35
+ if (error instanceof types_1.ApiError) {
36
+ const apiMessage = options.apiMessage
37
+ ? options.apiMessage(error)
38
+ : {
39
+ problem: `${context} failed with status ${error.status}.`,
40
+ suggestions: ['Retry in a moment.'],
41
+ };
42
+ (0, messages_1.printErrorWithHelp)(apiMessage.problem, apiMessage.suggestions ?? [], { command });
43
+ process.exitCode = 1;
44
+ return true;
45
+ }
46
+ if (isNetworkError(error)) {
47
+ (0, messages_1.printNetworkIssue)(options.networkMessage || 'Could not reach the Playdrop API.', command);
48
+ process.exitCode = 1;
49
+ return true;
50
+ }
51
+ return false;
52
+ }
@@ -0,0 +1,2 @@
1
+ import type { CliTask } from './taskUtils';
2
+ export declare function validateExternalAssetPackTasks(tasks: CliTask[]): Promise<string[]>;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.validateExternalAssetPackTasks = validateExternalAssetPackTasks;
37
+ const dnsPromises = __importStar(require("node:dns/promises"));
38
+ const node_net_1 = require("node:net");
39
+ function isExternalAssetPackTask(task) {
40
+ return task.kind === 'asset-pack' && task.hostingMode === 'EXTERNAL';
41
+ }
42
+ function isPrivateNetworkIpAddress(address) {
43
+ const normalized = address.trim().toLowerCase();
44
+ if (!normalized) {
45
+ return true;
46
+ }
47
+ if (normalized === '::1') {
48
+ return true;
49
+ }
50
+ if (normalized.startsWith('fe80:') || normalized.startsWith('fc') || normalized.startsWith('fd')) {
51
+ return true;
52
+ }
53
+ if (normalized.startsWith('::ffff:')) {
54
+ return isPrivateNetworkIpAddress(normalized.slice('::ffff:'.length));
55
+ }
56
+ if ((0, node_net_1.isIP)(normalized) !== 4) {
57
+ return false;
58
+ }
59
+ const octets = normalized.split('.').map((part) => Number.parseInt(part, 10));
60
+ if (octets.length !== 4 || octets.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
61
+ return true;
62
+ }
63
+ const [first, second] = octets;
64
+ if (first === 10 || first === 127) {
65
+ return true;
66
+ }
67
+ if (first === 169 && second === 254) {
68
+ return true;
69
+ }
70
+ if (first === 172 && second >= 16 && second <= 31) {
71
+ return true;
72
+ }
73
+ if (first === 192 && second === 168) {
74
+ return true;
75
+ }
76
+ return false;
77
+ }
78
+ async function validateExternalAssetPackTasks(tasks) {
79
+ const errors = [];
80
+ for (const task of tasks) {
81
+ if (!isExternalAssetPackTask(task)) {
82
+ continue;
83
+ }
84
+ const downloadUrl = typeof task.downloadUrl === 'string' ? task.downloadUrl.trim() : '';
85
+ if (!downloadUrl) {
86
+ continue;
87
+ }
88
+ let hostname = '';
89
+ try {
90
+ hostname = new URL(downloadUrl).hostname;
91
+ }
92
+ catch {
93
+ continue;
94
+ }
95
+ let resolvedAddresses;
96
+ try {
97
+ resolvedAddresses = await dnsPromises.lookup(hostname, { all: true, verbatim: true });
98
+ }
99
+ catch {
100
+ errors.push(`[${task.cataloguePath}] Asset pack "${task.name}" downloadUrl hostname could not be resolved.`);
101
+ continue;
102
+ }
103
+ if (!Array.isArray(resolvedAddresses) || resolvedAddresses.length === 0) {
104
+ errors.push(`[${task.cataloguePath}] Asset pack "${task.name}" downloadUrl hostname did not resolve to a public address.`);
105
+ continue;
106
+ }
107
+ if (resolvedAddresses.some((entry) => {
108
+ const address = typeof entry?.address === 'string' ? entry.address.trim() : '';
109
+ return !address || isPrivateNetworkIpAddress(address);
110
+ })) {
111
+ errors.push(`[${task.cataloguePath}] Asset pack "${task.name}" downloadUrl must resolve only to public IP addresses.`);
112
+ }
113
+ }
114
+ return errors;
115
+ }
package/dist/http.js CHANGED
@@ -43,7 +43,7 @@ function handleUnsupportedError(error, context) {
43
43
  if (requiredParts.length > 0) {
44
44
  console.error(`Required: ${requiredParts.join(', ')}`);
45
45
  }
46
- console.error('Run `playdrop-cli upgrade` to update the Playdrop CLI.');
46
+ console.error('Run `playdrop update` to update Playdrop.');
47
47
  process.exitCode = 1;
48
48
  throw new CLIUnsupportedClientError(error.message);
49
49
  }