@ricsam/r5d-macos-vm 0.0.0 → 0.0.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -28
- package/bootstrap/Install r5d.command +81 -0
- package/bootstrap/README.txt +7 -0
- package/bootstrap/dev.r5d.worker-terminal.plist.template +23 -0
- package/bootstrap/install-worker-launch-agent.sh +47 -0
- package/bootstrap/start-worker.command.template +71 -0
- package/dist/cjs/artifact-manifest.cjs +224 -0
- package/dist/cjs/cli.cjs +179 -0
- package/dist/cjs/host.cjs +62 -0
- package/dist/cjs/main.cjs +10 -0
- package/dist/cjs/native-runtime.cjs +464 -0
- package/dist/cjs/package.json +5 -0
- package/dist/mjs/artifact-manifest.mjs +176 -0
- package/dist/mjs/cli.mjs +144 -0
- package/dist/mjs/host.mjs +37 -0
- package/dist/mjs/main.mjs +9 -0
- package/dist/mjs/native-runtime.mjs +421 -0
- package/dist/mjs/package.json +5 -0
- package/dist/types/artifact-manifest.d.ts +33 -0
- package/dist/types/cli.d.ts +23 -0
- package/dist/types/host.d.ts +7 -0
- package/dist/types/main.d.ts +2 -0
- package/dist/types/native-runtime.d.ts +28 -0
- package/native-artifact.json +14 -0
- package/package.json +39 -7
package/README.md
CHANGED
|
@@ -1,45 +1,69 @@
|
|
|
1
1
|
# @ricsam/r5d-macos-vm
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`r5d-macos-vm` creates and runs a local macOS VM for r5d.dev on Apple silicon. The npm package contains the CLI and guest bootstrap scripts, but no Apple software or prebuilt VM image. It downloads a signed and notarized r5d native runner from `downloads.r5d.dev`; that runner downloads the current compatible macOS restore image directly from Apple and installs it locally.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Requirements
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- An Apple-silicon Mac running macOS 14 or newer.
|
|
8
|
+
- Node.js 18 or newer.
|
|
9
|
+
- Enough free storage for the Apple restore image and the VM disk. The default disk is sparse, but macOS installation can still use tens of gigabytes.
|
|
10
|
+
- Internet access to Apple, r5d.dev, npm, and `downloads.r5d.dev`.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
No Xcode installation, Apple Developer account, or prebuilt macOS image is required on the user's Mac.
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@ricsam/r5d-macos-vm`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
14
|
+
## Quick start
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
```bash
|
|
17
|
+
npx @ricsam/r5d-macos-vm doctor
|
|
18
|
+
npx @ricsam/r5d-macos-vm create
|
|
19
|
+
```
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
The default VM is named `r5d-macos`. To use a different name or size:
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
```bash
|
|
24
|
+
r5d-macos-vm create build-mac \
|
|
25
|
+
--cpus 4 \
|
|
26
|
+
--memory-bytes 8589934592 \
|
|
27
|
+
--disk-bytes 85899345920
|
|
28
|
+
```
|
|
21
29
|
|
|
22
|
-
|
|
30
|
+
Complete macOS Setup Assistant in the VM, then double-click `/Volumes/My Shared Files/r5d Setup/Install r5d.command`. The shared bootstrap opens a visible Terminal, installs Homebrew and the r5d command-line tools, and starts worker enrollment. On another device, visit `https://r5d.dev/auth` and enter the displayed code, or scan its QR code. The worker starts only after authorization succeeds.
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
32
|
+
Useful commands:
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
```bash
|
|
35
|
+
r5d-macos-vm list
|
|
36
|
+
r5d-macos-vm status build-mac
|
|
37
|
+
r5d-macos-vm start build-mac
|
|
38
|
+
r5d-macos-vm stop build-mac
|
|
39
|
+
r5d-macos-vm delete build-mac --yes # move the VM to Trash
|
|
40
|
+
r5d-macos-vm delete build-mac --yes --purge # permanently delete it
|
|
41
|
+
r5d-macos-vm cache prune # remove restore images and old native runners
|
|
42
|
+
```
|
|
30
43
|
|
|
31
|
-
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
44
|
+
Add `--json` for newline-delimited progress events suitable for automation. `--state-root <path>` and `--bootstrap-dir <path>` are development/test overrides.
|
|
36
45
|
|
|
37
|
-
##
|
|
46
|
+
## Storage and security
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
48
|
+
- VM bundles: `~/Library/Application Support/r5d/macos-vms/`
|
|
49
|
+
- Apple restore-image cache: `~/Library/Caches/dev.r5d.macos-vm/restore/`
|
|
50
|
+
- Versioned native runners: `~/Library/Caches/dev.r5d.macos-vm/runtimes/<package-version>/`
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
Every npm version pins the native archive's exact HTTPS URL, SHA-256, byte length, Apple Team ID, bundle ID, arm64 architecture, and macOS deployment target. Before execution, the CLI uses built-in macOS security tools to verify the checksum, Mach-O metadata, Developer ID signature, exact virtualization-only entitlement set, hardened runtime, stapled notarization ticket, and Gatekeeper's notarization assessment. It does not invoke Xcode or its command-line tools. Downloads and extraction are locked and promoted into the cache atomically.
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
The package never distributes an IPSW, installed macOS disk, auxiliary storage, hardware model, or machine identifier. Restore images come from Apple and remain in the local cache only when requested or needed for installation.
|
|
55
|
+
|
|
56
|
+
## Updating and removing
|
|
57
|
+
|
|
58
|
+
Updating the npm package installs a new versioned native runner and does not modify existing VM bundles. The guest's `r5d-worker` and `r5dctl` use their normal r5d update flow.
|
|
59
|
+
|
|
60
|
+
`npm uninstall -g @ricsam/r5d-macos-vm` removes only the CLI. For complete removal, revoke the worker in r5d.dev, delete or purge each VM, run `r5d-macos-vm cache prune`, and then uninstall the npm package. Native runtime caches can be removed manually after the CLI is uninstalled; VM bundles are intentionally never removed by npm lifecycle scripts.
|
|
61
|
+
|
|
62
|
+
## Troubleshooting
|
|
63
|
+
|
|
64
|
+
- Run `r5d-macos-vm doctor --json` for machine-readable host and Virtualization.framework diagnostics.
|
|
65
|
+
- A checksum, signature, Team ID, entitlement, or notarization error is treated as unsafe. Reinstall the npm package and retry; do not bypass the check.
|
|
66
|
+
- If an install was interrupted, rerun `create` with the same name. After acquiring the per-VM lock, the CLI moves incomplete staging data to Trash, rebuilds it from scratch, and reuses the resumable restore-image cache.
|
|
67
|
+
- Clipboard sharing is not required for enrollment; use the short code or QR code shown in the guest Terminal.
|
|
68
|
+
|
|
69
|
+
Use of macOS virtual machines is subject to Apple's software license terms. This tool is intended for eligible local use on Apple-branded hardware; it does not grant rights to host or relay macOS environments as a service.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/bin/zsh
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="${0:A:h}"
|
|
5
|
+
STATE_DIR="$HOME/Library/Application Support/r5d"
|
|
6
|
+
LOG_DIR="$HOME/Library/Logs/r5d"
|
|
7
|
+
LOG_FILE="$LOG_DIR/bootstrap.log"
|
|
8
|
+
WORKER_LABEL="macos-vm"
|
|
9
|
+
VM_NAME="r5d-macos"
|
|
10
|
+
|
|
11
|
+
mkdir -p "$STATE_DIR" "$LOG_DIR"
|
|
12
|
+
chmod 700 "$STATE_DIR" "$LOG_DIR"
|
|
13
|
+
exec > >(tee -a "$LOG_FILE") 2>&1
|
|
14
|
+
|
|
15
|
+
if [[ -f "$SCRIPT_DIR/.r5d-worker-label" ]]; then
|
|
16
|
+
configured_label="$(tr -d '[:space:]' < "$SCRIPT_DIR/.r5d-worker-label")"
|
|
17
|
+
if [[ "$configured_label" =~ '^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$' ]]; then
|
|
18
|
+
WORKER_LABEL="$configured_label"
|
|
19
|
+
fi
|
|
20
|
+
fi
|
|
21
|
+
if [[ -f "$SCRIPT_DIR/.r5d-vm-name" ]]; then
|
|
22
|
+
configured_name="$(tr -d '[:space:]' < "$SCRIPT_DIR/.r5d-vm-name")"
|
|
23
|
+
if [[ "$configured_name" =~ '^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$' ]]; then
|
|
24
|
+
VM_NAME="$configured_name"
|
|
25
|
+
fi
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
print
|
|
29
|
+
print "r5d macOS VM setup"
|
|
30
|
+
print "Worker label: $WORKER_LABEL"
|
|
31
|
+
print
|
|
32
|
+
|
|
33
|
+
if [[ "$(uname -s)" != "Darwin" || "$(uname -m)" != "arm64" ]]; then
|
|
34
|
+
print -u2 "This installer requires macOS on Apple silicon (arm64)."
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
print "Checking outbound network access…"
|
|
39
|
+
if ! /usr/bin/curl --fail --silent --show-error --location --head --max-time 20 https://r5d.dev/ >/dev/null; then
|
|
40
|
+
print -u2 "Could not reach r5d.dev. Check this VM's network connection, then run the installer again."
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
if ! xcode-select -p >/dev/null 2>&1; then
|
|
45
|
+
print "Apple Command Line Tools are required. macOS will open its installer."
|
|
46
|
+
print "Complete that installer, then run this command again."
|
|
47
|
+
xcode-select --install >/dev/null 2>&1 || true
|
|
48
|
+
read -r "?Press Return after the Command Line Tools installation finishes, or close this window and retry later: "
|
|
49
|
+
if ! xcode-select -p >/dev/null 2>&1; then
|
|
50
|
+
print "Command Line Tools are not installed yet. Run this setup again after installation completes."
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
if [[ ! -x /opt/homebrew/bin/brew ]]; then
|
|
56
|
+
print "Installing Homebrew. macOS may ask for the local account password."
|
|
57
|
+
/bin/bash -c "$(/usr/bin/curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
eval "$(/opt/homebrew/bin/brew shellenv)"
|
|
61
|
+
brew update
|
|
62
|
+
brew install gh node bun
|
|
63
|
+
|
|
64
|
+
print "Installing r5d command-line tools."
|
|
65
|
+
npm install --global --omit=dev @ricsam/r5dctl @ricsam/r5d-worker @ricsam/r5d-browser
|
|
66
|
+
|
|
67
|
+
gh --version >/dev/null
|
|
68
|
+
node --version >/dev/null
|
|
69
|
+
bun --version >/dev/null
|
|
70
|
+
r5dctl --version >/dev/null
|
|
71
|
+
r5d-worker --version >/dev/null
|
|
72
|
+
r5d-browser --version >/dev/null
|
|
73
|
+
|
|
74
|
+
print
|
|
75
|
+
"$SCRIPT_DIR/install-worker-launch-agent.sh" "$WORKER_LABEL" "$VM_NAME"
|
|
76
|
+
|
|
77
|
+
touch "$STATE_DIR/bootstrap-complete"
|
|
78
|
+
chmod 600 "$STATE_DIR/bootstrap-complete"
|
|
79
|
+
print
|
|
80
|
+
print "Tools installed. The visible r5d worker Terminal is opening for authorization and startup…"
|
|
81
|
+
print "You may close this setup window."
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
r5d macOS VM guest setup
|
|
2
|
+
|
|
3
|
+
After completing macOS Setup Assistant, open “Install r5d.command” from this
|
|
4
|
+
folder. It installs the development tools, asks you to authorize the worker,
|
|
5
|
+
and configures a visible r5d-worker Terminal at each graphical login.
|
|
6
|
+
|
|
7
|
+
The installer is idempotent and safe to run again after an interrupted step.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>Label</key>
|
|
6
|
+
<string>dev.r5d.worker-terminal</string>
|
|
7
|
+
<key>LimitLoadToSessionType</key>
|
|
8
|
+
<string>Aqua</string>
|
|
9
|
+
<key>ProgramArguments</key>
|
|
10
|
+
<array>
|
|
11
|
+
<string>/usr/bin/open</string>
|
|
12
|
+
<string>-a</string>
|
|
13
|
+
<string>Terminal</string>
|
|
14
|
+
<string>__START_SCRIPT__</string>
|
|
15
|
+
</array>
|
|
16
|
+
<key>RunAtLoad</key>
|
|
17
|
+
<true/>
|
|
18
|
+
<key>KeepAlive</key>
|
|
19
|
+
<false/>
|
|
20
|
+
<key>ProcessType</key>
|
|
21
|
+
<string>Interactive</string>
|
|
22
|
+
</dict>
|
|
23
|
+
</plist>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/zsh
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
WORKER_LABEL="${1:-macos-vm}"
|
|
5
|
+
VM_NAME="${2:-r5d-macos}"
|
|
6
|
+
if [[ ! "$WORKER_LABEL" =~ '^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$' ]]; then
|
|
7
|
+
print -u2 "Invalid worker label: $WORKER_LABEL"
|
|
8
|
+
exit 2
|
|
9
|
+
fi
|
|
10
|
+
if [[ ! "$VM_NAME" =~ '^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$' ]]; then
|
|
11
|
+
print -u2 "Invalid VM name: $VM_NAME"
|
|
12
|
+
exit 2
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
STATE_DIR="$HOME/Library/Application Support/r5d"
|
|
16
|
+
LAUNCH_AGENTS_DIR="$HOME/Library/LaunchAgents"
|
|
17
|
+
START_SCRIPT="$STATE_DIR/start-worker.command"
|
|
18
|
+
PLIST="$LAUNCH_AGENTS_DIR/dev.r5d.worker-terminal.plist"
|
|
19
|
+
|
|
20
|
+
mkdir -p "$STATE_DIR" "$LAUNCH_AGENTS_DIR"
|
|
21
|
+
chmod 700 "$STATE_DIR"
|
|
22
|
+
|
|
23
|
+
escaped_start_script="${START_SCRIPT//&/&}"
|
|
24
|
+
escaped_start_script="${escaped_start_script//</<}"
|
|
25
|
+
escaped_start_script="${escaped_start_script//>/>}"
|
|
26
|
+
|
|
27
|
+
temporary_start="$(mktemp "$STATE_DIR/.start-worker.XXXXXX")"
|
|
28
|
+
temporary_plist="$(mktemp "$LAUNCH_AGENTS_DIR/.dev.r5d.worker-terminal.XXXXXX")"
|
|
29
|
+
trap 'rm -f "$temporary_start" "$temporary_plist"' EXIT
|
|
30
|
+
|
|
31
|
+
sed \
|
|
32
|
+
-e "s|__WORKER_LABEL__|$WORKER_LABEL|g" \
|
|
33
|
+
-e "s|__VM_NAME__|$VM_NAME|g" \
|
|
34
|
+
"${0:A:h}/start-worker.command.template" > "$temporary_start"
|
|
35
|
+
chmod 700 "$temporary_start"
|
|
36
|
+
|
|
37
|
+
sed \
|
|
38
|
+
-e "s|__START_SCRIPT__|$escaped_start_script|g" \
|
|
39
|
+
"${0:A:h}/dev.r5d.worker-terminal.plist.template" > "$temporary_plist"
|
|
40
|
+
chmod 600 "$temporary_plist"
|
|
41
|
+
|
|
42
|
+
mv -f "$temporary_start" "$START_SCRIPT"
|
|
43
|
+
mv -f "$temporary_plist" "$PLIST"
|
|
44
|
+
trap - EXIT
|
|
45
|
+
|
|
46
|
+
launchctl bootout "gui/$(id -u)" "$PLIST" >/dev/null 2>&1 || true
|
|
47
|
+
launchctl bootstrap "gui/$(id -u)" "$PLIST"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/zsh
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
export PATH="/opt/homebrew/bin:$HOME/.bun/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
|
|
5
|
+
STATE_DIR="$HOME/Library/Application Support/r5d"
|
|
6
|
+
LOCK_DIR="$STATE_DIR/worker-terminal-__WORKER_LABEL__.lock"
|
|
7
|
+
START_SCRIPT="${0:A}"
|
|
8
|
+
|
|
9
|
+
mkdir -p "$STATE_DIR"
|
|
10
|
+
chmod 700 "$STATE_DIR"
|
|
11
|
+
|
|
12
|
+
while ! mkdir "$LOCK_DIR" 2>/dev/null; do
|
|
13
|
+
existing_pid="$(cat "$LOCK_DIR/pid" 2>/dev/null || true)"
|
|
14
|
+
if [[ "$existing_pid" == <-> ]] && kill -0 "$existing_pid" 2>/dev/null; then
|
|
15
|
+
existing_command="$(ps -ww -p "$existing_pid" -o command= 2>/dev/null || true)"
|
|
16
|
+
if [[ -n "$existing_command" && "$existing_command" == *"$START_SCRIPT"* ]]; then
|
|
17
|
+
print "r5d-worker is already running in another Terminal (PID $existing_pid)."
|
|
18
|
+
read -r "?Press Return to close this window: "
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# A newly-created lock may not have its PID file yet. Never reap that
|
|
24
|
+
# acquisition window; only recover ownerless locks that have stayed stale.
|
|
25
|
+
lock_modified_at="$(stat -f %m "$LOCK_DIR" 2>/dev/null || print 0)"
|
|
26
|
+
current_epoch="$(date +%s)"
|
|
27
|
+
if [[ "$lock_modified_at" == <-> ]] && (( current_epoch - lock_modified_at < 60 )); then
|
|
28
|
+
sleep 1
|
|
29
|
+
continue
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
stale_lock="$LOCK_DIR.stale.$$.$RANDOM"
|
|
33
|
+
if mv "$LOCK_DIR" "$stale_lock" 2>/dev/null; then
|
|
34
|
+
rm -rf "$stale_lock"
|
|
35
|
+
else
|
|
36
|
+
sleep 1
|
|
37
|
+
fi
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
print $$ > "$LOCK_DIR/pid"
|
|
41
|
+
chmod 600 "$LOCK_DIR/pid"
|
|
42
|
+
cleanup() { rm -rf "$LOCK_DIR"; }
|
|
43
|
+
trap cleanup EXIT INT TERM HUP
|
|
44
|
+
|
|
45
|
+
print "Starting r5d-worker (__WORKER_LABEL__)"
|
|
46
|
+
print "Keep this Terminal open while the VM is available to r5d.dev."
|
|
47
|
+
print
|
|
48
|
+
|
|
49
|
+
auth_source="$(r5dctl --json auth status 2>/dev/null | /usr/bin/sed -n 's/.*"source"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | /usr/bin/head -n 1 || true)"
|
|
50
|
+
if [[ "$auth_source" != "device" ]]; then
|
|
51
|
+
if [[ -n "$auth_source" ]]; then
|
|
52
|
+
print "The shared r5d config currently uses '$auth_source' authentication. This VM requires its own revocable device credential."
|
|
53
|
+
print
|
|
54
|
+
fi
|
|
55
|
+
print "Authorize this VM before its worker starts."
|
|
56
|
+
print
|
|
57
|
+
r5dctl auth login --no-open --device-name "r5d macOS VM (__VM_NAME__)" --worker-label "__WORKER_LABEL__"
|
|
58
|
+
print
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
r5d-worker start --label "__WORKER_LABEL__" &
|
|
62
|
+
worker_pid=$!
|
|
63
|
+
trap 'kill -INT "$worker_pid" 2>/dev/null || true' INT TERM HUP
|
|
64
|
+
set +e
|
|
65
|
+
wait "$worker_pid"
|
|
66
|
+
worker_status=$?
|
|
67
|
+
set -e
|
|
68
|
+
print
|
|
69
|
+
print "r5d-worker stopped with status $worker_status."
|
|
70
|
+
read -r "?Press Return to close this window: "
|
|
71
|
+
exit "$worker_status"
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var artifact_manifest_exports = {};
|
|
30
|
+
__export(artifact_manifest_exports, {
|
|
31
|
+
MACOS_VM_PACKAGE_NAME: () => MACOS_VM_PACKAGE_NAME,
|
|
32
|
+
NATIVE_APP_NAME: () => NATIVE_APP_NAME,
|
|
33
|
+
NATIVE_ARCHIVE_NAME: () => NATIVE_ARCHIVE_NAME,
|
|
34
|
+
NATIVE_ARTIFACT_HOST: () => NATIVE_ARTIFACT_HOST,
|
|
35
|
+
NATIVE_BUNDLE_IDENTIFIER: () => NATIVE_BUNDLE_IDENTIFIER,
|
|
36
|
+
NATIVE_EXECUTABLE_NAME: () => NATIVE_EXECUTABLE_NAME,
|
|
37
|
+
NATIVE_MINIMUM_MACOS_VERSION: () => NATIVE_MINIMUM_MACOS_VERSION,
|
|
38
|
+
expectedArtifactUrl: () => expectedArtifactUrl,
|
|
39
|
+
findFileUpward: () => findFileUpward,
|
|
40
|
+
findForbiddenVirtualMachineArtifactPaths: () => findForbiddenVirtualMachineArtifactPaths,
|
|
41
|
+
findPackageRoot: () => findPackageRoot,
|
|
42
|
+
findPackageVersion: () => findPackageVersion,
|
|
43
|
+
isForbiddenVirtualMachineArtifactPath: () => isForbiddenVirtualMachineArtifactPath,
|
|
44
|
+
parseNativeArtifactManifest: () => parseNativeArtifactManifest,
|
|
45
|
+
readNativeArtifactManifest: () => readNativeArtifactManifest
|
|
46
|
+
});
|
|
47
|
+
module.exports = __toCommonJS(artifact_manifest_exports);
|
|
48
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
49
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
50
|
+
const MACOS_VM_PACKAGE_NAME = "@ricsam/r5d-macos-vm";
|
|
51
|
+
const NATIVE_ARCHIVE_NAME = "R5DMacOSVM.app.zip";
|
|
52
|
+
const NATIVE_APP_NAME = "R5DMacOSVM.app";
|
|
53
|
+
const NATIVE_EXECUTABLE_NAME = "R5DMacOSVM";
|
|
54
|
+
const NATIVE_BUNDLE_IDENTIFIER = "dev.r5d.macos-vm";
|
|
55
|
+
const NATIVE_MINIMUM_MACOS_VERSION = "14.0";
|
|
56
|
+
const NATIVE_ARTIFACT_HOST = "downloads.r5d.dev";
|
|
57
|
+
const FORBIDDEN_VM_ARTIFACT_BASENAMES = /* @__PURE__ */ new Set([
|
|
58
|
+
"auxiliarystorage",
|
|
59
|
+
"disk.img",
|
|
60
|
+
"hardwaremodel",
|
|
61
|
+
"machineidentifier",
|
|
62
|
+
"restoreimage.ipsw",
|
|
63
|
+
"savefile.vzvmsave"
|
|
64
|
+
]);
|
|
65
|
+
const FORBIDDEN_VM_ARTIFACT_EXTENSION = /\.(?:img|ipsw|r5dvm|vzvmsave)$/i;
|
|
66
|
+
function expectedArtifactUrl(version) {
|
|
67
|
+
return `https://${NATIVE_ARTIFACT_HOST}/r5d-macos-vm/${encodeURIComponent(version)}/${NATIVE_ARCHIVE_NAME}`;
|
|
68
|
+
}
|
|
69
|
+
function isForbiddenVirtualMachineArtifactPath(filePath) {
|
|
70
|
+
const normalized = filePath.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
71
|
+
const basename = normalized.slice(normalized.lastIndexOf("/") + 1).toLowerCase();
|
|
72
|
+
return FORBIDDEN_VM_ARTIFACT_BASENAMES.has(basename) || FORBIDDEN_VM_ARTIFACT_EXTENSION.test(basename);
|
|
73
|
+
}
|
|
74
|
+
function findForbiddenVirtualMachineArtifactPaths(paths) {
|
|
75
|
+
return paths.filter(isForbiddenVirtualMachineArtifactPath);
|
|
76
|
+
}
|
|
77
|
+
function requireObject(value) {
|
|
78
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
79
|
+
throw new Error("The native artifact manifest must contain a JSON object.");
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
function requireString(value, field) {
|
|
84
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
85
|
+
throw new Error(`The native artifact manifest field '${field}' must be a non-empty string.`);
|
|
86
|
+
}
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
function parseNativeArtifactManifest(value, expectedPackageVersion) {
|
|
90
|
+
const input = requireObject(value);
|
|
91
|
+
if (input.schemaVersion !== 1) throw new Error("Unsupported native artifact manifest schema.");
|
|
92
|
+
const packageVersion = requireString(input.packageVersion, "packageVersion");
|
|
93
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(packageVersion)) {
|
|
94
|
+
throw new Error("Native artifact packageVersion must be a semantic version.");
|
|
95
|
+
}
|
|
96
|
+
if (packageVersion !== expectedPackageVersion) {
|
|
97
|
+
throw new Error(`Native artifact version ${packageVersion} does not match package version ${expectedPackageVersion}.`);
|
|
98
|
+
}
|
|
99
|
+
const url = requireString(input.url, "url");
|
|
100
|
+
if (url !== expectedArtifactUrl(packageVersion)) {
|
|
101
|
+
throw new Error(`Native artifact URL must be ${expectedArtifactUrl(packageVersion)}.`);
|
|
102
|
+
}
|
|
103
|
+
const sha256 = requireString(input.sha256, "sha256").toLowerCase();
|
|
104
|
+
if (!/^[0-9a-f]{64}$/.test(sha256)) throw new Error("Native artifact sha256 must contain 64 hexadecimal characters.");
|
|
105
|
+
const byteLength = input.byteLength;
|
|
106
|
+
if (!Number.isSafeInteger(byteLength) || byteLength <= 0 || byteLength > 100 * 1024 * 1024) {
|
|
107
|
+
throw new Error("Native artifact byteLength must be between 1 byte and 100 MiB.");
|
|
108
|
+
}
|
|
109
|
+
const teamIdentifier = requireString(input.teamIdentifier, "teamIdentifier");
|
|
110
|
+
if (!/^[A-Z0-9]{10}$/.test(teamIdentifier)) {
|
|
111
|
+
throw new Error("Native artifact teamIdentifier must be a 10-character Apple Team ID.");
|
|
112
|
+
}
|
|
113
|
+
const fixedFields = {
|
|
114
|
+
archiveName: NATIVE_ARCHIVE_NAME,
|
|
115
|
+
appName: NATIVE_APP_NAME,
|
|
116
|
+
executableName: NATIVE_EXECUTABLE_NAME,
|
|
117
|
+
bundleIdentifier: NATIVE_BUNDLE_IDENTIFIER,
|
|
118
|
+
architecture: "arm64",
|
|
119
|
+
minimumMacOSVersion: NATIVE_MINIMUM_MACOS_VERSION
|
|
120
|
+
};
|
|
121
|
+
for (const [field, expected] of Object.entries(fixedFields)) {
|
|
122
|
+
if (input[field] !== expected) throw new Error(`Native artifact ${field} must be ${expected}.`);
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
schemaVersion: 1,
|
|
126
|
+
packageVersion,
|
|
127
|
+
url,
|
|
128
|
+
sha256,
|
|
129
|
+
byteLength,
|
|
130
|
+
archiveName: NATIVE_ARCHIVE_NAME,
|
|
131
|
+
appName: NATIVE_APP_NAME,
|
|
132
|
+
executableName: NATIVE_EXECUTABLE_NAME,
|
|
133
|
+
bundleIdentifier: NATIVE_BUNDLE_IDENTIFIER,
|
|
134
|
+
teamIdentifier,
|
|
135
|
+
architecture: "arm64",
|
|
136
|
+
minimumMacOSVersion: NATIVE_MINIMUM_MACOS_VERSION
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function readNativeArtifactManifest(filePath, expectedPackageVersion) {
|
|
140
|
+
let value;
|
|
141
|
+
try {
|
|
142
|
+
value = JSON.parse(import_node_fs.default.readFileSync(filePath, "utf8"));
|
|
143
|
+
} catch (error) {
|
|
144
|
+
throw new Error(`Unable to read native artifact manifest at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
145
|
+
}
|
|
146
|
+
return parseNativeArtifactManifest(value, expectedPackageVersion);
|
|
147
|
+
}
|
|
148
|
+
function findFileUpward(startPath, fileName) {
|
|
149
|
+
let current = import_node_path.default.resolve(startPath);
|
|
150
|
+
try {
|
|
151
|
+
if (!import_node_fs.default.statSync(current).isDirectory()) current = import_node_path.default.dirname(current);
|
|
152
|
+
} catch {
|
|
153
|
+
current = import_node_path.default.dirname(current);
|
|
154
|
+
}
|
|
155
|
+
while (true) {
|
|
156
|
+
const candidate = import_node_path.default.join(current, fileName);
|
|
157
|
+
if (import_node_fs.default.existsSync(candidate)) return candidate;
|
|
158
|
+
const parent = import_node_path.default.dirname(current);
|
|
159
|
+
if (parent === current) return null;
|
|
160
|
+
current = parent;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function findPackageRoot(startPath) {
|
|
164
|
+
let current = import_node_path.default.resolve(startPath);
|
|
165
|
+
try {
|
|
166
|
+
if (!import_node_fs.default.statSync(current).isDirectory()) current = import_node_path.default.dirname(current);
|
|
167
|
+
} catch {
|
|
168
|
+
current = import_node_path.default.dirname(current);
|
|
169
|
+
}
|
|
170
|
+
let sourceRoot = null;
|
|
171
|
+
while (true) {
|
|
172
|
+
const packageJsonPath = import_node_path.default.join(current, "package.json");
|
|
173
|
+
if (import_node_fs.default.existsSync(packageJsonPath)) {
|
|
174
|
+
try {
|
|
175
|
+
const value = JSON.parse(import_node_fs.default.readFileSync(packageJsonPath, "utf8"));
|
|
176
|
+
if (value.name === MACOS_VM_PACKAGE_NAME && typeof value.bin?.["r5d-macos-vm"] === "string") return current;
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (import_node_path.default.basename(current) === "r5d-macos-vm" && import_node_fs.default.existsSync(import_node_path.default.join(import_node_path.default.dirname(current), "VERSION.txt"))) {
|
|
181
|
+
sourceRoot = current;
|
|
182
|
+
}
|
|
183
|
+
const parent = import_node_path.default.dirname(current);
|
|
184
|
+
if (parent === current) return sourceRoot;
|
|
185
|
+
current = parent;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function findPackageVersion(startPath) {
|
|
189
|
+
const packageRoot = findPackageRoot(startPath);
|
|
190
|
+
if (packageRoot) {
|
|
191
|
+
const packageJsonPath = import_node_path.default.join(packageRoot, "package.json");
|
|
192
|
+
if (import_node_fs.default.existsSync(packageJsonPath)) {
|
|
193
|
+
try {
|
|
194
|
+
const value = JSON.parse(import_node_fs.default.readFileSync(packageJsonPath, "utf8"));
|
|
195
|
+
if (value.name === MACOS_VM_PACKAGE_NAME && typeof value.version === "string") return value.version;
|
|
196
|
+
} catch {
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const localVersionPath = import_node_path.default.join(import_node_path.default.dirname(packageRoot), "VERSION.txt");
|
|
200
|
+
if (import_node_fs.default.existsSync(localVersionPath)) {
|
|
201
|
+
const version = import_node_fs.default.readFileSync(localVersionPath, "utf8").trim();
|
|
202
|
+
if (/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(version)) return version;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
throw new Error(`Unable to determine the installed ${MACOS_VM_PACKAGE_NAME} version.`);
|
|
206
|
+
}
|
|
207
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
208
|
+
0 && (module.exports = {
|
|
209
|
+
MACOS_VM_PACKAGE_NAME,
|
|
210
|
+
NATIVE_APP_NAME,
|
|
211
|
+
NATIVE_ARCHIVE_NAME,
|
|
212
|
+
NATIVE_ARTIFACT_HOST,
|
|
213
|
+
NATIVE_BUNDLE_IDENTIFIER,
|
|
214
|
+
NATIVE_EXECUTABLE_NAME,
|
|
215
|
+
NATIVE_MINIMUM_MACOS_VERSION,
|
|
216
|
+
expectedArtifactUrl,
|
|
217
|
+
findFileUpward,
|
|
218
|
+
findForbiddenVirtualMachineArtifactPaths,
|
|
219
|
+
findPackageRoot,
|
|
220
|
+
findPackageVersion,
|
|
221
|
+
isForbiddenVirtualMachineArtifactPath,
|
|
222
|
+
parseNativeArtifactManifest,
|
|
223
|
+
readNativeArtifactManifest
|
|
224
|
+
});
|