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
package/dist/login.js ADDED
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The `pyyol login` browser flow (mirrors the Python SDK). Opens the dashboard's
3
+ * `/cli-login` page and captures the issued token on a loopback callback — the
4
+ * GitHub-CLI model. The dashboard owns the actual auth (GitHub / Google / wallet /
5
+ * email); the CLI is provider-agnostic and just captures the returned token.
6
+ */
7
+ import { spawn } from "node:child_process";
8
+ import { randomBytes, timingSafeEqual } from "node:crypto";
9
+ import { createServer } from "node:http";
10
+ /** Constant-time string compare (length-guarded so timingSafeEqual never throws). */
11
+ function safeEqual(a, b) {
12
+ const ab = Buffer.from(a);
13
+ const bb = Buffer.from(b);
14
+ return ab.length === bb.length && timingSafeEqual(ab, bb);
15
+ }
16
+ /** Derive the WSS connect URL from a platform API/base URL. */
17
+ export function deriveConnectUrl(apiUrl) {
18
+ if (!apiUrl)
19
+ return "";
20
+ try {
21
+ const u = new URL(apiUrl);
22
+ const scheme = u.protocol === "https:" || u.protocol === "wss:" ? "wss:" : "ws:";
23
+ return `${scheme}//${u.host}/v1/agent/connect`;
24
+ }
25
+ catch {
26
+ return "";
27
+ }
28
+ }
29
+ function openBrowser(url) {
30
+ // NEVER use shell:true here — the auth URL contains `&` (…&state=…&provider=…),
31
+ // which cmd.exe would treat as a command separator (arg/command injection via a
32
+ // hostile --dashboard). Pass the URL as a discrete, non-shell argument.
33
+ let cmd;
34
+ let args;
35
+ if (process.platform === "win32") {
36
+ cmd = "cmd";
37
+ args = ["/c", "start", "", url]; // empty title arg so a quoted URL isn't taken as the title
38
+ }
39
+ else if (process.platform === "darwin") {
40
+ cmd = "open";
41
+ args = [url];
42
+ }
43
+ else {
44
+ cmd = "xdg-open";
45
+ args = [url];
46
+ }
47
+ try {
48
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true, shell: false });
49
+ child.unref();
50
+ }
51
+ catch {
52
+ /* headless: the user can open the URL manually (printed by the caller) */
53
+ }
54
+ }
55
+ /** Run the loopback browser login and resolve with captured Credentials.
56
+ * `open` overrides how the auth URL is opened (tests inject a fake). */
57
+ export function runLoginFlow(opts) {
58
+ const state = randomBytes(16).toString("base64url");
59
+ const apiUrl = opts.apiUrl ?? "";
60
+ const opener = opts.open ?? openBrowser;
61
+ const timeoutMs = opts.timeoutMs ?? 180_000;
62
+ return new Promise((resolveP, reject) => {
63
+ const server = createServer((req, res) => {
64
+ const u = new URL(req.url ?? "/", "http://127.0.0.1");
65
+ if (u.pathname !== "/callback") {
66
+ res.writeHead(404).end();
67
+ return;
68
+ }
69
+ const token = u.searchParams.get("token") ?? "";
70
+ const ok = Boolean(token) && safeEqual(u.searchParams.get("state") ?? "", state);
71
+ res.writeHead(ok ? 200 : 400, { "Content-Type": "text/html; charset=utf-8" });
72
+ res.end(ok
73
+ ? "<!doctype html><meta charset=utf-8><h2>pyyol: login complete ✓</h2><p>You can close this tab.</p>"
74
+ : "<!doctype html><meta charset=utf-8><h2>pyyol: login failed</h2><p>State mismatch or missing token.</p>");
75
+ if (!ok)
76
+ return;
77
+ clearTimeout(timer);
78
+ server.close();
79
+ resolveP({
80
+ url: apiUrl,
81
+ connectUrl: u.searchParams.get("connect_url") || deriveConnectUrl(apiUrl),
82
+ agentId: u.searchParams.get("agent_id") ?? "",
83
+ accessToken: token,
84
+ refreshToken: u.searchParams.get("refresh_token") ?? "",
85
+ // Optional: the dashboard may hand back a long-lived agent key directly.
86
+ // If it doesn't, `pyyol login` mints one post-auth.
87
+ apiKey: u.searchParams.get("api_key") ?? "",
88
+ authUrl: "",
89
+ });
90
+ });
91
+ const timer = setTimeout(() => {
92
+ server.close();
93
+ reject(new Error("login timed out or was cancelled"));
94
+ }, timeoutMs);
95
+ server.listen(0, "127.0.0.1", () => {
96
+ const addr = server.address();
97
+ const port = typeof addr === "object" && addr ? addr.port : 0;
98
+ const callback = `http://127.0.0.1:${port}/callback`;
99
+ let authUrl = `${opts.dashboardUrl.replace(/\/$/, "")}/cli-login` +
100
+ `?callback=${encodeURIComponent(callback)}&state=${state}`;
101
+ if (opts.provider)
102
+ authUrl += `&provider=${encodeURIComponent(opts.provider)}`;
103
+ opener(authUrl);
104
+ });
105
+ });
106
+ }
107
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAGzC,qFAAqF;AACrF,SAAS,SAAS,CAAC,CAAS,EAAE,CAAS;IACrC,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,OAAO,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM,IAAI,eAAe,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1B,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;QACjF,OAAO,GAAG,MAAM,KAAK,CAAC,CAAC,IAAI,mBAAmB,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,gFAAgF;IAChF,gFAAgF;IAChF,wEAAwE;IACxE,IAAI,GAAW,CAAC;IAChB,IAAI,IAAc,CAAC;IACnB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,GAAG,GAAG,KAAK,CAAC;QACZ,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,2DAA2D;IAC9F,CAAC;SAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACzC,GAAG,GAAG,MAAM,CAAC;QACb,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,UAAU,CAAC;QACjB,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAClF,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;IAC5E,CAAC;AACH,CAAC;AAMD;yEACyE;AACzE,MAAM,UAAU,YAAY,CAAC,IAM5B;IACC,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,IAAI,WAAW,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC;IAE5C,OAAO,IAAI,OAAO,CAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;QACnD,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACvC,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;YACtD,IAAI,CAAC,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;gBAC/B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;gBACzB,OAAO;YACT,CAAC;YACD,MAAM,KAAK,GAAG,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAChD,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;YACjF,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;YAC9E,GAAG,CAAC,GAAG,CACL,EAAE;gBACA,CAAC,CAAC,mGAAmG;gBACrG,CAAC,CAAC,wGAAwG,CAC7G,CAAC;YACF,IAAI,CAAC,EAAE;gBAAE,OAAO;YAChB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,QAAQ,CAAC;gBACP,GAAG,EAAE,MAAM;gBACX,UAAU,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,gBAAgB,CAAC,MAAM,CAAC;gBACzE,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE;gBAC7C,WAAW,EAAE,KAAK;gBAClB,YAAY,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE;gBACvD,yEAAyE;gBACzE,oDAAoD;gBACpD,MAAM,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE;gBAC3C,OAAO,EAAE,EAAE;aACZ,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC,CAAC;QACxD,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YACjC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9D,MAAM,QAAQ,GAAG,oBAAoB,IAAI,WAAW,CAAC;YACrD,IAAI,OAAO,GACT,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY;gBACnD,aAAa,kBAAkB,CAAC,QAAQ,CAAC,UAAU,KAAK,EAAE,CAAC;YAC7D,IAAI,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,aAAa,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/E,MAAM,CAAC,OAAO,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
package/dist/mode.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ export declare const SANDBOX = "sandbox";
2
+ export declare const RANKED = "ranked";
3
+ export declare function resolveMode(opts: {
4
+ rankedFlag?: boolean;
5
+ cfgMode?: string;
6
+ devLocked?: boolean;
7
+ }): string;
8
+ export declare function banner(mode: string, color?: boolean): string;
9
+ /** One-time confirmation before real-stakes play. In CI/non-TTY, only proceeds when
10
+ * `assumeYes` is set — never silently enters ranked. */
11
+ export declare function confirmRanked(assumeYes?: boolean): Promise<boolean>;
12
+ //# sourceMappingURL=mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.d.ts","sourceRoot":"","sources":["../src/mode.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,OAAO,YAAY,CAAC;AACjC,eAAO,MAAM,MAAM,WAAW,CAAC;AAW/B,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAChC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,MAAM,CAOT;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAQ5D;AAED;yDACyD;AACzD,wBAAsB,aAAa,CAAC,SAAS,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAYvE"}
package/dist/mode.js ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Money-safety mode model (mirrors the Python SDK): `sandbox` is the safe default;
3
+ * `ranked` (real stakes) needs an explicit opt-in. `pyyol dev` is hard-locked to
4
+ * sandbox; only `pyyol play <arena> --ranked` can reach ranked. Precedence for
5
+ * `play`: --ranked flag → PYYOL_MODE env → pyyol.toml → default sandbox.
6
+ */
7
+ import { createInterface } from "node:readline";
8
+ export const SANDBOX = "sandbox";
9
+ export const RANKED = "ranked";
10
+ const GREEN = "\x1b[32m";
11
+ const RED = "\x1b[1;31m";
12
+ const RESET = "\x1b[0m";
13
+ function useColor() {
14
+ if (process.env.NO_COLOR)
15
+ return false;
16
+ return Boolean(process.stdout.isTTY);
17
+ }
18
+ export function resolveMode(opts) {
19
+ if (opts.devLocked)
20
+ return SANDBOX; // development never risks money
21
+ if (opts.rankedFlag)
22
+ return RANKED;
23
+ const env = (process.env.PYYOL_MODE ?? "").trim().toLowerCase();
24
+ if (env === SANDBOX || env === RANKED)
25
+ return env;
26
+ if (opts.cfgMode === SANDBOX || opts.cfgMode === RANKED)
27
+ return opts.cfgMode;
28
+ return SANDBOX;
29
+ }
30
+ export function banner(mode, color) {
31
+ const use = color === undefined ? useColor() : color;
32
+ if (mode === RANKED) {
33
+ const text = "⚠ RANKED — real stakes (escrow · Elo · P-Index)";
34
+ return use ? `${RED}${text}${RESET}` : text;
35
+ }
36
+ const text = "● SANDBOX — practice, no stakes";
37
+ return use ? `${GREEN}${text}${RESET}` : text;
38
+ }
39
+ /** One-time confirmation before real-stakes play. In CI/non-TTY, only proceeds when
40
+ * `assumeYes` is set — never silently enters ranked. */
41
+ export async function confirmRanked(assumeYes = false) {
42
+ if (assumeYes)
43
+ return true;
44
+ if (!process.stdin.isTTY)
45
+ return false;
46
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
47
+ try {
48
+ const answer = await new Promise((res) => rl.question("This plays with REAL stakes. Continue? [y/N] ", res));
49
+ return ["y", "yes"].includes(answer.trim().toLowerCase());
50
+ }
51
+ finally {
52
+ rl.close();
53
+ }
54
+ }
55
+ //# sourceMappingURL=mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.js","sourceRoot":"","sources":["../src/mode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,CAAC,MAAM,OAAO,GAAG,SAAS,CAAC;AACjC,MAAM,CAAC,MAAM,MAAM,GAAG,QAAQ,CAAC;AAE/B,MAAM,KAAK,GAAG,UAAU,CAAC;AACzB,MAAM,GAAG,GAAG,YAAY,CAAC;AACzB,MAAM,KAAK,GAAG,SAAS,CAAC;AAExB,SAAS,QAAQ;IACf,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IACvC,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAI3B;IACC,IAAI,IAAI,CAAC,SAAS;QAAE,OAAO,OAAO,CAAC,CAAC,gCAAgC;IACpE,IAAI,IAAI,CAAC,UAAU;QAAE,OAAO,MAAM,CAAC;IACnC,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAChE,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,GAAG,CAAC;IAClD,IAAI,IAAI,CAAC,OAAO,KAAK,OAAO,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC;IAC7E,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,KAAe;IAClD,MAAM,GAAG,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IACrD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,kDAAkD,CAAC;QAChE,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9C,CAAC;IACD,MAAM,IAAI,GAAG,kCAAkC,CAAC;IAChD,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AAED;yDACyD;AACzD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAS,GAAG,KAAK;IACnD,IAAI,SAAS;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACvC,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,IAAI,CAAC;QACH,MAAM,MAAM,GAAW,MAAM,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAC/C,EAAE,CAAC,QAAQ,CAAC,+CAA+C,EAAE,GAAG,CAAC,CAClE,CAAC;QACF,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Typed models for the Pyyol push protocol.
3
+ *
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
6
+ * object so the SDK stays thin and never drifts from the server's evolving state
7
+ * shape. Nothing here contains game strategy — these are pure data shapes.
8
+ */
9
+ export declare const PROTOCOL_VERSION = "1.0";
10
+ export declare const GOOFSPIEL = "goofspiel";
11
+ export declare const MONOPOLY = "monopoly";
12
+ export declare const MAFIA = "mafia";
13
+ export declare const SUPPORTED_GAMES: readonly ["goofspiel", "monopoly", "mafia"];
14
+ export interface InitializeRequest {
15
+ protocol: string;
16
+ match_id: string;
17
+ game: string;
18
+ seat: number;
19
+ players: number;
20
+ role?: string;
21
+ config?: unknown;
22
+ deadline_ms?: number;
23
+ }
24
+ export interface EventNotification {
25
+ protocol: string;
26
+ match_id: string;
27
+ game: string;
28
+ seq: number;
29
+ type: string;
30
+ payload?: unknown;
31
+ }
32
+ export interface GameEndNotification {
33
+ protocol: string;
34
+ match_id: string;
35
+ game: string;
36
+ result?: unknown;
37
+ }
38
+ /** One resolved round from this seat's perspective (both cards open post-resolution). */
39
+ export interface GoofspielRound {
40
+ round: number;
41
+ prize: number;
42
+ prize_pool: number;
43
+ your_card: number;
44
+ opp_card: number;
45
+ winner: number;
46
+ scores: number[];
47
+ }
48
+ export interface GoofspielView {
49
+ game: "goofspiel";
50
+ match_id: string;
51
+ seat: number;
52
+ round: number;
53
+ current_prize: number;
54
+ prize_pool: number;
55
+ your_hand: number[];
56
+ scores: number[];
57
+ legal_actions: number[];
58
+ /** Every already-resolved round — the view is self-contained/replayable. */
59
+ history: GoofspielRound[];
60
+ raw: Record<string, unknown>;
61
+ }
62
+ export interface MonopolyView {
63
+ game: "monopoly";
64
+ match_id: string;
65
+ seat: number;
66
+ phase: string;
67
+ legal_actions: string[];
68
+ /** The raw board dict (players, holdings, phase, …) — inspect directly. */
69
+ state: Record<string, unknown>;
70
+ raw: Record<string, unknown>;
71
+ }
72
+ export interface MafiaView {
73
+ game: "mafia";
74
+ match_id: string;
75
+ your_seat: number;
76
+ your_role: string;
77
+ day: number;
78
+ phase: string;
79
+ alive: Record<number, boolean>;
80
+ allies: number[];
81
+ legal: string[];
82
+ public: Record<string, unknown>[];
83
+ private: Record<string, unknown>[];
84
+ raw: Record<string, unknown>;
85
+ }
86
+ export type TurnView = GoofspielView | MonopolyView | MafiaView | Record<string, unknown>;
87
+ export interface GoofspielMove {
88
+ round?: number;
89
+ card: number;
90
+ }
91
+ export interface MonopolyMove {
92
+ action: string;
93
+ property?: number;
94
+ amount?: number;
95
+ }
96
+ export interface MafiaMove {
97
+ action: string;
98
+ target?: number;
99
+ tone?: string;
100
+ text?: string;
101
+ }
102
+ export type Move = GoofspielMove | MonopolyMove | MafiaMove | Record<string, unknown>;
103
+ /** Parse a turn body into its typed view; unknown games return the raw object. */
104
+ export declare function parseView(d: Record<string, any>): TurnView;
105
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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"}
package/dist/models.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Typed models for the Pyyol push protocol.
3
+ *
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
6
+ * object so the SDK stays thin and never drifts from the server's evolving state
7
+ * shape. Nothing here contains game strategy — these are pure data shapes.
8
+ */
9
+ export const PROTOCOL_VERSION = "1.0";
10
+ export const GOOFSPIEL = "goofspiel";
11
+ export const MONOPOLY = "monopoly";
12
+ export const MAFIA = "mafia";
13
+ export const SUPPORTED_GAMES = [GOOFSPIEL, MONOPOLY, MAFIA];
14
+ const asNum = (v, d = 0) => (typeof v === "number" ? v : Number(v ?? d) || d);
15
+ const asStr = (v, d = "") => (typeof v === "string" ? v : d);
16
+ const asArr = (v) => (Array.isArray(v) ? v : []);
17
+ /** Parse a turn body into its typed view; unknown games return the raw object. */
18
+ export function parseView(d) {
19
+ switch (d.game) {
20
+ case GOOFSPIEL:
21
+ return {
22
+ game: GOOFSPIEL,
23
+ match_id: asStr(d.match_id),
24
+ seat: asNum(d.seat),
25
+ round: asNum(d.round),
26
+ current_prize: asNum(d.current_prize),
27
+ prize_pool: asNum(d.prize_pool),
28
+ your_hand: asArr(d.your_hand),
29
+ scores: asArr(d.scores),
30
+ legal_actions: asArr(d.legal_actions).length ? asArr(d.legal_actions) : asArr(d.your_hand),
31
+ history: asArr(d.history),
32
+ raw: d,
33
+ };
34
+ case MONOPOLY:
35
+ return {
36
+ game: MONOPOLY,
37
+ match_id: asStr(d.match_id),
38
+ seat: asNum(d.seat),
39
+ phase: asStr(d.phase),
40
+ legal_actions: asArr(d.legal_actions),
41
+ state: d.state ?? {},
42
+ raw: d,
43
+ };
44
+ case MAFIA: {
45
+ const aliveRaw = d.alive ?? {};
46
+ const alive = {};
47
+ for (const k of Object.keys(aliveRaw))
48
+ alive[Number(k)] = Boolean(aliveRaw[k]);
49
+ return {
50
+ game: MAFIA,
51
+ match_id: asStr(d.match_id),
52
+ your_seat: asNum(d.your_seat ?? d.seat),
53
+ your_role: asStr(d.your_role),
54
+ day: asNum(d.day),
55
+ phase: asStr(d.phase),
56
+ alive,
57
+ allies: asArr(d.allies),
58
+ legal: asArr(d.legal).length ? asArr(d.legal) : asArr(d.legal_actions),
59
+ public: asArr(d.public),
60
+ private: asArr(d.private),
61
+ raw: d,
62
+ };
63
+ }
64
+ default:
65
+ return d;
66
+ }
67
+ }
68
+ //# sourceMappingURL=models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** Full Markdown rules for all three games, or just one game's section.
2
+ * @param game optional "goofspiel" | "monopoly" | "mafia" to slice one section. */
3
+ export declare function gameRules(game?: string): string;
4
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AASA;oFACoF;AACpF,wBAAgB,SAAS,CAAC,IAAI,SAAK,GAAG,MAAM,CAY3C"}
package/dist/rules.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Programmatic access to the engine-generated game rules bundled with this package
3
+ * (rules/games.md — the same reference an LLM/agent author needs). Ships inside the
4
+ * npm tarball via the package.json `files` allowlist, so it is always available
5
+ * offline and can never drift from the deployed engine (CI drift-gates it).
6
+ */
7
+ import { readFileSync } from "node:fs";
8
+ import { fileURLToPath } from "node:url";
9
+ /** Full Markdown rules for all three games, or just one game's section.
10
+ * @param game optional "goofspiel" | "monopoly" | "mafia" to slice one section. */
11
+ export function gameRules(game = "") {
12
+ // dist/rules.js → ../rules/games.md (package root). Resolved relative to this
13
+ // module so it works regardless of the consumer's CWD.
14
+ const path = fileURLToPath(new URL("../rules/games.md", import.meta.url));
15
+ const text = readFileSync(path, "utf8");
16
+ if (!game)
17
+ return text;
18
+ const cap = game.charAt(0).toUpperCase() + game.slice(1).toLowerCase();
19
+ const marker = `## ${cap}`;
20
+ const start = text.indexOf(marker);
21
+ if (start === -1)
22
+ return text;
23
+ const next = text.indexOf("\n## ", start + marker.length);
24
+ return next === -1 ? text.slice(start) : text.slice(start, next);
25
+ }
26
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;oFACoF;AACpF,MAAM,UAAU,SAAS,CAAC,IAAI,GAAG,EAAE;IACjC,8EAA8E;IAC9E,uDAAuD;IACvD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1E,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACvE,MAAM,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1D,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * The Pyyol Runtime Connector — the local-runtime transport for JS/TS.
3
+ *
4
+ * Your agent runs on your own machine and dials OUT over a single persistent
5
+ * WebSocket to the platform. The platform pushes match lifecycle down that socket
6
+ * and reads decisions back over it, so a laptop behind NAT works with zero
7
+ * networking config — you never host an inbound endpoint. This is the Beta model.
8
+ *
9
+ * The connector owns registration, heartbeats, automatic reconnection with
10
+ * exponential backoff, and request/response correlation; it dispatches frames to
11
+ * the handlers you registered on your `Agent`. No game logic lives here.
12
+ *
13
+ * const agent = new Agent({ supportedGames: ["goofspiel"], name: "OlympAI" });
14
+ * agent.onTurn("goofspiel", (v) => ({ round: v.round, card: Math.max(...v.legal_actions) }));
15
+ * await agent.run({ url: "wss://pyyol.example/v1/agent/connect", agentId: "ag_…", token: "…" });
16
+ */
17
+ import type { Agent } from "./server.js";
18
+ export declare const PROTOCOL_VERSION = "1.0";
19
+ /** A minimal structural type for a WHATWG WebSocket (Node 22+ global, or `ws`). */
20
+ export interface WebSocketLike {
21
+ send(data: string): void;
22
+ close(code?: number, reason?: string): void;
23
+ addEventListener(type: string, listener: (ev: any) => void): void;
24
+ readyState: number;
25
+ }
26
+ export type WebSocketCtor = new (url: string) => WebSocketLike;
27
+ export interface RuntimeOptions {
28
+ url: string;
29
+ agentId?: string;
30
+ token?: string;
31
+ name?: string;
32
+ games?: string[];
33
+ version?: string;
34
+ heartbeatMs?: number;
35
+ reconnect?: boolean;
36
+ maxBackoffMs?: number;
37
+ /** Bound the opening WS handshake (ms); dead-air links hang without it. Default 10s. */
38
+ connectTimeoutMs?: number;
39
+ /** Liveness watchdog: if NO frame (of any type) arrives within this window (ms),
40
+ * treat the link as dead and close it. Catches a half-open (silently dropped) TCP
41
+ * link that app-level PINGs alone can miss — the platform sets protocol-level
42
+ * ping_timeout for the same reason. Closing makes session() reject → run()
43
+ * reconnects. Defaults to max(30s, 3× heartbeat). */
44
+ livenessTimeoutMs?: number;
45
+ /** Inject a WebSocket implementation (defaults to globalThis.WebSocket). */
46
+ WebSocketImpl?: WebSocketCtor;
47
+ /** Called (once per process) with a one-line notice when the gateway reports a
48
+ * newer SDK is available. Defaults to console.warn. */
49
+ onNotice?: (message: string) => void;
50
+ /** Optional live feed of match lifecycle for a CLI/console. kind is one of
51
+ * "connected" | "turn" | "event" | "game_end" | "reauth". No-op if unset. */
52
+ onFeed?: (kind: string, detail: string) => void;
53
+ /** Rotating refresh token. When set (with apiUrl), a rejected register spends it
54
+ * for a fresh access token and reconnects — so long-running agents stay authed
55
+ * past the short access-token TTL. */
56
+ refreshToken?: string;
57
+ /** API base URL for POST /v1/auth/refresh (e.g. https://api.pyyol.com). */
58
+ apiUrl?: string;
59
+ /** Persist a rotated (access, refresh) pair, e.g. back to the credentials file. */
60
+ onTokens?: (access: string, refresh: string) => void | Promise<void>;
61
+ /** Inject the refresh HTTP call (tests). Defaults to a fetch of /v1/auth/refresh. */
62
+ refreshHttp?: (apiUrl: string, refreshToken: string) => Promise<{
63
+ access: string;
64
+ refresh: string;
65
+ } | null>;
66
+ }
67
+ /** Terminal connector failure (e.g. auth rejected) — not retried. */
68
+ export declare class ConnectorError extends Error {
69
+ }
70
+ export declare class RuntimeConnector {
71
+ private agent;
72
+ private opts;
73
+ private stopped;
74
+ private nudged;
75
+ private registered;
76
+ private refreshAttempts;
77
+ private readonly tracer;
78
+ constructor(agent: Agent, opts: RuntimeOptions);
79
+ /** The gateway echoes the newest published version on the registered frame.
80
+ * Print a one-line upgrade hint at most once (not on every reconnect). */
81
+ private maybeNudge;
82
+ /** Connect and serve until stopped, reconnecting with exponential backoff. */
83
+ run(): Promise<void>;
84
+ stop(): void;
85
+ /** Emit a live-feed line to the CLI/console, if a sink was provided. */
86
+ private feed;
87
+ /** Warn (once) when connecting over cleartext ws:// to a non-local host — the
88
+ * register token is sent in the clear (mirrors the Python connector). */
89
+ private securityCheck;
90
+ /** One connection lifetime: resolves on a clean stop, rejects on transport loss. */
91
+ /** Spend the refresh token for a fresh access token; returns true on success
92
+ * (opts.token rotated + persisted). Bounded per connection to avoid a loop. */
93
+ private tryRefresh;
94
+ private session;
95
+ private dispatch;
96
+ }
97
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AASzC,eAAO,MAAM,gBAAgB,QAAQ,CAAC;AAEtC,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC;IAClE,UAAU,EAAE,MAAM,CAAC;CACpB;AACD,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,EAAE,MAAM,KAAK,aAAa,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;0DAIsD;IACtD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4EAA4E;IAC5E,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;4DACwD;IACxD,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC;kFAC8E;IAC9E,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD;;2CAEuC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,qFAAqF;IACrF,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CAC7G;AAkDD,qEAAqE;AACrE,qBAAa,cAAe,SAAQ,KAAK;CAAG;AA6B5C,qBAAa,gBAAgB;IASf,OAAO,CAAC,KAAK;IAAS,OAAO,CAAC,IAAI;IAR9C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAK;IAI5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBACZ,KAAK,EAAE,KAAK,EAAU,IAAI,EAAE,cAAc;IAI9D;+EAC2E;IAC3E,OAAO,CAAC,UAAU;IAWlB,8EAA8E;IACxE,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B1B,IAAI,IAAI,IAAI;IAKZ,wEAAwE;IACxE,OAAO,CAAC,IAAI;IAIZ;8EAC0E;IAC1E,OAAO,CAAC,aAAa;IAkBrB,oFAAoF;IACpF;oFACgF;YAClE,UAAU;IAwBxB,OAAO,CAAC,OAAO;YA+ED,QAAQ;CA0EvB"}