add http.cm and probe
This commit is contained in:
233
docs/library/probe.md
Normal file
233
docs/library/probe.md
Normal file
@@ -0,0 +1,233 @@
|
||||
---
|
||||
title: "probe"
|
||||
description: "Runtime observability for actors"
|
||||
weight: 90
|
||||
type: "docs"
|
||||
---
|
||||
|
||||
Runtime observability for actors. Register named probe functions on any actor and query them over HTTP while the program runs.
|
||||
|
||||
```javascript
|
||||
var probe = use('probe')
|
||||
```
|
||||
|
||||
The probe server starts automatically on the first `register()` call, listening on `127.0.0.1:9000`.
|
||||
|
||||
## Registering Probes
|
||||
|
||||
### probe.register(target, probes)
|
||||
|
||||
Register a group of probe functions under a target name. Each probe is a function that receives an `args` record and returns a value.
|
||||
|
||||
```javascript
|
||||
var probe = use('probe')
|
||||
|
||||
var world = {
|
||||
entities: [
|
||||
{id: 1, name: "player", x: 10, y: 20, hp: 100},
|
||||
{id: 2, name: "goblin", x: 55, y: 30, hp: 40}
|
||||
],
|
||||
tick: 0
|
||||
}
|
||||
|
||||
probe.register("game", {
|
||||
state: function(args) {
|
||||
return world
|
||||
},
|
||||
entities: function(args) {
|
||||
return world.entities
|
||||
},
|
||||
entity: function(args) {
|
||||
return find(world.entities, function(e) {
|
||||
return e.id == args.id
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
probe.register("render", {
|
||||
info: function(args) {
|
||||
return {fps: 60, draw_calls: 128, batches: 12}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
A target is just a namespace — group related probes under the same target name. Register as many targets as you like; the server starts once and serves them all.
|
||||
|
||||
## HTTP Endpoints
|
||||
|
||||
All responses are JSON with an `ok` field.
|
||||
|
||||
### GET /discover
|
||||
|
||||
Lists all registered targets and their probe names. Designed for tooling — an LLM or dashboard can call this first to learn what's available, then query specific probes.
|
||||
|
||||
```
|
||||
$ curl http://127.0.0.1:9000/discover
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"targets": {
|
||||
"game": ["state", "entities", "entity"],
|
||||
"render": ["info"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### POST /probe
|
||||
|
||||
Call a single probe function by target and name. Optionally pass arguments.
|
||||
|
||||
```
|
||||
$ curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"target":"game","name":"state"}' \
|
||||
http://127.0.0.1:9000/probe
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"result": {
|
||||
"entities": [
|
||||
{"id": 1, "name": "player", "x": 10, "y": 20, "hp": 100},
|
||||
{"id": 2, "name": "goblin", "x": 55, "y": 30, "hp": 40}
|
||||
],
|
||||
"tick": 4821
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With arguments:
|
||||
|
||||
```
|
||||
$ curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"target":"game","name":"entity","args":{"id":1}}' \
|
||||
http://127.0.0.1:9000/probe
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"result": {"id": 1, "name": "player", "x": 10, "y": 20, "hp": 100}
|
||||
}
|
||||
```
|
||||
|
||||
### POST /snapshot
|
||||
|
||||
Call multiple probes in one request. Returns all results keyed by `target/name`.
|
||||
|
||||
```
|
||||
$ curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"probes":[{"target":"game","name":"state"},{"target":"render","name":"info"}]}' \
|
||||
http://127.0.0.1:9000/snapshot
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"results": {
|
||||
"game/state": {
|
||||
"entities": [
|
||||
{"id": 1, "name": "player", "x": 10, "y": 20, "hp": 100},
|
||||
{"id": 2, "name": "goblin", "x": 55, "y": 30, "hp": 40}
|
||||
],
|
||||
"tick": 4821
|
||||
},
|
||||
"render/info": {
|
||||
"fps": 60,
|
||||
"draw_calls": 128,
|
||||
"batches": 12
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Errors
|
||||
|
||||
Unknown paths return 404:
|
||||
|
||||
```json
|
||||
{"ok": false, "error": "not found"}
|
||||
```
|
||||
|
||||
Unknown targets or probe names:
|
||||
|
||||
```json
|
||||
{"ok": false, "error": "unknown probe: game/nonexistent"}
|
||||
```
|
||||
|
||||
If a probe function disrupts:
|
||||
|
||||
```json
|
||||
{"ok": false, "error": "probe failed"}
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
A game actor with a simulation loop and probe observability:
|
||||
|
||||
```javascript
|
||||
// game.ce
|
||||
var probe = use('probe')
|
||||
|
||||
var state = {
|
||||
entities: [
|
||||
{id: 1, name: "player", x: 0, y: 0, hp: 100},
|
||||
{id: 2, name: "enemy", x: 50, y: 50, hp: 60}
|
||||
],
|
||||
frame: 0,
|
||||
paused: false
|
||||
}
|
||||
|
||||
probe.register("game", {
|
||||
state: function(args) {
|
||||
return state
|
||||
},
|
||||
entities: function(args) {
|
||||
return state.entities
|
||||
},
|
||||
entity: function(args) {
|
||||
return find(state.entities, function(e) {
|
||||
return e.id == args.id
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// game loop
|
||||
def tick = function(_) {
|
||||
if (!state.paused) {
|
||||
state.frame = state.frame + 1
|
||||
// ... update entities, physics, AI ...
|
||||
}
|
||||
$delay(tick, 0.016)
|
||||
}
|
||||
$delay(tick, 0.016)
|
||||
```
|
||||
|
||||
While the game runs, query it from a terminal:
|
||||
|
||||
```
|
||||
$ curl -s http://127.0.0.1:9000/discover | jq .targets
|
||||
{
|
||||
"game": ["state", "entities", "entity"]
|
||||
}
|
||||
|
||||
$ curl -s -X POST -d '{"target":"game","name":"state"}' \
|
||||
-H "Content-Type: application/json" \
|
||||
http://127.0.0.1:9000/probe | jq .result.frame
|
||||
7834
|
||||
|
||||
$ curl -s -X POST -d '{"target":"game","name":"entity","args":{"id":1}}' \
|
||||
-H "Content-Type: application/json" \
|
||||
http://127.0.0.1:9000/probe | jq .result
|
||||
{
|
||||
"id": 1,
|
||||
"name": "player",
|
||||
"x": 142,
|
||||
"y": 87,
|
||||
"hp": 100
|
||||
}
|
||||
```
|
||||
|
||||
Probes run inside the actor's normal turn, so the values are always consistent — never a half-updated frame.
|
||||
Reference in New Issue
Block a user