opera-browser-cli 0.1.45 → 0.1.47

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,80 @@
1
+ /**
2
+ * Browser profile inspection — is this user-data-dir in use, and if so, can we
3
+ * talk to the browser that holds it?
4
+ *
5
+ * Chromium refuses to start a second instance on a user-data-dir that is
6
+ * already open: it hands its command line to the running instance through a
7
+ * singleton socket and exits. Launching into a live profile therefore does not
8
+ * fail loudly, it fails as "the browser we asked for never appeared" — which is
9
+ * why this has to be detected before launch rather than diagnosed after it.
10
+ *
11
+ * Two files in the user-data-dir root tell us what we need:
12
+ *
13
+ * SingletonLock a symlink whose target is "<hostname>-<pid>" (POSIX).
14
+ * Present and live => the profile is in use.
15
+ * DevToolsActivePort written whenever the browser was started with
16
+ * --remote-debugging-port. Line 1 is the port. Its
17
+ * presence is what makes attaching to an already-running
18
+ * browser possible without any configuration.
19
+ *
20
+ * Neither file is authoritative on its own: SingletonLock outlives a crash, and
21
+ * DevToolsActivePort outlives a clean exit. Both are confirmed against the live
22
+ * system before being acted on.
23
+ */
24
+ export type ProfileLockState =
25
+ /** No lock file, or the lock belongs to a process that is gone. */
26
+ "free"
27
+ /** A live process on this machine holds the profile. */
28
+ | "locked"
29
+ /** A lock exists but we cannot attribute it — another host, or unreadable. */
30
+ | "unknown";
31
+ export interface ProfileLock {
32
+ state: ProfileLockState;
33
+ /** The owning browser process, when the lock names one we can verify. */
34
+ pid: number | null;
35
+ hostname: string | null;
36
+ }
37
+ /**
38
+ * Split a SingletonLock target into hostname and pid.
39
+ *
40
+ * The hostname routinely contains dashes ("Someones-MacBook-Pro-24601"), so the
41
+ * split has to come from the right.
42
+ */
43
+ export declare function parseSingletonTarget(target: string): {
44
+ hostname: string;
45
+ pid: number;
46
+ } | null;
47
+ /** First line of DevToolsActivePort is the port; the second is a ws path. */
48
+ export declare function parseDevToolsActivePort(contents: string): number | null;
49
+ /**
50
+ * Determine whether a user-data-dir is currently held by a running browser.
51
+ *
52
+ * A dangling lock reads as "free": Chromium cleans those up itself on the next
53
+ * launch, so treating one as a conflict would block a launch that would in fact
54
+ * succeed.
55
+ */
56
+ export declare function inspectProfileLock(userDataDir: string, aliveCheck?: (pid: number) => boolean): ProfileLock;
57
+ /** The debug port a running browser advertised, if it was given one. */
58
+ export declare function readDevToolsPort(userDataDir: string): number | null;
59
+ export interface DevToolsIdentity {
60
+ /** e.g. "Opera/121.0.0.0" or "Chrome/141.0.0.0" */
61
+ browser: string;
62
+ isOpera: boolean;
63
+ }
64
+ /**
65
+ * Confirm a debug port is live and find out what is on the other end.
66
+ *
67
+ * DevToolsActivePort survives a clean exit, so a recorded port proves nothing
68
+ * until something answers on it.
69
+ */
70
+ export declare function probeDevToolsEndpoint(port: number, timeoutMs?: number): Promise<DevToolsIdentity | null>;
71
+ /**
72
+ * The browser URL to attach to for this profile, or null if there is nothing
73
+ * live to attach to.
74
+ */
75
+ export declare function findAttachableEndpoint(userDataDir: string): Promise<{
76
+ url: string;
77
+ identity: DevToolsIdentity;
78
+ } | null>;
79
+ /** Where the given Opera build keeps its real profile, if we can find it. */
80
+ export declare function defaultProfileDir(browserPath: string | undefined, home: string, platform?: NodeJS.Platform): string | null;
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Browser profile inspection — is this user-data-dir in use, and if so, can we
3
+ * talk to the browser that holds it?
4
+ *
5
+ * Chromium refuses to start a second instance on a user-data-dir that is
6
+ * already open: it hands its command line to the running instance through a
7
+ * singleton socket and exits. Launching into a live profile therefore does not
8
+ * fail loudly, it fails as "the browser we asked for never appeared" — which is
9
+ * why this has to be detected before launch rather than diagnosed after it.
10
+ *
11
+ * Two files in the user-data-dir root tell us what we need:
12
+ *
13
+ * SingletonLock a symlink whose target is "<hostname>-<pid>" (POSIX).
14
+ * Present and live => the profile is in use.
15
+ * DevToolsActivePort written whenever the browser was started with
16
+ * --remote-debugging-port. Line 1 is the port. Its
17
+ * presence is what makes attaching to an already-running
18
+ * browser possible without any configuration.
19
+ *
20
+ * Neither file is authoritative on its own: SingletonLock outlives a crash, and
21
+ * DevToolsActivePort outlives a clean exit. Both are confirmed against the live
22
+ * system before being acted on.
23
+ */
24
+ import { existsSync, lstatSync, readFileSync, readlinkSync } from "node:fs";
25
+ import { hostname } from "node:os";
26
+ import { join } from "node:path";
27
+ import { request } from "node:http";
28
+ /**
29
+ * Split a SingletonLock target into hostname and pid.
30
+ *
31
+ * The hostname routinely contains dashes ("Someones-MacBook-Pro-24601"), so the
32
+ * split has to come from the right.
33
+ */
34
+ export function parseSingletonTarget(target) {
35
+ const split = target.lastIndexOf("-");
36
+ if (split <= 0)
37
+ return null;
38
+ const pid = Number.parseInt(target.slice(split + 1), 10);
39
+ if (!Number.isInteger(pid) || pid <= 0)
40
+ return null;
41
+ return { hostname: target.slice(0, split), pid };
42
+ }
43
+ /** First line of DevToolsActivePort is the port; the second is a ws path. */
44
+ export function parseDevToolsActivePort(contents) {
45
+ const first = contents.split("\n")[0]?.trim() ?? "";
46
+ const port = Number.parseInt(first, 10);
47
+ if (!Number.isInteger(port) || port <= 0 || port > 65_535)
48
+ return null;
49
+ return port;
50
+ }
51
+ function isProcessAlive(pid) {
52
+ try {
53
+ process.kill(pid, 0);
54
+ return true;
55
+ }
56
+ catch (error) {
57
+ // EPERM means it exists but belongs to another user — still alive.
58
+ return error.code === "EPERM";
59
+ }
60
+ }
61
+ /**
62
+ * Determine whether a user-data-dir is currently held by a running browser.
63
+ *
64
+ * A dangling lock reads as "free": Chromium cleans those up itself on the next
65
+ * launch, so treating one as a conflict would block a launch that would in fact
66
+ * succeed.
67
+ */
68
+ export function inspectProfileLock(userDataDir, aliveCheck = isProcessAlive) {
69
+ const lockPath = join(userDataDir, "SingletonLock");
70
+ let target;
71
+ try {
72
+ // lstat, not stat: the link is expected to dangle after a crash, and a
73
+ // dangling symlink is exactly the case we want to report as free.
74
+ if (!lstatSync(lockPath).isSymbolicLink()) {
75
+ // Windows writes a regular file instead of a symlink. We can see that the
76
+ // profile is claimed but not by whom.
77
+ return { state: "unknown", pid: null, hostname: null };
78
+ }
79
+ target = readlinkSync(lockPath);
80
+ }
81
+ catch {
82
+ return { state: "free", pid: null, hostname: null };
83
+ }
84
+ const parsed = parseSingletonTarget(target);
85
+ if (parsed === null)
86
+ return { state: "unknown", pid: null, hostname: null };
87
+ // A lock written by a different machine (a synced or networked profile) says
88
+ // nothing about processes here, and its pid must never be signalled.
89
+ if (parsed.hostname !== hostname()) {
90
+ return { state: "unknown", pid: null, hostname: parsed.hostname };
91
+ }
92
+ if (!aliveCheck(parsed.pid)) {
93
+ return { state: "free", pid: null, hostname: parsed.hostname };
94
+ }
95
+ return { state: "locked", pid: parsed.pid, hostname: parsed.hostname };
96
+ }
97
+ /** The debug port a running browser advertised, if it was given one. */
98
+ export function readDevToolsPort(userDataDir) {
99
+ const portFile = join(userDataDir, "DevToolsActivePort");
100
+ try {
101
+ if (!existsSync(portFile))
102
+ return null;
103
+ return parseDevToolsActivePort(readFileSync(portFile, "utf-8"));
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ }
109
+ /**
110
+ * Confirm a debug port is live and find out what is on the other end.
111
+ *
112
+ * DevToolsActivePort survives a clean exit, so a recorded port proves nothing
113
+ * until something answers on it.
114
+ */
115
+ export function probeDevToolsEndpoint(port, timeoutMs = 1500) {
116
+ return new Promise((resolve) => {
117
+ const req = request({
118
+ hostname: "127.0.0.1",
119
+ port,
120
+ path: "/json/version",
121
+ method: "GET",
122
+ timeout: timeoutMs,
123
+ }, (res) => {
124
+ let body = "";
125
+ res.on("data", (chunk) => (body += chunk));
126
+ res.on("end", () => {
127
+ try {
128
+ const parsed = JSON.parse(body);
129
+ if (typeof parsed.Browser !== "string")
130
+ return resolve(null);
131
+ resolve({
132
+ browser: parsed.Browser,
133
+ isOpera: /opera|opr\//i.test(parsed.Browser),
134
+ });
135
+ }
136
+ catch {
137
+ resolve(null);
138
+ }
139
+ });
140
+ });
141
+ req.on("error", () => resolve(null));
142
+ req.on("timeout", () => {
143
+ req.destroy();
144
+ resolve(null);
145
+ });
146
+ req.end();
147
+ });
148
+ }
149
+ /**
150
+ * The browser URL to attach to for this profile, or null if there is nothing
151
+ * live to attach to.
152
+ */
153
+ export async function findAttachableEndpoint(userDataDir) {
154
+ const port = readDevToolsPort(userDataDir);
155
+ if (port === null)
156
+ return null;
157
+ const identity = await probeDevToolsEndpoint(port);
158
+ if (identity === null)
159
+ return null;
160
+ return { url: `http://127.0.0.1:${port}`, identity };
161
+ }
162
+ // ---------------------------------------------------------------------------
163
+ // Default profile locations
164
+ // ---------------------------------------------------------------------------
165
+ /** Where the given Opera build keeps its real profile, if we can find it. */
166
+ export function defaultProfileDir(browserPath, home, platform = process.platform) {
167
+ let candidate;
168
+ if (platform === "darwin") {
169
+ const isDeveloper = browserPath?.includes("Opera Neon Developer.app") ?? false;
170
+ const bundle = isDeveloper
171
+ ? "com.operasoftware.OperaNeonDeveloper"
172
+ : "com.operasoftware.OperaNeon";
173
+ candidate = `${home}/Library/Application Support/${bundle}`;
174
+ }
175
+ else if (platform === "win32") {
176
+ const appData = process.env.APPDATA ?? `${home}\\AppData\\Roaming`;
177
+ const isDeveloper = browserPath?.includes("Developer") ?? false;
178
+ candidate = isDeveloper
179
+ ? `${appData}\\Opera Software\\Opera Neon Developer`
180
+ : `${appData}\\Opera Software\\Opera Neon`;
181
+ }
182
+ else {
183
+ return null;
184
+ }
185
+ return existsSync(candidate) ? candidate : null;
186
+ }
187
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Package version lookup, shared by the CLI, the bridge, and the health contract.
3
+ *
4
+ * Resolution walks up from this module so it works both from source
5
+ * (`src/version.ts` → `../package.json`) and from the build output
6
+ * (`dist/src/version.js` → `../../package.json`).
7
+ */
8
+ export declare function getPackageVersion(): string;
9
+ /** Reset the memoised version — for use in tests only. */
10
+ export declare function resetVersionCache(): void;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Package version lookup, shared by the CLI, the bridge, and the health contract.
3
+ *
4
+ * Resolution walks up from this module so it works both from source
5
+ * (`src/version.ts` → `../package.json`) and from the build output
6
+ * (`dist/src/version.js` → `../../package.json`).
7
+ */
8
+ import { existsSync, readFileSync } from "node:fs";
9
+ import { dirname, join } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+ let cached = null;
12
+ export function getPackageVersion() {
13
+ if (cached !== null)
14
+ return cached;
15
+ const here = dirname(fileURLToPath(import.meta.url));
16
+ for (const candidate of [
17
+ join(here, "..", "package.json"),
18
+ join(here, "..", "..", "package.json"),
19
+ ]) {
20
+ if (!existsSync(candidate))
21
+ continue;
22
+ const parsed = JSON.parse(readFileSync(candidate, "utf-8"));
23
+ if (typeof parsed.version === "string" && parsed.version.length > 0) {
24
+ cached = parsed.version;
25
+ return cached;
26
+ }
27
+ }
28
+ throw new Error("Could not determine opera-browser-cli package version");
29
+ }
30
+ /** Reset the memoised version — for use in tests only. */
31
+ export function resetVersionCache() {
32
+ cached = null;
33
+ }
34
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,IAAI,MAAM,GAAkB,IAAI,CAAC;AAEjC,MAAM,UAAU,iBAAiB;IAC/B,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAEnC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,KAAK,MAAM,SAAS,IAAI;QACtB,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;QAChC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;KACvC,EAAE,CAAC;QACF,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,SAAS;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAEzD,CAAC;QACF,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;YACxB,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;AAC3E,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,iBAAiB;IAC/B,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opera-browser-cli",
3
- "version": "0.1.45",
3
+ "version": "0.1.47",
4
4
  "description": "AXI-compliant opera-devtools-mcp wrapper — combined operations, TOON output, contextual suggestions",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env tsx
2
- export {};
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env tsx
2
- import { getErrorMessage, runBridge } from "../src/bridge.js";
3
- runBridge().catch((error) => {
4
- process.stderr.write(`[opera-cli] Fatal: ${getErrorMessage(error)}\n`);
5
- process.exit(1);
6
- });
7
- //# sourceMappingURL=opera-cli-bridge.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"opera-cli-bridge.js","sourceRoot":"","sources":["../../bin/opera-cli-bridge.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE9D,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- import { main } from '../src/cli.js';
3
- main(process.argv.slice(2));
4
- //# sourceMappingURL=opera-cli.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"opera-cli.js","sourceRoot":"","sources":["../../bin/opera-cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -1,8 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Read(//Users/opera_user/dev/git/opera-browser-cli/**)",
5
- "Bash(git -C /Users/opera_user/dev/git/opera-browser-cli ls-files)"
6
- ]
7
- }
8
- }