@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 CHANGED
@@ -1,45 +1,69 @@
1
1
  # @ricsam/r5d-macos-vm
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
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
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
5
+ ## Requirements
6
6
 
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
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
- ## Purpose
12
+ No Xcode installation, Apple Developer account, or prebuilt macOS image is required on the user's Mac.
10
13
 
11
- This package exists to:
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
- ## What is OIDC Trusted Publishing?
16
+ ```bash
17
+ npx @ricsam/r5d-macos-vm doctor
18
+ npx @ricsam/r5d-macos-vm create
19
+ ```
17
20
 
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
21
+ The default VM is named `r5d-macos`. To use a different name or size:
19
22
 
20
- ## Setup Instructions
23
+ ```bash
24
+ r5d-macos-vm create build-mac \
25
+ --cpus 4 \
26
+ --memory-bytes 8589934592 \
27
+ --disk-bytes 85899345920
28
+ ```
21
29
 
22
- To properly configure OIDC trusted publishing for this package:
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
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
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
- ## DO NOT USE THIS PACKAGE
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
- This package is a placeholder for OIDC configuration only. It:
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
- ## More Information
46
+ ## Storage and security
38
47
 
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
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
- **Maintained for OIDC setup purposes only**
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//&/&amp;}"
24
+ escaped_start_script="${escaped_start_script//</&lt;}"
25
+ escaped_start_script="${escaped_start_script//>/&gt;}"
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
+ });