@primitive.ai/prim 0.1.0-alpha.44 → 0.1.0-alpha.46
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/SKILL.md +91 -6
- package/dist/{chunk-OKUXEBPT.js → chunk-ESLX7ISX.js} +1 -1
- package/dist/{chunk-UISU3A7W.js → chunk-JT4Q333I.js} +1 -1
- package/dist/chunk-VQ2ZV2D5.js +502 -0
- package/dist/{chunk-BSZGI5RL.js → chunk-Z4VI6TNX.js} +1 -1
- package/dist/daemon/server.js +520 -207
- package/dist/hooks/post-commit.js +2 -2
- package/dist/hooks/post-tool-use.js +2 -2
- package/dist/hooks/pre-commit.js +2 -2
- package/dist/hooks/pre-tool-use.js +1 -1
- package/dist/hooks/prim-hook.js +3 -3
- package/dist/hooks/session-start.js +2 -2
- package/dist/index.js +339 -286
- package/dist/statusline-main.js +45 -37
- package/package.json +1 -1
- package/dist/chunk-W6PAKEDO.js +0 -211
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
checkAffectedDecisions,
|
|
13
13
|
daemonOrDirectGet,
|
|
14
14
|
formatDecisionsWarning
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-Z4VI6TNX.js";
|
|
16
16
|
import {
|
|
17
17
|
JOURNAL_DIR,
|
|
18
18
|
SESSIONS_DIR,
|
|
@@ -21,10 +21,10 @@ import {
|
|
|
21
21
|
listFlushing,
|
|
22
22
|
pendingJournalStats,
|
|
23
23
|
readMovesFromPath
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-ESLX7ISX.js";
|
|
25
25
|
import {
|
|
26
26
|
fetchFeedbackCapability
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-JT4Q333I.js";
|
|
28
28
|
import {
|
|
29
29
|
inspectWorkspaceId
|
|
30
30
|
} from "./chunk-IMAIBPUC.js";
|
|
@@ -41,14 +41,18 @@ import {
|
|
|
41
41
|
import {
|
|
42
42
|
HttpError,
|
|
43
43
|
REFRESH_TOKEN_PATH,
|
|
44
|
-
TOKEN_EXPIRES_PATH,
|
|
45
44
|
TOKEN_FILE_PATH,
|
|
46
|
-
|
|
45
|
+
clearStoredCredentials,
|
|
46
|
+
commitCredentials,
|
|
47
47
|
getClient,
|
|
48
48
|
getSiteUrl,
|
|
49
49
|
getTokenExpiresAt,
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
isSessionEnded,
|
|
51
|
+
refreshToken,
|
|
52
|
+
resolveAuthCredential,
|
|
53
|
+
setStoredToken,
|
|
54
|
+
withFileLock
|
|
55
|
+
} from "./chunk-VQ2ZV2D5.js";
|
|
52
56
|
import {
|
|
53
57
|
daemonIsLive,
|
|
54
58
|
daemonRequest
|
|
@@ -56,7 +60,7 @@ import {
|
|
|
56
60
|
|
|
57
61
|
// src/index.ts
|
|
58
62
|
import { readFileSync as readFileSync12 } from "fs";
|
|
59
|
-
import { dirname as
|
|
63
|
+
import { dirname as dirname8, resolve as resolve6 } from "path";
|
|
60
64
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
61
65
|
import { Command } from "commander";
|
|
62
66
|
import updateNotifier from "update-notifier";
|
|
@@ -96,10 +100,9 @@ function registerActivationCommands(program2) {
|
|
|
96
100
|
// src/commands/auth.ts
|
|
97
101
|
import { exec } from "child_process";
|
|
98
102
|
import { createHash, randomBytes } from "crypto";
|
|
99
|
-
import { existsSync,
|
|
103
|
+
import { existsSync, readFileSync } from "fs";
|
|
100
104
|
import { createServer } from "http";
|
|
101
105
|
import { platform } from "os";
|
|
102
|
-
import { dirname } from "path";
|
|
103
106
|
|
|
104
107
|
// src/commands/auth-pages.ts
|
|
105
108
|
var FAVICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="99.77 99.96 400.85 400.85" fill="currentColor">
|
|
@@ -248,12 +251,13 @@ var STATE_MISMATCH_HTML = renderPage({
|
|
|
248
251
|
});
|
|
249
252
|
|
|
250
253
|
// src/commands/auth.ts
|
|
251
|
-
var FILE_MODE = 384;
|
|
252
254
|
var LOCALHOST = "127.0.0.1";
|
|
253
255
|
var CALLBACK_PORT = 19876;
|
|
254
256
|
var CALLBACK_TIMEOUT_MS = 12e4;
|
|
255
257
|
var EXIT_OK = 0;
|
|
256
258
|
var EXIT_FAIL = 1;
|
|
259
|
+
var EXIT_UNREACHABLE = 2;
|
|
260
|
+
var AUTH_PROBE_TIMEOUT_MS = 1e4;
|
|
257
261
|
var HTML_HEADERS = { "Content-Type": "text/html; charset=utf-8" };
|
|
258
262
|
var BASE64_PLUS_RE = /\+/g;
|
|
259
263
|
var BASE64_SLASH_RE = /\//g;
|
|
@@ -271,13 +275,6 @@ function openBrowser(url) {
|
|
|
271
275
|
const cmd = os === "darwin" ? "open" : os === "win32" ? "start" : "xdg-open";
|
|
272
276
|
exec(`${cmd} "${url}"`);
|
|
273
277
|
}
|
|
274
|
-
function saveToken(token) {
|
|
275
|
-
const dir = dirname(TOKEN_FILE_PATH);
|
|
276
|
-
if (!existsSync(dir)) {
|
|
277
|
-
mkdirSync(dir, { recursive: true });
|
|
278
|
-
}
|
|
279
|
-
writeFileSync(TOKEN_FILE_PATH, token, { mode: FILE_MODE });
|
|
280
|
-
}
|
|
281
278
|
function resolveCallbackPage(params, expectedState) {
|
|
282
279
|
if (params.get("state") !== expectedState) {
|
|
283
280
|
return {
|
|
@@ -327,6 +324,123 @@ function reportSuccess() {
|
|
|
327
324
|
console.log(JSON.stringify({ authenticated: true, tokenFile: TOKEN_FILE_PATH }));
|
|
328
325
|
process.exitCode = EXIT_OK;
|
|
329
326
|
}
|
|
327
|
+
function hasStoredRefreshToken() {
|
|
328
|
+
if (!existsSync(REFRESH_TOKEN_PATH)) return false;
|
|
329
|
+
try {
|
|
330
|
+
return readFileSync(REFRESH_TOKEN_PATH, "utf-8").trim().length > 0;
|
|
331
|
+
} catch {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function authStatusResult(status, reason, source, refreshTokenPresent) {
|
|
336
|
+
const expiresAt = source === "token_file" ? getTokenExpiresAt() : void 0;
|
|
337
|
+
const expiresInMs = expiresAt === void 0 ? null : expiresAt - Date.now();
|
|
338
|
+
return {
|
|
339
|
+
authenticated: status === "valid",
|
|
340
|
+
status,
|
|
341
|
+
reason,
|
|
342
|
+
tokenFile: source === "token_file" ? TOKEN_FILE_PATH : null,
|
|
343
|
+
accessTokenExpiresInMs: expiresInMs,
|
|
344
|
+
accessTokenExpired: expiresInMs !== null && expiresInMs <= 0,
|
|
345
|
+
refreshTokenPresent,
|
|
346
|
+
warnings: source === "token_file" && !refreshTokenPresent ? ["no refresh token"] : []
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
async function verifyAuthStatus() {
|
|
350
|
+
const credential = resolveAuthCredential();
|
|
351
|
+
if (!credential) {
|
|
352
|
+
return authStatusResult("invalid", "missing_credentials", void 0, false);
|
|
353
|
+
}
|
|
354
|
+
const storedCredential = credential.source === "token_file";
|
|
355
|
+
const refreshPresent = storedCredential && hasStoredRefreshToken();
|
|
356
|
+
let accessToken = credential.token;
|
|
357
|
+
if (refreshPresent) {
|
|
358
|
+
try {
|
|
359
|
+
const refreshed = await refreshToken({
|
|
360
|
+
force: true,
|
|
361
|
+
signal: AbortSignal.timeout(AUTH_PROBE_TIMEOUT_MS)
|
|
362
|
+
});
|
|
363
|
+
if (!refreshed) {
|
|
364
|
+
const rejected = isSessionEnded();
|
|
365
|
+
return authStatusResult(
|
|
366
|
+
rejected ? "invalid" : "unreachable",
|
|
367
|
+
rejected ? "refresh_rejected" : "verification_unavailable",
|
|
368
|
+
credential.source,
|
|
369
|
+
true
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
accessToken = refreshed;
|
|
373
|
+
} catch {
|
|
374
|
+
const rejected = isSessionEnded();
|
|
375
|
+
return authStatusResult(
|
|
376
|
+
rejected ? "invalid" : "unreachable",
|
|
377
|
+
rejected ? "refresh_rejected" : "verification_unavailable",
|
|
378
|
+
credential.source,
|
|
379
|
+
true
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
const response = await fetch(`${getSiteUrl()}/api/cli/auth/status`, {
|
|
385
|
+
method: "GET",
|
|
386
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
387
|
+
signal: AbortSignal.timeout(AUTH_PROBE_TIMEOUT_MS)
|
|
388
|
+
});
|
|
389
|
+
if (response.ok) {
|
|
390
|
+
return authStatusResult("valid", "verified", credential.source, refreshPresent);
|
|
391
|
+
}
|
|
392
|
+
if (response.status === 401 || response.status === 403) {
|
|
393
|
+
return authStatusResult("invalid", "access_rejected", credential.source, refreshPresent);
|
|
394
|
+
}
|
|
395
|
+
return authStatusResult(
|
|
396
|
+
"unreachable",
|
|
397
|
+
"verification_unavailable",
|
|
398
|
+
credential.source,
|
|
399
|
+
refreshPresent
|
|
400
|
+
);
|
|
401
|
+
} catch {
|
|
402
|
+
return authStatusResult(
|
|
403
|
+
"unreachable",
|
|
404
|
+
"verification_unavailable",
|
|
405
|
+
credential.source,
|
|
406
|
+
refreshPresent
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function authStatusExitCode(status) {
|
|
411
|
+
if (status === "valid") return EXIT_OK;
|
|
412
|
+
return status === "invalid" ? EXIT_FAIL : EXIT_UNREACHABLE;
|
|
413
|
+
}
|
|
414
|
+
function writeHumanAuthStatus(result) {
|
|
415
|
+
if (result.status === "invalid") {
|
|
416
|
+
const detail = result.reason === "refresh_rejected" ? "The saved session was rejected." : result.reason === "access_rejected" ? "The selected access token was rejected." : "No credentials were found.";
|
|
417
|
+
console.log(`${detail} Run \`prim auth login\` to authenticate.`);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (result.status === "unreachable") {
|
|
421
|
+
console.log("Authentication could not be verified. Credentials were retained.");
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
console.log("Authenticated (verified).");
|
|
425
|
+
if (result.tokenFile) {
|
|
426
|
+
console.log(`Token file: ${result.tokenFile}`);
|
|
427
|
+
} else {
|
|
428
|
+
console.log("Token source: fixed environment credential");
|
|
429
|
+
}
|
|
430
|
+
if (result.accessTokenExpiresInMs === null) {
|
|
431
|
+
console.log("Access token expiry: unknown (no metadata)");
|
|
432
|
+
} else if (result.accessTokenExpiresInMs <= 0) {
|
|
433
|
+
console.log("Access token: expired");
|
|
434
|
+
} else {
|
|
435
|
+
const minutes = Math.floor(result.accessTokenExpiresInMs / 6e4);
|
|
436
|
+
const seconds = Math.floor(result.accessTokenExpiresInMs % 6e4 / 1e3);
|
|
437
|
+
console.log(`Access token expires in: ${minutes}m ${seconds}s`);
|
|
438
|
+
}
|
|
439
|
+
console.log(
|
|
440
|
+
`Refresh token: ${result.tokenFile ? result.refreshTokenPresent ? "present" : "missing" : "not applicable"}`
|
|
441
|
+
);
|
|
442
|
+
for (const warning of result.warnings) console.log(`Warning: ${warning}`);
|
|
443
|
+
}
|
|
330
444
|
function registerAuthCommands(program2) {
|
|
331
445
|
const auth = program2.command("auth").description("Manage CLI authentication");
|
|
332
446
|
auth.command("login").description("Authenticate via browser (WorkOS OAuth)").action(async () => {
|
|
@@ -417,27 +531,28 @@ ${authUrl.toString()}
|
|
|
417
531
|
return;
|
|
418
532
|
}
|
|
419
533
|
try {
|
|
420
|
-
const
|
|
421
|
-
|
|
534
|
+
const tokens = await exchangeCode(siteUrl, result.code, verifier, redirectUri);
|
|
535
|
+
await commitCredentials(tokens);
|
|
422
536
|
reportSuccess();
|
|
423
537
|
} catch (err) {
|
|
424
538
|
reportFailure("token_exchange_failed", err instanceof Error ? err.message : String(err));
|
|
425
539
|
}
|
|
426
540
|
});
|
|
427
|
-
auth.command("set-token <token>").description("Save a bearer token for authenticated CLI calls").action((token) => {
|
|
428
|
-
|
|
541
|
+
auth.command("set-token <token>").description("Save a bearer token for authenticated CLI calls").action(async (token) => {
|
|
542
|
+
await setStoredToken(token);
|
|
429
543
|
console.log(`Token saved to ${TOKEN_FILE_PATH}`);
|
|
430
544
|
});
|
|
431
545
|
auth.command("clear").description("Remove the saved authentication token").action(async () => {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
546
|
+
const removed = await clearStoredCredentials({
|
|
547
|
+
beforeClear: async (refreshTokenValue) => {
|
|
548
|
+
if (!refreshTokenValue) return;
|
|
435
549
|
try {
|
|
436
550
|
const siteUrl = getSiteUrl();
|
|
437
551
|
const res = await fetch(`${siteUrl}/mcp/broker/revoke`, {
|
|
438
552
|
method: "POST",
|
|
439
553
|
headers: { "Content-Type": "application/json" },
|
|
440
|
-
body: JSON.stringify({ refresh_token: refreshTokenValue })
|
|
554
|
+
body: JSON.stringify({ refresh_token: refreshTokenValue }),
|
|
555
|
+
signal: AbortSignal.timeout(AUTH_PROBE_TIMEOUT_MS)
|
|
441
556
|
});
|
|
442
557
|
if (res.ok) {
|
|
443
558
|
console.log("Server token revoked.");
|
|
@@ -451,62 +566,18 @@ ${authUrl.toString()}
|
|
|
451
566
|
console.warn("Could not reach server for revocation \u2014 clearing local files anyway.");
|
|
452
567
|
}
|
|
453
568
|
}
|
|
454
|
-
}
|
|
455
|
-
let removed = false;
|
|
456
|
-
for (const filePath of [TOKEN_FILE_PATH, REFRESH_TOKEN_PATH, TOKEN_EXPIRES_PATH]) {
|
|
457
|
-
if (existsSync(filePath)) {
|
|
458
|
-
rmSync(filePath);
|
|
459
|
-
removed = true;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
569
|
+
});
|
|
462
570
|
if (removed) {
|
|
463
571
|
console.log("Local tokens removed.");
|
|
464
572
|
} else {
|
|
465
573
|
console.log("No saved tokens found.");
|
|
466
574
|
}
|
|
467
575
|
});
|
|
468
|
-
auth.command("status").description("Check authentication status and token expiry").option("--json", "Output as JSON").action((opts) => {
|
|
469
|
-
const
|
|
470
|
-
if (opts.json)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
const refreshPresent = existsSync(REFRESH_TOKEN_PATH);
|
|
474
|
-
printJson({
|
|
475
|
-
authenticated: !!token,
|
|
476
|
-
tokenFile: token ? TOKEN_FILE_PATH : null,
|
|
477
|
-
accessTokenExpiresInMs: expiresInMs,
|
|
478
|
-
accessTokenExpired: expiresInMs !== null && expiresInMs <= 0,
|
|
479
|
-
refreshTokenPresent: refreshPresent,
|
|
480
|
-
warnings: !token || refreshPresent ? [] : ["no refresh token"]
|
|
481
|
-
});
|
|
482
|
-
process.exit(token ? 0 : 1);
|
|
483
|
-
}
|
|
484
|
-
if (!token) {
|
|
485
|
-
console.log("Not authenticated. Run `prim auth login` to authenticate.");
|
|
486
|
-
process.exit(1);
|
|
487
|
-
}
|
|
488
|
-
console.log("Authenticated.");
|
|
489
|
-
console.log(`Token file: ${TOKEN_FILE_PATH}`);
|
|
490
|
-
const expiresAt = getTokenExpiresAt();
|
|
491
|
-
if (expiresAt) {
|
|
492
|
-
const remaining = expiresAt - Date.now();
|
|
493
|
-
if (remaining <= 0) {
|
|
494
|
-
console.log("Access token: expired");
|
|
495
|
-
} else {
|
|
496
|
-
const minutes = Math.floor(remaining / 6e4);
|
|
497
|
-
const seconds = Math.floor(remaining % 6e4 / 1e3);
|
|
498
|
-
console.log(`Access token expires in: ${minutes}m ${seconds}s`);
|
|
499
|
-
}
|
|
500
|
-
} else {
|
|
501
|
-
console.log("Access token expiry: unknown (no metadata)");
|
|
502
|
-
}
|
|
503
|
-
const hasRefresh = existsSync(REFRESH_TOKEN_PATH);
|
|
504
|
-
console.log(`Refresh token: ${hasRefresh ? "present" : "missing"}`);
|
|
505
|
-
if (!hasRefresh) {
|
|
506
|
-
console.log(
|
|
507
|
-
"Warning: No refresh token. Re-run `prim auth login` when access token expires."
|
|
508
|
-
);
|
|
509
|
-
}
|
|
576
|
+
auth.command("status").description("Check authentication status and token expiry").option("--json", "Output as JSON").action(async (opts) => {
|
|
577
|
+
const result = await verifyAuthStatus();
|
|
578
|
+
if (opts.json) printJson(result);
|
|
579
|
+
else writeHumanAuthStatus(result);
|
|
580
|
+
process.exit(authStatusExitCode(result.status));
|
|
510
581
|
});
|
|
511
582
|
}
|
|
512
583
|
async function exchangeCode(siteUrl, code, codeVerifier, redirectUri) {
|
|
@@ -524,19 +595,14 @@ async function exchangeCode(siteUrl, code, codeVerifier, redirectUri) {
|
|
|
524
595
|
throw new Error(`Token exchange failed (${response.status}): ${body}`);
|
|
525
596
|
}
|
|
526
597
|
const data = await response.json();
|
|
527
|
-
if (!data.access_token) {
|
|
528
|
-
throw new Error("
|
|
598
|
+
if (!data.access_token || !data.refresh_token) {
|
|
599
|
+
throw new Error("Token exchange did not return a complete credential pair");
|
|
529
600
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
writeFileSync(refreshPath, data.refresh_token, { mode: FILE_MODE });
|
|
537
|
-
}
|
|
538
|
-
saveTokenExpiry(data.access_token, data.expires_in);
|
|
539
|
-
return data.access_token;
|
|
601
|
+
return {
|
|
602
|
+
accessToken: data.access_token,
|
|
603
|
+
refreshToken: data.refresh_token,
|
|
604
|
+
expiresIn: data.expires_in
|
|
605
|
+
};
|
|
540
606
|
}
|
|
541
607
|
|
|
542
608
|
// src/commands/claude-install.ts
|
|
@@ -544,14 +610,14 @@ import {
|
|
|
544
610
|
closeSync,
|
|
545
611
|
existsSync as existsSync3,
|
|
546
612
|
fsyncSync,
|
|
547
|
-
mkdirSync as
|
|
613
|
+
mkdirSync as mkdirSync2,
|
|
548
614
|
openSync,
|
|
549
615
|
readFileSync as readFileSync3,
|
|
550
616
|
renameSync as renameSync2,
|
|
551
|
-
writeFileSync as
|
|
617
|
+
writeFileSync as writeFileSync2
|
|
552
618
|
} from "fs";
|
|
553
619
|
import { homedir as homedir2 } from "os";
|
|
554
|
-
import { dirname as
|
|
620
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
555
621
|
|
|
556
622
|
// src/daemon/launchd.ts
|
|
557
623
|
import { spawnSync } from "child_process";
|
|
@@ -560,17 +626,16 @@ import {
|
|
|
560
626
|
chmodSync,
|
|
561
627
|
copyFileSync,
|
|
562
628
|
existsSync as existsSync2,
|
|
563
|
-
mkdirSync
|
|
629
|
+
mkdirSync,
|
|
564
630
|
mkdtempSync,
|
|
565
631
|
readFileSync as readFileSync2,
|
|
566
632
|
renameSync,
|
|
567
|
-
rmSync
|
|
568
|
-
statSync,
|
|
633
|
+
rmSync,
|
|
569
634
|
symlinkSync,
|
|
570
|
-
writeFileSync
|
|
635
|
+
writeFileSync
|
|
571
636
|
} from "fs";
|
|
572
637
|
import { homedir } from "os";
|
|
573
|
-
import { dirname
|
|
638
|
+
import { dirname, isAbsolute, join, resolve } from "path";
|
|
574
639
|
var LAUNCHD_LABEL = "ai.getprimitive.prim-daemon";
|
|
575
640
|
var RUNTIME_SCHEMA_VERSION = 1;
|
|
576
641
|
var RUNTIME_DIR_MODE = 448;
|
|
@@ -580,9 +645,6 @@ var PLIST_FILE_MODE = 384;
|
|
|
580
645
|
var READY_TIMEOUT_MS = 15e3;
|
|
581
646
|
var READY_POLL_MS = 100;
|
|
582
647
|
var READY_PROBE_TIMEOUT_MS = 250;
|
|
583
|
-
var LIFECYCLE_LOCK_TIMEOUT_MS = 3e4;
|
|
584
|
-
var LIFECYCLE_LOCK_POLL_MS = 50;
|
|
585
|
-
var LIFECYCLE_LOCK_INIT_GRACE_MS = 2e3;
|
|
586
648
|
function xdgDataHome(options) {
|
|
587
649
|
const home = options.homeDir ?? homedir();
|
|
588
650
|
const configured = (options.env ?? process.env).XDG_DATA_HOME;
|
|
@@ -606,7 +668,7 @@ function runtimePaths(options = {}) {
|
|
|
606
668
|
};
|
|
607
669
|
}
|
|
608
670
|
function findPackageVersion(entryFile) {
|
|
609
|
-
let dir =
|
|
671
|
+
let dir = dirname(resolve(entryFile));
|
|
610
672
|
for (let depth = 0; depth < 6; depth++) {
|
|
611
673
|
const packagePath = join(dir, "package.json");
|
|
612
674
|
if (existsSync2(packagePath)) {
|
|
@@ -618,7 +680,7 @@ function findPackageVersion(entryFile) {
|
|
|
618
680
|
} catch {
|
|
619
681
|
}
|
|
620
682
|
}
|
|
621
|
-
const parent =
|
|
683
|
+
const parent = dirname(dir);
|
|
622
684
|
if (parent === dir) break;
|
|
623
685
|
dir = parent;
|
|
624
686
|
}
|
|
@@ -646,7 +708,7 @@ function atomicSymlink(target, linkPath) {
|
|
|
646
708
|
try {
|
|
647
709
|
renameSync(tempLink, linkPath);
|
|
648
710
|
} catch (error) {
|
|
649
|
-
|
|
711
|
+
rmSync(tempLink, { force: true });
|
|
650
712
|
throw error;
|
|
651
713
|
}
|
|
652
714
|
}
|
|
@@ -677,11 +739,11 @@ function stageRuntime(options = {}) {
|
|
|
677
739
|
RUNTIME_LAUNCHER_MODE
|
|
678
740
|
);
|
|
679
741
|
} else {
|
|
680
|
-
|
|
742
|
+
rmSync(paths.statuslineLauncher, { force: true });
|
|
681
743
|
}
|
|
682
744
|
return { changed: false, manifest: current, paths };
|
|
683
745
|
}
|
|
684
|
-
|
|
746
|
+
mkdirSync(paths.releasesDir, { recursive: true, mode: RUNTIME_DIR_MODE });
|
|
685
747
|
chmodSync(paths.runtimeDir, RUNTIME_DIR_MODE);
|
|
686
748
|
chmodSync(paths.releasesDir, RUNTIME_DIR_MODE);
|
|
687
749
|
const stagingDir = mkdtempSync(join(paths.releasesDir, ".stage-"));
|
|
@@ -696,7 +758,7 @@ function stageRuntime(options = {}) {
|
|
|
696
758
|
chmodSync(statuslineTarget, RUNTIME_FILE_MODE);
|
|
697
759
|
}
|
|
698
760
|
const manifestTarget = join(stagingDir, "manifest.json");
|
|
699
|
-
|
|
761
|
+
writeFileSync(manifestTarget, `${JSON.stringify(desired, null, 2)}
|
|
700
762
|
`, {
|
|
701
763
|
encoding: "utf8",
|
|
702
764
|
mode: RUNTIME_FILE_MODE,
|
|
@@ -715,10 +777,10 @@ function stageRuntime(options = {}) {
|
|
|
715
777
|
RUNTIME_LAUNCHER_MODE
|
|
716
778
|
);
|
|
717
779
|
} else {
|
|
718
|
-
|
|
780
|
+
rmSync(paths.statuslineLauncher, { force: true });
|
|
719
781
|
}
|
|
720
782
|
} catch (error) {
|
|
721
|
-
|
|
783
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
722
784
|
throw error;
|
|
723
785
|
}
|
|
724
786
|
return { changed: true, manifest: desired, paths };
|
|
@@ -815,13 +877,13 @@ function atomicWrite(path, content, mode) {
|
|
|
815
877
|
}
|
|
816
878
|
} catch {
|
|
817
879
|
}
|
|
818
|
-
|
|
880
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
819
881
|
const temp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
820
|
-
|
|
882
|
+
writeFileSync(temp, content, { encoding: "utf8", mode, flag: "wx" });
|
|
821
883
|
try {
|
|
822
884
|
renameSync(temp, path);
|
|
823
885
|
} catch (error) {
|
|
824
|
-
|
|
886
|
+
rmSync(temp, { force: true });
|
|
825
887
|
throw error;
|
|
826
888
|
}
|
|
827
889
|
return true;
|
|
@@ -849,95 +911,9 @@ function processIsAlive(pid) {
|
|
|
849
911
|
return error.code === "EPERM";
|
|
850
912
|
}
|
|
851
913
|
}
|
|
852
|
-
function lifecycleOwnerPath(lockDir) {
|
|
853
|
-
return join(lockDir, "owner.json");
|
|
854
|
-
}
|
|
855
|
-
function readLifecycleOwner(lockDir) {
|
|
856
|
-
try {
|
|
857
|
-
const owner = JSON.parse(
|
|
858
|
-
readFileSync2(lifecycleOwnerPath(lockDir), "utf8")
|
|
859
|
-
);
|
|
860
|
-
if (!Number.isInteger(owner.pid) || owner.pid <= 0 || typeof owner.nonce !== "string" || typeof owner.createdAt !== "number") {
|
|
861
|
-
return null;
|
|
862
|
-
}
|
|
863
|
-
return owner;
|
|
864
|
-
} catch {
|
|
865
|
-
return null;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
function sameLifecycleOwner(a, b) {
|
|
869
|
-
return a?.pid === b.pid && a.nonce === b.nonce && a.createdAt === b.createdAt;
|
|
870
|
-
}
|
|
871
|
-
function tryTakeLifecycleLock(lockDir, now) {
|
|
872
|
-
try {
|
|
873
|
-
mkdirSync2(lockDir, { mode: RUNTIME_DIR_MODE });
|
|
874
|
-
} catch (error) {
|
|
875
|
-
if (error.code === "EEXIST") return null;
|
|
876
|
-
throw error;
|
|
877
|
-
}
|
|
878
|
-
const owner = {
|
|
879
|
-
pid: process.pid,
|
|
880
|
-
nonce: createHash2("sha256").update(`${process.pid}\0${now}\0${Math.random()}`).digest("hex"),
|
|
881
|
-
createdAt: now
|
|
882
|
-
};
|
|
883
|
-
try {
|
|
884
|
-
writeFileSync2(lifecycleOwnerPath(lockDir), `${JSON.stringify(owner)}
|
|
885
|
-
`, {
|
|
886
|
-
encoding: "utf8",
|
|
887
|
-
mode: RUNTIME_FILE_MODE,
|
|
888
|
-
flag: "wx"
|
|
889
|
-
});
|
|
890
|
-
return owner;
|
|
891
|
-
} catch (error) {
|
|
892
|
-
rmSync2(lockDir, { recursive: true, force: true });
|
|
893
|
-
throw error;
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
function recoverStaleLifecycleLock(lockDir, now, alive) {
|
|
897
|
-
const owner = readLifecycleOwner(lockDir);
|
|
898
|
-
if (owner) {
|
|
899
|
-
if (alive(owner.pid)) return false;
|
|
900
|
-
if (!sameLifecycleOwner(readLifecycleOwner(lockDir), owner)) return false;
|
|
901
|
-
rmSync2(lockDir, { recursive: true, force: true });
|
|
902
|
-
return true;
|
|
903
|
-
}
|
|
904
|
-
try {
|
|
905
|
-
const before = statSync(lockDir).mtimeMs;
|
|
906
|
-
if (now - before < LIFECYCLE_LOCK_INIT_GRACE_MS) return false;
|
|
907
|
-
if (readLifecycleOwner(lockDir)) return false;
|
|
908
|
-
if (statSync(lockDir).mtimeMs !== before) return false;
|
|
909
|
-
rmSync2(lockDir, { recursive: true, force: true });
|
|
910
|
-
return true;
|
|
911
|
-
} catch {
|
|
912
|
-
return true;
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
914
|
async function withDaemonLifecycleLock(operation, options = {}) {
|
|
916
915
|
const lockDir = runtimePaths(options).lifecycleLockDir;
|
|
917
|
-
|
|
918
|
-
const control = options.lifecycleLock;
|
|
919
|
-
const nowMs = control?.nowMs ?? Date.now;
|
|
920
|
-
const sleepFor = control?.sleep ?? defaultSleep;
|
|
921
|
-
const alive = control?.processAlive ?? processIsAlive;
|
|
922
|
-
const deadline = nowMs() + (control?.timeoutMs ?? LIFECYCLE_LOCK_TIMEOUT_MS);
|
|
923
|
-
let owner = null;
|
|
924
|
-
while (!owner) {
|
|
925
|
-
const now = nowMs();
|
|
926
|
-
owner = tryTakeLifecycleLock(lockDir, now);
|
|
927
|
-
if (owner) break;
|
|
928
|
-
recoverStaleLifecycleLock(lockDir, now, alive);
|
|
929
|
-
if (now >= deadline) {
|
|
930
|
-
throw new Error(`timed out waiting for daemon lifecycle lock ${lockDir}`);
|
|
931
|
-
}
|
|
932
|
-
await sleepFor(control?.pollMs ?? LIFECYCLE_LOCK_POLL_MS);
|
|
933
|
-
}
|
|
934
|
-
try {
|
|
935
|
-
return await operation();
|
|
936
|
-
} finally {
|
|
937
|
-
if (sameLifecycleOwner(readLifecycleOwner(lockDir), owner)) {
|
|
938
|
-
rmSync2(lockDir, { recursive: true, force: true });
|
|
939
|
-
}
|
|
940
|
-
}
|
|
916
|
+
return withFileLock(lockDir, operation, options.lifecycleLock);
|
|
941
917
|
}
|
|
942
918
|
async function stopVerifiedLegacyDaemon(homeDir) {
|
|
943
919
|
const pidPath = join(homeDir, ".config", "prim", "daemon.pid");
|
|
@@ -975,11 +951,11 @@ function daemonExplicitlyDisabled(options = {}) {
|
|
|
975
951
|
function setDaemonExplicitlyDisabled(disabled, options = {}) {
|
|
976
952
|
const marker = runtimePaths(options).disabledMarker;
|
|
977
953
|
if (!disabled) {
|
|
978
|
-
|
|
954
|
+
rmSync(marker, { force: true });
|
|
979
955
|
return;
|
|
980
956
|
}
|
|
981
|
-
|
|
982
|
-
|
|
957
|
+
mkdirSync(dirname(marker), { recursive: true, mode: RUNTIME_DIR_MODE });
|
|
958
|
+
writeFileSync(marker, "disabled by `prim daemon stop`\n", {
|
|
983
959
|
encoding: "utf8",
|
|
984
960
|
mode: RUNTIME_FILE_MODE
|
|
985
961
|
});
|
|
@@ -1002,8 +978,8 @@ async function ensureMacDaemonLocked(options) {
|
|
|
1002
978
|
if (options.explicitlyStarted) setDaemonExplicitlyDisabled(false, options);
|
|
1003
979
|
const runtime = stageRuntime(options);
|
|
1004
980
|
const servicePaths = launchdPaths(options);
|
|
1005
|
-
|
|
1006
|
-
|
|
981
|
+
mkdirSync(dirname(servicePaths.logPath), { recursive: true, mode: RUNTIME_DIR_MODE });
|
|
982
|
+
writeFileSync(servicePaths.logPath, "", { mode: RUNTIME_FILE_MODE, flag: "a" });
|
|
1007
983
|
chmodSync(servicePaths.logPath, RUNTIME_FILE_MODE);
|
|
1008
984
|
const plist = generateLaunchAgentPlist({
|
|
1009
985
|
nodePath: runtime.manifest.nodePath,
|
|
@@ -1318,12 +1294,12 @@ function isGateInstalled(settings) {
|
|
|
1318
1294
|
return (settings.hooks?.PreToolUse ?? []).some((e) => entryHasCommand(e, GATE_BIN));
|
|
1319
1295
|
}
|
|
1320
1296
|
function atomicWrite2(path, content) {
|
|
1321
|
-
const dir =
|
|
1297
|
+
const dir = dirname2(path);
|
|
1322
1298
|
if (!existsSync3(dir)) {
|
|
1323
|
-
|
|
1299
|
+
mkdirSync2(dir, { recursive: true });
|
|
1324
1300
|
}
|
|
1325
1301
|
const tmp = `${path}.tmp.${String(Date.now())}`;
|
|
1326
|
-
|
|
1302
|
+
writeFileSync2(tmp, `${JSON.stringify(content, null, JSON_INDENT)}
|
|
1327
1303
|
`, "utf-8");
|
|
1328
1304
|
const fd = openSync(tmp, "r+");
|
|
1329
1305
|
try {
|
|
@@ -1657,7 +1633,7 @@ ${line("project", result.project)}`);
|
|
|
1657
1633
|
|
|
1658
1634
|
// src/commands/daemon.ts
|
|
1659
1635
|
import { spawn } from "child_process";
|
|
1660
|
-
import { closeSync as closeSync2, existsSync as existsSync4, mkdirSync as
|
|
1636
|
+
import { closeSync as closeSync2, existsSync as existsSync4, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync as readFileSync4, unlinkSync } from "fs";
|
|
1661
1637
|
import { homedir as homedir4 } from "os";
|
|
1662
1638
|
import { join as join4 } from "path";
|
|
1663
1639
|
|
|
@@ -1727,6 +1703,30 @@ var HEALTHY_POLL_MS = 250;
|
|
|
1727
1703
|
var EXIT_OK2 = 0;
|
|
1728
1704
|
var EXIT_NOT_RUNNING = 2;
|
|
1729
1705
|
var EXIT_BOOTING = 3;
|
|
1706
|
+
var MAX_HEALTH_ERROR_LENGTH = 240;
|
|
1707
|
+
function boundedHealthError(value) {
|
|
1708
|
+
if (!value) return void 0;
|
|
1709
|
+
const clean = stripControlChars(value).replace(/\s+/g, " ").trim();
|
|
1710
|
+
if (!clean) return void 0;
|
|
1711
|
+
return clean.length <= MAX_HEALTH_ERROR_LENGTH ? clean : `${clean.slice(0, MAX_HEALTH_ERROR_LENGTH - 1)}\u2026`;
|
|
1712
|
+
}
|
|
1713
|
+
function daemonDegradedReason(snapshot) {
|
|
1714
|
+
if (!snapshot || snapshot.healthy !== false) return void 0;
|
|
1715
|
+
if (snapshot.needsReauth) {
|
|
1716
|
+
return "authentication requires `prim auth login`";
|
|
1717
|
+
}
|
|
1718
|
+
if (snapshot.heartbeat?.healthy === false) {
|
|
1719
|
+
const detail = boundedHealthError(snapshot.heartbeat.lastError);
|
|
1720
|
+
return `heartbeat unhealthy${detail ? `: ${detail}` : ""}`;
|
|
1721
|
+
}
|
|
1722
|
+
if (snapshot.ingestion?.healthy === false) {
|
|
1723
|
+
const detail = boundedHealthError(snapshot.ingestion.lastError);
|
|
1724
|
+
const pending = snapshot.ingestion.pendingCount;
|
|
1725
|
+
const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
|
|
1726
|
+
return `ingestion unhealthy${typeof pending === "number" ? ` (${qualifier}${String(pending)} pending)` : ""}${detail ? `: ${detail}` : ""}`;
|
|
1727
|
+
}
|
|
1728
|
+
return "health checks have not recovered";
|
|
1729
|
+
}
|
|
1730
1730
|
function readPidfile() {
|
|
1731
1731
|
if (!existsSync4(PID_PATH)) {
|
|
1732
1732
|
return null;
|
|
@@ -1765,7 +1765,7 @@ function spawnDaemon(options) {
|
|
|
1765
1765
|
}
|
|
1766
1766
|
function openDaemonLog(configDir = CONFIG_DIR) {
|
|
1767
1767
|
if (!existsSync4(configDir)) {
|
|
1768
|
-
|
|
1768
|
+
mkdirSync3(configDir, { recursive: true, mode: CONFIG_DIR_MODE });
|
|
1769
1769
|
}
|
|
1770
1770
|
return openSync2(join4(configDir, "daemon.log"), "a", LOG_FILE_MODE);
|
|
1771
1771
|
}
|
|
@@ -1805,6 +1805,15 @@ async function verifiedPid(existing) {
|
|
|
1805
1805
|
function daemonStartIsHealthy(serviceReady, snapshot, expectedVersion) {
|
|
1806
1806
|
return serviceReady && snapshot?.healthy === true && expectedVersion !== void 0 && snapshot.version === expectedVersion;
|
|
1807
1807
|
}
|
|
1808
|
+
function daemonStartHealthFields(healthy, snapshot) {
|
|
1809
|
+
if (healthy) return {};
|
|
1810
|
+
return {
|
|
1811
|
+
state: "degraded",
|
|
1812
|
+
needsReauth: snapshot?.needsReauth === true,
|
|
1813
|
+
heartbeat: snapshot?.heartbeat ?? null,
|
|
1814
|
+
ingestion: snapshot?.ingestion ?? null
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1808
1817
|
async function detachedDaemonStart(opts) {
|
|
1809
1818
|
const existing = readPidfile();
|
|
1810
1819
|
if (existing?.alive) {
|
|
@@ -1961,8 +1970,9 @@ async function macDaemonStart(forceRestart = false) {
|
|
|
1961
1970
|
`
|
|
1962
1971
|
);
|
|
1963
1972
|
} else {
|
|
1973
|
+
const reason = daemonDegradedReason(snapshot);
|
|
1964
1974
|
process.stderr.write(
|
|
1965
|
-
`[prim] \u2717 launchd daemon did not reach healthy heartbeat + ingestion state (see ${LOG_PATH})
|
|
1975
|
+
`[prim] \u2717 launchd daemon did not reach healthy heartbeat + ingestion state${reason ? ` \xB7 ${reason}` : ""} (see ${LOG_PATH})
|
|
1966
1976
|
`
|
|
1967
1977
|
);
|
|
1968
1978
|
if (!process.exitCode) process.exitCode = EXIT_NOT_RUNNING;
|
|
@@ -1977,6 +1987,7 @@ async function macDaemonStart(forceRestart = false) {
|
|
|
1977
1987
|
loaded: result.service.loaded,
|
|
1978
1988
|
responding: result.responding,
|
|
1979
1989
|
healthy,
|
|
1990
|
+
...daemonStartHealthFields(healthy, snapshot),
|
|
1980
1991
|
version: snapshot?.version,
|
|
1981
1992
|
expectedVersion
|
|
1982
1993
|
},
|
|
@@ -2138,8 +2149,9 @@ function writeLiveSnapshot(snapshot, supervised = false) {
|
|
|
2138
2149
|
}
|
|
2139
2150
|
const team = snapshot.onlineNames !== void 0 ? ` \xB7 team: ${formatTeammates(snapshot.onlineNames, Number.POSITIVE_INFINITY)}` : "";
|
|
2140
2151
|
if (snapshot.healthy === false) {
|
|
2152
|
+
const reason = daemonDegradedReason(snapshot);
|
|
2141
2153
|
process.stderr.write(
|
|
2142
|
-
`[prim] \u2717 daemon unhealthy${supervised ? " under launchd" : ""} \xB7 pid=${snapshot.pid}${team}
|
|
2154
|
+
`[prim] \u2717 daemon unhealthy${supervised ? " under launchd" : ""} \xB7 pid=${snapshot.pid}${team}${reason ? ` \xB7 ${reason}` : ""}
|
|
2143
2155
|
`
|
|
2144
2156
|
);
|
|
2145
2157
|
return;
|
|
@@ -3147,12 +3159,13 @@ function classifyDoctor(checks) {
|
|
|
3147
3159
|
exitCode: status === "fail" ? EXIT_UNHEALTHY : 0
|
|
3148
3160
|
};
|
|
3149
3161
|
}
|
|
3150
|
-
function
|
|
3151
|
-
if (!
|
|
3162
|
+
function classifyAuthCredential(credential, expiresAt, hasRefresh) {
|
|
3163
|
+
if (!credential) {
|
|
3152
3164
|
return { name: "auth", status: "fail", detail: "no token \u2014 run `prim auth login`" };
|
|
3153
3165
|
}
|
|
3154
|
-
|
|
3155
|
-
|
|
3166
|
+
if (credential.source !== "token_file") {
|
|
3167
|
+
return { name: "auth", status: "ok", detail: "valid fixed bearer credential" };
|
|
3168
|
+
}
|
|
3156
3169
|
if (expiresAt !== void 0 && Date.now() >= expiresAt) {
|
|
3157
3170
|
return hasRefresh ? { name: "auth", status: "warn", detail: "access token expired (refresh available)" } : {
|
|
3158
3171
|
name: "auth",
|
|
@@ -3166,6 +3179,15 @@ function checkAuth() {
|
|
|
3166
3179
|
const detail = expiresAt !== void 0 ? `valid (${String(Math.round((expiresAt - Date.now()) / MS_PER_SECOND))}s left)` : "valid";
|
|
3167
3180
|
return { name: "auth", status: "ok", detail };
|
|
3168
3181
|
}
|
|
3182
|
+
function checkAuth() {
|
|
3183
|
+
const credential = resolveAuthCredential();
|
|
3184
|
+
const storedCredential = credential?.source === "token_file";
|
|
3185
|
+
return classifyAuthCredential(
|
|
3186
|
+
credential,
|
|
3187
|
+
storedCredential ? getTokenExpiresAt() : void 0,
|
|
3188
|
+
storedCredential && existsSync5(REFRESH_TOKEN_PATH)
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3169
3191
|
function classifyDaemonHealth(snapshot, options = {}) {
|
|
3170
3192
|
if (options.disabled) {
|
|
3171
3193
|
return {
|
|
@@ -3195,6 +3217,13 @@ function classifyDaemonHealth(snapshot, options = {}) {
|
|
|
3195
3217
|
detail: `launchd does not own the daemon socket (launchd ${String(options.service.pid ?? "none")} \xB7 socket ${String(snapshot.pid ?? "none")})`
|
|
3196
3218
|
};
|
|
3197
3219
|
}
|
|
3220
|
+
if (snapshot.needsReauth) {
|
|
3221
|
+
return {
|
|
3222
|
+
name: "daemon",
|
|
3223
|
+
status: "fail",
|
|
3224
|
+
detail: "authentication ended \u2014 run `prim auth login`"
|
|
3225
|
+
};
|
|
3226
|
+
}
|
|
3198
3227
|
if (!snapshot.heartbeat?.healthy) {
|
|
3199
3228
|
return {
|
|
3200
3229
|
name: "daemon",
|
|
@@ -3463,15 +3492,15 @@ import {
|
|
|
3463
3492
|
closeSync as closeSync3,
|
|
3464
3493
|
existsSync as existsSync6,
|
|
3465
3494
|
fsyncSync as fsyncSync2,
|
|
3466
|
-
mkdirSync as
|
|
3495
|
+
mkdirSync as mkdirSync4,
|
|
3467
3496
|
openSync as openSync3,
|
|
3468
3497
|
readFileSync as readFileSync5,
|
|
3469
3498
|
renameSync as renameSync3,
|
|
3470
|
-
rmSync as
|
|
3471
|
-
writeFileSync as
|
|
3499
|
+
rmSync as rmSync2,
|
|
3500
|
+
writeFileSync as writeFileSync3
|
|
3472
3501
|
} from "fs";
|
|
3473
3502
|
import { homedir as homedir5 } from "os";
|
|
3474
|
-
import { dirname as
|
|
3503
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
3475
3504
|
import { Document, parseDocument, stringify } from "yaml";
|
|
3476
3505
|
var CAPTURE_BIN3 = "prim-hook";
|
|
3477
3506
|
var GATE_BIN3 = "prim-pre-tool-use";
|
|
@@ -3660,12 +3689,12 @@ function setAutoAccept(raw) {
|
|
|
3660
3689
|
`;
|
|
3661
3690
|
}
|
|
3662
3691
|
function atomicWriteFile(path, content) {
|
|
3663
|
-
const dir =
|
|
3692
|
+
const dir = dirname3(path);
|
|
3664
3693
|
if (!existsSync6(dir)) {
|
|
3665
|
-
|
|
3694
|
+
mkdirSync4(dir, { recursive: true });
|
|
3666
3695
|
}
|
|
3667
3696
|
const tmp = `${path}.tmp.${String(process.pid)}`;
|
|
3668
|
-
|
|
3697
|
+
writeFileSync3(tmp, content, "utf-8");
|
|
3669
3698
|
const fd = openSync3(tmp, "r+");
|
|
3670
3699
|
try {
|
|
3671
3700
|
fsyncSync2(fd);
|
|
@@ -3685,15 +3714,15 @@ function assertMergeValid(before, after) {
|
|
|
3685
3714
|
}
|
|
3686
3715
|
function writeShim() {
|
|
3687
3716
|
const path = shimPath();
|
|
3688
|
-
const dir =
|
|
3717
|
+
const dir = dirname3(path);
|
|
3689
3718
|
if (!existsSync6(dir)) {
|
|
3690
|
-
|
|
3719
|
+
mkdirSync4(dir, { recursive: true });
|
|
3691
3720
|
}
|
|
3692
|
-
|
|
3721
|
+
writeFileSync3(path, SHIM_SCRIPT, "utf-8");
|
|
3693
3722
|
chmodSync2(path, SHIM_MODE);
|
|
3694
3723
|
}
|
|
3695
3724
|
function removeShim() {
|
|
3696
|
-
|
|
3725
|
+
rmSync2(shimPath(), { force: true });
|
|
3697
3726
|
}
|
|
3698
3727
|
function autoAcceptOf(raw) {
|
|
3699
3728
|
if (raw.trim().length === 0) {
|
|
@@ -3801,9 +3830,9 @@ function registerHermesCommands(program2) {
|
|
|
3801
3830
|
|
|
3802
3831
|
// src/commands/hooks.ts
|
|
3803
3832
|
import { execFileSync } from "child_process";
|
|
3804
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
3833
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
3805
3834
|
import { homedir as homedir6 } from "os";
|
|
3806
|
-
import { dirname as
|
|
3835
|
+
import { dirname as dirname4, join as join6, resolve as resolve2 } from "path";
|
|
3807
3836
|
import { Option } from "commander";
|
|
3808
3837
|
var PRE_COMMIT = { hookName: "pre-commit", binName: "prim-pre-commit" };
|
|
3809
3838
|
var POST_COMMIT = { hookName: "post-commit", binName: "prim-post-commit" };
|
|
@@ -3856,12 +3885,12 @@ function mergePrimBlock(hookPath, block, binName) {
|
|
|
3856
3885
|
const existing = readFileSync6(hookPath, "utf-8");
|
|
3857
3886
|
if (containsPrimHook(existing, binName)) return false;
|
|
3858
3887
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
3859
|
-
|
|
3888
|
+
writeFileSync4(hookPath, `${existing}${separator}${block}
|
|
3860
3889
|
`, { mode: 493 });
|
|
3861
3890
|
return true;
|
|
3862
3891
|
}
|
|
3863
|
-
|
|
3864
|
-
|
|
3892
|
+
mkdirSync5(dirname4(hookPath), { recursive: true });
|
|
3893
|
+
writeFileSync4(hookPath, `#!/bin/sh
|
|
3865
3894
|
# ${PRIM_CREATED_MARK}
|
|
3866
3895
|
|
|
3867
3896
|
${block}
|
|
@@ -3924,7 +3953,7 @@ function installToDotGit(gitRoot, spec = PRE_COMMIT) {
|
|
|
3924
3953
|
const hooksDir = resolve2(gitRoot, ".git", "hooks");
|
|
3925
3954
|
const hookPath = resolve2(hooksDir, spec.hookName);
|
|
3926
3955
|
if (!existsSync7(hooksDir)) {
|
|
3927
|
-
|
|
3956
|
+
mkdirSync5(hooksDir, { recursive: true });
|
|
3928
3957
|
}
|
|
3929
3958
|
if (existsSync7(hookPath)) {
|
|
3930
3959
|
const existing = readFileSync6(hookPath, "utf-8");
|
|
@@ -3936,7 +3965,7 @@ function installToDotGit(gitRoot, spec = PRE_COMMIT) {
|
|
|
3936
3965
|
console.log("To replace it, run: prim hooks uninstall && prim hooks install");
|
|
3937
3966
|
return;
|
|
3938
3967
|
}
|
|
3939
|
-
|
|
3968
|
+
writeFileSync4(hookPath, dotGitScript(spec), { mode: 493 });
|
|
3940
3969
|
console.log(`Installed ${spec.hookName} hook at ${hookPath}`);
|
|
3941
3970
|
}
|
|
3942
3971
|
var PRIM_GIT_HOOKS_DIR = join6(homedir6(), ".config", "prim", "git-hooks");
|
|
@@ -4007,15 +4036,15 @@ function ownedHookNames() {
|
|
|
4007
4036
|
}
|
|
4008
4037
|
function writeOwnHooks() {
|
|
4009
4038
|
if (!existsSync7(PRIM_GIT_HOOKS_DIR)) {
|
|
4010
|
-
|
|
4039
|
+
mkdirSync5(PRIM_GIT_HOOKS_DIR, { recursive: true });
|
|
4011
4040
|
}
|
|
4012
4041
|
for (const spec of HOOKS) {
|
|
4013
|
-
|
|
4042
|
+
writeFileSync4(resolve2(PRIM_GIT_HOOKS_DIR, spec.hookName), globalHookScript(spec), {
|
|
4014
4043
|
mode: 493
|
|
4015
4044
|
});
|
|
4016
4045
|
}
|
|
4017
4046
|
for (const name of PASSTHROUGH_HOOKS) {
|
|
4018
|
-
|
|
4047
|
+
writeFileSync4(resolve2(PRIM_GIT_HOOKS_DIR, name), passThroughScript(name), { mode: 493 });
|
|
4019
4048
|
}
|
|
4020
4049
|
}
|
|
4021
4050
|
function appendPrimBlock(hookPath, spec) {
|
|
@@ -4035,7 +4064,7 @@ function stripPrimBlock(hookPath, spec) {
|
|
|
4035
4064
|
unlinkSync2(hookPath);
|
|
4036
4065
|
return;
|
|
4037
4066
|
}
|
|
4038
|
-
|
|
4067
|
+
writeFileSync4(hookPath, out, { mode: 493 });
|
|
4039
4068
|
}
|
|
4040
4069
|
function installGlobalHooks(opts = {}) {
|
|
4041
4070
|
const global = gitConfigGet("--global");
|
|
@@ -4183,7 +4212,7 @@ function registerHooksCommands(program2) {
|
|
|
4183
4212
|
}
|
|
4184
4213
|
|
|
4185
4214
|
// src/commands/moves.ts
|
|
4186
|
-
import { existsSync as existsSync8, mkdirSync as
|
|
4215
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
4187
4216
|
import { join as join7 } from "path";
|
|
4188
4217
|
|
|
4189
4218
|
// src/flusher.ts
|
|
@@ -4348,7 +4377,7 @@ var EVENT_COL_WIDTH = 20;
|
|
|
4348
4377
|
var BUCKET_COL_WIDTH = 20;
|
|
4349
4378
|
var TAIL_BUCKET_COL_WIDTH = 12;
|
|
4350
4379
|
var DIR_MODE = 448;
|
|
4351
|
-
var
|
|
4380
|
+
var FILE_MODE = 384;
|
|
4352
4381
|
var WORKSPACE_FILE = ".prim/workspace.json";
|
|
4353
4382
|
function registerMovesCommands(program2) {
|
|
4354
4383
|
const moves = program2.command("moves").description("Decision Event Pipeline \u2014 local journal");
|
|
@@ -4414,11 +4443,11 @@ function registerMovesCommands(program2) {
|
|
|
4414
4443
|
moves.command("bind").description("Pin the current directory to an org via .prim/workspace.json").requiredOption("--orgId <orgId>", "Convex organization id").action((opts) => {
|
|
4415
4444
|
const dir = join7(process.cwd(), ".prim");
|
|
4416
4445
|
if (!existsSync8(dir)) {
|
|
4417
|
-
|
|
4446
|
+
mkdirSync6(dir, { recursive: true, mode: DIR_MODE });
|
|
4418
4447
|
}
|
|
4419
4448
|
const file = join7(process.cwd(), WORKSPACE_FILE);
|
|
4420
|
-
|
|
4421
|
-
mode:
|
|
4449
|
+
writeFileSync5(file, JSON.stringify({ orgId: opts.orgId, boundAt: Date.now() }, null, 2), {
|
|
4450
|
+
mode: FILE_MODE
|
|
4422
4451
|
});
|
|
4423
4452
|
console.log(`[prim] bound ${process.cwd()} to org ${opts.orgId}`);
|
|
4424
4453
|
});
|
|
@@ -4518,18 +4547,18 @@ function registerReconcileCommands(program2) {
|
|
|
4518
4547
|
// src/commands/session.ts
|
|
4519
4548
|
import {
|
|
4520
4549
|
existsSync as existsSync9,
|
|
4521
|
-
mkdirSync as
|
|
4550
|
+
mkdirSync as mkdirSync7,
|
|
4522
4551
|
readFileSync as readFileSync7,
|
|
4523
4552
|
readdirSync,
|
|
4524
4553
|
unlinkSync as unlinkSync5,
|
|
4525
|
-
writeFileSync as
|
|
4554
|
+
writeFileSync as writeFileSync6
|
|
4526
4555
|
} from "fs";
|
|
4527
4556
|
import { join as join8 } from "path";
|
|
4528
4557
|
var DIR_MODE2 = 448;
|
|
4529
|
-
var
|
|
4558
|
+
var FILE_MODE2 = 384;
|
|
4530
4559
|
function ensureDir() {
|
|
4531
4560
|
if (!existsSync9(SESSIONS_DIR)) {
|
|
4532
|
-
|
|
4561
|
+
mkdirSync7(SESSIONS_DIR, { recursive: true, mode: DIR_MODE2 });
|
|
4533
4562
|
}
|
|
4534
4563
|
}
|
|
4535
4564
|
function markerPath(sessionId) {
|
|
@@ -4543,8 +4572,8 @@ function registerSessionCommands(program2) {
|
|
|
4543
4572
|
orgId: opts.orgId,
|
|
4544
4573
|
startedAt: Date.now()
|
|
4545
4574
|
};
|
|
4546
|
-
|
|
4547
|
-
mode:
|
|
4575
|
+
writeFileSync6(markerPath(sessionId), JSON.stringify(marker, null, 2), {
|
|
4576
|
+
mode: FILE_MODE2
|
|
4548
4577
|
});
|
|
4549
4578
|
console.log(`[prim] session ${sessionId} bound to org ${opts.orgId}`);
|
|
4550
4579
|
});
|
|
@@ -4684,7 +4713,17 @@ function resolveAgent(agentFlag, env) {
|
|
|
4684
4713
|
}
|
|
4685
4714
|
return { agent: detectAgent(env), detected: true };
|
|
4686
4715
|
}
|
|
4687
|
-
function
|
|
4716
|
+
function parseSetupAuthStatus(result) {
|
|
4717
|
+
try {
|
|
4718
|
+
const parsed = JSON.parse(result.stdout || "{}");
|
|
4719
|
+
if (parsed.status === "valid" || parsed.authenticated === true) return "valid";
|
|
4720
|
+
if (parsed.status === "unreachable") return "unreachable";
|
|
4721
|
+
if (parsed.status === "invalid") return "invalid";
|
|
4722
|
+
} catch {
|
|
4723
|
+
}
|
|
4724
|
+
return result.code === EXIT_USAGE3 ? "unreachable" : "invalid";
|
|
4725
|
+
}
|
|
4726
|
+
function registerSetupCommand(program2, dependencies = {}) {
|
|
4688
4727
|
program2.command("setup").description(
|
|
4689
4728
|
"Install everything in one shot (auth, session + git hooks, daemon, skill, welcome)"
|
|
4690
4729
|
).option("--agent <agent>", "claude, codex, or hermes (auto-detected when omitted)").option(
|
|
@@ -4701,50 +4740,64 @@ function registerSetupCommand(program2) {
|
|
|
4701
4740
|
`[prim] unknown --agent "${agentInput}" (expected claude, codex, or hermes)
|
|
4702
4741
|
`
|
|
4703
4742
|
);
|
|
4704
|
-
process.exit(EXIT_USAGE3);
|
|
4743
|
+
(dependencies.exit ?? process.exit)(EXIT_USAGE3);
|
|
4744
|
+
return;
|
|
4705
4745
|
}
|
|
4706
4746
|
if (opts.scope !== "project" && opts.scope !== "user") {
|
|
4707
4747
|
process.stderr.write(`[prim] unknown --scope "${opts.scope}" (expected project or user)
|
|
4708
4748
|
`);
|
|
4709
|
-
process.exit(EXIT_USAGE3);
|
|
4749
|
+
(dependencies.exit ?? process.exit)(EXIT_USAGE3);
|
|
4750
|
+
return;
|
|
4710
4751
|
}
|
|
4711
4752
|
const agent = agentInput;
|
|
4712
4753
|
const scope = opts.scope;
|
|
4713
4754
|
const self = process.argv[1];
|
|
4714
|
-
const run = (args, capture = false) => {
|
|
4755
|
+
const run = dependencies.run ?? ((args, capture = false) => {
|
|
4715
4756
|
const r = spawnSync2(process.execPath, [self, ...args], {
|
|
4716
4757
|
stdio: capture ? ["inherit", "pipe", "ignore"] : "inherit",
|
|
4717
4758
|
encoding: "utf-8"
|
|
4718
4759
|
});
|
|
4719
4760
|
return { code: r.status ?? 1, stdout: capture ? r.stdout ?? "" : "" };
|
|
4720
|
-
};
|
|
4761
|
+
});
|
|
4721
4762
|
const results = {};
|
|
4722
|
-
const note = (msg) => {
|
|
4763
|
+
const note = dependencies.note ?? ((msg) => {
|
|
4723
4764
|
process.stderr.write(`[prim] ${msg}
|
|
4724
4765
|
`);
|
|
4725
|
-
};
|
|
4726
|
-
const
|
|
4727
|
-
try {
|
|
4728
|
-
return JSON.parse(json || "{}").authenticated === true;
|
|
4729
|
-
} catch {
|
|
4730
|
-
return false;
|
|
4731
|
-
}
|
|
4732
|
-
};
|
|
4766
|
+
});
|
|
4767
|
+
const exit = dependencies.exit ?? process.exit;
|
|
4733
4768
|
if (detected && agent !== "claude") {
|
|
4734
4769
|
note(`agent \xB7 detected ${agent} session (override with --agent <agent>)`);
|
|
4735
4770
|
}
|
|
4771
|
+
let authStatus = parseSetupAuthStatus(run(["auth", "status", "--json"], true));
|
|
4772
|
+
if (authStatus === "unreachable") {
|
|
4773
|
+
note("auth \xB7 verification unavailable; no setup changes were made");
|
|
4774
|
+
exit(EXIT_USAGE3);
|
|
4775
|
+
return;
|
|
4776
|
+
}
|
|
4777
|
+
if (authStatus === "invalid") {
|
|
4778
|
+
note("auth \xB7 opening browser to authenticate\u2026");
|
|
4779
|
+
const login = run(["auth", "login"]);
|
|
4780
|
+
if (login.code !== 0) {
|
|
4781
|
+
note("auth \xB7 login failed; no integration changes were made");
|
|
4782
|
+
exit(EXIT_INCOMPLETE);
|
|
4783
|
+
return;
|
|
4784
|
+
}
|
|
4785
|
+
authStatus = parseSetupAuthStatus(run(["auth", "status", "--json"], true));
|
|
4786
|
+
if (authStatus !== "valid") {
|
|
4787
|
+
note(
|
|
4788
|
+
authStatus === "unreachable" ? "auth \xB7 login completed but verification is unavailable; no integration changes were made" : "auth \xB7 login did not produce valid credentials; no integration changes were made"
|
|
4789
|
+
);
|
|
4790
|
+
exit(authStatus === "unreachable" ? EXIT_USAGE3 : EXIT_INCOMPLETE);
|
|
4791
|
+
return;
|
|
4792
|
+
}
|
|
4793
|
+
} else {
|
|
4794
|
+
note("auth \xB7 already authenticated");
|
|
4795
|
+
}
|
|
4796
|
+
results.auth = "ok";
|
|
4736
4797
|
if (agent === "claude") {
|
|
4737
4798
|
note("pre-authorize \xB7 writing prim allow-rule (user scope)\u2026");
|
|
4738
4799
|
results.preauth = run(["claude", "preauth", "--scope", "user"]).code === 0 ? "ok" : "skipped";
|
|
4739
4800
|
}
|
|
4740
|
-
if (isAuthed(run(["auth", "status", "--json"], true).stdout)) {
|
|
4741
|
-
note("auth \xB7 already authenticated");
|
|
4742
|
-
results.auth = "ok";
|
|
4743
|
-
} else {
|
|
4744
|
-
note("auth \xB7 opening browser to authenticate\u2026");
|
|
4745
|
-
run(["auth", "login"]);
|
|
4746
|
-
results.auth = isAuthed(run(["auth", "status", "--json"], true).stdout) ? "ok" : "failed";
|
|
4747
|
-
}
|
|
4748
4801
|
for (const step of planSetupSteps({ agent, daemon: opts.daemon, scope })) {
|
|
4749
4802
|
note(`${step.label} \xB7 installing\u2026`);
|
|
4750
4803
|
const { code } = run(step.args);
|
|
@@ -4769,7 +4822,7 @@ function registerSetupCommand(program2) {
|
|
|
4769
4822
|
note(
|
|
4770
4823
|
`setup ${failed.length === 0 ? "complete" : `incomplete (failed: ${failed.join(", ")})`} \u2014 ${trail}`
|
|
4771
4824
|
);
|
|
4772
|
-
|
|
4825
|
+
exit(failed.length === 0 ? 0 : EXIT_INCOMPLETE);
|
|
4773
4826
|
});
|
|
4774
4827
|
}
|
|
4775
4828
|
|
|
@@ -4781,19 +4834,19 @@ import {
|
|
|
4781
4834
|
openSync as openSync4,
|
|
4782
4835
|
readFileSync as readFileSync10,
|
|
4783
4836
|
renameSync as renameSync5,
|
|
4784
|
-
writeFileSync as
|
|
4837
|
+
writeFileSync as writeFileSync7
|
|
4785
4838
|
} from "fs";
|
|
4786
4839
|
import { homedir as homedir8 } from "os";
|
|
4787
|
-
import { dirname as
|
|
4840
|
+
import { dirname as dirname6, join as join11, resolve as resolve4 } from "path";
|
|
4788
4841
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4789
4842
|
import { createPatch } from "diff";
|
|
4790
4843
|
|
|
4791
4844
|
// src/commands/claude-plugin.ts
|
|
4792
|
-
import { existsSync as existsSync11, mkdirSync as
|
|
4845
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync9, readdirSync as readdirSync2, rmSync as rmSync3, rmdirSync } from "fs";
|
|
4793
4846
|
import { homedir as homedir7 } from "os";
|
|
4794
|
-
import { dirname as
|
|
4847
|
+
import { dirname as dirname5, join as join10, resolve as resolve3 } from "path";
|
|
4795
4848
|
import { fileURLToPath } from "url";
|
|
4796
|
-
var __dirname =
|
|
4849
|
+
var __dirname = dirname5(fileURLToPath(import.meta.url));
|
|
4797
4850
|
var PLUGIN_DESCRIPTION = "Primitive decision-graph guidance for the prim CLI \u2014 a model-invoked skill installed by prim skill install.";
|
|
4798
4851
|
function resolvePluginDir(cwd, scope) {
|
|
4799
4852
|
if (scope && scope !== "user" && scope !== "project") {
|
|
@@ -4805,10 +4858,10 @@ function resolvePluginDir(cwd, scope) {
|
|
|
4805
4858
|
}
|
|
4806
4859
|
function packageVersion() {
|
|
4807
4860
|
let dir = __dirname;
|
|
4808
|
-
while (dir !==
|
|
4861
|
+
while (dir !== dirname5(dir)) {
|
|
4809
4862
|
const p = resolve3(dir, "package.json");
|
|
4810
4863
|
if (existsSync11(p)) return JSON.parse(readFileSync9(p, "utf-8")).version;
|
|
4811
|
-
dir =
|
|
4864
|
+
dir = dirname5(dir);
|
|
4812
4865
|
}
|
|
4813
4866
|
return "0.0.0";
|
|
4814
4867
|
}
|
|
@@ -4842,7 +4895,7 @@ function installClaudePlugin(cwd, opts) {
|
|
|
4842
4895
|
console.log(`Would write plugin to ${dir} (.claude-plugin/plugin.json + SKILL.md)`);
|
|
4843
4896
|
return 0;
|
|
4844
4897
|
}
|
|
4845
|
-
|
|
4898
|
+
mkdirSync8(join10(dir, ".claude-plugin"), { recursive: true });
|
|
4846
4899
|
atomicWrite3(manifestPath, manifest);
|
|
4847
4900
|
atomicWrite3(skillPath, skill);
|
|
4848
4901
|
console.log(`Installed prim skill plugin at ${dir}`);
|
|
@@ -4860,8 +4913,8 @@ function uninstallClaudePlugin(cwd, opts) {
|
|
|
4860
4913
|
console.log(`prim skill plugin not present at ${dir}`);
|
|
4861
4914
|
return 0;
|
|
4862
4915
|
}
|
|
4863
|
-
|
|
4864
|
-
|
|
4916
|
+
rmSync3(manifestPath, { force: true });
|
|
4917
|
+
rmSync3(skillPath, { force: true });
|
|
4865
4918
|
removeDirIfEmpty(join10(dir, ".claude-plugin"));
|
|
4866
4919
|
removeDirIfEmpty(dir);
|
|
4867
4920
|
console.log(`Removed prim skill plugin from ${dir}`);
|
|
@@ -4885,7 +4938,7 @@ function statusClaudePlugin(cwd, opts) {
|
|
|
4885
4938
|
}
|
|
4886
4939
|
|
|
4887
4940
|
// src/commands/skill.ts
|
|
4888
|
-
var __dirname2 =
|
|
4941
|
+
var __dirname2 = dirname6(fileURLToPath2(import.meta.url));
|
|
4889
4942
|
var SKILL_BEGIN = "<!-- BEGIN PRIM SKILL v1 -->";
|
|
4890
4943
|
var SKILL_END = "<!-- END PRIM SKILL v1 -->";
|
|
4891
4944
|
var TARGET_CANDIDATES = [
|
|
@@ -4912,10 +4965,10 @@ function userTargetFor(agent) {
|
|
|
4912
4965
|
}
|
|
4913
4966
|
function loadSkill() {
|
|
4914
4967
|
let dir = __dirname2;
|
|
4915
|
-
while (dir !==
|
|
4968
|
+
while (dir !== dirname6(dir)) {
|
|
4916
4969
|
const p = resolve4(dir, "SKILL.md");
|
|
4917
4970
|
if (existsSync12(p)) return readFileSync10(p, "utf-8");
|
|
4918
|
-
dir =
|
|
4971
|
+
dir = dirname6(dir);
|
|
4919
4972
|
}
|
|
4920
4973
|
throw new Error("SKILL.md not found in package");
|
|
4921
4974
|
}
|
|
@@ -4948,7 +5001,7 @@ function removeBlock(existing) {
|
|
|
4948
5001
|
}
|
|
4949
5002
|
function atomicWrite3(target, content) {
|
|
4950
5003
|
const tmp = `${target}.tmp`;
|
|
4951
|
-
|
|
5004
|
+
writeFileSync7(tmp, content);
|
|
4952
5005
|
const fd = openSync4(tmp, "r+");
|
|
4953
5006
|
try {
|
|
4954
5007
|
fsyncSync3(fd);
|
|
@@ -5069,13 +5122,13 @@ function registerSkillCommands(program2) {
|
|
|
5069
5122
|
|
|
5070
5123
|
// src/commands/statusline.ts
|
|
5071
5124
|
import { readFileSync as readFileSync11 } from "fs";
|
|
5072
|
-
import { dirname as
|
|
5125
|
+
import { dirname as dirname7, resolve as resolve5 } from "path";
|
|
5073
5126
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5074
5127
|
var STATUSLINE_TIMEOUT_MS = 200;
|
|
5075
5128
|
var STATUSLINE_NAME_CAP = 3;
|
|
5076
5129
|
function readPackageVersion() {
|
|
5077
5130
|
try {
|
|
5078
|
-
const here =
|
|
5131
|
+
const here = dirname7(fileURLToPath3(import.meta.url));
|
|
5079
5132
|
const candidates = [
|
|
5080
5133
|
// The supervised runtime stages a tiny manifest beside the standalone
|
|
5081
5134
|
// statusline bundle, so it remains versioned without loading the package.
|
|
@@ -5268,7 +5321,7 @@ function registerWelcomeCommand(program2, deps = { getClient }) {
|
|
|
5268
5321
|
}
|
|
5269
5322
|
|
|
5270
5323
|
// src/index.ts
|
|
5271
|
-
var __dirname3 =
|
|
5324
|
+
var __dirname3 = dirname8(fileURLToPath4(import.meta.url));
|
|
5272
5325
|
var pkg = JSON.parse(readFileSync12(resolve6(__dirname3, "../package.json"), "utf-8"));
|
|
5273
5326
|
updateNotifier({ pkg }).notify();
|
|
5274
5327
|
var program = new Command();
|