@homie-rocks/studio 0.29.0 → 0.30.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 +18 -2
- package/README.md +2 -2
- package/bin/homie-studio.mjs +6 -4
- package/lib/client.mjs +19 -0
- package/lib/doctor.mjs +4 -4
- package/lib/handoff.mjs +10 -8
- package/lib/scaffold.mjs +7 -6
- package/lib/setup.mjs +9 -6
- package/lib/template-history.json +40 -0
- package/package.json +1 -1
- package/worker/index.mjs +1 -1
- package/worker/version.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,7 @@ What changed in each release of Homie's open parts, newest first:
|
|
|
5
5
|
- **`@homie-rocks/studio`**: the `homie-studio` command, your studio's site Worker, its multiplayer rooms (the
|
|
6
6
|
netplay contract), the port kit and the starter games. Every version is on npm, and Homie for Claude Desktop
|
|
7
7
|
(`homie-studio-<version>.mcpb` on each GitHub release) is built from it.
|
|
8
|
-
- **The Homie plugin** for Claude Code and
|
|
8
|
+
- **The Homie plugin** for Claude Code, Codex and Grok: its skills. It ships from this repository's marketplace with its
|
|
9
9
|
own version number, given beside each studio version below.
|
|
10
10
|
|
|
11
11
|
Each version says what you can now do (**Added**), what works differently (**Changed**), what was broken and is
|
|
@@ -17,9 +17,25 @@ 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.30.0] - 2026-10-03
|
|
21
|
+
|
|
22
|
+
**Plugin 0.31.0** · [#45](https://github.com/homie-rocks/homie/pull/45)
|
|
23
|
+
|
|
24
|
+
Grok can set up a studio and run it with the same holds as Claude Code and Codex.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- `homie-studio setup attach <hs_…> --client grok` and `homie-studio handoff <hb_…> --client grok`. A Grok Bot in the studio folder checks the repository in. The directory is told the chat is Grok. Claude and Codex pass `--client claude` or `--client codex`; with no `--client` the words stay "the chat".
|
|
29
|
+
- Homie's hooks for Grok Build (`plugins/homie/.grok-plugin/plugin.json`, `hooks/grok.json`, `hooks/grok.mjs`). They ask `hooks/lib/holds.mjs`, the module the Claude Code mod and the Codex hooks already ask, so the three hold the same calls with the same words. A held call waits for your own `proceed <code>`. Secrets are taken out of what Grok reads.
|
|
30
|
+
- A new studio's home band says **Connect this chat**, for Claude, Codex or Grok. Grok has no Cloudflare connector: approving Cloudflare in the browser is still your step.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- The plugin manifests (Claude Code, Codex, Grok and `plugin.json`) name plugin 0.31.0 and say the same thing. `plugins/homie/test/manifests.test.mjs` checks the Grok manifest with the others.
|
|
35
|
+
|
|
20
36
|
## [0.29.0] - 2026-10-03
|
|
21
37
|
|
|
22
|
-
**Plugin 0.30.0** · [#44](https://github.com/homie-rocks/homie/pull/44)
|
|
38
|
+
**Plugin 0.30.0** · [#44](https://github.com/homie-rocks/homie/pull/44) · [release-2026-10-03-studio-0.29.0](https://github.com/homie-rocks/homie/releases/tag/release-2026-10-03-studio-0.29.0)
|
|
23
39
|
|
|
24
40
|
The Lounge: a community room on your own studio, with play nights, moderators, live rooms and "show what you made" cards, and chat history you can turn on.
|
|
25
41
|
|
package/README.md
CHANGED
|
@@ -83,8 +83,8 @@ The button copies the public template (`template/` in this repository, exactly w
|
|
|
83
83
|
writes) into the person's GitHub, makes the Worker, the D1 database and the rooms on their Cloudflare account,
|
|
84
84
|
and connects Workers Builds. The site goes live with its own home page ("First game coming soon": no starter game
|
|
85
85
|
is put in a studio unless the person asks for one), and its Home has a
|
|
86
|
-
**Connect
|
|
87
|
-
In the
|
|
86
|
+
**Connect this chat** band: one tap links it to the chat that set it up (the Homie MCP tool `studio_setup`).
|
|
87
|
+
In the session that works in the repository, `homie-studio setup attach <hs_…> [--client claude|codex|grok]` then gives the studio
|
|
88
88
|
the name chosen in the chat and removes the band.
|
|
89
89
|
|
|
90
90
|
**The site claims itself in the directory.** The homie.rocks directory lists a studio only when its site serves
|
package/bin/homie-studio.mjs
CHANGED
|
@@ -173,10 +173,12 @@
|
|
|
173
173
|
* into it (stage, each check going green, a preview picture) and stop
|
|
174
174
|
* when asked. Without one, nothing changes.
|
|
175
175
|
*
|
|
176
|
-
* homie-studio handoff <hb_…>
|
|
176
|
+
* homie-studio handoff <hb_…> [--client claude|codex|grok]
|
|
177
|
+
* (a session started from the chat with one line, "Continue building
|
|
177
178
|
* <Studio>: build hb_…": fetch the person's brief, check in for a new studio, take
|
|
178
179
|
* the build so the chat's card follows it, and print the steps; HANDOFF.md)
|
|
179
|
-
* homie-studio setup attach <hs_…>
|
|
180
|
+
* homie-studio setup attach <hs_…> [--client claude|codex|grok]
|
|
181
|
+
* (this repository is the studio the chat's setup card is making: say so, once,
|
|
180
182
|
* learn its live address, and give a template copy its real name)
|
|
181
183
|
* homie-studio setup --via stripe-projects [--with elevenlabs] [--accept-tos] [--dry-run]
|
|
182
184
|
* (a prototype, 0.24.3: Cloudflare, and ElevenLabs with --with, made or linked
|
|
@@ -711,14 +713,14 @@ async function main() {
|
|
|
711
713
|
if (cmd === 'statusline') return installStatusLine(root, { remove: flags.has('remove'), replace: flags.has('replace'), project: flags.get('project') ?? null });
|
|
712
714
|
if (cmd === 'codex') return codexCommand(root, sub);
|
|
713
715
|
if (cmd === 'progress') return progressCommand(root, sub);
|
|
714
|
-
if (cmd === 'setup' && sub === 'attach') return setupAttach(root, positional[2], { homie: flags.get('homie') });
|
|
716
|
+
if (cmd === 'setup' && sub === 'attach') return setupAttach(root, positional[2], { homie: flags.get('homie'), client: flags.get('client') });
|
|
715
717
|
if (cmd === 'setup' && flags.has('via')) {
|
|
716
718
|
if (flags.get('via') !== 'stripe-projects') return { ok: false, command: 'setup', why: '--via stripe-projects is the one other way this version knows (the default is npx wrangler login)' };
|
|
717
719
|
const extra = String(flags.get('with') ?? '').split(',').map((x) => x.trim()).filter(Boolean);
|
|
718
720
|
if (extra.some((x) => x !== 'elevenlabs')) return { ok: false, command: 'setup', why: '--with takes elevenlabs (Cloudflare is always set up)' };
|
|
719
721
|
return setupViaProjects(root, { withElevenlabs: extra.includes('elevenlabs'), acceptTos: flags.has('accept-tos'), dryRun: flags.has('dry-run'), log });
|
|
720
722
|
}
|
|
721
|
-
if (cmd === 'handoff') return handoff(root, sub, { homie: flags.get('homie') });
|
|
723
|
+
if (cmd === 'handoff') return handoff(root, sub, { homie: flags.get('homie'), client: flags.get('client') });
|
|
722
724
|
if (cmd === 'port' && sub === 'import') return importPort(root, positional[2], flags.get('id'), { name: flags.get('name'), mode: flags.get('mode') });
|
|
723
725
|
if (cmd === 'port' && sub === 'check') {
|
|
724
726
|
const game = positional[2] ?? listGames(root)[0]?.id;
|
package/lib/client.mjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which chat is building the studio. Named only when the session says so (`--client`, or HOMIE_CLIENT).
|
|
3
|
+
* Anything else is "the chat": never a guess that it is Claude. Grok has no Cloudflare connector and no
|
|
4
|
+
* Claude Code window; `--client grok` is how a Grok Bot checks a repository in.
|
|
5
|
+
*/
|
|
6
|
+
const KNOWN = {
|
|
7
|
+
claude: { id: 'claude', card: "the Claude app's card", connect: 'Connect this chat' },
|
|
8
|
+
codex: { id: 'codex', card: "Codex's card", connect: 'Connect this chat' },
|
|
9
|
+
grok: { id: 'grok', card: "Grok's card", connect: 'Connect this chat' },
|
|
10
|
+
chat: { id: 'chat', card: 'the setup card', connect: 'Connect this chat' },
|
|
11
|
+
};
|
|
12
|
+
const ALIAS = { 'claude-code': 'claude', 'claude-app': 'claude', 'grok-bot': 'grok', grokbot: 'grok' };
|
|
13
|
+
|
|
14
|
+
/** @param {string | null | undefined} input `--client`, or null to read HOMIE_CLIENT */
|
|
15
|
+
export function studioClient(input, env = process.env) {
|
|
16
|
+
const raw = String(input ?? env.HOMIE_CLIENT ?? '').trim().toLowerCase();
|
|
17
|
+
const id = ALIAS[raw] ?? raw;
|
|
18
|
+
return KNOWN[id] ?? KNOWN.chat;
|
|
19
|
+
}
|
package/lib/doctor.mjs
CHANGED
|
@@ -152,7 +152,7 @@ export async function setupStatus({
|
|
|
152
152
|
{
|
|
153
153
|
const fixConnector = {
|
|
154
154
|
who: 'person',
|
|
155
|
-
say: 'Turn the Homie connector on. Claude Code: run /plugin, install or enable "homie" (marketplace homie-rocks/homie), then /mcp shows homie connected. Codex: install the Homie plugin from the same marketplace. The Claude app
|
|
155
|
+
say: 'Turn the Homie connector on. Claude Code: run /plugin, install or enable "homie" (marketplace homie-rocks/homie), then /mcp shows homie connected. Codex: install the Homie plugin from the same marketplace. Grok Build: the same plugin (.grok-plugin/plugin.json). The Claude app, or Grok: add the connector https://homie.rocks/mcp; its setup card (studio_setup) makes the studio on your own Cloudflare. Grok has no Cloudflare connector: you still approve Cloudflare in the browser. A Grok Bot on this computer runs the checklist and checks in with setup attach <hs_…> --client grok.',
|
|
156
156
|
};
|
|
157
157
|
let state; let detail;
|
|
158
158
|
if (net.blocked) { state = 'act'; detail = `the network proxy of this machine refused ${new URL(directory).host}`; }
|
|
@@ -162,7 +162,7 @@ export async function setupStatus({
|
|
|
162
162
|
else { state = 'unknown'; detail = net.ok ? `${new URL(directory).host} answers; your AI knows whether its Homie tools (studio_scaffold) are here` : `this computer's request to the directory failed: ${net.why}`; }
|
|
163
163
|
rows.push({
|
|
164
164
|
id: 'connector', label: 'Homie connector', need: 'required', state, detail,
|
|
165
|
-
unlocks: 'making the studio with the right toolkit version, listing games in the homie.rocks directory, and the cards
|
|
165
|
+
unlocks: 'making the studio with the right toolkit version, listing games in the homie.rocks directory, and the setup cards',
|
|
166
166
|
fix: state === 'ok' ? null : net.blocked
|
|
167
167
|
? { who: 'person', say: `In claude.ai/code, open this environment's settings, set Network access to Custom, add ${new URL(directory).host} (keep the default package managers), and start a new session.` }
|
|
168
168
|
: said === 'no' || net.ok ? fixConnector : null,
|
|
@@ -174,13 +174,13 @@ export async function setupStatus({
|
|
|
174
174
|
const local = root ? readLocal(root) : {};
|
|
175
175
|
const created = studio?.cloudflare?.created ?? [];
|
|
176
176
|
const deployed = Boolean(local.deployedAt || created.some((c) => String(c).startsWith('worker:')));
|
|
177
|
-
// A studio the
|
|
177
|
+
// A studio the setup card made (`setup attach` wrote connectedAt): Cloudflare's Deploy button made
|
|
178
178
|
// it on the person's account and Workers Builds deploys every merge, so no sign-in is needed on this computer.
|
|
179
179
|
const attached = Boolean(local.connectedAt);
|
|
180
180
|
const signedIn = attached || who.signedIn === true ? 'ok' : who.signedIn === false ? 'act' : 'later';
|
|
181
181
|
const verified = deployed || attached ? 'ok' : local.needs === 'cloudflare-verify-email' ? 'act' : 'later';
|
|
182
182
|
const parts = attached ? [
|
|
183
|
-
{ label: 'connected', state: 'ok', detail: 'through the
|
|
183
|
+
{ label: 'connected', state: 'ok', detail: 'through the setup card (Deploy to Cloudflare); Workers Builds deploys every merge' },
|
|
184
184
|
{ label: 'email verified', state: 'ok', detail: 'the site is live' },
|
|
185
185
|
] : [
|
|
186
186
|
{ label: 'signed in', state: signedIn, detail: who.signedIn === true ? (who.how ?? `${who.accounts ?? 1} account${who.accounts === 1 || who.accounts === null || who.accounts === undefined ? '' : 's'}`) : who.signedIn === false ? 'not signed in' : root ? (who.why ?? 'run npm install in the studio, then this again') : 'checked once the studio exists (it brings its own Wrangler)' },
|
package/lib/handoff.mjs
CHANGED
|
@@ -15,6 +15,7 @@ import { request } from './net.mjs';
|
|
|
15
15
|
import { startProgress } from './progress.mjs';
|
|
16
16
|
import { spawnSync } from 'node:child_process';
|
|
17
17
|
import { studioRepo } from './repo.mjs';
|
|
18
|
+
import { studioClient } from './client.mjs';
|
|
18
19
|
|
|
19
20
|
/** The repository this checkout IS (its origin remote), even Homie's engine one: the session must be in the studio's. */
|
|
20
21
|
function originRepo(root) {
|
|
@@ -36,13 +37,13 @@ const line = (v, max) => String(v ?? '').replace(/[\x00-\x1f\x7f]+/g, ' ').repla
|
|
|
36
37
|
const lines = (v, max) => String(v ?? '').split(/\r?\n/).map((l) => line(l, max)).filter(Boolean).join('\n').slice(0, max);
|
|
37
38
|
|
|
38
39
|
/** The steps for a kind of build, in this toolkit's commands (the brief says what; these say how). */
|
|
39
|
-
export function stepsFor(h, { games = [] } = {}) {
|
|
40
|
+
export function stepsFor(h, { games = [], client = 'chat' } = {}) {
|
|
40
41
|
const id = ID.test(String(h.id ?? '')) ? h.id : null;
|
|
41
42
|
const has = id && games.includes(id);
|
|
42
43
|
const q = (s) => JSON.stringify(String(s));
|
|
43
44
|
const work = {
|
|
44
45
|
setup: [
|
|
45
|
-
|
|
46
|
+
`This studio was just set up from ${client === 'grok' ? 'Grok' : client === 'codex' ? 'Codex' : client === 'claude' ? 'the Claude app' : 'this chat'}. Go through the new-studio checklist with the person, one step at a time and never ahead (the plugin's studio-setup skill): it has no game yet, and its home page says "First game coming soon".`,
|
|
46
47
|
'See a working game: `npx --no-install homie-studio demo` names a live one on Homie Arcade; copy a starter in only if they ask.',
|
|
47
48
|
'One small change from one sentence of theirs (the studio\'s colours in site/theme.json, a tagline in studio.json, or a first post in posts/), then plan their game into its Game Codex (`npx --no-install homie-studio codex new <id> --name "<Name>"`) before any game is made.',
|
|
48
49
|
],
|
|
@@ -67,16 +68,17 @@ export function stepsFor(h, { games = [] } = {}) {
|
|
|
67
68
|
];
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
export async function handoff(root, build, { homie, fetchFn = globalThis.fetch, attach = startProgress, checkIn = setupAttach, repoOf = originRepo } = {}) {
|
|
71
|
+
export async function handoff(root, build, { homie, client, fetchFn = globalThis.fetch, attach = startProgress, checkIn = setupAttach, repoOf = originRepo } = {}) {
|
|
71
72
|
const studio = readStudio(root);
|
|
73
|
+
const who = studioClient(client);
|
|
72
74
|
// "Continue building <Studio>: setup hs_…": a studio being set up, before any build: check in, then the checklist.
|
|
73
75
|
if (SETUP.test(String(build ?? ''))) {
|
|
74
|
-
const r = await checkIn(root, build, { homie });
|
|
76
|
+
const r = await checkIn(root, build, { homie, client: who.id === 'chat' ? null : who.id });
|
|
75
77
|
if (!r.ok) return { ok: false, command: 'handoff', setup: build, why: r.why, ...(r.needs ? { needs: r.needs } : {}) };
|
|
76
78
|
const h = { kind: 'setup', what: 'game', title: 'first steps', studio: r.name ?? studio.name, brief: '', setup: build };
|
|
77
|
-
return { ok: true, command: 'handoff', build: null, ...h, setup: { ok: true, message: r.message, renamed: r.renamed ?? [] }, attached: null, attachWhy: 'no build was opened in the chat yet (the setup card\'s Start building opens one)', steps: stepsFor(h) };
|
|
79
|
+
return { ok: true, command: 'handoff', build: null, ...h, client: who.id, setup: { ok: true, message: r.message, renamed: r.renamed ?? [] }, attached: null, attachWhy: 'no build was opened in the chat yet (the setup card\'s Start building opens one)', steps: stepsFor(h, { client: who.id }) };
|
|
78
80
|
}
|
|
79
|
-
if (!BUILD.test(String(build ?? ''))) return { ok: false, command: 'handoff', why:
|
|
81
|
+
if (!BUILD.test(String(build ?? ''))) return { ok: false, command: 'handoff', why: `the id from the one line the chat gave: hb_ (a build) or hs_ (a setup) and 32 hex digits` };
|
|
80
82
|
const directory = String(homie || studio.homie?.directory || 'https://homie.rocks').replace(/\/+$/, '');
|
|
81
83
|
// lib/net.mjs: through the environment's proxy, and an honest reason when it fails (the directory's own words, or
|
|
82
84
|
// the connection's error; only a refusal by the proxy names the network setting).
|
|
@@ -98,7 +100,7 @@ export async function handoff(root, build, { homie, fetchFn = globalThis.fetch,
|
|
|
98
100
|
};
|
|
99
101
|
let setup = null;
|
|
100
102
|
if (h.setup) {
|
|
101
|
-
const r = await checkIn(root, h.setup, { homie: directory });
|
|
103
|
+
const r = await checkIn(root, h.setup, { homie: directory, client: who.id === 'chat' ? null : who.id });
|
|
102
104
|
setup = { ok: r.ok, message: r.ok ? r.message : r.why, renamed: r.renamed ?? [] };
|
|
103
105
|
}
|
|
104
106
|
const taken = await attach(root, { attach: build, what: h.what, id: h.id, directory });
|
|
@@ -108,7 +110,7 @@ export async function handoff(root, build, { homie, fetchFn = globalThis.fetch,
|
|
|
108
110
|
ok: true, command: 'handoff', build, ...h, setup,
|
|
109
111
|
attached: taken.ok ? { feed: taken.build, shared: taken.shared?.build ?? build } : null,
|
|
110
112
|
attachWhy: taken.ok ? null : taken.why,
|
|
111
|
-
steps: stepsFor(h, { games }),
|
|
113
|
+
steps: stepsFor(h, { games, client: who.id }),
|
|
112
114
|
};
|
|
113
115
|
}
|
|
114
116
|
|
package/lib/scaffold.mjs
CHANGED
|
@@ -666,14 +666,14 @@ export { default, Table, Lobby } from '@homie-rocks/studio/worker';
|
|
|
666
666
|
return files;
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
-
/** The template's first-run band on Home: one link that connects this studio to the
|
|
670
|
-
export const CONNECT_BAND = `<!-- A new studio from the Homie template. This band goes away once
|
|
669
|
+
/** The template's first-run band on Home: one link that connects this studio to the chat that set it up. */
|
|
670
|
+
export const CONNECT_BAND = `<!-- A new studio from the Homie template. This band goes away once a chat works in the studio
|
|
671
671
|
(homie-studio setup attach removes it); delete it by hand any time. -->
|
|
672
672
|
<div class="band-in" style="text-align:center">
|
|
673
673
|
<p class="kicker">New studio</p>
|
|
674
674
|
<h2>{{studio.name}} is live</h2>
|
|
675
|
-
<p>It runs on your own Cloudflare account. Connect it to the
|
|
676
|
-
<p><a class="btn" href="/_studio/connect">Connect
|
|
675
|
+
<p>It runs on your own Cloudflare account. Connect it to the chat that set it up, and that chat takes it from here.</p>
|
|
676
|
+
<p><a class="btn" href="/_studio/connect">Connect this chat</a></p>
|
|
677
677
|
</div>
|
|
678
678
|
`;
|
|
679
679
|
|
|
@@ -689,8 +689,9 @@ The button copies this studio into your GitHub, creates its Worker, database and
|
|
|
689
689
|
deploys it with Workers Builds: every push to \`main\` goes live, and every other branch gets its own Preview.
|
|
690
690
|
The site goes live at once with its own home page ("first game coming soon"); the games come next.
|
|
691
691
|
|
|
692
|
-
Then open the site and tap **Connect
|
|
693
|
-
|
|
692
|
+
Then open the site and tap **Connect this chat**, or ask Claude, Codex or Grok (with the Homie connector,
|
|
693
|
+
https://homie.rocks/mcp) to set up your studio: it makes games, songs and videos here, in a pull request you merge
|
|
694
|
+
with one tap. Grok has no Cloudflare connector. Approving Cloudflare in the browser is still your step.
|
|
694
695
|
|
|
695
696
|
\`AGENTS.md\` says how everything here works.
|
|
696
697
|
`;
|
package/lib/setup.mjs
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* and answers with the studio's live address once its site is connected (the card's Cloudflare step);
|
|
7
7
|
* - a copy of the public template (studio.json `template: true`) gets the name the person chose in the chat:
|
|
8
8
|
* studio.json, the Worker's STUDIO_NAME, and AGENTS.md and README.md while they are untouched; its first-run
|
|
9
|
-
* "Connect
|
|
9
|
+
* "Connect this chat" band goes; studio.json takes the Worker and database names Cloudflare's form chose;
|
|
10
10
|
* - the live address goes into .studio/local.json (git-ignored), so `check --url` and the card know it.
|
|
11
11
|
*
|
|
12
12
|
* It sends the repository's owner/name and the studio's name and slug, nothing else: no key, no path, no code.
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
17
17
|
import { join } from 'node:path';
|
|
18
18
|
import { configNames } from './cloudflare.mjs';
|
|
19
|
+
import { studioClient } from './client.mjs';
|
|
19
20
|
import { request } from './net.mjs';
|
|
20
21
|
import { ENGINE_REPO, repoOf } from './repo.mjs';
|
|
21
22
|
import { CONNECT_BAND, agentsMd, readme, slugify, templateReadme } from './scaffold.mjs';
|
|
@@ -24,8 +25,9 @@ import { STUDIO_VERSION } from './version.mjs';
|
|
|
24
25
|
|
|
25
26
|
const SETUP_ID = /^hs_[a-f0-9]{32}$/;
|
|
26
27
|
|
|
27
|
-
export async function setupAttach(root, id, { homie } = {}) {
|
|
28
|
-
if (!SETUP_ID.test(String(id ?? ''))) return { ok: false, command: 'setup attach', why: 'attach to the setup id the
|
|
28
|
+
export async function setupAttach(root, id, { homie, client } = {}) {
|
|
29
|
+
if (!SETUP_ID.test(String(id ?? ''))) return { ok: false, command: 'setup attach', why: 'attach to the setup id the setup card showed: hs_ and 32 hex digits' };
|
|
30
|
+
const who = studioClient(client);
|
|
29
31
|
const studio = readStudio(root);
|
|
30
32
|
const directory = String(homie || studio.homie?.directory || 'https://homie.rocks').replace(/\/+$/, '');
|
|
31
33
|
const repo = repoOf(root);
|
|
@@ -34,7 +36,7 @@ export async function setupAttach(root, id, { homie } = {}) {
|
|
|
34
36
|
}
|
|
35
37
|
const sent = await request(`${directory}/api/studio/setup/${id}/attach`, {
|
|
36
38
|
method: 'POST', headers: { 'content-type': 'application/json', 'user-agent': `homie-studio/${STUDIO_VERSION}` },
|
|
37
|
-
body: JSON.stringify({ repo, studio: { name: studio.name, slug: studio.slug, template: studio.template === true }, version: STUDIO_VERSION }),
|
|
39
|
+
body: JSON.stringify({ repo, studio: { name: studio.name, slug: studio.slug, template: studio.template === true }, version: STUDIO_VERSION, ...(who.id === 'chat' ? {} : { client: who.id }) }),
|
|
38
40
|
});
|
|
39
41
|
// What went wrong, as it is: the directory's own answer, or the connection's error. Never a guess at the network.
|
|
40
42
|
if (!sent.ok) return { ok: false, command: 'setup attach', repo, ...(sent.status ? { status: sent.status } : {}), ...(sent.code ? { code: sent.code } : {}), ...(sent.needs ? { needs: sent.needs } : {}), why: sent.why };
|
|
@@ -72,10 +74,11 @@ export async function setupAttach(root, id, { homie } = {}) {
|
|
|
72
74
|
if (now.github !== repo) { writeStudio(root, { ...now, github: repo }); if (!renamed.includes('studio.json')) renamed.push('studio.json'); }
|
|
73
75
|
if (body.site) writeLocal(root, { url: body.site, connectedAt: new Date().toISOString() });
|
|
74
76
|
return {
|
|
75
|
-
ok: true, command: 'setup attach', setup: id, repo, site: body.site ?? null, name: name ?? studio.name, renamed,
|
|
76
|
-
message: body.site ? `This repository is ${name ?? studio.name}, live at ${body.site}.
|
|
77
|
+
ok: true, command: 'setup attach', setup: id, repo, client: who.id, site: body.site ?? null, name: name ?? studio.name, renamed,
|
|
78
|
+
message: body.site ? `This repository is ${name ?? studio.name}, live at ${body.site}. ${who.card[0].toUpperCase()}${who.card.slice(1)} shows it.` : `This repository is ${name ?? studio.name}. Its site is not connected yet: ${body.connect ?? `open the site and tap ${who.connect}`}.`,
|
|
77
79
|
next: [
|
|
78
80
|
...(renamed.length ? ['commit the renamed files on a branch and open a pull request (merging it names the live site)'] : []),
|
|
81
|
+
...(who.id === 'grok' ? ['a Grok Bot in this folder runs the checklist from here (there is no second session to start)'] : []),
|
|
79
82
|
'npm install, then npx --no-install homie-studio progress attach <hb_…> when the chat opened a build',
|
|
80
83
|
],
|
|
81
84
|
};
|
|
@@ -1431,6 +1431,46 @@
|
|
|
1431
1431
|
}
|
|
1432
1432
|
}
|
|
1433
1433
|
}
|
|
1434
|
+
},
|
|
1435
|
+
"0.30.0": {
|
|
1436
|
+
"files": {
|
|
1437
|
+
"CLAUDE.md": "ddf2b2a0376202f6",
|
|
1438
|
+
"README.md": "684b3cca8001673c",
|
|
1439
|
+
"HANDOFF.md": "ce7a8fd91c6bbee2",
|
|
1440
|
+
"games/README.md": "5ad32fe80c61f117",
|
|
1441
|
+
"music/README.md": "a3dc042dadd39df8",
|
|
1442
|
+
"videos/README.md": "16babc3437d5545b",
|
|
1443
|
+
"posts/README.md": "37841aae476bd540",
|
|
1444
|
+
"site/README.md": "2928ca4f079ab76b",
|
|
1445
|
+
"site/src/worker.mjs": "ca3f1630f2420ad3"
|
|
1446
|
+
},
|
|
1447
|
+
"sections": {
|
|
1448
|
+
"AGENTS.md": {
|
|
1449
|
+
"(intro)": "214aa46eb3a895d5",
|
|
1450
|
+
"## Layout": "8baf49e4291e7e88",
|
|
1451
|
+
"## Commands (all through the pinned CLI in node_modules)": "d80ec56e4b4fc4c6",
|
|
1452
|
+
"## The site": "366ecce1a424837a",
|
|
1453
|
+
"## Making games": "abf2f0d59341512d",
|
|
1454
|
+
"## The Game Codex and progress": "0738ef719cef3a4c",
|
|
1455
|
+
"## Art direction and models": "aaea65236db52126",
|
|
1456
|
+
"## Running live games (the back office)": "4aa5f513a7cd2c3b",
|
|
1457
|
+
"## Servers and AI seats": "98d08705087a33ec",
|
|
1458
|
+
"## Selling things (the shop)": "b04b0a2b25f524f2",
|
|
1459
|
+
"## Continuing a build from the Claude app": "0d68081e924ca419",
|
|
1460
|
+
"## Rules": "7e8458adc1b81ece",
|
|
1461
|
+
"## Telling Homie": "80b46fdd9e578db8",
|
|
1462
|
+
"## Beta": "9d02638207a26e73"
|
|
1463
|
+
}
|
|
1464
|
+
},
|
|
1465
|
+
"named": {
|
|
1466
|
+
"Homie Arcade": {
|
|
1467
|
+
"sections": {
|
|
1468
|
+
"AGENTS.md": {
|
|
1469
|
+
"## Commands (all through the pinned CLI in node_modules)": "b35df2c19cd4b534"
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1434
1474
|
}
|
|
1435
1475
|
}
|
|
1436
1476
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homie-rocks/studio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "A studio for games, music and video on its own Cloudflare: scaffold, multiplayer starters on the netplay contract, a port toolkit that makes existing single-player web games multiplayer, the site Worker with public rooms (Table/Lobby Durable Objects and D1, all on the free Workers plan, no payment method), a site with the hub's sections (Home, Games, Music, Videos, Rooms, Posts with feeds) in the studio's own look, an epic landing page for every game, big songs and videos in the studio's own R2 once it has storage (uploaded, checked by SHA-256, served at the same addresses), deploy, and the homie.rocks directory.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
package/worker/index.mjs
CHANGED
|
@@ -589,7 +589,7 @@ async function route(request, env, ctx) {
|
|
|
589
589
|
const getAll = () => (catP ??= catalogue(env, url.origin).then((cat) => named(cat, env)));
|
|
590
590
|
const getCat = () => (pubP ??= Promise.all([getAll(), settingsOf(env)]).then(([cat, settings]) => publicCatalogue(cat, settings, env)));
|
|
591
591
|
|
|
592
|
-
// "Connect
|
|
592
|
+
// "Connect this chat" (the template's first-run band): the directory's setup page, for this site's address.
|
|
593
593
|
if (path === '/_studio/connect' && read) {
|
|
594
594
|
const directory = directoryOf(await getCat());
|
|
595
595
|
if (!directory || env.HOMIE_PREVIEW === '1') return json({ ok: false, error: 'no-directory', message: 'This studio is not connected to a directory.' }, 404);
|
package/worker/version.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** The @homie-rocks/studio version this Worker code is (package.json's; a test keeps them equal). Cache-busts /_homie/site.js. */
|
|
2
|
-
export const STUDIO_VERSION_TAG = '0.
|
|
2
|
+
export const STUDIO_VERSION_TAG = '0.30.0';
|