@bitmagic/cli 0.1.46-dev.2 → 0.1.46-dev.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/dist/auth/session.js +1 -1
- package/dist/auth/session.js.map +1 -1
- package/dist/auth/subscribe-flow.d.ts +30 -6
- package/dist/auth/subscribe-flow.js +64 -29
- package/dist/auth/subscribe-flow.js.map +1 -1
- package/dist/bin.js +6 -2
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts +43 -2
- package/dist/cli.js +78 -7
- package/dist/cli.js.map +1 -1
- package/dist/commands/dev.js +9 -0
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/forge.js +1 -1
- package/dist/commands/forge.js.map +1 -1
- package/dist/commands/generate.js +9 -1
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.js +6 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/login.js +15 -11
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/reset-account.d.ts +103 -0
- package/dist/commands/reset-account.js +227 -0
- package/dist/commands/reset-account.js.map +1 -0
- package/dist/commands/subscribe.d.ts +13 -1
- package/dist/commands/subscribe.js +21 -17
- package/dist/commands/subscribe.js.map +1 -1
- package/dist/commands/upgrade.d.ts +2 -1
- package/dist/commands/upgrade.js +12 -5
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/config/environments.d.ts +14 -0
- package/dist/config/environments.js +22 -0
- package/dist/config/environments.js.map +1 -1
- package/dist/errors.d.ts +10 -1
- package/dist/errors.js +11 -1
- package/dist/errors.js.map +1 -1
- package/dist/forge/run-pipeline.js +1 -1
- package/dist/forge/run-pipeline.js.map +1 -1
- package/dist/forge/stream.js +10 -5
- package/dist/forge/stream.js.map +1 -1
- package/dist/forge/upload-proxy.js +2 -0
- package/dist/forge/upload-proxy.js.map +1 -1
- package/dist/generate/stream.js +10 -5
- package/dist/generate/stream.js.map +1 -1
- package/dist/http/client.d.ts +6 -2
- package/dist/http/client.js +22 -12
- package/dist/http/client.js.map +1 -1
- package/dist/http/spark-refusal.d.ts +11 -0
- package/dist/http/spark-refusal.js +13 -0
- package/dist/http/spark-refusal.js.map +1 -1
- package/dist/publish/client.js +3 -1
- package/dist/publish/client.js.map +1 -1
- package/dist/scaffold/agents-md.d.ts +2 -1
- package/dist/scaffold/agents-md.js +7 -2
- package/dist/scaffold/agents-md.js.map +1 -1
- package/dist/scaffold/engine-download.js +5 -18
- package/dist/scaffold/engine-download.js.map +1 -1
- package/dist/scaffold/project-files.d.ts +3 -2
- package/dist/scaffold/project-files.js +46 -2
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/project.d.ts +30 -0
- package/dist/scaffold/project.js +55 -29
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scaffold/upgrade-project.js +19 -33
- package/dist/scaffold/upgrade-project.js.map +1 -1
- package/dist/telemetry/command-context.d.ts +73 -0
- package/dist/telemetry/command-context.js +114 -0
- package/dist/telemetry/command-context.js.map +1 -0
- package/dist/telemetry/report.d.ts +43 -0
- package/dist/telemetry/report.js +123 -0
- package/dist/telemetry/report.js.map +1 -0
- package/dist/telemetry/request-headers.d.ts +14 -0
- package/dist/telemetry/request-headers.js +48 -0
- package/dist/telemetry/request-headers.js.map +1 -0
- package/dist/update/check.d.ts +14 -0
- package/dist/update/check.js +17 -1
- package/dist/update/check.js.map +1 -1
- package/dist/update/notice.d.ts +40 -0
- package/dist/update/notice.js +53 -0
- package/dist/update/notice.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -92,6 +92,7 @@ browser reloads itself on every change and the assets panel shows each generatio
|
|
|
92
92
|
| `bitmagic usage [--days N]` | Where this account's sparks went, and what Pro allowance is left. |
|
|
93
93
|
| `bitmagic allowance [--allow-sparks \| --block-sparks]` | The two Pro allowance windows, and whether your purchased sparks may fund work once they run out. `--allow-sparks` needs a real terminal — it authorises spending your money, so an agent cannot run it for you. |
|
|
94
94
|
| `bitmagic logout [--env <env>]` | Remove stored credentials for an environment. |
|
|
95
|
+
| `bitmagic reset-account [--env <env>] [--yes] [--keep-cli]` | **Deletes your account** on `dev` or `local` so the first-run experience can be tested again — games, sparks, profile, terms acceptance and Bitmagic Pro (cancelled in Stripe first). Then clears that environment's stored credentials **and uninstalls this CLI**, so the next run starts where a new creator starts: `npm install -g @bitmagic/cli`. `--keep-cli` skips that last step. Refuses `prod`, where the endpoint is not deployed at all. Asks you to type the environment name; `--yes` skips that and is required in a shell with no terminal. Your project directory is left alone — its `gameId` no longer exists, so start the fresh run in a new one. |
|
|
95
96
|
| `bitmagic whoami [--env <env>]` | Show the identity the CLI is authenticated as, and which environment answered — including where that environment was chosen from. |
|
|
96
97
|
| `bitmagic init [dir] [--template <id>] [--name <name>] [--idea "<pitch>"] [--env <env>] [--force]` | Scaffold a new project: mints a game, downloads the vendored engine, writes `AGENTS.md`, `GAME-DESIGN.md` and templates. Starts from `empty-3d` unless told otherwise. `--idea` seeds the design doc's pitch **and** picks the template whose camera and controls fit it — "a doom-like corridor shooter" scaffolds `first-person`, "jump between floating islands" scaffolds `sidescroller`. `--template` overrides both; a suggestion that fails for any reason falls back to `empty-3d` rather than failing the scaffold. |
|
|
97
98
|
| `bitmagic check` | Typecheck (`tsc --noEmit`) against the vendored engine. Fast; does not prove the game runs. |
|
|
@@ -174,6 +175,14 @@ the release; `@bitmagic/cli@dev` is the prerelease line, developed against `dev`
|
|
|
174
175
|
endpoints it depends on exist first. Running a dev build against an environment that has not caught
|
|
175
176
|
up fails on endpoints that are not there yet — so pair `@dev` with `bitmagic login --env dev`.
|
|
176
177
|
|
|
178
|
+
**Inside a project, the pin decides the line**, and nobody should be asked which one they want:
|
|
179
|
+
`dev` and `local` projects take `@bitmagic/cli@dev`, `prod` projects take the plain
|
|
180
|
+
`@bitmagic/cli`. Run a command in a project on the other line and the CLI opens with a notice
|
|
181
|
+
naming the install to switch — and prints only that one, in place of the usual "a newer CLI is
|
|
182
|
+
available" nudge, so there is never a choice of two install commands. The AGENTS.md the CLI
|
|
183
|
+
scaffolds carries the same rule with the project's own command already filled in, which is what
|
|
184
|
+
keeps an agent from putting the question to you.
|
|
185
|
+
|
|
177
186
|
## Project layout and artifacts
|
|
178
187
|
|
|
179
188
|
```
|
|
@@ -561,6 +570,43 @@ Three nudges, all advisory — nothing is ever blocked because something is out
|
|
|
561
570
|
suggest `bitmagic upgrade`. Publishing is never refused for an old engine — the prompt belongs
|
|
562
571
|
where rebuilding is cheap, not at the moment you ship.
|
|
563
572
|
|
|
573
|
+
## Usage reporting
|
|
574
|
+
|
|
575
|
+
While you are logged in, each command tells Bitmagic how it went when it ends. That is: the command
|
|
576
|
+
name, whether it succeeded, its exit code and a failure *category* (`unreachable`, `pro_expired`,
|
|
577
|
+
`insufficient_sparks`, …), how long it took, and the CLI, Node and OS versions. Commands that talk to
|
|
578
|
+
Bitmagic anyway — `generate`, `forge`, `publish` — carry the same identification on the calls they
|
|
579
|
+
already make, which is how a spark charge can be attributed to the command that caused it.
|
|
580
|
+
|
|
581
|
+
**What is never sent:** your game's source, file names, paths, or command output. Failures are
|
|
582
|
+
reported as categories, never as messages, because a message can quote a path.
|
|
583
|
+
|
|
584
|
+
**What you type into a command is kept.** The text you pass to `bitmagic forge`, `bitmagic
|
|
585
|
+
generate`, `bitmagic cover` and `bitmagic init --idea` — the descriptions of the world, the asset,
|
|
586
|
+
the game you want — is stored against your account so we can see what creators are trying to make
|
|
587
|
+
and where the generators fall short. It is the same thing the web editor already keeps for prompts
|
|
588
|
+
typed there, and it goes to the same place. None of it reaches Google Analytics, which only ever
|
|
589
|
+
receives counts, outcomes and timings.
|
|
590
|
+
|
|
591
|
+
If your agent passes it, the wording *you* used when you asked it is kept alongside the command's
|
|
592
|
+
own text. That is optional and most agents will not send it.
|
|
593
|
+
|
|
594
|
+
This does not include what you type into Claude Code, Codex or any other agent. Bitmagic never sees
|
|
595
|
+
that — the CLI only ever receives the arguments your agent chooses to pass it.
|
|
596
|
+
|
|
597
|
+
Set `DO_NOT_TRACK=1` in your environment to switch the end-of-command report off. Nothing is
|
|
598
|
+
collected for it in that case — not even the file reads it would have needed.
|
|
599
|
+
|
|
600
|
+
It does **not** switch off the paragraph above. The text you pass to `forge`, `generate`, `cover`
|
|
601
|
+
and `init --idea` is not telemetry: it is the request, and the server cannot generate anything
|
|
602
|
+
without it. If you would rather it were not kept, the honest answer is that today there is no flag
|
|
603
|
+
for that — tell us and we will add one.
|
|
604
|
+
|
|
605
|
+
The report is deliberately unobtrusive but it is not free: it is capped at 1.5 seconds per request
|
|
606
|
+
(two at worst, when your access token is due for a refresh first), it never prints, and it can never
|
|
607
|
+
fail a command — but it does happen at exit, so a command can take that much longer to return on a
|
|
608
|
+
network that is dropping packets. It is skipped entirely when you are logged out.
|
|
609
|
+
|
|
564
610
|
## Licensing
|
|
565
611
|
|
|
566
612
|
`bitmagic init` vendors the engine into your project as readable source. It is Bitmagic's IP,
|
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\`.`, 2);
|
|
9
|
+
throw new CliError(`You are not logged in to the "${environment.name}" environment. Run \`bitmagic login\`.`, 2, 'not_logged_in');
|
|
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,EACzF,CAAC,
|
|
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,EACD,eAAe,CAChB,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"}
|
|
@@ -13,6 +13,15 @@ export interface WhoamiResponse {
|
|
|
13
13
|
proUntil: string | null;
|
|
14
14
|
/** Set when the subscription is live but will not renew. Optional: older api-servers omit it. */
|
|
15
15
|
cancelAtPeriodEnd?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The Bitmagic Pro storefront — where Pro is bought OR a Pro voucher redeemed.
|
|
18
|
+
*
|
|
19
|
+
* Optional for the same reason `cancelAtPeriodEnd` is: a CLI published today
|
|
20
|
+
* can be pointed at an api-server deployed before the storefront existed, and
|
|
21
|
+
* must still be able to sell a subscription. Absent means "fall back to
|
|
22
|
+
* minting Stripe Checkout myself".
|
|
23
|
+
*/
|
|
24
|
+
subscribeUrl?: string;
|
|
16
25
|
}
|
|
17
26
|
/**
|
|
18
27
|
* PURE. One line describing where the caller's subscription stands.
|
|
@@ -42,18 +51,33 @@ export interface SubscribeOptions {
|
|
|
42
51
|
noWait?: boolean;
|
|
43
52
|
}
|
|
44
53
|
export interface SubscribeResult {
|
|
45
|
-
/** True when
|
|
54
|
+
/** True when Pro was confirmed active before we returned. */
|
|
46
55
|
pro: boolean;
|
|
47
|
-
/**
|
|
48
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Where the creator finishes subscribing: the storefront, or — against an
|
|
58
|
+
* api-server predating it — a Stripe Checkout URL. Always set: every exit from
|
|
59
|
+
* the flow has already opened or printed one.
|
|
60
|
+
*/
|
|
61
|
+
subscribeUrl: string;
|
|
49
62
|
/** Why we stopped waiting, when we did not confirm. */
|
|
50
63
|
reason?: 'timeout' | 'not-waited';
|
|
51
64
|
}
|
|
52
65
|
/**
|
|
53
|
-
*
|
|
66
|
+
* PURE. The storefront URL to open for this caller.
|
|
67
|
+
*
|
|
68
|
+
* `cli=1` only changes the page's wording — it says "go back to your terminal"
|
|
69
|
+
* once Pro is active. `u` is the account this CLI is authenticated as, so the
|
|
70
|
+
* page can refuse to sell Pro to a DIFFERENT account that happens to be signed
|
|
71
|
+
* in to the browser. That mismatch was impossible while the CLI minted the
|
|
72
|
+
* Stripe session from its own token, and it is invisible from here: the creator
|
|
73
|
+
* would subscribe the wrong account and this poll would run to its timeout.
|
|
74
|
+
*/
|
|
75
|
+
export declare function storefrontUrlFor(subscribeUrl: string, userId: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Explain that Pro is required, open the storefront, and wait for it to land.
|
|
54
78
|
*
|
|
55
79
|
* Returns rather than throwing when the creator does not finish: abandoning
|
|
56
|
-
*
|
|
80
|
+
* the flow is a decision, not an error, and the credentials from a successful
|
|
57
81
|
* login are still worth keeping.
|
|
58
82
|
*/
|
|
59
|
-
export declare function runSubscribeFlow(environment: Environment, token: string, output: Output, options?: SubscribeOptions, deps?: SubscribeDeps): Promise<SubscribeResult>;
|
|
83
|
+
export declare function runSubscribeFlow(environment: Environment, token: string, me: WhoamiResponse, output: Output, options?: SubscribeOptions, deps?: SubscribeDeps): Promise<SubscribeResult>;
|
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Getting a logged-in creator from "no subscription" to "ready to work".
|
|
3
3
|
*
|
|
4
|
-
* The Bitmagic CLI requires
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The Bitmagic CLI requires active Bitmagic Pro, so for a new creator the very
|
|
5
|
+
* first command would otherwise be a 403. Rather than print an error and a URL
|
|
6
|
+
* to retype, the CLI opens the Bitmagic Pro storefront: `bitmagic login` flows
|
|
7
|
+
* straight from the device-code approval into subscribing, in the same browser,
|
|
8
|
+
* and then confirms.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
* entitlement gate for exactly this reason (see cli-onboarding-routes.ts):
|
|
12
|
-
* `GET /whoami` answers whether the caller is entitled, and
|
|
13
|
-
* `POST /pro/checkout` mints a checkout URL for the caller's own account.
|
|
10
|
+
* ── Why a storefront and not Stripe Checkout ─────────────────────────────────
|
|
14
11
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* This used to POST /pro/checkout and open the Stripe URL directly. That made a
|
|
13
|
+
* comped-Pro voucher unreachable from the CLI even though `pro_days` satisfies
|
|
14
|
+
* the entitlement gate exactly as a subscription does — someone holding a code
|
|
15
|
+
* was shown a card form and nothing else. The storefront offers both, and the
|
|
16
|
+
* poll below does not care which one the creator picked: `whoami` reports a
|
|
17
|
+
* grant and a subscription identically.
|
|
18
|
+
*
|
|
19
|
+
* The URL comes from `whoami` rather than from the CLI's own config, because
|
|
20
|
+
* PORTAL_URL is the api-server's to know. Deriving it from `apiUrl` would be
|
|
21
|
+
* wrong locally, where the api-server is on 3002 and the portal on 4000.
|
|
22
|
+
*
|
|
23
|
+
* Both routes live OUTSIDE the CLI's entitlement gate for the obvious reason
|
|
24
|
+
* (see cli-onboarding-routes.ts): `GET /whoami` answers whether the caller is
|
|
25
|
+
* entitled and where to fix it, and `POST /pro/checkout` — now called by the
|
|
26
|
+
* storefront, and by this file only against an api-server too old to have one —
|
|
27
|
+
* mints a checkout URL for the caller's own account.
|
|
28
|
+
*
|
|
29
|
+
* Shared by `login` and by `subscribe`, so a creator who abandoned the flow gets
|
|
30
|
+
* an identical second run rather than a different-looking recovery path.
|
|
17
31
|
*/
|
|
18
32
|
import { apiGet, apiPost } from '../http/client.js';
|
|
19
33
|
import { openInBrowser } from '../open-browser.js';
|
|
@@ -50,18 +64,34 @@ export function fetchWhoami(environment, token, deps) {
|
|
|
50
64
|
return apiGet(environment, '/api/cli/v1/whoami', token, { fetch: deps.fetch });
|
|
51
65
|
}
|
|
52
66
|
/**
|
|
53
|
-
*
|
|
67
|
+
* PURE. The storefront URL to open for this caller.
|
|
68
|
+
*
|
|
69
|
+
* `cli=1` only changes the page's wording — it says "go back to your terminal"
|
|
70
|
+
* once Pro is active. `u` is the account this CLI is authenticated as, so the
|
|
71
|
+
* page can refuse to sell Pro to a DIFFERENT account that happens to be signed
|
|
72
|
+
* in to the browser. That mismatch was impossible while the CLI minted the
|
|
73
|
+
* Stripe session from its own token, and it is invisible from here: the creator
|
|
74
|
+
* would subscribe the wrong account and this poll would run to its timeout.
|
|
75
|
+
*/
|
|
76
|
+
export function storefrontUrlFor(subscribeUrl, userId) {
|
|
77
|
+
const separator = subscribeUrl.includes('?') ? '&' : '?';
|
|
78
|
+
return `${subscribeUrl}${separator}cli=1&u=${encodeURIComponent(userId)}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Explain that Pro is required, open the storefront, and wait for it to land.
|
|
54
82
|
*
|
|
55
83
|
* Returns rather than throwing when the creator does not finish: abandoning
|
|
56
|
-
*
|
|
84
|
+
* the flow is a decision, not an error, and the credentials from a successful
|
|
57
85
|
* login are still worth keeping.
|
|
58
86
|
*/
|
|
59
|
-
export async function runSubscribeFlow(environment, token, output, options = {}, deps = defaultSubscribeDeps) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
87
|
+
export async function runSubscribeFlow(environment, token, me, output, options = {}, deps = defaultSubscribeDeps) {
|
|
88
|
+
// An api-server too old to serve a storefront still has to be able to sell a
|
|
89
|
+
// subscription, so fall back to minting Checkout the way this flow used to.
|
|
90
|
+
const url = me.subscribeUrl
|
|
91
|
+
? storefrontUrlFor(me.subscribeUrl, me.userId)
|
|
92
|
+
: (await apiPost(environment, '/api/cli/v1/pro/checkout', token, {}, { fetch: deps.fetch })).url;
|
|
63
93
|
output.info('');
|
|
64
|
-
output.info('The Bitmagic CLI needs
|
|
94
|
+
output.info('The Bitmagic CLI needs Bitmagic Pro.');
|
|
65
95
|
output.info('Pro gives you a weekly and a 5-hour spark allowance that refill on their own,');
|
|
66
96
|
output.info('and pays for generating assets, cover art and worlds.');
|
|
67
97
|
output.info('');
|
|
@@ -69,34 +99,39 @@ export async function runSubscribeFlow(environment, token, output, options = {},
|
|
|
69
99
|
// no browser, in a container, and in a log an agent reads back later.
|
|
70
100
|
const opened = options.noOpen === true
|
|
71
101
|
? false
|
|
72
|
-
: deps.openInBrowser(
|
|
73
|
-
|
|
102
|
+
: deps.openInBrowser(url, { log: (m) => output.info(m) });
|
|
103
|
+
// Naming the voucher here and not only on the page matters: an agent driving
|
|
104
|
+
// the CLI reads this text, and the human it is working for may be holding a code.
|
|
105
|
+
output.info(opened
|
|
106
|
+
? `Opening Bitmagic Pro in your browser — subscribe, or redeem a Pro voucher there:\n ${url}`
|
|
107
|
+
: `Subscribe, or redeem a Pro voucher, at:\n ${url}`);
|
|
74
108
|
if (options.noWait === true) {
|
|
75
|
-
return { pro: false,
|
|
109
|
+
return { pro: false, subscribeUrl: url, reason: 'not-waited' };
|
|
76
110
|
}
|
|
77
111
|
output.info('');
|
|
78
|
-
output.info('Waiting for
|
|
112
|
+
output.info('Waiting for Bitmagic Pro… (Ctrl-C is safe — run `bitmagic subscribe` to resume)');
|
|
79
113
|
const deadline = deps.now() + SUBSCRIBE_POLL_TIMEOUT_MS;
|
|
80
114
|
while (deps.now() < deadline) {
|
|
81
115
|
await deps.sleep(SUBSCRIBE_POLL_INTERVAL_MS);
|
|
82
116
|
// A transient failure mid-poll must not end the flow — the creator may
|
|
83
117
|
// still be typing their card in. Only a definitive "yes" stops the loop.
|
|
84
|
-
|
|
118
|
+
// This answers for a redeemed voucher too: `pro` is entitlement, not billing.
|
|
119
|
+
let current = null;
|
|
85
120
|
try {
|
|
86
|
-
|
|
121
|
+
current = await fetchWhoami(environment, token, deps);
|
|
87
122
|
}
|
|
88
123
|
catch {
|
|
89
124
|
continue;
|
|
90
125
|
}
|
|
91
|
-
if (
|
|
126
|
+
if (current.pro) {
|
|
92
127
|
output.info('');
|
|
93
128
|
output.info('Bitmagic Pro is active. You are ready — try `bitmagic init`.');
|
|
94
|
-
return { pro: true,
|
|
129
|
+
return { pro: true, subscribeUrl: url };
|
|
95
130
|
}
|
|
96
131
|
}
|
|
97
132
|
output.info('');
|
|
98
|
-
output.info('Still waiting
|
|
99
|
-
output.info('`bitmagic subscribe` to start again.');
|
|
100
|
-
return { pro: false,
|
|
133
|
+
output.info('Still waiting. If you paid, run `bitmagic usage` once Stripe confirms; otherwise');
|
|
134
|
+
output.info('run `bitmagic subscribe` to start again.');
|
|
135
|
+
return { pro: false, subscribeUrl: url, reason: 'timeout' };
|
|
101
136
|
}
|
|
102
137
|
//# sourceMappingURL=subscribe-flow.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscribe-flow.js","sourceRoot":"","sources":["../../src/auth/subscribe-flow.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"subscribe-flow.js","sourceRoot":"","sources":["../../src/auth/subscribe-flow.ts"],"names":[],"mappings":"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"}
|
package/dist/bin.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { runMain } from 'citty';
|
|
3
3
|
import { main } from './cli.js';
|
|
4
4
|
import { CliError } from './errors.js';
|
|
5
|
-
import {
|
|
5
|
+
import { readPinnedEnvironment } from './config/environments.js';
|
|
6
|
+
import { refreshUpdateCacheInBackground, startupNotice } from './update/check.js';
|
|
6
7
|
import { currentCliVersion } from './update/cli-version.js';
|
|
7
8
|
// The version is READ here, not at the import time of anything cli.ts pulls in — cli.test.ts
|
|
8
9
|
// imports cli.ts for `subCommands`, and a version check triggered against the test runner is
|
|
@@ -13,7 +14,10 @@ import { currentCliVersion } from './update/cli-version.js';
|
|
|
13
14
|
// costs no network and no measurable time here. A command that exits early (or via process.exit
|
|
14
15
|
// on a CliError) has still shown it, which printing afterwards could not guarantee.
|
|
15
16
|
const version = currentCliVersion();
|
|
16
|
-
|
|
17
|
+
// The pin is read from the committed bitmagic.json, so running in a project whose environment wants
|
|
18
|
+
// the other npm line says so — and says only that, rather than also offering the newest build of the
|
|
19
|
+
// line you should not be on. See startupNotice.
|
|
20
|
+
const notice = startupNotice(version, readPinnedEnvironment());
|
|
17
21
|
if (notice !== null)
|
|
18
22
|
console.error(notice);
|
|
19
23
|
// Deliberately NOT awaited: this is for the next invocation, not this one. Bounded by its own
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,8BAA8B,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,6FAA6F;AAC7F,6FAA6F;AAC7F,+FAA+F;AAC/F,+BAA+B;AAC/B,EAAE;AACF,kGAAkG;AAClG,gGAAgG;AAChG,oFAAoF;AACpF,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;AACpC,oGAAoG;AACpG,qGAAqG;AACrG,gDAAgD;AAChD,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,qBAAqB,EAAE,CAAC,CAAC;AAC/D,IAAI,MAAM,KAAK,IAAI;IAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAE3C,8FAA8F;AAC9F,iGAAiG;AACjG,2FAA2F;AAC3F,KAAK,8BAA8B,CAAC,OAAO,CAAC,CAAC;AAE7C,wFAAwF;AACxF,+FAA+F;AAC/F,8FAA8F;AAC9F,gGAAgG;AAChG,2FAA2F;AAC3F,kGAAkG;AAClG,mGAAmG;AACnG,gGAAgG;AAChG,OAAO,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,EAAE,GAAI,IAAI,CAAC,IAAgC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC1G,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import type { ArgsDef, CommandDef } from 'citty';
|
|
2
|
-
|
|
2
|
+
import { type BeginCommandInput } from './telemetry/command-context.js';
|
|
3
|
+
import { type CommandReport } from './telemetry/report.js';
|
|
4
|
+
/**
|
|
5
|
+
* What the wrapper needs from the outside world to report a command's outcome. The real ones
|
|
6
|
+
* (below) mark the command as begun and send the beacon to api-server; tests pass stubs so a
|
|
7
|
+
* wrapped command under test never touches `~/.bitmagic` or the network.
|
|
8
|
+
*/
|
|
9
|
+
export interface CommandRunDeps {
|
|
10
|
+
begin: (input: BeginCommandInput) => void;
|
|
11
|
+
report: (report: CommandReport) => Promise<void>;
|
|
12
|
+
now: () => number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Wraps a command's `run` so that (a) a CliError becomes a clean single-line message and exit
|
|
16
|
+
* code, and (b) EVERY run — success, refusal or crash — reports how it went to api-server, once,
|
|
17
|
+
* with the command's name and duration (see telemetry/report.ts). Doing both here, over the
|
|
18
|
+
* whole map, is what makes the reporting complete: there is no per-command call anyone can
|
|
19
|
+
* forget, and the offline commands (`dev`, `build`, `check`, ...) are covered exactly like the
|
|
20
|
+
* ones that talk to the server anyway.
|
|
21
|
+
*
|
|
22
|
+
* `name` is what the report calls the command — `publish`, `generate skybox` — as the creator
|
|
23
|
+
* typed it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function withCleanErrors<T extends ArgsDef>(command: CommandDef<T>, name?: string, deps?: CommandRunDeps): CommandDef<T>;
|
|
3
26
|
/**
|
|
4
27
|
* Structural check for tests: has this command actually passed through `withCleanErrors`? A leaf
|
|
5
28
|
* command (one with its own `run`) is wrapped when that `run` is in `wrappedRuns`. A grouping
|
|
@@ -28,6 +51,24 @@ declare const rawCommands: {
|
|
|
28
51
|
readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
|
|
29
52
|
};
|
|
30
53
|
}>;
|
|
54
|
+
'reset-account': CommandDef<{
|
|
55
|
+
readonly env: {
|
|
56
|
+
readonly type: "string";
|
|
57
|
+
readonly description: "Environment to reset in (dev, local). Never prod.";
|
|
58
|
+
};
|
|
59
|
+
readonly yes: {
|
|
60
|
+
readonly type: "boolean";
|
|
61
|
+
readonly description: "Skip the confirmation. Required in a non-interactive shell.";
|
|
62
|
+
};
|
|
63
|
+
readonly 'keep-cli': {
|
|
64
|
+
readonly type: "boolean";
|
|
65
|
+
readonly description: string;
|
|
66
|
+
};
|
|
67
|
+
readonly json: {
|
|
68
|
+
readonly type: "boolean";
|
|
69
|
+
readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
|
|
70
|
+
};
|
|
71
|
+
}>;
|
|
31
72
|
whoami: CommandDef<{
|
|
32
73
|
readonly env: {
|
|
33
74
|
readonly type: "string";
|
|
@@ -45,7 +86,7 @@ declare const rawCommands: {
|
|
|
45
86
|
};
|
|
46
87
|
readonly 'no-wait': {
|
|
47
88
|
readonly type: "boolean";
|
|
48
|
-
readonly description: "Open
|
|
89
|
+
readonly description: "Open the Bitmagic Pro page and exit instead of waiting for it to complete.";
|
|
49
90
|
};
|
|
50
91
|
readonly json: {
|
|
51
92
|
readonly type: "boolean";
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
2
|
import { CliError } from './errors.js';
|
|
3
3
|
import { jsonErrorResult } from './output.js';
|
|
4
|
+
import { beginCommand } from './telemetry/command-context.js';
|
|
5
|
+
import { crashCategory, reportCommandEvent } from './telemetry/report.js';
|
|
4
6
|
import { loginCommand } from './commands/login.js';
|
|
5
7
|
import { logoutCommand } from './commands/logout.js';
|
|
8
|
+
import { resetAccountCommand } from './commands/reset-account.js';
|
|
6
9
|
import { whoamiCommand } from './commands/whoami.js';
|
|
7
10
|
import { usageCommand } from './commands/usage.js';
|
|
8
11
|
import { allowanceCommand } from './commands/allowance.js';
|
|
@@ -40,13 +43,77 @@ function subCommandsOf(command) {
|
|
|
40
43
|
return undefined;
|
|
41
44
|
return subs;
|
|
42
45
|
}
|
|
43
|
-
|
|
46
|
+
const realRunDeps = {
|
|
47
|
+
begin: beginCommand,
|
|
48
|
+
report: reportCommandEvent,
|
|
49
|
+
now: Date.now,
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* The name the wrapper reports for `command`, when the caller did not give one: its own
|
|
53
|
+
* `meta.name`, or `unknown` for a command that has none. Every entry in `rawCommands` is named
|
|
54
|
+
* from the map key instead (and its subcommands as `parent sub`), so this only decides for a
|
|
55
|
+
* command wrapped on its own — which, in practice, means a test's.
|
|
56
|
+
*/
|
|
57
|
+
/**
|
|
58
|
+
* The `--env` this command will actually honour, or undefined.
|
|
59
|
+
*
|
|
60
|
+
* Gated on the command DECLARING an `env` argument, which only eight of the eighteen do — the
|
|
61
|
+
* rest resolve from the project (`resolveEnvironmentForCommand({ cwd })`) and ignore the flag
|
|
62
|
+
* entirely. citty still surfaces an undeclared flag in `args`: `--env=dev` arrives as the string
|
|
63
|
+
* `'dev'` (the space form `--env dev` arrives as `true`, which the type check below would have
|
|
64
|
+
* caught anyway). Reading it unconditionally therefore pointed the usage report at one
|
|
65
|
+
* environment while the command worked against another — `bitmagic publish --env=dev` inside a
|
|
66
|
+
* project pinned to `local` publishes to local and would have reported to dev.
|
|
67
|
+
*/
|
|
68
|
+
function declaredExplicitEnv(command, args) {
|
|
69
|
+
const declared = (command.args ?? {});
|
|
70
|
+
if (!Object.prototype.hasOwnProperty.call(declared, 'env'))
|
|
71
|
+
return undefined;
|
|
72
|
+
const value = args?.env;
|
|
73
|
+
return typeof value === 'string' ? value : undefined;
|
|
74
|
+
}
|
|
75
|
+
function commandNameOf(command) {
|
|
76
|
+
// citty's `meta` is a Resolvable (a value, a promise, or a function returning either); every
|
|
77
|
+
// meta in this CLI is a plain object, and a test double that is not simply reports `unknown`.
|
|
78
|
+
const meta = command.meta;
|
|
79
|
+
if (typeof meta !== 'object' || meta === null)
|
|
80
|
+
return 'unknown';
|
|
81
|
+
const name = meta.name;
|
|
82
|
+
return typeof name === 'string' ? name : 'unknown';
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Wraps a command's `run` so that (a) a CliError becomes a clean single-line message and exit
|
|
86
|
+
* code, and (b) EVERY run — success, refusal or crash — reports how it went to api-server, once,
|
|
87
|
+
* with the command's name and duration (see telemetry/report.ts). Doing both here, over the
|
|
88
|
+
* whole map, is what makes the reporting complete: there is no per-command call anyone can
|
|
89
|
+
* forget, and the offline commands (`dev`, `build`, `check`, ...) are covered exactly like the
|
|
90
|
+
* ones that talk to the server anyway.
|
|
91
|
+
*
|
|
92
|
+
* `name` is what the report calls the command — `publish`, `generate skybox` — as the creator
|
|
93
|
+
* typed it.
|
|
94
|
+
*/
|
|
95
|
+
export function withCleanErrors(command, name = commandNameOf(command), deps = realRunDeps) {
|
|
44
96
|
const originalRun = command.run;
|
|
45
97
|
const wrapped = { ...command };
|
|
46
98
|
if (originalRun) {
|
|
47
99
|
wrapped.run = async (context) => {
|
|
100
|
+
const json = context.args?.json === true;
|
|
101
|
+
const explicitEnv = declaredExplicitEnv(command, context.args);
|
|
102
|
+
const startedAt = deps.now();
|
|
103
|
+
// Before the run, so every api-server call the command makes is stamped with its name.
|
|
104
|
+
deps.begin({ name, json, explicitEnv, now: deps.now });
|
|
105
|
+
// Awaited, not fired: it is bounded (report.ts), and on the failure path process.exit
|
|
106
|
+
// comes next — a fire-and-forget request would be cut off mid-flight.
|
|
107
|
+
//
|
|
108
|
+
// `Math.max(0, …)` because the clock is `Date.now()` and therefore not monotonic: an NTP
|
|
109
|
+
// step backwards mid-command would otherwise send a negative duration, which api-server
|
|
110
|
+
// rejects — discarding the outcome and the failure category along with it, rather than
|
|
111
|
+
// just the one bad field.
|
|
112
|
+
const finished = (outcome) => deps.report({ stage: 'finished', durationMs: Math.max(0, deps.now() - startedAt), ...outcome });
|
|
48
113
|
try {
|
|
49
|
-
|
|
114
|
+
const result = await originalRun(context);
|
|
115
|
+
await finished({ ok: true });
|
|
116
|
+
return result;
|
|
50
117
|
}
|
|
51
118
|
catch (error) {
|
|
52
119
|
if (error instanceof CliError) {
|
|
@@ -55,13 +122,16 @@ export function withCleanErrors(command) {
|
|
|
55
122
|
// document, so an agent parses one stream for the outcome instead of branching between
|
|
56
123
|
// "parse stdout" on success and "read prose" on failure.
|
|
57
124
|
console.error(error.message);
|
|
58
|
-
if (
|
|
125
|
+
if (json) {
|
|
59
126
|
console.log(JSON.stringify(jsonErrorResult(error.message, error.exitCode)));
|
|
60
127
|
}
|
|
128
|
+
await finished({ ok: false, exitCode: error.exitCode, failure: error.code ?? 'cli_error' });
|
|
61
129
|
process.exit(error.exitCode);
|
|
62
130
|
}
|
|
63
131
|
// Not one of ours: let citty's default handling show it, stack trace included — that's
|
|
64
|
-
// a genuine bug, not a user-facing condition we've designed a message for.
|
|
132
|
+
// a genuine bug, not a user-facing condition we've designed a message for. citty exits 1
|
|
133
|
+
// for it, which is what the report says.
|
|
134
|
+
await finished({ ok: false, exitCode: 1, failure: crashCategory(error) });
|
|
65
135
|
throw error;
|
|
66
136
|
}
|
|
67
137
|
};
|
|
@@ -72,10 +142,10 @@ export function withCleanErrors(command) {
|
|
|
72
142
|
// unrecursed, a CliError thrown from one of those would clear this command's own (nonexistent)
|
|
73
143
|
// run untouched and hit citty's default handling instead — a raw stack trace, the exact thing
|
|
74
144
|
// this wrapper exists to prevent. Recurse so every depth gets the same clean single-line
|
|
75
|
-
// treatment
|
|
145
|
+
// treatment — and the same report, named `generate skybox` rather than `skybox`.
|
|
76
146
|
const subs = subCommandsOf(command);
|
|
77
147
|
if (subs) {
|
|
78
|
-
wrapped.subCommands = Object.fromEntries(Object.entries(subs).map(([
|
|
148
|
+
wrapped.subCommands = Object.fromEntries(Object.entries(subs).map(([subName, sub]) => [subName, withCleanErrors(sub, `${name} ${subName}`, deps)]));
|
|
79
149
|
}
|
|
80
150
|
return wrapped;
|
|
81
151
|
}
|
|
@@ -101,6 +171,7 @@ export function isWrappedWithCleanErrors(command) {
|
|
|
101
171
|
const rawCommands = {
|
|
102
172
|
login: loginCommand,
|
|
103
173
|
logout: logoutCommand,
|
|
174
|
+
'reset-account': resetAccountCommand,
|
|
104
175
|
whoami: whoamiCommand,
|
|
105
176
|
subscribe: subscribeCommand,
|
|
106
177
|
usage: usageCommand,
|
|
@@ -117,7 +188,7 @@ const rawCommands = {
|
|
|
117
188
|
forge: forgeCommand,
|
|
118
189
|
publish: publishCommand,
|
|
119
190
|
};
|
|
120
|
-
export const subCommands = Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => [name, withCleanErrors(command)]));
|
|
191
|
+
export const subCommands = Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => [name, withCleanErrors(command, name)]));
|
|
121
192
|
// This module only builds `main` and exports it (along with `subCommands` and `withCleanErrors`)
|
|
122
193
|
// for `bin.ts` to run and for cli.test.ts to import — it must not call `runMain` or otherwise
|
|
123
194
|
// have a side effect when imported, e.g. by cli.test.ts, which needs `subCommands` and
|
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,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,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,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,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,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,6FAA6F;AAC7F,2FAA2F;AAC3F,+FAA+F;AAC/F,gGAAgG;AAChG,8FAA8F;AAC9F,4EAA4E;AAE5E,gGAAgG;AAChG,+FAA+F;AAC/F,iFAAiF;AACjF,MAAM,WAAW,GAAG,IAAI,OAAO,EAAU,CAAC;AAE1C;;;;;GAKG;AACH,SAAS,aAAa,CACpB,OAAsB;IAEtB,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;IACjC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACxD,OAAO,IAA2C,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,eAAe,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/dev.js
CHANGED
|
@@ -29,6 +29,8 @@ import { CORS_ALLOWED_PORT_MAX, CORS_ALLOWED_PORT_MIN, isPortFree, parseLocalPor
|
|
|
29
29
|
import { openInBrowser, NO_OPEN_ENV } from '../open-browser.js';
|
|
30
30
|
import { waitForServer } from '../verify/wait-for-server.js';
|
|
31
31
|
import { engineUpdateNotice } from '../project/engine-update.js';
|
|
32
|
+
import { reportCommandEvent } from '../telemetry/report.js';
|
|
33
|
+
import { currentCommand } from '../telemetry/command-context.js';
|
|
32
34
|
/**
|
|
33
35
|
* Tell the creator, once, if a newer engine exists — the one place where acting on it is cheap.
|
|
34
36
|
*
|
|
@@ -207,6 +209,13 @@ export const devCommand = defineCommand({
|
|
|
207
209
|
// the tabs, kept addressable for a second window or a bookmark.
|
|
208
210
|
void waitForServer(gamePort, 30_000)
|
|
209
211
|
.then(() => {
|
|
212
|
+
// The one command that reports twice. Every command reports when it ENDS (cli.ts), but
|
|
213
|
+
// a dev server ends by the terminal closing at least as often as by Ctrl-C, and only the
|
|
214
|
+
// latter reaches the wrapper — so the session is counted here, the moment it is actually
|
|
215
|
+
// serving. The duration is the time to get here from the command start: build included,
|
|
216
|
+
// which is most of it. Fire-and-forget: a slow api-server must never delay the banner.
|
|
217
|
+
const commandStartedAt = currentCommand()?.startedAt ?? Date.now();
|
|
218
|
+
void reportCommandEvent({ stage: 'started', ok: true, durationMs: Date.now() - commandStartedAt });
|
|
210
219
|
// Opened rather than merely printed, because this command is now started as a BACKGROUND
|
|
211
220
|
// task by the agent that scaffolds the project — so this banner goes to a log the creator
|
|
212
221
|
// never reads. See open-browser.ts for why `login` makes the opposite call.
|