@vforsh/argus 0.1.19 → 0.1.20
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/.tsbuildinfo +1 -1
- package/dist/argus.js +257 -59
- package/dist/cli/register/extensionCommands.d.ts.map +1 -1
- package/dist/cli/register/extensionCommands.js +28 -3
- package/dist/cli/register/extensionCommands.js.map +1 -1
- package/dist/commands/extension/extensionId.d.ts +27 -0
- package/dist/commands/extension/extensionId.d.ts.map +1 -0
- package/dist/commands/extension/extensionId.js +37 -0
- package/dist/commands/extension/extensionId.js.map +1 -0
- package/dist/commands/extension/extensionPath.d.ts +15 -0
- package/dist/commands/extension/extensionPath.d.ts.map +1 -0
- package/dist/commands/extension/extensionPath.js +61 -0
- package/dist/commands/extension/extensionPath.js.map +1 -0
- package/dist/commands/extension/install.d.ts +16 -0
- package/dist/commands/extension/install.d.ts.map +1 -0
- package/dist/commands/extension/install.js +154 -0
- package/dist/commands/extension/install.js.map +1 -0
- package/dist/commands/extension/setup.d.ts +2 -1
- package/dist/commands/extension/setup.d.ts.map +1 -1
- package/dist/commands/extension/setup.js +5 -2
- package/dist/commands/extension/setup.js.map +1 -1
- package/dist/extension/dist/background/auth-header-monitor.js +77 -0
- package/dist/extension/dist/background/bridge-client.js +120 -0
- package/dist/extension/dist/background/cdp-proxy.js +201 -0
- package/dist/extension/dist/background/debugger-manager.js +296 -0
- package/dist/extension/dist/background/service-worker.js +2004 -0
- package/dist/extension/dist/background/service-worker.js.map +7 -0
- package/dist/extension/dist/popup/popup.js +730 -0
- package/dist/extension/dist/popup/popup.js.map +7 -0
- package/dist/extension/dist/popup/tabMarkup.js +104 -0
- package/dist/extension/dist/popup/viewState.js +60 -0
- package/dist/extension/dist/types/messages.js +5 -0
- package/dist/extension/icons/icon-128.png +0 -0
- package/dist/extension/icons/icon-16.png +0 -0
- package/dist/extension/icons/icon-48.png +0 -0
- package/dist/extension/manifest.json +26 -0
- package/dist/extension/src/popup/popup.html +756 -0
- package/dist/skill/argus/SKILL.md +3 -1
- package/dist/skill/argus/reference/EXTENSION.md +13 -8
- package/package.json +2 -2
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Stable Chrome extension ID for the Argus CDP Bridge.
|
|
4
|
+
*
|
|
5
|
+
* The extension's `manifest.json` pins a public `key`, so Chrome derives this
|
|
6
|
+
* exact ID on every machine and install path (instead of a path-dependent id).
|
|
7
|
+
* Because the ID is constant, `argus extension setup`/`install` need no ID
|
|
8
|
+
* argument and the native messaging host manifest never has to be rewritten.
|
|
9
|
+
*
|
|
10
|
+
* Source of truth: `packages/argus-extension/key.pem` (gitignored). Regenerate
|
|
11
|
+
* both values with `bun run --cwd packages/argus-extension generate-key`, which
|
|
12
|
+
* updates the manifest `key` and prints the matching id. The drift guard in
|
|
13
|
+
* `packages/argus/test/extension-id.test.ts` keeps these three in sync.
|
|
14
|
+
*/
|
|
15
|
+
export const ARGUS_EXTENSION_ID = 'ibhecpfiodfmkpflcnnkkgnjifheefhb';
|
|
16
|
+
/** Base64 SPKI public key pinned as the extension manifest `key` field. */
|
|
17
|
+
export const ARGUS_EXTENSION_PUBLIC_KEY = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt1lmJWE8y/3by5FlIta/a35cckp3mOVkFSmn8imDO+6S4xsD7naFwonrRMlMMd6lljOBG+sa5YgiMEeR1XwwyZlSDqXIOaNdizChK8wjtjhYldRmax9NQCbhejFWRyIDsR+B7xmbRsroCnSG667JR6D3q8L5v2Qbd/4rH+zZniqUdZKOPJEyjOBEbWoytX3nTWbJvCy+AHznAtnkIWysQyibUkMIBxDGNBSpx507Cc6kMrWZpnCSkjKtRj7qLpMEat4xQdkOhh3mZCisSimQoMD2Axhnqmyacj8IoKDQX+xkb6qLArO7nWr6QD4rOVWrTdgSBg+4TfW3C2zDCP5UhwIDAQAB';
|
|
18
|
+
/**
|
|
19
|
+
* Derive a Chrome extension ID from a base64 SPKI public key, matching Chrome's
|
|
20
|
+
* algorithm: SHA-256 of the DER public key, first 16 bytes, each hex nibble
|
|
21
|
+
* mapped 0-f -> a-p.
|
|
22
|
+
*
|
|
23
|
+
* @param publicKeyBase64 - Base64-encoded DER (SPKI) public key, i.e. the value
|
|
24
|
+
* of the manifest `key` field.
|
|
25
|
+
* @returns The 32-character extension ID.
|
|
26
|
+
*/
|
|
27
|
+
export const deriveExtensionId = (publicKeyBase64) => {
|
|
28
|
+
const der = Buffer.from(publicKeyBase64, 'base64');
|
|
29
|
+
const hash = createHash('sha256').update(der).digest();
|
|
30
|
+
const hex = hash.subarray(0, 16).toString('hex');
|
|
31
|
+
let id = '';
|
|
32
|
+
for (const char of hex) {
|
|
33
|
+
id += String.fromCharCode(parseInt(char, 16) + 'a'.charCodeAt(0));
|
|
34
|
+
}
|
|
35
|
+
return id;
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=extensionId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensionId.js","sourceRoot":"","sources":["../../../src/commands/extension/extensionId.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,kCAAkC,CAAA;AAEpE,2EAA2E;AAC3E,MAAM,CAAC,MAAM,0BAA0B,GACtC,0YAA0Y,CAAA;AAE3Y;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,eAAuB,EAAU,EAAE;IACpE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAA;IAClD,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAA;IACtD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAChD,IAAI,EAAE,GAAG,EAAE,CAAA;IACX,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;QACxB,EAAE,IAAI,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;IAClE,CAAC;IACD,OAAO,EAAE,CAAA;AACV,CAAC,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type ExtensionPathOptions = {
|
|
2
|
+
json?: boolean;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* Print the absolute path to the unpacked Argus extension to load via
|
|
6
|
+
* chrome://extensions ("Load unpacked"). The built extension ships inside the
|
|
7
|
+
* CLI package (`dist/extension`), so end users never run a build step.
|
|
8
|
+
*/
|
|
9
|
+
export declare const runExtensionPath: (options: ExtensionPathOptions) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the directory of the loadable unpacked extension, or `null` if no
|
|
12
|
+
* candidate contains both a manifest and a built service worker.
|
|
13
|
+
*/
|
|
14
|
+
export declare const resolveExtensionDir: () => string | null;
|
|
15
|
+
//# sourceMappingURL=extensionPath.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensionPath.d.ts","sourceRoot":"","sources":["../../../src/commands/extension/extensionPath.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,oBAAoB,GAAG;IAClC,IAAI,CAAC,EAAE,OAAO,CAAA;CACd,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,oBAAoB,KAAG,IAqBhE,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAO,MAAM,GAAG,IAO/C,CAAA"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { createOutput } from '../../output/io.js';
|
|
5
|
+
/**
|
|
6
|
+
* Print the absolute path to the unpacked Argus extension to load via
|
|
7
|
+
* chrome://extensions ("Load unpacked"). The built extension ships inside the
|
|
8
|
+
* CLI package (`dist/extension`), so end users never run a build step.
|
|
9
|
+
*/
|
|
10
|
+
export const runExtensionPath = (options) => {
|
|
11
|
+
const output = createOutput(options);
|
|
12
|
+
const extensionDir = resolveExtensionDir();
|
|
13
|
+
if (!extensionDir) {
|
|
14
|
+
const message = `Packaged Argus extension not found. Checked:\n${resolveExtensionCandidates().join('\n')}`;
|
|
15
|
+
if (options.json) {
|
|
16
|
+
output.writeJson({ ok: false, error: { message } });
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
console.error(message);
|
|
20
|
+
}
|
|
21
|
+
process.exitCode = 2;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (options.json) {
|
|
25
|
+
output.writeJson({ ok: true, path: extensionDir });
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
output.writeHuman(extensionDir);
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Resolve the directory of the loadable unpacked extension, or `null` if no
|
|
32
|
+
* candidate contains both a manifest and a built service worker.
|
|
33
|
+
*/
|
|
34
|
+
export const resolveExtensionDir = () => {
|
|
35
|
+
for (const candidate of resolveExtensionCandidates()) {
|
|
36
|
+
if (isLoadableExtensionDir(candidate)) {
|
|
37
|
+
return candidate;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
};
|
|
42
|
+
const isLoadableExtensionDir = (dir) => {
|
|
43
|
+
return fs.existsSync(path.join(dir, 'manifest.json')) && fs.existsSync(path.join(dir, 'dist', 'background', 'service-worker.js'));
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Candidate locations, covering the bundled CLI (`<dist>/extension`), the
|
|
47
|
+
* tsc-only build (`<dist>/commands/extension` -> `<dist>/extension`), and the
|
|
48
|
+
* dev source tree (`packages/argus-extension`).
|
|
49
|
+
*/
|
|
50
|
+
const resolveExtensionCandidates = () => {
|
|
51
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
52
|
+
const candidates = [
|
|
53
|
+
path.resolve(moduleDir, 'extension'),
|
|
54
|
+
path.resolve(moduleDir, '..', '..', 'extension'),
|
|
55
|
+
path.resolve(moduleDir, '..', 'extension'),
|
|
56
|
+
path.resolve(moduleDir, '..', '..', '..', 'argus-extension'),
|
|
57
|
+
path.resolve(moduleDir, '..', '..', '..', '..', 'argus-extension'),
|
|
58
|
+
];
|
|
59
|
+
return [...new Set(candidates)];
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=extensionPath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensionPath.js","sourceRoot":"","sources":["../../../src/commands/extension/extensionPath.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAMjD;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAA6B,EAAQ,EAAE;IACvE,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IACpC,MAAM,YAAY,GAAG,mBAAmB,EAAE,CAAA;IAE1C,IAAI,CAAC,YAAY,EAAE,CAAC;QACnB,MAAM,OAAO,GAAG,iDAAiD,0BAA0B,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;QAC1G,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YAClB,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,CAAA;QACpD,CAAC;aAAM,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACvB,CAAC;QACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACpB,OAAM;IACP,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QAClB,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAA;QAClD,OAAM;IACP,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAA;AAChC,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAkB,EAAE;IACtD,KAAK,MAAM,SAAS,IAAI,0BAA0B,EAAE,EAAE,CAAC;QACtD,IAAI,sBAAsB,CAAC,SAAS,CAAC,EAAE,CAAC;YACvC,OAAO,SAAS,CAAA;QACjB,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAA;AACZ,CAAC,CAAA;AAED,MAAM,sBAAsB,GAAG,CAAC,GAAW,EAAW,EAAE;IACvD,OAAO,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAC,CAAA;AAClI,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,0BAA0B,GAAG,GAAa,EAAE;IACjD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IAC9D,MAAM,UAAU,GAAG;QAClB,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,WAAW,CAAC;QACpC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC;QAChD,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC;QAC1C,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,iBAAiB,CAAC;QAC5D,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,iBAAiB,CAAC;KAClE,CAAA;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAA;AAChC,CAAC,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type ExtensionInstallOptions = {
|
|
2
|
+
/** Open chrome://extensions automatically. Disable with --no-open. */
|
|
3
|
+
open?: boolean;
|
|
4
|
+
/** Wait for the extension to connect. Disable with --no-wait. */
|
|
5
|
+
wait?: boolean;
|
|
6
|
+
/** Seconds to wait for the extension to connect (default 120). */
|
|
7
|
+
timeout?: string;
|
|
8
|
+
json?: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* One-command extension setup: install the native messaging hosts with the
|
|
12
|
+
* pinned ID, open chrome://extensions, point the user at the unpacked folder,
|
|
13
|
+
* then wait until the extension connects and report the first command to run.
|
|
14
|
+
*/
|
|
15
|
+
export declare const runExtensionInstall: (options: ExtensionInstallOptions) => Promise<void>;
|
|
16
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/commands/extension/install.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,uBAAuB,GAAG;IACrC,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,iEAAiE;IACjE,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,OAAO,CAAA;CACd,CAAA;AAKD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAU,SAAS,uBAAuB,KAAG,OAAO,CAAC,IAAI,CAqDxF,CAAA"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { readRegistry } from '@vforsh/argus-core';
|
|
3
|
+
import { createOutput } from '../../output/io.js';
|
|
4
|
+
import { pruneRegistry } from '../../registry.js';
|
|
5
|
+
import { resolveChromeBin } from '../../utils/chromeBin.js';
|
|
6
|
+
import { ARGUS_EXTENSION_ID } from './extensionId.js';
|
|
7
|
+
import { resolveExtensionDir } from './extensionPath.js';
|
|
8
|
+
import { CONTROL_WATCHER_ID, findArgusExecutable, getPlatform, installNativeHosts } from './nativeHost.js';
|
|
9
|
+
const DEFAULT_TIMEOUT_SECONDS = 120;
|
|
10
|
+
const POLL_INTERVAL_MS = 700;
|
|
11
|
+
/**
|
|
12
|
+
* One-command extension setup: install the native messaging hosts with the
|
|
13
|
+
* pinned ID, open chrome://extensions, point the user at the unpacked folder,
|
|
14
|
+
* then wait until the extension connects and report the first command to run.
|
|
15
|
+
*/
|
|
16
|
+
export const runExtensionInstall = async (options) => {
|
|
17
|
+
const output = createOutput(options);
|
|
18
|
+
let platform;
|
|
19
|
+
try {
|
|
20
|
+
platform = getPlatform();
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return fail(output, error.message);
|
|
24
|
+
}
|
|
25
|
+
const extensionDir = resolveExtensionDir();
|
|
26
|
+
if (!extensionDir) {
|
|
27
|
+
return fail(output, 'Packaged Argus extension not found. Build the CLI (npm run build:packages) or run from a built checkout.');
|
|
28
|
+
}
|
|
29
|
+
let executablePath;
|
|
30
|
+
try {
|
|
31
|
+
executablePath = findArgusExecutable();
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return fail(output, error.message);
|
|
35
|
+
}
|
|
36
|
+
let hosts;
|
|
37
|
+
try {
|
|
38
|
+
hosts = installNativeHosts(platform, ARGUS_EXTENSION_ID, executablePath);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return fail(output, `Failed to install native hosts: ${error.message}`);
|
|
42
|
+
}
|
|
43
|
+
await pruneRegistry();
|
|
44
|
+
let connected = await isControlConnected();
|
|
45
|
+
const shouldOpen = options.open !== false && !connected;
|
|
46
|
+
const opened = shouldOpen ? openExtensionsPage() : false;
|
|
47
|
+
if (!output.json) {
|
|
48
|
+
printSummary(output, { extensionDir, hosts, connected, opened, shouldOpen });
|
|
49
|
+
}
|
|
50
|
+
const shouldWait = options.wait !== false && !connected;
|
|
51
|
+
if (shouldWait) {
|
|
52
|
+
connected = await waitForControl(parseTimeoutMs(options.timeout), output);
|
|
53
|
+
}
|
|
54
|
+
if (output.json) {
|
|
55
|
+
output.writeJson({ success: true, extensionId: ARGUS_EXTENSION_ID, extensionPath: extensionDir, hosts, connected });
|
|
56
|
+
if (!connected && shouldWait) {
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
printResult(output, connected, shouldWait);
|
|
62
|
+
};
|
|
63
|
+
const isControlConnected = async () => {
|
|
64
|
+
const { registry } = await readRegistry();
|
|
65
|
+
return Boolean(registry.watchers[CONTROL_WATCHER_ID]);
|
|
66
|
+
};
|
|
67
|
+
const openExtensionsPage = () => {
|
|
68
|
+
const chromeBin = resolveChromeBin();
|
|
69
|
+
if (!chromeBin) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const child = spawn(chromeBin, ['chrome://extensions/'], { stdio: 'ignore', detached: true });
|
|
74
|
+
child.unref();
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const waitForControl = async (timeoutMs, output) => {
|
|
82
|
+
if (!output.json) {
|
|
83
|
+
output.writeHuman(`Waiting for the extension to connect (up to ${Math.round(timeoutMs / 1000)}s)...`);
|
|
84
|
+
}
|
|
85
|
+
const deadline = Date.now() + timeoutMs;
|
|
86
|
+
while (Date.now() < deadline) {
|
|
87
|
+
if (await isControlConnected()) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
await delay(POLL_INTERVAL_MS);
|
|
91
|
+
}
|
|
92
|
+
return isControlConnected();
|
|
93
|
+
};
|
|
94
|
+
const printSummary = (output, info) => {
|
|
95
|
+
output.writeHuman('');
|
|
96
|
+
output.writeHuman('Argus extension setup');
|
|
97
|
+
output.writeHuman('');
|
|
98
|
+
output.writeHuman(` Native hosts: installed (${info.hosts.map((host) => host.hostName).join(', ')})`);
|
|
99
|
+
output.writeHuman(` Extension ID: ${ARGUS_EXTENSION_ID}`);
|
|
100
|
+
output.writeHuman(` Load unpacked: ${info.extensionDir}`);
|
|
101
|
+
output.writeHuman('');
|
|
102
|
+
if (info.connected) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (info.opened) {
|
|
106
|
+
output.writeHuman('Opened chrome://extensions. Click "Load unpacked" and select the folder above.');
|
|
107
|
+
}
|
|
108
|
+
else if (info.shouldOpen) {
|
|
109
|
+
output.writeHuman('Open chrome://extensions, then click "Load unpacked" and select the folder above.');
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
output.writeHuman('Open chrome://extensions and "Load unpacked" the folder above.');
|
|
113
|
+
}
|
|
114
|
+
output.writeHuman('(Enable Developer mode if the "Load unpacked" button is hidden.)');
|
|
115
|
+
output.writeHuman('');
|
|
116
|
+
};
|
|
117
|
+
const printResult = (output, connected, waited) => {
|
|
118
|
+
if (connected) {
|
|
119
|
+
output.writeHuman('Extension connected.');
|
|
120
|
+
output.writeHuman('');
|
|
121
|
+
output.writeHuman('Try it:');
|
|
122
|
+
output.writeHuman(' argus ext tabs');
|
|
123
|
+
output.writeHuman('');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (waited) {
|
|
127
|
+
output.writeHuman('Extension did not connect yet.');
|
|
128
|
+
output.writeHuman(' - If it is already loaded, open its popup or reload it in chrome://extensions.');
|
|
129
|
+
output.writeHuman(' - Then check `argus extension status` and `argus ext doctor`.');
|
|
130
|
+
output.writeHuman('');
|
|
131
|
+
process.exitCode = 1;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
output.writeHuman('Once loaded, verify with `argus ext tabs`.');
|
|
135
|
+
output.writeHuman('');
|
|
136
|
+
};
|
|
137
|
+
const parseTimeoutMs = (value) => {
|
|
138
|
+
const seconds = value ? Number.parseInt(value, 10) : DEFAULT_TIMEOUT_SECONDS;
|
|
139
|
+
if (!Number.isFinite(seconds) || seconds < 5) {
|
|
140
|
+
return DEFAULT_TIMEOUT_SECONDS * 1000;
|
|
141
|
+
}
|
|
142
|
+
return seconds * 1000;
|
|
143
|
+
};
|
|
144
|
+
const fail = (output, message) => {
|
|
145
|
+
if (output.json) {
|
|
146
|
+
output.writeJson({ success: false, error: { message } });
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.error(message);
|
|
150
|
+
}
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
};
|
|
153
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
154
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/commands/extension/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type ExtensionSetupOptions = {
|
|
2
|
-
|
|
2
|
+
/** Override the pinned extension ID. Defaults to {@link ARGUS_EXTENSION_ID}. */
|
|
3
|
+
extensionId?: string;
|
|
3
4
|
json?: boolean;
|
|
4
5
|
};
|
|
5
6
|
export declare const runExtensionSetup: (options: ExtensionSetupOptions) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/commands/extension/setup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/commands/extension/setup.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,qBAAqB,GAAG;IACnC,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,OAAO,CAAA;CACd,CAAA;AAED,eAAO,MAAM,iBAAiB,GAAU,SAAS,qBAAqB,KAAG,OAAO,CAAC,IAAI,CAqEpF,CAAA"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createOutput } from '../../output/io.js';
|
|
2
2
|
import { getPlatform, findArgusExecutable, installNativeHosts, shortenPath } from './nativeHost.js';
|
|
3
|
+
import { ARGUS_EXTENSION_ID } from './extensionId.js';
|
|
3
4
|
export const runExtensionSetup = async (options) => {
|
|
4
5
|
const output = createOutput(options);
|
|
5
|
-
const
|
|
6
|
+
const extensionId = options.extensionId ?? ARGUS_EXTENSION_ID;
|
|
6
7
|
let platform;
|
|
7
8
|
try {
|
|
8
9
|
platform = getPlatform();
|
|
@@ -66,9 +67,11 @@ export const runExtensionSetup = async (options) => {
|
|
|
66
67
|
}
|
|
67
68
|
output.writeHuman('');
|
|
68
69
|
output.writeHuman('Next steps:');
|
|
69
|
-
output.writeHuman(' 1.
|
|
70
|
+
output.writeHuman(' 1. Load the unpacked extension from `argus extension path` (if not already loaded)');
|
|
70
71
|
output.writeHuman(' 2. Run `argus ext tabs` or open the extension popup and attach a tab');
|
|
71
72
|
output.writeHuman('');
|
|
73
|
+
output.writeHuman('Tip: `argus extension install` does this end-to-end and waits for the extension to connect.');
|
|
74
|
+
output.writeHuman('');
|
|
72
75
|
}
|
|
73
76
|
};
|
|
74
77
|
//# sourceMappingURL=setup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/commands/extension/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/commands/extension/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const AUTH_HEADER_NAMES = new Set([
|
|
2
|
+
'authorization',
|
|
3
|
+
'cookie',
|
|
4
|
+
'x-api-key',
|
|
5
|
+
'x-auth-token',
|
|
6
|
+
'x-csrf-token',
|
|
7
|
+
'x-csrftoken',
|
|
8
|
+
'x-xsrf-token',
|
|
9
|
+
'csrf-token',
|
|
10
|
+
'xsrf-token',
|
|
11
|
+
]);
|
|
12
|
+
const WEB_REQUEST_FILTER = { urls: ['<all_urls>'] };
|
|
13
|
+
const WEB_REQUEST_EXTRA_INFO = ['requestHeaders', 'extraHeaders'];
|
|
14
|
+
/**
|
|
15
|
+
* Observe auth-related request headers for currently attached tabs.
|
|
16
|
+
* We keep the extension payload narrow so the native bridge never has to ship full request headers.
|
|
17
|
+
*/
|
|
18
|
+
export class AuthHeaderMonitor {
|
|
19
|
+
debuggerManager;
|
|
20
|
+
bridgeClient;
|
|
21
|
+
constructor(debuggerManager, bridgeClient) {
|
|
22
|
+
this.debuggerManager = debuggerManager;
|
|
23
|
+
this.bridgeClient = bridgeClient;
|
|
24
|
+
chrome.webRequest.onBeforeSendHeaders.addListener((details) => {
|
|
25
|
+
this.handleBeforeSendHeaders(details);
|
|
26
|
+
}, WEB_REQUEST_FILTER, WEB_REQUEST_EXTRA_INFO);
|
|
27
|
+
}
|
|
28
|
+
handleBeforeSendHeaders(details) {
|
|
29
|
+
const message = createObservedHeadersMessage(details, this.debuggerManager);
|
|
30
|
+
if (!message) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
this.bridgeClient.send(message);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const createObservedHeadersMessage = (details, debuggerManager) => {
|
|
37
|
+
if (details.tabId < 0 || !debuggerManager.isAttached(details.tabId)) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const requestHeaders = pickObservedAuthHeaders(details.requestHeaders);
|
|
41
|
+
if (!requestHeaders) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
type: 'auth_headers_observed',
|
|
46
|
+
tabId: details.tabId,
|
|
47
|
+
url: details.url,
|
|
48
|
+
method: details.method,
|
|
49
|
+
ts: typeof details.timeStamp === 'number' ? details.timeStamp : Date.now(),
|
|
50
|
+
requestHeaders,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
const pickObservedAuthHeaders = (headers) => {
|
|
54
|
+
if (!headers || headers.length === 0) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const observed = {};
|
|
58
|
+
for (const header of headers) {
|
|
59
|
+
const name = header.name?.trim().toLowerCase();
|
|
60
|
+
if (!name || !AUTH_HEADER_NAMES.has(name)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const value = normalizeHeaderValue(header.value);
|
|
64
|
+
if (!value) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
observed[name] = value;
|
|
68
|
+
}
|
|
69
|
+
return Object.keys(observed).length > 0 ? observed : undefined;
|
|
70
|
+
};
|
|
71
|
+
const normalizeHeaderValue = (value) => {
|
|
72
|
+
if (typeof value !== 'string') {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const trimmed = value.trim();
|
|
76
|
+
return trimmed || null;
|
|
77
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native Messaging client for communicating with argus-watcher (extension mode).
|
|
3
|
+
* Handles connection lifecycle, message serialization, and reconnection.
|
|
4
|
+
*/
|
|
5
|
+
export class BridgeClient {
|
|
6
|
+
port = null;
|
|
7
|
+
hostName;
|
|
8
|
+
messageHandlers = new Set();
|
|
9
|
+
connectHandler = null;
|
|
10
|
+
disconnectHandler = null;
|
|
11
|
+
reconnectAttempts = 0;
|
|
12
|
+
maxReconnectAttempts = 5;
|
|
13
|
+
reconnectDelay = 1000;
|
|
14
|
+
constructor(hostName = 'com.vforsh.argus.bridge') {
|
|
15
|
+
this.hostName = hostName;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Set handler for incoming messages from the host.
|
|
19
|
+
*/
|
|
20
|
+
onMessage(handler) {
|
|
21
|
+
this.messageHandlers.add(handler);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Set handler for successful connection.
|
|
25
|
+
*/
|
|
26
|
+
onConnect(handler) {
|
|
27
|
+
this.connectHandler = handler;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Set handler for disconnection.
|
|
31
|
+
*/
|
|
32
|
+
onDisconnect(handler) {
|
|
33
|
+
this.disconnectHandler = handler;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Connect to the Native Messaging host.
|
|
37
|
+
*/
|
|
38
|
+
connect() {
|
|
39
|
+
if (this.port) {
|
|
40
|
+
return true; // Already connected
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
this.port = chrome.runtime.connectNative(this.hostName);
|
|
44
|
+
this.port.onMessage.addListener((message) => {
|
|
45
|
+
this.reconnectAttempts = 0; // Reset on successful message
|
|
46
|
+
for (const handler of this.messageHandlers) {
|
|
47
|
+
handler(message);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
this.port.onDisconnect.addListener(() => {
|
|
51
|
+
const error = chrome.runtime.lastError;
|
|
52
|
+
console.log('[BridgeClient] Disconnected:', error?.message ?? 'unknown reason');
|
|
53
|
+
this.port = null;
|
|
54
|
+
if (this.disconnectHandler) {
|
|
55
|
+
this.disconnectHandler();
|
|
56
|
+
}
|
|
57
|
+
// Attempt reconnection
|
|
58
|
+
this.scheduleReconnect();
|
|
59
|
+
});
|
|
60
|
+
console.log('[BridgeClient] Connected to', this.hostName);
|
|
61
|
+
this.reconnectAttempts = 0;
|
|
62
|
+
if (this.connectHandler) {
|
|
63
|
+
this.connectHandler();
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
console.error('[BridgeClient] Failed to connect:', err);
|
|
69
|
+
this.scheduleReconnect();
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Disconnect from the Native Messaging host.
|
|
75
|
+
*/
|
|
76
|
+
disconnect() {
|
|
77
|
+
if (this.port) {
|
|
78
|
+
this.port.disconnect();
|
|
79
|
+
this.port = null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Send a message to the Native Messaging host.
|
|
84
|
+
*/
|
|
85
|
+
send(message) {
|
|
86
|
+
if (!this.port) {
|
|
87
|
+
console.warn('[BridgeClient] Cannot send, not connected');
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
this.port.postMessage(message);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
console.error('[BridgeClient] Send failed:', err);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check if connected to the host.
|
|
101
|
+
*/
|
|
102
|
+
isConnected() {
|
|
103
|
+
return this.port !== null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Schedule a reconnection attempt with exponential backoff.
|
|
107
|
+
*/
|
|
108
|
+
scheduleReconnect() {
|
|
109
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
110
|
+
console.log('[BridgeClient] Max reconnection attempts reached');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);
|
|
114
|
+
this.reconnectAttempts++;
|
|
115
|
+
console.log(`[BridgeClient] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
this.connect();
|
|
118
|
+
}, delay);
|
|
119
|
+
}
|
|
120
|
+
}
|