isorite 0.5.0 → 0.7.0
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 +4 -3
- package/dist/main.js +466 -99
- package/docs/setup-for-agents.md +5 -1
- package/docs/setup.md +18 -4
- package/package.json +1 -1
- package/plugin/server.ts +1 -1
- package/web/_app/immutable/assets/0.BKnIhFWM.css +1 -0
- package/web/_app/immutable/assets/NoteEditor.8wWtZK_A.css +1 -0
- package/web/_app/immutable/chunks/0M1khDs2.js +2 -0
- package/web/_app/immutable/chunks/2b4Gb5sE.js +1 -0
- package/web/_app/immutable/chunks/62dhNtvj.js +673 -0
- package/web/_app/immutable/chunks/84nbDH0I.js +1 -0
- package/web/_app/immutable/chunks/8H7HVH4w.js +1 -0
- package/web/_app/immutable/chunks/8wVGBFSs.js +45 -0
- package/web/_app/immutable/chunks/AF4Hwhju.js +1 -0
- package/web/_app/immutable/chunks/{C1xA2mdF.js → B3dCo0od.js} +1 -1
- package/web/_app/immutable/chunks/B45xgp2S.js +1 -0
- package/web/_app/immutable/chunks/B78wSQJQ.js +1 -0
- package/web/_app/immutable/chunks/B7GA1FFB.js +1 -0
- package/web/_app/immutable/chunks/B85D_PlV.js +5 -0
- package/web/_app/immutable/chunks/B9NhS7L_.js +1 -0
- package/web/_app/immutable/chunks/B9YLdnR_.js +1 -0
- package/web/_app/immutable/chunks/BEHa2uNW.js +1 -0
- package/web/_app/immutable/chunks/BEMVcqjs.js +1 -0
- package/web/_app/immutable/chunks/BEjRio4Q.js +1 -0
- package/web/_app/immutable/chunks/BEn0Cwpx.js +1 -0
- package/web/_app/immutable/chunks/BGbXxNFa.js +2 -0
- package/web/_app/immutable/chunks/BGfruO1u.js +1 -0
- package/web/_app/immutable/chunks/BHEdbvp9.js +1 -0
- package/web/_app/immutable/chunks/BKJx4mf9.js +1 -0
- package/web/_app/immutable/chunks/BL3mLym4.js +5 -0
- package/web/_app/immutable/chunks/BLp5-jwC.js +1 -0
- package/web/_app/immutable/chunks/BN6xhG8e.js +1 -0
- package/web/_app/immutable/chunks/BNjH_DUx.js +1 -0
- package/web/_app/immutable/chunks/BOVgKnng.js +1 -0
- package/web/_app/immutable/chunks/BPTmHmyx.js +1 -0
- package/web/_app/immutable/chunks/BPW39P52.js +1 -0
- package/web/_app/immutable/chunks/BRKS-CCX.js +5 -0
- package/web/_app/immutable/chunks/BRsoo5mP.js +1 -0
- package/web/_app/immutable/chunks/BSzj6Xyz.js +1 -0
- package/web/_app/immutable/chunks/BUZw3wgd.js +1 -0
- package/web/_app/immutable/chunks/BV0c4Hdn.js +1 -0
- package/web/_app/immutable/chunks/BVIeWmwY.js +1 -0
- package/web/_app/immutable/chunks/BWonG38x.js +1 -0
- package/web/_app/immutable/chunks/BXxG0Yva.js +1 -0
- package/web/_app/immutable/chunks/BZmnMkFw.js +1 -0
- package/web/_app/immutable/chunks/BZyAS7I4.js +1 -0
- package/web/_app/immutable/chunks/B_1Jd8HM.js +1 -0
- package/web/_app/immutable/chunks/Bbt0DTmZ.js +1 -0
- package/web/_app/immutable/chunks/Bffqt7tx.js +9 -0
- package/web/_app/immutable/chunks/BflJGmDX.js +1 -0
- package/web/_app/immutable/chunks/BgczuZKA.js +4 -0
- package/web/_app/immutable/chunks/BguXRgvc.js +2 -0
- package/web/_app/immutable/chunks/BkSkiI9m.js +1 -0
- package/web/_app/immutable/chunks/BoNMG-YE.js +1 -0
- package/web/_app/immutable/chunks/Bq5hCPe1.js +2 -0
- package/web/_app/immutable/chunks/BrGmYbJT.js +2 -0
- package/web/_app/immutable/chunks/BrqJKzQU.js +2 -0
- package/web/_app/immutable/chunks/Brxvm_b1.js +4 -0
- package/web/_app/immutable/chunks/BsLNwr5A.js +2 -0
- package/web/_app/immutable/chunks/Bsdphhtm.js +1 -0
- package/web/_app/immutable/chunks/Bt6PBEDR.js +1 -0
- package/web/_app/immutable/chunks/BthdM_4N.js +1 -0
- package/web/_app/immutable/chunks/Bu8mWwtz.js +1 -0
- package/web/_app/immutable/chunks/BunrC_8P.js +4 -0
- package/web/_app/immutable/chunks/BwTct_5T.js +1 -0
- package/web/_app/immutable/chunks/BxTFtVqu.js +13 -0
- package/web/_app/immutable/chunks/BzI3yqBQ.js +3 -0
- package/web/_app/immutable/chunks/C-dfcq9W.js +7 -0
- package/web/_app/immutable/chunks/C2OyD2Y6.js +1 -0
- package/web/_app/immutable/chunks/C2dAgh7P.js +1 -0
- package/web/_app/immutable/chunks/C357ku5U.js +1 -0
- package/web/_app/immutable/chunks/C4jVoHG1.js +1 -0
- package/web/_app/immutable/chunks/C6Btt2Xs.js +1 -0
- package/web/_app/immutable/chunks/C9zpzAZU.js +1 -0
- package/web/_app/immutable/chunks/CDAjAYjS.js +1 -0
- package/web/_app/immutable/chunks/CETSRtnV.js +2 -0
- package/web/_app/immutable/chunks/CEufJyoj.js +1 -0
- package/web/_app/immutable/chunks/CHbKgHy3.js +1 -0
- package/web/_app/immutable/chunks/CKSRTWxH.js +2 -0
- package/web/_app/immutable/chunks/CLVUDRRk.js +2 -0
- package/web/_app/immutable/chunks/CLVgA2GD.js +1 -0
- package/web/_app/immutable/chunks/CO2Y5y64.js +2 -0
- package/web/_app/immutable/chunks/CPF5uNy5.js +1 -0
- package/web/_app/immutable/chunks/CQadubS9.js +2 -0
- package/web/_app/immutable/chunks/CQfgDRfP.js +1 -0
- package/web/_app/immutable/chunks/CRIvE37b.js +2 -0
- package/web/_app/immutable/chunks/CRzLMJPS.js +1 -0
- package/web/_app/immutable/chunks/CTIncYxj.js +1 -0
- package/web/_app/immutable/chunks/CTscJvp6.js +2 -0
- package/web/_app/immutable/chunks/CUpZ7dEl.js +1 -0
- package/web/_app/immutable/chunks/CVbgxz8Y.js +1 -0
- package/web/_app/immutable/chunks/CWmfjLLv.js +1 -0
- package/web/_app/immutable/chunks/CX-xckzu.js +4 -0
- package/web/_app/immutable/chunks/CXXgtw0p.js +1 -0
- package/web/_app/immutable/chunks/CYNZ4aV_.js +1 -0
- package/web/_app/immutable/chunks/CZ8wTb_M.js +8 -0
- package/web/_app/immutable/chunks/C_eisQCp.js +1 -0
- package/web/_app/immutable/chunks/CdY1YAit.js +2 -0
- package/web/_app/immutable/chunks/Ce_hS451.js +1 -0
- package/web/_app/immutable/chunks/CgA1WoRv.js +1 -0
- package/web/_app/immutable/chunks/CggtIYAM.js +1 -0
- package/web/_app/immutable/chunks/ChfNwlCY.js +1 -0
- package/web/_app/immutable/chunks/ChtP43wD.js +1 -0
- package/web/_app/immutable/chunks/CiVjR8gq.js +3 -0
- package/web/_app/immutable/chunks/CjI9Y6RI.js +1 -0
- package/web/_app/immutable/chunks/ClOhbFR7.js +1 -0
- package/web/_app/immutable/chunks/Cn-vtDpV.js +1 -0
- package/web/_app/immutable/chunks/CnaYoQib.js +1 -0
- package/web/_app/immutable/chunks/Co3PbHdE.js +1 -0
- package/web/_app/immutable/chunks/CoyQYage.js +1 -0
- package/web/_app/immutable/chunks/Cp5ooBfH.js +1 -0
- package/web/_app/immutable/chunks/Cp_JbuGh.js +2 -0
- package/web/_app/immutable/chunks/Cq5C893B.js +1 -0
- package/web/_app/immutable/chunks/CqHI_q-D.js +1 -0
- package/web/_app/immutable/chunks/CqftRsjg.js +1 -0
- package/web/_app/immutable/chunks/Cshzl8qu.js +1 -0
- package/web/_app/immutable/chunks/Cx3rYX2g.js +1 -0
- package/web/_app/immutable/chunks/CxDmU1I4.js +1 -0
- package/web/_app/immutable/chunks/CygcOYNo.js +1 -0
- package/web/_app/immutable/chunks/CzMBulkp.js +1 -0
- package/web/_app/immutable/chunks/D1ij_kNL.js +1 -0
- package/web/_app/immutable/chunks/D2Q8cUp1.js +1 -0
- package/web/_app/immutable/chunks/D4SKbj9K.js +6 -0
- package/web/_app/immutable/chunks/D4c0_ac3.js +1 -0
- package/web/_app/immutable/chunks/D4fLEcOQ.js +8 -0
- package/web/_app/immutable/chunks/D5EjA2JK.js +1 -0
- package/web/_app/immutable/chunks/D5jiJvOU.js +1 -0
- package/web/_app/immutable/chunks/D6QVYUu9.js +1 -0
- package/web/_app/immutable/chunks/D6RzAl64.js +1 -0
- package/web/_app/immutable/chunks/D6Vzg2HD.js +6 -0
- package/web/_app/immutable/chunks/D8WpQPaX.js +1 -0
- package/web/_app/immutable/chunks/D9f6rSsU.js +1 -0
- package/web/_app/immutable/chunks/D9qYN0V5.js +1 -0
- package/web/_app/immutable/chunks/DCl_8MCW.js +1 -0
- package/web/_app/immutable/chunks/DECNRCf2.js +1 -0
- package/web/_app/immutable/chunks/DHLQUbN7.js +1 -0
- package/web/_app/immutable/chunks/DHfKGggc.js +1 -0
- package/web/_app/immutable/chunks/DIGvaVgJ.js +2 -0
- package/web/_app/immutable/chunks/DKB6_1j6.js +1 -0
- package/web/_app/immutable/chunks/DKNOMI4Y.js +1 -0
- package/web/_app/immutable/chunks/DLa7NjNO.js +1 -0
- package/web/_app/immutable/chunks/DMAYXnpq.js +13 -0
- package/web/_app/immutable/chunks/DNzd62Q_.js +1 -0
- package/web/_app/immutable/chunks/DPCEkfD_.js +1 -0
- package/web/_app/immutable/chunks/DTF72YNO.js +1 -0
- package/web/_app/immutable/chunks/DTdlqvEb.js +12 -0
- package/web/_app/immutable/chunks/DTszW0OQ.js +1 -0
- package/web/_app/immutable/chunks/DWkQyNnU.js +1 -0
- package/web/_app/immutable/chunks/DXiLM1-G.js +1 -0
- package/web/_app/immutable/chunks/DZYaqCwi.js +1 -0
- package/web/_app/immutable/chunks/D_19S_Iu.js +1 -0
- package/web/_app/immutable/chunks/D_aBVMvK.js +11 -0
- package/web/_app/immutable/chunks/DciMwFJ1.js +1 -0
- package/web/_app/immutable/chunks/De_zI4J0.js +1 -0
- package/web/_app/immutable/chunks/DgAKBe0r.js +1 -0
- package/web/_app/immutable/chunks/DjVOVvNP.js +1 -0
- package/web/_app/immutable/chunks/DkvLWnSd.js +1 -0
- package/web/_app/immutable/chunks/Dp_KdFgX.js +1 -0
- package/web/_app/immutable/chunks/Ds8Pkiic.js +1 -0
- package/web/_app/immutable/chunks/{D4Jg9Smi.js → DsBJWNWe.js} +1 -1
- package/web/_app/immutable/chunks/DtbnBenW.js +1 -0
- package/web/_app/immutable/chunks/DuQMFDvy.js +1 -0
- package/web/_app/immutable/chunks/DwuoZtxw.js +1 -0
- package/web/_app/immutable/chunks/DxLkKbK2.js +5 -0
- package/web/_app/immutable/chunks/Dz-mlCAC.js +1 -0
- package/web/_app/immutable/chunks/EQVRMnZU.js +2 -0
- package/web/_app/immutable/chunks/GwMd3KmG.js +1 -0
- package/web/_app/immutable/chunks/K_3X9lyC.js +23 -0
- package/web/_app/immutable/chunks/PPSTk1ow.js +1 -0
- package/web/_app/immutable/chunks/S7ucR4pR.js +1 -0
- package/web/_app/immutable/chunks/TuFg0KqZ.js +4 -0
- package/web/_app/immutable/chunks/VlUiK86J.js +1 -0
- package/web/_app/immutable/chunks/WBFggAXs.js +1 -0
- package/web/_app/immutable/chunks/_rHYGvON.js +1 -0
- package/web/_app/immutable/chunks/bcQIrid8.js +1 -0
- package/web/_app/immutable/chunks/gfMMiTNf.js +1 -0
- package/web/_app/immutable/chunks/gpDK5sbt.js +1 -0
- package/web/_app/immutable/chunks/kS471soi.js +1 -0
- package/web/_app/immutable/chunks/mbLqADPr.js +1 -0
- package/web/_app/immutable/chunks/nMarIA7J.js +3 -0
- package/web/_app/immutable/chunks/oBAE_ms0.js +1 -0
- package/web/_app/immutable/chunks/p9eYesGn.js +1 -0
- package/web/_app/immutable/chunks/qgSLujhx.js +1 -0
- package/web/_app/immutable/chunks/spNKdrPy.js +3 -0
- package/web/_app/immutable/chunks/tLWDfEkZ.js +1 -0
- package/web/_app/immutable/chunks/u8YMmbU0.js +1 -0
- package/web/_app/immutable/chunks/vk5Wkr31.js +1 -0
- package/web/_app/immutable/chunks/wCTXGvXm.js +2 -0
- package/web/_app/immutable/chunks/wj7G6pU5.js +5 -0
- package/web/_app/immutable/chunks/xvONpD0Z.js +1 -0
- package/web/_app/immutable/chunks/yxd8jSz3.js +1 -0
- package/web/_app/immutable/entry/app.Dt52eXt2.js +2 -0
- package/web/_app/immutable/entry/start.mjqIl2PI.js +1 -0
- package/web/_app/immutable/nodes/0.DIhIOwuo.js +26 -0
- package/web/_app/immutable/nodes/1.B5qkcrvB.js +1 -0
- package/web/_app/immutable/nodes/2.CK3q7brK.js +1 -0
- package/web/_app/immutable/nodes/3.CrUewa31.js +1 -0
- package/web/_app/immutable/nodes/4.BhNJiqT-.js +2 -0
- package/web/_app/immutable/nodes/5.DXTbGemS.js +5 -0
- package/web/_app/immutable/nodes/6.BZWaFgFP.js +1 -0
- package/web/_app/immutable/nodes/7.BxQaF3lD.js +1 -0
- package/web/_app/version.json +1 -1
- package/web/index.html +22 -18
- package/web/_app/immutable/assets/0.DNvf5eFW.css +0 -1
- package/web/_app/immutable/assets/NoteEditor.BCjOff8L.css +0 -1
- package/web/_app/immutable/chunks/-7PsQOY4.js +0 -3
- package/web/_app/immutable/chunks/B9vLu37F.js +0 -1
- package/web/_app/immutable/chunks/BAVeNK5c.js +0 -20
- package/web/_app/immutable/chunks/CQqNkDRK.js +0 -1
- package/web/_app/immutable/chunks/Cs9sQ8Oa.js +0 -1
- package/web/_app/immutable/chunks/DG2llOXe.js +0 -666
- package/web/_app/immutable/chunks/DqVFJWdL.js +0 -1
- package/web/_app/immutable/chunks/HCrXwzvD.js +0 -1
- package/web/_app/immutable/chunks/MjV3DsQW.js +0 -1
- package/web/_app/immutable/chunks/uID4v_1K.js +0 -1
- package/web/_app/immutable/entry/app.CR9ubCCo.js +0 -2
- package/web/_app/immutable/entry/start.D4DOZCP6.js +0 -1
- package/web/_app/immutable/nodes/0.BYXv43VP.js +0 -26
- package/web/_app/immutable/nodes/1.DGP71l35.js +0 -1
- package/web/_app/immutable/nodes/2.CxspTYRG.js +0 -1
- package/web/_app/immutable/nodes/3.ZTzs6oEI.js +0 -1
- package/web/_app/immutable/nodes/4.CeIGu2c2.js +0 -2
- package/web/_app/immutable/nodes/5.k1DmYkGm.js +0 -5
- package/web/_app/immutable/nodes/6.Q0yGno8p.js +0 -1
- package/web/_app/immutable/nodes/7.BJRu312o.js +0 -1
package/dist/main.js
CHANGED
|
@@ -62,7 +62,7 @@ function cmpTuple(a, b) {
|
|
|
62
62
|
}
|
|
63
63
|
/** A frontmatter value as text: true/false, whole-number floats without ".0",
|
|
64
64
|
* nothing for null. */
|
|
65
|
-
function text(value) {
|
|
65
|
+
function text$1(value) {
|
|
66
66
|
if (value === null || value === void 0) return "";
|
|
67
67
|
if (typeof value === "boolean") return value ? "true" : "false";
|
|
68
68
|
return String(value);
|
|
@@ -450,14 +450,14 @@ function kindOf(type) {
|
|
|
450
450
|
}
|
|
451
451
|
/** Note ids from frontmatter: a list, or one; `/a/b.md` and `a/b` alike. */
|
|
452
452
|
function idsOf(raw) {
|
|
453
|
-
return (typeof raw === "string" ? [raw] : Array.isArray(raw) ? raw : []).map((t) => text(t).trim().replace(/^\/+/, "").replace(/\.md$/, "")).filter(Boolean);
|
|
453
|
+
return (typeof raw === "string" ? [raw] : Array.isArray(raw) ? raw : []).map((t) => text$1(t).trim().replace(/^\/+/, "").replace(/\.md$/, "")).filter(Boolean);
|
|
454
454
|
}
|
|
455
455
|
/** When it was resolved or solved, as written (a date), or null while it is open. */
|
|
456
456
|
function doneOf(meta, kind) {
|
|
457
457
|
if (kind === "note") return null;
|
|
458
458
|
const v = meta[DONE[kind]];
|
|
459
459
|
if (v === null || v === void 0 || v === false || v === "") return null;
|
|
460
|
-
return v instanceof Date ? v.toISOString().slice(0, 10) : text(v);
|
|
460
|
+
return v instanceof Date ? v.toISOString().slice(0, 10) : text$1(v);
|
|
461
461
|
}
|
|
462
462
|
/** The embeds in a note's body, in order, as written (a name, or a path). */
|
|
463
463
|
function embedRefs(body) {
|
|
@@ -575,17 +575,17 @@ var init_bundle = __esmMin((() => {
|
|
|
575
575
|
}
|
|
576
576
|
get type() {
|
|
577
577
|
const t = this.meta.type;
|
|
578
|
-
return t === null || t === void 0 || t === "" ? "" : text(t);
|
|
578
|
+
return t === null || t === void 0 || t === "" ? "" : text$1(t);
|
|
579
579
|
}
|
|
580
580
|
get title() {
|
|
581
581
|
const t = this.meta.title;
|
|
582
|
-
if (t) return text(t);
|
|
582
|
+
if (t) return text$1(t);
|
|
583
583
|
const stem = basename$1(this.id);
|
|
584
584
|
const words = strip(stem.replaceAll("-", " ").replaceAll("_", " "));
|
|
585
585
|
return words ? words[0].toUpperCase() + words.slice(1).toLowerCase() : stem;
|
|
586
586
|
}
|
|
587
587
|
get description() {
|
|
588
|
-
return text(this.meta.description || "");
|
|
588
|
+
return text$1(this.meta.description || "");
|
|
589
589
|
}
|
|
590
590
|
/** A note, an open question, or a problem (M12). */
|
|
591
591
|
get kind() {
|
|
@@ -598,10 +598,10 @@ var init_bundle = __esmMin((() => {
|
|
|
598
598
|
get tags() {
|
|
599
599
|
let tags = this.meta.tags || [];
|
|
600
600
|
if (typeof tags === "string") tags = [tags];
|
|
601
|
-
return Array.isArray(tags) ? tags.filter((t) => t !== null && t !== void 0).map(text) : [];
|
|
601
|
+
return Array.isArray(tags) ? tags.filter((t) => t !== null && t !== void 0).map(text$1) : [];
|
|
602
602
|
}
|
|
603
603
|
get status() {
|
|
604
|
-
return text(this.meta.status || "stable");
|
|
604
|
+
return text$1(this.meta.status || "stable");
|
|
605
605
|
}
|
|
606
606
|
get generated() {
|
|
607
607
|
return isEntry(this.meta.generated) ? this.meta.generated : {};
|
|
@@ -618,10 +618,10 @@ var init_bundle = __esmMin((() => {
|
|
|
618
618
|
get trust() {
|
|
619
619
|
const events = this.verified;
|
|
620
620
|
if (!events.length) return "unverified";
|
|
621
|
-
return events.some((e) => text(e.by).startsWith("human:")) ? "human-reviewed" : "machine-confirmed";
|
|
621
|
+
return events.some((e) => text$1(e.by).startsWith("human:")) ? "human-reviewed" : "machine-confirmed";
|
|
622
622
|
}
|
|
623
623
|
get lastHumanVerification() {
|
|
624
|
-
const times = this.verified.filter((e) => text(e.by).startsWith("human:")).map((e) => toTime(e.at)).filter((t) => t !== null);
|
|
624
|
+
const times = this.verified.filter((e) => text$1(e.by).startsWith("human:")).map((e) => toTime(e.at)).filter((t) => t !== null);
|
|
625
625
|
return times.length ? Math.max(...times) : null;
|
|
626
626
|
}
|
|
627
627
|
/** Human-reviewed, but meaningfully changed since the latest human check. */
|
|
@@ -1757,14 +1757,14 @@ function parseStudyConfig(source) {
|
|
|
1757
1757
|
}
|
|
1758
1758
|
const base = raw.base ?? {};
|
|
1759
1759
|
const format = raw.format === void 0 ? 1 : Number(raw.format);
|
|
1760
|
-
if (format !== 1) throw new StudyError(`${CONFIG_FILE} is format ${text(raw.format)}; this isorite reads format 1`);
|
|
1760
|
+
if (format !== 1) throw new StudyError(`${CONFIG_FILE} is format ${text$1(raw.format)}; this isorite reads format 1`);
|
|
1761
1761
|
if (typeof base.path !== "string" || !base.path) throw new StudyError(`${CONFIG_FILE} needs [base] path, the knowledge base's folder`);
|
|
1762
1762
|
return {
|
|
1763
1763
|
format,
|
|
1764
1764
|
base: {
|
|
1765
1765
|
path: base.path,
|
|
1766
|
-
remote: text(base.remote ?? ""),
|
|
1767
|
-
root: text(base.root ?? "")
|
|
1766
|
+
remote: text$1(base.remote ?? ""),
|
|
1767
|
+
root: text$1(base.root ?? "")
|
|
1768
1768
|
}
|
|
1769
1769
|
};
|
|
1770
1770
|
}
|
|
@@ -2665,6 +2665,12 @@ something at one place in the note they have open.`;
|
|
|
2665
2665
|
- Maths is LaTeX between dollar signs: $p(x)$ in a line, and a displayed
|
|
2666
2666
|
equation as a paragraph of its own, with $$ alone on the line before it
|
|
2667
2667
|
and $$ alone on the line after.
|
|
2668
|
+
- Code is a fenced block named with its language (\`\`\`python), so it is
|
|
2669
|
+
coloured by it.
|
|
2670
|
+
- A diagram is a fenced block in the language mermaid (Mermaid's syntax), and
|
|
2671
|
+
is drawn as the diagram. Draw one where a picture of how things stand to
|
|
2672
|
+
each other helps, or when asked; not by habit. Maths in a label goes
|
|
2673
|
+
between $$ and $$.
|
|
2668
2674
|
- Ordinary Markdown otherwise. No HTML.`;
|
|
2669
2675
|
FORMAT_RULES = FORMAT;
|
|
2670
2676
|
LOOKUP = (rounds, meaning) => `# Looking things up
|
|
@@ -4365,6 +4371,14 @@ var init_engine = __esmMin((() => {
|
|
|
4365
4371
|
get provider() {
|
|
4366
4372
|
return this.opts.provider ?? OPENROUTER;
|
|
4367
4373
|
}
|
|
4374
|
+
/** Take another provider into use, from the next request on: a setup kept from the settings (M18). What was
|
|
4375
|
+
* asked of the last one (its key's limit, its model's window) is asked again. */
|
|
4376
|
+
setProvider(provider, http) {
|
|
4377
|
+
this.opts.provider = provider;
|
|
4378
|
+
this.opts.http = http;
|
|
4379
|
+
this.window = void 0;
|
|
4380
|
+
this.keyLimit = void 0;
|
|
4381
|
+
}
|
|
4368
4382
|
async settings() {
|
|
4369
4383
|
return {
|
|
4370
4384
|
provider: this.provider.name,
|
|
@@ -4561,7 +4575,7 @@ var init_engine = __esmMin((() => {
|
|
|
4561
4575
|
async begin(req, http, asked) {
|
|
4562
4576
|
if (asked.length > 4e3) throw new EngineError("that is too long to ask at once", 413);
|
|
4563
4577
|
const key = await this.opts.secrets?.get("provider.key") ?? null;
|
|
4564
|
-
if (!key && /\/\/(?:[^/]*\.)?openrouter\.ai\b/.test(this.provider.url)) throw new EngineError("There is no provider key yet. Set one in settings
|
|
4578
|
+
if (!key && /\/\/(?:[^/]*\.)?openrouter\.ai\b/.test(this.provider.url)) throw new EngineError("There is no provider key yet. Set one in Iso's settings.", 409);
|
|
4565
4579
|
const { messages, olds } = await this.compose(req, asked);
|
|
4566
4580
|
const maxTokens = req.form === "insert" ? REPLY_TOKENS : req.form === "note" ? NOTE_TOKENS : Math.max(REPLY_TOKENS, Math.ceil(olds.join("").length / 3) + 1e3);
|
|
4567
4581
|
const most = (req.form === "insert" || req.form === "rewrite") && req.choice?.lookup === false ? 0 : void 0;
|
|
@@ -5506,7 +5520,7 @@ var init_engine = __esmMin((() => {
|
|
|
5506
5520
|
async beginTurn(conversation, req, http) {
|
|
5507
5521
|
const study = this.opts.own;
|
|
5508
5522
|
const key = await this.opts.secrets?.get("provider.key") ?? null;
|
|
5509
|
-
if (!key && /\/\/(?:[^/]*\.)?openrouter\.ai\b/.test(this.provider.url)) throw new EngineError("There is no provider key yet. Set one in settings
|
|
5523
|
+
if (!key && /\/\/(?:[^/]*\.)?openrouter\.ai\b/.test(this.provider.url)) throw new EngineError("There is no provider key yet. Set one in Iso's settings.", 409);
|
|
5510
5524
|
const lookupOn = req.lookup !== false, mode = req.mode;
|
|
5511
5525
|
let w = await this.working({
|
|
5512
5526
|
...req,
|
|
@@ -6702,7 +6716,11 @@ const textTable = (field, v, env) => v === void 0 ? void 0 : Object.fromEntries(
|
|
|
6702
6716
|
* `headers`, `key_header`, `query` and `stream_usage = false`; `max_tokens` for models that think at length; its
|
|
6703
6717
|
* client certificate is `gatewayTls`'s. */
|
|
6704
6718
|
function providerConfig(path = configPath(), env = process.env) {
|
|
6705
|
-
|
|
6719
|
+
return providerFrom(readConfig(path), env);
|
|
6720
|
+
}
|
|
6721
|
+
/** The provider a configuration names, as `providerConfig` reads it from the file: for a setup not yet kept (M18). */
|
|
6722
|
+
function providerFrom(config, env = process.env) {
|
|
6723
|
+
const p = table(config.provider), tiers = table(config.tiers), prices = table(config.prices);
|
|
6706
6724
|
const custom = typeof p.url === "string" && p.url !== "";
|
|
6707
6725
|
const base = custom ? {
|
|
6708
6726
|
name: typeof p.name === "string" && p.name ? p.name : "custom",
|
|
@@ -6733,10 +6751,14 @@ function providerConfig(path = configPath(), env = process.env) {
|
|
|
6733
6751
|
return out;
|
|
6734
6752
|
}
|
|
6735
6753
|
function gatewayTls(path = configPath(), env = process.env) {
|
|
6736
|
-
|
|
6754
|
+
return gatewayTlsFrom(readConfig(path), dirname(path), env);
|
|
6755
|
+
}
|
|
6756
|
+
/** The certificate a configuration names, its paths from `dir` (the configuration's folder). */
|
|
6757
|
+
function gatewayTlsFrom(config, dir, env = process.env) {
|
|
6758
|
+
const p = table(config.provider), files = [];
|
|
6737
6759
|
const file = (field) => {
|
|
6738
|
-
if (p[field] === void 0) return void 0;
|
|
6739
|
-
const named = valueOf(field, p[field], env), at = named.startsWith("~/") ? join(homedir(), named.slice(2)) : resolve(
|
|
6760
|
+
if (p[field] === void 0 || p[field] === "") return void 0;
|
|
6761
|
+
const named = valueOf(field, p[field], env), at = named.startsWith("~/") ? join(homedir(), named.slice(2)) : resolve(dir, named);
|
|
6740
6762
|
if (!existsSync(at)) throw new Error(`[provider] ${field}: there is no file at ${at}`);
|
|
6741
6763
|
files.push(at);
|
|
6742
6764
|
return readFileSync(at);
|
|
@@ -6754,6 +6776,10 @@ function gatewayTls(path = configPath(), env = process.env) {
|
|
|
6754
6776
|
if (p.pfx_passphrase !== void 0) tls.passphrase = valueOf("pfx_passphrase", p.pfx_passphrase, env);
|
|
6755
6777
|
return files.length ? tls : null;
|
|
6756
6778
|
}
|
|
6779
|
+
/** Whether Iso has a model here: a key kept, or a provider of your own (a gateway may need no key). */
|
|
6780
|
+
async function hasModel$1(config = configPath()) {
|
|
6781
|
+
return await new ConfigSecrets(config).get("provider.key") !== null || providerConfig(config).url !== OPENROUTER.url;
|
|
6782
|
+
}
|
|
6757
6783
|
//#endregion
|
|
6758
6784
|
//#region packages/server/src/model.ts
|
|
6759
6785
|
/** Where the server looks for the model, in order, and why each place is there. */
|
|
@@ -6784,7 +6810,7 @@ function modelPlaces(env = process.env) {
|
|
|
6784
6810
|
/** Where the model is, or null when it is not installed or is switched off (ISORITE_EMBED=off). */
|
|
6785
6811
|
function modelDir(env = process.env) {
|
|
6786
6812
|
if (env.ISORITE_EMBED === "off") return null;
|
|
6787
|
-
return modelPlaces(env).find((p) => hasModel
|
|
6813
|
+
return modelPlaces(env).find((p) => hasModel(p.dir))?.dir ?? null;
|
|
6788
6814
|
}
|
|
6789
6815
|
/** What is in a model's folder. `isorite-model.json` beside the files may give its `name` and `pooling` ("cls",
|
|
6790
6816
|
* the first token's state, as granite is used; or "mean", the average of them all, as all-MiniLM is). */
|
|
@@ -6918,7 +6944,7 @@ async function fetchModel(out, say = console.log, { from, pkg = PACKAGE, release
|
|
|
6918
6944
|
* checked by using it: it must load, and give DIM numbers for a sentence. Kept in the user's models, and used from
|
|
6919
6945
|
* then on. */
|
|
6920
6946
|
async function installFrom(from, say = console.log, env = process.env) {
|
|
6921
|
-
if (!hasModel
|
|
6947
|
+
if (!hasModel(from)) throw new Error(`${from} has no model: it needs model.onnx and tokenizer.json`);
|
|
6922
6948
|
const granite = !existsSync(join(from, "isorite-model.json")) && FILES.every(([, to, want]) => existsSync(join(from, to)) && sha(readFileSync(join(from, to))) === want);
|
|
6923
6949
|
let out = userModelDir(env);
|
|
6924
6950
|
if (granite) await fetchModel(out, say, { from });
|
|
@@ -6955,7 +6981,7 @@ async function modelCommand(from, say = console.log, env = process.env, sources
|
|
|
6955
6981
|
say(e.message);
|
|
6956
6982
|
return 1;
|
|
6957
6983
|
}
|
|
6958
|
-
const places = modelPlaces(env), found = places.find((p) => hasModel
|
|
6984
|
+
const places = modelPlaces(env), found = places.find((p) => hasModel(p.dir));
|
|
6959
6985
|
if (found) {
|
|
6960
6986
|
told(modelInfo(found.dir), found.why);
|
|
6961
6987
|
say("Nothing to fetch.");
|
|
@@ -6977,7 +7003,7 @@ async function modelCommand(from, say = console.log, env = process.env, sources
|
|
|
6977
7003
|
return 1;
|
|
6978
7004
|
}
|
|
6979
7005
|
}
|
|
6980
|
-
var NAME$1, MODEL, RELEASE, PACKAGE, HUB, FILES, sha, userModelsDir, userModelDir, hasModel
|
|
7006
|
+
var NAME$1, MODEL, RELEASE, PACKAGE, HUB, FILES, sha, userModelsDir, userModelDir, hasModel, ModelFetchError, modelFiles;
|
|
6981
7007
|
var init_model = __esmMin((() => {
|
|
6982
7008
|
init_src();
|
|
6983
7009
|
NAME$1 = "granite-embedding-small-english-r2";
|
|
@@ -7010,7 +7036,7 @@ var init_model = __esmMin((() => {
|
|
|
7010
7036
|
sha = (buf) => createHash("sha256").update(buf).digest("hex");
|
|
7011
7037
|
userModelsDir = (env = process.env) => join(env.XDG_DATA_HOME || join(homedir(), ".local", "share"), "isorite", "models");
|
|
7012
7038
|
userModelDir = (env = process.env) => join(userModelsDir(env), NAME$1);
|
|
7013
|
-
hasModel
|
|
7039
|
+
hasModel = (dir) => existsSync(join(dir, "model.onnx")) && existsSync(join(dir, "tokenizer.json"));
|
|
7014
7040
|
ModelFetchError = class extends Error {
|
|
7015
7041
|
tried;
|
|
7016
7042
|
constructor(tried) {
|
|
@@ -7145,16 +7171,17 @@ function gatewayHttp(provider, tls, env = process.env) {
|
|
|
7145
7171
|
}
|
|
7146
7172
|
/** What OpenCode may ask of the provider through isorite. */
|
|
7147
7173
|
const RELAYED = /* @__PURE__ */ new Set(["/chat/completions", "/models"]);
|
|
7148
|
-
/** The provider at /api/gateway: each request is sent on with what `endpoint` adds (the key, where the provider
|
|
7174
|
+
/** The provider at /api/gateway, as `live` has it: each request is sent on with what `endpoint` adds (the key, where the provider
|
|
7149
7175
|
* wants it, its headers and query) by `http`, and the reply streamed back. A provider that can't be reached is
|
|
7150
7176
|
* answered as an OpenAI-compatible one answers an error, so OpenCode shows why. */
|
|
7151
|
-
function relay(
|
|
7177
|
+
function relay(live, key) {
|
|
7152
7178
|
const json = (status, message) => new Response(JSON.stringify({ error: { message } }), {
|
|
7153
7179
|
status,
|
|
7154
7180
|
headers: { "Content-Type": "application/json" }
|
|
7155
7181
|
});
|
|
7156
7182
|
return async (path, req) => {
|
|
7157
7183
|
if (!RELAYED.has(path)) return json(404, `isorite relays only ${[...RELAYED].join(" and ")}`);
|
|
7184
|
+
const { provider, http } = live();
|
|
7158
7185
|
const to = endpoint(provider, await key(), path), type = req.headers.get("content-type");
|
|
7159
7186
|
const body = req.method === "POST" ? await req.text() : void 0;
|
|
7160
7187
|
let res;
|
|
@@ -7641,6 +7668,35 @@ const Revised = z.object({
|
|
|
7641
7668
|
text: z.union([z.string(), z.array(z.string()).max(40)])
|
|
7642
7669
|
}).strict();
|
|
7643
7670
|
const Key = z.object({ key: z.string().nullable() }).strict();
|
|
7671
|
+
const Pair = z.object({
|
|
7672
|
+
name: z.string().max(200),
|
|
7673
|
+
value: z.string().max(4e3),
|
|
7674
|
+
held: z.boolean().optional()
|
|
7675
|
+
}).strict();
|
|
7676
|
+
const Path = z.string().max(1e3);
|
|
7677
|
+
const SetupDraft = z.object({
|
|
7678
|
+
kind: z.enum(["openrouter", "gateway"]),
|
|
7679
|
+
name: z.string().max(100),
|
|
7680
|
+
url: z.string().max(2e3),
|
|
7681
|
+
keyHeld: z.boolean(),
|
|
7682
|
+
keyHeader: z.string().max(200),
|
|
7683
|
+
headers: z.array(Pair).max(40),
|
|
7684
|
+
query: z.array(Pair).max(40),
|
|
7685
|
+
pfx: Path,
|
|
7686
|
+
passphraseHeld: z.boolean(),
|
|
7687
|
+
cert: Path,
|
|
7688
|
+
certKey: Path,
|
|
7689
|
+
ca: Path,
|
|
7690
|
+
streamUsage: z.boolean(),
|
|
7691
|
+
maxTokens: z.number().int().positive().max(1e6).nullable(),
|
|
7692
|
+
tiers: z.object({
|
|
7693
|
+
low: z.string().max(200),
|
|
7694
|
+
mid: z.string().max(200),
|
|
7695
|
+
max: z.string().max(200)
|
|
7696
|
+
}).strict(),
|
|
7697
|
+
key: z.string().max(400).nullable().optional(),
|
|
7698
|
+
passphrase: z.string().max(1e3).nullable().optional()
|
|
7699
|
+
}).strict();
|
|
7644
7700
|
const AgentName = z.string().regex(/^[a-z0-9][a-z0-9-]{0,39}$/);
|
|
7645
7701
|
const AgentLook = z.object({
|
|
7646
7702
|
agent: AgentName,
|
|
@@ -7666,7 +7722,7 @@ const AgentStatus = z.object({
|
|
|
7666
7722
|
session: z.string().min(1).max(200),
|
|
7667
7723
|
busy: z.boolean()
|
|
7668
7724
|
}).strict();
|
|
7669
|
-
function createApp({ api, token, loopback, web, allowHosts = [], notes, terminal, gateway }) {
|
|
7725
|
+
function createApp({ api, token, loopback, web, allowHosts = [], notes, terminal, gateway, setup }) {
|
|
7670
7726
|
const app = new Hono();
|
|
7671
7727
|
const allowed = new Set([...allowHosts].map((h) => h.toLowerCase()));
|
|
7672
7728
|
const reply = (c, status, value) => c.body(JSON.stringify(value), status, {
|
|
@@ -7681,6 +7737,16 @@ function createApp({ api, token, loopback, web, allowHosts = [], notes, terminal
|
|
|
7681
7737
|
} catch {}
|
|
7682
7738
|
return !loopback || LOOPBACK.has(host) || host.endsWith(".ts.net") || allowed.has(host);
|
|
7683
7739
|
};
|
|
7740
|
+
/** Asked from this machine itself: its own name, and (where the connection says) from its own address. A
|
|
7741
|
+
* tailnet's name is not, even through tailscale serve, whose connections come from this machine. */
|
|
7742
|
+
const local = (c) => {
|
|
7743
|
+
let host = "";
|
|
7744
|
+
try {
|
|
7745
|
+
host = new URL("http://" + (c.req.header("host") ?? "")).hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
7746
|
+
} catch {}
|
|
7747
|
+
const from = c.env?.incoming?.socket?.remoteAddress;
|
|
7748
|
+
return LOOPBACK.has(host) && (from === void 0 || /^(127\.|::1$|::ffff:127\.)/.test(from));
|
|
7749
|
+
};
|
|
7684
7750
|
const NOT_HERE = "isorite answers only to this machine's own names (localhost) and to a tailnet's (….ts.net). To be reached by another name, start it with: isorite serve <base> --allow-host <that name>";
|
|
7685
7751
|
/** Why a write is refused, or null. */
|
|
7686
7752
|
const writeRefused = (c) => {
|
|
@@ -7829,14 +7895,36 @@ function createApp({ api, token, loopback, web, allowHosts = [], notes, terminal
|
|
|
7829
7895
|
app.get("/api/context", (c) => answer(c, () => api.context(c.req.query("id") || void 0, choiceOf(c.req.query("choice")))));
|
|
7830
7896
|
post("/api/context/clear", z.object({}).strict(), () => api.clearConversation());
|
|
7831
7897
|
app.get("/api/settings", (c) => answer(c, () => api.settings()));
|
|
7832
|
-
post("/api/settings/key", Key, (_c, b) =>
|
|
7898
|
+
post("/api/settings/key", Key, async (_c, b) => {
|
|
7899
|
+
const s = await api.setKey(b.key);
|
|
7900
|
+
await setup?.changed();
|
|
7901
|
+
return api.settings().catch(() => s);
|
|
7902
|
+
}, "put");
|
|
7833
7903
|
app.get("/api/spend", (c) => answer(c, () => api.spend()));
|
|
7834
7904
|
post("/api/settings/spend-cap", z.object({ usd: z.number().positive().max(1e6).nullable() }).strict(), (_c, b) => api.setSpendCap(b.usd), "put");
|
|
7835
7905
|
post("/api/settings/connect", z.object({ callback: z.string().max(2e3) }).strict(), (_c, b) => api.connect(b.callback));
|
|
7836
7906
|
post("/api/settings/connected", z.object({
|
|
7837
7907
|
code: z.string().min(1).max(2e3),
|
|
7838
7908
|
state: z.string().min(1).max(200)
|
|
7839
|
-
}).strict(), (_c, b) =>
|
|
7909
|
+
}).strict(), async (_c, b) => {
|
|
7910
|
+
const s = await api.connected(b.code, b.state);
|
|
7911
|
+
await setup?.changed();
|
|
7912
|
+
return api.settings().catch(() => s);
|
|
7913
|
+
});
|
|
7914
|
+
if (setup) {
|
|
7915
|
+
const FROM_HERE = "The provider's setup is changed only from this machine (localhost): the key goes where it says.";
|
|
7916
|
+
const here = (c) => {
|
|
7917
|
+
if (!local(c)) throw new EngineError(FROM_HERE, 403);
|
|
7918
|
+
};
|
|
7919
|
+
app.get("/api/settings/provider", (c) => answer(c, async () => setup.view(local(c))));
|
|
7920
|
+
post("/api/settings/provider", SetupDraft, (c, b) => {
|
|
7921
|
+
here(c);
|
|
7922
|
+
return setup.keep(b, true);
|
|
7923
|
+
}, "put");
|
|
7924
|
+
post("/api/settings/provider/check", z.object({ draft: SetupDraft.nullable() }).strict(), (c, b) => setup.check(local(c) ? b.draft : null));
|
|
7925
|
+
post("/api/settings/provider/models", z.object({ draft: SetupDraft.nullable() }).strict(), async (c, b) => ({ models: await setup.models(local(c) ? b.draft : null) }));
|
|
7926
|
+
post("/api/settings/report", z.object({}).strict(), async () => ({ text: await setup.report() }));
|
|
7927
|
+
}
|
|
7840
7928
|
app.get("/api/agent/tools", (c) => answer(c, () => api.agentTools()));
|
|
7841
7929
|
post("/api/agent/look", AgentLook, (_c, b) => api.agentLook(b));
|
|
7842
7930
|
app.get("/api/agent/base", (c) => answer(c, async () => ({ notes: notes ?? null })));
|
|
@@ -8037,6 +8125,251 @@ var NodeFiles = class {
|
|
|
8037
8125
|
}
|
|
8038
8126
|
};
|
|
8039
8127
|
//#endregion
|
|
8128
|
+
//#region packages/server/src/setup.ts
|
|
8129
|
+
init_src();
|
|
8130
|
+
/** What the page sent that cannot be kept: said back to it as it is (400). */
|
|
8131
|
+
var SetupError = class extends EngineError {};
|
|
8132
|
+
/** The fields of [provider] the form holds. The rest (spend_cap, cache_marks, context, …) are left as they are. */
|
|
8133
|
+
const FORM = [
|
|
8134
|
+
"url",
|
|
8135
|
+
"name",
|
|
8136
|
+
"auth",
|
|
8137
|
+
"key",
|
|
8138
|
+
"key_header",
|
|
8139
|
+
"headers",
|
|
8140
|
+
"query",
|
|
8141
|
+
"pfx",
|
|
8142
|
+
"pfx_passphrase",
|
|
8143
|
+
"cert",
|
|
8144
|
+
"cert_key",
|
|
8145
|
+
"ca",
|
|
8146
|
+
"stream_usage",
|
|
8147
|
+
"max_tokens"
|
|
8148
|
+
];
|
|
8149
|
+
const NAMED = /^\$[A-Za-z_][A-Za-z0-9_]*$/;
|
|
8150
|
+
const text = (v) => typeof v === "string" || typeof v === "number" ? String(v) : "";
|
|
8151
|
+
const secretPair = (name, value) => SECRET.test(name) && !NAMED.test(value);
|
|
8152
|
+
/** The setup a configuration holds, its secrets held back. */
|
|
8153
|
+
function readSetup(config) {
|
|
8154
|
+
const p = table(config.provider), tiers = table(config.tiers);
|
|
8155
|
+
const pairs = (v) => Object.entries(table(v)).map(([name, x]) => secretPair(name, text(x)) ? {
|
|
8156
|
+
name,
|
|
8157
|
+
value: "",
|
|
8158
|
+
held: true
|
|
8159
|
+
} : {
|
|
8160
|
+
name,
|
|
8161
|
+
value: text(x)
|
|
8162
|
+
});
|
|
8163
|
+
const gateway = typeof p.url === "string" && p.url !== "";
|
|
8164
|
+
return {
|
|
8165
|
+
kind: gateway ? "gateway" : "openrouter",
|
|
8166
|
+
name: text(p.name),
|
|
8167
|
+
url: gateway ? p.url : OPENROUTER.url,
|
|
8168
|
+
keyHeld: text(p.key) !== "",
|
|
8169
|
+
keyHeader: text(p.key_header),
|
|
8170
|
+
headers: pairs(p.headers),
|
|
8171
|
+
query: pairs(p.query),
|
|
8172
|
+
pfx: text(p.pfx),
|
|
8173
|
+
passphraseHeld: p.pfx_passphrase !== void 0 && text(p.pfx_passphrase) !== "",
|
|
8174
|
+
cert: text(p.cert),
|
|
8175
|
+
certKey: text(p.cert_key),
|
|
8176
|
+
ca: text(p.ca),
|
|
8177
|
+
streamUsage: p.stream_usage !== false,
|
|
8178
|
+
maxTokens: typeof p.max_tokens === "number" && p.max_tokens > 0 ? p.max_tokens : null,
|
|
8179
|
+
tiers: Object.fromEntries(TIERS.map((t) => [t, text(tiers[t]) || OPENROUTER.tiers[t]]))
|
|
8180
|
+
};
|
|
8181
|
+
}
|
|
8182
|
+
/** The configuration with `draft` in place of its setup; and whether the key was forgotten for a new address. */
|
|
8183
|
+
function applySetup(config, draft) {
|
|
8184
|
+
const old = table(config.provider), p = Object.fromEntries(Object.entries(old).filter(([k]) => !FORM.includes(k)));
|
|
8185
|
+
const gateway = draft.kind === "gateway", url = gateway ? draft.url.trim() : "";
|
|
8186
|
+
if (gateway) {
|
|
8187
|
+
let parsed;
|
|
8188
|
+
try {
|
|
8189
|
+
parsed = new URL(url);
|
|
8190
|
+
} catch {
|
|
8191
|
+
throw new SetupError("the gateway's address is not an address: it begins https:// (or http:// on this machine)");
|
|
8192
|
+
}
|
|
8193
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new SetupError("the gateway's address begins https:// (or http:// on this machine)");
|
|
8194
|
+
p.url = url.replace(/\/+$/, "");
|
|
8195
|
+
if (draft.name.trim()) p.name = draft.name.trim();
|
|
8196
|
+
}
|
|
8197
|
+
const at = (url) => (url || OPENROUTER.url).replace(/\/+$/, "");
|
|
8198
|
+
const moved = at(text(old.url)) !== at(text(p.url));
|
|
8199
|
+
if (!moved && old.auth !== void 0) p.auth = old.auth;
|
|
8200
|
+
let forgot = false;
|
|
8201
|
+
if (typeof draft.key === "string" && draft.key.trim()) {
|
|
8202
|
+
const key = draft.key.trim();
|
|
8203
|
+
if (key.length > 400 || /\s/.test(key)) throw new SetupError("that does not look like a key");
|
|
8204
|
+
p.key = key;
|
|
8205
|
+
} else if (draft.key === void 0 && old.key !== void 0) {
|
|
8206
|
+
if (moved) forgot = true;
|
|
8207
|
+
else p.key = old.key;
|
|
8208
|
+
}
|
|
8209
|
+
if (gateway) {
|
|
8210
|
+
if (draft.keyHeader.trim()) p.key_header = draft.keyHeader.trim();
|
|
8211
|
+
const pairs = (field, given) => {
|
|
8212
|
+
const before = table(old[field]), out = {};
|
|
8213
|
+
for (const pair of given) {
|
|
8214
|
+
const name = pair.name.trim();
|
|
8215
|
+
if (!name) continue;
|
|
8216
|
+
if (!/^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/.test(name)) throw new SetupError(`${field === "headers" ? "a header's" : "a query value's"} name has a character it cannot have: ${name}`);
|
|
8217
|
+
if (pair.held && !pair.value) {
|
|
8218
|
+
if (before[name] !== void 0 && !moved) out[name] = before[name];
|
|
8219
|
+
continue;
|
|
8220
|
+
}
|
|
8221
|
+
out[name] = pair.value;
|
|
8222
|
+
}
|
|
8223
|
+
if (Object.keys(out).length) p[field] = out;
|
|
8224
|
+
};
|
|
8225
|
+
pairs("headers", draft.headers);
|
|
8226
|
+
pairs("query", draft.query);
|
|
8227
|
+
for (const [field, value] of [
|
|
8228
|
+
["pfx", draft.pfx],
|
|
8229
|
+
["cert", draft.cert],
|
|
8230
|
+
["cert_key", draft.certKey],
|
|
8231
|
+
["ca", draft.ca]
|
|
8232
|
+
]) if (value.trim()) p[field] = value.trim();
|
|
8233
|
+
if (p.pfx !== void 0) {
|
|
8234
|
+
if (typeof draft.passphrase === "string" && draft.passphrase) p.pfx_passphrase = draft.passphrase;
|
|
8235
|
+
else if (draft.passphrase === void 0 && old.pfx_passphrase !== void 0) p.pfx_passphrase = old.pfx_passphrase;
|
|
8236
|
+
}
|
|
8237
|
+
if (!draft.streamUsage) p.stream_usage = false;
|
|
8238
|
+
if (draft.maxTokens !== null) {
|
|
8239
|
+
if (!(Number.isInteger(draft.maxTokens) && draft.maxTokens > 0 && draft.maxTokens <= 1e6)) throw new SetupError("max_tokens is a whole number of tokens");
|
|
8240
|
+
p.max_tokens = draft.maxTokens;
|
|
8241
|
+
}
|
|
8242
|
+
}
|
|
8243
|
+
const tiers = Object.fromEntries(Object.entries(table(config.tiers)).filter(([k]) => !TIERS.includes(k)));
|
|
8244
|
+
for (const t of TIERS) {
|
|
8245
|
+
const model = draft.tiers[t]?.trim() ?? "";
|
|
8246
|
+
if (model.length > 200 || /\s/.test(model)) throw new SetupError(`the ${t} tier's model is not a model's name`);
|
|
8247
|
+
if (model && (gateway || model !== OPENROUTER.tiers[t])) tiers[t] = model;
|
|
8248
|
+
}
|
|
8249
|
+
const out = {
|
|
8250
|
+
...config,
|
|
8251
|
+
provider: p
|
|
8252
|
+
};
|
|
8253
|
+
if (Object.keys(tiers).length) out.tiers = tiers;
|
|
8254
|
+
else delete out.tiers;
|
|
8255
|
+
if (!Object.keys(p).length) delete out.provider;
|
|
8256
|
+
return {
|
|
8257
|
+
config: out,
|
|
8258
|
+
forgot
|
|
8259
|
+
};
|
|
8260
|
+
}
|
|
8261
|
+
function reach(config, dir, env = process.env) {
|
|
8262
|
+
const provider = providerFrom(config, env), tls = gatewayTlsFrom(config, dir, env);
|
|
8263
|
+
return {
|
|
8264
|
+
provider,
|
|
8265
|
+
http: gatewayHttp(provider, tls, env),
|
|
8266
|
+
key: text(table(config.provider).key) || null,
|
|
8267
|
+
tls
|
|
8268
|
+
};
|
|
8269
|
+
}
|
|
8270
|
+
/** The setup, for the server's routes. */
|
|
8271
|
+
var Setup = class {
|
|
8272
|
+
path;
|
|
8273
|
+
env;
|
|
8274
|
+
o;
|
|
8275
|
+
constructor(o = {}) {
|
|
8276
|
+
this.o = o;
|
|
8277
|
+
this.env = o.env ?? process.env;
|
|
8278
|
+
this.path = o.config ?? configPath(this.env);
|
|
8279
|
+
}
|
|
8280
|
+
view(local) {
|
|
8281
|
+
return {
|
|
8282
|
+
setup: readSetup(readConfig(this.path)),
|
|
8283
|
+
local,
|
|
8284
|
+
config: this.path
|
|
8285
|
+
};
|
|
8286
|
+
}
|
|
8287
|
+
/** The configuration a draft would make, or the one kept (null). */
|
|
8288
|
+
draft(draft) {
|
|
8289
|
+
const config = readConfig(this.path);
|
|
8290
|
+
return draft ? applySetup(config, draft).config : config;
|
|
8291
|
+
}
|
|
8292
|
+
/** Doctor's checks of the provider, on the draft as if it were kept. Nothing is written. */
|
|
8293
|
+
async check(draft) {
|
|
8294
|
+
const checks = [], say = (step, outcome, said) => void checks.push({
|
|
8295
|
+
step,
|
|
8296
|
+
outcome,
|
|
8297
|
+
said
|
|
8298
|
+
});
|
|
8299
|
+
let r;
|
|
8300
|
+
try {
|
|
8301
|
+
r = reach(this.draft(draft), dirname(this.path), this.env);
|
|
8302
|
+
} catch (e) {
|
|
8303
|
+
say("setup", "fail", e.message);
|
|
8304
|
+
return checks;
|
|
8305
|
+
}
|
|
8306
|
+
if (!r.key && !r.tls && /\/\/(?:[^/]*\.)?openrouter\.ai\b/.test(r.provider.url)) {
|
|
8307
|
+
say("key", "fail", "none: paste one, or sign in for one");
|
|
8308
|
+
return checks;
|
|
8309
|
+
}
|
|
8310
|
+
await checkProvider(r.provider, r.http, r.key, say);
|
|
8311
|
+
return checks;
|
|
8312
|
+
}
|
|
8313
|
+
/** The models the provider lists, for the tiers to be chosen from. Empty where it has no list. */
|
|
8314
|
+
async models(draft) {
|
|
8315
|
+
let r;
|
|
8316
|
+
try {
|
|
8317
|
+
r = reach(this.draft(draft), dirname(this.path), this.env);
|
|
8318
|
+
} catch (e) {
|
|
8319
|
+
throw new SetupError(e.message);
|
|
8320
|
+
}
|
|
8321
|
+
const to = endpoint(r.provider, r.key, "/models");
|
|
8322
|
+
let res;
|
|
8323
|
+
try {
|
|
8324
|
+
res = await r.http.fetch(to.url, {
|
|
8325
|
+
headers: to.headers,
|
|
8326
|
+
signal: AbortSignal.timeout(2e4)
|
|
8327
|
+
});
|
|
8328
|
+
} catch (e) {
|
|
8329
|
+
throw new SetupError(unreachable(e, r.provider.url));
|
|
8330
|
+
}
|
|
8331
|
+
if (!res.ok) {
|
|
8332
|
+
await res.body?.cancel();
|
|
8333
|
+
return [];
|
|
8334
|
+
}
|
|
8335
|
+
const listed = ((await res.json().catch(() => ({}))).data ?? []).map((m) => m.id).filter((id) => typeof id === "string");
|
|
8336
|
+
return [...new Set(listed)].sort();
|
|
8337
|
+
}
|
|
8338
|
+
/** Keep the draft, once it is known to be usable, and take it into use. The file as it was is kept beside it the
|
|
8339
|
+
* first time, since writing it loses its comments. */
|
|
8340
|
+
async keep(draft, local) {
|
|
8341
|
+
const { config } = applySetup(readConfig(this.path), draft);
|
|
8342
|
+
try {
|
|
8343
|
+
reach(config, dirname(this.path), this.env);
|
|
8344
|
+
} catch (e) {
|
|
8345
|
+
throw new SetupError(e.message);
|
|
8346
|
+
}
|
|
8347
|
+
if (existsSync(this.path) && !existsSync(`${this.path}.bak`)) copyFileSync(this.path, `${this.path}.bak`);
|
|
8348
|
+
writeConfig(config, this.path);
|
|
8349
|
+
await this.o.kept?.();
|
|
8350
|
+
return this.view(local);
|
|
8351
|
+
}
|
|
8352
|
+
/** A key was kept or forgotten by the engine: taken into use as a setup is. */
|
|
8353
|
+
async changed() {
|
|
8354
|
+
await this.o.kept?.();
|
|
8355
|
+
}
|
|
8356
|
+
/** isorite doctor --report, as one block with its secrets hidden. */
|
|
8357
|
+
async report() {
|
|
8358
|
+
const lines = [];
|
|
8359
|
+
await doctor(this.o.notes, {
|
|
8360
|
+
report: true,
|
|
8361
|
+
env: this.env,
|
|
8362
|
+
out: (l) => lines.push(l),
|
|
8363
|
+
...this.o.version ? { version: this.o.version } : {},
|
|
8364
|
+
...this.o.port ? { port: this.o.port } : {}
|
|
8365
|
+
});
|
|
8366
|
+
return lines.join("\n");
|
|
8367
|
+
}
|
|
8368
|
+
};
|
|
8369
|
+
//#endregion
|
|
8370
|
+
//#region packages/server/src/version.ts
|
|
8371
|
+
const VERSION = "0.7.0";
|
|
8372
|
+
//#endregion
|
|
8040
8373
|
//#region packages/server/src/studybase.ts
|
|
8041
8374
|
init_src$1();
|
|
8042
8375
|
function git(cwd, ...args) {
|
|
@@ -8131,20 +8464,49 @@ async function openEngine({ notes }, device = deviceId(), config = configPath(),
|
|
|
8131
8464
|
} : {}
|
|
8132
8465
|
});
|
|
8133
8466
|
}
|
|
8467
|
+
/** The guide's finder, as a provider: no key, no sign-in, and a name that says what it is. */
|
|
8468
|
+
const { auth: _auth, ...openrouter } = OPENROUTER;
|
|
8469
|
+
const FINDER = {
|
|
8470
|
+
...openrouter,
|
|
8471
|
+
name: "finder",
|
|
8472
|
+
url: "http://finder.invalid/v1"
|
|
8473
|
+
};
|
|
8474
|
+
/** The provider as the configuration has it now: kept from the settings (M18), it is taken into use at once, by
|
|
8475
|
+
* Iso and the relay. With `stand`, that is asked while there is no model here. */
|
|
8476
|
+
async function liveProvider(config, stand) {
|
|
8477
|
+
if (stand && !await hasModel$1(config)) return {
|
|
8478
|
+
provider: FINDER,
|
|
8479
|
+
http: stand
|
|
8480
|
+
};
|
|
8481
|
+
const provider = providerConfig(config);
|
|
8482
|
+
return {
|
|
8483
|
+
provider,
|
|
8484
|
+
http: gatewayHttp(provider, gatewayTls(config))
|
|
8485
|
+
};
|
|
8486
|
+
}
|
|
8134
8487
|
/** A link most terminals let you click (OSC 8), when the output is a terminal; the address as it is otherwise. */
|
|
8135
8488
|
const link$1 = (url, tty = Boolean(process.stdout.isTTY)) => tty ? `\x1b]8;;${url}\x1b\\${url}\x1b]8;;\x1b\\` : url;
|
|
8136
8489
|
/** `stand`: in place of the provider, an answerer of its own, as the guide's finder is when no model is set up
|
|
8137
|
-
* (M16). It
|
|
8490
|
+
* (M16). It is asked while there is no model here (no key, no provider of your own), and needs no key. */
|
|
8138
8491
|
async function serve$1(path, { host = "127.0.0.1", port = 4767, allowHosts = [], stand } = {}) {
|
|
8139
8492
|
const opened = locate(path);
|
|
8140
|
-
const config = configPath()
|
|
8141
|
-
const
|
|
8142
|
-
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
const
|
|
8147
|
-
|
|
8493
|
+
const config = configPath();
|
|
8494
|
+
const current = () => liveProvider(config, stand);
|
|
8495
|
+
let live = await current();
|
|
8496
|
+
const secrets = new ConfigSecrets(config);
|
|
8497
|
+
const api = await openEngine(opened, deviceId(config), config, live.http, modelDir(), secrets);
|
|
8498
|
+
api.setProvider(live.provider, live.http);
|
|
8499
|
+
const setup = new Setup({
|
|
8500
|
+
config,
|
|
8501
|
+
notes: opened.notes,
|
|
8502
|
+
version: VERSION,
|
|
8503
|
+
port,
|
|
8504
|
+
kept: async () => {
|
|
8505
|
+
live = await current();
|
|
8506
|
+
api.setProvider(live.provider, live.http);
|
|
8507
|
+
}
|
|
8508
|
+
});
|
|
8509
|
+
const gateway = LOOPBACK.has(host) ? relay(() => live, () => secrets.get(KEY)) : void 0;
|
|
8148
8510
|
const web = existsSync(join(WEB_DIR, "index.html")) ? WEB_DIR : null;
|
|
8149
8511
|
const terminal = new Terminal();
|
|
8150
8512
|
const app = createApp({
|
|
@@ -8155,7 +8517,8 @@ async function serve$1(path, { host = "127.0.0.1", port = 4767, allowHosts = [],
|
|
|
8155
8517
|
allowHosts,
|
|
8156
8518
|
notes: opened.notes,
|
|
8157
8519
|
terminal,
|
|
8158
|
-
gateway
|
|
8520
|
+
gateway,
|
|
8521
|
+
setup
|
|
8159
8522
|
});
|
|
8160
8523
|
const server = serve({
|
|
8161
8524
|
fetch: app.fetch,
|
|
@@ -8502,6 +8865,65 @@ const TOOL = {
|
|
|
8502
8865
|
}
|
|
8503
8866
|
}
|
|
8504
8867
|
};
|
|
8868
|
+
/** The provider asked, as doctor asks it and as the page's check does (M18): its list of models, a short reply from
|
|
8869
|
+
* each tier's model, and one tool call. A few hundred tokens in all. */
|
|
8870
|
+
async function checkProvider(p, h, key, say) {
|
|
8871
|
+
const models = [.../* @__PURE__ */ new Set([
|
|
8872
|
+
p.tiers.low,
|
|
8873
|
+
p.tiers.mid,
|
|
8874
|
+
p.tiers.max
|
|
8875
|
+
])];
|
|
8876
|
+
let reached = true;
|
|
8877
|
+
try {
|
|
8878
|
+
const to = endpoint(p, key, "/models");
|
|
8879
|
+
const res = await h.fetch(to.url, {
|
|
8880
|
+
headers: to.headers,
|
|
8881
|
+
signal: AbortSignal.timeout(2e4)
|
|
8882
|
+
});
|
|
8883
|
+
if (res.ok) {
|
|
8884
|
+
const listed = ((await res.json().catch(() => ({}))).data ?? []).map((m) => m.id ?? "");
|
|
8885
|
+
const missing = models.filter((m) => !listed.includes(m));
|
|
8886
|
+
say("reached", "ok", `${to.url.replace(/\?.*$/, "")} answered with ${listed.length} models`);
|
|
8887
|
+
if (listed.length && missing.length) say("models", "fail", `${missing.join(", ")} not among them; it has ${listed.slice(0, 12).join(", ")}${listed.length > 12 ? ", …" : ""}. Name its models under [tiers]`);
|
|
8888
|
+
} else say("reached", res.status === 401 || res.status === 403 ? "fail" : "ok", res.status === 401 || res.status === 403 ? `it said ${res.status} to the list of models: the key${p.keyHeader ? "" : ", or the header it goes in (key_header)"}, or a header it wants (headers), is not accepted` : `it answered (${res.status} for the list of models, which some gateways don't have)`);
|
|
8889
|
+
} catch (e) {
|
|
8890
|
+
reached = false;
|
|
8891
|
+
say("reached", "fail", unreachable(e, p.url));
|
|
8892
|
+
}
|
|
8893
|
+
if (reached) {
|
|
8894
|
+
for (const model of models) try {
|
|
8895
|
+
const reply = await complete(h, p, key, {
|
|
8896
|
+
model,
|
|
8897
|
+
maxTokens: 16,
|
|
8898
|
+
messages: [{
|
|
8899
|
+
role: "user",
|
|
8900
|
+
content: "Reply with the one word: ready"
|
|
8901
|
+
}],
|
|
8902
|
+
signal: AbortSignal.timeout(6e4)
|
|
8903
|
+
});
|
|
8904
|
+
const usage = reply.usage.tokensIn + reply.usage.tokensOut > 0;
|
|
8905
|
+
say(`reply from ${model}`, usage || p.streamUsage === false ? "ok" : "warn", `"${reply.text.trim().slice(0, 40)}"${usage ? `, ${reply.usage.tokensIn} tokens in and ${reply.usage.tokensOut} out` : p.streamUsage === false ? "" : ": no usage reported, so the gauge can't count it"}`);
|
|
8906
|
+
} catch (e) {
|
|
8907
|
+
const said = e.message;
|
|
8908
|
+
say(`reply from ${model}`, "fail", /stream_options/i.test(said) ? `${said} Set stream_usage = false under [provider].` : said);
|
|
8909
|
+
}
|
|
8910
|
+
try {
|
|
8911
|
+
const reply = await complete(h, p, key, {
|
|
8912
|
+
model: p.tiers.mid,
|
|
8913
|
+
maxTokens: 64,
|
|
8914
|
+
tools: [TOOL],
|
|
8915
|
+
messages: [{
|
|
8916
|
+
role: "user",
|
|
8917
|
+
content: "Ping, using the ping tool."
|
|
8918
|
+
}],
|
|
8919
|
+
signal: AbortSignal.timeout(6e4)
|
|
8920
|
+
});
|
|
8921
|
+
say("tools", reply.calls.length ? "ok" : "fail", reply.calls.length ? `${p.tiers.mid} called ${reply.calls[0].name}` : `${p.tiers.mid} answered without calling the tool: Iso and the terminal need a model, and a gateway, that pass tools on`);
|
|
8922
|
+
} catch (e) {
|
|
8923
|
+
say("tools", "fail", e instanceof ModelError ? e.message : String(e));
|
|
8924
|
+
}
|
|
8925
|
+
}
|
|
8926
|
+
}
|
|
8505
8927
|
/** Run the checks; 0 when none failed. */
|
|
8506
8928
|
async function doctor(folder, o = {}) {
|
|
8507
8929
|
const env = o.env ?? process.env, out = o.out ?? console.log, checks = [];
|
|
@@ -8515,7 +8937,7 @@ async function doctor(folder, o = {}) {
|
|
|
8515
8937
|
};
|
|
8516
8938
|
const path = configPath(env), execArgv = o.execArgv ?? process.execArgv;
|
|
8517
8939
|
let provider = null, http = o.http ?? null, key = null;
|
|
8518
|
-
if (!existsSync(path)) say("config", "warn", `there is no ${path}: Iso uses OpenRouter, and needs a key (
|
|
8940
|
+
if (!existsSync(path)) say("config", "warn", `there is no ${path}: Iso uses OpenRouter, and needs a key (in Iso's settings, or key under [provider] there)`);
|
|
8519
8941
|
try {
|
|
8520
8942
|
readConfig(path);
|
|
8521
8943
|
provider = providerConfig(path, env);
|
|
@@ -8523,7 +8945,7 @@ async function doctor(folder, o = {}) {
|
|
|
8523
8945
|
const tls = gatewayTls(path, env);
|
|
8524
8946
|
http ??= gatewayHttp(provider, tls, env);
|
|
8525
8947
|
if (existsSync(path)) say("config", "ok", `${path}: ${provider.name} at ${provider.url}`);
|
|
8526
|
-
say("key", key ? "ok" : tls ? "warn" : "fail", key ? `${hidden(key)}, sent ${provider.keyHeader ? `in ${provider.keyHeader}` : "as Authorization: Bearer"}` : tls ? "none; the client certificate alone is sent" : "none: set one in settings
|
|
8948
|
+
say("key", key ? "ok" : tls ? "warn" : "fail", key ? `${hidden(key)}, sent ${provider.keyHeader ? `in ${provider.keyHeader}` : "as Authorization: Bearer"}` : tls ? "none; the client certificate alone is sent" : "none: set one in Iso's settings, or as key under [provider]");
|
|
8527
8949
|
if (tls) say("certificate", "ok", `${tls.files.join(", ")}${tls.pfx || tls.cert ? "" : " (an authority only, no client certificate)"}`);
|
|
8528
8950
|
const tiers = `low ${provider.tiers.low}, mid ${provider.tiers.mid}, max ${provider.tiers.max}`;
|
|
8529
8951
|
say("models", provider.defaultTiers ? "warn" : "ok", provider.defaultTiers ? `${tiers}: OpenRouter's names, kept because [tiers] names none for ${provider.name}; a gateway usually calls its models otherwise` : tiers);
|
|
@@ -8547,56 +8969,7 @@ async function doctor(folder, o = {}) {
|
|
|
8547
8969
|
p.tiers.max
|
|
8548
8970
|
])];
|
|
8549
8971
|
if (!o.report) out(` asking ${p.name}: its models, then ${models.length + 1} replies of a few words (a few hundred tokens in all)`);
|
|
8550
|
-
|
|
8551
|
-
try {
|
|
8552
|
-
const to = endpoint(p, key, "/models");
|
|
8553
|
-
const res = await h.fetch(to.url, {
|
|
8554
|
-
headers: to.headers,
|
|
8555
|
-
signal: AbortSignal.timeout(2e4)
|
|
8556
|
-
});
|
|
8557
|
-
if (res.ok) {
|
|
8558
|
-
const listed = ((await res.json().catch(() => ({}))).data ?? []).map((m) => m.id ?? "");
|
|
8559
|
-
const missing = models.filter((m) => !listed.includes(m));
|
|
8560
|
-
say("reached", "ok", `${to.url.replace(/\?.*$/, "")} answered with ${listed.length} models`);
|
|
8561
|
-
if (listed.length && missing.length) say("models", "fail", `${missing.join(", ")} not among them; it has ${listed.slice(0, 12).join(", ")}${listed.length > 12 ? ", …" : ""}. Name its models under [tiers]`);
|
|
8562
|
-
} else say("reached", res.status === 401 || res.status === 403 ? "fail" : "ok", res.status === 401 || res.status === 403 ? `it said ${res.status} to the list of models: the key${p.keyHeader ? "" : ", or the header it goes in (key_header)"}, or a header it wants (headers), is not accepted` : `it answered (${res.status} for the list of models, which some gateways don't have)`);
|
|
8563
|
-
} catch (e) {
|
|
8564
|
-
reached = false;
|
|
8565
|
-
say("reached", "fail", unreachable(e, p.url));
|
|
8566
|
-
}
|
|
8567
|
-
if (reached) {
|
|
8568
|
-
for (const model of models) try {
|
|
8569
|
-
const reply = await complete(h, p, key, {
|
|
8570
|
-
model,
|
|
8571
|
-
maxTokens: 16,
|
|
8572
|
-
messages: [{
|
|
8573
|
-
role: "user",
|
|
8574
|
-
content: "Reply with the one word: ready"
|
|
8575
|
-
}],
|
|
8576
|
-
signal: AbortSignal.timeout(6e4)
|
|
8577
|
-
});
|
|
8578
|
-
const usage = reply.usage.tokensIn + reply.usage.tokensOut > 0;
|
|
8579
|
-
say(`reply from ${model}`, usage || p.streamUsage === false ? "ok" : "warn", `"${reply.text.trim().slice(0, 40)}"${usage ? `, ${reply.usage.tokensIn} tokens in and ${reply.usage.tokensOut} out` : p.streamUsage === false ? "" : ": no usage reported, so the gauge can't count it"}`);
|
|
8580
|
-
} catch (e) {
|
|
8581
|
-
const said = e.message;
|
|
8582
|
-
say(`reply from ${model}`, "fail", /stream_options/i.test(said) ? `${said} Set stream_usage = false under [provider].` : said);
|
|
8583
|
-
}
|
|
8584
|
-
try {
|
|
8585
|
-
const reply = await complete(h, p, key, {
|
|
8586
|
-
model: p.tiers.mid,
|
|
8587
|
-
maxTokens: 64,
|
|
8588
|
-
tools: [TOOL],
|
|
8589
|
-
messages: [{
|
|
8590
|
-
role: "user",
|
|
8591
|
-
content: "Ping, using the ping tool."
|
|
8592
|
-
}],
|
|
8593
|
-
signal: AbortSignal.timeout(6e4)
|
|
8594
|
-
});
|
|
8595
|
-
say("tools", reply.calls.length ? "ok" : "fail", reply.calls.length ? `${p.tiers.mid} called ${reply.calls[0].name}` : `${p.tiers.mid} answered without calling the tool: Iso and the terminal need a model, and a gateway, that pass tools on`);
|
|
8596
|
-
} catch (e) {
|
|
8597
|
-
say("tools", "fail", e instanceof ModelError ? e.message : String(e));
|
|
8598
|
-
}
|
|
8599
|
-
}
|
|
8972
|
+
await checkProvider(p, h, key, say);
|
|
8600
8973
|
}
|
|
8601
8974
|
try {
|
|
8602
8975
|
const found = findOpenCode(env, o.probe);
|
|
@@ -10015,7 +10388,6 @@ function loadGuide(dir = GUIDE_DIR) {
|
|
|
10015
10388
|
}
|
|
10016
10389
|
//#endregion
|
|
10017
10390
|
//#region packages/server/src/guide.ts
|
|
10018
|
-
init_src();
|
|
10019
10391
|
init_src$1();
|
|
10020
10392
|
const GUIDE_FOLDER = join(homedir(), "isorite-guide");
|
|
10021
10393
|
/** Write the guide into `folder`, unless it is there already. Returns whether it was written now. A folder that
|
|
@@ -10037,19 +10409,15 @@ function writeGuide(folder) {
|
|
|
10037
10409
|
fresh: true
|
|
10038
10410
|
};
|
|
10039
10411
|
}
|
|
10040
|
-
/** Whether Iso has a model here: a key kept, or a provider of your own (a gateway may need no key). */
|
|
10041
|
-
async function hasModel(config = configPath()) {
|
|
10042
|
-
return await new ConfigSecrets(config).get("provider.key") !== null || providerConfig(config).url !== OPENROUTER.url;
|
|
10043
|
-
}
|
|
10044
10412
|
async function guide(folder = GUIDE_FOLDER, { port = 4767, out = console.log } = {}) {
|
|
10045
10413
|
const { notes, fresh } = writeGuide(folder);
|
|
10046
|
-
const model = await hasModel();
|
|
10414
|
+
const model = await hasModel$1();
|
|
10047
10415
|
out(fresh ? `isorite guide: written to ${notes}; it is yours to change, and run again it opens as you left it` : `isorite guide: ${notes}, as you left it`);
|
|
10048
10416
|
out(model ? " Iso: your provider's model" : " Iso: the guide's finder, which answers with the notes that match (no model is set up)");
|
|
10049
10417
|
out(` start at http://localhost:${port}/#/n/${START}`);
|
|
10050
10418
|
await serve$1(notes, {
|
|
10051
10419
|
port,
|
|
10052
|
-
|
|
10420
|
+
stand: finderHttp(16)
|
|
10053
10421
|
});
|
|
10054
10422
|
if (process.env.ISORITE_NO_BROWSER !== "1") openBrowser(`http://localhost:${port}/#/n/${START}`);
|
|
10055
10423
|
}
|
|
@@ -10126,7 +10494,6 @@ async function writeGeometry(folder, now = Date.now()) {
|
|
|
10126
10494
|
//#region packages/server/src/main.ts
|
|
10127
10495
|
init_src$1();
|
|
10128
10496
|
init_model();
|
|
10129
|
-
const VERSION = "0.5.0";
|
|
10130
10497
|
const USAGE = `isorite: an agentic harness for understanding
|
|
10131
10498
|
|
|
10132
10499
|
isorite new <folder> start a knowledge base: the folder, empty, with isorite's .isorite/
|