@learnrudi/cli 1.4.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 +2289 -891
- 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,51 +14856,63 @@ rudi - RUDI CLI
|
|
|
14819
14856
|
USAGE
|
|
14820
14857
|
rudi <command> [options]
|
|
14821
14858
|
|
|
14822
|
-
|
|
14823
|
-
|
|
14824
|
-
|
|
14825
|
-
|
|
14826
|
-
|
|
14827
|
-
|
|
14828
|
-
|
|
14829
|
-
|
|
14830
|
-
|
|
14831
|
-
|
|
14832
|
-
|
|
14833
|
-
|
|
14834
|
-
|
|
14859
|
+
SETUP
|
|
14860
|
+
init Bootstrap RUDI (download runtimes, create shims)
|
|
14861
|
+
|
|
14862
|
+
INTROSPECTION
|
|
14863
|
+
home Show ~/.rudi structure and installed packages
|
|
14864
|
+
stacks List installed stacks
|
|
14865
|
+
runtimes List installed runtimes
|
|
14866
|
+
binaries List installed binaries
|
|
14867
|
+
agents List installed agents
|
|
14868
|
+
prompts List installed prompts
|
|
14869
|
+
doctor Check system health and dependencies
|
|
14870
|
+
doctor --all Show all available runtimes/binaries from registry
|
|
14871
|
+
|
|
14872
|
+
PACKAGE MANAGEMENT
|
|
14873
|
+
search <query> Search registry for packages
|
|
14874
|
+
search --all List all available packages
|
|
14875
|
+
install <pkg> Install a package
|
|
14876
|
+
remove <pkg> Remove a package
|
|
14877
|
+
update [pkg] Update packages
|
|
14878
|
+
run <stack> Run a stack
|
|
14879
|
+
|
|
14880
|
+
DATABASE
|
|
14835
14881
|
db stats Show database statistics
|
|
14836
14882
|
db search <query> Search conversation history
|
|
14883
|
+
db reset --force Delete all data
|
|
14884
|
+
db vacuum Compact and reclaim space
|
|
14885
|
+
db tables Show table row counts
|
|
14837
14886
|
|
|
14838
|
-
|
|
14839
|
-
import
|
|
14840
|
-
|
|
14841
|
-
logs [options] Query agent visibility logs
|
|
14887
|
+
SESSION IMPORT
|
|
14888
|
+
import sessions Import from AI providers (claude, codex, gemini)
|
|
14889
|
+
import status Show import status
|
|
14842
14890
|
|
|
14843
|
-
|
|
14891
|
+
SECRETS
|
|
14892
|
+
secrets set <name> Set a secret
|
|
14893
|
+
secrets list List configured secrets
|
|
14894
|
+
secrets remove <name> Remove a secret
|
|
14844
14895
|
|
|
14845
14896
|
OPTIONS
|
|
14846
14897
|
-h, --help Show help
|
|
14847
14898
|
-v, --version Show version
|
|
14848
14899
|
--verbose Verbose output
|
|
14849
14900
|
--json Output as JSON
|
|
14850
|
-
--force Force operation
|
|
14901
|
+
--force Force operation
|
|
14851
14902
|
|
|
14852
14903
|
EXAMPLES
|
|
14853
|
-
rudi
|
|
14854
|
-
rudi
|
|
14855
|
-
rudi
|
|
14856
|
-
rudi
|
|
14857
|
-
rudi
|
|
14858
|
-
|
|
14859
|
-
PACKAGE
|
|
14860
|
-
stack:name
|
|
14861
|
-
|
|
14862
|
-
|
|
14863
|
-
|
|
14864
|
-
|
|
14865
|
-
|
|
14866
|
-
If no namespace is given, 'stack:' is assumed.
|
|
14904
|
+
rudi home Show ~/.rudi structure
|
|
14905
|
+
rudi runtimes List installed runtimes
|
|
14906
|
+
rudi install runtime:python Install Python in ~/.rudi
|
|
14907
|
+
rudi install binary:ffmpeg Install ffmpeg
|
|
14908
|
+
rudi doctor --all Show all available deps
|
|
14909
|
+
|
|
14910
|
+
PACKAGE TYPES
|
|
14911
|
+
stack:name MCP server stack
|
|
14912
|
+
runtime:name Node, Python, Deno, Bun
|
|
14913
|
+
binary:name ffmpeg, ripgrep, etc.
|
|
14914
|
+
agent:name Claude, Codex, Gemini CLIs
|
|
14915
|
+
prompt:name Prompt template
|
|
14867
14916
|
`);
|
|
14868
14917
|
}
|
|
14869
14918
|
function printCommandHelp(command) {
|
|
@@ -14932,12 +14981,15 @@ ARGUMENTS
|
|
|
14932
14981
|
|
|
14933
14982
|
OPTIONS
|
|
14934
14983
|
--json Output as JSON
|
|
14984
|
+
--detected Show MCP servers from agent configs (stacks only)
|
|
14985
|
+
--category=X Filter prompts by category
|
|
14935
14986
|
|
|
14936
14987
|
EXAMPLES
|
|
14937
14988
|
rudi list
|
|
14938
14989
|
rudi list stacks
|
|
14990
|
+
rudi list stacks --detected Show MCP servers in Claude/Gemini/Codex
|
|
14939
14991
|
rudi list binaries
|
|
14940
|
-
rudi list
|
|
14992
|
+
rudi list prompts --category=coding
|
|
14941
14993
|
`,
|
|
14942
14994
|
secrets: `
|
|
14943
14995
|
rudi secrets - Manage secrets
|
|
@@ -15007,6 +15059,54 @@ EXAMPLES
|
|
|
15007
15059
|
rudi import sessions claude Import only Claude sessions
|
|
15008
15060
|
rudi import sessions --dry-run Preview without importing
|
|
15009
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
|
|
15089
|
+
`,
|
|
15090
|
+
home: `
|
|
15091
|
+
rudi home - Show ~/.rudi structure and status
|
|
15092
|
+
|
|
15093
|
+
USAGE
|
|
15094
|
+
rudi home [options]
|
|
15095
|
+
|
|
15096
|
+
OPTIONS
|
|
15097
|
+
--verbose Show package details
|
|
15098
|
+
--json Output as JSON
|
|
15099
|
+
|
|
15100
|
+
SHOWS
|
|
15101
|
+
- Directory structure with sizes
|
|
15102
|
+
- Installed package counts
|
|
15103
|
+
- Database status
|
|
15104
|
+
- Quick commands reference
|
|
15105
|
+
|
|
15106
|
+
EXAMPLES
|
|
15107
|
+
rudi home
|
|
15108
|
+
rudi home --verbose
|
|
15109
|
+
rudi home --json
|
|
15010
15110
|
`,
|
|
15011
15111
|
doctor: `
|
|
15012
15112
|
rudi doctor - System health check
|
|
@@ -15016,13 +15116,20 @@ USAGE
|
|
|
15016
15116
|
|
|
15017
15117
|
OPTIONS
|
|
15018
15118
|
--fix Attempt to fix issues
|
|
15119
|
+
--all Show all available runtimes/binaries from registry
|
|
15019
15120
|
|
|
15020
15121
|
CHECKS
|
|
15021
15122
|
- Directory structure
|
|
15022
15123
|
- Database integrity
|
|
15023
15124
|
- Installed packages
|
|
15024
|
-
-
|
|
15125
|
+
- Available runtimes (node, python, deno, bun)
|
|
15126
|
+
- Available binaries (ffmpeg, ripgrep, etc.)
|
|
15025
15127
|
- Secrets configuration
|
|
15128
|
+
|
|
15129
|
+
EXAMPLES
|
|
15130
|
+
rudi doctor
|
|
15131
|
+
rudi doctor --fix
|
|
15132
|
+
rudi doctor --all
|
|
15026
15133
|
`,
|
|
15027
15134
|
logs: `
|
|
15028
15135
|
rudi logs - Query agent visibility logs
|
|
@@ -15280,7 +15387,7 @@ async function installSinglePackage(pkg, options = {}) {
|
|
|
15280
15387
|
onProgress?.({ phase: "downloading", package: pkg.id });
|
|
15281
15388
|
if (pkg.npmPackage) {
|
|
15282
15389
|
try {
|
|
15283
|
-
const { execSync:
|
|
15390
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
15284
15391
|
if (!import_fs4.default.existsSync(installPath)) {
|
|
15285
15392
|
import_fs4.default.mkdirSync(installPath, { recursive: true });
|
|
15286
15393
|
}
|
|
@@ -15288,16 +15395,16 @@ async function installSinglePackage(pkg, options = {}) {
|
|
|
15288
15395
|
const resourcesPath = process.env.RESOURCES_PATH;
|
|
15289
15396
|
const npmCmd = resourcesPath ? import_path4.default.join(resourcesPath, "bundled-runtimes", "node", "bin", "npm") : "npm";
|
|
15290
15397
|
if (!import_fs4.default.existsSync(import_path4.default.join(installPath, "package.json"))) {
|
|
15291
|
-
|
|
15398
|
+
execSync7(`"${npmCmd}" init -y`, { cwd: installPath, stdio: "pipe" });
|
|
15292
15399
|
}
|
|
15293
|
-
|
|
15400
|
+
execSync7(`"${npmCmd}" install ${pkg.npmPackage}`, { cwd: installPath, stdio: "pipe" });
|
|
15294
15401
|
if (pkg.postInstall) {
|
|
15295
15402
|
onProgress?.({ phase: "postInstall", package: pkg.id, message: pkg.postInstall });
|
|
15296
15403
|
const postInstallCmd = pkg.postInstall.replace(
|
|
15297
15404
|
/^npx\s+(\S+)/,
|
|
15298
15405
|
`"${import_path4.default.join(installPath, "node_modules", ".bin", "$1")}"`
|
|
15299
15406
|
);
|
|
15300
|
-
|
|
15407
|
+
execSync7(postInstallCmd, { cwd: installPath, stdio: "pipe" });
|
|
15301
15408
|
}
|
|
15302
15409
|
import_fs4.default.writeFileSync(
|
|
15303
15410
|
import_path4.default.join(installPath, "manifest.json"),
|
|
@@ -15319,15 +15426,15 @@ async function installSinglePackage(pkg, options = {}) {
|
|
|
15319
15426
|
}
|
|
15320
15427
|
if (pkg.pipPackage) {
|
|
15321
15428
|
try {
|
|
15322
|
-
const { execSync:
|
|
15429
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
15323
15430
|
if (!import_fs4.default.existsSync(installPath)) {
|
|
15324
15431
|
import_fs4.default.mkdirSync(installPath, { recursive: true });
|
|
15325
15432
|
}
|
|
15326
15433
|
onProgress?.({ phase: "installing", package: pkg.id, message: `pip install ${pkg.pipPackage}` });
|
|
15327
15434
|
const pythonPath = import_path4.default.join(PATHS.runtimes, "python", "bin", "python3");
|
|
15328
15435
|
const pythonCmd = import_fs4.default.existsSync(pythonPath) ? pythonPath : "python3";
|
|
15329
|
-
|
|
15330
|
-
|
|
15436
|
+
execSync7(`"${pythonCmd}" -m venv "${installPath}/venv"`, { stdio: "pipe" });
|
|
15437
|
+
execSync7(`"${installPath}/venv/bin/pip" install ${pkg.pipPackage}`, { stdio: "pipe" });
|
|
15331
15438
|
import_fs4.default.writeFileSync(
|
|
15332
15439
|
import_path4.default.join(installPath, "manifest.json"),
|
|
15333
15440
|
JSON.stringify({
|
|
@@ -15549,7 +15656,7 @@ async function listInstalled(kind) {
|
|
|
15549
15656
|
return packages;
|
|
15550
15657
|
}
|
|
15551
15658
|
async function installStackDependencies(stackPath, onProgress) {
|
|
15552
|
-
const { execSync:
|
|
15659
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
15553
15660
|
const nodePath = import_path4.default.join(stackPath, "node");
|
|
15554
15661
|
if (import_fs4.default.existsSync(nodePath)) {
|
|
15555
15662
|
const packageJsonPath = import_path4.default.join(nodePath, "package.json");
|
|
@@ -15557,7 +15664,7 @@ async function installStackDependencies(stackPath, onProgress) {
|
|
|
15557
15664
|
onProgress?.({ phase: "installing-deps", message: "Installing Node.js dependencies..." });
|
|
15558
15665
|
try {
|
|
15559
15666
|
const npmCmd = await findNpmExecutable();
|
|
15560
|
-
|
|
15667
|
+
execSync7(`"${npmCmd}" install`, { cwd: nodePath, stdio: "pipe" });
|
|
15561
15668
|
} catch (error) {
|
|
15562
15669
|
console.warn(`Warning: Failed to install Node.js dependencies: ${error.message}`);
|
|
15563
15670
|
}
|
|
@@ -15570,8 +15677,8 @@ async function installStackDependencies(stackPath, onProgress) {
|
|
|
15570
15677
|
onProgress?.({ phase: "installing-deps", message: "Installing Python dependencies..." });
|
|
15571
15678
|
try {
|
|
15572
15679
|
const pythonCmd = await findPythonExecutable();
|
|
15573
|
-
|
|
15574
|
-
|
|
15680
|
+
execSync7(`"${pythonCmd}" -m venv venv`, { cwd: pythonPath, stdio: "pipe" });
|
|
15681
|
+
execSync7("./venv/bin/pip install -r requirements.txt", { cwd: pythonPath, stdio: "pipe" });
|
|
15575
15682
|
} catch (error) {
|
|
15576
15683
|
console.warn(`Warning: Failed to install Python dependencies: ${error.message}`);
|
|
15577
15684
|
}
|
|
@@ -15923,407 +16030,319 @@ Total: ${totalCount} package(s) available`);
|
|
|
15923
16030
|
}
|
|
15924
16031
|
|
|
15925
16032
|
// src/commands/install.js
|
|
15926
|
-
var
|
|
15927
|
-
var
|
|
16033
|
+
var fs8 = __toESM(require("fs/promises"), 1);
|
|
16034
|
+
var path8 = __toESM(require("path"), 1);
|
|
16035
|
+
var import_child_process2 = require("child_process");
|
|
15928
16036
|
|
|
15929
|
-
// src/
|
|
15930
|
-
var fs6 = __toESM(require("fs
|
|
16037
|
+
// src/commands/secrets-store.js
|
|
16038
|
+
var fs6 = __toESM(require("fs"), 1);
|
|
15931
16039
|
var path6 = __toESM(require("path"), 1);
|
|
15932
|
-
|
|
15933
|
-
var
|
|
15934
|
-
|
|
15935
|
-
|
|
15936
|
-
|
|
15937
|
-
|
|
15938
|
-
}
|
|
15939
|
-
|
|
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();
|
|
15940
16058
|
try {
|
|
15941
|
-
const content =
|
|
16059
|
+
const content = fs6.readFileSync(SECRETS_FILE, "utf-8");
|
|
15942
16060
|
return JSON.parse(content);
|
|
15943
16061
|
} catch {
|
|
15944
16062
|
return {};
|
|
15945
16063
|
}
|
|
15946
16064
|
}
|
|
15947
|
-
|
|
15948
|
-
|
|
15949
|
-
|
|
15950
|
-
|
|
16065
|
+
function saveSecrets(secrets) {
|
|
16066
|
+
ensureSecretsFile();
|
|
16067
|
+
fs6.writeFileSync(SECRETS_FILE, JSON.stringify(secrets, null, 2), {
|
|
16068
|
+
encoding: "utf-8",
|
|
16069
|
+
mode: 384
|
|
16070
|
+
});
|
|
15951
16071
|
}
|
|
15952
|
-
function
|
|
15953
|
-
|
|
15954
|
-
|
|
15955
|
-
}
|
|
15956
|
-
if (value.startsWith("[") && value.endsWith("]")) {
|
|
15957
|
-
const inner = value.slice(1, -1).trim();
|
|
15958
|
-
if (!inner) return [];
|
|
15959
|
-
const items = [];
|
|
15960
|
-
let current = "";
|
|
15961
|
-
let inQuote = false;
|
|
15962
|
-
let quoteChar = "";
|
|
15963
|
-
for (const char of inner) {
|
|
15964
|
-
if ((char === '"' || char === "'") && !inQuote) {
|
|
15965
|
-
inQuote = true;
|
|
15966
|
-
quoteChar = char;
|
|
15967
|
-
} else if (char === quoteChar && inQuote) {
|
|
15968
|
-
inQuote = false;
|
|
15969
|
-
items.push(current);
|
|
15970
|
-
current = "";
|
|
15971
|
-
} else if (char === "," && !inQuote) {
|
|
15972
|
-
} else if (inQuote) {
|
|
15973
|
-
current += char;
|
|
15974
|
-
}
|
|
15975
|
-
}
|
|
15976
|
-
return items;
|
|
15977
|
-
}
|
|
15978
|
-
if (value === "true") return true;
|
|
15979
|
-
if (value === "false") return false;
|
|
15980
|
-
const num = Number(value);
|
|
15981
|
-
if (!isNaN(num)) return num;
|
|
15982
|
-
return value;
|
|
16072
|
+
async function getSecret(name) {
|
|
16073
|
+
const secrets = loadSecrets();
|
|
16074
|
+
return secrets[name] || null;
|
|
15983
16075
|
}
|
|
15984
|
-
function
|
|
15985
|
-
const
|
|
15986
|
-
|
|
15987
|
-
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
|
|
15993
|
-
|
|
15994
|
-
|
|
15995
|
-
|
|
15996
|
-
|
|
15997
|
-
|
|
15998
|
-
|
|
15999
|
-
|
|
16000
|
-
|
|
16001
|
-
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16005
|
-
|
|
16006
|
-
|
|
16007
|
-
|
|
16008
|
-
|
|
16009
|
-
|
|
16010
|
-
|
|
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)";
|
|
16011
16106
|
}
|
|
16012
16107
|
}
|
|
16013
|
-
return
|
|
16108
|
+
return masked;
|
|
16014
16109
|
}
|
|
16015
|
-
function
|
|
16016
|
-
|
|
16017
|
-
|
|
16018
|
-
|
|
16019
|
-
|
|
16020
|
-
|
|
16021
|
-
}
|
|
16022
|
-
if (typeof value === "number") {
|
|
16023
|
-
return String(value);
|
|
16024
|
-
}
|
|
16025
|
-
if (Array.isArray(value)) {
|
|
16026
|
-
const items = value.map((v) => tomlValue(v));
|
|
16027
|
-
return `[${items.join(", ")}]`;
|
|
16028
|
-
}
|
|
16029
|
-
return String(value);
|
|
16110
|
+
function getStorageInfo() {
|
|
16111
|
+
return {
|
|
16112
|
+
backend: "file",
|
|
16113
|
+
file: SECRETS_FILE,
|
|
16114
|
+
permissions: "0600 (owner read/write only)"
|
|
16115
|
+
};
|
|
16030
16116
|
}
|
|
16031
|
-
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
|
|
16035
|
-
|
|
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"]
|
|
16036
16132
|
}
|
|
16037
|
-
}
|
|
16038
|
-
|
|
16039
|
-
|
|
16040
|
-
|
|
16041
|
-
|
|
16042
|
-
|
|
16043
|
-
|
|
16044
|
-
|
|
16045
|
-
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
|
|
16049
|
-
|
|
16050
|
-
|
|
16051
|
-
|
|
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;
|
|
16052
16236
|
}
|
|
16053
16237
|
}
|
|
16054
|
-
return
|
|
16238
|
+
return null;
|
|
16055
16239
|
}
|
|
16056
|
-
|
|
16240
|
+
function readAgentMcpServers(agentConfig) {
|
|
16241
|
+
const configPath = findAgentConfig(agentConfig);
|
|
16242
|
+
if (!configPath) return [];
|
|
16057
16243
|
try {
|
|
16058
|
-
const content =
|
|
16059
|
-
|
|
16060
|
-
|
|
16061
|
-
|
|
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 [];
|
|
16062
16262
|
}
|
|
16063
16263
|
}
|
|
16064
|
-
|
|
16065
|
-
const
|
|
16066
|
-
|
|
16067
|
-
|
|
16264
|
+
function detectAllMcpServers() {
|
|
16265
|
+
const servers = [];
|
|
16266
|
+
for (const agentConfig of AGENT_CONFIGS2) {
|
|
16267
|
+
const agentServers = readAgentMcpServers(agentConfig);
|
|
16268
|
+
servers.push(...agentServers);
|
|
16269
|
+
}
|
|
16270
|
+
return servers;
|
|
16068
16271
|
}
|
|
16069
|
-
function
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
|
|
16076
|
-
|
|
16077
|
-
|
|
16078
|
-
|
|
16079
|
-
|
|
16080
|
-
|
|
16081
|
-
|
|
16082
|
-
|
|
16083
|
-
|
|
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
|
+
}));
|
|
16278
|
+
}
|
|
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
|
+
};
|
|
16084
16291
|
}
|
|
16085
16292
|
}
|
|
16086
|
-
return
|
|
16293
|
+
return summary;
|
|
16087
16294
|
}
|
|
16088
|
-
|
|
16089
|
-
|
|
16295
|
+
|
|
16296
|
+
// src/commands/install.js
|
|
16297
|
+
async function loadManifest(installPath) {
|
|
16298
|
+
const manifestPath = path8.join(installPath, "manifest.json");
|
|
16090
16299
|
try {
|
|
16091
|
-
const content = await
|
|
16092
|
-
return
|
|
16300
|
+
const content = await fs8.readFile(manifestPath, "utf-8");
|
|
16301
|
+
return JSON.parse(content);
|
|
16093
16302
|
} catch {
|
|
16094
|
-
return
|
|
16303
|
+
return null;
|
|
16095
16304
|
}
|
|
16096
16305
|
}
|
|
16097
|
-
async function
|
|
16098
|
-
|
|
16099
|
-
let args = [];
|
|
16100
|
-
const cwd = installPath;
|
|
16101
|
-
const resolveRelativePath2 = (value) => {
|
|
16102
|
-
if (!value || path6.isAbsolute(value)) return value;
|
|
16103
|
-
const isPathLike = value.startsWith(".") || value.includes("/") || value.includes("\\");
|
|
16104
|
-
return isPathLike ? path6.join(installPath, value) : value;
|
|
16105
|
-
};
|
|
16106
|
-
if (manifest.command) {
|
|
16107
|
-
const cmdArray = Array.isArray(manifest.command) ? manifest.command : [manifest.command];
|
|
16108
|
-
command = resolveRelativePath2(cmdArray[0]);
|
|
16109
|
-
args = cmdArray.slice(1).map((arg) => resolveRelativePath2(arg));
|
|
16110
|
-
} else if (manifest.mcp) {
|
|
16111
|
-
command = resolveRelativePath2(manifest.mcp.command);
|
|
16112
|
-
args = (manifest.mcp.args || []).map((arg) => resolveRelativePath2(arg));
|
|
16113
|
-
if (manifest.mcp.entry) {
|
|
16114
|
-
args = args.map(
|
|
16115
|
-
(arg) => arg === manifest.mcp.entry ? path6.join(installPath, manifest.mcp.entry) : arg
|
|
16116
|
-
);
|
|
16117
|
-
}
|
|
16118
|
-
} else {
|
|
16119
|
-
return null;
|
|
16120
|
-
}
|
|
16121
|
-
const optimized = await optimizeEntryPoint(installPath, command, args);
|
|
16122
|
-
if (optimized) {
|
|
16123
|
-
command = optimized.command;
|
|
16124
|
-
args = optimized.args;
|
|
16125
|
-
}
|
|
16126
|
-
const env = await readStackEnv(installPath);
|
|
16127
|
-
const config = {
|
|
16128
|
-
command,
|
|
16129
|
-
cwd
|
|
16130
|
-
};
|
|
16131
|
-
if (args.length > 0) {
|
|
16132
|
-
config.args = args;
|
|
16133
|
-
}
|
|
16134
|
-
if (Object.keys(env).length > 0) {
|
|
16135
|
-
config.env = env;
|
|
16136
|
-
}
|
|
16137
|
-
return config;
|
|
16138
|
-
}
|
|
16139
|
-
async function optimizeEntryPoint(installPath, command, args) {
|
|
16140
|
-
if (command !== "npx" || !args.includes("tsx")) {
|
|
16141
|
-
return null;
|
|
16142
|
-
}
|
|
16143
|
-
const tsFileIndex = args.findIndex((arg) => arg.endsWith(".ts"));
|
|
16144
|
-
if (tsFileIndex === -1) {
|
|
16145
|
-
return null;
|
|
16146
|
-
}
|
|
16147
|
-
const tsFile = args[tsFileIndex];
|
|
16148
|
-
const jsFile = tsFile.replace("/src/", "/dist/").replace(".ts", ".js");
|
|
16149
|
-
const jsPath = path6.isAbsolute(jsFile) ? jsFile : path6.join(installPath, jsFile);
|
|
16150
|
-
try {
|
|
16151
|
-
await fs6.access(jsPath);
|
|
16152
|
-
return {
|
|
16153
|
-
command: "node",
|
|
16154
|
-
args: [jsPath]
|
|
16155
|
-
};
|
|
16156
|
-
} catch {
|
|
16157
|
-
return null;
|
|
16158
|
-
}
|
|
16159
|
-
}
|
|
16160
|
-
async function registerMcpClaude(stackId, installPath, manifest) {
|
|
16161
|
-
const configPath = AGENT_CONFIGS.claude;
|
|
16162
|
-
try {
|
|
16163
|
-
const mcpConfig = await buildMcpConfig(stackId, installPath, manifest);
|
|
16164
|
-
if (!mcpConfig) {
|
|
16165
|
-
return { success: true, skipped: true };
|
|
16166
|
-
}
|
|
16167
|
-
mcpConfig.type = "stdio";
|
|
16168
|
-
const settings = await readJson(configPath);
|
|
16169
|
-
if (!settings.mcpServers) {
|
|
16170
|
-
settings.mcpServers = {};
|
|
16171
|
-
}
|
|
16172
|
-
settings.mcpServers[stackId] = mcpConfig;
|
|
16173
|
-
await writeJson(configPath, settings);
|
|
16174
|
-
console.log(` Registered MCP in Claude: ${stackId}`);
|
|
16175
|
-
return { success: true };
|
|
16176
|
-
} catch (error) {
|
|
16177
|
-
console.error(` Failed to register MCP in Claude: ${error.message}`);
|
|
16178
|
-
return { success: false, error: error.message };
|
|
16179
|
-
}
|
|
16180
|
-
}
|
|
16181
|
-
async function unregisterMcpClaude(stackId) {
|
|
16182
|
-
const configPath = AGENT_CONFIGS.claude;
|
|
16183
|
-
try {
|
|
16184
|
-
const settings = await readJson(configPath);
|
|
16185
|
-
if (!settings.mcpServers || !settings.mcpServers[stackId]) {
|
|
16186
|
-
return { success: true, skipped: true };
|
|
16187
|
-
}
|
|
16188
|
-
delete settings.mcpServers[stackId];
|
|
16189
|
-
await writeJson(configPath, settings);
|
|
16190
|
-
console.log(` Unregistered MCP from Claude: ${stackId}`);
|
|
16191
|
-
return { success: true };
|
|
16192
|
-
} catch (error) {
|
|
16193
|
-
console.error(` Failed to unregister MCP from Claude: ${error.message}`);
|
|
16194
|
-
return { success: false, error: error.message };
|
|
16195
|
-
}
|
|
16196
|
-
}
|
|
16197
|
-
async function registerMcpCodex(stackId, installPath, manifest) {
|
|
16198
|
-
const configPath = AGENT_CONFIGS.codex;
|
|
16199
|
-
try {
|
|
16200
|
-
const mcpConfig = await buildMcpConfig(stackId, installPath, manifest);
|
|
16201
|
-
if (!mcpConfig) {
|
|
16202
|
-
return { success: true, skipped: true };
|
|
16203
|
-
}
|
|
16204
|
-
const config = await readToml(configPath);
|
|
16205
|
-
if (!config.mcp_servers) {
|
|
16206
|
-
config.mcp_servers = {};
|
|
16207
|
-
}
|
|
16208
|
-
config.mcp_servers[stackId] = mcpConfig;
|
|
16209
|
-
await writeToml(configPath, config);
|
|
16210
|
-
console.log(` Registered MCP in Codex: ${stackId}`);
|
|
16211
|
-
return { success: true };
|
|
16212
|
-
} catch (error) {
|
|
16213
|
-
console.error(` Failed to register MCP in Codex: ${error.message}`);
|
|
16214
|
-
return { success: false, error: error.message };
|
|
16215
|
-
}
|
|
16216
|
-
}
|
|
16217
|
-
async function unregisterMcpCodex(stackId) {
|
|
16218
|
-
const configPath = AGENT_CONFIGS.codex;
|
|
16219
|
-
try {
|
|
16220
|
-
const config = await readToml(configPath);
|
|
16221
|
-
if (!config.mcp_servers || !config.mcp_servers[stackId]) {
|
|
16222
|
-
return { success: true, skipped: true };
|
|
16223
|
-
}
|
|
16224
|
-
delete config.mcp_servers[stackId];
|
|
16225
|
-
await writeToml(configPath, config);
|
|
16226
|
-
console.log(` Unregistered MCP from Codex: ${stackId}`);
|
|
16227
|
-
return { success: true };
|
|
16228
|
-
} catch (error) {
|
|
16229
|
-
console.error(` Failed to unregister MCP from Codex: ${error.message}`);
|
|
16230
|
-
return { success: false, error: error.message };
|
|
16231
|
-
}
|
|
16232
|
-
}
|
|
16233
|
-
async function registerMcpGemini(stackId, installPath, manifest) {
|
|
16234
|
-
const configPath = AGENT_CONFIGS.gemini;
|
|
16235
|
-
try {
|
|
16236
|
-
const mcpConfig = await buildMcpConfig(stackId, installPath, manifest);
|
|
16237
|
-
if (!mcpConfig) {
|
|
16238
|
-
return { success: true, skipped: true };
|
|
16239
|
-
}
|
|
16240
|
-
const settings = await readJson(configPath);
|
|
16241
|
-
if (!settings.mcpServers) {
|
|
16242
|
-
settings.mcpServers = {};
|
|
16243
|
-
}
|
|
16244
|
-
settings.mcpServers[stackId] = mcpConfig;
|
|
16245
|
-
await writeJson(configPath, settings);
|
|
16246
|
-
console.log(` Registered MCP in Gemini: ${stackId}`);
|
|
16247
|
-
return { success: true };
|
|
16248
|
-
} catch (error) {
|
|
16249
|
-
console.error(` Failed to register MCP in Gemini: ${error.message}`);
|
|
16250
|
-
return { success: false, error: error.message };
|
|
16251
|
-
}
|
|
16252
|
-
}
|
|
16253
|
-
async function unregisterMcpGemini(stackId) {
|
|
16254
|
-
const configPath = AGENT_CONFIGS.gemini;
|
|
16306
|
+
async function installDependencies(stackPath, manifest) {
|
|
16307
|
+
const runtime = manifest?.runtime || "node";
|
|
16255
16308
|
try {
|
|
16256
|
-
|
|
16257
|
-
|
|
16258
|
-
|
|
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 };
|
|
16259
16342
|
}
|
|
16260
|
-
|
|
16261
|
-
await writeJson(configPath, settings);
|
|
16262
|
-
console.log(` Unregistered MCP from Gemini: ${stackId}`);
|
|
16263
|
-
return { success: true };
|
|
16343
|
+
return { installed: false, reason: `Unknown runtime: ${runtime}` };
|
|
16264
16344
|
} catch (error) {
|
|
16265
|
-
|
|
16266
|
-
return { success: false, error: error.message };
|
|
16267
|
-
}
|
|
16268
|
-
}
|
|
16269
|
-
async function getInstalledAgents() {
|
|
16270
|
-
const agentsDir = path6.join(HOME, ".rudi", "agents");
|
|
16271
|
-
const installed = [];
|
|
16272
|
-
for (const agent of ["claude", "codex", "gemini"]) {
|
|
16273
|
-
const agentPath = path6.join(agentsDir, agent);
|
|
16274
|
-
try {
|
|
16275
|
-
await fs6.access(agentPath);
|
|
16276
|
-
installed.push(agent);
|
|
16277
|
-
} catch {
|
|
16278
|
-
}
|
|
16279
|
-
}
|
|
16280
|
-
return installed;
|
|
16281
|
-
}
|
|
16282
|
-
async function registerMcpAll(stackId, installPath, manifest, targetAgents = null) {
|
|
16283
|
-
const agents = targetAgents || await getInstalledAgents();
|
|
16284
|
-
const results = {};
|
|
16285
|
-
for (const agent of agents) {
|
|
16286
|
-
switch (agent) {
|
|
16287
|
-
case "claude":
|
|
16288
|
-
results.claude = await registerMcpClaude(stackId, installPath, manifest);
|
|
16289
|
-
break;
|
|
16290
|
-
case "codex":
|
|
16291
|
-
results.codex = await registerMcpCodex(stackId, installPath, manifest);
|
|
16292
|
-
break;
|
|
16293
|
-
case "gemini":
|
|
16294
|
-
results.gemini = await registerMcpGemini(stackId, installPath, manifest);
|
|
16295
|
-
break;
|
|
16296
|
-
}
|
|
16297
|
-
}
|
|
16298
|
-
return results;
|
|
16299
|
-
}
|
|
16300
|
-
async function unregisterMcpAll(stackId, targetAgents = null) {
|
|
16301
|
-
const agents = targetAgents || await getInstalledAgents();
|
|
16302
|
-
const results = {};
|
|
16303
|
-
for (const agent of agents) {
|
|
16304
|
-
switch (agent) {
|
|
16305
|
-
case "claude":
|
|
16306
|
-
results.claude = await unregisterMcpClaude(stackId);
|
|
16307
|
-
break;
|
|
16308
|
-
case "codex":
|
|
16309
|
-
results.codex = await unregisterMcpCodex(stackId);
|
|
16310
|
-
break;
|
|
16311
|
-
case "gemini":
|
|
16312
|
-
results.gemini = await unregisterMcpGemini(stackId);
|
|
16313
|
-
break;
|
|
16314
|
-
}
|
|
16315
|
-
}
|
|
16316
|
-
return results;
|
|
16317
|
-
}
|
|
16318
|
-
|
|
16319
|
-
// src/commands/install.js
|
|
16320
|
-
async function loadManifest(installPath) {
|
|
16321
|
-
const manifestPath = path7.join(installPath, "manifest.json");
|
|
16322
|
-
try {
|
|
16323
|
-
const content = await fs7.readFile(manifestPath, "utf-8");
|
|
16324
|
-
return JSON.parse(content);
|
|
16325
|
-
} catch {
|
|
16326
|
-
return null;
|
|
16345
|
+
return { installed: false, error: error.message };
|
|
16327
16346
|
}
|
|
16328
16347
|
}
|
|
16329
16348
|
function getManifestSecrets(manifest) {
|
|
@@ -16333,67 +16352,56 @@ function getSecretName(secret) {
|
|
|
16333
16352
|
if (typeof secret === "string") return secret;
|
|
16334
16353
|
return secret.name || secret.key;
|
|
16335
16354
|
}
|
|
16336
|
-
function getSecretDescription(secret) {
|
|
16337
|
-
if (typeof secret !== "object" || !secret) return null;
|
|
16338
|
-
return secret.description || secret.label || null;
|
|
16339
|
-
}
|
|
16340
16355
|
function getSecretLink(secret) {
|
|
16341
16356
|
if (typeof secret !== "object" || !secret) return null;
|
|
16342
16357
|
return secret.link || secret.helpUrl || null;
|
|
16343
16358
|
}
|
|
16344
|
-
function
|
|
16345
|
-
if (typeof secret !== "object" || !secret) return null;
|
|
16346
|
-
return secret.label || secret.name || secret.key || null;
|
|
16347
|
-
}
|
|
16348
|
-
async function createEnvFile(installPath, manifest) {
|
|
16359
|
+
async function checkSecrets(manifest) {
|
|
16349
16360
|
const secrets = getManifestSecrets(manifest);
|
|
16350
|
-
|
|
16351
|
-
const
|
|
16352
|
-
try {
|
|
16353
|
-
await fs7.access(envPath);
|
|
16354
|
-
console.log(` .env file already exists, preserving existing secrets`);
|
|
16355
|
-
return envPath;
|
|
16356
|
-
} catch {
|
|
16357
|
-
}
|
|
16358
|
-
const lines = [];
|
|
16359
|
-
lines.push("# Environment variables for this stack");
|
|
16360
|
-
lines.push("# Fill in your API keys below");
|
|
16361
|
-
lines.push("");
|
|
16361
|
+
const found = [];
|
|
16362
|
+
const missing = [];
|
|
16362
16363
|
for (const secret of secrets) {
|
|
16363
16364
|
const key = getSecretName(secret);
|
|
16364
|
-
const
|
|
16365
|
-
const
|
|
16366
|
-
if (
|
|
16367
|
-
|
|
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);
|
|
16368
16371
|
}
|
|
16369
|
-
|
|
16370
|
-
|
|
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
|
+
}
|
|
16371
16387
|
}
|
|
16372
|
-
|
|
16373
|
-
|
|
16388
|
+
return keys;
|
|
16389
|
+
} catch {
|
|
16390
|
+
return [];
|
|
16374
16391
|
}
|
|
16375
|
-
await fs7.writeFile(envPath, lines.join("\n"), "utf-8");
|
|
16376
|
-
return envPath;
|
|
16377
16392
|
}
|
|
16378
16393
|
async function cmdInstall(args, flags) {
|
|
16379
16394
|
const pkgId = args[0];
|
|
16380
16395
|
if (!pkgId) {
|
|
16381
16396
|
console.error("Usage: rudi install <package>");
|
|
16382
|
-
console.error("
|
|
16383
|
-
console.error("
|
|
16384
|
-
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");
|
|
16385
16402
|
process.exit(1);
|
|
16386
16403
|
}
|
|
16387
16404
|
const force = flags.force || false;
|
|
16388
|
-
let targetAgents = null;
|
|
16389
|
-
if (flags.agent) {
|
|
16390
|
-
const validAgents = ["claude", "codex", "gemini"];
|
|
16391
|
-
targetAgents = flags.agent.split(",").map((a) => a.trim()).filter((a) => validAgents.includes(a));
|
|
16392
|
-
if (targetAgents.length === 0) {
|
|
16393
|
-
console.error(`Invalid --agent value. Valid agents: ${validAgents.join(", ")}`);
|
|
16394
|
-
process.exit(1);
|
|
16395
|
-
}
|
|
16396
|
-
}
|
|
16397
16405
|
console.log(`Resolving ${pkgId}...`);
|
|
16398
16406
|
try {
|
|
16399
16407
|
const resolved = await resolvePackage(pkgId);
|
|
@@ -16416,26 +16424,40 @@ Dependencies:`);
|
|
|
16416
16424
|
console.log(` - ${dep.id} ${status}`);
|
|
16417
16425
|
}
|
|
16418
16426
|
}
|
|
16419
|
-
|
|
16420
|
-
|
|
16421
|
-
Required secrets:`);
|
|
16422
|
-
for (const secret of resolved.requires.secrets) {
|
|
16423
|
-
const name = typeof secret === "string" ? secret : secret.name;
|
|
16424
|
-
console.log(` - ${name}`);
|
|
16425
|
-
}
|
|
16426
|
-
}
|
|
16427
|
+
console.log(`
|
|
16428
|
+
Dependency check:`);
|
|
16427
16429
|
const depCheck = checkAllDependencies(resolved);
|
|
16428
16430
|
if (depCheck.results.length > 0) {
|
|
16429
|
-
console.log(`
|
|
16430
|
-
System dependencies:`);
|
|
16431
16431
|
for (const line of formatDependencyResults(depCheck.results)) {
|
|
16432
16432
|
console.log(line);
|
|
16433
16433
|
}
|
|
16434
|
-
|
|
16435
|
-
|
|
16436
|
-
|
|
16437
|
-
|
|
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
|
+
}
|
|
16450
|
+
}
|
|
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}`);
|
|
16438
16457
|
}
|
|
16458
|
+
console.error(`
|
|
16459
|
+
Or use --force to install anyway.`);
|
|
16460
|
+
process.exit(1);
|
|
16439
16461
|
}
|
|
16440
16462
|
console.log(`
|
|
16441
16463
|
Installing...`);
|
|
@@ -16461,29 +16483,70 @@ Installing...`);
|
|
|
16461
16483
|
if (resolved.kind === "stack") {
|
|
16462
16484
|
const manifest = await loadManifest(result.path);
|
|
16463
16485
|
if (manifest) {
|
|
16464
|
-
const
|
|
16465
|
-
|
|
16466
|
-
|
|
16467
|
-
|
|
16468
|
-
|
|
16469
|
-
|
|
16470
|
-
|
|
16471
|
-
|
|
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) {
|
|
16472
16515
|
console.log(`
|
|
16473
|
-
|
|
16474
|
-
for (const
|
|
16475
|
-
const
|
|
16476
|
-
|
|
16477
|
-
|
|
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
|
+
}
|
|
16478
16526
|
}
|
|
16479
16527
|
console.log(`
|
|
16480
|
-
|
|
16481
|
-
|
|
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`);
|
|
16482
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(", ")}`);
|
|
16542
|
+
}
|
|
16543
|
+
console.log(`
|
|
16544
|
+
3. Restart your agent to use the stack`);
|
|
16545
|
+
return;
|
|
16483
16546
|
}
|
|
16484
16547
|
}
|
|
16485
16548
|
console.log(`
|
|
16486
|
-
|
|
16549
|
+
\u2713 Installed successfully.`);
|
|
16487
16550
|
} else {
|
|
16488
16551
|
console.error(`
|
|
16489
16552
|
\u2717 Installation failed: ${result.error}`);
|
|
@@ -16499,38 +16562,28 @@ Run with: rudi run ${pkgId}`);
|
|
|
16499
16562
|
}
|
|
16500
16563
|
|
|
16501
16564
|
// ../packages/runner/src/spawn.js
|
|
16502
|
-
var
|
|
16503
|
-
var
|
|
16504
|
-
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);
|
|
16505
16568
|
|
|
16506
16569
|
// ../packages/runner/src/secrets.js
|
|
16507
|
-
var
|
|
16508
|
-
var
|
|
16509
|
-
var
|
|
16510
|
-
var SECRETS_PATH =
|
|
16511
|
-
function
|
|
16512
|
-
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)) {
|
|
16513
16576
|
return {};
|
|
16514
16577
|
}
|
|
16515
16578
|
try {
|
|
16516
|
-
const content =
|
|
16579
|
+
const content = import_fs7.default.readFileSync(SECRETS_PATH, "utf-8");
|
|
16517
16580
|
return JSON.parse(content);
|
|
16518
16581
|
} catch {
|
|
16519
16582
|
return {};
|
|
16520
16583
|
}
|
|
16521
16584
|
}
|
|
16522
|
-
function saveSecrets(secrets) {
|
|
16523
|
-
const dir = import_path6.default.dirname(SECRETS_PATH);
|
|
16524
|
-
if (!import_fs6.default.existsSync(dir)) {
|
|
16525
|
-
import_fs6.default.mkdirSync(dir, { recursive: true });
|
|
16526
|
-
}
|
|
16527
|
-
import_fs6.default.writeFileSync(SECRETS_PATH, JSON.stringify(secrets, null, 2), {
|
|
16528
|
-
mode: 384
|
|
16529
|
-
// Read/write only for owner
|
|
16530
|
-
});
|
|
16531
|
-
}
|
|
16532
16585
|
async function getSecrets(required) {
|
|
16533
|
-
const allSecrets =
|
|
16586
|
+
const allSecrets = loadSecrets2();
|
|
16534
16587
|
const result = {};
|
|
16535
16588
|
for (const req of required) {
|
|
16536
16589
|
const name = typeof req === "string" ? req : req.name;
|
|
@@ -16543,8 +16596,8 @@ async function getSecrets(required) {
|
|
|
16543
16596
|
}
|
|
16544
16597
|
return result;
|
|
16545
16598
|
}
|
|
16546
|
-
function
|
|
16547
|
-
const allSecrets =
|
|
16599
|
+
function checkSecrets2(required) {
|
|
16600
|
+
const allSecrets = loadSecrets2();
|
|
16548
16601
|
const missing = [];
|
|
16549
16602
|
for (const req of required) {
|
|
16550
16603
|
const name = typeof req === "string" ? req : req.name;
|
|
@@ -16558,34 +16611,12 @@ function checkSecrets(required) {
|
|
|
16558
16611
|
missing
|
|
16559
16612
|
};
|
|
16560
16613
|
}
|
|
16561
|
-
function setSecret(name, value) {
|
|
16562
|
-
const secrets = loadSecrets();
|
|
16563
|
-
secrets[name] = value;
|
|
16564
|
-
saveSecrets(secrets);
|
|
16565
|
-
}
|
|
16566
|
-
function removeSecret(name) {
|
|
16567
|
-
const secrets = loadSecrets();
|
|
16568
|
-
delete secrets[name];
|
|
16569
|
-
saveSecrets(secrets);
|
|
16570
|
-
}
|
|
16571
16614
|
function listSecretNames() {
|
|
16572
|
-
const secrets =
|
|
16615
|
+
const secrets = loadSecrets2();
|
|
16573
16616
|
return Object.keys(secrets);
|
|
16574
16617
|
}
|
|
16575
|
-
function getMaskedSecrets() {
|
|
16576
|
-
const secrets = loadSecrets();
|
|
16577
|
-
const masked = {};
|
|
16578
|
-
for (const [name, value] of Object.entries(secrets)) {
|
|
16579
|
-
if (typeof value === "string" && value.length > 8) {
|
|
16580
|
-
masked[name] = value.slice(0, 4) + "..." + value.slice(-4);
|
|
16581
|
-
} else {
|
|
16582
|
-
masked[name] = "***";
|
|
16583
|
-
}
|
|
16584
|
-
}
|
|
16585
|
-
return masked;
|
|
16586
|
-
}
|
|
16587
16618
|
function redactSecrets(text, secrets) {
|
|
16588
|
-
const allSecrets = secrets ||
|
|
16619
|
+
const allSecrets = secrets || loadSecrets2();
|
|
16589
16620
|
let result = text;
|
|
16590
16621
|
for (const value of Object.values(allSecrets)) {
|
|
16591
16622
|
if (typeof value === "string" && value.length > 3) {
|
|
@@ -16595,27 +16626,18 @@ function redactSecrets(text, secrets) {
|
|
|
16595
16626
|
}
|
|
16596
16627
|
return result;
|
|
16597
16628
|
}
|
|
16598
|
-
function exportToEnv() {
|
|
16599
|
-
const secrets = loadSecrets();
|
|
16600
|
-
const lines = [];
|
|
16601
|
-
for (const [name, value] of Object.entries(secrets)) {
|
|
16602
|
-
const escaped = String(value).replace(/'/g, "'\\''");
|
|
16603
|
-
lines.push(`export ${name}='${escaped}'`);
|
|
16604
|
-
}
|
|
16605
|
-
return lines.join("\n");
|
|
16606
|
-
}
|
|
16607
16629
|
|
|
16608
16630
|
// ../packages/runner/src/spawn.js
|
|
16609
16631
|
async function runStack(id, options = {}) {
|
|
16610
16632
|
const { inputs = {}, cwd, env = {}, onStdout, onStderr, onExit, signal } = options;
|
|
16611
16633
|
const startTime = Date.now();
|
|
16612
16634
|
const packagePath = getPackagePath(id);
|
|
16613
|
-
const manifestPath =
|
|
16614
|
-
const { default:
|
|
16615
|
-
if (!
|
|
16635
|
+
const manifestPath = import_path8.default.join(packagePath, "manifest.json");
|
|
16636
|
+
const { default: fs26 } = await import("fs");
|
|
16637
|
+
if (!fs26.existsSync(manifestPath)) {
|
|
16616
16638
|
throw new Error(`Stack manifest not found: ${id}`);
|
|
16617
16639
|
}
|
|
16618
|
-
const manifest = JSON.parse(
|
|
16640
|
+
const manifest = JSON.parse(fs26.readFileSync(manifestPath, "utf-8"));
|
|
16619
16641
|
const { command, args } = resolveCommandFromManifest(manifest, packagePath);
|
|
16620
16642
|
const secrets = await getSecrets(manifest.requires?.secrets || []);
|
|
16621
16643
|
const runEnv = {
|
|
@@ -16626,7 +16648,7 @@ async function runStack(id, options = {}) {
|
|
|
16626
16648
|
RUDI_PACKAGE_ID: id,
|
|
16627
16649
|
RUDI_PACKAGE_PATH: packagePath
|
|
16628
16650
|
};
|
|
16629
|
-
const proc = (0,
|
|
16651
|
+
const proc = (0, import_child_process3.spawn)(command, args, {
|
|
16630
16652
|
cwd: cwd || packagePath,
|
|
16631
16653
|
env: runEnv,
|
|
16632
16654
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -16671,15 +16693,15 @@ async function runStack(id, options = {}) {
|
|
|
16671
16693
|
}
|
|
16672
16694
|
function getCommand(runtime) {
|
|
16673
16695
|
const runtimeName = runtime.replace("runtime:", "");
|
|
16674
|
-
const runtimePath =
|
|
16696
|
+
const runtimePath = import_path8.default.join(PATHS.runtimes, runtimeName);
|
|
16675
16697
|
const binaryPaths = [
|
|
16676
|
-
|
|
16677
|
-
|
|
16678
|
-
|
|
16679
|
-
|
|
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)
|
|
16680
16702
|
];
|
|
16681
16703
|
for (const binPath of binaryPaths) {
|
|
16682
|
-
if (
|
|
16704
|
+
if (import_fs8.default.existsSync(binPath)) {
|
|
16683
16705
|
return binPath;
|
|
16684
16706
|
}
|
|
16685
16707
|
}
|
|
@@ -16703,7 +16725,7 @@ function resolveCommandFromManifest(manifest, packagePath) {
|
|
|
16703
16725
|
return { command: command2, args };
|
|
16704
16726
|
}
|
|
16705
16727
|
const entry = manifest.entry || "index.js";
|
|
16706
|
-
const entryPath =
|
|
16728
|
+
const entryPath = import_path8.default.join(packagePath, entry);
|
|
16707
16729
|
const runtime = manifest.runtime || "runtime:node";
|
|
16708
16730
|
const command = getCommand(runtime);
|
|
16709
16731
|
return { command, args: [entryPath] };
|
|
@@ -16712,21 +16734,21 @@ function resolveRelativePath(value, basePath) {
|
|
|
16712
16734
|
if (typeof value !== "string" || value.startsWith("-")) {
|
|
16713
16735
|
return value;
|
|
16714
16736
|
}
|
|
16715
|
-
if (
|
|
16737
|
+
if (import_path8.default.isAbsolute(value)) {
|
|
16716
16738
|
return value;
|
|
16717
16739
|
}
|
|
16718
16740
|
if (value.includes("/") || value.startsWith(".")) {
|
|
16719
|
-
return
|
|
16741
|
+
return import_path8.default.join(basePath, value);
|
|
16720
16742
|
}
|
|
16721
16743
|
return value;
|
|
16722
16744
|
}
|
|
16723
16745
|
|
|
16724
16746
|
// ../packages/manifest/src/stack.js
|
|
16725
16747
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
16726
|
-
var
|
|
16727
|
-
var
|
|
16748
|
+
var import_fs9 = __toESM(require("fs"), 1);
|
|
16749
|
+
var import_path9 = __toESM(require("path"), 1);
|
|
16728
16750
|
function parseStackManifest(filePath) {
|
|
16729
|
-
const content =
|
|
16751
|
+
const content = import_fs9.default.readFileSync(filePath, "utf-8");
|
|
16730
16752
|
return parseStackYaml(content, filePath);
|
|
16731
16753
|
}
|
|
16732
16754
|
function parseStackYaml(content, source = "stack.yaml") {
|
|
@@ -16844,8 +16866,8 @@ function validateStackManifest(manifest, source) {
|
|
|
16844
16866
|
function findStackManifest(dir) {
|
|
16845
16867
|
const candidates = ["stack.yaml", "stack.yml", "manifest.yaml", "manifest.yml"];
|
|
16846
16868
|
for (const filename of candidates) {
|
|
16847
|
-
const filePath =
|
|
16848
|
-
if (
|
|
16869
|
+
const filePath = import_path9.default.join(dir, filename);
|
|
16870
|
+
if (import_fs9.default.existsSync(filePath)) {
|
|
16849
16871
|
return filePath;
|
|
16850
16872
|
}
|
|
16851
16873
|
}
|
|
@@ -17000,8 +17022,8 @@ var validatePromptInternal = ajv.compile(promptSchema);
|
|
|
17000
17022
|
var validateRuntimeInternal = ajv.compile(runtimeSchema);
|
|
17001
17023
|
|
|
17002
17024
|
// src/commands/run.js
|
|
17003
|
-
var
|
|
17004
|
-
var
|
|
17025
|
+
var import_fs10 = __toESM(require("fs"), 1);
|
|
17026
|
+
var import_path10 = __toESM(require("path"), 1);
|
|
17005
17027
|
async function cmdRun(args, flags) {
|
|
17006
17028
|
const stackId = args[0];
|
|
17007
17029
|
if (!stackId) {
|
|
@@ -17022,9 +17044,9 @@ async function cmdRun(args, flags) {
|
|
|
17022
17044
|
if (manifestPath) {
|
|
17023
17045
|
manifest = parseStackManifest(manifestPath);
|
|
17024
17046
|
} else {
|
|
17025
|
-
const jsonPath =
|
|
17026
|
-
if (
|
|
17027
|
-
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"));
|
|
17028
17050
|
}
|
|
17029
17051
|
}
|
|
17030
17052
|
} catch (error) {
|
|
@@ -17038,7 +17060,7 @@ async function cmdRun(args, flags) {
|
|
|
17038
17060
|
console.log(`Running: ${manifest.name || stackId}`);
|
|
17039
17061
|
const requiredSecrets = manifest.requires?.secrets || [];
|
|
17040
17062
|
if (requiredSecrets.length > 0) {
|
|
17041
|
-
const { satisfied, missing } =
|
|
17063
|
+
const { satisfied, missing } = checkSecrets2(requiredSecrets);
|
|
17042
17064
|
if (!satisfied) {
|
|
17043
17065
|
console.error(`
|
|
17044
17066
|
Missing required secrets:`);
|
|
@@ -17115,6 +17137,66 @@ async function cmdList(args, flags) {
|
|
|
17115
17137
|
process.exit(1);
|
|
17116
17138
|
}
|
|
17117
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
|
+
}
|
|
17118
17200
|
try {
|
|
17119
17201
|
let packages = await listInstalled(kind);
|
|
17120
17202
|
const categoryFilter = flags.category;
|
|
@@ -17167,45 +17249,244 @@ Total: ${packages.length} prompt(s)`);
|
|
|
17167
17249
|
Filter by category: rudi list prompts --category=coding`);
|
|
17168
17250
|
return;
|
|
17169
17251
|
}
|
|
17170
|
-
const grouped = {
|
|
17171
|
-
stack: packages.filter((p) => p.kind === "stack"),
|
|
17172
|
-
prompt: packages.filter((p) => p.kind === "prompt"),
|
|
17173
|
-
runtime: packages.filter((p) => p.kind === "runtime"),
|
|
17174
|
-
binary: packages.filter((p) => p.kind === "binary"),
|
|
17175
|
-
agent: packages.filter((p) => p.kind === "agent")
|
|
17176
|
-
};
|
|
17177
|
-
let total = 0;
|
|
17178
|
-
for (const [pkgKind, pkgs] of Object.entries(grouped)) {
|
|
17179
|
-
if (pkgs.length === 0) continue;
|
|
17180
|
-
if (kind && kind !== pkgKind) continue;
|
|
17181
|
-
console.log(`
|
|
17182
|
-
${headingForKind2(pkgKind)} (${pkgs.length}):`);
|
|
17183
|
-
console.log("\u2500".repeat(50));
|
|
17184
|
-
for (const pkg of pkgs) {
|
|
17185
|
-
const icon = pkg.icon ? `${pkg.icon} ` : "";
|
|
17186
|
-
console.log(` ${icon}${pkg.id || `${pkgKind}:${pkg.name}`}`);
|
|
17187
|
-
console.log(` Version: ${pkg.version || "unknown"}`);
|
|
17188
|
-
if (pkg.description) {
|
|
17189
|
-
console.log(` ${pkg.description}`);
|
|
17190
|
-
}
|
|
17191
|
-
if (pkg.category) {
|
|
17192
|
-
console.log(` Category: ${pkg.category}`);
|
|
17193
|
-
}
|
|
17194
|
-
if (pkg.tags && pkg.tags.length > 0) {
|
|
17195
|
-
console.log(` Tags: ${pkg.tags.join(", ")}`);
|
|
17196
|
-
}
|
|
17197
|
-
if (pkg.installedAt) {
|
|
17198
|
-
console.log(` Installed: ${new Date(pkg.installedAt).toLocaleDateString()}`);
|
|
17199
|
-
}
|
|
17200
|
-
total++;
|
|
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);
|
|
17201
17410
|
}
|
|
17202
17411
|
}
|
|
17203
|
-
|
|
17204
|
-
|
|
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 };
|
|
17434
|
+
}
|
|
17435
|
+
delete config.mcp_servers[stackId];
|
|
17436
|
+
await writeToml(configPath, config);
|
|
17437
|
+
console.log(` Unregistered MCP from Codex: ${stackId}`);
|
|
17438
|
+
return { success: true };
|
|
17205
17439
|
} catch (error) {
|
|
17206
|
-
console.error(`Failed to
|
|
17207
|
-
|
|
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);
|
|
17208
17488
|
}
|
|
17489
|
+
return results;
|
|
17209
17490
|
}
|
|
17210
17491
|
|
|
17211
17492
|
// src/commands/remove.js
|
|
@@ -17344,89 +17625,136 @@ async function cmdSecrets(args, flags) {
|
|
|
17344
17625
|
case "set":
|
|
17345
17626
|
await secretsSet(args.slice(1), flags);
|
|
17346
17627
|
break;
|
|
17628
|
+
case "get":
|
|
17629
|
+
await secretsGet(args.slice(1), flags);
|
|
17630
|
+
break;
|
|
17347
17631
|
case "list":
|
|
17348
17632
|
case "ls":
|
|
17349
|
-
secretsList(flags);
|
|
17633
|
+
await secretsList(flags);
|
|
17350
17634
|
break;
|
|
17351
17635
|
case "remove":
|
|
17352
17636
|
case "rm":
|
|
17353
17637
|
case "delete":
|
|
17354
|
-
secretsRemove(args.slice(1), flags);
|
|
17638
|
+
await secretsRemove(args.slice(1), flags);
|
|
17355
17639
|
break;
|
|
17356
|
-
case "
|
|
17357
|
-
|
|
17640
|
+
case "info":
|
|
17641
|
+
secretsInfo();
|
|
17358
17642
|
break;
|
|
17359
17643
|
default:
|
|
17360
17644
|
console.log(`
|
|
17361
|
-
rudi secrets - Manage secrets
|
|
17645
|
+
rudi secrets - Manage secrets (stored in ${getStorageInfo().backend})
|
|
17362
17646
|
|
|
17363
17647
|
COMMANDS
|
|
17364
|
-
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)
|
|
17365
17650
|
list List configured secrets (values masked)
|
|
17366
17651
|
remove <name> Remove a secret
|
|
17367
|
-
|
|
17652
|
+
info Show storage backend info
|
|
17368
17653
|
|
|
17369
17654
|
EXAMPLES
|
|
17370
|
-
rudi secrets set
|
|
17655
|
+
rudi secrets set SLACK_BOT_TOKEN
|
|
17371
17656
|
rudi secrets list
|
|
17372
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
|
|
17373
17662
|
`);
|
|
17374
17663
|
}
|
|
17375
17664
|
}
|
|
17376
17665
|
async function secretsSet(args, flags) {
|
|
17377
17666
|
const name = args[0];
|
|
17667
|
+
const valueArg = args[1];
|
|
17378
17668
|
if (!name) {
|
|
17379
|
-
console.error("Usage: rudi secrets set <name>");
|
|
17380
|
-
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');
|
|
17381
17674
|
process.exit(1);
|
|
17382
17675
|
}
|
|
17383
17676
|
if (!/^[A-Z][A-Z0-9_]*$/.test(name)) {
|
|
17384
17677
|
console.error("Secret name should be UPPER_SNAKE_CASE");
|
|
17385
|
-
console.error("Example:
|
|
17678
|
+
console.error("Example: SLACK_BOT_TOKEN, GITHUB_API_KEY");
|
|
17386
17679
|
process.exit(1);
|
|
17387
17680
|
}
|
|
17388
|
-
const
|
|
17389
|
-
if (
|
|
17681
|
+
const exists = await hasSecret(name);
|
|
17682
|
+
if (exists && !flags.force) {
|
|
17390
17683
|
console.log(`Secret ${name} already exists.`);
|
|
17391
17684
|
console.log("Use --force to overwrite.");
|
|
17392
17685
|
process.exit(0);
|
|
17393
17686
|
}
|
|
17394
|
-
|
|
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
|
+
}
|
|
17395
17697
|
if (!value) {
|
|
17396
17698
|
console.error("No value provided");
|
|
17397
17699
|
process.exit(1);
|
|
17398
17700
|
}
|
|
17399
|
-
setSecret(name, value);
|
|
17400
|
-
|
|
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
|
+
}
|
|
17401
17717
|
}
|
|
17402
|
-
function secretsList(flags) {
|
|
17403
|
-
const names =
|
|
17718
|
+
async function secretsList(flags) {
|
|
17719
|
+
const names = await listSecrets();
|
|
17404
17720
|
if (names.length === 0) {
|
|
17405
17721
|
console.log("No secrets configured.");
|
|
17406
17722
|
console.log("\nSet with: rudi secrets set <name>");
|
|
17407
17723
|
return;
|
|
17408
17724
|
}
|
|
17409
17725
|
if (flags.json) {
|
|
17410
|
-
|
|
17726
|
+
const masked2 = await getMaskedSecrets();
|
|
17727
|
+
console.log(JSON.stringify(masked2, null, 2));
|
|
17411
17728
|
return;
|
|
17412
17729
|
}
|
|
17413
|
-
const masked = getMaskedSecrets();
|
|
17414
|
-
|
|
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}):`);
|
|
17415
17736
|
console.log("\u2500".repeat(50));
|
|
17416
|
-
for (const name of names
|
|
17417
|
-
|
|
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`);
|
|
17418
17748
|
}
|
|
17419
|
-
console.log(`
|
|
17420
|
-
Total: ${names.length} secret(s)`);
|
|
17421
17749
|
}
|
|
17422
|
-
function secretsRemove(args, flags) {
|
|
17750
|
+
async function secretsRemove(args, flags) {
|
|
17423
17751
|
const name = args[0];
|
|
17424
17752
|
if (!name) {
|
|
17425
17753
|
console.error("Usage: rudi secrets remove <name>");
|
|
17426
17754
|
process.exit(1);
|
|
17427
17755
|
}
|
|
17428
|
-
const
|
|
17429
|
-
if (!
|
|
17756
|
+
const allNames = await listSecrets();
|
|
17757
|
+
if (!allNames.includes(name)) {
|
|
17430
17758
|
console.error(`Secret not found: ${name}`);
|
|
17431
17759
|
process.exit(1);
|
|
17432
17760
|
}
|
|
@@ -17435,17 +17763,19 @@ function secretsRemove(args, flags) {
|
|
|
17435
17763
|
console.log("Run with --force to confirm.");
|
|
17436
17764
|
process.exit(0);
|
|
17437
17765
|
}
|
|
17438
|
-
removeSecret(name);
|
|
17766
|
+
await removeSecret(name);
|
|
17439
17767
|
console.log(`\u2713 Secret ${name} removed`);
|
|
17440
17768
|
}
|
|
17441
|
-
function
|
|
17442
|
-
const
|
|
17443
|
-
|
|
17444
|
-
|
|
17445
|
-
|
|
17446
|
-
}
|
|
17447
|
-
console.log(
|
|
17448
|
-
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.");
|
|
17449
17779
|
}
|
|
17450
17780
|
function promptSecret(prompt) {
|
|
17451
17781
|
return new Promise((resolve) => {
|
|
@@ -17480,13 +17810,13 @@ function promptSecret(prompt) {
|
|
|
17480
17810
|
}
|
|
17481
17811
|
|
|
17482
17812
|
// src/commands/db.js
|
|
17483
|
-
var
|
|
17484
|
-
var
|
|
17813
|
+
var import_fs12 = require("fs");
|
|
17814
|
+
var import_path12 = require("path");
|
|
17485
17815
|
|
|
17486
17816
|
// ../packages/db/src/index.js
|
|
17487
17817
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
17488
|
-
var
|
|
17489
|
-
var
|
|
17818
|
+
var import_path11 = __toESM(require("path"), 1);
|
|
17819
|
+
var import_fs11 = __toESM(require("fs"), 1);
|
|
17490
17820
|
init_src();
|
|
17491
17821
|
|
|
17492
17822
|
// ../packages/db/src/schema.js
|
|
@@ -18223,9 +18553,9 @@ var DB_PATH = PATHS.dbFile;
|
|
|
18223
18553
|
var db = null;
|
|
18224
18554
|
function getDb(options = {}) {
|
|
18225
18555
|
if (!db) {
|
|
18226
|
-
const dbDir =
|
|
18227
|
-
if (!
|
|
18228
|
-
|
|
18556
|
+
const dbDir = import_path11.default.dirname(DB_PATH);
|
|
18557
|
+
if (!import_fs11.default.existsSync(dbDir)) {
|
|
18558
|
+
import_fs11.default.mkdirSync(dbDir, { recursive: true });
|
|
18229
18559
|
}
|
|
18230
18560
|
db = new import_better_sqlite3.default(DB_PATH, {
|
|
18231
18561
|
readonly: options.readonly || false
|
|
@@ -18238,7 +18568,7 @@ function getDb(options = {}) {
|
|
|
18238
18568
|
return db;
|
|
18239
18569
|
}
|
|
18240
18570
|
function isDatabaseInitialized() {
|
|
18241
|
-
if (!
|
|
18571
|
+
if (!import_fs11.default.existsSync(DB_PATH)) {
|
|
18242
18572
|
return false;
|
|
18243
18573
|
}
|
|
18244
18574
|
try {
|
|
@@ -18258,7 +18588,7 @@ function getDbPath() {
|
|
|
18258
18588
|
}
|
|
18259
18589
|
function getDbSize() {
|
|
18260
18590
|
try {
|
|
18261
|
-
const stats =
|
|
18591
|
+
const stats = import_fs11.default.statSync(DB_PATH);
|
|
18262
18592
|
return stats.size;
|
|
18263
18593
|
} catch {
|
|
18264
18594
|
return null;
|
|
@@ -18519,12 +18849,12 @@ function dbBackup(args, flags) {
|
|
|
18519
18849
|
let backupPath = args[0];
|
|
18520
18850
|
if (!backupPath) {
|
|
18521
18851
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
18522
|
-
backupPath = (0,
|
|
18852
|
+
backupPath = (0, import_path12.join)((0, import_path12.dirname)(dbPath), `rudi-backup-${timestamp}.db`);
|
|
18523
18853
|
}
|
|
18524
18854
|
if (backupPath.startsWith("~")) {
|
|
18525
|
-
backupPath = (0,
|
|
18855
|
+
backupPath = (0, import_path12.join)(process.env.HOME || "", backupPath.slice(1));
|
|
18526
18856
|
}
|
|
18527
|
-
if ((0,
|
|
18857
|
+
if ((0, import_fs12.existsSync)(backupPath) && !flags.force) {
|
|
18528
18858
|
console.error(`Backup file already exists: ${backupPath}`);
|
|
18529
18859
|
console.error("Use --force to overwrite.");
|
|
18530
18860
|
process.exit(1);
|
|
@@ -18536,7 +18866,7 @@ function dbBackup(args, flags) {
|
|
|
18536
18866
|
const db3 = getDb();
|
|
18537
18867
|
db3.exec("VACUUM INTO ?", [backupPath]);
|
|
18538
18868
|
} catch (e) {
|
|
18539
|
-
(0,
|
|
18869
|
+
(0, import_fs12.copyFileSync)(dbPath, backupPath);
|
|
18540
18870
|
}
|
|
18541
18871
|
const size = getDbSize();
|
|
18542
18872
|
console.log(` Size: ${formatBytes(size)}`);
|
|
@@ -18641,24 +18971,24 @@ function dbTables(flags) {
|
|
|
18641
18971
|
}
|
|
18642
18972
|
|
|
18643
18973
|
// src/commands/import.js
|
|
18644
|
-
var
|
|
18645
|
-
var
|
|
18646
|
-
var
|
|
18974
|
+
var import_fs13 = require("fs");
|
|
18975
|
+
var import_path13 = require("path");
|
|
18976
|
+
var import_os4 = require("os");
|
|
18647
18977
|
var import_crypto2 = require("crypto");
|
|
18648
18978
|
var PROVIDERS = {
|
|
18649
18979
|
claude: {
|
|
18650
18980
|
name: "Claude Code",
|
|
18651
|
-
baseDir: (0,
|
|
18981
|
+
baseDir: (0, import_path13.join)((0, import_os4.homedir)(), ".claude", "projects"),
|
|
18652
18982
|
pattern: /\.jsonl$/
|
|
18653
18983
|
},
|
|
18654
18984
|
codex: {
|
|
18655
18985
|
name: "Codex",
|
|
18656
|
-
baseDir: (0,
|
|
18986
|
+
baseDir: (0, import_path13.join)((0, import_os4.homedir)(), ".codex", "sessions"),
|
|
18657
18987
|
pattern: /\.jsonl$/
|
|
18658
18988
|
},
|
|
18659
18989
|
gemini: {
|
|
18660
18990
|
name: "Gemini",
|
|
18661
|
-
baseDir: (0,
|
|
18991
|
+
baseDir: (0, import_path13.join)((0, import_os4.homedir)(), ".gemini", "sessions"),
|
|
18662
18992
|
pattern: /\.jsonl$/
|
|
18663
18993
|
}
|
|
18664
18994
|
};
|
|
@@ -18725,7 +19055,7 @@ async function importSessions(args, flags) {
|
|
|
18725
19055
|
console.log(`
|
|
18726
19056
|
\u25B6 ${provider.name}`);
|
|
18727
19057
|
console.log(` Source: ${provider.baseDir}`);
|
|
18728
|
-
if (!(0,
|
|
19058
|
+
if (!(0, import_fs13.existsSync)(provider.baseDir)) {
|
|
18729
19059
|
console.log(` \u26A0 Directory not found, skipping`);
|
|
18730
19060
|
continue;
|
|
18731
19061
|
}
|
|
@@ -18768,14 +19098,14 @@ async function importSessions(args, flags) {
|
|
|
18768
19098
|
const now = Date.now();
|
|
18769
19099
|
const maxAgeMs = maxAgeDays ? maxAgeDays * 24 * 60 * 60 * 1e3 : null;
|
|
18770
19100
|
for (const filepath of files) {
|
|
18771
|
-
const sessionId = (0,
|
|
19101
|
+
const sessionId = (0, import_path13.basename)(filepath, ".jsonl");
|
|
18772
19102
|
if (existingIds.has(sessionId)) {
|
|
18773
19103
|
skipped.existing++;
|
|
18774
19104
|
continue;
|
|
18775
19105
|
}
|
|
18776
19106
|
let stat;
|
|
18777
19107
|
try {
|
|
18778
|
-
stat = (0,
|
|
19108
|
+
stat = (0, import_fs13.statSync)(filepath);
|
|
18779
19109
|
} catch (e) {
|
|
18780
19110
|
skipped.error++;
|
|
18781
19111
|
continue;
|
|
@@ -18873,7 +19203,7 @@ function showImportStatus(flags) {
|
|
|
18873
19203
|
}
|
|
18874
19204
|
console.log("\nProvider directories:");
|
|
18875
19205
|
for (const [key, provider] of Object.entries(PROVIDERS)) {
|
|
18876
|
-
const exists = (0,
|
|
19206
|
+
const exists = (0, import_fs13.existsSync)(provider.baseDir);
|
|
18877
19207
|
let count = 0;
|
|
18878
19208
|
if (exists) {
|
|
18879
19209
|
const files = findSessionFiles(provider.baseDir, provider.pattern);
|
|
@@ -18887,10 +19217,10 @@ function showImportStatus(flags) {
|
|
|
18887
19217
|
console.log("To import: rudi import sessions [provider]");
|
|
18888
19218
|
}
|
|
18889
19219
|
function findSessionFiles(dir, pattern, files = []) {
|
|
18890
|
-
if (!(0,
|
|
19220
|
+
if (!(0, import_fs13.existsSync)(dir)) return files;
|
|
18891
19221
|
try {
|
|
18892
|
-
for (const entry of (0,
|
|
18893
|
-
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);
|
|
18894
19224
|
if (entry.isDirectory()) {
|
|
18895
19225
|
findSessionFiles(fullPath, pattern, files);
|
|
18896
19226
|
} else if (pattern.test(entry.name)) {
|
|
@@ -18903,11 +19233,11 @@ function findSessionFiles(dir, pattern, files = []) {
|
|
|
18903
19233
|
}
|
|
18904
19234
|
function parseSessionFile(filepath, provider) {
|
|
18905
19235
|
try {
|
|
18906
|
-
const stat = (0,
|
|
18907
|
-
const content = (0,
|
|
19236
|
+
const stat = (0, import_fs13.statSync)(filepath);
|
|
19237
|
+
const content = (0, import_fs13.readFileSync)(filepath, "utf-8");
|
|
18908
19238
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
18909
19239
|
if (lines.length === 0) return null;
|
|
18910
|
-
const sessionId = (0,
|
|
19240
|
+
const sessionId = (0, import_path13.basename)(filepath, ".jsonl");
|
|
18911
19241
|
const isAgent = sessionId.startsWith("agent-");
|
|
18912
19242
|
let title = null;
|
|
18913
19243
|
let cwd = null;
|
|
@@ -18939,11 +19269,11 @@ function parseSessionFile(filepath, provider) {
|
|
|
18939
19269
|
title = isAgent ? "Agent Session" : "Imported Session";
|
|
18940
19270
|
}
|
|
18941
19271
|
if (!cwd) {
|
|
18942
|
-
const parentDir = (0,
|
|
19272
|
+
const parentDir = (0, import_path13.basename)((0, import_path13.dirname)(filepath));
|
|
18943
19273
|
if (parentDir.startsWith("-")) {
|
|
18944
19274
|
cwd = parentDir.replace(/-/g, "/").replace(/^\//, "/");
|
|
18945
19275
|
} else {
|
|
18946
|
-
cwd = (0,
|
|
19276
|
+
cwd = (0, import_os4.homedir)();
|
|
18947
19277
|
}
|
|
18948
19278
|
}
|
|
18949
19279
|
return {
|
|
@@ -18963,7 +19293,7 @@ function parseSessionFile(filepath, provider) {
|
|
|
18963
19293
|
}
|
|
18964
19294
|
|
|
18965
19295
|
// src/commands/doctor.js
|
|
18966
|
-
var
|
|
19296
|
+
var import_fs14 = __toESM(require("fs"), 1);
|
|
18967
19297
|
async function cmdDoctor(args, flags) {
|
|
18968
19298
|
console.log("RUDI Health Check");
|
|
18969
19299
|
console.log("\u2550".repeat(50));
|
|
@@ -18981,12 +19311,12 @@ async function cmdDoctor(args, flags) {
|
|
|
18981
19311
|
{ path: PATHS.cache, name: "Cache" }
|
|
18982
19312
|
];
|
|
18983
19313
|
for (const dir of dirs) {
|
|
18984
|
-
const exists =
|
|
19314
|
+
const exists = import_fs14.default.existsSync(dir.path);
|
|
18985
19315
|
const status = exists ? "\u2713" : "\u2717";
|
|
18986
19316
|
console.log(` ${status} ${dir.name}: ${dir.path}`);
|
|
18987
19317
|
if (!exists) {
|
|
18988
19318
|
issues.push(`Missing directory: ${dir.name}`);
|
|
18989
|
-
fixes.push(() =>
|
|
19319
|
+
fixes.push(() => import_fs14.default.mkdirSync(dir.path, { recursive: true }));
|
|
18990
19320
|
}
|
|
18991
19321
|
}
|
|
18992
19322
|
console.log("\n\u{1F4BE} Database");
|
|
@@ -19060,35 +19390,416 @@ async function cmdDoctor(args, flags) {
|
|
|
19060
19390
|
if (!nodeOk) {
|
|
19061
19391
|
issues.push("Node.js version too old (requires >=18)");
|
|
19062
19392
|
}
|
|
19063
|
-
console.log("\n" + "\u2500".repeat(50));
|
|
19064
|
-
if (issues.length === 0) {
|
|
19065
|
-
console.log("\u2713 All checks passed!");
|
|
19393
|
+
console.log("\n" + "\u2500".repeat(50));
|
|
19394
|
+
if (issues.length === 0) {
|
|
19395
|
+
console.log("\u2713 All checks passed!");
|
|
19396
|
+
} else {
|
|
19397
|
+
console.log(`Found ${issues.length} issue(s):
|
|
19398
|
+
`);
|
|
19399
|
+
for (const issue of issues) {
|
|
19400
|
+
console.log(` \u2022 ${issue}`);
|
|
19401
|
+
}
|
|
19402
|
+
if (flags.fix && fixes.length > 0) {
|
|
19403
|
+
console.log("\nAttempting fixes...");
|
|
19404
|
+
for (const fix of fixes) {
|
|
19405
|
+
try {
|
|
19406
|
+
fix();
|
|
19407
|
+
} catch (error) {
|
|
19408
|
+
console.error(` Fix failed: ${error.message}`);
|
|
19409
|
+
}
|
|
19410
|
+
}
|
|
19411
|
+
console.log("Done. Run doctor again to verify.");
|
|
19412
|
+
} else if (fixes.length > 0) {
|
|
19413
|
+
console.log("\nRun with --fix to attempt automatic fixes.");
|
|
19414
|
+
}
|
|
19415
|
+
}
|
|
19416
|
+
}
|
|
19417
|
+
|
|
19418
|
+
// src/commands/home.js
|
|
19419
|
+
var import_fs15 = __toESM(require("fs"), 1);
|
|
19420
|
+
var import_path14 = __toESM(require("path"), 1);
|
|
19421
|
+
function formatBytes2(bytes) {
|
|
19422
|
+
if (bytes === 0) return "0 B";
|
|
19423
|
+
const k = 1024;
|
|
19424
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
19425
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
19426
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
|
|
19427
|
+
}
|
|
19428
|
+
function getDirSize(dir) {
|
|
19429
|
+
if (!import_fs15.default.existsSync(dir)) return 0;
|
|
19430
|
+
let size = 0;
|
|
19431
|
+
try {
|
|
19432
|
+
const entries = import_fs15.default.readdirSync(dir, { withFileTypes: true });
|
|
19433
|
+
for (const entry of entries) {
|
|
19434
|
+
const fullPath = import_path14.default.join(dir, entry.name);
|
|
19435
|
+
if (entry.isDirectory()) {
|
|
19436
|
+
size += getDirSize(fullPath);
|
|
19437
|
+
} else {
|
|
19438
|
+
size += import_fs15.default.statSync(fullPath).size;
|
|
19439
|
+
}
|
|
19440
|
+
}
|
|
19441
|
+
} catch {
|
|
19442
|
+
}
|
|
19443
|
+
return size;
|
|
19444
|
+
}
|
|
19445
|
+
function countItems(dir) {
|
|
19446
|
+
if (!import_fs15.default.existsSync(dir)) return 0;
|
|
19447
|
+
try {
|
|
19448
|
+
return import_fs15.default.readdirSync(dir).filter((f) => !f.startsWith(".")).length;
|
|
19449
|
+
} catch {
|
|
19450
|
+
return 0;
|
|
19451
|
+
}
|
|
19452
|
+
}
|
|
19453
|
+
async function cmdHome(args, flags) {
|
|
19454
|
+
console.log("\u2550".repeat(60));
|
|
19455
|
+
console.log("RUDI Home: " + PATHS.home);
|
|
19456
|
+
console.log("\u2550".repeat(60));
|
|
19457
|
+
if (flags.json) {
|
|
19458
|
+
const data = {
|
|
19459
|
+
home: PATHS.home,
|
|
19460
|
+
directories: {},
|
|
19461
|
+
packages: {},
|
|
19462
|
+
database: {}
|
|
19463
|
+
};
|
|
19464
|
+
const dirs2 = [
|
|
19465
|
+
{ key: "stacks", path: PATHS.stacks },
|
|
19466
|
+
{ key: "prompts", path: PATHS.prompts },
|
|
19467
|
+
{ key: "runtimes", path: PATHS.runtimes },
|
|
19468
|
+
{ key: "binaries", path: PATHS.binaries },
|
|
19469
|
+
{ key: "agents", path: PATHS.agents },
|
|
19470
|
+
{ key: "cache", path: PATHS.cache }
|
|
19471
|
+
];
|
|
19472
|
+
for (const dir of dirs2) {
|
|
19473
|
+
data.directories[dir.key] = {
|
|
19474
|
+
path: dir.path,
|
|
19475
|
+
exists: import_fs15.default.existsSync(dir.path),
|
|
19476
|
+
items: countItems(dir.path),
|
|
19477
|
+
size: getDirSize(dir.path)
|
|
19478
|
+
};
|
|
19479
|
+
}
|
|
19480
|
+
for (const kind of ["stack", "prompt", "runtime", "binary", "agent"]) {
|
|
19481
|
+
data.packages[kind] = getInstalledPackages(kind).length;
|
|
19482
|
+
}
|
|
19483
|
+
data.database = {
|
|
19484
|
+
initialized: isDatabaseInitialized(),
|
|
19485
|
+
size: getDbSize() || 0
|
|
19486
|
+
};
|
|
19487
|
+
console.log(JSON.stringify(data, null, 2));
|
|
19488
|
+
return;
|
|
19489
|
+
}
|
|
19490
|
+
console.log("\n\u{1F4C1} Directory Structure\n");
|
|
19491
|
+
const dirs = [
|
|
19492
|
+
{ name: "stacks", path: PATHS.stacks, icon: "\u{1F4E6}", desc: "MCP server stacks" },
|
|
19493
|
+
{ name: "prompts", path: PATHS.prompts, icon: "\u{1F4DD}", desc: "Prompt templates" },
|
|
19494
|
+
{ name: "runtimes", path: PATHS.runtimes, icon: "\u2699\uFE0F", desc: "Node, Python, Deno, Bun" },
|
|
19495
|
+
{ name: "binaries", path: PATHS.binaries, icon: "\u{1F527}", desc: "ffmpeg, ripgrep, etc." },
|
|
19496
|
+
{ name: "agents", path: PATHS.agents, icon: "\u{1F916}", desc: "Claude, Codex, Gemini CLIs" },
|
|
19497
|
+
{ name: "cache", path: PATHS.cache, icon: "\u{1F4BE}", desc: "Registry cache" }
|
|
19498
|
+
];
|
|
19499
|
+
for (const dir of dirs) {
|
|
19500
|
+
const exists = import_fs15.default.existsSync(dir.path);
|
|
19501
|
+
const count = countItems(dir.path);
|
|
19502
|
+
const size = getDirSize(dir.path);
|
|
19503
|
+
console.log(`${dir.icon} ${dir.name}/`);
|
|
19504
|
+
console.log(` ${dir.desc}`);
|
|
19505
|
+
if (exists) {
|
|
19506
|
+
console.log(` ${count} items, ${formatBytes2(size)}`);
|
|
19507
|
+
} else {
|
|
19508
|
+
console.log(` (not created)`);
|
|
19509
|
+
}
|
|
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]}`;
|
|
19066
19715
|
} else {
|
|
19067
|
-
|
|
19068
|
-
|
|
19069
|
-
|
|
19070
|
-
|
|
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);
|
|
19071
19773
|
}
|
|
19072
|
-
|
|
19073
|
-
|
|
19074
|
-
|
|
19075
|
-
|
|
19076
|
-
|
|
19077
|
-
|
|
19078
|
-
|
|
19079
|
-
|
|
19080
|
-
|
|
19081
|
-
|
|
19082
|
-
|
|
19083
|
-
console.log(
|
|
19774
|
+
}
|
|
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
|
+
}
|
|
19782
|
+
}
|
|
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)");
|
|
19084
19788
|
}
|
|
19085
19789
|
}
|
|
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);
|
|
19086
19797
|
}
|
|
19087
19798
|
|
|
19088
19799
|
// src/commands/update.js
|
|
19089
|
-
var
|
|
19090
|
-
var
|
|
19091
|
-
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");
|
|
19092
19803
|
init_src();
|
|
19093
19804
|
init_src2();
|
|
19094
19805
|
async function cmdUpdate(args, flags) {
|
|
@@ -19113,7 +19824,7 @@ async function cmdUpdate(args, flags) {
|
|
|
19113
19824
|
async function updatePackage(pkgId, flags) {
|
|
19114
19825
|
const [kind, name] = parsePackageId(pkgId);
|
|
19115
19826
|
const installPath = getPackagePath(pkgId);
|
|
19116
|
-
if (!
|
|
19827
|
+
if (!import_fs18.default.existsSync(installPath)) {
|
|
19117
19828
|
return { success: false, error: "Package not installed" };
|
|
19118
19829
|
}
|
|
19119
19830
|
const pkg = await getPackage(pkgId);
|
|
@@ -19123,7 +19834,7 @@ async function updatePackage(pkgId, flags) {
|
|
|
19123
19834
|
console.log(`Updating ${pkgId}...`);
|
|
19124
19835
|
if (pkg.npmPackage) {
|
|
19125
19836
|
try {
|
|
19126
|
-
(0,
|
|
19837
|
+
(0, import_child_process5.execSync)(`npm install ${pkg.npmPackage}@latest`, {
|
|
19127
19838
|
cwd: installPath,
|
|
19128
19839
|
stdio: flags.verbose ? "inherit" : "pipe"
|
|
19129
19840
|
});
|
|
@@ -19136,11 +19847,11 @@ async function updatePackage(pkgId, flags) {
|
|
|
19136
19847
|
}
|
|
19137
19848
|
if (pkg.pipPackage) {
|
|
19138
19849
|
try {
|
|
19139
|
-
const venvPip =
|
|
19140
|
-
(0,
|
|
19850
|
+
const venvPip = import_path16.default.join(installPath, "venv", "bin", "pip");
|
|
19851
|
+
(0, import_child_process5.execSync)(`"${venvPip}" install --upgrade ${pkg.pipPackage}`, {
|
|
19141
19852
|
stdio: flags.verbose ? "inherit" : "pipe"
|
|
19142
19853
|
});
|
|
19143
|
-
const versionOutput = (0,
|
|
19854
|
+
const versionOutput = (0, import_child_process5.execSync)(`"${venvPip}" show ${pkg.pipPackage} | grep Version`, {
|
|
19144
19855
|
encoding: "utf-8"
|
|
19145
19856
|
});
|
|
19146
19857
|
const version = versionOutput.split(":")[1]?.trim();
|
|
@@ -19152,9 +19863,9 @@ async function updatePackage(pkgId, flags) {
|
|
|
19152
19863
|
}
|
|
19153
19864
|
if (kind === "runtime" && !pkg.npmPackage && !pkg.pipPackage) {
|
|
19154
19865
|
try {
|
|
19155
|
-
const { downloadRuntime:
|
|
19156
|
-
|
|
19157
|
-
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, {
|
|
19158
19869
|
onProgress: (p) => {
|
|
19159
19870
|
if (flags.verbose) console.log(` ${p.phase}...`);
|
|
19160
19871
|
}
|
|
@@ -19173,8 +19884,8 @@ async function updateAll(flags) {
|
|
|
19173
19884
|
let failed = 0;
|
|
19174
19885
|
for (const kind of kinds) {
|
|
19175
19886
|
const dir = kind === "runtime" ? PATHS.runtimes : kind === "stack" ? PATHS.stacks : PATHS.prompts;
|
|
19176
|
-
if (!
|
|
19177
|
-
const entries =
|
|
19887
|
+
if (!import_fs18.default.existsSync(dir)) continue;
|
|
19888
|
+
const entries = import_fs18.default.readdirSync(dir, { withFileTypes: true });
|
|
19178
19889
|
for (const entry of entries) {
|
|
19179
19890
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
19180
19891
|
const pkgId = `${kind}:${entry.name}`;
|
|
@@ -19193,14 +19904,14 @@ Updated ${updated} package(s)${failed > 0 ? `, ${failed} failed` : ""}`);
|
|
|
19193
19904
|
}
|
|
19194
19905
|
function getInstalledVersion(installPath, npmPackage) {
|
|
19195
19906
|
try {
|
|
19196
|
-
const pkgJsonPath =
|
|
19197
|
-
if (
|
|
19198
|
-
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"));
|
|
19199
19910
|
return pkgJson.version;
|
|
19200
19911
|
}
|
|
19201
|
-
const rootPkgPath =
|
|
19202
|
-
if (
|
|
19203
|
-
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"));
|
|
19204
19915
|
const dep = rootPkg.dependencies?.[npmPackage];
|
|
19205
19916
|
if (dep) return dep.replace(/[\^~]/, "");
|
|
19206
19917
|
}
|
|
@@ -19209,30 +19920,30 @@ function getInstalledVersion(installPath, npmPackage) {
|
|
|
19209
19920
|
return null;
|
|
19210
19921
|
}
|
|
19211
19922
|
function updateRuntimeMetadata(installPath, updates) {
|
|
19212
|
-
const metaPath =
|
|
19923
|
+
const metaPath = import_path16.default.join(installPath, "runtime.json");
|
|
19213
19924
|
try {
|
|
19214
19925
|
let meta = {};
|
|
19215
|
-
if (
|
|
19216
|
-
meta = JSON.parse(
|
|
19926
|
+
if (import_fs18.default.existsSync(metaPath)) {
|
|
19927
|
+
meta = JSON.parse(import_fs18.default.readFileSync(metaPath, "utf-8"));
|
|
19217
19928
|
}
|
|
19218
19929
|
meta = { ...meta, ...updates };
|
|
19219
|
-
|
|
19930
|
+
import_fs18.default.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
19220
19931
|
} catch {
|
|
19221
19932
|
}
|
|
19222
19933
|
}
|
|
19223
19934
|
|
|
19224
19935
|
// db/index.js
|
|
19225
19936
|
var import_better_sqlite32 = __toESM(require("better-sqlite3"), 1);
|
|
19226
|
-
var
|
|
19227
|
-
var
|
|
19228
|
-
var
|
|
19229
|
-
var RUDI_HOME2 =
|
|
19230
|
-
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");
|
|
19231
19942
|
var db2 = null;
|
|
19232
19943
|
function getDb2(options = {}) {
|
|
19233
19944
|
if (!db2) {
|
|
19234
|
-
if (!
|
|
19235
|
-
|
|
19945
|
+
if (!import_fs19.default.existsSync(RUDI_HOME2)) {
|
|
19946
|
+
import_fs19.default.mkdirSync(RUDI_HOME2, { recursive: true });
|
|
19236
19947
|
}
|
|
19237
19948
|
db2 = new import_better_sqlite32.default(DB_PATH2, {
|
|
19238
19949
|
readonly: options.readonly || false
|
|
@@ -19390,7 +20101,7 @@ function getBeforeCrashLogs() {
|
|
|
19390
20101
|
}
|
|
19391
20102
|
|
|
19392
20103
|
// src/commands/logs.js
|
|
19393
|
-
var
|
|
20104
|
+
var import_fs20 = __toESM(require("fs"), 1);
|
|
19394
20105
|
function parseTimeAgo(str) {
|
|
19395
20106
|
const match = str.match(/^(\d+)([smhd])$/);
|
|
19396
20107
|
if (!match) return null;
|
|
@@ -19490,7 +20201,7 @@ function exportLogs(logs, filepath, format) {
|
|
|
19490
20201
|
});
|
|
19491
20202
|
content = JSON.stringify(formatted, null, 2);
|
|
19492
20203
|
}
|
|
19493
|
-
|
|
20204
|
+
import_fs20.default.writeFileSync(filepath, content, "utf-8");
|
|
19494
20205
|
return filepath;
|
|
19495
20206
|
}
|
|
19496
20207
|
function printStats(stats) {
|
|
@@ -19616,9 +20327,9 @@ async function handleLogsCommand(args, flags) {
|
|
|
19616
20327
|
}
|
|
19617
20328
|
|
|
19618
20329
|
// src/commands/which.js
|
|
19619
|
-
var
|
|
19620
|
-
var
|
|
19621
|
-
var
|
|
20330
|
+
var fs21 = __toESM(require("fs/promises"), 1);
|
|
20331
|
+
var path19 = __toESM(require("path"), 1);
|
|
20332
|
+
var import_child_process6 = require("child_process");
|
|
19622
20333
|
init_src();
|
|
19623
20334
|
async function cmdWhich(args, flags) {
|
|
19624
20335
|
const stackId = args[0];
|
|
@@ -19686,7 +20397,7 @@ Installed stacks:`);
|
|
|
19686
20397
|
if (runtimeInfo.entry) {
|
|
19687
20398
|
console.log("");
|
|
19688
20399
|
console.log("Run MCP server directly:");
|
|
19689
|
-
const entryPath =
|
|
20400
|
+
const entryPath = path19.join(stackPath, runtimeInfo.entry);
|
|
19690
20401
|
if (runtimeInfo.runtime === "node") {
|
|
19691
20402
|
console.log(` echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node ${entryPath}`);
|
|
19692
20403
|
} else if (runtimeInfo.runtime === "python") {
|
|
@@ -19705,27 +20416,27 @@ Installed stacks:`);
|
|
|
19705
20416
|
async function detectRuntime(stackPath) {
|
|
19706
20417
|
const runtimes = ["node", "python"];
|
|
19707
20418
|
for (const runtime of runtimes) {
|
|
19708
|
-
const runtimePath =
|
|
20419
|
+
const runtimePath = path19.join(stackPath, runtime);
|
|
19709
20420
|
try {
|
|
19710
|
-
await
|
|
20421
|
+
await fs21.access(runtimePath);
|
|
19711
20422
|
if (runtime === "node") {
|
|
19712
|
-
const distEntry =
|
|
19713
|
-
const srcEntry =
|
|
20423
|
+
const distEntry = path19.join(runtimePath, "dist", "index.js");
|
|
20424
|
+
const srcEntry = path19.join(runtimePath, "src", "index.ts");
|
|
19714
20425
|
try {
|
|
19715
|
-
await
|
|
20426
|
+
await fs21.access(distEntry);
|
|
19716
20427
|
return { runtime: "node", entry: `${runtime}/dist/index.js` };
|
|
19717
20428
|
} catch {
|
|
19718
20429
|
try {
|
|
19719
|
-
await
|
|
20430
|
+
await fs21.access(srcEntry);
|
|
19720
20431
|
return { runtime: "node", entry: `${runtime}/src/index.ts` };
|
|
19721
20432
|
} catch {
|
|
19722
20433
|
return { runtime: "node", entry: null };
|
|
19723
20434
|
}
|
|
19724
20435
|
}
|
|
19725
20436
|
} else if (runtime === "python") {
|
|
19726
|
-
const entry =
|
|
20437
|
+
const entry = path19.join(runtimePath, "src", "index.py");
|
|
19727
20438
|
try {
|
|
19728
|
-
await
|
|
20439
|
+
await fs21.access(entry);
|
|
19729
20440
|
return { runtime: "python", entry: `${runtime}/src/index.py` };
|
|
19730
20441
|
} catch {
|
|
19731
20442
|
return { runtime: "python", entry: null };
|
|
@@ -19741,21 +20452,21 @@ async function checkAuth(stackPath, runtime) {
|
|
|
19741
20452
|
const authFiles = [];
|
|
19742
20453
|
let configured = false;
|
|
19743
20454
|
if (runtime === "node" || runtime === "python") {
|
|
19744
|
-
const runtimePath =
|
|
19745
|
-
const tokenPath =
|
|
20455
|
+
const runtimePath = path19.join(stackPath, runtime);
|
|
20456
|
+
const tokenPath = path19.join(runtimePath, "token.json");
|
|
19746
20457
|
try {
|
|
19747
|
-
await
|
|
20458
|
+
await fs21.access(tokenPath);
|
|
19748
20459
|
authFiles.push(`${runtime}/token.json`);
|
|
19749
20460
|
configured = true;
|
|
19750
20461
|
} catch {
|
|
19751
|
-
const accountsPath =
|
|
20462
|
+
const accountsPath = path19.join(runtimePath, "accounts");
|
|
19752
20463
|
try {
|
|
19753
|
-
const accounts = await
|
|
20464
|
+
const accounts = await fs21.readdir(accountsPath);
|
|
19754
20465
|
for (const account of accounts) {
|
|
19755
20466
|
if (account.startsWith(".")) continue;
|
|
19756
|
-
const accountTokenPath =
|
|
20467
|
+
const accountTokenPath = path19.join(accountsPath, account, "token.json");
|
|
19757
20468
|
try {
|
|
19758
|
-
await
|
|
20469
|
+
await fs21.access(accountTokenPath);
|
|
19759
20470
|
authFiles.push(`${runtime}/accounts/${account}/token.json`);
|
|
19760
20471
|
configured = true;
|
|
19761
20472
|
} catch {
|
|
@@ -19765,9 +20476,9 @@ async function checkAuth(stackPath, runtime) {
|
|
|
19765
20476
|
}
|
|
19766
20477
|
}
|
|
19767
20478
|
}
|
|
19768
|
-
const envPath =
|
|
20479
|
+
const envPath = path19.join(stackPath, ".env");
|
|
19769
20480
|
try {
|
|
19770
|
-
const envContent = await
|
|
20481
|
+
const envContent = await fs21.readFile(envPath, "utf-8");
|
|
19771
20482
|
const hasValues = envContent.split("\n").some((line) => {
|
|
19772
20483
|
const trimmed = line.trim();
|
|
19773
20484
|
if (!trimmed || trimmed.startsWith("#")) return false;
|
|
@@ -19796,7 +20507,7 @@ async function checkAuth(stackPath, runtime) {
|
|
|
19796
20507
|
}
|
|
19797
20508
|
function checkIfRunning(stackName) {
|
|
19798
20509
|
try {
|
|
19799
|
-
const result = (0,
|
|
20510
|
+
const result = (0, import_child_process6.execSync)(`ps aux | grep "${stackName}" | grep -v grep || true`, {
|
|
19800
20511
|
encoding: "utf-8",
|
|
19801
20512
|
stdio: ["pipe", "pipe", "ignore"]
|
|
19802
20513
|
// Suppress stderr
|
|
@@ -19811,9 +20522,9 @@ function checkIfRunning(stackName) {
|
|
|
19811
20522
|
}
|
|
19812
20523
|
|
|
19813
20524
|
// src/commands/auth.js
|
|
19814
|
-
var
|
|
19815
|
-
var
|
|
19816
|
-
var
|
|
20525
|
+
var fs22 = __toESM(require("fs/promises"), 1);
|
|
20526
|
+
var path20 = __toESM(require("path"), 1);
|
|
20527
|
+
var import_child_process7 = require("child_process");
|
|
19817
20528
|
init_src();
|
|
19818
20529
|
var net = __toESM(require("net"), 1);
|
|
19819
20530
|
async function findAvailablePort(basePort = 3456) {
|
|
@@ -19844,26 +20555,26 @@ function isPortAvailable(port) {
|
|
|
19844
20555
|
async function detectRuntime2(stackPath) {
|
|
19845
20556
|
const runtimes = ["node", "python"];
|
|
19846
20557
|
for (const runtime of runtimes) {
|
|
19847
|
-
const runtimePath =
|
|
20558
|
+
const runtimePath = path20.join(stackPath, runtime);
|
|
19848
20559
|
try {
|
|
19849
|
-
await
|
|
20560
|
+
await fs22.access(runtimePath);
|
|
19850
20561
|
if (runtime === "node") {
|
|
19851
|
-
const authTs =
|
|
19852
|
-
const authJs =
|
|
20562
|
+
const authTs = path20.join(runtimePath, "src", "auth.ts");
|
|
20563
|
+
const authJs = path20.join(runtimePath, "dist", "auth.js");
|
|
19853
20564
|
try {
|
|
19854
|
-
await
|
|
20565
|
+
await fs22.access(authTs);
|
|
19855
20566
|
return { runtime: "node", authScript: authTs, useTsx: true };
|
|
19856
20567
|
} catch {
|
|
19857
20568
|
try {
|
|
19858
|
-
await
|
|
20569
|
+
await fs22.access(authJs);
|
|
19859
20570
|
return { runtime: "node", authScript: authJs, useTsx: false };
|
|
19860
20571
|
} catch {
|
|
19861
20572
|
}
|
|
19862
20573
|
}
|
|
19863
20574
|
} else if (runtime === "python") {
|
|
19864
|
-
const authPy =
|
|
20575
|
+
const authPy = path20.join(runtimePath, "src", "auth.py");
|
|
19865
20576
|
try {
|
|
19866
|
-
await
|
|
20577
|
+
await fs22.access(authPy);
|
|
19867
20578
|
return { runtime: "python", authScript: authPy, useTsx: false };
|
|
19868
20579
|
} catch {
|
|
19869
20580
|
}
|
|
@@ -19913,14 +20624,14 @@ Installed stacks:`);
|
|
|
19913
20624
|
console.log(`Using port: ${port}`);
|
|
19914
20625
|
console.log("");
|
|
19915
20626
|
let cmd;
|
|
19916
|
-
const cwd =
|
|
20627
|
+
const cwd = path20.dirname(authInfo.authScript);
|
|
19917
20628
|
if (authInfo.runtime === "node") {
|
|
19918
|
-
const distAuth =
|
|
20629
|
+
const distAuth = path20.join(cwd, "..", "dist", "auth.js");
|
|
19919
20630
|
let useBuiltInPort = false;
|
|
19920
20631
|
let tempAuthScript = null;
|
|
19921
20632
|
try {
|
|
19922
|
-
await
|
|
19923
|
-
const distContent = await
|
|
20633
|
+
await fs22.access(distAuth);
|
|
20634
|
+
const distContent = await fs22.readFile(distAuth, "utf-8");
|
|
19924
20635
|
if (distContent.includes("findAvailablePort")) {
|
|
19925
20636
|
console.log("Using compiled authentication script...");
|
|
19926
20637
|
cmd = `node ${distAuth}${accountEmail ? ` ${accountEmail}` : ""}`;
|
|
@@ -19929,11 +20640,11 @@ Installed stacks:`);
|
|
|
19929
20640
|
} catch {
|
|
19930
20641
|
}
|
|
19931
20642
|
if (!useBuiltInPort) {
|
|
19932
|
-
const authContent = await
|
|
20643
|
+
const authContent = await fs22.readFile(authInfo.authScript, "utf-8");
|
|
19933
20644
|
const tempExt = authInfo.useTsx ? ".ts" : ".mjs";
|
|
19934
|
-
tempAuthScript =
|
|
20645
|
+
tempAuthScript = path20.join(cwd, "..", `auth-temp${tempExt}`);
|
|
19935
20646
|
const modifiedContent = authContent.replace(/localhost:3456/g, `localhost:${port}`).replace(/server\.listen\(3456/g, `server.listen(${port}`);
|
|
19936
|
-
await
|
|
20647
|
+
await fs22.writeFile(tempAuthScript, modifiedContent);
|
|
19937
20648
|
if (authInfo.useTsx) {
|
|
19938
20649
|
cmd = `npx tsx ${tempAuthScript}${accountEmail ? ` ${accountEmail}` : ""}`;
|
|
19939
20650
|
} else {
|
|
@@ -19943,17 +20654,17 @@ Installed stacks:`);
|
|
|
19943
20654
|
console.log("Starting OAuth flow...");
|
|
19944
20655
|
console.log("");
|
|
19945
20656
|
try {
|
|
19946
|
-
(0,
|
|
20657
|
+
(0, import_child_process7.execSync)(cmd, {
|
|
19947
20658
|
cwd,
|
|
19948
20659
|
stdio: "inherit"
|
|
19949
20660
|
});
|
|
19950
20661
|
if (tempAuthScript) {
|
|
19951
|
-
await
|
|
20662
|
+
await fs22.unlink(tempAuthScript);
|
|
19952
20663
|
}
|
|
19953
20664
|
} catch (error) {
|
|
19954
20665
|
if (tempAuthScript) {
|
|
19955
20666
|
try {
|
|
19956
|
-
await
|
|
20667
|
+
await fs22.unlink(tempAuthScript);
|
|
19957
20668
|
} catch {
|
|
19958
20669
|
}
|
|
19959
20670
|
}
|
|
@@ -19963,7 +20674,7 @@ Installed stacks:`);
|
|
|
19963
20674
|
cmd = `python3 ${authInfo.authScript}${accountEmail ? ` ${accountEmail}` : ""}`;
|
|
19964
20675
|
console.log("Starting OAuth flow...");
|
|
19965
20676
|
console.log("");
|
|
19966
|
-
(0,
|
|
20677
|
+
(0, import_child_process7.execSync)(cmd, {
|
|
19967
20678
|
cwd,
|
|
19968
20679
|
stdio: "inherit",
|
|
19969
20680
|
env: {
|
|
@@ -19984,6 +20695,657 @@ Installed stacks:`);
|
|
|
19984
20695
|
}
|
|
19985
20696
|
}
|
|
19986
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
|
+
|
|
19987
21349
|
// src/index.js
|
|
19988
21350
|
var VERSION = "2.0.0";
|
|
19989
21351
|
async function main() {
|
|
@@ -20034,6 +21396,11 @@ async function main() {
|
|
|
20034
21396
|
case "check":
|
|
20035
21397
|
await cmdDoctor(args, flags);
|
|
20036
21398
|
break;
|
|
21399
|
+
case "init":
|
|
21400
|
+
case "bootstrap":
|
|
21401
|
+
case "setup":
|
|
21402
|
+
await cmdInit(args, flags);
|
|
21403
|
+
break;
|
|
20037
21404
|
case "update":
|
|
20038
21405
|
case "upgrade":
|
|
20039
21406
|
await cmdUpdate(args, flags);
|
|
@@ -20051,6 +21418,37 @@ async function main() {
|
|
|
20051
21418
|
case "login":
|
|
20052
21419
|
await cmdAuth(args, flags);
|
|
20053
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;
|
|
21430
|
+
case "home":
|
|
21431
|
+
case "status":
|
|
21432
|
+
await cmdHome(args, flags);
|
|
21433
|
+
break;
|
|
21434
|
+
// Shortcuts for listing specific package types
|
|
21435
|
+
case "stacks":
|
|
21436
|
+
await cmdList(["stacks"], flags);
|
|
21437
|
+
break;
|
|
21438
|
+
case "prompts":
|
|
21439
|
+
await cmdList(["prompts"], flags);
|
|
21440
|
+
break;
|
|
21441
|
+
case "runtimes":
|
|
21442
|
+
await cmdList(["runtimes"], flags);
|
|
21443
|
+
break;
|
|
21444
|
+
case "binaries":
|
|
21445
|
+
case "bins":
|
|
21446
|
+
case "tools":
|
|
21447
|
+
await cmdList(["binaries"], flags);
|
|
21448
|
+
break;
|
|
21449
|
+
case "agents":
|
|
21450
|
+
await cmdList(["agents"], flags);
|
|
21451
|
+
break;
|
|
20054
21452
|
case "help":
|
|
20055
21453
|
printHelp(args[0]);
|
|
20056
21454
|
break;
|