openmausbot 0.1.55 → 0.1.57
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/dist/assets/index-B433sv8K.css +1 -0
- package/dist/assets/index-DSh8AIUy.js +264 -0
- package/dist/assets/{index-D-u0LYsV.js → index-jWqt5dmY.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/companion/src/routes.js +218 -0
- package/dist-server/connector-proxy.js +31 -12
- package/dist-server/container-mcp.js +49 -31
- package/dist-server/drivers/agents-proxy.js +56 -2
- package/dist-server/index.js +2020 -361
- package/dist-server/openmausbot.js +45 -386
- package/dist-server/pair-cli.js +45 -386
- package/dist-server/server/auto-approve.js +48 -0
- package/dist-server/server/bot-folder.js +102 -0
- package/dist-server/server/bot-overview.js +206 -0
- package/dist-server/server/bot-package.js +5 -0
- package/dist-server/server/bot-profile.js +24 -0
- package/dist-server/server/channel-queue.js +1 -0
- package/dist-server/server/computer-control.js +15 -0
- package/dist-server/server/connector-proxy.js +37 -16
- package/dist-server/server/container-computer.js +9 -2
- package/dist-server/server/drivers/agents-proxy.js +63 -2
- package/dist-server/server/drivers/antigravity-acp.js +61 -13
- package/dist-server/server/drivers/antigravity-runtime.js +47 -3
- package/dist-server/server/drivers/claude.js +7 -1
- package/dist-server/server/exit.js +9 -0
- package/dist-server/server/index.js +707 -124
- package/dist-server/server/mcp-bridge.js +73 -44
- package/dist-server/server/openmausbot.js +3 -2
- package/dist-server/server/package-export.js +1 -0
- package/dist-server/server/pair-cli.js +3 -2
- package/dist-server/server/peer-approval.js +27 -0
- package/dist-server/server/peer-provenance.js +4 -1
- package/dist-server/server/peer-roster.js +15 -0
- package/dist-server/server/profile-requests.js +332 -0
- package/dist-server/server/profile-revision.js +15 -0
- package/dist-server/server/profile-versions.js +154 -0
- package/dist-server/server/request-auth.js +10 -0
- package/dist-server/server/routine-requests.js +34 -1
- package/dist-server/server/routines.js +70 -2
- package/dist-server/server/setup-mode.js +63 -0
- package/dist-server/server/skill-fetch.js +67 -11
- package/dist-server/server/skills.js +1 -1
- package/dist-server/server/store.js +87 -7
- package/dist-server/server/system-prompt.js +49 -0
- package/dist-server/server/team-backup.js +2 -2
- package/dist-server/server/team-manifest.js +7 -0
- package/dist-server/server/tunnel.js +1 -1
- package/dist-server/shared/bot-profile.js +5 -1
- package/dist-server/shared/line-diff.js +42 -0
- package/dist-server/shared/profile-request.js +9 -0
- package/dist-server/shared/team-backup.js +4 -0
- package/dist-server/tunnel-guardian.js +6 -19
- package/dist-server/vps-container-mcp.js +49 -31
- package/package.json +1 -1
- package/dist/assets/index-B9rLIY81.js +0 -263
- package/dist/assets/index-CJBrtDbC.css +0 -1
package/dist-server/pair-cli.js
CHANGED
|
@@ -1100,12 +1100,12 @@ var require_main = __commonJS({
|
|
|
1100
1100
|
|
|
1101
1101
|
// server/cli.ts
|
|
1102
1102
|
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
1103
|
-
import { spawn as
|
|
1103
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
1104
1104
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
1105
1105
|
import { homedir as homedir2 } from "node:os";
|
|
1106
1106
|
import { dirname as dirname2, join as join3, resolve as resolve2 } from "node:path";
|
|
1107
1107
|
import { createInterface } from "node:readline/promises";
|
|
1108
|
-
import { fileURLToPath as
|
|
1108
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1109
1109
|
|
|
1110
1110
|
// server/tailscale.ts
|
|
1111
1111
|
import { execFile } from "node:child_process";
|
|
@@ -1214,12 +1214,12 @@ async function tailscaleServeOff(status, httpsPort = 443) {
|
|
|
1214
1214
|
}
|
|
1215
1215
|
|
|
1216
1216
|
// server/tunnel.ts
|
|
1217
|
-
import { spawn as
|
|
1217
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
1218
1218
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
1219
1219
|
import { chmodSync, existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
1220
1220
|
import { hostname } from "node:os";
|
|
1221
1221
|
import { dirname, join as join2, resolve } from "node:path";
|
|
1222
|
-
import { fileURLToPath
|
|
1222
|
+
import { fileURLToPath } from "node:url";
|
|
1223
1223
|
|
|
1224
1224
|
// electron/control-plane-client.mjs
|
|
1225
1225
|
var INSTALLATION_CREDENTIAL = /^omb_install_[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}$/;
|
|
@@ -1348,10 +1348,10 @@ function createControlPlaneClient({
|
|
|
1348
1348
|
}) {
|
|
1349
1349
|
const origin = normalizeControlPlaneURL(baseURL);
|
|
1350
1350
|
if (!origin) throw new ControlPlaneError("control_plane_unavailable");
|
|
1351
|
-
const request = async (
|
|
1351
|
+
const request = async (path3, { method = "GET", token, body, allowEmpty = false, deadlineMs = timeoutMs } = {}) => {
|
|
1352
1352
|
const headers = new Headers({ accept: "application/json" });
|
|
1353
1353
|
if (token) headers.set("authorization", `Bearer ${token}`);
|
|
1354
|
-
if (
|
|
1354
|
+
if (path3.startsWith("/api/auth/")) headers.set("origin", origin);
|
|
1355
1355
|
if (body !== void 0) {
|
|
1356
1356
|
headers.set("content-type", "application/json");
|
|
1357
1357
|
}
|
|
@@ -1364,7 +1364,7 @@ function createControlPlaneClient({
|
|
|
1364
1364
|
signal: timeoutSignal(deadlineMs)
|
|
1365
1365
|
};
|
|
1366
1366
|
if (body !== void 0) init.body = JSON.stringify(body);
|
|
1367
|
-
response = await fetchImpl(`${origin}${
|
|
1367
|
+
response = await fetchImpl(`${origin}${path3}`, init);
|
|
1368
1368
|
} catch {
|
|
1369
1369
|
throw new ControlPlaneError("network_unavailable");
|
|
1370
1370
|
}
|
|
@@ -1530,19 +1530,17 @@ function createControlPlaneClient({
|
|
|
1530
1530
|
}
|
|
1531
1531
|
|
|
1532
1532
|
// electron/managed-companion-tunnel.mjs
|
|
1533
|
-
import { spawn
|
|
1533
|
+
import { spawn } from "node:child_process";
|
|
1534
1534
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1535
1535
|
import fs2 from "node:fs";
|
|
1536
1536
|
import os2 from "node:os";
|
|
1537
|
-
import
|
|
1537
|
+
import path2 from "node:path";
|
|
1538
1538
|
|
|
1539
1539
|
// electron/companion-origin-gateway.mjs
|
|
1540
1540
|
import fs from "node:fs";
|
|
1541
|
-
import http from "node:http";
|
|
1542
1541
|
import os from "node:os";
|
|
1543
1542
|
import path from "node:path";
|
|
1544
1543
|
import { randomUUID } from "node:crypto";
|
|
1545
|
-
var MANAGED_COMPANION_ORIGIN_HOST = "127.0.0.1";
|
|
1546
1544
|
var MANAGED_COMPANION_ORIGIN_PORT = 8812;
|
|
1547
1545
|
var RUNTIME_PREFIX = "omb-companion-origin-";
|
|
1548
1546
|
var SOCKET_NAME = "origin.sock";
|
|
@@ -1620,210 +1618,8 @@ function cleanupCompanionOriginEndpoint(endpoint, { platform = process.platform,
|
|
|
1620
1618
|
} catch {
|
|
1621
1619
|
}
|
|
1622
1620
|
}
|
|
1623
|
-
function unavailable(response) {
|
|
1624
|
-
if (response.headersSent) return response.destroy();
|
|
1625
|
-
const body = JSON.stringify({ error: "companion origin unavailable" });
|
|
1626
|
-
response.writeHead(503, {
|
|
1627
|
-
"cache-control": "private, no-store",
|
|
1628
|
-
"content-length": Buffer.byteLength(body),
|
|
1629
|
-
"content-type": "application/json"
|
|
1630
|
-
});
|
|
1631
|
-
response.end(body);
|
|
1632
|
-
}
|
|
1633
|
-
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
1634
|
-
"connection",
|
|
1635
|
-
"keep-alive",
|
|
1636
|
-
"proxy-authenticate",
|
|
1637
|
-
"proxy-authorization",
|
|
1638
|
-
"proxy-connection",
|
|
1639
|
-
"te",
|
|
1640
|
-
"trailer",
|
|
1641
|
-
"transfer-encoding",
|
|
1642
|
-
"upgrade"
|
|
1643
|
-
]);
|
|
1644
|
-
function endToEndHeaders(headers = {}) {
|
|
1645
|
-
const blocked = new Set(HOP_BY_HOP_HEADERS);
|
|
1646
|
-
const connection = Array.isArray(headers.connection) ? headers.connection.join(",") : String(headers.connection ?? "");
|
|
1647
|
-
for (const name of connection.split(",")) blocked.add(name.trim().toLowerCase());
|
|
1648
|
-
return Object.fromEntries(
|
|
1649
|
-
Object.entries(headers).filter(([name, value]) => value !== void 0 && !blocked.has(name.toLowerCase()))
|
|
1650
|
-
);
|
|
1651
|
-
}
|
|
1652
|
-
function upgradeHeaders(headers = {}) {
|
|
1653
|
-
if (String(headers.upgrade ?? "").toLowerCase() !== "websocket") return null;
|
|
1654
|
-
return {
|
|
1655
|
-
...endToEndHeaders(headers),
|
|
1656
|
-
connection: "Upgrade",
|
|
1657
|
-
upgrade: "websocket"
|
|
1658
|
-
};
|
|
1659
|
-
}
|
|
1660
|
-
function writeUpgrade(socket, response) {
|
|
1661
|
-
const lines = [];
|
|
1662
|
-
for (const name of [
|
|
1663
|
-
"upgrade",
|
|
1664
|
-
"connection",
|
|
1665
|
-
"sec-websocket-accept",
|
|
1666
|
-
"sec-websocket-protocol",
|
|
1667
|
-
"sec-websocket-extensions"
|
|
1668
|
-
]) {
|
|
1669
|
-
const value = response.headers[name];
|
|
1670
|
-
if (typeof value === "string") lines.push(`${name}: ${value}`);
|
|
1671
|
-
}
|
|
1672
|
-
socket.write(
|
|
1673
|
-
`HTTP/1.1 ${response.statusCode ?? 101} ${response.statusMessage ?? "Switching Protocols"}\r
|
|
1674
|
-
${lines.join("\r\n")}\r
|
|
1675
|
-
\r
|
|
1676
|
-
`
|
|
1677
|
-
);
|
|
1678
|
-
}
|
|
1679
|
-
function destroySockets(sockets) {
|
|
1680
|
-
for (const socket of sockets) socket.destroy();
|
|
1681
|
-
sockets.clear();
|
|
1682
|
-
}
|
|
1683
|
-
function createCompanionOriginGateway({
|
|
1684
|
-
target,
|
|
1685
|
-
originHost = MANAGED_COMPANION_ORIGIN_HOST,
|
|
1686
|
-
originPort = MANAGED_COMPANION_ORIGIN_PORT,
|
|
1687
|
-
isTargetAlive = () => true,
|
|
1688
|
-
createServer = http.createServer,
|
|
1689
|
-
request = http.request,
|
|
1690
|
-
upstreamTimeoutMs = 3e4
|
|
1691
|
-
} = {}) {
|
|
1692
|
-
if (!validCompanionOriginTarget(target)) {
|
|
1693
|
-
throw new Error("The companion origin target is invalid");
|
|
1694
|
-
}
|
|
1695
|
-
let accepting = true;
|
|
1696
|
-
let listening = false;
|
|
1697
|
-
let transition = Promise.resolve();
|
|
1698
|
-
const upgradedSockets = /* @__PURE__ */ new Set();
|
|
1699
|
-
const server = createServer((incoming, outgoing) => {
|
|
1700
|
-
if (!accepting || !isTargetAlive(target)) return unavailable(outgoing);
|
|
1701
|
-
const upstream = request(
|
|
1702
|
-
{
|
|
1703
|
-
headers: endToEndHeaders(incoming.headers),
|
|
1704
|
-
method: incoming.method,
|
|
1705
|
-
path: incoming.url,
|
|
1706
|
-
socketPath: target.socketPath,
|
|
1707
|
-
timeout: upstreamTimeoutMs
|
|
1708
|
-
},
|
|
1709
|
-
(response) => {
|
|
1710
|
-
upstream.setTimeout(0);
|
|
1711
|
-
if (!accepting || !isTargetAlive(target)) {
|
|
1712
|
-
response.destroy();
|
|
1713
|
-
return unavailable(outgoing);
|
|
1714
|
-
}
|
|
1715
|
-
outgoing.writeHead(response.statusCode ?? 502, endToEndHeaders(response.headers));
|
|
1716
|
-
response.once("error", () => outgoing.destroy());
|
|
1717
|
-
response.once("aborted", () => outgoing.destroy());
|
|
1718
|
-
response.once("close", () => {
|
|
1719
|
-
if (!response.complete) outgoing.destroy();
|
|
1720
|
-
});
|
|
1721
|
-
response.pipe(outgoing);
|
|
1722
|
-
}
|
|
1723
|
-
);
|
|
1724
|
-
upstream.once("timeout", () => upstream.destroy(new Error("companion origin timed out")));
|
|
1725
|
-
upstream.once("error", () => unavailable(outgoing));
|
|
1726
|
-
incoming.once("aborted", () => upstream.destroy());
|
|
1727
|
-
outgoing.once("close", () => upstream.destroy());
|
|
1728
|
-
incoming.pipe(upstream);
|
|
1729
|
-
});
|
|
1730
|
-
server.on("upgrade", (incoming, socket, head) => {
|
|
1731
|
-
if (!accepting || !isTargetAlive(target)) return socket.destroy();
|
|
1732
|
-
const headers = upgradeHeaders(incoming.headers);
|
|
1733
|
-
if (!headers || incoming.method !== "GET") return socket.destroy();
|
|
1734
|
-
upgradedSockets.add(socket);
|
|
1735
|
-
let remoteSocket = null;
|
|
1736
|
-
const release = () => {
|
|
1737
|
-
upgradedSockets.delete(socket);
|
|
1738
|
-
if (remoteSocket) upgradedSockets.delete(remoteSocket);
|
|
1739
|
-
};
|
|
1740
|
-
socket.once("close", release);
|
|
1741
|
-
const upstream = request({
|
|
1742
|
-
headers,
|
|
1743
|
-
method: "GET",
|
|
1744
|
-
path: incoming.url,
|
|
1745
|
-
socketPath: target.socketPath,
|
|
1746
|
-
timeout: upstreamTimeoutMs
|
|
1747
|
-
});
|
|
1748
|
-
upstream.once("timeout", () => upstream.destroy(new Error("companion origin upgrade timed out")));
|
|
1749
|
-
upstream.once("upgrade", (response, remote, remoteHead) => {
|
|
1750
|
-
upstream.setTimeout(0);
|
|
1751
|
-
remote.setTimeout(0);
|
|
1752
|
-
if (!accepting || !isTargetAlive(target)) {
|
|
1753
|
-
remote.destroy();
|
|
1754
|
-
socket.destroy();
|
|
1755
|
-
return;
|
|
1756
|
-
}
|
|
1757
|
-
writeUpgrade(socket, response);
|
|
1758
|
-
remoteSocket = remote;
|
|
1759
|
-
upgradedSockets.add(remote);
|
|
1760
|
-
remote.once("close", release);
|
|
1761
|
-
if (head.length) remote.write(head);
|
|
1762
|
-
if (remoteHead.length) socket.write(remoteHead);
|
|
1763
|
-
socket.pipe(remote).pipe(socket);
|
|
1764
|
-
});
|
|
1765
|
-
upstream.once("response", (response) => {
|
|
1766
|
-
response.resume();
|
|
1767
|
-
socket.destroy();
|
|
1768
|
-
});
|
|
1769
|
-
upstream.once("error", () => socket.destroy());
|
|
1770
|
-
socket.once("close", () => upstream.destroy());
|
|
1771
|
-
upstream.end();
|
|
1772
|
-
});
|
|
1773
|
-
server.on("clientError", (_error, socket) => socket.destroy());
|
|
1774
|
-
const serialize = (work) => {
|
|
1775
|
-
const next = transition.then(work, work);
|
|
1776
|
-
transition = next.then(
|
|
1777
|
-
() => {
|
|
1778
|
-
},
|
|
1779
|
-
() => {
|
|
1780
|
-
}
|
|
1781
|
-
);
|
|
1782
|
-
return next;
|
|
1783
|
-
};
|
|
1784
|
-
return Object.freeze({
|
|
1785
|
-
start() {
|
|
1786
|
-
return serialize(async () => {
|
|
1787
|
-
if (listening) return server.address();
|
|
1788
|
-
await new Promise((resolve3, reject) => {
|
|
1789
|
-
const onError = (error) => {
|
|
1790
|
-
server.off("listening", onListening);
|
|
1791
|
-
reject(error);
|
|
1792
|
-
};
|
|
1793
|
-
const onListening = () => {
|
|
1794
|
-
server.off("error", onError);
|
|
1795
|
-
resolve3();
|
|
1796
|
-
};
|
|
1797
|
-
server.once("error", onError);
|
|
1798
|
-
server.once("listening", onListening);
|
|
1799
|
-
server.listen({ exclusive: true, host: originHost, port: originPort });
|
|
1800
|
-
});
|
|
1801
|
-
listening = true;
|
|
1802
|
-
return server.address();
|
|
1803
|
-
});
|
|
1804
|
-
},
|
|
1805
|
-
invalidate() {
|
|
1806
|
-
accepting = false;
|
|
1807
|
-
server.closeAllConnections?.();
|
|
1808
|
-
destroySockets(upgradedSockets);
|
|
1809
|
-
},
|
|
1810
|
-
close() {
|
|
1811
|
-
return serialize(async () => {
|
|
1812
|
-
accepting = false;
|
|
1813
|
-
server.closeAllConnections?.();
|
|
1814
|
-
destroySockets(upgradedSockets);
|
|
1815
|
-
if (!listening) return;
|
|
1816
|
-
await new Promise((resolve3) => server.close(() => resolve3()));
|
|
1817
|
-
listening = false;
|
|
1818
|
-
});
|
|
1819
|
-
}
|
|
1820
|
-
});
|
|
1821
|
-
}
|
|
1822
1621
|
|
|
1823
1622
|
// electron/managed-companion-guardian.mjs
|
|
1824
|
-
import { spawn } from "node:child_process";
|
|
1825
|
-
import path2 from "node:path";
|
|
1826
|
-
import { fileURLToPath } from "node:url";
|
|
1827
1623
|
var copyEnvironmentValue = (source, target, names) => {
|
|
1828
1624
|
const entries = Object.entries(source);
|
|
1829
1625
|
for (const name of names) {
|
|
@@ -1845,147 +1641,6 @@ function minimalGuardianEnvironment(environment = process.env, platform = proces
|
|
|
1845
1641
|
ELECTRON_RUN_AS_NODE: "1"
|
|
1846
1642
|
};
|
|
1847
1643
|
}
|
|
1848
|
-
function processIsAlive(pid) {
|
|
1849
|
-
try {
|
|
1850
|
-
process.kill(pid, 0);
|
|
1851
|
-
return true;
|
|
1852
|
-
} catch (error) {
|
|
1853
|
-
return error?.code !== "ESRCH";
|
|
1854
|
-
}
|
|
1855
|
-
}
|
|
1856
|
-
var delay = (milliseconds) => new Promise((resolve3) => {
|
|
1857
|
-
const timer = setTimeout(resolve3, milliseconds);
|
|
1858
|
-
timer.unref?.();
|
|
1859
|
-
});
|
|
1860
|
-
async function terminateConnector(child, graceMs = 250) {
|
|
1861
|
-
if (!child || child.exitCode !== null || child.signalCode !== null) return;
|
|
1862
|
-
let exited = false;
|
|
1863
|
-
const exit = new Promise((resolve3) => {
|
|
1864
|
-
const finish = () => {
|
|
1865
|
-
if (exited) return;
|
|
1866
|
-
exited = true;
|
|
1867
|
-
resolve3();
|
|
1868
|
-
};
|
|
1869
|
-
child.once("exit", finish);
|
|
1870
|
-
child.once("error", () => {
|
|
1871
|
-
if (!Number.isSafeInteger(child.pid)) finish();
|
|
1872
|
-
});
|
|
1873
|
-
});
|
|
1874
|
-
try {
|
|
1875
|
-
child.kill("SIGTERM");
|
|
1876
|
-
} catch {
|
|
1877
|
-
}
|
|
1878
|
-
await Promise.race([exit, delay(graceMs)]);
|
|
1879
|
-
if (exited || child.exitCode !== null || child.signalCode !== null) return;
|
|
1880
|
-
try {
|
|
1881
|
-
child.kill("SIGKILL");
|
|
1882
|
-
} catch {
|
|
1883
|
-
}
|
|
1884
|
-
await exit;
|
|
1885
|
-
}
|
|
1886
|
-
function ownerLoss(ownerInput, signalSource) {
|
|
1887
|
-
return new Promise((resolve3) => {
|
|
1888
|
-
let settled = false;
|
|
1889
|
-
const finish = () => {
|
|
1890
|
-
if (settled) return;
|
|
1891
|
-
settled = true;
|
|
1892
|
-
resolve3({ kind: "owner" });
|
|
1893
|
-
};
|
|
1894
|
-
ownerInput.once("end", finish);
|
|
1895
|
-
ownerInput.once("close", finish);
|
|
1896
|
-
ownerInput.once("error", finish);
|
|
1897
|
-
signalSource.once("SIGINT", finish);
|
|
1898
|
-
signalSource.once("SIGTERM", finish);
|
|
1899
|
-
ownerInput.resume?.();
|
|
1900
|
-
});
|
|
1901
|
-
}
|
|
1902
|
-
async function runManagedCompanionGuardian({
|
|
1903
|
-
cloudflaredBinary,
|
|
1904
|
-
tokenFile,
|
|
1905
|
-
target,
|
|
1906
|
-
originPort = MANAGED_COMPANION_ORIGIN_PORT,
|
|
1907
|
-
environment = process.env,
|
|
1908
|
-
platform = process.platform,
|
|
1909
|
-
ownerInput = process.stdin,
|
|
1910
|
-
signalSource = process,
|
|
1911
|
-
spawnProcess = spawn,
|
|
1912
|
-
isTargetAlive = ({ pid }) => processIsAlive(pid),
|
|
1913
|
-
createGateway = createCompanionOriginGateway
|
|
1914
|
-
} = {}) {
|
|
1915
|
-
if (!path2.isAbsolute(cloudflaredBinary ?? "") || !path2.isAbsolute(tokenFile ?? "")) {
|
|
1916
|
-
throw new Error("The managed connector paths are invalid");
|
|
1917
|
-
}
|
|
1918
|
-
if (!validCompanionOriginTarget(target, platform)) {
|
|
1919
|
-
throw new Error("The managed companion origin target is invalid");
|
|
1920
|
-
}
|
|
1921
|
-
if (!Number.isInteger(originPort) || originPort < 1 || originPort > 65535) {
|
|
1922
|
-
throw new Error("The managed companion origin port is invalid");
|
|
1923
|
-
}
|
|
1924
|
-
const gateway = createGateway({
|
|
1925
|
-
target,
|
|
1926
|
-
originPort,
|
|
1927
|
-
isTargetAlive
|
|
1928
|
-
});
|
|
1929
|
-
await gateway.start();
|
|
1930
|
-
let connector;
|
|
1931
|
-
try {
|
|
1932
|
-
connector = spawnProcess(
|
|
1933
|
-
cloudflaredBinary,
|
|
1934
|
-
[
|
|
1935
|
-
"tunnel",
|
|
1936
|
-
"--no-autoupdate",
|
|
1937
|
-
"--loglevel",
|
|
1938
|
-
"info",
|
|
1939
|
-
"--output",
|
|
1940
|
-
"json",
|
|
1941
|
-
"run",
|
|
1942
|
-
"--token-file",
|
|
1943
|
-
tokenFile
|
|
1944
|
-
],
|
|
1945
|
-
{
|
|
1946
|
-
env: minimalCloudflaredEnvironment(environment, platform),
|
|
1947
|
-
shell: false,
|
|
1948
|
-
windowsHide: true,
|
|
1949
|
-
stdio: ["ignore", "ignore", "ignore"]
|
|
1950
|
-
}
|
|
1951
|
-
);
|
|
1952
|
-
} catch (error) {
|
|
1953
|
-
gateway.invalidate();
|
|
1954
|
-
await gateway.close();
|
|
1955
|
-
throw error;
|
|
1956
|
-
}
|
|
1957
|
-
const connectorExit = new Promise((resolve3) => {
|
|
1958
|
-
connector.once("exit", (code) => resolve3({ kind: "connector", code: code ?? 1 }));
|
|
1959
|
-
connector.once("error", () => {
|
|
1960
|
-
if (!Number.isSafeInteger(connector.pid)) {
|
|
1961
|
-
resolve3({ kind: "connector", code: 1 });
|
|
1962
|
-
}
|
|
1963
|
-
});
|
|
1964
|
-
});
|
|
1965
|
-
const outcome = await Promise.race([connectorExit, ownerLoss(ownerInput, signalSource)]);
|
|
1966
|
-
gateway.invalidate();
|
|
1967
|
-
if (outcome.kind === "owner") await terminateConnector(connector);
|
|
1968
|
-
await gateway.close();
|
|
1969
|
-
return outcome.kind === "connector" ? outcome.code : 0;
|
|
1970
|
-
}
|
|
1971
|
-
function guardianArguments(argv) {
|
|
1972
|
-
if (argv.length !== 5) throw new Error("The managed companion guardian arguments are invalid");
|
|
1973
|
-
const [cloudflaredBinary, tokenFile, socketPath, rawPid, rawPort] = argv;
|
|
1974
|
-
const pid = Number(rawPid);
|
|
1975
|
-
const originPort = Number(rawPort);
|
|
1976
|
-
const target = { pid, socketPath };
|
|
1977
|
-
if (!validCompanionOriginTarget(target)) {
|
|
1978
|
-
throw new Error("The managed companion guardian target is invalid");
|
|
1979
|
-
}
|
|
1980
|
-
return { cloudflaredBinary, tokenFile, target, originPort };
|
|
1981
|
-
}
|
|
1982
|
-
var isDirectExecution = process.argv[1] && path2.resolve(process.argv[1]) === path2.resolve(fileURLToPath(import.meta.url));
|
|
1983
|
-
if (isDirectExecution) {
|
|
1984
|
-
runManagedCompanionGuardian(guardianArguments(process.argv.slice(2))).then(
|
|
1985
|
-
(code) => process.exit(code),
|
|
1986
|
-
() => process.exit(1)
|
|
1987
|
-
);
|
|
1988
|
-
}
|
|
1989
1644
|
|
|
1990
1645
|
// electron/managed-companion-tunnel.mjs
|
|
1991
1646
|
var MANAGED_COMPANION_ENDPOINT_FIELD = "managedCompanionEndpointUrl";
|
|
@@ -2058,11 +1713,11 @@ function resolveCloudflaredBinary({
|
|
|
2058
1713
|
exists = fs2.existsSync
|
|
2059
1714
|
} = {}) {
|
|
2060
1715
|
const executable = platform === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
2061
|
-
const bundled =
|
|
1716
|
+
const bundled = path2.join(String(resourcesPath ?? ""), "cloudflared", executable);
|
|
2062
1717
|
if (isPackaged) return exists(bundled) ? bundled : null;
|
|
2063
1718
|
const override = environment.OMB_CLOUDFLARED_PATH?.trim();
|
|
2064
|
-
if (override) return
|
|
2065
|
-
const staged =
|
|
1719
|
+
if (override) return path2.isAbsolute(override) && exists(override) ? override : null;
|
|
1720
|
+
const staged = path2.join(
|
|
2066
1721
|
String(appPath ?? ""),
|
|
2067
1722
|
"dist-native",
|
|
2068
1723
|
"cloudflared",
|
|
@@ -2075,8 +1730,8 @@ function resolveCloudflaredBinary({
|
|
|
2075
1730
|
if (!pathValue) return null;
|
|
2076
1731
|
const delimiter2 = platform === "win32" ? ";" : ":";
|
|
2077
1732
|
for (const directory of pathValue.split(delimiter2)) {
|
|
2078
|
-
if (!directory || !
|
|
2079
|
-
const candidate =
|
|
1733
|
+
if (!directory || !path2.isAbsolute(directory)) continue;
|
|
1734
|
+
const candidate = path2.join(directory, executable);
|
|
2080
1735
|
if (exists(candidate)) return candidate;
|
|
2081
1736
|
}
|
|
2082
1737
|
return null;
|
|
@@ -2092,7 +1747,7 @@ function ensurePrivateDirectory(directory, fileSystem, currentUid) {
|
|
|
2092
1747
|
}
|
|
2093
1748
|
fileSystem.chmodSync(directory, 448);
|
|
2094
1749
|
}
|
|
2095
|
-
function
|
|
1750
|
+
function processIsAlive(pid) {
|
|
2096
1751
|
try {
|
|
2097
1752
|
process.kill(pid, 0);
|
|
2098
1753
|
return true;
|
|
@@ -2104,7 +1759,7 @@ var currentUserId2 = () => process.platform === "win32" ? void 0 : process.getui
|
|
|
2104
1759
|
function cleanupStaleManagedCompanionTokens(runtimeRoot, {
|
|
2105
1760
|
fileSystem = fs2,
|
|
2106
1761
|
currentUid = currentUserId2(),
|
|
2107
|
-
isProcessAlive =
|
|
1762
|
+
isProcessAlive = processIsAlive
|
|
2108
1763
|
} = {}) {
|
|
2109
1764
|
let entries;
|
|
2110
1765
|
try {
|
|
@@ -2119,7 +1774,7 @@ function cleanupStaleManagedCompanionTokens(runtimeRoot, {
|
|
|
2119
1774
|
if (!match || !entry.isFile() || entry.isSymbolicLink()) continue;
|
|
2120
1775
|
const ownerPid = Number(match[1]);
|
|
2121
1776
|
if (!Number.isSafeInteger(ownerPid) || isProcessAlive(ownerPid)) continue;
|
|
2122
|
-
const file =
|
|
1777
|
+
const file = path2.join(runtimeRoot, entry.name);
|
|
2123
1778
|
try {
|
|
2124
1779
|
const stat = fileSystem.lstatSync(file);
|
|
2125
1780
|
if (!stat.isFile() || stat.isSymbolicLink() || currentUid !== void 0 && stat.uid !== currentUid || process.platform !== "win32" && (stat.mode & 63) !== 0) {
|
|
@@ -2134,7 +1789,7 @@ function cleanupStaleManagedCompanionTokens(runtimeRoot, {
|
|
|
2134
1789
|
}
|
|
2135
1790
|
function writePrivateToken(runtimeRoot, token, { fileSystem, processId, identifier, currentUid }) {
|
|
2136
1791
|
ensurePrivateDirectory(runtimeRoot, fileSystem, currentUid);
|
|
2137
|
-
const file =
|
|
1792
|
+
const file = path2.join(runtimeRoot, `connector-${processId}-${identifier()}.token`);
|
|
2138
1793
|
let descriptor;
|
|
2139
1794
|
try {
|
|
2140
1795
|
descriptor = fileSystem.openSync(file, "wx", 384);
|
|
@@ -2211,13 +1866,13 @@ function createManagedCompanionTunnel({
|
|
|
2211
1866
|
runtimeRoot,
|
|
2212
1867
|
environment = process.env,
|
|
2213
1868
|
fileSystem = fs2,
|
|
2214
|
-
spawnProcess =
|
|
1869
|
+
spawnProcess = spawn,
|
|
2215
1870
|
fetchImpl = globalThis.fetch,
|
|
2216
1871
|
timeoutSignal = (milliseconds) => AbortSignal.timeout(milliseconds),
|
|
2217
1872
|
identifier = randomUUID2,
|
|
2218
1873
|
processId = process.pid,
|
|
2219
1874
|
currentUid = currentUserId2(),
|
|
2220
|
-
isProcessAlive =
|
|
1875
|
+
isProcessAlive = processIsAlive,
|
|
2221
1876
|
verifyTimeoutMs = 15e3,
|
|
2222
1877
|
verifyRequestTimeoutMs = 2500,
|
|
2223
1878
|
verifyIntervalMs = 250,
|
|
@@ -2277,19 +1932,19 @@ function createManagedCompanionTunnel({
|
|
|
2277
1932
|
};
|
|
2278
1933
|
const scheduleRetry = (reason, ownedGeneration) => {
|
|
2279
1934
|
if (!desired || ownedGeneration !== generation || retryTimer) return;
|
|
2280
|
-
const
|
|
1935
|
+
const delay = Math.min(1e3 * 2 ** retryAttempt, maxRetryMs);
|
|
2281
1936
|
retryAttempt += 1;
|
|
2282
|
-
publish({ status: "retrying", ready: false, error: reason, retryInMs:
|
|
1937
|
+
publish({ status: "retrying", ready: false, error: reason, retryInMs: delay });
|
|
2283
1938
|
retryTimer = setTimeout(() => {
|
|
2284
1939
|
retryTimer = null;
|
|
2285
1940
|
if (!desired || ownedGeneration !== generation) return;
|
|
2286
1941
|
void serialize(() => attempt(ownedGeneration));
|
|
2287
|
-
},
|
|
1942
|
+
}, delay);
|
|
2288
1943
|
retryTimer.unref?.();
|
|
2289
1944
|
};
|
|
2290
1945
|
const attempt = async (ownedGeneration) => {
|
|
2291
1946
|
if (!desired || ownedGeneration !== generation || !access) return state;
|
|
2292
|
-
if (!binaryPath || !
|
|
1947
|
+
if (!binaryPath || !path2.isAbsolute(binaryPath) || !guardianEntry2 || !path2.isAbsolute(guardianEntry2) || !path2.isAbsolute(runtimeExecutable) || !validCompanionOriginTarget(originTarget)) {
|
|
2293
1948
|
publish({
|
|
2294
1949
|
status: "unavailable",
|
|
2295
1950
|
ready: false,
|
|
@@ -3056,8 +2711,8 @@ function createSecureCredentialState(initialCredentials, persist, { writable = t
|
|
|
3056
2711
|
// server/atomic.ts
|
|
3057
2712
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
3058
2713
|
import { closeSync, fsyncSync, openSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3059
|
-
function writeFileAtomic(
|
|
3060
|
-
const tmp = `${
|
|
2714
|
+
function writeFileAtomic(path3, data, options = {}) {
|
|
2715
|
+
const tmp = `${path3}.${process.pid}.${randomUUID3()}.tmp`;
|
|
3061
2716
|
let fd = null;
|
|
3062
2717
|
try {
|
|
3063
2718
|
fd = openSync(tmp, "w", options.mode);
|
|
@@ -3065,7 +2720,7 @@ function writeFileAtomic(path4, data, options = {}) {
|
|
|
3065
2720
|
fsyncSync(fd);
|
|
3066
2721
|
closeSync(fd);
|
|
3067
2722
|
fd = null;
|
|
3068
|
-
renameSync(tmp,
|
|
2723
|
+
renameSync(tmp, path3);
|
|
3069
2724
|
} catch (e) {
|
|
3070
2725
|
if (fd !== null) {
|
|
3071
2726
|
try {
|
|
@@ -3082,7 +2737,7 @@ function writeFileAtomic(path4, data, options = {}) {
|
|
|
3082
2737
|
}
|
|
3083
2738
|
|
|
3084
2739
|
// server/tunnel.ts
|
|
3085
|
-
var HERE = dirname(
|
|
2740
|
+
var HERE = dirname(fileURLToPath(import.meta.url));
|
|
3086
2741
|
var TUNNEL_CREDENTIALS_FILE = "tunnel-account.json";
|
|
3087
2742
|
var TUNNEL_RUNTIME_DIR = "tunnel-runtime";
|
|
3088
2743
|
function plainDocument(value) {
|
|
@@ -3160,7 +2815,7 @@ function prepareEntry(here = HERE) {
|
|
|
3160
2815
|
function guardianEntry(here = HERE) {
|
|
3161
2816
|
const bundled = join2(here, "tunnel-guardian.js");
|
|
3162
2817
|
if (existsSync(bundled)) return bundled;
|
|
3163
|
-
const source = resolve(here, "..", "electron", "managed-companion-guardian.mjs");
|
|
2818
|
+
const source = resolve(here, "..", "electron", "managed-companion-guardian-main.mjs");
|
|
3164
2819
|
return existsSync(source) ? source : null;
|
|
3165
2820
|
}
|
|
3166
2821
|
async function ensureCloudflared(options) {
|
|
@@ -3169,7 +2824,7 @@ async function ensureCloudflared(options) {
|
|
|
3169
2824
|
if (found) return found;
|
|
3170
2825
|
options.log(`downloading cloudflared once (pinned version and digest, about 40 MB) into ${options.dataDir}`);
|
|
3171
2826
|
await new Promise((done, fail) => {
|
|
3172
|
-
const child =
|
|
2827
|
+
const child = spawn2(process.execPath, [prepareEntry(options.here), "--current", "--root", options.dataDir], {
|
|
3173
2828
|
env,
|
|
3174
2829
|
stdio: ["ignore", "inherit", "inherit"]
|
|
3175
2830
|
});
|
|
@@ -3228,7 +2883,7 @@ function describeTunnelState(state, address) {
|
|
|
3228
2883
|
}
|
|
3229
2884
|
|
|
3230
2885
|
// server/cli.ts
|
|
3231
|
-
var HERE2 = dirname2(
|
|
2886
|
+
var HERE2 = dirname2(fileURLToPath2(import.meta.url));
|
|
3232
2887
|
var COMMANDS = ["serve", "pair", "sessions", "status", "login", "logout", "help", "--help", "-h"];
|
|
3233
2888
|
function parseArgs(argv, env = process.env) {
|
|
3234
2889
|
const [command = "help", ...rest] = argv;
|
|
@@ -3324,8 +2979,8 @@ function serverVersion(here = HERE2) {
|
|
|
3324
2979
|
}
|
|
3325
2980
|
}
|
|
3326
2981
|
var message = (error) => error instanceof Error ? error.message : String(error);
|
|
3327
|
-
async function api(port,
|
|
3328
|
-
const res = await fetch(`http://127.0.0.1:${port}${
|
|
2982
|
+
async function api(port, path3, init = {}) {
|
|
2983
|
+
const res = await fetch(`http://127.0.0.1:${port}${path3}`, { method: init.method, body: init.body, headers: { "content-type": "application/json" } });
|
|
3329
2984
|
const body = await res.json().catch(() => ({}));
|
|
3330
2985
|
return { status: res.status, body };
|
|
3331
2986
|
}
|
|
@@ -3575,7 +3230,7 @@ async function runServe(options, log = console.log) {
|
|
|
3575
3230
|
log(`tailscale: serving https://${tailscale.dnsName} \u2192 http://127.0.0.1:${options.port} (only your tailnet can reach it)`);
|
|
3576
3231
|
}
|
|
3577
3232
|
if (publicUrl) env.OMB_PUBLIC_URL = publicUrl;
|
|
3578
|
-
const child =
|
|
3233
|
+
const child = spawn3(entry.command, entry.args, { env, stdio: ["ignore", "inherit", "inherit"] });
|
|
3579
3234
|
let exited = null;
|
|
3580
3235
|
child.on("exit", (code) => {
|
|
3581
3236
|
exited = code ?? 1;
|
|
@@ -3661,11 +3316,15 @@ ${USAGE}`);
|
|
|
3661
3316
|
}
|
|
3662
3317
|
}
|
|
3663
3318
|
|
|
3319
|
+
// server/exit.ts
|
|
3320
|
+
function exitAfterFlush(code) {
|
|
3321
|
+
process.exitCode = code;
|
|
3322
|
+
const flushed = (stream) => new Promise((done) => stream.write("", () => done()));
|
|
3323
|
+
void Promise.all([flushed(process.stdout), flushed(process.stderr)]).then(() => process.exit(code));
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3664
3326
|
// server/pair-cli.ts
|
|
3665
|
-
main(["pair", ...process.argv.slice(2)]).then(
|
|
3666
|
-
(
|
|
3667
|
-
(
|
|
3668
|
-
|
|
3669
|
-
process.exit(1);
|
|
3670
|
-
}
|
|
3671
|
-
);
|
|
3327
|
+
main(["pair", ...process.argv.slice(2)]).then(exitAfterFlush, (error) => {
|
|
3328
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
3329
|
+
exitAfterFlush(1);
|
|
3330
|
+
});
|
|
@@ -11,6 +11,30 @@
|
|
|
11
11
|
// backstop for the obvious catastrophes. Real containment is the
|
|
12
12
|
// sandbox and the bot's own computer, not a regex.
|
|
13
13
|
import { approvalModeFor } from "../shared/approval-mode.js";
|
|
14
|
+
/** The mode a turn actually runs under, given where the turn came from.
|
|
15
|
+
*
|
|
16
|
+
* Full and Custom are Codex's: on any other engine they fall to Ask, so a
|
|
17
|
+
* hand-edited record cannot hand Claude a bypass it was never granted.
|
|
18
|
+
*
|
|
19
|
+
* And Full is a decision the person made about THEIR OWN sessions with a
|
|
20
|
+
* bot — "run without asking me". A turn started by another bot is not one
|
|
21
|
+
* of those: the person never saw the request, and the sender may itself be
|
|
22
|
+
* unattended or working off a page it just read. A Full target reached that
|
|
23
|
+
* way runs as Approve for me instead — ordinary requests still flow, the
|
|
24
|
+
* destructive and sensitive guards card, an unattended sender's block holds,
|
|
25
|
+
* and every answer goes through the fold and into the decision log, where a
|
|
26
|
+
* driver-side Full accept never appears. A webhook or scheduled turn on a
|
|
27
|
+
* Full bot is unchanged: the person opted into that, and the docs say so. */
|
|
28
|
+
export function approvalModeForOrigin(mode, origin) {
|
|
29
|
+
// Provider support is settled by supportsApprovalMode at the call site;
|
|
30
|
+
// this only answers who STARTED the turn. A permissive mode is the
|
|
31
|
+
// person's grant to the bot they talk to, not to every teammate that can
|
|
32
|
+
// reach it, so a peer-started turn runs one notch down and still gets the
|
|
33
|
+
// unattended downgrade after this.
|
|
34
|
+
if ((mode === "full" || mode === "custom") && origin.peerInitiated)
|
|
35
|
+
return "auto";
|
|
36
|
+
return mode;
|
|
37
|
+
}
|
|
14
38
|
const DESTRUCTIVE = [
|
|
15
39
|
/\brm\s+(-[a-z]*\s+)*-[a-z]*[rf]/i, // rm -rf, rm -fr, rm -r -f
|
|
16
40
|
/\bmkfs\b|\bdiskutil\s+erase|\bdd\s+[^|]*\bof=\/dev\//i,
|
|
@@ -160,3 +184,27 @@ export function autoVerdict(bot, tool, summary, context) {
|
|
|
160
184
|
export function autoDecision(bot, tool, summary, context) {
|
|
161
185
|
return autoVerdict(bot, tool, summary, context).approve;
|
|
162
186
|
}
|
|
187
|
+
/** The note a card shows above its buttons, explaining why the bot stopped
|
|
188
|
+
* rather than answering for itself.
|
|
189
|
+
*
|
|
190
|
+
* The unattended case is the one users misread. A turn a webhook or another
|
|
191
|
+
* bot started never runs Auto at all — approvalModeForTurn downgrades it to
|
|
192
|
+
* Ask before the provider spawns — so "this action needs you" would name the
|
|
193
|
+
* wrong cause and imply the next action might pass. It will not: with a fleet
|
|
194
|
+
* delegating between bots, every card looks like this until someone answers.
|
|
195
|
+
* Say that plainly, and name the mode that keeps running. */
|
|
196
|
+
export function approvalHeldReason(context) {
|
|
197
|
+
if (context.source === "native-approval")
|
|
198
|
+
return "The provider requires your approval for this action.";
|
|
199
|
+
if (!context.permission)
|
|
200
|
+
return undefined;
|
|
201
|
+
if (context.requiresExplicitApproval) {
|
|
202
|
+
return "This changes the provider sandbox, so only Full access can approve it automatically.";
|
|
203
|
+
}
|
|
204
|
+
if (context.mode !== "auto")
|
|
205
|
+
return undefined;
|
|
206
|
+
if (!context.unattended)
|
|
207
|
+
return "This action needs you, so Approve for me stopped to ask.";
|
|
208
|
+
const hint = context.fullAccessAvailable ? " Full access keeps working unattended." : "";
|
|
209
|
+
return `A webhook or another bot started this turn, so Approve for me is paused and every action asks.${hint}`;
|
|
210
|
+
}
|