opentray 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -10
- package/assets/lynx-review/README.md +14 -0
- package/assets/lynx-review/main.lynx.bundle +0 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.mts.map +1 -1
- package/dist/cli.mjs +132 -19
- package/dist/cli.mjs.map +1 -1
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -38,11 +38,14 @@ await space.createTray({
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
const defaultSpace = await resolveDefaultSpace();
|
|
41
|
-
await createTray(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
await createTray(
|
|
42
|
+
{
|
|
43
|
+
trayId: "secondary",
|
|
44
|
+
title: "Secondary",
|
|
45
|
+
icon: { type: "rgba", data: [0, 0, 0, 0], width: 1, height: 1 },
|
|
46
|
+
},
|
|
47
|
+
{ space: defaultSpace.space }
|
|
48
|
+
);
|
|
46
49
|
```
|
|
47
50
|
|
|
48
51
|
`createTray()` resolves the broker default space when no explicit target is provided. If you already know the target, pass `space: defaultSpace.space` in the second argument instead of relying on default-space lookup.
|
|
@@ -94,9 +97,11 @@ After installing from npm, use the published CLI smoke path instead of workspace
|
|
|
94
97
|
|
|
95
98
|
```bash
|
|
96
99
|
opentray smoke daemon-tray
|
|
97
|
-
opentray smoke daemon-lynx
|
|
100
|
+
opentray smoke daemon-lynx
|
|
98
101
|
```
|
|
99
102
|
|
|
103
|
+
`opentray smoke daemon-lynx` now uses the package-owned review bundle by default so a fresh npm install can perform the final visual audit without a workspace checkout. Keep `--bundle <path-to-main.lynx.bundle>` only when you want to override that official audit asset with a custom bundle.
|
|
104
|
+
|
|
100
105
|
This example starts or reuses the same-version daemon automatically, creates a real tray through the public SDK, and prints broker-routed menu events. Use manual lifecycle commands only for operator/debug control:
|
|
101
106
|
|
|
102
107
|
```bash
|
|
@@ -113,7 +118,19 @@ The host side of that example can call `await tray.getBounds()` to read the curr
|
|
|
113
118
|
|
|
114
119
|
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.
|
|
115
120
|
|
|
116
|
-
The Lynx smoke path uses the same generic extension loader but launches a real `
|
|
121
|
+
The Lynx smoke path uses the same generic extension loader but launches a real `OpenTrayLynxRuntime.app.zip` sidecar from `@opentray/ext-lynx-darwin-*`. It starts the requested `.lynx.bundle` in a fixed host shell, sets an initial title and icon, applies an explicit startup feature expression, and exposes `Show Window`, `Hide Window`, and `Quit Smoke` through tray-routed events. Inside the Lynx window, use the rendered controls to verify `getCapabilities`, `getStyle`, `getTitle`, `setTitle`, `getIcon`, `setIcon`, `navigator.screen.getScreenDetails()`, `resizeTo`, `moveTo`, frameless toggling, `window.resizeTo()`, `window.getScreenDetails()`, and close behavior visually. On macOS, the runtime Dock icon should no longer appear blank.
|
|
122
|
+
|
|
123
|
+
For Lynx, `frameless` currently means borderless only. It does not imply a full-window drag region, because that would steal clicks from the page content. When isolating host-feature regressions, `--features "*,!frameless"` is the fastest way to keep the bridge on while removing the borderless shell from the test.
|
|
124
|
+
|
|
125
|
+
When you need to validate a GitHub-built macOS artifact before publish, use the workspace launcher instead of hand-written `/tmp` scripts:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pnpm run smoke:lynx -- --run <github-actions-run-id> --bundle packages/cli/assets/lynx-review/main.lynx.bundle
|
|
129
|
+
pnpm run smoke:lynx -- --run <github-actions-run-id> --bundle packages/cli/assets/lynx-review/main.lynx.bundle --features "nativeWindowApi,bindWindowGlobals,nativeScreenApi,bindScreenGlobals"
|
|
130
|
+
pnpm run smoke:lynx -- --run <github-actions-run-id> --bundle packages/cli/assets/lynx-review/main.lynx.bundle --features "*,!nativeScreenApi"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The package-owned review bundle is the full human acceptance surface. The separate `input-probe.lynx.bundle` is only a low-level diagnostic asset for isolating raw click/scroll/input delivery. The empty feature set is the baseline carrier check. Use it to validate the physical window baseline: click, scroll, input, red/yellow/green controls, and resize/move. Only after that baseline is healthy should you validate explicit startup feature sets on top of the same carrier.
|
|
117
134
|
|
|
118
135
|
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.
|
|
119
136
|
|
|
@@ -130,10 +147,10 @@ cargo build -p opentray-bin -p opentray-ext-webview -p opentray-ext-lynx
|
|
|
130
147
|
bun run scripts/binaries/stage-local.ts --kind daemon --source target/debug/opentray
|
|
131
148
|
bun run scripts/binaries/stage-local.ts --kind webview --source target/debug/libopentray_ext_webview.dylib
|
|
132
149
|
bun run scripts/binaries/stage-local.ts --kind lynx --source target/debug/libopentray_ext_lynx.dylib
|
|
133
|
-
bash scripts/release/build-lynx-runtime.sh /tmp/
|
|
134
|
-
bun run scripts/binaries/stage-local.ts --kind lynx-runtime --source /tmp/
|
|
150
|
+
bash scripts/release/build-lynx-runtime.sh /tmp/OpenTrayLynxRuntime.app.zip
|
|
151
|
+
bun run scripts/binaries/stage-local.ts --kind lynx-runtime --source /tmp/OpenTrayLynxRuntime.app.zip
|
|
135
152
|
OPENTRAY_EXAMPLE_WEBVIEW_SMOKE=1 pnpm --filter opentray cli -- smoke daemon-tray
|
|
136
|
-
pnpm --filter opentray cli -- smoke daemon-lynx
|
|
153
|
+
pnpm --filter opentray cli -- smoke daemon-lynx
|
|
137
154
|
```
|
|
138
155
|
|
|
139
156
|
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:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## Lynx Review Bundle
|
|
2
|
+
|
|
3
|
+
This directory contains the package-owned Lynx review bundle used by the public CLI smoke:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
opentray smoke daemon-lynx
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The goal is post-publish human verification. A fresh npm install must be able to open a real Lynx window without depending on a workspace checkout or `research/` paths.
|
|
10
|
+
|
|
11
|
+
TODO:
|
|
12
|
+
|
|
13
|
+
- move the source of this review app into a tracked repo-owned source root instead of relying on a copied research build artifact
|
|
14
|
+
- keep the bundle focused on visual/runtime acceptance, not on product features unrelated to extension-host verification
|
|
Binary file
|
package/dist/cli.d.mts
CHANGED
package/dist/cli.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.mts","names":[],"sources":["../src/cli.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"cli.d.mts","names":[],"sources":["../src/cli.ts"],"mappings":";;;KAuBK,UAAA;EACC,IAAA;EAAgB,MAAA;AAAA;EAChB,IAAA;EAAe,IAAA;AAAA;EAEf,IAAA;EACA,IAAA;EACA,UAAA;EACA,iBAAA;AAAA;EAEA,IAAA;AAAA;AAAA,cAEO,eAAA,GAAmB,IAAA,eAAiB,UA+BhD;AAAA,cAEY,MAAA,GAAgB,IAAA,eAAiB,OAAO;AAAA,cAwGxC,wBAAA,GAA4B,MAAoB,EAAZ,YAAY;AAAA,cAoBhD,eAAA,GACX,aAAA,sBACA,UAAkB"}
|
package/dist/cli.mjs
CHANGED
|
@@ -4,16 +4,29 @@ import { a as inspectDaemon, c as stopDaemon, i as createNodeDaemonDriver, n as
|
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { existsSync, realpathSync } from "node:fs";
|
|
7
|
-
import { resolve } from "node:path";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
8
|
//#region src/smoke/daemon-lynx.ts
|
|
9
9
|
const menuLabels$1 = new Map([
|
|
10
|
-
[1, "
|
|
10
|
+
[1, "Show Window"],
|
|
11
11
|
[2, "Hide Window"],
|
|
12
12
|
[99, "Quit Smoke"]
|
|
13
13
|
]);
|
|
14
14
|
const DEFAULT_LYNX_BUNDLE_ENV = "OPENTRAY_LYNX_BUNDLE";
|
|
15
|
+
const DEFAULT_LYNX_HOST_FEATURES_ENV = "OPENTRAY_LYNX_HOST_FEATURES";
|
|
16
|
+
const DEFAULT_REVIEW_BUNDLE_RELATIVE_PATH = "assets/lynx-review/main.lynx.bundle";
|
|
17
|
+
const LYNX_HOST_FEATURES = [
|
|
18
|
+
"nativeWindowApi",
|
|
19
|
+
"bindWindowGlobals",
|
|
20
|
+
"nativeScreenApi",
|
|
21
|
+
"bindScreenGlobals",
|
|
22
|
+
"frameless"
|
|
23
|
+
];
|
|
15
24
|
const runDaemonLynxSmoke = async (options = {}) => {
|
|
16
25
|
const bundlePath = resolveLynxBundlePath(options.bundlePath);
|
|
26
|
+
const hostFeatures = resolveLynxHostFeatures(options.featureExpression);
|
|
27
|
+
if (process.env.OPENTRAY_LYNX_DEBUG) console.log(`lynx debug modes: ${process.env.OPENTRAY_LYNX_DEBUG}`);
|
|
28
|
+
if (process.env[DEFAULT_LYNX_HOST_FEATURES_ENV]) console.log(`lynx host features: ${process.env[DEFAULT_LYNX_HOST_FEATURES_ENV]}`);
|
|
29
|
+
if (process.env.OPENTRAY_LYNX_DEBUG_LOG_PATH) console.log(`lynx debug log path: ${process.env.OPENTRAY_LYNX_DEBUG_LOG_PATH}`);
|
|
17
30
|
const connection = await connectLocalBroker();
|
|
18
31
|
const client = createClient(connection, { requestIdPrefix: "daemon-lynx" });
|
|
19
32
|
console.log(`connected: endpoint=${connection.endpoint} session=${connection.sessionId}`);
|
|
@@ -35,7 +48,7 @@ const runDaemonLynxSmoke = async (options = {}) => {
|
|
|
35
48
|
{
|
|
36
49
|
type: "item",
|
|
37
50
|
id: 1,
|
|
38
|
-
title: "
|
|
51
|
+
title: "Show Window"
|
|
39
52
|
},
|
|
40
53
|
{
|
|
41
54
|
type: "item",
|
|
@@ -63,11 +76,12 @@ const runDaemonLynxSmoke = async (options = {}) => {
|
|
|
63
76
|
let exitTimer;
|
|
64
77
|
let resolveClosed;
|
|
65
78
|
const show = async () => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
const command = createLynxShowCommand({
|
|
80
|
+
bundlePath,
|
|
81
|
+
hostFeatures
|
|
69
82
|
});
|
|
70
|
-
|
|
83
|
+
await tray.commandExtension("lynx", command);
|
|
84
|
+
console.log(`lynx command: show features=${describeLynxHostFeatures(hostFeatures)} bundle=${bundlePath}`);
|
|
71
85
|
};
|
|
72
86
|
const hide = async () => {
|
|
73
87
|
await tray.commandExtension("lynx", { type: "hide" });
|
|
@@ -125,7 +139,7 @@ const runDaemonLynxSmoke = async (options = {}) => {
|
|
|
125
139
|
}
|
|
126
140
|
try {
|
|
127
141
|
await show();
|
|
128
|
-
console.log(
|
|
142
|
+
console.log(`use the tray menu to re-show or hide the window, or press Ctrl+C to exit; features=${describeLynxHostFeatures(hostFeatures)}`);
|
|
129
143
|
} catch (error) {
|
|
130
144
|
await connection.close();
|
|
131
145
|
process.off("SIGINT", onSignal);
|
|
@@ -138,11 +152,96 @@ const runDaemonLynxSmoke = async (options = {}) => {
|
|
|
138
152
|
};
|
|
139
153
|
const resolveLynxBundlePath = (value) => {
|
|
140
154
|
const resolved = value ?? process.env[DEFAULT_LYNX_BUNDLE_ENV];
|
|
141
|
-
if (resolved === void 0 || resolved.length === 0)
|
|
155
|
+
if (resolved === void 0 || resolved.length === 0) {
|
|
156
|
+
const bundledReviewBundle = resolveBundledReviewBundlePath();
|
|
157
|
+
if (!existsSync(bundledReviewBundle)) throw new Error(`lynx smoke requires --bundle <path>, ${DEFAULT_LYNX_BUNDLE_ENV}=<path-to-main.lynx.bundle>, or a packaged review bundle at ${bundledReviewBundle}`);
|
|
158
|
+
return realpathSync(bundledReviewBundle);
|
|
159
|
+
}
|
|
142
160
|
const absolutePath = resolve(process.env.INIT_CWD ?? process.cwd(), resolved);
|
|
143
161
|
if (!existsSync(absolutePath)) throw new Error(`lynx smoke bundle does not exist: ${absolutePath}`);
|
|
144
162
|
return realpathSync(absolutePath);
|
|
145
163
|
};
|
|
164
|
+
const resolveLynxHostFeatures = (expression = process.env[DEFAULT_LYNX_HOST_FEATURES_ENV] ?? "") => {
|
|
165
|
+
const features = createEmptyLynxHostFeatures();
|
|
166
|
+
for (const rawToken of expression.split(",")) {
|
|
167
|
+
const token = rawToken.trim();
|
|
168
|
+
if (token.length === 0) continue;
|
|
169
|
+
applyLynxHostFeatureToken(features, token);
|
|
170
|
+
}
|
|
171
|
+
return features;
|
|
172
|
+
};
|
|
173
|
+
const createLynxShowCommand = ({ bundlePath, hostFeatures }) => {
|
|
174
|
+
const command = {
|
|
175
|
+
type: "show",
|
|
176
|
+
bundlePath,
|
|
177
|
+
width: 720,
|
|
178
|
+
height: 420,
|
|
179
|
+
minWidth: 320,
|
|
180
|
+
minHeight: 220,
|
|
181
|
+
maxWidth: 960,
|
|
182
|
+
maxHeight: 720,
|
|
183
|
+
nativeWindowApi: hostFeatures.nativeWindowApi,
|
|
184
|
+
bindWindowGlobals: hostFeatures.bindWindowGlobals,
|
|
185
|
+
nativeScreenApi: hostFeatures.nativeScreenApi,
|
|
186
|
+
bindScreenGlobals: hostFeatures.bindScreenGlobals,
|
|
187
|
+
title: `OpenTray Lynx Smoke (${describeLynxHostFeatures(hostFeatures)})`,
|
|
188
|
+
icon: createVisibleIcon$1()
|
|
189
|
+
};
|
|
190
|
+
if (hostFeatures.frameless) command.style = { frameless: true };
|
|
191
|
+
return command;
|
|
192
|
+
};
|
|
193
|
+
const describeLynxHostFeatures = (features) => {
|
|
194
|
+
const enabled = LYNX_HOST_FEATURES.filter((feature) => features[feature]);
|
|
195
|
+
return enabled.length === 0 ? "baseline" : enabled.join(",");
|
|
196
|
+
};
|
|
197
|
+
const resolveBundledReviewBundlePath = (moduleUrl = import.meta.url) => {
|
|
198
|
+
let currentDir = dirname(fileURLToPath(moduleUrl));
|
|
199
|
+
while (true) {
|
|
200
|
+
if (existsSync(join(currentDir, "package.json"))) return join(currentDir, DEFAULT_REVIEW_BUNDLE_RELATIVE_PATH);
|
|
201
|
+
const parent = dirname(currentDir);
|
|
202
|
+
if (parent === currentDir) throw new Error(`failed to resolve opentray package root from ${moduleUrl}`);
|
|
203
|
+
currentDir = parent;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
function createEmptyLynxHostFeatures() {
|
|
207
|
+
return {
|
|
208
|
+
nativeWindowApi: false,
|
|
209
|
+
bindWindowGlobals: false,
|
|
210
|
+
nativeScreenApi: false,
|
|
211
|
+
bindScreenGlobals: false,
|
|
212
|
+
frameless: false
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function applyLynxHostFeatureToken(features, rawToken) {
|
|
216
|
+
const disabled = rawToken.startsWith("!");
|
|
217
|
+
const token = disabled ? rawToken.slice(1) : rawToken;
|
|
218
|
+
if (token === "*" || token === "full") {
|
|
219
|
+
for (const feature of LYNX_HOST_FEATURES) features[feature] = !disabled;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (token === "baseline") {
|
|
223
|
+
for (const feature of LYNX_HOST_FEATURES) features[feature] = false;
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (!isLynxHostFeature(token)) throw new Error(`unsupported Lynx host feature token: ${rawToken}; supported tokens: baseline, full, *, !<feature>, ${LYNX_HOST_FEATURES.join(", ")}`);
|
|
227
|
+
features[token] = !disabled;
|
|
228
|
+
if (token === "nativeWindowApi" && disabled) {
|
|
229
|
+
features.bindWindowGlobals = false;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (token === "nativeScreenApi" && disabled) {
|
|
233
|
+
features.bindScreenGlobals = false;
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (token === "bindWindowGlobals" && !disabled) {
|
|
237
|
+
features.nativeWindowApi = true;
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (token === "bindScreenGlobals" && !disabled) features.nativeScreenApi = true;
|
|
241
|
+
}
|
|
242
|
+
function isLynxHostFeature(value) {
|
|
243
|
+
return LYNX_HOST_FEATURES.includes(value);
|
|
244
|
+
}
|
|
146
245
|
function createVisibleIcon$1() {
|
|
147
246
|
const width = 32;
|
|
148
247
|
const height = 32;
|
|
@@ -397,14 +496,12 @@ const parseCliCommand = (argv) => {
|
|
|
397
496
|
name: "daemon-tray"
|
|
398
497
|
};
|
|
399
498
|
if (group === "smoke" && action === "daemon-lynx") {
|
|
400
|
-
const
|
|
401
|
-
return
|
|
402
|
-
type: "smoke",
|
|
403
|
-
name: "daemon-lynx"
|
|
404
|
-
} : {
|
|
499
|
+
const smokeOptions = parseSmokeOptions(rest);
|
|
500
|
+
return {
|
|
405
501
|
type: "smoke",
|
|
406
502
|
name: "daemon-lynx",
|
|
407
|
-
bundlePath
|
|
503
|
+
...smokeOptions.bundlePath ? { bundlePath: smokeOptions.bundlePath } : {},
|
|
504
|
+
...smokeOptions.featureExpression ? { featureExpression: smokeOptions.featureExpression } : {}
|
|
408
505
|
};
|
|
409
506
|
}
|
|
410
507
|
return { type: "help" };
|
|
@@ -428,7 +525,10 @@ const runCli = async (argv) => {
|
|
|
428
525
|
}
|
|
429
526
|
if (command.type === "smoke") {
|
|
430
527
|
if (command.name === "daemon-tray") await runDaemonTraySmoke();
|
|
431
|
-
else await runDaemonLynxSmoke(
|
|
528
|
+
else await runDaemonLynxSmoke({
|
|
529
|
+
...command.bundlePath === void 0 ? {} : { bundlePath: command.bundlePath },
|
|
530
|
+
...command.featureExpression === void 0 ? {} : { featureExpression: command.featureExpression }
|
|
531
|
+
});
|
|
432
532
|
return 0;
|
|
433
533
|
}
|
|
434
534
|
const driver = createNodeDaemonDriver(cliModulePath);
|
|
@@ -488,7 +588,8 @@ const runCli = async (argv) => {
|
|
|
488
588
|
const printHelp = () => {
|
|
489
589
|
console.error("Usage: opentray daemon <start|stop|restart|health>");
|
|
490
590
|
console.error(" opentray smoke daemon-tray");
|
|
491
|
-
console.error(" opentray smoke daemon-lynx --bundle <path-to-main.lynx.bundle>");
|
|
591
|
+
console.error(" opentray smoke daemon-lynx [--bundle <path-to-main.lynx.bundle>] [--features <baseline|full|*,!feature,...>]");
|
|
592
|
+
console.error(" default Lynx smoke uses the packaged review bundle");
|
|
492
593
|
};
|
|
493
594
|
const formatDaemonHealthOutput = (health) => {
|
|
494
595
|
const lines = [
|
|
@@ -513,8 +614,20 @@ const isCliEntrypoint = (argvEntryPath, modulePath) => {
|
|
|
513
614
|
return argvEntryPath === modulePath;
|
|
514
615
|
}
|
|
515
616
|
};
|
|
516
|
-
function
|
|
517
|
-
|
|
617
|
+
function parseSmokeOptions(argv) {
|
|
618
|
+
let bundlePath;
|
|
619
|
+
let featureExpression;
|
|
620
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
621
|
+
if (argv[index] === "--bundle") {
|
|
622
|
+
bundlePath = argv[index + 1];
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
if (argv[index] === "--features") featureExpression = argv[index + 1];
|
|
626
|
+
}
|
|
627
|
+
return {
|
|
628
|
+
...bundlePath === void 0 ? {} : { bundlePath },
|
|
629
|
+
...featureExpression === void 0 ? {} : { featureExpression }
|
|
630
|
+
};
|
|
518
631
|
}
|
|
519
632
|
if (isCliEntrypoint(process.argv[1], cliModulePath)) runCli(process.argv.slice(2)).then((code) => {
|
|
520
633
|
process.exitCode = code;
|
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, \"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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"}
|
|
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 { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { createClient } from \"../client\";\nimport { connectLocalBroker } from \"../local-broker\";\n\nconst menuLabels = new Map<number, string>([\n [1, \"Show Window\"],\n [2, \"Hide Window\"],\n [99, \"Quit Smoke\"],\n]);\n\nconst DEFAULT_LYNX_BUNDLE_ENV = \"OPENTRAY_LYNX_BUNDLE\";\nconst DEFAULT_LYNX_HOST_FEATURES_ENV = \"OPENTRAY_LYNX_HOST_FEATURES\";\nconst DEFAULT_REVIEW_BUNDLE_RELATIVE_PATH =\n \"assets/lynx-review/main.lynx.bundle\";\nconst LYNX_HOST_FEATURES = [\n \"nativeWindowApi\",\n \"bindWindowGlobals\",\n \"nativeScreenApi\",\n \"bindScreenGlobals\",\n \"frameless\",\n] as const;\n\nexport type LynxHostFeature = (typeof LYNX_HOST_FEATURES)[number];\n\nexport interface LynxHostFeatureState {\n nativeWindowApi: boolean;\n bindWindowGlobals: boolean;\n nativeScreenApi: boolean;\n bindScreenGlobals: boolean;\n frameless: boolean;\n}\n\ninterface LynxSmokeShowCommand {\n type: \"show\";\n bundlePath: string;\n width: number;\n height: number;\n minWidth: number;\n minHeight: number;\n maxWidth: number;\n maxHeight: number;\n nativeWindowApi: boolean;\n bindWindowGlobals: boolean;\n nativeScreenApi: boolean;\n bindScreenGlobals: boolean;\n title: string;\n icon: ReturnType<typeof createVisibleIcon>;\n style?: {\n frameless: boolean;\n };\n}\n\nexport interface DaemonLynxSmokeOptions {\n bundlePath?: string;\n featureExpression?: string;\n}\n\nexport const runDaemonLynxSmoke = async (\n options: DaemonLynxSmokeOptions = {},\n): Promise<void> => {\n const bundlePath = resolveLynxBundlePath(options.bundlePath);\n const hostFeatures = resolveLynxHostFeatures(options.featureExpression);\n if (process.env.OPENTRAY_LYNX_DEBUG) {\n console.log(`lynx debug modes: ${process.env.OPENTRAY_LYNX_DEBUG}`);\n }\n if (process.env[DEFAULT_LYNX_HOST_FEATURES_ENV]) {\n console.log(\n `lynx host features: ${process.env[DEFAULT_LYNX_HOST_FEATURES_ENV]}`,\n );\n }\n if (process.env.OPENTRAY_LYNX_DEBUG_LOG_PATH) {\n console.log(\n `lynx debug log path: ${process.env.OPENTRAY_LYNX_DEBUG_LOG_PATH}`,\n );\n }\n const connection = await connectLocalBroker();\n const client = createClient(connection, { requestIdPrefix: \"daemon-lynx\" });\n console.log(\n `connected: endpoint=${connection.endpoint} session=${connection.sessionId}`,\n );\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: \"Show Window\" },\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 const command = createLynxShowCommand({\n bundlePath,\n hostFeatures,\n });\n await tray.commandExtension(\"lynx\", command);\n console.log(\n `lynx command: show features=${describeLynxHostFeatures(\n hostFeatures,\n )} bundle=${bundlePath}`,\n );\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(\n `menu click: ${menuLabels.get(frame.event.itemId) ?? frame.event.itemId}`,\n );\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(\n `use the tray menu to re-show or hide the window, or press Ctrl+C to exit; features=${describeLynxHostFeatures(\n hostFeatures,\n )}`,\n );\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 const bundledReviewBundle = resolveBundledReviewBundlePath();\n if (!existsSync(bundledReviewBundle)) {\n throw new Error(\n `lynx smoke requires --bundle <path>, ${DEFAULT_LYNX_BUNDLE_ENV}=<path-to-main.lynx.bundle>, or a packaged review bundle at ${bundledReviewBundle}`,\n );\n }\n return realpathSync(bundledReviewBundle);\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\nexport const resolveLynxHostFeatures = (\n expression = process.env[DEFAULT_LYNX_HOST_FEATURES_ENV] ?? \"\",\n): LynxHostFeatureState => {\n const features = createEmptyLynxHostFeatures();\n for (const rawToken of expression.split(\",\")) {\n const token = rawToken.trim();\n if (token.length === 0) {\n continue;\n }\n applyLynxHostFeatureToken(features, token);\n }\n return features;\n};\n\nexport const createLynxShowCommand = ({\n bundlePath,\n hostFeatures,\n}: {\n bundlePath: string;\n hostFeatures: LynxHostFeatureState;\n}): LynxSmokeShowCommand => {\n const command: LynxSmokeShowCommand = {\n type: \"show\",\n bundlePath,\n width: 720,\n height: 420,\n minWidth: 320,\n minHeight: 220,\n maxWidth: 960,\n maxHeight: 720,\n nativeWindowApi: hostFeatures.nativeWindowApi,\n bindWindowGlobals: hostFeatures.bindWindowGlobals,\n nativeScreenApi: hostFeatures.nativeScreenApi,\n bindScreenGlobals: hostFeatures.bindScreenGlobals,\n title: `OpenTray Lynx Smoke (${describeLynxHostFeatures(hostFeatures)})`,\n icon: createVisibleIcon(),\n };\n if (hostFeatures.frameless) {\n command.style = { frameless: true };\n }\n return command;\n};\n\nexport const describeLynxHostFeatures = (\n features: LynxHostFeatureState,\n): string => {\n const enabled = LYNX_HOST_FEATURES.filter((feature) => features[feature]);\n return enabled.length === 0 ? \"baseline\" : enabled.join(\",\");\n};\n\nexport const resolveBundledReviewBundlePath = (\n moduleUrl = import.meta.url,\n): string => {\n let currentDir = dirname(fileURLToPath(moduleUrl));\n\n while (true) {\n const packageJson = join(currentDir, \"package.json\");\n if (existsSync(packageJson)) {\n return join(currentDir, DEFAULT_REVIEW_BUNDLE_RELATIVE_PATH);\n }\n\n const parent = dirname(currentDir);\n if (parent === currentDir) {\n throw new Error(`failed to resolve opentray package root from ${moduleUrl}`);\n }\n currentDir = parent;\n }\n};\n\nfunction createEmptyLynxHostFeatures(): LynxHostFeatureState {\n return {\n nativeWindowApi: false,\n bindWindowGlobals: false,\n nativeScreenApi: false,\n bindScreenGlobals: false,\n frameless: false,\n };\n}\n\nfunction applyLynxHostFeatureToken(\n features: LynxHostFeatureState,\n rawToken: string,\n): void {\n const disabled = rawToken.startsWith(\"!\");\n const token = disabled ? rawToken.slice(1) : rawToken;\n\n if (token === \"*\" || token === \"full\") {\n for (const feature of LYNX_HOST_FEATURES) {\n features[feature] = !disabled;\n }\n return;\n }\n if (token === \"baseline\") {\n for (const feature of LYNX_HOST_FEATURES) {\n features[feature] = false;\n }\n return;\n }\n if (!isLynxHostFeature(token)) {\n throw new Error(\n `unsupported Lynx host feature token: ${rawToken}; supported tokens: baseline, full, *, !<feature>, ${LYNX_HOST_FEATURES.join(\n \", \",\n )}`,\n );\n }\n features[token] = !disabled;\n if (token === \"nativeWindowApi\" && disabled) {\n features.bindWindowGlobals = false;\n return;\n }\n if (token === \"nativeScreenApi\" && disabled) {\n features.bindScreenGlobals = false;\n return;\n }\n if (token === \"bindWindowGlobals\" && !disabled) {\n features.nativeWindowApi = true;\n return;\n }\n if (token === \"bindScreenGlobals\" && !disabled) {\n features.nativeScreenApi = true;\n }\n}\n\nfunction isLynxHostFeature(value: string): value is LynxHostFeature {\n return (LYNX_HOST_FEATURES as readonly string[]).includes(value);\n}\n\nfunction createVisibleIcon(): {\n type: \"rgba\";\n width: number;\n height: number;\n data: number[];\n} {\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 {\n createNodeDaemonDriver,\n inspectDaemon,\n restartDaemon,\n startDaemon,\n stopDaemon,\n} 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 | {\n type: \"smoke\";\n name: \"daemon-lynx\";\n bundlePath?: string;\n featureExpression?: string;\n }\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 smokeOptions = parseSmokeOptions(rest);\n return {\n type: \"smoke\",\n name: \"daemon-lynx\",\n ...(smokeOptions.bundlePath\n ? { bundlePath: smokeOptions.bundlePath }\n : {}),\n ...(smokeOptions.featureExpression\n ? { featureExpression: smokeOptions.featureExpression }\n : {}),\n };\n }\n return { type: \"help\" };\n }\n if (\n action === \"start\" ||\n action === \"stop\" ||\n action === \"restart\" ||\n action === \"health\"\n ) {\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 =\n process.env.OPENTRAY_DAEMON_PACKAGE_VERSION ??\n (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 {\n ...(command.bundlePath === undefined\n ? {}\n : { bundlePath: command.bundlePath }),\n ...(command.featureExpression === undefined\n ? {}\n : { featureExpression: command.featureExpression }),\n }\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(\n `opentray daemon ${result.status}: pid=${result.pid} endpoint=${result.paths.endpoint}`\n );\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(\n `opentray daemon ${result.status}: pid=${result.pid} endpoint=${result.paths.endpoint}`\n );\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(\n `expected daemon-health response, received ${response.type}`\n );\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(\n \" opentray smoke daemon-lynx [--bundle <path-to-main.lynx.bundle>] [--features <baseline|full|*,!feature,...>]\"\n );\n console.error(\" default Lynx smoke uses the packaged review 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 = (\n argvEntryPath: string | undefined,\n modulePath: string\n): boolean => {\n if (argvEntryPath === undefined) return false;\n try {\n return realpathSync(argvEntryPath) === realpathSync(modulePath);\n } catch {\n return argvEntryPath === modulePath;\n }\n};\n\nfunction parseSmokeOptions(argv: string[]): {\n bundlePath?: string;\n featureExpression?: string;\n} {\n let bundlePath: string | undefined;\n let featureExpression: string | undefined;\n for (let index = 0; index < argv.length; index += 1) {\n if (argv[index] === \"--bundle\") {\n bundlePath = argv[index + 1];\n continue;\n }\n if (argv[index] === \"--features\") {\n featureExpression = argv[index + 1];\n }\n }\n return {\n ...(bundlePath === undefined ? {} : { bundlePath }),\n ...(featureExpression === undefined ? {} : { featureExpression }),\n };\n}\n\nif (isCliEntrypoint(process.argv[1], cliModulePath)) {\n runCli(process.argv.slice(2)).then((code) => {\n process.exitCode = code;\n });\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opentray",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Developer-facing OpenTray SDK and CLI package.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
|
+
"assets",
|
|
27
28
|
"README.md"
|
|
28
29
|
],
|
|
29
30
|
"sideEffects": false,
|
|
@@ -36,12 +37,12 @@
|
|
|
36
37
|
"vitest": "^4.1.7"
|
|
37
38
|
},
|
|
38
39
|
"optionalDependencies": {
|
|
39
|
-
"@opentray/darwin-arm64": "0.5.
|
|
40
|
-
"@opentray/darwin-x64": "0.5.
|
|
41
|
-
"@opentray/linux-arm64": "0.5.
|
|
42
|
-
"@opentray/linux-x64": "0.5.
|
|
43
|
-
"@opentray/windows-arm64": "0.5.
|
|
44
|
-
"@opentray/windows-x64": "0.5.
|
|
40
|
+
"@opentray/darwin-arm64": "0.5.1",
|
|
41
|
+
"@opentray/darwin-x64": "0.5.1",
|
|
42
|
+
"@opentray/linux-arm64": "0.5.1",
|
|
43
|
+
"@opentray/linux-x64": "0.5.1",
|
|
44
|
+
"@opentray/windows-arm64": "0.5.1",
|
|
45
|
+
"@opentray/windows-x64": "0.5.1"
|
|
45
46
|
},
|
|
46
47
|
"scripts": {
|
|
47
48
|
"build": "tsdown src/index.ts src/node.ts src/cli.ts --format esm --dts",
|