@runinfra/cli
Advanced tools
+21
-1
| export const COMMANDS = ["login", "logout", "whoami", "doctor", "pull"]; | ||
| export const MIN_CONCURRENCY = 1; | ||
| export const MAX_CONCURRENCY = 16; | ||
| const BOOLEAN_FLAGS = new Set([ | ||
| "--device", | ||
| "--local", | ||
| "--weights", | ||
| "--json", | ||
| "--offline", | ||
| "--help", | ||
| "-h", | ||
| "--version", | ||
| "-V", | ||
| ]); | ||
| function emptyArgs(command) { | ||
@@ -28,2 +39,11 @@ return { | ||
| const tokens = [...argv]; | ||
| for (const token of tokens) { | ||
| const { name, value } = splitInlineValue(token); | ||
| if (value !== null && BOOLEAN_FLAGS.has(name)) { | ||
| return { | ||
| ok: false, | ||
| message: `Option ${name} does not take a value. Drop the \`=value\` suffix and use \`${name}\`.`, | ||
| }; | ||
| } | ||
| } | ||
| if (tokens.some((token) => token === "--help" || token === "-h")) { | ||
@@ -154,3 +174,3 @@ return { ok: true, args: emptyArgs("help") }; | ||
| ok: false, | ||
| message: `Unknown option ${name}. Run \`runinfra ${command} --help\`.`, | ||
| message: `Unknown option ${name}. Run \`runinfra --help\`.`, | ||
| }; | ||
@@ -157,0 +177,0 @@ } |
+8
-3
@@ -29,2 +29,6 @@ import { API_BASE_ENV } from "./endpoints.js"; | ||
| "", | ||
| "GLOBAL OPTIONS", | ||
| " -h, --help Show this help.", | ||
| " -V, --version Show the CLI version.", | ||
| "", | ||
| "LOGOUT OPTIONS", | ||
@@ -40,3 +44,3 @@ " --local Delete the stored key without calling the server.", | ||
| " credential check. No request is made.", | ||
| " --out DIR Diagnose the directory a later pull will use.", | ||
| " -o, --out DIR Diagnose the directory a later pull will use.", | ||
| " Defaults to the current directory.", | ||
@@ -51,5 +55,5 @@ "", | ||
| "PULL OPTIONS", | ||
| " --out DIR Where to write the file. Defaults to the current", | ||
| " -o, --out DIR Where to write the file. Defaults to the current", | ||
| " directory.", | ||
| " --concurrency N Parallel connections, 1 to 16. Defaults to 8.", | ||
| " -c, --concurrency N Parallel connections, 1 to 16. Defaults to 8.", | ||
| " --weights For a recipe package, fetch its exact pinned Hugging", | ||
@@ -67,2 +71,3 @@ " Face files into the kit's weights directory and verify", | ||
| " 0 success", | ||
| " 1 unexpected internal error", | ||
| " 2 bad usage or unsupported runtime", | ||
@@ -69,0 +74,0 @@ " 3 not signed in, denied, expired, or refused by a gated source", |
+1
-1
@@ -32,3 +32,3 @@ import { createServer } from "node:http"; | ||
| return { ok: false, reason: "missing_code", description: null }; | ||
| return { ok: true, code, state: url.searchParams.get("state") }; | ||
| return { ok: true, code }; | ||
| } | ||
@@ -35,0 +35,0 @@ export function callbackPage(kind, headline, body) { |
+1
-2
| import { parseArgs } from "./args.js"; | ||
| import { createContext } from "./context.js"; | ||
| import { doctor } from "./doctor.js"; | ||
| import { describeError, exitCodeFor, CliError } from "./errors.js"; | ||
| import { describeError, exitCodeFor } from "./errors.js"; | ||
| import { helpText } from "./help.js"; | ||
@@ -107,2 +107,1 @@ import { login } from "./login.js"; | ||
| } | ||
| export { CliError }; |
+1
-11
@@ -1,2 +0,2 @@ | ||
| import { createHash, randomBytes, timingSafeEqual } from "node:crypto"; | ||
| import { createHash, randomBytes } from "node:crypto"; | ||
| export function createPkcePair() { | ||
@@ -9,12 +9,2 @@ const verifier = randomBytes(32).toString("base64url"); | ||
| } | ||
| export function createState() { | ||
| return randomBytes(32).toString("base64url"); | ||
| } | ||
| export function statesMatch(expected, received) { | ||
| const left = Buffer.from(expected, "utf8"); | ||
| const right = Buffer.from(received, "utf8"); | ||
| if (left.length !== right.length) | ||
| return false; | ||
| return timingSafeEqual(left, right); | ||
| } | ||
| export function isWellFormedCodeVerifier(verifier) { | ||
@@ -21,0 +11,0 @@ return /^[A-Za-z0-9._~-]{43,128}$/u.test(verifier); |
+8
-3
@@ -42,3 +42,3 @@ import { open, mkdir, rename, rm, stat, writeFile } from "node:fs/promises"; | ||
| let checksumVerified = false; | ||
| if (lease.checksumSha256) { | ||
| if (lease.checksumSha256 !== null) { | ||
| let lastTickMs = 0; | ||
@@ -58,4 +58,9 @@ const actual = await sha256File(finalPath, (hashed) => { | ||
| } | ||
| checksumVerified = verdict === "match"; | ||
| output.info(`${fileName} is already downloaded, and its checksum matches.`); | ||
| if (verdict === "match") { | ||
| checksumVerified = true; | ||
| output.info(`${fileName} is already downloaded, and its checksum matches.`); | ||
| } | ||
| else { | ||
| output.warn(`${fileName} is already downloaded. UNVERIFIED: the published checksum could not be read.`); | ||
| } | ||
| } | ||
@@ -62,0 +67,0 @@ else { |
+0
-13
@@ -31,15 +31,2 @@ export const DEFAULT_CONCURRENCY = 8; | ||
| } | ||
| export function planChunks(sizeBytes, chunkBytes) { | ||
| if (sizeBytes <= 0) | ||
| return []; | ||
| const size = Math.max(1, Math.trunc(chunkBytes)); | ||
| const chunks = []; | ||
| for (let start = 0; start < sizeBytes; start += size) { | ||
| chunks.push({ | ||
| start, | ||
| endInclusive: Math.min(start + size, sizeBytes) - 1, | ||
| }); | ||
| } | ||
| return chunks; | ||
| } | ||
| export function normalizeRanges(ranges, sizeBytes) { | ||
@@ -46,0 +33,0 @@ if (!Number.isSafeInteger(sizeBytes) || sizeBytes < 0) |
+1
-2
| export const CLI_NAME = "runinfra"; | ||
| export const CLI_PACKAGE = "@runinfra/cli"; | ||
| export const CLI_VERSION = "0.2.4"; | ||
| export const CLI_CLIENT_ID = "runinfra-cli"; | ||
| export const CLI_VERSION = "0.2.5"; | ||
| export const MINIMUM_NODE_MAJOR = 20; | ||
@@ -6,0 +5,0 @@ export function isSupportedNodeVersion(version) { |
@@ -125,11 +125,1 @@ import { createHash } from "node:crypto"; | ||
| } | ||
| export async function weightsTreeSha256FromFiles(root, files, signal) { | ||
| const hash = createHash("sha256"); | ||
| let totalBytes = 0; | ||
| for (const file of files) { | ||
| totalBytes += await updateWithFile(hash, root, file.path, file.sizeBytes, signal); | ||
| } | ||
| if (files.length === 0) | ||
| throw new Error("weights file list is empty"); | ||
| return { digest: hash.digest("hex"), fileCount: files.length, totalBytes }; | ||
| } |
+1
-1
| { | ||
| "name": "@runinfra/cli", | ||
| "version": "0.2.4", | ||
| "version": "0.2.5", | ||
| "description": "RunInfra CLI: browser-approved sign-in and resumable downloads for optimized model packages", | ||
@@ -5,0 +5,0 @@ "license": "SEE LICENSE IN LICENSE", |
+17
-5
@@ -193,2 +193,5 @@ # @runinfra/cli | ||
| Working aliases are `-h, --help`, `-V, --version`, `-o, --out DIR` for | ||
| `doctor` and `pull`, and `-c, --concurrency N` for `pull`. | ||
| --- | ||
@@ -318,6 +321,14 @@ | ||
| One file, `credentials.json`, holding the key, the API base it was minted | ||
| against, the workspace id, the granted scope, and the expiry. No password, no | ||
| refresh token, no cookie. | ||
| The persistent credential file is `credentials.json`. It holds the key, the API | ||
| base it was minted against, the workspace id, the granted scope, and the expiry. | ||
| No password, no refresh token, no cookie. The same directory can also contain: | ||
| - `update-check.json`, the last optional update notice shown. | ||
| - `credentials.json.tmp`, while credentials are written atomically. | ||
| - `update-check.json.<pid>.tmp`, while update state is written atomically. | ||
| - `update-check.json.lock`, while one process decides whether to show a notice. | ||
| The temporary and lock files are normally removed after the operation. A crash | ||
| can leave a stale one behind. | ||
| | Platform | Path | Protection | | ||
@@ -497,4 +508,4 @@ | --- | --- | --- | | ||
| Options: `--out DIR` (defaults to the working directory), `--concurrency N` | ||
| (1 to 16, defaults to 8), and `--weights`. | ||
| Options: `-o, --out DIR` (defaults to the working directory), | ||
| `-c, --concurrency N` (1 to 16, defaults to 8), and `--weights`. | ||
@@ -651,2 +662,3 @@ ### Fetching recipe weights | ||
| | 0 | Success | | | ||
| | 1 | Unexpected internal CLI error | After reporting or fixing the CLI bug | | ||
| | 2 | Bad usage, or an unsupported Node runtime | No | | ||
@@ -653,0 +665,0 @@ | 3 | Not signed in, denied, expired, revoked, or refused by a gated Hugging Face source | After fixing authentication or source access | |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
271906
0.12%839
1.45%5631
-0.09%