github-router 0.3.73 → 0.3.74
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/main.js +208 -18
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "github-router browser bridge",
|
|
4
4
|
"short_name": "gh-router-browser",
|
|
5
|
-
"version": "0.3.
|
|
5
|
+
"version": "0.3.74",
|
|
6
6
|
"description": "Bridge between Claude (via github-router /mcp) and the browser. Implements tab control, navigation, clicks, form fill, downloads, screenshots, devtools eval. Blocks navigation to chrome://settings.",
|
|
7
7
|
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqJElxuBlonBS3TVW9FJN0mGTtShB3L1hoaYf6k39SOr1ogGYmF90EjRxy1i21k9wQQjPf26bcBu/9X67KrQjQV0uB38CaNukgiSeoLjfptN811u+PJHx6BP+jx3Qa6/3VenNPxHC8WEU0GXql8QSjIHEyCwKb6fMASXOK94JyB5Ywov2x8mt/+9ncqBBBMVzf6r5Sagy4PL1XnryLsuADD/vOEkPet8wXgH/Oj7v5tTsQQZ7U1JT51PoDs2BFnXc5v3TkVgZwd32k3ONh+nkDw1Hof+4zwUGOyJE6eMrlYzRlKM4Qxdf9JpavQvqfieAbTRWcyKeclnHeoIfE7cDBQIDAQAB",
|
|
8
8
|
"background": {
|
package/dist/main.js
CHANGED
|
@@ -13,7 +13,7 @@ import * as path$1 from "node:path";
|
|
|
13
13
|
import path, { dirname, join } from "node:path";
|
|
14
14
|
import process$1 from "node:process";
|
|
15
15
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
16
|
-
import fs$1, { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
16
|
+
import fs$1, { chmodSync, closeSync, cpSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
import { performance } from "node:perf_hooks";
|
|
19
19
|
import { createInterface } from "node:readline";
|
|
@@ -5674,33 +5674,61 @@ function packageRoot() {
|
|
|
5674
5674
|
} catch {}
|
|
5675
5675
|
return process$1?.cwd?.() ?? ".";
|
|
5676
5676
|
}
|
|
5677
|
+
function fileExists(p) {
|
|
5678
|
+
return existsSync(p);
|
|
5679
|
+
}
|
|
5680
|
+
/** Stable materialized extension dir: `<APP_DIR>/browser-ext`. */
|
|
5681
|
+
function stableExtensionDir() {
|
|
5682
|
+
return path.join(PATHS.APP_DIR, "browser-ext");
|
|
5683
|
+
}
|
|
5684
|
+
/** Stable materialized bridge bundle: `<APP_DIR>/browser-bridge/index.js`. */
|
|
5685
|
+
function stableBridgeBundlePath() {
|
|
5686
|
+
return path.join(PATHS.APP_DIR, "browser-bridge", "index.js");
|
|
5687
|
+
}
|
|
5677
5688
|
/**
|
|
5678
|
-
*
|
|
5689
|
+
* The bundled (shipped) extension dir — the SOURCE for provisioning,
|
|
5690
|
+
* never the runtime load path. Layouts:
|
|
5679
5691
|
*
|
|
5680
5692
|
* - Installed via npm: `<package>/dist/browser-ext/` (the published
|
|
5681
5693
|
* tarball ships only `dist/`, see package.json "files"). The build
|
|
5682
|
-
* step copies `src/browser-ext/` → `dist/browser-ext
|
|
5683
|
-
*
|
|
5684
|
-
*
|
|
5685
|
-
|
|
5694
|
+
* step copies `src/browser-ext/` → `dist/browser-ext/`.
|
|
5695
|
+
* - Running from this repo: `dist/browser-ext/` if built, else
|
|
5696
|
+
* `src/browser-ext/` for fresh-clone-pre-build.
|
|
5697
|
+
*/
|
|
5698
|
+
function bundledExtensionDir() {
|
|
5699
|
+
const root = packageRoot();
|
|
5700
|
+
const distExt = path.join(root, "dist", "browser-ext");
|
|
5701
|
+
if (fileExists(path.join(distExt, "manifest.json"))) return distExt;
|
|
5702
|
+
return path.join(root, "src", "browser-ext");
|
|
5703
|
+
}
|
|
5704
|
+
/** The bundled (shipped) bridge entrypoint — SOURCE for provisioning. */
|
|
5705
|
+
function bundledBridgeBundlePath() {
|
|
5706
|
+
return path.join(packageRoot(), "dist", "browser-bridge", "index.js");
|
|
5707
|
+
}
|
|
5708
|
+
/**
|
|
5709
|
+
* Runtime extension directory — the path Chrome "Load unpacked"s and the
|
|
5710
|
+
* path the NMH manifest's stable-id derivation reads. Resolution order:
|
|
5686
5711
|
*
|
|
5687
|
-
*
|
|
5688
|
-
*
|
|
5689
|
-
*
|
|
5712
|
+
* 1. `GH_ROUTER_BROWSER_EXT_DIR=<abs path>` dev override (lets you
|
|
5713
|
+
* point at a working copy you're editing without rebuilding).
|
|
5714
|
+
* 2. The stable materialized copy under `<APP_DIR>` if present.
|
|
5715
|
+
* 3. The bundled dir (dist, then src) as the pre-provision fallback.
|
|
5690
5716
|
*/
|
|
5691
5717
|
function extensionDir() {
|
|
5692
5718
|
const override = process$1.env.GH_ROUTER_BROWSER_EXT_DIR;
|
|
5693
5719
|
if (override && override.length > 0) return override;
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
try {
|
|
5697
|
-
if (readFileSync(path.join(distExt, "manifest.json")).length > 0) return distExt;
|
|
5698
|
-
} catch {}
|
|
5699
|
-
return path.join(root, "src", "browser-ext");
|
|
5720
|
+
if (fileExists(path.join(stableExtensionDir(), "manifest.json"))) return stableExtensionDir();
|
|
5721
|
+
return bundledExtensionDir();
|
|
5700
5722
|
}
|
|
5701
|
-
/**
|
|
5723
|
+
/**
|
|
5724
|
+
* Runtime bridge bundle path — what the launcher shim invokes. Prefers
|
|
5725
|
+
* the stable materialized copy; falls back to the bundled bundle when
|
|
5726
|
+
* provisioning hasn't run yet (or couldn't, on a fresh unbuilt checkout).
|
|
5727
|
+
*/
|
|
5702
5728
|
function bridgeBundlePath() {
|
|
5703
|
-
|
|
5729
|
+
const stable = stableBridgeBundlePath();
|
|
5730
|
+
if (fileExists(stable)) return stable;
|
|
5731
|
+
return bundledBridgeBundlePath();
|
|
5704
5732
|
}
|
|
5705
5733
|
function appBrowserMcpDir() {
|
|
5706
5734
|
const dir = path.join(PATHS.APP_DIR, "browser-mcp");
|
|
@@ -5828,6 +5856,164 @@ function installNativeHostForAll(browsers) {
|
|
|
5828
5856
|
return results;
|
|
5829
5857
|
}
|
|
5830
5858
|
|
|
5859
|
+
//#endregion
|
|
5860
|
+
//#region src/lib/browser-mcp/provision.ts
|
|
5861
|
+
/** Sidecar holding the content signature of the last materialized copy. */
|
|
5862
|
+
const SIGNATURE_FILE = ".provisioned";
|
|
5863
|
+
/** Source files excluded from both the copy and the content signature. */
|
|
5864
|
+
const EXCLUDED_FILES = new Set(["README.md", SIGNATURE_FILE]);
|
|
5865
|
+
let _provisioned = false;
|
|
5866
|
+
let _inFlight;
|
|
5867
|
+
/**
|
|
5868
|
+
* Materialize the extension + bridge into the stable app-dir and stamp
|
|
5869
|
+
* the running version. Single-flight + once-guarded so the startup fire-
|
|
5870
|
+
* and-forget and the lazy install-check call collapse to one run per
|
|
5871
|
+
* process. Resolves (never rejects) regardless of outcome.
|
|
5872
|
+
*/
|
|
5873
|
+
function provisionBrowserAssets() {
|
|
5874
|
+
if (_provisioned) return Promise.resolve();
|
|
5875
|
+
if (_inFlight) return _inFlight;
|
|
5876
|
+
_inFlight = _provisionImpl().finally(() => {
|
|
5877
|
+
_inFlight = void 0;
|
|
5878
|
+
});
|
|
5879
|
+
return _inFlight;
|
|
5880
|
+
}
|
|
5881
|
+
async function _provisionImpl() {
|
|
5882
|
+
try {
|
|
5883
|
+
if (process.env.GH_ROUTER_DISABLE_BROWSER_PROVISION === "1") return;
|
|
5884
|
+
const srcExtDir = bundledExtensionDir();
|
|
5885
|
+
const srcBridge = bundledBridgeBundlePath();
|
|
5886
|
+
if (!existsSync(srcBridge)) return;
|
|
5887
|
+
const destExtDir = stableExtensionDir();
|
|
5888
|
+
const destBridge = stableBridgeBundlePath();
|
|
5889
|
+
const sigPath = path.join(destExtDir, SIGNATURE_FILE);
|
|
5890
|
+
const signature = computeSignature(srcExtDir, srcBridge);
|
|
5891
|
+
const upToDate = existsSync(path.join(destExtDir, "manifest.json")) && existsSync(destBridge) && readSignature(sigPath) === signature;
|
|
5892
|
+
let fullySynced = true;
|
|
5893
|
+
if (!upToDate) {
|
|
5894
|
+
materializeExtension(srcExtDir, destExtDir);
|
|
5895
|
+
const stampOk = stampVersion(destExtDir);
|
|
5896
|
+
const bridgeUpdated = tryMaterializeBridge(srcBridge, destBridge);
|
|
5897
|
+
if (stampOk && bridgeUpdated) writeSignature(sigPath, signature);
|
|
5898
|
+
else fullySynced = false;
|
|
5899
|
+
}
|
|
5900
|
+
let hostOk = true;
|
|
5901
|
+
try {
|
|
5902
|
+
const browsers = detectSupportedBrowsers();
|
|
5903
|
+
if (browsers.length > 0) installNativeHostForAll(browsers);
|
|
5904
|
+
} catch (err) {
|
|
5905
|
+
hostOk = false;
|
|
5906
|
+
consola.debug("[browser-mcp] native-host install during provision failed:", err);
|
|
5907
|
+
}
|
|
5908
|
+
if (fullySynced && hostOk) _provisioned = true;
|
|
5909
|
+
} catch (err) {
|
|
5910
|
+
consola.debug("[browser-mcp] provisionBrowserAssets failed:", err);
|
|
5911
|
+
}
|
|
5912
|
+
}
|
|
5913
|
+
function computeSignature(srcExtDir, srcBridge) {
|
|
5914
|
+
const h = createHash("sha256");
|
|
5915
|
+
let names;
|
|
5916
|
+
try {
|
|
5917
|
+
names = readdirSync(srcExtDir).filter((n) => !EXCLUDED_FILES.has(n)).sort();
|
|
5918
|
+
} catch {
|
|
5919
|
+
names = [];
|
|
5920
|
+
}
|
|
5921
|
+
for (const name$1 of names) {
|
|
5922
|
+
h.update(name$1);
|
|
5923
|
+
try {
|
|
5924
|
+
h.update(readFileSync(path.join(srcExtDir, name$1)));
|
|
5925
|
+
} catch {
|
|
5926
|
+
h.update(`\x00unreadable:${name$1}\x00`);
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5929
|
+
h.update("bridge");
|
|
5930
|
+
try {
|
|
5931
|
+
h.update(readFileSync(srcBridge));
|
|
5932
|
+
} catch {
|
|
5933
|
+
h.update("\0missing:bridge\0");
|
|
5934
|
+
}
|
|
5935
|
+
h.update(`\x00version:${getPackageVersion()}\x00`);
|
|
5936
|
+
return h.digest("hex");
|
|
5937
|
+
}
|
|
5938
|
+
function readSignature(sigPath) {
|
|
5939
|
+
try {
|
|
5940
|
+
return readFileSync(sigPath, "utf8").trim();
|
|
5941
|
+
} catch {
|
|
5942
|
+
return;
|
|
5943
|
+
}
|
|
5944
|
+
}
|
|
5945
|
+
function writeSignature(sigPath, signature) {
|
|
5946
|
+
writeFileSync(sigPath, signature, "utf8");
|
|
5947
|
+
}
|
|
5948
|
+
/**
|
|
5949
|
+
* Copy the bundled extension dir into the stable dir, overwriting in
|
|
5950
|
+
* place. README and our sidecar are filtered out. We do NOT prune extra
|
|
5951
|
+
* files in the destination (a stale file left from an older version is
|
|
5952
|
+
* harmless — Chrome loads only what the manifest references).
|
|
5953
|
+
*/
|
|
5954
|
+
function materializeExtension(srcDir, destDir) {
|
|
5955
|
+
mkdirSync(destDir, { recursive: true });
|
|
5956
|
+
cpSync(srcDir, destDir, {
|
|
5957
|
+
recursive: true,
|
|
5958
|
+
force: true,
|
|
5959
|
+
filter: (s) => !EXCLUDED_FILES.has(path.basename(s))
|
|
5960
|
+
});
|
|
5961
|
+
}
|
|
5962
|
+
/**
|
|
5963
|
+
* Copy the bridge bundle into the stable path via temp-write + atomic
|
|
5964
|
+
* rename. Returns true on update; false when the destination couldn't be
|
|
5965
|
+
* replaced but a usable stable bridge already exists (e.g. Windows
|
|
5966
|
+
* EBUSY because a bridge process holds the old file — acceptable, the
|
|
5967
|
+
* old bridge is the version about to be reloaded). Throws only when there
|
|
5968
|
+
* is no usable bridge at all.
|
|
5969
|
+
*/
|
|
5970
|
+
function tryMaterializeBridge(srcBridge, destBridge) {
|
|
5971
|
+
mkdirSync(path.dirname(destBridge), { recursive: true });
|
|
5972
|
+
const tmp = `${destBridge}.tmp-${process.pid}`;
|
|
5973
|
+
try {
|
|
5974
|
+
writeFileSync(tmp, readFileSync(srcBridge));
|
|
5975
|
+
renameSync(tmp, destBridge);
|
|
5976
|
+
return true;
|
|
5977
|
+
} catch (err) {
|
|
5978
|
+
try {
|
|
5979
|
+
rmSync(tmp, { force: true });
|
|
5980
|
+
} catch {}
|
|
5981
|
+
if (existsSync(destBridge)) {
|
|
5982
|
+
consola.debug("[browser-mcp] bridge update deferred (file in use?):", err);
|
|
5983
|
+
return false;
|
|
5984
|
+
}
|
|
5985
|
+
throw err;
|
|
5986
|
+
}
|
|
5987
|
+
}
|
|
5988
|
+
/**
|
|
5989
|
+
* Stamp the running proxy version into the materialized manifest — the
|
|
5990
|
+
* single place the version is set on launch. Returns true when the
|
|
5991
|
+
* manifest is in its intended end state (stamped, already-correct, or
|
|
5992
|
+
* deliberately left at the bundled stamp for a non-Chrome-compliant
|
|
5993
|
+
* version), false only when a read/write threw. Chrome requires
|
|
5994
|
+
* `manifest.version` to be 1-4 dot-separated integers, so a non-numeric
|
|
5995
|
+
* value (`"unknown"`, a prerelease/build semver) is left as the bundled
|
|
5996
|
+
* build-time stamp rather than written (which would make the unpacked
|
|
5997
|
+
* extension fail to load).
|
|
5998
|
+
*/
|
|
5999
|
+
function stampVersion(destExtDir) {
|
|
6000
|
+
const version$2 = getPackageVersion();
|
|
6001
|
+
if (!/^\d{1,9}(\.\d{1,9}){0,3}$/.test(version$2)) return true;
|
|
6002
|
+
const manifestPath = path.join(destExtDir, "manifest.json");
|
|
6003
|
+
try {
|
|
6004
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
6005
|
+
if (manifest.version === version$2) return true;
|
|
6006
|
+
manifest.version = version$2;
|
|
6007
|
+
const tmp = `${manifestPath}.tmp-${process.pid}`;
|
|
6008
|
+
writeFileSync(tmp, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
6009
|
+
renameSync(tmp, manifestPath);
|
|
6010
|
+
return true;
|
|
6011
|
+
} catch (err) {
|
|
6012
|
+
consola.debug("[browser-mcp] manifest version stamp failed:", err);
|
|
6013
|
+
return false;
|
|
6014
|
+
}
|
|
6015
|
+
}
|
|
6016
|
+
|
|
5831
6017
|
//#endregion
|
|
5832
6018
|
//#region src/lib/browser-mcp/install-check.ts
|
|
5833
6019
|
function readBridgeDiscovery() {
|
|
@@ -5988,6 +6174,7 @@ async function ensureBridgeReady() {
|
|
|
5988
6174
|
}
|
|
5989
6175
|
async function _ensureBridgeReadyImpl() {
|
|
5990
6176
|
__implInvocationsForTests++;
|
|
6177
|
+
await provisionBrowserAssets();
|
|
5991
6178
|
const browsers = detectSupportedBrowsers();
|
|
5992
6179
|
if (browsers.length === 0) return buildInstallRequired("no_supported_browser", []);
|
|
5993
6180
|
if (!bridgeBundleExists()) return buildInstallRequired("bridge_bundle_missing", []);
|
|
@@ -17786,7 +17973,7 @@ function initProxyFromEnv() {
|
|
|
17786
17973
|
//#endregion
|
|
17787
17974
|
//#region package.json
|
|
17788
17975
|
var name = "github-router";
|
|
17789
|
-
var version$1 = "0.3.
|
|
17976
|
+
var version$1 = "0.3.74";
|
|
17790
17977
|
|
|
17791
17978
|
//#endregion
|
|
17792
17979
|
//#region src/lib/approval.ts
|
|
@@ -19905,6 +20092,7 @@ const claude = defineCommand({
|
|
|
19905
20092
|
}
|
|
19906
20093
|
}
|
|
19907
20094
|
provisionAndIndexColbert();
|
|
20095
|
+
if (browserToolsEnabled()) provisionBrowserAssets().catch((err) => consola.debug("Browser extension provisioning failed:", err));
|
|
19908
20096
|
const baseShutdown = async () => {
|
|
19909
20097
|
await removeOwnClaudeConfigMirror();
|
|
19910
20098
|
};
|
|
@@ -20017,6 +20205,7 @@ const codex = defineCommand({
|
|
|
20017
20205
|
runSelfUpdate({ selfUpdate: args["self-update"] !== false });
|
|
20018
20206
|
if (toolbeltEnabled()) provisionToolbelt().catch(() => {});
|
|
20019
20207
|
provisionAndIndexColbert();
|
|
20208
|
+
if ((state.browseEnabled || process$1.env.GH_ROUTER_ENABLE_BROWSE === "1") && hasSupportedBrowserInstalled()) provisionBrowserAssets().catch((err) => consola.debug("Browser extension provisioning failed:", err));
|
|
20020
20209
|
const usingDefault = !args.model;
|
|
20021
20210
|
const requestedModel = args.model ?? DEFAULT_CODEX_MODEL;
|
|
20022
20211
|
enableFileLogging();
|
|
@@ -20391,6 +20580,7 @@ const start = defineCommand({
|
|
|
20391
20580
|
});
|
|
20392
20581
|
runSelfUpdate({ selfUpdate: args["self-update"] !== false });
|
|
20393
20582
|
provisionAndIndexColbert();
|
|
20583
|
+
if (browserToolsEnabled()) provisionBrowserAssets().catch((err) => consola.debug("Browser extension provisioning failed:", err));
|
|
20394
20584
|
if (args.cc) generateClaudeCodeCommand(serverUrl, args.model);
|
|
20395
20585
|
if (args.cx) generateCodexCommand(serverUrl, args.model);
|
|
20396
20586
|
consola.box(`🌐 Usage Viewer: https://animeshkundu.github.io/github-router/dashboard.html?endpoint=${serverUrl}/usage`);
|