Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,631 changes: 2,631 additions & 0 deletions coding/Cargo.lock

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions coding/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[workspace]

[package]
name = "iii-coding"
version = "0.1.0"
edition = "2021"
publish = false

[[bin]]
name = "iii-coding"
path = "src/main.rs"

[dependencies]
iii-sdk = { version = "0.11.0-next.9", features = ["otel"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "signal", "process"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
anyhow = "1"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
clap = { version = "4", features = ["derive"] }
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1", features = ["v4"] }
61 changes: 61 additions & 0 deletions coding/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# iii-coding

Instead of reading docs and writing boilerplate, describe what you want: "create a function that processes payments and expose it on POST /payments." iii-coding scaffolds the complete worker project (Cargo.toml, main.rs, function handlers, trigger wiring), executes code in sandboxes with timeouts, runs tests, and prepares deployment. It generates real, compilable iii worker code for Rust, TypeScript, or Python — following the exact same patterns as every worker in this repo.

**Plug and play:** Build with `cargo build --release`, then run `./target/release/iii-coding --url ws://your-engine:49134`. It registers 6 functions. Call `coding::scaffold` with a worker name, language, and function descriptions to generate a complete project. Call `coding::execute` to run code safely in a subprocess with timeout.

## Functions

| Function ID | Description |
|---|---|
| `coding::scaffold` | Scaffold a complete iii worker project from a definition |
| `coding::generate_function` | Generate a single function handler file |
| `coding::generate_trigger` | Generate trigger registration code for a function |
| `coding::execute` | Execute code in a subprocess with timeout |
| `coding::test` | Run tests for a scaffolded worker or inline code |
| `coding::deploy` | Return worker files and deployment instructions |

## iii Primitives Used

- **State** -- scaffolded worker definitions, generated function code, deployment records
- **HTTP** -- all functions exposed as POST endpoints

## Prerequisites

- Rust 1.75+
- Running iii engine on `ws://127.0.0.1:49134`

## Build

```bash
cargo build --release
```

## Usage

```bash
./target/release/iii-coding --url ws://127.0.0.1:49134 --config ./config.yaml
```

```
Options:
--config <PATH> Path to config.yaml [default: ./config.yaml]
--url <URL> WebSocket URL of the iii engine [default: ws://127.0.0.1:49134]
--manifest Output module manifest as JSON and exit
-h, --help Print help
```

## Configuration

```yaml
workspace_dir: "/tmp/iii-coding-workspace" # directory for scaffolded projects
supported_languages: ["rust", "typescript", "python"] # languages for code generation
execute_timeout_ms: 30000 # subprocess execution timeout
max_file_size_kb: 256 # max generated file size
```

## Tests

```bash
cargo test
```
185 changes: 185 additions & 0 deletions coding/SPEC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# iii-coding Worker

Worker that scaffolds new iii workers, generates functions and triggers, executes code in sandboxes, runs tests, and deploys -- all using iii primitives (worker, function, trigger).

## Functions

### coding::scaffold
Scaffolds a complete iii worker project from a definition.

**Input:**
```json
{
"name": "my-worker",
"language": "rust",
"functions": [
{ "id": "my-worker::greet", "description": "Greet a user", "request_format": {}, "response_format": {} }
],
"triggers": [
{ "trigger_type": "http", "function_id": "my-worker::greet", "config": { "api_path": "my-worker/greet", "http_method": "POST" } }
]
}
```

**Output:**
```json
{
"worker_id": "my-worker_a1b2",
"files": [{ "path": "src/main.rs", "content": "...", "language": "rust" }],
"function_count": 1,
"trigger_count": 1
}
```

### coding::generate_function
Generates a single function handler file. Optionally adds it to an existing scaffolded worker.

**Input:**
```json
{
"worker_id": "my-worker_a1b2",
"language": "rust",
"id": "my-worker::compute",
"description": "Run a computation",
"request_format": {},
"response_format": {}
}
```

**Output:**
```json
{
"function_id": "fn_my_worker_compute_c3d4",
"file_path": "src/functions/compute.rs",
"content": "...",
"language": "rust"
}
```

### coding::generate_trigger
Generates trigger registration code for a function.

**Input:**
```json
{
"function_id": "my-worker::greet",
"trigger_type": "http",
"config": { "api_path": "my-worker/greet", "http_method": "POST" },
"language": "rust"
}
```

**Output:**
```json
{
"trigger_type": "http",
"function_id": "my-worker::greet",
"registration_code": "iii.register_trigger(...);",
"config": { "api_path": "my-worker/greet", "http_method": "POST" }
}
```

### coding::execute
Executes code in a subprocess with timeout.

**Input:**
```json
{
"code": "fn main() { println!(\"hello\"); }",
"language": "rust",
"input": {},
"timeout_ms": 10000
}
```

**Output:**
```json
{
"success": true,
"stdout": "hello\n",
"stderr": "",
"exit_code": 0,
"duration_ms": 1234
}
```

### coding::test
Runs tests for a scaffolded worker or inline code.

**Input (worker):**
```json
{ "worker_id": "my-worker_a1b2" }
```

**Input (inline):**
```json
{
"code": "pub fn add(a: i32, b: i32) -> i32 { a + b }",
"language": "rust",
"test_code": " #[test]\n fn test_add() { assert_eq!(add(1, 2), 3); }"
}
```

**Output:**
```json
{
"passed": true,
"total": 0,
"passed_count": 0,
"failed_count": 0,
"output": "..."
}
```

### coding::deploy
Returns worker files and deployment instructions.

**Input:**
```json
{ "worker_id": "my-worker_a1b2" }
```

**Output:**
```json
{
"deployed": true,
"worker_id": "my-worker_a1b2",
"deployment_id": "deploy_my-worker_a1b2_e5f6",
"files": [...],
"instructions": "1. cd into the worker directory\n2. Run: cargo build --release\n..."
}
```

## HTTP Triggers

| Endpoint | Method | Function |
|---|---|---|
| `coding/scaffold` | POST | `coding::scaffold` |
| `coding/generate-function` | POST | `coding::generate_function` |
| `coding/generate-trigger` | POST | `coding::generate_trigger` |
| `coding/execute` | POST | `coding::execute` |
| `coding/test` | POST | `coding::test` |
| `coding/deploy` | POST | `coding::deploy` |

## State Scopes

| Scope | Key | Description |
|---|---|---|
| `coding:workers` | `{worker_id}` | Scaffolded worker definitions and files |
| `coding:functions` | `{function_id}` | Generated function code |
| `coding:deployments` | `{deployment_id}` | Deployment records |

## Supported Languages

- **Rust** -- Generates Cargo.toml, build.rs, src/main.rs, config.rs, manifest.rs, function handlers
- **TypeScript** -- Generates package.json, tsconfig.json, src/index.ts, function handlers
- **Python** -- Generates pyproject.toml, src/worker.py, function handlers

## Configuration

```yaml
workspace_dir: "/tmp/iii-coding-workspace"
supported_languages: ["rust", "typescript", "python"]
execute_timeout_ms: 30000
max_file_size_kb: 256
```
6 changes: 6 additions & 0 deletions coding/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fn main() {
println!(
"cargo:rustc-env=TARGET={}",
std::env::var("TARGET").unwrap()
);
}
4 changes: 4 additions & 0 deletions coding/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
workspace_dir: "/tmp/iii-coding-workspace"
supported_languages: ["rust", "typescript", "python"]
execute_timeout_ms: 30000
max_file_size_kb: 256
91 changes: 91 additions & 0 deletions coding/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use anyhow::Result;
use serde::Deserialize;

#[derive(Deserialize, Debug, Clone)]
pub struct CodingConfig {
#[serde(default = "default_workspace_dir")]
pub workspace_dir: String,
#[serde(default = "default_supported_languages")]
pub supported_languages: Vec<String>,
#[serde(default = "default_execute_timeout_ms")]
pub execute_timeout_ms: u64,
#[serde(default = "default_max_file_size_kb")]
pub max_file_size_kb: u64,
}

fn default_workspace_dir() -> String {
"/tmp/iii-coding-workspace".to_string()
}

fn default_supported_languages() -> Vec<String> {
vec![
"rust".to_string(),
"typescript".to_string(),
"python".to_string(),
]
}

fn default_execute_timeout_ms() -> u64 {
30000
}

fn default_max_file_size_kb() -> u64 {
256
}

impl Default for CodingConfig {
fn default() -> Self {
CodingConfig {
workspace_dir: default_workspace_dir(),
supported_languages: default_supported_languages(),
execute_timeout_ms: default_execute_timeout_ms(),
max_file_size_kb: default_max_file_size_kb(),
}
}
}

pub fn load_config(path: &str) -> Result<CodingConfig> {
let contents = std::fs::read_to_string(path)?;
let config: CodingConfig = serde_yaml::from_str(&contents)?;
Ok(config)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_config_defaults() {
let config: CodingConfig = serde_yaml::from_str("{}").unwrap();
assert_eq!(config.workspace_dir, "/tmp/iii-coding-workspace");
assert_eq!(config.supported_languages.len(), 3);
assert_eq!(config.execute_timeout_ms, 30000);
assert_eq!(config.max_file_size_kb, 256);
}

#[test]
fn test_config_custom() {
let yaml = r#"
workspace_dir: "/home/user/workspace"
supported_languages: ["rust"]
execute_timeout_ms: 10000
max_file_size_kb: 512
"#;
let config: CodingConfig = serde_yaml::from_str(yaml).unwrap();
assert_eq!(config.workspace_dir, "/home/user/workspace");
assert_eq!(config.supported_languages, vec!["rust"]);
assert_eq!(config.execute_timeout_ms, 10000);
assert_eq!(config.max_file_size_kb, 512);
}

#[test]
fn test_config_default_impl() {
let config = CodingConfig::default();
assert_eq!(config.workspace_dir, "/tmp/iii-coding-workspace");
assert!(config.supported_languages.contains(&"rust".to_string()));
assert!(config.supported_languages.contains(&"typescript".to_string()));
assert!(config.supported_languages.contains(&"python".to_string()));
assert_eq!(config.execute_timeout_ms, 30000);
assert_eq!(config.max_file_size_kb, 256);
}
}
Loading
Loading