humanish 0.98.0 → 0.99.1
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/AGENTS.md +2 -0
- package/README.md +3 -2
- package/dist/browser-control-client.js +4 -2
- package/dist/browser-control-client.js.map +1 -1
- package/dist/browser-control-dispatcher.js +1 -1
- package/dist/browser-control-dispatcher.js.map +1 -1
- package/dist/computer-use.js +21 -8
- package/dist/computer-use.js.map +1 -1
- package/dist/doctor-lab.js +1 -1
- package/dist/doctor-lab.js.map +1 -1
- package/dist/local-firecracker-desktop.js +35 -19
- package/dist/local-firecracker-desktop.js.map +1 -1
- package/dist/local-runtime-host.d.ts +31 -0
- package/dist/local-runtime-host.js +128 -0
- package/dist/local-runtime-host.js.map +1 -0
- package/dist/local-runtime-release.d.ts +2 -0
- package/dist/local-runtime-release.js +14 -4
- package/dist/local-runtime-release.js.map +1 -1
- package/dist/local-runtime-ssh.d.ts +21 -0
- package/dist/local-runtime-ssh.js +89 -0
- package/dist/local-runtime-ssh.js.map +1 -0
- package/dist/local-runtime.d.ts +2 -3
- package/dist/local-runtime.js +37 -19
- package/dist/local-runtime.js.map +1 -1
- package/dist/restricted-codex-session.js +23 -9
- package/dist/restricted-codex-session.js.map +1 -1
- package/docs/architecture/browser-control.md +6 -0
- package/docs/architecture/local-browser-runtime.md +61 -14
- package/docs/architecture/restricted-codex-analysis.md +4 -2
- package/docs/contracts/schemas.md +1 -1
- package/docs/goals/current.md +7 -6
- package/docs/ramp/README.md +8 -1
- package/docs/release/0.99.0-local-browser-mac.md +34 -0
- package/docs/release/0.99.1-action-recovery.md +19 -0
- package/docs/release/open-source-readiness.md +10 -0
- package/package.json +1 -1
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
1
|
import { randomUUID } from "node:crypto";
|
|
3
2
|
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
3
|
import { connect, createServer } from "node:net";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
4
|
import path from "node:path";
|
|
7
5
|
import { setTimeout as delay } from "node:timers/promises";
|
|
8
|
-
import { promisify } from "node:util";
|
|
9
6
|
import { connectGuestBootstrap } from "./guest-bootstrap.js";
|
|
10
7
|
import { ownDesktopAllocation } from "./desktop-session.js";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
})).stdout.trim();
|
|
8
|
+
import { runtimeDocker, runtimeExec, usesLima } from "./local-runtime-host.js";
|
|
9
|
+
import { openLimaTunnel } from "./local-runtime-ssh.js";
|
|
10
|
+
const docker = async (args) => (await runtimeDocker(args, {}, 60_000)).stdout.trim();
|
|
15
11
|
const readLogs = async (id) => {
|
|
16
|
-
const result = await
|
|
12
|
+
const result = await runtimeDocker(["logs", "--tail", "100", id], {}, 10_000);
|
|
17
13
|
return result.stdout + result.stderr;
|
|
18
14
|
};
|
|
19
15
|
/** One VM and an opaque TCP forward to the explicitly selected loopback app. */
|
|
@@ -26,15 +22,17 @@ export async function createLocalFirecrackerDesktop(options) {
|
|
|
26
22
|
options.signal?.throwIfAborted();
|
|
27
23
|
await mkdir(options.outputRoot, { recursive: true, mode: 0o700 });
|
|
28
24
|
// Unix socket paths have a small fixed limit; project paths may be much longer.
|
|
29
|
-
const work = await mkdtemp(
|
|
25
|
+
const work = await mkdtemp("/tmp/humanish-fc-");
|
|
26
|
+
const lima = usesLima();
|
|
30
27
|
const sockets = new Set();
|
|
31
28
|
let container;
|
|
32
29
|
let client;
|
|
33
30
|
const stop = new AbortController();
|
|
34
31
|
const signal = options.signal ? AbortSignal.any([options.signal, stop.signal]) : stop.signal;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
let remoteWork;
|
|
33
|
+
let tunnel;
|
|
34
|
+
let socketRoot = path.join(work, "vm"), cidfile = path.join(work, "container-id");
|
|
35
|
+
let controlSocket = path.join(socketRoot, "vsock.sock");
|
|
38
36
|
const forward = createServer(incoming => {
|
|
39
37
|
if (signal.aborted) {
|
|
40
38
|
incoming.destroy();
|
|
@@ -62,7 +60,7 @@ export async function createLocalFirecrackerDesktop(options) {
|
|
|
62
60
|
options.signal?.removeEventListener("abort", aborted);
|
|
63
61
|
// Docker can create the container before its command's reply is interrupted.
|
|
64
62
|
// Only this invocation's private cidfile is cleanup authority.
|
|
65
|
-
container ??= await readFile(cidfile, "utf8").then(value => /^[a-f0-9]{64}$/.test(value.trim()) ? value.trim() : undefined, () => undefined);
|
|
63
|
+
container ??= await (lima ? runtimeExec("cat", [cidfile]).then(result => result.stdout) : readFile(cidfile, "utf8")).then(value => /^[a-f0-9]{64}$/.test(value.trim()) ? value.trim() : undefined, () => undefined);
|
|
66
64
|
if (container) {
|
|
67
65
|
try {
|
|
68
66
|
await docker(["rm", "--force", "--volumes", container]);
|
|
@@ -73,7 +71,11 @@ export async function createLocalFirecrackerDesktop(options) {
|
|
|
73
71
|
return { status: "unconfirmed", reason: "release_failed" };
|
|
74
72
|
}
|
|
75
73
|
}
|
|
74
|
+
if (tunnel && !await tunnel.close())
|
|
75
|
+
return { status: "unconfirmed", reason: "release_failed" };
|
|
76
76
|
try {
|
|
77
|
+
if (remoteWork)
|
|
78
|
+
await runtimeExec("rm", ["-rf", "--", remoteWork]);
|
|
77
79
|
await rm(work, { recursive: true, force: true });
|
|
78
80
|
}
|
|
79
81
|
catch {
|
|
@@ -83,10 +85,24 @@ export async function createLocalFirecrackerDesktop(options) {
|
|
|
83
85
|
})();
|
|
84
86
|
const aborted = () => { void close(); };
|
|
85
87
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
let uid = process.getuid?.() || 1000, gid = process.getgid?.() || 1000;
|
|
89
|
+
if (lima) {
|
|
90
|
+
remoteWork = (await runtimeExec("mktemp", ["-d", "/tmp/humanish-fc-XXXXXX"])).stdout.trim();
|
|
91
|
+
socketRoot = remoteWork;
|
|
92
|
+
cidfile = `${remoteWork}/container-id`;
|
|
93
|
+
controlSocket = path.join(work, "vsock.sock");
|
|
94
|
+
uid = Number((await runtimeExec("id", ["-u"])).stdout.trim());
|
|
95
|
+
gid = Number((await runtimeExec("id", ["-g"])).stdout.trim());
|
|
96
|
+
tunnel = await openLimaTunnel({ work, socketRoot, appUrl: url, signal });
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
await mkdir(socketRoot, { mode: 0o700 });
|
|
100
|
+
}
|
|
101
|
+
if (!lima)
|
|
102
|
+
await new Promise((resolve, reject) => {
|
|
103
|
+
forward.once("error", reject);
|
|
104
|
+
forward.listen(path.join(socketRoot, "vsock.sock_8000"), () => { forward.off("error", reject); resolve(); });
|
|
105
|
+
});
|
|
90
106
|
const created = await docker(["create", "--rm", "--cidfile", cidfile, "--init", "--user", "0:0", "--read-only", "--cap-drop", "ALL",
|
|
91
107
|
"--cap-add", "NET_ADMIN", "--cap-add", "SETUID", "--cap-add", "SETGID", "--cap-add", "CHOWN",
|
|
92
108
|
"--device", "/dev/kvm", "--device", "/dev/net/tun", "--security-opt", "no-new-privileges",
|
|
@@ -94,7 +110,7 @@ export async function createLocalFirecrackerDesktop(options) {
|
|
|
94
110
|
"--tmpfs", "/tmp:rw,nosuid,nodev,size=16m", "--stop-timeout", "5",
|
|
95
111
|
"--mount", `type=bind,src=${socketRoot},dst=/run/vm`,
|
|
96
112
|
"--mount", "type=volume,dst=/run/state,volume-nocopy",
|
|
97
|
-
options.assets.image, url.port, String(
|
|
113
|
+
options.assets.image, url.port, String(uid), String(gid)]);
|
|
98
114
|
if (!/^[a-f0-9]{64}$/.test(created))
|
|
99
115
|
throw new Error("Docker did not return a container ID.");
|
|
100
116
|
container = created;
|
|
@@ -115,7 +131,7 @@ export async function createLocalFirecrackerDesktop(options) {
|
|
|
115
131
|
if (performance.now() >= deadline)
|
|
116
132
|
throw new Error("Firecracker browser startup timed out.");
|
|
117
133
|
stream = await new Promise(resolve => {
|
|
118
|
-
const socket = connect(
|
|
134
|
+
const socket = connect(controlSocket);
|
|
119
135
|
socket.once("connect", () => resolve(socket));
|
|
120
136
|
socket.once("error", () => { socket.destroy(); resolve(undefined); });
|
|
121
137
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-firecracker-desktop.js","sourceRoot":"","sources":["../src/local-firecracker-desktop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"local-firecracker-desktop.js","sourceRoot":"","sources":["../src/local-firecracker-desktop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const LIMA_INSTANCE = "humanish-runtime";
|
|
2
|
+
export interface RuntimeHostOptions {
|
|
3
|
+
env?: NodeJS.ProcessEnv;
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
platform?: NodeJS.Platform;
|
|
6
|
+
arch?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const usesLima: (options?: RuntimeHostOptions) => boolean;
|
|
9
|
+
export declare function runtimeArchitecture(options?: RuntimeHostOptions): "amd64" | "arm64" | undefined;
|
|
10
|
+
export declare function hostExec(file: string, args: string[], options?: RuntimeHostOptions, timeout?: number): Promise<{
|
|
11
|
+
stdout: string;
|
|
12
|
+
stderr: string;
|
|
13
|
+
}>;
|
|
14
|
+
/** Commands inside the Linux host. Lima carries argv; no shell interpolation. */
|
|
15
|
+
export declare function runtimeExec(file: string, args: string[], options?: RuntimeHostOptions, timeout?: number): Promise<{
|
|
16
|
+
stdout: string;
|
|
17
|
+
stderr: string;
|
|
18
|
+
}>;
|
|
19
|
+
export declare function runtimeDocker(args: string[], options?: RuntimeHostOptions, timeout?: number): Promise<{
|
|
20
|
+
stdout: string;
|
|
21
|
+
stderr: string;
|
|
22
|
+
}>;
|
|
23
|
+
export declare const LIMA_TEMPLATE = "minimumLimaVersion: 2.2.0\nvmType: vz\narch: aarch64\nnestedVirtualization: true\ncpus: 6\nmemory: 8GiB\ndisk: 80GiB\nimages:\n - location: https://cloud.debian.org/images/cloud/trixie/20260712-2537/debian-13-genericcloud-arm64-20260712-2537.qcow2\n arch: aarch64\n digest: sha512:8543d795f2fde630eb66c492f245a8c1da19dedc636e0a8e7b3d0f95920e1a05aa911ef2d82d177d41cc53ced5fccbd2a3945d07fa5e15018914c4d864bb07ed\nmounts: []\ncontainerd:\n system: false\n user: false\nssh:\n forwardAgent: false\nportForwards:\n - guestIP: 0.0.0.0\n guestIPMustBeZero: false\n guestPortRange: [1, 65535]\n proto: any\n ignore: true\nprovision:\n - mode: system\n script: |\n #!/bin/sh\n set -eu\n export DEBIAN_FRONTEND=noninteractive\n apt-get update\n apt-get install -y --no-install-recommends docker.io docker-cli ca-certificates\n systemctl enable --now docker\n";
|
|
24
|
+
export declare function limaStatus(options?: RuntimeHostOptions): Promise<{
|
|
25
|
+
ready: boolean;
|
|
26
|
+
exists: boolean;
|
|
27
|
+
message: string;
|
|
28
|
+
}>;
|
|
29
|
+
export declare function prepareLima(options?: RuntimeHostOptions, progress?: (message: string) => void): Promise<void>;
|
|
30
|
+
/** Copy across the OS boundary explicitly; shared folders cannot carry sockets. */
|
|
31
|
+
export declare function loadRuntimeArchive(archive: string, options: RuntimeHostOptions): Promise<void>;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
const exec = promisify(execFile);
|
|
7
|
+
export const LIMA_INSTANCE = "humanish-runtime";
|
|
8
|
+
export const usesLima = (options = {}) => (options.platform ?? process.platform) === "darwin";
|
|
9
|
+
export function runtimeArchitecture(options = {}) {
|
|
10
|
+
const platform = options.platform ?? process.platform, arch = options.arch ?? process.arch;
|
|
11
|
+
if (platform === "linux" && arch === "x64")
|
|
12
|
+
return "amd64";
|
|
13
|
+
if ((platform === "linux" || platform === "darwin") && arch === "arm64")
|
|
14
|
+
return "arm64";
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
export async function hostExec(file, args, options = {}, timeout = 15_000) {
|
|
18
|
+
return exec(file, args, { env: options.env ?? process.env, timeout, maxBuffer: 1024 * 1024,
|
|
19
|
+
...(options.signal ? { signal: options.signal } : {}) });
|
|
20
|
+
}
|
|
21
|
+
/** Commands inside the Linux host. Lima carries argv; no shell interpolation. */
|
|
22
|
+
export async function runtimeExec(file, args, options = {}, timeout = 15_000) {
|
|
23
|
+
return usesLima(options)
|
|
24
|
+
? hostExec("limactl", ["shell", "--workdir", "/", LIMA_INSTANCE, "--", file, ...args], options, timeout)
|
|
25
|
+
: hostExec(file, args, options, timeout);
|
|
26
|
+
}
|
|
27
|
+
export async function runtimeDocker(args, options = {}, timeout = 15_000) {
|
|
28
|
+
return usesLima(options)
|
|
29
|
+
? runtimeExec("sudo", ["-n", "docker", "--host", "unix:///var/run/docker.sock", ...args], options, timeout)
|
|
30
|
+
: runtimeExec("docker", args, options, timeout);
|
|
31
|
+
}
|
|
32
|
+
// No host mounts, agent forwarding or automatic app-port discovery. Per-study
|
|
33
|
+
// OpenSSH forwards grant just the selected app and browser-control sockets.
|
|
34
|
+
export const LIMA_TEMPLATE = `minimumLimaVersion: 2.2.0
|
|
35
|
+
vmType: vz
|
|
36
|
+
arch: aarch64
|
|
37
|
+
nestedVirtualization: true
|
|
38
|
+
cpus: 6
|
|
39
|
+
memory: 8GiB
|
|
40
|
+
disk: 80GiB
|
|
41
|
+
images:
|
|
42
|
+
- location: https://cloud.debian.org/images/cloud/trixie/20260712-2537/debian-13-genericcloud-arm64-20260712-2537.qcow2
|
|
43
|
+
arch: aarch64
|
|
44
|
+
digest: sha512:8543d795f2fde630eb66c492f245a8c1da19dedc636e0a8e7b3d0f95920e1a05aa911ef2d82d177d41cc53ced5fccbd2a3945d07fa5e15018914c4d864bb07ed
|
|
45
|
+
mounts: []
|
|
46
|
+
containerd:
|
|
47
|
+
system: false
|
|
48
|
+
user: false
|
|
49
|
+
ssh:
|
|
50
|
+
forwardAgent: false
|
|
51
|
+
portForwards:
|
|
52
|
+
- guestIP: 0.0.0.0
|
|
53
|
+
guestIPMustBeZero: false
|
|
54
|
+
guestPortRange: [1, 65535]
|
|
55
|
+
proto: any
|
|
56
|
+
ignore: true
|
|
57
|
+
provision:
|
|
58
|
+
- mode: system
|
|
59
|
+
script: |
|
|
60
|
+
#!/bin/sh
|
|
61
|
+
set -eu
|
|
62
|
+
export DEBIAN_FRONTEND=noninteractive
|
|
63
|
+
apt-get update
|
|
64
|
+
apt-get install -y --no-install-recommends docker.io docker-cli ca-certificates
|
|
65
|
+
systemctl enable --now docker
|
|
66
|
+
`;
|
|
67
|
+
export async function limaStatus(options = {}) {
|
|
68
|
+
const chip = (await hostExec("sysctl", ["-n", "machdep.cpu.brand_string"], options)).stdout.trim();
|
|
69
|
+
const generation = /^Apple M(\d+)/.exec(chip);
|
|
70
|
+
if (!generation || Number(generation[1]) < 3)
|
|
71
|
+
throw new Error("Local Firecracker on Mac requires an M3 or newer Apple Silicon Mac with nested virtualization.");
|
|
72
|
+
let version;
|
|
73
|
+
try {
|
|
74
|
+
version = (await hostExec("limactl", ["--version"], options)).stdout;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
throw new Error("Install Lima 2.2 or newer (brew install lima), then run humanish runtime setup. Docker Desktop is not required.");
|
|
78
|
+
}
|
|
79
|
+
const match = /\b(\d+)\.(\d+)\.\d+/.exec(version);
|
|
80
|
+
if (!match || Number(match[1]) < 2 || (Number(match[1]) === 2 && Number(match[2]) < 2))
|
|
81
|
+
throw new Error("Update Lima to 2.2 or newer (brew upgrade lima), then run humanish runtime setup.");
|
|
82
|
+
const output = (await hostExec("limactl", ["list", "--json"], options)).stdout.trim();
|
|
83
|
+
const instance = output.split("\n").filter(Boolean).map(line => JSON.parse(line))
|
|
84
|
+
.find(value => value.name === LIMA_INSTANCE);
|
|
85
|
+
if (instance && (instance.name !== LIMA_INSTANCE || instance.vmType !== "vz" || instance.arch !== "aarch64"))
|
|
86
|
+
throw new Error(`The existing ${LIMA_INSTANCE} Lima instance is incompatible. Humanish will not replace it.`);
|
|
87
|
+
return { ready: instance?.status === "Running", exists: !!instance,
|
|
88
|
+
message: instance ? "The Humanish Lima host is stopped. Run humanish runtime setup to start it."
|
|
89
|
+
: "Run humanish runtime setup to create the Humanish Lima host and install the browser runtime." };
|
|
90
|
+
}
|
|
91
|
+
export async function prepareLima(options = {}, progress) {
|
|
92
|
+
const status = await limaStatus(options);
|
|
93
|
+
if (status.ready)
|
|
94
|
+
return;
|
|
95
|
+
progress?.(status.exists ? "Starting the Humanish Lima host…" : "Preparing the Humanish Lima host (first setup downloads Linux and Docker)…");
|
|
96
|
+
if (status.exists) {
|
|
97
|
+
await hostExec("limactl", ["start", "--tty=false", LIMA_INSTANCE], options, 15 * 60_000);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const work = await mkdtemp(path.join(tmpdir(), "humanish-lima-"));
|
|
101
|
+
try {
|
|
102
|
+
const file = path.join(work, "host.yaml");
|
|
103
|
+
await writeFile(file, LIMA_TEMPLATE, { mode: 0o600 });
|
|
104
|
+
await hostExec("limactl", ["start", "--tty=false", "--name", LIMA_INSTANCE, file], options, 15 * 60_000);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
await rm(work, { recursive: true, force: true });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Copy across the OS boundary explicitly; shared folders cannot carry sockets. */
|
|
112
|
+
export async function loadRuntimeArchive(archive, options) {
|
|
113
|
+
if (!usesLima(options)) {
|
|
114
|
+
await runtimeDocker(["load", "--input", archive], options, 10 * 60_000);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const work = (await runtimeExec("mktemp", ["-d", "/tmp/humanish-image-XXXXXX"], options)).stdout.trim();
|
|
118
|
+
try {
|
|
119
|
+
await hostExec("limactl", ["copy", archive, `${LIMA_INSTANCE}:${work}/runtime.tar.gz`], options, 10 * 60_000);
|
|
120
|
+
await runtimeDocker(["load", "--input", `${work}/runtime.tar.gz`], options, 10 * 60_000);
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
// Cancellation must not skip removal of this invocation's copied archive.
|
|
124
|
+
const { signal: _signal, ...cleanup } = options;
|
|
125
|
+
await runtimeExec("rm", ["-rf", "--", work], cleanup);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=local-runtime-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-runtime-host.js","sourceRoot":"","sources":["../src/local-runtime-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { LocalRuntimeRelease } from "./local-runtime.js";
|
|
2
2
|
/** Updated with the verified, public runtime artifact before release. */
|
|
3
3
|
export declare const LOCAL_RUNTIME_RELEASE: LocalRuntimeRelease;
|
|
4
|
+
/** Architecture-specific artifacts; each retains matching sources and notices. */
|
|
5
|
+
export declare const LOCAL_RUNTIME_RELEASES: Partial<Record<"amd64" | "arm64", LocalRuntimeRelease>>;
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
/** Updated with the verified, public runtime artifact before release. */
|
|
2
2
|
export const LOCAL_RUNTIME_RELEASE = {
|
|
3
|
-
url: "https://github.com/danielgwilson/humanish/releases/download/runtime-2026.09.24.
|
|
4
|
-
sha256: "
|
|
5
|
-
bytes:
|
|
6
|
-
image: "sha256:
|
|
3
|
+
url: "https://github.com/danielgwilson/humanish/releases/download/runtime-2026.09.24.3/runtime-linux-amd64.tar.gz",
|
|
4
|
+
sha256: "936e4bdc5c844347c6d6ae2149f1f96cfcabab0c33c8dd7fb3ae1b74b3b91812",
|
|
5
|
+
bytes: 596154336,
|
|
6
|
+
image: "sha256:062f7e9eb8096f6e48906ec0d277c3a9d086ef4ebd275aa2efc43edcd86e5349"
|
|
7
|
+
};
|
|
8
|
+
/** Architecture-specific artifacts; each retains matching sources and notices. */
|
|
9
|
+
export const LOCAL_RUNTIME_RELEASES = {
|
|
10
|
+
amd64: LOCAL_RUNTIME_RELEASE,
|
|
11
|
+
arm64: {
|
|
12
|
+
url: "https://github.com/danielgwilson/humanish/releases/download/runtime-2026.09.24.4/runtime-linux-arm64.tar.gz",
|
|
13
|
+
sha256: "9b396d1d222917ffbac21ae448340fdcc563a9b2f757682d9806248941125cda",
|
|
14
|
+
bytes: 582659561,
|
|
15
|
+
image: "sha256:ed92f65a1f5b064952ea8b289f6bc1e7b5a9babe15ed1c7efec49e395e86eead"
|
|
16
|
+
}
|
|
7
17
|
};
|
|
8
18
|
//# sourceMappingURL=local-runtime-release.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-runtime-release.js","sourceRoot":"","sources":["../src/local-runtime-release.ts"],"names":[],"mappings":"AAEA,yEAAyE;AACzE,MAAM,CAAC,MAAM,qBAAqB,GAAwB;IACxD,GAAG,EAAE,6GAA6G;IAClH,MAAM,EAAE,kEAAkE;IAC1E,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,yEAAyE;CACjF,CAAC"}
|
|
1
|
+
{"version":3,"file":"local-runtime-release.js","sourceRoot":"","sources":["../src/local-runtime-release.ts"],"names":[],"mappings":"AAEA,yEAAyE;AACzE,MAAM,CAAC,MAAM,qBAAqB,GAAwB;IACxD,GAAG,EAAE,6GAA6G;IAClH,MAAM,EAAE,kEAAkE;IAC1E,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,yEAAyE;CACjF,CAAC;AAEF,kFAAkF;AAClF,MAAM,CAAC,MAAM,sBAAsB,GAA4D;IAC7F,KAAK,EAAE,qBAAqB;IAC5B,KAAK,EAAE;QACL,GAAG,EAAE,6GAA6G;QAClH,MAAM,EAAE,kEAAkE;QAC1E,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,yEAAyE;KACjF;CACF,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Standard OpenSSH streamlocal forwarding; no browser-control protocol here. */
|
|
2
|
+
export declare function openRuntimeSshTunnel(options: {
|
|
3
|
+
config: string;
|
|
4
|
+
destination: string;
|
|
5
|
+
localSocket: string;
|
|
6
|
+
remoteSocket: string;
|
|
7
|
+
appSocket: string;
|
|
8
|
+
appHost: string;
|
|
9
|
+
appPort: number;
|
|
10
|
+
signal: AbortSignal;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
close(): Promise<boolean>;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function openLimaTunnel(options: {
|
|
15
|
+
work: string;
|
|
16
|
+
socketRoot: string;
|
|
17
|
+
appUrl: URL;
|
|
18
|
+
signal: AbortSignal;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
close(): Promise<boolean>;
|
|
21
|
+
}>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
4
|
+
import { hostExec, LIMA_INSTANCE } from "./local-runtime-host.js";
|
|
5
|
+
/** Standard OpenSSH streamlocal forwarding; no browser-control protocol here. */
|
|
6
|
+
export async function openRuntimeSshTunnel(options) {
|
|
7
|
+
options.signal.throwIfAborted();
|
|
8
|
+
const child = spawn("ssh", ["-F", options.config, "-T", "-a", "-o", "BatchMode=yes",
|
|
9
|
+
"-o", "ControlMaster=no", "-o", "ControlPath=none", "-o", "ExitOnForwardFailure=yes",
|
|
10
|
+
"-o", "ConnectTimeout=15", "-o", "StreamLocalBindMask=0177",
|
|
11
|
+
"-L", `${options.localSocket}:${options.remoteSocket}`,
|
|
12
|
+
"-R", `${options.appSocket}:${options.appHost}:${options.appPort}`,
|
|
13
|
+
options.destination, "sh -c 'printf \"HUMANISH_SSH_READY\\n\"; exec cat'"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
14
|
+
let exited = false;
|
|
15
|
+
const closed = new Promise(resolve => child.once("close", () => { exited = true; resolve(); }));
|
|
16
|
+
child.stdin.on("error", () => { });
|
|
17
|
+
let closing;
|
|
18
|
+
const close = () => closing ??= (async () => {
|
|
19
|
+
options.signal.removeEventListener("abort", aborted);
|
|
20
|
+
// EOF also closes the remote cat if the parent dies unexpectedly. Do not
|
|
21
|
+
// detach/multiplex this SSH child into Lima's persistent control connection.
|
|
22
|
+
child.stdin.end();
|
|
23
|
+
await Promise.race([closed, delay(1000)]);
|
|
24
|
+
if (!exited)
|
|
25
|
+
child.kill("SIGTERM");
|
|
26
|
+
await Promise.race([closed, delay(1000)]);
|
|
27
|
+
if (!exited)
|
|
28
|
+
child.kill("SIGKILL");
|
|
29
|
+
await Promise.race([closed, delay(1000)]);
|
|
30
|
+
return exited;
|
|
31
|
+
})();
|
|
32
|
+
const aborted = () => { void close(); };
|
|
33
|
+
options.signal.addEventListener("abort", aborted, { once: true });
|
|
34
|
+
try {
|
|
35
|
+
await new Promise((resolve, reject) => {
|
|
36
|
+
const timer = setTimeout(() => finish(new Error("Lima SSH forwarding timed out.")), 20_000);
|
|
37
|
+
let bytes = 0, output = "", ready = false;
|
|
38
|
+
const finish = (error) => {
|
|
39
|
+
if (ready)
|
|
40
|
+
return;
|
|
41
|
+
ready = true;
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
if (error)
|
|
44
|
+
reject(error);
|
|
45
|
+
else
|
|
46
|
+
resolve();
|
|
47
|
+
};
|
|
48
|
+
child.once("error", () => finish(new Error("Could not start SSH for the Lima browser.")));
|
|
49
|
+
child.once("close", () => finish(new Error("Lima SSH forwarding closed before it was ready.")));
|
|
50
|
+
child.stderr.on("data", (data) => {
|
|
51
|
+
bytes += data.length;
|
|
52
|
+
if (bytes > 64 * 1024) {
|
|
53
|
+
finish(new Error("Lima SSH forwarding failed."));
|
|
54
|
+
void close();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
child.stdout.on("data", (data) => {
|
|
58
|
+
bytes += data.length;
|
|
59
|
+
if (bytes > 64 * 1024) {
|
|
60
|
+
finish(new Error("Lima SSH forwarding failed."));
|
|
61
|
+
void close();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (!ready) {
|
|
65
|
+
output += data.toString();
|
|
66
|
+
if (output === "HUMANISH_SSH_READY\n")
|
|
67
|
+
finish();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
options.signal.throwIfAborted();
|
|
72
|
+
return { close };
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
await close();
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export async function openLimaTunnel(options) {
|
|
80
|
+
const settings = await hostExec("limactl", ["list", "--format={{.SSHConfigFile}}", LIMA_INSTANCE], { signal: options.signal });
|
|
81
|
+
const config = settings.stdout.trim();
|
|
82
|
+
if (!path.isAbsolute(config))
|
|
83
|
+
throw new Error("Lima did not return its SSH configuration path.");
|
|
84
|
+
return openRuntimeSshTunnel({ config, destination: `lima-${LIMA_INSTANCE}`,
|
|
85
|
+
localSocket: path.join(options.work, "vsock.sock"), remoteSocket: `${options.socketRoot}/vsock.sock`,
|
|
86
|
+
appSocket: `${options.socketRoot}/vsock.sock_8000`, appHost: options.appUrl.hostname,
|
|
87
|
+
appPort: Number(options.appUrl.port), signal: options.signal });
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=local-runtime-ssh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-runtime-ssh.js","sourceRoot":"","sources":["../src/local-runtime-ssh.ts"],"names":[],"mappings":"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"}
|
package/dist/local-runtime.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { LocalFirecrackerAssets } from "./local-firecracker-desktop.js";
|
|
2
|
+
import { type RuntimeHostOptions } from "./local-runtime-host.js";
|
|
2
3
|
export interface LocalRuntimeRelease {
|
|
3
4
|
url: string;
|
|
4
5
|
sha256: string;
|
|
@@ -11,9 +12,7 @@ export interface LocalRuntimeStatus {
|
|
|
11
12
|
message: string;
|
|
12
13
|
assets?: LocalFirecrackerAssets;
|
|
13
14
|
}
|
|
14
|
-
interface RuntimeOptions {
|
|
15
|
-
env?: NodeJS.ProcessEnv;
|
|
16
|
-
signal?: AbortSignal;
|
|
15
|
+
interface RuntimeOptions extends RuntimeHostOptions {
|
|
17
16
|
progress?: (message: string) => void;
|
|
18
17
|
/** Explicit source-build/test override; never provided by a participant. */
|
|
19
18
|
release?: LocalRuntimeRelease;
|
package/dist/local-runtime.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
1
|
import { createHash } from "node:crypto";
|
|
3
2
|
import { createWriteStream } from "node:fs";
|
|
4
3
|
import { access, mkdtemp, rm } from "node:fs/promises";
|
|
@@ -6,44 +5,59 @@ import { tmpdir } from "node:os";
|
|
|
6
5
|
import path from "node:path";
|
|
7
6
|
import { Readable, Transform } from "node:stream";
|
|
8
7
|
import { pipeline } from "node:stream/promises";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
...(options.signal ? { signal: options.signal } : {}) })).stdout.trim();
|
|
8
|
+
import { LOCAL_RUNTIME_RELEASES } from "./local-runtime-release.js";
|
|
9
|
+
import { limaStatus, loadRuntimeArchive, prepareLima, runtimeArchitecture, runtimeDocker, runtimeExec, usesLima } from "./local-runtime-host.js";
|
|
10
|
+
const docker = async (args, options) => (await runtimeDocker(args, options)).stdout.trim();
|
|
11
|
+
const runtimeRelease = (options) => options.release ?? LOCAL_RUNTIME_RELEASES[runtimeArchitecture(options) ?? "amd64"];
|
|
14
12
|
/** Read-only host and cache inspection. Never pulls an image or starts a container. */
|
|
15
13
|
export async function localRuntimeStatus(options = {}) {
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const architecture = runtimeArchitecture(options), lima = usesLima(options);
|
|
15
|
+
if (!architecture)
|
|
16
|
+
return { ok: false, installed: false, message: "Local browsers need Linux x64/ARM64 or an M3-or-newer Mac with Lima." };
|
|
17
|
+
if (lima) {
|
|
18
|
+
try {
|
|
19
|
+
const status = await limaStatus(options);
|
|
20
|
+
if (!status.ready)
|
|
21
|
+
return { ok: true, installed: false, message: status.message };
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return { ok: false, installed: false, message: error.message };
|
|
25
|
+
}
|
|
18
26
|
}
|
|
19
27
|
const env = options.env ?? process.env;
|
|
20
|
-
if (env.DOCKER_HOST && !env.DOCKER_HOST.startsWith("unix://")) {
|
|
28
|
+
if (!lima && env.DOCKER_HOST && !env.DOCKER_HOST.startsWith("unix://")) {
|
|
21
29
|
return { ok: false, installed: false, message: "Local browsers need Docker on this machine; a remote DOCKER_HOST cannot reach the local app and control socket." };
|
|
22
30
|
}
|
|
23
31
|
try {
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
if (lima)
|
|
33
|
+
await runtimeExec("sh", ["-c", "test -c /dev/kvm && test -c /dev/net/tun"], options);
|
|
34
|
+
else {
|
|
35
|
+
await access("/dev/kvm");
|
|
36
|
+
await access("/dev/net/tun");
|
|
37
|
+
}
|
|
26
38
|
}
|
|
27
39
|
catch {
|
|
28
40
|
return { ok: false, installed: false, message: "Local browsers need Linux KVM (/dev/kvm) and TUN (/dev/net/tun). Enable virtualization on this host." };
|
|
29
41
|
}
|
|
30
42
|
try {
|
|
31
|
-
if (env.DOCKER_CONTEXT || !env.DOCKER_HOST) {
|
|
43
|
+
if (!lima && (env.DOCKER_CONTEXT || !env.DOCKER_HOST)) {
|
|
32
44
|
const endpoint = JSON.parse(await docker(["context", "inspect", "--format", "{{json .Endpoints.docker.Host}}"], options));
|
|
33
45
|
if (typeof endpoint !== "string" || !endpoint.startsWith("unix://")) {
|
|
34
46
|
return { ok: false, installed: false, message: "Select a local Docker context. Remote Docker cannot reach the local app and control socket." };
|
|
35
47
|
}
|
|
36
48
|
}
|
|
37
49
|
const info = JSON.parse(await docker(["info", "--format", "{{json .}}"], options));
|
|
38
|
-
if (info.OSType !== "linux" || !["x86_64", "amd64"].includes(info.Architecture) ||
|
|
50
|
+
if (info.OSType !== "linux" || !(architecture === "amd64" ? ["x86_64", "amd64"] : ["aarch64", "arm64"]).includes(info.Architecture) ||
|
|
39
51
|
info.SecurityOptions?.some((value) => value.includes("rootless"))) {
|
|
40
|
-
return { ok: false, installed: false, message: "Local browsers need a local, rootful Linux
|
|
52
|
+
return { ok: false, installed: false, message: "Local browsers need a local, rootful Linux Docker engine of the matching architecture with KVM access." };
|
|
41
53
|
}
|
|
42
54
|
}
|
|
43
55
|
catch {
|
|
44
|
-
return { ok: false, installed: false, message:
|
|
56
|
+
return { ok: false, installed: false, message: lima
|
|
57
|
+
? "Docker is unavailable inside the Humanish Lima host. Check limactl shell humanish-runtime -- sudo systemctl status docker; Docker Desktop is not used."
|
|
58
|
+
: "Docker is unavailable. Install/start Docker Engine and give your account access, then run humanish runtime setup." };
|
|
45
59
|
}
|
|
46
|
-
const release = options
|
|
60
|
+
const release = runtimeRelease(options);
|
|
47
61
|
const reference = env.HUMANISH_LOCAL_RUNTIME_IMAGE?.trim() || release?.image;
|
|
48
62
|
if (!reference)
|
|
49
63
|
return { ok: false, installed: false, message: "This build does not include a published local runtime. Source builds can set HUMANISH_LOCAL_RUNTIME_IMAGE." };
|
|
@@ -59,7 +73,7 @@ export async function localRuntimeStatus(options = {}) {
|
|
|
59
73
|
const image = images[0];
|
|
60
74
|
const labels = image?.Config?.Labels ?? {};
|
|
61
75
|
const revision = labels["to.humanish.runtime.revision"];
|
|
62
|
-
if (image?.Os !== "linux" || image.Architecture !==
|
|
76
|
+
if (image?.Os !== "linux" || image.Architecture !== architecture || labels["to.humanish.runtime.api"] !== "1" ||
|
|
63
77
|
typeof revision !== "string" || !/^guest-api1-[a-f0-9]{64}$/.test(revision)) {
|
|
64
78
|
return { ok: false, installed: false, message: "The cached image is not a compatible Humanish local browser runtime." };
|
|
65
79
|
}
|
|
@@ -68,12 +82,16 @@ export async function localRuntimeStatus(options = {}) {
|
|
|
68
82
|
}
|
|
69
83
|
/** Docker owns the installed image/cache. The archive is verified before docker load. */
|
|
70
84
|
export async function prepareLocalRuntime(options = {}) {
|
|
85
|
+
if (!runtimeArchitecture(options))
|
|
86
|
+
throw new Error((await localRuntimeStatus(options)).message);
|
|
87
|
+
if (usesLima(options))
|
|
88
|
+
await prepareLima(options, options.progress);
|
|
71
89
|
const before = await localRuntimeStatus(options);
|
|
72
90
|
if (!before.ok)
|
|
73
91
|
throw new Error(before.message);
|
|
74
92
|
if (before.assets)
|
|
75
93
|
return before.assets;
|
|
76
|
-
const release = options
|
|
94
|
+
const release = runtimeRelease(options);
|
|
77
95
|
if (!release)
|
|
78
96
|
throw new Error(before.message);
|
|
79
97
|
options.progress?.(`Downloading local browser runtime (${Math.ceil(release.bytes / 1024 / 1024)} MiB)…`);
|
|
@@ -100,7 +118,7 @@ export async function prepareLocalRuntime(options = {}) {
|
|
|
100
118
|
if (bytes !== release.bytes || hash.digest("hex") !== release.sha256)
|
|
101
119
|
throw new Error("Runtime archive integrity check failed; it was not installed.");
|
|
102
120
|
options.progress?.("Installing the local browser image…");
|
|
103
|
-
await
|
|
121
|
+
await loadRuntimeArchive(archive, { ...options, signal });
|
|
104
122
|
const after = await localRuntimeStatus(options);
|
|
105
123
|
if (!after.assets)
|
|
106
124
|
throw new Error("Downloaded runtime did not provide the expected image. Run humanish runtime setup to retry.");
|