Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions profiles/profiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
"ANTHROPIC_API_KEY"
]
]
},
"local": {
"description": "Local tools profile (no external env required)",
"env_files": [],
"required_env": []
}
}
}
6 changes: 6 additions & 0 deletions scripts/adapters/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@
"default_profile": "anthropic",
"command_env": "PCODER_CLAUDE_CMD",
"candidate_commands": ["claude"]
},
"pwsh": {
"display_name": "PowerShell",
"default_profile": "local",
"command_env": "PCODER_PWSH_CMD",
"candidate_commands": ["pwsh", "powershell"]
}
}
49 changes: 47 additions & 2 deletions scripts/pcoder.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -743,11 +743,15 @@ function runInLinuxPortableVm(options) {
toolArgs,
noSyncBack,
skipProjectSync,
authMode
authMode,
settings
} = options;

// On non-Windows hosts, linux-portable mode uses portable host-native execution
// with isolated auth state instead of a VM. This provides portability without
// requiring Docker/Podman.
if (process.platform !== 'win32') {
fail('linux-portable mode is currently implemented for Windows hosts only.');
return runPortableHostNative(options);
}

loadJsonSafe(vmManifestPath, 'vm manifest');
Expand Down Expand Up @@ -845,6 +849,47 @@ function runInLinuxPortableVm(options) {
process.exitCode = typeof runResult.status === 'number' ? runResult.status : 1;
}

/**
* Run tool in portable host-native mode (non-Windows hosts).
* Uses isolated auth state in state/auth/<tool>/host/ but runs the tool
* directly on the host without a VM. This provides portability on Linux/macOS
* where a VM isn't needed for Linux tools.
*/
function runPortableHostNative(options) {
const {
tool,
adapter,
projectPath,
mergedEnv,
toolArgs,
authMode,
settings
} = options;

// Resolve the runner
const runner = resolveRunner(adapter, mergedEnv);
if (!runner) {
fail(`No executable found for tool '${tool}'. Set ${adapter.command_env} or install one of: ${adapter.candidate_commands.join(', ')}`);
}

// Apply portable auth environment (isolates auth state to state/auth/<tool>/host/)
const env = applyPortableHostAuthEnv(tool, { ...mergedEnv }, settings);

console.log(`[portable-native] Running ${tool} with isolated auth state...`);

const result = cp.spawnSync(runner, toolArgs, {
cwd: projectPath,
stdio: 'inherit',
env
});

if (result.error) {
fail(`Failed to launch '${runner}': ${result.error.message}`);
}

process.exitCode = typeof result.status === 'number' ? result.status : 1;
}

function startWindowsVm() {
const startScript = path.join(repoRoot, 'scripts', 'runtime', 'windows', 'start-vm.cmd');
if (!fs.existsSync(startScript)) {
Expand Down
58 changes: 58 additions & 0 deletions scripts/runtime/linux/smoke-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
# Portable Coder smoke test for Linux/macOS
# Run this to verify the portable launcher is working

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../../../" && pwd)"
PCODER="${REPO_ROOT}/scripts/pcoder"

echo "=== PortableCoder Smoke Test ==="
echo "Repo root: ${REPO_ROOT}"
echo ""

# Check if pcoder exists
if [[ ! -x "${PCODER}" ]]; then
echo "FAIL: pcoder launcher not found or not executable: ${PCODER}"
exit 1
fi
echo "OK: pcoder launcher found"

# Check if settings are initialized
if ! "${PCODER}" doctor >/dev/null 2>&1; then
echo "INFO: Settings not initialized, running setup --init..."
"${PCODER}" setup --init
fi

echo ""
echo "=== Running doctor ==="
if ! "${PCODER}" doctor; then
echo "FAIL: Doctor check failed"
exit 1
fi

echo ""
echo "=== Testing codex in portable-native mode ==="
if "${PCODER}" run codex --mode linux-portable -- --version 2>&1 | grep -q "codex-cli"; then
echo "OK: codex works in portable-native mode"
else
echo "FAIL: codex portable-native mode failed"
exit 1
fi

echo ""
echo "=== Testing claude in portable-native mode ==="
if "${PCODER}" run claude --mode linux-portable -- --version 2>&1 | grep -q "Claude Code"; then
echo "OK: claude works in portable-native mode"
else
echo "FAIL: claude portable-native mode failed"
exit 1
fi

echo ""
echo "=== Auth status ==="
"${PCODER}" auth status

echo ""
echo "=== All smoke tests passed ==="
Loading