forkit-connect 0.1.0

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/QUICKSTART.md +55 -0
  2. package/README.md +96 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.js +4724 -0
  5. package/dist/index.d.ts +7 -0
  6. package/dist/index.js +21 -0
  7. package/dist/launcher.d.ts +33 -0
  8. package/dist/launcher.js +9344 -0
  9. package/dist/ps-list-loader.d.ts +5 -0
  10. package/dist/ps-list-loader.js +20 -0
  11. package/dist/v1/agent-observation.d.ts +42 -0
  12. package/dist/v1/agent-observation.js +499 -0
  13. package/dist/v1/api.d.ts +276 -0
  14. package/dist/v1/api.js +390 -0
  15. package/dist/v1/credential-store.d.ts +92 -0
  16. package/dist/v1/credential-store.js +797 -0
  17. package/dist/v1/currency.d.ts +41 -0
  18. package/dist/v1/currency.js +127 -0
  19. package/dist/v1/daemon.d.ts +50 -0
  20. package/dist/v1/daemon.js +265 -0
  21. package/dist/v1/discovery.d.ts +61 -0
  22. package/dist/v1/discovery.js +168 -0
  23. package/dist/v1/filesystem-models.d.ts +11 -0
  24. package/dist/v1/filesystem-models.js +261 -0
  25. package/dist/v1/heartbeat.d.ts +45 -0
  26. package/dist/v1/heartbeat.js +463 -0
  27. package/dist/v1/lifecycle-monitor.d.ts +78 -0
  28. package/dist/v1/lifecycle-monitor.js +512 -0
  29. package/dist/v1/lmstudio.d.ts +11 -0
  30. package/dist/v1/lmstudio.js +148 -0
  31. package/dist/v1/ollama.d.ts +19 -0
  32. package/dist/v1/ollama.js +164 -0
  33. package/dist/v1/openai-compatible.d.ts +12 -0
  34. package/dist/v1/openai-compatible.js +124 -0
  35. package/dist/v1/process-scout.d.ts +50 -0
  36. package/dist/v1/process-scout.js +715 -0
  37. package/dist/v1/providers.d.ts +50 -0
  38. package/dist/v1/providers.js +106 -0
  39. package/dist/v1/service.d.ts +680 -0
  40. package/dist/v1/service.js +8286 -0
  41. package/dist/v1/state.d.ts +87 -0
  42. package/dist/v1/state.js +1318 -0
  43. package/dist/v1/test-credential-backend.d.ts +19 -0
  44. package/dist/v1/test-credential-backend.js +49 -0
  45. package/dist/v1/types.d.ts +873 -0
  46. package/dist/v1/types.js +3 -0
  47. package/dist/v1/update.d.ts +38 -0
  48. package/dist/v1/update.js +184 -0
  49. package/dist/v1/vitality-pulse.d.ts +36 -0
  50. package/dist/v1/vitality-pulse.js +512 -0
  51. package/package.json +53 -0
@@ -0,0 +1,41 @@
1
+ /**
2
+ * currency.ts — locale-aware ISO 4217 currency resolution for the Connect daemon.
3
+ *
4
+ * Priority chain (highest to lowest confidence):
5
+ * 1. User session / profile (backend tells us the account's country).
6
+ * 2. System locale from Node.js Intl (ICU data, no network needed).
7
+ * 3. Hard fallback: 'USD' — only used when ICU data is unavailable.
8
+ *
9
+ * No hardcoded amounts or rates live here. Cost amounts are always
10
+ * expressed in the resolved currency so the UI can format them with
11
+ * Intl.NumberFormat without any conversion.
12
+ */
13
+ /**
14
+ * Derives the ISO 4217 currency code from a BCP-47 locale string.
15
+ * Returns null when the locale has no recognisable country subtag.
16
+ */
17
+ export declare function currencyFromLocale(locale: string): string | null;
18
+ /**
19
+ * Returns the system's default locale using Node.js ICU data.
20
+ * Falls back to 'en-US' if ICU is unavailable (--no-icu Node builds).
21
+ */
22
+ export declare function getSystemLocale(): string;
23
+ /**
24
+ * Resolves the ISO 4217 currency code for the current Connect session.
25
+ *
26
+ * @param sessionCountry - ISO 3166-1 α-2 country from the user's backend profile (preferred).
27
+ * Pass `null` when the session hasn't loaded yet.
28
+ * @returns ISO 4217 currency code, e.g. 'USD', 'INR', 'EUR'.
29
+ * Falls back to 'USD' as last resort (logged at debug level).
30
+ */
31
+ export declare function resolveSessionCurrency(sessionCountry: string | null | undefined): string;
32
+ /**
33
+ * Formats a cost amount for display inside the daemon's log output.
34
+ * The web UI uses Intl.NumberFormat directly for richer formatting.
35
+ *
36
+ * @param amount - Numeric cost value.
37
+ * @param currency - ISO 4217 currency code.
38
+ * @param confidence - 'exact' | 'estimated' | 'relative'
39
+ */
40
+ export declare function formatCostForLog(amount: number | null, currency: string | null, confidence: 'exact' | 'estimated' | 'relative'): string;
41
+ //# sourceMappingURL=currency.d.ts.map
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ /**
3
+ * currency.ts — locale-aware ISO 4217 currency resolution for the Connect daemon.
4
+ *
5
+ * Priority chain (highest to lowest confidence):
6
+ * 1. User session / profile (backend tells us the account's country).
7
+ * 2. System locale from Node.js Intl (ICU data, no network needed).
8
+ * 3. Hard fallback: 'USD' — only used when ICU data is unavailable.
9
+ *
10
+ * No hardcoded amounts or rates live here. Cost amounts are always
11
+ * expressed in the resolved currency so the UI can format them with
12
+ * Intl.NumberFormat without any conversion.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.currencyFromLocale = currencyFromLocale;
16
+ exports.getSystemLocale = getSystemLocale;
17
+ exports.resolveSessionCurrency = resolveSessionCurrency;
18
+ exports.formatCostForLog = formatCostForLog;
19
+ /** Compact, authoritative country→currency map (ISO 3166-1 α-2 → ISO 4217). */
20
+ const COUNTRY_CURRENCY = {
21
+ // Americas
22
+ US: 'USD', CA: 'CAD', MX: 'MXN', BR: 'BRL', AR: 'ARS', CL: 'CLP',
23
+ CO: 'COP', PE: 'PEN', VE: 'VES', UY: 'UYU', PY: 'PYG', BO: 'BOB',
24
+ // Europe
25
+ DE: 'EUR', FR: 'EUR', IT: 'EUR', ES: 'EUR', PT: 'EUR', NL: 'EUR',
26
+ BE: 'EUR', AT: 'EUR', IE: 'EUR', FI: 'EUR', GR: 'EUR', LU: 'EUR',
27
+ SK: 'EUR', SI: 'EUR', CY: 'EUR', MT: 'EUR', EE: 'EUR', LV: 'EUR',
28
+ LT: 'EUR', HR: 'EUR',
29
+ GB: 'GBP', CH: 'CHF', SE: 'SEK', NO: 'NOK', DK: 'DKK', PL: 'PLN',
30
+ CZ: 'CZK', HU: 'HUF', RO: 'RON', BG: 'BGN', RS: 'RSD', UA: 'UAH',
31
+ TR: 'TRY', RU: 'RUB',
32
+ // Asia-Pacific
33
+ IN: 'INR', CN: 'CNY', JP: 'JPY', KR: 'KRW', AU: 'AUD', NZ: 'NZD',
34
+ SG: 'SGD', HK: 'HKD', TW: 'TWD', TH: 'THB', ID: 'IDR', MY: 'MYR',
35
+ PH: 'PHP', VN: 'VND', PK: 'PKR', BD: 'BDT', LK: 'LKR', NP: 'NPR',
36
+ MM: 'MMK', KH: 'KHR', MN: 'MNT', KZ: 'KZT', UZ: 'UZS',
37
+ // Middle-East & Africa
38
+ AE: 'AED', SA: 'SAR', QA: 'QAR', KW: 'KWD', BH: 'BHD', OM: 'OMR',
39
+ IQ: 'IQD', IR: 'IRR', IL: 'ILS', EG: 'EGP', ZA: 'ZAR', NG: 'NGN',
40
+ GH: 'GHS', KE: 'KES', TZ: 'TZS', ET: 'ETB', MA: 'MAD', DZ: 'DZD',
41
+ TN: 'TND',
42
+ };
43
+ /**
44
+ * Extracts the country code from a BCP-47 locale string.
45
+ * e.g. 'en-IN' → 'IN', 'zh-Hans-CN' → 'CN', 'fr' → null
46
+ */
47
+ function countryFromLocale(locale) {
48
+ const parts = locale.split('-');
49
+ for (let i = parts.length - 1; i >= 1; i--) {
50
+ const part = parts[i];
51
+ if (part === undefined)
52
+ continue;
53
+ if (/^[A-Z]{2}$/.test(part))
54
+ return part;
55
+ if (/^[a-z]{2}$/.test(part))
56
+ return part.toUpperCase();
57
+ }
58
+ return null;
59
+ }
60
+ /**
61
+ * Derives the ISO 4217 currency code from a BCP-47 locale string.
62
+ * Returns null when the locale has no recognisable country subtag.
63
+ */
64
+ function currencyFromLocale(locale) {
65
+ const country = countryFromLocale(locale);
66
+ if (!country)
67
+ return null;
68
+ return COUNTRY_CURRENCY[country] ?? null;
69
+ }
70
+ /**
71
+ * Returns the system's default locale using Node.js ICU data.
72
+ * Falls back to 'en-US' if ICU is unavailable (--no-icu Node builds).
73
+ */
74
+ function getSystemLocale() {
75
+ try {
76
+ return new Intl.DateTimeFormat().resolvedOptions().locale || 'en-US';
77
+ }
78
+ catch {
79
+ return 'en-US';
80
+ }
81
+ }
82
+ /**
83
+ * Resolves the ISO 4217 currency code for the current Connect session.
84
+ *
85
+ * @param sessionCountry - ISO 3166-1 α-2 country from the user's backend profile (preferred).
86
+ * Pass `null` when the session hasn't loaded yet.
87
+ * @returns ISO 4217 currency code, e.g. 'USD', 'INR', 'EUR'.
88
+ * Falls back to 'USD' as last resort (logged at debug level).
89
+ */
90
+ function resolveSessionCurrency(sessionCountry) {
91
+ // 1. Backend session country (most reliable — user explicitly set during registration)
92
+ if (sessionCountry) {
93
+ const code = COUNTRY_CURRENCY[sessionCountry.toUpperCase()];
94
+ if (code)
95
+ return code;
96
+ }
97
+ // 2. System locale (works offline, no network needed)
98
+ const systemLocale = getSystemLocale();
99
+ const localeCurrency = currencyFromLocale(systemLocale);
100
+ if (localeCurrency)
101
+ return localeCurrency;
102
+ // 3. Last resort — avoids null propagation; UI will show '~' prefix to signal estimation
103
+ return 'USD';
104
+ }
105
+ /**
106
+ * Formats a cost amount for display inside the daemon's log output.
107
+ * The web UI uses Intl.NumberFormat directly for richer formatting.
108
+ *
109
+ * @param amount - Numeric cost value.
110
+ * @param currency - ISO 4217 currency code.
111
+ * @param confidence - 'exact' | 'estimated' | 'relative'
112
+ */
113
+ function formatCostForLog(amount, currency, confidence) {
114
+ if (amount === null || currency === null)
115
+ return '(cost unavailable)';
116
+ if (confidence === 'relative') {
117
+ return `~${(amount * 100).toFixed(1)}% of team total`;
118
+ }
119
+ const prefix = confidence === 'estimated' ? '~' : '';
120
+ try {
121
+ return prefix + new Intl.NumberFormat('en', { style: 'currency', currency, maximumFractionDigits: 6 }).format(amount);
122
+ }
123
+ catch {
124
+ return `${prefix}${amount.toFixed(6)} ${currency}`;
125
+ }
126
+ }
127
+ //# sourceMappingURL=currency.js.map
@@ -0,0 +1,50 @@
1
+ import { ConnectV1Service, type DiscoveryCycleResult, type NotificationSender } from './service';
2
+ import type { SanitizedProcessMetadata } from './types';
3
+ import type { VitalityPulseDependencies } from './vitality-pulse';
4
+ import type { ProcessListEntry } from './process-scout';
5
+ export interface DaemonLaunchResult {
6
+ pid: number;
7
+ confirmed: boolean;
8
+ message: string | null;
9
+ }
10
+ export interface DaemonStopResult {
11
+ pid: number | null;
12
+ stopped: boolean;
13
+ message: string;
14
+ }
15
+ export interface DaemonHealthResult {
16
+ running: boolean;
17
+ pid: number | null;
18
+ alive: boolean;
19
+ lastScanAt: string | null;
20
+ lastError: string | null;
21
+ }
22
+ export declare function runDaemonScanCycle(service: ConnectV1Service, options?: {
23
+ processScoutResults?: SanitizedProcessMetadata[];
24
+ pulseDependencies?: VitalityPulseDependencies;
25
+ agentProcessEntries?: ProcessListEntry[];
26
+ notificationSender?: NotificationSender;
27
+ }): Promise<DiscoveryCycleResult>;
28
+ export declare function runDaemonLoopForeground(service: ConnectV1Service, options?: {
29
+ intervalSeconds?: number;
30
+ processScoutResults?: SanitizedProcessMetadata[];
31
+ pulseDependencies?: VitalityPulseDependencies;
32
+ agentProcessEntries?: ProcessListEntry[];
33
+ notificationSender?: NotificationSender;
34
+ }): Promise<void>;
35
+ export declare function getDaemonHealth(service: ConnectV1Service): DaemonHealthResult;
36
+ export declare function waitForDaemonStart(service: ConnectV1Service, expectedPid: number, waitMs?: number): Promise<boolean>;
37
+ export declare function waitForDaemonStop(service: ConnectV1Service, expectedPid: number, waitMs?: number): Promise<boolean>;
38
+ export declare function startDaemonBackground(service: ConnectV1Service, cliPath: string, intervalSeconds?: number, options?: {
39
+ waitMs?: number;
40
+ }): Promise<DaemonLaunchResult>;
41
+ export declare function stopDaemonBackground(service: ConnectV1Service, options?: {
42
+ waitMs?: number;
43
+ }): Promise<DaemonStopResult>;
44
+ export declare function restartDaemonBackground(service: ConnectV1Service, cliPath: string, intervalSeconds?: number, options?: {
45
+ waitMs?: number;
46
+ }): Promise<DaemonLaunchResult & {
47
+ previousPid: number | null;
48
+ stopMessage: string;
49
+ }>;
50
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runDaemonScanCycle = runDaemonScanCycle;
4
+ exports.runDaemonLoopForeground = runDaemonLoopForeground;
5
+ exports.getDaemonHealth = getDaemonHealth;
6
+ exports.waitForDaemonStart = waitForDaemonStart;
7
+ exports.waitForDaemonStop = waitForDaemonStop;
8
+ exports.startDaemonBackground = startDaemonBackground;
9
+ exports.stopDaemonBackground = stopDaemonBackground;
10
+ exports.restartDaemonBackground = restartDaemonBackground;
11
+ const node_child_process_1 = require("node:child_process");
12
+ const heartbeat_1 = require("./heartbeat");
13
+ const DEFAULT_DAEMON_WAIT_MS = 4000;
14
+ const DAEMON_POLL_INTERVAL_MS = 100;
15
+ function sleep(ms) {
16
+ return new Promise((resolve) => {
17
+ setTimeout(resolve, ms);
18
+ });
19
+ }
20
+ function isPidAlive(pid) {
21
+ try {
22
+ process.kill(pid, 0);
23
+ return true;
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
29
+ function buildDaemonChildEnv(service) {
30
+ return {
31
+ ...process.env,
32
+ FORKIT_CONNECT_STATE_DIR: service.getStateStore().getPaths().stateDir,
33
+ };
34
+ }
35
+ async function runDaemonScanCycle(service, options) {
36
+ try {
37
+ // Auto-configure runtime signal key from env var if not yet stored.
38
+ // This promotes pre-binding local-only events to syncable once a GAID is bound.
39
+ service.autoConfigureRuntimeSignalKeyFromEnv();
40
+ const result = options?.processScoutResults
41
+ ? await service.runDiscoveryCycle({ processScoutResults: options.processScoutResults })
42
+ : await service.runDiscoveryCycle();
43
+ service.recordDaemonScan(result.summary);
44
+ if (options?.pulseDependencies) {
45
+ await service.runVitalityPulseCycle(options.pulseDependencies);
46
+ }
47
+ else if (options?.processScoutResults) {
48
+ await service.runVitalityPulseCycle({ processScoutResults: options.processScoutResults });
49
+ }
50
+ else {
51
+ await service.runVitalityPulseCycle();
52
+ }
53
+ if (options?.agentProcessEntries) {
54
+ await service.scanAgents({ processEntries: options.agentProcessEntries, discoverySource: 'daemon_scan' });
55
+ }
56
+ service.syncEvolutionCandidates();
57
+ service.buildSmartRegistrationInbox();
58
+ const heartbeatBatch = await (0, heartbeat_1.sendAllBoundHeartbeats)(service);
59
+ if (heartbeatBatch.runtimeSignalQueued > 0) {
60
+ await service.processQueue();
61
+ }
62
+ await service.syncC2SessionsFromBackend({ suppressErrors: true });
63
+ await service.flushC2LifecycleEvents({ suppressErrors: true });
64
+ service.deliverPendingNotifications(options?.notificationSender ? { notificationSender: options.notificationSender } : undefined);
65
+ return result;
66
+ }
67
+ catch (error) {
68
+ const message = error instanceof Error ? error.message : 'daemon_scan_failed';
69
+ service.recordDaemonError(message);
70
+ throw error;
71
+ }
72
+ }
73
+ async function runDaemonLoopForeground(service, options) {
74
+ const intervalSeconds = options?.intervalSeconds ?? service.getConfig().daemon_interval_seconds;
75
+ service.markDaemonStarted(process.pid);
76
+ let keepRunning = true;
77
+ let stopPersisted = false;
78
+ const stopLoop = () => {
79
+ keepRunning = false;
80
+ };
81
+ const persistStopped = (lastError) => {
82
+ if (stopPersisted)
83
+ return;
84
+ stopPersisted = true;
85
+ service.markDaemonStopped(lastError);
86
+ };
87
+ const handleTermination = () => {
88
+ stopLoop();
89
+ persistStopped(null);
90
+ process.exit(0);
91
+ };
92
+ process.on('SIGINT', handleTermination);
93
+ process.on('SIGTERM', handleTermination);
94
+ try {
95
+ while (keepRunning) {
96
+ if (options?.processScoutResults) {
97
+ if (options.pulseDependencies) {
98
+ await runDaemonScanCycle(service, {
99
+ processScoutResults: options.processScoutResults,
100
+ pulseDependencies: options.pulseDependencies,
101
+ ...(options.notificationSender ? { notificationSender: options.notificationSender } : {}),
102
+ ...(options.agentProcessEntries ? { agentProcessEntries: options.agentProcessEntries } : {}),
103
+ });
104
+ }
105
+ else {
106
+ await runDaemonScanCycle(service, {
107
+ processScoutResults: options.processScoutResults,
108
+ ...(options.notificationSender ? { notificationSender: options.notificationSender } : {}),
109
+ ...(options.agentProcessEntries ? { agentProcessEntries: options.agentProcessEntries } : {}),
110
+ });
111
+ }
112
+ }
113
+ else {
114
+ await runDaemonScanCycle(service, options?.pulseDependencies
115
+ ? {
116
+ pulseDependencies: options.pulseDependencies,
117
+ ...(options.notificationSender ? { notificationSender: options.notificationSender } : {}),
118
+ ...(options.agentProcessEntries ? { agentProcessEntries: options.agentProcessEntries } : {}),
119
+ }
120
+ : options?.agentProcessEntries
121
+ ? { agentProcessEntries: options.agentProcessEntries, ...(options.notificationSender ? { notificationSender: options.notificationSender } : {}) }
122
+ : options?.notificationSender
123
+ ? { notificationSender: options.notificationSender }
124
+ : undefined);
125
+ }
126
+ if (!keepRunning)
127
+ break;
128
+ await sleep(intervalSeconds * 1000);
129
+ }
130
+ persistStopped(null);
131
+ }
132
+ catch (error) {
133
+ persistStopped(error instanceof Error ? error.message : 'daemon_loop_failed');
134
+ throw error;
135
+ }
136
+ finally {
137
+ process.off('SIGINT', handleTermination);
138
+ process.off('SIGTERM', handleTermination);
139
+ }
140
+ }
141
+ function getDaemonHealth(service) {
142
+ const status = service.getDaemonStatus();
143
+ return {
144
+ running: status.daemon_running,
145
+ pid: status.daemon_pid,
146
+ alive: Boolean(status.daemon_pid && isPidAlive(status.daemon_pid)),
147
+ lastScanAt: status.last_scan_at,
148
+ lastError: status.last_error,
149
+ };
150
+ }
151
+ async function waitForDaemonStart(service, expectedPid, waitMs = DEFAULT_DAEMON_WAIT_MS) {
152
+ const deadline = Date.now() + Math.max(waitMs, DAEMON_POLL_INTERVAL_MS);
153
+ while (Date.now() <= deadline) {
154
+ const status = service.getDaemonStatus();
155
+ if (status.daemon_running && status.daemon_pid === expectedPid) {
156
+ return true;
157
+ }
158
+ if (!isPidAlive(expectedPid)) {
159
+ return false;
160
+ }
161
+ await sleep(DAEMON_POLL_INTERVAL_MS);
162
+ }
163
+ return false;
164
+ }
165
+ async function waitForDaemonStop(service, expectedPid, waitMs = DEFAULT_DAEMON_WAIT_MS) {
166
+ const deadline = Date.now() + Math.max(waitMs, DAEMON_POLL_INTERVAL_MS);
167
+ while (Date.now() <= deadline) {
168
+ const status = service.getDaemonStatus();
169
+ if (!status.daemon_running || status.daemon_pid !== expectedPid) {
170
+ return true;
171
+ }
172
+ await sleep(DAEMON_POLL_INTERVAL_MS);
173
+ }
174
+ return !isPidAlive(expectedPid);
175
+ }
176
+ async function startDaemonBackground(service, cliPath, intervalSeconds, options) {
177
+ const current = getDaemonHealth(service);
178
+ if (current.running && current.pid && current.alive) {
179
+ return {
180
+ pid: current.pid,
181
+ confirmed: true,
182
+ message: `Forkit Connect daemon is already running. pid=${current.pid}`,
183
+ };
184
+ }
185
+ const args = [cliPath, 'daemon', 'run-loop'];
186
+ if (intervalSeconds !== undefined) {
187
+ args.push('--interval-seconds', String(intervalSeconds));
188
+ }
189
+ const child = (0, node_child_process_1.spawn)(process.execPath, args, {
190
+ detached: true,
191
+ stdio: 'ignore',
192
+ cwd: process.cwd(),
193
+ env: buildDaemonChildEnv(service),
194
+ });
195
+ child.unref();
196
+ const pid = child.pid ?? 0;
197
+ if (!pid) {
198
+ return {
199
+ pid: 0,
200
+ confirmed: false,
201
+ message: 'Forkit Connect could not allocate a daemon process id.',
202
+ };
203
+ }
204
+ const confirmed = await waitForDaemonStart(service, pid, options?.waitMs);
205
+ return {
206
+ pid,
207
+ confirmed,
208
+ message: confirmed ? null : 'Forkit Connect daemon did not report healthy startup before timeout.',
209
+ };
210
+ }
211
+ async function stopDaemonBackground(service, options) {
212
+ const status = service.getDaemonStatus();
213
+ if (!status.daemon_running || !status.daemon_pid) {
214
+ return {
215
+ pid: null,
216
+ stopped: true,
217
+ message: 'Daemon is not running.',
218
+ };
219
+ }
220
+ try {
221
+ process.kill(status.daemon_pid, 'SIGTERM');
222
+ }
223
+ catch (error) {
224
+ service.markDaemonStopped(error instanceof Error ? error.message : 'daemon_stop_failed');
225
+ return {
226
+ pid: status.daemon_pid,
227
+ stopped: false,
228
+ message: error instanceof Error ? error.message : 'Failed to stop daemon.',
229
+ };
230
+ }
231
+ const stopped = await waitForDaemonStop(service, status.daemon_pid, options?.waitMs);
232
+ if (!stopped) {
233
+ service.recordDaemonError('daemon_stop_timeout');
234
+ return {
235
+ pid: status.daemon_pid,
236
+ stopped: false,
237
+ message: 'Forkit Connect daemon did not stop before timeout.',
238
+ };
239
+ }
240
+ return {
241
+ pid: status.daemon_pid,
242
+ stopped: true,
243
+ message: `Daemon stopped. pid=${status.daemon_pid}`,
244
+ };
245
+ }
246
+ async function restartDaemonBackground(service, cliPath, intervalSeconds, options) {
247
+ const currentStatus = service.getDaemonStatus();
248
+ const stopResult = await stopDaemonBackground(service, options);
249
+ if (currentStatus.daemon_running && currentStatus.daemon_pid && !stopResult.stopped) {
250
+ return {
251
+ pid: currentStatus.daemon_pid,
252
+ confirmed: false,
253
+ previousPid: currentStatus.daemon_pid,
254
+ stopMessage: stopResult.message,
255
+ message: stopResult.message,
256
+ };
257
+ }
258
+ const launch = await startDaemonBackground(service, cliPath, intervalSeconds, options);
259
+ return {
260
+ ...launch,
261
+ previousPid: currentStatus.daemon_pid,
262
+ stopMessage: stopResult.message,
263
+ };
264
+ }
265
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1,61 @@
1
+ import type { DetectedModel, DetectedRuntime, DiscoverySource } from './types';
2
+ export interface RuntimeDiscoveryIdentityInput {
3
+ runtimeName: string;
4
+ runtimeType: string;
5
+ sourceEndpoint: string;
6
+ runtimeIdentity: string;
7
+ }
8
+ export interface ModelDiscoveryIdentityInput extends RuntimeDiscoveryIdentityInput {
9
+ modelName: string;
10
+ modelId: string | null;
11
+ }
12
+ export interface AgentDiscoveryIdentityInput {
13
+ agentName: string;
14
+ agentType: string;
15
+ processSignatureHash: string;
16
+ runtimeIdentity: string;
17
+ }
18
+ export interface RuntimeAttestationFingerprintInput {
19
+ runtimeName: string;
20
+ runtimeType: string;
21
+ runtimeProvider: string;
22
+ endpointHash: string;
23
+ runtimeIdentity: string;
24
+ processSignature: string | null;
25
+ processCommandHash: string | null;
26
+ processPathHash: string | null;
27
+ detectionMode: 'signature' | 'heuristic' | null;
28
+ sourceLabel: string | null;
29
+ matchedTerms: string[];
30
+ expectedPorts: number[];
31
+ }
32
+ export interface ModelObservationFingerprintInput {
33
+ runtimeName: string;
34
+ runtimeType: string;
35
+ modelName: string;
36
+ modelId: string | null;
37
+ digest: string;
38
+ sizeBytes: number;
39
+ modifiedAt: string | null;
40
+ metadataFingerprint: string;
41
+ }
42
+ export interface RuntimePassportGaidInput {
43
+ connectDeviceId: string;
44
+ runtimeName: string;
45
+ runtimeType: string;
46
+ provider: string;
47
+ endpointHash: string;
48
+ }
49
+ export declare function readEndpointPort(sourceEndpoint: string): number | null;
50
+ export declare function buildEndpointHash(sourceEndpoint: string): string;
51
+ export declare function buildSourceEndpointLabel(sourceEndpoint: string): string;
52
+ export declare function buildRuntimePassportGaid(input: RuntimePassportGaidInput): string;
53
+ export declare function buildRuntimeDiscoveryHash(input: RuntimeDiscoveryIdentityInput): string;
54
+ export declare function buildModelDiscoveryHash(input: ModelDiscoveryIdentityInput): string;
55
+ export declare function buildModelRegistrationKey(model: Pick<DetectedModel, 'model' | 'modelId'>, runtimeIdentity: string): string;
56
+ export declare function buildAgentDiscoveryHash(input: AgentDiscoveryIdentityInput): string;
57
+ export declare function buildRuntimeAttestationFingerprint(input: RuntimeAttestationFingerprintInput): string;
58
+ export declare function buildModelObservationFingerprint(input: ModelObservationFingerprintInput): string;
59
+ export declare function applyRuntimeDiscovery(runtime: Omit<DetectedRuntime, 'discoveryHash' | 'discoverySources'>, runtimeIdentity: string, sources?: DiscoverySource[]): DetectedRuntime;
60
+ export declare function applyModelDiscovery(model: Omit<DetectedModel, 'discoveryHash' | 'discoverySources'>, runtimeIdentity: string, sources?: DiscoverySource[]): DetectedModel;
61
+ //# sourceMappingURL=discovery.d.ts.map