@lynx-js/lynxtron 0.0.1 → 0.0.2

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.
Files changed (51) hide show
  1. package/README.md +80 -0
  2. package/apis/api/app.d.ts +1848 -0
  3. package/apis/api/asar.d.ts +124 -0
  4. package/apis/api/base-window.d.ts +1712 -0
  5. package/apis/api/clipboard.d.ts +54 -0
  6. package/apis/api/command-line.d.ts +46 -0
  7. package/apis/api/context-bridge.d.ts +8 -0
  8. package/apis/api/devtool.d.ts +34 -0
  9. package/apis/api/dialog.d.ts +633 -0
  10. package/apis/api/dock.d.ts +88 -0
  11. package/apis/api/environment.d.ts +9 -0
  12. package/apis/api/event.d.ts +8 -0
  13. package/apis/api/jump-list-item.d.ts +83 -0
  14. package/apis/api/lynx-library.d.ts +7 -0
  15. package/apis/api/lynx-template-bundle.d.ts +32 -0
  16. package/apis/api/lynx-template-data.d.ts +10 -0
  17. package/apis/api/lynx-update-meta.d.ts +16 -0
  18. package/apis/api/lynx-window.d.ts +405 -0
  19. package/apis/api/menu.d.ts +96 -0
  20. package/apis/api/native-image.d.ts +268 -0
  21. package/apis/api/notification-response.d.ts +26 -0
  22. package/apis/api/notification.d.ts +242 -0
  23. package/apis/api/power-monitor.d.ts +121 -0
  24. package/apis/api/process-metric.d.ts +93 -0
  25. package/apis/api/protocol.d.ts +54 -0
  26. package/apis/api/screen.d.ts +222 -0
  27. package/apis/api/shell.d.ts +124 -0
  28. package/apis/api/task.d.ts +39 -0
  29. package/apis/api/touch-bar.d.ts +206 -0
  30. package/apis/api/tray.d.ts +44 -0
  31. package/apis/api/utility-process.d.ts +73 -0
  32. package/apis/lynx.d.ts +15 -0
  33. package/apis/lynxtron.d.ts +39 -0
  34. package/apis/structures/point.d.ts +10 -0
  35. package/apis/structures/rectangle.d.ts +22 -0
  36. package/apis/structures/size.d.ts +8 -0
  37. package/apis/web-host.d.ts +24 -0
  38. package/cli.js +28 -0
  39. package/context-bridge.js +6 -0
  40. package/fuses-cli.js +143 -0
  41. package/fuses.js +299 -0
  42. package/install.js +127 -0
  43. package/lynx.js +1 -0
  44. package/lynxtron.js +43 -0
  45. package/lynxtron_bin.js +10 -0
  46. package/native-paths.cjs +38 -0
  47. package/package.json +71 -4
  48. package/utils/download.js +72 -0
  49. package/utils/env-config.js +35 -0
  50. package/web-host/index.js +110 -0
  51. package/web-worker.js +12 -0
@@ -0,0 +1,206 @@
1
+ // Copyright 2026 The Lynxtron Authors. All rights reserved.
2
+ // Licensed under the Apache License Version 2.0 that can be found in the
3
+ // LICENSE file in the root directory of this source tree.
4
+
5
+ import { NativeImage } from './native-image';
6
+
7
+ export interface ScrubberItem {
8
+ icon?: NativeImage;
9
+ label?: string;
10
+ }
11
+
12
+ export interface SegmentedControlSegment {
13
+ enabled?: boolean;
14
+ icon?: NativeImage;
15
+ label?: string;
16
+ }
17
+
18
+ export interface TouchBarButtonConstructorOptions {
19
+ label?: string;
20
+ accessibilityLabel?: string;
21
+ backgroundColor?: string;
22
+ icon?: NativeImage | string;
23
+ iconPosition?: 'left' | 'right' | 'overlay';
24
+ click?: () => void;
25
+ enabled?: boolean;
26
+ }
27
+
28
+ export interface TouchBarColorPickerConstructorOptions {
29
+ availableColors?: string[];
30
+ selectedColor?: string;
31
+ change?: (color: string) => void;
32
+ }
33
+
34
+ export interface TouchBarConstructorOptions {
35
+ items?: Array<
36
+ | TouchBarButton
37
+ | TouchBarColorPicker
38
+ | TouchBarGroup
39
+ | TouchBarLabel
40
+ | TouchBarPopover
41
+ | TouchBarScrubber
42
+ | TouchBarSegmentedControl
43
+ | TouchBarSlider
44
+ | TouchBarSpacer
45
+ >;
46
+ escapeItem?:
47
+ | TouchBarButton
48
+ | TouchBarColorPicker
49
+ | TouchBarGroup
50
+ | TouchBarLabel
51
+ | TouchBarPopover
52
+ | TouchBarScrubber
53
+ | TouchBarSegmentedControl
54
+ | TouchBarSlider
55
+ | TouchBarSpacer
56
+ | null;
57
+ }
58
+
59
+ export interface TouchBarGroupConstructorOptions {
60
+ items: TouchBar;
61
+ }
62
+
63
+ export interface TouchBarLabelConstructorOptions {
64
+ label?: string;
65
+ accessibilityLabel?: string;
66
+ textColor?: string;
67
+ }
68
+
69
+ export interface TouchBarPopoverConstructorOptions {
70
+ label?: string;
71
+ icon?: NativeImage;
72
+ items: TouchBar;
73
+ showCloseButton?: boolean;
74
+ }
75
+
76
+ export interface TouchBarScrubberConstructorOptions {
77
+ items: ScrubberItem[];
78
+ select?: (selectedIndex: number) => void;
79
+ highlight?: (highlightedIndex: number) => void;
80
+ selectedStyle?: 'background' | 'outline' | 'none';
81
+ overlayStyle?: 'background' | 'outline' | 'none';
82
+ showArrowButtons?: boolean;
83
+ mode?: 'fixed' | 'free';
84
+ continuous?: boolean;
85
+ }
86
+
87
+ export interface TouchBarSegmentedControlConstructorOptions {
88
+ segmentStyle?:
89
+ | 'automatic'
90
+ | 'rounded'
91
+ | 'textured-rounded'
92
+ | 'round-rect'
93
+ | 'textured-square'
94
+ | 'capsule'
95
+ | 'small-square'
96
+ | 'separated';
97
+ mode?: 'single' | 'multiple' | 'buttons';
98
+ segments: SegmentedControlSegment[];
99
+ selectedIndex?: number;
100
+ change?: (selectedIndex: number, isSelected: boolean) => void;
101
+ }
102
+
103
+ export interface TouchBarSliderConstructorOptions {
104
+ label?: string;
105
+ value?: number;
106
+ minValue?: number;
107
+ maxValue?: number;
108
+ change?: (newValue: number) => void;
109
+ }
110
+
111
+ export interface TouchBarSpacerConstructorOptions {
112
+ size?: 'small' | 'large' | 'flexible';
113
+ }
114
+
115
+ export declare class TouchBar {
116
+ constructor(options: TouchBarConstructorOptions);
117
+ escapeItem:
118
+ | TouchBarButton
119
+ | TouchBarColorPicker
120
+ | TouchBarGroup
121
+ | TouchBarLabel
122
+ | TouchBarPopover
123
+ | TouchBarScrubber
124
+ | TouchBarSegmentedControl
125
+ | TouchBarSlider
126
+ | TouchBarSpacer
127
+ | null;
128
+ static TouchBarButton: typeof TouchBarButton;
129
+ static TouchBarColorPicker: typeof TouchBarColorPicker;
130
+ static TouchBarGroup: typeof TouchBarGroup;
131
+ static TouchBarLabel: typeof TouchBarLabel;
132
+ static TouchBarOtherItemsProxy: typeof TouchBarOtherItemsProxy;
133
+ static TouchBarPopover: typeof TouchBarPopover;
134
+ static TouchBarScrubber: typeof TouchBarScrubber;
135
+ static TouchBarSegmentedControl: typeof TouchBarSegmentedControl;
136
+ static TouchBarSlider: typeof TouchBarSlider;
137
+ static TouchBarSpacer: typeof TouchBarSpacer;
138
+ }
139
+
140
+ export declare class TouchBarButton {
141
+ constructor(options: TouchBarButtonConstructorOptions);
142
+ accessibilityLabel: string;
143
+ backgroundColor: string;
144
+ enabled: boolean;
145
+ icon: NativeImage;
146
+ iconPosition: 'left' | 'right' | 'overlay';
147
+ label: string;
148
+ }
149
+
150
+ export declare class TouchBarColorPicker {
151
+ constructor(options: TouchBarColorPickerConstructorOptions);
152
+ availableColors: string[];
153
+ selectedColor: string;
154
+ }
155
+
156
+ export declare class TouchBarGroup {
157
+ constructor(options: TouchBarGroupConstructorOptions);
158
+ }
159
+
160
+ export declare class TouchBarLabel {
161
+ constructor(options: TouchBarLabelConstructorOptions);
162
+ accessibilityLabel: string;
163
+ label: string;
164
+ textColor: string;
165
+ }
166
+
167
+ export declare class TouchBarOtherItemsProxy {
168
+ constructor();
169
+ }
170
+
171
+ export declare class TouchBarPopover {
172
+ constructor(options: TouchBarPopoverConstructorOptions);
173
+ icon: NativeImage;
174
+ label: string;
175
+ }
176
+
177
+ export declare class TouchBarScrubber {
178
+ constructor(options: TouchBarScrubberConstructorOptions);
179
+ continuous: boolean;
180
+ items: ScrubberItem[];
181
+ mode: 'fixed' | 'free';
182
+ overlayStyle: 'background' | 'outline' | 'none';
183
+ selectedStyle: 'background' | 'outline' | 'none';
184
+ showArrowButtons: boolean;
185
+ }
186
+
187
+ export declare class TouchBarSegmentedControl {
188
+ constructor(options: TouchBarSegmentedControlConstructorOptions);
189
+ mode: 'single' | 'multiple' | 'buttons';
190
+ segments: SegmentedControlSegment[];
191
+ segmentStyle: string;
192
+ selectedIndex: number;
193
+ }
194
+
195
+ export declare class TouchBarSlider {
196
+ constructor(options: TouchBarSliderConstructorOptions);
197
+ label: string;
198
+ maxValue: number;
199
+ minValue: number;
200
+ value: number;
201
+ }
202
+
203
+ export declare class TouchBarSpacer {
204
+ constructor(options: TouchBarSpacerConstructorOptions);
205
+ size: 'small' | 'large' | 'flexible';
206
+ }
@@ -0,0 +1,44 @@
1
+ // Copyright 2026 The Lynxtron Authors. All rights reserved.
2
+ // Licensed under the Apache License Version 2.0 that can be found in the
3
+ // LICENSE file in the root directory of this source tree.
4
+
5
+ import { EventEmitter } from 'node:events';
6
+ import { Menu } from './menu';
7
+ import { NativeImage } from './native-image';
8
+ import { Point } from '../structures/point';
9
+ import { Rectangle } from '../structures/rectangle';
10
+
11
+ export interface DisplayBalloonOptions {
12
+ icon?: NativeImage | string;
13
+ iconType?: 'none' | 'info' | 'warning' | 'error' | 'custom';
14
+ title: string;
15
+ content: string;
16
+ largeIcon?: boolean;
17
+ noSound?: boolean;
18
+ respectQuietTime?: boolean;
19
+ }
20
+
21
+ export interface TitleOptions {
22
+ fontType?: 'monospaced' | 'monospacedDigit';
23
+ }
24
+
25
+ export declare class Tray extends EventEmitter {
26
+ constructor(image: NativeImage | string, guid?: string);
27
+ destroy(): void;
28
+ isDestroyed(): boolean;
29
+ setImage(image: NativeImage | string): void;
30
+ setPressedImage(image: NativeImage | string): void;
31
+ setToolTip(toolTip: string): void;
32
+ setTitle(title: string, options?: TitleOptions): void;
33
+ getTitle(): string;
34
+ setIgnoreDoubleClickEvents(ignore: boolean): void;
35
+ getIgnoreDoubleClickEvents(): boolean;
36
+ setContextMenu(menu: Menu | null): void;
37
+ popUpContextMenu(menu?: Menu, position?: Point): void;
38
+ closeContextMenu(): void;
39
+ displayBalloon(options: DisplayBalloonOptions): void;
40
+ removeBalloon(): void;
41
+ focus(): void;
42
+ getBounds(): Rectangle;
43
+ getGUID(): string | null;
44
+ }
@@ -0,0 +1,73 @@
1
+ // Copyright 2026 The Lynxtron Authors. All rights reserved.
2
+ // Licensed under the Apache License Version 2.0 that can be found in the
3
+ // LICENSE file in the root directory of this source tree.
4
+
5
+ import { EventEmitter } from 'events';
6
+ import { Readable } from 'stream';
7
+
8
+ // UtilityProcess API
9
+
10
+ export class UtilityProcess extends EventEmitter {
11
+ /**
12
+ * The `PID` of the process.
13
+ */
14
+ readonly pid: number | undefined;
15
+ /**
16
+ * A `Readable Stream` that represents the process's stdout.
17
+ */
18
+ readonly stdout: Readable | null;
19
+ /**
20
+ * A `Readable Stream` that represents the process's stderr.
21
+ */
22
+ readonly stderr: Readable | null;
23
+ /**
24
+ * Terminates the process gracefully. On some operating systems, the process will be
25
+ * terminated immediately.
26
+ */
27
+ kill(): boolean;
28
+ /**
29
+ * Send a message to the child process.
30
+ */
31
+ postMessage(message: any, transfer?: any[]): void;
32
+ }
33
+
34
+ export namespace utilityProcess {
35
+ /**
36
+ * Spawns a new process that runs the specified `modulePath`.
37
+ */
38
+ function fork(
39
+ modulePath: string,
40
+ args?: string[],
41
+ options?: ForkOptions
42
+ ): UtilityProcess;
43
+ }
44
+
45
+ export interface ForkOptions {
46
+ /**
47
+ * Environment key-value pairs. Default is `process.env`.
48
+ */
49
+ env?: Record<string, string>;
50
+ /**
51
+ * List of string arguments passed to the executable.
52
+ */
53
+ execArgv?: string[];
54
+ /**
55
+ * Current working directory of the child process.
56
+ */
57
+ cwd?: string;
58
+ /**
59
+ * If `true`, the child's stdin, stdout, and stderr will be piped to the parent,
60
+ * otherwise they will be inherited from the parent. Default is `false`.
61
+ */
62
+ stdio?: 'pipe' | 'ignore' | 'inherit' | ('pipe' | 'ignore' | 'inherit')[];
63
+ /**
64
+ * Allows configuring the mode for `stdout` and `stderr` of the child process.
65
+ * Default is `inherit`. String value can be one of `pipe`, `ignore`, `inherit`.
66
+ */
67
+ serviceName?: string;
68
+ /**
69
+ * With this flag, the utility process will be launched via the `allow_child_plugin`
70
+ * mechanism of the `UtilityProcess` API.
71
+ */
72
+ allowLoadingUnsignedLibraries?: boolean;
73
+ }
package/apis/lynx.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import '@lynx-js/types/background';
2
+
3
+ export interface LynxtronNodejsExposed {
4
+ [key: string]: unknown;
5
+ }
6
+
7
+ export interface LynxtronNodejs {
8
+ exposed: LynxtronNodejsExposed;
9
+ }
10
+
11
+ declare module '@lynx-js/types/background' {
12
+ interface NativeModules {
13
+ nodejs: LynxtronNodejs;
14
+ }
15
+ }
@@ -0,0 +1,39 @@
1
+ // Copyright 2026 The Lynxtron Authors. All rights reserved.
2
+ // Licensed under the Apache License Version 2.0 that can be found in the
3
+ // LICENSE file in the root directory of this source tree.
4
+
5
+ // apis
6
+ export * from './api/app';
7
+ export * from './api/context-bridge';
8
+ export * from './api/native-image';
9
+ export * from './api/notification';
10
+ export * from './api/command-line';
11
+ export * from './api/menu';
12
+ export * from './api/tray';
13
+ export * from './api/notification-response';
14
+ export * from './api/process-metric';
15
+ export * from './api/task';
16
+ export * from './api/dock';
17
+ export * from './api/jump-list-item';
18
+ export * from './api/base-window';
19
+ export * from './api/clipboard';
20
+ export * from './api/event';
21
+ export * from './api/lynx-window';
22
+ export * from './api/lynx-library';
23
+ export * from './api/touch-bar';
24
+ export * from './api/screen';
25
+ export * from './api/asar';
26
+ export * from './api/dialog';
27
+ export * from './api/devtool';
28
+ export * from './api/shell';
29
+ export * from './api/protocol';
30
+ export * from './api/environment';
31
+ export * from './api/utility-process';
32
+ export * from './api/lynx-template-bundle';
33
+ export * from './api/lynx-update-meta';
34
+ export * from './api/lynx-template-data';
35
+
36
+ // structures
37
+ export * from './structures/point';
38
+ export * from './structures/rectangle';
39
+ export * from './structures/size';
@@ -0,0 +1,10 @@
1
+ // Copyright 2026 The Lynxtron Authors. All rights reserved.
2
+ // Licensed under the Apache License Version 2.0 that can be found in the
3
+ // LICENSE file in the root directory of this source tree.
4
+
5
+ export interface Point {
6
+ // Docs: https://electronjs.org/docs/api/structures/point
7
+
8
+ x: number;
9
+ y: number;
10
+ }
@@ -0,0 +1,22 @@
1
+ // Copyright 2026 The Lynxtron Authors. All rights reserved.
2
+ // Licensed under the Apache License Version 2.0 that can be found in the
3
+ // LICENSE file in the root directory of this source tree.
4
+
5
+ export interface Rectangle {
6
+ /**
7
+ * The height of the rectangle (must be an integer).
8
+ */
9
+ height: number;
10
+ /**
11
+ * The width of the rectangle (must be an integer).
12
+ */
13
+ width: number;
14
+ /**
15
+ * The x coordinate of the origin of the rectangle (must be an integer).
16
+ */
17
+ x: number;
18
+ /**
19
+ * The y coordinate of the origin of the rectangle (must be an integer).
20
+ */
21
+ y: number;
22
+ }
@@ -0,0 +1,8 @@
1
+ // Copyright 2026 The Lynxtron Authors. All rights reserved.
2
+ // Licensed under the Apache License Version 2.0 that can be found in the
3
+ // LICENSE file in the root directory of this source tree.
4
+
5
+ export interface Size {
6
+ height: number;
7
+ width: number;
8
+ }
@@ -0,0 +1,24 @@
1
+ // Copyright 2026 The Lynxtron Authors. All rights reserved.
2
+ // Licensed under the Apache License Version 2.0 that can be found in the
3
+ // LICENSE file in the root directory of this source tree.
4
+
5
+ export interface MainThreadBridge {
6
+ [methodName: string]: (data: any) => Promise<any> | any;
7
+ }
8
+
9
+ export interface BackgroundServiceConfig {
10
+ workerURL: string;
11
+ methods: string[];
12
+ }
13
+
14
+ export interface HostConfig {
15
+ bridge?: MainThreadBridge;
16
+ nodejs?: BackgroundServiceConfig;
17
+ }
18
+
19
+ /**
20
+ * Setup Symmetric Host for Web.
21
+ * @param lynxView The <lynx-view> element.
22
+ * @param config Configuration for bridge and nodejs.
23
+ */
24
+ export function setupSymmetricHost(lynxView: any, config?: HostConfig): void;
package/cli.js ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+
3
+ import proc from 'node:child_process';
4
+ import lynxtron from './lynxtron_bin.js';
5
+
6
+ const args = process.argv.slice(2);
7
+ console.log('spawning', lynxtron, process.argv[2]);
8
+ console.log("args", args);
9
+
10
+ const child = proc.spawn(lynxtron, args, { stdio: 'inherit', windowsHide: false });
11
+ child.on('close', function (code, signal) {
12
+ if (code === null) {
13
+ console.error(lynxtron, 'exited with signal', signal);
14
+ process.exit(1);
15
+ }
16
+ process.exit(code);
17
+ });
18
+
19
+ const handleTerminationSignal = function (signal) {
20
+ process.on(signal, function signalHandler () {
21
+ if (!child.killed) {
22
+ child.kill(signal);
23
+ }
24
+ });
25
+ };
26
+
27
+ handleTerminationSignal('SIGINT');
28
+ handleTerminationSignal('SIGTERM');
@@ -0,0 +1,6 @@
1
+ // @ts-nocheck
2
+ import { createRequire } from 'module';
3
+ const require = createRequire(import.meta.url);
4
+ const lynxtron = require('lynxtron');
5
+
6
+ export const contextBridge = lynxtron.contextBridge;
package/fuses-cli.js ADDED
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ flipFuses,
5
+ FuseV1Options,
6
+ FuseVersion,
7
+ getCurrentFuses,
8
+ } from './fuses.js';
9
+
10
+ function printUsage() {
11
+ console.log(`Usage:
12
+ lynxtron-fuses read [--app <path>] [--binary <path>] [--json]
13
+ lynxtron-fuses write [--app <path>] [--binary <path>] <fuse=on|off>...
14
+
15
+ --app prefers the macOS framework binary or Windows lynxtron.dll when given an app path.
16
+
17
+ Supported fuses:
18
+ ${FuseV1Options.RunAsNode}
19
+ ${FuseV1Options.EnableNodeOptionsEnvironmentVariable}
20
+ ${FuseV1Options.EnableNodeCliInspectArguments}
21
+ ${FuseV1Options.EnableEmbeddedAsarIntegrityValidation}
22
+ ${FuseV1Options.OnlyLoadAppFromAsar}`);
23
+ }
24
+
25
+ function parseTargetAndFlags(args) {
26
+ const target = {};
27
+ const rest = [];
28
+ let json = false;
29
+
30
+ for (let index = 0; index < args.length; index += 1) {
31
+ const arg = args[index];
32
+ if (arg === '--app' || arg === '--binary') {
33
+ const value = args[index + 1];
34
+ if (!value) {
35
+ throw new Error(`Missing value for ${arg}`);
36
+ }
37
+ target[arg.slice(2)] = value;
38
+ index += 1;
39
+ continue;
40
+ }
41
+
42
+ if (arg === '--json') {
43
+ json = true;
44
+ continue;
45
+ }
46
+
47
+ rest.push(arg);
48
+ }
49
+
50
+ return { target, json, rest };
51
+ }
52
+
53
+ function parseFuseValue(rawValue) {
54
+ switch (rawValue) {
55
+ case '1':
56
+ case 'on':
57
+ case 'true':
58
+ case 'enabled':
59
+ return true;
60
+ case '0':
61
+ case 'off':
62
+ case 'false':
63
+ case 'disabled':
64
+ return false;
65
+ default:
66
+ throw new Error(`Unsupported fuse value "${rawValue}"`);
67
+ }
68
+ }
69
+
70
+ function parseAssignments(args) {
71
+ const config = { version: FuseVersion.V1 };
72
+
73
+ for (const arg of args) {
74
+ const separatorIndex = arg.indexOf('=');
75
+ if (separatorIndex === -1) {
76
+ throw new Error(`Expected <fuse=value>, got "${arg}"`);
77
+ }
78
+
79
+ const key = arg.slice(0, separatorIndex);
80
+ const rawValue = arg.slice(separatorIndex + 1).toLowerCase();
81
+ config[key] = parseFuseValue(rawValue);
82
+ }
83
+
84
+ return config;
85
+ }
86
+
87
+ function formatFuses(fuses) {
88
+ return [
89
+ `binaryPath: ${fuses.binaryPath}`,
90
+ `version: ${fuses.version}`,
91
+ `${FuseV1Options.RunAsNode}: ${fuses.runAsNode ? 'on' : 'off'}`,
92
+ `${FuseV1Options.EnableNodeOptionsEnvironmentVariable}: ${fuses.nodeOptions ? 'on' : 'off'}`,
93
+ `${FuseV1Options.EnableNodeCliInspectArguments}: ${fuses.nodeCliInspect ? 'on' : 'off'}`,
94
+ `${FuseV1Options.EnableEmbeddedAsarIntegrityValidation}: ${fuses.embeddedAsarIntegrityValidation ? 'on' : 'off'}`,
95
+ `${FuseV1Options.OnlyLoadAppFromAsar}: ${fuses.onlyLoadAppFromAsar ? 'on' : 'off'}`,
96
+ ].join('\n');
97
+ }
98
+
99
+ function getResignHint(binaryPath) {
100
+ const normalizedBinaryPath = binaryPath.toLowerCase();
101
+ if (normalizedBinaryPath.endsWith('.exe')) {
102
+ return 'Fuse bytes were updated. Re-sign the Windows executable before distributing it.';
103
+ }
104
+
105
+ return 'Fuse bytes were updated. Re-sign the app or binary before distributing it.';
106
+ }
107
+
108
+ async function main() {
109
+ const [command, ...args] = process.argv.slice(2);
110
+
111
+ if (!command || command === '--help' || command === '-h') {
112
+ printUsage();
113
+ return;
114
+ }
115
+
116
+ if (command !== 'read' && command !== 'write') {
117
+ throw new Error(`Unknown command "${command}"`);
118
+ }
119
+
120
+ const { target, json, rest } = parseTargetAndFlags(args);
121
+
122
+ if (command === 'read') {
123
+ const fuses = await getCurrentFuses(target);
124
+ console.log(json ? JSON.stringify(fuses, null, 2) : formatFuses(fuses));
125
+ return;
126
+ }
127
+
128
+ if (rest.length === 0) {
129
+ throw new Error('write requires at least one <fuse=on|off> assignment');
130
+ }
131
+
132
+ const fuses = await flipFuses(target, parseAssignments(rest));
133
+ console.log(json ? JSON.stringify(fuses, null, 2) : formatFuses(fuses));
134
+
135
+ if (process.platform === 'darwin' || fuses.binaryPath.toLowerCase().endsWith('.exe')) {
136
+ console.error(getResignHint(fuses.binaryPath));
137
+ }
138
+ }
139
+
140
+ main().catch((error) => {
141
+ console.error(error instanceof Error ? error.message : error);
142
+ process.exit(1);
143
+ });