@maintainer-pro/ai-bridge 0.1.28 → 0.1.30
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 +169 -44
- package/src/share-rewrite.mjs +45 -19
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.30",
|
|
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.16",
|
|
32
32
|
"@maintainer-pro/ai-server": "^0.1.8"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/src/daemon.mjs
CHANGED
|
@@ -451,13 +451,30 @@ async function loadAiCli() {
|
|
|
451
451
|
/** @type {{ at: number, ids: string[] } | null} */
|
|
452
452
|
let cliProviderCache = null;
|
|
453
453
|
|
|
454
|
-
|
|
455
|
-
|
|
454
|
+
function missingCliSetupText(folder) {
|
|
455
|
+
const folderLine = folder
|
|
456
|
+
? `No coding agent CLI is available in this folder:\n ${folder}`
|
|
457
|
+
: "No coding agent CLI found on this computer.";
|
|
458
|
+
return [
|
|
459
|
+
`${folderLine} Chat will not work until one is installed and reachable from this folder.`,
|
|
460
|
+
"",
|
|
461
|
+
" • Cursor Agent CLI — https://cursor.com (command: agent)",
|
|
462
|
+
" • Claude Code — https://docs.anthropic.com/en/docs/claude-code (command: claude)",
|
|
463
|
+
" • Antigravity — https://antigravity.google/ (command: agy)",
|
|
464
|
+
"",
|
|
465
|
+
"Then either sign in and re-run the Maintainer Pro command from a terminal where that CLI works, or set the full path in this folder's .env:",
|
|
466
|
+
" AI_CLI_PROVIDER=cursor",
|
|
467
|
+
" AI_CLI_COMMAND=C:\\\\Users\\\\you\\\\AppData\\\\Local\\\\cursor-agent\\\\agent.cmd",
|
|
468
|
+
].join("\n");
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
async function detectCliProviders(force = false) {
|
|
472
|
+
if (!force && cliProviderCache && Date.now() - cliProviderCache.at < 60_000) {
|
|
456
473
|
return cliProviderCache.ids;
|
|
457
474
|
}
|
|
458
475
|
try {
|
|
459
|
-
const
|
|
460
|
-
const provider = await resolveProvider({ preference: "auto" });
|
|
476
|
+
const cli = await loadAiCli();
|
|
477
|
+
const provider = await cli.resolveProvider({ preference: "auto" });
|
|
461
478
|
cliProviderCache = { at: Date.now(), ids: [provider.id] };
|
|
462
479
|
return cliProviderCache.ids;
|
|
463
480
|
} catch {
|
|
@@ -466,6 +483,65 @@ async function detectCliProviders() {
|
|
|
466
483
|
}
|
|
467
484
|
}
|
|
468
485
|
|
|
486
|
+
async function withCliProbe(result, folders) {
|
|
487
|
+
const roots = [...new Set((folders || []).filter(Boolean).map((row) => path.resolve(row)))];
|
|
488
|
+
let probe = { available: false };
|
|
489
|
+
for (const folder of roots) {
|
|
490
|
+
probe = await probeFolderCli(folder);
|
|
491
|
+
if (probe.available) break;
|
|
492
|
+
}
|
|
493
|
+
const folder = probe.folder || roots[0] || result.folderPath;
|
|
494
|
+
if (probe.available) {
|
|
495
|
+
log(
|
|
496
|
+
`coding agent CLI ready in ${folder} (${probe.providerId || "auto"}${
|
|
497
|
+
probe.source ? ` via ${probe.source}` : ""
|
|
498
|
+
})`
|
|
499
|
+
);
|
|
500
|
+
} else if (folder) {
|
|
501
|
+
activity(
|
|
502
|
+
result.sandboxId,
|
|
503
|
+
"warn",
|
|
504
|
+
`No coding agent CLI in ${folder}. Chat needs agent, claude, or agy — or AI_CLI_COMMAND in this folder's .env.`
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
return {
|
|
508
|
+
...result,
|
|
509
|
+
missingCli: !probe.available,
|
|
510
|
+
cliProvider: probe.providerId || null,
|
|
511
|
+
cliCommand: probe.command || null,
|
|
512
|
+
warning: !probe.available
|
|
513
|
+
? [result.warning, missingCliSetupText(folder)].filter(Boolean).join("\n\n")
|
|
514
|
+
: result.warning,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
async function probeFolderCli(folder) {
|
|
519
|
+
const root = folder ? path.resolve(folder) : "";
|
|
520
|
+
if (!root) return { available: false };
|
|
521
|
+
try {
|
|
522
|
+
const cli = await loadAiCli();
|
|
523
|
+
if (typeof cli.probeCliInFolder === "function") {
|
|
524
|
+
return await cli.probeCliInFolder(root);
|
|
525
|
+
}
|
|
526
|
+
const provider = await cli.resolveProvider({
|
|
527
|
+
preference: "auto",
|
|
528
|
+
workspaceDir: root,
|
|
529
|
+
});
|
|
530
|
+
return { available: true, providerId: provider.id, folder: root };
|
|
531
|
+
} catch {
|
|
532
|
+
return { available: false, folder: root };
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async function warnIfMissingCli() {
|
|
537
|
+
const ids = await detectCliProviders(true);
|
|
538
|
+
if (ids.length) {
|
|
539
|
+
log(`coding agent CLI ready (${ids.join(", ")})`);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
warn(missingCliSetupText());
|
|
543
|
+
}
|
|
544
|
+
|
|
469
545
|
async function resolveWorkspaceHostApps(ws, opts = {}) {
|
|
470
546
|
const folders = workspaceFolders(ws);
|
|
471
547
|
const folder = folders[0] || path.resolve(ws.folderPath || "");
|
|
@@ -3714,11 +3790,18 @@ async function startAiServerForWorkspace(ws, opts = {}) {
|
|
|
3714
3790
|
|
|
3715
3791
|
const storeEnv = await loadSandboxStoreEnv(ws, cfg);
|
|
3716
3792
|
const folderEnv = readProjectEnvValues(folder);
|
|
3793
|
+
const cliProbe = await probeFolderCli(folder);
|
|
3717
3794
|
const dataDir = dataDirFor(folder, ws.sandboxId);
|
|
3718
3795
|
const instanceEnv = {
|
|
3719
3796
|
...folderEnv,
|
|
3720
3797
|
...overrideEnv,
|
|
3721
3798
|
...storeEnv,
|
|
3799
|
+
...(cliProbe.command && !folderEnv.AI_CLI_COMMAND
|
|
3800
|
+
? { AI_CLI_COMMAND: cliProbe.command }
|
|
3801
|
+
: {}),
|
|
3802
|
+
...(cliProbe.providerId && !folderEnv.AI_CLI_PROVIDER
|
|
3803
|
+
? { AI_CLI_PROVIDER: cliProbe.providerId }
|
|
3804
|
+
: {}),
|
|
3722
3805
|
AI_SERVER_URL: localAi,
|
|
3723
3806
|
CORS_ORIGIN: overrideEnv.CORS_ORIGIN || folderEnv.CORS_ORIGIN || "",
|
|
3724
3807
|
CORS_ORIGINS: overrideEnv.CORS_ORIGINS || folderEnv.CORS_ORIGINS || "",
|
|
@@ -4448,7 +4531,7 @@ async function setupWorkspace(cfg, action) {
|
|
|
4448
4531
|
const host = workspaceHostReport(existingWs);
|
|
4449
4532
|
const aiServerUp = await isChatServerOnPort(existingWs.port);
|
|
4450
4533
|
log(`setup added folder ${resolved} sandbox=${shortId(sandboxId)}`);
|
|
4451
|
-
return {
|
|
4534
|
+
return await withCliProbe({
|
|
4452
4535
|
sandboxId,
|
|
4453
4536
|
folderPath: existingWs.folderPath,
|
|
4454
4537
|
extraFolders: extra,
|
|
@@ -4476,7 +4559,9 @@ async function setupWorkspace(cfg, action) {
|
|
|
4476
4559
|
reasons: ports.reasons || [],
|
|
4477
4560
|
projectInfo: existingWs.projectInfo || null,
|
|
4478
4561
|
ignorePaths: access.ignorePaths,
|
|
4479
|
-
}
|
|
4562
|
+
},
|
|
4563
|
+
workspaceFolders(existingWs)
|
|
4564
|
+
);
|
|
4480
4565
|
}
|
|
4481
4566
|
|
|
4482
4567
|
const client = configureClient({
|
|
@@ -4566,32 +4651,35 @@ async function setupWorkspace(cfg, action) {
|
|
|
4566
4651
|
);
|
|
4567
4652
|
|
|
4568
4653
|
const host = workspaceHostReport(entry);
|
|
4569
|
-
return
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4654
|
+
return await withCliProbe(
|
|
4655
|
+
{
|
|
4656
|
+
sandboxId,
|
|
4657
|
+
folderPath: entry.folderPath,
|
|
4658
|
+
extraFolders: entry.extraFolders || [],
|
|
4659
|
+
addFolder: false,
|
|
4660
|
+
port: entry.port,
|
|
4661
|
+
appUrl: host.appUrl || entry.appUrl || appUrl,
|
|
4662
|
+
origins: host.origins.length
|
|
4663
|
+
? host.origins
|
|
4664
|
+
: [...new Set([corsOrigin, aiOrigin, entry.appUrl].filter(Boolean))],
|
|
4665
|
+
wroteEnv: false,
|
|
4666
|
+
clientKind: client.kind,
|
|
4667
|
+
clientFiles: client.filesWritten,
|
|
4668
|
+
clientNotes: client.notes,
|
|
4669
|
+
aiServerUp,
|
|
4670
|
+
startedHosts: [],
|
|
4671
|
+
openUrl,
|
|
4672
|
+
processIssues,
|
|
4673
|
+
warning,
|
|
4674
|
+
waitingForStart,
|
|
4675
|
+
needsReview,
|
|
4676
|
+
hostApps: ports.apps || entry.hostApps || [],
|
|
4677
|
+
reasons: ports.reasons || [],
|
|
4678
|
+
projectInfo,
|
|
4679
|
+
ignorePaths: access.ignorePaths,
|
|
4680
|
+
},
|
|
4681
|
+
workspaceFolders(entry)
|
|
4682
|
+
);
|
|
4595
4683
|
}
|
|
4596
4684
|
|
|
4597
4685
|
async function proposeSetupForWorkspace(ws, cfg, opts = {}) {
|
|
@@ -4738,16 +4826,28 @@ async function runActions(cfg, actions) {
|
|
|
4738
4826
|
};
|
|
4739
4827
|
}
|
|
4740
4828
|
} else if (action.code === "recheck") {
|
|
4829
|
+
cliProviderCache = null;
|
|
4741
4830
|
const sandboxId = action.sandboxId || action.payload?.sandboxId;
|
|
4742
4831
|
const ws = (cfg.workspaces || []).find((w) => w.sandboxId === sandboxId);
|
|
4743
4832
|
if (!ws) {
|
|
4744
4833
|
log(`${label} recheck: no local workspace`);
|
|
4745
4834
|
}
|
|
4835
|
+
const cli = await detectCliProviders(true);
|
|
4836
|
+
const folderProbe = ws
|
|
4837
|
+
? await withCliProbe(
|
|
4838
|
+
{ sandboxId, folderPath: ws.folderPath },
|
|
4839
|
+
workspaceFolders(ws)
|
|
4840
|
+
)
|
|
4841
|
+
: { missingCli: cli.length === 0 };
|
|
4746
4842
|
const hostApps = ws
|
|
4747
4843
|
? await resolveWorkspaceHostApps(ws, { cfg, force: true, allowAi: false })
|
|
4748
4844
|
: null;
|
|
4749
4845
|
result = {
|
|
4750
4846
|
recheckedAt: new Date().toISOString(),
|
|
4847
|
+
cliProviders: cli,
|
|
4848
|
+
missingCli: folderProbe.missingCli === true,
|
|
4849
|
+
cliProvider: folderProbe.cliProvider || null,
|
|
4850
|
+
cliCommand: folderProbe.cliCommand || null,
|
|
4751
4851
|
hostApps: hostApps?.apps || [],
|
|
4752
4852
|
projectInfo: ws?.projectInfo || null,
|
|
4753
4853
|
};
|
|
@@ -5216,18 +5316,41 @@ function adminWsUrl(adminUrl, token) {
|
|
|
5216
5316
|
async function buildIssues(cfg, workspaceStates) {
|
|
5217
5317
|
/** @type {Array<Record<string, unknown>>} */
|
|
5218
5318
|
const issues = [];
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5319
|
+
if (workspaceStates.length) {
|
|
5320
|
+
for (const st of workspaceStates) {
|
|
5321
|
+
const ws = (cfg.workspaces || []).find((row) => row.sandboxId === st.sandboxId);
|
|
5322
|
+
const folders = ws ? workspaceFolders(ws) : [st.folderPath].filter(Boolean);
|
|
5323
|
+
let probe = { available: false, folder: folders[0] || st.folderPath };
|
|
5324
|
+
for (const folder of folders) {
|
|
5325
|
+
probe = await probeFolderCli(folder);
|
|
5326
|
+
if (probe.available) break;
|
|
5327
|
+
}
|
|
5328
|
+
if (probe.available) continue;
|
|
5329
|
+
issues.push({
|
|
5330
|
+
code: "missing_cli",
|
|
5331
|
+
severity: "error",
|
|
5332
|
+
title: "No coding agent CLI in this folder",
|
|
5333
|
+
message: missingCliSetupText(probe.folder || folders[0] || st.folderPath),
|
|
5334
|
+
resolution:
|
|
5335
|
+
"Install a CLI and re-run the Maintainer Pro command from a terminal where it works, or set AI_CLI_COMMAND in this folder's .env, then Check again.",
|
|
5336
|
+
actionCode: "recheck",
|
|
5337
|
+
sandboxId: st.sandboxId,
|
|
5338
|
+
});
|
|
5339
|
+
}
|
|
5340
|
+
} else {
|
|
5341
|
+
const cli = await detectCliProviders();
|
|
5342
|
+
if (!cli.length) {
|
|
5343
|
+
issues.push({
|
|
5344
|
+
code: "missing_cli",
|
|
5345
|
+
severity: "error",
|
|
5346
|
+
title: "No coding agent CLI found",
|
|
5347
|
+
message: missingCliSetupText(),
|
|
5348
|
+
resolution:
|
|
5349
|
+
"Install Cursor (agent), Claude Code (claude), or Antigravity (agy), sign in, then stop and re-run the Maintainer Pro command. Use Check again after it is on PATH.",
|
|
5350
|
+
actionCode: "recheck",
|
|
5351
|
+
sandboxId: null,
|
|
5352
|
+
});
|
|
5353
|
+
}
|
|
5231
5354
|
}
|
|
5232
5355
|
for (const st of workspaceStates) {
|
|
5233
5356
|
if (st.aiServerUp || st.appsRunning || st.startingAi) continue;
|
|
@@ -5377,6 +5500,7 @@ async function main() {
|
|
|
5377
5500
|
log(
|
|
5378
5501
|
`online via websocket (presence every ${HEARTBEAT_MS / 1000}s, ping every ${WS_PING_MS / 1000}s; reconnects until stopped)`
|
|
5379
5502
|
);
|
|
5503
|
+
await warnIfMissingCli();
|
|
5380
5504
|
await restoreHostsAfterReconnect(cfg);
|
|
5381
5505
|
|
|
5382
5506
|
/** @type {unknown[]} */
|
|
@@ -5391,6 +5515,7 @@ async function main() {
|
|
|
5391
5515
|
const batch = claimedActions.splice(0, claimedActions.length);
|
|
5392
5516
|
await runActions(cfg, batch);
|
|
5393
5517
|
}
|
|
5518
|
+
await sendPresenceOverWs();
|
|
5394
5519
|
} catch (err) {
|
|
5395
5520
|
warn(err instanceof Error ? err.message : String(err));
|
|
5396
5521
|
} finally {
|
package/src/share-rewrite.mjs
CHANGED
|
@@ -150,15 +150,22 @@ export function shouldProcessShareResponse(headers) {
|
|
|
150
150
|
return true;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
function
|
|
154
|
-
const
|
|
153
|
+
function hasContentHash(path) {
|
|
154
|
+
const base = String(path || "").split("?")[0].split("/").pop() || "";
|
|
155
155
|
return (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
/\.[0-9a-f]{8,}\.(?:js|css|woff2?)$/i.test(base) ||
|
|
157
|
+
/^[0-9a-f]{8,}\.(?:js|css|woff2?)$/i.test(base) ||
|
|
158
|
+
/-[0-9a-f]{8,}[^/]*\.(?:js|css)$/i.test(base)
|
|
159
159
|
);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
/** Production hashed assets only. Next/Vite dev chunks reuse the same URL. */
|
|
163
|
+
function isImmutableAssetPath(path) {
|
|
164
|
+
const p = String(path || "").split("?")[0] || "";
|
|
165
|
+
if (/\/__nextjs_font\//.test(p)) return true;
|
|
166
|
+
return hasContentHash(p);
|
|
167
|
+
}
|
|
168
|
+
|
|
162
169
|
function isViteDevSourcePath(path) {
|
|
163
170
|
const raw = String(path || "");
|
|
164
171
|
const p = raw.split("?")[0] || "";
|
|
@@ -184,12 +191,12 @@ function isDocumentPath(path) {
|
|
|
184
191
|
|
|
185
192
|
function isNoStorePath(path) {
|
|
186
193
|
const p = String(path || "").split("?")[0] || "";
|
|
194
|
+
if (isImmutableAssetPath(path) || isCacheableMediaPath(path)) return false;
|
|
187
195
|
return (
|
|
188
196
|
/\/embed-config\.js$/i.test(p) ||
|
|
189
197
|
p === "/api" ||
|
|
190
198
|
p.startsWith("/api/") ||
|
|
191
|
-
/\/_next
|
|
192
|
-
/\/_next\/webpack\/hmr/i.test(p) ||
|
|
199
|
+
/\/_next\//i.test(p) ||
|
|
193
200
|
/\/__nextjs_original-stack-frames/i.test(p) ||
|
|
194
201
|
/\/__mp\//.test(p) ||
|
|
195
202
|
isViteDevSourcePath(path) ||
|
|
@@ -241,9 +248,9 @@ function dropHopCacheNoise(headers) {
|
|
|
241
248
|
}
|
|
242
249
|
|
|
243
250
|
/**
|
|
244
|
-
* Cache-Control for the share URL.
|
|
245
|
-
*
|
|
246
|
-
*
|
|
251
|
+
* Cache-Control for the share URL. Only content-hashed assets are immutable.
|
|
252
|
+
* Next/Vite dev chunks reuse URLs, so they must not be cached or HMR/hydration
|
|
253
|
+
* breaks. HTML, RSC, and HMR stay no-store.
|
|
247
254
|
*
|
|
248
255
|
* @param {Record<string, string>} headers
|
|
249
256
|
* @param {string} path
|
|
@@ -485,8 +492,11 @@ export function prefixShareOriginUrl(value, publicBase) {
|
|
|
485
492
|
const basePort = base.port || (base.protocol === "https:" ? "443" : "80");
|
|
486
493
|
if (reqPort !== basePort) return value;
|
|
487
494
|
const path = u.pathname || "/";
|
|
488
|
-
if (path === "/" || path === "") return value;
|
|
489
495
|
if (path === prefix || path.indexOf(prefix + "/") === 0) return u.toString();
|
|
496
|
+
if (path === "/" || path === "") {
|
|
497
|
+
u.pathname = prefix || "/";
|
|
498
|
+
return u.toString();
|
|
499
|
+
}
|
|
490
500
|
if (path.indexOf("/p/") === 0) return u.toString();
|
|
491
501
|
u.pathname = prefix + path;
|
|
492
502
|
return u.toString();
|
|
@@ -722,11 +732,11 @@ function rewriteShareAsLocalEnv(body) {
|
|
|
722
732
|
let out = String(body);
|
|
723
733
|
out = out.replace(
|
|
724
734
|
/(\/\.\*localhost\.\*\/\.test\()([^)]+)(\))/g,
|
|
725
|
-
"($1$2$3||
|
|
735
|
+
"($1$2$3||window.__MP_SHARE_PREFIX__)"
|
|
726
736
|
);
|
|
727
737
|
out = out.replace(
|
|
728
738
|
/((?:window\.)?location\.hostname)\s*===\s*(['"])localhost\2/g,
|
|
729
|
-
"($1===$2localhost$2||
|
|
739
|
+
"($1===$2localhost$2||window.__MP_SHARE_PREFIX__)"
|
|
730
740
|
);
|
|
731
741
|
return out;
|
|
732
742
|
}
|
|
@@ -1026,6 +1036,7 @@ function injectMaintainerProEmbed(html, aiPublicBase) {
|
|
|
1026
1036
|
function shareProxyShim(p, portMap, rewriteMappedLocalUrls, bypassCors) {
|
|
1027
1037
|
if (!p || window.__MP_SHARE_SHIM__) return;
|
|
1028
1038
|
window.__MP_SHARE_SHIM__ = 1;
|
|
1039
|
+
window.__MP_SHARE_PREFIX__ = p;
|
|
1029
1040
|
window.__MP_PORT_MAP__ = portMap || {};
|
|
1030
1041
|
window.__MP_BYPASS_CORS__ = Boolean(bypassCors);
|
|
1031
1042
|
function rewriteText(text) {
|
|
@@ -1121,10 +1132,8 @@ function shareProxyShim(p, portMap, rewriteMappedLocalUrls, bypassCors) {
|
|
|
1121
1132
|
if (v.charAt(0) === "/" && v.charAt(1) !== "/") return prefixPath(v);
|
|
1122
1133
|
try {
|
|
1123
1134
|
var nav = new URL(v, location.href);
|
|
1124
|
-
if (!loopback(nav.hostname)) return v;
|
|
1125
|
-
|
|
1126
|
-
if (nav.pathname === "/" || nav.pathname === "") return v;
|
|
1127
|
-
nav.pathname = prefixPath(nav.pathname);
|
|
1135
|
+
if (!loopback(nav.hostname) && nav.origin !== location.origin) return v;
|
|
1136
|
+
nav.pathname = prefixPath(nav.pathname || "/");
|
|
1128
1137
|
return nav.toString();
|
|
1129
1138
|
} catch (e) {}
|
|
1130
1139
|
return v;
|
|
@@ -1363,6 +1372,24 @@ function shareProxyShim(p, portMap, rewriteMappedLocalUrls, bypassCors) {
|
|
|
1363
1372
|
});
|
|
1364
1373
|
}
|
|
1365
1374
|
} catch (e) {}
|
|
1375
|
+
document.addEventListener(
|
|
1376
|
+
"click",
|
|
1377
|
+
function (e) {
|
|
1378
|
+
if (e.defaultPrevented || e.button !== 0) return;
|
|
1379
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
1380
|
+
var a = e.target && e.target.closest ? e.target.closest("a[href]") : null;
|
|
1381
|
+
if (!a || (a.target && a.target !== "" && a.target !== "_self")) return;
|
|
1382
|
+
if (a.hasAttribute("download")) return;
|
|
1383
|
+
var href = a.getAttribute("href");
|
|
1384
|
+
if (!href || href.charAt(0) === "#") return;
|
|
1385
|
+
if (/^(mailto|tel|javascript):/i.test(href)) return;
|
|
1386
|
+
var next = addNav(href);
|
|
1387
|
+
if (!next || next === href) return;
|
|
1388
|
+
e.preventDefault();
|
|
1389
|
+
location.assign(next);
|
|
1390
|
+
},
|
|
1391
|
+
true
|
|
1392
|
+
);
|
|
1366
1393
|
try {
|
|
1367
1394
|
var tokenRoot = p.replace(/\/[^/]+$/, "/");
|
|
1368
1395
|
if (navigator.serviceWorker && tokenRoot.indexOf("/p/") === 0) {
|
|
@@ -1486,9 +1513,8 @@ function shareServiceWorkerMain(portMap, tokenRoot, rewriteMappedLocalUrls, bypa
|
|
|
1486
1513
|
return backend + path + (u.search || "") + (u.hash || "");
|
|
1487
1514
|
}
|
|
1488
1515
|
if (u.origin !== self.location.origin) return url;
|
|
1489
|
-
if (u.pathname === "/" || u.pathname === "") return url;
|
|
1490
1516
|
if (u.pathname === prefix || u.pathname.indexOf(prefix + "/") === 0) return url;
|
|
1491
|
-
u.pathname = u.pathname === "/" ? prefix : prefix + u.pathname;
|
|
1517
|
+
u.pathname = u.pathname === "/" || u.pathname === "" ? prefix : prefix + u.pathname;
|
|
1492
1518
|
return u.toString();
|
|
1493
1519
|
} catch (e) {}
|
|
1494
1520
|
return url;
|