@learnrudi/cli 1.5.0 → 1.6.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 +60 -63
- package/dist/index.cjs +2108 -903
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -186,6 +186,7 @@ __export(src_exports, {
|
|
|
186
186
|
PACKAGE_KINDS: () => PACKAGE_KINDS2,
|
|
187
187
|
RUNTIMES_DOWNLOAD_BASE: () => RUNTIMES_DOWNLOAD_BASE,
|
|
188
188
|
RUNTIMES_RELEASE_VERSION: () => RUNTIMES_RELEASE_VERSION,
|
|
189
|
+
STACKS_RELEASE_VERSION: () => STACKS_RELEASE_VERSION,
|
|
189
190
|
checkCache: () => checkCache,
|
|
190
191
|
clearCache: () => clearCache,
|
|
191
192
|
computeHash: () => computeHash,
|
|
@@ -377,24 +378,72 @@ function getPackageKinds() {
|
|
|
377
378
|
async function downloadPackage(pkg, destPath, options = {}) {
|
|
378
379
|
const { onProgress } = options;
|
|
379
380
|
const registryPath = pkg.path;
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
381
|
+
if (!import_fs2.default.existsSync(destPath)) {
|
|
382
|
+
import_fs2.default.mkdirSync(destPath, { recursive: true });
|
|
383
|
+
}
|
|
384
|
+
onProgress?.({ phase: "downloading", package: pkg.name || pkg.id });
|
|
385
|
+
if (pkg.kind === "stack" || registryPath.includes("/stacks/")) {
|
|
386
|
+
await downloadStackTarball(pkg, destPath, onProgress);
|
|
387
|
+
return { success: true, path: destPath };
|
|
388
|
+
}
|
|
389
|
+
if (registryPath.endsWith(".md")) {
|
|
390
|
+
const url = `${GITHUB_RAW_BASE}/${registryPath}`;
|
|
391
|
+
const response = await fetch(url, {
|
|
392
|
+
headers: { "User-Agent": "rudi-cli/2.0" }
|
|
393
|
+
});
|
|
394
|
+
if (!response.ok) {
|
|
395
|
+
throw new Error(`Failed to download ${registryPath}: HTTP ${response.status}`);
|
|
396
|
+
}
|
|
397
|
+
const content = await response.text();
|
|
398
|
+
const destDir = import_path2.default.dirname(destPath);
|
|
399
|
+
if (!import_fs2.default.existsSync(destDir)) {
|
|
400
|
+
import_fs2.default.mkdirSync(destDir, { recursive: true });
|
|
401
|
+
}
|
|
402
|
+
import_fs2.default.writeFileSync(destPath, content);
|
|
403
|
+
return { success: true, path: destPath };
|
|
404
|
+
}
|
|
405
|
+
throw new Error(`Unsupported package type: ${registryPath}`);
|
|
406
|
+
}
|
|
407
|
+
async function downloadStackTarball(pkg, destPath, onProgress) {
|
|
408
|
+
const stackName = pkg.id?.replace("stack:", "") || import_path2.default.basename(pkg.path);
|
|
409
|
+
const version = pkg.version || "1.0.0";
|
|
410
|
+
const tarballName = `${stackName}-${version}.tar.gz`;
|
|
411
|
+
const url = `${STACKS_DOWNLOAD_BASE}/${STACKS_RELEASE_VERSION}/${tarballName}`;
|
|
412
|
+
onProgress?.({ phase: "downloading", package: stackName, url });
|
|
413
|
+
const tempDir = import_path2.default.join(PATHS.cache, "downloads");
|
|
414
|
+
if (!import_fs2.default.existsSync(tempDir)) {
|
|
415
|
+
import_fs2.default.mkdirSync(tempDir, { recursive: true });
|
|
416
|
+
}
|
|
417
|
+
const tempFile = import_path2.default.join(tempDir, tarballName);
|
|
418
|
+
try {
|
|
419
|
+
const response = await fetch(url, {
|
|
420
|
+
headers: {
|
|
421
|
+
"User-Agent": "rudi-cli/2.0",
|
|
422
|
+
"Accept": "application/octet-stream"
|
|
393
423
|
}
|
|
394
|
-
|
|
424
|
+
});
|
|
425
|
+
if (!response.ok) {
|
|
426
|
+
throw new Error(`Failed to download ${stackName}: HTTP ${response.status}`);
|
|
427
|
+
}
|
|
428
|
+
const buffer = await response.arrayBuffer();
|
|
429
|
+
import_fs2.default.writeFileSync(tempFile, Buffer.from(buffer));
|
|
430
|
+
onProgress?.({ phase: "extracting", package: stackName });
|
|
431
|
+
if (import_fs2.default.existsSync(destPath)) {
|
|
432
|
+
import_fs2.default.rmSync(destPath, { recursive: true });
|
|
433
|
+
}
|
|
434
|
+
import_fs2.default.mkdirSync(destPath, { recursive: true });
|
|
435
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
436
|
+
execSync7(`tar -xzf "${tempFile}" -C "${destPath}"`, {
|
|
437
|
+
stdio: "pipe"
|
|
438
|
+
});
|
|
439
|
+
import_fs2.default.unlinkSync(tempFile);
|
|
440
|
+
onProgress?.({ phase: "complete", package: stackName, path: destPath });
|
|
441
|
+
} catch (error) {
|
|
442
|
+
if (import_fs2.default.existsSync(tempFile)) {
|
|
443
|
+
import_fs2.default.unlinkSync(tempFile);
|
|
395
444
|
}
|
|
445
|
+
throw new Error(`Failed to install ${stackName}: ${error.message}`);
|
|
396
446
|
}
|
|
397
|
-
throw new Error(`Package source not found: ${registryPath}`);
|
|
398
447
|
}
|
|
399
448
|
async function downloadRuntime(runtime, version, destPath, options = {}) {
|
|
400
449
|
const { onProgress } = options;
|
|
@@ -425,8 +474,8 @@ async function downloadRuntime(runtime, version, destPath, options = {}) {
|
|
|
425
474
|
import_fs2.default.rmSync(destPath, { recursive: true });
|
|
426
475
|
}
|
|
427
476
|
import_fs2.default.mkdirSync(destPath, { recursive: true });
|
|
428
|
-
const { execSync:
|
|
429
|
-
|
|
477
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
478
|
+
execSync7(`tar -xzf "${tempFile}" -C "${destPath}" --strip-components=1`, {
|
|
430
479
|
stdio: "pipe"
|
|
431
480
|
});
|
|
432
481
|
import_fs2.default.unlinkSync(tempFile);
|
|
@@ -464,7 +513,7 @@ async function downloadTool(toolName, destPath, options = {}) {
|
|
|
464
513
|
import_fs2.default.rmSync(destPath, { recursive: true });
|
|
465
514
|
}
|
|
466
515
|
import_fs2.default.mkdirSync(destPath, { recursive: true });
|
|
467
|
-
const { execSync:
|
|
516
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
468
517
|
const downloads = toolManifest.downloads?.[platformArch];
|
|
469
518
|
if (downloads && Array.isArray(downloads)) {
|
|
470
519
|
const downloadedUrls = /* @__PURE__ */ new Set();
|
|
@@ -493,11 +542,11 @@ async function downloadTool(toolName, destPath, options = {}) {
|
|
|
493
542
|
onProgress?.({ phase: "extracting", tool: toolName, binary: import_path2.default.basename(binary) });
|
|
494
543
|
const archiveType = type || guessArchiveType(urlFilename);
|
|
495
544
|
if (archiveType === "zip") {
|
|
496
|
-
|
|
545
|
+
execSync7(`unzip -o "${tempFile}" -d "${destPath}"`, { stdio: "pipe" });
|
|
497
546
|
} else if (archiveType === "tar.xz") {
|
|
498
|
-
|
|
547
|
+
execSync7(`tar -xJf "${tempFile}" -C "${destPath}"`, { stdio: "pipe" });
|
|
499
548
|
} else if (archiveType === "tar.gz" || archiveType === "tgz") {
|
|
500
|
-
|
|
549
|
+
execSync7(`tar -xzf "${tempFile}" -C "${destPath}"`, { stdio: "pipe" });
|
|
501
550
|
} else {
|
|
502
551
|
throw new Error(`Unsupported archive type: ${archiveType}`);
|
|
503
552
|
}
|
|
@@ -544,11 +593,11 @@ async function downloadTool(toolName, destPath, options = {}) {
|
|
|
544
593
|
onProgress?.({ phase: "extracting", tool: toolName });
|
|
545
594
|
const archiveType = extractConfig.type || guessArchiveType(urlFilename);
|
|
546
595
|
if (archiveType === "zip") {
|
|
547
|
-
|
|
596
|
+
execSync7(`unzip -o "${tempFile}" -d "${destPath}"`, { stdio: "pipe" });
|
|
548
597
|
} else if (archiveType === "tar.xz") {
|
|
549
|
-
|
|
598
|
+
execSync7(`tar -xJf "${tempFile}" -C "${destPath}"`, { stdio: "pipe" });
|
|
550
599
|
} else if (archiveType === "tar.gz" || archiveType === "tgz") {
|
|
551
|
-
|
|
600
|
+
execSync7(`tar -xzf "${tempFile}" -C "${destPath}"`, { stdio: "pipe" });
|
|
552
601
|
} else {
|
|
553
602
|
throw new Error(`Unsupported archive type: ${archiveType}`);
|
|
554
603
|
}
|
|
@@ -671,22 +720,7 @@ async function computeHash(filePath) {
|
|
|
671
720
|
stream.on("error", reject);
|
|
672
721
|
});
|
|
673
722
|
}
|
|
674
|
-
|
|
675
|
-
import_fs2.default.mkdirSync(dest, { recursive: true });
|
|
676
|
-
const entries = import_fs2.default.readdirSync(src, { withFileTypes: true });
|
|
677
|
-
for (const entry of entries) {
|
|
678
|
-
const srcPath = import_path2.default.join(src, entry.name);
|
|
679
|
-
const destPath = import_path2.default.join(dest, entry.name);
|
|
680
|
-
if (entry.isDirectory()) {
|
|
681
|
-
if (entry.name !== "node_modules" && entry.name !== ".git") {
|
|
682
|
-
await copyDirectory(srcPath, destPath);
|
|
683
|
-
}
|
|
684
|
-
} else {
|
|
685
|
-
import_fs2.default.copyFileSync(srcPath, destPath);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
var import_fs2, import_path2, import_crypto, DEFAULT_REGISTRY_URL, RUNTIMES_DOWNLOAD_BASE, CACHE_TTL, PACKAGE_KINDS2, KIND_PLURALS, RUNTIMES_RELEASE_VERSION;
|
|
723
|
+
var import_fs2, import_path2, import_crypto, DEFAULT_REGISTRY_URL, RUNTIMES_DOWNLOAD_BASE, CACHE_TTL, PACKAGE_KINDS2, KIND_PLURALS, GITHUB_RAW_BASE, STACKS_DOWNLOAD_BASE, STACKS_RELEASE_VERSION, RUNTIMES_RELEASE_VERSION;
|
|
690
724
|
var init_src2 = __esm({
|
|
691
725
|
"../packages/registry-client/src/index.js"() {
|
|
692
726
|
import_fs2 = __toESM(require("fs"), 1);
|
|
@@ -700,6 +734,9 @@ var init_src2 = __esm({
|
|
|
700
734
|
KIND_PLURALS = {
|
|
701
735
|
binary: "binaries"
|
|
702
736
|
};
|
|
737
|
+
GITHUB_RAW_BASE = "https://raw.githubusercontent.com/learn-rudi/registry/main";
|
|
738
|
+
STACKS_DOWNLOAD_BASE = "https://github.com/learn-rudi/registry/releases/download";
|
|
739
|
+
STACKS_RELEASE_VERSION = "stacks-v1.0.0";
|
|
703
740
|
RUNTIMES_RELEASE_VERSION = "v1.0.0";
|
|
704
741
|
}
|
|
705
742
|
});
|
|
@@ -781,17 +818,17 @@ var require_visit = __commonJS({
|
|
|
781
818
|
visit.BREAK = BREAK;
|
|
782
819
|
visit.SKIP = SKIP;
|
|
783
820
|
visit.REMOVE = REMOVE;
|
|
784
|
-
function visit_(key, node, visitor,
|
|
785
|
-
const ctrl = callVisitor(key, node, visitor,
|
|
821
|
+
function visit_(key, node, visitor, path24) {
|
|
822
|
+
const ctrl = callVisitor(key, node, visitor, path24);
|
|
786
823
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
787
|
-
replaceNode(key,
|
|
788
|
-
return visit_(key, ctrl, visitor,
|
|
824
|
+
replaceNode(key, path24, ctrl);
|
|
825
|
+
return visit_(key, ctrl, visitor, path24);
|
|
789
826
|
}
|
|
790
827
|
if (typeof ctrl !== "symbol") {
|
|
791
828
|
if (identity.isCollection(node)) {
|
|
792
|
-
|
|
829
|
+
path24 = Object.freeze(path24.concat(node));
|
|
793
830
|
for (let i = 0; i < node.items.length; ++i) {
|
|
794
|
-
const ci = visit_(i, node.items[i], visitor,
|
|
831
|
+
const ci = visit_(i, node.items[i], visitor, path24);
|
|
795
832
|
if (typeof ci === "number")
|
|
796
833
|
i = ci - 1;
|
|
797
834
|
else if (ci === BREAK)
|
|
@@ -802,13 +839,13 @@ var require_visit = __commonJS({
|
|
|
802
839
|
}
|
|
803
840
|
}
|
|
804
841
|
} else if (identity.isPair(node)) {
|
|
805
|
-
|
|
806
|
-
const ck = visit_("key", node.key, visitor,
|
|
842
|
+
path24 = Object.freeze(path24.concat(node));
|
|
843
|
+
const ck = visit_("key", node.key, visitor, path24);
|
|
807
844
|
if (ck === BREAK)
|
|
808
845
|
return BREAK;
|
|
809
846
|
else if (ck === REMOVE)
|
|
810
847
|
node.key = null;
|
|
811
|
-
const cv = visit_("value", node.value, visitor,
|
|
848
|
+
const cv = visit_("value", node.value, visitor, path24);
|
|
812
849
|
if (cv === BREAK)
|
|
813
850
|
return BREAK;
|
|
814
851
|
else if (cv === REMOVE)
|
|
@@ -829,17 +866,17 @@ var require_visit = __commonJS({
|
|
|
829
866
|
visitAsync.BREAK = BREAK;
|
|
830
867
|
visitAsync.SKIP = SKIP;
|
|
831
868
|
visitAsync.REMOVE = REMOVE;
|
|
832
|
-
async function visitAsync_(key, node, visitor,
|
|
833
|
-
const ctrl = await callVisitor(key, node, visitor,
|
|
869
|
+
async function visitAsync_(key, node, visitor, path24) {
|
|
870
|
+
const ctrl = await callVisitor(key, node, visitor, path24);
|
|
834
871
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
835
|
-
replaceNode(key,
|
|
836
|
-
return visitAsync_(key, ctrl, visitor,
|
|
872
|
+
replaceNode(key, path24, ctrl);
|
|
873
|
+
return visitAsync_(key, ctrl, visitor, path24);
|
|
837
874
|
}
|
|
838
875
|
if (typeof ctrl !== "symbol") {
|
|
839
876
|
if (identity.isCollection(node)) {
|
|
840
|
-
|
|
877
|
+
path24 = Object.freeze(path24.concat(node));
|
|
841
878
|
for (let i = 0; i < node.items.length; ++i) {
|
|
842
|
-
const ci = await visitAsync_(i, node.items[i], visitor,
|
|
879
|
+
const ci = await visitAsync_(i, node.items[i], visitor, path24);
|
|
843
880
|
if (typeof ci === "number")
|
|
844
881
|
i = ci - 1;
|
|
845
882
|
else if (ci === BREAK)
|
|
@@ -850,13 +887,13 @@ var require_visit = __commonJS({
|
|
|
850
887
|
}
|
|
851
888
|
}
|
|
852
889
|
} else if (identity.isPair(node)) {
|
|
853
|
-
|
|
854
|
-
const ck = await visitAsync_("key", node.key, visitor,
|
|
890
|
+
path24 = Object.freeze(path24.concat(node));
|
|
891
|
+
const ck = await visitAsync_("key", node.key, visitor, path24);
|
|
855
892
|
if (ck === BREAK)
|
|
856
893
|
return BREAK;
|
|
857
894
|
else if (ck === REMOVE)
|
|
858
895
|
node.key = null;
|
|
859
|
-
const cv = await visitAsync_("value", node.value, visitor,
|
|
896
|
+
const cv = await visitAsync_("value", node.value, visitor, path24);
|
|
860
897
|
if (cv === BREAK)
|
|
861
898
|
return BREAK;
|
|
862
899
|
else if (cv === REMOVE)
|
|
@@ -883,23 +920,23 @@ var require_visit = __commonJS({
|
|
|
883
920
|
}
|
|
884
921
|
return visitor;
|
|
885
922
|
}
|
|
886
|
-
function callVisitor(key, node, visitor,
|
|
923
|
+
function callVisitor(key, node, visitor, path24) {
|
|
887
924
|
if (typeof visitor === "function")
|
|
888
|
-
return visitor(key, node,
|
|
925
|
+
return visitor(key, node, path24);
|
|
889
926
|
if (identity.isMap(node))
|
|
890
|
-
return visitor.Map?.(key, node,
|
|
927
|
+
return visitor.Map?.(key, node, path24);
|
|
891
928
|
if (identity.isSeq(node))
|
|
892
|
-
return visitor.Seq?.(key, node,
|
|
929
|
+
return visitor.Seq?.(key, node, path24);
|
|
893
930
|
if (identity.isPair(node))
|
|
894
|
-
return visitor.Pair?.(key, node,
|
|
931
|
+
return visitor.Pair?.(key, node, path24);
|
|
895
932
|
if (identity.isScalar(node))
|
|
896
|
-
return visitor.Scalar?.(key, node,
|
|
933
|
+
return visitor.Scalar?.(key, node, path24);
|
|
897
934
|
if (identity.isAlias(node))
|
|
898
|
-
return visitor.Alias?.(key, node,
|
|
935
|
+
return visitor.Alias?.(key, node, path24);
|
|
899
936
|
return void 0;
|
|
900
937
|
}
|
|
901
|
-
function replaceNode(key,
|
|
902
|
-
const parent =
|
|
938
|
+
function replaceNode(key, path24, node) {
|
|
939
|
+
const parent = path24[path24.length - 1];
|
|
903
940
|
if (identity.isCollection(parent)) {
|
|
904
941
|
parent.items[key] = node;
|
|
905
942
|
} else if (identity.isPair(parent)) {
|
|
@@ -1507,10 +1544,10 @@ var require_Collection = __commonJS({
|
|
|
1507
1544
|
var createNode = require_createNode();
|
|
1508
1545
|
var identity = require_identity();
|
|
1509
1546
|
var Node = require_Node();
|
|
1510
|
-
function collectionFromPath(schema,
|
|
1547
|
+
function collectionFromPath(schema, path24, value) {
|
|
1511
1548
|
let v = value;
|
|
1512
|
-
for (let i =
|
|
1513
|
-
const k =
|
|
1549
|
+
for (let i = path24.length - 1; i >= 0; --i) {
|
|
1550
|
+
const k = path24[i];
|
|
1514
1551
|
if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
|
|
1515
1552
|
const a = [];
|
|
1516
1553
|
a[k] = v;
|
|
@@ -1529,7 +1566,7 @@ var require_Collection = __commonJS({
|
|
|
1529
1566
|
sourceObjects: /* @__PURE__ */ new Map()
|
|
1530
1567
|
});
|
|
1531
1568
|
}
|
|
1532
|
-
var isEmptyPath = (
|
|
1569
|
+
var isEmptyPath = (path24) => path24 == null || typeof path24 === "object" && !!path24[Symbol.iterator]().next().done;
|
|
1533
1570
|
var Collection = class extends Node.NodeBase {
|
|
1534
1571
|
constructor(type, schema) {
|
|
1535
1572
|
super(type);
|
|
@@ -1559,11 +1596,11 @@ var require_Collection = __commonJS({
|
|
|
1559
1596
|
* be a Pair instance or a `{ key, value }` object, which may not have a key
|
|
1560
1597
|
* that already exists in the map.
|
|
1561
1598
|
*/
|
|
1562
|
-
addIn(
|
|
1563
|
-
if (isEmptyPath(
|
|
1599
|
+
addIn(path24, value) {
|
|
1600
|
+
if (isEmptyPath(path24))
|
|
1564
1601
|
this.add(value);
|
|
1565
1602
|
else {
|
|
1566
|
-
const [key, ...rest] =
|
|
1603
|
+
const [key, ...rest] = path24;
|
|
1567
1604
|
const node = this.get(key, true);
|
|
1568
1605
|
if (identity.isCollection(node))
|
|
1569
1606
|
node.addIn(rest, value);
|
|
@@ -1577,8 +1614,8 @@ var require_Collection = __commonJS({
|
|
|
1577
1614
|
* Removes a value from the collection.
|
|
1578
1615
|
* @returns `true` if the item was found and removed.
|
|
1579
1616
|
*/
|
|
1580
|
-
deleteIn(
|
|
1581
|
-
const [key, ...rest] =
|
|
1617
|
+
deleteIn(path24) {
|
|
1618
|
+
const [key, ...rest] = path24;
|
|
1582
1619
|
if (rest.length === 0)
|
|
1583
1620
|
return this.delete(key);
|
|
1584
1621
|
const node = this.get(key, true);
|
|
@@ -1592,8 +1629,8 @@ var require_Collection = __commonJS({
|
|
|
1592
1629
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
1593
1630
|
* `true` (collections are always returned intact).
|
|
1594
1631
|
*/
|
|
1595
|
-
getIn(
|
|
1596
|
-
const [key, ...rest] =
|
|
1632
|
+
getIn(path24, keepScalar) {
|
|
1633
|
+
const [key, ...rest] = path24;
|
|
1597
1634
|
const node = this.get(key, true);
|
|
1598
1635
|
if (rest.length === 0)
|
|
1599
1636
|
return !keepScalar && identity.isScalar(node) ? node.value : node;
|
|
@@ -1611,8 +1648,8 @@ var require_Collection = __commonJS({
|
|
|
1611
1648
|
/**
|
|
1612
1649
|
* Checks if the collection includes a value with the key `key`.
|
|
1613
1650
|
*/
|
|
1614
|
-
hasIn(
|
|
1615
|
-
const [key, ...rest] =
|
|
1651
|
+
hasIn(path24) {
|
|
1652
|
+
const [key, ...rest] = path24;
|
|
1616
1653
|
if (rest.length === 0)
|
|
1617
1654
|
return this.has(key);
|
|
1618
1655
|
const node = this.get(key, true);
|
|
@@ -1622,8 +1659,8 @@ var require_Collection = __commonJS({
|
|
|
1622
1659
|
* Sets a value in this collection. For `!!set`, `value` needs to be a
|
|
1623
1660
|
* boolean to add/remove the item from the set.
|
|
1624
1661
|
*/
|
|
1625
|
-
setIn(
|
|
1626
|
-
const [key, ...rest] =
|
|
1662
|
+
setIn(path24, value) {
|
|
1663
|
+
const [key, ...rest] = path24;
|
|
1627
1664
|
if (rest.length === 0) {
|
|
1628
1665
|
this.set(key, value);
|
|
1629
1666
|
} else {
|
|
@@ -4127,9 +4164,9 @@ var require_Document = __commonJS({
|
|
|
4127
4164
|
this.contents.add(value);
|
|
4128
4165
|
}
|
|
4129
4166
|
/** Adds a value to the document. */
|
|
4130
|
-
addIn(
|
|
4167
|
+
addIn(path24, value) {
|
|
4131
4168
|
if (assertCollection(this.contents))
|
|
4132
|
-
this.contents.addIn(
|
|
4169
|
+
this.contents.addIn(path24, value);
|
|
4133
4170
|
}
|
|
4134
4171
|
/**
|
|
4135
4172
|
* Create a new `Alias` node, ensuring that the target `node` has the required anchor.
|
|
@@ -4204,14 +4241,14 @@ var require_Document = __commonJS({
|
|
|
4204
4241
|
* Removes a value from the document.
|
|
4205
4242
|
* @returns `true` if the item was found and removed.
|
|
4206
4243
|
*/
|
|
4207
|
-
deleteIn(
|
|
4208
|
-
if (Collection.isEmptyPath(
|
|
4244
|
+
deleteIn(path24) {
|
|
4245
|
+
if (Collection.isEmptyPath(path24)) {
|
|
4209
4246
|
if (this.contents == null)
|
|
4210
4247
|
return false;
|
|
4211
4248
|
this.contents = null;
|
|
4212
4249
|
return true;
|
|
4213
4250
|
}
|
|
4214
|
-
return assertCollection(this.contents) ? this.contents.deleteIn(
|
|
4251
|
+
return assertCollection(this.contents) ? this.contents.deleteIn(path24) : false;
|
|
4215
4252
|
}
|
|
4216
4253
|
/**
|
|
4217
4254
|
* Returns item at `key`, or `undefined` if not found. By default unwraps
|
|
@@ -4226,10 +4263,10 @@ var require_Document = __commonJS({
|
|
|
4226
4263
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
4227
4264
|
* `true` (collections are always returned intact).
|
|
4228
4265
|
*/
|
|
4229
|
-
getIn(
|
|
4230
|
-
if (Collection.isEmptyPath(
|
|
4266
|
+
getIn(path24, keepScalar) {
|
|
4267
|
+
if (Collection.isEmptyPath(path24))
|
|
4231
4268
|
return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
|
|
4232
|
-
return identity.isCollection(this.contents) ? this.contents.getIn(
|
|
4269
|
+
return identity.isCollection(this.contents) ? this.contents.getIn(path24, keepScalar) : void 0;
|
|
4233
4270
|
}
|
|
4234
4271
|
/**
|
|
4235
4272
|
* Checks if the document includes a value with the key `key`.
|
|
@@ -4240,10 +4277,10 @@ var require_Document = __commonJS({
|
|
|
4240
4277
|
/**
|
|
4241
4278
|
* Checks if the document includes a value at `path`.
|
|
4242
4279
|
*/
|
|
4243
|
-
hasIn(
|
|
4244
|
-
if (Collection.isEmptyPath(
|
|
4280
|
+
hasIn(path24) {
|
|
4281
|
+
if (Collection.isEmptyPath(path24))
|
|
4245
4282
|
return this.contents !== void 0;
|
|
4246
|
-
return identity.isCollection(this.contents) ? this.contents.hasIn(
|
|
4283
|
+
return identity.isCollection(this.contents) ? this.contents.hasIn(path24) : false;
|
|
4247
4284
|
}
|
|
4248
4285
|
/**
|
|
4249
4286
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
@@ -4260,13 +4297,13 @@ var require_Document = __commonJS({
|
|
|
4260
4297
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
4261
4298
|
* boolean to add/remove the item from the set.
|
|
4262
4299
|
*/
|
|
4263
|
-
setIn(
|
|
4264
|
-
if (Collection.isEmptyPath(
|
|
4300
|
+
setIn(path24, value) {
|
|
4301
|
+
if (Collection.isEmptyPath(path24)) {
|
|
4265
4302
|
this.contents = value;
|
|
4266
4303
|
} else if (this.contents == null) {
|
|
4267
|
-
this.contents = Collection.collectionFromPath(this.schema, Array.from(
|
|
4304
|
+
this.contents = Collection.collectionFromPath(this.schema, Array.from(path24), value);
|
|
4268
4305
|
} else if (assertCollection(this.contents)) {
|
|
4269
|
-
this.contents.setIn(
|
|
4306
|
+
this.contents.setIn(path24, value);
|
|
4270
4307
|
}
|
|
4271
4308
|
}
|
|
4272
4309
|
/**
|
|
@@ -6218,9 +6255,9 @@ var require_cst_visit = __commonJS({
|
|
|
6218
6255
|
visit.BREAK = BREAK;
|
|
6219
6256
|
visit.SKIP = SKIP;
|
|
6220
6257
|
visit.REMOVE = REMOVE;
|
|
6221
|
-
visit.itemAtPath = (cst,
|
|
6258
|
+
visit.itemAtPath = (cst, path24) => {
|
|
6222
6259
|
let item = cst;
|
|
6223
|
-
for (const [field, index] of
|
|
6260
|
+
for (const [field, index] of path24) {
|
|
6224
6261
|
const tok = item?.[field];
|
|
6225
6262
|
if (tok && "items" in tok) {
|
|
6226
6263
|
item = tok.items[index];
|
|
@@ -6229,23 +6266,23 @@ var require_cst_visit = __commonJS({
|
|
|
6229
6266
|
}
|
|
6230
6267
|
return item;
|
|
6231
6268
|
};
|
|
6232
|
-
visit.parentCollection = (cst,
|
|
6233
|
-
const parent = visit.itemAtPath(cst,
|
|
6234
|
-
const field =
|
|
6269
|
+
visit.parentCollection = (cst, path24) => {
|
|
6270
|
+
const parent = visit.itemAtPath(cst, path24.slice(0, -1));
|
|
6271
|
+
const field = path24[path24.length - 1][0];
|
|
6235
6272
|
const coll = parent?.[field];
|
|
6236
6273
|
if (coll && "items" in coll)
|
|
6237
6274
|
return coll;
|
|
6238
6275
|
throw new Error("Parent collection not found");
|
|
6239
6276
|
};
|
|
6240
|
-
function _visit(
|
|
6241
|
-
let ctrl = visitor(item,
|
|
6277
|
+
function _visit(path24, item, visitor) {
|
|
6278
|
+
let ctrl = visitor(item, path24);
|
|
6242
6279
|
if (typeof ctrl === "symbol")
|
|
6243
6280
|
return ctrl;
|
|
6244
6281
|
for (const field of ["key", "value"]) {
|
|
6245
6282
|
const token = item[field];
|
|
6246
6283
|
if (token && "items" in token) {
|
|
6247
6284
|
for (let i = 0; i < token.items.length; ++i) {
|
|
6248
|
-
const ci = _visit(Object.freeze(
|
|
6285
|
+
const ci = _visit(Object.freeze(path24.concat([[field, i]])), token.items[i], visitor);
|
|
6249
6286
|
if (typeof ci === "number")
|
|
6250
6287
|
i = ci - 1;
|
|
6251
6288
|
else if (ci === BREAK)
|
|
@@ -6256,10 +6293,10 @@ var require_cst_visit = __commonJS({
|
|
|
6256
6293
|
}
|
|
6257
6294
|
}
|
|
6258
6295
|
if (typeof ctrl === "function" && field === "key")
|
|
6259
|
-
ctrl = ctrl(item,
|
|
6296
|
+
ctrl = ctrl(item, path24);
|
|
6260
6297
|
}
|
|
6261
6298
|
}
|
|
6262
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
6299
|
+
return typeof ctrl === "function" ? ctrl(item, path24) : ctrl;
|
|
6263
6300
|
}
|
|
6264
6301
|
exports2.visit = visit;
|
|
6265
6302
|
}
|
|
@@ -7544,14 +7581,14 @@ var require_parser = __commonJS({
|
|
|
7544
7581
|
case "scalar":
|
|
7545
7582
|
case "single-quoted-scalar":
|
|
7546
7583
|
case "double-quoted-scalar": {
|
|
7547
|
-
const
|
|
7584
|
+
const fs26 = this.flowScalar(this.type);
|
|
7548
7585
|
if (atNextItem || it.value) {
|
|
7549
|
-
map.items.push({ start, key:
|
|
7586
|
+
map.items.push({ start, key: fs26, sep: [] });
|
|
7550
7587
|
this.onKeyLine = true;
|
|
7551
7588
|
} else if (it.sep) {
|
|
7552
|
-
this.stack.push(
|
|
7589
|
+
this.stack.push(fs26);
|
|
7553
7590
|
} else {
|
|
7554
|
-
Object.assign(it, { key:
|
|
7591
|
+
Object.assign(it, { key: fs26, sep: [] });
|
|
7555
7592
|
this.onKeyLine = true;
|
|
7556
7593
|
}
|
|
7557
7594
|
return;
|
|
@@ -7679,13 +7716,13 @@ var require_parser = __commonJS({
|
|
|
7679
7716
|
case "scalar":
|
|
7680
7717
|
case "single-quoted-scalar":
|
|
7681
7718
|
case "double-quoted-scalar": {
|
|
7682
|
-
const
|
|
7719
|
+
const fs26 = this.flowScalar(this.type);
|
|
7683
7720
|
if (!it || it.value)
|
|
7684
|
-
fc.items.push({ start: [], key:
|
|
7721
|
+
fc.items.push({ start: [], key: fs26, sep: [] });
|
|
7685
7722
|
else if (it.sep)
|
|
7686
|
-
this.stack.push(
|
|
7723
|
+
this.stack.push(fs26);
|
|
7687
7724
|
else
|
|
7688
|
-
Object.assign(it, { key:
|
|
7725
|
+
Object.assign(it, { key: fs26, sep: [] });
|
|
7689
7726
|
return;
|
|
7690
7727
|
}
|
|
7691
7728
|
case "flow-map-end":
|
|
@@ -11186,8 +11223,8 @@ var require_utils = __commonJS({
|
|
|
11186
11223
|
}
|
|
11187
11224
|
return ind;
|
|
11188
11225
|
}
|
|
11189
|
-
function removeDotSegments(
|
|
11190
|
-
let input =
|
|
11226
|
+
function removeDotSegments(path24) {
|
|
11227
|
+
let input = path24;
|
|
11191
11228
|
const output = [];
|
|
11192
11229
|
let nextSlash = -1;
|
|
11193
11230
|
let len = 0;
|
|
@@ -11386,8 +11423,8 @@ var require_schemes = __commonJS({
|
|
|
11386
11423
|
wsComponent.secure = void 0;
|
|
11387
11424
|
}
|
|
11388
11425
|
if (wsComponent.resourceName) {
|
|
11389
|
-
const [
|
|
11390
|
-
wsComponent.path =
|
|
11426
|
+
const [path24, query] = wsComponent.resourceName.split("?");
|
|
11427
|
+
wsComponent.path = path24 && path24 !== "/" ? path24 : void 0;
|
|
11391
11428
|
wsComponent.query = query;
|
|
11392
11429
|
wsComponent.resourceName = void 0;
|
|
11393
11430
|
}
|
|
@@ -14740,12 +14777,12 @@ var require_dist2 = __commonJS({
|
|
|
14740
14777
|
throw new Error(`Unknown format "${name}"`);
|
|
14741
14778
|
return f;
|
|
14742
14779
|
};
|
|
14743
|
-
function addFormats2(ajv2, list,
|
|
14780
|
+
function addFormats2(ajv2, list, fs26, exportName) {
|
|
14744
14781
|
var _a;
|
|
14745
14782
|
var _b;
|
|
14746
14783
|
(_a = (_b = ajv2.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
14747
14784
|
for (const f of list)
|
|
14748
|
-
ajv2.addFormat(f,
|
|
14785
|
+
ajv2.addFormat(f, fs26[f]);
|
|
14749
14786
|
}
|
|
14750
14787
|
module2.exports = exports2 = formatsPlugin;
|
|
14751
14788
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -14819,6 +14856,9 @@ rudi - RUDI CLI
|
|
|
14819
14856
|
USAGE
|
|
14820
14857
|
rudi <command> [options]
|
|
14821
14858
|
|
|
14859
|
+
SETUP
|
|
14860
|
+
init Bootstrap RUDI (download runtimes, create shims)
|
|
14861
|
+
|
|
14822
14862
|
INTROSPECTION
|
|
14823
14863
|
home Show ~/.rudi structure and installed packages
|
|
14824
14864
|
stacks List installed stacks
|
|
@@ -14941,12 +14981,15 @@ ARGUMENTS
|
|
|
14941
14981
|
|
|
14942
14982
|
OPTIONS
|
|
14943
14983
|
--json Output as JSON
|
|
14984
|
+
--detected Show MCP servers from agent configs (stacks only)
|
|
14985
|
+
--category=X Filter prompts by category
|
|
14944
14986
|
|
|
14945
14987
|
EXAMPLES
|
|
14946
14988
|
rudi list
|
|
14947
14989
|
rudi list stacks
|
|
14990
|
+
rudi list stacks --detected Show MCP servers in Claude/Gemini/Codex
|
|
14948
14991
|
rudi list binaries
|
|
14949
|
-
rudi list
|
|
14992
|
+
rudi list prompts --category=coding
|
|
14950
14993
|
`,
|
|
14951
14994
|
secrets: `
|
|
14952
14995
|
rudi secrets - Manage secrets
|
|
@@ -15016,6 +15059,33 @@ EXAMPLES
|
|
|
15016
15059
|
rudi import sessions claude Import only Claude sessions
|
|
15017
15060
|
rudi import sessions --dry-run Preview without importing
|
|
15018
15061
|
rudi import status Check what's available to import
|
|
15062
|
+
`,
|
|
15063
|
+
init: `
|
|
15064
|
+
rudi init - Bootstrap RUDI environment
|
|
15065
|
+
|
|
15066
|
+
USAGE
|
|
15067
|
+
rudi init [options]
|
|
15068
|
+
|
|
15069
|
+
OPTIONS
|
|
15070
|
+
--force Reinitialize even if already set up
|
|
15071
|
+
--skip-downloads Skip downloading runtimes/binaries
|
|
15072
|
+
--quiet Minimal output (for programmatic use)
|
|
15073
|
+
|
|
15074
|
+
WHAT IT DOES
|
|
15075
|
+
1. Creates ~/.rudi directory structure (if missing)
|
|
15076
|
+
2. Downloads bundled runtimes (Node.js, Python) if not installed
|
|
15077
|
+
3. Downloads essential binaries (sqlite3, ripgrep) if not installed
|
|
15078
|
+
4. Creates/updates shims in ~/.rudi/shims/
|
|
15079
|
+
5. Initializes the database (if missing)
|
|
15080
|
+
6. Creates settings.json (if missing)
|
|
15081
|
+
|
|
15082
|
+
NOTE: Safe to run multiple times - only creates what's missing.
|
|
15083
|
+
|
|
15084
|
+
EXAMPLES
|
|
15085
|
+
rudi init
|
|
15086
|
+
rudi init --force
|
|
15087
|
+
rudi init --skip-downloads
|
|
15088
|
+
rudi init --quiet
|
|
15019
15089
|
`,
|
|
15020
15090
|
home: `
|
|
15021
15091
|
rudi home - Show ~/.rudi structure and status
|
|
@@ -15317,7 +15387,7 @@ async function installSinglePackage(pkg, options = {}) {
|
|
|
15317
15387
|
onProgress?.({ phase: "downloading", package: pkg.id });
|
|
15318
15388
|
if (pkg.npmPackage) {
|
|
15319
15389
|
try {
|
|
15320
|
-
const { execSync:
|
|
15390
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
15321
15391
|
if (!import_fs4.default.existsSync(installPath)) {
|
|
15322
15392
|
import_fs4.default.mkdirSync(installPath, { recursive: true });
|
|
15323
15393
|
}
|
|
@@ -15325,16 +15395,16 @@ async function installSinglePackage(pkg, options = {}) {
|
|
|
15325
15395
|
const resourcesPath = process.env.RESOURCES_PATH;
|
|
15326
15396
|
const npmCmd = resourcesPath ? import_path4.default.join(resourcesPath, "bundled-runtimes", "node", "bin", "npm") : "npm";
|
|
15327
15397
|
if (!import_fs4.default.existsSync(import_path4.default.join(installPath, "package.json"))) {
|
|
15328
|
-
|
|
15398
|
+
execSync7(`"${npmCmd}" init -y`, { cwd: installPath, stdio: "pipe" });
|
|
15329
15399
|
}
|
|
15330
|
-
|
|
15400
|
+
execSync7(`"${npmCmd}" install ${pkg.npmPackage}`, { cwd: installPath, stdio: "pipe" });
|
|
15331
15401
|
if (pkg.postInstall) {
|
|
15332
15402
|
onProgress?.({ phase: "postInstall", package: pkg.id, message: pkg.postInstall });
|
|
15333
15403
|
const postInstallCmd = pkg.postInstall.replace(
|
|
15334
15404
|
/^npx\s+(\S+)/,
|
|
15335
15405
|
`"${import_path4.default.join(installPath, "node_modules", ".bin", "$1")}"`
|
|
15336
15406
|
);
|
|
15337
|
-
|
|
15407
|
+
execSync7(postInstallCmd, { cwd: installPath, stdio: "pipe" });
|
|
15338
15408
|
}
|
|
15339
15409
|
import_fs4.default.writeFileSync(
|
|
15340
15410
|
import_path4.default.join(installPath, "manifest.json"),
|
|
@@ -15356,15 +15426,15 @@ async function installSinglePackage(pkg, options = {}) {
|
|
|
15356
15426
|
}
|
|
15357
15427
|
if (pkg.pipPackage) {
|
|
15358
15428
|
try {
|
|
15359
|
-
const { execSync:
|
|
15429
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
15360
15430
|
if (!import_fs4.default.existsSync(installPath)) {
|
|
15361
15431
|
import_fs4.default.mkdirSync(installPath, { recursive: true });
|
|
15362
15432
|
}
|
|
15363
15433
|
onProgress?.({ phase: "installing", package: pkg.id, message: `pip install ${pkg.pipPackage}` });
|
|
15364
15434
|
const pythonPath = import_path4.default.join(PATHS.runtimes, "python", "bin", "python3");
|
|
15365
15435
|
const pythonCmd = import_fs4.default.existsSync(pythonPath) ? pythonPath : "python3";
|
|
15366
|
-
|
|
15367
|
-
|
|
15436
|
+
execSync7(`"${pythonCmd}" -m venv "${installPath}/venv"`, { stdio: "pipe" });
|
|
15437
|
+
execSync7(`"${installPath}/venv/bin/pip" install ${pkg.pipPackage}`, { stdio: "pipe" });
|
|
15368
15438
|
import_fs4.default.writeFileSync(
|
|
15369
15439
|
import_path4.default.join(installPath, "manifest.json"),
|
|
15370
15440
|
JSON.stringify({
|
|
@@ -15586,7 +15656,7 @@ async function listInstalled(kind) {
|
|
|
15586
15656
|
return packages;
|
|
15587
15657
|
}
|
|
15588
15658
|
async function installStackDependencies(stackPath, onProgress) {
|
|
15589
|
-
const { execSync:
|
|
15659
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
15590
15660
|
const nodePath = import_path4.default.join(stackPath, "node");
|
|
15591
15661
|
if (import_fs4.default.existsSync(nodePath)) {
|
|
15592
15662
|
const packageJsonPath = import_path4.default.join(nodePath, "package.json");
|
|
@@ -15594,7 +15664,7 @@ async function installStackDependencies(stackPath, onProgress) {
|
|
|
15594
15664
|
onProgress?.({ phase: "installing-deps", message: "Installing Node.js dependencies..." });
|
|
15595
15665
|
try {
|
|
15596
15666
|
const npmCmd = await findNpmExecutable();
|
|
15597
|
-
|
|
15667
|
+
execSync7(`"${npmCmd}" install`, { cwd: nodePath, stdio: "pipe" });
|
|
15598
15668
|
} catch (error) {
|
|
15599
15669
|
console.warn(`Warning: Failed to install Node.js dependencies: ${error.message}`);
|
|
15600
15670
|
}
|
|
@@ -15607,8 +15677,8 @@ async function installStackDependencies(stackPath, onProgress) {
|
|
|
15607
15677
|
onProgress?.({ phase: "installing-deps", message: "Installing Python dependencies..." });
|
|
15608
15678
|
try {
|
|
15609
15679
|
const pythonCmd = await findPythonExecutable();
|
|
15610
|
-
|
|
15611
|
-
|
|
15680
|
+
execSync7(`"${pythonCmd}" -m venv venv`, { cwd: pythonPath, stdio: "pipe" });
|
|
15681
|
+
execSync7("./venv/bin/pip install -r requirements.txt", { cwd: pythonPath, stdio: "pipe" });
|
|
15612
15682
|
} catch (error) {
|
|
15613
15683
|
console.warn(`Warning: Failed to install Python dependencies: ${error.message}`);
|
|
15614
15684
|
}
|
|
@@ -15960,407 +16030,319 @@ Total: ${totalCount} package(s) available`);
|
|
|
15960
16030
|
}
|
|
15961
16031
|
|
|
15962
16032
|
// src/commands/install.js
|
|
15963
|
-
var
|
|
15964
|
-
var
|
|
16033
|
+
var fs8 = __toESM(require("fs/promises"), 1);
|
|
16034
|
+
var path8 = __toESM(require("path"), 1);
|
|
16035
|
+
var import_child_process2 = require("child_process");
|
|
15965
16036
|
|
|
15966
|
-
// src/
|
|
15967
|
-
var fs6 = __toESM(require("fs
|
|
16037
|
+
// src/commands/secrets-store.js
|
|
16038
|
+
var fs6 = __toESM(require("fs"), 1);
|
|
15968
16039
|
var path6 = __toESM(require("path"), 1);
|
|
15969
|
-
|
|
15970
|
-
var
|
|
15971
|
-
|
|
15972
|
-
|
|
15973
|
-
|
|
15974
|
-
|
|
15975
|
-
}
|
|
15976
|
-
|
|
16040
|
+
init_src();
|
|
16041
|
+
var SECRETS_FILE = path6.join(PATHS.home, "secrets.json");
|
|
16042
|
+
function ensureSecretsFile() {
|
|
16043
|
+
const dir = path6.dirname(SECRETS_FILE);
|
|
16044
|
+
if (!fs6.existsSync(dir)) {
|
|
16045
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
16046
|
+
}
|
|
16047
|
+
if (!fs6.existsSync(SECRETS_FILE)) {
|
|
16048
|
+
fs6.writeFileSync(SECRETS_FILE, "{}", { mode: 384 });
|
|
16049
|
+
} else {
|
|
16050
|
+
try {
|
|
16051
|
+
fs6.chmodSync(SECRETS_FILE, 384);
|
|
16052
|
+
} catch {
|
|
16053
|
+
}
|
|
16054
|
+
}
|
|
16055
|
+
}
|
|
16056
|
+
function loadSecrets() {
|
|
16057
|
+
ensureSecretsFile();
|
|
15977
16058
|
try {
|
|
15978
|
-
const content =
|
|
16059
|
+
const content = fs6.readFileSync(SECRETS_FILE, "utf-8");
|
|
15979
16060
|
return JSON.parse(content);
|
|
15980
16061
|
} catch {
|
|
15981
16062
|
return {};
|
|
15982
16063
|
}
|
|
15983
16064
|
}
|
|
15984
|
-
|
|
15985
|
-
|
|
15986
|
-
|
|
15987
|
-
|
|
16065
|
+
function saveSecrets(secrets) {
|
|
16066
|
+
ensureSecretsFile();
|
|
16067
|
+
fs6.writeFileSync(SECRETS_FILE, JSON.stringify(secrets, null, 2), {
|
|
16068
|
+
encoding: "utf-8",
|
|
16069
|
+
mode: 384
|
|
16070
|
+
});
|
|
15988
16071
|
}
|
|
15989
|
-
function
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
}
|
|
15993
|
-
if (value.startsWith("[") && value.endsWith("]")) {
|
|
15994
|
-
const inner = value.slice(1, -1).trim();
|
|
15995
|
-
if (!inner) return [];
|
|
15996
|
-
const items = [];
|
|
15997
|
-
let current = "";
|
|
15998
|
-
let inQuote = false;
|
|
15999
|
-
let quoteChar = "";
|
|
16000
|
-
for (const char of inner) {
|
|
16001
|
-
if ((char === '"' || char === "'") && !inQuote) {
|
|
16002
|
-
inQuote = true;
|
|
16003
|
-
quoteChar = char;
|
|
16004
|
-
} else if (char === quoteChar && inQuote) {
|
|
16005
|
-
inQuote = false;
|
|
16006
|
-
items.push(current);
|
|
16007
|
-
current = "";
|
|
16008
|
-
} else if (char === "," && !inQuote) {
|
|
16009
|
-
} else if (inQuote) {
|
|
16010
|
-
current += char;
|
|
16011
|
-
}
|
|
16012
|
-
}
|
|
16013
|
-
return items;
|
|
16014
|
-
}
|
|
16015
|
-
if (value === "true") return true;
|
|
16016
|
-
if (value === "false") return false;
|
|
16017
|
-
const num = Number(value);
|
|
16018
|
-
if (!isNaN(num)) return num;
|
|
16019
|
-
return value;
|
|
16072
|
+
async function getSecret(name) {
|
|
16073
|
+
const secrets = loadSecrets();
|
|
16074
|
+
return secrets[name] || null;
|
|
16020
16075
|
}
|
|
16021
|
-
function
|
|
16022
|
-
const
|
|
16023
|
-
|
|
16024
|
-
|
|
16025
|
-
|
|
16026
|
-
|
|
16027
|
-
|
|
16028
|
-
|
|
16029
|
-
|
|
16030
|
-
|
|
16031
|
-
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
|
|
16035
|
-
|
|
16036
|
-
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
|
|
16040
|
-
|
|
16041
|
-
|
|
16042
|
-
|
|
16043
|
-
|
|
16044
|
-
|
|
16045
|
-
|
|
16046
|
-
|
|
16047
|
-
|
|
16076
|
+
async function setSecret(name, value) {
|
|
16077
|
+
const secrets = loadSecrets();
|
|
16078
|
+
secrets[name] = value;
|
|
16079
|
+
saveSecrets(secrets);
|
|
16080
|
+
return true;
|
|
16081
|
+
}
|
|
16082
|
+
async function removeSecret(name) {
|
|
16083
|
+
const secrets = loadSecrets();
|
|
16084
|
+
delete secrets[name];
|
|
16085
|
+
saveSecrets(secrets);
|
|
16086
|
+
return true;
|
|
16087
|
+
}
|
|
16088
|
+
async function listSecrets() {
|
|
16089
|
+
const secrets = loadSecrets();
|
|
16090
|
+
return Object.keys(secrets).sort();
|
|
16091
|
+
}
|
|
16092
|
+
async function hasSecret(name) {
|
|
16093
|
+
const secrets = loadSecrets();
|
|
16094
|
+
return secrets[name] !== void 0 && secrets[name] !== null && secrets[name] !== "";
|
|
16095
|
+
}
|
|
16096
|
+
async function getMaskedSecrets() {
|
|
16097
|
+
const secrets = loadSecrets();
|
|
16098
|
+
const masked = {};
|
|
16099
|
+
for (const [name, value] of Object.entries(secrets)) {
|
|
16100
|
+
if (value && typeof value === "string" && value.length > 8) {
|
|
16101
|
+
masked[name] = value.slice(0, 4) + "..." + value.slice(-4);
|
|
16102
|
+
} else if (value && typeof value === "string" && value.length > 0) {
|
|
16103
|
+
masked[name] = "****";
|
|
16104
|
+
} else {
|
|
16105
|
+
masked[name] = "(pending)";
|
|
16048
16106
|
}
|
|
16049
16107
|
}
|
|
16050
|
-
return
|
|
16108
|
+
return masked;
|
|
16051
16109
|
}
|
|
16052
|
-
function
|
|
16053
|
-
|
|
16054
|
-
|
|
16055
|
-
|
|
16056
|
-
|
|
16057
|
-
|
|
16058
|
-
}
|
|
16059
|
-
if (typeof value === "number") {
|
|
16060
|
-
return String(value);
|
|
16061
|
-
}
|
|
16062
|
-
if (Array.isArray(value)) {
|
|
16063
|
-
const items = value.map((v) => tomlValue(v));
|
|
16064
|
-
return `[${items.join(", ")}]`;
|
|
16065
|
-
}
|
|
16066
|
-
return String(value);
|
|
16110
|
+
function getStorageInfo() {
|
|
16111
|
+
return {
|
|
16112
|
+
backend: "file",
|
|
16113
|
+
file: SECRETS_FILE,
|
|
16114
|
+
permissions: "0600 (owner read/write only)"
|
|
16115
|
+
};
|
|
16067
16116
|
}
|
|
16068
|
-
|
|
16069
|
-
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
|
|
16117
|
+
|
|
16118
|
+
// src/utils/agents.js
|
|
16119
|
+
var import_fs6 = __toESM(require("fs"), 1);
|
|
16120
|
+
var import_path6 = __toESM(require("path"), 1);
|
|
16121
|
+
var import_os2 = __toESM(require("os"), 1);
|
|
16122
|
+
var AGENT_CONFIGS2 = [
|
|
16123
|
+
// Claude Desktop (Anthropic)
|
|
16124
|
+
{
|
|
16125
|
+
id: "claude-desktop",
|
|
16126
|
+
name: "Claude Desktop",
|
|
16127
|
+
key: "mcpServers",
|
|
16128
|
+
paths: {
|
|
16129
|
+
darwin: ["Library/Application Support/Claude/claude_desktop_config.json"],
|
|
16130
|
+
win32: ["AppData/Roaming/Claude/claude_desktop_config.json"],
|
|
16131
|
+
linux: [".config/claude/claude_desktop_config.json"]
|
|
16073
16132
|
}
|
|
16074
|
-
}
|
|
16075
|
-
|
|
16076
|
-
|
|
16077
|
-
|
|
16078
|
-
|
|
16079
|
-
|
|
16080
|
-
|
|
16081
|
-
|
|
16082
|
-
|
|
16083
|
-
|
|
16084
|
-
|
|
16085
|
-
|
|
16086
|
-
|
|
16087
|
-
|
|
16088
|
-
|
|
16133
|
+
},
|
|
16134
|
+
// Claude Code CLI (Anthropic)
|
|
16135
|
+
{
|
|
16136
|
+
id: "claude-code",
|
|
16137
|
+
name: "Claude Code",
|
|
16138
|
+
key: "mcpServers",
|
|
16139
|
+
paths: {
|
|
16140
|
+
darwin: [".claude.json"],
|
|
16141
|
+
// User scope (all projects)
|
|
16142
|
+
win32: [".claude.json"],
|
|
16143
|
+
linux: [".claude.json"]
|
|
16144
|
+
}
|
|
16145
|
+
},
|
|
16146
|
+
// Cursor (Anysphere)
|
|
16147
|
+
{
|
|
16148
|
+
id: "cursor",
|
|
16149
|
+
name: "Cursor",
|
|
16150
|
+
key: "mcpServers",
|
|
16151
|
+
paths: {
|
|
16152
|
+
darwin: [".cursor/mcp.json"],
|
|
16153
|
+
win32: [".cursor/mcp.json"],
|
|
16154
|
+
linux: [".cursor/mcp.json"]
|
|
16155
|
+
}
|
|
16156
|
+
},
|
|
16157
|
+
// Windsurf (Codeium)
|
|
16158
|
+
{
|
|
16159
|
+
id: "windsurf",
|
|
16160
|
+
name: "Windsurf",
|
|
16161
|
+
key: "mcpServers",
|
|
16162
|
+
paths: {
|
|
16163
|
+
darwin: [".codeium/windsurf/mcp_config.json"],
|
|
16164
|
+
win32: [".codeium/windsurf/mcp_config.json"],
|
|
16165
|
+
linux: [".codeium/windsurf/mcp_config.json"]
|
|
16166
|
+
}
|
|
16167
|
+
},
|
|
16168
|
+
// Cline (VS Code extension)
|
|
16169
|
+
{
|
|
16170
|
+
id: "cline",
|
|
16171
|
+
name: "Cline",
|
|
16172
|
+
key: "mcpServers",
|
|
16173
|
+
paths: {
|
|
16174
|
+
darwin: ["Documents/Cline/cline_mcp_settings.json"],
|
|
16175
|
+
win32: ["Documents/Cline/cline_mcp_settings.json"],
|
|
16176
|
+
linux: ["Documents/Cline/cline_mcp_settings.json"]
|
|
16177
|
+
}
|
|
16178
|
+
},
|
|
16179
|
+
// Zed Editor
|
|
16180
|
+
{
|
|
16181
|
+
id: "zed",
|
|
16182
|
+
name: "Zed",
|
|
16183
|
+
key: "context_servers",
|
|
16184
|
+
// Zed uses different key
|
|
16185
|
+
paths: {
|
|
16186
|
+
darwin: [".zed/settings.json"],
|
|
16187
|
+
win32: [".config/zed/settings.json"],
|
|
16188
|
+
linux: [".config/zed/settings.json"]
|
|
16189
|
+
}
|
|
16190
|
+
},
|
|
16191
|
+
// VS Code / GitHub Copilot
|
|
16192
|
+
{
|
|
16193
|
+
id: "vscode",
|
|
16194
|
+
name: "VS Code",
|
|
16195
|
+
key: "servers",
|
|
16196
|
+
paths: {
|
|
16197
|
+
darwin: ["Library/Application Support/Code/User/mcp.json"],
|
|
16198
|
+
win32: ["AppData/Roaming/Code/User/mcp.json"],
|
|
16199
|
+
linux: [".config/Code/User/mcp.json"]
|
|
16200
|
+
}
|
|
16201
|
+
},
|
|
16202
|
+
// Gemini CLI (Google)
|
|
16203
|
+
{
|
|
16204
|
+
id: "gemini",
|
|
16205
|
+
name: "Gemini",
|
|
16206
|
+
key: "mcpServers",
|
|
16207
|
+
paths: {
|
|
16208
|
+
darwin: [".gemini/settings.json"],
|
|
16209
|
+
win32: [".gemini/settings.json"],
|
|
16210
|
+
linux: [".gemini/settings.json"]
|
|
16211
|
+
}
|
|
16212
|
+
},
|
|
16213
|
+
// Codex CLI (OpenAI)
|
|
16214
|
+
{
|
|
16215
|
+
id: "codex",
|
|
16216
|
+
name: "Codex",
|
|
16217
|
+
key: "mcpServers",
|
|
16218
|
+
paths: {
|
|
16219
|
+
darwin: [".codex/config.json", ".codex/settings.json"],
|
|
16220
|
+
win32: [".codex/config.json", ".codex/settings.json"],
|
|
16221
|
+
linux: [".codex/config.json", ".codex/settings.json"]
|
|
16222
|
+
}
|
|
16223
|
+
}
|
|
16224
|
+
];
|
|
16225
|
+
function getAgentConfigPaths(agentConfig) {
|
|
16226
|
+
const home = import_os2.default.homedir();
|
|
16227
|
+
const platform = process.platform;
|
|
16228
|
+
const relativePaths = agentConfig.paths[platform] || agentConfig.paths.linux || [];
|
|
16229
|
+
return relativePaths.map((p) => import_path6.default.join(home, p));
|
|
16230
|
+
}
|
|
16231
|
+
function findAgentConfig(agentConfig) {
|
|
16232
|
+
const paths = getAgentConfigPaths(agentConfig);
|
|
16233
|
+
for (const configPath of paths) {
|
|
16234
|
+
if (import_fs6.default.existsSync(configPath)) {
|
|
16235
|
+
return configPath;
|
|
16089
16236
|
}
|
|
16090
16237
|
}
|
|
16091
|
-
return
|
|
16238
|
+
return null;
|
|
16092
16239
|
}
|
|
16093
|
-
|
|
16240
|
+
function readAgentMcpServers(agentConfig) {
|
|
16241
|
+
const configPath = findAgentConfig(agentConfig);
|
|
16242
|
+
if (!configPath) return [];
|
|
16094
16243
|
try {
|
|
16095
|
-
const content =
|
|
16096
|
-
|
|
16097
|
-
|
|
16098
|
-
|
|
16244
|
+
const content = JSON.parse(import_fs6.default.readFileSync(configPath, "utf-8"));
|
|
16245
|
+
const mcpServers = content[agentConfig.key] || {};
|
|
16246
|
+
return Object.entries(mcpServers).map(([name, config]) => {
|
|
16247
|
+
const command = config.command || config.path || config.command?.path;
|
|
16248
|
+
return {
|
|
16249
|
+
name,
|
|
16250
|
+
agent: agentConfig.id,
|
|
16251
|
+
agentName: agentConfig.name,
|
|
16252
|
+
command: command || "unknown",
|
|
16253
|
+
args: config.args,
|
|
16254
|
+
cwd: config.cwd,
|
|
16255
|
+
env: config.env ? Object.keys(config.env) : [],
|
|
16256
|
+
// Just list env var names, not values
|
|
16257
|
+
configFile: configPath
|
|
16258
|
+
};
|
|
16259
|
+
});
|
|
16260
|
+
} catch (e) {
|
|
16261
|
+
return [];
|
|
16099
16262
|
}
|
|
16100
16263
|
}
|
|
16101
|
-
|
|
16102
|
-
const
|
|
16103
|
-
|
|
16104
|
-
|
|
16105
|
-
|
|
16106
|
-
function parseEnvFile(content) {
|
|
16107
|
-
const env = {};
|
|
16108
|
-
const lines = content.split("\n");
|
|
16109
|
-
for (const line of lines) {
|
|
16110
|
-
const trimmed = line.trim();
|
|
16111
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
16112
|
-
const eqIndex = trimmed.indexOf("=");
|
|
16113
|
-
if (eqIndex === -1) continue;
|
|
16114
|
-
const key = trimmed.slice(0, eqIndex).trim();
|
|
16115
|
-
let value = trimmed.slice(eqIndex + 1).trim();
|
|
16116
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
16117
|
-
value = value.slice(1, -1);
|
|
16118
|
-
}
|
|
16119
|
-
if (value) {
|
|
16120
|
-
env[key] = value;
|
|
16121
|
-
}
|
|
16264
|
+
function detectAllMcpServers() {
|
|
16265
|
+
const servers = [];
|
|
16266
|
+
for (const agentConfig of AGENT_CONFIGS2) {
|
|
16267
|
+
const agentServers = readAgentMcpServers(agentConfig);
|
|
16268
|
+
servers.push(...agentServers);
|
|
16122
16269
|
}
|
|
16123
|
-
return
|
|
16270
|
+
return servers;
|
|
16124
16271
|
}
|
|
16125
|
-
|
|
16126
|
-
|
|
16127
|
-
|
|
16128
|
-
|
|
16129
|
-
|
|
16130
|
-
}
|
|
16131
|
-
return {};
|
|
16132
|
-
}
|
|
16272
|
+
function getInstalledAgents() {
|
|
16273
|
+
return AGENT_CONFIGS2.filter((agent) => findAgentConfig(agent) !== null).map((agent) => ({
|
|
16274
|
+
id: agent.id,
|
|
16275
|
+
name: agent.name,
|
|
16276
|
+
configFile: findAgentConfig(agent)
|
|
16277
|
+
}));
|
|
16133
16278
|
}
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
|
|
16137
|
-
|
|
16138
|
-
|
|
16139
|
-
|
|
16140
|
-
|
|
16141
|
-
|
|
16142
|
-
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
args = cmdArray.slice(1).map((arg) => resolveRelativePath2(arg));
|
|
16147
|
-
} else if (manifest.mcp) {
|
|
16148
|
-
command = resolveRelativePath2(manifest.mcp.command);
|
|
16149
|
-
args = (manifest.mcp.args || []).map((arg) => resolveRelativePath2(arg));
|
|
16150
|
-
if (manifest.mcp.entry) {
|
|
16151
|
-
args = args.map(
|
|
16152
|
-
(arg) => arg === manifest.mcp.entry ? path6.join(installPath, manifest.mcp.entry) : arg
|
|
16153
|
-
);
|
|
16279
|
+
function getMcpServerSummary() {
|
|
16280
|
+
const summary = {};
|
|
16281
|
+
for (const agentConfig of AGENT_CONFIGS2) {
|
|
16282
|
+
const configPath = findAgentConfig(agentConfig);
|
|
16283
|
+
if (configPath) {
|
|
16284
|
+
const servers = readAgentMcpServers(agentConfig);
|
|
16285
|
+
summary[agentConfig.id] = {
|
|
16286
|
+
name: agentConfig.name,
|
|
16287
|
+
configFile: configPath,
|
|
16288
|
+
serverCount: servers.length,
|
|
16289
|
+
servers: servers.map((s) => s.name)
|
|
16290
|
+
};
|
|
16154
16291
|
}
|
|
16155
|
-
} else {
|
|
16156
|
-
return null;
|
|
16157
16292
|
}
|
|
16158
|
-
|
|
16159
|
-
if (optimized) {
|
|
16160
|
-
command = optimized.command;
|
|
16161
|
-
args = optimized.args;
|
|
16162
|
-
}
|
|
16163
|
-
const env = await readStackEnv(installPath);
|
|
16164
|
-
const config = {
|
|
16165
|
-
command,
|
|
16166
|
-
cwd
|
|
16167
|
-
};
|
|
16168
|
-
if (args.length > 0) {
|
|
16169
|
-
config.args = args;
|
|
16170
|
-
}
|
|
16171
|
-
if (Object.keys(env).length > 0) {
|
|
16172
|
-
config.env = env;
|
|
16173
|
-
}
|
|
16174
|
-
return config;
|
|
16293
|
+
return summary;
|
|
16175
16294
|
}
|
|
16176
|
-
|
|
16177
|
-
|
|
16178
|
-
|
|
16179
|
-
|
|
16180
|
-
const tsFileIndex = args.findIndex((arg) => arg.endsWith(".ts"));
|
|
16181
|
-
if (tsFileIndex === -1) {
|
|
16182
|
-
return null;
|
|
16183
|
-
}
|
|
16184
|
-
const tsFile = args[tsFileIndex];
|
|
16185
|
-
const jsFile = tsFile.replace("/src/", "/dist/").replace(".ts", ".js");
|
|
16186
|
-
const jsPath = path6.isAbsolute(jsFile) ? jsFile : path6.join(installPath, jsFile);
|
|
16295
|
+
|
|
16296
|
+
// src/commands/install.js
|
|
16297
|
+
async function loadManifest(installPath) {
|
|
16298
|
+
const manifestPath = path8.join(installPath, "manifest.json");
|
|
16187
16299
|
try {
|
|
16188
|
-
await
|
|
16189
|
-
return
|
|
16190
|
-
command: "node",
|
|
16191
|
-
args: [jsPath]
|
|
16192
|
-
};
|
|
16300
|
+
const content = await fs8.readFile(manifestPath, "utf-8");
|
|
16301
|
+
return JSON.parse(content);
|
|
16193
16302
|
} catch {
|
|
16194
16303
|
return null;
|
|
16195
16304
|
}
|
|
16196
16305
|
}
|
|
16197
|
-
async function
|
|
16198
|
-
const
|
|
16306
|
+
async function installDependencies(stackPath, manifest) {
|
|
16307
|
+
const runtime = manifest?.runtime || "node";
|
|
16199
16308
|
try {
|
|
16200
|
-
|
|
16201
|
-
|
|
16202
|
-
|
|
16203
|
-
|
|
16204
|
-
|
|
16205
|
-
|
|
16206
|
-
|
|
16207
|
-
|
|
16208
|
-
|
|
16209
|
-
|
|
16210
|
-
|
|
16211
|
-
|
|
16212
|
-
|
|
16213
|
-
|
|
16214
|
-
|
|
16215
|
-
|
|
16216
|
-
|
|
16217
|
-
|
|
16218
|
-
|
|
16219
|
-
|
|
16220
|
-
|
|
16221
|
-
|
|
16222
|
-
|
|
16223
|
-
|
|
16224
|
-
|
|
16225
|
-
|
|
16226
|
-
|
|
16227
|
-
|
|
16228
|
-
|
|
16229
|
-
|
|
16230
|
-
|
|
16231
|
-
|
|
16232
|
-
|
|
16233
|
-
}
|
|
16234
|
-
async function registerMcpCodex(stackId, installPath, manifest) {
|
|
16235
|
-
const configPath = AGENT_CONFIGS.codex;
|
|
16236
|
-
try {
|
|
16237
|
-
const mcpConfig = await buildMcpConfig(stackId, installPath, manifest);
|
|
16238
|
-
if (!mcpConfig) {
|
|
16239
|
-
return { success: true, skipped: true };
|
|
16240
|
-
}
|
|
16241
|
-
const config = await readToml(configPath);
|
|
16242
|
-
if (!config.mcp_servers) {
|
|
16243
|
-
config.mcp_servers = {};
|
|
16244
|
-
}
|
|
16245
|
-
config.mcp_servers[stackId] = mcpConfig;
|
|
16246
|
-
await writeToml(configPath, config);
|
|
16247
|
-
console.log(` Registered MCP in Codex: ${stackId}`);
|
|
16248
|
-
return { success: true };
|
|
16249
|
-
} catch (error) {
|
|
16250
|
-
console.error(` Failed to register MCP in Codex: ${error.message}`);
|
|
16251
|
-
return { success: false, error: error.message };
|
|
16252
|
-
}
|
|
16253
|
-
}
|
|
16254
|
-
async function unregisterMcpCodex(stackId) {
|
|
16255
|
-
const configPath = AGENT_CONFIGS.codex;
|
|
16256
|
-
try {
|
|
16257
|
-
const config = await readToml(configPath);
|
|
16258
|
-
if (!config.mcp_servers || !config.mcp_servers[stackId]) {
|
|
16259
|
-
return { success: true, skipped: true };
|
|
16260
|
-
}
|
|
16261
|
-
delete config.mcp_servers[stackId];
|
|
16262
|
-
await writeToml(configPath, config);
|
|
16263
|
-
console.log(` Unregistered MCP from Codex: ${stackId}`);
|
|
16264
|
-
return { success: true };
|
|
16265
|
-
} catch (error) {
|
|
16266
|
-
console.error(` Failed to unregister MCP from Codex: ${error.message}`);
|
|
16267
|
-
return { success: false, error: error.message };
|
|
16268
|
-
}
|
|
16269
|
-
}
|
|
16270
|
-
async function registerMcpGemini(stackId, installPath, manifest) {
|
|
16271
|
-
const configPath = AGENT_CONFIGS.gemini;
|
|
16272
|
-
try {
|
|
16273
|
-
const mcpConfig = await buildMcpConfig(stackId, installPath, manifest);
|
|
16274
|
-
if (!mcpConfig) {
|
|
16275
|
-
return { success: true, skipped: true };
|
|
16276
|
-
}
|
|
16277
|
-
const settings = await readJson(configPath);
|
|
16278
|
-
if (!settings.mcpServers) {
|
|
16279
|
-
settings.mcpServers = {};
|
|
16280
|
-
}
|
|
16281
|
-
settings.mcpServers[stackId] = mcpConfig;
|
|
16282
|
-
await writeJson(configPath, settings);
|
|
16283
|
-
console.log(` Registered MCP in Gemini: ${stackId}`);
|
|
16284
|
-
return { success: true };
|
|
16285
|
-
} catch (error) {
|
|
16286
|
-
console.error(` Failed to register MCP in Gemini: ${error.message}`);
|
|
16287
|
-
return { success: false, error: error.message };
|
|
16288
|
-
}
|
|
16289
|
-
}
|
|
16290
|
-
async function unregisterMcpGemini(stackId) {
|
|
16291
|
-
const configPath = AGENT_CONFIGS.gemini;
|
|
16292
|
-
try {
|
|
16293
|
-
const settings = await readJson(configPath);
|
|
16294
|
-
if (!settings.mcpServers || !settings.mcpServers[stackId]) {
|
|
16295
|
-
return { success: true, skipped: true };
|
|
16309
|
+
if (runtime === "node") {
|
|
16310
|
+
const packageJsonPath = path8.join(stackPath, "package.json");
|
|
16311
|
+
try {
|
|
16312
|
+
await fs8.access(packageJsonPath);
|
|
16313
|
+
} catch {
|
|
16314
|
+
return { installed: false, reason: "No package.json" };
|
|
16315
|
+
}
|
|
16316
|
+
const nodeModulesPath = path8.join(stackPath, "node_modules");
|
|
16317
|
+
try {
|
|
16318
|
+
await fs8.access(nodeModulesPath);
|
|
16319
|
+
return { installed: false, reason: "Dependencies already installed" };
|
|
16320
|
+
} catch {
|
|
16321
|
+
}
|
|
16322
|
+
console.log(` Installing npm dependencies...`);
|
|
16323
|
+
(0, import_child_process2.execSync)("npm install --production", {
|
|
16324
|
+
cwd: stackPath,
|
|
16325
|
+
stdio: "pipe"
|
|
16326
|
+
// Suppress output
|
|
16327
|
+
});
|
|
16328
|
+
return { installed: true };
|
|
16329
|
+
} else if (runtime === "python") {
|
|
16330
|
+
const requirementsPath = path8.join(stackPath, "requirements.txt");
|
|
16331
|
+
try {
|
|
16332
|
+
await fs8.access(requirementsPath);
|
|
16333
|
+
} catch {
|
|
16334
|
+
return { installed: false, reason: "No requirements.txt" };
|
|
16335
|
+
}
|
|
16336
|
+
console.log(` Installing pip dependencies...`);
|
|
16337
|
+
(0, import_child_process2.execSync)("pip install -r requirements.txt", {
|
|
16338
|
+
cwd: stackPath,
|
|
16339
|
+
stdio: "pipe"
|
|
16340
|
+
});
|
|
16341
|
+
return { installed: true };
|
|
16296
16342
|
}
|
|
16297
|
-
|
|
16298
|
-
await writeJson(configPath, settings);
|
|
16299
|
-
console.log(` Unregistered MCP from Gemini: ${stackId}`);
|
|
16300
|
-
return { success: true };
|
|
16343
|
+
return { installed: false, reason: `Unknown runtime: ${runtime}` };
|
|
16301
16344
|
} catch (error) {
|
|
16302
|
-
|
|
16303
|
-
return { success: false, error: error.message };
|
|
16304
|
-
}
|
|
16305
|
-
}
|
|
16306
|
-
async function getInstalledAgents() {
|
|
16307
|
-
const agentsDir = path6.join(HOME, ".rudi", "agents");
|
|
16308
|
-
const installed = [];
|
|
16309
|
-
for (const agent of ["claude", "codex", "gemini"]) {
|
|
16310
|
-
const agentPath = path6.join(agentsDir, agent);
|
|
16311
|
-
try {
|
|
16312
|
-
await fs6.access(agentPath);
|
|
16313
|
-
installed.push(agent);
|
|
16314
|
-
} catch {
|
|
16315
|
-
}
|
|
16316
|
-
}
|
|
16317
|
-
return installed;
|
|
16318
|
-
}
|
|
16319
|
-
async function registerMcpAll(stackId, installPath, manifest, targetAgents = null) {
|
|
16320
|
-
const agents = targetAgents || await getInstalledAgents();
|
|
16321
|
-
const results = {};
|
|
16322
|
-
for (const agent of agents) {
|
|
16323
|
-
switch (agent) {
|
|
16324
|
-
case "claude":
|
|
16325
|
-
results.claude = await registerMcpClaude(stackId, installPath, manifest);
|
|
16326
|
-
break;
|
|
16327
|
-
case "codex":
|
|
16328
|
-
results.codex = await registerMcpCodex(stackId, installPath, manifest);
|
|
16329
|
-
break;
|
|
16330
|
-
case "gemini":
|
|
16331
|
-
results.gemini = await registerMcpGemini(stackId, installPath, manifest);
|
|
16332
|
-
break;
|
|
16333
|
-
}
|
|
16334
|
-
}
|
|
16335
|
-
return results;
|
|
16336
|
-
}
|
|
16337
|
-
async function unregisterMcpAll(stackId, targetAgents = null) {
|
|
16338
|
-
const agents = targetAgents || await getInstalledAgents();
|
|
16339
|
-
const results = {};
|
|
16340
|
-
for (const agent of agents) {
|
|
16341
|
-
switch (agent) {
|
|
16342
|
-
case "claude":
|
|
16343
|
-
results.claude = await unregisterMcpClaude(stackId);
|
|
16344
|
-
break;
|
|
16345
|
-
case "codex":
|
|
16346
|
-
results.codex = await unregisterMcpCodex(stackId);
|
|
16347
|
-
break;
|
|
16348
|
-
case "gemini":
|
|
16349
|
-
results.gemini = await unregisterMcpGemini(stackId);
|
|
16350
|
-
break;
|
|
16351
|
-
}
|
|
16352
|
-
}
|
|
16353
|
-
return results;
|
|
16354
|
-
}
|
|
16355
|
-
|
|
16356
|
-
// src/commands/install.js
|
|
16357
|
-
async function loadManifest(installPath) {
|
|
16358
|
-
const manifestPath = path7.join(installPath, "manifest.json");
|
|
16359
|
-
try {
|
|
16360
|
-
const content = await fs7.readFile(manifestPath, "utf-8");
|
|
16361
|
-
return JSON.parse(content);
|
|
16362
|
-
} catch {
|
|
16363
|
-
return null;
|
|
16345
|
+
return { installed: false, error: error.message };
|
|
16364
16346
|
}
|
|
16365
16347
|
}
|
|
16366
16348
|
function getManifestSecrets(manifest) {
|
|
@@ -16370,67 +16352,56 @@ function getSecretName(secret) {
|
|
|
16370
16352
|
if (typeof secret === "string") return secret;
|
|
16371
16353
|
return secret.name || secret.key;
|
|
16372
16354
|
}
|
|
16373
|
-
function getSecretDescription(secret) {
|
|
16374
|
-
if (typeof secret !== "object" || !secret) return null;
|
|
16375
|
-
return secret.description || secret.label || null;
|
|
16376
|
-
}
|
|
16377
16355
|
function getSecretLink(secret) {
|
|
16378
16356
|
if (typeof secret !== "object" || !secret) return null;
|
|
16379
16357
|
return secret.link || secret.helpUrl || null;
|
|
16380
16358
|
}
|
|
16381
|
-
function
|
|
16382
|
-
if (typeof secret !== "object" || !secret) return null;
|
|
16383
|
-
return secret.label || secret.name || secret.key || null;
|
|
16384
|
-
}
|
|
16385
|
-
async function createEnvFile(installPath, manifest) {
|
|
16359
|
+
async function checkSecrets(manifest) {
|
|
16386
16360
|
const secrets = getManifestSecrets(manifest);
|
|
16387
|
-
|
|
16388
|
-
const
|
|
16389
|
-
try {
|
|
16390
|
-
await fs7.access(envPath);
|
|
16391
|
-
console.log(` .env file already exists, preserving existing secrets`);
|
|
16392
|
-
return envPath;
|
|
16393
|
-
} catch {
|
|
16394
|
-
}
|
|
16395
|
-
const lines = [];
|
|
16396
|
-
lines.push("# Environment variables for this stack");
|
|
16397
|
-
lines.push("# Fill in your API keys below");
|
|
16398
|
-
lines.push("");
|
|
16361
|
+
const found = [];
|
|
16362
|
+
const missing = [];
|
|
16399
16363
|
for (const secret of secrets) {
|
|
16400
16364
|
const key = getSecretName(secret);
|
|
16401
|
-
const
|
|
16402
|
-
const
|
|
16403
|
-
if (
|
|
16404
|
-
|
|
16365
|
+
const isRequired = typeof secret === "object" ? secret.required !== false : true;
|
|
16366
|
+
const exists = await hasSecret(key);
|
|
16367
|
+
if (exists) {
|
|
16368
|
+
found.push(key);
|
|
16369
|
+
} else if (isRequired) {
|
|
16370
|
+
missing.push(key);
|
|
16405
16371
|
}
|
|
16406
|
-
|
|
16407
|
-
|
|
16372
|
+
}
|
|
16373
|
+
return { found, missing };
|
|
16374
|
+
}
|
|
16375
|
+
async function parseEnvExample(installPath) {
|
|
16376
|
+
const examplePath = path8.join(installPath, ".env.example");
|
|
16377
|
+
try {
|
|
16378
|
+
const content = await fs8.readFile(examplePath, "utf-8");
|
|
16379
|
+
const keys = [];
|
|
16380
|
+
for (const line of content.split("\n")) {
|
|
16381
|
+
const trimmed = line.trim();
|
|
16382
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
16383
|
+
const match = trimmed.match(/^([A-Z][A-Z0-9_]*)=/);
|
|
16384
|
+
if (match) {
|
|
16385
|
+
keys.push(match[1]);
|
|
16386
|
+
}
|
|
16408
16387
|
}
|
|
16409
|
-
|
|
16410
|
-
|
|
16388
|
+
return keys;
|
|
16389
|
+
} catch {
|
|
16390
|
+
return [];
|
|
16411
16391
|
}
|
|
16412
|
-
await fs7.writeFile(envPath, lines.join("\n"), "utf-8");
|
|
16413
|
-
return envPath;
|
|
16414
16392
|
}
|
|
16415
16393
|
async function cmdInstall(args, flags) {
|
|
16416
16394
|
const pkgId = args[0];
|
|
16417
16395
|
if (!pkgId) {
|
|
16418
16396
|
console.error("Usage: rudi install <package>");
|
|
16419
|
-
console.error("
|
|
16420
|
-
console.error("
|
|
16421
|
-
console.error("
|
|
16397
|
+
console.error("Example: rudi install slack");
|
|
16398
|
+
console.error("");
|
|
16399
|
+
console.error("After installing, run:");
|
|
16400
|
+
console.error(" rudi secrets set <KEY> # Configure required secrets");
|
|
16401
|
+
console.error(" rudi integrate all # Wire up your agents");
|
|
16422
16402
|
process.exit(1);
|
|
16423
16403
|
}
|
|
16424
16404
|
const force = flags.force || false;
|
|
16425
|
-
let targetAgents = null;
|
|
16426
|
-
if (flags.agent) {
|
|
16427
|
-
const validAgents = ["claude", "codex", "gemini"];
|
|
16428
|
-
targetAgents = flags.agent.split(",").map((a) => a.trim()).filter((a) => validAgents.includes(a));
|
|
16429
|
-
if (targetAgents.length === 0) {
|
|
16430
|
-
console.error(`Invalid --agent value. Valid agents: ${validAgents.join(", ")}`);
|
|
16431
|
-
process.exit(1);
|
|
16432
|
-
}
|
|
16433
|
-
}
|
|
16434
16405
|
console.log(`Resolving ${pkgId}...`);
|
|
16435
16406
|
try {
|
|
16436
16407
|
const resolved = await resolvePackage(pkgId);
|
|
@@ -16453,27 +16424,41 @@ Dependencies:`);
|
|
|
16453
16424
|
console.log(` - ${dep.id} ${status}`);
|
|
16454
16425
|
}
|
|
16455
16426
|
}
|
|
16456
|
-
|
|
16457
|
-
|
|
16458
|
-
Required secrets:`);
|
|
16459
|
-
for (const secret of resolved.requires.secrets) {
|
|
16460
|
-
const name = typeof secret === "string" ? secret : secret.name;
|
|
16461
|
-
console.log(` - ${name}`);
|
|
16462
|
-
}
|
|
16463
|
-
}
|
|
16427
|
+
console.log(`
|
|
16428
|
+
Dependency check:`);
|
|
16464
16429
|
const depCheck = checkAllDependencies(resolved);
|
|
16465
16430
|
if (depCheck.results.length > 0) {
|
|
16466
|
-
console.log(`
|
|
16467
|
-
System dependencies:`);
|
|
16468
16431
|
for (const line of formatDependencyResults(depCheck.results)) {
|
|
16469
16432
|
console.log(line);
|
|
16470
16433
|
}
|
|
16471
|
-
|
|
16472
|
-
|
|
16473
|
-
|
|
16474
|
-
|
|
16434
|
+
}
|
|
16435
|
+
const secretsCheck = { found: [], missing: [] };
|
|
16436
|
+
if (resolved.requires?.secrets?.length > 0) {
|
|
16437
|
+
for (const secret of resolved.requires.secrets) {
|
|
16438
|
+
const name = typeof secret === "string" ? secret : secret.name;
|
|
16439
|
+
const isRequired = typeof secret === "object" ? secret.required !== false : true;
|
|
16440
|
+
const exists = await hasSecret(name);
|
|
16441
|
+
if (exists) {
|
|
16442
|
+
secretsCheck.found.push(name);
|
|
16443
|
+
console.log(` \u2713 ${name} (from secrets store)`);
|
|
16444
|
+
} else if (isRequired) {
|
|
16445
|
+
secretsCheck.missing.push(name);
|
|
16446
|
+
console.log(` \u25CB ${name} - not configured`);
|
|
16447
|
+
} else {
|
|
16448
|
+
console.log(` \u25CB ${name} (optional)`);
|
|
16449
|
+
}
|
|
16475
16450
|
}
|
|
16476
16451
|
}
|
|
16452
|
+
if (!depCheck.satisfied && !force) {
|
|
16453
|
+
console.error(`
|
|
16454
|
+
\u2717 Missing required dependencies. Install them first:`);
|
|
16455
|
+
for (const r of depCheck.results.filter((r2) => !r2.available)) {
|
|
16456
|
+
console.error(` rudi install ${r.type}:${r.name}`);
|
|
16457
|
+
}
|
|
16458
|
+
console.error(`
|
|
16459
|
+
Or use --force to install anyway.`);
|
|
16460
|
+
process.exit(1);
|
|
16461
|
+
}
|
|
16477
16462
|
console.log(`
|
|
16478
16463
|
Installing...`);
|
|
16479
16464
|
const result = await installPackage(pkgId, {
|
|
@@ -16498,29 +16483,70 @@ Installing...`);
|
|
|
16498
16483
|
if (resolved.kind === "stack") {
|
|
16499
16484
|
const manifest = await loadManifest(result.path);
|
|
16500
16485
|
if (manifest) {
|
|
16501
|
-
const
|
|
16502
|
-
|
|
16503
|
-
|
|
16504
|
-
|
|
16505
|
-
|
|
16506
|
-
|
|
16507
|
-
|
|
16508
|
-
|
|
16486
|
+
const depResult = await installDependencies(result.path, manifest);
|
|
16487
|
+
if (depResult.installed) {
|
|
16488
|
+
console.log(` \u2713 Dependencies installed`);
|
|
16489
|
+
} else if (depResult.error) {
|
|
16490
|
+
console.log(` \u26A0 Failed to install dependencies: ${depResult.error}`);
|
|
16491
|
+
}
|
|
16492
|
+
const { found, missing } = await checkSecrets(manifest);
|
|
16493
|
+
const envExampleKeys = await parseEnvExample(result.path);
|
|
16494
|
+
for (const key of envExampleKeys) {
|
|
16495
|
+
if (!found.includes(key) && !missing.includes(key)) {
|
|
16496
|
+
const exists = await hasSecret(key);
|
|
16497
|
+
if (!exists) {
|
|
16498
|
+
missing.push(key);
|
|
16499
|
+
} else {
|
|
16500
|
+
found.push(key);
|
|
16501
|
+
}
|
|
16502
|
+
}
|
|
16503
|
+
}
|
|
16504
|
+
if (missing.length > 0) {
|
|
16505
|
+
for (const key of missing) {
|
|
16506
|
+
const existing = await getSecret(key);
|
|
16507
|
+
if (existing === null) {
|
|
16508
|
+
await setSecret(key, "");
|
|
16509
|
+
}
|
|
16510
|
+
}
|
|
16511
|
+
}
|
|
16512
|
+
console.log(`
|
|
16513
|
+
Next steps:`);
|
|
16514
|
+
if (missing.length > 0) {
|
|
16509
16515
|
console.log(`
|
|
16510
|
-
|
|
16511
|
-
for (const
|
|
16512
|
-
const
|
|
16513
|
-
|
|
16514
|
-
|
|
16516
|
+
1. Configure secrets (${missing.length} pending):`);
|
|
16517
|
+
for (const key of missing) {
|
|
16518
|
+
const secret = getManifestSecrets(manifest).find(
|
|
16519
|
+
(s) => (typeof s === "string" ? s : s.name) === key
|
|
16520
|
+
);
|
|
16521
|
+
const helpUrl = getSecretLink(secret);
|
|
16522
|
+
console.log(` rudi secrets set ${key} "<your-value>"`);
|
|
16523
|
+
if (helpUrl) {
|
|
16524
|
+
console.log(` # Get yours: ${helpUrl}`);
|
|
16525
|
+
}
|
|
16515
16526
|
}
|
|
16516
16527
|
console.log(`
|
|
16517
|
-
|
|
16518
|
-
|
|
16528
|
+
Check status: rudi secrets list`);
|
|
16529
|
+
} else if (found.length > 0) {
|
|
16530
|
+
console.log(`
|
|
16531
|
+
1. Secrets: \u2713 ${found.length} configured`);
|
|
16532
|
+
} else {
|
|
16533
|
+
console.log(`
|
|
16534
|
+
1. Secrets: \u2713 None required`);
|
|
16535
|
+
}
|
|
16536
|
+
const agents = getInstalledAgents();
|
|
16537
|
+
if (agents.length > 0) {
|
|
16538
|
+
console.log(`
|
|
16539
|
+
2. Wire up your agents:`);
|
|
16540
|
+
console.log(` rudi integrate all`);
|
|
16541
|
+
console.log(` # Detected: ${agents.map((a) => a.name).join(", ")}`);
|
|
16519
16542
|
}
|
|
16543
|
+
console.log(`
|
|
16544
|
+
3. Restart your agent to use the stack`);
|
|
16545
|
+
return;
|
|
16520
16546
|
}
|
|
16521
16547
|
}
|
|
16522
16548
|
console.log(`
|
|
16523
|
-
|
|
16549
|
+
\u2713 Installed successfully.`);
|
|
16524
16550
|
} else {
|
|
16525
16551
|
console.error(`
|
|
16526
16552
|
\u2717 Installation failed: ${result.error}`);
|
|
@@ -16536,38 +16562,28 @@ Run with: rudi run ${pkgId}`);
|
|
|
16536
16562
|
}
|
|
16537
16563
|
|
|
16538
16564
|
// ../packages/runner/src/spawn.js
|
|
16539
|
-
var
|
|
16540
|
-
var
|
|
16541
|
-
var
|
|
16565
|
+
var import_child_process3 = require("child_process");
|
|
16566
|
+
var import_path8 = __toESM(require("path"), 1);
|
|
16567
|
+
var import_fs8 = __toESM(require("fs"), 1);
|
|
16542
16568
|
|
|
16543
16569
|
// ../packages/runner/src/secrets.js
|
|
16544
|
-
var
|
|
16545
|
-
var
|
|
16546
|
-
var
|
|
16547
|
-
var SECRETS_PATH =
|
|
16548
|
-
function
|
|
16549
|
-
if (!
|
|
16570
|
+
var import_fs7 = __toESM(require("fs"), 1);
|
|
16571
|
+
var import_path7 = __toESM(require("path"), 1);
|
|
16572
|
+
var import_os3 = __toESM(require("os"), 1);
|
|
16573
|
+
var SECRETS_PATH = import_path7.default.join(import_os3.default.homedir(), ".rudi", "secrets.json");
|
|
16574
|
+
function loadSecrets2() {
|
|
16575
|
+
if (!import_fs7.default.existsSync(SECRETS_PATH)) {
|
|
16550
16576
|
return {};
|
|
16551
16577
|
}
|
|
16552
16578
|
try {
|
|
16553
|
-
const content =
|
|
16579
|
+
const content = import_fs7.default.readFileSync(SECRETS_PATH, "utf-8");
|
|
16554
16580
|
return JSON.parse(content);
|
|
16555
16581
|
} catch {
|
|
16556
16582
|
return {};
|
|
16557
16583
|
}
|
|
16558
16584
|
}
|
|
16559
|
-
function saveSecrets(secrets) {
|
|
16560
|
-
const dir = import_path6.default.dirname(SECRETS_PATH);
|
|
16561
|
-
if (!import_fs6.default.existsSync(dir)) {
|
|
16562
|
-
import_fs6.default.mkdirSync(dir, { recursive: true });
|
|
16563
|
-
}
|
|
16564
|
-
import_fs6.default.writeFileSync(SECRETS_PATH, JSON.stringify(secrets, null, 2), {
|
|
16565
|
-
mode: 384
|
|
16566
|
-
// Read/write only for owner
|
|
16567
|
-
});
|
|
16568
|
-
}
|
|
16569
16585
|
async function getSecrets(required) {
|
|
16570
|
-
const allSecrets =
|
|
16586
|
+
const allSecrets = loadSecrets2();
|
|
16571
16587
|
const result = {};
|
|
16572
16588
|
for (const req of required) {
|
|
16573
16589
|
const name = typeof req === "string" ? req : req.name;
|
|
@@ -16580,8 +16596,8 @@ async function getSecrets(required) {
|
|
|
16580
16596
|
}
|
|
16581
16597
|
return result;
|
|
16582
16598
|
}
|
|
16583
|
-
function
|
|
16584
|
-
const allSecrets =
|
|
16599
|
+
function checkSecrets2(required) {
|
|
16600
|
+
const allSecrets = loadSecrets2();
|
|
16585
16601
|
const missing = [];
|
|
16586
16602
|
for (const req of required) {
|
|
16587
16603
|
const name = typeof req === "string" ? req : req.name;
|
|
@@ -16595,34 +16611,12 @@ function checkSecrets(required) {
|
|
|
16595
16611
|
missing
|
|
16596
16612
|
};
|
|
16597
16613
|
}
|
|
16598
|
-
function setSecret(name, value) {
|
|
16599
|
-
const secrets = loadSecrets();
|
|
16600
|
-
secrets[name] = value;
|
|
16601
|
-
saveSecrets(secrets);
|
|
16602
|
-
}
|
|
16603
|
-
function removeSecret(name) {
|
|
16604
|
-
const secrets = loadSecrets();
|
|
16605
|
-
delete secrets[name];
|
|
16606
|
-
saveSecrets(secrets);
|
|
16607
|
-
}
|
|
16608
16614
|
function listSecretNames() {
|
|
16609
|
-
const secrets =
|
|
16615
|
+
const secrets = loadSecrets2();
|
|
16610
16616
|
return Object.keys(secrets);
|
|
16611
16617
|
}
|
|
16612
|
-
function getMaskedSecrets() {
|
|
16613
|
-
const secrets = loadSecrets();
|
|
16614
|
-
const masked = {};
|
|
16615
|
-
for (const [name, value] of Object.entries(secrets)) {
|
|
16616
|
-
if (typeof value === "string" && value.length > 8) {
|
|
16617
|
-
masked[name] = value.slice(0, 4) + "..." + value.slice(-4);
|
|
16618
|
-
} else {
|
|
16619
|
-
masked[name] = "***";
|
|
16620
|
-
}
|
|
16621
|
-
}
|
|
16622
|
-
return masked;
|
|
16623
|
-
}
|
|
16624
16618
|
function redactSecrets(text, secrets) {
|
|
16625
|
-
const allSecrets = secrets ||
|
|
16619
|
+
const allSecrets = secrets || loadSecrets2();
|
|
16626
16620
|
let result = text;
|
|
16627
16621
|
for (const value of Object.values(allSecrets)) {
|
|
16628
16622
|
if (typeof value === "string" && value.length > 3) {
|
|
@@ -16632,27 +16626,18 @@ function redactSecrets(text, secrets) {
|
|
|
16632
16626
|
}
|
|
16633
16627
|
return result;
|
|
16634
16628
|
}
|
|
16635
|
-
function exportToEnv() {
|
|
16636
|
-
const secrets = loadSecrets();
|
|
16637
|
-
const lines = [];
|
|
16638
|
-
for (const [name, value] of Object.entries(secrets)) {
|
|
16639
|
-
const escaped = String(value).replace(/'/g, "'\\''");
|
|
16640
|
-
lines.push(`export ${name}='${escaped}'`);
|
|
16641
|
-
}
|
|
16642
|
-
return lines.join("\n");
|
|
16643
|
-
}
|
|
16644
16629
|
|
|
16645
16630
|
// ../packages/runner/src/spawn.js
|
|
16646
16631
|
async function runStack(id, options = {}) {
|
|
16647
16632
|
const { inputs = {}, cwd, env = {}, onStdout, onStderr, onExit, signal } = options;
|
|
16648
16633
|
const startTime = Date.now();
|
|
16649
16634
|
const packagePath = getPackagePath(id);
|
|
16650
|
-
const manifestPath =
|
|
16651
|
-
const { default:
|
|
16652
|
-
if (!
|
|
16635
|
+
const manifestPath = import_path8.default.join(packagePath, "manifest.json");
|
|
16636
|
+
const { default: fs26 } = await import("fs");
|
|
16637
|
+
if (!fs26.existsSync(manifestPath)) {
|
|
16653
16638
|
throw new Error(`Stack manifest not found: ${id}`);
|
|
16654
16639
|
}
|
|
16655
|
-
const manifest = JSON.parse(
|
|
16640
|
+
const manifest = JSON.parse(fs26.readFileSync(manifestPath, "utf-8"));
|
|
16656
16641
|
const { command, args } = resolveCommandFromManifest(manifest, packagePath);
|
|
16657
16642
|
const secrets = await getSecrets(manifest.requires?.secrets || []);
|
|
16658
16643
|
const runEnv = {
|
|
@@ -16663,7 +16648,7 @@ async function runStack(id, options = {}) {
|
|
|
16663
16648
|
RUDI_PACKAGE_ID: id,
|
|
16664
16649
|
RUDI_PACKAGE_PATH: packagePath
|
|
16665
16650
|
};
|
|
16666
|
-
const proc = (0,
|
|
16651
|
+
const proc = (0, import_child_process3.spawn)(command, args, {
|
|
16667
16652
|
cwd: cwd || packagePath,
|
|
16668
16653
|
env: runEnv,
|
|
16669
16654
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -16708,15 +16693,15 @@ async function runStack(id, options = {}) {
|
|
|
16708
16693
|
}
|
|
16709
16694
|
function getCommand(runtime) {
|
|
16710
16695
|
const runtimeName = runtime.replace("runtime:", "");
|
|
16711
|
-
const runtimePath =
|
|
16696
|
+
const runtimePath = import_path8.default.join(PATHS.runtimes, runtimeName);
|
|
16712
16697
|
const binaryPaths = [
|
|
16713
|
-
|
|
16714
|
-
|
|
16715
|
-
|
|
16716
|
-
|
|
16698
|
+
import_path8.default.join(runtimePath, "bin", runtimeName === "python" ? "python3" : runtimeName),
|
|
16699
|
+
import_path8.default.join(runtimePath, "bin", runtimeName),
|
|
16700
|
+
import_path8.default.join(runtimePath, runtimeName === "python" ? "python3" : runtimeName),
|
|
16701
|
+
import_path8.default.join(runtimePath, runtimeName)
|
|
16717
16702
|
];
|
|
16718
16703
|
for (const binPath of binaryPaths) {
|
|
16719
|
-
if (
|
|
16704
|
+
if (import_fs8.default.existsSync(binPath)) {
|
|
16720
16705
|
return binPath;
|
|
16721
16706
|
}
|
|
16722
16707
|
}
|
|
@@ -16740,7 +16725,7 @@ function resolveCommandFromManifest(manifest, packagePath) {
|
|
|
16740
16725
|
return { command: command2, args };
|
|
16741
16726
|
}
|
|
16742
16727
|
const entry = manifest.entry || "index.js";
|
|
16743
|
-
const entryPath =
|
|
16728
|
+
const entryPath = import_path8.default.join(packagePath, entry);
|
|
16744
16729
|
const runtime = manifest.runtime || "runtime:node";
|
|
16745
16730
|
const command = getCommand(runtime);
|
|
16746
16731
|
return { command, args: [entryPath] };
|
|
@@ -16749,21 +16734,21 @@ function resolveRelativePath(value, basePath) {
|
|
|
16749
16734
|
if (typeof value !== "string" || value.startsWith("-")) {
|
|
16750
16735
|
return value;
|
|
16751
16736
|
}
|
|
16752
|
-
if (
|
|
16737
|
+
if (import_path8.default.isAbsolute(value)) {
|
|
16753
16738
|
return value;
|
|
16754
16739
|
}
|
|
16755
16740
|
if (value.includes("/") || value.startsWith(".")) {
|
|
16756
|
-
return
|
|
16741
|
+
return import_path8.default.join(basePath, value);
|
|
16757
16742
|
}
|
|
16758
16743
|
return value;
|
|
16759
16744
|
}
|
|
16760
16745
|
|
|
16761
16746
|
// ../packages/manifest/src/stack.js
|
|
16762
16747
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
16763
|
-
var
|
|
16764
|
-
var
|
|
16748
|
+
var import_fs9 = __toESM(require("fs"), 1);
|
|
16749
|
+
var import_path9 = __toESM(require("path"), 1);
|
|
16765
16750
|
function parseStackManifest(filePath) {
|
|
16766
|
-
const content =
|
|
16751
|
+
const content = import_fs9.default.readFileSync(filePath, "utf-8");
|
|
16767
16752
|
return parseStackYaml(content, filePath);
|
|
16768
16753
|
}
|
|
16769
16754
|
function parseStackYaml(content, source = "stack.yaml") {
|
|
@@ -16881,8 +16866,8 @@ function validateStackManifest(manifest, source) {
|
|
|
16881
16866
|
function findStackManifest(dir) {
|
|
16882
16867
|
const candidates = ["stack.yaml", "stack.yml", "manifest.yaml", "manifest.yml"];
|
|
16883
16868
|
for (const filename of candidates) {
|
|
16884
|
-
const filePath =
|
|
16885
|
-
if (
|
|
16869
|
+
const filePath = import_path9.default.join(dir, filename);
|
|
16870
|
+
if (import_fs9.default.existsSync(filePath)) {
|
|
16886
16871
|
return filePath;
|
|
16887
16872
|
}
|
|
16888
16873
|
}
|
|
@@ -17037,8 +17022,8 @@ var validatePromptInternal = ajv.compile(promptSchema);
|
|
|
17037
17022
|
var validateRuntimeInternal = ajv.compile(runtimeSchema);
|
|
17038
17023
|
|
|
17039
17024
|
// src/commands/run.js
|
|
17040
|
-
var
|
|
17041
|
-
var
|
|
17025
|
+
var import_fs10 = __toESM(require("fs"), 1);
|
|
17026
|
+
var import_path10 = __toESM(require("path"), 1);
|
|
17042
17027
|
async function cmdRun(args, flags) {
|
|
17043
17028
|
const stackId = args[0];
|
|
17044
17029
|
if (!stackId) {
|
|
@@ -17059,9 +17044,9 @@ async function cmdRun(args, flags) {
|
|
|
17059
17044
|
if (manifestPath) {
|
|
17060
17045
|
manifest = parseStackManifest(manifestPath);
|
|
17061
17046
|
} else {
|
|
17062
|
-
const jsonPath =
|
|
17063
|
-
if (
|
|
17064
|
-
manifest = JSON.parse(
|
|
17047
|
+
const jsonPath = import_path10.default.join(packagePath, "manifest.json");
|
|
17048
|
+
if (import_fs10.default.existsSync(jsonPath)) {
|
|
17049
|
+
manifest = JSON.parse(import_fs10.default.readFileSync(jsonPath, "utf-8"));
|
|
17065
17050
|
}
|
|
17066
17051
|
}
|
|
17067
17052
|
} catch (error) {
|
|
@@ -17075,7 +17060,7 @@ async function cmdRun(args, flags) {
|
|
|
17075
17060
|
console.log(`Running: ${manifest.name || stackId}`);
|
|
17076
17061
|
const requiredSecrets = manifest.requires?.secrets || [];
|
|
17077
17062
|
if (requiredSecrets.length > 0) {
|
|
17078
|
-
const { satisfied, missing } =
|
|
17063
|
+
const { satisfied, missing } = checkSecrets2(requiredSecrets);
|
|
17079
17064
|
if (!satisfied) {
|
|
17080
17065
|
console.error(`
|
|
17081
17066
|
Missing required secrets:`);
|
|
@@ -17152,6 +17137,66 @@ async function cmdList(args, flags) {
|
|
|
17152
17137
|
process.exit(1);
|
|
17153
17138
|
}
|
|
17154
17139
|
}
|
|
17140
|
+
if (flags.detected && kind === "agent") {
|
|
17141
|
+
const installedAgents = getInstalledAgents();
|
|
17142
|
+
const summary = getMcpServerSummary();
|
|
17143
|
+
if (flags.json) {
|
|
17144
|
+
console.log(JSON.stringify({ installedAgents, summary }, null, 2));
|
|
17145
|
+
return;
|
|
17146
|
+
}
|
|
17147
|
+
console.log(`
|
|
17148
|
+
DETECTED AI AGENTS (${installedAgents.length}/${AGENT_CONFIGS2.length}):`);
|
|
17149
|
+
console.log("\u2500".repeat(50));
|
|
17150
|
+
for (const agent of AGENT_CONFIGS2) {
|
|
17151
|
+
const installed = installedAgents.find((a) => a.id === agent.id);
|
|
17152
|
+
const serverCount = summary[agent.id]?.serverCount || 0;
|
|
17153
|
+
if (installed) {
|
|
17154
|
+
console.log(` \u2713 ${agent.name}`);
|
|
17155
|
+
console.log(` ${serverCount} MCP server(s)`);
|
|
17156
|
+
console.log(` ${installed.configFile}`);
|
|
17157
|
+
} else {
|
|
17158
|
+
console.log(` \u25CB ${agent.name} (not installed)`);
|
|
17159
|
+
}
|
|
17160
|
+
}
|
|
17161
|
+
console.log(`
|
|
17162
|
+
Installed: ${installedAgents.length} of ${AGENT_CONFIGS2.length} agents`);
|
|
17163
|
+
return;
|
|
17164
|
+
}
|
|
17165
|
+
if (flags.detected && kind === "stack") {
|
|
17166
|
+
const servers = detectAllMcpServers();
|
|
17167
|
+
if (flags.json) {
|
|
17168
|
+
console.log(JSON.stringify(servers, null, 2));
|
|
17169
|
+
return;
|
|
17170
|
+
}
|
|
17171
|
+
if (servers.length === 0) {
|
|
17172
|
+
console.log("No MCP servers detected in agent configs.");
|
|
17173
|
+
console.log("\nChecked these agents:");
|
|
17174
|
+
for (const agent of AGENT_CONFIGS2) {
|
|
17175
|
+
console.log(` - ${agent.name}`);
|
|
17176
|
+
}
|
|
17177
|
+
return;
|
|
17178
|
+
}
|
|
17179
|
+
const byAgent = {};
|
|
17180
|
+
for (const server of servers) {
|
|
17181
|
+
if (!byAgent[server.agent]) byAgent[server.agent] = [];
|
|
17182
|
+
byAgent[server.agent].push(server);
|
|
17183
|
+
}
|
|
17184
|
+
console.log(`
|
|
17185
|
+
DETECTED MCP SERVERS (${servers.length}):`);
|
|
17186
|
+
console.log("\u2500".repeat(50));
|
|
17187
|
+
for (const [agentId, agentServers] of Object.entries(byAgent)) {
|
|
17188
|
+
const agentName = agentServers[0]?.agentName || agentId;
|
|
17189
|
+
console.log(`
|
|
17190
|
+
${agentName.toUpperCase()} (${agentServers.length}):`);
|
|
17191
|
+
for (const server of agentServers) {
|
|
17192
|
+
console.log(` \u{1F4E6} ${server.name}`);
|
|
17193
|
+
console.log(` ${server.command} ${server.cwd ? `(${server.cwd})` : ""}`);
|
|
17194
|
+
}
|
|
17195
|
+
}
|
|
17196
|
+
console.log(`
|
|
17197
|
+
Total: ${servers.length} MCP server(s) configured`);
|
|
17198
|
+
return;
|
|
17199
|
+
}
|
|
17155
17200
|
try {
|
|
17156
17201
|
let packages = await listInstalled(kind);
|
|
17157
17202
|
const categoryFilter = flags.category;
|
|
@@ -17181,68 +17226,267 @@ Install with: rudi install <package>`);
|
|
|
17181
17226
|
if (!byCategory[cat]) byCategory[cat] = [];
|
|
17182
17227
|
byCategory[cat].push(pkg);
|
|
17183
17228
|
}
|
|
17184
|
-
console.log(`
|
|
17185
|
-
PROMPTS (${packages.length}):`);
|
|
17186
|
-
console.log("\u2500".repeat(50));
|
|
17187
|
-
for (const [category, prompts] of Object.entries(byCategory).sort()) {
|
|
17188
|
-
console.log(`
|
|
17189
|
-
${category.toUpperCase()} (${prompts.length}):`);
|
|
17190
|
-
for (const pkg of prompts) {
|
|
17191
|
-
const icon = pkg.icon ? `${pkg.icon} ` : "";
|
|
17192
|
-
console.log(` ${icon}${pkg.id || `prompt:${pkg.name}`}`);
|
|
17193
|
-
if (pkg.description) {
|
|
17194
|
-
console.log(` ${pkg.description}`);
|
|
17195
|
-
}
|
|
17196
|
-
if (pkg.tags && pkg.tags.length > 0) {
|
|
17197
|
-
console.log(` Tags: ${pkg.tags.join(", ")}`);
|
|
17198
|
-
}
|
|
17199
|
-
}
|
|
17229
|
+
console.log(`
|
|
17230
|
+
PROMPTS (${packages.length}):`);
|
|
17231
|
+
console.log("\u2500".repeat(50));
|
|
17232
|
+
for (const [category, prompts] of Object.entries(byCategory).sort()) {
|
|
17233
|
+
console.log(`
|
|
17234
|
+
${category.toUpperCase()} (${prompts.length}):`);
|
|
17235
|
+
for (const pkg of prompts) {
|
|
17236
|
+
const icon = pkg.icon ? `${pkg.icon} ` : "";
|
|
17237
|
+
console.log(` ${icon}${pkg.id || `prompt:${pkg.name}`}`);
|
|
17238
|
+
if (pkg.description) {
|
|
17239
|
+
console.log(` ${pkg.description}`);
|
|
17240
|
+
}
|
|
17241
|
+
if (pkg.tags && pkg.tags.length > 0) {
|
|
17242
|
+
console.log(` Tags: ${pkg.tags.join(", ")}`);
|
|
17243
|
+
}
|
|
17244
|
+
}
|
|
17245
|
+
}
|
|
17246
|
+
console.log(`
|
|
17247
|
+
Total: ${packages.length} prompt(s)`);
|
|
17248
|
+
console.log(`
|
|
17249
|
+
Filter by category: rudi list prompts --category=coding`);
|
|
17250
|
+
return;
|
|
17251
|
+
}
|
|
17252
|
+
const grouped = {
|
|
17253
|
+
stack: packages.filter((p) => p.kind === "stack"),
|
|
17254
|
+
prompt: packages.filter((p) => p.kind === "prompt"),
|
|
17255
|
+
runtime: packages.filter((p) => p.kind === "runtime"),
|
|
17256
|
+
binary: packages.filter((p) => p.kind === "binary"),
|
|
17257
|
+
agent: packages.filter((p) => p.kind === "agent")
|
|
17258
|
+
};
|
|
17259
|
+
let total = 0;
|
|
17260
|
+
for (const [pkgKind, pkgs] of Object.entries(grouped)) {
|
|
17261
|
+
if (pkgs.length === 0) continue;
|
|
17262
|
+
if (kind && kind !== pkgKind) continue;
|
|
17263
|
+
console.log(`
|
|
17264
|
+
${headingForKind2(pkgKind)} (${pkgs.length}):`);
|
|
17265
|
+
console.log("\u2500".repeat(50));
|
|
17266
|
+
for (const pkg of pkgs) {
|
|
17267
|
+
const icon = pkg.icon ? `${pkg.icon} ` : "";
|
|
17268
|
+
console.log(` ${icon}${pkg.id || `${pkgKind}:${pkg.name}`}`);
|
|
17269
|
+
console.log(` Version: ${pkg.version || "unknown"}`);
|
|
17270
|
+
if (pkg.description) {
|
|
17271
|
+
console.log(` ${pkg.description}`);
|
|
17272
|
+
}
|
|
17273
|
+
if (pkg.category) {
|
|
17274
|
+
console.log(` Category: ${pkg.category}`);
|
|
17275
|
+
}
|
|
17276
|
+
if (pkg.tags && pkg.tags.length > 0) {
|
|
17277
|
+
console.log(` Tags: ${pkg.tags.join(", ")}`);
|
|
17278
|
+
}
|
|
17279
|
+
if (pkg.installedAt) {
|
|
17280
|
+
console.log(` Installed: ${new Date(pkg.installedAt).toLocaleDateString()}`);
|
|
17281
|
+
}
|
|
17282
|
+
total++;
|
|
17283
|
+
}
|
|
17284
|
+
}
|
|
17285
|
+
console.log(`
|
|
17286
|
+
Total: ${total} package(s)`);
|
|
17287
|
+
} catch (error) {
|
|
17288
|
+
console.error(`Failed to list packages: ${error.message}`);
|
|
17289
|
+
process.exit(1);
|
|
17290
|
+
}
|
|
17291
|
+
}
|
|
17292
|
+
|
|
17293
|
+
// src/utils/mcp-registry.js
|
|
17294
|
+
var fs13 = __toESM(require("fs/promises"), 1);
|
|
17295
|
+
var path13 = __toESM(require("path"), 1);
|
|
17296
|
+
var os4 = __toESM(require("os"), 1);
|
|
17297
|
+
var HOME = os4.homedir();
|
|
17298
|
+
async function readJson(filePath) {
|
|
17299
|
+
try {
|
|
17300
|
+
const content = await fs13.readFile(filePath, "utf-8");
|
|
17301
|
+
return JSON.parse(content);
|
|
17302
|
+
} catch {
|
|
17303
|
+
return {};
|
|
17304
|
+
}
|
|
17305
|
+
}
|
|
17306
|
+
async function writeJson(filePath, data) {
|
|
17307
|
+
const dir = path13.dirname(filePath);
|
|
17308
|
+
await fs13.mkdir(dir, { recursive: true });
|
|
17309
|
+
await fs13.writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
17310
|
+
}
|
|
17311
|
+
function parseTomlValue(value) {
|
|
17312
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
17313
|
+
return value.slice(1, -1);
|
|
17314
|
+
}
|
|
17315
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
17316
|
+
const inner = value.slice(1, -1).trim();
|
|
17317
|
+
if (!inner) return [];
|
|
17318
|
+
const items = [];
|
|
17319
|
+
let current = "";
|
|
17320
|
+
let inQuote = false;
|
|
17321
|
+
let quoteChar = "";
|
|
17322
|
+
for (const char of inner) {
|
|
17323
|
+
if ((char === '"' || char === "'") && !inQuote) {
|
|
17324
|
+
inQuote = true;
|
|
17325
|
+
quoteChar = char;
|
|
17326
|
+
} else if (char === quoteChar && inQuote) {
|
|
17327
|
+
inQuote = false;
|
|
17328
|
+
items.push(current);
|
|
17329
|
+
current = "";
|
|
17330
|
+
} else if (char === "," && !inQuote) {
|
|
17331
|
+
} else if (inQuote) {
|
|
17332
|
+
current += char;
|
|
17333
|
+
}
|
|
17334
|
+
}
|
|
17335
|
+
return items;
|
|
17336
|
+
}
|
|
17337
|
+
if (value === "true") return true;
|
|
17338
|
+
if (value === "false") return false;
|
|
17339
|
+
const num = Number(value);
|
|
17340
|
+
if (!isNaN(num)) return num;
|
|
17341
|
+
return value;
|
|
17342
|
+
}
|
|
17343
|
+
function parseToml(content) {
|
|
17344
|
+
const result = {};
|
|
17345
|
+
const lines = content.split("\n");
|
|
17346
|
+
let currentTable = [];
|
|
17347
|
+
for (const line of lines) {
|
|
17348
|
+
const trimmed = line.trim();
|
|
17349
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
17350
|
+
const tableMatch = trimmed.match(/^\[([^\]]+)\]$/);
|
|
17351
|
+
if (tableMatch) {
|
|
17352
|
+
currentTable = tableMatch[1].split(".");
|
|
17353
|
+
let obj = result;
|
|
17354
|
+
for (const key of currentTable) {
|
|
17355
|
+
obj[key] = obj[key] || {};
|
|
17356
|
+
obj = obj[key];
|
|
17357
|
+
}
|
|
17358
|
+
continue;
|
|
17359
|
+
}
|
|
17360
|
+
const kvMatch = trimmed.match(/^([^=]+)=(.*)$/);
|
|
17361
|
+
if (kvMatch) {
|
|
17362
|
+
const key = kvMatch[1].trim();
|
|
17363
|
+
let value = kvMatch[2].trim();
|
|
17364
|
+
const parsed = parseTomlValue(value);
|
|
17365
|
+
let obj = result;
|
|
17366
|
+
for (const tableKey of currentTable) {
|
|
17367
|
+
obj = obj[tableKey];
|
|
17368
|
+
}
|
|
17369
|
+
obj[key] = parsed;
|
|
17370
|
+
}
|
|
17371
|
+
}
|
|
17372
|
+
return result;
|
|
17373
|
+
}
|
|
17374
|
+
function tomlValue(value) {
|
|
17375
|
+
if (typeof value === "string") {
|
|
17376
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
17377
|
+
}
|
|
17378
|
+
if (typeof value === "boolean") {
|
|
17379
|
+
return value ? "true" : "false";
|
|
17380
|
+
}
|
|
17381
|
+
if (typeof value === "number") {
|
|
17382
|
+
return String(value);
|
|
17383
|
+
}
|
|
17384
|
+
if (Array.isArray(value)) {
|
|
17385
|
+
const items = value.map((v) => tomlValue(v));
|
|
17386
|
+
return `[${items.join(", ")}]`;
|
|
17387
|
+
}
|
|
17388
|
+
return String(value);
|
|
17389
|
+
}
|
|
17390
|
+
function stringifyToml(config, prefix = "") {
|
|
17391
|
+
const lines = [];
|
|
17392
|
+
for (const [key, value] of Object.entries(config)) {
|
|
17393
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
17394
|
+
lines.push(`${key} = ${tomlValue(value)}`);
|
|
17395
|
+
}
|
|
17396
|
+
}
|
|
17397
|
+
for (const [key, value] of Object.entries(config)) {
|
|
17398
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
17399
|
+
const tablePath = prefix ? `${prefix}.${key}` : key;
|
|
17400
|
+
const hasSimpleValues = Object.values(value).some(
|
|
17401
|
+
(v) => typeof v !== "object" || Array.isArray(v)
|
|
17402
|
+
);
|
|
17403
|
+
if (hasSimpleValues) {
|
|
17404
|
+
lines.push("");
|
|
17405
|
+
lines.push(`[${tablePath}]`);
|
|
17406
|
+
}
|
|
17407
|
+
const nested = stringifyToml(value, tablePath);
|
|
17408
|
+
if (nested.trim()) {
|
|
17409
|
+
lines.push(nested);
|
|
17200
17410
|
}
|
|
17201
|
-
console.log(`
|
|
17202
|
-
Total: ${packages.length} prompt(s)`);
|
|
17203
|
-
console.log(`
|
|
17204
|
-
Filter by category: rudi list prompts --category=coding`);
|
|
17205
|
-
return;
|
|
17206
17411
|
}
|
|
17207
|
-
|
|
17208
|
-
|
|
17209
|
-
|
|
17210
|
-
|
|
17211
|
-
|
|
17212
|
-
|
|
17213
|
-
|
|
17214
|
-
|
|
17215
|
-
|
|
17216
|
-
|
|
17217
|
-
|
|
17218
|
-
|
|
17219
|
-
|
|
17220
|
-
|
|
17221
|
-
|
|
17222
|
-
|
|
17223
|
-
|
|
17224
|
-
|
|
17225
|
-
|
|
17226
|
-
|
|
17227
|
-
|
|
17228
|
-
|
|
17229
|
-
console.log(` Category: ${pkg.category}`);
|
|
17230
|
-
}
|
|
17231
|
-
if (pkg.tags && pkg.tags.length > 0) {
|
|
17232
|
-
console.log(` Tags: ${pkg.tags.join(", ")}`);
|
|
17233
|
-
}
|
|
17234
|
-
if (pkg.installedAt) {
|
|
17235
|
-
console.log(` Installed: ${new Date(pkg.installedAt).toLocaleDateString()}`);
|
|
17236
|
-
}
|
|
17237
|
-
total++;
|
|
17238
|
-
}
|
|
17412
|
+
}
|
|
17413
|
+
return lines.join("\n");
|
|
17414
|
+
}
|
|
17415
|
+
async function readToml(filePath) {
|
|
17416
|
+
try {
|
|
17417
|
+
const content = await fs13.readFile(filePath, "utf-8");
|
|
17418
|
+
return parseToml(content);
|
|
17419
|
+
} catch {
|
|
17420
|
+
return {};
|
|
17421
|
+
}
|
|
17422
|
+
}
|
|
17423
|
+
async function writeToml(filePath, data) {
|
|
17424
|
+
const dir = path13.dirname(filePath);
|
|
17425
|
+
await fs13.mkdir(dir, { recursive: true });
|
|
17426
|
+
await fs13.writeFile(filePath, stringifyToml(data), "utf-8");
|
|
17427
|
+
}
|
|
17428
|
+
async function unregisterMcpCodex(stackId) {
|
|
17429
|
+
const configPath = AGENT_CONFIGS.codex;
|
|
17430
|
+
try {
|
|
17431
|
+
const config = await readToml(configPath);
|
|
17432
|
+
if (!config.mcp_servers || !config.mcp_servers[stackId]) {
|
|
17433
|
+
return { success: true, skipped: true };
|
|
17239
17434
|
}
|
|
17240
|
-
|
|
17241
|
-
|
|
17435
|
+
delete config.mcp_servers[stackId];
|
|
17436
|
+
await writeToml(configPath, config);
|
|
17437
|
+
console.log(` Unregistered MCP from Codex: ${stackId}`);
|
|
17438
|
+
return { success: true };
|
|
17242
17439
|
} catch (error) {
|
|
17243
|
-
console.error(`Failed to
|
|
17244
|
-
|
|
17440
|
+
console.error(` Failed to unregister MCP from Codex: ${error.message}`);
|
|
17441
|
+
return { success: false, error: error.message };
|
|
17442
|
+
}
|
|
17443
|
+
}
|
|
17444
|
+
function getInstalledAgentIds() {
|
|
17445
|
+
return getInstalledAgents().map((a) => a.id);
|
|
17446
|
+
}
|
|
17447
|
+
async function unregisterMcpGeneric(agentId, stackId) {
|
|
17448
|
+
const agentConfig = AGENT_CONFIGS2.find((a) => a.id === agentId);
|
|
17449
|
+
if (!agentConfig) {
|
|
17450
|
+
return { success: false, error: `Unknown agent: ${agentId}` };
|
|
17451
|
+
}
|
|
17452
|
+
const configPath = findAgentConfig(agentConfig);
|
|
17453
|
+
if (!configPath) {
|
|
17454
|
+
return { success: true, skipped: true, reason: "Agent not installed" };
|
|
17455
|
+
}
|
|
17456
|
+
if (agentId === "codex") {
|
|
17457
|
+
return unregisterMcpCodex(stackId);
|
|
17458
|
+
}
|
|
17459
|
+
try {
|
|
17460
|
+
const settings = await readJson(configPath);
|
|
17461
|
+
const key = agentConfig.key;
|
|
17462
|
+
if (!settings[key] || !settings[key][stackId]) {
|
|
17463
|
+
return { success: true, skipped: true, reason: "Server not found" };
|
|
17464
|
+
}
|
|
17465
|
+
delete settings[key][stackId];
|
|
17466
|
+
await writeJson(configPath, settings);
|
|
17467
|
+
console.log(` Unregistered MCP from ${agentConfig.name}: ${stackId}`);
|
|
17468
|
+
return { success: true, configPath };
|
|
17469
|
+
} catch (error) {
|
|
17470
|
+
console.error(` Failed to unregister MCP from ${agentConfig.name}: ${error.message}`);
|
|
17471
|
+
return { success: false, error: error.message };
|
|
17472
|
+
}
|
|
17473
|
+
}
|
|
17474
|
+
async function unregisterMcpAll(stackId, targetAgents = null) {
|
|
17475
|
+
let agentIds = getInstalledAgentIds();
|
|
17476
|
+
if (targetAgents && targetAgents.length > 0) {
|
|
17477
|
+
const idMap = {
|
|
17478
|
+
"claude": "claude-code",
|
|
17479
|
+
"codex": "codex",
|
|
17480
|
+
"gemini": "gemini"
|
|
17481
|
+
};
|
|
17482
|
+
const targetIds = targetAgents.map((a) => idMap[a] || a);
|
|
17483
|
+
agentIds = agentIds.filter((id) => targetIds.includes(id));
|
|
17484
|
+
}
|
|
17485
|
+
const results = {};
|
|
17486
|
+
for (const agentId of agentIds) {
|
|
17487
|
+
results[agentId] = await unregisterMcpGeneric(agentId, stackId);
|
|
17245
17488
|
}
|
|
17489
|
+
return results;
|
|
17246
17490
|
}
|
|
17247
17491
|
|
|
17248
17492
|
// src/commands/remove.js
|
|
@@ -17381,89 +17625,136 @@ async function cmdSecrets(args, flags) {
|
|
|
17381
17625
|
case "set":
|
|
17382
17626
|
await secretsSet(args.slice(1), flags);
|
|
17383
17627
|
break;
|
|
17628
|
+
case "get":
|
|
17629
|
+
await secretsGet(args.slice(1), flags);
|
|
17630
|
+
break;
|
|
17384
17631
|
case "list":
|
|
17385
17632
|
case "ls":
|
|
17386
|
-
secretsList(flags);
|
|
17633
|
+
await secretsList(flags);
|
|
17387
17634
|
break;
|
|
17388
17635
|
case "remove":
|
|
17389
17636
|
case "rm":
|
|
17390
17637
|
case "delete":
|
|
17391
|
-
secretsRemove(args.slice(1), flags);
|
|
17638
|
+
await secretsRemove(args.slice(1), flags);
|
|
17392
17639
|
break;
|
|
17393
|
-
case "
|
|
17394
|
-
|
|
17640
|
+
case "info":
|
|
17641
|
+
secretsInfo();
|
|
17395
17642
|
break;
|
|
17396
17643
|
default:
|
|
17397
17644
|
console.log(`
|
|
17398
|
-
rudi secrets - Manage secrets
|
|
17645
|
+
rudi secrets - Manage secrets (stored in ${getStorageInfo().backend})
|
|
17399
17646
|
|
|
17400
17647
|
COMMANDS
|
|
17401
|
-
set <name> Set a secret (prompts for value)
|
|
17648
|
+
set <name> Set a secret (prompts for value securely)
|
|
17649
|
+
get <name> Get a secret value (for scripts)
|
|
17402
17650
|
list List configured secrets (values masked)
|
|
17403
17651
|
remove <name> Remove a secret
|
|
17404
|
-
|
|
17652
|
+
info Show storage backend info
|
|
17405
17653
|
|
|
17406
17654
|
EXAMPLES
|
|
17407
|
-
rudi secrets set
|
|
17655
|
+
rudi secrets set SLACK_BOT_TOKEN
|
|
17408
17656
|
rudi secrets list
|
|
17409
17657
|
rudi secrets remove GITHUB_TOKEN
|
|
17658
|
+
|
|
17659
|
+
SECURITY
|
|
17660
|
+
Secrets are stored in macOS Keychain when available.
|
|
17661
|
+
Fallback uses encrypted JSON at ~/.rudi/secrets.json
|
|
17410
17662
|
`);
|
|
17411
17663
|
}
|
|
17412
17664
|
}
|
|
17413
17665
|
async function secretsSet(args, flags) {
|
|
17414
17666
|
const name = args[0];
|
|
17667
|
+
const valueArg = args[1];
|
|
17415
17668
|
if (!name) {
|
|
17416
|
-
console.error("Usage: rudi secrets set <name>");
|
|
17417
|
-
console.error("
|
|
17669
|
+
console.error("Usage: rudi secrets set <name> [value]");
|
|
17670
|
+
console.error("");
|
|
17671
|
+
console.error("Examples:");
|
|
17672
|
+
console.error(" rudi secrets set SLACK_BOT_TOKEN # Interactive prompt");
|
|
17673
|
+
console.error(' rudi secrets set SLACK_BOT_TOKEN "xoxb-..." # Direct value');
|
|
17418
17674
|
process.exit(1);
|
|
17419
17675
|
}
|
|
17420
17676
|
if (!/^[A-Z][A-Z0-9_]*$/.test(name)) {
|
|
17421
17677
|
console.error("Secret name should be UPPER_SNAKE_CASE");
|
|
17422
|
-
console.error("Example:
|
|
17678
|
+
console.error("Example: SLACK_BOT_TOKEN, GITHUB_API_KEY");
|
|
17423
17679
|
process.exit(1);
|
|
17424
17680
|
}
|
|
17425
|
-
const
|
|
17426
|
-
if (
|
|
17681
|
+
const exists = await hasSecret(name);
|
|
17682
|
+
if (exists && !flags.force) {
|
|
17427
17683
|
console.log(`Secret ${name} already exists.`);
|
|
17428
17684
|
console.log("Use --force to overwrite.");
|
|
17429
17685
|
process.exit(0);
|
|
17430
17686
|
}
|
|
17431
|
-
|
|
17687
|
+
let value = valueArg;
|
|
17688
|
+
if (!value) {
|
|
17689
|
+
if (process.stdin.isTTY) {
|
|
17690
|
+
value = await promptSecret(`Enter value for ${name}: `);
|
|
17691
|
+
} else {
|
|
17692
|
+
console.error("No value provided.");
|
|
17693
|
+
console.error("Usage: rudi secrets set <name> <value>");
|
|
17694
|
+
process.exit(1);
|
|
17695
|
+
}
|
|
17696
|
+
}
|
|
17432
17697
|
if (!value) {
|
|
17433
17698
|
console.error("No value provided");
|
|
17434
17699
|
process.exit(1);
|
|
17435
17700
|
}
|
|
17436
|
-
setSecret(name, value);
|
|
17437
|
-
|
|
17701
|
+
await setSecret(name, value);
|
|
17702
|
+
const info = getStorageInfo();
|
|
17703
|
+
console.log(`\u2713 Secret ${name} saved (${info.backend})`);
|
|
17704
|
+
}
|
|
17705
|
+
async function secretsGet(args, flags) {
|
|
17706
|
+
const name = args[0];
|
|
17707
|
+
if (!name) {
|
|
17708
|
+
console.error("Usage: rudi secrets get <name>");
|
|
17709
|
+
process.exit(1);
|
|
17710
|
+
}
|
|
17711
|
+
const value = await getSecret(name);
|
|
17712
|
+
if (value) {
|
|
17713
|
+
process.stdout.write(value);
|
|
17714
|
+
} else {
|
|
17715
|
+
process.exit(1);
|
|
17716
|
+
}
|
|
17438
17717
|
}
|
|
17439
|
-
function secretsList(flags) {
|
|
17440
|
-
const names =
|
|
17718
|
+
async function secretsList(flags) {
|
|
17719
|
+
const names = await listSecrets();
|
|
17441
17720
|
if (names.length === 0) {
|
|
17442
17721
|
console.log("No secrets configured.");
|
|
17443
17722
|
console.log("\nSet with: rudi secrets set <name>");
|
|
17444
17723
|
return;
|
|
17445
17724
|
}
|
|
17446
17725
|
if (flags.json) {
|
|
17447
|
-
|
|
17726
|
+
const masked2 = await getMaskedSecrets();
|
|
17727
|
+
console.log(JSON.stringify(masked2, null, 2));
|
|
17448
17728
|
return;
|
|
17449
17729
|
}
|
|
17450
|
-
const masked = getMaskedSecrets();
|
|
17451
|
-
|
|
17730
|
+
const masked = await getMaskedSecrets();
|
|
17731
|
+
const info = getStorageInfo();
|
|
17732
|
+
const pending = Object.values(masked).filter((v) => v === "(pending)").length;
|
|
17733
|
+
const configured = names.length - pending;
|
|
17734
|
+
console.log(`
|
|
17735
|
+
Secrets (${info.backend}):`);
|
|
17452
17736
|
console.log("\u2500".repeat(50));
|
|
17453
|
-
for (const name of names
|
|
17454
|
-
|
|
17737
|
+
for (const name of names) {
|
|
17738
|
+
const status = masked[name] === "(pending)" ? "\u25CB" : "\u2713";
|
|
17739
|
+
console.log(` ${status} ${name.padEnd(28)} ${masked[name]}`);
|
|
17740
|
+
}
|
|
17741
|
+
console.log("\u2500".repeat(50));
|
|
17742
|
+
if (pending > 0) {
|
|
17743
|
+
console.log(` ${configured} configured, ${pending} pending`);
|
|
17744
|
+
console.log(`
|
|
17745
|
+
Set pending: rudi secrets set <name> "<value>"`);
|
|
17746
|
+
} else {
|
|
17747
|
+
console.log(` ${configured} configured`);
|
|
17455
17748
|
}
|
|
17456
|
-
console.log(`
|
|
17457
|
-
Total: ${names.length} secret(s)`);
|
|
17458
17749
|
}
|
|
17459
|
-
function secretsRemove(args, flags) {
|
|
17750
|
+
async function secretsRemove(args, flags) {
|
|
17460
17751
|
const name = args[0];
|
|
17461
17752
|
if (!name) {
|
|
17462
17753
|
console.error("Usage: rudi secrets remove <name>");
|
|
17463
17754
|
process.exit(1);
|
|
17464
17755
|
}
|
|
17465
|
-
const
|
|
17466
|
-
if (!
|
|
17756
|
+
const allNames = await listSecrets();
|
|
17757
|
+
if (!allNames.includes(name)) {
|
|
17467
17758
|
console.error(`Secret not found: ${name}`);
|
|
17468
17759
|
process.exit(1);
|
|
17469
17760
|
}
|
|
@@ -17472,17 +17763,19 @@ function secretsRemove(args, flags) {
|
|
|
17472
17763
|
console.log("Run with --force to confirm.");
|
|
17473
17764
|
process.exit(0);
|
|
17474
17765
|
}
|
|
17475
|
-
removeSecret(name);
|
|
17766
|
+
await removeSecret(name);
|
|
17476
17767
|
console.log(`\u2713 Secret ${name} removed`);
|
|
17477
17768
|
}
|
|
17478
|
-
function
|
|
17479
|
-
const
|
|
17480
|
-
|
|
17481
|
-
|
|
17482
|
-
|
|
17483
|
-
}
|
|
17484
|
-
console.log(
|
|
17485
|
-
console.log(
|
|
17769
|
+
function secretsInfo() {
|
|
17770
|
+
const info = getStorageInfo();
|
|
17771
|
+
console.log("\nSecrets Storage:");
|
|
17772
|
+
console.log("\u2500".repeat(50));
|
|
17773
|
+
console.log(` Backend: ${info.backend}`);
|
|
17774
|
+
console.log(` File: ${info.file}`);
|
|
17775
|
+
console.log(` Permissions: ${info.permissions}`);
|
|
17776
|
+
console.log("");
|
|
17777
|
+
console.log(" Security: File permissions (0600) protect secrets.");
|
|
17778
|
+
console.log(" Same approach as AWS CLI, SSH, GitHub CLI.");
|
|
17486
17779
|
}
|
|
17487
17780
|
function promptSecret(prompt) {
|
|
17488
17781
|
return new Promise((resolve) => {
|
|
@@ -17517,13 +17810,13 @@ function promptSecret(prompt) {
|
|
|
17517
17810
|
}
|
|
17518
17811
|
|
|
17519
17812
|
// src/commands/db.js
|
|
17520
|
-
var
|
|
17521
|
-
var
|
|
17813
|
+
var import_fs12 = require("fs");
|
|
17814
|
+
var import_path12 = require("path");
|
|
17522
17815
|
|
|
17523
17816
|
// ../packages/db/src/index.js
|
|
17524
17817
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
17525
|
-
var
|
|
17526
|
-
var
|
|
17818
|
+
var import_path11 = __toESM(require("path"), 1);
|
|
17819
|
+
var import_fs11 = __toESM(require("fs"), 1);
|
|
17527
17820
|
init_src();
|
|
17528
17821
|
|
|
17529
17822
|
// ../packages/db/src/schema.js
|
|
@@ -18260,9 +18553,9 @@ var DB_PATH = PATHS.dbFile;
|
|
|
18260
18553
|
var db = null;
|
|
18261
18554
|
function getDb(options = {}) {
|
|
18262
18555
|
if (!db) {
|
|
18263
|
-
const dbDir =
|
|
18264
|
-
if (!
|
|
18265
|
-
|
|
18556
|
+
const dbDir = import_path11.default.dirname(DB_PATH);
|
|
18557
|
+
if (!import_fs11.default.existsSync(dbDir)) {
|
|
18558
|
+
import_fs11.default.mkdirSync(dbDir, { recursive: true });
|
|
18266
18559
|
}
|
|
18267
18560
|
db = new import_better_sqlite3.default(DB_PATH, {
|
|
18268
18561
|
readonly: options.readonly || false
|
|
@@ -18275,7 +18568,7 @@ function getDb(options = {}) {
|
|
|
18275
18568
|
return db;
|
|
18276
18569
|
}
|
|
18277
18570
|
function isDatabaseInitialized() {
|
|
18278
|
-
if (!
|
|
18571
|
+
if (!import_fs11.default.existsSync(DB_PATH)) {
|
|
18279
18572
|
return false;
|
|
18280
18573
|
}
|
|
18281
18574
|
try {
|
|
@@ -18295,7 +18588,7 @@ function getDbPath() {
|
|
|
18295
18588
|
}
|
|
18296
18589
|
function getDbSize() {
|
|
18297
18590
|
try {
|
|
18298
|
-
const stats =
|
|
18591
|
+
const stats = import_fs11.default.statSync(DB_PATH);
|
|
18299
18592
|
return stats.size;
|
|
18300
18593
|
} catch {
|
|
18301
18594
|
return null;
|
|
@@ -18556,12 +18849,12 @@ function dbBackup(args, flags) {
|
|
|
18556
18849
|
let backupPath = args[0];
|
|
18557
18850
|
if (!backupPath) {
|
|
18558
18851
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
18559
|
-
backupPath = (0,
|
|
18852
|
+
backupPath = (0, import_path12.join)((0, import_path12.dirname)(dbPath), `rudi-backup-${timestamp}.db`);
|
|
18560
18853
|
}
|
|
18561
18854
|
if (backupPath.startsWith("~")) {
|
|
18562
|
-
backupPath = (0,
|
|
18855
|
+
backupPath = (0, import_path12.join)(process.env.HOME || "", backupPath.slice(1));
|
|
18563
18856
|
}
|
|
18564
|
-
if ((0,
|
|
18857
|
+
if ((0, import_fs12.existsSync)(backupPath) && !flags.force) {
|
|
18565
18858
|
console.error(`Backup file already exists: ${backupPath}`);
|
|
18566
18859
|
console.error("Use --force to overwrite.");
|
|
18567
18860
|
process.exit(1);
|
|
@@ -18573,7 +18866,7 @@ function dbBackup(args, flags) {
|
|
|
18573
18866
|
const db3 = getDb();
|
|
18574
18867
|
db3.exec("VACUUM INTO ?", [backupPath]);
|
|
18575
18868
|
} catch (e) {
|
|
18576
|
-
(0,
|
|
18869
|
+
(0, import_fs12.copyFileSync)(dbPath, backupPath);
|
|
18577
18870
|
}
|
|
18578
18871
|
const size = getDbSize();
|
|
18579
18872
|
console.log(` Size: ${formatBytes(size)}`);
|
|
@@ -18678,24 +18971,24 @@ function dbTables(flags) {
|
|
|
18678
18971
|
}
|
|
18679
18972
|
|
|
18680
18973
|
// src/commands/import.js
|
|
18681
|
-
var
|
|
18682
|
-
var
|
|
18683
|
-
var
|
|
18974
|
+
var import_fs13 = require("fs");
|
|
18975
|
+
var import_path13 = require("path");
|
|
18976
|
+
var import_os4 = require("os");
|
|
18684
18977
|
var import_crypto2 = require("crypto");
|
|
18685
18978
|
var PROVIDERS = {
|
|
18686
18979
|
claude: {
|
|
18687
18980
|
name: "Claude Code",
|
|
18688
|
-
baseDir: (0,
|
|
18981
|
+
baseDir: (0, import_path13.join)((0, import_os4.homedir)(), ".claude", "projects"),
|
|
18689
18982
|
pattern: /\.jsonl$/
|
|
18690
18983
|
},
|
|
18691
18984
|
codex: {
|
|
18692
18985
|
name: "Codex",
|
|
18693
|
-
baseDir: (0,
|
|
18986
|
+
baseDir: (0, import_path13.join)((0, import_os4.homedir)(), ".codex", "sessions"),
|
|
18694
18987
|
pattern: /\.jsonl$/
|
|
18695
18988
|
},
|
|
18696
18989
|
gemini: {
|
|
18697
18990
|
name: "Gemini",
|
|
18698
|
-
baseDir: (0,
|
|
18991
|
+
baseDir: (0, import_path13.join)((0, import_os4.homedir)(), ".gemini", "sessions"),
|
|
18699
18992
|
pattern: /\.jsonl$/
|
|
18700
18993
|
}
|
|
18701
18994
|
};
|
|
@@ -18762,7 +19055,7 @@ async function importSessions(args, flags) {
|
|
|
18762
19055
|
console.log(`
|
|
18763
19056
|
\u25B6 ${provider.name}`);
|
|
18764
19057
|
console.log(` Source: ${provider.baseDir}`);
|
|
18765
|
-
if (!(0,
|
|
19058
|
+
if (!(0, import_fs13.existsSync)(provider.baseDir)) {
|
|
18766
19059
|
console.log(` \u26A0 Directory not found, skipping`);
|
|
18767
19060
|
continue;
|
|
18768
19061
|
}
|
|
@@ -18805,14 +19098,14 @@ async function importSessions(args, flags) {
|
|
|
18805
19098
|
const now = Date.now();
|
|
18806
19099
|
const maxAgeMs = maxAgeDays ? maxAgeDays * 24 * 60 * 60 * 1e3 : null;
|
|
18807
19100
|
for (const filepath of files) {
|
|
18808
|
-
const sessionId = (0,
|
|
19101
|
+
const sessionId = (0, import_path13.basename)(filepath, ".jsonl");
|
|
18809
19102
|
if (existingIds.has(sessionId)) {
|
|
18810
19103
|
skipped.existing++;
|
|
18811
19104
|
continue;
|
|
18812
19105
|
}
|
|
18813
19106
|
let stat;
|
|
18814
19107
|
try {
|
|
18815
|
-
stat = (0,
|
|
19108
|
+
stat = (0, import_fs13.statSync)(filepath);
|
|
18816
19109
|
} catch (e) {
|
|
18817
19110
|
skipped.error++;
|
|
18818
19111
|
continue;
|
|
@@ -18910,7 +19203,7 @@ function showImportStatus(flags) {
|
|
|
18910
19203
|
}
|
|
18911
19204
|
console.log("\nProvider directories:");
|
|
18912
19205
|
for (const [key, provider] of Object.entries(PROVIDERS)) {
|
|
18913
|
-
const exists = (0,
|
|
19206
|
+
const exists = (0, import_fs13.existsSync)(provider.baseDir);
|
|
18914
19207
|
let count = 0;
|
|
18915
19208
|
if (exists) {
|
|
18916
19209
|
const files = findSessionFiles(provider.baseDir, provider.pattern);
|
|
@@ -18924,10 +19217,10 @@ function showImportStatus(flags) {
|
|
|
18924
19217
|
console.log("To import: rudi import sessions [provider]");
|
|
18925
19218
|
}
|
|
18926
19219
|
function findSessionFiles(dir, pattern, files = []) {
|
|
18927
|
-
if (!(0,
|
|
19220
|
+
if (!(0, import_fs13.existsSync)(dir)) return files;
|
|
18928
19221
|
try {
|
|
18929
|
-
for (const entry of (0,
|
|
18930
|
-
const fullPath = (0,
|
|
19222
|
+
for (const entry of (0, import_fs13.readdirSync)(dir, { withFileTypes: true })) {
|
|
19223
|
+
const fullPath = (0, import_path13.join)(dir, entry.name);
|
|
18931
19224
|
if (entry.isDirectory()) {
|
|
18932
19225
|
findSessionFiles(fullPath, pattern, files);
|
|
18933
19226
|
} else if (pattern.test(entry.name)) {
|
|
@@ -18940,11 +19233,11 @@ function findSessionFiles(dir, pattern, files = []) {
|
|
|
18940
19233
|
}
|
|
18941
19234
|
function parseSessionFile(filepath, provider) {
|
|
18942
19235
|
try {
|
|
18943
|
-
const stat = (0,
|
|
18944
|
-
const content = (0,
|
|
19236
|
+
const stat = (0, import_fs13.statSync)(filepath);
|
|
19237
|
+
const content = (0, import_fs13.readFileSync)(filepath, "utf-8");
|
|
18945
19238
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
18946
19239
|
if (lines.length === 0) return null;
|
|
18947
|
-
const sessionId = (0,
|
|
19240
|
+
const sessionId = (0, import_path13.basename)(filepath, ".jsonl");
|
|
18948
19241
|
const isAgent = sessionId.startsWith("agent-");
|
|
18949
19242
|
let title = null;
|
|
18950
19243
|
let cwd = null;
|
|
@@ -18976,11 +19269,11 @@ function parseSessionFile(filepath, provider) {
|
|
|
18976
19269
|
title = isAgent ? "Agent Session" : "Imported Session";
|
|
18977
19270
|
}
|
|
18978
19271
|
if (!cwd) {
|
|
18979
|
-
const parentDir = (0,
|
|
19272
|
+
const parentDir = (0, import_path13.basename)((0, import_path13.dirname)(filepath));
|
|
18980
19273
|
if (parentDir.startsWith("-")) {
|
|
18981
19274
|
cwd = parentDir.replace(/-/g, "/").replace(/^\//, "/");
|
|
18982
19275
|
} else {
|
|
18983
|
-
cwd = (0,
|
|
19276
|
+
cwd = (0, import_os4.homedir)();
|
|
18984
19277
|
}
|
|
18985
19278
|
}
|
|
18986
19279
|
return {
|
|
@@ -19000,7 +19293,7 @@ function parseSessionFile(filepath, provider) {
|
|
|
19000
19293
|
}
|
|
19001
19294
|
|
|
19002
19295
|
// src/commands/doctor.js
|
|
19003
|
-
var
|
|
19296
|
+
var import_fs14 = __toESM(require("fs"), 1);
|
|
19004
19297
|
async function cmdDoctor(args, flags) {
|
|
19005
19298
|
console.log("RUDI Health Check");
|
|
19006
19299
|
console.log("\u2550".repeat(50));
|
|
@@ -19018,12 +19311,12 @@ async function cmdDoctor(args, flags) {
|
|
|
19018
19311
|
{ path: PATHS.cache, name: "Cache" }
|
|
19019
19312
|
];
|
|
19020
19313
|
for (const dir of dirs) {
|
|
19021
|
-
const exists =
|
|
19314
|
+
const exists = import_fs14.default.existsSync(dir.path);
|
|
19022
19315
|
const status = exists ? "\u2713" : "\u2717";
|
|
19023
19316
|
console.log(` ${status} ${dir.name}: ${dir.path}`);
|
|
19024
19317
|
if (!exists) {
|
|
19025
19318
|
issues.push(`Missing directory: ${dir.name}`);
|
|
19026
|
-
fixes.push(() =>
|
|
19319
|
+
fixes.push(() => import_fs14.default.mkdirSync(dir.path, { recursive: true }));
|
|
19027
19320
|
}
|
|
19028
19321
|
}
|
|
19029
19322
|
console.log("\n\u{1F4BE} Database");
|
|
@@ -19123,8 +19416,8 @@ async function cmdDoctor(args, flags) {
|
|
|
19123
19416
|
}
|
|
19124
19417
|
|
|
19125
19418
|
// src/commands/home.js
|
|
19126
|
-
var
|
|
19127
|
-
var
|
|
19419
|
+
var import_fs15 = __toESM(require("fs"), 1);
|
|
19420
|
+
var import_path14 = __toESM(require("path"), 1);
|
|
19128
19421
|
function formatBytes2(bytes) {
|
|
19129
19422
|
if (bytes === 0) return "0 B";
|
|
19130
19423
|
const k = 1024;
|
|
@@ -19133,16 +19426,16 @@ function formatBytes2(bytes) {
|
|
|
19133
19426
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
|
|
19134
19427
|
}
|
|
19135
19428
|
function getDirSize(dir) {
|
|
19136
|
-
if (!
|
|
19429
|
+
if (!import_fs15.default.existsSync(dir)) return 0;
|
|
19137
19430
|
let size = 0;
|
|
19138
19431
|
try {
|
|
19139
|
-
const entries =
|
|
19432
|
+
const entries = import_fs15.default.readdirSync(dir, { withFileTypes: true });
|
|
19140
19433
|
for (const entry of entries) {
|
|
19141
|
-
const fullPath =
|
|
19434
|
+
const fullPath = import_path14.default.join(dir, entry.name);
|
|
19142
19435
|
if (entry.isDirectory()) {
|
|
19143
19436
|
size += getDirSize(fullPath);
|
|
19144
19437
|
} else {
|
|
19145
|
-
size +=
|
|
19438
|
+
size += import_fs15.default.statSync(fullPath).size;
|
|
19146
19439
|
}
|
|
19147
19440
|
}
|
|
19148
19441
|
} catch {
|
|
@@ -19150,9 +19443,9 @@ function getDirSize(dir) {
|
|
|
19150
19443
|
return size;
|
|
19151
19444
|
}
|
|
19152
19445
|
function countItems(dir) {
|
|
19153
|
-
if (!
|
|
19446
|
+
if (!import_fs15.default.existsSync(dir)) return 0;
|
|
19154
19447
|
try {
|
|
19155
|
-
return
|
|
19448
|
+
return import_fs15.default.readdirSync(dir).filter((f) => !f.startsWith(".")).length;
|
|
19156
19449
|
} catch {
|
|
19157
19450
|
return 0;
|
|
19158
19451
|
}
|
|
@@ -19179,7 +19472,7 @@ async function cmdHome(args, flags) {
|
|
|
19179
19472
|
for (const dir of dirs2) {
|
|
19180
19473
|
data.directories[dir.key] = {
|
|
19181
19474
|
path: dir.path,
|
|
19182
|
-
exists:
|
|
19475
|
+
exists: import_fs15.default.existsSync(dir.path),
|
|
19183
19476
|
items: countItems(dir.path),
|
|
19184
19477
|
size: getDirSize(dir.path)
|
|
19185
19478
|
};
|
|
@@ -19204,7 +19497,7 @@ async function cmdHome(args, flags) {
|
|
|
19204
19497
|
{ name: "cache", path: PATHS.cache, icon: "\u{1F4BE}", desc: "Registry cache" }
|
|
19205
19498
|
];
|
|
19206
19499
|
for (const dir of dirs) {
|
|
19207
|
-
const exists =
|
|
19500
|
+
const exists = import_fs15.default.existsSync(dir.path);
|
|
19208
19501
|
const count = countItems(dir.path);
|
|
19209
19502
|
const size = getDirSize(dir.path);
|
|
19210
19503
|
console.log(`${dir.icon} ${dir.name}/`);
|
|
@@ -19214,52 +19507,299 @@ async function cmdHome(args, flags) {
|
|
|
19214
19507
|
} else {
|
|
19215
19508
|
console.log(` (not created)`);
|
|
19216
19509
|
}
|
|
19217
|
-
console.log();
|
|
19510
|
+
console.log();
|
|
19511
|
+
}
|
|
19512
|
+
console.log("\u{1F4BE} Database");
|
|
19513
|
+
const dbPath = import_path14.default.join(PATHS.home, "rudi.db");
|
|
19514
|
+
if (import_fs15.default.existsSync(dbPath)) {
|
|
19515
|
+
const dbSize = getDbSize() || import_fs15.default.statSync(dbPath).size;
|
|
19516
|
+
console.log(` ${formatBytes2(dbSize)}`);
|
|
19517
|
+
console.log(` ${dbPath}`);
|
|
19518
|
+
} else {
|
|
19519
|
+
console.log(` Not initialized`);
|
|
19520
|
+
}
|
|
19521
|
+
console.log();
|
|
19522
|
+
console.log("\u2550".repeat(60));
|
|
19523
|
+
console.log("Installed Packages");
|
|
19524
|
+
console.log("\u2550".repeat(60));
|
|
19525
|
+
const kinds = ["stack", "prompt", "runtime", "binary", "agent"];
|
|
19526
|
+
let total = 0;
|
|
19527
|
+
for (const kind of kinds) {
|
|
19528
|
+
const packages = getInstalledPackages(kind);
|
|
19529
|
+
const label = kind === "binary" ? "Binaries" : `${kind.charAt(0).toUpperCase() + kind.slice(1)}s`;
|
|
19530
|
+
console.log(` ${label.padEnd(12)} ${packages.length}`);
|
|
19531
|
+
if (packages.length > 0 && flags.verbose) {
|
|
19532
|
+
for (const pkg of packages.slice(0, 3)) {
|
|
19533
|
+
console.log(` - ${pkg.name || pkg.id}`);
|
|
19534
|
+
}
|
|
19535
|
+
if (packages.length > 3) {
|
|
19536
|
+
console.log(` ... and ${packages.length - 3} more`);
|
|
19537
|
+
}
|
|
19538
|
+
}
|
|
19539
|
+
total += packages.length;
|
|
19540
|
+
}
|
|
19541
|
+
console.log("\u2500".repeat(30));
|
|
19542
|
+
console.log(` ${"Total".padEnd(12)} ${total}`);
|
|
19543
|
+
console.log("\n\u{1F4CB} Quick Commands");
|
|
19544
|
+
console.log("\u2500".repeat(30));
|
|
19545
|
+
console.log(" rudi list stacks Show installed stacks");
|
|
19546
|
+
console.log(" rudi list runtimes Show installed runtimes");
|
|
19547
|
+
console.log(" rudi list binaries Show installed binaries");
|
|
19548
|
+
console.log(" rudi doctor --all Check system dependencies");
|
|
19549
|
+
console.log(" rudi db stats Database statistics");
|
|
19550
|
+
}
|
|
19551
|
+
|
|
19552
|
+
// src/commands/init.js
|
|
19553
|
+
var import_fs16 = __toESM(require("fs"), 1);
|
|
19554
|
+
var import_path15 = __toESM(require("path"), 1);
|
|
19555
|
+
var import_promises = require("stream/promises");
|
|
19556
|
+
var import_fs17 = require("fs");
|
|
19557
|
+
var import_child_process4 = require("child_process");
|
|
19558
|
+
init_src();
|
|
19559
|
+
init_src2();
|
|
19560
|
+
var RELEASES_BASE = "https://github.com/learn-rudi/registry/releases/download/v1.0.0";
|
|
19561
|
+
var BUNDLED_RUNTIMES = ["node", "python"];
|
|
19562
|
+
var ESSENTIAL_BINARIES = ["sqlite", "ripgrep"];
|
|
19563
|
+
async function cmdInit(args, flags) {
|
|
19564
|
+
const force = flags.force || false;
|
|
19565
|
+
const skipDownloads = flags["skip-downloads"] || false;
|
|
19566
|
+
const quiet = flags.quiet || false;
|
|
19567
|
+
if (!quiet) {
|
|
19568
|
+
console.log("\u2550".repeat(60));
|
|
19569
|
+
console.log("RUDI Initialization");
|
|
19570
|
+
console.log("\u2550".repeat(60));
|
|
19571
|
+
console.log(`Home: ${PATHS.home}`);
|
|
19572
|
+
console.log();
|
|
19573
|
+
}
|
|
19574
|
+
const actions = { created: [], skipped: [], failed: [] };
|
|
19575
|
+
if (!quiet) console.log("1. Checking directory structure...");
|
|
19576
|
+
ensureDirectories();
|
|
19577
|
+
const dirs = [
|
|
19578
|
+
PATHS.stacks,
|
|
19579
|
+
PATHS.prompts,
|
|
19580
|
+
PATHS.runtimes,
|
|
19581
|
+
PATHS.binaries,
|
|
19582
|
+
PATHS.agents,
|
|
19583
|
+
PATHS.cache,
|
|
19584
|
+
import_path15.default.join(PATHS.home, "shims")
|
|
19585
|
+
];
|
|
19586
|
+
for (const dir of dirs) {
|
|
19587
|
+
const dirName = import_path15.default.basename(dir);
|
|
19588
|
+
if (!import_fs16.default.existsSync(dir)) {
|
|
19589
|
+
import_fs16.default.mkdirSync(dir, { recursive: true });
|
|
19590
|
+
actions.created.push(`dir:${dirName}`);
|
|
19591
|
+
if (!quiet) console.log(` + ${dirName}/ (created)`);
|
|
19592
|
+
} else {
|
|
19593
|
+
actions.skipped.push(`dir:${dirName}`);
|
|
19594
|
+
if (!quiet) console.log(` \u2713 ${dirName}/ (exists)`);
|
|
19595
|
+
}
|
|
19596
|
+
}
|
|
19597
|
+
if (!skipDownloads) {
|
|
19598
|
+
if (!quiet) console.log("\n2. Checking runtimes...");
|
|
19599
|
+
const index = await fetchIndex();
|
|
19600
|
+
const platform = getPlatformArch();
|
|
19601
|
+
for (const runtimeName of BUNDLED_RUNTIMES) {
|
|
19602
|
+
const runtime = index.packages?.runtimes?.official?.find(
|
|
19603
|
+
(r) => r.id === `runtime:${runtimeName}` || r.id === runtimeName
|
|
19604
|
+
);
|
|
19605
|
+
if (!runtime) {
|
|
19606
|
+
actions.failed.push(`runtime:${runtimeName}`);
|
|
19607
|
+
if (!quiet) console.log(` \u26A0 ${runtimeName}: not found in registry`);
|
|
19608
|
+
continue;
|
|
19609
|
+
}
|
|
19610
|
+
const destPath = import_path15.default.join(PATHS.runtimes, runtimeName);
|
|
19611
|
+
if (import_fs16.default.existsSync(destPath) && !force) {
|
|
19612
|
+
actions.skipped.push(`runtime:${runtimeName}`);
|
|
19613
|
+
if (!quiet) console.log(` \u2713 ${runtimeName}: already installed`);
|
|
19614
|
+
continue;
|
|
19615
|
+
}
|
|
19616
|
+
try {
|
|
19617
|
+
await downloadRuntime2(runtime, runtimeName, destPath, platform);
|
|
19618
|
+
actions.created.push(`runtime:${runtimeName}`);
|
|
19619
|
+
if (!quiet) console.log(` + ${runtimeName}: installed`);
|
|
19620
|
+
} catch (error) {
|
|
19621
|
+
actions.failed.push(`runtime:${runtimeName}`);
|
|
19622
|
+
if (!quiet) console.log(` \u2717 ${runtimeName}: ${error.message}`);
|
|
19623
|
+
}
|
|
19624
|
+
}
|
|
19625
|
+
if (!quiet) console.log("\n3. Checking essential binaries...");
|
|
19626
|
+
for (const binaryName of ESSENTIAL_BINARIES) {
|
|
19627
|
+
const binary = index.packages?.binaries?.official?.find(
|
|
19628
|
+
(b) => b.id === `binary:${binaryName}` || b.id === binaryName || b.name?.toLowerCase() === binaryName
|
|
19629
|
+
);
|
|
19630
|
+
if (!binary) {
|
|
19631
|
+
actions.failed.push(`binary:${binaryName}`);
|
|
19632
|
+
if (!quiet) console.log(` \u26A0 ${binaryName}: not found in registry`);
|
|
19633
|
+
continue;
|
|
19634
|
+
}
|
|
19635
|
+
const destPath = import_path15.default.join(PATHS.binaries, binaryName);
|
|
19636
|
+
if (import_fs16.default.existsSync(destPath) && !force) {
|
|
19637
|
+
actions.skipped.push(`binary:${binaryName}`);
|
|
19638
|
+
if (!quiet) console.log(` \u2713 ${binaryName}: already installed`);
|
|
19639
|
+
continue;
|
|
19640
|
+
}
|
|
19641
|
+
try {
|
|
19642
|
+
await downloadBinary(binary, binaryName, destPath, platform);
|
|
19643
|
+
actions.created.push(`binary:${binaryName}`);
|
|
19644
|
+
if (!quiet) console.log(` + ${binaryName}: installed`);
|
|
19645
|
+
} catch (error) {
|
|
19646
|
+
actions.failed.push(`binary:${binaryName}`);
|
|
19647
|
+
if (!quiet) console.log(` \u2717 ${binaryName}: ${error.message}`);
|
|
19648
|
+
}
|
|
19649
|
+
}
|
|
19650
|
+
} else {
|
|
19651
|
+
if (!quiet) console.log("\n2-3. Skipping downloads (--skip-downloads)");
|
|
19652
|
+
}
|
|
19653
|
+
if (!quiet) console.log("\n4. Updating shims...");
|
|
19654
|
+
const shimsDir = import_path15.default.join(PATHS.home, "shims");
|
|
19655
|
+
const shimCount = await createShims(shimsDir, quiet);
|
|
19656
|
+
if (shimCount > 0) {
|
|
19657
|
+
actions.created.push(`shims:${shimCount}`);
|
|
19658
|
+
}
|
|
19659
|
+
if (!quiet) console.log("\n5. Checking database...");
|
|
19660
|
+
const dbPath = import_path15.default.join(PATHS.home, "rudi.db");
|
|
19661
|
+
const dbExists = import_fs16.default.existsSync(dbPath);
|
|
19662
|
+
try {
|
|
19663
|
+
const result = initSchema();
|
|
19664
|
+
if (dbExists) {
|
|
19665
|
+
actions.skipped.push("database");
|
|
19666
|
+
if (!quiet) console.log(` \u2713 Database exists (v${result.version})`);
|
|
19667
|
+
} else {
|
|
19668
|
+
actions.created.push("database");
|
|
19669
|
+
if (!quiet) console.log(` + Database created (v${result.version})`);
|
|
19670
|
+
}
|
|
19671
|
+
} catch (error) {
|
|
19672
|
+
actions.failed.push("database");
|
|
19673
|
+
if (!quiet) console.log(` \u2717 Database error: ${error.message}`);
|
|
19674
|
+
}
|
|
19675
|
+
if (!quiet) console.log("\n6. Checking settings...");
|
|
19676
|
+
const settingsPath = import_path15.default.join(PATHS.home, "settings.json");
|
|
19677
|
+
if (!import_fs16.default.existsSync(settingsPath)) {
|
|
19678
|
+
const settings = {
|
|
19679
|
+
version: "1.0.0",
|
|
19680
|
+
initialized: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19681
|
+
theme: "system"
|
|
19682
|
+
};
|
|
19683
|
+
import_fs16.default.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
19684
|
+
actions.created.push("settings");
|
|
19685
|
+
if (!quiet) console.log(" + settings.json created");
|
|
19686
|
+
} else {
|
|
19687
|
+
actions.skipped.push("settings");
|
|
19688
|
+
if (!quiet) console.log(" \u2713 settings.json exists");
|
|
19689
|
+
}
|
|
19690
|
+
if (!quiet) {
|
|
19691
|
+
console.log("\n" + "\u2550".repeat(60));
|
|
19692
|
+
if (actions.created.length > 0) {
|
|
19693
|
+
console.log(`\u2713 RUDI initialized! (${actions.created.length} items created, ${actions.skipped.length} already existed)`);
|
|
19694
|
+
} else {
|
|
19695
|
+
console.log("\u2713 RUDI is up to date! (all items already existed)");
|
|
19696
|
+
}
|
|
19697
|
+
console.log("\u2550".repeat(60));
|
|
19698
|
+
if (actions.created.includes("settings")) {
|
|
19699
|
+
const shimsPath = import_path15.default.join(PATHS.home, "shims");
|
|
19700
|
+
console.log("\nAdd to your shell profile (~/.zshrc or ~/.bashrc):");
|
|
19701
|
+
console.log(` export PATH="${shimsPath}:$PATH"`);
|
|
19702
|
+
console.log("\nThen run:");
|
|
19703
|
+
console.log(" rudi home # View your setup");
|
|
19704
|
+
console.log(" rudi doctor # Check health");
|
|
19705
|
+
}
|
|
19706
|
+
}
|
|
19707
|
+
return actions;
|
|
19708
|
+
}
|
|
19709
|
+
async function downloadRuntime2(runtime, name, destPath, platform) {
|
|
19710
|
+
let url;
|
|
19711
|
+
if (runtime.upstream?.[platform]) {
|
|
19712
|
+
url = runtime.upstream[platform];
|
|
19713
|
+
} else if (runtime.download?.[platform]) {
|
|
19714
|
+
url = `${RELEASES_BASE}/${runtime.download[platform]}`;
|
|
19715
|
+
} else {
|
|
19716
|
+
throw new Error(`No download for ${platform}`);
|
|
19717
|
+
}
|
|
19718
|
+
await downloadAndExtract(url, destPath, name);
|
|
19719
|
+
}
|
|
19720
|
+
async function downloadBinary(binary, name, destPath, platform) {
|
|
19721
|
+
let url;
|
|
19722
|
+
if (binary.upstream?.[platform]) {
|
|
19723
|
+
url = binary.upstream[platform];
|
|
19724
|
+
} else if (binary.download?.[platform]) {
|
|
19725
|
+
url = `${RELEASES_BASE}/${binary.download[platform]}`;
|
|
19726
|
+
} else {
|
|
19727
|
+
throw new Error(`No download for ${platform}`);
|
|
19728
|
+
}
|
|
19729
|
+
await downloadAndExtract(url, destPath, name, binary.extract);
|
|
19730
|
+
}
|
|
19731
|
+
async function downloadAndExtract(url, destPath, name, extractConfig) {
|
|
19732
|
+
const tempFile = import_path15.default.join(PATHS.cache, `${name}-download.tar.gz`);
|
|
19733
|
+
const response = await fetch(url);
|
|
19734
|
+
if (!response.ok) {
|
|
19735
|
+
throw new Error(`HTTP ${response.status}`);
|
|
19736
|
+
}
|
|
19737
|
+
if (!import_fs16.default.existsSync(destPath)) {
|
|
19738
|
+
import_fs16.default.mkdirSync(destPath, { recursive: true });
|
|
19739
|
+
}
|
|
19740
|
+
const fileStream = (0, import_fs17.createWriteStream)(tempFile);
|
|
19741
|
+
await (0, import_promises.pipeline)(response.body, fileStream);
|
|
19742
|
+
try {
|
|
19743
|
+
(0, import_child_process4.execSync)(`tar -xzf "${tempFile}" -C "${destPath}" --strip-components=1`, {
|
|
19744
|
+
stdio: "pipe"
|
|
19745
|
+
});
|
|
19746
|
+
} catch {
|
|
19747
|
+
(0, import_child_process4.execSync)(`tar -xzf "${tempFile}" -C "${destPath}"`, { stdio: "pipe" });
|
|
19748
|
+
}
|
|
19749
|
+
import_fs16.default.unlinkSync(tempFile);
|
|
19750
|
+
}
|
|
19751
|
+
async function createShims(shimsDir, quiet = false) {
|
|
19752
|
+
const shims = [];
|
|
19753
|
+
const runtimeShims = {
|
|
19754
|
+
node: "runtimes/node/bin/node",
|
|
19755
|
+
npm: "runtimes/node/bin/npm",
|
|
19756
|
+
npx: "runtimes/node/bin/npx",
|
|
19757
|
+
python: "runtimes/python/bin/python3",
|
|
19758
|
+
python3: "runtimes/python/bin/python3",
|
|
19759
|
+
pip: "runtimes/python/bin/pip3",
|
|
19760
|
+
pip3: "runtimes/python/bin/pip3"
|
|
19761
|
+
};
|
|
19762
|
+
const binaryShims = {
|
|
19763
|
+
sqlite3: "binaries/sqlite/sqlite3",
|
|
19764
|
+
rg: "binaries/ripgrep/rg",
|
|
19765
|
+
ripgrep: "binaries/ripgrep/rg"
|
|
19766
|
+
};
|
|
19767
|
+
for (const [shimName, targetPath] of Object.entries(runtimeShims)) {
|
|
19768
|
+
const fullTarget = import_path15.default.join(PATHS.home, targetPath);
|
|
19769
|
+
const shimPath = import_path15.default.join(shimsDir, shimName);
|
|
19770
|
+
if (import_fs16.default.existsSync(fullTarget)) {
|
|
19771
|
+
createShim(shimPath, fullTarget);
|
|
19772
|
+
shims.push(shimName);
|
|
19773
|
+
}
|
|
19218
19774
|
}
|
|
19219
|
-
|
|
19220
|
-
|
|
19221
|
-
|
|
19222
|
-
|
|
19223
|
-
|
|
19224
|
-
|
|
19225
|
-
|
|
19226
|
-
console.log(` Not initialized`);
|
|
19775
|
+
for (const [shimName, targetPath] of Object.entries(binaryShims)) {
|
|
19776
|
+
const fullTarget = import_path15.default.join(PATHS.home, targetPath);
|
|
19777
|
+
const shimPath = import_path15.default.join(shimsDir, shimName);
|
|
19778
|
+
if (import_fs16.default.existsSync(fullTarget)) {
|
|
19779
|
+
createShim(shimPath, fullTarget);
|
|
19780
|
+
shims.push(shimName);
|
|
19781
|
+
}
|
|
19227
19782
|
}
|
|
19228
|
-
|
|
19229
|
-
|
|
19230
|
-
|
|
19231
|
-
|
|
19232
|
-
|
|
19233
|
-
let total = 0;
|
|
19234
|
-
for (const kind of kinds) {
|
|
19235
|
-
const packages = getInstalledPackages(kind);
|
|
19236
|
-
const label = kind === "binary" ? "Binaries" : `${kind.charAt(0).toUpperCase() + kind.slice(1)}s`;
|
|
19237
|
-
console.log(` ${label.padEnd(12)} ${packages.length}`);
|
|
19238
|
-
if (packages.length > 0 && flags.verbose) {
|
|
19239
|
-
for (const pkg of packages.slice(0, 3)) {
|
|
19240
|
-
console.log(` - ${pkg.name || pkg.id}`);
|
|
19241
|
-
}
|
|
19242
|
-
if (packages.length > 3) {
|
|
19243
|
-
console.log(` ... and ${packages.length - 3} more`);
|
|
19244
|
-
}
|
|
19783
|
+
if (!quiet) {
|
|
19784
|
+
if (shims.length > 0) {
|
|
19785
|
+
console.log(` \u2713 ${shims.length} shims: ${shims.join(", ")}`);
|
|
19786
|
+
} else {
|
|
19787
|
+
console.log(" \u26A0 No shims (runtimes/binaries not installed)");
|
|
19245
19788
|
}
|
|
19246
|
-
total += packages.length;
|
|
19247
19789
|
}
|
|
19248
|
-
|
|
19249
|
-
|
|
19250
|
-
|
|
19251
|
-
|
|
19252
|
-
|
|
19253
|
-
|
|
19254
|
-
|
|
19255
|
-
console.log(" rudi doctor --all Check system dependencies");
|
|
19256
|
-
console.log(" rudi db stats Database statistics");
|
|
19790
|
+
return shims.length;
|
|
19791
|
+
}
|
|
19792
|
+
function createShim(shimPath, targetPath) {
|
|
19793
|
+
if (import_fs16.default.existsSync(shimPath)) {
|
|
19794
|
+
import_fs16.default.unlinkSync(shimPath);
|
|
19795
|
+
}
|
|
19796
|
+
import_fs16.default.symlinkSync(targetPath, shimPath);
|
|
19257
19797
|
}
|
|
19258
19798
|
|
|
19259
19799
|
// src/commands/update.js
|
|
19260
|
-
var
|
|
19261
|
-
var
|
|
19262
|
-
var
|
|
19800
|
+
var import_fs18 = __toESM(require("fs"), 1);
|
|
19801
|
+
var import_path16 = __toESM(require("path"), 1);
|
|
19802
|
+
var import_child_process5 = require("child_process");
|
|
19263
19803
|
init_src();
|
|
19264
19804
|
init_src2();
|
|
19265
19805
|
async function cmdUpdate(args, flags) {
|
|
@@ -19284,7 +19824,7 @@ async function cmdUpdate(args, flags) {
|
|
|
19284
19824
|
async function updatePackage(pkgId, flags) {
|
|
19285
19825
|
const [kind, name] = parsePackageId(pkgId);
|
|
19286
19826
|
const installPath = getPackagePath(pkgId);
|
|
19287
|
-
if (!
|
|
19827
|
+
if (!import_fs18.default.existsSync(installPath)) {
|
|
19288
19828
|
return { success: false, error: "Package not installed" };
|
|
19289
19829
|
}
|
|
19290
19830
|
const pkg = await getPackage(pkgId);
|
|
@@ -19294,7 +19834,7 @@ async function updatePackage(pkgId, flags) {
|
|
|
19294
19834
|
console.log(`Updating ${pkgId}...`);
|
|
19295
19835
|
if (pkg.npmPackage) {
|
|
19296
19836
|
try {
|
|
19297
|
-
(0,
|
|
19837
|
+
(0, import_child_process5.execSync)(`npm install ${pkg.npmPackage}@latest`, {
|
|
19298
19838
|
cwd: installPath,
|
|
19299
19839
|
stdio: flags.verbose ? "inherit" : "pipe"
|
|
19300
19840
|
});
|
|
@@ -19307,11 +19847,11 @@ async function updatePackage(pkgId, flags) {
|
|
|
19307
19847
|
}
|
|
19308
19848
|
if (pkg.pipPackage) {
|
|
19309
19849
|
try {
|
|
19310
|
-
const venvPip =
|
|
19311
|
-
(0,
|
|
19850
|
+
const venvPip = import_path16.default.join(installPath, "venv", "bin", "pip");
|
|
19851
|
+
(0, import_child_process5.execSync)(`"${venvPip}" install --upgrade ${pkg.pipPackage}`, {
|
|
19312
19852
|
stdio: flags.verbose ? "inherit" : "pipe"
|
|
19313
19853
|
});
|
|
19314
|
-
const versionOutput = (0,
|
|
19854
|
+
const versionOutput = (0, import_child_process5.execSync)(`"${venvPip}" show ${pkg.pipPackage} | grep Version`, {
|
|
19315
19855
|
encoding: "utf-8"
|
|
19316
19856
|
});
|
|
19317
19857
|
const version = versionOutput.split(":")[1]?.trim();
|
|
@@ -19323,9 +19863,9 @@ async function updatePackage(pkgId, flags) {
|
|
|
19323
19863
|
}
|
|
19324
19864
|
if (kind === "runtime" && !pkg.npmPackage && !pkg.pipPackage) {
|
|
19325
19865
|
try {
|
|
19326
|
-
const { downloadRuntime:
|
|
19327
|
-
|
|
19328
|
-
await
|
|
19866
|
+
const { downloadRuntime: downloadRuntime3 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
19867
|
+
import_fs18.default.rmSync(installPath, { recursive: true, force: true });
|
|
19868
|
+
await downloadRuntime3(name, pkg.version || "latest", installPath, {
|
|
19329
19869
|
onProgress: (p) => {
|
|
19330
19870
|
if (flags.verbose) console.log(` ${p.phase}...`);
|
|
19331
19871
|
}
|
|
@@ -19344,8 +19884,8 @@ async function updateAll(flags) {
|
|
|
19344
19884
|
let failed = 0;
|
|
19345
19885
|
for (const kind of kinds) {
|
|
19346
19886
|
const dir = kind === "runtime" ? PATHS.runtimes : kind === "stack" ? PATHS.stacks : PATHS.prompts;
|
|
19347
|
-
if (!
|
|
19348
|
-
const entries =
|
|
19887
|
+
if (!import_fs18.default.existsSync(dir)) continue;
|
|
19888
|
+
const entries = import_fs18.default.readdirSync(dir, { withFileTypes: true });
|
|
19349
19889
|
for (const entry of entries) {
|
|
19350
19890
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
19351
19891
|
const pkgId = `${kind}:${entry.name}`;
|
|
@@ -19364,14 +19904,14 @@ Updated ${updated} package(s)${failed > 0 ? `, ${failed} failed` : ""}`);
|
|
|
19364
19904
|
}
|
|
19365
19905
|
function getInstalledVersion(installPath, npmPackage) {
|
|
19366
19906
|
try {
|
|
19367
|
-
const pkgJsonPath =
|
|
19368
|
-
if (
|
|
19369
|
-
const pkgJson = JSON.parse(
|
|
19907
|
+
const pkgJsonPath = import_path16.default.join(installPath, "node_modules", npmPackage.replace("@", "").split("/")[0], "package.json");
|
|
19908
|
+
if (import_fs18.default.existsSync(pkgJsonPath)) {
|
|
19909
|
+
const pkgJson = JSON.parse(import_fs18.default.readFileSync(pkgJsonPath, "utf-8"));
|
|
19370
19910
|
return pkgJson.version;
|
|
19371
19911
|
}
|
|
19372
|
-
const rootPkgPath =
|
|
19373
|
-
if (
|
|
19374
|
-
const rootPkg = JSON.parse(
|
|
19912
|
+
const rootPkgPath = import_path16.default.join(installPath, "package.json");
|
|
19913
|
+
if (import_fs18.default.existsSync(rootPkgPath)) {
|
|
19914
|
+
const rootPkg = JSON.parse(import_fs18.default.readFileSync(rootPkgPath, "utf-8"));
|
|
19375
19915
|
const dep = rootPkg.dependencies?.[npmPackage];
|
|
19376
19916
|
if (dep) return dep.replace(/[\^~]/, "");
|
|
19377
19917
|
}
|
|
@@ -19380,30 +19920,30 @@ function getInstalledVersion(installPath, npmPackage) {
|
|
|
19380
19920
|
return null;
|
|
19381
19921
|
}
|
|
19382
19922
|
function updateRuntimeMetadata(installPath, updates) {
|
|
19383
|
-
const metaPath =
|
|
19923
|
+
const metaPath = import_path16.default.join(installPath, "runtime.json");
|
|
19384
19924
|
try {
|
|
19385
19925
|
let meta = {};
|
|
19386
|
-
if (
|
|
19387
|
-
meta = JSON.parse(
|
|
19926
|
+
if (import_fs18.default.existsSync(metaPath)) {
|
|
19927
|
+
meta = JSON.parse(import_fs18.default.readFileSync(metaPath, "utf-8"));
|
|
19388
19928
|
}
|
|
19389
19929
|
meta = { ...meta, ...updates };
|
|
19390
|
-
|
|
19930
|
+
import_fs18.default.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
19391
19931
|
} catch {
|
|
19392
19932
|
}
|
|
19393
19933
|
}
|
|
19394
19934
|
|
|
19395
19935
|
// db/index.js
|
|
19396
19936
|
var import_better_sqlite32 = __toESM(require("better-sqlite3"), 1);
|
|
19397
|
-
var
|
|
19398
|
-
var
|
|
19399
|
-
var
|
|
19400
|
-
var RUDI_HOME2 =
|
|
19401
|
-
var DB_PATH2 =
|
|
19937
|
+
var import_path17 = __toESM(require("path"), 1);
|
|
19938
|
+
var import_os5 = __toESM(require("os"), 1);
|
|
19939
|
+
var import_fs19 = __toESM(require("fs"), 1);
|
|
19940
|
+
var RUDI_HOME2 = import_path17.default.join(import_os5.default.homedir(), ".rudi");
|
|
19941
|
+
var DB_PATH2 = import_path17.default.join(RUDI_HOME2, "rudi.db");
|
|
19402
19942
|
var db2 = null;
|
|
19403
19943
|
function getDb2(options = {}) {
|
|
19404
19944
|
if (!db2) {
|
|
19405
|
-
if (!
|
|
19406
|
-
|
|
19945
|
+
if (!import_fs19.default.existsSync(RUDI_HOME2)) {
|
|
19946
|
+
import_fs19.default.mkdirSync(RUDI_HOME2, { recursive: true });
|
|
19407
19947
|
}
|
|
19408
19948
|
db2 = new import_better_sqlite32.default(DB_PATH2, {
|
|
19409
19949
|
readonly: options.readonly || false
|
|
@@ -19561,7 +20101,7 @@ function getBeforeCrashLogs() {
|
|
|
19561
20101
|
}
|
|
19562
20102
|
|
|
19563
20103
|
// src/commands/logs.js
|
|
19564
|
-
var
|
|
20104
|
+
var import_fs20 = __toESM(require("fs"), 1);
|
|
19565
20105
|
function parseTimeAgo(str) {
|
|
19566
20106
|
const match = str.match(/^(\d+)([smhd])$/);
|
|
19567
20107
|
if (!match) return null;
|
|
@@ -19661,7 +20201,7 @@ function exportLogs(logs, filepath, format) {
|
|
|
19661
20201
|
});
|
|
19662
20202
|
content = JSON.stringify(formatted, null, 2);
|
|
19663
20203
|
}
|
|
19664
|
-
|
|
20204
|
+
import_fs20.default.writeFileSync(filepath, content, "utf-8");
|
|
19665
20205
|
return filepath;
|
|
19666
20206
|
}
|
|
19667
20207
|
function printStats(stats) {
|
|
@@ -19787,9 +20327,9 @@ async function handleLogsCommand(args, flags) {
|
|
|
19787
20327
|
}
|
|
19788
20328
|
|
|
19789
20329
|
// src/commands/which.js
|
|
19790
|
-
var
|
|
19791
|
-
var
|
|
19792
|
-
var
|
|
20330
|
+
var fs21 = __toESM(require("fs/promises"), 1);
|
|
20331
|
+
var path19 = __toESM(require("path"), 1);
|
|
20332
|
+
var import_child_process6 = require("child_process");
|
|
19793
20333
|
init_src();
|
|
19794
20334
|
async function cmdWhich(args, flags) {
|
|
19795
20335
|
const stackId = args[0];
|
|
@@ -19857,7 +20397,7 @@ Installed stacks:`);
|
|
|
19857
20397
|
if (runtimeInfo.entry) {
|
|
19858
20398
|
console.log("");
|
|
19859
20399
|
console.log("Run MCP server directly:");
|
|
19860
|
-
const entryPath =
|
|
20400
|
+
const entryPath = path19.join(stackPath, runtimeInfo.entry);
|
|
19861
20401
|
if (runtimeInfo.runtime === "node") {
|
|
19862
20402
|
console.log(` echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node ${entryPath}`);
|
|
19863
20403
|
} else if (runtimeInfo.runtime === "python") {
|
|
@@ -19876,27 +20416,27 @@ Installed stacks:`);
|
|
|
19876
20416
|
async function detectRuntime(stackPath) {
|
|
19877
20417
|
const runtimes = ["node", "python"];
|
|
19878
20418
|
for (const runtime of runtimes) {
|
|
19879
|
-
const runtimePath =
|
|
20419
|
+
const runtimePath = path19.join(stackPath, runtime);
|
|
19880
20420
|
try {
|
|
19881
|
-
await
|
|
20421
|
+
await fs21.access(runtimePath);
|
|
19882
20422
|
if (runtime === "node") {
|
|
19883
|
-
const distEntry =
|
|
19884
|
-
const srcEntry =
|
|
20423
|
+
const distEntry = path19.join(runtimePath, "dist", "index.js");
|
|
20424
|
+
const srcEntry = path19.join(runtimePath, "src", "index.ts");
|
|
19885
20425
|
try {
|
|
19886
|
-
await
|
|
20426
|
+
await fs21.access(distEntry);
|
|
19887
20427
|
return { runtime: "node", entry: `${runtime}/dist/index.js` };
|
|
19888
20428
|
} catch {
|
|
19889
20429
|
try {
|
|
19890
|
-
await
|
|
20430
|
+
await fs21.access(srcEntry);
|
|
19891
20431
|
return { runtime: "node", entry: `${runtime}/src/index.ts` };
|
|
19892
20432
|
} catch {
|
|
19893
20433
|
return { runtime: "node", entry: null };
|
|
19894
20434
|
}
|
|
19895
20435
|
}
|
|
19896
20436
|
} else if (runtime === "python") {
|
|
19897
|
-
const entry =
|
|
20437
|
+
const entry = path19.join(runtimePath, "src", "index.py");
|
|
19898
20438
|
try {
|
|
19899
|
-
await
|
|
20439
|
+
await fs21.access(entry);
|
|
19900
20440
|
return { runtime: "python", entry: `${runtime}/src/index.py` };
|
|
19901
20441
|
} catch {
|
|
19902
20442
|
return { runtime: "python", entry: null };
|
|
@@ -19912,21 +20452,21 @@ async function checkAuth(stackPath, runtime) {
|
|
|
19912
20452
|
const authFiles = [];
|
|
19913
20453
|
let configured = false;
|
|
19914
20454
|
if (runtime === "node" || runtime === "python") {
|
|
19915
|
-
const runtimePath =
|
|
19916
|
-
const tokenPath =
|
|
20455
|
+
const runtimePath = path19.join(stackPath, runtime);
|
|
20456
|
+
const tokenPath = path19.join(runtimePath, "token.json");
|
|
19917
20457
|
try {
|
|
19918
|
-
await
|
|
20458
|
+
await fs21.access(tokenPath);
|
|
19919
20459
|
authFiles.push(`${runtime}/token.json`);
|
|
19920
20460
|
configured = true;
|
|
19921
20461
|
} catch {
|
|
19922
|
-
const accountsPath =
|
|
20462
|
+
const accountsPath = path19.join(runtimePath, "accounts");
|
|
19923
20463
|
try {
|
|
19924
|
-
const accounts = await
|
|
20464
|
+
const accounts = await fs21.readdir(accountsPath);
|
|
19925
20465
|
for (const account of accounts) {
|
|
19926
20466
|
if (account.startsWith(".")) continue;
|
|
19927
|
-
const accountTokenPath =
|
|
20467
|
+
const accountTokenPath = path19.join(accountsPath, account, "token.json");
|
|
19928
20468
|
try {
|
|
19929
|
-
await
|
|
20469
|
+
await fs21.access(accountTokenPath);
|
|
19930
20470
|
authFiles.push(`${runtime}/accounts/${account}/token.json`);
|
|
19931
20471
|
configured = true;
|
|
19932
20472
|
} catch {
|
|
@@ -19936,9 +20476,9 @@ async function checkAuth(stackPath, runtime) {
|
|
|
19936
20476
|
}
|
|
19937
20477
|
}
|
|
19938
20478
|
}
|
|
19939
|
-
const envPath =
|
|
20479
|
+
const envPath = path19.join(stackPath, ".env");
|
|
19940
20480
|
try {
|
|
19941
|
-
const envContent = await
|
|
20481
|
+
const envContent = await fs21.readFile(envPath, "utf-8");
|
|
19942
20482
|
const hasValues = envContent.split("\n").some((line) => {
|
|
19943
20483
|
const trimmed = line.trim();
|
|
19944
20484
|
if (!trimmed || trimmed.startsWith("#")) return false;
|
|
@@ -19967,7 +20507,7 @@ async function checkAuth(stackPath, runtime) {
|
|
|
19967
20507
|
}
|
|
19968
20508
|
function checkIfRunning(stackName) {
|
|
19969
20509
|
try {
|
|
19970
|
-
const result = (0,
|
|
20510
|
+
const result = (0, import_child_process6.execSync)(`ps aux | grep "${stackName}" | grep -v grep || true`, {
|
|
19971
20511
|
encoding: "utf-8",
|
|
19972
20512
|
stdio: ["pipe", "pipe", "ignore"]
|
|
19973
20513
|
// Suppress stderr
|
|
@@ -19982,9 +20522,9 @@ function checkIfRunning(stackName) {
|
|
|
19982
20522
|
}
|
|
19983
20523
|
|
|
19984
20524
|
// src/commands/auth.js
|
|
19985
|
-
var
|
|
19986
|
-
var
|
|
19987
|
-
var
|
|
20525
|
+
var fs22 = __toESM(require("fs/promises"), 1);
|
|
20526
|
+
var path20 = __toESM(require("path"), 1);
|
|
20527
|
+
var import_child_process7 = require("child_process");
|
|
19988
20528
|
init_src();
|
|
19989
20529
|
var net = __toESM(require("net"), 1);
|
|
19990
20530
|
async function findAvailablePort(basePort = 3456) {
|
|
@@ -20015,26 +20555,26 @@ function isPortAvailable(port) {
|
|
|
20015
20555
|
async function detectRuntime2(stackPath) {
|
|
20016
20556
|
const runtimes = ["node", "python"];
|
|
20017
20557
|
for (const runtime of runtimes) {
|
|
20018
|
-
const runtimePath =
|
|
20558
|
+
const runtimePath = path20.join(stackPath, runtime);
|
|
20019
20559
|
try {
|
|
20020
|
-
await
|
|
20560
|
+
await fs22.access(runtimePath);
|
|
20021
20561
|
if (runtime === "node") {
|
|
20022
|
-
const authTs =
|
|
20023
|
-
const authJs =
|
|
20562
|
+
const authTs = path20.join(runtimePath, "src", "auth.ts");
|
|
20563
|
+
const authJs = path20.join(runtimePath, "dist", "auth.js");
|
|
20024
20564
|
try {
|
|
20025
|
-
await
|
|
20565
|
+
await fs22.access(authTs);
|
|
20026
20566
|
return { runtime: "node", authScript: authTs, useTsx: true };
|
|
20027
20567
|
} catch {
|
|
20028
20568
|
try {
|
|
20029
|
-
await
|
|
20569
|
+
await fs22.access(authJs);
|
|
20030
20570
|
return { runtime: "node", authScript: authJs, useTsx: false };
|
|
20031
20571
|
} catch {
|
|
20032
20572
|
}
|
|
20033
20573
|
}
|
|
20034
20574
|
} else if (runtime === "python") {
|
|
20035
|
-
const authPy =
|
|
20575
|
+
const authPy = path20.join(runtimePath, "src", "auth.py");
|
|
20036
20576
|
try {
|
|
20037
|
-
await
|
|
20577
|
+
await fs22.access(authPy);
|
|
20038
20578
|
return { runtime: "python", authScript: authPy, useTsx: false };
|
|
20039
20579
|
} catch {
|
|
20040
20580
|
}
|
|
@@ -20084,14 +20624,14 @@ Installed stacks:`);
|
|
|
20084
20624
|
console.log(`Using port: ${port}`);
|
|
20085
20625
|
console.log("");
|
|
20086
20626
|
let cmd;
|
|
20087
|
-
const cwd =
|
|
20627
|
+
const cwd = path20.dirname(authInfo.authScript);
|
|
20088
20628
|
if (authInfo.runtime === "node") {
|
|
20089
|
-
const distAuth =
|
|
20629
|
+
const distAuth = path20.join(cwd, "..", "dist", "auth.js");
|
|
20090
20630
|
let useBuiltInPort = false;
|
|
20091
20631
|
let tempAuthScript = null;
|
|
20092
20632
|
try {
|
|
20093
|
-
await
|
|
20094
|
-
const distContent = await
|
|
20633
|
+
await fs22.access(distAuth);
|
|
20634
|
+
const distContent = await fs22.readFile(distAuth, "utf-8");
|
|
20095
20635
|
if (distContent.includes("findAvailablePort")) {
|
|
20096
20636
|
console.log("Using compiled authentication script...");
|
|
20097
20637
|
cmd = `node ${distAuth}${accountEmail ? ` ${accountEmail}` : ""}`;
|
|
@@ -20100,11 +20640,11 @@ Installed stacks:`);
|
|
|
20100
20640
|
} catch {
|
|
20101
20641
|
}
|
|
20102
20642
|
if (!useBuiltInPort) {
|
|
20103
|
-
const authContent = await
|
|
20643
|
+
const authContent = await fs22.readFile(authInfo.authScript, "utf-8");
|
|
20104
20644
|
const tempExt = authInfo.useTsx ? ".ts" : ".mjs";
|
|
20105
|
-
tempAuthScript =
|
|
20645
|
+
tempAuthScript = path20.join(cwd, "..", `auth-temp${tempExt}`);
|
|
20106
20646
|
const modifiedContent = authContent.replace(/localhost:3456/g, `localhost:${port}`).replace(/server\.listen\(3456/g, `server.listen(${port}`);
|
|
20107
|
-
await
|
|
20647
|
+
await fs22.writeFile(tempAuthScript, modifiedContent);
|
|
20108
20648
|
if (authInfo.useTsx) {
|
|
20109
20649
|
cmd = `npx tsx ${tempAuthScript}${accountEmail ? ` ${accountEmail}` : ""}`;
|
|
20110
20650
|
} else {
|
|
@@ -20114,17 +20654,17 @@ Installed stacks:`);
|
|
|
20114
20654
|
console.log("Starting OAuth flow...");
|
|
20115
20655
|
console.log("");
|
|
20116
20656
|
try {
|
|
20117
|
-
(0,
|
|
20657
|
+
(0, import_child_process7.execSync)(cmd, {
|
|
20118
20658
|
cwd,
|
|
20119
20659
|
stdio: "inherit"
|
|
20120
20660
|
});
|
|
20121
20661
|
if (tempAuthScript) {
|
|
20122
|
-
await
|
|
20662
|
+
await fs22.unlink(tempAuthScript);
|
|
20123
20663
|
}
|
|
20124
20664
|
} catch (error) {
|
|
20125
20665
|
if (tempAuthScript) {
|
|
20126
20666
|
try {
|
|
20127
|
-
await
|
|
20667
|
+
await fs22.unlink(tempAuthScript);
|
|
20128
20668
|
} catch {
|
|
20129
20669
|
}
|
|
20130
20670
|
}
|
|
@@ -20134,7 +20674,7 @@ Installed stacks:`);
|
|
|
20134
20674
|
cmd = `python3 ${authInfo.authScript}${accountEmail ? ` ${accountEmail}` : ""}`;
|
|
20135
20675
|
console.log("Starting OAuth flow...");
|
|
20136
20676
|
console.log("");
|
|
20137
|
-
(0,
|
|
20677
|
+
(0, import_child_process7.execSync)(cmd, {
|
|
20138
20678
|
cwd,
|
|
20139
20679
|
stdio: "inherit",
|
|
20140
20680
|
env: {
|
|
@@ -20155,6 +20695,657 @@ Installed stacks:`);
|
|
|
20155
20695
|
}
|
|
20156
20696
|
}
|
|
20157
20697
|
|
|
20698
|
+
// src/commands/mcp.js
|
|
20699
|
+
var fs23 = __toESM(require("fs"), 1);
|
|
20700
|
+
var path21 = __toESM(require("path"), 1);
|
|
20701
|
+
var import_child_process8 = require("child_process");
|
|
20702
|
+
init_src();
|
|
20703
|
+
function getBundledRuntime(runtime) {
|
|
20704
|
+
const platform = process.platform;
|
|
20705
|
+
if (runtime === "node") {
|
|
20706
|
+
const nodePath = platform === "win32" ? path21.join(PATHS.runtimes, "node", "node.exe") : path21.join(PATHS.runtimes, "node", "bin", "node");
|
|
20707
|
+
if (fs23.existsSync(nodePath)) {
|
|
20708
|
+
return nodePath;
|
|
20709
|
+
}
|
|
20710
|
+
}
|
|
20711
|
+
if (runtime === "python") {
|
|
20712
|
+
const pythonPath = platform === "win32" ? path21.join(PATHS.runtimes, "python", "python.exe") : path21.join(PATHS.runtimes, "python", "bin", "python3");
|
|
20713
|
+
if (fs23.existsSync(pythonPath)) {
|
|
20714
|
+
return pythonPath;
|
|
20715
|
+
}
|
|
20716
|
+
}
|
|
20717
|
+
return null;
|
|
20718
|
+
}
|
|
20719
|
+
function getBundledNpx() {
|
|
20720
|
+
const platform = process.platform;
|
|
20721
|
+
const npxPath = platform === "win32" ? path21.join(PATHS.runtimes, "node", "npx.cmd") : path21.join(PATHS.runtimes, "node", "bin", "npx");
|
|
20722
|
+
if (fs23.existsSync(npxPath)) {
|
|
20723
|
+
return npxPath;
|
|
20724
|
+
}
|
|
20725
|
+
return null;
|
|
20726
|
+
}
|
|
20727
|
+
function loadManifest2(stackPath) {
|
|
20728
|
+
const manifestPath = path21.join(stackPath, "manifest.json");
|
|
20729
|
+
if (!fs23.existsSync(manifestPath)) {
|
|
20730
|
+
return null;
|
|
20731
|
+
}
|
|
20732
|
+
return JSON.parse(fs23.readFileSync(manifestPath, "utf-8"));
|
|
20733
|
+
}
|
|
20734
|
+
function getRequiredSecrets(manifest) {
|
|
20735
|
+
const secrets = manifest?.requires?.secrets || manifest?.secrets || [];
|
|
20736
|
+
return secrets.map((s) => ({
|
|
20737
|
+
name: typeof s === "string" ? s : s.name || s.key,
|
|
20738
|
+
required: typeof s === "object" ? s.required !== false : true
|
|
20739
|
+
}));
|
|
20740
|
+
}
|
|
20741
|
+
async function buildEnv(manifest) {
|
|
20742
|
+
const env = { ...process.env };
|
|
20743
|
+
const requiredSecrets = getRequiredSecrets(manifest);
|
|
20744
|
+
const missing = [];
|
|
20745
|
+
for (const secret of requiredSecrets) {
|
|
20746
|
+
const value = await getSecret(secret.name);
|
|
20747
|
+
if (value) {
|
|
20748
|
+
env[secret.name] = value;
|
|
20749
|
+
} else if (secret.required) {
|
|
20750
|
+
missing.push(secret.name);
|
|
20751
|
+
}
|
|
20752
|
+
}
|
|
20753
|
+
return { env, missing };
|
|
20754
|
+
}
|
|
20755
|
+
async function cmdMcp(args, flags) {
|
|
20756
|
+
const stackName = args[0];
|
|
20757
|
+
if (!stackName) {
|
|
20758
|
+
console.error("Usage: rudi mcp <stack>");
|
|
20759
|
+
console.error("");
|
|
20760
|
+
console.error("This command is typically called by agent shims, not directly.");
|
|
20761
|
+
console.error("");
|
|
20762
|
+
console.error("Example: rudi mcp slack");
|
|
20763
|
+
process.exit(1);
|
|
20764
|
+
}
|
|
20765
|
+
const stackPath = path21.join(PATHS.stacks, stackName);
|
|
20766
|
+
if (!fs23.existsSync(stackPath)) {
|
|
20767
|
+
console.error(`Stack not found: ${stackName}`);
|
|
20768
|
+
console.error(`Expected at: ${stackPath}`);
|
|
20769
|
+
console.error("");
|
|
20770
|
+
console.error(`Install with: rudi install ${stackName}`);
|
|
20771
|
+
process.exit(1);
|
|
20772
|
+
}
|
|
20773
|
+
const manifest = loadManifest2(stackPath);
|
|
20774
|
+
if (!manifest) {
|
|
20775
|
+
console.error(`No manifest.json found in stack: ${stackName}`);
|
|
20776
|
+
process.exit(1);
|
|
20777
|
+
}
|
|
20778
|
+
const { env, missing } = await buildEnv(manifest);
|
|
20779
|
+
if (missing.length > 0 && !flags.force) {
|
|
20780
|
+
console.error(`Missing required secrets for ${stackName}:`);
|
|
20781
|
+
for (const name of missing) {
|
|
20782
|
+
console.error(` - ${name}`);
|
|
20783
|
+
}
|
|
20784
|
+
console.error("");
|
|
20785
|
+
console.error(`Set with: rudi secrets set ${missing[0]}`);
|
|
20786
|
+
process.exit(1);
|
|
20787
|
+
}
|
|
20788
|
+
let command = manifest.command;
|
|
20789
|
+
if (!command || command.length === 0) {
|
|
20790
|
+
if (manifest.mcp?.command) {
|
|
20791
|
+
const mcpCmd = manifest.mcp.command;
|
|
20792
|
+
const mcpArgs = manifest.mcp.args || [];
|
|
20793
|
+
command = [mcpCmd, ...mcpArgs];
|
|
20794
|
+
}
|
|
20795
|
+
}
|
|
20796
|
+
if (!command || command.length === 0) {
|
|
20797
|
+
console.error(`No command defined in manifest for: ${stackName}`);
|
|
20798
|
+
process.exit(1);
|
|
20799
|
+
}
|
|
20800
|
+
const runtime = manifest.runtime || manifest.mcp?.runtime || "node";
|
|
20801
|
+
const resolvedCommand = command.map((part, i) => {
|
|
20802
|
+
if (i === 0) {
|
|
20803
|
+
if (part === "node") {
|
|
20804
|
+
const bundledNode = getBundledRuntime("node");
|
|
20805
|
+
if (bundledNode) return bundledNode;
|
|
20806
|
+
} else if (part === "npx") {
|
|
20807
|
+
const bundledNpx = getBundledNpx();
|
|
20808
|
+
if (bundledNpx) return bundledNpx;
|
|
20809
|
+
} else if (part === "python" || part === "python3") {
|
|
20810
|
+
const bundledPython = getBundledRuntime("python");
|
|
20811
|
+
if (bundledPython) return bundledPython;
|
|
20812
|
+
}
|
|
20813
|
+
return part;
|
|
20814
|
+
}
|
|
20815
|
+
if (part.startsWith("./") || part.startsWith("../") || !path21.isAbsolute(part)) {
|
|
20816
|
+
const resolved = path21.join(stackPath, part);
|
|
20817
|
+
if (fs23.existsSync(resolved)) {
|
|
20818
|
+
return resolved;
|
|
20819
|
+
}
|
|
20820
|
+
}
|
|
20821
|
+
return part;
|
|
20822
|
+
});
|
|
20823
|
+
const [cmd, ...cmdArgs] = resolvedCommand;
|
|
20824
|
+
const bundledNodeBin = path21.join(PATHS.runtimes, "node", "bin");
|
|
20825
|
+
const bundledPythonBin = path21.join(PATHS.runtimes, "python", "bin");
|
|
20826
|
+
if (fs23.existsSync(bundledNodeBin) || fs23.existsSync(bundledPythonBin)) {
|
|
20827
|
+
const runtimePaths = [];
|
|
20828
|
+
if (fs23.existsSync(bundledNodeBin)) runtimePaths.push(bundledNodeBin);
|
|
20829
|
+
if (fs23.existsSync(bundledPythonBin)) runtimePaths.push(bundledPythonBin);
|
|
20830
|
+
env.PATH = runtimePaths.join(path21.delimiter) + path21.delimiter + (env.PATH || "");
|
|
20831
|
+
}
|
|
20832
|
+
if (flags.debug) {
|
|
20833
|
+
console.error(`[rudi mcp] Stack: ${stackName}`);
|
|
20834
|
+
console.error(`[rudi mcp] Path: ${stackPath}`);
|
|
20835
|
+
console.error(`[rudi mcp] Runtime: ${runtime}`);
|
|
20836
|
+
console.error(`[rudi mcp] Command: ${cmd} ${cmdArgs.join(" ")}`);
|
|
20837
|
+
console.error(`[rudi mcp] Secrets loaded: ${getRequiredSecrets(manifest).length - missing.length}`);
|
|
20838
|
+
if (getBundledRuntime(runtime)) {
|
|
20839
|
+
console.error(`[rudi mcp] Using bundled ${runtime} runtime`);
|
|
20840
|
+
} else {
|
|
20841
|
+
console.error(`[rudi mcp] Using system ${runtime} (no bundled runtime found)`);
|
|
20842
|
+
}
|
|
20843
|
+
}
|
|
20844
|
+
const child = (0, import_child_process8.spawn)(cmd, cmdArgs, {
|
|
20845
|
+
cwd: stackPath,
|
|
20846
|
+
env,
|
|
20847
|
+
stdio: "inherit"
|
|
20848
|
+
// MCP uses stdio for communication
|
|
20849
|
+
});
|
|
20850
|
+
child.on("error", (err) => {
|
|
20851
|
+
console.error(`Failed to start MCP server: ${err.message}`);
|
|
20852
|
+
process.exit(1);
|
|
20853
|
+
});
|
|
20854
|
+
child.on("exit", (code) => {
|
|
20855
|
+
process.exit(code || 0);
|
|
20856
|
+
});
|
|
20857
|
+
}
|
|
20858
|
+
|
|
20859
|
+
// src/commands/integrate.js
|
|
20860
|
+
var fs24 = __toESM(require("fs"), 1);
|
|
20861
|
+
var path22 = __toESM(require("path"), 1);
|
|
20862
|
+
var import_os6 = __toESM(require("os"), 1);
|
|
20863
|
+
init_src();
|
|
20864
|
+
var HOME2 = import_os6.default.homedir();
|
|
20865
|
+
var SHIM_PATH = path22.join(PATHS.home, "shims", "rudi-mcp");
|
|
20866
|
+
function getInstalledStacks() {
|
|
20867
|
+
const stacksDir = PATHS.stacks;
|
|
20868
|
+
if (!fs24.existsSync(stacksDir)) return [];
|
|
20869
|
+
return fs24.readdirSync(stacksDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).filter((d) => fs24.existsSync(path22.join(stacksDir, d.name, "manifest.json"))).map((d) => d.name);
|
|
20870
|
+
}
|
|
20871
|
+
function ensureShim() {
|
|
20872
|
+
const shimsDir = path22.dirname(SHIM_PATH);
|
|
20873
|
+
if (!fs24.existsSync(shimsDir)) {
|
|
20874
|
+
fs24.mkdirSync(shimsDir, { recursive: true });
|
|
20875
|
+
}
|
|
20876
|
+
const shimContent = `#!/usr/bin/env bash
|
|
20877
|
+
set -euo pipefail
|
|
20878
|
+
# Try rudi in PATH first, fall back to npx
|
|
20879
|
+
if command -v rudi &> /dev/null; then
|
|
20880
|
+
exec rudi mcp "$1"
|
|
20881
|
+
else
|
|
20882
|
+
exec npx --yes @learnrudi/cli mcp "$1"
|
|
20883
|
+
fi
|
|
20884
|
+
`;
|
|
20885
|
+
if (fs24.existsSync(SHIM_PATH)) {
|
|
20886
|
+
const existing = fs24.readFileSync(SHIM_PATH, "utf-8");
|
|
20887
|
+
if (existing === shimContent) {
|
|
20888
|
+
return { created: false, path: SHIM_PATH };
|
|
20889
|
+
}
|
|
20890
|
+
}
|
|
20891
|
+
fs24.writeFileSync(SHIM_PATH, shimContent, { mode: 493 });
|
|
20892
|
+
return { created: true, path: SHIM_PATH };
|
|
20893
|
+
}
|
|
20894
|
+
function backupConfig(configPath) {
|
|
20895
|
+
if (!fs24.existsSync(configPath)) return null;
|
|
20896
|
+
const backupPath = configPath + ".backup." + Date.now();
|
|
20897
|
+
fs24.copyFileSync(configPath, backupPath);
|
|
20898
|
+
return backupPath;
|
|
20899
|
+
}
|
|
20900
|
+
function readJsonConfig(configPath) {
|
|
20901
|
+
if (!fs24.existsSync(configPath)) {
|
|
20902
|
+
return {};
|
|
20903
|
+
}
|
|
20904
|
+
try {
|
|
20905
|
+
return JSON.parse(fs24.readFileSync(configPath, "utf-8"));
|
|
20906
|
+
} catch {
|
|
20907
|
+
return {};
|
|
20908
|
+
}
|
|
20909
|
+
}
|
|
20910
|
+
function writeJsonConfig(configPath, config) {
|
|
20911
|
+
const dir = path22.dirname(configPath);
|
|
20912
|
+
if (!fs24.existsSync(dir)) {
|
|
20913
|
+
fs24.mkdirSync(dir, { recursive: true });
|
|
20914
|
+
}
|
|
20915
|
+
fs24.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
20916
|
+
}
|
|
20917
|
+
function buildMcpEntry(stackName, agentId) {
|
|
20918
|
+
const base = {
|
|
20919
|
+
command: SHIM_PATH,
|
|
20920
|
+
args: [stackName]
|
|
20921
|
+
};
|
|
20922
|
+
if (agentId === "claude-desktop" || agentId === "claude-code") {
|
|
20923
|
+
return { type: "stdio", ...base };
|
|
20924
|
+
}
|
|
20925
|
+
return base;
|
|
20926
|
+
}
|
|
20927
|
+
async function integrateAgent(agentId, stacks, flags) {
|
|
20928
|
+
const agentConfig = AGENT_CONFIGS2.find((a) => a.id === agentId);
|
|
20929
|
+
if (!agentConfig) {
|
|
20930
|
+
console.error(`Unknown agent: ${agentId}`);
|
|
20931
|
+
return { success: false, error: "Unknown agent" };
|
|
20932
|
+
}
|
|
20933
|
+
const configPath = findAgentConfig(agentConfig);
|
|
20934
|
+
const configDir = configPath ? path22.dirname(configPath) : null;
|
|
20935
|
+
const targetPath = configPath || path22.join(HOME2, agentConfig.paths[process.platform]?.[0] || agentConfig.paths.darwin[0]);
|
|
20936
|
+
console.log(`
|
|
20937
|
+
${agentConfig.name}:`);
|
|
20938
|
+
console.log(` Config: ${targetPath}`);
|
|
20939
|
+
if (fs24.existsSync(targetPath)) {
|
|
20940
|
+
const backup = backupConfig(targetPath);
|
|
20941
|
+
if (backup && flags.verbose) {
|
|
20942
|
+
console.log(` Backup: ${backup}`);
|
|
20943
|
+
}
|
|
20944
|
+
}
|
|
20945
|
+
const config = readJsonConfig(targetPath);
|
|
20946
|
+
const key = agentConfig.key;
|
|
20947
|
+
if (!config[key]) {
|
|
20948
|
+
config[key] = {};
|
|
20949
|
+
}
|
|
20950
|
+
let added = 0;
|
|
20951
|
+
let updated = 0;
|
|
20952
|
+
for (const stackName of stacks) {
|
|
20953
|
+
const entry = buildMcpEntry(stackName, agentId);
|
|
20954
|
+
const existing = config[key][stackName];
|
|
20955
|
+
if (!existing) {
|
|
20956
|
+
config[key][stackName] = entry;
|
|
20957
|
+
added++;
|
|
20958
|
+
} else if (existing.command !== entry.command || JSON.stringify(existing.args) !== JSON.stringify(entry.args)) {
|
|
20959
|
+
config[key][stackName] = entry;
|
|
20960
|
+
updated++;
|
|
20961
|
+
}
|
|
20962
|
+
}
|
|
20963
|
+
if (added > 0 || updated > 0) {
|
|
20964
|
+
writeJsonConfig(targetPath, config);
|
|
20965
|
+
console.log(` Added: ${added}, Updated: ${updated}`);
|
|
20966
|
+
} else {
|
|
20967
|
+
console.log(` Already up to date`);
|
|
20968
|
+
}
|
|
20969
|
+
return { success: true, added, updated };
|
|
20970
|
+
}
|
|
20971
|
+
async function cmdIntegrate(args, flags) {
|
|
20972
|
+
const target = args[0];
|
|
20973
|
+
if (!target) {
|
|
20974
|
+
console.log(`
|
|
20975
|
+
rudi integrate - Wire RUDI stacks into agent configs
|
|
20976
|
+
|
|
20977
|
+
USAGE
|
|
20978
|
+
rudi integrate <agent> Integrate with specific agent
|
|
20979
|
+
rudi integrate all Integrate with all detected agents
|
|
20980
|
+
rudi integrate --list Show detected agents
|
|
20981
|
+
|
|
20982
|
+
AGENTS
|
|
20983
|
+
claude Claude Desktop + Claude Code
|
|
20984
|
+
cursor Cursor IDE
|
|
20985
|
+
windsurf Windsurf IDE
|
|
20986
|
+
vscode VS Code / GitHub Copilot
|
|
20987
|
+
gemini Gemini CLI
|
|
20988
|
+
codex OpenAI Codex CLI
|
|
20989
|
+
zed Zed Editor
|
|
20990
|
+
|
|
20991
|
+
OPTIONS
|
|
20992
|
+
--verbose Show detailed output
|
|
20993
|
+
--dry-run Show what would be done without making changes
|
|
20994
|
+
|
|
20995
|
+
EXAMPLES
|
|
20996
|
+
rudi integrate claude
|
|
20997
|
+
rudi integrate all
|
|
20998
|
+
`);
|
|
20999
|
+
return;
|
|
21000
|
+
}
|
|
21001
|
+
if (flags.list || target === "list") {
|
|
21002
|
+
const installed = getInstalledAgents();
|
|
21003
|
+
console.log("\nDetected agents:");
|
|
21004
|
+
for (const agent of installed) {
|
|
21005
|
+
console.log(` \u2713 ${agent.name}`);
|
|
21006
|
+
console.log(` ${agent.configFile}`);
|
|
21007
|
+
}
|
|
21008
|
+
if (installed.length === 0) {
|
|
21009
|
+
console.log(" (none detected)");
|
|
21010
|
+
}
|
|
21011
|
+
return;
|
|
21012
|
+
}
|
|
21013
|
+
const stacks = getInstalledStacks();
|
|
21014
|
+
if (stacks.length === 0) {
|
|
21015
|
+
console.log("No stacks installed. Install with: rudi install <stack>");
|
|
21016
|
+
return;
|
|
21017
|
+
}
|
|
21018
|
+
console.log(`
|
|
21019
|
+
Integrating ${stacks.length} stack(s)...`);
|
|
21020
|
+
const shimResult = ensureShim();
|
|
21021
|
+
if (shimResult.created) {
|
|
21022
|
+
console.log(`Created shim: ${shimResult.path}`);
|
|
21023
|
+
}
|
|
21024
|
+
let targetAgents = [];
|
|
21025
|
+
if (target === "all") {
|
|
21026
|
+
targetAgents = getInstalledAgents().map((a) => a.id);
|
|
21027
|
+
if (targetAgents.length === 0) {
|
|
21028
|
+
console.log("No agents detected.");
|
|
21029
|
+
return;
|
|
21030
|
+
}
|
|
21031
|
+
} else if (target === "claude") {
|
|
21032
|
+
targetAgents = ["claude-desktop", "claude-code"].filter((id) => {
|
|
21033
|
+
const agent = AGENT_CONFIGS2.find((a) => a.id === id);
|
|
21034
|
+
return agent && findAgentConfig(agent);
|
|
21035
|
+
});
|
|
21036
|
+
if (targetAgents.length === 0) {
|
|
21037
|
+
targetAgents = ["claude-code"];
|
|
21038
|
+
}
|
|
21039
|
+
} else {
|
|
21040
|
+
const idMap = {
|
|
21041
|
+
"cursor": "cursor",
|
|
21042
|
+
"windsurf": "windsurf",
|
|
21043
|
+
"vscode": "vscode",
|
|
21044
|
+
"gemini": "gemini",
|
|
21045
|
+
"codex": "codex",
|
|
21046
|
+
"zed": "zed",
|
|
21047
|
+
"cline": "cline"
|
|
21048
|
+
};
|
|
21049
|
+
const agentId = idMap[target] || target;
|
|
21050
|
+
targetAgents = [agentId];
|
|
21051
|
+
}
|
|
21052
|
+
if (flags["dry-run"]) {
|
|
21053
|
+
console.log("\nDry run - would integrate:");
|
|
21054
|
+
for (const agentId of targetAgents) {
|
|
21055
|
+
const agent = AGENT_CONFIGS2.find((a) => a.id === agentId);
|
|
21056
|
+
console.log(` ${agent?.name || agentId}:`);
|
|
21057
|
+
for (const stack of stacks) {
|
|
21058
|
+
console.log(` - ${stack}`);
|
|
21059
|
+
}
|
|
21060
|
+
}
|
|
21061
|
+
return;
|
|
21062
|
+
}
|
|
21063
|
+
const results = [];
|
|
21064
|
+
for (const agentId of targetAgents) {
|
|
21065
|
+
const result = await integrateAgent(agentId, stacks, flags);
|
|
21066
|
+
results.push({ agent: agentId, ...result });
|
|
21067
|
+
}
|
|
21068
|
+
const successful = results.filter((r) => r.success);
|
|
21069
|
+
console.log(`
|
|
21070
|
+
\u2713 Integrated with ${successful.length} agent(s)`);
|
|
21071
|
+
console.log("\nRestart your agent(s) to use the new stacks.");
|
|
21072
|
+
}
|
|
21073
|
+
|
|
21074
|
+
// src/commands/migrate.js
|
|
21075
|
+
var fs25 = __toESM(require("fs"), 1);
|
|
21076
|
+
var path23 = __toESM(require("path"), 1);
|
|
21077
|
+
var import_os7 = __toESM(require("os"), 1);
|
|
21078
|
+
init_src();
|
|
21079
|
+
var HOME3 = import_os7.default.homedir();
|
|
21080
|
+
var OLD_PROMPT_STACK = path23.join(HOME3, ".prompt-stack");
|
|
21081
|
+
var SHIM_PATH2 = path23.join(PATHS.home, "shims", "rudi-mcp");
|
|
21082
|
+
function getOldStacks() {
|
|
21083
|
+
const stacksDir = path23.join(OLD_PROMPT_STACK, "stacks");
|
|
21084
|
+
if (!fs25.existsSync(stacksDir)) return [];
|
|
21085
|
+
return fs25.readdirSync(stacksDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).filter((d) => {
|
|
21086
|
+
const hasManifest = fs25.existsSync(path23.join(stacksDir, d.name, "manifest.json"));
|
|
21087
|
+
const hasPackage = fs25.existsSync(path23.join(stacksDir, d.name, "package.json"));
|
|
21088
|
+
return hasManifest || hasPackage;
|
|
21089
|
+
}).map((d) => d.name);
|
|
21090
|
+
}
|
|
21091
|
+
function copyStack(stackName) {
|
|
21092
|
+
const oldPath = path23.join(OLD_PROMPT_STACK, "stacks", stackName);
|
|
21093
|
+
const newPath = path23.join(PATHS.stacks, stackName);
|
|
21094
|
+
if (!fs25.existsSync(oldPath)) {
|
|
21095
|
+
return { success: false, error: "Source not found" };
|
|
21096
|
+
}
|
|
21097
|
+
if (fs25.existsSync(newPath)) {
|
|
21098
|
+
return { success: true, skipped: true, reason: "Already exists" };
|
|
21099
|
+
}
|
|
21100
|
+
if (!fs25.existsSync(PATHS.stacks)) {
|
|
21101
|
+
fs25.mkdirSync(PATHS.stacks, { recursive: true });
|
|
21102
|
+
}
|
|
21103
|
+
copyRecursive(oldPath, newPath);
|
|
21104
|
+
return { success: true, copied: true };
|
|
21105
|
+
}
|
|
21106
|
+
function copyRecursive(src, dest) {
|
|
21107
|
+
const stat = fs25.statSync(src);
|
|
21108
|
+
if (stat.isDirectory()) {
|
|
21109
|
+
fs25.mkdirSync(dest, { recursive: true });
|
|
21110
|
+
for (const child of fs25.readdirSync(src)) {
|
|
21111
|
+
copyRecursive(path23.join(src, child), path23.join(dest, child));
|
|
21112
|
+
}
|
|
21113
|
+
} else {
|
|
21114
|
+
fs25.copyFileSync(src, dest);
|
|
21115
|
+
}
|
|
21116
|
+
}
|
|
21117
|
+
function ensureShim2() {
|
|
21118
|
+
const shimsDir = path23.dirname(SHIM_PATH2);
|
|
21119
|
+
if (!fs25.existsSync(shimsDir)) {
|
|
21120
|
+
fs25.mkdirSync(shimsDir, { recursive: true });
|
|
21121
|
+
}
|
|
21122
|
+
const shimContent = `#!/usr/bin/env bash
|
|
21123
|
+
set -euo pipefail
|
|
21124
|
+
if command -v rudi &> /dev/null; then
|
|
21125
|
+
exec rudi mcp "$1"
|
|
21126
|
+
else
|
|
21127
|
+
exec npx --yes @learnrudi/cli mcp "$1"
|
|
21128
|
+
fi
|
|
21129
|
+
`;
|
|
21130
|
+
fs25.writeFileSync(SHIM_PATH2, shimContent, { mode: 493 });
|
|
21131
|
+
}
|
|
21132
|
+
function buildNewEntry(stackName, agentId) {
|
|
21133
|
+
const base = {
|
|
21134
|
+
command: SHIM_PATH2,
|
|
21135
|
+
args: [stackName]
|
|
21136
|
+
};
|
|
21137
|
+
if (agentId === "claude-desktop" || agentId === "claude-code") {
|
|
21138
|
+
return { type: "stdio", ...base };
|
|
21139
|
+
}
|
|
21140
|
+
return base;
|
|
21141
|
+
}
|
|
21142
|
+
function isOldEntry(entry) {
|
|
21143
|
+
if (!entry) return false;
|
|
21144
|
+
const command = entry.command || "";
|
|
21145
|
+
const args = entry.args || [];
|
|
21146
|
+
const cwd = entry.cwd || "";
|
|
21147
|
+
return command.includes(".prompt-stack") || args.some((a) => typeof a === "string" && a.includes(".prompt-stack")) || cwd.includes(".prompt-stack");
|
|
21148
|
+
}
|
|
21149
|
+
function migrateAgentConfig(agentConfig, installedStacks, flags) {
|
|
21150
|
+
const configPath = findAgentConfig(agentConfig);
|
|
21151
|
+
if (!configPath) return { skipped: true, reason: "Config not found" };
|
|
21152
|
+
let config;
|
|
21153
|
+
try {
|
|
21154
|
+
config = JSON.parse(fs25.readFileSync(configPath, "utf-8"));
|
|
21155
|
+
} catch {
|
|
21156
|
+
return { skipped: true, reason: "Could not parse config" };
|
|
21157
|
+
}
|
|
21158
|
+
const key = agentConfig.key;
|
|
21159
|
+
const mcpServers = config[key] || {};
|
|
21160
|
+
let updated = 0;
|
|
21161
|
+
let removed = 0;
|
|
21162
|
+
const changes = [];
|
|
21163
|
+
for (const [name, entry] of Object.entries(mcpServers)) {
|
|
21164
|
+
if (isOldEntry(entry)) {
|
|
21165
|
+
if (installedStacks.includes(name)) {
|
|
21166
|
+
const newEntry = buildNewEntry(name, agentConfig.id);
|
|
21167
|
+
mcpServers[name] = newEntry;
|
|
21168
|
+
updated++;
|
|
21169
|
+
changes.push({ name, action: "updated" });
|
|
21170
|
+
} else if (flags.removeOrphans) {
|
|
21171
|
+
delete mcpServers[name];
|
|
21172
|
+
removed++;
|
|
21173
|
+
changes.push({ name, action: "removed (not installed)" });
|
|
21174
|
+
} else {
|
|
21175
|
+
changes.push({ name, action: "skipped (not installed in .rudi)" });
|
|
21176
|
+
}
|
|
21177
|
+
}
|
|
21178
|
+
}
|
|
21179
|
+
if (updated > 0 || removed > 0) {
|
|
21180
|
+
const backupPath = configPath + ".backup." + Date.now();
|
|
21181
|
+
fs25.copyFileSync(configPath, backupPath);
|
|
21182
|
+
config[key] = mcpServers;
|
|
21183
|
+
fs25.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
21184
|
+
}
|
|
21185
|
+
return { updated, removed, changes };
|
|
21186
|
+
}
|
|
21187
|
+
async function cmdMigrate(args, flags) {
|
|
21188
|
+
const subcommand = args[0];
|
|
21189
|
+
if (!subcommand || subcommand === "help") {
|
|
21190
|
+
console.log(`
|
|
21191
|
+
rudi migrate - Migrate from .prompt-stack to .rudi
|
|
21192
|
+
|
|
21193
|
+
USAGE
|
|
21194
|
+
rudi migrate status Show what needs to be migrated
|
|
21195
|
+
rudi migrate stacks Copy stacks from .prompt-stack to .rudi
|
|
21196
|
+
rudi migrate configs Update agent configs to use new shim
|
|
21197
|
+
rudi migrate all Do everything
|
|
21198
|
+
|
|
21199
|
+
OPTIONS
|
|
21200
|
+
--remove-orphans Remove entries for stacks not installed in .rudi
|
|
21201
|
+
--dry-run Show what would be done without making changes
|
|
21202
|
+
`);
|
|
21203
|
+
return;
|
|
21204
|
+
}
|
|
21205
|
+
if (subcommand === "status") {
|
|
21206
|
+
await migrateStatus();
|
|
21207
|
+
return;
|
|
21208
|
+
}
|
|
21209
|
+
if (subcommand === "stacks") {
|
|
21210
|
+
await migrateStacks(flags);
|
|
21211
|
+
return;
|
|
21212
|
+
}
|
|
21213
|
+
if (subcommand === "configs") {
|
|
21214
|
+
await migrateConfigs(flags);
|
|
21215
|
+
return;
|
|
21216
|
+
}
|
|
21217
|
+
if (subcommand === "all") {
|
|
21218
|
+
await migrateStacks(flags);
|
|
21219
|
+
console.log("");
|
|
21220
|
+
await migrateConfigs(flags);
|
|
21221
|
+
return;
|
|
21222
|
+
}
|
|
21223
|
+
console.error(`Unknown subcommand: ${subcommand}`);
|
|
21224
|
+
console.error("Run: rudi migrate help");
|
|
21225
|
+
}
|
|
21226
|
+
async function migrateStatus() {
|
|
21227
|
+
console.log("\n=== Migration Status ===\n");
|
|
21228
|
+
const oldStacks = getOldStacks();
|
|
21229
|
+
console.log(`Old .prompt-stack stacks: ${oldStacks.length}`);
|
|
21230
|
+
if (oldStacks.length > 0) {
|
|
21231
|
+
for (const name of oldStacks) {
|
|
21232
|
+
const existsInRudi = fs25.existsSync(path23.join(PATHS.stacks, name));
|
|
21233
|
+
const status = existsInRudi ? "\u2713 (already in .rudi)" : "\u25CB (needs migration)";
|
|
21234
|
+
console.log(` ${status} ${name}`);
|
|
21235
|
+
}
|
|
21236
|
+
}
|
|
21237
|
+
const newStacksDir = PATHS.stacks;
|
|
21238
|
+
let newStacks = [];
|
|
21239
|
+
if (fs25.existsSync(newStacksDir)) {
|
|
21240
|
+
newStacks = fs25.readdirSync(newStacksDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name);
|
|
21241
|
+
}
|
|
21242
|
+
console.log(`
|
|
21243
|
+
New .rudi stacks: ${newStacks.length}`);
|
|
21244
|
+
if (newStacks.length > 0) {
|
|
21245
|
+
for (const name of newStacks) {
|
|
21246
|
+
console.log(` \u2713 ${name}`);
|
|
21247
|
+
}
|
|
21248
|
+
}
|
|
21249
|
+
console.log("\n=== Agent Configs ===\n");
|
|
21250
|
+
for (const agentConfig of AGENT_CONFIGS2) {
|
|
21251
|
+
const configPath = findAgentConfig(agentConfig);
|
|
21252
|
+
if (!configPath) continue;
|
|
21253
|
+
let config;
|
|
21254
|
+
try {
|
|
21255
|
+
config = JSON.parse(fs25.readFileSync(configPath, "utf-8"));
|
|
21256
|
+
} catch {
|
|
21257
|
+
continue;
|
|
21258
|
+
}
|
|
21259
|
+
const mcpServers = config[agentConfig.key] || {};
|
|
21260
|
+
const entries = Object.entries(mcpServers);
|
|
21261
|
+
const oldEntries = entries.filter(([_, e]) => isOldEntry(e));
|
|
21262
|
+
const newEntries = entries.filter(([_, e]) => !isOldEntry(e));
|
|
21263
|
+
if (entries.length === 0) continue;
|
|
21264
|
+
console.log(`${agentConfig.name}:`);
|
|
21265
|
+
console.log(` Config: ${configPath}`);
|
|
21266
|
+
console.log(` Old entries: ${oldEntries.length}`);
|
|
21267
|
+
console.log(` New entries: ${newEntries.length}`);
|
|
21268
|
+
if (oldEntries.length > 0) {
|
|
21269
|
+
console.log(" Needs update:");
|
|
21270
|
+
for (const [name] of oldEntries) {
|
|
21271
|
+
const installed = newStacks.includes(name);
|
|
21272
|
+
const status = installed ? "(ready)" : "(not in .rudi)";
|
|
21273
|
+
console.log(` - ${name} ${status}`);
|
|
21274
|
+
}
|
|
21275
|
+
}
|
|
21276
|
+
console.log("");
|
|
21277
|
+
}
|
|
21278
|
+
console.log("Run: rudi migrate all");
|
|
21279
|
+
}
|
|
21280
|
+
async function migrateStacks(flags) {
|
|
21281
|
+
console.log("=== Migrating Stacks ===\n");
|
|
21282
|
+
const oldStacks = getOldStacks();
|
|
21283
|
+
if (oldStacks.length === 0) {
|
|
21284
|
+
console.log("No stacks found in .prompt-stack");
|
|
21285
|
+
return;
|
|
21286
|
+
}
|
|
21287
|
+
console.log(`Found ${oldStacks.length} stack(s) in .prompt-stack
|
|
21288
|
+
`);
|
|
21289
|
+
for (const name of oldStacks) {
|
|
21290
|
+
if (flags.dryRun) {
|
|
21291
|
+
const exists = fs25.existsSync(path23.join(PATHS.stacks, name));
|
|
21292
|
+
console.log(` [dry-run] ${name}: ${exists ? "would skip (exists)" : "would copy"}`);
|
|
21293
|
+
} else {
|
|
21294
|
+
const result = copyStack(name);
|
|
21295
|
+
if (result.skipped) {
|
|
21296
|
+
console.log(` \u25CB ${name}: skipped (${result.reason})`);
|
|
21297
|
+
} else if (result.copied) {
|
|
21298
|
+
console.log(` \u2713 ${name}: copied to .rudi/stacks/`);
|
|
21299
|
+
} else {
|
|
21300
|
+
console.log(` \u2717 ${name}: ${result.error}`);
|
|
21301
|
+
}
|
|
21302
|
+
}
|
|
21303
|
+
}
|
|
21304
|
+
if (!flags.dryRun) {
|
|
21305
|
+
console.log(`
|
|
21306
|
+
Stacks migrated to: ${PATHS.stacks}`);
|
|
21307
|
+
}
|
|
21308
|
+
}
|
|
21309
|
+
async function migrateConfigs(flags) {
|
|
21310
|
+
console.log("=== Updating Agent Configs ===\n");
|
|
21311
|
+
if (!flags.dryRun) {
|
|
21312
|
+
ensureShim2();
|
|
21313
|
+
console.log(`Shim ready: ${SHIM_PATH2}
|
|
21314
|
+
`);
|
|
21315
|
+
}
|
|
21316
|
+
let installedStacks = [];
|
|
21317
|
+
if (fs25.existsSync(PATHS.stacks)) {
|
|
21318
|
+
installedStacks = fs25.readdirSync(PATHS.stacks, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name);
|
|
21319
|
+
}
|
|
21320
|
+
for (const agentConfig of AGENT_CONFIGS2) {
|
|
21321
|
+
const configPath = findAgentConfig(agentConfig);
|
|
21322
|
+
if (!configPath) continue;
|
|
21323
|
+
if (flags.dryRun) {
|
|
21324
|
+
console.log(`${agentConfig.name}:`);
|
|
21325
|
+
console.log(` [dry-run] Would update entries using .prompt-stack paths`);
|
|
21326
|
+
continue;
|
|
21327
|
+
}
|
|
21328
|
+
const result = migrateAgentConfig(agentConfig, installedStacks, flags);
|
|
21329
|
+
if (result.skipped) {
|
|
21330
|
+
continue;
|
|
21331
|
+
}
|
|
21332
|
+
console.log(`${agentConfig.name}:`);
|
|
21333
|
+
console.log(` Config: ${configPath}`);
|
|
21334
|
+
if (result.changes && result.changes.length > 0) {
|
|
21335
|
+
for (const change of result.changes) {
|
|
21336
|
+
console.log(` ${change.action}: ${change.name}`);
|
|
21337
|
+
}
|
|
21338
|
+
}
|
|
21339
|
+
if (result.updated > 0 || result.removed > 0) {
|
|
21340
|
+
console.log(` Updated: ${result.updated}, Removed: ${result.removed}`);
|
|
21341
|
+
} else {
|
|
21342
|
+
console.log(` No changes needed`);
|
|
21343
|
+
}
|
|
21344
|
+
console.log("");
|
|
21345
|
+
}
|
|
21346
|
+
console.log("Restart your agents to use the updated configs.");
|
|
21347
|
+
}
|
|
21348
|
+
|
|
20158
21349
|
// src/index.js
|
|
20159
21350
|
var VERSION = "2.0.0";
|
|
20160
21351
|
async function main() {
|
|
@@ -20205,6 +21396,11 @@ async function main() {
|
|
|
20205
21396
|
case "check":
|
|
20206
21397
|
await cmdDoctor(args, flags);
|
|
20207
21398
|
break;
|
|
21399
|
+
case "init":
|
|
21400
|
+
case "bootstrap":
|
|
21401
|
+
case "setup":
|
|
21402
|
+
await cmdInit(args, flags);
|
|
21403
|
+
break;
|
|
20208
21404
|
case "update":
|
|
20209
21405
|
case "upgrade":
|
|
20210
21406
|
await cmdUpdate(args, flags);
|
|
@@ -20222,6 +21418,15 @@ async function main() {
|
|
|
20222
21418
|
case "login":
|
|
20223
21419
|
await cmdAuth(args, flags);
|
|
20224
21420
|
break;
|
|
21421
|
+
case "mcp":
|
|
21422
|
+
await cmdMcp(args, flags);
|
|
21423
|
+
break;
|
|
21424
|
+
case "integrate":
|
|
21425
|
+
await cmdIntegrate(args, flags);
|
|
21426
|
+
break;
|
|
21427
|
+
case "migrate":
|
|
21428
|
+
await cmdMigrate(args, flags);
|
|
21429
|
+
break;
|
|
20225
21430
|
case "home":
|
|
20226
21431
|
case "status":
|
|
20227
21432
|
await cmdHome(args, flags);
|