Skip to content

Commit e0a8f26

Browse files
simongdaviesCopilot
andcommitted
Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
1 parent 62d98d0 commit e0a8f26

File tree

9 files changed

+363
-162
lines changed

9 files changed

+363
-162
lines changed

src/js-host-api/examples/mcp-server/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ cd src\js-host-api\examples\mcp-server
298298
| `--cpu-timeout <ms>`| `-CpuTimeout <ms>` | CPU time limit per execution (default: 1000ms) |
299299
| `--wall-timeout <ms>`| `-WallTimeout <ms>`| Wall-clock backstop per execution (default: 5000ms) |
300300
| `--heap-size <MB>` | `-HeapSize <MB>` | Guest heap size (default: 16MB) |
301-
| `--stack-size <MB>` | `-StackSize <MB>` | Guest stack size (default: 1MB) |
301+
| `--scratch-size <MB>` | `-ScratchSize <MB>` | Guest scratch size (default: 1MB) |
302302

303303
**What the script does:**
304304

@@ -496,7 +496,7 @@ and return the result.
496496

497497
### 🔢 Mathematics
498498

499-
> **"Calculate π to 50 decimal places using the Bailey–Borwein–Plouffe formula"**
499+
> **"Calculate π to 50 decimal places using Machin's formula"**
500500
>
501501
> Tests: BigInt arithmetic, series computation, precision handling
502502
@@ -634,7 +634,7 @@ The Hyperlight sandbox provides **hardware-level isolation**:
634634
- 🌐 **No network access** — can't make HTTP requests
635635
- 🖥️ **No host access** — can't access environment variables, processes, or system calls
636636
- ⏱️ **CPU bounded** — configurable limit (default 1000ms), enforced by the hypervisor
637-
- 💾 **Memory bounded** — configurable (default 16MB heap, 1MB stack)
637+
- 💾 **Memory bounded** — configurable (default 16MB heap, 1MB scratch)
638638
- 🔄 **Automatic recovery** — sandbox rebuilds after failures
639639

640640
This makes it safe to execute untrusted, AI-generated code.
@@ -646,7 +646,7 @@ This makes it safe to execute untrusted, AI-generated code.
646646
| `HYPERLIGHT_CPU_TIMEOUT_MS` | `1000` | Maximum CPU time per execution (milliseconds). The hypervisor hard-kills the guest when exceeded. |
647647
| `HYPERLIGHT_WALL_TIMEOUT_MS` | `5000` | Maximum wall-clock time per execution (milliseconds). Backstop for edge cases where CPU time alone doesn't catch the issue. |
648648
| `HYPERLIGHT_HEAP_SIZE_MB` | `16` | Guest heap size in megabytes. Increase for memory-heavy computations (large arrays, BigInt work). |
649-
| `HYPERLIGHT_STACK_SIZE_MB` | `1` | Guest stack size in megabytes. Increase for deeply recursive algorithms. |
649+
| `HYPERLIGHT_SCRATCH_SIZE_MB` | `1` | Guest scratch size in megabytes. Increase for deeply recursive algorithms. |
650650
| `HYPERLIGHT_TIMING_LOG` || Path to a file. When set, the server appends one JSON line per tool call with a timing breakdown (init, setup, compile, snapshot, execute, total). Used by the demo script to show model vs. tool time. |
651651
| `HYPERLIGHT_CODE_LOG` || Path to a file. When set, the server writes the received JavaScript source code on each tool call. Used by the demo script's `--show-code` flag. |
652652

@@ -700,7 +700,7 @@ The code exceeded the CPU time limit (default: 1000ms). Options:
700700

701701
Check that:
702702

703-
1. Node.js >= 18 is installed
703+
1. Node.js >= 20 is installed
704704
2. The native module is built (`ls src/js-host-api/js-host-api.*.node`)
705705
3. Dependencies are installed (`cd examples/mcp-server && npm install`)
706706

src/js-host-api/examples/mcp-server/demo-copilot-cli.ps1

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
.PARAMETER HeapSize
4040
Guest heap size in megabytes (default: 16).
4141
42-
.PARAMETER StackSize
43-
Guest stack size in megabytes (default: 1).
42+
.PARAMETER ScratchSize
43+
Guest scratch size in megabytes (default: 1).
4444
4545
.EXAMPLE
4646
.\demo-copilot-cli.ps1
@@ -94,7 +94,7 @@ param(
9494
[int]$HeapSize = $(if ($env:HYPERLIGHT_HEAP_SIZE_MB) { [int]$env:HYPERLIGHT_HEAP_SIZE_MB } else { 16 }),
9595

9696
[ValidateRange(1, [int]::MaxValue)]
97-
[int]$StackSize = $(if ($env:HYPERLIGHT_STACK_SIZE_MB) { [int]$env:HYPERLIGHT_STACK_SIZE_MB } else { 1 })
97+
[int]$ScratchSize = $(if ($env:HYPERLIGHT_SCRATCH_SIZE_MB) { [int]$env:HYPERLIGHT_SCRATCH_SIZE_MB } else { 1 })
9898
)
9999

100100
# ── Strict Mode ──────────────────────────────────────────────────────
@@ -251,7 +251,7 @@ function Build-McpJson {
251251
$env:HYPERLIGHT_CPU_TIMEOUT_MS = $CpuTimeout
252252
$env:HYPERLIGHT_WALL_TIMEOUT_MS = $WallTimeout
253253
$env:HYPERLIGHT_HEAP_SIZE_MB = $HeapSize
254-
$env:HYPERLIGHT_STACK_SIZE_MB = $StackSize
254+
$env:HYPERLIGHT_SCRATCH_SIZE_MB = $ScratchSize
255255

256256
$config = [ordered]@{
257257
mcpServers = [ordered]@{
@@ -277,16 +277,17 @@ function Build-McpServerEntry {
277277
HYPERLIGHT_CPU_TIMEOUT_MS = [string]$CpuTimeout
278278
HYPERLIGHT_WALL_TIMEOUT_MS = [string]$WallTimeout
279279
HYPERLIGHT_HEAP_SIZE_MB = [string]$HeapSize
280-
HYPERLIGHT_STACK_SIZE_MB = [string]$StackSize
280+
HYPERLIGHT_SCRATCH_SIZE_MB = [string]$ScratchSize
281281
}
282282

283283
if ($ForInstall) {
284-
# Permanent install: use fixed well-known paths so Copilot can
285-
# spawn the server with predictable log locations.
284+
# Permanent install: include timing log at a fixed well-known path.
285+
# Code logging is intentionally omitted from permanent installs —
286+
# it would persist all executed JS to disk by default, which is a
287+
# privacy concern and can grow unbounded.
286288
# "Roads? Where we're going, we don't need roads." — Back to the Future (1985)
287289
$tempDir = [System.IO.Path]::GetTempPath()
288290
$envHash['HYPERLIGHT_TIMING_LOG'] = (Join-Path $tempDir 'hyperlight-timing.jsonl') -replace '\\', '/'
289-
$envHash['HYPERLIGHT_CODE_LOG'] = (Join-Path $tempDir 'hyperlight-code.js') -replace '\\', '/'
290291
} else {
291292
# Per-session: use the current env vars (random per-session paths
292293
# set by the script to avoid clobbering between concurrent runs).
@@ -457,29 +458,6 @@ INSTRUCTIONS: You have an MCP tool called 'execute_javascript' from the 'hyperli
457458

458459
# Write the prompt to a temp file and pass via @file to avoid PS7's
459460
# native-command argument mangling with multi-line here-strings.
460-
# The copilot CLI reads -p @file the same as -p "string".
461-
# "Nobody puts Baby in a corner." — Dirty Dancing (1987)
462-
$promptFile = Join-Path ([System.IO.Path]::GetTempPath()) "hyperlight-prompt-$PID-$(Get-Random).txt"
463-
$fullPrompt | Set-Content -Path $promptFile -Encoding utf8NoBOM
464-
465-
# Build the command args list for copilot CLI.
466-
# We assemble it as an array so we can display it with -ShowCommand
467-
# before actually executing it.
468-
$copilotArgs = @(
469-
'-p', $fullPrompt,
470-
'-s',
471-
'--additional-mcp-config', "@$mcpTmp",
472-
'--allow-all-tools',
473-
'--deny-tool', 'shell',
474-
'--deny-tool', 'write',
475-
'--deny-tool', 'read',
476-
'--deny-tool', 'fetch',
477-
'--no-custom-instructions',
478-
'--no-ask-user',
479-
'--disable-builtin-mcps',
480-
'--model', $Model
481-
)
482-
483461
# Emit the command if -ShowCommand was requested.
484462
# "Show me the money!" — Jerry Maguire (1996... close enough to the 80s)
485463
if ($ShowCommand) {
@@ -497,7 +475,7 @@ INSTRUCTIONS: You have an MCP tool called 'execute_javascript' from the 'hyperli
497475
if ($CpuTimeout -ne 1000) { $installFlags += " -CpuTimeout $CpuTimeout" }
498476
if ($WallTimeout -ne 5000) { $installFlags += " -WallTimeout $WallTimeout" }
499477
if ($HeapSize -ne 16) { $installFlags += " -HeapSize $HeapSize" }
500-
if ($StackSize -ne 1) { $installFlags += " -StackSize $StackSize" }
478+
if ($ScratchSize -ne 1) { $installFlags += " -ScratchSize $ScratchSize" }
501479

502480
Write-Host ''
503481
Write-Host '🔧 Copy-pasteable command:' -ForegroundColor Cyan
@@ -687,7 +665,6 @@ INSTRUCTIONS: You have an MCP tool called 'execute_javascript' from the 'hyperli
687665
# Clean up temp files
688666
Remove-Item $mcpTmp -ErrorAction SilentlyContinue
689667
Remove-Item $timingLog -ErrorAction SilentlyContinue
690-
Remove-Item $promptFile -ErrorAction SilentlyContinue
691668
if ($codeLog) { Remove-Item $codeLog -ErrorAction SilentlyContinue }
692669

693670
return $success
@@ -697,7 +674,7 @@ INSTRUCTIONS: You have an MCP tool called 'execute_javascript' from the 'hyperli
697674

698675
Write-Banner
699676
Write-Info "Using model: $Model"
700-
Write-Info "Sandbox limits: CPU ${CpuTimeout}ms, wall ${WallTimeout}ms, heap ${HeapSize}MB, stack ${StackSize}MB"
677+
Write-Info "Sandbox limits: CPU ${CpuTimeout}ms, wall ${WallTimeout}ms, heap ${HeapSize}MB, scratch ${ScratchSize}MB"
701678

702679
switch ($Mode) {
703680
'Install' {

src/js-host-api/examples/mcp-server/demo-copilot-cli.sh

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
# --cpu-timeout <ms> CPU time limit per execution (default: 1000)
2828
# --wall-timeout <ms> Wall-clock backstop per execution (default: 5000)
2929
# --heap-size <MB> Guest heap size in megabytes (default: 16)
30-
# --stack-size <MB> Guest stack size in megabytes (default: 1)
30+
# --scratch-size <MB> Guest scratch size in megabytes (default: 1)
3131
#
3232
# ─────────────────────────────────────────────────────────────────────
3333
set -euo pipefail
@@ -55,7 +55,7 @@ CUSTOM_PROMPT=""
5555
CPU_TIMEOUT="${HYPERLIGHT_CPU_TIMEOUT_MS:-1000}"
5656
WALL_TIMEOUT="${HYPERLIGHT_WALL_TIMEOUT_MS:-5000}"
5757
HEAP_SIZE="${HYPERLIGHT_HEAP_SIZE_MB:-16}"
58-
STACK_SIZE="${HYPERLIGHT_STACK_SIZE_MB:-1}"
58+
SCRATCH_SIZE="${HYPERLIGHT_SCRATCH_SIZE_MB:-1}"
5959

6060
# ── Paths ────────────────────────────────────────────────────────────
6161
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -82,9 +82,24 @@ separator() {
8282
}
8383

8484
# Timing helpers — because "Time... is not on my side" — The Rolling Stones (1964, close enough)
85-
# Uses millisecond precision via date +%s%3N (GNU coreutils)
85+
# Uses millisecond precision when available; falls back to portable options on macOS/BSD.
8686
now_ms() {
87-
date +%s%3N
87+
# Prefer GNU date if available (system date or gdate), and verify output is numeric.
88+
if date +%s%3N 2>/dev/null | grep -Eq '^[0-9]+$'; then
89+
date +%s%3N
90+
elif command -v gdate >/dev/null 2>&1; then
91+
gdate +%s%3N
92+
elif command -v python3 >/dev/null 2>&1; then
93+
python3 - <<'EOF'
94+
import time, sys
95+
sys.stdout.write(str(int(time.time() * 1000)))
96+
EOF
97+
elif command -v node >/dev/null 2>&1; then
98+
node -e 'console.log(Date.now())'
99+
else
100+
# Portable fallback: seconds since epoch with millisecond field set to 000.
101+
date +%s000
102+
fi
88103
}
89104

90105
# Log elapsed time since a given start timestamp (ms)
@@ -144,8 +159,7 @@ check_prerequisites() {
144159
info "Verifying native addon loads correctly..."
145160
local lib_js="${SCRIPT_DIR}/../../lib.js"
146161
local smoke_err
147-
smoke_err="$(node --input-type=module -e "import('${lib_js}').then(() => console.log('OK')).catch(e => { console.error(e.message); process.exit(1); })" 2>&1)"
148-
if [[ $? -ne 0 ]]; then
162+
if ! smoke_err="$(node --input-type=module -e "import('${lib_js}').then(() => console.log('OK')).catch(e => { console.error(e.message); process.exit(1); })" 2>&1)"; then
149163
fail "Native addon failed to load — rebuild with 'just build'.\n${smoke_err}"
150164
fi
151165
ok "Native addon loads successfully"
@@ -170,22 +184,21 @@ build_mcp_json() {
170184
export HYPERLIGHT_CPU_TIMEOUT_MS="${CPU_TIMEOUT}"
171185
export HYPERLIGHT_WALL_TIMEOUT_MS="${WALL_TIMEOUT}"
172186
export HYPERLIGHT_HEAP_SIZE_MB="${HEAP_SIZE}"
173-
export HYPERLIGHT_STACK_SIZE_MB="${STACK_SIZE}"
187+
export HYPERLIGHT_SCRATCH_SIZE_MB="${SCRATCH_SIZE}"
174188

175189
node -e "
176190
const installMode = '${install_mode}' === 'install';
177191
const env = {
178192
HYPERLIGHT_CPU_TIMEOUT_MS: process.env.HYPERLIGHT_CPU_TIMEOUT_MS,
179193
HYPERLIGHT_WALL_TIMEOUT_MS: process.env.HYPERLIGHT_WALL_TIMEOUT_MS,
180194
HYPERLIGHT_HEAP_SIZE_MB: process.env.HYPERLIGHT_HEAP_SIZE_MB,
181-
HYPERLIGHT_STACK_SIZE_MB: process.env.HYPERLIGHT_STACK_SIZE_MB,
195+
HYPERLIGHT_SCRATCH_SIZE_MB: process.env.HYPERLIGHT_SCRATCH_SIZE_MB,
182196
};
183197
if (installMode) {
184-
// Permanent install: fixed well-known paths so Copilot can
185-
// spawn the server with predictable log locations.
186-
// \"Roads? Where we're going, we don't need roads.\" — Back to the Future (1985)
198+
// Permanent install: include timing log at a fixed well-known path.
199+
// Code logging is intentionally omitted — it would persist all
200+
// executed JS to disk by default (privacy concern, unbounded growth).
187201
env.HYPERLIGHT_TIMING_LOG = '/tmp/hyperlight-timing.jsonl';
188-
env.HYPERLIGHT_CODE_LOG = '/tmp/hyperlight-code.js';
189202
} else {
190203
if (process.env.HYPERLIGHT_TIMING_LOG) env.HYPERLIGHT_TIMING_LOG = process.env.HYPERLIGHT_TIMING_LOG;
191204
if (process.env.HYPERLIGHT_CODE_LOG) env.HYPERLIGHT_CODE_LOG = process.env.HYPERLIGHT_CODE_LOG;
@@ -233,9 +246,8 @@ install_mcp_config() {
233246
HYPERLIGHT_CPU_TIMEOUT_MS: '${CPU_TIMEOUT}',
234247
HYPERLIGHT_WALL_TIMEOUT_MS: '${WALL_TIMEOUT}',
235248
HYPERLIGHT_HEAP_SIZE_MB: '${HEAP_SIZE}',
236-
HYPERLIGHT_STACK_SIZE_MB: '${STACK_SIZE}',
249+
HYPERLIGHT_SCRATCH_SIZE_MB: '${SCRATCH_SIZE}',
237250
HYPERLIGHT_TIMING_LOG: '/tmp/hyperlight-timing.jsonl',
238-
HYPERLIGHT_CODE_LOG: '/tmp/hyperlight-code.js',
239251
},
240252
};
241253
fs.writeFileSync('${MCP_CONFIG_FILE}', JSON.stringify(existing, null, 4) + '\n');
@@ -440,7 +452,7 @@ Your task is complete the moment you present the result."
440452
[[ "${CPU_TIMEOUT}" != "1000" ]] && install_flags+=" --cpu-timeout ${CPU_TIMEOUT}"
441453
[[ "${WALL_TIMEOUT}" != "5000" ]] && install_flags+=" --wall-timeout ${WALL_TIMEOUT}"
442454
[[ "${HEAP_SIZE}" != "16" ]] && install_flags+=" --heap-size ${HEAP_SIZE}"
443-
[[ "${STACK_SIZE}" != "1" ]] && install_flags+=" --stack-size ${STACK_SIZE}"
455+
[[ "${SCRATCH_SIZE}" != "1" ]] && install_flags+=" --scratch-size ${SCRATCH_SIZE}"
444456

445457
echo ""
446458
echo -e "${CYAN}${BOLD}🔧 Copy-pasteable command:${RESET}"
@@ -603,24 +615,24 @@ main() {
603615
HEAP_SIZE="$2"
604616
shift 2
605617
;;
606-
--stack-size)
607-
if [[ -z "${2:-}" ]]; then fail "--stack-size requires a value in MB"; fi
608-
STACK_SIZE="$2"
618+
--scratch-size)
619+
if [[ -z "${2:-}" ]]; then fail "--scratch-size requires a value in MB"; fi
620+
SCRATCH_SIZE="$2"
609621
shift 2
610622
;;
611623
--install|--uninstall|--headless)
612624
mode="$1"
613625
shift
614626
;;
615627
*)
616-
fail "Unknown argument: $1\nUsage: $0 [--headless] [--install] [--uninstall] [--model <name>] [--prompt <text>] [--show-code] [--show-command] [--cpu-timeout <ms>] [--wall-timeout <ms>] [--heap-size <MB>] [--stack-size <MB>]"
628+
fail "Unknown argument: $1\nUsage: $0 [--headless] [--install] [--uninstall] [--model <name>] [--prompt <text>] [--show-code] [--show-command] [--cpu-timeout <ms>] [--wall-timeout <ms>] [--heap-size <MB>] [--scratch-size <MB>]"
617629
;;
618630
esac
619631
done
620632

621633
banner
622634
info "Using model: ${BOLD}${MODEL}${RESET}"
623-
info "Sandbox limits: CPU ${BOLD}${CPU_TIMEOUT}ms${RESET}, wall ${BOLD}${WALL_TIMEOUT}ms${RESET}, heap ${BOLD}${HEAP_SIZE}MB${RESET}, stack ${BOLD}${STACK_SIZE}MB${RESET}"
635+
info "Sandbox limits: CPU ${BOLD}${CPU_TIMEOUT}ms${RESET}, wall ${BOLD}${WALL_TIMEOUT}ms${RESET}, heap ${BOLD}${HEAP_SIZE}MB${RESET}, scratch ${BOLD}${SCRATCH_SIZE}MB${RESET}"
624636

625637
case "${mode}" in
626638
--install)

src/js-host-api/examples/mcp-server/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,8 @@
1515
},
1616
"devDependencies": {
1717
"vitest": "^4.0.18"
18+
},
19+
"engines": {
20+
"node": ">= 20"
1821
}
1922
}

0 commit comments

Comments
 (0)