@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,179 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.browseNotifications = browseNotifications;
4
+ exports.readNotification = readNotification;
5
+ exports.readAllNotifications = readAllNotifications;
6
+ const commandContext_1 = require("../commandContext");
7
+ const errors_1 = require("../errors");
8
+ const messages_1 = require("../messages");
9
+ const output_1 = require("../output");
10
+ function parseStatus(raw) {
11
+ if (!raw || raw.trim().length === 0) {
12
+ return 'unread';
13
+ }
14
+ const normalized = raw.trim().toLowerCase();
15
+ if (normalized === 'all' || normalized === 'read' || normalized === 'unread') {
16
+ return normalized;
17
+ }
18
+ return null;
19
+ }
20
+ function parsePositiveInteger(raw, label, fallback) {
21
+ if (raw === undefined) {
22
+ return fallback;
23
+ }
24
+ const parsed = typeof raw === 'number' ? raw : Number.parseInt(String(raw), 10);
25
+ if (!Number.isInteger(parsed) || parsed < 0) {
26
+ return null;
27
+ }
28
+ if (label === 'limit' && parsed <= 0) {
29
+ return null;
30
+ }
31
+ return parsed;
32
+ }
33
+ async function fetchNotificationsByStatus(fetchPage, status, limit, offset) {
34
+ if (status === 'all' || status === 'unread') {
35
+ return fetchPage(status, limit, offset);
36
+ }
37
+ const pageSize = Math.max(limit, 50);
38
+ const collected = [];
39
+ let unreadCount = 0;
40
+ let cursor = 0;
41
+ let hasMore = true;
42
+ while (hasMore && collected.length < offset + limit) {
43
+ const response = await fetchPage('all', pageSize, cursor);
44
+ unreadCount = response.unreadCount;
45
+ const matching = response.notifications.filter((notification) => notification.readAt !== null);
46
+ collected.push(...matching);
47
+ if (!response.pagination.hasMore || response.notifications.length === 0) {
48
+ hasMore = false;
49
+ break;
50
+ }
51
+ cursor += response.notifications.length;
52
+ }
53
+ const notifications = collected.slice(offset, offset + limit);
54
+ return {
55
+ notifications,
56
+ unreadCount,
57
+ pagination: {
58
+ limit,
59
+ offset,
60
+ count: notifications.length,
61
+ hasMore: collected.length > offset + limit || hasMore,
62
+ },
63
+ };
64
+ }
65
+ async function browseNotifications(options = {}) {
66
+ const status = parseStatus(options.status);
67
+ if (!status) {
68
+ (0, messages_1.printErrorWithHelp)(`Status "${options.status ?? ''}" is not supported.`, ['Use one of: unread, read, all.'], { command: 'notifications browse' });
69
+ process.exitCode = 1;
70
+ return;
71
+ }
72
+ const limit = parsePositiveInteger(options.limit, 'limit', 20);
73
+ if (limit === null) {
74
+ (0, messages_1.printErrorWithHelp)('The --limit value must be a positive integer.', ['Example: --limit 20'], { command: 'notifications browse' });
75
+ process.exitCode = 1;
76
+ return;
77
+ }
78
+ const offset = parsePositiveInteger(options.offset, 'offset', 0);
79
+ if (offset === null) {
80
+ (0, messages_1.printErrorWithHelp)('The --offset value must be zero or a positive integer.', ['Example: --offset 20'], { command: 'notifications browse' });
81
+ process.exitCode = 1;
82
+ return;
83
+ }
84
+ await (0, commandContext_1.withEnvironment)('notifications browse', 'Checking your notifications', async ({ client }) => {
85
+ try {
86
+ const response = await fetchNotificationsByStatus(async (requestStatus, requestLimit, requestOffset) => client.fetchNotifications({
87
+ status: requestStatus,
88
+ limit: requestLimit,
89
+ offset: requestOffset,
90
+ }), status, limit, offset);
91
+ if (options.json) {
92
+ (0, output_1.printJson)({
93
+ notifications: response.notifications,
94
+ unreadCount: response.unreadCount,
95
+ pagination: response.pagination,
96
+ });
97
+ return;
98
+ }
99
+ if (response.notifications.length === 0) {
100
+ console.log(`No ${status} notifications found.`);
101
+ console.log('Next: run "playdrop browse" or "playdrop creations browse" to check recent activity.');
102
+ return;
103
+ }
104
+ console.log(`Notifications (${status}):\n`);
105
+ for (const notification of response.notifications) {
106
+ const state = notification.readAt ? 'read' : 'unread';
107
+ console.log(`#${notification.id} | ${state} | ${(0, output_1.formatTimestamp)(notification.createdAt)}`);
108
+ console.log(` ${notification.title}`);
109
+ if (notification.body) {
110
+ console.log(` ${notification.body}`);
111
+ }
112
+ }
113
+ console.log('\nNext: run "playdrop notifications read <id>" or "playdrop notifications read-all".');
114
+ }
115
+ catch (error) {
116
+ const handled = (0, errors_1.handleCommandFailure)(error, 'notifications browse', 'Notification lookup', {
117
+ apiMessage: (apiError) => ({
118
+ problem: `Notification lookup failed with status ${apiError.status}.`,
119
+ suggestions: ['Run "playdrop auth login" and retry.'],
120
+ }),
121
+ });
122
+ if (!handled) {
123
+ throw error;
124
+ }
125
+ }
126
+ });
127
+ }
128
+ async function readNotification(rawId, options = {}) {
129
+ const id = parsePositiveInteger(rawId, 'Notification id', 0);
130
+ if (id === null || id === 0) {
131
+ (0, messages_1.printErrorWithHelp)('Notification id must be a positive integer.', ['Example: playdrop notifications read 42'], { command: 'notifications read' });
132
+ process.exitCode = 1;
133
+ return;
134
+ }
135
+ await (0, commandContext_1.withEnvironment)('notifications read', 'Marking a notification as read', async ({ client }) => {
136
+ try {
137
+ const response = await client.markNotificationRead(id);
138
+ if (options.json) {
139
+ (0, output_1.printJson)({ success: Boolean(response.success), unreadCount: response.unreadCount });
140
+ return;
141
+ }
142
+ (0, output_1.printSuccess)(`Notification #${id} marked as read.`, ['Next: run "playdrop notifications browse --status unread" to review what remains.']);
143
+ }
144
+ catch (error) {
145
+ const handled = (0, errors_1.handleCommandFailure)(error, 'notifications read', 'Notification update', {
146
+ apiMessage: (apiError) => ({
147
+ problem: `Notification update failed with status ${apiError.status}.`,
148
+ suggestions: ['Run "playdrop auth login" and retry.'],
149
+ }),
150
+ });
151
+ if (!handled) {
152
+ throw error;
153
+ }
154
+ }
155
+ });
156
+ }
157
+ async function readAllNotifications(options = {}) {
158
+ await (0, commandContext_1.withEnvironment)('notifications read-all', 'Marking all notifications as read', async ({ client }) => {
159
+ try {
160
+ const response = await client.markAllNotificationsRead();
161
+ if (options.json) {
162
+ (0, output_1.printJson)({ success: Boolean(response.success), unreadCount: response.unreadCount });
163
+ return;
164
+ }
165
+ (0, output_1.printSuccess)('All notifications marked as read.', ['Next: run "playdrop notifications browse --status unread" to confirm your inbox is clear.']);
166
+ }
167
+ catch (error) {
168
+ const handled = (0, errors_1.handleCommandFailure)(error, 'notifications read-all', 'Notification update', {
169
+ apiMessage: (apiError) => ({
170
+ problem: `Notification update failed with status ${apiError.status}.`,
171
+ suggestions: ['Run "playdrop auth login" and retry.'],
172
+ }),
173
+ });
174
+ if (!handled) {
175
+ throw error;
176
+ }
177
+ }
178
+ });
179
+ }
@@ -0,0 +1,13 @@
1
+ type SearchOptions = {
2
+ kind?: string;
3
+ appType?: string;
4
+ assetCategory?: string;
5
+ assetSubcategory?: string;
6
+ packContainsCategory?: string;
7
+ packContainsSubcategory?: string;
8
+ limit?: string | number;
9
+ offset?: string | number;
10
+ json?: boolean;
11
+ };
12
+ export declare function search(query: string | undefined, options?: SearchOptions): Promise<void>;
13
+ export {};
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.search = search;
4
+ const types_1 = require("@playdrop/types");
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
+ const refs_1 = require("../refs");
10
+ function parseSearchKind(raw) {
11
+ if (!raw || raw.trim().length === 0) {
12
+ return 'all';
13
+ }
14
+ const normalized = raw.trim().toLowerCase();
15
+ if (normalized === 'app' || normalized === 'asset' || normalized === 'asset-pack' || normalized === 'all') {
16
+ return normalized;
17
+ }
18
+ return null;
19
+ }
20
+ function parsePositiveInteger(raw, label, fallback) {
21
+ if (raw === undefined) {
22
+ return fallback;
23
+ }
24
+ const parsed = typeof raw === 'number' ? raw : Number.parseInt(String(raw), 10);
25
+ if (!Number.isInteger(parsed) || parsed < 0) {
26
+ return null;
27
+ }
28
+ if (label === 'limit' && parsed <= 0) {
29
+ return null;
30
+ }
31
+ return parsed;
32
+ }
33
+ function parseAssetCategory(raw) {
34
+ if (!raw || raw.trim().length === 0) {
35
+ return undefined;
36
+ }
37
+ const normalized = raw.trim().toUpperCase().replace(/[-\s]/g, '_');
38
+ if (normalized === '3D' || normalized === 'MODEL3D') {
39
+ return 'MODEL_3D';
40
+ }
41
+ if (types_1.ASSET_CATEGORY_VALUES.includes(normalized)) {
42
+ return normalized;
43
+ }
44
+ return null;
45
+ }
46
+ function parseAssetSubcategory(raw) {
47
+ if (!raw || raw.trim().length === 0) {
48
+ return undefined;
49
+ }
50
+ const normalized = (0, types_1.normalizeAssetSubcategory)(raw);
51
+ return normalized.length > 0 ? normalized : null;
52
+ }
53
+ function toSearchItem(result) {
54
+ if (result.kind === 'app') {
55
+ return {
56
+ kind: 'app',
57
+ ref: `${result.app.creatorUsername}/app/${result.app.name}`,
58
+ targetPath: result.targetPath,
59
+ item: result.app,
60
+ };
61
+ }
62
+ if (result.kind === 'asset') {
63
+ return {
64
+ kind: 'asset',
65
+ ref: `${result.asset.creatorUsername}/asset/${result.asset.name}`,
66
+ targetPath: result.targetPath,
67
+ item: result.asset,
68
+ };
69
+ }
70
+ if (result.kind === 'pack') {
71
+ return {
72
+ kind: 'asset-pack',
73
+ ref: `${result.pack.creatorUsername}/asset-pack/${result.pack.name}`,
74
+ targetPath: result.targetPath,
75
+ item: result.pack,
76
+ };
77
+ }
78
+ return null;
79
+ }
80
+ function itemSummary(item) {
81
+ if (item.kind === 'app') {
82
+ return item.item.type.toLowerCase();
83
+ }
84
+ if (item.kind === 'asset') {
85
+ const subcategory = item.item.currentVersion?.subcategory;
86
+ return subcategory ? `${item.item.category.toLowerCase()}/${subcategory}` : item.item.category.toLowerCase();
87
+ }
88
+ return 'asset-pack';
89
+ }
90
+ async function search(query, options = {}) {
91
+ const trimmedQuery = typeof query === 'string' ? query.trim() : '';
92
+ if (!trimmedQuery) {
93
+ (0, messages_1.printErrorWithHelp)('A search query is required.', ['Example: playdrop search "city builder"'], { command: 'search' });
94
+ process.exitCode = 1;
95
+ return;
96
+ }
97
+ const kind = parseSearchKind(options.kind);
98
+ if (!kind) {
99
+ (0, messages_1.printErrorWithHelp)(`Kind "${options.kind ?? ''}" is not supported.`, ['Use one of: app, asset, asset-pack, all.'], { command: 'search' });
100
+ process.exitCode = 1;
101
+ return;
102
+ }
103
+ const limit = parsePositiveInteger(options.limit, 'limit', 10);
104
+ if (limit === null) {
105
+ (0, messages_1.printErrorWithHelp)('The --limit value must be a positive integer.', ['Example: --limit 10'], { command: 'search' });
106
+ process.exitCode = 1;
107
+ return;
108
+ }
109
+ const offset = parsePositiveInteger(options.offset, 'offset', 0);
110
+ if (offset === null) {
111
+ (0, messages_1.printErrorWithHelp)('The --offset value must be zero or a positive integer.', ['Example: --offset 20'], { command: 'search' });
112
+ process.exitCode = 1;
113
+ return;
114
+ }
115
+ const appType = options.appType ? (0, types_1.parseAppType)(options.appType.trim().toUpperCase()) : undefined;
116
+ if (options.appType !== undefined && !appType) {
117
+ (0, messages_1.printErrorWithHelp)(`App type "${options.appType}" is not supported.`, ['Use one of: game, demo, tool, template.'], { command: 'search' });
118
+ process.exitCode = 1;
119
+ return;
120
+ }
121
+ const assetCategory = parseAssetCategory(options.assetCategory);
122
+ if (assetCategory === null) {
123
+ (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' });
124
+ process.exitCode = 1;
125
+ return;
126
+ }
127
+ const assetSubcategory = parseAssetSubcategory(options.assetSubcategory);
128
+ if (assetSubcategory === null) {
129
+ (0, messages_1.printErrorWithHelp)(`Asset subcategory "${options.assetSubcategory}" is invalid.`, ['Use lowercase slug values like generic, avatar, music, or sfx.'], { command: 'search' });
130
+ process.exitCode = 1;
131
+ return;
132
+ }
133
+ const packContainsCategory = parseAssetCategory(options.packContainsCategory);
134
+ if (packContainsCategory === null) {
135
+ (0, messages_1.printErrorWithHelp)(`Pack category "${options.packContainsCategory}" is not supported.`, ['Use a canonical asset category like IMAGE, VIDEO, AUDIO, SPRITESHEET, or MODEL_3D.'], { command: 'search' });
136
+ process.exitCode = 1;
137
+ return;
138
+ }
139
+ const packContainsSubcategory = parseAssetSubcategory(options.packContainsSubcategory);
140
+ if (packContainsSubcategory === null) {
141
+ (0, messages_1.printErrorWithHelp)(`Pack subcategory "${options.packContainsSubcategory}" is invalid.`, ['Use lowercase slug values like generic, avatar, music, or sfx.'], { command: 'search' });
142
+ process.exitCode = 1;
143
+ return;
144
+ }
145
+ await (0, commandContext_1.withPublicEnvironment)('search', async ({ client }) => {
146
+ try {
147
+ const requestLimit = limit + offset;
148
+ const response = await client.search({
149
+ q: trimmedQuery,
150
+ mode: 'flat',
151
+ kind: (0, refs_1.contentKindToSearchKind)(kind),
152
+ page: 1,
153
+ pageSize: requestLimit,
154
+ appType: appType,
155
+ assetCategory: assetCategory ?? undefined,
156
+ assetSubcategory: assetSubcategory ?? undefined,
157
+ packContainsCategory: packContainsCategory ?? undefined,
158
+ packContainsSubcategory: packContainsSubcategory ?? undefined,
159
+ });
160
+ const normalizedItems = (response.results ?? [])
161
+ .map((result) => toSearchItem(result))
162
+ .filter((item) => Boolean(item));
163
+ const items = normalizedItems.slice(offset, offset + limit);
164
+ const pagination = {
165
+ limit,
166
+ offset,
167
+ count: items.length,
168
+ hasMore: normalizedItems.length > offset + limit || Boolean(response.pagination?.hasMore),
169
+ };
170
+ if (options.json) {
171
+ (0, output_1.printJson)({ items, pagination });
172
+ return;
173
+ }
174
+ if (items.length === 0) {
175
+ console.log(`No results found for "${trimmedQuery}".`);
176
+ console.log('Next: adjust your filters or run "playdrop browse" to explore available content.');
177
+ return;
178
+ }
179
+ console.log(`Search results for "${trimmedQuery}":\n`);
180
+ for (const [index, item] of items.entries()) {
181
+ const displayName = item.item.displayName || item.item.name;
182
+ console.log(`${index + 1}. [${item.kind}] ${item.ref} | ${displayName} | @${item.item.creatorUsername} | ${itemSummary(item)}`);
183
+ }
184
+ console.log('\nNext: run "playdrop detail <creator>/<kind>/<name>" to inspect one result.');
185
+ }
186
+ catch (error) {
187
+ const handled = (0, errors_1.handleCommandFailure)(error, 'search', 'Search', {
188
+ apiMessage: (apiError) => ({
189
+ problem: `Search failed with status ${apiError.status}.`,
190
+ suggestions: ['Retry in a moment.', 'Use "playdrop help search" for usage details.'],
191
+ }),
192
+ });
193
+ if (!handled) {
194
+ throw error;
195
+ }
196
+ }
197
+ });
198
+ }
@@ -242,7 +242,11 @@ async function uploadAssetTask(client, task, sourceAppVersionId, creatorUsername
242
242
  fieldName: role,
243
243
  filename: (0, node_path_1.basename)(filePath),
244
244
  }));
245
- const response = await client.createAssetVersion(task.name, {
245
+ const targetCreatorUsername = typeof creatorUsername === 'string' ? creatorUsername.trim() : '';
246
+ if (!targetCreatorUsername) {
247
+ throw new Error(`Asset "${task.name}" upload is missing creator username.`);
248
+ }
249
+ const response = await client.createAssetVersion(targetCreatorUsername, task.name, {
246
250
  displayName: task.kind === 'asset' ? task.displayName : undefined,
247
251
  category: task.category,
248
252
  subcategory,
@@ -253,8 +257,6 @@ async function uploadAssetTask(client, task, sourceAppVersionId, creatorUsername
253
257
  shopListed: task.shopListed,
254
258
  shopPriceCredits: task.shopPriceCredits,
255
259
  files,
256
- }, {
257
- creatorUsername,
258
260
  });
259
261
  const uploadedCreatorUsername = response.asset.creatorUsername;
260
262
  const name = response.asset.name;
@@ -329,10 +331,19 @@ async function uploadAssetPackTask(client, task, creatorUsername, uploadedAssets
329
331
  for (const ref of task.assets) {
330
332
  assetRefs.push(await resolveAssetReference(client, ref, creatorUsername, uploadedAssets));
331
333
  }
332
- const response = await client.uploadAssetPackVersion(task.name, {
334
+ const mutationCreatorUsername = typeof targetCreatorUsername === 'string' && targetCreatorUsername.trim().length > 0
335
+ ? targetCreatorUsername.trim()
336
+ : creatorUsername.trim();
337
+ if (!mutationCreatorUsername) {
338
+ throw new Error(`Asset pack "${task.name}@${task.version}" upload is missing creator username.`);
339
+ }
340
+ const response = await client.uploadAssetPackVersion(mutationCreatorUsername, task.name, {
333
341
  request: {
334
342
  version: task.version,
335
343
  visibility: task.visibility,
344
+ hostingMode: task.hostingMode,
345
+ externalUrl: task.externalUrl,
346
+ downloadUrl: task.downloadUrl,
336
347
  releaseNotes: task.releaseNotes,
337
348
  assets: assetRefs.map((assetRef) => ({ assetRef })),
338
349
  },
@@ -343,8 +354,6 @@ async function uploadAssetPackTask(client, task, creatorUsername, uploadedAssets
343
354
  screenshotsLandscape: task.listing?.screenshotLandscapePaths?.map((filePath) => createListingFileFromPath(filePath, 'image/png')),
344
355
  videosPortrait: task.listing?.videoPortraitPaths?.map((filePath) => createListingFileFromPath(filePath, 'video/mp4')),
345
356
  videosLandscape: task.listing?.videoLandscapePaths?.map((filePath) => createListingFileFromPath(filePath, 'video/mp4')),
346
- }, {
347
- creatorUsername: targetCreatorUsername,
348
357
  });
349
358
  const versionNodeId = typeof response.versionNodeId === 'string' ? response.versionNodeId.trim() : '';
350
359
  if (!versionNodeId) {
@@ -497,7 +506,7 @@ async function processUploadTasks(client, tasks, owner, ownerUsername, currentUs
497
506
  if (task.kind === 'app') {
498
507
  const { upload } = await (0, apps_1.runAppPipeline)(client, task, {
499
508
  skipEcs: options?.skipEcs,
500
- creatorUsername: task.username,
509
+ creatorUsername: taskCreator,
501
510
  });
502
511
  if (!upload.versionCreated || !upload.version) {
503
512
  throw new Error(`App "${task.name}" upload did not return a created version.`);
@@ -542,10 +551,7 @@ async function processUploadTasks(client, tasks, owner, ownerUsername, currentUs
542
551
  console.log((0, uploadLog_1.formatTaskLogLine)(entry));
543
552
  }
544
553
  else if (task.kind === 'asset') {
545
- const mutationTargetCreator = typeof task.username === 'string' && task.username.trim().length > 0
546
- ? taskCreator
547
- : undefined;
548
- const uploaded = await uploadAssetTask(client, task, undefined, mutationTargetCreator);
554
+ const uploaded = await uploadAssetTask(client, task, undefined, taskCreator);
549
555
  uploadedAssetsByKey.set(`${uploaded.creatorUsername}/${uploaded.name}`, uploaded);
550
556
  registerCanonicalNode(graphState, uploaded.ref, uploaded.versionNodeId);
551
557
  registerLocalRef(graphState.localAssetNodeByName, graphState.ambiguousAssetNames, task.name, uploaded.versionNodeId);
@@ -569,10 +575,7 @@ async function processUploadTasks(client, tasks, owner, ownerUsername, currentUs
569
575
  if (!sourceApp) {
570
576
  throw new Error(`Embedded asset "${task.name}" references app "${task.appName}" that was not uploaded in this run.`);
571
577
  }
572
- const mutationTargetCreator = sourceApp.creatorUsername !== defaultCreator
573
- ? sourceApp.creatorUsername
574
- : undefined;
575
- const uploaded = await uploadAssetTask(client, task, sourceApp.versionId, mutationTargetCreator);
578
+ const uploaded = await uploadAssetTask(client, task, sourceApp.versionId, sourceApp.creatorUsername);
576
579
  uploadedAssetsByKey.set(`${uploaded.creatorUsername}/${uploaded.name}`, uploaded);
577
580
  registerCanonicalNode(graphState, uploaded.ref, uploaded.versionNodeId);
578
581
  registerLocalRef(graphState.localAssetNodeByName, graphState.ambiguousAssetNames, task.name, uploaded.versionNodeId);
@@ -664,7 +667,7 @@ async function processUploadTasks(client, tasks, owner, ownerUsername, currentUs
664
667
  return results;
665
668
  }
666
669
  async function upload(pathOrName, options) {
667
- await (0, commandContext_1.withEnvironment)('upload', 'Uploading content', async ({ client, env, envConfig }) => {
670
+ await (0, commandContext_1.withEnvironment)('project publish', 'Publishing content', async ({ client, env, envConfig }) => {
668
671
  let userInfo = { username: null, role: null };
669
672
  try {
670
673
  userInfo = await fetchCurrentUserInfo(client, envConfig.apiBase);
@@ -680,7 +683,7 @@ async function upload(pathOrName, options) {
680
683
  : 'unknown';
681
684
  const selection = (0, taskSelection_1.selectTasks)(pathOrName);
682
685
  if (selection.errors.length > 0) {
683
- (0, taskSelection_1.reportTaskErrors)(selection.errors, 'upload');
686
+ (0, taskSelection_1.reportTaskErrors)(selection.errors, 'project publish');
684
687
  process.exitCode = 1;
685
688
  return;
686
689
  }
@@ -5,10 +5,21 @@ const taskSelection_1 = require("../taskSelection");
5
5
  const taskUtils_1 = require("../taskUtils");
6
6
  const uploadLog_1 = require("../uploadLog");
7
7
  const apps_1 = require("../apps");
8
+ const externalAssetPackValidation_1 = require("../externalAssetPackValidation");
8
9
  async function validate(pathOrName) {
9
10
  const selection = (0, taskSelection_1.selectTasks)(pathOrName);
10
11
  if (selection.errors.length > 0) {
11
- (0, taskSelection_1.reportTaskErrors)(selection.errors, 'validate');
12
+ (0, taskSelection_1.reportTaskErrors)(selection.errors, 'project validate');
13
+ process.exitCode = 1;
14
+ return;
15
+ }
16
+ const externalAssetPackErrors = await (0, externalAssetPackValidation_1.validateExternalAssetPackTasks)(selection.tasks);
17
+ if (externalAssetPackErrors.length > 0) {
18
+ (0, taskSelection_1.reportTaskErrors)(externalAssetPackErrors.map((message) => ({
19
+ type: 'invalid-catalogue',
20
+ message,
21
+ help: ['Update the asset pack downloadUrl to a public hostname that resolves successfully, then try again.'],
22
+ })), 'project validate');
12
23
  process.exitCode = 1;
13
24
  return;
14
25
  }
@@ -25,6 +36,9 @@ async function validate(pathOrName) {
25
36
  if (task.kind === 'app') {
26
37
  await (0, apps_1.validateAppTask)(task);
27
38
  }
39
+ else {
40
+ throw new Error(`project validate does not support ${task.kind} "${entityId}".`);
41
+ }
28
42
  const entry = {
29
43
  action: 'validate',
30
44
  status: 'success',
@@ -0,0 +1,7 @@
1
+ type BrowseVersionsOptions = {
2
+ limit?: string | number;
3
+ offset?: string | number;
4
+ json?: boolean;
5
+ };
6
+ export declare function browseVersions(rawRef: string | undefined, options?: BrowseVersionsOptions): Promise<void>;
7
+ export {};