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,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":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBpC;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAc;IAEd,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;AACnD,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,uBAAuB,CAAC,QAAgB;IACtD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACpD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,MAAM;QAAE,OAAO,IAAI,CAAC;IACvE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mEAAmE;QACnE,OAAQ,KAA+B,CAAC,IAAI,KAAK,OAAO,CAAC;IAC3D,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,WAAmB,EACnB,aAAuC,cAAc;IAErD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAEpD,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,uEAAuE;QACvE,kEAAkE;QAClE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC;YAC1C,0EAA0E;YAC1E,sCAAsC;YACtC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QACzD,CAAC;QACD,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACtD,CAAC;IAED,MAAM,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAE5E,6EAA6E;IAC7E,qEAAqE;IACrE,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,EAAE,CAAC;QACnC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;IACpE,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;IACjE,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;AACzE,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,oBAAoB,CAAC,CAAC;IACzD,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QACvC,OAAO,uBAAuB,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAY,EACZ,SAAS,GAAG,IAAI;IAEhB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,GAAG,GAAG,OAAO,CACjB;YACE,QAAQ,EAAE,WAAW;YACrB,IAAI;YACJ,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,SAAS;SACnB,EACD,CAAC,GAAG,EAAE,EAAE;YACN,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC;YAC3C,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA0B,CAAC;oBACzD,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;wBAAE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;oBAC7D,OAAO,CAAC;wBACN,OAAO,EAAE,MAAM,CAAC,OAAO;wBACvB,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;qBAC7C,CAAC,CAAC;gBACL,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CACF,CAAC;QACF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACrC,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YACrB,GAAG,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,WAAmB;IAEnB,MAAM,IAAI,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,EAAE,GAAG,EAAE,oBAAoB,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC;AACvD,CAAC;AAED,8EAA8E;AAC9E,4BAA4B;AAC5B,8EAA8E;AAE9E,6EAA6E;AAC7E,MAAM,UAAU,iBAAiB,CAC/B,WAA+B,EAC/B,IAAY,EACZ,WAA4B,OAAO,CAAC,QAAQ;IAE5C,IAAI,SAAiB,CAAC;IACtB,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,WAAW,GAAG,WAAW,EAAE,QAAQ,CAAC,0BAA0B,CAAC,IAAI,KAAK,CAAC;QAC/E,MAAM,MAAM,GAAG,WAAW;YACxB,CAAC,CAAC,sCAAsC;YACxC,CAAC,CAAC,6BAA6B,CAAC;QAClC,SAAS,GAAG,GAAG,IAAI,gCAAgC,MAAM,EAAE,CAAC;IAC9D,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,IAAI,oBAAoB,CAAC;QACnE,MAAM,WAAW,GAAG,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC;QAChE,SAAS,GAAG,WAAW;YACrB,CAAC,CAAC,GAAG,OAAO,wCAAwC;YACpD,CAAC,CAAC,GAAG,OAAO,8BAA8B,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AAClD,CAAC"}
|
|
@@ -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 +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"}
|
package/dist/bin/opera-cli.d.ts
DELETED
package/dist/bin/opera-cli.js
DELETED
|
@@ -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"}
|