@chipzen-ai/bot 0.3.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/CHANGELOG.md +220 -0
- package/README.md +96 -0
- package/dist/bin.js +2545 -0
- package/dist/bin.js.map +1 -0
- package/dist/index.cjs +2310 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +746 -0
- package/dist/index.d.ts +746 -0
- package/dist/index.js +2244 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `@chipzen-ai/bot` JavaScript / TypeScript
|
|
4
|
+
SDK will be documented in this file.
|
|
5
|
+
|
|
6
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
### Security
|
|
12
|
+
|
|
13
|
+
- **External-API: refuse a cross-origin or `wss`→`ws` gateway URL.**
|
|
14
|
+
`runExternalBot()` now rejects a server-supplied absolute `gateway_ws_url`
|
|
15
|
+
whose origin differs from the lobby's, or that downgrades to cleartext
|
|
16
|
+
`ws://` — so the bot token can never be sent to a different host or
|
|
17
|
+
unencrypted; the offending match is skipped. A relative URL is always
|
|
18
|
+
re-anchored to the lobby origin.
|
|
19
|
+
([#58](https://github.com/chipzen-ai/chipzen-sdk/issues/58))
|
|
20
|
+
|
|
21
|
+
## [0.3.0] — 2026-06-13
|
|
22
|
+
|
|
23
|
+
Reaches feature parity with the Python SDK's external-API remote-play
|
|
24
|
+
path (chipzen-ai/chipzen-sdk#56), and ships the just-merged reconnect
|
|
25
|
+
fix from the Python side.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **External-API remote-play surface.** `runExternalBot()` connects a bot
|
|
30
|
+
to the platform over the public token-authed external-API path — lobby
|
|
31
|
+
(`/ws/external/bot/{botId}`) → `matched` → per-match gateway WS
|
|
32
|
+
(`/ws/external/match/{mid}/{pid}`, token in the `Sec-WebSocket-Protocol`
|
|
33
|
+
header) — and plays every match dispatched to it (a single challenge, or
|
|
34
|
+
each round of a tournament) on one persistent lobby connection. The match
|
|
35
|
+
data plane reuses the same `Bot.decide(GameState) -> Action` loop
|
|
36
|
+
(`_runSession`) as the containerized `runBot()` path, so one bot class
|
|
37
|
+
works on both. ([#56](https://github.com/chipzen-ai/chipzen-sdk/issues/56))
|
|
38
|
+
- `connectToChipzen(botId, env)` — env-aware lobby-URL helper
|
|
39
|
+
(`prod` / `staging` / `local`, honoring `$CHIPZEN_ENV`), returning a
|
|
40
|
+
`ConnectionConfig`.
|
|
41
|
+
- `chipzen.toml` config-file convention: drop your `cz_extbot_` token (and
|
|
42
|
+
optional `url` / `bot_id`) into `[external_api]` once; discovered from
|
|
43
|
+
cwd → `~/.chipzen/` → `/etc/chipzen/`. Explicit kwargs always win. Parsed
|
|
44
|
+
with a minimal inline reader so the package keeps its single runtime
|
|
45
|
+
dependency (`ws`) — no TOML library added.
|
|
46
|
+
- `chipzen-sdk run-external <bot.js>` CLI: loads config, resolves the env
|
|
47
|
+
URL, finds your exported `Bot` subclass, and runs it. Flags mirror the
|
|
48
|
+
Python CLI: `--env`, `--token`, `--bot-id`, `--bot-class`,
|
|
49
|
+
`--max-matches`, `--no-safe-mode`.
|
|
50
|
+
- `RetryPolicy` reconnect/backoff knobs (`maxReconnectAttempts`,
|
|
51
|
+
`initialBackoffMs`, `maxBackoffMs`, `backoffMultiplier`) + the shared
|
|
52
|
+
`DEFAULT_RETRY_POLICY`, accepted by both `runBot()` and
|
|
53
|
+
`runExternalBot()`. Default: 5 attempts, 500 ms initial backoff doubling
|
|
54
|
+
to a 30 s cap.
|
|
55
|
+
- `Bot.onDecisionLatency(latencyMs)` hook — called after each `turn_action`
|
|
56
|
+
is sent, with the wall-clock time your `decide()` took. Default no-op.
|
|
57
|
+
([#46](https://github.com/chipzen-ai/chipzen-sdk/issues/46))
|
|
58
|
+
- `safeMode` option on `runBot()` / `runExternalBot()` (default `true`,
|
|
59
|
+
preserving the existing fold-on-error behavior). Set `false` for
|
|
60
|
+
dev/eval so an exception in `decide()` raises `BotDecisionError` and
|
|
61
|
+
exits non-zero instead of being silently folded. `BotDecisionError` is
|
|
62
|
+
now exported. ([#52](https://github.com/chipzen-ai/chipzen-sdk/issues/52))
|
|
63
|
+
- A non-default `User-Agent` (`chipzen-sdk-js/<version>`) is now sent on
|
|
64
|
+
the WebSocket handshake (defense-in-depth against the platform's
|
|
65
|
+
Cloudflare bot-fight rule). Override with `userAgent`.
|
|
66
|
+
([#46](https://github.com/chipzen-ai/chipzen-sdk/issues/46))
|
|
67
|
+
- `VERSION` export — the SDK version string, sourced from `package.json`.
|
|
68
|
+
|
|
69
|
+
### Changed
|
|
70
|
+
|
|
71
|
+
- `runBot()` now returns the `match_end` payload
|
|
72
|
+
(`Record<string, unknown> | null`) instead of `void`.
|
|
73
|
+
Backward-compatible — callers that ignore the return value are
|
|
74
|
+
unaffected. (`_runSession` likewise returns the payload so the lobby
|
|
75
|
+
loop can distinguish a clean end from a drop.)
|
|
76
|
+
- `runBot()`'s default reconnect cap is now 5 attempts (from the default
|
|
77
|
+
`RetryPolicy`) with the policy's exponential backoff, instead of the
|
|
78
|
+
previous hardcoded `maxRetries=3` with `min(2**n, 8)s`. Pass
|
|
79
|
+
`maxRetries` or a `retryPolicy` to override.
|
|
80
|
+
|
|
81
|
+
### Fixed
|
|
82
|
+
|
|
83
|
+
- The default `clientVersion` sent in the `hello` handshake now tracks the
|
|
84
|
+
installed package version instead of the hardcoded `"0.2.0"` literal
|
|
85
|
+
that would drift on a release bump.
|
|
86
|
+
([#41](https://github.com/chipzen-ai/chipzen-sdk/issues/41))
|
|
87
|
+
- **External-API: a mid-match gateway disconnect no longer silently
|
|
88
|
+
forfeits the match.** `runExternalBot()` reconnects a dropped per-match
|
|
89
|
+
gateway socket (bounded by the `RetryPolicy`) and resumes via the
|
|
90
|
+
platform's reconnect-resume — `_runSession` consumes the server
|
|
91
|
+
`reconnected` frame and replays the pending turn, and the bot instance
|
|
92
|
+
keeps its state across the gap. The reconnect budget is bounded, so an
|
|
93
|
+
unrecoverable match is abandoned (`end: null`) rather than hanging.
|
|
94
|
+
- **External-API: in-flight matches survive a lobby reconnect, and no
|
|
95
|
+
match task is orphaned on teardown.** Match-task ownership is hoisted to
|
|
96
|
+
the top-level `runExternalBot` (not the per-lobby-session), so a lobby
|
|
97
|
+
blip no longer abandons a match playing on its own gateway socket; on
|
|
98
|
+
teardown, still-running matches get a short grace window then are
|
|
99
|
+
drained and awaited.
|
|
100
|
+
|
|
101
|
+
### Added (already shipped pre-0.3.0, in conformance)
|
|
102
|
+
|
|
103
|
+
- Three new conformance scenarios in `validate --check-connectivity`,
|
|
104
|
+
bringing the total from 1 to 4. The previously-shipped scenario only
|
|
105
|
+
covered a clean handshake + 1 hand + match_end; bots could pass it
|
|
106
|
+
and still crash in production. The new scenarios are:
|
|
107
|
+
- `multi_turn_request_id_echo` — drives 3 `turn_request`s across
|
|
108
|
+
preflop/flop/turn and verifies the SDK echoes each `request_id`
|
|
109
|
+
correctly (the previous harness only checked the first action).
|
|
110
|
+
- `action_rejected_recovery` — verifies the SDK retries with a
|
|
111
|
+
safe-fallback `check`/`fold` and the original `request_id` when the
|
|
112
|
+
server sends `action_rejected` (a routine production code path
|
|
113
|
+
that had no harness coverage).
|
|
114
|
+
- `retry_storm_bounded` — verifies the SDK responds reactively to 3
|
|
115
|
+
back-to-back `action_rejected` messages without hanging or entering
|
|
116
|
+
an unbounded send loop.
|
|
117
|
+
- Closes part of
|
|
118
|
+
[#28](https://github.com/chipzen-ai/chipzen-sdk/issues/28) for the
|
|
119
|
+
JavaScript SDK.
|
|
120
|
+
- Public `SCENARIOS` export from `conformance.ts` listing each
|
|
121
|
+
scenario name and runner function. Lets tests and downstream tooling
|
|
122
|
+
enumerate the registered scenarios without parsing CLI output.
|
|
123
|
+
|
|
124
|
+
### Documentation
|
|
125
|
+
|
|
126
|
+
- `chipzen-sdk validate --help` now enumerates all 4 conformance
|
|
127
|
+
scenarios and notes that the validator is a courtesy linter — the
|
|
128
|
+
authoritative gate is server-side seccomp + cap-drop. Closes part of
|
|
129
|
+
[#28](https://github.com/chipzen-ai/chipzen-sdk/issues/28).
|
|
130
|
+
- Documented a known limitation in `runConformanceChecks`: the
|
|
131
|
+
JavaScript harness does not yet include a hard wall-clock watchdog
|
|
132
|
+
against bots that synchronously block the event loop (busy-loop,
|
|
133
|
+
`Atomics.wait`). The Python SDK has a daemon-thread watchdog; the
|
|
134
|
+
JS equivalent (a Worker) is heavier-weight and deferred.
|
|
135
|
+
|
|
136
|
+
## [0.2.0] — Initial public release
|
|
137
|
+
|
|
138
|
+
First release of `@chipzen-ai/bot` to npm. Mirrors the Python SDK's
|
|
139
|
+
shape (see [`packages/python/CHANGELOG.md`](../python/CHANGELOG.md))
|
|
140
|
+
so a developer using either language sees the same command surface
|
|
141
|
+
and protocol behavior.
|
|
142
|
+
|
|
143
|
+
### Scope
|
|
144
|
+
|
|
145
|
+
The published SDK is intentionally narrow:
|
|
146
|
+
|
|
147
|
+
1. A **protocol adapter** (`Bot` base class plus the WebSocket client)
|
|
148
|
+
so your bot doesn't hand-roll the wire protocol.
|
|
149
|
+
2. A **`chipzen-sdk validate`** CLI that runs the same pre-upload
|
|
150
|
+
checks the platform performs (size, imports, sandbox-blocked
|
|
151
|
+
modules, `decide()` timeout sniff, optional protocol-conformance
|
|
152
|
+
harness via `--check-connectivity`).
|
|
153
|
+
3. An **IP-protected Dockerfile recipe** at
|
|
154
|
+
[`starters/javascript/`](starters/javascript/) — multi-stage Bun
|
|
155
|
+
build (`bun build --compile`) that ships a single statically-
|
|
156
|
+
linked binary, not your `.js` source. See
|
|
157
|
+
[`IP-PROTECTION.md`](IP-PROTECTION.md).
|
|
158
|
+
|
|
159
|
+
Local match simulation, hand evaluation, opponent pools, and
|
|
160
|
+
bot-vs-bot strength testing are explicitly out of scope; the platform
|
|
161
|
+
runs that evaluation post-upload.
|
|
162
|
+
|
|
163
|
+
### CLI surface
|
|
164
|
+
|
|
165
|
+
Two commands. Both have detailed `--help` output.
|
|
166
|
+
|
|
167
|
+
- `chipzen-sdk init <name>` — scaffold a new bot project from the
|
|
168
|
+
IP-protected starter template. Emits `bot.js`, `package.json`
|
|
169
|
+
(depends on `@chipzen-ai/bot`), `Dockerfile` (real
|
|
170
|
+
`bun build --compile` recipe, byte-identical to the canonical
|
|
171
|
+
starter), `.dockerignore`, `.gitignore`, `README.md`.
|
|
172
|
+
- `chipzen-sdk validate <path>` — pre-upload go/no-go. Add
|
|
173
|
+
`--check-connectivity` to also drive the bot through one canned
|
|
174
|
+
full-match exchange.
|
|
175
|
+
|
|
176
|
+
### Public API (re-exported from `@chipzen-ai/bot`)
|
|
177
|
+
|
|
178
|
+
- **`Bot`** — abstract base class. Override `decide(state) -> Action`.
|
|
179
|
+
Optional lifecycle hooks: `onMatchStart`, `onRoundStart`,
|
|
180
|
+
`onPhaseChange`, `onTurnResult`, `onRoundResult`, `onMatchEnd`.
|
|
181
|
+
- **`Action`** — class with `private constructor` + static factories:
|
|
182
|
+
`Action.fold()`, `Action.check()`, `Action.call()`,
|
|
183
|
+
`Action.raiseTo(amount)`, `Action.allIn()`. `action.toWire()`
|
|
184
|
+
produces the two-layer `turn_action` params schema.
|
|
185
|
+
- **`Card`**, **`GameState`**, **`ActionHistoryEntry`**, **`ActionKind`**
|
|
186
|
+
— types mirroring the wire schema. `parseGameState(message)` and
|
|
187
|
+
`cardFromString("Ah")` bridge the snake_case wire format.
|
|
188
|
+
- **`runBot(url, bot, options)`** — async runner driving the full
|
|
189
|
+
WebSocket lifecycle (handshake, envelope sequence check,
|
|
190
|
+
ping/pong, `action_rejected` retry, reconnect with bounded
|
|
191
|
+
exponential backoff, clean exit on `match_end`).
|
|
192
|
+
- **`runConformanceChecks(bot, options)`** — drives a bot through
|
|
193
|
+
the canned full-match exchange against an in-process mock socket;
|
|
194
|
+
same severity model as the Python harness. Surfaced via the CLI's
|
|
195
|
+
`--check-connectivity` flag.
|
|
196
|
+
- **`SUPPORTED_PROTOCOL_VERSIONS`** — `["1.0"]` baseline.
|
|
197
|
+
|
|
198
|
+
### Two-layer wire protocol
|
|
199
|
+
|
|
200
|
+
The client speaks the same Chipzen two-layer protocol the Python
|
|
201
|
+
SDK does, defined in
|
|
202
|
+
[`docs/protocol/TRANSPORT-PROTOCOL.md`](https://github.com/chipzen-ai/chipzen-sdk/blob/main/docs/protocol/TRANSPORT-PROTOCOL.md)
|
|
203
|
+
and
|
|
204
|
+
[`docs/protocol/POKER-GAME-STATE-PROTOCOL.md`](https://github.com/chipzen-ai/chipzen-sdk/blob/main/docs/protocol/POKER-GAME-STATE-PROTOCOL.md).
|
|
205
|
+
|
|
206
|
+
### Packaging
|
|
207
|
+
|
|
208
|
+
- Dual ESM + CJS via [tsup](https://tsup.egoist.dev/), with `.d.ts`
|
|
209
|
+
for both module systems.
|
|
210
|
+
- CLI binary `chipzen-sdk` ships as `dist/bin.js` with a shebang
|
|
211
|
+
prepended by the tsup banner.
|
|
212
|
+
- Published with **npm Trusted Publishing (sigstore-attested
|
|
213
|
+
provenance)** — see [`RELEASING.md`](RELEASING.md).
|
|
214
|
+
|
|
215
|
+
### License
|
|
216
|
+
|
|
217
|
+
Apache-2.0.
|
|
218
|
+
|
|
219
|
+
[0.3.0]: https://github.com/chipzen-ai/chipzen-sdk/releases/tag/javascript-v0.3.0
|
|
220
|
+
[0.2.0]: https://github.com/chipzen-ai/chipzen-sdk/releases/tag/javascript-v0.2.0
|
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# @chipzen-ai/bot
|
|
2
|
+
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> **Alpha software.** This SDK is in active development; the public
|
|
5
|
+
> API may change between minor versions before 1.0. Pin to a specific
|
|
6
|
+
> version in production. Report issues at
|
|
7
|
+
> [chipzen-ai/chipzen-sdk/issues](https://github.com/chipzen-ai/chipzen-sdk/issues).
|
|
8
|
+
|
|
9
|
+
The JavaScript adapter for the [Chipzen](https://chipzen.ai) AI poker
|
|
10
|
+
competition platform. Wraps the WebSocket protocol so your bot only
|
|
11
|
+
has to implement `decide(state) -> action`.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @chipzen-ai/bot # or pnpm / yarn
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Node 20+ supported. Also runs cleanly on Bun (the IP-protected starter
|
|
20
|
+
Dockerfile uses `bun build --compile` to ship your bot as a single
|
|
21
|
+
binary). Single runtime dependency: `ws`.
|
|
22
|
+
|
|
23
|
+
## Minimal bot
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { Bot, Action, GameState, runBot } from "@chipzen-ai/bot";
|
|
27
|
+
|
|
28
|
+
class MyBot extends Bot {
|
|
29
|
+
decide(state: GameState): Action {
|
|
30
|
+
if (state.validActions.includes("check")) return Action.check();
|
|
31
|
+
return Action.fold();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await runBot(process.env.CHIPZEN_WS_URL!, new MyBot(), {
|
|
36
|
+
token: process.env.CHIPZEN_TOKEN,
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The SDK handles the Layer-1 transport handshake, Layer-2 game-state
|
|
41
|
+
parsing, ping/pong, request-id echoing, `action_rejected` retries,
|
|
42
|
+
and reconnect. Subclass `Bot`, override `decide()`, return an
|
|
43
|
+
`Action`. That's the entire surface for a working bot.
|
|
44
|
+
|
|
45
|
+
Lifecycle hooks (`onMatchStart`, `onRoundStart`, `onPhaseChange`,
|
|
46
|
+
`onTurnResult`, `onRoundResult`, `onMatchEnd`) are optional —
|
|
47
|
+
override them if you need to maintain per-match or per-hand state
|
|
48
|
+
between turns.
|
|
49
|
+
|
|
50
|
+
## Field naming
|
|
51
|
+
|
|
52
|
+
The user-facing API uses idiomatic camelCase
|
|
53
|
+
(`state.validActions`, `state.holeCards`). The on-the-wire JSON the
|
|
54
|
+
protocol uses is snake_case (`valid_actions`, `your_hole_cards`) —
|
|
55
|
+
defined in the
|
|
56
|
+
[protocol spec](https://github.com/chipzen-ai/chipzen-sdk/tree/main/docs/protocol).
|
|
57
|
+
The parser in `parseGameState` translates between the two.
|
|
58
|
+
|
|
59
|
+
## What the SDK is for (and what it isn't)
|
|
60
|
+
|
|
61
|
+
The SDK does three things and nothing else:
|
|
62
|
+
|
|
63
|
+
1. **Protocol adapter** — your bot doesn't hand-roll WebSockets.
|
|
64
|
+
2. **`chipzen-sdk validate`** (Phase 2 PR 2) — pre-upload conformance
|
|
65
|
+
check, equivalent to the Python CLI.
|
|
66
|
+
3. **(Phase 2 PR 3) IP-protected Dockerfile recipe** — `bun build
|
|
67
|
+
--compile` multi-stage build that produces a single executable
|
|
68
|
+
instead of shipping your `.ts`/`.js` source.
|
|
69
|
+
|
|
70
|
+
It does **not** include a local match simulator, hand evaluator, or
|
|
71
|
+
opponent pool. Bot strength testing happens after upload; the
|
|
72
|
+
platform runs comprehensive bot-vs-bot evaluation as part of the
|
|
73
|
+
submission pipeline.
|
|
74
|
+
|
|
75
|
+
## Reference
|
|
76
|
+
|
|
77
|
+
Full developer documentation lives in the
|
|
78
|
+
[chipzen-sdk repo](https://github.com/chipzen-ai/chipzen-sdk):
|
|
79
|
+
|
|
80
|
+
- [DEV-MANUAL](https://github.com/chipzen-ai/chipzen-sdk/blob/main/docs/DEV-MANUAL.md)
|
|
81
|
+
— SDK reference, lifecycle hooks, performance budgets, container
|
|
82
|
+
contract, troubleshooting.
|
|
83
|
+
- [QUICKSTART](https://github.com/chipzen-ai/chipzen-sdk/blob/main/docs/QUICKSTART.md)
|
|
84
|
+
— write a bot, validate it, package it.
|
|
85
|
+
- [Protocol spec](https://github.com/chipzen-ai/chipzen-sdk/tree/main/docs/protocol)
|
|
86
|
+
— Layer 1 (Transport) + Layer 2 (Poker game state). Authoritative.
|
|
87
|
+
- [Bot runtime security model](https://github.com/chipzen-ai/chipzen-sdk/blob/main/SECURITY.md#bot-runtime--what-the-platform-enforces-on-uploaded-bots)
|
|
88
|
+
— what the platform enforces on uploaded bots.
|
|
89
|
+
- [Python adapter](https://github.com/chipzen-ai/chipzen-sdk/tree/main/packages/python)
|
|
90
|
+
— same SDK shape in Python (pip install `chipzen-bot`).
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
Apache-2.0. See the
|
|
95
|
+
[LICENSE](https://github.com/chipzen-ai/chipzen-sdk/blob/main/LICENSE)
|
|
96
|
+
file in the repo.
|