vibeusage 0.2.20 → 0.2.22
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 +306 -173
- package/README.old.md +324 -0
- package/README.zh-CN.md +304 -188
- package/package.json +32 -30
- package/src/cli.js +41 -37
- package/src/commands/activate-if-needed.js +41 -0
- package/src/commands/diagnostics.js +8 -9
- package/src/commands/doctor.js +31 -26
- package/src/commands/init.js +324 -208
- package/src/commands/status.js +86 -80
- package/src/commands/sync.js +182 -130
- package/src/commands/uninstall.js +69 -58
- package/src/lib/activation-check.js +290 -0
- package/src/lib/browser-auth.js +52 -54
- package/src/lib/claude-config.js +25 -25
- package/src/lib/cli-ui.js +35 -35
- package/src/lib/codex-config.js +40 -36
- package/src/lib/debug-flags.js +2 -2
- package/src/lib/diagnostics.js +73 -55
- package/src/lib/doctor.js +139 -132
- package/src/lib/fs.js +17 -17
- package/src/lib/gemini-config.js +44 -40
- package/src/lib/init-flow.js +16 -22
- package/src/lib/insforge-client.js +10 -10
- package/src/lib/insforge.js +9 -3
- package/src/lib/openclaw-hook.js +91 -67
- package/src/lib/openclaw-session-plugin.js +520 -0
- package/src/lib/opencode-config.js +31 -32
- package/src/lib/opencode-usage-audit.js +34 -31
- package/src/lib/progress.js +12 -13
- package/src/lib/project-usage-purge.js +23 -17
- package/src/lib/prompt.js +8 -4
- package/src/lib/rollout.js +342 -241
- package/src/lib/runtime-config.js +34 -22
- package/src/lib/subscriptions.js +94 -92
- package/src/lib/tracker-paths.js +6 -6
- package/src/lib/upload-throttle.js +35 -16
- package/src/lib/uploader.js +33 -29
- package/src/lib/vibeusage-api.js +72 -56
- package/src/lib/vibeusage-public-repo.js +41 -24
package/src/commands/init.js
CHANGED
|
@@ -1,36 +1,54 @@
|
|
|
1
|
-
const os = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const fs = require(
|
|
4
|
-
const fssync = require(
|
|
5
|
-
const cp = require(
|
|
6
|
-
const crypto = require(
|
|
1
|
+
const os = require("node:os");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const fs = require("node:fs/promises");
|
|
4
|
+
const fssync = require("node:fs");
|
|
5
|
+
const cp = require("node:child_process");
|
|
6
|
+
const crypto = require("node:crypto");
|
|
7
7
|
|
|
8
|
-
const {
|
|
9
|
-
|
|
8
|
+
const {
|
|
9
|
+
ensureDir,
|
|
10
|
+
writeFileAtomic,
|
|
11
|
+
readJson,
|
|
12
|
+
writeJson,
|
|
13
|
+
chmod600IfPossible,
|
|
14
|
+
} = require("../lib/fs");
|
|
15
|
+
const { prompt, promptHidden } = require("../lib/prompt");
|
|
10
16
|
const {
|
|
11
17
|
upsertCodexNotify,
|
|
12
18
|
upsertEveryCodeNotify,
|
|
13
19
|
readCodexNotify,
|
|
14
|
-
readEveryCodeNotify
|
|
15
|
-
} = require(
|
|
16
|
-
const {
|
|
20
|
+
readEveryCodeNotify,
|
|
21
|
+
} = require("../lib/codex-config");
|
|
22
|
+
const {
|
|
23
|
+
upsertClaudeHook,
|
|
24
|
+
buildClaudeHookCommand,
|
|
25
|
+
isClaudeHookConfigured,
|
|
26
|
+
} = require("../lib/claude-config");
|
|
17
27
|
const {
|
|
18
28
|
resolveGeminiConfigDir,
|
|
19
29
|
resolveGeminiSettingsPath,
|
|
20
30
|
buildGeminiHookCommand,
|
|
21
31
|
upsertGeminiHook,
|
|
22
|
-
isGeminiHookConfigured
|
|
23
|
-
} = require(
|
|
24
|
-
const {
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
isGeminiHookConfigured,
|
|
33
|
+
} = require("../lib/gemini-config");
|
|
34
|
+
const {
|
|
35
|
+
resolveOpencodeConfigDir,
|
|
36
|
+
upsertOpencodePlugin,
|
|
37
|
+
isOpencodePluginInstalled,
|
|
38
|
+
} = require("../lib/opencode-config");
|
|
39
|
+
const { removeOpenclawHookConfig, probeOpenclawHookState } = require("../lib/openclaw-hook");
|
|
40
|
+
const {
|
|
41
|
+
installOpenclawSessionPlugin,
|
|
42
|
+
probeOpenclawSessionPluginState,
|
|
43
|
+
} = require("../lib/openclaw-session-plugin");
|
|
44
|
+
const { beginBrowserAuth, openInBrowser } = require("../lib/browser-auth");
|
|
27
45
|
const {
|
|
28
46
|
issueDeviceTokenWithPassword,
|
|
29
47
|
issueDeviceTokenWithAccessToken,
|
|
30
|
-
issueDeviceTokenWithLinkCode
|
|
31
|
-
} = require(
|
|
32
|
-
const { resolveTrackerPaths } = require(
|
|
33
|
-
const { resolveRuntimeConfig } = require(
|
|
48
|
+
issueDeviceTokenWithLinkCode,
|
|
49
|
+
} = require("../lib/insforge");
|
|
50
|
+
const { resolveTrackerPaths } = require("../lib/tracker-paths");
|
|
51
|
+
const { resolveRuntimeConfig } = require("../lib/runtime-config");
|
|
34
52
|
const {
|
|
35
53
|
BOLD,
|
|
36
54
|
DIM,
|
|
@@ -39,21 +57,21 @@ const {
|
|
|
39
57
|
color,
|
|
40
58
|
isInteractive,
|
|
41
59
|
promptMenu,
|
|
42
|
-
createSpinner
|
|
43
|
-
} = require(
|
|
44
|
-
const { renderLocalReport, renderAuthTransition, renderSuccessBox } = require(
|
|
60
|
+
createSpinner,
|
|
61
|
+
} = require("../lib/cli-ui");
|
|
62
|
+
const { renderLocalReport, renderAuthTransition, renderSuccessBox } = require("../lib/init-flow");
|
|
45
63
|
|
|
46
64
|
const ASCII_LOGO = [
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
].join(
|
|
65
|
+
"██╗ ██╗██╗██████╗ ███████╗██╗ ██╗███████╗ █████╗ ██████╗ ███████╗",
|
|
66
|
+
"██║ ██║██║██╔══██╗██╔════╝██║ ██║██╔════╝██╔══██╗██╔════╝ ██╔════╝",
|
|
67
|
+
"██║ ██║██║██████╔╝█████╗ ██║ ██║███████╗███████║██║ ███╗█████╗",
|
|
68
|
+
"╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██║╚════██║██╔══██║██║ ██║██╔══╝",
|
|
69
|
+
" ╚████╔╝ ██║██████╔╝███████╗╚██████╔╝███████║██║ ██║╚██████╔╝███████╗",
|
|
70
|
+
" ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝",
|
|
71
|
+
].join("\n");
|
|
54
72
|
|
|
55
|
-
const DIVIDER =
|
|
56
|
-
const DEFAULT_DASHBOARD_URL =
|
|
73
|
+
const DIVIDER = "----------------------------------------------";
|
|
74
|
+
const DEFAULT_DASHBOARD_URL = "https://www.vibeusage.cc";
|
|
57
75
|
|
|
58
76
|
async function cmdInit(argv) {
|
|
59
77
|
const opts = parseArgs(argv);
|
|
@@ -61,37 +79,39 @@ async function cmdInit(argv) {
|
|
|
61
79
|
|
|
62
80
|
const { rootDir, trackerDir, binDir } = await resolveTrackerPaths({ home });
|
|
63
81
|
|
|
64
|
-
const configPath = path.join(trackerDir,
|
|
65
|
-
const notifyOriginalPath = path.join(trackerDir,
|
|
66
|
-
const linkCodeStatePath = path.join(trackerDir,
|
|
82
|
+
const configPath = path.join(trackerDir, "config.json");
|
|
83
|
+
const notifyOriginalPath = path.join(trackerDir, "codex_notify_original.json");
|
|
84
|
+
const linkCodeStatePath = path.join(trackerDir, "link_code_state.json");
|
|
67
85
|
|
|
68
86
|
const existingConfig = await readJson(configPath);
|
|
69
87
|
const runtime = resolveRuntimeConfig({
|
|
70
88
|
cli: { baseUrl: opts.baseUrl, dashboardUrl: opts.dashboardUrl },
|
|
71
89
|
config: existingConfig || {},
|
|
72
|
-
env: process.env
|
|
90
|
+
env: process.env,
|
|
73
91
|
});
|
|
74
92
|
const baseUrl = runtime.baseUrl;
|
|
75
93
|
let dashboardUrl = runtime.dashboardUrl || DEFAULT_DASHBOARD_URL;
|
|
76
|
-
const notifyPath = path.join(binDir,
|
|
77
|
-
const appDir = path.join(trackerDir,
|
|
78
|
-
const trackerBinPath = path.join(appDir,
|
|
94
|
+
const notifyPath = path.join(binDir, "notify.cjs");
|
|
95
|
+
const appDir = path.join(trackerDir, "app");
|
|
96
|
+
const trackerBinPath = path.join(appDir, "bin", "tracker.js");
|
|
79
97
|
|
|
80
98
|
renderWelcome();
|
|
81
99
|
|
|
82
100
|
if (opts.dryRun) {
|
|
83
|
-
process.stdout.write(`${color(
|
|
101
|
+
process.stdout.write(`${color("Dry run: preview only (no changes applied).", DIM)}\n\n`);
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
if (isInteractive() && !opts.yes && !opts.dryRun) {
|
|
87
105
|
const choice = await promptMenu({
|
|
88
|
-
message:
|
|
89
|
-
options: [
|
|
90
|
-
defaultIndex: 0
|
|
106
|
+
message: "? Proceed with installation?",
|
|
107
|
+
options: ["Yes, configure my environment", "No, exit"],
|
|
108
|
+
defaultIndex: 0,
|
|
91
109
|
});
|
|
92
|
-
const normalizedChoice = String(choice ||
|
|
93
|
-
|
|
94
|
-
|
|
110
|
+
const normalizedChoice = String(choice || "")
|
|
111
|
+
.trim()
|
|
112
|
+
.toLowerCase();
|
|
113
|
+
if (normalizedChoice.startsWith("no") || normalizedChoice.includes("exit")) {
|
|
114
|
+
process.stdout.write("Setup cancelled.\n");
|
|
95
115
|
return;
|
|
96
116
|
}
|
|
97
117
|
}
|
|
@@ -102,18 +122,18 @@ async function cmdInit(argv) {
|
|
|
102
122
|
home,
|
|
103
123
|
trackerDir,
|
|
104
124
|
notifyPath,
|
|
105
|
-
runtime
|
|
125
|
+
runtime,
|
|
106
126
|
});
|
|
107
127
|
renderLocalReport({ summary: preview.summary, isDryRun: true });
|
|
108
128
|
if (preview.pendingBrowserAuth) {
|
|
109
|
-
process.stdout.write(
|
|
129
|
+
process.stdout.write("Account linking would be required for full setup.\n");
|
|
110
130
|
} else if (!preview.deviceToken) {
|
|
111
|
-
renderAccountNotLinked({ context:
|
|
131
|
+
renderAccountNotLinked({ context: "dry-run" });
|
|
112
132
|
}
|
|
113
133
|
return;
|
|
114
134
|
}
|
|
115
135
|
|
|
116
|
-
const spinner = createSpinner({ text:
|
|
136
|
+
const spinner = createSpinner({ text: "Analyzing and configuring local environment..." });
|
|
117
137
|
spinner.start();
|
|
118
138
|
let setup;
|
|
119
139
|
try {
|
|
@@ -130,7 +150,7 @@ async function cmdInit(argv) {
|
|
|
130
150
|
appDir,
|
|
131
151
|
trackerBinPath,
|
|
132
152
|
runtime,
|
|
133
|
-
existingConfig
|
|
153
|
+
existingConfig,
|
|
134
154
|
});
|
|
135
155
|
} catch (err) {
|
|
136
156
|
spinner.stop();
|
|
@@ -145,7 +165,12 @@ async function cmdInit(argv) {
|
|
|
145
165
|
|
|
146
166
|
if (setup.pendingBrowserAuth) {
|
|
147
167
|
const deviceName = opts.deviceName || os.hostname();
|
|
148
|
-
const flow = await beginBrowserAuth({
|
|
168
|
+
const flow = await beginBrowserAuth({
|
|
169
|
+
baseUrl,
|
|
170
|
+
dashboardUrl,
|
|
171
|
+
timeoutMs: 10 * 60_000,
|
|
172
|
+
open: false,
|
|
173
|
+
});
|
|
149
174
|
const canAutoOpen = !opts.noOpen;
|
|
150
175
|
renderAuthTransition({ authUrl: flow.authUrl, canAutoOpen });
|
|
151
176
|
if (canAutoOpen) {
|
|
@@ -153,7 +178,11 @@ async function cmdInit(argv) {
|
|
|
153
178
|
openInBrowser(flow.authUrl);
|
|
154
179
|
}
|
|
155
180
|
const callback = await flow.waitForCallback();
|
|
156
|
-
const issued = await issueDeviceTokenWithAccessToken({
|
|
181
|
+
const issued = await issueDeviceTokenWithAccessToken({
|
|
182
|
+
baseUrl,
|
|
183
|
+
accessToken: callback.accessToken,
|
|
184
|
+
deviceName,
|
|
185
|
+
});
|
|
157
186
|
deviceToken = issued.token;
|
|
158
187
|
deviceId = issued.deviceId;
|
|
159
188
|
await writeJson(configPath, { baseUrl, deviceToken, deviceId, installedAt: setup.installedAt });
|
|
@@ -168,9 +197,9 @@ async function cmdInit(argv) {
|
|
|
168
197
|
}
|
|
169
198
|
|
|
170
199
|
try {
|
|
171
|
-
spawnInitSync({ trackerBinPath, packageName:
|
|
200
|
+
spawnInitSync({ trackerBinPath, packageName: "vibeusage" });
|
|
172
201
|
} catch (err) {
|
|
173
|
-
const msg = err && err.message ? err.message :
|
|
202
|
+
const msg = err && err.message ? err.message : "unknown error";
|
|
174
203
|
process.stderr.write(`Initial sync spawn failed: ${msg}\n`);
|
|
175
204
|
}
|
|
176
205
|
}
|
|
@@ -179,29 +208,38 @@ function renderWelcome() {
|
|
|
179
208
|
process.stdout.write(
|
|
180
209
|
[
|
|
181
210
|
ASCII_LOGO,
|
|
182
|
-
|
|
211
|
+
"",
|
|
183
212
|
`${BOLD}Welcome to VibeScore CLI${RESET}`,
|
|
184
213
|
DIVIDER,
|
|
185
214
|
`${CYAN}Privacy First: Your content stays local. We only upload token counts and minimal metadata, never prompts or responses.${RESET}`,
|
|
186
215
|
DIVIDER,
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
].join(
|
|
216
|
+
"",
|
|
217
|
+
"This tool will:",
|
|
218
|
+
" - Analyze your local AI CLI configurations (Codex, Every Code, Claude, Gemini, Opencode, OpenClaw)",
|
|
219
|
+
" - Set up lightweight hooks to track your flow state",
|
|
220
|
+
" - Link your device to your VibeScore account",
|
|
221
|
+
"",
|
|
222
|
+
"(Nothing will be changed until you confirm below)",
|
|
223
|
+
"",
|
|
224
|
+
].join("\n"),
|
|
196
225
|
);
|
|
197
226
|
}
|
|
198
227
|
|
|
199
228
|
function renderAccountNotLinked({ context } = {}) {
|
|
200
|
-
if (context ===
|
|
201
|
-
process.stdout.write(
|
|
229
|
+
if (context === "dry-run") {
|
|
230
|
+
process.stdout.write(
|
|
231
|
+
[
|
|
232
|
+
"",
|
|
233
|
+
"Account not linked (dry run).",
|
|
234
|
+
"Run init without --dry-run to link your account.",
|
|
235
|
+
"",
|
|
236
|
+
].join("\n"),
|
|
237
|
+
);
|
|
202
238
|
return;
|
|
203
239
|
}
|
|
204
|
-
process.stdout.write(
|
|
240
|
+
process.stdout.write(
|
|
241
|
+
["", "Account not linked.", "Set VIBEUSAGE_DEVICE_TOKEN then re-run init.", ""].join("\n"),
|
|
242
|
+
);
|
|
205
243
|
}
|
|
206
244
|
|
|
207
245
|
function shouldUseBrowserAuth({ deviceToken, opts }) {
|
|
@@ -233,7 +271,7 @@ async function runSetup({
|
|
|
233
271
|
appDir,
|
|
234
272
|
trackerBinPath,
|
|
235
273
|
runtime,
|
|
236
|
-
existingConfig
|
|
274
|
+
existingConfig,
|
|
237
275
|
}) {
|
|
238
276
|
await ensureDir(trackerDir);
|
|
239
277
|
await ensureDir(binDir);
|
|
@@ -248,7 +286,7 @@ async function runSetup({
|
|
|
248
286
|
const deviceName = opts.deviceName || os.hostname();
|
|
249
287
|
const platform = normalizePlatform(process.platform);
|
|
250
288
|
const linkCode = String(opts.linkCode);
|
|
251
|
-
const linkCodeHash = crypto.createHash(
|
|
289
|
+
const linkCodeHash = crypto.createHash("sha256").update(linkCode).digest("hex");
|
|
252
290
|
const existingLinkState = await readJson(linkCodeStatePath);
|
|
253
291
|
let requestId =
|
|
254
292
|
existingLinkState?.linkCodeHash === linkCodeHash && existingLinkState?.requestId
|
|
@@ -259,7 +297,7 @@ async function runSetup({
|
|
|
259
297
|
await writeJson(linkCodeStatePath, {
|
|
260
298
|
linkCodeHash,
|
|
261
299
|
requestId,
|
|
262
|
-
createdAt: new Date().toISOString()
|
|
300
|
+
createdAt: new Date().toISOString(),
|
|
263
301
|
});
|
|
264
302
|
await chmod600IfPossible(linkCodeStatePath);
|
|
265
303
|
}
|
|
@@ -268,7 +306,7 @@ async function runSetup({
|
|
|
268
306
|
linkCode,
|
|
269
307
|
requestId,
|
|
270
308
|
deviceName,
|
|
271
|
-
platform
|
|
309
|
+
platform,
|
|
272
310
|
});
|
|
273
311
|
deviceToken = issued.token;
|
|
274
312
|
deviceId = issued.deviceId;
|
|
@@ -277,8 +315,8 @@ async function runSetup({
|
|
|
277
315
|
const deviceName = opts.deviceName || os.hostname();
|
|
278
316
|
|
|
279
317
|
if (opts.email || opts.password) {
|
|
280
|
-
const email = opts.email || (await prompt(
|
|
281
|
-
const password = opts.password || (await promptHidden(
|
|
318
|
+
const email = opts.email || (await prompt("Email: "));
|
|
319
|
+
const password = opts.password || (await promptHidden("Password: "));
|
|
282
320
|
const issued = await issueDeviceTokenWithPassword({ baseUrl, email, password, deviceName });
|
|
283
321
|
deviceToken = issued.token;
|
|
284
322
|
deviceId = issued.deviceId;
|
|
@@ -291,7 +329,7 @@ async function runSetup({
|
|
|
291
329
|
baseUrl,
|
|
292
330
|
deviceToken,
|
|
293
331
|
deviceId,
|
|
294
|
-
installedAt
|
|
332
|
+
installedAt,
|
|
295
333
|
};
|
|
296
334
|
|
|
297
335
|
await writeJson(configPath, config);
|
|
@@ -299,7 +337,7 @@ async function runSetup({
|
|
|
299
337
|
|
|
300
338
|
await writeFileAtomic(
|
|
301
339
|
notifyPath,
|
|
302
|
-
buildNotifyHandler({ trackerDir, trackerBinPath, packageName:
|
|
340
|
+
buildNotifyHandler({ trackerDir, trackerBinPath, packageName: "vibeusage" }),
|
|
303
341
|
);
|
|
304
342
|
await fs.chmod(notifyPath, 0o755).catch(() => {});
|
|
305
343
|
|
|
@@ -307,7 +345,7 @@ async function runSetup({
|
|
|
307
345
|
home,
|
|
308
346
|
trackerDir,
|
|
309
347
|
notifyPath,
|
|
310
|
-
notifyOriginalPath
|
|
348
|
+
notifyOriginalPath,
|
|
311
349
|
});
|
|
312
350
|
|
|
313
351
|
return {
|
|
@@ -315,21 +353,21 @@ async function runSetup({
|
|
|
315
353
|
pendingBrowserAuth,
|
|
316
354
|
deviceToken,
|
|
317
355
|
deviceId,
|
|
318
|
-
installedAt
|
|
356
|
+
installedAt,
|
|
319
357
|
};
|
|
320
358
|
}
|
|
321
359
|
|
|
322
360
|
function buildIntegrationTargets({ home, trackerDir, notifyPath }) {
|
|
323
|
-
const codexHome = process.env.CODEX_HOME || path.join(home,
|
|
324
|
-
const codexConfigPath = path.join(codexHome,
|
|
325
|
-
const codeHome = process.env.CODE_HOME || path.join(home,
|
|
326
|
-
const codeConfigPath = path.join(codeHome,
|
|
327
|
-
const notifyOriginalPath = path.join(trackerDir,
|
|
328
|
-
const codeNotifyOriginalPath = path.join(trackerDir,
|
|
329
|
-
const notifyCmd = [
|
|
330
|
-
const codeNotifyCmd = [
|
|
331
|
-
const claudeDir = path.join(home,
|
|
332
|
-
const claudeSettingsPath = path.join(claudeDir,
|
|
361
|
+
const codexHome = process.env.CODEX_HOME || path.join(home, ".codex");
|
|
362
|
+
const codexConfigPath = path.join(codexHome, "config.toml");
|
|
363
|
+
const codeHome = process.env.CODE_HOME || path.join(home, ".code");
|
|
364
|
+
const codeConfigPath = path.join(codeHome, "config.toml");
|
|
365
|
+
const notifyOriginalPath = path.join(trackerDir, "codex_notify_original.json");
|
|
366
|
+
const codeNotifyOriginalPath = path.join(trackerDir, "code_notify_original.json");
|
|
367
|
+
const notifyCmd = ["/usr/bin/env", "node", notifyPath];
|
|
368
|
+
const codeNotifyCmd = ["/usr/bin/env", "node", notifyPath, "--source=every-code"];
|
|
369
|
+
const claudeDir = path.join(home, ".claude");
|
|
370
|
+
const claudeSettingsPath = path.join(claudeDir, "settings.json");
|
|
333
371
|
const claudeHookCommand = buildClaudeHookCommand(notifyPath);
|
|
334
372
|
const geminiConfigDir = resolveGeminiConfigDir({ home, env: process.env });
|
|
335
373
|
const geminiSettingsPath = resolveGeminiSettingsPath({ configDir: geminiConfigDir });
|
|
@@ -350,7 +388,7 @@ function buildIntegrationTargets({ home, trackerDir, notifyPath }) {
|
|
|
350
388
|
geminiConfigDir,
|
|
351
389
|
geminiSettingsPath,
|
|
352
390
|
geminiHookCommand,
|
|
353
|
-
opencodeConfigDir
|
|
391
|
+
opencodeConfigDir,
|
|
354
392
|
};
|
|
355
393
|
}
|
|
356
394
|
|
|
@@ -365,75 +403,108 @@ async function applyIntegrationSetup({ home, trackerDir, notifyPath, notifyOrigi
|
|
|
365
403
|
const result = await upsertCodexNotify({
|
|
366
404
|
codexConfigPath: context.codexConfigPath,
|
|
367
405
|
notifyCmd: context.notifyCmd,
|
|
368
|
-
notifyOriginalPath: context.notifyOriginalPath
|
|
406
|
+
notifyOriginalPath: context.notifyOriginalPath,
|
|
369
407
|
});
|
|
370
408
|
summary.push({
|
|
371
|
-
label:
|
|
372
|
-
status: result.changed ?
|
|
373
|
-
detail: result.changed ?
|
|
409
|
+
label: "Codex CLI",
|
|
410
|
+
status: result.changed ? "updated" : "set",
|
|
411
|
+
detail: result.changed ? "Updated config" : "Config already set",
|
|
374
412
|
});
|
|
375
413
|
} else {
|
|
376
|
-
summary.push({ label:
|
|
414
|
+
summary.push({ label: "Codex CLI", status: "skipped", detail: renderSkipDetail(codexProbe) });
|
|
377
415
|
}
|
|
378
416
|
|
|
379
417
|
const claudeDirExists = await isDir(context.claudeDir);
|
|
380
418
|
if (claudeDirExists) {
|
|
381
419
|
await upsertClaudeHook({
|
|
382
420
|
settingsPath: context.claudeSettingsPath,
|
|
383
|
-
hookCommand: context.claudeHookCommand
|
|
421
|
+
hookCommand: context.claudeHookCommand,
|
|
384
422
|
});
|
|
385
|
-
summary.push({ label:
|
|
423
|
+
summary.push({ label: "Claude", status: "installed", detail: "Hooks installed" });
|
|
386
424
|
} else {
|
|
387
|
-
summary.push({ label:
|
|
425
|
+
summary.push({ label: "Claude", status: "skipped", detail: "Config not found" });
|
|
388
426
|
}
|
|
389
427
|
|
|
390
428
|
const geminiConfigExists = await isDir(context.geminiConfigDir);
|
|
391
429
|
if (geminiConfigExists) {
|
|
392
430
|
await upsertGeminiHook({
|
|
393
431
|
settingsPath: context.geminiSettingsPath,
|
|
394
|
-
hookCommand: context.geminiHookCommand
|
|
432
|
+
hookCommand: context.geminiHookCommand,
|
|
395
433
|
});
|
|
396
|
-
summary.push({ label:
|
|
434
|
+
summary.push({ label: "Gemini", status: "installed", detail: "Hooks installed" });
|
|
397
435
|
} else {
|
|
398
|
-
summary.push({ label:
|
|
436
|
+
summary.push({ label: "Gemini", status: "skipped", detail: "Config not found" });
|
|
399
437
|
}
|
|
400
438
|
|
|
401
439
|
const opencodeResult = await upsertOpencodePlugin({
|
|
402
440
|
configDir: context.opencodeConfigDir,
|
|
403
|
-
notifyPath
|
|
441
|
+
notifyPath,
|
|
404
442
|
});
|
|
405
|
-
if (opencodeResult?.skippedReason ===
|
|
406
|
-
summary.push({ label:
|
|
443
|
+
if (opencodeResult?.skippedReason === "config-missing") {
|
|
444
|
+
summary.push({ label: "Opencode Plugin", status: "skipped", detail: "Config not found" });
|
|
407
445
|
} else {
|
|
408
|
-
summary.push({
|
|
446
|
+
summary.push({
|
|
447
|
+
label: "Opencode Plugin",
|
|
448
|
+
status: opencodeResult?.changed ? "installed" : "set",
|
|
449
|
+
detail: "Plugin installed",
|
|
450
|
+
});
|
|
409
451
|
}
|
|
410
452
|
|
|
411
|
-
const openclawBefore = await
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
453
|
+
const openclawBefore = await probeOpenclawSessionPluginState({
|
|
454
|
+
home,
|
|
455
|
+
trackerDir,
|
|
456
|
+
env: process.env,
|
|
457
|
+
});
|
|
458
|
+
const openclawInstall = await installOpenclawSessionPlugin({
|
|
459
|
+
home,
|
|
460
|
+
trackerDir,
|
|
461
|
+
packageName: "vibeusage",
|
|
462
|
+
env: process.env,
|
|
463
|
+
});
|
|
464
|
+
if (openclawInstall?.skippedReason === "openclaw-cli-missing") {
|
|
416
465
|
summary.push({
|
|
417
|
-
label:
|
|
418
|
-
status:
|
|
419
|
-
detail:
|
|
466
|
+
label: "OpenClaw Session Plugin",
|
|
467
|
+
status: "skipped",
|
|
468
|
+
detail: "OpenClaw CLI not found",
|
|
420
469
|
});
|
|
421
|
-
} else if (openclawInstall?.skippedReason ===
|
|
470
|
+
} else if (openclawInstall?.skippedReason === "openclaw-plugins-install-failed") {
|
|
422
471
|
summary.push({
|
|
423
|
-
label:
|
|
424
|
-
status:
|
|
425
|
-
detail: openclawInstall.error ?
|
|
472
|
+
label: "OpenClaw Session Plugin",
|
|
473
|
+
status: "skipped",
|
|
474
|
+
detail: `Install failed${openclawInstall.error ? `: ${openclawInstall.error}` : ""}`,
|
|
475
|
+
});
|
|
476
|
+
} else if (openclawInstall?.skippedReason === "openclaw-config-unreadable") {
|
|
477
|
+
summary.push({
|
|
478
|
+
label: "OpenClaw Session Plugin",
|
|
479
|
+
status: "skipped",
|
|
480
|
+
detail: openclawInstall.error
|
|
481
|
+
? `OpenClaw config unreadable: ${openclawInstall.error}`
|
|
482
|
+
: "OpenClaw config unreadable",
|
|
426
483
|
});
|
|
427
484
|
} else if (openclawInstall?.configured) {
|
|
428
485
|
summary.push({
|
|
429
|
-
label:
|
|
430
|
-
status: openclawBefore?.configured ?
|
|
486
|
+
label: "OpenClaw Session Plugin",
|
|
487
|
+
status: openclawBefore?.configured ? "set" : "installed",
|
|
431
488
|
detail: openclawBefore?.configured
|
|
432
|
-
?
|
|
433
|
-
:
|
|
489
|
+
? "Session plugin already linked"
|
|
490
|
+
: "Session plugin linked (restart OpenClaw gateway to activate)",
|
|
434
491
|
});
|
|
435
492
|
} else {
|
|
436
|
-
summary.push({
|
|
493
|
+
summary.push({
|
|
494
|
+
label: "OpenClaw Session Plugin",
|
|
495
|
+
status: "skipped",
|
|
496
|
+
detail: "OpenClaw session plugin unavailable",
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const legacyHookState = await probeOpenclawHookState({ home, trackerDir, env: process.env });
|
|
501
|
+
if (legacyHookState?.configured || legacyHookState?.linked || legacyHookState?.enabled) {
|
|
502
|
+
await removeOpenclawHookConfig({ home, trackerDir, env: process.env });
|
|
503
|
+
summary.push({
|
|
504
|
+
label: "OpenClaw Hook (legacy)",
|
|
505
|
+
status: "updated",
|
|
506
|
+
detail: "Removed legacy command hook (migrated to session plugin)",
|
|
507
|
+
});
|
|
437
508
|
}
|
|
438
509
|
|
|
439
510
|
const codeProbe = await probeFile(context.codeConfigPath);
|
|
@@ -441,15 +512,15 @@ async function applyIntegrationSetup({ home, trackerDir, notifyPath, notifyOrigi
|
|
|
441
512
|
const result = await upsertEveryCodeNotify({
|
|
442
513
|
codeConfigPath: context.codeConfigPath,
|
|
443
514
|
notifyCmd: context.codeNotifyCmd,
|
|
444
|
-
notifyOriginalPath: context.codeNotifyOriginalPath
|
|
515
|
+
notifyOriginalPath: context.codeNotifyOriginalPath,
|
|
445
516
|
});
|
|
446
517
|
summary.push({
|
|
447
|
-
label:
|
|
448
|
-
status: result.changed ?
|
|
449
|
-
detail: result.changed ?
|
|
518
|
+
label: "Every Code",
|
|
519
|
+
status: result.changed ? "updated" : "set",
|
|
520
|
+
detail: result.changed ? "Updated config" : "Config already set",
|
|
450
521
|
});
|
|
451
522
|
} else {
|
|
452
|
-
summary.push({ label:
|
|
523
|
+
summary.push({ label: "Every Code", status: "skipped", detail: renderSkipDetail(codeProbe) });
|
|
453
524
|
}
|
|
454
525
|
|
|
455
526
|
return summary;
|
|
@@ -464,73 +535,96 @@ async function previewIntegrations({ context }) {
|
|
|
464
535
|
const existing = await readCodexNotify(context.codexConfigPath);
|
|
465
536
|
const matches = arraysEqual(existing, context.notifyCmd);
|
|
466
537
|
summary.push({
|
|
467
|
-
label:
|
|
468
|
-
status: matches ?
|
|
469
|
-
detail: matches ?
|
|
538
|
+
label: "Codex CLI",
|
|
539
|
+
status: matches ? "set" : "updated",
|
|
540
|
+
detail: matches ? "Already configured" : "Will update config",
|
|
470
541
|
});
|
|
471
542
|
} else {
|
|
472
|
-
summary.push({ label:
|
|
543
|
+
summary.push({ label: "Codex CLI", status: "skipped", detail: renderSkipDetail(codexProbe) });
|
|
473
544
|
}
|
|
474
545
|
|
|
475
546
|
const claudeDirExists = await isDir(context.claudeDir);
|
|
476
547
|
if (claudeDirExists) {
|
|
477
548
|
const configured = await isClaudeHookConfigured({
|
|
478
549
|
settingsPath: context.claudeSettingsPath,
|
|
479
|
-
hookCommand: context.claudeHookCommand
|
|
550
|
+
hookCommand: context.claudeHookCommand,
|
|
480
551
|
});
|
|
481
552
|
summary.push({
|
|
482
|
-
label:
|
|
483
|
-
status:
|
|
484
|
-
detail: configured ?
|
|
553
|
+
label: "Claude",
|
|
554
|
+
status: "installed",
|
|
555
|
+
detail: configured ? "Hooks already installed" : "Will install hooks",
|
|
485
556
|
});
|
|
486
557
|
} else {
|
|
487
|
-
summary.push({ label:
|
|
558
|
+
summary.push({ label: "Claude", status: "skipped", detail: "Config not found" });
|
|
488
559
|
}
|
|
489
560
|
|
|
490
561
|
const geminiConfigExists = await isDir(context.geminiConfigDir);
|
|
491
562
|
if (geminiConfigExists) {
|
|
492
563
|
const configured = await isGeminiHookConfigured({
|
|
493
564
|
settingsPath: context.geminiSettingsPath,
|
|
494
|
-
hookCommand: context.geminiHookCommand
|
|
565
|
+
hookCommand: context.geminiHookCommand,
|
|
495
566
|
});
|
|
496
567
|
summary.push({
|
|
497
|
-
label:
|
|
498
|
-
status:
|
|
499
|
-
detail: configured ?
|
|
568
|
+
label: "Gemini",
|
|
569
|
+
status: "installed",
|
|
570
|
+
detail: configured ? "Hooks already installed" : "Will install hooks",
|
|
500
571
|
});
|
|
501
572
|
} else {
|
|
502
|
-
summary.push({ label:
|
|
573
|
+
summary.push({ label: "Gemini", status: "skipped", detail: "Config not found" });
|
|
503
574
|
}
|
|
504
575
|
|
|
505
576
|
const opencodeDirExists = await isDir(context.opencodeConfigDir);
|
|
506
577
|
const installed = await isOpencodePluginInstalled({ configDir: context.opencodeConfigDir });
|
|
507
578
|
const opencodeDetail = installed
|
|
508
|
-
?
|
|
579
|
+
? "Plugin already installed"
|
|
509
580
|
: opencodeDirExists
|
|
510
|
-
?
|
|
511
|
-
:
|
|
581
|
+
? "Will install plugin"
|
|
582
|
+
: "Will create config and install plugin";
|
|
512
583
|
summary.push({
|
|
513
|
-
label:
|
|
514
|
-
status:
|
|
515
|
-
detail: opencodeDetail
|
|
584
|
+
label: "Opencode Plugin",
|
|
585
|
+
status: "installed",
|
|
586
|
+
detail: opencodeDetail,
|
|
516
587
|
});
|
|
517
588
|
|
|
518
|
-
const openclawState = await
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
589
|
+
const openclawState = await probeOpenclawSessionPluginState({
|
|
590
|
+
home,
|
|
591
|
+
trackerDir: context.trackerDir,
|
|
592
|
+
env: process.env,
|
|
593
|
+
});
|
|
594
|
+
if (openclawState?.skippedReason === "openclaw-config-missing") {
|
|
522
595
|
summary.push({
|
|
523
|
-
label:
|
|
524
|
-
status:
|
|
525
|
-
detail:
|
|
596
|
+
label: "OpenClaw Session Plugin",
|
|
597
|
+
status: "skipped",
|
|
598
|
+
detail: "OpenClaw config not found",
|
|
599
|
+
});
|
|
600
|
+
} else if (openclawState?.skippedReason === "openclaw-config-unreadable") {
|
|
601
|
+
summary.push({
|
|
602
|
+
label: "OpenClaw Session Plugin",
|
|
603
|
+
status: "skipped",
|
|
604
|
+
detail: openclawState.error
|
|
605
|
+
? `OpenClaw config unreadable: ${openclawState.error}`
|
|
606
|
+
: "OpenClaw config unreadable",
|
|
526
607
|
});
|
|
527
608
|
} else {
|
|
528
609
|
summary.push({
|
|
529
|
-
label:
|
|
530
|
-
status: openclawState?.configured ?
|
|
610
|
+
label: "OpenClaw Session Plugin",
|
|
611
|
+
status: openclawState?.configured ? "set" : "installed",
|
|
531
612
|
detail: openclawState?.configured
|
|
532
|
-
?
|
|
533
|
-
:
|
|
613
|
+
? "Session plugin already linked"
|
|
614
|
+
: "Will link session plugin (restart OpenClaw gateway to activate)",
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const legacyHookState = await probeOpenclawHookState({
|
|
619
|
+
home,
|
|
620
|
+
trackerDir: context.trackerDir,
|
|
621
|
+
env: process.env,
|
|
622
|
+
});
|
|
623
|
+
if (legacyHookState?.configured || legacyHookState?.linked || legacyHookState?.enabled) {
|
|
624
|
+
summary.push({
|
|
625
|
+
label: "OpenClaw Hook (legacy)",
|
|
626
|
+
status: "updated",
|
|
627
|
+
detail: "Will remove legacy command hook during migration",
|
|
534
628
|
});
|
|
535
629
|
}
|
|
536
630
|
|
|
@@ -539,22 +633,22 @@ async function previewIntegrations({ context }) {
|
|
|
539
633
|
const existing = await readEveryCodeNotify(context.codeConfigPath);
|
|
540
634
|
const matches = arraysEqual(existing, context.codeNotifyCmd);
|
|
541
635
|
summary.push({
|
|
542
|
-
label:
|
|
543
|
-
status: matches ?
|
|
544
|
-
detail: matches ?
|
|
636
|
+
label: "Every Code",
|
|
637
|
+
status: matches ? "set" : "updated",
|
|
638
|
+
detail: matches ? "Already configured" : "Will update config",
|
|
545
639
|
});
|
|
546
640
|
} else {
|
|
547
|
-
summary.push({ label:
|
|
641
|
+
summary.push({ label: "Every Code", status: "skipped", detail: renderSkipDetail(codeProbe) });
|
|
548
642
|
}
|
|
549
643
|
|
|
550
644
|
return summary;
|
|
551
645
|
}
|
|
552
646
|
|
|
553
647
|
function renderSkipDetail(probe) {
|
|
554
|
-
if (!probe || probe.reason ===
|
|
555
|
-
if (probe.reason ===
|
|
556
|
-
if (probe.reason ===
|
|
557
|
-
return
|
|
648
|
+
if (!probe || probe.reason === "missing") return "Config not found";
|
|
649
|
+
if (probe.reason === "permission-denied") return "Permission denied";
|
|
650
|
+
if (probe.reason === "not-file") return "Invalid config";
|
|
651
|
+
return "Unavailable";
|
|
558
652
|
}
|
|
559
653
|
|
|
560
654
|
function arraysEqual(a, b) {
|
|
@@ -575,21 +669,21 @@ function parseArgs(argv) {
|
|
|
575
669
|
noAuth: false,
|
|
576
670
|
noOpen: false,
|
|
577
671
|
yes: false,
|
|
578
|
-
dryRun: false
|
|
672
|
+
dryRun: false,
|
|
579
673
|
};
|
|
580
674
|
|
|
581
675
|
for (let i = 0; i < argv.length; i++) {
|
|
582
676
|
const a = argv[i];
|
|
583
|
-
if (a ===
|
|
584
|
-
else if (a ===
|
|
585
|
-
else if (a ===
|
|
586
|
-
else if (a ===
|
|
587
|
-
else if (a ===
|
|
588
|
-
else if (a ===
|
|
589
|
-
else if (a ===
|
|
590
|
-
else if (a ===
|
|
591
|
-
else if (a ===
|
|
592
|
-
else if (a ===
|
|
677
|
+
if (a === "--base-url") out.baseUrl = argv[++i] || null;
|
|
678
|
+
else if (a === "--dashboard-url") out.dashboardUrl = argv[++i] || null;
|
|
679
|
+
else if (a === "--email") out.email = argv[++i] || null;
|
|
680
|
+
else if (a === "--password") out.password = argv[++i] || null;
|
|
681
|
+
else if (a === "--device-name") out.deviceName = argv[++i] || null;
|
|
682
|
+
else if (a === "--link-code") out.linkCode = argv[++i] || null;
|
|
683
|
+
else if (a === "--no-auth") out.noAuth = true;
|
|
684
|
+
else if (a === "--no-open") out.noOpen = true;
|
|
685
|
+
else if (a === "--yes") out.yes = true;
|
|
686
|
+
else if (a === "--dry-run") out.dryRun = true;
|
|
593
687
|
else throw new Error(`Unknown option: ${a}`);
|
|
594
688
|
}
|
|
595
689
|
return out;
|
|
@@ -601,19 +695,19 @@ function sleep(ms) {
|
|
|
601
695
|
}
|
|
602
696
|
|
|
603
697
|
function normalizePlatform(value) {
|
|
604
|
-
if (value ===
|
|
605
|
-
if (value ===
|
|
606
|
-
if (value ===
|
|
607
|
-
return
|
|
698
|
+
if (value === "darwin") return "macos";
|
|
699
|
+
if (value === "win32") return "windows";
|
|
700
|
+
if (value === "linux") return "linux";
|
|
701
|
+
return "unknown";
|
|
608
702
|
}
|
|
609
703
|
|
|
610
704
|
function buildNotifyHandler({ trackerDir, packageName }) {
|
|
611
705
|
// Keep this file dependency-free: Node built-ins only.
|
|
612
706
|
// It must never block Codex; it spawns sync in the background and exits 0.
|
|
613
|
-
const queueSignalPath = path.join(trackerDir,
|
|
614
|
-
const originalPath = path.join(trackerDir,
|
|
615
|
-
const fallbackPkg = packageName ||
|
|
616
|
-
const trackerBinPath = path.join(trackerDir,
|
|
707
|
+
const queueSignalPath = path.join(trackerDir, "notify.signal");
|
|
708
|
+
const originalPath = path.join(trackerDir, "codex_notify_original.json");
|
|
709
|
+
const fallbackPkg = packageName || "vibeusage";
|
|
710
|
+
const trackerBinPath = path.join(trackerDir, "app", "bin", "tracker.js");
|
|
617
711
|
|
|
618
712
|
return `#!/usr/bin/env node
|
|
619
713
|
'use strict';
|
|
@@ -643,7 +737,7 @@ for (let i = 0; i < rawArgs.length; i++) {
|
|
|
643
737
|
const trackerDir = ${JSON.stringify(trackerDir)};
|
|
644
738
|
const signalPath = ${JSON.stringify(queueSignalPath)};
|
|
645
739
|
const codexOriginalPath = ${JSON.stringify(originalPath)};
|
|
646
|
-
const codeOriginalPath = ${JSON.stringify(path.join(trackerDir,
|
|
740
|
+
const codeOriginalPath = ${JSON.stringify(path.join(trackerDir, "code_notify_original.json"))};
|
|
647
741
|
const trackerBinPath = ${JSON.stringify(trackerBinPath)};
|
|
648
742
|
const depsMarkerPath = path.join(trackerDir, 'app', 'node_modules', '@insforge', 'sdk', 'package.json');
|
|
649
743
|
const configPath = path.join(trackerDir, 'config.json');
|
|
@@ -763,11 +857,12 @@ async function probeFile(p) {
|
|
|
763
857
|
try {
|
|
764
858
|
const st = await fs.stat(p);
|
|
765
859
|
if (st.isFile()) return { exists: true, reason: null };
|
|
766
|
-
return { exists: false, reason:
|
|
860
|
+
return { exists: false, reason: "not-file" };
|
|
767
861
|
} catch (e) {
|
|
768
|
-
if (e?.code ===
|
|
769
|
-
if (e?.code ===
|
|
770
|
-
|
|
862
|
+
if (e?.code === "ENOENT" || e?.code === "ENOTDIR") return { exists: false, reason: "missing" };
|
|
863
|
+
if (e?.code === "EACCES" || e?.code === "EPERM")
|
|
864
|
+
return { exists: false, reason: "permission-denied" };
|
|
865
|
+
return { exists: false, reason: "error", code: e?.code || "unknown" };
|
|
771
866
|
}
|
|
772
867
|
}
|
|
773
868
|
|
|
@@ -782,15 +877,21 @@ async function isDir(p) {
|
|
|
782
877
|
|
|
783
878
|
async function installLocalTrackerApp({ appDir }) {
|
|
784
879
|
// Copy the current package's runtime (bin + src) into ~/.vibeusage so notify can run sync without npx.
|
|
785
|
-
const packageRoot = path.resolve(__dirname,
|
|
786
|
-
const srcFrom = path.join(packageRoot,
|
|
787
|
-
const binFrom = path.join(packageRoot,
|
|
788
|
-
const nodeModulesFrom = path.join(packageRoot,
|
|
880
|
+
const packageRoot = path.resolve(__dirname, "../..");
|
|
881
|
+
const srcFrom = path.join(packageRoot, "src");
|
|
882
|
+
const binFrom = path.join(packageRoot, "bin", "tracker.js");
|
|
883
|
+
const nodeModulesFrom = path.join(packageRoot, "node_modules");
|
|
884
|
+
|
|
885
|
+
// When running from the installed local runtime (or when appDir is symlinked to this package),
|
|
886
|
+
// source and destination resolve to the same place. Do not delete appDir in that case.
|
|
887
|
+
if (await pathsPointToSameLocation(packageRoot, appDir)) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
789
890
|
|
|
790
|
-
const srcTo = path.join(appDir,
|
|
791
|
-
const binToDir = path.join(appDir,
|
|
792
|
-
const binTo = path.join(binToDir,
|
|
793
|
-
const nodeModulesTo = path.join(appDir,
|
|
891
|
+
const srcTo = path.join(appDir, "src");
|
|
892
|
+
const binToDir = path.join(appDir, "bin");
|
|
893
|
+
const binTo = path.join(binToDir, "tracker.js");
|
|
894
|
+
const nodeModulesTo = path.join(appDir, "node_modules");
|
|
794
895
|
|
|
795
896
|
await fs.rm(appDir, { recursive: true, force: true }).catch(() => {});
|
|
796
897
|
await ensureDir(appDir);
|
|
@@ -801,23 +902,38 @@ async function installLocalTrackerApp({ appDir }) {
|
|
|
801
902
|
await copyRuntimeDependencies({ from: nodeModulesFrom, to: nodeModulesTo });
|
|
802
903
|
}
|
|
803
904
|
|
|
905
|
+
async function pathsPointToSameLocation(a, b) {
|
|
906
|
+
const aReal = await safeRealpath(a);
|
|
907
|
+
const bReal = await safeRealpath(b);
|
|
908
|
+
if (aReal && bReal) return aReal === bReal;
|
|
909
|
+
return path.resolve(a) === path.resolve(b);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
async function safeRealpath(p) {
|
|
913
|
+
try {
|
|
914
|
+
return await fs.realpath(p);
|
|
915
|
+
} catch (_err) {
|
|
916
|
+
return null;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
804
920
|
function spawnInitSync({ trackerBinPath, packageName }) {
|
|
805
|
-
const fallbackPkg = packageName ||
|
|
806
|
-
const argv = [
|
|
807
|
-
const hasLocalRuntime = typeof trackerBinPath ===
|
|
921
|
+
const fallbackPkg = packageName || "vibeusage";
|
|
922
|
+
const argv = ["sync", "--drain"];
|
|
923
|
+
const hasLocalRuntime = typeof trackerBinPath === "string" && fssync.existsSync(trackerBinPath);
|
|
808
924
|
const cmd = hasLocalRuntime
|
|
809
925
|
? [process.execPath, trackerBinPath, ...argv]
|
|
810
|
-
: [
|
|
926
|
+
: ["npx", "--yes", fallbackPkg, ...argv];
|
|
811
927
|
const child = cp.spawn(cmd[0], cmd.slice(1), {
|
|
812
928
|
detached: true,
|
|
813
|
-
stdio:
|
|
814
|
-
env: process.env
|
|
929
|
+
stdio: "ignore",
|
|
930
|
+
env: process.env,
|
|
815
931
|
});
|
|
816
|
-
child.on(
|
|
817
|
-
const msg = err && err.message ? err.message :
|
|
818
|
-
const detail = isDebugEnabled() ? ` (${msg})` :
|
|
932
|
+
child.on("error", (err) => {
|
|
933
|
+
const msg = err && err.message ? err.message : "unknown error";
|
|
934
|
+
const detail = isDebugEnabled() ? ` (${msg})` : "";
|
|
819
935
|
process.stderr.write(`Minor issue: Background sync could not start${detail}.\n`);
|
|
820
|
-
process.stderr.write(
|
|
936
|
+
process.stderr.write("Run: npx --yes vibeusage sync\n");
|
|
821
937
|
});
|
|
822
938
|
child.unref();
|
|
823
939
|
}
|
|
@@ -838,5 +954,5 @@ async function copyRuntimeDependencies({ from, to }) {
|
|
|
838
954
|
}
|
|
839
955
|
|
|
840
956
|
function isDebugEnabled() {
|
|
841
|
-
return process.env.VIBEUSAGE_DEBUG ===
|
|
957
|
+
return process.env.VIBEUSAGE_DEBUG === "1";
|
|
842
958
|
}
|