forkit-connect 0.1.21 → 0.1.23

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.
@@ -0,0 +1,23 @@
1
+ import { type ReleaseCheckResult } from './update';
2
+ export interface NativeShellStatus {
3
+ supported: boolean;
4
+ installed: boolean;
5
+ platform: 'macos' | 'windows' | 'linux';
6
+ installationPath: string | null;
7
+ trayAvailable: boolean;
8
+ startupAvailable: boolean;
9
+ directInstallerAvailable: boolean;
10
+ downloadUrl: string | null;
11
+ releaseNotesUrl: string | null;
12
+ note: string;
13
+ }
14
+ export interface NativeShellOpenResult {
15
+ ok: boolean;
16
+ opened: 'installed' | 'download';
17
+ installationPath: string | null;
18
+ downloadUrl: string | null;
19
+ message: string;
20
+ }
21
+ export declare function getNativeShellStatus(updateCheck?: ReleaseCheckResult): Promise<NativeShellStatus>;
22
+ export declare function openNativeShell(updateCheck?: ReleaseCheckResult): Promise<NativeShellOpenResult>;
23
+ //# sourceMappingURL=native-shell.d.ts.map
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getNativeShellStatus = getNativeShellStatus;
7
+ exports.openNativeShell = openNativeShell;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_os_1 = __importDefault(require("node:os"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const node_child_process_1 = require("node:child_process");
12
+ const update_1 = require("./update");
13
+ const CONNECT_DESKTOP_FALLBACK_URL = 'https://www.forkit.dev/connect';
14
+ function normalizePlatform() {
15
+ if (process.platform === 'darwin')
16
+ return 'macos';
17
+ if (process.platform === 'win32')
18
+ return 'windows';
19
+ return 'linux';
20
+ }
21
+ function getCandidatePaths(platform) {
22
+ const home = node_os_1.default.homedir();
23
+ if (platform === 'macos') {
24
+ return [
25
+ '/Applications/Forkit Connect.app',
26
+ node_path_1.default.join(home, 'Applications', 'Forkit Connect.app'),
27
+ ];
28
+ }
29
+ if (platform === 'windows') {
30
+ const localAppData = process.env.LOCALAPPDATA || node_path_1.default.join(home, 'AppData', 'Local');
31
+ const programFiles = process.env.ProgramFiles || 'C:\\Program Files';
32
+ const programFilesX86 = process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)';
33
+ return [
34
+ node_path_1.default.join(localAppData, 'Programs', 'Forkit Connect', 'Forkit Connect.exe'),
35
+ node_path_1.default.join(programFiles, 'Forkit Connect', 'Forkit Connect.exe'),
36
+ node_path_1.default.join(programFilesX86, 'Forkit Connect', 'Forkit Connect.exe'),
37
+ ];
38
+ }
39
+ return [
40
+ node_path_1.default.join(home, '.local', 'bin', 'forkit-connect-desktop'),
41
+ node_path_1.default.join(home, '.local', 'share', 'Forkit Connect', 'Forkit Connect.AppImage'),
42
+ '/usr/local/bin/forkit-connect-desktop',
43
+ '/opt/Forkit Connect/forkit-connect-desktop',
44
+ ];
45
+ }
46
+ function findInstalledPath(platform) {
47
+ for (const candidate of getCandidatePaths(platform)) {
48
+ try {
49
+ if (node_fs_1.default.existsSync(candidate)) {
50
+ return candidate;
51
+ }
52
+ }
53
+ catch {
54
+ continue;
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+ function parseVersion(version) {
60
+ const cleaned = String(version || '').trim().replace(/^v/i, '').split('-')[0] || '0.0.0';
61
+ const [major, minor, patch] = cleaned.split('.').map((value) => Number.parseInt(value || '0', 10));
62
+ return [major || 0, minor || 0, patch || 0];
63
+ }
64
+ function compareVersions(left, right) {
65
+ const [leftMajor, leftMinor, leftPatch] = parseVersion(left);
66
+ const [rightMajor, rightMinor, rightPatch] = parseVersion(right);
67
+ if (leftMajor !== rightMajor)
68
+ return leftMajor - rightMajor;
69
+ if (leftMinor !== rightMinor)
70
+ return leftMinor - rightMinor;
71
+ return leftPatch - rightPatch;
72
+ }
73
+ function buildDownloadUrl(updateCheck) {
74
+ if (!updateCheck.ok) {
75
+ return CONNECT_DESKTOP_FALLBACK_URL;
76
+ }
77
+ if (compareVersions(updateCheck.manifest.latestVersion, updateCheck.currentVersion) < 0) {
78
+ return CONNECT_DESKTOP_FALLBACK_URL;
79
+ }
80
+ return updateCheck.downloadUrl || updateCheck.manifest.releaseNotesUrl || CONNECT_DESKTOP_FALLBACK_URL;
81
+ }
82
+ function hasDirectInstaller(updateCheck) {
83
+ if (!updateCheck.ok)
84
+ return false;
85
+ if (compareVersions(updateCheck.manifest.latestVersion, updateCheck.currentVersion) < 0) {
86
+ return false;
87
+ }
88
+ return Boolean(updateCheck.downloadUrl);
89
+ }
90
+ function isInstallerOnlyRelease(updateCheck) {
91
+ if (!updateCheck.ok)
92
+ return false;
93
+ const manifest = updateCheck.manifest;
94
+ const integrity = manifest.integrity;
95
+ if (!integrity || typeof integrity !== 'object')
96
+ return false;
97
+ return integrity.installerOnly === true;
98
+ }
99
+ async function getNativeShellStatus(updateCheck) {
100
+ const platform = normalizePlatform();
101
+ const installationPath = findInstalledPath(platform);
102
+ const installed = Boolean(installationPath);
103
+ const resolvedUpdateCheck = updateCheck ?? await (0, update_1.checkForUpdates)();
104
+ const directInstallerAvailable = hasDirectInstaller(resolvedUpdateCheck);
105
+ const installerOnlyRelease = isInstallerOnlyRelease(resolvedUpdateCheck);
106
+ const downloadUrl = buildDownloadUrl(resolvedUpdateCheck);
107
+ const releaseNotesUrl = resolvedUpdateCheck.ok ? resolvedUpdateCheck.manifest.releaseNotesUrl : CONNECT_DESKTOP_FALLBACK_URL;
108
+ return {
109
+ supported: true,
110
+ installed,
111
+ platform,
112
+ installationPath,
113
+ trayAvailable: installed,
114
+ startupAvailable: installed,
115
+ directInstallerAvailable,
116
+ downloadUrl,
117
+ releaseNotesUrl,
118
+ note: installed
119
+ ? 'Desktop app installed. Tray or menu-bar controls and launch at sign-in are handled there.'
120
+ : installerOnlyRelease
121
+ ? 'Install the desktop app to get tray or menu-bar controls and launch at sign-in. In-app updates are not published for this release yet.'
122
+ : directInstallerAvailable
123
+ ? 'Install the desktop app to get tray or menu-bar controls, launch at sign-in, and in-app updates.'
124
+ : 'The desktop app installer is not published for this platform yet. Use the Connect downloads page for the current release.',
125
+ };
126
+ }
127
+ function spawnDetached(command, args, cwd) {
128
+ const child = (0, node_child_process_1.spawn)(command, args, {
129
+ detached: true,
130
+ stdio: 'ignore',
131
+ windowsHide: true,
132
+ ...(cwd ? { cwd } : {}),
133
+ });
134
+ child.unref();
135
+ }
136
+ function openInstalledShell(status) {
137
+ if (!status.installationPath) {
138
+ throw new Error('native_shell_missing');
139
+ }
140
+ if (status.platform === 'macos') {
141
+ spawnDetached('open', [status.installationPath]);
142
+ return;
143
+ }
144
+ if (status.platform === 'windows') {
145
+ spawnDetached(status.installationPath, [], node_path_1.default.dirname(status.installationPath));
146
+ return;
147
+ }
148
+ spawnDetached(status.installationPath, [], node_path_1.default.dirname(status.installationPath));
149
+ }
150
+ async function openNativeShell(updateCheck) {
151
+ const status = await getNativeShellStatus(updateCheck);
152
+ if (status.installed) {
153
+ openInstalledShell(status);
154
+ return {
155
+ ok: true,
156
+ opened: 'installed',
157
+ installationPath: status.installationPath,
158
+ downloadUrl: status.downloadUrl,
159
+ message: 'Opening the native Forkit Connect shell.',
160
+ };
161
+ }
162
+ return {
163
+ ok: false,
164
+ opened: 'download',
165
+ installationPath: null,
166
+ downloadUrl: status.downloadUrl,
167
+ message: 'Install the Forkit Connect desktop app to enable tray or menu-bar controls and launch at sign-in.',
168
+ };
169
+ }
170
+ //# sourceMappingURL=native-shell.js.map
@@ -173,6 +173,33 @@ function notificationTargetUrl(candidate) {
173
173
  }
174
174
  return `${base}/?${params.toString()}`;
175
175
  }
176
+ function notificationTargetCommand(candidate) {
177
+ const targetArgs = (() => {
178
+ if (!candidate) {
179
+ return ['inbox'];
180
+ }
181
+ if (candidate.suggested_action === 'reconnect_account' || candidate.type === 'credential_reconnect_needed') {
182
+ return ['login'];
183
+ }
184
+ if (candidate.suggested_action === 'sync_c2' || candidate.type === 'c2_sync_pending') {
185
+ return ['sync'];
186
+ }
187
+ if (candidate.suggested_action === 'view_pulse_history') {
188
+ return ['changes'];
189
+ }
190
+ return ['inbox'];
191
+ })();
192
+ const currentScriptPath = process.argv[1] && node_path_1.default.isAbsolute(process.argv[1])
193
+ ? process.argv[1]
194
+ : process.argv[1]
195
+ ? node_path_1.default.resolve(process.cwd(), process.argv[1])
196
+ : null;
197
+ if (currentScriptPath && node_fs_1.default.existsSync(currentScriptPath)) {
198
+ const quote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
199
+ return [quote(process.execPath), quote(currentScriptPath), ...targetArgs.map(quote)].join(' ');
200
+ }
201
+ return `forkit-connect ${targetArgs.join(' ')}`;
202
+ }
176
203
  function hasLinuxGuiSession() {
177
204
  return Boolean(normalizeSessionReference(process.env.DISPLAY)
178
205
  || normalizeSessionReference(process.env.WAYLAND_DISPLAY)
@@ -186,6 +213,129 @@ function resolveTerminalNotifierPath() {
186
213
  const resolved = String(notifierPath.stdout || '').trim();
187
214
  return resolved || null;
188
215
  }
216
+ function resolveBundledTerminalNotifierBinary() {
217
+ try {
218
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
219
+ const notifierPackageJson = require.resolve('node-notifier/package.json');
220
+ const candidate = node_path_1.default.join(node_path_1.default.dirname(notifierPackageJson), 'vendor', 'mac.noindex', 'terminal-notifier.app', 'Contents', 'MacOS', 'terminal-notifier');
221
+ return node_fs_1.default.existsSync(candidate) ? candidate : null;
222
+ }
223
+ catch {
224
+ return null;
225
+ }
226
+ }
227
+ function resolveBrandedNotifierIconSource() {
228
+ const candidate = node_path_1.default.resolve(__dirname, '..', '..', 'assets', 'connect-notifier-512.png');
229
+ return node_fs_1.default.existsSync(candidate) ? candidate : null;
230
+ }
231
+ function ensureBrandedMacNotifierIcon(helperApp) {
232
+ const sourcePng = resolveBrandedNotifierIconSource();
233
+ if (!sourcePng) {
234
+ return;
235
+ }
236
+ const resourcesDir = node_path_1.default.join(helperApp, 'Contents', 'Resources');
237
+ const iconTarget = node_path_1.default.join(resourcesDir, 'Terminal.icns');
238
+ const iconMarker = node_path_1.default.join(resourcesDir, '.forkit-notifier-icon-v1');
239
+ if (node_fs_1.default.existsSync(iconMarker) && node_fs_1.default.existsSync(iconTarget)) {
240
+ return;
241
+ }
242
+ const iconsetDir = node_path_1.default.join(resourcesDir, 'ForkitConnect.iconset');
243
+ node_fs_1.default.rmSync(iconsetDir, { recursive: true, force: true });
244
+ node_fs_1.default.mkdirSync(iconsetDir, { recursive: true });
245
+ const variants = [
246
+ { file: 'icon_16x16.png', size: 16 },
247
+ { file: 'icon_16x16@2x.png', size: 32 },
248
+ { file: 'icon_32x32.png', size: 32 },
249
+ { file: 'icon_32x32@2x.png', size: 64 },
250
+ { file: 'icon_128x128.png', size: 128 },
251
+ { file: 'icon_128x128@2x.png', size: 256 },
252
+ { file: 'icon_256x256.png', size: 256 },
253
+ { file: 'icon_256x256@2x.png', size: 512 },
254
+ { file: 'icon_512x512.png', size: 512 },
255
+ ];
256
+ try {
257
+ for (const variant of variants) {
258
+ const outputPath = node_path_1.default.join(iconsetDir, variant.file);
259
+ const result = (0, node_child_process_1.spawnSync)('/usr/bin/sips', ['-z', String(variant.size), String(variant.size), sourcePng, '--out', outputPath], {
260
+ stdio: 'ignore',
261
+ });
262
+ if (result.status !== 0) {
263
+ throw new Error('icon_resize_failed');
264
+ }
265
+ }
266
+ node_fs_1.default.copyFileSync(sourcePng, node_path_1.default.join(iconsetDir, 'icon_512x512@2x.png'));
267
+ const buildResult = (0, node_child_process_1.spawnSync)('/usr/bin/iconutil', ['-c', 'icns', iconsetDir, '-o', iconTarget], {
268
+ stdio: 'ignore',
269
+ });
270
+ if (buildResult.status !== 0) {
271
+ throw new Error('iconutil_failed');
272
+ }
273
+ node_fs_1.default.writeFileSync(iconMarker, 'forkit-connect-notifier-icon-v1\n', 'utf8');
274
+ }
275
+ catch {
276
+ return;
277
+ }
278
+ finally {
279
+ node_fs_1.default.rmSync(iconsetDir, { recursive: true, force: true });
280
+ }
281
+ }
282
+ function notificationExecuteCommand(candidate) {
283
+ if (process.platform !== 'darwin') {
284
+ return null;
285
+ }
286
+ const targetCommand = notificationTargetCommand(candidate);
287
+ const escapedCommand = `${targetCommand}; exec "\${SHELL:-/bin/zsh}" -l`
288
+ .replace(/\\/g, '\\\\')
289
+ .replace(/"/g, '\\"');
290
+ return [
291
+ '/usr/bin/osascript',
292
+ `-e 'tell application "Terminal" to activate'`,
293
+ `-e 'tell application "Terminal" to do script "${escapedCommand}"'`,
294
+ ].join(' ');
295
+ }
296
+ function resolveBrandedMacNotifierBinary() {
297
+ if (process.platform !== 'darwin') {
298
+ return null;
299
+ }
300
+ const bundledBinary = resolveBundledTerminalNotifierBinary();
301
+ if (!bundledBinary) {
302
+ return null;
303
+ }
304
+ const sourceApp = node_path_1.default.resolve(bundledBinary, '..', '..', '..');
305
+ const helperApp = node_path_1.default.join(node_os_1.default.homedir(), '.forkit-connect', 'vendor', 'Forkit Connect.app');
306
+ const helperBinary = node_path_1.default.join(helperApp, 'Contents', 'MacOS', 'terminal-notifier');
307
+ const helperPlist = node_path_1.default.join(helperApp, 'Contents', 'Info.plist');
308
+ const isAlreadyBranded = () => {
309
+ if (!node_fs_1.default.existsSync(helperBinary) || !node_fs_1.default.existsSync(helperPlist)) {
310
+ return false;
311
+ }
312
+ try {
313
+ const result = (0, node_child_process_1.spawnSync)('plutil', ['-extract', 'CFBundleIdentifier', 'raw', '-o', '-', helperPlist], {
314
+ encoding: 'utf8',
315
+ stdio: ['ignore', 'pipe', 'ignore'],
316
+ });
317
+ return result.status === 0 && String(result.stdout || '').trim() === 'dev.forkit.connect.notifications';
318
+ }
319
+ catch {
320
+ return false;
321
+ }
322
+ };
323
+ if (!isAlreadyBranded()) {
324
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(helperApp), { recursive: true });
325
+ node_fs_1.default.rmSync(helperApp, { recursive: true, force: true });
326
+ node_fs_1.default.cpSync(sourceApp, helperApp, { recursive: true });
327
+ const replacements = [
328
+ ['CFBundleIdentifier', 'dev.forkit.connect.notifications'],
329
+ ['CFBundleName', 'Forkit Connect'],
330
+ ['CFBundleDisplayName', 'Forkit Connect'],
331
+ ];
332
+ for (const [key, value] of replacements) {
333
+ (0, node_child_process_1.spawnSync)('plutil', ['-replace', key, '-string', value, helperPlist], { stdio: 'ignore' });
334
+ }
335
+ }
336
+ ensureBrandedMacNotifierIcon(helperApp);
337
+ return node_fs_1.default.existsSync(helperBinary) ? helperBinary : null;
338
+ }
189
339
  function defaultNotificationSender(title, message, candidate) {
190
340
  if (process.platform === 'linux') {
191
341
  if (!hasLinuxGuiSession()) {
@@ -210,15 +360,21 @@ function defaultNotificationSender(title, message, candidate) {
210
360
  }
211
361
  }
212
362
  if (process.platform === 'darwin') {
213
- const targetUrl = notificationTargetUrl(candidate);
214
- const resolvedNotifier = resolveTerminalNotifierPath();
363
+ const resolvedNotifier = resolveBrandedMacNotifierBinary() ?? resolveTerminalNotifierPath();
215
364
  if (resolvedNotifier) {
216
- const openCommand = `/usr/bin/open '${targetUrl.replace(/'/g, "'\\''")}'`;
217
- const result = (0, node_child_process_1.spawnSync)(resolvedNotifier, [
365
+ const executeCommand = notificationExecuteCommand(candidate);
366
+ const args = [
218
367
  '-title', title,
368
+ '-subtitle', 'Forkit Connect',
219
369
  '-message', message,
220
- '-execute', openCommand,
221
370
  '-group', 'forkit-connect',
371
+ '-sender', 'dev.forkit.connect.notifications',
372
+ ];
373
+ if (executeCommand) {
374
+ args.push('-execute', executeCommand);
375
+ }
376
+ const result = (0, node_child_process_1.spawnSync)(resolvedNotifier, [
377
+ ...args,
222
378
  ], { stdio: 'ignore' });
223
379
  if (result.status === 0) {
224
380
  return true;
@@ -8925,13 +9081,13 @@ class ConnectV1Service {
8925
9081
  : `Unavailable at ${ollama.endpoint}: ${ollama.error ?? 'unknown_error'}`,
8926
9082
  });
8927
9083
  if (process.platform === 'darwin') {
8928
- const notifierPath = resolveTerminalNotifierPath();
9084
+ const notifierPath = resolveBrandedMacNotifierBinary() ?? resolveTerminalNotifierPath();
8929
9085
  checks.push({
8930
9086
  name: 'desktop_notifications',
8931
9087
  status: notifierPath ? 'PASS' : 'WARN',
8932
9088
  details: notifierPath
8933
- ? `terminal-notifier available at ${notifierPath}`
8934
- : 'terminal-notifier is not installed. Notifications will be recorded locally but click-through will not work on macOS.',
9089
+ ? 'Forkit Connect native notifications are ready with click-through support.'
9090
+ : 'Forkit Connect notifications are not ready yet on this Mac.',
8935
9091
  });
8936
9092
  }
8937
9093
  const sessionRefSource = this.getSessionRefSource();
@@ -0,0 +1,12 @@
1
+ export interface CliStartupStatus {
2
+ supported: boolean;
3
+ platform: 'macos' | 'windows' | 'linux';
4
+ enabled: boolean | null;
5
+ mode: 'launch_agent' | 'registry_run' | 'systemd_user' | 'unsupported';
6
+ configPath: string | null;
7
+ note: string;
8
+ }
9
+ export declare function getCliStartupStatus(): CliStartupStatus;
10
+ export declare function enableCliStartup(processExecPath: string, cliPath: string): CliStartupStatus;
11
+ export declare function disableCliStartup(): CliStartupStatus;
12
+ //# sourceMappingURL=startup.d.ts.map
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getCliStartupStatus = getCliStartupStatus;
7
+ exports.enableCliStartup = enableCliStartup;
8
+ exports.disableCliStartup = disableCliStartup;
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const node_os_1 = __importDefault(require("node:os"));
11
+ const node_path_1 = __importDefault(require("node:path"));
12
+ const node_child_process_1 = require("node:child_process");
13
+ function normalizePlatform() {
14
+ if (process.platform === 'darwin')
15
+ return 'macos';
16
+ if (process.platform === 'win32')
17
+ return 'windows';
18
+ return 'linux';
19
+ }
20
+ function macLabel() {
21
+ return 'dev.forkit.connect.cli';
22
+ }
23
+ function macPlistPath() {
24
+ return node_path_1.default.join(node_os_1.default.homedir(), 'Library', 'LaunchAgents', `${macLabel()}.plist`);
25
+ }
26
+ function linuxServiceName() {
27
+ return 'forkit-connect.service';
28
+ }
29
+ function linuxServicePath() {
30
+ return node_path_1.default.join(node_os_1.default.homedir(), '.config', 'systemd', 'user', linuxServiceName());
31
+ }
32
+ function windowsRegistryKey() {
33
+ return 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run';
34
+ }
35
+ function windowsRegistryValueName() {
36
+ return 'ForkitConnectCLI';
37
+ }
38
+ function launchctlGuiTarget() {
39
+ return `gui/${process.getuid?.() ?? 0}`;
40
+ }
41
+ function quoteWindows(value) {
42
+ return `"${value.replace(/"/g, '\\"')}"`;
43
+ }
44
+ function startupCommand(processExecPath, cliPath) {
45
+ return {
46
+ executable: processExecPath,
47
+ args: [cliPath, 'daemon', 'run-loop'],
48
+ display: `${quoteWindows(processExecPath)} ${quoteWindows(cliPath)} daemon run-loop`,
49
+ };
50
+ }
51
+ function writeMacPlist(processExecPath, cliPath) {
52
+ const command = startupCommand(processExecPath, cliPath);
53
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
54
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
55
+ <plist version="1.0">
56
+ <dict>
57
+ <key>Label</key>
58
+ <string>${macLabel()}</string>
59
+ <key>ProgramArguments</key>
60
+ <array>
61
+ <string>${command.executable}</string>
62
+ <string>${command.args[0]}</string>
63
+ <string>${command.args[1]}</string>
64
+ <string>${command.args[2]}</string>
65
+ </array>
66
+ <key>RunAtLoad</key>
67
+ <true/>
68
+ <key>KeepAlive</key>
69
+ <true/>
70
+ <key>WorkingDirectory</key>
71
+ <string>${process.cwd()}</string>
72
+ </dict>
73
+ </plist>
74
+ `;
75
+ const plistPath = macPlistPath();
76
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(plistPath), { recursive: true });
77
+ node_fs_1.default.writeFileSync(plistPath, plist, 'utf8');
78
+ return plistPath;
79
+ }
80
+ function writeLinuxService(processExecPath, cliPath) {
81
+ const command = startupCommand(processExecPath, cliPath);
82
+ const unit = `[Unit]
83
+ Description=Forkit Connect CLI background daemon
84
+ After=default.target
85
+
86
+ [Service]
87
+ Type=simple
88
+ ExecStart=${command.executable} ${command.args.join(' ')}
89
+ Restart=always
90
+ RestartSec=3
91
+ WorkingDirectory=${process.cwd()}
92
+
93
+ [Install]
94
+ WantedBy=default.target
95
+ `;
96
+ const servicePath = linuxServicePath();
97
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(servicePath), { recursive: true });
98
+ node_fs_1.default.writeFileSync(servicePath, unit, 'utf8');
99
+ return servicePath;
100
+ }
101
+ function getMacStatus() {
102
+ const plistPath = macPlistPath();
103
+ const exists = node_fs_1.default.existsSync(plistPath);
104
+ return {
105
+ supported: true,
106
+ platform: 'macos',
107
+ enabled: exists,
108
+ mode: 'launch_agent',
109
+ configPath: plistPath,
110
+ note: exists
111
+ ? 'Forkit Connect CLI will start at sign-in through a LaunchAgent.'
112
+ : 'Forkit Connect CLI is not set to start at sign-in on this Mac.',
113
+ };
114
+ }
115
+ function getWindowsStatus() {
116
+ const query = (0, node_child_process_1.spawnSync)('reg', ['query', windowsRegistryKey(), '/v', windowsRegistryValueName()], {
117
+ encoding: 'utf8',
118
+ stdio: ['ignore', 'pipe', 'ignore'],
119
+ });
120
+ const enabled = query.status === 0;
121
+ return {
122
+ supported: true,
123
+ platform: 'windows',
124
+ enabled,
125
+ mode: 'registry_run',
126
+ configPath: windowsRegistryKey(),
127
+ note: enabled
128
+ ? 'Forkit Connect CLI will start after sign-in through the Windows Run registry.'
129
+ : 'Forkit Connect CLI is not set to start after sign-in on Windows.',
130
+ };
131
+ }
132
+ function getLinuxStatus() {
133
+ const servicePath = linuxServicePath();
134
+ const exists = node_fs_1.default.existsSync(servicePath);
135
+ return {
136
+ supported: true,
137
+ platform: 'linux',
138
+ enabled: exists,
139
+ mode: 'systemd_user',
140
+ configPath: servicePath,
141
+ note: exists
142
+ ? 'Forkit Connect CLI will start in the user session through systemd.'
143
+ : 'Forkit Connect CLI is not set to start in the user session on Linux.',
144
+ };
145
+ }
146
+ function getCliStartupStatus() {
147
+ const platform = normalizePlatform();
148
+ if (platform === 'macos')
149
+ return getMacStatus();
150
+ if (platform === 'windows')
151
+ return getWindowsStatus();
152
+ if (platform === 'linux')
153
+ return getLinuxStatus();
154
+ return {
155
+ supported: false,
156
+ platform,
157
+ enabled: null,
158
+ mode: 'unsupported',
159
+ configPath: null,
160
+ note: 'Startup management is not available on this platform.',
161
+ };
162
+ }
163
+ function enableCliStartup(processExecPath, cliPath) {
164
+ const platform = normalizePlatform();
165
+ if (platform === 'macos') {
166
+ const plistPath = writeMacPlist(processExecPath, cliPath);
167
+ (0, node_child_process_1.spawnSync)('launchctl', ['bootout', launchctlGuiTarget(), plistPath], { stdio: 'ignore' });
168
+ const bootstrap = (0, node_child_process_1.spawnSync)('launchctl', ['bootstrap', launchctlGuiTarget(), plistPath], { stdio: 'ignore' });
169
+ if (bootstrap.status !== 0) {
170
+ throw new Error('cli_startup_enable_failed_macos');
171
+ }
172
+ (0, node_child_process_1.spawnSync)('launchctl', ['kickstart', '-k', `${launchctlGuiTarget()}/${macLabel()}`], { stdio: 'ignore' });
173
+ return getMacStatus();
174
+ }
175
+ if (platform === 'windows') {
176
+ const command = startupCommand(processExecPath, cliPath);
177
+ const add = (0, node_child_process_1.spawnSync)('reg', [
178
+ 'add',
179
+ windowsRegistryKey(),
180
+ '/v',
181
+ windowsRegistryValueName(),
182
+ '/t',
183
+ 'REG_SZ',
184
+ '/d',
185
+ command.display,
186
+ '/f',
187
+ ], { stdio: 'ignore' });
188
+ if (add.status !== 0) {
189
+ throw new Error('cli_startup_enable_failed_windows');
190
+ }
191
+ return getWindowsStatus();
192
+ }
193
+ if (platform === 'linux') {
194
+ const servicePath = writeLinuxService(processExecPath, cliPath);
195
+ const reload = (0, node_child_process_1.spawnSync)('systemctl', ['--user', 'daemon-reload'], { stdio: 'ignore' });
196
+ const enable = (0, node_child_process_1.spawnSync)('systemctl', ['--user', 'enable', '--now', linuxServiceName()], { stdio: 'ignore' });
197
+ if (reload.status !== 0 || enable.status !== 0 || !node_fs_1.default.existsSync(servicePath)) {
198
+ throw new Error('cli_startup_enable_failed_linux');
199
+ }
200
+ return getLinuxStatus();
201
+ }
202
+ throw new Error('cli_startup_unsupported');
203
+ }
204
+ function disableCliStartup() {
205
+ const platform = normalizePlatform();
206
+ if (platform === 'macos') {
207
+ const plistPath = macPlistPath();
208
+ (0, node_child_process_1.spawnSync)('launchctl', ['bootout', launchctlGuiTarget(), plistPath], { stdio: 'ignore' });
209
+ if (node_fs_1.default.existsSync(plistPath)) {
210
+ node_fs_1.default.rmSync(plistPath, { force: true });
211
+ }
212
+ return getMacStatus();
213
+ }
214
+ if (platform === 'windows') {
215
+ (0, node_child_process_1.spawnSync)('reg', ['delete', windowsRegistryKey(), '/v', windowsRegistryValueName(), '/f'], { stdio: 'ignore' });
216
+ return getWindowsStatus();
217
+ }
218
+ if (platform === 'linux') {
219
+ (0, node_child_process_1.spawnSync)('systemctl', ['--user', 'disable', '--now', linuxServiceName()], { stdio: 'ignore' });
220
+ (0, node_child_process_1.spawnSync)('systemctl', ['--user', 'daemon-reload'], { stdio: 'ignore' });
221
+ const servicePath = linuxServicePath();
222
+ if (node_fs_1.default.existsSync(servicePath)) {
223
+ node_fs_1.default.rmSync(servicePath, { force: true });
224
+ }
225
+ return getLinuxStatus();
226
+ }
227
+ throw new Error('cli_startup_unsupported');
228
+ }
229
+ //# sourceMappingURL=startup.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forkit-connect",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "Forkit Connect Local Engine - The Global AI Governance Fabric",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -10,6 +10,7 @@
10
10
  },
11
11
  "files": [
12
12
  "dist",
13
+ "assets",
13
14
  "README.md",
14
15
  "QUICKSTART.md"
15
16
  ],
@@ -33,6 +34,7 @@
33
34
  "better-sqlite3": "^12.10.0",
34
35
  "cors": "^2.8.6",
35
36
  "express": "^4.19.2",
37
+ "node-notifier": "^10.0.1",
36
38
  "ps-list": "^8.1.1",
37
39
  "uuid": "^10.0.0",
38
40
  "ws": "^8.16.0",