pyyol 1.12.0 → 1.12.1

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/dist/cli.js CHANGED
@@ -26,7 +26,7 @@ const BAD = "✗";
26
26
  const WARN = "•";
27
27
  // N-player games use the group matchmaking queue; Goofspiel (1v1) uses the 2-player
28
28
  // queue. Same enqueue request shape, different endpoint.
29
- const GROUP_GAMES = new Set(["mafia", "monopoly"]);
29
+ const GROUP_GAMES = new Set(["mafia"]);
30
30
  const queuePathFor = (game) => (GROUP_GAMES.has(game) ? "/v1/group-queue" : "/v1/queue");
31
31
  // Public platform defaults. `pyyol login` with no flags hits the live platform;
32
32
  // self-hosted/local users override via PYYOL_API / PYYOL_DASHBOARD (or --api /
@@ -45,12 +45,10 @@ const AGENT_KEY_PREFIX = "sk_arena_";
45
45
  const PLAY_PATH = {
46
46
  goofspiel: "/v1/sandbox/pushplay",
47
47
  mafia: "/v1/mafia/pushplay",
48
- monopoly: "/v1/monopoly/pushplay",
49
48
  };
50
49
  const REPLAY_PATH = {
51
50
  goofspiel: "/v1/match/{id}/replay",
52
51
  mafia: "/v1/mafia/{id}/replay",
53
- monopoly: "/v1/monopoly/{id}/replay",
54
52
  };
55
53
  /**
56
54
  * The `--watch` value: where to follow a match. "ask" (default) shows the pop-up when
@@ -751,13 +749,22 @@ async function cmdQueue(a) {
751
749
  console.log(` ${String(t.key ?? "").padEnd(8)} ${String(Number(t.coins ?? 0)).padStart(8)} coins ${t.label ?? ""}`);
752
750
  return 0;
753
751
  }
754
- // Queuing needs a session auto-launch login on this device if absent.
755
- let token = c?.accessToken || str(a, "token") || process.env.PYYOL_TOKEN || "";
752
+ // Queuing is an AGENT action, so it needs the AGENT key.
753
+ //
754
+ // /v1/queue is registered with RequireScope(ScopeAgent). This sent the dashboard
755
+ // session token, so every ranked queue attempt came back `forbidden_scope` — for every
756
+ // developer, every time, on the command the scaffold prints as THE way to play ranked.
757
+ //
758
+ // It also read stored credentials BEFORE the explicit --token flag, so a caller passing
759
+ // a credential was ignored whenever anything happened to be logged in on the machine.
760
+ // connectionToken gets both right, and is what the play/dev commands already use to
761
+ // reach the same agent-scoped surface.
762
+ let { token } = connectionToken(a, c);
756
763
  if (!token) {
757
764
  const got = await ensureLogin(a);
758
765
  if (!got)
759
766
  return 2;
760
- token = got.accessToken || got.apiKey || "";
767
+ ({ token } = connectionToken(a, got));
761
768
  }
762
769
  const body = { game };
763
770
  if (str(a, "tier"))
@@ -807,13 +814,14 @@ async function cmdRoom(a) {
807
814
  console.error(` pyyol room join <room-id>`);
808
815
  return 2;
809
816
  }
810
- // A room is staked on both sides, so it needs a session exactly like `queue` does.
811
- let token = c?.accessToken || str(a, "token") || process.env.PYYOL_TOKEN || "";
817
+ // A room is an AGENT action exactly like `queue`: /v1/room/create and /v1/lobby/join
818
+ // are both agent-scoped, so the dashboard session token fails with `forbidden_scope`.
819
+ let { token } = connectionToken(a, c);
812
820
  if (!token) {
813
821
  const got = await ensureLogin(a);
814
822
  if (!got)
815
823
  return 2;
816
- token = got.accessToken || got.apiKey || "";
824
+ ({ token } = connectionToken(a, got));
817
825
  }
818
826
  if (action === "join") {
819
827
  const id = a.positionals[1] ?? "";
@@ -1466,13 +1474,6 @@ async function signedRequest(url, method, secret, payload, signPath) {
1466
1474
  }
1467
1475
  /** (view, legal, isLegalMove) for a probe turn — mirrors Python `_synthetic_turn`. */
1468
1476
  function syntheticTurn(game) {
1469
- if (game === "monopoly") {
1470
- const view = {
1471
- game: "monopoly", match_id: "validate", seat: 0, phase: "roll",
1472
- legal_actions: ["roll", "end_turn"], state: { players: [], phase: "roll" },
1473
- };
1474
- return [view, ["roll", "end_turn"], (m) => Boolean(m) && ["roll", "end_turn"].includes(m.action)];
1475
- }
1476
1477
  if (game === "mafia") {
1477
1478
  const view = {
1478
1479
  game: "mafia", match_id: "validate", your_seat: 1, your_role: "Villager", day: 1,
@@ -1768,7 +1769,7 @@ Commands:
1768
1769
  login [--with github|google|wallet] [--dashboard URL] [--token PAT]
1769
1770
  logout
1770
1771
  whoami
1771
- init <dir> [--arena goofspiel|mafia|monopoly] [--framework F] [--name N]
1772
+ init <dir> [--arena goofspiel|mafia] [--framework F] [--name N]
1772
1773
  dev [--matches N] local dev loop — SANDBOX, no stakes
1773
1774
  play <arena> [--ranked] [--tier] compete; --ranked = real stakes
1774
1775
  publish --manifest <file> certify your agent for ranked
package/dist/config.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export declare const CONFIG_NAME = "pyyol.toml";
2
- export declare const KNOWN_ARENAS: readonly ["goofspiel", "mafia", "monopoly"];
2
+ export declare const KNOWN_ARENAS: readonly ["goofspiel", "mafia"];
3
3
  export declare const MODES: readonly ["sandbox", "ranked"];
4
4
  export interface Config {
5
5
  name: string;
package/dist/config.js CHANGED
@@ -15,7 +15,7 @@
15
15
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
16
  import { basename, dirname, join, resolve } from "node:path";
17
17
  export const CONFIG_NAME = "pyyol.toml";
18
- export const KNOWN_ARENAS = ["goofspiel", "mafia", "monopoly"];
18
+ export const KNOWN_ARENAS = ["goofspiel", "mafia"];
19
19
  export const MODES = ["sandbox", "ranked"];
20
20
  const ORDER = [
21
21
  "name",
package/dist/index.d.ts CHANGED
@@ -27,6 +27,6 @@ export { instrument, uninstrument, recordResponse, extractUsage, patchPrototype
27
27
  export { route, enableGateway, disableGateway, gatewayBaseUrl, gatewayHeaders } from "./instrument.js";
28
28
  export type { ExtractedUsage } from "./instrument.js";
29
29
  export { estimateCost, rateFor, isKnown, canonical, cacheWriteRate, PRICING_VERSION, } from "./pricing.js";
30
- export { moveTool, moveToolChoice, moveToolName, moveFromResponse, boundMove, boundPlan, canonPlan, promptFor, PLAN_KEY, MAX_SPAN_ROUNDS, canonMove, canonGoofspiel, canonMafia, canonMonopoly, NO_TARGET, TOOL_GOOFSPIEL, TOOL_MAFIA, TOOL_MONOPOLY, GAME_GOOFSPIEL, GAME_MAFIA, GAME_MONOPOLY, } from "./movetools.js";
30
+ export { moveTool, moveToolChoice, moveToolName, moveFromResponse, boundMove, boundPlan, canonPlan, promptFor, PLAN_KEY, MAX_SPAN_ROUNDS, canonMove, canonGoofspiel, canonMafia, NO_TARGET, TOOL_GOOFSPIEL, TOOL_MAFIA, GAME_GOOFSPIEL, GAME_MAFIA, } from "./movetools.js";
31
31
  export type { Rate, CostArgs } from "./pricing.js";
32
32
  export { fingerprint as scaffoldFingerprint, fromRequest as scaffoldFromRequest, eligibleForPairing as scaffoldEligibleForPairing, SCAFFOLD_VERSION, } from "./scaffold.js";
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ export { moveTool, moveToolChoice, moveToolName, moveFromResponse, boundMove,
32
32
  // model made, not calls, so batching no longer costs an agent its verified share.
33
33
  boundPlan, canonPlan,
34
34
  // Renders a turn view as a prompt the move tools expect. Parity with Python's prompt_for.
35
- promptFor, PLAN_KEY, MAX_SPAN_ROUNDS, canonMove, canonGoofspiel, canonMafia, canonMonopoly, NO_TARGET, TOOL_GOOFSPIEL, TOOL_MAFIA, TOOL_MONOPOLY, GAME_GOOFSPIEL, GAME_MAFIA, GAME_MONOPOLY, } from "./movetools.js";
35
+ promptFor, PLAN_KEY, MAX_SPAN_ROUNDS, canonMove, canonGoofspiel, canonMafia, NO_TARGET, TOOL_GOOFSPIEL, TOOL_MAFIA, GAME_GOOFSPIEL, GAME_MAFIA, } from "./movetools.js";
36
36
  // Scaffold fingerprinting: the harness identity that makes a paired model comparison
37
37
  // possible (same scaffold, different model). Exported so a developer can print their own
38
38
  // fingerprint and confirm it is stable before relying on it.
package/dist/models.d.ts CHANGED
@@ -2,15 +2,14 @@
2
2
  * Typed models for the Pyyol push protocol.
3
3
  *
4
4
  * Lifecycle envelopes are fully typed. Turn views are typed for their common
5
- * fields; complex nested state (e.g. the Monopoly board) is left as an open
5
+ * fields; complex nested state is left as an open
6
6
  * object so the SDK stays thin and never drifts from the server's evolving state
7
7
  * shape. Nothing here contains game strategy — these are pure data shapes.
8
8
  */
9
9
  export declare const PROTOCOL_VERSION = "1.0";
10
10
  export declare const GOOFSPIEL = "goofspiel";
11
- export declare const MONOPOLY = "monopoly";
12
11
  export declare const MAFIA = "mafia";
13
- export declare const SUPPORTED_GAMES: readonly ["goofspiel", "monopoly", "mafia"];
12
+ export declare const SUPPORTED_GAMES: readonly ["goofspiel", "mafia"];
14
13
  export interface InitializeRequest {
15
14
  protocol: string;
16
15
  match_id: string;
@@ -71,24 +70,6 @@ export interface GoofspielView {
71
70
  warn_in_ms: number;
72
71
  raw: Record<string, unknown>;
73
72
  }
74
- export interface MonopolyView {
75
- game: "monopoly";
76
- match_id: string;
77
- seat: number;
78
- phase: string;
79
- legal_actions: string[];
80
- /** The raw board dict (players, holdings, phase, …) — inspect directly. */
81
- state: Record<string, unknown>;
82
- /** The engine's turn counter for this decision. The turn proof is bound to
83
- * (agent, match, ROUND), so a wrong number verifies against nothing and the decision
84
- * silently fails to earn Verified. The runtime reads it for you; it is typed here for
85
- * agents that call the gateway themselves. */
86
- round?: number;
87
- /** Proves a model call was made FOR THIS decision. Attach as X-Pyyol-Proof when calling
88
- * the gateway yourself; the SDK runtime does it automatically. */
89
- turn_proof?: string;
90
- raw: Record<string, unknown>;
91
- }
92
73
  export interface MafiaView {
93
74
  game: "mafia";
94
75
  match_id: string;
@@ -103,7 +84,7 @@ export interface MafiaView {
103
84
  private: Record<string, unknown>[];
104
85
  raw: Record<string, unknown>;
105
86
  }
106
- export type TurnView = GoofspielView | MonopolyView | MafiaView | Record<string, unknown>;
87
+ export type TurnView = GoofspielView | MafiaView | Record<string, unknown>;
107
88
  export interface GoofspielMove {
108
89
  round?: number;
109
90
  card: number;
@@ -129,45 +110,6 @@ export interface GoofspielMove {
129
110
  */
130
111
  rationale?: string;
131
112
  }
132
- /**
133
- * OPEN_TO_TABLE is the Monopoly trade target meaning "offer this to the whole table".
134
- *
135
- * -1, never 0: seat 0 is a real player, so a forgotten target is an offer to THEM, not to
136
- * everyone. Any seat that can satisfy an open offer may take it; they are asked in seat order
137
- * and the first yes wins, so a `reject_trade` from one seat only PASSES — the offer stays up
138
- * for the seats behind it (watch for `trade_declined` rather than `trade_rejected`).
139
- */
140
- export declare const OPEN_TO_TABLE = -1;
141
- /**
142
- * A proposed exchange. You give `give_*` and receive `want_*`.
143
- *
144
- * Houses and hotels cannot be traded (official rule) — sell them back to the bank first.
145
- */
146
- export interface MonopolyTrade {
147
- /** The seat you are offering to, or OPEN_TO_TABLE (-1) for the whole table. */
148
- target: number;
149
- give_props?: number[];
150
- give_cash?: number;
151
- /** Get-out-of-jail-free cards. */
152
- give_cards?: number;
153
- want_props?: number[];
154
- want_cash?: number;
155
- want_cards?: number;
156
- }
157
- export interface MonopolyMove {
158
- action: string;
159
- property?: number;
160
- amount?: number;
161
- /** REQUIRED to originate a `propose_trade` or `counter_trade`; ignored otherwise.
162
- * Without it the SDK could not express a Monopoly trade AT ALL — the negotiation half of
163
- * the game was unreachable from JavaScript and Python even though the engine had always
164
- * supported it. `accept_trade` / `reject_trade` need no payload: they answer the offer
165
- * already on the table. */
166
- trade?: MonopolyTrade;
167
- /** Published as table talk before the move lands, so the table watches you argue the deal
168
- * rather than a silent action appearing. Same one-call economics as Goofspiel's. */
169
- rationale?: string;
170
- }
171
113
  export interface MafiaMove {
172
114
  action: string;
173
115
  /** Seat to act on. Seat 0 is a real player, so for a night action
@@ -177,7 +119,7 @@ export interface MafiaMove {
177
119
  target?: number;
178
120
  tone?: string;
179
121
  /** Your PUBLIC in-game speech. Rides along with the action — one model call produces both
180
- * the decision and what the table hears. This is the house style; Goofspiel and Monopoly
122
+ * the decision and what the table hears. This is the house style; Goofspiel
181
123
  * do the same with `rationale`. */
182
124
  text?: string;
183
125
  /** PRIVATE reasoning, captured for observability only — deliberately NOT published. In
@@ -185,6 +127,6 @@ export interface MafiaMove {
185
127
  * plan to the town, so this never becomes table talk. Use `text` to speak. */
186
128
  rationale?: string;
187
129
  }
188
- export type Move = GoofspielMove | MonopolyMove | MafiaMove | Record<string, unknown>;
130
+ export type Move = GoofspielMove | MafiaMove | Record<string, unknown>;
189
131
  /** Parse a turn body into its typed view; unknown games return the raw object. */
190
132
  export declare function parseView(d: Record<string, any>): TurnView;
package/dist/models.js CHANGED
@@ -2,24 +2,14 @@
2
2
  * Typed models for the Pyyol push protocol.
3
3
  *
4
4
  * Lifecycle envelopes are fully typed. Turn views are typed for their common
5
- * fields; complex nested state (e.g. the Monopoly board) is left as an open
5
+ * fields; complex nested state is left as an open
6
6
  * object so the SDK stays thin and never drifts from the server's evolving state
7
7
  * shape. Nothing here contains game strategy — these are pure data shapes.
8
8
  */
9
9
  export const PROTOCOL_VERSION = "1.0";
10
10
  export const GOOFSPIEL = "goofspiel";
11
- export const MONOPOLY = "monopoly";
12
11
  export const MAFIA = "mafia";
13
- export const SUPPORTED_GAMES = [GOOFSPIEL, MONOPOLY, MAFIA];
14
- /**
15
- * OPEN_TO_TABLE is the Monopoly trade target meaning "offer this to the whole table".
16
- *
17
- * -1, never 0: seat 0 is a real player, so a forgotten target is an offer to THEM, not to
18
- * everyone. Any seat that can satisfy an open offer may take it; they are asked in seat order
19
- * and the first yes wins, so a `reject_trade` from one seat only PASSES — the offer stays up
20
- * for the seats behind it (watch for `trade_declined` rather than `trade_rejected`).
21
- */
22
- export const OPEN_TO_TABLE = -1;
12
+ export const SUPPORTED_GAMES = [GOOFSPIEL, MAFIA];
23
13
  const asNum = (v, d = 0) => (typeof v === "number" ? v : Number(v ?? d) || d);
24
14
  const asStr = (v, d = "") => (typeof v === "string" ? v : d);
25
15
  const asArr = (v) => (Array.isArray(v) ? v : []);
@@ -41,16 +31,6 @@ export function parseView(d) {
41
31
  warn_in_ms: Number(d.warn_in_ms ?? 0) || 0,
42
32
  raw: d,
43
33
  };
44
- case MONOPOLY:
45
- return {
46
- game: MONOPOLY,
47
- match_id: asStr(d.match_id),
48
- seat: asNum(d.seat),
49
- phase: asStr(d.phase),
50
- legal_actions: asArr(d.legal_actions),
51
- state: d.state ?? {},
52
- raw: d,
53
- };
54
34
  case MAFIA: {
55
35
  const aliveRaw = d.alive ?? {};
56
36
  const alive = {};
@@ -1,9 +1,7 @@
1
1
  export declare const TOOL_GOOFSPIEL = "play_card";
2
2
  export declare const TOOL_MAFIA = "mafia_action";
3
- export declare const TOOL_MONOPOLY = "monopoly_action";
4
3
  export declare const GAME_GOOFSPIEL = "goofspiel";
5
4
  export declare const GAME_MAFIA = "mafia";
6
- export declare const GAME_MONOPOLY = "monopoly";
7
5
  /**
8
6
  * NO_TARGET is the wire convention for "this action names no seat".
9
7
  *
@@ -73,14 +71,6 @@ export declare function canonGoofspiel(card: number): string;
73
71
  * substituted for doing nothing.
74
72
  */
75
73
  export declare function canonMafia(kind: string, target: number): string;
76
- /**
77
- * The bound form of a Monopoly action: verb, property, amount.
78
- *
79
- * All three are always rendered, including zeros. Omitting an absent field would let "mortgage
80
- * property 0 for 50" and "mortgage property 50 for 0" reduce to the same string, and two
81
- * different decisions sharing one canonical form is the one thing this mechanism cannot tolerate.
82
- */
83
- export declare function canonMonopoly(kind: string, property?: number, amount?: number): string;
84
74
  /**
85
75
  * Reduce move arguments to the canonical string a bound decision stores.
86
76
  *
package/dist/movetools.js CHANGED
@@ -41,10 +41,8 @@
41
41
  // its own name keeps passing.
42
42
  export const TOOL_GOOFSPIEL = "play_card";
43
43
  export const TOOL_MAFIA = "mafia_action";
44
- export const TOOL_MONOPOLY = "monopoly_action";
45
44
  export const GAME_GOOFSPIEL = "goofspiel";
46
45
  export const GAME_MAFIA = "mafia";
47
- export const GAME_MONOPOLY = "monopoly";
48
46
  /**
49
47
  * NO_TARGET is the wire convention for "this action names no seat".
50
48
  *
@@ -56,7 +54,6 @@ export const NO_TARGET = -1;
56
54
  const TOOL_BY_GAME = {
57
55
  [GAME_GOOFSPIEL]: TOOL_GOOFSPIEL,
58
56
  [GAME_MAFIA]: TOOL_MAFIA,
59
- [GAME_MONOPOLY]: TOOL_MONOPOLY,
60
57
  };
61
58
  // JSON Schema for each game's move arguments. Kept minimal on purpose: every field a model
62
59
  // must fill is a field it can fill wrongly, and a wrong field means an unbound turn.
@@ -83,49 +80,10 @@ const SCHEMAS = {
83
80
  },
84
81
  required: ["kind"],
85
82
  },
86
- [GAME_MONOPOLY]: {
87
- type: "object",
88
- properties: {
89
- kind: {
90
- type: "string",
91
- description: "The action verb, e.g. buy, pass, bid, mortgage, build.",
92
- },
93
- property: {
94
- type: "integer",
95
- description: "Board index of the property this action concerns, or 0. On a bid during a " +
96
- "HOUSING SHORTAGE auction this is the square you would put the piece on.",
97
- },
98
- amount: { type: "integer", description: "Coin amount this action carries, or 0." },
99
- // The trade payload. OPTIONAL and NOT part of the canonical bound form — a trade binds
100
- // on its verb alone (a nested structure re-rendered cosmetically differently would
101
- // reject an honest turn), so nothing here can cost a turn its binding. Without it a
102
- // bound agent could act but never DEAL, which is most of Monopoly.
103
- trade: {
104
- type: "object",
105
- description: "Required to propose or counter a trade. Ignored for other actions.",
106
- properties: {
107
- target: {
108
- type: "integer",
109
- description: "Seat to offer to, or -1 to offer to the WHOLE TABLE (any player who can " +
110
- "satisfy it may take it). Never 0 for 'everyone' — seat 0 is a real player.",
111
- },
112
- give_props: { type: "array", items: { type: "integer" }, description: "Squares you give." },
113
- give_cash: { type: "integer", description: "Cash you give." },
114
- give_cards: { type: "integer", description: "Get-out-of-jail-free cards you give." },
115
- want_props: { type: "array", items: { type: "integer" }, description: "Squares you want." },
116
- want_cash: { type: "integer", description: "Cash you want." },
117
- want_cards: { type: "integer", description: "Get-out-of-jail-free cards you want." },
118
- },
119
- required: ["target"],
120
- },
121
- },
122
- required: ["kind"],
123
- },
124
83
  };
125
84
  const DESCRIPTIONS = {
126
85
  [GAME_GOOFSPIEL]: "Play one card from your hand for this round. Call this to make your move.",
127
86
  [GAME_MAFIA]: "Take your action for this phase. Call this to make your move.",
128
- [GAME_MONOPOLY]: "Take your action for this turn. Call this to make your move.",
129
87
  };
130
88
  /** The tool name that carries a move for `game`, or "" if the game has no contract. */
131
89
  export function moveToolName(game) {
@@ -373,16 +331,6 @@ export function canonMafia(kind, target) {
373
331
  const t = Math.trunc(target) < 0 ? "none" : String(Math.trunc(target));
374
332
  return `${kind.trim().toLowerCase()}:${t}`;
375
333
  }
376
- /**
377
- * The bound form of a Monopoly action: verb, property, amount.
378
- *
379
- * All three are always rendered, including zeros. Omitting an absent field would let "mortgage
380
- * property 0 for 50" and "mortgage property 50 for 0" reduce to the same string, and two
381
- * different decisions sharing one canonical form is the one thing this mechanism cannot tolerate.
382
- */
383
- export function canonMonopoly(kind, property = 0, amount = 0) {
384
- return `${kind.trim().toLowerCase()}:${Math.trunc(property)}:${Math.trunc(amount)}`;
385
- }
386
334
  /**
387
335
  * Reduce move arguments to the canonical string a bound decision stores.
388
336
  *
@@ -403,14 +351,6 @@ export function canonMove(game, args) {
403
351
  const [target, has] = intArg(args, "target");
404
352
  return canonMafia(kind, has ? target : NO_TARGET);
405
353
  }
406
- if (game === GAME_MONOPOLY) {
407
- const kind = strArg(args, "kind").trim();
408
- if (!kind)
409
- return null;
410
- const [property] = intArg(args, "property");
411
- const [amount] = intArg(args, "amount");
412
- return canonMonopoly(kind, property, amount);
413
- }
414
354
  return null;
415
355
  }
416
356
  /**
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "1.12.0";
1
+ export declare const SDK_VERSION = "1.12.1";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // GENERATED by scripts/genversion.mjs — do not edit by hand.
2
2
  // Source of truth is the "version" field in package.json.
3
- export const SDK_VERSION = "1.12.0"; // x-release-please-version
3
+ export const SDK_VERSION = "1.12.1"; // x-release-please-version
package/dist/watch.js CHANGED
@@ -31,7 +31,6 @@ export const DEFAULT_DASHBOARD = (process.env.PYYOL_DASHBOARD || "").replace(/\/
31
31
  const WATCH_ROUTE = {
32
32
  goofspiel: "/goofspiel",
33
33
  mafia: "/arena/mafia",
34
- monopoly: "/monopoly",
35
34
  };
36
35
  /** Browser URL for a specific live match, or "" when it cannot be named exactly. */
37
36
  export function watchUrl(arena, matchId, dashboard = DEFAULT_DASHBOARD) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pyyol",
3
- "version": "1.12.0",
3
+ "version": "1.12.1",
4
4
  "description": "Official JS/TS SDK for pyyol — run AI game-playing agents locally over a WebSocket (Beta)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",