opentray 0.10.2 → 0.10.3
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 +5 -13
- package/dist/cli.d.mts.map +1 -1
- package/dist/cli.mjs +1 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +80 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +7 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -14
- package/dist/native-runtime-Cn8BaffH.d.mts +0 -136
- package/dist/native-runtime-Cn8BaffH.d.mts.map +0 -1
- package/dist/node.d.mts +0 -37
- package/dist/node.d.mts.map +0 -1
- package/dist/node.mjs +0 -17
- package/dist/node.mjs.map +0 -1
- package/dist/runtime-binding-transport-UPunusBT.mjs +0 -177
- package/dist/runtime-binding-transport-UPunusBT.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -79,11 +79,9 @@ Visible tray text belongs to `icon.text`, `icon["text-only"]`, or `icon["icon-te
|
|
|
79
79
|
|
|
80
80
|
OpenTray does not ask developers to create a public broker object. The application process or an application-owned background service imports `opentray`, calls `createTray()`, owns event handlers, and releases the tray when that process exits.
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
Platform runtime packages carry the packaged runtime executable at `bin/opentray` or `bin/opentray.exe`.
|
|
83
83
|
|
|
84
|
-
By default, `createTray()` routes through the local
|
|
85
|
-
|
|
86
|
-
For protocol/session diagnostics, `createTray(options, { runtime: "headless-binding" })` routes through the Node binding without visible native state. Source-tree diagnostics may use `{ runtime: "visible-binding" }` or `{ runtime: "local-broker" }`, but those transports are not the default package runtime.
|
|
84
|
+
By default, `createTray()` routes through the local runtime host and starts it on first use when needed. The executable host remains the source of truth for tray lifecycle, session cleanup, and native event routing on supported platforms.
|
|
87
85
|
|
|
88
86
|
Runtime options may also carry app identity facts:
|
|
89
87
|
|
|
@@ -96,8 +94,6 @@ await createTray(options, {
|
|
|
96
94
|
|
|
97
95
|
The runtime host reports those facts as `appId` and `appName` in `runtime-host-health`; tray icon text, menu labels, and tooltip text remain projection data.
|
|
98
96
|
|
|
99
|
-
The `opentray/node` subpath exposes binding-resolution, visible host-loop, and binding-transport helpers. Source-tree diagnostics may still use the internal local broker transport, but that transport is not exported as a package contract.
|
|
100
|
-
|
|
101
97
|
## Examples
|
|
102
98
|
|
|
103
99
|
Run the quickstart example and the protocol-only example:
|
|
@@ -111,17 +107,13 @@ Run the finite source-tree smoke matrix instead of relying on shell expansion fo
|
|
|
111
107
|
|
|
112
108
|
```bash
|
|
113
109
|
pnpm --filter opentray example:matrix
|
|
114
|
-
pnpm --filter opentray example:matrix -- --row
|
|
110
|
+
pnpm --filter opentray example:matrix -- --row webview-control
|
|
115
111
|
```
|
|
116
112
|
|
|
117
113
|
Run human-visible tray and extension examples from a source checkout:
|
|
118
114
|
|
|
119
115
|
```bash
|
|
120
|
-
cargo build -p opentray-
|
|
121
|
-
pnpm --filter opentray build
|
|
122
|
-
bun run scripts/binaries/stage-local.ts --kind runtime --source target/debug/libopentray_runtime_node.dylib
|
|
123
|
-
OPENTRAY_EXAMPLE_EXIT_AFTER_MS=1500 pnpm --filter opentray example:visible-binding
|
|
124
|
-
|
|
116
|
+
cargo build -p opentray-bin
|
|
125
117
|
pnpm --filter opentray example:debug-runtime-tray
|
|
126
118
|
pnpm --filter opentray example:webview-control
|
|
127
119
|
pnpm --filter opentray example:tray-panel
|
|
@@ -130,4 +122,4 @@ pnpm --filter opentray example:mediaQuery
|
|
|
130
122
|
pnpm --filter opentray example:debug-runtime-lynx -- --bundle packages/cli/assets/lynx-review/main.lynx.bundle
|
|
131
123
|
```
|
|
132
124
|
|
|
133
|
-
The example matrix
|
|
125
|
+
The example matrix warms the runtime executable before `first-app`, skips unsupported or missing native extension carrier artifacts with an explicit reason, and labels contributor-only extension rows as `extension-debug-runtime` coverage. The first-app example exercises the default package runtime. The debug-runtime examples exercise the contributor-only source-tree transport for extension and panel iteration. The public API they demonstrate is tray-first: application code creates trays directly and treats background/service lifecycle as application-owned.
|
package/dist/cli.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.mts","names":[],"sources":["../src/cli.ts"],"mappings":";KAMK,UAAA;EACC,IAAA;EAAc,QAAA;AAAA;EACd,IAAA;EAAqB,OAAA;AAAA;AAAA,cAEd,eAAA,GAAmB,IAAA,eAAiB,UAMhD;AAAA,cAEY,MAAA,GAAgB,IAAA,eAAiB,OAAO;AAAA,
|
|
1
|
+
{"version":3,"file":"cli.d.mts","names":[],"sources":["../src/cli.ts"],"mappings":";KAMK,UAAA;EACC,IAAA;EAAc,QAAA;AAAA;EACd,IAAA;EAAqB,OAAA;AAAA;AAAA,cAEd,eAAA,GAAmB,IAAA,eAAiB,UAMhD;AAAA,cAEY,MAAA,GAAgB,IAAA,eAAiB,OAAO;AAAA,cAsBxC,eAAA,GACX,aAAA,sBACA,UAAkB"}
|
package/dist/cli.mjs
CHANGED
|
@@ -22,12 +22,11 @@ const runCli = async (argv) => {
|
|
|
22
22
|
const printHelp = (unsupportedCommand) => {
|
|
23
23
|
if (unsupportedCommand !== void 0) console.error(`Unsupported opentray command: ${unsupportedCommand}`);
|
|
24
24
|
console.error([
|
|
25
|
-
"OpenTray
|
|
25
|
+
"OpenTray does not expose daemon lifecycle commands.",
|
|
26
26
|
"Create trays from an app-owned process with:",
|
|
27
27
|
"",
|
|
28
28
|
" import { createTray } from \"opentray\";",
|
|
29
29
|
"",
|
|
30
|
-
"Node runtime diagnostics live under the opentray/node binding helpers.",
|
|
31
30
|
"Source-tree visual diagnostics live in packages/cli/examples."
|
|
32
31
|
].join("\n"));
|
|
33
32
|
};
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { realpathSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\n\nconst cliModulePath = fileURLToPath(import.meta.url);\n\ntype CliCommand =\n | { type: \"help\"; exitCode: 0 }\n | { type: \"unsupported\"; command: string | undefined };\n\nexport const parseCliCommand = (argv: string[]): CliCommand => {\n const [command] = argv;\n if (command === undefined || command === \"--help\" || command === \"-h\") {\n return { type: \"help\", exitCode: 0 };\n }\n return { type: \"unsupported\", command };\n};\n\nexport const runCli = async (argv: string[]): Promise<number> => {\n const command = parseCliCommand(argv);\n printHelp(command.type === \"unsupported\" ? command.command : undefined);\n return command.type === \"help\" ? command.exitCode : 1;\n};\n\nconst printHelp = (unsupportedCommand?: string): void => {\n if (unsupportedCommand !== undefined) {\n console.error(`Unsupported opentray command: ${unsupportedCommand}`);\n }\n console.error(\n [\n \"OpenTray
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { realpathSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\n\nconst cliModulePath = fileURLToPath(import.meta.url);\n\ntype CliCommand =\n | { type: \"help\"; exitCode: 0 }\n | { type: \"unsupported\"; command: string | undefined };\n\nexport const parseCliCommand = (argv: string[]): CliCommand => {\n const [command] = argv;\n if (command === undefined || command === \"--help\" || command === \"-h\") {\n return { type: \"help\", exitCode: 0 };\n }\n return { type: \"unsupported\", command };\n};\n\nexport const runCli = async (argv: string[]): Promise<number> => {\n const command = parseCliCommand(argv);\n printHelp(command.type === \"unsupported\" ? command.command : undefined);\n return command.type === \"help\" ? command.exitCode : 1;\n};\n\nconst printHelp = (unsupportedCommand?: string): void => {\n if (unsupportedCommand !== undefined) {\n console.error(`Unsupported opentray command: ${unsupportedCommand}`);\n }\n console.error(\n [\n \"OpenTray does not expose daemon lifecycle commands.\",\n \"Create trays from an app-owned process with:\",\n \"\",\n ' import { createTray } from \"opentray\";',\n \"\",\n \"Source-tree visual diagnostics live in packages/cli/examples.\",\n ].join(\"\\n\")\n );\n};\n\nexport const isCliEntrypoint = (\n argvEntryPath: string | undefined,\n modulePath: string\n): boolean => {\n if (argvEntryPath === undefined) return false;\n try {\n return realpathSync(argvEntryPath) === realpathSync(modulePath);\n } catch {\n return argvEntryPath === modulePath;\n }\n};\n\nif (isCliEntrypoint(process.argv[1], cliModulePath)) {\n runCli(process.argv.slice(2)).then((code) => {\n process.exitCode = code;\n });\n}\n"],"mappings":";;;;AAIA,MAAM,gBAAgB,cAAc,OAAO,KAAK,GAAG;AAMnD,MAAa,mBAAmB,SAA+B;CAC7D,MAAM,CAAC,WAAW;CAClB,IAAI,YAAY,KAAA,KAAa,YAAY,YAAY,YAAY,MAC/D,OAAO;EAAE,MAAM;EAAQ,UAAU;CAAE;CAErC,OAAO;EAAE,MAAM;EAAe;CAAQ;AACxC;AAEA,MAAa,SAAS,OAAO,SAAoC;CAC/D,MAAM,UAAU,gBAAgB,IAAI;CACpC,UAAU,QAAQ,SAAS,gBAAgB,QAAQ,UAAU,KAAA,CAAS;CACtE,OAAO,QAAQ,SAAS,SAAS,QAAQ,WAAW;AACtD;AAEA,MAAM,aAAa,uBAAsC;CACvD,IAAI,uBAAuB,KAAA,GACzB,QAAQ,MAAM,iCAAiC,oBAAoB;CAErE,QAAQ,MACN;EACE;EACA;EACA;EACA;EACA;EACA;CACF,EAAE,KAAK,IAAI,CACb;AACF;AAEA,MAAa,mBACX,eACA,eACY;CACZ,IAAI,kBAAkB,KAAA,GAAW,OAAO;CACxC,IAAI;EACF,OAAO,aAAa,aAAa,MAAM,aAAa,UAAU;CAChE,QAAQ;EACN,OAAO,kBAAkB;CAC3B;AACF;AAEA,IAAI,gBAAgB,QAAQ,KAAK,IAAI,aAAa,GAChD,OAAO,QAAQ,KAAK,MAAM,CAAC,CAAC,EAAE,MAAM,SAAS;CAC3C,QAAQ,WAAW;AACrB,CAAC"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,86 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BrokerEndpointIdentity, BrokerEndpointIdentityOptions, PROTOCOL_VERSION, TrayOptions, createBrokerEndpointIdentity, formatBrokerEndpointName, formatBrokerStateRoot, formatUnixSocketPath, formatWindowsPipeName, isSupportedProtocolVersion } from "@opentray/spec";
|
|
1
|
+
import { BrokerEndpointIdentity, BrokerEndpointIdentityOptions, ClientFrame, ClientRequestFrame, ExtensionEnvelope, Icon, Menu, PROTOCOL_VERSION, RequestId, ServerFrame, Tooltip, TrayBoundsResult, TrayEvent, TrayId, TrayOptions, createBrokerEndpointIdentity, formatBrokerEndpointName, formatBrokerStateRoot, formatUnixSocketPath, formatWindowsPipeName, isSupportedProtocolVersion } from "@opentray/spec";
|
|
3
2
|
|
|
3
|
+
//#region src/client.d.ts
|
|
4
|
+
interface OpenTrayTransport {
|
|
5
|
+
request(frame: ClientRequestFrame): Promise<ServerFrame>;
|
|
6
|
+
}
|
|
7
|
+
type OpenTrayEventFrame = Extract<ServerFrame, {
|
|
8
|
+
type: "event" | "ext-event";
|
|
9
|
+
}>;
|
|
10
|
+
interface OpenTrayEventSource {
|
|
11
|
+
onEvent(listener: (frame: OpenTrayEventFrame) => void): () => void;
|
|
12
|
+
}
|
|
13
|
+
interface OpenTrayConnection extends OpenTrayTransport, OpenTrayEventSource {}
|
|
14
|
+
interface OpenTrayClient {
|
|
15
|
+
createTray(options: TrayOptions): Promise<TrayHandle>;
|
|
16
|
+
}
|
|
17
|
+
interface OpenTrayEventfulClient extends OpenTrayClient {
|
|
18
|
+
createTray(options: TrayOptions): Promise<EventfulTrayHandle>;
|
|
19
|
+
}
|
|
20
|
+
interface TrayHandle {
|
|
21
|
+
trayId: TrayId;
|
|
22
|
+
getBounds(): Promise<TrayBoundsResult>;
|
|
23
|
+
setMenu(menu: Menu): Promise<void>;
|
|
24
|
+
setTooltip(tooltip: Tooltip): Promise<void>;
|
|
25
|
+
setIcon(icon: Icon): Promise<void>;
|
|
26
|
+
loadExtension(options: ExtensionLoadOptions): Promise<void>;
|
|
27
|
+
commandExtension(ext: string, data: unknown): Promise<void>;
|
|
28
|
+
requestExtension(ext: string, data: unknown): Promise<ExtensionEnvelope[]>;
|
|
29
|
+
extend<TCapability extends object, TOptions = undefined>(extension: TrayExtension<TCapability, TOptions>, options?: TOptions): TrayHandle & TCapability;
|
|
30
|
+
destroy(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
type TrayScopedEvent = Exclude<TrayEvent, {
|
|
33
|
+
type: "ready";
|
|
34
|
+
}>;
|
|
35
|
+
type TrayEventType = TrayScopedEvent["type"];
|
|
36
|
+
type TrayEventByType<TEvent extends TrayEventType> = Extract<TrayScopedEvent, {
|
|
37
|
+
type: TEvent;
|
|
38
|
+
}>;
|
|
39
|
+
interface EventfulTrayHandle extends TrayHandle {
|
|
40
|
+
extend<TCapability extends object, TOptions = undefined>(extension: TrayExtension<TCapability, TOptions>, options?: TOptions): EventfulTrayHandle & TCapability;
|
|
41
|
+
listenExtension<TData = unknown>(ext: string, handler: (event: ExtensionEnvelope<TData>) => void): () => void;
|
|
42
|
+
listen<TEvent extends TrayEventType>(event: TEvent, handler: (event: TrayEventByType<TEvent>) => void): () => void;
|
|
43
|
+
onMenuClick(handler: (event: TrayEventByType<"menuClick">) => void): () => void;
|
|
44
|
+
onTrayClick(handler: (event: TrayEventByType<"trayClick">) => void): () => void;
|
|
45
|
+
onTrayDoubleClick(handler: (event: TrayEventByType<"trayDoubleClick">) => void): () => void;
|
|
46
|
+
}
|
|
47
|
+
interface ExtensionLoadOptions {
|
|
48
|
+
name: string;
|
|
49
|
+
path: string;
|
|
50
|
+
mountId?: string;
|
|
51
|
+
}
|
|
52
|
+
interface TrayExtensionMountSpec {
|
|
53
|
+
name?: string;
|
|
54
|
+
path?: string;
|
|
55
|
+
mountId?: string;
|
|
56
|
+
}
|
|
57
|
+
interface TrayExtensionContext {
|
|
58
|
+
readonly appId: string;
|
|
59
|
+
readonly trayId: TrayId;
|
|
60
|
+
readonly name: string;
|
|
61
|
+
readonly path: string;
|
|
62
|
+
readonly mountId: string;
|
|
63
|
+
ensureLoaded(): Promise<void>;
|
|
64
|
+
command(data: unknown): Promise<void>;
|
|
65
|
+
request(data: unknown): Promise<ExtensionEnvelope[]>;
|
|
66
|
+
}
|
|
67
|
+
interface TrayExtension<TCapability extends object, TOptions = undefined> {
|
|
68
|
+
readonly name: string;
|
|
69
|
+
readonly path: string;
|
|
70
|
+
resolveMount?(options: TOptions | undefined): TrayExtensionMountSpec;
|
|
71
|
+
extend(tray: TrayHandle, context: TrayExtensionContext, options: TOptions | undefined): TCapability;
|
|
72
|
+
}
|
|
73
|
+
declare const createInitFrame: (clientVersion: string) => ClientFrame;
|
|
74
|
+
interface CreateClientOptions {
|
|
75
|
+
requestIdPrefix?: string;
|
|
76
|
+
}
|
|
77
|
+
declare function createClient(transport: OpenTrayConnection, options?: CreateClientOptions): OpenTrayEventfulClient;
|
|
78
|
+
declare function createClient(transport: OpenTrayTransport, options?: CreateClientOptions): OpenTrayClient;
|
|
79
|
+
declare function createTrayHandle(transport: OpenTrayConnection, appId: string, trayId: TrayId, nextRequestId?: () => RequestId): EventfulTrayHandle;
|
|
80
|
+
declare function createTrayHandle(transport: OpenTrayTransport, appId: string, trayId: TrayId, nextRequestId?: () => RequestId): TrayHandle;
|
|
81
|
+
//#endregion
|
|
4
82
|
//#region src/sdk.d.ts
|
|
5
|
-
type OpenTrayRuntimeMode = "visible-binding" | "local-broker" | "headless-binding";
|
|
6
83
|
interface OpenTrayRuntimeOptions {
|
|
7
|
-
runtime?: OpenTrayRuntimeMode;
|
|
8
84
|
endpoint?: string;
|
|
9
85
|
homeDir?: string;
|
|
10
86
|
packageVersion?: string;
|
|
@@ -13,7 +89,6 @@ interface OpenTrayRuntimeOptions {
|
|
|
13
89
|
appId?: string;
|
|
14
90
|
appName?: string;
|
|
15
91
|
autoStart?: boolean;
|
|
16
|
-
binding?: OpenTrayRuntimeBinding;
|
|
17
92
|
}
|
|
18
93
|
declare const createTray: (options: TrayOptions, runtimeOptions?: OpenTrayRuntimeOptions) => Promise<EventfulTrayHandle>;
|
|
19
94
|
//#endregion
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/sdk.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/client.ts","../src/sdk.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { t as createRuntimeBindingTransport } from "./runtime-binding-transport-UPunusBT.mjs";
|
|
2
1
|
import { PROTOCOL_VERSION, PROTOCOL_VERSION as PROTOCOL_VERSION$1, createBrokerEndpointIdentity, createBrokerEndpointIdentity as createBrokerEndpointIdentity$1, formatBrokerEndpointName, formatBrokerStateRoot, formatUnixSocketPath, formatUnixSocketPath as formatUnixSocketPath$1, formatWindowsPipeName, formatWindowsPipeName as formatWindowsPipeName$1, isSupportedProtocolVersion, parseServerFrame, sanitizeCallerLabel } from "@opentray/spec";
|
|
3
2
|
import { createConnection } from "node:net";
|
|
4
3
|
import { homedir } from "node:os";
|
|
@@ -106,7 +105,7 @@ function createTrayHandle(transport, appId, trayId, nextRequestId = createReques
|
|
|
106
105
|
return expectResponse(response, requestId, "ext-command-result").events;
|
|
107
106
|
},
|
|
108
107
|
extend(extension, options) {
|
|
109
|
-
const context = createTrayExtensionContext(handle, extension, options, nextMountId);
|
|
108
|
+
const context = createTrayExtensionContext(handle, appId, extension, options, nextMountId);
|
|
110
109
|
const capability = extension.extend(handle, context, options);
|
|
111
110
|
return Object.assign({}, handle, capability);
|
|
112
111
|
},
|
|
@@ -156,7 +155,7 @@ const attachEventfulTrayHandle = (handle, source, appId, nextMountId) => {
|
|
|
156
155
|
return listen("trayDoubleClick", handler);
|
|
157
156
|
},
|
|
158
157
|
extend(extension, options) {
|
|
159
|
-
const context = createTrayExtensionContext(eventful, extension, options, nextMountId);
|
|
158
|
+
const context = createTrayExtensionContext(eventful, appId, extension, options, nextMountId);
|
|
160
159
|
const capability = extension.extend(eventful, context, options);
|
|
161
160
|
return Object.assign({}, eventful, capability);
|
|
162
161
|
}
|
|
@@ -172,7 +171,7 @@ const resolveDefaultAppRef = async (transport, nextRequestId) => {
|
|
|
172
171
|
requestId
|
|
173
172
|
}), requestId, "default-app").app;
|
|
174
173
|
};
|
|
175
|
-
const createTrayExtensionContext = (tray, extension, options, nextMountId) => {
|
|
174
|
+
const createTrayExtensionContext = (tray, appId, extension, options, nextMountId) => {
|
|
176
175
|
const mount = extension.resolveMount?.(options) ?? {};
|
|
177
176
|
const name = mount.name ?? extension.name;
|
|
178
177
|
const path = mount.path ?? extension.path;
|
|
@@ -190,6 +189,8 @@ const createTrayExtensionContext = (tray, extension, options, nextMountId) => {
|
|
|
190
189
|
await loadPromise;
|
|
191
190
|
};
|
|
192
191
|
return {
|
|
192
|
+
appId,
|
|
193
|
+
trayId: tray.trayId,
|
|
193
194
|
name,
|
|
194
195
|
path,
|
|
195
196
|
mountId,
|
|
@@ -307,7 +308,7 @@ const resolveBrokerCommand = async (paths, options = {}) => {
|
|
|
307
308
|
if (explicit !== void 0 && explicit.length > 0) return commandForBinary(explicit, paths);
|
|
308
309
|
const sourceDir = options.sourceDir ?? dirname(fileURLToPath(sourceUrl));
|
|
309
310
|
const workspaceRoot = await (options.findWorkspaceRoot ?? findWorkspaceRoot)(sourceDir);
|
|
310
|
-
if (workspaceRoot !== void 0) return commandForBinary(await (options.ensureDevBrokerBinary ?? ensureDevBrokerBinary)(workspaceRoot), paths
|
|
311
|
+
if (workspaceRoot !== void 0) return commandForBinary(await (options.ensureDevBrokerBinary ?? ensureDevBrokerBinary)(workspaceRoot), paths);
|
|
311
312
|
const platform = options.platform ?? process.platform;
|
|
312
313
|
const arch = options.arch ?? process.arch;
|
|
313
314
|
const target = resolveBrokerNativeTarget(platform, arch);
|
|
@@ -760,19 +761,7 @@ const resolveCliEntrypoint = () => {
|
|
|
760
761
|
//#endregion
|
|
761
762
|
//#region src/sdk.ts
|
|
762
763
|
const createTray = async (options, runtimeOptions = {}) => {
|
|
763
|
-
|
|
764
|
-
...runtimeOptions.binding === void 0 ? {} : { binding: runtimeOptions.binding },
|
|
765
|
-
...runtimeOptions.packageVersion === void 0 ? {} : { packageVersion: runtimeOptions.packageVersion },
|
|
766
|
-
...runtimeOptions.clientVersion === void 0 ? {} : { clientVersion: runtimeOptions.clientVersion },
|
|
767
|
-
...runtimeOptions.protocolVersion === void 0 ? {} : { protocolVersion: runtimeOptions.protocolVersion },
|
|
768
|
-
...runtimeOptions.appId === void 0 ? {} : { appId: runtimeOptions.appId },
|
|
769
|
-
...runtimeOptions.appName === void 0 ? {} : { appName: runtimeOptions.appName }
|
|
770
|
-
};
|
|
771
|
-
const runtime = runtimeOptions.runtime ?? "local-broker";
|
|
772
|
-
return createClient(runtime === "local-broker" ? await connectLocalBroker(runtimeOptions) : await createRuntimeBindingTransport({
|
|
773
|
-
...bindingTransportOptions,
|
|
774
|
-
kind: runtime === "headless-binding" ? "headless" : "visible"
|
|
775
|
-
})).createTray(options);
|
|
764
|
+
return createClient(await connectLocalBroker(runtimeOptions)).createTray(options);
|
|
776
765
|
};
|
|
777
766
|
//#endregion
|
|
778
767
|
export { PROTOCOL_VERSION, createBrokerEndpointIdentity, createClient, createInitFrame, createTray, createTrayHandle, formatBrokerEndpointName, formatBrokerStateRoot, formatUnixSocketPath, formatWindowsPipeName, isSupportedProtocolVersion };
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["PROTOCOL_VERSION","createBrokerEndpointIdentity","normalizeAppIdentityField","formatWindowsPipeName","formatUnixSocketPath","PROTOCOL_VERSION"],"sources":["../src/client.ts","../src/daemon/caller-label.ts","../src/daemon/native-target.ts","../src/daemon/broker-command.ts","../src/daemon/lifecycle.ts","../src/daemon/package-version.ts","../src/daemon/paths.ts","../src/local-broker.ts","../src/sdk.ts"],"sourcesContent":["import {\n PROTOCOL_VERSION,\n type ClientFrame,\n type ClientRequestFrame,\n type ExtensionEnvelope,\n type Icon,\n type Menu,\n type RequestId,\n type ServerFrame,\n type Tooltip,\n type TrayBoundsResult,\n type TrayEvent,\n type TrayId,\n type TrayOptions,\n} from \"@opentray/spec\";\n\nexport interface OpenTrayTransport {\n request(frame: ClientRequestFrame): Promise<ServerFrame>;\n}\n\nexport type OpenTrayEventFrame = Extract<\n ServerFrame,\n { type: \"event\" | \"ext-event\" }\n>;\n\nexport interface OpenTrayEventSource {\n onEvent(listener: (frame: OpenTrayEventFrame) => void): () => void;\n}\n\nexport interface OpenTrayConnection\n extends OpenTrayTransport,\n OpenTrayEventSource {}\n\nexport interface OpenTrayClient {\n createTray(options: TrayOptions): Promise<TrayHandle>;\n}\n\nexport interface OpenTrayEventfulClient extends OpenTrayClient {\n createTray(options: TrayOptions): Promise<EventfulTrayHandle>;\n}\n\nexport interface TrayHandle {\n trayId: TrayId;\n getBounds(): Promise<TrayBoundsResult>;\n setMenu(menu: Menu): Promise<void>;\n setTooltip(tooltip: Tooltip): Promise<void>;\n setIcon(icon: Icon): Promise<void>;\n loadExtension(options: ExtensionLoadOptions): Promise<void>;\n commandExtension(ext: string, data: unknown): Promise<void>;\n requestExtension(ext: string, data: unknown): Promise<ExtensionEnvelope[]>;\n extend<TCapability extends object, TOptions = undefined>(\n extension: TrayExtension<TCapability, TOptions>,\n options?: TOptions\n ): TrayHandle & TCapability;\n destroy(): Promise<void>;\n}\n\nexport type TrayScopedEvent = Exclude<TrayEvent, { type: \"ready\" }>;\nexport type TrayEventType = TrayScopedEvent[\"type\"];\nexport type TrayEventByType<TEvent extends TrayEventType> = Extract<\n TrayScopedEvent,\n { type: TEvent }\n>;\n\nexport interface EventfulTrayHandle extends TrayHandle {\n extend<TCapability extends object, TOptions = undefined>(\n extension: TrayExtension<TCapability, TOptions>,\n options?: TOptions\n ): EventfulTrayHandle & TCapability;\n listenExtension<TData = unknown>(\n ext: string,\n handler: (event: ExtensionEnvelope<TData>) => void\n ): () => void;\n listen<TEvent extends TrayEventType>(\n event: TEvent,\n handler: (event: TrayEventByType<TEvent>) => void\n ): () => void;\n onMenuClick(\n handler: (event: TrayEventByType<\"menuClick\">) => void\n ): () => void;\n onTrayClick(\n handler: (event: TrayEventByType<\"trayClick\">) => void\n ): () => void;\n onTrayDoubleClick(\n handler: (event: TrayEventByType<\"trayDoubleClick\">) => void\n ): () => void;\n}\n\nexport interface ExtensionLoadOptions {\n name: string;\n path: string;\n mountId?: string;\n}\n\nexport interface TrayExtensionMountSpec {\n name?: string;\n path?: string;\n mountId?: string;\n}\n\nexport interface TrayExtensionContext {\n readonly name: string;\n readonly path: string;\n readonly mountId: string;\n ensureLoaded(): Promise<void>;\n command(data: unknown): Promise<void>;\n request(data: unknown): Promise<ExtensionEnvelope[]>;\n}\n\nexport interface TrayExtension<\n TCapability extends object,\n TOptions = undefined\n> {\n readonly name: string;\n readonly path: string;\n resolveMount?(options: TOptions | undefined): TrayExtensionMountSpec;\n extend(\n tray: TrayHandle,\n context: TrayExtensionContext,\n options: TOptions | undefined\n ): TCapability;\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 function createClient(\n transport: OpenTrayConnection,\n options?: CreateClientOptions\n): OpenTrayEventfulClient;\nexport function createClient(\n transport: OpenTrayTransport,\n options?: CreateClientOptions\n): OpenTrayClient;\nexport function createClient(\n transport: OpenTrayTransport,\n options: CreateClientOptions = {}\n): OpenTrayClient | OpenTrayEventfulClient {\n const nextRequestId = createRequestIdFactory(\n options.requestIdPrefix ?? \"opentray\"\n );\n\n return {\n async createTray(options: TrayOptions): Promise<TrayHandle> {\n const app = await resolveDefaultAppRef(transport, nextRequestId);\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"create-tray\",\n requestId,\n app,\n tray: options,\n });\n const frame = expectResponse(response, requestId, \"tray-created\");\n return createTrayHandle(\n transport,\n app.appId,\n frame.trayId ?? options.id,\n nextRequestId\n );\n },\n };\n}\n\nexport function createTrayHandle(\n transport: OpenTrayConnection,\n appId: string,\n trayId: TrayId,\n nextRequestId?: () => RequestId\n): EventfulTrayHandle;\nexport function createTrayHandle(\n transport: OpenTrayTransport,\n appId: string,\n trayId: TrayId,\n nextRequestId?: () => RequestId\n): TrayHandle;\nexport function createTrayHandle(\n transport: OpenTrayTransport,\n appId: string,\n trayId: TrayId,\n nextRequestId: () => RequestId = createRequestIdFactory(\"opentray\")\n): TrayHandle | EventfulTrayHandle {\n let nextMountOrdinal = 1;\n const nextMountId = (extensionName: string): string => {\n const mountId = `${formatMountIdComponent(\n extensionName\n )}.${formatMountIdComponent(trayId)}.${nextMountOrdinal}`;\n nextMountOrdinal += 1;\n return mountId;\n };\n const handle: TrayHandle = {\n trayId,\n async getBounds(): Promise<TrayBoundsResult> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"get-tray-bounds\",\n requestId,\n appId,\n trayId,\n });\n return expectResponse(response, requestId, \"tray-bounds\").bounds;\n },\n async setMenu(menu: Menu): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"set-tray-menu\",\n requestId,\n appId,\n trayId,\n menu,\n });\n expectResponse(response, requestId, \"ack\");\n },\n async setTooltip(tooltip: Tooltip): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"set-tray-tooltip\",\n requestId,\n appId,\n trayId,\n tooltip,\n });\n expectResponse(response, requestId, \"ack\");\n },\n async setIcon(icon: Icon): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"set-tray-icon\",\n requestId,\n appId,\n trayId,\n icon,\n });\n expectResponse(response, requestId, \"ack\");\n },\n async loadExtension(options): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"load-ext\",\n requestId,\n appId,\n name: options.name,\n path: options.path,\n ...(options.mountId === undefined ? {} : { mountId: options.mountId }),\n });\n expectResponse(response, requestId, \"ack\");\n },\n async commandExtension(ext: string, data: unknown): Promise<void> {\n await this.requestExtension(ext, data);\n },\n async requestExtension(\n ext: string,\n data: unknown\n ): Promise<ExtensionEnvelope[]> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"ext-command\",\n requestId,\n appId,\n trayId,\n ext,\n data,\n });\n if (response.type === \"ack\") {\n expectResponse(response, requestId, \"ack\");\n return [];\n }\n return expectResponse(response, requestId, \"ext-command-result\").events;\n },\n extend<TCapability extends object, TOptions = undefined>(\n extension: TrayExtension<TCapability, TOptions>,\n options?: TOptions\n ): TrayHandle & TCapability {\n const context = createTrayExtensionContext(\n handle,\n extension,\n options,\n nextMountId\n );\n const capability = extension.extend(handle, context, options);\n return Object.assign({}, handle, capability);\n },\n async destroy(): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"destroy-tray\",\n requestId,\n appId,\n trayId,\n });\n expectResponse(response, requestId, \"ack\");\n },\n };\n if (!isOpenTrayEventSource(transport)) {\n return handle;\n }\n return attachEventfulTrayHandle(handle, transport, appId, nextMountId);\n}\n\nconst attachEventfulTrayHandle = (\n handle: TrayHandle,\n source: OpenTrayEventSource,\n appId: string,\n nextMountId: (extensionName: string) => string\n): EventfulTrayHandle => {\n const listen: EventfulTrayHandle[\"listen\"] = (event, handler) =>\n source.onEvent((frame) => {\n if (frame.type !== \"event\" || frame.event.type !== event) {\n return;\n }\n if (!isOwnedTrayEvent(frame.event, appId, handle.trayId)) {\n return;\n }\n handler(frame.event as TrayEventByType<typeof event>);\n });\n\n const eventful: EventfulTrayHandle = {\n ...handle,\n listenExtension<TData = unknown>(\n ext: string,\n handler: (event: ExtensionEnvelope<TData>) => void\n ) {\n return source.onEvent((frame) => {\n if (frame.type !== \"ext-event\" || frame.ext !== ext) {\n return;\n }\n if (frame.appId !== appId || frame.trayId !== handle.trayId) {\n return;\n }\n handler({\n scope: { appId: frame.appId, trayId: frame.trayId, ext: frame.ext },\n data: frame.data as TData,\n });\n });\n },\n listen,\n onMenuClick(handler) {\n return listen(\"menuClick\", handler);\n },\n onTrayClick(handler) {\n return listen(\"trayClick\", handler);\n },\n onTrayDoubleClick(handler) {\n return listen(\"trayDoubleClick\", handler);\n },\n extend<TCapability extends object, TOptions = undefined>(\n extension: TrayExtension<TCapability, TOptions>,\n options?: TOptions\n ): EventfulTrayHandle & TCapability {\n const context = createTrayExtensionContext(\n eventful,\n extension,\n options,\n nextMountId\n );\n const capability = extension.extend(eventful, context, options);\n return Object.assign({}, eventful, capability);\n },\n };\n return eventful;\n};\n\nconst isOwnedTrayEvent = (\n event: TrayEvent,\n appId: string,\n trayId: string\n): event is TrayScopedEvent =>\n event.type !== \"ready\" && event.appId === appId && event.trayId === trayId;\n\nconst isOpenTrayEventSource = (\n transport: OpenTrayTransport\n): transport is OpenTrayConnection =>\n \"onEvent\" in transport && typeof transport.onEvent === \"function\";\n\nconst resolveDefaultAppRef = async (\n transport: OpenTrayTransport,\n nextRequestId: () => RequestId\n): Promise<{ appId: string }> => {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"resolve-default-app\",\n requestId,\n });\n return expectResponse(response, requestId, \"default-app\").app;\n};\n\nconst createTrayExtensionContext = <TCapability extends object, TOptions>(\n tray: TrayHandle,\n extension: TrayExtension<TCapability, TOptions>,\n options: TOptions | undefined,\n nextMountId: (extensionName: string) => string\n): TrayExtensionContext => {\n const mount = extension.resolveMount?.(options) ?? {};\n const name = mount.name ?? extension.name;\n const path = mount.path ?? extension.path;\n const mountId = mount.mountId ?? nextMountId(name);\n let loadPromise: Promise<void> | undefined;\n\n const ensureLoaded = async (): Promise<void> => {\n if (loadPromise === undefined) {\n loadPromise = tray\n .loadExtension({ name, path, mountId })\n .catch((error: unknown) => {\n loadPromise = undefined;\n throw error;\n });\n }\n await loadPromise;\n };\n\n return {\n name,\n path,\n mountId,\n ensureLoaded,\n async command(data: unknown): Promise<void> {\n await ensureLoaded();\n await tray.commandExtension(mountId, data);\n },\n async request(data: unknown): Promise<ExtensionEnvelope[]> {\n await ensureLoaded();\n return tray.requestExtension(mountId, data);\n },\n };\n};\n\nconst formatMountIdComponent = (value: string): string => {\n const formatted = value.replaceAll(/[^a-zA-Z0-9._+-]/g, \"_\");\n return formatted.length > 0 ? formatted : \"ext\";\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(\n `expected ${type} for ${requestId}, received ${frame.type}`\n );\n }\n if (requestIdOf(frame) !== requestId) {\n throw new Error(\n `expected response for ${requestId}, received ${\n requestIdOf(frame) ?? \"none\"\n }`\n );\n }\n return frame as Extract<ServerFrame, { type: TType }>;\n};\n\nconst requestIdOf = (frame: ServerFrame): RequestId | undefined => {\n switch (frame.type) {\n case \"app-created\":\n case \"default-app\":\n case \"tray-created\":\n case \"tray-bounds\":\n case \"ack\":\n case \"ext-command-result\":\n case \"runtime-host-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","import { basename } from \"node:path\";\n\nimport { sanitizeCallerLabel } from \"@opentray/spec\";\n\nexport interface CallerLabelSources {\n /** Explicit developer-provided label, highest precedence. */\n readonly explicit?: string;\n /** `npm_package_name` environment value. */\n readonly npmPackageName?: string;\n /** Script path resolved from `process.argv[1]` or equivalent. */\n readonly scriptPath?: string;\n}\n\nexport interface ResolveCallerLabelOptions extends Partial<CallerLabelSources> {\n env?: NodeJS.ProcessEnv;\n argv?: readonly string[];\n}\n\n/**\n * Derives a caller label using the precedence: explicit > npm_package_name >\n * script basename > neutral default. The result is always sanitized to a\n * filesystem- and process-safe component, so callers can use it directly in\n * endpoint identity, runtime directories, and process titles.\n */\nexport const resolveCallerLabel = (options: ResolveCallerLabelOptions = {}): string => {\n const env = options.env ?? process.env;\n const argv = options.argv ?? process.argv;\n\n const candidates: string[] = [];\n if (options.explicit !== undefined && options.explicit.length > 0) {\n candidates.push(options.explicit);\n }\n const npmName = options.npmPackageName ?? env.npm_package_name;\n if (typeof npmName === \"string\" && npmName.length > 0) {\n candidates.push(npmName);\n }\n const scriptPath = options.scriptPath ?? argv[1];\n if (typeof scriptPath === \"string\" && scriptPath.length > 0) {\n candidates.push(basename(scriptPath));\n }\n\n for (const candidate of candidates) {\n const sanitized = sanitizeCallerLabel(candidate);\n if (sanitized.length > 0) {\n return sanitized;\n }\n }\n\n return sanitizeCallerLabel(undefined);\n};\n","export type BrokerPackageOs = \"darwin\" | \"linux\" | \"windows\";\nexport type BrokerArch = \"arm64\" | \"x64\";\n\nexport interface BrokerNativeTarget {\n packageName: string;\n binaryRelativePath: string;\n}\n\nexport const resolveBrokerNativeTarget = (\n platform: string = process.platform,\n arch: string = process.arch,\n): BrokerNativeTarget => {\n const packageOs = platformToPackageOs(platform);\n const nativeArch = normalizeArch(arch);\n return {\n packageName: `@opentray/${packageOs}-${nativeArch}`,\n binaryRelativePath: `bin/${packageOs === \"windows\" ? \"opentray.exe\" : \"opentray\"}`,\n };\n};\n\nconst platformToPackageOs = (platform: string): BrokerPackageOs => {\n switch (platform) {\n case \"darwin\":\n case \"linux\":\n return platform;\n case \"win32\":\n return \"windows\";\n default:\n throw new MissingPlatformBrokerBinaryError(\n `unsupported OpenTray broker platform: ${platform}`,\n platform,\n process.arch,\n );\n }\n};\n\nconst normalizeArch = (arch: string): BrokerArch => {\n switch (arch) {\n case \"arm64\":\n case \"x64\":\n return arch;\n default:\n throw new MissingPlatformBrokerBinaryError(\n `unsupported OpenTray broker architecture: ${arch}`,\n process.platform,\n arch,\n );\n }\n};\n\nexport class MissingPlatformBrokerBinaryError extends Error {\n readonly code = \"OPENTRAY_MISSING_PLATFORM_BROKER_BINARY\";\n readonly platform: string;\n readonly arch: string;\n readonly packageName: string | undefined;\n readonly binaryPath: string | undefined;\n\n constructor(\n message: string,\n platform: string,\n arch: string,\n options: {\n packageName?: string;\n binaryPath?: string;\n cause?: unknown;\n } = {},\n ) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"MissingPlatformBrokerBinaryError\";\n this.platform = platform;\n this.arch = arch;\n this.packageName = options.packageName;\n this.binaryPath = options.binaryPath;\n }\n}\n","import { access } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawn } from \"node:child_process\";\n\nimport type { DaemonPaths } from \"./paths\";\nimport {\n MissingPlatformBrokerBinaryError,\n resolveBrokerNativeTarget,\n} from \"./native-target\";\n\nexport interface BrokerCommand {\n command: string;\n args: string[];\n cwd?: string;\n}\n\nconst sourceUrl = import.meta.url;\n\nexport interface ResolveBrokerCommandOptions {\n env?: NodeJS.ProcessEnv;\n platform?: NodeJS.Platform;\n arch?: string;\n sourceDir?: string;\n findWorkspaceRoot?: (start: string) => Promise<string | undefined>;\n ensureDevBrokerBinary?: (workspaceRoot: string) => Promise<string>;\n}\n\nexport const resolveBrokerCommand = async (\n paths: DaemonPaths,\n options: ResolveBrokerCommandOptions = {}\n): Promise<BrokerCommand> => {\n const env = options.env ?? process.env;\n const explicit = env.OPENTRAY_BROKER_BIN;\n if (explicit !== undefined && explicit.length > 0) {\n return commandForBinary(explicit, paths);\n }\n\n const sourceDir = options.sourceDir ?? dirname(fileURLToPath(sourceUrl));\n const workspaceRoot = await (options.findWorkspaceRoot ?? findWorkspaceRoot)(\n sourceDir\n );\n if (workspaceRoot !== undefined) {\n const binary = await (\n options.ensureDevBrokerBinary ?? ensureDevBrokerBinary\n )(workspaceRoot);\n return commandForBinary(binary, paths, workspaceRoot);\n }\n\n const platform = options.platform ?? process.platform;\n const arch = options.arch ?? process.arch;\n const target = resolveBrokerNativeTarget(platform, arch);\n throw new MissingPlatformBrokerBinaryError(\n `unable to resolve OpenTray debug broker binary for ${platform}/${arch}; run from the source workspace or set OPENTRAY_BROKER_BIN`,\n platform,\n arch,\n { packageName: target.packageName }\n );\n};\n\nconst commandForBinary = (\n binary: string,\n paths: DaemonPaths,\n cwd?: string\n): BrokerCommand => {\n const command: BrokerCommand = {\n command: binary,\n args: [\n \"broker\",\n \"--endpoint\",\n paths.endpoint,\n \"--ready-file\",\n paths.readyFile,\n \"--package-version\",\n paths.packageVersion,\n \"--protocol-version\",\n `${paths.protocolVersion}`,\n \"--app-id\",\n paths.appId,\n \"--app-name\",\n paths.appName,\n \"--caller-label\",\n paths.callerLabel,\n ],\n };\n if (cwd !== undefined) {\n command.cwd = cwd;\n }\n return command;\n};\n\nconst findWorkspaceRoot = async (\n start: string\n): Promise<string | undefined> => {\n let current = start;\n while (true) {\n if (await exists(join(current, \"Cargo.toml\"))) {\n if (await exists(join(current, \"crates\", \"opentray-bin\", \"Cargo.toml\"))) {\n return current;\n }\n }\n\n const parent = dirname(current);\n if (parent === current) {\n return undefined;\n }\n current = parent;\n }\n};\n\nexport const resolveDevBrokerBinaryPath = (\n workspaceRoot: string,\n platform: NodeJS.Platform = process.platform\n): string =>\n join(\n workspaceRoot,\n \"target\",\n \"debug\",\n platform === \"win32\" ? \"opentray.exe\" : \"opentray\"\n );\n\nexport const terminateWorkspaceDevBrokerProcess = async (\n workspaceRoot: string,\n platform: NodeJS.Platform = process.platform\n): Promise<void> => {\n if (platform !== \"win32\") {\n return;\n }\n await terminateWindowsProcessByExecutable(\n resolveDevBrokerBinaryPath(workspaceRoot, platform)\n );\n};\n\nconst ensureDevBrokerBinary = async (\n workspaceRoot: string\n): Promise<string> => {\n const binary = resolveDevBrokerBinaryPath(workspaceRoot);\n await terminateWorkspaceDevBrokerProcess(workspaceRoot);\n await runCargoBuild(workspaceRoot);\n return binary;\n};\n\nconst runCargoBuild = (workspaceRoot: string): Promise<void> =>\n new Promise((resolve, reject) => {\n const child = spawn(\"cargo\", [\"build\", \"-p\", \"opentray-bin\"], {\n cwd: workspaceRoot,\n stdio:\n process.env.OPENTRAY_BROKER_BUILD_LOGS === \"1\" ? \"inherit\" : \"ignore\",\n });\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => {\n if (code === 0) {\n resolve();\n return;\n }\n reject(\n new Error(\n `cargo build -p opentray-bin failed with code ${code ?? \"unknown\"}`\n )\n );\n });\n });\n\nconst exists = async (path: string): Promise<boolean> => {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n};\n\nconst terminateWindowsProcessByExecutable = (binary: string): Promise<void> =>\n new Promise((resolve, reject) => {\n const script = String.raw`\n$ErrorActionPreference = 'Stop'\n$target = [System.IO.Path]::GetFullPath(${powerShellString(binary)})\n$processes = @(Get-CimInstance Win32_Process -Filter \"Name = 'opentray.exe'\" | Where-Object {\n $_.ExecutablePath -and ([System.IO.Path]::GetFullPath($_.ExecutablePath) -ieq $target)\n})\nforeach ($process in $processes) {\n Stop-Process -Id $process.ProcessId -Force -ErrorAction Stop\n}\nforeach ($process in $processes) {\n Wait-Process -Id $process.ProcessId -Timeout 5 -ErrorAction SilentlyContinue\n}\n`;\n const child = spawn(\n \"powershell.exe\",\n [\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-ExecutionPolicy\",\n \"Bypass\",\n \"-Command\",\n script,\n ],\n { stdio: [\"ignore\", \"ignore\", \"pipe\"] }\n );\n let stderr = \"\";\n child.stderr?.on(\"data\", (chunk: Buffer | string) => {\n stderr += String(chunk);\n });\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => {\n if (code === 0) {\n resolve();\n return;\n }\n reject(\n new Error(\n `failed to stop running dev broker before cargo build: ${\n stderr.trim() || code\n }`\n )\n );\n });\n });\n\nconst powerShellString = (value: string): string =>\n `'${value.replace(/'/g, \"''\")}'`;\n\nconst isNodeError = (error: unknown): error is NodeJS.ErrnoException =>\n error instanceof Error && \"code\" in error;\n","import { constants } from \"node:fs\";\nimport { mkdir, open, readFile, rm, unlink, writeFile } from \"node:fs/promises\";\nimport { spawn } from \"node:child_process\";\n\nimport { resolveBrokerCommand } from \"./broker-command\";\nimport type { DaemonPaths } from \"./paths\";\n\nconst DAEMON_STDIO_ENV = \"OPENTRAY_DAEMON_STDIO\";\n\nexport type DaemonStartResult =\n | { status: \"started\"; pid: number; paths: DaemonPaths }\n | { status: \"already-running\"; pid: number; paths: DaemonPaths };\n\nexport type DaemonStopResult =\n | { status: \"stopped\"; pid: number; paths: DaemonPaths }\n | { status: \"not-running\"; paths: DaemonPaths };\n\nexport type DaemonInspectResult =\n | { status: \"running\"; pid: number; paths: DaemonPaths }\n | { status: \"not-running\"; paths: DaemonPaths; stalePid?: number };\n\nexport interface DaemonDriver {\n isAlive(pid: number): Promise<boolean>;\n spawnBroker(paths: DaemonPaths): Promise<number>;\n stop(pid: number): Promise<void>;\n}\n\nexport interface StartDaemonOptions {\n paths: DaemonPaths;\n driver: DaemonDriver;\n lockTimeoutMs?: number;\n}\n\nexport interface StopDaemonOptions {\n paths: DaemonPaths;\n driver: DaemonDriver;\n}\n\nexport interface InspectDaemonOptions {\n paths: DaemonPaths;\n driver: DaemonDriver;\n}\n\nexport const createNodeDaemonDriver = (\n cliEntrypoint: string\n): DaemonDriver => ({\n async isAlive(pid) {\n return isProcessAlive(pid);\n },\n async spawnBroker(paths) {\n const broker = await resolveBrokerCommand(paths);\n // The caller label flows to the broker via both a CLI flag and an env var.\n // The broker uses it to bind the per-caller endpoint and to set its own\n // process title so task managers show the owning application.\n const child = spawn(broker.command, broker.args, {\n cwd: broker.cwd,\n detached: true,\n env: {\n ...process.env,\n OPENTRAY_DAEMON_HOME: paths.homeDir,\n OPENTRAY_DAEMON_PACKAGE_VERSION: paths.packageVersion,\n OPENTRAY_DAEMON_CLI_ENTRYPOINT: cliEntrypoint,\n OPENTRAY_DAEMON_APP_ID: paths.appId,\n OPENTRAY_DAEMON_APP_NAME: paths.appName,\n OPENTRAY_DAEMON_CALLER_LABEL: paths.callerLabel,\n },\n stdio: resolveBrokerStdio(process.env[DAEMON_STDIO_ENV]),\n });\n\n child.unref();\n return child.pid ?? 0;\n },\n async stop(pid) {\n process.kill(pid, \"SIGTERM\");\n },\n});\n\nexport const resolveBrokerStdio = (\n value: string | undefined\n): \"ignore\" | \"inherit\" => {\n return value === \"inherit\" ? \"inherit\" : \"ignore\";\n};\n\nexport const startDaemon = async ({\n paths,\n driver,\n lockTimeoutMs = 1_000,\n}: StartDaemonOptions): Promise<DaemonStartResult> => {\n await mkdir(paths.runtimeDir, { recursive: true });\n\n const lock = await acquireLock(paths.lockFile, lockTimeoutMs);\n try {\n const existingPid = await readPid(paths.pidFile);\n if (existingPid !== undefined && (await driver.isAlive(existingPid))) {\n return { status: \"already-running\", pid: existingPid, paths };\n }\n\n await cleanupRuntimeFiles(paths);\n const pid = await driver.spawnBroker(paths);\n if (!Number.isInteger(pid) || pid <= 0) {\n throw new Error(\"daemon broker process did not expose a valid pid\");\n }\n await writeFile(paths.pidFile, `${pid}\\n`, \"utf8\");\n await waitForReadyFile(paths.readyFile, driver, pid);\n return { status: \"started\", pid, paths };\n } finally {\n await lock.release();\n }\n};\n\nexport const stopDaemon = async ({\n paths,\n driver,\n}: StopDaemonOptions): Promise<DaemonStopResult> => {\n const pid = await readPid(paths.pidFile);\n if (pid === undefined || !(await driver.isAlive(pid))) {\n await cleanupRuntimeFiles(paths);\n return { status: \"not-running\", paths };\n }\n\n await driver.stop(pid);\n await waitUntilStopped(driver, pid);\n await cleanupRuntimeFiles(paths);\n return { status: \"stopped\", pid, paths };\n};\n\nexport const restartDaemon = async (\n options: StartDaemonOptions\n): Promise<DaemonStartResult> => {\n await stopDaemon({ paths: options.paths, driver: options.driver });\n return startDaemon(options);\n};\n\nexport const inspectDaemon = async ({\n paths,\n driver,\n}: InspectDaemonOptions): Promise<DaemonInspectResult> => {\n const pid = await readPid(paths.pidFile);\n if (pid !== undefined && (await driver.isAlive(pid))) {\n return { status: \"running\", pid, paths };\n }\n\n await cleanupRuntimeFiles(paths);\n return pid === undefined\n ? { status: \"not-running\", paths }\n : { status: \"not-running\", paths, stalePid: pid };\n};\n\nconst readPid = async (pidFile: string): Promise<number | undefined> => {\n try {\n const content = await readFile(pidFile, \"utf8\");\n const pid = Number.parseInt(content.trim(), 10);\n return Number.isInteger(pid) && pid > 0 ? pid : undefined;\n } catch (error) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n};\n\nconst cleanupRuntimeFiles = async (paths: DaemonPaths): Promise<void> => {\n await rm(paths.readyFile, { force: true });\n await rm(paths.pidFile, { force: true });\n if (process.platform !== \"win32\") {\n await rm(paths.endpoint, { force: true });\n }\n};\n\nconst waitUntilStopped = async (\n driver: DaemonDriver,\n pid: number\n): Promise<void> => {\n for (let attempt = 0; attempt < 20; attempt += 1) {\n if (!(await driver.isAlive(pid))) {\n return;\n }\n await sleep(50);\n }\n};\n\nconst waitForReadyFile = async (\n readyFile: string,\n driver: DaemonDriver,\n pid: number\n): Promise<void> => {\n for (let attempt = 0; attempt < 40; attempt += 1) {\n try {\n await readFile(readyFile, \"utf8\");\n return;\n } catch (error) {\n if (!isNodeError(error) || error.code !== \"ENOENT\") {\n throw error;\n }\n if (!(await driver.isAlive(pid))) {\n throw new Error(\n `daemon broker exited before readiness: pid=${pid}; readyFile=${readyFile}; set ${DAEMON_STDIO_ENV}=inherit to inspect broker stderr`\n );\n }\n await sleep(50);\n }\n }\n\n throw new Error(`timed out waiting for daemon readiness: ${readyFile}`);\n};\n\nconst acquireLock = async (\n lockFile: string,\n timeoutMs: number\n): Promise<{\n release(): Promise<void>;\n}> => {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() <= deadline) {\n try {\n const handle = await open(\n lockFile,\n constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY\n );\n await handle.writeFile(`${process.pid}\\n`, \"utf8\");\n return {\n async release() {\n await handle.close();\n await unlink(lockFile).catch((error: unknown) => {\n if (!isNodeError(error) || error.code !== \"ENOENT\") {\n throw error;\n }\n });\n },\n };\n } catch (error) {\n if (isNodeError(error) && error.code === \"EEXIST\") {\n await sleep(25);\n continue;\n }\n throw error;\n }\n }\n\n throw new Error(`timed out acquiring daemon lock: ${lockFile}`);\n};\n\nconst isProcessAlive = (pid: number): boolean => {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n};\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nconst isNodeError = (error: unknown): error is NodeJS.ErrnoException =>\n error instanceof Error && \"code\" in error;\n","import { readFile } from \"node:fs/promises\";\n\nexport const readPackageVersion = async (packageJsonUrl: URL): Promise<string> => {\n const value: unknown = JSON.parse(await readFile(packageJsonUrl, \"utf8\"));\n if (!isPackageJsonWithVersion(value)) {\n throw new Error(`package.json is missing a string version: ${packageJsonUrl.href}`);\n }\n\n return value.version;\n};\n\nconst isPackageJsonWithVersion = (value: unknown): value is { version: string } =>\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n \"version\" in value &&\n typeof value.version === \"string\";\n","import {\n createBrokerEndpointIdentity,\n formatUnixSocketPath,\n formatWindowsPipeName,\n} from \"@opentray/spec\";\n\nexport interface DaemonPathOptions {\n homeDir: string;\n packageVersion: string;\n callerLabel?: string;\n appId?: string;\n appName?: string;\n platform?: NodeJS.Platform;\n}\n\nexport interface DaemonPaths {\n homeDir: string;\n packageVersion: string;\n callerLabel: string;\n appId: string;\n appName: string;\n protocolVersion: number;\n stateRoot: string;\n runtimeDir: string;\n endpoint: string;\n pidFile: string;\n lockFile: string;\n readyFile: string;\n}\n\nexport const resolveDaemonPaths = ({\n homeDir,\n packageVersion,\n callerLabel,\n appId,\n appName,\n platform = process.platform,\n}: DaemonPathOptions): DaemonPaths => {\n const identity = createBrokerEndpointIdentity(\n callerLabel === undefined\n ? { packageVersion }\n : { packageVersion, callerLabel }\n );\n const resolvedAppId = normalizeAppIdentityField(appId, identity.callerLabel);\n const resolvedAppName = normalizeAppIdentityField(\n appName,\n identity.callerLabel\n );\n const normalizedHome = homeDir.replace(/[\\\\/]+$/u, \"\");\n const stateRoot = `${normalizedHome}/.opentray/${identity.packageVersion}/${identity.callerLabel}`;\n const runtimeDir = `${stateRoot}/runtime`;\n const endpoint =\n platform === \"win32\"\n ? formatWindowsPipeName(identity)\n : formatUnixSocketPath(normalizedHome, identity);\n\n return {\n homeDir: normalizedHome,\n packageVersion: identity.packageVersion,\n callerLabel: identity.callerLabel,\n appId: resolvedAppId,\n appName: resolvedAppName,\n protocolVersion: identity.protocolVersion,\n stateRoot,\n runtimeDir,\n endpoint,\n pidFile: `${runtimeDir}/broker.pid`,\n lockFile: `${runtimeDir}/broker.lock`,\n readyFile: `${runtimeDir}/ready.json`,\n };\n};\n\nconst normalizeAppIdentityField = (\n value: string | undefined,\n fallback: string\n): string => {\n const trimmed = value?.trim();\n return trimmed === undefined || trimmed.length === 0 ? fallback : trimmed;\n};\n","import { createConnection, type Socket } from \"node:net\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\n\nimport {\n PROTOCOL_VERSION,\n parseServerFrame,\n type BrokerEndpointIdentityOptions,\n type ClientRequestFrame,\n type RequestId,\n type ServerFrame,\n} from \"@opentray/spec\";\n\nimport type { OpenTrayTransport } from \"./client\";\nimport { resolveCallerLabel } from \"./daemon/caller-label\";\nimport {\n createNodeDaemonDriver,\n startDaemon,\n type DaemonDriver,\n} from \"./daemon/lifecycle\";\nimport { readPackageVersion } from \"./daemon/package-version\";\nimport { resolveDaemonPaths } from \"./daemon/paths\";\n\nconst packageJsonUrl = new URL(\"../package.json\", import.meta.url);\n\nexport type LocalRuntimeEventFrame = Extract<\n ServerFrame,\n { type: \"event\" | \"ext-event\" }\n>;\n\nexport interface LocalBrokerClient extends OpenTrayTransport {\n readonly endpoint: string;\n readonly callerLabel: string;\n readonly sessionId: string;\n onEvent(listener: (frame: LocalRuntimeEventFrame) => void): () => void;\n close(): Promise<void>;\n}\n\nexport interface ConnectLocalBrokerOptions\n extends Partial<BrokerEndpointIdentityOptions> {\n endpoint?: string;\n homeDir?: string;\n appId?: string;\n appName?: string;\n clientVersion?: string;\n autoStart?: boolean;\n daemonDriver?: DaemonDriver;\n cliEntrypoint?: string;\n callerLabel?: string;\n}\n\ninterface PendingRequest {\n resolve(frame: ServerFrame): void;\n reject(error: Error): void;\n}\n\ninterface BrokerSocket {\n setEncoding(encoding: BufferEncoding): void;\n on(event: \"data\", listener: (chunk: Buffer | string) => void): void;\n on(event: \"error\", listener: (error: Error) => void): void;\n on(event: \"close\", listener: () => void): void;\n once(event: \"connect\", listener: () => void): void;\n once(event: \"error\", listener: (error: Error) => void): void;\n off(event: \"error\", listener: (error: Error) => void): void;\n write(data: string): void;\n end(callback: () => void): void;\n}\n\nexport const connectLocalBroker = async (\n options: ConnectLocalBrokerOptions = {}\n): Promise<LocalBrokerClient> => {\n const packageVersion =\n options.packageVersion ?? (await readPackageVersion(packageJsonUrl));\n const clientVersion = options.clientVersion ?? packageVersion;\n const appId = normalizeAppIdentityField(options.appId);\n const appName = normalizeAppIdentityField(options.appName);\n const callerLabel =\n options.callerLabel ?? appName ?? appId ?? resolveCallerLabel();\n const paths = resolveDaemonPaths({\n homeDir: options.homeDir ?? process.env.OPENTRAY_HOME ?? homedir(),\n packageVersion,\n callerLabel,\n ...(appId === undefined ? {} : { appId }),\n ...(appName === undefined ? {} : { appName }),\n });\n const endpoint = options.endpoint ?? paths.endpoint;\n const autoStart = options.autoStart ?? endpoint === paths.endpoint;\n if (autoStart && endpoint !== paths.endpoint) {\n throw new Error(\n \"local broker autoStart requires the derived same-version endpoint\"\n );\n }\n if (autoStart) {\n const cliEntrypoint = options.cliEntrypoint ?? resolveCliEntrypoint();\n const driver =\n options.daemonDriver ?? createNodeDaemonDriver(cliEntrypoint);\n await startDaemon({ paths, driver });\n }\n const socket = await connectSocket(endpoint);\n const connection = new LocalBrokerConnection(socket, endpoint, callerLabel);\n\n await connection.init(\n clientVersion,\n options.protocolVersion ?? PROTOCOL_VERSION\n );\n return connection;\n};\n\nconst normalizeAppIdentityField = (\n value: string | undefined\n): string | undefined => {\n const trimmed = value?.trim();\n return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed;\n};\n\nclass LocalBrokerConnection implements LocalBrokerClient {\n readonly endpoint: string;\n readonly callerLabel: string;\n sessionId = \"\";\n\n private buffer = \"\";\n private readonly listeners = new Set<\n (frame: LocalRuntimeEventFrame) => void\n >();\n private readonly pending = new Map<RequestId, PendingRequest>();\n private ready:\n | {\n resolve(frame: Extract<ServerFrame, { type: \"ready\" }>): void;\n reject(error: Error): void;\n }\n | undefined;\n\n constructor(\n private readonly socket: BrokerSocket,\n endpoint: string,\n callerLabel: string\n ) {\n this.endpoint = endpoint;\n this.callerLabel = callerLabel;\n socket.setEncoding(\"utf8\");\n socket.on(\"data\", (chunk: Buffer | string) => {\n this.consume(String(chunk));\n });\n socket.on(\"error\", (error) => {\n this.rejectAll(error);\n });\n socket.on(\"close\", () => {\n this.rejectAll(new Error(\"broker connection closed\"));\n });\n }\n\n async init(clientVersion: string, protocolVersion: number): Promise<void> {\n const ready = new Promise<Extract<ServerFrame, { type: \"ready\" }>>(\n (resolve, reject) => {\n this.ready = { resolve, reject };\n }\n );\n this.write({\n type: \"init\",\n protocolVersion,\n clientVersion,\n });\n const frame = await ready;\n this.sessionId = frame.sessionId;\n }\n\n async request(frame: ClientRequestFrame): Promise<ServerFrame> {\n if (this.pending.has(frame.requestId)) {\n throw new Error(`duplicate requestId: ${frame.requestId}`);\n }\n\n const response = new Promise<ServerFrame>((resolve, reject) => {\n this.pending.set(frame.requestId, { resolve, reject });\n });\n this.write(frame);\n return response;\n }\n\n onEvent(listener: (frame: LocalRuntimeEventFrame) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n async close(): Promise<void> {\n this.write({ type: \"exit\" });\n await new Promise<void>((resolve) => {\n this.socket.end(resolve);\n });\n }\n\n private consume(chunk: string): void {\n this.buffer += chunk;\n while (true) {\n const newline = this.buffer.indexOf(\"\\n\");\n if (newline < 0) {\n return;\n }\n\n const line = this.buffer.slice(0, newline).trim();\n this.buffer = this.buffer.slice(newline + 1);\n if (line.length > 0) {\n this.dispatchLine(line);\n }\n }\n }\n\n private dispatchLine(line: string): void {\n const parsed = parseServerFrame(line);\n if (!parsed.ok || parsed.frame === undefined) {\n this.rejectAll(\n new Error(`${parsed.error ?? \"invalid server frame\"}: ${line}`)\n );\n return;\n }\n\n this.dispatchFrame(parsed.frame);\n }\n\n private dispatchFrame(frame: ServerFrame): void {\n if (frame.type === \"ready\") {\n this.ready?.resolve(frame);\n this.ready = undefined;\n return;\n }\n\n if (frame.type === \"error\") {\n const error = new Error(`${frame.code}: ${frame.message}`);\n if (frame.requestId !== undefined) {\n this.pending.get(frame.requestId)?.reject(error);\n this.pending.delete(frame.requestId);\n return;\n }\n // An error with no requestId could not be correlated to a specific request.\n // If the handshake is still pending, reject it; otherwise reject every\n // pending request so callers fail loudly instead of hanging indefinitely.\n // (See issue #3: createTray never resolves on a malformed frame.)\n if (this.ready) {\n this.ready.reject(error);\n this.ready = undefined;\n return;\n }\n this.rejectAll(error);\n return;\n }\n\n const requestId = responseRequestId(frame);\n if (requestId !== undefined) {\n // Request responses and broker events are separate streams even on one socket.\n this.pending.get(requestId)?.resolve(frame);\n this.pending.delete(requestId);\n return;\n }\n\n if (frame.type === \"event\" || frame.type === \"ext-event\") {\n for (const listener of this.listeners) {\n listener(frame);\n }\n }\n }\n\n private write(frame: unknown): void {\n this.socket.write(`${JSON.stringify(frame)}\\n`);\n }\n\n private rejectAll(error: Error): void {\n this.ready?.reject(error);\n this.ready = undefined;\n for (const pending of this.pending.values()) {\n pending.reject(error);\n }\n this.pending.clear();\n }\n}\n\nconst connectSocket = (endpoint: string): Promise<BrokerSocket> =>\n new Promise((resolve, reject) => {\n const socket = createConnection(endpoint);\n socket.once(\"connect\", () => {\n socket.off(\"error\", reject);\n resolve(socket);\n });\n socket.once(\"error\", reject);\n });\n\nconst responseRequestId = (frame: ServerFrame): RequestId | undefined => {\n switch (frame.type) {\n case \"app-created\":\n case \"default-app\":\n case \"tray-created\":\n case \"tray-bounds\":\n case \"ack\":\n case \"ext-command-result\":\n case \"runtime-host-health\":\n return frame.requestId;\n case \"ready\":\n case \"event\":\n case \"ext-event\":\n case \"error\":\n return undefined;\n }\n};\n\nconst resolveCliEntrypoint = (): string => {\n const suffix = fileURLToPath(import.meta.url).endsWith(\".ts\")\n ? \"cli.ts\"\n : \"cli.mjs\";\n return fileURLToPath(new URL(`./${suffix}`, import.meta.url));\n};\n","import type { TrayOptions } from \"@opentray/spec\";\n\nimport { createClient, type EventfulTrayHandle } from \"./client\";\nimport { connectLocalBroker } from \"./local-broker\";\nimport type { OpenTrayRuntimeBinding } from \"./native-runtime\";\nimport { createRuntimeBindingTransport } from \"./runtime-binding-transport\";\n\nexport type OpenTrayRuntimeMode =\n | \"visible-binding\"\n | \"local-broker\"\n | \"headless-binding\";\n\nexport interface OpenTrayRuntimeOptions {\n runtime?: OpenTrayRuntimeMode;\n endpoint?: string;\n homeDir?: string;\n packageVersion?: string;\n protocolVersion?: number;\n clientVersion?: string;\n appId?: string;\n appName?: string;\n autoStart?: boolean;\n binding?: OpenTrayRuntimeBinding;\n}\n\nexport const createTray = async (\n options: TrayOptions,\n runtimeOptions: OpenTrayRuntimeOptions = {}\n): Promise<EventfulTrayHandle> => {\n const bindingTransportOptions = {\n ...(runtimeOptions.binding === undefined\n ? {}\n : { binding: runtimeOptions.binding }),\n ...(runtimeOptions.packageVersion === undefined\n ? {}\n : { packageVersion: runtimeOptions.packageVersion }),\n ...(runtimeOptions.clientVersion === undefined\n ? {}\n : { clientVersion: runtimeOptions.clientVersion }),\n ...(runtimeOptions.protocolVersion === undefined\n ? {}\n : { protocolVersion: runtimeOptions.protocolVersion }),\n ...(runtimeOptions.appId === undefined\n ? {}\n : { appId: runtimeOptions.appId }),\n ...(runtimeOptions.appName === undefined\n ? {}\n : { appName: runtimeOptions.appName }),\n };\n const runtime = runtimeOptions.runtime ?? \"local-broker\";\n const connection =\n runtime === \"local-broker\"\n ? await connectLocalBroker(runtimeOptions)\n : await createRuntimeBindingTransport({\n ...bindingTransportOptions,\n kind: runtime === \"headless-binding\" ? \"headless\" : \"visible\",\n });\n return createClient(connection).createTray(options);\n};\n"],"mappings":";;;;;;;;;;AA2HA,MAAa,mBAAmB,mBAAwC;CACtE,MAAM;CACN,iBAAiBA;CACjB;AACF;AAcA,SAAgB,aACd,WACA,UAA+B,CAAC,GACS;CACzC,MAAM,gBAAgB,uBACpB,QAAQ,mBAAmB,UAC7B;CAEA,OAAO,EACL,MAAM,WAAW,SAA2C;EAC1D,MAAM,MAAM,MAAM,qBAAqB,WAAW,aAAa;EAC/D,MAAM,YAAY,cAAc;EAOhC,MAAM,QAAQ,eAAe,MANN,UAAU,QAAQ;GACvC,MAAM;GACN;GACA;GACA,MAAM;EACR,CAAC,GACsC,WAAW,cAAc;EAChE,OAAO,iBACL,WACA,IAAI,OACJ,MAAM,UAAU,QAAQ,IACxB,aACF;CACF,EACF;AACF;AAcA,SAAgB,iBACd,WACA,OACA,QACA,gBAAiC,uBAAuB,UAAU,GACjC;CACjC,IAAI,mBAAmB;CACvB,MAAM,eAAe,kBAAkC;EACrD,MAAM,UAAU,GAAG,uBACjB,aACF,EAAE,GAAG,uBAAuB,MAAM,EAAE,GAAG;EACvC,oBAAoB;EACpB,OAAO;CACT;CACA,MAAM,SAAqB;EACzB;EACA,MAAM,YAAuC;GAC3C,MAAM,YAAY,cAAc;GAOhC,OAAO,eAAe,MANC,UAAU,QAAQ;IACvC,MAAM;IACN;IACA;IACA;GACF,CAAC,GAC+B,WAAW,aAAa,EAAE;EAC5D;EACA,MAAM,QAAQ,MAA2B;GACvC,MAAM,YAAY,cAAc;GAQhC,eAAe,MAPQ,UAAU,QAAQ;IACvC,MAAM;IACN;IACA;IACA;IACA;GACF,CAAC,GACwB,WAAW,KAAK;EAC3C;EACA,MAAM,WAAW,SAAiC;GAChD,MAAM,YAAY,cAAc;GAQhC,eAAe,MAPQ,UAAU,QAAQ;IACvC,MAAM;IACN;IACA;IACA;IACA;GACF,CAAC,GACwB,WAAW,KAAK;EAC3C;EACA,MAAM,QAAQ,MAA2B;GACvC,MAAM,YAAY,cAAc;GAQhC,eAAe,MAPQ,UAAU,QAAQ;IACvC,MAAM;IACN;IACA;IACA;IACA;GACF,CAAC,GACwB,WAAW,KAAK;EAC3C;EACA,MAAM,cAAc,SAAwB;GAC1C,MAAM,YAAY,cAAc;GAShC,eAAe,MARQ,UAAU,QAAQ;IACvC,MAAM;IACN;IACA;IACA,MAAM,QAAQ;IACd,MAAM,QAAQ;IACd,GAAI,QAAQ,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,QAAQ,QAAQ;GACtE,CAAC,GACwB,WAAW,KAAK;EAC3C;EACA,MAAM,iBAAiB,KAAa,MAA8B;GAChE,MAAM,KAAK,iBAAiB,KAAK,IAAI;EACvC;EACA,MAAM,iBACJ,KACA,MAC8B;GAC9B,MAAM,YAAY,cAAc;GAChC,MAAM,WAAW,MAAM,UAAU,QAAQ;IACvC,MAAM;IACN;IACA;IACA;IACA;IACA;GACF,CAAC;GACD,IAAI,SAAS,SAAS,OAAO;IAC3B,eAAe,UAAU,WAAW,KAAK;IACzC,OAAO,CAAC;GACV;GACA,OAAO,eAAe,UAAU,WAAW,oBAAoB,EAAE;EACnE;EACA,OACE,WACA,SAC0B;GAC1B,MAAM,UAAU,2BACd,QACA,WACA,SACA,WACF;GACA,MAAM,aAAa,UAAU,OAAO,QAAQ,SAAS,OAAO;GAC5D,OAAO,OAAO,OAAO,CAAC,GAAG,QAAQ,UAAU;EAC7C;EACA,MAAM,UAAyB;GAC7B,MAAM,YAAY,cAAc;GAOhC,eAAe,MANQ,UAAU,QAAQ;IACvC,MAAM;IACN;IACA;IACA;GACF,CAAC,GACwB,WAAW,KAAK;EAC3C;CACF;CACA,IAAI,CAAC,sBAAsB,SAAS,GAClC,OAAO;CAET,OAAO,yBAAyB,QAAQ,WAAW,OAAO,WAAW;AACvE;AAEA,MAAM,4BACJ,QACA,QACA,OACA,gBACuB;CACvB,MAAM,UAAwC,OAAO,YACnD,OAAO,SAAS,UAAU;EACxB,IAAI,MAAM,SAAS,WAAW,MAAM,MAAM,SAAS,OACjD;EAEF,IAAI,CAAC,iBAAiB,MAAM,OAAO,OAAO,OAAO,MAAM,GACrD;EAEF,QAAQ,MAAM,KAAsC;CACtD,CAAC;CAEH,MAAM,WAA+B;EACnC,GAAG;EACH,gBACE,KACA,SACA;GACA,OAAO,OAAO,SAAS,UAAU;IAC/B,IAAI,MAAM,SAAS,eAAe,MAAM,QAAQ,KAC9C;IAEF,IAAI,MAAM,UAAU,SAAS,MAAM,WAAW,OAAO,QACnD;IAEF,QAAQ;KACN,OAAO;MAAE,OAAO,MAAM;MAAO,QAAQ,MAAM;MAAQ,KAAK,MAAM;KAAI;KAClE,MAAM,MAAM;IACd,CAAC;GACH,CAAC;EACH;EACA;EACA,YAAY,SAAS;GACnB,OAAO,OAAO,aAAa,OAAO;EACpC;EACA,YAAY,SAAS;GACnB,OAAO,OAAO,aAAa,OAAO;EACpC;EACA,kBAAkB,SAAS;GACzB,OAAO,OAAO,mBAAmB,OAAO;EAC1C;EACA,OACE,WACA,SACkC;GAClC,MAAM,UAAU,2BACd,UACA,WACA,SACA,WACF;GACA,MAAM,aAAa,UAAU,OAAO,UAAU,SAAS,OAAO;GAC9D,OAAO,OAAO,OAAO,CAAC,GAAG,UAAU,UAAU;EAC/C;CACF;CACA,OAAO;AACT;AAEA,MAAM,oBACJ,OACA,OACA,WAEA,MAAM,SAAS,WAAW,MAAM,UAAU,SAAS,MAAM,WAAW;AAEtE,MAAM,yBACJ,cAEA,aAAa,aAAa,OAAO,UAAU,YAAY;AAEzD,MAAM,uBAAuB,OAC3B,WACA,kBAC+B;CAC/B,MAAM,YAAY,cAAc;CAKhC,OAAO,eAAe,MAJC,UAAU,QAAQ;EACvC,MAAM;EACN;CACF,CAAC,GAC+B,WAAW,aAAa,EAAE;AAC5D;AAEA,MAAM,8BACJ,MACA,WACA,SACA,gBACyB;CACzB,MAAM,QAAQ,UAAU,eAAe,OAAO,KAAK,CAAC;CACpD,MAAM,OAAO,MAAM,QAAQ,UAAU;CACrC,MAAM,OAAO,MAAM,QAAQ,UAAU;CACrC,MAAM,UAAU,MAAM,WAAW,YAAY,IAAI;CACjD,IAAI;CAEJ,MAAM,eAAe,YAA2B;EAC9C,IAAI,gBAAgB,KAAA,GAClB,cAAc,KACX,cAAc;GAAE;GAAM;GAAM;EAAQ,CAAC,EACrC,OAAO,UAAmB;GACzB,cAAc,KAAA;GACd,MAAM;EACR,CAAC;EAEL,MAAM;CACR;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA,MAAM,QAAQ,MAA8B;GAC1C,MAAM,aAAa;GACnB,MAAM,KAAK,iBAAiB,SAAS,IAAI;EAC3C;EACA,MAAM,QAAQ,MAA6C;GACzD,MAAM,aAAa;GACnB,OAAO,KAAK,iBAAiB,SAAS,IAAI;EAC5C;CACF;AACF;AAEA,MAAM,0BAA0B,UAA0B;CACxD,MAAM,YAAY,MAAM,WAAW,qBAAqB,GAAG;CAC3D,OAAO,UAAU,SAAS,IAAI,YAAY;AAC5C;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,MACR,YAAY,KAAK,OAAO,UAAU,aAAa,MAAM,MACvD;CAEF,IAAI,YAAY,KAAK,MAAM,WACzB,MAAM,IAAI,MACR,yBAAyB,UAAU,aACjC,YAAY,KAAK,KAAK,QAE1B;CAEF,OAAO;AACT;AAEA,MAAM,eAAe,UAA8C;CACjE,QAAQ,MAAM,MAAd;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,uBACH,OAAO,MAAM;EACf,KAAK,SACH,OAAO,MAAM;EACf,KAAK;EACL,KAAK;EACL,KAAK,aACH;CACJ;AACF;;;;;;;;;AC9cA,MAAa,sBAAsB,UAAqC,CAAC,MAAc;CACrF,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,OAAO,QAAQ,QAAQ,QAAQ;CAErC,MAAM,aAAuB,CAAC;CAC9B,IAAI,QAAQ,aAAa,KAAA,KAAa,QAAQ,SAAS,SAAS,GAC9D,WAAW,KAAK,QAAQ,QAAQ;CAElC,MAAM,UAAU,QAAQ,kBAAkB,IAAI;CAC9C,IAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,GAClD,WAAW,KAAK,OAAO;CAEzB,MAAM,aAAa,QAAQ,cAAc,KAAK;CAC9C,IAAI,OAAO,eAAe,YAAY,WAAW,SAAS,GACxD,WAAW,KAAK,SAAS,UAAU,CAAC;CAGtC,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,YAAY,oBAAoB,SAAS;EAC/C,IAAI,UAAU,SAAS,GACrB,OAAO;CAEX;CAEA,OAAO,oBAAoB,KAAA,CAAS;AACtC;;;ACzCA,MAAa,6BACX,WAAmB,QAAQ,UAC3B,OAAe,QAAQ,SACA;CACvB,MAAM,YAAY,oBAAoB,QAAQ;CAE9C,OAAO;EACL,aAAa,aAAa,UAAU,GAFnB,cAAc,IAEiB;EAChD,oBAAoB,OAAO,cAAc,YAAY,iBAAiB;CACxE;AACF;AAEA,MAAM,uBAAuB,aAAsC;CACjE,QAAQ,UAAR;EACE,KAAK;EACL,KAAK,SACH,OAAO;EACT,KAAK,SACH,OAAO;EACT,SACE,MAAM,IAAI,iCACR,yCAAyC,YACzC,UACA,QAAQ,IACV;CACJ;AACF;AAEA,MAAM,iBAAiB,SAA6B;CAClD,QAAQ,MAAR;EACE,KAAK;EACL,KAAK,OACH,OAAO;EACT,SACE,MAAM,IAAI,iCACR,6CAA6C,QAC7C,QAAQ,UACR,IACF;CACJ;AACF;AAEA,IAAa,mCAAb,cAAsD,MAAM;CAC1D,OAAgB;CAChB;CACA;CACA;CACA;CAEA,YACE,SACA,UACA,MACA,UAII,CAAC,GACL;EACA,MAAM,SAAS,QAAQ,UAAU,KAAA,IAAY,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,CAAC;EACjF,KAAK,OAAO;EACZ,KAAK,WAAW;EAChB,KAAK,OAAO;EACZ,KAAK,cAAc,QAAQ;EAC3B,KAAK,aAAa,QAAQ;CAC5B;AACF;;;ACxDA,MAAM,YAAY,OAAO,KAAK;AAW9B,MAAa,uBAAuB,OAClC,OACA,UAAuC,CAAC,MACb;CAE3B,MAAM,YADM,QAAQ,OAAO,QAAQ,KACd;CACrB,IAAI,aAAa,KAAA,KAAa,SAAS,SAAS,GAC9C,OAAO,iBAAiB,UAAU,KAAK;CAGzC,MAAM,YAAY,QAAQ,aAAa,QAAQ,cAAc,SAAS,CAAC;CACvE,MAAM,gBAAgB,OAAO,QAAQ,qBAAqB,mBACxD,SACF;CACA,IAAI,kBAAkB,KAAA,GAIpB,OAAO,iBAAiB,OAFtB,QAAQ,yBAAyB,uBACjC,aAAa,GACiB,OAAO,aAAa;CAGtD,MAAM,WAAW,QAAQ,YAAY,QAAQ;CAC7C,MAAM,OAAO,QAAQ,QAAQ,QAAQ;CACrC,MAAM,SAAS,0BAA0B,UAAU,IAAI;CACvD,MAAM,IAAI,iCACR,sDAAsD,SAAS,GAAG,KAAK,6DACvE,UACA,MACA,EAAE,aAAa,OAAO,YAAY,CACpC;AACF;AAEA,MAAM,oBACJ,QACA,OACA,QACkB;CAClB,MAAM,UAAyB;EAC7B,SAAS;EACT,MAAM;GACJ;GACA;GACA,MAAM;GACN;GACA,MAAM;GACN;GACA,MAAM;GACN;GACA,GAAG,MAAM;GACT;GACA,MAAM;GACN;GACA,MAAM;GACN;GACA,MAAM;EACR;CACF;CACA,IAAI,QAAQ,KAAA,GACV,QAAQ,MAAM;CAEhB,OAAO;AACT;AAEA,MAAM,oBAAoB,OACxB,UACgC;CAChC,IAAI,UAAU;CACd,OAAO,MAAM;EACX,IAAI,MAAM,OAAO,KAAK,SAAS,YAAY,CAAC;OACtC,MAAM,OAAO,KAAK,SAAS,UAAU,gBAAgB,YAAY,CAAC,GACpE,OAAO;EAAA;EAIX,MAAM,SAAS,QAAQ,OAAO;EAC9B,IAAI,WAAW,SACb;EAEF,UAAU;CACZ;AACF;AAEA,MAAa,8BACX,eACA,WAA4B,QAAQ,aAEpC,KACE,eACA,UACA,SACA,aAAa,UAAU,iBAAiB,UAC1C;AAEF,MAAa,qCAAqC,OAChD,eACA,WAA4B,QAAQ,aAClB;CAClB,IAAI,aAAa,SACf;CAEF,MAAM,oCACJ,2BAA2B,eAAe,QAAQ,CACpD;AACF;AAEA,MAAM,wBAAwB,OAC5B,kBACoB;CACpB,MAAM,SAAS,2BAA2B,aAAa;CACvD,MAAM,mCAAmC,aAAa;CACtD,MAAM,cAAc,aAAa;CACjC,OAAO;AACT;AAEA,MAAM,iBAAiB,kBACrB,IAAI,SAAS,SAAS,WAAW;CAC/B,MAAM,QAAQ,MAAM,SAAS;EAAC;EAAS;EAAM;CAAc,GAAG;EAC5D,KAAK;EACL,OACE,QAAQ,IAAI,+BAA+B,MAAM,YAAY;CACjE,CAAC;CACD,MAAM,KAAK,SAAS,MAAM;CAC1B,MAAM,KAAK,SAAS,SAAS;EAC3B,IAAI,SAAS,GAAG;GACd,QAAQ;GACR;EACF;EACA,uBACE,IAAI,MACF,gDAAgD,QAAQ,WAC1D,CACF;CACF,CAAC;AACH,CAAC;AAEH,MAAM,SAAS,OAAO,SAAmC;CACvD,IAAI;EACF,MAAM,OAAO,MAAM,UAAU,IAAI;EACjC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAM,uCAAuC,WAC3C,IAAI,SAAS,SAAS,WAAW;CAc/B,MAAM,QAAQ,MACZ,kBACA;EACE;EACA;EACA;EACA;EACA;EACA,OArBkB,GAAG;;0CAEa,iBAAiB,MAAM,EAAE;;;;;;;;;;;CAoB7D,GACA,EAAE,OAAO;EAAC;EAAU;EAAU;CAAM,EAAE,CACxC;CACA,IAAI,SAAS;CACb,MAAM,QAAQ,GAAG,SAAS,UAA2B;EACnD,UAAU,OAAO,KAAK;CACxB,CAAC;CACD,MAAM,KAAK,SAAS,MAAM;CAC1B,MAAM,KAAK,SAAS,SAAS;EAC3B,IAAI,SAAS,GAAG;GACd,QAAQ;GACR;EACF;EACA,uBACE,IAAI,MACF,yDACE,OAAO,KAAK,KAAK,MAErB,CACF;CACF,CAAC;AACH,CAAC;AAEH,MAAM,oBAAoB,UACxB,IAAI,MAAM,QAAQ,MAAM,IAAI,EAAE;;;ACtNhC,MAAM,mBAAmB;AAoCzB,MAAa,0BACX,mBACkB;CAClB,MAAM,QAAQ,KAAK;EACjB,OAAO,eAAe,GAAG;CAC3B;CACA,MAAM,YAAY,OAAO;EACvB,MAAM,SAAS,MAAM,qBAAqB,KAAK;EAI/C,MAAM,QAAQ,MAAM,OAAO,SAAS,OAAO,MAAM;GAC/C,KAAK,OAAO;GACZ,UAAU;GACV,KAAK;IACH,GAAG,QAAQ;IACX,sBAAsB,MAAM;IAC5B,iCAAiC,MAAM;IACvC,gCAAgC;IAChC,wBAAwB,MAAM;IAC9B,0BAA0B,MAAM;IAChC,8BAA8B,MAAM;GACtC;GACA,OAAO,mBAAmB,QAAQ,IAAI,iBAAiB;EACzD,CAAC;EAED,MAAM,MAAM;EACZ,OAAO,MAAM,OAAO;CACtB;CACA,MAAM,KAAK,KAAK;EACd,QAAQ,KAAK,KAAK,SAAS;CAC7B;AACF;AAEA,MAAa,sBACX,UACyB;CACzB,OAAO,UAAU,YAAY,YAAY;AAC3C;AAEA,MAAa,cAAc,OAAO,EAChC,OACA,QACA,gBAAgB,UACoC;CACpD,MAAM,MAAM,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;CAEjD,MAAM,OAAO,MAAM,YAAY,MAAM,UAAU,aAAa;CAC5D,IAAI;EACF,MAAM,cAAc,MAAM,QAAQ,MAAM,OAAO;EAC/C,IAAI,gBAAgB,KAAA,KAAc,MAAM,OAAO,QAAQ,WAAW,GAChE,OAAO;GAAE,QAAQ;GAAmB,KAAK;GAAa;EAAM;EAG9D,MAAM,oBAAoB,KAAK;EAC/B,MAAM,MAAM,MAAM,OAAO,YAAY,KAAK;EAC1C,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,OAAO,GACnC,MAAM,IAAI,MAAM,kDAAkD;EAEpE,MAAM,UAAU,MAAM,SAAS,GAAG,IAAI,KAAK,MAAM;EACjD,MAAM,iBAAiB,MAAM,WAAW,QAAQ,GAAG;EACnD,OAAO;GAAE,QAAQ;GAAW;GAAK;EAAM;CACzC,UAAU;EACR,MAAM,KAAK,QAAQ;CACrB;AACF;AAwCA,MAAM,UAAU,OAAO,YAAiD;CACtE,IAAI;EACF,MAAM,UAAU,MAAM,SAAS,SAAS,MAAM;EAC9C,MAAM,MAAM,OAAO,SAAS,QAAQ,KAAK,GAAG,EAAE;EAC9C,OAAO,OAAO,UAAU,GAAG,KAAK,MAAM,IAAI,MAAM,KAAA;CAClD,SAAS,OAAO;EACd,IAAI,YAAY,KAAK,KAAK,MAAM,SAAS,UACvC;EAEF,MAAM;CACR;AACF;AAEA,MAAM,sBAAsB,OAAO,UAAsC;CACvE,MAAM,GAAG,MAAM,WAAW,EAAE,OAAO,KAAK,CAAC;CACzC,MAAM,GAAG,MAAM,SAAS,EAAE,OAAO,KAAK,CAAC;CACvC,IAAI,QAAQ,aAAa,SACvB,MAAM,GAAG,MAAM,UAAU,EAAE,OAAO,KAAK,CAAC;AAE5C;AAcA,MAAM,mBAAmB,OACvB,WACA,QACA,QACkB;CAClB,KAAK,IAAI,UAAU,GAAG,UAAU,IAAI,WAAW,GAC7C,IAAI;EACF,MAAM,SAAS,WAAW,MAAM;EAChC;CACF,SAAS,OAAO;EACd,IAAI,CAAC,YAAY,KAAK,KAAK,MAAM,SAAS,UACxC,MAAM;EAER,IAAI,CAAE,MAAM,OAAO,QAAQ,GAAG,GAC5B,MAAM,IAAI,MACR,8CAA8C,IAAI,cAAc,UAAU,QAAQ,iBAAiB,kCACrG;EAEF,MAAM,MAAM,EAAE;CAChB;CAGF,MAAM,IAAI,MAAM,2CAA2C,WAAW;AACxE;AAEA,MAAM,cAAc,OAClB,UACA,cAGI;CACJ,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,OAAO,KAAK,IAAI,KAAK,UACnB,IAAI;EACF,MAAM,SAAS,MAAM,KACnB,UACA,UAAU,UAAU,UAAU,SAAS,UAAU,QACnD;EACA,MAAM,OAAO,UAAU,GAAG,QAAQ,IAAI,KAAK,MAAM;EACjD,OAAO,EACL,MAAM,UAAU;GACd,MAAM,OAAO,MAAM;GACnB,MAAM,OAAO,QAAQ,EAAE,OAAO,UAAmB;IAC/C,IAAI,CAAC,YAAY,KAAK,KAAK,MAAM,SAAS,UACxC,MAAM;GAEV,CAAC;EACH,EACF;CACF,SAAS,OAAO;EACd,IAAI,YAAY,KAAK,KAAK,MAAM,SAAS,UAAU;GACjD,MAAM,MAAM,EAAE;GACd;EACF;EACA,MAAM;CACR;CAGF,MAAM,IAAI,MAAM,oCAAoC,UAAU;AAChE;AAEA,MAAM,kBAAkB,QAAyB;CAC/C,IAAI;EACF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAM,SAAS,OACb,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;AAElD,MAAM,eAAe,UACnB,iBAAiB,SAAS,UAAU;;;AC7PtC,MAAa,qBAAqB,OAAO,mBAAyC;CAChF,MAAM,QAAiB,KAAK,MAAM,MAAM,SAAS,gBAAgB,MAAM,CAAC;CACxE,IAAI,CAAC,yBAAyB,KAAK,GACjC,MAAM,IAAI,MAAM,6CAA6C,eAAe,MAAM;CAGpF,OAAO,MAAM;AACf;AAEA,MAAM,4BAA4B,UAChC,OAAO,UAAU,YACjB,UAAU,QACV,CAAC,MAAM,QAAQ,KAAK,KACpB,aAAa,SACb,OAAO,MAAM,YAAY;;;ACc3B,MAAa,sBAAsB,EACjC,SACA,gBACA,aACA,OACA,SACA,WAAW,QAAQ,eACiB;CACpC,MAAM,WAAWC,+BACf,gBAAgB,KAAA,IACZ,EAAE,eAAe,IACjB;EAAE;EAAgB;CAAY,CACpC;CACA,MAAM,gBAAgBC,4BAA0B,OAAO,SAAS,WAAW;CAC3E,MAAM,kBAAkBA,4BACtB,SACA,SAAS,WACX;CACA,MAAM,iBAAiB,QAAQ,QAAQ,YAAY,EAAE;CACrD,MAAM,YAAY,GAAG,eAAe,aAAa,SAAS,eAAe,GAAG,SAAS;CACrF,MAAM,aAAa,GAAG,UAAU;CAChC,MAAM,WACJ,aAAa,UACTC,wBAAsB,QAAQ,IAC9BC,uBAAqB,gBAAgB,QAAQ;CAEnD,OAAO;EACL,SAAS;EACT,gBAAgB,SAAS;EACzB,aAAa,SAAS;EACtB,OAAO;EACP,SAAS;EACT,iBAAiB,SAAS;EAC1B;EACA;EACA;EACA,SAAS,GAAG,WAAW;EACvB,UAAU,GAAG,WAAW;EACxB,WAAW,GAAG,WAAW;CAC3B;AACF;AAEA,MAAMF,+BACJ,OACA,aACW;CACX,MAAM,UAAU,OAAO,KAAK;CAC5B,OAAO,YAAY,KAAA,KAAa,QAAQ,WAAW,IAAI,WAAW;AACpE;;;ACvDA,MAAM,iBAAiB,IAAI,IAAI,mBAAmB,OAAO,KAAK,GAAG;AA6CjE,MAAa,qBAAqB,OAChC,UAAqC,CAAC,MACP;CAC/B,MAAM,iBACJ,QAAQ,kBAAmB,MAAM,mBAAmB,cAAc;CACpE,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,MAAM,QAAQ,0BAA0B,QAAQ,KAAK;CACrD,MAAM,UAAU,0BAA0B,QAAQ,OAAO;CACzD,MAAM,cACJ,QAAQ,eAAe,WAAW,SAAS,mBAAmB;CAChE,MAAM,QAAQ,mBAAmB;EAC/B,SAAS,QAAQ,WAAW,QAAQ,IAAI,iBAAiB,QAAQ;EACjE;EACA;EACA,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACvC,GAAI,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ;CAC7C,CAAC;CACD,MAAM,WAAW,QAAQ,YAAY,MAAM;CAC3C,MAAM,YAAY,QAAQ,aAAa,aAAa,MAAM;CAC1D,IAAI,aAAa,aAAa,MAAM,UAClC,MAAM,IAAI,MACR,mEACF;CAEF,IAAI,WAAW;EACb,MAAM,gBAAgB,QAAQ,iBAAiB,qBAAqB;EAGpE,MAAM,YAAY;GAAE;GAAO,QADzB,QAAQ,gBAAgB,uBAAuB,aAAa;EAC5B,CAAC;CACrC;CAEA,MAAM,aAAa,IAAI,sBAAsB,MADxB,cAAc,QAAQ,GACU,UAAU,WAAW;CAE1E,MAAM,WAAW,KACf,eACA,QAAQ,mBAAmBG,kBAC7B;CACA,OAAO;AACT;AAEA,MAAM,6BACJ,UACuB;CACvB,MAAM,UAAU,OAAO,KAAK;CAC5B,OAAO,YAAY,KAAA,KAAa,QAAQ,WAAW,IAAI,KAAA,IAAY;AACrE;AAEA,IAAM,wBAAN,MAAyD;CAkBpC;CAjBnB;CACA;CACA,YAAY;CAEZ,SAAiB;CACjB,4BAA6B,IAAI,IAE/B;CACF,0BAA2B,IAAI,IAA+B;CAC9D;CAOA,YACE,QACA,UACA,aACA;EAHiB,KAAA,SAAA;EAIjB,KAAK,WAAW;EAChB,KAAK,cAAc;EACnB,OAAO,YAAY,MAAM;EACzB,OAAO,GAAG,SAAS,UAA2B;GAC5C,KAAK,QAAQ,OAAO,KAAK,CAAC;EAC5B,CAAC;EACD,OAAO,GAAG,UAAU,UAAU;GAC5B,KAAK,UAAU,KAAK;EACtB,CAAC;EACD,OAAO,GAAG,eAAe;GACvB,KAAK,0BAAU,IAAI,MAAM,0BAA0B,CAAC;EACtD,CAAC;CACH;CAEA,MAAM,KAAK,eAAuB,iBAAwC;EACxE,MAAM,QAAQ,IAAI,SACf,SAAS,WAAW;GACnB,KAAK,QAAQ;IAAE;IAAS;GAAO;EACjC,CACF;EACA,KAAK,MAAM;GACT,MAAM;GACN;GACA;EACF,CAAC;EACD,MAAM,QAAQ,MAAM;EACpB,KAAK,YAAY,MAAM;CACzB;CAEA,MAAM,QAAQ,OAAiD;EAC7D,IAAI,KAAK,QAAQ,IAAI,MAAM,SAAS,GAClC,MAAM,IAAI,MAAM,wBAAwB,MAAM,WAAW;EAG3D,MAAM,WAAW,IAAI,SAAsB,SAAS,WAAW;GAC7D,KAAK,QAAQ,IAAI,MAAM,WAAW;IAAE;IAAS;GAAO,CAAC;EACvD,CAAC;EACD,KAAK,MAAM,KAAK;EAChB,OAAO;CACT;CAEA,QAAQ,UAA+D;EACrE,KAAK,UAAU,IAAI,QAAQ;EAC3B,aAAa;GACX,KAAK,UAAU,OAAO,QAAQ;EAChC;CACF;CAEA,MAAM,QAAuB;EAC3B,KAAK,MAAM,EAAE,MAAM,OAAO,CAAC;EAC3B,MAAM,IAAI,SAAe,YAAY;GACnC,KAAK,OAAO,IAAI,OAAO;EACzB,CAAC;CACH;CAEA,QAAgB,OAAqB;EACnC,KAAK,UAAU;EACf,OAAO,MAAM;GACX,MAAM,UAAU,KAAK,OAAO,QAAQ,IAAI;GACxC,IAAI,UAAU,GACZ;GAGF,MAAM,OAAO,KAAK,OAAO,MAAM,GAAG,OAAO,EAAE,KAAK;GAChD,KAAK,SAAS,KAAK,OAAO,MAAM,UAAU,CAAC;GAC3C,IAAI,KAAK,SAAS,GAChB,KAAK,aAAa,IAAI;EAE1B;CACF;CAEA,aAAqB,MAAoB;EACvC,MAAM,SAAS,iBAAiB,IAAI;EACpC,IAAI,CAAC,OAAO,MAAM,OAAO,UAAU,KAAA,GAAW;GAC5C,KAAK,0BACH,IAAI,MAAM,GAAG,OAAO,SAAS,uBAAuB,IAAI,MAAM,CAChE;GACA;EACF;EAEA,KAAK,cAAc,OAAO,KAAK;CACjC;CAEA,cAAsB,OAA0B;EAC9C,IAAI,MAAM,SAAS,SAAS;GAC1B,KAAK,OAAO,QAAQ,KAAK;GACzB,KAAK,QAAQ,KAAA;GACb;EACF;EAEA,IAAI,MAAM,SAAS,SAAS;GAC1B,MAAM,wBAAQ,IAAI,MAAM,GAAG,MAAM,KAAK,IAAI,MAAM,SAAS;GACzD,IAAI,MAAM,cAAc,KAAA,GAAW;IACjC,KAAK,QAAQ,IAAI,MAAM,SAAS,GAAG,OAAO,KAAK;IAC/C,KAAK,QAAQ,OAAO,MAAM,SAAS;IACnC;GACF;GAKA,IAAI,KAAK,OAAO;IACd,KAAK,MAAM,OAAO,KAAK;IACvB,KAAK,QAAQ,KAAA;IACb;GACF;GACA,KAAK,UAAU,KAAK;GACpB;EACF;EAEA,MAAM,YAAY,kBAAkB,KAAK;EACzC,IAAI,cAAc,KAAA,GAAW;GAE3B,KAAK,QAAQ,IAAI,SAAS,GAAG,QAAQ,KAAK;GAC1C,KAAK,QAAQ,OAAO,SAAS;GAC7B;EACF;EAEA,IAAI,MAAM,SAAS,WAAW,MAAM,SAAS,aAC3C,KAAK,MAAM,YAAY,KAAK,WAC1B,SAAS,KAAK;CAGpB;CAEA,MAAc,OAAsB;EAClC,KAAK,OAAO,MAAM,GAAG,KAAK,UAAU,KAAK,EAAE,GAAG;CAChD;CAEA,UAAkB,OAAoB;EACpC,KAAK,OAAO,OAAO,KAAK;EACxB,KAAK,QAAQ,KAAA;EACb,KAAK,MAAM,WAAW,KAAK,QAAQ,OAAO,GACxC,QAAQ,OAAO,KAAK;EAEtB,KAAK,QAAQ,MAAM;CACrB;AACF;AAEA,MAAM,iBAAiB,aACrB,IAAI,SAAS,SAAS,WAAW;CAC/B,MAAM,SAAS,iBAAiB,QAAQ;CACxC,OAAO,KAAK,iBAAiB;EAC3B,OAAO,IAAI,SAAS,MAAM;EAC1B,QAAQ,MAAM;CAChB,CAAC;CACD,OAAO,KAAK,SAAS,MAAM;AAC7B,CAAC;AAEH,MAAM,qBAAqB,UAA8C;CACvE,QAAQ,MAAM,MAAd;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,uBACH,OAAO,MAAM;EACf,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,SACH;CACJ;AACF;AAEA,MAAM,6BAAqC;CACzC,MAAM,SAAS,cAAc,OAAO,KAAK,GAAG,EAAE,SAAS,KAAK,IACxD,WACA;CACJ,OAAO,cAAc,IAAI,IAAI,KAAK,UAAU,OAAO,KAAK,GAAG,CAAC;AAC9D;;;AC5RA,MAAa,aAAa,OACxB,SACA,iBAAyC,CAAC,MACV;CAChC,MAAM,0BAA0B;EAC9B,GAAI,eAAe,YAAY,KAAA,IAC3B,CAAC,IACD,EAAE,SAAS,eAAe,QAAQ;EACtC,GAAI,eAAe,mBAAmB,KAAA,IAClC,CAAC,IACD,EAAE,gBAAgB,eAAe,eAAe;EACpD,GAAI,eAAe,kBAAkB,KAAA,IACjC,CAAC,IACD,EAAE,eAAe,eAAe,cAAc;EAClD,GAAI,eAAe,oBAAoB,KAAA,IACnC,CAAC,IACD,EAAE,iBAAiB,eAAe,gBAAgB;EACtD,GAAI,eAAe,UAAU,KAAA,IACzB,CAAC,IACD,EAAE,OAAO,eAAe,MAAM;EAClC,GAAI,eAAe,YAAY,KAAA,IAC3B,CAAC,IACD,EAAE,SAAS,eAAe,QAAQ;CACxC;CACA,MAAM,UAAU,eAAe,WAAW;CAQ1C,OAAO,aANL,YAAY,iBACR,MAAM,mBAAmB,cAAc,IACvC,MAAM,8BAA8B;EAClC,GAAG;EACH,MAAM,YAAY,qBAAqB,aAAa;CACtD,CAAC,CACuB,EAAE,WAAW,OAAO;AACpD"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["PROTOCOL_VERSION","createBrokerEndpointIdentity","normalizeAppIdentityField","formatWindowsPipeName","formatUnixSocketPath","PROTOCOL_VERSION"],"sources":["../src/client.ts","../src/daemon/caller-label.ts","../src/daemon/native-target.ts","../src/daemon/broker-command.ts","../src/daemon/lifecycle.ts","../src/daemon/package-version.ts","../src/daemon/paths.ts","../src/local-broker.ts","../src/sdk.ts"],"sourcesContent":["import {\n PROTOCOL_VERSION,\n type ClientFrame,\n type ClientRequestFrame,\n type ExtensionEnvelope,\n type Icon,\n type Menu,\n type RequestId,\n type ServerFrame,\n type Tooltip,\n type TrayBoundsResult,\n type TrayEvent,\n type TrayId,\n type TrayOptions,\n} from \"@opentray/spec\";\n\nexport interface OpenTrayTransport {\n request(frame: ClientRequestFrame): Promise<ServerFrame>;\n}\n\nexport type OpenTrayEventFrame = Extract<\n ServerFrame,\n { type: \"event\" | \"ext-event\" }\n>;\n\nexport interface OpenTrayEventSource {\n onEvent(listener: (frame: OpenTrayEventFrame) => void): () => void;\n}\n\nexport interface OpenTrayConnection\n extends OpenTrayTransport,\n OpenTrayEventSource {}\n\nexport interface OpenTrayClient {\n createTray(options: TrayOptions): Promise<TrayHandle>;\n}\n\nexport interface OpenTrayEventfulClient extends OpenTrayClient {\n createTray(options: TrayOptions): Promise<EventfulTrayHandle>;\n}\n\nexport interface TrayHandle {\n trayId: TrayId;\n getBounds(): Promise<TrayBoundsResult>;\n setMenu(menu: Menu): Promise<void>;\n setTooltip(tooltip: Tooltip): Promise<void>;\n setIcon(icon: Icon): Promise<void>;\n loadExtension(options: ExtensionLoadOptions): Promise<void>;\n commandExtension(ext: string, data: unknown): Promise<void>;\n requestExtension(ext: string, data: unknown): Promise<ExtensionEnvelope[]>;\n extend<TCapability extends object, TOptions = undefined>(\n extension: TrayExtension<TCapability, TOptions>,\n options?: TOptions\n ): TrayHandle & TCapability;\n destroy(): Promise<void>;\n}\n\nexport type TrayScopedEvent = Exclude<TrayEvent, { type: \"ready\" }>;\nexport type TrayEventType = TrayScopedEvent[\"type\"];\nexport type TrayEventByType<TEvent extends TrayEventType> = Extract<\n TrayScopedEvent,\n { type: TEvent }\n>;\n\nexport interface EventfulTrayHandle extends TrayHandle {\n extend<TCapability extends object, TOptions = undefined>(\n extension: TrayExtension<TCapability, TOptions>,\n options?: TOptions\n ): EventfulTrayHandle & TCapability;\n listenExtension<TData = unknown>(\n ext: string,\n handler: (event: ExtensionEnvelope<TData>) => void\n ): () => void;\n listen<TEvent extends TrayEventType>(\n event: TEvent,\n handler: (event: TrayEventByType<TEvent>) => void\n ): () => void;\n onMenuClick(\n handler: (event: TrayEventByType<\"menuClick\">) => void\n ): () => void;\n onTrayClick(\n handler: (event: TrayEventByType<\"trayClick\">) => void\n ): () => void;\n onTrayDoubleClick(\n handler: (event: TrayEventByType<\"trayDoubleClick\">) => void\n ): () => void;\n}\n\nexport interface ExtensionLoadOptions {\n name: string;\n path: string;\n mountId?: string;\n}\n\nexport interface TrayExtensionMountSpec {\n name?: string;\n path?: string;\n mountId?: string;\n}\n\nexport interface TrayExtensionContext {\n readonly appId: string;\n readonly trayId: TrayId;\n readonly name: string;\n readonly path: string;\n readonly mountId: string;\n ensureLoaded(): Promise<void>;\n command(data: unknown): Promise<void>;\n request(data: unknown): Promise<ExtensionEnvelope[]>;\n}\n\nexport interface TrayExtension<\n TCapability extends object,\n TOptions = undefined\n> {\n readonly name: string;\n readonly path: string;\n resolveMount?(options: TOptions | undefined): TrayExtensionMountSpec;\n extend(\n tray: TrayHandle,\n context: TrayExtensionContext,\n options: TOptions | undefined\n ): TCapability;\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 function createClient(\n transport: OpenTrayConnection,\n options?: CreateClientOptions\n): OpenTrayEventfulClient;\nexport function createClient(\n transport: OpenTrayTransport,\n options?: CreateClientOptions\n): OpenTrayClient;\nexport function createClient(\n transport: OpenTrayTransport,\n options: CreateClientOptions = {}\n): OpenTrayClient | OpenTrayEventfulClient {\n const nextRequestId = createRequestIdFactory(\n options.requestIdPrefix ?? \"opentray\"\n );\n\n return {\n async createTray(options: TrayOptions): Promise<TrayHandle> {\n const app = await resolveDefaultAppRef(transport, nextRequestId);\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"create-tray\",\n requestId,\n app,\n tray: options,\n });\n const frame = expectResponse(response, requestId, \"tray-created\");\n return createTrayHandle(\n transport,\n app.appId,\n frame.trayId ?? options.id,\n nextRequestId\n );\n },\n };\n}\n\nexport function createTrayHandle(\n transport: OpenTrayConnection,\n appId: string,\n trayId: TrayId,\n nextRequestId?: () => RequestId\n): EventfulTrayHandle;\nexport function createTrayHandle(\n transport: OpenTrayTransport,\n appId: string,\n trayId: TrayId,\n nextRequestId?: () => RequestId\n): TrayHandle;\nexport function createTrayHandle(\n transport: OpenTrayTransport,\n appId: string,\n trayId: TrayId,\n nextRequestId: () => RequestId = createRequestIdFactory(\"opentray\")\n): TrayHandle | EventfulTrayHandle {\n let nextMountOrdinal = 1;\n const nextMountId = (extensionName: string): string => {\n const mountId = `${formatMountIdComponent(\n extensionName\n )}.${formatMountIdComponent(trayId)}.${nextMountOrdinal}`;\n nextMountOrdinal += 1;\n return mountId;\n };\n const handle: TrayHandle = {\n trayId,\n async getBounds(): Promise<TrayBoundsResult> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"get-tray-bounds\",\n requestId,\n appId,\n trayId,\n });\n return expectResponse(response, requestId, \"tray-bounds\").bounds;\n },\n async setMenu(menu: Menu): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"set-tray-menu\",\n requestId,\n appId,\n trayId,\n menu,\n });\n expectResponse(response, requestId, \"ack\");\n },\n async setTooltip(tooltip: Tooltip): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"set-tray-tooltip\",\n requestId,\n appId,\n trayId,\n tooltip,\n });\n expectResponse(response, requestId, \"ack\");\n },\n async setIcon(icon: Icon): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"set-tray-icon\",\n requestId,\n appId,\n trayId,\n icon,\n });\n expectResponse(response, requestId, \"ack\");\n },\n async loadExtension(options): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"load-ext\",\n requestId,\n appId,\n name: options.name,\n path: options.path,\n ...(options.mountId === undefined ? {} : { mountId: options.mountId }),\n });\n expectResponse(response, requestId, \"ack\");\n },\n async commandExtension(ext: string, data: unknown): Promise<void> {\n await this.requestExtension(ext, data);\n },\n async requestExtension(\n ext: string,\n data: unknown\n ): Promise<ExtensionEnvelope[]> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"ext-command\",\n requestId,\n appId,\n trayId,\n ext,\n data,\n });\n if (response.type === \"ack\") {\n expectResponse(response, requestId, \"ack\");\n return [];\n }\n return expectResponse(response, requestId, \"ext-command-result\").events;\n },\n extend<TCapability extends object, TOptions = undefined>(\n extension: TrayExtension<TCapability, TOptions>,\n options?: TOptions\n ): TrayHandle & TCapability {\n const context = createTrayExtensionContext(\n handle,\n appId,\n extension,\n options,\n nextMountId\n );\n const capability = extension.extend(handle, context, options);\n return Object.assign({}, handle, capability);\n },\n async destroy(): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"destroy-tray\",\n requestId,\n appId,\n trayId,\n });\n expectResponse(response, requestId, \"ack\");\n },\n };\n if (!isOpenTrayEventSource(transport)) {\n return handle;\n }\n return attachEventfulTrayHandle(handle, transport, appId, nextMountId);\n}\n\nconst attachEventfulTrayHandle = (\n handle: TrayHandle,\n source: OpenTrayEventSource,\n appId: string,\n nextMountId: (extensionName: string) => string\n): EventfulTrayHandle => {\n const listen: EventfulTrayHandle[\"listen\"] = (event, handler) =>\n source.onEvent((frame) => {\n if (frame.type !== \"event\" || frame.event.type !== event) {\n return;\n }\n if (!isOwnedTrayEvent(frame.event, appId, handle.trayId)) {\n return;\n }\n handler(frame.event as TrayEventByType<typeof event>);\n });\n\n const eventful: EventfulTrayHandle = {\n ...handle,\n listenExtension<TData = unknown>(\n ext: string,\n handler: (event: ExtensionEnvelope<TData>) => void\n ) {\n return source.onEvent((frame) => {\n if (frame.type !== \"ext-event\" || frame.ext !== ext) {\n return;\n }\n if (frame.appId !== appId || frame.trayId !== handle.trayId) {\n return;\n }\n handler({\n scope: { appId: frame.appId, trayId: frame.trayId, ext: frame.ext },\n data: frame.data as TData,\n });\n });\n },\n listen,\n onMenuClick(handler) {\n return listen(\"menuClick\", handler);\n },\n onTrayClick(handler) {\n return listen(\"trayClick\", handler);\n },\n onTrayDoubleClick(handler) {\n return listen(\"trayDoubleClick\", handler);\n },\n extend<TCapability extends object, TOptions = undefined>(\n extension: TrayExtension<TCapability, TOptions>,\n options?: TOptions\n ): EventfulTrayHandle & TCapability {\n const context = createTrayExtensionContext(\n eventful,\n appId,\n extension,\n options,\n nextMountId\n );\n const capability = extension.extend(eventful, context, options);\n return Object.assign({}, eventful, capability);\n },\n };\n return eventful;\n};\n\nconst isOwnedTrayEvent = (\n event: TrayEvent,\n appId: string,\n trayId: string\n): event is TrayScopedEvent =>\n event.type !== \"ready\" && event.appId === appId && event.trayId === trayId;\n\nconst isOpenTrayEventSource = (\n transport: OpenTrayTransport\n): transport is OpenTrayConnection =>\n \"onEvent\" in transport && typeof transport.onEvent === \"function\";\n\nconst resolveDefaultAppRef = async (\n transport: OpenTrayTransport,\n nextRequestId: () => RequestId\n): Promise<{ appId: string }> => {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"resolve-default-app\",\n requestId,\n });\n return expectResponse(response, requestId, \"default-app\").app;\n};\n\nconst createTrayExtensionContext = <TCapability extends object, TOptions>(\n tray: TrayHandle,\n appId: string,\n extension: TrayExtension<TCapability, TOptions>,\n options: TOptions | undefined,\n nextMountId: (extensionName: string) => string\n): TrayExtensionContext => {\n const mount = extension.resolveMount?.(options) ?? {};\n const name = mount.name ?? extension.name;\n const path = mount.path ?? extension.path;\n const mountId = mount.mountId ?? nextMountId(name);\n let loadPromise: Promise<void> | undefined;\n\n const ensureLoaded = async (): Promise<void> => {\n if (loadPromise === undefined) {\n loadPromise = tray\n .loadExtension({ name, path, mountId })\n .catch((error: unknown) => {\n loadPromise = undefined;\n throw error;\n });\n }\n await loadPromise;\n };\n\n return {\n appId,\n trayId: tray.trayId,\n name,\n path,\n mountId,\n ensureLoaded,\n async command(data: unknown): Promise<void> {\n await ensureLoaded();\n await tray.commandExtension(mountId, data);\n },\n async request(data: unknown): Promise<ExtensionEnvelope[]> {\n await ensureLoaded();\n return tray.requestExtension(mountId, data);\n },\n };\n};\n\nconst formatMountIdComponent = (value: string): string => {\n const formatted = value.replaceAll(/[^a-zA-Z0-9._+-]/g, \"_\");\n return formatted.length > 0 ? formatted : \"ext\";\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(\n `expected ${type} for ${requestId}, received ${frame.type}`\n );\n }\n if (requestIdOf(frame) !== requestId) {\n throw new Error(\n `expected response for ${requestId}, received ${\n requestIdOf(frame) ?? \"none\"\n }`\n );\n }\n return frame as Extract<ServerFrame, { type: TType }>;\n};\n\nconst requestIdOf = (frame: ServerFrame): RequestId | undefined => {\n switch (frame.type) {\n case \"app-created\":\n case \"default-app\":\n case \"tray-created\":\n case \"tray-bounds\":\n case \"ack\":\n case \"ext-command-result\":\n case \"runtime-host-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","import { basename } from \"node:path\";\n\nimport { sanitizeCallerLabel } from \"@opentray/spec\";\n\nexport interface CallerLabelSources {\n /** Explicit developer-provided label, highest precedence. */\n readonly explicit?: string;\n /** `npm_package_name` environment value. */\n readonly npmPackageName?: string;\n /** Script path resolved from `process.argv[1]` or equivalent. */\n readonly scriptPath?: string;\n}\n\nexport interface ResolveCallerLabelOptions extends Partial<CallerLabelSources> {\n env?: NodeJS.ProcessEnv;\n argv?: readonly string[];\n}\n\n/**\n * Derives a caller label using the precedence: explicit > npm_package_name >\n * script basename > neutral default. The result is always sanitized to a\n * filesystem- and process-safe component, so callers can use it directly in\n * endpoint identity, runtime directories, and process titles.\n */\nexport const resolveCallerLabel = (options: ResolveCallerLabelOptions = {}): string => {\n const env = options.env ?? process.env;\n const argv = options.argv ?? process.argv;\n\n const candidates: string[] = [];\n if (options.explicit !== undefined && options.explicit.length > 0) {\n candidates.push(options.explicit);\n }\n const npmName = options.npmPackageName ?? env.npm_package_name;\n if (typeof npmName === \"string\" && npmName.length > 0) {\n candidates.push(npmName);\n }\n const scriptPath = options.scriptPath ?? argv[1];\n if (typeof scriptPath === \"string\" && scriptPath.length > 0) {\n candidates.push(basename(scriptPath));\n }\n\n for (const candidate of candidates) {\n const sanitized = sanitizeCallerLabel(candidate);\n if (sanitized.length > 0) {\n return sanitized;\n }\n }\n\n return sanitizeCallerLabel(undefined);\n};\n","export type BrokerPackageOs = \"darwin\" | \"linux\" | \"windows\";\nexport type BrokerArch = \"arm64\" | \"x64\";\n\nexport interface BrokerNativeTarget {\n packageName: string;\n binaryRelativePath: string;\n}\n\nexport const resolveBrokerNativeTarget = (\n platform: string = process.platform,\n arch: string = process.arch,\n): BrokerNativeTarget => {\n const packageOs = platformToPackageOs(platform);\n const nativeArch = normalizeArch(arch);\n return {\n packageName: `@opentray/${packageOs}-${nativeArch}`,\n binaryRelativePath: `bin/${packageOs === \"windows\" ? \"opentray.exe\" : \"opentray\"}`,\n };\n};\n\nconst platformToPackageOs = (platform: string): BrokerPackageOs => {\n switch (platform) {\n case \"darwin\":\n case \"linux\":\n return platform;\n case \"win32\":\n return \"windows\";\n default:\n throw new MissingPlatformBrokerBinaryError(\n `unsupported OpenTray broker platform: ${platform}`,\n platform,\n process.arch,\n );\n }\n};\n\nconst normalizeArch = (arch: string): BrokerArch => {\n switch (arch) {\n case \"arm64\":\n case \"x64\":\n return arch;\n default:\n throw new MissingPlatformBrokerBinaryError(\n `unsupported OpenTray broker architecture: ${arch}`,\n process.platform,\n arch,\n );\n }\n};\n\nexport class MissingPlatformBrokerBinaryError extends Error {\n readonly code = \"OPENTRAY_MISSING_PLATFORM_BROKER_BINARY\";\n readonly platform: string;\n readonly arch: string;\n readonly packageName: string | undefined;\n readonly binaryPath: string | undefined;\n\n constructor(\n message: string,\n platform: string,\n arch: string,\n options: {\n packageName?: string;\n binaryPath?: string;\n cause?: unknown;\n } = {},\n ) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"MissingPlatformBrokerBinaryError\";\n this.platform = platform;\n this.arch = arch;\n this.packageName = options.packageName;\n this.binaryPath = options.binaryPath;\n }\n}\n","import { access } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawn } from \"node:child_process\";\n\nimport type { DaemonPaths } from \"./paths\";\nimport {\n MissingPlatformBrokerBinaryError,\n resolveBrokerNativeTarget,\n} from \"./native-target\";\n\nexport interface BrokerCommand {\n command: string;\n args: string[];\n cwd?: string;\n}\n\nconst sourceUrl = import.meta.url;\n\nexport interface ResolveBrokerCommandOptions {\n env?: NodeJS.ProcessEnv;\n platform?: NodeJS.Platform;\n arch?: string;\n sourceDir?: string;\n findWorkspaceRoot?: (start: string) => Promise<string | undefined>;\n ensureDevBrokerBinary?: (workspaceRoot: string) => Promise<string>;\n}\n\nexport const resolveBrokerCommand = async (\n paths: DaemonPaths,\n options: ResolveBrokerCommandOptions = {}\n): Promise<BrokerCommand> => {\n const env = options.env ?? process.env;\n const explicit = env.OPENTRAY_BROKER_BIN;\n if (explicit !== undefined && explicit.length > 0) {\n return commandForBinary(explicit, paths);\n }\n\n const sourceDir = options.sourceDir ?? dirname(fileURLToPath(sourceUrl));\n const workspaceRoot = await (options.findWorkspaceRoot ?? findWorkspaceRoot)(\n sourceDir\n );\n if (workspaceRoot !== undefined) {\n const binary = await (\n options.ensureDevBrokerBinary ?? ensureDevBrokerBinary\n )(workspaceRoot);\n return commandForBinary(binary, paths);\n }\n\n const platform = options.platform ?? process.platform;\n const arch = options.arch ?? process.arch;\n const target = resolveBrokerNativeTarget(platform, arch);\n throw new MissingPlatformBrokerBinaryError(\n `unable to resolve OpenTray debug broker binary for ${platform}/${arch}; run from the source workspace or set OPENTRAY_BROKER_BIN`,\n platform,\n arch,\n { packageName: target.packageName }\n );\n};\n\nconst commandForBinary = (\n binary: string,\n paths: DaemonPaths,\n cwd?: string\n): BrokerCommand => {\n const command: BrokerCommand = {\n command: binary,\n args: [\n \"broker\",\n \"--endpoint\",\n paths.endpoint,\n \"--ready-file\",\n paths.readyFile,\n \"--package-version\",\n paths.packageVersion,\n \"--protocol-version\",\n `${paths.protocolVersion}`,\n \"--app-id\",\n paths.appId,\n \"--app-name\",\n paths.appName,\n \"--caller-label\",\n paths.callerLabel,\n ],\n };\n if (cwd !== undefined) {\n command.cwd = cwd;\n }\n return command;\n};\n\nconst findWorkspaceRoot = async (\n start: string\n): Promise<string | undefined> => {\n let current = start;\n while (true) {\n if (await exists(join(current, \"Cargo.toml\"))) {\n if (await exists(join(current, \"crates\", \"opentray-bin\", \"Cargo.toml\"))) {\n return current;\n }\n }\n\n const parent = dirname(current);\n if (parent === current) {\n return undefined;\n }\n current = parent;\n }\n};\n\nexport const resolveDevBrokerBinaryPath = (\n workspaceRoot: string,\n platform: NodeJS.Platform = process.platform\n): string =>\n join(\n workspaceRoot,\n \"target\",\n \"debug\",\n platform === \"win32\" ? \"opentray.exe\" : \"opentray\"\n );\n\nexport const terminateWorkspaceDevBrokerProcess = async (\n workspaceRoot: string,\n platform: NodeJS.Platform = process.platform\n): Promise<void> => {\n if (platform !== \"win32\") {\n return;\n }\n await terminateWindowsProcessByExecutable(\n resolveDevBrokerBinaryPath(workspaceRoot, platform)\n );\n};\n\nconst ensureDevBrokerBinary = async (\n workspaceRoot: string\n): Promise<string> => {\n const binary = resolveDevBrokerBinaryPath(workspaceRoot);\n await terminateWorkspaceDevBrokerProcess(workspaceRoot);\n await runCargoBuild(workspaceRoot);\n return binary;\n};\n\nconst runCargoBuild = (workspaceRoot: string): Promise<void> =>\n new Promise((resolve, reject) => {\n const child = spawn(\"cargo\", [\"build\", \"-p\", \"opentray-bin\"], {\n cwd: workspaceRoot,\n stdio:\n process.env.OPENTRAY_BROKER_BUILD_LOGS === \"1\" ? \"inherit\" : \"ignore\",\n });\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => {\n if (code === 0) {\n resolve();\n return;\n }\n reject(\n new Error(\n `cargo build -p opentray-bin failed with code ${code ?? \"unknown\"}`\n )\n );\n });\n });\n\nconst exists = async (path: string): Promise<boolean> => {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n};\n\nconst terminateWindowsProcessByExecutable = (binary: string): Promise<void> =>\n new Promise((resolve, reject) => {\n const script = String.raw`\n$ErrorActionPreference = 'Stop'\n$target = [System.IO.Path]::GetFullPath(${powerShellString(binary)})\n$processes = @(Get-CimInstance Win32_Process -Filter \"Name = 'opentray.exe'\" | Where-Object {\n $_.ExecutablePath -and ([System.IO.Path]::GetFullPath($_.ExecutablePath) -ieq $target)\n})\nforeach ($process in $processes) {\n Stop-Process -Id $process.ProcessId -Force -ErrorAction Stop\n}\nforeach ($process in $processes) {\n Wait-Process -Id $process.ProcessId -Timeout 5 -ErrorAction SilentlyContinue\n}\n`;\n const child = spawn(\n \"powershell.exe\",\n [\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-ExecutionPolicy\",\n \"Bypass\",\n \"-Command\",\n script,\n ],\n { stdio: [\"ignore\", \"ignore\", \"pipe\"] }\n );\n let stderr = \"\";\n child.stderr?.on(\"data\", (chunk: Buffer | string) => {\n stderr += String(chunk);\n });\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => {\n if (code === 0) {\n resolve();\n return;\n }\n reject(\n new Error(\n `failed to stop running dev broker before cargo build: ${\n stderr.trim() || code\n }`\n )\n );\n });\n });\n\nconst powerShellString = (value: string): string =>\n `'${value.replace(/'/g, \"''\")}'`;\n\nconst isNodeError = (error: unknown): error is NodeJS.ErrnoException =>\n error instanceof Error && \"code\" in error;\n","import { constants } from \"node:fs\";\nimport { mkdir, open, readFile, rm, unlink, writeFile } from \"node:fs/promises\";\nimport { spawn } from \"node:child_process\";\n\nimport { resolveBrokerCommand } from \"./broker-command\";\nimport type { DaemonPaths } from \"./paths\";\n\nconst DAEMON_STDIO_ENV = \"OPENTRAY_DAEMON_STDIO\";\n\nexport type DaemonStartResult =\n | { status: \"started\"; pid: number; paths: DaemonPaths }\n | { status: \"already-running\"; pid: number; paths: DaemonPaths };\n\nexport type DaemonStopResult =\n | { status: \"stopped\"; pid: number; paths: DaemonPaths }\n | { status: \"not-running\"; paths: DaemonPaths };\n\nexport type DaemonInspectResult =\n | { status: \"running\"; pid: number; paths: DaemonPaths }\n | { status: \"not-running\"; paths: DaemonPaths; stalePid?: number };\n\nexport interface DaemonDriver {\n isAlive(pid: number): Promise<boolean>;\n spawnBroker(paths: DaemonPaths): Promise<number>;\n stop(pid: number): Promise<void>;\n}\n\nexport interface StartDaemonOptions {\n paths: DaemonPaths;\n driver: DaemonDriver;\n lockTimeoutMs?: number;\n}\n\nexport interface StopDaemonOptions {\n paths: DaemonPaths;\n driver: DaemonDriver;\n}\n\nexport interface InspectDaemonOptions {\n paths: DaemonPaths;\n driver: DaemonDriver;\n}\n\nexport const createNodeDaemonDriver = (\n cliEntrypoint: string\n): DaemonDriver => ({\n async isAlive(pid) {\n return isProcessAlive(pid);\n },\n async spawnBroker(paths) {\n const broker = await resolveBrokerCommand(paths);\n // The caller label flows to the broker via both a CLI flag and an env var.\n // The broker uses it to bind the per-caller endpoint and to set its own\n // process title so task managers show the owning application.\n const child = spawn(broker.command, broker.args, {\n cwd: broker.cwd,\n detached: true,\n env: {\n ...process.env,\n OPENTRAY_DAEMON_HOME: paths.homeDir,\n OPENTRAY_DAEMON_PACKAGE_VERSION: paths.packageVersion,\n OPENTRAY_DAEMON_CLI_ENTRYPOINT: cliEntrypoint,\n OPENTRAY_DAEMON_APP_ID: paths.appId,\n OPENTRAY_DAEMON_APP_NAME: paths.appName,\n OPENTRAY_DAEMON_CALLER_LABEL: paths.callerLabel,\n },\n stdio: resolveBrokerStdio(process.env[DAEMON_STDIO_ENV]),\n });\n\n child.unref();\n return child.pid ?? 0;\n },\n async stop(pid) {\n process.kill(pid, \"SIGTERM\");\n },\n});\n\nexport const resolveBrokerStdio = (\n value: string | undefined\n): \"ignore\" | \"inherit\" => {\n return value === \"inherit\" ? \"inherit\" : \"ignore\";\n};\n\nexport const startDaemon = async ({\n paths,\n driver,\n lockTimeoutMs = 1_000,\n}: StartDaemonOptions): Promise<DaemonStartResult> => {\n await mkdir(paths.runtimeDir, { recursive: true });\n\n const lock = await acquireLock(paths.lockFile, lockTimeoutMs);\n try {\n const existingPid = await readPid(paths.pidFile);\n if (existingPid !== undefined && (await driver.isAlive(existingPid))) {\n return { status: \"already-running\", pid: existingPid, paths };\n }\n\n await cleanupRuntimeFiles(paths);\n const pid = await driver.spawnBroker(paths);\n if (!Number.isInteger(pid) || pid <= 0) {\n throw new Error(\"daemon broker process did not expose a valid pid\");\n }\n await writeFile(paths.pidFile, `${pid}\\n`, \"utf8\");\n await waitForReadyFile(paths.readyFile, driver, pid);\n return { status: \"started\", pid, paths };\n } finally {\n await lock.release();\n }\n};\n\nexport const stopDaemon = async ({\n paths,\n driver,\n}: StopDaemonOptions): Promise<DaemonStopResult> => {\n const pid = await readPid(paths.pidFile);\n if (pid === undefined || !(await driver.isAlive(pid))) {\n await cleanupRuntimeFiles(paths);\n return { status: \"not-running\", paths };\n }\n\n await driver.stop(pid);\n await waitUntilStopped(driver, pid);\n await cleanupRuntimeFiles(paths);\n return { status: \"stopped\", pid, paths };\n};\n\nexport const restartDaemon = async (\n options: StartDaemonOptions\n): Promise<DaemonStartResult> => {\n await stopDaemon({ paths: options.paths, driver: options.driver });\n return startDaemon(options);\n};\n\nexport const inspectDaemon = async ({\n paths,\n driver,\n}: InspectDaemonOptions): Promise<DaemonInspectResult> => {\n const pid = await readPid(paths.pidFile);\n if (pid !== undefined && (await driver.isAlive(pid))) {\n return { status: \"running\", pid, paths };\n }\n\n await cleanupRuntimeFiles(paths);\n return pid === undefined\n ? { status: \"not-running\", paths }\n : { status: \"not-running\", paths, stalePid: pid };\n};\n\nconst readPid = async (pidFile: string): Promise<number | undefined> => {\n try {\n const content = await readFile(pidFile, \"utf8\");\n const pid = Number.parseInt(content.trim(), 10);\n return Number.isInteger(pid) && pid > 0 ? pid : undefined;\n } catch (error) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n};\n\nconst cleanupRuntimeFiles = async (paths: DaemonPaths): Promise<void> => {\n await rm(paths.readyFile, { force: true });\n await rm(paths.pidFile, { force: true });\n if (process.platform !== \"win32\") {\n await rm(paths.endpoint, { force: true });\n }\n};\n\nconst waitUntilStopped = async (\n driver: DaemonDriver,\n pid: number\n): Promise<void> => {\n for (let attempt = 0; attempt < 20; attempt += 1) {\n if (!(await driver.isAlive(pid))) {\n return;\n }\n await sleep(50);\n }\n};\n\nconst waitForReadyFile = async (\n readyFile: string,\n driver: DaemonDriver,\n pid: number\n): Promise<void> => {\n for (let attempt = 0; attempt < 40; attempt += 1) {\n try {\n await readFile(readyFile, \"utf8\");\n return;\n } catch (error) {\n if (!isNodeError(error) || error.code !== \"ENOENT\") {\n throw error;\n }\n if (!(await driver.isAlive(pid))) {\n throw new Error(\n `daemon broker exited before readiness: pid=${pid}; readyFile=${readyFile}; set ${DAEMON_STDIO_ENV}=inherit to inspect broker stderr`\n );\n }\n await sleep(50);\n }\n }\n\n throw new Error(`timed out waiting for daemon readiness: ${readyFile}`);\n};\n\nconst acquireLock = async (\n lockFile: string,\n timeoutMs: number\n): Promise<{\n release(): Promise<void>;\n}> => {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() <= deadline) {\n try {\n const handle = await open(\n lockFile,\n constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY\n );\n await handle.writeFile(`${process.pid}\\n`, \"utf8\");\n return {\n async release() {\n await handle.close();\n await unlink(lockFile).catch((error: unknown) => {\n if (!isNodeError(error) || error.code !== \"ENOENT\") {\n throw error;\n }\n });\n },\n };\n } catch (error) {\n if (isNodeError(error) && error.code === \"EEXIST\") {\n await sleep(25);\n continue;\n }\n throw error;\n }\n }\n\n throw new Error(`timed out acquiring daemon lock: ${lockFile}`);\n};\n\nconst isProcessAlive = (pid: number): boolean => {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n};\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nconst isNodeError = (error: unknown): error is NodeJS.ErrnoException =>\n error instanceof Error && \"code\" in error;\n","import { readFile } from \"node:fs/promises\";\n\nexport const readPackageVersion = async (packageJsonUrl: URL): Promise<string> => {\n const value: unknown = JSON.parse(await readFile(packageJsonUrl, \"utf8\"));\n if (!isPackageJsonWithVersion(value)) {\n throw new Error(`package.json is missing a string version: ${packageJsonUrl.href}`);\n }\n\n return value.version;\n};\n\nconst isPackageJsonWithVersion = (value: unknown): value is { version: string } =>\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n \"version\" in value &&\n typeof value.version === \"string\";\n","import {\n createBrokerEndpointIdentity,\n formatUnixSocketPath,\n formatWindowsPipeName,\n} from \"@opentray/spec\";\n\nexport interface DaemonPathOptions {\n homeDir: string;\n packageVersion: string;\n callerLabel?: string;\n appId?: string;\n appName?: string;\n platform?: NodeJS.Platform;\n}\n\nexport interface DaemonPaths {\n homeDir: string;\n packageVersion: string;\n callerLabel: string;\n appId: string;\n appName: string;\n protocolVersion: number;\n stateRoot: string;\n runtimeDir: string;\n endpoint: string;\n pidFile: string;\n lockFile: string;\n readyFile: string;\n}\n\nexport const resolveDaemonPaths = ({\n homeDir,\n packageVersion,\n callerLabel,\n appId,\n appName,\n platform = process.platform,\n}: DaemonPathOptions): DaemonPaths => {\n const identity = createBrokerEndpointIdentity(\n callerLabel === undefined\n ? { packageVersion }\n : { packageVersion, callerLabel }\n );\n const resolvedAppId = normalizeAppIdentityField(appId, identity.callerLabel);\n const resolvedAppName = normalizeAppIdentityField(\n appName,\n identity.callerLabel\n );\n const normalizedHome = homeDir.replace(/[\\\\/]+$/u, \"\");\n const stateRoot = `${normalizedHome}/.opentray/${identity.packageVersion}/${identity.callerLabel}`;\n const runtimeDir = `${stateRoot}/runtime`;\n const endpoint =\n platform === \"win32\"\n ? formatWindowsPipeName(identity)\n : formatUnixSocketPath(normalizedHome, identity);\n\n return {\n homeDir: normalizedHome,\n packageVersion: identity.packageVersion,\n callerLabel: identity.callerLabel,\n appId: resolvedAppId,\n appName: resolvedAppName,\n protocolVersion: identity.protocolVersion,\n stateRoot,\n runtimeDir,\n endpoint,\n pidFile: `${runtimeDir}/broker.pid`,\n lockFile: `${runtimeDir}/broker.lock`,\n readyFile: `${runtimeDir}/ready.json`,\n };\n};\n\nconst normalizeAppIdentityField = (\n value: string | undefined,\n fallback: string\n): string => {\n const trimmed = value?.trim();\n return trimmed === undefined || trimmed.length === 0 ? fallback : trimmed;\n};\n","import { createConnection, type Socket } from \"node:net\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\n\nimport {\n PROTOCOL_VERSION,\n parseServerFrame,\n type BrokerEndpointIdentityOptions,\n type ClientRequestFrame,\n type RequestId,\n type ServerFrame,\n} from \"@opentray/spec\";\n\nimport type { OpenTrayTransport } from \"./client\";\nimport { resolveCallerLabel } from \"./daemon/caller-label\";\nimport {\n createNodeDaemonDriver,\n startDaemon,\n type DaemonDriver,\n} from \"./daemon/lifecycle\";\nimport { readPackageVersion } from \"./daemon/package-version\";\nimport { resolveDaemonPaths } from \"./daemon/paths\";\n\nconst packageJsonUrl = new URL(\"../package.json\", import.meta.url);\n\nexport type LocalRuntimeEventFrame = Extract<\n ServerFrame,\n { type: \"event\" | \"ext-event\" }\n>;\n\nexport interface LocalBrokerClient extends OpenTrayTransport {\n readonly endpoint: string;\n readonly callerLabel: string;\n readonly sessionId: string;\n onEvent(listener: (frame: LocalRuntimeEventFrame) => void): () => void;\n close(): Promise<void>;\n}\n\nexport interface ConnectLocalBrokerOptions\n extends Partial<BrokerEndpointIdentityOptions> {\n endpoint?: string;\n homeDir?: string;\n appId?: string;\n appName?: string;\n clientVersion?: string;\n autoStart?: boolean;\n daemonDriver?: DaemonDriver;\n cliEntrypoint?: string;\n callerLabel?: string;\n}\n\ninterface PendingRequest {\n resolve(frame: ServerFrame): void;\n reject(error: Error): void;\n}\n\ninterface BrokerSocket {\n setEncoding(encoding: BufferEncoding): void;\n on(event: \"data\", listener: (chunk: Buffer | string) => void): void;\n on(event: \"error\", listener: (error: Error) => void): void;\n on(event: \"close\", listener: () => void): void;\n once(event: \"connect\", listener: () => void): void;\n once(event: \"error\", listener: (error: Error) => void): void;\n off(event: \"error\", listener: (error: Error) => void): void;\n write(data: string): void;\n end(callback: () => void): void;\n}\n\nexport const connectLocalBroker = async (\n options: ConnectLocalBrokerOptions = {}\n): Promise<LocalBrokerClient> => {\n const packageVersion =\n options.packageVersion ?? (await readPackageVersion(packageJsonUrl));\n const clientVersion = options.clientVersion ?? packageVersion;\n const appId = normalizeAppIdentityField(options.appId);\n const appName = normalizeAppIdentityField(options.appName);\n const callerLabel =\n options.callerLabel ?? appName ?? appId ?? resolveCallerLabel();\n const paths = resolveDaemonPaths({\n homeDir: options.homeDir ?? process.env.OPENTRAY_HOME ?? homedir(),\n packageVersion,\n callerLabel,\n ...(appId === undefined ? {} : { appId }),\n ...(appName === undefined ? {} : { appName }),\n });\n const endpoint = options.endpoint ?? paths.endpoint;\n const autoStart = options.autoStart ?? endpoint === paths.endpoint;\n if (autoStart && endpoint !== paths.endpoint) {\n throw new Error(\n \"local broker autoStart requires the derived same-version endpoint\"\n );\n }\n if (autoStart) {\n const cliEntrypoint = options.cliEntrypoint ?? resolveCliEntrypoint();\n const driver =\n options.daemonDriver ?? createNodeDaemonDriver(cliEntrypoint);\n await startDaemon({ paths, driver });\n }\n const socket = await connectSocket(endpoint);\n const connection = new LocalBrokerConnection(socket, endpoint, callerLabel);\n\n await connection.init(\n clientVersion,\n options.protocolVersion ?? PROTOCOL_VERSION\n );\n return connection;\n};\n\nconst normalizeAppIdentityField = (\n value: string | undefined\n): string | undefined => {\n const trimmed = value?.trim();\n return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed;\n};\n\nclass LocalBrokerConnection implements LocalBrokerClient {\n readonly endpoint: string;\n readonly callerLabel: string;\n sessionId = \"\";\n\n private buffer = \"\";\n private readonly listeners = new Set<\n (frame: LocalRuntimeEventFrame) => void\n >();\n private readonly pending = new Map<RequestId, PendingRequest>();\n private ready:\n | {\n resolve(frame: Extract<ServerFrame, { type: \"ready\" }>): void;\n reject(error: Error): void;\n }\n | undefined;\n\n constructor(\n private readonly socket: BrokerSocket,\n endpoint: string,\n callerLabel: string\n ) {\n this.endpoint = endpoint;\n this.callerLabel = callerLabel;\n socket.setEncoding(\"utf8\");\n socket.on(\"data\", (chunk: Buffer | string) => {\n this.consume(String(chunk));\n });\n socket.on(\"error\", (error) => {\n this.rejectAll(error);\n });\n socket.on(\"close\", () => {\n this.rejectAll(new Error(\"broker connection closed\"));\n });\n }\n\n async init(clientVersion: string, protocolVersion: number): Promise<void> {\n const ready = new Promise<Extract<ServerFrame, { type: \"ready\" }>>(\n (resolve, reject) => {\n this.ready = { resolve, reject };\n }\n );\n this.write({\n type: \"init\",\n protocolVersion,\n clientVersion,\n });\n const frame = await ready;\n this.sessionId = frame.sessionId;\n }\n\n async request(frame: ClientRequestFrame): Promise<ServerFrame> {\n if (this.pending.has(frame.requestId)) {\n throw new Error(`duplicate requestId: ${frame.requestId}`);\n }\n\n const response = new Promise<ServerFrame>((resolve, reject) => {\n this.pending.set(frame.requestId, { resolve, reject });\n });\n this.write(frame);\n return response;\n }\n\n onEvent(listener: (frame: LocalRuntimeEventFrame) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n async close(): Promise<void> {\n this.write({ type: \"exit\" });\n await new Promise<void>((resolve) => {\n this.socket.end(resolve);\n });\n }\n\n private consume(chunk: string): void {\n this.buffer += chunk;\n while (true) {\n const newline = this.buffer.indexOf(\"\\n\");\n if (newline < 0) {\n return;\n }\n\n const line = this.buffer.slice(0, newline).trim();\n this.buffer = this.buffer.slice(newline + 1);\n if (line.length > 0) {\n this.dispatchLine(line);\n }\n }\n }\n\n private dispatchLine(line: string): void {\n const parsed = parseServerFrame(line);\n if (!parsed.ok || parsed.frame === undefined) {\n this.rejectAll(\n new Error(`${parsed.error ?? \"invalid server frame\"}: ${line}`)\n );\n return;\n }\n\n this.dispatchFrame(parsed.frame);\n }\n\n private dispatchFrame(frame: ServerFrame): void {\n if (frame.type === \"ready\") {\n this.ready?.resolve(frame);\n this.ready = undefined;\n return;\n }\n\n if (frame.type === \"error\") {\n const error = new Error(`${frame.code}: ${frame.message}`);\n if (frame.requestId !== undefined) {\n this.pending.get(frame.requestId)?.reject(error);\n this.pending.delete(frame.requestId);\n return;\n }\n // An error with no requestId could not be correlated to a specific request.\n // If the handshake is still pending, reject it; otherwise reject every\n // pending request so callers fail loudly instead of hanging indefinitely.\n // (See issue #3: createTray never resolves on a malformed frame.)\n if (this.ready) {\n this.ready.reject(error);\n this.ready = undefined;\n return;\n }\n this.rejectAll(error);\n return;\n }\n\n const requestId = responseRequestId(frame);\n if (requestId !== undefined) {\n // Request responses and broker events are separate streams even on one socket.\n this.pending.get(requestId)?.resolve(frame);\n this.pending.delete(requestId);\n return;\n }\n\n if (frame.type === \"event\" || frame.type === \"ext-event\") {\n for (const listener of this.listeners) {\n listener(frame);\n }\n }\n }\n\n private write(frame: unknown): void {\n this.socket.write(`${JSON.stringify(frame)}\\n`);\n }\n\n private rejectAll(error: Error): void {\n this.ready?.reject(error);\n this.ready = undefined;\n for (const pending of this.pending.values()) {\n pending.reject(error);\n }\n this.pending.clear();\n }\n}\n\nconst connectSocket = (endpoint: string): Promise<BrokerSocket> =>\n new Promise((resolve, reject) => {\n const socket = createConnection(endpoint);\n socket.once(\"connect\", () => {\n socket.off(\"error\", reject);\n resolve(socket);\n });\n socket.once(\"error\", reject);\n });\n\nconst responseRequestId = (frame: ServerFrame): RequestId | undefined => {\n switch (frame.type) {\n case \"app-created\":\n case \"default-app\":\n case \"tray-created\":\n case \"tray-bounds\":\n case \"ack\":\n case \"ext-command-result\":\n case \"runtime-host-health\":\n return frame.requestId;\n case \"ready\":\n case \"event\":\n case \"ext-event\":\n case \"error\":\n return undefined;\n }\n};\n\nconst resolveCliEntrypoint = (): string => {\n const suffix = fileURLToPath(import.meta.url).endsWith(\".ts\")\n ? \"cli.ts\"\n : \"cli.mjs\";\n return fileURLToPath(new URL(`./${suffix}`, import.meta.url));\n};\n","import type { TrayOptions } from \"@opentray/spec\";\n\nimport { createClient, type EventfulTrayHandle } from \"./client\";\nimport { connectLocalBroker } from \"./local-broker\";\n\nexport interface OpenTrayRuntimeOptions {\n endpoint?: string;\n homeDir?: string;\n packageVersion?: string;\n protocolVersion?: number;\n clientVersion?: string;\n appId?: string;\n appName?: string;\n autoStart?: boolean;\n}\n\nexport const createTray = async (\n options: TrayOptions,\n runtimeOptions: OpenTrayRuntimeOptions = {}\n): Promise<EventfulTrayHandle> => {\n const connection = await connectLocalBroker(runtimeOptions);\n return createClient(connection).createTray(options);\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opentray",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "Developer-facing OpenTray SDK and CLI package.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,10 +13,6 @@
|
|
|
13
13
|
"types": "./dist/index.d.mts",
|
|
14
14
|
"import": "./dist/index.mjs"
|
|
15
15
|
},
|
|
16
|
-
"./node": {
|
|
17
|
-
"types": "./dist/node.d.mts",
|
|
18
|
-
"import": "./dist/node.mjs"
|
|
19
|
-
},
|
|
20
16
|
"./package.json": "./package.json"
|
|
21
17
|
},
|
|
22
18
|
"bin": {
|
|
@@ -28,7 +24,7 @@
|
|
|
28
24
|
],
|
|
29
25
|
"sideEffects": false,
|
|
30
26
|
"dependencies": {
|
|
31
|
-
"@opentray/spec": "0.10.
|
|
27
|
+
"@opentray/spec": "0.10.3"
|
|
32
28
|
},
|
|
33
29
|
"devDependencies": {
|
|
34
30
|
"tsdown": "^0.22.1",
|
|
@@ -36,22 +32,21 @@
|
|
|
36
32
|
"vitest": "^4.1.7"
|
|
37
33
|
},
|
|
38
34
|
"optionalDependencies": {
|
|
39
|
-
"@opentray/darwin-arm64": "0.10.
|
|
40
|
-
"@opentray/darwin-x64": "0.10.
|
|
41
|
-
"@opentray/linux-arm64": "0.10.
|
|
42
|
-
"@opentray/linux-x64": "0.10.
|
|
43
|
-
"@opentray/windows-arm64": "0.10.
|
|
44
|
-
"@opentray/windows-x64": "0.10.
|
|
35
|
+
"@opentray/darwin-arm64": "0.10.3",
|
|
36
|
+
"@opentray/darwin-x64": "0.10.3",
|
|
37
|
+
"@opentray/linux-arm64": "0.10.3",
|
|
38
|
+
"@opentray/linux-x64": "0.10.3",
|
|
39
|
+
"@opentray/windows-arm64": "0.10.3",
|
|
40
|
+
"@opentray/windows-x64": "0.10.3"
|
|
45
41
|
},
|
|
46
42
|
"scripts": {
|
|
47
|
-
"build": "tsdown src/index.ts src/
|
|
43
|
+
"build": "tsdown src/index.ts src/cli.ts --format esm --dts",
|
|
48
44
|
"cli": "bun run src/cli.ts",
|
|
49
45
|
"example:matrix": "bun run examples/example-matrix.ts",
|
|
50
46
|
"example:basic": "bun run examples/basic-tray.ts",
|
|
51
47
|
"example:first-app": "bun run examples/first-app.ts",
|
|
52
48
|
"example:debug-runtime-lynx": "bun run examples/debug-runtime-lynx.ts",
|
|
53
49
|
"example:debug-runtime-tray": "bun run examples/debug-runtime-tray.ts",
|
|
54
|
-
"example:visible-binding": "node examples/visible-binding-host.mjs",
|
|
55
50
|
"example:mediaQuery": "bun run examples/media-query-panel.ts",
|
|
56
51
|
"example:badge": "bun run examples/badge-panel.ts",
|
|
57
52
|
"example:placement": "bun run examples/placement-panel.ts",
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { ClientFrame, ClientRequestFrame, ExtensionEnvelope, Icon, Menu, RequestId, ServerFrame, Tooltip, TrayBoundsResult, TrayEvent, TrayId, TrayOptions } from "@opentray/spec";
|
|
2
|
-
|
|
3
|
-
//#region src/client.d.ts
|
|
4
|
-
interface OpenTrayTransport {
|
|
5
|
-
request(frame: ClientRequestFrame): Promise<ServerFrame>;
|
|
6
|
-
}
|
|
7
|
-
type OpenTrayEventFrame = Extract<ServerFrame, {
|
|
8
|
-
type: "event" | "ext-event";
|
|
9
|
-
}>;
|
|
10
|
-
interface OpenTrayEventSource {
|
|
11
|
-
onEvent(listener: (frame: OpenTrayEventFrame) => void): () => void;
|
|
12
|
-
}
|
|
13
|
-
interface OpenTrayConnection extends OpenTrayTransport, OpenTrayEventSource {}
|
|
14
|
-
interface OpenTrayClient {
|
|
15
|
-
createTray(options: TrayOptions): Promise<TrayHandle>;
|
|
16
|
-
}
|
|
17
|
-
interface OpenTrayEventfulClient extends OpenTrayClient {
|
|
18
|
-
createTray(options: TrayOptions): Promise<EventfulTrayHandle>;
|
|
19
|
-
}
|
|
20
|
-
interface TrayHandle {
|
|
21
|
-
trayId: TrayId;
|
|
22
|
-
getBounds(): Promise<TrayBoundsResult>;
|
|
23
|
-
setMenu(menu: Menu): Promise<void>;
|
|
24
|
-
setTooltip(tooltip: Tooltip): Promise<void>;
|
|
25
|
-
setIcon(icon: Icon): Promise<void>;
|
|
26
|
-
loadExtension(options: ExtensionLoadOptions): Promise<void>;
|
|
27
|
-
commandExtension(ext: string, data: unknown): Promise<void>;
|
|
28
|
-
requestExtension(ext: string, data: unknown): Promise<ExtensionEnvelope[]>;
|
|
29
|
-
extend<TCapability extends object, TOptions = undefined>(extension: TrayExtension<TCapability, TOptions>, options?: TOptions): TrayHandle & TCapability;
|
|
30
|
-
destroy(): Promise<void>;
|
|
31
|
-
}
|
|
32
|
-
type TrayScopedEvent = Exclude<TrayEvent, {
|
|
33
|
-
type: "ready";
|
|
34
|
-
}>;
|
|
35
|
-
type TrayEventType = TrayScopedEvent["type"];
|
|
36
|
-
type TrayEventByType<TEvent extends TrayEventType> = Extract<TrayScopedEvent, {
|
|
37
|
-
type: TEvent;
|
|
38
|
-
}>;
|
|
39
|
-
interface EventfulTrayHandle extends TrayHandle {
|
|
40
|
-
extend<TCapability extends object, TOptions = undefined>(extension: TrayExtension<TCapability, TOptions>, options?: TOptions): EventfulTrayHandle & TCapability;
|
|
41
|
-
listenExtension<TData = unknown>(ext: string, handler: (event: ExtensionEnvelope<TData>) => void): () => void;
|
|
42
|
-
listen<TEvent extends TrayEventType>(event: TEvent, handler: (event: TrayEventByType<TEvent>) => void): () => void;
|
|
43
|
-
onMenuClick(handler: (event: TrayEventByType<"menuClick">) => void): () => void;
|
|
44
|
-
onTrayClick(handler: (event: TrayEventByType<"trayClick">) => void): () => void;
|
|
45
|
-
onTrayDoubleClick(handler: (event: TrayEventByType<"trayDoubleClick">) => void): () => void;
|
|
46
|
-
}
|
|
47
|
-
interface ExtensionLoadOptions {
|
|
48
|
-
name: string;
|
|
49
|
-
path: string;
|
|
50
|
-
mountId?: string;
|
|
51
|
-
}
|
|
52
|
-
interface TrayExtensionMountSpec {
|
|
53
|
-
name?: string;
|
|
54
|
-
path?: string;
|
|
55
|
-
mountId?: string;
|
|
56
|
-
}
|
|
57
|
-
interface TrayExtensionContext {
|
|
58
|
-
readonly name: string;
|
|
59
|
-
readonly path: string;
|
|
60
|
-
readonly mountId: string;
|
|
61
|
-
ensureLoaded(): Promise<void>;
|
|
62
|
-
command(data: unknown): Promise<void>;
|
|
63
|
-
request(data: unknown): Promise<ExtensionEnvelope[]>;
|
|
64
|
-
}
|
|
65
|
-
interface TrayExtension<TCapability extends object, TOptions = undefined> {
|
|
66
|
-
readonly name: string;
|
|
67
|
-
readonly path: string;
|
|
68
|
-
resolveMount?(options: TOptions | undefined): TrayExtensionMountSpec;
|
|
69
|
-
extend(tray: TrayHandle, context: TrayExtensionContext, options: TOptions | undefined): TCapability;
|
|
70
|
-
}
|
|
71
|
-
declare const createInitFrame: (clientVersion: string) => ClientFrame;
|
|
72
|
-
interface CreateClientOptions {
|
|
73
|
-
requestIdPrefix?: string;
|
|
74
|
-
}
|
|
75
|
-
declare function createClient(transport: OpenTrayConnection, options?: CreateClientOptions): OpenTrayEventfulClient;
|
|
76
|
-
declare function createClient(transport: OpenTrayTransport, options?: CreateClientOptions): OpenTrayClient;
|
|
77
|
-
declare function createTrayHandle(transport: OpenTrayConnection, appId: string, trayId: TrayId, nextRequestId?: () => RequestId): EventfulTrayHandle;
|
|
78
|
-
declare function createTrayHandle(transport: OpenTrayTransport, appId: string, trayId: TrayId, nextRequestId?: () => RequestId): TrayHandle;
|
|
79
|
-
//#endregion
|
|
80
|
-
//#region src/native-runtime.d.ts
|
|
81
|
-
interface OpenTrayRuntimeBindingInfo {
|
|
82
|
-
readonly kind: string;
|
|
83
|
-
readonly protocolVersion: number;
|
|
84
|
-
}
|
|
85
|
-
interface OpenTrayRuntimeBinding {
|
|
86
|
-
runtimeBindingInfo(): OpenTrayRuntimeBindingInfo;
|
|
87
|
-
runVisibleRuntimeHost?(options?: OpenTrayVisibleRuntimeHostOptions): void;
|
|
88
|
-
createVisibleRuntime?(): OpenTrayVisibleRuntime;
|
|
89
|
-
createHeadlessRuntime?(packageVersion?: string, appId?: string, appName?: string): OpenTrayHeadlessRuntime;
|
|
90
|
-
}
|
|
91
|
-
interface OpenTrayVisibleRuntimeHostOptions {
|
|
92
|
-
readonly packageVersion?: string;
|
|
93
|
-
readonly appId?: string;
|
|
94
|
-
readonly appName?: string;
|
|
95
|
-
readonly autoExitAfterMs?: number;
|
|
96
|
-
}
|
|
97
|
-
interface OpenTrayRuntimeHost {
|
|
98
|
-
request(frameJson: string): string[] | Promise<string[]>;
|
|
99
|
-
close(): string[] | Promise<string[]>;
|
|
100
|
-
}
|
|
101
|
-
interface OpenTrayVisibleRuntime extends OpenTrayRuntimeHost {
|
|
102
|
-
pollEvents(): string[] | Promise<string[]>;
|
|
103
|
-
}
|
|
104
|
-
type OpenTrayHeadlessRuntime = OpenTrayRuntimeHost;
|
|
105
|
-
interface RuntimeNativeTarget {
|
|
106
|
-
readonly packageName: string;
|
|
107
|
-
readonly bindingRelativePath: string;
|
|
108
|
-
}
|
|
109
|
-
interface ResolveRuntimeBindingOptions {
|
|
110
|
-
readonly platform?: string;
|
|
111
|
-
readonly arch?: string;
|
|
112
|
-
readonly resolvePackageJson?: (packageName: string) => string | undefined;
|
|
113
|
-
readonly nativeLoader?: (bindingPath: string) => unknown;
|
|
114
|
-
}
|
|
115
|
-
declare const resolveRuntimeNativeTarget: (platform?: string, arch?: string) => RuntimeNativeTarget;
|
|
116
|
-
declare const resolveInstalledRuntimeBindingPath: ({
|
|
117
|
-
platform,
|
|
118
|
-
arch,
|
|
119
|
-
resolvePackageJson
|
|
120
|
-
}?: ResolveRuntimeBindingOptions) => Promise<string>;
|
|
121
|
-
declare const loadOpenTrayRuntimeBinding: (options?: ResolveRuntimeBindingOptions) => Promise<OpenTrayRuntimeBinding>;
|
|
122
|
-
declare class MissingPlatformRuntimeBindingError extends Error {
|
|
123
|
-
readonly code = "OPENTRAY_MISSING_PLATFORM_RUNTIME_BINDING";
|
|
124
|
-
readonly platform: string;
|
|
125
|
-
readonly arch: string;
|
|
126
|
-
readonly packageName: string | undefined;
|
|
127
|
-
readonly bindingPath: string | undefined;
|
|
128
|
-
constructor(message: string, platform: string, arch: string, options?: {
|
|
129
|
-
readonly packageName?: string;
|
|
130
|
-
readonly bindingPath?: string;
|
|
131
|
-
readonly cause?: unknown;
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
//#endregion
|
|
135
|
-
export { TrayHandle as C, createTrayHandle as E, TrayExtensionMountSpec as S, createInitFrame as T, OpenTrayTransport as _, ResolveRuntimeBindingOptions as a, TrayExtension as b, resolveInstalledRuntimeBindingPath as c, EventfulTrayHandle as d, ExtensionLoadOptions as f, OpenTrayEventSource as g, OpenTrayEventFrame as h, OpenTrayVisibleRuntimeHostOptions as i, resolveRuntimeNativeTarget as l, OpenTrayConnection as m, OpenTrayRuntimeBinding as n, RuntimeNativeTarget as o, OpenTrayClient as p, OpenTrayRuntimeBindingInfo as r, loadOpenTrayRuntimeBinding as s, MissingPlatformRuntimeBindingError as t, CreateClientOptions as u, TrayEventByType as v, createClient as w, TrayExtensionContext as x, TrayEventType as y };
|
|
136
|
-
//# sourceMappingURL=native-runtime-Cn8BaffH.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"native-runtime-Cn8BaffH.d.mts","names":[],"sources":["../src/client.ts","../src/native-runtime.ts"],"mappings":"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"}
|
package/dist/node.d.mts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { a as ResolveRuntimeBindingOptions, c as resolveInstalledRuntimeBindingPath, i as OpenTrayVisibleRuntimeHostOptions, l as resolveRuntimeNativeTarget, m as OpenTrayConnection, n as OpenTrayRuntimeBinding, o as RuntimeNativeTarget, r as OpenTrayRuntimeBindingInfo, s as loadOpenTrayRuntimeBinding, t as MissingPlatformRuntimeBindingError } from "./native-runtime-Cn8BaffH.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/runtime-binding-transport.d.ts
|
|
4
|
-
type RuntimeBindingTransportKind = "visible" | "headless";
|
|
5
|
-
interface CreateRuntimeBindingTransportOptions {
|
|
6
|
-
readonly kind?: RuntimeBindingTransportKind;
|
|
7
|
-
readonly binding?: OpenTrayRuntimeBinding;
|
|
8
|
-
readonly packageVersion?: string;
|
|
9
|
-
readonly clientVersion?: string;
|
|
10
|
-
readonly protocolVersion?: number;
|
|
11
|
-
readonly appId?: string;
|
|
12
|
-
readonly appName?: string;
|
|
13
|
-
}
|
|
14
|
-
declare const createRuntimeBindingTransport: ({
|
|
15
|
-
binding,
|
|
16
|
-
packageVersion,
|
|
17
|
-
clientVersion,
|
|
18
|
-
protocolVersion,
|
|
19
|
-
appId,
|
|
20
|
-
appName,
|
|
21
|
-
kind
|
|
22
|
-
}?: CreateRuntimeBindingTransportOptions) => Promise<OpenTrayConnection>;
|
|
23
|
-
//#endregion
|
|
24
|
-
//#region src/node-host.d.ts
|
|
25
|
-
/** Options for resolving the native binding and running the visible host loop. */
|
|
26
|
-
interface RunVisibleRuntimeHostOptions extends ResolveRuntimeBindingOptions, OpenTrayVisibleRuntimeHostOptions {}
|
|
27
|
-
/** Run the visible native host loop on the caller-owned Node main thread. */
|
|
28
|
-
declare const runVisibleRuntimeHost: ({
|
|
29
|
-
platform,
|
|
30
|
-
arch,
|
|
31
|
-
resolvePackageJson,
|
|
32
|
-
nativeLoader,
|
|
33
|
-
...hostOptions
|
|
34
|
-
}?: RunVisibleRuntimeHostOptions) => Promise<void>;
|
|
35
|
-
//#endregion
|
|
36
|
-
export { type CreateRuntimeBindingTransportOptions, MissingPlatformRuntimeBindingError, type OpenTrayRuntimeBinding, type OpenTrayRuntimeBindingInfo, type OpenTrayVisibleRuntimeHostOptions, type ResolveRuntimeBindingOptions, type RunVisibleRuntimeHostOptions, type RuntimeNativeTarget, createRuntimeBindingTransport, loadOpenTrayRuntimeBinding, resolveInstalledRuntimeBindingPath, resolveRuntimeNativeTarget, runVisibleRuntimeHost };
|
|
37
|
-
//# sourceMappingURL=node.d.mts.map
|
package/dist/node.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.mts","names":[],"sources":["../src/runtime-binding-transport.ts","../src/node-host.ts"],"mappings":";;;KAgBY,2BAAA;AAAA,UAEK,oCAAA;EAAA,SACN,IAAA,GAAO,2BAAA;EAAA,SACP,OAAA,GAAU,sBAAsB;EAAA,SAChC,cAAA;EAAA,SACA,aAAA;EAAA,SACA,eAAA;EAAA,SACA,KAAA;EAAA,SACA,OAAA;AAAA;AAAA,cAGE,6BAAA;EAAuC,OAAA;EAAA,cAAA;EAAA,aAAA;EAAA,eAAA;EAAA,KAAA;EAAA,OAAA;EAAA;AAAA,IAQjD,oCAAA,KAA4C,OAAA,CAAQ,kBAAA;;;;UC7BtC,4BAAA,SACP,4BAAA,EACN,iCAAiC;;cAGxB,qBAAA;EAA+B,QAAA;EAAA,IAAA;EAAA,kBAAA;EAAA,YAAA;EAAA,GAAA;AAAA,IAMzC,4BAAA,KAAoC,OAAA"}
|
package/dist/node.mjs
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { a as resolveRuntimeNativeTarget, i as resolveInstalledRuntimeBindingPath, n as MissingPlatformRuntimeBindingError, r as loadOpenTrayRuntimeBinding, t as createRuntimeBindingTransport } from "./runtime-binding-transport-UPunusBT.mjs";
|
|
2
|
-
//#region src/node-host.ts
|
|
3
|
-
/** Run the visible native host loop on the caller-owned Node main thread. */
|
|
4
|
-
const runVisibleRuntimeHost = async ({ platform, arch, resolvePackageJson, nativeLoader, ...hostOptions } = {}) => {
|
|
5
|
-
const binding = await loadOpenTrayRuntimeBinding({
|
|
6
|
-
...platform === void 0 ? {} : { platform },
|
|
7
|
-
...arch === void 0 ? {} : { arch },
|
|
8
|
-
...resolvePackageJson === void 0 ? {} : { resolvePackageJson },
|
|
9
|
-
...nativeLoader === void 0 ? {} : { nativeLoader }
|
|
10
|
-
});
|
|
11
|
-
if (binding.runVisibleRuntimeHost === void 0) throw new Error("OpenTray runtime binding does not expose runVisibleRuntimeHost()");
|
|
12
|
-
binding.runVisibleRuntimeHost(hostOptions);
|
|
13
|
-
};
|
|
14
|
-
//#endregion
|
|
15
|
-
export { MissingPlatformRuntimeBindingError, createRuntimeBindingTransport, loadOpenTrayRuntimeBinding, resolveInstalledRuntimeBindingPath, resolveRuntimeNativeTarget, runVisibleRuntimeHost };
|
|
16
|
-
|
|
17
|
-
//# sourceMappingURL=node.mjs.map
|
package/dist/node.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"node.mjs","names":[],"sources":["../src/node-host.ts"],"sourcesContent":["import {\n loadOpenTrayRuntimeBinding,\n type OpenTrayVisibleRuntimeHostOptions,\n type ResolveRuntimeBindingOptions,\n} from \"./native-runtime\";\n\n/** Options for resolving the native binding and running the visible host loop. */\nexport interface RunVisibleRuntimeHostOptions\n extends ResolveRuntimeBindingOptions,\n OpenTrayVisibleRuntimeHostOptions {}\n\n/** Run the visible native host loop on the caller-owned Node main thread. */\nexport const runVisibleRuntimeHost = async ({\n platform,\n arch,\n resolvePackageJson,\n nativeLoader,\n ...hostOptions\n}: RunVisibleRuntimeHostOptions = {}): Promise<void> => {\n const binding = await loadOpenTrayRuntimeBinding({\n ...(platform === undefined ? {} : { platform }),\n ...(arch === undefined ? {} : { arch }),\n ...(resolvePackageJson === undefined ? {} : { resolvePackageJson }),\n ...(nativeLoader === undefined ? {} : { nativeLoader }),\n });\n if (binding.runVisibleRuntimeHost === undefined) {\n throw new Error(\n \"OpenTray runtime binding does not expose runVisibleRuntimeHost()\"\n );\n }\n binding.runVisibleRuntimeHost(hostOptions);\n};\n\nexport {\n MissingPlatformRuntimeBindingError,\n resolveInstalledRuntimeBindingPath,\n resolveRuntimeNativeTarget,\n loadOpenTrayRuntimeBinding,\n type OpenTrayRuntimeBinding,\n type OpenTrayRuntimeBindingInfo,\n type OpenTrayVisibleRuntimeHostOptions,\n type ResolveRuntimeBindingOptions,\n type RuntimeNativeTarget,\n} from \"./native-runtime\";\nexport {\n createRuntimeBindingTransport,\n type CreateRuntimeBindingTransportOptions,\n} from \"./runtime-binding-transport\";\n"],"mappings":";;;AAYA,MAAa,wBAAwB,OAAO,EAC1C,UACA,MACA,oBACA,cACA,GAAG,gBAC6B,CAAC,MAAqB;CACtD,MAAM,UAAU,MAAM,2BAA2B;EAC/C,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;EAC7C,GAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK;EACrC,GAAI,uBAAuB,KAAA,IAAY,CAAC,IAAI,EAAE,mBAAmB;EACjE,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;CACvD,CAAC;CACD,IAAI,QAAQ,0BAA0B,KAAA,GACpC,MAAM,IAAI,MACR,kEACF;CAEF,QAAQ,sBAAsB,WAAW;AAC3C"}
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import { PROTOCOL_VERSION, parseServerFrame } from "@opentray/spec";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { constants } from "node:fs";
|
|
5
|
-
import { access } from "node:fs/promises";
|
|
6
|
-
//#region src/native-runtime.ts
|
|
7
|
-
const requireFromSource = createRequire(import.meta.url);
|
|
8
|
-
const runtimeBindingRelativePath = "runtime/opentray_runtime.node";
|
|
9
|
-
const resolveRuntimeNativeTarget = (platform = process.platform, arch = process.arch) => {
|
|
10
|
-
return {
|
|
11
|
-
packageName: `@opentray/${platformToPackageOs(platform)}-${normalizeArch(arch)}`,
|
|
12
|
-
bindingRelativePath: runtimeBindingRelativePath
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
const resolveInstalledRuntimeBindingPath = async ({ platform = process.platform, arch = process.arch, resolvePackageJson = resolvePackageJsonFromSource } = {}) => {
|
|
16
|
-
const target = resolveRuntimeNativeTarget(platform, arch);
|
|
17
|
-
const packageJsonPath = resolvePackageJson(target.packageName);
|
|
18
|
-
if (packageJsonPath === void 0) throw new MissingPlatformRuntimeBindingError(`unable to resolve OpenTray runtime binding for ${platform}/${arch}; install ${target.packageName}`, platform, arch, { packageName: target.packageName });
|
|
19
|
-
const bindingPath = join(dirname(packageJsonPath), target.bindingRelativePath);
|
|
20
|
-
if (!await exists(bindingPath)) throw new MissingPlatformRuntimeBindingError(`OpenTray runtime package ${target.packageName} is installed but missing ${target.bindingRelativePath}`, platform, arch, {
|
|
21
|
-
packageName: target.packageName,
|
|
22
|
-
bindingPath
|
|
23
|
-
});
|
|
24
|
-
return bindingPath;
|
|
25
|
-
};
|
|
26
|
-
const loadOpenTrayRuntimeBinding = async (options = {}) => {
|
|
27
|
-
const bindingPath = await resolveInstalledRuntimeBindingPath(options);
|
|
28
|
-
const loaded = (options.nativeLoader ?? loadNativeModule)(bindingPath);
|
|
29
|
-
if (!isRuntimeBinding(loaded)) throw new MissingPlatformRuntimeBindingError(`OpenTray runtime binding ${bindingPath} does not expose runtimeBindingInfo()`, options.platform ?? process.platform, options.arch ?? process.arch, { bindingPath });
|
|
30
|
-
return loaded;
|
|
31
|
-
};
|
|
32
|
-
const loadNativeModule = (bindingPath) => requireFromSource(bindingPath);
|
|
33
|
-
const isRuntimeBinding = (value) => {
|
|
34
|
-
if (typeof value !== "object" || value === null || !("runtimeBindingInfo" in value)) return false;
|
|
35
|
-
return typeof value.runtimeBindingInfo === "function";
|
|
36
|
-
};
|
|
37
|
-
const platformToPackageOs = (platform) => {
|
|
38
|
-
switch (platform) {
|
|
39
|
-
case "darwin":
|
|
40
|
-
case "linux": return platform;
|
|
41
|
-
case "win32": return "windows";
|
|
42
|
-
default: throw new MissingPlatformRuntimeBindingError(`unsupported OpenTray runtime platform: ${platform}`, platform, process.arch);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
const normalizeArch = (arch) => {
|
|
46
|
-
switch (arch) {
|
|
47
|
-
case "arm64":
|
|
48
|
-
case "x64": return arch;
|
|
49
|
-
default: throw new MissingPlatformRuntimeBindingError(`unsupported OpenTray runtime architecture: ${arch}`, process.platform, arch);
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
const resolvePackageJsonFromSource = (packageName) => {
|
|
53
|
-
try {
|
|
54
|
-
return requireFromSource.resolve(`${packageName}/package.json`);
|
|
55
|
-
} catch (error) {
|
|
56
|
-
if (isNodeError(error) && error.code === "MODULE_NOT_FOUND") return;
|
|
57
|
-
throw error;
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
const exists = async (path) => {
|
|
61
|
-
try {
|
|
62
|
-
await access(path, constants.F_OK);
|
|
63
|
-
return true;
|
|
64
|
-
} catch {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
const isNodeError = (error) => error instanceof Error && "code" in error;
|
|
69
|
-
var MissingPlatformRuntimeBindingError = class extends Error {
|
|
70
|
-
code = "OPENTRAY_MISSING_PLATFORM_RUNTIME_BINDING";
|
|
71
|
-
platform;
|
|
72
|
-
arch;
|
|
73
|
-
packageName;
|
|
74
|
-
bindingPath;
|
|
75
|
-
constructor(message, platform, arch, options = {}) {
|
|
76
|
-
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
77
|
-
this.name = "MissingPlatformRuntimeBindingError";
|
|
78
|
-
this.platform = platform;
|
|
79
|
-
this.arch = arch;
|
|
80
|
-
this.packageName = options.packageName;
|
|
81
|
-
this.bindingPath = options.bindingPath;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
//#endregion
|
|
85
|
-
//#region src/runtime-binding-transport.ts
|
|
86
|
-
const createRuntimeBindingTransport = async ({ binding, packageVersion = "0.0.0", clientVersion = packageVersion, protocolVersion = PROTOCOL_VERSION, appId, appName, kind = "visible" } = {}) => {
|
|
87
|
-
const resolvedBinding = binding ?? await loadOpenTrayRuntimeBinding();
|
|
88
|
-
const transport = new RuntimeBindingTransport(kind === "headless" ? createHeadlessRuntime(resolvedBinding, packageVersion, appId, appName) : createVisibleRuntime(resolvedBinding));
|
|
89
|
-
await transport.init(clientVersion, protocolVersion);
|
|
90
|
-
return transport;
|
|
91
|
-
};
|
|
92
|
-
const createVisibleRuntime = (binding) => {
|
|
93
|
-
if (binding.createVisibleRuntime === void 0) throw new Error("OpenTray runtime binding does not expose createVisibleRuntime()");
|
|
94
|
-
return binding.createVisibleRuntime();
|
|
95
|
-
};
|
|
96
|
-
const createHeadlessRuntime = (binding, packageVersion, appId, appName) => {
|
|
97
|
-
if (binding.createHeadlessRuntime === void 0) throw new Error("OpenTray runtime binding does not expose createHeadlessRuntime()");
|
|
98
|
-
return binding.createHeadlessRuntime(packageVersion, appId, appName);
|
|
99
|
-
};
|
|
100
|
-
var RuntimeBindingTransport = class {
|
|
101
|
-
listeners = /* @__PURE__ */ new Set();
|
|
102
|
-
runtime;
|
|
103
|
-
pollInterval;
|
|
104
|
-
constructor(runtime) {
|
|
105
|
-
this.runtime = runtime;
|
|
106
|
-
this.pollInterval = runtime.pollEvents === void 0 ? void 0 : setInterval(() => {
|
|
107
|
-
this.pollRuntimeEvents().catch(() => {});
|
|
108
|
-
}, 50);
|
|
109
|
-
this.pollInterval?.unref();
|
|
110
|
-
}
|
|
111
|
-
async init(clientVersion, protocolVersion) {
|
|
112
|
-
if ((await this.invoke({
|
|
113
|
-
type: "init",
|
|
114
|
-
protocolVersion,
|
|
115
|
-
clientVersion
|
|
116
|
-
})).find((frame) => frame.type === "ready") === void 0) throw new Error("OpenTray runtime binding did not return a ready frame");
|
|
117
|
-
}
|
|
118
|
-
async request(frame) {
|
|
119
|
-
const response = (await this.invoke(frame)).find((candidate) => responseRequestId(candidate) === frame.requestId);
|
|
120
|
-
if (response === void 0) throw new Error(`OpenTray runtime binding did not return response for requestId: ${frame.requestId}`);
|
|
121
|
-
return response;
|
|
122
|
-
}
|
|
123
|
-
onEvent(listener) {
|
|
124
|
-
this.listeners.add(listener);
|
|
125
|
-
return () => {
|
|
126
|
-
this.listeners.delete(listener);
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
async close() {
|
|
130
|
-
if (this.pollInterval !== void 0) clearInterval(this.pollInterval);
|
|
131
|
-
const frames = await this.parseFrames(await this.runtime.close());
|
|
132
|
-
this.dispatchEvents(frames);
|
|
133
|
-
}
|
|
134
|
-
async invoke(frame) {
|
|
135
|
-
const frames = await this.parseFrames(await this.runtime.request(JSON.stringify(frame)));
|
|
136
|
-
this.dispatchEvents(frames);
|
|
137
|
-
return frames;
|
|
138
|
-
}
|
|
139
|
-
async parseFrames(frameJson) {
|
|
140
|
-
return (await frameJson).map((raw) => {
|
|
141
|
-
const parsed = parseServerFrame(raw);
|
|
142
|
-
if (!parsed.ok || parsed.frame === void 0) throw new Error(`${parsed.error ?? "invalid server frame"}: ${raw}`);
|
|
143
|
-
return parsed.frame;
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
dispatchEvents(frames) {
|
|
147
|
-
for (const frame of frames) {
|
|
148
|
-
if (frame.type !== "event" && frame.type !== "ext-event") continue;
|
|
149
|
-
for (const listener of this.listeners) listener(frame);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
async pollRuntimeEvents() {
|
|
153
|
-
const pollEvents = this.runtime.pollEvents;
|
|
154
|
-
if (pollEvents === void 0) return;
|
|
155
|
-
const frames = await this.parseFrames(await pollEvents.call(this.runtime));
|
|
156
|
-
this.dispatchEvents(frames);
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
const responseRequestId = (frame) => {
|
|
160
|
-
switch (frame.type) {
|
|
161
|
-
case "app-created":
|
|
162
|
-
case "default-app":
|
|
163
|
-
case "tray-created":
|
|
164
|
-
case "tray-bounds":
|
|
165
|
-
case "ack":
|
|
166
|
-
case "ext-command-result":
|
|
167
|
-
case "runtime-host-health": return frame.requestId;
|
|
168
|
-
case "ready":
|
|
169
|
-
case "event":
|
|
170
|
-
case "ext-event":
|
|
171
|
-
case "error": return;
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
//#endregion
|
|
175
|
-
export { resolveRuntimeNativeTarget as a, resolveInstalledRuntimeBindingPath as i, MissingPlatformRuntimeBindingError as n, loadOpenTrayRuntimeBinding as r, createRuntimeBindingTransport as t };
|
|
176
|
-
|
|
177
|
-
//# sourceMappingURL=runtime-binding-transport-UPunusBT.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-binding-transport-UPunusBT.mjs","names":["binding"],"sources":["../src/native-runtime.ts","../src/runtime-binding-transport.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\n\nconst requireFromSource = createRequire(import.meta.url);\n\nexport interface OpenTrayRuntimeBindingInfo {\n readonly kind: string;\n readonly protocolVersion: number;\n}\n\nexport interface OpenTrayRuntimeBinding {\n runtimeBindingInfo(): OpenTrayRuntimeBindingInfo;\n runVisibleRuntimeHost?(options?: OpenTrayVisibleRuntimeHostOptions): void;\n createVisibleRuntime?(): OpenTrayVisibleRuntime;\n createHeadlessRuntime?(\n packageVersion?: string,\n appId?: string,\n appName?: string\n ): OpenTrayHeadlessRuntime;\n}\n\nexport interface OpenTrayVisibleRuntimeHostOptions {\n readonly packageVersion?: string;\n readonly appId?: string;\n readonly appName?: string;\n readonly autoExitAfterMs?: number;\n}\n\nexport interface OpenTrayRuntimeHost {\n request(frameJson: string): string[] | Promise<string[]>;\n close(): string[] | Promise<string[]>;\n}\n\nexport interface OpenTrayVisibleRuntime extends OpenTrayRuntimeHost {\n pollEvents(): string[] | Promise<string[]>;\n}\n\nexport type OpenTrayHeadlessRuntime = OpenTrayRuntimeHost;\n\nexport interface RuntimeNativeTarget {\n readonly packageName: string;\n readonly bindingRelativePath: string;\n}\n\nexport interface ResolveRuntimeBindingOptions {\n readonly platform?: string;\n readonly arch?: string;\n readonly resolvePackageJson?: (packageName: string) => string | undefined;\n readonly nativeLoader?: (bindingPath: string) => unknown;\n}\n\nexport const runtimeBindingRelativePath = \"runtime/opentray_runtime.node\";\n\nexport const resolveRuntimeNativeTarget = (\n platform: string = process.platform,\n arch: string = process.arch\n): RuntimeNativeTarget => {\n const packageOs = platformToPackageOs(platform);\n const nativeArch = normalizeArch(arch);\n return {\n packageName: `@opentray/${packageOs}-${nativeArch}`,\n bindingRelativePath: runtimeBindingRelativePath,\n };\n};\n\nexport const resolveInstalledRuntimeBindingPath = async ({\n platform = process.platform,\n arch = process.arch,\n resolvePackageJson = resolvePackageJsonFromSource,\n}: ResolveRuntimeBindingOptions = {}): Promise<string> => {\n const target = resolveRuntimeNativeTarget(platform, arch);\n const packageJsonPath = resolvePackageJson(target.packageName);\n if (packageJsonPath === undefined) {\n throw new MissingPlatformRuntimeBindingError(\n `unable to resolve OpenTray runtime binding for ${platform}/${arch}; install ${target.packageName}`,\n platform,\n arch,\n { packageName: target.packageName }\n );\n }\n\n const bindingPath = join(\n dirname(packageJsonPath),\n target.bindingRelativePath\n );\n if (!(await exists(bindingPath))) {\n throw new MissingPlatformRuntimeBindingError(\n `OpenTray runtime package ${target.packageName} is installed but missing ${target.bindingRelativePath}`,\n platform,\n arch,\n {\n packageName: target.packageName,\n bindingPath,\n }\n );\n }\n return bindingPath;\n};\n\nexport const loadOpenTrayRuntimeBinding = async (\n options: ResolveRuntimeBindingOptions = {}\n): Promise<OpenTrayRuntimeBinding> => {\n const bindingPath = await resolveInstalledRuntimeBindingPath(options);\n const loaded = (options.nativeLoader ?? loadNativeModule)(bindingPath);\n if (!isRuntimeBinding(loaded)) {\n throw new MissingPlatformRuntimeBindingError(\n `OpenTray runtime binding ${bindingPath} does not expose runtimeBindingInfo()`,\n options.platform ?? process.platform,\n options.arch ?? process.arch,\n { bindingPath }\n );\n }\n return loaded;\n};\n\nconst loadNativeModule = (bindingPath: string): unknown =>\n requireFromSource(bindingPath);\n\nconst isRuntimeBinding = (value: unknown): value is OpenTrayRuntimeBinding => {\n if (\n typeof value !== \"object\" ||\n value === null ||\n !(\"runtimeBindingInfo\" in value)\n ) {\n return false;\n }\n const binding = value as { runtimeBindingInfo?: unknown };\n return typeof binding.runtimeBindingInfo === \"function\";\n};\n\nconst platformToPackageOs = (\n platform: string\n): \"darwin\" | \"linux\" | \"windows\" => {\n switch (platform) {\n case \"darwin\":\n case \"linux\":\n return platform;\n case \"win32\":\n return \"windows\";\n default:\n throw new MissingPlatformRuntimeBindingError(\n `unsupported OpenTray runtime platform: ${platform}`,\n platform,\n process.arch\n );\n }\n};\n\nconst normalizeArch = (arch: string): \"arm64\" | \"x64\" => {\n switch (arch) {\n case \"arm64\":\n case \"x64\":\n return arch;\n default:\n throw new MissingPlatformRuntimeBindingError(\n `unsupported OpenTray runtime architecture: ${arch}`,\n process.platform,\n arch\n );\n }\n};\n\nconst resolvePackageJsonFromSource = (\n packageName: string\n): string | undefined => {\n try {\n return requireFromSource.resolve(`${packageName}/package.json`);\n } catch (error) {\n if (isNodeError(error) && error.code === \"MODULE_NOT_FOUND\") {\n return undefined;\n }\n throw error;\n }\n};\n\nconst exists = async (path: string): Promise<boolean> => {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n};\n\nconst isNodeError = (error: unknown): error is NodeJS.ErrnoException =>\n error instanceof Error && \"code\" in error;\n\nexport class MissingPlatformRuntimeBindingError extends Error {\n readonly code = \"OPENTRAY_MISSING_PLATFORM_RUNTIME_BINDING\";\n readonly platform: string;\n readonly arch: string;\n readonly packageName: string | undefined;\n readonly bindingPath: string | undefined;\n\n constructor(\n message: string,\n platform: string,\n arch: string,\n options: {\n readonly packageName?: string;\n readonly bindingPath?: string;\n readonly cause?: unknown;\n } = {}\n ) {\n super(\n message,\n options.cause === undefined ? undefined : { cause: options.cause }\n );\n this.name = \"MissingPlatformRuntimeBindingError\";\n this.platform = platform;\n this.arch = arch;\n this.packageName = options.packageName;\n this.bindingPath = options.bindingPath;\n }\n}\n","import {\n PROTOCOL_VERSION,\n parseServerFrame,\n type ClientRequestFrame,\n type RequestId,\n type ServerFrame,\n} from \"@opentray/spec\";\n\nimport type { OpenTrayConnection, OpenTrayEventFrame } from \"./client\";\nimport {\n loadOpenTrayRuntimeBinding,\n type OpenTrayRuntimeHost,\n type OpenTrayRuntimeBinding,\n type OpenTrayVisibleRuntime,\n} from \"./native-runtime\";\n\nexport type RuntimeBindingTransportKind = \"visible\" | \"headless\";\n\nexport interface CreateRuntimeBindingTransportOptions {\n readonly kind?: RuntimeBindingTransportKind;\n readonly binding?: OpenTrayRuntimeBinding;\n readonly packageVersion?: string;\n readonly clientVersion?: string;\n readonly protocolVersion?: number;\n readonly appId?: string;\n readonly appName?: string;\n}\n\nexport const createRuntimeBindingTransport = async ({\n binding,\n packageVersion = \"0.0.0\",\n clientVersion = packageVersion,\n protocolVersion = PROTOCOL_VERSION,\n appId,\n appName,\n kind = \"visible\",\n}: CreateRuntimeBindingTransportOptions = {}): Promise<OpenTrayConnection> => {\n const resolvedBinding = binding ?? (await loadOpenTrayRuntimeBinding());\n const runtime =\n kind === \"headless\"\n ? createHeadlessRuntime(resolvedBinding, packageVersion, appId, appName)\n : createVisibleRuntime(resolvedBinding);\n const transport = new RuntimeBindingTransport(runtime);\n await transport.init(clientVersion, protocolVersion);\n return transport;\n};\n\nconst createVisibleRuntime = (\n binding: OpenTrayRuntimeBinding\n): OpenTrayVisibleRuntime => {\n if (binding.createVisibleRuntime === undefined) {\n throw new Error(\n \"OpenTray runtime binding does not expose createVisibleRuntime()\"\n );\n }\n return binding.createVisibleRuntime();\n};\n\nconst createHeadlessRuntime = (\n binding: OpenTrayRuntimeBinding,\n packageVersion: string,\n appId: string | undefined,\n appName: string | undefined\n): OpenTrayRuntimeHost => {\n if (binding.createHeadlessRuntime === undefined) {\n throw new Error(\n \"OpenTray runtime binding does not expose createHeadlessRuntime()\"\n );\n }\n return binding.createHeadlessRuntime(packageVersion, appId, appName);\n};\n\ntype RuntimeFrameInvoker = (frameJson: string) => string[] | Promise<string[]>;\n\ninterface RuntimeBindingHost extends OpenTrayRuntimeHost {\n request: RuntimeFrameInvoker;\n pollEvents?(): string[] | Promise<string[]>;\n}\n\nclass RuntimeBindingTransport implements OpenTrayConnection {\n private readonly listeners = new Set<(frame: OpenTrayEventFrame) => void>();\n private readonly runtime: RuntimeBindingHost;\n private readonly pollInterval: NodeJS.Timeout | undefined;\n\n constructor(runtime: RuntimeBindingHost) {\n this.runtime = runtime;\n this.pollInterval =\n runtime.pollEvents === undefined\n ? undefined\n : setInterval(() => {\n void this.pollRuntimeEvents().catch(() => {\n // Polling is an event ingress path. Request and close failures still surface to callers.\n });\n }, 50);\n this.pollInterval?.unref();\n }\n\n async init(clientVersion: string, protocolVersion: number): Promise<void> {\n const frames = await this.invoke({\n type: \"init\",\n protocolVersion,\n clientVersion,\n });\n const ready = frames.find(\n (frame): frame is Extract<ServerFrame, { type: \"ready\" }> =>\n frame.type === \"ready\"\n );\n if (ready === undefined) {\n throw new Error(\"OpenTray runtime binding did not return a ready frame\");\n }\n }\n\n async request(frame: ClientRequestFrame): Promise<ServerFrame> {\n const frames = await this.invoke(frame);\n const response = frames.find(\n (candidate) => responseRequestId(candidate) === frame.requestId\n );\n if (response === undefined) {\n throw new Error(\n `OpenTray runtime binding did not return response for requestId: ${frame.requestId}`\n );\n }\n return response;\n }\n\n onEvent(listener: (frame: OpenTrayEventFrame) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n async close(): Promise<void> {\n if (this.pollInterval !== undefined) {\n clearInterval(this.pollInterval);\n }\n const frames = await this.parseFrames(await this.runtime.close());\n this.dispatchEvents(frames);\n }\n\n private async invoke(frame: object): Promise<ServerFrame[]> {\n const frames = await this.parseFrames(\n await this.runtime.request(JSON.stringify(frame))\n );\n this.dispatchEvents(frames);\n return frames;\n }\n\n private async parseFrames(\n frameJson: string[] | Promise<string[]>\n ): Promise<ServerFrame[]> {\n const rawFrames = await frameJson;\n return rawFrames.map((raw) => {\n const parsed = parseServerFrame(raw);\n if (!parsed.ok || parsed.frame === undefined) {\n throw new Error(`${parsed.error ?? \"invalid server frame\"}: ${raw}`);\n }\n return parsed.frame;\n });\n }\n\n private dispatchEvents(frames: readonly ServerFrame[]): void {\n for (const frame of frames) {\n if (frame.type !== \"event\" && frame.type !== \"ext-event\") {\n continue;\n }\n for (const listener of this.listeners) {\n listener(frame);\n }\n }\n }\n\n private async pollRuntimeEvents(): Promise<void> {\n const pollEvents = this.runtime.pollEvents;\n if (pollEvents === undefined) {\n return;\n }\n const frames = await this.parseFrames(await pollEvents.call(this.runtime));\n this.dispatchEvents(frames);\n }\n}\n\nconst responseRequestId = (frame: ServerFrame): RequestId | undefined => {\n switch (frame.type) {\n case \"app-created\":\n case \"default-app\":\n case \"tray-created\":\n case \"tray-bounds\":\n case \"ack\":\n case \"ext-command-result\":\n case \"runtime-host-health\":\n return frame.requestId;\n case \"ready\":\n case \"event\":\n case \"ext-event\":\n case \"error\":\n return undefined;\n }\n};\n"],"mappings":"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"}
|