turbollm 1.6.2 → 1.6.3
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/README.md +7 -3
- package/dist/cli.js +203 -62
- package/dist/webdist/assets/{AgentsScreen-BviDj463.js → AgentsScreen-DSJoisHr.js} +1 -1
- package/dist/webdist/assets/{ChatScreen-4a_6ZX6c.js → ChatScreen-CPTQTPDq.js} +3 -3
- package/dist/webdist/assets/{CustomizeScreen-BhZJK7xv.js → CustomizeScreen-CveMZwK6.js} +1 -1
- package/dist/webdist/assets/{DeveloperScreen-8CVekggw.js → DeveloperScreen-fnTDP0tf.js} +1 -1
- package/dist/webdist/assets/{EnginesScreen-zHLzht8k.js → EnginesScreen-rlz0E1mk.js} +1 -1
- package/dist/webdist/assets/{ModelDetailDialog-DjMSf0rz.js → ModelDetailDialog-b1xxFqb8.js} +1 -1
- package/dist/webdist/assets/{ModelDirs-CIOcl5qu.js → ModelDirs-DPJoCOcI.js} +1 -1
- package/dist/webdist/assets/{ModelsScreen-v7kDcWmr.js → ModelsScreen-BQF-ZHhj.js} +2 -2
- package/dist/webdist/assets/{SettingsScreen-DnSgBps7.js → SettingsScreen-Z4AoirUg.js} +1 -1
- package/dist/webdist/assets/{alert-dialog-93rFXd1_.js → alert-dialog-DzOwQljM.js} +1 -1
- package/dist/webdist/assets/{arc-BFc9YP9w.js → arc-DJj3Lyu5.js} +1 -1
- package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-Bd6WFbHz.js → architectureDiagram-3BPJPVTR-BcgkjjsC.js} +1 -1
- package/dist/webdist/assets/{arrow-left--NjoPnor.js → arrow-left-C8---sig.js} +1 -1
- package/dist/webdist/assets/{blockDiagram-GPEHLZMM-Dupawk92.js → blockDiagram-GPEHLZMM-C5iJ2c-N.js} +1 -1
- package/dist/webdist/assets/{c4Diagram-AAUBKEIU-BMhAUE8i.js → c4Diagram-AAUBKEIU-BnsiE4U5.js} +1 -1
- package/dist/webdist/assets/channel-DidzH_JR.js +1 -0
- package/dist/webdist/assets/check-CRM4LOGX.js +1 -0
- package/dist/webdist/assets/{chevron-right-kiIQKgde.js → chevron-right-C18nBpLx.js} +1 -1
- package/dist/webdist/assets/{chunk-2J33WTMH-BhxySqcq.js → chunk-2J33WTMH-D_Hm4Lii.js} +1 -1
- package/dist/webdist/assets/{chunk-4BX2VUAB-B-UGkx88.js → chunk-4BX2VUAB-Cooqn_zH.js} +1 -1
- package/dist/webdist/assets/{chunk-55IACEB6-BDvjmk8T.js → chunk-55IACEB6-C2XDZnC0.js} +1 -1
- package/dist/webdist/assets/{chunk-727SXJPM-BhyFFQxZ.js → chunk-727SXJPM-CCEeI48H.js} +1 -1
- package/dist/webdist/assets/{chunk-AQP2D5EJ-VTSW3fZJ.js → chunk-AQP2D5EJ-7FdU3GXV.js} +1 -1
- package/dist/webdist/assets/{chunk-FMBD7UC4-QKx1b_-m.js → chunk-FMBD7UC4-Dpyt8woB.js} +1 -1
- package/dist/webdist/assets/{chunk-ND2GUHAM-DZANzpY0.js → chunk-ND2GUHAM-ildY-Wx8.js} +1 -1
- package/dist/webdist/assets/{chunk-QZHKN3VN-DVZIy115.js → chunk-QZHKN3VN-DfdDD-h6.js} +1 -1
- package/dist/webdist/assets/{circle-check-C4aACGyb.js → circle-check-C9nr7e7f.js} +1 -1
- package/dist/webdist/assets/{circle-x-koFsrlGA.js → circle-x-CrulSiVz.js} +1 -1
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-DrgybMsY.js +1 -0
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-DrgybMsY.js +1 -0
- package/dist/webdist/assets/{common-CQVogVIs.js → common-B6XHnwTG.js} +1 -1
- package/dist/webdist/assets/{copy-button-3_2Us2zb.js → copy-button-Sp-U8ULJ.js} +1 -1
- package/dist/webdist/assets/{cose-bilkent-S5V4N54A-Ci07N3_4.js → cose-bilkent-S5V4N54A-DggDgWVR.js} +1 -1
- package/dist/webdist/assets/{dagre-BM42HDAG-45qc0phu.js → dagre-BM42HDAG-C3owUH7E.js} +1 -1
- package/dist/webdist/assets/{diagram-2AECGRRQ-DA3u3hzE.js → diagram-2AECGRRQ-Ch2E5js7.js} +1 -1
- package/dist/webdist/assets/{diagram-5GNKFQAL-B3wpvPKp.js → diagram-5GNKFQAL-DLeGX2xz.js} +1 -1
- package/dist/webdist/assets/{diagram-KO2AKTUF-h1qR3ckq.js → diagram-KO2AKTUF-DpxkKgZH.js} +1 -1
- package/dist/webdist/assets/{diagram-LMA3HP47-B3uU2roT.js → diagram-LMA3HP47-ia4luRgC.js} +1 -1
- package/dist/webdist/assets/{diagram-OG6HWLK6-CibVtZrO.js → diagram-OG6HWLK6-BMmkGNMK.js} +1 -1
- package/dist/webdist/assets/{dialog-Dn3dxmMP.js → dialog-DLncpxeJ.js} +1 -1
- package/dist/webdist/assets/{erDiagram-TEJ5UH35-VsHZBAOZ.js → erDiagram-TEJ5UH35-DkKBBG05.js} +1 -1
- package/dist/webdist/assets/{flowDiagram-I6XJVG4X-DhYah8sF.js → flowDiagram-I6XJVG4X-kUtoUBI6.js} +1 -1
- package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-RPkNEJnH.js → ganttDiagram-6RSMTGT7-DQReYR3Z.js} +1 -1
- package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-JknfsYQp.js → gitGraphDiagram-PVQCEYII-B2kU7JBk.js} +1 -1
- package/dist/webdist/assets/{index-K2ZQBdky.js → index-CjGc01NF.js} +2 -2
- package/dist/webdist/assets/{infoDiagram-5YYISTIA-BeGMPySd.js → infoDiagram-5YYISTIA-k1S105HR.js} +1 -1
- package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-D38Q1-tz.js → ishikawaDiagram-YF4QCWOH-BgPQRD_p.js} +1 -1
- package/dist/webdist/assets/{journeyDiagram-JHISSGLW-CG8TFzp6.js → journeyDiagram-JHISSGLW-_aNOepbu.js} +1 -1
- package/dist/webdist/assets/{kanban-definition-UN3LZRKU-CNgUZ1rd.js → kanban-definition-UN3LZRKU-ooXgg2sY.js} +1 -1
- package/dist/webdist/assets/{linear-DYIxmEeo.js → linear-B6ijN2hK.js} +1 -1
- package/dist/webdist/assets/{mermaid.core-CZhJJFjL.js → mermaid.core-C39fo6yP.js} +4 -4
- package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-BIVYSbvh.js → mindmap-definition-RKZ34NQL-Ci1PW-rt.js} +1 -1
- package/dist/webdist/assets/{pencil-iVnKKeCp.js → pencil-CvIU0pkN.js} +1 -1
- package/dist/webdist/assets/{personas-DslX_qdt.js → personas-CIW1sEuT.js} +2 -0
- package/dist/webdist/assets/{pieDiagram-4H26LBE5-CU9qALXo.js → pieDiagram-4H26LBE5-SVL4rTNg.js} +1 -1
- package/dist/webdist/assets/{plus-mSm32jpN.js → plus-3YM8ko_h.js} +1 -1
- package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-AE50YmvR.js → quadrantDiagram-W4KKPZXB-sTVWSdlQ.js} +1 -1
- package/dist/webdist/assets/{refresh-cw-C04dn1r1.js → refresh-cw-BrUDSVRs.js} +1 -1
- package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-Crymka5P.js → requirementDiagram-4Y6WPE33-3R4K8jZs.js} +1 -1
- package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-DRviwCen.js → sankeyDiagram-5OEKKPKP-BMozn5XV.js} +1 -1
- package/dist/webdist/assets/{save-BT2nzRB6.js → save-CeI7y6CK.js} +1 -1
- package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-DuBllsfJ.js → sequenceDiagram-3UESZ5HK-B0bhq6qo.js} +1 -1
- package/dist/webdist/assets/{skeleton-BfMJjCGZ.js → skeleton-Dm2BPYbG.js} +1 -1
- package/dist/webdist/assets/{stateDiagram-AJRCARHV-Cj4BwRLl.js → stateDiagram-AJRCARHV-CXaf-H6k.js} +1 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-BYFRKnq-.js +1 -0
- package/dist/webdist/assets/{timeline-definition-PNZ67QCA-BWnH2-RY.js → timeline-definition-PNZ67QCA-BUO22RTP.js} +1 -1
- package/dist/webdist/assets/{trash-2-Ce39UFy5.js → trash-2-D1fUAajM.js} +1 -1
- package/dist/webdist/assets/{vennDiagram-CIIHVFJN-GgdSx9c7.js → vennDiagram-CIIHVFJN-B6l8WK-i.js} +1 -1
- package/dist/webdist/assets/{wardley-L42UT6IY-C9XKbMb6.js → wardley-L42UT6IY-nBQZIDHO.js} +1 -1
- package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-ZxceNtWm.js → wardleyDiagram-YWT4CUSO-Bl5nA5Sj.js} +1 -1
- package/dist/webdist/assets/{wrench-Ceqe74n2.js → wrench-2q903Ji-.js} +1 -1
- package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-C-mAbbHW.js → xychartDiagram-2RQKCTM6-BfRB7FkA.js} +1 -1
- package/dist/webdist/index.html +1 -1
- package/package.json +1 -1
- package/dist/webdist/assets/channel-BCSKocRg.js +0 -1
- package/dist/webdist/assets/check-Bu2UFiB0.js +0 -1
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-B_Eb_jRj.js +0 -1
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-B_Eb_jRj.js +0 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-Y25sKBZm.js +0 -1
package/README.md
CHANGED
|
@@ -138,10 +138,14 @@ the detail:
|
|
|
138
138
|
metadata (arch, params, quant, context, vision) for every file.
|
|
139
139
|
- **Browse & download from Hugging Face**, in-app: a live, sortable list (trending / downloads
|
|
140
140
|
/ likes / recently updated / newest) alongside a permanent detail pane — pick a quant, read
|
|
141
|
-
the **rendered model card**, and download with **resume + SHA-256 verification**.
|
|
142
|
-
|
|
141
|
+
the **rendered model card**, and download with **resume + SHA-256 verification**. Each model
|
|
142
|
+
lands in its own folder (mirroring Hugging Face's own layout) with its **vision projector and
|
|
143
|
+
every shard of a split/multipart quant fetched alongside it automatically**. Gated models
|
|
144
|
+
(Llama, Gemma) work via your own HF token, which **never leaves your machine**.
|
|
143
145
|
- **Import from any URL** — not just Hugging Face. Paste a direct `.gguf` link (model-author
|
|
144
|
-
sites, mirrors, private servers)
|
|
146
|
+
sites, mirrors, private servers) and it disk-space-checks and downloads through the same
|
|
147
|
+
manager; paste a Hugging Face **model page** link instead and it opens that repo's quant
|
|
148
|
+
picker so you can choose which one to download.
|
|
145
149
|
- **Quant recommendation per GPU** and a **VRAM-fit verdict** so you pick a quant that
|
|
146
150
|
actually fits before you commit.
|
|
147
151
|
- **Primary download folder**, real-time **measured t/s per model**, and **delete-from-disk**.
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { spawn as spawn5 } from "child_process";
|
|
5
5
|
import { openSync as openSync3, readFileSync as readFileSync11 } from "fs";
|
|
6
6
|
import { serve } from "@hono/node-server";
|
|
7
|
-
import { dirname as
|
|
7
|
+
import { dirname as dirname10, join as join24 } from "path";
|
|
8
8
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9
9
|
|
|
10
10
|
// src/config/config.ts
|
|
@@ -4247,6 +4247,54 @@ var HfClient = class {
|
|
|
4247
4247
|
...safetensors ? { safetensors } : {}
|
|
4248
4248
|
};
|
|
4249
4249
|
}
|
|
4250
|
+
/** Expand a chosen GGUF into every concrete file needed for a working model
|
|
4251
|
+
* (spec 10 §3): all shards of its split group (NNNNN-of-NNNNN) plus its mmproj vision
|
|
4252
|
+
* projector. Matching is scoped to the chosen file's OWN repo directory so a repo
|
|
4253
|
+
* that organises quants in per-quant subfolders (e.g. `Q4_K_M/…`, `Q8_0/…`) never
|
|
4254
|
+
* cross-matches another quant's identically-named shards. The mmproj is preferred
|
|
4255
|
+
* from the same directory (mirrors the scanner's per-directory pairing), falling back
|
|
4256
|
+
* to the largest anywhere in the repo. `rev` is the git revision (branch/tag/commit)
|
|
4257
|
+
* the download targets. Best-effort: on any HF failure it returns just the one
|
|
4258
|
+
* requested file. When the requested file is itself an mmproj, none is paired. */
|
|
4259
|
+
async expandModelFiles(repo, rfilename, rev = "main") {
|
|
4260
|
+
const wantBase = base(rfilename).toLowerCase();
|
|
4261
|
+
let tree;
|
|
4262
|
+
try {
|
|
4263
|
+
tree = await this.getJson(`${BASE}/api/models/${repo}/tree/${encodeURIComponent(rev)}?recursive=true`);
|
|
4264
|
+
} catch {
|
|
4265
|
+
return { dir: dirOf(rfilename), files: [{ rfilename, size: 0, mmproj: wantBase.includes("mmproj") }] };
|
|
4266
|
+
}
|
|
4267
|
+
const ggufs = tree.filter((e) => e.type === "file" && /\.gguf$/i.test(e.path));
|
|
4268
|
+
const chosen = ggufs.find((e) => base(e.path).toLowerCase() === wantBase);
|
|
4269
|
+
if (!chosen) {
|
|
4270
|
+
return { dir: dirOf(rfilename), files: [{ rfilename, size: 0, mmproj: wantBase.includes("mmproj") }] };
|
|
4271
|
+
}
|
|
4272
|
+
const modelDir = dirOf(chosen.path);
|
|
4273
|
+
const inModelDir = (e) => dirOf(e.path) === modelDir;
|
|
4274
|
+
const files = [];
|
|
4275
|
+
const split = base(chosen.path).match(SPLIT_RE2);
|
|
4276
|
+
if (split) {
|
|
4277
|
+
const prefix = split[1].toLowerCase();
|
|
4278
|
+
const total = split[3];
|
|
4279
|
+
for (const e of ggufs) {
|
|
4280
|
+
if (!inModelDir(e)) continue;
|
|
4281
|
+
const sm = base(e.path).match(SPLIT_RE2);
|
|
4282
|
+
if (sm && sm[1].toLowerCase() === prefix && sm[3] === total) {
|
|
4283
|
+
files.push({ rfilename: e.path, size: sizeOf(e), sha256: e.lfs?.oid, mmproj: false });
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
files.sort((a, b) => a.rfilename.localeCompare(b.rfilename));
|
|
4287
|
+
} else {
|
|
4288
|
+
files.push({ rfilename: chosen.path, size: sizeOf(chosen), sha256: chosen.lfs?.oid, mmproj: false });
|
|
4289
|
+
}
|
|
4290
|
+
if (!wantBase.includes("mmproj")) {
|
|
4291
|
+
const projectors = ggufs.filter((e) => base(e.path).toLowerCase().includes("mmproj"));
|
|
4292
|
+
const sameDir = projectors.filter(inModelDir);
|
|
4293
|
+
const best = (sameDir.length ? sameDir : projectors).sort((a, b) => sizeOf(b) - sizeOf(a))[0];
|
|
4294
|
+
if (best) files.push({ rfilename: best.path, size: sizeOf(best), sha256: best.lfs?.oid, mmproj: true });
|
|
4295
|
+
}
|
|
4296
|
+
return { dir: modelDir, files };
|
|
4297
|
+
}
|
|
4250
4298
|
/** Public model-card fetch (ADR-099): the cleaned README for `owner/repo`, or '' when
|
|
4251
4299
|
* missing/unreachable. Used by auto-tune to read recommended sampling from the card.
|
|
4252
4300
|
* Uses a LARGER cap than the display path: popular requanters (e.g. unsloth) put their
|
|
@@ -4397,6 +4445,10 @@ function base(p) {
|
|
|
4397
4445
|
const i = p.lastIndexOf("/");
|
|
4398
4446
|
return i >= 0 ? p.slice(i + 1) : p;
|
|
4399
4447
|
}
|
|
4448
|
+
function dirOf(p) {
|
|
4449
|
+
const i = p.lastIndexOf("/");
|
|
4450
|
+
return i >= 0 ? p.slice(0, i) : "";
|
|
4451
|
+
}
|
|
4400
4452
|
|
|
4401
4453
|
// src/downloads/downloads.ts
|
|
4402
4454
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -4411,11 +4463,10 @@ import {
|
|
|
4411
4463
|
statSync as statSync3,
|
|
4412
4464
|
writeFileSync as writeFileSync5
|
|
4413
4465
|
} from "fs";
|
|
4414
|
-
import { basename as basename2, join as join15 } from "path";
|
|
4466
|
+
import { basename as basename2, dirname as dirname5, join as join15 } from "path";
|
|
4415
4467
|
import { Readable as Readable2 } from "stream";
|
|
4416
4468
|
import { pipeline as pipeline2 } from "stream/promises";
|
|
4417
4469
|
var MAX_CONCURRENT = 2;
|
|
4418
|
-
var HF_BLOB_RE = /^https?:\/\/huggingface\.co\/.+\/resolve\/.+\.gguf$/i;
|
|
4419
4470
|
var DownloadError = class extends Error {
|
|
4420
4471
|
constructor(code, message) {
|
|
4421
4472
|
super(message);
|
|
@@ -4425,10 +4476,11 @@ var DownloadError = class extends Error {
|
|
|
4425
4476
|
code;
|
|
4426
4477
|
};
|
|
4427
4478
|
var DownloadManager = class {
|
|
4428
|
-
constructor(store2, onComplete, authHeaders) {
|
|
4479
|
+
constructor(store2, onComplete, authHeaders, expand) {
|
|
4429
4480
|
this.store = store2;
|
|
4430
4481
|
this.onComplete = onComplete;
|
|
4431
4482
|
this.authHeaders = authHeaders;
|
|
4483
|
+
this.expand = expand;
|
|
4432
4484
|
const dir = join15(store2.dir(), "downloads");
|
|
4433
4485
|
mkdirSync8(dir, { recursive: true });
|
|
4434
4486
|
this.manifestPath = join15(dir, "manifest.json");
|
|
@@ -4439,6 +4491,7 @@ var DownloadManager = class {
|
|
|
4439
4491
|
store;
|
|
4440
4492
|
onComplete;
|
|
4441
4493
|
authHeaders;
|
|
4494
|
+
expand;
|
|
4442
4495
|
records = /* @__PURE__ */ new Map();
|
|
4443
4496
|
controllers = /* @__PURE__ */ new Map();
|
|
4444
4497
|
manifestPath;
|
|
@@ -4460,57 +4513,115 @@ var DownloadManager = class {
|
|
|
4460
4513
|
list() {
|
|
4461
4514
|
return [...this.records.values()].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
4462
4515
|
}
|
|
4463
|
-
/** Enqueue a download. Validates the target + disk space, persists, then kicks
|
|
4464
|
-
*
|
|
4465
|
-
*
|
|
4466
|
-
|
|
4516
|
+
/** Enqueue a download. Validates the target + disk space, persists, then kicks the
|
|
4517
|
+
* queue. Returns every record created — a single file for a raw non-HF URL, or the
|
|
4518
|
+
* whole model (all split shards + the shared mmproj) for an HF repo file. Throws
|
|
4519
|
+
* DownloadError for caller-actionable failures (no dir set, bad URL, insufficient
|
|
4520
|
+
* disk). */
|
|
4521
|
+
async enqueue(input) {
|
|
4467
4522
|
const dir = this.primaryDir();
|
|
4468
4523
|
if (!dir) throw new DownloadError("no_model_dir", "Add a model folder in Settings before downloading.");
|
|
4469
4524
|
let repo = (input.repo ?? "").trim();
|
|
4470
|
-
const
|
|
4471
|
-
let
|
|
4472
|
-
let
|
|
4525
|
+
const explicitSubdir = (input.subdir ?? "").trim();
|
|
4526
|
+
let rfilename = (input.rfilename ?? "").trim();
|
|
4527
|
+
let rev = "main";
|
|
4473
4528
|
if (input.url) {
|
|
4474
4529
|
const u = normalizeHfBlobUrl(input.url.trim());
|
|
4475
4530
|
if (!/^https?:\/\//i.test(u)) throw new DownloadError("invalid_url", "URL must start with http:// or https://.");
|
|
4476
|
-
const
|
|
4477
|
-
if (
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
if (
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
};
|
|
4510
|
-
|
|
4531
|
+
const hf2 = parseHfResolveUrl(u);
|
|
4532
|
+
if (hf2) {
|
|
4533
|
+
repo = hf2.repo;
|
|
4534
|
+
rfilename = hf2.rfilename;
|
|
4535
|
+
rev = hf2.rev;
|
|
4536
|
+
} else {
|
|
4537
|
+
const path = safePathname(u);
|
|
4538
|
+
if (!explicitSubdir && !/\.gguf$/i.test(path)) throw new DownloadError("invalid_url", "URL must point to a .gguf file.");
|
|
4539
|
+
const filename = basename2(path);
|
|
4540
|
+
if (!explicitSubdir && !/\.gguf$/i.test(filename)) throw new DownloadError("invalid_url", "Could not derive a .gguf filename from that URL.");
|
|
4541
|
+
const destDir2 = explicitSubdir ? join15(dir, explicitSubdir) : dir;
|
|
4542
|
+
mkdirSync8(destDir2, { recursive: true });
|
|
4543
|
+
if ((input.size ?? 0) > 0) this.assertDisk(dir, input.size);
|
|
4544
|
+
const dest = join15(destDir2, filename);
|
|
4545
|
+
if (this.hasLiveDest(dest)) return [];
|
|
4546
|
+
return this.commit([{ name: filename, repo: "", url: u, dest, total: input.size ?? 0, sha256: input.sha256 }]);
|
|
4547
|
+
}
|
|
4548
|
+
}
|
|
4549
|
+
if (!repo.includes("/") || !rfilename) throw new DownloadError("invalid_request", "repo and rfilename are required.");
|
|
4550
|
+
if (!explicitSubdir && !/\.gguf$/i.test(rfilename)) throw new DownloadError("invalid_url", "The file must be a .gguf.");
|
|
4551
|
+
if (explicitSubdir || !this.expand) {
|
|
4552
|
+
const filename = basename2(rfilename);
|
|
4553
|
+
const destDir2 = join15(dir, explicitSubdir || repoSubdir(repo));
|
|
4554
|
+
mkdirSync8(destDir2, { recursive: true });
|
|
4555
|
+
const dest = join15(destDir2, filename);
|
|
4556
|
+
if (this.hasLiveDest(dest)) return [];
|
|
4557
|
+
if ((input.size ?? 0) > 0) this.assertDisk(dir, input.size);
|
|
4558
|
+
return this.commit([
|
|
4559
|
+
{ name: filename, repo, url: hfResolveUrl(repo, rev, rfilename), dest, total: input.size ?? 0, sha256: input.sha256 }
|
|
4560
|
+
]);
|
|
4561
|
+
}
|
|
4562
|
+
const { dir: modelDir, files } = await this.expand(repo, rfilename, rev);
|
|
4563
|
+
const destDir = join15(dir, repoSubdir(repo), modelDir);
|
|
4564
|
+
mkdirSync8(destDir, { recursive: true });
|
|
4565
|
+
const targets = files.map((f) => ({ f, dest: join15(destDir, basename2(f.rfilename)) })).filter(({ f, dest }) => {
|
|
4566
|
+
if (this.hasLiveDest(dest)) return false;
|
|
4567
|
+
if (f.mmproj && existsSync14(dest)) {
|
|
4568
|
+
if (f.size <= 0) return false;
|
|
4569
|
+
try {
|
|
4570
|
+
return statSync3(dest).size !== f.size;
|
|
4571
|
+
} catch {
|
|
4572
|
+
return true;
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
return true;
|
|
4576
|
+
});
|
|
4577
|
+
if (targets.length === 0) return [];
|
|
4578
|
+
const totalBytes = targets.reduce((s, { f }) => s + (f.size || 0), 0);
|
|
4579
|
+
if (totalBytes > 0) this.assertDisk(dir, totalBytes);
|
|
4580
|
+
return this.commit(
|
|
4581
|
+
targets.map(({ f, dest }) => ({
|
|
4582
|
+
name: basename2(f.rfilename),
|
|
4583
|
+
repo,
|
|
4584
|
+
url: hfResolveUrl(repo, rev, f.rfilename),
|
|
4585
|
+
dest,
|
|
4586
|
+
total: f.size || 0,
|
|
4587
|
+
sha256: f.sha256
|
|
4588
|
+
}))
|
|
4589
|
+
);
|
|
4590
|
+
}
|
|
4591
|
+
/** True when a queued/downloading/paused record already targets this exact dest — the
|
|
4592
|
+
* guard against two concurrent writers corrupting one file. Terminal records
|
|
4593
|
+
* (done/cancelled/error) don't block a fresh attempt. */
|
|
4594
|
+
hasLiveDest(dest) {
|
|
4595
|
+
for (const r of this.records.values()) {
|
|
4596
|
+
if (r.dest === dest && (r.status === "queued" || r.status === "downloading" || r.status === "paused")) return true;
|
|
4597
|
+
}
|
|
4598
|
+
return false;
|
|
4599
|
+
}
|
|
4600
|
+
/** Materialise queued records from resolved targets, persist once, and kick the queue. */
|
|
4601
|
+
commit(targets) {
|
|
4602
|
+
const created = [];
|
|
4603
|
+
for (const t of targets) {
|
|
4604
|
+
const id = `dl-${Date.now().toString(36)}-${(this.nextSeq++).toString(36)}`;
|
|
4605
|
+
const rec = {
|
|
4606
|
+
id,
|
|
4607
|
+
name: t.name,
|
|
4608
|
+
repo: t.repo,
|
|
4609
|
+
url: t.url,
|
|
4610
|
+
dest: t.dest,
|
|
4611
|
+
total: t.total,
|
|
4612
|
+
received: 0,
|
|
4613
|
+
status: "queued",
|
|
4614
|
+
error: null,
|
|
4615
|
+
bytesPerSec: 0,
|
|
4616
|
+
sha256: t.sha256,
|
|
4617
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4618
|
+
};
|
|
4619
|
+
this.records.set(id, rec);
|
|
4620
|
+
created.push(rec);
|
|
4621
|
+
}
|
|
4511
4622
|
this.persist();
|
|
4512
4623
|
this.pump();
|
|
4513
|
-
return
|
|
4624
|
+
return created;
|
|
4514
4625
|
}
|
|
4515
4626
|
/** Cancel an in-flight or queued job: abort the stream, delete the .part, mark
|
|
4516
4627
|
* cancelled. Keeps the record so the UI can show + clear it. */
|
|
@@ -4605,6 +4716,7 @@ var DownloadManager = class {
|
|
|
4605
4716
|
}
|
|
4606
4717
|
const clen = Number(res.headers.get("content-length") ?? 0);
|
|
4607
4718
|
if (clen > 0) rec.total = resuming ? startAt + clen : clen;
|
|
4719
|
+
if (clen > 0) this.assertDisk(dirname5(part), clen);
|
|
4608
4720
|
const hash = rec.sha256 ? createHash3("sha256") : null;
|
|
4609
4721
|
const verifyHash = hash !== null && startAt === 0;
|
|
4610
4722
|
let lastTick = Date.now();
|
|
@@ -4763,6 +4875,30 @@ function safePathname(u) {
|
|
|
4763
4875
|
return "";
|
|
4764
4876
|
}
|
|
4765
4877
|
}
|
|
4878
|
+
function parseHfResolveUrl(u) {
|
|
4879
|
+
let parsed;
|
|
4880
|
+
try {
|
|
4881
|
+
parsed = new URL(u);
|
|
4882
|
+
} catch {
|
|
4883
|
+
return null;
|
|
4884
|
+
}
|
|
4885
|
+
if (parsed.hostname !== "huggingface.co") return null;
|
|
4886
|
+
const parts = parsed.pathname.split("/").filter(Boolean);
|
|
4887
|
+
const ri = parts.indexOf("resolve");
|
|
4888
|
+
if (ri < 2 || parts.length < ri + 3) return null;
|
|
4889
|
+
const repo = parts.slice(0, ri).join("/");
|
|
4890
|
+
const rev = decodeURIComponent(parts[ri + 1]);
|
|
4891
|
+
const rfilename = parts.slice(ri + 2).map((s) => decodeURIComponent(s)).join("/");
|
|
4892
|
+
if (!repo.includes("/") || !rev || !rfilename || !/\.gguf$/i.test(rfilename)) return null;
|
|
4893
|
+
return { repo, rev, rfilename };
|
|
4894
|
+
}
|
|
4895
|
+
function hfResolveUrl(repo, rev, rfilename) {
|
|
4896
|
+
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
4897
|
+
return `https://huggingface.co/${enc(repo)}/resolve/${encodeURIComponent(rev)}/${enc(rfilename)}`;
|
|
4898
|
+
}
|
|
4899
|
+
function repoSubdir(repo) {
|
|
4900
|
+
return repo.replace(/\\/g, "/").split("/").filter((s) => s && s !== "." && s !== "..").join("/");
|
|
4901
|
+
}
|
|
4766
4902
|
|
|
4767
4903
|
// src/bench/bench.ts
|
|
4768
4904
|
import { execFile as execFile7 } from "child_process";
|
|
@@ -8044,13 +8180,13 @@ import { Hono } from "hono";
|
|
|
8044
8180
|
import { cors } from "hono/cors";
|
|
8045
8181
|
import { existsSync as existsSync21, readFileSync as readFileSync10, statSync as statSync6 } from "fs";
|
|
8046
8182
|
import { fileURLToPath } from "url";
|
|
8047
|
-
import { dirname as
|
|
8183
|
+
import { dirname as dirname9, join as join23, normalize as normalize2 } from "path";
|
|
8048
8184
|
import { Agent, setGlobalDispatcher } from "undici";
|
|
8049
8185
|
|
|
8050
8186
|
// src/api/routes.ts
|
|
8051
8187
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
8052
8188
|
import { existsSync as existsSync20, mkdirSync as mkdirSync12, readFileSync as readFileSync9, readdirSync as readdirSync7, realpathSync, rmSync as rmSync10, statSync as statSync5, writeFileSync as writeFileSync9 } from "fs";
|
|
8053
|
-
import { basename as basename3, dirname as
|
|
8189
|
+
import { basename as basename3, dirname as dirname8, join as join22, resolve, sep } from "path";
|
|
8054
8190
|
|
|
8055
8191
|
// src/comfyui/gate-template.ts
|
|
8056
8192
|
var GATE_VERSION = 2;
|
|
@@ -8988,7 +9124,7 @@ async function autoTitle2(d, convId, prevMessages, assistantReply, target) {
|
|
|
8988
9124
|
}
|
|
8989
9125
|
|
|
8990
9126
|
// src/engines/scan.ts
|
|
8991
|
-
import { dirname as
|
|
9127
|
+
import { dirname as dirname6 } from "path";
|
|
8992
9128
|
import { existsSync as existsSync16, statSync as statSync4 } from "fs";
|
|
8993
9129
|
var serverBinName = process.platform === "win32" ? "llama-server.exe" : "llama-server";
|
|
8994
9130
|
function skipScanDir(name) {
|
|
@@ -9024,7 +9160,7 @@ var GENERIC_DIRS = /* @__PURE__ */ new Set([
|
|
|
9024
9160
|
"osx"
|
|
9025
9161
|
]);
|
|
9026
9162
|
function meaningfulFolder(binPath) {
|
|
9027
|
-
const parts =
|
|
9163
|
+
const parts = dirname6(binPath).split(/[\\/]+/).filter(Boolean);
|
|
9028
9164
|
for (let i = parts.length - 1; i >= 0; i--) {
|
|
9029
9165
|
if (!GENERIC_DIRS.has(parts[i].toLowerCase())) return parts[i];
|
|
9030
9166
|
}
|
|
@@ -9554,7 +9690,7 @@ async function checkBuildPrereqs(toolchainDirs = []) {
|
|
|
9554
9690
|
// src/engines/build-runner.ts
|
|
9555
9691
|
import { execFile as execFile9, spawn as spawn4 } from "child_process";
|
|
9556
9692
|
import { copyFileSync, existsSync as existsSync17, mkdirSync as mkdirSync10, readFileSync as readFileSync8, readdirSync as readdirSync5, rmSync as rmSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
9557
|
-
import { delimiter as delimiter2, dirname as
|
|
9693
|
+
import { delimiter as delimiter2, dirname as dirname7, join as join19 } from "path";
|
|
9558
9694
|
import { promisify as promisify6 } from "util";
|
|
9559
9695
|
var execFileP6 = promisify6(execFile9);
|
|
9560
9696
|
function buildDirName(repoUrl, branch) {
|
|
@@ -9667,7 +9803,7 @@ function cudaSoSourceDirs(env) {
|
|
|
9667
9803
|
}
|
|
9668
9804
|
});
|
|
9669
9805
|
if (!nvccDir) return [];
|
|
9670
|
-
const toolkitRoot =
|
|
9806
|
+
const toolkitRoot = dirname7(nvccDir);
|
|
9671
9807
|
return [join19(toolkitRoot, "lib64"), join19(toolkitRoot, "lib"), join19(toolkitRoot, "targets", "x86_64-linux", "lib")].filter((d) => {
|
|
9672
9808
|
try {
|
|
9673
9809
|
return existsSync17(d);
|
|
@@ -9888,8 +10024,8 @@ async function runBuild(req, hooks, signal) {
|
|
|
9888
10024
|
if (!binPath) {
|
|
9889
10025
|
throw new Error("Build finished but no llama-server binary was found in the output \u2014 see the log above.");
|
|
9890
10026
|
}
|
|
9891
|
-
if (isWindows) copyCudaRuntimeDlls(env,
|
|
9892
|
-
else copyCudaRuntimeLibs(env,
|
|
10027
|
+
if (isWindows) copyCudaRuntimeDlls(env, dirname7(binPath), hooks.log);
|
|
10028
|
+
else copyCudaRuntimeLibs(env, dirname7(binPath), hooks.log);
|
|
9893
10029
|
return { binPath, commit, buildRoot };
|
|
9894
10030
|
}
|
|
9895
10031
|
|
|
@@ -10916,7 +11052,7 @@ function registerApi(app2, d) {
|
|
|
10916
11052
|
} catch {
|
|
10917
11053
|
return err2(c, 400, "fs_read_failed", "Could not read that folder (permission denied or not a directory).");
|
|
10918
11054
|
}
|
|
10919
|
-
const parentDir = real === home ? null :
|
|
11055
|
+
const parentDir = real === home ? null : dirname8(real);
|
|
10920
11056
|
const parent = parentDir && isWithinHome(parentDir, home) ? parentDir : null;
|
|
10921
11057
|
return c.json({ path: real, parent, entries });
|
|
10922
11058
|
});
|
|
@@ -11484,8 +11620,8 @@ function registerApi(app2, d) {
|
|
|
11484
11620
|
app2.post("/api/v1/downloads", async (c) => {
|
|
11485
11621
|
const b = await body2(c);
|
|
11486
11622
|
try {
|
|
11487
|
-
const
|
|
11488
|
-
return c.json(
|
|
11623
|
+
const recs = await d.downloads.enqueue(b);
|
|
11624
|
+
return c.json({ downloads: recs }, 202);
|
|
11489
11625
|
} catch (e) {
|
|
11490
11626
|
return dlErr(c, e);
|
|
11491
11627
|
}
|
|
@@ -12525,7 +12661,7 @@ async function recordOpenAiStreamUsage(d, body4) {
|
|
|
12525
12661
|
|
|
12526
12662
|
// src/server.ts
|
|
12527
12663
|
setGlobalDispatcher(new Agent({ keepAliveMaxTimeout: 6e4, connections: 10 }));
|
|
12528
|
-
var WEB_ROOT = join23(
|
|
12664
|
+
var WEB_ROOT = join23(dirname9(fileURLToPath(import.meta.url)), "webdist");
|
|
12529
12665
|
function createApp(d) {
|
|
12530
12666
|
const app2 = new Hono();
|
|
12531
12667
|
app2.use(
|
|
@@ -12583,7 +12719,7 @@ function contentType(file) {
|
|
|
12583
12719
|
// src/cli.ts
|
|
12584
12720
|
var version = "0.1.1";
|
|
12585
12721
|
try {
|
|
12586
|
-
const pkgPath = join24(
|
|
12722
|
+
const pkgPath = join24(dirname10(fileURLToPath2(import.meta.url)), "..", "package.json");
|
|
12587
12723
|
version = JSON.parse(readFileSync11(pkgPath, "utf8")).version ?? version;
|
|
12588
12724
|
} catch {
|
|
12589
12725
|
}
|
|
@@ -12623,7 +12759,7 @@ if (argv[0] === "launch") {
|
|
|
12623
12759
|
const launchConfigPath = argValue("--config", defaultConfigPath());
|
|
12624
12760
|
const explicitPort = Number(argValue("--port", "")) || void 0;
|
|
12625
12761
|
const port2 = resolveDaemonPort(
|
|
12626
|
-
|
|
12762
|
+
dirname10(launchConfigPath),
|
|
12627
12763
|
explicitPort,
|
|
12628
12764
|
configuredDaemonPort(launchConfigPath)
|
|
12629
12765
|
);
|
|
@@ -12697,7 +12833,12 @@ void scanner.rescan();
|
|
|
12697
12833
|
var hashes = new HashStore(store.dir());
|
|
12698
12834
|
var db = new ConversationStore(store.dir());
|
|
12699
12835
|
var hf = new HfClient(() => store.snapshot().hf.token, version);
|
|
12700
|
-
var downloads = new DownloadManager(
|
|
12836
|
+
var downloads = new DownloadManager(
|
|
12837
|
+
store,
|
|
12838
|
+
() => void scanner.rescan(),
|
|
12839
|
+
() => hf.authHeaders(),
|
|
12840
|
+
(repo, rfilename, rev) => hf.expandModelFiles(repo, rfilename, rev)
|
|
12841
|
+
);
|
|
12701
12842
|
var bench = new BenchRunner(manager, store, scanner, registry, version, hf);
|
|
12702
12843
|
var comfy = new ComfyGuard(store, manager);
|
|
12703
12844
|
var modelRouter = new ModelRouter(store, registry, manager, scanner, comfy);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e}from"./radix-9WP-OkMY.js";import{r as u}from"./vendor-W5GIuFcv.js";import{a as w,d as k,A as T,u as v,L as h,e as S,c as b,f as C,g as q,X as A}from"./index-
|
|
1
|
+
import{j as e}from"./radix-9WP-OkMY.js";import{r as u}from"./vendor-W5GIuFcv.js";import{a as w,d as k,A as T,u as v,L as h,e as S,c as b,f as C,g as q,X as A}from"./index-CjGc01NF.js";import{P as N}from"./plus-3YM8ko_h.js";import{C as y}from"./circle-x-CrulSiVz.js";import{C as O}from"./circle-check-C9nr7e7f.js";import{W as E}from"./wrench-2q903Ji-.js";const _=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}]],D=w("circle-dot",_);const L=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 6v6l4 2",key:"mmk7yg"}]],P=w("clock",L),p={all:["agent-runs"],list:()=>[...p.all,"list"],detail:t=>[...p.all,"detail",t]};async function g(t,a){const l={Accept:"application/json",...k(),...a?.headers??{}},i=await fetch(t,{...a,headers:l});if(i.status===204)return;const r=await i.text(),o=r?JSON.parse(r):void 0;if(!i.ok){const d=o;throw new T(d?.error?.code??"http_error",d?.error?.message??`Request failed with status ${i.status}.`,i.status)}return o}async function $(){return g("/api/v1/agents/runs")}async function I(t){return g(`/api/v1/agents/runs/${t}`)}async function Q(t){return g("/api/v1/agents/runs",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}async function K(t){await g(`/api/v1/agents/runs/${t}`,{method:"DELETE"})}async function*F(t,a=0,l){const i=`/api/v1/agents/runs/${t}/stream?fromSeq=${a}`,r=await fetch(i,{headers:{Accept:"text/event-stream",...k()},signal:l});if(!r.ok||!r.body)throw new Error(`Stream error: ${r.status}`);const o=r.body.getReader(),d=new TextDecoder;let m="";try{for(;;){const{done:s,value:x}=await o.read();if(s)break;m+=d.decode(x,{stream:!0});const c=m.split(`
|
|
2
2
|
`);m=c.pop()??"";let n="message";for(const f of c){if(f.startsWith("event: ")){n=f.slice(7).trim();continue}if(f.startsWith("data: ")){const j=f.slice(6).trim();if(j==="[DONE]")return;try{const R=JSON.parse(j);if(yield{event:n,data:R},n==="done"||n==="error")return}catch{}n="message"}}}}finally{o.cancel()}}const J={queued:e.jsx(P,{size:12}),running:e.jsx(h,{size:12,className:"animate-spin"}),done:e.jsx(O,{size:12}),failed:e.jsx(y,{size:12}),cancelled:e.jsx(y,{size:12}),interrupted:e.jsx(y,{size:12})},M={queued:"text-muted",running:"text-accent",done:"text-green-500",failed:"text-red-500",cancelled:"text-muted",interrupted:"text-orange-500"};function z({status:t}){return e.jsxs("span",{className:b("flex items-center gap-1 text-[11px] capitalize",M[t]),children:[J[t],t]})}function W(t){const a=Date.now()-new Date(t).getTime();return a<6e4?"just now":a<36e5?`${Math.floor(a/6e4)}m ago`:a<864e5?`${Math.floor(a/36e5)}h ago`:new Date(t).toLocaleDateString()}const U=[{id:"web_search",label:"Web search",hint:"Search the web (needs a provider configured in Settings)"},{id:"fetch_url",label:"Fetch URL",hint:"Read the contents of a web page"},{id:"run_code",label:"Run code",hint:"Execute JS in a sandbox — no mid-run confirmation, opt in only if you trust the task"}];function B({onClose:t,onCreated:a}){const[l,i]=u.useState(""),[r,o]=u.useState(""),[d,m]=u.useState({web_search:!0,fetch_url:!0,run_code:!1}),s=C(),x=n=>m(f=>({...f,[n]:!f[n]})),c=q({mutationFn:()=>Q({title:l.slice(0,60)||"Agent run",systemPrompt:r||void 0,userMessage:l,allowedTools:Object.entries(d).filter(([,n])=>n).map(([n])=>n)}),onSuccess:n=>{s.invalidateQueries({queryKey:p.list()}),a(n.id),t()}});return e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40",onClick:t,children:e.jsxs("div",{className:"w-full max-w-lg rounded-xl border border-border bg-panel p-5 shadow-xl",onClick:n=>n.stopPropagation(),children:[e.jsxs("div",{className:"mb-4 flex items-center justify-between",children:[e.jsx("h2",{className:"text-[15px] font-semibold",children:"New agent run"}),e.jsx("button",{type:"button",onClick:t,className:"text-faint hover:text-ink",children:e.jsx(A,{size:16})})]}),e.jsx("label",{className:"mb-1 block text-[12px] text-muted",children:"Task"}),e.jsx("textarea",{autoFocus:!0,value:l,onChange:n=>i(n.target.value),placeholder:"Describe what the agent should do…",rows:4,className:"mb-3 w-full resize-none rounded-lg border border-border bg-input px-3 py-2 text-[13px] text-ink placeholder:text-faint focus:border-accent focus:outline-none"}),e.jsxs("label",{className:"mb-1 block text-[12px] text-muted",children:["System prompt ",e.jsx("span",{className:"text-faint",children:"(optional)"})]}),e.jsx("textarea",{value:r,onChange:n=>o(n.target.value),placeholder:"You are a helpful assistant…",rows:2,className:"mb-4 w-full resize-none rounded-lg border border-border bg-input px-3 py-2 text-[13px] text-ink placeholder:text-faint focus:border-accent focus:outline-none"}),e.jsx("label",{className:"mb-1.5 block text-[12px] text-muted",children:"Tools"}),e.jsx("div",{className:"mb-4 space-y-1.5",children:U.map(n=>e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 rounded-lg border border-border px-2.5 py-2 hover:bg-panel-2",children:[e.jsx("input",{type:"checkbox",checked:d[n.id],onChange:()=>x(n.id),className:"mt-0.5 accent-[var(--accent)]"}),e.jsxs("span",{className:"min-w-0",children:[e.jsx("span",{className:"block text-[13px] font-medium text-ink",children:n.label}),e.jsx("span",{className:"block text-[11px] leading-snug text-faint",children:n.hint})]})]},n.id))}),e.jsxs("div",{className:"flex items-center justify-end gap-2",children:[e.jsx("button",{type:"button",onClick:t,className:"rounded-lg px-3 py-1.5 text-[13px] text-muted hover:text-ink",children:"Cancel"}),e.jsxs("button",{type:"button",disabled:!l.trim()||c.isPending,onClick:()=>c.mutate(),className:"flex items-center gap-1.5 rounded-lg bg-accent px-3 py-1.5 text-[13px] font-medium text-white disabled:opacity-50",children:[c.isPending?e.jsx(h,{size:13,className:"animate-spin"}):e.jsx(S,{size:13}),"Run agent"]})]}),c.isError&&e.jsx("p",{className:"mt-2 text-[12px]",style:{color:"var(--err)"},children:c.error.message})]})})}function X({call:t}){const[a,l]=u.useState(!1),i=typeof t.args?.query=="string"?t.args.query:typeof t.args?.url=="string"?t.args.url:JSON.stringify(t.args??{});return e.jsxs("div",{className:"w-full max-w-[85%] rounded-lg border border-border bg-panel px-2.5 py-1.5 text-[12px]",children:[e.jsxs("button",{type:"button",onClick:()=>l(r=>!r),className:"flex w-full items-center gap-1.5 text-left text-muted hover:text-ink",children:[e.jsx(E,{size:12,className:"shrink-0 text-accent"}),e.jsx("span",{className:"font-medium text-ink",children:t.name}),e.jsx("span",{className:"truncate text-faint",children:i})]}),a&&e.jsx("pre",{className:"mt-1.5 max-h-48 overflow-auto whitespace-pre-wrap break-words rounded bg-panel-2 p-2 text-[11px] text-muted",children:t.result})]})}function H({run:t}){const a=C(),l=u.useRef(null),[i,r]=u.useState(""),o=u.useRef(null),d=q({mutationFn:()=>K(t.id),onSuccess:()=>{a.invalidateQueries({queryKey:p.list()})}});u.useEffect(()=>{if(t.status!=="running"&&t.status!=="queued")return;const s=new AbortController;l.current=s;let x="";return(async()=>{try{for await(const c of F(t.id,0,s.signal))if(c.event==="delta")x+=c.data.delta,r(x);else if(c.event==="done"||c.event==="error"){r(""),a.invalidateQueries({queryKey:p.detail(t.id)}),a.invalidateQueries({queryKey:p.list()});break}}catch{}})(),()=>{s.abort(),l.current=null}},[t.id,t.status,a]),u.useEffect(()=>{o.current?.scrollIntoView({behavior:"smooth"})},[i,t.messages?.length]);const m=t.messages??[];return e.jsxs("div",{className:"flex h-full flex-col",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-border px-4 py-2.5",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[14px] font-medium text-ink",children:t.title}),e.jsx(z,{status:t.status})]}),(t.status==="running"||t.status==="queued")&&e.jsx("button",{type:"button",onClick:()=>d.mutate(),className:"rounded-lg border border-border px-3 py-1 text-[12px] text-muted transition-colors hover:border-red-400 hover:text-red-500",children:d.isPending?e.jsx(h,{size:12,className:"inline animate-spin"}):"Cancel"})]}),e.jsxs("div",{className:"flex-1 space-y-3 overflow-y-auto p-4",children:[m.map(s=>e.jsxs("div",{className:b("flex flex-col gap-1.5",s.role==="user"?"items-end":"items-start"),children:[s.toolCalls?.map(x=>e.jsx(X,{call:x},x.id)),(s.content||s.role==="user"||t.status==="running")&&e.jsx("div",{className:b("max-w-[85%] rounded-xl px-3 py-2 text-[13px] leading-relaxed",s.role==="user"?"bg-accent/15 text-ink":"bg-panel-2 text-ink"),children:e.jsx("p",{className:"whitespace-pre-wrap break-words",children:s.content||(s.role==="assistant"&&t.status==="running"?"…":"")})})]},s.id)),i&&e.jsx("div",{className:"flex gap-2",children:e.jsx("div",{className:"max-w-[85%] rounded-xl bg-panel-2 px-3 py-2 text-[13px] leading-relaxed text-ink",children:e.jsx("p",{className:"whitespace-pre-wrap break-words",children:i})})}),t.error&&e.jsxs("div",{className:"rounded-lg bg-red-500/10 px-3 py-2 text-[12px] text-red-500",children:["Error: ",t.error]}),e.jsx("div",{ref:o})]})]})}function ne(){const[t,a]=u.useState(null),[l,i]=u.useState(!1),r=v({queryKey:p.list(),queryFn:$,refetchInterval:s=>(s.state.data??[]).some(n=>n.status==="queued"||n.status==="running")?3e3:!1}),o=v({queryKey:p.detail(t??""),queryFn:()=>I(t),enabled:!!t,refetchInterval:s=>{const x=s.state.data;return x?.status==="queued"||x?.status==="running"?5e3:!1}}),d=r.data??[],m=o.data;return e.jsxs("div",{className:"flex h-full",children:[e.jsxs("div",{className:"flex w-64 shrink-0 flex-col border-r border-border",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-border px-3 py-2.5",children:[e.jsx("span",{className:"text-[13px] font-semibold text-ink",children:"Agents"}),e.jsxs("button",{type:"button",title:"New agent run",onClick:()=>i(!0),className:"flex items-center gap-1 rounded-lg bg-accent px-2 py-1 text-[11px] font-medium text-white transition-opacity hover:opacity-90",children:[e.jsx(N,{size:11})," New"]})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto",children:[r.isLoading&&e.jsx("div",{className:"flex h-20 items-center justify-center",children:e.jsx(h,{size:16,className:"animate-spin text-faint"})}),d.length===0&&!r.isLoading&&e.jsxs("div",{className:"px-4 py-8 text-center",children:[e.jsx(S,{size:28,className:"mx-auto mb-2 text-faint"}),e.jsx("p",{className:"text-[12px] text-muted",children:"No agent runs yet."}),e.jsx("p",{className:"mt-0.5 text-[11px] text-faint",children:"Click “New” to start one."})]}),d.map(s=>e.jsxs("button",{type:"button",onClick:()=>a(s.id),className:b("w-full border-b border-border px-3 py-2.5 text-left transition-colors hover:bg-panel-2",t===s.id&&"bg-panel-2"),children:[e.jsx("p",{className:"mb-0.5 truncate text-[13px] font-medium text-ink",children:s.title}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(z,{status:s.status}),e.jsx("span",{className:"text-[10px] text-faint",children:W(s.createdAt)})]})]},s.id))]})]}),e.jsx("div",{className:"flex min-w-0 flex-1 flex-col",children:t?o.isLoading?e.jsx("div",{className:"flex h-full items-center justify-center",children:e.jsx(h,{size:20,className:"animate-spin text-faint"})}):m?e.jsx(H,{run:m}):null:e.jsxs("div",{className:"flex h-full flex-col items-center justify-center text-center",children:[e.jsx(D,{size:32,className:"mb-3 text-faint"}),e.jsx("p",{className:"text-[14px] font-medium text-ink",children:"Select a run"}),e.jsx("p",{className:"mt-1 text-[12px] text-muted",children:"or create a new agent run to get started"}),e.jsxs("button",{type:"button",onClick:()=>i(!0),className:"mt-4 flex items-center gap-1.5 rounded-lg bg-accent px-3 py-1.5 text-[13px] font-medium text-white hover:opacity-90",children:[e.jsx(N,{size:13})," New agent run"]})]})}),l&&e.jsx(B,{onClose:()=>i(!1),onCreated:s=>{a(s),r.refetch()}})]})}export{ne as AgentsScreen};
|