@bitmagic/cli 0.1.0

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 (79) hide show
  1. package/dist/auth/device-flow.d.ts +18 -0
  2. package/dist/auth/device-flow.js +130 -0
  3. package/dist/auth/device-flow.js.map +1 -0
  4. package/dist/auth/session.d.ts +3 -0
  5. package/dist/auth/session.js +19 -0
  6. package/dist/auth/session.js.map +1 -0
  7. package/dist/bin.d.ts +2 -0
  8. package/dist/bin.js +18 -0
  9. package/dist/bin.js.map +1 -0
  10. package/dist/cli.d.ts +70 -0
  11. package/dist/cli.js +96 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/commands/check.d.ts +1 -0
  14. package/dist/commands/check.js +22 -0
  15. package/dist/commands/check.js.map +1 -0
  16. package/dist/commands/dev.d.ts +6 -0
  17. package/dist/commands/dev.js +89 -0
  18. package/dist/commands/dev.js.map +1 -0
  19. package/dist/commands/generate.d.ts +62 -0
  20. package/dist/commands/generate.js +348 -0
  21. package/dist/commands/generate.js.map +1 -0
  22. package/dist/commands/init.d.ts +23 -0
  23. package/dist/commands/init.js +123 -0
  24. package/dist/commands/init.js.map +1 -0
  25. package/dist/commands/login.d.ts +6 -0
  26. package/dist/commands/login.js +36 -0
  27. package/dist/commands/login.js.map +1 -0
  28. package/dist/commands/logout.d.ts +6 -0
  29. package/dist/commands/logout.js +18 -0
  30. package/dist/commands/logout.js.map +1 -0
  31. package/dist/commands/verify.d.ts +6 -0
  32. package/dist/commands/verify.js +124 -0
  33. package/dist/commands/verify.js.map +1 -0
  34. package/dist/commands/whoami.d.ts +11 -0
  35. package/dist/commands/whoami.js +29 -0
  36. package/dist/commands/whoami.js.map +1 -0
  37. package/dist/config/credentials.d.ts +13 -0
  38. package/dist/config/credentials.js +114 -0
  39. package/dist/config/credentials.js.map +1 -0
  40. package/dist/config/environments.d.ts +21 -0
  41. package/dist/config/environments.js +67 -0
  42. package/dist/config/environments.js.map +1 -0
  43. package/dist/errors.d.ts +8 -0
  44. package/dist/errors.js +13 -0
  45. package/dist/errors.js.map +1 -0
  46. package/dist/generate/apply-patches.d.ts +7 -0
  47. package/dist/generate/apply-patches.js +97 -0
  48. package/dist/generate/apply-patches.js.map +1 -0
  49. package/dist/generate/stream.d.ts +33 -0
  50. package/dist/generate/stream.js +185 -0
  51. package/dist/generate/stream.js.map +1 -0
  52. package/dist/http/client.d.ts +6 -0
  53. package/dist/http/client.js +49 -0
  54. package/dist/http/client.js.map +1 -0
  55. package/dist/project/context.d.ts +18 -0
  56. package/dist/project/context.js +67 -0
  57. package/dist/project/context.js.map +1 -0
  58. package/dist/scaffold/aliases.d.ts +27 -0
  59. package/dist/scaffold/aliases.js +51 -0
  60. package/dist/scaffold/aliases.js.map +1 -0
  61. package/dist/scaffold/engine-download.d.ts +27 -0
  62. package/dist/scaffold/engine-download.js +78 -0
  63. package/dist/scaffold/engine-download.js.map +1 -0
  64. package/dist/scaffold/project-files.d.ts +15 -0
  65. package/dist/scaffold/project-files.js +225 -0
  66. package/dist/scaffold/project-files.js.map +1 -0
  67. package/dist/scaffold/project.d.ts +28 -0
  68. package/dist/scaffold/project.js +118 -0
  69. package/dist/scaffold/project.js.map +1 -0
  70. package/dist/verify/browser.d.ts +13 -0
  71. package/dist/verify/browser.js +75 -0
  72. package/dist/verify/browser.js.map +1 -0
  73. package/dist/verify/classify.d.ts +29 -0
  74. package/dist/verify/classify.js +59 -0
  75. package/dist/verify/classify.js.map +1 -0
  76. package/dist/verify/wait-for-server.d.ts +7 -0
  77. package/dist/verify/wait-for-server.js +25 -0
  78. package/dist/verify/wait-for-server.js.map +1 -0
  79. package/package.json +70 -0
@@ -0,0 +1,348 @@
1
+ import { defineCommand } from 'citty';
2
+ import * as path from 'path';
3
+ import { CliError } from '../errors.js';
4
+ import { loadProjectContext } from '../project/context.js';
5
+ import { aliasSourceDir } from '../scaffold/aliases.js';
6
+ import { resolveEnvironment, resolveAuth0ClientId } from '../config/environments.js';
7
+ import { readDefaultEnvironment } from '../config/credentials.js';
8
+ import { getAccessToken } from '../auth/session.js';
9
+ import { requestGeneration } from '../generate/stream.js';
10
+ import { applyPatchesToWorld } from '../generate/apply-patches.js';
11
+ function parseNumberFlag(flagName, raw, options = {}) {
12
+ if (raw === undefined)
13
+ return undefined;
14
+ const value = Number(raw);
15
+ if (!Number.isFinite(value)) {
16
+ throw new CliError(`--${flagName} must be a number, got ${JSON.stringify(raw)}.`);
17
+ }
18
+ if (options.integer && !Number.isInteger(value)) {
19
+ throw new CliError(`--${flagName} must be a whole number, got ${JSON.stringify(raw)}.`);
20
+ }
21
+ if (options.min !== undefined && value < options.min) {
22
+ throw new CliError(`--${flagName} must be at least ${options.min}, got ${value}.`);
23
+ }
24
+ if (options.max !== undefined && value > options.max) {
25
+ throw new CliError(`--${flagName} must be at most ${options.max}, got ${value}.`);
26
+ }
27
+ return value;
28
+ }
29
+ export function buildSkyboxBody(args) {
30
+ return {
31
+ description: args.description,
32
+ ...(args.referenceImageUrl !== undefined ? { referenceImageUrl: args.referenceImageUrl } : {}),
33
+ };
34
+ }
35
+ export function buildSoundBody(args) {
36
+ const durationSeconds = parseNumberFlag('duration', args.duration, { min: 0.5, max: 22 });
37
+ const promptInfluence = parseNumberFlag('prompt-influence', args.promptInfluence, { min: 0, max: 1 });
38
+ return {
39
+ prompt: args.prompt,
40
+ ...(durationSeconds !== undefined ? { durationSeconds } : {}),
41
+ ...(promptInfluence !== undefined ? { promptInfluence } : {}),
42
+ ...(args.loop !== undefined ? { loop: args.loop } : {}),
43
+ };
44
+ }
45
+ export function buildImageBody(args) {
46
+ const width = parseNumberFlag('width', args.width, { min: 1, integer: true });
47
+ const height = parseNumberFlag('height', args.height, { min: 1, integer: true });
48
+ return {
49
+ name: args.name,
50
+ prompt: args.prompt,
51
+ ...(width !== undefined ? { width } : {}),
52
+ ...(height !== undefined ? { height } : {}),
53
+ ...(args.referenceImageUrl !== undefined ? { referenceImageUrl: args.referenceImageUrl } : {}),
54
+ };
55
+ }
56
+ export function buildCharacterBody(args) {
57
+ const headScale = parseNumberFlag('head-scale', args.headScale, { min: 50, max: 300 });
58
+ return {
59
+ name: args.name,
60
+ prompt: args.prompt,
61
+ ...(args.applyToPlayer !== undefined ? { applyToPlayer: args.applyToPlayer } : {}),
62
+ ...(headScale !== undefined ? { headScale } : {}),
63
+ };
64
+ }
65
+ export function buildAnimationBody(args) {
66
+ const length = parseNumberFlag('length', args.length);
67
+ const cfgScale = parseNumberFlag('cfg-scale', args.cfgScale);
68
+ const seed = parseNumberFlag('seed', args.seed);
69
+ return {
70
+ description: args.description,
71
+ ...(length !== undefined ? { length } : {}),
72
+ ...(cfgScale !== undefined ? { cfgScale } : {}),
73
+ ...(seed !== undefined ? { seed } : {}),
74
+ ...(args.footIk !== undefined ? { footIk: args.footIk } : {}),
75
+ ...(args.inPlace !== undefined ? { inPlace: args.inPlace } : {}),
76
+ ...(args.loop !== undefined ? { loop: args.loop } : {}),
77
+ };
78
+ }
79
+ function defaultDeps() {
80
+ return {
81
+ fetch: globalThis.fetch,
82
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
83
+ log: (message) => console.log(message),
84
+ };
85
+ }
86
+ /**
87
+ * Everything up to and including the network round-trip: resolves the project, the world.json
88
+ * path, and an access token, then calls the generator. Split out from `runAssetGeneration` so
89
+ * that function can wrap exactly this span in one try/catch — every error surfaced from in here
90
+ * happened strictly before any patch could have arrived.
91
+ */
92
+ async function requestAssetGeneration(type, body, deps) {
93
+ const context = loadProjectContext(deps.startDir);
94
+ // The 'work' alias is the scaffold's one source of truth for where the creator's game (and its
95
+ // world.json) lives — see scaffold/aliases.ts. Hardcoding 'src/work' here would be a second
96
+ // source of truth that silently drifts if the scaffold layout ever moves.
97
+ const worldPath = path.join(context.root, aliasSourceDir('work'), 'world.json');
98
+ const environment = resolveEnvironment({ storedDefault: readDefaultEnvironment(deps.baseDir) });
99
+ const clientId = resolveAuth0ClientId(environment);
100
+ const token = await getAccessToken(environment, clientId, { fetch: deps.fetch, sleep: deps.sleep, now: deps.now }, deps.baseDir);
101
+ const requestBody = { gameId: context.metadata.gameId, ...body };
102
+ const outcome = await requestGeneration(environment, token, type, requestBody, {
103
+ fetch: deps.fetch,
104
+ onProgress: deps.log,
105
+ });
106
+ return { outcome, worldPath };
107
+ }
108
+ /**
109
+ * Runs one `bitmagic generate <subcommand>`: requests the asset, then applies the returned
110
+ * patches to world.json. Every failure path says explicitly whether world.json changed, because
111
+ * a creator (or the agent driving this CLI) who does not know has to go look for themselves:
112
+ *
113
+ * - Anything that fails before a `result`/`error` frame arrives (auth, ownership, spark balance,
114
+ * a network error, ...) — world.json is untouched; the message says so.
115
+ * - A terminal `success: false` outcome — asset-core's `fail()` always carries an empty patch
116
+ * array (see shared/asset-core/src/result.ts), so this is also an unchanged world.json.
117
+ * - A successful outcome whose patches could not be written (a corrupt world.json, an
118
+ * unrecognised patch shape) — distinct from the above: the Forger may already have billed for
119
+ * this generation, so the message says the asset was NOT lost, only not yet applied.
120
+ */
121
+ export async function runAssetGeneration(type, label, body, deps = defaultDeps()) {
122
+ let result;
123
+ try {
124
+ result = await requestAssetGeneration(type, body, deps);
125
+ }
126
+ catch (error) {
127
+ if (error instanceof CliError) {
128
+ throw new CliError(`${error.message} world.json is unchanged.`);
129
+ }
130
+ throw error;
131
+ }
132
+ const { outcome, worldPath } = result;
133
+ if (!outcome.success) {
134
+ throw new CliError(`${outcome.message} world.json is unchanged.`);
135
+ }
136
+ let applyResult;
137
+ try {
138
+ applyResult = applyPatchesToWorld(worldPath, outcome.patches);
139
+ }
140
+ catch (error) {
141
+ if (error instanceof CliError) {
142
+ throw new CliError(`Generated ${label} successfully, but could not write it into world.json: ${error.message} ` +
143
+ 'world.json is unchanged — the asset was generated (and may have been billed) but is not yet in your project.');
144
+ }
145
+ throw error;
146
+ }
147
+ deps.log(`Generated ${label}: ${outcome.message}`);
148
+ for (const line of applyResult.summary) {
149
+ deps.log(` - ${line}`);
150
+ }
151
+ deps.log(`world.json updated (${applyResult.applied} patch${applyResult.applied === 1 ? '' : 'es'}).`);
152
+ deps.log('If `bitmagic dev` is running, reload the browser tab to see the change.');
153
+ }
154
+ // ============================================================================
155
+ // Subcommands
156
+ // ============================================================================
157
+ // Multi-word flags are declared with their kebab-case spelling as the literal arg key (rather
158
+ // than relying on citty's automatic camelCase<->kebab-case aliasing) so that `--help` — an
159
+ // agent's actual discovery mechanism for what it can ask for — prints exactly the flag spelling
160
+ // documented for this command, instead of the camelCase form citty would show by default.
161
+ // Reading them back out of `args` below therefore uses the same kebab-case bracket key.
162
+ const skyboxCommand = defineCommand({
163
+ meta: { name: 'skybox', description: 'Generate a 360° skybox and apply it to world.json.' },
164
+ args: {
165
+ description: {
166
+ type: 'string',
167
+ required: true,
168
+ description: 'Description of the skybox environment.',
169
+ },
170
+ 'reference-image-url': {
171
+ type: 'string',
172
+ description: 'Style/composition reference image URL forwarded to the image model.',
173
+ },
174
+ },
175
+ async run({ args }) {
176
+ const body = buildSkyboxBody({
177
+ description: args.description,
178
+ referenceImageUrl: args['reference-image-url'],
179
+ });
180
+ await runAssetGeneration('skybox', 'skybox', body);
181
+ },
182
+ });
183
+ const soundCommand = defineCommand({
184
+ meta: { name: 'sound', description: 'Generate a sound effect and add it to world.json assets.' },
185
+ args: {
186
+ prompt: {
187
+ type: 'string',
188
+ required: true,
189
+ description: 'Text describing the SFX to generate.',
190
+ },
191
+ duration: {
192
+ type: 'string',
193
+ description: 'Length in seconds, 0.5-22; upstream auto-picks when omitted.',
194
+ },
195
+ 'prompt-influence': {
196
+ type: 'string',
197
+ description: 'How tightly to follow the prompt, 0-1.',
198
+ },
199
+ loop: {
200
+ type: 'boolean',
201
+ description: 'Ask upstream for a seamlessly loopable SFX.',
202
+ },
203
+ },
204
+ async run({ args }) {
205
+ const body = buildSoundBody({
206
+ prompt: args.prompt,
207
+ duration: args.duration,
208
+ promptInfluence: args['prompt-influence'],
209
+ loop: args.loop,
210
+ });
211
+ // The type on the wire is 'sound-effect' (see api-server/src/cli/cli-assets.ts's generator
212
+ // registry); the subcommand is named 'sound' for brevity, so these deliberately differ.
213
+ await runAssetGeneration('sound-effect', 'sound effect', body);
214
+ },
215
+ });
216
+ const imageCommand = defineCommand({
217
+ meta: { name: 'image', description: 'Generate an image asset and add it to world.json assets.' },
218
+ args: {
219
+ name: {
220
+ type: 'string',
221
+ required: true,
222
+ description: 'Unique asset name.',
223
+ },
224
+ prompt: {
225
+ type: 'string',
226
+ required: true,
227
+ description: 'Text description of the image to generate.',
228
+ },
229
+ width: {
230
+ type: 'string',
231
+ description: 'Image width in pixels. Omit to use the generator default.',
232
+ },
233
+ height: {
234
+ type: 'string',
235
+ description: 'Image height in pixels. Omit to use the generator default.',
236
+ },
237
+ 'reference-image-url': {
238
+ type: 'string',
239
+ description: 'Style/composition reference image URL forwarded to the image model.',
240
+ },
241
+ },
242
+ async run({ args }) {
243
+ const body = buildImageBody({
244
+ name: args.name,
245
+ prompt: args.prompt,
246
+ width: args.width,
247
+ height: args.height,
248
+ referenceImageUrl: args['reference-image-url'],
249
+ });
250
+ await runAssetGeneration('image', 'image asset', body);
251
+ },
252
+ });
253
+ const characterCommand = defineCommand({
254
+ meta: { name: 'character', description: 'Generate a rigged character and add it to world.json assets.' },
255
+ args: {
256
+ name: {
257
+ type: 'string',
258
+ required: true,
259
+ description: 'Unique name for the character.',
260
+ },
261
+ prompt: {
262
+ type: 'string',
263
+ required: true,
264
+ description: 'Description of the character.',
265
+ },
266
+ 'apply-to-player': {
267
+ type: 'boolean',
268
+ description: 'Set this character as the PLAYER. Omit to use the generator default (true).',
269
+ },
270
+ 'head-scale': {
271
+ type: 'string',
272
+ description: 'Head size as a percentage of normal, 50-300. Omit to use the generator default.',
273
+ },
274
+ },
275
+ async run({ args }) {
276
+ const body = buildCharacterBody({
277
+ name: args.name,
278
+ prompt: args.prompt,
279
+ applyToPlayer: args['apply-to-player'],
280
+ headScale: args['head-scale'],
281
+ });
282
+ await runAssetGeneration('character', 'character', body);
283
+ },
284
+ });
285
+ const animationCommand = defineCommand({
286
+ meta: { name: 'animation', description: 'Generate an animation clip and add it to world.json assets.' },
287
+ args: {
288
+ description: {
289
+ type: 'string',
290
+ required: true,
291
+ description: 'Simple action phrase describing the animation, e.g. "a person swimming slowly".',
292
+ },
293
+ length: {
294
+ type: 'string',
295
+ description: 'Animation length in seconds. Omit to use the generator default.',
296
+ },
297
+ 'cfg-scale': {
298
+ type: 'string',
299
+ description: 'Classifier-free guidance scale; higher follows the prompt more closely. Omit to use the generator default.',
300
+ },
301
+ seed: {
302
+ type: 'string',
303
+ description: 'Random seed for reproducibility. Omit for random results.',
304
+ },
305
+ 'foot-ik': {
306
+ type: 'boolean',
307
+ // The generator schema defaults footIk to true; the negative form is the only one worth
308
+ // documenting since the positive is already the default. citty auto-derives --no-foot-ik
309
+ // for any boolean flag; `negativeDescription` just makes --help show it without us having
310
+ // to also restate `default: true` here (which would defeat "omit when absent").
311
+ negativeDescription: 'Disable foot IK — use for airborne animations like jumping.',
312
+ },
313
+ 'in-place': {
314
+ type: 'boolean',
315
+ description: 'Keep the character in place, e.g. for idle animations.',
316
+ },
317
+ loop: {
318
+ type: 'boolean',
319
+ description: 'Whether the animation should loop continuously.',
320
+ },
321
+ },
322
+ async run({ args }) {
323
+ const body = buildAnimationBody({
324
+ description: args.description,
325
+ length: args.length,
326
+ cfgScale: args['cfg-scale'],
327
+ seed: args.seed,
328
+ footIk: args['foot-ik'],
329
+ inPlace: args['in-place'],
330
+ loop: args.loop,
331
+ });
332
+ await runAssetGeneration('animation', 'animation', body);
333
+ },
334
+ });
335
+ export const generateCommand = defineCommand({
336
+ meta: {
337
+ name: 'generate',
338
+ description: 'Generate a game asset from your own agent tool and apply it to world.json.',
339
+ },
340
+ subCommands: {
341
+ skybox: skyboxCommand,
342
+ sound: soundCommand,
343
+ image: imageCommand,
344
+ character: characterCommand,
345
+ animation: animationCommand,
346
+ },
347
+ });
348
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ export declare const initCommand: import("citty").CommandDef<{
2
+ readonly dir: {
3
+ readonly type: "positional";
4
+ readonly required: false;
5
+ readonly description: "Directory to create the project in.";
6
+ };
7
+ readonly template: {
8
+ readonly type: "string";
9
+ readonly description: "Template id (see templates in the engine bundle).";
10
+ };
11
+ readonly name: {
12
+ readonly type: "string";
13
+ readonly description: "Game name.";
14
+ };
15
+ readonly env: {
16
+ readonly type: "string";
17
+ readonly description: "Environment (dev, prod, local).";
18
+ };
19
+ readonly force: {
20
+ readonly type: "boolean";
21
+ readonly description: "Scaffold into a non-empty directory.";
22
+ };
23
+ }>;
@@ -0,0 +1,123 @@
1
+ import { defineCommand } from 'citty';
2
+ import * as fs from 'fs';
3
+ import * as os from 'os';
4
+ import * as path from 'path';
5
+ import { spawnSync } from 'child_process';
6
+ import { CliError } from '../errors.js';
7
+ import { resolveEnvironment, resolveAuth0ClientId } from '../config/environments.js';
8
+ import { readDefaultEnvironment } from '../config/credentials.js';
9
+ import { getAccessToken } from '../auth/session.js';
10
+ import { apiPost } from '../http/client.js';
11
+ import { fetchEngineManifest, downloadAndVerify, extractEngine } from '../scaffold/engine-download.js';
12
+ import { resolveTemplate, scaffoldProject } from '../scaffold/project.js';
13
+ const deps = {
14
+ fetch: globalThis.fetch,
15
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
16
+ };
17
+ /** Throws a CliError naming the directory if it exists, is non-empty, and `force` was not given. */
18
+ function assertScaffoldable(dir, force) {
19
+ if (force)
20
+ return;
21
+ let entries;
22
+ try {
23
+ entries = fs.readdirSync(dir);
24
+ }
25
+ catch (err) {
26
+ const code = err.code;
27
+ if (code === 'ENOENT') {
28
+ // Directory doesn't exist yet: nothing to protect against.
29
+ return;
30
+ }
31
+ throw new CliError(`Cannot read directory ${dir}: ${code ?? String(err)}`);
32
+ }
33
+ if (entries.length > 0) {
34
+ throw new CliError(`${dir} already exists and is not empty. Pass --force to scaffold into it anyway.`);
35
+ }
36
+ }
37
+ /**
38
+ * `npm install` is the least reliable step: network flakiness or a broken local npm config are
39
+ * common and have nothing to do with whether the scaffold itself is correct. Its failure must
40
+ * not be reported as a scaffold failure — the project on disk is already complete and valid,
41
+ * only its dependencies are missing. Returns whether it succeeded so callers can adjust what
42
+ * they tell the creator to do next — `bitmagic check` cannot run without node_modules.
43
+ */
44
+ function installDependencies(dir) {
45
+ const result = spawnSync('npm', ['install'], { cwd: dir, stdio: 'inherit' });
46
+ if (result.error || result.status !== 0) {
47
+ console.warn('Warning: `npm install` failed. The project was scaffolded successfully — only its ' +
48
+ `dependencies are missing. cd into ${dir} and run \`npm install\` yourself.`);
49
+ return false;
50
+ }
51
+ return true;
52
+ }
53
+ function printNextSteps(dir, gameId, installSucceeded) {
54
+ console.log('');
55
+ console.log(`Created ${dir} (game ${gameId}).`);
56
+ console.log('');
57
+ console.log('Next steps:');
58
+ console.log(` cd ${dir}`);
59
+ if (installSucceeded) {
60
+ console.log(' bitmagic check');
61
+ }
62
+ else {
63
+ console.log(' npm install');
64
+ console.log(' bitmagic check');
65
+ }
66
+ }
67
+ export const initCommand = defineCommand({
68
+ meta: { name: 'init', description: 'Scaffold a new Bitmagic game project.' },
69
+ args: {
70
+ dir: { type: 'positional', required: false, description: 'Directory to create the project in.' },
71
+ template: { type: 'string', description: 'Template id (see templates in the engine bundle).' },
72
+ name: { type: 'string', description: 'Game name.' },
73
+ env: { type: 'string', description: 'Environment (dev, prod, local).' },
74
+ force: { type: 'boolean', description: 'Scaffold into a non-empty directory.' },
75
+ },
76
+ async run({ args }) {
77
+ const targetDir = path.resolve(args.dir ?? '.');
78
+ assertScaffoldable(targetDir, args.force === true);
79
+ const environment = resolveEnvironment({
80
+ explicit: args.env,
81
+ storedDefault: readDefaultEnvironment(),
82
+ });
83
+ const clientId = resolveAuth0ClientId(environment);
84
+ const token = await getAccessToken(environment, clientId, deps);
85
+ console.log('Fetching the engine manifest…');
86
+ const manifest = await fetchEngineManifest(environment, token, { fetch: globalThis.fetch });
87
+ const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bitmagic-init-'));
88
+ try {
89
+ const tarballPath = path.join(workDir, 'engine.tar.gz');
90
+ console.log(`Downloading engine ${manifest.version}…`);
91
+ await downloadAndVerify(manifest.tarballUrl, manifest.sha256, tarballPath, {
92
+ fetch: globalThis.fetch,
93
+ });
94
+ const extracted = path.join(workDir, 'extracted');
95
+ const templatesDir = path.join(workDir, 'templates');
96
+ await extractEngine(tarballPath, extracted, templatesDir);
97
+ const template = resolveTemplate(templatesDir, args.template);
98
+ // Minted only after the engine is in hand: a failed download must not leave an orphaned,
99
+ // unreachable game row behind.
100
+ const projectName = args.name ?? path.basename(targetDir);
101
+ const created = await apiPost(environment, '/api/cli/v1/games', token, { genre: template.genre, name: projectName }, { fetch: globalThis.fetch });
102
+ scaffoldProject({
103
+ targetDir,
104
+ extractedEngineDir: extracted,
105
+ templatesDir,
106
+ template,
107
+ metadata: {
108
+ gameId: created.gameId,
109
+ engineVersion: manifest.version,
110
+ genre: template.genre,
111
+ template: template.id,
112
+ },
113
+ projectName,
114
+ });
115
+ const installSucceeded = installDependencies(targetDir);
116
+ printNextSteps(targetDir, created.gameId, installSucceeded);
117
+ }
118
+ finally {
119
+ fs.rmSync(workDir, { recursive: true, force: true });
120
+ }
121
+ },
122
+ });
123
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare const loginCommand: import("citty").CommandDef<{
2
+ readonly env: {
3
+ readonly type: "string";
4
+ readonly description: "Environment to log in to (dev, prod, local).";
5
+ };
6
+ }>;
@@ -0,0 +1,36 @@
1
+ import { defineCommand } from 'citty';
2
+ import { resolveEnvironment, resolveAuth0ClientId } from '../config/environments.js';
3
+ import { writeCredentials, writeDefaultEnvironment, readDefaultEnvironment } from '../config/credentials.js';
4
+ import { requestDeviceCode, pollForToken } from '../auth/device-flow.js';
5
+ import { runWhoami } from './whoami.js';
6
+ const deps = {
7
+ fetch: globalThis.fetch,
8
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
9
+ };
10
+ export const loginCommand = defineCommand({
11
+ meta: { name: 'login', description: 'Authenticate with Bitmagic via your browser.' },
12
+ args: {
13
+ env: { type: 'string', description: 'Environment to log in to (dev, prod, local).' },
14
+ },
15
+ async run({ args }) {
16
+ const environment = resolveEnvironment({
17
+ explicit: args.env,
18
+ storedDefault: readDefaultEnvironment(),
19
+ });
20
+ const clientId = resolveAuth0ClientId(environment);
21
+ const grant = await requestDeviceCode(environment, clientId, deps);
22
+ // Always print the URL: agent tools frequently run without a browser.
23
+ console.log(`Open: ${grant.verificationUriComplete}`);
24
+ console.log(`Code: ${grant.userCode}`);
25
+ console.log('Waiting for approval…');
26
+ const credentials = await pollForToken(environment, clientId, grant, deps);
27
+ writeCredentials(environment.name, credentials);
28
+ writeDefaultEnvironment(environment.name);
29
+ // Prove the login rather than assuming it: a token Auth0 accepted is not the same as an
30
+ // account api-server accepts with the Admin role.
31
+ const me = await runWhoami(environment.name);
32
+ console.log(`Logged in to ${environment.name} as ${me.userId}.`);
33
+ console.log(`roles: ${me.roles.length > 0 ? me.roles.join(', ') : '(none)'}`);
34
+ },
35
+ });
36
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC7G,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,UAAU,CAAC,KAAK;IACvB,KAAK,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;CAC/E,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;IACxC,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,8CAA8C,EAAE;IACpF,IAAI,EAAE;QACJ,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8CAA8C,EAAE;KACrF;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAChB,MAAM,WAAW,GAAG,kBAAkB,CAAC;YACrC,QAAQ,EAAE,IAAI,CAAC,GAAG;YAClB,aAAa,EAAE,sBAAsB,EAAE;SACxC,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;QAEnD,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QAEnE,sEAAsE;QACtE,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,uBAAuB,EAAE,CAAC,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QAErC,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC3E,gBAAgB,CAAC,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAChD,uBAAuB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAE1C,wFAAwF;QACxF,kDAAkD;QAClD,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,gBAAgB,WAAW,CAAC,IAAI,OAAO,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IAChF,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare const logoutCommand: import("citty").CommandDef<{
2
+ readonly env: {
3
+ readonly type: "string";
4
+ readonly description: "Environment to log out of (dev, prod, local).";
5
+ };
6
+ }>;
@@ -0,0 +1,18 @@
1
+ import { defineCommand } from 'citty';
2
+ import { resolveEnvironment } from '../config/environments.js';
3
+ import { clearCredentials, readDefaultEnvironment } from '../config/credentials.js';
4
+ export const logoutCommand = defineCommand({
5
+ meta: { name: 'logout', description: 'Remove stored credentials for an environment.' },
6
+ args: {
7
+ env: { type: 'string', description: 'Environment to log out of (dev, prod, local).' },
8
+ },
9
+ run({ args }) {
10
+ const environment = resolveEnvironment({
11
+ explicit: args.env,
12
+ storedDefault: readDefaultEnvironment(),
13
+ });
14
+ clearCredentials(environment.name);
15
+ console.log(`Logged out of ${environment.name}.`);
16
+ },
17
+ });
18
+ //# sourceMappingURL=logout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEpF,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;IACzC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+CAA+C,EAAE;IACtF,IAAI,EAAE;QACJ,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+CAA+C,EAAE;KACtF;IACD,GAAG,CAAC,EAAE,IAAI,EAAE;QACV,MAAM,WAAW,GAAG,kBAAkB,CAAC;YACrC,QAAQ,EAAE,IAAI,CAAC,GAAG;YAClB,aAAa,EAAE,sBAAsB,EAAE;SACxC,CAAC,CAAC;QACH,gBAAgB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,iBAAiB,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC;IACpD,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare const verifyCommand: import("citty").CommandDef<{
2
+ readonly timeout: {
3
+ readonly type: "string";
4
+ readonly description: "Settle time in ms (default 15000).";
5
+ };
6
+ }>;