@playdrop/playdrop-cli 0.3.9 → 0.3.11

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 (47) hide show
  1. package/README.md +43 -1
  2. package/config/client-meta.json +5 -6
  3. package/dist/catalogue-utils.js +9 -2
  4. package/dist/catalogue.d.ts +0 -1
  5. package/dist/catalogue.js +26 -8
  6. package/dist/commands/browse.js +1 -1
  7. package/dist/commands/capture.js +2 -2
  8. package/dist/commands/comments.js +3 -3
  9. package/dist/commands/create.js +108 -12
  10. package/dist/commands/createRemixContent.js +33 -3
  11. package/dist/commands/creations.js +2 -2
  12. package/dist/commands/credits.js +2 -2
  13. package/dist/commands/detail.js +1 -1
  14. package/dist/commands/dev.js +2 -2
  15. package/dist/commands/feedback.js +1 -1
  16. package/dist/commands/generation.js +2 -2
  17. package/dist/commands/gettingStarted.js +1 -1
  18. package/dist/commands/init.js +31 -3
  19. package/dist/commands/login.d.ts +10 -10
  20. package/dist/commands/login.js +202 -79
  21. package/dist/commands/logout.js +1 -1
  22. package/dist/commands/notifications.js +3 -3
  23. package/dist/commands/versionsBrowse.js +1 -1
  24. package/dist/commands/whoami.js +9 -9
  25. package/dist/index.js +41 -25
  26. package/dist/messages.js +5 -5
  27. package/dist/taskSelection.js +2 -2
  28. package/node_modules/@playdrop/api-client/dist/client.d.ts +13 -2
  29. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  30. package/node_modules/@playdrop/api-client/dist/client.js +21 -0
  31. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +10 -1
  32. package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -1
  33. package/node_modules/@playdrop/api-client/dist/domains/auth.js +94 -0
  34. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +2 -2
  35. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
  36. package/node_modules/@playdrop/api-client/dist/domains/payments.js +2 -1
  37. package/node_modules/@playdrop/api-client/dist/index.d.ts +16 -3
  38. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  39. package/node_modules/@playdrop/api-client/dist/index.js +37 -4
  40. package/node_modules/@playdrop/config/client-meta.json +5 -6
  41. package/node_modules/@playdrop/config/dist/test/validateClientEnvironment.test.js +13 -0
  42. package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
  43. package/node_modules/@playdrop/types/dist/api.d.ts +204 -2
  44. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  45. package/node_modules/@playdrop/types/dist/asset.d.ts +18 -50
  46. package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
  47. package/package.json +23 -2
@@ -16,7 +16,9 @@ const http_1 = require("../http");
16
16
  const environment_1 = require("../environment");
17
17
  const messages_1 = require("../messages");
18
18
  const clientInfo_1 = require("../clientInfo");
19
- const DEFAULT_CATALOGUE = `${JSON.stringify({ apps: [] }, null, 2)}\n`;
19
+ const DEFAULT_CATALOGUE = '{}\n';
20
+ const LEGACY_CATALOGUE_VERSION_KEY = ['schema', 'Version'].join('');
21
+ const ALLOWED_CATALOGUE_TOP_LEVEL_KEYS = new Set(['apps', 'assets', 'assetPacks']);
20
22
  function ensureTrailingNewline(content) {
21
23
  return content.endsWith('\n') ? content : `${content}\n`;
22
24
  }
@@ -53,7 +55,7 @@ async function resolveUsername(apiBase, token) {
53
55
  }
54
56
  if (unknownError instanceof types_1.ApiError) {
55
57
  (0, messages_1.printErrorWithHelp)(`Request failed with status ${unknownError.status}.`, [
56
- 'Run "playdrop auth login" to refresh your credentials.',
58
+ 'Run "playdrop login" to refresh your credentials.',
57
59
  'Use "playdrop project init" again after logging in.'
58
60
  ], { command: 'project init' });
59
61
  process.exitCode = 1;
@@ -100,7 +102,33 @@ function normalizeBootstrapPayload(payload) {
100
102
  const catalogueRaw = typeof payload.catalogue === 'string' && payload.catalogue.trim().length > 0
101
103
  ? payload.catalogue
102
104
  : DEFAULT_CATALOGUE;
103
- const catalogue = ensureTrailingNewline(catalogueRaw.trim() ? catalogueRaw : DEFAULT_CATALOGUE);
105
+ const catalogueCandidate = ensureTrailingNewline(catalogueRaw.trim() ? catalogueRaw : DEFAULT_CATALOGUE);
106
+ let parsedCatalogue;
107
+ try {
108
+ parsedCatalogue = JSON.parse(catalogueCandidate);
109
+ }
110
+ catch {
111
+ (0, messages_1.printErrorWithHelp)('Bootstrap payload returned an invalid catalogue.json.', ['Contact the platform team or try again shortly.'], { command: 'project init' });
112
+ process.exitCode = 1;
113
+ return null;
114
+ }
115
+ if (!parsedCatalogue || typeof parsedCatalogue !== 'object' || Array.isArray(parsedCatalogue)) {
116
+ (0, messages_1.printErrorWithHelp)('Bootstrap payload returned an invalid catalogue.json.', ['Contact the platform team or try again shortly.'], { command: 'project init' });
117
+ process.exitCode = 1;
118
+ return null;
119
+ }
120
+ if (Object.prototype.hasOwnProperty.call(parsedCatalogue, LEGACY_CATALOGUE_VERSION_KEY)) {
121
+ (0, messages_1.printErrorWithHelp)('Bootstrap payload returned a legacy catalogue.json with a top-level version field.', ['Contact the platform team or try again shortly.'], { command: 'project init' });
122
+ process.exitCode = 1;
123
+ return null;
124
+ }
125
+ const unsupportedKey = Object.keys(parsedCatalogue).find((key) => !ALLOWED_CATALOGUE_TOP_LEVEL_KEYS.has(key));
126
+ if (unsupportedKey) {
127
+ (0, messages_1.printErrorWithHelp)(`Bootstrap payload returned catalogue.json with unsupported top-level key "${unsupportedKey}".`, ['Contact the platform team or try again shortly.'], { command: 'project init' });
128
+ process.exitCode = 1;
129
+ return null;
130
+ }
131
+ const catalogue = catalogueCandidate;
104
132
  return { readmeUri, agentsUri, catalogue };
105
133
  }
106
134
  async function downloadBootstrapAsset(webBase, uri, label) {
@@ -1,10 +1,10 @@
1
- import { Writable } from 'node:stream';
2
- export declare class MaskedStdout extends Writable {
3
- private readonly target;
4
- muted: boolean;
5
- constructor(target?: NodeJS.WriteStream & {
6
- fd: 1;
7
- });
8
- _write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void;
9
- }
10
- export declare function login(env: string, usernameArg?: string, passwordArg?: string): Promise<void>;
1
+ type LoginOptions = {
2
+ username?: string;
3
+ password?: string;
4
+ key?: string;
5
+ };
6
+ type OpenBrowserFn = (url: string) => Promise<boolean>;
7
+ export declare function setOpenBrowserForTests(fn: OpenBrowserFn): void;
8
+ export declare function resetOpenBrowserForTests(): void;
9
+ export declare function login(env: string, options?: LoginOptions): Promise<void>;
10
+ export {};
@@ -1,118 +1,241 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.MaskedStdout = void 0;
3
+ exports.setOpenBrowserForTests = setOpenBrowserForTests;
4
+ exports.resetOpenBrowserForTests = resetOpenBrowserForTests;
7
5
  exports.login = login;
8
- const promises_1 = __importDefault(require("node:readline/promises"));
9
- const node_stream_1 = require("node:stream");
10
- const node_process_1 = require("node:process");
6
+ const node_child_process_1 = require("node:child_process");
11
7
  const types_1 = require("@playdrop/types");
12
8
  const config_1 = require("../config");
13
9
  const apiClient_1 = require("../apiClient");
14
10
  const http_1 = require("../http");
15
11
  const environment_1 = require("../environment");
16
12
  const messages_1 = require("../messages");
17
- class MaskedStdout extends node_stream_1.Writable {
18
- constructor(target = node_process_1.stdout) {
19
- super();
20
- this.target = target;
21
- this.muted = false;
22
- }
23
- _write(chunk, encoding, callback) {
24
- if (!this.muted) {
25
- this.target.write(chunk, encoding);
26
- }
27
- else {
28
- const str = typeof chunk === 'string' ? chunk : chunk?.toString?.();
29
- if (str && str.includes('\n')) {
30
- this.target.write('\n');
31
- }
32
- }
33
- callback();
13
+ function sleep(ms) {
14
+ return new Promise((resolve) => setTimeout(resolve, ms));
15
+ }
16
+ function getBrowserCommand(url) {
17
+ if (process.platform === 'darwin') {
18
+ return { command: 'open', args: [url] };
19
+ }
20
+ if (process.platform === 'win32') {
21
+ return { command: 'cmd', args: ['/c', 'start', '', url] };
34
22
  }
23
+ if (process.platform === 'linux') {
24
+ return { command: 'xdg-open', args: [url] };
25
+ }
26
+ return null;
35
27
  }
36
- exports.MaskedStdout = MaskedStdout;
37
- async function login(env, usernameArg, passwordArg) {
38
- const envConfig = (0, environment_1.resolveEnvironmentConfig)(env);
39
- if (!envConfig) {
40
- const choices = (0, environment_1.formatEnvironmentList)();
41
- (0, messages_1.printUnknownEnvironment)(env, choices, 'login');
28
+ async function defaultOpenBrowser(url) {
29
+ const command = getBrowserCommand(url);
30
+ if (!command) {
31
+ return false;
32
+ }
33
+ return await new Promise((resolve) => {
34
+ const child = (0, node_child_process_1.spawn)(command.command, command.args, {
35
+ detached: true,
36
+ stdio: 'ignore',
37
+ });
38
+ child.once('error', () => resolve(false));
39
+ child.once('spawn', () => {
40
+ child.unref();
41
+ resolve(true);
42
+ });
43
+ });
44
+ }
45
+ let openBrowser = defaultOpenBrowser;
46
+ function setOpenBrowserForTests(fn) {
47
+ openBrowser = fn;
48
+ }
49
+ function resetOpenBrowserForTests() {
50
+ openBrowser = defaultOpenBrowser;
51
+ }
52
+ function extractAccessToken(data) {
53
+ return data.accessToken
54
+ ?? (typeof data === 'object' && data !== null && 'token' in data ? data.token : undefined);
55
+ }
56
+ function isLoginResponse(data) {
57
+ return typeof data === 'object' && data !== null && 'user' in data;
58
+ }
59
+ function storeLogin(env, data) {
60
+ const token = extractAccessToken(data);
61
+ if (!token) {
62
+ (0, messages_1.printErrorWithHelp)('Login succeeded but no access token was returned.', [
63
+ 'Please try again in a moment.',
64
+ 'If the issue persists, contact the Playdrop team.',
65
+ ], { command: 'login' });
42
66
  process.exitCode = 1;
43
67
  return;
44
68
  }
45
- if (envConfig.allowInsecureRequests) {
46
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
69
+ (0, config_1.saveConfig)({ token, env });
70
+ const username = data.user?.username ?? 'unknown';
71
+ console.log(`Logged in as ${username} on ${env}.`);
72
+ console.log('Next: run "playdrop whoami" to confirm your session.');
73
+ }
74
+ async function loginWithUsernamePassword(env, baseUrl, username, password) {
75
+ const client = (0, apiClient_1.createCliApiClient)({ baseUrl });
76
+ try {
77
+ const data = await client.login({ username, password });
78
+ storeLogin(env, data);
47
79
  }
48
- const base = envConfig.apiBase;
49
- let username = usernameArg;
50
- let password = passwordArg;
51
- if (!username || !password) {
52
- const maskedOutput = new MaskedStdout();
53
- const rl = promises_1.default.createInterface({ input: node_process_1.stdin, output: maskedOutput, terminal: true });
54
- if (!username) {
55
- maskedOutput.muted = false;
56
- username = (await rl.question('Username: ')).trim();
80
+ catch (unknownError) {
81
+ if (unknownError instanceof types_1.UnsupportedClientError) {
82
+ (0, http_1.handleUnsupportedError)(unknownError, 'Login');
83
+ return;
57
84
  }
58
- if (!password) {
59
- maskedOutput.muted = true;
60
- password = await rl.question('Password: ');
61
- maskedOutput.muted = false;
85
+ if (unknownError instanceof types_1.ApiError) {
86
+ (0, messages_1.printErrorWithHelp)(`Login failed with status ${unknownError.status}.`, [
87
+ 'Verify your username and password, then rerun "playdrop login".',
88
+ 'Use "playdrop whoami" after logging in to confirm your status.',
89
+ ], { command: 'login' });
90
+ process.exitCode = 1;
91
+ return;
62
92
  }
63
- rl.close();
93
+ throw unknownError;
64
94
  }
65
- if (!username || !password) {
66
- (0, messages_1.printErrorWithHelp)('Username and password are required to log in.', [
67
- 'Provide both credentials when prompted, then rerun "playdrop auth login".',
68
- ], { command: 'auth login' });
69
- process.exitCode = 1;
70
- return;
95
+ }
96
+ async function loginWithKey(env, baseUrl, apiKey) {
97
+ const client = (0, apiClient_1.createCliApiClient)({ baseUrl });
98
+ try {
99
+ const data = await client.loginWithApiKey({ apiKey });
100
+ storeLogin(env, data);
101
+ }
102
+ catch (unknownError) {
103
+ if (unknownError instanceof types_1.UnsupportedClientError) {
104
+ (0, http_1.handleUnsupportedError)(unknownError, 'Login');
105
+ return;
106
+ }
107
+ if (unknownError instanceof types_1.ApiError) {
108
+ (0, messages_1.printErrorWithHelp)(`Login failed with status ${unknownError.status}.`, [
109
+ 'Verify your API key, then rerun "playdrop login --key <api-key>".',
110
+ 'Use "playdrop whoami" after logging in to confirm your status.',
111
+ ], { command: 'login' });
112
+ process.exitCode = 1;
113
+ return;
114
+ }
115
+ throw unknownError;
116
+ }
117
+ }
118
+ async function loginInBrowser(env, baseUrl) {
119
+ const client = (0, apiClient_1.createCliApiClient)({ baseUrl });
120
+ let flow;
121
+ try {
122
+ flow = await client.startCliLogin();
71
123
  }
72
- async function doLogin(currentBase) {
73
- const client = (0, apiClient_1.createCliApiClient)({ baseUrl: currentBase });
124
+ catch (unknownError) {
125
+ if (unknownError instanceof types_1.UnsupportedClientError) {
126
+ (0, http_1.handleUnsupportedError)(unknownError, 'Login');
127
+ return;
128
+ }
129
+ if (unknownError instanceof types_1.ApiError) {
130
+ (0, messages_1.printErrorWithHelp)(`Could not start browser login (status ${unknownError.status}).`, [
131
+ 'Try again in a moment.',
132
+ 'If the problem continues, contact the Playdrop team.',
133
+ ], { command: 'login' });
134
+ process.exitCode = 1;
135
+ return;
136
+ }
137
+ throw unknownError;
138
+ }
139
+ console.log(`Open this URL to continue: ${flow.loginUrl}`);
140
+ const opened = await openBrowser(flow.loginUrl).catch(() => false);
141
+ if (opened) {
142
+ console.log('Opened the login page in your browser.');
143
+ }
144
+ else {
145
+ console.log('Open the URL above in your browser to continue.');
146
+ }
147
+ const expiresAtMs = Date.parse(flow.expiresAt);
148
+ const deadlineMs = Number.isFinite(expiresAtMs)
149
+ ? expiresAtMs
150
+ : Date.now() + 10 * 60 * 1000;
151
+ const intervalMs = Math.max(250, Number(flow.pollIntervalMs) || 1000);
152
+ while (Date.now() < deadlineMs) {
74
153
  try {
75
- return await client.login({ username: username, password: password });
154
+ const result = await client.pollCliLogin({
155
+ requestId: flow.requestId,
156
+ pollToken: flow.pollToken,
157
+ });
158
+ if (!isLoginResponse(result)) {
159
+ await sleep(intervalMs);
160
+ continue;
161
+ }
162
+ storeLogin(env, result);
163
+ return;
76
164
  }
77
165
  catch (unknownError) {
78
166
  if (unknownError instanceof types_1.UnsupportedClientError) {
79
167
  (0, http_1.handleUnsupportedError)(unknownError, 'Login');
168
+ return;
80
169
  }
81
170
  if (unknownError instanceof types_1.ApiError) {
82
- (0, messages_1.printErrorWithHelp)(`Login failed with status ${unknownError.status}.`, [
83
- 'Verify your username and password, then rerun "playdrop auth login".',
84
- 'Use "playdrop auth whoami" after logging in to confirm your status.'
85
- ], { command: 'auth login' });
171
+ if (unknownError.status === 410 || unknownError.code === 'cli_login_expired') {
172
+ (0, messages_1.printErrorWithHelp)('Browser login expired before it was approved.', [
173
+ 'Rerun "playdrop login" to start a new browser login request.',
174
+ ], { command: 'login' });
175
+ process.exitCode = 1;
176
+ return;
177
+ }
178
+ (0, messages_1.printErrorWithHelp)(`Browser login failed with status ${unknownError.status}.`, [
179
+ 'Rerun "playdrop login" to try again.',
180
+ ], { command: 'login' });
86
181
  process.exitCode = 1;
87
- return null;
182
+ return;
88
183
  }
89
184
  throw unknownError;
90
185
  }
91
186
  }
92
- let data = null;
93
- try {
94
- data = await doLogin(base);
95
- }
96
- catch (e) {
97
- if (e instanceof http_1.CLIUnsupportedClientError) {
98
- return;
99
- }
100
- (0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to log in.', 'auth login');
187
+ (0, messages_1.printErrorWithHelp)('Browser login timed out after 10 minutes.', [
188
+ 'Rerun "playdrop login" to start a new browser login request.',
189
+ ], { command: 'login' });
190
+ process.exitCode = 1;
191
+ }
192
+ async function login(env, options = {}) {
193
+ const envConfig = (0, environment_1.resolveEnvironmentConfig)(env);
194
+ if (!envConfig) {
195
+ const choices = (0, environment_1.formatEnvironmentList)();
196
+ (0, messages_1.printUnknownEnvironment)(env, choices, 'login');
101
197
  process.exitCode = 1;
102
198
  return;
103
199
  }
104
- if (!data)
200
+ if (envConfig.allowInsecureRequests) {
201
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
202
+ }
203
+ const username = options.username?.trim();
204
+ const password = options.password;
205
+ const apiKey = options.key?.trim();
206
+ if (apiKey && (username || password)) {
207
+ (0, messages_1.printErrorWithHelp)('Choose exactly one login method.', [
208
+ 'Use "playdrop login" for browser login.',
209
+ 'Use "--username" with "--password" for direct login.',
210
+ 'Use "--key" by itself for API key login.',
211
+ ], { command: 'login' });
212
+ process.exitCode = 1;
105
213
  return;
106
- const token = data.accessToken ?? (typeof data === 'object' && data !== null && 'token' in data ? data.token : undefined);
107
- if (!token) {
108
- (0, messages_1.printErrorWithHelp)('Login succeeded but no access token was returned.', [
109
- 'Please try again in a moment.',
110
- 'If the issue persists, contact the Playdrop team.'
111
- ], { command: 'auth login' });
214
+ }
215
+ if ((username && !password) || (!username && password)) {
216
+ (0, messages_1.printErrorWithHelp)('Provide both "--username" and "--password" together.', [
217
+ 'Rerun "playdrop login --username <username> --password <password>".',
218
+ 'Or run "playdrop login" to use browser login instead.',
219
+ ], { command: 'login' });
112
220
  process.exitCode = 1;
113
221
  return;
114
222
  }
115
- (0, config_1.saveConfig)({ token, env });
116
- console.log(`Logged in as ${username} on ${env}.`);
117
- console.log('Next: run "playdrop auth whoami" to confirm your session.');
223
+ try {
224
+ if (apiKey) {
225
+ await loginWithKey(env, envConfig.apiBase, apiKey);
226
+ return;
227
+ }
228
+ if (username && password) {
229
+ await loginWithUsernamePassword(env, envConfig.apiBase, username, password);
230
+ return;
231
+ }
232
+ await loginInBrowser(env, envConfig.apiBase);
233
+ }
234
+ catch (error) {
235
+ if (error instanceof http_1.CLIUnsupportedClientError) {
236
+ return;
237
+ }
238
+ (0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to log in.', 'login');
239
+ process.exitCode = 1;
240
+ }
118
241
  }
@@ -5,5 +5,5 @@ const config_1 = require("../config");
5
5
  function logout() {
6
6
  (0, config_1.clearConfig)();
7
7
  console.log('Logged out.');
8
- console.log('Next: run "playdrop auth login" when you need a new session.');
8
+ console.log('Next: run "playdrop login" when you need a new session.');
9
9
  }
@@ -116,7 +116,7 @@ async function browseNotifications(options = {}) {
116
116
  const handled = (0, errors_1.handleCommandFailure)(error, 'notifications browse', 'Notification lookup', {
117
117
  apiMessage: (apiError) => ({
118
118
  problem: `Notification lookup failed with status ${apiError.status}.`,
119
- suggestions: ['Run "playdrop auth login" and retry.'],
119
+ suggestions: ['Run "playdrop login" and retry.'],
120
120
  }),
121
121
  });
122
122
  if (!handled) {
@@ -145,7 +145,7 @@ async function readNotification(rawId, options = {}) {
145
145
  const handled = (0, errors_1.handleCommandFailure)(error, 'notifications read', 'Notification update', {
146
146
  apiMessage: (apiError) => ({
147
147
  problem: `Notification update failed with status ${apiError.status}.`,
148
- suggestions: ['Run "playdrop auth login" and retry.'],
148
+ suggestions: ['Run "playdrop login" and retry.'],
149
149
  }),
150
150
  });
151
151
  if (!handled) {
@@ -168,7 +168,7 @@ async function readAllNotifications(options = {}) {
168
168
  const handled = (0, errors_1.handleCommandFailure)(error, 'notifications read-all', 'Notification update', {
169
169
  apiMessage: (apiError) => ({
170
170
  problem: `Notification update failed with status ${apiError.status}.`,
171
- suggestions: ['Run "playdrop auth login" and retry.'],
171
+ suggestions: ['Run "playdrop login" and retry.'],
172
172
  }),
173
173
  });
174
174
  if (!handled) {
@@ -192,7 +192,7 @@ async function browseVersions(rawRef, options = {}) {
192
192
  if (apiError.status === 401 || apiError.status === 403) {
193
193
  return {
194
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.'],
195
+ suggestions: ['Run "playdrop login" if this is private content you own.'],
196
196
  };
197
197
  }
198
198
  return {
@@ -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', 'auth whoami');
13
+ (0, messages_1.printLoginRequired)('Checking your Playdrop account status', '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, 'auth whoami');
20
+ (0, messages_1.printUnknownEnvironment)(cfg.env, choices, '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 auth login" to refresh your credentials.',
40
- 'Use "playdrop auth whoami" again after logging in to confirm your status.'
41
- ], { command: 'auth whoami' });
39
+ 'Run "playdrop login" to refresh your credentials.',
40
+ 'Use "playdrop whoami" again after logging in to confirm your status.',
41
+ ], { command: '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.', 'auth whoami');
56
+ (0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to check your account.', 'whoami');
57
57
  process.exitCode = 1;
58
58
  return;
59
59
  }
@@ -62,9 +62,9 @@ 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 auth whoami" in a moment.',
66
- 'If the problem continues, contact the Playdrop team.'
67
- ], { command: 'auth whoami' });
65
+ 'Retry "playdrop whoami" in a moment.',
66
+ 'If the problem continues, contact the Playdrop team.',
67
+ ], { command: 'whoami' });
68
68
  process.exitCode = 1;
69
69
  return;
70
70
  }
package/dist/index.js CHANGED
@@ -40,9 +40,6 @@ function resolveInitTarget(pathArg) {
40
40
  }
41
41
  function printRemovedCommandHint(command) {
42
42
  const replacements = {
43
- login: 'playdrop auth login',
44
- whoami: 'playdrop auth whoami',
45
- logout: 'playdrop auth logout',
46
43
  list: 'playdrop browse',
47
44
  generate: 'playdrop ai create',
48
45
  generations: 'playdrop ai jobs browse',
@@ -93,29 +90,48 @@ program.on('command:*', (unknownCommands) => {
93
90
  }
94
91
  process.exitCode = 1;
95
92
  });
96
- const envOption = new commander_1.Option('--env <env>', 'Environment (dev|local|prod)').default('prod');
93
+ function createEnvOption() {
94
+ return new commander_1.Option('--env <env>', 'Environment (dev|local|prod)').default('prod');
95
+ }
96
+ function registerLoginCommand(parent) {
97
+ parent
98
+ .command('login')
99
+ .description('Log in to Playdrop')
100
+ .addOption(createEnvOption())
101
+ .option('--username <username>', 'Username for direct login')
102
+ .option('--password <password>', 'Password for direct login')
103
+ .option('--key <apiKey>', 'API key for direct login')
104
+ .action(async (opts) => {
105
+ await (0, login_1.login)(opts.env, {
106
+ username: opts.username,
107
+ password: opts.password,
108
+ key: opts.key,
109
+ });
110
+ });
111
+ }
112
+ function registerLogoutCommand(parent) {
113
+ parent
114
+ .command('logout')
115
+ .description('Log out of Playdrop')
116
+ .action(() => {
117
+ (0, logout_1.logout)();
118
+ });
119
+ }
120
+ function registerWhoamiCommand(parent) {
121
+ parent
122
+ .command('whoami')
123
+ .description('Show the current account')
124
+ .action(async () => {
125
+ await (0, whoami_1.whoami)();
126
+ });
127
+ }
128
+ registerLoginCommand(program);
129
+ registerLogoutCommand(program);
130
+ registerWhoamiCommand(program);
97
131
  const auth = program.command('auth').description('Authentication commands');
98
- auth
99
- .command('login')
100
- .description('Log in to Playdrop')
101
- .addOption(envOption)
102
- .option('--username <username>', 'Username for non-interactive login')
103
- .option('--password <password>', 'Password for non-interactive login')
104
- .action(async (opts) => {
105
- await (0, login_1.login)(opts.env, opts.username, opts.password);
106
- });
107
- auth
108
- .command('logout')
109
- .description('Log out of Playdrop')
110
- .action(() => {
111
- (0, logout_1.logout)();
112
- });
113
- auth
114
- .command('whoami')
115
- .description('Show the current account')
116
- .action(async () => {
117
- await (0, whoami_1.whoami)();
118
- });
132
+ registerLoginCommand(auth);
133
+ registerLogoutCommand(auth);
134
+ registerWhoamiCommand(auth);
119
135
  const browseCommand = program.command('browse').description('Browse public content');
120
136
  browseCommand
121
137
  .option('--kind <kind>', 'app, asset, asset-pack, or all')
package/dist/messages.js CHANGED
@@ -28,22 +28,22 @@ function printErrorWithHelp(problem, suggestions, options = {}) {
28
28
  }
29
29
  function printLoginRequired(context, command) {
30
30
  printErrorWithHelp(`${context} requires you to be logged in.`, [
31
- 'Run "playdrop auth login" to authenticate.',
32
- 'Use "playdrop auth whoami" to verify your current status.'
31
+ 'Run "playdrop login" to authenticate.',
32
+ 'Use "playdrop whoami" to verify your current status.',
33
33
  ], { command });
34
34
  }
35
35
  function printUnknownEnvironment(env, available, command) {
36
36
  const listLine = available ? `Available environments: ${available}.` : '';
37
37
  const suggestions = [
38
38
  listLine,
39
- 'Run "playdrop auth login --env <env>" to store a supported environment before retrying.'
39
+ 'Run "playdrop login --env <env>" to store a supported environment before retrying.',
40
40
  ].filter(Boolean);
41
41
  printErrorWithHelp(`Environment "${env}" is not supported.`, suggestions, { command });
42
42
  }
43
43
  function printConfigEnvironmentMissing(command) {
44
44
  printErrorWithHelp('No environment is configured yet.', [
45
- 'Run "playdrop auth login" to choose an environment and save your credentials.',
46
- 'Use "playdrop auth whoami" after logging in to confirm your status.'
45
+ 'Run "playdrop login" to choose an environment and save your credentials.',
46
+ 'Use "playdrop whoami" after logging in to confirm your status.',
47
47
  ], { command });
48
48
  }
49
49
  function printNetworkIssue(context, command) {
@@ -66,8 +66,8 @@ function selectTasks(pathOrName) {
66
66
  if (taskList.length === 0) {
67
67
  errors.push({
68
68
  type: 'no-tasks',
69
- message: `No catalogue.json files were found under ${absolute}.`,
70
- help: ['Add catalogue.json files to the project folders you want to process, then rerun the command.'],
69
+ message: `No publishable entries were found under ${absolute}.`,
70
+ help: ['Add child catalogue.json files or add apps, assets, or assetPacks to the root catalogue.json, then rerun the command.'],
71
71
  directory: absolute,
72
72
  });
73
73
  return { tasks: [], warnings, errors, resolution: 'directory' };