@playdrop/playdrop-cli 0.5.2 → 0.5.4

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 (118) hide show
  1. package/config/client-meta.json +4 -4
  2. package/dist/apps/build.js +49 -6
  3. package/dist/apps/index.d.ts +2 -0
  4. package/dist/apps/index.js +2 -0
  5. package/dist/apps/upload.d.ts +2 -0
  6. package/dist/apps/upload.js +126 -28
  7. package/dist/assetSpecs.d.ts +16 -0
  8. package/dist/assetSpecs.js +263 -0
  9. package/dist/assets/model-artifacts.js +3 -0
  10. package/dist/catalogue.d.ts +57 -3
  11. package/dist/catalogue.js +342 -16
  12. package/dist/commandContext.d.ts +6 -2
  13. package/dist/commandContext.js +144 -20
  14. package/dist/commands/accounts.d.ts +2 -0
  15. package/dist/commands/accounts.js +48 -0
  16. package/dist/commands/ads.d.ts +8 -0
  17. package/dist/commands/ads.js +124 -0
  18. package/dist/commands/boosts.d.ts +25 -0
  19. package/dist/commands/boosts.js +209 -0
  20. package/dist/commands/browse.d.ts +6 -1
  21. package/dist/commands/browse.js +365 -124
  22. package/dist/commands/capture.js +30 -9
  23. package/dist/commands/captureListing.d.ts +53 -0
  24. package/dist/commands/captureListing.js +815 -0
  25. package/dist/commands/create.d.ts +1 -0
  26. package/dist/commands/create.js +183 -3
  27. package/dist/commands/credits.d.ts +6 -0
  28. package/dist/commands/credits.js +47 -1
  29. package/dist/commands/detail.js +38 -4
  30. package/dist/commands/dev.js +169 -192
  31. package/dist/commands/devServer.d.ts +26 -3
  32. package/dist/commands/devServer.js +415 -72
  33. package/dist/commands/login.js +10 -2
  34. package/dist/commands/logout.d.ts +6 -1
  35. package/dist/commands/logout.js +25 -3
  36. package/dist/commands/search.d.ts +5 -0
  37. package/dist/commands/search.js +139 -17
  38. package/dist/commands/tags.d.ts +7 -0
  39. package/dist/commands/tags.js +63 -0
  40. package/dist/commands/upload-content.d.ts +13 -3
  41. package/dist/commands/upload-content.js +86 -20
  42. package/dist/commands/upload.d.ts +2 -0
  43. package/dist/commands/upload.js +187 -11
  44. package/dist/commands/validate.js +163 -2
  45. package/dist/commands/versionsBrowse.js +128 -91
  46. package/dist/commands/whoami.js +10 -2
  47. package/dist/config.d.ts +37 -0
  48. package/dist/config.js +205 -3
  49. package/dist/index.js +177 -5
  50. package/dist/refs.d.ts +2 -2
  51. package/dist/refs.js +13 -1
  52. package/dist/taskSelection.js +6 -3
  53. package/dist/taskUtils.d.ts +2 -2
  54. package/dist/taskUtils.js +1 -0
  55. package/dist/uploadLog.d.ts +1 -1
  56. package/dist/uploadLog.js +2 -2
  57. package/dist/workspaceAuth.d.ts +14 -0
  58. package/dist/workspaceAuth.js +75 -0
  59. package/node_modules/@playdrop/ai-client/package.json +1 -1
  60. package/node_modules/@playdrop/api-client/dist/client.d.ts +139 -10
  61. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  62. package/node_modules/@playdrop/api-client/dist/client.js +6 -0
  63. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +9 -1
  64. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
  65. package/node_modules/@playdrop/api-client/dist/domains/admin.js +45 -0
  66. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +7 -1
  67. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
  68. package/node_modules/@playdrop/api-client/dist/domains/apps.js +58 -0
  69. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts +2 -0
  70. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts.map +1 -1
  71. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.js +16 -0
  72. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +44 -2
  73. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
  74. package/node_modules/@playdrop/api-client/dist/domains/assets.js +260 -3
  75. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +22 -1
  76. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
  77. package/node_modules/@playdrop/api-client/dist/domains/payments.js +228 -0
  78. package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -1
  79. package/node_modules/@playdrop/api-client/dist/domains/search.js +39 -11
  80. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts +34 -0
  81. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts.map +1 -0
  82. package/node_modules/@playdrop/api-client/dist/domains/tags.js +111 -0
  83. package/node_modules/@playdrop/api-client/dist/index.d.ts +69 -1
  84. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  85. package/node_modules/@playdrop/api-client/dist/index.js +74 -0
  86. package/node_modules/@playdrop/api-client/package.json +1 -1
  87. package/node_modules/@playdrop/boxel-core/package.json +1 -1
  88. package/node_modules/@playdrop/boxel-three/package.json +1 -1
  89. package/node_modules/@playdrop/config/client-meta.json +4 -4
  90. package/node_modules/@playdrop/config/dist/src/constants.d.ts +11 -0
  91. package/node_modules/@playdrop/config/dist/src/constants.d.ts.map +1 -1
  92. package/node_modules/@playdrop/config/dist/src/constants.js +12 -1
  93. package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
  94. package/node_modules/@playdrop/config/package.json +1 -1
  95. package/node_modules/@playdrop/types/dist/api.d.ts +366 -6
  96. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  97. package/node_modules/@playdrop/types/dist/api.js +52 -1
  98. package/node_modules/@playdrop/types/dist/asset-pack.d.ts +7 -1
  99. package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
  100. package/node_modules/@playdrop/types/dist/asset-spec-contract-meta-schema.json +86 -0
  101. package/node_modules/@playdrop/types/dist/asset-spec.d.ts +163 -0
  102. package/node_modules/@playdrop/types/dist/asset-spec.d.ts.map +1 -0
  103. package/node_modules/@playdrop/types/dist/asset-spec.js +101 -0
  104. package/node_modules/@playdrop/types/dist/asset.d.ts +23 -6
  105. package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
  106. package/node_modules/@playdrop/types/dist/asset.js +4 -1
  107. package/node_modules/@playdrop/types/dist/graph.d.ts +4 -2
  108. package/node_modules/@playdrop/types/dist/graph.d.ts.map +1 -1
  109. package/node_modules/@playdrop/types/dist/graph.js +9 -2
  110. package/node_modules/@playdrop/types/dist/index.d.ts +1 -0
  111. package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
  112. package/node_modules/@playdrop/types/dist/index.js +1 -0
  113. package/node_modules/@playdrop/types/dist/version.d.ts +13 -0
  114. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  115. package/node_modules/@playdrop/types/dist/version.js +21 -0
  116. package/node_modules/@playdrop/types/package.json +6 -1
  117. package/node_modules/@playdrop/vox-three/package.json +1 -1
  118. package/package.json +3 -1
@@ -6,21 +6,19 @@ const config_1 = require("./config");
6
6
  const apiClient_1 = require("./apiClient");
7
7
  const environment_1 = require("./environment");
8
8
  const messages_1 = require("./messages");
9
+ const workspaceAuth_1 = require("./workspaceAuth");
9
10
  const DEFAULT_PUBLIC_ENV = 'prod';
10
- function resolveConfiguredEnvironment(cfg, command, options) {
11
- const configuredEnv = typeof cfg.env === 'string' && cfg.env.trim().length > 0
12
- ? cfg.env.trim()
13
- : undefined;
14
- if (!configuredEnv && options.requireAuth) {
11
+ function resolveConfiguredEnvironment(envName, command, options) {
12
+ if (!envName && options.requireAuth) {
15
13
  (0, messages_1.printConfigEnvironmentMissing)(command);
16
14
  process.exitCode = 1;
17
15
  return null;
18
16
  }
19
- const envName = configuredEnv ?? (options.allowDefaultPublicEnv ? DEFAULT_PUBLIC_ENV : undefined);
20
- const envConfig = (0, environment_1.resolveEnvironmentConfig)(envName);
17
+ const resolvedEnvName = envName ?? (options.allowDefaultPublicEnv ? DEFAULT_PUBLIC_ENV : undefined);
18
+ const envConfig = (0, environment_1.resolveEnvironmentConfig)(resolvedEnvName);
21
19
  if (!envConfig) {
22
20
  const choices = (0, environment_1.formatEnvironmentList)();
23
- (0, messages_1.printUnknownEnvironment)(envName || '', choices, command);
21
+ (0, messages_1.printUnknownEnvironment)(resolvedEnvName || '', choices, command);
24
22
  process.exitCode = 1;
25
23
  return null;
26
24
  }
@@ -29,42 +27,168 @@ function resolveConfiguredEnvironment(cfg, command, options) {
29
27
  }
30
28
  return envConfig;
31
29
  }
32
- function buildContext(cfg, envConfig) {
33
- const client = (0, apiClient_1.createCliApiClient)({ baseUrl: envConfig.apiBase, token: cfg.token });
34
- const aiClient = (0, apiClient_1.createCliAiClient)({ baseUrl: envConfig.aiBase, token: cfg.token });
30
+ function buildContext(cfg, envConfig, account) {
31
+ const token = account?.token ?? cfg.token ?? '';
32
+ const client = (0, apiClient_1.createCliApiClient)({ baseUrl: envConfig.apiBase, token });
33
+ const aiClient = (0, apiClient_1.createCliAiClient)({ baseUrl: envConfig.aiBase, token });
35
34
  return {
36
35
  client,
37
36
  aiClient,
38
37
  env: envConfig.name,
39
38
  envConfig,
40
- token: cfg.token || '',
39
+ token,
41
40
  config: cfg,
41
+ account,
42
42
  };
43
43
  }
44
- async function withEnvironment(command, actionLabel, callback) {
45
- const cfg = (0, config_1.loadConfig)();
46
- if (!cfg.token) {
44
+ function buildLegacyAccountSession(cfg) {
45
+ if (!cfg.token || !cfg.env) {
46
+ return null;
47
+ }
48
+ return {
49
+ username: cfg.currentUsername ?? '',
50
+ env: cfg.env,
51
+ token: cfg.token,
52
+ updatedAt: '',
53
+ };
54
+ }
55
+ async function migrateLegacyConfigIfNeeded(cfg, envConfig, command) {
56
+ if ((0, config_1.getCurrentAccountSession)(cfg) || !cfg.token || !cfg.env) {
57
+ return cfg;
58
+ }
59
+ const client = (0, apiClient_1.createCliApiClient)({ baseUrl: envConfig.apiBase, token: cfg.token });
60
+ try {
61
+ const data = await client.me();
62
+ const username = typeof data.user?.username === 'string' ? data.user.username.trim() : '';
63
+ if (!username) {
64
+ (0, messages_1.printErrorWithHelp)('Could not migrate your existing Playdrop CLI session.', ['Run "playdrop auth login" to reauthenticate and rebuild your local account store.'], { command });
65
+ process.exitCode = 1;
66
+ return null;
67
+ }
68
+ return (0, config_1.migrateLegacySession)({
69
+ username,
70
+ env: envConfig.name,
71
+ token: cfg.token,
72
+ });
73
+ }
74
+ catch {
75
+ (0, messages_1.printErrorWithHelp)('Could not migrate your existing Playdrop CLI session.', ['Run "playdrop auth login" to reauthenticate and rebuild your local account store.'], { command });
76
+ process.exitCode = 1;
77
+ return null;
78
+ }
79
+ }
80
+ function resolveWorkspaceSelectedAccount(cfg, currentAccount, workspaceAuth) {
81
+ const matchingSessions = (0, config_1.listAccountSessionsForUsername)(workspaceAuth.config.ownerUsername, cfg);
82
+ if (workspaceAuth.config.env) {
83
+ return {
84
+ account: (0, config_1.findAccountSession)(workspaceAuth.config.ownerUsername, workspaceAuth.config.env, cfg),
85
+ matchingSessions,
86
+ };
87
+ }
88
+ if (matchingSessions.length === 1) {
89
+ return {
90
+ account: matchingSessions[0],
91
+ matchingSessions,
92
+ };
93
+ }
94
+ if (currentAccount && currentAccount.username === workspaceAuth.config.ownerUsername) {
95
+ return {
96
+ account: (0, config_1.findAccountSession)(workspaceAuth.config.ownerUsername, currentAccount.env, cfg),
97
+ matchingSessions,
98
+ };
99
+ }
100
+ return {
101
+ account: null,
102
+ matchingSessions,
103
+ };
104
+ }
105
+ async function resolveAuthenticatedEnvironment(command, actionLabel, options = {}) {
106
+ let cfg = (0, config_1.loadConfig)();
107
+ let workspaceAuth = null;
108
+ try {
109
+ workspaceAuth = options.workspacePath ? (0, workspaceAuth_1.findWorkspaceAuthConfig)(options.workspacePath) : null;
110
+ }
111
+ catch (error) {
112
+ if (error instanceof workspaceAuth_1.WorkspaceAuthConfigError) {
113
+ (0, messages_1.printErrorWithHelp)(`Invalid .playdrop.json at ${error.filePath}.`, [
114
+ 'The file must be valid JSON.',
115
+ 'It must include a non-empty "ownerUsername" string.',
116
+ ], { command });
117
+ process.exitCode = 1;
118
+ return null;
119
+ }
120
+ throw error;
121
+ }
122
+ if (workspaceAuth && !(0, config_1.getCurrentAccountSession)(cfg) && cfg.token && cfg.env) {
123
+ const provisionalEnvConfig = resolveConfiguredEnvironment(cfg.env, command, {
124
+ requireAuth: true,
125
+ allowDefaultPublicEnv: false,
126
+ });
127
+ if (!provisionalEnvConfig) {
128
+ return null;
129
+ }
130
+ const migrated = await migrateLegacyConfigIfNeeded(cfg, provisionalEnvConfig, command);
131
+ if (!migrated) {
132
+ return null;
133
+ }
134
+ cfg = migrated;
135
+ }
136
+ const currentAccount = (0, config_1.getCurrentAccountSession)(cfg) ?? buildLegacyAccountSession(cfg);
137
+ const selectedAccount = workspaceAuth
138
+ ? resolveWorkspaceSelectedAccount(cfg, currentAccount, workspaceAuth).account
139
+ : currentAccount;
140
+ if (!selectedAccount) {
141
+ if (workspaceAuth) {
142
+ const matchingSessions = (0, config_1.listAccountSessionsForUsername)(workspaceAuth.config.ownerUsername, cfg);
143
+ if (!workspaceAuth.config.env && matchingSessions.length > 1) {
144
+ (0, messages_1.printErrorWithHelp)(`This workspace is pinned to ${workspaceAuth.config.ownerUsername}, but that account is logged in on multiple environments.`, [
145
+ `Run "playdrop auth use ${workspaceAuth.config.ownerUsername} --env <env>" to select the matching environment.`,
146
+ 'Or add an "env" field to .playdrop.json.',
147
+ ], { command });
148
+ process.exitCode = 1;
149
+ return null;
150
+ }
151
+ const envSuffix = workspaceAuth.config.env ? ` on ${workspaceAuth.config.env}` : '';
152
+ (0, messages_1.printErrorWithHelp)(`This workspace is pinned to ${workspaceAuth.config.ownerUsername}${envSuffix}, but that account is not logged in.`, [
153
+ `Run "playdrop auth login${workspaceAuth.config.env ? ` --env ${workspaceAuth.config.env}` : ''}" while authenticated as ${workspaceAuth.config.ownerUsername}.`,
154
+ 'Or remove .playdrop.json if this workspace should use your current default account.',
155
+ ], { command });
156
+ process.exitCode = 1;
157
+ return null;
158
+ }
47
159
  (0, messages_1.printLoginRequired)(actionLabel, command);
48
160
  process.exitCode = 1;
49
- return;
161
+ return null;
50
162
  }
51
- const envConfig = resolveConfiguredEnvironment(cfg, command, {
163
+ const envConfig = resolveConfiguredEnvironment(selectedAccount.env, command, {
52
164
  requireAuth: true,
53
165
  allowDefaultPublicEnv: false,
54
166
  });
55
167
  if (!envConfig) {
168
+ return null;
169
+ }
170
+ return {
171
+ cfg: (0, config_1.loadConfig)(),
172
+ account: selectedAccount,
173
+ envConfig,
174
+ };
175
+ }
176
+ async function withEnvironment(command, actionLabel, callback, options = {}) {
177
+ const resolved = await resolveAuthenticatedEnvironment(command, actionLabel, options);
178
+ if (!resolved) {
56
179
  return;
57
180
  }
58
- await callback(buildContext(cfg, envConfig));
181
+ await callback(buildContext(resolved.cfg, resolved.envConfig, resolved.account));
59
182
  }
60
183
  async function withPublicEnvironment(command, callback) {
61
184
  const cfg = (0, config_1.loadConfig)();
62
- const envConfig = resolveConfiguredEnvironment(cfg, command, {
185
+ const account = (0, config_1.getCurrentAccountSession)(cfg);
186
+ const envConfig = resolveConfiguredEnvironment(account?.env ?? cfg.env, command, {
63
187
  requireAuth: false,
64
188
  allowDefaultPublicEnv: true,
65
189
  });
66
190
  if (!envConfig) {
67
191
  return;
68
192
  }
69
- await callback(buildContext(cfg, envConfig));
193
+ await callback(buildContext(cfg, envConfig, account));
70
194
  }
@@ -0,0 +1,2 @@
1
+ export declare function listAccounts(): void;
2
+ export declare function useAccount(username: string, env?: string): void;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listAccounts = listAccounts;
4
+ exports.useAccount = useAccount;
5
+ const config_1 = require("../config");
6
+ const messages_1 = require("../messages");
7
+ function listAccounts() {
8
+ const accounts = (0, config_1.listAccountSessions)();
9
+ if (accounts.length === 0) {
10
+ const cfg = (0, config_1.loadConfig)();
11
+ if (cfg.token && cfg.env) {
12
+ console.log('Your current Playdrop session is using the legacy single-account format.');
13
+ console.log('Next: run "playdrop auth whoami" to migrate it into the multi-account store.');
14
+ return;
15
+ }
16
+ console.log('No Playdrop accounts are stored.');
17
+ console.log('Next: run "playdrop auth login" to add an account.');
18
+ return;
19
+ }
20
+ const current = (0, config_1.getCurrentAccountSession)();
21
+ for (const account of accounts) {
22
+ const marker = current && current.username === account.username && current.env === account.env ? '*' : ' ';
23
+ console.log(`${marker} ${account.username} (${account.env})`);
24
+ }
25
+ console.log('Next: run "playdrop auth use <username>" to switch the default account.');
26
+ }
27
+ function useAccount(username, env) {
28
+ const normalizedUsername = username.trim();
29
+ if (!normalizedUsername) {
30
+ (0, messages_1.printErrorWithHelp)('Provide the username of the account you want to use.', ['Run "playdrop auth accounts" to list stored accounts.'], { command: 'auth use' });
31
+ process.exitCode = 1;
32
+ return;
33
+ }
34
+ const existing = (0, config_1.findAccountSession)(normalizedUsername, env);
35
+ if (!existing) {
36
+ (0, messages_1.printErrorWithHelp)(env
37
+ ? `No stored Playdrop session was found for ${normalizedUsername} on ${env}.`
38
+ : `No stored Playdrop session was found for ${normalizedUsername}.`, [
39
+ 'Run "playdrop auth accounts" to list stored accounts.',
40
+ `Run "playdrop auth login${env ? ` --env ${env}` : ''}" to add the missing session.`,
41
+ ], { command: 'auth use' });
42
+ process.exitCode = 1;
43
+ return;
44
+ }
45
+ (0, config_1.setCurrentAccount)({ username: normalizedUsername, env: env?.trim() || undefined });
46
+ console.log(`Current Playdrop account is now ${existing.username} (${existing.env}).`);
47
+ console.log('Next: run "playdrop auth whoami" to confirm your session.');
48
+ }
@@ -0,0 +1,8 @@
1
+ type AdsCommandOptions = {
2
+ app?: string;
3
+ days?: string | number;
4
+ json?: boolean;
5
+ };
6
+ export declare function showAdsEarnings(options?: AdsCommandOptions): Promise<void>;
7
+ export declare function showAdsReport(options?: AdsCommandOptions): Promise<void>;
8
+ export {};
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.showAdsEarnings = showAdsEarnings;
4
+ exports.showAdsReport = showAdsReport;
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 parsePositiveInteger(raw, command) {
11
+ if (raw === undefined) {
12
+ return 30;
13
+ }
14
+ const parsed = typeof raw === 'number' ? raw : Number.parseInt(String(raw), 10);
15
+ if (!Number.isInteger(parsed) || parsed <= 0) {
16
+ (0, messages_1.printErrorWithHelp)('The --days value must be a positive integer.', ['Example: --days 30'], { command });
17
+ process.exitCode = 1;
18
+ return null;
19
+ }
20
+ return parsed;
21
+ }
22
+ async function resolveOptionalAppId(client, rawAppRef) {
23
+ if (!rawAppRef) {
24
+ return undefined;
25
+ }
26
+ const ref = (0, refs_1.parseContentRef)(rawAppRef);
27
+ if (ref.kind !== 'app') {
28
+ throw new Error('invalid_app_ref');
29
+ }
30
+ const detail = await client.fetchAppBySlug(ref.creator, ref.name);
31
+ return Number(detail.app.id);
32
+ }
33
+ async function showAdsEarnings(options = {}) {
34
+ const days = parsePositiveInteger(options.days, 'ads earnings');
35
+ if (days === null) {
36
+ return;
37
+ }
38
+ await (0, commandContext_1.withEnvironment)('ads earnings', 'Checking creator ad earnings', async ({ client }) => {
39
+ try {
40
+ const appId = await resolveOptionalAppId(client, options.app);
41
+ const response = await client.fetchCreatorEarnings({
42
+ days,
43
+ ...(typeof appId === 'number' && appId > 0 ? { appId } : {}),
44
+ });
45
+ if (options.json) {
46
+ (0, output_1.printJson)(response);
47
+ return;
48
+ }
49
+ const aggregateRows = new Map();
50
+ for (const day of response.days) {
51
+ for (const row of day.apps) {
52
+ const key = `${row.appId}:${row.format}`;
53
+ const current = aggregateRows.get(key) ?? {
54
+ appName: row.appName,
55
+ format: row.format,
56
+ amount: 0,
57
+ servedCount: 0,
58
+ creditedCount: 0,
59
+ };
60
+ current.amount += row.amount;
61
+ current.servedCount += row.servedCount;
62
+ current.creditedCount += row.creditedCount;
63
+ aggregateRows.set(key, current);
64
+ }
65
+ }
66
+ console.log(`Today provisional: ${response.summary.provisionalToday}`);
67
+ console.log(`Last 7 days settled: ${response.summary.settledLast7Days}`);
68
+ console.log(`Last 30 days settled: ${response.summary.settledLast30Days}`);
69
+ if (aggregateRows.size === 0) {
70
+ console.log('\nNo ad earnings found for the selected window.');
71
+ return;
72
+ }
73
+ console.log('\nBy app and format:\n');
74
+ for (const row of aggregateRows.values()) {
75
+ console.log(`${row.appName} | ${row.format} | earnings ${row.amount} | served ${row.servedCount} | credited ${row.creditedCount}`);
76
+ }
77
+ }
78
+ catch (error) {
79
+ const handled = (0, errors_1.handleCommandFailure)(error, 'ads earnings', 'Ad earnings lookup', {
80
+ apiMessage: (apiError) => ({
81
+ problem: `Ad earnings lookup failed with status ${apiError.status}.`,
82
+ suggestions: ['Run "playdrop auth login" and retry.'],
83
+ }),
84
+ });
85
+ if (!handled) {
86
+ throw error;
87
+ }
88
+ }
89
+ });
90
+ }
91
+ async function showAdsReport(options = {}) {
92
+ const days = parsePositiveInteger(options.days, 'ads report');
93
+ if (days === null) {
94
+ return;
95
+ }
96
+ await (0, commandContext_1.withEnvironment)('ads report', 'Checking creator ad report', async ({ client }) => {
97
+ try {
98
+ const appId = await resolveOptionalAppId(client, options.app);
99
+ const response = await client.fetchCreatorEarnings({
100
+ days,
101
+ ...(typeof appId === 'number' && appId > 0 ? { appId } : {}),
102
+ });
103
+ if (options.json) {
104
+ (0, output_1.printJson)(response);
105
+ return;
106
+ }
107
+ console.log('Daily ad report:\n');
108
+ for (const row of response.days) {
109
+ console.log(`${row.dateUtc} | total ${row.totalAmount} | interstitial ${row.interstitialAmount} | rewarded ${row.rewardedAmount} | served ${row.servedCount} | credited ${row.creditedCount}${row.provisional ? ' | provisional' : ''}`);
110
+ }
111
+ }
112
+ catch (error) {
113
+ const handled = (0, errors_1.handleCommandFailure)(error, 'ads report', 'Ad report lookup', {
114
+ apiMessage: (apiError) => ({
115
+ problem: `Ad report lookup failed with status ${apiError.status}.`,
116
+ suggestions: ['Run "playdrop auth login" and retry.'],
117
+ }),
118
+ });
119
+ if (!handled) {
120
+ throw error;
121
+ }
122
+ }
123
+ });
124
+ }
@@ -0,0 +1,25 @@
1
+ export declare function showBoostBalance(options?: {
2
+ json?: boolean;
3
+ }): Promise<void>;
4
+ export declare function browseBoostOffers(options?: {
5
+ json?: boolean;
6
+ }): Promise<void>;
7
+ export declare function buyBoostPack(sku: string, options?: {
8
+ quantity?: string | number;
9
+ json?: boolean;
10
+ }): Promise<void>;
11
+ export declare function activateBoost(rawRef: string, options?: {
12
+ units?: string | number;
13
+ json?: boolean;
14
+ }): Promise<void>;
15
+ export declare function showBoostStatus(rawRef: string, options?: {
16
+ json?: boolean;
17
+ }): Promise<void>;
18
+ export declare function showBoostHistory(rawRef: string, options?: {
19
+ limit?: string | number;
20
+ json?: boolean;
21
+ }): Promise<void>;
22
+ export declare function showBoostReport(rawRef: string, options?: {
23
+ days?: string | number;
24
+ json?: boolean;
25
+ }): Promise<void>;
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.showBoostBalance = showBoostBalance;
4
+ exports.browseBoostOffers = browseBoostOffers;
5
+ exports.buyBoostPack = buyBoostPack;
6
+ exports.activateBoost = activateBoost;
7
+ exports.showBoostStatus = showBoostStatus;
8
+ exports.showBoostHistory = showBoostHistory;
9
+ exports.showBoostReport = showBoostReport;
10
+ const commandContext_1 = require("../commandContext");
11
+ const errors_1 = require("../errors");
12
+ const messages_1 = require("../messages");
13
+ const output_1 = require("../output");
14
+ const refs_1 = require("../refs");
15
+ function parsePositiveInteger(raw, label, fallback) {
16
+ if (raw === undefined) {
17
+ return fallback;
18
+ }
19
+ const parsed = typeof raw === 'number' ? raw : Number.parseInt(String(raw), 10);
20
+ if (!Number.isInteger(parsed) || parsed <= 0) {
21
+ return null;
22
+ }
23
+ return parsed;
24
+ }
25
+ async function resolveAppForBoost(client, rawRef) {
26
+ const ref = (0, refs_1.parseContentRef)(rawRef);
27
+ if (ref.kind !== 'app') {
28
+ throw new Error('invalid_app_ref');
29
+ }
30
+ const detail = await client.fetchAppBySlug(ref.creator, ref.name);
31
+ return {
32
+ ref,
33
+ app: detail.app,
34
+ };
35
+ }
36
+ function formatReportRow(row) {
37
+ return [
38
+ row.dateUtc,
39
+ `impressions ${row.interstitialImpressions + row.rewardedImpressions}`,
40
+ `views ${row.regularDetailViews} regular / ${row.promotedDetailViews} boosted`,
41
+ `plays ${row.regularPlays} regular / ${row.promotedPlays} boosted`,
42
+ `earnings ${row.adEarnings}`,
43
+ ].join(' | ');
44
+ }
45
+ function formatRunDuration(startedAt, endsAt) {
46
+ const totalHours = Math.max(1, Math.round((new Date(endsAt).getTime() - new Date(startedAt).getTime()) / (60 * 60 * 1000)));
47
+ const days = Math.floor(totalHours / 24);
48
+ const hours = totalHours % 24;
49
+ if (days > 0 && hours > 0) {
50
+ return `${days}d ${hours}h`;
51
+ }
52
+ if (days > 0) {
53
+ return `${days}d`;
54
+ }
55
+ return `${hours}h`;
56
+ }
57
+ function formatUtcDate(value) {
58
+ return value.toISOString().slice(0, 10);
59
+ }
60
+ function resolveReportRangeFromDays(days, now = new Date()) {
61
+ const end = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
62
+ const start = new Date(end.getTime() - ((days - 1) * 24 * 60 * 60 * 1000));
63
+ return {
64
+ startDateUtc: formatUtcDate(start),
65
+ endDateUtc: formatUtcDate(end),
66
+ };
67
+ }
68
+ async function showBoostBalance(options = {}) {
69
+ await (0, commandContext_1.withEnvironment)('boosts balance', 'Checking your boost balance', async ({ client }) => {
70
+ const response = await client.fetchBoostBalance();
71
+ if (options.json) {
72
+ (0, output_1.printJson)(response);
73
+ return;
74
+ }
75
+ (0, output_1.printSuccess)(`Boost balance: ${response.balance}`, ['Next: run "playdrop boosts offers" or "playdrop boosts buy boost_1".']);
76
+ });
77
+ }
78
+ async function browseBoostOffers(options = {}) {
79
+ await (0, commandContext_1.withEnvironment)('boosts offers', 'Checking available boost packs', async ({ client }) => {
80
+ const response = await client.fetchBoostBalance();
81
+ if (options.json) {
82
+ (0, output_1.printJson)({ offers: response.offers });
83
+ return;
84
+ }
85
+ console.log('Boost offers:\n');
86
+ for (const offer of response.offers) {
87
+ console.log(`${offer.sku} | ${offer.units} units | ${offer.priceCredits} credits`);
88
+ }
89
+ });
90
+ }
91
+ async function buyBoostPack(sku, options = {}) {
92
+ const normalizedSku = sku.trim().toLowerCase();
93
+ if (!['boost_1', 'boost_3', 'boost_7', 'boost_14', 'boost_31'].includes(normalizedSku)) {
94
+ (0, messages_1.printErrorWithHelp)('Boost SKU must be one of boost_1, boost_3, boost_7, boost_14, or boost_31.', ['Example: playdrop boosts buy boost_1'], { command: 'boosts buy' });
95
+ process.exitCode = 1;
96
+ return;
97
+ }
98
+ const quantity = parsePositiveInteger(options.quantity, 'quantity', 1);
99
+ if (quantity === null) {
100
+ (0, messages_1.printErrorWithHelp)('The --quantity value must be a positive integer.', ['Example: --quantity 2'], { command: 'boosts buy' });
101
+ process.exitCode = 1;
102
+ return;
103
+ }
104
+ await (0, commandContext_1.withEnvironment)('boosts buy', 'Buying boost pack', async ({ client }) => {
105
+ try {
106
+ const response = await client.purchaseBoostPack({ sku: normalizedSku, quantity });
107
+ if (options.json) {
108
+ (0, output_1.printJson)(response);
109
+ return;
110
+ }
111
+ (0, output_1.printSuccess)(`Purchased ${response.offer.displayName}${response.quantity > 1 ? ` x ${response.quantity}` : ''}. Added ${response.unitsAdded} boost${response.unitsAdded === 1 ? '' : 's'}. Boost balance: ${response.boostBalance}. Credit balance: ${response.creditBalance}.`, ['Next: run "playdrop boosts activate <creator>/app/<name>".']);
112
+ }
113
+ catch (error) {
114
+ const handled = (0, errors_1.handleCommandFailure)(error, 'boosts buy', 'Boost purchase', {
115
+ apiMessage: (apiError) => ({
116
+ problem: `Boost purchase failed with status ${apiError.status}.`,
117
+ suggestions: ['Check your credit balance with "playdrop credits balance".'],
118
+ }),
119
+ });
120
+ if (!handled) {
121
+ throw error;
122
+ }
123
+ }
124
+ });
125
+ }
126
+ async function activateBoost(rawRef, options = {}) {
127
+ const units = parsePositiveInteger(options.units, 'units', 1);
128
+ if (units === null) {
129
+ (0, messages_1.printErrorWithHelp)('The --units value must be a positive integer.', ['Example: --units 2'], { command: 'boosts activate' });
130
+ process.exitCode = 1;
131
+ return;
132
+ }
133
+ await (0, commandContext_1.withEnvironment)('boosts activate', 'Activating boost', async ({ client }) => {
134
+ const { app } = await resolveAppForBoost(client, rawRef);
135
+ const response = await client.activateAppBoost(app.id, { units });
136
+ if (options.json) {
137
+ (0, output_1.printJson)(response);
138
+ return;
139
+ }
140
+ (0, output_1.printSuccess)(`Boost active until ${response.boostedUntil ?? 'unknown'}. Remaining inventory: ${response.boostsAvailable}.`);
141
+ });
142
+ }
143
+ async function showBoostStatus(rawRef, options = {}) {
144
+ await (0, commandContext_1.withEnvironment)('boosts status', 'Checking boost status', async ({ client }) => {
145
+ const { app } = await resolveAppForBoost(client, rawRef);
146
+ const response = await client.fetchAppBoostStatus(app.id);
147
+ if (options.json) {
148
+ (0, output_1.printJson)(response);
149
+ return;
150
+ }
151
+ console.log(`${response.app.displayName || response.app.name}`);
152
+ console.log(`Status: ${response.isActive ? 'boosted' : 'inactive'}`);
153
+ console.log(`Boosts available: ${response.boostsAvailable}`);
154
+ console.log(`Boosted until: ${response.boostedUntil ?? 'none'}`);
155
+ if (response.currentRun) {
156
+ console.log(`Current run started: ${(0, output_1.formatTimestamp)(response.currentRun.startedAt)}`);
157
+ console.log(`Current run ends: ${(0, output_1.formatTimestamp)(response.currentRun.endsAt)}`);
158
+ console.log(`Current run units: ${response.currentRun.consumedUnits}`);
159
+ console.log(`Current run impressions: ${response.currentRun.deliveredImpressionCount}`);
160
+ console.log(`Current run views: ${response.currentRun.promotedDetailViewCount}`);
161
+ console.log(`Current run plays: ${response.currentRun.promotedPlayCount}`);
162
+ }
163
+ });
164
+ }
165
+ async function showBoostHistory(rawRef, options = {}) {
166
+ const limit = parsePositiveInteger(options.limit, 'limit', 30);
167
+ if (limit === null) {
168
+ (0, messages_1.printErrorWithHelp)('The --limit value must be a positive integer.', ['Example: --limit 20'], { command: 'boosts history' });
169
+ process.exitCode = 1;
170
+ return;
171
+ }
172
+ await (0, commandContext_1.withEnvironment)('boosts history', 'Checking boost history', async ({ client }) => {
173
+ const { app } = await resolveAppForBoost(client, rawRef);
174
+ const response = await client.fetchAppBoostHistory(app.id, { limit });
175
+ if (options.json) {
176
+ (0, output_1.printJson)(response);
177
+ return;
178
+ }
179
+ if (response.runs.length === 0) {
180
+ console.log('No boost runs found.');
181
+ return;
182
+ }
183
+ console.log('Boost history:\n');
184
+ for (const run of response.runs) {
185
+ console.log(`#${run.id} | ${run.status.toLowerCase()} | ${(0, output_1.formatTimestamp)(run.startedAt)} -> ${(0, output_1.formatTimestamp)(run.endsAt)}`);
186
+ console.log(` duration ${formatRunDuration(run.startedAt, run.endsAt)} | units ${run.consumedUnits} | impressions ${run.deliveredImpressionCount} | views ${run.promotedDetailViewCount} | plays ${run.promotedPlayCount}`);
187
+ }
188
+ });
189
+ }
190
+ async function showBoostReport(rawRef, options = {}) {
191
+ const days = parsePositiveInteger(options.days, 'days', 30);
192
+ if (days === null) {
193
+ (0, messages_1.printErrorWithHelp)('The --days value must be a positive integer.', ['Example: --days 30'], { command: 'boosts report' });
194
+ process.exitCode = 1;
195
+ return;
196
+ }
197
+ await (0, commandContext_1.withEnvironment)('boosts report', 'Checking boost report', async ({ client }) => {
198
+ const { app } = await resolveAppForBoost(client, rawRef);
199
+ const response = await client.fetchAppBoostReport(app.id, resolveReportRangeFromDays(days));
200
+ if (options.json) {
201
+ (0, output_1.printJson)(response);
202
+ return;
203
+ }
204
+ console.log(`Boost report for ${app.displayName || app.name}:\n`);
205
+ for (const row of response.days) {
206
+ console.log(formatReportRow(row));
207
+ }
208
+ });
209
+ }
@@ -1,4 +1,4 @@
1
- export type BrowseKind = 'app' | 'asset' | 'asset-pack' | 'all';
1
+ export type BrowseKind = 'app' | 'asset' | 'asset-pack' | 'asset-spec' | 'all';
2
2
  export type BrowseCreator = 'all' | 'me' | string;
3
3
  export type BrowseOptions = {
4
4
  kind?: string;
@@ -6,8 +6,13 @@ export type BrowseOptions = {
6
6
  appType?: string;
7
7
  assetCategory?: string;
8
8
  assetSubcategory?: string;
9
+ assetSpec?: string;
10
+ assetSpecOwner?: string;
11
+ assetSpecName?: string;
12
+ sort?: string;
9
13
  limit?: string | number;
10
14
  offset?: string | number;
15
+ tag?: string[];
11
16
  json?: boolean;
12
17
  };
13
18
  export declare function browse(options?: BrowseOptions): Promise<void>;