github-router 0.3.131 → 0.3.136
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/browser-ext/manifest.json +1 -1
- package/dist/engine-BrbdG9Cp.js +6 -0
- package/dist/{lifecycle-CbtmGbjI.js → lifecycle-C0Y_e0zA.js} +2 -2
- package/dist/{lifecycle-BoId1aMF.js → lifecycle-C8t7-5pU.js} +2 -2
- package/dist/{lifecycle-Cyxwmj1c.js → lifecycle-CeVDX6av.js} +2 -2
- package/dist/{lifecycle-Cyxwmj1c.js.map → lifecycle-CeVDX6av.js.map} +1 -1
- package/dist/{lifecycle-DTJ2Ugqf.js → lifecycle-Cqe8OQVX.js} +2 -2
- package/dist/{lifecycle-DTJ2Ugqf.js.map → lifecycle-Cqe8OQVX.js.map} +1 -1
- package/dist/main.js +350 -14
- package/dist/main.js.map +1 -1
- package/dist/paths-Bljq3UJC.js +3 -0
- package/dist/{paths-CNgpeaWd.js → paths-Cn5OzmYL.js} +34 -4
- package/dist/{paths-CNgpeaWd.js.map → paths-Cn5OzmYL.js.map} +1 -1
- package/dist/{peer-mcp-personas-CH2gmdPN.js → peer-mcp-personas-Dmx4S0_5.js} +285 -76
- package/dist/peer-mcp-personas-Dmx4S0_5.js.map +1 -0
- package/package.json +2 -1
- package/dist/engine-DCsTvsSw.js +0 -6
- package/dist/paths-B-ATynF7.js +0 -3
- package/dist/peer-mcp-personas-CH2gmdPN.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as PATHS } from "./paths-
|
|
2
|
-
import { d as runCommandCapture, l as parseBoolEnv, n as isPidAlive, o as trackChild, p as runManagedExeCapture, r as registerColbertExitHandlers, t as getColbertInstanceUuid, u as resolveExecutable } from "./lifecycle-
|
|
3
|
-
import { i as registerExitHandlers, n as getInstanceUuid, r as recordWorkerRepo, t as WorktreeRegistry } from "./lifecycle-
|
|
1
|
+
import { t as PATHS } from "./paths-Cn5OzmYL.js";
|
|
2
|
+
import { d as runCommandCapture, l as parseBoolEnv, n as isPidAlive, o as trackChild, p as runManagedExeCapture, r as registerColbertExitHandlers, t as getColbertInstanceUuid, u as resolveExecutable } from "./lifecycle-Cqe8OQVX.js";
|
|
3
|
+
import { i as registerExitHandlers, n as getInstanceUuid, r as recordWorkerRepo, t as WorktreeRegistry } from "./lifecycle-CeVDX6av.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import consola from "consola";
|
|
6
6
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
@@ -1047,6 +1047,24 @@ function collapsePathKeys(env) {
|
|
|
1047
1047
|
return env;
|
|
1048
1048
|
}
|
|
1049
1049
|
|
|
1050
|
+
//#endregion
|
|
1051
|
+
//#region src/lib/insecure-tls.ts
|
|
1052
|
+
const IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
1053
|
+
let sharedInsecureDispatcher;
|
|
1054
|
+
function insecureDispatcher() {
|
|
1055
|
+
return sharedInsecureDispatcher ??= new Agent({ connect: { rejectUnauthorized: false } });
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Attach the runtime-correct TLS-verification-off mechanism to a fetch init for a
|
|
1059
|
+
* single self-signed direct-HTTPS instance: Bun → `tls`, Node → an undici
|
|
1060
|
+
* `dispatcher`. Exported so BOTH runtime branches are unit-testable under one
|
|
1061
|
+
* interpreter (the untested Node branch is exactly what shipped broken).
|
|
1062
|
+
*/
|
|
1063
|
+
function applyInsecureTls(init, isBun = IS_BUN) {
|
|
1064
|
+
if (isBun) init.tls = { rejectUnauthorized: false };
|
|
1065
|
+
else init.dispatcher = insecureDispatcher();
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1050
1068
|
//#endregion
|
|
1051
1069
|
//#region src/lib/artifact/client.ts
|
|
1052
1070
|
var ArtifactError = class extends Error {
|
|
@@ -1068,11 +1086,13 @@ var ArtifactClient = class {
|
|
|
1068
1086
|
token;
|
|
1069
1087
|
sessionId;
|
|
1070
1088
|
fetchFn;
|
|
1089
|
+
insecureTLS;
|
|
1071
1090
|
constructor(options) {
|
|
1072
1091
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
1073
1092
|
this.token = options.token;
|
|
1074
1093
|
this.sessionId = options.sessionId;
|
|
1075
1094
|
this.fetchFn = options.fetchFn ?? globalThis.fetch.bind(globalThis);
|
|
1095
|
+
this.insecureTLS = options.insecureTLS ?? false;
|
|
1076
1096
|
}
|
|
1077
1097
|
open(file, signal) {
|
|
1078
1098
|
return this.request("POST", `/api/artifact/${encodeURIComponent(this.sessionId)}/open`, { file }, signal);
|
|
@@ -1101,7 +1121,7 @@ var ArtifactClient = class {
|
|
|
1101
1121
|
const timeout = combineSignalAndTimeout(signal, timeoutMsHint);
|
|
1102
1122
|
let response;
|
|
1103
1123
|
try {
|
|
1104
|
-
|
|
1124
|
+
const init = {
|
|
1105
1125
|
method,
|
|
1106
1126
|
headers: {
|
|
1107
1127
|
Authorization: `Bearer ${this.token}`,
|
|
@@ -1110,7 +1130,9 @@ var ArtifactClient = class {
|
|
|
1110
1130
|
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
1111
1131
|
redirect: "error",
|
|
1112
1132
|
signal: timeout.signal
|
|
1113
|
-
}
|
|
1133
|
+
};
|
|
1134
|
+
if (this.insecureTLS) applyInsecureTls(init);
|
|
1135
|
+
response = await this.fetchFn(url.toString(), init);
|
|
1114
1136
|
} catch (err) {
|
|
1115
1137
|
throw mapNetworkError$1(err);
|
|
1116
1138
|
} finally {
|
|
@@ -1308,10 +1330,29 @@ function readArtifactEnv() {
|
|
|
1308
1330
|
return {
|
|
1309
1331
|
baseUrl,
|
|
1310
1332
|
token,
|
|
1311
|
-
sessionId
|
|
1333
|
+
sessionId,
|
|
1334
|
+
insecureTLS: shouldUseInsecureTls(baseUrl)
|
|
1312
1335
|
};
|
|
1313
1336
|
}
|
|
1337
|
+
function shouldUseInsecureTls(baseUrl) {
|
|
1338
|
+
let url;
|
|
1339
|
+
try {
|
|
1340
|
+
url = new URL(baseUrl);
|
|
1341
|
+
} catch {
|
|
1342
|
+
return false;
|
|
1343
|
+
}
|
|
1344
|
+
if (url.protocol !== "https:") return false;
|
|
1345
|
+
const explicit = (process.env.AIORDIE_INSECURE_TLS ?? "").trim().toLowerCase();
|
|
1346
|
+
if (explicit === "0" || explicit === "false" || explicit === "off") return false;
|
|
1347
|
+
if (isLoopbackIp(url.hostname)) return true;
|
|
1348
|
+
return url.hostname === "localhost" && (explicit === "1" || explicit === "true");
|
|
1349
|
+
}
|
|
1350
|
+
function isLoopbackIp(hostname) {
|
|
1351
|
+
const host = hostname.replace(/^\[|\]$/g, "");
|
|
1352
|
+
return host === "::1" || /^127(?:\.\d{1,3}){3}$/.test(host);
|
|
1353
|
+
}
|
|
1314
1354
|
function clientFromEnv(env) {
|
|
1355
|
+
consola.debug(`ARTIFACT_ENV: token present=${env.token.length > 0}, insecureTLS=${env.insecureTLS}`);
|
|
1315
1356
|
return new ArtifactClient(env);
|
|
1316
1357
|
}
|
|
1317
1358
|
async function pollUntilReady(client, signal) {
|
|
@@ -1644,21 +1685,6 @@ function createTunnelTokenProvider(runner = realDevtunnelRunner()) {
|
|
|
1644
1685
|
|
|
1645
1686
|
//#endregion
|
|
1646
1687
|
//#region src/lib/fleet/client.ts
|
|
1647
|
-
const IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
1648
|
-
let sharedInsecureDispatcher;
|
|
1649
|
-
function insecureDispatcher() {
|
|
1650
|
-
return sharedInsecureDispatcher ??= new Agent({ connect: { rejectUnauthorized: false } });
|
|
1651
|
-
}
|
|
1652
|
-
/**
|
|
1653
|
-
* Attach the runtime-correct TLS-verification-off mechanism to a fetch init for a
|
|
1654
|
-
* single self-signed direct-HTTPS instance: Bun → `tls`, Node → an undici
|
|
1655
|
-
* `dispatcher`. Exported so BOTH runtime branches are unit-testable under one
|
|
1656
|
-
* interpreter (the untested Node branch is exactly what shipped broken).
|
|
1657
|
-
*/
|
|
1658
|
-
function applyInsecureTls(init, isBun = IS_BUN) {
|
|
1659
|
-
if (isBun) init.tls = { rejectUnauthorized: false };
|
|
1660
|
-
else init.dispatcher = insecureDispatcher();
|
|
1661
|
-
}
|
|
1662
1688
|
var FleetError = class extends Error {
|
|
1663
1689
|
code;
|
|
1664
1690
|
retryable;
|
|
@@ -1691,7 +1717,7 @@ function decodeSessionId(globalId) {
|
|
|
1691
1717
|
var FleetClient = class {
|
|
1692
1718
|
baseUrl;
|
|
1693
1719
|
origin;
|
|
1694
|
-
|
|
1720
|
+
auth;
|
|
1695
1721
|
fetchFn;
|
|
1696
1722
|
getTunnelToken;
|
|
1697
1723
|
onTunnelAuthInvalidate;
|
|
@@ -1699,7 +1725,10 @@ var FleetClient = class {
|
|
|
1699
1725
|
constructor(options) {
|
|
1700
1726
|
this.baseUrl = options.url.replace(/\/+$/, "");
|
|
1701
1727
|
this.origin = new URL(this.baseUrl).origin;
|
|
1702
|
-
this.
|
|
1728
|
+
this.auth = options.auth ?? {
|
|
1729
|
+
type: "bearer",
|
|
1730
|
+
token: options.token ?? ""
|
|
1731
|
+
};
|
|
1703
1732
|
this.fetchFn = options.fetchFn ?? globalThis.fetch.bind(globalThis);
|
|
1704
1733
|
this.getTunnelToken = options.getTunnelToken;
|
|
1705
1734
|
this.onTunnelAuthInvalidate = options.onTunnelAuthInvalidate;
|
|
@@ -1793,7 +1822,7 @@ var FleetClient = class {
|
|
|
1793
1822
|
const attachTunnel = tunnelToken !== void 0 && tunnelToken !== "";
|
|
1794
1823
|
const canRetry = attachTunnel && !!this.onTunnelAuthInvalidate && attempt === 0;
|
|
1795
1824
|
const headers = {
|
|
1796
|
-
Authorization: `Bearer ${this.token}
|
|
1825
|
+
...this.auth.type === "bearer" ? { Authorization: `Bearer ${this.auth.token}` } : {},
|
|
1797
1826
|
...devtunnelHost ? { "X-Tunnel-Skip-Anti-Phishing-Page": "true" } : {},
|
|
1798
1827
|
...attachTunnel ? { "X-Tunnel-Authorization": `tunnel ${tunnelToken}` } : {},
|
|
1799
1828
|
...body === void 0 ? {} : { "Content-Type": "application/json" }
|
|
@@ -1814,7 +1843,7 @@ var FleetClient = class {
|
|
|
1814
1843
|
this.onTunnelAuthInvalidate();
|
|
1815
1844
|
continue;
|
|
1816
1845
|
}
|
|
1817
|
-
throw mapNetworkError(err, devtunnelHost);
|
|
1846
|
+
throw this.auth.type === "mesh" ? mapMeshUnreachable(err) : mapNetworkError(err, devtunnelHost);
|
|
1818
1847
|
}
|
|
1819
1848
|
if (!response.ok) {
|
|
1820
1849
|
if ((response.status === 401 || response.status === 403) && canRetry) {
|
|
@@ -1946,6 +1975,20 @@ function detailToSearchString(detail) {
|
|
|
1946
1975
|
return String(detail);
|
|
1947
1976
|
}
|
|
1948
1977
|
}
|
|
1978
|
+
function mapMeshUnreachable(err) {
|
|
1979
|
+
if (isAbortLike$1(err)) return new FleetError({
|
|
1980
|
+
code: "TIMEOUT",
|
|
1981
|
+
message: "fleet mesh peer request timed out or was aborted",
|
|
1982
|
+
retryable: true,
|
|
1983
|
+
detail: err
|
|
1984
|
+
});
|
|
1985
|
+
return new FleetError({
|
|
1986
|
+
code: "TAILNET_UNREACHABLE",
|
|
1987
|
+
message: `fleet mesh peer unreachable: ${err instanceof Error ? err.message : String(err)} — the peer is a tailnet node but the request did not land. A mesh ACL drops blocked traffic SILENTLY, so the likely cause is the \`tag:aiordie\` ACL (verify the peer permits this node), not a dead instance; also confirm the peer's mesh sidecar is up and serving HTTPS on the tailnet.`,
|
|
1988
|
+
retryable: true,
|
|
1989
|
+
detail: err
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1949
1992
|
function mapNetworkError(err, devtunnelHost = false) {
|
|
1950
1993
|
if (isAbortLike$1(err)) return new FleetError({
|
|
1951
1994
|
code: "TIMEOUT",
|
|
@@ -2031,20 +2074,11 @@ var FleetRegistry = class {
|
|
|
2031
2074
|
}
|
|
2032
2075
|
}
|
|
2033
2076
|
async resolveInstance(arg) {
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
const labelMatches = instances.filter((instance) => instance.label.toLocaleLowerCase() === wanted.toLocaleLowerCase());
|
|
2040
|
-
if (labelMatches.length > 1) throw new FleetRegistryError("AMBIGUOUS_LABEL", `fleet instance label ${JSON.stringify(wanted)} matches ${labelMatches.length} instances; use an id`);
|
|
2041
|
-
if (labelMatches.length === 1) return resolvedInstance(labelMatches[0]);
|
|
2042
|
-
throw new FleetRegistryError("INSTANCE_NOT_FOUND", `fleet instance ${JSON.stringify(wanted)} was not found`);
|
|
2043
|
-
}
|
|
2044
|
-
const defaultInstance = instances.find((instance) => instance.default === true);
|
|
2045
|
-
if (defaultInstance) return resolvedInstance(defaultInstance);
|
|
2046
|
-
if (instances.length === 1) return resolvedInstance(instances[0]);
|
|
2047
|
-
throw new FleetRegistryError("INSTANCE_REQUIRED", instances.length === 0 ? "fleet instance is required; registry is empty" : "fleet instance is required; specify an instance id or label");
|
|
2077
|
+
return selectInstance((await this.instancesWithTokens()).map(resolvedInstance), arg);
|
|
2078
|
+
}
|
|
2079
|
+
/** All static instances, fully resolved (with tokens). Used to build the merged static∪discovered set. */
|
|
2080
|
+
async resolveAll() {
|
|
2081
|
+
return (await this.instancesWithTokens()).map(resolvedInstance);
|
|
2048
2082
|
}
|
|
2049
2083
|
async listInstances() {
|
|
2050
2084
|
return (await this.instancesWithTokens()).map((instance) => ({
|
|
@@ -2060,6 +2094,27 @@ var FleetRegistry = class {
|
|
|
2060
2094
|
return this.loaded;
|
|
2061
2095
|
}
|
|
2062
2096
|
};
|
|
2097
|
+
/**
|
|
2098
|
+
* Pure instance selection over an already-resolved set: id (exact) → label
|
|
2099
|
+
* (case-insensitive, ambiguity-checked) → default → single. Shared by the static
|
|
2100
|
+
* registry and the merged static∪discovered registry so both apply identical
|
|
2101
|
+
* matching + error semantics.
|
|
2102
|
+
*/
|
|
2103
|
+
function selectInstance(instances, arg) {
|
|
2104
|
+
const wanted = typeof arg === "string" ? arg.trim() : "";
|
|
2105
|
+
if (wanted) {
|
|
2106
|
+
const byId = instances.find((instance) => instance.id === wanted);
|
|
2107
|
+
if (byId) return byId;
|
|
2108
|
+
const labelMatches = instances.filter((instance) => instance.label.toLocaleLowerCase() === wanted.toLocaleLowerCase());
|
|
2109
|
+
if (labelMatches.length > 1) throw new FleetRegistryError("AMBIGUOUS_LABEL", `fleet instance label ${JSON.stringify(wanted)} matches ${labelMatches.length} instances; use an id`);
|
|
2110
|
+
if (labelMatches.length === 1) return labelMatches[0];
|
|
2111
|
+
throw new FleetRegistryError("INSTANCE_NOT_FOUND", `fleet instance ${JSON.stringify(wanted)} was not found`);
|
|
2112
|
+
}
|
|
2113
|
+
const defaultInstance = instances.find((instance) => instance.default === true);
|
|
2114
|
+
if (defaultInstance) return defaultInstance;
|
|
2115
|
+
if (instances.length === 1) return instances[0];
|
|
2116
|
+
throw new FleetRegistryError("INSTANCE_REQUIRED", instances.length === 0 ? "fleet instance is required; registry is empty" : "fleet instance is required; specify an instance id or label");
|
|
2117
|
+
}
|
|
2063
2118
|
function normalizeConfig(config) {
|
|
2064
2119
|
if (!isObject(config)) throw new FleetRegistryError("INVALID_CONFIG", "fleet registry config must be an object");
|
|
2065
2120
|
const instances = config.instances ?? [];
|
|
@@ -2181,6 +2236,11 @@ function resolvedInstance(instance) {
|
|
|
2181
2236
|
label: instance.label,
|
|
2182
2237
|
url: instance.url,
|
|
2183
2238
|
token: instance.token,
|
|
2239
|
+
auth: {
|
|
2240
|
+
type: "bearer",
|
|
2241
|
+
token: instance.token
|
|
2242
|
+
},
|
|
2243
|
+
default: instance.default,
|
|
2184
2244
|
allowExec: instance.allowExec,
|
|
2185
2245
|
tunnelId: instance.tunnelId,
|
|
2186
2246
|
tunnelToken: instance.tunnelToken,
|
|
@@ -2194,6 +2254,158 @@ function isNodeErrorCode(err, code) {
|
|
|
2194
2254
|
return isObject(err) && err.code === code;
|
|
2195
2255
|
}
|
|
2196
2256
|
|
|
2257
|
+
//#endregion
|
|
2258
|
+
//#region src/lib/fleet/discovery.ts
|
|
2259
|
+
/**
|
|
2260
|
+
* Mesh fleet discovery: read the local ai-or-die instance's `mesh/peers.json`
|
|
2261
|
+
* (written by its MeshManager from the sidecar's tailnet `Status()`) off disk and
|
|
2262
|
+
* synthesize token-less, mesh-auth fleet instances. No `AIORDIE_*` env, no HTTP,
|
|
2263
|
+
* no token — filesystem permissions are the gate. Discovered peers are driven
|
|
2264
|
+
* over the tailnet with NO Authorization header; each peer's own sidecar injects
|
|
2265
|
+
* the bearer (ACL-gated by `tag:aiordie`). See docs / the fleet plan.
|
|
2266
|
+
*/
|
|
2267
|
+
const DISCOVERY_CACHE_TTL_MS = 5e3;
|
|
2268
|
+
const PEERS_JSON_MAX_BYTES = 256 * 1024;
|
|
2269
|
+
/** The ai-or-die app data dir (NOT github-router's) — mirrors MeshManager's `base`. */
|
|
2270
|
+
function aiordieAppDir() {
|
|
2271
|
+
if (process.platform === "win32") {
|
|
2272
|
+
const localApp = process.env.LOCALAPPDATA || nodePath.join(os.homedir(), "AppData", "Local");
|
|
2273
|
+
return nodePath.join(localApp, "ai-or-die");
|
|
2274
|
+
}
|
|
2275
|
+
return nodePath.join(os.homedir(), ".ai-or-die");
|
|
2276
|
+
}
|
|
2277
|
+
function meshPeersFilePath() {
|
|
2278
|
+
const override = process.env.GH_ROUTER_FLEET_PEERS_FILE;
|
|
2279
|
+
if (override && override.trim() !== "") return override.trim();
|
|
2280
|
+
return nodePath.join(aiordieAppDir(), "mesh", "peers.json");
|
|
2281
|
+
}
|
|
2282
|
+
function meshDiscoveryDisabled() {
|
|
2283
|
+
return process.env.GH_ROUTER_FLEET_DISCOVERY === "0";
|
|
2284
|
+
}
|
|
2285
|
+
const TS_NET_DNS_RE = /^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+ts\.net$/;
|
|
2286
|
+
function validDnsName(raw) {
|
|
2287
|
+
if (typeof raw !== "string") return void 0;
|
|
2288
|
+
const name = raw.trim().replace(/\.$/, "").toLowerCase();
|
|
2289
|
+
if (name.length === 0 || name.length > 253) return void 0;
|
|
2290
|
+
if (!TS_NET_DNS_RE.test(name)) return void 0;
|
|
2291
|
+
return name;
|
|
2292
|
+
}
|
|
2293
|
+
function nonEmptyString(raw) {
|
|
2294
|
+
return typeof raw === "string" && raw.trim() !== "" ? raw.trim() : void 0;
|
|
2295
|
+
}
|
|
2296
|
+
/**
|
|
2297
|
+
* Read + validate the discovery file into resolved mesh instances. Pure I/O +
|
|
2298
|
+
* validation; never throws — a missing/unreadable/oversized/malformed file
|
|
2299
|
+
* yields `[]` (discovery is best-effort and must never break static fleet use).
|
|
2300
|
+
*/
|
|
2301
|
+
async function readMeshPeers(readFileFn = (p) => fs.readFile(p, "utf8")) {
|
|
2302
|
+
if (meshDiscoveryDisabled()) return [];
|
|
2303
|
+
let raw;
|
|
2304
|
+
try {
|
|
2305
|
+
raw = await readFileFn(meshPeersFilePath());
|
|
2306
|
+
} catch {
|
|
2307
|
+
return [];
|
|
2308
|
+
}
|
|
2309
|
+
if (Buffer.byteLength(raw, "utf8") > PEERS_JSON_MAX_BYTES) return [];
|
|
2310
|
+
let parsed;
|
|
2311
|
+
try {
|
|
2312
|
+
parsed = JSON.parse(raw);
|
|
2313
|
+
} catch {
|
|
2314
|
+
return [];
|
|
2315
|
+
}
|
|
2316
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return [];
|
|
2317
|
+
if (!Array.isArray(parsed.peers)) return [];
|
|
2318
|
+
const selfDnsName = validDnsName(parsed.self?.dnsName);
|
|
2319
|
+
if (selfDnsName === void 0) return [];
|
|
2320
|
+
const tailnetSuffix = selfDnsName.slice(selfDnsName.indexOf(".") + 1);
|
|
2321
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2322
|
+
const out = [];
|
|
2323
|
+
for (const peer of parsed.peers) {
|
|
2324
|
+
if (typeof peer !== "object" || peer === null) continue;
|
|
2325
|
+
const record = peer;
|
|
2326
|
+
const dnsName = validDnsName(record.dnsName);
|
|
2327
|
+
if (dnsName === void 0) continue;
|
|
2328
|
+
if (dnsName === selfDnsName) continue;
|
|
2329
|
+
if (!dnsName.endsWith(`.${tailnetSuffix}`)) continue;
|
|
2330
|
+
if (seen.has(dnsName)) continue;
|
|
2331
|
+
seen.add(dnsName);
|
|
2332
|
+
const hostname = nonEmptyString(record.hostname);
|
|
2333
|
+
out.push({
|
|
2334
|
+
id: dnsName,
|
|
2335
|
+
label: hostname ?? dnsName,
|
|
2336
|
+
url: `https://${dnsName}`,
|
|
2337
|
+
token: "",
|
|
2338
|
+
auth: { type: "mesh" }
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
return out;
|
|
2342
|
+
}
|
|
2343
|
+
function toInfo(instance) {
|
|
2344
|
+
return {
|
|
2345
|
+
id: instance.id,
|
|
2346
|
+
label: instance.label,
|
|
2347
|
+
url: instance.url
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
/**
|
|
2351
|
+
* Registry that merges a static `fleet.json` registry with mesh discovery. Static
|
|
2352
|
+
* ALWAYS wins on an id collision (a discovered peer sharing a static id is dropped
|
|
2353
|
+
* — never overwrites a configured instance, never merges auth across sources).
|
|
2354
|
+
* Discovery is cached briefly so a fan-out doesn't re-read the file per call, and a
|
|
2355
|
+
* failed discovery read leaves the static set untouched.
|
|
2356
|
+
*/
|
|
2357
|
+
var MergedFleetRegistry = class {
|
|
2358
|
+
staticRegistry;
|
|
2359
|
+
discover;
|
|
2360
|
+
cache;
|
|
2361
|
+
inflight;
|
|
2362
|
+
ttlMs;
|
|
2363
|
+
now;
|
|
2364
|
+
constructor(options = {}) {
|
|
2365
|
+
this.staticRegistry = options.staticRegistry ?? new FleetRegistry();
|
|
2366
|
+
this.discover = options.discover ?? (() => readMeshPeers());
|
|
2367
|
+
this.ttlMs = options.ttlMs ?? DISCOVERY_CACHE_TTL_MS;
|
|
2368
|
+
this.now = options.now ?? (() => Date.now());
|
|
2369
|
+
}
|
|
2370
|
+
async discoverCached() {
|
|
2371
|
+
const now = this.now();
|
|
2372
|
+
if (this.cache && now - this.cache.at < this.ttlMs) return this.cache.peers;
|
|
2373
|
+
if (this.inflight) return this.inflight;
|
|
2374
|
+
this.inflight = (async () => {
|
|
2375
|
+
let peers;
|
|
2376
|
+
try {
|
|
2377
|
+
peers = await this.discover();
|
|
2378
|
+
} catch {
|
|
2379
|
+
peers = [];
|
|
2380
|
+
}
|
|
2381
|
+
this.cache = {
|
|
2382
|
+
at: this.now(),
|
|
2383
|
+
peers
|
|
2384
|
+
};
|
|
2385
|
+
return peers;
|
|
2386
|
+
})().finally(() => {
|
|
2387
|
+
this.inflight = void 0;
|
|
2388
|
+
});
|
|
2389
|
+
return this.inflight;
|
|
2390
|
+
}
|
|
2391
|
+
/** Static∪discovered with static winning on id collision. */
|
|
2392
|
+
async union() {
|
|
2393
|
+
const staticResolved = await this.staticRegistry.resolveAll();
|
|
2394
|
+
const staticIds = new Set(staticResolved.map((instance) => instance.id));
|
|
2395
|
+
const discovered = (await this.discoverCached()).filter((peer) => !staticIds.has(peer.id));
|
|
2396
|
+
return [...staticResolved, ...discovered];
|
|
2397
|
+
}
|
|
2398
|
+
async resolveInstance(arg) {
|
|
2399
|
+
return selectInstance(await this.union(), arg);
|
|
2400
|
+
}
|
|
2401
|
+
async listInstances() {
|
|
2402
|
+
const staticInfos = await this.staticRegistry.listInstances();
|
|
2403
|
+
const staticIds = new Set(staticInfos.map((info) => info.id));
|
|
2404
|
+
const discovered = (await this.discoverCached()).filter((peer) => !staticIds.has(peer.id));
|
|
2405
|
+
return [...staticInfos, ...discovered.map(toInfo)];
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2408
|
+
|
|
2197
2409
|
//#endregion
|
|
2198
2410
|
//#region src/lib/fleet/tools.ts
|
|
2199
2411
|
const FLEET_GROUP = "fleet";
|
|
@@ -2229,16 +2441,16 @@ function createFleetTools(options = {}) {
|
|
|
2229
2441
|
const awaitTurnDeadlineSlackMs = nonNegativeNumberOrDefault(options.awaitTurnDeadlineSlackMs, AWAIT_TURN_TIMEOUT_SLACK_MS);
|
|
2230
2442
|
function getRegistry() {
|
|
2231
2443
|
if (registry) return registry;
|
|
2232
|
-
defaultRegistry ??= new
|
|
2444
|
+
defaultRegistry ??= new MergedFleetRegistry();
|
|
2233
2445
|
return defaultRegistry;
|
|
2234
2446
|
}
|
|
2235
2447
|
function clientFor(instance) {
|
|
2236
|
-
const key = `${instance.id}\0${instance.url}\0${instance.token}\0${instance.tunnelId ?? ""}\0${instance.tunnelToken ?? ""}\0${instance.insecureTLS === true ? "1" : "0"}`;
|
|
2448
|
+
const key = `${instance.id}\0${instance.url}\0${instance.auth.type}\0${instance.token}\0${instance.tunnelId ?? ""}\0${instance.tunnelToken ?? ""}\0${instance.insecureTLS === true ? "1" : "0"}`;
|
|
2237
2449
|
const existing = clients.get(key);
|
|
2238
2450
|
if (existing) return existing;
|
|
2239
2451
|
const created = options.createClient ? options.createClient(instance) : new FleetClient({
|
|
2240
2452
|
url: instance.url,
|
|
2241
|
-
|
|
2453
|
+
auth: instance.auth,
|
|
2242
2454
|
fetchFn: options.fetchFn,
|
|
2243
2455
|
insecureTLS: instance.insecureTLS,
|
|
2244
2456
|
...tunnelClientOptions(instance, tunnelProvider)
|
|
@@ -2393,18 +2605,14 @@ function createFleetTools(options = {}) {
|
|
|
2393
2605
|
sessionId: stringProp("Global session id in the form instanceId:localSessionId."),
|
|
2394
2606
|
instance: stringProp("Optional instance id/label; when supplied it must agree with sessionId."),
|
|
2395
2607
|
message: stringProp("Message text to deliver to the session."),
|
|
2396
|
-
idempotencyKey: stringProp("
|
|
2608
|
+
idempotencyKey: stringProp("Optional caller idempotency key; AUTO-GENERATED when omitted, so you normally never pass it. Supply your OWN stable key only when you will retry the SAME send and need the upstream to dedupe it."),
|
|
2397
2609
|
awaitMs: numberProp("Optional best-effort confirmation wait (ms) — NOT a deadline. Prefer awaitMs:0 plus await_turn; a turn that outruns awaitMs returns confirmationPending, not an error.")
|
|
2398
|
-
}, [
|
|
2399
|
-
"sessionId",
|
|
2400
|
-
"message",
|
|
2401
|
-
"idempotencyKey"
|
|
2402
|
-
]), async (args, signal) => {
|
|
2610
|
+
}, ["sessionId", "message"]), async (args, signal) => {
|
|
2403
2611
|
const { instance, localId, globalId } = await resolveSession(args);
|
|
2404
2612
|
const awaitMs = optionalNumber(args, "awaitMs");
|
|
2405
2613
|
const response = await clientFor(instance).sendMessage(localId, {
|
|
2406
2614
|
message: requiredString(args, "message"),
|
|
2407
|
-
idempotencyKey:
|
|
2615
|
+
idempotencyKey: optionalString(args, "idempotencyKey") ?? randomUUID(),
|
|
2408
2616
|
...awaitMs === void 0 ? {} : { awaitMs }
|
|
2409
2617
|
}, signal);
|
|
2410
2618
|
const delivered = !(response.delivered === false || response.delivery?.status === "failed" || response.delivery?.status === "error");
|
|
@@ -2428,18 +2636,14 @@ function createFleetTools(options = {}) {
|
|
|
2428
2636
|
sessionId: stringProp("Global session id in the form instanceId:localSessionId."),
|
|
2429
2637
|
instance: stringProp("Optional instance id/label; when supplied it must agree with sessionId."),
|
|
2430
2638
|
keys: stringProp("Key sequence to send."),
|
|
2431
|
-
idempotencyKey: stringProp("
|
|
2639
|
+
idempotencyKey: stringProp("Optional caller idempotency key; auto-generated when omitted."),
|
|
2432
2640
|
raw: booleanProp("Pass keys through as raw input when the instance supports it.")
|
|
2433
|
-
}, [
|
|
2434
|
-
"sessionId",
|
|
2435
|
-
"keys",
|
|
2436
|
-
"idempotencyKey"
|
|
2437
|
-
]), async (args, signal) => {
|
|
2641
|
+
}, ["sessionId", "keys"]), async (args, signal) => {
|
|
2438
2642
|
const { instance, localId, globalId } = await resolveSession(args);
|
|
2439
2643
|
const raw = optionalBoolean(args, "raw");
|
|
2440
2644
|
const response = await clientFor(instance).sendKeys(localId, {
|
|
2441
2645
|
keys: requiredString(args, "keys"),
|
|
2442
|
-
idempotencyKey:
|
|
2646
|
+
idempotencyKey: optionalString(args, "idempotencyKey") ?? randomUUID(),
|
|
2443
2647
|
...raw === void 0 ? {} : { raw }
|
|
2444
2648
|
}, signal);
|
|
2445
2649
|
return ok({
|
|
@@ -2454,14 +2658,14 @@ function createFleetTools(options = {}) {
|
|
|
2454
2658
|
choice: stringProp("Named or numbered choice to select."),
|
|
2455
2659
|
optionValue: stringProp("Exact option value to select."),
|
|
2456
2660
|
keys: stringProp("Explicit key override to send instead of a mapped choice."),
|
|
2457
|
-
idempotencyKey: stringProp("
|
|
2458
|
-
}, ["sessionId"
|
|
2661
|
+
idempotencyKey: stringProp("Optional caller idempotency key; auto-generated when omitted.")
|
|
2662
|
+
}, ["sessionId"]), async (args, signal) => {
|
|
2459
2663
|
const { instance, localId, globalId } = await resolveSession(args);
|
|
2460
2664
|
const input = definedObject({
|
|
2461
2665
|
choice: optionalString(args, "choice"),
|
|
2462
2666
|
optionValue: optionalString(args, "optionValue"),
|
|
2463
2667
|
keys: optionalString(args, "keys"),
|
|
2464
|
-
idempotencyKey:
|
|
2668
|
+
idempotencyKey: optionalString(args, "idempotencyKey") ?? randomUUID()
|
|
2465
2669
|
});
|
|
2466
2670
|
const response = await clientFor(instance).respond(localId, input, signal);
|
|
2467
2671
|
return ok({
|
|
@@ -2475,19 +2679,15 @@ function createFleetTools(options = {}) {
|
|
|
2475
2679
|
agent: stringProp("Agent/runtime to create on the instance."),
|
|
2476
2680
|
name: stringProp("Optional display name for the session."),
|
|
2477
2681
|
workingDir: stringProp("Optional working directory on the remote instance."),
|
|
2478
|
-
idempotencyKey: stringProp("
|
|
2682
|
+
idempotencyKey: stringProp("Optional caller idempotency key; auto-generated when omitted."),
|
|
2479
2683
|
start: booleanProp("Whether the remote instance should start the session immediately."),
|
|
2480
2684
|
readyTimeoutMs: numberProp("F17: bounded ms to wait for the agent to become driveable before returning. The response carries ready/bound/blocker."),
|
|
2481
2685
|
permissionMode: stringProp("F10 (claude only): permission mode the launched agent starts in — one of plan | acceptEdits | default | bypassPermissions. Rejected with BAD_REQUEST if unknown or if agentArgs also sets it."),
|
|
2482
2686
|
agentArgs: arrayProp("F10 (claude only): extra launcher args appended after the github-router prefix. Must NOT include --permission-mode or --dangerously-skip-permissions (use permissionMode) — rejected with BAD_REQUEST.")
|
|
2483
|
-
}, [
|
|
2484
|
-
"instance",
|
|
2485
|
-
"agent",
|
|
2486
|
-
"idempotencyKey"
|
|
2487
|
-
]), async (args, signal) => {
|
|
2687
|
+
}, ["instance", "agent"]), async (args, signal) => {
|
|
2488
2688
|
const instance = await resolve(requiredString(args, "instance"));
|
|
2489
2689
|
const agent = requiredString(args, "agent");
|
|
2490
|
-
const idempotencyKey =
|
|
2690
|
+
const idempotencyKey = optionalString(args, "idempotencyKey") ?? randomUUID();
|
|
2491
2691
|
const permissionMode = optionalString(args, "permissionMode");
|
|
2492
2692
|
const agentArgs = optionalStringArray(args, "agentArgs");
|
|
2493
2693
|
if (permissionMode !== void 0) await assertCapability(instance, "permission_mode", "permissionMode", signal);
|
|
@@ -2512,11 +2712,11 @@ function createFleetTools(options = {}) {
|
|
|
2512
2712
|
tool$1("stop_session", "Stop a fleet session.", objectSchema({
|
|
2513
2713
|
sessionId: stringProp("Global session id in the form instanceId:localSessionId."),
|
|
2514
2714
|
instance: stringProp("Optional instance id/label; when supplied it must agree with sessionId."),
|
|
2515
|
-
idempotencyKey: stringProp("
|
|
2715
|
+
idempotencyKey: stringProp("Optional caller idempotency key; auto-generated when omitted."),
|
|
2516
2716
|
mode: stringProp("Optional stop mode understood by the remote instance.")
|
|
2517
|
-
}, ["sessionId"
|
|
2717
|
+
}, ["sessionId"]), async (args, signal) => {
|
|
2518
2718
|
const { instance, localId, globalId } = await resolveSession(args);
|
|
2519
|
-
const idempotencyKey =
|
|
2719
|
+
const idempotencyKey = optionalString(args, "idempotencyKey") ?? randomUUID();
|
|
2520
2720
|
const response = await clientFor(instance).stopSession(localId, definedObject({
|
|
2521
2721
|
mode: optionalString(args, "mode"),
|
|
2522
2722
|
idempotencyKey
|
|
@@ -8420,7 +8620,7 @@ function logAudit$1(record) {
|
|
|
8420
8620
|
try {
|
|
8421
8621
|
const fs$2 = await import("node:fs/promises");
|
|
8422
8622
|
const path$1 = await import("node:path");
|
|
8423
|
-
const { PATHS: PATHS$1 } = await import("./paths-
|
|
8623
|
+
const { PATHS: PATHS$1 } = await import("./paths-Bljq3UJC.js");
|
|
8424
8624
|
const dir = path$1.join(PATHS$1.APP_DIR, "browser-mcp");
|
|
8425
8625
|
await fs$2.mkdir(dir, { recursive: true });
|
|
8426
8626
|
const line = JSON.stringify({
|
|
@@ -20699,7 +20899,7 @@ function entryHasCommand(entry, command) {
|
|
|
20699
20899
|
* other entries. Returns a new object (never mutates the input). Re-running the
|
|
20700
20900
|
* launcher with the same command+event does not duplicate the hook.
|
|
20701
20901
|
*/
|
|
20702
|
-
function mergeStopHookIntoSettings(existing, command, event = "Stop", timeoutSec) {
|
|
20902
|
+
function mergeStopHookIntoSettings(existing, command, event = "Stop", timeoutSec, matcher) {
|
|
20703
20903
|
const base = existing && typeof existing === "object" ? { ...existing } : {};
|
|
20704
20904
|
const hooks = base.hooks && typeof base.hooks === "object" ? { ...base.hooks } : {};
|
|
20705
20905
|
const arr = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
|
|
@@ -20709,7 +20909,10 @@ function mergeStopHookIntoSettings(existing, command, event = "Stop", timeoutSec
|
|
|
20709
20909
|
command
|
|
20710
20910
|
};
|
|
20711
20911
|
if (typeof timeoutSec === "number" && Number.isFinite(timeoutSec) && timeoutSec > 0) hook.timeout = timeoutSec;
|
|
20712
|
-
arr.push(
|
|
20912
|
+
arr.push(matcher ? {
|
|
20913
|
+
matcher,
|
|
20914
|
+
hooks: [hook]
|
|
20915
|
+
} : { hooks: [hook] });
|
|
20713
20916
|
}
|
|
20714
20917
|
hooks[event] = arr;
|
|
20715
20918
|
base.hooks = hooks;
|
|
@@ -20915,6 +21118,12 @@ function buildSessionBindHookCommand(execPath, scriptPath, outPath) {
|
|
|
20915
21118
|
const q = (s) => `"${s}"`;
|
|
20916
21119
|
return `${scriptPath && scriptPath !== execPath ? `${q(execPath)} ${q(scriptPath)}` : q(execPath)} internal-session-bind --out ${q(outPath)}`;
|
|
20917
21120
|
}
|
|
21121
|
+
/** Command for the `internal-artifact-open` hook (no args — token comes from the
|
|
21122
|
+
* mirror creds file, plan from the plans dir; nothing secret in argv). */
|
|
21123
|
+
function buildArtifactOpenHookCommand(execPath, scriptPath) {
|
|
21124
|
+
const q = (s) => `"${s}"`;
|
|
21125
|
+
return `${scriptPath && scriptPath !== execPath ? `${q(execPath)} ${q(scriptPath)}` : q(execPath)} internal-artifact-open`;
|
|
21126
|
+
}
|
|
20918
21127
|
/**
|
|
20919
21128
|
* Read-merge-atomic-write the Stop hook into a Claude Code `settings.json` file
|
|
20920
21129
|
* (the mirrored one). A MISSING file (ENOENT) starts from `{}`; any OTHER read or
|
|
@@ -20923,7 +21132,7 @@ function buildSessionBindHookCommand(execPath, scriptPath, outPath) {
|
|
|
20923
21132
|
* other setting, is idempotent, and uses temp+rename so Claude Code's mtime
|
|
20924
21133
|
* watcher never sees a half-written file. Returns the merged object.
|
|
20925
21134
|
*/
|
|
20926
|
-
async function injectStopHookIntoSettingsFile(settingsPath, command, event = "Stop", timeoutSec) {
|
|
21135
|
+
async function injectStopHookIntoSettingsFile(settingsPath, command, event = "Stop", timeoutSec, matcher) {
|
|
20927
21136
|
let existing = {};
|
|
20928
21137
|
let raw;
|
|
20929
21138
|
try {
|
|
@@ -20937,7 +21146,7 @@ async function injectStopHookIntoSettingsFile(settingsPath, command, event = "St
|
|
|
20937
21146
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) existing = parsed;
|
|
20938
21147
|
else throw new Error(`settings.json at ${settingsPath} is not a JSON object; refusing to overwrite`);
|
|
20939
21148
|
}
|
|
20940
|
-
const merged = mergeStopHookIntoSettings(existing, command, event, timeoutSec);
|
|
21149
|
+
const merged = mergeStopHookIntoSettings(existing, command, event, timeoutSec, matcher);
|
|
20941
21150
|
const tmp = `${settingsPath}.${process.pid}.tmp`;
|
|
20942
21151
|
await promises.writeFile(tmp, `${JSON.stringify(merged, null, 2)}\n`, { mode: 384 });
|
|
20943
21152
|
await promises.rename(tmp, settingsPath);
|
|
@@ -22705,5 +22914,5 @@ async function runStandInToolCall(args, signal) {
|
|
|
22705
22914
|
}
|
|
22706
22915
|
|
|
22707
22916
|
//#endregion
|
|
22708
|
-
export {
|
|
22709
|
-
//# sourceMappingURL=peer-mcp-personas-
|
|
22917
|
+
export { readIteratorWithTimeout as $, state as $t, DEFAULT_MODEL as A, generateRandomPort as At, toolbeltSkipSet as B, filterBetaHeader as Bt, repoRoot as C, toolbeltPathOverride as Ct, resolveSealedGate as D, DEFAULT_PORT as Dt, trustRepo as E, DEFAULT_CODEX_MODEL_FALLBACKS as Et, runWorkerAgent as F, setupGitHubToken as Ft, ADVISOR_INTERNAL_TOOL_NAME as G, getModels as Gt, TOOLBELT_TOOLS$1 as H, resolveCodexModel as Ht, withNoOutputRetry as I, tryRefreshAndRetry as It, injectAdvisorTool as J, forwardError as Jt, ADVISOR_TOOL_INSTRUCTIONS as K, fetchWithTransientRetry as Kt, availableToolCommands as L, cacheCopilotVersion as Lt, PLAN_DEFAULT_MODEL as M, getPackageVersion as Mt, REVIEW_DEFAULT_MODEL as N, withInstallLock as Nt, liveExec as O, UPSTREAM_FETCH_TIMEOUT_MS as Ot, appendPlanReminder as P, setupCopilotToken as Pt, logStreamError as Q, githubHeaders as Qt, buildToolbeltAwareness as R, cacheModels as Rt, repoFingerprint as S, collapsePathKeys as St, stopReviewStateDir as T, DEFAULT_CODEX_MODEL as Tt, assetFor as U, resolveModel as Ut, vscodeRipgrepPath as V, isNullish as Vt, searchWeb as W, sleep as Wt, buildOpenAIErrorEvent as X, copilotBaseUrl as Xt, isAdvisorRequested as Y, GITHUB_API_BASE_URL as Yt, isControllerClosedError as Z, copilotHeaders as Zt, fileBaselineStore as _, provisionAndIndexColbert as _t, buildPeerAwarenessSnippet as a, standInToolEnabled as at, fileReviewDebounce as b, shouldUseInsecureTls as bt, buildSessionBindHookCommand as c, createMessages as ct, decideStopHook as d, createChatCompletions as dt, relayAnthropicStream as et, fileBlockBudget as f, MAX_RESPONSE_BODY_BYTES as ft, stopReviewEnabled as g, hasSupportedBrowserInstalled as gt, stopGateId as h, provisionBrowserAssets as ht, buildAgentPrompt as i, fleetToolsEnabled as it, IMPLEMENT_DEFAULT_MODEL as j, pickClaudeDefault as jt, BROWSE_DEFAULT_MODEL as k, UPSTREAM_INACTIVITY_TIMEOUT_MS as kt, buildStopHookCommand as l, getTokenCount as lt, launchBaselineKey as m, parseJsonOrDiagnose as mt, MCP_GROUPS as n, handleMcpPost as nt, personasFor as o, workerToolsEnabled as ot, injectStopHookIntoSettingsFile as p, readResponseBodyCapped as pt, buildAdvisorStream as q, HTTPError as qt, assertMcpToolSurfaceConsistent as r, browserToolsEnabled as rt, buildArtifactOpenHookCommand as s, countTokens as st, GROUP_META as t, handleMcpDelete as tt, captureLaunchBaseline as u, createResponses as ut, fileFindingsStore as v, extractTarGzMember as vt, stopGateEnabledForRepo as w, DEFAULT_CLAUDE_MODEL_FALLBACKS as wt, isSubagentContext as x, ArtifactClient as xt, fileLastPromptStore as y, extractZipMember as yt, toolbeltEnabled as z, cacheVSCodeVersion as zt };
|
|
22918
|
+
//# sourceMappingURL=peer-mcp-personas-Dmx4S0_5.js.map
|