facult 2.13.2 → 2.13.3
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 +5 -0
- package/docs/reference.md +4 -0
- package/package.json +1 -1
- package/src/self-update.ts +188 -21
package/README.md
CHANGED
|
@@ -100,6 +100,11 @@ fclt self-update
|
|
|
100
100
|
fclt self-update --version 2.12.0
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
`self-update` follows the active install mode. It updates release-script binaries
|
|
104
|
+
directly, npm/Bun global installs through their package manager, and
|
|
105
|
+
mise-managed npm installs with `mise use -g --pin npm:facult@<version>`, then
|
|
106
|
+
verifies the active `fclt --version`.
|
|
107
|
+
|
|
103
108
|
## Quick start
|
|
104
109
|
|
|
105
110
|
### 1. Inspect existing AI state
|
package/docs/reference.md
CHANGED
|
@@ -108,6 +108,10 @@ fclt audit [--non-interactive]
|
|
|
108
108
|
fclt self-update
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
`self-update` detects release-script, npm/Bun, and mise-managed npm installs.
|
|
112
|
+
For mise installs it updates the global `npm:facult` pin and verifies the
|
|
113
|
+
resolved `fclt` version through mise.
|
|
114
|
+
|
|
111
115
|
Use `--strict-source-trust` when installing or updating remote capability from catalogs.
|
|
112
116
|
|
|
113
117
|
## Root Selection
|
package/package.json
CHANGED
package/src/self-update.ts
CHANGED
|
@@ -19,6 +19,7 @@ type InstallMethod =
|
|
|
19
19
|
| "script-bin"
|
|
20
20
|
| "release-script"
|
|
21
21
|
| "npm-binary-cache"
|
|
22
|
+
| "mise-npm"
|
|
22
23
|
| "unknown";
|
|
23
24
|
|
|
24
25
|
interface InstallState {
|
|
@@ -113,21 +114,27 @@ export function detectInstallMethod(
|
|
|
113
114
|
if (envMethod === "script-dev" || envMethod === "script-bin") {
|
|
114
115
|
return envMethod;
|
|
115
116
|
}
|
|
116
|
-
if (envMethod === "npm-binary-cache") {
|
|
117
|
-
return
|
|
117
|
+
if (envMethod === "npm-binary-cache" || envMethod === "mise-npm") {
|
|
118
|
+
return envMethod;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const exec = context.executablePath ?? process.execPath;
|
|
122
|
+
const home = context.homeDir ?? homedir();
|
|
123
|
+
if (looksLikeMiseNpmFacultExecutable(exec)) {
|
|
124
|
+
return "mise-npm";
|
|
118
125
|
}
|
|
126
|
+
|
|
119
127
|
const raw = state?.method?.trim();
|
|
120
128
|
if (
|
|
121
129
|
raw === "script-dev" ||
|
|
122
130
|
raw === "script-bin" ||
|
|
123
131
|
raw === "release-script" ||
|
|
124
|
-
raw === "npm-binary-cache"
|
|
132
|
+
raw === "npm-binary-cache" ||
|
|
133
|
+
raw === "mise-npm"
|
|
125
134
|
) {
|
|
126
135
|
return raw;
|
|
127
136
|
}
|
|
128
137
|
|
|
129
|
-
const exec = context.executablePath ?? process.execPath;
|
|
130
|
-
const home = context.homeDir ?? homedir();
|
|
131
138
|
const facultBins = [
|
|
132
139
|
join(preferredGlobalFacultStateDir(home), "bin"),
|
|
133
140
|
join(legacyExternalFacultStateDir(home), "bin"),
|
|
@@ -145,6 +152,68 @@ export function detectInstallMethod(
|
|
|
145
152
|
return "unknown";
|
|
146
153
|
}
|
|
147
154
|
|
|
155
|
+
async function detectActiveInstallMethod(
|
|
156
|
+
state: InstallState | null
|
|
157
|
+
): Promise<InstallMethod> {
|
|
158
|
+
const method = detectInstallMethod(state);
|
|
159
|
+
if (method !== "npm-binary-cache" && method !== "unknown") {
|
|
160
|
+
return method;
|
|
161
|
+
}
|
|
162
|
+
if (await activeFcltUsesMiseNpmFacult()) {
|
|
163
|
+
return "mise-npm";
|
|
164
|
+
}
|
|
165
|
+
return method;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function looksLikeMiseNpmFacultExecutable(executablePath: string): boolean {
|
|
169
|
+
const normalized = executablePath.split("\\").join(sep);
|
|
170
|
+
return (
|
|
171
|
+
normalized.includes(`${sep}mise${sep}installs${sep}npm-facult${sep}`) &&
|
|
172
|
+
CLI_BASENAME_PATTERN.test(basename(normalized))
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function looksLikeMiseShim(
|
|
177
|
+
pathValue: string | null | undefined
|
|
178
|
+
): boolean {
|
|
179
|
+
if (!pathValue) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
const normalized = pathValue.split("\\").join(sep);
|
|
183
|
+
return (
|
|
184
|
+
normalized.includes(`${sep}mise${sep}shims${sep}`) &&
|
|
185
|
+
CLI_BASENAME_PATTERN.test(basename(normalized))
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function activeFcltUsesMiseNpmFacult(): Promise<boolean> {
|
|
190
|
+
if (!Bun.which("mise")) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
const fcltPath = Bun.which("fclt");
|
|
194
|
+
if (looksLikeMiseNpmFacultExecutable(fcltPath ?? "")) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
if (!looksLikeMiseShim(fcltPath)) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
const proc = Bun.spawn({
|
|
201
|
+
cmd: ["mise", "which", "fclt"],
|
|
202
|
+
stdin: "ignore",
|
|
203
|
+
stdout: "pipe",
|
|
204
|
+
stderr: "ignore",
|
|
205
|
+
env: process.env,
|
|
206
|
+
});
|
|
207
|
+
const [stdout, code] = await Promise.all([
|
|
208
|
+
new Response(proc.stdout).text(),
|
|
209
|
+
proc.exited,
|
|
210
|
+
]);
|
|
211
|
+
if (code !== 0) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
return looksLikeMiseNpmFacultExecutable(stdout.trim());
|
|
215
|
+
}
|
|
216
|
+
|
|
148
217
|
function resolvePlatformTarget(): {
|
|
149
218
|
platform: string;
|
|
150
219
|
arch: string;
|
|
@@ -284,14 +353,17 @@ async function selfUpdateBinary(args: {
|
|
|
284
353
|
console.log(`Path: ${binaryPath}`);
|
|
285
354
|
}
|
|
286
355
|
|
|
287
|
-
type PackageManager = "npm" | "bun";
|
|
356
|
+
type PackageManager = "npm" | "bun" | "mise";
|
|
288
357
|
|
|
289
358
|
function chooseGlobalPackageManager(preferred?: string): PackageManager {
|
|
290
359
|
const forced = process.env.FACULT_INSTALL_PM?.trim();
|
|
291
|
-
if (forced === "npm" || forced === "bun") {
|
|
360
|
+
if (forced === "npm" || forced === "bun" || forced === "mise") {
|
|
292
361
|
return forced;
|
|
293
362
|
}
|
|
294
363
|
|
|
364
|
+
if (preferred === "mise" && Bun.which("mise")) {
|
|
365
|
+
return "mise";
|
|
366
|
+
}
|
|
295
367
|
if (preferred === "npm" && Bun.which("npm")) {
|
|
296
368
|
return "npm";
|
|
297
369
|
}
|
|
@@ -305,25 +377,58 @@ function chooseGlobalPackageManager(preferred?: string): PackageManager {
|
|
|
305
377
|
if (Bun.which("bun")) {
|
|
306
378
|
return "bun";
|
|
307
379
|
}
|
|
380
|
+
if (Bun.which("mise")) {
|
|
381
|
+
return "mise";
|
|
382
|
+
}
|
|
308
383
|
return "npm";
|
|
309
384
|
}
|
|
310
385
|
|
|
386
|
+
export function buildPackageManagerUpdateCommand(args: {
|
|
387
|
+
packageManager: PackageManager;
|
|
388
|
+
version: string;
|
|
389
|
+
}): string[] {
|
|
390
|
+
const installSpec = `${PACKAGE_NAME}@${args.version}`;
|
|
391
|
+
if (args.packageManager === "npm") {
|
|
392
|
+
return ["npm", "install", "-g", installSpec];
|
|
393
|
+
}
|
|
394
|
+
if (args.packageManager === "bun") {
|
|
395
|
+
return ["bun", "add", "-g", installSpec];
|
|
396
|
+
}
|
|
397
|
+
return ["mise", "use", "-g", "--pin", `npm:${PACKAGE_NAME}@${args.version}`];
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async function resolvePackageTargetVersion(requestedVersion?: string): Promise<{
|
|
401
|
+
version: string;
|
|
402
|
+
resolvedFromLatest: boolean;
|
|
403
|
+
}> {
|
|
404
|
+
if (requestedVersion && requestedVersion !== "latest") {
|
|
405
|
+
return {
|
|
406
|
+
version: stripTagPrefix(requestedVersion),
|
|
407
|
+
resolvedFromLatest: false,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
const tag = await resolveLatestTag();
|
|
411
|
+
return { version: stripTagPrefix(tag), resolvedFromLatest: true };
|
|
412
|
+
}
|
|
413
|
+
|
|
311
414
|
async function selfUpdateViaPackageManager(args: {
|
|
312
415
|
requestedVersion?: string;
|
|
313
416
|
dryRun: boolean;
|
|
314
417
|
preferredPackageManager?: string;
|
|
418
|
+
method?: InstallMethod;
|
|
315
419
|
}) {
|
|
316
|
-
const pm = chooseGlobalPackageManager(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
420
|
+
const pm = chooseGlobalPackageManager(
|
|
421
|
+
args.method === "mise-npm" ? "mise" : args.preferredPackageManager
|
|
422
|
+
);
|
|
423
|
+
const target =
|
|
424
|
+
args.dryRun &&
|
|
425
|
+
(!args.requestedVersion || args.requestedVersion === "latest")
|
|
426
|
+
? { version: "latest", resolvedFromLatest: false }
|
|
427
|
+
: await resolvePackageTargetVersion(args.requestedVersion);
|
|
428
|
+
const cmd = buildPackageManagerUpdateCommand({
|
|
429
|
+
packageManager: pm,
|
|
430
|
+
version: target.version,
|
|
431
|
+
});
|
|
327
432
|
|
|
328
433
|
if (args.dryRun) {
|
|
329
434
|
console.log(`[dry-run] Would run: ${cmd.join(" ")}`);
|
|
@@ -341,7 +446,68 @@ async function selfUpdateViaPackageManager(args: {
|
|
|
341
446
|
if (code !== 0) {
|
|
342
447
|
throw new Error(`Self-update failed via ${pm} (exit ${code}).`);
|
|
343
448
|
}
|
|
344
|
-
|
|
449
|
+
if (pm === "mise") {
|
|
450
|
+
await runBestEffort(["mise", "reshim", `npm:${PACKAGE_NAME}`]);
|
|
451
|
+
}
|
|
452
|
+
await assertActiveFcltVersion(target.version, pm);
|
|
453
|
+
console.log(`Updated fclt via ${pm}: ${PACKAGE_NAME}@${target.version}`);
|
|
454
|
+
if (target.resolvedFromLatest) {
|
|
455
|
+
console.log(`Resolved latest release to ${target.version}`);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
async function runBestEffort(cmd: string[]): Promise<void> {
|
|
460
|
+
const proc = Bun.spawn({
|
|
461
|
+
cmd,
|
|
462
|
+
stdin: "ignore",
|
|
463
|
+
stdout: "ignore",
|
|
464
|
+
stderr: "ignore",
|
|
465
|
+
env: process.env,
|
|
466
|
+
});
|
|
467
|
+
await proc.exited;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async function assertActiveFcltVersion(
|
|
471
|
+
expectedVersion: string,
|
|
472
|
+
packageManager: PackageManager
|
|
473
|
+
): Promise<void> {
|
|
474
|
+
const cmd =
|
|
475
|
+
packageManager === "mise"
|
|
476
|
+
? [
|
|
477
|
+
"mise",
|
|
478
|
+
"exec",
|
|
479
|
+
`npm:${PACKAGE_NAME}@${expectedVersion}`,
|
|
480
|
+
"--",
|
|
481
|
+
"fclt",
|
|
482
|
+
"--version",
|
|
483
|
+
]
|
|
484
|
+
: ["fclt", "--version"];
|
|
485
|
+
const proc = Bun.spawn({
|
|
486
|
+
cmd,
|
|
487
|
+
stdin: "ignore",
|
|
488
|
+
stdout: "pipe",
|
|
489
|
+
stderr: "pipe",
|
|
490
|
+
env: process.env,
|
|
491
|
+
});
|
|
492
|
+
const [stdout, stderr, code] = await Promise.all([
|
|
493
|
+
new Response(proc.stdout).text(),
|
|
494
|
+
new Response(proc.stderr).text(),
|
|
495
|
+
proc.exited,
|
|
496
|
+
]);
|
|
497
|
+
const actual = stdout.trim();
|
|
498
|
+
if (code !== 0) {
|
|
499
|
+
throw new Error(
|
|
500
|
+
`Updated package, but could not verify active fclt version: ${stderr.trim()}`
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
if (actual !== expectedVersion) {
|
|
504
|
+
throw new Error(
|
|
505
|
+
[
|
|
506
|
+
`Updated package to ${expectedVersion}, but active fclt is still ${actual}.`,
|
|
507
|
+
"Your PATH may be resolving an older shim. Run `mise which fclt` or `which fclt` to inspect it.",
|
|
508
|
+
].join("\n")
|
|
509
|
+
);
|
|
510
|
+
}
|
|
345
511
|
}
|
|
346
512
|
|
|
347
513
|
async function fetchReleaseBinaryWithRetry(url: string): Promise<ArrayBuffer> {
|
|
@@ -424,7 +590,7 @@ export async function selfUpdateCommand(argv: string[]) {
|
|
|
424
590
|
|
|
425
591
|
const home = homedir();
|
|
426
592
|
const state = await loadInstallState(home);
|
|
427
|
-
const method =
|
|
593
|
+
const method = await detectActiveInstallMethod(state);
|
|
428
594
|
|
|
429
595
|
try {
|
|
430
596
|
if (method === "script-dev") {
|
|
@@ -434,9 +600,10 @@ export async function selfUpdateCommand(argv: string[]) {
|
|
|
434
600
|
);
|
|
435
601
|
return;
|
|
436
602
|
}
|
|
437
|
-
if (method === "npm-binary-cache") {
|
|
603
|
+
if (method === "npm-binary-cache" || method === "mise-npm") {
|
|
438
604
|
await selfUpdateViaPackageManager({
|
|
439
605
|
...parsed,
|
|
606
|
+
method,
|
|
440
607
|
preferredPackageManager:
|
|
441
608
|
process.env.FACULT_INSTALL_PM?.trim() || state?.packageManager,
|
|
442
609
|
});
|