@leafarcade/cli 0.5.0 → 0.6.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 +7 -3
- package/dist/cli.mjs +65 -21
- package/package.json +1 -1
- package/skills/leafarcade/SKILL.md +3 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Let your own agent play a private board-game room while you use the browser board. The CLI reads positions and submits moves; it does not call a model, collect model credentials or implement its own opponent.
|
|
4
4
|
|
|
5
|
-
**Scope of 0.
|
|
5
|
+
**Scope of 0.6.0:** all 53 multiplayer games in unclocked, unrated rooms that admit agents. Nine games expose complete convenience move lists; Amazons, Lines of Action, The Last Greenhouse, Euchre, Cribbage and Fanorona expose complete legal wire actions. The others expose player-visible state and game-specific JSON action formats, validated by the game service. For these new games, use this release’s CLI with the matching game-service deployment. Local automatic play is available through the watcher below. OAuth login, seat-scoped pairing and resuming a stopped desktop chat are not implemented. Approval-only private tables require the host to approve a joining player before that seat is admitted. Invite-only tables admit players holding the invitation. Spectator and agent admission follow the host’s settings.
|
|
6
6
|
|
|
7
7
|
## Start a game from your agent
|
|
8
8
|
|
|
@@ -15,15 +15,19 @@ This creates a private, unrated room with the CLI agent as host, saves its sessi
|
|
|
15
15
|
```sh
|
|
16
16
|
leafarcade game wait --room ROOMCODE --after-revision R
|
|
17
17
|
leafarcade game start --room ROOMCODE --revision R
|
|
18
|
-
leafarcade game play --room ROOMCODE --revision R --column 4 --wait
|
|
18
|
+
leafarcade game play --room ROOMCODE --revision R --column 4 --wait --brief
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
Every JSON response includes `next`: the exact command to run next (wait, start, play, or stop and report). Agents should follow it and never ask the human to report their moves.
|
|
22
22
|
|
|
23
|
+
`--brief` (on `status`, `wait`, `start`, `play` and the other position commands) returns only `status` (room, revision, phase, your piece, whose turn, outcome), `board_labeled`, `last_move`, the legal moves and `next`, which repeats the flag. Games without a labeled board also include `visible_state`. A mid-game Connect Four status shrinks from about 1.6 KB to about 0.8 KB.
|
|
24
|
+
|
|
23
25
|
**Sandboxed agents.** Codex and Claude Code sandboxes block network access by default. Run every command with network access from the start; otherwise `npx` hangs without output and requests fail with `NETWORK_ERROR`. If `~/.config/leafarcade` is not writable, the CLI reports it; allow the write or use the same `LEAFARCADE_CONFIG_DIR` for every command.
|
|
24
26
|
|
|
25
27
|
Omit `--open` on a remote or headless machine and share the returned URL. If launching the browser fails, the room is still valid: open that URL manually or use `leafarcade game open --room ROOMCODE`. No agent credential is placed in the URL. `--capacity N` selects a supported seat count (default: the game's minimum); Go also supports `--go-size 9|13|19`. For a custom service, provide both `--server` and its matching browser frontend `--site`.
|
|
26
28
|
|
|
29
|
+
Tables created by the CLI are labeled as CLI tables in the service's aggregate daily counts (no names, codes or moves). Set `LEAFARCADE_TEST_TRAFFIC=1` for test runs; CI runs (`CI=true`) are marked as test traffic automatically.
|
|
30
|
+
|
|
27
31
|
The CLI stores a persistent creator identity so creating another room does not reset guest limits. An uncertain create keeps its request ID locally; rerunning the same command resumes that request. Do not discard the saved creation or blindly create another room after a network error. CLI-created rooms currently use guest limits; browser account limits and owner exemptions require authenticated browser-hosted creation.
|
|
28
32
|
|
|
29
33
|
## Install the agent skill
|
|
@@ -115,7 +119,7 @@ Use `game pause --room CODE --revision R` to explicitly take a break. The pause
|
|
|
115
119
|
| `game rules --game GAME` | Returns rules, board representation, coordinates and instructions offline |
|
|
116
120
|
| `game status --room CODE` | Returns public board, your seat, turn, revision, outcome, expiry and your legal actions |
|
|
117
121
|
| `game legal-actions --room CODE` | Returns actions, completeness flag, action types and revision |
|
|
118
|
-
| `game play --room CODE … --revision R [--wait]` | Sends one validated move; stale moves fail, with no automatic mutation retry. `--wait` then returns at your next turn, pause or finish |
|
|
122
|
+
| `game play --room CODE … --revision R [--wait] [--brief]` | Sends one validated move; stale moves fail, with no automatic mutation retry. `--wait` then returns at your next turn, pause or finish; `--brief` keeps only the decision fields |
|
|
119
123
|
| `game wait --room CODE --after-revision R [--timeout S]` | Subscribes by WebSocket; catches an already-played move, reconnects at most twice and returns an event or `waiting` |
|
|
120
124
|
| `watch --room CODE --agent-command EXECUTABLE` | Persistent subscription and platform-independent decision adapter; see below |
|
|
121
125
|
| `game pause/resume --room CODE --revision R` | Saves or clears your explicit pause; any paused player blocks play |
|
package/dist/cli.mjs
CHANGED
|
@@ -4799,9 +4799,9 @@ async function openBrowser(url) {
|
|
|
4799
4799
|
|
|
4800
4800
|
// packages/cli/src/next.ts
|
|
4801
4801
|
var defaultWaitSeconds = 90;
|
|
4802
|
-
function nextStep(position, event) {
|
|
4803
|
-
const room = `--room ${position.room}`, revision = position.revision;
|
|
4804
|
-
const wait = `leafarcade game wait ${room} --after-revision ${revision} --timeout ${defaultWaitSeconds}`;
|
|
4802
|
+
function nextStep(position, event, brief = false) {
|
|
4803
|
+
const room = `--room ${position.room}`, revision = position.revision, flag = brief ? " --brief" : "";
|
|
4804
|
+
const wait = `leafarcade game wait ${room} --after-revision ${revision} --timeout ${defaultWaitSeconds}${flag}`;
|
|
4805
4805
|
if (position.you.left) return "You left this room. Create or join another room only if the human asks.";
|
|
4806
4806
|
if (position.phase === "finished") return `Match finished${position.outcome?.label ? ` (${position.outcome.label})` : ""}. Tell the human the result. Rematch only if they ask: leafarcade game rematch ${room} --revision ${revision}.`;
|
|
4807
4807
|
if (position.paused) return `Paused. Stop and tell the human. Resume only when asked: leafarcade game resume ${room} --revision ${revision} (each player who paused must resume).`;
|
|
@@ -4810,11 +4810,11 @@ function nextStep(position, event) {
|
|
|
4810
4810
|
if (host && position.access_requests?.length) return `A player is asking to join. Approve the person the human expects: leafarcade game approve ${room} --revision ${revision} --request ID.`;
|
|
4811
4811
|
if (!host) return `Waiting for the host to start. Wait yourself: ${wait}`;
|
|
4812
4812
|
if (seated < position.capacity) return `Waiting for players (${seated}/${position.capacity}). Wait yourself; do not ask the human to report joining: ${wait}`;
|
|
4813
|
-
return `All seats are filled. Start the match: leafarcade game start ${room} --revision ${revision}`;
|
|
4813
|
+
return `All seats are filled. Start the match: leafarcade game start ${room} --revision ${revision}${flag}`;
|
|
4814
4814
|
}
|
|
4815
4815
|
if (position.turn?.yours) {
|
|
4816
4816
|
const move = position.legal_wire_actions_complete ? "'<one legal_wire_actions entry>'" : position.legal_actions_complete ? "'<one legal_actions entry>'" : "'<wire action JSON from game rules>'";
|
|
4817
|
-
return `Your move. Check your immediate win and your opponent's immediate threats (every line, including diagonals), then: leafarcade game play ${room} --revision ${revision} --action ${move} --wait`;
|
|
4817
|
+
return `Your move. Check your immediate win and your opponent's immediate threats (every line, including diagonals), then: leafarcade game play ${room} --revision ${revision} --action ${move} --wait${flag}`;
|
|
4818
4818
|
}
|
|
4819
4819
|
if (event === "waiting") return `No change yet. If the human is still playing, wait again: ${wait}. After several silent waits, stop and tell the human how to resume.`;
|
|
4820
4820
|
const outOfTurn = position.legal_actions_complete || position.legal_wire_actions_complete ? "" : " Some phases allow out-of-turn actions; check game rules for this phase first.";
|
|
@@ -4822,6 +4822,7 @@ function nextStep(position, event) {
|
|
|
4822
4822
|
}
|
|
4823
4823
|
|
|
4824
4824
|
// packages/cli/src/create.ts
|
|
4825
|
+
var testTraffic = (env = process.env) => env.LEAFARCADE_TEST_TRAFFIC === "1" || /^(1|true)$/i.test(env.CI ?? "");
|
|
4825
4826
|
async function createRoom(options2, signal) {
|
|
4826
4827
|
if (!supportsAgent(options2.game)) throw Error("Unknown game ID. Run leafarcade help games.");
|
|
4827
4828
|
const capacity = options2.capacity ?? allowedSeats(options2.game)[0];
|
|
@@ -4841,7 +4842,7 @@ async function createRoom(options2, signal) {
|
|
|
4841
4842
|
pending = await storeSession(pendingPath, { ...creator, name: name.trim(), creation: { requestId: randomUUID(), game: options2.game, capacity, goSize: options2.goSize, access: options2.access ?? "invite", allowSpectators: options2.allowSpectators ?? true } });
|
|
4842
4843
|
}
|
|
4843
4844
|
await rpc(pending, "register", { name: pending.name }, false, signal);
|
|
4844
|
-
const reply = await rpc(pending, "create", { ...pending.creation, agent: true }, false, signal);
|
|
4845
|
+
const reply = await rpc(pending, "create", { ...pending.creation, agent: true, client: "cli", ...testTraffic() ? { test: true } : {} }, false, signal);
|
|
4845
4846
|
if (typeof reply?.code !== "string") throw Error("The room service returned an invalid room code. Your saved creation is retained.");
|
|
4846
4847
|
const code = roomCode(reply.code);
|
|
4847
4848
|
const path = sessionPath(options2.server, code);
|
|
@@ -4852,7 +4853,7 @@ async function createRoom(options2, signal) {
|
|
|
4852
4853
|
const position = agentPosition(await readRoom(session, signal));
|
|
4853
4854
|
await forgetSession(pendingPath);
|
|
4854
4855
|
const url = roomUrl(options2.site, options2.game, code);
|
|
4855
|
-
return { event: "created", room: code, url, ...options2.open ? { browser: await openBrowser(url) } : {}, position, next: `You are the host. Give the human this url (no second room). ${nextStep(position)}` };
|
|
4856
|
+
return { event: "created", room: code, url, ...options2.open ? { browser: await openBrowser(url) } : {}, position, next: `You are the host. Give the human this url (no second room). ${nextStep(position, void 0, options2.brief)}` };
|
|
4856
4857
|
}
|
|
4857
4858
|
|
|
4858
4859
|
// packages/cli/src/skill.ts
|
|
@@ -4904,7 +4905,7 @@ async function installSkill(agent = "codex", update = false) {
|
|
|
4904
4905
|
// packages/cli/package.json
|
|
4905
4906
|
var package_default = {
|
|
4906
4907
|
name: "@leafarcade/cli",
|
|
4907
|
-
version: "0.
|
|
4908
|
+
version: "0.6.0",
|
|
4908
4909
|
private: false,
|
|
4909
4910
|
description: "Create private browser board games and play with your AI agent. Includes 53 games and an installable Codex/Claude skill.",
|
|
4910
4911
|
type: "module",
|
|
@@ -4956,19 +4957,19 @@ var commands = {
|
|
|
4956
4957
|
approve: { usage: "game approve --room CODE --revision R --request ID", description: "As host, approve a player or spectator access request shown by status. Approve only the requested participant.", options: ["room", "revision", "request", "server"] },
|
|
4957
4958
|
decline: { usage: "game decline --room CODE --revision R --request ID", description: "As host, decline a pending access request.", options: ["room", "revision", "request", "server"] },
|
|
4958
4959
|
tables: { usage: "game tables [--game GAME_ID]", description: "Find public tables with an open seat that accept AI agents. No automatic joining or polling.", options: ["game", "server"] },
|
|
4959
|
-
create: { usage: 'game create --game connect-four --name "My agent" [--open] [--capacity N]', description: "Create a table as the agent host (--access public|invite|private; default invite) and return the human browser link. --open launches the browser. Saved creator identity preserves limits across rooms; uncertain creation can be resumed. Give the human the returned url; never create a second room for the same request.", options: ["game", "name", "open", "capacity", "access", "no-spectators", "go-size", "site", "server"] },
|
|
4960
|
+
create: { usage: 'game create --game connect-four --name "My agent" [--open] [--capacity N]', description: "Create a table as the agent host (--access public|invite|private; default invite) and return the human browser link. --open launches the browser. Saved creator identity preserves limits across rooms; uncertain creation can be resumed. Give the human the returned url; never create a second room for the same request.", options: ["game", "name", "open", "capacity", "access", "no-spectators", "go-size", "site", "brief", "server"] },
|
|
4960
4961
|
open: { usage: "game open --room ROOMCODE", description: "Reopen the human browser link for a saved room. Does not create another game or share the agent credential.", options: ["room", "site", "server"] },
|
|
4961
|
-
start: { usage: "game start --room ROOMCODE --revision R", description: "Start a room you host once the requested players have joined. Other seats cannot start it.", options: ["room", "revision", "server"] },
|
|
4962
|
+
start: { usage: "game start --room ROOMCODE --revision R", description: "Start a room you host once the requested players have joined. Other seats cannot start it.", options: ["room", "revision", "brief", "server"] },
|
|
4962
4963
|
skill: { usage: "skill install --agent codex|claude [--update]", description: "Install the bundled Leaf Arcade skill into your chosen agent user directory. Explicit command only; never runs during npm install. A differing existing skill is kept unless you pass --update, which moves it to a backup outside the skills directory first. Use skill show to inspect it.", options: ["agent", "update"] },
|
|
4963
|
-
join: { usage: 'join ROOMCODE --name "My agent"', description: "Join an open private seat, or restore your saved seat. The human host starts the match. Joining identifies this client as an external agent.", options: ["name", "server"] },
|
|
4964
|
+
join: { usage: 'join ROOMCODE --name "My agent"', description: "Join an open private seat, or restore your saved seat. The human host starts the match. Joining identifies this client as an external agent.", options: ["name", "brief", "server"] },
|
|
4964
4965
|
rules: { usage: "game rules --game GAME_ID", description: "Read game rules, action payloads and coordinate conventions offline. JSON output is suitable for agents.", options: ["game"] },
|
|
4965
|
-
status: { usage: "game status --room ROOMCODE", description: "Read your seat's visible state, phase, turn, revision, outcome and action completeness. Hidden information belonging to other players stays hidden. Includes a labeled board, last_move and a concrete next command.", options: ["room", "server"] },
|
|
4966
|
+
status: { usage: "game status --room ROOMCODE", description: "Read your seat's visible state, phase, turn, revision, outcome and action completeness. Hidden information belonging to other players stays hidden. Includes a labeled board, last_move and a concrete next command.", options: ["room", "brief", "server"] },
|
|
4966
4967
|
"legal-actions": { usage: "game legal-actions --room ROOMCODE", description: "Read actions and legal_actions_complete. An empty partial list is not proof that no move exists. Use game rules and visible_state for JSON actions.", options: ["room", "server"] },
|
|
4967
|
-
play: { usage: "game play --room ROOMCODE --revision R MOVE_FLAGS [--wait]", description: "Submit one move at the exact latest revision. Use convenience flags or --action JSON, never both; --action also accepts an exact legal_actions entry unchanged. --wait then waits for your next turn or the end, like game wait. The service validates legality. On stale or uncertain results, read status before acting again.", options: ["room", "revision", "action", "column", "row", "from", "to", "promotion", "swap", "edge", "pit", "wait", "timeout", "server"] },
|
|
4968
|
-
wait: { usage: "game wait --room ROOMCODE --after-revision R [--timeout 90]", description: "Wait for changes through WebSocket (1\u20133600 seconds, default 90). Returns your_turn, changed, paused, finished, left or waiting. Use it after every move instead of asking the human to report theirs. A timeout is normal, not resignation. This command cannot restart a stopped agent.", options: ["room", "after-revision", "timeout", "server"] },
|
|
4969
|
-
pause: { usage: "game pause --room ROOMCODE --revision R", description: "Save your explicit pause in an active, unclocked private match. Any paused player blocks moves. Room expiry is not extended.", options: ["room", "revision", "server"] },
|
|
4970
|
-
resume: { usage: "game resume --room ROOMCODE --revision R", description: "Clear your own pause. Each player who paused must resume before play continues.", options: ["room", "revision", "server"] },
|
|
4971
|
-
rematch: { usage: "game rematch --room ROOMCODE --revision R", description: "Request another round after a normal finish. All players must agree and remain at the table. After resignation or leaving, create a fresh room. Never request a rematch without the human asking.", options: ["room", "revision", "server"] },
|
|
4968
|
+
play: { usage: "game play --room ROOMCODE --revision R MOVE_FLAGS [--wait]", description: "Submit one move at the exact latest revision. Use convenience flags or --action JSON, never both; --action also accepts an exact legal_actions entry unchanged. --wait then waits for your next turn or the end, like game wait. The service validates legality. On stale or uncertain results, read status before acting again.", options: ["room", "revision", "action", "column", "row", "from", "to", "promotion", "swap", "edge", "pit", "wait", "timeout", "brief", "server"] },
|
|
4969
|
+
wait: { usage: "game wait --room ROOMCODE --after-revision R [--timeout 90]", description: "Wait for changes through WebSocket (1\u20133600 seconds, default 90). Returns your_turn, changed, paused, finished, left or waiting. Use it after every move instead of asking the human to report theirs. A timeout is normal, not resignation. This command cannot restart a stopped agent.", options: ["room", "after-revision", "timeout", "brief", "server"] },
|
|
4970
|
+
pause: { usage: "game pause --room ROOMCODE --revision R", description: "Save your explicit pause in an active, unclocked private match. Any paused player blocks moves. Room expiry is not extended.", options: ["room", "revision", "brief", "server"] },
|
|
4971
|
+
resume: { usage: "game resume --room ROOMCODE --revision R", description: "Clear your own pause. Each player who paused must resume before play continues.", options: ["room", "revision", "brief", "server"] },
|
|
4972
|
+
rematch: { usage: "game rematch --room ROOMCODE --revision R", description: "Request another round after a normal finish. All players must agree and remain at the table. After resignation or leaving, create a fresh room. Never request a rematch without the human asking.", options: ["room", "revision", "brief", "server"] },
|
|
4972
4973
|
resign: { usage: "game resign --room ROOMCODE --revision R", description: "Leave/resign through the service and remove your saved credential after acknowledgement. Returns the final position when available. This ends participation; closing a terminal does not.", options: ["room", "revision", "server"] },
|
|
4973
4974
|
leave: { usage: "game leave --room ROOMCODE --revision R", description: "Alias for resign. Leaves the server room and removes local access after acknowledgement.", options: ["room", "revision", "server"] },
|
|
4974
4975
|
forget: { usage: "forget --room ROOMCODE", description: "Delete local access only. Does not resign or revoke a credential copied elsewhere. Use game leave to leave the room.", options: ["room", "server"] }
|
|
@@ -4995,6 +4996,7 @@ var options = {
|
|
|
4995
4996
|
"after-revision": "--after-revision R Revision you last saw; returns on any later change",
|
|
4996
4997
|
timeout: "--timeout S Wait limit, 1\u20133600 seconds (default 90)",
|
|
4997
4998
|
wait: "--wait After the move, wait for your next turn or the end",
|
|
4999
|
+
brief: "--brief Compact JSON: status, board_labeled, last_move, legal moves, next",
|
|
4998
5000
|
request: "--request ID Access request ID from status access_requests",
|
|
4999
5001
|
action: "--action JSON Wire action, or an exact legal_actions entry",
|
|
5000
5002
|
column: "--column N One-based column (Connect Four 1\u20137; with --row for grids)",
|
|
@@ -5145,6 +5147,7 @@ Start playing (the agent hosts)
|
|
|
5145
5147
|
Repeat step 4 until the match is finished.
|
|
5146
5148
|
Every response includes "next" with the exact command to run next.
|
|
5147
5149
|
Wait on the table yourself; never ask the human to report their move.
|
|
5150
|
+
Add --brief to status, play and wait for compact JSON; "next" keeps the flag.
|
|
5148
5151
|
|
|
5149
5152
|
Joining a browser-hosted room instead:
|
|
5150
5153
|
leafarcade join ROOMCODE --name "My agent", then follow "next".
|
|
@@ -5568,6 +5571,46 @@ async function watchRoom(session, options2, signal, emit, dependencies = watchDe
|
|
|
5568
5571
|
}
|
|
5569
5572
|
}
|
|
5570
5573
|
|
|
5574
|
+
// packages/cli/src/brief.ts
|
|
5575
|
+
var winner = (position) => {
|
|
5576
|
+
const seat = position.outcome?.winner;
|
|
5577
|
+
return seat === void 0 || seat === null ? null : seat === position.you.seat ? "you" : `seat ${seat + 1}`;
|
|
5578
|
+
};
|
|
5579
|
+
function briefPosition(position) {
|
|
5580
|
+
const turn = position.turn ? position.turn.yours ? "yours" : `seat ${position.turn.seat + 1}` : null;
|
|
5581
|
+
const wire = position.legal_wire_actions_complete ? position.legal_wire_actions ?? [] : null;
|
|
5582
|
+
return {
|
|
5583
|
+
status: {
|
|
5584
|
+
room: position.room,
|
|
5585
|
+
game: position.game,
|
|
5586
|
+
revision: position.revision,
|
|
5587
|
+
phase: position.phase,
|
|
5588
|
+
you: position.you.piece,
|
|
5589
|
+
turn,
|
|
5590
|
+
...position.paused ? { paused: true } : {},
|
|
5591
|
+
...position.you.left ? { left: true } : {},
|
|
5592
|
+
...position.phase === "waiting" ? { seated: position.players.filter((player) => !player.left).length, capacity: position.capacity } : {},
|
|
5593
|
+
...position.phase === "finished" ? { outcome: position.outcome?.label ?? null, winner: winner(position), terminal_reason: position.terminal_reason } : {}
|
|
5594
|
+
},
|
|
5595
|
+
board_labeled: position.board_labeled,
|
|
5596
|
+
last_move: position.last_move,
|
|
5597
|
+
...wire ? { legal_wire_actions: wire } : { legal_actions: position.legal_actions, legal_actions_complete: position.legal_actions_complete },
|
|
5598
|
+
// Card, tile and other games without a labeled board still need their seat-visible state.
|
|
5599
|
+
...position.board_labeled || !position.visible_state ? {} : { visible_state: position.visible_state },
|
|
5600
|
+
...position.access_requests?.length ? { access_requests: position.access_requests } : {}
|
|
5601
|
+
};
|
|
5602
|
+
}
|
|
5603
|
+
var isPosition = (value) => "protocol_version" in value && "you" in value && "board_size" in value;
|
|
5604
|
+
function briefResult(result) {
|
|
5605
|
+
if (isPosition(result)) {
|
|
5606
|
+
const { next: next2, ...position2 } = result;
|
|
5607
|
+
return { ...briefPosition(position2), next: next2 };
|
|
5608
|
+
}
|
|
5609
|
+
if (!("position" in result) || !result.position || typeof result.position !== "object") return result;
|
|
5610
|
+
const { position, next, ...rest } = result;
|
|
5611
|
+
return { ...rest, ...briefPosition(position), next };
|
|
5612
|
+
}
|
|
5613
|
+
|
|
5571
5614
|
// packages/cli/src/main.ts
|
|
5572
5615
|
var agentCliVersion2 = package_default.version;
|
|
5573
5616
|
function integer(value, label, min, max) {
|
|
@@ -5614,6 +5657,7 @@ try {
|
|
|
5614
5657
|
"after-revision": { type: "string" },
|
|
5615
5658
|
timeout: { type: "string" },
|
|
5616
5659
|
wait: { type: "boolean" },
|
|
5660
|
+
brief: { type: "boolean" },
|
|
5617
5661
|
update: { type: "boolean" },
|
|
5618
5662
|
json: { type: "boolean" },
|
|
5619
5663
|
help: { type: "boolean", short: "h" },
|
|
@@ -5650,7 +5694,7 @@ try {
|
|
|
5650
5694
|
if (values.server && server !== defaultServer && !values.site) throw Error("Provide --site for your custom server's browser frontend.");
|
|
5651
5695
|
const goSize = integer(values["go-size"] ?? "9", "go-size", 9, 19);
|
|
5652
5696
|
if (goSize !== 9 && goSize !== 13 && goSize !== 19) throw Error("go-size must be 9, 13 or 19.");
|
|
5653
|
-
result = await createRoom({ server, site: values.site ?? "https://leafarcade.com", game: values.game ?? "connect-four", name: values.name, capacity: values.capacity === void 0 ? void 0 : integer(values.capacity, "capacity", 2, 8), goSize, open: values.open, access: values.access, allowSpectators: !values["no-spectators"] }, controller.signal);
|
|
5697
|
+
result = await createRoom({ server, site: values.site ?? "https://leafarcade.com", game: values.game ?? "connect-four", name: values.name, capacity: values.capacity === void 0 ? void 0 : integer(values.capacity, "capacity", 2, 8), goSize, open: values.open, access: values.access, allowSpectators: !values["no-spectators"], brief: values.brief }, controller.signal);
|
|
5654
5698
|
} else if (command === "rules") result = agentRules(values.game);
|
|
5655
5699
|
else {
|
|
5656
5700
|
const server = serviceUrl(values.server ?? defaultServer);
|
|
@@ -5727,13 +5771,13 @@ try {
|
|
|
5727
5771
|
await rpc(session, "play", { revision, action }, false, controller.signal);
|
|
5728
5772
|
const played = agentPosition(await readRoom(session, controller.signal));
|
|
5729
5773
|
result = values.wait && played.phase === "playing" && !played.paused && !played.turn?.yours && !played.you.left ? { ...await waitForRoom(session, played.revision, waitSeconds, controller.signal), played: { revision: played.revision, last_move: played.last_move } } : { event: "played", position: played };
|
|
5730
|
-
} else result = command === "legal-actions" ? { protocol_version: 1, room, revision: position.revision, legal_actions: position.legal_actions, legal_actions_complete: position.legal_actions_complete, action_types: position.action_types, ...position.legal_wire_actions_complete ? { legal_wire_actions_complete: true, legal_wire_actions: position.legal_wire_actions } : {}, next: nextStep(position) } : { ...position, next: nextStep(position) };
|
|
5774
|
+
} else result = command === "legal-actions" ? { protocol_version: 1, room, revision: position.revision, legal_actions: position.legal_actions, legal_actions_complete: position.legal_actions_complete, action_types: position.action_types, ...position.legal_wire_actions_complete ? { legal_wire_actions_complete: true, legal_wire_actions: position.legal_wire_actions } : {}, next: nextStep(position, void 0, values.brief) } : { ...position, next: nextStep(position, void 0, values.brief) };
|
|
5731
5775
|
}
|
|
5732
5776
|
}
|
|
5733
5777
|
}
|
|
5734
5778
|
}
|
|
5735
|
-
if ("position" in result && result.position && !("next" in result)) result = { ...result, next: nextStep(result.position, result.event) };
|
|
5736
|
-
output(result);
|
|
5779
|
+
if ("position" in result && result.position && !("next" in result)) result = { ...result, next: nextStep(result.position, result.event, values.brief) };
|
|
5780
|
+
output(values.brief ? briefResult(result) : result);
|
|
5737
5781
|
}
|
|
5738
5782
|
} catch (error) {
|
|
5739
5783
|
const code = error instanceof ServiceError ? error.code : "CLI_ERROR";
|
package/package.json
CHANGED
|
@@ -20,10 +20,12 @@ leafarcade game create --game connect-four --name "My agent" # add --open on
|
|
|
20
20
|
|
|
21
21
|
1. Give the human the returned `url`. Do not create a second table, even if something looked wrong. Use `leafarcade game open --room CODE` to reopen it.
|
|
22
22
|
2. `leafarcade game wait --room CODE --after-revision R` until the human has joined. Then `leafarcade game start --room CODE --revision R`.
|
|
23
|
-
3. On your turn: `leafarcade game play --room CODE --revision R --column 4 --wait`. This moves, then waits until it is your turn again or the match ends. Repeat.
|
|
23
|
+
3. On your turn: `leafarcade game play --room CODE --revision R --column 4 --wait --brief`. This moves, then waits until it is your turn again or the match ends. Repeat.
|
|
24
24
|
|
|
25
25
|
Every JSON response has a `next` field with the exact command to run next. Follow it.
|
|
26
26
|
|
|
27
|
+
Add `--brief` to `status`, `wait`, `start` and `play`. It returns only `status` (room, revision, phase, your piece, whose turn, outcome), `board_labeled`, `last_move`, the legal moves and `next`, which keeps the flag. Games without a labeled board (cards, tiles) also get `visible_state`. Drop `--brief` once if you need the full position: player list, raw state or rules.
|
|
28
|
+
|
|
27
29
|
`leafarcade help games` lists game IDs. `--capacity N` picks a supported table size; the default is the game's minimum. Each additional agent needs its own `LEAFARCADE_CONFIG_DIR` and seat.
|
|
28
30
|
|
|
29
31
|
## Joining an existing table
|