surf-cli 2.0.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.
@@ -0,0 +1,115 @@
1
+ #!/bin/bash
2
+ cd "$(dirname "$0")/.."
3
+
4
+ PASS=0
5
+ FAIL=0
6
+
7
+ test_output() {
8
+ local name="$1"
9
+ local cmd="$2"
10
+ local expect="$3"
11
+
12
+ output=$(eval "$cmd" 2>&1) || true
13
+ if echo "$output" | grep -q "$expect"; then
14
+ echo "PASS: $name"
15
+ PASS=$((PASS + 1))
16
+ else
17
+ echo "FAIL: $name"
18
+ echo " Command: $cmd"
19
+ echo " Expected: $expect"
20
+ echo " Got: $output"
21
+ FAIL=$((FAIL + 1))
22
+ fi
23
+ }
24
+
25
+ test_exit_code() {
26
+ local name="$1"
27
+ local cmd="$2"
28
+ local expect_code="$3"
29
+
30
+ eval "$cmd" > /dev/null 2>&1
31
+ actual_code=$?
32
+ if [ "$actual_code" -eq "$expect_code" ]; then
33
+ echo "PASS: $name"
34
+ PASS=$((PASS + 1))
35
+ else
36
+ echo "FAIL: $name"
37
+ echo " Command: $cmd"
38
+ echo " Expected exit code: $expect_code"
39
+ echo " Got: $actual_code"
40
+ FAIL=$((FAIL + 1))
41
+ fi
42
+ }
43
+
44
+ echo "=== CLI Unit Tests (no extension required) ==="
45
+ echo ""
46
+
47
+ echo "-- Version and Help --"
48
+ test_output "version flag" "node cli.cjs --version" "surf version"
49
+ test_output "version short" "node cli.cjs -v" "surf version"
50
+ test_output "basic help" "node cli.cjs --help" "Common Commands"
51
+ test_output "full help" "node cli.cjs --help-full" "Aliases:"
52
+ test_output "help topic refs" "node cli.cjs --help-topic refs" "Element References"
53
+ test_output "help topic selectors" "node cli.cjs --help-topic selectors" "CSS Selectors"
54
+ test_output "help topic cookies" "node cli.cjs --help-topic cookies" "Cookie Management"
55
+
56
+ echo ""
57
+ echo "-- Migration Hints --"
58
+ test_output "removed read_page" "node cli.cjs read_page" "Use: page.read"
59
+ test_output "removed list_tabs" "node cli.cjs list_tabs" "Use: tab.list"
60
+ test_output "removed wait_for_element" "node cli.cjs wait_for_element" "Use: wait.element"
61
+ test_output "removed javascript_tool" "node cli.cjs javascript_tool" "Use: js"
62
+
63
+ echo ""
64
+ echo "-- Aliases --"
65
+ test_output "snap help" "node cli.cjs snap --help" "snap -> screenshot"
66
+ test_output "read help" "node cli.cjs read --help" "accessibility tree"
67
+ test_output "find help" "node cli.cjs find --help" "search"
68
+ test_output "go help" "node cli.cjs go --help" "URL"
69
+
70
+ echo ""
71
+ echo "-- Find Command --"
72
+ test_output "find screenshot" "node cli.cjs --find screenshot" "screenshot"
73
+ test_output "find cookie" "node cli.cjs --find cookie" "cookie"
74
+ test_output "find wait" "node cli.cjs --find wait" "wait"
75
+
76
+ echo ""
77
+ echo "-- About Command --"
78
+ test_output "about refs" "node cli.cjs --about refs" "Element References"
79
+ test_output "about cookies" "node cli.cjs --about cookies" "Cookie Management"
80
+ test_output "about tab" "node cli.cjs --about tab" "Tab management"
81
+
82
+ echo ""
83
+ echo "-- Group Help --"
84
+ test_output "tab group help" "node cli.cjs tab" "tab.list"
85
+ test_output "cookie group help" "node cli.cjs cookie" "cookie.list"
86
+ test_output "scroll group help" "node cli.cjs scroll" "scroll.top"
87
+
88
+ echo ""
89
+ echo "-- Command Help with Examples --"
90
+ test_output "click help examples" "node cli.cjs click --help" "Examples"
91
+ test_output "type help examples" "node cli.cjs type --help" "Examples"
92
+ test_output "screenshot help examples" "node cli.cjs screenshot --help" "Examples"
93
+
94
+ echo ""
95
+ echo "-- New Commands in Help --"
96
+ test_output "back in help" "node cli.cjs --help-full" "back"
97
+ test_output "forward in help" "node cli.cjs --help-full" "forward"
98
+ test_output "zoom in help" "node cli.cjs --help-full" "zoom"
99
+ test_output "bookmark in help" "node cli.cjs --help-full" "bookmark"
100
+ test_output "history in help" "node cli.cjs --help-full" "history"
101
+
102
+ echo ""
103
+ echo "-- List Command --"
104
+ test_output "list shows new commands" "node cli.cjs --list" "back"
105
+ test_output "list shows zoom" "node cli.cjs --list" "zoom"
106
+
107
+ echo ""
108
+ echo "==================================="
109
+ echo "Results: $PASS passed, $FAIL failed"
110
+ echo "==================================="
111
+
112
+ if [ "$FAIL" -gt 0 ]; then
113
+ exit 1
114
+ fi
115
+ exit 0
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "surf-cli",
3
+ "version": "2.0.0",
4
+ "description": "CLI for AI agents to control Chrome. Zero config, agent-agnostic, battle-tested.",
5
+ "keywords": [
6
+ "chrome",
7
+ "browser",
8
+ "automation",
9
+ "ai",
10
+ "agent",
11
+ "cli",
12
+ "cdp",
13
+ "devtools"
14
+ ],
15
+ "author": "Nico Bailon",
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/nicobailon/surf-cli.git"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/nicobailon/surf-cli/issues"
23
+ },
24
+ "homepage": "https://github.com/nicobailon/surf-cli#readme",
25
+ "type": "module",
26
+ "bin": {
27
+ "surf": "native/cli.cjs"
28
+ },
29
+ "files": [
30
+ "native/",
31
+ "scripts/",
32
+ "dist/",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "scripts": {
37
+ "dev": "vite build --watch --mode development",
38
+ "build": "vite build",
39
+ "check": "tsc --noEmit",
40
+ "lint": "biome check .",
41
+ "lint:fix": "biome check --write .",
42
+ "lint:test": "biome check test/",
43
+ "format": "biome format --write .",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest",
46
+ "test:coverage": "vitest run --coverage",
47
+ "test:ui": "vitest --ui",
48
+ "install:native": "node scripts/install-native-host.cjs",
49
+ "uninstall:native": "node scripts/uninstall-native-host.cjs"
50
+ },
51
+ "dependencies": {
52
+ "@google/generative-ai": "^0.21.0",
53
+ "@modelcontextprotocol/sdk": "^1.7.0",
54
+ "buffer": "^6.0.3",
55
+ "crypto-browserify": "^3.12.1",
56
+ "events": "^3.3.0",
57
+ "stream-browserify": "^3.0.0",
58
+ "vite-plugin-node-polyfills": "^0.24.0",
59
+ "zod": "^3.24.0"
60
+ },
61
+ "devDependencies": {
62
+ "@biomejs/biome": "^2.3.11",
63
+ "@types/chrome": "^0.0.287",
64
+ "@vitest/coverage-v8": "^4.0.16",
65
+ "@vitest/ui": "^4.0.16",
66
+ "typescript": "^5.7.2",
67
+ "vite": "^6.0.0",
68
+ "vitest": "^4.0.16"
69
+ }
70
+ }
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env node
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const os = require("os");
5
+ const { execSync, spawnSync } = require("child_process");
6
+
7
+ const HOST_NAME = "surf.browser.host";
8
+
9
+ const BROWSERS = {
10
+ chrome: {
11
+ name: "Google Chrome",
12
+ darwin: "Library/Application Support/Google/Chrome/NativeMessagingHosts",
13
+ linux: ".config/google-chrome/NativeMessagingHosts",
14
+ win32: "Google\\Chrome",
15
+ },
16
+ chromium: {
17
+ name: "Chromium",
18
+ darwin: "Library/Application Support/Chromium/NativeMessagingHosts",
19
+ linux: ".config/chromium/NativeMessagingHosts",
20
+ win32: "Chromium",
21
+ },
22
+ brave: {
23
+ name: "Brave",
24
+ darwin:
25
+ "Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts",
26
+ linux: ".config/BraveSoftware/Brave-Browser/NativeMessagingHosts",
27
+ win32: "BraveSoftware\\Brave-Browser",
28
+ },
29
+ edge: {
30
+ name: "Microsoft Edge",
31
+ darwin: "Library/Application Support/Microsoft Edge/NativeMessagingHosts",
32
+ linux: ".config/microsoft-edge/NativeMessagingHosts",
33
+ win32: "Microsoft\\Edge",
34
+ },
35
+ arc: {
36
+ name: "Arc",
37
+ darwin:
38
+ "Library/Application Support/Arc/User Data/NativeMessagingHosts",
39
+ linux: null,
40
+ win32: null,
41
+ },
42
+ };
43
+
44
+ const NODE_PATHS = {
45
+ darwin: [
46
+ "/opt/homebrew/bin/node",
47
+ "/usr/local/bin/node",
48
+ "/usr/bin/node",
49
+ ],
50
+ linux: [
51
+ "/usr/bin/node",
52
+ "/usr/local/bin/node",
53
+ ],
54
+ win32: [
55
+ "C:\\Program Files\\nodejs\\node.exe",
56
+ "C:\\Program Files (x86)\\nodejs\\node.exe",
57
+ ],
58
+ };
59
+
60
+ function findNode() {
61
+ const platform = process.platform;
62
+ const paths = NODE_PATHS[platform] || [];
63
+ for (const p of paths) {
64
+ if (fs.existsSync(p)) return p;
65
+ }
66
+ try {
67
+ const which = platform === "win32" ? "where" : "which";
68
+ const result = execSync(`${which} node`, { encoding: "utf8" }).trim();
69
+ if (result) return result.split("\n")[0];
70
+ } catch {}
71
+ return null;
72
+ }
73
+
74
+ function findNpmGlobalRoot() {
75
+ try {
76
+ return execSync("npm root -g", { encoding: "utf8" }).trim();
77
+ } catch {
78
+ return null;
79
+ }
80
+ }
81
+
82
+ function getWrapperDir() {
83
+ const platform = process.platform;
84
+ const home = os.homedir();
85
+ switch (platform) {
86
+ case "darwin":
87
+ return path.join(home, "Library/Application Support/surf-cli");
88
+ case "linux":
89
+ return path.join(home, ".local/share/surf-cli");
90
+ case "win32":
91
+ return path.join(process.env.LOCALAPPDATA || path.join(home, "AppData/Local"), "surf-cli");
92
+ default:
93
+ return null;
94
+ }
95
+ }
96
+
97
+ function getHostPath() {
98
+ const npmRoot = findNpmGlobalRoot();
99
+ if (npmRoot) {
100
+ const globalPath = path.join(npmRoot, "surf-cli/native/host.cjs");
101
+ if (fs.existsSync(globalPath)) return globalPath;
102
+ }
103
+ const localPath = path.resolve(__dirname, "../native/host.cjs");
104
+ if (fs.existsSync(localPath)) return localPath;
105
+ return null;
106
+ }
107
+
108
+ function createWrapper(wrapperDir, nodePath, hostPath) {
109
+ const platform = process.platform;
110
+ fs.mkdirSync(wrapperDir, { recursive: true });
111
+
112
+ if (platform === "win32") {
113
+ const batPath = path.join(wrapperDir, "host-wrapper.bat");
114
+ const content = `@echo off\r\n"${nodePath}" "${hostPath}"\r\n`;
115
+ fs.writeFileSync(batPath, content);
116
+ return batPath;
117
+ } else {
118
+ const shPath = path.join(wrapperDir, "host-wrapper.sh");
119
+ const hostDir = path.dirname(hostPath);
120
+ const content = `#!/bin/bash
121
+ cd "${hostDir}"
122
+ exec "${nodePath}" "${hostPath}"
123
+ `;
124
+ fs.writeFileSync(shPath, content);
125
+ fs.chmodSync(shPath, "755");
126
+ return shPath;
127
+ }
128
+ }
129
+
130
+ function installManifest(browser, extensionId, wrapperPath) {
131
+ const platform = process.platform;
132
+ const browserConfig = BROWSERS[browser];
133
+
134
+ if (!browserConfig || !browserConfig[platform]) {
135
+ return null;
136
+ }
137
+
138
+ if (platform === "win32") {
139
+ return installWindowsRegistry(browser, extensionId, wrapperPath);
140
+ }
141
+
142
+ const manifestDir = path.join(os.homedir(), browserConfig[platform]);
143
+ fs.mkdirSync(manifestDir, { recursive: true });
144
+
145
+ const manifest = {
146
+ name: HOST_NAME,
147
+ description: "Surf CLI Native Host",
148
+ path: wrapperPath,
149
+ type: "stdio",
150
+ allowed_origins: [`chrome-extension://${extensionId}/`],
151
+ };
152
+
153
+ const manifestPath = path.join(manifestDir, `${HOST_NAME}.json`);
154
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
155
+ return manifestPath;
156
+ }
157
+
158
+ function installWindowsRegistry(browser, extensionId, wrapperPath) {
159
+ const browserConfig = BROWSERS[browser];
160
+ const regPath = `HKCU\\Software\\${browserConfig.win32}\\NativeMessagingHosts\\${HOST_NAME}`;
161
+
162
+ const manifestDir = getWrapperDir();
163
+ const manifestPath = path.join(manifestDir, `${HOST_NAME}.json`);
164
+
165
+ const manifest = {
166
+ name: HOST_NAME,
167
+ description: "Surf CLI Native Host",
168
+ path: wrapperPath,
169
+ type: "stdio",
170
+ allowed_origins: [`chrome-extension://${extensionId}/`],
171
+ };
172
+
173
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
174
+
175
+ try {
176
+ execSync(`reg add "${regPath}" /ve /t REG_SZ /d "${manifestPath}" /f`, {
177
+ stdio: "pipe",
178
+ });
179
+ return manifestPath;
180
+ } catch (e) {
181
+ console.error(`Failed to add registry entry: ${e.message}`);
182
+ return null;
183
+ }
184
+ }
185
+
186
+ function parseArgs() {
187
+ const args = process.argv.slice(2);
188
+ const result = { extensionId: null, browsers: ["chrome"] };
189
+
190
+ for (let i = 0; i < args.length; i++) {
191
+ const arg = args[i];
192
+ if (arg === "--browser" || arg === "-b") {
193
+ const browserArg = args[++i];
194
+ if (browserArg === "all") {
195
+ result.browsers = Object.keys(BROWSERS);
196
+ } else {
197
+ result.browsers = browserArg.split(",").map((b) => b.trim().toLowerCase());
198
+ }
199
+ } else if (arg === "--help" || arg === "-h") {
200
+ printHelp();
201
+ process.exit(0);
202
+ } else if (!arg.startsWith("-")) {
203
+ result.extensionId = arg;
204
+ }
205
+ }
206
+ return result;
207
+ }
208
+
209
+ function printHelp() {
210
+ console.log(`
211
+ Surf CLI Native Host Installer
212
+
213
+ Usage: install-native-host.cjs <extension-id> [options]
214
+
215
+ Arguments:
216
+ extension-id Chrome extension ID (32 lowercase letters a-p)
217
+ Find at chrome://extensions with Developer Mode enabled
218
+
219
+ Options:
220
+ -b, --browser Browser(s) to install for (default: chrome)
221
+ Values: chrome, chromium, brave, edge, arc, all
222
+ Multiple: --browser chrome,brave
223
+
224
+ Examples:
225
+ node install-native-host.cjs abcdefghijklmnopabcdefghijklmnop
226
+ node install-native-host.cjs abcdefghijklmnop --browser brave
227
+ node install-native-host.cjs abcdefghijklmnop --browser all
228
+ `);
229
+ }
230
+
231
+ function main() {
232
+ const { extensionId, browsers } = parseArgs();
233
+
234
+ if (!extensionId) {
235
+ console.error("Error: Extension ID required");
236
+ console.error("Usage: install-native-host.cjs <extension-id> [--browser chrome|brave|edge|all]");
237
+ console.error("\nFind your extension ID at chrome://extensions (enable Developer Mode)");
238
+ process.exit(1);
239
+ }
240
+
241
+ if (!/^[a-p]{32}$/.test(extensionId)) {
242
+ console.error("Error: Invalid extension ID format");
243
+ console.error("Expected 32 lowercase letters (a-p)");
244
+ process.exit(1);
245
+ }
246
+
247
+ const nodePath = findNode();
248
+ if (!nodePath) {
249
+ console.error("Error: Could not find Node.js");
250
+ console.error("Make sure Node.js is installed and in your PATH");
251
+ process.exit(1);
252
+ }
253
+
254
+ const hostPath = getHostPath();
255
+ if (!hostPath) {
256
+ console.error("Error: Could not find host.cjs");
257
+ console.error("Make sure surf-cli is installed correctly");
258
+ process.exit(1);
259
+ }
260
+
261
+ const wrapperDir = getWrapperDir();
262
+ if (!wrapperDir) {
263
+ console.error("Error: Unsupported platform");
264
+ process.exit(1);
265
+ }
266
+
267
+ console.log(`Platform: ${process.platform}`);
268
+ console.log(`Node: ${nodePath}`);
269
+ console.log(`Host: ${hostPath}`);
270
+ console.log(`Wrapper dir: ${wrapperDir}`);
271
+ console.log("");
272
+
273
+ const wrapperPath = createWrapper(wrapperDir, nodePath, hostPath);
274
+ console.log(`Created wrapper: ${wrapperPath}`);
275
+ console.log("");
276
+
277
+ const installed = [];
278
+ const skipped = [];
279
+
280
+ for (const browser of browsers) {
281
+ if (!BROWSERS[browser]) {
282
+ console.error(`Unknown browser: ${browser}`);
283
+ continue;
284
+ }
285
+
286
+ const result = installManifest(browser, extensionId, wrapperPath);
287
+ if (result) {
288
+ installed.push({ browser: BROWSERS[browser].name, path: result });
289
+ } else {
290
+ skipped.push(BROWSERS[browser].name);
291
+ }
292
+ }
293
+
294
+ if (installed.length > 0) {
295
+ console.log("Installed for:");
296
+ for (const { browser, path: p } of installed) {
297
+ console.log(` ${browser}: ${p}`);
298
+ }
299
+ }
300
+
301
+ if (skipped.length > 0) {
302
+ console.log(`\nSkipped (not supported on ${process.platform}): ${skipped.join(", ")}`);
303
+ }
304
+
305
+ console.log("\nDone! Restart your browser for changes to take effect.");
306
+ }
307
+
308
+ main();
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const os = require("os");
5
+ const { execSync } = require("child_process");
6
+
7
+ const HOST_NAME = "surf.browser.host";
8
+
9
+ const BROWSERS = {
10
+ chrome: {
11
+ name: "Google Chrome",
12
+ darwin: "Library/Application Support/Google/Chrome/NativeMessagingHosts",
13
+ linux: ".config/google-chrome/NativeMessagingHosts",
14
+ win32: "Google\\Chrome",
15
+ },
16
+ chromium: {
17
+ name: "Chromium",
18
+ darwin: "Library/Application Support/Chromium/NativeMessagingHosts",
19
+ linux: ".config/chromium/NativeMessagingHosts",
20
+ win32: "Chromium",
21
+ },
22
+ brave: {
23
+ name: "Brave",
24
+ darwin: "Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts",
25
+ linux: ".config/BraveSoftware/Brave-Browser/NativeMessagingHosts",
26
+ win32: "BraveSoftware\\Brave-Browser",
27
+ },
28
+ edge: {
29
+ name: "Microsoft Edge",
30
+ darwin: "Library/Application Support/Microsoft Edge/NativeMessagingHosts",
31
+ linux: ".config/microsoft-edge/NativeMessagingHosts",
32
+ win32: "Microsoft\\Edge",
33
+ },
34
+ arc: {
35
+ name: "Arc",
36
+ darwin: "Library/Application Support/Arc/User Data/NativeMessagingHosts",
37
+ linux: null,
38
+ win32: null,
39
+ },
40
+ };
41
+
42
+ function getWrapperDir() {
43
+ const platform = process.platform;
44
+ const home = os.homedir();
45
+ switch (platform) {
46
+ case "darwin":
47
+ return path.join(home, "Library/Application Support/surf-cli");
48
+ case "linux":
49
+ return path.join(home, ".local/share/surf-cli");
50
+ case "win32":
51
+ return path.join(process.env.LOCALAPPDATA || path.join(home, "AppData/Local"), "surf-cli");
52
+ default:
53
+ return null;
54
+ }
55
+ }
56
+
57
+ function removeManifest(browser) {
58
+ const platform = process.platform;
59
+ const browserConfig = BROWSERS[browser];
60
+
61
+ if (!browserConfig || !browserConfig[platform]) {
62
+ return null;
63
+ }
64
+
65
+ if (platform === "win32") {
66
+ return removeWindowsRegistry(browser);
67
+ }
68
+
69
+ const manifestPath = path.join(
70
+ os.homedir(),
71
+ browserConfig[platform],
72
+ `${HOST_NAME}.json`
73
+ );
74
+
75
+ try {
76
+ fs.unlinkSync(manifestPath);
77
+ return manifestPath;
78
+ } catch {
79
+ return null;
80
+ }
81
+ }
82
+
83
+ function removeWindowsRegistry(browser) {
84
+ const browserConfig = BROWSERS[browser];
85
+ const regPath = `HKCU\\Software\\${browserConfig.win32}\\NativeMessagingHosts\\${HOST_NAME}`;
86
+
87
+ try {
88
+ execSync(`reg delete "${regPath}" /f`, { stdio: "pipe" });
89
+ return regPath;
90
+ } catch {
91
+ return null;
92
+ }
93
+ }
94
+
95
+ function removeWrapperDir() {
96
+ const wrapperDir = getWrapperDir();
97
+ if (!wrapperDir) return null;
98
+
99
+ try {
100
+ fs.rmSync(wrapperDir, { recursive: true, force: true });
101
+ return wrapperDir;
102
+ } catch {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ function parseArgs() {
108
+ const args = process.argv.slice(2);
109
+ const result = { browsers: ["chrome"], all: false };
110
+
111
+ for (let i = 0; i < args.length; i++) {
112
+ const arg = args[i];
113
+ if (arg === "--browser" || arg === "-b") {
114
+ const browserArg = args[++i];
115
+ if (browserArg === "all") {
116
+ result.browsers = Object.keys(BROWSERS);
117
+ result.all = true;
118
+ } else {
119
+ result.browsers = browserArg.split(",").map((b) => b.trim().toLowerCase());
120
+ }
121
+ } else if (arg === "--all" || arg === "-a") {
122
+ result.browsers = Object.keys(BROWSERS);
123
+ result.all = true;
124
+ } else if (arg === "--help" || arg === "-h") {
125
+ printHelp();
126
+ process.exit(0);
127
+ }
128
+ }
129
+ return result;
130
+ }
131
+
132
+ function printHelp() {
133
+ console.log(`
134
+ Surf CLI Native Host Uninstaller
135
+
136
+ Usage: uninstall-native-host.cjs [options]
137
+
138
+ Options:
139
+ -b, --browser Browser(s) to uninstall from (default: chrome)
140
+ Values: chrome, chromium, brave, edge, arc, all
141
+ -a, --all Uninstall from all browsers and remove wrapper
142
+
143
+ Examples:
144
+ node uninstall-native-host.cjs
145
+ node uninstall-native-host.cjs --browser brave
146
+ node uninstall-native-host.cjs --all
147
+ `);
148
+ }
149
+
150
+ function main() {
151
+ const { browsers, all } = parseArgs();
152
+
153
+ console.log(`Platform: ${process.platform}`);
154
+ console.log("");
155
+
156
+ const removed = [];
157
+ const notFound = [];
158
+
159
+ for (const browser of browsers) {
160
+ if (!BROWSERS[browser]) {
161
+ console.error(`Unknown browser: ${browser}`);
162
+ continue;
163
+ }
164
+
165
+ const result = removeManifest(browser);
166
+ if (result) {
167
+ removed.push({ browser: BROWSERS[browser].name, path: result });
168
+ } else {
169
+ notFound.push(BROWSERS[browser].name);
170
+ }
171
+ }
172
+
173
+ if (removed.length > 0) {
174
+ console.log("Removed manifests:");
175
+ for (const { browser, path: p } of removed) {
176
+ console.log(` ${browser}: ${p}`);
177
+ }
178
+ }
179
+
180
+ if (notFound.length > 0) {
181
+ console.log(`\nNot found: ${notFound.join(", ")}`);
182
+ }
183
+
184
+ if (all) {
185
+ const wrapperDir = removeWrapperDir();
186
+ if (wrapperDir) {
187
+ console.log(`\nRemoved wrapper directory: ${wrapperDir}`);
188
+ }
189
+ }
190
+
191
+ console.log("\nDone!");
192
+ }
193
+
194
+ main();