- Added snapshot functions to SDK requirements - Updated testing guide with snapshot examples - Added snapshot checklist items for refactoring - Clarified image() function in utilities section All agents refactoring SDKs must now implement: - sessionSnapshot(session_id, opts) - serviceSnapshot(service_id, opts) - listSnapshots(opts) - restoreSnapshot(snapshot_id, opts) - deleteSnapshot(snapshot_id, opts)
384 lines
10 KiB
Markdown
384 lines
10 KiB
Markdown
# SDK Refactoring Instructions for Agents
|
|
|
|
This document explains how to refactor UN SDK implementations with proper testing and validation.
|
|
|
|
## Quick Start
|
|
|
|
### Step 1: Understand the Reference Implementations
|
|
Study these complete, working SDK implementations first:
|
|
- **Python**: `/home/fox/git/un-inception/un.py` (DONE - use as reference)
|
|
- **JavaScript**: `/home/fox/git/un-inception/un.js` (DONE - use as reference)
|
|
- **Go**: Check if exists; if incomplete, use Python/JS as pattern
|
|
|
|
### Step 2: Apply the Pattern to Your Language
|
|
Every refactored SDK needs:
|
|
|
|
1. **Library functions** (can be imported/required)
|
|
```
|
|
execute(language, code, opts)
|
|
executeAsync(language, code, opts)
|
|
run(file, opts)
|
|
runAsync(file, opts)
|
|
wait(job_id)
|
|
getJob(job_id)
|
|
cancelJob(job_id)
|
|
listJobs()
|
|
languages()
|
|
detectLanguage(filename)
|
|
image(prompt, opts)
|
|
|
|
# Snapshot functions (NEW!)
|
|
sessionSnapshot(session_id, opts)
|
|
serviceSnapshot(service_id, opts)
|
|
listSnapshots(opts)
|
|
restoreSnapshot(snapshot_id, opts)
|
|
deleteSnapshot(snapshot_id, opts)
|
|
```
|
|
|
|
2. **Credential system** (4-tier with fallback)
|
|
```
|
|
Priority: args > env vars > ~/.unsandbox/accounts.csv > ./accounts.csv
|
|
```
|
|
|
|
3. **HMAC-SHA256 request signing**
|
|
```
|
|
signature = HMAC(secret, "timestamp:METHOD:endpoint:body")
|
|
```
|
|
|
|
4. **1-hour cache** for `/languages` API endpoint
|
|
```
|
|
~/.unsandbox/languages.json with TTL check
|
|
```
|
|
|
|
5. **CLI functionality** (preserve original behavior)
|
|
```
|
|
Command-line interface for executing code files
|
|
```
|
|
|
|
6. **Proper docstrings** (language-appropriate style)
|
|
```
|
|
Python: """ """
|
|
JavaScript: /** */
|
|
Go: // line comments
|
|
Ruby: # line comments
|
|
PHP: /** */
|
|
Etc.
|
|
```
|
|
|
|
7. **Complete license header** (permacomputer public domain)
|
|
```
|
|
Must include full 35+ line license notice
|
|
```
|
|
|
|
### Step 3: Test Your Work
|
|
|
|
Use the provided test framework to validate:
|
|
|
|
```bash
|
|
# Run unit tests (no API key required)
|
|
cd /home/fox/git/un-inception
|
|
python3 tests/test_sdk_library.py --languages YOUR_LANGUAGE
|
|
|
|
# Or run the master test runner
|
|
./tests/run_sdk_tests.sh --languages YOUR_LANGUAGE
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
SDK LIBRARY TEST SUMMARY
|
|
========================================
|
|
✓ your_language 3/ 4 passed (1 skipped)
|
|
========================================
|
|
Total: 3 passed, 0 failed, 1 skipped
|
|
```
|
|
|
|
### Step 4: Validate Examples Still Work
|
|
|
|
After refactoring, ensure the example files execute properly:
|
|
|
|
```bash
|
|
# Set credentials (if available)
|
|
export UNSANDBOX_API_KEY='your-test-key'
|
|
|
|
# Test CLI still works
|
|
python un.py examples/hello.py
|
|
node un.js examples/hello.js
|
|
ruby un.rb examples/hello.rb
|
|
# etc.
|
|
|
|
# Test library imports work
|
|
python3 -c "import un; result = un.execute('python', 'print(1)')"
|
|
node -e "const un = require('./un.js'); un.execute('javascript', 'console.log(1)')"
|
|
```
|
|
|
|
### Step 5: Update Documentation Examples
|
|
|
|
Once your SDK is refactored, update the example files:
|
|
|
|
```
|
|
/home/fox/git/unsandbox.com/priv/static/docs/examples/{language}/execute.txt
|
|
/home/fox/git/unsandbox.com/priv/static/docs/examples/{language}/execute_async.txt
|
|
```
|
|
|
|
Pattern to follow (from Python/JavaScript):
|
|
- Show SDK library import/require
|
|
- Demonstrate credential setup
|
|
- Show execute() call with proper options
|
|
- Show executeAsync() + wait() pattern
|
|
- Include error handling
|
|
|
|
### Step 6: Commit Your Work
|
|
|
|
```bash
|
|
git add un.{ext} tests/test_un_{lang}.{ext}
|
|
git commit -m "Refactor {Language} SDK: add library exports + HMAC auth + caching
|
|
|
|
- Implement execute, executeAsync, wait, getJob, cancelJob, listJobs
|
|
- Add 4-tier credential system (args > env > home > local)
|
|
- Add 1-hour cache for languages list in ~/.unsandbox/
|
|
- Implement HMAC-SHA256 request signing
|
|
- Preserve original CLI functionality
|
|
- Proper docstrings for {Language}
|
|
- Full test coverage with unit + integration tests"
|
|
```
|
|
|
|
## Key Implementation Details
|
|
|
|
### Credential Loading (4-tier system)
|
|
|
|
Priority order (stop at first match):
|
|
|
|
1. **Function arguments** - explicit parameters to execute()
|
|
```python
|
|
execute('python', code, public_key='...', secret_key='...')
|
|
```
|
|
|
|
2. **Environment variables**
|
|
```bash
|
|
export UNSANDBOX_PUBLIC_KEY='unsb-pk-xxxx'
|
|
export UNSANDBOX_SECRET_KEY='unsb-sk-xxxx'
|
|
```
|
|
|
|
3. **Home directory** (default location)
|
|
```
|
|
~/.unsandbox/accounts.csv
|
|
Format: public_key,secret_key (one per line)
|
|
```
|
|
|
|
4. **Local directory** (current working dir)
|
|
```
|
|
./accounts.csv
|
|
Format: public_key,secret_key (one per line)
|
|
```
|
|
|
|
### HMAC Signature Format
|
|
|
|
Every API request must include:
|
|
|
|
```
|
|
Authorization: Bearer {public_key}
|
|
X-Timestamp: {unix_timestamp}
|
|
X-Signature: {hmac_sha256}
|
|
```
|
|
|
|
Where signature is:
|
|
```
|
|
HMAC-SHA256(
|
|
secret_key,
|
|
"{timestamp}:{METHOD}:{endpoint}:{json_body}"
|
|
)
|
|
```
|
|
|
|
Example with Python:
|
|
```python
|
|
import hmac
|
|
import hashlib
|
|
import json
|
|
import time
|
|
|
|
secret = 'unsb-sk-...'
|
|
timestamp = str(int(time.time()))
|
|
method = 'POST'
|
|
endpoint = '/execute'
|
|
body = json.dumps({'language': 'python', 'code': 'print(1)'})
|
|
|
|
message = f"{timestamp}:{method}:{endpoint}:{body}"
|
|
signature = hmac.new(
|
|
secret.encode(),
|
|
message.encode(),
|
|
hashlib.sha256
|
|
).hexdigest()
|
|
```
|
|
|
|
### Languages Cache (1-hour TTL)
|
|
|
|
Implement caching to avoid repeated API calls:
|
|
|
|
1. **Cache location**: `~/.unsandbox/languages.json`
|
|
2. **TTL**: 3600 seconds (1 hour)
|
|
3. **Check logic**:
|
|
- If file exists AND age < TTL → return cached
|
|
- Otherwise → fetch from API AND update cache
|
|
|
|
Example with Python:
|
|
```python
|
|
from pathlib import Path
|
|
import json
|
|
import time
|
|
|
|
def languages(cache_ttl=3600):
|
|
cache_path = Path.home() / '.unsandbox' / 'languages.json'
|
|
|
|
# Check cache
|
|
if cache_path.exists():
|
|
age = time.time() - cache_path.stat().st_mtime
|
|
if age < cache_ttl:
|
|
return json.loads(cache_path.read_text())
|
|
|
|
# Fetch from API
|
|
result = api_request('GET', '/languages')
|
|
langs = result['languages']
|
|
|
|
# Update cache
|
|
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
|
cache_path.write_text(json.dumps(langs))
|
|
|
|
return langs
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Exception Hierarchy
|
|
Every SDK should define:
|
|
```python
|
|
class UnsandboxError(Exception): pass
|
|
class AuthenticationError(UnsandboxError): pass
|
|
class ExecutionError(UnsandboxError): pass
|
|
class APIError(UnsandboxError): pass
|
|
class TimeoutError(UnsandboxError): pass
|
|
```
|
|
|
|
### Exponential Backoff Polling
|
|
When waiting for async jobs:
|
|
```
|
|
Initial delays (ms): [300, 450, 700, 900, 650, 1600, 2000, ...]
|
|
Pattern: Increases generally but with variation to avoid thundering herd
|
|
Max polls: Usually around 120 (2+ hours for patience)
|
|
```
|
|
|
|
### CLI Entry Point
|
|
Preserve CLI functionality:
|
|
```python
|
|
# Python: at end of file
|
|
if __name__ == '__main__':
|
|
import sys
|
|
from pathlib import Path
|
|
# Parse args, call execute/run, handle errors
|
|
```
|
|
|
|
```javascript
|
|
// JavaScript: at end of file
|
|
if (require.main === module) {
|
|
cliMain().catch(e => {
|
|
console.error(e.message);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
```
|
|
|
|
```go
|
|
// Go: in main()
|
|
if len(os.Args) < 2 {
|
|
// Show usage
|
|
}
|
|
// Parse args, call Execute/Run, handle errors
|
|
```
|
|
|
|
## Testing Checklist
|
|
|
|
Before committing, verify ALL of these:
|
|
|
|
- [ ] **Unit tests pass** - Library functions work
|
|
- [ ] **Library functions exist** - execute, executeAsync, wait, etc.
|
|
- [ ] **Credentials load** - All 4 sources (args, env, home, local)
|
|
- [ ] **HMAC signature** - Correct format and length (64 hex chars)
|
|
- [ ] **Cache works** - languages.json created and used
|
|
- [ ] **CLI still works** - Command-line execution preserved
|
|
- [ ] **Error handling** - Appropriate exceptions for errors
|
|
- [ ] **Docstrings** - Language-appropriate comment style
|
|
- [ ] **License header** - Full permacomputer notice at top
|
|
- [ ] **No hardcoded credentials** - All removed
|
|
- [ ] **Examples updated** - execute.txt and execute_async.txt
|
|
- [ ] **Git status clean** - All files committed
|
|
|
|
## Troubleshooting
|
|
|
|
### Problem: "Cannot import/require un module"
|
|
**Solution:**
|
|
1. Check file is in correct location: `/home/fox/git/un-inception/un.{ext}`
|
|
2. Check filename matches language (un.py, un.js, un.go, etc.)
|
|
3. Check for syntax errors: try running file directly
|
|
|
|
### Problem: "HMAC signature invalid"
|
|
**Solution:**
|
|
1. Verify message format: `"{timestamp}:{METHOD}:{endpoint}:{body}"`
|
|
2. Verify secret key encoding: bytes, not string
|
|
3. Verify hexdigest: should be 64 lowercase hex characters
|
|
4. Verify HMAC algorithm: must be SHA256, not SHA1 or other
|
|
|
|
### Problem: "Credentials not found"
|
|
**Solution:**
|
|
1. Check env vars: `echo $UNSANDBOX_PUBLIC_KEY`
|
|
2. Check files exist: `ls ~/.unsandbox/accounts.csv`
|
|
3. Check file format: one credential pair per line
|
|
4. Check loading order: function args, env, home, local
|
|
|
|
### Problem: "Cache not working"
|
|
**Solution:**
|
|
1. Check directory exists: `mkdir -p ~/.unsandbox/`
|
|
2. Check file created: `ls -la ~/.unsandbox/languages.json`
|
|
3. Check TTL logic: age < 3600 seconds
|
|
4. Check mtime: `stat ~/.unsandbox/languages.json`
|
|
|
|
## Examples of Refactored SDKs
|
|
|
|
To understand the pattern better, study:
|
|
|
|
1. **Python (un.py)** - Complete, well-commented implementation
|
|
- Shows all library functions
|
|
- Demonstrates credential system
|
|
- Shows HMAC signing
|
|
- Shows caching
|
|
- Preserves CLI
|
|
|
|
2. **JavaScript (un.js)** - Node.js implementation
|
|
- Shows async/await pattern
|
|
- Shows Client class wrapper
|
|
- Shows error handling
|
|
- Shows function validation
|
|
|
|
3. **Go** (partially done) - if available
|
|
- Shows goroutine patterns
|
|
- Shows error handling
|
|
- Shows HTTP client usage
|
|
|
|
## Questions or Blockers?
|
|
|
|
If you're stuck:
|
|
|
|
1. **Check the guide**: `/home/fox/git/un-inception/tests/SDK_TESTING_GUIDE.md`
|
|
2. **Review Python SDK**: `un.py` is the canonical reference
|
|
3. **Look at examples**: `priv/static/docs/examples/python/` and `javascript/`
|
|
4. **Run tests**: `python3 tests/test_sdk_library.py --languages YOUR_LANGUAGE`
|
|
5. **Check git history**: `git log --oneline un.py` to see refactoring commits
|
|
|
|
## Final Notes
|
|
|
|
- **Don't rush** - Quality over speed. Tests will catch errors.
|
|
- **Test frequently** - Run tests after each function you implement
|
|
- **Copy patterns** - Use Python/JavaScript as templates exactly
|
|
- **Preserve functionality** - Original CLI must still work
|
|
- **Document clearly** - Use language-appropriate docstrings
|
|
- **Commit atomically** - Each language in separate commit
|
|
|
|
Good luck! The test framework is there to help you validate your work. Use it!
|