opentray 0.1.0 → 0.2.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,29 @@
1
+ import { ClientFrame, ClientRequestFrame, RequestId, ServerFrame, SurfaceOptions, SurfaceRef, TrayId, TrayOptions } from "@opentray/spec";
2
+
3
+ //#region src/client.d.ts
4
+ interface OpenTrayTransport {
5
+ request(frame: ClientRequestFrame): Promise<ServerFrame>;
6
+ }
7
+ interface OpenTrayClient {
8
+ createSurface(options: SurfaceOptions): Promise<SurfaceHandle>;
9
+ }
10
+ interface SurfaceHandle {
11
+ surface: SurfaceRef;
12
+ createTray(options: TrayOptions): Promise<TrayHandle>;
13
+ }
14
+ interface TrayHandle {
15
+ surface: SurfaceRef;
16
+ trayId: TrayId;
17
+ commandExtension(ext: string, data: unknown): Promise<void>;
18
+ destroy(): Promise<void>;
19
+ }
20
+ declare const createInitFrame: (clientVersion: string) => ClientFrame;
21
+ interface CreateClientOptions {
22
+ requestIdPrefix?: string;
23
+ }
24
+ declare const createClient: (transport: OpenTrayTransport, options?: CreateClientOptions) => OpenTrayClient;
25
+ declare const createSurfaceHandle: (transport: OpenTrayTransport, surface: SurfaceRef, nextRequestId?: () => RequestId) => SurfaceHandle;
26
+ declare const createTrayHandle: (transport: OpenTrayTransport, surface: SurfaceRef, trayId: TrayId, nextRequestId?: () => RequestId) => TrayHandle;
27
+ //#endregion
28
+ export { TrayHandle as a, createSurfaceHandle as c, SurfaceHandle as i, createTrayHandle as l, OpenTrayClient as n, createClient as o, OpenTrayTransport as r, createInitFrame as s, CreateClientOptions as t };
29
+ //# sourceMappingURL=client-COyxXT9C.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-COyxXT9C.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;;UAYiB,iBAAA;EACf,OAAA,CAAQ,KAAA,EAAO,kBAAA,GAAqB,OAAA,CAAQ,WAAA;AAAA;AAAA,UAG7B,cAAA;EACf,aAAA,CAAc,OAAA,EAAS,cAAA,GAAiB,OAAA,CAAQ,aAAA;AAAA;AAAA,UAGjC,aAAA;EACf,OAAA,EAAS,UAAA;EACT,UAAA,CAAW,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,UAAA;AAAA;AAAA,UAG3B,UAAA;EACf,OAAA,EAAS,UAAA;EACT,MAAA,EAAQ,MAAA;EACR,gBAAA,CAAiB,GAAA,UAAa,IAAA,YAAgB,OAAA;EAC9C,OAAA,IAAW,OAAA;AAAA;AAAA,cAGA,eAAA,GAAmB,aAAA,aAAwB,WAItD;AAAA,UAEe,mBAAA;EACf,eAAe;AAAA;AAAA,cAGJ,YAAA,GACX,SAAA,EAAW,iBAAA,EACX,OAAA,GAAS,mBAAA,KACR,cAAA;AAAA,cAiBU,mBAAA,GACX,SAAA,EAAW,iBAAA,EACX,OAAA,EAAS,UAAA,EACT,aAAA,SAAqB,SAAA,KACpB,aAAA;AAAA,cAeU,gBAAA,GACX,SAAA,EAAW,iBAAA,EACX,OAAA,EAAS,UAAA,EACT,MAAA,EAAQ,MAAA,EACR,aAAA,SAAqB,SAAA,KACpB,UAAA"}
package/dist/index.d.mts CHANGED
@@ -1,24 +1,3 @@
1
- import { ClientFrame, SurfaceOptions, SurfaceRef, TrayId, TrayOptions } from "@opentray/spec";
2
-
3
- //#region src/index.d.ts
4
- interface OpenTrayTransport {
5
- send(frame: ClientFrame): Promise<void>;
6
- }
7
- interface SurfaceHandle {
8
- surface: SurfaceRef;
9
- createTray(options: TrayOptions): Promise<TrayHandle>;
10
- }
11
- interface TrayHandle {
12
- surface: SurfaceRef;
13
- trayId: TrayId;
14
- commandExtension(ext: string, data: unknown): Promise<void>;
15
- destroy(): Promise<void>;
16
- }
17
- declare const createClient: (transport: OpenTrayTransport) => {
18
- createSurface(options: SurfaceOptions): Promise<SurfaceHandle>;
19
- };
20
- declare const createSurfaceHandle: (transport: OpenTrayTransport, surface: SurfaceRef) => SurfaceHandle;
21
- declare const createTrayHandle: (transport: OpenTrayTransport, surface: SurfaceRef, trayId: TrayId) => TrayHandle;
22
- //#endregion
23
- export { OpenTrayTransport, SurfaceHandle, TrayHandle, createClient, createSurfaceHandle, createTrayHandle };
24
- //# sourceMappingURL=index.d.mts.map
1
+ import { a as TrayHandle, c as createSurfaceHandle, i as SurfaceHandle, l as createTrayHandle, n as OpenTrayClient, o as createClient, r as OpenTrayTransport, s as createInitFrame, t as CreateClientOptions } from "./client-COyxXT9C.mjs";
2
+ import { BrokerEndpointIdentity, BrokerEndpointIdentityOptions, PROTOCOL_VERSION, createBrokerEndpointIdentity, formatBrokerEndpointName, formatBrokerStateRoot, formatUnixSocketPath, formatWindowsPipeName, isSupportedProtocolVersion } from "@opentray/spec";
3
+ export { type BrokerEndpointIdentity, type BrokerEndpointIdentityOptions, type CreateClientOptions, type OpenTrayClient, type OpenTrayTransport, PROTOCOL_VERSION, type SurfaceHandle, type TrayHandle, createBrokerEndpointIdentity, createClient, createInitFrame, createSurfaceHandle, createTrayHandle, formatBrokerEndpointName, formatBrokerStateRoot, formatUnixSocketPath, formatWindowsPipeName, isSupportedProtocolVersion };
package/dist/index.mjs CHANGED
@@ -1,46 +1,3 @@
1
- //#region src/index.ts
2
- const createClient = (transport) => ({ async createSurface(options) {
3
- await transport.send({
4
- type: "create-surface",
5
- ...options
6
- });
7
- return createSurfaceHandle(transport, {
8
- surfaceId: `pending:${options.appId}`,
9
- appId: options.appId
10
- });
11
- } });
12
- const createSurfaceHandle = (transport, surface) => ({
13
- surface,
14
- async createTray(options) {
15
- await transport.send({
16
- type: "create-tray",
17
- surface,
18
- tray: options
19
- });
20
- return createTrayHandle(transport, surface, options.trayId ?? "pending");
21
- }
22
- });
23
- const createTrayHandle = (transport, surface, trayId) => ({
24
- surface,
25
- trayId,
26
- async commandExtension(ext, data) {
27
- await transport.send({
28
- type: "ext-command",
29
- surfaceId: surface.surfaceId,
30
- trayId,
31
- ext,
32
- data
33
- });
34
- },
35
- async destroy() {
36
- await transport.send({
37
- type: "destroy-tray",
38
- surfaceId: surface.surfaceId,
39
- trayId
40
- });
41
- }
42
- });
43
- //#endregion
44
- export { createClient, createSurfaceHandle, createTrayHandle };
45
-
46
- //# sourceMappingURL=index.mjs.map
1
+ import { i as createTrayHandle, n as createInitFrame, r as createSurfaceHandle, t as createClient } from "./client-BfXT-Zz9.mjs";
2
+ import { PROTOCOL_VERSION, createBrokerEndpointIdentity, formatBrokerEndpointName, formatBrokerStateRoot, formatUnixSocketPath, formatWindowsPipeName, isSupportedProtocolVersion } from "@opentray/spec";
3
+ export { PROTOCOL_VERSION, createBrokerEndpointIdentity, createClient, createInitFrame, createSurfaceHandle, createTrayHandle, formatBrokerEndpointName, formatBrokerStateRoot, formatUnixSocketPath, formatWindowsPipeName, isSupportedProtocolVersion };
@@ -0,0 +1,502 @@
1
+ import { createRequire } from "node:module";
2
+ import { PROTOCOL_VERSION, createBrokerEndpointIdentity, formatUnixSocketPath, formatWindowsPipeName, parseServerFrame } from "@opentray/spec";
3
+ import { createConnection } from "node:net";
4
+ import { homedir } from "node:os";
5
+ import { fileURLToPath } from "node:url";
6
+ import { constants } from "node:fs";
7
+ import { access, mkdir, open, readFile, rm, unlink, writeFile } from "node:fs/promises";
8
+ import { spawn } from "node:child_process";
9
+ import { dirname, join } from "node:path";
10
+ //#region src/daemon/native-target.ts
11
+ const resolveBrokerNativeTarget = (platform = process.platform, arch = process.arch) => {
12
+ const packageOs = platformToPackageOs(platform);
13
+ return {
14
+ packageName: `@opentray/${packageOs}-${normalizeArch(arch)}`,
15
+ binaryRelativePath: `bin/${packageOs === "windows" ? "opentray.exe" : "opentray"}`
16
+ };
17
+ };
18
+ const platformToPackageOs = (platform) => {
19
+ switch (platform) {
20
+ case "darwin":
21
+ case "linux": return platform;
22
+ case "win32": return "windows";
23
+ default: throw new MissingPlatformBrokerBinaryError(`unsupported OpenTray broker platform: ${platform}`, platform, process.arch);
24
+ }
25
+ };
26
+ const normalizeArch = (arch) => {
27
+ switch (arch) {
28
+ case "arm64":
29
+ case "x64": return arch;
30
+ default: throw new MissingPlatformBrokerBinaryError(`unsupported OpenTray broker architecture: ${arch}`, process.platform, arch);
31
+ }
32
+ };
33
+ var MissingPlatformBrokerBinaryError = class extends Error {
34
+ code = "OPENTRAY_MISSING_PLATFORM_BROKER_BINARY";
35
+ platform;
36
+ arch;
37
+ packageName;
38
+ binaryPath;
39
+ constructor(message, platform, arch, options = {}) {
40
+ super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
41
+ this.name = "MissingPlatformBrokerBinaryError";
42
+ this.platform = platform;
43
+ this.arch = arch;
44
+ this.packageName = options.packageName;
45
+ this.binaryPath = options.binaryPath;
46
+ }
47
+ };
48
+ //#endregion
49
+ //#region src/daemon/broker-command.ts
50
+ const sourceUrl = import.meta.url;
51
+ const requireFromSource = createRequire(sourceUrl);
52
+ const resolveBrokerCommand = async (paths, options = {}) => {
53
+ const explicit = (options.env ?? process.env).OPENTRAY_BROKER_BIN;
54
+ if (explicit !== void 0 && explicit.length > 0) return commandForBinary(explicit, paths);
55
+ const platform = options.platform ?? process.platform;
56
+ const arch = options.arch ?? process.arch;
57
+ let installedBinaryError;
58
+ try {
59
+ const installedBinary = await resolveInstalledBrokerBinary({
60
+ platform,
61
+ arch,
62
+ resolvePackageJson: options.resolvePackageJson ?? resolvePackageJson
63
+ });
64
+ if (installedBinary !== void 0) return commandForBinary(installedBinary, paths);
65
+ } catch (error) {
66
+ if (!(error instanceof MissingPlatformBrokerBinaryError)) throw error;
67
+ installedBinaryError = error;
68
+ }
69
+ const sourceDir = options.sourceDir ?? dirname(fileURLToPath(sourceUrl));
70
+ const workspaceRoot = await (options.findWorkspaceRoot ?? findWorkspaceRoot)(sourceDir);
71
+ if (workspaceRoot !== void 0) return commandForBinary(await (options.ensureDevBrokerBinary ?? ensureDevBrokerBinary)(workspaceRoot), paths, workspaceRoot);
72
+ if (installedBinaryError !== void 0) throw installedBinaryError;
73
+ const target = resolveBrokerNativeTarget(platform, arch);
74
+ throw new MissingPlatformBrokerBinaryError(`unable to resolve OpenTray broker binary for ${platform}/${arch}; install ${target.packageName} or set OPENTRAY_BROKER_BIN`, platform, arch, { packageName: target.packageName });
75
+ };
76
+ const commandForBinary = (binary, paths, cwd) => {
77
+ const command = {
78
+ command: binary,
79
+ args: [
80
+ "broker",
81
+ "--endpoint",
82
+ paths.endpoint,
83
+ "--ready-file",
84
+ paths.readyFile,
85
+ "--package-version",
86
+ paths.packageVersion,
87
+ "--protocol-version",
88
+ `${paths.protocolVersion}`
89
+ ]
90
+ };
91
+ if (cwd !== void 0) command.cwd = cwd;
92
+ return command;
93
+ };
94
+ const resolveInstalledBrokerBinary = async ({ platform, arch, resolvePackageJson }) => {
95
+ const target = resolveBrokerNativeTarget(platform, arch);
96
+ const packageJsonPath = resolvePackageJson(target.packageName);
97
+ if (packageJsonPath === void 0) return;
98
+ const binary = join(dirname(packageJsonPath), target.binaryRelativePath);
99
+ if (!await exists(binary)) throw new MissingPlatformBrokerBinaryError(`OpenTray broker package ${target.packageName} is installed but missing ${target.binaryRelativePath}`, platform, arch, {
100
+ packageName: target.packageName,
101
+ binaryPath: binary
102
+ });
103
+ return binary;
104
+ };
105
+ const resolvePackageJson = (packageName) => {
106
+ try {
107
+ return requireFromSource.resolve(`${packageName}/package.json`);
108
+ } catch (error) {
109
+ if (isNodeError$1(error) && error.code === "MODULE_NOT_FOUND") return;
110
+ throw error;
111
+ }
112
+ };
113
+ const findWorkspaceRoot = async (start) => {
114
+ let current = start;
115
+ while (true) {
116
+ if (await exists(join(current, "Cargo.toml"))) {
117
+ if (await exists(join(current, "crates", "opentray-bin", "Cargo.toml"))) return current;
118
+ }
119
+ const parent = dirname(current);
120
+ if (parent === current) return;
121
+ current = parent;
122
+ }
123
+ };
124
+ const ensureDevBrokerBinary = async (workspaceRoot) => {
125
+ const binary = join(workspaceRoot, "target", "debug", process.platform === "win32" ? "opentray.exe" : "opentray");
126
+ await runCargoBuild(workspaceRoot);
127
+ return binary;
128
+ };
129
+ const runCargoBuild = (workspaceRoot) => new Promise((resolve, reject) => {
130
+ const child = spawn("cargo", [
131
+ "build",
132
+ "-p",
133
+ "opentray-bin"
134
+ ], {
135
+ cwd: workspaceRoot,
136
+ stdio: process.env.OPENTRAY_BROKER_BUILD_LOGS === "1" ? "inherit" : "ignore"
137
+ });
138
+ child.once("error", reject);
139
+ child.once("exit", (code) => {
140
+ if (code === 0) {
141
+ resolve();
142
+ return;
143
+ }
144
+ reject(/* @__PURE__ */ new Error(`cargo build -p opentray-bin failed with code ${code ?? "unknown"}`));
145
+ });
146
+ });
147
+ const exists = async (path) => {
148
+ try {
149
+ await access(path, constants.F_OK);
150
+ return true;
151
+ } catch {
152
+ return false;
153
+ }
154
+ };
155
+ const isNodeError$1 = (error) => error instanceof Error && "code" in error;
156
+ //#endregion
157
+ //#region src/daemon/lifecycle.ts
158
+ const createNodeDaemonDriver = (cliEntrypoint) => ({
159
+ async isAlive(pid) {
160
+ return isProcessAlive(pid);
161
+ },
162
+ async spawnBroker(paths) {
163
+ const broker = await resolveBrokerCommand(paths);
164
+ const child = spawn(broker.command, broker.args, {
165
+ cwd: broker.cwd,
166
+ detached: true,
167
+ env: {
168
+ ...process.env,
169
+ OPENTRAY_DAEMON_HOME: paths.homeDir,
170
+ OPENTRAY_DAEMON_PACKAGE_VERSION: paths.packageVersion,
171
+ OPENTRAY_DAEMON_CLI_ENTRYPOINT: cliEntrypoint
172
+ },
173
+ stdio: "ignore"
174
+ });
175
+ child.unref();
176
+ return child.pid ?? 0;
177
+ },
178
+ async stop(pid) {
179
+ process.kill(pid, "SIGTERM");
180
+ }
181
+ });
182
+ const startDaemon = async ({ paths, driver, lockTimeoutMs = 1e3 }) => {
183
+ await mkdir(paths.runtimeDir, { recursive: true });
184
+ const lock = await acquireLock(paths.lockFile, lockTimeoutMs);
185
+ try {
186
+ const existingPid = await readPid(paths.pidFile);
187
+ if (existingPid !== void 0 && await driver.isAlive(existingPid)) return {
188
+ status: "already-running",
189
+ pid: existingPid,
190
+ paths
191
+ };
192
+ await cleanupRuntimeFiles(paths);
193
+ const pid = await driver.spawnBroker(paths);
194
+ if (!Number.isInteger(pid) || pid <= 0) throw new Error("daemon broker process did not expose a valid pid");
195
+ await writeFile(paths.pidFile, `${pid}\n`, "utf8");
196
+ await waitForReadyFile(paths.readyFile);
197
+ return {
198
+ status: "started",
199
+ pid,
200
+ paths
201
+ };
202
+ } finally {
203
+ await lock.release();
204
+ }
205
+ };
206
+ const stopDaemon = async ({ paths, driver }) => {
207
+ const pid = await readPid(paths.pidFile);
208
+ if (pid === void 0 || !await driver.isAlive(pid)) {
209
+ await cleanupRuntimeFiles(paths);
210
+ return {
211
+ status: "not-running",
212
+ paths
213
+ };
214
+ }
215
+ await driver.stop(pid);
216
+ await waitUntilStopped(driver, pid);
217
+ await cleanupRuntimeFiles(paths);
218
+ return {
219
+ status: "stopped",
220
+ pid,
221
+ paths
222
+ };
223
+ };
224
+ const restartDaemon = async (options) => {
225
+ await stopDaemon({
226
+ paths: options.paths,
227
+ driver: options.driver
228
+ });
229
+ return startDaemon(options);
230
+ };
231
+ const inspectDaemon = async ({ paths, driver }) => {
232
+ const pid = await readPid(paths.pidFile);
233
+ if (pid !== void 0 && await driver.isAlive(pid)) return {
234
+ status: "running",
235
+ pid,
236
+ paths
237
+ };
238
+ await cleanupRuntimeFiles(paths);
239
+ return pid === void 0 ? {
240
+ status: "not-running",
241
+ paths
242
+ } : {
243
+ status: "not-running",
244
+ paths,
245
+ stalePid: pid
246
+ };
247
+ };
248
+ const readPid = async (pidFile) => {
249
+ try {
250
+ const content = await readFile(pidFile, "utf8");
251
+ const pid = Number.parseInt(content.trim(), 10);
252
+ return Number.isInteger(pid) && pid > 0 ? pid : void 0;
253
+ } catch (error) {
254
+ if (isNodeError(error) && error.code === "ENOENT") return;
255
+ throw error;
256
+ }
257
+ };
258
+ const cleanupRuntimeFiles = async (paths) => {
259
+ await rm(paths.readyFile, { force: true });
260
+ await rm(paths.pidFile, { force: true });
261
+ if (process.platform !== "win32") await rm(paths.endpoint, { force: true });
262
+ };
263
+ const waitUntilStopped = async (driver, pid) => {
264
+ for (let attempt = 0; attempt < 20; attempt += 1) {
265
+ if (!await driver.isAlive(pid)) return;
266
+ await sleep(50);
267
+ }
268
+ };
269
+ const waitForReadyFile = async (readyFile) => {
270
+ for (let attempt = 0; attempt < 40; attempt += 1) try {
271
+ await readFile(readyFile, "utf8");
272
+ return;
273
+ } catch (error) {
274
+ if (!isNodeError(error) || error.code !== "ENOENT") throw error;
275
+ await sleep(50);
276
+ }
277
+ throw new Error(`timed out waiting for daemon readiness: ${readyFile}`);
278
+ };
279
+ const acquireLock = async (lockFile, timeoutMs) => {
280
+ const deadline = Date.now() + timeoutMs;
281
+ while (Date.now() <= deadline) try {
282
+ const handle = await open(lockFile, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY);
283
+ await handle.writeFile(`${process.pid}\n`, "utf8");
284
+ return { async release() {
285
+ await handle.close();
286
+ await unlink(lockFile).catch((error) => {
287
+ if (!isNodeError(error) || error.code !== "ENOENT") throw error;
288
+ });
289
+ } };
290
+ } catch (error) {
291
+ if (isNodeError(error) && error.code === "EEXIST") {
292
+ await sleep(25);
293
+ continue;
294
+ }
295
+ throw error;
296
+ }
297
+ throw new Error(`timed out acquiring daemon lock: ${lockFile}`);
298
+ };
299
+ const isProcessAlive = (pid) => {
300
+ try {
301
+ process.kill(pid, 0);
302
+ return true;
303
+ } catch {
304
+ return false;
305
+ }
306
+ };
307
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
308
+ const isNodeError = (error) => error instanceof Error && "code" in error;
309
+ //#endregion
310
+ //#region src/daemon/package-version.ts
311
+ const readPackageVersion = async (packageJsonUrl) => {
312
+ const value = JSON.parse(await readFile(packageJsonUrl, "utf8"));
313
+ if (!isPackageJsonWithVersion(value)) throw new Error(`package.json is missing a string version: ${packageJsonUrl.href}`);
314
+ return value.version;
315
+ };
316
+ const isPackageJsonWithVersion = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && "version" in value && typeof value.version === "string";
317
+ //#endregion
318
+ //#region src/daemon/paths.ts
319
+ const resolveDaemonPaths = ({ homeDir, packageVersion, platform = process.platform }) => {
320
+ const identity = createBrokerEndpointIdentity({ packageVersion });
321
+ const normalizedHome = homeDir.replace(/[\\/]+$/u, "");
322
+ const stateRoot = `${normalizedHome}/.opentray/${identity.packageVersion}`;
323
+ const runtimeDir = `${stateRoot}/runtime`;
324
+ const endpoint = platform === "win32" ? formatWindowsPipeName(identity) : formatUnixSocketPath(normalizedHome, identity);
325
+ return {
326
+ homeDir: normalizedHome,
327
+ packageVersion: identity.packageVersion,
328
+ protocolVersion: identity.protocolVersion,
329
+ stateRoot,
330
+ runtimeDir,
331
+ endpoint,
332
+ pidFile: `${runtimeDir}/broker.pid`,
333
+ lockFile: `${runtimeDir}/broker.lock`,
334
+ readyFile: `${runtimeDir}/ready.json`
335
+ };
336
+ };
337
+ //#endregion
338
+ //#region src/local-broker.ts
339
+ const packageJsonUrl = new URL("../package.json", import.meta.url);
340
+ const connectLocalBroker = async (options = {}) => {
341
+ const packageVersion = options.packageVersion ?? await readPackageVersion(packageJsonUrl);
342
+ const clientVersion = options.clientVersion ?? packageVersion;
343
+ const paths = resolveDaemonPaths({
344
+ homeDir: options.homeDir ?? process.env.OPENTRAY_HOME ?? homedir(),
345
+ packageVersion
346
+ });
347
+ const endpoint = options.endpoint ?? paths.endpoint;
348
+ const autoStart = options.autoStart ?? endpoint === paths.endpoint;
349
+ if (autoStart && endpoint !== paths.endpoint) throw new Error("local broker autoStart requires the derived same-version endpoint");
350
+ if (autoStart) {
351
+ const cliEntrypoint = options.cliEntrypoint ?? resolveCliEntrypoint();
352
+ await startDaemon({
353
+ paths,
354
+ driver: options.daemonDriver ?? createNodeDaemonDriver(cliEntrypoint)
355
+ });
356
+ }
357
+ const connection = new LocalBrokerConnection(await connectSocket(endpoint), endpoint);
358
+ await connection.init(clientVersion, options.protocolVersion ?? PROTOCOL_VERSION);
359
+ return connection;
360
+ };
361
+ var LocalBrokerConnection = class {
362
+ socket;
363
+ endpoint;
364
+ leaseId = "";
365
+ buffer = "";
366
+ listeners = /* @__PURE__ */ new Set();
367
+ pending = /* @__PURE__ */ new Map();
368
+ ready;
369
+ constructor(socket, endpoint) {
370
+ this.socket = socket;
371
+ this.endpoint = endpoint;
372
+ socket.setEncoding("utf8");
373
+ socket.on("data", (chunk) => {
374
+ this.consume(String(chunk));
375
+ });
376
+ socket.on("error", (error) => {
377
+ this.rejectAll(error);
378
+ });
379
+ socket.on("close", () => {
380
+ this.rejectAll(/* @__PURE__ */ new Error("broker connection closed"));
381
+ });
382
+ }
383
+ async init(clientVersion, protocolVersion) {
384
+ const ready = new Promise((resolve, reject) => {
385
+ this.ready = {
386
+ resolve,
387
+ reject
388
+ };
389
+ });
390
+ this.write({
391
+ type: "init",
392
+ protocolVersion,
393
+ clientVersion
394
+ });
395
+ const frame = await ready;
396
+ this.leaseId = frame.leaseId;
397
+ }
398
+ async request(frame) {
399
+ if (this.pending.has(frame.requestId)) throw new Error(`duplicate requestId: ${frame.requestId}`);
400
+ const response = new Promise((resolve, reject) => {
401
+ this.pending.set(frame.requestId, {
402
+ resolve,
403
+ reject
404
+ });
405
+ });
406
+ this.write(frame);
407
+ return response;
408
+ }
409
+ onEvent(listener) {
410
+ this.listeners.add(listener);
411
+ return () => {
412
+ this.listeners.delete(listener);
413
+ };
414
+ }
415
+ async close() {
416
+ this.write({ type: "exit" });
417
+ await new Promise((resolve) => {
418
+ this.socket.end(resolve);
419
+ });
420
+ }
421
+ consume(chunk) {
422
+ this.buffer += chunk;
423
+ while (true) {
424
+ const newline = this.buffer.indexOf("\n");
425
+ if (newline < 0) return;
426
+ const line = this.buffer.slice(0, newline).trim();
427
+ this.buffer = this.buffer.slice(newline + 1);
428
+ if (line.length > 0) this.dispatchLine(line);
429
+ }
430
+ }
431
+ dispatchLine(line) {
432
+ const parsed = parseServerFrame(line);
433
+ if (!parsed.ok || parsed.frame === void 0) {
434
+ this.rejectAll(new Error(parsed.error ?? "invalid server frame"));
435
+ return;
436
+ }
437
+ this.dispatchFrame(parsed.frame);
438
+ }
439
+ dispatchFrame(frame) {
440
+ if (frame.type === "ready") {
441
+ this.ready?.resolve(frame);
442
+ this.ready = void 0;
443
+ return;
444
+ }
445
+ if (frame.type === "error") {
446
+ const error = /* @__PURE__ */ new Error(`${frame.code}: ${frame.message}`);
447
+ if (frame.requestId !== void 0) {
448
+ this.pending.get(frame.requestId)?.reject(error);
449
+ this.pending.delete(frame.requestId);
450
+ return;
451
+ }
452
+ this.ready?.reject(error);
453
+ this.ready = void 0;
454
+ return;
455
+ }
456
+ const requestId = responseRequestId(frame);
457
+ if (requestId !== void 0) {
458
+ this.pending.get(requestId)?.resolve(frame);
459
+ this.pending.delete(requestId);
460
+ return;
461
+ }
462
+ if (frame.type === "event" || frame.type === "ext-event") for (const listener of this.listeners) listener(frame);
463
+ }
464
+ write(frame) {
465
+ this.socket.write(`${JSON.stringify(frame)}\n`);
466
+ }
467
+ rejectAll(error) {
468
+ this.ready?.reject(error);
469
+ this.ready = void 0;
470
+ for (const pending of this.pending.values()) pending.reject(error);
471
+ this.pending.clear();
472
+ }
473
+ };
474
+ const connectSocket = (endpoint) => new Promise((resolve, reject) => {
475
+ const socket = createConnection(endpoint);
476
+ socket.once("connect", () => {
477
+ socket.off("error", reject);
478
+ resolve(socket);
479
+ });
480
+ socket.once("error", reject);
481
+ });
482
+ const responseRequestId = (frame) => {
483
+ switch (frame.type) {
484
+ case "surface-created":
485
+ case "default-surface":
486
+ case "tray-created":
487
+ case "ack":
488
+ case "daemon-health": return frame.requestId;
489
+ case "ready":
490
+ case "event":
491
+ case "ext-event":
492
+ case "error": return;
493
+ }
494
+ };
495
+ const resolveCliEntrypoint = () => {
496
+ const suffix = fileURLToPath(import.meta.url).endsWith(".ts") ? "cli.ts" : "cli.mjs";
497
+ return fileURLToPath(new URL(`./${suffix}`, import.meta.url));
498
+ };
499
+ //#endregion
500
+ export { inspectDaemon as a, stopDaemon as c, createNodeDaemonDriver as i, resolveDaemonPaths as n, restartDaemon as o, readPackageVersion as r, startDaemon as s, connectLocalBroker as t };
501
+
502
+ //# sourceMappingURL=local-broker-CXOpd9Z8.mjs.map