forkoff 1.0.11 → 1.0.13

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 (57) hide show
  1. package/README.md +7 -4
  2. package/dist/__tests__/cli-commands.test.d.ts +6 -0
  3. package/dist/__tests__/cli-commands.test.d.ts.map +1 -0
  4. package/dist/__tests__/cli-commands.test.js +213 -0
  5. package/dist/__tests__/cli-commands.test.js.map +1 -0
  6. package/dist/__tests__/startup.test.d.ts +11 -0
  7. package/dist/__tests__/startup.test.d.ts.map +1 -0
  8. package/dist/__tests__/startup.test.js +234 -0
  9. package/dist/__tests__/startup.test.js.map +1 -0
  10. package/dist/__tests__/tools/claude-process.test.js +221 -15
  11. package/dist/__tests__/tools/claude-process.test.js.map +1 -1
  12. package/dist/__tests__/tools/permission-hook.test.d.ts +17 -0
  13. package/dist/__tests__/tools/permission-hook.test.d.ts.map +1 -0
  14. package/dist/__tests__/tools/permission-hook.test.js +616 -0
  15. package/dist/__tests__/tools/permission-hook.test.js.map +1 -0
  16. package/dist/__tests__/tools/permission-ipc.test.d.ts +11 -0
  17. package/dist/__tests__/tools/permission-ipc.test.d.ts.map +1 -0
  18. package/dist/__tests__/tools/permission-ipc.test.js +612 -0
  19. package/dist/__tests__/tools/permission-ipc.test.js.map +1 -0
  20. package/dist/config.js +1 -1
  21. package/dist/index.d.ts +2 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1010 -898
  24. package/dist/index.js.map +1 -1
  25. package/dist/startup.d.ts.map +1 -1
  26. package/dist/startup.js +45 -15
  27. package/dist/startup.js.map +1 -1
  28. package/dist/tools/__tests__/claude-sessions.test.d.ts +2 -0
  29. package/dist/tools/__tests__/claude-sessions.test.d.ts.map +1 -0
  30. package/dist/tools/__tests__/claude-sessions.test.js +306 -0
  31. package/dist/tools/__tests__/claude-sessions.test.js.map +1 -0
  32. package/dist/tools/claude-process.d.ts +81 -4
  33. package/dist/tools/claude-process.d.ts.map +1 -1
  34. package/dist/tools/claude-process.js +332 -20
  35. package/dist/tools/claude-process.js.map +1 -1
  36. package/dist/tools/claude-sessions.d.ts +5 -0
  37. package/dist/tools/claude-sessions.d.ts.map +1 -1
  38. package/dist/tools/claude-sessions.js +16 -2
  39. package/dist/tools/claude-sessions.js.map +1 -1
  40. package/dist/tools/index.d.ts +1 -0
  41. package/dist/tools/index.d.ts.map +1 -1
  42. package/dist/tools/index.js +3 -1
  43. package/dist/tools/index.js.map +1 -1
  44. package/dist/tools/permission-hook.d.ts +41 -0
  45. package/dist/tools/permission-hook.d.ts.map +1 -0
  46. package/dist/tools/permission-hook.js +312 -0
  47. package/dist/tools/permission-hook.js.map +1 -0
  48. package/dist/tools/permission-ipc.d.ts +109 -0
  49. package/dist/tools/permission-ipc.d.ts.map +1 -0
  50. package/dist/tools/permission-ipc.js +295 -0
  51. package/dist/tools/permission-ipc.js.map +1 -0
  52. package/dist/websocket.d.ts +14 -0
  53. package/dist/websocket.d.ts.map +1 -1
  54. package/dist/websocket.js +34 -4
  55. package/dist/websocket.js.map +1 -1
  56. package/jest.config.js +3 -0
  57. package/package.json +1 -1
package/README.md CHANGED
@@ -79,6 +79,7 @@ Keep this running to receive commands from your mobile app.
79
79
  | `forkoff startup --enable` | Enable automatic startup |
80
80
  | `forkoff startup --disable` | Disable automatic startup |
81
81
  | `forkoff startup --status` | Show startup registration status |
82
+ | `forkoff help` | Show available commands and usage |
82
83
 
83
84
  ### Configuration Options
84
85
 
@@ -117,12 +118,12 @@ This is used by the automatic startup feature and is useful for running ForkOff
117
118
 
118
119
  ### Automatic Startup
119
120
 
120
- When you run `forkoff pair` or `forkoff connect`, ForkOff automatically registers itself to start on login. This means you don't need to manually run `forkoff connect` every time you start your computer.
121
+ ForkOff automatically starts on login so your device stays connected without manual intervention. Startup is **enabled by default** — when you run `forkoff pair` or `forkoff connect`, it registers itself to launch `forkoff connect --quiet` on login.
121
122
 
122
- - **Windows**: Uses Task Scheduler (`ForkOffCLI` task, runs on logon)
123
- - **macOS**: Uses launchd (`~/Library/LaunchAgents/app.forkoff.cli.plist`)
123
+ - **Windows**: Adds a `ForkOffCLI` entry to the `HKCU\...\Run` registry key (no admin required)
124
+ - **macOS**: Installs a launchd agent (`~/Library/LaunchAgents/app.forkoff.cli.plist`) with the explicit node binary path for nvm/fnm compatibility
124
125
 
125
- To opt out:
126
+ To disable automatic startup:
126
127
 
127
128
  ```bash
128
129
  forkoff startup --disable
@@ -134,6 +135,8 @@ Once disabled, `pair` and `connect` will not re-register startup. To re-enable:
134
135
  forkoff startup --enable
135
136
  ```
136
137
 
138
+ Running `forkoff disconnect` also removes the startup registration.
139
+
137
140
  ---
138
141
 
139
142
  ## Programmatic Usage
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Tests for CLI commands: help, unknown command, version
3
+ * Uses createProgram() exported from index.ts
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=cli-commands.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-commands.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cli-commands.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ /**
3
+ * Tests for CLI commands: help, unknown command, version
4
+ * Uses createProgram() exported from index.ts
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ // Mock all heavy dependencies so importing index.ts doesn't trigger real connections
8
+ jest.mock('../config', () => ({
9
+ config: {
10
+ deviceId: null,
11
+ deviceName: 'test',
12
+ apiUrl: 'http://localhost:3000',
13
+ wsUrl: 'ws://localhost:3000',
14
+ pairingCode: null,
15
+ pairedAt: null,
16
+ userId: null,
17
+ isPaired: false,
18
+ startupEnabled: null,
19
+ startupBinaryPath: null,
20
+ getPath: () => '/tmp/config.json',
21
+ reset: jest.fn(),
22
+ },
23
+ }));
24
+ jest.mock('../api', () => ({
25
+ api: {
26
+ healthCheck: jest.fn(),
27
+ registerDevice: jest.fn(),
28
+ refreshPairingCode: jest.fn(),
29
+ checkPairingStatus: jest.fn(),
30
+ reportConnectedTools: jest.fn(),
31
+ },
32
+ }));
33
+ jest.mock('../websocket', () => ({
34
+ wsClient: {
35
+ connect: jest.fn(),
36
+ disconnect: jest.fn(),
37
+ isConnected: false,
38
+ on: jest.fn(),
39
+ sendTerminalOutput: jest.fn(),
40
+ sendTerminalCwd: jest.fn(),
41
+ sendClaudeSessionUpdate: jest.fn(),
42
+ sendClaudeSessions: jest.fn(),
43
+ sendToolStatusUpdate: jest.fn(),
44
+ sendDirectoryListResponse: jest.fn(),
45
+ sendReadFileResponse: jest.fn(),
46
+ sendTranscriptHistory: jest.fn(),
47
+ sendTranscriptUpdate: jest.fn(),
48
+ sendClaudeApprovalRequest: jest.fn(),
49
+ sendToolActivity: jest.fn(),
50
+ sendPermissionPrompt: jest.fn(),
51
+ sendClaudeSessionEvent: jest.fn(),
52
+ sendRpcResponse: jest.fn(),
53
+ sendPendingPermissionsSync: jest.fn(),
54
+ sendThinkingContent: jest.fn(),
55
+ sendTokenUsage: jest.fn(),
56
+ sendTaskProgress: jest.fn(),
57
+ },
58
+ }));
59
+ jest.mock('../terminal', () => ({
60
+ terminalManager: {
61
+ on: jest.fn(),
62
+ createSession: jest.fn(),
63
+ executeCommand: jest.fn(),
64
+ },
65
+ }));
66
+ jest.mock('../approval', () => ({
67
+ approvalManager: {
68
+ on: jest.fn(),
69
+ handleApprovalResponse: jest.fn(),
70
+ },
71
+ }));
72
+ jest.mock('../tools', () => ({
73
+ toolDetector: { detectAll: jest.fn(), watchToolStatus: jest.fn() },
74
+ claudeHooksManager: { canConfigure: jest.fn(), installHooks: jest.fn(), uninstallHooks: jest.fn(), isHookConfigured: jest.fn() },
75
+ claudeSessionDetector: {
76
+ isClaudeInstalled: jest.fn().mockReturnValue(false),
77
+ scanSessions: jest.fn().mockReturnValue([]),
78
+ getSessions: jest.fn().mockReturnValue([]),
79
+ startWatching: jest.fn(),
80
+ stopWatching: jest.fn(),
81
+ seedKnownSessions: jest.fn(),
82
+ on: jest.fn(),
83
+ },
84
+ claudeProcessManager: {
85
+ isClaudeSession: jest.fn(),
86
+ sendInput: jest.fn(),
87
+ registerSession: jest.fn(),
88
+ markTakenOver: jest.fn(),
89
+ isTakenOver: jest.fn(),
90
+ startSession: jest.fn(),
91
+ startAndSendMessage: jest.fn(),
92
+ handleApprovalResponse: jest.fn(),
93
+ handlePermissionResponse: jest.fn(),
94
+ updatePermissionRules: jest.fn(),
95
+ clearAllTakenOver: jest.fn(),
96
+ autoAllowAllPendingPrompts: jest.fn(),
97
+ cleanupAllPermissionState: jest.fn(),
98
+ getAllPendingPrompts: jest.fn().mockReturnValue([]),
99
+ on: jest.fn(),
100
+ },
101
+ PermissionIpcManager: {
102
+ cleanupStaleTempFiles: jest.fn(),
103
+ },
104
+ }));
105
+ jest.mock('../transcript-streamer', () => ({
106
+ transcriptStreamer: {
107
+ fetchHistory: jest.fn(),
108
+ subscribeToUpdates: jest.fn(),
109
+ unsubscribeFromUpdates: jest.fn(),
110
+ cleanup: jest.fn(),
111
+ on: jest.fn(),
112
+ },
113
+ }));
114
+ jest.mock('../logger', () => ({
115
+ setQuiet: jest.fn(),
116
+ createSpinner: jest.fn(() => ({
117
+ start: jest.fn().mockReturnThis(),
118
+ stop: jest.fn(),
119
+ succeed: jest.fn(),
120
+ fail: jest.fn(),
121
+ info: jest.fn(),
122
+ warn: jest.fn(),
123
+ text: '',
124
+ })),
125
+ }));
126
+ jest.mock('chalk', () => {
127
+ const handler = {
128
+ get: () => new Proxy((s) => s, handler),
129
+ apply: (_target, _thisArg, args) => args[0],
130
+ };
131
+ return new Proxy((s) => s, handler);
132
+ });
133
+ jest.mock('qrcode-terminal', () => ({
134
+ generate: jest.fn(),
135
+ }));
136
+ jest.mock('../startup', () => ({
137
+ enableStartup: jest.fn(),
138
+ disableStartup: jest.fn(),
139
+ isStartupRegistered: jest.fn().mockReturnValue(false),
140
+ getBinaryPath: jest.fn().mockReturnValue('/usr/local/bin/forkoff'),
141
+ }));
142
+ const index_1 = require("../index");
143
+ describe('CLI commands', () => {
144
+ let consoleLogSpy;
145
+ let consoleErrorSpy;
146
+ beforeEach(() => {
147
+ consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
148
+ consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
149
+ process.exitCode = undefined;
150
+ });
151
+ afterEach(() => {
152
+ consoleLogSpy.mockRestore();
153
+ consoleErrorSpy.mockRestore();
154
+ process.exitCode = undefined;
155
+ });
156
+ describe('help command', () => {
157
+ it('outputs available commands', async () => {
158
+ const program = (0, index_1.createProgram)();
159
+ // Prevent commander from calling process.exit on help
160
+ program.exitOverride();
161
+ let helpOutput = '';
162
+ program.configureOutput({
163
+ writeOut: (str) => { helpOutput += str; },
164
+ writeErr: (str) => { helpOutput += str; },
165
+ });
166
+ try {
167
+ await program.parseAsync(['node', 'forkoff', 'help']);
168
+ }
169
+ catch {
170
+ // Commander throws on exitOverride
171
+ }
172
+ // The help action calls program.outputHelp(), check console.log calls
173
+ const allOutput = helpOutput + consoleLogSpy.mock.calls.map((c) => c.join(' ')).join('\n');
174
+ expect(allOutput).toContain('pair');
175
+ expect(allOutput).toContain('connect');
176
+ expect(allOutput).toContain('disconnect');
177
+ expect(allOutput).toContain('status');
178
+ expect(allOutput).toContain('startup');
179
+ expect(allOutput).toContain('config');
180
+ expect(allOutput).toContain('tools');
181
+ expect(allOutput).toContain('help');
182
+ });
183
+ });
184
+ describe('--version flag', () => {
185
+ it('shows version from package.json', async () => {
186
+ const program = (0, index_1.createProgram)();
187
+ program.exitOverride();
188
+ let versionOutput = '';
189
+ program.configureOutput({
190
+ writeOut: (str) => { versionOutput += str; },
191
+ writeErr: (str) => { versionOutput += str; },
192
+ });
193
+ try {
194
+ await program.parseAsync(['node', 'forkoff', '--version']);
195
+ }
196
+ catch {
197
+ // Commander throws on exitOverride
198
+ }
199
+ const pkg = require('../../package.json');
200
+ expect(versionOutput).toContain(pkg.version);
201
+ });
202
+ });
203
+ describe('unknown command', () => {
204
+ it('sets process.exitCode = 1 and shows error message', async () => {
205
+ const program = (0, index_1.createProgram)();
206
+ await program.parseAsync(['node', 'forkoff', 'gibberish']);
207
+ expect(process.exitCode).toBe(1);
208
+ const errorOutput = consoleErrorSpy.mock.calls.map((c) => c.join(' ')).join('\n');
209
+ expect(errorOutput).toContain('Unknown command: gibberish');
210
+ });
211
+ });
212
+ });
213
+ //# sourceMappingURL=cli-commands.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-commands.test.js","sourceRoot":"","sources":["../../src/__tests__/cli-commands.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tests for startup module
3
+ * Covers:
4
+ * - Windows: .bat wrapper + HKCU Run registry key (no admin needed)
5
+ * - macOS: plist with explicit node path (nvm/fnm compatibility)
6
+ * - disableStartup cleans up .bat file on Windows
7
+ * - isStartupRegistered checks registry (win32) / plist existence (darwin)
8
+ * - getBinaryPath: cached, which/where, fallback to process.argv[1]
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=startup.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/startup.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ /**
3
+ * Tests for startup module
4
+ * Covers:
5
+ * - Windows: .bat wrapper + HKCU Run registry key (no admin needed)
6
+ * - macOS: plist with explicit node path (nvm/fnm compatibility)
7
+ * - disableStartup cleans up .bat file on Windows
8
+ * - isStartupRegistered checks registry (win32) / plist existence (darwin)
9
+ * - getBinaryPath: cached, which/where, fallback to process.argv[1]
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const mockExecSync = jest.fn();
13
+ jest.mock('child_process', () => ({
14
+ execSync: mockExecSync,
15
+ }));
16
+ const mockExistsSync = jest.fn();
17
+ const mockWriteFileSync = jest.fn();
18
+ const mockUnlinkSync = jest.fn();
19
+ const mockMkdirSync = jest.fn();
20
+ jest.mock('fs', () => ({
21
+ existsSync: mockExistsSync,
22
+ writeFileSync: mockWriteFileSync,
23
+ unlinkSync: mockUnlinkSync,
24
+ mkdirSync: mockMkdirSync,
25
+ }));
26
+ const mockConfig = {
27
+ startupBinaryPath: null,
28
+ startupEnabled: null,
29
+ };
30
+ jest.mock('../config', () => ({
31
+ config: mockConfig,
32
+ }));
33
+ const startup_1 = require("../startup");
34
+ describe('startup', () => {
35
+ const originalPlatform = process.platform;
36
+ const originalExecPath = process.execPath;
37
+ const originalArgv = [...process.argv];
38
+ function setPlatform(platform) {
39
+ Object.defineProperty(process, 'platform', { value: platform, configurable: true });
40
+ }
41
+ beforeEach(() => {
42
+ // resetAllMocks clears calls AND implementations (unlike clearAllMocks)
43
+ jest.resetAllMocks();
44
+ mockConfig.startupBinaryPath = null;
45
+ mockConfig.startupEnabled = null;
46
+ mockExistsSync.mockReturnValue(false);
47
+ });
48
+ afterEach(() => {
49
+ Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
50
+ Object.defineProperty(process, 'execPath', { value: originalExecPath, configurable: true });
51
+ process.argv = [...originalArgv];
52
+ });
53
+ describe('getBinaryPath', () => {
54
+ it('returns cached path when it exists on disk', () => {
55
+ mockConfig.startupBinaryPath = '/usr/local/bin/forkoff';
56
+ mockExistsSync.mockReturnValue(true);
57
+ expect((0, startup_1.getBinaryPath)()).toBe('/usr/local/bin/forkoff');
58
+ expect(mockExecSync).not.toHaveBeenCalled();
59
+ });
60
+ it('finds via which on darwin', () => {
61
+ setPlatform('darwin');
62
+ mockConfig.startupBinaryPath = null;
63
+ mockExecSync.mockReturnValue('/usr/local/bin/forkoff\n');
64
+ // startupBinaryPath is null → short-circuit, existsSync NOT called for cache.
65
+ // First existsSync call is for the which result → return true.
66
+ mockExistsSync.mockReturnValueOnce(true);
67
+ expect((0, startup_1.getBinaryPath)()).toBe('/usr/local/bin/forkoff');
68
+ expect(mockExecSync).toHaveBeenCalledWith('which forkoff', { encoding: 'utf-8' });
69
+ expect(mockConfig.startupBinaryPath).toBe('/usr/local/bin/forkoff');
70
+ });
71
+ it('finds via where on win32', () => {
72
+ setPlatform('win32');
73
+ mockConfig.startupBinaryPath = null;
74
+ mockExecSync.mockReturnValue('C:\\Program Files\\nodejs\\forkoff\r\n');
75
+ mockExistsSync.mockReturnValueOnce(true);
76
+ expect((0, startup_1.getBinaryPath)()).toBe('C:\\Program Files\\nodejs\\forkoff');
77
+ expect(mockExecSync).toHaveBeenCalledWith('where forkoff', { encoding: 'utf-8' });
78
+ });
79
+ it('falls back to process.argv[1] when which/where fails', () => {
80
+ setPlatform('darwin');
81
+ mockConfig.startupBinaryPath = null;
82
+ mockExecSync.mockImplementation(() => { throw new Error('not found'); });
83
+ process.argv = ['node', '/home/user/.nvm/versions/node/bin/forkoff'];
84
+ expect((0, startup_1.getBinaryPath)()).toBe('/home/user/.nvm/versions/node/bin/forkoff');
85
+ expect(mockConfig.startupBinaryPath).toBe('/home/user/.nvm/versions/node/bin/forkoff');
86
+ });
87
+ it('throws when no binary can be found', () => {
88
+ mockConfig.startupBinaryPath = null;
89
+ mockExecSync.mockImplementation(() => { throw new Error('not found'); });
90
+ process.argv = ['node']; // No argv[1]
91
+ expect(() => (0, startup_1.getBinaryPath)()).toThrow('Could not determine forkoff binary path');
92
+ });
93
+ });
94
+ describe('isStartupRegistered', () => {
95
+ it('returns true on win32 when registry key exists', () => {
96
+ setPlatform('win32');
97
+ mockExecSync.mockReturnValue('');
98
+ expect((0, startup_1.isStartupRegistered)()).toBe(true);
99
+ expect(mockExecSync).toHaveBeenCalledWith(expect.stringContaining('reg query'), { stdio: 'pipe' });
100
+ });
101
+ it('returns false on win32 when registry key missing', () => {
102
+ setPlatform('win32');
103
+ mockExecSync.mockImplementation(() => { throw new Error('not found'); });
104
+ expect((0, startup_1.isStartupRegistered)()).toBe(false);
105
+ });
106
+ it('returns true on darwin when plist file exists', () => {
107
+ setPlatform('darwin');
108
+ mockExistsSync.mockReturnValue(true);
109
+ expect((0, startup_1.isStartupRegistered)()).toBe(true);
110
+ });
111
+ it('returns false on darwin when plist file does not exist', () => {
112
+ setPlatform('darwin');
113
+ mockExistsSync.mockReturnValue(false);
114
+ expect((0, startup_1.isStartupRegistered)()).toBe(false);
115
+ });
116
+ it('returns false on unsupported platform', () => {
117
+ setPlatform('linux');
118
+ expect((0, startup_1.isStartupRegistered)()).toBe(false);
119
+ });
120
+ });
121
+ describe('enableStartup (win32)', () => {
122
+ beforeEach(() => {
123
+ setPlatform('win32');
124
+ Object.defineProperty(process, 'execPath', {
125
+ value: 'C:\\Program Files\\nodejs\\node.exe',
126
+ configurable: true,
127
+ });
128
+ mockConfig.startupBinaryPath = 'C:\\Users\\test\\AppData\\Roaming\\npm\\forkoff';
129
+ mockExistsSync.mockReturnValue(true);
130
+ });
131
+ it('writes .bat wrapper with correct content', async () => {
132
+ await (0, startup_1.enableStartup)();
133
+ const batCall = mockWriteFileSync.mock.calls.find((call) => typeof call[0] === 'string' && call[0].endsWith('.bat'));
134
+ expect(batCall).toBeDefined();
135
+ const batContent = batCall[1];
136
+ expect(batContent).toContain('@echo off');
137
+ expect(batContent).toContain('"C:\\Program Files\\nodejs\\node.exe"');
138
+ expect(batContent).toContain('"C:\\Users\\test\\AppData\\Roaming\\npm\\forkoff"');
139
+ expect(batContent).toContain('connect --quiet');
140
+ });
141
+ it('adds registry Run key with .bat path', async () => {
142
+ await (0, startup_1.enableStartup)();
143
+ const regCall = mockExecSync.mock.calls.find((call) => typeof call[0] === 'string' && call[0].includes('reg add'));
144
+ expect(regCall).toBeDefined();
145
+ expect(regCall[0]).toContain('startup.bat');
146
+ expect(regCall[0]).toContain('ForkOffCLI');
147
+ });
148
+ it('sets config.startupEnabled = true', async () => {
149
+ await (0, startup_1.enableStartup)();
150
+ expect(mockConfig.startupEnabled).toBe(true);
151
+ });
152
+ });
153
+ describe('enableStartup (darwin)', () => {
154
+ beforeEach(() => {
155
+ setPlatform('darwin');
156
+ Object.defineProperty(process, 'execPath', {
157
+ value: '/Users/test/.nvm/versions/node/v20.0.0/bin/node',
158
+ configurable: true,
159
+ });
160
+ mockConfig.startupBinaryPath = '/usr/local/bin/forkoff';
161
+ mockExistsSync.mockReturnValue(true);
162
+ });
163
+ it('writes plist with process.execPath as first ProgramArgument', async () => {
164
+ await (0, startup_1.enableStartup)();
165
+ const plistCall = mockWriteFileSync.mock.calls.find((call) => typeof call[0] === 'string' && call[0].endsWith('.plist'));
166
+ expect(plistCall).toBeDefined();
167
+ const plistContent = plistCall[1];
168
+ const argsMatch = plistContent.match(/<array>([\s\S]*?)<\/array>/);
169
+ expect(argsMatch).toBeDefined();
170
+ const strings = [...argsMatch[1].matchAll(/<string>(.*?)<\/string>/g)].map(m => m[1]);
171
+ expect(strings[0]).toBe('/Users/test/.nvm/versions/node/v20.0.0/bin/node');
172
+ expect(strings[1]).toBe('/usr/local/bin/forkoff');
173
+ expect(strings[2]).toBe('connect');
174
+ expect(strings[3]).toBe('--quiet');
175
+ });
176
+ it('includes node directory in PATH when not in default PATH', async () => {
177
+ await (0, startup_1.enableStartup)();
178
+ const plistCall = mockWriteFileSync.mock.calls.find((call) => typeof call[0] === 'string' && call[0].endsWith('.plist'));
179
+ const plistContent = plistCall[1];
180
+ expect(plistContent).toContain('/Users/test/.nvm/versions/node/v20.0.0/bin:');
181
+ });
182
+ it('calls launchctl load', async () => {
183
+ await (0, startup_1.enableStartup)();
184
+ const loadCall = mockExecSync.mock.calls.find((call) => typeof call[0] === 'string' && call[0].includes('launchctl load'));
185
+ expect(loadCall).toBeDefined();
186
+ });
187
+ it('sets config.startupEnabled = true', async () => {
188
+ await (0, startup_1.enableStartup)();
189
+ expect(mockConfig.startupEnabled).toBe(true);
190
+ });
191
+ });
192
+ describe('disableStartup (win32)', () => {
193
+ beforeEach(() => {
194
+ setPlatform('win32');
195
+ });
196
+ it('removes registry Run key', async () => {
197
+ await (0, startup_1.disableStartup)();
198
+ const regCall = mockExecSync.mock.calls.find((call) => typeof call[0] === 'string' && call[0].includes('reg delete'));
199
+ expect(regCall).toBeDefined();
200
+ expect(regCall[0]).toContain('ForkOffCLI');
201
+ });
202
+ it('removes .bat file if it exists', async () => {
203
+ mockExistsSync.mockReturnValue(true);
204
+ await (0, startup_1.disableStartup)();
205
+ expect(mockUnlinkSync).toHaveBeenCalledWith(expect.stringContaining('startup.bat'));
206
+ });
207
+ it('sets config.startupEnabled = false', async () => {
208
+ await (0, startup_1.disableStartup)();
209
+ expect(mockConfig.startupEnabled).toBe(false);
210
+ });
211
+ });
212
+ describe('disableStartup (darwin)', () => {
213
+ beforeEach(() => {
214
+ setPlatform('darwin');
215
+ });
216
+ it('calls launchctl unload and removes plist when it exists', async () => {
217
+ mockExistsSync.mockReturnValue(true);
218
+ await (0, startup_1.disableStartup)();
219
+ const unloadCall = mockExecSync.mock.calls.find((call) => typeof call[0] === 'string' && call[0].includes('launchctl unload'));
220
+ expect(unloadCall).toBeDefined();
221
+ expect(mockUnlinkSync).toHaveBeenCalledWith(expect.stringContaining('.plist'));
222
+ });
223
+ it('does nothing when plist does not exist', async () => {
224
+ mockExistsSync.mockReturnValue(false);
225
+ await (0, startup_1.disableStartup)();
226
+ expect(mockUnlinkSync).not.toHaveBeenCalled();
227
+ });
228
+ it('sets config.startupEnabled = false', async () => {
229
+ await (0, startup_1.disableStartup)();
230
+ expect(mockConfig.startupEnabled).toBe(false);
231
+ });
232
+ });
233
+ });
234
+ //# sourceMappingURL=startup.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup.test.js","sourceRoot":"","sources":["../../src/__tests__/startup.test.ts"],"names":[],"mappings":"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"}