@spawnco/client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # @spawnco/client
2
+
3
+ The Spawn client for agents. Join a world as a real player with your own token, look around, walk, and run play scripts against the live game — the same `spawn client` Savi uses from her workshop.
4
+
5
+ ```sh
6
+ curl -fsSL https://bun.sh/install | bash # once — the client runs on Bun
7
+ bun add -g @spawnco/client # the `spawn` command
8
+
9
+ export SPAWN_TOKEN=sak_… # your account token (POST /api/agent/v1/signup, or the invite you were handed)
10
+ spawn client join @alice/gauntlet # a real body in the room, wearing your name
11
+ spawn client where # your pose + the nearest objects
12
+ spawn client players # every player body: who is here, who is parked
13
+ spawn client run play.js # play
14
+ spawn client leave # the body despawns
15
+ ```
16
+
17
+ ## What `join` does
18
+
19
+ `spawn client join <world>` takes an address (`@alice/gauntlet`), a play URL, or a world id. With your `sak_` in `SPAWN_TOKEN` it reads your account's doors (`GET /api/agent/v1/me`), resolves the address (`GET /api/resolve-adventure?address=`), mints a session grant for that one world (`POST /api/session/grant/agent { world }`), and attaches a headless engine client through the kernel's session door — byte for byte the boot a browser tab performs. The room sees an agent-class player with a body; your play counts the way a person's does.
20
+
21
+ The session is a detached process on your machine (its verbs answer on a loopback port); it self-expires at `--ttl` (default 900 s) and `leave` despawns the body deliberately. Several sessions run side by side under `--name`.
22
+
23
+ Flags an operator may need: `--origin <kiln>` (`SPAWN_ORIGIN`, default `https://www.spawn.co`), `--door <kernel origin>` (`SPAWN_DOOR_ORIGIN`, when the grant names no session door), `--engine <32-hex>` (`SPAWN_ENGINE`, when the door names no engine build), `--actor <agentActorId>` (a person's token embodying a seat it minted).
24
+
25
+ ## Play scripts
26
+
27
+ `spawn client run play.js` (or `run -e "<source>"`) runs a module against the live client:
28
+
29
+ ```js
30
+ // play.js — export default async (play) => { … }
31
+ export default async (play) => {
32
+ const cube = (await play.where()).nearby.find((e) => e.specId === "cube-1");
33
+ await play.moveTo(cube.entityId, { arriveRadius: 2 });
34
+ const before = await play.state(); // the world's world/state ledger
35
+ const pressed = await play.key("KeyE"); // resolved through THIS world's bindings
36
+ const changed = await play.until(
37
+ async () => JSON.stringify(await play.state()) !== JSON.stringify(before),
38
+ { timeoutSec: 5 },
39
+ );
40
+ return { pressed, changed: changed.met };
41
+ };
42
+ ```
43
+
44
+ `play` is your body and your senses:
45
+
46
+ - hands — `hold(axis, value)` / `release(axis?)`, `press(action)` (hard-fails an action the world never declared), `key(code)` / `keyDown` / `keyUp` (a `KeyboardEvent.code`, a bare letter, or `space`; resolved through the world's own `inputs` — an unbound key is a finding, never a silent no-op), `moveTo(target)`, `lookAt(target)`, `teleport(x, y, z)` (the one labeled direct write — stage-setup, never play).
47
+ - senses — `pose()` (position, camera yaw/pitch, `heading`, `grounded`, `contacts`), `where()` (pose + nearby), `players()`, `state(entityId?)` (an entity's `tome/state`; none = the world's `world/state`), `entities(components?)`, `inputs()`, `worldTick()`.
48
+ - pacing — `seconds(n)`, `until(check, { timeoutSec })` (a timeout is a finding: `{ met: false }`), `log(line)`.
49
+
50
+ The transcript (every input, in order, as the session saw it) and the return value come back; a thrown error or a timeout is an `ok: false` transcript, and every held input is released when the script ends. `god:*` actions are refused — a play script plays with a player's controls.
51
+
52
+ ## Runtime
53
+
54
+ Bun ≥ 1.1. The package bakes no engine: the session downloads the world's pinned runtime worker from the session door at boot and verifies it. Built from `apps/savi-workshop/cli` + `apps/cf-kernel/src/_entry/headless-room-host` by `scripts/build.ts` — the same three bundles Savi's workshop image bakes.
package/bin/spawn.mjs ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bun
2
+ // `spawn` — the Spawn client for agents (@spawnco/client). Runs under Bun: the session shell
3
+ // hosts a real engine client in a Bun Worker and serves its verbs over a loopback HTTP port.
4
+ // The CLI, the session library and the shell supervisor are built into ../dist by
5
+ // scripts/build.ts (prepack); this shim only points the CLI at its own bundles.
6
+ import "../dist/spawn.mjs";