opentray 0.8.0 → 0.8.1
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 +54 -202
- package/dist/cli.d.mts +5 -7
- package/dist/cli.d.mts.map +1 -1
- package/dist/cli.mjs +22 -89
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +10 -12
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +578 -80
- package/dist/index.mjs.map +1 -1
- package/dist/native-runtime-Cn8BaffH.d.mts +136 -0
- package/dist/native-runtime-Cn8BaffH.d.mts.map +1 -0
- package/dist/node.d.mts +30 -45
- package/dist/node.d.mts.map +1 -1
- package/dist/node.mjs +16 -2
- package/dist/node.mjs.map +1 -0
- package/dist/runtime-binding-transport-UPunusBT.mjs +177 -0
- package/dist/runtime-binding-transport-UPunusBT.mjs.map +1 -0
- package/package.json +12 -10
- package/dist/client-C27qmRfr.d.mts +0 -102
- package/dist/client-C27qmRfr.d.mts.map +0 -1
- package/dist/local-broker-BMxjb-eO.mjs +0 -589
- package/dist/local-broker-BMxjb-eO.mjs.map +0 -1
|
@@ -1,589 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import { PROTOCOL_VERSION, createBrokerEndpointIdentity, formatUnixSocketPath, formatWindowsPipeName, parseServerFrame, sanitizeCallerLabel } from "@opentray/spec";
|
|
3
|
-
import { createConnection } from "node:net";
|
|
4
|
-
import { homedir } from "node:os";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { basename, dirname, join } from "node:path";
|
|
7
|
-
import { constants } from "node:fs";
|
|
8
|
-
import { access, chmod, mkdir, open, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
9
|
-
import { spawn } from "node:child_process";
|
|
10
|
-
//#region src/daemon/caller-label.ts
|
|
11
|
-
/**
|
|
12
|
-
* Derives a caller label using the precedence: explicit > npm_package_name >
|
|
13
|
-
* script basename > neutral default. The result is always sanitized to a
|
|
14
|
-
* filesystem- and process-safe component, so callers can use it directly in
|
|
15
|
-
* endpoint identity, runtime directories, and process titles.
|
|
16
|
-
*/
|
|
17
|
-
const resolveCallerLabel = (options = {}) => {
|
|
18
|
-
const env = options.env ?? process.env;
|
|
19
|
-
const argv = options.argv ?? process.argv;
|
|
20
|
-
const candidates = [];
|
|
21
|
-
if (options.explicit !== void 0 && options.explicit.length > 0) candidates.push(options.explicit);
|
|
22
|
-
const npmName = options.npmPackageName ?? env.npm_package_name;
|
|
23
|
-
if (typeof npmName === "string" && npmName.length > 0) candidates.push(npmName);
|
|
24
|
-
const scriptPath = options.scriptPath ?? argv[1];
|
|
25
|
-
if (typeof scriptPath === "string" && scriptPath.length > 0) candidates.push(basename(scriptPath));
|
|
26
|
-
for (const candidate of candidates) {
|
|
27
|
-
const sanitized = sanitizeCallerLabel(candidate);
|
|
28
|
-
if (sanitized.length > 0) return sanitized;
|
|
29
|
-
}
|
|
30
|
-
return sanitizeCallerLabel(void 0);
|
|
31
|
-
};
|
|
32
|
-
//#endregion
|
|
33
|
-
//#region src/daemon/native-target.ts
|
|
34
|
-
const resolveBrokerNativeTarget = (platform = process.platform, arch = process.arch) => {
|
|
35
|
-
const packageOs = platformToPackageOs(platform);
|
|
36
|
-
return {
|
|
37
|
-
packageName: `@opentray/${packageOs}-${normalizeArch(arch)}`,
|
|
38
|
-
binaryRelativePath: `bin/${packageOs === "windows" ? "opentray.exe" : "opentray"}`
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
const platformToPackageOs = (platform) => {
|
|
42
|
-
switch (platform) {
|
|
43
|
-
case "darwin":
|
|
44
|
-
case "linux": return platform;
|
|
45
|
-
case "win32": return "windows";
|
|
46
|
-
default: throw new MissingPlatformBrokerBinaryError(`unsupported OpenTray broker platform: ${platform}`, platform, process.arch);
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
const normalizeArch = (arch) => {
|
|
50
|
-
switch (arch) {
|
|
51
|
-
case "arm64":
|
|
52
|
-
case "x64": return arch;
|
|
53
|
-
default: throw new MissingPlatformBrokerBinaryError(`unsupported OpenTray broker architecture: ${arch}`, process.platform, arch);
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
var MissingPlatformBrokerBinaryError = class extends Error {
|
|
57
|
-
code = "OPENTRAY_MISSING_PLATFORM_BROKER_BINARY";
|
|
58
|
-
platform;
|
|
59
|
-
arch;
|
|
60
|
-
packageName;
|
|
61
|
-
binaryPath;
|
|
62
|
-
constructor(message, platform, arch, options = {}) {
|
|
63
|
-
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
64
|
-
this.name = "MissingPlatformBrokerBinaryError";
|
|
65
|
-
this.platform = platform;
|
|
66
|
-
this.arch = arch;
|
|
67
|
-
this.packageName = options.packageName;
|
|
68
|
-
this.binaryPath = options.binaryPath;
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
//#endregion
|
|
72
|
-
//#region src/daemon/broker-command.ts
|
|
73
|
-
const sourceUrl = import.meta.url;
|
|
74
|
-
const requireFromSource = createRequire(sourceUrl);
|
|
75
|
-
const resolveBrokerCommand = async (paths, options = {}) => {
|
|
76
|
-
const explicit = (options.env ?? process.env).OPENTRAY_BROKER_BIN;
|
|
77
|
-
if (explicit !== void 0 && explicit.length > 0) return commandForBinary(explicit, paths);
|
|
78
|
-
const sourceDir = options.sourceDir ?? dirname(fileURLToPath(sourceUrl));
|
|
79
|
-
const workspaceRoot = await (options.findWorkspaceRoot ?? findWorkspaceRoot)(sourceDir);
|
|
80
|
-
if (workspaceRoot !== void 0) return commandForBinary(await (options.ensureDevBrokerBinary ?? ensureDevBrokerBinary)(workspaceRoot), paths, workspaceRoot);
|
|
81
|
-
const platform = options.platform ?? process.platform;
|
|
82
|
-
const arch = options.arch ?? process.arch;
|
|
83
|
-
const installedBinary = await resolveInstalledBrokerBinary({
|
|
84
|
-
platform,
|
|
85
|
-
arch,
|
|
86
|
-
resolvePackageJson: options.resolvePackageJson ?? resolvePackageJson
|
|
87
|
-
});
|
|
88
|
-
if (installedBinary !== void 0) return commandForBinary(installedBinary, paths);
|
|
89
|
-
const target = resolveBrokerNativeTarget(platform, arch);
|
|
90
|
-
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 });
|
|
91
|
-
};
|
|
92
|
-
const commandForBinary = (binary, paths, cwd) => {
|
|
93
|
-
const command = {
|
|
94
|
-
command: binary,
|
|
95
|
-
args: [
|
|
96
|
-
"broker",
|
|
97
|
-
"--endpoint",
|
|
98
|
-
paths.endpoint,
|
|
99
|
-
"--ready-file",
|
|
100
|
-
paths.readyFile,
|
|
101
|
-
"--package-version",
|
|
102
|
-
paths.packageVersion,
|
|
103
|
-
"--protocol-version",
|
|
104
|
-
`${paths.protocolVersion}`,
|
|
105
|
-
"--caller-label",
|
|
106
|
-
paths.callerLabel
|
|
107
|
-
]
|
|
108
|
-
};
|
|
109
|
-
if (cwd !== void 0) command.cwd = cwd;
|
|
110
|
-
return command;
|
|
111
|
-
};
|
|
112
|
-
const resolveInstalledBrokerBinary = async ({ platform, arch, resolvePackageJson }) => {
|
|
113
|
-
const target = resolveBrokerNativeTarget(platform, arch);
|
|
114
|
-
const packageJsonPath = resolvePackageJson(target.packageName);
|
|
115
|
-
if (packageJsonPath === void 0) return;
|
|
116
|
-
const binary = join(dirname(packageJsonPath), target.binaryRelativePath);
|
|
117
|
-
if (!await exists(binary)) throw new MissingPlatformBrokerBinaryError(`OpenTray broker package ${target.packageName} is installed but missing ${target.binaryRelativePath}`, platform, arch, {
|
|
118
|
-
packageName: target.packageName,
|
|
119
|
-
binaryPath: binary
|
|
120
|
-
});
|
|
121
|
-
if (platform !== "win32") await chmod(binary, 493);
|
|
122
|
-
return binary;
|
|
123
|
-
};
|
|
124
|
-
const resolvePackageJson = (packageName) => {
|
|
125
|
-
try {
|
|
126
|
-
return requireFromSource.resolve(`${packageName}/package.json`);
|
|
127
|
-
} catch (error) {
|
|
128
|
-
if (isNodeError$1(error) && error.code === "MODULE_NOT_FOUND") return;
|
|
129
|
-
throw error;
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
const findWorkspaceRoot = async (start) => {
|
|
133
|
-
let current = start;
|
|
134
|
-
while (true) {
|
|
135
|
-
if (await exists(join(current, "Cargo.toml"))) {
|
|
136
|
-
if (await exists(join(current, "crates", "opentray-bin", "Cargo.toml"))) return current;
|
|
137
|
-
}
|
|
138
|
-
const parent = dirname(current);
|
|
139
|
-
if (parent === current) return;
|
|
140
|
-
current = parent;
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
const resolveDevBrokerBinaryPath = (workspaceRoot, platform = process.platform) => join(workspaceRoot, "target", "debug", platform === "win32" ? "opentray.exe" : "opentray");
|
|
144
|
-
const terminateWorkspaceDevBrokerProcess = async (workspaceRoot, platform = process.platform) => {
|
|
145
|
-
if (platform !== "win32") return;
|
|
146
|
-
await terminateWindowsProcessByExecutable(resolveDevBrokerBinaryPath(workspaceRoot, platform));
|
|
147
|
-
};
|
|
148
|
-
const ensureDevBrokerBinary = async (workspaceRoot) => {
|
|
149
|
-
const binary = resolveDevBrokerBinaryPath(workspaceRoot);
|
|
150
|
-
await terminateWorkspaceDevBrokerProcess(workspaceRoot);
|
|
151
|
-
await runCargoBuild(workspaceRoot);
|
|
152
|
-
return binary;
|
|
153
|
-
};
|
|
154
|
-
const runCargoBuild = (workspaceRoot) => new Promise((resolve, reject) => {
|
|
155
|
-
const child = spawn("cargo", [
|
|
156
|
-
"build",
|
|
157
|
-
"-p",
|
|
158
|
-
"opentray-bin"
|
|
159
|
-
], {
|
|
160
|
-
cwd: workspaceRoot,
|
|
161
|
-
stdio: process.env.OPENTRAY_BROKER_BUILD_LOGS === "1" ? "inherit" : "ignore"
|
|
162
|
-
});
|
|
163
|
-
child.once("error", reject);
|
|
164
|
-
child.once("exit", (code) => {
|
|
165
|
-
if (code === 0) {
|
|
166
|
-
resolve();
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
reject(/* @__PURE__ */ new Error(`cargo build -p opentray-bin failed with code ${code ?? "unknown"}`));
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
const exists = async (path) => {
|
|
173
|
-
try {
|
|
174
|
-
await access(path, constants.F_OK);
|
|
175
|
-
return true;
|
|
176
|
-
} catch {
|
|
177
|
-
return false;
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
const terminateWindowsProcessByExecutable = (binary) => new Promise((resolve, reject) => {
|
|
181
|
-
const child = spawn("powershell.exe", [
|
|
182
|
-
"-NoProfile",
|
|
183
|
-
"-NonInteractive",
|
|
184
|
-
"-ExecutionPolicy",
|
|
185
|
-
"Bypass",
|
|
186
|
-
"-Command",
|
|
187
|
-
String.raw`
|
|
188
|
-
$ErrorActionPreference = 'Stop'
|
|
189
|
-
$target = [System.IO.Path]::GetFullPath(${powerShellString(binary)})
|
|
190
|
-
$processes = @(Get-CimInstance Win32_Process -Filter "Name = 'opentray.exe'" | Where-Object {
|
|
191
|
-
$_.ExecutablePath -and ([System.IO.Path]::GetFullPath($_.ExecutablePath) -ieq $target)
|
|
192
|
-
})
|
|
193
|
-
foreach ($process in $processes) {
|
|
194
|
-
Stop-Process -Id $process.ProcessId -Force -ErrorAction Stop
|
|
195
|
-
}
|
|
196
|
-
foreach ($process in $processes) {
|
|
197
|
-
Wait-Process -Id $process.ProcessId -Timeout 5 -ErrorAction SilentlyContinue
|
|
198
|
-
}
|
|
199
|
-
`
|
|
200
|
-
], { stdio: [
|
|
201
|
-
"ignore",
|
|
202
|
-
"ignore",
|
|
203
|
-
"pipe"
|
|
204
|
-
] });
|
|
205
|
-
let stderr = "";
|
|
206
|
-
child.stderr?.on("data", (chunk) => {
|
|
207
|
-
stderr += String(chunk);
|
|
208
|
-
});
|
|
209
|
-
child.once("error", reject);
|
|
210
|
-
child.once("exit", (code) => {
|
|
211
|
-
if (code === 0) {
|
|
212
|
-
resolve();
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
reject(/* @__PURE__ */ new Error(`failed to stop running dev broker before cargo build: ${stderr.trim() || code}`));
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
const powerShellString = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
219
|
-
const isNodeError$1 = (error) => error instanceof Error && "code" in error;
|
|
220
|
-
//#endregion
|
|
221
|
-
//#region src/daemon/lifecycle.ts
|
|
222
|
-
const DAEMON_STDIO_ENV = "OPENTRAY_DAEMON_STDIO";
|
|
223
|
-
const createNodeDaemonDriver = (cliEntrypoint) => ({
|
|
224
|
-
async isAlive(pid) {
|
|
225
|
-
return isProcessAlive(pid);
|
|
226
|
-
},
|
|
227
|
-
async spawnBroker(paths) {
|
|
228
|
-
const broker = await resolveBrokerCommand(paths);
|
|
229
|
-
const child = spawn(broker.command, broker.args, {
|
|
230
|
-
cwd: broker.cwd,
|
|
231
|
-
detached: true,
|
|
232
|
-
env: {
|
|
233
|
-
...process.env,
|
|
234
|
-
OPENTRAY_DAEMON_HOME: paths.homeDir,
|
|
235
|
-
OPENTRAY_DAEMON_PACKAGE_VERSION: paths.packageVersion,
|
|
236
|
-
OPENTRAY_DAEMON_CLI_ENTRYPOINT: cliEntrypoint,
|
|
237
|
-
OPENTRAY_DAEMON_CALLER_LABEL: paths.callerLabel
|
|
238
|
-
},
|
|
239
|
-
stdio: resolveBrokerStdio(process.env[DAEMON_STDIO_ENV])
|
|
240
|
-
});
|
|
241
|
-
child.unref();
|
|
242
|
-
return child.pid ?? 0;
|
|
243
|
-
},
|
|
244
|
-
async stop(pid) {
|
|
245
|
-
process.kill(pid, "SIGTERM");
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
const resolveBrokerStdio = (value) => {
|
|
249
|
-
return value === "inherit" ? "inherit" : "ignore";
|
|
250
|
-
};
|
|
251
|
-
const startDaemon = async ({ paths, driver, lockTimeoutMs = 1e3 }) => {
|
|
252
|
-
await mkdir(paths.runtimeDir, { recursive: true });
|
|
253
|
-
const lock = await acquireLock(paths.lockFile, lockTimeoutMs);
|
|
254
|
-
try {
|
|
255
|
-
const existingPid = await readPid(paths.pidFile);
|
|
256
|
-
if (existingPid !== void 0 && await driver.isAlive(existingPid)) return {
|
|
257
|
-
status: "already-running",
|
|
258
|
-
pid: existingPid,
|
|
259
|
-
paths
|
|
260
|
-
};
|
|
261
|
-
await cleanupRuntimeFiles(paths);
|
|
262
|
-
const pid = await driver.spawnBroker(paths);
|
|
263
|
-
if (!Number.isInteger(pid) || pid <= 0) throw new Error("daemon broker process did not expose a valid pid");
|
|
264
|
-
await writeFile(paths.pidFile, `${pid}\n`, "utf8");
|
|
265
|
-
await waitForReadyFile(paths.readyFile, driver, pid);
|
|
266
|
-
return {
|
|
267
|
-
status: "started",
|
|
268
|
-
pid,
|
|
269
|
-
paths
|
|
270
|
-
};
|
|
271
|
-
} finally {
|
|
272
|
-
await lock.release();
|
|
273
|
-
}
|
|
274
|
-
};
|
|
275
|
-
const stopDaemon = async ({ paths, driver }) => {
|
|
276
|
-
const pid = await readPid(paths.pidFile);
|
|
277
|
-
if (pid === void 0 || !await driver.isAlive(pid)) {
|
|
278
|
-
await cleanupRuntimeFiles(paths);
|
|
279
|
-
return {
|
|
280
|
-
status: "not-running",
|
|
281
|
-
paths
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
await driver.stop(pid);
|
|
285
|
-
await waitUntilStopped(driver, pid);
|
|
286
|
-
await cleanupRuntimeFiles(paths);
|
|
287
|
-
return {
|
|
288
|
-
status: "stopped",
|
|
289
|
-
pid,
|
|
290
|
-
paths
|
|
291
|
-
};
|
|
292
|
-
};
|
|
293
|
-
const restartDaemon = async (options) => {
|
|
294
|
-
await stopDaemon({
|
|
295
|
-
paths: options.paths,
|
|
296
|
-
driver: options.driver
|
|
297
|
-
});
|
|
298
|
-
return startDaemon(options);
|
|
299
|
-
};
|
|
300
|
-
const inspectDaemon = async ({ paths, driver }) => {
|
|
301
|
-
const pid = await readPid(paths.pidFile);
|
|
302
|
-
if (pid !== void 0 && await driver.isAlive(pid)) return {
|
|
303
|
-
status: "running",
|
|
304
|
-
pid,
|
|
305
|
-
paths
|
|
306
|
-
};
|
|
307
|
-
await cleanupRuntimeFiles(paths);
|
|
308
|
-
return pid === void 0 ? {
|
|
309
|
-
status: "not-running",
|
|
310
|
-
paths
|
|
311
|
-
} : {
|
|
312
|
-
status: "not-running",
|
|
313
|
-
paths,
|
|
314
|
-
stalePid: pid
|
|
315
|
-
};
|
|
316
|
-
};
|
|
317
|
-
const readPid = async (pidFile) => {
|
|
318
|
-
try {
|
|
319
|
-
const content = await readFile(pidFile, "utf8");
|
|
320
|
-
const pid = Number.parseInt(content.trim(), 10);
|
|
321
|
-
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
322
|
-
} catch (error) {
|
|
323
|
-
if (isNodeError(error) && error.code === "ENOENT") return;
|
|
324
|
-
throw error;
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
const cleanupRuntimeFiles = async (paths) => {
|
|
328
|
-
await rm(paths.readyFile, { force: true });
|
|
329
|
-
await rm(paths.pidFile, { force: true });
|
|
330
|
-
if (process.platform !== "win32") await rm(paths.endpoint, { force: true });
|
|
331
|
-
};
|
|
332
|
-
const waitUntilStopped = async (driver, pid) => {
|
|
333
|
-
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
334
|
-
if (!await driver.isAlive(pid)) return;
|
|
335
|
-
await sleep(50);
|
|
336
|
-
}
|
|
337
|
-
};
|
|
338
|
-
const waitForReadyFile = async (readyFile, driver, pid) => {
|
|
339
|
-
for (let attempt = 0; attempt < 40; attempt += 1) try {
|
|
340
|
-
await readFile(readyFile, "utf8");
|
|
341
|
-
return;
|
|
342
|
-
} catch (error) {
|
|
343
|
-
if (!isNodeError(error) || error.code !== "ENOENT") throw error;
|
|
344
|
-
if (!await driver.isAlive(pid)) throw new Error(`daemon broker exited before readiness: pid=${pid}; readyFile=${readyFile}; set ${DAEMON_STDIO_ENV}=inherit to inspect broker stderr`);
|
|
345
|
-
await sleep(50);
|
|
346
|
-
}
|
|
347
|
-
throw new Error(`timed out waiting for daemon readiness: ${readyFile}`);
|
|
348
|
-
};
|
|
349
|
-
const acquireLock = async (lockFile, timeoutMs) => {
|
|
350
|
-
const deadline = Date.now() + timeoutMs;
|
|
351
|
-
while (Date.now() <= deadline) try {
|
|
352
|
-
const handle = await open(lockFile, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY);
|
|
353
|
-
await handle.writeFile(`${process.pid}\n`, "utf8");
|
|
354
|
-
return { async release() {
|
|
355
|
-
await handle.close();
|
|
356
|
-
await unlink(lockFile).catch((error) => {
|
|
357
|
-
if (!isNodeError(error) || error.code !== "ENOENT") throw error;
|
|
358
|
-
});
|
|
359
|
-
} };
|
|
360
|
-
} catch (error) {
|
|
361
|
-
if (isNodeError(error) && error.code === "EEXIST") {
|
|
362
|
-
await sleep(25);
|
|
363
|
-
continue;
|
|
364
|
-
}
|
|
365
|
-
throw error;
|
|
366
|
-
}
|
|
367
|
-
throw new Error(`timed out acquiring daemon lock: ${lockFile}`);
|
|
368
|
-
};
|
|
369
|
-
const isProcessAlive = (pid) => {
|
|
370
|
-
try {
|
|
371
|
-
process.kill(pid, 0);
|
|
372
|
-
return true;
|
|
373
|
-
} catch {
|
|
374
|
-
return false;
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
378
|
-
const isNodeError = (error) => error instanceof Error && "code" in error;
|
|
379
|
-
//#endregion
|
|
380
|
-
//#region src/daemon/package-version.ts
|
|
381
|
-
const readPackageVersion = async (packageJsonUrl) => {
|
|
382
|
-
const value = JSON.parse(await readFile(packageJsonUrl, "utf8"));
|
|
383
|
-
if (!isPackageJsonWithVersion(value)) throw new Error(`package.json is missing a string version: ${packageJsonUrl.href}`);
|
|
384
|
-
return value.version;
|
|
385
|
-
};
|
|
386
|
-
const isPackageJsonWithVersion = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && "version" in value && typeof value.version === "string";
|
|
387
|
-
//#endregion
|
|
388
|
-
//#region src/daemon/paths.ts
|
|
389
|
-
const resolveDaemonPaths = ({ homeDir, packageVersion, callerLabel, platform = process.platform }) => {
|
|
390
|
-
const identity = createBrokerEndpointIdentity(callerLabel === void 0 ? { packageVersion } : {
|
|
391
|
-
packageVersion,
|
|
392
|
-
callerLabel
|
|
393
|
-
});
|
|
394
|
-
const normalizedHome = homeDir.replace(/[\\/]+$/u, "");
|
|
395
|
-
const stateRoot = `${normalizedHome}/.opentray/${identity.packageVersion}/${identity.callerLabel}`;
|
|
396
|
-
const runtimeDir = `${stateRoot}/runtime`;
|
|
397
|
-
const endpoint = platform === "win32" ? formatWindowsPipeName(identity) : formatUnixSocketPath(normalizedHome, identity);
|
|
398
|
-
return {
|
|
399
|
-
homeDir: normalizedHome,
|
|
400
|
-
packageVersion: identity.packageVersion,
|
|
401
|
-
callerLabel: identity.callerLabel,
|
|
402
|
-
protocolVersion: identity.protocolVersion,
|
|
403
|
-
stateRoot,
|
|
404
|
-
runtimeDir,
|
|
405
|
-
endpoint,
|
|
406
|
-
pidFile: `${runtimeDir}/broker.pid`,
|
|
407
|
-
lockFile: `${runtimeDir}/broker.lock`,
|
|
408
|
-
readyFile: `${runtimeDir}/ready.json`
|
|
409
|
-
};
|
|
410
|
-
};
|
|
411
|
-
//#endregion
|
|
412
|
-
//#region src/local-broker.ts
|
|
413
|
-
const packageJsonUrl = new URL("../package.json", import.meta.url);
|
|
414
|
-
const connectLocalBroker = async (options = {}) => {
|
|
415
|
-
const packageVersion = options.packageVersion ?? await readPackageVersion(packageJsonUrl);
|
|
416
|
-
const clientVersion = options.clientVersion ?? packageVersion;
|
|
417
|
-
const callerLabel = options.callerLabel ?? resolveCallerLabel();
|
|
418
|
-
const paths = resolveDaemonPaths({
|
|
419
|
-
homeDir: options.homeDir ?? process.env.OPENTRAY_HOME ?? homedir(),
|
|
420
|
-
packageVersion,
|
|
421
|
-
callerLabel
|
|
422
|
-
});
|
|
423
|
-
const endpoint = options.endpoint ?? paths.endpoint;
|
|
424
|
-
const autoStart = options.autoStart ?? endpoint === paths.endpoint;
|
|
425
|
-
if (autoStart && endpoint !== paths.endpoint) throw new Error("local broker autoStart requires the derived same-version endpoint");
|
|
426
|
-
if (autoStart) {
|
|
427
|
-
const cliEntrypoint = options.cliEntrypoint ?? resolveCliEntrypoint();
|
|
428
|
-
await startDaemon({
|
|
429
|
-
paths,
|
|
430
|
-
driver: options.daemonDriver ?? createNodeDaemonDriver(cliEntrypoint)
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
const connection = new LocalBrokerConnection(await connectSocket(endpoint), endpoint, callerLabel);
|
|
434
|
-
await connection.init(clientVersion, options.protocolVersion ?? PROTOCOL_VERSION);
|
|
435
|
-
return connection;
|
|
436
|
-
};
|
|
437
|
-
var LocalBrokerConnection = class {
|
|
438
|
-
socket;
|
|
439
|
-
endpoint;
|
|
440
|
-
callerLabel;
|
|
441
|
-
sessionId = "";
|
|
442
|
-
/** @deprecated Use `sessionId`. */
|
|
443
|
-
leaseId = "";
|
|
444
|
-
buffer = "";
|
|
445
|
-
listeners = /* @__PURE__ */ new Set();
|
|
446
|
-
pending = /* @__PURE__ */ new Map();
|
|
447
|
-
ready;
|
|
448
|
-
constructor(socket, endpoint, callerLabel) {
|
|
449
|
-
this.socket = socket;
|
|
450
|
-
this.endpoint = endpoint;
|
|
451
|
-
this.callerLabel = callerLabel;
|
|
452
|
-
socket.setEncoding("utf8");
|
|
453
|
-
socket.on("data", (chunk) => {
|
|
454
|
-
this.consume(String(chunk));
|
|
455
|
-
});
|
|
456
|
-
socket.on("error", (error) => {
|
|
457
|
-
this.rejectAll(error);
|
|
458
|
-
});
|
|
459
|
-
socket.on("close", () => {
|
|
460
|
-
this.rejectAll(/* @__PURE__ */ new Error("broker connection closed"));
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
async init(clientVersion, protocolVersion) {
|
|
464
|
-
const ready = new Promise((resolve, reject) => {
|
|
465
|
-
this.ready = {
|
|
466
|
-
resolve,
|
|
467
|
-
reject
|
|
468
|
-
};
|
|
469
|
-
});
|
|
470
|
-
this.write({
|
|
471
|
-
type: "init",
|
|
472
|
-
protocolVersion,
|
|
473
|
-
clientVersion
|
|
474
|
-
});
|
|
475
|
-
const frame = await ready;
|
|
476
|
-
this.sessionId = frame.sessionId;
|
|
477
|
-
this.leaseId = frame.sessionId;
|
|
478
|
-
}
|
|
479
|
-
async request(frame) {
|
|
480
|
-
if (this.pending.has(frame.requestId)) throw new Error(`duplicate requestId: ${frame.requestId}`);
|
|
481
|
-
const response = new Promise((resolve, reject) => {
|
|
482
|
-
this.pending.set(frame.requestId, {
|
|
483
|
-
resolve,
|
|
484
|
-
reject
|
|
485
|
-
});
|
|
486
|
-
});
|
|
487
|
-
this.write(frame);
|
|
488
|
-
return response;
|
|
489
|
-
}
|
|
490
|
-
onEvent(listener) {
|
|
491
|
-
this.listeners.add(listener);
|
|
492
|
-
return () => {
|
|
493
|
-
this.listeners.delete(listener);
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
async close() {
|
|
497
|
-
this.write({ type: "exit" });
|
|
498
|
-
await new Promise((resolve) => {
|
|
499
|
-
this.socket.end(resolve);
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
consume(chunk) {
|
|
503
|
-
this.buffer += chunk;
|
|
504
|
-
while (true) {
|
|
505
|
-
const newline = this.buffer.indexOf("\n");
|
|
506
|
-
if (newline < 0) return;
|
|
507
|
-
const line = this.buffer.slice(0, newline).trim();
|
|
508
|
-
this.buffer = this.buffer.slice(newline + 1);
|
|
509
|
-
if (line.length > 0) this.dispatchLine(line);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
dispatchLine(line) {
|
|
513
|
-
const parsed = parseServerFrame(line);
|
|
514
|
-
if (!parsed.ok || parsed.frame === void 0) {
|
|
515
|
-
this.rejectAll(/* @__PURE__ */ new Error(`${parsed.error ?? "invalid server frame"}: ${line}`));
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
this.dispatchFrame(parsed.frame);
|
|
519
|
-
}
|
|
520
|
-
dispatchFrame(frame) {
|
|
521
|
-
if (frame.type === "ready") {
|
|
522
|
-
this.ready?.resolve(frame);
|
|
523
|
-
this.ready = void 0;
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
if (frame.type === "error") {
|
|
527
|
-
const error = /* @__PURE__ */ new Error(`${frame.code}: ${frame.message}`);
|
|
528
|
-
if (frame.requestId !== void 0) {
|
|
529
|
-
this.pending.get(frame.requestId)?.reject(error);
|
|
530
|
-
this.pending.delete(frame.requestId);
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
if (this.ready) {
|
|
534
|
-
this.ready.reject(error);
|
|
535
|
-
this.ready = void 0;
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
this.rejectAll(error);
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
const requestId = responseRequestId(frame);
|
|
542
|
-
if (requestId !== void 0) {
|
|
543
|
-
this.pending.get(requestId)?.resolve(frame);
|
|
544
|
-
this.pending.delete(requestId);
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
if (frame.type === "event" || frame.type === "ext-event") for (const listener of this.listeners) listener(frame);
|
|
548
|
-
}
|
|
549
|
-
write(frame) {
|
|
550
|
-
this.socket.write(`${JSON.stringify(frame)}\n`);
|
|
551
|
-
}
|
|
552
|
-
rejectAll(error) {
|
|
553
|
-
this.ready?.reject(error);
|
|
554
|
-
this.ready = void 0;
|
|
555
|
-
for (const pending of this.pending.values()) pending.reject(error);
|
|
556
|
-
this.pending.clear();
|
|
557
|
-
}
|
|
558
|
-
};
|
|
559
|
-
const connectSocket = (endpoint) => new Promise((resolve, reject) => {
|
|
560
|
-
const socket = createConnection(endpoint);
|
|
561
|
-
socket.once("connect", () => {
|
|
562
|
-
socket.off("error", reject);
|
|
563
|
-
resolve(socket);
|
|
564
|
-
});
|
|
565
|
-
socket.once("error", reject);
|
|
566
|
-
});
|
|
567
|
-
const responseRequestId = (frame) => {
|
|
568
|
-
switch (frame.type) {
|
|
569
|
-
case "space-created":
|
|
570
|
-
case "default-space":
|
|
571
|
-
case "tray-created":
|
|
572
|
-
case "tray-bounds":
|
|
573
|
-
case "ack":
|
|
574
|
-
case "ext-command-result":
|
|
575
|
-
case "daemon-health": return frame.requestId;
|
|
576
|
-
case "ready":
|
|
577
|
-
case "event":
|
|
578
|
-
case "ext-event":
|
|
579
|
-
case "error": return;
|
|
580
|
-
}
|
|
581
|
-
};
|
|
582
|
-
const resolveCliEntrypoint = () => {
|
|
583
|
-
const suffix = fileURLToPath(import.meta.url).endsWith(".ts") ? "cli.ts" : "cli.mjs";
|
|
584
|
-
return fileURLToPath(new URL(`./${suffix}`, import.meta.url));
|
|
585
|
-
};
|
|
586
|
-
//#endregion
|
|
587
|
-
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 };
|
|
588
|
-
|
|
589
|
-
//# sourceMappingURL=local-broker-BMxjb-eO.mjs.map
|