agent-shim 8.20.2 → 8.21.0
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/cli.cjs +365 -48
- package/dist/index.cjs +344 -12
- package/dist/index.mjs +336 -12
- package/dist/types/index.d.ts +92 -10
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -6891,7 +6891,7 @@ var require_util2 = __commonJS({
|
|
|
6891
6891
|
var { kDestroyed, kBodyUsed, kListeners, kBody } = require_symbols();
|
|
6892
6892
|
var { IncomingMessage } = require("node:http");
|
|
6893
6893
|
var stream = require("node:stream");
|
|
6894
|
-
var
|
|
6894
|
+
var net4 = require("node:net");
|
|
6895
6895
|
var { stringify } = require("node:querystring");
|
|
6896
6896
|
var { EventEmitter: EE } = require("node:events");
|
|
6897
6897
|
var timers = require_timers();
|
|
@@ -7040,7 +7040,7 @@ var require_util2 = __commonJS({
|
|
|
7040
7040
|
}
|
|
7041
7041
|
assert2(typeof host === "string");
|
|
7042
7042
|
const servername = getHostname(host);
|
|
7043
|
-
if (
|
|
7043
|
+
if (net4.isIP(servername)) {
|
|
7044
7044
|
return "";
|
|
7045
7045
|
}
|
|
7046
7046
|
return servername;
|
|
@@ -8703,7 +8703,7 @@ var require_dispatcher_base = __commonJS({
|
|
|
8703
8703
|
var require_connect = __commonJS({
|
|
8704
8704
|
"node_modules/.pnpm/undici@7.29.1/node_modules/undici/lib/core/connect.js"(exports2, module2) {
|
|
8705
8705
|
"use strict";
|
|
8706
|
-
var
|
|
8706
|
+
var net4 = require("node:net");
|
|
8707
8707
|
var assert2 = require("node:assert");
|
|
8708
8708
|
var util = require_util2();
|
|
8709
8709
|
var { InvalidArgumentError: InvalidArgumentError2 } = require_errors();
|
|
@@ -8786,7 +8786,7 @@ var require_connect = __commonJS({
|
|
|
8786
8786
|
} else {
|
|
8787
8787
|
assert2(!httpSocket, "httpSocket can only be sent on TLS update");
|
|
8788
8788
|
port = port || 80;
|
|
8789
|
-
socket =
|
|
8789
|
+
socket = net4.connect({
|
|
8790
8790
|
highWaterMark: 64 * 1024,
|
|
8791
8791
|
// Same as nodejs fs streams.
|
|
8792
8792
|
...options,
|
|
@@ -14463,8 +14463,8 @@ var require_client = __commonJS({
|
|
|
14463
14463
|
"node_modules/.pnpm/undici@7.29.1/node_modules/undici/lib/dispatcher/client.js"(exports2, module2) {
|
|
14464
14464
|
"use strict";
|
|
14465
14465
|
var assert2 = require("node:assert");
|
|
14466
|
-
var
|
|
14467
|
-
var
|
|
14466
|
+
var net4 = require("node:net");
|
|
14467
|
+
var http8 = require("node:http");
|
|
14468
14468
|
var util = require_util2();
|
|
14469
14469
|
var { ClientStats } = require_stats();
|
|
14470
14470
|
var { channels } = require_diagnostics();
|
|
@@ -14522,7 +14522,7 @@ var require_client = __commonJS({
|
|
|
14522
14522
|
var connectH1 = require_client_h1();
|
|
14523
14523
|
var connectH2 = require_client_h2();
|
|
14524
14524
|
var kClosedResolve = /* @__PURE__ */ Symbol("kClosedResolve");
|
|
14525
|
-
var getDefaultNodeMaxHeaderSize =
|
|
14525
|
+
var getDefaultNodeMaxHeaderSize = http8 && http8.maxHeaderSize && Number.isInteger(http8.maxHeaderSize) && http8.maxHeaderSize > 0 ? () => http8.maxHeaderSize : () => {
|
|
14526
14526
|
throw new InvalidArgumentError2("http module not available or http.maxHeaderSize invalid");
|
|
14527
14527
|
};
|
|
14528
14528
|
var noop = () => {
|
|
@@ -14618,7 +14618,7 @@ var require_client = __commonJS({
|
|
|
14618
14618
|
if (maxRequestsPerClient != null && (!Number.isInteger(maxRequestsPerClient) || maxRequestsPerClient < 0)) {
|
|
14619
14619
|
throw new InvalidArgumentError2("maxRequestsPerClient must be a positive number");
|
|
14620
14620
|
}
|
|
14621
|
-
if (localAddress != null && (typeof localAddress !== "string" ||
|
|
14621
|
+
if (localAddress != null && (typeof localAddress !== "string" || net4.isIP(localAddress) === 0)) {
|
|
14622
14622
|
throw new InvalidArgumentError2("localAddress must be valid string IP address");
|
|
14623
14623
|
}
|
|
14624
14624
|
if (maxResponseSize != null && (!Number.isInteger(maxResponseSize) || maxResponseSize < -1)) {
|
|
@@ -14798,7 +14798,7 @@ var require_client = __commonJS({
|
|
|
14798
14798
|
const idx = hostname3.indexOf("]");
|
|
14799
14799
|
assert2(idx !== -1);
|
|
14800
14800
|
const ip = hostname3.substring(1, idx);
|
|
14801
|
-
assert2(
|
|
14801
|
+
assert2(net4.isIPv6(ip));
|
|
14802
14802
|
hostname3 = ip;
|
|
14803
14803
|
}
|
|
14804
14804
|
client[kConnecting] = true;
|
|
@@ -15743,10 +15743,10 @@ var require_socks5_utils = __commonJS({
|
|
|
15743
15743
|
"node_modules/.pnpm/undici@7.29.1/node_modules/undici/lib/core/socks5-utils.js"(exports2, module2) {
|
|
15744
15744
|
"use strict";
|
|
15745
15745
|
var { Buffer: Buffer3 } = require("node:buffer");
|
|
15746
|
-
var
|
|
15746
|
+
var net4 = require("node:net");
|
|
15747
15747
|
var { InvalidArgumentError: InvalidArgumentError2 } = require_errors();
|
|
15748
15748
|
function parseAddress(address) {
|
|
15749
|
-
if (
|
|
15749
|
+
if (net4.isIPv4(address)) {
|
|
15750
15750
|
const parts = address.split(".").map(Number);
|
|
15751
15751
|
return {
|
|
15752
15752
|
type: 1,
|
|
@@ -15754,7 +15754,7 @@ var require_socks5_utils = __commonJS({
|
|
|
15754
15754
|
buffer: Buffer3.from(parts)
|
|
15755
15755
|
};
|
|
15756
15756
|
}
|
|
15757
|
-
if (
|
|
15757
|
+
if (net4.isIPv6(address)) {
|
|
15758
15758
|
return {
|
|
15759
15759
|
type: 4,
|
|
15760
15760
|
// IPv6
|
|
@@ -15777,7 +15777,7 @@ var require_socks5_utils = __commonJS({
|
|
|
15777
15777
|
if (address.includes(".")) {
|
|
15778
15778
|
const lastColonIndex = address.lastIndexOf(":");
|
|
15779
15779
|
const ipv4Part = address.slice(lastColonIndex + 1);
|
|
15780
|
-
if (
|
|
15780
|
+
if (net4.isIPv4(ipv4Part)) {
|
|
15781
15781
|
const octets = ipv4Part.split(".").map(Number);
|
|
15782
15782
|
const high = (octets[0] << 8 | octets[1]).toString(16);
|
|
15783
15783
|
const low = (octets[2] << 8 | octets[3]).toString(16);
|
|
@@ -16658,18 +16658,18 @@ var require_proxy_agent = __commonJS({
|
|
|
16658
16658
|
}
|
|
16659
16659
|
}
|
|
16660
16660
|
[kClose]() {
|
|
16661
|
-
const
|
|
16661
|
+
const promises2 = [this[kAgent].close()];
|
|
16662
16662
|
if (this[kClient]) {
|
|
16663
|
-
|
|
16663
|
+
promises2.push(this[kClient].close());
|
|
16664
16664
|
}
|
|
16665
|
-
return Promise.all(
|
|
16665
|
+
return Promise.all(promises2);
|
|
16666
16666
|
}
|
|
16667
16667
|
[kDestroy]() {
|
|
16668
|
-
const
|
|
16668
|
+
const promises2 = [this[kAgent].destroy()];
|
|
16669
16669
|
if (this[kClient]) {
|
|
16670
|
-
|
|
16670
|
+
promises2.push(this[kClient].destroy());
|
|
16671
16671
|
}
|
|
16672
|
-
return Promise.all(
|
|
16672
|
+
return Promise.all(promises2);
|
|
16673
16673
|
}
|
|
16674
16674
|
};
|
|
16675
16675
|
function buildHeaders(headers) {
|
|
@@ -20817,7 +20817,7 @@ var require_dump = __commonJS({
|
|
|
20817
20817
|
var require_dns = __commonJS({
|
|
20818
20818
|
"node_modules/.pnpm/undici@7.29.1/node_modules/undici/lib/interceptor/dns.js"(exports2, module2) {
|
|
20819
20819
|
"use strict";
|
|
20820
|
-
var { isIP:
|
|
20820
|
+
var { isIP: isIP3 } = require("node:net");
|
|
20821
20821
|
var { lookup } = require("node:dns");
|
|
20822
20822
|
var DecoratorHandler = require_decorator_handler();
|
|
20823
20823
|
var { InvalidArgumentError: InvalidArgumentError2, InformationalError } = require_errors();
|
|
@@ -21223,7 +21223,7 @@ var require_dns = __commonJS({
|
|
|
21223
21223
|
return (dispatch) => {
|
|
21224
21224
|
return function dnsInterceptor(origDispatchOpts, handler) {
|
|
21225
21225
|
const origin = origDispatchOpts.origin.constructor === URL ? origDispatchOpts.origin : new URL(origDispatchOpts.origin);
|
|
21226
|
-
if (
|
|
21226
|
+
if (isIP3(origin.hostname) !== 0) {
|
|
21227
21227
|
return dispatch(origDispatchOpts, handler);
|
|
21228
21228
|
}
|
|
21229
21229
|
instance.runLookup(origin, origDispatchOpts, (err, newOrigin) => {
|
|
@@ -36009,8 +36009,8 @@ function getElementAtPath(obj, path44) {
|
|
|
36009
36009
|
}
|
|
36010
36010
|
function promiseAllObject(promisesObj) {
|
|
36011
36011
|
const keys = Object.keys(promisesObj);
|
|
36012
|
-
const
|
|
36013
|
-
return Promise.all(
|
|
36012
|
+
const promises2 = keys.map((key) => promisesObj[key]);
|
|
36013
|
+
return Promise.all(promises2).then((results) => {
|
|
36014
36014
|
const resolvedObj = {};
|
|
36015
36015
|
for (let i2 = 0; i2 < keys.length; i2++) {
|
|
36016
36016
|
resolvedObj[keys[i2]] = results[i2];
|
|
@@ -61790,7 +61790,7 @@ function registerIdentityCommand(program2, deps) {
|
|
|
61790
61790
|
// package.json
|
|
61791
61791
|
var package_default = {
|
|
61792
61792
|
name: "agent-shim",
|
|
61793
|
-
version: "8.
|
|
61793
|
+
version: "8.21.0",
|
|
61794
61794
|
description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
|
|
61795
61795
|
license: "MIT",
|
|
61796
61796
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -65671,22 +65671,22 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
65671
65671
|
};
|
|
65672
65672
|
writeFrontDoorState(fs19, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
|
|
65673
65673
|
const previousSticky = lastPort;
|
|
65674
|
-
let
|
|
65674
|
+
let http8;
|
|
65675
65675
|
try {
|
|
65676
|
-
|
|
65676
|
+
http8 = await ports.startProviderListener(lastPort);
|
|
65677
65677
|
} catch (error62) {
|
|
65678
65678
|
return fail(`could not start the provider listener: ${error62 instanceof Error ? error62.message : String(error62)}`);
|
|
65679
65679
|
}
|
|
65680
|
-
if (previousSticky !== void 0 &&
|
|
65681
|
-
ports.log(`agent-shim frontdoor supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(
|
|
65680
|
+
if (previousSticky !== void 0 && http8.port !== previousSticky) {
|
|
65681
|
+
ports.log(`agent-shim frontdoor supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(http8.port)} (sessions launched against the old port are stale until they relaunch)`);
|
|
65682
65682
|
}
|
|
65683
|
-
lastPort =
|
|
65683
|
+
lastPort = http8.port;
|
|
65684
65684
|
const previousStickyConnect = lastConnectPort;
|
|
65685
65685
|
let connect3;
|
|
65686
65686
|
try {
|
|
65687
65687
|
connect3 = await ports.startConnectListener(lastConnectPort);
|
|
65688
65688
|
} catch (error62) {
|
|
65689
|
-
await
|
|
65689
|
+
await http8.close();
|
|
65690
65690
|
return fail(`could not start the CONNECT surface: ${error62 instanceof Error ? error62.message : String(error62)}`);
|
|
65691
65691
|
}
|
|
65692
65692
|
if (previousStickyConnect !== void 0 && connect3.port !== previousStickyConnect) {
|
|
@@ -65699,16 +65699,16 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
65699
65699
|
direct = await ports.startDirectListener(lastDirectPort);
|
|
65700
65700
|
} catch (error62) {
|
|
65701
65701
|
await connect3.close();
|
|
65702
|
-
await
|
|
65702
|
+
await http8.close();
|
|
65703
65703
|
return fail(`could not start the direct listener: ${error62 instanceof Error ? error62.message : String(error62)}`);
|
|
65704
65704
|
}
|
|
65705
65705
|
if (previousStickyDirect !== void 0 && direct.port !== previousStickyDirect) {
|
|
65706
65706
|
ports.log(`agent-shim frontdoor supervisor: sticky direct port ${String(previousStickyDirect)} was occupied; moving to ${String(direct.port)} (headroom must be restarted to admit the new address)`);
|
|
65707
65707
|
}
|
|
65708
65708
|
lastDirectPort = direct.port;
|
|
65709
|
-
writeFrontDoorState(fs19, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port:
|
|
65709
|
+
writeFrontDoorState(fs19, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http8.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
|
|
65710
65710
|
fs19.removeRecursive(paths.frontdoorLockFile);
|
|
65711
|
-
ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: front door on 127.0.0.1:${String(
|
|
65711
|
+
ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: front door on 127.0.0.1:${String(http8.port)}, CONNECT surface on 127.0.0.1:${String(connect3.port)}`);
|
|
65712
65712
|
let idleSince;
|
|
65713
65713
|
let ticks = 0;
|
|
65714
65714
|
for (; ; ) {
|
|
@@ -65726,7 +65726,7 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
65726
65726
|
ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
|
|
65727
65727
|
await direct.close();
|
|
65728
65728
|
await connect3.close();
|
|
65729
|
-
await
|
|
65729
|
+
await http8.close();
|
|
65730
65730
|
ports.closeRcStreamHub();
|
|
65731
65731
|
writeFrontDoorState(fs19, paths.frontdoorStateFile, sticky());
|
|
65732
65732
|
return 0;
|
|
@@ -73994,6 +73994,252 @@ function createRcClientPage(expectedToken) {
|
|
|
73994
73994
|
};
|
|
73995
73995
|
}
|
|
73996
73996
|
|
|
73997
|
+
// src/frontdoor/rcWebFetch.ts
|
|
73998
|
+
var dns2 = __toESM(require("node:dns"), 1);
|
|
73999
|
+
var http6 = __toESM(require("node:http"), 1);
|
|
74000
|
+
var https5 = __toESM(require("node:https"), 1);
|
|
74001
|
+
var net3 = __toESM(require("node:net"), 1);
|
|
74002
|
+
var RC_WEB_FETCH_MAX_BYTES = 12582912;
|
|
74003
|
+
var RC_WEB_FETCH_DEADLINE_MS = 4e4;
|
|
74004
|
+
var RC_WEB_FETCH_MAX_REDIRECTS = 20;
|
|
74005
|
+
var RC_WEB_FETCH_ALLOW_PRIVATE_ENV = "AGENT_SHIM_FRONTDOOR_RC_SELF_HOST_WEB_FETCH_ALLOW_PRIVATE";
|
|
74006
|
+
function rcWebFetchAllowsPrivateFromEnv(env) {
|
|
74007
|
+
return env[RC_WEB_FETCH_ALLOW_PRIVATE_ENV] === "1";
|
|
74008
|
+
}
|
|
74009
|
+
var WEB_FETCH_USER_AGENT = "agent-shim-frontdoor";
|
|
74010
|
+
var ERROR_PREFIX = "web_fetch_";
|
|
74011
|
+
var V4_THIS_NETWORK_OCTET = 0;
|
|
74012
|
+
var V4_PRIVATE_A_OCTET = 10;
|
|
74013
|
+
var V4_LOOPBACK_OCTET = 127;
|
|
74014
|
+
var V4_CGNAT_OCTET = 100;
|
|
74015
|
+
var V4_CGNAT_SECOND_FROM = 64;
|
|
74016
|
+
var V4_CGNAT_SECOND_TO = 127;
|
|
74017
|
+
var V4_LINK_LOCAL_OCTET = 169;
|
|
74018
|
+
var V4_LINK_LOCAL_SECOND = 254;
|
|
74019
|
+
var V4_PRIVATE_B_OCTET = 172;
|
|
74020
|
+
var V4_PRIVATE_B_SECOND_FROM = 16;
|
|
74021
|
+
var V4_PRIVATE_B_SECOND_TO = 31;
|
|
74022
|
+
var V4_PRIVATE_C_OCTET = 192;
|
|
74023
|
+
var V4_PRIVATE_C_SECOND = 168;
|
|
74024
|
+
var V4_MULTICAST_FROM_OCTET = 224;
|
|
74025
|
+
var V6_UNIQUE_LOCAL_FROM = 64512;
|
|
74026
|
+
var V6_UNIQUE_LOCAL_TO = 65023;
|
|
74027
|
+
var V6_LINK_LOCAL_FROM = 65152;
|
|
74028
|
+
var V6_LINK_LOCAL_TO = 65215;
|
|
74029
|
+
var V6_MULTICAST_FROM = 65280;
|
|
74030
|
+
var IP_VERSION_4 = 4;
|
|
74031
|
+
var IP_VERSION_6 = 6;
|
|
74032
|
+
var HTTP_MOVED_PERMANENTLY = 301;
|
|
74033
|
+
var HTTP_FOUND = 302;
|
|
74034
|
+
var HTTP_SEE_OTHER = 303;
|
|
74035
|
+
var HTTP_TEMPORARY_REDIRECT = 307;
|
|
74036
|
+
var HTTP_PERMANENT_REDIRECT = 308;
|
|
74037
|
+
var SUCCESS_STATUS_MAX_EXCLUSIVE3 = 300;
|
|
74038
|
+
function refused(errorType, errorMessage2) {
|
|
74039
|
+
return { kind: "refused", errorType: `${ERROR_PREFIX}${errorType}`, errorMessage: errorMessage2 };
|
|
74040
|
+
}
|
|
74041
|
+
function isPrivateAddress(address) {
|
|
74042
|
+
const version2 = net3.isIP(address);
|
|
74043
|
+
if (version2 === IP_VERSION_4) {
|
|
74044
|
+
const octets = address.split(".").map((part) => Number(part));
|
|
74045
|
+
const first = octets[0] ?? -1;
|
|
74046
|
+
const second = octets[1] ?? -1;
|
|
74047
|
+
if (first === V4_THIS_NETWORK_OCTET || first === V4_PRIVATE_A_OCTET || first === V4_LOOPBACK_OCTET) {
|
|
74048
|
+
return true;
|
|
74049
|
+
}
|
|
74050
|
+
if (first === V4_CGNAT_OCTET && second >= V4_CGNAT_SECOND_FROM && second <= V4_CGNAT_SECOND_TO) {
|
|
74051
|
+
return true;
|
|
74052
|
+
}
|
|
74053
|
+
if (first === V4_LINK_LOCAL_OCTET && second === V4_LINK_LOCAL_SECOND) {
|
|
74054
|
+
return true;
|
|
74055
|
+
}
|
|
74056
|
+
if (first === V4_PRIVATE_B_OCTET && second >= V4_PRIVATE_B_SECOND_FROM && second <= V4_PRIVATE_B_SECOND_TO) {
|
|
74057
|
+
return true;
|
|
74058
|
+
}
|
|
74059
|
+
if (first === V4_PRIVATE_C_OCTET && second === V4_PRIVATE_C_SECOND) {
|
|
74060
|
+
return true;
|
|
74061
|
+
}
|
|
74062
|
+
return first >= V4_MULTICAST_FROM_OCTET;
|
|
74063
|
+
}
|
|
74064
|
+
if (version2 === IP_VERSION_6) {
|
|
74065
|
+
const lower = address.toLowerCase();
|
|
74066
|
+
const mapped = /^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/.exec(lower);
|
|
74067
|
+
if (mapped !== null) {
|
|
74068
|
+
return isPrivateAddress(mapped[1] ?? "");
|
|
74069
|
+
}
|
|
74070
|
+
if (lower === "::" || lower === "::1") {
|
|
74071
|
+
return true;
|
|
74072
|
+
}
|
|
74073
|
+
const firstGroup = Number.parseInt(/^([0-9a-f]+):/.exec(lower)?.[1] ?? "0", 16);
|
|
74074
|
+
if (Number.isNaN(firstGroup)) {
|
|
74075
|
+
return true;
|
|
74076
|
+
}
|
|
74077
|
+
if (firstGroup >= V6_UNIQUE_LOCAL_FROM && firstGroup <= V6_UNIQUE_LOCAL_TO) {
|
|
74078
|
+
return true;
|
|
74079
|
+
}
|
|
74080
|
+
if (firstGroup >= V6_LINK_LOCAL_FROM && firstGroup <= V6_LINK_LOCAL_TO) {
|
|
74081
|
+
return true;
|
|
74082
|
+
}
|
|
74083
|
+
return firstGroup >= V6_MULTICAST_FROM;
|
|
74084
|
+
}
|
|
74085
|
+
return true;
|
|
74086
|
+
}
|
|
74087
|
+
function isSuccessful3(status) {
|
|
74088
|
+
return status >= HTTP_STATUS.ok && status < SUCCESS_STATUS_MAX_EXCLUSIVE3;
|
|
74089
|
+
}
|
|
74090
|
+
function isRedirect(status) {
|
|
74091
|
+
return status === HTTP_MOVED_PERMANENTLY || status === HTTP_FOUND || status === HTTP_SEE_OTHER || status === HTTP_TEMPORARY_REDIRECT || status === HTTP_PERMANENT_REDIRECT;
|
|
74092
|
+
}
|
|
74093
|
+
function readableContentType(contentType) {
|
|
74094
|
+
if (contentType === void 0 || contentType.trim() === "") {
|
|
74095
|
+
return true;
|
|
74096
|
+
}
|
|
74097
|
+
const normalised = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
74098
|
+
const slash = normalised.indexOf("/");
|
|
74099
|
+
const main2 = slash === -1 ? normalised : normalised.slice(0, slash);
|
|
74100
|
+
const sub = slash === -1 ? "" : normalised.slice(slash + 1);
|
|
74101
|
+
if (main2 === "text") {
|
|
74102
|
+
return true;
|
|
74103
|
+
}
|
|
74104
|
+
return /json|xml|javascript|ecmascript|yaml|csv/.test(sub);
|
|
74105
|
+
}
|
|
74106
|
+
async function readCapped(chunks) {
|
|
74107
|
+
const collected = [];
|
|
74108
|
+
let total = 0;
|
|
74109
|
+
for await (const chunk of chunks) {
|
|
74110
|
+
total += chunk.byteLength;
|
|
74111
|
+
if (total > RC_WEB_FETCH_MAX_BYTES) {
|
|
74112
|
+
return { tooLarge: true, text: "" };
|
|
74113
|
+
}
|
|
74114
|
+
collected.push(Buffer.from(chunk));
|
|
74115
|
+
}
|
|
74116
|
+
return { tooLarge: false, text: Buffer.concat(collected).toString("utf8") };
|
|
74117
|
+
}
|
|
74118
|
+
async function checkTarget(url2, deps) {
|
|
74119
|
+
if (url2.protocol !== "http:" && url2.protocol !== "https:") {
|
|
74120
|
+
return refused("scheme", `the web-fetch proxy dials http and https only, not ${url2.protocol}`);
|
|
74121
|
+
}
|
|
74122
|
+
if (deps.allowPrivate) {
|
|
74123
|
+
return void 0;
|
|
74124
|
+
}
|
|
74125
|
+
const hostname3 = url2.hostname.replace(/^\[|\]$/g, "");
|
|
74126
|
+
let addresses;
|
|
74127
|
+
try {
|
|
74128
|
+
addresses = net3.isIP(hostname3) === 0 ? await deps.lookup(hostname3) : [hostname3];
|
|
74129
|
+
} catch {
|
|
74130
|
+
return refused("resolve", `${hostname3} did not resolve`);
|
|
74131
|
+
}
|
|
74132
|
+
for (const address of addresses) {
|
|
74133
|
+
if (isPrivateAddress(address)) {
|
|
74134
|
+
return refused("private_address", `${hostname3} resolves to ${address}, a private address this web-fetch proxy refuses; set ${RC_WEB_FETCH_ALLOW_PRIVATE_ENV}=1 on the door if serving internal targets is intended`);
|
|
74135
|
+
}
|
|
74136
|
+
}
|
|
74137
|
+
return void 0;
|
|
74138
|
+
}
|
|
74139
|
+
async function fetchThroughWebProxy(url2, deps) {
|
|
74140
|
+
let current;
|
|
74141
|
+
try {
|
|
74142
|
+
current = new URL(url2);
|
|
74143
|
+
} catch {
|
|
74144
|
+
return refused("malformed_url", `${url2} is not a URL this proxy can dial`);
|
|
74145
|
+
}
|
|
74146
|
+
const controller = new AbortController();
|
|
74147
|
+
const timer = setTimeout(() => {
|
|
74148
|
+
controller.abort();
|
|
74149
|
+
}, deps.deadlineMs);
|
|
74150
|
+
timer.unref();
|
|
74151
|
+
try {
|
|
74152
|
+
let redirects = 0;
|
|
74153
|
+
for (; ; ) {
|
|
74154
|
+
const refusal = await checkTarget(current, deps);
|
|
74155
|
+
if (refusal !== void 0) {
|
|
74156
|
+
return refusal;
|
|
74157
|
+
}
|
|
74158
|
+
let hop;
|
|
74159
|
+
try {
|
|
74160
|
+
hop = await deps.dial(current, controller.signal);
|
|
74161
|
+
} catch (error62) {
|
|
74162
|
+
if (controller.signal.aborted) {
|
|
74163
|
+
return refused("deadline", `the fetch of ${url2} did not settle within its deadline`);
|
|
74164
|
+
}
|
|
74165
|
+
return refused("transport", `the dial of ${current.toString()} failed: ${error62 instanceof Error ? error62.message : "unknown error"}`);
|
|
74166
|
+
}
|
|
74167
|
+
if (isRedirect(hop.status) && hop.location !== void 0) {
|
|
74168
|
+
if (redirects >= RC_WEB_FETCH_MAX_REDIRECTS) {
|
|
74169
|
+
controller.abort();
|
|
74170
|
+
return refused("too_many_redirects", `${url2} followed more than ${String(RC_WEB_FETCH_MAX_REDIRECTS)} redirects without settling`);
|
|
74171
|
+
}
|
|
74172
|
+
let next;
|
|
74173
|
+
try {
|
|
74174
|
+
next = new URL(hop.location, current);
|
|
74175
|
+
} catch {
|
|
74176
|
+
controller.abort();
|
|
74177
|
+
return refused("malformed_url", `a redirect of ${current.toString()} named ${hop.location}, which is not a URL this proxy can dial`);
|
|
74178
|
+
}
|
|
74179
|
+
const drained = await readCapped(hop.chunks);
|
|
74180
|
+
if (drained.tooLarge) {
|
|
74181
|
+
controller.abort();
|
|
74182
|
+
return refused("too_large", `the body of a redirect from ${url2} exceeds the ${String(RC_WEB_FETCH_MAX_BYTES)} byte cap the CLI's proxy client reads`);
|
|
74183
|
+
}
|
|
74184
|
+
redirects += 1;
|
|
74185
|
+
current = next;
|
|
74186
|
+
continue;
|
|
74187
|
+
}
|
|
74188
|
+
if (!isSuccessful3(hop.status)) {
|
|
74189
|
+
controller.abort();
|
|
74190
|
+
return refused("status", `the target answered HTTP ${String(hop.status)} for ${current.toString()}`);
|
|
74191
|
+
}
|
|
74192
|
+
const contentType = hop.contentType ?? "";
|
|
74193
|
+
if (!readableContentType(contentType)) {
|
|
74194
|
+
controller.abort();
|
|
74195
|
+
return refused("content_type", `the target at ${current.toString()} served a ${contentType} answer, which the proxy's text field cannot carry`);
|
|
74196
|
+
}
|
|
74197
|
+
const read = await readCapped(hop.chunks);
|
|
74198
|
+
if (read.tooLarge) {
|
|
74199
|
+
controller.abort();
|
|
74200
|
+
return refused("too_large", `the body of ${current.toString()} exceeds the ${String(RC_WEB_FETCH_MAX_BYTES)} byte cap the CLI's proxy client reads`);
|
|
74201
|
+
}
|
|
74202
|
+
return { kind: "fetched", url: url2, destinationUrl: current.toString(), contentType: hop.contentType, text: read.text };
|
|
74203
|
+
}
|
|
74204
|
+
} finally {
|
|
74205
|
+
clearTimeout(timer);
|
|
74206
|
+
}
|
|
74207
|
+
}
|
|
74208
|
+
function realRcWebFetch(options) {
|
|
74209
|
+
const plainAgent = new ExemptHttpAgent();
|
|
74210
|
+
const tlsAgent = new ExemptTlsAgent();
|
|
74211
|
+
const deps = {
|
|
74212
|
+
dial: async (url2, signal) => await new Promise((resolve, reject) => {
|
|
74213
|
+
const request7 = (url2.protocol === "https:" ? https5 : http6).request(
|
|
74214
|
+
{
|
|
74215
|
+
hostname: url2.hostname,
|
|
74216
|
+
port: url2.port === "" ? void 0 : Number(url2.port),
|
|
74217
|
+
method: "GET",
|
|
74218
|
+
path: `${url2.pathname}${url2.search}`,
|
|
74219
|
+
headers: { accept: "*/*", "accept-encoding": "identity", "user-agent": WEB_FETCH_USER_AGENT },
|
|
74220
|
+
agent: url2.protocol === "https:" ? tlsAgent : plainAgent,
|
|
74221
|
+
signal
|
|
74222
|
+
},
|
|
74223
|
+
(response) => {
|
|
74224
|
+
const location = response.headers.location;
|
|
74225
|
+
resolve({
|
|
74226
|
+
status: response.statusCode ?? 0,
|
|
74227
|
+
location: typeof location === "string" ? location : void 0,
|
|
74228
|
+
contentType: typeof response.headers["content-type"] === "string" ? response.headers["content-type"] : void 0,
|
|
74229
|
+
chunks: response
|
|
74230
|
+
});
|
|
74231
|
+
}
|
|
74232
|
+
);
|
|
74233
|
+
request7.once("error", reject);
|
|
74234
|
+
request7.end();
|
|
74235
|
+
}),
|
|
74236
|
+
lookup: async (hostname3) => (await dns2.promises.lookup(hostname3, { all: true })).map((entry) => entry.address),
|
|
74237
|
+
allowPrivate: options.allowPrivate,
|
|
74238
|
+
deadlineMs: RC_WEB_FETCH_DEADLINE_MS
|
|
74239
|
+
};
|
|
74240
|
+
return async (url2) => await fetchThroughWebProxy(url2, deps);
|
|
74241
|
+
}
|
|
74242
|
+
|
|
73997
74243
|
// src/frontdoor/rcSelfHost.ts
|
|
73998
74244
|
var RC_SELF_HOST_ENV = "AGENT_SHIM_FRONTDOOR_RC_SELF_HOST";
|
|
73999
74245
|
function rcSelfHostFromEnv(env) {
|
|
@@ -74023,6 +74269,9 @@ var CONFLICT_REASON_EPOCH_STALE = "epoch_stale";
|
|
|
74023
74269
|
function isRecord5(value2) {
|
|
74024
74270
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
74025
74271
|
}
|
|
74272
|
+
function isWebSearchRefusal(value2) {
|
|
74273
|
+
return !Array.isArray(value2);
|
|
74274
|
+
}
|
|
74026
74275
|
function nonEmptyString2(record2, field) {
|
|
74027
74276
|
const value2 = record2 === void 0 ? void 0 : record2[field];
|
|
74028
74277
|
return typeof value2 === "string" && value2 !== "" ? value2 : void 0;
|
|
@@ -74037,6 +74286,20 @@ function numberOf(record2, field) {
|
|
|
74037
74286
|
}
|
|
74038
74287
|
return void 0;
|
|
74039
74288
|
}
|
|
74289
|
+
function stringArray(record2, field) {
|
|
74290
|
+
const value2 = record2 === void 0 ? void 0 : record2[field];
|
|
74291
|
+
if (!Array.isArray(value2)) {
|
|
74292
|
+
return void 0;
|
|
74293
|
+
}
|
|
74294
|
+
const members2 = [];
|
|
74295
|
+
for (const member of value2) {
|
|
74296
|
+
if (typeof member !== "string") {
|
|
74297
|
+
return void 0;
|
|
74298
|
+
}
|
|
74299
|
+
members2.push(member);
|
|
74300
|
+
}
|
|
74301
|
+
return members2;
|
|
74302
|
+
}
|
|
74040
74303
|
function singleHeader2(headers, name) {
|
|
74041
74304
|
const value2 = headers[name];
|
|
74042
74305
|
return typeof value2 === "string" ? value2 : void 0;
|
|
@@ -74267,11 +74530,12 @@ data: ${JSON.stringify(envelope)}
|
|
|
74267
74530
|
return;
|
|
74268
74531
|
}
|
|
74269
74532
|
if (tail === "worker" || tail.startsWith("worker/")) {
|
|
74270
|
-
|
|
74533
|
+
const workerTail = tail === "worker" ? "" : tail.slice("worker/".length);
|
|
74534
|
+
const webProxy = workerTail === "web-fetch" || workerTail === "web-search";
|
|
74535
|
+
if (!(webProxy ? presentsCredential(request7.headers) || presentsWorkerJwt(request7.headers, session) : presentsWorkerJwt(request7.headers, session))) {
|
|
74271
74536
|
await unauthorized(response);
|
|
74272
74537
|
return;
|
|
74273
74538
|
}
|
|
74274
|
-
const workerTail = tail === "worker" ? "" : tail.slice("worker/".length);
|
|
74275
74539
|
if (workerTail === "" && method === "GET") {
|
|
74276
74540
|
await answerJson2(response, HTTP_STATUS.ok, { worker: { external_metadata: session.externalMetadata ?? null } });
|
|
74277
74541
|
return;
|
|
@@ -74370,6 +74634,58 @@ data: ${JSON.stringify(envelope)}
|
|
|
74370
74634
|
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
74371
74635
|
return;
|
|
74372
74636
|
}
|
|
74637
|
+
if (workerTail === "web-fetch" && method === "POST") {
|
|
74638
|
+
const body = parseJsonObject2(await readBody3(request7.body, CONTROL_BODY_CAP_BYTES));
|
|
74639
|
+
const target = nonEmptyString2(body, "url");
|
|
74640
|
+
if (target === void 0) {
|
|
74641
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-fetch carries one url" } });
|
|
74642
|
+
return;
|
|
74643
|
+
}
|
|
74644
|
+
const outcome = await deps.webFetch(target);
|
|
74645
|
+
if (outcome.kind === "fetched") {
|
|
74646
|
+
const answer = { url: target, destination_url: outcome.destinationUrl, text: outcome.text, ...outcome.contentType === void 0 ? {} : { content_type: outcome.contentType } };
|
|
74647
|
+
if (Buffer.byteLength(JSON.stringify(answer), "utf8") > RC_WEB_FETCH_MAX_BYTES) {
|
|
74648
|
+
deps.log?.(`rc selfhost ${session.id}: worker web-fetch of ${target} escaped past the reader cap when framed`);
|
|
74649
|
+
await answerJson2(response, HTTP_STATUS.ok, { error: { error_type: "web_fetch_too_large", error_message: `the answer for ${target} exceeds the ${String(RC_WEB_FETCH_MAX_BYTES)} byte cap the CLI's proxy client reads` } });
|
|
74650
|
+
return;
|
|
74651
|
+
}
|
|
74652
|
+
deps.log?.(`rc selfhost ${session.id}: worker web-fetch served ${target} as ${outcome.destinationUrl}`);
|
|
74653
|
+
await answerJson2(response, HTTP_STATUS.ok, answer);
|
|
74654
|
+
return;
|
|
74655
|
+
}
|
|
74656
|
+
deps.log?.(`rc selfhost ${session.id}: worker web-fetch of ${target} refused: ${outcome.errorType}`);
|
|
74657
|
+
await answerJson2(response, HTTP_STATUS.ok, { error: { error_type: outcome.errorType, error_message: outcome.errorMessage } });
|
|
74658
|
+
return;
|
|
74659
|
+
}
|
|
74660
|
+
if (workerTail === "web-search" && method === "POST") {
|
|
74661
|
+
const body = parseJsonObject2(await readBody3(request7.body, CONTROL_BODY_CAP_BYTES));
|
|
74662
|
+
const query = nonEmptyString2(body, "query");
|
|
74663
|
+
if (query === void 0) {
|
|
74664
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-search carries one query" } });
|
|
74665
|
+
return;
|
|
74666
|
+
}
|
|
74667
|
+
const allowedDomains = stringArray(body, "allowed_domains");
|
|
74668
|
+
const blockedDomains = stringArray(body, "blocked_domains");
|
|
74669
|
+
if (body?.allowed_domains !== void 0 && allowedDomains === void 0 || body?.blocked_domains !== void 0 && blockedDomains === void 0) {
|
|
74670
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "the domain filters of a worker web-search are string arrays" } });
|
|
74671
|
+
return;
|
|
74672
|
+
}
|
|
74673
|
+
const searchProfile = nonEmptyString2(body, "search_profile");
|
|
74674
|
+
const backend = deps.webSearch;
|
|
74675
|
+
if (backend === void 0) {
|
|
74676
|
+
deps.log?.(`rc selfhost ${session.id}: worker web-search of "${query}" refused: no search backend serves this door`);
|
|
74677
|
+
await answerJson2(response, HTTP_STATUS.ok, { results: [], error: { error_type: "web_search_unavailable", error_message: "this front door serves the Remote Control web-search proxy without a search backend, so no query can be served here" } });
|
|
74678
|
+
return;
|
|
74679
|
+
}
|
|
74680
|
+
const answered = await backend({ query, ...allowedDomains === void 0 ? {} : { allowedDomains }, ...blockedDomains === void 0 ? {} : { blockedDomains }, ...searchProfile === void 0 ? {} : { searchProfile } });
|
|
74681
|
+
deps.log?.(`rc selfhost ${session.id}: worker web-search of "${query}" answered by its backend`);
|
|
74682
|
+
if (isWebSearchRefusal(answered)) {
|
|
74683
|
+
await answerJson2(response, HTTP_STATUS.ok, { results: [], error: { error_type: answered.errorType, error_message: answered.errorMessage } });
|
|
74684
|
+
return;
|
|
74685
|
+
}
|
|
74686
|
+
await answerJson2(response, HTTP_STATUS.ok, { results: answered.map((result) => ({ title: result.title ?? "", url: result.url, snippet: result.snippet ?? "" })) });
|
|
74687
|
+
return;
|
|
74688
|
+
}
|
|
74373
74689
|
await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no worker path "${workerTail}"` } });
|
|
74374
74690
|
return;
|
|
74375
74691
|
}
|
|
@@ -74684,9 +75000,9 @@ function mintRcSelfHostCredential(deps) {
|
|
|
74684
75000
|
var RC_STREAM_EVENT_NAME2 = "client_event";
|
|
74685
75001
|
var SSE_BYTE_ORDER_MARK = 65279;
|
|
74686
75002
|
var RC_STREAM_BACKOFF_MS = 45e3;
|
|
74687
|
-
var
|
|
74688
|
-
function
|
|
74689
|
-
return status >= HTTP_STATUS.ok && status <
|
|
75003
|
+
var SUCCESS_STATUS_MAX_EXCLUSIVE4 = 300;
|
|
75004
|
+
function isSuccessful4(status) {
|
|
75005
|
+
return status >= HTTP_STATUS.ok && status < SUCCESS_STATUS_MAX_EXCLUSIVE4;
|
|
74690
75006
|
}
|
|
74691
75007
|
function createSseParser() {
|
|
74692
75008
|
let buffer = "";
|
|
@@ -74853,7 +75169,7 @@ function createRcStreamHub(deps) {
|
|
|
74853
75169
|
if (presence.status === HTTP_STATUS.unauthorized) {
|
|
74854
75170
|
return "unauthorized";
|
|
74855
75171
|
}
|
|
74856
|
-
if (!
|
|
75172
|
+
if (!isSuccessful4(presence.status)) {
|
|
74857
75173
|
throw new Error(`the presence call answered HTTP ${String(presence.status)}: ${presence.body}`);
|
|
74858
75174
|
}
|
|
74859
75175
|
const cursor3 = deps.sequenceNumOf(attachment.sessionId) ?? deps.storedSequenceNumOf?.(attachment.sessionId);
|
|
@@ -74862,7 +75178,7 @@ function createRcStreamHub(deps) {
|
|
|
74862
75178
|
if (answer.status === HTTP_STATUS.unauthorized) {
|
|
74863
75179
|
return "unauthorized";
|
|
74864
75180
|
}
|
|
74865
|
-
if (!
|
|
75181
|
+
if (!isSuccessful4(answer.status)) {
|
|
74866
75182
|
throw new Error(`the client read stream answered HTTP ${String(answer.status)}`);
|
|
74867
75183
|
}
|
|
74868
75184
|
deps.log?.(`rc stream ${attachment.sessionId}: attached as client ${attachment.clientId}${resume === void 0 ? ", reading from the stream's own head" : `, resuming after sequence number ${String(resume.fromSequenceNum)}`}`);
|
|
@@ -75066,8 +75382,8 @@ function zaiWindow(limit) {
|
|
|
75066
75382
|
var zaiQuotaAdapter = {
|
|
75067
75383
|
source: ZAI_SOURCE,
|
|
75068
75384
|
matches: (baseUrl) => URL.canParse(baseUrl) && new URL(baseUrl).hostname === ZAI_HOST,
|
|
75069
|
-
fetch: async (apiKey,
|
|
75070
|
-
const response = await
|
|
75385
|
+
fetch: async (apiKey, http8, signal, nowMs) => {
|
|
75386
|
+
const response = await http8(ZAI_QUOTA_URL, { authorization: apiKey, accept: "application/json" }, signal);
|
|
75071
75387
|
if (response.status !== HTTP_OK) {
|
|
75072
75388
|
throw new ProviderQuotaError(ZAI_SOURCE, `answered HTTP ${String(response.status)}`);
|
|
75073
75389
|
}
|
|
@@ -75108,8 +75424,8 @@ var OpenRouterKeySchema = external_exports.object({
|
|
|
75108
75424
|
})
|
|
75109
75425
|
});
|
|
75110
75426
|
var OpenRouterCreditsSchema = external_exports.object({ data: external_exports.object({ total_credits: external_exports.number().nonnegative(), total_usage: external_exports.number().nonnegative() }) });
|
|
75111
|
-
async function openRouterGet(url2, schema, apiKey,
|
|
75112
|
-
const response = await
|
|
75427
|
+
async function openRouterGet(url2, schema, apiKey, http8, signal) {
|
|
75428
|
+
const response = await http8(url2, { authorization: `Bearer ${apiKey}`, accept: "application/json" }, signal);
|
|
75113
75429
|
if (response.status === HTTP_FORBIDDEN) {
|
|
75114
75430
|
return "forbidden";
|
|
75115
75431
|
}
|
|
@@ -75134,12 +75450,12 @@ function nextUtcMidnight(nowMs) {
|
|
|
75134
75450
|
var openRouterQuotaAdapter = {
|
|
75135
75451
|
source: OPENROUTER_SOURCE,
|
|
75136
75452
|
matches: (baseUrl) => URL.canParse(baseUrl) && new URL(baseUrl).hostname === OPENROUTER_HOST,
|
|
75137
|
-
fetch: async (apiKey,
|
|
75138
|
-
const key = await openRouterGet(OPENROUTER_KEY_URL, OpenRouterKeySchema, apiKey,
|
|
75453
|
+
fetch: async (apiKey, http8, signal, nowMs) => {
|
|
75454
|
+
const key = await openRouterGet(OPENROUTER_KEY_URL, OpenRouterKeySchema, apiKey, http8, signal);
|
|
75139
75455
|
if (key === "forbidden") {
|
|
75140
75456
|
throw new ProviderQuotaError(OPENROUTER_SOURCE, "/api/v1/key refused the key (HTTP 403)");
|
|
75141
75457
|
}
|
|
75142
|
-
const credits = await openRouterGet(OPENROUTER_CREDITS_URL, OpenRouterCreditsSchema, apiKey,
|
|
75458
|
+
const credits = await openRouterGet(OPENROUTER_CREDITS_URL, OpenRouterCreditsSchema, apiKey, http8, signal);
|
|
75143
75459
|
const { limit, limit_remaining: limitRemaining, limit_reset: limitReset, free_model_daily_requests: free, is_free_tier: isFreeTier } = key.data;
|
|
75144
75460
|
const windows = [];
|
|
75145
75461
|
if (limit !== null && limit > 0) {
|
|
@@ -75335,6 +75651,7 @@ function realFrontDoorSupervisorPorts(paths) {
|
|
|
75335
75651
|
newUuid: import_node_crypto14.randomUUID,
|
|
75336
75652
|
randomToken: () => (0, import_node_crypto14.randomBytes)(MINTED_TOKEN_RANDOM_BYTES).toString("base64url"),
|
|
75337
75653
|
credentialRecord: () => readRcSelfHostRecord(realFarmFs, paths.frontdoorDir),
|
|
75654
|
+
webFetch: realRcWebFetch({ allowPrivate: rcWebFetchAllowsPrivateFromEnv(process.env) }),
|
|
75338
75655
|
log
|
|
75339
75656
|
}) : void 0;
|
|
75340
75657
|
const rcCredentialStore = createRcCredentialStore(realFarmFs, paths.frontdoorRcCredentialsDir);
|