rush-ai 0.18.0 → 0.19.0
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 +21 -3
- package/dist/_codex-content-loader-Q7KBWZSB.js +16 -0
- package/dist/_codex-content-loader-Q7KBWZSB.js.map +1 -0
- package/dist/chunk-2AICQRQP.js +447 -0
- package/dist/chunk-2AICQRQP.js.map +1 -0
- package/dist/{chunk-YKZIRW26.js → chunk-E6OSONYW.js} +14 -332
- package/dist/chunk-E6OSONYW.js.map +1 -0
- package/dist/chunk-OIKYNVKO.js +291 -0
- package/dist/chunk-OIKYNVKO.js.map +1 -0
- package/dist/chunk-RLKEUPBP.js +992 -0
- package/dist/chunk-RLKEUPBP.js.map +1 -0
- package/dist/chunk-T5S6NCHZ.js +48 -0
- package/dist/chunk-T5S6NCHZ.js.map +1 -0
- package/dist/chunk-X45FKY3L.js +98 -0
- package/dist/chunk-X45FKY3L.js.map +1 -0
- package/dist/index.js +691 -2010
- package/dist/index.js.map +1 -1
- package/dist/{install-IH2VPYXH.js → install-JQN4BIHX.js} +6 -3
- package/dist/{install-IH2VPYXH.js.map → install-JQN4BIHX.js.map} +1 -1
- package/dist/mcp-UZYID3GG.js +20 -0
- package/dist/mcp-UZYID3GG.js.map +1 -0
- package/dist/mirror-IHLOSPAS.js +17 -0
- package/dist/mirror-IHLOSPAS.js.map +1 -0
- package/dist/{server-Y2CXHDCK.js → server-PGXKRIPY.js} +6 -3
- package/dist/{server-Y2CXHDCK.js.map → server-PGXKRIPY.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-YKZIRW26.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
backupCodexConfig,
|
|
4
|
+
codexConfigBackupPath,
|
|
5
|
+
codexConfigTomlPath,
|
|
6
|
+
codexHomeDir,
|
|
7
|
+
codexMarketplaceDir,
|
|
8
|
+
codexMarketplaceManifestPath,
|
|
9
|
+
codexMarketplacePluginDir,
|
|
10
|
+
codexPluginManifestPath,
|
|
11
|
+
codexPluginMcpPath,
|
|
12
|
+
codexPluginSkillsDir,
|
|
13
|
+
codexPluginVersionDir,
|
|
14
|
+
downgradeCodexCatalogEntryToStub,
|
|
15
|
+
getMcpServerOwner,
|
|
16
|
+
pluginSectionKey,
|
|
17
|
+
readCodexConfig,
|
|
18
|
+
removeMarketplaceSection,
|
|
19
|
+
removeMcpServerSection,
|
|
20
|
+
removePluginEntry,
|
|
21
|
+
restoreCodexConfigFromBackup,
|
|
22
|
+
setMarketplaceSection,
|
|
23
|
+
setMcpServerSection,
|
|
24
|
+
setPluginEntry,
|
|
25
|
+
syncCodexMarketplaceMirror,
|
|
26
|
+
writeCodexConfig,
|
|
27
|
+
writeCodexMarketplacePluginMirror
|
|
28
|
+
} from "./chunk-RLKEUPBP.js";
|
|
29
|
+
import {
|
|
30
|
+
SkillAuthError,
|
|
31
|
+
fetchRushMarketplace,
|
|
32
|
+
fetchRushPlugin,
|
|
33
|
+
materializeRushPlugin,
|
|
34
|
+
pickContentLoader
|
|
35
|
+
} from "./chunk-2AICQRQP.js";
|
|
2
36
|
import {
|
|
3
37
|
consumeSSEStreamWithReconnect
|
|
4
38
|
} from "./chunk-MG4HY2PD.js";
|
|
@@ -15,9 +49,15 @@ import {
|
|
|
15
49
|
RushError,
|
|
16
50
|
TaskFailedError,
|
|
17
51
|
VERSION,
|
|
52
|
+
createClient,
|
|
53
|
+
isRushError,
|
|
54
|
+
revokeCurrentSession,
|
|
55
|
+
setVerbosity,
|
|
56
|
+
verifyCurrentAuthSession
|
|
57
|
+
} from "./chunk-E6OSONYW.js";
|
|
58
|
+
import {
|
|
18
59
|
checkAuthSourceMismatch,
|
|
19
60
|
clearAuthConfig,
|
|
20
|
-
createClient,
|
|
21
61
|
createProfile,
|
|
22
62
|
deleteProfile,
|
|
23
63
|
getActiveProfile,
|
|
@@ -29,16 +69,19 @@ import {
|
|
|
29
69
|
getProfileAuth,
|
|
30
70
|
getProfileConfig,
|
|
31
71
|
isLoggedIn,
|
|
32
|
-
isRushError,
|
|
33
72
|
listProfiles,
|
|
34
|
-
output,
|
|
35
|
-
revokeCurrentSession,
|
|
36
73
|
setActiveProfile,
|
|
37
74
|
setAuthConfig,
|
|
38
|
-
setGlobalConfig
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
75
|
+
setGlobalConfig
|
|
76
|
+
} from "./chunk-OIKYNVKO.js";
|
|
77
|
+
import {
|
|
78
|
+
output
|
|
79
|
+
} from "./chunk-T5S6NCHZ.js";
|
|
80
|
+
import {
|
|
81
|
+
defaultRushBinaryResolver,
|
|
82
|
+
normalizeClaudeMcpServers,
|
|
83
|
+
readExternalMcpServers
|
|
84
|
+
} from "./chunk-X45FKY3L.js";
|
|
42
85
|
|
|
43
86
|
// src/index.ts
|
|
44
87
|
import chalk7 from "chalk";
|
|
@@ -403,7 +446,7 @@ function getApiBaseUrl() {
|
|
|
403
446
|
async function loginViaBrowser(jsonMode) {
|
|
404
447
|
const baseUrl = getApiBaseUrl();
|
|
405
448
|
const state = randomBytes(16).toString("hex");
|
|
406
|
-
return new Promise((
|
|
449
|
+
return new Promise((resolve23, reject) => {
|
|
407
450
|
let authSaved = false;
|
|
408
451
|
const rejectLogin = (error) => {
|
|
409
452
|
clearAuthConfig();
|
|
@@ -506,7 +549,7 @@ async function loginViaBrowser(jsonMode) {
|
|
|
506
549
|
);
|
|
507
550
|
}
|
|
508
551
|
}
|
|
509
|
-
|
|
552
|
+
resolve23();
|
|
510
553
|
} catch (err) {
|
|
511
554
|
server.close();
|
|
512
555
|
const authError = err instanceof AuthError ? err : new AuthError(
|
|
@@ -2385,24 +2428,24 @@ function registerDoctorCommand(program) {
|
|
|
2385
2428
|
import { homedir as homedir5 } from "os";
|
|
2386
2429
|
|
|
2387
2430
|
// src/installers/codex/installer.ts
|
|
2388
|
-
import { constants as
|
|
2431
|
+
import { constants as fsConstants } from "fs";
|
|
2389
2432
|
import {
|
|
2390
|
-
access
|
|
2391
|
-
cp
|
|
2392
|
-
mkdir as
|
|
2393
|
-
readdir
|
|
2394
|
-
readFile as
|
|
2395
|
-
rm
|
|
2396
|
-
stat
|
|
2397
|
-
writeFile as
|
|
2433
|
+
access,
|
|
2434
|
+
cp,
|
|
2435
|
+
mkdir as mkdir2,
|
|
2436
|
+
readdir,
|
|
2437
|
+
readFile as readFile2,
|
|
2438
|
+
rm,
|
|
2439
|
+
stat,
|
|
2440
|
+
writeFile as writeFile2
|
|
2398
2441
|
} from "fs/promises";
|
|
2399
2442
|
import { homedir as homedir3 } from "os";
|
|
2400
2443
|
import {
|
|
2401
|
-
basename as
|
|
2402
|
-
dirname as
|
|
2444
|
+
basename as basename2,
|
|
2445
|
+
dirname as dirname2,
|
|
2403
2446
|
extname,
|
|
2404
2447
|
relative as pathRelative,
|
|
2405
|
-
resolve as
|
|
2448
|
+
resolve as resolve3
|
|
2406
2449
|
} from "path";
|
|
2407
2450
|
|
|
2408
2451
|
// src/installers/codex/mcp.ts
|
|
@@ -2485,1092 +2528,135 @@ function buildCodexMcpServerTomlEntry(config, options = {}) {
|
|
|
2485
2528
|
return null;
|
|
2486
2529
|
}
|
|
2487
2530
|
|
|
2488
|
-
// src/installers/codex/
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
import {
|
|
2492
|
-
access as access2,
|
|
2493
|
-
cp,
|
|
2494
|
-
mkdir as mkdir3,
|
|
2495
|
-
mkdtemp,
|
|
2496
|
-
readdir,
|
|
2497
|
-
readFile as readFile3,
|
|
2498
|
-
rename as rename2,
|
|
2499
|
-
rm as rm2,
|
|
2500
|
-
stat as stat2,
|
|
2501
|
-
writeFile as writeFile3
|
|
2502
|
-
} from "fs/promises";
|
|
2503
|
-
import { basename as basename2, dirname as dirname3, resolve as resolve3 } from "path";
|
|
2504
|
-
|
|
2505
|
-
// src/installers/codex/paths.ts
|
|
2506
|
-
import { resolve as pathResolve } from "path";
|
|
2507
|
-
function codexHomeDir(home) {
|
|
2508
|
-
return pathResolve(home, ".codex");
|
|
2509
|
-
}
|
|
2510
|
-
function codexConfigTomlPath(home) {
|
|
2511
|
-
return pathResolve(codexHomeDir(home), "config.toml");
|
|
2512
|
-
}
|
|
2513
|
-
function codexPluginsCacheDir(home) {
|
|
2514
|
-
return pathResolve(codexHomeDir(home), "plugins", "cache");
|
|
2515
|
-
}
|
|
2516
|
-
function codexPluginsMarketplacesDir(home) {
|
|
2517
|
-
return pathResolve(codexHomeDir(home), "plugins", "marketplaces");
|
|
2518
|
-
}
|
|
2519
|
-
function codexMarketplaceDir(home, marketplace) {
|
|
2520
|
-
return pathResolve(codexPluginsMarketplacesDir(home), marketplace);
|
|
2521
|
-
}
|
|
2522
|
-
function codexMarketplaceManifestPath(marketplaceDir) {
|
|
2523
|
-
return pathResolve(marketplaceDir, ".agents", "plugins", "marketplace.json");
|
|
2524
|
-
}
|
|
2525
|
-
function codexMarketplacePluginDir(marketplaceDir, pluginName) {
|
|
2526
|
-
return pathResolve(marketplaceDir, "plugins", pluginName);
|
|
2527
|
-
}
|
|
2528
|
-
function codexPluginVersionDir(home, ref, version) {
|
|
2529
|
-
return pathResolve(
|
|
2530
|
-
codexPluginsCacheDir(home),
|
|
2531
|
-
ref.marketplace,
|
|
2532
|
-
ref.name,
|
|
2533
|
-
version
|
|
2534
|
-
);
|
|
2535
|
-
}
|
|
2536
|
-
function codexPluginManifestPath(versionDir) {
|
|
2537
|
-
return pathResolve(versionDir, ".codex-plugin", "plugin.json");
|
|
2538
|
-
}
|
|
2539
|
-
function codexPluginMcpPath(versionDir) {
|
|
2540
|
-
return pathResolve(versionDir, ".mcp.json");
|
|
2541
|
-
}
|
|
2542
|
-
function codexPluginSkillsDir(versionDir) {
|
|
2543
|
-
return pathResolve(versionDir, "skills");
|
|
2544
|
-
}
|
|
2545
|
-
function codexConfigBackupPath(home, now) {
|
|
2546
|
-
const ts = formatBackupTimestamp(now);
|
|
2547
|
-
return `${codexConfigTomlPath(home)}.bak.${ts}`;
|
|
2548
|
-
}
|
|
2549
|
-
function formatBackupTimestamp(date) {
|
|
2550
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
2551
|
-
const yyyy = date.getUTCFullYear();
|
|
2552
|
-
const mm = pad(date.getUTCMonth() + 1);
|
|
2553
|
-
const dd = pad(date.getUTCDate());
|
|
2554
|
-
const hh = pad(date.getUTCHours());
|
|
2555
|
-
const mi = pad(date.getUTCMinutes());
|
|
2556
|
-
const ss = pad(date.getUTCSeconds());
|
|
2557
|
-
return `${yyyy}${mm}${dd}-${hh}${mi}${ss}`;
|
|
2558
|
-
}
|
|
2559
|
-
function pluginSectionKey(ref) {
|
|
2560
|
-
return `${ref.name}@${ref.marketplace}`;
|
|
2561
|
-
}
|
|
2562
|
-
|
|
2563
|
-
// src/installers/codex/toml.ts
|
|
2564
|
-
import { randomUUID } from "crypto";
|
|
2565
|
-
import { constants as fsConstants } from "fs";
|
|
2566
|
-
import {
|
|
2567
|
-
access,
|
|
2568
|
-
copyFile,
|
|
2569
|
-
mkdir as mkdir2,
|
|
2570
|
-
readFile as readFile2,
|
|
2571
|
-
rename,
|
|
2572
|
-
rm,
|
|
2573
|
-
stat,
|
|
2574
|
-
writeFile as writeFile2
|
|
2575
|
-
} from "fs/promises";
|
|
2576
|
-
import { dirname as dirname2 } from "path";
|
|
2577
|
-
import tomlModule from "@iarna/toml";
|
|
2578
|
-
var { parse: tomlParse, stringify: tomlStringify } = tomlModule;
|
|
2579
|
-
var CodexConfigTomlError = class extends Error {
|
|
2531
|
+
// src/installers/codex/installer.ts
|
|
2532
|
+
var CODEX_UNSUPPORTED = ["rules", "hooks"];
|
|
2533
|
+
var CodexInstallError = class extends Error {
|
|
2580
2534
|
constructor(message) {
|
|
2581
2535
|
super(message);
|
|
2582
|
-
this.name = "
|
|
2583
|
-
}
|
|
2584
|
-
};
|
|
2585
|
-
var CodexConfigTomlCorruptError = class extends CodexConfigTomlError {
|
|
2586
|
-
constructor(filePath, cause) {
|
|
2587
|
-
super(
|
|
2588
|
-
`Codex config.toml \u89E3\u6790\u5931\u8D25\uFF08${filePath}\uFF09\u3002\u8BF7\u5907\u4EFD\u540E\u624B\u5DE5\u4FEE\u590D\u3002\u539F\u56E0\uFF1A${String(
|
|
2589
|
-
cause?.message ?? cause
|
|
2590
|
-
)}`
|
|
2591
|
-
);
|
|
2592
|
-
this.filePath = filePath;
|
|
2593
|
-
this.cause = cause;
|
|
2594
|
-
this.name = "CodexConfigTomlCorruptError";
|
|
2595
|
-
}
|
|
2596
|
-
};
|
|
2597
|
-
var CodexConfigTomlConflictError = class extends CodexConfigTomlError {
|
|
2598
|
-
constructor(filePath) {
|
|
2599
|
-
super(
|
|
2600
|
-
`Codex config.toml \u5E76\u53D1\u5199\u51B2\u7A81\uFF1A\u8BFB\u53D6\u548C\u5199\u5165\u4E4B\u95F4 ${filePath} \u88AB\u5176\u4ED6\u8FDB\u7A0B\u4FEE\u6539\u3002\u8BF7\u91CD\u8BD5\u3002`
|
|
2601
|
-
);
|
|
2602
|
-
this.filePath = filePath;
|
|
2603
|
-
this.name = "CodexConfigTomlConflictError";
|
|
2536
|
+
this.name = "CodexInstallError";
|
|
2604
2537
|
}
|
|
2605
2538
|
};
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2539
|
+
var CodexInstaller = class {
|
|
2540
|
+
target = "codex";
|
|
2541
|
+
home;
|
|
2542
|
+
rushAiBinaryResolver;
|
|
2543
|
+
now;
|
|
2544
|
+
marketplaceSource;
|
|
2545
|
+
constructor(opts = {}) {
|
|
2546
|
+
this.home = opts.home ?? homedir3();
|
|
2547
|
+
this.rushAiBinaryResolver = opts.rushAiBinaryResolver ?? defaultRushAiBinaryResolver;
|
|
2548
|
+
this.now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
2549
|
+
this.marketplaceSource = opts.marketplaceSource;
|
|
2617
2550
|
}
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2551
|
+
// -------------------------------------------------------------------------
|
|
2552
|
+
// detect / isInstalled / list
|
|
2553
|
+
// -------------------------------------------------------------------------
|
|
2554
|
+
async detect() {
|
|
2555
|
+
return isDir(codexHomeDir(this.home));
|
|
2622
2556
|
}
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2557
|
+
async isInstalled(ref) {
|
|
2558
|
+
const cfgPath = codexConfigTomlPath(this.home);
|
|
2559
|
+
if (!await pathExists2(cfgPath)) return false;
|
|
2560
|
+
const { data } = await readCodexConfig(cfgPath);
|
|
2561
|
+
const plugins = data.plugins;
|
|
2562
|
+
if (!plugins || typeof plugins !== "object") return false;
|
|
2563
|
+
const key = pluginSectionKey(ref);
|
|
2564
|
+
const entry = plugins[key];
|
|
2565
|
+
if (!entry || typeof entry !== "object") return false;
|
|
2566
|
+
return entry.enabled === true;
|
|
2626
2567
|
}
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
await
|
|
2568
|
+
async list() {
|
|
2569
|
+
const cfgPath = codexConfigTomlPath(this.home);
|
|
2570
|
+
if (!await pathExists2(cfgPath)) return [];
|
|
2571
|
+
const { data } = await readCodexConfig(cfgPath);
|
|
2572
|
+
const plugins = data.plugins;
|
|
2573
|
+
if (!plugins || typeof plugins !== "object") return [];
|
|
2574
|
+
const results = [];
|
|
2575
|
+
for (const [key, raw] of Object.entries(plugins)) {
|
|
2576
|
+
if (!raw || typeof raw !== "object") continue;
|
|
2577
|
+
const entry = raw;
|
|
2578
|
+
if (entry.enabled !== true) continue;
|
|
2579
|
+
const ref = parsePluginKey(key);
|
|
2580
|
+
if (!ref) continue;
|
|
2581
|
+
const detected = await detectInstalledVersion(this.home, ref);
|
|
2582
|
+
results.push({
|
|
2583
|
+
ref,
|
|
2584
|
+
version: detected?.version ?? "unknown",
|
|
2585
|
+
installedAt: detected?.installedAt ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
2586
|
+
targets: ["codex"]
|
|
2587
|
+
});
|
|
2641
2588
|
}
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
await rm(backupPath, { force: true }).catch(() => {
|
|
2647
|
-
});
|
|
2648
|
-
}
|
|
2649
|
-
function setMarketplaceSection(config, name, entry) {
|
|
2650
|
-
const mkts = ensureObjectSection(config, "marketplaces");
|
|
2651
|
-
mkts[name] = { ...entry };
|
|
2652
|
-
}
|
|
2653
|
-
function removeMarketplaceSection(config, name) {
|
|
2654
|
-
const mkts = config.marketplaces;
|
|
2655
|
-
if (!mkts || typeof mkts !== "object") return;
|
|
2656
|
-
delete mkts[name];
|
|
2657
|
-
if (Object.keys(mkts).length === 0) {
|
|
2658
|
-
delete config.marketplaces;
|
|
2659
|
-
}
|
|
2660
|
-
}
|
|
2661
|
-
function setPluginEntry(config, key, entry) {
|
|
2662
|
-
const plugins = ensureObjectSection(config, "plugins");
|
|
2663
|
-
plugins[key] = { ...entry };
|
|
2664
|
-
}
|
|
2665
|
-
function removePluginEntry(config, key) {
|
|
2666
|
-
const plugins = config.plugins;
|
|
2667
|
-
if (!plugins || typeof plugins !== "object") return;
|
|
2668
|
-
delete plugins[key];
|
|
2669
|
-
if (Object.keys(plugins).length === 0) {
|
|
2670
|
-
delete config.plugins;
|
|
2671
|
-
}
|
|
2672
|
-
}
|
|
2673
|
-
var MCP_OWNER_MARKER_KEY = "_rush_ai_owner";
|
|
2674
|
-
function setMcpServerSection(config, serverName, ownerKey, entry) {
|
|
2675
|
-
const servers = ensureObjectSection(config, "mcp_servers");
|
|
2676
|
-
servers[serverName] = { ...entry, [MCP_OWNER_MARKER_KEY]: ownerKey };
|
|
2677
|
-
}
|
|
2678
|
-
function removeMcpServerSection(config, serverName, expectedOwner) {
|
|
2679
|
-
const servers = config.mcp_servers;
|
|
2680
|
-
if (!servers || typeof servers !== "object") return "absent";
|
|
2681
|
-
const entry = servers[serverName];
|
|
2682
|
-
if (!entry || typeof entry !== "object") return "absent";
|
|
2683
|
-
const owner = entry[MCP_OWNER_MARKER_KEY];
|
|
2684
|
-
if (owner !== expectedOwner) return "foreign";
|
|
2685
|
-
delete servers[serverName];
|
|
2686
|
-
if (Object.keys(servers).length === 0) {
|
|
2687
|
-
delete config.mcp_servers;
|
|
2688
|
-
}
|
|
2689
|
-
return "removed";
|
|
2690
|
-
}
|
|
2691
|
-
function getMcpServerOwner(config, serverName) {
|
|
2692
|
-
const servers = config.mcp_servers;
|
|
2693
|
-
if (!servers || typeof servers !== "object") return null;
|
|
2694
|
-
const entry = servers[serverName];
|
|
2695
|
-
if (!entry || typeof entry !== "object") return null;
|
|
2696
|
-
const owner = entry[MCP_OWNER_MARKER_KEY];
|
|
2697
|
-
return typeof owner === "string" ? owner : void 0;
|
|
2698
|
-
}
|
|
2699
|
-
async function pathExists2(p) {
|
|
2700
|
-
try {
|
|
2701
|
-
await access(p, fsConstants.F_OK);
|
|
2702
|
-
return true;
|
|
2703
|
-
} catch {
|
|
2704
|
-
return false;
|
|
2705
|
-
}
|
|
2706
|
-
}
|
|
2707
|
-
async function atomicWrite(filePath, content) {
|
|
2708
|
-
await mkdir2(dirname2(filePath), { recursive: true });
|
|
2709
|
-
const tmp = `${filePath}.${randomUUID()}.tmp`;
|
|
2710
|
-
try {
|
|
2711
|
-
await writeFile2(tmp, content, { encoding: "utf8", flag: "w" });
|
|
2712
|
-
await rename(tmp, filePath);
|
|
2713
|
-
} catch (err) {
|
|
2714
|
-
await rm(tmp, { force: true }).catch(() => {
|
|
2589
|
+
results.sort((a, b) => {
|
|
2590
|
+
const ka = `${a.ref.name}@${a.ref.marketplace}`;
|
|
2591
|
+
const kb = `${b.ref.name}@${b.ref.marketplace}`;
|
|
2592
|
+
return ka < kb ? -1 : ka > kb ? 1 : 0;
|
|
2715
2593
|
});
|
|
2716
|
-
|
|
2594
|
+
return results;
|
|
2717
2595
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2596
|
+
// -------------------------------------------------------------------------
|
|
2597
|
+
// install / uninstall
|
|
2598
|
+
// -------------------------------------------------------------------------
|
|
2599
|
+
async install(plugin, opts = {}) {
|
|
2600
|
+
if (!await this.detect()) {
|
|
2601
|
+
return {
|
|
2602
|
+
target: "codex",
|
|
2603
|
+
status: "skipped",
|
|
2604
|
+
included: [],
|
|
2605
|
+
skipped: [],
|
|
2606
|
+
artifacts: { files: [], mcpKeys: [] }
|
|
2607
|
+
};
|
|
2723
2608
|
}
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
}
|
|
2734
|
-
function ensureObjectSection(config, key) {
|
|
2735
|
-
const existing = config[key];
|
|
2736
|
-
if (existing === void 0) {
|
|
2737
|
-
const fresh = {};
|
|
2738
|
-
config[key] = fresh;
|
|
2739
|
-
return fresh;
|
|
2740
|
-
}
|
|
2741
|
-
if (typeof existing !== "object" || existing === null || Array.isArray(existing)) {
|
|
2742
|
-
throw new CodexConfigTomlError(
|
|
2743
|
-
`config.toml \u9876\u5C42 '${key}' \u5DF2\u5B58\u5728\u4F46\u4E0D\u662F object\uFF08type=${Array.isArray(existing) ? "array" : typeof existing}\uFF09\u3002\u8BF7\u624B\u5DE5\u5907\u4EFD\u5E76\u6574\u7406\u8BE5\u5B57\u6BB5\u540E\u91CD\u8BD5\u3002`
|
|
2744
|
-
);
|
|
2745
|
-
}
|
|
2746
|
-
return existing;
|
|
2747
|
-
}
|
|
2748
|
-
|
|
2749
|
-
// src/installers/codex/mirror.ts
|
|
2750
|
-
function isSafePluginName(name) {
|
|
2751
|
-
if (typeof name !== "string" || name.length === 0) return false;
|
|
2752
|
-
if (name === "." || name === "..") return false;
|
|
2753
|
-
if (name.startsWith(".")) return false;
|
|
2754
|
-
if (name.includes("/") || name.includes("\\")) return false;
|
|
2755
|
-
if (name.includes("..")) return false;
|
|
2756
|
-
return /^[A-Za-z0-9_@][A-Za-z0-9._@-]*$/.test(name);
|
|
2757
|
-
}
|
|
2758
|
-
var DEFAULT_CATEGORY = "Engineering";
|
|
2759
|
-
var DEFAULT_CAPABILITIES = ["Read", "Write"];
|
|
2760
|
-
async function syncCodexMarketplaceMirror(home, resolved, opts = {}) {
|
|
2761
|
-
const result = {
|
|
2762
|
-
skipped: false,
|
|
2763
|
-
added: [],
|
|
2764
|
-
refreshed: [],
|
|
2765
|
-
preservedFull: [],
|
|
2766
|
-
removed: [],
|
|
2767
|
-
warnings: []
|
|
2768
|
-
};
|
|
2769
|
-
if (!await isDir(codexHomeDir(home))) {
|
|
2770
|
-
result.skipped = true;
|
|
2771
|
-
return result;
|
|
2772
|
-
}
|
|
2773
|
-
const marketplaceDir = codexMarketplaceDir(home, resolved.name);
|
|
2774
|
-
const manifestPath = codexMarketplaceManifestPath(marketplaceDir);
|
|
2775
|
-
const contentLoader = opts.contentLoader ?? (async () => ({}));
|
|
2776
|
-
const fallback = {
|
|
2777
|
-
name: resolved.name,
|
|
2778
|
-
interface: { displayName: resolved.name },
|
|
2779
|
-
plugins: []
|
|
2780
|
-
};
|
|
2781
|
-
const existing = await readCodexMarketplaceCatalog(manifestPath, fallback);
|
|
2782
|
-
const existingByName = new Map(existing.plugins.map((p) => [p.name, p]));
|
|
2783
|
-
const remotePlugins = [];
|
|
2784
|
-
const remoteNames = /* @__PURE__ */ new Set();
|
|
2785
|
-
for (const entry of resolved.manifest.plugins) {
|
|
2786
|
-
if (typeof entry?.name !== "string" || !isSafePluginName(entry.name)) {
|
|
2787
|
-
const display = typeof entry?.name === "string" ? entry.name : "<unnamed>";
|
|
2788
|
-
result.warnings.push(`skipped plugin with unsafe name: ${display}`);
|
|
2789
|
-
output.warn(
|
|
2790
|
-
`[codex] marketplace '${resolved.name}': skipped plugin with unsafe name: ${display}`
|
|
2791
|
-
);
|
|
2792
|
-
continue;
|
|
2609
|
+
const pathGuardError = assertSafePathComponents(plugin.ref, plugin.version);
|
|
2610
|
+
if (pathGuardError) {
|
|
2611
|
+
return {
|
|
2612
|
+
target: "codex",
|
|
2613
|
+
status: "failed",
|
|
2614
|
+
included: [],
|
|
2615
|
+
skipped: [],
|
|
2616
|
+
artifacts: { files: [], mcpKeys: [] },
|
|
2617
|
+
errors: [pathGuardError]
|
|
2618
|
+
};
|
|
2793
2619
|
}
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2620
|
+
const ref = plugin.ref;
|
|
2621
|
+
if (!opts.force && plugin.version !== "unknown" && await this.isInstalled(ref) && await isFullyInstalledMirror(this.home, ref)) {
|
|
2622
|
+
const existingVersion = await detectInstalledVersion(this.home, ref);
|
|
2623
|
+
if (existingVersion?.version === plugin.version) {
|
|
2624
|
+
return {
|
|
2625
|
+
target: "codex",
|
|
2626
|
+
status: "ok",
|
|
2627
|
+
included: computeIncludedCapabilities(plugin),
|
|
2628
|
+
skipped: [...CODEX_UNSUPPORTED],
|
|
2629
|
+
artifacts: { files: [], mcpKeys: [] },
|
|
2630
|
+
notes: [`\u63D2\u4EF6\u5DF2\u88C5\uFF08version=${plugin.version}\uFF09\uFF0C\u8DF3\u8FC7\u91CD\u590D\u5B89\u88C5`]
|
|
2631
|
+
};
|
|
2632
|
+
}
|
|
2797
2633
|
}
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
}
|
|
2801
|
-
const nextEntries = [];
|
|
2802
|
-
for (const entry of remotePlugins) {
|
|
2803
|
-
const pluginDir = codexMarketplacePluginDir(marketplaceDir, entry.name);
|
|
2804
|
-
const fullState = await detectFullPluginDir(pluginDir);
|
|
2805
|
-
if (fullState === "full") {
|
|
2806
|
-
const prior = existingByName.get(entry.name);
|
|
2807
|
-
const base = prior ?? buildStubCatalogEntry(entry);
|
|
2808
|
-
const fullEntry = {
|
|
2809
|
-
...base,
|
|
2810
|
-
policy: { installation: "AVAILABLE", authentication: "ON_INSTALL" }
|
|
2811
|
-
};
|
|
2812
|
-
nextEntries.push(fullEntry);
|
|
2813
|
-
result.preservedFull.push(entry.name);
|
|
2814
|
-
continue;
|
|
2634
|
+
if (opts.dryRun) {
|
|
2635
|
+
return this.buildDryRunResult(plugin);
|
|
2815
2636
|
}
|
|
2816
|
-
|
|
2637
|
+
const rollback = {
|
|
2638
|
+
createdVersionDir: null,
|
|
2639
|
+
createdMarketplacePluginDir: null,
|
|
2640
|
+
backupPath: null,
|
|
2641
|
+
preExistingConfig: null
|
|
2642
|
+
};
|
|
2817
2643
|
try {
|
|
2818
|
-
|
|
2644
|
+
return await this.doInstall(plugin, rollback);
|
|
2819
2645
|
} catch (err) {
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
output.warn(
|
|
2835
|
-
`[codex] marketplace '${resolved.name}': failed to write stub for '${entry.name}': ${err.message}`
|
|
2836
|
-
);
|
|
2837
|
-
continue;
|
|
2838
|
-
}
|
|
2839
|
-
nextEntries.push(buildStubCatalogEntry(entry, content));
|
|
2840
|
-
if (existingByName.has(entry.name)) {
|
|
2841
|
-
result.refreshed.push(entry.name);
|
|
2842
|
-
} else {
|
|
2843
|
-
result.added.push(entry.name);
|
|
2844
|
-
}
|
|
2845
|
-
}
|
|
2846
|
-
for (const prior of existing.plugins) {
|
|
2847
|
-
if (remoteNames.has(prior.name)) continue;
|
|
2848
|
-
if (!isSafePluginName(prior.name)) {
|
|
2849
|
-
result.warnings.push(
|
|
2850
|
-
`dropped catalog entry with unsafe name: ${prior.name}`
|
|
2851
|
-
);
|
|
2852
|
-
output.warn(
|
|
2853
|
-
`[codex] marketplace '${resolved.name}': dropped catalog entry with unsafe name: ${prior.name}`
|
|
2854
|
-
);
|
|
2855
|
-
result.removed.push(prior.name);
|
|
2856
|
-
continue;
|
|
2857
|
-
}
|
|
2858
|
-
const pluginDir = codexMarketplacePluginDir(marketplaceDir, prior.name);
|
|
2859
|
-
if (await detectFullPluginDir(pluginDir) === "full") {
|
|
2860
|
-
nextEntries.push(prior);
|
|
2861
|
-
output.warn(
|
|
2862
|
-
`[codex] marketplace '${resolved.name}': installed plugin '${prior.name}' no longer in remote catalog; keeping entry. Run 'rush-ai plugin uninstall ${prior.name}@${resolved.name}' if intended.`
|
|
2863
|
-
);
|
|
2864
|
-
continue;
|
|
2865
|
-
}
|
|
2866
|
-
await rm2(pluginDir, { recursive: true, force: true }).catch(() => {
|
|
2867
|
-
});
|
|
2868
|
-
result.removed.push(prior.name);
|
|
2869
|
-
}
|
|
2870
|
-
nextEntries.sort((a, b) => a.name.localeCompare(b.name));
|
|
2871
|
-
const nextCatalog = {
|
|
2872
|
-
name: existing.name || resolved.name,
|
|
2873
|
-
interface: existing.interface ?? fallback.interface,
|
|
2874
|
-
plugins: nextEntries
|
|
2875
|
-
};
|
|
2876
|
-
await writeFileAtomic(
|
|
2877
|
-
manifestPath,
|
|
2878
|
-
`${JSON.stringify(nextCatalog, null, 2)}
|
|
2879
|
-
`
|
|
2880
|
-
);
|
|
2881
|
-
try {
|
|
2882
|
-
await registerMarketplaceInCodexConfig({
|
|
2883
|
-
home,
|
|
2884
|
-
marketplaceName: resolved.name,
|
|
2885
|
-
marketplaceDir,
|
|
2886
|
-
now: opts.now ?? (() => /* @__PURE__ */ new Date())
|
|
2887
|
-
});
|
|
2888
|
-
} catch (err) {
|
|
2889
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2890
|
-
result.warnings.push(
|
|
2891
|
-
`failed to register [marketplaces.${resolved.name}] in config.toml: ${msg}`
|
|
2892
|
-
);
|
|
2893
|
-
output.warn(
|
|
2894
|
-
`[codex] marketplace '${resolved.name}': failed to register in config.toml: ${msg}`
|
|
2895
|
-
);
|
|
2896
|
-
}
|
|
2897
|
-
return result;
|
|
2898
|
-
}
|
|
2899
|
-
async function registerMarketplaceInCodexConfig(args) {
|
|
2900
|
-
const cfgPath = codexConfigTomlPath(args.home);
|
|
2901
|
-
await backupCodexConfig(
|
|
2902
|
-
cfgPath,
|
|
2903
|
-
codexConfigBackupPath(args.home, args.now())
|
|
2904
|
-
);
|
|
2905
|
-
const { data, mtimeMs } = await readCodexConfig(cfgPath);
|
|
2906
|
-
setMarketplaceSection(data, args.marketplaceName, {
|
|
2907
|
-
last_updated: args.now().toISOString(),
|
|
2908
|
-
source_type: "local",
|
|
2909
|
-
source: args.marketplaceDir
|
|
2910
|
-
});
|
|
2911
|
-
await writeCodexConfig(cfgPath, data, mtimeMs);
|
|
2912
|
-
}
|
|
2913
|
-
async function writeCodexMarketplacePluginMirror(input) {
|
|
2914
|
-
const pluginParent = dirname3(input.pluginDir);
|
|
2915
|
-
const pluginBase = basename2(input.pluginDir);
|
|
2916
|
-
await mkdir3(pluginParent, { recursive: true });
|
|
2917
|
-
const tmpPluginDir = await mkdtemp(
|
|
2918
|
-
resolve3(pluginParent, `.${pluginBase}.tmp-`)
|
|
2919
|
-
);
|
|
2920
|
-
const backupPluginDir = await mkdtemp(
|
|
2921
|
-
resolve3(pluginParent, `.${pluginBase}.bak-`)
|
|
2922
|
-
);
|
|
2923
|
-
let hadExistingPluginDir = false;
|
|
2924
|
-
let swappedPluginDir = false;
|
|
2925
|
-
try {
|
|
2926
|
-
await rm2(tmpPluginDir, { recursive: true, force: true });
|
|
2927
|
-
await rm2(backupPluginDir, { recursive: true, force: true }).catch(() => {
|
|
2928
|
-
});
|
|
2929
|
-
await cp(input.versionDir, tmpPluginDir, {
|
|
2930
|
-
recursive: true,
|
|
2931
|
-
dereference: false,
|
|
2932
|
-
preserveTimestamps: true,
|
|
2933
|
-
verbatimSymlinks: true
|
|
2934
|
-
});
|
|
2935
|
-
hadExistingPluginDir = await pathExists3(input.pluginDir);
|
|
2936
|
-
if (hadExistingPluginDir) {
|
|
2937
|
-
await rename2(input.pluginDir, backupPluginDir);
|
|
2938
|
-
}
|
|
2939
|
-
await rename2(tmpPluginDir, input.pluginDir);
|
|
2940
|
-
swappedPluginDir = true;
|
|
2941
|
-
await upsertCodexCatalogPluginFull(input.marketplaceDir, input.plugin);
|
|
2942
|
-
if (hadExistingPluginDir) {
|
|
2943
|
-
await rm2(backupPluginDir, { recursive: true, force: true });
|
|
2944
|
-
}
|
|
2945
|
-
} catch (err) {
|
|
2946
|
-
await rm2(tmpPluginDir, { recursive: true, force: true }).catch(() => {
|
|
2947
|
-
});
|
|
2948
|
-
let restoredBackup = false;
|
|
2949
|
-
if (hadExistingPluginDir && await pathExists3(backupPluginDir)) {
|
|
2950
|
-
let displacedPluginDir = null;
|
|
2951
|
-
try {
|
|
2952
|
-
if (await pathExists3(input.pluginDir)) {
|
|
2953
|
-
displacedPluginDir = await mkdtemp(
|
|
2954
|
-
resolve3(pluginParent, `.${pluginBase}.failed-`)
|
|
2955
|
-
);
|
|
2956
|
-
await rm2(displacedPluginDir, { recursive: true, force: true });
|
|
2957
|
-
await rename2(input.pluginDir, displacedPluginDir);
|
|
2958
|
-
}
|
|
2959
|
-
await rename2(backupPluginDir, input.pluginDir);
|
|
2960
|
-
restoredBackup = true;
|
|
2961
|
-
if (displacedPluginDir) {
|
|
2962
|
-
await rm2(displacedPluginDir, { recursive: true, force: true }).catch(
|
|
2963
|
-
() => {
|
|
2964
|
-
}
|
|
2965
|
-
);
|
|
2966
|
-
}
|
|
2967
|
-
} catch {
|
|
2968
|
-
if (displacedPluginDir && !await pathExists3(input.pluginDir) && await pathExists3(displacedPluginDir)) {
|
|
2969
|
-
await rename2(displacedPluginDir, input.pluginDir).catch(() => {
|
|
2970
|
-
});
|
|
2971
|
-
}
|
|
2972
|
-
}
|
|
2973
|
-
} else if (swappedPluginDir) {
|
|
2974
|
-
await rm2(input.pluginDir, { recursive: true, force: true }).catch(
|
|
2975
|
-
() => {
|
|
2976
|
-
}
|
|
2977
|
-
);
|
|
2978
|
-
}
|
|
2979
|
-
if (restoredBackup || !hadExistingPluginDir) {
|
|
2980
|
-
await rm2(backupPluginDir, { recursive: true, force: true }).catch(
|
|
2981
|
-
() => {
|
|
2982
|
-
}
|
|
2983
|
-
);
|
|
2984
|
-
}
|
|
2985
|
-
throw err;
|
|
2986
|
-
}
|
|
2987
|
-
}
|
|
2988
|
-
async function upsertCodexCatalogPluginFull(marketplaceDir, plugin) {
|
|
2989
|
-
const manifestPath = codexMarketplaceManifestPath(marketplaceDir);
|
|
2990
|
-
const fallback = {
|
|
2991
|
-
name: plugin.ref.marketplace,
|
|
2992
|
-
interface: { displayName: plugin.ref.marketplace },
|
|
2993
|
-
plugins: []
|
|
2994
|
-
};
|
|
2995
|
-
const catalog = await readCodexMarketplaceCatalog(manifestPath, fallback);
|
|
2996
|
-
const nextEntry = {
|
|
2997
|
-
name: plugin.ref.name,
|
|
2998
|
-
source: { source: "local", path: `./plugins/${plugin.ref.name}` },
|
|
2999
|
-
policy: { installation: "AVAILABLE", authentication: "ON_INSTALL" },
|
|
3000
|
-
category: buildInterfaceCategory(plugin.manifest)
|
|
3001
|
-
};
|
|
3002
|
-
if (plugin.manifest.description !== void 0) {
|
|
3003
|
-
nextEntry.description = plugin.manifest.description;
|
|
3004
|
-
}
|
|
3005
|
-
if (typeof plugin.manifest.version === "string") {
|
|
3006
|
-
nextEntry.version = plugin.manifest.version;
|
|
3007
|
-
}
|
|
3008
|
-
const next = [
|
|
3009
|
-
...catalog.plugins.filter((entry) => entry.name !== plugin.ref.name),
|
|
3010
|
-
nextEntry
|
|
3011
|
-
].sort((a, b) => a.name.localeCompare(b.name));
|
|
3012
|
-
await writeFileAtomic(
|
|
3013
|
-
manifestPath,
|
|
3014
|
-
`${JSON.stringify({ ...catalog, plugins: next }, null, 2)}
|
|
3015
|
-
`
|
|
3016
|
-
);
|
|
3017
|
-
}
|
|
3018
|
-
async function downgradeCodexCatalogEntryToStub(marketplaceDir, pluginName) {
|
|
3019
|
-
const manifestPath = codexMarketplaceManifestPath(marketplaceDir);
|
|
3020
|
-
const pluginDir = codexMarketplacePluginDir(marketplaceDir, pluginName);
|
|
3021
|
-
await rm2(pluginDir, { recursive: true, force: true }).catch(() => {
|
|
3022
|
-
});
|
|
3023
|
-
if (!await pathExists3(manifestPath)) return;
|
|
3024
|
-
try {
|
|
3025
|
-
const fallback = {
|
|
3026
|
-
name: "",
|
|
3027
|
-
interface: {},
|
|
3028
|
-
plugins: []
|
|
3029
|
-
};
|
|
3030
|
-
const catalog = await readCodexMarketplaceCatalog(manifestPath, fallback);
|
|
3031
|
-
const next = catalog.plugins.map((entry) => {
|
|
3032
|
-
if (entry.name !== pluginName) return entry;
|
|
3033
|
-
return {
|
|
3034
|
-
...entry,
|
|
3035
|
-
policy: {
|
|
3036
|
-
installation: "NOT_AVAILABLE",
|
|
3037
|
-
authentication: "ON_INSTALL"
|
|
3038
|
-
}
|
|
3039
|
-
};
|
|
3040
|
-
});
|
|
3041
|
-
await writeFileAtomic(
|
|
3042
|
-
manifestPath,
|
|
3043
|
-
`${JSON.stringify({ ...catalog, plugins: next }, null, 2)}
|
|
3044
|
-
`
|
|
3045
|
-
);
|
|
3046
|
-
} catch {
|
|
3047
|
-
}
|
|
3048
|
-
}
|
|
3049
|
-
async function readCodexMarketplaceCatalog(manifestPath, fallback) {
|
|
3050
|
-
if (!await pathExists3(manifestPath)) return fallback;
|
|
3051
|
-
try {
|
|
3052
|
-
const parsed = JSON.parse(await readFile3(manifestPath, "utf8"));
|
|
3053
|
-
return {
|
|
3054
|
-
name: typeof parsed.name === "string" ? parsed.name : fallback.name,
|
|
3055
|
-
interface: parsed.interface && typeof parsed.interface === "object" && !Array.isArray(parsed.interface) ? parsed.interface : fallback.interface,
|
|
3056
|
-
plugins: Array.isArray(parsed.plugins) ? parsed.plugins.filter(isCatalogPluginEntry) : fallback.plugins
|
|
3057
|
-
};
|
|
3058
|
-
} catch {
|
|
3059
|
-
return fallback;
|
|
3060
|
-
}
|
|
3061
|
-
}
|
|
3062
|
-
function isCatalogPluginEntry(value) {
|
|
3063
|
-
return !!value && typeof value === "object" && !Array.isArray(value) && typeof value.name === "string";
|
|
3064
|
-
}
|
|
3065
|
-
function buildStubCatalogEntry(entry, content = {}) {
|
|
3066
|
-
const description = pickNonEmptyString(entry.description) ?? pickNonEmptyString(content.manifest?.description);
|
|
3067
|
-
const version = pickNonEmptyString(entry.version) ?? pickNonEmptyString(content.manifest?.version);
|
|
3068
|
-
const m = content.manifest ?? void 0;
|
|
3069
|
-
const ifaceIn = m?.interface ?? {};
|
|
3070
|
-
const entryCategory = pickNonEmptyString(
|
|
3071
|
-
entry.category
|
|
3072
|
-
);
|
|
3073
|
-
const category = pickNonEmptyString(ifaceIn.category) ?? normalizeCategory(entryCategory) ?? DEFAULT_CATEGORY;
|
|
3074
|
-
const out = {
|
|
3075
|
-
name: entry.name,
|
|
3076
|
-
source: { source: "local", path: `./plugins/${entry.name}` },
|
|
3077
|
-
// stub 入口 → 标记为 NOT_AVAILABLE,让 Codex UI 禁用"连接"按钮,
|
|
3078
|
-
// 因为点击连接只会写一份没有 MCP key 的伪安装。真正安装走
|
|
3079
|
-
// `npx rush-ai@latest plugin install <name>@<marketplace>`。
|
|
3080
|
-
// 已装 full 路径在 upsertCodexCatalogPluginFull 中覆写为 AVAILABLE。
|
|
3081
|
-
policy: { installation: "NOT_AVAILABLE", authentication: "ON_INSTALL" },
|
|
3082
|
-
category
|
|
3083
|
-
};
|
|
3084
|
-
if (description !== void 0) out.description = description;
|
|
3085
|
-
if (version !== void 0) out.version = version;
|
|
3086
|
-
return out;
|
|
3087
|
-
}
|
|
3088
|
-
function buildPluginJson(entry, content, marketplaceName) {
|
|
3089
|
-
const m = content.manifest ?? void 0;
|
|
3090
|
-
const author = m?.author ?? (entry.author && typeof entry.author === "object" ? entry.author : void 0);
|
|
3091
|
-
const homepage = pickNonEmptyString(m?.homepage) ?? pickNonEmptyString(entry.homepage);
|
|
3092
|
-
const license = pickNonEmptyString(m?.license) ?? pickNonEmptyString(entry.license);
|
|
3093
|
-
const keywords = Array.isArray(m?.keywords) && m.keywords.length > 0 ? m.keywords : Array.isArray(entry.keywords) && entry.keywords.length > 0 ? entry.keywords : void 0;
|
|
3094
|
-
const entryCategory = pickNonEmptyString(
|
|
3095
|
-
entry.category
|
|
3096
|
-
);
|
|
3097
|
-
const sourceUrl = extractSourceUrl(entry.source);
|
|
3098
|
-
const description = pickNonEmptyString(m?.description) ?? pickNonEmptyString(entry.description) ?? entry.name;
|
|
3099
|
-
const version = pickNonEmptyString(m?.version) ?? pickNonEmptyString(entry.version) ?? "0.0.0";
|
|
3100
|
-
const ifaceIn = m?.interface ?? {};
|
|
3101
|
-
const developerName = pickNonEmptyString(ifaceIn.developerName) ?? pickNonEmptyString(author?.name);
|
|
3102
|
-
const websiteURL = pickNonEmptyString(ifaceIn.websiteURL) ?? sourceUrl ?? homepage;
|
|
3103
|
-
const category = pickNonEmptyString(ifaceIn.category) ?? normalizeCategory(entryCategory) ?? DEFAULT_CATEGORY;
|
|
3104
|
-
const capabilities = Array.isArray(ifaceIn.capabilities) && ifaceIn.capabilities.length > 0 ? [...ifaceIn.capabilities] : [...DEFAULT_CAPABILITIES];
|
|
3105
|
-
const installCmd = `npx rush-ai@latest plugin install ${entry.name}@${marketplaceName} --target codex`;
|
|
3106
|
-
const shortHintPrefix = "\u26A0 \u9700\u5728\u7EC8\u7AEF\u5B89\u88C5 \uFF5C ";
|
|
3107
|
-
const authorShort = pickNonEmptyString(ifaceIn.shortDescription) ?? description;
|
|
3108
|
-
const baseLongDescription = pickNonEmptyString(ifaceIn.longDescription) ?? pickNonEmptyString(ifaceIn.shortDescription) ?? description;
|
|
3109
|
-
const extraLinks = [];
|
|
3110
|
-
if (homepage !== void 0 && homepage !== websiteURL) {
|
|
3111
|
-
extraLinks.push(`\u5B98\u7F51\uFF1A${homepage}`);
|
|
3112
|
-
}
|
|
3113
|
-
if (sourceUrl !== void 0 && sourceUrl !== websiteURL && sourceUrl !== homepage) {
|
|
3114
|
-
extraLinks.push(`\u6E90\u4EE3\u7801\uFF1A${sourceUrl}`);
|
|
3115
|
-
}
|
|
3116
|
-
const extraLinksSection = extraLinks.length > 0 ? `
|
|
3117
|
-
|
|
3118
|
-
${extraLinks.join("\n")}` : "";
|
|
3119
|
-
const installSection = `\u2014\u2014\u2014
|
|
3120
|
-
\u26A0 Codex \u684C\u9762\u7AEF\u7684"\u8FDE\u63A5"\u6309\u94AE\u65E0\u6CD5\u5B8C\u6574\u5B89\u88C5\u672C\u63D2\u4EF6
|
|
3121
|
-
\u8BF7\u5728\u7EC8\u7AEF\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\u5B8C\u6210\u5B89\u88C5\uFF1A
|
|
3122
|
-
${installCmd}`;
|
|
3123
|
-
const longDescription = `${baseLongDescription}${extraLinksSection}
|
|
3124
|
-
|
|
3125
|
-
${installSection}`;
|
|
3126
|
-
const defaultPrompt = Array.isArray(ifaceIn.defaultPrompt) ? [...ifaceIn.defaultPrompt] : [];
|
|
3127
|
-
if (defaultPrompt.length === 0) {
|
|
3128
|
-
defaultPrompt.push(installCmd);
|
|
3129
|
-
}
|
|
3130
|
-
const ifaceOut = {
|
|
3131
|
-
displayName: pickNonEmptyString(ifaceIn.displayName) ?? entry.name,
|
|
3132
|
-
shortDescription: `${shortHintPrefix}${authorShort}`,
|
|
3133
|
-
longDescription,
|
|
3134
|
-
category,
|
|
3135
|
-
capabilities,
|
|
3136
|
-
defaultPrompt
|
|
3137
|
-
};
|
|
3138
|
-
if (developerName !== void 0) ifaceOut.developerName = developerName;
|
|
3139
|
-
if (websiteURL !== void 0) ifaceOut.websiteURL = websiteURL;
|
|
3140
|
-
if (pickNonEmptyString(ifaceIn.privacyPolicyURL) !== void 0)
|
|
3141
|
-
ifaceOut.privacyPolicyURL = ifaceIn.privacyPolicyURL;
|
|
3142
|
-
if (pickNonEmptyString(ifaceIn.termsOfServiceURL) !== void 0)
|
|
3143
|
-
ifaceOut.termsOfServiceURL = ifaceIn.termsOfServiceURL;
|
|
3144
|
-
if (pickNonEmptyString(ifaceIn.brandColor) !== void 0)
|
|
3145
|
-
ifaceOut.brandColor = ifaceIn.brandColor;
|
|
3146
|
-
if (pickNonEmptyString(ifaceIn.composerIcon) !== void 0)
|
|
3147
|
-
ifaceOut.composerIcon = ifaceIn.composerIcon;
|
|
3148
|
-
if (pickNonEmptyString(ifaceIn.logo) !== void 0)
|
|
3149
|
-
ifaceOut.logo = ifaceIn.logo;
|
|
3150
|
-
if (Array.isArray(ifaceIn.screenshots) && ifaceIn.screenshots.length > 0)
|
|
3151
|
-
ifaceOut.screenshots = [...ifaceIn.screenshots];
|
|
3152
|
-
const out = {
|
|
3153
|
-
// marker:让 detectFullPluginDir 可靠区分 sync vs install 写出的目录。
|
|
3154
|
-
// Codex 不知道这个字段,会被忽略;marker 必须是合法 JSON 字段名才能被 plugin.json 接受。
|
|
3155
|
-
[STUB_MARKER_KEY]: STUB_MARKER_VALUE,
|
|
3156
|
-
name: entry.name,
|
|
3157
|
-
version,
|
|
3158
|
-
description,
|
|
3159
|
-
skills: "./skills/",
|
|
3160
|
-
interface: ifaceOut
|
|
3161
|
-
};
|
|
3162
|
-
if (author && typeof author === "object") {
|
|
3163
|
-
out.author = { ...author };
|
|
3164
|
-
}
|
|
3165
|
-
if (homepage !== void 0) {
|
|
3166
|
-
out.homepage = homepage;
|
|
3167
|
-
}
|
|
3168
|
-
if (sourceUrl !== void 0) {
|
|
3169
|
-
out.repository = sourceUrl;
|
|
3170
|
-
}
|
|
3171
|
-
if (license !== void 0) {
|
|
3172
|
-
out.license = license;
|
|
3173
|
-
}
|
|
3174
|
-
if (keywords !== void 0) {
|
|
3175
|
-
out.keywords = [...keywords];
|
|
3176
|
-
}
|
|
3177
|
-
if (hasMcpServers(content)) {
|
|
3178
|
-
out.mcpServers = "./.mcp.json";
|
|
3179
|
-
}
|
|
3180
|
-
return out;
|
|
3181
|
-
}
|
|
3182
|
-
function extractSourceUrl(source) {
|
|
3183
|
-
if (!source || typeof source === "string") return void 0;
|
|
3184
|
-
const obj = source;
|
|
3185
|
-
const url = obj.url;
|
|
3186
|
-
if (typeof url !== "string" || url.length === 0) return void 0;
|
|
3187
|
-
if (!/^https?:\/\//i.test(url)) return void 0;
|
|
3188
|
-
return url.replace(/\.git$/, "");
|
|
3189
|
-
}
|
|
3190
|
-
function normalizeCategory(raw) {
|
|
3191
|
-
if (raw === void 0) return void 0;
|
|
3192
|
-
const lower = raw.toLowerCase().trim();
|
|
3193
|
-
const map = {
|
|
3194
|
-
development: "Engineering",
|
|
3195
|
-
data: "Engineering",
|
|
3196
|
-
devops: "Engineering",
|
|
3197
|
-
design: "Design",
|
|
3198
|
-
productivity: "Productivity",
|
|
3199
|
-
security: "Security",
|
|
3200
|
-
research: "Research",
|
|
3201
|
-
automation: "Productivity",
|
|
3202
|
-
integration: "Engineering"
|
|
3203
|
-
};
|
|
3204
|
-
if (lower in map) return map[lower];
|
|
3205
|
-
return lower.charAt(0).toUpperCase() + lower.slice(1);
|
|
3206
|
-
}
|
|
3207
|
-
async function writePluginMaterials(pluginDir, entry, content, marketplaceName) {
|
|
3208
|
-
const manifestPath = codexPluginManifestPath(pluginDir);
|
|
3209
|
-
await mkdir3(dirname3(manifestPath), { recursive: true });
|
|
3210
|
-
await rm2(codexPluginMcpPath(pluginDir), { force: true }).catch(() => {
|
|
3211
|
-
});
|
|
3212
|
-
await rm2(codexPluginSkillsDir(pluginDir), {
|
|
3213
|
-
recursive: true,
|
|
3214
|
-
force: true
|
|
3215
|
-
}).catch(() => {
|
|
3216
|
-
});
|
|
3217
|
-
const pluginJson = buildPluginJson(entry, content, marketplaceName);
|
|
3218
|
-
await writeFileAtomic(
|
|
3219
|
-
manifestPath,
|
|
3220
|
-
`${JSON.stringify(pluginJson, null, 2)}
|
|
3221
|
-
`
|
|
3222
|
-
);
|
|
3223
|
-
if (hasMcpServers(content)) {
|
|
3224
|
-
const mcpPath = codexPluginMcpPath(pluginDir);
|
|
3225
|
-
await writeFileAtomic(
|
|
3226
|
-
mcpPath,
|
|
3227
|
-
`${JSON.stringify({ mcpServers: content.mcpServers }, null, 2)}
|
|
3228
|
-
`
|
|
3229
|
-
);
|
|
3230
|
-
}
|
|
3231
|
-
if (content.skillsSourceDir && await isDir(content.skillsSourceDir)) {
|
|
3232
|
-
const skillsDir = codexPluginSkillsDir(pluginDir);
|
|
3233
|
-
await mkdir3(skillsDir, { recursive: true });
|
|
3234
|
-
await cp(content.skillsSourceDir, skillsDir, {
|
|
3235
|
-
recursive: true,
|
|
3236
|
-
dereference: false,
|
|
3237
|
-
preserveTimestamps: true,
|
|
3238
|
-
verbatimSymlinks: true
|
|
3239
|
-
});
|
|
3240
|
-
} else if (Array.isArray(content.skillsPlaceholders) && content.skillsPlaceholders.length > 0) {
|
|
3241
|
-
const installCmd = pickNonEmptyString(content.installCommand) ?? `npx rush-ai@latest plugin install ${entry.name}@${marketplaceName} --target codex`;
|
|
3242
|
-
const pluginWebUrl = pickNonEmptyString(content.pluginWebUrl);
|
|
3243
|
-
const skillsDir = codexPluginSkillsDir(pluginDir);
|
|
3244
|
-
const usedDirs = /* @__PURE__ */ new Set();
|
|
3245
|
-
for (const skill of content.skillsPlaceholders) {
|
|
3246
|
-
if (!isSafeSkillName(skill.name)) continue;
|
|
3247
|
-
const dirName = uniqueDirName(slugifySkillName(skill.name), usedDirs);
|
|
3248
|
-
usedDirs.add(dirName);
|
|
3249
|
-
const skillDir = resolve3(skillsDir, dirName);
|
|
3250
|
-
await mkdir3(skillDir, { recursive: true });
|
|
3251
|
-
const rawSkillMd = pickNonEmptyString(skill.rawSkillMd);
|
|
3252
|
-
if (rawSkillMd !== void 0) {
|
|
3253
|
-
const rewritten = rewriteSkillMdName(rawSkillMd, dirName);
|
|
3254
|
-
await writeFileAtomic(resolve3(skillDir, "SKILL.md"), rewritten);
|
|
3255
|
-
continue;
|
|
3256
|
-
}
|
|
3257
|
-
const desc = pickNonEmptyString(skill.description) ?? `Skill ${skill.name}`;
|
|
3258
|
-
const fullDesc = skill.name === dirName ? desc : `${skill.name} \u2014 ${desc}`;
|
|
3259
|
-
const skillUrl = pickNonEmptyString(skill.url);
|
|
3260
|
-
const body = buildSkillPlaceholderBody({
|
|
3261
|
-
name: skill.name,
|
|
3262
|
-
description: fullDesc,
|
|
3263
|
-
skillUrl,
|
|
3264
|
-
pluginWebUrl,
|
|
3265
|
-
installCmd
|
|
3266
|
-
});
|
|
3267
|
-
const md = `---
|
|
3268
|
-
name: ${jsonQuote(dirName)}
|
|
3269
|
-
description: ${jsonQuote(fullDesc)}
|
|
3270
|
-
---
|
|
3271
|
-
|
|
3272
|
-
${body}
|
|
3273
|
-
`;
|
|
3274
|
-
await writeFileAtomic(resolve3(skillDir, "SKILL.md"), md);
|
|
3275
|
-
}
|
|
3276
|
-
}
|
|
3277
|
-
}
|
|
3278
|
-
function rewriteSkillMdName(raw, dirName) {
|
|
3279
|
-
const text = raw.replace(/\r\n/g, "\n");
|
|
3280
|
-
if (!text.startsWith("---\n")) {
|
|
3281
|
-
return `---
|
|
3282
|
-
name: ${jsonQuote(dirName)}
|
|
3283
|
-
description: ${jsonQuote(dirName)}
|
|
3284
|
-
---
|
|
3285
|
-
|
|
3286
|
-
` + text;
|
|
3287
|
-
}
|
|
3288
|
-
const lines = text.split("\n");
|
|
3289
|
-
let closeIdx = -1;
|
|
3290
|
-
for (let i = 1; i < lines.length; i++) {
|
|
3291
|
-
if (lines[i] === "---") {
|
|
3292
|
-
closeIdx = i;
|
|
3293
|
-
break;
|
|
3294
|
-
}
|
|
3295
|
-
}
|
|
3296
|
-
if (closeIdx === -1) {
|
|
3297
|
-
return `---
|
|
3298
|
-
name: ${jsonQuote(dirName)}
|
|
3299
|
-
description: ${jsonQuote(dirName)}
|
|
3300
|
-
---
|
|
3301
|
-
|
|
3302
|
-
` + text;
|
|
3303
|
-
}
|
|
3304
|
-
let foundName = false;
|
|
3305
|
-
for (let i = 1; i < closeIdx; i++) {
|
|
3306
|
-
const line = lines[i] ?? "";
|
|
3307
|
-
if (/^name\s*:/.test(line)) {
|
|
3308
|
-
lines[i] = `name: ${jsonQuote(dirName)}`;
|
|
3309
|
-
foundName = true;
|
|
3310
|
-
break;
|
|
3311
|
-
}
|
|
3312
|
-
}
|
|
3313
|
-
if (!foundName) {
|
|
3314
|
-
lines.splice(1, 0, `name: ${jsonQuote(dirName)}`);
|
|
3315
|
-
}
|
|
3316
|
-
return lines.join("\n");
|
|
3317
|
-
}
|
|
3318
|
-
function buildSkillPlaceholderBody(input) {
|
|
3319
|
-
const lines = [];
|
|
3320
|
-
lines.push(`# ${input.name}`);
|
|
3321
|
-
lines.push("");
|
|
3322
|
-
lines.push(input.description);
|
|
3323
|
-
lines.push("");
|
|
3324
|
-
lines.push(
|
|
3325
|
-
"> \u8FD9\u662F rush-ai marketplace \u540C\u6B65\u51FA\u7684\u5360\u4F4D skill \u2014\u2014 skill \u5B8C\u6574\u5185\u5BB9\uFF08trigger / \u64CD\u4F5C\u6B65\u9AA4 / \u63D0\u793A\u8BCD\uFF09\u5C1A\u672A\u4E0B\u8F7D\u3002"
|
|
3326
|
-
);
|
|
3327
|
-
lines.push(">");
|
|
3328
|
-
lines.push("> \u5728\u7EC8\u7AEF\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\u4EE5\u5B8C\u6574\u5B89\u88C5\uFF1A");
|
|
3329
|
-
lines.push(">");
|
|
3330
|
-
lines.push("> ```bash");
|
|
3331
|
-
lines.push(`> ${input.installCmd}`);
|
|
3332
|
-
lines.push("> ```");
|
|
3333
|
-
if (input.skillUrl !== void 0) {
|
|
3334
|
-
lines.push(">");
|
|
3335
|
-
lines.push(`> \u4E5F\u53EF\u5728 web \u7AD9\u70B9\u67E5\u770B\uFF1A[${input.name}](${input.skillUrl})`);
|
|
3336
|
-
}
|
|
3337
|
-
if (input.pluginWebUrl !== void 0) {
|
|
3338
|
-
lines.push(">");
|
|
3339
|
-
lines.push(`> \u6D4F\u89C8\u6240\u5C5E plugin\uFF1A${input.pluginWebUrl}`);
|
|
3340
|
-
}
|
|
3341
|
-
return lines.join("\n");
|
|
3342
|
-
}
|
|
3343
|
-
function jsonQuote(s) {
|
|
3344
|
-
return JSON.stringify(s);
|
|
3345
|
-
}
|
|
3346
|
-
function slugifySkillName(name) {
|
|
3347
|
-
return name.replace(/^@/, "").replace(/[\\/.]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "") || "skill";
|
|
3348
|
-
}
|
|
3349
|
-
function uniqueDirName(base, used) {
|
|
3350
|
-
if (!used.has(base)) return base;
|
|
3351
|
-
let i = 2;
|
|
3352
|
-
while (used.has(`${base}-${i}`)) i += 1;
|
|
3353
|
-
return `${base}-${i}`;
|
|
3354
|
-
}
|
|
3355
|
-
function isSafeSkillName(name) {
|
|
3356
|
-
if (typeof name !== "string" || name.length === 0) return false;
|
|
3357
|
-
if (name === "." || name === "..") return false;
|
|
3358
|
-
if (name.startsWith(".")) return false;
|
|
3359
|
-
if (name.includes("\\")) return false;
|
|
3360
|
-
if (name.includes("..")) return false;
|
|
3361
|
-
const slashCount = (name.match(/\//g) ?? []).length;
|
|
3362
|
-
if (slashCount > 1) return false;
|
|
3363
|
-
if (slashCount === 1 && !name.startsWith("@")) return false;
|
|
3364
|
-
return /^@?[A-Za-z0-9_][A-Za-z0-9._@/-]*$/.test(name);
|
|
3365
|
-
}
|
|
3366
|
-
function hasMcpServers(content) {
|
|
3367
|
-
return !!content.mcpServers && typeof content.mcpServers === "object" && Object.keys(content.mcpServers).length > 0;
|
|
3368
|
-
}
|
|
3369
|
-
function pickNonEmptyString(value) {
|
|
3370
|
-
if (typeof value === "string" && value.length > 0) return value;
|
|
3371
|
-
return void 0;
|
|
3372
|
-
}
|
|
3373
|
-
var STUB_MARKER_KEY = "x-rush-mirror";
|
|
3374
|
-
var STUB_MARKER_VALUE = "stub";
|
|
3375
|
-
async function detectFullPluginDir(pluginDir) {
|
|
3376
|
-
if (!await isDir(pluginDir)) return "stub";
|
|
3377
|
-
const manifestPath = codexPluginManifestPath(pluginDir);
|
|
3378
|
-
if (await pathExists3(manifestPath)) {
|
|
3379
|
-
try {
|
|
3380
|
-
const raw = await readFile3(manifestPath, "utf8");
|
|
3381
|
-
const parsed = JSON.parse(raw);
|
|
3382
|
-
if (parsed[STUB_MARKER_KEY] === STUB_MARKER_VALUE) {
|
|
3383
|
-
return "stub";
|
|
3384
|
-
}
|
|
3385
|
-
} catch {
|
|
3386
|
-
}
|
|
3387
|
-
}
|
|
3388
|
-
if (await dirHasEntries(codexPluginSkillsDir(pluginDir))) {
|
|
3389
|
-
return "full";
|
|
3390
|
-
}
|
|
3391
|
-
const mcpPath = codexPluginMcpPath(pluginDir);
|
|
3392
|
-
if (await pathExists3(mcpPath)) {
|
|
3393
|
-
try {
|
|
3394
|
-
const raw = await readFile3(mcpPath, "utf8");
|
|
3395
|
-
if (!/\$\{[^}]+\}/.test(raw)) {
|
|
3396
|
-
return "full";
|
|
3397
|
-
}
|
|
3398
|
-
} catch {
|
|
3399
|
-
}
|
|
3400
|
-
}
|
|
3401
|
-
return "stub";
|
|
3402
|
-
}
|
|
3403
|
-
async function dirHasEntries(dir) {
|
|
3404
|
-
if (!await isDir(dir)) return false;
|
|
3405
|
-
let entries;
|
|
3406
|
-
try {
|
|
3407
|
-
entries = await readdir(dir, { withFileTypes: true });
|
|
3408
|
-
} catch {
|
|
3409
|
-
return false;
|
|
3410
|
-
}
|
|
3411
|
-
return entries.length > 0;
|
|
3412
|
-
}
|
|
3413
|
-
function buildInterfaceCategory(manifest) {
|
|
3414
|
-
return manifest.interface?.category ?? DEFAULT_CATEGORY;
|
|
3415
|
-
}
|
|
3416
|
-
async function writeFileAtomic(filePath, content) {
|
|
3417
|
-
await mkdir3(dirname3(filePath), { recursive: true });
|
|
3418
|
-
const tmp = `${filePath}.${randomUUID2()}.tmp`;
|
|
3419
|
-
try {
|
|
3420
|
-
await writeFile3(tmp, content, { encoding: "utf8", flag: "w" });
|
|
3421
|
-
await rename2(tmp, filePath);
|
|
3422
|
-
} catch (err) {
|
|
3423
|
-
await rm2(tmp, { force: true }).catch(() => {
|
|
3424
|
-
});
|
|
3425
|
-
throw err;
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
|
-
async function pathExists3(p) {
|
|
3429
|
-
try {
|
|
3430
|
-
await access2(p, fsConstants2.F_OK);
|
|
3431
|
-
return true;
|
|
3432
|
-
} catch {
|
|
3433
|
-
return false;
|
|
3434
|
-
}
|
|
3435
|
-
}
|
|
3436
|
-
async function isDir(p) {
|
|
3437
|
-
try {
|
|
3438
|
-
const s = await stat2(p);
|
|
3439
|
-
return s.isDirectory();
|
|
3440
|
-
} catch {
|
|
3441
|
-
return false;
|
|
3442
|
-
}
|
|
3443
|
-
}
|
|
3444
|
-
|
|
3445
|
-
// src/installers/codex/installer.ts
|
|
3446
|
-
var CODEX_UNSUPPORTED = ["rules", "hooks"];
|
|
3447
|
-
var CodexInstallError = class extends Error {
|
|
3448
|
-
constructor(message) {
|
|
3449
|
-
super(message);
|
|
3450
|
-
this.name = "CodexInstallError";
|
|
3451
|
-
}
|
|
3452
|
-
};
|
|
3453
|
-
var CodexInstaller = class {
|
|
3454
|
-
target = "codex";
|
|
3455
|
-
home;
|
|
3456
|
-
rushAiBinaryResolver;
|
|
3457
|
-
now;
|
|
3458
|
-
marketplaceSource;
|
|
3459
|
-
constructor(opts = {}) {
|
|
3460
|
-
this.home = opts.home ?? homedir3();
|
|
3461
|
-
this.rushAiBinaryResolver = opts.rushAiBinaryResolver ?? defaultRushAiBinaryResolver;
|
|
3462
|
-
this.now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
3463
|
-
this.marketplaceSource = opts.marketplaceSource;
|
|
3464
|
-
}
|
|
3465
|
-
// -------------------------------------------------------------------------
|
|
3466
|
-
// detect / isInstalled / list
|
|
3467
|
-
// -------------------------------------------------------------------------
|
|
3468
|
-
async detect() {
|
|
3469
|
-
return isDir2(codexHomeDir(this.home));
|
|
3470
|
-
}
|
|
3471
|
-
async isInstalled(ref) {
|
|
3472
|
-
const cfgPath = codexConfigTomlPath(this.home);
|
|
3473
|
-
if (!await pathExists4(cfgPath)) return false;
|
|
3474
|
-
const { data } = await readCodexConfig(cfgPath);
|
|
3475
|
-
const plugins = data.plugins;
|
|
3476
|
-
if (!plugins || typeof plugins !== "object") return false;
|
|
3477
|
-
const key = pluginSectionKey(ref);
|
|
3478
|
-
const entry = plugins[key];
|
|
3479
|
-
if (!entry || typeof entry !== "object") return false;
|
|
3480
|
-
return entry.enabled === true;
|
|
3481
|
-
}
|
|
3482
|
-
async list() {
|
|
3483
|
-
const cfgPath = codexConfigTomlPath(this.home);
|
|
3484
|
-
if (!await pathExists4(cfgPath)) return [];
|
|
3485
|
-
const { data } = await readCodexConfig(cfgPath);
|
|
3486
|
-
const plugins = data.plugins;
|
|
3487
|
-
if (!plugins || typeof plugins !== "object") return [];
|
|
3488
|
-
const results = [];
|
|
3489
|
-
for (const [key, raw] of Object.entries(plugins)) {
|
|
3490
|
-
if (!raw || typeof raw !== "object") continue;
|
|
3491
|
-
const entry = raw;
|
|
3492
|
-
if (entry.enabled !== true) continue;
|
|
3493
|
-
const ref = parsePluginKey(key);
|
|
3494
|
-
if (!ref) continue;
|
|
3495
|
-
const detected = await detectInstalledVersion(this.home, ref);
|
|
3496
|
-
results.push({
|
|
3497
|
-
ref,
|
|
3498
|
-
version: detected?.version ?? "unknown",
|
|
3499
|
-
installedAt: detected?.installedAt ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
3500
|
-
targets: ["codex"]
|
|
3501
|
-
});
|
|
3502
|
-
}
|
|
3503
|
-
results.sort((a, b) => {
|
|
3504
|
-
const ka = `${a.ref.name}@${a.ref.marketplace}`;
|
|
3505
|
-
const kb = `${b.ref.name}@${b.ref.marketplace}`;
|
|
3506
|
-
return ka < kb ? -1 : ka > kb ? 1 : 0;
|
|
3507
|
-
});
|
|
3508
|
-
return results;
|
|
3509
|
-
}
|
|
3510
|
-
// -------------------------------------------------------------------------
|
|
3511
|
-
// install / uninstall
|
|
3512
|
-
// -------------------------------------------------------------------------
|
|
3513
|
-
async install(plugin, opts = {}) {
|
|
3514
|
-
if (!await this.detect()) {
|
|
3515
|
-
return {
|
|
3516
|
-
target: "codex",
|
|
3517
|
-
status: "skipped",
|
|
3518
|
-
included: [],
|
|
3519
|
-
skipped: [],
|
|
3520
|
-
artifacts: { files: [], mcpKeys: [] }
|
|
3521
|
-
};
|
|
3522
|
-
}
|
|
3523
|
-
const pathGuardError = assertSafePathComponents(plugin.ref, plugin.version);
|
|
3524
|
-
if (pathGuardError) {
|
|
3525
|
-
return {
|
|
3526
|
-
target: "codex",
|
|
3527
|
-
status: "failed",
|
|
3528
|
-
included: [],
|
|
3529
|
-
skipped: [],
|
|
3530
|
-
artifacts: { files: [], mcpKeys: [] },
|
|
3531
|
-
errors: [pathGuardError]
|
|
3532
|
-
};
|
|
3533
|
-
}
|
|
3534
|
-
const ref = plugin.ref;
|
|
3535
|
-
if (!opts.force && plugin.version !== "unknown" && await this.isInstalled(ref) && await isFullyInstalledMirror(this.home, ref)) {
|
|
3536
|
-
const existingVersion = await detectInstalledVersion(this.home, ref);
|
|
3537
|
-
if (existingVersion?.version === plugin.version) {
|
|
3538
|
-
return {
|
|
3539
|
-
target: "codex",
|
|
3540
|
-
status: "ok",
|
|
3541
|
-
included: computeIncludedCapabilities(plugin),
|
|
3542
|
-
skipped: [...CODEX_UNSUPPORTED],
|
|
3543
|
-
artifacts: { files: [], mcpKeys: [] },
|
|
3544
|
-
notes: [`\u63D2\u4EF6\u5DF2\u88C5\uFF08version=${plugin.version}\uFF09\uFF0C\u8DF3\u8FC7\u91CD\u590D\u5B89\u88C5`]
|
|
3545
|
-
};
|
|
3546
|
-
}
|
|
3547
|
-
}
|
|
3548
|
-
if (opts.dryRun) {
|
|
3549
|
-
return this.buildDryRunResult(plugin);
|
|
3550
|
-
}
|
|
3551
|
-
const rollback = {
|
|
3552
|
-
createdVersionDir: null,
|
|
3553
|
-
createdMarketplacePluginDir: null,
|
|
3554
|
-
backupPath: null,
|
|
3555
|
-
preExistingConfig: null
|
|
3556
|
-
};
|
|
3557
|
-
try {
|
|
3558
|
-
return await this.doInstall(plugin, rollback);
|
|
3559
|
-
} catch (err) {
|
|
3560
|
-
await rollbackInstall(this.home, rollback);
|
|
3561
|
-
if (isExpectedInstallError(err)) {
|
|
3562
|
-
const message = err.message;
|
|
3563
|
-
return {
|
|
3564
|
-
target: "codex",
|
|
3565
|
-
status: "failed",
|
|
3566
|
-
included: [],
|
|
3567
|
-
skipped: [],
|
|
3568
|
-
artifacts: { files: [], mcpKeys: [] },
|
|
3569
|
-
errors: [message],
|
|
3570
|
-
notes: rollback.backupPath ? [`\u5DF2\u4ECE\u5907\u4EFD\u6062\u590D config.toml\uFF08${rollback.backupPath} \u5DF2\u5220\u9664\uFF09`] : void 0
|
|
3571
|
-
};
|
|
3572
|
-
}
|
|
3573
|
-
throw err;
|
|
2646
|
+
await rollbackInstall(this.home, rollback);
|
|
2647
|
+
if (isExpectedInstallError(err)) {
|
|
2648
|
+
const message = err.message;
|
|
2649
|
+
return {
|
|
2650
|
+
target: "codex",
|
|
2651
|
+
status: "failed",
|
|
2652
|
+
included: [],
|
|
2653
|
+
skipped: [],
|
|
2654
|
+
artifacts: { files: [], mcpKeys: [] },
|
|
2655
|
+
errors: [message],
|
|
2656
|
+
notes: rollback.backupPath ? [`\u5DF2\u4ECE\u5907\u4EFD\u6062\u590D config.toml\uFF08${rollback.backupPath} \u5DF2\u5220\u9664\uFF09`] : void 0
|
|
2657
|
+
};
|
|
2658
|
+
}
|
|
2659
|
+
throw err;
|
|
3574
2660
|
}
|
|
3575
2661
|
}
|
|
3576
2662
|
async uninstall(ref, opts = {}) {
|
|
@@ -3595,7 +2681,7 @@ var CodexInstaller = class {
|
|
|
3595
2681
|
};
|
|
3596
2682
|
}
|
|
3597
2683
|
const cfgPath = codexConfigTomlPath(this.home);
|
|
3598
|
-
if (!await
|
|
2684
|
+
if (!await pathExists2(cfgPath)) {
|
|
3599
2685
|
return {
|
|
3600
2686
|
target: "codex",
|
|
3601
2687
|
status: "ok",
|
|
@@ -3687,11 +2773,11 @@ var CodexInstaller = class {
|
|
|
3687
2773
|
ref.name
|
|
3688
2774
|
);
|
|
3689
2775
|
const cfgPath = codexConfigTomlPath(this.home);
|
|
3690
|
-
const preExisted = await
|
|
2776
|
+
const preExisted = await pathExists2(versionDir);
|
|
3691
2777
|
if (preExisted) {
|
|
3692
|
-
await
|
|
2778
|
+
await rm(versionDir, { recursive: true, force: true });
|
|
3693
2779
|
}
|
|
3694
|
-
await
|
|
2780
|
+
await mkdir2(versionDir, { recursive: true });
|
|
3695
2781
|
rollback.createdVersionDir = versionDir;
|
|
3696
2782
|
const dstSkills = codexPluginSkillsDir(versionDir);
|
|
3697
2783
|
const writtenFiles = [versionDir];
|
|
@@ -3729,7 +2815,7 @@ var CodexInstaller = class {
|
|
|
3729
2815
|
);
|
|
3730
2816
|
if (mcpContent) {
|
|
3731
2817
|
const mcpPath = codexPluginMcpPath(versionDir);
|
|
3732
|
-
await
|
|
2818
|
+
await writeFileAtomic(mcpPath, serializeMcpJson(mcpContent));
|
|
3733
2819
|
writtenFiles.push(mcpPath);
|
|
3734
2820
|
mcpJsonRef = "./.mcp.json";
|
|
3735
2821
|
mcpKeys = listMcpKeys(mcpContent);
|
|
@@ -3739,7 +2825,7 @@ var CodexInstaller = class {
|
|
|
3739
2825
|
}
|
|
3740
2826
|
const pluginJsonObj = buildCodexPluginJson(plugin.manifest, mcpJsonRef);
|
|
3741
2827
|
const pluginJsonPath = codexPluginManifestPath(versionDir);
|
|
3742
|
-
await
|
|
2828
|
+
await writeFileAtomic(
|
|
3743
2829
|
pluginJsonPath,
|
|
3744
2830
|
`${JSON.stringify(pluginJsonObj, null, 2)}
|
|
3745
2831
|
`
|
|
@@ -3857,12 +2943,12 @@ var CodexInstaller = class {
|
|
|
3857
2943
|
const srcMcp = plugin.manifest.mcpServers;
|
|
3858
2944
|
if (srcMcp !== void 0 && srcMcp !== null) {
|
|
3859
2945
|
if (typeof srcMcp === "string") {
|
|
3860
|
-
const destPath =
|
|
2946
|
+
const destPath = resolve3(versionDir, srcMcp);
|
|
3861
2947
|
files.push(destPath);
|
|
3862
|
-
const srcPath =
|
|
3863
|
-
const rel = pathRelative(
|
|
2948
|
+
const srcPath = resolve3(plugin.sourceDir, srcMcp);
|
|
2949
|
+
const rel = pathRelative(resolve3(plugin.sourceDir), srcPath);
|
|
3864
2950
|
const traversal = rel === ".." || rel.startsWith("..") || rel.startsWith("/");
|
|
3865
|
-
const exists = await
|
|
2951
|
+
const exists = await pathExists2(srcPath);
|
|
3866
2952
|
if (traversal || !exists) {
|
|
3867
2953
|
dryRunStatus = "failed";
|
|
3868
2954
|
dryRunErrors.push(
|
|
@@ -3870,7 +2956,7 @@ var CodexInstaller = class {
|
|
|
3870
2956
|
);
|
|
3871
2957
|
} else {
|
|
3872
2958
|
try {
|
|
3873
|
-
const raw = await
|
|
2959
|
+
const raw = await readFile2(srcPath, "utf8");
|
|
3874
2960
|
const parsed = JSON.parse(raw);
|
|
3875
2961
|
if (parsed.mcpServers && typeof parsed.mcpServers === "object") {
|
|
3876
2962
|
mcpKeys = Object.keys(parsed.mcpServers).sort();
|
|
@@ -3956,18 +3042,18 @@ async function rollbackInstall(home, rollback) {
|
|
|
3956
3042
|
}
|
|
3957
3043
|
);
|
|
3958
3044
|
} else if (rollback.preExistingConfig === false) {
|
|
3959
|
-
await
|
|
3045
|
+
await rm(cfgPath, { force: true }).catch(() => {
|
|
3960
3046
|
});
|
|
3961
3047
|
}
|
|
3962
3048
|
if (rollback.createdVersionDir) {
|
|
3963
|
-
await
|
|
3049
|
+
await rm(rollback.createdVersionDir, {
|
|
3964
3050
|
recursive: true,
|
|
3965
3051
|
force: true
|
|
3966
3052
|
}).catch(() => {
|
|
3967
3053
|
});
|
|
3968
3054
|
}
|
|
3969
3055
|
if (rollback.createdMarketplacePluginDir) {
|
|
3970
|
-
await
|
|
3056
|
+
await rm(rollback.createdMarketplacePluginDir, {
|
|
3971
3057
|
recursive: true,
|
|
3972
3058
|
force: true
|
|
3973
3059
|
}).catch(() => {
|
|
@@ -4005,16 +3091,16 @@ function assertSafePathComponents(ref, version) {
|
|
|
4005
3091
|
}
|
|
4006
3092
|
async function copyAuthorProvidedMcp(sourceDir, versionDir, relativeRef) {
|
|
4007
3093
|
if (typeof relativeRef !== "string" || relativeRef.length === 0) return null;
|
|
4008
|
-
const srcPath =
|
|
4009
|
-
const rel = pathRelative(
|
|
3094
|
+
const srcPath = resolve3(sourceDir, relativeRef);
|
|
3095
|
+
const rel = pathRelative(resolve3(sourceDir), srcPath);
|
|
4010
3096
|
if (rel === ".." || rel.startsWith("..") || rel.startsWith("/")) {
|
|
4011
3097
|
return null;
|
|
4012
3098
|
}
|
|
4013
|
-
if (!await
|
|
4014
|
-
const destPath =
|
|
4015
|
-
await
|
|
4016
|
-
const raw = await
|
|
4017
|
-
await
|
|
3099
|
+
if (!await pathExists2(srcPath)) return null;
|
|
3100
|
+
const destPath = resolve3(versionDir, relativeRef);
|
|
3101
|
+
await mkdir2(dirname2(destPath), { recursive: true });
|
|
3102
|
+
const raw = await readFile2(srcPath, "utf8");
|
|
3103
|
+
await writeFileAtomic(destPath, raw);
|
|
4018
3104
|
let mcpKeys = [];
|
|
4019
3105
|
let mcpServers = {};
|
|
4020
3106
|
try {
|
|
@@ -4036,7 +3122,7 @@ async function planCodexSkills(plugin) {
|
|
|
4036
3122
|
const usedNames = /* @__PURE__ */ new Set();
|
|
4037
3123
|
const coveredAliases = /* @__PURE__ */ new Set();
|
|
4038
3124
|
const commandFiles = await listMarkdownFiles(
|
|
4039
|
-
|
|
3125
|
+
resolve3(plugin.sourceDir, "commands"),
|
|
4040
3126
|
{
|
|
4041
3127
|
recursive: false
|
|
4042
3128
|
}
|
|
@@ -4046,19 +3132,19 @@ async function planCodexSkills(plugin) {
|
|
|
4046
3132
|
commandFiles.map((file2) => markdownSkillAlias(plugin.manifest.name, file2))
|
|
4047
3133
|
)
|
|
4048
3134
|
);
|
|
4049
|
-
const nativeSkillsDir =
|
|
3135
|
+
const nativeSkillsDir = resolve3(plugin.sourceDir, "skills");
|
|
4050
3136
|
if (await hasCodexSkillDir(nativeSkillsDir)) {
|
|
4051
3137
|
for (const name of await listCodexSkillNames(nativeSkillsDir)) {
|
|
4052
3138
|
usedNames.add(name);
|
|
4053
3139
|
coveredAliases.add(skillAlias(plugin.manifest.name, name));
|
|
4054
3140
|
sources.push({
|
|
4055
3141
|
kind: "copy-skill",
|
|
4056
|
-
sourceDir:
|
|
3142
|
+
sourceDir: resolve3(nativeSkillsDir, name),
|
|
4057
3143
|
skillName: name
|
|
4058
3144
|
});
|
|
4059
3145
|
}
|
|
4060
3146
|
} else {
|
|
4061
|
-
const dotSkillsDir =
|
|
3147
|
+
const dotSkillsDir = resolve3(plugin.sourceDir, ".skills");
|
|
4062
3148
|
const claudeSkills = await listMarkdownFiles(dotSkillsDir);
|
|
4063
3149
|
const claudeAliases = /* @__PURE__ */ new Map();
|
|
4064
3150
|
for (const file2 of claudeSkills) {
|
|
@@ -4097,7 +3183,7 @@ async function planCodexSkills(plugin) {
|
|
|
4097
3183
|
async function materializeCodexSkills(plan, dstSkills) {
|
|
4098
3184
|
for (const source of plan.sources) {
|
|
4099
3185
|
if (source.kind === "copy-skill") {
|
|
4100
|
-
await
|
|
3186
|
+
await cp(source.sourceDir, resolve3(dstSkills, source.skillName), {
|
|
4101
3187
|
recursive: true,
|
|
4102
3188
|
dereference: false,
|
|
4103
3189
|
preserveTimestamps: true,
|
|
@@ -4105,10 +3191,10 @@ async function materializeCodexSkills(plan, dstSkills) {
|
|
|
4105
3191
|
});
|
|
4106
3192
|
continue;
|
|
4107
3193
|
}
|
|
4108
|
-
const raw = await
|
|
3194
|
+
const raw = await readFile2(source.sourceFile, "utf8");
|
|
4109
3195
|
const normalized = normalizeSkillMarkdown(raw, source.skillName);
|
|
4110
|
-
await
|
|
4111
|
-
|
|
3196
|
+
await writeFileAtomic(
|
|
3197
|
+
resolve3(dstSkills, source.skillName, "SKILL.md"),
|
|
4112
3198
|
normalized
|
|
4113
3199
|
);
|
|
4114
3200
|
}
|
|
@@ -4184,35 +3270,35 @@ async function hasCodexSkillDir(skillsDir) {
|
|
|
4184
3270
|
return (await listCodexSkillNames(skillsDir)).length > 0;
|
|
4185
3271
|
}
|
|
4186
3272
|
async function listCodexSkillNames(skillsDir) {
|
|
4187
|
-
if (!await
|
|
3273
|
+
if (!await isDir(skillsDir)) return [];
|
|
4188
3274
|
let entries;
|
|
4189
3275
|
try {
|
|
4190
|
-
entries = await
|
|
3276
|
+
entries = await readdir(skillsDir, { withFileTypes: true });
|
|
4191
3277
|
} catch {
|
|
4192
3278
|
return [];
|
|
4193
3279
|
}
|
|
4194
3280
|
const names = [];
|
|
4195
3281
|
for (const entry of entries) {
|
|
4196
3282
|
if (!entry.isDirectory()) continue;
|
|
4197
|
-
if (await
|
|
3283
|
+
if (await pathExists2(resolve3(skillsDir, entry.name, "SKILL.md"))) {
|
|
4198
3284
|
names.push(entry.name);
|
|
4199
3285
|
}
|
|
4200
3286
|
}
|
|
4201
3287
|
return names.sort();
|
|
4202
3288
|
}
|
|
4203
3289
|
async function listMarkdownFiles(dir, opts = {}) {
|
|
4204
|
-
if (!await
|
|
3290
|
+
if (!await isDir(dir)) return [];
|
|
4205
3291
|
const recursive = opts.recursive !== false;
|
|
4206
3292
|
const out = [];
|
|
4207
3293
|
async function walk2(current) {
|
|
4208
3294
|
let entries;
|
|
4209
3295
|
try {
|
|
4210
|
-
entries = await
|
|
3296
|
+
entries = await readdir(current, { withFileTypes: true });
|
|
4211
3297
|
} catch {
|
|
4212
3298
|
return;
|
|
4213
3299
|
}
|
|
4214
3300
|
for (const entry of entries) {
|
|
4215
|
-
const abs =
|
|
3301
|
+
const abs = resolve3(current, entry.name);
|
|
4216
3302
|
if (entry.isDirectory()) {
|
|
4217
3303
|
if (recursive) await walk2(abs);
|
|
4218
3304
|
continue;
|
|
@@ -4237,10 +3323,10 @@ function uniqueSkillName(used, desired) {
|
|
|
4237
3323
|
return candidate;
|
|
4238
3324
|
}
|
|
4239
3325
|
async function markdownSkillAlias(pluginName, filePath) {
|
|
4240
|
-
const triggerAlias = extractTriggerAlias(await
|
|
3326
|
+
const triggerAlias = extractTriggerAlias(await readFile2(filePath, "utf8"));
|
|
4241
3327
|
return skillAlias(
|
|
4242
3328
|
pluginName,
|
|
4243
|
-
triggerAlias ??
|
|
3329
|
+
triggerAlias ?? basename2(filePath, extname(filePath))
|
|
4244
3330
|
);
|
|
4245
3331
|
}
|
|
4246
3332
|
function markdownPathAlias(pluginName, baseDir, filePath) {
|
|
@@ -4266,30 +3352,30 @@ function slugify(value) {
|
|
|
4266
3352
|
const slug = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
4267
3353
|
return slug.length > 0 ? slug : "skill";
|
|
4268
3354
|
}
|
|
4269
|
-
async function
|
|
4270
|
-
await
|
|
3355
|
+
async function writeFileAtomic(filePath, content) {
|
|
3356
|
+
await mkdir2(dirname2(filePath), { recursive: true });
|
|
4271
3357
|
const tmp = `${filePath}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
4272
3358
|
try {
|
|
4273
|
-
await
|
|
4274
|
-
const { rename:
|
|
4275
|
-
await
|
|
3359
|
+
await writeFile2(tmp, content, { encoding: "utf8", flag: "w" });
|
|
3360
|
+
const { rename: rename7 } = await import("fs/promises");
|
|
3361
|
+
await rename7(tmp, filePath);
|
|
4276
3362
|
} catch (err) {
|
|
4277
|
-
await
|
|
3363
|
+
await rm(tmp, { force: true }).catch(() => {
|
|
4278
3364
|
});
|
|
4279
3365
|
throw err;
|
|
4280
3366
|
}
|
|
4281
3367
|
}
|
|
4282
|
-
async function
|
|
3368
|
+
async function pathExists2(p) {
|
|
4283
3369
|
try {
|
|
4284
|
-
await
|
|
3370
|
+
await access(p, fsConstants.F_OK);
|
|
4285
3371
|
return true;
|
|
4286
3372
|
} catch {
|
|
4287
3373
|
return false;
|
|
4288
3374
|
}
|
|
4289
3375
|
}
|
|
4290
|
-
async function
|
|
3376
|
+
async function isDir(p) {
|
|
4291
3377
|
try {
|
|
4292
|
-
const s = await
|
|
3378
|
+
const s = await stat(p);
|
|
4293
3379
|
return s.isDirectory();
|
|
4294
3380
|
} catch {
|
|
4295
3381
|
return false;
|
|
@@ -4304,28 +3390,28 @@ function parsePluginKey(key) {
|
|
|
4304
3390
|
return { name: key.slice(0, at), marketplace: key.slice(at + 1) };
|
|
4305
3391
|
}
|
|
4306
3392
|
async function isFullyInstalledMirror(home, ref) {
|
|
4307
|
-
const mktDir =
|
|
3393
|
+
const mktDir = resolve3(
|
|
4308
3394
|
codexHomeDir(home),
|
|
4309
3395
|
"plugins",
|
|
4310
3396
|
"marketplaces",
|
|
4311
3397
|
ref.marketplace
|
|
4312
3398
|
);
|
|
4313
|
-
const pluginDir =
|
|
4314
|
-
if (!await
|
|
4315
|
-
const manifestPath =
|
|
4316
|
-
if (await
|
|
3399
|
+
const pluginDir = resolve3(mktDir, "plugins", ref.name);
|
|
3400
|
+
if (!await isDir(pluginDir)) return false;
|
|
3401
|
+
const manifestPath = resolve3(pluginDir, ".codex-plugin", "plugin.json");
|
|
3402
|
+
if (await pathExists2(manifestPath)) {
|
|
4317
3403
|
try {
|
|
4318
|
-
const raw = await
|
|
3404
|
+
const raw = await readFile2(manifestPath, "utf8");
|
|
4319
3405
|
const parsed = JSON.parse(raw);
|
|
4320
3406
|
if (parsed["x-rush-mirror"] === "stub") return false;
|
|
4321
3407
|
} catch {
|
|
4322
3408
|
return false;
|
|
4323
3409
|
}
|
|
4324
3410
|
}
|
|
4325
|
-
const mcpPath =
|
|
4326
|
-
if (await
|
|
3411
|
+
const mcpPath = resolve3(pluginDir, ".mcp.json");
|
|
3412
|
+
if (await pathExists2(mcpPath)) {
|
|
4327
3413
|
try {
|
|
4328
|
-
const raw = await
|
|
3414
|
+
const raw = await readFile2(mcpPath, "utf8");
|
|
4329
3415
|
if (/\$\{[^}]+\}/.test(raw)) return false;
|
|
4330
3416
|
} catch {
|
|
4331
3417
|
return false;
|
|
@@ -4334,30 +3420,30 @@ async function isFullyInstalledMirror(home, ref) {
|
|
|
4334
3420
|
return true;
|
|
4335
3421
|
}
|
|
4336
3422
|
async function detectInstalledVersion(home, ref) {
|
|
4337
|
-
const baseDir =
|
|
3423
|
+
const baseDir = resolve3(
|
|
4338
3424
|
codexHomeDir(home),
|
|
4339
3425
|
"plugins",
|
|
4340
3426
|
"cache",
|
|
4341
3427
|
ref.marketplace,
|
|
4342
3428
|
ref.name
|
|
4343
3429
|
);
|
|
4344
|
-
if (!await
|
|
3430
|
+
if (!await isDir(baseDir)) return null;
|
|
4345
3431
|
let entries;
|
|
4346
3432
|
try {
|
|
4347
|
-
entries = await
|
|
3433
|
+
entries = await readdir(baseDir);
|
|
4348
3434
|
} catch {
|
|
4349
3435
|
return null;
|
|
4350
3436
|
}
|
|
4351
3437
|
let best = null;
|
|
4352
3438
|
for (const entry of entries) {
|
|
4353
|
-
const versionDir =
|
|
4354
|
-
const manifestPath =
|
|
4355
|
-
if (!await
|
|
3439
|
+
const versionDir = resolve3(baseDir, entry);
|
|
3440
|
+
const manifestPath = resolve3(versionDir, ".codex-plugin", "plugin.json");
|
|
3441
|
+
if (!await pathExists2(manifestPath)) continue;
|
|
4356
3442
|
try {
|
|
4357
|
-
const raw = await
|
|
3443
|
+
const raw = await readFile2(manifestPath, "utf8");
|
|
4358
3444
|
const obj = JSON.parse(raw);
|
|
4359
3445
|
const version = typeof obj.version === "string" && obj.version.length > 0 ? obj.version : entry;
|
|
4360
|
-
const s = await
|
|
3446
|
+
const s = await stat(versionDir);
|
|
4361
3447
|
if (!best || s.mtimeMs > best.mtimeMs) {
|
|
4362
3448
|
best = { version, mtimeMs: s.mtimeMs };
|
|
4363
3449
|
}
|
|
@@ -4379,28 +3465,28 @@ function defaultRushAiBinaryResolver() {
|
|
|
4379
3465
|
}
|
|
4380
3466
|
|
|
4381
3467
|
// src/marketplaces/cache.ts
|
|
4382
|
-
import { randomUUID
|
|
4383
|
-
import { constants as
|
|
3468
|
+
import { randomUUID } from "crypto";
|
|
3469
|
+
import { constants as fsConstants4 } from "fs";
|
|
4384
3470
|
import {
|
|
4385
|
-
access as
|
|
4386
|
-
mkdir as
|
|
4387
|
-
readdir as
|
|
4388
|
-
readFile as
|
|
4389
|
-
rename
|
|
4390
|
-
rm as
|
|
4391
|
-
writeFile as
|
|
3471
|
+
access as access4,
|
|
3472
|
+
mkdir as mkdir3,
|
|
3473
|
+
readdir as readdir2,
|
|
3474
|
+
readFile as readFile4,
|
|
3475
|
+
rename,
|
|
3476
|
+
rm as rm3,
|
|
3477
|
+
writeFile as writeFile3
|
|
4392
3478
|
} from "fs/promises";
|
|
4393
3479
|
import { homedir as homedir4 } from "os";
|
|
4394
|
-
import { dirname as
|
|
3480
|
+
import { dirname as dirname3, resolve as resolve7 } from "path";
|
|
4395
3481
|
|
|
4396
3482
|
// src/marketplaces/directory.ts
|
|
4397
3483
|
import { stat as fsStat } from "fs/promises";
|
|
4398
|
-
import { resolve as
|
|
3484
|
+
import { resolve as resolve5 } from "path";
|
|
4399
3485
|
|
|
4400
3486
|
// src/marketplaces/manifest.ts
|
|
4401
|
-
import { constants as
|
|
4402
|
-
import { access as
|
|
4403
|
-
import { resolve as
|
|
3487
|
+
import { constants as fsConstants2 } from "fs";
|
|
3488
|
+
import { access as access2, readFile as readFile3 } from "fs/promises";
|
|
3489
|
+
import { resolve as resolve4 } from "path";
|
|
4404
3490
|
|
|
4405
3491
|
// src/marketplaces/source.ts
|
|
4406
3492
|
import { isAbsolute as isAbsolute2 } from "path";
|
|
@@ -4599,9 +3685,9 @@ var MarketplaceCorruptError = class extends MarketplaceError {
|
|
|
4599
3685
|
}
|
|
4600
3686
|
};
|
|
4601
3687
|
async function readMarketplaceJson(rootDir) {
|
|
4602
|
-
const filePath =
|
|
3688
|
+
const filePath = resolve4(rootDir, MARKETPLACE_MANIFEST_RELATIVE_PATH);
|
|
4603
3689
|
try {
|
|
4604
|
-
await
|
|
3690
|
+
await access2(filePath, fsConstants2.R_OK);
|
|
4605
3691
|
} catch {
|
|
4606
3692
|
throw new MarketplaceCorruptError(
|
|
4607
3693
|
filePath,
|
|
@@ -4609,7 +3695,7 @@ async function readMarketplaceJson(rootDir) {
|
|
|
4609
3695
|
"file does not exist or is not readable"
|
|
4610
3696
|
);
|
|
4611
3697
|
}
|
|
4612
|
-
const raw = await
|
|
3698
|
+
const raw = await readFile3(filePath, "utf8");
|
|
4613
3699
|
return parseMarketplaceJson(raw, filePath);
|
|
4614
3700
|
}
|
|
4615
3701
|
function parseMarketplaceJson(raw, sourcePathForError) {
|
|
@@ -4675,7 +3761,7 @@ async function resolveDirectoryMarketplace(source, name) {
|
|
|
4675
3761
|
"marketplace name must be non-empty string"
|
|
4676
3762
|
);
|
|
4677
3763
|
}
|
|
4678
|
-
const rootDir =
|
|
3764
|
+
const rootDir = resolve5(source.path);
|
|
4679
3765
|
let stats;
|
|
4680
3766
|
try {
|
|
4681
3767
|
stats = await fsStat(rootDir);
|
|
@@ -4714,9 +3800,9 @@ async function ensureDirectoryExists(source) {
|
|
|
4714
3800
|
|
|
4715
3801
|
// src/marketplaces/github.ts
|
|
4716
3802
|
import { spawn } from "child_process";
|
|
4717
|
-
import { constants as
|
|
4718
|
-
import { access as
|
|
4719
|
-
import { resolve as
|
|
3803
|
+
import { constants as fsConstants3 } from "fs";
|
|
3804
|
+
import { access as access3, rm as rm2 } from "fs/promises";
|
|
3805
|
+
import { resolve as resolve6 } from "path";
|
|
4720
3806
|
var GitCloneFailedError = class extends MarketplaceError {
|
|
4721
3807
|
constructor(source, exitCode, stderr) {
|
|
4722
3808
|
super(
|
|
@@ -4769,7 +3855,7 @@ async function cloneGithubMarketplace(source, cacheDir, name, opts) {
|
|
|
4769
3855
|
if (existing === "missing") {
|
|
4770
3856
|
await runClone(runner, url, cacheDir, source);
|
|
4771
3857
|
} else if (existing === "not-git") {
|
|
4772
|
-
await
|
|
3858
|
+
await rm2(cacheDir, { recursive: true, force: true });
|
|
4773
3859
|
await runClone(runner, url, cacheDir, source);
|
|
4774
3860
|
} else {
|
|
4775
3861
|
await runUpdate(runner, cacheDir, source);
|
|
@@ -4777,7 +3863,7 @@ async function cloneGithubMarketplace(source, cacheDir, name, opts) {
|
|
|
4777
3863
|
const manifest = await readMarketplaceJson(cacheDir);
|
|
4778
3864
|
return {
|
|
4779
3865
|
name,
|
|
4780
|
-
rootDir:
|
|
3866
|
+
rootDir: resolve6(cacheDir),
|
|
4781
3867
|
source,
|
|
4782
3868
|
manifest
|
|
4783
3869
|
};
|
|
@@ -4785,264 +3871,66 @@ async function cloneGithubMarketplace(source, cacheDir, name, opts) {
|
|
|
4785
3871
|
async function updateGithubMarketplace(source, cacheDir, name, opts) {
|
|
4786
3872
|
const runner = opts?.runner ?? defaultGitRunner;
|
|
4787
3873
|
const state = await detectCacheState(cacheDir);
|
|
4788
|
-
if (state !== "git") {
|
|
4789
|
-
return cloneGithubMarketplace(source, cacheDir, name, opts);
|
|
4790
|
-
}
|
|
4791
|
-
await runUpdate(runner, cacheDir, source);
|
|
4792
|
-
const manifest = await readMarketplaceJson(cacheDir);
|
|
4793
|
-
return {
|
|
4794
|
-
name,
|
|
4795
|
-
rootDir: resolve7(cacheDir),
|
|
4796
|
-
source,
|
|
4797
|
-
manifest
|
|
4798
|
-
};
|
|
4799
|
-
}
|
|
4800
|
-
async function runClone(runner, url, cacheDir, source) {
|
|
4801
|
-
const args = ["clone", "--depth", "1"];
|
|
4802
|
-
if (source.ref) {
|
|
4803
|
-
args.push("--branch", source.ref);
|
|
4804
|
-
}
|
|
4805
|
-
args.push("--", url, cacheDir);
|
|
4806
|
-
const result = await runner(args);
|
|
4807
|
-
if (result.exitCode !== 0) {
|
|
4808
|
-
throw new GitCloneFailedError(source, result.exitCode, result.stderr);
|
|
4809
|
-
}
|
|
4810
|
-
}
|
|
4811
|
-
async function runUpdate(runner, cacheDir, source) {
|
|
4812
|
-
const refArg = source.ref ?? "HEAD";
|
|
4813
|
-
const fetchResult = await runner(
|
|
4814
|
-
["fetch", "--depth", "1", "origin", refArg],
|
|
4815
|
-
{ cwd: cacheDir }
|
|
4816
|
-
);
|
|
4817
|
-
if (fetchResult.exitCode !== 0) {
|
|
4818
|
-
throw new GitCloneFailedError(
|
|
4819
|
-
source,
|
|
4820
|
-
fetchResult.exitCode,
|
|
4821
|
-
fetchResult.stderr
|
|
4822
|
-
);
|
|
4823
|
-
}
|
|
4824
|
-
const checkoutResult = await runner(
|
|
4825
|
-
["checkout", "--quiet", "--detach", "FETCH_HEAD"],
|
|
4826
|
-
{ cwd: cacheDir }
|
|
4827
|
-
);
|
|
4828
|
-
if (checkoutResult.exitCode !== 0) {
|
|
4829
|
-
throw new GitCloneFailedError(
|
|
4830
|
-
source,
|
|
4831
|
-
checkoutResult.exitCode,
|
|
4832
|
-
checkoutResult.stderr
|
|
4833
|
-
);
|
|
4834
|
-
}
|
|
4835
|
-
}
|
|
4836
|
-
async function detectCacheState(cacheDir) {
|
|
4837
|
-
try {
|
|
4838
|
-
await access5(cacheDir, fsConstants5.F_OK);
|
|
4839
|
-
} catch {
|
|
4840
|
-
return "missing";
|
|
4841
|
-
}
|
|
4842
|
-
try {
|
|
4843
|
-
await access5(resolve7(cacheDir, ".git"), fsConstants5.F_OK);
|
|
4844
|
-
return "git";
|
|
4845
|
-
} catch {
|
|
4846
|
-
return "not-git";
|
|
4847
|
-
}
|
|
4848
|
-
}
|
|
4849
|
-
|
|
4850
|
-
// src/marketplaces/rush.ts
|
|
4851
|
-
import { execFileSync as execFileSync4 } from "child_process";
|
|
4852
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
4853
|
-
import { mkdir as mkdir5, rename as rename3, rm as rm5, stat as stat4, writeFile as writeFile5 } from "fs/promises";
|
|
4854
|
-
import { resolve as resolve8 } from "path";
|
|
4855
|
-
async function fetchRushMarketplace(source, opts = {}) {
|
|
4856
|
-
const fetchFn = opts.fetchFn ?? fetch;
|
|
4857
|
-
const url = `${inferProtocol(source.host)}${source.host}/api/marketplace`;
|
|
4858
|
-
const headers = {
|
|
4859
|
-
Accept: "application/json"
|
|
4860
|
-
};
|
|
4861
|
-
if (opts.token) {
|
|
4862
|
-
headers.Authorization = `Bearer ${opts.token}`;
|
|
4863
|
-
}
|
|
4864
|
-
const response = await fetchFn(url, { headers });
|
|
4865
|
-
if (!response.ok) {
|
|
4866
|
-
throw new Error(
|
|
4867
|
-
`Failed to fetch rush marketplace from ${url}: ${response.status} ${response.statusText}`
|
|
4868
|
-
);
|
|
4869
|
-
}
|
|
4870
|
-
const data = await response.json();
|
|
4871
|
-
if (!data.plugins || !Array.isArray(data.plugins)) {
|
|
4872
|
-
throw new Error(
|
|
4873
|
-
`Invalid marketplace response from ${url}: missing 'plugins' array`
|
|
4874
|
-
);
|
|
4875
|
-
}
|
|
4876
|
-
return data;
|
|
4877
|
-
}
|
|
4878
|
-
async function fetchRushPlugin(source, pluginSlug, opts = {}) {
|
|
4879
|
-
const fetchFn = opts.fetchFn ?? fetch;
|
|
4880
|
-
const url = `${inferProtocol(source.host)}${source.host}/api/marketplace/${encodeURIComponent(pluginSlug)}`;
|
|
4881
|
-
const headers = {
|
|
4882
|
-
Accept: "application/json"
|
|
4883
|
-
};
|
|
4884
|
-
if (opts.token) {
|
|
4885
|
-
headers.Authorization = `Bearer ${opts.token}`;
|
|
4886
|
-
}
|
|
4887
|
-
const response = await fetchFn(url, { headers });
|
|
4888
|
-
if (response.status === 401) {
|
|
4889
|
-
throw new Error(
|
|
4890
|
-
`Plugin '${pluginSlug}' requires authentication. Run 'rush-ai login' first.`
|
|
4891
|
-
);
|
|
4892
|
-
}
|
|
4893
|
-
if (response.status === 404) {
|
|
4894
|
-
throw new Error(
|
|
4895
|
-
`Plugin '${pluginSlug}' not found in rush marketplace at ${source.host}`
|
|
4896
|
-
);
|
|
4897
|
-
}
|
|
4898
|
-
if (!response.ok) {
|
|
4899
|
-
throw new Error(
|
|
4900
|
-
`Failed to fetch plugin '${pluginSlug}' from ${url}: ${response.status} ${response.statusText}`
|
|
4901
|
-
);
|
|
4902
|
-
}
|
|
4903
|
-
return await response.json();
|
|
4904
|
-
}
|
|
4905
|
-
async function materializeRushPlugin(source, pluginSlug, targetDir, opts = {}) {
|
|
4906
|
-
validateSlug(pluginSlug, "plugin slug");
|
|
4907
|
-
const manifest = await fetchRushPlugin(source, pluginSlug, opts);
|
|
4908
|
-
let mcpServers = {};
|
|
4909
|
-
if (manifest.mcpServers && Object.keys(manifest.mcpServers).length > 0) {
|
|
4910
|
-
mcpServers = { ...manifest.mcpServers };
|
|
4911
|
-
if (opts.secrets && Object.keys(opts.secrets).length > 0) {
|
|
4912
|
-
mcpServers = substituteMcpSecrets(mcpServers, opts.secrets);
|
|
4913
|
-
}
|
|
4914
|
-
}
|
|
4915
|
-
const pluginJson = {
|
|
4916
|
-
name: manifest.name,
|
|
4917
|
-
version: manifest.version || "1.0.0",
|
|
4918
|
-
description: manifest.description,
|
|
4919
|
-
...Object.keys(mcpServers).length > 0 ? { mcpServers } : {}
|
|
4920
|
-
};
|
|
4921
|
-
const tmpDir = `${targetDir}.${randomUUID3()}.tmp`;
|
|
4922
|
-
try {
|
|
4923
|
-
const pluginJsonDir = resolve8(tmpDir, ".claude-plugin");
|
|
4924
|
-
await mkdir5(pluginJsonDir, { recursive: true });
|
|
4925
|
-
await writeFile5(
|
|
4926
|
-
resolve8(pluginJsonDir, "plugin.json"),
|
|
4927
|
-
`${JSON.stringify(pluginJson, null, 2)}
|
|
4928
|
-
`,
|
|
4929
|
-
"utf8"
|
|
4930
|
-
);
|
|
4931
|
-
if (manifest.skills && manifest.skills.length > 0) {
|
|
4932
|
-
await writeFile5(
|
|
4933
|
-
resolve8(tmpDir, "skills.json"),
|
|
4934
|
-
'{"skills":{}}\n',
|
|
4935
|
-
"utf8"
|
|
4936
|
-
);
|
|
4937
|
-
const registryUrl = `${inferProtocol(source.host)}${source.host}`;
|
|
4938
|
-
for (const skill of manifest.skills) {
|
|
4939
|
-
try {
|
|
4940
|
-
const args = [
|
|
4941
|
-
"-y",
|
|
4942
|
-
"reskill@latest",
|
|
4943
|
-
"install",
|
|
4944
|
-
skill.name,
|
|
4945
|
-
"--no-save",
|
|
4946
|
-
"--skip-manifest",
|
|
4947
|
-
"-y",
|
|
4948
|
-
"-f",
|
|
4949
|
-
"-r",
|
|
4950
|
-
registryUrl,
|
|
4951
|
-
...opts.token ? ["-t", opts.token] : []
|
|
4952
|
-
];
|
|
4953
|
-
execFileSync4("npx", args, {
|
|
4954
|
-
cwd: tmpDir,
|
|
4955
|
-
timeout: 6e4,
|
|
4956
|
-
stdio: "pipe"
|
|
4957
|
-
});
|
|
4958
|
-
} catch (err) {
|
|
4959
|
-
const stderr = err && typeof err === "object" && "stderr" in err ? err.stderr?.toString() ?? "" : "";
|
|
4960
|
-
if (stderr.includes("401") || stderr.includes("403") || stderr.includes("Unauthorized") || stderr.includes("Forbidden")) {
|
|
4961
|
-
throw new SkillAuthError(skill.name, 401);
|
|
4962
|
-
}
|
|
4963
|
-
output.warn(
|
|
4964
|
-
` Warning: failed to install skill '${skill.name}': ${err instanceof Error ? err.message : String(err)}`
|
|
4965
|
-
);
|
|
4966
|
-
}
|
|
4967
|
-
}
|
|
4968
|
-
}
|
|
4969
|
-
const dotSkillsDir = resolve8(tmpDir, ".skills");
|
|
4970
|
-
const skillsDir = resolve8(tmpDir, "skills");
|
|
4971
|
-
if (await stat4(dotSkillsDir).then((s) => s.isDirectory()).catch(() => false)) {
|
|
4972
|
-
if (!await stat4(skillsDir).then((s) => s.isDirectory()).catch(() => false)) {
|
|
4973
|
-
await rename3(dotSkillsDir, skillsDir);
|
|
4974
|
-
}
|
|
4975
|
-
}
|
|
4976
|
-
await rm5(resolve8(tmpDir, "skills.json"), { force: true });
|
|
4977
|
-
await rm5(resolve8(tmpDir, ".cursor"), { recursive: true, force: true });
|
|
4978
|
-
await rm5(resolve8(tmpDir, ".claude"), { recursive: true, force: true });
|
|
4979
|
-
await rm5(resolve8(tmpDir, ".codex"), { recursive: true, force: true });
|
|
4980
|
-
await rm5(resolve8(tmpDir, ".github"), { recursive: true, force: true });
|
|
4981
|
-
await rm5(resolve8(tmpDir, ".opencode"), { recursive: true, force: true });
|
|
4982
|
-
await rm5(targetDir, { recursive: true, force: true });
|
|
4983
|
-
await mkdir5(resolve8(targetDir, ".."), { recursive: true });
|
|
4984
|
-
await rename3(tmpDir, targetDir);
|
|
4985
|
-
} catch (err) {
|
|
4986
|
-
await rm5(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
4987
|
-
});
|
|
4988
|
-
throw err;
|
|
3874
|
+
if (state !== "git") {
|
|
3875
|
+
return cloneGithubMarketplace(source, cacheDir, name, opts);
|
|
4989
3876
|
}
|
|
4990
|
-
|
|
3877
|
+
await runUpdate(runner, cacheDir, source);
|
|
3878
|
+
const manifest = await readMarketplaceJson(cacheDir);
|
|
3879
|
+
return {
|
|
3880
|
+
name,
|
|
3881
|
+
rootDir: resolve6(cacheDir),
|
|
3882
|
+
source,
|
|
3883
|
+
manifest
|
|
3884
|
+
};
|
|
4991
3885
|
}
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
);
|
|
4997
|
-
this.name = "SkillAuthError";
|
|
3886
|
+
async function runClone(runner, url, cacheDir, source) {
|
|
3887
|
+
const args = ["clone", "--depth", "1"];
|
|
3888
|
+
if (source.ref) {
|
|
3889
|
+
args.push("--branch", source.ref);
|
|
4998
3890
|
}
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
if (!serverConfig || typeof serverConfig !== "object") {
|
|
5004
|
-
result[serverName] = serverConfig;
|
|
5005
|
-
continue;
|
|
5006
|
-
}
|
|
5007
|
-
const cfg = { ...serverConfig };
|
|
5008
|
-
if (cfg.env && typeof cfg.env === "object") {
|
|
5009
|
-
const env = {};
|
|
5010
|
-
for (const [k, v] of Object.entries(cfg.env)) {
|
|
5011
|
-
env[k] = substituteValue(v, secrets);
|
|
5012
|
-
}
|
|
5013
|
-
cfg.env = env;
|
|
5014
|
-
}
|
|
5015
|
-
if (cfg.headers && typeof cfg.headers === "object") {
|
|
5016
|
-
const headers = {};
|
|
5017
|
-
for (const [k, v] of Object.entries(
|
|
5018
|
-
cfg.headers
|
|
5019
|
-
)) {
|
|
5020
|
-
headers[k] = substituteValue(v, secrets);
|
|
5021
|
-
}
|
|
5022
|
-
cfg.headers = headers;
|
|
5023
|
-
}
|
|
5024
|
-
result[serverName] = cfg;
|
|
3891
|
+
args.push("--", url, cacheDir);
|
|
3892
|
+
const result = await runner(args);
|
|
3893
|
+
if (result.exitCode !== 0) {
|
|
3894
|
+
throw new GitCloneFailedError(source, result.exitCode, result.stderr);
|
|
5025
3895
|
}
|
|
5026
|
-
return result;
|
|
5027
|
-
}
|
|
5028
|
-
function substituteValue(value, secrets) {
|
|
5029
|
-
return value.replace(/\$\{([^}]+)\}/g, (match, key) => {
|
|
5030
|
-
return key in secrets ? secrets[key] : match;
|
|
5031
|
-
});
|
|
5032
3896
|
}
|
|
5033
|
-
function
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
3897
|
+
async function runUpdate(runner, cacheDir, source) {
|
|
3898
|
+
const refArg = source.ref ?? "HEAD";
|
|
3899
|
+
const fetchResult = await runner(
|
|
3900
|
+
["fetch", "--depth", "1", "origin", refArg],
|
|
3901
|
+
{ cwd: cacheDir }
|
|
3902
|
+
);
|
|
3903
|
+
if (fetchResult.exitCode !== 0) {
|
|
3904
|
+
throw new GitCloneFailedError(
|
|
3905
|
+
source,
|
|
3906
|
+
fetchResult.exitCode,
|
|
3907
|
+
fetchResult.stderr
|
|
3908
|
+
);
|
|
3909
|
+
}
|
|
3910
|
+
const checkoutResult = await runner(
|
|
3911
|
+
["checkout", "--quiet", "--detach", "FETCH_HEAD"],
|
|
3912
|
+
{ cwd: cacheDir }
|
|
3913
|
+
);
|
|
3914
|
+
if (checkoutResult.exitCode !== 0) {
|
|
3915
|
+
throw new GitCloneFailedError(
|
|
3916
|
+
source,
|
|
3917
|
+
checkoutResult.exitCode,
|
|
3918
|
+
checkoutResult.stderr
|
|
5037
3919
|
);
|
|
5038
3920
|
}
|
|
5039
3921
|
}
|
|
5040
|
-
function
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
3922
|
+
async function detectCacheState(cacheDir) {
|
|
3923
|
+
try {
|
|
3924
|
+
await access3(cacheDir, fsConstants3.F_OK);
|
|
3925
|
+
} catch {
|
|
3926
|
+
return "missing";
|
|
3927
|
+
}
|
|
3928
|
+
try {
|
|
3929
|
+
await access3(resolve6(cacheDir, ".git"), fsConstants3.F_OK);
|
|
3930
|
+
return "git";
|
|
3931
|
+
} catch {
|
|
3932
|
+
return "not-git";
|
|
5044
3933
|
}
|
|
5045
|
-
return "https://";
|
|
5046
3934
|
}
|
|
5047
3935
|
|
|
5048
3936
|
// src/marketplaces/cache.ts
|
|
@@ -5071,10 +3959,10 @@ var MarketplaceCache = class {
|
|
|
5071
3959
|
gitRunner;
|
|
5072
3960
|
constructor(opts = {}) {
|
|
5073
3961
|
if (opts.cacheBaseDir) {
|
|
5074
|
-
this.cacheBaseDir =
|
|
3962
|
+
this.cacheBaseDir = resolve7(opts.cacheBaseDir);
|
|
5075
3963
|
} else {
|
|
5076
3964
|
const home = opts.home ?? homedir4();
|
|
5077
|
-
this.cacheBaseDir =
|
|
3965
|
+
this.cacheBaseDir = resolve7(home, MARKETPLACE_CACHE_RELATIVE_PATH);
|
|
5078
3966
|
}
|
|
5079
3967
|
if (opts.gitRunner) {
|
|
5080
3968
|
this.gitRunner = opts.gitRunner;
|
|
@@ -5087,7 +3975,7 @@ var MarketplaceCache = class {
|
|
|
5087
3975
|
`Invalid marketplace name '${name}'. Names must be non-empty and cannot contain '/' or '..'.`
|
|
5088
3976
|
);
|
|
5089
3977
|
}
|
|
5090
|
-
return
|
|
3978
|
+
return resolve7(this.cacheBaseDir, name);
|
|
5091
3979
|
}
|
|
5092
3980
|
/**
|
|
5093
3981
|
* 添加一个 marketplace 到本地 cache。
|
|
@@ -5127,7 +4015,7 @@ var MarketplaceCache = class {
|
|
|
5127
4015
|
return resolved;
|
|
5128
4016
|
}
|
|
5129
4017
|
case "github": {
|
|
5130
|
-
await
|
|
4018
|
+
await mkdir3(dirname3(targetDir), { recursive: true });
|
|
5131
4019
|
const resolved = await cloneGithubMarketplace(
|
|
5132
4020
|
source,
|
|
5133
4021
|
targetDir,
|
|
@@ -5156,10 +4044,10 @@ var MarketplaceCache = class {
|
|
|
5156
4044
|
*/
|
|
5157
4045
|
async remove(name) {
|
|
5158
4046
|
const target = this.pathFor(name);
|
|
5159
|
-
if (!await
|
|
4047
|
+
if (!await pathExists3(target)) {
|
|
5160
4048
|
return;
|
|
5161
4049
|
}
|
|
5162
|
-
await
|
|
4050
|
+
await rm3(target, { recursive: true, force: true });
|
|
5163
4051
|
}
|
|
5164
4052
|
/**
|
|
5165
4053
|
* 重新 fetch 远端 source(仅对 github: / git: / rush:// / npm:)。
|
|
@@ -5207,10 +4095,10 @@ var MarketplaceCache = class {
|
|
|
5207
4095
|
* v1 暂不单列损坏项;若未来需要,可加 `options.includeCorrupt` 返回 warning。
|
|
5208
4096
|
*/
|
|
5209
4097
|
async list() {
|
|
5210
|
-
if (!await
|
|
4098
|
+
if (!await pathExists3(this.cacheBaseDir)) {
|
|
5211
4099
|
return [];
|
|
5212
4100
|
}
|
|
5213
|
-
const entries = await
|
|
4101
|
+
const entries = await readdir2(this.cacheBaseDir, { withFileTypes: true });
|
|
5214
4102
|
const result = [];
|
|
5215
4103
|
for (const entry of entries) {
|
|
5216
4104
|
if (!entry.isDirectory()) continue;
|
|
@@ -5218,7 +4106,7 @@ var MarketplaceCache = class {
|
|
|
5218
4106
|
if (!meta) continue;
|
|
5219
4107
|
result.push({
|
|
5220
4108
|
name: entry.name,
|
|
5221
|
-
path:
|
|
4109
|
+
path: resolve7(this.cacheBaseDir, entry.name),
|
|
5222
4110
|
source: meta.source,
|
|
5223
4111
|
cachedAt: meta.cachedAt
|
|
5224
4112
|
});
|
|
@@ -5291,20 +4179,20 @@ var MarketplaceCache = class {
|
|
|
5291
4179
|
};
|
|
5292
4180
|
const resolvedName = name ?? defaultNameFromSource(source);
|
|
5293
4181
|
const rootDir = this.pathFor(resolvedName);
|
|
5294
|
-
const manifestDir =
|
|
5295
|
-
await
|
|
5296
|
-
await atomicWriteJson(
|
|
4182
|
+
const manifestDir = resolve7(rootDir, ".claude-plugin");
|
|
4183
|
+
await mkdir3(manifestDir, { recursive: true });
|
|
4184
|
+
await atomicWriteJson(resolve7(manifestDir, "marketplace.json"), manifest);
|
|
5297
4185
|
return { name: resolvedName, rootDir, source, manifest };
|
|
5298
4186
|
}
|
|
5299
4187
|
metaPathFor(name) {
|
|
5300
|
-
return
|
|
4188
|
+
return resolve7(this.pathFor(name), CACHE_META_FILENAME);
|
|
5301
4189
|
}
|
|
5302
4190
|
async readMeta(name) {
|
|
5303
4191
|
const metaPath = this.metaPathFor(name);
|
|
5304
|
-
if (!await
|
|
4192
|
+
if (!await pathExists3(metaPath)) {
|
|
5305
4193
|
return null;
|
|
5306
4194
|
}
|
|
5307
|
-
const raw = await
|
|
4195
|
+
const raw = await readFile4(metaPath, "utf8");
|
|
5308
4196
|
let parsed;
|
|
5309
4197
|
try {
|
|
5310
4198
|
parsed = JSON.parse(raw);
|
|
@@ -5334,7 +4222,7 @@ var MarketplaceCache = class {
|
|
|
5334
4222
|
}
|
|
5335
4223
|
async writeMeta(name, source) {
|
|
5336
4224
|
const metaPath = this.metaPathFor(name);
|
|
5337
|
-
await
|
|
4225
|
+
await mkdir3(dirname3(metaPath), { recursive: true });
|
|
5338
4226
|
const payload = {
|
|
5339
4227
|
sourceRaw: source.raw,
|
|
5340
4228
|
cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5344,9 +4232,9 @@ var MarketplaceCache = class {
|
|
|
5344
4232
|
await atomicWriteJson(metaPath, payload);
|
|
5345
4233
|
}
|
|
5346
4234
|
};
|
|
5347
|
-
async function
|
|
4235
|
+
async function pathExists3(p) {
|
|
5348
4236
|
try {
|
|
5349
|
-
await
|
|
4237
|
+
await access4(p, fsConstants4.F_OK);
|
|
5350
4238
|
return true;
|
|
5351
4239
|
} catch {
|
|
5352
4240
|
return false;
|
|
@@ -5355,237 +4243,17 @@ async function pathExists5(p) {
|
|
|
5355
4243
|
async function atomicWriteJson(filePath, payload) {
|
|
5356
4244
|
const content = `${JSON.stringify(payload, null, 2)}
|
|
5357
4245
|
`;
|
|
5358
|
-
const tmp = `${filePath}.${
|
|
4246
|
+
const tmp = `${filePath}.${randomUUID()}.tmp`;
|
|
5359
4247
|
try {
|
|
5360
|
-
await
|
|
5361
|
-
await
|
|
4248
|
+
await writeFile3(tmp, content, { encoding: "utf8" });
|
|
4249
|
+
await rename(tmp, filePath);
|
|
5362
4250
|
} catch (err) {
|
|
5363
|
-
await
|
|
4251
|
+
await rm3(tmp, { force: true }).catch(() => {
|
|
5364
4252
|
});
|
|
5365
4253
|
throw err;
|
|
5366
4254
|
}
|
|
5367
4255
|
}
|
|
5368
4256
|
|
|
5369
|
-
// src/commands/marketplace/_codex-content-loader.ts
|
|
5370
|
-
import { constants as fsConstants7 } from "fs";
|
|
5371
|
-
import { access as access7, readFile as readFile7, stat as stat5 } from "fs/promises";
|
|
5372
|
-
import { resolve as pathResolve2, sep } from "path";
|
|
5373
|
-
function pickContentLoader(resolved, opts = {}) {
|
|
5374
|
-
switch (resolved.source.kind) {
|
|
5375
|
-
case "rush":
|
|
5376
|
-
return buildRushPluginContentLoader(resolved.source, opts);
|
|
5377
|
-
case "directory":
|
|
5378
|
-
case "github":
|
|
5379
|
-
return buildLocalCachePluginContentLoader(resolved);
|
|
5380
|
-
default:
|
|
5381
|
-
return async () => ({});
|
|
5382
|
-
}
|
|
5383
|
-
}
|
|
5384
|
-
function buildRushPluginContentLoader(source, opts = {}) {
|
|
5385
|
-
const token = opts.token ?? getAuthToken();
|
|
5386
|
-
const fetchOpts = {};
|
|
5387
|
-
if (opts.fetchFn !== void 0) fetchOpts.fetchFn = opts.fetchFn;
|
|
5388
|
-
if (token !== void 0 && token !== null) fetchOpts.token = token;
|
|
5389
|
-
const webBase = inferWebBase(source.host);
|
|
5390
|
-
const fetchFn = opts.fetchFn ?? globalThis.fetch;
|
|
5391
|
-
return async (entry) => {
|
|
5392
|
-
if (typeof entry.name !== "string" || entry.name.length === 0) return {};
|
|
5393
|
-
try {
|
|
5394
|
-
const detail = await fetchRushPlugin(source, entry.name, fetchOpts);
|
|
5395
|
-
const manifest = pickManifestFields(
|
|
5396
|
-
detail
|
|
5397
|
-
);
|
|
5398
|
-
const hasMcp2 = detail.mcpServers && Object.keys(detail.mcpServers).length > 0;
|
|
5399
|
-
const pluginWebUrl = `${webBase}/next/plugins/${encodeURIComponent(
|
|
5400
|
-
entry.name
|
|
5401
|
-
)}`;
|
|
5402
|
-
const skillEntries = Array.isArray(detail.skills) && detail.skills.length > 0 ? detail.skills.filter(
|
|
5403
|
-
(s) => !!s && typeof s.name === "string" && s.name.length > 0
|
|
5404
|
-
) : [];
|
|
5405
|
-
const skillsPlaceholders = [];
|
|
5406
|
-
for (const s of skillEntries) {
|
|
5407
|
-
const url = `${webBase}/next/skills/${encodeURIComponent(s.name)}`;
|
|
5408
|
-
const mdUrl = `${url}.md`;
|
|
5409
|
-
let rawSkillMd;
|
|
5410
|
-
try {
|
|
5411
|
-
const headers = {
|
|
5412
|
-
Accept: "text/markdown,text/plain,*/*"
|
|
5413
|
-
};
|
|
5414
|
-
if (token) headers.Authorization = `Bearer ${token}`;
|
|
5415
|
-
const res = await fetchFn(mdUrl, { headers });
|
|
5416
|
-
if (res.ok) {
|
|
5417
|
-
const text = await res.text();
|
|
5418
|
-
if (text.length > 0) rawSkillMd = text;
|
|
5419
|
-
}
|
|
5420
|
-
} catch {
|
|
5421
|
-
}
|
|
5422
|
-
skillsPlaceholders.push({
|
|
5423
|
-
name: s.name,
|
|
5424
|
-
url,
|
|
5425
|
-
...rawSkillMd !== void 0 ? { rawSkillMd } : {}
|
|
5426
|
-
});
|
|
5427
|
-
}
|
|
5428
|
-
return {
|
|
5429
|
-
...manifest !== void 0 ? { manifest } : {},
|
|
5430
|
-
...hasMcp2 ? { mcpServers: detail.mcpServers } : {},
|
|
5431
|
-
...skillsPlaceholders.length > 0 ? { skillsPlaceholders } : {},
|
|
5432
|
-
pluginWebUrl
|
|
5433
|
-
};
|
|
5434
|
-
} catch {
|
|
5435
|
-
return {};
|
|
5436
|
-
}
|
|
5437
|
-
};
|
|
5438
|
-
}
|
|
5439
|
-
function inferWebBase(host) {
|
|
5440
|
-
const hostname = host.split(":")[0];
|
|
5441
|
-
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0") {
|
|
5442
|
-
return `http://${host}`;
|
|
5443
|
-
}
|
|
5444
|
-
return `https://${host}`;
|
|
5445
|
-
}
|
|
5446
|
-
function buildLocalCachePluginContentLoader(resolved) {
|
|
5447
|
-
return async (entry) => {
|
|
5448
|
-
const relPath = extractLocalPluginPath(entry);
|
|
5449
|
-
if (relPath === null) return {};
|
|
5450
|
-
const pluginDir = pathResolve2(resolved.rootDir, relPath);
|
|
5451
|
-
const root = pathResolve2(resolved.rootDir);
|
|
5452
|
-
const rootWithSep = root.endsWith(sep) ? root : root + sep;
|
|
5453
|
-
const safe = pluginDir === root || pluginDir.startsWith(rootWithSep);
|
|
5454
|
-
if (!safe) return {};
|
|
5455
|
-
let manifest;
|
|
5456
|
-
let mcpServers;
|
|
5457
|
-
let skillsSourceDir;
|
|
5458
|
-
try {
|
|
5459
|
-
const pj = await readFile7(
|
|
5460
|
-
pathResolve2(pluginDir, ".claude-plugin", "plugin.json"),
|
|
5461
|
-
"utf8"
|
|
5462
|
-
);
|
|
5463
|
-
const parsed = JSON.parse(pj);
|
|
5464
|
-
manifest = pickManifestFields(parsed);
|
|
5465
|
-
const inline = parsed.mcpServers;
|
|
5466
|
-
if (inline && typeof inline === "object" && !Array.isArray(inline) && Object.keys(inline).length > 0) {
|
|
5467
|
-
mcpServers = inline;
|
|
5468
|
-
}
|
|
5469
|
-
} catch {
|
|
5470
|
-
}
|
|
5471
|
-
try {
|
|
5472
|
-
const raw = await readFile7(pathResolve2(pluginDir, ".mcp.json"), "utf8");
|
|
5473
|
-
const parsed = JSON.parse(raw);
|
|
5474
|
-
let servers = null;
|
|
5475
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && "mcpServers" in parsed && typeof parsed.mcpServers === "object") {
|
|
5476
|
-
servers = parsed.mcpServers;
|
|
5477
|
-
} else if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
5478
|
-
servers = parsed;
|
|
5479
|
-
}
|
|
5480
|
-
if (servers && Object.keys(servers).length > 0) {
|
|
5481
|
-
mcpServers = servers;
|
|
5482
|
-
}
|
|
5483
|
-
} catch {
|
|
5484
|
-
}
|
|
5485
|
-
const skillsDir = pathResolve2(pluginDir, "skills");
|
|
5486
|
-
if (await isDir3(skillsDir)) {
|
|
5487
|
-
skillsSourceDir = skillsDir;
|
|
5488
|
-
}
|
|
5489
|
-
return {
|
|
5490
|
-
...manifest !== void 0 ? { manifest } : {},
|
|
5491
|
-
...mcpServers !== void 0 ? { mcpServers } : {},
|
|
5492
|
-
...skillsSourceDir !== void 0 ? { skillsSourceDir } : {}
|
|
5493
|
-
};
|
|
5494
|
-
};
|
|
5495
|
-
}
|
|
5496
|
-
function extractLocalPluginPath(entry) {
|
|
5497
|
-
if (typeof entry.source === "string") {
|
|
5498
|
-
return entry.source;
|
|
5499
|
-
}
|
|
5500
|
-
if (entry.source && typeof entry.source === "object" && !Array.isArray(entry.source)) {
|
|
5501
|
-
const obj = entry.source;
|
|
5502
|
-
if (typeof obj.url === "string" && obj.url.length > 0) {
|
|
5503
|
-
return null;
|
|
5504
|
-
}
|
|
5505
|
-
if (typeof obj.path === "string" && obj.path.length > 0) {
|
|
5506
|
-
return obj.path;
|
|
5507
|
-
}
|
|
5508
|
-
}
|
|
5509
|
-
if (entry.source === void 0 && typeof entry.name === "string" && entry.name.length > 0) {
|
|
5510
|
-
return `plugins/${entry.name}`;
|
|
5511
|
-
}
|
|
5512
|
-
return null;
|
|
5513
|
-
}
|
|
5514
|
-
async function isDir3(p) {
|
|
5515
|
-
try {
|
|
5516
|
-
const s = await stat5(p);
|
|
5517
|
-
return s.isDirectory();
|
|
5518
|
-
} catch {
|
|
5519
|
-
return false;
|
|
5520
|
-
}
|
|
5521
|
-
}
|
|
5522
|
-
function pickManifestFields(raw) {
|
|
5523
|
-
const m = {};
|
|
5524
|
-
if (typeof raw.description === "string" && raw.description.length > 0) {
|
|
5525
|
-
m.description = raw.description;
|
|
5526
|
-
}
|
|
5527
|
-
if (typeof raw.version === "string" && raw.version.length > 0) {
|
|
5528
|
-
m.version = raw.version;
|
|
5529
|
-
}
|
|
5530
|
-
if (typeof raw.homepage === "string" && raw.homepage.length > 0) {
|
|
5531
|
-
m.homepage = raw.homepage;
|
|
5532
|
-
}
|
|
5533
|
-
if (typeof raw.license === "string" && raw.license.length > 0) {
|
|
5534
|
-
m.license = raw.license;
|
|
5535
|
-
}
|
|
5536
|
-
if (Array.isArray(raw.keywords)) {
|
|
5537
|
-
const kw = raw.keywords.filter(
|
|
5538
|
-
(x) => typeof x === "string" && x.length > 0
|
|
5539
|
-
);
|
|
5540
|
-
if (kw.length > 0) m.keywords = kw;
|
|
5541
|
-
}
|
|
5542
|
-
if (raw.author && typeof raw.author === "object" && !Array.isArray(raw.author)) {
|
|
5543
|
-
const a = raw.author;
|
|
5544
|
-
const author = {};
|
|
5545
|
-
if (typeof a.name === "string") author.name = a.name;
|
|
5546
|
-
if (typeof a.email === "string") author.email = a.email;
|
|
5547
|
-
if (typeof a.url === "string") author.url = a.url;
|
|
5548
|
-
if (Object.keys(author).length > 0) m.author = author;
|
|
5549
|
-
}
|
|
5550
|
-
if (raw.interface && typeof raw.interface === "object" && !Array.isArray(raw.interface)) {
|
|
5551
|
-
const iface = pickInterfaceFields(raw.interface);
|
|
5552
|
-
if (iface !== void 0) m.interface = iface;
|
|
5553
|
-
}
|
|
5554
|
-
return Object.keys(m).length > 0 ? m : void 0;
|
|
5555
|
-
}
|
|
5556
|
-
function pickInterfaceFields(raw) {
|
|
5557
|
-
const out = {};
|
|
5558
|
-
const stringKeys = [
|
|
5559
|
-
"displayName",
|
|
5560
|
-
"shortDescription",
|
|
5561
|
-
"longDescription",
|
|
5562
|
-
"developerName",
|
|
5563
|
-
"category",
|
|
5564
|
-
"websiteURL",
|
|
5565
|
-
"privacyPolicyURL",
|
|
5566
|
-
"termsOfServiceURL",
|
|
5567
|
-
"brandColor",
|
|
5568
|
-
"composerIcon",
|
|
5569
|
-
"logo"
|
|
5570
|
-
];
|
|
5571
|
-
for (const key of stringKeys) {
|
|
5572
|
-
const v = raw[key];
|
|
5573
|
-
if (typeof v === "string" && v.length > 0) {
|
|
5574
|
-
out[key] = v;
|
|
5575
|
-
}
|
|
5576
|
-
}
|
|
5577
|
-
for (const key of ["capabilities", "defaultPrompt", "screenshots"]) {
|
|
5578
|
-
const v = raw[key];
|
|
5579
|
-
if (Array.isArray(v)) {
|
|
5580
|
-
const arr = v.filter(
|
|
5581
|
-
(x) => typeof x === "string" && x.length > 0
|
|
5582
|
-
);
|
|
5583
|
-
if (arr.length > 0) out[key] = arr;
|
|
5584
|
-
}
|
|
5585
|
-
}
|
|
5586
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
5587
|
-
}
|
|
5588
|
-
|
|
5589
4257
|
// src/commands/marketplace/add.ts
|
|
5590
4258
|
async function runAdd(input) {
|
|
5591
4259
|
let source;
|
|
@@ -5717,19 +4385,19 @@ function registerAddCommand(group, _root) {
|
|
|
5717
4385
|
}
|
|
5718
4386
|
|
|
5719
4387
|
// src/installers/registry.ts
|
|
5720
|
-
import { randomUUID as
|
|
5721
|
-
import { constants as
|
|
4388
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
4389
|
+
import { constants as fsConstants5 } from "fs";
|
|
5722
4390
|
import {
|
|
5723
|
-
access as
|
|
5724
|
-
mkdir as
|
|
5725
|
-
readFile as
|
|
5726
|
-
rename as
|
|
5727
|
-
rm as
|
|
5728
|
-
stat as
|
|
5729
|
-
writeFile as
|
|
4391
|
+
access as access5,
|
|
4392
|
+
mkdir as mkdir4,
|
|
4393
|
+
readFile as readFile5,
|
|
4394
|
+
rename as rename2,
|
|
4395
|
+
rm as rm4,
|
|
4396
|
+
stat as stat2,
|
|
4397
|
+
writeFile as writeFile4
|
|
5730
4398
|
} from "fs/promises";
|
|
5731
4399
|
import { homedir as homedir6 } from "os";
|
|
5732
|
-
import { dirname as
|
|
4400
|
+
import { dirname as dirname4, resolve as resolve8 } from "path";
|
|
5733
4401
|
var REGISTRY_RELATIVE_PATH = ".rush/plugins/registry.json";
|
|
5734
4402
|
var REGISTRY_SCHEMA_VERSION = 1;
|
|
5735
4403
|
var RushRegistryError = class extends Error {
|
|
@@ -5809,35 +4477,35 @@ function assertRegistryShape(raw, filePath) {
|
|
|
5809
4477
|
);
|
|
5810
4478
|
}
|
|
5811
4479
|
}
|
|
5812
|
-
async function
|
|
4480
|
+
async function pathExists4(p) {
|
|
5813
4481
|
try {
|
|
5814
|
-
await
|
|
4482
|
+
await access5(p, fsConstants5.F_OK);
|
|
5815
4483
|
return true;
|
|
5816
4484
|
} catch {
|
|
5817
4485
|
return false;
|
|
5818
4486
|
}
|
|
5819
4487
|
}
|
|
5820
|
-
async function
|
|
5821
|
-
await
|
|
5822
|
-
const tmp = `${filePath}.${
|
|
4488
|
+
async function atomicWrite(filePath, content) {
|
|
4489
|
+
await mkdir4(dirname4(filePath), { recursive: true });
|
|
4490
|
+
const tmp = `${filePath}.${randomUUID2()}.tmp`;
|
|
5823
4491
|
try {
|
|
5824
|
-
await
|
|
5825
|
-
await
|
|
4492
|
+
await writeFile4(tmp, content, { encoding: "utf8", flag: "w" });
|
|
4493
|
+
await rename2(tmp, filePath);
|
|
5826
4494
|
} catch (err) {
|
|
5827
|
-
await
|
|
4495
|
+
await rm4(tmp, { force: true }).catch(() => {
|
|
5828
4496
|
});
|
|
5829
4497
|
throw err;
|
|
5830
4498
|
}
|
|
5831
4499
|
}
|
|
5832
4500
|
async function loadFromPath(filePath) {
|
|
5833
|
-
if (!await
|
|
4501
|
+
if (!await pathExists4(filePath)) {
|
|
5834
4502
|
return {
|
|
5835
4503
|
data: { schemaVersion: REGISTRY_SCHEMA_VERSION, plugins: {} },
|
|
5836
4504
|
mtimeMs: null
|
|
5837
4505
|
};
|
|
5838
4506
|
}
|
|
5839
|
-
const stats = await
|
|
5840
|
-
const raw = await
|
|
4507
|
+
const stats = await stat2(filePath);
|
|
4508
|
+
const raw = await readFile5(filePath, "utf8");
|
|
5841
4509
|
let parsed;
|
|
5842
4510
|
try {
|
|
5843
4511
|
parsed = JSON.parse(raw);
|
|
@@ -5886,7 +4554,7 @@ var RushRegistryStore = class _RushRegistryStore {
|
|
|
5886
4554
|
*/
|
|
5887
4555
|
static resolvePath(opts) {
|
|
5888
4556
|
const home = opts?.home ?? homedir6();
|
|
5889
|
-
return
|
|
4557
|
+
return resolve8(home, REGISTRY_RELATIVE_PATH);
|
|
5890
4558
|
}
|
|
5891
4559
|
/**
|
|
5892
4560
|
* 读取 registry 文件。
|
|
@@ -5937,20 +4605,20 @@ var RushRegistryStore = class _RushRegistryStore {
|
|
|
5937
4605
|
async saveWithRetry(attempt) {
|
|
5938
4606
|
this.assertWriteableSchema();
|
|
5939
4607
|
if (this.loadedMtimeMs === null) {
|
|
5940
|
-
if (await
|
|
4608
|
+
if (await pathExists4(this.filePath)) {
|
|
5941
4609
|
if (attempt === 0) {
|
|
5942
4610
|
await this.reloadAndMerge();
|
|
5943
4611
|
return this.saveWithRetry(1);
|
|
5944
4612
|
}
|
|
5945
4613
|
throw new RushRegistryConflictError(this.filePath);
|
|
5946
4614
|
}
|
|
5947
|
-
await
|
|
5948
|
-
const stats2 = await
|
|
4615
|
+
await atomicWrite(this.filePath, this.serialize());
|
|
4616
|
+
const stats2 = await stat2(this.filePath);
|
|
5949
4617
|
this.loadedMtimeMs = stats2.mtimeMs;
|
|
5950
4618
|
this.deletedPluginKeys.clear();
|
|
5951
4619
|
return;
|
|
5952
4620
|
}
|
|
5953
|
-
const stats = await
|
|
4621
|
+
const stats = await stat2(this.filePath).catch(() => null);
|
|
5954
4622
|
if (stats === null) {
|
|
5955
4623
|
if (attempt === 0) {
|
|
5956
4624
|
await this.reloadAndMerge();
|
|
@@ -5965,8 +4633,8 @@ var RushRegistryStore = class _RushRegistryStore {
|
|
|
5965
4633
|
}
|
|
5966
4634
|
throw new RushRegistryConflictError(this.filePath);
|
|
5967
4635
|
}
|
|
5968
|
-
await
|
|
5969
|
-
const afterStats = await
|
|
4636
|
+
await atomicWrite(this.filePath, this.serialize());
|
|
4637
|
+
const afterStats = await stat2(this.filePath);
|
|
5970
4638
|
this.loadedMtimeMs = afterStats.mtimeMs;
|
|
5971
4639
|
this.deletedPluginKeys.clear();
|
|
5972
4640
|
}
|
|
@@ -6134,7 +4802,7 @@ function registerListCommand(group, root) {
|
|
|
6134
4802
|
}
|
|
6135
4803
|
|
|
6136
4804
|
// src/commands/marketplace/remove.ts
|
|
6137
|
-
import { rm as
|
|
4805
|
+
import { rm as rm5 } from "fs/promises";
|
|
6138
4806
|
import { homedir as homedir7 } from "os";
|
|
6139
4807
|
async function runRemove(input) {
|
|
6140
4808
|
const homeOpts = input.home !== void 0 ? { home: input.home } : {};
|
|
@@ -6170,10 +4838,10 @@ async function runRemove(input) {
|
|
|
6170
4838
|
}
|
|
6171
4839
|
async function cleanupCodexMarketplace(home, name, now) {
|
|
6172
4840
|
const codexHome = codexHomeDir(home);
|
|
6173
|
-
if (!await
|
|
4841
|
+
if (!await pathExists3(codexHome)) return "absent";
|
|
6174
4842
|
let didSomething = false;
|
|
6175
4843
|
const cfgPath = codexConfigTomlPath(home);
|
|
6176
|
-
if (await
|
|
4844
|
+
if (await pathExists3(cfgPath)) {
|
|
6177
4845
|
try {
|
|
6178
4846
|
await backupCodexConfig(cfgPath, codexConfigBackupPath(home, now()));
|
|
6179
4847
|
const { data, mtimeMs } = await readCodexConfig(cfgPath);
|
|
@@ -6188,8 +4856,8 @@ async function cleanupCodexMarketplace(home, name, now) {
|
|
|
6188
4856
|
}
|
|
6189
4857
|
}
|
|
6190
4858
|
const mirrorDir = codexMarketplaceDir(home, name);
|
|
6191
|
-
if (await
|
|
6192
|
-
await
|
|
4859
|
+
if (await pathExists3(mirrorDir)) {
|
|
4860
|
+
await rm5(mirrorDir, { recursive: true, force: true }).catch(() => {
|
|
6193
4861
|
});
|
|
6194
4862
|
didSomething = true;
|
|
6195
4863
|
}
|
|
@@ -6566,7 +5234,7 @@ function truncate3(str, max) {
|
|
|
6566
5234
|
function registerMcpCommand(program) {
|
|
6567
5235
|
const mcp = program.command("mcp").description("MCP server and platform MCP discovery");
|
|
6568
5236
|
mcp.command("serve").description("Start the MCP stdio server").action(async () => {
|
|
6569
|
-
const { startMcpServer } = await import("./server-
|
|
5237
|
+
const { startMcpServer } = await import("./server-PGXKRIPY.js");
|
|
6570
5238
|
await startMcpServer();
|
|
6571
5239
|
});
|
|
6572
5240
|
mcp.command("list").alias("ls").description("List available MCP servers on the platform").option("-c, --category <category>", "Filter by category").option("-t, --tag <tag>", "Filter by tag").option("-s, --search <query>", "Search by name or description").option("--transport <type>", "Filter by transport type (stdio|sse|http)").option(
|
|
@@ -6688,7 +5356,7 @@ function registerMcpCommand(program) {
|
|
|
6688
5356
|
"Set credential values (e.g. --set TOKEN=xxx KEY=yyy)"
|
|
6689
5357
|
).action(async (mcpId, opts) => {
|
|
6690
5358
|
const format = resolveFormat(program.opts());
|
|
6691
|
-
const { runMcpInstall, printMcpInstallSummary } = await import("./install-
|
|
5359
|
+
const { runMcpInstall, printMcpInstallSummary } = await import("./install-JQN4BIHX.js");
|
|
6692
5360
|
let setValues;
|
|
6693
5361
|
if (opts.set) {
|
|
6694
5362
|
setValues = {};
|
|
@@ -6749,7 +5417,7 @@ function registerMcpCommand(program) {
|
|
|
6749
5417
|
"Comma-separated targets: claude-desktop,claude-code (default: both)"
|
|
6750
5418
|
).action(async (mcpId, opts) => {
|
|
6751
5419
|
const format = resolveFormat(program.opts());
|
|
6752
|
-
const { runMcpUninstall, printMcpUninstallSummary } = await import("./install-
|
|
5420
|
+
const { runMcpUninstall, printMcpUninstallSummary } = await import("./install-JQN4BIHX.js");
|
|
6753
5421
|
try {
|
|
6754
5422
|
const result = await runMcpUninstall({
|
|
6755
5423
|
mcpId,
|
|
@@ -6783,7 +5451,7 @@ function registerMcpCommand(program) {
|
|
|
6783
5451
|
|
|
6784
5452
|
// src/commands/playbook/index.ts
|
|
6785
5453
|
import { existsSync as existsSync11, readdirSync, readFileSync as readFileSync7 } from "fs";
|
|
6786
|
-
import { basename as
|
|
5454
|
+
import { basename as basename3, resolve as resolve9 } from "path";
|
|
6787
5455
|
var PLAYBOOK_DESCRIPTION = "Print agent usage playbooks (hand-off, agent-shelf, ...) as raw markdown.";
|
|
6788
5456
|
var PLAYBOOK_HELP_AFTER = `
|
|
6789
5457
|
Examples:
|
|
@@ -6800,19 +5468,19 @@ function resolvePlaybooksDir() {
|
|
|
6800
5468
|
const baseDir = import.meta.dirname ?? __dirname;
|
|
6801
5469
|
if (!baseDir) return null;
|
|
6802
5470
|
const candidates = [
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
5471
|
+
resolve9(baseDir, "skills"),
|
|
5472
|
+
resolve9(baseDir, "..", "skills"),
|
|
5473
|
+
resolve9(baseDir, "..", "..", "skills"),
|
|
5474
|
+
resolve9(baseDir, "..", "..", "..", "skills"),
|
|
5475
|
+
resolve9(baseDir, "..", "..", "..", "..", "skills")
|
|
6808
5476
|
];
|
|
6809
5477
|
for (const p of candidates) {
|
|
6810
|
-
if (existsSync11(
|
|
5478
|
+
if (existsSync11(resolve9(p, ".rush-playbooks"))) return p;
|
|
6811
5479
|
}
|
|
6812
5480
|
return null;
|
|
6813
5481
|
}
|
|
6814
5482
|
function listPlaybooks(dir) {
|
|
6815
|
-
return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) =>
|
|
5483
|
+
return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => basename3(f, ".md")).sort();
|
|
6816
5484
|
}
|
|
6817
5485
|
function getAvailablePlaybooks() {
|
|
6818
5486
|
const dir = resolvePlaybooksDir();
|
|
@@ -6837,7 +5505,7 @@ function printPlaybook(name, opts = {}) {
|
|
|
6837
5505
|
return;
|
|
6838
5506
|
}
|
|
6839
5507
|
const target = name ?? "README";
|
|
6840
|
-
const file2 =
|
|
5508
|
+
const file2 = resolve9(dir, `${target}.md`);
|
|
6841
5509
|
if (!existsSync11(file2)) {
|
|
6842
5510
|
output.error(`Unknown playbook "${target}".`);
|
|
6843
5511
|
output.dim(`Available: ${available.join(", ") || "(none)"}`);
|
|
@@ -6853,78 +5521,30 @@ function registerPlaybookCommand(program) {
|
|
|
6853
5521
|
}
|
|
6854
5522
|
|
|
6855
5523
|
// src/commands/plugin/install.ts
|
|
6856
|
-
import { rm as
|
|
6857
|
-
import {
|
|
5524
|
+
import { rm as rm11 } from "fs/promises";
|
|
5525
|
+
import { homedir as homedir17 } from "os";
|
|
5526
|
+
import { resolve as resolve20 } from "path";
|
|
6858
5527
|
|
|
6859
5528
|
// src/installers/claude-code/installer.ts
|
|
6860
|
-
import { randomUUID as
|
|
5529
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
6861
5530
|
import {
|
|
6862
|
-
copyFile
|
|
5531
|
+
copyFile,
|
|
6863
5532
|
lstat,
|
|
6864
|
-
mkdir as
|
|
6865
|
-
readdir as
|
|
6866
|
-
readFile as
|
|
5533
|
+
mkdir as mkdir5,
|
|
5534
|
+
readdir as readdir3,
|
|
5535
|
+
readFile as readFile6,
|
|
6867
5536
|
readlink,
|
|
6868
|
-
rename as
|
|
6869
|
-
rm as
|
|
6870
|
-
stat as
|
|
5537
|
+
rename as rename3,
|
|
5538
|
+
rm as rm6,
|
|
5539
|
+
stat as stat3,
|
|
6871
5540
|
symlink,
|
|
6872
5541
|
unlink
|
|
6873
5542
|
} from "fs/promises";
|
|
6874
5543
|
import { homedir as homedir10 } from "os";
|
|
6875
|
-
import { dirname as
|
|
6876
|
-
|
|
6877
|
-
// src/installers/claude-code/mcp.ts
|
|
6878
|
-
import { execFileSync as execFileSync5 } from "child_process";
|
|
6879
|
-
import { isAbsolute as isAbsolute3 } from "path";
|
|
6880
|
-
var RUSH_AI_PLUGIN_NAME2 = "rush";
|
|
6881
|
-
var RUSH_AI_MARKETPLACE_NAME2 = "rush-marketplace";
|
|
6882
|
-
var RUSH_MCP_SERVER_KEY2 = "rush";
|
|
6883
|
-
function isRushOwnPlugin(ref) {
|
|
6884
|
-
return ref.name === RUSH_AI_PLUGIN_NAME2 && ref.marketplace === RUSH_AI_MARKETPLACE_NAME2;
|
|
6885
|
-
}
|
|
6886
|
-
function normalizeClaudeMcpServers(ref, manifest, resolver = defaultRushBinaryResolver) {
|
|
6887
|
-
const servers = manifest.mcpServers;
|
|
6888
|
-
if (servers === void 0 || servers === null) return void 0;
|
|
6889
|
-
if (typeof servers === "string") {
|
|
6890
|
-
return void 0;
|
|
6891
|
-
}
|
|
6892
|
-
if (!isRushOwnPlugin(ref)) {
|
|
6893
|
-
return { ...servers };
|
|
6894
|
-
}
|
|
6895
|
-
const rushServer = servers[RUSH_MCP_SERVER_KEY2];
|
|
6896
|
-
if (!rushServer) {
|
|
6897
|
-
return { ...servers };
|
|
6898
|
-
}
|
|
6899
|
-
const currentCommand = rushServer.command;
|
|
6900
|
-
if (typeof currentCommand === "string" && isAbsolute3(currentCommand)) {
|
|
6901
|
-
return { ...servers };
|
|
6902
|
-
}
|
|
6903
|
-
const resolved = resolver();
|
|
6904
|
-
if (!resolved || !isAbsolute3(resolved)) {
|
|
6905
|
-
return { ...servers };
|
|
6906
|
-
}
|
|
6907
|
-
return {
|
|
6908
|
-
...servers,
|
|
6909
|
-
[RUSH_MCP_SERVER_KEY2]: { ...rushServer, command: resolved }
|
|
6910
|
-
};
|
|
6911
|
-
}
|
|
6912
|
-
function defaultRushBinaryResolver() {
|
|
6913
|
-
try {
|
|
6914
|
-
const result = execFileSync5("which", ["rush-ai"], {
|
|
6915
|
-
encoding: "utf8",
|
|
6916
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
6917
|
-
}).trim();
|
|
6918
|
-
if (result.length > 0 && isAbsolute3(result)) {
|
|
6919
|
-
return result;
|
|
6920
|
-
}
|
|
6921
|
-
} catch {
|
|
6922
|
-
}
|
|
6923
|
-
return void 0;
|
|
6924
|
-
}
|
|
5544
|
+
import { dirname as dirname5, join as join9, resolve as resolve11 } from "path";
|
|
6925
5545
|
|
|
6926
5546
|
// src/installers/claude-code/paths.ts
|
|
6927
|
-
import { resolve as
|
|
5547
|
+
import { resolve as resolve10 } from "path";
|
|
6928
5548
|
var CLAUDE_DIR = ".claude";
|
|
6929
5549
|
var PLUGINS_SUBDIR = "plugins";
|
|
6930
5550
|
var CACHE_SUBDIR = "cache";
|
|
@@ -6942,27 +5562,27 @@ var ClaudeCodePaths = class {
|
|
|
6942
5562
|
}
|
|
6943
5563
|
/** `<home>/.claude/` */
|
|
6944
5564
|
get claudeDir() {
|
|
6945
|
-
return
|
|
5565
|
+
return resolve10(this.home, CLAUDE_DIR);
|
|
6946
5566
|
}
|
|
6947
5567
|
/** `<home>/.claude/settings.json` */
|
|
6948
5568
|
get settingsJson() {
|
|
6949
|
-
return
|
|
5569
|
+
return resolve10(this.claudeDir, "settings.json");
|
|
6950
5570
|
}
|
|
6951
5571
|
/** `<home>/.claude/plugins/` */
|
|
6952
5572
|
get pluginsDir() {
|
|
6953
|
-
return
|
|
5573
|
+
return resolve10(this.claudeDir, PLUGINS_SUBDIR);
|
|
6954
5574
|
}
|
|
6955
5575
|
/** `<home>/.claude/plugins/known_marketplaces.json` */
|
|
6956
5576
|
get knownMarketplacesJson() {
|
|
6957
|
-
return
|
|
5577
|
+
return resolve10(this.pluginsDir, "known_marketplaces.json");
|
|
6958
5578
|
}
|
|
6959
5579
|
/** `<home>/.claude/plugins/installed_plugins.json` */
|
|
6960
5580
|
get installedPluginsJson() {
|
|
6961
|
-
return
|
|
5581
|
+
return resolve10(this.pluginsDir, "installed_plugins.json");
|
|
6962
5582
|
}
|
|
6963
5583
|
/** `<home>/.claude/plugins/cache/` */
|
|
6964
5584
|
get cacheDir() {
|
|
6965
|
-
return
|
|
5585
|
+
return resolve10(this.pluginsDir, CACHE_SUBDIR);
|
|
6966
5586
|
}
|
|
6967
5587
|
/**
|
|
6968
5588
|
* `<home>/.claude/plugins/marketplaces/`。
|
|
@@ -6972,34 +5592,34 @@ var ClaudeCodePaths = class {
|
|
|
6972
5592
|
* Claude Code 会识别不到 plugin 条目(regression fix,bug #4)。
|
|
6973
5593
|
*/
|
|
6974
5594
|
get marketplacesRootDir() {
|
|
6975
|
-
return
|
|
5595
|
+
return resolve10(this.pluginsDir, "marketplaces");
|
|
6976
5596
|
}
|
|
6977
5597
|
/** `<home>/.claude/plugins/marketplaces/<mkt>/` */
|
|
6978
5598
|
marketplaceInstallDir(marketplace) {
|
|
6979
|
-
return
|
|
5599
|
+
return resolve10(this.marketplacesRootDir, marketplace);
|
|
6980
5600
|
}
|
|
6981
5601
|
/** `<home>/.claude/plugins/cache/<mkt>/` */
|
|
6982
5602
|
marketplaceCacheDir(marketplace) {
|
|
6983
|
-
return
|
|
5603
|
+
return resolve10(this.cacheDir, marketplace);
|
|
6984
5604
|
}
|
|
6985
5605
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/` */
|
|
6986
5606
|
pluginCacheDir(ref) {
|
|
6987
|
-
return
|
|
5607
|
+
return resolve10(this.marketplaceCacheDir(ref.marketplace), ref.name);
|
|
6988
5608
|
}
|
|
6989
5609
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/` */
|
|
6990
5610
|
pluginVersionDir(ref, version) {
|
|
6991
|
-
return
|
|
5611
|
+
return resolve10(this.pluginCacheDir(ref), version);
|
|
6992
5612
|
}
|
|
6993
5613
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/.claude-plugin/plugin.json` */
|
|
6994
5614
|
pluginManifestPath(ref, version) {
|
|
6995
|
-
return
|
|
5615
|
+
return resolve10(
|
|
6996
5616
|
this.pluginVersionDir(ref, version),
|
|
6997
5617
|
PLUGIN_MANIFEST_RELATIVE
|
|
6998
5618
|
);
|
|
6999
5619
|
}
|
|
7000
5620
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/<capability>/` */
|
|
7001
5621
|
capabilityDir(ref, version, capability) {
|
|
7002
|
-
return
|
|
5622
|
+
return resolve10(this.pluginVersionDir(ref, version), capability);
|
|
7003
5623
|
}
|
|
7004
5624
|
};
|
|
7005
5625
|
function pluginKey(ref) {
|
|
@@ -7113,29 +5733,29 @@ var ClaudeCodeInstaller = class {
|
|
|
7113
5733
|
}
|
|
7114
5734
|
try {
|
|
7115
5735
|
const writtenFiles = [];
|
|
7116
|
-
await
|
|
5736
|
+
await mkdir5(pluginVersionDir, { recursive: true });
|
|
7117
5737
|
writtenFiles.push(pluginVersionDir);
|
|
7118
5738
|
for (const cap of CAPABILITY_DIRS) {
|
|
7119
|
-
const srcDir =
|
|
5739
|
+
const srcDir = resolve11(plugin.sourceDir, cap);
|
|
7120
5740
|
if (!await dirExists(srcDir)) continue;
|
|
7121
5741
|
const dstDir = this.paths.capabilityDir(ref, version, cap);
|
|
7122
|
-
await
|
|
5742
|
+
await mkdir5(dstDir, { recursive: true });
|
|
7123
5743
|
writtenFiles.push(dstDir);
|
|
7124
5744
|
const copied = await copyDirTracked(srcDir, dstDir);
|
|
7125
5745
|
writtenFiles.push(...copied);
|
|
7126
5746
|
}
|
|
7127
5747
|
const manifestPath = this.paths.pluginManifestPath(ref, version);
|
|
7128
|
-
const normalizedMcp =
|
|
5748
|
+
const normalizedMcp = await resolveClaudeMcpServers(
|
|
7129
5749
|
ref,
|
|
7130
|
-
plugin
|
|
5750
|
+
plugin,
|
|
7131
5751
|
this.rushBinaryResolver
|
|
7132
5752
|
);
|
|
7133
5753
|
if (normalizedMcp && Object.keys(normalizedMcp).length > 0) {
|
|
7134
|
-
const mcpJsonPath =
|
|
5754
|
+
const mcpJsonPath = resolve11(pluginVersionDir, ".mcp.json");
|
|
7135
5755
|
await writeJsonFile(mcpJsonPath, { mcpServers: normalizedMcp });
|
|
7136
5756
|
writtenFiles.push(mcpJsonPath);
|
|
7137
5757
|
}
|
|
7138
|
-
const pluginJsonContent =
|
|
5758
|
+
const pluginJsonContent = buildPluginJson(plugin, normalizedMcp);
|
|
7139
5759
|
await writeJsonFile(manifestPath, pluginJsonContent);
|
|
7140
5760
|
writtenFiles.push(manifestPath);
|
|
7141
5761
|
const mcpKeys = normalizedMcp ? Object.keys(normalizedMcp) : [];
|
|
@@ -7260,7 +5880,7 @@ var ClaudeCodeInstaller = class {
|
|
|
7260
5880
|
files.push(mktDir);
|
|
7261
5881
|
} else if (kind === "dir") {
|
|
7262
5882
|
if (await pathExists(join9(mktDir, ".rush-marketplace-source.json"))) {
|
|
7263
|
-
await
|
|
5883
|
+
await rm6(mktDir, { recursive: true, force: true });
|
|
7264
5884
|
files.push(mktDir);
|
|
7265
5885
|
}
|
|
7266
5886
|
}
|
|
@@ -7331,12 +5951,12 @@ var ClaudeCodeInstaller = class {
|
|
|
7331
5951
|
return { written: false, path: "" };
|
|
7332
5952
|
}
|
|
7333
5953
|
const mktDir = this.paths.marketplaceInstallDir(marketplaceName);
|
|
7334
|
-
await
|
|
5954
|
+
await mkdir5(this.paths.marketplacesRootDir, { recursive: true });
|
|
7335
5955
|
const kind = await inodeKind(mktDir);
|
|
7336
5956
|
if (kind === "symlink") {
|
|
7337
5957
|
const target = await readlink(mktDir).catch(() => null);
|
|
7338
|
-
const resolvedTarget = target !== null ?
|
|
7339
|
-
if (resolvedTarget && resolvedTarget ===
|
|
5958
|
+
const resolvedTarget = target !== null ? resolve11(dirname5(mktDir), target) : null;
|
|
5959
|
+
if (resolvedTarget && resolvedTarget === resolve11(src.rootDir)) {
|
|
7340
5960
|
return { written: false, path: mktDir };
|
|
7341
5961
|
}
|
|
7342
5962
|
throw new Error(
|
|
@@ -7369,7 +5989,7 @@ var ClaudeCodeInstaller = class {
|
|
|
7369
5989
|
return { written: true, path: mktDir };
|
|
7370
5990
|
}
|
|
7371
5991
|
await copyDirTracked(src.rootDir, mktDir).catch(async (err) => {
|
|
7372
|
-
await
|
|
5992
|
+
await rm6(mktDir, { recursive: true, force: true }).catch(() => {
|
|
7373
5993
|
});
|
|
7374
5994
|
throw err;
|
|
7375
5995
|
});
|
|
@@ -7405,7 +6025,7 @@ var ClaudeCodeInstaller = class {
|
|
|
7405
6025
|
const pluginVersionDir = this.paths.pluginVersionDir(ref, version);
|
|
7406
6026
|
const dryFiles = [pluginVersionDir];
|
|
7407
6027
|
for (const cap of CAPABILITY_DIRS) {
|
|
7408
|
-
const srcDir =
|
|
6028
|
+
const srcDir = resolve11(plugin.sourceDir, cap);
|
|
7409
6029
|
if (!await dirExists(srcDir)) continue;
|
|
7410
6030
|
const dstDir = this.paths.capabilityDir(ref, version, cap);
|
|
7411
6031
|
dryFiles.push(dstDir);
|
|
@@ -7413,13 +6033,13 @@ var ClaudeCodeInstaller = class {
|
|
|
7413
6033
|
dryFiles.push(...plannedFiles);
|
|
7414
6034
|
}
|
|
7415
6035
|
dryFiles.push(this.paths.pluginManifestPath(ref, version));
|
|
7416
|
-
const normalizedMcpDry =
|
|
6036
|
+
const normalizedMcpDry = await resolveClaudeMcpServers(
|
|
7417
6037
|
ref,
|
|
7418
|
-
plugin
|
|
6038
|
+
plugin,
|
|
7419
6039
|
this.rushBinaryResolver
|
|
7420
6040
|
);
|
|
7421
6041
|
if (normalizedMcpDry && Object.keys(normalizedMcpDry).length > 0) {
|
|
7422
|
-
dryFiles.push(
|
|
6042
|
+
dryFiles.push(resolve11(pluginVersionDir, ".mcp.json"));
|
|
7423
6043
|
}
|
|
7424
6044
|
if (this.marketplaceSource) {
|
|
7425
6045
|
const mktInstallDir = this.paths.marketplaceInstallDir(ref.marketplace);
|
|
@@ -7436,9 +6056,9 @@ var ClaudeCodeInstaller = class {
|
|
|
7436
6056
|
if (await this.wouldSettingsChange(pluginKey(ref))) {
|
|
7437
6057
|
dryFiles.push(this.paths.settingsJson);
|
|
7438
6058
|
}
|
|
7439
|
-
const normalizedMcp =
|
|
6059
|
+
const normalizedMcp = await resolveClaudeMcpServers(
|
|
7440
6060
|
ref,
|
|
7441
|
-
plugin
|
|
6061
|
+
plugin,
|
|
7442
6062
|
this.rushBinaryResolver
|
|
7443
6063
|
);
|
|
7444
6064
|
const mcpKeys = normalizedMcp ? Object.keys(normalizedMcp) : [];
|
|
@@ -7533,8 +6153,8 @@ var ClaudeCodeInstaller = class {
|
|
|
7533
6153
|
const settings = await this.readRawJsonForRollback(this.paths.settingsJson);
|
|
7534
6154
|
let backupVersionDir = null;
|
|
7535
6155
|
if (preexistedVersion === "dir") {
|
|
7536
|
-
backupVersionDir = `${pluginVersionDir}.rollback-${
|
|
7537
|
-
await
|
|
6156
|
+
backupVersionDir = `${pluginVersionDir}.rollback-${randomUUID3()}`;
|
|
6157
|
+
await rename3(pluginVersionDir, backupVersionDir);
|
|
7538
6158
|
}
|
|
7539
6159
|
return {
|
|
7540
6160
|
ref,
|
|
@@ -7555,7 +6175,7 @@ var ClaudeCodeInstaller = class {
|
|
|
7555
6175
|
*/
|
|
7556
6176
|
async finalizeRollback(snap) {
|
|
7557
6177
|
if (snap.backupVersionDir) {
|
|
7558
|
-
await
|
|
6178
|
+
await rm6(snap.backupVersionDir, { recursive: true, force: true });
|
|
7559
6179
|
}
|
|
7560
6180
|
}
|
|
7561
6181
|
/**
|
|
@@ -7568,19 +6188,19 @@ var ClaudeCodeInstaller = class {
|
|
|
7568
6188
|
*/
|
|
7569
6189
|
async applyRollback(snap) {
|
|
7570
6190
|
if (snap.preexistedVersion === "dir" && snap.backupVersionDir) {
|
|
7571
|
-
await
|
|
7572
|
-
await
|
|
6191
|
+
await rm6(snap.pluginVersionDir, { recursive: true, force: true });
|
|
6192
|
+
await rename3(snap.backupVersionDir, snap.pluginVersionDir);
|
|
7573
6193
|
} else if (snap.preexistedVersion === "missing") {
|
|
7574
|
-
await
|
|
6194
|
+
await rm6(snap.pluginVersionDir, { recursive: true, force: true });
|
|
7575
6195
|
}
|
|
7576
6196
|
if (snap.preexistedPlugin === "missing") {
|
|
7577
|
-
await
|
|
6197
|
+
await rm6(this.paths.pluginCacheDir(snap.ref), {
|
|
7578
6198
|
recursive: true,
|
|
7579
6199
|
force: true
|
|
7580
6200
|
});
|
|
7581
6201
|
}
|
|
7582
6202
|
if (snap.preexistedMarketplace === "missing") {
|
|
7583
|
-
await
|
|
6203
|
+
await rm6(this.paths.marketplaceCacheDir(snap.ref.marketplace), {
|
|
7584
6204
|
recursive: true,
|
|
7585
6205
|
force: true
|
|
7586
6206
|
});
|
|
@@ -7594,7 +6214,7 @@ var ClaudeCodeInstaller = class {
|
|
|
7594
6214
|
await unlink(mktInstallDir).catch(() => {
|
|
7595
6215
|
});
|
|
7596
6216
|
} else if (currentKind !== "missing") {
|
|
7597
|
-
await
|
|
6217
|
+
await rm6(mktInstallDir, { recursive: true, force: true }).catch(
|
|
7598
6218
|
() => {
|
|
7599
6219
|
}
|
|
7600
6220
|
);
|
|
@@ -7617,7 +6237,7 @@ var ClaudeCodeInstaller = class {
|
|
|
7617
6237
|
async restoreRawJson(path4, state) {
|
|
7618
6238
|
if (!state.existed) {
|
|
7619
6239
|
if (await pathExists(path4)) {
|
|
7620
|
-
await
|
|
6240
|
+
await rm6(path4, { force: true });
|
|
7621
6241
|
}
|
|
7622
6242
|
return;
|
|
7623
6243
|
}
|
|
@@ -7869,7 +6489,7 @@ var ClaudeCodeInstaller = class {
|
|
|
7869
6489
|
);
|
|
7870
6490
|
const servers = data.mcpServers;
|
|
7871
6491
|
if (typeof servers === "string") {
|
|
7872
|
-
const mcpJsonPath =
|
|
6492
|
+
const mcpJsonPath = resolve11(dirname5(manifestPath), "..", servers);
|
|
7873
6493
|
if (!await pathExists(mcpJsonPath)) return [];
|
|
7874
6494
|
const { data: mcpData } = await readJsonFile(
|
|
7875
6495
|
mcpJsonPath,
|
|
@@ -7914,7 +6534,24 @@ function parsePluginKey2(key) {
|
|
|
7914
6534
|
if (at <= 0 || at === key.length - 1) return null;
|
|
7915
6535
|
return { name: key.slice(0, at), marketplace: key.slice(at + 1) };
|
|
7916
6536
|
}
|
|
7917
|
-
function
|
|
6537
|
+
async function resolveClaudeMcpServers(ref, plugin, rushBinaryResolver) {
|
|
6538
|
+
const decl = plugin.manifest.mcpServers;
|
|
6539
|
+
if (decl === void 0 || decl === null) return void 0;
|
|
6540
|
+
if (typeof decl === "object") {
|
|
6541
|
+
return normalizeClaudeMcpServers(ref, plugin.manifest, rushBinaryResolver);
|
|
6542
|
+
}
|
|
6543
|
+
if (typeof decl === "string") {
|
|
6544
|
+
const external = await readExternalMcpServers(plugin.sourceDir, decl);
|
|
6545
|
+
if (!external || Object.keys(external).length === 0) return void 0;
|
|
6546
|
+
return normalizeClaudeMcpServers(
|
|
6547
|
+
ref,
|
|
6548
|
+
{ ...plugin.manifest, mcpServers: external },
|
|
6549
|
+
rushBinaryResolver
|
|
6550
|
+
);
|
|
6551
|
+
}
|
|
6552
|
+
return void 0;
|
|
6553
|
+
}
|
|
6554
|
+
function buildPluginJson(plugin, mcpServers) {
|
|
7918
6555
|
const { manifest } = plugin;
|
|
7919
6556
|
const {
|
|
7920
6557
|
mcpServers: _origMcp,
|
|
@@ -7930,7 +6567,7 @@ function buildPluginJson2(plugin, mcpServers) {
|
|
|
7930
6567
|
}
|
|
7931
6568
|
async function readMarketplaceCacheSource(metaPath) {
|
|
7932
6569
|
try {
|
|
7933
|
-
const raw = await
|
|
6570
|
+
const raw = await readFile6(metaPath, "utf8");
|
|
7934
6571
|
const parsed = JSON.parse(raw);
|
|
7935
6572
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
7936
6573
|
const sourceRaw = parsed.sourceRaw;
|
|
@@ -7965,7 +6602,7 @@ async function defaultSymlinkRunner(target, linkPath) {
|
|
|
7965
6602
|
}
|
|
7966
6603
|
async function dirExists(p) {
|
|
7967
6604
|
try {
|
|
7968
|
-
const s = await
|
|
6605
|
+
const s = await stat3(p);
|
|
7969
6606
|
return s.isDirectory();
|
|
7970
6607
|
} catch {
|
|
7971
6608
|
return false;
|
|
@@ -7984,7 +6621,7 @@ async function inodeKind(p) {
|
|
|
7984
6621
|
}
|
|
7985
6622
|
async function listDirFilesRecursive(srcDir, dstDir) {
|
|
7986
6623
|
const planned = [];
|
|
7987
|
-
const entries = await
|
|
6624
|
+
const entries = await readdir3(srcDir, { withFileTypes: true });
|
|
7988
6625
|
for (const entry of entries) {
|
|
7989
6626
|
const srcPath = join9(srcDir, entry.name);
|
|
7990
6627
|
const dstPath = join9(dstDir, entry.name);
|
|
@@ -8000,18 +6637,18 @@ async function listDirFilesRecursive(srcDir, dstDir) {
|
|
|
8000
6637
|
}
|
|
8001
6638
|
async function copyDirTracked(srcDir, dstDir) {
|
|
8002
6639
|
const written = [];
|
|
8003
|
-
const entries = await
|
|
6640
|
+
const entries = await readdir3(srcDir, { withFileTypes: true });
|
|
8004
6641
|
for (const entry of entries) {
|
|
8005
6642
|
const srcPath = join9(srcDir, entry.name);
|
|
8006
6643
|
const dstPath = join9(dstDir, entry.name);
|
|
8007
6644
|
if (entry.isDirectory()) {
|
|
8008
|
-
await
|
|
6645
|
+
await mkdir5(dstPath, { recursive: true });
|
|
8009
6646
|
written.push(dstPath);
|
|
8010
6647
|
const subWritten = await copyDirTracked(srcPath, dstPath);
|
|
8011
6648
|
written.push(...subWritten);
|
|
8012
6649
|
} else if (entry.isFile()) {
|
|
8013
|
-
await
|
|
8014
|
-
await
|
|
6650
|
+
await mkdir5(dirname5(dstPath), { recursive: true });
|
|
6651
|
+
await copyFile(srcPath, dstPath);
|
|
8015
6652
|
written.push(dstPath);
|
|
8016
6653
|
}
|
|
8017
6654
|
}
|
|
@@ -8021,7 +6658,7 @@ async function writeAtomicWithMtimeCheck(filePath, data, expectedMtimeMs) {
|
|
|
8021
6658
|
if (expectedMtimeMs !== null) {
|
|
8022
6659
|
let currentMtime = null;
|
|
8023
6660
|
try {
|
|
8024
|
-
const s = await
|
|
6661
|
+
const s = await stat3(filePath);
|
|
8025
6662
|
currentMtime = s.mtimeMs;
|
|
8026
6663
|
} catch {
|
|
8027
6664
|
currentMtime = null;
|
|
@@ -8044,20 +6681,20 @@ async function retryOnConflict(fn) {
|
|
|
8044
6681
|
}
|
|
8045
6682
|
|
|
8046
6683
|
// src/installers/cursor/installer.ts
|
|
8047
|
-
import { randomUUID as
|
|
8048
|
-
import { constants as
|
|
6684
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
6685
|
+
import { constants as fsConstants7 } from "fs";
|
|
8049
6686
|
import {
|
|
8050
|
-
access as
|
|
8051
|
-
cp as
|
|
8052
|
-
mkdir as
|
|
8053
|
-
readFile as
|
|
8054
|
-
rename as
|
|
8055
|
-
rm as
|
|
8056
|
-
stat as
|
|
8057
|
-
writeFile as
|
|
6687
|
+
access as access7,
|
|
6688
|
+
cp as cp2,
|
|
6689
|
+
mkdir as mkdir7,
|
|
6690
|
+
readFile as readFile9,
|
|
6691
|
+
rename as rename5,
|
|
6692
|
+
rm as rm8,
|
|
6693
|
+
stat as stat5,
|
|
6694
|
+
writeFile as writeFile6
|
|
8058
6695
|
} from "fs/promises";
|
|
8059
6696
|
import { homedir as homedir11 } from "os";
|
|
8060
|
-
import { dirname as
|
|
6697
|
+
import { dirname as dirname7, relative, resolve as resolve14 } from "path";
|
|
8061
6698
|
|
|
8062
6699
|
// src/installers/cursor/marker.ts
|
|
8063
6700
|
var RUSH_AI_MARKER = "<!-- rush-ai:auto-generated -->";
|
|
@@ -8066,27 +6703,27 @@ function hasRushAiMarker(fileContent) {
|
|
|
8066
6703
|
}
|
|
8067
6704
|
|
|
8068
6705
|
// src/installers/cursor/mcp.ts
|
|
8069
|
-
import { randomUUID as
|
|
8070
|
-
import { constants as
|
|
6706
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
6707
|
+
import { constants as fsConstants6 } from "fs";
|
|
8071
6708
|
import {
|
|
8072
|
-
access as
|
|
8073
|
-
mkdir as
|
|
8074
|
-
readFile as
|
|
8075
|
-
rename as
|
|
8076
|
-
rm as
|
|
8077
|
-
stat as
|
|
8078
|
-
writeFile as
|
|
6709
|
+
access as access6,
|
|
6710
|
+
mkdir as mkdir6,
|
|
6711
|
+
readFile as readFile7,
|
|
6712
|
+
rename as rename4,
|
|
6713
|
+
rm as rm7,
|
|
6714
|
+
stat as stat4,
|
|
6715
|
+
writeFile as writeFile5
|
|
8079
6716
|
} from "fs/promises";
|
|
8080
|
-
import { dirname as
|
|
6717
|
+
import { dirname as dirname6 } from "path";
|
|
8081
6718
|
async function readCursorMcpJson(filePath) {
|
|
8082
6719
|
return (await readCursorMcpJsonWithMtime(filePath)).data;
|
|
8083
6720
|
}
|
|
8084
6721
|
async function readCursorMcpJsonWithMtime(filePath) {
|
|
8085
|
-
if (!await
|
|
6722
|
+
if (!await pathExists5(filePath)) {
|
|
8086
6723
|
return { data: {}, mtimeMs: null };
|
|
8087
6724
|
}
|
|
8088
|
-
const stats = await
|
|
8089
|
-
const raw = await
|
|
6725
|
+
const stats = await stat4(filePath);
|
|
6726
|
+
const raw = await readFile7(filePath, "utf8");
|
|
8090
6727
|
let parsed;
|
|
8091
6728
|
try {
|
|
8092
6729
|
parsed = JSON.parse(raw);
|
|
@@ -8153,22 +6790,22 @@ function removeMcpServers(current, keysToRemove) {
|
|
|
8153
6790
|
return cloned;
|
|
8154
6791
|
}
|
|
8155
6792
|
async function writeCursorMcpJson(filePath, data) {
|
|
8156
|
-
await
|
|
6793
|
+
await mkdir6(dirname6(filePath), { recursive: true });
|
|
8157
6794
|
const content = `${JSON.stringify(data, null, 2)}
|
|
8158
6795
|
`;
|
|
8159
|
-
const tmp = `${filePath}.${
|
|
6796
|
+
const tmp = `${filePath}.${randomUUID4()}.tmp`;
|
|
8160
6797
|
try {
|
|
8161
|
-
await
|
|
8162
|
-
await
|
|
6798
|
+
await writeFile5(tmp, content, { encoding: "utf8", flag: "w" });
|
|
6799
|
+
await rename4(tmp, filePath);
|
|
8163
6800
|
} catch (err) {
|
|
8164
|
-
await
|
|
6801
|
+
await rm7(tmp, { force: true }).catch(() => {
|
|
8165
6802
|
});
|
|
8166
6803
|
throw err;
|
|
8167
6804
|
}
|
|
8168
6805
|
}
|
|
8169
|
-
async function
|
|
6806
|
+
async function pathExists5(p) {
|
|
8170
6807
|
try {
|
|
8171
|
-
await
|
|
6808
|
+
await access6(p, fsConstants6.F_OK);
|
|
8172
6809
|
return true;
|
|
8173
6810
|
} catch {
|
|
8174
6811
|
return false;
|
|
@@ -8176,25 +6813,25 @@ async function pathExists7(p) {
|
|
|
8176
6813
|
}
|
|
8177
6814
|
|
|
8178
6815
|
// src/installers/cursor/paths.ts
|
|
8179
|
-
import { resolve as
|
|
6816
|
+
import { resolve as resolve12 } from "path";
|
|
8180
6817
|
var CURSOR_RELATIVE_DIR = ".cursor";
|
|
8181
6818
|
function cursorDir(home) {
|
|
8182
|
-
return
|
|
6819
|
+
return resolve12(home, CURSOR_RELATIVE_DIR);
|
|
8183
6820
|
}
|
|
8184
6821
|
function cursorMcpJsonPath(home) {
|
|
8185
|
-
return
|
|
6822
|
+
return resolve12(cursorDir(home), "mcp.json");
|
|
8186
6823
|
}
|
|
8187
6824
|
function cursorRulesDir(home) {
|
|
8188
|
-
return
|
|
6825
|
+
return resolve12(cursorDir(home), "rules");
|
|
8189
6826
|
}
|
|
8190
6827
|
function cursorRuleMdcPath(home, ruleName) {
|
|
8191
|
-
return
|
|
6828
|
+
return resolve12(cursorRulesDir(home), `${ruleName}.mdc`);
|
|
8192
6829
|
}
|
|
8193
6830
|
function cursorSkillsDir(home) {
|
|
8194
|
-
return
|
|
6831
|
+
return resolve12(cursorDir(home), "skills");
|
|
8195
6832
|
}
|
|
8196
6833
|
function cursorSkillDir(home, skillName) {
|
|
8197
|
-
return
|
|
6834
|
+
return resolve12(cursorSkillsDir(home), skillName);
|
|
8198
6835
|
}
|
|
8199
6836
|
function bridgeSkillFileReference(skillName) {
|
|
8200
6837
|
return `.cursor/skills/${skillName}/SKILL.md`;
|
|
@@ -8304,13 +6941,13 @@ function quoteIfNeeded(value) {
|
|
|
8304
6941
|
}
|
|
8305
6942
|
|
|
8306
6943
|
// src/installers/cursor/skills.ts
|
|
8307
|
-
import { readFile as
|
|
8308
|
-
import { resolve as
|
|
6944
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
6945
|
+
import { resolve as resolve13 } from "path";
|
|
8309
6946
|
async function readSkillDescription(skillSourceDir) {
|
|
8310
|
-
const skillMdPath =
|
|
6947
|
+
const skillMdPath = resolve13(skillSourceDir, "SKILL.md");
|
|
8311
6948
|
let raw;
|
|
8312
6949
|
try {
|
|
8313
|
-
raw = await
|
|
6950
|
+
raw = await readFile8(skillMdPath, "utf8");
|
|
8314
6951
|
} catch {
|
|
8315
6952
|
return void 0;
|
|
8316
6953
|
}
|
|
@@ -8340,11 +6977,11 @@ async function executeRollback(ledger) {
|
|
|
8340
6977
|
if (entry.kind === "restore-file") {
|
|
8341
6978
|
await atomicWriteFile(entry.path, entry.originalContent);
|
|
8342
6979
|
} else if (entry.kind === "remove-file") {
|
|
8343
|
-
await
|
|
6980
|
+
await rm8(entry.path, { force: true });
|
|
8344
6981
|
} else if (entry.kind === "remove-dir") {
|
|
8345
|
-
await
|
|
6982
|
+
await rm8(entry.path, { recursive: true, force: true });
|
|
8346
6983
|
} else {
|
|
8347
|
-
await
|
|
6984
|
+
await rm8(entry.path, { recursive: true, force: true });
|
|
8348
6985
|
await safeRename(entry.backupPath, entry.path);
|
|
8349
6986
|
}
|
|
8350
6987
|
} catch (err) {
|
|
@@ -8356,20 +6993,20 @@ async function executeRollback(ledger) {
|
|
|
8356
6993
|
return errors;
|
|
8357
6994
|
}
|
|
8358
6995
|
async function atomicWriteFile(filePath, content) {
|
|
8359
|
-
await
|
|
8360
|
-
const tmp = `${filePath}.${
|
|
6996
|
+
await mkdir7(dirname7(filePath), { recursive: true });
|
|
6997
|
+
const tmp = `${filePath}.${randomUUID5()}.tmp`;
|
|
8361
6998
|
try {
|
|
8362
|
-
await
|
|
8363
|
-
await
|
|
6999
|
+
await writeFile6(tmp, content, { encoding: "utf8", flag: "w" });
|
|
7000
|
+
await rename5(tmp, filePath);
|
|
8364
7001
|
} catch (err) {
|
|
8365
|
-
await
|
|
7002
|
+
await rm8(tmp, { force: true }).catch(() => {
|
|
8366
7003
|
});
|
|
8367
7004
|
throw err;
|
|
8368
7005
|
}
|
|
8369
7006
|
}
|
|
8370
7007
|
async function getMcpMtimeMs(filePath) {
|
|
8371
7008
|
try {
|
|
8372
|
-
const s = await
|
|
7009
|
+
const s = await stat5(filePath);
|
|
8373
7010
|
return s.mtimeMs;
|
|
8374
7011
|
} catch {
|
|
8375
7012
|
return null;
|
|
@@ -8602,13 +7239,13 @@ var CursorInstaller = class {
|
|
|
8602
7239
|
if (info === "missing") continue;
|
|
8603
7240
|
if (info === "skill-dir") {
|
|
8604
7241
|
if (!dryRun) {
|
|
8605
|
-
await
|
|
7242
|
+
await rm8(filePath, { recursive: true, force: true });
|
|
8606
7243
|
}
|
|
8607
7244
|
removedFiles.push(filePath);
|
|
8608
7245
|
continue;
|
|
8609
7246
|
}
|
|
8610
7247
|
if (info === "mdc-file") {
|
|
8611
|
-
const content = await
|
|
7248
|
+
const content = await readFile9(filePath, "utf8").catch(() => "");
|
|
8612
7249
|
if (!hasRushAiMarker(content)) {
|
|
8613
7250
|
warnings.push(
|
|
8614
7251
|
`${filePath} \u5DF2\u88AB\u4FEE\u6539\uFF08marker \u4E22\u5931\uFF09\uFF0C\u8DF3\u8FC7\u4E0D\u52A8\u2014\u2014\u8BF7\u7528\u6237\u624B\u52A8\u5904\u7406`
|
|
@@ -8616,13 +7253,13 @@ var CursorInstaller = class {
|
|
|
8616
7253
|
continue;
|
|
8617
7254
|
}
|
|
8618
7255
|
if (!dryRun) {
|
|
8619
|
-
await
|
|
7256
|
+
await rm8(filePath, { force: true });
|
|
8620
7257
|
}
|
|
8621
7258
|
removedFiles.push(filePath);
|
|
8622
7259
|
continue;
|
|
8623
7260
|
}
|
|
8624
7261
|
if (!dryRun) {
|
|
8625
|
-
await
|
|
7262
|
+
await rm8(filePath, { force: true });
|
|
8626
7263
|
}
|
|
8627
7264
|
removedFiles.push(filePath);
|
|
8628
7265
|
}
|
|
@@ -8670,10 +7307,10 @@ var CursorInstaller = class {
|
|
|
8670
7307
|
const prevEntry = registryStore.get(plugin.ref);
|
|
8671
7308
|
const prevFiles = new Set(prevEntry?.targets?.cursor?.files ?? []);
|
|
8672
7309
|
if (plugin.capabilities.includes("skills")) {
|
|
8673
|
-
const skillsSourceDir =
|
|
7310
|
+
const skillsSourceDir = resolve14(plugin.sourceDir, "skills");
|
|
8674
7311
|
const skillDirs = await listSkillDirs(skillsSourceDir);
|
|
8675
7312
|
for (const skillName of skillDirs) {
|
|
8676
|
-
const srcDir =
|
|
7313
|
+
const srcDir = resolve14(skillsSourceDir, skillName);
|
|
8677
7314
|
const destDir = cursorSkillDir(this.home, skillName);
|
|
8678
7315
|
const bridgeRulePath = cursorRuleMdcPath(this.home, skillName);
|
|
8679
7316
|
if (await dirExists2(destDir)) {
|
|
@@ -8705,10 +7342,10 @@ var CursorInstaller = class {
|
|
|
8705
7342
|
}
|
|
8706
7343
|
}
|
|
8707
7344
|
if (plugin.capabilities.includes("rules")) {
|
|
8708
|
-
const rulesSourceDir =
|
|
7345
|
+
const rulesSourceDir = resolve14(plugin.sourceDir, "rules");
|
|
8709
7346
|
const rulesFiles = await listRuleMdFiles(rulesSourceDir);
|
|
8710
7347
|
for (const ruleFile of rulesFiles) {
|
|
8711
|
-
const srcPath =
|
|
7348
|
+
const srcPath = resolve14(rulesSourceDir, ruleFile);
|
|
8712
7349
|
const ruleName = ruleFile.replace(/\.md$/i, "");
|
|
8713
7350
|
const destPath = cursorRuleMdcPath(this.home, ruleName);
|
|
8714
7351
|
await assertMdcWritable(destPath, args.force);
|
|
@@ -8717,7 +7354,7 @@ var CursorInstaller = class {
|
|
|
8717
7354
|
`rule "${ruleName}" \u4E0E skill "${ruleName}" \u7684 bridge rule \u540C\u540D\uFF0Crule \u5185\u5BB9\u4F1A\u8986\u76D6 bridge rule`
|
|
8718
7355
|
);
|
|
8719
7356
|
}
|
|
8720
|
-
const src = await
|
|
7357
|
+
const src = await readFile9(srcPath, "utf8");
|
|
8721
7358
|
const parsed = parseMarkdown(src);
|
|
8722
7359
|
const content = renderMdc(parsed, {
|
|
8723
7360
|
descriptionFallback: plugin.manifest.description ?? ruleName
|
|
@@ -8726,7 +7363,7 @@ var CursorInstaller = class {
|
|
|
8726
7363
|
plannedFiles.push(destPath);
|
|
8727
7364
|
}
|
|
8728
7365
|
}
|
|
8729
|
-
const mcpAdditions = extractMcpAdditions(plugin);
|
|
7366
|
+
const mcpAdditions = await extractMcpAdditions(plugin);
|
|
8730
7367
|
if (mcpAdditions && Object.keys(mcpAdditions).length > 0) {
|
|
8731
7368
|
plannedFiles.push(cursorMcpJsonPath(this.home));
|
|
8732
7369
|
plannedMcpKeys.push(...Object.keys(mcpAdditions));
|
|
@@ -8745,7 +7382,7 @@ var CursorInstaller = class {
|
|
|
8745
7382
|
// -------------------------------------------------------------------------
|
|
8746
7383
|
async writeSkillDir(args) {
|
|
8747
7384
|
const preExisted = await dirExists2(args.destDir);
|
|
8748
|
-
await
|
|
7385
|
+
await mkdir7(dirname7(args.destDir), { recursive: true });
|
|
8749
7386
|
if (preExisted) {
|
|
8750
7387
|
const backup = `${args.destDir}.${process.pid}.${Date.now()}.bak`;
|
|
8751
7388
|
await safeRename(args.destDir, backup);
|
|
@@ -8757,12 +7394,12 @@ var CursorInstaller = class {
|
|
|
8757
7394
|
} else {
|
|
8758
7395
|
args.ledger.push({ kind: "remove-dir", path: args.destDir });
|
|
8759
7396
|
}
|
|
8760
|
-
await
|
|
7397
|
+
await cp2(args.srcDir, args.destDir, { recursive: true });
|
|
8761
7398
|
}
|
|
8762
7399
|
async writeMdcFile(args) {
|
|
8763
7400
|
const preExisted = await fileExists(args.path);
|
|
8764
7401
|
if (preExisted) {
|
|
8765
|
-
const original = await
|
|
7402
|
+
const original = await readFile9(args.path, "utf8");
|
|
8766
7403
|
args.ledger.push({
|
|
8767
7404
|
kind: "restore-file",
|
|
8768
7405
|
path: args.path,
|
|
@@ -8776,7 +7413,7 @@ var CursorInstaller = class {
|
|
|
8776
7413
|
async writeMcpMerge(args) {
|
|
8777
7414
|
const preExisted = await fileExists(args.mcpPath);
|
|
8778
7415
|
if (preExisted) {
|
|
8779
|
-
const original = await
|
|
7416
|
+
const original = await readFile9(args.mcpPath, "utf8");
|
|
8780
7417
|
args.ledger.push({
|
|
8781
7418
|
kind: "restore-file",
|
|
8782
7419
|
path: args.mcpPath,
|
|
@@ -8860,7 +7497,7 @@ function skippedResult() {
|
|
|
8860
7497
|
}
|
|
8861
7498
|
async function assertMdcWritable(path4, force) {
|
|
8862
7499
|
if (!await fileExists(path4)) return;
|
|
8863
|
-
const raw = await
|
|
7500
|
+
const raw = await readFile9(path4, "utf8").catch(() => "");
|
|
8864
7501
|
if (hasRushAiMarker(raw)) return;
|
|
8865
7502
|
if (force) return;
|
|
8866
7503
|
throw new CursorInstallConflictError(
|
|
@@ -8868,12 +7505,19 @@ async function assertMdcWritable(path4, force) {
|
|
|
8868
7505
|
`\u76EE\u6807 .mdc \u5DF2\u5B58\u5728\u4E14\u65E0 rush-ai marker\uFF08${path4}\uFF09\u2014\u2014\u4E0D\u8986\u76D6\u7528\u6237\u624B\u5199 rule`
|
|
8869
7506
|
);
|
|
8870
7507
|
}
|
|
8871
|
-
function extractMcpAdditions(plugin) {
|
|
7508
|
+
async function extractMcpAdditions(plugin) {
|
|
8872
7509
|
const m = plugin.manifest.mcpServers;
|
|
8873
7510
|
if (m === void 0 || m === null) return null;
|
|
8874
|
-
|
|
7511
|
+
let serverMap = null;
|
|
7512
|
+
if (typeof m === "object") {
|
|
7513
|
+
serverMap = m;
|
|
7514
|
+
} else if (typeof m === "string") {
|
|
7515
|
+
const { readExternalMcpServers: readExternalMcpServers2 } = await import("./mcp-UZYID3GG.js");
|
|
7516
|
+
serverMap = await readExternalMcpServers2(plugin.sourceDir, m);
|
|
7517
|
+
}
|
|
7518
|
+
if (!serverMap) return null;
|
|
8875
7519
|
const out = {};
|
|
8876
|
-
for (const [key, cfg] of Object.entries(
|
|
7520
|
+
for (const [key, cfg] of Object.entries(serverMap)) {
|
|
8877
7521
|
if (cfg && typeof cfg === "object" && typeof cfg.command === "string") {
|
|
8878
7522
|
out[key] = cfg;
|
|
8879
7523
|
}
|
|
@@ -8882,19 +7526,19 @@ function extractMcpAdditions(plugin) {
|
|
|
8882
7526
|
}
|
|
8883
7527
|
async function listSkillDirs(skillsDir) {
|
|
8884
7528
|
if (!await dirExists2(skillsDir)) return [];
|
|
8885
|
-
const { readdir:
|
|
8886
|
-
const entries = await
|
|
7529
|
+
const { readdir: readdir5 } = await import("fs/promises");
|
|
7530
|
+
const entries = await readdir5(skillsDir, { withFileTypes: true });
|
|
8887
7531
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name).filter((name) => !name.startsWith(".")).sort();
|
|
8888
7532
|
}
|
|
8889
7533
|
async function listRuleMdFiles(rulesDir) {
|
|
8890
7534
|
if (!await dirExists2(rulesDir)) return [];
|
|
8891
|
-
const { readdir:
|
|
8892
|
-
const entries = await
|
|
7535
|
+
const { readdir: readdir5 } = await import("fs/promises");
|
|
7536
|
+
const entries = await readdir5(rulesDir, { withFileTypes: true });
|
|
8893
7537
|
return entries.filter((e) => e.isFile() && /\.md$/i.test(e.name)).map((e) => e.name).sort();
|
|
8894
7538
|
}
|
|
8895
7539
|
async function dirExists2(p) {
|
|
8896
7540
|
try {
|
|
8897
|
-
const s = await
|
|
7541
|
+
const s = await stat5(p);
|
|
8898
7542
|
return s.isDirectory();
|
|
8899
7543
|
} catch {
|
|
8900
7544
|
return false;
|
|
@@ -8902,21 +7546,21 @@ async function dirExists2(p) {
|
|
|
8902
7546
|
}
|
|
8903
7547
|
async function fileExists(p) {
|
|
8904
7548
|
try {
|
|
8905
|
-
await
|
|
8906
|
-
const s = await
|
|
7549
|
+
await access7(p, fsConstants7.F_OK);
|
|
7550
|
+
const s = await stat5(p);
|
|
8907
7551
|
return s.isFile();
|
|
8908
7552
|
} catch {
|
|
8909
7553
|
return false;
|
|
8910
7554
|
}
|
|
8911
7555
|
}
|
|
8912
7556
|
async function safeRename(from, to) {
|
|
8913
|
-
const { rename:
|
|
8914
|
-
await
|
|
7557
|
+
const { rename: rename7 } = await import("fs/promises");
|
|
7558
|
+
await rename7(from, to);
|
|
8915
7559
|
}
|
|
8916
7560
|
async function classifyArtifactPath(p) {
|
|
8917
7561
|
let s;
|
|
8918
7562
|
try {
|
|
8919
|
-
s = await
|
|
7563
|
+
s = await stat5(p);
|
|
8920
7564
|
} catch {
|
|
8921
7565
|
return "missing";
|
|
8922
7566
|
}
|
|
@@ -8929,22 +7573,22 @@ async function classifyArtifactPath(p) {
|
|
|
8929
7573
|
}
|
|
8930
7574
|
|
|
8931
7575
|
// src/migration/cleanup.ts
|
|
8932
|
-
import { lstat as lstat3, rm as
|
|
7576
|
+
import { lstat as lstat3, rm as rm9, unlink as unlink2 } from "fs/promises";
|
|
8933
7577
|
import { homedir as homedir13 } from "os";
|
|
8934
|
-
import { resolve as
|
|
7578
|
+
import { resolve as resolve16 } from "path";
|
|
8935
7579
|
|
|
8936
7580
|
// src/migration/detect.ts
|
|
8937
|
-
import { access as
|
|
7581
|
+
import { access as access8, lstat as lstat2, readFile as readFile10, readlink as readlink2, stat as stat6 } from "fs/promises";
|
|
8938
7582
|
import { homedir as homedir12 } from "os";
|
|
8939
|
-
import { isAbsolute as
|
|
7583
|
+
import { isAbsolute as isAbsolute3, resolve as resolve15 } from "path";
|
|
8940
7584
|
var LEGACY_ASSET_MANIFEST_RELATIVE = ".rush/plugins/claude-code/asset-manifest.json";
|
|
8941
7585
|
var LEGACY_ASSETS_DIR_RELATIVE = ".rush/plugins/claude-code/assets";
|
|
8942
7586
|
var LEGACY_CLAUDE_CODE_DIR_RELATIVE = ".rush/plugins/claude-code";
|
|
8943
7587
|
var LEGACY_SKILL_SYMLINK_RELATIVE = ".claude/skills/rush-task";
|
|
8944
7588
|
var CLAUDE_SETTINGS_JSON_RELATIVE = ".claude/settings.json";
|
|
8945
|
-
async function
|
|
7589
|
+
async function pathExists6(p) {
|
|
8946
7590
|
try {
|
|
8947
|
-
await
|
|
7591
|
+
await access8(p);
|
|
8948
7592
|
return true;
|
|
8949
7593
|
} catch {
|
|
8950
7594
|
return false;
|
|
@@ -8955,7 +7599,7 @@ async function isSymlinkPointingInto(linkPath, expectedTarget) {
|
|
|
8955
7599
|
const lst = await lstat2(linkPath);
|
|
8956
7600
|
if (!lst.isSymbolicLink()) return false;
|
|
8957
7601
|
const rawTarget = await readlink2(linkPath);
|
|
8958
|
-
const resolvedTarget =
|
|
7602
|
+
const resolvedTarget = isAbsolute3(rawTarget) ? rawTarget : resolve15(linkPath, "..", rawTarget);
|
|
8959
7603
|
return resolvedTarget === expectedTarget || resolvedTarget.startsWith(`${expectedTarget}/`);
|
|
8960
7604
|
} catch {
|
|
8961
7605
|
return false;
|
|
@@ -8963,8 +7607,8 @@ async function isSymlinkPointingInto(linkPath, expectedTarget) {
|
|
|
8963
7607
|
}
|
|
8964
7608
|
async function hasLegacyMcpWithoutEnabled(settingsJsonPath) {
|
|
8965
7609
|
try {
|
|
8966
|
-
if (!await
|
|
8967
|
-
const raw = await
|
|
7610
|
+
if (!await pathExists6(settingsJsonPath)) return false;
|
|
7611
|
+
const raw = await readFile10(settingsJsonPath, "utf8");
|
|
8968
7612
|
let parsed;
|
|
8969
7613
|
try {
|
|
8970
7614
|
parsed = JSON.parse(raw);
|
|
@@ -8992,12 +7636,12 @@ async function hasLegacyMcpWithoutEnabled(settingsJsonPath) {
|
|
|
8992
7636
|
}
|
|
8993
7637
|
async function detectLegacyInstall(opts = {}) {
|
|
8994
7638
|
const home = opts.home ?? homedir12();
|
|
8995
|
-
const assetManifestPath =
|
|
8996
|
-
const legacyAssetsDir =
|
|
8997
|
-
const skillSymlinkPath =
|
|
8998
|
-
const settingsJsonPath =
|
|
7639
|
+
const assetManifestPath = resolve15(home, LEGACY_ASSET_MANIFEST_RELATIVE);
|
|
7640
|
+
const legacyAssetsDir = resolve15(home, LEGACY_ASSETS_DIR_RELATIVE);
|
|
7641
|
+
const skillSymlinkPath = resolve15(home, LEGACY_SKILL_SYMLINK_RELATIVE);
|
|
7642
|
+
const settingsJsonPath = resolve15(home, CLAUDE_SETTINGS_JSON_RELATIVE);
|
|
8999
7643
|
const [hasAssetManifest, hasLegacySkillSymlink, legacyMcpWithoutEnabled] = await Promise.all([
|
|
9000
|
-
|
|
7644
|
+
pathExists6(assetManifestPath),
|
|
9001
7645
|
isSymlinkPointingInto(skillSymlinkPath, legacyAssetsDir),
|
|
9002
7646
|
hasLegacyMcpWithoutEnabled(settingsJsonPath)
|
|
9003
7647
|
]);
|
|
@@ -9010,11 +7654,11 @@ async function detectLegacyInstall(opts = {}) {
|
|
|
9010
7654
|
}
|
|
9011
7655
|
async function detectLegacyVersion(opts = {}) {
|
|
9012
7656
|
const home = opts.home ?? homedir12();
|
|
9013
|
-
const manifestPath =
|
|
7657
|
+
const manifestPath = resolve15(home, LEGACY_ASSET_MANIFEST_RELATIVE);
|
|
9014
7658
|
try {
|
|
9015
|
-
const lst = await
|
|
7659
|
+
const lst = await stat6(manifestPath);
|
|
9016
7660
|
if (!lst.isFile()) return "0.7.x";
|
|
9017
|
-
const raw = await
|
|
7661
|
+
const raw = await readFile10(manifestPath, "utf8");
|
|
9018
7662
|
const parsed = JSON.parse(raw);
|
|
9019
7663
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
9020
7664
|
const v = parsed.version;
|
|
@@ -9028,9 +7672,9 @@ async function detectLegacyVersion(opts = {}) {
|
|
|
9028
7672
|
|
|
9029
7673
|
// src/migration/cleanup.ts
|
|
9030
7674
|
async function cleanupLegacyDir(home) {
|
|
9031
|
-
const dir =
|
|
7675
|
+
const dir = resolve16(home, LEGACY_CLAUDE_CODE_DIR_RELATIVE);
|
|
9032
7676
|
try {
|
|
9033
|
-
await
|
|
7677
|
+
await rm9(dir, { recursive: true, force: true });
|
|
9034
7678
|
return { ok: true };
|
|
9035
7679
|
} catch (err) {
|
|
9036
7680
|
return {
|
|
@@ -9040,7 +7684,7 @@ async function cleanupLegacyDir(home) {
|
|
|
9040
7684
|
}
|
|
9041
7685
|
}
|
|
9042
7686
|
async function cleanupLegacySymlink(home) {
|
|
9043
|
-
const path4 =
|
|
7687
|
+
const path4 = resolve16(home, LEGACY_SKILL_SYMLINK_RELATIVE);
|
|
9044
7688
|
try {
|
|
9045
7689
|
let lst;
|
|
9046
7690
|
try {
|
|
@@ -9062,7 +7706,7 @@ async function cleanupLegacySymlink(home) {
|
|
|
9062
7706
|
}
|
|
9063
7707
|
}
|
|
9064
7708
|
async function cleanupLegacyMcp(home) {
|
|
9065
|
-
const path4 =
|
|
7709
|
+
const path4 = resolve16(home, CLAUDE_SETTINGS_JSON_RELATIVE);
|
|
9066
7710
|
try {
|
|
9067
7711
|
const { data, existed } = await readJsonFile(
|
|
9068
7712
|
path4,
|
|
@@ -9118,13 +7762,13 @@ function errorMessage(err) {
|
|
|
9118
7762
|
}
|
|
9119
7763
|
|
|
9120
7764
|
// src/migration/log.ts
|
|
9121
|
-
import { appendFile as appendFile2, mkdir as
|
|
7765
|
+
import { appendFile as appendFile2, mkdir as mkdir8 } from "fs/promises";
|
|
9122
7766
|
import { homedir as homedir14 } from "os";
|
|
9123
|
-
import { dirname as
|
|
7767
|
+
import { dirname as dirname8, resolve as resolve17 } from "path";
|
|
9124
7768
|
var MIGRATION_LOG_RELATIVE_PATH = ".rush/plugins/migration-failed.log";
|
|
9125
7769
|
function resolveMigrationLogPath(opts = {}) {
|
|
9126
7770
|
const home = opts.home ?? homedir14();
|
|
9127
|
-
return
|
|
7771
|
+
return resolve17(home, MIGRATION_LOG_RELATIVE_PATH);
|
|
9128
7772
|
}
|
|
9129
7773
|
async function appendMigrationFailure(reason, opts = {}) {
|
|
9130
7774
|
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -9141,31 +7785,31 @@ async function appendMigrationFailure(reason, opts = {}) {
|
|
|
9141
7785
|
const payload = `${lines.join("\n")}
|
|
9142
7786
|
`;
|
|
9143
7787
|
try {
|
|
9144
|
-
await
|
|
7788
|
+
await mkdir8(dirname8(logPath), { recursive: true });
|
|
9145
7789
|
await appendFile2(logPath, payload, { encoding: "utf8" });
|
|
9146
7790
|
} catch {
|
|
9147
7791
|
}
|
|
9148
7792
|
}
|
|
9149
7793
|
|
|
9150
7794
|
// src/migration/migrate.ts
|
|
9151
|
-
import { access as
|
|
7795
|
+
import { access as access11, readFile as readFile12 } from "fs/promises";
|
|
9152
7796
|
import { homedir as homedir16 } from "os";
|
|
9153
7797
|
|
|
9154
7798
|
// src/plugins/resolver.ts
|
|
9155
|
-
import { randomUUID as
|
|
9156
|
-
import { mkdir as
|
|
7799
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
7800
|
+
import { mkdir as mkdir9, rename as rename6, rm as rm10 } from "fs/promises";
|
|
9157
7801
|
import { homedir as homedir15 } from "os";
|
|
9158
7802
|
import {
|
|
9159
|
-
isAbsolute as
|
|
7803
|
+
isAbsolute as isAbsolute4,
|
|
9160
7804
|
relative as pathRelative2,
|
|
9161
|
-
resolve as
|
|
7805
|
+
resolve as pathResolve,
|
|
9162
7806
|
sep as pathSep
|
|
9163
7807
|
} from "path";
|
|
9164
7808
|
|
|
9165
7809
|
// src/plugins/capabilities.ts
|
|
9166
|
-
import { constants as
|
|
9167
|
-
import { access as
|
|
9168
|
-
import { extname as extname2, resolve as
|
|
7810
|
+
import { constants as fsConstants8 } from "fs";
|
|
7811
|
+
import { access as access9, readdir as readdir4, stat as stat7 } from "fs/promises";
|
|
7812
|
+
import { extname as extname2, resolve as resolve18 } from "path";
|
|
9169
7813
|
var CAPABILITY_ORDER = [
|
|
9170
7814
|
"commands",
|
|
9171
7815
|
"skills",
|
|
@@ -9194,20 +7838,20 @@ async function scanCapabilities(sourceDir, manifest) {
|
|
|
9194
7838
|
return CAPABILITY_ORDER.filter((cap) => found.has(cap));
|
|
9195
7839
|
}
|
|
9196
7840
|
async function hasCommands(sourceDir) {
|
|
9197
|
-
return dirHasFileWithExt(
|
|
7841
|
+
return dirHasFileWithExt(resolve18(sourceDir, "commands"), ".md");
|
|
9198
7842
|
}
|
|
9199
7843
|
async function hasSkills(sourceDir) {
|
|
9200
|
-
const skillsDir =
|
|
7844
|
+
const skillsDir = resolve18(sourceDir, "skills");
|
|
9201
7845
|
if (await dirExists3(skillsDir)) {
|
|
9202
7846
|
let entries;
|
|
9203
7847
|
try {
|
|
9204
|
-
entries = await
|
|
7848
|
+
entries = await readdir4(skillsDir, { withFileTypes: true });
|
|
9205
7849
|
} catch {
|
|
9206
7850
|
return false;
|
|
9207
7851
|
}
|
|
9208
7852
|
for (const entry of entries) {
|
|
9209
7853
|
if (!entry.isDirectory()) continue;
|
|
9210
|
-
const skillMdPath =
|
|
7854
|
+
const skillMdPath = resolve18(skillsDir, entry.name, "SKILL.md");
|
|
9211
7855
|
if (await fileExists2(skillMdPath)) {
|
|
9212
7856
|
return true;
|
|
9213
7857
|
}
|
|
@@ -9216,16 +7860,16 @@ async function hasSkills(sourceDir) {
|
|
|
9216
7860
|
return hasClaudeStyleSkills(sourceDir);
|
|
9217
7861
|
}
|
|
9218
7862
|
async function hasRules(sourceDir) {
|
|
9219
|
-
return dirHasFileWithExt(
|
|
7863
|
+
return dirHasFileWithExt(resolve18(sourceDir, "rules"), ".md");
|
|
9220
7864
|
}
|
|
9221
7865
|
async function hasHooks(sourceDir) {
|
|
9222
|
-
const hooksDir =
|
|
7866
|
+
const hooksDir = resolve18(sourceDir, "hooks");
|
|
9223
7867
|
if (!await dirExists3(hooksDir)) {
|
|
9224
7868
|
return false;
|
|
9225
7869
|
}
|
|
9226
7870
|
let entries;
|
|
9227
7871
|
try {
|
|
9228
|
-
entries = await
|
|
7872
|
+
entries = await readdir4(hooksDir, { withFileTypes: true });
|
|
9229
7873
|
} catch {
|
|
9230
7874
|
return false;
|
|
9231
7875
|
}
|
|
@@ -9251,7 +7895,7 @@ async function dirHasFileWithExt(dirPath, ext) {
|
|
|
9251
7895
|
}
|
|
9252
7896
|
let entries;
|
|
9253
7897
|
try {
|
|
9254
|
-
entries = await
|
|
7898
|
+
entries = await readdir4(dirPath, { withFileTypes: true });
|
|
9255
7899
|
} catch {
|
|
9256
7900
|
return false;
|
|
9257
7901
|
}
|
|
@@ -9261,19 +7905,19 @@ async function dirHasFileWithExt(dirPath, ext) {
|
|
|
9261
7905
|
);
|
|
9262
7906
|
}
|
|
9263
7907
|
async function hasClaudeStyleSkills(sourceDir) {
|
|
9264
|
-
const dotSkillsDir =
|
|
7908
|
+
const dotSkillsDir = resolve18(sourceDir, ".skills");
|
|
9265
7909
|
if (!await dirExists3(dotSkillsDir)) {
|
|
9266
7910
|
return false;
|
|
9267
7911
|
}
|
|
9268
7912
|
async function walk2(dirPath) {
|
|
9269
7913
|
let entries;
|
|
9270
7914
|
try {
|
|
9271
|
-
entries = await
|
|
7915
|
+
entries = await readdir4(dirPath, { withFileTypes: true });
|
|
9272
7916
|
} catch {
|
|
9273
7917
|
return false;
|
|
9274
7918
|
}
|
|
9275
7919
|
for (const entry of entries) {
|
|
9276
|
-
const abs =
|
|
7920
|
+
const abs = resolve18(dirPath, entry.name);
|
|
9277
7921
|
if (entry.isDirectory()) {
|
|
9278
7922
|
if (await walk2(abs)) return true;
|
|
9279
7923
|
continue;
|
|
@@ -9288,7 +7932,7 @@ async function hasClaudeStyleSkills(sourceDir) {
|
|
|
9288
7932
|
}
|
|
9289
7933
|
async function dirExists3(p) {
|
|
9290
7934
|
try {
|
|
9291
|
-
const s = await
|
|
7935
|
+
const s = await stat7(p);
|
|
9292
7936
|
return s.isDirectory();
|
|
9293
7937
|
} catch {
|
|
9294
7938
|
return false;
|
|
@@ -9296,8 +7940,8 @@ async function dirExists3(p) {
|
|
|
9296
7940
|
}
|
|
9297
7941
|
async function fileExists2(p) {
|
|
9298
7942
|
try {
|
|
9299
|
-
await
|
|
9300
|
-
const s = await
|
|
7943
|
+
await access9(p, fsConstants8.R_OK);
|
|
7944
|
+
const s = await stat7(p);
|
|
9301
7945
|
return s.isFile();
|
|
9302
7946
|
} catch {
|
|
9303
7947
|
return false;
|
|
@@ -9366,18 +8010,18 @@ var PluginCloneFailedError = class extends PluginResolverError {
|
|
|
9366
8010
|
};
|
|
9367
8011
|
|
|
9368
8012
|
// src/plugins/manifest.ts
|
|
9369
|
-
import { constants as
|
|
9370
|
-
import { access as
|
|
9371
|
-
import { resolve as
|
|
8013
|
+
import { constants as fsConstants9 } from "fs";
|
|
8014
|
+
import { access as access10, readFile as readFile11 } from "fs/promises";
|
|
8015
|
+
import { resolve as resolve19 } from "path";
|
|
9372
8016
|
var PLUGIN_MANIFEST_RELATIVE_PATH = ".claude-plugin/plugin.json";
|
|
9373
8017
|
async function readPluginManifest(pluginName, sourceDir) {
|
|
9374
|
-
const manifestPath =
|
|
8018
|
+
const manifestPath = resolve19(sourceDir, PLUGIN_MANIFEST_RELATIVE_PATH);
|
|
9375
8019
|
try {
|
|
9376
|
-
await
|
|
8020
|
+
await access10(manifestPath, fsConstants9.R_OK);
|
|
9377
8021
|
} catch {
|
|
9378
8022
|
throw new PluginManifestNotFoundError(pluginName, manifestPath);
|
|
9379
8023
|
}
|
|
9380
|
-
const raw = await
|
|
8024
|
+
const raw = await readFile11(manifestPath, "utf8");
|
|
9381
8025
|
return parsePluginManifest(pluginName, raw, manifestPath);
|
|
9382
8026
|
}
|
|
9383
8027
|
function parsePluginManifest(pluginName, raw, sourcePathForError) {
|
|
@@ -9432,10 +8076,17 @@ async function resolvePlugin(ref, marketplace, options = {}) {
|
|
|
9432
8076
|
});
|
|
9433
8077
|
const sourceDir = resolvePluginSourceDir(ref, marketplace, entry);
|
|
9434
8078
|
const manifest = await readPluginManifest(ref.name, sourceDir);
|
|
9435
|
-
const
|
|
8079
|
+
const manifestWithImplicitMcp = await injectImplicitMcpRef(
|
|
8080
|
+
sourceDir,
|
|
8081
|
+
manifest
|
|
8082
|
+
);
|
|
8083
|
+
const capabilities = await scanCapabilities(
|
|
8084
|
+
sourceDir,
|
|
8085
|
+
manifestWithImplicitMcp
|
|
8086
|
+
);
|
|
9436
8087
|
const normalizedManifest = normalizeRushMcpCommand(
|
|
9437
8088
|
ref,
|
|
9438
|
-
|
|
8089
|
+
manifestWithImplicitMcp,
|
|
9439
8090
|
options.rushAiBinaryResolver
|
|
9440
8091
|
);
|
|
9441
8092
|
return {
|
|
@@ -9463,17 +8114,17 @@ function resolvePluginSourceDir(ref, marketplace, entry) {
|
|
|
9463
8114
|
return extractUrlSourceDir(marketplace, entry);
|
|
9464
8115
|
}
|
|
9465
8116
|
const relPath = extractRelativeSourcePath(ref, marketplace, entry);
|
|
9466
|
-
if (
|
|
8117
|
+
if (isAbsolute4(relPath)) {
|
|
9467
8118
|
throw new PluginSourceUnresolvableError(
|
|
9468
8119
|
ref.name,
|
|
9469
8120
|
marketplace.name,
|
|
9470
8121
|
`source path must be relative to marketplace root; got absolute '${relPath}'`
|
|
9471
8122
|
);
|
|
9472
8123
|
}
|
|
9473
|
-
const rootAbs =
|
|
9474
|
-
const abs =
|
|
8124
|
+
const rootAbs = pathResolve(marketplace.rootDir);
|
|
8125
|
+
const abs = pathResolve(rootAbs, relPath);
|
|
9475
8126
|
const rel = pathRelative2(rootAbs, abs);
|
|
9476
|
-
const isEscaping = rel === ".." || rel.startsWith(`..${pathSep}`) ||
|
|
8127
|
+
const isEscaping = rel === ".." || rel.startsWith(`..${pathSep}`) || isAbsolute4(rel);
|
|
9477
8128
|
if (isEscaping) {
|
|
9478
8129
|
throw new PluginSourceUnresolvableError(
|
|
9479
8130
|
ref.name,
|
|
@@ -9488,16 +8139,16 @@ function extractUrlSourceDir(marketplace, entry) {
|
|
|
9488
8139
|
const cacheDir = pluginCacheDir(marketplace.name, entry.name);
|
|
9489
8140
|
const subPath = typeof src.path === "string" ? src.path.trim() : "";
|
|
9490
8141
|
if (!subPath) return cacheDir;
|
|
9491
|
-
if (
|
|
8142
|
+
if (isAbsolute4(subPath)) {
|
|
9492
8143
|
throw new PluginSourceUnresolvableError(
|
|
9493
8144
|
entry.name,
|
|
9494
8145
|
marketplace.name,
|
|
9495
8146
|
`URL source subpath must be relative; got absolute '${subPath}'`
|
|
9496
8147
|
);
|
|
9497
8148
|
}
|
|
9498
|
-
const resolved =
|
|
8149
|
+
const resolved = pathResolve(cacheDir, subPath);
|
|
9499
8150
|
const rel = pathRelative2(cacheDir, resolved);
|
|
9500
|
-
if (rel === ".." || rel.startsWith(`..${pathSep}`) ||
|
|
8151
|
+
if (rel === ".." || rel.startsWith(`..${pathSep}`) || isAbsolute4(rel)) {
|
|
9501
8152
|
throw new PluginSourceUnresolvableError(
|
|
9502
8153
|
entry.name,
|
|
9503
8154
|
marketplace.name,
|
|
@@ -9539,6 +8190,12 @@ function extractRelativeSourcePath(ref, marketplace, entry) {
|
|
|
9539
8190
|
`source field has unexpected type '${typeof entry.source}'`
|
|
9540
8191
|
);
|
|
9541
8192
|
}
|
|
8193
|
+
async function injectImplicitMcpRef(sourceDir, manifest) {
|
|
8194
|
+
if (manifest.mcpServers !== void 0) return manifest;
|
|
8195
|
+
const mcpJsonPath = pathResolve(sourceDir, ".mcp.json");
|
|
8196
|
+
if (!await pathExists3(mcpJsonPath)) return manifest;
|
|
8197
|
+
return { ...manifest, mcpServers: "./.mcp.json" };
|
|
8198
|
+
}
|
|
9542
8199
|
function normalizeRushMcpCommand(ref, manifest, rushAiBinaryResolver) {
|
|
9543
8200
|
if (ref.name !== RUSH_AI_PLUGIN_NAME3 || ref.marketplace !== RUSH_AI_MARKETPLACE_NAME3 && ref.marketplace !== RUSH_AI_MARKETPLACE_NAME_NEW2) {
|
|
9544
8201
|
return manifest;
|
|
@@ -9555,11 +8212,11 @@ function normalizeRushMcpCommand(ref, manifest, rushAiBinaryResolver) {
|
|
|
9555
8212
|
if (typeof currentCommand !== "string" || currentCommand.length === 0) {
|
|
9556
8213
|
return manifest;
|
|
9557
8214
|
}
|
|
9558
|
-
if (
|
|
8215
|
+
if (isAbsolute4(currentCommand)) {
|
|
9559
8216
|
return manifest;
|
|
9560
8217
|
}
|
|
9561
8218
|
const resolved = (rushAiBinaryResolver ?? defaultRushAiBinaryResolver2)();
|
|
9562
|
-
if (!resolved || !
|
|
8219
|
+
if (!resolved || !isAbsolute4(resolved)) {
|
|
9563
8220
|
return manifest;
|
|
9564
8221
|
}
|
|
9565
8222
|
const nextServers = {
|
|
@@ -9570,7 +8227,7 @@ function normalizeRushMcpCommand(ref, manifest, rushAiBinaryResolver) {
|
|
|
9570
8227
|
}
|
|
9571
8228
|
function defaultRushAiBinaryResolver2() {
|
|
9572
8229
|
const argv1 = process.argv[1];
|
|
9573
|
-
if (typeof argv1 === "string" &&
|
|
8230
|
+
if (typeof argv1 === "string" && isAbsolute4(argv1)) {
|
|
9574
8231
|
return argv1;
|
|
9575
8232
|
}
|
|
9576
8233
|
return void 0;
|
|
@@ -9578,7 +8235,7 @@ function defaultRushAiBinaryResolver2() {
|
|
|
9578
8235
|
function pluginCacheDir(marketplaceName, pluginName) {
|
|
9579
8236
|
assertSafePathComponent(marketplaceName, "marketplace name");
|
|
9580
8237
|
assertSafePathComponent(pluginName, "plugin name");
|
|
9581
|
-
return
|
|
8238
|
+
return pathResolve(
|
|
9582
8239
|
homedir15(),
|
|
9583
8240
|
".rush",
|
|
9584
8241
|
"plugin-cache",
|
|
@@ -9604,7 +8261,7 @@ async function ensurePluginCloned(ref, marketplace, entry, opts) {
|
|
|
9604
8261
|
if (!src || typeof src !== "object" || Array.isArray(src)) return;
|
|
9605
8262
|
if (typeof src.url !== "string" || !src.url) return;
|
|
9606
8263
|
const cacheDir = pluginCacheDir(marketplace.name, entry.name);
|
|
9607
|
-
if (await
|
|
8264
|
+
if (await pathExists3(cacheDir)) return;
|
|
9608
8265
|
if (opts?.dryRun) {
|
|
9609
8266
|
throw new PluginSourceUnresolvableError(
|
|
9610
8267
|
ref.name,
|
|
@@ -9613,9 +8270,9 @@ async function ensurePluginCloned(ref, marketplace, entry, opts) {
|
|
|
9613
8270
|
);
|
|
9614
8271
|
}
|
|
9615
8272
|
const git = opts?.runner ?? defaultGitRunner;
|
|
9616
|
-
const parent =
|
|
9617
|
-
await
|
|
9618
|
-
const tmpDir = `${cacheDir}.${
|
|
8273
|
+
const parent = pathResolve(cacheDir, "..");
|
|
8274
|
+
await mkdir9(parent, { recursive: true });
|
|
8275
|
+
const tmpDir = `${cacheDir}.${randomUUID6()}.tmp`;
|
|
9619
8276
|
try {
|
|
9620
8277
|
const args = ["clone", "--depth", "1"];
|
|
9621
8278
|
if (typeof src.ref === "string" && src.ref.length > 0) {
|
|
@@ -9641,16 +8298,16 @@ async function ensurePluginCloned(ref, marketplace, entry, opts) {
|
|
|
9641
8298
|
}
|
|
9642
8299
|
}
|
|
9643
8300
|
try {
|
|
9644
|
-
await
|
|
8301
|
+
await rename6(tmpDir, cacheDir);
|
|
9645
8302
|
} catch (err) {
|
|
9646
|
-
if (err.code === "ENOTEMPTY" && await
|
|
9647
|
-
await
|
|
8303
|
+
if (err.code === "ENOTEMPTY" && await pathExists3(cacheDir)) {
|
|
8304
|
+
await rm10(tmpDir, { recursive: true, force: true });
|
|
9648
8305
|
return;
|
|
9649
8306
|
}
|
|
9650
8307
|
throw err;
|
|
9651
8308
|
}
|
|
9652
8309
|
} catch (err) {
|
|
9653
|
-
await
|
|
8310
|
+
await rm10(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
9654
8311
|
});
|
|
9655
8312
|
if (err instanceof PluginSourceUnresolvableError) throw err;
|
|
9656
8313
|
throw new PluginCloneFailedError(ref.name, marketplace.name, err);
|
|
@@ -9880,7 +8537,7 @@ function isTruthyEnvValue(value) {
|
|
|
9880
8537
|
}
|
|
9881
8538
|
async function verifyNewFormat(manifestPath, expectedName, _expectedVersion) {
|
|
9882
8539
|
try {
|
|
9883
|
-
await
|
|
8540
|
+
await access11(manifestPath);
|
|
9884
8541
|
} catch (err) {
|
|
9885
8542
|
return {
|
|
9886
8543
|
ok: false,
|
|
@@ -9889,7 +8546,7 @@ async function verifyNewFormat(manifestPath, expectedName, _expectedVersion) {
|
|
|
9889
8546
|
}
|
|
9890
8547
|
let raw;
|
|
9891
8548
|
try {
|
|
9892
|
-
raw = await
|
|
8549
|
+
raw = await readFile12(manifestPath, "utf8");
|
|
9893
8550
|
} catch (err) {
|
|
9894
8551
|
return {
|
|
9895
8552
|
ok: false,
|
|
@@ -10270,9 +8927,9 @@ async function runInstall(input) {
|
|
|
10270
8927
|
}
|
|
10271
8928
|
}
|
|
10272
8929
|
if (marketplace.source.kind === "rush") {
|
|
10273
|
-
const pluginDir =
|
|
10274
|
-
const manifestPath =
|
|
10275
|
-
const alreadyMaterialized = await
|
|
8930
|
+
const pluginDir = resolve20(marketplace.rootDir, "plugins", ref.name);
|
|
8931
|
+
const manifestPath = resolve20(pluginDir, ".claude-plugin/plugin.json");
|
|
8932
|
+
const alreadyMaterialized = await pathExists3(manifestPath);
|
|
10276
8933
|
const hasNewSecrets = input.secrets && Object.keys(input.secrets).length > 0;
|
|
10277
8934
|
const shouldMaterialize = !alreadyMaterialized || force || hasNewSecrets;
|
|
10278
8935
|
if (shouldMaterialize && dryRun && !alreadyMaterialized) {
|
|
@@ -10337,7 +8994,7 @@ async function runInstall(input) {
|
|
|
10337
8994
|
if (force && !dryRun) {
|
|
10338
8995
|
const entry = marketplace.manifest.plugins.find((p) => p.name === ref.name);
|
|
10339
8996
|
if (entry && hasUrlSource(entry)) {
|
|
10340
|
-
await
|
|
8997
|
+
await rm11(pluginCacheDir(ref.marketplace, ref.name), {
|
|
10341
8998
|
recursive: true,
|
|
10342
8999
|
force: true
|
|
10343
9000
|
});
|
|
@@ -10463,6 +9120,24 @@ async function runInstall(input) {
|
|
|
10463
9120
|
registryError = formatRegistryError(err);
|
|
10464
9121
|
}
|
|
10465
9122
|
}
|
|
9123
|
+
let codexMirrorSyncError;
|
|
9124
|
+
const codexResult = results.find((r) => r.target === "codex");
|
|
9125
|
+
if (!dryRun && codexResult?.status === "ok" && !skipCodexMirrorSync(input)) {
|
|
9126
|
+
try {
|
|
9127
|
+
const [{ syncCodexMarketplaceMirror: syncCodexMarketplaceMirror2 }, { pickContentLoader: pickContentLoader2 }] = await Promise.all([
|
|
9128
|
+
import("./mirror-IHLOSPAS.js"),
|
|
9129
|
+
import("./_codex-content-loader-Q7KBWZSB.js")
|
|
9130
|
+
]);
|
|
9131
|
+
await syncCodexMarketplaceMirror2(input.home ?? homedir17(), marketplace, {
|
|
9132
|
+
contentLoader: pickContentLoader2(marketplace)
|
|
9133
|
+
});
|
|
9134
|
+
} catch (err) {
|
|
9135
|
+
codexMirrorSyncError = err instanceof Error ? err.message : String(err);
|
|
9136
|
+
output.warn(
|
|
9137
|
+
`[codex] post-install marketplace mirror sync failed: ${codexMirrorSyncError}`
|
|
9138
|
+
);
|
|
9139
|
+
}
|
|
9140
|
+
}
|
|
10466
9141
|
const hasFailed = results.some((r) => r.status === "failed");
|
|
10467
9142
|
const exitCode = hasFailed ? 1 : 0;
|
|
10468
9143
|
return {
|
|
@@ -10473,9 +9148,15 @@ async function runInstall(input) {
|
|
|
10473
9148
|
dryRun,
|
|
10474
9149
|
exitCode,
|
|
10475
9150
|
...registryError !== void 0 ? { registryError } : {},
|
|
9151
|
+
...codexMirrorSyncError !== void 0 ? { codexMirrorSyncError } : {},
|
|
10476
9152
|
...migrationOutcome !== void 0 ? { migration: migrationOutcome } : {}
|
|
10477
9153
|
};
|
|
10478
9154
|
}
|
|
9155
|
+
function skipCodexMirrorSync(input) {
|
|
9156
|
+
if (input.skipCodexMirrorSync === true) return true;
|
|
9157
|
+
if (input.installerFactory !== void 0) return true;
|
|
9158
|
+
return false;
|
|
9159
|
+
}
|
|
10479
9160
|
function formatRegistryError(err) {
|
|
10480
9161
|
if (err instanceof RushRegistryConflictError) {
|
|
10481
9162
|
return `rush-ai registry write conflict: ${err.message}`;
|
|
@@ -11258,7 +9939,7 @@ function getReskillInvocation() {
|
|
|
11258
9939
|
};
|
|
11259
9940
|
}
|
|
11260
9941
|
function runReskill(args) {
|
|
11261
|
-
return new Promise((
|
|
9942
|
+
return new Promise((resolve23, reject) => {
|
|
11262
9943
|
const reskill = getReskillInvocation();
|
|
11263
9944
|
const child = spawn2(reskill.command, [...reskill.args, ...args], {
|
|
11264
9945
|
env: createReskillEnv(),
|
|
@@ -11268,10 +9949,10 @@ function runReskill(args) {
|
|
|
11268
9949
|
child.on("close", (code, signal) => {
|
|
11269
9950
|
if (signal) {
|
|
11270
9951
|
output.error(`reskill exited from signal ${signal}`);
|
|
11271
|
-
|
|
9952
|
+
resolve23(1);
|
|
11272
9953
|
return;
|
|
11273
9954
|
}
|
|
11274
|
-
|
|
9955
|
+
resolve23(code ?? 1);
|
|
11275
9956
|
});
|
|
11276
9957
|
});
|
|
11277
9958
|
}
|
|
@@ -11345,7 +10026,7 @@ function registerSkillCommand(program) {
|
|
|
11345
10026
|
|
|
11346
10027
|
// src/commands/task/index.ts
|
|
11347
10028
|
import { createWriteStream } from "fs";
|
|
11348
|
-
import { mkdir as
|
|
10029
|
+
import { mkdir as mkdir10, readFile as readFile13, stat as stat8 } from "fs/promises";
|
|
11349
10030
|
import path3 from "path";
|
|
11350
10031
|
import { Readable } from "stream";
|
|
11351
10032
|
import { pipeline } from "stream/promises";
|
|
@@ -11371,13 +10052,13 @@ async function readStdinIfPiped() {
|
|
|
11371
10052
|
if (process.stdin.isTTY) {
|
|
11372
10053
|
return null;
|
|
11373
10054
|
}
|
|
11374
|
-
return new Promise((
|
|
10055
|
+
return new Promise((resolve23, reject) => {
|
|
11375
10056
|
const chunks = [];
|
|
11376
10057
|
process.stdin.on("data", (chunk) => {
|
|
11377
10058
|
chunks.push(chunk);
|
|
11378
10059
|
});
|
|
11379
10060
|
process.stdin.on("end", () => {
|
|
11380
|
-
|
|
10061
|
+
resolve23(Buffer.concat(chunks).toString("utf-8").trim());
|
|
11381
10062
|
});
|
|
11382
10063
|
process.stdin.on("error", reject);
|
|
11383
10064
|
});
|
|
@@ -11578,7 +10259,7 @@ function buildDeployUrl(prefix, env, apiBaseUrl) {
|
|
|
11578
10259
|
}
|
|
11579
10260
|
|
|
11580
10261
|
// src/commands/task/push.ts
|
|
11581
|
-
import { resolve as
|
|
10262
|
+
import { resolve as resolve21 } from "path";
|
|
11582
10263
|
import chalk5 from "chalk";
|
|
11583
10264
|
|
|
11584
10265
|
// src/util/env-file.ts
|
|
@@ -11722,7 +10403,7 @@ function registerPushSubcommand(task, program) {
|
|
|
11722
10403
|
requireAuth();
|
|
11723
10404
|
const format = resolveFormat(program.opts());
|
|
11724
10405
|
const client = createClient();
|
|
11725
|
-
const projectPath =
|
|
10406
|
+
const projectPath = resolve21(opts.path);
|
|
11726
10407
|
if (format !== "json") {
|
|
11727
10408
|
output.info("Checking project readiness...");
|
|
11728
10409
|
}
|
|
@@ -12153,7 +10834,7 @@ async function confirmPublish(args) {
|
|
|
12153
10834
|
return answer.trim().toLowerCase().startsWith("y");
|
|
12154
10835
|
}
|
|
12155
10836
|
function readOneLine() {
|
|
12156
|
-
return new Promise((
|
|
10837
|
+
return new Promise((resolve23) => {
|
|
12157
10838
|
let acc = "";
|
|
12158
10839
|
const onData = (chunk) => {
|
|
12159
10840
|
acc += chunk.toString("utf-8");
|
|
@@ -12161,7 +10842,7 @@ function readOneLine() {
|
|
|
12161
10842
|
if (nlIdx !== -1) {
|
|
12162
10843
|
process.stdin.removeListener("data", onData);
|
|
12163
10844
|
process.stdin.pause();
|
|
12164
|
-
|
|
10845
|
+
resolve23(acc.slice(0, nlIdx));
|
|
12165
10846
|
}
|
|
12166
10847
|
};
|
|
12167
10848
|
process.stdin.resume();
|
|
@@ -12347,12 +11028,12 @@ function registerDomainSubcommand(task, program) {
|
|
|
12347
11028
|
}
|
|
12348
11029
|
|
|
12349
11030
|
// src/commands/task/env.ts
|
|
12350
|
-
import { resolve as
|
|
11031
|
+
import { resolve as resolve22 } from "path";
|
|
12351
11032
|
|
|
12352
11033
|
// src/util/prompt.ts
|
|
12353
11034
|
import { createInterface } from "readline";
|
|
12354
11035
|
function promptSecret(message) {
|
|
12355
|
-
return new Promise((
|
|
11036
|
+
return new Promise((resolve23, reject) => {
|
|
12356
11037
|
const rl = createInterface({
|
|
12357
11038
|
input: process.stdin,
|
|
12358
11039
|
output: process.stdout,
|
|
@@ -12365,7 +11046,7 @@ function promptSecret(message) {
|
|
|
12365
11046
|
rl.question("", (answer) => {
|
|
12366
11047
|
rl.close();
|
|
12367
11048
|
process.stdout.write("\n");
|
|
12368
|
-
|
|
11049
|
+
resolve23(answer);
|
|
12369
11050
|
});
|
|
12370
11051
|
rl.on("SIGINT", () => {
|
|
12371
11052
|
rl.close();
|
|
@@ -12388,7 +11069,7 @@ var NO_PROJECT_HINT2 = [
|
|
|
12388
11069
|
].join("\n");
|
|
12389
11070
|
function resolveProjectId(opts) {
|
|
12390
11071
|
if (opts.project) return Promise.resolve(opts.project);
|
|
12391
|
-
const projectPath =
|
|
11072
|
+
const projectPath = resolve22(opts.path ?? ".");
|
|
12392
11073
|
const remoteUrl = isGitRepo(projectPath) ? getRemoteUrl(projectPath) : null;
|
|
12393
11074
|
const idFromRemote = remoteUrl ? extractRushProjectId(remoteUrl) : null;
|
|
12394
11075
|
if (idFromRemote) return Promise.resolve(idFromRemote);
|
|
@@ -13116,7 +11797,7 @@ async function downloadFile(file2, destPath, client) {
|
|
|
13116
11797
|
if (!response.body) {
|
|
13117
11798
|
throw new Error("No response body");
|
|
13118
11799
|
}
|
|
13119
|
-
await
|
|
11800
|
+
await mkdir10(path3.dirname(destPath), { recursive: true });
|
|
13120
11801
|
const nodeStream = Readable.fromWeb(
|
|
13121
11802
|
response.body
|
|
13122
11803
|
);
|
|
@@ -13657,7 +12338,7 @@ async function uploadFileForTask(client, localPath) {
|
|
|
13657
12338
|
const resolved = path3.resolve(localPath);
|
|
13658
12339
|
let fileStat;
|
|
13659
12340
|
try {
|
|
13660
|
-
fileStat = await
|
|
12341
|
+
fileStat = await stat8(resolved);
|
|
13661
12342
|
} catch {
|
|
13662
12343
|
throw new RushError(`File not found: ${localPath}`);
|
|
13663
12344
|
}
|
|
@@ -13671,7 +12352,7 @@ async function uploadFileForTask(client, localPath) {
|
|
|
13671
12352
|
}
|
|
13672
12353
|
const filename = path3.basename(resolved);
|
|
13673
12354
|
const mediaType = guessMediaType(resolved);
|
|
13674
|
-
const buf = await
|
|
12355
|
+
const buf = await readFile13(resolved);
|
|
13675
12356
|
const blob = new File([buf], filename, { type: mediaType });
|
|
13676
12357
|
const formData = new FormData();
|
|
13677
12358
|
formData.append("file", blob);
|
|
@@ -13724,9 +12405,9 @@ function registerCommands(program) {
|
|
|
13724
12405
|
}
|
|
13725
12406
|
|
|
13726
12407
|
// src/util/update-check.ts
|
|
13727
|
-
import { mkdir as
|
|
13728
|
-
import { homedir as
|
|
13729
|
-
import { dirname as
|
|
12408
|
+
import { mkdir as mkdir11, readFile as readFile14, writeFile as writeFile7 } from "fs/promises";
|
|
12409
|
+
import { homedir as homedir18 } from "os";
|
|
12410
|
+
import { dirname as dirname9, join as join10 } from "path";
|
|
13730
12411
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
13731
12412
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
13732
12413
|
var REGISTRY_URL = "https://registry.npmjs.org/rush-ai/latest";
|
|
@@ -13749,19 +12430,19 @@ function isNewerVersion(current, latest) {
|
|
|
13749
12430
|
}
|
|
13750
12431
|
async function writeLastCheck(checkFile) {
|
|
13751
12432
|
try {
|
|
13752
|
-
await
|
|
13753
|
-
await
|
|
12433
|
+
await mkdir11(dirname9(checkFile), { recursive: true });
|
|
12434
|
+
await writeFile7(checkFile, JSON.stringify({ lastCheck: Date.now() }));
|
|
13754
12435
|
} catch {
|
|
13755
12436
|
}
|
|
13756
12437
|
}
|
|
13757
12438
|
async function checkForUpdate(currentVersion) {
|
|
13758
|
-
const rushDir = join10(
|
|
12439
|
+
const rushDir = join10(homedir18(), ".rush");
|
|
13759
12440
|
const checkFile = join10(rushDir, "update-check.json");
|
|
13760
12441
|
try {
|
|
13761
12442
|
if (process.env.CI) return;
|
|
13762
12443
|
let lastCheck = 0;
|
|
13763
12444
|
try {
|
|
13764
|
-
const data = JSON.parse(await
|
|
12445
|
+
const data = JSON.parse(await readFile14(checkFile, "utf-8"));
|
|
13765
12446
|
lastCheck = data.lastCheck ?? 0;
|
|
13766
12447
|
} catch {
|
|
13767
12448
|
}
|