From b5921724256249132b360246eb343c2a81a7458e Mon Sep 17 00:00:00 2001 From: Wanjohi <71614375+wanjohiryan@users.noreply.github.com> Date: Thu, 6 Nov 2025 14:57:01 +0300 Subject: [PATCH] feat: Add JS package manager support --- packages/core/src/installation/index.ts | 62 +++++++++++++++++++++---- packages/studio/src/index.ts | 10 ++++ 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/packages/core/src/installation/index.ts b/packages/core/src/installation/index.ts index 71d33b3b..e1d299a2 100644 --- a/packages/core/src/installation/index.ts +++ b/packages/core/src/installation/index.ts @@ -54,6 +54,41 @@ export namespace Installation { if (process.execPath.includes(path.join(".nestri", "bin"))) return "curl"; if (process.execPath.includes(path.join(".local", "bin"))) return "curl"; + const exec = process.execPath.toLowerCase(); + const checks = [ + { + name: "npm" as const, + command: () => $`npm list -g --depth=0`.throws(false).text(), + }, + { + name: "yarn" as const, + command: () => $`yarn global list`.throws(false).text(), + }, + { + name: "pnpm" as const, + command: () => $`pnpm list -g --depth=0`.throws(false).text(), + }, + { + name: "bun" as const, + command: () => $`bun pm ls -g`.throws(false).text(), + }, + ]; + + checks.sort((a, b) => { + const aMatches = exec.includes(a.name); + const bMatches = exec.includes(b.name); + if (aMatches && !bMatches) return -1; + if (!aMatches && bMatches) return 1; + return 0; + }); + + for (const check of checks) { + const output = await check.command(); + if (output.includes("@nestri/studio")) { + return check.name; + } + } + return "unknown"; } @@ -73,6 +108,15 @@ export namespace Installation { VERSION: target, }); break; + case "npm": + cmd = $`npm install -g @nestri/studio@${target}`; + break; + case "pnpm": + cmd = $`pnpm install -g @nestri/studio@${target}`; + break; + case "bun": + cmd = $`bun install -g @nestri/studio@${target}`; + break; default: throw new Error(`Unknown method: ${method}`); } @@ -96,15 +140,13 @@ export namespace Installation { export const USER_AGENT = `nestri/${CHANNEL}/${VERSION}`; export async function latest() { - const res = await fetch( - `https://api.github.com/repos/nestrilabs/nestri/releases/latest`, - ); - - if (!res.ok) { - throw new Error(`GitHub API error: ${res.statusText}`); - } - - const data = await res.json(); - return data.tag_name?.replace(/^v/, ""); // removes leading "v" if present + const [major] = VERSION.split(".").map((x) => Number(x)); + const channel = CHANNEL === "latest" ? `latest-${major}` : CHANNEL; + return fetch(`https://registry.npmjs.org/@nestri/studio/${channel}`) + .then((res) => { + if (!res.ok) throw new Error(res.statusText); + return res.json(); + }) + .then((data: any) => data.version); } } diff --git a/packages/studio/src/index.ts b/packages/studio/src/index.ts index 2183735c..11f68644 100644 --- a/packages/studio/src/index.ts +++ b/packages/studio/src/index.ts @@ -5,6 +5,16 @@ import { Log } from "@nestri/core/utils/log"; import { NamedError } from "@nestri/core/utils/error"; import { Installation } from "@nestri/core/installation/index"; +/** + * + * So, the utility of this is: + * 1. To run the API, and Auth servers (locally or in the cloud) using docker sockets + * 2. To provide a CLI interface for managing GPUs, games, and other resources (Studio) thru an ssh utility + * 3. To build client and package it into a docker container for local or cloud deployment + * 4. + * + */ + process.on("unhandledRejection", (e) => { Log.Default.error("rejection", { e: e instanceof Error ? e.message : e,