-
-
Notifications
You must be signed in to change notification settings - Fork 2
USOP
USOP is MAINFRAME's standard for structured output that AI agents can reliably parse and act upon.
- Overview
- Why USOP?
- Enabling USOP
- Output Envelope Format
- Success Functions
- Error Functions
- Typed Output Functions
- Error Codes
- Integration Patterns
- Best Practices
USOP (Universal Structured Output Protocol) provides a consistent JSON envelope format for all MAINFRAME operations. This enables AI agents to:
- Parse operation results reliably
- Understand success/failure states unambiguously
- Access error context for self-correction
- Process typed data without guessing formats
Traditional bash output is hostile to AI agents:
# What does this mean?
$ some_command
Error: something went wrong
# Is this success or failure?
$ another_command
Done.
# How do I parse this?
$ list_items
Item 1
Item 2
Item 3AI agents must:
- Guess output formats
- Parse free-form text
- Interpret ambiguous messages
- Handle inconsistent error reporting
USOP provides consistent JSON envelopes:
# Clear success
{"ok":true,"data":"operation completed"}
# Clear failure with context
{"ok":false,"error":{"code":"E_NOT_FOUND","msg":"File not found","suggestion":"Check path exists"}}
# Typed data
{"ok":true,"data":42}
{"ok":true,"data":["item1","item2","item3"]}export MAINFRAME_OUTPUT=json
source "$MAINFRAME_ROOT/lib/common.sh"#!/bin/bash
source "$MAINFRAME_ROOT/lib/common.sh"
export MAINFRAME_OUTPUT=jsonif [[ "${MAINFRAME_OUTPUT:-text}" == "json" ]]; then
# USOP mode
else
# Plain text mode
fi{
"ok": true,
"data": "<result>",
"hint": "<optional hint for next action>",
"meta": {
"elapsed_ms": 42,
"timestamp": "2024-01-15T10:30:00"
}
}{
"ok": false,
"error": {
"code": "E_ERROR_CODE",
"msg": "Human-readable message",
"suggestion": "How to fix this",
"details": {
"<additional context>"
}
},
"meta": {
"elapsed_ms": 42,
"timestamp": "2024-01-15T10:30:00"
}
}Basic success output.
output_success "operation completed"
# {"ok":true,"data":"operation completed"}
output_success "file created" "verify_file"
# {"ok":true,"data":"file created","hint":"verify_file"}Output with arbitrary data.
output_data "result value"
# {"ok":true,"data":"result value"}Simple completion marker.
output_done
# {"ok":true,"data":"done"}Standard error output.
output_error "E_NOT_FOUND" "Config file missing"
# {"ok":false,"error":{"code":"E_NOT_FOUND","msg":"Config file missing"}}
output_error "E_NOT_FOUND" "Config file missing" "Run 'init' first"
# {"ok":false,"error":{"code":"E_NOT_FOUND","msg":"Config file missing","suggestion":"Run 'init' first"}}Error with additional details.
output_error_details "E_INVALID_INPUT" "Validation failed" "Check input format" \
"field=email" "value=not-an-email" "expected=valid email address"
# {"ok":false,"error":{"code":"E_INVALID_INPUT","msg":"Validation failed","suggestion":"Check input format","details":{"field":"email","value":"not-an-email","expected":"valid email address"}}}Specialized error functions:
usop_error_not_found "config.json" "/app/config"
# {"ok":false,"error":{"code":"E_NOT_FOUND","msg":"config.json not found","suggestion":"Check path: /app/config"}}
usop_error_permission "write" "/etc/hosts"
# {"ok":false,"error":{"code":"E_PERMISSION","msg":"Permission denied for write","suggestion":"Check permissions on /etc/hosts"}}
usop_error_invalid_input "email" "not-valid" "user@example.com"
# {"ok":false,"error":{"code":"E_INVALID_INPUT","msg":"Invalid email: not-valid","suggestion":"Expected format: user@example.com"}}
usop_error_command_failed "git push" 128 "rejected: non-fast-forward"
# {"ok":false,"error":{"code":"E_COMMAND_FAILED","msg":"Command failed: git push","exit_code":128,"stderr":"rejected: non-fast-forward"}}Integer output.
output_int 42
# {"ok":true,"data":42}
output_int -1
# {"ok":true,"data":-1}Float output.
output_float 3.14159
# {"ok":true,"data":3.14159}Boolean output.
output_bool true
# {"ok":true,"data":true}
output_bool false
# {"ok":true,"data":false}Null output.
output_null
# {"ok":true,"data":null}Array output.
output_list "item1" "item2" "item3"
# {"ok":true,"data":["item1","item2","item3"]}Pre-formed JSON object output.
output_json_object '{"id":1,"name":"test"}'
# {"ok":true,"data":{"id":1,"name":"test"}}Pre-formed JSON array output.
output_json_array '["a","b","c"]'
# {"ok":true,"data":["a","b","c"]}Standard error codes for consistent handling:
| Code | Meaning | Typical Cause | AI Agent Response |
|---|---|---|---|
E_NOT_FOUND |
Resource not found | File/path doesn't exist | Check path, create if needed |
E_PERMISSION |
Permission denied | Insufficient permissions | Fix permissions or use sudo |
E_INVALID_INPUT |
Invalid input data | Validation failed | Correct input and retry |
E_FORBIDDEN |
Operation not allowed | Security restriction | Use different approach |
E_TIMEOUT |
Operation timed out | Slow operation | Retry with longer timeout |
E_CONFLICT |
Resource conflict | Concurrent modification | Resolve conflict, retry |
E_ALREADY_EXISTS |
Resource exists | Duplicate creation | Skip or update instead |
E_DEPENDENCY |
Missing dependency | Required tool missing | Install dependency |
E_NETWORK |
Network error | Connection failed | Check network, retry |
E_COMMAND_FAILED |
Command failed | Non-zero exit | Check stderr, fix command |
E_INTERNAL |
Internal error | Bug in code | Report issue |
import subprocess
import json
def run_mainframe(cmd: str) -> dict:
"""Execute MAINFRAME command and parse result."""
env = {"MAINFRAME_OUTPUT": "json", **os.environ}
result = subprocess.run(
["bash", "-c", f"source $MAINFRAME_ROOT/lib/common.sh && {cmd}"],
capture_output=True,
text=True,
env=env
)
return json.loads(result.stdout)
# Usage
result = run_mainframe('json_object "name=test"')
if result["ok"]:
print(f"Data: {result['data']}")
else:
print(f"Error: {result['error']['msg']}")const { execSync } = require('child_process');
function runMainframe(cmd) {
const result = execSync(
`source $MAINFRAME_ROOT/lib/common.sh && ${cmd}`,
{
shell: '/bin/bash',
env: { ...process.env, MAINFRAME_OUTPUT: 'json' }
}
);
return JSON.parse(result.toString());
}
// Usage
const result = runMainframe('json_object "name=test"');
if (result.ok) {
console.log('Data:', result.data);
} else {
console.log('Error:', result.error.msg);
}You have access to MAINFRAME with USOP enabled.
All commands return JSON envelopes:
- Success: {"ok":true,"data":<result>}
- Failure: {"ok":false,"error":{"code":"E_*","msg":"...","suggestion":"..."}}
Always check the "ok" field before using data.
Use the "suggestion" field to fix errors.
result=$(some_operation)
if [[ $(echo "$result" | jq -r '.ok') == "true" ]]; then
data=$(echo "$result" | jq -r '.data')
else
error=$(echo "$result" | jq -r '.error.msg')
suggestion=$(echo "$result" | jq -r '.error.suggestion')
fi# GOOD: Specific code
output_error "E_NOT_FOUND" "Config file missing"
# BAD: Generic code
output_error "E_ERROR" "Something went wrong"# GOOD: Actionable
output_error "E_PERMISSION" "Cannot write to /etc/hosts" "Use sudo or run as root"
# BAD: Not helpful
output_error "E_PERMISSION" "Permission denied" "Fix permissions"# GOOD: Context included
output_error_details "E_INVALID_INPUT" "Email validation failed" "Use valid email" \
"field=email" "value=$input" "pattern=^[^@]+@[^@]+$"
# BAD: No context
output_error "E_INVALID_INPUT" "Invalid"# GOOD: Type preserved
output_int 42
output_bool true
# BAD: Type lost
output_success "42"
output_success "true"When MAINFRAME_OUTPUT is not json, functions output plain text:
unset MAINFRAME_OUTPUT
output_success "done"
# Output: done (plain text)
output_error "E_NOT_FOUND" "File not found"
# Output: ERROR [E_NOT_FOUND]: File not foundThis ensures scripts work in both interactive and automated contexts.
MAINFRAME · The AI-Native Bash Runtime
MAINFRAME · The AI-Native Bash Runtime
2,000+ Functions · Zero Dependencies · Safe by Default
GitHub · Discussions · Issues
"Knowing Your Shell is half the battle."