agentroam 0.2.0-preview.16 → 0.2.0-preview.18
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 +17 -17
- package/bin/desktop-download.mjs +62 -0
- package/bin/node-preflight.mjs +15 -22
- package/bin/node-runtime-policy.json +4 -0
- package/bin/runtime-policy.d.mts +5 -0
- package/bin/runtime-policy.mjs +37 -0
- package/dist/args.js +31 -2
- package/dist/args.test.js +16 -0
- package/dist/cli.js +64 -15
- package/dist/desktop-download.test.js +83 -0
- package/dist/device-pairing.js +146 -0
- package/dist/device-pairing.test.js +33 -0
- package/dist/install-script-contract.test.js +35 -11
- package/dist/native-runtime.js +46 -0
- package/dist/native-runtime.test.js +23 -2
- package/dist/node-preflight.test.js +21 -15
- package/dist/opencode-runtime-manager.js +15 -3
- package/dist/opencode-runtime-manager.test.js +33 -1
- package/dist/platform-packages.js +7 -4
- package/dist/platform-packages.test.js +15 -4
- package/dist/platform.js +2 -4
- package/dist/platform.test.js +3 -3
- package/dist/runtime-manager.js +3 -5
- package/dist/service/service-command.js +10 -6
- package/dist/service/service-command.test.js +21 -0
- package/dist/tunnel/public-readiness.js +1 -1
- package/install/README.md +13 -5
- package/install/install-agentroam.ps1 +45 -13
- package/install/install-agentroam.sh +68 -16
- package/package.json +8 -8
- package/dist/pairing.js +0 -2
package/dist/platform.test.js
CHANGED
|
@@ -2,15 +2,15 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
import { detectPlatform, detectPlatformTarget } from "./platform.js";
|
|
3
3
|
describe("detectPlatform", () => {
|
|
4
4
|
it("maps supported targets", () => {
|
|
5
|
-
expect(detectPlatform("darwin", "arm64", "22.
|
|
6
|
-
expect(detectPlatform("win32", "x64", "
|
|
5
|
+
expect(detectPlatform("darwin", "arm64", "22.22.0")).toBe("darwin-arm64");
|
|
6
|
+
expect(detectPlatform("win32", "x64", "25.8.0")).toBe("windows-amd64");
|
|
7
7
|
});
|
|
8
8
|
it("detects a target before Node version validation", () => {
|
|
9
9
|
expect(detectPlatformTarget("darwin", "arm64")).toBe("darwin-arm64");
|
|
10
10
|
expect(detectPlatformTarget("win32", "x64")).toBe("windows-amd64");
|
|
11
11
|
});
|
|
12
12
|
it("rejects unsupported versions", () => {
|
|
13
|
-
expect(() => detectPlatform("darwin", "arm64", "
|
|
13
|
+
expect(() => detectPlatform("darwin", "arm64", "22.21.9")).toThrow("Node.js >=22.22.0");
|
|
14
14
|
});
|
|
15
15
|
it("rejects unsupported platforms", () => {
|
|
16
16
|
expect(() => detectPlatformTarget("linux", "x64")).toThrow("unsupported platform");
|
package/dist/runtime-manager.js
CHANGED
|
@@ -18,7 +18,7 @@ export class RuntimeManager {
|
|
|
18
18
|
this.report = report;
|
|
19
19
|
this.opencodeResolver = opencodeResolver;
|
|
20
20
|
}
|
|
21
|
-
async start(options,
|
|
21
|
+
async start(options, target) {
|
|
22
22
|
const port = await findAvailablePort(options.port);
|
|
23
23
|
const dataDir = resolve(options.dataDir);
|
|
24
24
|
await Promise.all(["data", "bin", "cache", "logs"].map((name) => mkdir(resolve(dataDir, name), { recursive: true })));
|
|
@@ -27,19 +27,17 @@ export class RuntimeManager {
|
|
|
27
27
|
const { runtimeRoot } = resolvePlatformRuntime(target);
|
|
28
28
|
await repairNativeRuntimePermissions(runtimeRoot);
|
|
29
29
|
const gateway = resolve(runtimeRoot, "ws-server.mjs");
|
|
30
|
-
const child = this.supervisor.spawn(process.execPath, [gateway], {
|
|
30
|
+
const child = this.supervisor.spawn(process.execPath, [gateway, ...(options.testNoPairing ? ["--test-no-pairing"] : [])], {
|
|
31
31
|
cwd: runtimeRoot,
|
|
32
32
|
env: {
|
|
33
33
|
...childEnvironment,
|
|
34
34
|
NODE_ENV: "production",
|
|
35
35
|
NEXT_DIST_DIR: ".next",
|
|
36
36
|
PORT: String(port),
|
|
37
|
-
HOST: "127.0.0.1",
|
|
37
|
+
HOST: options.localOnly ? "0.0.0.0" : "127.0.0.1",
|
|
38
38
|
AGENT_DATA_DIR: resolve(dataDir, "data"),
|
|
39
39
|
AGENT_WEB_ROOTS: options.roots.join(delimiter),
|
|
40
40
|
AGENT_TRUST_TUNNEL_PROXY: "1",
|
|
41
|
-
AGENT_PAIRING_HASH: pairing?.hashHex || "",
|
|
42
|
-
AGENT_PAIRING_EXPIRES_AT: pairing?.expiresAt || "",
|
|
43
41
|
AGENTROAM_VERSION: resolvePlatformRuntime(target).manifest.packageVersion,
|
|
44
42
|
AGENTROAM_DATA_DIR: dataDir,
|
|
45
43
|
AGENTROAM_CLI_PATH: fileURLToPath(new URL("../bin/agentroam.mjs", import.meta.url)),
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { delimiter, dirname, isAbsolute, resolve, win32 } from "node:path";
|
|
2
2
|
import { resolveCodexRuntime } from "../codex-runtime-manager.js";
|
|
3
3
|
import { detectPlatform } from "../platform.js";
|
|
4
|
+
import { printPairingCode } from "../device-pairing.js";
|
|
4
5
|
import { renderQr } from "../qr.js";
|
|
5
6
|
import { MacLaunchAgent } from "./macos-launch-agent.js";
|
|
6
7
|
import { WindowsTaskService } from "./windows-task-service.js";
|
|
8
|
+
import { assertSupportedNodeVersion } from "../../bin/runtime-policy.mjs";
|
|
7
9
|
export async function runServiceCommand(options, context) {
|
|
8
10
|
const platform = context.platform ?? process.platform;
|
|
9
11
|
const log = context.log ?? console.log;
|
|
@@ -19,9 +21,7 @@ export async function runServiceCommand(options, context) {
|
|
|
19
21
|
switch (options.serviceAction) {
|
|
20
22
|
case "install": {
|
|
21
23
|
const nodeVersion = context.nodeVersion ?? process.versions.node;
|
|
22
|
-
|
|
23
|
-
throw cliError(`Node.js 22 is required to install the service (current ${nodeVersion})`);
|
|
24
|
-
}
|
|
24
|
+
assertSupportedNodeVersion(nodeVersion);
|
|
25
25
|
const now = context.now?.() ?? new Date();
|
|
26
26
|
const environment = context.environment ?? process.env;
|
|
27
27
|
const environmentPath = buildServiceEnvironmentPath(context.nodePath, environment.PATH ?? environment.Path, platform);
|
|
@@ -57,10 +57,14 @@ export async function runServiceCommand(options, context) {
|
|
|
57
57
|
};
|
|
58
58
|
const { definition, state } = await controller.install(config);
|
|
59
59
|
log(`✓ AgentRoam service installed: ${definition}`);
|
|
60
|
-
if (state?.status === "ready" && state.accessUrl)
|
|
61
|
-
|
|
60
|
+
if (state?.status === "ready" && state.accessUrl) {
|
|
61
|
+
log(`Open: ${state.accessUrl}`);
|
|
62
|
+
if (state.localUrl)
|
|
63
|
+
log(`电脑本机:${state.localUrl.replace(/\/$/, "")}/web(配对后可进行远程授权)`);
|
|
64
|
+
await (context.pairingPrinter ?? printPairingCode)(options.dataDir, log, { accessUrl: state.accessUrl, qr: options.qr, interactive: stdoutIsTTY });
|
|
65
|
+
}
|
|
62
66
|
else
|
|
63
|
-
log("Service is starting. Run `agentroam service url` shortly.");
|
|
67
|
+
log("Service is starting. Run `agentroam service url` and `agentroam pair` shortly.");
|
|
64
68
|
return;
|
|
65
69
|
}
|
|
66
70
|
case "start": {
|
|
@@ -2,6 +2,26 @@ import { resolve } from "node:path";
|
|
|
2
2
|
import { describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { buildServiceEnvironmentPath, runServiceCommand } from "./service-command.js";
|
|
4
4
|
describe("runServiceCommand", () => {
|
|
5
|
+
it.each(["22.22.0", "24.0.0", "25.8.0", "26.0.0"])("keeps the selected Node %s when installing a service", async (nodeVersion) => {
|
|
6
|
+
const install = vi.fn(async () => ({ definition: "/test/service.plist" }));
|
|
7
|
+
const nodePath = `/runtimes/node-${nodeVersion}/bin/node`;
|
|
8
|
+
const codexResolver = vi.fn(async () => ({ executable: "/tools/codex", version: "test", source: "global" }));
|
|
9
|
+
await runServiceCommand(options("install"), {
|
|
10
|
+
platform: "darwin", arch: "arm64", nodeVersion, nodePath,
|
|
11
|
+
cliPath: "/agentroam.mjs", version: "test", environment: { PATH: "/usr/bin" },
|
|
12
|
+
codexResolver, controller: controller({ install }), log: vi.fn(),
|
|
13
|
+
});
|
|
14
|
+
expect(install).toHaveBeenCalledWith(expect.objectContaining({ nodePath }));
|
|
15
|
+
expect(codexResolver).toHaveBeenCalledWith(expect.objectContaining({ nodeExecutable: nodePath }));
|
|
16
|
+
});
|
|
17
|
+
it("rejects a below-minimum service runtime before installation", async () => {
|
|
18
|
+
const install = vi.fn();
|
|
19
|
+
await expect(runServiceCommand(options("install"), {
|
|
20
|
+
platform: "darwin", nodePath: "/node", nodeVersion: "22.21.9", cliPath: "/agentroam.mjs",
|
|
21
|
+
version: "test", controller: controller({ install }),
|
|
22
|
+
})).rejects.toThrow("Node.js >=22.22.0");
|
|
23
|
+
expect(install).not.toHaveBeenCalled();
|
|
24
|
+
});
|
|
5
25
|
it("installs the service using absolute runtime context and parsed start options", async () => {
|
|
6
26
|
const install = vi.fn(async (config) => ({
|
|
7
27
|
paths: { plistPath: "/Users/test/Library/LaunchAgents/com.agentroam.service.plist" },
|
|
@@ -20,6 +40,7 @@ describe("runServiceCommand", () => {
|
|
|
20
40
|
now: () => new Date("2026-09-03T00:00:00.000Z"),
|
|
21
41
|
log,
|
|
22
42
|
controller: controller({ install }),
|
|
43
|
+
pairingPrinter: vi.fn(async () => { }),
|
|
23
44
|
});
|
|
24
45
|
expect(install).toHaveBeenCalledWith(expect.objectContaining({
|
|
25
46
|
nodePath: "/node",
|
|
@@ -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.18" },
|
|
26
26
|
signal: options.signal,
|
|
27
27
|
});
|
|
28
28
|
if (response.ok) {
|
package/install/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# AgentRoam bootstrap installers
|
|
2
2
|
|
|
3
|
-
These versioned installers support macOS Apple Silicon and Windows x64. They reuse
|
|
3
|
+
These versioned installers support macOS Apple Silicon and Windows x64. They reuse the active Node.js installation when it meets `>=22.22.0`, with no upper version bound. Otherwise they search NVM for the highest compatible version, then fall back to 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
4
|
|
|
5
5
|
## macOS Apple Silicon
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
curl -fsSL https://gitee.com/caoqu/team-agent/
|
|
8
|
+
curl -fsSL https://gitee.com/caoqu/team-agent/raw/master/packages/cli/install/install-agentroam.sh | sh
|
|
9
9
|
```
|
|
10
10
|
|
|
11
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.
|
|
@@ -13,14 +13,14 @@ The command installs the wrapper at `~/.local/bin/agentroam`. If that directory
|
|
|
13
13
|
## Windows x64
|
|
14
14
|
|
|
15
15
|
```powershell
|
|
16
|
-
irm https://gitee.com/caoqu/team-agent/
|
|
16
|
+
irm https://gitee.com/caoqu/team-agent/raw/master/packages/cli/install/install-agentroam.ps1 | iex
|
|
17
17
|
```
|
|
18
18
|
|
|
19
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
20
|
|
|
21
21
|
## Service behavior
|
|
22
22
|
|
|
23
|
-
Run the installer from the project directory AgentRoam should expose, or set `AGENTROAM_ROOT` explicitly.
|
|
23
|
+
Run the installer from the project directory AgentRoam should expose, or set `AGENTROAM_ROOT` explicitly. The current directory is accepted even when it is the home directory.
|
|
24
24
|
|
|
25
25
|
The installer registers and starts the service after Node, CLI, and `doctor` validation. When a previous AgentRoam user service is installed, the new CLI stops it, waits for its process and platform registration to exit, removes only the old service registration, and then registers the replacement. Session data, pairing state, managed runtimes, and logs remain in the data directory. 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
26
|
|
|
@@ -40,8 +40,16 @@ agentroam service uninstall
|
|
|
40
40
|
|
|
41
41
|
## Data and updates
|
|
42
42
|
|
|
43
|
-
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.
|
|
43
|
+
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.18`.
|
|
44
44
|
|
|
45
45
|
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.
|
|
46
46
|
|
|
47
47
|
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.
|
|
48
|
+
|
|
49
|
+
## Optional desktop app
|
|
50
|
+
|
|
51
|
+
After CLI setup succeeds, interactive installers ask whether to download the desktop app. Answer `y` to download the matching macOS DMG or Windows installer into a unique folder under Downloads, verify its release-manifest SHA-256, and show the file. Open it to complete installation. Answer `n` or press Enter to keep using CLI only. Noninteractive runs skip the prompt.
|
|
52
|
+
|
|
53
|
+
Set `AGENTROAM_INSTALL_DESKTOP=yes` or `no` explicitly for automation. The Shell installer reads `/dev/tty`, so `curl ... | sh` remains interactive. A missing desktop artifact or failed download does not undo CLI installation. This requires a published CLI that includes `bin/desktop-download.mjs` and matching desktop release assets.
|
|
54
|
+
|
|
55
|
+
On macOS, the desktop app opens its own permission guide on first launch, even before connecting to a service. Screen Recording enables phone viewing; Accessibility enables keyboard and mouse control. Returning from System Settings rechecks permissions and resumes an enabled session. Restart the desktop app if macOS requires it. Desktop live publishing follows the selected CLI service, including its actual port. Windows desktop download is supported, but the current screen/input helper is macOS-only.
|
|
@@ -2,7 +2,10 @@ $ErrorActionPreference = "Stop"
|
|
|
2
2
|
$ProgressPreference = "SilentlyContinue"
|
|
3
3
|
|
|
4
4
|
$NodeVersion = "22.22.0"
|
|
5
|
-
$
|
|
5
|
+
$MinimumNodeVersion = "22.22.0"
|
|
6
|
+
# 默认跟随 npm preview 最新标签;可用 $env:AGENTROAM_VERSION 钉精确版本
|
|
7
|
+
$AgentRoamVersionRequest = if ($env:AGENTROAM_VERSION) { $env:AGENTROAM_VERSION } else { "preview" }
|
|
8
|
+
$AgentRoamVersion = "pending-version"
|
|
6
9
|
$NodeArchive = "node-v22.22.0-win-x64.zip"
|
|
7
10
|
$NodeSha256 = "c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
|
|
8
11
|
$NodeUrl = "https://nodejs.org/dist/v22.22.0/$NodeArchive"
|
|
@@ -20,16 +23,16 @@ $ServiceRootExplicit = -not [string]::IsNullOrWhiteSpace($env:AGENTROAM_ROOT)
|
|
|
20
23
|
$ServiceRoot = if ($ServiceRootExplicit) { $env:AGENTROAM_ROOT } else { (Get-Location).Path }
|
|
21
24
|
if (-not (Test-Path -LiteralPath $ServiceRoot -PathType Container)) { throw "AgentRoam root is not a directory: $ServiceRoot" }
|
|
22
25
|
$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
26
|
New-Item -ItemType Directory -Force -Path $NodeParent, $LauncherParent, $WrapperDir | Out-Null
|
|
29
27
|
|
|
30
|
-
function Test-
|
|
28
|
+
function Test-NodeVersion([string]$VersionText) {
|
|
29
|
+
if ($VersionText -notmatch '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$') { return $false }
|
|
30
|
+
try { return [Version]($VersionText.Split('+')[0]) -ge [Version]$MinimumNodeVersion } catch { return $false }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function Test-SupportedNode([string]$NodeBin) {
|
|
31
34
|
if (-not (Test-Path -LiteralPath $NodeBin -PathType Leaf)) { return $false }
|
|
32
|
-
try { return (& $NodeBin -p 'process.versions.node
|
|
35
|
+
try { return Test-NodeVersion ((& $NodeBin -p 'process.versions.node' 2>$null).Trim()) } catch { return $false }
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
function Get-NvmRoots {
|
|
@@ -50,15 +53,15 @@ function Get-NvmRoots {
|
|
|
50
53
|
@($Candidates | Where-Object { $_ -and (Test-Path -LiteralPath $_ -PathType Container) } | Select-Object -Unique)
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
function Find-
|
|
56
|
+
function Find-NvmNode {
|
|
54
57
|
$VersionMatches = foreach ($NvmRoot in (Get-NvmRoots)) {
|
|
55
58
|
foreach ($VersionDirectory in Get-ChildItem -LiteralPath $NvmRoot -Directory -ErrorAction SilentlyContinue) {
|
|
56
59
|
$Candidate = Join-Path $VersionDirectory.FullName "node.exe"
|
|
57
60
|
if (-not (Test-Path -LiteralPath $Candidate -PathType Leaf)) { continue }
|
|
58
61
|
try {
|
|
59
62
|
$VersionText = (& $Candidate -p 'process.versions.node' 2>$null).Trim()
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
if (Test-NodeVersion $VersionText) {
|
|
64
|
+
$Version = [Version]($VersionText.Split('+')[0])
|
|
62
65
|
[pscustomobject]@{ Path = $Candidate; Version = $Version }
|
|
63
66
|
}
|
|
64
67
|
} catch {}
|
|
@@ -97,10 +100,10 @@ function Enter-InstallLock([string]$Path) {
|
|
|
97
100
|
|
|
98
101
|
$SystemNode = Get-Command node.exe -ErrorAction SilentlyContinue
|
|
99
102
|
if ($env:AGENTROAM_BOOTSTRAP_TEST -eq "1" -and $env:AGENTROAM_FORCE_PRIVATE_NODE -eq "1") { $SystemNode = $null }
|
|
100
|
-
if ($SystemNode -and (Test-
|
|
103
|
+
if ($SystemNode -and (Test-SupportedNode $SystemNode.Source)) {
|
|
101
104
|
$NodeBin = $SystemNode.Source
|
|
102
105
|
} else {
|
|
103
|
-
$NvmNode = if ($env:AGENTROAM_BOOTSTRAP_TEST -eq "1" -and $env:AGENTROAM_FORCE_PRIVATE_NODE -eq "1") { $null } else { Find-
|
|
106
|
+
$NvmNode = if ($env:AGENTROAM_BOOTSTRAP_TEST -eq "1" -and $env:AGENTROAM_FORCE_PRIVATE_NODE -eq "1") { $null } else { Find-NvmNode }
|
|
104
107
|
if ($NvmNode) {
|
|
105
108
|
$NodeBin = $NvmNode
|
|
106
109
|
Write-Host "Using Node.js $(& $NodeBin --version) from NVM: $NodeBin"
|
|
@@ -158,6 +161,17 @@ $NpmCandidates = @(
|
|
|
158
161
|
$NpmCli = $NpmCandidates | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
|
|
159
162
|
if (-not $NpmCli) { throw "npm CLI was not found beside $NodeBin" }
|
|
160
163
|
|
|
164
|
+
# 解析 dist-tag → 精确版本(安装最总是最新发布)
|
|
165
|
+
if ($AgentRoamVersionRequest -notmatch '\d$') {
|
|
166
|
+
$Resolved = (& $NodeBin $NpmCli view "agentroam@$AgentRoamVersionRequest" version --registry $NpmRegistry 2>$null | Select-Object -Last 1)
|
|
167
|
+
if (-not $Resolved) { throw "could not resolve agentroam@$AgentRoamVersionRequest from $NpmRegistry" }
|
|
168
|
+
$AgentRoamVersion = ([string]$Resolved).Trim()
|
|
169
|
+
Write-Host "Installing AgentRoam $AgentRoamVersion (resolved from $AgentRoamVersionRequest)"
|
|
170
|
+
} else {
|
|
171
|
+
$AgentRoamVersion = $AgentRoamVersionRequest
|
|
172
|
+
}
|
|
173
|
+
$LauncherRoot = Join-Path $LauncherParent $AgentRoamVersion
|
|
174
|
+
$LauncherLock = Join-Path $LauncherRoot ".lock"
|
|
161
175
|
$PackageSpec = "agentroam@$AgentRoamVersion"
|
|
162
176
|
$ExtraPackageSpecs = @()
|
|
163
177
|
if ($env:AGENTROAM_BOOTSTRAP_TEST -eq "1" -and $env:AGENTROAM_PACKAGE_SPEC) {
|
|
@@ -221,3 +235,21 @@ if ($env:AGENTROAM_INSTALL_SKIP_SERVICE -eq "1") {
|
|
|
221
235
|
}
|
|
222
236
|
Write-Host "`nAgentRoam $AgentRoamVersion installed: $WrapperPath"
|
|
223
237
|
Write-Host "Open a new terminal, then run: agentroam service status"
|
|
238
|
+
|
|
239
|
+
$DesktopChoice = if ($env:AGENTROAM_INSTALL_DESKTOP) { $env:AGENTROAM_INSTALL_DESKTOP } else { "ask" }
|
|
240
|
+
if ($env:AGENTROAM_INSTALL_SKIP_SERVICE -eq "1" -and $DesktopChoice -eq "ask") { $DesktopChoice = "no" }
|
|
241
|
+
if ($DesktopChoice -eq "ask") {
|
|
242
|
+
$DesktopChoice = "no"
|
|
243
|
+
if ([Environment]::UserInteractive -and -not [Console]::IsInputRedirected) {
|
|
244
|
+
try { $DesktopChoice = Read-Host "是否下载桌面端?用于手机查看和控制电脑桌面 [y/N]" } catch { $DesktopChoice = "no" }
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if ($DesktopChoice -match '^(y|yes|是)$') {
|
|
248
|
+
$DesktopHelper = Join-Path $LauncherRoot "node_modules\agentroam\bin\desktop-download.mjs"
|
|
249
|
+
if (Test-Path -LiteralPath $DesktopHelper -PathType Leaf) {
|
|
250
|
+
& $NodeBin $DesktopHelper $AgentRoamVersion
|
|
251
|
+
if ($LASTEXITCODE -ne 0) { Write-Warning "CLI 安装已完成,桌面端可稍后重新下载。" }
|
|
252
|
+
} else {
|
|
253
|
+
Write-Warning "当前发布的 CLI 尚未包含桌面下载功能,请在新版发布后重试。CLI 安装已完成。"
|
|
254
|
+
}
|
|
255
|
+
} else { Write-Host "已跳过桌面端下载,CLI 可正常使用。" }
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
set -eu
|
|
3
3
|
|
|
4
4
|
NODE_VERSION="22.22.0"
|
|
5
|
-
|
|
5
|
+
MINIMUM_NODE_VERSION="22.22.0"
|
|
6
|
+
# Default to the newest published preview; pin an exact version via
|
|
7
|
+
# AGENTROAM_VERSION=0.2.0-preview.18 sh install-agentroam.sh
|
|
8
|
+
AGENTROAM_VERSION_REQUEST="${AGENTROAM_VERSION:-preview}"
|
|
9
|
+
AGENTROAM_VERSION=""
|
|
6
10
|
NODE_ARCHIVE="node-v22.22.0-darwin-arm64.tar.xz"
|
|
7
11
|
NODE_SHA256="2bd596bbfc4a275ceb8721a5954ee97daea5ebe673e96a185ebd732f6fb023ac"
|
|
8
12
|
NODE_URL="https://nodejs.org/dist/v22.22.0/$NODE_ARCHIVE"
|
|
@@ -12,14 +16,12 @@ NODE_PARENT="$DATA_DIR/runtimes/node"
|
|
|
12
16
|
NODE_ROOT="$NODE_PARENT/$NODE_VERSION"
|
|
13
17
|
NODE_LOCK="$NODE_ROOT.lock"
|
|
14
18
|
LAUNCHER_PARENT="$DATA_DIR/launcher"
|
|
15
|
-
LAUNCHER_ROOT="$LAUNCHER_PARENT
|
|
19
|
+
LAUNCHER_ROOT="$LAUNCHER_PARENT/pending-version"
|
|
16
20
|
LAUNCHER_LOCK="$LAUNCHER_ROOT.lock"
|
|
17
21
|
WRAPPER_DIR="$HOME/.local/bin"
|
|
18
22
|
WRAPPER_PATH="$WRAPPER_DIR/agentroam"
|
|
19
|
-
SERVICE_ROOT_EXPLICIT=0
|
|
20
23
|
if [ "${AGENTROAM_ROOT+x}" = "x" ]; then
|
|
21
24
|
SERVICE_ROOT="$AGENTROAM_ROOT"
|
|
22
|
-
SERVICE_ROOT_EXPLICIT=1
|
|
23
25
|
else
|
|
24
26
|
SERVICE_ROOT="$PWD"
|
|
25
27
|
fi
|
|
@@ -48,33 +50,44 @@ command -v shasum >/dev/null 2>&1 || fail "shasum is required"
|
|
|
48
50
|
[ -x /usr/bin/tar ] || fail "/usr/bin/tar is required"
|
|
49
51
|
[ -d "$SERVICE_ROOT" ] || fail "AgentRoam root is not a directory: $SERVICE_ROOT"
|
|
50
52
|
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
53
|
SERVICE_ROOT="$service_root_physical"
|
|
56
54
|
|
|
57
55
|
mkdir -p "$NODE_PARENT" "$LAUNCHER_PARENT" "$WRAPPER_DIR"
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
version_is_supported() {
|
|
58
|
+
awk -v version="$1" -v minimum="$MINIMUM_NODE_VERSION" 'BEGIN {
|
|
59
|
+
if (version !~ /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/) exit 1;
|
|
60
|
+
sub(/\+.*/, "", version);
|
|
61
|
+
split(version, actual, "."); split(minimum, required, ".");
|
|
62
|
+
for (component = 1; component <= 3; component++) {
|
|
63
|
+
if (actual[component] + 0 > required[component] + 0) exit 0;
|
|
64
|
+
if (actual[component] + 0 < required[component] + 0) exit 1;
|
|
65
|
+
}
|
|
66
|
+
exit 0;
|
|
67
|
+
}'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
node_is_supported() {
|
|
71
|
+
[ -x "$1" ] && version_is_supported "$("$1" -p 'process.versions.node' 2>/dev/null || true)"
|
|
61
72
|
}
|
|
62
73
|
|
|
63
|
-
|
|
74
|
+
find_nvm_node() {
|
|
64
75
|
best_bin=""
|
|
76
|
+
best_major=-1
|
|
65
77
|
best_minor=-1
|
|
66
78
|
best_patch=-1
|
|
67
79
|
for nvm_root in "${NVM_DIR:-}" "$HOME/.nvm"; do
|
|
68
80
|
[ -n "$nvm_root" ] || continue
|
|
69
|
-
for candidate in "$nvm_root"/versions/node/
|
|
81
|
+
for candidate in "$nvm_root"/versions/node/v*/bin/node; do
|
|
70
82
|
[ -x "$candidate" ] || continue
|
|
71
83
|
version=$("$candidate" -p 'process.versions.node' 2>/dev/null || true)
|
|
84
|
+
version_is_supported "$version" || continue
|
|
72
85
|
major=$(printf '%s\n' "$version" | awk -F. '{ print $1 + 0 }')
|
|
73
86
|
minor=$(printf '%s\n' "$version" | awk -F. '{ print $2 + 0 }')
|
|
74
87
|
patch=$(printf '%s\n' "$version" | awk -F. '{ sub(/[^0-9].*$/, "", $3); print $3 + 0 }')
|
|
75
|
-
[ "$major" -eq
|
|
76
|
-
if [ "$minor" -gt "$best_minor" ] || { [ "$minor" -eq "$best_minor" ] && [ "$patch" -gt "$best_patch" ]; }; then
|
|
88
|
+
if [ "$major" -gt "$best_major" ] || { [ "$major" -eq "$best_major" ] && { [ "$minor" -gt "$best_minor" ] || { [ "$minor" -eq "$best_minor" ] && [ "$patch" -gt "$best_patch" ]; }; }; }; then
|
|
77
89
|
best_bin="$candidate"
|
|
90
|
+
best_major=$major
|
|
78
91
|
best_minor=$minor
|
|
79
92
|
best_patch=$patch
|
|
80
93
|
fi
|
|
@@ -112,13 +125,13 @@ SYSTEM_NODE=$(command -v node 2>/dev/null || true)
|
|
|
112
125
|
if [ "${AGENTROAM_BOOTSTRAP_TEST:-}" = "1" ] && [ "${AGENTROAM_FORCE_PRIVATE_NODE:-}" = "1" ]; then
|
|
113
126
|
SYSTEM_NODE=""
|
|
114
127
|
fi
|
|
115
|
-
if [ -n "$SYSTEM_NODE" ] &&
|
|
128
|
+
if [ -n "$SYSTEM_NODE" ] && node_is_supported "$SYSTEM_NODE"; then
|
|
116
129
|
NODE_BIN="$SYSTEM_NODE"
|
|
117
130
|
else
|
|
118
131
|
if [ "${AGENTROAM_BOOTSTRAP_TEST:-}" = "1" ] && [ "${AGENTROAM_FORCE_PRIVATE_NODE:-}" = "1" ]; then
|
|
119
132
|
NVM_NODE=""
|
|
120
133
|
else
|
|
121
|
-
NVM_NODE=$(
|
|
134
|
+
NVM_NODE=$(find_nvm_node || true)
|
|
122
135
|
fi
|
|
123
136
|
if [ -n "$NVM_NODE" ]; then
|
|
124
137
|
NODE_BIN="$NVM_NODE"
|
|
@@ -184,7 +197,23 @@ find_npm_cli() {
|
|
|
184
197
|
}
|
|
185
198
|
|
|
186
199
|
NPM_CLI=$(find_npm_cli)
|
|
200
|
+
case "$AGENTROAM_VERSION_REQUEST" in
|
|
201
|
+
*[0-9])
|
|
202
|
+
# Looks like an exact version — honor the pin as-is.
|
|
203
|
+
AGENTROAM_VERSION="$AGENTROAM_VERSION_REQUEST"
|
|
204
|
+
;;
|
|
205
|
+
*)
|
|
206
|
+
# A dist-tag (default "preview") — resolve to the newest published version
|
|
207
|
+
# so installs always pick up the latest release without touching this file.
|
|
208
|
+
resolved_version="$("$NODE_BIN" "$NPM_CLI" view "agentroam@$AGENTROAM_VERSION_REQUEST" version --registry "$NPM_REGISTRY" 2>/dev/null | tail -1 | tr -d '[:space:]')"
|
|
209
|
+
[ -n "$resolved_version" ] || fail "could not resolve agentroam@$AGENTROAM_VERSION_REQUEST from $NPM_REGISTRY"
|
|
210
|
+
AGENTROAM_VERSION="$resolved_version"
|
|
211
|
+
printf 'Installing AgentRoam %s (resolved from %s)\n' "$AGENTROAM_VERSION" "$AGENTROAM_VERSION_REQUEST"
|
|
212
|
+
;;
|
|
213
|
+
esac
|
|
187
214
|
PACKAGE_SPEC="agentroam@$AGENTROAM_VERSION"
|
|
215
|
+
LAUNCHER_ROOT="$LAUNCHER_PARENT/$AGENTROAM_VERSION"
|
|
216
|
+
LAUNCHER_LOCK="$LAUNCHER_ROOT.lock"
|
|
188
217
|
EXTRA_PACKAGE_SPECS=""
|
|
189
218
|
if [ "${AGENTROAM_BOOTSTRAP_TEST:-}" = "1" ] && [ -n "${AGENTROAM_PACKAGE_SPEC:-}" ]; then
|
|
190
219
|
PACKAGE_SPEC="$AGENTROAM_PACKAGE_SPEC"
|
|
@@ -235,3 +264,26 @@ case ":${PATH:-}:" in
|
|
|
235
264
|
*":$WRAPPER_DIR:"*) ;;
|
|
236
265
|
*) printf 'Add this directory to PATH: export PATH="%s:$PATH"\n' "$WRAPPER_DIR" ;;
|
|
237
266
|
esac
|
|
267
|
+
|
|
268
|
+
# stdin is the script itself when launched with curl | sh. Read the choice
|
|
269
|
+
# from the controlling terminal instead; unattended installs never block.
|
|
270
|
+
desktop_choice="${AGENTROAM_INSTALL_DESKTOP:-ask}"
|
|
271
|
+
if [ "${AGENTROAM_INSTALL_SKIP_SERVICE:-}" = "1" ] && [ "$desktop_choice" = "ask" ]; then desktop_choice=no; fi
|
|
272
|
+
if [ "$desktop_choice" = "ask" ]; then
|
|
273
|
+
desktop_choice=no
|
|
274
|
+
if ( : </dev/tty ) 2>/dev/null; then
|
|
275
|
+
printf '\n是否下载桌面端?用于手机查看和控制电脑桌面 [y/N]:' >/dev/tty
|
|
276
|
+
IFS= read -r desktop_choice </dev/tty || desktop_choice=no
|
|
277
|
+
fi
|
|
278
|
+
fi
|
|
279
|
+
case "$desktop_choice" in
|
|
280
|
+
y|Y|yes|YES|是)
|
|
281
|
+
desktop_helper="$LAUNCHER_ROOT/node_modules/agentroam/bin/desktop-download.mjs"
|
|
282
|
+
if [ -f "$desktop_helper" ]; then
|
|
283
|
+
"$NODE_BIN" "$desktop_helper" "$AGENTROAM_VERSION" || printf 'CLI 安装已完成,桌面端可稍后重新下载。\n'
|
|
284
|
+
else
|
|
285
|
+
printf '当前发布的 CLI 尚未包含桌面下载功能,请在新版发布后重试。CLI 安装已完成。\n'
|
|
286
|
+
fi
|
|
287
|
+
;;
|
|
288
|
+
*) printf '已跳过桌面端下载,CLI 可正常使用。\n' ;;
|
|
289
|
+
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.18",
|
|
4
4
|
"description": "Zero-install mobile remote terminal and file console",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"agent-tui": "./bin/agent-tui.mjs"
|
|
9
9
|
},
|
|
10
10
|
"engines": {
|
|
11
|
-
"node": ">=22
|
|
11
|
+
"node": ">=22.22.0"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"bin",
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
"undici": "^6.25.0"
|
|
27
27
|
},
|
|
28
28
|
"optionalDependencies": {
|
|
29
|
-
"agentroam-runtime-darwin-arm64": "0.2.0-preview.
|
|
30
|
-
"agentroam-runtime-win32-x64": "0.2.0-preview.
|
|
31
|
-
"agentroam-cloudflared-darwin-arm64": "0.2.0-preview.
|
|
32
|
-
"agentroam-cloudflared-win32-x64": "0.2.0-preview.
|
|
33
|
-
"agentroam-tui-darwin-arm64": "0.2.0-preview.
|
|
34
|
-
"@caoqu/agentroam-tui-win32-x64": "0.2.0-preview.
|
|
29
|
+
"agentroam-runtime-darwin-arm64": "0.2.0-preview.18",
|
|
30
|
+
"agentroam-runtime-win32-x64": "0.2.0-preview.18",
|
|
31
|
+
"agentroam-cloudflared-darwin-arm64": "0.2.0-preview.18",
|
|
32
|
+
"agentroam-cloudflared-win32-x64": "0.2.0-preview.18",
|
|
33
|
+
"agentroam-tui-darwin-arm64": "0.2.0-preview.18",
|
|
34
|
+
"@caoqu/agentroam-tui-win32-x64": "0.2.0-preview.18"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^22.0.0",
|
package/dist/pairing.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
-
export function createPairingSecret(now = Date.now()) { const token = randomBytes(32).toString("base64url"); return { token, hashHex: createHash("sha256").update(token).digest("hex"), expiresAt: new Date(now + 5 * 60 * 1000).toISOString() }; }
|