@learnrudi/cli 1.10.8 → 1.10.10

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/dist/index.cjs CHANGED
@@ -33,6 +33,35 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
33
33
  ));
34
34
 
35
35
  // packages/env/src/index.js
36
+ function getNodeRuntimeRoot() {
37
+ return import_path.default.join(PATHS.runtimes, "node");
38
+ }
39
+ function resolveNodeRuntimeBin(binName) {
40
+ const root = getNodeRuntimeRoot();
41
+ const isWindows = import_os.default.platform() === "win32";
42
+ const arch = import_os.default.arch() === "arm64" ? "arm64" : "x64";
43
+ const binDir = isWindows ? "Scripts" : "bin";
44
+ const candidates = [];
45
+ if (isWindows) {
46
+ const names = [`${binName}.cmd`, `${binName}.exe`, binName];
47
+ for (const name of names) {
48
+ candidates.push(import_path.default.join(root, arch, binDir, name));
49
+ candidates.push(import_path.default.join(root, binDir, name));
50
+ }
51
+ } else {
52
+ candidates.push(import_path.default.join(root, arch, binDir, binName));
53
+ candidates.push(import_path.default.join(root, binDir, binName));
54
+ }
55
+ for (const candidate of candidates) {
56
+ if (import_fs.default.existsSync(candidate)) {
57
+ return candidate;
58
+ }
59
+ }
60
+ return candidates[candidates.length - 1];
61
+ }
62
+ function getNodeRuntimeBinDir() {
63
+ return import_path.default.dirname(resolveNodeRuntimeBin("node"));
64
+ }
36
65
  function getPlatformArch() {
37
66
  const platform = import_os.default.platform();
38
67
  const arch = import_os.default.arch();
@@ -118,8 +147,20 @@ function isPackageInstalled(id) {
118
147
  return false;
119
148
  }
120
149
  if (kind2 === "agent") {
121
- const binPath = import_path.default.join(packagePath, "node_modules", ".bin", name);
122
- return import_fs.default.existsSync(binPath);
150
+ const manifestPath = import_path.default.join(packagePath, "manifest.json");
151
+ let bins = [];
152
+ if (import_fs.default.existsSync(manifestPath)) {
153
+ try {
154
+ const manifest = JSON.parse(import_fs.default.readFileSync(manifestPath, "utf-8"));
155
+ bins = manifest.bins || manifest.binaries || [];
156
+ } catch {
157
+ bins = [];
158
+ }
159
+ }
160
+ if (bins.length === 0) {
161
+ bins = [name];
162
+ }
163
+ return bins.some((bin) => import_fs.default.existsSync(resolveNodeRuntimeBin(bin)));
123
164
  }
124
165
  try {
125
166
  const contents = import_fs.default.readdirSync(packagePath);
@@ -8726,6 +8767,8 @@ function resolveBinTarget(manifest, bin) {
8726
8767
  return import_path5.default.join(manifest.installDir, bin);
8727
8768
  case "npm":
8728
8769
  return import_path5.default.join(manifest.installDir, "node_modules", ".bin", bin);
8770
+ case "npm-global":
8771
+ return resolveNodeRuntimeBin(bin);
8729
8772
  case "pip":
8730
8773
  return import_path5.default.join(manifest.installDir, "venv", "bin", bin);
8731
8774
  case "system":
@@ -8830,9 +8873,18 @@ var init_shims = __esm({
8830
8873
  });
8831
8874
 
8832
8875
  // packages/core/src/installer.js
8833
- function discoverNpmBins(installPath, packageName) {
8876
+ function getNpmModulesRoot(installRoot, scope = "local") {
8877
+ if (scope === "global") {
8878
+ return import_path6.default.join(installRoot, "lib", "node_modules");
8879
+ }
8880
+ return import_path6.default.join(installRoot, "node_modules");
8881
+ }
8882
+ function getNpmPackageJsonPath(installRoot, packageName, scope = "local") {
8883
+ return import_path6.default.join(getNpmModulesRoot(installRoot, scope), packageName, "package.json");
8884
+ }
8885
+ function discoverNpmBins(installRoot, packageName, scope = "local") {
8834
8886
  try {
8835
- const pkgJsonPath = import_path6.default.join(installPath, "node_modules", packageName, "package.json");
8887
+ const pkgJsonPath = getNpmPackageJsonPath(installRoot, packageName, scope);
8836
8888
  if (!import_fs5.default.existsSync(pkgJsonPath)) {
8837
8889
  console.warn(`[Installer] Warning: Could not find package.json at ${pkgJsonPath}`);
8838
8890
  return [];
@@ -8853,9 +8905,9 @@ function discoverNpmBins(installPath, packageName) {
8853
8905
  return [];
8854
8906
  }
8855
8907
  }
8856
- function hasInstallScripts(installPath, packageName) {
8908
+ function hasInstallScripts(installRoot, packageName, scope = "local") {
8857
8909
  try {
8858
- const pkgJsonPath = import_path6.default.join(installPath, "node_modules", packageName, "package.json");
8910
+ const pkgJsonPath = getNpmPackageJsonPath(installRoot, packageName, scope);
8859
8911
  if (!import_fs5.default.existsSync(pkgJsonPath)) {
8860
8912
  return false;
8861
8913
  }
@@ -8910,35 +8962,60 @@ async function installPackage(id, options = {}) {
8910
8962
  async function installSinglePackage(pkg, options = {}) {
8911
8963
  const { force = false, allowScripts = false, onProgress } = options;
8912
8964
  const installPath = getPackagePath(pkg.id);
8965
+ const pkgName = pkg.id.replace(/^(runtime|binary|agent):/, "");
8966
+ const isAgentNpm = pkg.kind === "agent" && pkg.npmPackage;
8913
8967
  if (import_fs5.default.existsSync(installPath) && !force) {
8914
- return { success: true, id: pkg.id, path: installPath, skipped: true };
8968
+ if (!isAgentNpm) {
8969
+ return { success: true, id: pkg.id, path: installPath, skipped: true };
8970
+ }
8971
+ const manifestPath = import_path6.default.join(installPath, "manifest.json");
8972
+ let bins = pkg.bins || [];
8973
+ if (import_fs5.default.existsSync(manifestPath)) {
8974
+ try {
8975
+ const manifest2 = JSON.parse(import_fs5.default.readFileSync(manifestPath, "utf-8"));
8976
+ bins = manifest2.bins || manifest2.binaries || bins;
8977
+ } catch {
8978
+ }
8979
+ }
8980
+ if (bins.length === 0) {
8981
+ bins = [pkgName];
8982
+ }
8983
+ const hasGlobalBin = bins.some((bin) => import_fs5.default.existsSync(resolveNodeRuntimeBin(bin)));
8984
+ if (hasGlobalBin) {
8985
+ return { success: true, id: pkg.id, path: installPath, skipped: true };
8986
+ }
8915
8987
  }
8916
8988
  if (pkg.kind === "runtime" || pkg.kind === "binary" || pkg.kind === "agent") {
8917
- const pkgName = pkg.id.replace(/^(runtime|binary|agent):/, "");
8918
8989
  onProgress?.({ phase: "downloading", package: pkg.id });
8919
8990
  if (pkg.npmPackage) {
8920
8991
  try {
8921
8992
  const { execSync: execSync11 } = await import("child_process");
8993
+ const npmInstallRoot = isAgentNpm ? getNodeRuntimeRoot() : installPath;
8994
+ const npmScope = isAgentNpm ? "global" : "local";
8922
8995
  if (!import_fs5.default.existsSync(installPath)) {
8923
8996
  import_fs5.default.mkdirSync(installPath, { recursive: true });
8924
8997
  }
8998
+ if (isAgentNpm && !import_fs5.default.existsSync(npmInstallRoot)) {
8999
+ import_fs5.default.mkdirSync(npmInstallRoot, { recursive: true });
9000
+ }
8925
9001
  onProgress?.({ phase: "installing", package: pkg.id, message: `npm install ${pkg.npmPackage}` });
8926
9002
  const resourcesPath = process.env.RESOURCES_PATH;
8927
- const npmCmd = resourcesPath ? import_path6.default.join(resourcesPath, "bundled-runtimes", "node", "bin", "npm") : "npm";
8928
- if (!import_fs5.default.existsSync(import_path6.default.join(installPath, "package.json"))) {
8929
- execSync11(`"${npmCmd}" init -y`, { cwd: installPath, stdio: "pipe" });
9003
+ const npmCmd = resourcesPath ? import_path6.default.join(resourcesPath, "bundled-runtimes", "node", "bin", "npm") : await findNpmExecutable();
9004
+ if (!isAgentNpm && !import_fs5.default.existsSync(import_path6.default.join(installPath, "package.json"))) {
9005
+ execSync11(`"${npmCmd}" init -y`, { cwd: installPath, stdio: "pipe", env: buildNpmEnv(npmCmd) });
8930
9006
  }
8931
9007
  const shouldIgnoreScripts = pkg.source?.type === "npm" && !allowScripts;
8932
9008
  const installFlags = shouldIgnoreScripts ? "--ignore-scripts --no-audit --no-fund" : "--no-audit --no-fund";
8933
- execSync11(`"${npmCmd}" install ${pkg.npmPackage} ${installFlags}`, { cwd: installPath, stdio: "pipe" });
9009
+ const installCmd = isAgentNpm ? `install -g ${pkg.npmPackage} ${installFlags} --prefix "${npmInstallRoot}"` : `install ${pkg.npmPackage} ${installFlags}`;
9010
+ execSync11(`"${npmCmd}" ${installCmd}`, { cwd: installPath, stdio: "pipe", env: buildNpmEnv(npmCmd) });
8934
9011
  let bins = pkg.bins;
8935
9012
  if (!bins || bins.length === 0) {
8936
- bins = discoverNpmBins(installPath, pkg.npmPackage);
9013
+ bins = discoverNpmBins(npmInstallRoot, pkg.npmPackage, npmScope);
8937
9014
  console.log(`[Installer] Discovered binaries: ${bins.join(", ") || "(none)"}`);
8938
9015
  }
8939
9016
  let installedVersion = pkg.version || "latest";
8940
9017
  try {
8941
- const pkgJsonPath = import_path6.default.join(installPath, "node_modules", pkg.npmPackage, "package.json");
9018
+ const pkgJsonPath = getNpmPackageJsonPath(npmInstallRoot, pkg.npmPackage, npmScope);
8942
9019
  if (import_fs5.default.existsSync(pkgJsonPath)) {
8943
9020
  const pkgJson = JSON.parse(import_fs5.default.readFileSync(pkgJsonPath, "utf8"));
8944
9021
  installedVersion = pkgJson.version;
@@ -8947,13 +9024,21 @@ async function installSinglePackage(pkg, options = {}) {
8947
9024
  }
8948
9025
  if (pkg.postInstall) {
8949
9026
  onProgress?.({ phase: "postInstall", package: pkg.id, message: pkg.postInstall });
9027
+ const binDir = isAgentNpm ? getNodeRuntimeBinDir() : import_path6.default.join(installPath, "node_modules", ".bin");
8950
9028
  const postInstallCmd = pkg.postInstall.replace(
8951
9029
  /^npx\s+(\S+)/,
8952
- `"${import_path6.default.join(installPath, "node_modules", ".bin", "$1")}"`
9030
+ `"${import_path6.default.join(binDir, "$1")}"`
8953
9031
  );
8954
- execSync11(postInstallCmd, { cwd: installPath, stdio: "pipe" });
9032
+ execSync11(postInstallCmd, {
9033
+ cwd: installPath,
9034
+ stdio: "pipe",
9035
+ env: {
9036
+ ...process.env,
9037
+ PATH: `${binDir}${import_path6.default.delimiter}${process.env.PATH || ""}`
9038
+ }
9039
+ });
8955
9040
  }
8956
- const scriptsDetected = hasInstallScripts(installPath, pkg.npmPackage);
9041
+ const scriptsDetected = hasInstallScripts(npmInstallRoot, pkg.npmPackage, npmScope);
8957
9042
  const scriptsPolicy = installFlags.includes("--ignore-scripts") ? "ignore" : "allow";
8958
9043
  if (scriptsDetected && scriptsPolicy === "ignore") {
8959
9044
  console.warn(`
@@ -8972,6 +9057,8 @@ async function installSinglePackage(pkg, options = {}) {
8972
9057
  hasInstallScripts: scriptsDetected,
8973
9058
  scriptsPolicy,
8974
9059
  postInstall: pkg.postInstall,
9060
+ installType: isAgentNpm ? "npm-global" : "npm",
9061
+ npmPrefix: isAgentNpm ? npmInstallRoot : void 0,
8975
9062
  installedAt: (/* @__PURE__ */ new Date()).toISOString(),
8976
9063
  source: pkg.source || { type: "npm" }
8977
9064
  };
@@ -8982,14 +9069,20 @@ async function installSinglePackage(pkg, options = {}) {
8982
9069
  if (bins && bins.length > 0) {
8983
9070
  await createShimsForTool({
8984
9071
  id: pkg.id,
8985
- installType: "npm",
8986
- installDir: installPath,
9072
+ installType: isAgentNpm ? "npm-global" : "npm",
9073
+ installDir: npmInstallRoot,
8987
9074
  bins,
8988
9075
  name: pkgName
8989
9076
  });
8990
9077
  } else {
8991
9078
  console.warn(`[Installer] Warning: No binaries found for ${pkg.npmPackage}`);
8992
9079
  }
9080
+ if (isAgentNpm) {
9081
+ const legacyPath = import_path6.default.join(installPath, "node_modules");
9082
+ if (import_fs5.default.existsSync(legacyPath)) {
9083
+ import_fs5.default.rmSync(legacyPath, { recursive: true, force: true });
9084
+ }
9085
+ }
8993
9086
  return { success: true, id: pkg.id, path: installPath };
8994
9087
  } catch (error) {
8995
9088
  throw new Error(`Failed to install ${pkg.npmPackage}: ${error.message}`);
@@ -9138,17 +9231,31 @@ async function uninstallPackage(id) {
9138
9231
  }
9139
9232
  try {
9140
9233
  let bins = [];
9234
+ let manifest = null;
9141
9235
  if (kind2 !== "prompt") {
9142
9236
  const manifestPath = import_path6.default.join(installPath, "manifest.json");
9143
9237
  if (import_fs5.default.existsSync(manifestPath)) {
9144
9238
  try {
9145
- const manifest = JSON.parse(import_fs5.default.readFileSync(manifestPath, "utf-8"));
9239
+ manifest = JSON.parse(import_fs5.default.readFileSync(manifestPath, "utf-8"));
9146
9240
  bins = manifest.bins || manifest.binaries || [];
9147
9241
  } catch {
9148
9242
  bins = [name];
9149
9243
  }
9150
9244
  }
9151
9245
  }
9246
+ if (kind2 === "agent" && manifest?.npmPackage) {
9247
+ try {
9248
+ const { execSync: execSync11 } = await import("child_process");
9249
+ const npmCmd = await findNpmExecutable();
9250
+ const npmPrefix = getNodeRuntimeRoot();
9251
+ execSync11(`"${npmCmd}" uninstall -g ${manifest.npmPackage} --prefix "${npmPrefix}" --no-audit --no-fund`, {
9252
+ stdio: "pipe",
9253
+ env: buildNpmEnv(npmCmd)
9254
+ });
9255
+ } catch (error) {
9256
+ console.warn(`[Installer] Warning: Failed to uninstall ${manifest.npmPackage}: ${error.message}`);
9257
+ }
9258
+ }
9152
9259
  if (bins.length > 0) {
9153
9260
  removeShims(bins);
9154
9261
  }
@@ -9328,7 +9435,7 @@ async function installStackDependencies(stackPath, onProgress) {
9328
9435
  onProgress?.({ phase: "installing-deps", message: "Installing Node.js dependencies..." });
9329
9436
  try {
9330
9437
  const npmCmd = await findNpmExecutable();
9331
- execSync11(`"${npmCmd}" install`, { cwd: nodePath, stdio: "pipe" });
9438
+ execSync11(`"${npmCmd}" install`, { cwd: nodePath, stdio: "pipe", env: buildNpmEnv(npmCmd) });
9332
9439
  } catch (error) {
9333
9440
  console.warn(`Warning: Failed to install Node.js dependencies: ${error.message}`);
9334
9441
  }
@@ -9346,6 +9453,17 @@ async function installStackDependencies(stackPath, onProgress) {
9346
9453
  }
9347
9454
  }
9348
9455
  }
9456
+ function buildNpmEnv(npmCmd) {
9457
+ if (!import_path6.default.isAbsolute(npmCmd)) {
9458
+ return process.env;
9459
+ }
9460
+ const npmBinDir = import_path6.default.dirname(npmCmd);
9461
+ const basePath = process.env.PATH || "";
9462
+ return {
9463
+ ...process.env,
9464
+ PATH: [npmBinDir, basePath].join(import_path6.default.delimiter)
9465
+ };
9466
+ }
9349
9467
  async function findNpmExecutable() {
9350
9468
  const isWindows = process.platform === "win32";
9351
9469
  const arch = process.arch === "arm64" ? "arm64" : "x64";
@@ -10358,6 +10476,8 @@ __export(src_exports2, {
10358
10476
  getInstallOrder: () => getInstallOrder,
10359
10477
  getInstalledPackages: () => getInstalledPackages,
10360
10478
  getLockfilePath: () => getLockfilePath,
10479
+ getNodeRuntimeBinDir: () => getNodeRuntimeBinDir,
10480
+ getNodeRuntimeRoot: () => getNodeRuntimeRoot,
10361
10481
  getPackage: () => getPackage,
10362
10482
  getPackagePath: () => getPackagePath,
10363
10483
  getShimOwner: () => getShimOwner,
@@ -10379,6 +10499,7 @@ __export(src_exports2, {
10379
10499
  registerSystemBinary: () => registerSystemBinary,
10380
10500
  removeShims: () => removeShims,
10381
10501
  removeStack: () => removeStack,
10502
+ resolveNodeRuntimeBin: () => resolveNodeRuntimeBin,
10382
10503
  resolvePackage: () => resolvePackage,
10383
10504
  resolvePackages: () => resolvePackages,
10384
10505
  rudiConfigExists: () => rudiConfigExists,
@@ -39332,10 +39453,31 @@ function getVersion2(command, versionFlag) {
39332
39453
  return null;
39333
39454
  }
39334
39455
  }
39456
+ function getAgentBins(agentId) {
39457
+ const manifestPath = import_path22.default.join(PATHS.agents, agentId, "manifest.json");
39458
+ if (import_fs24.default.existsSync(manifestPath)) {
39459
+ try {
39460
+ const manifest = JSON.parse(import_fs24.default.readFileSync(manifestPath, "utf-8"));
39461
+ const bins = manifest.bins || manifest.binaries || [];
39462
+ if (bins.length > 0) return bins;
39463
+ } catch {
39464
+ }
39465
+ }
39466
+ return [agentId];
39467
+ }
39468
+ function findRudiAgentBin(agentId) {
39469
+ const bins = getAgentBins(agentId);
39470
+ for (const bin of bins) {
39471
+ const binPath = resolveNodeRuntimeBin(bin);
39472
+ if (import_fs24.default.existsSync(binPath)) return binPath;
39473
+ }
39474
+ return null;
39475
+ }
39335
39476
  function findBinary(command, kind2 = "binary") {
39336
39477
  const rudiPaths = [
39337
39478
  import_path22.default.join(PATHS.agents, command, "node_modules", ".bin", command),
39338
39479
  import_path22.default.join(PATHS.runtimes, command, "bin", command),
39480
+ resolveNodeRuntimeBin(command),
39339
39481
  import_path22.default.join(PATHS.binaries, command, command),
39340
39482
  import_path22.default.join(PATHS.binaries, command)
39341
39483
  ];
@@ -39358,8 +39500,8 @@ function findBinary(command, kind2 = "binary") {
39358
39500
  return { found: false, path: null, source: null };
39359
39501
  }
39360
39502
  function getAgentStatus(agent) {
39361
- const rudiPath = import_path22.default.join(PATHS.agents, agent.id, "node_modules", ".bin", agent.id);
39362
- const rudiInstalled = import_fs24.default.existsSync(rudiPath);
39503
+ const rudiPath = findRudiAgentBin(agent.id);
39504
+ const rudiInstalled = !!rudiPath;
39363
39505
  let globalPath = null;
39364
39506
  let globalInstalled = false;
39365
39507
  if (!rudiInstalled) {
@@ -39590,9 +39732,30 @@ function getVersion3(binaryPath, versionFlag = "--version") {
39590
39732
  return null;
39591
39733
  }
39592
39734
  }
39735
+ function getAgentBins2(name) {
39736
+ const manifestPath = import_path23.default.join(PATHS.agents, name, "manifest.json");
39737
+ if (import_fs25.default.existsSync(manifestPath)) {
39738
+ try {
39739
+ const manifest = JSON.parse(import_fs25.default.readFileSync(manifestPath, "utf-8"));
39740
+ const bins = manifest.bins || manifest.binaries || [];
39741
+ if (bins.length > 0) return bins;
39742
+ } catch {
39743
+ }
39744
+ }
39745
+ return [name];
39746
+ }
39747
+ function findRudiAgentBin2(name) {
39748
+ const bins = getAgentBins2(name);
39749
+ for (const bin of bins) {
39750
+ const binPath = resolveNodeRuntimeBin(bin);
39751
+ if (import_fs25.default.existsSync(binPath)) return binPath;
39752
+ }
39753
+ return null;
39754
+ }
39593
39755
  function detectKindFromFilesystem(name) {
39594
- const agentPath = import_path23.default.join(PATHS.agents, name, "node_modules", ".bin", name);
39595
- if (import_fs25.default.existsSync(agentPath)) return "agent";
39756
+ const agentManifestPath = import_path23.default.join(PATHS.agents, name, "manifest.json");
39757
+ if (import_fs25.default.existsSync(agentManifestPath)) return "agent";
39758
+ if (findRudiAgentBin2(name)) return "agent";
39596
39759
  const runtimePath = import_path23.default.join(PATHS.runtimes, name, "bin", name);
39597
39760
  if (import_fs25.default.existsSync(runtimePath)) return "runtime";
39598
39761
  const binaryPath = import_path23.default.join(PATHS.binaries, name, name);
@@ -39645,8 +39808,8 @@ async function cmdCheck(args, flags) {
39645
39808
  };
39646
39809
  switch (kind2) {
39647
39810
  case "agent": {
39648
- const rudiPath = import_path23.default.join(PATHS.agents, name, "node_modules", ".bin", name);
39649
- const rudiInstalled = import_fs25.default.existsSync(rudiPath);
39811
+ const rudiPath = findRudiAgentBin2(name);
39812
+ const rudiInstalled = !!rudiPath;
39650
39813
  let globalPath = null;
39651
39814
  let globalInstalled = false;
39652
39815
  if (!rudiInstalled) {
@@ -39801,16 +39964,6 @@ function getShimTarget(name, shimPath, type) {
39801
39964
  }
39802
39965
  function getPackageFromShim(shimName, target) {
39803
39966
  if (!target) return null;
39804
- const match = target.match(/\/(binaries|runtimes|agents)\/([^\/]+)/);
39805
- if (match) {
39806
- const [, kind2, pkgName] = match;
39807
- const kindMap = {
39808
- "binaries": "binary",
39809
- "runtimes": "runtime",
39810
- "agents": "agent"
39811
- };
39812
- return `${kindMap[kind2]}:${pkgName}`;
39813
- }
39814
39967
  const manifestDirs = [
39815
39968
  import_path24.default.join(PATHS.binaries),
39816
39969
  import_path24.default.join(PATHS.runtimes),
@@ -39834,6 +39987,16 @@ function getPackageFromShim(shimName, target) {
39834
39987
  }
39835
39988
  }
39836
39989
  }
39990
+ const match = target.match(/\/(binaries|runtimes|agents)\/([^\/]+)/);
39991
+ if (match) {
39992
+ const [, kind2, pkgName] = match;
39993
+ const kindMap = {
39994
+ "binaries": "binary",
39995
+ "runtimes": "runtime",
39996
+ "agents": "agent"
39997
+ };
39998
+ return `${kindMap[kind2]}:${pkgName}`;
39999
+ }
39837
40000
  return null;
39838
40001
  }
39839
40002
  function formatShimStatus(shim, flags) {
@@ -40670,7 +40833,7 @@ async function cmdStudio(args, flags) {
40670
40833
  }
40671
40834
 
40672
40835
  // src/index.js
40673
- var VERSION2 = true ? "1.10.8" : process.env.npm_package_version || "0.0.0";
40836
+ var VERSION2 = true ? "1.10.10" : process.env.npm_package_version || "0.0.0";
40674
40837
  async function main() {
40675
40838
  const { command, args, flags } = parseArgs(process.argv.slice(2));
40676
40839
  if (flags.version || flags.v) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "1.0.0",
3
- "generated": "2026-01-11T15:40:59.295Z",
3
+ "generated": "2026-01-11T22:42:52.951Z",
4
4
  "packages": {
5
5
  "runtimes": [
6
6
  {
@@ -122,13 +122,13 @@
122
122
  "id": "claude",
123
123
  "name": "Claude Code",
124
124
  "kind": "agent",
125
- "installDir": "claude",
126
- "basePath": "agents",
127
- "installType": "npm",
125
+ "installDir": "node",
126
+ "basePath": "runtimes",
127
+ "installType": "npm-global",
128
128
  "commands": [
129
129
  {
130
130
  "name": "claude",
131
- "bin": "node_modules/.bin/claude",
131
+ "bin": "bin/claude",
132
132
  "args": null
133
133
  }
134
134
  ]
@@ -137,13 +137,13 @@
137
137
  "id": "codex",
138
138
  "name": "OpenAI Codex",
139
139
  "kind": "agent",
140
- "installDir": "codex",
141
- "basePath": "agents",
142
- "installType": "npm",
140
+ "installDir": "node",
141
+ "basePath": "runtimes",
142
+ "installType": "npm-global",
143
143
  "commands": [
144
144
  {
145
145
  "name": "codex",
146
- "bin": "node_modules/.bin/codex",
146
+ "bin": "bin/codex",
147
147
  "args": null
148
148
  }
149
149
  ]
@@ -152,18 +152,18 @@
152
152
  "id": "copilot",
153
153
  "name": "GitHub Copilot",
154
154
  "kind": "agent",
155
- "installDir": "copilot",
156
- "basePath": "agents",
157
- "installType": "npm",
155
+ "installDir": "node",
156
+ "basePath": "runtimes",
157
+ "installType": "npm-global",
158
158
  "commands": [
159
159
  {
160
160
  "name": "copilot",
161
- "bin": "node_modules/.bin/github-copilot-cli",
161
+ "bin": "bin/github-copilot-cli",
162
162
  "args": null
163
163
  },
164
164
  {
165
165
  "name": "github-copilot-cli",
166
- "bin": "node_modules/.bin/github-copilot-cli",
166
+ "bin": "bin/github-copilot-cli",
167
167
  "args": null
168
168
  }
169
169
  ]
@@ -172,13 +172,13 @@
172
172
  "id": "gemini",
173
173
  "name": "Gemini CLI",
174
174
  "kind": "agent",
175
- "installDir": "gemini",
176
- "basePath": "agents",
177
- "installType": "npm",
175
+ "installDir": "node",
176
+ "basePath": "runtimes",
177
+ "installType": "npm-global",
178
178
  "commands": [
179
179
  {
180
180
  "name": "gemini",
181
- "bin": "node_modules/.bin/gemini",
181
+ "bin": "bin/gemini",
182
182
  "args": null
183
183
  }
184
184
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@learnrudi/cli",
3
- "version": "1.10.8",
3
+ "version": "1.10.10",
4
4
  "description": "RUDI CLI - Install and manage MCP stacks, runtimes, and AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -113,6 +113,17 @@ function getKindBasePath(kind) {
113
113
  }
114
114
  }
115
115
 
116
+ function normalizeGlobalNpmCommands(commands) {
117
+ return commands.map(cmd => {
118
+ const binName = path.basename(cmd.bin || cmd.name);
119
+ return {
120
+ name: cmd.name,
121
+ bin: path.posix.join('bin', binName),
122
+ args: cmd.args || null,
123
+ };
124
+ });
125
+ }
126
+
116
127
  /**
117
128
  * Process packages from a catalog directory
118
129
  */
@@ -122,8 +133,18 @@ function processPackages(catalogPath, kind) {
122
133
 
123
134
  return packages.map(pkg => {
124
135
  const id = pkg.id.replace(/^(runtime|binary|agent):/, '');
125
- const installDir = getInstallDir(pkg, kind);
126
- const basePath = getKindBasePath(kind);
136
+ let installDir = getInstallDir(pkg, kind);
137
+ let basePath = getKindBasePath(kind);
138
+ let installType = pkg.installType || 'binary';
139
+ let commands = extractCommands(pkg, kind);
140
+
141
+ const isGlobalNpmAgent = kind === 'agent' && (installType === 'npm' || pkg.npmPackage);
142
+ if (isGlobalNpmAgent) {
143
+ basePath = 'runtimes';
144
+ installDir = 'node';
145
+ installType = 'npm-global';
146
+ commands = normalizeGlobalNpmCommands(commands);
147
+ }
127
148
 
128
149
  return {
129
150
  id,
@@ -131,8 +152,8 @@ function processPackages(catalogPath, kind) {
131
152
  kind,
132
153
  installDir,
133
154
  basePath,
134
- installType: pkg.installType || 'binary',
135
- commands: extractCommands(pkg, kind),
155
+ installType,
156
+ commands,
136
157
  };
137
158
  });
138
159
  }
@@ -5,7 +5,7 @@
5
5
  * Fetches runtime manifests from registry and downloads:
6
6
  * 1. Node.js runtime → ~/.rudi/runtimes/node/
7
7
  * 2. Python runtime → ~/.rudi/runtimes/python/
8
- * 3. Creates shims → ~/.rudi/shims/
8
+ * 3. Creates shims → ~/.rudi/bins/
9
9
  * 4. Initializes rudi.json → ~/.rudi/rudi.json
10
10
  */
11
11
 
@@ -287,6 +287,37 @@ function loadPackagesManifest() {
287
287
  return { packages: { runtimes: [], agents: [], binaries: [] } };
288
288
  }
289
289
 
290
+ function resolveNodeRuntimeBinDir() {
291
+ const isWindows = process.platform === 'win32';
292
+ const arch = process.arch === 'arm64' ? 'arm64' : 'x64';
293
+ const binDir = isWindows ? 'Scripts' : 'bin';
294
+ const nodeRoot = path.join(RUDI_HOME, 'runtimes', 'node');
295
+ const archBin = path.join(nodeRoot, arch, binDir);
296
+ const flatBin = path.join(nodeRoot, binDir);
297
+ const nodeExe = isWindows ? 'node.exe' : 'node';
298
+
299
+ if (fs.existsSync(path.join(archBin, nodeExe))) {
300
+ return archBin;
301
+ }
302
+
303
+ return flatBin;
304
+ }
305
+
306
+ function getCliEntryPath() {
307
+ const candidates = [
308
+ path.join(path.dirname(process.argv[1]), '..', 'dist', 'index.cjs'),
309
+ path.join(path.dirname(process.argv[1]), '..', 'src', 'index.js'),
310
+ ];
311
+
312
+ for (const candidate of candidates) {
313
+ if (fs.existsSync(candidate)) {
314
+ return candidate;
315
+ }
316
+ }
317
+
318
+ return null;
319
+ }
320
+
290
321
  /**
291
322
  * Build a shim script that executes a target binary
292
323
  * Shows helpful error if binary not installed
@@ -338,6 +369,7 @@ function createShims() {
338
369
 
339
370
  const manifest = loadPackagesManifest();
340
371
  const shimDefs = [];
372
+ const nodeBinDir = resolveNodeRuntimeBinDir();
341
373
 
342
374
  // ---------------------------------------------------------------------------
343
375
  // GENERATE SHIMS FROM MANIFEST (only for installed packages)
@@ -350,7 +382,10 @@ function createShims() {
350
382
  ];
351
383
 
352
384
  for (const pkg of allPackages) {
353
- const installPath = path.join(RUDI_HOME, pkg.basePath, pkg.installDir);
385
+ const isAgentNpm = pkg.kind === 'agent' && (pkg.installType === 'npm' || pkg.installType === 'npm-global');
386
+ const installPath = isAgentNpm
387
+ ? path.join(RUDI_HOME, 'runtimes', 'node')
388
+ : path.join(RUDI_HOME, pkg.basePath, pkg.installDir);
354
389
 
355
390
  // Only create shims for packages that are actually installed
356
391
  // Check if the install directory exists
@@ -359,7 +394,10 @@ function createShims() {
359
394
  }
360
395
 
361
396
  for (const cmd of pkg.commands) {
362
- const binPath = path.join(installPath, cmd.bin);
397
+ const binName = isAgentNpm ? path.basename(cmd.bin) : cmd.bin;
398
+ const binPath = isAgentNpm
399
+ ? path.join(nodeBinDir, binName)
400
+ : path.join(installPath, cmd.bin);
363
401
 
364
402
  // Only create shim if the target binary exists
365
403
  if (!fs.existsSync(binPath)) {
@@ -386,6 +424,26 @@ function createShims() {
386
424
  // RUDI SHIMS (router, mcp) - always included
387
425
  // ---------------------------------------------------------------------------
388
426
 
427
+ const cliEntryPath = getCliEntryPath();
428
+ if (cliEntryPath) {
429
+ const nodeBin = path.join(nodeBinDir, isWindows ? 'node.exe' : 'node');
430
+ shimDefs.push({
431
+ name: 'rudi',
432
+ script: `#!/bin/sh
433
+ CLI_ENTRY="${cliEntryPath.replace(/"/g, '\\"')}"
434
+ NODE_BIN="${nodeBin.replace(/"/g, '\\"')}"
435
+ if [ -x "$CLI_ENTRY" ]; then
436
+ if [ -x "$NODE_BIN" ]; then
437
+ exec "$NODE_BIN" "$CLI_ENTRY" "$@"
438
+ fi
439
+ exec node "$CLI_ENTRY" "$@"
440
+ fi
441
+ echo "RUDI: CLI entry not found at $CLI_ENTRY" 1>&2
442
+ exit 127
443
+ `
444
+ });
445
+ }
446
+
389
447
  // rudi-mcp shim for direct stack access
390
448
  shimDefs.push({
391
449
  name: 'rudi-mcp',
@@ -397,14 +455,16 @@ exec rudi mcp "$@"
397
455
  });
398
456
 
399
457
  // rudi-router shim for aggregated MCP server
458
+ const routerNodeBin = path.join(nodeBinDir, isWindows ? 'node.exe' : 'node');
400
459
  shimDefs.push({
401
460
  name: 'rudi-router',
402
461
  script: `#!/bin/bash
403
462
  # RUDI Router - Master MCP server for all installed stacks
404
463
  # Reads ~/.rudi/rudi.json and proxies tool calls to correct stack
405
464
  RUDI_HOME="$HOME/.rudi"
406
- if [ -x "$RUDI_HOME/runtimes/node/bin/node" ]; then
407
- exec "$RUDI_HOME/runtimes/node/bin/node" "$RUDI_HOME/router/router-mcp.js" "$@"
465
+ NODE_BIN="${routerNodeBin.replace(/"/g, '\\"')}"
466
+ if [ -x "$NODE_BIN" ]; then
467
+ exec "$NODE_BIN" "$RUDI_HOME/router/router-mcp.js" "$@"
408
468
  else
409
469
  exec node "$RUDI_HOME/router/router-mcp.js" "$@"
410
470
  fi