@bitmagic/cli 0.1.7 → 0.1.9
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.
- package/README.md +140 -0
- package/dist/auth/session.js +1 -1
- package/dist/auth/session.js.map +1 -1
- package/dist/cli.d.ts +29 -0
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/build.d.ts +1 -0
- package/dist/commands/build.js +16 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/publish.d.ts +44 -0
- package/dist/commands/publish.js +202 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/upgrade.d.ts +36 -0
- package/dist/commands/upgrade.js +170 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/verify.js +8 -9
- package/dist/commands/verify.js.map +1 -1
- package/dist/forge/apply-modifications.js +7 -13
- package/dist/forge/apply-modifications.js.map +1 -1
- package/dist/project/context.js +6 -1
- package/dist/project/context.js.map +1 -1
- package/dist/project/package-manager.d.ts +17 -0
- package/dist/project/package-manager.js +48 -0
- package/dist/project/package-manager.js.map +1 -0
- package/dist/publish/bundle.d.ts +37 -0
- package/dist/publish/bundle.js +88 -0
- package/dist/publish/bundle.js.map +1 -0
- package/dist/publish/client.d.ts +94 -0
- package/dist/publish/client.js +150 -0
- package/dist/publish/client.js.map +1 -0
- package/dist/publish/fingerprint.d.ts +27 -0
- package/dist/publish/fingerprint.js +84 -0
- package/dist/publish/fingerprint.js.map +1 -0
- package/dist/publish/meta-block.d.ts +26 -0
- package/dist/publish/meta-block.js +48 -0
- package/dist/publish/meta-block.js.map +1 -0
- package/dist/scaffold/dependency-diff.d.ts +28 -0
- package/dist/scaffold/dependency-diff.js +28 -0
- package/dist/scaffold/dependency-diff.js.map +1 -0
- package/dist/scaffold/project-files.d.ts +22 -0
- package/dist/scaffold/project-files.js +114 -5
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/project.d.ts +17 -0
- package/dist/scaffold/project.js +24 -4
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scaffold/upgrade-project.d.ts +35 -0
- package/dist/scaffold/upgrade-project.js +63 -0
- package/dist/scaffold/upgrade-project.js.map +1 -0
- package/dist/verify/result.d.ts +60 -0
- package/dist/verify/result.js +74 -0
- package/dist/verify/result.js.map +1 -0
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# @bitmagic/cli
|
|
2
|
+
|
|
3
|
+
Build Bitmagic games from your own agent tool (Claude Code, Codex, OpenCode, ...) instead of the
|
|
4
|
+
web Creator UI. `bitmagic init` scaffolds a project with the engine vendored in as source, your
|
|
5
|
+
agent edits `src/work/`, and `bitmagic verify` / `bitmagic publish` take it to bitmagic.ai — no
|
|
6
|
+
web project or per-session pod required.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g @bitmagic/cli
|
|
12
|
+
bitmagic --version
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bitmagic login # device-code auth in your browser
|
|
19
|
+
bitmagic init my-game # scaffold a new project into ./my-game
|
|
20
|
+
cd my-game
|
|
21
|
+
bitmagic check # typecheck against the vendored engine
|
|
22
|
+
bitmagic dev # build, then serve with live rebuilds
|
|
23
|
+
bitmagic verify # boot the game in a real browser and report what broke
|
|
24
|
+
bitmagic publish # verify-gate, build, and ship — private by default
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Commands
|
|
28
|
+
|
|
29
|
+
| Command | What it does |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `bitmagic login [--env <env>]` | Device-code auth against Auth0; stores credentials per environment. |
|
|
32
|
+
| `bitmagic logout [--env <env>]` | Remove stored credentials for an environment. |
|
|
33
|
+
| `bitmagic whoami [--env <env>]` | Show the identity the CLI is authenticated as. |
|
|
34
|
+
| `bitmagic init [dir] [--template <id>] [--name <name>] [--env <env>] [--force]` | Scaffold a new project: mints a game, downloads the vendored engine, writes `AGENTS.md` and templates. |
|
|
35
|
+
| `bitmagic check` | Typecheck (`tsc --noEmit`) against the vendored engine. Fast; does not prove the game runs. |
|
|
36
|
+
| `bitmagic dev [--port <port>]` | Build, then serve with `tsc --watch` + `vite`, default port 3010. |
|
|
37
|
+
| `bitmagic verify [--timeout <ms>]` | Build, boot the game in a headless browser, and report what broke. Writes the artifacts the publish gate reads (see below). |
|
|
38
|
+
| `bitmagic build` | Bundle the game into one self-contained `.bitmagic/build/index.html`. Rarely needs to be run by hand — `publish` builds automatically when the project changed. |
|
|
39
|
+
| `bitmagic publish [--visibility public\|private] [--name <name>] [--description <desc>] [--force]` | Verify-gate, build if needed, and upload straight to GCS. **Private unless `--visibility public` is passed.** |
|
|
40
|
+
| `bitmagic upgrade [--engine <version>] [--force]` | Refresh vendored platform files to the currently published engine. Refuses a dirty git tree unless `--force`. |
|
|
41
|
+
| `bitmagic generate <skybox\|sound\|image\|character\|animation> ...` | Generate one asset directly into `src/work/world.json`. Costs sparks. |
|
|
42
|
+
| `bitmagic forge --prompt "..." [--city\|--dungeon\|--platformer\|--freeform] [--name <name>] [--resume <jobId>]` | Design and bake a whole playable level into `world.json`. Slow and expensive; supports `--resume`. |
|
|
43
|
+
|
|
44
|
+
Every command exits non-zero on failure with a single-line message (no stack trace) and, where a
|
|
45
|
+
distinction is useful to an automated caller, a specific exit code — see **Exit codes** below.
|
|
46
|
+
|
|
47
|
+
## Environments
|
|
48
|
+
|
|
49
|
+
`--env` accepts `dev`, `prod`, or `local`. The environment used at `login` becomes the stored
|
|
50
|
+
default for subsequent commands until you log in to a different one.
|
|
51
|
+
|
|
52
|
+
## Project layout and artifacts
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
my-game/
|
|
56
|
+
src/work/ # your game — Game.ts, game.json, world.json
|
|
57
|
+
engine/ # vendored engine, read-only, replaced wholesale by `bitmagic upgrade`
|
|
58
|
+
AGENTS.md # guidance for the agent editing this project
|
|
59
|
+
.bitmagic/
|
|
60
|
+
build/
|
|
61
|
+
index.html # bitmagic build's output bundle
|
|
62
|
+
manifest.json # engineVersion, fingerprint, bytes, sha256, builtAt
|
|
63
|
+
verify/
|
|
64
|
+
result.json # bitmagic verify's verdict — ok, failures, warnings, fingerprint, at
|
|
65
|
+
screenshot.png # becomes the publish thumbnail
|
|
66
|
+
console.log # captured browser console output
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Publishing
|
|
70
|
+
|
|
71
|
+
`bitmagic publish` is two gated steps in one command:
|
|
72
|
+
|
|
73
|
+
1. **The verify gate.** Publish refuses to run unless `.bitmagic/verify/result.json` records a
|
|
74
|
+
passing run of the **current** project state. "Current" is decided by a content fingerprint —
|
|
75
|
+
a hash of your project's files — not a timestamp: editing any file after verifying makes the
|
|
76
|
+
record stale immediately, while an unmodified project stays fresh no matter how much time has
|
|
77
|
+
passed. There is no way to check freshness except running `bitmagic verify` again.
|
|
78
|
+
`.git/`, `node_modules/`, `dist/`, `.bitmagic/` and `.vite-cache/` are excluded, so committing
|
|
79
|
+
between `bitmagic verify` and `bitmagic publish` does **not** invalidate the verify.
|
|
80
|
+
2. **The build.** If the project has changed since the last `.bitmagic/build/manifest.json`,
|
|
81
|
+
publish rebuilds automatically; a prior manual `bitmagic build` is never required, only useful
|
|
82
|
+
if you want to inspect `.bitmagic/build/index.html` before it ships.
|
|
83
|
+
|
|
84
|
+
**Publishing does not make a game public.** Every `bitmagic publish` uploads and registers the
|
|
85
|
+
game, but by default `visibility` is `private` — reachable by URL, not listed anywhere on
|
|
86
|
+
bitmagic.ai. You must pass `--visibility public` explicitly, every time you want the game listed;
|
|
87
|
+
omitting it on a later publish makes it private again — and takes the game's `/play/` page out of
|
|
88
|
+
service, so a previously-public game re-published bare is gone from bitmagic.ai until you publish
|
|
89
|
+
publicly again.
|
|
90
|
+
|
|
91
|
+
`--force` overrides a **stale or failed** verify verdict. It does **not** override a missing
|
|
92
|
+
verify record — publish's thumbnail comes from the verify screenshot, so `bitmagic verify` must
|
|
93
|
+
have produced at least one `.bitmagic/verify/screenshot.png`, ever, before `--force` has anything
|
|
94
|
+
to work with.
|
|
95
|
+
|
|
96
|
+
`--name` and `--description` apply to that one publish call only. They are never written back to
|
|
97
|
+
`src/work/game.json`, so pass them again on every future `bitmagic publish` if you want them to
|
|
98
|
+
stick — and note that writing them into `game.json` yourself would change the fingerprint and
|
|
99
|
+
invalidate the verify you just checked.
|
|
100
|
+
|
|
101
|
+
### Exit codes
|
|
102
|
+
|
|
103
|
+
`bitmagic publish` uses these to let a script or agent branch without parsing the message:
|
|
104
|
+
|
|
105
|
+
| Code | Meaning |
|
|
106
|
+
|---|---|
|
|
107
|
+
| 1 | Build failed |
|
|
108
|
+
| 2 | Not logged in |
|
|
109
|
+
| 3 | Verify missing, stale, or failed |
|
|
110
|
+
| 4 | Engine below the publish floor |
|
|
111
|
+
| 5 | Not the game's owner, or the account/game is banned |
|
|
112
|
+
| 6 | Upload or finalize failed — usually transient (network/storage), but also covers permanent failures such as an unknown game or a rejected request. Retry once; if it fails identically, stop and report it. |
|
|
113
|
+
|
|
114
|
+
`bitmagic build` alone exits 1 on a build failure and 0 on success.
|
|
115
|
+
|
|
116
|
+
## Upgrading
|
|
117
|
+
|
|
118
|
+
`bitmagic upgrade` refreshes the vendored `engine/`, the generated config files, and `index.html`
|
|
119
|
+
to the currently published engine version. It never touches `src/`, `world.json`, `game.json`,
|
|
120
|
+
`package.json`, or the agent guidance files (`AGENTS.md`, `CLAUDE.md`, `.claude/skills/`) — those
|
|
121
|
+
are yours. It refuses to run against a dirty git working tree unless `--force`, so the upgrade
|
|
122
|
+
always shows up as its own reviewable diff.
|
|
123
|
+
|
|
124
|
+
`upgrade` **reports** missing or outdated dependencies rather than installing them — it prints the
|
|
125
|
+
exact install command to run, using your project's own package manager (detected from
|
|
126
|
+
`pnpm-lock.yaml` / `yarn.lock`, defaulting to npm). A project scaffolded before a dependency was
|
|
127
|
+
added needs that command run once before `bitmagic build` (and therefore `bitmagic publish`) will
|
|
128
|
+
work.
|
|
129
|
+
|
|
130
|
+
A project scaffolded before `vite.publish.config.js` existed cannot be bundled at all: `bitmagic
|
|
131
|
+
build` stops with a message naming `bitmagic upgrade`, which vendors the file without touching
|
|
132
|
+
your game code. Run the printed install command afterwards if `upgrade` reports missing
|
|
133
|
+
dependencies too.
|
|
134
|
+
|
|
135
|
+
## Development
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pnpm run check # lint + typecheck (from cli/, or from the repo root for every project)
|
|
139
|
+
pnpm test # jest
|
|
140
|
+
```
|
package/dist/auth/session.js
CHANGED
|
@@ -6,7 +6,7 @@ const EXPIRY_SKEW_MS = 60_000;
|
|
|
6
6
|
export async function getAccessToken(environment, clientId, deps, baseDir = defaultBaseDir()) {
|
|
7
7
|
const stored = readCredentials(environment.name, baseDir);
|
|
8
8
|
if (!stored) {
|
|
9
|
-
throw new CliError(`You are not logged in to the "${environment.name}" environment. Run \`bitmagic login
|
|
9
|
+
throw new CliError(`You are not logged in to the "${environment.name}" environment. Run \`bitmagic login\`.`, 2);
|
|
10
10
|
}
|
|
11
11
|
const now = deps.now ?? Date.now;
|
|
12
12
|
if (stored.expiresAt - EXPIRY_SKEW_MS > now()) {
|
package/dist/auth/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/auth/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC7F,OAAO,EAAE,kBAAkB,EAAuB,MAAM,kBAAkB,CAAC;AAE3E,oEAAoE;AACpE,MAAM,cAAc,GAAG,MAAM,CAAC;AAE9B,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,WAAwB,EACxB,QAAgB,EAChB,IAAoB,EACpB,UAAkB,cAAc,EAAE;IAElC,MAAM,MAAM,GAAG,eAAe,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,QAAQ,CAChB,iCAAiC,WAAW,CAAC,IAAI,wCAAwC,
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/auth/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC7F,OAAO,EAAE,kBAAkB,EAAuB,MAAM,kBAAkB,CAAC;AAE3E,oEAAoE;AACpE,MAAM,cAAc,GAAG,MAAM,CAAC;AAE9B,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,WAAwB,EACxB,QAAgB,EAChB,IAAoB,EACpB,UAAkB,cAAc,EAAE;IAElC,MAAM,MAAM,GAAG,eAAe,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,QAAQ,CAChB,iCAAiC,WAAW,CAAC,IAAI,wCAAwC,EACzF,CAAC,CACF,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACjC,IAAI,MAAM,CAAC,SAAS,GAAG,cAAc,GAAG,GAAG,EAAE,EAAE,CAAC;QAC9C,OAAO,MAAM,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,WAAW,EAAE,QAAQ,EAAE,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAC7F,gBAAgB,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACvD,OAAO,SAAS,CAAC,WAAW,CAAC;AAC/B,CAAC"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -49,6 +49,16 @@ declare const rawCommands: {
|
|
|
49
49
|
readonly description: "Scaffold into a non-empty directory.";
|
|
50
50
|
};
|
|
51
51
|
}>;
|
|
52
|
+
upgrade: CommandDef<{
|
|
53
|
+
readonly engine: {
|
|
54
|
+
readonly type: "string";
|
|
55
|
+
readonly description: "Engine version to upgrade to (must be the currently published one).";
|
|
56
|
+
};
|
|
57
|
+
readonly force: {
|
|
58
|
+
readonly type: "boolean";
|
|
59
|
+
readonly description: "Upgrade even with uncommitted changes in the working tree.";
|
|
60
|
+
};
|
|
61
|
+
}>;
|
|
52
62
|
check: CommandDef<ArgsDef>;
|
|
53
63
|
dev: CommandDef<{
|
|
54
64
|
readonly port: {
|
|
@@ -62,6 +72,7 @@ declare const rawCommands: {
|
|
|
62
72
|
readonly description: "Settle time in ms (default 15000).";
|
|
63
73
|
};
|
|
64
74
|
}>;
|
|
75
|
+
build: CommandDef<ArgsDef>;
|
|
65
76
|
generate: CommandDef<ArgsDef>;
|
|
66
77
|
forge: CommandDef<{
|
|
67
78
|
readonly prompt: {
|
|
@@ -93,6 +104,24 @@ declare const rawCommands: {
|
|
|
93
104
|
readonly description: "Build a custom place (arena, fortress, camp).";
|
|
94
105
|
};
|
|
95
106
|
}>;
|
|
107
|
+
publish: CommandDef<{
|
|
108
|
+
readonly visibility: {
|
|
109
|
+
readonly type: "string";
|
|
110
|
+
readonly description: "\"public\" or \"private\" (default). Public also requires an unbanned profile.";
|
|
111
|
+
};
|
|
112
|
+
readonly name: {
|
|
113
|
+
readonly type: "string";
|
|
114
|
+
readonly description: "Override the published name for this publish only — never written to game.json.";
|
|
115
|
+
};
|
|
116
|
+
readonly description: {
|
|
117
|
+
readonly type: "string";
|
|
118
|
+
readonly description: "Override the published description for this publish only — never written to game.json.";
|
|
119
|
+
};
|
|
120
|
+
readonly force: {
|
|
121
|
+
readonly type: "boolean";
|
|
122
|
+
readonly description: "Publish despite a stale or failed verify. Does NOT bypass a missing verify record.";
|
|
123
|
+
};
|
|
124
|
+
}>;
|
|
96
125
|
};
|
|
97
126
|
type CommandName = keyof typeof rawCommands;
|
|
98
127
|
export declare const subCommands: Record<CommandName, CommandDef<ArgsDef>>;
|
package/dist/cli.js
CHANGED
|
@@ -4,11 +4,14 @@ import { loginCommand } from './commands/login.js';
|
|
|
4
4
|
import { logoutCommand } from './commands/logout.js';
|
|
5
5
|
import { whoamiCommand } from './commands/whoami.js';
|
|
6
6
|
import { initCommand } from './commands/init.js';
|
|
7
|
+
import { upgradeCommand } from './commands/upgrade.js';
|
|
7
8
|
import { checkCommand } from './commands/check.js';
|
|
8
9
|
import { devCommand } from './commands/dev.js';
|
|
9
10
|
import { verifyCommand } from './commands/verify.js';
|
|
11
|
+
import { buildCommand } from './commands/build.js';
|
|
10
12
|
import { generateCommand } from './commands/generate.js';
|
|
11
13
|
import { forgeCommand } from './commands/forge.js';
|
|
14
|
+
import { publishCommand } from './commands/publish.js';
|
|
12
15
|
// citty's runMain() catches every error thrown from a command's `run()` internally and calls
|
|
13
16
|
// process.exit() itself — it never rejects the promise it returns, so the `.catch()` below
|
|
14
17
|
// never runs for those. For anything that isn't citty's own internal CLIError class, its catch
|
|
@@ -87,11 +90,14 @@ const rawCommands = {
|
|
|
87
90
|
logout: logoutCommand,
|
|
88
91
|
whoami: whoamiCommand,
|
|
89
92
|
init: initCommand,
|
|
93
|
+
upgrade: upgradeCommand,
|
|
90
94
|
check: checkCommand,
|
|
91
95
|
dev: devCommand,
|
|
92
96
|
verify: verifyCommand,
|
|
97
|
+
build: buildCommand,
|
|
93
98
|
generate: generateCommand,
|
|
94
99
|
forge: forgeCommand,
|
|
100
|
+
publish: publishCommand,
|
|
95
101
|
};
|
|
96
102
|
export const subCommands = Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => [name, withCleanErrors(command)]));
|
|
97
103
|
// This module only builds `main` and exports it (along with `subCommands` and `withCleanErrors`)
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const buildCommand: import("citty").CommandDef<import("citty").ArgsDef>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineCommand } from 'citty';
|
|
2
|
+
import { loadProjectContext } from '../project/context.js';
|
|
3
|
+
import { runBuild } from '../publish/bundle.js';
|
|
4
|
+
export const buildCommand = defineCommand({
|
|
5
|
+
meta: {
|
|
6
|
+
name: 'build',
|
|
7
|
+
description: 'Bundle the game for publishing into .bitmagic/build/index.html.',
|
|
8
|
+
},
|
|
9
|
+
async run() {
|
|
10
|
+
const context = loadProjectContext();
|
|
11
|
+
console.log('Building…');
|
|
12
|
+
const manifest = runBuild(context.root, context.metadata.engineVersion);
|
|
13
|
+
console.log(`Bundle ready: .bitmagic/build/index.html (${manifest.bytes} bytes)`);
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;IACxC,IAAI,EAAE;QACJ,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,iEAAiE;KAC/E;IACD,KAAK,CAAC,GAAG;QACP,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QACxE,OAAO,CAAC,GAAG,CAAC,6CAA6C,QAAQ,CAAC,KAAK,SAAS,CAAC,CAAC;IACpF,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type BuildManifest } from '../publish/bundle.js';
|
|
2
|
+
/** `'public' | 'private'`, defaulting to private — a typo is rejected rather than silently
|
|
3
|
+
* coerced, matching the server's own asymmetric contract (see cli-publish.ts). */
|
|
4
|
+
export declare function resolveVisibility(raw: string | undefined): 'public' | 'private';
|
|
5
|
+
export interface PublishRunDeps {
|
|
6
|
+
fetch: typeof globalThis.fetch;
|
|
7
|
+
sleep: (ms: number) => Promise<void>;
|
|
8
|
+
/** Test-only override; production omits it and loadProjectContext walks up from process.cwd(). */
|
|
9
|
+
startDir?: string;
|
|
10
|
+
/** Test-only override; production omits it and the credential helpers read ~/.bitmagic. */
|
|
11
|
+
baseDir?: string;
|
|
12
|
+
log: (message: string) => void;
|
|
13
|
+
/** Injectable so a test can assert reuse-vs-rebuild without a real tsc/vite toolchain. */
|
|
14
|
+
runBuild: (root: string, engineVersion: string) => BuildManifest;
|
|
15
|
+
readBuildManifest: (root: string) => BuildManifest | null;
|
|
16
|
+
}
|
|
17
|
+
export interface PublishArgs {
|
|
18
|
+
visibility?: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
force?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The whole command. See the file header for why the order below cannot be reshuffled.
|
|
25
|
+
*/
|
|
26
|
+
export declare function runPublish(args: PublishArgs, deps?: PublishRunDeps): Promise<void>;
|
|
27
|
+
export declare const publishCommand: import("citty").CommandDef<{
|
|
28
|
+
readonly visibility: {
|
|
29
|
+
readonly type: "string";
|
|
30
|
+
readonly description: "\"public\" or \"private\" (default). Public also requires an unbanned profile.";
|
|
31
|
+
};
|
|
32
|
+
readonly name: {
|
|
33
|
+
readonly type: "string";
|
|
34
|
+
readonly description: "Override the published name for this publish only — never written to game.json.";
|
|
35
|
+
};
|
|
36
|
+
readonly description: {
|
|
37
|
+
readonly type: "string";
|
|
38
|
+
readonly description: "Override the published description for this publish only — never written to game.json.";
|
|
39
|
+
};
|
|
40
|
+
readonly force: {
|
|
41
|
+
readonly type: "boolean";
|
|
42
|
+
readonly description: "Publish despite a stale or failed verify. Does NOT bypass a missing verify record.";
|
|
43
|
+
};
|
|
44
|
+
}>;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bitmagic publish` — build, verify-gate, and ship one self-contained bundle straight to GCS.
|
|
3
|
+
*
|
|
4
|
+
* Two-phase, matching api-server's split (see `api-server/src/cli/cli-publish.ts`):
|
|
5
|
+
*
|
|
6
|
+
* 1. `POST /api/cli/v1/publish/begin` authorizes the publish, allocates a version, mints a
|
|
7
|
+
* signed PUT URL, and issues the exact meta-tag block the bundle must carry.
|
|
8
|
+
* 2. This process PUTs the bundle straight to GCS — the multi-MB HTML never passes through
|
|
9
|
+
* api-server.
|
|
10
|
+
* 3. `POST /api/cli/v1/publish/complete` verifies what actually landed and writes the row.
|
|
11
|
+
*
|
|
12
|
+
* The order below is load-bearing, not incidental:
|
|
13
|
+
*
|
|
14
|
+
* - The verify gate runs BEFORE the build, because a stale or failing verify means there is
|
|
15
|
+
* nothing worth building yet, and `bitmagic build` on top of a broken game just wastes time.
|
|
16
|
+
* - The thumbnail — the verify screenshot, uploaded through the same signed-upload lane
|
|
17
|
+
* `forge/upload-proxy.ts` bridges for the browser — is uploaded BEFORE `begin`, because
|
|
18
|
+
* `begin` needs its public URL to render the meta block.
|
|
19
|
+
* - The bundle is PUT to GCS BEFORE `complete`, because `complete` range-reads the object to
|
|
20
|
+
* confirm it carries the meta block `begin` issued — there is nothing to confirm yet.
|
|
21
|
+
*
|
|
22
|
+
* `--force` overrides the verdict and staleness `checkVerifyGate` checks, never the existence of
|
|
23
|
+
* a record: the thumbnail comes from the verify screenshot, so with no verify there is nothing to
|
|
24
|
+
* publish a thumbnail from. `--name`/`--description` are sent on the wire for THIS publish only —
|
|
25
|
+
* they are never written back into the project's own `game.json`, which would change the
|
|
26
|
+
* fingerprint and invalidate the very verify this command just checked.
|
|
27
|
+
*/
|
|
28
|
+
import { defineCommand } from 'citty';
|
|
29
|
+
import { Buffer } from 'buffer';
|
|
30
|
+
import { createHash } from 'crypto';
|
|
31
|
+
import * as fs from 'fs';
|
|
32
|
+
import * as path from 'path';
|
|
33
|
+
import { CliError } from '../errors.js';
|
|
34
|
+
import { loadProjectContext } from '../project/context.js';
|
|
35
|
+
import { resolveEnvironment, resolveAuth0ClientId } from '../config/environments.js';
|
|
36
|
+
import { readDefaultEnvironment } from '../config/credentials.js';
|
|
37
|
+
import { getAccessToken } from '../auth/session.js';
|
|
38
|
+
import { computeFingerprint } from '../publish/fingerprint.js';
|
|
39
|
+
import { readVerifyRecord } from '../verify/result.js';
|
|
40
|
+
import { checkVerifyGate, injectMetaBlock } from '../publish/meta-block.js';
|
|
41
|
+
import { runBuild as runBuildImpl, readBuildManifest as readBuildManifestImpl, buildBundlePaths, } from '../publish/bundle.js';
|
|
42
|
+
import { beginPublish, completePublish, putBundle, requestThumbnailUploadUrl, putThumbnail, } from '../publish/client.js';
|
|
43
|
+
/** Where `bitmagic verify` writes its record and screenshot — see `commands/verify.ts`. */
|
|
44
|
+
function verifyArtifactDir(root) {
|
|
45
|
+
return path.join(root, '.bitmagic', 'verify');
|
|
46
|
+
}
|
|
47
|
+
/** `'public' | 'private'`, defaulting to private — a typo is rejected rather than silently
|
|
48
|
+
* coerced, matching the server's own asymmetric contract (see cli-publish.ts). */
|
|
49
|
+
export function resolveVisibility(raw) {
|
|
50
|
+
if (raw === undefined)
|
|
51
|
+
return 'private';
|
|
52
|
+
if (raw === 'public' || raw === 'private')
|
|
53
|
+
return raw;
|
|
54
|
+
throw new CliError(`--visibility must be "public" or "private", got ${JSON.stringify(raw)}.`);
|
|
55
|
+
}
|
|
56
|
+
function defaultDeps() {
|
|
57
|
+
return {
|
|
58
|
+
fetch: globalThis.fetch,
|
|
59
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
60
|
+
log: (message) => console.log(message),
|
|
61
|
+
runBuild: runBuildImpl,
|
|
62
|
+
readBuildManifest: readBuildManifestImpl,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Upload the last verify run's screenshot as this publish's thumbnail, and return its public URL.
|
|
67
|
+
*
|
|
68
|
+
* There is no other source: this lane has no image generator of its own, and the verify gate
|
|
69
|
+
* above already guarantees a record exists — which is what the screenshot alongside it is for.
|
|
70
|
+
*/
|
|
71
|
+
async function uploadThumbnail(context, environment, token, apiDeps) {
|
|
72
|
+
const screenshotPath = path.join(verifyArtifactDir(context.root), 'screenshot.png');
|
|
73
|
+
let bytes;
|
|
74
|
+
try {
|
|
75
|
+
bytes = fs.readFileSync(screenshotPath);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Exit 3, not 6: the record passed the verify gate above, but there is nothing to publish a
|
|
79
|
+
// thumbnail FROM — the same remedy as every other exit-3 case ("run bitmagic verify"), not an
|
|
80
|
+
// upload failure.
|
|
81
|
+
throw new CliError(`No verify screenshot found at ${screenshotPath}. Run \`bitmagic verify\` before publishing.`, 3);
|
|
82
|
+
}
|
|
83
|
+
const contentType = 'image/png';
|
|
84
|
+
const signed = await requestThumbnailUploadUrl(environment, token, { gameId: context.metadata.gameId, filename: 'thumbnail.png', contentType }, apiDeps);
|
|
85
|
+
await putThumbnail(signed, bytes, contentType, apiDeps);
|
|
86
|
+
return signed.publicUrl;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The whole command. See the file header for why the order below cannot be reshuffled.
|
|
90
|
+
*/
|
|
91
|
+
export async function runPublish(args, deps = defaultDeps()) {
|
|
92
|
+
const visibility = resolveVisibility(args.visibility);
|
|
93
|
+
const force = args.force === true;
|
|
94
|
+
const context = loadProjectContext(deps.startDir);
|
|
95
|
+
const fingerprint = computeFingerprint(context.root);
|
|
96
|
+
const gate = checkVerifyGate(readVerifyRecord(verifyArtifactDir(context.root)), fingerprint, force);
|
|
97
|
+
if (!gate.ok)
|
|
98
|
+
throw new CliError(gate.message, 3);
|
|
99
|
+
// Reuse: same inputs, same bundle — skip the rebuild. `manifest.bytes`/`manifest.sha256`
|
|
100
|
+
// themselves are never read past this point: what's actually PUT and what `complete` validates
|
|
101
|
+
// is the bundle AFTER the meta block is injected below, which this pre-injection manifest
|
|
102
|
+
// cannot describe either way.
|
|
103
|
+
const existing = deps.readBuildManifest(context.root);
|
|
104
|
+
if (existing?.fingerprint !== fingerprint) {
|
|
105
|
+
deps.runBuild(context.root, context.metadata.engineVersion);
|
|
106
|
+
}
|
|
107
|
+
const environment = resolveEnvironment({ storedDefault: readDefaultEnvironment(deps.baseDir) });
|
|
108
|
+
const clientId = resolveAuth0ClientId(environment);
|
|
109
|
+
const token = await getAccessToken(environment, clientId, { fetch: deps.fetch, sleep: deps.sleep }, deps.baseDir);
|
|
110
|
+
const apiDeps = { fetch: deps.fetch };
|
|
111
|
+
const thumbnailUrl = await uploadThumbnail(context, environment, token, apiDeps);
|
|
112
|
+
// No bundleBytes/sha256 sent here — see BeginPublishRequest's doc comment in publish/client.ts.
|
|
113
|
+
// The only honest number for those fields is the size/hash of the bundle AFTER the meta block
|
|
114
|
+
// below is injected, and that block (and the publishVersion inside it) isn't known until THIS
|
|
115
|
+
// call returns.
|
|
116
|
+
const begun = await beginPublish(environment, token, {
|
|
117
|
+
gameId: context.metadata.gameId,
|
|
118
|
+
visibility,
|
|
119
|
+
...(args.name !== undefined ? { name: args.name } : {}),
|
|
120
|
+
...(args.description !== undefined ? { description: args.description } : {}),
|
|
121
|
+
engineVersion: context.metadata.engineVersion,
|
|
122
|
+
thumbnailUrl,
|
|
123
|
+
}, apiDeps);
|
|
124
|
+
const htmlPath = buildBundlePaths(context.root).htmlPath;
|
|
125
|
+
let builtHtml;
|
|
126
|
+
try {
|
|
127
|
+
builtHtml = fs.readFileSync(htmlPath, 'utf-8');
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// A reused build's manifest can outlive its own bundle file (e.g. `.bitmagic/build/` cleaned
|
|
131
|
+
// by hand) — report that plainly rather than letting the raw filesystem error (ENOENT, with
|
|
132
|
+
// its own stack) escape uncaught past citty's clean-error wrapper.
|
|
133
|
+
throw new CliError(`The built bundle at ${htmlPath} is missing. Run \`bitmagic build\` again, then publish.`, 1);
|
|
134
|
+
}
|
|
135
|
+
// Computed from the INJECTED bundle, not `manifest.bytes`/`manifest.sha256` — those describe
|
|
136
|
+
// the bundle BEFORE the meta block below is added, and `complete` (cli-publish.ts) 409s the
|
|
137
|
+
// instant `bundleBytes` doesn't match what's actually sitting in GCS: the object it heads is the
|
|
138
|
+
// injected one. Sending the pre-injection numbers would 409 every single publish.
|
|
139
|
+
const publishedHtml = injectMetaBlock(builtHtml, begun.metaBlock);
|
|
140
|
+
const publishedBytes = Buffer.byteLength(publishedHtml, 'utf-8');
|
|
141
|
+
const publishedSha256 = createHash('sha256').update(publishedHtml, 'utf-8').digest('hex');
|
|
142
|
+
// The PUT must send `Cache-Control: no-cache` — `begin` binds that header into the signed
|
|
143
|
+
// URL's signature (so the published entry HTML is never served stale), and GCS rejects a
|
|
144
|
+
// signed PUT whose headers don't match what was signed. Omitting it does not produce a stale
|
|
145
|
+
// cache — it produces a 403 on upload. `cli-uploads.ts` established the same pattern.
|
|
146
|
+
await putBundle(begun.signedUrl, publishedHtml, begun.cacheControl, apiDeps);
|
|
147
|
+
// `visibility`, `name`, `description`, and `thumbnailUrl` are all re-sent, not implied.
|
|
148
|
+
// `complete` re-authorizes against its OWN body — `begin` writes nothing, so nothing it
|
|
149
|
+
// validated is bound — and an absent visibility defaults to private. Omitting them here would
|
|
150
|
+
// make `--visibility public` pass the ban gate at `begin` and then silently publish as
|
|
151
|
+
// private, would overwrite a stored name with a default, and (for thumbnailUrl specifically)
|
|
152
|
+
// would make `complete`'s own meta-block recomputation diverge from what `begin` actually
|
|
153
|
+
// rendered — see `CompletePublishRequest`'s doc comment in publish/client.ts.
|
|
154
|
+
const done = await completePublish(environment, token, {
|
|
155
|
+
gameId: context.metadata.gameId,
|
|
156
|
+
key: begun.key,
|
|
157
|
+
publishVersion: begun.publishVersion,
|
|
158
|
+
visibility,
|
|
159
|
+
...(args.name !== undefined ? { name: args.name } : {}),
|
|
160
|
+
...(args.description !== undefined ? { description: args.description } : {}),
|
|
161
|
+
thumbnailUrl,
|
|
162
|
+
bundleBytes: publishedBytes,
|
|
163
|
+
sha256: publishedSha256,
|
|
164
|
+
// The spec's audit trail: engineVersion + fingerprint + sha256, recorded together against
|
|
165
|
+
// the publish that actually landed. See CompletePublishRequest's doc comment for why these
|
|
166
|
+
// are declarative (logged, never verified) and why engineVersion here is not a second gate.
|
|
167
|
+
engineVersion: context.metadata.engineVersion,
|
|
168
|
+
fingerprint,
|
|
169
|
+
}, apiDeps);
|
|
170
|
+
deps.log(`Published ${done.visibility}: ${done.url}`);
|
|
171
|
+
if (done.visibility === 'private') {
|
|
172
|
+
deps.log('Not listed. Publish with --visibility public when you are ready.');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export const publishCommand = defineCommand({
|
|
176
|
+
meta: {
|
|
177
|
+
name: 'publish',
|
|
178
|
+
description: 'Build, verify-gate, and publish the game — private unless --visibility public.',
|
|
179
|
+
},
|
|
180
|
+
args: {
|
|
181
|
+
visibility: {
|
|
182
|
+
type: 'string',
|
|
183
|
+
description: '"public" or "private" (default). Public also requires an unbanned profile.',
|
|
184
|
+
},
|
|
185
|
+
name: {
|
|
186
|
+
type: 'string',
|
|
187
|
+
description: 'Override the published name for this publish only — never written to game.json.',
|
|
188
|
+
},
|
|
189
|
+
description: {
|
|
190
|
+
type: 'string',
|
|
191
|
+
description: 'Override the published description for this publish only — never written to game.json.',
|
|
192
|
+
},
|
|
193
|
+
force: {
|
|
194
|
+
type: 'boolean',
|
|
195
|
+
description: 'Publish despite a stale or failed verify. Does NOT bypass a missing verify record.',
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
async run({ args }) {
|
|
199
|
+
await runPublish(args);
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
//# sourceMappingURL=publish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface GitStatus {
|
|
2
|
+
dirty: boolean;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* The boundary that makes `upgrade` safe to run at all: the whole value of the command is that a
|
|
6
|
+
* creator can read what it changed as a git diff. Overwriting vendored files on top of already
|
|
7
|
+
* -uncommitted work mixes the two together and makes that diff unreadable, so a dirty tree is
|
|
8
|
+
* refused unless the creator opts in with --force. Kept as a pure function, separate from the
|
|
9
|
+
* git call that produces `status`, so it is reachable from a test with no real repo involved.
|
|
10
|
+
*/
|
|
11
|
+
export declare function assertCleanTree(status: GitStatus, force: boolean): void;
|
|
12
|
+
/**
|
|
13
|
+
* The other half of "package.json is never rewritten": upgradeProject leaves it untouched even
|
|
14
|
+
* when the vendored engine now needs a dependency it doesn't have (e.g. vite-plugin-singlefile,
|
|
15
|
+
* which every pre-Task-3 project's package.json predates) — silently doing nothing here would
|
|
16
|
+
* mean `upgrade` reports success while `bitmagic build` still fails later with an opaque
|
|
17
|
+
* module-resolution error. Prints the exact install command instead; never writes the file.
|
|
18
|
+
*
|
|
19
|
+
* The command names the project's OWN package manager (`detectPackageManager`), not a hardcoded
|
|
20
|
+
* npm: a creator on pnpm who runs the printed `npm install` gets a tree npm linked and a second,
|
|
21
|
+
* far less obvious failure on the next build.
|
|
22
|
+
*
|
|
23
|
+
* Exported for its test — the detection helpers can be unit-tested on their own, but only
|
|
24
|
+
* calling this proves the printed line actually uses them.
|
|
25
|
+
*/
|
|
26
|
+
export declare function printDependencyReport(root: string): void;
|
|
27
|
+
export declare const upgradeCommand: import("citty").CommandDef<{
|
|
28
|
+
readonly engine: {
|
|
29
|
+
readonly type: "string";
|
|
30
|
+
readonly description: "Engine version to upgrade to (must be the currently published one).";
|
|
31
|
+
};
|
|
32
|
+
readonly force: {
|
|
33
|
+
readonly type: "boolean";
|
|
34
|
+
readonly description: "Upgrade even with uncommitted changes in the working tree.";
|
|
35
|
+
};
|
|
36
|
+
}>;
|