coworld 0.1.0__tar.gz
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.
- coworld-0.1.0/PKG-INFO +209 -0
- coworld-0.1.0/pyproject.toml +79 -0
- coworld-0.1.0/setup.cfg +4 -0
- coworld-0.1.0/src/coworld/CLI_README.md +125 -0
- coworld-0.1.0/src/coworld/COGAME_README.md +130 -0
- coworld-0.1.0/src/coworld/COWORLD_README.md +174 -0
- coworld-0.1.0/src/coworld/__init__.py +61 -0
- coworld-0.1.0/src/coworld/__main__.py +3 -0
- coworld-0.1.0/src/coworld/api_client.py +553 -0
- coworld-0.1.0/src/coworld/certifier.py +206 -0
- coworld-0.1.0/src/coworld/cli.py +414 -0
- coworld-0.1.0/src/coworld/cli_support.py +13 -0
- coworld-0.1.0/src/coworld/commissioner/__init__.py +47 -0
- coworld-0.1.0/src/coworld/commissioner/protocol.py +192 -0
- coworld-0.1.0/src/coworld/config.py +1 -0
- coworld-0.1.0/src/coworld/coworld_manifest_schema.json +453 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/README.md +52 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/coworld_manifest.json +143 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/Dockerfile +67 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/clients/admin.html +264 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/clients/global.html +283 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/clients/player.html +274 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/clients/replay.html +27 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/docs/global_protocol_spec.md +129 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/docs/player_protocol_spec.md +94 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/server.py +439 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/player/Dockerfile +22 -0
- coworld-0.1.0/src/coworld/examples/cogs_vs_clips/player/policy_player.py +134 -0
- coworld-0.1.0/src/coworld/examples/paintarena/Dockerfile +14 -0
- coworld-0.1.0/src/coworld/examples/paintarena/README.md +83 -0
- coworld-0.1.0/src/coworld/examples/paintarena/coworld_manifest.json +154 -0
- coworld-0.1.0/src/coworld/examples/paintarena/game/clients/admin.html +60 -0
- coworld-0.1.0/src/coworld/examples/paintarena/game/clients/global.html +250 -0
- coworld-0.1.0/src/coworld/examples/paintarena/game/clients/player.html +271 -0
- coworld-0.1.0/src/coworld/examples/paintarena/game/clients/replay.html +325 -0
- coworld-0.1.0/src/coworld/examples/paintarena/game/docs/global_protocol_spec.md +42 -0
- coworld-0.1.0/src/coworld/examples/paintarena/game/docs/player_protocol_spec.md +39 -0
- coworld-0.1.0/src/coworld/examples/paintarena/game/server.py +322 -0
- coworld-0.1.0/src/coworld/examples/paintarena/player/player.py +44 -0
- coworld-0.1.0/src/coworld/manifest_uri.py +121 -0
- coworld-0.1.0/src/coworld/manifest_validation.py +62 -0
- coworld-0.1.0/src/coworld/play.py +221 -0
- coworld-0.1.0/src/coworld/py.typed +1 -0
- coworld-0.1.0/src/coworld/runner/KUBERNETES_RUNNER_README.md +182 -0
- coworld-0.1.0/src/coworld/runner/RUNNER_README.md +13 -0
- coworld-0.1.0/src/coworld/runner/__init__.py +1 -0
- coworld-0.1.0/src/coworld/runner/io.py +56 -0
- coworld-0.1.0/src/coworld/runner/kubernetes_runner.py +449 -0
- coworld-0.1.0/src/coworld/runner/runner.py +424 -0
- coworld-0.1.0/src/coworld/schema_validation.py +22 -0
- coworld-0.1.0/src/coworld/submit.py +54 -0
- coworld-0.1.0/src/coworld/tournament_cli.py +948 -0
- coworld-0.1.0/src/coworld/types.py +187 -0
- coworld-0.1.0/src/coworld/upload.py +698 -0
- coworld-0.1.0/src/coworld.egg-info/PKG-INFO +209 -0
- coworld-0.1.0/src/coworld.egg-info/SOURCES.txt +68 -0
- coworld-0.1.0/src/coworld.egg-info/dependency_links.txt +1 -0
- coworld-0.1.0/src/coworld.egg-info/entry_points.txt +2 -0
- coworld-0.1.0/src/coworld.egg-info/requires.txt +30 -0
- coworld-0.1.0/src/coworld.egg-info/top_level.txt +1 -0
- coworld-0.1.0/tests/test_cogs_vs_clips_coworld.py +118 -0
- coworld-0.1.0/tests/test_cogs_vs_clips_policy_player.py +156 -0
- coworld-0.1.0/tests/test_commissioner_protocol.py +155 -0
- coworld-0.1.0/tests/test_coworld_certifier.py +808 -0
- coworld-0.1.0/tests/test_coworld_cli_manifest_uri.py +133 -0
- coworld-0.1.0/tests/test_coworld_kubernetes_runner.py +324 -0
- coworld-0.1.0/tests/test_coworld_manifest_uri.py +97 -0
- coworld-0.1.0/tests/test_coworld_submit_cli.py +114 -0
- coworld-0.1.0/tests/test_coworld_tournament_cli.py +344 -0
- coworld-0.1.0/tests/test_coworld_upload_cli.py +907 -0
coworld-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: coworld
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Coworld certification, upload, and runner tooling
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: <3.13,>=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: httpx>=0.28.1
|
|
9
|
+
Requires-Dist: jsonschema>=4.25.1
|
|
10
|
+
Requires-Dist: kubernetes>=31.0.0
|
|
11
|
+
Requires-Dist: mettagrid
|
|
12
|
+
Requires-Dist: packaging>=24.0.0
|
|
13
|
+
Requires-Dist: pydantic>=2.11.5
|
|
14
|
+
Requires-Dist: referencing>=0.36.2
|
|
15
|
+
Requires-Dist: rich>=13.7.0
|
|
16
|
+
Requires-Dist: typer>=0.19.2
|
|
17
|
+
Requires-Dist: websockets>=15.0.1
|
|
18
|
+
Provides-Extra: auth
|
|
19
|
+
Requires-Dist: softmax-cli; extra == "auth"
|
|
20
|
+
Provides-Extra: examples
|
|
21
|
+
Requires-Dist: cogsguard; extra == "examples"
|
|
22
|
+
Requires-Dist: fastapi>=0.115.0; extra == "examples"
|
|
23
|
+
Requires-Dist: numpy>=2.3.0; extra == "examples"
|
|
24
|
+
Requires-Dist: uvicorn>=0.34.0; extra == "examples"
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: cogsguard; extra == "test"
|
|
27
|
+
Requires-Dist: fastapi>=0.115.0; extra == "test"
|
|
28
|
+
Requires-Dist: numpy>=2.3.0; extra == "test"
|
|
29
|
+
Requires-Dist: pytest; extra == "test"
|
|
30
|
+
Requires-Dist: pytest-httpserver; extra == "test"
|
|
31
|
+
Requires-Dist: pytest-xdist; extra == "test"
|
|
32
|
+
Requires-Dist: ruff; extra == "test"
|
|
33
|
+
Requires-Dist: softmax-cli; extra == "test"
|
|
34
|
+
Requires-Dist: uvicorn>=0.34.0; extra == "test"
|
|
35
|
+
|
|
36
|
+
# Coworld Guide
|
|
37
|
+
|
|
38
|
+
This is the canonical overview for Coworlds. Use it to understand the shape of the system and which command to run
|
|
39
|
+
next. Use [COGAME_README.md](COGAME_README.md) for the game-container runtime contract and
|
|
40
|
+
[CLI_README.md](CLI_README.md) for the command reference.
|
|
41
|
+
|
|
42
|
+
A Coworld is a containerized game package that Softmax can run locally, in hosted play, and in leagues. It has:
|
|
43
|
+
|
|
44
|
+
- one game image that owns rules, state, viewers, results, and replays;
|
|
45
|
+
- one or more player images that connect to the game and choose actions;
|
|
46
|
+
- a `coworld_manifest.json` file that names the images, configs, schemas, and player docs.
|
|
47
|
+
|
|
48
|
+
## Player Loop
|
|
49
|
+
|
|
50
|
+
Use this flow when you want to build a player for an existing Coworld league:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv run softmax login
|
|
54
|
+
uv run coworld download cow_... --output-dir ./coworld
|
|
55
|
+
python -m json.tool ./coworld/coworld_manifest.json | less
|
|
56
|
+
docker build --platform=linux/amd64 -t my-player:latest .
|
|
57
|
+
uv run coworld run-episode ./coworld/coworld_manifest.json my-player:latest
|
|
58
|
+
uv run coworld upload-policy my-player:latest --name my-player
|
|
59
|
+
uv run coworld submit my-player --league league_...
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Before writing code, read the downloaded manifest:
|
|
63
|
+
|
|
64
|
+
- `game.protocols.player` links to the websocket protocol your player must implement.
|
|
65
|
+
- `game.docs.pages` may contain extra game-authored docs such as strategy notes.
|
|
66
|
+
- `certification.game_config` is the small local episode used by `coworld run-episode`.
|
|
67
|
+
- `variants` are named game configs used by leagues or local testing.
|
|
68
|
+
|
|
69
|
+
A player image receives `COGAMES_ENGINE_WS_URL`, connects to that websocket, follows the game protocol, plays until the
|
|
70
|
+
episode ends, and exits.
|
|
71
|
+
|
|
72
|
+
For local testing, one image can fill every player slot:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
uv run coworld run-episode ./coworld/coworld_manifest.json my-player:latest
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
You can also pass one image per slot:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
uv run coworld run-episode ./coworld/coworld_manifest.json player-one:latest player-two:latest
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
If the image needs a specific player command, pass it explicitly:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
uv run coworld run-episode ./coworld/coworld_manifest.json my-runtime:latest --run python --run /app/player.py
|
|
88
|
+
uv run coworld upload-policy my-runtime:latest --name my-player --run python --run /app/player.py
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Game Author Loop
|
|
92
|
+
|
|
93
|
+
Use this flow when you want to package a new Coworld:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
docker build --platform=linux/amd64 -t my-coworld-game:latest .
|
|
97
|
+
uv run coworld certify path/to/coworld_manifest.json
|
|
98
|
+
uv run coworld upload-coworld path/to/coworld_manifest.json
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The smallest complete example is [examples/paintarena/](examples/paintarena/).
|
|
102
|
+
|
|
103
|
+
Certification validates the manifest, checks the referenced Docker images, runs one short episode, checks player and
|
|
104
|
+
global client routes, checks replay viewing, and validates the results file. The certifier does not build images; build
|
|
105
|
+
or pull them first.
|
|
106
|
+
|
|
107
|
+
## Manifest
|
|
108
|
+
|
|
109
|
+
Every Coworld package has a `coworld_manifest.json` file that follows
|
|
110
|
+
[coworld_manifest_schema.json](coworld_manifest_schema.json). The main sections are:
|
|
111
|
+
|
|
112
|
+
- `game`: the game server image, config schema, result schema, and protocol docs.
|
|
113
|
+
- `player`: bundled player images that can play the game.
|
|
114
|
+
- `variants`: named game configs, such as maps, difficulty levels, or league settings.
|
|
115
|
+
- `certification`: the short smoke-test episode used by `coworld certify` and `coworld run-episode`.
|
|
116
|
+
|
|
117
|
+
The game, player, grader, reporter, commissioner, diagnoser, and optimizer sections all use the same runnable shape: an
|
|
118
|
+
image, an optional command (`run`), and optional public environment variables (`env`). Secrets do not belong in the
|
|
119
|
+
manifest.
|
|
120
|
+
|
|
121
|
+
Protocol docs are explicit document objects:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{ "type": "uri", "value": "https://example.com/player_protocol.md" }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Use `type: "uri"` for public HTTP(S) docs. Use `type: "text"` only when the docs are intentionally stored inline in the
|
|
128
|
+
manifest.
|
|
129
|
+
|
|
130
|
+
Extra docs go in `game.docs.pages`:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"id": "play",
|
|
135
|
+
"title": "play.md",
|
|
136
|
+
"content": { "type": "uri", "value": "https://example.com/play.md" }
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Upload stores the manifest as JSON. It does not bundle local Markdown files, schemas, or assets, so public docs should
|
|
141
|
+
use public URLs.
|
|
142
|
+
|
|
143
|
+
## Runtime Contract
|
|
144
|
+
|
|
145
|
+
The game image owns the episode. It must:
|
|
146
|
+
|
|
147
|
+
- read the config from `COGAME_CONFIG_URI`;
|
|
148
|
+
- serve `GET /healthz`;
|
|
149
|
+
- serve player clients at `GET /clients/player?...` and player websockets at `WEBSOCKET /player?...`;
|
|
150
|
+
- serve a live viewer at `GET /clients/global` and `WEBSOCKET /global`;
|
|
151
|
+
- write final results to `COGAME_RESULTS_URI`;
|
|
152
|
+
- write a replay artifact to `COGAME_SAVE_REPLAY_URI`;
|
|
153
|
+
- serve replay viewers when started with `COGAME_REPLAY_SERVER=1`.
|
|
154
|
+
|
|
155
|
+
Browser client pages forward their query string when they open the websocket. Hosted proxies may pass an `address`
|
|
156
|
+
query parameter containing the full websocket URL. See [COGAME_README.md](COGAME_README.md) for the exact route,
|
|
157
|
+
websocket, token, reconnect, replay, and artifact contract.
|
|
158
|
+
|
|
159
|
+
The game config schema must define `tokens` as a required string array with equal `minItems` and `maxItems`. That fixed
|
|
160
|
+
length is the number of player slots. Coworld-authored configs omit `tokens`; the runner creates fresh tokens for each
|
|
161
|
+
episode and injects them into the concrete runtime config.
|
|
162
|
+
|
|
163
|
+
## Upload And Inspect
|
|
164
|
+
|
|
165
|
+
Coworld upload certifies the package, uploads every runnable image, rewrites image references to Softmax-managed image
|
|
166
|
+
IDs, and uploads the standalone manifest:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
uv run coworld upload-coworld path/to/coworld_manifest.json
|
|
170
|
+
uv run coworld list
|
|
171
|
+
uv run coworld show cow_...
|
|
172
|
+
uv run coworld images
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Player upload creates a policy version, and submit enters that policy into a league:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
uv run coworld upload-policy my-player:latest --name my-player
|
|
179
|
+
uv run coworld submit my-player --league league_...
|
|
180
|
+
uv run coworld submit my-player:v2 --league league_...
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Public runtime settings belong in the Docker image or manifest `env`. Secrets should be attached to the uploaded policy
|
|
184
|
+
version:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
uv run coworld upload-policy my-player:latest \
|
|
188
|
+
--name my-player \
|
|
189
|
+
--use-bedrock \
|
|
190
|
+
--secret-env ANTHROPIC_API_KEY=sk-ant-...
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`--use-bedrock` adds `USE_BEDROCK=true`. `--secret-env KEY=VALUE` can be repeated. During Coworld episodes, only the
|
|
194
|
+
player pod for that policy version receives those secret variables.
|
|
195
|
+
|
|
196
|
+
## Results And Replays
|
|
197
|
+
|
|
198
|
+
Use the Coworld CLI to inspect leagues, submissions, standings, episode requests, logs, and replays:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
uv run coworld submissions --mine --league league_...
|
|
202
|
+
uv run coworld memberships --mine --division div_... --active-only
|
|
203
|
+
uv run coworld episodes --division div_... --mine --with-replay
|
|
204
|
+
uv run coworld episode-logs ereq_... --mine --download-dir logs/
|
|
205
|
+
uv run coworld replays --division div_... --mine --download-dir replays/
|
|
206
|
+
uv run coworld replay-open ereq_...
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
See [CLI_README.md](CLI_README.md) for the full command reference.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools==80.9.0", "wheel==0.45.1"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "coworld"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Coworld certification, upload, and runner tooling"
|
|
9
|
+
readme = "src/coworld/COWORLD_README.md"
|
|
10
|
+
requires-python = ">=3.11,<3.13"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"httpx>=0.28.1",
|
|
14
|
+
"jsonschema>=4.25.1",
|
|
15
|
+
"kubernetes>=31.0.0",
|
|
16
|
+
"mettagrid",
|
|
17
|
+
"packaging>=24.0.0",
|
|
18
|
+
"pydantic>=2.11.5",
|
|
19
|
+
"referencing>=0.36.2",
|
|
20
|
+
"rich>=13.7.0",
|
|
21
|
+
"typer>=0.19.2",
|
|
22
|
+
"websockets>=15.0.1",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
auth = ["softmax-cli"]
|
|
27
|
+
examples = ["cogsguard", "fastapi>=0.115.0", "numpy>=2.3.0", "uvicorn>=0.34.0"]
|
|
28
|
+
test = [
|
|
29
|
+
"cogsguard",
|
|
30
|
+
"fastapi>=0.115.0",
|
|
31
|
+
"numpy>=2.3.0",
|
|
32
|
+
"pytest",
|
|
33
|
+
"pytest-httpserver",
|
|
34
|
+
"pytest-xdist",
|
|
35
|
+
"ruff",
|
|
36
|
+
"softmax-cli",
|
|
37
|
+
"uvicorn>=0.34.0",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.scripts]
|
|
41
|
+
coworld = "coworld.cli:app"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.packages.find]
|
|
44
|
+
where = ["src"]
|
|
45
|
+
include = ["coworld", "coworld.*"]
|
|
46
|
+
|
|
47
|
+
[tool.setuptools]
|
|
48
|
+
include-package-data = true
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.package-data]
|
|
51
|
+
coworld = ["py.typed", "*.json", "*.md", "runner/*.md", "examples/**/*"]
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.exclude-package-data]
|
|
54
|
+
coworld = [
|
|
55
|
+
"**/__pycache__/*",
|
|
56
|
+
"**/*.pyc",
|
|
57
|
+
# Files the Python runtime never reads. Excluding them from the
|
|
58
|
+
# wheel means a non-runtime edit (e.g. updating an example
|
|
59
|
+
# Dockerfile) doesn't change wheel content, doesn't change the
|
|
60
|
+
# @pyenv tree bazel hashes into action keys, and so doesn't
|
|
61
|
+
# invalidate every downstream test that has @pyenv in its
|
|
62
|
+
# transitive deps.
|
|
63
|
+
"**/Dockerfile",
|
|
64
|
+
"examples/**/README.md",
|
|
65
|
+
"examples/**/*_spec.md",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[tool.pytest.ini_options]
|
|
69
|
+
pythonpath = ["src"]
|
|
70
|
+
testpaths = ["tests"]
|
|
71
|
+
markers = ["slow: marks tests as slow"]
|
|
72
|
+
|
|
73
|
+
[tool.uv.sources]
|
|
74
|
+
mettagrid = { workspace = true }
|
|
75
|
+
softmax-cli = { workspace = true }
|
|
76
|
+
cogsguard = { git = "https://github.com/Metta-AI/cogame-cogsguard.git" }
|
|
77
|
+
|
|
78
|
+
[tool.ruff]
|
|
79
|
+
extend = "../../.ruff.toml"
|
coworld-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Coworld CLI
|
|
2
|
+
|
|
3
|
+
The `coworld` command is the local tool for Coworld development and league work. It downloads Coworlds, runs local
|
|
4
|
+
episodes, uploads player images, submits policies to leagues, and inspects results. For concepts, start with
|
|
5
|
+
[COWORLD_README.md](COWORLD_README.md).
|
|
6
|
+
|
|
7
|
+
Commands that talk to Softmax use the current `softmax-cli` login:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv run softmax login
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Pass `--server` only when targeting a non-default Observatory API environment.
|
|
14
|
+
|
|
15
|
+
## Player Loop
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv run coworld download cow_... --output-dir ./coworld
|
|
19
|
+
docker build --platform=linux/amd64 -t my-player:latest .
|
|
20
|
+
uv run coworld run-episode ./coworld/coworld_manifest.json my-player:latest
|
|
21
|
+
uv run coworld upload-policy my-player:latest --name my-player
|
|
22
|
+
uv run coworld submit my-player --league league_...
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
If the image needs a specific player command:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv run coworld run-episode ./coworld/coworld_manifest.json my-runtime:latest --run python --run /app/player.py
|
|
29
|
+
uv run coworld upload-policy my-runtime:latest --name my-player --run python --run /app/player.py
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Check the submission and its first episodes:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv run coworld submissions --mine --league league_...
|
|
36
|
+
uv run coworld memberships --mine --division div_... --active-only
|
|
37
|
+
uv run coworld episodes --division div_... --mine --with-replay
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Coworld Packages
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uv run coworld certify path/to/coworld_manifest.json
|
|
44
|
+
uv run coworld upload-coworld path/to/coworld_manifest.json
|
|
45
|
+
uv run coworld list
|
|
46
|
+
uv run coworld show cow_...
|
|
47
|
+
uv run coworld images
|
|
48
|
+
uv run coworld images img_...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Tournaments
|
|
52
|
+
|
|
53
|
+
Inspect the tournament structure:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv run coworld leagues
|
|
57
|
+
uv run coworld leagues league_...
|
|
58
|
+
uv run coworld divisions --league league_...
|
|
59
|
+
uv run coworld divisions div_...
|
|
60
|
+
uv run coworld rounds --division div_... --status completed
|
|
61
|
+
uv run coworld rounds round_...
|
|
62
|
+
uv run coworld pools --round round_...
|
|
63
|
+
uv run coworld pools pool_...
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Inspect tournament outcomes:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
uv run coworld results league_...
|
|
70
|
+
uv run coworld results div_...
|
|
71
|
+
uv run coworld results round_...
|
|
72
|
+
uv run coworld memberships --mine --division div_... --active-only
|
|
73
|
+
uv run coworld submissions --mine --league league_...
|
|
74
|
+
uv run coworld events --division div_...
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Most commands support `--json` for machine-readable output.
|
|
78
|
+
|
|
79
|
+
## Episodes
|
|
80
|
+
|
|
81
|
+
List episode requests by pool, round, or division:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
uv run coworld episodes --division div_...
|
|
85
|
+
uv run coworld episodes --round round_... --with-replay
|
|
86
|
+
uv run coworld episodes --pool pool_... --policy my-policy:v3
|
|
87
|
+
uv run coworld episodes ereq_...
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Use `--mine` to keep only episodes involving policy versions in your current league memberships:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
uv run coworld episodes --division div_... --mine --with-replay --json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Fetch artifacts for one episode request:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
uv run coworld episode-stats ereq_...
|
|
100
|
+
uv run coworld episode-stats ereq_... --json
|
|
101
|
+
uv run coworld episode-results ereq_... --output results.json
|
|
102
|
+
uv run coworld episode-logs ereq_... --list
|
|
103
|
+
uv run coworld episode-logs ereq_... --agent 0
|
|
104
|
+
uv run coworld episode-logs ereq_... --mine --download-dir logs/
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Download replay files:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
uv run coworld replays --division div_... --mine --download-dir replays/
|
|
111
|
+
uv run coworld replays --round round_... --policy my-policy:v3 --json
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`replays --download-dir` writes one replay JSON file per episode request plus `index.json` metadata with the episode,
|
|
115
|
+
job, Coworld, participant, score, and source replay URI details.
|
|
116
|
+
|
|
117
|
+
Open one replay:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
uv run coworld replay-open ereq_...
|
|
121
|
+
uv run coworld replay-open ereq_... --hosted
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The default `replay-open` path runs the replay locally with the Coworld manifest and replay artifact. `--hosted` creates
|
|
125
|
+
an Observatory-hosted replay session and prints the viewer URL.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Coworld Game Runtime Contract
|
|
2
|
+
|
|
3
|
+
This is the canonical contract for game images. Player authors usually only need the player protocol linked from a
|
|
4
|
+
Coworld manifest; game authors need this file.
|
|
5
|
+
|
|
6
|
+
A Coworld episode has one game container and one player container per slot. The runner starts the game, gives it a
|
|
7
|
+
config, starts the players, records logs, and collects results and replay artifacts. The game owns the rules. Players
|
|
8
|
+
connect over websocket and choose actions.
|
|
9
|
+
|
|
10
|
+
## Manifest Fields
|
|
11
|
+
|
|
12
|
+
The `game` object in `coworld_manifest.json` describes the game container:
|
|
13
|
+
|
|
14
|
+
- `name`, `version`, `description`, and `owner`
|
|
15
|
+
- `runnable`: Docker image, optional command, and public environment variables
|
|
16
|
+
- `config_schema`: JSON Schema for the runtime game config
|
|
17
|
+
- `results_schema`: JSON Schema for the final results file
|
|
18
|
+
- `protocols.player`: player websocket protocol docs
|
|
19
|
+
- `protocols.global`: live viewer websocket protocol docs
|
|
20
|
+
|
|
21
|
+
Protocol docs are document objects:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{ "type": "uri", "value": "https://example.com/player_protocol.md" }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Use `type: "uri"` for public HTTP(S) docs and `type: "text"` only for deliberately inline docs.
|
|
28
|
+
|
|
29
|
+
## Player Slots
|
|
30
|
+
|
|
31
|
+
The game config schema must require a `tokens` field. It must be a string array with equal `minItems` and `maxItems`.
|
|
32
|
+
That fixed length is the number of player slots.
|
|
33
|
+
|
|
34
|
+
Coworld-authored configs, variants, and certification fixtures do not include `tokens`. The runner creates fresh tokens
|
|
35
|
+
for each episode and injects them into the concrete runtime config.
|
|
36
|
+
|
|
37
|
+
The game must reject a `/player` websocket connection when the slot/token pair is not valid for that episode.
|
|
38
|
+
|
|
39
|
+
## Rollout Mode
|
|
40
|
+
|
|
41
|
+
For a normal episode, the runner starts the game with:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
COGAME_CONFIG_URI=...
|
|
45
|
+
COGAME_RESULTS_URI=...
|
|
46
|
+
COGAME_SAVE_REPLAY_URI=...
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The game must support `file://` URIs. Hosted runners may use other writable URI schemes when the game supports them.
|
|
50
|
+
|
|
51
|
+
In rollout mode, the game listens on `0.0.0.0:8080` and exposes:
|
|
52
|
+
|
|
53
|
+
- `GET /healthz`
|
|
54
|
+
- `GET /clients/player?slot=0&token=...&...`
|
|
55
|
+
- `WEBSOCKET /player?slot=0&token=...&...`
|
|
56
|
+
- `GET /clients/global`
|
|
57
|
+
- `WEBSOCKET /global`
|
|
58
|
+
|
|
59
|
+
`GET /healthz` returns 200 when the game is ready.
|
|
60
|
+
|
|
61
|
+
`GET /clients/player` serves a browser client for one slot. `GET /clients/global` serves a live viewer.
|
|
62
|
+
|
|
63
|
+
The served browser clients read the complete page query string before opening their websocket. If the query contains
|
|
64
|
+
`address`, the client uses that value as the full websocket URL after converting `http` or `https` to `ws` or `wss`;
|
|
65
|
+
it must not merge other page query params into that URL. Otherwise, the client derives the websocket URL by replacing
|
|
66
|
+
`/clients/player` with `/player`, or `/clients/global` with `/global`, and preserving the page query params such as
|
|
67
|
+
`slot`, `token`, and game-owned params.
|
|
68
|
+
|
|
69
|
+
For example, `/clients/player?slot=0&token=abc&role=scout` should open
|
|
70
|
+
`/player?slot=0&token=abc&role=scout`. A hosted proxy may instead serve
|
|
71
|
+
`/clients/player?address=wss://example.com/player?slot=0&token=abc`.
|
|
72
|
+
|
|
73
|
+
The `/global` websocket must support late viewers. A viewer that joins after the episode starts should receive enough
|
|
74
|
+
state to render from that point forward.
|
|
75
|
+
|
|
76
|
+
The `/player` websocket must allow the same slot to reconnect with the same token while the episode is still running.
|
|
77
|
+
The slot's game state survives disconnects. During a disconnect, the game may use no-op actions or another documented
|
|
78
|
+
behavior.
|
|
79
|
+
|
|
80
|
+
Games may expose local-only admin controls by convention:
|
|
81
|
+
|
|
82
|
+
- `GET /clients/admin`
|
|
83
|
+
- `WEBSOCKET /admin?...`
|
|
84
|
+
|
|
85
|
+
The admin route is game-owned. The platform must not expose `/admin` in production.
|
|
86
|
+
|
|
87
|
+
## Replay Mode
|
|
88
|
+
|
|
89
|
+
For replay viewing, the runner starts or reuses the same game image with:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
COGAME_REPLAY_SERVER=1
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
In replay mode, the game listens on `0.0.0.0:8080` and exposes:
|
|
96
|
+
|
|
97
|
+
- `GET /healthz`
|
|
98
|
+
- `GET /clients/replay?uri=<uri>`
|
|
99
|
+
- `WEBSOCKET /replay?uri=<uri>`
|
|
100
|
+
|
|
101
|
+
`GET /clients/replay?uri=<uri>` serves a browser replay viewer. The served client opens `/replay?uri=<uri>`. The game
|
|
102
|
+
loads the replay artifact and handles game-owned replay controls such as start, stop, seek, or speed changes.
|
|
103
|
+
|
|
104
|
+
The replay artifact format and replay websocket protocol are game-owned.
|
|
105
|
+
|
|
106
|
+
## Episode Lifecycle
|
|
107
|
+
|
|
108
|
+
1. The runner receives a job with the manifest, game config, players, and artifact output URIs.
|
|
109
|
+
2. The runner generates one token per player slot.
|
|
110
|
+
3. The runner writes the concrete game config, including `tokens`.
|
|
111
|
+
4. The runner starts the game container with config, result, and replay URIs.
|
|
112
|
+
5. The game reads its config and starts listening on port 8080.
|
|
113
|
+
6. The runner waits for `GET /healthz` to return 200.
|
|
114
|
+
7. The runner starts one player container per slot.
|
|
115
|
+
8. Each player gets `COGAMES_ENGINE_WS_URL=ws://<engine-host>/player?slot=<slot>&token=<token>`.
|
|
116
|
+
9. Players connect to `/player` and send game-specific actions.
|
|
117
|
+
10. Viewers may connect to `/global`.
|
|
118
|
+
11. The game runs until the episode ends.
|
|
119
|
+
12. The game writes results to `COGAME_RESULTS_URI`.
|
|
120
|
+
13. The game writes a replay to `COGAME_SAVE_REPLAY_URI`.
|
|
121
|
+
14. The runner validates results against `results_schema` and stores results, replay, and logs.
|
|
122
|
+
|
|
123
|
+
The final results file must match `game.results_schema`. It must include `scores`, one number per player slot, and may
|
|
124
|
+
include extra game-specific fields declared by the schema.
|
|
125
|
+
|
|
126
|
+
## Certification
|
|
127
|
+
|
|
128
|
+
Coworld certification turns the manifest's `certification` fixture into one local episode. It runs the game and bundled
|
|
129
|
+
player images, checks the HTTP routes, verifies that bad player tokens are rejected, validates final results, and checks
|
|
130
|
+
that the replay viewer can start.
|