opentray 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,6 +53,43 @@ Run the human-visible daemon tray example:
53
53
  pnpm --filter opentray example:daemon-tray
54
54
  ```
55
55
 
56
+ Run the direct webview control demo:
57
+
58
+ ```bash
59
+ pnpm --filter opentray example:webview-control
60
+ ```
61
+
62
+ That demo opens a native WebView window immediately and puts the controls inside the page itself, so you can exercise frameless, transparent, keep-on-top, title, icon, screen, and navigation behavior without going through the tray menu first.
63
+ Treat `example:webview-control` as the API exercise surface. It is useful for probing capabilities and events, but it is not the canonical recipe for a tray-anchored glass shell.
64
+
65
+ Run the dedicated tray-panel demo:
66
+
67
+ ```bash
68
+ cargo build -p opentray-bin -p opentray-ext-webview
69
+ pnpm --filter opentray example:tray-panel
70
+ ```
71
+
72
+ This demo is the canonical custom TrayPanel case: one `primaryEvent` tray item, backend `tray.getBounds()`, page `navigator.opentray.tray.getBounds()`, screen-aware repositioning, and a frameless glass panel with `keepOnTop`.
73
+ It also follows the native-glass rule strictly: transparent native window background, no root HTML shell styling, and content padding only inside the page.
74
+ It now also pins `style.platform.macos.materialState: "active"` so the tray-launched material surface does not immediately fall back to the inactive grey AppKit appearance.
75
+ For a step-by-step walkthrough of the examples and expected behavior, read [examples/EXAMPLE.md](./examples/EXAMPLE.md).
76
+
77
+ ## Release Channels And Maturity
78
+
79
+ OpenTray uses release channels and capability maturity together. Do not read a published platform package as proof that every visible runtime path is already stable.
80
+
81
+ - `latest`: the stable package line
82
+ - `alpha`: the prerelease/testing package line, installed as `npm i opentray@alpha`
83
+
84
+ Current WebView truth:
85
+
86
+ - macOS is the current `stable` human-visible acceptance path
87
+ - Windows and Linux are currently `alpha` for WebView runtime behavior, even though their platform packages are published
88
+ - some requests are `unsupported by design`, such as asking the macOS runtime to apply a Windows-only style family
89
+ - some results are `unavailable by context`, such as tray-bounds projection when the current session has no authoritative tray anchor
90
+
91
+ When run from the repo worktree, the example automatically discovers `target/debug` or `target/release` `libopentray_ext_webview` and wires it through `OPENTRAY_EXT_PATH` before starting the daemon. That keeps the example on the real `load-ext` path without requiring a manual staging step for routine source-level testing.
92
+
56
93
  After installing from npm, use the published CLI smoke path instead of workspace scripts:
57
94
 
58
95
  ```bash
@@ -68,14 +105,24 @@ pnpm --filter opentray cli -- daemon stop
68
105
  pnpm --filter opentray cli -- daemon restart
69
106
  ```
70
107
 
71
- The menu includes `WebView Commands` entries that call the `@opentray/ext-webview` facade. On macOS, `Show HTML` loads the platform WebView dynamic library and opens a real native WebView window owned by that library; `Navigate`, `Post Message`, `Evaluate JS`, and `Hide` operate on that window.
108
+ The menu intentionally declares only one plain item: `Open WebView` with `primaryEvent: true`. On macOS, that single primary item lets clicking the status item direct-trigger the normal `menuClick` event without opening a menu, so the example can behave like a one-action launcher for a WebView-built surface.
109
+
110
+ On platforms that expose a direct tray activation gesture, the same item opens the WebView immediately while still remaining a normal native menu item on platforms or gestures that show a menu.
111
+
112
+ The host side of that example can call `await tray.getBounds()` to read the current tray geometry before opening the window. The rendered page can also opt into `navigator.opentray.tray.getBounds()` when it needs the same tray anchor for layout.
72
113
 
73
- The `Show HTML` demo also enables the injected page bridge so the rendered page can call `navigator.window` / `navigator.opentrayWindow` and, for the demo only, opt into `window.close()` / `window.moveTo()` / `window.resizeTo()` overrides. Use the in-page buttons to verify `getCapabilities`, `getStyle`, `setStyle({ frameless })`, move, resize, and close behavior visually instead of relying only on terminal logs.
114
+ The opened WebView also enables the injected page bridge so the rendered page can call `navigator.window` / `navigator.opentrayWindow` and, for the demo only, opt into `window.close()` / `window.moveTo()` / `window.resizeTo()` overrides. Use the in-page buttons to verify `getCapabilities`, `getStyle`, `setStyle({ frameless })`, move, resize, close, and tray-bounds behavior visually instead of relying only on terminal logs.
74
115
 
75
116
  The Lynx smoke path uses the same generic extension loader but launches a real `LynxExplorer.app.zip` runtime sidecar from `@opentray/ext-lynx-darwin-*`. It starts the requested `.lynx.bundle` immediately on run and exposes `Reload Bundle`, `Hide Window`, and `Quit Smoke` through tray-routed events.
76
117
 
77
118
  First-stage platform packages are published for macOS, Linux, and Windows. macOS is the current human-visual acceptance path. Linux and Windows artifacts are present for package topology validation, but unsupported broker/WebView capability must fail explicitly rather than pretending a visible UI exists. Lynx is intentionally macOS-first for now and should fail honestly on other platforms instead of pretending the runtime exists.
78
119
 
120
+ If you are validating the current prerelease branch before stable publication, install from the alpha channel and treat that as alpha evidence rather than stable evidence:
121
+
122
+ ```bash
123
+ npm i opentray@alpha
124
+ ```
125
+
79
126
  For local native smoke before npm publish, stage the current platform artifacts first:
80
127
 
81
128
  ```bash
@@ -89,8 +136,18 @@ OPENTRAY_EXAMPLE_WEBVIEW_SMOKE=1 pnpm --filter opentray cli -- smoke daemon-tray
89
136
  pnpm --filter opentray cli -- smoke daemon-lynx --bundle ./research/lynx/app/dist/main.lynx.bundle
90
137
  ```
91
138
 
139
+ During source-level daemon work, restart the daemon with the freshly built broker binary before testing tray behavior. Otherwise the CLI may reuse the already staged same-version daemon:
140
+
141
+ ```bash
142
+ cargo build -p opentray-bin
143
+ OPENTRAY_BROKER_BIN="$PWD/target/debug/opentray" pnpm --filter opentray cli -- daemon restart
144
+ OPENTRAY_BROKER_BIN="$PWD/target/debug/opentray" pnpm --filter opentray cli -- smoke daemon-tray
145
+ ```
146
+
92
147
  The daemon exits automatically after 30 seconds with no connected clients. Set `OPENTRAY_DAEMON_IDLE_TIMEOUT_MS=0` to keep it alive during debugging, or provide another millisecond value for a custom idle release window.
93
148
 
149
+ `OPENTRAY_HOME` should point at a home root, not the `.opentray` state directory itself. OpenTray always stores versioned state under `"$OPENTRAY_HOME/.opentray/<package-version>"`.
150
+
94
151
  To confirm the native runtime split on macOS after a release build:
95
152
 
96
153
  ```bash
package/dist/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { t as createClient } from "./client-Db-tztnj.mjs";
3
- import { a as inspectDaemon, c as stopDaemon, i as createNodeDaemonDriver, n as resolveDaemonPaths, o as restartDaemon, r as readPackageVersion, s as startDaemon, t as connectLocalBroker } from "./local-broker-mQmeVLF5.mjs";
2
+ import { t as createClient } from "./client-B_zxQOo5.mjs";
3
+ import { a as inspectDaemon, c as stopDaemon, i as createNodeDaemonDriver, n as resolveDaemonPaths, o as restartDaemon, r as readPackageVersion, s as startDaemon, t as connectLocalBroker } from "./local-broker-CEIHPBcK.mjs";
4
4
  import { homedir } from "node:os";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { existsSync, realpathSync } from "node:fs";
@@ -162,20 +162,7 @@ function createVisibleIcon$1() {
162
162
  }
163
163
  //#endregion
164
164
  //#region src/smoke/daemon-tray.ts
165
- const menuLabels = new Map([
166
- [1, "Primary Action"],
167
- [3, "Checked Capability"],
168
- [4, "Radio: Active"],
169
- [5, "Radio: Passive"],
170
- [6, "Nested Action"],
171
- [7, "Nested Check"],
172
- [8, "WebView: Show HTML"],
173
- [9, "WebView: Navigate"],
174
- [10, "WebView: Post Message"],
175
- [11, "WebView: Evaluate"],
176
- [12, "WebView: Hide"],
177
- [99, "Quit Demo"]
178
- ]);
165
+ const menuLabels = new Map([[1, "Open WebView"]]);
179
166
  const runDaemonTraySmoke = async () => {
180
167
  const connection = await connectLocalBroker();
181
168
  const client = createClient(connection, { requestIdPrefix: "daemon-smoke" });
@@ -191,93 +178,15 @@ const runDaemonTraySmoke = async () => {
191
178
  title: "OpenTray",
192
179
  tooltip: {
193
180
  title: "OpenTray",
194
- description: "npm-installed daemon smoke with broker-routed menu events"
181
+ description: "Single primary tray action; macOS direct-triggers without opening a menu"
195
182
  },
196
183
  icon: createVisibleIcon(),
197
- menu: { items: [
198
- {
199
- type: "item",
200
- id: 1,
201
- title: "Primary Action"
202
- },
203
- {
204
- type: "item",
205
- id: 2,
206
- title: "Disabled Action",
207
- enabled: false
208
- },
209
- {
210
- type: "check",
211
- id: 3,
212
- title: "Checked Capability",
213
- checked: true
214
- },
215
- {
216
- type: "radio",
217
- id: 4,
218
- title: "Radio: Active",
219
- group: 1,
220
- checked: true
221
- },
222
- {
223
- type: "radio",
224
- id: 5,
225
- title: "Radio: Passive",
226
- group: 1
227
- },
228
- { type: "separator" },
229
- {
230
- type: "submenu",
231
- title: "Nested Actions",
232
- items: [{
233
- type: "item",
234
- id: 6,
235
- title: "Nested Action"
236
- }, {
237
- type: "check",
238
- id: 7,
239
- title: "Nested Check",
240
- checked: false
241
- }]
242
- },
243
- {
244
- type: "submenu",
245
- title: "WebView Commands",
246
- items: [
247
- {
248
- type: "item",
249
- id: 8,
250
- title: "Show HTML"
251
- },
252
- {
253
- type: "item",
254
- id: 9,
255
- title: "Navigate"
256
- },
257
- {
258
- type: "item",
259
- id: 10,
260
- title: "Post Message"
261
- },
262
- {
263
- type: "item",
264
- id: 11,
265
- title: "Evaluate JS"
266
- },
267
- {
268
- type: "item",
269
- id: 12,
270
- title: "Hide"
271
- }
272
- ]
273
- },
274
- { type: "separator" },
275
- {
276
- type: "item",
277
- id: 99,
278
- title: "Quit Demo"
279
- }
280
- ] }
184
+ menu: { items: [{
185
+ type: "item",
186
+ id: 1,
187
+ title: "Open WebView",
188
+ primaryEvent: true
189
+ }] }
281
190
  });
282
191
  console.log(`tray: ${tray.trayId}`);
283
192
  await connection.request({
@@ -288,10 +197,8 @@ const runDaemonTraySmoke = async () => {
288
197
  path: "@opentray/ext-webview"
289
198
  });
290
199
  console.log("webview extension requested through the generic load-ext path");
291
- console.log("open the system tray item and choose any enabled menu item to see routed events");
200
+ console.log("click the tray icon: macOS should direct-trigger the single primary action without opening a menu");
292
201
  let closed = false;
293
- let messageCount = 0;
294
- let evalCount = 0;
295
202
  let exitTimer;
296
203
  const shutdown = async () => {
297
204
  if (closed) return;
@@ -303,63 +210,56 @@ const runDaemonTraySmoke = async () => {
303
210
  await tray.commandExtension("webview", command);
304
211
  };
305
212
  const handleMenuClick = async (itemId) => {
306
- if (itemId === 99) {
307
- console.log("quit item routed; closing smoke connection");
308
- await shutdown();
213
+ if (itemId === 1) {
214
+ const trayBounds = await tray.getBounds();
215
+ console.log(`tray bounds: ${JSON.stringify(trayBounds)}`);
216
+ await commandWebview({
217
+ type: "show",
218
+ html: createWebviewDemoHtml(),
219
+ width: 420,
220
+ height: 260,
221
+ title: "OpenTray WebView Smoke",
222
+ icon: {
223
+ type: "href",
224
+ href: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII="
225
+ },
226
+ style: {
227
+ frameless: true,
228
+ transparent: true,
229
+ keepOnTop: true,
230
+ platform: { macos: {
231
+ material: "hudWindow",
232
+ materialState: "active"
233
+ } }
234
+ },
235
+ fallbackRect: trayBounds.rect ?? {
236
+ x: 0,
237
+ y: 0,
238
+ width: 1,
239
+ height: 1
240
+ },
241
+ nativeWindowApi: true,
242
+ bindWindowGlobals: true,
243
+ nativeScreenApi: true,
244
+ bindScreenGlobals: true,
245
+ nativeTrayApi: true,
246
+ titleSync: {
247
+ documentToWindow: true,
248
+ windowToDocument: true
249
+ },
250
+ iconSync: true,
251
+ nativeApiPolicy: {
252
+ defaultSrc: ["'local'"],
253
+ window: ["https://example.com"],
254
+ screen: ["https://example.com"],
255
+ tray: ["https://example.com"],
256
+ titleSync: ["https://example.com"],
257
+ iconSync: ["https://example.com"]
258
+ }
259
+ });
260
+ console.log("primary tray action: webview show");
309
261
  return;
310
262
  }
311
- if (itemId < 8 || itemId > 12) return;
312
- switch (itemId) {
313
- case 8:
314
- await commandWebview({
315
- type: "show",
316
- html: createWebviewDemoHtml(),
317
- width: 420,
318
- height: 260,
319
- fallbackRect: {
320
- x: 0,
321
- y: 0,
322
- width: 1,
323
- height: 1
324
- },
325
- nativeWindowApi: true,
326
- bindWindowGlobals: true
327
- });
328
- console.log("webview command: show");
329
- break;
330
- case 9:
331
- await commandWebview({
332
- type: "navigate",
333
- url: "https://example.com/opentray-status"
334
- });
335
- console.log("webview command: navigate");
336
- break;
337
- case 10:
338
- messageCount += 1;
339
- await commandWebview({
340
- type: "postMessage",
341
- payload: {
342
- kind: "ping",
343
- source: "opentray smoke daemon-tray",
344
- count: messageCount,
345
- sentAt: (/* @__PURE__ */ new Date()).toISOString()
346
- }
347
- });
348
- console.log("webview command: postMessage");
349
- break;
350
- case 11:
351
- evalCount += 1;
352
- await commandWebview({
353
- type: "evaluate",
354
- js: createVisibleEvaluateScript(evalCount)
355
- });
356
- console.log("webview command: evaluate");
357
- break;
358
- case 12:
359
- await commandWebview({ type: "hide" });
360
- console.log("webview command: hide");
361
- break;
362
- }
363
263
  };
364
264
  connection.onEvent((frame) => {
365
265
  console.log(`broker -> client ${JSON.stringify(frame)}`);
@@ -377,19 +277,12 @@ const runDaemonTraySmoke = async () => {
377
277
  }
378
278
  const webviewSmoke = process.env.OPENTRAY_EXAMPLE_WEBVIEW_SMOKE;
379
279
  try {
380
- if (webviewSmoke === "show") await handleMenuClick(8);
381
- else if (webviewSmoke === "1") {
280
+ if (webviewSmoke === "show" || webviewSmoke === "1") await handleMenuClick(1);
281
+ if (webviewSmoke === "1") {
382
282
  if (exitTimer !== void 0) {
383
283
  clearTimeout(exitTimer);
384
284
  exitTimer = void 0;
385
285
  }
386
- for (const itemId of [
387
- 8,
388
- 10,
389
- 11,
390
- 9,
391
- 12
392
- ]) await handleMenuClick(itemId);
393
286
  await sleep(300);
394
287
  await shutdown();
395
288
  }
@@ -478,47 +371,17 @@ function createWebviewDemoHtml() {
478
371
  <body>
479
372
  <main>
480
373
  <h1>OpenTray WebView</h1>
481
- <p>Use the tray menu to mutate this native WebView through extension commands.</p>
374
+ <p>This window was opened by the single primary tray action.</p>
482
375
  <section>
483
376
  <div class="card">
484
- <div class="label">postMessage</div>
485
- <code id="message-status">Waiting for WebView Commands -> Post Message</code>
486
- </div>
487
- <div class="card">
488
- <div class="label">evaluate JS</div>
489
- <code id="eval-status">Waiting for WebView Commands -> Evaluate JS</code>
377
+ <div class="label">primary event</div>
378
+ <code>macOS single primary item direct-triggered menuClick -> WebView show</code>
490
379
  </div>
491
380
  </section>
492
381
  </main>
493
- <script>
494
- window.addEventListener("message", (event) => {
495
- const target = document.getElementById("message-status");
496
- if (target) {
497
- target.textContent = JSON.stringify(event.data, null, 2);
498
- }
499
- });
500
- <\/script>
501
382
  </body>
502
383
  </html>`;
503
384
  }
504
- function createVisibleEvaluateScript(count) {
505
- return `(() => {
506
- let target = document.getElementById("eval-status");
507
- if (!target) {
508
- const panel = document.createElement("div");
509
- panel.style.cssText = "position:fixed;left:16px;right:16px;bottom:16px;z-index:2147483647;padding:12px;border-radius:12px;background:#fff8e7;color:#18220f;box-shadow:0 12px 30px rgba(0,0,0,.18);font:14px ui-rounded, sans-serif;";
510
- panel.textContent = "OpenTray Evaluate JS: ";
511
- target = document.createElement("code");
512
- target.id = "eval-status";
513
- panel.appendChild(target);
514
- document.body.appendChild(panel);
515
- }
516
- if (target) {
517
- target.textContent = "Evaluate JS updated the WebView, count=${count}, at " + new Date().toLocaleTimeString();
518
- }
519
- window.__OPENTRAY_DEMO__ = { evaluated: true, count: ${count} };
520
- })();`;
521
- }
522
385
  function sleep(ms) {
523
386
  return new Promise((resolve) => setTimeout(resolve, ms));
524
387
  }
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["menuLabels","createVisibleIcon"],"sources":["../src/smoke/daemon-lynx.ts","../src/smoke/daemon-tray.ts","../src/cli.ts"],"sourcesContent":["import { existsSync, realpathSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nimport { createClient } from \"../client\";\nimport { connectLocalBroker } from \"../local-broker\";\n\nconst menuLabels = new Map<number, string>([\n [1, \"Reload Bundle\"],\n [2, \"Hide Window\"],\n [99, \"Quit Smoke\"],\n]);\n\nconst DEFAULT_LYNX_BUNDLE_ENV = \"OPENTRAY_LYNX_BUNDLE\";\n\nexport interface DaemonLynxSmokeOptions {\n bundlePath?: string;\n}\n\nexport const runDaemonLynxSmoke = async (\n options: DaemonLynxSmokeOptions = {},\n): Promise<void> => {\n const bundlePath = resolveLynxBundlePath(options.bundlePath);\n const connection = await connectLocalBroker();\n const client = createClient(connection, { requestIdPrefix: \"daemon-lynx\" });\n console.log(`connected: endpoint=${connection.endpoint} session=${connection.sessionId}`);\n\n const space = await client.createSpace({\n id: \"com.example.opentray.daemon-lynx\",\n title: \"OpenTray Lynx Smoke\",\n default: true,\n });\n console.log(`space: ${JSON.stringify(space.space)}`);\n\n const tray = await space.createTray({\n trayId: \"daemon-lynx-status\",\n title: \"OpenTray Lynx\",\n tooltip: {\n title: \"OpenTray Lynx\",\n description: \"Generic extension smoke for a real Lynx runtime window\",\n },\n icon: createVisibleIcon(),\n menu: {\n items: [\n { type: \"item\", id: 1, title: \"Reload Bundle\" },\n { type: \"item\", id: 2, title: \"Hide Window\" },\n { type: \"separator\" },\n { type: \"item\", id: 99, title: \"Quit Smoke\" },\n ],\n },\n });\n console.log(`tray: ${tray.trayId}`);\n\n await connection.request({\n type: \"load-ext\",\n requestId: \"daemon-lynx-load\",\n spaceId: space.space.spaceId,\n name: \"lynx\",\n path: \"@opentray/ext-lynx\",\n });\n console.log(\"lynx extension requested through the generic load-ext path\");\n\n let closed = false;\n let exitTimer: NodeJS.Timeout | undefined;\n let resolveClosed: (() => void) | undefined;\n\n const show = async (): Promise<void> => {\n await tray.commandExtension(\"lynx\", {\n type: \"show\",\n bundlePath,\n });\n console.log(`lynx command: show bundle=${bundlePath}`);\n };\n\n const hide = async (): Promise<void> => {\n await tray.commandExtension(\"lynx\", { type: \"hide\" });\n console.log(\"lynx command: hide\");\n };\n\n const shutdown = async (): Promise<void> => {\n if (closed) {\n return;\n }\n closed = true;\n if (exitTimer !== undefined) {\n clearTimeout(exitTimer);\n }\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n try {\n await hide();\n } catch (error) {\n console.error(`lynx hide during shutdown failed: ${String(error)}`);\n }\n await connection.close();\n resolveClosed?.();\n };\n\n const handleMenuClick = async (itemId: number): Promise<void> => {\n switch (itemId) {\n case 1:\n await show();\n return;\n case 2:\n await hide();\n return;\n case 99:\n console.log(\"quit item routed; closing lynx smoke connection\");\n await shutdown();\n return;\n default:\n return;\n }\n };\n\n connection.onEvent((frame) => {\n console.log(`broker -> client ${JSON.stringify(frame)}`);\n if (frame.type === \"event\" && frame.event.type === \"menuClick\") {\n console.log(`menu click: ${menuLabels.get(frame.event.itemId) ?? frame.event.itemId}`);\n void handleMenuClick(frame.event.itemId);\n return;\n }\n if (frame.type === \"ext-event\" && frame.ext === \"lynx\") {\n console.log(`lynx event: ${JSON.stringify(frame.data)}`);\n }\n });\n\n const onSignal = (): void => {\n void shutdown();\n };\n process.on(\"SIGINT\", onSignal);\n process.on(\"SIGTERM\", onSignal);\n\n const exitAfter = process.env.OPENTRAY_EXAMPLE_EXIT_AFTER_MS;\n if (exitAfter !== undefined && exitAfter.length > 0) {\n const duration = Number.parseInt(exitAfter, 10);\n if (Number.isInteger(duration) && duration > 0) {\n exitTimer = setTimeout(() => {\n void shutdown();\n }, duration);\n }\n }\n\n try {\n await show();\n console.log(\"use the tray menu to reload or hide the Lynx window, or press Ctrl+C to exit\");\n } catch (error) {\n await connection.close();\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n throw error;\n }\n\n await new Promise<void>((resolve) => {\n resolveClosed = resolve;\n });\n};\n\nexport const resolveLynxBundlePath = (value?: string): string => {\n const resolved = value ?? process.env[DEFAULT_LYNX_BUNDLE_ENV];\n if (resolved === undefined || resolved.length === 0) {\n throw new Error(\n `lynx smoke requires --bundle <path> or ${DEFAULT_LYNX_BUNDLE_ENV}=<path-to-main.lynx.bundle>`,\n );\n }\n const baseDir = process.env.INIT_CWD ?? process.cwd();\n const absolutePath = resolve(baseDir, resolved);\n if (!existsSync(absolutePath)) {\n throw new Error(`lynx smoke bundle does not exist: ${absolutePath}`);\n }\n return realpathSync(absolutePath);\n};\n\nfunction createVisibleIcon(): { type: \"rgba\"; width: number; height: number; data: number[] } {\n const width = 32;\n const height = 32;\n const data: number[] = [];\n\n for (let y = 0; y < height; y += 1) {\n for (let x = 0; x < width; x += 1) {\n const inStroke = x <= 5 || (x >= 13 && x <= 18) || y >= 26;\n const inDiagonal = y >= x + 10 && x <= 18;\n const alpha = inStroke || inDiagonal ? 255 : 0;\n data.push(34, 92, 56, alpha);\n }\n }\n\n return { type: \"rgba\", width, height, data };\n}\n","import type { Rect } from \"@opentray/spec\";\n\nimport { createClient } from \"../client\";\nimport { connectLocalBroker } from \"../local-broker\";\n\nconst menuLabels = new Map<number, string>([\n [1, \"Primary Action\"],\n [3, \"Checked Capability\"],\n [4, \"Radio: Active\"],\n [5, \"Radio: Passive\"],\n [6, \"Nested Action\"],\n [7, \"Nested Check\"],\n [8, \"WebView: Show HTML\"],\n [9, \"WebView: Navigate\"],\n [10, \"WebView: Post Message\"],\n [11, \"WebView: Evaluate\"],\n [12, \"WebView: Hide\"],\n [99, \"Quit Demo\"],\n]);\n\ninterface WebviewShowCommand {\n type: \"show\";\n html?: string;\n url?: string;\n width?: number;\n height?: number;\n fallbackRect?: Rect;\n nativeWindowApi?: boolean;\n bindWindowGlobals?: boolean;\n}\n\nexport const runDaemonTraySmoke = async (): Promise<void> => {\n const connection = await connectLocalBroker();\n const client = createClient(connection, { requestIdPrefix: \"daemon-smoke\" });\n console.log(`connected: endpoint=${connection.endpoint} session=${connection.sessionId}`);\n\n const space = await client.createSpace({\n id: \"com.example.opentray.daemon-smoke\",\n title: \"OpenTray Daemon Smoke\",\n default: true,\n });\n console.log(`space: ${JSON.stringify(space.space)}`);\n\n const tray = await space.createTray({\n trayId: \"daemon-smoke-status\",\n title: \"OpenTray\",\n tooltip: {\n title: \"OpenTray\",\n description: \"npm-installed daemon smoke with broker-routed menu events\",\n },\n icon: createVisibleIcon(),\n menu: {\n items: [\n { type: \"item\", id: 1, title: \"Primary Action\" },\n { type: \"item\", id: 2, title: \"Disabled Action\", enabled: false },\n { type: \"check\", id: 3, title: \"Checked Capability\", checked: true },\n { type: \"radio\", id: 4, title: \"Radio: Active\", group: 1, checked: true },\n { type: \"radio\", id: 5, title: \"Radio: Passive\", group: 1 },\n { type: \"separator\" },\n {\n type: \"submenu\",\n title: \"Nested Actions\",\n items: [\n { type: \"item\", id: 6, title: \"Nested Action\" },\n { type: \"check\", id: 7, title: \"Nested Check\", checked: false },\n ],\n },\n {\n type: \"submenu\",\n title: \"WebView Commands\",\n items: [\n { type: \"item\", id: 8, title: \"Show HTML\" },\n { type: \"item\", id: 9, title: \"Navigate\" },\n { type: \"item\", id: 10, title: \"Post Message\" },\n { type: \"item\", id: 11, title: \"Evaluate JS\" },\n { type: \"item\", id: 12, title: \"Hide\" },\n ],\n },\n { type: \"separator\" },\n { type: \"item\", id: 99, title: \"Quit Demo\" },\n ],\n },\n });\n console.log(`tray: ${tray.trayId}`);\n\n await connection.request({\n type: \"load-ext\",\n requestId: \"daemon-smoke-load-webview\",\n spaceId: space.space.spaceId,\n name: \"webview\",\n path: \"@opentray/ext-webview\",\n });\n console.log(\"webview extension requested through the generic load-ext path\");\n console.log(\"open the system tray item and choose any enabled menu item to see routed events\");\n\n let closed = false;\n let messageCount = 0;\n let evalCount = 0;\n let exitTimer: NodeJS.Timeout | undefined;\n\n const shutdown = async (): Promise<void> => {\n if (closed) {\n return;\n }\n closed = true;\n if (exitTimer !== undefined) {\n clearTimeout(exitTimer);\n }\n await connection.close();\n };\n\n const commandWebview = async (command: WebviewShowCommand | Record<string, unknown>): Promise<void> => {\n await tray.commandExtension(\"webview\", command);\n };\n\n const handleMenuClick = async (itemId: number): Promise<void> => {\n if (itemId === 99) {\n console.log(\"quit item routed; closing smoke connection\");\n await shutdown();\n return;\n }\n\n if (itemId < 8 || itemId > 12) {\n return;\n }\n\n switch (itemId) {\n case 8:\n await commandWebview({\n type: \"show\",\n html: createWebviewDemoHtml(),\n width: 420,\n height: 260,\n fallbackRect: { x: 0, y: 0, width: 1, height: 1 },\n nativeWindowApi: true,\n bindWindowGlobals: true,\n });\n console.log(\"webview command: show\");\n break;\n case 9:\n await commandWebview({ type: \"navigate\", url: \"https://example.com/opentray-status\" });\n console.log(\"webview command: navigate\");\n break;\n case 10:\n messageCount += 1;\n await commandWebview({\n type: \"postMessage\",\n payload: {\n kind: \"ping\",\n source: \"opentray smoke daemon-tray\",\n count: messageCount,\n sentAt: new Date().toISOString(),\n },\n });\n console.log(\"webview command: postMessage\");\n break;\n case 11:\n evalCount += 1;\n await commandWebview({ type: \"evaluate\", js: createVisibleEvaluateScript(evalCount) });\n console.log(\"webview command: evaluate\");\n break;\n case 12:\n await commandWebview({ type: \"hide\" });\n console.log(\"webview command: hide\");\n break;\n }\n };\n\n connection.onEvent((frame) => {\n console.log(`broker -> client ${JSON.stringify(frame)}`);\n if (frame.type === \"event\" && frame.event.type === \"menuClick\") {\n console.log(`menu click: ${menuLabels.get(frame.event.itemId) ?? frame.event.itemId}`);\n void handleMenuClick(frame.event.itemId);\n }\n });\n\n const exitAfter = process.env.OPENTRAY_EXAMPLE_EXIT_AFTER_MS;\n if (exitAfter !== undefined && exitAfter.length > 0) {\n const duration = Number.parseInt(exitAfter, 10);\n if (Number.isInteger(duration) && duration > 0) {\n exitTimer = setTimeout(() => {\n void shutdown();\n }, duration);\n }\n }\n\n const webviewSmoke = process.env.OPENTRAY_EXAMPLE_WEBVIEW_SMOKE;\n try {\n if (webviewSmoke === \"show\") {\n await handleMenuClick(8);\n } else if (webviewSmoke === \"1\") {\n if (exitTimer !== undefined) {\n clearTimeout(exitTimer);\n exitTimer = undefined;\n }\n for (const itemId of [8, 10, 11, 9, 12]) {\n await handleMenuClick(itemId);\n }\n await sleep(300);\n await shutdown();\n }\n } catch (error) {\n await shutdown();\n throw error;\n }\n};\n\nfunction createVisibleIcon(): { type: \"rgba\"; width: number; height: number; data: number[] } {\n const width = 32;\n const height = 32;\n const data: number[] = [];\n\n for (let y = 0; y < height; y += 1) {\n for (let x = 0; x < width; x += 1) {\n const dx = x - 15.5;\n const dy = y - 15.5;\n const distance = Math.sqrt(dx * dx + dy * dy);\n const inRing = distance >= 9 && distance <= 14;\n const inCore = distance <= 5;\n const inNeedle = Math.abs(dx + dy) <= 1.2 && x >= 9 && x <= 23 && y >= 9 && y <= 23;\n\n if (inRing || inCore || inNeedle) {\n data.push(inCore ? 255 : 24, inNeedle ? 240 : 132, inRing ? 72 : 96, 255);\n } else {\n data.push(0, 0, 0, 0);\n }\n }\n }\n\n return { type: \"rgba\", width, height, data };\n}\n\nfunction createWebviewDemoHtml(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>OpenTray WebView</title>\n <style>\n :root {\n color: #18220f;\n background: #f6edd8;\n font: 15px ui-rounded, \"SF Pro Rounded\", \"Avenir Next\", sans-serif;\n }\n body {\n margin: 0;\n }\n main {\n min-height: 100vh;\n box-sizing: border-box;\n padding: 22px;\n background:\n radial-gradient(circle at 84% 10%, rgba(34, 132, 96, 0.22), transparent 34%),\n linear-gradient(135deg, #fff8e7 0%, #e9f0d8 100%);\n }\n h1 {\n margin: 0 0 8px;\n font-size: 24px;\n letter-spacing: -0.04em;\n }\n p {\n margin: 0 0 16px;\n color: #526044;\n }\n section {\n display: grid;\n gap: 10px;\n }\n .card {\n border: 1px solid rgba(24, 34, 15, 0.16);\n border-radius: 14px;\n padding: 12px;\n background: rgba(255, 255, 255, 0.72);\n box-shadow: 0 12px 30px rgba(56, 72, 36, 0.12);\n }\n .label {\n margin-bottom: 5px;\n color: #7b5b1d;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n code {\n word-break: break-word;\n }\n </style>\n </head>\n <body>\n <main>\n <h1>OpenTray WebView</h1>\n <p>Use the tray menu to mutate this native WebView through extension commands.</p>\n <section>\n <div class=\"card\">\n <div class=\"label\">postMessage</div>\n <code id=\"message-status\">Waiting for WebView Commands -> Post Message</code>\n </div>\n <div class=\"card\">\n <div class=\"label\">evaluate JS</div>\n <code id=\"eval-status\">Waiting for WebView Commands -> Evaluate JS</code>\n </div>\n </section>\n </main>\n <script>\n window.addEventListener(\"message\", (event) => {\n const target = document.getElementById(\"message-status\");\n if (target) {\n target.textContent = JSON.stringify(event.data, null, 2);\n }\n });\n </script>\n </body>\n</html>`;\n}\n\nfunction createVisibleEvaluateScript(count: number): string {\n return `(() => {\n let target = document.getElementById(\"eval-status\");\n if (!target) {\n const panel = document.createElement(\"div\");\n panel.style.cssText = \"position:fixed;left:16px;right:16px;bottom:16px;z-index:2147483647;padding:12px;border-radius:12px;background:#fff8e7;color:#18220f;box-shadow:0 12px 30px rgba(0,0,0,.18);font:14px ui-rounded, sans-serif;\";\n panel.textContent = \"OpenTray Evaluate JS: \";\n target = document.createElement(\"code\");\n target.id = \"eval-status\";\n panel.appendChild(target);\n document.body.appendChild(panel);\n }\n if (target) {\n target.textContent = \"Evaluate JS updated the WebView, count=${count}, at \" + new Date().toLocaleTimeString();\n }\n window.__OPENTRAY_DEMO__ = { evaluated: true, count: ${count} };\n })();`;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","#!/usr/bin/env node\nimport { realpathSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { DaemonHealth } from \"@opentray/spec\";\n\nimport { createNodeDaemonDriver, inspectDaemon, restartDaemon, startDaemon, stopDaemon } from \"./daemon/lifecycle\";\nimport { readPackageVersion } from \"./daemon/package-version\";\nimport { resolveDaemonPaths } from \"./daemon/paths\";\nimport { connectLocalBroker } from \"./local-broker\";\nimport { runDaemonLynxSmoke } from \"./smoke/daemon-lynx\";\nimport { runDaemonTraySmoke } from \"./smoke/daemon-tray\";\n\nconst packageJsonUrl = new URL(\"../package.json\", import.meta.url);\nconst cliModulePath = fileURLToPath(import.meta.url);\n\ntype CliCommand =\n | { type: \"daemon\"; action: \"start\" | \"stop\" | \"restart\" | \"health\" }\n | { type: \"smoke\"; name: \"daemon-tray\" }\n | { type: \"smoke\"; name: \"daemon-lynx\"; bundlePath?: string }\n | { type: \"help\" };\n\nexport const parseCliCommand = (argv: string[]): CliCommand => {\n const [group, action, ...rest] = argv;\n if (group !== \"daemon\") {\n if (group === \"smoke\" && action === \"daemon-tray\") {\n return { type: \"smoke\", name: \"daemon-tray\" };\n }\n if (group === \"smoke\" && action === \"daemon-lynx\") {\n const bundlePath = parseSmokeBundlePath(rest);\n return bundlePath === undefined\n ? { type: \"smoke\", name: \"daemon-lynx\" }\n : { type: \"smoke\", name: \"daemon-lynx\", bundlePath };\n }\n return { type: \"help\" };\n }\n if (action === \"start\" || action === \"stop\" || action === \"restart\" || action === \"health\") {\n return { type: \"daemon\", action };\n }\n\n return { type: \"help\" };\n};\n\nexport const runCli = async (argv: string[]): Promise<number> => {\n const command = parseCliCommand(argv);\n const packageVersion = process.env.OPENTRAY_DAEMON_PACKAGE_VERSION ?? (await readPackageVersion(packageJsonUrl));\n const paths = resolveDaemonPaths({\n homeDir: process.env.OPENTRAY_HOME ?? homedir(),\n packageVersion,\n });\n\n if (command.type === \"help\") {\n printHelp();\n return 1;\n }\n\n if (command.type === \"smoke\") {\n if (command.name === \"daemon-tray\") {\n await runDaemonTraySmoke();\n } else {\n await runDaemonLynxSmoke(\n command.bundlePath === undefined ? {} : { bundlePath: command.bundlePath },\n );\n }\n return 0;\n }\n\n const driver = createNodeDaemonDriver(cliModulePath);\n\n if (command.action === \"start\") {\n const result = await startDaemon({ paths, driver });\n console.log(`opentray daemon ${result.status}: pid=${result.pid} endpoint=${result.paths.endpoint}`);\n return 0;\n }\n\n if (command.action === \"stop\") {\n const result = await stopDaemon({ paths, driver });\n console.log(\n result.status === \"stopped\"\n ? `opentray daemon stopped: pid=${result.pid}`\n : \"opentray daemon not running\",\n );\n return 0;\n }\n\n if (command.action === \"restart\") {\n const result = await restartDaemon({ paths, driver });\n console.log(`opentray daemon ${result.status}: pid=${result.pid} endpoint=${result.paths.endpoint}`);\n return 0;\n }\n\n if (command.action === \"health\") {\n const inspected = await inspectDaemon({ paths, driver });\n if (inspected.status === \"not-running\") {\n console.log(\"opentray daemon not running\");\n return 0;\n }\n\n const connection = await connectLocalBroker({\n autoStart: false,\n endpoint: paths.endpoint,\n homeDir: paths.homeDir,\n packageVersion,\n });\n try {\n const response = await connection.request({\n type: \"health\",\n requestId: \"opentray-daemon-health\",\n });\n if (response.type !== \"daemon-health\") {\n throw new Error(`expected daemon-health response, received ${response.type}`);\n }\n console.log(formatDaemonHealthOutput(response.health));\n } finally {\n await connection.close();\n }\n return 0;\n }\n\n printHelp();\n return 1;\n};\n\nconst printHelp = (): void => {\n console.error(\"Usage: opentray daemon <start|stop|restart|health>\");\n console.error(\" opentray smoke daemon-tray\");\n console.error(\" opentray smoke daemon-lynx --bundle <path-to-main.lynx.bundle>\");\n};\n\nexport const formatDaemonHealthOutput = (health: DaemonHealth): string => {\n const lines = [\n \"opentray daemon running\",\n `pid: ${health.pid}`,\n `endpoint: ${health.endpoint}`,\n `packageVersion: ${health.packageVersion}`,\n `protocolVersion: ${health.protocolVersion}`,\n `sessions: ${health.sessionCount}`,\n ];\n\n for (const session of health.sessions) {\n const internalLease = session.internalLeaseId ?? \"(pending)\";\n lines.push(\n `- sessionId=${session.sessionId} initialized=${session.initialized} internalLeaseId=${internalLease}`,\n );\n }\n\n return lines.join(\"\\n\");\n};\n\nexport const isCliEntrypoint = (argvEntryPath: string | undefined, modulePath: string): boolean => {\n if (argvEntryPath === undefined) return false;\n try {\n return realpathSync(argvEntryPath) === realpathSync(modulePath);\n } catch {\n return argvEntryPath === modulePath;\n }\n};\n\nfunction parseSmokeBundlePath(argv: string[]): string | undefined {\n for (let index = 0; index < argv.length; index += 1) {\n if (argv[index] === \"--bundle\") {\n return argv[index + 1];\n }\n }\n return undefined;\n}\n\nif (isCliEntrypoint(process.argv[1], cliModulePath)) {\n runCli(process.argv.slice(2)).then((code) => {\n process.exitCode = code;\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["menuLabels","createVisibleIcon"],"sources":["../src/smoke/daemon-lynx.ts","../src/smoke/daemon-tray.ts","../src/cli.ts"],"sourcesContent":["import { existsSync, realpathSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nimport { createClient } from \"../client\";\nimport { connectLocalBroker } from \"../local-broker\";\n\nconst menuLabels = new Map<number, string>([\n [1, \"Reload Bundle\"],\n [2, \"Hide Window\"],\n [99, \"Quit Smoke\"],\n]);\n\nconst DEFAULT_LYNX_BUNDLE_ENV = \"OPENTRAY_LYNX_BUNDLE\";\n\nexport interface DaemonLynxSmokeOptions {\n bundlePath?: string;\n}\n\nexport const runDaemonLynxSmoke = async (\n options: DaemonLynxSmokeOptions = {},\n): Promise<void> => {\n const bundlePath = resolveLynxBundlePath(options.bundlePath);\n const connection = await connectLocalBroker();\n const client = createClient(connection, { requestIdPrefix: \"daemon-lynx\" });\n console.log(`connected: endpoint=${connection.endpoint} session=${connection.sessionId}`);\n\n const space = await client.createSpace({\n id: \"com.example.opentray.daemon-lynx\",\n title: \"OpenTray Lynx Smoke\",\n default: true,\n });\n console.log(`space: ${JSON.stringify(space.space)}`);\n\n const tray = await space.createTray({\n trayId: \"daemon-lynx-status\",\n title: \"OpenTray Lynx\",\n tooltip: {\n title: \"OpenTray Lynx\",\n description: \"Generic extension smoke for a real Lynx runtime window\",\n },\n icon: createVisibleIcon(),\n menu: {\n items: [\n { type: \"item\", id: 1, title: \"Reload Bundle\" },\n { type: \"item\", id: 2, title: \"Hide Window\" },\n { type: \"separator\" },\n { type: \"item\", id: 99, title: \"Quit Smoke\" },\n ],\n },\n });\n console.log(`tray: ${tray.trayId}`);\n\n await connection.request({\n type: \"load-ext\",\n requestId: \"daemon-lynx-load\",\n spaceId: space.space.spaceId,\n name: \"lynx\",\n path: \"@opentray/ext-lynx\",\n });\n console.log(\"lynx extension requested through the generic load-ext path\");\n\n let closed = false;\n let exitTimer: NodeJS.Timeout | undefined;\n let resolveClosed: (() => void) | undefined;\n\n const show = async (): Promise<void> => {\n await tray.commandExtension(\"lynx\", {\n type: \"show\",\n bundlePath,\n });\n console.log(`lynx command: show bundle=${bundlePath}`);\n };\n\n const hide = async (): Promise<void> => {\n await tray.commandExtension(\"lynx\", { type: \"hide\" });\n console.log(\"lynx command: hide\");\n };\n\n const shutdown = async (): Promise<void> => {\n if (closed) {\n return;\n }\n closed = true;\n if (exitTimer !== undefined) {\n clearTimeout(exitTimer);\n }\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n try {\n await hide();\n } catch (error) {\n console.error(`lynx hide during shutdown failed: ${String(error)}`);\n }\n await connection.close();\n resolveClosed?.();\n };\n\n const handleMenuClick = async (itemId: number): Promise<void> => {\n switch (itemId) {\n case 1:\n await show();\n return;\n case 2:\n await hide();\n return;\n case 99:\n console.log(\"quit item routed; closing lynx smoke connection\");\n await shutdown();\n return;\n default:\n return;\n }\n };\n\n connection.onEvent((frame) => {\n console.log(`broker -> client ${JSON.stringify(frame)}`);\n if (frame.type === \"event\" && frame.event.type === \"menuClick\") {\n console.log(`menu click: ${menuLabels.get(frame.event.itemId) ?? frame.event.itemId}`);\n void handleMenuClick(frame.event.itemId);\n return;\n }\n if (frame.type === \"ext-event\" && frame.ext === \"lynx\") {\n console.log(`lynx event: ${JSON.stringify(frame.data)}`);\n }\n });\n\n const onSignal = (): void => {\n void shutdown();\n };\n process.on(\"SIGINT\", onSignal);\n process.on(\"SIGTERM\", onSignal);\n\n const exitAfter = process.env.OPENTRAY_EXAMPLE_EXIT_AFTER_MS;\n if (exitAfter !== undefined && exitAfter.length > 0) {\n const duration = Number.parseInt(exitAfter, 10);\n if (Number.isInteger(duration) && duration > 0) {\n exitTimer = setTimeout(() => {\n void shutdown();\n }, duration);\n }\n }\n\n try {\n await show();\n console.log(\"use the tray menu to reload or hide the Lynx window, or press Ctrl+C to exit\");\n } catch (error) {\n await connection.close();\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n throw error;\n }\n\n await new Promise<void>((resolve) => {\n resolveClosed = resolve;\n });\n};\n\nexport const resolveLynxBundlePath = (value?: string): string => {\n const resolved = value ?? process.env[DEFAULT_LYNX_BUNDLE_ENV];\n if (resolved === undefined || resolved.length === 0) {\n throw new Error(\n `lynx smoke requires --bundle <path> or ${DEFAULT_LYNX_BUNDLE_ENV}=<path-to-main.lynx.bundle>`,\n );\n }\n const baseDir = process.env.INIT_CWD ?? process.cwd();\n const absolutePath = resolve(baseDir, resolved);\n if (!existsSync(absolutePath)) {\n throw new Error(`lynx smoke bundle does not exist: ${absolutePath}`);\n }\n return realpathSync(absolutePath);\n};\n\nfunction createVisibleIcon(): { type: \"rgba\"; width: number; height: number; data: number[] } {\n const width = 32;\n const height = 32;\n const data: number[] = [];\n\n for (let y = 0; y < height; y += 1) {\n for (let x = 0; x < width; x += 1) {\n const inStroke = x <= 5 || (x >= 13 && x <= 18) || y >= 26;\n const inDiagonal = y >= x + 10 && x <= 18;\n const alpha = inStroke || inDiagonal ? 255 : 0;\n data.push(34, 92, 56, alpha);\n }\n }\n\n return { type: \"rgba\", width, height, data };\n}\n","import type { Rect } from \"@opentray/spec\";\n\nimport { createClient } from \"../client\";\nimport { connectLocalBroker } from \"../local-broker\";\n\nconst menuLabels = new Map<number, string>([\n [1, \"Open WebView\"],\n]);\n\ninterface WebviewShowCommand {\n type: \"show\";\n html?: string;\n url?: string;\n width?: number;\n height?: number;\n fallbackRect?: Rect;\n nativeWindowApi?: boolean;\n bindWindowGlobals?: boolean;\n nativeScreenApi?: boolean;\n bindScreenGlobals?: boolean;\n nativeTrayApi?: boolean;\n title?: string;\n icon?: { type: \"href\"; href: string };\n style?: {\n frameless?: boolean;\n transparent?: boolean;\n keepOnTop?: boolean;\n platform?: {\n macos?: {\n material?: string | null;\n materialState?: \"followsWindowActiveState\" | \"active\" | \"inactive\";\n cornerRadius?: number | null;\n };\n };\n };\n titleSync?: boolean | { documentToWindow?: boolean; windowToDocument?: boolean };\n iconSync?: boolean | { faviconToWindow?: boolean; windowToFavicon?: boolean };\n nativeApiPolicy?: {\n defaultSrc?: string[];\n window?: string[];\n screen?: string[];\n tray?: string[];\n windowGlobals?: string[];\n screenGlobals?: string[];\n titleSync?: string[];\n iconSync?: string[];\n };\n}\n\nexport const runDaemonTraySmoke = async (): Promise<void> => {\n const connection = await connectLocalBroker();\n const client = createClient(connection, { requestIdPrefix: \"daemon-smoke\" });\n console.log(`connected: endpoint=${connection.endpoint} session=${connection.sessionId}`);\n\n const space = await client.createSpace({\n id: \"com.example.opentray.daemon-smoke\",\n title: \"OpenTray Daemon Smoke\",\n default: true,\n });\n console.log(`space: ${JSON.stringify(space.space)}`);\n\n const tray = await space.createTray({\n trayId: \"daemon-smoke-status\",\n title: \"OpenTray\",\n tooltip: {\n title: \"OpenTray\",\n description: \"Single primary tray action; macOS direct-triggers without opening a menu\",\n },\n icon: createVisibleIcon(),\n menu: {\n items: [\n { type: \"item\", id: 1, title: \"Open WebView\", primaryEvent: true },\n ],\n },\n });\n console.log(`tray: ${tray.trayId}`);\n\n await connection.request({\n type: \"load-ext\",\n requestId: \"daemon-smoke-load-webview\",\n spaceId: space.space.spaceId,\n name: \"webview\",\n path: \"@opentray/ext-webview\",\n });\n console.log(\"webview extension requested through the generic load-ext path\");\n console.log(\"click the tray icon: macOS should direct-trigger the single primary action without opening a menu\");\n\n let closed = false;\n let exitTimer: NodeJS.Timeout | undefined;\n\n const shutdown = async (): Promise<void> => {\n if (closed) {\n return;\n }\n closed = true;\n if (exitTimer !== undefined) {\n clearTimeout(exitTimer);\n }\n await connection.close();\n };\n\n const commandWebview = async (command: WebviewShowCommand | Record<string, unknown>): Promise<void> => {\n await tray.commandExtension(\"webview\", command);\n };\n\n const handleMenuClick = async (itemId: number): Promise<void> => {\n if (itemId === 1) {\n const trayBounds = await tray.getBounds();\n console.log(`tray bounds: ${JSON.stringify(trayBounds)}`);\n await commandWebview({\n type: \"show\",\n html: createWebviewDemoHtml(),\n width: 420,\n height: 260,\n title: \"OpenTray WebView Smoke\",\n icon: {\n type: \"href\",\n href: \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=\",\n },\n style: {\n frameless: true,\n transparent: true,\n keepOnTop: true,\n platform: {\n macos: {\n material: \"hudWindow\",\n materialState: \"active\",\n },\n },\n },\n fallbackRect: trayBounds.rect ?? { x: 0, y: 0, width: 1, height: 1 },\n nativeWindowApi: true,\n bindWindowGlobals: true,\n nativeScreenApi: true,\n bindScreenGlobals: true,\n nativeTrayApi: true,\n titleSync: {\n documentToWindow: true,\n windowToDocument: true,\n },\n iconSync: true,\n nativeApiPolicy: {\n defaultSrc: [\"'local'\"],\n window: [\"https://example.com\"],\n screen: [\"https://example.com\"],\n tray: [\"https://example.com\"],\n titleSync: [\"https://example.com\"],\n iconSync: [\"https://example.com\"],\n },\n });\n console.log(\"primary tray action: webview show\");\n return;\n }\n };\n\n connection.onEvent((frame) => {\n console.log(`broker -> client ${JSON.stringify(frame)}`);\n if (frame.type === \"event\" && frame.event.type === \"menuClick\") {\n console.log(`menu click: ${menuLabels.get(frame.event.itemId) ?? frame.event.itemId}`);\n void handleMenuClick(frame.event.itemId);\n }\n });\n\n const exitAfter = process.env.OPENTRAY_EXAMPLE_EXIT_AFTER_MS;\n if (exitAfter !== undefined && exitAfter.length > 0) {\n const duration = Number.parseInt(exitAfter, 10);\n if (Number.isInteger(duration) && duration > 0) {\n exitTimer = setTimeout(() => {\n void shutdown();\n }, duration);\n }\n }\n\n const webviewSmoke = process.env.OPENTRAY_EXAMPLE_WEBVIEW_SMOKE;\n try {\n if (webviewSmoke === \"show\" || webviewSmoke === \"1\") {\n await handleMenuClick(1);\n }\n if (webviewSmoke === \"1\") {\n if (exitTimer !== undefined) {\n clearTimeout(exitTimer);\n exitTimer = undefined;\n }\n await sleep(300);\n await shutdown();\n }\n } catch (error) {\n await shutdown();\n throw error;\n }\n};\n\nfunction createVisibleIcon(): { type: \"rgba\"; width: number; height: number; data: number[] } {\n const width = 32;\n const height = 32;\n const data: number[] = [];\n\n for (let y = 0; y < height; y += 1) {\n for (let x = 0; x < width; x += 1) {\n const dx = x - 15.5;\n const dy = y - 15.5;\n const distance = Math.sqrt(dx * dx + dy * dy);\n const inRing = distance >= 9 && distance <= 14;\n const inCore = distance <= 5;\n const inNeedle = Math.abs(dx + dy) <= 1.2 && x >= 9 && x <= 23 && y >= 9 && y <= 23;\n\n if (inRing || inCore || inNeedle) {\n data.push(inCore ? 255 : 24, inNeedle ? 240 : 132, inRing ? 72 : 96, 255);\n } else {\n data.push(0, 0, 0, 0);\n }\n }\n }\n\n return { type: \"rgba\", width, height, data };\n}\n\nfunction createWebviewDemoHtml(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>OpenTray WebView</title>\n <style>\n :root {\n color: #18220f;\n background: #f6edd8;\n font: 15px ui-rounded, \"SF Pro Rounded\", \"Avenir Next\", sans-serif;\n }\n body {\n margin: 0;\n }\n main {\n min-height: 100vh;\n box-sizing: border-box;\n padding: 22px;\n background:\n radial-gradient(circle at 84% 10%, rgba(34, 132, 96, 0.22), transparent 34%),\n linear-gradient(135deg, #fff8e7 0%, #e9f0d8 100%);\n }\n h1 {\n margin: 0 0 8px;\n font-size: 24px;\n letter-spacing: -0.04em;\n }\n p {\n margin: 0 0 16px;\n color: #526044;\n }\n section {\n display: grid;\n gap: 10px;\n }\n .card {\n border: 1px solid rgba(24, 34, 15, 0.16);\n border-radius: 14px;\n padding: 12px;\n background: rgba(255, 255, 255, 0.72);\n box-shadow: 0 12px 30px rgba(56, 72, 36, 0.12);\n }\n .label {\n margin-bottom: 5px;\n color: #7b5b1d;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n code {\n word-break: break-word;\n }\n </style>\n </head>\n <body>\n <main>\n <h1>OpenTray WebView</h1>\n <p>This window was opened by the single primary tray action.</p>\n <section>\n <div class=\"card\">\n <div class=\"label\">primary event</div>\n <code>macOS single primary item direct-triggered menuClick -> WebView show</code>\n </div>\n </section>\n </main>\n </body>\n</html>`;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","#!/usr/bin/env node\nimport { realpathSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { DaemonHealth } from \"@opentray/spec\";\n\nimport { createNodeDaemonDriver, inspectDaemon, restartDaemon, startDaemon, stopDaemon } from \"./daemon/lifecycle\";\nimport { readPackageVersion } from \"./daemon/package-version\";\nimport { resolveDaemonPaths } from \"./daemon/paths\";\nimport { connectLocalBroker } from \"./local-broker\";\nimport { runDaemonLynxSmoke } from \"./smoke/daemon-lynx\";\nimport { runDaemonTraySmoke } from \"./smoke/daemon-tray\";\n\nconst packageJsonUrl = new URL(\"../package.json\", import.meta.url);\nconst cliModulePath = fileURLToPath(import.meta.url);\n\ntype CliCommand =\n | { type: \"daemon\"; action: \"start\" | \"stop\" | \"restart\" | \"health\" }\n | { type: \"smoke\"; name: \"daemon-tray\" }\n | { type: \"smoke\"; name: \"daemon-lynx\"; bundlePath?: string }\n | { type: \"help\" };\n\nexport const parseCliCommand = (argv: string[]): CliCommand => {\n const [group, action, ...rest] = argv;\n if (group !== \"daemon\") {\n if (group === \"smoke\" && action === \"daemon-tray\") {\n return { type: \"smoke\", name: \"daemon-tray\" };\n }\n if (group === \"smoke\" && action === \"daemon-lynx\") {\n const bundlePath = parseSmokeBundlePath(rest);\n return bundlePath === undefined\n ? { type: \"smoke\", name: \"daemon-lynx\" }\n : { type: \"smoke\", name: \"daemon-lynx\", bundlePath };\n }\n return { type: \"help\" };\n }\n if (action === \"start\" || action === \"stop\" || action === \"restart\" || action === \"health\") {\n return { type: \"daemon\", action };\n }\n\n return { type: \"help\" };\n};\n\nexport const runCli = async (argv: string[]): Promise<number> => {\n const command = parseCliCommand(argv);\n const packageVersion = process.env.OPENTRAY_DAEMON_PACKAGE_VERSION ?? (await readPackageVersion(packageJsonUrl));\n const paths = resolveDaemonPaths({\n homeDir: process.env.OPENTRAY_HOME ?? homedir(),\n packageVersion,\n });\n\n if (command.type === \"help\") {\n printHelp();\n return 1;\n }\n\n if (command.type === \"smoke\") {\n if (command.name === \"daemon-tray\") {\n await runDaemonTraySmoke();\n } else {\n await runDaemonLynxSmoke(\n command.bundlePath === undefined ? {} : { bundlePath: command.bundlePath },\n );\n }\n return 0;\n }\n\n const driver = createNodeDaemonDriver(cliModulePath);\n\n if (command.action === \"start\") {\n const result = await startDaemon({ paths, driver });\n console.log(`opentray daemon ${result.status}: pid=${result.pid} endpoint=${result.paths.endpoint}`);\n return 0;\n }\n\n if (command.action === \"stop\") {\n const result = await stopDaemon({ paths, driver });\n console.log(\n result.status === \"stopped\"\n ? `opentray daemon stopped: pid=${result.pid}`\n : \"opentray daemon not running\",\n );\n return 0;\n }\n\n if (command.action === \"restart\") {\n const result = await restartDaemon({ paths, driver });\n console.log(`opentray daemon ${result.status}: pid=${result.pid} endpoint=${result.paths.endpoint}`);\n return 0;\n }\n\n if (command.action === \"health\") {\n const inspected = await inspectDaemon({ paths, driver });\n if (inspected.status === \"not-running\") {\n console.log(\"opentray daemon not running\");\n return 0;\n }\n\n const connection = await connectLocalBroker({\n autoStart: false,\n endpoint: paths.endpoint,\n homeDir: paths.homeDir,\n packageVersion,\n });\n try {\n const response = await connection.request({\n type: \"health\",\n requestId: \"opentray-daemon-health\",\n });\n if (response.type !== \"daemon-health\") {\n throw new Error(`expected daemon-health response, received ${response.type}`);\n }\n console.log(formatDaemonHealthOutput(response.health));\n } finally {\n await connection.close();\n }\n return 0;\n }\n\n printHelp();\n return 1;\n};\n\nconst printHelp = (): void => {\n console.error(\"Usage: opentray daemon <start|stop|restart|health>\");\n console.error(\" opentray smoke daemon-tray\");\n console.error(\" opentray smoke daemon-lynx --bundle <path-to-main.lynx.bundle>\");\n};\n\nexport const formatDaemonHealthOutput = (health: DaemonHealth): string => {\n const lines = [\n \"opentray daemon running\",\n `pid: ${health.pid}`,\n `endpoint: ${health.endpoint}`,\n `packageVersion: ${health.packageVersion}`,\n `protocolVersion: ${health.protocolVersion}`,\n `sessions: ${health.sessionCount}`,\n ];\n\n for (const session of health.sessions) {\n const internalLease = session.internalLeaseId ?? \"(pending)\";\n lines.push(\n `- sessionId=${session.sessionId} initialized=${session.initialized} internalLeaseId=${internalLease}`,\n );\n }\n\n return lines.join(\"\\n\");\n};\n\nexport const isCliEntrypoint = (argvEntryPath: string | undefined, modulePath: string): boolean => {\n if (argvEntryPath === undefined) return false;\n try {\n return realpathSync(argvEntryPath) === realpathSync(modulePath);\n } catch {\n return argvEntryPath === modulePath;\n }\n};\n\nfunction parseSmokeBundlePath(argv: string[]): string | undefined {\n for (let index = 0; index < argv.length; index += 1) {\n if (argv[index] === \"--bundle\") {\n return argv[index + 1];\n }\n }\n return undefined;\n}\n\nif (isCliEntrypoint(process.argv[1], cliModulePath)) {\n runCli(process.argv.slice(2)).then((code) => {\n process.exitCode = code;\n });\n}\n"],"mappings":"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"}
@@ -47,6 +47,15 @@ const createSurfaceHandle = createSpaceHandle;
47
47
  const createTrayHandle = (transport, space, trayId, nextRequestId = createRequestIdFactory("opentray")) => ({
48
48
  space,
49
49
  trayId,
50
+ async getBounds() {
51
+ const requestId = nextRequestId();
52
+ return expectResponse(await transport.request({
53
+ type: "get-tray-bounds",
54
+ requestId,
55
+ spaceId: space.spaceId,
56
+ trayId
57
+ }), requestId, "tray-bounds").bounds;
58
+ },
50
59
  async commandExtension(ext, data) {
51
60
  const requestId = nextRequestId();
52
61
  expectResponse(await transport.request({
@@ -87,6 +96,7 @@ const requestIdOf = (frame) => {
87
96
  case "space-created":
88
97
  case "default-space":
89
98
  case "tray-created":
99
+ case "tray-bounds":
90
100
  case "ack":
91
101
  case "daemon-health": return frame.requestId;
92
102
  case "error": return frame.requestId;
@@ -98,4 +108,4 @@ const requestIdOf = (frame) => {
98
108
  //#endregion
99
109
  export { createTrayHandle as a, createSurfaceHandle as i, createInitFrame as n, createSpaceHandle as r, createClient as t };
100
110
 
101
- //# sourceMappingURL=client-Db-tztnj.mjs.map
111
+ //# sourceMappingURL=client-B_zxQOo5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-B_zxQOo5.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import {\n PROTOCOL_VERSION,\n type ClientFrame,\n type ClientRequestFrame,\n type RequestId,\n type ServerFrame,\n type SpaceOptions,\n type SpaceRef,\n type TrayBoundsResult,\n type TrayId,\n type TrayOptions,\n} from \"@opentray/spec\";\n\nexport interface OpenTrayTransport {\n request(frame: ClientRequestFrame): Promise<ServerFrame>;\n}\n\nexport interface OpenTrayClient {\n createSpace(options: SpaceOptions): Promise<SpaceHandle>;\n resolveDefaultSpace(): Promise<SpaceHandle>;\n /** @deprecated Use `createSpace`. */\n createSurface(options: SpaceOptions): Promise<SpaceHandle>;\n}\n\nexport interface SpaceHandle {\n space: SpaceRef;\n createTray(options: TrayOptions): Promise<TrayHandle>;\n}\n\n/** @deprecated Use `SpaceHandle`. */\nexport type SurfaceHandle = SpaceHandle;\n\nexport interface TrayHandle {\n space: SpaceRef;\n trayId: TrayId;\n getBounds(): Promise<TrayBoundsResult>;\n commandExtension(ext: string, data: unknown): Promise<void>;\n destroy(): Promise<void>;\n}\n\nexport const createInitFrame = (clientVersion: string): ClientFrame => ({\n type: \"init\",\n protocolVersion: PROTOCOL_VERSION,\n clientVersion,\n});\n\nexport interface CreateClientOptions {\n requestIdPrefix?: string;\n}\n\nexport const createClient = (\n transport: OpenTrayTransport,\n options: CreateClientOptions = {},\n): OpenTrayClient => {\n const nextRequestId = createRequestIdFactory(options.requestIdPrefix ?? \"opentray\");\n\n return {\n async createSpace(spaceOptions): Promise<SpaceHandle> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"create-space\",\n requestId,\n ...spaceOptions,\n });\n const space = expectResponse(response, requestId, \"space-created\").space;\n return createSpaceHandle(transport, space, nextRequestId);\n },\n async resolveDefaultSpace(): Promise<SpaceHandle> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"resolve-default-space\",\n requestId,\n });\n const space = expectResponse(response, requestId, \"default-space\").space;\n return createSpaceHandle(transport, space, nextRequestId);\n },\n async createSurface(spaceOptions): Promise<SpaceHandle> {\n return this.createSpace(spaceOptions);\n },\n };\n};\n\nexport const createSpaceHandle = (\n transport: OpenTrayTransport,\n space: SpaceRef,\n nextRequestId: () => RequestId = createRequestIdFactory(\"opentray\"),\n): SpaceHandle => ({\n space,\n async createTray(options: TrayOptions): Promise<TrayHandle> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"create-tray\",\n requestId,\n space,\n tray: options,\n });\n const frame = expectResponse(response, requestId, \"tray-created\");\n return createTrayHandle(transport, space, frame.trayId, nextRequestId);\n },\n});\n\n/** @deprecated Use `createSpaceHandle`. */\nexport const createSurfaceHandle = createSpaceHandle;\n\nexport const createTrayHandle = (\n transport: OpenTrayTransport,\n space: SpaceRef,\n trayId: TrayId,\n nextRequestId: () => RequestId = createRequestIdFactory(\"opentray\"),\n): TrayHandle => ({\n space,\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 spaceId: space.spaceId,\n trayId,\n });\n return expectResponse(response, requestId, \"tray-bounds\").bounds;\n },\n async commandExtension(ext: string, data: unknown): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"ext-command\",\n requestId,\n spaceId: space.spaceId,\n trayId,\n ext,\n data,\n });\n expectResponse(response, requestId, \"ack\");\n },\n async destroy(): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"destroy-tray\",\n requestId,\n spaceId: space.spaceId,\n trayId,\n });\n expectResponse(response, requestId, \"ack\");\n },\n});\n\nconst createRequestIdFactory = (prefix: string): (() => RequestId) => {\n let next = 1;\n return () => {\n const requestId = `${prefix}-${next}`;\n next += 1;\n return requestId;\n };\n};\n\nconst expectResponse = <TType extends ServerFrame[\"type\"]>(\n frame: ServerFrame,\n requestId: RequestId,\n type: TType,\n): Extract<ServerFrame, { type: TType }> => {\n if (frame.type === \"error\") {\n throw new Error(`${frame.code}: ${frame.message}`);\n }\n if (frame.type !== type) {\n throw new Error(`expected ${type} for ${requestId}, received ${frame.type}`);\n }\n if (requestIdOf(frame) !== requestId) {\n throw new Error(`expected response for ${requestId}, received ${requestIdOf(frame) ?? \"none\"}`);\n }\n return frame as Extract<ServerFrame, { type: TType }>;\n};\n\nconst requestIdOf = (frame: ServerFrame): RequestId | undefined => {\n switch (frame.type) {\n case \"space-created\":\n case \"default-space\":\n case \"tray-created\":\n case \"tray-bounds\":\n case \"ack\":\n case \"daemon-health\":\n return frame.requestId;\n case \"error\":\n return frame.requestId;\n case \"ready\":\n case \"event\":\n case \"ext-event\":\n return undefined;\n }\n};\n"],"mappings":";;AAwCA,MAAa,mBAAmB,mBAAwC;CACtE,MAAM;CACN,iBAAiB;CACjB;AACF;AAMA,MAAa,gBACX,WACA,UAA+B,CAAC,MACb;CACnB,MAAM,gBAAgB,uBAAuB,QAAQ,mBAAmB,UAAU;CAElF,OAAO;EACL,MAAM,YAAY,cAAoC;GACpD,MAAM,YAAY,cAAc;GAMhC,MAAM,QAAQ,eAAe,MALN,UAAU,QAAQ;IACvC,MAAM;IACN;IACA,GAAG;GACL,CAAC,GACsC,WAAW,eAAe,EAAE;GACnE,OAAO,kBAAkB,WAAW,OAAO,aAAa;EAC1D;EACA,MAAM,sBAA4C;GAChD,MAAM,YAAY,cAAc;GAKhC,MAAM,QAAQ,eAAe,MAJN,UAAU,QAAQ;IACvC,MAAM;IACN;GACF,CAAC,GACsC,WAAW,eAAe,EAAE;GACnE,OAAO,kBAAkB,WAAW,OAAO,aAAa;EAC1D;EACA,MAAM,cAAc,cAAoC;GACtD,OAAO,KAAK,YAAY,YAAY;EACtC;CACF;AACF;AAEA,MAAa,qBACX,WACA,OACA,gBAAiC,uBAAuB,UAAU,OACjD;CACjB;CACA,MAAM,WAAW,SAA2C;EAC1D,MAAM,YAAY,cAAc;EAQhC,OAAO,iBAAiB,WAAW,OADrB,eAAe,MANN,UAAU,QAAQ;GACvC,MAAM;GACN;GACA;GACA,MAAM;EACR,CAAC,GACsC,WAAW,cACJ,EAAE,QAAQ,aAAa;CACvE;AACF;;AAGA,MAAa,sBAAsB;AAEnC,MAAa,oBACX,WACA,OACA,QACA,gBAAiC,uBAAuB,UAAU,OAClD;CAChB;CACA;CACA,MAAM,YAAuC;EAC3C,MAAM,YAAY,cAAc;EAOhC,OAAO,eAAe,MANC,UAAU,QAAQ;GACvC,MAAM;GACN;GACA,SAAS,MAAM;GACf;EACF,CAAC,GAC+B,WAAW,aAAa,EAAE;CAC5D;CACA,MAAM,iBAAiB,KAAa,MAA8B;EAChE,MAAM,YAAY,cAAc;EAShC,eAAe,MARQ,UAAU,QAAQ;GACvC,MAAM;GACN;GACA,SAAS,MAAM;GACf;GACA;GACA;EACF,CAAC,GACwB,WAAW,KAAK;CAC3C;CACA,MAAM,UAAyB;EAC7B,MAAM,YAAY,cAAc;EAOhC,eAAe,MANQ,UAAU,QAAQ;GACvC,MAAM;GACN;GACA,SAAS,MAAM;GACf;EACF,CAAC,GACwB,WAAW,KAAK;CAC3C;AACF;AAEA,MAAM,0BAA0B,WAAsC;CACpE,IAAI,OAAO;CACX,aAAa;EACX,MAAM,YAAY,GAAG,OAAO,GAAG;EAC/B,QAAQ;EACR,OAAO;CACT;AACF;AAEA,MAAM,kBACJ,OACA,WACA,SAC0C;CAC1C,IAAI,MAAM,SAAS,SACjB,MAAM,IAAI,MAAM,GAAG,MAAM,KAAK,IAAI,MAAM,SAAS;CAEnD,IAAI,MAAM,SAAS,MACjB,MAAM,IAAI,MAAM,YAAY,KAAK,OAAO,UAAU,aAAa,MAAM,MAAM;CAE7E,IAAI,YAAY,KAAK,MAAM,WACzB,MAAM,IAAI,MAAM,yBAAyB,UAAU,aAAa,YAAY,KAAK,KAAK,QAAQ;CAEhG,OAAO;AACT;AAEA,MAAM,eAAe,UAA8C;CACjE,QAAQ,MAAM,MAAd;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,iBACH,OAAO,MAAM;EACf,KAAK,SACH,OAAO,MAAM;EACf,KAAK;EACL,KAAK;EACL,KAAK,aACH;CACJ;AACF"}
@@ -1,4 +1,4 @@
1
- import { ClientFrame, ClientRequestFrame, RequestId, ServerFrame, SpaceOptions, SpaceRef, TrayId, TrayOptions } from "@opentray/spec";
1
+ import { ClientFrame, ClientRequestFrame, RequestId, ServerFrame, SpaceOptions, SpaceRef, TrayBoundsResult, TrayId, TrayOptions } from "@opentray/spec";
2
2
 
3
3
  //#region src/client.d.ts
4
4
  interface OpenTrayTransport {
@@ -19,6 +19,7 @@ type SurfaceHandle = SpaceHandle;
19
19
  interface TrayHandle {
20
20
  space: SpaceRef;
21
21
  trayId: TrayId;
22
+ getBounds(): Promise<TrayBoundsResult>;
22
23
  commandExtension(ext: string, data: unknown): Promise<void>;
23
24
  destroy(): Promise<void>;
24
25
  }
@@ -33,4 +34,4 @@ declare const createSurfaceHandle: (transport: OpenTrayTransport, space: SpaceRe
33
34
  declare const createTrayHandle: (transport: OpenTrayTransport, space: SpaceRef, trayId: TrayId, nextRequestId?: () => RequestId) => TrayHandle;
34
35
  //#endregion
35
36
  export { SurfaceHandle as a, createInitFrame as c, createTrayHandle as d, SpaceHandle as i, createSpaceHandle as l, OpenTrayClient as n, TrayHandle as o, OpenTrayTransport as r, createClient as s, CreateClientOptions as t, createSurfaceHandle as u };
36
- //# sourceMappingURL=client-C_zT7PEt.d.mts.map
37
+ //# sourceMappingURL=client-h6gZdeOk.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-h6gZdeOk.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;;UAaiB,iBAAA;EACf,OAAA,CAAQ,KAAA,EAAO,kBAAA,GAAqB,OAAA,CAAQ,WAAA;AAAA;AAAA,UAG7B,cAAA;EACf,WAAA,CAAY,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,WAAA;EAC5C,mBAAA,IAAuB,OAAA,CAAQ,WAAA;EALa;EAO5C,aAAA,CAAc,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,WAAA;AAAA;AAAA,UAG/B,WAAA;EACf,KAAA,EAAO,QAAA;EACP,UAAA,CAAW,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,UAAA;AAAA;;KAIhC,aAAA,GAAgB,WAAW;AAAA,UAEtB,UAAA;EACf,KAAA,EAAO,QAAA;EACP,MAAA,EAAQ,MAAA;EACR,SAAA,IAAa,OAAA,CAAQ,gBAAA;EACrB,gBAAA,CAAiB,GAAA,UAAa,IAAA,YAAgB,OAAA;EAC9C,OAAA,IAAW,OAAA;AAAA;AAAA,cAGA,eAAA,GAAmB,aAAA,aAAwB,WAItD;AAAA,UAEe,mBAAA;EACf,eAAe;AAAA;AAAA,cAGJ,YAAA,GACX,SAAA,EAAW,iBAAA,EACX,OAAA,GAAS,mBAAA,KACR,cAAA;AAAA,cA6BU,iBAAA,GACX,SAAA,EAAW,iBAAA,EACX,KAAA,EAAO,QAAA,EACP,aAAA,SAAqB,SAAA,KACpB,WAAA;;cAgBU,mBAAA,GAAmB,SAAA,EAnBnB,iBAAA,EAAiB,KAAA,EACrB,QAAA,EAAQ,aAAA,SACM,SAAA,KACpB,WAAA;AAAA,cAkBU,gBAAA,GACX,SAAA,EAAW,iBAAA,EACX,KAAA,EAAO,QAAA,EACP,MAAA,EAAQ,MAAA,EACR,aAAA,SAAqB,SAAA,KACpB,UAAA"}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as SurfaceHandle, c as createInitFrame, d as createTrayHandle, i as SpaceHandle, l as createSpaceHandle, n as OpenTrayClient, o as TrayHandle, r as OpenTrayTransport, s as createClient, t as CreateClientOptions, u as createSurfaceHandle } from "./client-C_zT7PEt.mjs";
1
+ import { a as SurfaceHandle, c as createInitFrame, d as createTrayHandle, i as SpaceHandle, l as createSpaceHandle, n as OpenTrayClient, o as TrayHandle, r as OpenTrayTransport, s as createClient, t as CreateClientOptions, u as createSurfaceHandle } from "./client-h6gZdeOk.mjs";
2
2
  import { BrokerEndpointIdentity, BrokerEndpointIdentityOptions, PROTOCOL_VERSION, SpaceOptions, SpaceRef, TrayOptions, createBrokerEndpointIdentity, formatBrokerEndpointName, formatBrokerStateRoot, formatUnixSocketPath, formatWindowsPipeName, isSupportedProtocolVersion } from "@opentray/spec";
3
3
 
4
4
  //#region src/sdk.d.ts
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { a as createTrayHandle, i as createSurfaceHandle, n as createInitFrame, r as createSpaceHandle, t as createClient } from "./client-Db-tztnj.mjs";
2
- import { t as connectLocalBroker } from "./local-broker-mQmeVLF5.mjs";
1
+ import { a as createTrayHandle, i as createSurfaceHandle, n as createInitFrame, r as createSpaceHandle, t as createClient } from "./client-B_zxQOo5.mjs";
2
+ import { t as connectLocalBroker } from "./local-broker-CEIHPBcK.mjs";
3
3
  import { PROTOCOL_VERSION, createBrokerEndpointIdentity, formatBrokerEndpointName, formatBrokerStateRoot, formatUnixSocketPath, formatWindowsPipeName, isSupportedProtocolVersion } from "@opentray/spec";
4
4
  //#region src/sdk.ts
5
5
  const createSpace = async (options, brokerOptions = {}) => {
@@ -52,24 +52,17 @@ const requireFromSource = createRequire(sourceUrl);
52
52
  const resolveBrokerCommand = async (paths, options = {}) => {
53
53
  const explicit = (options.env ?? process.env).OPENTRAY_BROKER_BIN;
54
54
  if (explicit !== void 0 && explicit.length > 0) return commandForBinary(explicit, paths);
55
- const platform = options.platform ?? process.platform;
56
- const arch = options.arch ?? process.arch;
57
- let installedBinaryError;
58
- try {
59
- const installedBinary = await resolveInstalledBrokerBinary({
60
- platform,
61
- arch,
62
- resolvePackageJson: options.resolvePackageJson ?? resolvePackageJson
63
- });
64
- if (installedBinary !== void 0) return commandForBinary(installedBinary, paths);
65
- } catch (error) {
66
- if (!(error instanceof MissingPlatformBrokerBinaryError)) throw error;
67
- installedBinaryError = error;
68
- }
69
55
  const sourceDir = options.sourceDir ?? dirname(fileURLToPath(sourceUrl));
70
56
  const workspaceRoot = await (options.findWorkspaceRoot ?? findWorkspaceRoot)(sourceDir);
71
57
  if (workspaceRoot !== void 0) return commandForBinary(await (options.ensureDevBrokerBinary ?? ensureDevBrokerBinary)(workspaceRoot), paths, workspaceRoot);
72
- if (installedBinaryError !== void 0) throw installedBinaryError;
58
+ const platform = options.platform ?? process.platform;
59
+ const arch = options.arch ?? process.arch;
60
+ const installedBinary = await resolveInstalledBrokerBinary({
61
+ platform,
62
+ arch,
63
+ resolvePackageJson: options.resolvePackageJson ?? resolvePackageJson
64
+ });
65
+ if (installedBinary !== void 0) return commandForBinary(installedBinary, paths);
73
66
  const target = resolveBrokerNativeTarget(platform, arch);
74
67
  throw new MissingPlatformBrokerBinaryError(`unable to resolve OpenTray broker binary for ${platform}/${arch}; install ${target.packageName} or set OPENTRAY_BROKER_BIN`, platform, arch, { packageName: target.packageName });
75
68
  };
@@ -156,6 +149,7 @@ const exists = async (path) => {
156
149
  const isNodeError$1 = (error) => error instanceof Error && "code" in error;
157
150
  //#endregion
158
151
  //#region src/daemon/lifecycle.ts
152
+ const DAEMON_STDIO_ENV = "OPENTRAY_DAEMON_STDIO";
159
153
  const createNodeDaemonDriver = (cliEntrypoint) => ({
160
154
  async isAlive(pid) {
161
155
  return isProcessAlive(pid);
@@ -171,7 +165,7 @@ const createNodeDaemonDriver = (cliEntrypoint) => ({
171
165
  OPENTRAY_DAEMON_PACKAGE_VERSION: paths.packageVersion,
172
166
  OPENTRAY_DAEMON_CLI_ENTRYPOINT: cliEntrypoint
173
167
  },
174
- stdio: "ignore"
168
+ stdio: resolveBrokerStdio(process.env[DAEMON_STDIO_ENV])
175
169
  });
176
170
  child.unref();
177
171
  return child.pid ?? 0;
@@ -180,6 +174,9 @@ const createNodeDaemonDriver = (cliEntrypoint) => ({
180
174
  process.kill(pid, "SIGTERM");
181
175
  }
182
176
  });
177
+ const resolveBrokerStdio = (value) => {
178
+ return value === "inherit" ? "inherit" : "ignore";
179
+ };
183
180
  const startDaemon = async ({ paths, driver, lockTimeoutMs = 1e3 }) => {
184
181
  await mkdir(paths.runtimeDir, { recursive: true });
185
182
  const lock = await acquireLock(paths.lockFile, lockTimeoutMs);
@@ -488,6 +485,7 @@ const responseRequestId = (frame) => {
488
485
  case "space-created":
489
486
  case "default-space":
490
487
  case "tray-created":
488
+ case "tray-bounds":
491
489
  case "ack":
492
490
  case "daemon-health": return frame.requestId;
493
491
  case "ready":
@@ -503,4 +501,4 @@ const resolveCliEntrypoint = () => {
503
501
  //#endregion
504
502
  export { inspectDaemon as a, stopDaemon as c, createNodeDaemonDriver as i, resolveDaemonPaths as n, restartDaemon as o, readPackageVersion as r, startDaemon as s, connectLocalBroker as t };
505
503
 
506
- //# sourceMappingURL=local-broker-mQmeVLF5.mjs.map
504
+ //# sourceMappingURL=local-broker-CEIHPBcK.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-broker-CEIHPBcK.mjs","names":["isNodeError"],"sources":["../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"],"sourcesContent":["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, chmod } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawn } from \"node:child_process\";\n\nimport type { DaemonPaths } from \"./paths\";\nimport { MissingPlatformBrokerBinaryError, resolveBrokerNativeTarget } from \"./native-target\";\n\nexport interface BrokerCommand {\n command: string;\n args: string[];\n cwd?: string;\n}\n\nconst sourceUrl = import.meta.url;\nconst requireFromSource = createRequire(sourceUrl);\n\nexport interface ResolveBrokerCommandOptions {\n env?: NodeJS.ProcessEnv;\n platform?: NodeJS.Platform;\n arch?: string;\n sourceDir?: string;\n resolvePackageJson?: (packageName: string) => string | undefined;\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)(sourceDir);\n if (workspaceRoot !== undefined) {\n const binary = await (options.ensureDevBrokerBinary ?? ensureDevBrokerBinary)(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 installedBinary = await resolveInstalledBrokerBinary({\n platform,\n arch,\n resolvePackageJson: options.resolvePackageJson ?? resolvePackageJson,\n });\n if (installedBinary !== undefined) {\n return commandForBinary(installedBinary, paths);\n }\n\n const target = resolveBrokerNativeTarget(platform, arch);\n throw new MissingPlatformBrokerBinaryError(\n `unable to resolve OpenTray broker binary for ${platform}/${arch}; install ${target.packageName} or set OPENTRAY_BROKER_BIN`,\n platform,\n arch,\n { packageName: target.packageName },\n );\n};\n\nconst commandForBinary = (binary: string, paths: DaemonPaths, cwd?: string): 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 ],\n };\n if (cwd !== undefined) {\n command.cwd = cwd;\n }\n return command;\n};\n\ninterface ResolveInstalledBrokerBinaryOptions {\n platform: NodeJS.Platform;\n arch: string;\n resolvePackageJson: (packageName: string) => string | undefined;\n}\n\nexport const resolveInstalledBrokerBinary = async ({\n platform,\n arch,\n resolvePackageJson,\n}: ResolveInstalledBrokerBinaryOptions): Promise<string | undefined> => {\n const target = resolveBrokerNativeTarget(platform, arch);\n const packageJsonPath = resolvePackageJson(target.packageName);\n if (packageJsonPath === undefined) {\n return undefined;\n }\n\n const binary = join(dirname(packageJsonPath), target.binaryRelativePath);\n if (!(await exists(binary))) {\n throw new MissingPlatformBrokerBinaryError(\n `OpenTray broker package ${target.packageName} is installed but missing ${target.binaryRelativePath}`,\n platform,\n arch,\n {\n packageName: target.packageName,\n binaryPath: binary,\n },\n );\n }\n\n if (platform !== \"win32\") {\n await chmod(binary, 0o755);\n }\n\n return binary;\n};\n\nconst resolvePackageJson = (packageName: string): 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 findWorkspaceRoot = async (start: string): 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\nconst ensureDevBrokerBinary = async (workspaceRoot: string): Promise<string> => {\n const binary = join(\n workspaceRoot,\n \"target\",\n \"debug\",\n process.platform === \"win32\" ? \"opentray.exe\" : \"opentray\",\n );\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: 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(new Error(`cargo build -p opentray-bin failed with code ${code ?? \"unknown\"}`));\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","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 = (cliEntrypoint: string): DaemonDriver => ({\n async isAlive(pid) {\n return isProcessAlive(pid);\n },\n async spawnBroker(paths) {\n const broker = await resolveBrokerCommand(paths);\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 },\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);\n return { status: \"started\", pid, paths };\n } finally {\n await lock.release();\n }\n};\n\nexport const stopDaemon = async ({ paths, driver }: 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 (options: StartDaemonOptions): Promise<DaemonStartResult> => {\n await stopDaemon({ paths: options.paths, driver: options.driver });\n return startDaemon(options);\n};\n\nexport const inspectDaemon = async ({ paths, driver }: 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 (driver: DaemonDriver, pid: number): 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 (readyFile: string): 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 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(lockFile, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY);\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> => 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 { createBrokerEndpointIdentity, formatUnixSocketPath, formatWindowsPipeName } from \"@opentray/spec\";\n\nexport interface DaemonPathOptions {\n homeDir: string;\n packageVersion: string;\n platform?: NodeJS.Platform;\n}\n\nexport interface DaemonPaths {\n homeDir: string;\n packageVersion: 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 platform = process.platform,\n}: DaemonPathOptions): DaemonPaths => {\n const identity = createBrokerEndpointIdentity({ packageVersion });\n const normalizedHome = homeDir.replace(/[\\\\/]+$/u, \"\");\n const stateRoot = `${normalizedHome}/.opentray/${identity.packageVersion}`;\n const runtimeDir = `${stateRoot}/runtime`;\n const endpoint =\n platform === \"win32\" ? formatWindowsPipeName(identity) : formatUnixSocketPath(normalizedHome, identity);\n\n return {\n homeDir: normalizedHome,\n packageVersion: identity.packageVersion,\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","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 { createNodeDaemonDriver, startDaemon, type DaemonDriver } 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 BrokerEventFrame = Extract<ServerFrame, { type: \"event\" | \"ext-event\" }>;\n\nexport interface LocalBrokerClient extends OpenTrayTransport {\n readonly endpoint: string;\n readonly sessionId: string;\n /** @deprecated Use `sessionId`. */\n readonly leaseId: string;\n onEvent(listener: (frame: BrokerEventFrame) => void): () => void;\n close(): Promise<void>;\n}\n\nexport interface ConnectLocalBrokerOptions extends Partial<BrokerEndpointIdentityOptions> {\n endpoint?: string;\n homeDir?: string;\n clientVersion?: string;\n autoStart?: boolean;\n daemonDriver?: DaemonDriver;\n cliEntrypoint?: 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 = options.packageVersion ?? (await readPackageVersion(packageJsonUrl));\n const clientVersion = options.clientVersion ?? packageVersion;\n const paths = resolveDaemonPaths({\n homeDir: options.homeDir ?? process.env.OPENTRAY_HOME ?? homedir(),\n packageVersion,\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(\"local broker autoStart requires the derived same-version endpoint\");\n }\n if (autoStart) {\n const cliEntrypoint = options.cliEntrypoint ?? resolveCliEntrypoint();\n const driver = options.daemonDriver ?? createNodeDaemonDriver(cliEntrypoint);\n await startDaemon({ paths, driver });\n }\n const socket = await connectSocket(endpoint);\n const connection = new LocalBrokerConnection(socket, endpoint);\n\n await connection.init(clientVersion, options.protocolVersion ?? PROTOCOL_VERSION);\n return connection;\n};\n\nclass LocalBrokerConnection implements LocalBrokerClient {\n readonly endpoint: string;\n sessionId = \"\";\n /** @deprecated Use `sessionId`. */\n leaseId = \"\";\n\n private buffer = \"\";\n private readonly listeners = new Set<(frame: BrokerEventFrame) => void>();\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 ) {\n this.endpoint = endpoint;\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\" }>>((resolve, reject) => {\n this.ready = { resolve, reject };\n });\n this.write({\n type: \"init\",\n protocolVersion,\n clientVersion,\n });\n const frame = await ready;\n this.sessionId = frame.sessionId;\n this.leaseId = 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: BrokerEventFrame) => 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(new Error(parsed.error ?? \"invalid server frame\"));\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 this.ready?.reject(error);\n this.ready = undefined;\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 \"space-created\":\n case \"default-space\":\n case \"tray-created\":\n case \"tray-bounds\":\n case \"ack\":\n case \"daemon-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\") ? \"cli.ts\" : \"cli.mjs\";\n return fileURLToPath(new URL(`./${suffix}`, import.meta.url));\n};\n"],"mappings":"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"}
package/dist/node.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { r as OpenTrayTransport } from "./client-C_zT7PEt.mjs";
1
+ import { r as OpenTrayTransport } from "./client-h6gZdeOk.mjs";
2
2
  import { BrokerEndpointIdentityOptions, ServerFrame } from "@opentray/spec";
3
3
 
4
4
  //#region src/daemon/paths.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.mts","names":[],"sources":["../src/daemon/paths.ts","../src/daemon/lifecycle.ts","../src/local-broker.ts"],"mappings":";;;;UAQiB,WAAA;EACf,OAAA;EACA,cAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;AAAA;;;UCEe,YAAA;EACf,OAAA,CAAQ,GAAA,WAAc,OAAA;EACtB,WAAA,CAAY,KAAA,EAAO,WAAA,GAAc,OAAA;EACjC,IAAA,CAAK,GAAA,WAAc,OAAA;AAAA;;;KCFT,gBAAA,GAAmB,OAAO,CAAC,WAAA;EAAe,IAAA;AAAA;AAAA,UAErC,iBAAA,SAA0B,iBAAA;EAAA,SAChC,QAAA;EAAA,SACA,SAAA;EFdT;EAAA,SEgBS,OAAA;EACT,OAAA,CAAQ,QAAA,GAAW,KAAA,EAAO,gBAAA;EAC1B,KAAA,IAAS,OAAA;AAAA;AAAA,UAGM,yBAAA,SAAkC,OAAA,CAAQ,6BAAA;EACzD,QAAA;EACA,OAAA;EACA,aAAA;EACA,SAAA;EACA,YAAA,GAAe,YAAA;EACf,aAAA;AAAA;AAAA,cAoBW,kBAAA,GACX,OAAA,GAAS,yBAAA,KACR,OAAA,CAAQ,iBAAA"}
1
+ {"version":3,"file":"node.d.mts","names":[],"sources":["../src/daemon/paths.ts","../src/daemon/lifecycle.ts","../src/local-broker.ts"],"mappings":";;;;UAQiB,WAAA;EACf,OAAA;EACA,cAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;AAAA;;;UCIe,YAAA;EACf,OAAA,CAAQ,GAAA,WAAc,OAAA;EACtB,WAAA,CAAY,KAAA,EAAO,WAAA,GAAc,OAAA;EACjC,IAAA,CAAK,GAAA,WAAc,OAAA;AAAA;;;KCJT,gBAAA,GAAmB,OAAO,CAAC,WAAA;EAAe,IAAA;AAAA;AAAA,UAErC,iBAAA,SAA0B,iBAAA;EAAA,SAChC,QAAA;EAAA,SACA,SAAA;EFdT;EAAA,SEgBS,OAAA;EACT,OAAA,CAAQ,QAAA,GAAW,KAAA,EAAO,gBAAA;EAC1B,KAAA,IAAS,OAAA;AAAA;AAAA,UAGM,yBAAA,SAAkC,OAAA,CAAQ,6BAAA;EACzD,QAAA;EACA,OAAA;EACA,aAAA;EACA,SAAA;EACA,YAAA,GAAe,YAAA;EACf,aAAA;AAAA;AAAA,cAoBW,kBAAA,GACX,OAAA,GAAS,yBAAA,KACR,OAAA,CAAQ,iBAAA"}
package/dist/node.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as connectLocalBroker } from "./local-broker-mQmeVLF5.mjs";
1
+ import { t as connectLocalBroker } from "./local-broker-CEIHPBcK.mjs";
2
2
  export { connectLocalBroker };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opentray",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Developer-facing OpenTray SDK and CLI package.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "sideEffects": false,
30
30
  "dependencies": {
31
- "@opentray/spec": "0.3.0"
31
+ "@opentray/spec": "0.4.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "tsdown": "^0.22.1",
@@ -36,12 +36,12 @@
36
36
  "vitest": "^4.1.7"
37
37
  },
38
38
  "optionalDependencies": {
39
- "@opentray/darwin-arm64": "0.4.0",
40
- "@opentray/darwin-x64": "0.4.0",
41
- "@opentray/linux-arm64": "0.4.0",
42
- "@opentray/linux-x64": "0.4.0",
43
- "@opentray/windows-arm64": "0.4.0",
44
- "@opentray/windows-x64": "0.4.0"
39
+ "@opentray/darwin-arm64": "0.5.0",
40
+ "@opentray/darwin-x64": "0.5.0",
41
+ "@opentray/linux-arm64": "0.5.0",
42
+ "@opentray/linux-x64": "0.5.0",
43
+ "@opentray/windows-arm64": "0.5.0",
44
+ "@opentray/windows-x64": "0.5.0"
45
45
  },
46
46
  "scripts": {
47
47
  "build": "tsdown src/index.ts src/node.ts src/cli.ts --format esm --dts",
@@ -49,6 +49,8 @@
49
49
  "example:basic": "bun run examples/basic-space.ts",
50
50
  "example:daemon-lynx": "bun run examples/daemon-lynx.ts",
51
51
  "example:daemon-tray": "bun run examples/daemon-tray.ts",
52
+ "example:tray-panel": "bun run examples/tray-panel.ts",
53
+ "example:webview-control": "bun run examples/webview-control.ts",
52
54
  "test": "vitest run",
53
55
  "typecheck": "tsc --noEmit"
54
56
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-C_zT7PEt.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;;UAYiB,iBAAA;EACf,OAAA,CAAQ,KAAA,EAAO,kBAAA,GAAqB,OAAA,CAAQ,WAAA;AAAA;AAAA,UAG7B,cAAA;EACf,WAAA,CAAY,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,WAAA;EAC5C,mBAAA,IAAuB,OAAA,CAAQ,WAAA;EALa;EAO5C,aAAA,CAAc,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,WAAA;AAAA;AAAA,UAG/B,WAAA;EACf,KAAA,EAAO,QAAA;EACP,UAAA,CAAW,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,UAAA;AAAA;;KAIhC,aAAA,GAAgB,WAAW;AAAA,UAEtB,UAAA;EACf,KAAA,EAAO,QAAA;EACP,MAAA,EAAQ,MAAA;EACR,gBAAA,CAAiB,GAAA,UAAa,IAAA,YAAgB,OAAA;EAC9C,OAAA,IAAW,OAAA;AAAA;AAAA,cAGA,eAAA,GAAmB,aAAA,aAAwB,WAItD;AAAA,UAEe,mBAAA;EACf,eAAe;AAAA;AAAA,cAGJ,YAAA,GACX,SAAA,EAAW,iBAAA,EACX,OAAA,GAAS,mBAAA,KACR,cAAA;AAAA,cA6BU,iBAAA,GACX,SAAA,EAAW,iBAAA,EACX,KAAA,EAAO,QAAA,EACP,aAAA,SAAqB,SAAA,KACpB,WAAA;;cAgBU,mBAAA,GAAmB,SAAA,EAnBnB,iBAAA,EAAiB,KAAA,EACrB,QAAA,EAAQ,aAAA,SACM,SAAA,KACpB,WAAA;AAAA,cAkBU,gBAAA,GACX,SAAA,EAAW,iBAAA,EACX,KAAA,EAAO,QAAA,EACP,MAAA,EAAQ,MAAA,EACR,aAAA,SAAqB,SAAA,KACpB,UAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-Db-tztnj.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import {\n PROTOCOL_VERSION,\n type ClientFrame,\n type ClientRequestFrame,\n type RequestId,\n type ServerFrame,\n type SpaceOptions,\n type SpaceRef,\n type TrayId,\n type TrayOptions,\n} from \"@opentray/spec\";\n\nexport interface OpenTrayTransport {\n request(frame: ClientRequestFrame): Promise<ServerFrame>;\n}\n\nexport interface OpenTrayClient {\n createSpace(options: SpaceOptions): Promise<SpaceHandle>;\n resolveDefaultSpace(): Promise<SpaceHandle>;\n /** @deprecated Use `createSpace`. */\n createSurface(options: SpaceOptions): Promise<SpaceHandle>;\n}\n\nexport interface SpaceHandle {\n space: SpaceRef;\n createTray(options: TrayOptions): Promise<TrayHandle>;\n}\n\n/** @deprecated Use `SpaceHandle`. */\nexport type SurfaceHandle = SpaceHandle;\n\nexport interface TrayHandle {\n space: SpaceRef;\n trayId: TrayId;\n commandExtension(ext: string, data: unknown): Promise<void>;\n destroy(): Promise<void>;\n}\n\nexport const createInitFrame = (clientVersion: string): ClientFrame => ({\n type: \"init\",\n protocolVersion: PROTOCOL_VERSION,\n clientVersion,\n});\n\nexport interface CreateClientOptions {\n requestIdPrefix?: string;\n}\n\nexport const createClient = (\n transport: OpenTrayTransport,\n options: CreateClientOptions = {},\n): OpenTrayClient => {\n const nextRequestId = createRequestIdFactory(options.requestIdPrefix ?? \"opentray\");\n\n return {\n async createSpace(spaceOptions): Promise<SpaceHandle> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"create-space\",\n requestId,\n ...spaceOptions,\n });\n const space = expectResponse(response, requestId, \"space-created\").space;\n return createSpaceHandle(transport, space, nextRequestId);\n },\n async resolveDefaultSpace(): Promise<SpaceHandle> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"resolve-default-space\",\n requestId,\n });\n const space = expectResponse(response, requestId, \"default-space\").space;\n return createSpaceHandle(transport, space, nextRequestId);\n },\n async createSurface(spaceOptions): Promise<SpaceHandle> {\n return this.createSpace(spaceOptions);\n },\n };\n};\n\nexport const createSpaceHandle = (\n transport: OpenTrayTransport,\n space: SpaceRef,\n nextRequestId: () => RequestId = createRequestIdFactory(\"opentray\"),\n): SpaceHandle => ({\n space,\n async createTray(options: TrayOptions): Promise<TrayHandle> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"create-tray\",\n requestId,\n space,\n tray: options,\n });\n const frame = expectResponse(response, requestId, \"tray-created\");\n return createTrayHandle(transport, space, frame.trayId, nextRequestId);\n },\n});\n\n/** @deprecated Use `createSpaceHandle`. */\nexport const createSurfaceHandle = createSpaceHandle;\n\nexport const createTrayHandle = (\n transport: OpenTrayTransport,\n space: SpaceRef,\n trayId: TrayId,\n nextRequestId: () => RequestId = createRequestIdFactory(\"opentray\"),\n): TrayHandle => ({\n space,\n trayId,\n async commandExtension(ext: string, data: unknown): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"ext-command\",\n requestId,\n spaceId: space.spaceId,\n trayId,\n ext,\n data,\n });\n expectResponse(response, requestId, \"ack\");\n },\n async destroy(): Promise<void> {\n const requestId = nextRequestId();\n const response = await transport.request({\n type: \"destroy-tray\",\n requestId,\n spaceId: space.spaceId,\n trayId,\n });\n expectResponse(response, requestId, \"ack\");\n },\n});\n\nconst createRequestIdFactory = (prefix: string): (() => RequestId) => {\n let next = 1;\n return () => {\n const requestId = `${prefix}-${next}`;\n next += 1;\n return requestId;\n };\n};\n\nconst expectResponse = <TType extends ServerFrame[\"type\"]>(\n frame: ServerFrame,\n requestId: RequestId,\n type: TType,\n): Extract<ServerFrame, { type: TType }> => {\n if (frame.type === \"error\") {\n throw new Error(`${frame.code}: ${frame.message}`);\n }\n if (frame.type !== type) {\n throw new Error(`expected ${type} for ${requestId}, received ${frame.type}`);\n }\n if (requestIdOf(frame) !== requestId) {\n throw new Error(`expected response for ${requestId}, received ${requestIdOf(frame) ?? \"none\"}`);\n }\n return frame as Extract<ServerFrame, { type: TType }>;\n};\n\nconst requestIdOf = (frame: ServerFrame): RequestId | undefined => {\n switch (frame.type) {\n case \"space-created\":\n case \"default-space\":\n case \"tray-created\":\n case \"ack\":\n case \"daemon-health\":\n return frame.requestId;\n case \"error\":\n return frame.requestId;\n case \"ready\":\n case \"event\":\n case \"ext-event\":\n return undefined;\n }\n};\n"],"mappings":";;AAsCA,MAAa,mBAAmB,mBAAwC;CACtE,MAAM;CACN,iBAAiB;CACjB;AACF;AAMA,MAAa,gBACX,WACA,UAA+B,CAAC,MACb;CACnB,MAAM,gBAAgB,uBAAuB,QAAQ,mBAAmB,UAAU;CAElF,OAAO;EACL,MAAM,YAAY,cAAoC;GACpD,MAAM,YAAY,cAAc;GAMhC,MAAM,QAAQ,eAAe,MALN,UAAU,QAAQ;IACvC,MAAM;IACN;IACA,GAAG;GACL,CAAC,GACsC,WAAW,eAAe,EAAE;GACnE,OAAO,kBAAkB,WAAW,OAAO,aAAa;EAC1D;EACA,MAAM,sBAA4C;GAChD,MAAM,YAAY,cAAc;GAKhC,MAAM,QAAQ,eAAe,MAJN,UAAU,QAAQ;IACvC,MAAM;IACN;GACF,CAAC,GACsC,WAAW,eAAe,EAAE;GACnE,OAAO,kBAAkB,WAAW,OAAO,aAAa;EAC1D;EACA,MAAM,cAAc,cAAoC;GACtD,OAAO,KAAK,YAAY,YAAY;EACtC;CACF;AACF;AAEA,MAAa,qBACX,WACA,OACA,gBAAiC,uBAAuB,UAAU,OACjD;CACjB;CACA,MAAM,WAAW,SAA2C;EAC1D,MAAM,YAAY,cAAc;EAQhC,OAAO,iBAAiB,WAAW,OADrB,eAAe,MANN,UAAU,QAAQ;GACvC,MAAM;GACN;GACA;GACA,MAAM;EACR,CAAC,GACsC,WAAW,cACJ,EAAE,QAAQ,aAAa;CACvE;AACF;;AAGA,MAAa,sBAAsB;AAEnC,MAAa,oBACX,WACA,OACA,QACA,gBAAiC,uBAAuB,UAAU,OAClD;CAChB;CACA;CACA,MAAM,iBAAiB,KAAa,MAA8B;EAChE,MAAM,YAAY,cAAc;EAShC,eAAe,MARQ,UAAU,QAAQ;GACvC,MAAM;GACN;GACA,SAAS,MAAM;GACf;GACA;GACA;EACF,CAAC,GACwB,WAAW,KAAK;CAC3C;CACA,MAAM,UAAyB;EAC7B,MAAM,YAAY,cAAc;EAOhC,eAAe,MANQ,UAAU,QAAQ;GACvC,MAAM;GACN;GACA,SAAS,MAAM;GACf;EACF,CAAC,GACwB,WAAW,KAAK;CAC3C;AACF;AAEA,MAAM,0BAA0B,WAAsC;CACpE,IAAI,OAAO;CACX,aAAa;EACX,MAAM,YAAY,GAAG,OAAO,GAAG;EAC/B,QAAQ;EACR,OAAO;CACT;AACF;AAEA,MAAM,kBACJ,OACA,WACA,SAC0C;CAC1C,IAAI,MAAM,SAAS,SACjB,MAAM,IAAI,MAAM,GAAG,MAAM,KAAK,IAAI,MAAM,SAAS;CAEnD,IAAI,MAAM,SAAS,MACjB,MAAM,IAAI,MAAM,YAAY,KAAK,OAAO,UAAU,aAAa,MAAM,MAAM;CAE7E,IAAI,YAAY,KAAK,MAAM,WACzB,MAAM,IAAI,MAAM,yBAAyB,UAAU,aAAa,YAAY,KAAK,KAAK,QAAQ;CAEhG,OAAO;AACT;AAEA,MAAM,eAAe,UAA8C;CACjE,QAAQ,MAAM,MAAd;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,iBACH,OAAO,MAAM;EACf,KAAK,SACH,OAAO,MAAM;EACf,KAAK;EACL,KAAK;EACL,KAAK,aACH;CACJ;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"local-broker-mQmeVLF5.mjs","names":["isNodeError"],"sources":["../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"],"sourcesContent":["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, chmod } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawn } from \"node:child_process\";\n\nimport type { DaemonPaths } from \"./paths\";\nimport { MissingPlatformBrokerBinaryError, resolveBrokerNativeTarget } from \"./native-target\";\n\nexport interface BrokerCommand {\n command: string;\n args: string[];\n cwd?: string;\n}\n\nconst sourceUrl = import.meta.url;\nconst requireFromSource = createRequire(sourceUrl);\n\nexport interface ResolveBrokerCommandOptions {\n env?: NodeJS.ProcessEnv;\n platform?: NodeJS.Platform;\n arch?: string;\n sourceDir?: string;\n resolvePackageJson?: (packageName: string) => string | undefined;\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 platform = options.platform ?? process.platform;\n const arch = options.arch ?? process.arch;\n let installedBinaryError: MissingPlatformBrokerBinaryError | undefined;\n try {\n const installedBinary = await resolveInstalledBrokerBinary({\n platform,\n arch,\n resolvePackageJson: options.resolvePackageJson ?? resolvePackageJson,\n });\n if (installedBinary !== undefined) {\n return commandForBinary(installedBinary, paths);\n }\n } catch (error) {\n if (!(error instanceof MissingPlatformBrokerBinaryError)) {\n throw error;\n }\n installedBinaryError = error;\n }\n\n const sourceDir = options.sourceDir ?? dirname(fileURLToPath(sourceUrl));\n const workspaceRoot = await (options.findWorkspaceRoot ?? findWorkspaceRoot)(sourceDir);\n if (workspaceRoot !== undefined) {\n const binary = await (options.ensureDevBrokerBinary ?? ensureDevBrokerBinary)(workspaceRoot);\n return commandForBinary(binary, paths, workspaceRoot);\n }\n\n if (installedBinaryError !== undefined) {\n throw installedBinaryError;\n }\n\n const target = resolveBrokerNativeTarget(platform, arch);\n throw new MissingPlatformBrokerBinaryError(\n `unable to resolve OpenTray broker binary for ${platform}/${arch}; install ${target.packageName} or set OPENTRAY_BROKER_BIN`,\n platform,\n arch,\n { packageName: target.packageName },\n );\n};\n\nconst commandForBinary = (binary: string, paths: DaemonPaths, cwd?: string): 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 ],\n };\n if (cwd !== undefined) {\n command.cwd = cwd;\n }\n return command;\n};\n\ninterface ResolveInstalledBrokerBinaryOptions {\n platform: NodeJS.Platform;\n arch: string;\n resolvePackageJson: (packageName: string) => string | undefined;\n}\n\nexport const resolveInstalledBrokerBinary = async ({\n platform,\n arch,\n resolvePackageJson,\n}: ResolveInstalledBrokerBinaryOptions): Promise<string | undefined> => {\n const target = resolveBrokerNativeTarget(platform, arch);\n const packageJsonPath = resolvePackageJson(target.packageName);\n if (packageJsonPath === undefined) {\n return undefined;\n }\n\n const binary = join(dirname(packageJsonPath), target.binaryRelativePath);\n if (!(await exists(binary))) {\n throw new MissingPlatformBrokerBinaryError(\n `OpenTray broker package ${target.packageName} is installed but missing ${target.binaryRelativePath}`,\n platform,\n arch,\n {\n packageName: target.packageName,\n binaryPath: binary,\n },\n );\n }\n\n if (platform !== \"win32\") {\n await chmod(binary, 0o755);\n }\n\n return binary;\n};\n\nconst resolvePackageJson = (packageName: string): 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 findWorkspaceRoot = async (start: string): 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\nconst ensureDevBrokerBinary = async (workspaceRoot: string): Promise<string> => {\n const binary = join(\n workspaceRoot,\n \"target\",\n \"debug\",\n process.platform === \"win32\" ? \"opentray.exe\" : \"opentray\",\n );\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: 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(new Error(`cargo build -p opentray-bin failed with code ${code ?? \"unknown\"}`));\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","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\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 = (cliEntrypoint: string): DaemonDriver => ({\n async isAlive(pid) {\n return isProcessAlive(pid);\n },\n async spawnBroker(paths) {\n const broker = await resolveBrokerCommand(paths);\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 },\n stdio: \"ignore\",\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 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);\n return { status: \"started\", pid, paths };\n } finally {\n await lock.release();\n }\n};\n\nexport const stopDaemon = async ({ paths, driver }: 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 (options: StartDaemonOptions): Promise<DaemonStartResult> => {\n await stopDaemon({ paths: options.paths, driver: options.driver });\n return startDaemon(options);\n};\n\nexport const inspectDaemon = async ({ paths, driver }: 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 (driver: DaemonDriver, pid: number): 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 (readyFile: string): 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 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(lockFile, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY);\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> => 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 { createBrokerEndpointIdentity, formatUnixSocketPath, formatWindowsPipeName } from \"@opentray/spec\";\n\nexport interface DaemonPathOptions {\n homeDir: string;\n packageVersion: string;\n platform?: NodeJS.Platform;\n}\n\nexport interface DaemonPaths {\n homeDir: string;\n packageVersion: 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 platform = process.platform,\n}: DaemonPathOptions): DaemonPaths => {\n const identity = createBrokerEndpointIdentity({ packageVersion });\n const normalizedHome = homeDir.replace(/[\\\\/]+$/u, \"\");\n const stateRoot = `${normalizedHome}/.opentray/${identity.packageVersion}`;\n const runtimeDir = `${stateRoot}/runtime`;\n const endpoint =\n platform === \"win32\" ? formatWindowsPipeName(identity) : formatUnixSocketPath(normalizedHome, identity);\n\n return {\n homeDir: normalizedHome,\n packageVersion: identity.packageVersion,\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","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 { createNodeDaemonDriver, startDaemon, type DaemonDriver } 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 BrokerEventFrame = Extract<ServerFrame, { type: \"event\" | \"ext-event\" }>;\n\nexport interface LocalBrokerClient extends OpenTrayTransport {\n readonly endpoint: string;\n readonly sessionId: string;\n /** @deprecated Use `sessionId`. */\n readonly leaseId: string;\n onEvent(listener: (frame: BrokerEventFrame) => void): () => void;\n close(): Promise<void>;\n}\n\nexport interface ConnectLocalBrokerOptions extends Partial<BrokerEndpointIdentityOptions> {\n endpoint?: string;\n homeDir?: string;\n clientVersion?: string;\n autoStart?: boolean;\n daemonDriver?: DaemonDriver;\n cliEntrypoint?: 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 = options.packageVersion ?? (await readPackageVersion(packageJsonUrl));\n const clientVersion = options.clientVersion ?? packageVersion;\n const paths = resolveDaemonPaths({\n homeDir: options.homeDir ?? process.env.OPENTRAY_HOME ?? homedir(),\n packageVersion,\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(\"local broker autoStart requires the derived same-version endpoint\");\n }\n if (autoStart) {\n const cliEntrypoint = options.cliEntrypoint ?? resolveCliEntrypoint();\n const driver = options.daemonDriver ?? createNodeDaemonDriver(cliEntrypoint);\n await startDaemon({ paths, driver });\n }\n const socket = await connectSocket(endpoint);\n const connection = new LocalBrokerConnection(socket, endpoint);\n\n await connection.init(clientVersion, options.protocolVersion ?? PROTOCOL_VERSION);\n return connection;\n};\n\nclass LocalBrokerConnection implements LocalBrokerClient {\n readonly endpoint: string;\n sessionId = \"\";\n /** @deprecated Use `sessionId`. */\n leaseId = \"\";\n\n private buffer = \"\";\n private readonly listeners = new Set<(frame: BrokerEventFrame) => void>();\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 ) {\n this.endpoint = endpoint;\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\" }>>((resolve, reject) => {\n this.ready = { resolve, reject };\n });\n this.write({\n type: \"init\",\n protocolVersion,\n clientVersion,\n });\n const frame = await ready;\n this.sessionId = frame.sessionId;\n this.leaseId = 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: BrokerEventFrame) => 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(new Error(parsed.error ?? \"invalid server frame\"));\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 this.ready?.reject(error);\n this.ready = undefined;\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 \"space-created\":\n case \"default-space\":\n case \"tray-created\":\n case \"ack\":\n case \"daemon-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\") ? \"cli.ts\" : \"cli.mjs\";\n return fileURLToPath(new URL(`./${suffix}`, import.meta.url));\n};\n"],"mappings":"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"}