forkit-connect 0.1.23 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,8 @@ Naming convention:
6
6
 
7
7
  - Product name: Forkit Connect
8
8
  - Command name: `forkit-connect`
9
- - Package name: `@forkit/connect`
9
+ - Published package name: `forkit-connect`
10
+ - Workspace dependency alias: `@forkit/connect`
10
11
 
11
12
  ## Install Surface
12
13
 
@@ -60,10 +61,13 @@ If `~/.local/bin` is already on your `PATH`, the command is immediately accessib
60
61
  - `forkit-connect login` — link this device to Forkit.dev with the device flow
61
62
  - `forkit-connect scan` — discover local runtimes and AI models
62
63
  - `forkit-connect inbox` — review the Smart Registration Inbox
64
+ - `forkit-connect connect <modelNameOrDiscoveryHash>` — prepare or sync a passport draft for a detected model
63
65
  - `forkit-connect runtime register` — register or reuse the current repo/worktree as a governed runtime
64
66
  - `forkit-connect runtime observe --gaid <gaid>` — emit a repo-scoped runtime journal for the current runtime target
65
67
  - `forkit-connect status` — show public Connect readiness status
66
68
  - `forkit-connect changes` — view collected local evidence, runtime signal history, and pending sync items
69
+ - `forkit-connect doctor` — run local environment, secure-storage, backend-session, and account checks
70
+ - `forkit-connect update-check` — inspect release metadata without enabling auto-update
67
71
  - `forkit-connect start` — start the local daemon loop
68
72
  - `forkit-connect stop` — stop the background daemon without deleting local state
69
73
  - `forkit-connect uninstall` — preview a cautious uninstall plan and write local backup logs
@@ -72,7 +76,7 @@ If `~/.local/bin` is already on your `PATH`, the command is immediately accessib
72
76
 
73
77
  ## Advanced Commands
74
78
 
75
- Advanced flows remain available under the same binary, including `connect`, `review`, `daemon`, `config`, `pulse`, `c2`, `train`, `agent`, notification utilities, and runtime target management.
79
+ Advanced flows remain available under the same binary, including `review`, `daemon`, `config`, `pulse`, `c2`, `train`, `agent`, notification utilities, and runtime target management.
76
80
 
77
81
  Runtime registration notes:
78
82
 
@@ -117,7 +121,7 @@ Then install the generated tarball into a clean directory:
117
121
  mkdir -p /tmp/forkit-connect-smoke
118
122
  cd /tmp/forkit-connect-smoke
119
123
  npm init -y
120
- npm install /absolute/path/to/forkit-connect-0.1.1.tgz
124
+ npm install /absolute/path/to/forkit-connect-0.1.24.tgz
121
125
  npx forkit-connect --help
122
126
  npx forkit-connect status
123
127
  npx forkit-connect inbox
package/dist/cli.js CHANGED
@@ -13,7 +13,6 @@ const daemon_1 = require("./v1/daemon");
13
13
  const service_1 = require("./v1/service");
14
14
  const discovery_1 = require("./v1/discovery");
15
15
  const heartbeat_1 = require("./v1/heartbeat");
16
- const startup_1 = require("./v1/startup");
17
16
  const runtime_activity_1 = require("./v1/runtime-activity");
18
17
  const runtime_editor_activity_1 = require("./v1/runtime-editor-activity");
19
18
  const runtime_observation_runner_1 = require("./v1/runtime-observation-runner");
@@ -82,7 +81,6 @@ const PUBLIC_COMMANDS = [
82
81
  ['login', 'Link this device to your Forkit.dev account'],
83
82
  ['logout', 'Remove the stored Forkit.dev session from this device'],
84
83
  ['status', 'Show account, scope, daemon, and queue status'],
85
- ['startup', 'Enable, disable, or inspect CLI startup on login'],
86
84
  ['changes', 'View all collected local changes and queued sync items'],
87
85
  ['start', 'Open the interactive launcher or start foreground sync'],
88
86
  ['stop', 'Stop the local Connect daemon'],
@@ -119,28 +117,14 @@ const ADVANCED_COMMAND_GROUPS = [
119
117
  ['notify', 'Notification preview and delivery controls'],
120
118
  ];
121
119
  function usage() {
122
- printCliHeader('CLI', 'Local AI discovery, review, and registration from your terminal.');
123
- console.log(cliKeyLine('command', 'forkit-connect <command> [options]'));
124
- console.log(cliKeyLine('always on', 'forkit-connect startup enable'));
125
- console.log(cliKeyLine('review', 'forkit-connect inbox'));
120
+ printCliHeader('CLI', 'Precise local review, runtime detection, and registration handoff.');
121
+ console.log(cliKeyLine('usage', 'forkit-connect <command> [options]'));
122
+ console.log(cliKeyLine('scope', 'forkit-connect workspace <list|select|create|status>'));
126
123
  console.log(cliKeyLine('runtime', 'forkit-connect runtime <register|review|status>'));
127
- printCliSection('Start Here');
128
- console.log(cliKeyLine('login', 'Connect this device to your Forkit.dev account'));
129
- console.log(cliKeyLine('scan', 'Detect local models, runtimes, and agents'));
130
- console.log(cliKeyLine('inbox', 'Review what needs action'));
131
- console.log(cliKeyLine('startup', 'Keep Connect running from sign-in'));
132
- printCliSection('Daily');
133
- console.log(cliKeyLine('status', 'See account, queue, and daemon state'));
134
- console.log(cliKeyLine('start', 'Open the interactive local console'));
135
- console.log(cliKeyLine('sync', 'Flush queued local metadata'));
136
- console.log(cliKeyLine('stop', 'Stop the background daemon'));
137
- printCliSection('Governed');
138
- console.log(cliKeyLine('workspace', 'Optional workspace and project scope'));
139
- console.log(cliKeyLine('register', 'Register ready local models'));
140
- console.log(cliKeyLine('runtime', 'Register or review local runtimes'));
141
- printCliSection('Safety');
142
- console.log(cliKeyLine('ignore', 'Deny one detected local model on this device'));
143
- console.log(cliKeyLine('doctor', 'Check notifications, storage, and runtime wiring'));
124
+ printCliSection('Core');
125
+ for (const [command, description] of PUBLIC_COMMANDS) {
126
+ console.log(cliKeyLine(command, description));
127
+ }
144
128
  printCliSection('Flags');
145
129
  console.log(cliKeyLine('--json', 'Machine-readable output when supported'));
146
130
  console.log(cliKeyLine('--all-ready', 'Register every ready local model in the current scope'));
@@ -269,10 +253,10 @@ function printDeviceLoginInstructions(start) {
269
253
  }
270
254
  function printSessionExportFallback(token) {
271
255
  void token;
272
- console.log('[forkit-connect] Approval succeeded, but secure credential storage is unavailable in this Linux session.');
273
- console.log('[forkit-connect] Forkit Connect can keep using this approved session in the current interactive run.');
274
- console.log('[forkit-connect] For persistent login, install libsecret-tools and run forkit-connect login again.');
275
- console.log('[forkit-connect] Headless automation may pass FORKIT_CONNECT_SESSION_REF explicitly, but Connect will not print session tokens.');
256
+ console.log('[forkit-connect] Approval succeeded, but secure credential storage is unavailable or timed out on this machine.');
257
+ console.log('[forkit-connect] Forkit Connect will not print session tokens. Persistent login requires working secure storage.');
258
+ console.log('[forkit-connect] macOS: unlock Keychain and run forkit-connect login again. Linux: install libsecret-tools and run inside a DBus user session.');
259
+ console.log('[forkit-connect] Headless automation may pass FORKIT_CONNECT_SESSION_REF explicitly from a secret manager.');
276
260
  }
277
261
  function hasLinuxGuiSession() {
278
262
  if (process.platform !== 'linux') {
@@ -951,7 +935,7 @@ function formatCliScopeLabel(workspaceId, projectId) {
951
935
  if (workspace) {
952
936
  return `${shortId(workspace)} / project needed`;
953
937
  }
954
- return 'solo / no workspace';
938
+ return 'personal device / workspace not selected';
955
939
  }
956
940
  function summarizeReviewQueueCounts(values) {
957
941
  const parts = [
@@ -2146,7 +2130,6 @@ function printPublicStatusOverview(status) {
2146
2130
  console.log(cliKeyLine('device', status.device_paired ? 'paired' : 'approval pending'));
2147
2131
  console.log(cliKeyLine('scope', formatCliScopeLabel(status.workspace_id, status.project_id)));
2148
2132
  console.log(cliKeyLine('daemon', status.daemon_status));
2149
- console.log(cliKeyLine('background', status.daemon_status === 'running' ? 'always on' : 'start required'));
2150
2133
  console.log(cliKeyLine('privacy', status.privacy_mode));
2151
2134
  console.log(cliKeyLine('inventory', joinCliSummary([
2152
2135
  formatCliCompactCount(status.models_discovered, 'model'),
@@ -2167,47 +2150,6 @@ function printPublicStatusOverview(status) {
2167
2150
  console.log(cliKeyLine('warning', 'Local governed scope exists, but login is still required.'));
2168
2151
  }
2169
2152
  }
2170
- function formatStartupSummary(status) {
2171
- if (!status.supported)
2172
- return 'not supported';
2173
- if (status.enabled === true)
2174
- return 'enabled';
2175
- if (status.enabled === false)
2176
- return 'disabled';
2177
- return 'unknown';
2178
- }
2179
- function printStartupStatusLine(status) {
2180
- console.log(cliKeyLine('startup', formatStartupSummary(status)));
2181
- }
2182
- function printPublicStartupStatus(status) {
2183
- const enabled = status.enabled === true;
2184
- const platform = status.mode === 'launch_agent'
2185
- ? 'macOS LaunchAgent'
2186
- : status.mode === 'registry_run'
2187
- ? 'Windows sign-in registry'
2188
- : status.mode === 'systemd_user'
2189
- ? 'Linux user service'
2190
- : 'not available';
2191
- printCliHeader('Startup', 'Keep Forkit Connect running from sign-in without a tray app.');
2192
- console.log(cliKeyLine('auto start', enabled ? 'on' : status.enabled === false ? 'off' : 'unknown'));
2193
- console.log(cliKeyLine('behavior', enabled
2194
- ? 'Forkit Connect launches its background daemon when you sign in.'
2195
- : 'Forkit Connect starts only when you run it manually.'));
2196
- console.log(cliKeyLine('platform', platform));
2197
- console.log(cliKeyLine('next', enabled ? 'forkit-connect startup disable' : 'forkit-connect startup enable'));
2198
- if (hasFlag('--advanced-help') && status.configPath) {
2199
- console.log(cliKeyLine('config', status.configPath));
2200
- }
2201
- }
2202
- function getNotificationReadiness() {
2203
- if (process.platform === 'darwin') {
2204
- return 'Forkit Connect click-through ready';
2205
- }
2206
- if (process.platform === 'win32') {
2207
- return 'native Windows toasts ready';
2208
- }
2209
- return 'native desktop alerts when a GUI session is available';
2210
- }
2211
2153
  function getOtherReadyCount(readyToConnectCount, draftFirstCount) {
2212
2154
  return Math.max(0, readyToConnectCount - draftFirstCount);
2213
2155
  }
@@ -2688,7 +2630,6 @@ async function run() {
2688
2630
  const runPublicConnectStatus = async () => {
2689
2631
  const sessionState = await checkBackendSessionState(service);
2690
2632
  const secureStorage = service.getCredentialStoreStatus();
2691
- const startupStatus = (0, startup_1.getCliStartupStatus)();
2692
2633
  if (service.readSessionRef()) {
2693
2634
  await withTimeout(service.refreshEffectiveBinding(), STATUS_BINDING_TIMEOUT_MS, undefined);
2694
2635
  }
@@ -2696,7 +2637,12 @@ async function run() {
2696
2637
  const overview = withSmartInboxSnapshotCounts(service.getConnectStatusOverview({ includeInbox: false }));
2697
2638
  const localScopeCached = Boolean(String(overview.workspace_id || '').trim() || String(overview.project_id || '').trim());
2698
2639
  const displayOverview = accountTrusted
2699
- ? overview
2640
+ ? {
2641
+ ...overview,
2642
+ lifecycle_note: !localScopeCached && overview.device_paired
2643
+ ? 'Account connected. Personal scanning works; select workspace/project later for C2.'
2644
+ : overview.lifecycle_note,
2645
+ }
2700
2646
  : {
2701
2647
  ...overview,
2702
2648
  workspace_id: null,
@@ -2714,10 +2660,6 @@ async function run() {
2714
2660
  session_truth: accountTrusted ? 'account_verified_or_offline' : 'local_scope_cached_login_required',
2715
2661
  local_scope_cached: !accountTrusted && localScopeCached,
2716
2662
  binding_truth: accountTrusted ? 'account_binding_active' : 'account_login_required',
2717
- startup_on_login: startupStatus,
2718
- notifications: {
2719
- readiness: getNotificationReadiness(),
2720
- },
2721
2663
  secure_storage: {
2722
2664
  backend: secureStorage.backend,
2723
2665
  available: secureStorage.available,
@@ -2733,28 +2675,8 @@ async function run() {
2733
2675
  if (!secureStorage.available || secureStorage.plaintextFallbackActive) {
2734
2676
  console.log(cliKeyLine('secure note', secureStorage.detail));
2735
2677
  }
2736
- console.log(cliKeyLine('notifications', getNotificationReadiness()));
2737
- printStartupStatusLine(startupStatus);
2738
2678
  printPublicStatusGuidance(displayOverview, sessionState);
2739
2679
  };
2740
- const runPublicStartupStatus = () => {
2741
- const startupStatus = (0, startup_1.getCliStartupStatus)();
2742
- if (hasFlag('--json')) {
2743
- printJson(startupStatus);
2744
- return;
2745
- }
2746
- printPublicStartupStatus(startupStatus);
2747
- };
2748
- const runPublicStartupEnable = () => {
2749
- const startupStatus = (0, startup_1.enableCliStartup)(process.execPath, __filename);
2750
- printPublicStartupStatus(startupStatus);
2751
- console.log('[forkit-connect] CLI startup on login is enabled. The background daemon will come up when your session starts.');
2752
- };
2753
- const runPublicStartupDisable = () => {
2754
- const startupStatus = (0, startup_1.disableCliStartup)();
2755
- printPublicStartupStatus(startupStatus);
2756
- console.log('[forkit-connect] CLI startup on login is disabled. Use forkit-connect stop if you also want to stop the daemon right now.');
2757
- };
2758
2680
  const runPublicConnectInbox = async () => {
2759
2681
  const forceRefresh = hasFlag('--refresh');
2760
2682
  const inbox = service.getSmartRegistrationInbox({
@@ -2850,7 +2772,8 @@ async function run() {
2850
2772
  }
2851
2773
  console.log(`[forkit-connect] Queue processed=${result.sync.processed} succeeded=${result.sync.succeeded} failed=${result.sync.failed}`);
2852
2774
  if (!result.ok) {
2853
- process.exitCode = 2;
2775
+ console.log('[forkit-connect] No runtime passport was created. Start Ollama, LM Studio, an OpenAI-compatible local server, or add local model directories, then rerun scan.');
2776
+ process.exitCode = 0;
2854
2777
  }
2855
2778
  };
2856
2779
  const runPublicUpdateCheck = async () => {
@@ -5017,24 +4940,6 @@ async function run() {
5017
4940
  await runPublicConnectStatus();
5018
4941
  return;
5019
4942
  }
5020
- if (command === 'startup') {
5021
- const subcommand = args[1] || 'status';
5022
- if (subcommand === 'status') {
5023
- runPublicStartupStatus();
5024
- return;
5025
- }
5026
- if (subcommand === 'enable') {
5027
- runPublicStartupEnable();
5028
- return;
5029
- }
5030
- if (subcommand === 'disable') {
5031
- runPublicStartupDisable();
5032
- return;
5033
- }
5034
- showUsage();
5035
- process.exitCode = 1;
5036
- return;
5037
- }
5038
4943
  if (command === 'changes') {
5039
4944
  runPublicCollectedChanges();
5040
4945
  return;
@@ -20,6 +20,7 @@ exports.ConnectCredentialStoreError = ConnectCredentialStoreError;
20
20
  const SERVICE_BASE_NAME = 'ForkitConnect';
21
21
  const LEGACY_SERVICE_NAME = SERVICE_BASE_NAME;
22
22
  const LINUX_SECRET_TOOL_TIMEOUT_MS = 1200;
23
+ const MACOS_SECURITY_TIMEOUT_MS = Number(process.env.FORKIT_CONNECT_MACOS_SECURITY_TIMEOUT_MS || 15000);
23
24
  function nowIso() {
24
25
  return new Date().toISOString();
25
26
  }
@@ -187,6 +188,7 @@ class MacOsKeychainBackend {
187
188
  '-w',
188
189
  ], {
189
190
  encoding: 'utf8',
191
+ timeout: MACOS_SECURITY_TIMEOUT_MS,
190
192
  });
191
193
  if (result.error) {
192
194
  throw new ConnectCredentialStoreError('secure_storage_read_failed', `Forkit Connect could not read secure credentials from macOS Keychain: ${result.error.message}`);
@@ -211,6 +213,7 @@ class MacOsKeychainBackend {
211
213
  value,
212
214
  ], {
213
215
  encoding: 'utf8',
216
+ timeout: MACOS_SECURITY_TIMEOUT_MS,
214
217
  });
215
218
  if (result.error) {
216
219
  throw new ConnectCredentialStoreError('secure_storage_write_failed', `Forkit Connect could not write secure credentials to macOS Keychain: ${result.error.message}`);
@@ -229,6 +232,7 @@ class MacOsKeychainBackend {
229
232
  account,
230
233
  ], {
231
234
  encoding: 'utf8',
235
+ timeout: MACOS_SECURITY_TIMEOUT_MS,
232
236
  });
233
237
  if (result.error) {
234
238
  throw new ConnectCredentialStoreError('secure_storage_delete_failed', `Forkit Connect could not remove secure credentials from macOS Keychain: ${result.error.message}`);
@@ -241,6 +245,7 @@ class MacOsKeychainBackend {
241
245
  getStatus() {
242
246
  const result = (0, node_child_process_1.spawnSync)('security', ['list-keychains'], {
243
247
  encoding: 'utf8',
248
+ timeout: MACOS_SECURITY_TIMEOUT_MS,
244
249
  });
245
250
  if (result.error) {
246
251
  return {
@@ -173,33 +173,6 @@ 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
- }
203
176
  function hasLinuxGuiSession() {
204
177
  return Boolean(normalizeSessionReference(process.env.DISPLAY)
205
178
  || normalizeSessionReference(process.env.WAYLAND_DISPLAY)
@@ -213,129 +186,6 @@ function resolveTerminalNotifierPath() {
213
186
  const resolved = String(notifierPath.stdout || '').trim();
214
187
  return resolved || null;
215
188
  }
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
- }
339
189
  function defaultNotificationSender(title, message, candidate) {
340
190
  if (process.platform === 'linux') {
341
191
  if (!hasLinuxGuiSession()) {
@@ -360,21 +210,15 @@ function defaultNotificationSender(title, message, candidate) {
360
210
  }
361
211
  }
362
212
  if (process.platform === 'darwin') {
363
- const resolvedNotifier = resolveBrandedMacNotifierBinary() ?? resolveTerminalNotifierPath();
213
+ const targetUrl = notificationTargetUrl(candidate);
214
+ const resolvedNotifier = resolveTerminalNotifierPath();
364
215
  if (resolvedNotifier) {
365
- const executeCommand = notificationExecuteCommand(candidate);
366
- const args = [
216
+ const openCommand = `/usr/bin/open '${targetUrl.replace(/'/g, "'\\''")}'`;
217
+ const result = (0, node_child_process_1.spawnSync)(resolvedNotifier, [
367
218
  '-title', title,
368
- '-subtitle', 'Forkit Connect',
369
219
  '-message', message,
220
+ '-execute', openCommand,
370
221
  '-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,
378
222
  ], { stdio: 'ignore' });
379
223
  if (result.status === 0) {
380
224
  return true;
@@ -9081,13 +8925,13 @@ class ConnectV1Service {
9081
8925
  : `Unavailable at ${ollama.endpoint}: ${ollama.error ?? 'unknown_error'}`,
9082
8926
  });
9083
8927
  if (process.platform === 'darwin') {
9084
- const notifierPath = resolveBrandedMacNotifierBinary() ?? resolveTerminalNotifierPath();
8928
+ const notifierPath = resolveTerminalNotifierPath();
9085
8929
  checks.push({
9086
8930
  name: 'desktop_notifications',
9087
8931
  status: notifierPath ? 'PASS' : 'WARN',
9088
8932
  details: notifierPath
9089
- ? 'Forkit Connect native notifications are ready with click-through support.'
9090
- : 'Forkit Connect notifications are not ready yet on this Mac.',
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.',
9091
8935
  });
9092
8936
  }
9093
8937
  const sessionRefSource = this.getSessionRefSource();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forkit-connect",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "Forkit Connect Local Engine - The Global AI Governance Fabric",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -10,7 +10,6 @@
10
10
  },
11
11
  "files": [
12
12
  "dist",
13
- "assets",
14
13
  "README.md",
15
14
  "QUICKSTART.md"
16
15
  ],
@@ -34,7 +33,6 @@
34
33
  "better-sqlite3": "^12.10.0",
35
34
  "cors": "^2.8.6",
36
35
  "express": "^4.19.2",
37
- "node-notifier": "^10.0.1",
38
36
  "ps-list": "^8.1.1",
39
37
  "uuid": "^10.0.0",
40
38
  "ws": "^8.16.0",
Binary file
@@ -1,12 +0,0 @@
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
@@ -1,229 +0,0 @@
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