forkit-connect 0.1.22 → 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 +7 -3
- package/dist/cli.js +13 -7
- package/dist/v1/credential-store.js +5 -0
- package/dist/v1/native-shell.d.ts +23 -0
- package/dist/v1/native-shell.js +170 -0
- package/package.json +1 -1
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
|
@@ -253,10 +253,10 @@ function printDeviceLoginInstructions(start) {
|
|
|
253
253
|
}
|
|
254
254
|
function printSessionExportFallback(token) {
|
|
255
255
|
void token;
|
|
256
|
-
console.log('[forkit-connect] Approval succeeded, but secure credential storage is unavailable
|
|
257
|
-
console.log('[forkit-connect] Forkit Connect
|
|
258
|
-
console.log('[forkit-connect]
|
|
259
|
-
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.');
|
|
260
260
|
}
|
|
261
261
|
function hasLinuxGuiSession() {
|
|
262
262
|
if (process.platform !== 'linux') {
|
|
@@ -935,7 +935,7 @@ function formatCliScopeLabel(workspaceId, projectId) {
|
|
|
935
935
|
if (workspace) {
|
|
936
936
|
return `${shortId(workspace)} / project needed`;
|
|
937
937
|
}
|
|
938
|
-
return '
|
|
938
|
+
return 'personal device / workspace not selected';
|
|
939
939
|
}
|
|
940
940
|
function summarizeReviewQueueCounts(values) {
|
|
941
941
|
const parts = [
|
|
@@ -2637,7 +2637,12 @@ async function run() {
|
|
|
2637
2637
|
const overview = withSmartInboxSnapshotCounts(service.getConnectStatusOverview({ includeInbox: false }));
|
|
2638
2638
|
const localScopeCached = Boolean(String(overview.workspace_id || '').trim() || String(overview.project_id || '').trim());
|
|
2639
2639
|
const displayOverview = accountTrusted
|
|
2640
|
-
?
|
|
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
|
+
}
|
|
2641
2646
|
: {
|
|
2642
2647
|
...overview,
|
|
2643
2648
|
workspace_id: null,
|
|
@@ -2767,7 +2772,8 @@ async function run() {
|
|
|
2767
2772
|
}
|
|
2768
2773
|
console.log(`[forkit-connect] Queue processed=${result.sync.processed} succeeded=${result.sync.succeeded} failed=${result.sync.failed}`);
|
|
2769
2774
|
if (!result.ok) {
|
|
2770
|
-
|
|
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;
|
|
2771
2777
|
}
|
|
2772
2778
|
};
|
|
2773
2779
|
const runPublicUpdateCheck = async () => {
|
|
@@ -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 {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ReleaseCheckResult } from './update';
|
|
2
|
+
export interface NativeShellStatus {
|
|
3
|
+
supported: boolean;
|
|
4
|
+
installed: boolean;
|
|
5
|
+
platform: 'macos' | 'windows' | 'linux';
|
|
6
|
+
installationPath: string | null;
|
|
7
|
+
trayAvailable: boolean;
|
|
8
|
+
startupAvailable: boolean;
|
|
9
|
+
directInstallerAvailable: boolean;
|
|
10
|
+
downloadUrl: string | null;
|
|
11
|
+
releaseNotesUrl: string | null;
|
|
12
|
+
note: string;
|
|
13
|
+
}
|
|
14
|
+
export interface NativeShellOpenResult {
|
|
15
|
+
ok: boolean;
|
|
16
|
+
opened: 'installed' | 'download';
|
|
17
|
+
installationPath: string | null;
|
|
18
|
+
downloadUrl: string | null;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function getNativeShellStatus(updateCheck?: ReleaseCheckResult): Promise<NativeShellStatus>;
|
|
22
|
+
export declare function openNativeShell(updateCheck?: ReleaseCheckResult): Promise<NativeShellOpenResult>;
|
|
23
|
+
//# sourceMappingURL=native-shell.d.ts.map
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getNativeShellStatus = getNativeShellStatus;
|
|
7
|
+
exports.openNativeShell = openNativeShell;
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const node_child_process_1 = require("node:child_process");
|
|
12
|
+
const update_1 = require("./update");
|
|
13
|
+
const CONNECT_DESKTOP_FALLBACK_URL = 'https://www.forkit.dev/connect';
|
|
14
|
+
function normalizePlatform() {
|
|
15
|
+
if (process.platform === 'darwin')
|
|
16
|
+
return 'macos';
|
|
17
|
+
if (process.platform === 'win32')
|
|
18
|
+
return 'windows';
|
|
19
|
+
return 'linux';
|
|
20
|
+
}
|
|
21
|
+
function getCandidatePaths(platform) {
|
|
22
|
+
const home = node_os_1.default.homedir();
|
|
23
|
+
if (platform === 'macos') {
|
|
24
|
+
return [
|
|
25
|
+
'/Applications/Forkit Connect.app',
|
|
26
|
+
node_path_1.default.join(home, 'Applications', 'Forkit Connect.app'),
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
if (platform === 'windows') {
|
|
30
|
+
const localAppData = process.env.LOCALAPPDATA || node_path_1.default.join(home, 'AppData', 'Local');
|
|
31
|
+
const programFiles = process.env.ProgramFiles || 'C:\\Program Files';
|
|
32
|
+
const programFilesX86 = process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)';
|
|
33
|
+
return [
|
|
34
|
+
node_path_1.default.join(localAppData, 'Programs', 'Forkit Connect', 'Forkit Connect.exe'),
|
|
35
|
+
node_path_1.default.join(programFiles, 'Forkit Connect', 'Forkit Connect.exe'),
|
|
36
|
+
node_path_1.default.join(programFilesX86, 'Forkit Connect', 'Forkit Connect.exe'),
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
return [
|
|
40
|
+
node_path_1.default.join(home, '.local', 'bin', 'forkit-connect-desktop'),
|
|
41
|
+
node_path_1.default.join(home, '.local', 'share', 'Forkit Connect', 'Forkit Connect.AppImage'),
|
|
42
|
+
'/usr/local/bin/forkit-connect-desktop',
|
|
43
|
+
'/opt/Forkit Connect/forkit-connect-desktop',
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
function findInstalledPath(platform) {
|
|
47
|
+
for (const candidate of getCandidatePaths(platform)) {
|
|
48
|
+
try {
|
|
49
|
+
if (node_fs_1.default.existsSync(candidate)) {
|
|
50
|
+
return candidate;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function parseVersion(version) {
|
|
60
|
+
const cleaned = String(version || '').trim().replace(/^v/i, '').split('-')[0] || '0.0.0';
|
|
61
|
+
const [major, minor, patch] = cleaned.split('.').map((value) => Number.parseInt(value || '0', 10));
|
|
62
|
+
return [major || 0, minor || 0, patch || 0];
|
|
63
|
+
}
|
|
64
|
+
function compareVersions(left, right) {
|
|
65
|
+
const [leftMajor, leftMinor, leftPatch] = parseVersion(left);
|
|
66
|
+
const [rightMajor, rightMinor, rightPatch] = parseVersion(right);
|
|
67
|
+
if (leftMajor !== rightMajor)
|
|
68
|
+
return leftMajor - rightMajor;
|
|
69
|
+
if (leftMinor !== rightMinor)
|
|
70
|
+
return leftMinor - rightMinor;
|
|
71
|
+
return leftPatch - rightPatch;
|
|
72
|
+
}
|
|
73
|
+
function buildDownloadUrl(updateCheck) {
|
|
74
|
+
if (!updateCheck.ok) {
|
|
75
|
+
return CONNECT_DESKTOP_FALLBACK_URL;
|
|
76
|
+
}
|
|
77
|
+
if (compareVersions(updateCheck.manifest.latestVersion, updateCheck.currentVersion) < 0) {
|
|
78
|
+
return CONNECT_DESKTOP_FALLBACK_URL;
|
|
79
|
+
}
|
|
80
|
+
return updateCheck.downloadUrl || updateCheck.manifest.releaseNotesUrl || CONNECT_DESKTOP_FALLBACK_URL;
|
|
81
|
+
}
|
|
82
|
+
function hasDirectInstaller(updateCheck) {
|
|
83
|
+
if (!updateCheck.ok)
|
|
84
|
+
return false;
|
|
85
|
+
if (compareVersions(updateCheck.manifest.latestVersion, updateCheck.currentVersion) < 0) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return Boolean(updateCheck.downloadUrl);
|
|
89
|
+
}
|
|
90
|
+
function isInstallerOnlyRelease(updateCheck) {
|
|
91
|
+
if (!updateCheck.ok)
|
|
92
|
+
return false;
|
|
93
|
+
const manifest = updateCheck.manifest;
|
|
94
|
+
const integrity = manifest.integrity;
|
|
95
|
+
if (!integrity || typeof integrity !== 'object')
|
|
96
|
+
return false;
|
|
97
|
+
return integrity.installerOnly === true;
|
|
98
|
+
}
|
|
99
|
+
async function getNativeShellStatus(updateCheck) {
|
|
100
|
+
const platform = normalizePlatform();
|
|
101
|
+
const installationPath = findInstalledPath(platform);
|
|
102
|
+
const installed = Boolean(installationPath);
|
|
103
|
+
const resolvedUpdateCheck = updateCheck ?? await (0, update_1.checkForUpdates)();
|
|
104
|
+
const directInstallerAvailable = hasDirectInstaller(resolvedUpdateCheck);
|
|
105
|
+
const installerOnlyRelease = isInstallerOnlyRelease(resolvedUpdateCheck);
|
|
106
|
+
const downloadUrl = buildDownloadUrl(resolvedUpdateCheck);
|
|
107
|
+
const releaseNotesUrl = resolvedUpdateCheck.ok ? resolvedUpdateCheck.manifest.releaseNotesUrl : CONNECT_DESKTOP_FALLBACK_URL;
|
|
108
|
+
return {
|
|
109
|
+
supported: true,
|
|
110
|
+
installed,
|
|
111
|
+
platform,
|
|
112
|
+
installationPath,
|
|
113
|
+
trayAvailable: installed,
|
|
114
|
+
startupAvailable: installed,
|
|
115
|
+
directInstallerAvailable,
|
|
116
|
+
downloadUrl,
|
|
117
|
+
releaseNotesUrl,
|
|
118
|
+
note: installed
|
|
119
|
+
? 'Desktop app installed. Tray or menu-bar controls and launch at sign-in are handled there.'
|
|
120
|
+
: installerOnlyRelease
|
|
121
|
+
? 'Install the desktop app to get tray or menu-bar controls and launch at sign-in. In-app updates are not published for this release yet.'
|
|
122
|
+
: directInstallerAvailable
|
|
123
|
+
? 'Install the desktop app to get tray or menu-bar controls, launch at sign-in, and in-app updates.'
|
|
124
|
+
: 'The desktop app installer is not published for this platform yet. Use the Connect downloads page for the current release.',
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function spawnDetached(command, args, cwd) {
|
|
128
|
+
const child = (0, node_child_process_1.spawn)(command, args, {
|
|
129
|
+
detached: true,
|
|
130
|
+
stdio: 'ignore',
|
|
131
|
+
windowsHide: true,
|
|
132
|
+
...(cwd ? { cwd } : {}),
|
|
133
|
+
});
|
|
134
|
+
child.unref();
|
|
135
|
+
}
|
|
136
|
+
function openInstalledShell(status) {
|
|
137
|
+
if (!status.installationPath) {
|
|
138
|
+
throw new Error('native_shell_missing');
|
|
139
|
+
}
|
|
140
|
+
if (status.platform === 'macos') {
|
|
141
|
+
spawnDetached('open', [status.installationPath]);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (status.platform === 'windows') {
|
|
145
|
+
spawnDetached(status.installationPath, [], node_path_1.default.dirname(status.installationPath));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
spawnDetached(status.installationPath, [], node_path_1.default.dirname(status.installationPath));
|
|
149
|
+
}
|
|
150
|
+
async function openNativeShell(updateCheck) {
|
|
151
|
+
const status = await getNativeShellStatus(updateCheck);
|
|
152
|
+
if (status.installed) {
|
|
153
|
+
openInstalledShell(status);
|
|
154
|
+
return {
|
|
155
|
+
ok: true,
|
|
156
|
+
opened: 'installed',
|
|
157
|
+
installationPath: status.installationPath,
|
|
158
|
+
downloadUrl: status.downloadUrl,
|
|
159
|
+
message: 'Opening the native Forkit Connect shell.',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
opened: 'download',
|
|
165
|
+
installationPath: null,
|
|
166
|
+
downloadUrl: status.downloadUrl,
|
|
167
|
+
message: 'Install the Forkit Connect desktop app to enable tray or menu-bar controls and launch at sign-in.',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=native-shell.js.map
|