@beeuni/agent-tunnel 0.1.2 → 0.1.3
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 +18 -7
- package/dist/agent-cli.js +87 -38
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -68,7 +68,10 @@ It starts at login, and the supervisor restarts it after every exit (launchd
|
|
|
68
68
|
`KeepAlive` with a 10 s throttle, systemd `Restart=always`, a 5 s Windows loop).
|
|
69
69
|
On Linux, install enables `loginctl enable-linger` so the user service keeps
|
|
70
70
|
running after logout and starts at boot; when that is not allowed, install
|
|
71
|
-
prints a warning. The Windows task runs
|
|
71
|
+
prints a warning. The Windows task runs PowerShell under `conhost --headless`
|
|
72
|
+
(no console window, also where Windows Terminal is the default terminal), on
|
|
73
|
+
battery, with no time limit. Every process the agent starts on Windows sets
|
|
74
|
+
`windowsHide`, so no console window opens.
|
|
72
75
|
The agent itself never stops on its own:
|
|
73
76
|
|
|
74
77
|
- It reconnects with exponential backoff from 1 s to 30 s, ±20 % jitter, forever.
|
|
@@ -181,9 +184,10 @@ change to the agent as `tunnel.access.set`:
|
|
|
181
184
|
A pairing made from the Beeuni desktop app starts in `ask`: the app shows the
|
|
182
185
|
prompt. A pairing made with this CLI alone starts in `always`, because nothing
|
|
183
186
|
on the machine could answer a prompt. This is a deliberate deviation from
|
|
184
|
-
"new pairings default to ask".
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
"new pairings default to ask". The mode and keep awake are the owner's
|
|
188
|
+
machine settings: pairing again (`--reauth`, a rejected or stale credential)
|
|
189
|
+
keeps them, and only a grant or a denial from the earlier pairing is dropped.
|
|
190
|
+
`logout` deletes `access.json`, so the next pairing starts from the default. The agent home (`config.json`, `access.json`, `desktop-app.json`)
|
|
187
191
|
is always a blocked path: no file or shell call can change these files.
|
|
188
192
|
|
|
189
193
|
The agent reports its state to Beeuni as the signed notification
|
|
@@ -233,9 +237,16 @@ you trust with this computer.
|
|
|
233
237
|
|
|
234
238
|
## Computer Use driver
|
|
235
239
|
|
|
236
|
-
Agent Tunnel never downloads or executes a desktop driver.
|
|
237
|
-
|
|
238
|
-
|
|
240
|
+
Agent Tunnel never downloads or executes a desktop driver. The Beeuni desktop
|
|
241
|
+
app bundles `cua-driver` on macOS and Windows (x64), and the agent it runs uses
|
|
242
|
+
that copy. A CLI install uses an existing binary from `CUA_DRIVER_BIN`,
|
|
243
|
+
`~/.local/bin`, `/usr/local/bin`, or `/opt/homebrew/bin`.
|
|
244
|
+
|
|
245
|
+
An approved `desktop` registers whenever the driver is present. Missing macOS
|
|
246
|
+
grants do not unregister it: the agent reports `desktopPermissions` and the
|
|
247
|
+
setup asks for them. When the driver is missing or rejected (wrong owner,
|
|
248
|
+
group-writable), `desktop` does not register and the reason is reported as
|
|
249
|
+
`unavailableCapabilities.desktop` in `status --json` and in `machineInfo`.
|
|
239
250
|
Treat that binary as trusted local code. Agent Tunnel does not verify or update
|
|
240
251
|
it.
|
|
241
252
|
|
package/dist/agent-cli.js
CHANGED
|
@@ -3516,6 +3516,15 @@ function writePrivateJson(file, home, value) {
|
|
|
3516
3516
|
function writeAccess(state, home = agentTunnelHome()) {
|
|
3517
3517
|
writePrivateJson(accessFilePath(home), home, state);
|
|
3518
3518
|
}
|
|
3519
|
+
function accessAfterPairing(home, desktopApp) {
|
|
3520
|
+
const kept = existsSync2(accessFilePath(home)) ? readAccess(home) : null;
|
|
3521
|
+
return {
|
|
3522
|
+
mode: kept?.mode ?? (desktopApp ? "ask" : "always"),
|
|
3523
|
+
grantedUntil: null,
|
|
3524
|
+
deniedUntil: null,
|
|
3525
|
+
keepAwake: kept?.keepAwake ?? false
|
|
3526
|
+
};
|
|
3527
|
+
}
|
|
3519
3528
|
function decideAccess(state, now = Date.now()) {
|
|
3520
3529
|
if (state.mode === "always")
|
|
3521
3530
|
return "run";
|
|
@@ -3803,7 +3812,7 @@ function machineDisplayName({
|
|
|
3803
3812
|
os = platform2(),
|
|
3804
3813
|
host = hostname(),
|
|
3805
3814
|
env = process.env,
|
|
3806
|
-
run = (command, args) => execFileSync(command, args, { encoding: "utf8", timeout: 1000, stdio: ["ignore", "pipe", "ignore"] })
|
|
3815
|
+
run = (command, args) => execFileSync(command, args, { encoding: "utf8", timeout: 1000, stdio: ["ignore", "pipe", "ignore"], windowsHide: true })
|
|
3807
3816
|
} = {}) {
|
|
3808
3817
|
const fallback = host.replace(/\.local$/i, "") || host;
|
|
3809
3818
|
try {
|
|
@@ -3815,7 +3824,7 @@ function machineDisplayName({
|
|
|
3815
3824
|
}
|
|
3816
3825
|
function machineId({
|
|
3817
3826
|
os = platform2(),
|
|
3818
|
-
run = (command, args) => execFileSync(command, args, { encoding: "utf8", timeout: 2000, stdio: ["ignore", "pipe", "ignore"] }),
|
|
3827
|
+
run = (command, args) => execFileSync(command, args, { encoding: "utf8", timeout: 2000, stdio: ["ignore", "pipe", "ignore"], windowsHide: true }),
|
|
3819
3828
|
read = (path) => readFileSync4(path, "utf8")
|
|
3820
3829
|
} = {}) {
|
|
3821
3830
|
const attempt = (source) => {
|
|
@@ -4460,7 +4469,8 @@ class TunnelAgent {
|
|
|
4460
4469
|
osVersion: release(),
|
|
4461
4470
|
agentVersion: AGENT_VERSION,
|
|
4462
4471
|
homeDir: this.config.workingDir,
|
|
4463
|
-
allowedPaths: this.config.allowedPaths
|
|
4472
|
+
allowedPaths: this.config.allowedPaths,
|
|
4473
|
+
unavailableCapabilities: Object.fromEntries(this.registry.unavailable)
|
|
4464
4474
|
}
|
|
4465
4475
|
}
|
|
4466
4476
|
});
|
|
@@ -4585,7 +4595,7 @@ class TunnelAgent {
|
|
|
4585
4595
|
const blocker = keepAwakeCommand(platform3(), process.pid);
|
|
4586
4596
|
if (!blocker)
|
|
4587
4597
|
return;
|
|
4588
|
-
const child = spawn3(blocker.command, blocker.args, { stdio: "ignore" });
|
|
4598
|
+
const child = spawn3(blocker.command, blocker.args, { stdio: "ignore", windowsHide: true });
|
|
4589
4599
|
child.on("error", () => {
|
|
4590
4600
|
this.keepAwakeFailed = true;
|
|
4591
4601
|
log(`${c.yellow}!${c.reset}`, `Keep awake is unavailable: ${blocker.command} could not start`);
|
|
@@ -4753,6 +4763,7 @@ async function printStartupBanner({
|
|
|
4753
4763
|
// src/agent/capabilities/index.ts
|
|
4754
4764
|
class CapabilityRegistry {
|
|
4755
4765
|
capabilities = new Map;
|
|
4766
|
+
unavailable = new Map;
|
|
4756
4767
|
register(capability) {
|
|
4757
4768
|
this.capabilities.set(capability.name, capability);
|
|
4758
4769
|
}
|
|
@@ -4778,7 +4789,7 @@ class CapabilityRegistry {
|
|
|
4778
4789
|
// src/agent/capabilities/desktop/cua-driver.ts
|
|
4779
4790
|
import { spawn as spawn4 } from "child_process";
|
|
4780
4791
|
import { existsSync as existsSync3, readFileSync as readFileSync6, realpathSync, rmSync as rmSync2, statSync as statSync2 } from "fs";
|
|
4781
|
-
import { homedir as homedir3, platform as platform5, tmpdir } from "os";
|
|
4792
|
+
import { homedir as homedir3, platform as platform5, tmpdir, userInfo } from "os";
|
|
4782
4793
|
import { basename as basename2, dirname as dirname3, join as join5 } from "path";
|
|
4783
4794
|
var MAX_DRIVER_OUTPUT_BYTES = 5 * 1024 * 1024;
|
|
4784
4795
|
var DRIVER_ENV_KEYS = [
|
|
@@ -4793,7 +4804,19 @@ var DRIVER_ENV_KEYS = [
|
|
|
4793
4804
|
"WAYLAND_DISPLAY",
|
|
4794
4805
|
"XDG_RUNTIME_DIR",
|
|
4795
4806
|
"XAUTHORITY",
|
|
4796
|
-
"DBUS_SESSION_BUS_ADDRESS"
|
|
4807
|
+
"DBUS_SESSION_BUS_ADDRESS",
|
|
4808
|
+
"SystemRoot",
|
|
4809
|
+
"windir",
|
|
4810
|
+
"USERPROFILE",
|
|
4811
|
+
"USERNAME",
|
|
4812
|
+
"USERDOMAIN",
|
|
4813
|
+
"APPDATA",
|
|
4814
|
+
"LOCALAPPDATA",
|
|
4815
|
+
"ProgramData",
|
|
4816
|
+
"TEMP",
|
|
4817
|
+
"TMP",
|
|
4818
|
+
"ComSpec",
|
|
4819
|
+
"PATHEXT"
|
|
4797
4820
|
];
|
|
4798
4821
|
function driverEnvironment(extra = {}) {
|
|
4799
4822
|
const env = { CUA_DRIVER_RS_TELEMETRY_ENABLED: "0" };
|
|
@@ -4856,7 +4879,8 @@ function execFile(cmd, args, timeoutMs = 30000, env = driverEnvironment()) {
|
|
|
4856
4879
|
return new Promise((resolve2, reject) => {
|
|
4857
4880
|
const proc = spawn4(cmd, args, {
|
|
4858
4881
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4859
|
-
env
|
|
4882
|
+
env,
|
|
4883
|
+
windowsHide: true
|
|
4860
4884
|
});
|
|
4861
4885
|
let stdout = "";
|
|
4862
4886
|
let stderr = "";
|
|
@@ -4943,9 +4967,8 @@ class CuaDriver {
|
|
|
4943
4967
|
constructor(options = {}) {
|
|
4944
4968
|
this.binary = options.binary ?? null;
|
|
4945
4969
|
this.embedded = options.embedded;
|
|
4946
|
-
const uid = typeof process.getuid === "function" ? process.getuid() : "user";
|
|
4947
4970
|
const home = serviceLabelFor(agentTunnelHome()).slice(SERVICE_LABEL.length);
|
|
4948
|
-
this.socketPath = options.socketPath ?? join5(tmpdir(), `beeuni-cua-${
|
|
4971
|
+
this.socketPath = options.socketPath ?? (process.platform === "win32" ? `\\\\.\\pipe\\beeuni-cua-${userInfo().username}${home}` : join5(tmpdir(), `beeuni-cua-${typeof process.getuid === "function" ? process.getuid() : "user"}${home}.sock`));
|
|
4949
4972
|
this.hostBundleId = options.hostBundleId;
|
|
4950
4973
|
}
|
|
4951
4974
|
isEmbedded(binary) {
|
|
@@ -4954,10 +4977,11 @@ class CuaDriver {
|
|
|
4954
4977
|
mode(binary) {
|
|
4955
4978
|
if (!this.isEmbedded(binary))
|
|
4956
4979
|
return { env: driverEnvironment(), socket: [] };
|
|
4980
|
+
const bundleId = this.hostBundleId ?? hostBundleId();
|
|
4957
4981
|
return {
|
|
4958
4982
|
env: driverEnvironment({
|
|
4959
4983
|
CUA_DRIVER_EMBEDDED: "1",
|
|
4960
|
-
CUA_DRIVER_HOST_BUNDLE_ID:
|
|
4984
|
+
...bundleId ? { CUA_DRIVER_HOST_BUNDLE_ID: bundleId } : {}
|
|
4961
4985
|
}),
|
|
4962
4986
|
socket: ["--socket", this.socketPath]
|
|
4963
4987
|
};
|
|
@@ -5031,7 +5055,8 @@ class CuaDriver {
|
|
|
5031
5055
|
const child = spawn4("open", ["-n", "-g", "-a", "CuaDriver", "--args", "serve"], {
|
|
5032
5056
|
detached: true,
|
|
5033
5057
|
stdio: "ignore",
|
|
5034
|
-
env: driverEnvironment()
|
|
5058
|
+
env: driverEnvironment(),
|
|
5059
|
+
windowsHide: true
|
|
5035
5060
|
});
|
|
5036
5061
|
child.unref();
|
|
5037
5062
|
await sleep2(750);
|
|
@@ -5039,7 +5064,8 @@ class CuaDriver {
|
|
|
5039
5064
|
const child = spawn4(bin, ["serve"], {
|
|
5040
5065
|
detached: true,
|
|
5041
5066
|
stdio: "ignore",
|
|
5042
|
-
env: driverEnvironment()
|
|
5067
|
+
env: driverEnvironment(),
|
|
5068
|
+
windowsHide: true
|
|
5043
5069
|
});
|
|
5044
5070
|
child.unref();
|
|
5045
5071
|
await sleep2(750);
|
|
@@ -5056,7 +5082,11 @@ class CuaDriver {
|
|
|
5056
5082
|
if (this.daemon && this.daemon.exitCode === null)
|
|
5057
5083
|
return;
|
|
5058
5084
|
const { env, socket } = this.mode(bin);
|
|
5059
|
-
if (
|
|
5085
|
+
if (process.platform === "win32") {
|
|
5086
|
+
await execFile(bin, ["stop", ...socket], 1e4, env).catch(() => {
|
|
5087
|
+
return;
|
|
5088
|
+
});
|
|
5089
|
+
} else if (existsSync3(this.socketPath)) {
|
|
5060
5090
|
await execFile(bin, ["stop", ...socket], 1e4, env).catch(() => {
|
|
5061
5091
|
return;
|
|
5062
5092
|
});
|
|
@@ -5064,7 +5094,8 @@ class CuaDriver {
|
|
|
5064
5094
|
}
|
|
5065
5095
|
const child = spawn4(bin, ["serve", "--embedded", "--no-permissions-gate", ...socket], {
|
|
5066
5096
|
stdio: "ignore",
|
|
5067
|
-
env
|
|
5097
|
+
env,
|
|
5098
|
+
windowsHide: true
|
|
5068
5099
|
});
|
|
5069
5100
|
child.unref();
|
|
5070
5101
|
child.on("exit", () => {
|
|
@@ -5075,16 +5106,23 @@ class CuaDriver {
|
|
|
5075
5106
|
});
|
|
5076
5107
|
this.daemon = child;
|
|
5077
5108
|
process.once("exit", () => child.kill());
|
|
5078
|
-
|
|
5109
|
+
const started = Date.now();
|
|
5110
|
+
while (Date.now() - started < 1e4 && !await this.endpointUp(bin)) {
|
|
5079
5111
|
if (child.exitCode !== null)
|
|
5080
5112
|
break;
|
|
5081
5113
|
await sleep2(50);
|
|
5082
5114
|
}
|
|
5083
|
-
if (!
|
|
5115
|
+
if (!await this.endpointUp(bin)) {
|
|
5084
5116
|
child.kill();
|
|
5085
5117
|
throw new Error("cua-driver did not start (no socket after 10 s)");
|
|
5086
5118
|
}
|
|
5087
5119
|
}
|
|
5120
|
+
async endpointUp(bin) {
|
|
5121
|
+
if (process.platform !== "win32")
|
|
5122
|
+
return existsSync3(this.socketPath);
|
|
5123
|
+
const { env, socket } = this.mode(bin);
|
|
5124
|
+
return execFile(bin, ["status", ...socket], 5000, env).then(({ stdout }) => !/not\s+running|unhealthy/i.test(stdout), () => false);
|
|
5125
|
+
}
|
|
5088
5126
|
async permissionOwner() {
|
|
5089
5127
|
return this.isEmbedded(await this.ensureInstalled()) ? "Beeuni" : "CuaDriver";
|
|
5090
5128
|
}
|
|
@@ -5105,7 +5143,7 @@ class CuaDriver {
|
|
|
5105
5143
|
async ensureDaemonReady() {
|
|
5106
5144
|
const bin = await this.ensureInstalled();
|
|
5107
5145
|
if (this.isEmbedded(bin)) {
|
|
5108
|
-
if (this.daemon?.exitCode === null && existsSync3(this.socketPath))
|
|
5146
|
+
if (this.daemon?.exitCode === null && (process.platform === "win32" ? this.daemonReady : existsSync3(this.socketPath)))
|
|
5109
5147
|
return;
|
|
5110
5148
|
this.stop();
|
|
5111
5149
|
await this.startDaemon();
|
|
@@ -5551,7 +5589,8 @@ function createShellCapability(config) {
|
|
|
5551
5589
|
cwd,
|
|
5552
5590
|
shell: false,
|
|
5553
5591
|
timeout,
|
|
5554
|
-
env: safeEnv
|
|
5592
|
+
env: safeEnv,
|
|
5593
|
+
windowsHide: true
|
|
5555
5594
|
});
|
|
5556
5595
|
let stdout = "";
|
|
5557
5596
|
let stderr = "";
|
|
@@ -5607,6 +5646,14 @@ function createShellCapability(config) {
|
|
|
5607
5646
|
}
|
|
5608
5647
|
|
|
5609
5648
|
// src/agent/capabilities/enabled-registry.ts
|
|
5649
|
+
var DRIVER_NOT_FOUND = "cua-driver was not found. The Beeuni desktop app bundles it on macOS and Windows; a CLI install needs it in CUA_DRIVER_BIN, ~/.local/bin, /usr/local/bin or /opt/homebrew/bin.";
|
|
5650
|
+
function desktopUnavailableReason(findDesktopDriver = findCuaDriverBinary) {
|
|
5651
|
+
try {
|
|
5652
|
+
return findDesktopDriver() ? null : DRIVER_NOT_FOUND;
|
|
5653
|
+
} catch (error) {
|
|
5654
|
+
return error instanceof Error ? error.message : String(error);
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
5610
5657
|
function createEnabledCapabilityRegistry(config, findDesktopDriver = findCuaDriverBinary, desktop = {}) {
|
|
5611
5658
|
const registry = new CapabilityRegistry;
|
|
5612
5659
|
const enabled = new Set(config.enabledCapabilities ?? ["filesystem", "shell", "desktop"]);
|
|
@@ -5614,8 +5661,12 @@ function createEnabledCapabilityRegistry(config, findDesktopDriver = findCuaDriv
|
|
|
5614
5661
|
registry.register(createFilesystemCapability(config));
|
|
5615
5662
|
if (enabled.has("shell"))
|
|
5616
5663
|
registry.register(createShellCapability(config));
|
|
5617
|
-
if (enabled.has("desktop")
|
|
5618
|
-
|
|
5664
|
+
if (enabled.has("desktop")) {
|
|
5665
|
+
const reason = desktopUnavailableReason(findDesktopDriver);
|
|
5666
|
+
if (reason)
|
|
5667
|
+
registry.unavailable.set("desktop", reason);
|
|
5668
|
+
else
|
|
5669
|
+
registry.register(createDesktopCapability(desktop));
|
|
5619
5670
|
}
|
|
5620
5671
|
return registry;
|
|
5621
5672
|
}
|
|
@@ -5764,7 +5815,7 @@ import { join as join10 } from "path";
|
|
|
5764
5815
|
// src/agent/service-drivers.ts
|
|
5765
5816
|
import { spawnSync } from "child_process";
|
|
5766
5817
|
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync8, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
5767
|
-
import { homedir as homedir4, platform as platform6, userInfo } from "os";
|
|
5818
|
+
import { homedir as homedir4, platform as platform6, userInfo as userInfo2 } from "os";
|
|
5768
5819
|
import { dirname as dirname6, join as join9 } from "path";
|
|
5769
5820
|
|
|
5770
5821
|
// src/agent/service-quoting.ts
|
|
@@ -5787,7 +5838,7 @@ function posixShellCommand(runner) {
|
|
|
5787
5838
|
}
|
|
5788
5839
|
var supervisor = {
|
|
5789
5840
|
run(command, args) {
|
|
5790
|
-
const result = spawnSync(command, args, { encoding: "utf8" });
|
|
5841
|
+
const result = spawnSync(command, args, { encoding: "utf8", windowsHide: true });
|
|
5791
5842
|
return {
|
|
5792
5843
|
ok: result.status === 0,
|
|
5793
5844
|
detail: [result.stdout, result.stderr].filter(Boolean).join(`
|
|
@@ -5805,7 +5856,7 @@ function joinDetails(...results) {
|
|
|
5805
5856
|
`);
|
|
5806
5857
|
}
|
|
5807
5858
|
function launchdTarget() {
|
|
5808
|
-
const uid = typeof process.getuid === "function" ? process.getuid() :
|
|
5859
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : userInfo2().uid;
|
|
5809
5860
|
return `gui/${uid}`;
|
|
5810
5861
|
}
|
|
5811
5862
|
function logPaths(paths) {
|
|
@@ -5878,7 +5929,8 @@ function renderWindowsPowerShellScript(runner) {
|
|
|
5878
5929
|
const args = runner.args.map(powershellQuote).join(" ");
|
|
5879
5930
|
const env = Object.entries(runner.env ?? {}).map(([key, value]) => `$env:${key} = ${powershellQuote(value)}
|
|
5880
5931
|
`).join("");
|
|
5881
|
-
return
|
|
5932
|
+
return `# Run by the Beeuni task under conhost --headless: no console window.
|
|
5933
|
+
$ErrorActionPreference = 'Continue'
|
|
5882
5934
|
${env}while ($true) {
|
|
5883
5935
|
# The pipe makes PowerShell wait for a GUI-subsystem binary (the desktop
|
|
5884
5936
|
# app's Beeuni.exe) and set $LASTEXITCODE; a bare call returns at once.
|
|
@@ -5938,7 +5990,7 @@ var launchd = {
|
|
|
5938
5990
|
},
|
|
5939
5991
|
status(paths, installed) {
|
|
5940
5992
|
const disabled = run("launchctl", ["print-disabled", launchdTarget()]);
|
|
5941
|
-
const enabled = !
|
|
5993
|
+
const enabled = ![`"${paths.label}" => disabled`, `"${paths.label}" => true`].some((line) => disabled.detail.includes(line));
|
|
5942
5994
|
const status = run("launchctl", ["print", `${launchdTarget()}/${paths.label}`]);
|
|
5943
5995
|
return {
|
|
5944
5996
|
active: status.ok,
|
|
@@ -5993,7 +6045,7 @@ var systemd = {
|
|
|
5993
6045
|
}
|
|
5994
6046
|
};
|
|
5995
6047
|
function ensureLinger() {
|
|
5996
|
-
const user =
|
|
6048
|
+
const user = userInfo2().username;
|
|
5997
6049
|
const linger = run("loginctl", ["show-user", user, "-p", "Linger"]);
|
|
5998
6050
|
if (/Linger=yes/.test(linger.detail))
|
|
5999
6051
|
return { ok: true, detail: "" };
|
|
@@ -6018,11 +6070,11 @@ function renderWindowsTaskXml(paths, user) {
|
|
|
6018
6070
|
<StartWhenAvailable>true</StartWhenAvailable>
|
|
6019
6071
|
<Enabled>true</Enabled>
|
|
6020
6072
|
</Settings>
|
|
6021
|
-
<Actions Context="Author"><Exec><Command>
|
|
6073
|
+
<Actions Context="Author"><Exec><Command>conhost.exe</Command><Arguments>${xmlEscape(`--headless powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File "${paths.windowsScript}"`)}</Arguments></Exec></Actions>
|
|
6022
6074
|
</Task>
|
|
6023
6075
|
`;
|
|
6024
6076
|
}
|
|
6025
|
-
var windowsUser = () => process.env.USERDOMAIN ? `${process.env.USERDOMAIN}\\${
|
|
6077
|
+
var windowsUser = () => process.env.USERDOMAIN ? `${process.env.USERDOMAIN}\\${userInfo2().username}` : userInfo2().username;
|
|
6026
6078
|
var windowsTaskXmlPath = (paths) => paths.windowsScript.replace(/\.ps1$/, ".xml");
|
|
6027
6079
|
var scheduledTask = {
|
|
6028
6080
|
unitPath: (paths) => paths.windowsScript,
|
|
@@ -6386,9 +6438,9 @@ function startAgent(config, options = {}) {
|
|
|
6386
6438
|
const registry = createEnabledCapabilityRegistry(config, undefined, {
|
|
6387
6439
|
onPermissionMissing: restart?.permissionMissing
|
|
6388
6440
|
});
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6441
|
+
const desktopReason = registry.unavailable.get("desktop");
|
|
6442
|
+
if (desktopReason)
|
|
6443
|
+
console.error(`[agent-tunnel] Computer Use is approved but unavailable: ${desktopReason}`);
|
|
6392
6444
|
if (options.service) {
|
|
6393
6445
|
process.stdout.write(`[agent-tunnel] service starting: ${config.tunnelId} -> ${config.apiUrl}
|
|
6394
6446
|
`);
|
|
@@ -6405,7 +6457,6 @@ function startAgent(config, options = {}) {
|
|
|
6405
6457
|
persistent: options.service === true,
|
|
6406
6458
|
reloadConfig: () => loadConfig({ apiUrl: config.apiUrl })
|
|
6407
6459
|
});
|
|
6408
|
-
agent.connect();
|
|
6409
6460
|
const shutdown = () => {
|
|
6410
6461
|
if (!options.service)
|
|
6411
6462
|
console.log(`
|
|
@@ -6415,6 +6466,7 @@ ${c.dim} Shutting down…${c.reset}`);
|
|
|
6415
6466
|
};
|
|
6416
6467
|
process.on("SIGTERM", shutdown);
|
|
6417
6468
|
process.on("SIGINT", shutdown);
|
|
6469
|
+
agent.connect();
|
|
6418
6470
|
}
|
|
6419
6471
|
function chooseBackgroundMode(flags) {
|
|
6420
6472
|
if (anyFlag(flags, BACKGROUND_FLAGS))
|
|
@@ -6525,12 +6577,7 @@ async function pairThisMachine(apiUrl, flags, lease) {
|
|
|
6525
6577
|
process.exit(1);
|
|
6526
6578
|
}
|
|
6527
6579
|
saveCredentials(outcome.tunnelId, outcome.token, apiUrl, outcome.capabilities);
|
|
6528
|
-
writeAccess(
|
|
6529
|
-
mode: existsSync7(desktopAppPath()) ? "ask" : "always",
|
|
6530
|
-
grantedUntil: null,
|
|
6531
|
-
deniedUntil: null,
|
|
6532
|
-
keepAwake: readAccess().keepAwake
|
|
6533
|
-
});
|
|
6580
|
+
writeAccess(accessAfterPairing(agentTunnelHome(), existsSync7(desktopAppPath())));
|
|
6534
6581
|
if (jsonMode) {
|
|
6535
6582
|
emit("approved", { tunnelId: outcome.tunnelId, capabilities: outcome.capabilities });
|
|
6536
6583
|
} else {
|
|
@@ -6637,12 +6684,14 @@ function commandStatus(flags) {
|
|
|
6637
6684
|
const service = getServiceStatus();
|
|
6638
6685
|
const paired = Boolean(config.token && config.tunnelId);
|
|
6639
6686
|
const approved = new Set(config.enabledCapabilities ?? []);
|
|
6687
|
+
const desktopReason = approved.has("desktop") ? desktopUnavailableReason() : null;
|
|
6640
6688
|
if (isTruthyFlag(flags.json)) {
|
|
6641
6689
|
console.log(JSON.stringify({
|
|
6642
6690
|
paired,
|
|
6643
6691
|
tunnelId: paired ? config.tunnelId : null,
|
|
6644
6692
|
apiUrl: config.apiUrl,
|
|
6645
6693
|
capabilities: [...approved],
|
|
6694
|
+
unavailableCapabilities: desktopReason ? { desktop: desktopReason } : {},
|
|
6646
6695
|
version: agentTunnelVersion(),
|
|
6647
6696
|
home: agentTunnelHome(),
|
|
6648
6697
|
serviceLabel: getServicePaths().label,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beeuni/agent-tunnel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Tunnel relay between cloud AI agents and local machines — server relay, local agent, client SDK, JSON-RPC, HMAC signing",
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
"dist/",
|
|
13
13
|
"README.md"
|
|
14
14
|
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "bun build src/agent/cli.ts --target=node --outfile=dist/agent-cli.js --banner='#!/usr/bin/env node' && bun build src/client/cli.ts --target=node --outfile=dist/client-cli.js --banner='#!/usr/bin/env node' && node -e \"const fs=require('node:fs'); fs.chmodSync('dist/agent-cli.js', 0o755); fs.chmodSync('dist/client-cli.js', 0o755);\"",
|
|
17
|
+
"prepack": "bun run build",
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"test": "bun test --timeout ${BEEUNI_TEST_TIMEOUT_MS:-15000}"
|
|
20
|
+
},
|
|
15
21
|
"dependencies": {
|
|
16
22
|
"hono": "^4.13.7",
|
|
17
23
|
"ws": "^8.18.0"
|