forkit-connect 0.1.23 → 0.1.25
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 +7 -3
- package/dist/cli.js +41 -124
- package/dist/v1/api.d.ts +5 -0
- package/dist/v1/api.js +1 -1
- package/dist/v1/credential-store.js +5 -0
- package/dist/v1/service.js +8 -164
- package/package.json +1 -3
- package/assets/connect-notifier-512.png +0 -0
- package/dist/v1/startup.d.ts +0 -12
- package/dist/v1/startup.js +0 -229
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
|
-
-
|
|
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 `
|
|
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.
|
|
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");
|
|
@@ -55,17 +54,17 @@ const CLI_FALLBACK_PLAN_LIMITS = {
|
|
|
55
54
|
signal: {
|
|
56
55
|
privatePassports: 25,
|
|
57
56
|
draftPassports: 25,
|
|
58
|
-
maxWorkspaces:
|
|
59
|
-
maxProjects:
|
|
60
|
-
maxGovernedPassports:
|
|
57
|
+
maxWorkspaces: 1,
|
|
58
|
+
maxProjects: 1,
|
|
59
|
+
maxGovernedPassports: 25,
|
|
61
60
|
runtimeSignalsPerMonth: 10000,
|
|
62
61
|
},
|
|
63
62
|
protocol: {
|
|
64
63
|
privatePassports: 20,
|
|
65
64
|
draftPassports: 20,
|
|
66
|
-
maxWorkspaces:
|
|
67
|
-
maxProjects:
|
|
68
|
-
maxGovernedPassports:
|
|
65
|
+
maxWorkspaces: 10,
|
|
66
|
+
maxProjects: 10,
|
|
67
|
+
maxGovernedPassports: 20,
|
|
69
68
|
runtimeSignalsPerMonth: 250000,
|
|
70
69
|
},
|
|
71
70
|
sovereign: {
|
|
@@ -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', '
|
|
123
|
-
console.log(cliKeyLine('
|
|
124
|
-
console.log(cliKeyLine('
|
|
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('
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
|
273
|
-
console.log('[forkit-connect] Forkit Connect
|
|
274
|
-
console.log('[forkit-connect]
|
|
275
|
-
console.log('[forkit-connect] Headless automation may pass FORKIT_CONNECT_SESSION_REF explicitly
|
|
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 '
|
|
938
|
+
return 'personal device / workspace not selected';
|
|
955
939
|
}
|
|
956
940
|
function summarizeReviewQueueCounts(values) {
|
|
957
941
|
const parts = [
|
|
@@ -1984,7 +1968,14 @@ async function maybeShowInteractiveAgentReviewTable(snapshot) {
|
|
|
1984
1968
|
});
|
|
1985
1969
|
}
|
|
1986
1970
|
function formatWorkspaceStateCell(workspace) {
|
|
1987
|
-
|
|
1971
|
+
const projectCount = Number(workspace.projectCountHint);
|
|
1972
|
+
const passportCount = Number(workspace.passportCountHint);
|
|
1973
|
+
return [
|
|
1974
|
+
String(workspace.visibility || '').trim(),
|
|
1975
|
+
String(workspace.lifecycleStatus || '').trim(),
|
|
1976
|
+
Number.isFinite(projectCount) && projectCount >= 0 ? `${projectCount} project${projectCount === 1 ? '' : 's'}` : null,
|
|
1977
|
+
Number.isFinite(passportCount) && passportCount >= 0 ? `${passportCount} passport${passportCount === 1 ? '' : 's'}` : null,
|
|
1978
|
+
]
|
|
1988
1979
|
.filter(Boolean)
|
|
1989
1980
|
.join(' · ') || 'active';
|
|
1990
1981
|
}
|
|
@@ -2146,7 +2137,6 @@ function printPublicStatusOverview(status) {
|
|
|
2146
2137
|
console.log(cliKeyLine('device', status.device_paired ? 'paired' : 'approval pending'));
|
|
2147
2138
|
console.log(cliKeyLine('scope', formatCliScopeLabel(status.workspace_id, status.project_id)));
|
|
2148
2139
|
console.log(cliKeyLine('daemon', status.daemon_status));
|
|
2149
|
-
console.log(cliKeyLine('background', status.daemon_status === 'running' ? 'always on' : 'start required'));
|
|
2150
2140
|
console.log(cliKeyLine('privacy', status.privacy_mode));
|
|
2151
2141
|
console.log(cliKeyLine('inventory', joinCliSummary([
|
|
2152
2142
|
formatCliCompactCount(status.models_discovered, 'model'),
|
|
@@ -2167,47 +2157,6 @@ function printPublicStatusOverview(status) {
|
|
|
2167
2157
|
console.log(cliKeyLine('warning', 'Local governed scope exists, but login is still required.'));
|
|
2168
2158
|
}
|
|
2169
2159
|
}
|
|
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
2160
|
function getOtherReadyCount(readyToConnectCount, draftFirstCount) {
|
|
2212
2161
|
return Math.max(0, readyToConnectCount - draftFirstCount);
|
|
2213
2162
|
}
|
|
@@ -2688,7 +2637,6 @@ async function run() {
|
|
|
2688
2637
|
const runPublicConnectStatus = async () => {
|
|
2689
2638
|
const sessionState = await checkBackendSessionState(service);
|
|
2690
2639
|
const secureStorage = service.getCredentialStoreStatus();
|
|
2691
|
-
const startupStatus = (0, startup_1.getCliStartupStatus)();
|
|
2692
2640
|
if (service.readSessionRef()) {
|
|
2693
2641
|
await withTimeout(service.refreshEffectiveBinding(), STATUS_BINDING_TIMEOUT_MS, undefined);
|
|
2694
2642
|
}
|
|
@@ -2696,7 +2644,12 @@ async function run() {
|
|
|
2696
2644
|
const overview = withSmartInboxSnapshotCounts(service.getConnectStatusOverview({ includeInbox: false }));
|
|
2697
2645
|
const localScopeCached = Boolean(String(overview.workspace_id || '').trim() || String(overview.project_id || '').trim());
|
|
2698
2646
|
const displayOverview = accountTrusted
|
|
2699
|
-
?
|
|
2647
|
+
? {
|
|
2648
|
+
...overview,
|
|
2649
|
+
lifecycle_note: !localScopeCached && overview.device_paired
|
|
2650
|
+
? 'Account connected. Personal scanning works; select workspace/project later for C2.'
|
|
2651
|
+
: overview.lifecycle_note,
|
|
2652
|
+
}
|
|
2700
2653
|
: {
|
|
2701
2654
|
...overview,
|
|
2702
2655
|
workspace_id: null,
|
|
@@ -2714,10 +2667,6 @@ async function run() {
|
|
|
2714
2667
|
session_truth: accountTrusted ? 'account_verified_or_offline' : 'local_scope_cached_login_required',
|
|
2715
2668
|
local_scope_cached: !accountTrusted && localScopeCached,
|
|
2716
2669
|
binding_truth: accountTrusted ? 'account_binding_active' : 'account_login_required',
|
|
2717
|
-
startup_on_login: startupStatus,
|
|
2718
|
-
notifications: {
|
|
2719
|
-
readiness: getNotificationReadiness(),
|
|
2720
|
-
},
|
|
2721
2670
|
secure_storage: {
|
|
2722
2671
|
backend: secureStorage.backend,
|
|
2723
2672
|
available: secureStorage.available,
|
|
@@ -2733,28 +2682,8 @@ async function run() {
|
|
|
2733
2682
|
if (!secureStorage.available || secureStorage.plaintextFallbackActive) {
|
|
2734
2683
|
console.log(cliKeyLine('secure note', secureStorage.detail));
|
|
2735
2684
|
}
|
|
2736
|
-
console.log(cliKeyLine('notifications', getNotificationReadiness()));
|
|
2737
|
-
printStartupStatusLine(startupStatus);
|
|
2738
2685
|
printPublicStatusGuidance(displayOverview, sessionState);
|
|
2739
2686
|
};
|
|
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
2687
|
const runPublicConnectInbox = async () => {
|
|
2759
2688
|
const forceRefresh = hasFlag('--refresh');
|
|
2760
2689
|
const inbox = service.getSmartRegistrationInbox({
|
|
@@ -2850,7 +2779,8 @@ async function run() {
|
|
|
2850
2779
|
}
|
|
2851
2780
|
console.log(`[forkit-connect] Queue processed=${result.sync.processed} succeeded=${result.sync.succeeded} failed=${result.sync.failed}`);
|
|
2852
2781
|
if (!result.ok) {
|
|
2853
|
-
|
|
2782
|
+
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.');
|
|
2783
|
+
process.exitCode = 0;
|
|
2854
2784
|
}
|
|
2855
2785
|
};
|
|
2856
2786
|
const runPublicUpdateCheck = async () => {
|
|
@@ -3009,12 +2939,14 @@ async function run() {
|
|
|
3009
2939
|
}
|
|
3010
2940
|
const operatingMode = resolveOperatingMode(service);
|
|
3011
2941
|
const accountTrusted = sessionState === 'authorized';
|
|
2942
|
+
const accountLimits = accountTrusted ? await loadCliAccountLimits().catch(() => null) : null;
|
|
3012
2943
|
const overview = withSmartInboxSnapshotCounts(service.getConnectStatusOverview({ includeInbox: false }));
|
|
3013
2944
|
const localScopeCached = Boolean(String(overview.workspace_id || '').trim() || String(overview.project_id || '').trim());
|
|
3014
2945
|
return {
|
|
3015
2946
|
session_state: sessionState,
|
|
3016
2947
|
operating_mode: operatingMode.mode,
|
|
3017
|
-
tier: operatingMode.tier,
|
|
2948
|
+
tier: accountLimits?.planKey ?? operatingMode.tier,
|
|
2949
|
+
plan_name: accountLimits?.planName ?? (operatingMode.tier ? getCliPlanName(resolveCliPlanKey(operatingMode.tier)) : null),
|
|
3018
2950
|
workspace_id: accountTrusted ? overview.workspace_id : null,
|
|
3019
2951
|
project_id: accountTrusted ? overview.project_id : null,
|
|
3020
2952
|
binding_state: accountTrusted ? overview.binding_state : 'login_required',
|
|
@@ -4290,7 +4222,7 @@ async function run() {
|
|
|
4290
4222
|
? draftsResult.body.drafts
|
|
4291
4223
|
: [];
|
|
4292
4224
|
const passports = passportsResult?.ok ? readPassportList(passportsResult.body) : [];
|
|
4293
|
-
const planKey = resolveCliPlanKey(summaryPayload?.planCapabilities?.planKey, summaryPayload?.package?.slug, summaryPayload?.tier, accessPayload?.summary?.tier, service.getServiceEntitlements().tier);
|
|
4225
|
+
const planKey = resolveCliPlanKey(summaryPayload?.planCapabilities?.planKey, summaryPayload?.package?.slug, accessPayload?.summary?.planKey, accessPayload?.summary?.packageSlug, summaryPayload?.tier, accessPayload?.summary?.tier, service.getServiceEntitlements().tier);
|
|
4294
4226
|
const fallback = CLI_FALLBACK_PLAN_LIMITS[planKey];
|
|
4295
4227
|
let projectsUsed = typeof summaryPayload?.usage?.projects === 'number' ? summaryPayload.usage.projects : null;
|
|
4296
4228
|
if (projectsUsed === null && workspaces.length > 0) {
|
|
@@ -4695,6 +4627,7 @@ async function run() {
|
|
|
4695
4627
|
console.log('[forkit-connect] Workspace status');
|
|
4696
4628
|
console.log(`- mode=${payload.operating_mode}`);
|
|
4697
4629
|
console.log(`- tier=${payload.tier || 'unknown'}`);
|
|
4630
|
+
console.log(`- plan=${payload.plan_name || 'unknown'}`);
|
|
4698
4631
|
console.log(`- workspace=${payload.workspace_id || 'not selected'}`);
|
|
4699
4632
|
console.log(`- project=${payload.project_id || 'not selected'}`);
|
|
4700
4633
|
console.log(`- session=${payload.session_state}`);
|
|
@@ -5017,24 +4950,6 @@ async function run() {
|
|
|
5017
4950
|
await runPublicConnectStatus();
|
|
5018
4951
|
return;
|
|
5019
4952
|
}
|
|
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
4953
|
if (command === 'changes') {
|
|
5039
4954
|
runPublicCollectedChanges();
|
|
5040
4955
|
return;
|
|
@@ -6293,12 +6208,14 @@ async function run() {
|
|
|
6293
6208
|
? tokenPayload.role
|
|
6294
6209
|
: null;
|
|
6295
6210
|
const workspaces = Array.isArray(payload.workspaces) ? payload.workspaces : [];
|
|
6211
|
+
const accountLimits = await loadCliAccountLimits().catch(() => null);
|
|
6296
6212
|
if (await maybeShowInteractiveWorkspaceTable(workspaces)) {
|
|
6297
6213
|
return;
|
|
6298
6214
|
}
|
|
6299
6215
|
printWorkspaceListSurface(workspaces, {
|
|
6300
6216
|
accountEmail: email,
|
|
6301
6217
|
platformRole,
|
|
6218
|
+
accountLimits,
|
|
6302
6219
|
});
|
|
6303
6220
|
return;
|
|
6304
6221
|
}
|
package/dist/v1/api.d.ts
CHANGED
|
@@ -62,6 +62,8 @@ export interface ProfileAccessWorkspace {
|
|
|
62
62
|
visibility?: 'private' | 'public' | string;
|
|
63
63
|
verificationStatus?: 'unverified' | 'verified' | 'revoked' | string;
|
|
64
64
|
lifecycleStatus?: 'active' | 'deactivated' | string;
|
|
65
|
+
projectCountHint?: number | null;
|
|
66
|
+
passportCountHint?: number | null;
|
|
65
67
|
createdAt?: string | null;
|
|
66
68
|
updatedAt?: string | null;
|
|
67
69
|
}
|
|
@@ -69,6 +71,9 @@ export interface ProfileAccessResponse {
|
|
|
69
71
|
summary?: {
|
|
70
72
|
platformRole?: string;
|
|
71
73
|
tier?: string;
|
|
74
|
+
planKey?: string;
|
|
75
|
+
planLabel?: string;
|
|
76
|
+
packageSlug?: string;
|
|
72
77
|
};
|
|
73
78
|
workspaces?: ProfileAccessWorkspace[];
|
|
74
79
|
}
|
package/dist/v1/api.js
CHANGED
|
@@ -112,7 +112,7 @@ class ConnectApiClient {
|
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
114
|
async getProductSummary() {
|
|
115
|
-
const url = `${this.config.baseUrl}/product/summary`;
|
|
115
|
+
const url = `${this.config.baseUrl}/api/product/summary`;
|
|
116
116
|
const res = await fetch(url, {
|
|
117
117
|
method: 'GET',
|
|
118
118
|
headers: this.getHeaders(),
|
|
@@ -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 {
|
package/dist/v1/service.js
CHANGED
|
@@ -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
|
|
213
|
+
const targetUrl = notificationTargetUrl(candidate);
|
|
214
|
+
const resolvedNotifier = resolveTerminalNotifierPath();
|
|
364
215
|
if (resolvedNotifier) {
|
|
365
|
-
const
|
|
366
|
-
const
|
|
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 =
|
|
8928
|
+
const notifierPath = resolveTerminalNotifierPath();
|
|
9085
8929
|
checks.push({
|
|
9086
8930
|
name: 'desktop_notifications',
|
|
9087
8931
|
status: notifierPath ? 'PASS' : 'WARN',
|
|
9088
8932
|
details: notifierPath
|
|
9089
|
-
?
|
|
9090
|
-
: '
|
|
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.
|
|
3
|
+
"version": "0.1.25",
|
|
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
|
package/dist/v1/startup.d.ts
DELETED
|
@@ -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
|
package/dist/v1/startup.js
DELETED
|
@@ -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
|