# 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!