Skip to content

Commit d65c39c

Browse files
authored
Merge pull request #133 from operatorstack/feat/pitot-workbench-runnable
Make Pitot Workbench runnable from a clean repository
2 parents 58ebfac + 67082ad commit d65c39c

18 files changed

Lines changed: 1109 additions & 267 deletions

File tree

labs/15-pitot/pitot-distribution/UPSTREAM.json

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"files": {
33
"CONTRIBUTING.md": "23728d8a132d62b8adfb2e5c3eb9d9bfcf8a4d04543765b1e22ad8d55424af8f",
4-
"README.md": "091e47c27d0ee66a533b9aa9f3046a1bc9b84c0db5021926c6ab573d465ca57a",
4+
"README.md": "77995d36de1a6ac5f5c687fad4a152b8824ea3ad25abe65325a7c4ee9a2ef52d",
55
"adapter-verification.json": "f8ad4e206571650f698826a8b66d8c00822be425e8d2de8ae98d98239e575eb4",
66
"adapters/adapters.go": "1b46ba131fa3b2c93eed23526330275a3506451ba4bbd4f497e5378dfab2b6a8",
77
"assets/pitot-boundary.png": "8a0ddb7d81831d94e14813f50ea4ca8670d77417f339ed2f91f0c653bf52f41d",
@@ -14,9 +14,10 @@
1414
"bridge/bridge.go": "5adfcd3f743cae46e4446a6e030d53464ada97de0261a8588fa2a9fcd62136b8",
1515
"bridge/bridge_test.go": "6dcc6d05f2b39c25955fc0b2d21d3d148dd9d77600fb12799941f86bdb1acb61",
1616
"cmd/generate-schema/main.go": "6e9d0030290d99e36967433f96e38385a122974f899ad9421aac1ef7e50d8fcb",
17-
"cmd/pitot/main.go": "d78ce2331aed6db847b29bd683bb6df57eb9e3bd82fdb438b29371c02a893a88",
17+
"cmd/pitot/main.go": "14de1d6bf7ef7a75172ffd015c7cab1b66c38706b879de8d354137b34596c7f3",
1818
"cmd/pitot/main_test.go": "544997295e0c4b75ef8f3d698b3de0883153f671b6b8f62057cc6e3452d6dc93",
19-
"cmd/pitot/workbench.go": "3b4fe94e4663842194e9439af3a4049a86cf71fc2b38aac8af8ca1f667a59999",
19+
"cmd/pitot/workbench.go": "2e2522491437c624b241fd594d678be2dab6824fc5aa3235db1ec82e00a669c7",
20+
"cmd/pitot/workbench_test.go": "457caa11cd4b73c1fb4e0dad806b3050b196ddac690a8125f1615a4c695cc073",
2021
"config/config.go": "e6666567d0c0cca41de69361e8f1243adda1ec0a54a9300b39a84d2290bff319",
2122
"config/config_test.go": "87d3e5ddc4a3b43c736070de671d03e03ffe29cdd759771526ad27fd9bc0034c",
2223
"conformance/conformance.go": "43b692114f45c8b52958e34b35aee1cee339d8321c90f92ab4f5b963e79935bb",
@@ -49,7 +50,7 @@
4950
"runtime/capabilities.go": "76c27bdd7ddfa11a1d639915ac2eb2d573b7ccdd687a5f548b3731b7c1e1828f",
5051
"runtime/descriptor_unix.go": "df41b6867e9840933f186c93b6bc61861e7ea5252a365455686ea0414bfa0044",
5152
"runtime/descriptor_windows.go": "2d9ffefe3af0154fa8042de6b67460d4e86dd3f4cdd9e986f180f7d0c535c9a5",
52-
"runtime/runtime.go": "693189eadd040629b616614e25dcdebb954e9c3546c25d064d0b8c3a7d01394a",
53+
"runtime/runtime.go": "e0624a16ac9f79e8080246042912c73b1588f1b9aeb4f9f95b3711c7acef7e81",
5354
"runtime/runtime_test.go": "afd78d122af20148bf30d0db873ff002544189df0dfec0f6167b8cf5cd0d42b1",
5455
"runtime/transport.go": "83e2218fb28474e875dafa6943bc5b665acef0565aaf5955fa88b1b4fd21614e",
5556
"runtime/transport_test.go": "9b69f590f1e258470adea249b3ac6d4a00f1001f10bb08dfa7b56c6e2d6709ae",
@@ -69,21 +70,18 @@
6970
"sdk/python/pitot/__init__.py": "9cab11b333536f167d4e7bb6089ef00488690ec7b5cd5701f08e8bbb13cef0f7",
7071
"sdk/python/pitot/runner.py": "8d4537ffc3aee2ea22b1d691559ac1eb1e95521df9ad5bf241ee6ac54c9925b1",
7172
"sdk/python/pitot/types.py": "c9a7221f1ad6627f152f26148155d3c74f249c52262c73a515251f469e8eb4ad",
72-
"sdk/python/pyproject.toml": "3f42de8223640978ad88e3f7b4ad1464053c43a393f0edf48445cacd31dd537c",
73+
"sdk/python/pyproject.toml": "4b43380a1ebc12350ee3c30633695160ba0fc0bb64501b1e16f990a6fbe56d02",
7374
"sdk/runner.go": "e9a8db96d3cf6df7ea7e661e6755650f97e580970995dfe881b4268db0c3f832",
75+
"sdk/rust/Cargo.toml": "b8435f6c600ad0791bd29ada4bc396b14e54b96ee3804896c8e610583cc70c1a",
7476
"sdk/rust/src/lib.rs": "fd2dcb9bf9df47fb58e52e4e94136f95867616d941ba66b4b324413d1c5b1777",
75-
"sdk/rust/src/runner.rs": "96b9a04332b03999449ab24cc4e38df68dfc575a82d8fd599ad5211c7bfeabe6",
77+
"sdk/rust/src/runner.rs": "168eb3f926aed4b86aa27ad66ab605326974b6a345b07a35126d4b66e0968b33",
7678
"sdk/rust/src/types.rs": "82cd5a5ec12effa6688e8b9b6028bbef67ec43f24d9fd9d19b4235934061ffb8",
77-
"sdk/tests/go_test.go": "4700cc0bcebb0be8af79e84438ef47a75d2892fcfef63352620ca22a2ac5fb33",
78-
"sdk/tests/go_test_runner.go": "4700cc0bcebb0be8af79e84438ef47a75d2892fcfef63352620ca22a2ac5fb33",
79-
"sdk/tests/python_test.py": "c52fae5cfa0e433ec95fa8c79a67804857facd456d75b6523d1ed16ddbb2672c",
80-
"sdk/tests/test_runners.py": "19a6f950ee5b53b34d275ec39d3f9536bd2d693c2e11efa0e20ffe423f101f4c",
81-
"sdk/tests/ts_test.ts": "63fe8bd68e4e8aa22d7ae24929f34c502978c19ec41c0f1645c1d513cb17329b",
82-
"sdk/typescript/package.json": "c26c726559ff0bc7c5d92b68303efc3c35314613248fe346fa125aea06c8dba6",
79+
"sdk/tests/test_runners.py": "9e86a03b53b108b70204e797b4c65b3d157c6765543a9bb9d7bb892daaf4d8c4",
80+
"sdk/typescript/package.json": "8bd1fa7716b65c3c6599307d8d2e9398c75ae842c6047e9e0ad7716122db9a03",
8381
"sdk/typescript/src/index.ts": "bddb336bc120ecd979de0288e432ed2b41749d39f11c697f019af5e54c2e9fba",
8482
"sdk/typescript/src/pitot.ts": "9c243824cbb7edc54b1e125abfd828bf2ed77e7151a0bd5c5d4f63e81ca9b00c",
85-
"sdk/typescript/src/runner.ts": "2022d4ee7efedec2c4c3baca2229440aa9773f5c32b58ffed8b1a2915add0278",
86-
"sdk/typescript/tsconfig.json": "e4d7ecb203fcb7d93cd9b9fb235d7fd75d1b6fb2b28eff773f4aecfdc1924d5d",
83+
"sdk/typescript/src/runner.ts": "c58babd3ec3996a05988f3cb7dec061cf7c48f41cd5125625be3408cb9b207f0",
84+
"sdk/typescript/tsconfig.json": "3acd617cc06089125bcd963a3624131d765bb59e1a93db9736e78de6cf4fb40d",
8785
"sensor/decode_fuzz_test.go": "d27f2fbbc069eded26a73c9cd9bace98dd8a9e34949576790b81a08d130fbaf2",
8886
"sensor/sensor.go": "5c503d07ac33e7894d635f2d98bcd6d165d442d60c127ed6aeac80ec319086c5",
8987
"sensor/sensor_test.go": "4ddbdde3e486c9e189a2ed4174ad413df107d43dc98f5242da3667a24c7a5da1",
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
### Make the Workbench runnable from a clean repository
2+
3+
A first-time user can now go from a clean checkout to a real allow/deny decision in two commands, across all four first-class languages (Python, TypeScript, Go, Rust).
4+
5+
- **`pitot init`**: validates `--role`/`--language`, detects the language from files already present, prompts when running interactively, and refuses to overwrite existing files without `--force`. It now scaffolds a complete, runnable project — source, package manifest (`go.mod` / `Cargo.toml` / `package.json` + `tsconfig.json` / `requirements.txt` + `pyproject.toml`), and `.pitot.yaml`.
6+
- **`pitot dev`**: accepts a full command line via `--exec` (or `-- CMD ARGS`), validates `--host`, isolates each run in a unique runtime path that is cleaned up on exit, waits for the runtime to be ready before launching the agent, and prints an allow/deny receipt for every decision.
7+
- **SDK correctness**: the TypeScript runner emits responses in request order; the Rust runner flushes each response and surfaces stdin read errors. TypeScript packaging (`main`/`types`/`tsconfig`/`files`) is now consistent, and the Python package declares `requires-python >= 3.10` to match its generated types.
8+
- **Conformance**: a shared suite exercises every language across Consumer and Controller roles for ordered streams, malformed input, deny outcomes, and stdout cleanliness.
9+
10+
The README now leads with the `pitot init``pitot dev` happy path and swapping to a second agent, with the manual runtime flow kept as an advanced option.

labs/15-pitot/pitot-distribution/sdk/python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ authors = [
1010
{ name = "Operator Stack" }
1111
]
1212
license = { text = "MIT" }
13-
requires-python = ">=3.8"
13+
requires-python = ">=3.10"
1414
dependencies = []
1515

1616
[project.urls]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "pitot"
3+
version = "0.1.0"
4+
edition = "2021"
5+
description = "Pitot: The passive, protocol-first measurement boundary for coding agents."
6+
license = "MIT"
7+
repository = "https://github.com/operatorstack/pitot"
8+
9+
[lib]
10+
path = "src/lib.rs"
11+
12+
[dependencies]
13+
serde = { version = "1", features = ["derive"] }
14+
serde_json = "1"

labs/15-pitot/pitot-distribution/sdk/rust/src/runner.rs

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::io::{self, BufRead};
1+
use std::io::{self, BufRead, Write};
22
use serde_json;
33

44
use crate::types::{Event, ControlRequested, ControlResponse};
@@ -11,17 +11,23 @@ where
1111
{
1212
let stdin = io::stdin();
1313
for line in stdin.lock().lines() {
14-
if let Ok(line_str) = line {
15-
if line_str.trim().is_empty() {
14+
let line_str = match line {
15+
Ok(value) => value,
16+
Err(err) => {
17+
// Surface stdin read faults instead of silently dropping them.
18+
eprintln!("Pitot Consumer error: {}", err);
1619
continue;
1720
}
18-
match serde_json::from_str::<Event>(&line_str) {
19-
Ok(event) => {
20-
handler(event);
21-
}
22-
Err(err) => {
23-
eprintln!("Pitot Consumer error: {}", err);
24-
}
21+
};
22+
if line_str.trim().is_empty() {
23+
continue;
24+
}
25+
match serde_json::from_str::<Event>(&line_str) {
26+
Ok(event) => {
27+
handler(event);
28+
}
29+
Err(err) => {
30+
eprintln!("Pitot Consumer error: {}", err);
2531
}
2632
}
2733
}
@@ -53,29 +59,46 @@ where
5359
F: Fn(ControlRequested) -> Outcome + Send + Sync + 'static,
5460
{
5561
let stdin = io::stdin();
62+
let stdout = io::stdout();
5663
for line in stdin.lock().lines() {
57-
if let Ok(line_str) = line {
58-
if line_str.trim().is_empty() {
64+
let line_str = match line {
65+
Ok(value) => value,
66+
Err(err) => {
67+
eprintln!("Pitot Controller error: {}", err);
5968
continue;
6069
}
61-
match serde_json::from_str::<ControlRequested>(&line_str) {
62-
Ok(req) => {
63-
let result = handler(req.clone());
64-
let response = ControlResponse {
65-
pitot_version: "1".to_string(),
66-
control_response_type: "control.response".to_string(),
67-
controller_id: controller_id.to_string(),
68-
action_id: req.action_id,
69-
outcome: result.outcome,
70-
message: result.message,
71-
};
72-
if let Ok(json) = serde_json::to_string(&response) {
73-
println!("{}", json);
70+
};
71+
if line_str.trim().is_empty() {
72+
continue;
73+
}
74+
match serde_json::from_str::<ControlRequested>(&line_str) {
75+
Ok(req) => {
76+
let result = handler(req.clone());
77+
let response = ControlResponse {
78+
pitot_version: "1".to_string(),
79+
control_response_type: "control.response".to_string(),
80+
controller_id: controller_id.to_string(),
81+
action_id: req.action_id,
82+
outcome: result.outcome,
83+
message: result.message,
84+
};
85+
match serde_json::to_string(&response) {
86+
Ok(json) => {
87+
let mut handle = stdout.lock();
88+
// stdout is block-buffered when piped (the normal case
89+
// behind a runtime), so flush every response to keep the
90+
// controller responsive.
91+
if writeln!(handle, "{}", json).is_err() || handle.flush().is_err() {
92+
eprintln!("Pitot Controller error: failed to write response");
93+
}
94+
}
95+
Err(err) => {
96+
eprintln!("Pitot Controller error: {}", err);
7497
}
7598
}
76-
Err(err) => {
77-
eprintln!("Pitot Controller error: {}", err);
78-
}
99+
}
100+
Err(err) => {
101+
eprintln!("Pitot Controller error: {}", err);
79102
}
80103
}
81104
}

labs/15-pitot/pitot-distribution/sdk/tests/go_test.go

Lines changed: 0 additions & 13 deletions
This file was deleted.

labs/15-pitot/pitot-distribution/sdk/tests/go_test_runner.go

Lines changed: 0 additions & 13 deletions
This file was deleted.

labs/15-pitot/pitot-distribution/sdk/tests/python_test.py

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)