@homie-rocks/studio 0.19.2 → 0.21.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.
- package/CHANGELOG.md +93 -2
- package/README.md +39 -0
- package/bin/homie-studio.mjs +56 -3
- package/lab/harness.js +371 -0
- package/lab/lab.ts +180 -0
- package/lab/page/index.html +83 -0
- package/lab/page/lab.css +174 -0
- package/lab/page/page.js +802 -0
- package/lab/take.js +189 -0
- package/lib/build.mjs +61 -44
- package/lib/check.mjs +3 -1
- package/lib/lab-check.mjs +212 -0
- package/lib/lab.mjs +546 -0
- package/lib/mcp-tools.mjs +45 -1
- package/lib/mcp.mjs +4 -2
- package/lib/office.mjs +30 -8
- package/lib/port-check.mjs +3 -1
- package/lib/progress.mjs +16 -1
- package/lib/scaffold.mjs +13 -2
- package/lib/studio.mjs +14 -0
- package/lib/template-history.json +74 -0
- package/lib/thumb.mjs +82 -0
- package/mcp/ui/lab.js +67 -0
- package/package.json +6 -1
- package/port/index.ts +2 -0
- package/starters/ember-vale/lab.json +22 -0
- package/starters/ember-vale/src/main.ts +271 -19
- package/starters/ember-vale/tunables.json +13 -0
- package/starters/gem-rush/lab.json +19 -0
- package/starters/gem-rush/src/main.ts +213 -38
- package/starters/gem-rush/tunables.json +13 -0
- package/worker/version.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -17,9 +17,99 @@ To bring a studio up to date, tell Claude: "Upgrade my studio to the newest Homi
|
|
|
17
17
|
`npx -y @homie-rocks/studio@latest upgrade`, which shows what's new since the version your studio pins (from this
|
|
18
18
|
file) and what the upgrade would change, and changes nothing until you agree.
|
|
19
19
|
|
|
20
|
+
## [0.21.0] - 2026-10-02
|
|
21
|
+
|
|
22
|
+
**Plugin 0.22.0** · [#31](https://github.com/homie-rocks/homie/pull/31)
|
|
23
|
+
|
|
24
|
+
The Homie mod for Claude Code: your studio, its rooms and a real seat in a live game beside the chat, with a look
|
|
25
|
+
before protected files, deploys and spending, and keys kept out of what Claude reads.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Plugin: the Homie mod (Claude Code 2.1.287 or later). Inside a studio, a one-row band above the prompt shows the
|
|
30
|
+
studio, the game, the build step, ▶ Play and how many people are playing now. Outside a studio it adds nothing.
|
|
31
|
+
- Plugin: the Studio pane (`/studio`) opens by itself when a build starts, on a wide enough terminal. **Build** has the
|
|
32
|
+
live Plan, Build, Checks, Deploy feed and the latest check frame, or a live Watch of a room of the game being built.
|
|
33
|
+
**Rooms** lists who is playing and the AI players, with Watch and Join links; Announce, Mute and Kick go through
|
|
34
|
+
the office, and Mute and Kick are only ever asked for, so you still confirm them with one tap. **Games** has
|
|
35
|
+
launch states and the remix switch; **Stats** and **Codex** link to the rest. **Lab** shows each game's last Game
|
|
36
|
+
Lab check (New's phases beside Today's, whether the replays match) and the lab's link while it runs.
|
|
37
|
+
- Plugin: the Arcade pane (`/arcade`). Take a real seat in a public room of a Homie game (Homie Arcade's, or your
|
|
38
|
+
studio's own) and play it in the pane with w/a/s/d or the arrow keys while Claude works. It runs in one headless
|
|
39
|
+
Chrome on your computer at the lowest priority, about 8 frames a second in a terminal, pauses while the pane is
|
|
40
|
+
hidden and stops when you leave.
|
|
41
|
+
- Plugin: a Parts pane for the `parallel` skill: each part, its last step, and the merge waiting for all of them.
|
|
42
|
+
- Plugin: instant commands that answer without a Claude turn: `/play`, `/watch [room]`, `/rooms`, `/build`, `/codex`,
|
|
43
|
+
`/deploy-status`, `/perf-numbers`, `/parts`, `/arcade`. They print links or open a pane; nothing opens a browser.
|
|
44
|
+
- Plugin: guards. An edit to a file matched by studio.json `"protect"` (globs, such as `"games/*/game.json"`) shows
|
|
45
|
+
the diff and waits for Proceed or Cancel. So does a deploy (where it goes, what it creates, the commits and files
|
|
46
|
+
since the last one, the checks) and a paid fal or ElevenLabs run that would pass studio.json `"budget"`.
|
|
47
|
+
- Plugin: Cloudflare tokens, provider keys, agent passes, office keys and one-time owner links are taken out of
|
|
48
|
+
command output before Claude reads it.
|
|
49
|
+
- Plugin: setup status, check, playtest and deploy results are drawn as checklists and rows, with the live link.
|
|
50
|
+
- `homie-studio office mute <game> <room> <seat | name> [--minutes 10] [--off]`, asked for like `office kick`.
|
|
51
|
+
- A build's latest check frame is also kept as a small picture beside its progress feed, for the mod to draw.
|
|
52
|
+
|
|
53
|
+
### Upgrade notes
|
|
54
|
+
|
|
55
|
+
- Every part of the mod has its own switch (`/config`, or `pluginConfigs` in settings.json). Codex installs the plugin
|
|
56
|
+
as before; it does not run plugin hooks, so there it is the skills and tools you had.
|
|
57
|
+
- `homie-studio upgrade --apply` adds `office mute` and the `protect` and `budget` lines to AGENTS.md.
|
|
58
|
+
|
|
59
|
+
## [0.20.0] - 2026-10-02
|
|
60
|
+
|
|
61
|
+
**Plugin 0.21.0** · [#30](https://github.com/homie-rocks/homie/pull/30) · [release-2026-10-02-studio-0.20.0](https://github.com/homie-rocks/homie/releases/tag/release-2026-10-02-studio-0.20.0)
|
|
62
|
+
|
|
63
|
+
A Game Lab: tune how one move of your game feels (a jump, a hit, a dash), your new version beside your last commit,
|
|
64
|
+
frame by frame.
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- Plugin: the `lab` skill. Say "iterate on the jump", "make the hit feel punchier" or "tune the drift". Claude first
|
|
69
|
+
teaches your game to tell the lab about that move (its phases, the numbers that shape it) without changing how it
|
|
70
|
+
plays, and commits that. Then it proposes the change as named phases and numbers, shows it to you New beside Today,
|
|
71
|
+
keeps only what you like, and can record a side-by-side clip to share.
|
|
72
|
+
- `homie-studio lab <game>`: the Game Lab, a page on your own computer. One short take of your game plays in New (your
|
|
73
|
+
working copy, rebuilt every time a file is saved) beside Today (your last commit), on one clock, with the same
|
|
74
|
+
presses on the same frames. Slow it to a quarter or a tenth, step a frame at a time, try 30, 15 or 12 frames a
|
|
75
|
+
second, or switch to a phone-sized screen; the frames are the same frames at any speed.
|
|
76
|
+
- In the lab: a timeline that names each phase of the move, graphs of New against Today, captions, the game's own
|
|
77
|
+
onion skin and arcs, and its views (close, the whole arena). Its numbers are sliders: move one and New plays it at
|
|
78
|
+
once, and "Keep in code" writes the ones you like into the game's `tunables.json`. Press REC and play a move to make
|
|
79
|
+
a new take; both versions get your presses.
|
|
80
|
+
- `homie-studio lab check <game>`: the same take with no window. How long each phase lasts in each version, the peaks
|
|
81
|
+
of what the game tracks, whether a replay landed on exactly the same frames, a contact sheet and a still.
|
|
82
|
+
`homie-studio lab set <game> name=value` writes a number into `tunables.json`; `homie-studio lab --stop` stops the lab.
|
|
83
|
+
- Homie for Claude Desktop (`homie-studio mcp`): `game_lab` opens the lab and answers with a card: a still of both
|
|
84
|
+
versions at the move's busiest moment, their phases side by side, and Open.
|
|
85
|
+
- For your game's code, `@homie-rocks/studio/lab`: `lab.tunables`, `lab.phase`, `lab.track`, `lab.camera`,
|
|
86
|
+
`lab.overlay`, `lab.stage` (a training dummy for one move) and `lab.random` (dice for sparks and shake). Every call
|
|
87
|
+
does nothing outside the lab, so the calls stay in your game.
|
|
88
|
+
- Plugin: the perf skill's `try --measure` measures a change you keep for how it feels and says what it costs, without
|
|
89
|
+
reverting it (`try` on its own reverts anything that is not faster).
|
|
90
|
+
|
|
91
|
+
### Changed
|
|
92
|
+
|
|
93
|
+
- Gem Rush's bump lands. The bumped body holds for 70 ms, white and squashed against the hit, then flies stretched
|
|
94
|
+
along it, eases to a stop 185 px away at any frame rate (it used to slide a different distance at 12 and at 60
|
|
95
|
+
frames a second, then creep), and wobbles as it settles. Sparks, and a small camera kick for whoever was in it.
|
|
96
|
+
- Ember Vale's strike lands. The slime freezes white when it is hit and its damage pops up, a swipe carries through,
|
|
97
|
+
the slime is pushed back and wobbles like jelly, and a kill bursts. Your hero steps into each strike, so three
|
|
98
|
+
strikes in a row still reach.
|
|
99
|
+
- Both starters come ready for the lab: their numbers in `tunables.json` and a take in `lab.json`, which
|
|
100
|
+
`homie-studio game new` copies with the game. A new studio's AGENTS.md names both files and the lab command.
|
|
101
|
+
|
|
102
|
+
### Upgrade notes
|
|
103
|
+
|
|
104
|
+
- A game you made from a starter before 0.20.0 keeps its old feel: the new bump and strike come with a new copy
|
|
105
|
+
(`homie-studio game new`). To tune a move of your own game, tell Claude "build a Game Lab for <the move>": it adds
|
|
106
|
+
the lab's calls and a take, and commits that first.
|
|
107
|
+
- Today is your last commit, so commit (or stash) your work in progress before you open the lab. Its builds live in
|
|
108
|
+
`.studio/lab/`, which git already ignores.
|
|
109
|
+
|
|
20
110
|
## [0.19.2] - 2026-10-02
|
|
21
111
|
|
|
22
|
-
**Plugin 0.20.2** · [#29](https://github.com/homie-rocks/homie/pull/29) · [
|
|
112
|
+
**Plugin 0.20.2** · [#29](https://github.com/homie-rocks/homie/pull/29) · [release-2026-10-02-studio-0.19.2](https://github.com/homie-rocks/homie/releases/tag/release-2026-10-02-studio-0.19.2)
|
|
23
113
|
|
|
24
114
|
Release notes: this changelog, on every GitHub release, inside the npm package, and at the top of
|
|
25
115
|
`homie-studio upgrade`.
|
|
@@ -846,7 +936,8 @@ The first studio toolkit: a studio in one folder, a multiplayer starter, and dep
|
|
|
846
936
|
create, and `publish` to the homie.rocks directory.
|
|
847
937
|
- Plugin: the `studio-setup`, `game` and `publish` skills, and the remote Homie MCP.
|
|
848
938
|
|
|
849
|
-
[0.
|
|
939
|
+
[0.20.0]: https://github.com/homie-rocks/homie/pull/30
|
|
940
|
+
[0.19.2]: https://github.com/homie-rocks/homie/releases/tag/release-2026-10-02-studio-0.19.2
|
|
850
941
|
[0.19.1]: https://github.com/homie-rocks/homie/releases/tag/release-2026-10-02-studio-0.19.1
|
|
851
942
|
[0.19.0]: https://github.com/homie-rocks/homie/releases/tag/release-2026-10-02-studio-0.19.0
|
|
852
943
|
[0.18.2]: https://github.com/homie-rocks/homie/releases/tag/release-2026-10-02-studio-0.18.2
|
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ npx homie-studio game new crown-thief --from gem-rush --name "Crown Thief"
|
|
|
16
16
|
npx homie-studio dev # the whole site locally
|
|
17
17
|
npx homie-studio check crown-thief --url http://127.0.0.1:8787
|
|
18
18
|
npx homie-studio perf crown-thief --url http://127.0.0.1:8787 # how fast it runs: a computer and an emulated phone, host and replica
|
|
19
|
+
npx homie-studio lab crown-thief # the Game Lab: how a move feels, New beside Today, frame by frame
|
|
19
20
|
npx homie-studio deploy --plan # what deploy will create, and what it costs; changes nothing
|
|
20
21
|
npx homie-studio deploy # the studio's own Cloudflare
|
|
21
22
|
npx homie-studio publish # the homie.rocks directory
|
|
@@ -452,6 +453,44 @@ Minified JavaScript gzips about as well as source text, and a bundle with three.
|
|
|
452
453
|
source in strings, which no minifier touches; the line for such a file says `minified, 19% GLSL shader source in
|
|
453
454
|
strings`, and the JSON's `code` has every measure.
|
|
454
455
|
|
|
456
|
+
## How it feels: the Game Lab (`homie-studio lab`)
|
|
457
|
+
|
|
458
|
+
```sh
|
|
459
|
+
npx homie-studio lab crown-thief [--today HEAD|<ref>] [--port 8790] # http://127.0.0.1:8790/crown-thief/ until --stop
|
|
460
|
+
npx homie-studio lab check crown-thief [--take knock] [--fps 30] [--device phone] # the same, headless: numbers and pictures
|
|
461
|
+
npx homie-studio lab set crown-thief hitStopMs=80 knockDistance=200 # values into games/crown-thief/tunables.json
|
|
462
|
+
npx homie-studio lab --stop
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
A page on this computer for one mechanic at a time (a jump, a hit, a dash, a drift). It plays one short **take**
|
|
466
|
+
(`games/<id>/lab.json`: a few seconds, a seed, the presses) in two builds side by side: **New**, the working tree,
|
|
467
|
+
rebuilt on every save, and **Today**, the last commit (or any ref), built from git's own checkout of it with only the
|
|
468
|
+
game's files (and what its bundle reads from the studio), cached per commit in `.studio/lab/`. Both play on the lab's
|
|
469
|
+
clock: requestAnimationFrame, performance.now(), Date.now(), timers and Math.random() are the lab's in the game's page
|
|
470
|
+
(its harness, `lab/harness.js`, is the page's first script), so frame f is exactly f/fps seconds in both builds, the
|
|
471
|
+
dice are the same, the presses land on the same frames, and netplay's offline host, a round's clock and a knockback's
|
|
472
|
+
timer all slow down, pause and step together. Every replay is compared with the last run, frame by frame: the page
|
|
473
|
+
says "Replays match", or the frame where a build read something the lab does not drive.
|
|
474
|
+
|
|
475
|
+
The page: New, Today, both side by side or Today ghosted over New; the game's own views (`lab.camera`) and overlays
|
|
476
|
+
(`lab.overlay`: onion skin, arcs, reach); Desk (1280x800) or Phone (390x844); play, pause, a frame back or forward,
|
|
477
|
+
start and end; 1x, ½, ¼ and ⅒ speed (each frame is the same frame at any speed); 60, 30, 15 or 12 frames a second (what
|
|
478
|
+
a slower device draws); a frame counter; a timeline of the phases each build names (`lab.phase`), scrubbable, with each
|
|
479
|
+
frame's motion marked HOLD, MOVE or FAST; graphs of every tracked value (`lab.track`), New against Today, beside the
|
|
480
|
+
body's speed from the port probe and the game's JavaScript per frame; the phase each pane is in, as a caption; the
|
|
481
|
+
game's tunables (`games/<id>/tunables.json`) as sliders, with Today's value marked, that restart New with the new value
|
|
482
|
+
and write kept values back into the file ("Keep in code", one tunable a line); and REC, which records a new take while
|
|
483
|
+
the person plays it in New (both builds get the same presses, on the same frames). A phone opens it too.
|
|
484
|
+
|
|
485
|
+
A game opts in with `@homie-rocks/studio/lab` (`lab` from `/port` too, `HomiePort.lab` in a static game): every call is
|
|
486
|
+
a no-op outside the lab. Both starters do: Gem Rush's bump (a dummy to bump: `lab.stage`) and Ember Vale's strike (a
|
|
487
|
+
training slime). `lab check` plays the take headless twice per build and writes `.studio/lab/<id>/check-<time>/`:
|
|
488
|
+
summary.json (each phase's frames, each tracked value's peak and where it ends, the JavaScript per frame, errors, the
|
|
489
|
+
replay check), REPORT.md (New against Today), sheet.png (the lab at each phase's start) and still.jpg; and
|
|
490
|
+
`.studio/lab/<id>/latest.json`, the last check in a few fields (`.studio/lab/server.json` says where a running lab is).
|
|
491
|
+
The local MCP's `game_lab` starts the lab and answers with a card. The Homie plugin's `lab` skill has the method:
|
|
492
|
+
instrument and commit first (Today is the game as it is), then change how it feels, and keep what the person likes.
|
|
493
|
+
|
|
455
494
|
## Checks on a computer without a GPU
|
|
456
495
|
|
|
457
496
|
`check`, `port check` and `look` run Chrome headless. On a Mac they use its GPU. On Linux (a Claude Code cloud
|
package/bin/homie-studio.mjs
CHANGED
|
@@ -37,6 +37,20 @@
|
|
|
37
37
|
* (better, worse or the same within the noise: medians, a 95% interval, a rank test,
|
|
38
38
|
* and the guards that must not get worse; runs taken side by side with the same
|
|
39
39
|
* --pair label on both sides are judged as pairs, so a computer's drift cancels)
|
|
40
|
+
* homie-studio lab <id> [--port 8790] [--today HEAD|<ref>] [--take <name>]
|
|
41
|
+
* (the Game Lab, on this computer, until --stop: one take of the game (games/<id>/lab.json)
|
|
42
|
+
* played in New, the working tree, rebuilt on every save, beside Today, built from git's
|
|
43
|
+
* checkout of the ref, on one clock with the same seed and presses: slowed to a tenth or
|
|
44
|
+
* a frame at a time, at 60, 30, 15 or 12 fps, on a computer or a phone; a timeline of the
|
|
45
|
+
* phases the game names, graphs New against Today, the game's own overlays and views,
|
|
46
|
+
* sliders that write kept values into games/<id>/tunables.json, and REC for a new take.
|
|
47
|
+
* The game calls @homie-rocks/studio/lab; the plugin's lab skill has the method)
|
|
48
|
+
* homie-studio lab check <id> [--take <name>] [--today <ref>] [--device desk|phone] [--fps 60] [--frames 6] [--out <dir>]
|
|
49
|
+
* (the same take headless, twice per build: every phase, each tracked value's peak, the
|
|
50
|
+
* game's JavaScript per frame, whether a replay lands on the same frames, a contact sheet
|
|
51
|
+
* and a still; .studio/lab/<id>/check-<time>/)
|
|
52
|
+
* homie-studio lab set <id> <name>=<value> ... (values into games/<id>/tunables.json, one tunable a line)
|
|
53
|
+
* homie-studio lab --stop (this studio's Game Lab only, and the checkouts Today was built from)
|
|
40
54
|
* homie-studio chrome [install] [--fresh] (which Chrome the checks use; on Linux, `install` fetches Chrome for Testing;
|
|
41
55
|
* --fresh fetches it even when the machine has a Chrome)
|
|
42
56
|
* homie-studio look [<path>...] [--url <site>] [--shots <dir>] [--only computer,phone,sideways]
|
|
@@ -80,8 +94,9 @@
|
|
|
80
94
|
* homie-studio office invite <game> [--label "<who>"] [--uses 1|<n>|any] [--count 1] [--days <n>] [--server <id>]
|
|
81
95
|
* homie-studio office launch <game> private|invite|public [--remixable on|off] [--max <n>|game]
|
|
82
96
|
* homie-studio office kick <game> <room> <seat number | name> [--minutes 10]
|
|
97
|
+
* homie-studio office mute <game> <room> <seat number | name> [--minutes 10] [--off]
|
|
83
98
|
* homie-studio office close <game> <room> [--minutes 10] [--reopen]
|
|
84
|
-
* (kick, close and launch are ASKED for: the owner confirms each with one
|
|
99
|
+
* (kick, mute, close and launch are ASKED for: the owner confirms each with one
|
|
85
100
|
* tap in their own browser, from the link this prints)
|
|
86
101
|
* homie-studio office revoke (every office key, play ticket and pending ask ends)
|
|
87
102
|
*
|
|
@@ -169,6 +184,7 @@ import { look } from '../lib/look.mjs';
|
|
|
169
184
|
import { importPort, planPort } from '../lib/port.mjs';
|
|
170
185
|
import { portCheck } from '../lib/port-check.mjs';
|
|
171
186
|
import { DEFAULT_GOAL, perfCompare, perfRun, perfSizes } from '../lib/perf.mjs';
|
|
187
|
+
import { LAB_PORT, labServe, labSet, labStop } from '../lib/lab.mjs';
|
|
172
188
|
import { R2_COST, lineOf, mediaPlan, r2OverOf, recordUpload, resolveMedia, sizeOf, typeOf } from '../lib/media.mjs';
|
|
173
189
|
import { ensureMigrations, migrationWord, newStudio } from '../lib/scaffold.mjs';
|
|
174
190
|
import { lineDiff, upgradeApply, upgradePlan } from '../lib/upgrade.mjs';
|
|
@@ -177,7 +193,7 @@ import { listGames, newGame, readStudio, remixGame, requireStudio, siteUrl, star
|
|
|
177
193
|
import { STUDIO_VERSION } from '../lib/version.mjs';
|
|
178
194
|
import { statsKey, statsLink, statsRevoke, statsShare, statsShow } from '../lib/stats.mjs';
|
|
179
195
|
import { playersOwner, playersShow } from '../lib/players.mjs';
|
|
180
|
-
import { officeAnnounce, officeClose, officeInvite, officeKey, officeKick, officeLaunch, officeLines, officeLink, officeRevoke, officeShow } from '../lib/office.mjs';
|
|
196
|
+
import { officeAnnounce, officeClose, officeInvite, officeKey, officeKick, officeLaunch, officeLines, officeLink, officeMute, officeRevoke, officeShow } from '../lib/office.mjs';
|
|
181
197
|
import { agentsBrain, agentsBrainKey, agentsPass, agentsPasses, agentsRevoke, serversClose, serversLevel, serversLines, serversList, serversMember, serversNew, serversSet } from '../lib/servers.mjs';
|
|
182
198
|
import { AgentSeat } from '../lib/agent-seat.mjs';
|
|
183
199
|
import { Feed, currentFeed, currentId, flushProgress, publicFeed, readFeed, recordChange, startProgress } from '../lib/progress.mjs';
|
|
@@ -191,7 +207,7 @@ import { formatHandoff, handoff } from '../lib/handoff.mjs';
|
|
|
191
207
|
|
|
192
208
|
const argv = process.argv.slice(2);
|
|
193
209
|
const flags = new Map();
|
|
194
|
-
const BOOL_FLAGS = ['revoke', 'json', 'yes', 'detach', 'no-install', 'plan', 'stop', 'share', 'apply', 'diff', 'template', 'ci', 'fresh', 'install', 'remove', 'replace', 'artifact', 'open', 'reopen', 'kids', 'remote-ai', 'dry-run', 'verify', 'maps', 'profile'];
|
|
210
|
+
const BOOL_FLAGS = ['off', 'revoke', 'json', 'yes', 'detach', 'no-install', 'plan', 'stop', 'share', 'apply', 'diff', 'template', 'ci', 'fresh', 'install', 'remove', 'replace', 'artifact', 'open', 'reopen', 'kids', 'remote-ai', 'dry-run', 'verify', 'maps', 'profile'];
|
|
195
211
|
const positional = [];
|
|
196
212
|
for (let i = 0; i < argv.length; i++) {
|
|
197
213
|
const a = argv[i];
|
|
@@ -234,6 +250,25 @@ function print(result) {
|
|
|
234
250
|
for (const m of result.siteSkipped ?? []) lines.push(` left out: ${m.what}: ${m.why}`);
|
|
235
251
|
if (result.codexes?.length) lines.push(`Game Codex (the owner's private pages; codex link <id> opens one): ${result.codexes.map((x) => `/_studio/codex/${x}/`).join(', ')}`);
|
|
236
252
|
break;
|
|
253
|
+
case 'lab':
|
|
254
|
+
lines.push(result.already ? `The Game Lab is running: ${result.url}` : `The Game Lab stopped (it was at ${result.url}).`);
|
|
255
|
+
break;
|
|
256
|
+
case 'lab stop':
|
|
257
|
+
lines.push(result.stopped.length ? 'Stopped this studio\'s Game Lab.' : 'No Game Lab of this studio was running.', ...(result.removed.length ? [`Removed the checkouts Today was built from: ${result.removed.join(', ')}`] : []));
|
|
258
|
+
break;
|
|
259
|
+
case 'lab set':
|
|
260
|
+
lines.push(result.changed.length ? `${result.file}: ${result.changed.map((c) => `${c.name} ${c.from} -> ${c.to}`).join(', ')}` : `${result.file}: nothing changed`);
|
|
261
|
+
break;
|
|
262
|
+
case 'lab check': {
|
|
263
|
+
const det = (x) => (x === null ? 'same frames' : `DIFFERED from frame ${x}`);
|
|
264
|
+
lines.push(`${result.game}: take "${result.take ?? '(none)'}", ${result.frames} frames at ${result.fps} fps`,
|
|
265
|
+
` New: ${result.phases.new.join(' · ') || 'no phases'}; JavaScript ${result.cost.new?.mean} ms a frame (p95 ${result.cost.new?.p95}); replay: ${det(result.deterministic.new)}`,
|
|
266
|
+
result.phases.today ? ` Today (${result.today}): ${result.phases.today.join(' · ') || 'no phases'}; JavaScript ${result.cost.today?.mean} ms a frame (p95 ${result.cost.today?.p95}); replay: ${det(result.deterministic.today)}` : ` Today: ${result.todayNote ?? 'none'}`,
|
|
267
|
+
...(result.software ? [` Note: a software renderer (${result.renderer}): pictures are right, the JavaScript numbers are slow`] : []),
|
|
268
|
+
...result.errors.map((e) => ` error: ${e}`),
|
|
269
|
+
`Report: ${result.report}`, `Pictures: ${result.sheet}, ${result.still}`);
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
237
272
|
case 'media list':
|
|
238
273
|
for (const kind of ['music', 'videos']) {
|
|
239
274
|
lines.push(`${kind}/manifest.json: ${result[kind].pages.length} page(s)`);
|
|
@@ -377,6 +412,7 @@ function print(result) {
|
|
|
377
412
|
case 'agents brain':
|
|
378
413
|
case 'office launch':
|
|
379
414
|
case 'office kick':
|
|
415
|
+
case 'office mute':
|
|
380
416
|
case 'office close':
|
|
381
417
|
lines.push(result.asked ? `Asked: ${result.what}` : result.message, ...(result.asked ? [`Owner's one-tap link (until the ask ends, 15 min): ${result.link}`, result.use] : []));
|
|
382
418
|
break;
|
|
@@ -555,6 +591,7 @@ async function main() {
|
|
|
555
591
|
if (cmd === 'games') return { ok: true, command: 'games', games: listGames(root).map(({ dir, ...g }) => ({ ...g, dir: relative(root, dir) })) };
|
|
556
592
|
if (cmd === 'build') return tracked(root, 'build', () => build(root, { only: positional[1] ?? null, log, maps: flags.has('maps') }), 'build');
|
|
557
593
|
if (cmd === 'perf' && sub === 'sizes') return perfSizes(root, positional[2] ?? listGames(root)[0]?.id);
|
|
594
|
+
if (cmd === 'lab') return labCommand(root, sub);
|
|
558
595
|
if (cmd === 'perf') {
|
|
559
596
|
const game = sub ?? listGames(root)[0]?.id;
|
|
560
597
|
const url = flags.get('url') ?? siteUrl(root);
|
|
@@ -619,6 +656,7 @@ async function main() {
|
|
|
619
656
|
if (sub === 'invite') return officeInvite(root, positional[2], { url, label: flags.get('label'), uses: flags.get('uses'), count: flags.get('count'), days: flags.get('days'), server: flags.get('server') });
|
|
620
657
|
if (sub === 'launch') return officeLaunch(root, positional[2], positional[3], { url, remixable: flags.get('remixable'), max: flags.get('max') });
|
|
621
658
|
if (sub === 'kick') return officeKick(root, positional[2], positional[3], positional.slice(4).join(' ') || undefined, { url, minutes: flags.get('minutes') });
|
|
659
|
+
if (sub === 'mute') return officeMute(root, positional[2], positional[3], positional.slice(4).join(' ') || undefined, { url, minutes: flags.get('minutes'), off: flags.has('off') });
|
|
622
660
|
if (sub === 'close') return officeClose(root, positional[2], positional[3], { url, minutes: flags.get('minutes'), reopen: flags.has('reopen') });
|
|
623
661
|
if (sub === 'revoke') return officeRevoke(root, { url });
|
|
624
662
|
}
|
|
@@ -662,6 +700,21 @@ async function main() {
|
|
|
662
700
|
return { ok: false, command: cmd, why: `unknown command "${[cmd, sub].filter(Boolean).join(' ')}" (homie-studio help)` };
|
|
663
701
|
}
|
|
664
702
|
|
|
703
|
+
/** `homie-studio lab …` (lib/lab.mjs, lib/lab-check.mjs): the Game Lab of one game. */
|
|
704
|
+
async function labCommand(root, sub) {
|
|
705
|
+
if (flags.has('stop') || sub === 'stop') return labStop(root);
|
|
706
|
+
if (sub === 'set') return labSet(root, positional[2], positional.slice(3));
|
|
707
|
+
if (sub === 'check') {
|
|
708
|
+
const { labCheck } = await import('../lib/lab-check.mjs');
|
|
709
|
+
const num = (k, d) => (flags.has(k) ? Number(flags.get(k)) : d);
|
|
710
|
+
return labCheck(root, positional[2] ?? null, {
|
|
711
|
+
take: flags.get('take') ?? null, today: String(flags.get('today') ?? 'HEAD'), device: flags.get('device') ?? null, fps: num('fps', null),
|
|
712
|
+
out: flags.get('out') ? resolve(String(flags.get('out'))) : null, still: flags.get('still') ? resolve(String(flags.get('still'))) : null, frames: num('frames', 6), log,
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
return labServe(root, sub ?? null, { port: Number(flags.get('port') ?? LAB_PORT), today: String(flags.get('today') ?? 'HEAD'), take: flags.get('take') ?? null, log });
|
|
716
|
+
}
|
|
717
|
+
|
|
665
718
|
/** `homie-studio codex …` (lib/codex.mjs): the Game Codex of one game. */
|
|
666
719
|
function codexCommand(root, sub) {
|
|
667
720
|
const games = listGames(root);
|