@bitmagic/cli 0.1.83-dev.1 → 0.1.83-dev.2

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 (158) hide show
  1. package/README.md +69 -0
  2. package/assets/skills/publishing-to-steam/SKILL.md +218 -0
  3. package/dist/assets/vxl-summary.d.ts +6 -20
  4. package/dist/assets/vxl-summary.js +9 -100
  5. package/dist/assets/vxl-summary.js.map +1 -1
  6. package/dist/cli.d.ts +1 -0
  7. package/dist/cli.js +2 -0
  8. package/dist/cli.js.map +1 -1
  9. package/dist/commands/check.js +5 -0
  10. package/dist/commands/check.js.map +1 -1
  11. package/dist/commands/forge.js +1 -1
  12. package/dist/commands/forge.js.map +1 -1
  13. package/dist/commands/ios.js +88 -136
  14. package/dist/commands/ios.js.map +1 -1
  15. package/dist/commands/steam-build.d.ts +67 -0
  16. package/dist/commands/steam-build.js +286 -0
  17. package/dist/commands/steam-build.js.map +1 -0
  18. package/dist/commands/steam-portal.d.ts +1 -0
  19. package/dist/commands/steam-portal.js +319 -0
  20. package/dist/commands/steam-portal.js.map +1 -0
  21. package/dist/commands/steam-upload.d.ts +37 -0
  22. package/dist/commands/steam-upload.js +172 -0
  23. package/dist/commands/steam-upload.js.map +1 -0
  24. package/dist/commands/steam.d.ts +1 -0
  25. package/dist/commands/steam.js +502 -0
  26. package/dist/commands/steam.js.map +1 -0
  27. package/dist/commands/verify.js +6 -0
  28. package/dist/commands/verify.js.map +1 -1
  29. package/dist/editor/asset-viewer.js +5 -4
  30. package/dist/editor/asset-viewer.js.map +1 -1
  31. package/dist/editor/server.js +1 -1
  32. package/dist/editor/server.js.map +1 -1
  33. package/dist/editor/shell-page.js +3 -3
  34. package/dist/forge/browser-host.d.ts +1 -1
  35. package/dist/ios/native-overrides.d.ts +27 -0
  36. package/dist/ios/native-overrides.js +87 -0
  37. package/dist/ios/native-overrides.js.map +1 -0
  38. package/dist/ios/record-validate.d.ts +1 -33
  39. package/dist/ios/record-validate.js +2 -100
  40. package/dist/ios/record-validate.js.map +1 -1
  41. package/dist/ios/store-bridge.d.ts +26 -0
  42. package/dist/ios/store-bridge.js +224 -0
  43. package/dist/ios/store-bridge.js.map +1 -0
  44. package/dist/ios/storekit-config.d.ts +15 -0
  45. package/dist/ios/storekit-config.js +106 -0
  46. package/dist/ios/storekit-config.js.map +1 -0
  47. package/dist/ios/xcode-template.d.ts +12 -3
  48. package/dist/ios/xcode-template.js +158 -27
  49. package/dist/ios/xcode-template.js.map +1 -1
  50. package/dist/package/collect-urls.d.ts +1 -1
  51. package/dist/package/collect-urls.js +10 -2
  52. package/dist/package/collect-urls.js.map +1 -1
  53. package/dist/package/offline-payload.d.ts +35 -0
  54. package/dist/package/offline-payload.js +142 -0
  55. package/dist/package/offline-payload.js.map +1 -0
  56. package/dist/package/packaged-check.d.ts +39 -0
  57. package/dist/package/packaged-check.js +104 -0
  58. package/dist/package/packaged-check.js.map +1 -0
  59. package/dist/package/transform.d.ts +4 -4
  60. package/dist/package/transform.js +3 -3
  61. package/dist/project/published-three.d.ts +14 -0
  62. package/dist/project/published-three.js +93 -0
  63. package/dist/project/published-three.js.map +1 -0
  64. package/dist/publish/bundle.js +6 -0
  65. package/dist/publish/bundle.js.map +1 -1
  66. package/dist/scaffold/project-files.js +5 -2
  67. package/dist/scaffold/project-files.js.map +1 -1
  68. package/dist/scaffold/project.js +2 -0
  69. package/dist/scaffold/project.js.map +1 -1
  70. package/dist/scaffold/skill-files.js +3 -1
  71. package/dist/scaffold/skill-files.js.map +1 -1
  72. package/dist/scaffold/steam-skill-files.d.ts +2 -0
  73. package/dist/scaffold/steam-skill-files.js +7 -0
  74. package/dist/scaffold/steam-skill-files.js.map +1 -0
  75. package/dist/steam/art-generate.d.ts +55 -0
  76. package/dist/steam/art-generate.js +173 -0
  77. package/dist/steam/art-generate.js.map +1 -0
  78. package/dist/steam/art-render.d.ts +25 -0
  79. package/dist/steam/art-render.js +157 -0
  80. package/dist/steam/art-render.js.map +1 -0
  81. package/dist/steam/art-use.d.ts +20 -0
  82. package/dist/steam/art-use.js +84 -0
  83. package/dist/steam/art-use.js.map +1 -0
  84. package/dist/steam/art.d.ts +98 -0
  85. package/dist/steam/art.js +243 -0
  86. package/dist/steam/art.js.map +1 -0
  87. package/dist/steam/check.d.ts +21 -0
  88. package/dist/steam/check.js +113 -0
  89. package/dist/steam/check.js.map +1 -0
  90. package/dist/steam/config.d.ts +57 -0
  91. package/dist/steam/config.js +145 -0
  92. package/dist/steam/config.js.map +1 -0
  93. package/dist/steam/electron-cache.d.ts +11 -0
  94. package/dist/steam/electron-cache.js +105 -0
  95. package/dist/steam/electron-cache.js.map +1 -0
  96. package/dist/steam/fuses.d.ts +16 -0
  97. package/dist/steam/fuses.js +75 -0
  98. package/dist/steam/fuses.js.map +1 -0
  99. package/dist/steam/icon-files.d.ts +16 -0
  100. package/dist/steam/icon-files.js +70 -0
  101. package/dist/steam/icon-files.js.map +1 -0
  102. package/dist/steam/image-info.d.ts +16 -0
  103. package/dist/steam/image-info.js +89 -0
  104. package/dist/steam/image-info.js.map +1 -0
  105. package/dist/steam/launch-check.d.ts +12 -0
  106. package/dist/steam/launch-check.js +101 -0
  107. package/dist/steam/launch-check.js.map +1 -0
  108. package/dist/steam/media.d.ts +48 -0
  109. package/dist/steam/media.js +151 -0
  110. package/dist/steam/media.js.map +1 -0
  111. package/dist/steam/package-macos.d.ts +52 -0
  112. package/dist/steam/package-macos.js +268 -0
  113. package/dist/steam/package-macos.js.map +1 -0
  114. package/dist/steam/package-windows.d.ts +24 -0
  115. package/dist/steam/package-windows.js +84 -0
  116. package/dist/steam/package-windows.js.map +1 -0
  117. package/dist/steam/paths.d.ts +37 -0
  118. package/dist/steam/paths.js +45 -0
  119. package/dist/steam/paths.js.map +1 -0
  120. package/dist/steam/playtest-shots.d.ts +18 -0
  121. package/dist/steam/playtest-shots.js +83 -0
  122. package/dist/steam/playtest-shots.js.map +1 -0
  123. package/dist/steam/portal-recipes.d.ts +25 -0
  124. package/dist/steam/portal-recipes.js +61 -0
  125. package/dist/steam/portal-recipes.js.map +1 -0
  126. package/dist/steam/portal.d.ts +58 -0
  127. package/dist/steam/portal.js +340 -0
  128. package/dist/steam/portal.js.map +1 -0
  129. package/dist/steam/raster.d.ts +59 -0
  130. package/dist/steam/raster.js +285 -0
  131. package/dist/steam/raster.js.map +1 -0
  132. package/dist/steam/shell-files.d.ts +17 -0
  133. package/dist/steam/shell-files.js +268 -0
  134. package/dist/steam/shell-files.js.map +1 -0
  135. package/dist/steam/specs.d.ts +61 -0
  136. package/dist/steam/specs.js +103 -0
  137. package/dist/steam/specs.js.map +1 -0
  138. package/dist/steam/steampipe.d.ts +46 -0
  139. package/dist/steam/steampipe.js +170 -0
  140. package/dist/steam/steampipe.js.map +1 -0
  141. package/dist/steam/store-copy.d.ts +89 -0
  142. package/dist/steam/store-copy.js +221 -0
  143. package/dist/steam/store-copy.js.map +1 -0
  144. package/dist/steam/tags.d.ts +9 -0
  145. package/dist/steam/tags.js +49 -0
  146. package/dist/steam/tags.js.map +1 -0
  147. package/dist/steam/trailer.d.ts +34 -0
  148. package/dist/steam/trailer.js +141 -0
  149. package/dist/steam/trailer.js.map +1 -0
  150. package/dist/verify/browser.d.ts +11 -0
  151. package/dist/verify/browser.js +195 -152
  152. package/dist/verify/browser.js.map +1 -1
  153. package/dist/verify/classify.d.ts +11 -2
  154. package/dist/verify/classify.js +16 -4
  155. package/dist/verify/classify.js.map +1 -1
  156. package/dist/verify/mobile-parity.d.ts +1 -1
  157. package/dist/verify/mobile-parity.js +1 -1
  158. package/package.json +5 -4
@@ -0,0 +1,286 @@
1
+ /**
2
+ * `bitmagic steam build` — the Windows and macOS depots.
3
+ *
4
+ * payload the self-contained bundle, every asset vendored, proven to boot with no network
5
+ * on WebGPU and WebGL (shared with `ios build`: package/offline-payload.ts)
6
+ * windows Electron win32-x64 stamped into `<Game>.exe` (any OS)
7
+ * macos a universal `<Game>.app`, signed with the creator's Developer ID and notarized —
8
+ * Steam requires notarization for new macOS apps (a Mac is required)
9
+ * check the finished app for this machine's platform is launched and must reach gameplay
10
+ *
11
+ * Output: `.bitmagic/steam/build/windows/` and `.bitmagic/steam/build/macos/` — exactly the
12
+ * content roots `steam upload` hands SteamPipe — plus `build.json` describing what was built.
13
+ */
14
+ import { spawn } from 'child_process';
15
+ import { defineCommand } from 'citty';
16
+ import * as fs from 'fs';
17
+ import * as path from 'path';
18
+ import { writeJsonAtomic, readJsonFile } from '../atomic-json.js';
19
+ import { CliError } from '../errors.js';
20
+ import { createOutput } from '../output.js';
21
+ import { loadProjectContext } from '../project/context.js';
22
+ import { buildOfflinePayload, ensureSelfContainedBundle } from '../package/offline-payload.js';
23
+ import { computeFingerprint } from '../publish/fingerprint.js';
24
+ import { submitCredentialsPath } from './ios.js';
25
+ import { resolveSteamConfig, updateSteamConfigFile } from '../steam/config.js';
26
+ import { ELECTRON_VERSION, ensureElectronShell } from '../steam/electron-cache.js';
27
+ import { launchCheckApp } from '../steam/launch-check.js';
28
+ import { findDeveloperIdIdentities, gatekeeperAssessment, mergeUniversal, notarizeMacApp, signMacApp, stampMacApp, } from '../steam/package-macos.js';
29
+ import { directoryBytes, packageWindows } from '../steam/package-windows.js';
30
+ import { macosContentRoot, steamPaths, windowsContentRoot } from '../steam/paths.js';
31
+ import { shellPackageName } from '../steam/shell-files.js';
32
+ import { MAC_ICNS_FILE, SHORTCUT_ICO_FILE } from '../steam/specs.js';
33
+ import { readStoreCopy } from '../steam/store-copy.js';
34
+ const RENDERERS = ['webgpu', 'webgl'];
35
+ export function buildRecordPath(root) {
36
+ return path.join(steamPaths(root).buildDir, 'build.json');
37
+ }
38
+ export function readBuildRecord(root) {
39
+ const parsed = readJsonFile(buildRecordPath(root));
40
+ return typeof parsed === 'object' && parsed !== null ? parsed : null;
41
+ }
42
+ function parsePlatforms(value) {
43
+ if (value === undefined || value === 'all')
44
+ return ['windows', 'macos'];
45
+ const platforms = value.split(',').map((p) => p.trim());
46
+ for (const platform of platforms) {
47
+ if (platform !== 'windows' && platform !== 'macos')
48
+ throw new CliError('--platform takes windows, macos or all.');
49
+ }
50
+ return platforms;
51
+ }
52
+ /** The notary credentials to use, or null when there are none (and the reason why). */
53
+ function resolveNotaryCredentials(profileFlag, configProfile) {
54
+ const profile = profileFlag ?? configProfile;
55
+ if (profile !== undefined)
56
+ return { kind: 'profile', profile };
57
+ const parsed = readJsonFile(submitCredentialsPath());
58
+ if (parsed !== null && typeof parsed.keyId === 'string' && typeof parsed.issuerId === 'string' && typeof parsed.keyPath === 'string'
59
+ && fs.existsSync(parsed.keyPath)) {
60
+ return { kind: 'key', keyId: parsed.keyId, issuerId: parsed.issuerId, keyPath: parsed.keyPath };
61
+ }
62
+ return null;
63
+ }
64
+ async function runSteamBuild(args) {
65
+ const json = args.json === true;
66
+ const output = createOutput(json);
67
+ const context = loadProjectContext();
68
+ const paths = steamPaths(context.root);
69
+ const config = resolveSteamConfig({ root: context.root, gameId: context.metadata.gameId, configPath: paths.configPath });
70
+ const platforms = parsePlatforms(args.platform);
71
+ if (platforms.includes('macos') && process.platform !== 'darwin') {
72
+ throw new CliError('The macOS build needs a Mac (lipo, codesign and notarization are macOS tools). Build `--platform windows` here and the macOS depot on a Mac.', 3);
73
+ }
74
+ const copy = readStoreCopy(paths.storePath);
75
+ const copyrightHolder = copy?.developer?.trim() || `the creator of ${config.displayName}`;
76
+ const identity = { displayName: config.displayName, packageName: shellPackageName(context.metadata.gameId), version: config.version };
77
+ const icoPath = path.join(paths.kitDir, SHORTCUT_ICO_FILE);
78
+ const icnsPath = path.join(paths.kitDir, MAC_ICNS_FILE);
79
+ if (!fs.existsSync(icoPath) || !fs.existsSync(icnsPath)) {
80
+ output.info('No game icons yet, so the builds keep Electron\'s. Run `bitmagic steam art render` (it writes them from the icon art) and build again.');
81
+ }
82
+ // Signing is decided before the slow work, so a missing identity fails in seconds, not minutes.
83
+ let signIdentity = null;
84
+ if (platforms.includes('macos') && args.sign !== false) {
85
+ const identities = findDeveloperIdIdentities();
86
+ const wanted = args.identity ?? config.macIdentity;
87
+ if (wanted !== undefined) {
88
+ signIdentity = identities.find((candidate) => candidate === wanted || candidate.includes(wanted)) ?? null;
89
+ if (signIdentity === null)
90
+ throw new CliError(`No signing identity matching "${wanted}" in the keychain. Found: ${identities.join(', ') || 'none'}.`, 3);
91
+ if (args.identity !== undefined)
92
+ updateSteamConfigFile(paths.configPath, { macIdentity: signIdentity });
93
+ }
94
+ else if (identities.length === 1) {
95
+ signIdentity = identities[0];
96
+ }
97
+ else if (identities.length > 1) {
98
+ throw new CliError(`The keychain holds several Developer ID identities; pick one with --identity:\n ${identities.join('\n ')}`, 3);
99
+ }
100
+ else {
101
+ throw new CliError('Steam requires the macOS app to be notarized, which needs a "Developer ID Application" certificate in this Mac\'s '
102
+ + 'keychain (Apple Developer Program → Certificates). Create one, or build unsigned with --no-sign (Steam may reject it).', 3);
103
+ }
104
+ }
105
+ let notary = null;
106
+ if (signIdentity !== null && args.notarize !== false) {
107
+ notary = resolveNotaryCredentials(args['notary-profile'], config.notaryProfile);
108
+ if (notary === null) {
109
+ throw new CliError('Notarizing needs Apple credentials. Either write an App Store Connect API key to '
110
+ + `${submitCredentialsPath()} as {"keyId": "…", "issuerId": "…", "keyPath": "/path/to/AuthKey_….p8"} `
111
+ + '(Users and Access → Integrations → App Store Connect API, role Developer), or have the creator run '
112
+ + '`xcrun notarytool store-credentials <name>` in their own terminal and pass --notary-profile <name>. '
113
+ + 'To build without notarizing, pass --no-notarize.', 3);
114
+ }
115
+ if (args['notary-profile'] !== undefined)
116
+ updateSteamConfigFile(paths.configPath, { notaryProfile: args['notary-profile'] });
117
+ }
118
+ const html = ensureSelfContainedBundle(context, output, json);
119
+ const fingerprint = computeFingerprint(context.root);
120
+ const { offlineValidated } = await buildOfflinePayload({
121
+ root: context.root,
122
+ html,
123
+ payloadDir: paths.payloadDir,
124
+ cacheDir: paths.cacheDir,
125
+ checkDir: paths.checkDir,
126
+ distribution: 'steam',
127
+ platform: 'desktop',
128
+ renderers: RENDERERS,
129
+ skipValidation: args['skip-validation'] === true,
130
+ deviceLabel: 'desktop window',
131
+ output,
132
+ });
133
+ const record = {
134
+ version: config.version,
135
+ buildNumber: config.buildNumber,
136
+ electron: ELECTRON_VERSION,
137
+ fingerprint,
138
+ offlineValidated,
139
+ builtAt: new Date().toISOString(),
140
+ };
141
+ if (platforms.includes('windows')) {
142
+ const shell = await ensureElectronShell('win32-x64', output.info);
143
+ output.info('Stamping the Windows build…');
144
+ const result = await packageWindows({
145
+ shellDir: shell,
146
+ outDir: windowsContentRoot(paths),
147
+ productName: config.productName,
148
+ identity,
149
+ version: config.version,
150
+ copyrightHolder,
151
+ payloadDir: paths.payloadDir,
152
+ ico: fs.existsSync(icoPath) ? fs.readFileSync(icoPath) : null,
153
+ });
154
+ record.windows = { exe: result.exePath, bytes: result.bytes };
155
+ output.info(`Windows: ${result.exePath} (${(result.bytes / 1e6).toFixed(0)} MB)`);
156
+ }
157
+ if (platforms.includes('macos')) {
158
+ const work = path.join(paths.steamDir, 'work');
159
+ const stamped = { arm64: '', x64: '' };
160
+ for (const arch of ['arm64', 'x64']) {
161
+ const shell = await ensureElectronShell(`darwin-${arch}`, output.info);
162
+ output.info(`Stamping the macOS ${arch} app…`);
163
+ stamped[arch] = stampMacApp({
164
+ shellDir: shell,
165
+ outDir: path.join(work, `macos-${arch}`),
166
+ productName: config.productName,
167
+ bundleId: config.bundleId,
168
+ version: config.version,
169
+ buildNumber: config.buildNumber,
170
+ copyrightHolder,
171
+ identity,
172
+ payloadDir: paths.payloadDir,
173
+ icns: fs.existsSync(icnsPath) ? fs.readFileSync(icnsPath) : null,
174
+ });
175
+ }
176
+ const outRoot = macosContentRoot(paths);
177
+ fs.rmSync(outRoot, { recursive: true, force: true });
178
+ fs.mkdirSync(outRoot, { recursive: true });
179
+ const app = path.join(outRoot, `${config.productName}.app`);
180
+ output.info('Merging Apple Silicon and Intel into one universal app…');
181
+ mergeUniversal(stamped.arm64, stamped.x64, app);
182
+ fs.rmSync(work, { recursive: true, force: true });
183
+ let notarized = false;
184
+ let gatekeeper = null;
185
+ if (signIdentity !== null) {
186
+ signMacApp(app, signIdentity, output.info);
187
+ if (notary !== null) {
188
+ const result = notarizeMacApp(app, notary, output.info);
189
+ notarized = true;
190
+ output.info(`Notarized (submission ${result.submissionId}) and stapled.`);
191
+ }
192
+ gatekeeper = gatekeeperAssessment(app);
193
+ output.info(`Gatekeeper: ${gatekeeper.split('\n').filter((line) => /accepted|rejected|source=/.test(line)).join(' ') || gatekeeper}`);
194
+ }
195
+ else {
196
+ output.info('Unsigned macOS build (--no-sign). Steam requires new macOS apps to be notarized; do not ship this one.');
197
+ }
198
+ let launchChecked = false;
199
+ if (args['launch-check'] !== false && process.platform === 'darwin') {
200
+ output.info('Launching the finished app to check it reaches gameplay…');
201
+ const check = await launchCheckApp({
202
+ executable: path.join(app, 'Contents', 'MacOS', config.productName),
203
+ artifactDir: path.join(paths.checkDir, 'launch-macos'),
204
+ });
205
+ for (const warning of check.result.warnings)
206
+ output.info(` warning: ${warning}`);
207
+ if (!check.result.ok) {
208
+ throw new CliError(`The packaged macOS app did not reach gameplay:\n${check.result.failures.map((f) => ` ✗ ${f}`).join('\n')}\n`
209
+ + `Artifacts: ${path.dirname(check.screenshotPath)}`, 5);
210
+ }
211
+ launchChecked = true;
212
+ output.info(`The macOS app launches and plays (screenshot: ${check.screenshotPath}).`);
213
+ }
214
+ record.macos = { app, bytes: directoryBytes(app), signedBy: signIdentity, notarized, gatekeeper, launchChecked };
215
+ output.info(`macOS: ${app} (${(record.macos.bytes / 1e6).toFixed(0)} MB)`);
216
+ }
217
+ writeJsonAtomic(buildRecordPath(context.root), record);
218
+ output.info('Next: `bitmagic steam upload` (the creator logs steamcmd in once with `bitmagic steam login`).');
219
+ output.result({ ok: true, ...record });
220
+ }
221
+ /** The executable `play` and the launch check start on this machine, or null when none was built. */
222
+ function builtExecutable(record, productName) {
223
+ if (process.platform === 'darwin' && record?.macos !== undefined) {
224
+ return { executable: path.join(record.macos.app, 'Contents', 'MacOS', productName), app: record.macos.app };
225
+ }
226
+ if (process.platform === 'win32' && record?.windows !== undefined)
227
+ return { executable: record.windows.exe };
228
+ return null;
229
+ }
230
+ export const steamPlayCommand = defineCommand({
231
+ meta: {
232
+ name: 'play',
233
+ description: 'Open the built app on this machine to play it as a Steam player will. --check runs the automated launch check instead.',
234
+ },
235
+ args: {
236
+ check: { type: 'boolean', description: 'Launch it headlessly-driven and check it reaches gameplay, as `steam build` does.' },
237
+ json: { type: 'boolean', description: 'Print the result as JSON on stdout; all progress goes to stderr.' },
238
+ },
239
+ async run({ args }) {
240
+ const output = createOutput(args.json === true);
241
+ const context = loadProjectContext();
242
+ const paths = steamPaths(context.root);
243
+ const config = resolveSteamConfig({ root: context.root, gameId: context.metadata.gameId, configPath: paths.configPath });
244
+ const built = builtExecutable(readBuildRecord(context.root), config.productName);
245
+ if (built === null)
246
+ throw new CliError('No build for this machine\'s platform. Run `bitmagic steam build` first.', 3);
247
+ if (args.check === true) {
248
+ const check = await launchCheckApp({ executable: built.executable, artifactDir: path.join(paths.checkDir, `launch-${process.platform === 'darwin' ? 'macos' : 'windows'}`) });
249
+ for (const warning of check.result.warnings)
250
+ output.info(` warning: ${warning}`);
251
+ for (const failure of check.result.failures)
252
+ output.info(` ✗ ${failure}`);
253
+ output.info(check.result.ok ? `The app launches and plays (screenshot: ${check.screenshotPath}).` : `It did not reach gameplay. Artifacts: ${path.dirname(check.screenshotPath)}`);
254
+ output.result({ ok: check.result.ok, ...check });
255
+ if (!check.result.ok)
256
+ throw new CliError('The packaged app did not reach gameplay.', 5);
257
+ return;
258
+ }
259
+ const child = built.app !== undefined
260
+ ? spawn('open', [built.app], { detached: true, stdio: 'ignore' })
261
+ : spawn(built.executable, [], { detached: true, stdio: 'ignore' });
262
+ child.unref();
263
+ output.info(`Opened ${built.app ?? built.executable}.`);
264
+ output.result({ ok: true, opened: built.app ?? built.executable });
265
+ },
266
+ });
267
+ export const steamBuildCommand = defineCommand({
268
+ meta: {
269
+ name: 'build',
270
+ description: 'Build the Windows and macOS depots: offline-validated payload, <Game>.exe, a signed and notarized universal <Game>.app.',
271
+ },
272
+ args: {
273
+ platform: { type: 'string', description: 'windows, macos, or all (default). The macOS build needs a Mac.' },
274
+ 'skip-validation': { type: 'boolean', description: 'Skip the offline record/validation passes. Working without a network is then NOT confirmed.' },
275
+ sign: { type: 'boolean', negativeDescription: 'Do not sign or notarize the macOS app (a local test build; Steam requires notarization).' },
276
+ notarize: { type: 'boolean', negativeDescription: 'Sign but do not notarize the macOS app.' },
277
+ identity: { type: 'string', description: 'The Developer ID Application identity to sign with, when the keychain has several. Remembered.' },
278
+ 'notary-profile': { type: 'string', description: 'A `notarytool store-credentials` profile to notarize with instead of the App Store Connect key. Remembered.' },
279
+ 'launch-check': { type: 'boolean', negativeDescription: 'Do not launch the finished app to check it reaches gameplay.' },
280
+ json: { type: 'boolean', description: 'Print the result as JSON on stdout; all progress goes to stderr.' },
281
+ },
282
+ async run({ args }) {
283
+ await runSteamBuild(args);
284
+ },
285
+ });
286
+ //# sourceMappingURL=steam-build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"steam-build.js","sourceRoot":"","sources":["../../src/commands/steam-build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare const steamPortalCommand: import("citty").CommandDef<import("citty").ArgsDef>;
@@ -0,0 +1,319 @@
1
+ /**
2
+ * `bitmagic steam portal` — the Steamworks website in a Chrome window the creator logs in to,
3
+ * with commands any agent can drive it by: look at the page, fill a field, upload files, click.
4
+ * See steam/portal.ts for the window and the lines it will not cross.
5
+ */
6
+ import { defineCommand } from 'citty';
7
+ import * as fs from 'fs';
8
+ import * as os from 'os';
9
+ import * as path from 'path';
10
+ import { CliError } from '../errors.js';
11
+ import { createOutput } from '../output.js';
12
+ import { findDirWithFileUpwards, loadProjectContext } from '../project/context.js';
13
+ import { steamPaths } from '../steam/paths.js';
14
+ import { resolveSteamConfig } from '../steam/config.js';
15
+ import { readStoreCopy } from '../steam/store-copy.js';
16
+ import { STEAMWORKS_HOME, attachPortal, clickElement, closePortal, detach, fillElement, gotoUrl, lookAtPage, openPortal, pageText, portalDir, selectOption, setChecked, uploadToElement, } from '../steam/portal.js';
17
+ import { runPortalRecipe, RECIPES } from '../steam/portal-recipes.js';
18
+ const JSON_FLAG = {
19
+ type: 'boolean',
20
+ description: 'Print the result as JSON on stdout; all progress goes to stderr.',
21
+ };
22
+ /** Where `portal screenshot` writes: the project's check dir inside a project, else the portal dir. */
23
+ function screenshotTarget() {
24
+ const root = findDirWithFileUpwards('bitmagic.json');
25
+ return root !== null
26
+ ? path.join(steamPaths(root).checkDir, 'portal.png')
27
+ : path.join(portalDir(), 'portal.png');
28
+ }
29
+ async function withPage(fn) {
30
+ const { browser, page } = await attachPortal();
31
+ try {
32
+ return await fn(page);
33
+ }
34
+ finally {
35
+ await detach(browser);
36
+ }
37
+ }
38
+ function printLook(output, look) {
39
+ output.info(`${look.title} — ${look.url}${look.loggedIn ? '' : ' (not logged in)'}`);
40
+ if (look.headings.length > 0)
41
+ output.info(`Headings: ${look.headings.join(' | ')}`);
42
+ for (const notice of look.notices)
43
+ output.info(`Notice: ${notice}`);
44
+ for (const element of look.elements) {
45
+ const state = [
46
+ element.value !== undefined && element.value !== '' ? `= "${element.value}"` : '',
47
+ element.checked !== undefined ? (element.checked ? '[x]' : '[ ]') : '',
48
+ element.disabled ? '(disabled)' : '',
49
+ ].filter((part) => part !== '').join(' ');
50
+ output.info(` ${element.ref.padEnd(5)} ${element.role.padEnd(9)} ${element.label}${state ? ` ${state}` : ''}`);
51
+ }
52
+ }
53
+ /** A dotted path into store.json, e.g. `about` or `systemRequirements.windows.minimum.os`. */
54
+ function storeValue(field) {
55
+ const root = findDirWithFileUpwards('bitmagic.json');
56
+ if (root === null)
57
+ throw new CliError('--store reads .bitmagic/steam/store.json, so run it inside the game project.');
58
+ const copy = readStoreCopy(steamPaths(root).storePath);
59
+ if (copy === null)
60
+ throw new CliError('No store copy yet — `bitmagic steam init`, then write it.', 3);
61
+ let value = copy;
62
+ for (const key of field.split('.')) {
63
+ value = typeof value === 'object' && value !== null ? value[key] : undefined;
64
+ }
65
+ if (typeof value === 'number')
66
+ return String(value);
67
+ if (Array.isArray(value))
68
+ return value.join(', ');
69
+ if (typeof value !== 'string')
70
+ throw new CliError(`store.json has no text at "${field}".`);
71
+ return value;
72
+ }
73
+ const openCommand = defineCommand({
74
+ meta: {
75
+ name: 'open',
76
+ description: 'Open Steamworks in a dedicated Chrome window (or bring the open one to a page). The creator logs in there.',
77
+ },
78
+ args: {
79
+ url: { type: 'string', description: 'A Steamworks page to open instead of the home page.' },
80
+ app: { type: 'boolean', description: 'Open this game\'s app page (needs the app id from `steam init`).' },
81
+ json: JSON_FLAG,
82
+ },
83
+ async run({ args }) {
84
+ const output = createOutput(args.json === true);
85
+ let url = args.url;
86
+ if (args.app === true) {
87
+ const context = loadProjectContext();
88
+ const paths = steamPaths(context.root);
89
+ const config = resolveSteamConfig({ root: context.root, gameId: context.metadata.gameId, configPath: paths.configPath });
90
+ if (config.appId === undefined)
91
+ throw new CliError('No app id yet — `bitmagic steam init --app-id <id>`.', 3);
92
+ url = `${STEAMWORKS_HOME}apps/landing/${config.appId}`;
93
+ }
94
+ const result = await openPortal(url !== undefined ? { url } : {});
95
+ const look = await withPage(lookAtPage);
96
+ output.info(result.reused ? 'The Steamworks window was already open.' : 'Opened Steamworks in its own Chrome window.');
97
+ output.info(look.loggedIn
98
+ ? `Logged in. ${look.title} — ${look.url}`
99
+ : 'Not logged in yet. The creator logs in in that window — their password and Steam Guard code are theirs to type, '
100
+ + 'nobody else\'s. Then run `bitmagic steam portal status`.');
101
+ output.result({ ok: true, ...result, loggedIn: look.loggedIn, url: look.url });
102
+ },
103
+ });
104
+ const statusCommand = defineCommand({
105
+ meta: { name: 'status', description: 'Whether the window is open and logged in, and which page it is on.' },
106
+ args: { json: JSON_FLAG },
107
+ async run({ args }) {
108
+ const output = createOutput(args.json === true);
109
+ const look = await withPage(lookAtPage);
110
+ output.info(`${look.loggedIn ? 'Logged in' : 'NOT logged in'} — ${look.title} — ${look.url}`);
111
+ output.result({ ok: true, loggedIn: look.loggedIn, url: look.url, title: look.title });
112
+ },
113
+ });
114
+ const lookCommand = defineCommand({
115
+ meta: {
116
+ name: 'look',
117
+ description: 'Describe the current page: headings, notices, and every control numbered e1, e2, … for fill/click/upload.',
118
+ },
119
+ args: {
120
+ screenshot: { type: 'boolean', description: 'Also save a screenshot of the page and print its path.' },
121
+ json: JSON_FLAG,
122
+ },
123
+ async run({ args }) {
124
+ const output = createOutput(args.json === true);
125
+ const result = await withPage(async (page) => {
126
+ const look = await lookAtPage(page);
127
+ let shot;
128
+ if (args.screenshot === true) {
129
+ shot = screenshotTarget();
130
+ fs.mkdirSync(path.dirname(shot), { recursive: true });
131
+ await page.screenshot({ path: shot, fullPage: false });
132
+ }
133
+ return { look, shot };
134
+ });
135
+ printLook(output, result.look);
136
+ if (result.shot)
137
+ output.info(`Screenshot: ${result.shot}`);
138
+ output.result({ ok: true, ...result.look, ...(result.shot ? { screenshot: result.shot } : {}) });
139
+ },
140
+ });
141
+ const textCommand = defineCommand({
142
+ meta: { name: 'text', description: 'Print the page\'s visible text — to read instructions, errors and what was saved.' },
143
+ args: { max: { type: 'string', description: 'Characters to print (default 20000).' }, json: JSON_FLAG },
144
+ async run({ args }) {
145
+ const output = createOutput(args.json === true);
146
+ const text = await withPage((page) => pageText(page, args.max !== undefined ? Number(args.max) : 20_000));
147
+ output.info(text);
148
+ output.result({ ok: true, text });
149
+ },
150
+ });
151
+ const screenshotCommand = defineCommand({
152
+ meta: { name: 'screenshot', description: 'Save a screenshot of the page and print its path, to look at it.' },
153
+ args: { full: { type: 'boolean', description: 'The whole scrolling page, not just the window.' }, json: JSON_FLAG },
154
+ async run({ args }) {
155
+ const output = createOutput(args.json === true);
156
+ const target = screenshotTarget();
157
+ fs.mkdirSync(path.dirname(target), { recursive: true });
158
+ await withPage((page) => page.screenshot({ path: target, fullPage: args.full === true }));
159
+ output.info(target);
160
+ output.result({ ok: true, path: target });
161
+ },
162
+ });
163
+ const gotoCommand = defineCommand({
164
+ meta: { name: 'goto', description: 'Go to a Steamworks page (full URL, or a path such as /apps/landing/<appid>).' },
165
+ args: { url: { type: 'positional', required: true, description: 'URL or path on partner.steamgames.com.' }, json: JSON_FLAG },
166
+ async run({ args }) {
167
+ const output = createOutput(args.json === true);
168
+ const look = await withPage(async (page) => {
169
+ await gotoUrl(page, args.url);
170
+ return lookAtPage(page);
171
+ });
172
+ printLook(output, look);
173
+ output.result({ ok: true, ...look });
174
+ },
175
+ });
176
+ const clickCommand = defineCommand({
177
+ meta: {
178
+ name: 'click',
179
+ description: 'Click an element by its ref from `look`. Submit/publish/release/pay/delete buttons need --approved.',
180
+ },
181
+ args: {
182
+ ref: { type: 'positional', required: true, description: 'An element ref from `look`, e.g. e12.' },
183
+ approved: {
184
+ type: 'boolean',
185
+ description: 'The creator has explicitly approved THIS step (a submission, publish, release, payment or deletion).',
186
+ },
187
+ json: JSON_FLAG,
188
+ },
189
+ async run({ args }) {
190
+ const output = createOutput(args.json === true);
191
+ const result = await withPage(async (page) => {
192
+ const label = await clickElement(page, args.ref, { approved: args.approved === true });
193
+ await page.waitForTimeout(800);
194
+ return { label, look: await lookAtPage(page) };
195
+ });
196
+ output.info(`Clicked "${result.label}".`);
197
+ printLook(output, result.look);
198
+ output.result({ ok: true, clicked: result.label, ...result.look });
199
+ },
200
+ });
201
+ const fillCommand = defineCommand({
202
+ meta: {
203
+ name: 'fill',
204
+ description: 'Type into a field by its ref: --value text, --file <path>, or --store <field of store.json>. Never a password.',
205
+ },
206
+ args: {
207
+ ref: { type: 'positional', required: true, description: 'An element ref from `look`.' },
208
+ value: { type: 'string', description: 'The text to put in the field.' },
209
+ file: { type: 'string', description: 'Read the text from this file.' },
210
+ store: { type: 'string', description: 'Take the text from .bitmagic/steam/store.json, e.g. about, shortDescription, systemRequirements.windows.minimum.os.' },
211
+ json: JSON_FLAG,
212
+ },
213
+ async run({ args }) {
214
+ const output = createOutput(args.json === true);
215
+ const sources = [args.value, args.file, args.store].filter((source) => source !== undefined);
216
+ if (sources.length !== 1)
217
+ throw new CliError('Pass exactly one of --value, --file or --store.');
218
+ const value = args.value ?? (args.file !== undefined ? fs.readFileSync(path.resolve(args.file), 'utf-8') : storeValue(args.store ?? ''));
219
+ await withPage((page) => fillElement(page, args.ref, value));
220
+ output.info(`Filled ${args.ref} (${value.length} characters). Nothing is saved until the page's own Save is clicked.`);
221
+ output.result({ ok: true, ref: args.ref, characters: value.length });
222
+ },
223
+ });
224
+ const uploadCommand = defineCommand({
225
+ meta: { name: 'upload', description: 'Give files to a file input by its ref (comma-separated paths).' },
226
+ args: {
227
+ ref: { type: 'positional', required: true, description: 'A file input ref from `look`.' },
228
+ files: { type: 'positional', required: true, description: 'Comma-separated file paths.' },
229
+ json: JSON_FLAG,
230
+ },
231
+ async run({ args }) {
232
+ const output = createOutput(args.json === true);
233
+ const files = args.files.split(',').map((file) => path.resolve(file.trim().replace(/^~(?=$|\/)/, os.homedir())));
234
+ const look = await withPage(async (page) => {
235
+ await uploadToElement(page, args.ref, files);
236
+ await page.waitForTimeout(1500);
237
+ return lookAtPage(page);
238
+ });
239
+ output.info(`Gave ${files.length} file(s) to ${args.ref}.`);
240
+ printLook(output, look);
241
+ output.result({ ok: true, files, ...look });
242
+ },
243
+ });
244
+ const selectCommand = defineCommand({
245
+ meta: { name: 'select', description: 'Choose an option in a dropdown by its ref.' },
246
+ args: {
247
+ ref: { type: 'positional', required: true, description: 'A select ref from `look`.' },
248
+ option: { type: 'positional', required: true, description: 'The option\'s visible text (or value).' },
249
+ json: JSON_FLAG,
250
+ },
251
+ async run({ args }) {
252
+ const output = createOutput(args.json === true);
253
+ const chosen = await withPage((page) => selectOption(page, args.ref, args.option));
254
+ output.info(`Selected ${chosen.join(', ')} in ${args.ref}.`);
255
+ output.result({ ok: true, selected: chosen });
256
+ },
257
+ });
258
+ const checkCommand = defineCommand({
259
+ meta: { name: 'check', description: 'Tick (or with --off untick) a checkbox by its ref.' },
260
+ args: {
261
+ ref: { type: 'positional', required: true, description: 'A checkbox ref from `look`.' },
262
+ off: { type: 'boolean', description: 'Untick instead.' },
263
+ json: JSON_FLAG,
264
+ },
265
+ async run({ args }) {
266
+ const output = createOutput(args.json === true);
267
+ await withPage((page) => setChecked(page, args.ref, args.off !== true));
268
+ output.info(`${args.off === true ? 'Unticked' : 'Ticked'} ${args.ref}.`);
269
+ output.result({ ok: true });
270
+ },
271
+ });
272
+ const runCommand = defineCommand({
273
+ meta: {
274
+ name: 'run',
275
+ description: `Run a scripted Steamworks step from the store kit: ${Object.keys(RECIPES).join(', ')}. Stops before anything is submitted.`,
276
+ },
277
+ args: {
278
+ recipe: { type: 'positional', required: true, description: `One of: ${Object.keys(RECIPES).join(', ')}.` },
279
+ json: JSON_FLAG,
280
+ },
281
+ async run({ args }) {
282
+ const output = createOutput(args.json === true);
283
+ const context = loadProjectContext();
284
+ const result = await withPage((page) => runPortalRecipe(args.recipe, { page, root: context.root, gameId: context.metadata.gameId, log: output.info }));
285
+ output.result({ ok: true, ...result });
286
+ },
287
+ });
288
+ const closeCommand = defineCommand({
289
+ meta: { name: 'close', description: 'Close the Steamworks window. The login is kept in its profile for next time.' },
290
+ args: { json: JSON_FLAG },
291
+ async run({ args }) {
292
+ const output = createOutput(args.json === true);
293
+ const closed = await closePortal();
294
+ output.info(closed ? 'Closed the Steamworks window.' : 'No Steamworks window was open.');
295
+ output.result({ ok: true, closed });
296
+ },
297
+ });
298
+ export const steamPortalCommand = defineCommand({
299
+ meta: {
300
+ name: 'portal',
301
+ description: 'Drive the Steamworks website in a Chrome window the creator logs in to: look, fill, upload, click, run steps.',
302
+ },
303
+ subCommands: {
304
+ open: openCommand,
305
+ status: statusCommand,
306
+ look: lookCommand,
307
+ text: textCommand,
308
+ screenshot: screenshotCommand,
309
+ goto: gotoCommand,
310
+ click: clickCommand,
311
+ fill: fillCommand,
312
+ upload: uploadCommand,
313
+ select: selectCommand,
314
+ check: checkCommand,
315
+ run: runCommand,
316
+ close: closeCommand,
317
+ },
318
+ });
319
+ //# sourceMappingURL=steam-portal.js.map