@playdrop/playdrop-cli 0.14.6 → 0.14.7

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 (55) hide show
  1. package/config/client-meta.json +2 -2
  2. package/dist/apps/upload.js +1 -0
  3. package/dist/catalogue.d.ts +3 -1
  4. package/dist/catalogue.js +10 -0
  5. package/dist/commands/dev.js +9 -0
  6. package/dist/commands/devRuntimeAssets.d.ts +2 -1
  7. package/dist/commands/devRuntimeAssets.js +3 -0
  8. package/dist/commands/notes.d.ts +7 -0
  9. package/dist/commands/notes.js +107 -0
  10. package/dist/commands/tweaks.d.ts +6 -0
  11. package/dist/commands/tweaks.js +93 -0
  12. package/dist/commands/validate.js +14 -0
  13. package/dist/index.js +39 -0
  14. package/dist/listingPreflight.d.ts +4 -0
  15. package/dist/listingPreflight.js +17 -0
  16. package/dist/tweakStaleness.d.ts +7 -0
  17. package/dist/tweakStaleness.js +36 -0
  18. package/node_modules/@playdrop/api-client/dist/client.d.ts +25 -4
  19. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  20. package/node_modules/@playdrop/api-client/dist/client.js +40 -0
  21. package/node_modules/@playdrop/api-client/dist/core/request.d.ts +1 -0
  22. package/node_modules/@playdrop/api-client/dist/core/request.d.ts.map +1 -1
  23. package/node_modules/@playdrop/api-client/dist/core/request.js +2 -1
  24. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +2 -1
  25. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
  26. package/node_modules/@playdrop/api-client/dist/domains/admin.js +27 -0
  27. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +18 -4
  28. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
  29. package/node_modules/@playdrop/api-client/dist/domains/apps.js +76 -6
  30. package/node_modules/@playdrop/api-client/dist/index.d.ts +35 -4
  31. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  32. package/node_modules/@playdrop/api-client/dist/index.js +79 -4
  33. package/node_modules/@playdrop/config/client-meta.json +2 -2
  34. package/node_modules/@playdrop/config/dist/src/game-arena.d.ts +2 -2
  35. package/node_modules/@playdrop/config/dist/src/game-arena.js +2 -2
  36. package/node_modules/@playdrop/config/dist/test/game-arena.test.js +4 -3
  37. package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
  38. package/node_modules/@playdrop/types/dist/api.d.ts +76 -2
  39. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  40. package/node_modules/@playdrop/types/dist/api.js +39 -2
  41. package/node_modules/@playdrop/types/dist/app-review.d.ts +11 -0
  42. package/node_modules/@playdrop/types/dist/app-review.d.ts.map +1 -0
  43. package/node_modules/@playdrop/types/dist/app-review.js +56 -0
  44. package/node_modules/@playdrop/types/dist/index.d.ts +3 -0
  45. package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
  46. package/node_modules/@playdrop/types/dist/index.js +6 -0
  47. package/node_modules/@playdrop/types/dist/playtest-notes.d.ts +69 -0
  48. package/node_modules/@playdrop/types/dist/playtest-notes.d.ts.map +1 -0
  49. package/node_modules/@playdrop/types/dist/playtest-notes.js +30 -0
  50. package/node_modules/@playdrop/types/dist/tweaks.d.ts +48 -0
  51. package/node_modules/@playdrop/types/dist/tweaks.d.ts.map +1 -0
  52. package/node_modules/@playdrop/types/dist/tweaks.js +156 -0
  53. package/node_modules/@playdrop/types/dist/version.d.ts +17 -0
  54. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  55. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "0.14.6",
2
+ "version": "0.14.7",
3
3
  "build": 1,
4
- "runtimeSdkVersion": "0.14.6",
4
+ "runtimeSdkVersion": "0.14.7",
5
5
  "runtimeSdkBuild": 1,
6
6
  "clients": {
7
7
  "all": {
@@ -245,6 +245,7 @@ async function uploadAppVersion(client, task, artifacts, options) {
245
245
  usesPacks: task.uses.packs,
246
246
  tags: task.tags,
247
247
  design: task.design,
248
+ tweaks: task.tweaks,
248
249
  clearTags: options?.clearTags,
249
250
  surfaceTargets: task.surfaceTargets,
250
251
  primarySurface: task.primarySurface,
@@ -1,4 +1,4 @@
1
- import { type AppAchievementCatalogueDefinition, type AppAuthMode, type AppControllerMode, type AppHostingMode, type AppLeaderboardCatalogueDefinition, type AppListingMediaMetadata, type AppMetadataAssetSpecSupport, type AppPlaytestTapes, type AppSurface, type AppType, type AppVersionDesign, type AppVersionVisibility, type AssetSpecContract, type AssetSpecStatus, type AssetSpecValidationKind, type CatalogueTagGroupDefinition, type ContentLicense, type PlayerMetaDefinitionStatus } from '@playdrop/types';
1
+ import { type AppAchievementCatalogueDefinition, type AppAuthMode, type AppControllerMode, type AppHostingMode, type AppLeaderboardCatalogueDefinition, type AppListingMediaMetadata, type AppMetadataAssetSpecSupport, type AppPlaytestTapes, type AppSurface, type AppType, type AppTweaksDeclaration, type AppVersionDesign, type AppVersionVisibility, type AssetSpecContract, type AssetSpecStatus, type AssetSpecValidationKind, type CatalogueTagGroupDefinition, type ContentLicense, type PlayerMetaDefinitionStatus } from '@playdrop/types';
2
2
  import { type AssetSpecSupportDeclaration } from './assetSpecs';
3
3
  export type CatalogueJson = {
4
4
  apps?: Array<Record<string, unknown>>;
@@ -54,6 +54,7 @@ export type AppCatalogueEntry = {
54
54
  primarySurface?: string | null;
55
55
  playtestTapes?: unknown;
56
56
  design?: unknown;
57
+ tweaks?: unknown;
57
58
  version?: string;
58
59
  releaseNotes?: string;
59
60
  visibility?: string;
@@ -196,6 +197,7 @@ export type AppTask = {
196
197
  primarySurface?: AppSurface;
197
198
  playtestTapes?: AppPlaytestTapes;
198
199
  design?: AppVersionDesign;
200
+ tweaks?: AppTweaksDeclaration;
199
201
  missingMetadata: string[];
200
202
  version?: string;
201
203
  releaseNotes?: string;
package/dist/catalogue.js CHANGED
@@ -1375,6 +1375,15 @@ function buildAppTasks(rootDir, catalogues, options) {
1375
1375
  }
1376
1376
  design = parsedDesign.value;
1377
1377
  }
1378
+ let tweaks;
1379
+ if (Object.prototype.hasOwnProperty.call(entry, 'tweaks')) {
1380
+ const parsedTweaks = (0, types_1.normalizeAppTweaks)(entry.tweaks);
1381
+ if (!parsedTweaks.ok) {
1382
+ errors.push(`[${label}] App "${rawName}" ${parsedTweaks.error}`);
1383
+ continue;
1384
+ }
1385
+ tweaks = parsedTweaks.value;
1386
+ }
1378
1387
  const errorCountBeforeTagMetadata = errors.length;
1379
1388
  const remix = normalizeCatalogueRemixRef(entry.remix, 'app', errors, `[${label}] App "${rawName}"`);
1380
1389
  const tags = normalizeCatalogueTags(entry.tags, errors, `[${label}] App "${rawName}"`);
@@ -1579,6 +1588,7 @@ function buildAppTasks(rootDir, catalogues, options) {
1579
1588
  primarySurface: metadata.primarySurface,
1580
1589
  playtestTapes: metadata.playtestTapes,
1581
1590
  design,
1591
+ tweaks,
1582
1592
  missingMetadata: metadata.missingFields,
1583
1593
  // Versioning fields
1584
1594
  version,
@@ -16,6 +16,7 @@ const http_1 = require("../http");
16
16
  const messages_1 = require("../messages");
17
17
  const devRuntimeAssets_1 = require("./devRuntimeAssets");
18
18
  const devServer_1 = require("./devServer");
19
+ const tweakStaleness_1 = require("../tweakStaleness");
19
20
  const devShared_1 = require("./devShared");
20
21
  function formatDevRuntimeAssetManifestFailure(error) {
21
22
  const rawMessage = error instanceof Error ? error.message : String(error ?? 'unknown_error');
@@ -301,6 +302,14 @@ async function dev(targetArg, appOption, devOptions = {}) {
301
302
  });
302
303
  let runtimeAssetManifest = (0, devRuntimeAssets_1.createEmptyDevRuntimeAssetManifest)();
303
304
  if (taskLookup.task) {
305
+ try {
306
+ await (0, tweakStaleness_1.fetchAndAssertTweakState)(client, currentUsername, taskLookup.task);
307
+ }
308
+ catch (error) {
309
+ (0, messages_1.printErrorWithHelp)(error instanceof Error ? error.message : 'Could not verify the current tweak state.', [], { command: 'project dev' });
310
+ process.exitCode = 1;
311
+ return;
312
+ }
304
313
  try {
305
314
  runtimeAssetManifest = await (0, devRuntimeAssets_1.buildDevRuntimeAssetManifest)({
306
315
  client,
@@ -1,5 +1,5 @@
1
1
  import type { ApiClient } from '@playdrop/api-client';
2
- import { type AppAuthMode, type AppControllerMode, type AppMetadataAssetSpecSupport, type AppRuntimeAssetsResponse, type AppSurface, type AppType } from '@playdrop/types';
2
+ import { type AppAuthMode, type AppControllerMode, type AppMetadataAssetSpecSupport, type AppRuntimeAssetsResponse, type AppSurface, type AppType, type TweakValues } from '@playdrop/types';
3
3
  import type { AppTask } from '../catalogue';
4
4
  export type LocalOwnedRuntimeAssetFileRecord = {
5
5
  absolutePath: string;
@@ -14,6 +14,7 @@ export type LocalDevAppMetadata = {
14
14
  previewable: boolean;
15
15
  assetSpecSupport: AppMetadataAssetSpecSupport[];
16
16
  version: string | null;
17
+ tweakValues?: TweakValues;
17
18
  };
18
19
  export type DevRuntimeAssetsResponse = AppRuntimeAssetsResponse & {
19
20
  localAppMetadata: LocalDevAppMetadata;
@@ -124,6 +124,9 @@ function buildLocalAppMetadata(task) {
124
124
  capabilities: Array.from(entry.capabilities),
125
125
  })),
126
126
  version: typeof task.version === 'string' && task.version.trim().length > 0 ? task.version.trim() : null,
127
+ ...(task.tweaks && task.tweaks.removed !== true
128
+ ? { tweakValues: { ...task.tweaks.defaults } }
129
+ : {}),
127
130
  };
128
131
  }
129
132
  function assertCustomAssetVersionSupported(task, assetRef, version) {
@@ -0,0 +1,7 @@
1
+ export declare function browseNotes(rawRef: string | undefined, options?: {
2
+ json?: boolean;
3
+ output?: string;
4
+ }): Promise<void>;
5
+ export declare function clearNotes(rawRef: string | undefined, rawIds: string[], options?: {
6
+ json?: boolean;
7
+ }): Promise<void>;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.browseNotes = browseNotes;
4
+ exports.clearNotes = clearNotes;
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = require("node:path");
7
+ const types_1 = require("@playdrop/types");
8
+ const commandContext_1 = require("../commandContext");
9
+ const errors_1 = require("../errors");
10
+ const messages_1 = require("../messages");
11
+ const output_1 = require("../output");
12
+ function notesApiMessage(error) {
13
+ if (error.code === 'playtest_notes_changed') {
14
+ const missingIds = Array.isArray(error.details?.['missingIds']) ? error.details['missingIds'] : [];
15
+ return {
16
+ problem: `The note inbox changed before it could be cleared${missingIds.length > 0 ? `: ${missingIds.join(', ')}` : '.'}`,
17
+ suggestions: ['Run notes browse again, then clear the current IDs.'],
18
+ };
19
+ }
20
+ return { problem: `PlayDrop rejected the notes request: ${error.code ?? error.message} (HTTP ${error.status}).` };
21
+ }
22
+ function parseAppVersionRef(raw, command) {
23
+ const ref = (0, types_1.parseContentVersionRef)(raw ?? '');
24
+ if (!ref || ref.kind !== 'app') {
25
+ (0, messages_1.printErrorWithHelp)('An exact canonical app-version ref is required.', ['Use app:creator/name@x.y.z.', `Example: playdrop ${command} app:olivier/platformer@1.4.0`], { command });
26
+ process.exitCode = 1;
27
+ return null;
28
+ }
29
+ return ref;
30
+ }
31
+ async function browseNotes(rawRef, options = {}) {
32
+ const ref = parseAppVersionRef(rawRef, 'notes browse');
33
+ if (!ref)
34
+ return;
35
+ await (0, commandContext_1.withEnvironment)('notes browse', 'Reading playtest notes', async ({ client }) => {
36
+ try {
37
+ const response = await client.listPlaytestNotes(ref.creatorUsername, ref.name, ref.version);
38
+ let output = response;
39
+ if (options.output) {
40
+ const outputDirectory = (0, node_path_1.resolve)(options.output);
41
+ (0, node_fs_1.mkdirSync)(outputDirectory, { recursive: true });
42
+ const notes = [];
43
+ for (const note of response.notes) {
44
+ if (!note.attachment) {
45
+ notes.push(note);
46
+ continue;
47
+ }
48
+ const localPath = (0, node_path_1.resolve)(outputDirectory, `${note.id}-${note.attachment.fileName}`);
49
+ const blob = await client.downloadPlaytestNoteAttachment(ref.creatorUsername, ref.name, ref.version, note.id);
50
+ (0, node_fs_1.writeFileSync)(localPath, Buffer.from(await blob.arrayBuffer()));
51
+ notes.push({ ...note, localPath });
52
+ }
53
+ output = { notes };
54
+ (0, node_fs_1.writeFileSync)((0, node_path_1.resolve)(outputDirectory, 'notes.json'), `${JSON.stringify(output, null, 2)}\n`, 'utf8');
55
+ }
56
+ if (options.json) {
57
+ (0, output_1.printJson)(output);
58
+ return;
59
+ }
60
+ if (options.output) {
61
+ (0, output_1.printSuccess)(`Materialized ${output.notes.length} playtest notes in ${(0, node_path_1.resolve)(options.output)}.`);
62
+ return;
63
+ }
64
+ if (output.notes.length === 0) {
65
+ console.log('No pending playtest notes.');
66
+ return;
67
+ }
68
+ for (const note of output.notes) {
69
+ const summary = note.kind === 'TEXT'
70
+ ? note.text
71
+ : note.kind === 'ASSET'
72
+ ? `${note.assetRef}: ${note.comment}`
73
+ : note.attachment?.fileName;
74
+ console.log(`${note.id}\t${note.kind}\tv${note.authoredVersion}\t${summary ?? ''}`);
75
+ }
76
+ }
77
+ catch (error) {
78
+ if (!(0, errors_1.handleCommandFailure)(error, 'notes browse', 'Read playtest notes', { apiMessage: notesApiMessage }))
79
+ throw error;
80
+ }
81
+ });
82
+ }
83
+ async function clearNotes(rawRef, rawIds, options = {}) {
84
+ const ref = parseAppVersionRef(rawRef, 'notes clear');
85
+ if (!ref)
86
+ return;
87
+ const ids = rawIds.map(Number);
88
+ if (ids.length === 0 || ids.some((id) => !Number.isInteger(id) || id <= 0) || new Set(ids).size !== ids.length) {
89
+ (0, messages_1.printErrorWithHelp)('One or more explicit note IDs are required.', ['Example: playdrop notes clear app:olivier/platformer@1.4.0 42 43'], { command: 'notes clear' });
90
+ process.exitCode = 1;
91
+ return;
92
+ }
93
+ await (0, commandContext_1.withEnvironment)('notes clear', 'Clearing playtest notes', async ({ client }) => {
94
+ try {
95
+ const result = await client.clearPlaytestNotes(ref.creatorUsername, ref.name, ref.version, ids);
96
+ if (options.json) {
97
+ (0, output_1.printJson)(result);
98
+ return;
99
+ }
100
+ (0, output_1.printSuccess)(`Cleared ${result.cleared.length} playtest notes. ${result.count} pending.`);
101
+ }
102
+ catch (error) {
103
+ if (!(0, errors_1.handleCommandFailure)(error, 'notes clear', 'Clear playtest notes', { apiMessage: notesApiMessage }))
104
+ throw error;
105
+ }
106
+ });
107
+ }
@@ -0,0 +1,6 @@
1
+ type TweaksOptions = {
2
+ json?: boolean;
3
+ };
4
+ export declare function getTweaks(rawRef: string | undefined, options?: TweaksOptions): Promise<void>;
5
+ export declare function replaceTweaks(rawRef: string | undefined, file: string | undefined, options?: TweaksOptions): Promise<void>;
6
+ export {};
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTweaks = getTweaks;
4
+ exports.replaceTweaks = replaceTweaks;
5
+ const node_fs_1 = require("node:fs");
6
+ const types_1 = require("@playdrop/types");
7
+ const commandContext_1 = require("../commandContext");
8
+ const errors_1 = require("../errors");
9
+ const messages_1 = require("../messages");
10
+ const output_1 = require("../output");
11
+ function tweakApiMessage(error) {
12
+ if (error.code === 'creator_latest_private_version_required') {
13
+ const latestVersion = typeof error.details?.['latestVersion'] === 'string' ? error.details['latestVersion'] : null;
14
+ const latestId = typeof error.details?.['latestId'] === 'string' ? error.details['latestId'] : null;
15
+ const latestVisibility = typeof error.details?.['latestVisibility'] === 'string' ? error.details['latestVisibility'] : null;
16
+ return {
17
+ problem: latestVersion && latestVisibility === 'PUBLIC'
18
+ ? `The latest tweak base, ${latestVersion}, is live and cannot be edited.`
19
+ : latestVersion
20
+ ? `Tweaks can only be changed on the latest editable version, ${latestVersion}.`
21
+ : 'This app has no editable tweak base version.',
22
+ suggestions: latestVersion && latestVisibility === 'PUBLIC'
23
+ ? [`Create a new private version based on ${latestId ?? 'the latest tweak state'}, then tune that version.`]
24
+ : latestId ? [`Latest tweak state: ${latestId}`] : [],
25
+ };
26
+ }
27
+ return { problem: `PlayDrop rejected the tweak request: ${error.code ?? error.message} (HTTP ${error.status}).` };
28
+ }
29
+ function parseAppVersionRef(raw, command) {
30
+ const ref = (0, types_1.parseContentVersionRef)(raw ?? '');
31
+ if (!ref || ref.kind !== 'app') {
32
+ (0, messages_1.printErrorWithHelp)('An exact canonical app-version ref is required.', ['Use app:creator/name@x.y.z.', `Example: playdrop ${command} app:olivier/platformer@1.4.0`], { command });
33
+ process.exitCode = 1;
34
+ return null;
35
+ }
36
+ return ref;
37
+ }
38
+ async function getTweaks(rawRef, options = {}) {
39
+ const ref = parseAppVersionRef(rawRef, 'tweaks get');
40
+ if (!ref)
41
+ return;
42
+ await (0, commandContext_1.withEnvironment)('tweaks get', 'Reading tweaks', async ({ client }) => {
43
+ try {
44
+ const snapshot = await client.getAppTweaks(ref.creatorUsername, ref.name, ref.version);
45
+ if (options.json) {
46
+ (0, output_1.printJson)(snapshot);
47
+ return;
48
+ }
49
+ console.log(`Tweak state: ${snapshot.id}`);
50
+ console.log(JSON.stringify(snapshot.values, null, 2));
51
+ }
52
+ catch (error) {
53
+ if (!(0, errors_1.handleCommandFailure)(error, 'tweaks get', 'Read tweaks', { apiMessage: tweakApiMessage }))
54
+ throw error;
55
+ }
56
+ });
57
+ }
58
+ async function replaceTweaks(rawRef, file, options = {}) {
59
+ const ref = parseAppVersionRef(rawRef, 'tweaks replace');
60
+ if (!ref)
61
+ return;
62
+ if (!file?.trim()) {
63
+ (0, messages_1.printErrorWithHelp)('A JSON file is required.', ['Pass --file <path>.'], { command: 'tweaks replace' });
64
+ process.exitCode = 1;
65
+ return;
66
+ }
67
+ let values;
68
+ try {
69
+ const parsed = JSON.parse((0, node_fs_1.readFileSync)(file, 'utf8'));
70
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
71
+ throw new Error('not_object');
72
+ values = parsed;
73
+ }
74
+ catch {
75
+ (0, messages_1.printErrorWithHelp)(`Could not read a JSON object from ${file}.`, [], { command: 'tweaks replace' });
76
+ process.exitCode = 1;
77
+ return;
78
+ }
79
+ await (0, commandContext_1.withEnvironment)('tweaks replace', 'Replacing tweaks', async ({ client }) => {
80
+ try {
81
+ const snapshot = await client.replaceAppTweaks(ref.creatorUsername, ref.name, ref.version, values);
82
+ if (options.json) {
83
+ (0, output_1.printJson)(snapshot);
84
+ return;
85
+ }
86
+ (0, output_1.printSuccess)(`Tweaks replaced. New state: ${snapshot.id}`);
87
+ }
88
+ catch (error) {
89
+ if (!(0, errors_1.handleCommandFailure)(error, 'tweaks replace', 'Replace tweaks', { apiMessage: tweakApiMessage }))
90
+ throw error;
91
+ }
92
+ });
93
+ }
@@ -9,6 +9,7 @@ const externalAssetPackValidation_1 = require("../externalAssetPackValidation");
9
9
  const taskSelection_1 = require("../taskSelection");
10
10
  const taskUtils_1 = require("../taskUtils");
11
11
  const uploadLog_1 = require("../uploadLog");
12
+ const tweakStaleness_1 = require("../tweakStaleness");
12
13
  const listingPreflight_1 = require("../listingPreflight");
13
14
  const upload_content_1 = require("./upload-content");
14
15
  const upload_1 = require("./upload");
@@ -189,11 +190,24 @@ async function validate(pathOrName, options = {}) {
189
190
  const assetSpecTasks = tasks.filter((task) => task.kind === 'asset-spec');
190
191
  const assetSpecLookups = buildLocalAssetSpecLookups(assetSpecTasks);
191
192
  const sortedTasks = (0, taskUtils_1.sortTasks)(tasks);
193
+ const tweakValidationContext = await loadAuthenticatedValidationContext(workspacePath, options.env);
192
194
  for (const task of sortedTasks) {
193
195
  const entityId = task.name;
194
196
  try {
195
197
  if (task.kind === 'app') {
196
198
  const workerContext = (0, listingPreflight_1.readWorkerUploadPreflightContext)(task.projectDir);
199
+ if (tweakValidationContext) {
200
+ const creatorResult = (0, upload_content_1.getTaskCreatorResult)(task, tweakValidationContext.username, tweakValidationContext.currentUserRole);
201
+ if (creatorResult.creatorTargetError)
202
+ throw new Error(creatorResult.creatorTargetError);
203
+ await (0, tweakStaleness_1.fetchAndAssertTweakState)(tweakValidationContext.client, creatorResult.taskCreator, task, { allowAppNotFound: true });
204
+ }
205
+ else if (workerContext?.tweaks) {
206
+ (0, tweakStaleness_1.assertTweakStateMatches)(task, workerContext.tweaks);
207
+ }
208
+ else if (task.tweaks !== undefined) {
209
+ warnings.add(`App "${task.name}" tweak basedOn was validated locally; remote staleness was not checked.`);
210
+ }
197
211
  if (workerContext) {
198
212
  await (0, upload_1.assertWorkerAppLocalUploadPreflight)({
199
213
  task,
package/dist/index.js CHANGED
@@ -44,6 +44,8 @@ const feedback_1 = require("./commands/feedback");
44
44
  const changelog_1 = require("./commands/changelog");
45
45
  const publicPages_1 = require("./commands/publicPages");
46
46
  const upgrade_1 = require("./commands/upgrade");
47
+ const tweaks_1 = require("./commands/tweaks");
48
+ const notes_1 = require("./commands/notes");
47
49
  const worker_1 = require("./commands/worker");
48
50
  const messages_1 = require("./messages");
49
51
  // Restricted shim: inside a worker task workspace only task-scoped commands,
@@ -577,6 +579,43 @@ versions
577
579
  .action(async (ref, opts) => {
578
580
  await (0, versionsBrowse_1.browseVersions)(ref, opts);
579
581
  });
582
+ const tweaks = program.command('tweaks').description('Read and replace version-scoped game tweaks');
583
+ tweaks
584
+ .command('get')
585
+ .description('Read the current tweak state for an exact app version')
586
+ .argument('<ref>', 'Exact ref: app:creator/name@x.y.z')
587
+ .option('--json', 'Output JSON')
588
+ .action(async (ref, options) => {
589
+ await (0, tweaks_1.getTweaks)(ref, options);
590
+ });
591
+ tweaks
592
+ .command('replace')
593
+ .description('Replace tweaks on the latest private app version')
594
+ .argument('<ref>', 'Exact ref: app:creator/name@x.y.z')
595
+ .requiredOption('--file <path>', 'JSON object containing the complete tweak values')
596
+ .option('--json', 'Output JSON')
597
+ .action(async (ref, options) => {
598
+ await (0, tweaks_1.replaceTweaks)(ref, options.file, options);
599
+ });
600
+ const notes = program.command('notes').description('Read and clear pending creator playtest notes');
601
+ notes
602
+ .command('browse')
603
+ .description('Browse pending playtest notes for an app')
604
+ .argument('<ref>', 'Exact ref: app:creator/name@x.y.z')
605
+ .option('--json', 'Output JSON')
606
+ .option('--output <directory>', 'Materialize attachments and a notes.json manifest')
607
+ .action(async (ref, options) => {
608
+ await (0, notes_1.browseNotes)(ref, options);
609
+ });
610
+ notes
611
+ .command('clear')
612
+ .description('Clear explicit note IDs after incorporating them')
613
+ .argument('<ref>', 'Exact ref: app:creator/name@x.y.z')
614
+ .argument('<ids...>', 'One or more note IDs')
615
+ .option('--json', 'Output JSON')
616
+ .action(async (ref, ids, options) => {
617
+ await (0, notes_1.clearNotes)(ref, ids, options);
618
+ });
580
619
  const credits = program.command('credits').description('Credit balance and history');
581
620
  credits
582
621
  .command('balance')
@@ -9,6 +9,10 @@ export type WorkerUploadPreflightContext = {
9
9
  target: 'FIRST_PARTY' | 'PERSONAL';
10
10
  creatorUsername: string;
11
11
  creatorRequest: string;
12
+ tweaks?: {
13
+ id: string;
14
+ values: Record<string, string | number | boolean>;
15
+ };
12
16
  };
13
17
  export declare function readMediaCaptureProof(input: {
14
18
  task: AppTask;
@@ -303,6 +303,15 @@ function readWorkerUploadPreflightContext(projectDir) {
303
303
  target,
304
304
  creatorUsername: typeof playdrop.creatorUsername === 'string' ? playdrop.creatorUsername.trim() : '',
305
305
  creatorRequest: typeof request.prompt === 'string' ? request.prompt.trim() : '',
306
+ ...(playdrop.tweaks
307
+ && typeof playdrop.tweaks === 'object'
308
+ && !Array.isArray(playdrop.tweaks)
309
+ && typeof playdrop.tweaks.id === 'string'
310
+ && playdrop.tweaks.values
311
+ && typeof playdrop.tweaks.values === 'object'
312
+ && !Array.isArray(playdrop.tweaks.values)
313
+ ? { tweaks: { id: playdrop.tweaks.id, values: playdrop.tweaks.values } }
314
+ : {}),
306
315
  };
307
316
  }
308
317
  if (parsed?.protocolVersion !== undefined) {
@@ -319,6 +328,14 @@ function readWorkerUploadPreflightContext(projectDir) {
319
328
  target: parsed.target,
320
329
  creatorUsername: typeof parsed.creatorUsername === 'string' ? parsed.creatorUsername.trim() : '',
321
330
  creatorRequest: typeof parsed.creatorRequest === 'string' ? parsed.creatorRequest.trim() : '',
331
+ ...(parsed.tweaks
332
+ && typeof parsed.tweaks === 'object'
333
+ && typeof parsed.tweaks.id === 'string'
334
+ && parsed.tweaks.values
335
+ && typeof parsed.tweaks.values === 'object'
336
+ && !Array.isArray(parsed.tweaks.values)
337
+ ? { tweaks: parsed.tweaks }
338
+ : {}),
322
339
  };
323
340
  }
324
341
  function assertAgentGameHeroListingPreflight(task) {
@@ -0,0 +1,7 @@
1
+ import { type AppTweaksSnapshot } from '@playdrop/types';
2
+ import type { ApiClient } from '@playdrop/api-client';
3
+ import type { AppTask } from './catalogue';
4
+ export declare function assertTweakStateMatches(task: AppTask, latest: AppTweaksSnapshot | null): void;
5
+ export declare function fetchAndAssertTweakState(client: ApiClient, creatorUsername: string, task: AppTask, options?: {
6
+ allowAppNotFound?: boolean;
7
+ }): Promise<void>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertTweakStateMatches = assertTweakStateMatches;
4
+ exports.fetchAndAssertTweakState = fetchAndAssertTweakState;
5
+ const types_1 = require("@playdrop/types");
6
+ function assertTweakStateMatches(task, latest) {
7
+ const basedOn = task.tweaks?.basedOn ?? null;
8
+ const latestId = latest?.id ?? null;
9
+ if (task.tweaks === undefined && latestId === null)
10
+ return;
11
+ if (task.tweaks !== undefined && basedOn === latestId)
12
+ return;
13
+ throw new Error([
14
+ 'Tweak definition is out of date.',
15
+ '',
16
+ `Code is based on: ${basedOn ?? 'none'}`,
17
+ `Latest state is: ${latestId ?? 'none'}`,
18
+ '',
19
+ 'Latest values:',
20
+ JSON.stringify(latest?.values ?? null, null, 2),
21
+ ].join('\n'));
22
+ }
23
+ async function fetchAndAssertTweakState(client, creatorUsername, task, options = {}) {
24
+ let latest = null;
25
+ try {
26
+ latest = await client.getLatestAppTweaks(creatorUsername, task.name);
27
+ }
28
+ catch (error) {
29
+ if (!(error instanceof types_1.ApiError))
30
+ throw error;
31
+ if (error.code !== 'tweaks_not_declared' && !(options.allowAppNotFound && error.code === 'app_not_found')) {
32
+ throw error;
33
+ }
34
+ }
35
+ assertTweakStateMatches(task, latest);
36
+ }
@@ -1,4 +1,4 @@
1
- import type { AcquisitionAttributionRequest, AcquisitionAttributionResponse, AbortAppUploadResponse, AbortAssetPackUploadResponse, AdInterstitialShowResponse, AdLoadRequest, AdLoadResponse, AdRewardedShowResponse, AdShowRequest, AdminAgentTaskDetailResponse, AdminAgentTaskTranscriptResponse, AdminAgentTasksResponse, AdminAgentWorkersStatusResponse, AdminAppPlayerMetaResponse, AdminAppPlayerMetaUserResponse, AdminAppVersionReviewsListResponse, AdminAppsListResponse, AdminCreatorEngagementExecutionsResponse, AdminCreatorEngagementRulePreviewRequest, AdminCreatorEngagementRulePreviewResponse, AdminCreatorEngagementRulesResponse, AdminCreatorEngagementUserResponse, AdminCreditTransactionsResponse, AdminEnqueueNextAppVersionReviewTaskResponse, AdminFeedbackListResponse, AdminFeedbackResponse, AdminGameCollectionCandidatesResponse, AdminGameCollectionResponse, AdminGameCollectionsResponse, AdminMutateAchievementStateRequest, AdminMutateAchievementStateResponse, AdminMutateLeaderboardScoreRequest, AdminMutateLeaderboardScoreResponse, AdminPaymentDetailResponse, AdminPaymentsListResponse, AdminPlatformAnalyticsRequest, AdminPlatformAnalyticsResponse, AdminRefundPaymentRequest, AdminRetirePlayerMetaDefinitionResponse, AdminTagGroupResponse, AdminTagGroupsResponse, AdminTagResponse, AdminUpdateAppRequest, AdminUpdateAppResponse, AdminUpdateAppVersionReviewRequest, AdminUpdateAppVersionReviewResponse, AdminUserResponse, AgentTaskAttachmentUploadResponse, AgentTaskAvailabilityResponse, AgentTaskCreatorFeedbackStatus, AgentTaskDetailResponse, AgentTaskEventsResponse, AgentTaskKind, AgentTaskNewGameSuggestionsResponse, AgentTasksResponse, AiGenerationDetailResponse, AiGenerationsResponse, ApiKeyLoginRequest, AppAccessTokenResponse, AppAgentTaskResponse, AppBrowseRequest, AppDetailRequest, AppDetailResponse, AppLogEntryRequest, AppLogEntryResponse, AppMoreCollectionKey, AppMoreCollectionPage, AppMoreCollectionRequest, AppMoreCollectionsRequest, AppMoreCollectionsResponse, AppRuntimeAssetsResponse, AppAuthMode, AppSourceBundleResponse, AppType, AppUploadLaunchCheckPreviewResponse, AppVersionDetailResponse, AppVersionVisibility, AppVersionsListResponse, AppleCreditPackFulfillmentRequest, AppleLinkRequest, AppleLinkResponse, AppleNativeChallengeResponse, AppleOAuthHandoffExchangeRequest, AppleOAuthHandoffExchangeResponse, AppleOAuthLinkStartRequest, AppleOAuthLinkStartResponse, ApplePendingSignupResponse, ApplePushDeviceRequest, ApplePushDeviceStatusResponse, AppleSignInRequest, AppleSignInResponse, AppsListResponse, AssetBrowseKind, AssetCategoriesResponse, AssetCategory, AssetDetailResponse, AssetFormat, AssetListResponse, AssetListSort, AssetPackDetailResponse, AssetPackListResponse, AssetPackListSort, AssetPackSourceBundleResponse, AssetPackVersionsListResponse, AssetPurchaseRequest, AssetSourceBundleResponse, AssetSourceKind, AssetSpecAppsListResponse, AssetSpecAssetsListResponse, AssetSpecDetailResponse, AssetSpecListResponse, AssetSpecListSort, AssetSpecVersionResponseEnvelope, AssetSpecVersionsListResponse, AssetVersionsListResponse, AssetVisibility, BatchCreateUsersResponse, BatchUpsertNodeRelationsRequest, BatchUpsertNodeRelationsResponse, BoostActivationRequest, BoostBalanceResponse, BoostHistoryResponse, BoostPurchaseRequest, BoostPurchaseResponse, BoostReportQuery, BoostReportResponse, BoostStatusResponse, BootstrapResponse, CancelAgentTaskResponse, ChangePasswordRequest, ChangePasswordResponse, ClaimFreeCreditRewardResponse, ClearAgentTaskResponse, CliApiKeyStatusResponse, CliApiTokenSecretResponse, CliApiTokensResponse, CliLoginApproveRequest, CliLoginApproveResponse, CliLoginPollRequest, CliLoginPollResponse, CliLoginStartResponse, CliLoginStatusResponse, CliWebLaunchStartRequest, CliWebLaunchStartResponse, CompleteAppleSignupRequest, CompleteAppleSignupResponse, CompleteGoogleSignupRequest, CompleteGoogleSignupResponse, CompleteXSignupRequest, CompleteXSignupResponse, NativeOAuthHandoffExchangeRequest, NativeOAuthHandoffExchangeResponse, NativeOAuthLinkStartRequest, NativeOAuthLinkStartResponse, ContentCommentsResponse, ContentLicense, ContentRemixRelationsResponse, CreateAdminGameCollectionRequest, CreateAppAgentTaskRequest, CreateAppAgentTaskResponse, CreateAppRequest, CreateAppResponse, CreateAssetSpecVersionResponse, CreateAssetVersionResponse, CreateCliApiKeyResponse, CreateCliApiTokenRequest, CreateCommentReportRequest, CreateContentCommentRequest, CreateContentCommentResponse, CreateContentReportRequest, CreateCreatorReportRequest, CreateGameEvalAgentTaskRequest, CreateGameEvalAgentTaskResponse, CreateNewGameAgentTaskRequest, CreateNewGameAgentTaskResponse, CreateStaticGameAgentTaskRequest, CreateStaticGameAgentTaskResponse, CreateRemixGameAgentTaskRequest, CreateRemixGameAgentTaskResponse, CreateTagGroupRequest, CreateTagRequest, CreateUserRequest, CreateUserResponse, CreatorAppsBrowseRequest, CreatorBlockMutationResponse, CreatorEarningsResponse, CreatorEngagementRuleExecutionStatus, CreatorFollowMutationResponse, CreatorGameCreatorFeedbackStatusResponse, CreatorGameShareResponse, CreatorGameStatusResponse, CreatorGameVersionsResponse, CreatorGamesListResponse, CreationConversationResponse, CreditPackCheckoutSessionRequest, CreditPackCheckoutSessionResponse, CreditPackCheckoutStatusResponse, CreditPackResponse, CreditPacksResponse, DeleteAdminGameCollectionResponse, DeleteAdminUserResponse, DeleteAppResponse, DeleteAssetPackResponse, DeleteAssetPackVersionResponse, DeleteAssetResponse, DeleteAssetSpecResponse, DeleteAssetSpecVersionResponse, DeleteAssetVersionResponse, DeleteCliApiTokenResponse, DeleteContentCommentResponse, DeleteFeedbackResponse, DeleteMyAccountRequest, DeleteMyAccountResponse, DeleteOwnedCustomAssetResponse, DeleteVersionResponse, DevAppAccessTokenResponse, DevPlayerResetResponse, DislikedItemMutationResponse, EmailPreferencesResponse, EmailUnsubscribeResponse, FeedbackCategory, FeedbackClient, FeedbackListResponse, FeedbackStatus, FetchHomeOptions, FinalizeAppUploadResponse, FinalizeAssetPackUploadResponse, FirebasePushDeviceRequest, FirebasePushDeviceStatusResponse, FollowingCreatorsResponse, FollowingFeedResponse, FollowingFeedSection, FollowingSort, FreeCreditsResponse, GetAchievementResponse, GetLeaderboardResponse, GoogleAppleLinkRequest, GoogleAppleLinkResponse, GoogleAppleSignInRequest, GoogleAppleSignInResponse, GoogleCreditPackFulfillmentRequest, GoogleOneTapExchangeRequest, GoogleOneTapExchangeResponse, GooglePendingSignupResponse, HomeCollectionPageResponse, HomeResponse, IapPurchaseRequest, IapPurchaseResponse, IapReceiptListParams, IapReceiptResponse, IapReceiptsResponse, InitializeAppUploadRequest, InitializeAppUploadResponse, InitializeAssetPackUploadRequest, InitializeAssetPackUploadResponse, InvitePreviewResponse, LibraryCategory, LibraryResponse, LibrarySort, LikedItemMutationResponse, ListAchievementsResponse, ListLeaderboardsResponse, ListOwnedCustomAssetsResponse, LoadOwnedCustomAssetResponse, LoginRequest, LoginResponse, LogoutResponse, MeAgentWorkersResponse, MeResponse, MobileCreditPackFulfillmentResponse, ModerationReportResponse, MutateOwnedCustomAssetRequest, MutateOwnedCustomAssetResponse, MyAchievementsResponse, MyAppsResponse, MyInviteCodeResponse, MyLeaderboardsResponse, NotificationReadResponse, NotificationStatus, NotificationsResponse, OwnedProfileAssetsResponse, PaymentProvider, PaymentStatus, PrivacyExportResponse, PublicEmailPreferencesResponse, PublicEmailPreferencesUpdateRequest, PurchaseResponse, PushSubscriptionMutationResponse, RecordAppUploadLaunchCheckRequest, RecordAppUploadLaunchCheckResponse, RecordAppUploadCausalCheckRequest, RecordAppUploadCausalCheckResponse, RecordAppUploadMediaCaptureRequest, RecordAppUploadMediaCaptureResponse, RegisterRequest, RegisterResponse, RemixScaffoldResponse, ResolveRuntimeAssetsRequest, ResolveRuntimeAssetsResponse, RetryAgentTaskResponse, ReviewState, SavedItemKind, SavedItemMutationResponse, SearchRequest, SearchResponse, SearchSuggestRequest, SearchSuggestResponse, SetAchievementProgressRequest, SetAchievementProgressResponse, SetProfileAssetRequest, SetProfileAssetResponse, ShopResponse, StarterProfileAssetsResponse, SubmitFeedbackRequest, SubmitFeedbackResponse, SubmitLeaderboardScoreRequest, SubmitLeaderboardScoreResponse, TagDetailResponse, TagDirectoryResponse, TagGroupDetailResponse, TagGroupSort, TagListResponse, TemplateScaffoldResponse, TrackEngagementEventRequest, TrackEngagementEventResponse, TransactionsResponse, UnlinkGoogleResponse, UnlinkXResponse, UnlockAchievementResponse, UpdateAdminFeedbackRequest, UpdateAdminFeedbackResponse, UpdateAdminGameCollectionGamesRequest, UpdateAdminGameCollectionRequest, UpdateAdminUserRequest, UpdateAppRequest, UpdateAppResponse, UpdateAssetPackRequest, UpdateAssetPackResponse, UpdateAssetPackVersionRequest, UpdateAssetPackVersionResponse, UpdateAssetRequest, UpdateAssetResponse, UpdateAssetSpecVersionResponse, UpdateAssetVersionRequest, UpdateAssetVersionResponse, UpdateProfileRequest, UpdateProfileResponse, UpdateTagGroupRequest, UpdateTagRequest, UpdateUserCommunicationPreferencesRequest, UpdateVersionResponse, UploadAppSessionFileResponse, UploadAppSessionOwnedAssetResponse, UploadAssetPackSessionAssetResponse, UploadAssetPackSessionMediaResponse, UpsertWebPushSubscriptionRequest, UserDetailResponse, UsernameAvailableResponse, UsersListResponse, WebPushConfigResponse, WebPushSubscriptionStatusResponse, WorkerAgentTaskAttemptUnavailableRequest, WorkerAgentTaskAttemptUnavailableResponse, WorkerClaimAgentTaskRequest, WorkerClaimAgentTaskResponse, WorkerClaimAgentTaskSlugRequest, WorkerClaimAgentTaskSlugResponse, WorkerCompleteAgentTaskRequest, WorkerCreateAgentTaskEventRequest, WorkerCreateAgentTaskEventResponse, WorkerCreateAgentTaskTranscriptChunkRequest, WorkerCreateAgentTaskTranscriptChunkResponse, WorkerFailAgentTaskRequest, WorkerHeartbeatAgentTaskRequest, WorkerHeartbeatAgentTaskResponse, WorkerPresenceRequest, WorkerPresenceResponse, RegisterAgentBundleRequest, RegisterAgentBundleResponse, WorkerRecordAgentTaskRunTelemetryRequest, WorkerRecordAgentTaskRunTelemetryResponse, WorkerSubmitAgentTaskEvalRequest, WorkerSubmitAgentTaskEvalResponse, WorkerSubmitAgentTaskReviewRequest, WorkerSubmitAgentTaskReviewResponse, WorkerAgentTaskMaterialResponse, WorkerUpdateIntentRequest, WorkerUpdateIntentResponse, XPendingSignupResponse } from '@playdrop/types';
1
+ import type { AcquisitionAttributionRequest, AcquisitionAttributionResponse, AbortAppUploadResponse, AbortAssetPackUploadResponse, AdInterstitialShowResponse, AdLoadRequest, AdLoadResponse, AdRewardedShowResponse, AdShowRequest, AdminAgentTaskDetailResponse, AdminAgentTaskTranscriptResponse, AdminAgentTasksResponse, AdminAgentWorkersStatusResponse, AdminAppPlayerMetaResponse, AdminAppPlayerMetaUserResponse, AdminAppVersionReviewsListResponse, AdminAppsListResponse, AdminCreatorEngagementExecutionsResponse, AdminCreatorEngagementRulePreviewRequest, AdminCreatorEngagementRulePreviewResponse, AdminCreatorEngagementRulesResponse, AdminCreatorEngagementUserResponse, AdminCreditTransactionsResponse, AdminEnqueueNextAppVersionReviewTaskResponse, AdminFeedbackListResponse, AdminFeedbackResponse, AdminGameCollectionCandidatesResponse, AdminGameCollectionResponse, AdminGameCollectionsResponse, AdminMutateAchievementStateRequest, AdminMutateAchievementStateResponse, AdminMutateLeaderboardScoreRequest, AdminMutateLeaderboardScoreResponse, AdminPaymentDetailResponse, AdminPaymentsListResponse, AdminPlatformAnalyticsRequest, AdminPlatformAnalyticsResponse, AdminTractionAnalyticsRequest, AdminRefundPaymentRequest, AdminRetirePlayerMetaDefinitionResponse, AdminTagGroupResponse, AdminTagGroupsResponse, AdminTagResponse, AdminUpdateAppRequest, AdminUpdateAppResponse, AdminUpdateAppVersionReviewRequest, AdminUpdateAppVersionReviewResponse, AdminUserResponse, AgentTaskAttachmentUploadResponse, AgentTaskAvailabilityResponse, AgentTaskCreatorFeedbackStatus, AgentTaskDetailResponse, AgentTaskEventsResponse, AgentTaskKind, AgentTaskNewGameSuggestionsResponse, AgentTasksResponse, AiGenerationDetailResponse, AiGenerationsResponse, ApiKeyLoginRequest, AppAccessTokenResponse, AppAgentTaskResponse, AppBrowseRequest, AppDetailRequest, AppDetailResponse, AppLogEntryRequest, AppLogEntryResponse, AppMoreCollectionKey, AppMoreCollectionPage, AppMoreCollectionRequest, AppMoreCollectionsRequest, AppMoreCollectionsResponse, AppRuntimeAssetsResponse, AppAuthMode, AppSourceBundleResponse, AppType, AppUploadLaunchCheckPreviewResponse, AppVersionDetailResponse, AppVersionVisibility, AppTweaksSnapshot, CreatorNotesClearResponse, CreatorNotesResponse, TweakValues, AppVersionsListResponse, AppleCreditPackFulfillmentRequest, AppleLinkRequest, AppleLinkResponse, AppleNativeChallengeResponse, AppleOAuthHandoffExchangeRequest, AppleOAuthHandoffExchangeResponse, AppleOAuthLinkStartRequest, AppleOAuthLinkStartResponse, ApplePendingSignupResponse, ApplePushDeviceRequest, ApplePushDeviceStatusResponse, AppleSignInRequest, AppleSignInResponse, AppsListResponse, AssetBrowseKind, AssetCategoriesResponse, AssetCategory, AssetDetailResponse, AssetFormat, AssetListResponse, AssetListSort, AssetPackDetailResponse, AssetPackListResponse, AssetPackListSort, AssetPackSourceBundleResponse, AssetPackVersionsListResponse, AssetPurchaseRequest, AssetSourceBundleResponse, AssetSourceKind, AssetSpecAppsListResponse, AssetSpecAssetsListResponse, AssetSpecDetailResponse, AssetSpecListResponse, AssetSpecListSort, AssetSpecVersionResponseEnvelope, AssetSpecVersionsListResponse, AssetVersionsListResponse, AssetVisibility, BatchCreateUsersResponse, BatchUpsertNodeRelationsRequest, BatchUpsertNodeRelationsResponse, BoostActivationRequest, BoostBalanceResponse, BoostHistoryResponse, BoostPurchaseRequest, BoostPurchaseResponse, BoostReportQuery, BoostReportResponse, BoostStatusResponse, BootstrapResponse, CancelAgentTaskResponse, ChangePasswordRequest, ChangePasswordResponse, ClaimFreeCreditRewardResponse, ClearAgentTaskResponse, CliApiKeyStatusResponse, CliApiTokenSecretResponse, CliApiTokensResponse, CliLoginApproveRequest, CliLoginApproveResponse, CliLoginPollRequest, CliLoginPollResponse, CliLoginStartResponse, CliLoginStatusResponse, CliWebLaunchStartRequest, CliWebLaunchStartResponse, CompleteAppleSignupRequest, CompleteAppleSignupResponse, CompleteGoogleSignupRequest, CompleteGoogleSignupResponse, CompleteXSignupRequest, CompleteXSignupResponse, NativeOAuthHandoffExchangeRequest, NativeOAuthHandoffExchangeResponse, NativeOAuthLinkStartRequest, NativeOAuthLinkStartResponse, ContentCommentsResponse, ContentLicense, ContentRemixRelationsResponse, CreateAdminGameCollectionRequest, CreateAppAgentTaskRequest, CreateAppAgentTaskResponse, CreateAppRequest, CreateAppResponse, CreateAssetSpecVersionResponse, CreateAssetVersionResponse, CreateCliApiKeyResponse, CreateCliApiTokenRequest, CreateCommentReportRequest, CreateContentCommentRequest, CreateContentCommentResponse, CreateContentReportRequest, CreateCreatorReportRequest, CreateGameEvalAgentTaskRequest, CreateGameEvalAgentTaskResponse, CreateNewGameAgentTaskRequest, CreateNewGameAgentTaskResponse, CreateStaticGameAgentTaskRequest, CreateStaticGameAgentTaskResponse, CreateRemixGameAgentTaskRequest, CreateRemixGameAgentTaskResponse, CreateTagGroupRequest, CreateTagRequest, CreateUserRequest, CreateUserResponse, CreatorAppsBrowseRequest, CreatorBlockMutationResponse, CreatorEarningsResponse, CreatorEngagementRuleExecutionStatus, CreatorFollowMutationResponse, CreatorGameCreatorFeedbackStatusResponse, CreatorGameShareResponse, CreatorGameStatusResponse, CreatorGameVersionsResponse, CreatorGamesListResponse, CreationConversationResponse, CreditPackCheckoutSessionRequest, CreditPackCheckoutSessionResponse, CreditPackCheckoutStatusResponse, CreditPackResponse, CreditPacksResponse, DeleteAdminGameCollectionResponse, DeleteAdminUserResponse, DeleteAppResponse, DeleteAssetPackResponse, DeleteAssetPackVersionResponse, DeleteAssetResponse, DeleteAssetSpecResponse, DeleteAssetSpecVersionResponse, DeleteAssetVersionResponse, DeleteCliApiTokenResponse, DeleteContentCommentResponse, DeleteFeedbackResponse, DeleteMyAccountRequest, DeleteMyAccountResponse, DeleteOwnedCustomAssetResponse, DeleteVersionResponse, DevAppAccessTokenResponse, DevPlayerResetResponse, DislikedItemMutationResponse, EmailPreferencesResponse, EmailUnsubscribeResponse, FeedbackCategory, FeedbackClient, FeedbackListResponse, FeedbackStatus, FetchHomeOptions, FinalizeAppUploadResponse, FinalizeAppUploadRequest, FinalizeAssetPackUploadResponse, FirebasePushDeviceRequest, FirebasePushDeviceStatusResponse, GameplaySessionResponse, GameplaySessionActivityRequest, GameplaySessionStartRequest, FollowingCreatorsResponse, FollowingFeedResponse, FollowingFeedSection, FollowingSort, FreeCreditsResponse, GetAchievementResponse, GetLeaderboardResponse, GoogleAppleLinkRequest, GoogleAppleLinkResponse, GoogleAppleSignInRequest, GoogleAppleSignInResponse, GoogleCreditPackFulfillmentRequest, GoogleOneTapExchangeRequest, GoogleOneTapExchangeResponse, GooglePendingSignupResponse, HomeCollectionPageResponse, HomeResponse, IapPurchaseRequest, IapPurchaseResponse, IapReceiptListParams, IapReceiptResponse, IapReceiptsResponse, InitializeAppUploadRequest, InitializeAppUploadResponse, InitializeAssetPackUploadRequest, InitializeAssetPackUploadResponse, InvitePreviewResponse, LibraryCategory, LibraryResponse, LibrarySort, LikedItemMutationResponse, ListAchievementsResponse, ListLeaderboardsResponse, ListOwnedCustomAssetsResponse, LoadOwnedCustomAssetResponse, LoginRequest, LoginResponse, LogoutResponse, MeAgentWorkersResponse, MeResponse, MobileCreditPackFulfillmentResponse, ModerationReportResponse, MutateOwnedCustomAssetRequest, MutateOwnedCustomAssetResponse, MyAchievementsResponse, MyAppsResponse, MyInviteCodeResponse, MyLeaderboardsResponse, NotificationReadResponse, NotificationStatus, NotificationsResponse, OwnedProfileAssetsResponse, PaymentProvider, PaymentStatus, PrivacyExportResponse, PublicEmailPreferencesResponse, PublicEmailPreferencesUpdateRequest, PurchaseResponse, PushSubscriptionMutationResponse, RecordAppUploadLaunchCheckRequest, RecordAppUploadLaunchCheckResponse, RecordAppUploadCausalCheckRequest, RecordAppUploadCausalCheckResponse, RecordAppUploadMediaCaptureRequest, RecordAppUploadMediaCaptureResponse, RegisterRequest, RegisterResponse, RemixScaffoldResponse, ResolveRuntimeAssetsRequest, ResolveRuntimeAssetsResponse, RetryAgentTaskResponse, ReviewState, SavedItemKind, SavedItemMutationResponse, SearchRequest, SearchResponse, SearchSuggestRequest, SearchSuggestResponse, SetAchievementProgressRequest, SetAchievementProgressResponse, SetProfileAssetRequest, SetProfileAssetResponse, ShopResponse, StarterProfileAssetsResponse, SubmitFeedbackRequest, SubmitFeedbackResponse, SubmitLeaderboardScoreRequest, SubmitLeaderboardScoreResponse, TagDetailResponse, TagDirectoryResponse, TagGroupDetailResponse, TagGroupSort, TagListResponse, TemplateScaffoldResponse, TrackEngagementEventRequest, TrackEngagementEventResponse, TractionAnalyticsResponse, TransactionsResponse, UnlinkGoogleResponse, UnlinkXResponse, UnlockAchievementResponse, UpdateAdminFeedbackRequest, UpdateAdminFeedbackResponse, UpdateAdminGameCollectionGamesRequest, UpdateAdminGameCollectionRequest, UpdateAdminUserRequest, UpdateAppRequest, UpdateAppResponse, UpdateAssetPackRequest, UpdateAssetPackResponse, UpdateAssetPackVersionRequest, UpdateAssetPackVersionResponse, UpdateAssetRequest, UpdateAssetResponse, UpdateAssetSpecVersionResponse, UpdateAssetVersionRequest, UpdateAssetVersionResponse, UpdateProfileRequest, UpdateProfileResponse, UpdateTagGroupRequest, UpdateTagRequest, UpdateUserCommunicationPreferencesRequest, UpdateVersionResponse, UploadAppSessionFileResponse, UploadAppSessionOwnedAssetResponse, UploadAssetPackSessionAssetResponse, UploadAssetPackSessionMediaResponse, UpsertWebPushSubscriptionRequest, UserDetailResponse, UsernameAvailableResponse, UsersListResponse, WebPushConfigResponse, WebPushSubscriptionStatusResponse, WorkerAgentTaskAttemptUnavailableRequest, WorkerAgentTaskAttemptUnavailableResponse, WorkerClaimAgentTaskRequest, WorkerClaimAgentTaskResponse, WorkerClaimAgentTaskSlugRequest, WorkerClaimAgentTaskSlugResponse, WorkerCompleteAgentTaskRequest, WorkerCreateAgentTaskEventRequest, WorkerCreateAgentTaskEventResponse, WorkerCreateAgentTaskTranscriptChunkRequest, WorkerCreateAgentTaskTranscriptChunkResponse, WorkerFailAgentTaskRequest, WorkerHeartbeatAgentTaskRequest, WorkerHeartbeatAgentTaskResponse, WorkerPresenceRequest, WorkerPresenceResponse, RegisterAgentBundleRequest, RegisterAgentBundleResponse, WorkerRecordAgentTaskRunTelemetryRequest, WorkerRecordAgentTaskRunTelemetryResponse, WorkerSubmitAgentTaskEvalRequest, WorkerSubmitAgentTaskEvalResponse, WorkerSubmitAgentTaskReviewRequest, WorkerSubmitAgentTaskReviewResponse, WorkerAgentTaskMaterialResponse, WorkerUpdateIntentRequest, WorkerUpdateIntentResponse, XPendingSignupResponse } from '@playdrop/types';
2
2
  import { type AdminAgentTaskTranscriptOptions, type CreationGamesFetchResult, type GetCreationConversationOptions, type ListAgentTasksOptions, type ListCreatorGamesOptions, type UploadWorkerAgentTaskMaterialOptions } from './domains/agent-tasks.js';
3
3
  export interface HttpMethod {
4
4
  GET: 'GET';
@@ -26,6 +26,9 @@ export interface ApiClientConfig {
26
26
  requestTimeoutMs?: number;
27
27
  includeBrowserCredentials?: boolean;
28
28
  }
29
+ export interface GameplaySessionRequestOptions {
30
+ appAccessToken?: string;
31
+ }
29
32
  type UploadAssetVersionFile = {
30
33
  file: File | Blob;
31
34
  fieldName?: string;
@@ -210,6 +213,9 @@ export interface ApiClient {
210
213
  reportComment(commentId: number, request: CreateCommentReportRequest): Promise<ModerationReportResponse>;
211
214
  deleteContentComment(commentId: number): Promise<DeleteContentCommentResponse>;
212
215
  trackEngagementEvent(request: TrackEngagementEventRequest): Promise<TrackEngagementEventResponse>;
216
+ startGameplaySession(request: GameplaySessionStartRequest, options?: GameplaySessionRequestOptions): Promise<GameplaySessionResponse>;
217
+ heartbeatGameplaySession(sessionId: string, request: GameplaySessionActivityRequest, options?: GameplaySessionRequestOptions): Promise<GameplaySessionResponse>;
218
+ endGameplaySession(sessionId: string, request: GameplaySessionActivityRequest, options?: GameplaySessionRequestOptions): Promise<GameplaySessionResponse>;
213
219
  fetchLibrary(options?: {
214
220
  category?: LibraryCategory;
215
221
  sort?: LibrarySort;
@@ -280,11 +286,25 @@ export interface ApiClient {
280
286
  fetchAppBySlug(creatorUsername: string, appName: string, options?: AppDetailRequest): Promise<AppDetailResponse>;
281
287
  fetchAppMoreCollections(creatorUsername: string, appName: string, options?: AppMoreCollectionsRequest): Promise<AppMoreCollectionsResponse>;
282
288
  fetchAppMoreCollectionPage(creatorUsername: string, appName: string, collectionKey: AppMoreCollectionKey, options?: AppMoreCollectionRequest): Promise<AppMoreCollectionPage>;
283
- requestAppAccessToken(appId: number): Promise<AppAccessTokenResponse>;
284
- requestDevAppAccessToken(appId: number, slot: number): Promise<DevAppAccessTokenResponse>;
289
+ requestAppAccessToken(appId: number, options?: {
290
+ sessionId?: string;
291
+ version?: string;
292
+ }): Promise<AppAccessTokenResponse>;
293
+ requestDevAppAccessToken(appId: number, slot: number, options: {
294
+ version: string;
295
+ sessionId?: string;
296
+ }): Promise<DevAppAccessTokenResponse>;
285
297
  resetDevPlayer(appId: number, slot: number): Promise<DevPlayerResetResponse>;
286
298
  resetAllDevPlayers(appId: number): Promise<DevPlayerResetResponse>;
287
299
  sendAppLog(appId: number, entry: AppLogEntryRequest): Promise<AppLogEntryResponse>;
300
+ getAppTweaks(creatorUsername: string, appName: string, version: string): Promise<AppTweaksSnapshot>;
301
+ getLatestAppTweaks(creatorUsername: string, appName: string): Promise<AppTweaksSnapshot & {
302
+ version: string;
303
+ }>;
304
+ replaceAppTweaks(creatorUsername: string, appName: string, version: string, values: TweakValues): Promise<AppTweaksSnapshot>;
305
+ listPlaytestNotes(creatorUsername: string, appName: string, version: string): Promise<CreatorNotesResponse>;
306
+ clearPlaytestNotes(creatorUsername: string, appName: string, version: string, ids: number[]): Promise<CreatorNotesClearResponse>;
307
+ downloadPlaytestNoteAttachment(creatorUsername: string, appName: string, version: string, noteId: number): Promise<Blob>;
288
308
  listAchievements(appId: number, options?: {
289
309
  includeHiddenDefinitions?: boolean;
290
310
  includeRetired?: boolean;
@@ -465,7 +485,7 @@ export interface ApiClient {
465
485
  initializeAppUpload(creatorUsername: string, appName: string, request: InitializeAppUploadRequest): Promise<InitializeAppUploadResponse>;
466
486
  uploadAppSessionFile(creatorUsername: string, appName: string, sessionId: string, fileKey: string, options: UploadAppSessionFileOptions): Promise<UploadAppSessionFileResponse>;
467
487
  uploadAppSessionOwnedAsset(creatorUsername: string, appName: string, sessionId: string, uploadKey: string, options: UploadAppSessionOwnedAssetOptions): Promise<UploadAppSessionOwnedAssetResponse>;
468
- finalizeAppUpload(creatorUsername: string, appName: string, sessionId: string): Promise<FinalizeAppUploadResponse>;
488
+ finalizeAppUpload(creatorUsername: string, appName: string, sessionId: string, request?: FinalizeAppUploadRequest): Promise<FinalizeAppUploadResponse>;
469
489
  fetchAppUploadLaunchCheckPreview(creatorUsername: string, appName: string, sessionId: string): Promise<AppUploadLaunchCheckPreviewResponse>;
470
490
  recordAppUploadLaunchCheck(creatorUsername: string, appName: string, sessionId: string, request: RecordAppUploadLaunchCheckRequest): Promise<RecordAppUploadLaunchCheckResponse>;
471
491
  recordAppUploadCausalCheck(creatorUsername: string, appName: string, sessionId: string, request: RecordAppUploadCausalCheckRequest): Promise<RecordAppUploadCausalCheckResponse>;
@@ -537,6 +557,7 @@ export interface ApiClient {
537
557
  deleteAdminUser(userId: number): Promise<DeleteAdminUserResponse>;
538
558
  fetchAdminApps(): Promise<AdminAppsListResponse>;
539
559
  fetchAdminPlatformAnalytics(options?: AdminPlatformAnalyticsRequest): Promise<AdminPlatformAnalyticsResponse>;
560
+ fetchAdminTractionAnalytics(options: AdminTractionAnalyticsRequest): Promise<TractionAnalyticsResponse>;
540
561
  updateAdminApp(id: number, request: AdminUpdateAppRequest): Promise<AdminUpdateAppResponse>;
541
562
  deleteAdminApp(id: number): Promise<DeleteAppResponse>;
542
563
  fetchAdminGameCollections(): Promise<AdminGameCollectionsResponse>;