@caupulican/pi-adaptative 0.81.19 → 0.81.20
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/CHANGELOG.md +5 -0
- package/dist/core/models/local-runtime.d.ts +1 -0
- package/dist/core/models/local-runtime.d.ts.map +1 -1
- package/dist/core/models/local-runtime.js +21 -11
- package/dist/core/models/local-runtime.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -233,6 +233,7 @@ export declare class TransformersRuntime {
|
|
|
233
233
|
private findPython;
|
|
234
234
|
private torchInstallArgs;
|
|
235
235
|
private runPython;
|
|
236
|
+
private runtimeDependenciesInstalled;
|
|
236
237
|
installManaged(onProgress?: (status: string) => void): Promise<{
|
|
237
238
|
ok: boolean;
|
|
238
239
|
error?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-runtime.d.ts","sourceRoot":"","sources":["../../../src/core/models/local-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createWriteStream, existsSync, mkdirSync, rmSync } from \"node:fs\";\nimport { homedir, arch as osArch, platform as osPlatform } from \"node:os\";\nimport { delimiter, join } from \"node:path\";\nimport type { Readable, Writable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport * as nodeZlib from \"node:zlib\";\nimport { getBundledResourcesDir } from \"../../config.ts\";\nimport { spawnProcess, spawnProcessSync, waitForChildProcess } from \"../../utils/child-process.ts\";\n\n/**\n * `spawnProcess(..., { stdio: [\"pipe\", \"pipe\", \"pipe\"] })` always yields a non-null `stdin` at\n * runtime, but child-process.ts's typed overload only narrows `.stdin` to non-null for the\n * capture-only (`StdioNull, StdioPipe, StdioPipe`) shape, so the generic overload's `Writable |\n * null` leaks through here. This makes the true invariant explicit and fails loudly (instead of\n * silently misbehaving) if it's ever violated by a future stdio change.\n */\nfunction requireStdin(proc: ChildProcess, label: string): Writable {\n\tif (!proc.stdin) throw new Error(`${label}: no stdin pipe`);\n\treturn proc.stdin;\n}\n\n/**\n * Local model runtime manager (local-model-lifecycle-design.md): Ollama first, interface kept\n * runtime-agnostic. Pi SPAWNS the serve process itself with OWNED model storage\n * (`OLLAMA_MODELS=<agentDir>/models/ollama`) so every downloaded weight lives inside pi's tree —\n * per-model disk accounting is trivial and full cleanup is one directory. If a system server is\n * already running, pi uses it instead of double-installing, with the honest tradeoff surfaced:\n * storage then lives in the system daemon's dir and removal control is limited to `ollama rm`.\n *\n * Hard boundaries (design \"Hard boundaries\"): lifecycle actions are USER commands only — this\n * module is never exposed as a model-invokable tool; install is GUIDE MODE (exact manual steps,\n * never `curl | sh`); removal is explicit, disclosed, and never automatic.\n */\n\nexport interface LocalRuntimeStatus {\n\tbinaryPath?: string;\n\tbinarySource?: \"system\" | \"user\" | \"pi-owned\";\n\tserverUp: boolean;\n\tserverUrl: string;\n\t/** True when the responding server is a child process pi spawned (owned storage applies). */\n\tmanagedByPi: boolean;\n\t/** Owned weights directory (only meaningful for pi-managed serves). */\n\townedModelsDir: string;\n}\n\nexport interface InstalledLocalModel {\n\tname: string;\n\tsizeBytes: number;\n}\n\nexport interface OllamaModelInfo {\n\tmodelInfo: Record<string, unknown>;\n}\n\n/** Pinned ollama release for the managed installer below. Bump here when needed — one constant. */\nexport const OLLAMA_PINNED_VERSION = \"0.31.1\";\n\nexport type OllamaAssetKind = \"tar-zst\" | \"tar-gz\" | \"zip\";\n\nexport interface OllamaReleaseAsset {\n\tname: string;\n\tkind: OllamaAssetKind;\n}\n\n/**\n * Maps a platform/arch pair (node's `os.platform()`/`os.arch()`) to the exact ollama release asset\n * name for {@link OLLAMA_PINNED_VERSION} — verified against the real ollama/ollama GitHub release\n * and its own install.sh, not guessed: darwin ships a CLI `.tgz` (the `.app`/`.dmg`/`Ollama-*.zip`\n * assets are the separate GUI-app installer, not what we want), linux ships `.tar.zst` per arch,\n * windows ships `.zip` per arch. Pure and exported so it's independently testable and reusable\n * (e.g. by a future doctor-driven managed install) without touching OllamaRuntime.\n */\nexport function resolveOllamaAsset(plat: string, architecture: string): OllamaReleaseAsset | undefined {\n\tif (plat === \"darwin\") {\n\t\t// Universal binary — one asset for both arm64 and x64.\n\t\treturn { name: \"ollama-darwin.tgz\", kind: \"tar-gz\" };\n\t}\n\tif (plat === \"linux\") {\n\t\tconst archName = architecture === \"arm64\" ? \"arm64\" : \"amd64\";\n\t\treturn { name: `ollama-linux-${archName}.tar.zst`, kind: \"tar-zst\" };\n\t}\n\tif (plat === \"win32\") {\n\t\tconst archName = architecture === \"arm64\" ? \"arm64\" : \"amd64\";\n\t\treturn { name: `ollama-windows-${archName}.zip`, kind: \"zip\" };\n\t}\n\treturn undefined;\n}\n\nexport interface RuntimeCommandResult {\n\tok: boolean;\n\tstdout: string;\n\tstderr: string;\n\tcode?: number | null;\n\terror?: string;\n}\n\nexport type RuntimeCommandRunner = (\n\tcommand: string,\n\targs: string[],\n\toptions?: { env?: NodeJS.ProcessEnv; onOutput?: (chunk: string) => void },\n) => Promise<RuntimeCommandResult>;\n\nexport interface LocalRuntimeDeps {\n\tfetchFn?: typeof fetch;\n\tspawnFn?: (\n\t\tcommand: string,\n\t\targs: string[],\n\t\toptions: { env: NodeJS.ProcessEnv },\n\t) => Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\">;\n\texistsFn?: (path: string) => boolean;\n\tenvPath?: string;\n\thomeDir?: string;\n\tsleepFn?: (ms: number) => Promise<void>;\n\t/** os.platform()/os.arch() equivalents — injectable so asset/runtime resolution is testable per platform. */\n\tplatform?: () => string;\n\tarch?: () => string;\n\t/** Runs a runtime-management command (Python venv/pip/download probe). Injectable so tests never\n\t * install packages or hit the network. */\n\trunCommand?: RuntimeCommandRunner;\n\t/** Override for bundled runtime helper scripts, used by tests and source/binary packaging checks. */\n\ttransformersServerScriptPath?: string;\n\t/** Node's built-in zstd decompress transform, when THIS runtime's node:zlib has it — undefined\n\t * forces the system-zstd fallback. Injectable so tests can force either path deterministically\n\t * instead of depending on whatever Node happens to be running the test. */\n\tcreateZstdDecompress?: () => NodeJS.ReadWriteStream;\n\t/** Whether a named command exists on PATH (e.g. system `zstd`), for the extraction fallback. */\n\thasCommand?: (command: string) => boolean;\n\t/** Runs the extraction step for a downloaded archive. Injectable so installManaged's\n\t * download->extract->verify orchestration is testable without a real tar/zstd/unzip pipeline;\n\t * defaults to the real spawn-based extractor. */\n\textractArchive?: (\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: OllamaAssetKind,\n\t) => Promise<{ ok: boolean; error?: string }>;\n}\n\nconst DEFAULT_BASE_URL = \"http://127.0.0.1:11434\";\nconst HEALTH_TIMEOUT_MS = 2_000;\nconst START_POLL_ATTEMPTS = 20;\nconst START_POLL_INTERVAL_MS = 500;\nconst TRANSFORMERS_PORT_BASE = 18_100;\nconst TRANSFORMERS_PORT_SPAN = 1_000;\nconst TRANSFORMERS_START_POLL_ATTEMPTS = 240;\nconst TRANSFORMERS_RUNTIME_DIR_NAME = \"hf-transformers\";\nconst TRANSFORMERS_VENV_DIR_NAME = \"venv\";\nconst TRANSFORMERS_SERVER_RELATIVE_PATH = join(\"runtimes\", \"hf-transformers-openai-server.py\");\nconst TRANSFORMERS_PINNED_PACKAGES = [\"transformers==5.13.0\", \"huggingface-hub==1.22.0\", \"safetensors==0.8.0\"];\nconst TORCH_PINNED_VERSION = \"2.12.1\";\n\nasync function runCommandDefault(\n\tcommand: string,\n\targs: string[],\n\toptions: { env?: NodeJS.ProcessEnv; onOutput?: (chunk: string) => void } = {},\n): Promise<RuntimeCommandResult> {\n\ttry {\n\t\tconst proc = spawnProcess(command, args, {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\tenv: options.env ? { ...process.env, ...options.env } : process.env,\n\t\t});\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tproc.stdout.setEncoding(\"utf8\");\n\t\tproc.stderr.setEncoding(\"utf8\");\n\t\tproc.stdout.on(\"data\", (chunk: string) => {\n\t\t\tstdout += chunk;\n\t\t\toptions.onOutput?.(chunk);\n\t\t});\n\t\tproc.stderr.on(\"data\", (chunk: string) => {\n\t\t\tstderr += chunk;\n\t\t\toptions.onOutput?.(chunk);\n\t\t});\n\t\tconst code = await waitForChildProcess(proc);\n\t\treturn {\n\t\t\tok: code === 0,\n\t\t\tstdout,\n\t\t\tstderr,\n\t\t\tcode,\n\t\t\t...(code === 0 ? {} : { error: stderr.trim() || stdout.trim() || `exit code ${code ?? \"unknown\"}` }),\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tstdout: \"\",\n\t\t\tstderr: \"\",\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t};\n\t}\n}\n\nfunction sanitizeCommandOutput(value: string | undefined): string {\n\treturn value?.trim().split(\"\\n\").slice(-3).join(\"\\n\") || \"unknown error\";\n}\n\nfunction hashString(value: string): number {\n\tlet hash = 2_166_136_261;\n\tfor (let index = 0; index < value.length; index++) {\n\t\thash ^= value.charCodeAt(index);\n\t\thash = Math.imul(hash, 16_777_619) >>> 0;\n\t}\n\treturn hash;\n}\n\nexport function resolveTransformersBaseUrl(modelId: string): string {\n\tconst port = TRANSFORMERS_PORT_BASE + (hashString(modelId) % TRANSFORMERS_PORT_SPAN);\n\treturn `http://127.0.0.1:${port}`;\n}\n\nexport class OllamaRuntime {\n\tprivate readonly _agentDir: string;\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _fetch: typeof fetch;\n\tprivate readonly _spawn: NonNullable<LocalRuntimeDeps[\"spawnFn\"]>;\n\tprivate readonly _exists: (path: string) => boolean;\n\tprivate readonly _envPath: string;\n\tprivate readonly _homeDir: string;\n\tprivate readonly _sleep: (ms: number) => Promise<void>;\n\tprivate readonly _platform: () => string;\n\tprivate readonly _arch: () => string;\n\tprivate readonly _createZstdDecompress: (() => NodeJS.ReadWriteStream) | undefined;\n\tprivate readonly _hasCommand: (command: string) => boolean;\n\tprivate readonly _extractArchiveOverride: LocalRuntimeDeps[\"extractArchive\"] | undefined;\n\tprivate _child: Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\"> | undefined;\n\n\tconstructor(args: { agentDir: string; baseUrl?: string; deps?: LocalRuntimeDeps }) {\n\t\tthis._agentDir = args.agentDir;\n\t\tthis._baseUrl = (args.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n\t\tthis._fetch = args.deps?.fetchFn ?? fetch;\n\t\tthis._spawn = args.deps?.spawnFn ?? ((command, argv, options) => spawn(command, argv, options));\n\t\tthis._exists = args.deps?.existsFn ?? existsSync;\n\t\tthis._envPath = args.deps?.envPath ?? process.env.PATH ?? \"\";\n\t\tthis._homeDir = args.deps?.homeDir ?? homedir();\n\t\tthis._sleep = args.deps?.sleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n\t\tthis._platform = args.deps?.platform ?? osPlatform;\n\t\tthis._arch = args.deps?.arch ?? osArch;\n\t\t// Feature-detect (not version-sniff): recent Node added zstd natively to node:zlib. Verified\n\t\t// present on this repo's minimum supported Node (>=22.19.0); still detected at runtime rather\n\t\t// than assumed, so an older/different Node correctly falls through to the system-zstd path.\n\t\t// `\"createZstdDecompress\" in deps` (rather than `??`) so a test can force the fallback path by\n\t\t// passing the key as explicitly undefined, distinct from omitting it (real auto-detection).\n\t\tthis._createZstdDecompress =\n\t\t\targs.deps && \"createZstdDecompress\" in args.deps\n\t\t\t\t? args.deps.createZstdDecompress\n\t\t\t\t: typeof nodeZlib.createZstdDecompress === \"function\"\n\t\t\t\t\t? () => nodeZlib.createZstdDecompress()\n\t\t\t\t\t: undefined;\n\t\tthis._hasCommand =\n\t\t\targs.deps?.hasCommand ??\n\t\t\t((command) => spawnProcessSync(command, [\"--version\"], { encoding: \"utf8\" }).error === undefined);\n\t\tthis._extractArchiveOverride = args.deps?.extractArchive;\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this._baseUrl;\n\t}\n\n\townedModelsDir(): string {\n\t\treturn join(this._agentDir, \"models\", \"ollama\");\n\t}\n\n\tprivate _findBinary(): { path: string; source: \"system\" | \"user\" | \"pi-owned\" } | undefined {\n\t\tconst piOwned = join(this._agentDir, \"runtimes\", \"ollama\", \"bin\", \"ollama\");\n\t\tif (this._exists(piOwned)) return { path: piOwned, source: \"pi-owned\" };\n\t\tconst userLevel = join(this._homeDir, \".local\", \"share\", \"ollama-dist\", \"bin\", \"ollama\");\n\t\tif (this._exists(userLevel)) return { path: userLevel, source: \"user\" };\n\t\tfor (const dir of this._envPath.split(delimiter)) {\n\t\t\tif (!dir) continue;\n\t\t\tconst candidate = join(dir, \"ollama\");\n\t\t\tif (this._exists(candidate)) return { path: candidate, source: \"system\" };\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate async _serverUp(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/tags`, {\n\t\t\t\tsignal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n\t\t\t});\n\t\t\treturn response.ok;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync detect(): Promise<LocalRuntimeStatus> {\n\t\tconst binary = this._findBinary();\n\t\tconst serverUp = await this._serverUp();\n\t\treturn {\n\t\t\tbinaryPath: binary?.path,\n\t\t\tbinarySource: binary?.source,\n\t\t\tserverUp,\n\t\t\tserverUrl: this._baseUrl,\n\t\t\tmanagedByPi: this._child !== undefined && serverUp,\n\t\t\townedModelsDir: this.ownedModelsDir(),\n\t\t};\n\t}\n\n\t/**\n\t * Manual-steps fallback text — shown when the managed install below wasn't offered (headless),\n\t * was declined, or itself failed. #31 reversed the stance for ollama specifically: pi CAN install\n\t * it for you now (consent-gated, a controlled download into pi's own runtimes dir, never\n\t * `curl | sh`) — these steps are the alternative for when that path isn't taken.\n\t */\n\tinstallGuide(): string[] {\n\t\treturn [\n\t\t\t\"Ollama is not installed. Pi can install it for you now (asks first, never curl|sh) — or follow these manual steps yourself:\",\n\t\t\t\" 1. Download the pinned release archive for your platform:\",\n\t\t\t\" https://github.com/ollama/ollama/releases (asset: ollama-linux-amd64.tar.zst or your platform)\",\n\t\t\t` 2. Extract it to ${join(this._homeDir, \".local\", \"share\", \"ollama-dist\")}`,\n\t\t\t\" 3. Re-run your /models command - pi detects the binary automatically.\",\n\t\t\t\"Alternatively install system-wide from https://ollama.com/download and pi will use the system server.\",\n\t\t];\n\t}\n\n\t/** Which decompressor to use for a `.tar.zst` asset — native (this Node's own zlib) is always\n\t * preferred when available, even if a system `zstd` ALSO exists, since it needs no external\n\t * dependency at all. Verified per-runtime by feature detection (see the constructor), not by\n\t * guessing from a Node version number. */\n\tprivate _chooseZstdStrategy(): { kind: \"native\" } | { kind: \"system\" } | { kind: \"unavailable\" } {\n\t\tif (this._createZstdDecompress) return { kind: \"native\" };\n\t\tif (this._hasCommand(\"zstd\")) return { kind: \"system\" };\n\t\treturn { kind: \"unavailable\" };\n\t}\n\n\t/**\n\t * Managed install (#31): a consent-gated, controlled download of the pinned ollama release into\n\t * pi's OWN runtimes dir (`<agentDir>/runtimes/ollama/`) — never `curl | sh`, never a second\n\t * server instance, and distinct from {@link start}'s owned MODEL storage (this is the binary\n\t * itself). Callers (the router's consent flow, and later a doctor-driven install) own the\n\t * consent/UI; this method only does the mechanical download+extract+verify and reports the\n\t * outcome honestly. `onProgress` is best-effort UI feedback, not a completion signal.\n\t */\n\tasync installManaged(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tconst asset = resolveOllamaAsset(this._platform(), this._arch());\n\t\tif (!asset) return { ok: false, error: \"unsupported-platform\" };\n\n\t\tonProgress?.(`Downloading ${asset.name}…`);\n\t\tconst downloadUrl = `https://github.com/ollama/ollama/releases/download/v${OLLAMA_PINNED_VERSION}/${asset.name}`;\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await this._fetch(downloadUrl);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: `download-fail: ${error instanceof Error ? error.message : String(error)}` };\n\t\t}\n\t\tif (!response.ok || !response.body) {\n\t\t\treturn { ok: false, error: `download-fail: HTTP ${response.status}` };\n\t\t}\n\n\t\tconst destDir = join(this._agentDir, \"runtimes\", \"ollama\");\n\t\tmkdirSync(destDir, { recursive: true });\n\n\t\tonProgress?.(`Extracting ${asset.name}…`);\n\t\tconst extract = this._extractArchiveOverride ?? ((input, dest, kind) => this._extractArchive(input, dest, kind));\n\t\tconst extracted = await extract(response.body as unknown as NodeJS.ReadableStream, destDir, asset.kind);\n\t\tif (!extracted.ok) return extracted;\n\n\t\tconst binaryName = this._platform() === \"win32\" ? \"ollama.exe\" : \"ollama\";\n\t\tconst binaryPath = join(destDir, \"bin\", binaryName);\n\t\tif (!this._exists(binaryPath)) {\n\t\t\treturn { ok: false, error: \"extract-fail: binary not found after extraction\" };\n\t\t}\n\t\tonProgress?.(\"Ollama installed.\");\n\t\treturn { ok: true };\n\t}\n\n\t/** Real extraction: `tar-gz` lets `tar` itself gunzip; `tar-zst` decompresses first (native\n\t * node:zlib preferred, system `zstd` as fallback, an honest error if neither exists — see\n\t * {@link _chooseZstdStrategy}); `zip` needs a seekable file, so it's buffered to disk first. */\n\tprivate async _extractArchive(\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: OllamaAssetKind,\n\t): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tif (kind === \"zip\") {\n\t\t\t\treturn await this._extractZip(input, destDir);\n\t\t\t}\n\t\t\treturn await this._extractTar(input, destDir, kind);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: `extract-fail: ${error instanceof Error ? error.message : String(error)}` };\n\t\t}\n\t}\n\n\tprivate async _extractTar(\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: \"tar-gz\" | \"tar-zst\",\n\t): Promise<{ ok: boolean; error?: string }> {\n\t\tlet decompressed: Readable = input as unknown as Readable;\n\t\tconst tarArgs = [\"-xf\", \"-\", \"-C\", destDir];\n\t\tif (kind === \"tar-zst\") {\n\t\t\tconst strategy = this._chooseZstdStrategy();\n\t\t\tif (strategy.kind === \"unavailable\") {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"zstd-missing: this archive needs zstd to decompress and neither Node's built-in support nor a system `zstd` binary was found. Install zstd (e.g. `apt-get install zstd`, `dnf install zstd`, `pacman -S zstd`) and try again.\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (strategy.kind === \"native\") {\n\t\t\t\tdecompressed = decompressed.pipe(\n\t\t\t\t\tthis._createZstdDecompress?.() as unknown as NodeJS.ReadWriteStream & Readable,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst zstdProc = spawnProcess(\"zstd\", [\"-d\"], { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n\t\t\t\tdecompressed.pipe(requireStdin(zstdProc, \"zstd\"));\n\t\t\t\tdecompressed = zstdProc.stdout as unknown as Readable;\n\t\t\t}\n\t\t} else {\n\t\t\t// tar itself handles gzip via -z — no separate decompression step needed.\n\t\t\ttarArgs[0] = \"-xzf\";\n\t\t}\n\t\tconst tarProc = spawnProcess(\"tar\", tarArgs, { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n\t\tdecompressed.pipe(requireStdin(tarProc, \"tar\"));\n\t\tconst code = await waitForChildProcess(tarProc);\n\t\tif (code !== 0) {\n\t\t\treturn { ok: false, error: `extract-fail: tar exited with code ${code}` };\n\t\t}\n\t\treturn { ok: true };\n\t}\n\n\tprivate async _extractZip(input: NodeJS.ReadableStream, destDir: string): Promise<{ ok: boolean; error?: string }> {\n\t\t// Zip's central directory needs seekable file access — buffer to a temp file first.\n\t\tconst zipPath = join(destDir, \"..\", `ollama-download-${process.pid}-${Date.now()}.zip`);\n\t\tawait pipeline(input as unknown as Readable, createWriteStream(zipPath));\n\t\ttry {\n\t\t\tconst extractCommand = this._platform() === \"win32\" && this._hasCommand(\"tar\") ? \"tar\" : \"unzip\";\n\t\t\tconst args = extractCommand === \"tar\" ? [\"-xf\", zipPath, \"-C\", destDir] : [\"-q\", zipPath, \"-d\", destDir];\n\t\t\tconst proc = spawnProcess(extractCommand, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\tconst code = await waitForChildProcess(proc);\n\t\t\tif (code !== 0) {\n\t\t\t\treturn { ok: false, error: `extract-fail: ${extractCommand} exited with code ${code}` };\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\trmSync(zipPath, { force: true });\n\t\t\t} catch {\n\t\t\t\t// best-effort cleanup\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Shared spawn-then-health-poll for both start modes below; `extraEnv` is the only thing that\n\t * differs between them (owned storage vs reusing the user's own). */\n\tprivate async _spawnAndPoll(\n\t\tbinary: { path: string },\n\t\textraEnv: NodeJS.ProcessEnv,\n\t): Promise<{ started: boolean; reason: string }> {\n\t\tconst host = this._baseUrl.replace(/^https?:\\/\\//, \"\");\n\t\tthis._child = this._spawn(binary.path, [\"serve\"], {\n\t\t\tenv: { ...process.env, OLLAMA_HOST: host, ...extraEnv },\n\t\t});\n\t\tthis._child.unref?.();\n\t\tfor (let attempt = 0; attempt < START_POLL_ATTEMPTS; attempt++) {\n\t\t\tif (await this._serverUp()) return { started: true, reason: \"started\" };\n\t\t\tawait this._sleep(START_POLL_INTERVAL_MS);\n\t\t}\n\t\tthis.stop();\n\t\treturn { started: false, reason: \"health_check_timeout\" };\n\t}\n\n\t/**\n\t * Start a pi-managed serve with OWNED storage and the hardened env verified on this class of\n\t * hardware. No-op (reported) when a server already responds — pi never double-serves. Used by\n\t * `/models add` et al, where isolated per-model-pull storage is the point.\n\t */\n\tasync start(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this._serverUp()) {\n\t\t\treturn { started: false, reason: this._child ? \"already_running_managed\" : \"already_running_system\" };\n\t\t}\n\t\tconst binary = this._findBinary();\n\t\tif (!binary) return { started: false, reason: \"binary_missing\" };\n\t\tmkdirSync(this.ownedModelsDir(), { recursive: true });\n\t\treturn this._spawnAndPoll(binary, {\n\t\t\tOLLAMA_MODELS: this.ownedModelsDir(),\n\t\t\tOLLAMA_FLASH_ATTENTION: \"1\",\n\t\t\tOLLAMA_KV_CACHE_TYPE: \"q8_0\",\n\t\t\tOLLAMA_NUM_PARALLEL: \"1\",\n\t\t\tOLLAMA_KEEP_ALIVE: \"30m\",\n\t\t\tOLLAMA_MAX_LOADED_MODELS: \"3\",\n\t\t});\n\t}\n\n\t/**\n\t * Start a serve that REUSES the user's own existing models directory (no `OLLAMA_MODELS`\n\t * override — falls through to Ollama's own default, `~/.ollama`), for callers that must see the\n\t * user's already-pulled models rather than pi's isolated/owned storage. Same idempotency and\n\t * hardened perf env as {@link start}; the only difference is which storage the server sees.\n\t */\n\tasync startReuseExisting(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this._serverUp()) {\n\t\t\treturn { started: false, reason: this._child ? \"already_running_managed\" : \"already_running_system\" };\n\t\t}\n\t\tconst binary = this._findBinary();\n\t\tif (!binary) return { started: false, reason: \"binary_missing\" };\n\t\treturn this._spawnAndPoll(binary, {\n\t\t\tOLLAMA_FLASH_ATTENTION: \"1\",\n\t\t\tOLLAMA_KV_CACHE_TYPE: \"q8_0\",\n\t\t\tOLLAMA_NUM_PARALLEL: \"1\",\n\t\t\tOLLAMA_KEEP_ALIVE: \"30m\",\n\t\t\tOLLAMA_MAX_LOADED_MODELS: \"3\",\n\t\t});\n\t}\n\n\t/** Resource hygiene only: stops the pi-managed serve process; never deletes anything. */\n\tstop(): { stopped: boolean } {\n\t\tif (!this._child) return { stopped: false };\n\t\ttry {\n\t\t\tthis._child.kill(\"SIGTERM\");\n\t\t} catch {\n\t\t\t// already gone\n\t\t}\n\t\tthis._child = undefined;\n\t\treturn { stopped: true };\n\t}\n\n\tasync list(): Promise<InstalledLocalModel[]> {\n\t\tconst response = await this._fetch(`${this._baseUrl}/api/tags`, { signal: AbortSignal.timeout(10_000) });\n\t\tif (!response.ok) throw new Error(`ollama list failed: HTTP ${response.status}`);\n\t\tconst data = (await response.json()) as { models?: Array<{ name?: string; size?: number }> };\n\t\treturn (data.models ?? [])\n\t\t\t.filter((model): model is { name: string; size?: number } => typeof model.name === \"string\")\n\t\t\t.map((model) => ({ name: model.name, sizeBytes: model.size ?? 0 }));\n\t}\n\n\tasync show(ref: string): Promise<{ ok: true; info: OllamaModelInfo } | { ok: false; error: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/show`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref }),\n\t\t\t\tsignal: AbortSignal.timeout(10_000),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `show failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst data = (await response.json()) as { model_info?: Record<string, unknown> };\n\t\t\treturn { ok: true, info: { modelInfo: data.model_info ?? {} } };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\tasync createFromModelfile(args: {\n\t\tname: string;\n\t\tmodelfile: string;\n\t}): Promise<{ ok: true } | { ok: false; error: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/create`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: args.name, modelfile: args.modelfile, stream: false }),\n\t\t\t\tsignal: AbortSignal.timeout(60_000),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `create failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** Pull a model through the server API (weights land in the SERVER's models dir). */\n\tasync pull(ref: string, onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/pull`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref, stream: true }),\n\t\t\t});\n\t\t\tif (!response.ok || !response.body) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `pull failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst reader = response.body.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\t\t\tlet lastStatus = \"\";\n\t\t\tlet errorMessage: string | undefined;\n\t\t\tconst handleLine = (line: string): void => {\n\t\t\t\tif (!line.trim()) return;\n\t\t\t\ttry {\n\t\t\t\t\tconst event = JSON.parse(line) as { status?: string; error?: string };\n\t\t\t\t\tif (event.error) errorMessage = event.error;\n\t\t\t\t\tif (event.status && event.status !== lastStatus) {\n\t\t\t\t\t\tlastStatus = event.status;\n\t\t\t\t\t\tonProgress?.(event.status);\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// partial line noise\n\t\t\t\t}\n\t\t\t};\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() ?? \"\";\n\t\t\t\tfor (const line of lines) handleLine(line);\n\t\t\t}\n\t\t\t// The stream's FINAL line has no trailing newline — it stays in the buffer and holds\n\t\t\t// the terminal \"success\"/error event; dropping it misreports every completed pull.\n\t\t\thandleLine(buffer);\n\t\t\tif (errorMessage) return { ok: false, error: errorMessage };\n\t\t\treturn lastStatus === \"success\"\n\t\t\t\t? { ok: true }\n\t\t\t\t: { ok: false, error: `pull ended with: ${lastStatus || \"unknown\"}` };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** EXPLICIT user action only — callers must have shown what gets deleted and confirmed. */\n\tasync remove(ref: string): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/delete`, {\n\t\t\t\tmethod: \"DELETE\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref }),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `delete failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n}\n\nexport interface TransformersRuntimeStatus {\n\truntimeInstalled: boolean;\n\tserverUp: boolean;\n\tbaseUrl: string;\n\tmodelId: string;\n\tvenvDir: string;\n\tcacheDir: string;\n\tserverScriptPath: string;\n}\n\nexport class TransformersRuntime {\n\tprivate readonly _agentDir: string;\n\tprivate readonly _modelId: string;\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _fetch: typeof fetch;\n\tprivate readonly _spawn: NonNullable<LocalRuntimeDeps[\"spawnFn\"]>;\n\tprivate readonly _exists: (path: string) => boolean;\n\tprivate readonly _sleep: (ms: number) => Promise<void>;\n\tprivate readonly _platform: () => string;\n\tprivate readonly _runCommand: RuntimeCommandRunner;\n\tprivate readonly _serverScriptPath: string;\n\tprivate _proc?: Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\">;\n\n\tconstructor(args: { agentDir: string; modelId: string; baseUrl?: string; deps?: LocalRuntimeDeps }) {\n\t\tthis._agentDir = args.agentDir;\n\t\tthis._modelId = args.modelId;\n\t\tthis._baseUrl = args.baseUrl?.replace(/\\/$/, \"\") ?? resolveTransformersBaseUrl(args.modelId);\n\t\tthis._fetch = args.deps?.fetchFn ?? fetch;\n\t\tthis._spawn = args.deps?.spawnFn ?? ((command, argv, options) => spawn(command, argv, { env: options.env }));\n\t\tthis._exists = args.deps?.existsFn ?? existsSync;\n\t\tthis._sleep = args.deps?.sleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n\t\tthis._platform = args.deps?.platform ?? osPlatform;\n\t\tthis._runCommand = args.deps?.runCommand ?? runCommandDefault;\n\t\tthis._serverScriptPath =\n\t\t\targs.deps?.transformersServerScriptPath ?? join(getBundledResourcesDir(), TRANSFORMERS_SERVER_RELATIVE_PATH);\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this._baseUrl;\n\t}\n\n\tget modelId(): string {\n\t\treturn this._modelId;\n\t}\n\n\tget runtimeDir(): string {\n\t\treturn join(this._agentDir, \"runtimes\", TRANSFORMERS_RUNTIME_DIR_NAME);\n\t}\n\n\tget venvDir(): string {\n\t\treturn join(this.runtimeDir, TRANSFORMERS_VENV_DIR_NAME);\n\t}\n\n\tget cacheDir(): string {\n\t\treturn join(this._agentDir, \"models\", \"huggingface\");\n\t}\n\n\tget pythonPath(): string {\n\t\treturn this._platform() === \"win32\"\n\t\t\t? join(this.venvDir, \"Scripts\", \"python.exe\")\n\t\t\t: join(this.venvDir, \"bin\", \"python\");\n\t}\n\n\tprivate get serverPort(): string {\n\t\tconst parsed = new URL(this._baseUrl);\n\t\treturn parsed.port || (parsed.protocol === \"https:\" ? \"443\" : \"80\");\n\t}\n\n\tprivate get serverHost(): string {\n\t\treturn new URL(this._baseUrl).hostname;\n\t}\n\n\tprivate runtimeEnv(): NodeJS.ProcessEnv {\n\t\tconst venvBin = this._platform() === \"win32\" ? join(this.venvDir, \"Scripts\") : join(this.venvDir, \"bin\");\n\t\treturn {\n\t\t\t...process.env,\n\t\t\tVIRTUAL_ENV: this.venvDir,\n\t\t\tPATH: `${venvBin}${delimiter}${process.env.PATH ?? \"\"}`,\n\t\t\tPYTHONNOUSERSITE: \"1\",\n\t\t\tHF_HOME: this.cacheDir,\n\t\t\tHUGGINGFACE_HUB_CACHE: join(this.cacheDir, \"hub\"),\n\t\t\tTRANSFORMERS_CACHE: join(this.cacheDir, \"transformers\"),\n\t\t\tHF_HUB_DISABLE_TELEMETRY: \"1\",\n\t\t};\n\t}\n\n\tprivate async serverUp(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/health`, {\n\t\t\t\tsignal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n\t\t\t});\n\t\t\tif (!response.ok) return false;\n\t\t\tconst body = (await response.json().catch(() => undefined)) as { model?: unknown } | undefined;\n\t\t\treturn body?.model === this._modelId;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync detect(): Promise<TransformersRuntimeStatus> {\n\t\treturn {\n\t\t\truntimeInstalled: this._exists(this.pythonPath),\n\t\t\tserverUp: await this.serverUp(),\n\t\t\tbaseUrl: this._baseUrl,\n\t\t\tmodelId: this._modelId,\n\t\t\tvenvDir: this.venvDir,\n\t\t\tcacheDir: this.cacheDir,\n\t\t\tserverScriptPath: this._serverScriptPath,\n\t\t};\n\t}\n\n\tprivate pythonCandidates(): Array<{ command: string; args: string[] }> {\n\t\treturn this._platform() === \"win32\"\n\t\t\t? [\n\t\t\t\t\t{ command: \"py\", args: [\"-3\"] },\n\t\t\t\t\t{ command: \"python\", args: [] },\n\t\t\t\t]\n\t\t\t: [\n\t\t\t\t\t{ command: \"python3\", args: [] },\n\t\t\t\t\t{ command: \"python\", args: [] },\n\t\t\t\t];\n\t}\n\n\tprivate async findPython(): Promise<{ command: string; args: string[] } | undefined> {\n\t\tfor (const candidate of this.pythonCandidates()) {\n\t\t\tconst result = await this._runCommand(candidate.command, [...candidate.args, \"--version\"]);\n\t\t\tif (result.ok) return candidate;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate torchInstallArgs(): string[] {\n\t\tif (this._platform() === \"darwin\") {\n\t\t\treturn [\"-m\", \"pip\", \"install\", `torch==${TORCH_PINNED_VERSION}`];\n\t\t}\n\t\treturn [\n\t\t\t\"-m\",\n\t\t\t\"pip\",\n\t\t\t\"install\",\n\t\t\t`torch==${TORCH_PINNED_VERSION}+cpu`,\n\t\t\t\"--index-url\",\n\t\t\t\"https://download.pytorch.org/whl/cpu\",\n\t\t];\n\t}\n\n\tprivate async runPython(args: string[], onProgress?: (status: string) => void): Promise<RuntimeCommandResult> {\n\t\treturn this._runCommand(this.pythonPath, args, {\n\t\t\tenv: this.runtimeEnv(),\n\t\t\tonOutput: onProgress\n\t\t\t\t? (chunk) => {\n\t\t\t\t\t\tconst line = chunk.trim();\n\t\t\t\t\t\tif (line) onProgress(line);\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t});\n\t}\n\n\tasync installManaged(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tmkdirSync(this.runtimeDir, { recursive: true });\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tif (!this._exists(this.pythonPath)) {\n\t\t\tconst python = await this.findPython();\n\t\t\tif (!python) return { ok: false, error: \"python-missing\" };\n\t\t\tonProgress?.(`creating isolated Python venv at ${this.venvDir}`);\n\t\t\tconst venv = await this._runCommand(python.command, [...python.args, \"-m\", \"venv\", this.venvDir]);\n\t\t\tif (!venv.ok) return { ok: false, error: `venv-fail: ${sanitizeCommandOutput(venv.error ?? venv.stderr)}` };\n\t\t}\n\t\tif (!this._exists(this.pythonPath)) {\n\t\t\treturn { ok: false, error: `venv-fail: ${this.pythonPath} was not created` };\n\t\t}\n\n\t\tonProgress?.(\"upgrading pip inside pi-managed venv\");\n\t\tconst pip = await this.runPython([\"-m\", \"pip\", \"install\", \"--upgrade\", \"pip\"], onProgress);\n\t\tif (!pip.ok) return { ok: false, error: `pip-upgrade-fail: ${sanitizeCommandOutput(pip.error ?? pip.stderr)}` };\n\n\t\tonProgress?.(\"installing pinned Transformers runtime packages\");\n\t\tconst transformers = await this.runPython([\"-m\", \"pip\", \"install\", ...TRANSFORMERS_PINNED_PACKAGES], onProgress);\n\t\tif (!transformers.ok) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `transformers-install-fail: ${sanitizeCommandOutput(transformers.error ?? transformers.stderr)}`,\n\t\t\t};\n\t\t}\n\n\t\tonProgress?.(\"installing pinned CPU PyTorch backend\");\n\t\tconst torch = await this.runPython(this.torchInstallArgs(), onProgress);\n\t\tif (!torch.ok)\n\t\t\treturn { ok: false, error: `torch-install-fail: ${sanitizeCommandOutput(torch.error ?? torch.stderr)}` };\n\n\t\tconst verify = await this.runPython([\n\t\t\t\"-c\",\n\t\t\t\"import torch, transformers, huggingface_hub; print(transformers.__version__)\",\n\t\t]);\n\t\tif (!verify.ok)\n\t\t\treturn { ok: false, error: `verify-fail: ${sanitizeCommandOutput(verify.error ?? verify.stderr)}` };\n\t\treturn { ok: true };\n\t}\n\n\tasync downloadModel(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tif (!this._exists(this.pythonPath)) return { ok: false, error: \"runtime-missing\" };\n\t\tif (!this._exists(this._serverScriptPath))\n\t\t\treturn { ok: false, error: `server-script-missing: ${this._serverScriptPath}` };\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tonProgress?.(`downloading ${this._modelId} into ${this.cacheDir}`);\n\t\tconst result = await this.runPython(\n\t\t\t[this._serverScriptPath, \"--model-id\", this._modelId, \"--cache-dir\", this.cacheDir, \"--download-only\"],\n\t\t\tonProgress,\n\t\t);\n\t\treturn result.ok\n\t\t\t? { ok: true }\n\t\t\t: { ok: false, error: `download-fail: ${sanitizeCommandOutput(result.error ?? result.stderr)}` };\n\t}\n\n\tasync start(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this.serverUp()) return { started: false, reason: \"already_running\" };\n\t\tif (!this._exists(this.pythonPath)) return { started: false, reason: \"runtime_missing\" };\n\t\tif (!this._exists(this._serverScriptPath)) return { started: false, reason: \"server_script_missing\" };\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tconst env = this.runtimeEnv();\n\t\tthis._proc = this._spawn(\n\t\t\tthis.pythonPath,\n\t\t\t[\n\t\t\t\tthis._serverScriptPath,\n\t\t\t\t\"--model-id\",\n\t\t\t\tthis._modelId,\n\t\t\t\t\"--host\",\n\t\t\t\tthis.serverHost,\n\t\t\t\t\"--port\",\n\t\t\t\tthis.serverPort,\n\t\t\t\t\"--cache-dir\",\n\t\t\t\tthis.cacheDir,\n\t\t\t],\n\t\t\t{ env },\n\t\t);\n\t\tthis._proc.unref?.();\n\t\tfor (let attempt = 0; attempt < TRANSFORMERS_START_POLL_ATTEMPTS; attempt++) {\n\t\t\tif (await this.serverUp()) return { started: true, reason: \"started\" };\n\t\t\tawait this._sleep(START_POLL_INTERVAL_MS);\n\t\t}\n\t\tthis.stop();\n\t\treturn { started: false, reason: \"health_check_timeout\" };\n\t}\n\n\tstop(): { stopped: boolean } {\n\t\tif (!this._proc) return { stopped: false };\n\t\tthis._proc.kill();\n\t\tthis._proc = undefined;\n\t\treturn { stopped: true };\n\t}\n\n\tinstallGuide(): string[] {\n\t\treturn [\n\t\t\t\"Hugging Face Transformers runtime is not installed for this model.\",\n\t\t\t`Pi can install it into its own venv at ${this.venvDir} and cache weights under ${this.cacheDir}.`,\n\t\t\t\"Manual equivalent:\",\n\t\t\t` python3 -m venv ${this.venvDir}`,\n\t\t\t` ${this.pythonPath} -m pip install --upgrade pip`,\n\t\t\t` ${this.pythonPath} -m pip install ${TRANSFORMERS_PINNED_PACKAGES.join(\" \")}`,\n\t\t\t` ${this.pythonPath} ${this.torchInstallArgs().join(\" \")}`,\n\t\t];\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"local-runtime.d.ts","sourceRoot":"","sources":["../../../src/core/models/local-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createWriteStream, existsSync, mkdirSync, rmSync } from \"node:fs\";\nimport { homedir, arch as osArch, platform as osPlatform } from \"node:os\";\nimport { delimiter, join } from \"node:path\";\nimport type { Readable, Writable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport * as nodeZlib from \"node:zlib\";\nimport { getBundledResourcesDir } from \"../../config.ts\";\nimport { spawnProcess, spawnProcessSync, waitForChildProcess } from \"../../utils/child-process.ts\";\n\n/**\n * `spawnProcess(..., { stdio: [\"pipe\", \"pipe\", \"pipe\"] })` always yields a non-null `stdin` at\n * runtime, but child-process.ts's typed overload only narrows `.stdin` to non-null for the\n * capture-only (`StdioNull, StdioPipe, StdioPipe`) shape, so the generic overload's `Writable |\n * null` leaks through here. This makes the true invariant explicit and fails loudly (instead of\n * silently misbehaving) if it's ever violated by a future stdio change.\n */\nfunction requireStdin(proc: ChildProcess, label: string): Writable {\n\tif (!proc.stdin) throw new Error(`${label}: no stdin pipe`);\n\treturn proc.stdin;\n}\n\n/**\n * Local model runtime manager (local-model-lifecycle-design.md): Ollama first, interface kept\n * runtime-agnostic. Pi SPAWNS the serve process itself with OWNED model storage\n * (`OLLAMA_MODELS=<agentDir>/models/ollama`) so every downloaded weight lives inside pi's tree —\n * per-model disk accounting is trivial and full cleanup is one directory. If a system server is\n * already running, pi uses it instead of double-installing, with the honest tradeoff surfaced:\n * storage then lives in the system daemon's dir and removal control is limited to `ollama rm`.\n *\n * Hard boundaries (design \"Hard boundaries\"): lifecycle actions are USER commands only — this\n * module is never exposed as a model-invokable tool; install is GUIDE MODE (exact manual steps,\n * never `curl | sh`); removal is explicit, disclosed, and never automatic.\n */\n\nexport interface LocalRuntimeStatus {\n\tbinaryPath?: string;\n\tbinarySource?: \"system\" | \"user\" | \"pi-owned\";\n\tserverUp: boolean;\n\tserverUrl: string;\n\t/** True when the responding server is a child process pi spawned (owned storage applies). */\n\tmanagedByPi: boolean;\n\t/** Owned weights directory (only meaningful for pi-managed serves). */\n\townedModelsDir: string;\n}\n\nexport interface InstalledLocalModel {\n\tname: string;\n\tsizeBytes: number;\n}\n\nexport interface OllamaModelInfo {\n\tmodelInfo: Record<string, unknown>;\n}\n\n/** Pinned ollama release for the managed installer below. Bump here when needed — one constant. */\nexport const OLLAMA_PINNED_VERSION = \"0.31.1\";\n\nexport type OllamaAssetKind = \"tar-zst\" | \"tar-gz\" | \"zip\";\n\nexport interface OllamaReleaseAsset {\n\tname: string;\n\tkind: OllamaAssetKind;\n}\n\n/**\n * Maps a platform/arch pair (node's `os.platform()`/`os.arch()`) to the exact ollama release asset\n * name for {@link OLLAMA_PINNED_VERSION} — verified against the real ollama/ollama GitHub release\n * and its own install.sh, not guessed: darwin ships a CLI `.tgz` (the `.app`/`.dmg`/`Ollama-*.zip`\n * assets are the separate GUI-app installer, not what we want), linux ships `.tar.zst` per arch,\n * windows ships `.zip` per arch. Pure and exported so it's independently testable and reusable\n * (e.g. by a future doctor-driven managed install) without touching OllamaRuntime.\n */\nexport function resolveOllamaAsset(plat: string, architecture: string): OllamaReleaseAsset | undefined {\n\tif (plat === \"darwin\") {\n\t\t// Universal binary — one asset for both arm64 and x64.\n\t\treturn { name: \"ollama-darwin.tgz\", kind: \"tar-gz\" };\n\t}\n\tif (plat === \"linux\") {\n\t\tconst archName = architecture === \"arm64\" ? \"arm64\" : \"amd64\";\n\t\treturn { name: `ollama-linux-${archName}.tar.zst`, kind: \"tar-zst\" };\n\t}\n\tif (plat === \"win32\") {\n\t\tconst archName = architecture === \"arm64\" ? \"arm64\" : \"amd64\";\n\t\treturn { name: `ollama-windows-${archName}.zip`, kind: \"zip\" };\n\t}\n\treturn undefined;\n}\n\nexport interface RuntimeCommandResult {\n\tok: boolean;\n\tstdout: string;\n\tstderr: string;\n\tcode?: number | null;\n\terror?: string;\n}\n\nexport type RuntimeCommandRunner = (\n\tcommand: string,\n\targs: string[],\n\toptions?: { env?: NodeJS.ProcessEnv; onOutput?: (chunk: string) => void },\n) => Promise<RuntimeCommandResult>;\n\nexport interface LocalRuntimeDeps {\n\tfetchFn?: typeof fetch;\n\tspawnFn?: (\n\t\tcommand: string,\n\t\targs: string[],\n\t\toptions: { env: NodeJS.ProcessEnv },\n\t) => Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\">;\n\texistsFn?: (path: string) => boolean;\n\tenvPath?: string;\n\thomeDir?: string;\n\tsleepFn?: (ms: number) => Promise<void>;\n\t/** os.platform()/os.arch() equivalents — injectable so asset/runtime resolution is testable per platform. */\n\tplatform?: () => string;\n\tarch?: () => string;\n\t/** Runs a runtime-management command (Python venv/pip/download probe). Injectable so tests never\n\t * install packages or hit the network. */\n\trunCommand?: RuntimeCommandRunner;\n\t/** Override for bundled runtime helper scripts, used by tests and source/binary packaging checks. */\n\ttransformersServerScriptPath?: string;\n\t/** Node's built-in zstd decompress transform, when THIS runtime's node:zlib has it — undefined\n\t * forces the system-zstd fallback. Injectable so tests can force either path deterministically\n\t * instead of depending on whatever Node happens to be running the test. */\n\tcreateZstdDecompress?: () => NodeJS.ReadWriteStream;\n\t/** Whether a named command exists on PATH (e.g. system `zstd`), for the extraction fallback. */\n\thasCommand?: (command: string) => boolean;\n\t/** Runs the extraction step for a downloaded archive. Injectable so installManaged's\n\t * download->extract->verify orchestration is testable without a real tar/zstd/unzip pipeline;\n\t * defaults to the real spawn-based extractor. */\n\textractArchive?: (\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: OllamaAssetKind,\n\t) => Promise<{ ok: boolean; error?: string }>;\n}\n\nconst DEFAULT_BASE_URL = \"http://127.0.0.1:11434\";\nconst HEALTH_TIMEOUT_MS = 2_000;\nconst START_POLL_ATTEMPTS = 20;\nconst START_POLL_INTERVAL_MS = 500;\nconst TRANSFORMERS_PORT_BASE = 18_100;\nconst TRANSFORMERS_PORT_SPAN = 1_000;\nconst TRANSFORMERS_START_POLL_ATTEMPTS = 240;\nconst TRANSFORMERS_RUNTIME_DIR_NAME = \"hf-transformers\";\nconst TRANSFORMERS_VENV_DIR_NAME = \"venv\";\nconst TRANSFORMERS_SERVER_RELATIVE_PATH = join(\"runtimes\", \"hf-transformers-openai-server.py\");\nconst TRANSFORMERS_PINNED_PACKAGES = [\"transformers==5.13.0\", \"huggingface-hub==1.22.0\", \"safetensors==0.8.0\"];\nconst TRANSFORMERS_REQUIRED_MODULES = [\"torch\", \"transformers\", \"huggingface_hub\", \"safetensors\"];\nconst TORCH_PINNED_VERSION = \"2.12.1\";\n\nasync function runCommandDefault(\n\tcommand: string,\n\targs: string[],\n\toptions: { env?: NodeJS.ProcessEnv; onOutput?: (chunk: string) => void } = {},\n): Promise<RuntimeCommandResult> {\n\ttry {\n\t\tconst proc = spawnProcess(command, args, {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\tenv: options.env ? { ...process.env, ...options.env } : process.env,\n\t\t});\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tproc.stdout.setEncoding(\"utf8\");\n\t\tproc.stderr.setEncoding(\"utf8\");\n\t\tproc.stdout.on(\"data\", (chunk: string) => {\n\t\t\tstdout += chunk;\n\t\t\toptions.onOutput?.(chunk);\n\t\t});\n\t\tproc.stderr.on(\"data\", (chunk: string) => {\n\t\t\tstderr += chunk;\n\t\t\toptions.onOutput?.(chunk);\n\t\t});\n\t\tconst code = await waitForChildProcess(proc);\n\t\treturn {\n\t\t\tok: code === 0,\n\t\t\tstdout,\n\t\t\tstderr,\n\t\t\tcode,\n\t\t\t...(code === 0 ? {} : { error: stderr.trim() || stdout.trim() || `exit code ${code ?? \"unknown\"}` }),\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tstdout: \"\",\n\t\t\tstderr: \"\",\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t};\n\t}\n}\n\nfunction sanitizeCommandOutput(value: string | undefined): string {\n\treturn value?.trim().split(\"\\n\").slice(-3).join(\"\\n\") || \"unknown error\";\n}\n\nfunction hashString(value: string): number {\n\tlet hash = 2_166_136_261;\n\tfor (let index = 0; index < value.length; index++) {\n\t\thash ^= value.charCodeAt(index);\n\t\thash = Math.imul(hash, 16_777_619) >>> 0;\n\t}\n\treturn hash;\n}\n\nexport function resolveTransformersBaseUrl(modelId: string): string {\n\tconst port = TRANSFORMERS_PORT_BASE + (hashString(modelId) % TRANSFORMERS_PORT_SPAN);\n\treturn `http://127.0.0.1:${port}`;\n}\n\nexport class OllamaRuntime {\n\tprivate readonly _agentDir: string;\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _fetch: typeof fetch;\n\tprivate readonly _spawn: NonNullable<LocalRuntimeDeps[\"spawnFn\"]>;\n\tprivate readonly _exists: (path: string) => boolean;\n\tprivate readonly _envPath: string;\n\tprivate readonly _homeDir: string;\n\tprivate readonly _sleep: (ms: number) => Promise<void>;\n\tprivate readonly _platform: () => string;\n\tprivate readonly _arch: () => string;\n\tprivate readonly _createZstdDecompress: (() => NodeJS.ReadWriteStream) | undefined;\n\tprivate readonly _hasCommand: (command: string) => boolean;\n\tprivate readonly _extractArchiveOverride: LocalRuntimeDeps[\"extractArchive\"] | undefined;\n\tprivate _child: Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\"> | undefined;\n\n\tconstructor(args: { agentDir: string; baseUrl?: string; deps?: LocalRuntimeDeps }) {\n\t\tthis._agentDir = args.agentDir;\n\t\tthis._baseUrl = (args.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n\t\tthis._fetch = args.deps?.fetchFn ?? fetch;\n\t\tthis._spawn = args.deps?.spawnFn ?? ((command, argv, options) => spawn(command, argv, options));\n\t\tthis._exists = args.deps?.existsFn ?? existsSync;\n\t\tthis._envPath = args.deps?.envPath ?? process.env.PATH ?? \"\";\n\t\tthis._homeDir = args.deps?.homeDir ?? homedir();\n\t\tthis._sleep = args.deps?.sleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n\t\tthis._platform = args.deps?.platform ?? osPlatform;\n\t\tthis._arch = args.deps?.arch ?? osArch;\n\t\t// Feature-detect (not version-sniff): recent Node added zstd natively to node:zlib. Verified\n\t\t// present on this repo's minimum supported Node (>=22.19.0); still detected at runtime rather\n\t\t// than assumed, so an older/different Node correctly falls through to the system-zstd path.\n\t\t// `\"createZstdDecompress\" in deps` (rather than `??`) so a test can force the fallback path by\n\t\t// passing the key as explicitly undefined, distinct from omitting it (real auto-detection).\n\t\tthis._createZstdDecompress =\n\t\t\targs.deps && \"createZstdDecompress\" in args.deps\n\t\t\t\t? args.deps.createZstdDecompress\n\t\t\t\t: typeof nodeZlib.createZstdDecompress === \"function\"\n\t\t\t\t\t? () => nodeZlib.createZstdDecompress()\n\t\t\t\t\t: undefined;\n\t\tthis._hasCommand =\n\t\t\targs.deps?.hasCommand ??\n\t\t\t((command) => spawnProcessSync(command, [\"--version\"], { encoding: \"utf8\" }).error === undefined);\n\t\tthis._extractArchiveOverride = args.deps?.extractArchive;\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this._baseUrl;\n\t}\n\n\townedModelsDir(): string {\n\t\treturn join(this._agentDir, \"models\", \"ollama\");\n\t}\n\n\tprivate _findBinary(): { path: string; source: \"system\" | \"user\" | \"pi-owned\" } | undefined {\n\t\tconst piOwned = join(this._agentDir, \"runtimes\", \"ollama\", \"bin\", \"ollama\");\n\t\tif (this._exists(piOwned)) return { path: piOwned, source: \"pi-owned\" };\n\t\tconst userLevel = join(this._homeDir, \".local\", \"share\", \"ollama-dist\", \"bin\", \"ollama\");\n\t\tif (this._exists(userLevel)) return { path: userLevel, source: \"user\" };\n\t\tfor (const dir of this._envPath.split(delimiter)) {\n\t\t\tif (!dir) continue;\n\t\t\tconst candidate = join(dir, \"ollama\");\n\t\t\tif (this._exists(candidate)) return { path: candidate, source: \"system\" };\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate async _serverUp(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/tags`, {\n\t\t\t\tsignal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n\t\t\t});\n\t\t\treturn response.ok;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync detect(): Promise<LocalRuntimeStatus> {\n\t\tconst binary = this._findBinary();\n\t\tconst serverUp = await this._serverUp();\n\t\treturn {\n\t\t\tbinaryPath: binary?.path,\n\t\t\tbinarySource: binary?.source,\n\t\t\tserverUp,\n\t\t\tserverUrl: this._baseUrl,\n\t\t\tmanagedByPi: this._child !== undefined && serverUp,\n\t\t\townedModelsDir: this.ownedModelsDir(),\n\t\t};\n\t}\n\n\t/**\n\t * Manual-steps fallback text — shown when the managed install below wasn't offered (headless),\n\t * was declined, or itself failed. #31 reversed the stance for ollama specifically: pi CAN install\n\t * it for you now (consent-gated, a controlled download into pi's own runtimes dir, never\n\t * `curl | sh`) — these steps are the alternative for when that path isn't taken.\n\t */\n\tinstallGuide(): string[] {\n\t\treturn [\n\t\t\t\"Ollama is not installed. Pi can install it for you now (asks first, never curl|sh) — or follow these manual steps yourself:\",\n\t\t\t\" 1. Download the pinned release archive for your platform:\",\n\t\t\t\" https://github.com/ollama/ollama/releases (asset: ollama-linux-amd64.tar.zst or your platform)\",\n\t\t\t` 2. Extract it to ${join(this._homeDir, \".local\", \"share\", \"ollama-dist\")}`,\n\t\t\t\" 3. Re-run your /models command - pi detects the binary automatically.\",\n\t\t\t\"Alternatively install system-wide from https://ollama.com/download and pi will use the system server.\",\n\t\t];\n\t}\n\n\t/** Which decompressor to use for a `.tar.zst` asset — native (this Node's own zlib) is always\n\t * preferred when available, even if a system `zstd` ALSO exists, since it needs no external\n\t * dependency at all. Verified per-runtime by feature detection (see the constructor), not by\n\t * guessing from a Node version number. */\n\tprivate _chooseZstdStrategy(): { kind: \"native\" } | { kind: \"system\" } | { kind: \"unavailable\" } {\n\t\tif (this._createZstdDecompress) return { kind: \"native\" };\n\t\tif (this._hasCommand(\"zstd\")) return { kind: \"system\" };\n\t\treturn { kind: \"unavailable\" };\n\t}\n\n\t/**\n\t * Managed install (#31): a consent-gated, controlled download of the pinned ollama release into\n\t * pi's OWN runtimes dir (`<agentDir>/runtimes/ollama/`) — never `curl | sh`, never a second\n\t * server instance, and distinct from {@link start}'s owned MODEL storage (this is the binary\n\t * itself). Callers (the router's consent flow, and later a doctor-driven install) own the\n\t * consent/UI; this method only does the mechanical download+extract+verify and reports the\n\t * outcome honestly. `onProgress` is best-effort UI feedback, not a completion signal.\n\t */\n\tasync installManaged(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tconst asset = resolveOllamaAsset(this._platform(), this._arch());\n\t\tif (!asset) return { ok: false, error: \"unsupported-platform\" };\n\n\t\tonProgress?.(`Downloading ${asset.name}…`);\n\t\tconst downloadUrl = `https://github.com/ollama/ollama/releases/download/v${OLLAMA_PINNED_VERSION}/${asset.name}`;\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await this._fetch(downloadUrl);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: `download-fail: ${error instanceof Error ? error.message : String(error)}` };\n\t\t}\n\t\tif (!response.ok || !response.body) {\n\t\t\treturn { ok: false, error: `download-fail: HTTP ${response.status}` };\n\t\t}\n\n\t\tconst destDir = join(this._agentDir, \"runtimes\", \"ollama\");\n\t\tmkdirSync(destDir, { recursive: true });\n\n\t\tonProgress?.(`Extracting ${asset.name}…`);\n\t\tconst extract = this._extractArchiveOverride ?? ((input, dest, kind) => this._extractArchive(input, dest, kind));\n\t\tconst extracted = await extract(response.body as unknown as NodeJS.ReadableStream, destDir, asset.kind);\n\t\tif (!extracted.ok) return extracted;\n\n\t\tconst binaryName = this._platform() === \"win32\" ? \"ollama.exe\" : \"ollama\";\n\t\tconst binaryPath = join(destDir, \"bin\", binaryName);\n\t\tif (!this._exists(binaryPath)) {\n\t\t\treturn { ok: false, error: \"extract-fail: binary not found after extraction\" };\n\t\t}\n\t\tonProgress?.(\"Ollama installed.\");\n\t\treturn { ok: true };\n\t}\n\n\t/** Real extraction: `tar-gz` lets `tar` itself gunzip; `tar-zst` decompresses first (native\n\t * node:zlib preferred, system `zstd` as fallback, an honest error if neither exists — see\n\t * {@link _chooseZstdStrategy}); `zip` needs a seekable file, so it's buffered to disk first. */\n\tprivate async _extractArchive(\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: OllamaAssetKind,\n\t): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tif (kind === \"zip\") {\n\t\t\t\treturn await this._extractZip(input, destDir);\n\t\t\t}\n\t\t\treturn await this._extractTar(input, destDir, kind);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: `extract-fail: ${error instanceof Error ? error.message : String(error)}` };\n\t\t}\n\t}\n\n\tprivate async _extractTar(\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: \"tar-gz\" | \"tar-zst\",\n\t): Promise<{ ok: boolean; error?: string }> {\n\t\tlet decompressed: Readable = input as unknown as Readable;\n\t\tconst tarArgs = [\"-xf\", \"-\", \"-C\", destDir];\n\t\tif (kind === \"tar-zst\") {\n\t\t\tconst strategy = this._chooseZstdStrategy();\n\t\t\tif (strategy.kind === \"unavailable\") {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"zstd-missing: this archive needs zstd to decompress and neither Node's built-in support nor a system `zstd` binary was found. Install zstd (e.g. `apt-get install zstd`, `dnf install zstd`, `pacman -S zstd`) and try again.\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (strategy.kind === \"native\") {\n\t\t\t\tdecompressed = decompressed.pipe(\n\t\t\t\t\tthis._createZstdDecompress?.() as unknown as NodeJS.ReadWriteStream & Readable,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst zstdProc = spawnProcess(\"zstd\", [\"-d\"], { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n\t\t\t\tdecompressed.pipe(requireStdin(zstdProc, \"zstd\"));\n\t\t\t\tdecompressed = zstdProc.stdout as unknown as Readable;\n\t\t\t}\n\t\t} else {\n\t\t\t// tar itself handles gzip via -z — no separate decompression step needed.\n\t\t\ttarArgs[0] = \"-xzf\";\n\t\t}\n\t\tconst tarProc = spawnProcess(\"tar\", tarArgs, { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n\t\tdecompressed.pipe(requireStdin(tarProc, \"tar\"));\n\t\tconst code = await waitForChildProcess(tarProc);\n\t\tif (code !== 0) {\n\t\t\treturn { ok: false, error: `extract-fail: tar exited with code ${code}` };\n\t\t}\n\t\treturn { ok: true };\n\t}\n\n\tprivate async _extractZip(input: NodeJS.ReadableStream, destDir: string): Promise<{ ok: boolean; error?: string }> {\n\t\t// Zip's central directory needs seekable file access — buffer to a temp file first.\n\t\tconst zipPath = join(destDir, \"..\", `ollama-download-${process.pid}-${Date.now()}.zip`);\n\t\tawait pipeline(input as unknown as Readable, createWriteStream(zipPath));\n\t\ttry {\n\t\t\tconst extractCommand = this._platform() === \"win32\" && this._hasCommand(\"tar\") ? \"tar\" : \"unzip\";\n\t\t\tconst args = extractCommand === \"tar\" ? [\"-xf\", zipPath, \"-C\", destDir] : [\"-q\", zipPath, \"-d\", destDir];\n\t\t\tconst proc = spawnProcess(extractCommand, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\tconst code = await waitForChildProcess(proc);\n\t\t\tif (code !== 0) {\n\t\t\t\treturn { ok: false, error: `extract-fail: ${extractCommand} exited with code ${code}` };\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\trmSync(zipPath, { force: true });\n\t\t\t} catch {\n\t\t\t\t// best-effort cleanup\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Shared spawn-then-health-poll for both start modes below; `extraEnv` is the only thing that\n\t * differs between them (owned storage vs reusing the user's own). */\n\tprivate async _spawnAndPoll(\n\t\tbinary: { path: string },\n\t\textraEnv: NodeJS.ProcessEnv,\n\t): Promise<{ started: boolean; reason: string }> {\n\t\tconst host = this._baseUrl.replace(/^https?:\\/\\//, \"\");\n\t\tthis._child = this._spawn(binary.path, [\"serve\"], {\n\t\t\tenv: { ...process.env, OLLAMA_HOST: host, ...extraEnv },\n\t\t});\n\t\tthis._child.unref?.();\n\t\tfor (let attempt = 0; attempt < START_POLL_ATTEMPTS; attempt++) {\n\t\t\tif (await this._serverUp()) return { started: true, reason: \"started\" };\n\t\t\tawait this._sleep(START_POLL_INTERVAL_MS);\n\t\t}\n\t\tthis.stop();\n\t\treturn { started: false, reason: \"health_check_timeout\" };\n\t}\n\n\t/**\n\t * Start a pi-managed serve with OWNED storage and the hardened env verified on this class of\n\t * hardware. No-op (reported) when a server already responds — pi never double-serves. Used by\n\t * `/models add` et al, where isolated per-model-pull storage is the point.\n\t */\n\tasync start(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this._serverUp()) {\n\t\t\treturn { started: false, reason: this._child ? \"already_running_managed\" : \"already_running_system\" };\n\t\t}\n\t\tconst binary = this._findBinary();\n\t\tif (!binary) return { started: false, reason: \"binary_missing\" };\n\t\tmkdirSync(this.ownedModelsDir(), { recursive: true });\n\t\treturn this._spawnAndPoll(binary, {\n\t\t\tOLLAMA_MODELS: this.ownedModelsDir(),\n\t\t\tOLLAMA_FLASH_ATTENTION: \"1\",\n\t\t\tOLLAMA_KV_CACHE_TYPE: \"q8_0\",\n\t\t\tOLLAMA_NUM_PARALLEL: \"1\",\n\t\t\tOLLAMA_KEEP_ALIVE: \"30m\",\n\t\t\tOLLAMA_MAX_LOADED_MODELS: \"3\",\n\t\t});\n\t}\n\n\t/**\n\t * Start a serve that REUSES the user's own existing models directory (no `OLLAMA_MODELS`\n\t * override — falls through to Ollama's own default, `~/.ollama`), for callers that must see the\n\t * user's already-pulled models rather than pi's isolated/owned storage. Same idempotency and\n\t * hardened perf env as {@link start}; the only difference is which storage the server sees.\n\t */\n\tasync startReuseExisting(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this._serverUp()) {\n\t\t\treturn { started: false, reason: this._child ? \"already_running_managed\" : \"already_running_system\" };\n\t\t}\n\t\tconst binary = this._findBinary();\n\t\tif (!binary) return { started: false, reason: \"binary_missing\" };\n\t\treturn this._spawnAndPoll(binary, {\n\t\t\tOLLAMA_FLASH_ATTENTION: \"1\",\n\t\t\tOLLAMA_KV_CACHE_TYPE: \"q8_0\",\n\t\t\tOLLAMA_NUM_PARALLEL: \"1\",\n\t\t\tOLLAMA_KEEP_ALIVE: \"30m\",\n\t\t\tOLLAMA_MAX_LOADED_MODELS: \"3\",\n\t\t});\n\t}\n\n\t/** Resource hygiene only: stops the pi-managed serve process; never deletes anything. */\n\tstop(): { stopped: boolean } {\n\t\tif (!this._child) return { stopped: false };\n\t\ttry {\n\t\t\tthis._child.kill(\"SIGTERM\");\n\t\t} catch {\n\t\t\t// already gone\n\t\t}\n\t\tthis._child = undefined;\n\t\treturn { stopped: true };\n\t}\n\n\tasync list(): Promise<InstalledLocalModel[]> {\n\t\tconst response = await this._fetch(`${this._baseUrl}/api/tags`, { signal: AbortSignal.timeout(10_000) });\n\t\tif (!response.ok) throw new Error(`ollama list failed: HTTP ${response.status}`);\n\t\tconst data = (await response.json()) as { models?: Array<{ name?: string; size?: number }> };\n\t\treturn (data.models ?? [])\n\t\t\t.filter((model): model is { name: string; size?: number } => typeof model.name === \"string\")\n\t\t\t.map((model) => ({ name: model.name, sizeBytes: model.size ?? 0 }));\n\t}\n\n\tasync show(ref: string): Promise<{ ok: true; info: OllamaModelInfo } | { ok: false; error: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/show`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref }),\n\t\t\t\tsignal: AbortSignal.timeout(10_000),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `show failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst data = (await response.json()) as { model_info?: Record<string, unknown> };\n\t\t\treturn { ok: true, info: { modelInfo: data.model_info ?? {} } };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\tasync createFromModelfile(args: {\n\t\tname: string;\n\t\tmodelfile: string;\n\t}): Promise<{ ok: true } | { ok: false; error: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/create`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: args.name, modelfile: args.modelfile, stream: false }),\n\t\t\t\tsignal: AbortSignal.timeout(60_000),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `create failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** Pull a model through the server API (weights land in the SERVER's models dir). */\n\tasync pull(ref: string, onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/pull`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref, stream: true }),\n\t\t\t});\n\t\t\tif (!response.ok || !response.body) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `pull failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst reader = response.body.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\t\t\tlet lastStatus = \"\";\n\t\t\tlet errorMessage: string | undefined;\n\t\t\tconst handleLine = (line: string): void => {\n\t\t\t\tif (!line.trim()) return;\n\t\t\t\ttry {\n\t\t\t\t\tconst event = JSON.parse(line) as { status?: string; error?: string };\n\t\t\t\t\tif (event.error) errorMessage = event.error;\n\t\t\t\t\tif (event.status && event.status !== lastStatus) {\n\t\t\t\t\t\tlastStatus = event.status;\n\t\t\t\t\t\tonProgress?.(event.status);\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// partial line noise\n\t\t\t\t}\n\t\t\t};\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() ?? \"\";\n\t\t\t\tfor (const line of lines) handleLine(line);\n\t\t\t}\n\t\t\t// The stream's FINAL line has no trailing newline — it stays in the buffer and holds\n\t\t\t// the terminal \"success\"/error event; dropping it misreports every completed pull.\n\t\t\thandleLine(buffer);\n\t\t\tif (errorMessage) return { ok: false, error: errorMessage };\n\t\t\treturn lastStatus === \"success\"\n\t\t\t\t? { ok: true }\n\t\t\t\t: { ok: false, error: `pull ended with: ${lastStatus || \"unknown\"}` };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** EXPLICIT user action only — callers must have shown what gets deleted and confirmed. */\n\tasync remove(ref: string): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/delete`, {\n\t\t\t\tmethod: \"DELETE\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref }),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `delete failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n}\n\nexport interface TransformersRuntimeStatus {\n\truntimeInstalled: boolean;\n\tserverUp: boolean;\n\tbaseUrl: string;\n\tmodelId: string;\n\tvenvDir: string;\n\tcacheDir: string;\n\tserverScriptPath: string;\n}\n\nexport class TransformersRuntime {\n\tprivate readonly _agentDir: string;\n\tprivate readonly _modelId: string;\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _fetch: typeof fetch;\n\tprivate readonly _spawn: NonNullable<LocalRuntimeDeps[\"spawnFn\"]>;\n\tprivate readonly _exists: (path: string) => boolean;\n\tprivate readonly _sleep: (ms: number) => Promise<void>;\n\tprivate readonly _platform: () => string;\n\tprivate readonly _runCommand: RuntimeCommandRunner;\n\tprivate readonly _serverScriptPath: string;\n\tprivate _proc?: Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\">;\n\n\tconstructor(args: { agentDir: string; modelId: string; baseUrl?: string; deps?: LocalRuntimeDeps }) {\n\t\tthis._agentDir = args.agentDir;\n\t\tthis._modelId = args.modelId;\n\t\tthis._baseUrl = args.baseUrl?.replace(/\\/$/, \"\") ?? resolveTransformersBaseUrl(args.modelId);\n\t\tthis._fetch = args.deps?.fetchFn ?? fetch;\n\t\tthis._spawn = args.deps?.spawnFn ?? ((command, argv, options) => spawn(command, argv, { env: options.env }));\n\t\tthis._exists = args.deps?.existsFn ?? existsSync;\n\t\tthis._sleep = args.deps?.sleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n\t\tthis._platform = args.deps?.platform ?? osPlatform;\n\t\tthis._runCommand = args.deps?.runCommand ?? runCommandDefault;\n\t\tthis._serverScriptPath =\n\t\t\targs.deps?.transformersServerScriptPath ?? join(getBundledResourcesDir(), TRANSFORMERS_SERVER_RELATIVE_PATH);\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this._baseUrl;\n\t}\n\n\tget modelId(): string {\n\t\treturn this._modelId;\n\t}\n\n\tget runtimeDir(): string {\n\t\treturn join(this._agentDir, \"runtimes\", TRANSFORMERS_RUNTIME_DIR_NAME);\n\t}\n\n\tget venvDir(): string {\n\t\treturn join(this.runtimeDir, TRANSFORMERS_VENV_DIR_NAME);\n\t}\n\n\tget cacheDir(): string {\n\t\treturn join(this._agentDir, \"models\", \"huggingface\");\n\t}\n\n\tget pythonPath(): string {\n\t\treturn this._platform() === \"win32\"\n\t\t\t? join(this.venvDir, \"Scripts\", \"python.exe\")\n\t\t\t: join(this.venvDir, \"bin\", \"python\");\n\t}\n\n\tprivate get serverPort(): string {\n\t\tconst parsed = new URL(this._baseUrl);\n\t\treturn parsed.port || (parsed.protocol === \"https:\" ? \"443\" : \"80\");\n\t}\n\n\tprivate get serverHost(): string {\n\t\treturn new URL(this._baseUrl).hostname;\n\t}\n\n\tprivate runtimeEnv(): NodeJS.ProcessEnv {\n\t\tconst venvBin = this._platform() === \"win32\" ? join(this.venvDir, \"Scripts\") : join(this.venvDir, \"bin\");\n\t\treturn {\n\t\t\t...process.env,\n\t\t\tVIRTUAL_ENV: this.venvDir,\n\t\t\tPATH: `${venvBin}${delimiter}${process.env.PATH ?? \"\"}`,\n\t\t\tPYTHONNOUSERSITE: \"1\",\n\t\t\tHF_HOME: this.cacheDir,\n\t\t\tHUGGINGFACE_HUB_CACHE: join(this.cacheDir, \"hub\"),\n\t\t\tTRANSFORMERS_CACHE: join(this.cacheDir, \"transformers\"),\n\t\t\tHF_HUB_DISABLE_TELEMETRY: \"1\",\n\t\t};\n\t}\n\n\tprivate async serverUp(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/health`, {\n\t\t\t\tsignal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n\t\t\t});\n\t\t\tif (!response.ok) return false;\n\t\t\tconst body = (await response.json().catch(() => undefined)) as { model?: unknown } | undefined;\n\t\t\treturn body?.model === this._modelId;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync detect(): Promise<TransformersRuntimeStatus> {\n\t\tconst serverUp = await this.serverUp();\n\t\treturn {\n\t\t\truntimeInstalled: this._exists(this.pythonPath) && (serverUp || (await this.runtimeDependenciesInstalled())),\n\t\t\tserverUp,\n\t\t\tbaseUrl: this._baseUrl,\n\t\t\tmodelId: this._modelId,\n\t\t\tvenvDir: this.venvDir,\n\t\t\tcacheDir: this.cacheDir,\n\t\t\tserverScriptPath: this._serverScriptPath,\n\t\t};\n\t}\n\n\tprivate pythonCandidates(): Array<{ command: string; args: string[] }> {\n\t\tconst envPython = process.env.PYTHON?.trim();\n\t\tconst envCandidates = envPython ? [{ command: envPython, args: [] }] : [];\n\t\treturn this._platform() === \"win32\"\n\t\t\t? [...envCandidates, { command: \"py\", args: [\"-3\"] }, { command: \"python\", args: [] }]\n\t\t\t: [...envCandidates, { command: \"python\", args: [] }, { command: \"python3\", args: [] }];\n\t}\n\n\tprivate async findPython(): Promise<{ command: string; args: string[] } | undefined> {\n\t\tfor (const candidate of this.pythonCandidates()) {\n\t\t\tconst result = await this._runCommand(candidate.command, [...candidate.args, \"--version\"]);\n\t\t\tif (result.ok) return candidate;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate torchInstallArgs(): string[] {\n\t\tif (this._platform() === \"darwin\") {\n\t\t\treturn [\"-m\", \"pip\", \"install\", `torch==${TORCH_PINNED_VERSION}`];\n\t\t}\n\t\treturn [\n\t\t\t\"-m\",\n\t\t\t\"pip\",\n\t\t\t\"install\",\n\t\t\t`torch==${TORCH_PINNED_VERSION}+cpu`,\n\t\t\t\"--index-url\",\n\t\t\t\"https://download.pytorch.org/whl/cpu\",\n\t\t];\n\t}\n\n\tprivate async runPython(args: string[], onProgress?: (status: string) => void): Promise<RuntimeCommandResult> {\n\t\treturn this._runCommand(this.pythonPath, args, {\n\t\t\tenv: this.runtimeEnv(),\n\t\t\tonOutput: onProgress\n\t\t\t\t? (chunk) => {\n\t\t\t\t\t\tconst line = chunk.trim();\n\t\t\t\t\t\tif (line) onProgress(line);\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t});\n\t}\n\n\tprivate async runtimeDependenciesInstalled(): Promise<boolean> {\n\t\tif (!this._exists(this.pythonPath)) return false;\n\t\tconst modules = JSON.stringify(TRANSFORMERS_REQUIRED_MODULES);\n\t\tconst result = await this.runPython([\n\t\t\t\"-c\",\n\t\t\t`import importlib.util, sys; missing = [name for name in ${modules} if importlib.util.find_spec(name) is None]; sys.exit(1 if missing else 0)`,\n\t\t]);\n\t\treturn result.ok;\n\t}\n\n\tasync installManaged(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tmkdirSync(this.runtimeDir, { recursive: true });\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tif (!this._exists(this.pythonPath)) {\n\t\t\tconst python = await this.findPython();\n\t\t\tif (!python) return { ok: false, error: \"python-missing\" };\n\t\t\tonProgress?.(`creating isolated Python venv at ${this.venvDir}`);\n\t\t\tconst venv = await this._runCommand(python.command, [...python.args, \"-m\", \"venv\", this.venvDir]);\n\t\t\tif (!venv.ok) return { ok: false, error: `venv-fail: ${sanitizeCommandOutput(venv.error ?? venv.stderr)}` };\n\t\t}\n\t\tif (!this._exists(this.pythonPath)) {\n\t\t\treturn { ok: false, error: `venv-fail: ${this.pythonPath} was not created` };\n\t\t}\n\n\t\tonProgress?.(\"upgrading pip inside pi-managed venv\");\n\t\tconst pip = await this.runPython([\"-m\", \"pip\", \"install\", \"--upgrade\", \"pip\"], onProgress);\n\t\tif (!pip.ok) return { ok: false, error: `pip-upgrade-fail: ${sanitizeCommandOutput(pip.error ?? pip.stderr)}` };\n\n\t\tonProgress?.(\"installing pinned Transformers runtime packages\");\n\t\tconst transformers = await this.runPython([\"-m\", \"pip\", \"install\", ...TRANSFORMERS_PINNED_PACKAGES], onProgress);\n\t\tif (!transformers.ok) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `transformers-install-fail: ${sanitizeCommandOutput(transformers.error ?? transformers.stderr)}`,\n\t\t\t};\n\t\t}\n\n\t\tonProgress?.(\"installing pinned CPU PyTorch backend\");\n\t\tconst torch = await this.runPython(this.torchInstallArgs(), onProgress);\n\t\tif (!torch.ok)\n\t\t\treturn { ok: false, error: `torch-install-fail: ${sanitizeCommandOutput(torch.error ?? torch.stderr)}` };\n\n\t\tconst verify = await this.runPython([\n\t\t\t\"-c\",\n\t\t\t\"import torch, transformers, huggingface_hub; print(transformers.__version__)\",\n\t\t]);\n\t\tif (!verify.ok)\n\t\t\treturn { ok: false, error: `verify-fail: ${sanitizeCommandOutput(verify.error ?? verify.stderr)}` };\n\t\treturn { ok: true };\n\t}\n\n\tasync downloadModel(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tif (!this._exists(this.pythonPath)) return { ok: false, error: \"runtime-missing\" };\n\t\tif (!(await this.runtimeDependenciesInstalled())) return { ok: false, error: \"runtime-dependencies-missing\" };\n\t\tif (!this._exists(this._serverScriptPath))\n\t\t\treturn { ok: false, error: `server-script-missing: ${this._serverScriptPath}` };\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tonProgress?.(`downloading ${this._modelId} into ${this.cacheDir}`);\n\t\tconst result = await this.runPython(\n\t\t\t[this._serverScriptPath, \"--model-id\", this._modelId, \"--cache-dir\", this.cacheDir, \"--download-only\"],\n\t\t\tonProgress,\n\t\t);\n\t\treturn result.ok\n\t\t\t? { ok: true }\n\t\t\t: { ok: false, error: `download-fail: ${sanitizeCommandOutput(result.error ?? result.stderr)}` };\n\t}\n\n\tasync start(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this.serverUp()) return { started: false, reason: \"already_running\" };\n\t\tif (!this._exists(this.pythonPath)) return { started: false, reason: \"runtime_missing\" };\n\t\tif (!this._exists(this._serverScriptPath)) return { started: false, reason: \"server_script_missing\" };\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tconst env = this.runtimeEnv();\n\t\tthis._proc = this._spawn(\n\t\t\tthis.pythonPath,\n\t\t\t[\n\t\t\t\tthis._serverScriptPath,\n\t\t\t\t\"--model-id\",\n\t\t\t\tthis._modelId,\n\t\t\t\t\"--host\",\n\t\t\t\tthis.serverHost,\n\t\t\t\t\"--port\",\n\t\t\t\tthis.serverPort,\n\t\t\t\t\"--cache-dir\",\n\t\t\t\tthis.cacheDir,\n\t\t\t],\n\t\t\t{ env },\n\t\t);\n\t\tthis._proc.unref?.();\n\t\tfor (let attempt = 0; attempt < TRANSFORMERS_START_POLL_ATTEMPTS; attempt++) {\n\t\t\tif (await this.serverUp()) return { started: true, reason: \"started\" };\n\t\t\tawait this._sleep(START_POLL_INTERVAL_MS);\n\t\t}\n\t\tthis.stop();\n\t\treturn { started: false, reason: \"health_check_timeout\" };\n\t}\n\n\tstop(): { stopped: boolean } {\n\t\tif (!this._proc) return { stopped: false };\n\t\tthis._proc.kill();\n\t\tthis._proc = undefined;\n\t\treturn { stopped: true };\n\t}\n\n\tinstallGuide(): string[] {\n\t\treturn [\n\t\t\t\"Hugging Face Transformers runtime is not installed for this model.\",\n\t\t\t`Pi can install it into its own venv at ${this.venvDir} and cache weights under ${this.cacheDir}.`,\n\t\t\t\"Manual equivalent:\",\n\t\t\t` python -m venv ${this.venvDir}`,\n\t\t\t` ${this.pythonPath} -m pip install --upgrade pip`,\n\t\t\t` ${this.pythonPath} -m pip install ${TRANSFORMERS_PINNED_PACKAGES.join(\" \")}`,\n\t\t\t` ${this.pythonPath} ${this.torchInstallArgs().join(\" \")}`,\n\t\t];\n\t}\n}\n"]}
|
|
@@ -54,6 +54,7 @@ const TRANSFORMERS_RUNTIME_DIR_NAME = "hf-transformers";
|
|
|
54
54
|
const TRANSFORMERS_VENV_DIR_NAME = "venv";
|
|
55
55
|
const TRANSFORMERS_SERVER_RELATIVE_PATH = join("runtimes", "hf-transformers-openai-server.py");
|
|
56
56
|
const TRANSFORMERS_PINNED_PACKAGES = ["transformers==5.13.0", "huggingface-hub==1.22.0", "safetensors==0.8.0"];
|
|
57
|
+
const TRANSFORMERS_REQUIRED_MODULES = ["torch", "transformers", "huggingface_hub", "safetensors"];
|
|
57
58
|
const TORCH_PINNED_VERSION = "2.12.1";
|
|
58
59
|
async function runCommandDefault(command, args, options = {}) {
|
|
59
60
|
try {
|
|
@@ -609,9 +610,10 @@ export class TransformersRuntime {
|
|
|
609
610
|
}
|
|
610
611
|
}
|
|
611
612
|
async detect() {
|
|
613
|
+
const serverUp = await this.serverUp();
|
|
612
614
|
return {
|
|
613
|
-
runtimeInstalled: this._exists(this.pythonPath),
|
|
614
|
-
serverUp
|
|
615
|
+
runtimeInstalled: this._exists(this.pythonPath) && (serverUp || (await this.runtimeDependenciesInstalled())),
|
|
616
|
+
serverUp,
|
|
615
617
|
baseUrl: this._baseUrl,
|
|
616
618
|
modelId: this._modelId,
|
|
617
619
|
venvDir: this.venvDir,
|
|
@@ -620,15 +622,11 @@ export class TransformersRuntime {
|
|
|
620
622
|
};
|
|
621
623
|
}
|
|
622
624
|
pythonCandidates() {
|
|
625
|
+
const envPython = process.env.PYTHON?.trim();
|
|
626
|
+
const envCandidates = envPython ? [{ command: envPython, args: [] }] : [];
|
|
623
627
|
return this._platform() === "win32"
|
|
624
|
-
? [
|
|
625
|
-
|
|
626
|
-
{ command: "python", args: [] },
|
|
627
|
-
]
|
|
628
|
-
: [
|
|
629
|
-
{ command: "python3", args: [] },
|
|
630
|
-
{ command: "python", args: [] },
|
|
631
|
-
];
|
|
628
|
+
? [...envCandidates, { command: "py", args: ["-3"] }, { command: "python", args: [] }]
|
|
629
|
+
: [...envCandidates, { command: "python", args: [] }, { command: "python3", args: [] }];
|
|
632
630
|
}
|
|
633
631
|
async findPython() {
|
|
634
632
|
for (const candidate of this.pythonCandidates()) {
|
|
@@ -663,6 +661,16 @@ export class TransformersRuntime {
|
|
|
663
661
|
: undefined,
|
|
664
662
|
});
|
|
665
663
|
}
|
|
664
|
+
async runtimeDependenciesInstalled() {
|
|
665
|
+
if (!this._exists(this.pythonPath))
|
|
666
|
+
return false;
|
|
667
|
+
const modules = JSON.stringify(TRANSFORMERS_REQUIRED_MODULES);
|
|
668
|
+
const result = await this.runPython([
|
|
669
|
+
"-c",
|
|
670
|
+
`import importlib.util, sys; missing = [name for name in ${modules} if importlib.util.find_spec(name) is None]; sys.exit(1 if missing else 0)`,
|
|
671
|
+
]);
|
|
672
|
+
return result.ok;
|
|
673
|
+
}
|
|
666
674
|
async installManaged(onProgress) {
|
|
667
675
|
mkdirSync(this.runtimeDir, { recursive: true });
|
|
668
676
|
mkdirSync(this.cacheDir, { recursive: true });
|
|
@@ -705,6 +713,8 @@ export class TransformersRuntime {
|
|
|
705
713
|
async downloadModel(onProgress) {
|
|
706
714
|
if (!this._exists(this.pythonPath))
|
|
707
715
|
return { ok: false, error: "runtime-missing" };
|
|
716
|
+
if (!(await this.runtimeDependenciesInstalled()))
|
|
717
|
+
return { ok: false, error: "runtime-dependencies-missing" };
|
|
708
718
|
if (!this._exists(this._serverScriptPath))
|
|
709
719
|
return { ok: false, error: `server-script-missing: ${this._serverScriptPath}` };
|
|
710
720
|
mkdirSync(this.cacheDir, { recursive: true });
|
|
@@ -755,7 +765,7 @@ export class TransformersRuntime {
|
|
|
755
765
|
"Hugging Face Transformers runtime is not installed for this model.",
|
|
756
766
|
`Pi can install it into its own venv at ${this.venvDir} and cache weights under ${this.cacheDir}.`,
|
|
757
767
|
"Manual equivalent:",
|
|
758
|
-
`
|
|
768
|
+
` python -m venv ${this.venvDir}`,
|
|
759
769
|
` ${this.pythonPath} -m pip install --upgrade pip`,
|
|
760
770
|
` ${this.pythonPath} -m pip install ${TRANSFORMERS_PINNED_PACKAGES.join(" ")}`,
|
|
761
771
|
` ${this.pythonPath} ${this.torchInstallArgs().join(" ")}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-runtime.js","sourceRoot":"","sources":["../../../src/core/models/local-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createWriteStream, existsSync, mkdirSync, rmSync } from \"node:fs\";\nimport { homedir, arch as osArch, platform as osPlatform } from \"node:os\";\nimport { delimiter, join } from \"node:path\";\nimport type { Readable, Writable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport * as nodeZlib from \"node:zlib\";\nimport { getBundledResourcesDir } from \"../../config.ts\";\nimport { spawnProcess, spawnProcessSync, waitForChildProcess } from \"../../utils/child-process.ts\";\n\n/**\n * `spawnProcess(..., { stdio: [\"pipe\", \"pipe\", \"pipe\"] })` always yields a non-null `stdin` at\n * runtime, but child-process.ts's typed overload only narrows `.stdin` to non-null for the\n * capture-only (`StdioNull, StdioPipe, StdioPipe`) shape, so the generic overload's `Writable |\n * null` leaks through here. This makes the true invariant explicit and fails loudly (instead of\n * silently misbehaving) if it's ever violated by a future stdio change.\n */\nfunction requireStdin(proc: ChildProcess, label: string): Writable {\n\tif (!proc.stdin) throw new Error(`${label}: no stdin pipe`);\n\treturn proc.stdin;\n}\n\n/**\n * Local model runtime manager (local-model-lifecycle-design.md): Ollama first, interface kept\n * runtime-agnostic. Pi SPAWNS the serve process itself with OWNED model storage\n * (`OLLAMA_MODELS=<agentDir>/models/ollama`) so every downloaded weight lives inside pi's tree —\n * per-model disk accounting is trivial and full cleanup is one directory. If a system server is\n * already running, pi uses it instead of double-installing, with the honest tradeoff surfaced:\n * storage then lives in the system daemon's dir and removal control is limited to `ollama rm`.\n *\n * Hard boundaries (design \"Hard boundaries\"): lifecycle actions are USER commands only — this\n * module is never exposed as a model-invokable tool; install is GUIDE MODE (exact manual steps,\n * never `curl | sh`); removal is explicit, disclosed, and never automatic.\n */\n\nexport interface LocalRuntimeStatus {\n\tbinaryPath?: string;\n\tbinarySource?: \"system\" | \"user\" | \"pi-owned\";\n\tserverUp: boolean;\n\tserverUrl: string;\n\t/** True when the responding server is a child process pi spawned (owned storage applies). */\n\tmanagedByPi: boolean;\n\t/** Owned weights directory (only meaningful for pi-managed serves). */\n\townedModelsDir: string;\n}\n\nexport interface InstalledLocalModel {\n\tname: string;\n\tsizeBytes: number;\n}\n\nexport interface OllamaModelInfo {\n\tmodelInfo: Record<string, unknown>;\n}\n\n/** Pinned ollama release for the managed installer below. Bump here when needed — one constant. */\nexport const OLLAMA_PINNED_VERSION = \"0.31.1\";\n\nexport type OllamaAssetKind = \"tar-zst\" | \"tar-gz\" | \"zip\";\n\nexport interface OllamaReleaseAsset {\n\tname: string;\n\tkind: OllamaAssetKind;\n}\n\n/**\n * Maps a platform/arch pair (node's `os.platform()`/`os.arch()`) to the exact ollama release asset\n * name for {@link OLLAMA_PINNED_VERSION} — verified against the real ollama/ollama GitHub release\n * and its own install.sh, not guessed: darwin ships a CLI `.tgz` (the `.app`/`.dmg`/`Ollama-*.zip`\n * assets are the separate GUI-app installer, not what we want), linux ships `.tar.zst` per arch,\n * windows ships `.zip` per arch. Pure and exported so it's independently testable and reusable\n * (e.g. by a future doctor-driven managed install) without touching OllamaRuntime.\n */\nexport function resolveOllamaAsset(plat: string, architecture: string): OllamaReleaseAsset | undefined {\n\tif (plat === \"darwin\") {\n\t\t// Universal binary — one asset for both arm64 and x64.\n\t\treturn { name: \"ollama-darwin.tgz\", kind: \"tar-gz\" };\n\t}\n\tif (plat === \"linux\") {\n\t\tconst archName = architecture === \"arm64\" ? \"arm64\" : \"amd64\";\n\t\treturn { name: `ollama-linux-${archName}.tar.zst`, kind: \"tar-zst\" };\n\t}\n\tif (plat === \"win32\") {\n\t\tconst archName = architecture === \"arm64\" ? \"arm64\" : \"amd64\";\n\t\treturn { name: `ollama-windows-${archName}.zip`, kind: \"zip\" };\n\t}\n\treturn undefined;\n}\n\nexport interface RuntimeCommandResult {\n\tok: boolean;\n\tstdout: string;\n\tstderr: string;\n\tcode?: number | null;\n\terror?: string;\n}\n\nexport type RuntimeCommandRunner = (\n\tcommand: string,\n\targs: string[],\n\toptions?: { env?: NodeJS.ProcessEnv; onOutput?: (chunk: string) => void },\n) => Promise<RuntimeCommandResult>;\n\nexport interface LocalRuntimeDeps {\n\tfetchFn?: typeof fetch;\n\tspawnFn?: (\n\t\tcommand: string,\n\t\targs: string[],\n\t\toptions: { env: NodeJS.ProcessEnv },\n\t) => Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\">;\n\texistsFn?: (path: string) => boolean;\n\tenvPath?: string;\n\thomeDir?: string;\n\tsleepFn?: (ms: number) => Promise<void>;\n\t/** os.platform()/os.arch() equivalents — injectable so asset/runtime resolution is testable per platform. */\n\tplatform?: () => string;\n\tarch?: () => string;\n\t/** Runs a runtime-management command (Python venv/pip/download probe). Injectable so tests never\n\t * install packages or hit the network. */\n\trunCommand?: RuntimeCommandRunner;\n\t/** Override for bundled runtime helper scripts, used by tests and source/binary packaging checks. */\n\ttransformersServerScriptPath?: string;\n\t/** Node's built-in zstd decompress transform, when THIS runtime's node:zlib has it — undefined\n\t * forces the system-zstd fallback. Injectable so tests can force either path deterministically\n\t * instead of depending on whatever Node happens to be running the test. */\n\tcreateZstdDecompress?: () => NodeJS.ReadWriteStream;\n\t/** Whether a named command exists on PATH (e.g. system `zstd`), for the extraction fallback. */\n\thasCommand?: (command: string) => boolean;\n\t/** Runs the extraction step for a downloaded archive. Injectable so installManaged's\n\t * download->extract->verify orchestration is testable without a real tar/zstd/unzip pipeline;\n\t * defaults to the real spawn-based extractor. */\n\textractArchive?: (\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: OllamaAssetKind,\n\t) => Promise<{ ok: boolean; error?: string }>;\n}\n\nconst DEFAULT_BASE_URL = \"http://127.0.0.1:11434\";\nconst HEALTH_TIMEOUT_MS = 2_000;\nconst START_POLL_ATTEMPTS = 20;\nconst START_POLL_INTERVAL_MS = 500;\nconst TRANSFORMERS_PORT_BASE = 18_100;\nconst TRANSFORMERS_PORT_SPAN = 1_000;\nconst TRANSFORMERS_START_POLL_ATTEMPTS = 240;\nconst TRANSFORMERS_RUNTIME_DIR_NAME = \"hf-transformers\";\nconst TRANSFORMERS_VENV_DIR_NAME = \"venv\";\nconst TRANSFORMERS_SERVER_RELATIVE_PATH = join(\"runtimes\", \"hf-transformers-openai-server.py\");\nconst TRANSFORMERS_PINNED_PACKAGES = [\"transformers==5.13.0\", \"huggingface-hub==1.22.0\", \"safetensors==0.8.0\"];\nconst TORCH_PINNED_VERSION = \"2.12.1\";\n\nasync function runCommandDefault(\n\tcommand: string,\n\targs: string[],\n\toptions: { env?: NodeJS.ProcessEnv; onOutput?: (chunk: string) => void } = {},\n): Promise<RuntimeCommandResult> {\n\ttry {\n\t\tconst proc = spawnProcess(command, args, {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\tenv: options.env ? { ...process.env, ...options.env } : process.env,\n\t\t});\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tproc.stdout.setEncoding(\"utf8\");\n\t\tproc.stderr.setEncoding(\"utf8\");\n\t\tproc.stdout.on(\"data\", (chunk: string) => {\n\t\t\tstdout += chunk;\n\t\t\toptions.onOutput?.(chunk);\n\t\t});\n\t\tproc.stderr.on(\"data\", (chunk: string) => {\n\t\t\tstderr += chunk;\n\t\t\toptions.onOutput?.(chunk);\n\t\t});\n\t\tconst code = await waitForChildProcess(proc);\n\t\treturn {\n\t\t\tok: code === 0,\n\t\t\tstdout,\n\t\t\tstderr,\n\t\t\tcode,\n\t\t\t...(code === 0 ? {} : { error: stderr.trim() || stdout.trim() || `exit code ${code ?? \"unknown\"}` }),\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tstdout: \"\",\n\t\t\tstderr: \"\",\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t};\n\t}\n}\n\nfunction sanitizeCommandOutput(value: string | undefined): string {\n\treturn value?.trim().split(\"\\n\").slice(-3).join(\"\\n\") || \"unknown error\";\n}\n\nfunction hashString(value: string): number {\n\tlet hash = 2_166_136_261;\n\tfor (let index = 0; index < value.length; index++) {\n\t\thash ^= value.charCodeAt(index);\n\t\thash = Math.imul(hash, 16_777_619) >>> 0;\n\t}\n\treturn hash;\n}\n\nexport function resolveTransformersBaseUrl(modelId: string): string {\n\tconst port = TRANSFORMERS_PORT_BASE + (hashString(modelId) % TRANSFORMERS_PORT_SPAN);\n\treturn `http://127.0.0.1:${port}`;\n}\n\nexport class OllamaRuntime {\n\tprivate readonly _agentDir: string;\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _fetch: typeof fetch;\n\tprivate readonly _spawn: NonNullable<LocalRuntimeDeps[\"spawnFn\"]>;\n\tprivate readonly _exists: (path: string) => boolean;\n\tprivate readonly _envPath: string;\n\tprivate readonly _homeDir: string;\n\tprivate readonly _sleep: (ms: number) => Promise<void>;\n\tprivate readonly _platform: () => string;\n\tprivate readonly _arch: () => string;\n\tprivate readonly _createZstdDecompress: (() => NodeJS.ReadWriteStream) | undefined;\n\tprivate readonly _hasCommand: (command: string) => boolean;\n\tprivate readonly _extractArchiveOverride: LocalRuntimeDeps[\"extractArchive\"] | undefined;\n\tprivate _child: Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\"> | undefined;\n\n\tconstructor(args: { agentDir: string; baseUrl?: string; deps?: LocalRuntimeDeps }) {\n\t\tthis._agentDir = args.agentDir;\n\t\tthis._baseUrl = (args.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n\t\tthis._fetch = args.deps?.fetchFn ?? fetch;\n\t\tthis._spawn = args.deps?.spawnFn ?? ((command, argv, options) => spawn(command, argv, options));\n\t\tthis._exists = args.deps?.existsFn ?? existsSync;\n\t\tthis._envPath = args.deps?.envPath ?? process.env.PATH ?? \"\";\n\t\tthis._homeDir = args.deps?.homeDir ?? homedir();\n\t\tthis._sleep = args.deps?.sleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n\t\tthis._platform = args.deps?.platform ?? osPlatform;\n\t\tthis._arch = args.deps?.arch ?? osArch;\n\t\t// Feature-detect (not version-sniff): recent Node added zstd natively to node:zlib. Verified\n\t\t// present on this repo's minimum supported Node (>=22.19.0); still detected at runtime rather\n\t\t// than assumed, so an older/different Node correctly falls through to the system-zstd path.\n\t\t// `\"createZstdDecompress\" in deps` (rather than `??`) so a test can force the fallback path by\n\t\t// passing the key as explicitly undefined, distinct from omitting it (real auto-detection).\n\t\tthis._createZstdDecompress =\n\t\t\targs.deps && \"createZstdDecompress\" in args.deps\n\t\t\t\t? args.deps.createZstdDecompress\n\t\t\t\t: typeof nodeZlib.createZstdDecompress === \"function\"\n\t\t\t\t\t? () => nodeZlib.createZstdDecompress()\n\t\t\t\t\t: undefined;\n\t\tthis._hasCommand =\n\t\t\targs.deps?.hasCommand ??\n\t\t\t((command) => spawnProcessSync(command, [\"--version\"], { encoding: \"utf8\" }).error === undefined);\n\t\tthis._extractArchiveOverride = args.deps?.extractArchive;\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this._baseUrl;\n\t}\n\n\townedModelsDir(): string {\n\t\treturn join(this._agentDir, \"models\", \"ollama\");\n\t}\n\n\tprivate _findBinary(): { path: string; source: \"system\" | \"user\" | \"pi-owned\" } | undefined {\n\t\tconst piOwned = join(this._agentDir, \"runtimes\", \"ollama\", \"bin\", \"ollama\");\n\t\tif (this._exists(piOwned)) return { path: piOwned, source: \"pi-owned\" };\n\t\tconst userLevel = join(this._homeDir, \".local\", \"share\", \"ollama-dist\", \"bin\", \"ollama\");\n\t\tif (this._exists(userLevel)) return { path: userLevel, source: \"user\" };\n\t\tfor (const dir of this._envPath.split(delimiter)) {\n\t\t\tif (!dir) continue;\n\t\t\tconst candidate = join(dir, \"ollama\");\n\t\t\tif (this._exists(candidate)) return { path: candidate, source: \"system\" };\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate async _serverUp(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/tags`, {\n\t\t\t\tsignal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n\t\t\t});\n\t\t\treturn response.ok;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync detect(): Promise<LocalRuntimeStatus> {\n\t\tconst binary = this._findBinary();\n\t\tconst serverUp = await this._serverUp();\n\t\treturn {\n\t\t\tbinaryPath: binary?.path,\n\t\t\tbinarySource: binary?.source,\n\t\t\tserverUp,\n\t\t\tserverUrl: this._baseUrl,\n\t\t\tmanagedByPi: this._child !== undefined && serverUp,\n\t\t\townedModelsDir: this.ownedModelsDir(),\n\t\t};\n\t}\n\n\t/**\n\t * Manual-steps fallback text — shown when the managed install below wasn't offered (headless),\n\t * was declined, or itself failed. #31 reversed the stance for ollama specifically: pi CAN install\n\t * it for you now (consent-gated, a controlled download into pi's own runtimes dir, never\n\t * `curl | sh`) — these steps are the alternative for when that path isn't taken.\n\t */\n\tinstallGuide(): string[] {\n\t\treturn [\n\t\t\t\"Ollama is not installed. Pi can install it for you now (asks first, never curl|sh) — or follow these manual steps yourself:\",\n\t\t\t\" 1. Download the pinned release archive for your platform:\",\n\t\t\t\" https://github.com/ollama/ollama/releases (asset: ollama-linux-amd64.tar.zst or your platform)\",\n\t\t\t` 2. Extract it to ${join(this._homeDir, \".local\", \"share\", \"ollama-dist\")}`,\n\t\t\t\" 3. Re-run your /models command - pi detects the binary automatically.\",\n\t\t\t\"Alternatively install system-wide from https://ollama.com/download and pi will use the system server.\",\n\t\t];\n\t}\n\n\t/** Which decompressor to use for a `.tar.zst` asset — native (this Node's own zlib) is always\n\t * preferred when available, even if a system `zstd` ALSO exists, since it needs no external\n\t * dependency at all. Verified per-runtime by feature detection (see the constructor), not by\n\t * guessing from a Node version number. */\n\tprivate _chooseZstdStrategy(): { kind: \"native\" } | { kind: \"system\" } | { kind: \"unavailable\" } {\n\t\tif (this._createZstdDecompress) return { kind: \"native\" };\n\t\tif (this._hasCommand(\"zstd\")) return { kind: \"system\" };\n\t\treturn { kind: \"unavailable\" };\n\t}\n\n\t/**\n\t * Managed install (#31): a consent-gated, controlled download of the pinned ollama release into\n\t * pi's OWN runtimes dir (`<agentDir>/runtimes/ollama/`) — never `curl | sh`, never a second\n\t * server instance, and distinct from {@link start}'s owned MODEL storage (this is the binary\n\t * itself). Callers (the router's consent flow, and later a doctor-driven install) own the\n\t * consent/UI; this method only does the mechanical download+extract+verify and reports the\n\t * outcome honestly. `onProgress` is best-effort UI feedback, not a completion signal.\n\t */\n\tasync installManaged(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tconst asset = resolveOllamaAsset(this._platform(), this._arch());\n\t\tif (!asset) return { ok: false, error: \"unsupported-platform\" };\n\n\t\tonProgress?.(`Downloading ${asset.name}…`);\n\t\tconst downloadUrl = `https://github.com/ollama/ollama/releases/download/v${OLLAMA_PINNED_VERSION}/${asset.name}`;\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await this._fetch(downloadUrl);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: `download-fail: ${error instanceof Error ? error.message : String(error)}` };\n\t\t}\n\t\tif (!response.ok || !response.body) {\n\t\t\treturn { ok: false, error: `download-fail: HTTP ${response.status}` };\n\t\t}\n\n\t\tconst destDir = join(this._agentDir, \"runtimes\", \"ollama\");\n\t\tmkdirSync(destDir, { recursive: true });\n\n\t\tonProgress?.(`Extracting ${asset.name}…`);\n\t\tconst extract = this._extractArchiveOverride ?? ((input, dest, kind) => this._extractArchive(input, dest, kind));\n\t\tconst extracted = await extract(response.body as unknown as NodeJS.ReadableStream, destDir, asset.kind);\n\t\tif (!extracted.ok) return extracted;\n\n\t\tconst binaryName = this._platform() === \"win32\" ? \"ollama.exe\" : \"ollama\";\n\t\tconst binaryPath = join(destDir, \"bin\", binaryName);\n\t\tif (!this._exists(binaryPath)) {\n\t\t\treturn { ok: false, error: \"extract-fail: binary not found after extraction\" };\n\t\t}\n\t\tonProgress?.(\"Ollama installed.\");\n\t\treturn { ok: true };\n\t}\n\n\t/** Real extraction: `tar-gz` lets `tar` itself gunzip; `tar-zst` decompresses first (native\n\t * node:zlib preferred, system `zstd` as fallback, an honest error if neither exists — see\n\t * {@link _chooseZstdStrategy}); `zip` needs a seekable file, so it's buffered to disk first. */\n\tprivate async _extractArchive(\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: OllamaAssetKind,\n\t): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tif (kind === \"zip\") {\n\t\t\t\treturn await this._extractZip(input, destDir);\n\t\t\t}\n\t\t\treturn await this._extractTar(input, destDir, kind);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: `extract-fail: ${error instanceof Error ? error.message : String(error)}` };\n\t\t}\n\t}\n\n\tprivate async _extractTar(\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: \"tar-gz\" | \"tar-zst\",\n\t): Promise<{ ok: boolean; error?: string }> {\n\t\tlet decompressed: Readable = input as unknown as Readable;\n\t\tconst tarArgs = [\"-xf\", \"-\", \"-C\", destDir];\n\t\tif (kind === \"tar-zst\") {\n\t\t\tconst strategy = this._chooseZstdStrategy();\n\t\t\tif (strategy.kind === \"unavailable\") {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"zstd-missing: this archive needs zstd to decompress and neither Node's built-in support nor a system `zstd` binary was found. Install zstd (e.g. `apt-get install zstd`, `dnf install zstd`, `pacman -S zstd`) and try again.\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (strategy.kind === \"native\") {\n\t\t\t\tdecompressed = decompressed.pipe(\n\t\t\t\t\tthis._createZstdDecompress?.() as unknown as NodeJS.ReadWriteStream & Readable,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst zstdProc = spawnProcess(\"zstd\", [\"-d\"], { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n\t\t\t\tdecompressed.pipe(requireStdin(zstdProc, \"zstd\"));\n\t\t\t\tdecompressed = zstdProc.stdout as unknown as Readable;\n\t\t\t}\n\t\t} else {\n\t\t\t// tar itself handles gzip via -z — no separate decompression step needed.\n\t\t\ttarArgs[0] = \"-xzf\";\n\t\t}\n\t\tconst tarProc = spawnProcess(\"tar\", tarArgs, { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n\t\tdecompressed.pipe(requireStdin(tarProc, \"tar\"));\n\t\tconst code = await waitForChildProcess(tarProc);\n\t\tif (code !== 0) {\n\t\t\treturn { ok: false, error: `extract-fail: tar exited with code ${code}` };\n\t\t}\n\t\treturn { ok: true };\n\t}\n\n\tprivate async _extractZip(input: NodeJS.ReadableStream, destDir: string): Promise<{ ok: boolean; error?: string }> {\n\t\t// Zip's central directory needs seekable file access — buffer to a temp file first.\n\t\tconst zipPath = join(destDir, \"..\", `ollama-download-${process.pid}-${Date.now()}.zip`);\n\t\tawait pipeline(input as unknown as Readable, createWriteStream(zipPath));\n\t\ttry {\n\t\t\tconst extractCommand = this._platform() === \"win32\" && this._hasCommand(\"tar\") ? \"tar\" : \"unzip\";\n\t\t\tconst args = extractCommand === \"tar\" ? [\"-xf\", zipPath, \"-C\", destDir] : [\"-q\", zipPath, \"-d\", destDir];\n\t\t\tconst proc = spawnProcess(extractCommand, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\tconst code = await waitForChildProcess(proc);\n\t\t\tif (code !== 0) {\n\t\t\t\treturn { ok: false, error: `extract-fail: ${extractCommand} exited with code ${code}` };\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\trmSync(zipPath, { force: true });\n\t\t\t} catch {\n\t\t\t\t// best-effort cleanup\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Shared spawn-then-health-poll for both start modes below; `extraEnv` is the only thing that\n\t * differs between them (owned storage vs reusing the user's own). */\n\tprivate async _spawnAndPoll(\n\t\tbinary: { path: string },\n\t\textraEnv: NodeJS.ProcessEnv,\n\t): Promise<{ started: boolean; reason: string }> {\n\t\tconst host = this._baseUrl.replace(/^https?:\\/\\//, \"\");\n\t\tthis._child = this._spawn(binary.path, [\"serve\"], {\n\t\t\tenv: { ...process.env, OLLAMA_HOST: host, ...extraEnv },\n\t\t});\n\t\tthis._child.unref?.();\n\t\tfor (let attempt = 0; attempt < START_POLL_ATTEMPTS; attempt++) {\n\t\t\tif (await this._serverUp()) return { started: true, reason: \"started\" };\n\t\t\tawait this._sleep(START_POLL_INTERVAL_MS);\n\t\t}\n\t\tthis.stop();\n\t\treturn { started: false, reason: \"health_check_timeout\" };\n\t}\n\n\t/**\n\t * Start a pi-managed serve with OWNED storage and the hardened env verified on this class of\n\t * hardware. No-op (reported) when a server already responds — pi never double-serves. Used by\n\t * `/models add` et al, where isolated per-model-pull storage is the point.\n\t */\n\tasync start(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this._serverUp()) {\n\t\t\treturn { started: false, reason: this._child ? \"already_running_managed\" : \"already_running_system\" };\n\t\t}\n\t\tconst binary = this._findBinary();\n\t\tif (!binary) return { started: false, reason: \"binary_missing\" };\n\t\tmkdirSync(this.ownedModelsDir(), { recursive: true });\n\t\treturn this._spawnAndPoll(binary, {\n\t\t\tOLLAMA_MODELS: this.ownedModelsDir(),\n\t\t\tOLLAMA_FLASH_ATTENTION: \"1\",\n\t\t\tOLLAMA_KV_CACHE_TYPE: \"q8_0\",\n\t\t\tOLLAMA_NUM_PARALLEL: \"1\",\n\t\t\tOLLAMA_KEEP_ALIVE: \"30m\",\n\t\t\tOLLAMA_MAX_LOADED_MODELS: \"3\",\n\t\t});\n\t}\n\n\t/**\n\t * Start a serve that REUSES the user's own existing models directory (no `OLLAMA_MODELS`\n\t * override — falls through to Ollama's own default, `~/.ollama`), for callers that must see the\n\t * user's already-pulled models rather than pi's isolated/owned storage. Same idempotency and\n\t * hardened perf env as {@link start}; the only difference is which storage the server sees.\n\t */\n\tasync startReuseExisting(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this._serverUp()) {\n\t\t\treturn { started: false, reason: this._child ? \"already_running_managed\" : \"already_running_system\" };\n\t\t}\n\t\tconst binary = this._findBinary();\n\t\tif (!binary) return { started: false, reason: \"binary_missing\" };\n\t\treturn this._spawnAndPoll(binary, {\n\t\t\tOLLAMA_FLASH_ATTENTION: \"1\",\n\t\t\tOLLAMA_KV_CACHE_TYPE: \"q8_0\",\n\t\t\tOLLAMA_NUM_PARALLEL: \"1\",\n\t\t\tOLLAMA_KEEP_ALIVE: \"30m\",\n\t\t\tOLLAMA_MAX_LOADED_MODELS: \"3\",\n\t\t});\n\t}\n\n\t/** Resource hygiene only: stops the pi-managed serve process; never deletes anything. */\n\tstop(): { stopped: boolean } {\n\t\tif (!this._child) return { stopped: false };\n\t\ttry {\n\t\t\tthis._child.kill(\"SIGTERM\");\n\t\t} catch {\n\t\t\t// already gone\n\t\t}\n\t\tthis._child = undefined;\n\t\treturn { stopped: true };\n\t}\n\n\tasync list(): Promise<InstalledLocalModel[]> {\n\t\tconst response = await this._fetch(`${this._baseUrl}/api/tags`, { signal: AbortSignal.timeout(10_000) });\n\t\tif (!response.ok) throw new Error(`ollama list failed: HTTP ${response.status}`);\n\t\tconst data = (await response.json()) as { models?: Array<{ name?: string; size?: number }> };\n\t\treturn (data.models ?? [])\n\t\t\t.filter((model): model is { name: string; size?: number } => typeof model.name === \"string\")\n\t\t\t.map((model) => ({ name: model.name, sizeBytes: model.size ?? 0 }));\n\t}\n\n\tasync show(ref: string): Promise<{ ok: true; info: OllamaModelInfo } | { ok: false; error: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/show`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref }),\n\t\t\t\tsignal: AbortSignal.timeout(10_000),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `show failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst data = (await response.json()) as { model_info?: Record<string, unknown> };\n\t\t\treturn { ok: true, info: { modelInfo: data.model_info ?? {} } };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\tasync createFromModelfile(args: {\n\t\tname: string;\n\t\tmodelfile: string;\n\t}): Promise<{ ok: true } | { ok: false; error: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/create`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: args.name, modelfile: args.modelfile, stream: false }),\n\t\t\t\tsignal: AbortSignal.timeout(60_000),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `create failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** Pull a model through the server API (weights land in the SERVER's models dir). */\n\tasync pull(ref: string, onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/pull`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref, stream: true }),\n\t\t\t});\n\t\t\tif (!response.ok || !response.body) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `pull failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst reader = response.body.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\t\t\tlet lastStatus = \"\";\n\t\t\tlet errorMessage: string | undefined;\n\t\t\tconst handleLine = (line: string): void => {\n\t\t\t\tif (!line.trim()) return;\n\t\t\t\ttry {\n\t\t\t\t\tconst event = JSON.parse(line) as { status?: string; error?: string };\n\t\t\t\t\tif (event.error) errorMessage = event.error;\n\t\t\t\t\tif (event.status && event.status !== lastStatus) {\n\t\t\t\t\t\tlastStatus = event.status;\n\t\t\t\t\t\tonProgress?.(event.status);\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// partial line noise\n\t\t\t\t}\n\t\t\t};\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() ?? \"\";\n\t\t\t\tfor (const line of lines) handleLine(line);\n\t\t\t}\n\t\t\t// The stream's FINAL line has no trailing newline — it stays in the buffer and holds\n\t\t\t// the terminal \"success\"/error event; dropping it misreports every completed pull.\n\t\t\thandleLine(buffer);\n\t\t\tif (errorMessage) return { ok: false, error: errorMessage };\n\t\t\treturn lastStatus === \"success\"\n\t\t\t\t? { ok: true }\n\t\t\t\t: { ok: false, error: `pull ended with: ${lastStatus || \"unknown\"}` };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** EXPLICIT user action only — callers must have shown what gets deleted and confirmed. */\n\tasync remove(ref: string): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/delete`, {\n\t\t\t\tmethod: \"DELETE\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref }),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `delete failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n}\n\nexport interface TransformersRuntimeStatus {\n\truntimeInstalled: boolean;\n\tserverUp: boolean;\n\tbaseUrl: string;\n\tmodelId: string;\n\tvenvDir: string;\n\tcacheDir: string;\n\tserverScriptPath: string;\n}\n\nexport class TransformersRuntime {\n\tprivate readonly _agentDir: string;\n\tprivate readonly _modelId: string;\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _fetch: typeof fetch;\n\tprivate readonly _spawn: NonNullable<LocalRuntimeDeps[\"spawnFn\"]>;\n\tprivate readonly _exists: (path: string) => boolean;\n\tprivate readonly _sleep: (ms: number) => Promise<void>;\n\tprivate readonly _platform: () => string;\n\tprivate readonly _runCommand: RuntimeCommandRunner;\n\tprivate readonly _serverScriptPath: string;\n\tprivate _proc?: Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\">;\n\n\tconstructor(args: { agentDir: string; modelId: string; baseUrl?: string; deps?: LocalRuntimeDeps }) {\n\t\tthis._agentDir = args.agentDir;\n\t\tthis._modelId = args.modelId;\n\t\tthis._baseUrl = args.baseUrl?.replace(/\\/$/, \"\") ?? resolveTransformersBaseUrl(args.modelId);\n\t\tthis._fetch = args.deps?.fetchFn ?? fetch;\n\t\tthis._spawn = args.deps?.spawnFn ?? ((command, argv, options) => spawn(command, argv, { env: options.env }));\n\t\tthis._exists = args.deps?.existsFn ?? existsSync;\n\t\tthis._sleep = args.deps?.sleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n\t\tthis._platform = args.deps?.platform ?? osPlatform;\n\t\tthis._runCommand = args.deps?.runCommand ?? runCommandDefault;\n\t\tthis._serverScriptPath =\n\t\t\targs.deps?.transformersServerScriptPath ?? join(getBundledResourcesDir(), TRANSFORMERS_SERVER_RELATIVE_PATH);\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this._baseUrl;\n\t}\n\n\tget modelId(): string {\n\t\treturn this._modelId;\n\t}\n\n\tget runtimeDir(): string {\n\t\treturn join(this._agentDir, \"runtimes\", TRANSFORMERS_RUNTIME_DIR_NAME);\n\t}\n\n\tget venvDir(): string {\n\t\treturn join(this.runtimeDir, TRANSFORMERS_VENV_DIR_NAME);\n\t}\n\n\tget cacheDir(): string {\n\t\treturn join(this._agentDir, \"models\", \"huggingface\");\n\t}\n\n\tget pythonPath(): string {\n\t\treturn this._platform() === \"win32\"\n\t\t\t? join(this.venvDir, \"Scripts\", \"python.exe\")\n\t\t\t: join(this.venvDir, \"bin\", \"python\");\n\t}\n\n\tprivate get serverPort(): string {\n\t\tconst parsed = new URL(this._baseUrl);\n\t\treturn parsed.port || (parsed.protocol === \"https:\" ? \"443\" : \"80\");\n\t}\n\n\tprivate get serverHost(): string {\n\t\treturn new URL(this._baseUrl).hostname;\n\t}\n\n\tprivate runtimeEnv(): NodeJS.ProcessEnv {\n\t\tconst venvBin = this._platform() === \"win32\" ? join(this.venvDir, \"Scripts\") : join(this.venvDir, \"bin\");\n\t\treturn {\n\t\t\t...process.env,\n\t\t\tVIRTUAL_ENV: this.venvDir,\n\t\t\tPATH: `${venvBin}${delimiter}${process.env.PATH ?? \"\"}`,\n\t\t\tPYTHONNOUSERSITE: \"1\",\n\t\t\tHF_HOME: this.cacheDir,\n\t\t\tHUGGINGFACE_HUB_CACHE: join(this.cacheDir, \"hub\"),\n\t\t\tTRANSFORMERS_CACHE: join(this.cacheDir, \"transformers\"),\n\t\t\tHF_HUB_DISABLE_TELEMETRY: \"1\",\n\t\t};\n\t}\n\n\tprivate async serverUp(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/health`, {\n\t\t\t\tsignal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n\t\t\t});\n\t\t\tif (!response.ok) return false;\n\t\t\tconst body = (await response.json().catch(() => undefined)) as { model?: unknown } | undefined;\n\t\t\treturn body?.model === this._modelId;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync detect(): Promise<TransformersRuntimeStatus> {\n\t\treturn {\n\t\t\truntimeInstalled: this._exists(this.pythonPath),\n\t\t\tserverUp: await this.serverUp(),\n\t\t\tbaseUrl: this._baseUrl,\n\t\t\tmodelId: this._modelId,\n\t\t\tvenvDir: this.venvDir,\n\t\t\tcacheDir: this.cacheDir,\n\t\t\tserverScriptPath: this._serverScriptPath,\n\t\t};\n\t}\n\n\tprivate pythonCandidates(): Array<{ command: string; args: string[] }> {\n\t\treturn this._platform() === \"win32\"\n\t\t\t? [\n\t\t\t\t\t{ command: \"py\", args: [\"-3\"] },\n\t\t\t\t\t{ command: \"python\", args: [] },\n\t\t\t\t]\n\t\t\t: [\n\t\t\t\t\t{ command: \"python3\", args: [] },\n\t\t\t\t\t{ command: \"python\", args: [] },\n\t\t\t\t];\n\t}\n\n\tprivate async findPython(): Promise<{ command: string; args: string[] } | undefined> {\n\t\tfor (const candidate of this.pythonCandidates()) {\n\t\t\tconst result = await this._runCommand(candidate.command, [...candidate.args, \"--version\"]);\n\t\t\tif (result.ok) return candidate;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate torchInstallArgs(): string[] {\n\t\tif (this._platform() === \"darwin\") {\n\t\t\treturn [\"-m\", \"pip\", \"install\", `torch==${TORCH_PINNED_VERSION}`];\n\t\t}\n\t\treturn [\n\t\t\t\"-m\",\n\t\t\t\"pip\",\n\t\t\t\"install\",\n\t\t\t`torch==${TORCH_PINNED_VERSION}+cpu`,\n\t\t\t\"--index-url\",\n\t\t\t\"https://download.pytorch.org/whl/cpu\",\n\t\t];\n\t}\n\n\tprivate async runPython(args: string[], onProgress?: (status: string) => void): Promise<RuntimeCommandResult> {\n\t\treturn this._runCommand(this.pythonPath, args, {\n\t\t\tenv: this.runtimeEnv(),\n\t\t\tonOutput: onProgress\n\t\t\t\t? (chunk) => {\n\t\t\t\t\t\tconst line = chunk.trim();\n\t\t\t\t\t\tif (line) onProgress(line);\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t});\n\t}\n\n\tasync installManaged(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tmkdirSync(this.runtimeDir, { recursive: true });\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tif (!this._exists(this.pythonPath)) {\n\t\t\tconst python = await this.findPython();\n\t\t\tif (!python) return { ok: false, error: \"python-missing\" };\n\t\t\tonProgress?.(`creating isolated Python venv at ${this.venvDir}`);\n\t\t\tconst venv = await this._runCommand(python.command, [...python.args, \"-m\", \"venv\", this.venvDir]);\n\t\t\tif (!venv.ok) return { ok: false, error: `venv-fail: ${sanitizeCommandOutput(venv.error ?? venv.stderr)}` };\n\t\t}\n\t\tif (!this._exists(this.pythonPath)) {\n\t\t\treturn { ok: false, error: `venv-fail: ${this.pythonPath} was not created` };\n\t\t}\n\n\t\tonProgress?.(\"upgrading pip inside pi-managed venv\");\n\t\tconst pip = await this.runPython([\"-m\", \"pip\", \"install\", \"--upgrade\", \"pip\"], onProgress);\n\t\tif (!pip.ok) return { ok: false, error: `pip-upgrade-fail: ${sanitizeCommandOutput(pip.error ?? pip.stderr)}` };\n\n\t\tonProgress?.(\"installing pinned Transformers runtime packages\");\n\t\tconst transformers = await this.runPython([\"-m\", \"pip\", \"install\", ...TRANSFORMERS_PINNED_PACKAGES], onProgress);\n\t\tif (!transformers.ok) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `transformers-install-fail: ${sanitizeCommandOutput(transformers.error ?? transformers.stderr)}`,\n\t\t\t};\n\t\t}\n\n\t\tonProgress?.(\"installing pinned CPU PyTorch backend\");\n\t\tconst torch = await this.runPython(this.torchInstallArgs(), onProgress);\n\t\tif (!torch.ok)\n\t\t\treturn { ok: false, error: `torch-install-fail: ${sanitizeCommandOutput(torch.error ?? torch.stderr)}` };\n\n\t\tconst verify = await this.runPython([\n\t\t\t\"-c\",\n\t\t\t\"import torch, transformers, huggingface_hub; print(transformers.__version__)\",\n\t\t]);\n\t\tif (!verify.ok)\n\t\t\treturn { ok: false, error: `verify-fail: ${sanitizeCommandOutput(verify.error ?? verify.stderr)}` };\n\t\treturn { ok: true };\n\t}\n\n\tasync downloadModel(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tif (!this._exists(this.pythonPath)) return { ok: false, error: \"runtime-missing\" };\n\t\tif (!this._exists(this._serverScriptPath))\n\t\t\treturn { ok: false, error: `server-script-missing: ${this._serverScriptPath}` };\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tonProgress?.(`downloading ${this._modelId} into ${this.cacheDir}`);\n\t\tconst result = await this.runPython(\n\t\t\t[this._serverScriptPath, \"--model-id\", this._modelId, \"--cache-dir\", this.cacheDir, \"--download-only\"],\n\t\t\tonProgress,\n\t\t);\n\t\treturn result.ok\n\t\t\t? { ok: true }\n\t\t\t: { ok: false, error: `download-fail: ${sanitizeCommandOutput(result.error ?? result.stderr)}` };\n\t}\n\n\tasync start(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this.serverUp()) return { started: false, reason: \"already_running\" };\n\t\tif (!this._exists(this.pythonPath)) return { started: false, reason: \"runtime_missing\" };\n\t\tif (!this._exists(this._serverScriptPath)) return { started: false, reason: \"server_script_missing\" };\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tconst env = this.runtimeEnv();\n\t\tthis._proc = this._spawn(\n\t\t\tthis.pythonPath,\n\t\t\t[\n\t\t\t\tthis._serverScriptPath,\n\t\t\t\t\"--model-id\",\n\t\t\t\tthis._modelId,\n\t\t\t\t\"--host\",\n\t\t\t\tthis.serverHost,\n\t\t\t\t\"--port\",\n\t\t\t\tthis.serverPort,\n\t\t\t\t\"--cache-dir\",\n\t\t\t\tthis.cacheDir,\n\t\t\t],\n\t\t\t{ env },\n\t\t);\n\t\tthis._proc.unref?.();\n\t\tfor (let attempt = 0; attempt < TRANSFORMERS_START_POLL_ATTEMPTS; attempt++) {\n\t\t\tif (await this.serverUp()) return { started: true, reason: \"started\" };\n\t\t\tawait this._sleep(START_POLL_INTERVAL_MS);\n\t\t}\n\t\tthis.stop();\n\t\treturn { started: false, reason: \"health_check_timeout\" };\n\t}\n\n\tstop(): { stopped: boolean } {\n\t\tif (!this._proc) return { stopped: false };\n\t\tthis._proc.kill();\n\t\tthis._proc = undefined;\n\t\treturn { stopped: true };\n\t}\n\n\tinstallGuide(): string[] {\n\t\treturn [\n\t\t\t\"Hugging Face Transformers runtime is not installed for this model.\",\n\t\t\t`Pi can install it into its own venv at ${this.venvDir} and cache weights under ${this.cacheDir}.`,\n\t\t\t\"Manual equivalent:\",\n\t\t\t` python3 -m venv ${this.venvDir}`,\n\t\t\t` ${this.pythonPath} -m pip install --upgrade pip`,\n\t\t\t` ${this.pythonPath} -m pip install ${TRANSFORMERS_PINNED_PACKAGES.join(\" \")}`,\n\t\t\t` ${this.pythonPath} ${this.torchInstallArgs().join(\" \")}`,\n\t\t];\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"local-runtime.js","sourceRoot":"","sources":["../../../src/core/models/local-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createWriteStream, existsSync, mkdirSync, rmSync } from \"node:fs\";\nimport { homedir, arch as osArch, platform as osPlatform } from \"node:os\";\nimport { delimiter, join } from \"node:path\";\nimport type { Readable, Writable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport * as nodeZlib from \"node:zlib\";\nimport { getBundledResourcesDir } from \"../../config.ts\";\nimport { spawnProcess, spawnProcessSync, waitForChildProcess } from \"../../utils/child-process.ts\";\n\n/**\n * `spawnProcess(..., { stdio: [\"pipe\", \"pipe\", \"pipe\"] })` always yields a non-null `stdin` at\n * runtime, but child-process.ts's typed overload only narrows `.stdin` to non-null for the\n * capture-only (`StdioNull, StdioPipe, StdioPipe`) shape, so the generic overload's `Writable |\n * null` leaks through here. This makes the true invariant explicit and fails loudly (instead of\n * silently misbehaving) if it's ever violated by a future stdio change.\n */\nfunction requireStdin(proc: ChildProcess, label: string): Writable {\n\tif (!proc.stdin) throw new Error(`${label}: no stdin pipe`);\n\treturn proc.stdin;\n}\n\n/**\n * Local model runtime manager (local-model-lifecycle-design.md): Ollama first, interface kept\n * runtime-agnostic. Pi SPAWNS the serve process itself with OWNED model storage\n * (`OLLAMA_MODELS=<agentDir>/models/ollama`) so every downloaded weight lives inside pi's tree —\n * per-model disk accounting is trivial and full cleanup is one directory. If a system server is\n * already running, pi uses it instead of double-installing, with the honest tradeoff surfaced:\n * storage then lives in the system daemon's dir and removal control is limited to `ollama rm`.\n *\n * Hard boundaries (design \"Hard boundaries\"): lifecycle actions are USER commands only — this\n * module is never exposed as a model-invokable tool; install is GUIDE MODE (exact manual steps,\n * never `curl | sh`); removal is explicit, disclosed, and never automatic.\n */\n\nexport interface LocalRuntimeStatus {\n\tbinaryPath?: string;\n\tbinarySource?: \"system\" | \"user\" | \"pi-owned\";\n\tserverUp: boolean;\n\tserverUrl: string;\n\t/** True when the responding server is a child process pi spawned (owned storage applies). */\n\tmanagedByPi: boolean;\n\t/** Owned weights directory (only meaningful for pi-managed serves). */\n\townedModelsDir: string;\n}\n\nexport interface InstalledLocalModel {\n\tname: string;\n\tsizeBytes: number;\n}\n\nexport interface OllamaModelInfo {\n\tmodelInfo: Record<string, unknown>;\n}\n\n/** Pinned ollama release for the managed installer below. Bump here when needed — one constant. */\nexport const OLLAMA_PINNED_VERSION = \"0.31.1\";\n\nexport type OllamaAssetKind = \"tar-zst\" | \"tar-gz\" | \"zip\";\n\nexport interface OllamaReleaseAsset {\n\tname: string;\n\tkind: OllamaAssetKind;\n}\n\n/**\n * Maps a platform/arch pair (node's `os.platform()`/`os.arch()`) to the exact ollama release asset\n * name for {@link OLLAMA_PINNED_VERSION} — verified against the real ollama/ollama GitHub release\n * and its own install.sh, not guessed: darwin ships a CLI `.tgz` (the `.app`/`.dmg`/`Ollama-*.zip`\n * assets are the separate GUI-app installer, not what we want), linux ships `.tar.zst` per arch,\n * windows ships `.zip` per arch. Pure and exported so it's independently testable and reusable\n * (e.g. by a future doctor-driven managed install) without touching OllamaRuntime.\n */\nexport function resolveOllamaAsset(plat: string, architecture: string): OllamaReleaseAsset | undefined {\n\tif (plat === \"darwin\") {\n\t\t// Universal binary — one asset for both arm64 and x64.\n\t\treturn { name: \"ollama-darwin.tgz\", kind: \"tar-gz\" };\n\t}\n\tif (plat === \"linux\") {\n\t\tconst archName = architecture === \"arm64\" ? \"arm64\" : \"amd64\";\n\t\treturn { name: `ollama-linux-${archName}.tar.zst`, kind: \"tar-zst\" };\n\t}\n\tif (plat === \"win32\") {\n\t\tconst archName = architecture === \"arm64\" ? \"arm64\" : \"amd64\";\n\t\treturn { name: `ollama-windows-${archName}.zip`, kind: \"zip\" };\n\t}\n\treturn undefined;\n}\n\nexport interface RuntimeCommandResult {\n\tok: boolean;\n\tstdout: string;\n\tstderr: string;\n\tcode?: number | null;\n\terror?: string;\n}\n\nexport type RuntimeCommandRunner = (\n\tcommand: string,\n\targs: string[],\n\toptions?: { env?: NodeJS.ProcessEnv; onOutput?: (chunk: string) => void },\n) => Promise<RuntimeCommandResult>;\n\nexport interface LocalRuntimeDeps {\n\tfetchFn?: typeof fetch;\n\tspawnFn?: (\n\t\tcommand: string,\n\t\targs: string[],\n\t\toptions: { env: NodeJS.ProcessEnv },\n\t) => Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\">;\n\texistsFn?: (path: string) => boolean;\n\tenvPath?: string;\n\thomeDir?: string;\n\tsleepFn?: (ms: number) => Promise<void>;\n\t/** os.platform()/os.arch() equivalents — injectable so asset/runtime resolution is testable per platform. */\n\tplatform?: () => string;\n\tarch?: () => string;\n\t/** Runs a runtime-management command (Python venv/pip/download probe). Injectable so tests never\n\t * install packages or hit the network. */\n\trunCommand?: RuntimeCommandRunner;\n\t/** Override for bundled runtime helper scripts, used by tests and source/binary packaging checks. */\n\ttransformersServerScriptPath?: string;\n\t/** Node's built-in zstd decompress transform, when THIS runtime's node:zlib has it — undefined\n\t * forces the system-zstd fallback. Injectable so tests can force either path deterministically\n\t * instead of depending on whatever Node happens to be running the test. */\n\tcreateZstdDecompress?: () => NodeJS.ReadWriteStream;\n\t/** Whether a named command exists on PATH (e.g. system `zstd`), for the extraction fallback. */\n\thasCommand?: (command: string) => boolean;\n\t/** Runs the extraction step for a downloaded archive. Injectable so installManaged's\n\t * download->extract->verify orchestration is testable without a real tar/zstd/unzip pipeline;\n\t * defaults to the real spawn-based extractor. */\n\textractArchive?: (\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: OllamaAssetKind,\n\t) => Promise<{ ok: boolean; error?: string }>;\n}\n\nconst DEFAULT_BASE_URL = \"http://127.0.0.1:11434\";\nconst HEALTH_TIMEOUT_MS = 2_000;\nconst START_POLL_ATTEMPTS = 20;\nconst START_POLL_INTERVAL_MS = 500;\nconst TRANSFORMERS_PORT_BASE = 18_100;\nconst TRANSFORMERS_PORT_SPAN = 1_000;\nconst TRANSFORMERS_START_POLL_ATTEMPTS = 240;\nconst TRANSFORMERS_RUNTIME_DIR_NAME = \"hf-transformers\";\nconst TRANSFORMERS_VENV_DIR_NAME = \"venv\";\nconst TRANSFORMERS_SERVER_RELATIVE_PATH = join(\"runtimes\", \"hf-transformers-openai-server.py\");\nconst TRANSFORMERS_PINNED_PACKAGES = [\"transformers==5.13.0\", \"huggingface-hub==1.22.0\", \"safetensors==0.8.0\"];\nconst TRANSFORMERS_REQUIRED_MODULES = [\"torch\", \"transformers\", \"huggingface_hub\", \"safetensors\"];\nconst TORCH_PINNED_VERSION = \"2.12.1\";\n\nasync function runCommandDefault(\n\tcommand: string,\n\targs: string[],\n\toptions: { env?: NodeJS.ProcessEnv; onOutput?: (chunk: string) => void } = {},\n): Promise<RuntimeCommandResult> {\n\ttry {\n\t\tconst proc = spawnProcess(command, args, {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\tenv: options.env ? { ...process.env, ...options.env } : process.env,\n\t\t});\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tproc.stdout.setEncoding(\"utf8\");\n\t\tproc.stderr.setEncoding(\"utf8\");\n\t\tproc.stdout.on(\"data\", (chunk: string) => {\n\t\t\tstdout += chunk;\n\t\t\toptions.onOutput?.(chunk);\n\t\t});\n\t\tproc.stderr.on(\"data\", (chunk: string) => {\n\t\t\tstderr += chunk;\n\t\t\toptions.onOutput?.(chunk);\n\t\t});\n\t\tconst code = await waitForChildProcess(proc);\n\t\treturn {\n\t\t\tok: code === 0,\n\t\t\tstdout,\n\t\t\tstderr,\n\t\t\tcode,\n\t\t\t...(code === 0 ? {} : { error: stderr.trim() || stdout.trim() || `exit code ${code ?? \"unknown\"}` }),\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tstdout: \"\",\n\t\t\tstderr: \"\",\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t};\n\t}\n}\n\nfunction sanitizeCommandOutput(value: string | undefined): string {\n\treturn value?.trim().split(\"\\n\").slice(-3).join(\"\\n\") || \"unknown error\";\n}\n\nfunction hashString(value: string): number {\n\tlet hash = 2_166_136_261;\n\tfor (let index = 0; index < value.length; index++) {\n\t\thash ^= value.charCodeAt(index);\n\t\thash = Math.imul(hash, 16_777_619) >>> 0;\n\t}\n\treturn hash;\n}\n\nexport function resolveTransformersBaseUrl(modelId: string): string {\n\tconst port = TRANSFORMERS_PORT_BASE + (hashString(modelId) % TRANSFORMERS_PORT_SPAN);\n\treturn `http://127.0.0.1:${port}`;\n}\n\nexport class OllamaRuntime {\n\tprivate readonly _agentDir: string;\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _fetch: typeof fetch;\n\tprivate readonly _spawn: NonNullable<LocalRuntimeDeps[\"spawnFn\"]>;\n\tprivate readonly _exists: (path: string) => boolean;\n\tprivate readonly _envPath: string;\n\tprivate readonly _homeDir: string;\n\tprivate readonly _sleep: (ms: number) => Promise<void>;\n\tprivate readonly _platform: () => string;\n\tprivate readonly _arch: () => string;\n\tprivate readonly _createZstdDecompress: (() => NodeJS.ReadWriteStream) | undefined;\n\tprivate readonly _hasCommand: (command: string) => boolean;\n\tprivate readonly _extractArchiveOverride: LocalRuntimeDeps[\"extractArchive\"] | undefined;\n\tprivate _child: Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\"> | undefined;\n\n\tconstructor(args: { agentDir: string; baseUrl?: string; deps?: LocalRuntimeDeps }) {\n\t\tthis._agentDir = args.agentDir;\n\t\tthis._baseUrl = (args.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n\t\tthis._fetch = args.deps?.fetchFn ?? fetch;\n\t\tthis._spawn = args.deps?.spawnFn ?? ((command, argv, options) => spawn(command, argv, options));\n\t\tthis._exists = args.deps?.existsFn ?? existsSync;\n\t\tthis._envPath = args.deps?.envPath ?? process.env.PATH ?? \"\";\n\t\tthis._homeDir = args.deps?.homeDir ?? homedir();\n\t\tthis._sleep = args.deps?.sleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n\t\tthis._platform = args.deps?.platform ?? osPlatform;\n\t\tthis._arch = args.deps?.arch ?? osArch;\n\t\t// Feature-detect (not version-sniff): recent Node added zstd natively to node:zlib. Verified\n\t\t// present on this repo's minimum supported Node (>=22.19.0); still detected at runtime rather\n\t\t// than assumed, so an older/different Node correctly falls through to the system-zstd path.\n\t\t// `\"createZstdDecompress\" in deps` (rather than `??`) so a test can force the fallback path by\n\t\t// passing the key as explicitly undefined, distinct from omitting it (real auto-detection).\n\t\tthis._createZstdDecompress =\n\t\t\targs.deps && \"createZstdDecompress\" in args.deps\n\t\t\t\t? args.deps.createZstdDecompress\n\t\t\t\t: typeof nodeZlib.createZstdDecompress === \"function\"\n\t\t\t\t\t? () => nodeZlib.createZstdDecompress()\n\t\t\t\t\t: undefined;\n\t\tthis._hasCommand =\n\t\t\targs.deps?.hasCommand ??\n\t\t\t((command) => spawnProcessSync(command, [\"--version\"], { encoding: \"utf8\" }).error === undefined);\n\t\tthis._extractArchiveOverride = args.deps?.extractArchive;\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this._baseUrl;\n\t}\n\n\townedModelsDir(): string {\n\t\treturn join(this._agentDir, \"models\", \"ollama\");\n\t}\n\n\tprivate _findBinary(): { path: string; source: \"system\" | \"user\" | \"pi-owned\" } | undefined {\n\t\tconst piOwned = join(this._agentDir, \"runtimes\", \"ollama\", \"bin\", \"ollama\");\n\t\tif (this._exists(piOwned)) return { path: piOwned, source: \"pi-owned\" };\n\t\tconst userLevel = join(this._homeDir, \".local\", \"share\", \"ollama-dist\", \"bin\", \"ollama\");\n\t\tif (this._exists(userLevel)) return { path: userLevel, source: \"user\" };\n\t\tfor (const dir of this._envPath.split(delimiter)) {\n\t\t\tif (!dir) continue;\n\t\t\tconst candidate = join(dir, \"ollama\");\n\t\t\tif (this._exists(candidate)) return { path: candidate, source: \"system\" };\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate async _serverUp(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/tags`, {\n\t\t\t\tsignal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n\t\t\t});\n\t\t\treturn response.ok;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync detect(): Promise<LocalRuntimeStatus> {\n\t\tconst binary = this._findBinary();\n\t\tconst serverUp = await this._serverUp();\n\t\treturn {\n\t\t\tbinaryPath: binary?.path,\n\t\t\tbinarySource: binary?.source,\n\t\t\tserverUp,\n\t\t\tserverUrl: this._baseUrl,\n\t\t\tmanagedByPi: this._child !== undefined && serverUp,\n\t\t\townedModelsDir: this.ownedModelsDir(),\n\t\t};\n\t}\n\n\t/**\n\t * Manual-steps fallback text — shown when the managed install below wasn't offered (headless),\n\t * was declined, or itself failed. #31 reversed the stance for ollama specifically: pi CAN install\n\t * it for you now (consent-gated, a controlled download into pi's own runtimes dir, never\n\t * `curl | sh`) — these steps are the alternative for when that path isn't taken.\n\t */\n\tinstallGuide(): string[] {\n\t\treturn [\n\t\t\t\"Ollama is not installed. Pi can install it for you now (asks first, never curl|sh) — or follow these manual steps yourself:\",\n\t\t\t\" 1. Download the pinned release archive for your platform:\",\n\t\t\t\" https://github.com/ollama/ollama/releases (asset: ollama-linux-amd64.tar.zst or your platform)\",\n\t\t\t` 2. Extract it to ${join(this._homeDir, \".local\", \"share\", \"ollama-dist\")}`,\n\t\t\t\" 3. Re-run your /models command - pi detects the binary automatically.\",\n\t\t\t\"Alternatively install system-wide from https://ollama.com/download and pi will use the system server.\",\n\t\t];\n\t}\n\n\t/** Which decompressor to use for a `.tar.zst` asset — native (this Node's own zlib) is always\n\t * preferred when available, even if a system `zstd` ALSO exists, since it needs no external\n\t * dependency at all. Verified per-runtime by feature detection (see the constructor), not by\n\t * guessing from a Node version number. */\n\tprivate _chooseZstdStrategy(): { kind: \"native\" } | { kind: \"system\" } | { kind: \"unavailable\" } {\n\t\tif (this._createZstdDecompress) return { kind: \"native\" };\n\t\tif (this._hasCommand(\"zstd\")) return { kind: \"system\" };\n\t\treturn { kind: \"unavailable\" };\n\t}\n\n\t/**\n\t * Managed install (#31): a consent-gated, controlled download of the pinned ollama release into\n\t * pi's OWN runtimes dir (`<agentDir>/runtimes/ollama/`) — never `curl | sh`, never a second\n\t * server instance, and distinct from {@link start}'s owned MODEL storage (this is the binary\n\t * itself). Callers (the router's consent flow, and later a doctor-driven install) own the\n\t * consent/UI; this method only does the mechanical download+extract+verify and reports the\n\t * outcome honestly. `onProgress` is best-effort UI feedback, not a completion signal.\n\t */\n\tasync installManaged(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tconst asset = resolveOllamaAsset(this._platform(), this._arch());\n\t\tif (!asset) return { ok: false, error: \"unsupported-platform\" };\n\n\t\tonProgress?.(`Downloading ${asset.name}…`);\n\t\tconst downloadUrl = `https://github.com/ollama/ollama/releases/download/v${OLLAMA_PINNED_VERSION}/${asset.name}`;\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await this._fetch(downloadUrl);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: `download-fail: ${error instanceof Error ? error.message : String(error)}` };\n\t\t}\n\t\tif (!response.ok || !response.body) {\n\t\t\treturn { ok: false, error: `download-fail: HTTP ${response.status}` };\n\t\t}\n\n\t\tconst destDir = join(this._agentDir, \"runtimes\", \"ollama\");\n\t\tmkdirSync(destDir, { recursive: true });\n\n\t\tonProgress?.(`Extracting ${asset.name}…`);\n\t\tconst extract = this._extractArchiveOverride ?? ((input, dest, kind) => this._extractArchive(input, dest, kind));\n\t\tconst extracted = await extract(response.body as unknown as NodeJS.ReadableStream, destDir, asset.kind);\n\t\tif (!extracted.ok) return extracted;\n\n\t\tconst binaryName = this._platform() === \"win32\" ? \"ollama.exe\" : \"ollama\";\n\t\tconst binaryPath = join(destDir, \"bin\", binaryName);\n\t\tif (!this._exists(binaryPath)) {\n\t\t\treturn { ok: false, error: \"extract-fail: binary not found after extraction\" };\n\t\t}\n\t\tonProgress?.(\"Ollama installed.\");\n\t\treturn { ok: true };\n\t}\n\n\t/** Real extraction: `tar-gz` lets `tar` itself gunzip; `tar-zst` decompresses first (native\n\t * node:zlib preferred, system `zstd` as fallback, an honest error if neither exists — see\n\t * {@link _chooseZstdStrategy}); `zip` needs a seekable file, so it's buffered to disk first. */\n\tprivate async _extractArchive(\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: OllamaAssetKind,\n\t): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tif (kind === \"zip\") {\n\t\t\t\treturn await this._extractZip(input, destDir);\n\t\t\t}\n\t\t\treturn await this._extractTar(input, destDir, kind);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: `extract-fail: ${error instanceof Error ? error.message : String(error)}` };\n\t\t}\n\t}\n\n\tprivate async _extractTar(\n\t\tinput: NodeJS.ReadableStream,\n\t\tdestDir: string,\n\t\tkind: \"tar-gz\" | \"tar-zst\",\n\t): Promise<{ ok: boolean; error?: string }> {\n\t\tlet decompressed: Readable = input as unknown as Readable;\n\t\tconst tarArgs = [\"-xf\", \"-\", \"-C\", destDir];\n\t\tif (kind === \"tar-zst\") {\n\t\t\tconst strategy = this._chooseZstdStrategy();\n\t\t\tif (strategy.kind === \"unavailable\") {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"zstd-missing: this archive needs zstd to decompress and neither Node's built-in support nor a system `zstd` binary was found. Install zstd (e.g. `apt-get install zstd`, `dnf install zstd`, `pacman -S zstd`) and try again.\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (strategy.kind === \"native\") {\n\t\t\t\tdecompressed = decompressed.pipe(\n\t\t\t\t\tthis._createZstdDecompress?.() as unknown as NodeJS.ReadWriteStream & Readable,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst zstdProc = spawnProcess(\"zstd\", [\"-d\"], { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n\t\t\t\tdecompressed.pipe(requireStdin(zstdProc, \"zstd\"));\n\t\t\t\tdecompressed = zstdProc.stdout as unknown as Readable;\n\t\t\t}\n\t\t} else {\n\t\t\t// tar itself handles gzip via -z — no separate decompression step needed.\n\t\t\ttarArgs[0] = \"-xzf\";\n\t\t}\n\t\tconst tarProc = spawnProcess(\"tar\", tarArgs, { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n\t\tdecompressed.pipe(requireStdin(tarProc, \"tar\"));\n\t\tconst code = await waitForChildProcess(tarProc);\n\t\tif (code !== 0) {\n\t\t\treturn { ok: false, error: `extract-fail: tar exited with code ${code}` };\n\t\t}\n\t\treturn { ok: true };\n\t}\n\n\tprivate async _extractZip(input: NodeJS.ReadableStream, destDir: string): Promise<{ ok: boolean; error?: string }> {\n\t\t// Zip's central directory needs seekable file access — buffer to a temp file first.\n\t\tconst zipPath = join(destDir, \"..\", `ollama-download-${process.pid}-${Date.now()}.zip`);\n\t\tawait pipeline(input as unknown as Readable, createWriteStream(zipPath));\n\t\ttry {\n\t\t\tconst extractCommand = this._platform() === \"win32\" && this._hasCommand(\"tar\") ? \"tar\" : \"unzip\";\n\t\t\tconst args = extractCommand === \"tar\" ? [\"-xf\", zipPath, \"-C\", destDir] : [\"-q\", zipPath, \"-d\", destDir];\n\t\t\tconst proc = spawnProcess(extractCommand, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\tconst code = await waitForChildProcess(proc);\n\t\t\tif (code !== 0) {\n\t\t\t\treturn { ok: false, error: `extract-fail: ${extractCommand} exited with code ${code}` };\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\trmSync(zipPath, { force: true });\n\t\t\t} catch {\n\t\t\t\t// best-effort cleanup\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Shared spawn-then-health-poll for both start modes below; `extraEnv` is the only thing that\n\t * differs between them (owned storage vs reusing the user's own). */\n\tprivate async _spawnAndPoll(\n\t\tbinary: { path: string },\n\t\textraEnv: NodeJS.ProcessEnv,\n\t): Promise<{ started: boolean; reason: string }> {\n\t\tconst host = this._baseUrl.replace(/^https?:\\/\\//, \"\");\n\t\tthis._child = this._spawn(binary.path, [\"serve\"], {\n\t\t\tenv: { ...process.env, OLLAMA_HOST: host, ...extraEnv },\n\t\t});\n\t\tthis._child.unref?.();\n\t\tfor (let attempt = 0; attempt < START_POLL_ATTEMPTS; attempt++) {\n\t\t\tif (await this._serverUp()) return { started: true, reason: \"started\" };\n\t\t\tawait this._sleep(START_POLL_INTERVAL_MS);\n\t\t}\n\t\tthis.stop();\n\t\treturn { started: false, reason: \"health_check_timeout\" };\n\t}\n\n\t/**\n\t * Start a pi-managed serve with OWNED storage and the hardened env verified on this class of\n\t * hardware. No-op (reported) when a server already responds — pi never double-serves. Used by\n\t * `/models add` et al, where isolated per-model-pull storage is the point.\n\t */\n\tasync start(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this._serverUp()) {\n\t\t\treturn { started: false, reason: this._child ? \"already_running_managed\" : \"already_running_system\" };\n\t\t}\n\t\tconst binary = this._findBinary();\n\t\tif (!binary) return { started: false, reason: \"binary_missing\" };\n\t\tmkdirSync(this.ownedModelsDir(), { recursive: true });\n\t\treturn this._spawnAndPoll(binary, {\n\t\t\tOLLAMA_MODELS: this.ownedModelsDir(),\n\t\t\tOLLAMA_FLASH_ATTENTION: \"1\",\n\t\t\tOLLAMA_KV_CACHE_TYPE: \"q8_0\",\n\t\t\tOLLAMA_NUM_PARALLEL: \"1\",\n\t\t\tOLLAMA_KEEP_ALIVE: \"30m\",\n\t\t\tOLLAMA_MAX_LOADED_MODELS: \"3\",\n\t\t});\n\t}\n\n\t/**\n\t * Start a serve that REUSES the user's own existing models directory (no `OLLAMA_MODELS`\n\t * override — falls through to Ollama's own default, `~/.ollama`), for callers that must see the\n\t * user's already-pulled models rather than pi's isolated/owned storage. Same idempotency and\n\t * hardened perf env as {@link start}; the only difference is which storage the server sees.\n\t */\n\tasync startReuseExisting(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this._serverUp()) {\n\t\t\treturn { started: false, reason: this._child ? \"already_running_managed\" : \"already_running_system\" };\n\t\t}\n\t\tconst binary = this._findBinary();\n\t\tif (!binary) return { started: false, reason: \"binary_missing\" };\n\t\treturn this._spawnAndPoll(binary, {\n\t\t\tOLLAMA_FLASH_ATTENTION: \"1\",\n\t\t\tOLLAMA_KV_CACHE_TYPE: \"q8_0\",\n\t\t\tOLLAMA_NUM_PARALLEL: \"1\",\n\t\t\tOLLAMA_KEEP_ALIVE: \"30m\",\n\t\t\tOLLAMA_MAX_LOADED_MODELS: \"3\",\n\t\t});\n\t}\n\n\t/** Resource hygiene only: stops the pi-managed serve process; never deletes anything. */\n\tstop(): { stopped: boolean } {\n\t\tif (!this._child) return { stopped: false };\n\t\ttry {\n\t\t\tthis._child.kill(\"SIGTERM\");\n\t\t} catch {\n\t\t\t// already gone\n\t\t}\n\t\tthis._child = undefined;\n\t\treturn { stopped: true };\n\t}\n\n\tasync list(): Promise<InstalledLocalModel[]> {\n\t\tconst response = await this._fetch(`${this._baseUrl}/api/tags`, { signal: AbortSignal.timeout(10_000) });\n\t\tif (!response.ok) throw new Error(`ollama list failed: HTTP ${response.status}`);\n\t\tconst data = (await response.json()) as { models?: Array<{ name?: string; size?: number }> };\n\t\treturn (data.models ?? [])\n\t\t\t.filter((model): model is { name: string; size?: number } => typeof model.name === \"string\")\n\t\t\t.map((model) => ({ name: model.name, sizeBytes: model.size ?? 0 }));\n\t}\n\n\tasync show(ref: string): Promise<{ ok: true; info: OllamaModelInfo } | { ok: false; error: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/show`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref }),\n\t\t\t\tsignal: AbortSignal.timeout(10_000),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `show failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst data = (await response.json()) as { model_info?: Record<string, unknown> };\n\t\t\treturn { ok: true, info: { modelInfo: data.model_info ?? {} } };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\tasync createFromModelfile(args: {\n\t\tname: string;\n\t\tmodelfile: string;\n\t}): Promise<{ ok: true } | { ok: false; error: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/create`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: args.name, modelfile: args.modelfile, stream: false }),\n\t\t\t\tsignal: AbortSignal.timeout(60_000),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `create failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** Pull a model through the server API (weights land in the SERVER's models dir). */\n\tasync pull(ref: string, onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/pull`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref, stream: true }),\n\t\t\t});\n\t\t\tif (!response.ok || !response.body) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `pull failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst reader = response.body.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\t\t\tlet lastStatus = \"\";\n\t\t\tlet errorMessage: string | undefined;\n\t\t\tconst handleLine = (line: string): void => {\n\t\t\t\tif (!line.trim()) return;\n\t\t\t\ttry {\n\t\t\t\t\tconst event = JSON.parse(line) as { status?: string; error?: string };\n\t\t\t\t\tif (event.error) errorMessage = event.error;\n\t\t\t\t\tif (event.status && event.status !== lastStatus) {\n\t\t\t\t\t\tlastStatus = event.status;\n\t\t\t\t\t\tonProgress?.(event.status);\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// partial line noise\n\t\t\t\t}\n\t\t\t};\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() ?? \"\";\n\t\t\t\tfor (const line of lines) handleLine(line);\n\t\t\t}\n\t\t\t// The stream's FINAL line has no trailing newline — it stays in the buffer and holds\n\t\t\t// the terminal \"success\"/error event; dropping it misreports every completed pull.\n\t\t\thandleLine(buffer);\n\t\t\tif (errorMessage) return { ok: false, error: errorMessage };\n\t\t\treturn lastStatus === \"success\"\n\t\t\t\t? { ok: true }\n\t\t\t\t: { ok: false, error: `pull ended with: ${lastStatus || \"unknown\"}` };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** EXPLICIT user action only — callers must have shown what gets deleted and confirmed. */\n\tasync remove(ref: string): Promise<{ ok: boolean; error?: string }> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/api/delete`, {\n\t\t\t\tmethod: \"DELETE\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ model: ref }),\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `delete failed: HTTP ${response.status} ${await response.text().catch(() => \"\")}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { ok: true };\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n}\n\nexport interface TransformersRuntimeStatus {\n\truntimeInstalled: boolean;\n\tserverUp: boolean;\n\tbaseUrl: string;\n\tmodelId: string;\n\tvenvDir: string;\n\tcacheDir: string;\n\tserverScriptPath: string;\n}\n\nexport class TransformersRuntime {\n\tprivate readonly _agentDir: string;\n\tprivate readonly _modelId: string;\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _fetch: typeof fetch;\n\tprivate readonly _spawn: NonNullable<LocalRuntimeDeps[\"spawnFn\"]>;\n\tprivate readonly _exists: (path: string) => boolean;\n\tprivate readonly _sleep: (ms: number) => Promise<void>;\n\tprivate readonly _platform: () => string;\n\tprivate readonly _runCommand: RuntimeCommandRunner;\n\tprivate readonly _serverScriptPath: string;\n\tprivate _proc?: Pick<ChildProcess, \"pid\" | \"kill\" | \"unref\" | \"on\">;\n\n\tconstructor(args: { agentDir: string; modelId: string; baseUrl?: string; deps?: LocalRuntimeDeps }) {\n\t\tthis._agentDir = args.agentDir;\n\t\tthis._modelId = args.modelId;\n\t\tthis._baseUrl = args.baseUrl?.replace(/\\/$/, \"\") ?? resolveTransformersBaseUrl(args.modelId);\n\t\tthis._fetch = args.deps?.fetchFn ?? fetch;\n\t\tthis._spawn = args.deps?.spawnFn ?? ((command, argv, options) => spawn(command, argv, { env: options.env }));\n\t\tthis._exists = args.deps?.existsFn ?? existsSync;\n\t\tthis._sleep = args.deps?.sleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n\t\tthis._platform = args.deps?.platform ?? osPlatform;\n\t\tthis._runCommand = args.deps?.runCommand ?? runCommandDefault;\n\t\tthis._serverScriptPath =\n\t\t\targs.deps?.transformersServerScriptPath ?? join(getBundledResourcesDir(), TRANSFORMERS_SERVER_RELATIVE_PATH);\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this._baseUrl;\n\t}\n\n\tget modelId(): string {\n\t\treturn this._modelId;\n\t}\n\n\tget runtimeDir(): string {\n\t\treturn join(this._agentDir, \"runtimes\", TRANSFORMERS_RUNTIME_DIR_NAME);\n\t}\n\n\tget venvDir(): string {\n\t\treturn join(this.runtimeDir, TRANSFORMERS_VENV_DIR_NAME);\n\t}\n\n\tget cacheDir(): string {\n\t\treturn join(this._agentDir, \"models\", \"huggingface\");\n\t}\n\n\tget pythonPath(): string {\n\t\treturn this._platform() === \"win32\"\n\t\t\t? join(this.venvDir, \"Scripts\", \"python.exe\")\n\t\t\t: join(this.venvDir, \"bin\", \"python\");\n\t}\n\n\tprivate get serverPort(): string {\n\t\tconst parsed = new URL(this._baseUrl);\n\t\treturn parsed.port || (parsed.protocol === \"https:\" ? \"443\" : \"80\");\n\t}\n\n\tprivate get serverHost(): string {\n\t\treturn new URL(this._baseUrl).hostname;\n\t}\n\n\tprivate runtimeEnv(): NodeJS.ProcessEnv {\n\t\tconst venvBin = this._platform() === \"win32\" ? join(this.venvDir, \"Scripts\") : join(this.venvDir, \"bin\");\n\t\treturn {\n\t\t\t...process.env,\n\t\t\tVIRTUAL_ENV: this.venvDir,\n\t\t\tPATH: `${venvBin}${delimiter}${process.env.PATH ?? \"\"}`,\n\t\t\tPYTHONNOUSERSITE: \"1\",\n\t\t\tHF_HOME: this.cacheDir,\n\t\t\tHUGGINGFACE_HUB_CACHE: join(this.cacheDir, \"hub\"),\n\t\t\tTRANSFORMERS_CACHE: join(this.cacheDir, \"transformers\"),\n\t\t\tHF_HUB_DISABLE_TELEMETRY: \"1\",\n\t\t};\n\t}\n\n\tprivate async serverUp(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await this._fetch(`${this._baseUrl}/health`, {\n\t\t\t\tsignal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n\t\t\t});\n\t\t\tif (!response.ok) return false;\n\t\t\tconst body = (await response.json().catch(() => undefined)) as { model?: unknown } | undefined;\n\t\t\treturn body?.model === this._modelId;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync detect(): Promise<TransformersRuntimeStatus> {\n\t\tconst serverUp = await this.serverUp();\n\t\treturn {\n\t\t\truntimeInstalled: this._exists(this.pythonPath) && (serverUp || (await this.runtimeDependenciesInstalled())),\n\t\t\tserverUp,\n\t\t\tbaseUrl: this._baseUrl,\n\t\t\tmodelId: this._modelId,\n\t\t\tvenvDir: this.venvDir,\n\t\t\tcacheDir: this.cacheDir,\n\t\t\tserverScriptPath: this._serverScriptPath,\n\t\t};\n\t}\n\n\tprivate pythonCandidates(): Array<{ command: string; args: string[] }> {\n\t\tconst envPython = process.env.PYTHON?.trim();\n\t\tconst envCandidates = envPython ? [{ command: envPython, args: [] }] : [];\n\t\treturn this._platform() === \"win32\"\n\t\t\t? [...envCandidates, { command: \"py\", args: [\"-3\"] }, { command: \"python\", args: [] }]\n\t\t\t: [...envCandidates, { command: \"python\", args: [] }, { command: \"python3\", args: [] }];\n\t}\n\n\tprivate async findPython(): Promise<{ command: string; args: string[] } | undefined> {\n\t\tfor (const candidate of this.pythonCandidates()) {\n\t\t\tconst result = await this._runCommand(candidate.command, [...candidate.args, \"--version\"]);\n\t\t\tif (result.ok) return candidate;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate torchInstallArgs(): string[] {\n\t\tif (this._platform() === \"darwin\") {\n\t\t\treturn [\"-m\", \"pip\", \"install\", `torch==${TORCH_PINNED_VERSION}`];\n\t\t}\n\t\treturn [\n\t\t\t\"-m\",\n\t\t\t\"pip\",\n\t\t\t\"install\",\n\t\t\t`torch==${TORCH_PINNED_VERSION}+cpu`,\n\t\t\t\"--index-url\",\n\t\t\t\"https://download.pytorch.org/whl/cpu\",\n\t\t];\n\t}\n\n\tprivate async runPython(args: string[], onProgress?: (status: string) => void): Promise<RuntimeCommandResult> {\n\t\treturn this._runCommand(this.pythonPath, args, {\n\t\t\tenv: this.runtimeEnv(),\n\t\t\tonOutput: onProgress\n\t\t\t\t? (chunk) => {\n\t\t\t\t\t\tconst line = chunk.trim();\n\t\t\t\t\t\tif (line) onProgress(line);\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t});\n\t}\n\n\tprivate async runtimeDependenciesInstalled(): Promise<boolean> {\n\t\tif (!this._exists(this.pythonPath)) return false;\n\t\tconst modules = JSON.stringify(TRANSFORMERS_REQUIRED_MODULES);\n\t\tconst result = await this.runPython([\n\t\t\t\"-c\",\n\t\t\t`import importlib.util, sys; missing = [name for name in ${modules} if importlib.util.find_spec(name) is None]; sys.exit(1 if missing else 0)`,\n\t\t]);\n\t\treturn result.ok;\n\t}\n\n\tasync installManaged(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tmkdirSync(this.runtimeDir, { recursive: true });\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tif (!this._exists(this.pythonPath)) {\n\t\t\tconst python = await this.findPython();\n\t\t\tif (!python) return { ok: false, error: \"python-missing\" };\n\t\t\tonProgress?.(`creating isolated Python venv at ${this.venvDir}`);\n\t\t\tconst venv = await this._runCommand(python.command, [...python.args, \"-m\", \"venv\", this.venvDir]);\n\t\t\tif (!venv.ok) return { ok: false, error: `venv-fail: ${sanitizeCommandOutput(venv.error ?? venv.stderr)}` };\n\t\t}\n\t\tif (!this._exists(this.pythonPath)) {\n\t\t\treturn { ok: false, error: `venv-fail: ${this.pythonPath} was not created` };\n\t\t}\n\n\t\tonProgress?.(\"upgrading pip inside pi-managed venv\");\n\t\tconst pip = await this.runPython([\"-m\", \"pip\", \"install\", \"--upgrade\", \"pip\"], onProgress);\n\t\tif (!pip.ok) return { ok: false, error: `pip-upgrade-fail: ${sanitizeCommandOutput(pip.error ?? pip.stderr)}` };\n\n\t\tonProgress?.(\"installing pinned Transformers runtime packages\");\n\t\tconst transformers = await this.runPython([\"-m\", \"pip\", \"install\", ...TRANSFORMERS_PINNED_PACKAGES], onProgress);\n\t\tif (!transformers.ok) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `transformers-install-fail: ${sanitizeCommandOutput(transformers.error ?? transformers.stderr)}`,\n\t\t\t};\n\t\t}\n\n\t\tonProgress?.(\"installing pinned CPU PyTorch backend\");\n\t\tconst torch = await this.runPython(this.torchInstallArgs(), onProgress);\n\t\tif (!torch.ok)\n\t\t\treturn { ok: false, error: `torch-install-fail: ${sanitizeCommandOutput(torch.error ?? torch.stderr)}` };\n\n\t\tconst verify = await this.runPython([\n\t\t\t\"-c\",\n\t\t\t\"import torch, transformers, huggingface_hub; print(transformers.__version__)\",\n\t\t]);\n\t\tif (!verify.ok)\n\t\t\treturn { ok: false, error: `verify-fail: ${sanitizeCommandOutput(verify.error ?? verify.stderr)}` };\n\t\treturn { ok: true };\n\t}\n\n\tasync downloadModel(onProgress?: (status: string) => void): Promise<{ ok: boolean; error?: string }> {\n\t\tif (!this._exists(this.pythonPath)) return { ok: false, error: \"runtime-missing\" };\n\t\tif (!(await this.runtimeDependenciesInstalled())) return { ok: false, error: \"runtime-dependencies-missing\" };\n\t\tif (!this._exists(this._serverScriptPath))\n\t\t\treturn { ok: false, error: `server-script-missing: ${this._serverScriptPath}` };\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tonProgress?.(`downloading ${this._modelId} into ${this.cacheDir}`);\n\t\tconst result = await this.runPython(\n\t\t\t[this._serverScriptPath, \"--model-id\", this._modelId, \"--cache-dir\", this.cacheDir, \"--download-only\"],\n\t\t\tonProgress,\n\t\t);\n\t\treturn result.ok\n\t\t\t? { ok: true }\n\t\t\t: { ok: false, error: `download-fail: ${sanitizeCommandOutput(result.error ?? result.stderr)}` };\n\t}\n\n\tasync start(): Promise<{ started: boolean; reason: string }> {\n\t\tif (await this.serverUp()) return { started: false, reason: \"already_running\" };\n\t\tif (!this._exists(this.pythonPath)) return { started: false, reason: \"runtime_missing\" };\n\t\tif (!this._exists(this._serverScriptPath)) return { started: false, reason: \"server_script_missing\" };\n\t\tmkdirSync(this.cacheDir, { recursive: true });\n\t\tconst env = this.runtimeEnv();\n\t\tthis._proc = this._spawn(\n\t\t\tthis.pythonPath,\n\t\t\t[\n\t\t\t\tthis._serverScriptPath,\n\t\t\t\t\"--model-id\",\n\t\t\t\tthis._modelId,\n\t\t\t\t\"--host\",\n\t\t\t\tthis.serverHost,\n\t\t\t\t\"--port\",\n\t\t\t\tthis.serverPort,\n\t\t\t\t\"--cache-dir\",\n\t\t\t\tthis.cacheDir,\n\t\t\t],\n\t\t\t{ env },\n\t\t);\n\t\tthis._proc.unref?.();\n\t\tfor (let attempt = 0; attempt < TRANSFORMERS_START_POLL_ATTEMPTS; attempt++) {\n\t\t\tif (await this.serverUp()) return { started: true, reason: \"started\" };\n\t\t\tawait this._sleep(START_POLL_INTERVAL_MS);\n\t\t}\n\t\tthis.stop();\n\t\treturn { started: false, reason: \"health_check_timeout\" };\n\t}\n\n\tstop(): { stopped: boolean } {\n\t\tif (!this._proc) return { stopped: false };\n\t\tthis._proc.kill();\n\t\tthis._proc = undefined;\n\t\treturn { stopped: true };\n\t}\n\n\tinstallGuide(): string[] {\n\t\treturn [\n\t\t\t\"Hugging Face Transformers runtime is not installed for this model.\",\n\t\t\t`Pi can install it into its own venv at ${this.venvDir} and cache weights under ${this.cacheDir}.`,\n\t\t\t\"Manual equivalent:\",\n\t\t\t` python -m venv ${this.venvDir}`,\n\t\t\t` ${this.pythonPath} -m pip install --upgrade pip`,\n\t\t\t` ${this.pythonPath} -m pip install ${TRANSFORMERS_PINNED_PACKAGES.join(\" \")}`,\n\t\t\t` ${this.pythonPath} ${this.torchInstallArgs().join(\" \")}`,\n\t\t];\n\t}\n}\n"]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-custom-provider",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.20",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-custom-provider",
|
|
9
|
-
"version": "0.81.
|
|
9
|
+
"version": "0.81.20",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.52.0"
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-sandbox",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.20",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-sandbox",
|
|
9
|
-
"version": "0.81.
|
|
9
|
+
"version": "0.81.20",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sandbox-runtime": "^0.0.26"
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-with-deps",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.20",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-with-deps",
|
|
9
|
-
"version": "0.81.
|
|
9
|
+
"version": "0.81.20",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"ms": "^2.1.3"
|
|
12
12
|
},
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caupulican/pi-adaptative",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.20",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@caupulican/pi-adaptative",
|
|
9
|
-
"version": "0.81.
|
|
9
|
+
"version": "0.81.20",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@caupulican/pi-agent-core": "^0.81.
|
|
13
|
-
"@caupulican/pi-ai": "^0.81.
|
|
14
|
-
"@caupulican/pi-tui": "^0.81.
|
|
12
|
+
"@caupulican/pi-agent-core": "^0.81.20",
|
|
13
|
+
"@caupulican/pi-ai": "^0.81.20",
|
|
14
|
+
"@caupulican/pi-tui": "^0.81.20",
|
|
15
15
|
"@ff-labs/fff-node": "0.9.6",
|
|
16
16
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
17
17
|
"chalk": "5.6.2",
|
|
@@ -475,11 +475,11 @@
|
|
|
475
475
|
}
|
|
476
476
|
},
|
|
477
477
|
"node_modules/@caupulican/pi-agent-core": {
|
|
478
|
-
"version": "0.81.
|
|
479
|
-
"resolved": "https://registry.npmjs.org/@caupulican/pi-agent-core/-/pi-agent-core-0.81.
|
|
478
|
+
"version": "0.81.20",
|
|
479
|
+
"resolved": "https://registry.npmjs.org/@caupulican/pi-agent-core/-/pi-agent-core-0.81.20.tgz",
|
|
480
480
|
"license": "MIT",
|
|
481
481
|
"dependencies": {
|
|
482
|
-
"@caupulican/pi-ai": "^0.81.
|
|
482
|
+
"@caupulican/pi-ai": "^0.81.20",
|
|
483
483
|
"ignore": "7.0.5",
|
|
484
484
|
"typebox": "1.1.38",
|
|
485
485
|
"yaml": "2.9.0"
|
|
@@ -489,8 +489,8 @@
|
|
|
489
489
|
}
|
|
490
490
|
},
|
|
491
491
|
"node_modules/@caupulican/pi-ai": {
|
|
492
|
-
"version": "0.81.
|
|
493
|
-
"resolved": "https://registry.npmjs.org/@caupulican/pi-ai/-/pi-ai-0.81.
|
|
492
|
+
"version": "0.81.20",
|
|
493
|
+
"resolved": "https://registry.npmjs.org/@caupulican/pi-ai/-/pi-ai-0.81.20.tgz",
|
|
494
494
|
"license": "MIT",
|
|
495
495
|
"dependencies": {
|
|
496
496
|
"@anthropic-ai/sdk": "0.91.1",
|
|
@@ -512,8 +512,8 @@
|
|
|
512
512
|
}
|
|
513
513
|
},
|
|
514
514
|
"node_modules/@caupulican/pi-tui": {
|
|
515
|
-
"version": "0.81.
|
|
516
|
-
"resolved": "https://registry.npmjs.org/@caupulican/pi-tui/-/pi-tui-0.81.
|
|
515
|
+
"version": "0.81.20",
|
|
516
|
+
"resolved": "https://registry.npmjs.org/@caupulican/pi-tui/-/pi-tui-0.81.20.tgz",
|
|
517
517
|
"license": "MIT",
|
|
518
518
|
"dependencies": {
|
|
519
519
|
"get-east-asian-width": "1.6.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caupulican/pi-adaptative",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.20",
|
|
4
4
|
"description": "Adaptive fork of Pi coding agent for self-evolving agent harness experiments",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -73,9 +73,9 @@
|
|
|
73
73
|
"prepublishOnly": "npm run clean && npm run build && npm run shrinkwrap"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@caupulican/pi-agent-core": "^0.81.
|
|
77
|
-
"@caupulican/pi-ai": "^0.81.
|
|
78
|
-
"@caupulican/pi-tui": "^0.81.
|
|
76
|
+
"@caupulican/pi-agent-core": "^0.81.20",
|
|
77
|
+
"@caupulican/pi-ai": "^0.81.20",
|
|
78
|
+
"@caupulican/pi-tui": "^0.81.20",
|
|
79
79
|
"@ff-labs/fff-node": "0.9.6",
|
|
80
80
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
81
81
|
"chalk": "5.6.2",
|