@maintainer-pro/ai-bridge 0.1.21 → 0.1.23
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/package.json +2 -2
- package/src/daemon.mjs +58 -3
- package/src/share-rewrite.mjs +129 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maintainer-pro/ai-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "Local bridge daemon that pairs a machine to Maintainer Pro and configures multiple client sandboxes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"maintainer-pro",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"node": ">=22"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@maintainer-pro/ai-cli": "^0.1.
|
|
31
|
+
"@maintainer-pro/ai-cli": "^0.1.13",
|
|
32
32
|
"@maintainer-pro/ai-server": "^0.1.7"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/src/daemon.mjs
CHANGED
|
@@ -16,6 +16,7 @@ import os from "node:os";
|
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
import readline from "node:readline";
|
|
18
18
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
19
|
+
import { createRequire } from "node:module";
|
|
19
20
|
import { createLogger, ensureProjectDataDir } from "@maintainer-pro/ai-cli";
|
|
20
21
|
import { findIife, startAiServer } from "@maintainer-pro/ai-server";
|
|
21
22
|
import {
|
|
@@ -39,7 +40,9 @@ import {
|
|
|
39
40
|
} from "./discarded-tunnels.mjs";
|
|
40
41
|
|
|
41
42
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
43
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
42
44
|
const PACKAGE_VERSION = readPackageVersion();
|
|
45
|
+
const PACKAGE_VERSIONS = readPackageVersions();
|
|
43
46
|
const HEARTBEAT_MS = 15_000;
|
|
44
47
|
const WS_PING_MS = 10_000;
|
|
45
48
|
const WS_RECONNECT_MIN_MS = 1_000;
|
|
@@ -157,6 +160,53 @@ function readPackageVersion() {
|
|
|
157
160
|
}
|
|
158
161
|
}
|
|
159
162
|
|
|
163
|
+
function readDepPackageVersion(name) {
|
|
164
|
+
const files = [];
|
|
165
|
+
try {
|
|
166
|
+
files.push(requireFromHere.resolve(`${name}/package.json`));
|
|
167
|
+
} catch {
|
|
168
|
+
/* try nested next */
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const serverDir = path.dirname(
|
|
172
|
+
requireFromHere.resolve("@maintainer-pro/ai-server/package.json")
|
|
173
|
+
);
|
|
174
|
+
files.push(path.join(serverDir, "node_modules", name, "package.json"));
|
|
175
|
+
files.push(path.join(serverDir, "..", name.replace("@maintainer-pro/", ""), "package.json"));
|
|
176
|
+
} catch {
|
|
177
|
+
/* ignore */
|
|
178
|
+
}
|
|
179
|
+
for (const file of files) {
|
|
180
|
+
try {
|
|
181
|
+
const pkg = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
182
|
+
if (typeof pkg.version === "string" && pkg.version) return pkg.version;
|
|
183
|
+
} catch {
|
|
184
|
+
/* try next */
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function readPackageVersions() {
|
|
191
|
+
return {
|
|
192
|
+
bridge: PACKAGE_VERSION,
|
|
193
|
+
cli: readDepPackageVersion("@maintainer-pro/ai-cli"),
|
|
194
|
+
server: readDepPackageVersion("@maintainer-pro/ai-server"),
|
|
195
|
+
ui: readDepPackageVersion("@maintainer-pro/ai-ui"),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function formatPackageVersions(versions = PACKAGE_VERSIONS) {
|
|
200
|
+
return [
|
|
201
|
+
versions.bridge && `bridge ${versions.bridge}`,
|
|
202
|
+
versions.cli && `cli ${versions.cli}`,
|
|
203
|
+
versions.server && `server ${versions.server}`,
|
|
204
|
+
versions.ui && `ui ${versions.ui}`,
|
|
205
|
+
]
|
|
206
|
+
.filter(Boolean)
|
|
207
|
+
.join(" · ");
|
|
208
|
+
}
|
|
209
|
+
|
|
160
210
|
async function warnIfBridgeOutdated() {
|
|
161
211
|
const fromWorkspace = path
|
|
162
212
|
.normalize(__dirname)
|
|
@@ -2325,6 +2375,7 @@ function bridgeEmbedConfigJs(ws) {
|
|
|
2325
2375
|
logLevel: "debug",
|
|
2326
2376
|
maintainerProUrl: bridgeCfg?.adminUrl || "",
|
|
2327
2377
|
maintainerProApiKey: String(store.clientKey || "").trim(),
|
|
2378
|
+
versions: PACKAGE_VERSIONS,
|
|
2328
2379
|
};
|
|
2329
2380
|
return `window.__MAINTAINER_PRO__=${JSON.stringify(payload)};`;
|
|
2330
2381
|
}
|
|
@@ -4834,6 +4885,7 @@ async function sendHeartbeat(cfg, folders, localStates) {
|
|
|
4834
4885
|
hostname: os.hostname(),
|
|
4835
4886
|
platform: `${os.platform()}-${os.arch()}`,
|
|
4836
4887
|
bridgeVersion: PACKAGE_VERSION,
|
|
4888
|
+
packageVersions: PACKAGE_VERSIONS,
|
|
4837
4889
|
folders,
|
|
4838
4890
|
issues: await buildIssues(cfg, localStates),
|
|
4839
4891
|
workspaces: localStates.map((st) => ({
|
|
@@ -4866,6 +4918,7 @@ function buildLightHeartbeatPayload(cfg, folders) {
|
|
|
4866
4918
|
hostname: os.hostname(),
|
|
4867
4919
|
platform: `${os.platform()}-${os.arch()}`,
|
|
4868
4920
|
bridgeVersion: PACKAGE_VERSION,
|
|
4921
|
+
packageVersions: PACKAGE_VERSIONS,
|
|
4869
4922
|
folders,
|
|
4870
4923
|
};
|
|
4871
4924
|
}
|
|
@@ -4887,6 +4940,7 @@ async function buildHeartbeatPayload(cfg, folders, localStates) {
|
|
|
4887
4940
|
hostname: os.hostname(),
|
|
4888
4941
|
platform: `${os.platform()}-${os.arch()}`,
|
|
4889
4942
|
bridgeVersion: PACKAGE_VERSION,
|
|
4943
|
+
packageVersions: PACKAGE_VERSIONS,
|
|
4890
4944
|
folders,
|
|
4891
4945
|
issues: await buildIssues(cfg, localStates),
|
|
4892
4946
|
workspaces: localStates.map((st) => ({
|
|
@@ -5021,6 +5075,7 @@ async function pairFlow(args) {
|
|
|
5021
5075
|
hostname: os.hostname(),
|
|
5022
5076
|
platform: `${os.platform()}-${os.arch()}`,
|
|
5023
5077
|
bridgeVersion: PACKAGE_VERSION,
|
|
5078
|
+
packageVersions: PACKAGE_VERSIONS,
|
|
5024
5079
|
name: os.hostname(),
|
|
5025
5080
|
});
|
|
5026
5081
|
|
|
@@ -5048,9 +5103,9 @@ async function main() {
|
|
|
5048
5103
|
process.exit(0);
|
|
5049
5104
|
}
|
|
5050
5105
|
|
|
5051
|
-
logger.
|
|
5052
|
-
{
|
|
5053
|
-
|
|
5106
|
+
logger.info(
|
|
5107
|
+
{ versions: PACKAGE_VERSIONS },
|
|
5108
|
+
`bridge v${PACKAGE_VERSION} starting (${formatPackageVersions()})`
|
|
5054
5109
|
);
|
|
5055
5110
|
void warnIfBridgeOutdated();
|
|
5056
5111
|
|
package/src/share-rewrite.mjs
CHANGED
|
@@ -274,6 +274,33 @@ function mappedUrlForPort(portUrls, port) {
|
|
|
274
274
|
return dest ? String(dest).replace(/\/$/, "") : "";
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
+
function backendShareBase(portUrls) {
|
|
278
|
+
if (!portUrls || typeof portUrls !== "object") return "";
|
|
279
|
+
for (const dest of Object.values(portUrls)) {
|
|
280
|
+
const url = String(dest || "").replace(/\/$/, "");
|
|
281
|
+
if (!url) continue;
|
|
282
|
+
try {
|
|
283
|
+
const slug = new URL(url).pathname.split("/").filter(Boolean)[2] || "";
|
|
284
|
+
if (slug === "backend" || slug.startsWith("backend-")) return url;
|
|
285
|
+
} catch {
|
|
286
|
+
if (/\/backend(?:-[^/]+)?$/i.test(url)) return url;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return "";
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function shareBaseForPath(path, publicBase, portUrls) {
|
|
293
|
+
const pathname = String(path || "").split("?")[0] || "/";
|
|
294
|
+
if (
|
|
295
|
+
isShareAssetPath(pathname) ||
|
|
296
|
+
pathname.startsWith("/_next/") ||
|
|
297
|
+
pathname.startsWith("/@")
|
|
298
|
+
) {
|
|
299
|
+
return publicBase;
|
|
300
|
+
}
|
|
301
|
+
return backendShareBase(portUrls) || publicBase;
|
|
302
|
+
}
|
|
303
|
+
|
|
277
304
|
function isProbablyUtf8Text(buf) {
|
|
278
305
|
if (!Buffer.isBuffer(buf) || !buf.length || buf.length > 8 * 1024 * 1024) {
|
|
279
306
|
return false;
|
|
@@ -410,7 +437,7 @@ function rewriteHeaderUrls(headers, publicBase, port, portUrls) {
|
|
|
410
437
|
if (lower === "set-cookie" || lower === "content-length" || lower === "content-encoding") {
|
|
411
438
|
continue;
|
|
412
439
|
}
|
|
413
|
-
headers[key] = rewriteShareOriginPaths(headers[key], publicBase);
|
|
440
|
+
headers[key] = rewriteShareOriginPaths(headers[key], publicBase, portUrls);
|
|
414
441
|
}
|
|
415
442
|
}
|
|
416
443
|
}
|
|
@@ -448,7 +475,7 @@ export function prefixShareOriginUrl(value, publicBase) {
|
|
|
448
475
|
}
|
|
449
476
|
}
|
|
450
477
|
|
|
451
|
-
export function rewriteShareOriginPaths(text, publicBase) {
|
|
478
|
+
export function rewriteShareOriginPaths(text, publicBase, portUrls) {
|
|
452
479
|
if (!text || !publicBase) return text;
|
|
453
480
|
let origin = "";
|
|
454
481
|
try {
|
|
@@ -461,15 +488,24 @@ export function rewriteShareOriginPaths(text, publicBase) {
|
|
|
461
488
|
const originSlashEsc = origin
|
|
462
489
|
.replace(/\//g, "\\/")
|
|
463
490
|
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
491
|
+
const baseFor = function (matched) {
|
|
492
|
+
let path = "/";
|
|
493
|
+
try {
|
|
494
|
+
path = new URL(matched).pathname || "/";
|
|
495
|
+
} catch {
|
|
496
|
+
/* keep */
|
|
497
|
+
}
|
|
498
|
+
return shareBaseForPath(path, publicBase, portUrls);
|
|
499
|
+
};
|
|
464
500
|
let out = String(text);
|
|
465
501
|
out = out.replace(new RegExp(originEsc + "/(?!p/)[^\\s'\"<>\\\\]+", "gi"), function (matched) {
|
|
466
|
-
return prefixShareOriginUrl(matched,
|
|
502
|
+
return prefixShareOriginUrl(matched, baseFor(matched));
|
|
467
503
|
});
|
|
468
504
|
out = out.replace(
|
|
469
505
|
new RegExp(originSlashEsc + "\\\\/(?!p/)[^\\s'\"<>]+", "gi"),
|
|
470
506
|
function (matched) {
|
|
471
507
|
const unescaped = matched.replace(/\\\//g, "/");
|
|
472
|
-
return prefixShareOriginUrl(unescaped,
|
|
508
|
+
return prefixShareOriginUrl(unescaped, baseFor(unescaped)).replace(/\//g, "\\/");
|
|
473
509
|
}
|
|
474
510
|
);
|
|
475
511
|
return out;
|
|
@@ -495,7 +531,18 @@ function rewriteLocation(value, publicBase, port, portUrls) {
|
|
|
495
531
|
}
|
|
496
532
|
const fromPorts = rewriteMappedLocalUrls(String(value), portUrls);
|
|
497
533
|
if (fromPorts !== String(value)) return fromPorts;
|
|
498
|
-
if (publicBase)
|
|
534
|
+
if (publicBase) {
|
|
535
|
+
let path = "/";
|
|
536
|
+
try {
|
|
537
|
+
path = new URL(String(value)).pathname || "/";
|
|
538
|
+
} catch {
|
|
539
|
+
/* keep */
|
|
540
|
+
}
|
|
541
|
+
return prefixShareOriginUrl(
|
|
542
|
+
String(value),
|
|
543
|
+
shareBaseForPath(path, publicBase, portUrls)
|
|
544
|
+
);
|
|
545
|
+
}
|
|
499
546
|
} catch {
|
|
500
547
|
/* keep */
|
|
501
548
|
}
|
|
@@ -651,6 +698,19 @@ function isShareAssetPath(path) {
|
|
|
651
698
|
);
|
|
652
699
|
}
|
|
653
700
|
|
|
701
|
+
function rewriteShareAsLocalEnv(body) {
|
|
702
|
+
let out = String(body);
|
|
703
|
+
out = out.replace(
|
|
704
|
+
/(\/\.\*localhost\.\*\/\.test\()([^)]+)(\))/g,
|
|
705
|
+
"($1$2$3||location.pathname.indexOf(\"/p/\")===0)"
|
|
706
|
+
);
|
|
707
|
+
out = out.replace(
|
|
708
|
+
/((?:window\.)?location\.hostname)\s*===\s*(['"])localhost\2/g,
|
|
709
|
+
"($1===$2localhost$2||location.pathname.indexOf(\"/p/\")===0)"
|
|
710
|
+
);
|
|
711
|
+
return out;
|
|
712
|
+
}
|
|
713
|
+
|
|
654
714
|
function prefixQuotedAssetPaths(body, pathPrefix) {
|
|
655
715
|
if (!pathPrefix) return body;
|
|
656
716
|
return String(body).replace(/(["'`])(\/(?!\/)[^"'`]*)/g, (full, q, path) => {
|
|
@@ -797,6 +857,7 @@ function prefixRootPaths(body, publicBase, contentType = "") {
|
|
|
797
857
|
const css = mime === "text/css" || (!isCode && !html && /css/.test(mime));
|
|
798
858
|
if (!html && !css) {
|
|
799
859
|
let code = rewriteViteBaseLiterals(body, pathPrefix);
|
|
860
|
+
code = rewriteShareAsLocalEnv(code);
|
|
800
861
|
code = prefixQuotedAssetPaths(code, pathPrefix);
|
|
801
862
|
code = code.replace(
|
|
802
863
|
/(?<![A-Za-z0-9])\/__nextjs_/g,
|
|
@@ -866,7 +927,7 @@ function isHtmlDocument(headers, body) {
|
|
|
866
927
|
|
|
867
928
|
function isScriptRequestPath(path) {
|
|
868
929
|
const p = String(path || "").split("?")[0]?.toLowerCase() || "";
|
|
869
|
-
return /\.(?:m?
|
|
930
|
+
return /\.(?:m?[jt]sx?|cjs)$/.test(p);
|
|
870
931
|
}
|
|
871
932
|
|
|
872
933
|
function isScriptOrJsonBody(headers, path) {
|
|
@@ -1017,6 +1078,21 @@ function shareProxyShim(p, portMap, rewriteMappedLocalUrls) {
|
|
|
1017
1078
|
} catch (e) {}
|
|
1018
1079
|
return v;
|
|
1019
1080
|
}
|
|
1081
|
+
function backendBase() {
|
|
1082
|
+
var map = portMap || {};
|
|
1083
|
+
var keys = Object.keys(map);
|
|
1084
|
+
for (var i = 0; i < keys.length; i++) {
|
|
1085
|
+
var url = String(map[keys[i]] || "").replace(/\/$/, "");
|
|
1086
|
+
if (!url) continue;
|
|
1087
|
+
try {
|
|
1088
|
+
var slug = new URL(url).pathname.split("/").filter(Boolean)[2] || "";
|
|
1089
|
+
if (slug === "backend" || slug.indexOf("backend-") === 0) return url;
|
|
1090
|
+
} catch (e) {
|
|
1091
|
+
if (/\/backend(?:-[^/]+)?$/i.test(url)) return url;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
return "";
|
|
1095
|
+
}
|
|
1020
1096
|
function addNav(v) {
|
|
1021
1097
|
if (typeof v !== "string" || !v) return v;
|
|
1022
1098
|
var mapped = mapLocal(v);
|
|
@@ -1038,10 +1114,27 @@ function shareProxyShim(p, portMap, rewriteMappedLocalUrls) {
|
|
|
1038
1114
|
if (mapped !== v) return mapped;
|
|
1039
1115
|
if (/^(https?:|wss?:)\/\//i.test(v)) {
|
|
1040
1116
|
try {
|
|
1041
|
-
var
|
|
1042
|
-
if (!loopback(
|
|
1117
|
+
var abs = new URL(v);
|
|
1118
|
+
if (!loopback(abs.hostname)) return v;
|
|
1043
1119
|
} catch (e) {}
|
|
1044
1120
|
}
|
|
1121
|
+
if (v.charAt(0) === "/" && v.charAt(1) !== "/") {
|
|
1122
|
+
var backend = backendBase();
|
|
1123
|
+
var path = v.split("?")[0];
|
|
1124
|
+
if (path === "/api/v1" || path.indexOf("/api/v1/") === 0 || path.indexOf("/p/") === 0) {
|
|
1125
|
+
return v;
|
|
1126
|
+
}
|
|
1127
|
+
if (
|
|
1128
|
+
backend &&
|
|
1129
|
+
!isAsset(path) &&
|
|
1130
|
+
(/^\/sreo(?:\/|$)/i.test(path) || /^\/api(?:\/|$)/i.test(path))
|
|
1131
|
+
) {
|
|
1132
|
+
try {
|
|
1133
|
+
var rel = new URL(v, location.href);
|
|
1134
|
+
return backend + rel.pathname + rel.search + rel.hash;
|
|
1135
|
+
} catch (e) {}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1045
1138
|
return addNav(v);
|
|
1046
1139
|
}
|
|
1047
1140
|
function mapSel(sel) {
|
|
@@ -1244,6 +1337,21 @@ function shareServiceWorkerMain(portMap, tokenRoot, rewriteMappedLocalUrls) {
|
|
|
1244
1337
|
h === "localhost" || h === "127.0.0.1" || h === "::1" || h === "0.0.0.0"
|
|
1245
1338
|
);
|
|
1246
1339
|
}
|
|
1340
|
+
function backendBase() {
|
|
1341
|
+
var map = portMap || {};
|
|
1342
|
+
var keys = Object.keys(map);
|
|
1343
|
+
for (var i = 0; i < keys.length; i++) {
|
|
1344
|
+
var url = String(map[keys[i]] || "").replace(/\/$/, "");
|
|
1345
|
+
if (!url) continue;
|
|
1346
|
+
try {
|
|
1347
|
+
var slug = new URL(url).pathname.split("/").filter(Boolean)[2] || "";
|
|
1348
|
+
if (slug === "backend" || slug.indexOf("backend-") === 0) return url;
|
|
1349
|
+
} catch (e) {
|
|
1350
|
+
if (/\/backend(?:-[^/]+)?$/i.test(url)) return url;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
return "";
|
|
1354
|
+
}
|
|
1247
1355
|
function prefixWith(url, prefix) {
|
|
1248
1356
|
if (!prefix) return url;
|
|
1249
1357
|
try {
|
|
@@ -1252,9 +1360,19 @@ function shareServiceWorkerMain(portMap, tokenRoot, rewriteMappedLocalUrls) {
|
|
|
1252
1360
|
if (!loopback(abs.hostname)) return url;
|
|
1253
1361
|
}
|
|
1254
1362
|
var u = new URL(url, self.location.href);
|
|
1363
|
+
var path = u.pathname || "/";
|
|
1364
|
+
var backend = backendBase();
|
|
1365
|
+
if (path === "/api/v1" || path.indexOf("/api/v1/") === 0) return url;
|
|
1366
|
+
if (path.indexOf("/p/") === 0) return url;
|
|
1367
|
+
if (
|
|
1368
|
+
backend &&
|
|
1369
|
+
!/^(https?:|wss?:)\/\//i.test(url) &&
|
|
1370
|
+
(/^\/sreo(?:\/|$)/i.test(path) || /^\/api(?:\/|$)/i.test(path))
|
|
1371
|
+
) {
|
|
1372
|
+
return backend + path + (u.search || "") + (u.hash || "");
|
|
1373
|
+
}
|
|
1255
1374
|
if (u.origin !== self.location.origin) return url;
|
|
1256
1375
|
if (u.pathname === "/" || u.pathname === "") return url;
|
|
1257
|
-
if (u.pathname.indexOf("/p/") === 0) return url;
|
|
1258
1376
|
if (u.pathname === prefix || u.pathname.indexOf(prefix + "/") === 0) return url;
|
|
1259
1377
|
u.pathname = u.pathname === "/" ? prefix : prefix + u.pathname;
|
|
1260
1378
|
return u.toString();
|
|
@@ -1420,7 +1538,7 @@ export function processShareHttpResponse(opts) {
|
|
|
1420
1538
|
text = prefixRootPaths(text, publicBase, headers["content-type"] || "");
|
|
1421
1539
|
mutated = true;
|
|
1422
1540
|
}
|
|
1423
|
-
if (Object.keys(portUrls).length && rewriteTextBody) {
|
|
1541
|
+
if (Object.keys(portUrls).length && rewriteTextBody && !isScriptOrJsonBody(headers, path)) {
|
|
1424
1542
|
const mapped = rewriteMappedLocalUrls(text, portUrls);
|
|
1425
1543
|
if (mapped !== text) {
|
|
1426
1544
|
text = mapped;
|
|
@@ -1428,7 +1546,7 @@ export function processShareHttpResponse(opts) {
|
|
|
1428
1546
|
}
|
|
1429
1547
|
}
|
|
1430
1548
|
if (publicBase && rewriteTextBody && !isScriptOrJsonBody(headers, path)) {
|
|
1431
|
-
const prefixed = rewriteShareOriginPaths(text, publicBase);
|
|
1549
|
+
const prefixed = rewriteShareOriginPaths(text, publicBase, portUrls);
|
|
1432
1550
|
if (prefixed !== text) {
|
|
1433
1551
|
text = prefixed;
|
|
1434
1552
|
mutated = true;
|