@empir3/empir3-bridge 0.3.21
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/CHANGELOG.md +1531 -0
- package/CODE_OF_CONDUCT.md +9 -0
- package/CONTRIBUTING.md +75 -0
- package/LICENSE +21 -0
- package/README.md +464 -0
- package/SECURITY.md +130 -0
- package/assets/accuracy-lab.html +2639 -0
- package/assets/api-clis-real.jpg +0 -0
- package/assets/bridge-console-hero.jpg +0 -0
- package/assets/browser-privacy.svg +151 -0
- package/assets/demo-orchestration.svg +74 -0
- package/assets/desktop-select-region.jpg +0 -0
- package/assets/in-page-chat.gif +0 -0
- package/assets/orchestration-hero.svg +126 -0
- package/assets/social-preview.png +0 -0
- package/assets/zara-accent.png +0 -0
- package/build/bootstrap.js +548 -0
- package/build/build.js +680 -0
- package/build/payload-entry.js +649 -0
- package/build/payload-signing-pub.json +7 -0
- package/docs/AGENT_GUIDE.md +259 -0
- package/docs/RELEASE.md +106 -0
- package/docs/SAFETY.md +112 -0
- package/docs/TESTING.md +181 -0
- package/installer/server.js +231 -0
- package/installer/ui/app.js +278 -0
- package/installer/ui/index.html +24 -0
- package/installer/ui/styles.css +146 -0
- package/package.json +95 -0
- package/scripts/bootstrap-e2e.mjs +650 -0
- package/scripts/certify-bridge.mjs +636 -0
- package/scripts/check-companion-surface.mjs +118 -0
- package/scripts/extract-welcome.mjs +64 -0
- package/scripts/gh-route-handler-check.mjs +57 -0
- package/scripts/gh-wire-test.mjs +107 -0
- package/scripts/publish-downloads.mjs +180 -0
- package/scripts/smoke-all-tools.mjs +509 -0
- package/scripts/smoke-live-bridge.mjs +696 -0
- package/scripts/splice-welcome.mjs +63 -0
- package/scripts/welcome-body.txt +2733 -0
- package/src/anthropic-client.ts +192 -0
- package/src/bootstrap-exe.ts +69 -0
- package/src/bridge.ts +2444 -0
- package/src/chat.ts +345 -0
- package/src/cli-runner.ts +239 -0
- package/src/cli.ts +649 -0
- package/src/config.ts +199 -0
- package/src/desktop-overlay.ps1 +121 -0
- package/src/executable-resolver.ts +330 -0
- package/src/handlers/agy-imagegen.ts +179 -0
- package/src/handlers/github-cli.ts +399 -0
- package/src/handlers/higgsfield-cli.ts +783 -0
- package/src/launch.js +337 -0
- package/src/mcp-server.ts +1265 -0
- package/src/pair-claim.ts +218 -0
- package/src/payload-daemon.ts +168 -0
- package/src/server.ts +21036 -0
- package/src/tool-defaults.ts +230 -0
- package/src/update-check.js +136 -0
- package/tray/build.py +76 -0
- package/tray/requirements.txt +2 -0
- package/tray/tray.py +1843 -0
package/src/launch.js
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Empir3 Browser Bridge — One-Command Launcher
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npm start # launch the bridge + Chrome (single-bridge default)
|
|
7
|
+
* npm run kill # shut it down
|
|
8
|
+
* npm run status # health check
|
|
9
|
+
*
|
|
10
|
+
* What it does:
|
|
11
|
+
* 1. Stops bridge-owned processes on its ports
|
|
12
|
+
* 2. Launches src/bridge.ts (CDP bridge → Chrome)
|
|
13
|
+
* 3. Waits for Chrome to connect
|
|
14
|
+
* 4. Launches src/server.ts (HTTP wrapper)
|
|
15
|
+
* 5. Confirms both are healthy
|
|
16
|
+
*
|
|
17
|
+
* Parallel bridges (advanced):
|
|
18
|
+
* Most users want one bridge. If you actually need a second one running
|
|
19
|
+
* alongside the first (e.g. an isolated test profile while your main
|
|
20
|
+
* browser keeps working), set the env vars below and re-run npm start
|
|
21
|
+
* in another terminal:
|
|
22
|
+
*
|
|
23
|
+
* EMPIR3_PW_PORT=3106 \
|
|
24
|
+
* EMPIR3_BRIDGE_HTTP_PORT=9967 \
|
|
25
|
+
* EMPIR3_CDP_PORT=9322 \
|
|
26
|
+
* EMPIR3_BRIDGE_PROFILE=$HOME/.empir3-bridge/profile-test \
|
|
27
|
+
* EMPIR3_BRIDGE_LABEL=TEST \
|
|
28
|
+
* npm start
|
|
29
|
+
*
|
|
30
|
+
* Then drive the parallel bridge with:
|
|
31
|
+
* BRIDGE_URL=http://localhost:3106 npx tsx src/cli.ts <command>
|
|
32
|
+
*
|
|
33
|
+
* The MCP server only sees the default bridge on :3006.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const { spawn, execSync } = require('child_process');
|
|
37
|
+
const crypto = require('crypto');
|
|
38
|
+
const fs = require('fs');
|
|
39
|
+
const path = require('path');
|
|
40
|
+
const http = require('http');
|
|
41
|
+
const os = require('os');
|
|
42
|
+
const { checkForUpdate } = require('./update-check.js');
|
|
43
|
+
|
|
44
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
45
|
+
const CDP_BRIDGE = path.join(__dirname, 'bridge.ts');
|
|
46
|
+
const PW_BRIDGE = path.join(__dirname, 'server.ts');
|
|
47
|
+
|
|
48
|
+
// ── Config: defaults are the single-bridge user setup. Override any of these
|
|
49
|
+
// env vars to spin a parallel bridge (see header comment). ──────────────────
|
|
50
|
+
const PW_PORT = parseInt(process.env.EMPIR3_PW_PORT || '3006', 10);
|
|
51
|
+
const BRIDGE_PORT = parseInt(process.env.EMPIR3_BRIDGE_HTTP_PORT || '9867', 10);
|
|
52
|
+
const CDP_PORT = parseInt(process.env.EMPIR3_CDP_PORT || '9222', 10);
|
|
53
|
+
const PROFILE = process.env.EMPIR3_BRIDGE_PROFILE
|
|
54
|
+
|| path.join(os.homedir(), '.empir3-bridge', 'profile');
|
|
55
|
+
const LABEL = process.env.EMPIR3_BRIDGE_LABEL
|
|
56
|
+
|| (PW_PORT === 3006 ? 'BRIDGE' : `BRIDGE:${PW_PORT}`);
|
|
57
|
+
|
|
58
|
+
function fetch(url, timeout = 3000) {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const req = http.get(url, { timeout }, (res) => {
|
|
61
|
+
let data = '';
|
|
62
|
+
res.on('data', c => data += c);
|
|
63
|
+
res.on('end', () => {
|
|
64
|
+
try { resolve(JSON.parse(data)); }
|
|
65
|
+
catch { resolve(data); }
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
req.on('error', reject);
|
|
69
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function killPort(port) {
|
|
74
|
+
try {
|
|
75
|
+
const lines = execSync(`netstat -ano | findstr ":${port} "`, { encoding: 'utf-8' }).trim().split('\n');
|
|
76
|
+
const pids = new Set();
|
|
77
|
+
for (const line of lines) {
|
|
78
|
+
const match = line.match(/LISTENING\s+(\d+)/);
|
|
79
|
+
if (match) pids.add(match[1]);
|
|
80
|
+
}
|
|
81
|
+
for (const pid of pids) {
|
|
82
|
+
try {
|
|
83
|
+
if (!isRepoBridgeProcess(pid)) {
|
|
84
|
+
console.log(` Port ${port} is used by PID ${pid}; leaving it alone`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
execSync(`taskkill /PID ${pid} /F`, { stdio: 'ignore' });
|
|
88
|
+
console.log(` Stopped bridge PID ${pid} on port ${port}`);
|
|
89
|
+
} catch {}
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
// No process on port — that's fine
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Recognise any Empir3 Bridge process we are allowed to reap when freeing our
|
|
97
|
+
// ports — BOTH the repo/dev process (tsx running our src) AND the installed
|
|
98
|
+
// payload daemon (runs from ~/.empir3-bridge/payload, often under the bundled
|
|
99
|
+
// node at ~/.empir3-bridge/node/<ver>/node.exe). Previously this matched only
|
|
100
|
+
// the repo process, so the payload daemon was "left alone" and a second bridge
|
|
101
|
+
// would stack on the same ports → EADDRINUSE, zombie Chromes, and CDP timeouts.
|
|
102
|
+
// Only one bridge can own these ports, so a bridge incumbent must be reaped
|
|
103
|
+
// before we (re)launch. The matches stay specific so an unrelated app that
|
|
104
|
+
// merely happens to use the port is never touched.
|
|
105
|
+
function isRepoBridgeProcess(pid) {
|
|
106
|
+
try {
|
|
107
|
+
const out = execSync(`wmic process where "ProcessId=${pid}" get CommandLine /value`, { encoding: 'utf-8' });
|
|
108
|
+
// Normalise every separator to '/' so matching is robust to how the path
|
|
109
|
+
// was written on the command line.
|
|
110
|
+
const cmd = out.toLowerCase().replace(/\\/g, '/');
|
|
111
|
+
const root = ROOT.toLowerCase().replace(/\\/g, '/');
|
|
112
|
+
const isRepo = cmd.includes(root) && (
|
|
113
|
+
cmd.includes('src/bridge.ts') ||
|
|
114
|
+
cmd.includes('src/server.ts') ||
|
|
115
|
+
cmd.includes('src/launch.js')
|
|
116
|
+
);
|
|
117
|
+
// Installed payload daemon: the published bridge running out of the
|
|
118
|
+
// per-user state dir. Path is stable regardless of how it was launched.
|
|
119
|
+
const isPayloadDaemon =
|
|
120
|
+
cmd.includes('.empir3-bridge/payload') ||
|
|
121
|
+
cmd.includes('.empir3-bridge/node/');
|
|
122
|
+
return isRepo || isPayloadDaemon;
|
|
123
|
+
} catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function waitForHealth(url, label, maxWait = 15000) {
|
|
129
|
+
const start = Date.now();
|
|
130
|
+
while (Date.now() - start < maxWait) {
|
|
131
|
+
try {
|
|
132
|
+
const data = await fetch(url);
|
|
133
|
+
if (data && (data.status || data.running !== undefined)) {
|
|
134
|
+
return data;
|
|
135
|
+
}
|
|
136
|
+
} catch {}
|
|
137
|
+
await new Promise(r => setTimeout(r, 500));
|
|
138
|
+
}
|
|
139
|
+
throw new Error(`${label} did not become healthy within ${maxWait/1000}s`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function status() {
|
|
143
|
+
console.log(`\n Bridge Status [${LABEL}]`);
|
|
144
|
+
console.log(' ' + '─'.repeat(40));
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const cdp = await fetch(`http://localhost:${BRIDGE_PORT}/health`, 2000);
|
|
148
|
+
console.log(` CDP Bridge (:${BRIDGE_PORT}) ✓ ${cdp.status || 'running'}`);
|
|
149
|
+
} catch {
|
|
150
|
+
console.log(` CDP Bridge (:${BRIDGE_PORT}) ✗ not running`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const pw = await fetch(`http://localhost:${PW_PORT}/api/status`, 2000);
|
|
155
|
+
console.log(` HTTP Wrapper (:${PW_PORT}) ✓ ${pw.running ? 'running' : 'error'}`);
|
|
156
|
+
if (pw.currentUrl) console.log(` Current URL: ${pw.currentUrl}`);
|
|
157
|
+
} catch {
|
|
158
|
+
console.log(` HTTP Wrapper (:${PW_PORT}) ✗ not running`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
console.log(` Profile dir: ${PROFILE}`);
|
|
162
|
+
console.log();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function kill() {
|
|
166
|
+
console.log(`\n Shutting down [${LABEL}] bridge...`);
|
|
167
|
+
killPort(PW_PORT);
|
|
168
|
+
killPort(BRIDGE_PORT);
|
|
169
|
+
// Kill the Chrome instance launched by THIS bridge (identified by --remote-debugging-port=CDP_PORT)
|
|
170
|
+
try {
|
|
171
|
+
const procs = execSync('wmic process where "name=\'chrome.exe\'" get ProcessId,CommandLine /format:csv', { encoding: 'utf-8' });
|
|
172
|
+
for (const line of procs.split('\n')) {
|
|
173
|
+
if (line.includes(`--remote-debugging-port=${CDP_PORT}`)) {
|
|
174
|
+
const pid = line.trim().split(',').find(p => /^\d+$/.test(p.trim()));
|
|
175
|
+
if (pid) {
|
|
176
|
+
try { execSync(`taskkill /PID ${pid.trim()} /F`, { stdio: 'ignore' }); console.log(` Killed bridge Chrome PID ${pid.trim()}`); } catch {}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch {}
|
|
181
|
+
console.log(' Done.\n');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function launch() {
|
|
185
|
+
console.log('\n ╔═══════════════════════════════════════╗');
|
|
186
|
+
console.log(` ║ Empir3 Browser Bridge [${LABEL.padEnd(8)}] ║`);
|
|
187
|
+
console.log(' ╚═══════════════════════════════════════╝\n');
|
|
188
|
+
console.log(` Ports: bridge=${BRIDGE_PORT}, chrome-cdp=${CDP_PORT}, wrapper=${PW_PORT}`);
|
|
189
|
+
console.log(` Profile: ${PROFILE}\n`);
|
|
190
|
+
|
|
191
|
+
// Non-blocking version check — prints a yellow banner if we're outdated.
|
|
192
|
+
// Always wrapped in try so a bad GitHub response can never block startup.
|
|
193
|
+
try { await checkForUpdate(); } catch {}
|
|
194
|
+
|
|
195
|
+
// Step 1: Clean up — kill OUR ports + OUR Chrome instance only
|
|
196
|
+
console.log(' [1/4] Cleaning up old processes on these ports...');
|
|
197
|
+
killPort(PW_PORT);
|
|
198
|
+
killPort(BRIDGE_PORT);
|
|
199
|
+
// Kill only Chrome with our specific CDP port (leaves user's Chrome alone)
|
|
200
|
+
try {
|
|
201
|
+
const procs = execSync('wmic process where "name=\'chrome.exe\'" get ProcessId,CommandLine /format:csv', { encoding: 'utf-8' });
|
|
202
|
+
for (const line of procs.split('\n')) {
|
|
203
|
+
if (line.includes(`--remote-debugging-port=${CDP_PORT}`)) {
|
|
204
|
+
const pid = line.trim().split(',').find(p => /^\d+$/.test(p.trim()));
|
|
205
|
+
if (pid) {
|
|
206
|
+
try { execSync(`taskkill /PID ${pid.trim()} /F`, { stdio: 'ignore' }); console.log(` Killed bridge Chrome PID ${pid.trim()}`); } catch {}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} catch {}
|
|
211
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
212
|
+
|
|
213
|
+
// Per-launch nonce — lets the overlay / page scripts match the right Chrome
|
|
214
|
+
// to the right bridge when multiple bridges are running. Bridge stamps it on
|
|
215
|
+
// the welcome URL; server exposes it on /api/identity; page scripts cache it
|
|
216
|
+
// and prefer the matching port. Single-bridge users hit the first-up fallback.
|
|
217
|
+
const BRIDGE_NONCE = crypto.randomBytes(8).toString('hex');
|
|
218
|
+
|
|
219
|
+
// Build env for child processes — pass through our port + profile choices
|
|
220
|
+
const childEnv = {
|
|
221
|
+
...process.env,
|
|
222
|
+
BRIDGE_PORT: String(BRIDGE_PORT),
|
|
223
|
+
CDP_PORT: String(CDP_PORT),
|
|
224
|
+
BRIDGE_PROFILE: PROFILE,
|
|
225
|
+
PW_PORT: String(PW_PORT),
|
|
226
|
+
EMPIR3_BRIDGE_PORT: String(BRIDGE_PORT),
|
|
227
|
+
EMPIR3_BRIDGE_NONCE: BRIDGE_NONCE,
|
|
228
|
+
// bridge.ts reads this and runs Storage.clearDataForOrigin('*') right
|
|
229
|
+
// after CDP connects. Cleared once per launch — not on every request.
|
|
230
|
+
...(FRESH ? { EMPIR3_BRIDGE_FRESH: '1' } : {}),
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
if (FRESH) {
|
|
234
|
+
console.log(' --fresh: cookies + localStorage + IndexedDB will be cleared post-connect');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Step 2: Launch CDP bridge
|
|
238
|
+
console.log(' [2/4] Launching CDP bridge (Chrome)...');
|
|
239
|
+
spawn('cmd', ['/c', 'start', '/b', 'npx', 'tsx', CDP_BRIDGE], {
|
|
240
|
+
cwd: ROOT,
|
|
241
|
+
stdio: 'ignore',
|
|
242
|
+
windowsHide: true,
|
|
243
|
+
env: childEnv,
|
|
244
|
+
}).unref();
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
const health = await waitForHealth(`http://localhost:${BRIDGE_PORT}/health`, 'CDP Bridge', 45000);
|
|
248
|
+
console.log(` ✓ Chrome connected (${health.status})`);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
console.error(` ✗ ${e.message}`);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Step 3: Launch HTTP wrapper
|
|
255
|
+
console.log(' [3/4] Launching HTTP wrapper...');
|
|
256
|
+
spawn('cmd', ['/c', 'start', '/b', 'npx', 'tsx', PW_BRIDGE], {
|
|
257
|
+
cwd: ROOT,
|
|
258
|
+
stdio: 'ignore',
|
|
259
|
+
windowsHide: true,
|
|
260
|
+
env: childEnv,
|
|
261
|
+
}).unref();
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
await waitForHealth(`http://localhost:${PW_PORT}/api/status`, 'HTTP Wrapper', 20000);
|
|
265
|
+
console.log(` ✓ Bridge ready on :${PW_PORT}`);
|
|
266
|
+
} catch (e) {
|
|
267
|
+
console.error(` ✗ ${e.message}`);
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Step 4: Confirm
|
|
272
|
+
console.log(' [4/4] Verifying...');
|
|
273
|
+
await status();
|
|
274
|
+
|
|
275
|
+
console.log(' Ready!');
|
|
276
|
+
if (PW_PORT === 3006) {
|
|
277
|
+
console.log(' MCP tools should now be available (empir3-browser).');
|
|
278
|
+
} else {
|
|
279
|
+
console.log(` Drive this bridge with:`);
|
|
280
|
+
console.log(` BRIDGE_URL=http://localhost:${PW_PORT} npx tsx src/cli.ts <command>`);
|
|
281
|
+
console.log(` (MCP only auto-discovers the default :3006 bridge.)`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Wave 1.5 — surface chat-with-Claude readiness so first-run users
|
|
285
|
+
// know which knob to turn. We read the config JSON directly here so
|
|
286
|
+
// launch.js doesn't have to load TS modules.
|
|
287
|
+
try {
|
|
288
|
+
const cfgPath = path.join(os.homedir(), '.empir3-bridge', 'config.json');
|
|
289
|
+
const legacyCfgPath = path.join(os.homedir(), '.claude-bridge', 'config.json');
|
|
290
|
+
let mode = null, hasKey = false, cliPath = '', model = '';
|
|
291
|
+
const readableCfgPath = fs.existsSync(cfgPath) ? cfgPath : fs.existsSync(legacyCfgPath) ? legacyCfgPath : '';
|
|
292
|
+
if (readableCfgPath) {
|
|
293
|
+
const cfg = JSON.parse(fs.readFileSync(readableCfgPath, 'utf-8'));
|
|
294
|
+
mode = cfg.mode || null;
|
|
295
|
+
hasKey = !!(cfg.anthropicApiKey && cfg.anthropicApiKey.length > 0);
|
|
296
|
+
cliPath = cfg.claudeCliPath || '';
|
|
297
|
+
model = cfg.model || '';
|
|
298
|
+
}
|
|
299
|
+
const cliOnPath = !cliPath ? detectClaudeOnPath() : cliPath;
|
|
300
|
+
const ready = (mode === 'api' && hasKey) || (mode === 'cli' && cliOnPath) || (!mode && cliOnPath);
|
|
301
|
+
if (ready) {
|
|
302
|
+
const effectiveMode = mode || (cliOnPath ? 'cli' : 'api');
|
|
303
|
+
const detail = effectiveMode === 'cli'
|
|
304
|
+
? `mode=cli, ${cliOnPath || 'auto-detect'}${model ? ', model=' + model : ''}`
|
|
305
|
+
: `mode=api, key set${model ? ', model=' + model : ''}`;
|
|
306
|
+
console.log(` empir3 Chat: configured (${detail})`);
|
|
307
|
+
} else {
|
|
308
|
+
console.log(` empir3 Chat: NOT configured - open http://localhost:${PW_PORT}/settings to set up.`);
|
|
309
|
+
}
|
|
310
|
+
} catch { /* config check is best-effort; never block startup */ }
|
|
311
|
+
console.log('');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function detectClaudeOnPath() {
|
|
315
|
+
try {
|
|
316
|
+
const cmd = process.platform === 'win32' ? 'where claude' : 'which claude';
|
|
317
|
+
const out = execSync(cmd, { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf-8' });
|
|
318
|
+
const lines = out.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
|
|
319
|
+
if (lines.length === 0) return '';
|
|
320
|
+
if (process.platform === 'win32') {
|
|
321
|
+
const cmdShim = lines.find(l => l.toLowerCase().endsWith('.cmd'));
|
|
322
|
+
if (cmdShim) return cmdShim;
|
|
323
|
+
}
|
|
324
|
+
return lines[0];
|
|
325
|
+
} catch { return ''; }
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ── CLI ──
|
|
329
|
+
const args = process.argv.slice(2);
|
|
330
|
+
// `--fresh` (alias `--clean`) hard-clears cookies + localStorage + IndexedDB
|
|
331
|
+
// for every origin BEFORE returning. Use it for smoke tests, demos, or any
|
|
332
|
+
// time you want a guaranteed clean-user state. Extensions, settings, and the
|
|
333
|
+
// profile dir itself are preserved — only site data is wiped.
|
|
334
|
+
const FRESH = args.includes('--fresh') || args.includes('--clean');
|
|
335
|
+
if (args.includes('--kill')) kill();
|
|
336
|
+
else if (args.includes('--status')) status();
|
|
337
|
+
else launch();
|