Initial commit: 42 UN CLI implementations

Unsandbox CLI implementations in 42 programming languages for the
permacomputer project. Public domain software for code execution
across all ecosystems.

Languages: Python, JavaScript, Ruby, Go, Rust, C, C++, Java, Kotlin,
C#, F#, Haskell, OCaml, Clojure, Scheme, Common Lisp, Erlang, Elixir,
D, Nim, Zig, V, Dart, Groovy, Scala, Julia, R, Crystal, Fortran,
COBOL, Prolog, Forth, Tcl, Raku, Lua, PHP, Perl, Bash, TypeScript,
Objective-C, PowerShell, AWK

Includes test suites and service lifecycle tests.
This commit is contained in:
Russell Ballestrini 2025-12-23 09:57:08 -05:00
commit 2b35819393
143 changed files with 30022 additions and 0 deletions

541
un.php Normal file
View file

@ -0,0 +1,541 @@
# PUBLIC DOMAIN - NO LICENSE, NO WARRANTY
#
# This is free public domain software for the public good of a permacomputer hosted
# at permacomputer.com - an always-on computer by the people, for the people. One
# which is durable, easy to repair, and distributed like tap water for machine
# learning intelligence.
#
# The permacomputer is community-owned infrastructure optimized around four values:
#
# TRUTH - Source code must be open source & freely distributed
# FREEDOM - Voluntary participation without corporate control
# HARMONY - Systems operating with minimal waste that self-renew
# LOVE - Individual rights protected while fostering cooperation
#
# This software contributes to that vision by enabling code execution across 42+
# programming languages through a unified interface, accessible to all. Code is
# seeds to sprout on any abandoned technology.
#
# Learn more: https://www.permacomputer.com
#
# Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
# software, either in source code form or as a compiled binary, for any purpose,
# commercial or non-commercial, and by any means.
#
# NO WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
#
# That said, our permacomputer's digital membrane stratum continuously runs unit,
# integration, and functional tests on all of it's own software - with our
# permacomputer monitoring itself, repairing itself, with minimal human in the
# loop guidance. Our agents do their best.
#
# Copyright 2025 TimeHexOn & foxhop & russell@unturf
# https://www.timehexon.com
# https://www.foxhop.net
# https://www.unturf.com/software
#!/usr/bin/env php
<?php
/**
* un.php - Unsandbox CLI Client (PHP Implementation)
*
* Full-featured CLI matching un.c capabilities:
* - Execute code with env vars, input files, artifacts
* - Interactive sessions with shell/REPL support
* - Persistent services with domains and ports
*
* Usage:
* un.php [options] <source_file>
* un.php session [options]
* un.php service [options]
*
* Requires: UNSANDBOX_API_KEY environment variable
*/
const API_BASE = 'https://api.unsandbox.com';
const BLUE = "\033[34m";
const RED = "\033[31m";
const GREEN = "\033[32m";
const YELLOW = "\033[33m";
const RESET = "\033[0m";
const EXT_MAP = [
'.py' => 'python', '.js' => 'javascript', '.ts' => 'typescript',
'.rb' => 'ruby', '.php' => 'php', '.pl' => 'perl', '.lua' => 'lua',
'.sh' => 'bash', '.go' => 'go', '.rs' => 'rust', '.c' => 'c',
'.cpp' => 'cpp', '.cc' => 'cpp', '.cxx' => 'cpp',
'.java' => 'java', '.kt' => 'kotlin', '.cs' => 'csharp', '.fs' => 'fsharp',
'.hs' => 'haskell', '.ml' => 'ocaml', '.clj' => 'clojure', '.scm' => 'scheme',
'.lisp' => 'commonlisp', '.erl' => 'erlang', '.ex' => 'elixir', '.exs' => 'elixir',
'.jl' => 'julia', '.r' => 'r', '.R' => 'r', '.cr' => 'crystal',
'.d' => 'd', '.nim' => 'nim', '.zig' => 'zig', '.v' => 'v',
'.dart' => 'dart', '.groovy' => 'groovy', '.scala' => 'scala',
'.f90' => 'fortran', '.f95' => 'fortran', '.cob' => 'cobol',
'.pro' => 'prolog', '.forth' => 'forth', '.4th' => 'forth',
'.tcl' => 'tcl', '.raku' => 'raku', '.m' => 'objc'
];
function get_api_key($args_key = null) {
$key = $args_key ?: getenv('UNSANDBOX_API_KEY');
if (!$key) {
fwrite(STDERR, RED . "Error: UNSANDBOX_API_KEY not set" . RESET . "\n");
exit(1);
}
return $key;
}
function detect_language($filename) {
$ext = '.' . strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$lang = EXT_MAP[$ext] ?? null;
if (!$lang) {
$file = @fopen($filename, 'r');
if ($file) {
$first_line = fgets($file);
fclose($file);
if (str_starts_with($first_line, '#!')) {
if (str_contains($first_line, 'python')) return 'python';
if (str_contains($first_line, 'node')) return 'javascript';
if (str_contains($first_line, 'ruby')) return 'ruby';
if (str_contains($first_line, 'perl')) return 'perl';
if (str_contains($first_line, 'bash') || str_contains($first_line, '/sh')) return 'bash';
if (str_contains($first_line, 'lua')) return 'lua';
if (str_contains($first_line, 'php')) return 'php';
}
}
fwrite(STDERR, RED . "Error: Cannot detect language for $filename" . RESET . "\n");
exit(1);
}
return $lang;
}
function api_request($endpoint, $method = 'GET', $data = null, $api_key = null) {
$url = API_BASE . $endpoint;
$ch = curl_init($url);
$headers = [
'Authorization: Bearer ' . $api_key,
'Content-Type: application/json'
];
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 300
]);
if ($data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) {
fwrite(STDERR, RED . "Error: " . curl_error($ch) . RESET . "\n");
curl_close($ch);
exit(1);
}
curl_close($ch);
if ($http_code < 200 || $http_code >= 300) {
fwrite(STDERR, RED . "Error: HTTP $http_code - $response" . RESET . "\n");
exit(1);
}
return json_decode($response, true);
}
function cmd_execute($options) {
$api_key = get_api_key($options['api_key']);
if (!file_exists($options['source_file'])) {
fwrite(STDERR, RED . "Error: File not found: {$options['source_file']}" . RESET . "\n");
exit(1);
}
$code = file_get_contents($options['source_file']);
$language = detect_language($options['source_file']);
$payload = ['language' => $language, 'code' => $code];
if (!empty($options['env'])) {
$env_vars = [];
foreach ($options['env'] as $e) {
$parts = explode('=', $e, 2);
if (count($parts) === 2) {
$env_vars[$parts[0]] = $parts[1];
}
}
if (!empty($env_vars)) {
$payload['env'] = $env_vars;
}
}
if (!empty($options['files'])) {
$input_files = [];
foreach ($options['files'] as $filepath) {
if (!file_exists($filepath)) {
fwrite(STDERR, RED . "Error: Input file not found: $filepath" . RESET . "\n");
exit(1);
}
$input_files[] = [
'filename' => basename($filepath),
'content_base64' => base64_encode(file_get_contents($filepath))
];
}
$payload['input_files'] = $input_files;
}
if ($options['artifacts']) $payload['return_artifacts'] = true;
if ($options['network']) $payload['network'] = $options['network'];
if ($options['vcpu']) $payload['vcpu'] = $options['vcpu'];
$result = api_request('/execute', 'POST', $payload, $api_key);
if (!empty($result['stdout'])) {
echo BLUE . $result['stdout'] . RESET;
}
if (!empty($result['stderr'])) {
fwrite(STDERR, RED . $result['stderr'] . RESET);
}
if ($options['artifacts'] && !empty($result['artifacts'])) {
$out_dir = $options['output_dir'] ?: '.';
if (!is_dir($out_dir)) {
mkdir($out_dir, 0755, true);
}
foreach ($result['artifacts'] as $artifact) {
$filename = $artifact['filename'] ?? 'artifact';
$content = base64_decode($artifact['content_base64']);
$filepath = $out_dir . '/' . $filename;
file_put_contents($filepath, $content);
chmod($filepath, 0755);
fwrite(STDERR, GREEN . "Saved: $filepath" . RESET . "\n");
}
}
exit($result['exit_code'] ?? 0);
}
function cmd_session($options) {
$api_key = get_api_key($options['api_key']);
if ($options['list']) {
$result = api_request('/sessions', 'GET', null, $api_key);
$sessions = $result['sessions'] ?? [];
if (empty($sessions)) {
echo "No active sessions\n";
} else {
printf("%-40s %-10s %-10s %s\n", 'ID', 'Shell', 'Status', 'Created');
foreach ($sessions as $s) {
printf("%-40s %-10s %-10s %s\n",
$s['id'] ?? 'N/A', $s['shell'] ?? 'N/A',
$s['status'] ?? 'N/A', $s['created_at'] ?? 'N/A');
}
}
return;
}
if ($options['kill']) {
api_request("/sessions/{$options['kill']}", 'DELETE', null, $api_key);
echo GREEN . "Session terminated: {$options['kill']}" . RESET . "\n";
return;
}
if ($options['attach']) {
echo YELLOW . "Attaching to session {$options['attach']}..." . RESET . "\n";
echo YELLOW . "(Interactive sessions require WebSocket - use un2 for full support)" . RESET . "\n";
return;
}
$payload = ['shell' => $options['shell'] ?: 'bash'];
if ($options['network']) $payload['network'] = $options['network'];
if ($options['vcpu']) $payload['vcpu'] = $options['vcpu'];
if ($options['tmux']) $payload['persistence'] = 'tmux';
if ($options['screen']) $payload['persistence'] = 'screen';
if ($options['audit']) $payload['audit'] = true;
echo YELLOW . "Creating session..." . RESET . "\n";
$result = api_request('/sessions', 'POST', $payload, $api_key);
echo GREEN . "Session created: " . ($result['id'] ?? 'N/A') . RESET . "\n";
echo YELLOW . "(Interactive sessions require WebSocket - use un2 for full support)" . RESET . "\n";
}
function cmd_service($options) {
$api_key = get_api_key($options['api_key']);
if ($options['list']) {
$result = api_request('/services', 'GET', null, $api_key);
$services = $result['services'] ?? [];
if (empty($services)) {
echo "No services\n";
} else {
printf("%-20s %-15s %-10s %-15s %s\n", 'ID', 'Name', 'Status', 'Ports', 'Domains');
foreach ($services as $s) {
$ports = implode(',', $s['ports'] ?? []);
$domains = implode(',', $s['domains'] ?? []);
printf("%-20s %-15s %-10s %-15s %s\n",
$s['id'] ?? 'N/A', $s['name'] ?? 'N/A',
$s['status'] ?? 'N/A', $ports, $domains);
}
}
return;
}
if ($options['info']) {
$result = api_request("/services/{$options['info']}", 'GET', null, $api_key);
echo json_encode($result, JSON_PRETTY_PRINT) . "\n";
return;
}
if ($options['logs']) {
$result = api_request("/services/{$options['logs']}/logs", 'GET', null, $api_key);
echo $result['logs'] ?? '';
return;
}
if ($options['tail']) {
$result = api_request("/services/{$options['tail']}/logs?lines=9000", 'GET', null, $api_key);
echo $result['logs'] ?? '';
return;
}
if ($options['sleep']) {
api_request("/services/{$options['sleep']}/sleep", 'POST', null, $api_key);
echo GREEN . "Service sleeping: {$options['sleep']}" . RESET . "\n";
return;
}
if ($options['wake']) {
api_request("/services/{$options['wake']}/wake", 'POST', null, $api_key);
echo GREEN . "Service waking: {$options['wake']}" . RESET . "\n";
return;
}
if ($options['destroy']) {
api_request("/services/{$options['destroy']}", 'DELETE', null, $api_key);
echo GREEN . "Service destroyed: {$options['destroy']}" . RESET . "\n";
return;
}
if ($options['execute']) {
$payload = ['command' => $options['command']];
$result = api_request("/services/{$options['execute']}/execute", 'POST', $payload, $api_key);
if (!empty($result['stdout'])) echo BLUE . $result['stdout'] . RESET;
if (!empty($result['stderr'])) fwrite(STDERR, RED . $result['stderr'] . RESET);
return;
}
if ($options['name']) {
$payload = ['name' => $options['name']];
if ($options['ports']) {
$payload['ports'] = array_map('intval', explode(',', $options['ports']));
}
if ($options['domains']) {
$payload['domains'] = explode(',', $options['domains']);
}
if ($options['bootstrap']) {
if (file_exists($options['bootstrap'])) {
$payload['bootstrap'] = file_get_contents($options['bootstrap']);
} else {
$payload['bootstrap'] = $options['bootstrap'];
}
}
if ($options['network']) $payload['network'] = $options['network'];
if ($options['vcpu']) $payload['vcpu'] = $options['vcpu'];
$result = api_request('/services', 'POST', $payload, $api_key);
echo GREEN . "Service created: " . ($result['id'] ?? 'N/A') . RESET . "\n";
echo "Name: " . ($result['name'] ?? 'N/A') . "\n";
if (!empty($result['url'])) echo "URL: {$result['url']}\n";
return;
}
fwrite(STDERR, RED . "Error: Specify --name to create a service, or use --list, --info, etc." . RESET . "\n");
exit(1);
}
function main() {
global $argv;
$options = [
'command' => null,
'source_file' => null,
'env' => [],
'files' => [],
'artifacts' => false,
'output_dir' => null,
'network' => null,
'vcpu' => null,
'api_key' => null,
'shell' => null,
'list' => false,
'attach' => null,
'kill' => null,
'audit' => false,
'tmux' => false,
'screen' => false,
'name' => null,
'ports' => null,
'domains' => null,
'bootstrap' => null,
'info' => null,
'logs' => null,
'tail' => null,
'sleep' => null,
'wake' => null,
'destroy' => null,
'execute' => null,
'command' => null
];
for ($i = 1; $i < count($argv); $i++) {
$arg = $argv[$i];
switch ($arg) {
case 'session':
case 'service':
$options['command'] = $arg;
break;
case '-e':
$options['env'][] = $argv[++$i];
break;
case '-f':
$options['files'][] = $argv[++$i];
break;
case '-a':
$options['artifacts'] = true;
break;
case '-o':
$options['output_dir'] = $argv[++$i];
break;
case '-n':
$options['network'] = $argv[++$i];
break;
case '-v':
$options['vcpu'] = (int)$argv[++$i];
break;
case '-k':
$options['api_key'] = $argv[++$i];
break;
case '-s':
case '--shell':
$options['shell'] = $argv[++$i];
break;
case '-l':
case '--list':
$options['list'] = true;
break;
case '--attach':
$options['attach'] = $argv[++$i];
break;
case '--kill':
$options['kill'] = $argv[++$i];
break;
case '--audit':
$options['audit'] = true;
break;
case '--tmux':
$options['tmux'] = true;
break;
case '--screen':
$options['screen'] = true;
break;
case '--name':
$options['name'] = $argv[++$i];
break;
case '--ports':
$options['ports'] = $argv[++$i];
break;
case '--domains':
$options['domains'] = $argv[++$i];
break;
case '--bootstrap':
$options['bootstrap'] = $argv[++$i];
break;
case '--info':
$options['info'] = $argv[++$i];
break;
case '--logs':
$options['logs'] = $argv[++$i];
break;
case '--tail':
$options['tail'] = $argv[++$i];
break;
case '--sleep':
$options['sleep'] = $argv[++$i];
break;
case '--wake':
$options['wake'] = $argv[++$i];
break;
case '--destroy':
$options['destroy'] = $argv[++$i];
break;
case '--execute':
$options['execute'] = $argv[++$i];
break;
case '--command':
$options['command'] = $argv[++$i];
break;
default:
if (!str_starts_with($arg, '-')) {
$options['source_file'] = $arg;
}
break;
}
}
if ($options['command'] === 'session') {
cmd_session($options);
} elseif ($options['command'] === 'service') {
cmd_service($options);
} elseif ($options['source_file']) {
cmd_execute($options);
} else {
echo "Unsandbox CLI - Execute code in secure sandboxes
Usage:
{$argv[0]} [options] <source_file>
{$argv[0]} session [options]
{$argv[0]} service [options]
Execute options:
-e KEY=VALUE Environment variable (multiple allowed)
-f FILE Input file (multiple allowed)
-a Return artifacts
-o DIR Output directory for artifacts
-n MODE Network mode (zerotrust|semitrusted)
-v N vCPU count (1-8)
-k KEY API key
Session options:
-s, --shell NAME Shell/REPL (default: bash)
-l, --list List sessions
--attach ID Attach to session
--kill ID Terminate session
--audit Record session
--tmux Enable tmux persistence
--screen Enable screen persistence
Service options:
--name NAME Service name
--ports PORTS Comma-separated ports
--domains DOMAINS Custom domains
--bootstrap CMD Bootstrap command/file
-l, --list List services
--info ID Get service details
--logs ID Get all logs
--tail ID Get last 9000 lines
--sleep ID Freeze service
--wake ID Unfreeze service
--destroy ID Destroy service
--execute ID Execute command in service
--command CMD Command to execute (with --execute)
";
exit(1);
}
}
main();