opentray 0.1.0 → 0.2.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 +37 -0
- package/dist/cli.d.mts +19 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +484 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/client-BfXT-Zz9.mjs +86 -0
- package/dist/client-BfXT-Zz9.mjs.map +1 -0
- package/dist/client-COyxXT9C.d.mts +29 -0
- package/dist/client-COyxXT9C.d.mts.map +1 -0
- package/dist/index.d.mts +3 -24
- package/dist/index.mjs +3 -46
- package/dist/local-broker-CXOpd9Z8.mjs +502 -0
- package/dist/local-broker-CXOpd9Z8.mjs.map +1 -0
- package/dist/node.d.mts +45 -0
- package/dist/node.d.mts.map +1 -0
- package/dist/node.mjs +2 -0
- package/package.json +18 -9
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-BfXT-Zz9.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import {\n PROTOCOL_VERSION,\n type ClientFrame,\n type ClientRequestFrame,\n type RequestId,\n type ServerFrame,\n type SurfaceOptions,\n type SurfaceRef,\n type TrayId,\n type TrayOptions,\n} from \"@opentray/spec\";\n\nexport interface OpenTrayTransport {\n request(frame: ClientRequestFrame): Promise<ServerFrame>;\n}\n\nexport interface OpenTrayClient {\n createSurface(options: SurfaceOptions): Promise<SurfaceHandle>;\n}\n\nexport interface SurfaceHandle {\n surface: SurfaceRef;\n createTray(options: TrayOptions): Promise<TrayHandle>;\n}\n\nexport interface TrayHandle {\n surface: SurfaceRef;\n trayId: TrayId;\n commandExtension(ext: string, data: unknown): Promise<void>;\n destroy(): Promise<void>;\n}\n\nexport const createInitFrame = (clientVersion: string): ClientFrame => ({\n type: \"init\",\n protocolVersion: PROTOCOL_VERSION,\n clientVersion,\n});\n\nexport interface CreateClientOptions {\n requestIdPrefix?: string;\n}\n\nexport const createClient = (\n transport: OpenTrayTransport,\n options: CreateClientOptions = {},\n): OpenTrayClient => {\n const nextRequestId = createRequestIdFactory(options.requestIdPrefix ?? \"opentray\");\n\n return {\n async createSurface(surfaceOptions): Promise<SurfaceHandle> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"create-surface\",\n requestId,\n ...surfaceOptions,\n });\n const surface = expectResponse(response, requestId, \"surface-created\").surface;\n return createSurfaceHandle(transport, surface, nextRequestId);\n },\n };\n};\n\nexport const createSurfaceHandle = (\n transport: OpenTrayTransport,\n surface: SurfaceRef,\n nextRequestId: () => RequestId = createRequestIdFactory(\"opentray\"),\n): SurfaceHandle => ({\n surface,\n async createTray(options: TrayOptions): Promise<TrayHandle> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"create-tray\",\n requestId,\n surface,\n tray: options,\n });\n const frame = expectResponse(response, requestId, \"tray-created\");\n return createTrayHandle(transport, surface, frame.trayId, nextRequestId);\n },\n});\n\nexport const createTrayHandle = (\n transport: OpenTrayTransport,\n surface: SurfaceRef,\n trayId: TrayId,\n nextRequestId: () => RequestId = createRequestIdFactory(\"opentray\"),\n): TrayHandle => ({\n surface,\n trayId,\n async commandExtension(ext: string, data: unknown): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"ext-command\",\n requestId,\n surfaceId: surface.surfaceId,\n trayId,\n ext,\n data,\n });\n expectResponse(response, requestId, \"ack\");\n },\n async destroy(): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"destroy-tray\",\n requestId,\n surfaceId: surface.surfaceId,\n trayId,\n });\n expectResponse(response, requestId, \"ack\");\n },\n});\n\nconst createRequestIdFactory = (prefix: string): (() => RequestId) => {\n let next = 1;\n return () => {\n const requestId = `${prefix}-${next}`;\n next += 1;\n return requestId;\n };\n};\n\nconst expectResponse = <TType extends ServerFrame[\"type\"]>(\n frame: ServerFrame,\n requestId: RequestId,\n type: TType,\n): Extract<ServerFrame, { type: TType }> => {\n if (frame.type === \"error\") {\n throw new Error(`${frame.code}: ${frame.message}`);\n }\n if (frame.type !== type) {\n throw new Error(`expected ${type} for ${requestId}, received ${frame.type}`);\n }\n if (requestIdOf(frame) !== requestId) {\n throw new Error(`expected response for ${requestId}, received ${requestIdOf(frame) ?? \"none\"}`);\n }\n return frame as Extract<ServerFrame, { type: TType }>;\n};\n\nconst requestIdOf = (frame: ServerFrame): RequestId | undefined => {\n switch (frame.type) {\n case \"surface-created\":\n case \"default-surface\":\n case \"tray-created\":\n case \"ack\":\n case \"daemon-health\":\n return frame.requestId;\n case \"error\":\n return frame.requestId;\n case \"ready\":\n case \"event\":\n case \"ext-event\":\n return undefined;\n }\n};\n"],"mappings":";;AAgCA,MAAa,mBAAmB,mBAAwC;CACtE,MAAM;CACN,iBAAiB;CACjB;AACF;AAMA,MAAa,gBACX,WACA,UAA+B,CAAC,MACb;CACnB,MAAM,gBAAgB,uBAAuB,QAAQ,mBAAmB,UAAU;CAElF,OAAO,EACL,MAAM,cAAc,gBAAwC;EAC1D,MAAM,YAAY,cAAc;EAMhC,MAAM,UAAU,eAAe,MALR,UAAU,QAAQ;GACvC,MAAM;GACN;GACA,GAAG;EACL,CAAC,GACwC,WAAW,iBAAiB,EAAE;EACvE,OAAO,oBAAoB,WAAW,SAAS,aAAa;CAC9D,EACF;AACF;AAEA,MAAa,uBACX,WACA,SACA,gBAAiC,uBAAuB,UAAU,OAC/C;CACnB;CACA,MAAM,WAAW,SAA2C;EAC1D,MAAM,YAAY,cAAc;EAQhC,OAAO,iBAAiB,WAAW,SADrB,eAAe,MANN,UAAU,QAAQ;GACvC,MAAM;GACN;GACA;GACA,MAAM;EACR,CAAC,GACsC,WAAW,cACF,EAAE,QAAQ,aAAa;CACzE;AACF;AAEA,MAAa,oBACX,WACA,SACA,QACA,gBAAiC,uBAAuB,UAAU,OAClD;CAChB;CACA;CACA,MAAM,iBAAiB,KAAa,MAA8B;EAChE,MAAM,YAAY,cAAc;EAShC,eAAe,MARQ,UAAU,QAAQ;GACvC,MAAM;GACN;GACA,WAAW,QAAQ;GACnB;GACA;GACA;EACF,CAAC,GACwB,WAAW,KAAK;CAC3C;CACA,MAAM,UAAyB;EAC7B,MAAM,YAAY,cAAc;EAOhC,eAAe,MANQ,UAAU,QAAQ;GACvC,MAAM;GACN;GACA,WAAW,QAAQ;GACnB;EACF,CAAC,GACwB,WAAW,KAAK;CAC3C;AACF;AAEA,MAAM,0BAA0B,WAAsC;CACpE,IAAI,OAAO;CACX,aAAa;EACX,MAAM,YAAY,GAAG,OAAO,GAAG;EAC/B,QAAQ;EACR,OAAO;CACT;AACF;AAEA,MAAM,kBACJ,OACA,WACA,SAC0C;CAC1C,IAAI,MAAM,SAAS,SACjB,MAAM,IAAI,MAAM,GAAG,MAAM,KAAK,IAAI,MAAM,SAAS;CAEnD,IAAI,MAAM,SAAS,MACjB,MAAM,IAAI,MAAM,YAAY,KAAK,OAAO,UAAU,aAAa,MAAM,MAAM;CAE7E,IAAI,YAAY,KAAK,MAAM,WACzB,MAAM,IAAI,MAAM,yBAAyB,UAAU,aAAa,YAAY,KAAK,KAAK,QAAQ;CAEhG,OAAO;AACT;AAEA,MAAM,eAAe,UAA8C;CACjE,QAAQ,MAAM,MAAd;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,iBACH,OAAO,MAAM;EACf,KAAK,SACH,OAAO,MAAM;EACf,KAAK;EACL,KAAK;EACL,KAAK,aACH;CACJ;AACF"}
|
|
@@ -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 {
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|