opera-browser-cli 0.1.45 → 0.1.46
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/README.md +98 -16
- package/SKILL.md +31 -2
- package/dist/src/bridge.d.ts +69 -1
- package/dist/src/bridge.js +259 -23
- package/dist/src/bridge.js.map +1 -1
- package/dist/src/browser-target.d.ts +84 -0
- package/dist/src/browser-target.js +161 -0
- package/dist/src/browser-target.js.map +1 -0
- package/dist/src/cli.d.ts +72 -2
- package/dist/src/cli.js +947 -213
- package/dist/src/cli.js.map +1 -1
- package/dist/src/client.d.ts +75 -8
- package/dist/src/client.js +671 -119
- package/dist/src/client.js.map +1 -1
- package/dist/src/config.d.ts +58 -0
- package/dist/src/config.js +171 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/detect.d.ts +21 -0
- package/dist/src/detect.js +76 -0
- package/dist/src/detect.js.map +1 -0
- package/dist/src/identity.d.ts +61 -0
- package/dist/src/identity.js +87 -0
- package/dist/src/identity.js.map +1 -0
- package/dist/src/profile.d.ts +80 -0
- package/dist/src/profile.js +187 -0
- package/dist/src/profile.js.map +1 -0
- package/dist/src/version.d.ts +10 -0
- package/dist/src/version.js +34 -0
- package/dist/src/version.js.map +1 -0
- package/package.json +1 -1
- package/dist/bin/opera-cli-bridge.d.ts +0 -2
- package/dist/bin/opera-cli-bridge.js +0 -7
- package/dist/bin/opera-cli-bridge.js.map +0 -1
- package/dist/bin/opera-cli.d.ts +0 -2
- package/dist/bin/opera-cli.js +0 -4
- package/dist/bin/opera-cli.js.map +0 -1
- package/openclaw/.claude/settings.local.json +0 -8
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide what browser the bridge should talk to, before it starts.
|
|
3
|
+
*
|
|
4
|
+
* The constraint that shapes all of this: --remote-debugging-port is a
|
|
5
|
+
* startup-only flag. A browser the user opened normally cannot be attached to,
|
|
6
|
+
* ever. So there is no way to "connect to the Opera that is already open" —
|
|
7
|
+
* only ways to arrange that the open Opera was started with a port in the first
|
|
8
|
+
* place, and a way to detect it when it was.
|
|
9
|
+
*
|
|
10
|
+
* That gives three states for a configured profile:
|
|
11
|
+
*
|
|
12
|
+
* free → let opera-devtools-mcp launch it, as before.
|
|
13
|
+
* locked, debug port live → attach. No prompt, no restart, nothing to do.
|
|
14
|
+
* locked, no debug port → a conflict only the user can resolve, by
|
|
15
|
+
* letting us restart their browser.
|
|
16
|
+
*
|
|
17
|
+
* The second case is the one that makes this feel automatic: once a browser has
|
|
18
|
+
* been started with a port — by us, or by the user following `launch-args` —
|
|
19
|
+
* every later command finds it on its own via DevToolsActivePort.
|
|
20
|
+
*/
|
|
21
|
+
import { spawn } from "node:child_process";
|
|
22
|
+
import { existsSync, unlinkSync } from "node:fs";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { findAttachableEndpoint, inspectProfileLock, probeDevToolsEndpoint, readDevToolsPort, } from "./profile.js";
|
|
25
|
+
export async function resolveBrowserTarget(ctx) {
|
|
26
|
+
// An explicit browser URL is the user telling us they manage the browser.
|
|
27
|
+
if (ctx.browserUrl) {
|
|
28
|
+
return { mode: "attach", url: ctx.browserUrl, note: "OPERA_CLI_BROWSER_URL" };
|
|
29
|
+
}
|
|
30
|
+
// No persistent profile means an isolated one, which nothing else can hold.
|
|
31
|
+
if (!ctx.userDataDir) {
|
|
32
|
+
return { mode: "managed", note: "isolated profile" };
|
|
33
|
+
}
|
|
34
|
+
// A live debug port wins outright: the browser is running and reachable, so
|
|
35
|
+
// there is no conflict to resolve regardless of what the lock says.
|
|
36
|
+
const attachable = await findAttachableEndpoint(ctx.userDataDir);
|
|
37
|
+
if (attachable !== null) {
|
|
38
|
+
return {
|
|
39
|
+
mode: "attach",
|
|
40
|
+
url: attachable.url,
|
|
41
|
+
note: `running ${attachable.identity.browser}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const lock = inspectProfileLock(ctx.userDataDir);
|
|
45
|
+
if (lock.state === "free") {
|
|
46
|
+
return { mode: "managed", note: "profile is free" };
|
|
47
|
+
}
|
|
48
|
+
return { mode: "conflict", userDataDir: ctx.userDataDir, lock };
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Takeover
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
function sleep(ms) {
|
|
54
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Ask a running browser to quit, and wait for it to let go of the profile.
|
|
58
|
+
*
|
|
59
|
+
* SIGTERM only. Chromium treats it as a clean shutdown — session saved, profile
|
|
60
|
+
* flushed — whereas SIGKILL risks a corrupted profile and loses the user's
|
|
61
|
+
* tabs. If it will not go, we say so rather than escalating: this is somebody's
|
|
62
|
+
* browser, and forcing it is not ours to decide.
|
|
63
|
+
*/
|
|
64
|
+
export async function quitBrowser(lock, userDataDir, timeoutMs = 20_000) {
|
|
65
|
+
if (lock.pid === null)
|
|
66
|
+
return { ok: false, reason: "no-pid" };
|
|
67
|
+
try {
|
|
68
|
+
process.kill(lock.pid, "SIGTERM");
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Already gone between inspection and now — that is a success.
|
|
72
|
+
return { ok: true };
|
|
73
|
+
}
|
|
74
|
+
const deadline = Date.now() + timeoutMs;
|
|
75
|
+
while (Date.now() < deadline) {
|
|
76
|
+
await sleep(250);
|
|
77
|
+
if (inspectProfileLock(userDataDir).state === "free")
|
|
78
|
+
return { ok: true };
|
|
79
|
+
}
|
|
80
|
+
return { ok: false, reason: "timeout" };
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Start a browser we can attach to, and that outlives us.
|
|
84
|
+
*
|
|
85
|
+
* `--remote-debugging-port=0` has Chromium pick a free port itself and record
|
|
86
|
+
* it in DevToolsActivePort. That satisfies two requirements at once: we never
|
|
87
|
+
* squat a predictable port like 9222, and the port is discoverable by every
|
|
88
|
+
* later command without being written to any config.
|
|
89
|
+
*
|
|
90
|
+
* The browser is detached deliberately. Having just restarted the user's
|
|
91
|
+
* browser, closing it again when the CLI's bridge stops would be a poor trade.
|
|
92
|
+
*/
|
|
93
|
+
export async function launchAttachableBrowser(executablePath, userDataDir, extraArgs = [], timeoutMs = 30_000) {
|
|
94
|
+
if (!executablePath || !existsSync(executablePath)) {
|
|
95
|
+
return { ok: false, reason: "no-executable" };
|
|
96
|
+
}
|
|
97
|
+
// Chromium rewrites this on startup, but clearing it first means a stale port
|
|
98
|
+
// from a previous run can never be mistaken for the new browser's.
|
|
99
|
+
const portFile = join(userDataDir, "DevToolsActivePort");
|
|
100
|
+
try {
|
|
101
|
+
unlinkSync(portFile);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Absent already — fine.
|
|
105
|
+
}
|
|
106
|
+
const args = [
|
|
107
|
+
"--remote-debugging-port=0",
|
|
108
|
+
// Explicit even though it is the default: the debug port must never be
|
|
109
|
+
// reachable from off-box.
|
|
110
|
+
"--remote-debugging-address=127.0.0.1",
|
|
111
|
+
`--user-data-dir=${userDataDir}`,
|
|
112
|
+
// We just took their browser away; give the tabs back.
|
|
113
|
+
"--restore-last-session",
|
|
114
|
+
...extraArgs,
|
|
115
|
+
];
|
|
116
|
+
let child;
|
|
117
|
+
try {
|
|
118
|
+
child = spawn(executablePath, args, { stdio: "ignore", detached: true });
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
reason: "spawn-failed",
|
|
124
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
child.unref();
|
|
128
|
+
let spawnError = null;
|
|
129
|
+
child.on("error", (error) => {
|
|
130
|
+
spawnError = error.message;
|
|
131
|
+
});
|
|
132
|
+
const deadline = Date.now() + timeoutMs;
|
|
133
|
+
while (Date.now() < deadline) {
|
|
134
|
+
if (spawnError !== null) {
|
|
135
|
+
return { ok: false, reason: "spawn-failed", detail: spawnError };
|
|
136
|
+
}
|
|
137
|
+
const port = readDevToolsPort(userDataDir);
|
|
138
|
+
if (port !== null && (await probeDevToolsEndpoint(port)) !== null) {
|
|
139
|
+
return { ok: true, url: `http://127.0.0.1:${port}` };
|
|
140
|
+
}
|
|
141
|
+
await sleep(250);
|
|
142
|
+
}
|
|
143
|
+
return { ok: false, reason: "timeout" };
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The flags a user needs to start Opera themselves so the CLI can attach.
|
|
147
|
+
*
|
|
148
|
+
* Deliberately not `--remote-allow-origins=*`: Chromium's default rejection of
|
|
149
|
+
* CDP WebSocket upgrades that carry an Origin header is what stops a web page
|
|
150
|
+
* from driving the browser, and this profile is logged into everything.
|
|
151
|
+
*/
|
|
152
|
+
export function browserLaunchArgs(userDataDir) {
|
|
153
|
+
const args = [
|
|
154
|
+
"--remote-debugging-port=0",
|
|
155
|
+
"--remote-debugging-address=127.0.0.1",
|
|
156
|
+
];
|
|
157
|
+
if (userDataDir)
|
|
158
|
+
args.push(`--user-data-dir=${userDataDir}`);
|
|
159
|
+
return args;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=browser-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-target.js","sourceRoot":"","sources":["../../src/browser-target.ts"],"names":[],"mappings":"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"}
|
package/dist/src/cli.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { type ErrorCode, type BridgeStatus, type StopResult } from "./client.js";
|
|
1
2
|
type CliStdout = Pick<NodeJS.WriteStream, "write">;
|
|
2
3
|
export type MainOptions = {
|
|
3
4
|
argv?: string[];
|
|
4
5
|
stdout?: CliStdout;
|
|
5
6
|
};
|
|
6
|
-
export declare const TOP_HELP = "usage: opera-browser-cli [command] [args] [flags]\ncommands[
|
|
7
|
+
export declare const TOP_HELP = "usage: opera-browser-cli [command] [args] [flags]\ncommands[46]:\n open <url>, snapshot, screenshot <path>, click @<uid>, fill @<uid> <text>,\n type <text>, press <key>, scroll <dir>, back, wait <ms|text>, eval <js>,\n run,\n hover @<uid>, drag @<from> @<to>, fillform @<uid>=<val>..., dialog <action>,\n upload @<uid> <path>, pages, newpage <url>, selectpage <id>, closepage <id>,\n resize <w> <h>, emulate, console, console-get <id>, network,\n network-get [id], lighthouse, perf-start, perf-stop,\n perf-insight <set> <name>, heap <path>, start, stop, restart, status,\n attach, launch-args, login,\n chat [--model <id>] <prompt>, invoke-do <prompt>, make <prompt>,\n research <prompt>, models,\n setup, logs, doctor\n\nexit codes:\n 0 ok 2 bad arguments 3 environment not ready 4 sign-in required\n 5 timed out (retry) 6 stale page ref (re-snapshot) 1 other\n\nflags[3]:\n --help, -v/-V/--version, --takeover\n\nenvironment:\n OPERA_CLI_HEADED Set to 1 to run Chrome in headed (visible) mode\n OPERA_CLI_CHROME_ARGS Whitespace-separated Chrome flags forwarded to the browser\n (no shell-style quoting; flags with spaces are not supported)\n e.g. \"--enable-gpu --ignore-gpu-blocklist\"\n OPERA_CLI_PORT Base bridge port (default: 9225); the next 9 ports are\n tried in turn if it is occupied\n OPERA_CLI_BROWSER_URL Connect to an existing Chrome instance instead of launching one\n e.g. \"http://127.0.0.1:9222\"\n OPERA_CLI_USER_DATA_DIR Persistent Chrome profile directory (skips --isolated mode)\n e.g. \"/path/to/.chrome-profile\"\n OPERA_CLI_EXECUTABLE_PATH Path to a custom browser binary (e.g. Opera Neon)\n OPERA_CLI_ENABLE_HOOKS Set to 1 to auto-install session hooks on startup\n OPERA_CLI_TAKEOVER Set to 1 to allow restarting a running Opera without\n asking (same as the --takeover flag)\n\n Environment variables can also be set in ~/.opera-browser-cli/config (KEY=VALUE, one per line).\n Run `opera-browser-cli setup` to configure interactively.\n\nopera ai:\n chat is available on any Opera browser. Use --model to select a model.\n Run \"models\" to list available models.\n invoke-do, make, and research require Opera Neon with an active sign-in.\n Run `opera-browser-cli setup` to configure the executable path, or set\n OPERA_CLI_EXECUTABLE_PATH=\"/Applications/Opera Neon.app/Contents/MacOS/Opera\".\n\ngpu:\n Headless Chrome cannot access hardware GPU on most Linux systems.\n For GPU-accelerated WebGL, use headed mode with GPU flags:\n OPERA_CLI_HEADED=1\n OPERA_CLI_CHROME_ARGS=\"--enable-gpu --ignore-gpu-blocklist\"\n For WebGPU, Vulkan must also be enabled (required for the Dawn backend):\n OPERA_CLI_CHROME_ARGS=\"--enable-gpu --ignore-gpu-blocklist --enable-unsafe-webgpu --enable-features=Vulkan\"\n\ntips:\n Pipe output through grep/head to extract specific data from large pages.\n";
|
|
7
8
|
export declare function getCommandHelp(command: string): string | null;
|
|
8
9
|
export interface ScreenshotArgs {
|
|
9
10
|
filePath: string | null;
|
|
@@ -61,9 +62,59 @@ export declare function parsePerfStartArgs(args: string[]): {
|
|
|
61
62
|
autoStop?: boolean;
|
|
62
63
|
filePath?: string;
|
|
63
64
|
};
|
|
65
|
+
/**
|
|
66
|
+
* Exit codes, so a caller can branch on *why* something failed without parsing
|
|
67
|
+
* the message. Documented in README.md and SKILL.md — treat as a contract.
|
|
68
|
+
*
|
|
69
|
+
* 2 fix the command 3 environment not ready 4 ask the user
|
|
70
|
+
* 5 retry later 6 page state moved; re-snapshot
|
|
71
|
+
*/
|
|
72
|
+
export declare const EXIT_CODES: Record<ErrorCode, number>;
|
|
73
|
+
export declare function exitCodeForCdpError(error: unknown): number;
|
|
74
|
+
export declare function formatCliError(error: unknown): {
|
|
75
|
+
output: string;
|
|
76
|
+
exitCode: number;
|
|
77
|
+
};
|
|
64
78
|
/** Wrap plain JS expressions for MCP evaluate_script, but pass functions through unchanged. */
|
|
65
79
|
export declare function wrapJsExpression(js: string): string;
|
|
66
|
-
export declare function formatStopOutput(
|
|
80
|
+
export declare function formatStopOutput(result: StopResult): string;
|
|
81
|
+
export declare function formatStatusOutput(status: BridgeStatus): string;
|
|
82
|
+
export interface SetupArgs {
|
|
83
|
+
interactive: boolean;
|
|
84
|
+
executable: string | undefined;
|
|
85
|
+
profile: string | undefined;
|
|
86
|
+
headed: boolean | undefined;
|
|
87
|
+
}
|
|
88
|
+
export declare function parseSetupArgs(args: string[]): SetupArgs;
|
|
89
|
+
export declare function parseAttachArgs(args: string[]): {
|
|
90
|
+
port: number | null;
|
|
91
|
+
clear: boolean;
|
|
92
|
+
};
|
|
93
|
+
export declare function parseLogsArgs(args: string[]): {
|
|
94
|
+
lines: number;
|
|
95
|
+
follow: boolean;
|
|
96
|
+
errorsOnly: boolean;
|
|
97
|
+
};
|
|
98
|
+
export declare function filterLogLines(lines: string[], errorsOnly: boolean): string[];
|
|
99
|
+
/**
|
|
100
|
+
* Pre-flight check for AI commands. Fails fast if Opera Neon is clearly
|
|
101
|
+
* not configured, so we don't pay the 30s bridge-startup tax just to surface
|
|
102
|
+
* a confusing protocol error.
|
|
103
|
+
*
|
|
104
|
+
* Skipped when OPERA_CLI_BROWSER_URL is set — the user manages the browser
|
|
105
|
+
* themselves and presumably knows it's Opera Neon.
|
|
106
|
+
*/
|
|
107
|
+
export type BrowserKind = "neon" | "opera" | "other" | "unknown";
|
|
108
|
+
/**
|
|
109
|
+
* What kind of browser we are about to drive.
|
|
110
|
+
*
|
|
111
|
+
* The old check only asked whether the configured path existed, which cannot
|
|
112
|
+
* tell Neon from Opera from Chrome — so it passed in exactly the two cases that
|
|
113
|
+
* fail: a plain Opera (no invoke-do/make/research) and a non-Opera browser
|
|
114
|
+
* (no Opera AI at all). Attached browsers report their real identity; launched
|
|
115
|
+
* ones are identified by their build, which is how Opera names its binaries.
|
|
116
|
+
*/
|
|
117
|
+
export declare function classifyBrowser(executablePath: string | undefined, attachedBrowser?: string | undefined): BrowserKind;
|
|
67
118
|
declare const VALID_RESEARCH_TYPES: readonly ["local", "one-minute", "deep"];
|
|
68
119
|
type ResearchType = (typeof VALID_RESEARCH_TYPES)[number];
|
|
69
120
|
export declare function parseChatArgs(args: string[]): {
|
|
@@ -74,5 +125,24 @@ export declare function parseResearchArgs(args: string[]): {
|
|
|
74
125
|
prompt: string;
|
|
75
126
|
researchType?: ResearchType;
|
|
76
127
|
};
|
|
128
|
+
/**
|
|
129
|
+
* Work out which browser this command should drive, before the bridge starts.
|
|
130
|
+
*
|
|
131
|
+
* Runs in the CLI rather than the bridge because resolving a conflict may need
|
|
132
|
+
* to ask the user something, and the bridge is detached with no terminal.
|
|
133
|
+
*
|
|
134
|
+
* This runs even when a bridge is already alive. A bridge fixes its browser
|
|
135
|
+
* (attach URL, profile, flags) at startup, so a healthy bridge is only "the
|
|
136
|
+
* question is settled" while it is still driving the right browser. The case
|
|
137
|
+
* that must never be silently skipped is a conflict: the user's own Opera is
|
|
138
|
+
* running on the configured profile without a debug port. That used to be
|
|
139
|
+
* bypassed whenever any bridge was running, so the restart prompt never fired
|
|
140
|
+
* and the CLI kept driving a stale headless / separate-profile browser.
|
|
141
|
+
*/
|
|
142
|
+
export declare function preflightBrowser(argv: string[], takeover: boolean): Promise<void>;
|
|
143
|
+
export declare function extractTakeoverFlag(argv: string[]): {
|
|
144
|
+
argv: string[];
|
|
145
|
+
takeover: boolean;
|
|
146
|
+
};
|
|
77
147
|
export declare function main(options?: MainOptions | string[]): Promise<void>;
|
|
78
148
|
export {};
|