pyyol 1.2.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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +267 -0
  3. package/dist/adapter.d.ts +24 -0
  4. package/dist/adapter.d.ts.map +1 -0
  5. package/dist/adapter.js +68 -0
  6. package/dist/adapter.js.map +1 -0
  7. package/dist/cli.d.ts +20 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +1325 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config.d.ts +28 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +160 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/credentials.d.ts +13 -0
  16. package/dist/credentials.d.ts.map +1 -0
  17. package/dist/credentials.js +175 -0
  18. package/dist/credentials.js.map +1 -0
  19. package/dist/index.d.ts +26 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +21 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/login.d.ts +16 -0
  24. package/dist/login.d.ts.map +1 -0
  25. package/dist/login.js +107 -0
  26. package/dist/login.js.map +1 -0
  27. package/dist/mode.d.ts +12 -0
  28. package/dist/mode.d.ts.map +1 -0
  29. package/dist/mode.js +55 -0
  30. package/dist/mode.js.map +1 -0
  31. package/dist/models.d.ts +105 -0
  32. package/dist/models.d.ts.map +1 -0
  33. package/dist/models.js +68 -0
  34. package/dist/models.js.map +1 -0
  35. package/dist/rules.d.ts +4 -0
  36. package/dist/rules.d.ts.map +1 -0
  37. package/dist/rules.js +26 -0
  38. package/dist/rules.js.map +1 -0
  39. package/dist/runtime.d.ts +97 -0
  40. package/dist/runtime.d.ts.map +1 -0
  41. package/dist/runtime.js +372 -0
  42. package/dist/runtime.js.map +1 -0
  43. package/dist/server.d.ts +75 -0
  44. package/dist/server.d.ts.map +1 -0
  45. package/dist/server.js +174 -0
  46. package/dist/server.js.map +1 -0
  47. package/dist/signing.d.ts +42 -0
  48. package/dist/signing.d.ts.map +1 -0
  49. package/dist/signing.js +115 -0
  50. package/dist/signing.js.map +1 -0
  51. package/dist/simulator.d.ts +38 -0
  52. package/dist/simulator.d.ts.map +1 -0
  53. package/dist/simulator.js +109 -0
  54. package/dist/simulator.js.map +1 -0
  55. package/dist/telemetry.d.ts +81 -0
  56. package/dist/telemetry.d.ts.map +1 -0
  57. package/dist/telemetry.js +225 -0
  58. package/dist/telemetry.js.map +1 -0
  59. package/dist/version.d.ts +2 -0
  60. package/dist/version.d.ts.map +1 -0
  61. package/dist/version.js +4 -0
  62. package/dist/version.js.map +1 -0
  63. package/package.json +62 -0
  64. package/rules/games.md +349 -0
  65. package/rules/llms-full.txt +985 -0
@@ -0,0 +1,372 @@
1
+ import { SDK_VERSION } from "./server.js";
2
+ import { Tracer } from "./telemetry.js";
3
+ // Frame types — byte-identical to the Go gateway (internal/agentgw/frame.go).
4
+ const HELLO = "hello", REGISTERED = "registered", PONG = "pong";
5
+ const INITIALIZE = "initialize", TURN = "turn", EVENT = "event", GAME_END = "game_end", ERROR = "error";
6
+ const REGISTER = "register", PING = "ping", RESPONSE = "response";
7
+ export const PROTOCOL_VERSION = "1.0";
8
+ /** POST {apiUrl}/v1/auth/refresh {refresh_token} → {access, refresh} or null.
9
+ * A non-200 (refresh token expired/revoked) → null → terminal (must re-login). */
10
+ async function defaultRefreshHttp(apiUrl, refreshToken) {
11
+ try {
12
+ const res = await fetch(apiUrl.replace(/\/$/, "") + "/v1/auth/refresh", {
13
+ method: "POST",
14
+ headers: { "content-type": "application/json" },
15
+ body: JSON.stringify({ refresh_token: refreshToken }),
16
+ });
17
+ if (!res.ok)
18
+ return null;
19
+ const j = (await res.json().catch(() => null));
20
+ if (!j?.dashboard_token)
21
+ return null;
22
+ return { access: j.dashboard_token, refresh: j.refresh_token ?? "" };
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ }
28
+ function summarizeMove(game, move) {
29
+ if (!move || typeof move !== "object")
30
+ return String(move);
31
+ const m = move;
32
+ if ("card" in m)
33
+ return `card ${m.card}${m.round !== undefined ? ` (round ${m.round})` : ""}`;
34
+ if ("action" in m)
35
+ return `${m.action}${m.target !== undefined ? ` → ${m.target}` : ""}`;
36
+ return JSON.stringify(m);
37
+ }
38
+ function summarizeResult(result) {
39
+ if (!result || typeof result !== "object")
40
+ return "game finished";
41
+ const outer = result;
42
+ // The gateway may nest the outcome under `result` (with a viewer-relative winner
43
+ // label like "you"/"opponent"/"tie"); unwrap it like the Python SDK does.
44
+ const inner = (outer.result && typeof outer.result === "object" ? outer.result : outer);
45
+ const bits = [];
46
+ if (inner.winner !== undefined && inner.winner !== null)
47
+ bits.push(`winner: ${inner.winner}`);
48
+ for (const k of ["coins_delta", "your_coins", "coins"]) {
49
+ if (inner[k]) {
50
+ bits.push(`${k}=${inner[k]}`);
51
+ break;
52
+ }
53
+ }
54
+ return "game finished" + (bits.length ? " · " + bits.join(" · ") : "");
55
+ }
56
+ /** Terminal connector failure (e.g. auth rejected) — not retried. */
57
+ export class ConnectorError extends Error {
58
+ }
59
+ /** Internal: the access token was just refreshed; reconnect immediately with the
60
+ * new token (not a transport error, so no backoff / scary log). */
61
+ class RefreshRetry extends Error {
62
+ }
63
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
64
+ /** Parse a dotted version into a comparable number array; unparseable ⇒ [0] so a
65
+ * garbage latest_sdk never triggers a spurious nudge. */
66
+ function versionParts(v) {
67
+ const core = v.trim().replace(/^v/, "").split(/[-+]/)[0];
68
+ const parts = core.split(".").map((p) => Number(p));
69
+ return parts.some((n) => !Number.isFinite(n)) ? [0] : parts;
70
+ }
71
+ /** True if a is strictly older than b (numeric field-by-field). */
72
+ function isOlder(a, b) {
73
+ const pa = versionParts(a);
74
+ const pb = versionParts(b);
75
+ const n = Math.max(pa.length, pb.length);
76
+ for (let i = 0; i < n; i++) {
77
+ const x = pa[i] ?? 0;
78
+ const y = pb[i] ?? 0;
79
+ if (x !== y)
80
+ return x < y;
81
+ }
82
+ return false;
83
+ }
84
+ export class RuntimeConnector {
85
+ agent;
86
+ opts;
87
+ stopped = false;
88
+ nudged = false; // print the "upgrade available" notice at most once
89
+ registered = false; // true once this session's register succeeded
90
+ refreshAttempts = 0; // per-connection guard against a refresh loop
91
+ // Opt-in Pyyol Lens telemetry (no-op unless PYYOL_LENS_ENDPOINT+KEY set).
92
+ // Correlated to the match trace so the agent's model/tool calls render with
93
+ // the platform's authoritative gateway spans.
94
+ tracer;
95
+ constructor(agent, opts) {
96
+ this.agent = agent;
97
+ this.opts = opts;
98
+ this.tracer = Tracer.fromEnv({ agentId: opts.agentId, service: opts.name });
99
+ }
100
+ /** The gateway echoes the newest published version on the registered frame.
101
+ * Print a one-line upgrade hint at most once (not on every reconnect). */
102
+ maybeNudge(latest) {
103
+ if (this.nudged || typeof latest !== "string" || !latest)
104
+ return;
105
+ if (isOlder(SDK_VERSION, latest)) {
106
+ this.nudged = true;
107
+ const notify = this.opts.onNotice ?? ((m) => console.warn(m));
108
+ notify(`a new pyyol ${latest} is available (you have ${SDK_VERSION}) — upgrade with \`npm update pyyol\``);
109
+ }
110
+ }
111
+ /** Connect and serve until stopped, reconnecting with exponential backoff. */
112
+ async run() {
113
+ let backoff = 1000;
114
+ const maxBackoff = this.opts.maxBackoffMs ?? 30000;
115
+ while (!this.stopped) {
116
+ try {
117
+ await this.session();
118
+ backoff = 1000;
119
+ if (this.opts.reconnect === false)
120
+ return;
121
+ }
122
+ catch (e) {
123
+ if (e instanceof RefreshRetry) {
124
+ // Token was just refreshed — reconnect right away with the new one, no
125
+ // backoff and no "connection lost" noise (even in one-shot mode).
126
+ this.feed("reauth", "access token refreshed — reconnecting");
127
+ backoff = 1000;
128
+ continue;
129
+ }
130
+ if (e instanceof ConnectorError)
131
+ throw e; // terminal (auth) — don't spin
132
+ if (this.opts.reconnect === false || this.stopped)
133
+ throw e;
134
+ // Surface the retry so a bad/absent network doesn't look like a frozen
135
+ // terminal (the reason is often "can't reach the platform").
136
+ this.feed("reconnecting", `${e.message} — retrying in ${Math.round(backoff / 1000)}s`);
137
+ await sleep(backoff);
138
+ backoff = Math.min(backoff * 2, maxBackoff);
139
+ }
140
+ }
141
+ }
142
+ stop() {
143
+ this.stopped = true;
144
+ void this.tracer.close();
145
+ }
146
+ /** Emit a live-feed line to the CLI/console, if a sink was provided. */
147
+ feed(kind, detail) {
148
+ this.opts.onFeed?.(kind, detail);
149
+ }
150
+ /** Warn (once) when connecting over cleartext ws:// to a non-local host — the
151
+ * register token is sent in the clear (mirrors the Python connector). */
152
+ securityCheck() {
153
+ try {
154
+ const u = new URL(this.opts.url);
155
+ if (u.protocol === "ws:") {
156
+ const h = u.hostname.toLowerCase();
157
+ if (!(h === "localhost" || h === "127.0.0.1" || h === "::1" || h.endsWith(".localhost"))) {
158
+ // Security warning goes straight to stderr — NOT through onNotice, which is
159
+ // reserved for the SDK upgrade nudge (a host app may route that to its UI).
160
+ console.warn(`WARNING: connecting over insecure ws:// to ${h} — your token is sent in cleartext; use wss://`);
161
+ }
162
+ }
163
+ }
164
+ catch {
165
+ /* unparseable url — the connect below will surface it */
166
+ }
167
+ }
168
+ /** One connection lifetime: resolves on a clean stop, rejects on transport loss. */
169
+ /** Spend the refresh token for a fresh access token; returns true on success
170
+ * (opts.token rotated + persisted). Bounded per connection to avoid a loop. */
171
+ async tryRefresh() {
172
+ const rt = this.opts.refreshToken;
173
+ const api = this.opts.apiUrl;
174
+ if (!rt || !api || this.refreshAttempts >= 2)
175
+ return false;
176
+ this.refreshAttempts++;
177
+ let pair;
178
+ try {
179
+ pair = await (this.opts.refreshHttp ?? defaultRefreshHttp)(api, rt);
180
+ }
181
+ catch {
182
+ return false;
183
+ }
184
+ if (!pair?.access)
185
+ return false;
186
+ this.opts.token = pair.access;
187
+ if (pair.refresh)
188
+ this.opts.refreshToken = pair.refresh;
189
+ if (this.opts.onTokens) {
190
+ try {
191
+ await this.opts.onTokens(pair.access, this.opts.refreshToken ?? "");
192
+ }
193
+ catch {
194
+ /* persistence is best-effort */
195
+ }
196
+ }
197
+ return true;
198
+ }
199
+ session() {
200
+ this.securityCheck();
201
+ this.registered = false;
202
+ const WS = this.opts.WebSocketImpl ?? globalThis.WebSocket;
203
+ if (!WS)
204
+ throw new ConnectorError("no WebSocket implementation (Node >=22 or pass WebSocketImpl)");
205
+ let host = this.opts.url;
206
+ try {
207
+ host = new URL(this.opts.url).host;
208
+ }
209
+ catch { /* keep raw */ }
210
+ this.feed("connecting", host);
211
+ const ws = new WS(this.opts.url);
212
+ return new Promise((resolve, reject) => {
213
+ let hb;
214
+ let settled = false;
215
+ let opened = false;
216
+ let queue = Promise.resolve(); // serialize frame dispatch (FIFO)
217
+ const send = (f) => ws.send(JSON.stringify(f));
218
+ // Bound the OPENING handshake — the WHATWG WebSocket has no connect timeout, so
219
+ // a dead-air link (accepts TCP, never upgrades) would hang forever. On timeout
220
+ // we fail with a transient error → run() reconnects.
221
+ const openTimer = setTimeout(() => {
222
+ if (!opened)
223
+ fail(new Error("connect timed out (platform unreachable)"));
224
+ }, this.opts.connectTimeoutMs ?? 10000);
225
+ // Liveness watchdog — a window with zero frames means the link is (half-)dead.
226
+ // Rearmed on every received frame below; a healthy socket sees gateway PINGs
227
+ // well inside the window, so it only fires when the link has actually gone away.
228
+ const hbMs = this.opts.heartbeatMs ?? 10000;
229
+ const liveWindow = this.opts.livenessTimeoutMs ?? Math.max(30000, hbMs * 3);
230
+ let liveTimer;
231
+ const armLive = () => {
232
+ if (liveTimer)
233
+ clearTimeout(liveTimer);
234
+ liveTimer = setTimeout(() => fail(new Error("liveness timeout — no frames received (link half-open?)")), liveWindow);
235
+ };
236
+ const cleanup = () => {
237
+ clearTimeout(openTimer);
238
+ if (liveTimer)
239
+ clearTimeout(liveTimer);
240
+ if (hb)
241
+ clearInterval(hb);
242
+ try {
243
+ ws.close();
244
+ }
245
+ catch { /* already closing */ }
246
+ };
247
+ const fail = (e) => { if (!settled) {
248
+ settled = true;
249
+ cleanup();
250
+ reject(e);
251
+ } };
252
+ const done = () => { if (!settled) {
253
+ settled = true;
254
+ cleanup();
255
+ resolve();
256
+ } };
257
+ armLive(); // start the clock now; the first HELLO should arrive promptly
258
+ ws.addEventListener("open", () => { opened = true; });
259
+ ws.addEventListener("message", (ev) => {
260
+ armLive(); // any frame (of any type) proves the link is alive → reset
261
+ let frame;
262
+ try {
263
+ frame = JSON.parse(typeof ev.data === "string" ? ev.data : String(ev.data));
264
+ }
265
+ catch {
266
+ return;
267
+ }
268
+ // Chain onto the queue so frames are handled strictly one-at-a-time, in
269
+ // arrival order — responses can't be sent out of order under a burst.
270
+ queue = queue.then(async () => {
271
+ if (settled)
272
+ return;
273
+ try {
274
+ await this.dispatch(frame, send, () => {
275
+ hb = setInterval(() => send({ t: PING }), this.opts.heartbeatMs ?? 10000);
276
+ });
277
+ }
278
+ catch (e) {
279
+ fail(e instanceof Error ? e : new Error(String(e)));
280
+ }
281
+ });
282
+ });
283
+ ws.addEventListener("close", () => {
284
+ // Drain any queued frame dispatches BEFORE settling, so a close that arrives
285
+ // right after the last frame doesn't cut off its handling (e.g. a final turn
286
+ // response). In one-shot mode (or after stop()) a close is a clean end;
287
+ // otherwise it is a transport loss that run() turns into a reconnect.
288
+ queue = queue.then(() => {
289
+ if (this.stopped || this.opts.reconnect === false)
290
+ done();
291
+ else
292
+ fail(new Error("connection closed"));
293
+ });
294
+ });
295
+ ws.addEventListener("error", () => { });
296
+ });
297
+ }
298
+ async dispatch(frame, send, onRegistered) {
299
+ switch (frame.t) {
300
+ case HELLO:
301
+ send({
302
+ t: REGISTER, agent_id: this.opts.agentId ?? "", token: this.opts.token ?? "",
303
+ agent_name: this.opts.name ?? "pyyol-agent", version: this.opts.version ?? "1.0.0",
304
+ games: this.opts.games ?? [], sdk_version: SDK_VERSION, sdk_language: "js",
305
+ });
306
+ break;
307
+ case REGISTERED:
308
+ this.registered = true;
309
+ this.refreshAttempts = 0; // a good register clears the refresh guard
310
+ this.maybeNudge(frame.latest_sdk);
311
+ this.feed("connected", `as ${frame.agent_id ?? this.opts.agentId ?? ""} · games=${(this.opts.games ?? []).join(",")}`);
312
+ onRegistered();
313
+ break;
314
+ case ERROR:
315
+ if (!this.registered) {
316
+ // Register rejected — almost always an expired access token. If we hold a
317
+ // refresh token, spend it and reconnect; only terminal when refresh fails.
318
+ if (await this.tryRefresh())
319
+ throw new RefreshRetry();
320
+ throw new ConnectorError(`register rejected: ${frame.error} (${frame.reason ?? ""})`);
321
+ }
322
+ // Mid-session gateway error — surface it, not terminal (mirrors Python).
323
+ this.feed("error", `${frame.error} (${frame.reason ?? ""})`);
324
+ break;
325
+ case PING:
326
+ send({ t: PONG, id: frame.id ?? "" });
327
+ break;
328
+ case PONG:
329
+ break;
330
+ case TURN: {
331
+ const view = frame.payload ?? {};
332
+ // Bracket the developer's handler in a Lens span; inside decideTurn the
333
+ // author can reach it via pyyol.currentSpan() to record model/tool calls.
334
+ const { status, body } = await this.tracer.runTurn({
335
+ matchId: view.match_id ?? "",
336
+ game: view.game ?? "",
337
+ round: Number(view.round ?? 0) || 0,
338
+ agentId: this.opts.agentId,
339
+ }, () => this.agent.decideTurn(view));
340
+ if (status === 200) {
341
+ send({ t: RESPONSE, id: frame.id ?? "", payload: body });
342
+ this.feed("turn", summarizeMove(frame.payload?.game ?? "", body));
343
+ }
344
+ else {
345
+ send({ t: RESPONSE, id: frame.id ?? "", error: body?.error ?? "handler_error" });
346
+ }
347
+ break;
348
+ }
349
+ case INITIALIZE: {
350
+ const ack = await this.agent.ackInitialize(frame.payload ?? {});
351
+ send({ t: RESPONSE, id: frame.id ?? "", payload: ack });
352
+ break;
353
+ }
354
+ case EVENT:
355
+ await this.agent.notifyEvent({
356
+ match_id: frame.match_id ?? "", game: frame.game ?? "",
357
+ seq: frame.seq ?? 0, type: frame.kind ?? "", payload: frame.payload,
358
+ });
359
+ this.feed("event", `${frame.kind ?? "event"}${frame.seq !== undefined ? ` seq=${frame.seq}` : ""}`);
360
+ break;
361
+ case GAME_END:
362
+ await this.agent.notifyGameEnd({
363
+ match_id: frame.match_id ?? "", game: frame.game ?? "", result: frame.payload,
364
+ });
365
+ this.feed("game_end", summarizeResult(frame.payload));
366
+ break;
367
+ default:
368
+ break;
369
+ }
370
+ }
371
+ }
372
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The Pyyol agent server.
3
+ *
4
+ * You register decision handlers; the SDK owns the wire protocol — routing,
5
+ * signature verification, replay protection, payload parsing, and response
6
+ * serialization. It contains no game strategy: your turn handler returns the move.
7
+ *
8
+ * import { Agent } from "pyyol";
9
+ *
10
+ * const agent = new Agent({ secret: process.env.PYYOL_SECRET });
11
+ * agent.onTurn("goofspiel", (v) => ({ round: v.round, card: Math.min(...v.legal_actions) }));
12
+ * agent.serve(9099);
13
+ *
14
+ * Routes served (base path comes from your manifest `endpoint.url`; the rest are
15
+ * its siblings): GET /health, POST /handshake, /initialize, <endpoint> (turn),
16
+ * /event, /game-end.
17
+ */
18
+ import { createServer } from "node:http";
19
+ import { EventNotification, GameEndNotification, InitializeRequest, Move, TurnView } from "./models.js";
20
+ import { Headers } from "./signing.js";
21
+ import { SDK_VERSION } from "./version.js";
22
+ export { SDK_VERSION };
23
+ export interface AgentOptions {
24
+ /** Manifest endpoint secret — the shared key the platform signs with. When set
25
+ * (recommended) every POST is signature-verified with replay protection. */
26
+ secret?: string;
27
+ supportedGames?: string[];
28
+ name?: string;
29
+ skewSeconds?: number;
30
+ /** Force verification on/off; defaults to on iff a secret is set. */
31
+ verify?: boolean;
32
+ }
33
+ export interface DispatchResult {
34
+ status: number;
35
+ body: Record<string, unknown>;
36
+ }
37
+ type TurnHandler = (view: TurnView) => Move | Promise<Move>;
38
+ export declare class Agent {
39
+ readonly secret: string;
40
+ readonly supportedGames: string[];
41
+ readonly name: string;
42
+ private skew;
43
+ private verify;
44
+ private replay;
45
+ private turnHandlers;
46
+ private defaultTurn?;
47
+ private initHandler?;
48
+ private eventHandler?;
49
+ private gameEndHandler?;
50
+ constructor(opts?: AgentOptions);
51
+ /** Register the per-turn decision handler. Pass a game to scope it, or omit for
52
+ * a catch-all used when no game-specific handler is set. */
53
+ onTurn(game: string | TurnHandler, handler?: TurnHandler): this;
54
+ onInitialize(fn: (r: InitializeRequest) => unknown): this;
55
+ onEvent(fn: (n: EventNotification) => unknown): this;
56
+ onGameEnd(fn: (n: GameEndNotification) => unknown): this;
57
+ /** Route one request and return `{ status, body }`. Framework-agnostic — call it
58
+ * from the built-in server or any web framework you already run. `path` must be
59
+ * the request path as received (no query). */
60
+ handle(method: string, path: string, headers: Headers, body: Buffer): Promise<DispatchResult>;
61
+ private handleTurn;
62
+ /** Run the turn handler for a raw view dict; returns `{status, body}`. */
63
+ decideTurn(view: Record<string, unknown>): Promise<DispatchResult>;
64
+ /** Run the initialize handler and return the ack object. */
65
+ ackInitialize(data: Record<string, unknown>): Promise<Record<string, unknown>>;
66
+ notifyEvent(data: Record<string, unknown>): Promise<void>;
67
+ notifyGameEnd(data: Record<string, unknown>): Promise<void>;
68
+ /** Connect to the platform and serve matches over an outbound WebSocket — the
69
+ * Beta local-runtime path (no inbound endpoint; works behind NAT). Blocks
70
+ * until stopped; reconnects automatically. */
71
+ run(opts: import("./runtime.js").RuntimeOptions): Promise<void>;
72
+ /** Run an HTTP server until the process exits. Zero dependencies. */
73
+ serve(port?: number, host?: string): ReturnType<typeof createServer>;
74
+ }
75
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/dist/server.js ADDED
@@ -0,0 +1,174 @@
1
+ /**
2
+ * The Pyyol agent server.
3
+ *
4
+ * You register decision handlers; the SDK owns the wire protocol — routing,
5
+ * signature verification, replay protection, payload parsing, and response
6
+ * serialization. It contains no game strategy: your turn handler returns the move.
7
+ *
8
+ * import { Agent } from "pyyol";
9
+ *
10
+ * const agent = new Agent({ secret: process.env.PYYOL_SECRET });
11
+ * agent.onTurn("goofspiel", (v) => ({ round: v.round, card: Math.min(...v.legal_actions) }));
12
+ * agent.serve(9099);
13
+ *
14
+ * Routes served (base path comes from your manifest `endpoint.url`; the rest are
15
+ * its siblings): GET /health, POST /handshake, /initialize, <endpoint> (turn),
16
+ * /event, /game-end.
17
+ */
18
+ import { createServer } from "node:http";
19
+ import { SUPPORTED_GAMES, parseView, } from "./models.js";
20
+ import { ReplayGuard, VerificationError, verifyRequest } from "./signing.js";
21
+ // Single source of truth: the generated version.ts (from package.json). Imported
22
+ // for local use below and re-exported so `import { SDK_VERSION } from "pyyol"` works.
23
+ import { SDK_VERSION } from "./version.js";
24
+ export { SDK_VERSION };
25
+ export class Agent {
26
+ secret;
27
+ supportedGames;
28
+ name;
29
+ skew;
30
+ verify;
31
+ replay = new ReplayGuard();
32
+ turnHandlers = new Map();
33
+ defaultTurn;
34
+ initHandler;
35
+ eventHandler;
36
+ gameEndHandler;
37
+ constructor(opts = {}) {
38
+ this.secret = opts.secret ?? "";
39
+ this.supportedGames = opts.supportedGames ?? [...SUPPORTED_GAMES];
40
+ this.name = opts.name ?? "pyyol-agent";
41
+ this.skew = opts.skewSeconds ?? 300;
42
+ this.verify = opts.verify ?? Boolean(this.secret);
43
+ }
44
+ /** Register the per-turn decision handler. Pass a game to scope it, or omit for
45
+ * a catch-all used when no game-specific handler is set. */
46
+ onTurn(game, handler) {
47
+ if (typeof game === "function")
48
+ this.defaultTurn = game;
49
+ else if (handler)
50
+ this.turnHandlers.set(game, handler);
51
+ return this;
52
+ }
53
+ onInitialize(fn) { this.initHandler = fn; return this; }
54
+ onEvent(fn) { this.eventHandler = fn; return this; }
55
+ onGameEnd(fn) { this.gameEndHandler = fn; return this; }
56
+ /** Route one request and return `{ status, body }`. Framework-agnostic — call it
57
+ * from the built-in server or any web framework you already run. `path` must be
58
+ * the request path as received (no query). */
59
+ async handle(method, path, headers, body) {
60
+ method = method.toUpperCase();
61
+ const suffix = path.replace(/\/+$/, "").split("/").pop() ?? "";
62
+ // Health is an unauthenticated liveness probe — never signature-checked.
63
+ if (method === "GET" && suffix === "health") {
64
+ return { status: 200, body: { status: "healthy", agent: this.name, version: SDK_VERSION } };
65
+ }
66
+ if (method !== "POST")
67
+ return { status: 405, body: { error: "method_not_allowed" } };
68
+ if (this.verify) {
69
+ try {
70
+ verifyRequest(this.secret, headers, method, path, body, { skewSeconds: this.skew, replayGuard: this.replay });
71
+ }
72
+ catch (e) {
73
+ const reason = e instanceof VerificationError ? e.reason : "bad_signature";
74
+ return { status: 401, body: { error: "unauthorized", reason } };
75
+ }
76
+ }
77
+ const data = loadJson(body);
78
+ if (suffix === "handshake") {
79
+ return { status: 200, body: { accepted: true, sdkVersion: SDK_VERSION, supportedGames: this.supportedGames } };
80
+ }
81
+ if (suffix === "initialize") {
82
+ const ack = this.initHandler ? await this.initHandler(data) : undefined;
83
+ if (ack && typeof ack === "object")
84
+ return { status: 200, body: ack };
85
+ return { status: 200, body: { ready: true, display_name: this.name } };
86
+ }
87
+ if (suffix === "event") {
88
+ if (this.eventHandler)
89
+ await this.eventHandler(data);
90
+ return { status: 200, body: { ok: true } };
91
+ }
92
+ if (suffix === "game-end") {
93
+ if (this.gameEndHandler)
94
+ await this.gameEndHandler(data);
95
+ return { status: 200, body: { ok: true } };
96
+ }
97
+ return this.handleTurn(data);
98
+ }
99
+ async handleTurn(data) {
100
+ const game = typeof data.game === "string" ? data.game : "";
101
+ const handler = this.turnHandlers.get(game) ?? this.defaultTurn;
102
+ if (!handler)
103
+ return { status: 501, body: { error: "no_turn_handler", game } };
104
+ try {
105
+ const move = await handler(parseView(data));
106
+ return { status: 200, body: move ?? {} };
107
+ }
108
+ catch {
109
+ return { status: 500, body: { error: "handler_error" } };
110
+ }
111
+ }
112
+ // --- shared handler invocation (used by both the HTTP path and the socket
113
+ // RuntimeConnector, so both transports run identical decision logic) ---
114
+ /** Run the turn handler for a raw view dict; returns `{status, body}`. */
115
+ decideTurn(view) {
116
+ return this.handleTurn(view);
117
+ }
118
+ /** Run the initialize handler and return the ack object. */
119
+ async ackInitialize(data) {
120
+ const ack = this.initHandler ? await this.initHandler(data) : undefined;
121
+ return ack && typeof ack === "object" ? ack : { ready: true, display_name: this.name };
122
+ }
123
+ async notifyEvent(data) {
124
+ if (this.eventHandler)
125
+ await this.eventHandler(data);
126
+ }
127
+ async notifyGameEnd(data) {
128
+ if (this.gameEndHandler)
129
+ await this.gameEndHandler(data);
130
+ }
131
+ /** Connect to the platform and serve matches over an outbound WebSocket — the
132
+ * Beta local-runtime path (no inbound endpoint; works behind NAT). Blocks
133
+ * until stopped; reconnects automatically. */
134
+ async run(opts) {
135
+ const { RuntimeConnector } = await import("./runtime.js");
136
+ await new RuntimeConnector(this, {
137
+ name: this.name,
138
+ games: this.supportedGames,
139
+ ...opts,
140
+ }).run();
141
+ }
142
+ /** Run an HTTP server until the process exits. Zero dependencies. */
143
+ serve(port = 9099, host = "127.0.0.1") {
144
+ const server = createServer((req, res) => {
145
+ const chunks = [];
146
+ req.on("data", (c) => chunks.push(c));
147
+ req.on("end", async () => {
148
+ const body = Buffer.concat(chunks);
149
+ const path = (req.url ?? "/").split("?")[0];
150
+ const { status, body: payload } = await this.handle(req.method ?? "GET", path, req.headers, body);
151
+ const out = Buffer.from(JSON.stringify(payload));
152
+ res.writeHead(status, { "Content-Type": "application/json", "Content-Length": String(out.length) });
153
+ res.end(out);
154
+ });
155
+ });
156
+ server.listen(port, host, () => {
157
+ // eslint-disable-next-line no-console
158
+ console.log(`pyyol agent "${this.name}" listening on http://${host}:${port} (verify=${this.verify})`);
159
+ });
160
+ return server;
161
+ }
162
+ }
163
+ function loadJson(body) {
164
+ if (!body || body.length === 0)
165
+ return {};
166
+ try {
167
+ const obj = JSON.parse(body.toString("utf8"));
168
+ return obj && typeof obj === "object" ? obj : {};
169
+ }
170
+ catch {
171
+ return {};
172
+ }
173
+ }
174
+ //# sourceMappingURL=server.js.map