Add -s/--shell flag and bash default for inline code execution

Matches un.c CLI behavior:
- If -s/--shell LANG is specified, treat argument as inline code
- If argument doesn't exist as a file, default to bash for inline execution
- Normal file execution unchanged
This commit is contained in:
Russell Ballestrini 2026-01-09 09:43:44 -05:00
parent 1cb7e2895f
commit 1b35f1099e
5 changed files with 125 additions and 33 deletions

34
un.js
View file

@ -321,14 +321,28 @@ async function cmdExecute(args) {
const { publicKey, secretKey } = getApiKeys(args.apiKey);
let code;
try {
code = fs.readFileSync(args.sourceFile, 'utf-8');
} catch (e) {
console.error(`${RED}Error: File not found: ${args.sourceFile}${RESET}`);
process.exit(1);
let language;
// Check for inline mode: -s/--shell specified, or sourceFile doesn't exist
if (args.execShell) {
// Inline mode with specified language
code = args.sourceFile;
language = args.execShell;
} else if (!fs.existsSync(args.sourceFile)) {
// File doesn't exist - treat as inline bash code
code = args.sourceFile;
language = "bash";
} else {
// Normal file execution
try {
code = fs.readFileSync(args.sourceFile, 'utf-8');
} catch (e) {
console.error(`${RED}Error: File not found: ${args.sourceFile}${RESET}`);
process.exit(1);
}
language = detectLanguage(args.sourceFile);
}
const language = detectLanguage(args.sourceFile);
const payload = { language, code };
if (args.env && args.env.length > 0) {
@ -607,6 +621,7 @@ function parseArgs(argv) {
dumpBootstrap: null,
dumpFile: null,
extend: false,
execShell: null,
};
let i = 2;
@ -638,7 +653,12 @@ function parseArgs(argv) {
args.apiKey = argv[++i];
i++;
} else if (arg === '-s' || arg === '--shell') {
args.shell = argv[++i];
// For session command, this is shell type. For execute, it's inline exec language.
if (args.command === 'session') {
args.shell = argv[++i];
} else {
args.execShell = argv[++i];
}
i++;
} else if (arg === '-l' || arg === '--list') {
args.list = true;

38
un.lua
View file

@ -242,10 +242,34 @@ local function base64_decode(data)
end))
end
local function file_exists(filename)
local file = io.open(filename, "r")
if file then
file:close()
return true
end
return false
end
local function cmd_execute(options)
local keys = get_api_keys(options.api_key)
local code = read_file(options.source_file)
local language = detect_language(options.source_file)
local code
local language
-- Check for inline mode: -s/--shell specified, or source_file doesn't exist
if options.exec_shell then
-- Inline mode with specified language
code = options.source_file
language = options.exec_shell
elseif not file_exists(options.source_file) then
-- File doesn't exist - treat as inline bash code
code = options.source_file
language = "bash"
else
-- Normal file execution
code = read_file(options.source_file)
language = detect_language(options.source_file)
end
local payload = { language = language, code = code }
@ -668,7 +692,8 @@ local function main()
command = nil,
dump_bootstrap = nil,
dump_file = nil,
extend = false
extend = false,
exec_shell = nil
}
local i = 1
@ -699,7 +724,12 @@ local function main()
options.api_key = arg[i]
elseif a == "-s" or a == "--shell" then
i = i + 1
options.shell = arg[i]
-- For session command, this is shell type. For execute, it's inline exec language.
if options.command == "session" then
options.shell = arg[i]
else
options.exec_shell = arg[i]
end
elseif a == "-l" or a == "--list" then
options.list = true
elseif a == "--attach" then

30
un.py
View file

@ -200,15 +200,28 @@ def cmd_execute(args):
"""Execute source code"""
public_key, secret_key = get_api_keys(args.api_key)
# Read source file
try:
with open(args.source_file, 'r') as f:
code = f.read()
except FileNotFoundError:
print(f"{RED}Error: File not found: {args.source_file}{RESET}", file=sys.stderr)
sys.exit(1)
# Check for inline mode: -s/--shell specified, or source_file doesn't exist
inline_mode = False
if args.exec_shell:
inline_mode = True
language = args.exec_shell
code = args.source_file # The "file" argument is actually the code
elif not os.path.exists(args.source_file):
# File doesn't exist - treat as inline bash code
inline_mode = True
language = "bash"
code = args.source_file
language = detect_language(args.source_file)
if not inline_mode:
# Read source file
try:
with open(args.source_file, 'r') as f:
code = f.read()
except FileNotFoundError:
print(f"{RED}Error: File not found: {args.source_file}{RESET}", file=sys.stderr)
sys.exit(1)
language = detect_language(args.source_file)
# Build request payload
payload = {
@ -746,6 +759,7 @@ Examples:
# Execute options (default command)
parser.add_argument("source_file", nargs="?", help="Source file to execute")
parser.add_argument("-s", "--shell", dest="exec_shell", metavar="LANG", help="Execute inline code with specified language (defaults to bash if arg is not a file)")
parser.add_argument("-e", "--env", action="append", metavar="KEY=VALUE", help="Set environment variable")
parser.add_argument("-f", "--files", action="append", metavar="FILE", help="Add input file")
parser.add_argument("-a", "--artifacts", action="store_true", help="Return artifacts")

29
un.rb
View file

@ -172,14 +172,21 @@ end
def cmd_execute(options)
keys = get_api_keys(options[:api_key])
unless File.exist?(options[:source_file])
warn "#{RED}Error: File not found: #{options[:source_file]}#{RESET}"
exit 1
# Check for inline mode: -s/--shell specified, or source_file doesn't exist
if options[:exec_shell]
# Inline mode with specified language
code = options[:source_file]
language = options[:exec_shell]
elsif !File.exist?(options[:source_file])
# File doesn't exist - treat as inline bash code
code = options[:source_file]
language = "bash"
else
# Normal file execution
code = File.read(options[:source_file])
language = detect_language(options[:source_file])
end
code = File.read(options[:source_file])
language = detect_language(options[:source_file])
payload = { language: language, code: code }
if options[:env] && !options[:env].empty?
@ -670,7 +677,8 @@ def main
dump_bootstrap: nil,
dump_file: nil,
extend: false,
bootstrap_file: nil
bootstrap_file: nil,
exec_shell: nil
}
# Manual argument parsing
@ -703,7 +711,12 @@ def main
options[:api_key] = ARGV[i]
when '-s', '--shell'
i += 1
options[:shell] = ARGV[i]
# For session command, this is shell type. For execute, it's inline exec language.
if options[:command] == 'session'
options[:shell] = ARGV[i]
else
options[:exec_shell] = ARGV[i]
end
when '-l', '--list'
options[:list] = true
when '--attach'

27
un.sh
View file

@ -215,10 +215,15 @@ cmd_execute() {
local network=""
local vcpu=""
local api_key="${UNSANDBOX_API_KEY:-}"
local exec_shell=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-s|--shell)
exec_shell="$2"
shift 2
;;
-e)
env_vars+=("$2")
shift 2
@ -258,13 +263,23 @@ cmd_execute() {
esac
done
if [[ ! -f "$source_file" ]]; then
echo -e "${RED}Error: File not found: $source_file${RESET}" >&2
exit 1
fi
local code
local language
local code=$(cat "$source_file")
local language=$(detect_language "$source_file")
# Check for inline mode: -s/--shell specified, or source_file doesn't exist
if [[ -n "$exec_shell" ]]; then
# Inline mode with specified language
code="$source_file"
language="$exec_shell"
elif [[ ! -f "$source_file" ]]; then
# File doesn't exist - treat as inline bash code
code="$source_file"
language="bash"
else
# Normal file execution
code=$(cat "$source_file")
language=$(detect_language "$source_file")
fi
# Build JSON payload
local payload=$(jq -n \