yaver-cli
Advanced tools
| "use strict"; | ||
| /** | ||
| * Optional native desktop companion for the unified npm CLI. | ||
| * | ||
| * The npm package intentionally does not contain Electron itself. `yaver | ||
| * desktop install` downloads the architecture-specific canonical `electron/` | ||
| * release, verifies the release checksum, verifies the OS signature where the | ||
| * host provides one, and installs per-user. The Go agent remains independently | ||
| * usable through the console and an already-running agent is adopted by the | ||
| * GUI instead of duplicated. | ||
| */ | ||
| const crypto = require("node:crypto"); | ||
| const fs = require("node:fs"); | ||
| const https = require("node:https"); | ||
| const os = require("node:os"); | ||
| const path = require("node:path"); | ||
| const { spawn, spawnSync } = require("node:child_process"); | ||
| const { pipeline } = require("node:stream/promises"); | ||
| const DEFAULT_REPO = "yaver-io/yaver.io"; | ||
| const WINDOWS_PUBLISHER_PATTERN = "Simkab"; | ||
| const DESKTOP_HELP = ` | ||
| yaver desktop — optional native Yaver GUI (the console remains available as \`yaver\`) | ||
| Commands: | ||
| yaver desktop Open the installed desktop app | ||
| yaver desktop install Download, verify, install per-user, and open | ||
| yaver desktop update Install the latest verified desktop release | ||
| yaver desktop status Show platform, installed path, and release state | ||
| yaver desktop path Print the installed application path | ||
| yaver desktop download [--format] Download a verified installer without running it | ||
| Options: | ||
| --no-open Install without launching (used by npm bootstrap) | ||
| Formats: | ||
| macOS: dmg (default) | ||
| Linux: appimage (default), deb, rpm | ||
| Windows: exe (default) | ||
| The GUI is downloaded as a separately signed, checksum-verified artifact; npm | ||
| does not embed Electron in the CLI tarball. Global installs bootstrap it on an | ||
| interactive desktop unless YAVER_SKIP_POSTINSTALL_DESKTOP=1 is set. Headless | ||
| Linux/CI stays console-only. Runner bootstrap can independently be disabled | ||
| with YAVER_SKIP_POSTINSTALL_RUNNERS=1. | ||
| `; | ||
| function normalizedArch(arch = process.arch) { | ||
| if (arch === "x64" || arch === "amd64") return "x64"; | ||
| if (arch === "arm64" || arch === "aarch64") return "arm64"; | ||
| throw new Error(`Yaver Desktop is not published for architecture ${arch}.`); | ||
| } | ||
| function defaultFormat(platform = process.platform) { | ||
| if (platform === "darwin") return "dmg"; | ||
| if (platform === "linux") return "appimage"; | ||
| if (platform === "win32") return "exe"; | ||
| throw new Error(`Yaver Desktop is not published for platform ${platform}.`); | ||
| } | ||
| function assetName(version, platform = process.platform, arch = process.arch, format = defaultFormat(platform)) { | ||
| const cpu = normalizedArch(arch); | ||
| const ext = String(format).toLowerCase(); | ||
| if (platform === "darwin" && ext === "dmg") return `yaver-gui-${version}-mac-${cpu}.dmg`; | ||
| if (platform === "linux" && ["appimage", "deb", "rpm"].includes(ext)) { | ||
| const suffix = ext === "appimage" ? "AppImage" : ext; | ||
| return `yaver-gui-${version}-linux-${cpu}.${suffix}`; | ||
| } | ||
| if (platform === "win32" && ext === "exe") return `yaver-gui-${version}-win-${cpu}-setup.exe`; | ||
| throw new Error(`Desktop format ${format} is not supported on ${platform}.`); | ||
| } | ||
| function parseGuiRelease(releases, requestedVersion = "") { | ||
| const rows = Array.isArray(releases) ? releases : []; | ||
| const wanted = String(requestedVersion || "").replace(/^gui\/v/, "").replace(/^v/, ""); | ||
| const release = rows.find((row) => { | ||
| if (!row || row.draft || row.prerelease) return false; | ||
| const match = String(row.tag_name || "").match(/^gui\/v(\d+\.\d+\.\d+)$/); | ||
| return match && (!wanted || match[1] === wanted); | ||
| }); | ||
| if (!release) throw new Error(wanted ? `No published GUI release exists for ${wanted}.` : "No published GUI release exists."); | ||
| return { ...release, version: String(release.tag_name).slice("gui/v".length) }; | ||
| } | ||
| function checksumFor(text, filename) { | ||
| for (const line of String(text || "").split(/\r?\n/)) { | ||
| const match = line.trim().match(/^([a-fA-F0-9]{64})\s+\*?(.+)$/); | ||
| if (match && match[2] === filename) return match[1].toLowerCase(); | ||
| } | ||
| throw new Error(`checksums.txt has no SHA-256 entry for ${filename}.`); | ||
| } | ||
| function installedDesktopCandidates(platform = process.platform, env = process.env, home = os.homedir()) { | ||
| if (platform === "darwin") { | ||
| return [path.join(home, "Applications", "Yaver.app"), "/Applications/Yaver.app"]; | ||
| } | ||
| if (platform === "linux") { | ||
| return [ | ||
| path.join(home, ".local", "opt", "yaver", "Yaver.AppImage"), | ||
| "/opt/Yaver/yaver-desktop", | ||
| "/usr/bin/yaver-desktop", | ||
| "/usr/local/bin/yaver-desktop", | ||
| ]; | ||
| } | ||
| if (platform === "win32") { | ||
| const local = env.LOCALAPPDATA || path.join(home, "AppData", "Local"); | ||
| return [ | ||
| path.join(local, "Programs", "Yaver", "Yaver.exe"), | ||
| path.join(local, "Yaver", "Yaver.exe"), | ||
| ]; | ||
| } | ||
| return []; | ||
| } | ||
| function installedDesktopPath(platform = process.platform) { | ||
| return installedDesktopCandidates(platform).find((candidate) => fs.existsSync(candidate)) || ""; | ||
| } | ||
| function request(url, options = {}) { | ||
| return new Promise((resolve, reject) => { | ||
| const req = https.get(url, { | ||
| headers: { "User-Agent": "yaver-cli-desktop", Accept: "application/vnd.github+json", ...(options.headers || {}) }, | ||
| }, (response) => { | ||
| if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) { | ||
| response.resume(); | ||
| resolve(request(response.headers.location, options)); | ||
| return; | ||
| } | ||
| resolve(response); | ||
| }); | ||
| req.setTimeout(30_000, () => req.destroy(new Error(`Desktop release request timed out: ${url}`))); | ||
| req.on("error", reject); | ||
| }); | ||
| } | ||
| async function responseText(response) { | ||
| let body = ""; | ||
| response.setEncoding("utf8"); | ||
| for await (const chunk of response) body += chunk; | ||
| return body; | ||
| } | ||
| async function download(url, destination) { | ||
| const response = await request(url, { headers: { Accept: "application/octet-stream" } }); | ||
| if (response.statusCode !== 200) { | ||
| const body = await responseText(response); | ||
| throw new Error(`Desktop download failed (HTTP ${response.statusCode}) from ${url}${body ? `: ${body.slice(0, 160)}` : ""}`); | ||
| } | ||
| await pipeline(response, fs.createWriteStream(destination, { mode: 0o700 })); | ||
| } | ||
| function sha256(file) { | ||
| const hash = crypto.createHash("sha256"); | ||
| const fd = fs.openSync(file, "r"); | ||
| const buffer = Buffer.allocUnsafe(1024 * 1024); | ||
| try { | ||
| let count = 0; | ||
| while ((count = fs.readSync(fd, buffer, 0, buffer.length, null)) > 0) hash.update(buffer.subarray(0, count)); | ||
| } finally { | ||
| fs.closeSync(fd); | ||
| } | ||
| return hash.digest("hex"); | ||
| } | ||
| async function resolveDownload({ platform, arch, format, version = "" }) { | ||
| const repo = process.env.YAVER_DESKTOP_REPO || DEFAULT_REPO; | ||
| const response = await request(`https://api.github.com/repos/${repo}/releases?per_page=100`); | ||
| if (response.statusCode !== 200) throw new Error(`Desktop release lookup failed (HTTP ${response.statusCode}).`); | ||
| const release = parseGuiRelease(JSON.parse(await responseText(response)), version || process.env.YAVER_DESKTOP_VERSION); | ||
| const filename = assetName(release.version, platform, arch, format); | ||
| const assets = new Map((release.assets || []).map((entry) => [entry.name, entry])); | ||
| const artifact = assets.get(filename); | ||
| const checksums = assets.get("checksums.txt"); | ||
| if (!artifact) throw new Error(`Release ${release.tag_name} does not contain ${filename}.`); | ||
| if (!checksums) throw new Error(`Release ${release.tag_name} has no checksums.txt; refusing an unverified desktop download.`); | ||
| return { release, artifact, checksums, filename }; | ||
| } | ||
| async function downloadVerified(options) { | ||
| const resolved = await resolveDownload(options); | ||
| const dir = fs.mkdtempSync(path.join(os.tmpdir(), "yaver-desktop-")); | ||
| const artifactPath = path.join(dir, resolved.filename); | ||
| const checksumsPath = path.join(dir, "checksums.txt"); | ||
| try { | ||
| await download(resolved.artifact.browser_download_url, artifactPath); | ||
| await download(resolved.checksums.browser_download_url, checksumsPath); | ||
| const expected = checksumFor(fs.readFileSync(checksumsPath, "utf8"), resolved.filename); | ||
| const actual = sha256(artifactPath); | ||
| if (actual !== expected) throw new Error(`SHA-256 mismatch for ${resolved.filename}: expected ${expected}, downloaded ${actual}.`); | ||
| return { ...resolved, artifactPath, tempDir: dir, sha256: actual }; | ||
| } catch (error) { | ||
| fs.rmSync(dir, { recursive: true, force: true }); | ||
| throw error; | ||
| } | ||
| } | ||
| function runChecked(command, args, message) { | ||
| const result = spawnSync(command, args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }); | ||
| if (result.status !== 0) throw new Error(`${message}: ${(result.stderr || result.stdout || `exit ${result.status}`).trim()}`); | ||
| return result.stdout || result.stderr || ""; | ||
| } | ||
| function verifyMacApp(appPath) { | ||
| runChecked("codesign", ["--verify", "--deep", "--strict", "--verbose=2", appPath], "macOS code-sign verification failed"); | ||
| const details = runChecked("codesign", ["-dv", "--verbose=4", appPath], "Could not inspect the macOS signature"); | ||
| if (!details.includes("Identifier=io.yaver.gui")) throw new Error("The signed macOS app has an unexpected bundle identifier."); | ||
| runChecked("spctl", ["--assess", "--type", "exec", "--verbose=2", appPath], "macOS Gatekeeper rejected Yaver"); | ||
| runChecked("xcrun", ["stapler", "validate", appPath], "The macOS notarization ticket is missing or invalid"); | ||
| } | ||
| function installMac(downloaded) { | ||
| const mount = path.join(downloaded.tempDir, "mount"); | ||
| fs.mkdirSync(mount); | ||
| runChecked("hdiutil", ["attach", "-nobrowse", "-readonly", "-mountpoint", mount, downloaded.artifactPath], "Could not mount the Yaver DMG"); | ||
| try { | ||
| const source = path.join(mount, "Yaver.app"); | ||
| if (!fs.existsSync(source)) throw new Error("The verified DMG does not contain Yaver.app."); | ||
| verifyMacApp(source); | ||
| const applications = path.join(os.homedir(), "Applications"); | ||
| fs.mkdirSync(applications, { recursive: true }); | ||
| const target = path.join(applications, "Yaver.app"); | ||
| const staging = path.join(applications, `.Yaver.app.install-${process.pid}`); | ||
| const backup = path.join(applications, `.Yaver.app.backup-${process.pid}`); | ||
| fs.rmSync(staging, { recursive: true, force: true }); | ||
| fs.cpSync(source, staging, { recursive: true, force: false, preserveTimestamps: true }); | ||
| verifyMacApp(staging); | ||
| let backedUp = false; | ||
| try { | ||
| if (fs.existsSync(target)) { fs.renameSync(target, backup); backedUp = true; } | ||
| fs.renameSync(staging, target); | ||
| if (backedUp) fs.rmSync(backup, { recursive: true, force: true }); | ||
| } catch (error) { | ||
| if (!fs.existsSync(target) && backedUp && fs.existsSync(backup)) fs.renameSync(backup, target); | ||
| throw error; | ||
| } finally { | ||
| fs.rmSync(staging, { recursive: true, force: true }); | ||
| } | ||
| return target; | ||
| } finally { | ||
| spawnSync("hdiutil", ["detach", mount], { stdio: "ignore" }); | ||
| } | ||
| } | ||
| function installLinuxAppImage(downloaded) { | ||
| const root = path.join(os.homedir(), ".local", "opt", "yaver"); | ||
| const target = path.join(root, "Yaver.AppImage"); | ||
| const staging = path.join(root, `.Yaver.AppImage.install-${process.pid}`); | ||
| fs.mkdirSync(root, { recursive: true }); | ||
| fs.copyFileSync(downloaded.artifactPath, staging); | ||
| fs.chmodSync(staging, 0o755); | ||
| fs.renameSync(staging, target); | ||
| const applications = path.join(os.homedir(), ".local", "share", "applications"); | ||
| fs.mkdirSync(applications, { recursive: true }); | ||
| const execPath = `"${target.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/%/g, "%%")}"`; | ||
| fs.writeFileSync(path.join(applications, "io.yaver.gui.desktop"), [ | ||
| "[Desktop Entry]", "Name=Yaver", "Comment=AI development workspace and remote node", | ||
| `Exec=${execPath} %U`, "Terminal=false", "Type=Application", "Categories=Development;Utility;", | ||
| "StartupWMClass=Yaver", "MimeType=x-scheme-handler/yaver;", "", | ||
| ].join("\n")); | ||
| const binDir = path.join(os.homedir(), ".local", "bin"); | ||
| const link = path.join(binDir, "yaver-desktop"); | ||
| fs.mkdirSync(binDir, { recursive: true }); | ||
| try { | ||
| const stat = fs.lstatSync(link); | ||
| if (!stat.isSymbolicLink()) throw new Error(`${link} already exists and is not a Yaver-managed symlink.`); | ||
| const priorTarget = path.resolve(path.dirname(link), fs.readlinkSync(link)); | ||
| if (!priorTarget.startsWith(root + path.sep) && priorTarget !== target) { | ||
| throw new Error(`${link} points outside the Yaver install directory; refusing to replace it.`); | ||
| } | ||
| fs.unlinkSync(link); | ||
| } catch (error) { | ||
| if (error.code !== "ENOENT") throw error; | ||
| } | ||
| fs.symlinkSync(target, link); | ||
| return target; | ||
| } | ||
| function verifyWindowsInstaller(installer) { | ||
| const escaped = installer.replace(/'/g, "''"); | ||
| const script = `$s=Get-AuthenticodeSignature -LiteralPath '${escaped}'; if ($s.Status -ne 'Valid') { throw \"Authenticode status: $($s.Status) $($s.StatusMessage)\" }; if ($s.SignerCertificate.Subject -notmatch '(?i)${WINDOWS_PUBLISHER_PATTERN}') { throw \"Unexpected publisher: $($s.SignerCertificate.Subject)\" }`; | ||
| runChecked("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], "Windows Authenticode verification failed"); | ||
| } | ||
| function openDesktop(appPath, platform = process.platform) { | ||
| let command; | ||
| let args; | ||
| if (platform === "darwin") { command = "open"; args = [appPath]; } | ||
| else { command = appPath; args = []; } | ||
| const child = spawn(command, args, { detached: true, stdio: "ignore", env: process.env }); | ||
| child.once("error", (error) => console.error(`Could not open Yaver Desktop: ${error.message}`)); | ||
| child.unref(); | ||
| } | ||
| function parseDesktopArgs(args) { | ||
| const command = !args[0] || args[0].startsWith("-") ? "open" : args[0]; | ||
| let format = ""; | ||
| let destination = ""; | ||
| let noOpen = false; | ||
| for (let i = command === "open" && args[0]?.startsWith("-") ? 0 : 1; i < args.length; i++) { | ||
| if (args[i] === "--format" && args[i + 1]) format = args[++i].toLowerCase(); | ||
| else if (args[i] === "--output" && args[i + 1]) destination = args[++i]; | ||
| else if (args[i] === "--no-open") noOpen = true; | ||
| } | ||
| return { command, format: format || defaultFormat(), destination, noOpen }; | ||
| } | ||
| async function desktop(args = []) { | ||
| if (args.includes("--help") || args.includes("-h") || args[0] === "help") { | ||
| console.log(DESKTOP_HELP); | ||
| return; | ||
| } | ||
| const options = parseDesktopArgs(args); | ||
| const existing = installedDesktopPath(); | ||
| if (options.command === "status") { | ||
| console.log(JSON.stringify({ installed: Boolean(existing), path: existing || null, platform: process.platform, arch: normalizedArch() }, null, 2)); | ||
| return; | ||
| } | ||
| if (options.command === "path") { | ||
| if (!existing) throw new Error("Yaver Desktop is not installed. Run `yaver desktop install`."); | ||
| console.log(existing); | ||
| return; | ||
| } | ||
| if (options.command === "open") { | ||
| if (!existing) throw new Error("Yaver Desktop is not installed. Run `yaver desktop install` (the console remains available as `yaver`)."); | ||
| openDesktop(existing); | ||
| return; | ||
| } | ||
| if (!["install", "update", "download"].includes(options.command)) throw new Error(`Unknown desktop command: ${options.command}\n${DESKTOP_HELP}`); | ||
| const downloaded = await downloadVerified({ platform: process.platform, arch: process.arch, format: options.format }); | ||
| if (options.command === "download" || (process.platform === "linux" && options.format !== "appimage")) { | ||
| const destination = path.resolve(options.destination || path.join(process.cwd(), downloaded.filename)); | ||
| fs.copyFileSync(downloaded.artifactPath, destination); | ||
| fs.rmSync(downloaded.tempDir, { recursive: true, force: true }); | ||
| console.log(`Verified ${downloaded.filename} → ${destination}`); | ||
| if (process.platform === "linux" && options.format === "deb") console.log(`Install with: sudo apt-get install ${JSON.stringify(destination)}`); | ||
| if (process.platform === "linux" && options.format === "rpm") console.log(`Install with: sudo dnf install ${JSON.stringify(destination)}`); | ||
| return; | ||
| } | ||
| let installed = ""; | ||
| try { | ||
| if (process.platform === "darwin") installed = installMac(downloaded); | ||
| else if (process.platform === "linux") installed = installLinuxAppImage(downloaded); | ||
| else if (process.platform === "win32") { | ||
| verifyWindowsInstaller(downloaded.artifactPath); | ||
| const child = spawn(downloaded.artifactPath, options.noOpen ? ["/S"] : [], { detached: true, stdio: "ignore" }); | ||
| child.once("error", (error) => console.error(`Could not open the verified Yaver installer: ${error.message}`)); | ||
| child.unref(); | ||
| console.log(options.noOpen | ||
| ? "Verified installer started in per-user silent mode." | ||
| : "Verified installer opened. Complete the per-user installation in the Yaver setup window."); | ||
| return; | ||
| } | ||
| } finally { | ||
| if (process.platform !== "win32") fs.rmSync(downloaded.tempDir, { recursive: true, force: true }); | ||
| } | ||
| console.log(`Yaver Desktop ${downloaded.release.version} installed at ${installed}`); | ||
| if (!options.noOpen) openDesktop(installed); | ||
| } | ||
| module.exports = { | ||
| DESKTOP_HELP, | ||
| assetName, | ||
| checksumFor, | ||
| defaultFormat, | ||
| desktop, | ||
| installedDesktopCandidates, | ||
| normalizedArch, | ||
| parseDesktopArgs, | ||
| parseGuiRelease, | ||
| WINDOWS_PUBLISHER_PATTERN, | ||
| }; |
| "use strict"; | ||
| /** | ||
| * A machine that already has a supported coding runner has made its choice. | ||
| * Yaver should wire that runner, not install competitors beside it. This is | ||
| * especially important for existing OpenCode/BYOK setups: provider and model | ||
| * configuration belong to OpenCode and must remain untouched. | ||
| * | ||
| * Fresh machines retain the existing bootstrap behaviour so the global npm | ||
| * install still produces a usable development node. | ||
| */ | ||
| function codingRunnerBootstrapPlan(entries, commandExists) { | ||
| const installed = entries.filter((entry) => commandExists(entry.command)); | ||
| return { | ||
| installed, | ||
| toInstall: installed.length > 0 ? [] : entries, | ||
| }; | ||
| } | ||
| module.exports = { codingRunnerBootstrapPlan }; |
+4
-3
| { | ||
| "name": "yaver-cli", | ||
| "version": "1.99.411", | ||
| "version": "1.99.415", | ||
| "mcpName": "io.github.yaver-io/yaver", | ||
@@ -14,2 +14,3 @@ "description": "Unified npm bootstrap for the Yaver agent, SDK injection, and local-first developer runtime", | ||
| "scripts": { | ||
| "test": "node --test test/*.test.js", | ||
| "postinstall": "node src/postinstall.js", | ||
@@ -43,3 +44,3 @@ "preuninstall": "node src/preuninstall.js" | ||
| "type": "git", | ||
| "url": "git+https://github.com/kivanccakmak/yaver.io.git" | ||
| "url": "git+https://github.com/yaver-io/yaver.io.git" | ||
| }, | ||
@@ -54,2 +55,2 @@ "homepage": "https://yaver.io", | ||
| } | ||
| } | ||
| } |
+92
-92
@@ -7,3 +7,3 @@ { | ||
| "hermes": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "bytecodeVersion": 96 | ||
@@ -129,3 +129,3 @@ }, | ||
| "@amplitude/analytics-react-native": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -137,3 +137,3 @@ "cliPush": true, | ||
| "@intercom/intercom-react-native": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -145,3 +145,3 @@ "cliPush": true, | ||
| "@notifee/react-native": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -153,3 +153,3 @@ "cliPush": true, | ||
| "@react-native-async-storage/async-storage": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -161,3 +161,3 @@ "cliPush": true, | ||
| "@react-native-community/datetimepicker": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -169,3 +169,3 @@ "cliPush": true, | ||
| "@react-native-community/netinfo": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -177,3 +177,3 @@ "cliPush": true, | ||
| "@react-native-community/slider": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -185,3 +185,3 @@ "cliPush": true, | ||
| "@react-native-google-signin/google-signin": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -193,3 +193,3 @@ "cliPush": true, | ||
| "@react-native-masked-view/masked-view": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -201,3 +201,3 @@ "cliPush": true, | ||
| "@react-native-menu/menu": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -209,3 +209,3 @@ "cliPush": true, | ||
| "@react-native-ml-kit/text-recognition": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -217,3 +217,3 @@ "cliPush": true, | ||
| "@react-native-picker/picker": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -225,3 +225,3 @@ "cliPush": true, | ||
| "@react-native-segmented-control/segmented-control": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -233,3 +233,3 @@ "cliPush": true, | ||
| "@sentry/react-native": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -241,3 +241,3 @@ "cliPush": true, | ||
| "@shopify/react-native-skia": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -249,3 +249,3 @@ "cliPush": true, | ||
| "@stripe/stripe-react-native": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -257,3 +257,3 @@ "cliPush": true, | ||
| "expo-apple-authentication": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -265,3 +265,3 @@ "cliPush": true, | ||
| "expo-asset": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -273,3 +273,3 @@ "cliPush": true, | ||
| "expo-av": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -281,3 +281,3 @@ "cliPush": true, | ||
| "expo-background-fetch": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -289,3 +289,3 @@ "cliPush": true, | ||
| "expo-battery": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -297,3 +297,3 @@ "cliPush": true, | ||
| "expo-blur": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -305,3 +305,3 @@ "cliPush": true, | ||
| "expo-brightness": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -313,3 +313,3 @@ "cliPush": true, | ||
| "expo-calendar": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -321,3 +321,3 @@ "cliPush": true, | ||
| "expo-camera": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -329,3 +329,3 @@ "cliPush": true, | ||
| "expo-clipboard": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -337,3 +337,3 @@ "cliPush": true, | ||
| "expo-constants": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -345,3 +345,3 @@ "cliPush": true, | ||
| "expo-contacts": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -353,3 +353,3 @@ "cliPush": true, | ||
| "expo-crypto": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -361,3 +361,3 @@ "cliPush": true, | ||
| "expo-device": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -369,3 +369,3 @@ "cliPush": true, | ||
| "expo-document-picker": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -377,3 +377,3 @@ "cliPush": true, | ||
| "expo-file-system": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -385,3 +385,3 @@ "cliPush": true, | ||
| "expo-font": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -393,3 +393,3 @@ "cliPush": true, | ||
| "expo-haptics": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -401,3 +401,3 @@ "cliPush": true, | ||
| "expo-image": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -409,3 +409,3 @@ "cliPush": true, | ||
| "expo-image-manipulator": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -417,3 +417,3 @@ "cliPush": true, | ||
| "expo-image-picker": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -425,3 +425,3 @@ "cliPush": true, | ||
| "expo-keep-awake": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -433,3 +433,3 @@ "cliPush": true, | ||
| "expo-linear-gradient": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -441,3 +441,3 @@ "cliPush": true, | ||
| "expo-linking": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -449,3 +449,3 @@ "cliPush": true, | ||
| "expo-local-authentication": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -457,3 +457,3 @@ "cliPush": true, | ||
| "expo-localization": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -465,3 +465,3 @@ "cliPush": true, | ||
| "expo-location": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -473,3 +473,3 @@ "cliPush": true, | ||
| "expo-mail-composer": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -481,3 +481,3 @@ "cliPush": true, | ||
| "expo-media-library": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -489,3 +489,3 @@ "cliPush": true, | ||
| "expo-notifications": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -497,3 +497,3 @@ "cliPush": true, | ||
| "expo-print": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -505,3 +505,3 @@ "cliPush": true, | ||
| "expo-screen-orientation": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -513,3 +513,3 @@ "cliPush": true, | ||
| "expo-secure-store": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -521,3 +521,3 @@ "cliPush": true, | ||
| "expo-sensors": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -529,3 +529,3 @@ "cliPush": true, | ||
| "expo-share-intent": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -537,3 +537,3 @@ "cliPush": true, | ||
| "expo-sharing": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -545,3 +545,3 @@ "cliPush": true, | ||
| "expo-speech": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -553,3 +553,3 @@ "cliPush": true, | ||
| "expo-splash-screen": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -561,3 +561,3 @@ "cliPush": true, | ||
| "expo-sqlite": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -569,3 +569,3 @@ "cliPush": true, | ||
| "expo-status-bar": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -577,3 +577,3 @@ "cliPush": true, | ||
| "expo-system-ui": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -585,3 +585,3 @@ "cliPush": true, | ||
| "expo-task-manager": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -593,3 +593,3 @@ "cliPush": true, | ||
| "expo-updates": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -601,3 +601,3 @@ "cliPush": true, | ||
| "expo-video": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -609,3 +609,3 @@ "cliPush": true, | ||
| "expo-video-thumbnails": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -617,3 +617,3 @@ "cliPush": true, | ||
| "expo-web-browser": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -625,3 +625,3 @@ "cliPush": true, | ||
| "lottie-react-native": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -633,3 +633,3 @@ "cliPush": true, | ||
| "react-native-audio-api": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -641,3 +641,3 @@ "cliPush": true, | ||
| "react-native-ble-plx": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -649,3 +649,3 @@ "cliPush": true, | ||
| "react-native-device-info": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -657,3 +657,3 @@ "cliPush": true, | ||
| "react-native-gesture-handler": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -665,3 +665,3 @@ "cliPush": true, | ||
| "react-native-get-random-values": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -673,3 +673,3 @@ "cliPush": true, | ||
| "react-native-iap": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -681,3 +681,3 @@ "cliPush": true, | ||
| "react-native-keyboard-controller": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -689,3 +689,3 @@ "cliPush": true, | ||
| "react-native-maps": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -697,3 +697,3 @@ "cliPush": true, | ||
| "react-native-mmkv": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -705,3 +705,3 @@ "cliPush": true, | ||
| "react-native-nitro-image": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -713,3 +713,3 @@ "cliPush": true, | ||
| "react-native-nitro-modules": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -721,3 +721,3 @@ "cliPush": true, | ||
| "react-native-pager-view": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -729,3 +729,3 @@ "cliPush": true, | ||
| "react-native-passkey": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -737,3 +737,3 @@ "cliPush": true, | ||
| "react-native-pdf": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -745,3 +745,3 @@ "cliPush": true, | ||
| "react-native-permissions": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -753,3 +753,3 @@ "cliPush": true, | ||
| "react-native-purchases": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -761,3 +761,3 @@ "cliPush": true, | ||
| "react-native-reanimated": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -769,3 +769,3 @@ "cliPush": true, | ||
| "react-native-safe-area-context": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -777,3 +777,3 @@ "cliPush": true, | ||
| "react-native-screens": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -785,3 +785,3 @@ "cliPush": true, | ||
| "react-native-share": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -793,3 +793,3 @@ "cliPush": true, | ||
| "react-native-svg": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -801,3 +801,3 @@ "cliPush": true, | ||
| "react-native-udp": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -809,3 +809,3 @@ "cliPush": true, | ||
| "react-native-video": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -817,3 +817,3 @@ "cliPush": true, | ||
| "react-native-view-shot": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -825,3 +825,3 @@ "cliPush": true, | ||
| "react-native-vision-camera": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -833,3 +833,3 @@ "cliPush": true, | ||
| "react-native-webview": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -841,3 +841,3 @@ "cliPush": true, | ||
| "react-native-worklets": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -849,3 +849,3 @@ "cliPush": true, | ||
| "whisper.rn": { | ||
| "version": "1.99.410", | ||
| "version": "1.99.415", | ||
| "hostApp": true, | ||
@@ -852,0 +852,0 @@ "cliPush": true, |
@@ -16,3 +16,6 @@ const fs = require('fs'); | ||
| const DEFAULT_REPO = process.env.YAVER_AGENT_REPO || 'yaver-io/yaver.io'; | ||
| const WINDOWS_REPO = process.env.YAVER_WINDOWS_AGENT_REPO || 'kivanccakmak/yaver-cli'; | ||
| // Windows is released beside every other agent build in the canonical repo. | ||
| // The historical yaver-cli repo stopped at v1.37.0; resolving from it made a | ||
| // current npm wrapper silently install a years-old Windows agent. | ||
| const WINDOWS_REPO = process.env.YAVER_WINDOWS_AGENT_REPO || DEFAULT_REPO; | ||
| const CACHE_ROOT = process.env.YAVER_AGENT_CACHE_DIR || path.join(os.homedir(), '.yaver', 'bin'); | ||
@@ -19,0 +22,0 @@ let resolvedAgentVersionPromise = null; |
+12
-0
@@ -11,2 +11,3 @@ const PACKAGE = require('../package.json'); | ||
| const { feedback } = require('./commands/feedback'); | ||
| const { desktop } = require('./commands/desktop'); | ||
| const { deploy, isLocalDeployToken } = require('./commands/deploy'); | ||
@@ -55,2 +56,8 @@ const { run, isLocalRunToken } = require('./commands/run'); | ||
| Optional desktop GUI (same account, agent, tasks, and remote-node state): | ||
| yaver desktop Open the installed native GUI | ||
| yaver desktop install Download, verify, and install it per-user | ||
| yaver desktop update Update from the latest verified GUI release | ||
| yaver desktop status Show whether and where the GUI is installed | ||
| Push-to-device commands: | ||
@@ -222,2 +229,7 @@ yaver push Bundle + validate + push current RN/Expo project | ||
| if (command === 'desktop' || command === 'gui') { | ||
| await desktop(args.slice(1)); | ||
| return; | ||
| } | ||
| if (command === 'run') { | ||
@@ -224,0 +236,0 @@ // Local-handle `yaver run dev[:target]`. Anything else (future |
+61
-18
@@ -11,6 +11,8 @@ #!/usr/bin/env node | ||
| const { ensureHermesc } = require("./hermesc-runtime"); | ||
| const { execSync } = require("child_process"); | ||
| const { execFileSync, execSync, spawnSync } = require("child_process"); | ||
| const fs = require("fs"); | ||
| const os = require("os"); | ||
| const path = require("path"); | ||
| const { desktop, installedDesktopCandidates } = require("./commands/desktop"); | ||
| const { codingRunnerBootstrapPlan } = require("./runner-bootstrap-policy"); | ||
@@ -49,8 +51,6 @@ const CODING_RUNNER_BOOTSTRAP = [ | ||
| function commandExists(name) { | ||
| try { | ||
| execSync(`command -v ${name}`, { stdio: ["ignore", "pipe", "ignore"] }); | ||
| return true; | ||
| } catch (_) { | ||
| return false; | ||
| } | ||
| const probe = process.platform === "win32" | ||
| ? spawnSync("where.exe", [name], { stdio: "ignore", windowsHide: true }) | ||
| : spawnSync("/bin/sh", ["-c", `command -v ${name}`], { stdio: "ignore" }); | ||
| return !probe.error && probe.status === 0; | ||
| } | ||
@@ -61,5 +61,18 @@ | ||
| if (!prefix) return ""; | ||
| return path.join(prefix, "bin"); | ||
| return process.platform === "win32" ? prefix : path.join(prefix, "bin"); | ||
| } | ||
| function installGlobalNpmPackages(packages) { | ||
| const args = ["install", "-g", "--no-fund", "--no-audit", ...packages]; | ||
| const npmExecPath = String(process.env.npm_execpath || "").trim(); | ||
| if (npmExecPath && /\.[cm]?js$/i.test(npmExecPath)) { | ||
| execFileSync(process.execPath, [npmExecPath, ...args], { stdio: "inherit", windowsHide: true }); | ||
| return; | ||
| } | ||
| execFileSync(process.platform === "win32" ? "npm.cmd" : "npm", args, { | ||
| stdio: "inherit", | ||
| windowsHide: true, | ||
| }); | ||
| } | ||
| function addNpmGlobalBinToProcessPath() { | ||
@@ -75,16 +88,13 @@ const binDir = npmGlobalBinDir(); | ||
| function installMissingCodingRunners() { | ||
| const missing = CODING_RUNNER_BOOTSTRAP.filter((entry) => !commandExists(entry.command)); | ||
| if (missing.length === 0) { | ||
| log("Claude Code, Codex, and OpenCode already exist on PATH."); | ||
| const plan = codingRunnerBootstrapPlan(CODING_RUNNER_BOOTSTRAP, commandExists); | ||
| if (plan.installed.length > 0) { | ||
| const labels = plan.installed.map((entry) => entry.label).join(", "); | ||
| log(`Using existing coding runner${plan.installed.length === 1 ? "" : "s"}: ${labels}. Other runners were not installed.`); | ||
| return; | ||
| } | ||
| const npmCmd = (process.env.npm_execpath || "npm").trim() || "npm"; | ||
| const packages = missing.map((entry) => entry.pkg); | ||
| const labels = missing.map((entry) => entry.label).join(", "); | ||
| const packages = plan.toInstall.map((entry) => entry.pkg); | ||
| const labels = plan.toInstall.map((entry) => entry.label).join(", "); | ||
| try { | ||
| execSync( | ||
| `"${npmCmd}" install -g --no-fund --no-audit ${packages.join(" ")}`, | ||
| { stdio: "inherit" }, | ||
| ); | ||
| installGlobalNpmPackages(packages); | ||
| addNpmGlobalBinToProcessPath(); | ||
@@ -118,2 +128,20 @@ log(`Installed missing coding runners: ${labels}.`); | ||
| function desktopBootstrapEligible() { | ||
| if (envEnabled("YAVER_SKIP_POSTINSTALL_DESKTOP")) return false; | ||
| if (envEnabled("CI") && !envEnabled("YAVER_FORCE_POSTINSTALL_DESKTOP")) return false; | ||
| if (installedDesktopCandidates().some((candidate) => fs.existsSync(candidate))) return false; | ||
| if (process.platform === "linux") return Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY); | ||
| return process.platform === "darwin" || process.platform === "win32"; | ||
| } | ||
| async function installDesktopCompanion() { | ||
| if (!desktopBootstrapEligible()) return; | ||
| try { | ||
| await desktop(["install", "--no-open"]); | ||
| log("Installed the verified Yaver Desktop companion (set YAVER_SKIP_POSTINSTALL_DESKTOP=1 to opt out)."); | ||
| } catch (error) { | ||
| log(`Skipping desktop companion bootstrap: ${error.message}`); | ||
| } | ||
| } | ||
| // ensureLinuxHermescBuildDeps installs the apt packages required to | ||
@@ -522,2 +550,14 @@ // compile hermesc from facebook/hermes sources on linux/arm64 (no | ||
| if (process.platform === "win32") { | ||
| // Windows is a first-class Yaver node. The mobile/remote-runtime bootstrap | ||
| // below still has Unix-only recipes, but the three supported coding | ||
| // runners are ordinary npm globals and must not disappear merely because | ||
| // the install came from PowerShell. | ||
| if (!envEnabled("YAVER_SKIP_POSTINSTALL_RUNNERS")) { | ||
| installMissingCodingRunners(); | ||
| await setupMCPForInstalledRunners(); | ||
| } | ||
| await installDesktopCompanion(); | ||
| return; | ||
| } | ||
| if (process.platform !== "linux" && process.platform !== "darwin") { | ||
@@ -531,2 +571,3 @@ return; | ||
| } | ||
| await installDesktopCompanion(); | ||
| return; | ||
@@ -560,2 +601,4 @@ } | ||
| await installDesktopCompanion(); | ||
| // Vibe Preview tool stack — best-effort provisioning so a fresh | ||
@@ -562,0 +605,0 @@ // global npm install gives the user a working chromium-based frame |
Network access
Supply chain riskThis module accesses the network.
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 4 instances
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 3 instances
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 4 instances
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
12822564
0.16%32
6.67%5331
8.44%0
-100%69
11.29%24
9.09%