agentroam 0.2.0-preview.7 → 0.2.0-preview.9
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 +42 -8
- package/bin/agentroam.mjs +10 -1
- package/bin/node-preflight.mjs +138 -0
- package/dist/args.js +14 -1
- package/dist/args.test.js +27 -0
- package/dist/cli.js +59 -7
- package/dist/cloudflared/bundled-asset.test.js +2 -2
- package/dist/codex-runtime-manager.js +293 -0
- package/dist/codex-runtime-manager.test.js +192 -0
- package/dist/install-script-contract.test.js +41 -0
- package/dist/node-preflight.test.js +109 -0
- package/dist/node-runtime-manager.js +251 -0
- package/dist/node-runtime-manager.test.js +184 -0
- package/dist/opencode-runtime-manager.js +207 -0
- package/dist/opencode-runtime-manager.test.js +79 -0
- package/dist/platform-packages.js +1 -1
- package/dist/platform.js +8 -4
- package/dist/platform.test.js +17 -2
- package/dist/power/sleep-inhibitor.js +151 -0
- package/dist/power/sleep-inhibitor.test.js +104 -0
- package/dist/runtime-manager.js +50 -2
- package/dist/runtime-manager.test.js +38 -0
- package/dist/service/macos-launch-agent.js +250 -0
- package/dist/service/macos-launch-agent.test.js +197 -0
- package/dist/service/runtime-state.js +40 -0
- package/dist/service/runtime-state.test.js +43 -0
- package/dist/service/service-command.js +100 -0
- package/dist/service/service-command.test.js +102 -0
- package/dist/service/service-controller.js +27 -0
- package/dist/service/service-files.js +78 -0
- package/dist/service/service-files.test.js +48 -0
- package/dist/service/windows-service-host.js +53 -0
- package/dist/service/windows-service-host.test.js +46 -0
- package/dist/service/windows-task-service.js +275 -0
- package/dist/service/windows-task-service.test.js +95 -0
- package/dist/tunnel/cloudflare-provider.js +4 -1
- package/dist/tunnel/cloudflare-provider.test.js +13 -1
- package/dist/tunnel/public-readiness.js +1 -1
- package/dist/tunnel/relay-orchestrator.js +4 -0
- package/dist/tunnel/relay-orchestrator.test.js +11 -0
- package/install/README.md +45 -0
- package/install/install-agentroam.ps1 +175 -0
- package/install/install-agentroam.sh +199 -0
- package/package.json +8 -7
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { mkdtemp, mkdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { resolveServicePaths, writePrivateText } from "./service-files.js";
|
|
6
|
+
import { WindowsTaskService, buildRegisterTaskScript, quoteWindowsCommandLineArgument, resolveWindowsServiceHost, } from "./windows-task-service.js";
|
|
7
|
+
describe("WindowsTaskService", () => {
|
|
8
|
+
it("registers, stops, starts, and uninstalls a current-user task", async () => {
|
|
9
|
+
const home = await mkdtemp(resolve(tmpdir(), "agentroam-windows-task-"));
|
|
10
|
+
const paths = resolveServicePaths(home, resolve(home, "data"));
|
|
11
|
+
const value = config(home, paths.dataDir);
|
|
12
|
+
await mkdir(value.roots[0], { recursive: true });
|
|
13
|
+
let installed = false;
|
|
14
|
+
let running = false;
|
|
15
|
+
let processRunning = false;
|
|
16
|
+
let pid = 800;
|
|
17
|
+
const scripts = [];
|
|
18
|
+
const runner = async (command, args) => {
|
|
19
|
+
expect(command).toBe("powershell.exe");
|
|
20
|
+
const script = Buffer.from(args.at(-1), "base64").toString("utf16le");
|
|
21
|
+
scripts.push(script);
|
|
22
|
+
if (script.includes("Register-ScheduledTask"))
|
|
23
|
+
installed = true;
|
|
24
|
+
if (script.includes("Start-ScheduledTask")) {
|
|
25
|
+
running = true;
|
|
26
|
+
processRunning = true;
|
|
27
|
+
pid++;
|
|
28
|
+
await writePrivateText(paths.statePath, JSON.stringify({
|
|
29
|
+
status: "ready", pid, version: value.version, startedAt: "x", updatedAt: "x",
|
|
30
|
+
accessUrl: "https://ready.example/web",
|
|
31
|
+
}));
|
|
32
|
+
await writePrivateText(paths.urlPath, "https://ready.example/web\n");
|
|
33
|
+
}
|
|
34
|
+
if (script.includes("Stop-ScheduledTask")) {
|
|
35
|
+
running = false;
|
|
36
|
+
processRunning = false;
|
|
37
|
+
}
|
|
38
|
+
if (script.includes("Unregister-ScheduledTask"))
|
|
39
|
+
installed = false;
|
|
40
|
+
if (script.includes("Get-ScheduledTask")) {
|
|
41
|
+
return { code: 0, stdout: JSON.stringify({ installed, running }), stderr: "" };
|
|
42
|
+
}
|
|
43
|
+
return { code: 0, stdout: "", stderr: "" };
|
|
44
|
+
};
|
|
45
|
+
const service = new WindowsTaskService({
|
|
46
|
+
homeDir: home,
|
|
47
|
+
runner,
|
|
48
|
+
processExists: () => processRunning,
|
|
49
|
+
validateConfig: async () => undefined,
|
|
50
|
+
readyTimeoutMs: 50,
|
|
51
|
+
stopTimeoutMs: 50,
|
|
52
|
+
pollIntervalMs: 1,
|
|
53
|
+
});
|
|
54
|
+
const installedResult = await service.install(value);
|
|
55
|
+
expect(installedResult.state).toMatchObject({ status: "ready", pid: 801 });
|
|
56
|
+
expect(installedResult.definition).toBe("AgentRoam");
|
|
57
|
+
expect(await service.url()).toBe("https://ready.example/web");
|
|
58
|
+
await service.stop();
|
|
59
|
+
expect((await service.status()).running).toBe(false);
|
|
60
|
+
await service.start();
|
|
61
|
+
expect((await service.status()).running).toBe(true);
|
|
62
|
+
await writePrivateText(resolve(paths.dataDir, "data/keep.txt"), "keep");
|
|
63
|
+
await writePrivateText(paths.stdoutPath, "keep logs");
|
|
64
|
+
const result = await service.uninstall();
|
|
65
|
+
expect(result).toEqual({ removed: true, preservedDataDir: paths.dataDir });
|
|
66
|
+
expect(await readFile(resolve(paths.dataDir, "data/keep.txt"), "utf8")).toBe("keep");
|
|
67
|
+
expect(await readFile(paths.stdoutPath, "utf8")).toBe("keep logs");
|
|
68
|
+
expect(scripts.some((script) => script.includes("New-ScheduledTaskPrincipal") && script.includes("RunLevel Limited"))).toBe(true);
|
|
69
|
+
expect(scripts.some((script) => script.includes("cmd.exe") || script.includes("ES_DISPLAY_REQUIRED"))).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
it("builds a safely quoted task action for paths with spaces and apostrophes", () => {
|
|
72
|
+
const value = config("C:\\Users\\O'Brien", "C:\\Users\\O'Brien\\.agentroam");
|
|
73
|
+
const host = resolveWindowsServiceHost(value.cliPath);
|
|
74
|
+
const script = buildRegisterTaskScript(value, host, "C:\\Users\\O'Brien\\config.json");
|
|
75
|
+
expect(host).toBe("C:\\Agent Roam\\node_modules\\agentroam\\dist\\service\\windows-service-host.js");
|
|
76
|
+
expect(script).toContain("O''Brien");
|
|
77
|
+
expect(script).toContain("New-ScheduledTaskAction");
|
|
78
|
+
expect(script).toContain("RestartCount 999");
|
|
79
|
+
expect(quoteWindowsCommandLineArgument("C:\\path with spaces\\")).toBe('"C:\\path with spaces\\\\"');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
function config(home, dataDir) {
|
|
83
|
+
return {
|
|
84
|
+
version: "0.2.0-preview.9",
|
|
85
|
+
nodePath: "C:\\Node Runtime\\node.exe",
|
|
86
|
+
cliPath: "C:\\Agent Roam\\node_modules\\agentroam\\bin\\agentroam.mjs",
|
|
87
|
+
roots: [resolve(home, "workspace")],
|
|
88
|
+
port: null,
|
|
89
|
+
relay: "auto",
|
|
90
|
+
tunnelCommand: null,
|
|
91
|
+
localOnly: false,
|
|
92
|
+
dataDir,
|
|
93
|
+
installedAt: "2026-09-04T00:00:00.000Z",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -6,7 +6,7 @@ export class CloudflareTunnelProvider {
|
|
|
6
6
|
this.executable = executable;
|
|
7
7
|
}
|
|
8
8
|
async start({ localUrl, signal, log }) {
|
|
9
|
-
const child = spawn(this.executable,
|
|
9
|
+
const child = spawn(this.executable, buildCloudflaredArguments(localUrl), {
|
|
10
10
|
stdio: ["ignore", "pipe", "pipe"],
|
|
11
11
|
windowsHide: true,
|
|
12
12
|
detached: process.platform !== "win32",
|
|
@@ -26,6 +26,9 @@ export class CloudflareTunnelProvider {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
export function buildCloudflaredArguments(localUrl) {
|
|
30
|
+
return ["tunnel", "--no-autoupdate", "--protocol", "http2", "--url", localUrl];
|
|
31
|
+
}
|
|
29
32
|
export function waitForConnectedUrl(child, log, timeoutMs) {
|
|
30
33
|
return new Promise((resolvePromise, reject) => {
|
|
31
34
|
const buffers = { stdout: "", stderr: "" };
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import { PassThrough } from "node:stream";
|
|
3
3
|
import { describe, expect, it, vi } from "vitest";
|
|
4
|
-
import { waitForConnectedUrl } from "./cloudflare-provider.js";
|
|
4
|
+
import { buildCloudflaredArguments, waitForConnectedUrl } from "./cloudflare-provider.js";
|
|
5
|
+
describe("buildCloudflaredArguments", () => {
|
|
6
|
+
it("uses HTTP/2 so launchd tunnels do not depend on outbound UDP", () => {
|
|
7
|
+
expect(buildCloudflaredArguments("http://127.0.0.1:3000")).toEqual([
|
|
8
|
+
"tunnel",
|
|
9
|
+
"--no-autoupdate",
|
|
10
|
+
"--protocol",
|
|
11
|
+
"http2",
|
|
12
|
+
"--url",
|
|
13
|
+
"http://127.0.0.1:3000",
|
|
14
|
+
]);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
5
17
|
describe("waitForConnectedUrl", () => {
|
|
6
18
|
it("waits for both the public URL and a registered tunnel connection", async () => {
|
|
7
19
|
const { child, stderr } = fakeChild();
|
|
@@ -22,7 +22,7 @@ export async function waitForPublicReadiness(publicUrl, options = {}) {
|
|
|
22
22
|
try {
|
|
23
23
|
const response = await fetchImpl(endpoint, {
|
|
24
24
|
cache: "no-store",
|
|
25
|
-
headers: { "user-agent": "agentroam-readiness/0.2.0-preview.
|
|
25
|
+
headers: { "user-agent": "agentroam-readiness/0.2.0-preview.9" },
|
|
26
26
|
signal: options.signal,
|
|
27
27
|
});
|
|
28
28
|
if (response.ok) {
|
|
@@ -27,6 +27,10 @@ export async function selectRelay(options) {
|
|
|
27
27
|
break;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
if (options.allowLanFallback === false) {
|
|
31
|
+
const detail = failures.map(({ provider, message }) => `${provider}: ${message}`).join("; ");
|
|
32
|
+
throw new Error(`public relay unavailable${detail ? ` (${detail})` : ""}`);
|
|
33
|
+
}
|
|
30
34
|
return { tunnel: null, publicUrl: options.lanUrl, provider: "lan", failures };
|
|
31
35
|
}
|
|
32
36
|
async function createProvider(name, options) {
|
|
@@ -27,6 +27,16 @@ describe("selectRelay", () => {
|
|
|
27
27
|
expect(result.publicUrl).toBe("http://10.0.0.2:43210");
|
|
28
28
|
expect(result.failures).toHaveLength(2);
|
|
29
29
|
});
|
|
30
|
+
it("fails instead of leaving a background service permanently on LAN", async () => {
|
|
31
|
+
const cloudflare = fakeProvider("https://bad.trycloudflare.com");
|
|
32
|
+
const pinggy = fakeProvider("https://bad.run.pinggy-free.link");
|
|
33
|
+
await expect(selectRelay({
|
|
34
|
+
...options("auto", { cloudflare, pinggy }, async () => { throw new Error("blocked"); }),
|
|
35
|
+
allowLanFallback: false,
|
|
36
|
+
})).rejects.toThrow("public relay unavailable (cloudflare: blocked; pinggy: blocked)");
|
|
37
|
+
expect(cloudflare.handle.close).toHaveBeenCalledOnce();
|
|
38
|
+
expect(pinggy.handle.close).toHaveBeenCalledOnce();
|
|
39
|
+
});
|
|
30
40
|
it("bypasses every public provider in local-only mode", async () => {
|
|
31
41
|
const cloudflare = fakeProvider("https://unused.trycloudflare.com");
|
|
32
42
|
const result = await selectRelay({ ...options("auto", { cloudflare }), cli: cli("auto", true) });
|
|
@@ -50,6 +60,7 @@ function options(relay, providers, readiness = async () => { }) {
|
|
|
50
60
|
function cli(relay, localOnly = false) {
|
|
51
61
|
return {
|
|
52
62
|
command: "start",
|
|
63
|
+
serviceAction: null,
|
|
53
64
|
roots: [process.cwd()],
|
|
54
65
|
port: null,
|
|
55
66
|
relay,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# AgentRoam bootstrap installers
|
|
2
|
+
|
|
3
|
+
These versioned installers support macOS Apple Silicon and Windows x64. They reuse a visible Node.js 22 installation when available. Otherwise they install the verified private Node.js `22.22.0` runtime under the AgentRoam data directory. They then register and start the current-user AgentRoam background service. They never modify, replace, or uninstall system Node.js or global power settings.
|
|
4
|
+
|
|
5
|
+
## macOS Apple Silicon
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
curl -fsSL https://gitee.com/caoqu/team-agent/releases/download/v0.2.0-preview.9/install-agentroam.sh | sh
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The command installs the wrapper at `~/.local/bin/agentroam`. If that directory is not already in `PATH`, the installer prints the required shell setting without editing shell profiles.
|
|
12
|
+
|
|
13
|
+
## Windows x64
|
|
14
|
+
|
|
15
|
+
```powershell
|
|
16
|
+
irm https://gitee.com/caoqu/team-agent/releases/download/v0.2.0-preview.9/install-agentroam.ps1 | iex
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The command installs `%USERPROFILE%\.agentroam\bin\agentroam.cmd` and adds only that user directory to the user-level `PATH`. It does not require administrator access or change the machine-level `PATH`.
|
|
20
|
+
|
|
21
|
+
## Service behavior
|
|
22
|
+
|
|
23
|
+
Run the installer from the project directory AgentRoam should expose, or set `AGENTROAM_ROOT` explicitly. An installer launched from the home directory without an explicit root stops before service registration so it cannot expose the entire home directory accidentally.
|
|
24
|
+
|
|
25
|
+
The installer registers and starts the service after Node, CLI, and `doctor` validation. While foreground AgentRoam or the service is running, it prevents idle system sleep but still allows the display to dim and turn off. Lid close, explicit sleep, hibernation, shutdown, and low-battery forced sleep remain controlled by the operating system.
|
|
26
|
+
|
|
27
|
+
Use the installer for first installation, upgrades, and repair. Use these commands for routine control:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
agentroam service start
|
|
31
|
+
agentroam service stop
|
|
32
|
+
agentroam service restart
|
|
33
|
+
agentroam service status
|
|
34
|
+
agentroam service url
|
|
35
|
+
agentroam service logs
|
|
36
|
+
agentroam service uninstall
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Data and updates
|
|
40
|
+
|
|
41
|
+
Set `AGENTROAM_DATA_DIR` before running either installer to override the default `~/.agentroam` data directory. Node is stored at `<data-dir>/runtimes/node/22.22.0`, and the launcher is stored at `<data-dir>/launcher/0.2.0-preview.9`.
|
|
42
|
+
|
|
43
|
+
Each AgentRoam release pins its own Node patch and installer assets. Updates happen only by running a newer versioned installer or installing a newer AgentRoam release; startup never follows a moving Node.js release channel.
|
|
44
|
+
|
|
45
|
+
To uninstall the wrapper, remove `~/.local/bin/agentroam` on macOS or `%USERPROFILE%\.agentroam\bin\agentroam.cmd` on Windows. The private runtime, launcher, session data, and configuration under the data directory are preserved until explicitly removed by the user.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
$ErrorActionPreference = "Stop"
|
|
2
|
+
$ProgressPreference = "SilentlyContinue"
|
|
3
|
+
|
|
4
|
+
$NodeVersion = "22.22.0"
|
|
5
|
+
$AgentRoamVersion = "0.2.0-preview.9"
|
|
6
|
+
$NodeArchive = "node-v22.22.0-win-x64.zip"
|
|
7
|
+
$NodeSha256 = "c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
|
|
8
|
+
$NodeUrl = "https://nodejs.org/dist/v22.22.0/$NodeArchive"
|
|
9
|
+
$NpmRegistry = "https://registry.npmjs.org"
|
|
10
|
+
$DataDir = if ($env:AGENTROAM_DATA_DIR) { $env:AGENTROAM_DATA_DIR } else { Join-Path $HOME ".agentroam" }
|
|
11
|
+
$NodeParent = Join-Path $DataDir "runtimes\node"
|
|
12
|
+
$NodeRoot = Join-Path $NodeParent $NodeVersion
|
|
13
|
+
$NodeLock = "$NodeRoot.lock"
|
|
14
|
+
$LauncherParent = Join-Path $DataDir "launcher"
|
|
15
|
+
$LauncherRoot = Join-Path $LauncherParent $AgentRoamVersion
|
|
16
|
+
$LauncherLock = "$LauncherRoot.lock"
|
|
17
|
+
$WrapperDir = Join-Path $HOME ".agentroam\bin"
|
|
18
|
+
$WrapperPath = Join-Path $WrapperDir "agentroam.cmd"
|
|
19
|
+
$ServiceRootExplicit = -not [string]::IsNullOrWhiteSpace($env:AGENTROAM_ROOT)
|
|
20
|
+
$ServiceRoot = if ($ServiceRootExplicit) { $env:AGENTROAM_ROOT } else { (Get-Location).Path }
|
|
21
|
+
if (-not (Test-Path -LiteralPath $ServiceRoot -PathType Container)) { throw "AgentRoam root is not a directory: $ServiceRoot" }
|
|
22
|
+
$ServiceRoot = [System.IO.Path]::GetFullPath($ServiceRoot).TrimEnd([System.IO.Path]::DirectorySeparatorChar)
|
|
23
|
+
$HomeRoot = [System.IO.Path]::GetFullPath($HOME).TrimEnd([System.IO.Path]::DirectorySeparatorChar)
|
|
24
|
+
if (-not $ServiceRootExplicit -and $ServiceRoot -eq $HomeRoot) {
|
|
25
|
+
throw "Refusing to expose the entire home directory implicitly; run from a project directory or set AGENTROAM_ROOT explicitly"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
New-Item -ItemType Directory -Force -Path $NodeParent, $LauncherParent, $WrapperDir | Out-Null
|
|
29
|
+
|
|
30
|
+
function Test-Node22([string]$NodeBin) {
|
|
31
|
+
if (-not (Test-Path -LiteralPath $NodeBin -PathType Leaf)) { return $false }
|
|
32
|
+
try { return (& $NodeBin -p 'process.versions.node.split(".")[0]' 2>$null) -eq "22" } catch { return $false }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function Test-ManagedNode {
|
|
36
|
+
$NodeBin = Join-Path $NodeRoot "node.exe"
|
|
37
|
+
$NpmCli = Join-Path $NodeRoot "node_modules\npm\bin\npm-cli.js"
|
|
38
|
+
if (-not (Test-Path -LiteralPath $NodeBin -PathType Leaf) -or -not (Test-Path -LiteralPath $NpmCli -PathType Leaf)) { return $false }
|
|
39
|
+
try { return (& $NodeBin --version 2>$null) -eq "v$NodeVersion" } catch { return $false }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function Enter-InstallLock([string]$Path) {
|
|
43
|
+
$Deadline = [DateTime]::UtcNow.AddMinutes(15)
|
|
44
|
+
while ($true) {
|
|
45
|
+
try {
|
|
46
|
+
$Stream = [System.IO.File]::Open($Path, [System.IO.FileMode]::CreateNew, [System.IO.FileAccess]::Write, [System.IO.FileShare]::None)
|
|
47
|
+
$Bytes = [Text.Encoding]::UTF8.GetBytes("$PID`n$([DateTime]::UtcNow.ToString('o'))`n")
|
|
48
|
+
$Stream.Write($Bytes, 0, $Bytes.Length)
|
|
49
|
+
return $Stream
|
|
50
|
+
} catch [System.IO.IOException] {
|
|
51
|
+
if (Test-Path -LiteralPath $Path) {
|
|
52
|
+
$Age = [DateTime]::UtcNow - (Get-Item -LiteralPath $Path).LastWriteTimeUtc
|
|
53
|
+
if ($Age.TotalMinutes -gt 15) { Remove-Item -LiteralPath $Path -Force -ErrorAction SilentlyContinue; continue }
|
|
54
|
+
}
|
|
55
|
+
if ([DateTime]::UtcNow -ge $Deadline) { throw "Timed out waiting for install lock $Path" }
|
|
56
|
+
Start-Sleep -Milliseconds 500
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
$SystemNode = Get-Command node.exe -ErrorAction SilentlyContinue
|
|
62
|
+
if ($env:AGENTROAM_BOOTSTRAP_TEST -eq "1" -and $env:AGENTROAM_FORCE_PRIVATE_NODE -eq "1") { $SystemNode = $null }
|
|
63
|
+
if ($SystemNode -and (Test-Node22 $SystemNode.Source)) {
|
|
64
|
+
$NodeBin = $SystemNode.Source
|
|
65
|
+
} else {
|
|
66
|
+
if (-not (Test-ManagedNode)) {
|
|
67
|
+
$NodeLockHandle = Enter-InstallLock $NodeLock
|
|
68
|
+
try {
|
|
69
|
+
if (-not (Test-ManagedNode)) {
|
|
70
|
+
$TempRoot = Join-Path $NodeParent ".node-$NodeVersion-$PID-$([Guid]::NewGuid().ToString('N'))"
|
|
71
|
+
$ArchivePath = Join-Path $TempRoot $NodeArchive
|
|
72
|
+
$ExtractPath = Join-Path $TempRoot "extract"
|
|
73
|
+
New-Item -ItemType Directory -Force -Path $ExtractPath | Out-Null
|
|
74
|
+
try {
|
|
75
|
+
Write-Host "Downloading Node.js $NodeVersion..."
|
|
76
|
+
if ($env:AGENTROAM_NODE_ARCHIVE_FILE) {
|
|
77
|
+
Copy-Item -LiteralPath $env:AGENTROAM_NODE_ARCHIVE_FILE -Destination $ArchivePath
|
|
78
|
+
} else {
|
|
79
|
+
Invoke-WebRequest -UseBasicParsing -TimeoutSec 600 -Uri $NodeUrl -OutFile $ArchivePath
|
|
80
|
+
}
|
|
81
|
+
$ActualHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $ArchivePath).Hash.ToLowerInvariant()
|
|
82
|
+
if ($ActualHash -ne $NodeSha256) { throw "Node.js archive checksum mismatch" }
|
|
83
|
+
Expand-Archive -LiteralPath $ArchivePath -DestinationPath $ExtractPath -Force
|
|
84
|
+
$Entries = @(Get-ChildItem -LiteralPath $ExtractPath)
|
|
85
|
+
if ($Entries.Count -ne 1 -or $Entries[0].Name -ne "node-v$NodeVersion-win-x64") { throw "Unexpected Node.js archive layout" }
|
|
86
|
+
$Extracted = $Entries[0].FullName
|
|
87
|
+
$ExtractedNode = Join-Path $Extracted "node.exe"
|
|
88
|
+
$ExtractedNpm = Join-Path $Extracted "node_modules\npm\bin\npm-cli.js"
|
|
89
|
+
if (-not (Test-Path -LiteralPath $ExtractedNpm) -or (& $ExtractedNode --version) -ne "v$NodeVersion") { throw "Node.js runtime validation failed" }
|
|
90
|
+
Remove-Item -LiteralPath $NodeRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
91
|
+
Move-Item -LiteralPath $Extracted -Destination $NodeRoot
|
|
92
|
+
if (-not (Test-ManagedNode)) { throw "Node.js activation validation failed" }
|
|
93
|
+
} finally {
|
|
94
|
+
Remove-Item -LiteralPath $TempRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} finally {
|
|
98
|
+
$NodeLockHandle.Dispose()
|
|
99
|
+
Remove-Item -LiteralPath $NodeLock -Force -ErrorAction SilentlyContinue
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
$NodeBin = Join-Path $NodeRoot "node.exe"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
$NodeDirectory = Split-Path -Parent $NodeBin
|
|
106
|
+
$NpmCandidates = @(
|
|
107
|
+
(Join-Path $NodeDirectory "node_modules\npm\bin\npm-cli.js"),
|
|
108
|
+
(Join-Path $NodeDirectory "..\node_modules\npm\bin\npm-cli.js")
|
|
109
|
+
)
|
|
110
|
+
$NpmCli = $NpmCandidates | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
|
|
111
|
+
if (-not $NpmCli) { throw "npm CLI was not found beside $NodeBin" }
|
|
112
|
+
|
|
113
|
+
$PackageSpec = "agentroam@$AgentRoamVersion"
|
|
114
|
+
$ExtraPackageSpecs = @()
|
|
115
|
+
if ($env:AGENTROAM_BOOTSTRAP_TEST -eq "1" -and $env:AGENTROAM_PACKAGE_SPEC) {
|
|
116
|
+
$PackageSpec = $env:AGENTROAM_PACKAGE_SPEC
|
|
117
|
+
if ($env:AGENTROAM_BOOTSTRAP_EXTRA_SPECS) { $ExtraPackageSpecs = @($env:AGENTROAM_BOOTSTRAP_EXTRA_SPECS.Split(';') | Where-Object { $_ }) }
|
|
118
|
+
}
|
|
119
|
+
$Entry = Join-Path $LauncherRoot "node_modules\agentroam\bin\agentroam.mjs"
|
|
120
|
+
|
|
121
|
+
function Test-Launcher {
|
|
122
|
+
if (-not (Test-Path -LiteralPath $Entry -PathType Leaf)) { return $false }
|
|
123
|
+
try {
|
|
124
|
+
$Output = & $NodeBin $Entry version 2>$null
|
|
125
|
+
return $LASTEXITCODE -eq 0 -and $Output -match [Regex]::Escape($AgentRoamVersion)
|
|
126
|
+
} catch { return $false }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (-not (Test-Launcher)) {
|
|
130
|
+
$LauncherLockHandle = Enter-InstallLock $LauncherLock
|
|
131
|
+
try {
|
|
132
|
+
if (-not (Test-Launcher)) {
|
|
133
|
+
$TempLauncher = Join-Path $LauncherParent ".launcher-$AgentRoamVersion-$PID-$([Guid]::NewGuid().ToString('N'))"
|
|
134
|
+
try {
|
|
135
|
+
$NpmArgs = @("install", "--no-audit", "--no-fund", "--registry", $NpmRegistry, "--prefix", $TempLauncher) + $ExtraPackageSpecs + @($PackageSpec)
|
|
136
|
+
& $NodeBin $NpmCli @NpmArgs
|
|
137
|
+
if ($LASTEXITCODE -ne 0) { throw "npm install failed with exit code $LASTEXITCODE" }
|
|
138
|
+
$TempEntry = Join-Path $TempLauncher "node_modules\agentroam\bin\agentroam.mjs"
|
|
139
|
+
$VersionOutput = & $NodeBin $TempEntry version
|
|
140
|
+
if ($LASTEXITCODE -ne 0 -or $VersionOutput -notmatch [Regex]::Escape($AgentRoamVersion)) { throw "AgentRoam version validation failed" }
|
|
141
|
+
Remove-Item -LiteralPath $LauncherRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
142
|
+
Move-Item -LiteralPath $TempLauncher -Destination $LauncherRoot
|
|
143
|
+
} finally {
|
|
144
|
+
Remove-Item -LiteralPath $TempLauncher -Recurse -Force -ErrorAction SilentlyContinue
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} finally {
|
|
148
|
+
$LauncherLockHandle.Dispose()
|
|
149
|
+
Remove-Item -LiteralPath $LauncherLock -Force -ErrorAction SilentlyContinue
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
$WrapperTemp = "$WrapperPath.$PID.tmp"
|
|
154
|
+
$WrapperContent = "@echo off`r`n`"$NodeBin`" `"$Entry`" %*`r`n"
|
|
155
|
+
[System.IO.File]::WriteAllText($WrapperTemp, $WrapperContent, [Text.Encoding]::ASCII)
|
|
156
|
+
Move-Item -LiteralPath $WrapperTemp -Destination $WrapperPath -Force
|
|
157
|
+
|
|
158
|
+
$UserPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
|
159
|
+
$PathParts = @($UserPath -split ";" | Where-Object { $_ })
|
|
160
|
+
if ($PathParts -notcontains $WrapperDir) {
|
|
161
|
+
$UpdatedPath = if ($UserPath) { "$UserPath;$WrapperDir" } else { $WrapperDir }
|
|
162
|
+
[Environment]::SetEnvironmentVariable("Path", $UpdatedPath, "User")
|
|
163
|
+
$env:Path = "$env:Path;$WrapperDir"
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
& $NodeBin $Entry doctor --data-dir $DataDir
|
|
167
|
+
if ($LASTEXITCODE -ne 0) { throw "AgentRoam doctor failed with exit code $LASTEXITCODE" }
|
|
168
|
+
if ($env:AGENTROAM_INSTALL_SKIP_SERVICE -eq "1") {
|
|
169
|
+
Write-Host "AgentRoam service registration skipped for isolated verification."
|
|
170
|
+
} else {
|
|
171
|
+
& $NodeBin $Entry service install --root $ServiceRoot --data-dir $DataDir
|
|
172
|
+
if ($LASTEXITCODE -ne 0) { throw "AgentRoam service installation failed with exit code $LASTEXITCODE" }
|
|
173
|
+
}
|
|
174
|
+
Write-Host "`nAgentRoam $AgentRoamVersion installed: $WrapperPath"
|
|
175
|
+
Write-Host "Open a new terminal, then run: agentroam service status"
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
NODE_VERSION="22.22.0"
|
|
5
|
+
AGENTROAM_VERSION="0.2.0-preview.9"
|
|
6
|
+
NODE_ARCHIVE="node-v22.22.0-darwin-arm64.tar.xz"
|
|
7
|
+
NODE_SHA256="2bd596bbfc4a275ceb8721a5954ee97daea5ebe673e96a185ebd732f6fb023ac"
|
|
8
|
+
NODE_URL="https://nodejs.org/dist/v22.22.0/$NODE_ARCHIVE"
|
|
9
|
+
NPM_REGISTRY="https://registry.npmjs.org"
|
|
10
|
+
DATA_DIR="${AGENTROAM_DATA_DIR:-$HOME/.agentroam}"
|
|
11
|
+
NODE_PARENT="$DATA_DIR/runtimes/node"
|
|
12
|
+
NODE_ROOT="$NODE_PARENT/$NODE_VERSION"
|
|
13
|
+
NODE_LOCK="$NODE_ROOT.lock"
|
|
14
|
+
LAUNCHER_PARENT="$DATA_DIR/launcher"
|
|
15
|
+
LAUNCHER_ROOT="$LAUNCHER_PARENT/$AGENTROAM_VERSION"
|
|
16
|
+
LAUNCHER_LOCK="$LAUNCHER_ROOT.lock"
|
|
17
|
+
WRAPPER_DIR="$HOME/.local/bin"
|
|
18
|
+
WRAPPER_PATH="$WRAPPER_DIR/agentroam"
|
|
19
|
+
SERVICE_ROOT_EXPLICIT=0
|
|
20
|
+
if [ "${AGENTROAM_ROOT+x}" = "x" ]; then
|
|
21
|
+
SERVICE_ROOT="$AGENTROAM_ROOT"
|
|
22
|
+
SERVICE_ROOT_EXPLICIT=1
|
|
23
|
+
else
|
|
24
|
+
SERVICE_ROOT="$PWD"
|
|
25
|
+
fi
|
|
26
|
+
TEMP_ROOT=""
|
|
27
|
+
OWN_NODE_LOCK=0
|
|
28
|
+
OWN_LAUNCHER_LOCK=0
|
|
29
|
+
|
|
30
|
+
fail() {
|
|
31
|
+
printf 'agentroam installer: %s\n' "$*" >&2
|
|
32
|
+
exit 1
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
cleanup() {
|
|
36
|
+
if [ -n "$TEMP_ROOT" ] && [ -d "$TEMP_ROOT" ]; then
|
|
37
|
+
rm -rf "$TEMP_ROOT"
|
|
38
|
+
fi
|
|
39
|
+
if [ "$OWN_NODE_LOCK" -eq 1 ]; then rmdir "$NODE_LOCK" 2>/dev/null || true; fi
|
|
40
|
+
if [ "$OWN_LAUNCHER_LOCK" -eq 1 ]; then rmdir "$LAUNCHER_LOCK" 2>/dev/null || true; fi
|
|
41
|
+
}
|
|
42
|
+
trap cleanup EXIT HUP INT TERM
|
|
43
|
+
|
|
44
|
+
[ "$(uname -s)" = "Darwin" ] || fail "this installer supports macOS only"
|
|
45
|
+
[ "$(uname -m)" = "arm64" ] || fail "managed Node.js supports Apple Silicon only"
|
|
46
|
+
command -v curl >/dev/null 2>&1 || fail "curl is required"
|
|
47
|
+
command -v shasum >/dev/null 2>&1 || fail "shasum is required"
|
|
48
|
+
[ -x /usr/bin/tar ] || fail "/usr/bin/tar is required"
|
|
49
|
+
[ -d "$SERVICE_ROOT" ] || fail "AgentRoam root is not a directory: $SERVICE_ROOT"
|
|
50
|
+
service_root_physical=$(cd "$SERVICE_ROOT" && pwd -P)
|
|
51
|
+
home_physical=$(cd "$HOME" && pwd -P)
|
|
52
|
+
if [ "$SERVICE_ROOT_EXPLICIT" -eq 0 ] && [ "$service_root_physical" = "$home_physical" ]; then
|
|
53
|
+
fail "refusing to expose the entire home directory implicitly; run from a project directory or set AGENTROAM_ROOT explicitly"
|
|
54
|
+
fi
|
|
55
|
+
SERVICE_ROOT="$service_root_physical"
|
|
56
|
+
|
|
57
|
+
mkdir -p "$NODE_PARENT" "$LAUNCHER_PARENT" "$WRAPPER_DIR"
|
|
58
|
+
|
|
59
|
+
node_is_22() {
|
|
60
|
+
[ -x "$1" ] && [ "$("$1" -p 'process.versions.node.split(".")[0]' 2>/dev/null || true)" = "22" ]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
runtime_is_valid() {
|
|
64
|
+
[ -x "$NODE_ROOT/bin/node" ] \
|
|
65
|
+
&& [ -f "$NODE_ROOT/lib/node_modules/npm/bin/npm-cli.js" ] \
|
|
66
|
+
&& [ "$("$NODE_ROOT/bin/node" --version 2>/dev/null || true)" = "v$NODE_VERSION" ]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
acquire_directory_lock() {
|
|
70
|
+
lock_path=$1
|
|
71
|
+
waited=0
|
|
72
|
+
while ! mkdir "$lock_path" 2>/dev/null; do
|
|
73
|
+
if [ "$waited" -ge 900 ]; then
|
|
74
|
+
lock_mtime=$(stat -f %m "$lock_path" 2>/dev/null || printf '0')
|
|
75
|
+
now=$(date +%s)
|
|
76
|
+
if [ $((now - lock_mtime)) -gt 900 ]; then
|
|
77
|
+
rmdir "$lock_path" 2>/dev/null || true
|
|
78
|
+
waited=0
|
|
79
|
+
continue
|
|
80
|
+
fi
|
|
81
|
+
fail "timed out waiting for install lock $lock_path"
|
|
82
|
+
fi
|
|
83
|
+
sleep 1
|
|
84
|
+
waited=$((waited + 1))
|
|
85
|
+
done
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
SYSTEM_NODE=$(command -v node 2>/dev/null || true)
|
|
89
|
+
if [ "${AGENTROAM_BOOTSTRAP_TEST:-}" = "1" ] && [ "${AGENTROAM_FORCE_PRIVATE_NODE:-}" = "1" ]; then
|
|
90
|
+
SYSTEM_NODE=""
|
|
91
|
+
fi
|
|
92
|
+
if [ -n "$SYSTEM_NODE" ] && node_is_22 "$SYSTEM_NODE"; then
|
|
93
|
+
NODE_BIN="$SYSTEM_NODE"
|
|
94
|
+
else
|
|
95
|
+
if ! runtime_is_valid; then
|
|
96
|
+
acquire_directory_lock "$NODE_LOCK"
|
|
97
|
+
OWN_NODE_LOCK=1
|
|
98
|
+
if ! runtime_is_valid; then
|
|
99
|
+
TEMP_ROOT=$(mktemp -d "$NODE_PARENT/.node-$NODE_VERSION.XXXXXX")
|
|
100
|
+
archive_path="$TEMP_ROOT/$NODE_ARCHIVE"
|
|
101
|
+
extract_path="$TEMP_ROOT/extract"
|
|
102
|
+
mkdir "$extract_path"
|
|
103
|
+
printf 'Downloading Node.js %s...\n' "$NODE_VERSION"
|
|
104
|
+
if [ -n "${AGENTROAM_NODE_ARCHIVE_FILE:-}" ]; then
|
|
105
|
+
cp "$AGENTROAM_NODE_ARCHIVE_FILE" "$archive_path"
|
|
106
|
+
else
|
|
107
|
+
download_attempt=1
|
|
108
|
+
while ! curl -fL -C - --connect-timeout 15 --max-time 600 "$NODE_URL" -o "$archive_path"; do
|
|
109
|
+
[ "$download_attempt" -lt 3 ] || fail "Node.js download failed after 3 attempts"
|
|
110
|
+
download_attempt=$((download_attempt + 1))
|
|
111
|
+
sleep "$download_attempt"
|
|
112
|
+
done
|
|
113
|
+
fi
|
|
114
|
+
actual_sha=$(shasum -a 256 "$archive_path" | awk '{print $1}')
|
|
115
|
+
[ "$actual_sha" = "$NODE_SHA256" ] || fail "Node.js archive checksum mismatch"
|
|
116
|
+
/usr/bin/tar -xJf "$archive_path" -C "$extract_path"
|
|
117
|
+
entry_count=$(find "$extract_path" -mindepth 1 -maxdepth 1 | wc -l | tr -d ' ')
|
|
118
|
+
[ "$entry_count" = "1" ] || fail "unexpected Node.js archive layout"
|
|
119
|
+
extracted="$extract_path/node-v$NODE_VERSION-darwin-arm64"
|
|
120
|
+
[ -x "$extracted/bin/node" ] || fail "Node.js executable is missing"
|
|
121
|
+
[ -f "$extracted/lib/node_modules/npm/bin/npm-cli.js" ] || fail "npm CLI is missing"
|
|
122
|
+
[ "$("$extracted/bin/node" --version)" = "v$NODE_VERSION" ] || fail "Node.js version validation failed"
|
|
123
|
+
rm -rf "$NODE_ROOT"
|
|
124
|
+
mv "$extracted" "$NODE_ROOT"
|
|
125
|
+
runtime_is_valid || fail "Node.js activation validation failed"
|
|
126
|
+
rm -rf "$TEMP_ROOT"
|
|
127
|
+
TEMP_ROOT=""
|
|
128
|
+
fi
|
|
129
|
+
rmdir "$NODE_LOCK" 2>/dev/null || true
|
|
130
|
+
OWN_NODE_LOCK=0
|
|
131
|
+
fi
|
|
132
|
+
NODE_BIN="$NODE_ROOT/bin/node"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
find_npm_cli() {
|
|
136
|
+
node_dir=$(dirname "$NODE_BIN")
|
|
137
|
+
for candidate in \
|
|
138
|
+
"$node_dir/../lib/node_modules/npm/bin/npm-cli.js" \
|
|
139
|
+
"$node_dir/lib/node_modules/npm/bin/npm-cli.js"; do
|
|
140
|
+
if [ -f "$candidate" ]; then
|
|
141
|
+
printf '%s\n' "$candidate"
|
|
142
|
+
return
|
|
143
|
+
fi
|
|
144
|
+
done
|
|
145
|
+
fail "npm CLI was not found beside $NODE_BIN"
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
NPM_CLI=$(find_npm_cli)
|
|
149
|
+
PACKAGE_SPEC="agentroam@$AGENTROAM_VERSION"
|
|
150
|
+
EXTRA_PACKAGE_SPECS=""
|
|
151
|
+
if [ "${AGENTROAM_BOOTSTRAP_TEST:-}" = "1" ] && [ -n "${AGENTROAM_PACKAGE_SPEC:-}" ]; then
|
|
152
|
+
PACKAGE_SPEC="$AGENTROAM_PACKAGE_SPEC"
|
|
153
|
+
EXTRA_PACKAGE_SPECS="${AGENTROAM_BOOTSTRAP_EXTRA_SPECS:-}"
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
launcher_is_valid() {
|
|
157
|
+
[ -f "$LAUNCHER_ROOT/node_modules/agentroam/bin/agentroam.mjs" ] \
|
|
158
|
+
&& [ "$("$NODE_BIN" "$LAUNCHER_ROOT/node_modules/agentroam/bin/agentroam.mjs" version 2>/dev/null | awk '{print $2}' || true)" = "$AGENTROAM_VERSION" ]
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if ! launcher_is_valid; then
|
|
162
|
+
acquire_directory_lock "$LAUNCHER_LOCK"
|
|
163
|
+
OWN_LAUNCHER_LOCK=1
|
|
164
|
+
if ! launcher_is_valid; then
|
|
165
|
+
TEMP_ROOT=$(mktemp -d "$LAUNCHER_PARENT/.launcher-$AGENTROAM_VERSION.XXXXXX")
|
|
166
|
+
# Test-only extra specs are whitespace-delimited artifact paths in isolated CI directories.
|
|
167
|
+
# shellcheck disable=SC2086
|
|
168
|
+
"$NODE_BIN" "$NPM_CLI" install --no-audit --no-fund --registry "$NPM_REGISTRY" --prefix "$TEMP_ROOT" $EXTRA_PACKAGE_SPECS "$PACKAGE_SPEC"
|
|
169
|
+
entry="$TEMP_ROOT/node_modules/agentroam/bin/agentroam.mjs"
|
|
170
|
+
[ -f "$entry" ] || fail "AgentRoam launcher is missing after npm install"
|
|
171
|
+
[ "$("$NODE_BIN" "$entry" version | awk '{print $2}')" = "$AGENTROAM_VERSION" ] || fail "AgentRoam version validation failed"
|
|
172
|
+
rm -rf "$LAUNCHER_ROOT"
|
|
173
|
+
mv "$TEMP_ROOT" "$LAUNCHER_ROOT"
|
|
174
|
+
TEMP_ROOT=""
|
|
175
|
+
fi
|
|
176
|
+
rmdir "$LAUNCHER_LOCK" 2>/dev/null || true
|
|
177
|
+
OWN_LAUNCHER_LOCK=0
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
entry="$LAUNCHER_ROOT/node_modules/agentroam/bin/agentroam.mjs"
|
|
181
|
+
wrapper_temp=$(mktemp "$WRAPPER_DIR/.agentroam.XXXXXX")
|
|
182
|
+
{
|
|
183
|
+
printf '#!/bin/sh\n'
|
|
184
|
+
printf 'exec "%s" "%s" "$@"\n' "$NODE_BIN" "$entry"
|
|
185
|
+
} > "$wrapper_temp"
|
|
186
|
+
chmod 755 "$wrapper_temp"
|
|
187
|
+
mv "$wrapper_temp" "$WRAPPER_PATH"
|
|
188
|
+
|
|
189
|
+
"$NODE_BIN" "$entry" doctor --data-dir "$DATA_DIR"
|
|
190
|
+
if [ "${AGENTROAM_INSTALL_SKIP_SERVICE:-}" = "1" ]; then
|
|
191
|
+
printf 'AgentRoam service registration skipped for isolated verification.\n'
|
|
192
|
+
else
|
|
193
|
+
"$NODE_BIN" "$entry" service install --root "$SERVICE_ROOT" --data-dir "$DATA_DIR"
|
|
194
|
+
fi
|
|
195
|
+
printf '\nAgentRoam %s installed: %s\n' "$AGENTROAM_VERSION" "$WRAPPER_PATH"
|
|
196
|
+
case ":${PATH:-}:" in
|
|
197
|
+
*":$WRAPPER_DIR:"*) ;;
|
|
198
|
+
*) printf 'Add this directory to PATH: export PATH="%s:$PATH"\n' "$WRAPPER_DIR" ;;
|
|
199
|
+
esac
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentroam",
|
|
3
|
-
"version": "0.2.0-preview.
|
|
3
|
+
"version": "0.2.0-preview.9",
|
|
4
4
|
"description": "Zero-install mobile remote terminal and file console",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"bin",
|
|
15
15
|
"dist",
|
|
16
|
+
"install",
|
|
16
17
|
"README.md"
|
|
17
18
|
],
|
|
18
19
|
"scripts": {
|
|
@@ -25,12 +26,12 @@
|
|
|
25
26
|
"undici": "^6.25.0"
|
|
26
27
|
},
|
|
27
28
|
"optionalDependencies": {
|
|
28
|
-
"agentroam-runtime-darwin-arm64": "0.2.0-preview.
|
|
29
|
-
"agentroam-runtime-win32-x64": "0.2.0-preview.
|
|
30
|
-
"agentroam-cloudflared-darwin-arm64": "0.2.0-preview.
|
|
31
|
-
"agentroam-cloudflared-win32-x64": "0.2.0-preview.
|
|
32
|
-
"agentroam-tui-darwin-arm64": "0.2.0-preview.
|
|
33
|
-
"@caoqu/agentroam-tui-win32-x64": "0.2.0-preview.
|
|
29
|
+
"agentroam-runtime-darwin-arm64": "0.2.0-preview.9",
|
|
30
|
+
"agentroam-runtime-win32-x64": "0.2.0-preview.9",
|
|
31
|
+
"agentroam-cloudflared-darwin-arm64": "0.2.0-preview.9",
|
|
32
|
+
"agentroam-cloudflared-win32-x64": "0.2.0-preview.9",
|
|
33
|
+
"agentroam-tui-darwin-arm64": "0.2.0-preview.9",
|
|
34
|
+
"@caoqu/agentroam-tui-win32-x64": "0.2.0-preview.9"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@types/node": "^22.0.0",
|