@astralyn/pi 1.4.3 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/bundle/chunks/{chunk-Z2YN4E74.js → chunk-SBVVM3NV.js} +14 -14
- package/dist/bundle/chunks/{chunk-ISXCH6TG.js → chunk-VGSZTBVJ.js} +1 -1
- package/dist/bundle/chunks/{virtual-modules-QX6D3D7X.js → virtual-modules-ETYFHSUG.js} +1 -1
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +1 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +10 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +2 -0
- package/dist/extensions/index.js.map +1 -1
- package/dist/extensions/provider/store.d.ts +2 -0
- package/dist/extensions/provider/store.d.ts.map +1 -1
- package/dist/extensions/provider/store.js +4 -0
- package/dist/extensions/provider/store.js.map +1 -1
- package/dist/extensions/provider/ui/editor.d.ts +7 -9
- package/dist/extensions/provider/ui/editor.d.ts.map +1 -1
- package/dist/extensions/provider/ui/editor.js +103 -48
- package/dist/extensions/provider/ui/editor.js.map +1 -1
- package/dist/extensions/provider/ui/provider-list.d.ts.map +1 -1
- package/dist/extensions/provider/ui/provider-list.js +13 -19
- package/dist/extensions/provider/ui/provider-list.js.map +1 -1
- package/dist/extensions/run-summary/format.d.ts +2 -0
- package/dist/extensions/run-summary/format.d.ts.map +1 -0
- package/dist/extensions/run-summary/format.js +11 -0
- package/dist/extensions/run-summary/format.js.map +1 -0
- package/dist/extensions/run-summary/index.d.ts +4 -0
- package/dist/extensions/run-summary/index.d.ts.map +1 -0
- package/dist/extensions/run-summary/index.js +48 -0
- package/dist/extensions/run-summary/index.js.map +1 -0
- package/dist/extensions/run-summary/record.d.ts +11 -0
- package/dist/extensions/run-summary/record.d.ts.map +1 -0
- package/dist/extensions/run-summary/record.js +29 -0
- package/dist/extensions/run-summary/record.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +11 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +60 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/docs/bundled/README.md +1 -0
- package/docs/bundled/extensions/provider.md +9 -7
- package/docs/bundled/extensions/run-summary.md +17 -0
- package/docs/docs.json +4 -0
- package/docs/extensions.md +4 -0
- package/docs/tui.md +13 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/extensions/provider/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Op-based models.json editing store.\n *\n * Three states are tracked: `baseline` (last known disk content), pending\n * field-level ops, and `view` (baseline + ops, what the UI renders). Every\n * confirmed edit queues an op and schedules a save. A save re-reads the file\n * under a cross-process lock and applies the pending ops onto that freshest\n * content — unrelated external edits are preserved, a same-field race\n * resolves last-writer-wins, and ops targeting externally removed models\n * simply miss. The candidate is validated via ModelConfig on a temp file,\n * backed up once per session, and atomically renamed into place. Failed\n * saves keep the ops pending; closing offers retry or explicit discard.\n * Whole-snapshot blind overwrites are never performed.\n *\n * Comments and custom formatting are normalized to two-space JSON on save;\n * unknown fields anywhere in the document are preserved verbatim.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, realpath, rename, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { ModelConfig, type ModelsJsonModel, type ModelsJsonProvider } from \"../../core/model-config.ts\";\nimport { withFileMutationQueue } from \"../../core/tools/file-mutation-queue.ts\";\nimport { stripJsonComments } from \"../../utils/json.ts\";\nimport { normalizePath } from \"../../utils/paths.ts\";\nimport { stripBom } from \"../../utils/text.ts\";\nimport { hasProviderSettings } from \"./configuration.ts\";\nimport { formatError } from \"./constants.ts\";\nimport { DELETE, setPath } from \"./json-fields.ts\";\n\nexport { DELETE, type DeleteMarker } from \"./json-fields.ts\";\n\nexport interface ModelsJsonDocument {\n\tproviders: Record<string, ModelsJsonProvider>;\n\t[key: string]: unknown;\n}\n\ninterface OpBase {\n\tseq: number;\n}\nexport interface SetFieldOp extends OpBase {\n\tkind: \"set\";\n\tproviderId: string;\n\t/** Undefined for provider-level fields. */\n\tmodelId?: string;\n\t/** Path within the provider/model object, e.g. [\"compat\", \"allowEmptySignature\"]. */\n\tpath: readonly string[];\n\tvalue: unknown;\n\tcreateProvider: boolean;\n}\nexport interface AddModelOp extends OpBase {\n\tkind: \"addModel\";\n\tproviderId: string;\n\tmodel: ModelsJsonModel;\n\tcreateProvider: boolean;\n}\nexport interface RemoveModelOp extends OpBase {\n\tkind: \"removeModel\";\n\tproviderId: string;\n\tmodelId: string;\n}\nexport interface RenameModelOp extends OpBase {\n\tkind: \"renameModel\";\n\tproviderId: string;\n\toldId: string;\n\tnewId: string;\n}\nexport interface RemoveProviderOp extends OpBase {\n\tkind: \"removeProvider\";\n\tproviderId: string;\n}\nexport type Op = SetFieldOp | AddModelOp | RemoveModelOp | RenameModelOp | RemoveProviderOp;\n\nexport type SaveResult =\n\t| { kind: \"clean\" }\n\t| { kind: \"saved\"; applied: number }\n\t| { kind: \"invalid\"; error: string }\n\t| { kind: \"error\"; error: string };\n\nexport type StoreLoad = { ok: true; store: ModelsJsonStore } | { ok: false; error: string };\n\ninterface DiskRead {\n\texists: boolean;\n\tdoc: ModelsJsonDocument;\n\trawText?: string;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Canonical comparison for JSON values, insensitive to object key order. */\nexport function jsonEquals(a: unknown, b: unknown): boolean {\n\tif (a === b) return true;\n\tif (Array.isArray(a) && Array.isArray(b)) {\n\t\treturn a.length === b.length && a.every((entry, index) => jsonEquals(entry, b[index]));\n\t}\n\tif (isPlainObject(a) && isPlainObject(b)) {\n\t\tconst aKeys = Object.keys(a).sort();\n\t\tconst bKeys = Object.keys(b).sort();\n\t\treturn (\n\t\t\taKeys.length === bKeys.length &&\n\t\t\taKeys.every((key, index) => key === bKeys[index] && jsonEquals(a[key], b[key]))\n\t\t);\n\t}\n\treturn false;\n}\n\nfunction cloneValue(value: unknown): unknown {\n\treturn value === DELETE ? value : structuredClone(value);\n}\n\nasync function readDocument(path: string): Promise<DiskRead> {\n\tlet rawText: string;\n\ttry {\n\t\trawText = await readFile(path, \"utf8\");\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { exists: false, doc: { providers: {} } };\n\t\tthrow error;\n\t}\n\tconst parsed: unknown = JSON.parse(stripJsonComments(stripBom(rawText)));\n\tif (!isPlainObject(parsed)) throw new Error(\"models.json root must be a JSON object.\");\n\tconst doc = parsed as ModelsJsonDocument;\n\tif (!isPlainObject(doc.providers)) throw new Error('models.json \"providers\" must be an object.');\n\treturn { exists: true, doc, rawText };\n}\n\nexport class ModelsJsonStore {\n\treadonly path: string;\n\tprivate baseline: ModelsJsonDocument;\n\tprivate ops: Op[] = [];\n\tprivate view: ModelsJsonDocument;\n\tprivate seq = 0;\n\tprivate backedUp = false;\n\tprivate batching = false;\n\tprivate saveChain: Promise<void> = Promise.resolve();\n\tprivate readonly drafts = new Set<string>();\n\tprivate lastSaveResult: SaveResult = { kind: \"clean\" };\n\t/** Called after each save settles (success, conflict, or failure). */\n\tonSaveResult: ((result: SaveResult) => void) | undefined;\n\n\tprivate constructor(path: string, baseline: ModelsJsonDocument) {\n\t\tthis.path = path;\n\t\tthis.baseline = baseline;\n\t\tthis.view = structuredClone(baseline);\n\t}\n\n\tstatic async load(path: string): Promise<StoreLoad> {\n\t\tlet normalized = normalizePath(path);\n\t\tlet disk: DiskRead;\n\t\ttry {\n\t\t\tnormalized = await realpath(normalized).catch((error: NodeJS.ErrnoException) => {\n\t\t\t\tif (error.code === \"ENOENT\") return normalized;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t\tdisk = await readDocument(normalized);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: `Failed to parse models.json: ${formatError(error)}\\n\\nFile: ${normalized}` };\n\t\t}\n\t\tif (disk.exists) {\n\t\t\t// Surface schema errors as a read-only state; never auto-overwrite an invalid file.\n\t\t\tconst check = await ModelConfig.load(normalized);\n\t\t\tconst validationError = check.getError();\n\t\t\tif (validationError) return { ok: false, error: validationError };\n\t\t}\n\t\treturn { ok: true, store: new ModelsJsonStore(normalized, disk.doc) };\n\t}\n\n\t// ---------------------------------------------------------------------\n\t// View access (read-only for the UI; mutations go through the methods)\n\t// ---------------------------------------------------------------------\n\n\tgetDoc(): ModelsJsonDocument {\n\t\treturn this.view;\n\t}\n\n\tgetProviderIds(): string[] {\n\t\treturn [...new Set([...Object.keys(this.view.providers), ...this.ops.map((op) => op.providerId)])];\n\t}\n\n\tgetProvider(providerId: string): ModelsJsonProvider | undefined {\n\t\treturn Object.hasOwn(this.view.providers, providerId) ? this.view.providers[providerId] : undefined;\n\t}\n\n\t/** Materialize a draft provider in the view without queueing an op (never written while empty). */\n\tensureProviderView(providerId: string): void {\n\t\tif (Object.hasOwn(this.view.providers, providerId)) return;\n\t\tthis.drafts.add(providerId);\n\t\tthis.recomputeView();\n\t}\n\n\t/** Provider present in the view but not yet on disk. */\n\tisDraftProvider(providerId: string): boolean {\n\t\treturn !Object.hasOwn(this.baseline.providers, providerId) && Object.hasOwn(this.view.providers, providerId);\n\t}\n\n\tgetModels(providerId: string): ModelsJsonModel[] {\n\t\tconst provider = this.getProvider(providerId);\n\t\treturn Array.isArray(provider?.models) ? provider.models : [];\n\t}\n\n\tgetModel(providerId: string, modelId: string): ModelsJsonModel | undefined {\n\t\treturn this.getModels(providerId).find((model) => model.id === modelId);\n\t}\n\n\tget pendingCount(): number {\n\t\treturn this.ops.length;\n\t}\n\n\tgetPendingError(providerId?: string): string | undefined {\n\t\tconst pending = this.ops.filter((op) => providerId === undefined || op.providerId === providerId);\n\t\tif (pending.length === 0) return undefined;\n\t\tif (this.lastSaveResult.kind === \"error\" || this.lastSaveResult.kind === \"invalid\")\n\t\t\treturn this.lastSaveResult.error;\n\t\treturn `${pending.length} change(s) are not saved; retry or discard before continuing.`;\n\t}\n\n\tretrySave(): void {\n\t\tif (this.ops.length > 0) this.scheduleSave();\n\t}\n\n\t/** Used only after flush, when the user explicitly discards unsaved edits. */\n\tdiscardPending(): void {\n\t\tthis.ops = [];\n\t\tthis.drafts.clear();\n\t\tthis.recomputeView();\n\t}\n\n\t// ---------------------------------------------------------------------\n\t// Mutations (queue an op, refresh the view, schedule a save)\n\t// ---------------------------------------------------------------------\n\n\tprivate enqueue(op: Op): void {\n\t\tthis.ops.push(op);\n\t\tthis.recomputeView();\n\t\tthis.scheduleSave();\n\t}\n\n\tprivate nextSeq(): number {\n\t\treturn ++this.seq;\n\t}\n\n\tsetProviderField(providerId: string, path: readonly string[], value: unknown): void {\n\t\tthis.enqueue({\n\t\t\tkind: \"set\",\n\t\t\tseq: this.nextSeq(),\n\t\t\tproviderId,\n\t\t\tpath,\n\t\t\tvalue: cloneValue(value),\n\t\t\tcreateProvider: !Object.hasOwn(this.baseline.providers, providerId),\n\t\t});\n\t}\n\n\tsetModelField(providerId: string, modelId: string, path: readonly string[], value: unknown): void {\n\t\tthis.enqueue({\n\t\t\tkind: \"set\",\n\t\t\tseq: this.nextSeq(),\n\t\t\tproviderId,\n\t\t\tmodelId,\n\t\t\tpath,\n\t\t\tvalue: cloneValue(value),\n\t\t\tcreateProvider: !Object.hasOwn(this.baseline.providers, providerId),\n\t\t});\n\t}\n\n\taddModel(providerId: string, model: ModelsJsonModel): void {\n\t\tthis.enqueue({\n\t\t\tkind: \"addModel\",\n\t\t\tseq: this.nextSeq(),\n\t\t\tproviderId,\n\t\t\tmodel: structuredClone(model),\n\t\t\tcreateProvider: !Object.hasOwn(this.baseline.providers, providerId),\n\t\t});\n\t}\n\n\tremoveModel(providerId: string, modelId: string): void {\n\t\tthis.enqueue({\n\t\t\tkind: \"removeModel\",\n\t\t\tseq: this.nextSeq(),\n\t\t\tproviderId,\n\t\t\tmodelId,\n\t\t});\n\t}\n\n\tasync renameModel(providerId: string, oldId: string, newId: string): Promise<string | undefined> {\n\t\tawait this.flush();\n\t\tconst pendingError = this.getPendingError(providerId);\n\t\tif (pendingError) return pendingError;\n\t\tconst before = this.getModel(providerId, oldId);\n\t\tif (!before) return \"The model no longer exists.\";\n\t\tconst seq = this.nextSeq();\n\t\tthis.enqueue({\n\t\t\tkind: \"renameModel\",\n\t\t\tseq,\n\t\t\tproviderId,\n\t\t\toldId,\n\t\t\tnewId,\n\t\t});\n\t\tawait this.flush();\n\t\tif (this.ops.some((op) => op.seq === seq)) {\n\t\t\t// The save failed; drop the rename rather than surprising the user later.\n\t\t\tthis.ops = this.ops.filter((op) => op.seq !== seq);\n\t\t\tthis.recomputeView();\n\t\t\treturn this.getPendingError(providerId) ?? \"Failed to save models.json.\";\n\t\t}\n\t\t// A settled rename can still have missed when the model moved on disk; never\n\t\t// adopt an unrelated model that already uses the destination id.\n\t\tconst renamed = this.getModel(providerId, newId);\n\t\treturn renamed && jsonEquals(renamed, { ...before, id: newId })\n\t\t\t? undefined\n\t\t\t: \"The model changed on disk; reopen it before renaming.\";\n\t}\n\n\tremoveProvider(providerId: string): void {\n\t\tthis.enqueue({\n\t\t\tkind: \"removeProvider\",\n\t\t\tseq: this.nextSeq(),\n\t\t\tproviderId,\n\t\t});\n\t}\n\n\t/** Queue several ops but schedule a single save (batch import, built-in data apply). */\n\tbatch(mutate: () => void): void {\n\t\tthis.batching = true;\n\t\ttry {\n\t\t\tmutate();\n\t\t} finally {\n\t\t\tthis.batching = false;\n\t\t}\n\t\tif (this.ops.length > 0) this.scheduleSave();\n\t}\n\n\t/** Drop all pending ops targeting a draft provider (used when leaving its editor untouched). */\n\tdiscardProviderDraft(providerId: string): void {\n\t\tthis.drafts.delete(providerId);\n\t\tif (Object.hasOwn(this.baseline.providers, providerId)) return;\n\t\tthis.ops = this.ops.filter((op) => op.providerId !== providerId);\n\t\tthis.recomputeView();\n\t}\n\n\t// ---------------------------------------------------------------------\n\t// View computation\n\t// ---------------------------------------------------------------------\n\n\tprivate recomputeView(): void {\n\t\tconst view = structuredClone(this.baseline);\n\t\tfor (const id of this.drafts) ensureProvider(view, id);\n\t\tfor (const op of this.ops) applyOp(view, op);\n\t\tthis.view = view;\n\t}\n\n\t// ---------------------------------------------------------------------\n\t// Saving\n\t// ---------------------------------------------------------------------\n\n\tprivate scheduleSave(): void {\n\t\tif (this.batching) return;\n\t\tthis.saveChain = this.saveChain\n\t\t\t.catch(() => {})\n\t\t\t.then(async () => {\n\t\t\t\tconst result = await this.save();\n\t\t\t\tthis.lastSaveResult = result;\n\t\t\t\tif (result.kind !== \"clean\") this.onSaveResult?.(result);\n\t\t\t});\n\t}\n\n\t/** Wait for all scheduled saves; the settled result of the last save, if any. */\n\tasync flush(): Promise<void> {\n\t\tlet pending: Promise<void>;\n\t\tdo {\n\t\t\tpending = this.saveChain;\n\t\t\tawait pending;\n\t\t} while (pending !== this.saveChain);\n\t}\n\n\tprivate async save(): Promise<SaveResult> {\n\t\tif (this.ops.length === 0) return { kind: \"clean\" };\n\t\ttry {\n\t\t\treturn await withFileMutationQueue(this.path, async () => {\n\t\t\t\tawait mkdir(dirname(this.path), { recursive: true });\n\t\t\t\tlet compromised: Error | undefined;\n\t\t\t\tconst release = await lockfile.lock(this.path, {\n\t\t\t\t\trealpath: false,\n\t\t\t\t\tstale: 30_000,\n\t\t\t\t\tretries: { retries: 20, factor: 1.2, minTimeout: 50, maxTimeout: 250 },\n\t\t\t\t\tonCompromised: (error) => {\n\t\t\t\t\t\tcompromised = error;\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tlet result: SaveResult;\n\t\t\t\ttry {\n\t\t\t\t\tresult = await this.saveLocked(() => {\n\t\t\t\t\t\tif (compromised) throw compromised;\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tresult = { kind: \"error\", error: `Failed to save models.json: ${formatError(error)}` };\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!compromised && result.kind !== \"error\" && result.kind !== \"invalid\") {\n\t\t\t\t\t\tresult = { kind: \"error\", error: `Could not release the models.json lock: ${formatError(error)}` };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treturn { kind: \"error\", error: `Failed to access models.json for saving: ${formatError(error)}` };\n\t\t}\n\t}\n\n\tprivate async saveLocked(assertLock: () => void): Promise<SaveResult> {\n\t\tconst tempPath = join(dirname(this.path), `.models.${randomUUID()}.tmp`);\n\t\ttry {\n\t\t\tassertLock();\n\t\t\tlet disk: DiskRead;\n\t\t\ttry {\n\t\t\t\tdisk = await readDocument(this.path);\n\t\t\t} catch (error) {\n\t\t\t\t// Never clobber a file we cannot parse.\n\t\t\t\treturn { kind: \"error\", error: `models.json changed on disk and is unreadable: ${formatError(error)}` };\n\t\t\t}\n\t\t\tconst working = structuredClone(disk.doc);\n\t\t\tconst batch = this.ops.filter((op) => {\n\t\t\t\tconst provider = this.getProvider(op.providerId);\n\t\t\t\treturn (\n\t\t\t\t\tObject.hasOwn(disk.doc.providers, op.providerId) ||\n\t\t\t\t\t!provider ||\n\t\t\t\t\tObject.keys(provider).length === 0 ||\n\t\t\t\t\thasProviderSettings(provider)\n\t\t\t\t);\n\t\t\t});\n\t\t\t// Apply blindly onto the freshest disk content: unrelated external\n\t\t\t// edits are preserved, a same-field race resolves last-writer-wins,\n\t\t\t// and ops targeting externally removed models simply miss.\n\t\t\tfor (const op of batch) applyOp(working, op);\n\t\t\tpruneEmptyNewProviders(working, disk.doc);\n\t\t\tconst settle = () => {\n\t\t\t\tconst settled = new Set(batch.map((op) => op.seq));\n\t\t\t\tthis.ops = this.ops.filter((op) => !settled.has(op.seq));\n\t\t\t\tthis.recomputeView();\n\t\t\t};\n\t\t\tif (jsonEquals(working, disk.doc)) {\n\t\t\t\t// Ops cancelled each other out or already matched disk; nothing to write.\n\t\t\t\tthis.baseline = disk.doc;\n\t\t\t\tsettle();\n\t\t\t\treturn { kind: \"saved\", applied: batch.length };\n\t\t\t}\n\t\t\tconst candidate = `${JSON.stringify(working, null, 2)}\\n`;\n\t\t\tawait writeFile(tempPath, candidate, { encoding: \"utf8\", mode: 0o600 });\n\t\t\tconst check = await ModelConfig.load(tempPath);\n\t\t\tconst validationError = check.getError();\n\t\t\tif (validationError) {\n\t\t\t\treturn { kind: \"invalid\", error: validationError };\n\t\t\t}\n\t\t\tconst latest = await readDocument(this.path);\n\t\t\tif (latest.exists !== disk.exists || latest.rawText !== disk.rawText) {\n\t\t\t\tthrow new Error(\"models.json changed while saving; the edits were kept for retry.\");\n\t\t\t}\n\t\t\tif (!this.backedUp && disk.exists && disk.rawText !== undefined) {\n\t\t\t\tawait writeFile(`${this.path}.bak`, disk.rawText, { encoding: \"utf8\", mode: 0o600 });\n\t\t\t\tthis.backedUp = true;\n\t\t\t}\n\t\t\tassertLock();\n\t\t\tawait rename(tempPath, this.path);\n\t\t\tthis.baseline = working;\n\t\t\tsettle();\n\t\t\treturn { kind: \"saved\", applied: batch.length };\n\t\t} finally {\n\t\t\tawait rm(tempPath, { force: true }).catch(() => {});\n\t\t}\n\t}\n}\n\n// -------------------------------------------------------------------------\n// Op application\n// -------------------------------------------------------------------------\n\nfunction ensureProvider(doc: ModelsJsonDocument, providerId: string): Record<string, unknown> {\n\tconst existing = Object.hasOwn(doc.providers, providerId) ? doc.providers[providerId] : undefined;\n\tif (existing) return existing;\n\tconst created: Record<string, unknown> = {};\n\tObject.defineProperty(doc.providers, providerId, {\n\t\tvalue: created,\n\t\twritable: true,\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t});\n\treturn created;\n}\n\nfunction applyOp(doc: ModelsJsonDocument, op: Op): void {\n\tswitch (op.kind) {\n\t\tcase \"set\": {\n\t\t\tconst provider = ensureProvider(doc, op.providerId);\n\t\t\tif (op.modelId === undefined) {\n\t\t\t\tsetPath(provider, op.path, op.value);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst models = Array.isArray(provider.models) ? (provider.models as Record<string, unknown>[]) : [];\n\t\t\tconst model = models.find((entry) => entry.id === op.modelId);\n\t\t\tif (model) setPath(model, op.path, op.value);\n\t\t\treturn;\n\t\t}\n\t\tcase \"addModel\": {\n\t\t\tconst provider = ensureProvider(doc, op.providerId);\n\t\t\tconst models = Array.isArray(provider.models) ? (provider.models as Record<string, unknown>[]) : [];\n\t\t\tconst index = models.findIndex((entry) => entry.id === op.model.id);\n\t\t\tconst clone = structuredClone(op.model) as Record<string, unknown>;\n\t\t\tif (index >= 0) models[index] = clone;\n\t\t\telse models.push(clone);\n\t\t\tprovider.models = models;\n\t\t\treturn;\n\t\t}\n\t\tcase \"removeModel\": {\n\t\t\tconst provider = doc.providers[op.providerId] as Record<string, unknown> | undefined;\n\t\t\tconst models = Array.isArray(provider?.models) ? (provider.models as Record<string, unknown>[]) : undefined;\n\t\t\tif (!provider || !models) return;\n\t\t\tprovider.models = models.filter((entry) => entry.id !== op.modelId);\n\t\t\treturn;\n\t\t}\n\t\tcase \"renameModel\": {\n\t\t\tconst provider = doc.providers[op.providerId] as Record<string, unknown> | undefined;\n\t\t\tconst models = Array.isArray(provider?.models) ? (provider.models as Record<string, unknown>[]) : undefined;\n\t\t\tif (!models) return;\n\t\t\tconst model = models.find((entry) => entry.id === op.oldId);\n\t\t\tif (model && !models.some((entry) => entry.id === op.newId)) model.id = op.newId;\n\t\t\treturn;\n\t\t}\n\t\tcase \"removeProvider\": {\n\t\t\tdelete doc.providers[op.providerId];\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n/** Remove providers the ops materialized but which ended up with no keys (self-cancelled drafts). */\nfunction pruneEmptyNewProviders(working: ModelsJsonDocument, disk: ModelsJsonDocument): void {\n\tfor (const [providerId, provider] of Object.entries(working.providers)) {\n\t\tif (Object.hasOwn(disk.providers, providerId)) continue;\n\t\tif (isPlainObject(provider) && Object.keys(provider).length === 0) delete working.providers[providerId];\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/extensions/provider/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Op-based models.json editing store.\n *\n * Three states are tracked: `baseline` (last known disk content), pending\n * field-level ops, and `view` (baseline + ops, what the UI renders). Every\n * confirmed edit queues an op and schedules a save. A save re-reads the file\n * under a cross-process lock and applies the pending ops onto that freshest\n * content — unrelated external edits are preserved, a same-field race\n * resolves last-writer-wins, and ops targeting externally removed models\n * simply miss. The candidate is validated via ModelConfig on a temp file,\n * backed up once per session, and atomically renamed into place. Failed\n * saves keep the ops pending; closing offers retry or explicit discard.\n * Whole-snapshot blind overwrites are never performed.\n *\n * Comments and custom formatting are normalized to two-space JSON on save;\n * unknown fields anywhere in the document are preserved verbatim.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, realpath, rename, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { ModelConfig, type ModelsJsonModel, type ModelsJsonProvider } from \"../../core/model-config.ts\";\nimport { withFileMutationQueue } from \"../../core/tools/file-mutation-queue.ts\";\nimport { stripJsonComments } from \"../../utils/json.ts\";\nimport { normalizePath } from \"../../utils/paths.ts\";\nimport { stripBom } from \"../../utils/text.ts\";\nimport { hasProviderSettings } from \"./configuration.ts\";\nimport { formatError } from \"./constants.ts\";\nimport { DELETE, setPath } from \"./json-fields.ts\";\n\nexport { DELETE, type DeleteMarker } from \"./json-fields.ts\";\n\nexport interface ModelsJsonDocument {\n\tproviders: Record<string, ModelsJsonProvider>;\n\t[key: string]: unknown;\n}\n\ninterface OpBase {\n\tseq: number;\n}\nexport interface SetFieldOp extends OpBase {\n\tkind: \"set\";\n\tproviderId: string;\n\t/** Undefined for provider-level fields. */\n\tmodelId?: string;\n\t/** Path within the provider/model object, e.g. [\"compat\", \"allowEmptySignature\"]. */\n\tpath: readonly string[];\n\tvalue: unknown;\n\tcreateProvider: boolean;\n}\nexport interface AddModelOp extends OpBase {\n\tkind: \"addModel\";\n\tproviderId: string;\n\tmodel: ModelsJsonModel;\n\tcreateProvider: boolean;\n}\nexport interface RemoveModelOp extends OpBase {\n\tkind: \"removeModel\";\n\tproviderId: string;\n\tmodelId: string;\n}\nexport interface RenameModelOp extends OpBase {\n\tkind: \"renameModel\";\n\tproviderId: string;\n\toldId: string;\n\tnewId: string;\n}\nexport interface RemoveProviderOp extends OpBase {\n\tkind: \"removeProvider\";\n\tproviderId: string;\n}\nexport type Op = SetFieldOp | AddModelOp | RemoveModelOp | RenameModelOp | RemoveProviderOp;\n\nexport type SaveResult =\n\t| { kind: \"clean\" }\n\t| { kind: \"saved\"; applied: number }\n\t| { kind: \"invalid\"; error: string }\n\t| { kind: \"error\"; error: string };\n\nexport type StoreLoad = { ok: true; store: ModelsJsonStore } | { ok: false; error: string };\n\ninterface DiskRead {\n\texists: boolean;\n\tdoc: ModelsJsonDocument;\n\trawText?: string;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Canonical comparison for JSON values, insensitive to object key order. */\nexport function jsonEquals(a: unknown, b: unknown): boolean {\n\tif (a === b) return true;\n\tif (Array.isArray(a) && Array.isArray(b)) {\n\t\treturn a.length === b.length && a.every((entry, index) => jsonEquals(entry, b[index]));\n\t}\n\tif (isPlainObject(a) && isPlainObject(b)) {\n\t\tconst aKeys = Object.keys(a).sort();\n\t\tconst bKeys = Object.keys(b).sort();\n\t\treturn (\n\t\t\taKeys.length === bKeys.length &&\n\t\t\taKeys.every((key, index) => key === bKeys[index] && jsonEquals(a[key], b[key]))\n\t\t);\n\t}\n\treturn false;\n}\n\nfunction cloneValue(value: unknown): unknown {\n\treturn value === DELETE ? value : structuredClone(value);\n}\n\nasync function readDocument(path: string): Promise<DiskRead> {\n\tlet rawText: string;\n\ttry {\n\t\trawText = await readFile(path, \"utf8\");\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { exists: false, doc: { providers: {} } };\n\t\tthrow error;\n\t}\n\tconst parsed: unknown = JSON.parse(stripJsonComments(stripBom(rawText)));\n\tif (!isPlainObject(parsed)) throw new Error(\"models.json root must be a JSON object.\");\n\tconst doc = parsed as ModelsJsonDocument;\n\tif (!isPlainObject(doc.providers)) throw new Error('models.json \"providers\" must be an object.');\n\treturn { exists: true, doc, rawText };\n}\n\nexport class ModelsJsonStore {\n\treadonly path: string;\n\tprivate baseline: ModelsJsonDocument;\n\tprivate ops: Op[] = [];\n\tprivate view: ModelsJsonDocument;\n\tprivate seq = 0;\n\tprivate backedUp = false;\n\tprivate batching = false;\n\tprivate saveChain: Promise<void> = Promise.resolve();\n\tprivate readonly drafts = new Set<string>();\n\tprivate lastSaveResult: SaveResult = { kind: \"clean\" };\n\t/** Called after each save settles (success, conflict, or failure). */\n\tonSaveResult: ((result: SaveResult) => void) | undefined;\n\n\tprivate constructor(path: string, baseline: ModelsJsonDocument) {\n\t\tthis.path = path;\n\t\tthis.baseline = baseline;\n\t\tthis.view = structuredClone(baseline);\n\t}\n\n\tstatic async load(path: string): Promise<StoreLoad> {\n\t\tlet normalized = normalizePath(path);\n\t\tlet disk: DiskRead;\n\t\ttry {\n\t\t\tnormalized = await realpath(normalized).catch((error: NodeJS.ErrnoException) => {\n\t\t\t\tif (error.code === \"ENOENT\") return normalized;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t\tdisk = await readDocument(normalized);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: `Failed to parse models.json: ${formatError(error)}\\n\\nFile: ${normalized}` };\n\t\t}\n\t\tif (disk.exists) {\n\t\t\t// Surface schema errors as a read-only state; never auto-overwrite an invalid file.\n\t\t\tconst check = await ModelConfig.load(normalized);\n\t\t\tconst validationError = check.getError();\n\t\t\tif (validationError) return { ok: false, error: validationError };\n\t\t}\n\t\treturn { ok: true, store: new ModelsJsonStore(normalized, disk.doc) };\n\t}\n\n\t// ---------------------------------------------------------------------\n\t// View access (read-only for the UI; mutations go through the methods)\n\t// ---------------------------------------------------------------------\n\n\tgetDoc(): ModelsJsonDocument {\n\t\treturn this.view;\n\t}\n\n\tgetProviderIds(): string[] {\n\t\treturn [...new Set([...Object.keys(this.view.providers), ...this.ops.map((op) => op.providerId)])];\n\t}\n\n\tgetProvider(providerId: string): ModelsJsonProvider | undefined {\n\t\treturn Object.hasOwn(this.view.providers, providerId) ? this.view.providers[providerId] : undefined;\n\t}\n\n\t/** Materialize a draft provider in the view without queueing an op (never written while empty). */\n\tensureProviderView(providerId: string): void {\n\t\tif (Object.hasOwn(this.view.providers, providerId)) return;\n\t\tthis.drafts.add(providerId);\n\t\tthis.recomputeView();\n\t}\n\n\t/** Provider present in the view but not yet on disk. */\n\tisDraftProvider(providerId: string): boolean {\n\t\treturn !Object.hasOwn(this.baseline.providers, providerId) && Object.hasOwn(this.view.providers, providerId);\n\t}\n\n\tgetModels(providerId: string): ModelsJsonModel[] {\n\t\tconst provider = this.getProvider(providerId);\n\t\treturn Array.isArray(provider?.models) ? provider.models : [];\n\t}\n\n\tgetModel(providerId: string, modelId: string): ModelsJsonModel | undefined {\n\t\treturn this.getModels(providerId).find((model) => model.id === modelId);\n\t}\n\n\tget pendingCount(): number {\n\t\treturn this.ops.length;\n\t}\n\n\t/** The latest save failed; pending edits remain available for retry. */\n\tget saveFailed(): boolean {\n\t\treturn this.lastSaveResult.kind === \"error\" || this.lastSaveResult.kind === \"invalid\";\n\t}\n\n\tgetPendingError(providerId?: string): string | undefined {\n\t\tconst pending = this.ops.filter((op) => providerId === undefined || op.providerId === providerId);\n\t\tif (pending.length === 0) return undefined;\n\t\tif (this.lastSaveResult.kind === \"error\" || this.lastSaveResult.kind === \"invalid\")\n\t\t\treturn this.lastSaveResult.error;\n\t\treturn `${pending.length} change(s) are not saved; retry or discard before continuing.`;\n\t}\n\n\tretrySave(): void {\n\t\tif (this.ops.length > 0) this.scheduleSave();\n\t}\n\n\t/** Used only after flush, when the user explicitly discards unsaved edits. */\n\tdiscardPending(): void {\n\t\tthis.ops = [];\n\t\tthis.drafts.clear();\n\t\tthis.recomputeView();\n\t}\n\n\t// ---------------------------------------------------------------------\n\t// Mutations (queue an op, refresh the view, schedule a save)\n\t// ---------------------------------------------------------------------\n\n\tprivate enqueue(op: Op): void {\n\t\tthis.ops.push(op);\n\t\tthis.recomputeView();\n\t\tthis.scheduleSave();\n\t}\n\n\tprivate nextSeq(): number {\n\t\treturn ++this.seq;\n\t}\n\n\tsetProviderField(providerId: string, path: readonly string[], value: unknown): void {\n\t\tthis.enqueue({\n\t\t\tkind: \"set\",\n\t\t\tseq: this.nextSeq(),\n\t\t\tproviderId,\n\t\t\tpath,\n\t\t\tvalue: cloneValue(value),\n\t\t\tcreateProvider: !Object.hasOwn(this.baseline.providers, providerId),\n\t\t});\n\t}\n\n\tsetModelField(providerId: string, modelId: string, path: readonly string[], value: unknown): void {\n\t\tthis.enqueue({\n\t\t\tkind: \"set\",\n\t\t\tseq: this.nextSeq(),\n\t\t\tproviderId,\n\t\t\tmodelId,\n\t\t\tpath,\n\t\t\tvalue: cloneValue(value),\n\t\t\tcreateProvider: !Object.hasOwn(this.baseline.providers, providerId),\n\t\t});\n\t}\n\n\taddModel(providerId: string, model: ModelsJsonModel): void {\n\t\tthis.enqueue({\n\t\t\tkind: \"addModel\",\n\t\t\tseq: this.nextSeq(),\n\t\t\tproviderId,\n\t\t\tmodel: structuredClone(model),\n\t\t\tcreateProvider: !Object.hasOwn(this.baseline.providers, providerId),\n\t\t});\n\t}\n\n\tremoveModel(providerId: string, modelId: string): void {\n\t\tthis.enqueue({\n\t\t\tkind: \"removeModel\",\n\t\t\tseq: this.nextSeq(),\n\t\t\tproviderId,\n\t\t\tmodelId,\n\t\t});\n\t}\n\n\tasync renameModel(providerId: string, oldId: string, newId: string): Promise<string | undefined> {\n\t\tawait this.flush();\n\t\tconst pendingError = this.getPendingError(providerId);\n\t\tif (pendingError) return pendingError;\n\t\tconst before = this.getModel(providerId, oldId);\n\t\tif (!before) return \"The model no longer exists.\";\n\t\tconst seq = this.nextSeq();\n\t\tthis.enqueue({\n\t\t\tkind: \"renameModel\",\n\t\t\tseq,\n\t\t\tproviderId,\n\t\t\toldId,\n\t\t\tnewId,\n\t\t});\n\t\tawait this.flush();\n\t\tif (this.ops.some((op) => op.seq === seq)) {\n\t\t\t// The save failed; drop the rename rather than surprising the user later.\n\t\t\tthis.ops = this.ops.filter((op) => op.seq !== seq);\n\t\t\tthis.recomputeView();\n\t\t\treturn this.getPendingError(providerId) ?? \"Failed to save models.json.\";\n\t\t}\n\t\t// A settled rename can still have missed when the model moved on disk; never\n\t\t// adopt an unrelated model that already uses the destination id.\n\t\tconst renamed = this.getModel(providerId, newId);\n\t\treturn renamed && jsonEquals(renamed, { ...before, id: newId })\n\t\t\t? undefined\n\t\t\t: \"The model changed on disk; reopen it before renaming.\";\n\t}\n\n\tremoveProvider(providerId: string): void {\n\t\tthis.enqueue({\n\t\t\tkind: \"removeProvider\",\n\t\t\tseq: this.nextSeq(),\n\t\t\tproviderId,\n\t\t});\n\t}\n\n\t/** Queue several ops but schedule a single save (batch import, built-in data apply). */\n\tbatch(mutate: () => void): void {\n\t\tthis.batching = true;\n\t\ttry {\n\t\t\tmutate();\n\t\t} finally {\n\t\t\tthis.batching = false;\n\t\t}\n\t\tif (this.ops.length > 0) this.scheduleSave();\n\t}\n\n\t/** Drop all pending ops targeting a draft provider (used when leaving its editor untouched). */\n\tdiscardProviderDraft(providerId: string): void {\n\t\tthis.drafts.delete(providerId);\n\t\tif (Object.hasOwn(this.baseline.providers, providerId)) return;\n\t\tthis.ops = this.ops.filter((op) => op.providerId !== providerId);\n\t\tthis.recomputeView();\n\t}\n\n\t// ---------------------------------------------------------------------\n\t// View computation\n\t// ---------------------------------------------------------------------\n\n\tprivate recomputeView(): void {\n\t\tconst view = structuredClone(this.baseline);\n\t\tfor (const id of this.drafts) ensureProvider(view, id);\n\t\tfor (const op of this.ops) applyOp(view, op);\n\t\tthis.view = view;\n\t}\n\n\t// ---------------------------------------------------------------------\n\t// Saving\n\t// ---------------------------------------------------------------------\n\n\tprivate scheduleSave(): void {\n\t\tif (this.batching) return;\n\t\tthis.saveChain = this.saveChain\n\t\t\t.catch(() => {})\n\t\t\t.then(async () => {\n\t\t\t\tconst result = await this.save();\n\t\t\t\tthis.lastSaveResult = result;\n\t\t\t\tif (result.kind !== \"clean\") this.onSaveResult?.(result);\n\t\t\t});\n\t}\n\n\t/** Wait for all scheduled saves; the settled result of the last save, if any. */\n\tasync flush(): Promise<void> {\n\t\tlet pending: Promise<void>;\n\t\tdo {\n\t\t\tpending = this.saveChain;\n\t\t\tawait pending;\n\t\t} while (pending !== this.saveChain);\n\t}\n\n\tprivate async save(): Promise<SaveResult> {\n\t\tif (this.ops.length === 0) return { kind: \"clean\" };\n\t\ttry {\n\t\t\treturn await withFileMutationQueue(this.path, async () => {\n\t\t\t\tawait mkdir(dirname(this.path), { recursive: true });\n\t\t\t\tlet compromised: Error | undefined;\n\t\t\t\tconst release = await lockfile.lock(this.path, {\n\t\t\t\t\trealpath: false,\n\t\t\t\t\tstale: 30_000,\n\t\t\t\t\tretries: { retries: 20, factor: 1.2, minTimeout: 50, maxTimeout: 250 },\n\t\t\t\t\tonCompromised: (error) => {\n\t\t\t\t\t\tcompromised = error;\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tlet result: SaveResult;\n\t\t\t\ttry {\n\t\t\t\t\tresult = await this.saveLocked(() => {\n\t\t\t\t\t\tif (compromised) throw compromised;\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tresult = { kind: \"error\", error: `Failed to save models.json: ${formatError(error)}` };\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!compromised && result.kind !== \"error\" && result.kind !== \"invalid\") {\n\t\t\t\t\t\tresult = { kind: \"error\", error: `Could not release the models.json lock: ${formatError(error)}` };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treturn { kind: \"error\", error: `Failed to access models.json for saving: ${formatError(error)}` };\n\t\t}\n\t}\n\n\tprivate async saveLocked(assertLock: () => void): Promise<SaveResult> {\n\t\tconst tempPath = join(dirname(this.path), `.models.${randomUUID()}.tmp`);\n\t\ttry {\n\t\t\tassertLock();\n\t\t\tlet disk: DiskRead;\n\t\t\ttry {\n\t\t\t\tdisk = await readDocument(this.path);\n\t\t\t} catch (error) {\n\t\t\t\t// Never clobber a file we cannot parse.\n\t\t\t\treturn { kind: \"error\", error: `models.json changed on disk and is unreadable: ${formatError(error)}` };\n\t\t\t}\n\t\t\tconst working = structuredClone(disk.doc);\n\t\t\tconst batch = this.ops.filter((op) => {\n\t\t\t\tconst provider = this.getProvider(op.providerId);\n\t\t\t\treturn (\n\t\t\t\t\tObject.hasOwn(disk.doc.providers, op.providerId) ||\n\t\t\t\t\t!provider ||\n\t\t\t\t\tObject.keys(provider).length === 0 ||\n\t\t\t\t\thasProviderSettings(provider)\n\t\t\t\t);\n\t\t\t});\n\t\t\t// Apply blindly onto the freshest disk content: unrelated external\n\t\t\t// edits are preserved, a same-field race resolves last-writer-wins,\n\t\t\t// and ops targeting externally removed models simply miss.\n\t\t\tfor (const op of batch) applyOp(working, op);\n\t\t\tpruneEmptyNewProviders(working, disk.doc);\n\t\t\tconst settle = () => {\n\t\t\t\tconst settled = new Set(batch.map((op) => op.seq));\n\t\t\t\tthis.ops = this.ops.filter((op) => !settled.has(op.seq));\n\t\t\t\tthis.recomputeView();\n\t\t\t};\n\t\t\tif (jsonEquals(working, disk.doc)) {\n\t\t\t\t// Ops cancelled each other out or already matched disk; nothing to write.\n\t\t\t\tthis.baseline = disk.doc;\n\t\t\t\tsettle();\n\t\t\t\treturn { kind: \"saved\", applied: batch.length };\n\t\t\t}\n\t\t\tconst candidate = `${JSON.stringify(working, null, 2)}\\n`;\n\t\t\tawait writeFile(tempPath, candidate, { encoding: \"utf8\", mode: 0o600 });\n\t\t\tconst check = await ModelConfig.load(tempPath);\n\t\t\tconst validationError = check.getError();\n\t\t\tif (validationError) {\n\t\t\t\treturn { kind: \"invalid\", error: validationError };\n\t\t\t}\n\t\t\tconst latest = await readDocument(this.path);\n\t\t\tif (latest.exists !== disk.exists || latest.rawText !== disk.rawText) {\n\t\t\t\tthrow new Error(\"models.json changed while saving; the edits were kept for retry.\");\n\t\t\t}\n\t\t\tif (!this.backedUp && disk.exists && disk.rawText !== undefined) {\n\t\t\t\tawait writeFile(`${this.path}.bak`, disk.rawText, { encoding: \"utf8\", mode: 0o600 });\n\t\t\t\tthis.backedUp = true;\n\t\t\t}\n\t\t\tassertLock();\n\t\t\tawait rename(tempPath, this.path);\n\t\t\tthis.baseline = working;\n\t\t\tsettle();\n\t\t\treturn { kind: \"saved\", applied: batch.length };\n\t\t} finally {\n\t\t\tawait rm(tempPath, { force: true }).catch(() => {});\n\t\t}\n\t}\n}\n\n// -------------------------------------------------------------------------\n// Op application\n// -------------------------------------------------------------------------\n\nfunction ensureProvider(doc: ModelsJsonDocument, providerId: string): Record<string, unknown> {\n\tconst existing = Object.hasOwn(doc.providers, providerId) ? doc.providers[providerId] : undefined;\n\tif (existing) return existing;\n\tconst created: Record<string, unknown> = {};\n\tObject.defineProperty(doc.providers, providerId, {\n\t\tvalue: created,\n\t\twritable: true,\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t});\n\treturn created;\n}\n\nfunction applyOp(doc: ModelsJsonDocument, op: Op): void {\n\tswitch (op.kind) {\n\t\tcase \"set\": {\n\t\t\tconst provider = ensureProvider(doc, op.providerId);\n\t\t\tif (op.modelId === undefined) {\n\t\t\t\tsetPath(provider, op.path, op.value);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst models = Array.isArray(provider.models) ? (provider.models as Record<string, unknown>[]) : [];\n\t\t\tconst model = models.find((entry) => entry.id === op.modelId);\n\t\t\tif (model) setPath(model, op.path, op.value);\n\t\t\treturn;\n\t\t}\n\t\tcase \"addModel\": {\n\t\t\tconst provider = ensureProvider(doc, op.providerId);\n\t\t\tconst models = Array.isArray(provider.models) ? (provider.models as Record<string, unknown>[]) : [];\n\t\t\tconst index = models.findIndex((entry) => entry.id === op.model.id);\n\t\t\tconst clone = structuredClone(op.model) as Record<string, unknown>;\n\t\t\tif (index >= 0) models[index] = clone;\n\t\t\telse models.push(clone);\n\t\t\tprovider.models = models;\n\t\t\treturn;\n\t\t}\n\t\tcase \"removeModel\": {\n\t\t\tconst provider = doc.providers[op.providerId] as Record<string, unknown> | undefined;\n\t\t\tconst models = Array.isArray(provider?.models) ? (provider.models as Record<string, unknown>[]) : undefined;\n\t\t\tif (!provider || !models) return;\n\t\t\tprovider.models = models.filter((entry) => entry.id !== op.modelId);\n\t\t\treturn;\n\t\t}\n\t\tcase \"renameModel\": {\n\t\t\tconst provider = doc.providers[op.providerId] as Record<string, unknown> | undefined;\n\t\t\tconst models = Array.isArray(provider?.models) ? (provider.models as Record<string, unknown>[]) : undefined;\n\t\t\tif (!models) return;\n\t\t\tconst model = models.find((entry) => entry.id === op.oldId);\n\t\t\tif (model && !models.some((entry) => entry.id === op.newId)) model.id = op.newId;\n\t\t\treturn;\n\t\t}\n\t\tcase \"removeProvider\": {\n\t\t\tdelete doc.providers[op.providerId];\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n/** Remove providers the ops materialized but which ended up with no keys (self-cancelled drafts). */\nfunction pruneEmptyNewProviders(working: ModelsJsonDocument, disk: ModelsJsonDocument): void {\n\tfor (const [providerId, provider] of Object.entries(working.providers)) {\n\t\tif (Object.hasOwn(disk.providers, providerId)) continue;\n\t\tif (isPlainObject(provider) && Object.keys(provider).length === 0) delete working.providers[providerId];\n\t}\n}\n"]}
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Two-pane provider editor.
|
|
3
3
|
*
|
|
4
|
-
* Left column
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* fixed window with a (n/N) position indicator instead of resizing, and the
|
|
10
|
-
* selected row stays accent in both panes while the unfocused pane's other
|
|
11
|
-
* rows dim back. Typing on a value row overwrites, Enter tweaks or enters,
|
|
12
|
-
* ←/→ switch panes, Esc cancels/pops/backs out.
|
|
4
|
+
* Left column groups connection, models, and management actions. The right
|
|
5
|
+
* column hosts the selected item's field pane and sub-pane stack. Both columns
|
|
6
|
+
* scroll inside a fixed-height frame; below the wide-layout threshold, only
|
|
7
|
+
* the focused column is shown. Typing on a value row overwrites, Enter tweaks
|
|
8
|
+
* or enters, ←/→ switch panes, and Esc cancels/pops/backs out.
|
|
13
9
|
*/
|
|
14
10
|
import "../keybindings.ts";
|
|
15
11
|
import { type Component, type Focusable, type TUI } from "@earendil-works/pi-tui";
|
|
@@ -82,6 +78,8 @@ export declare class ProviderEditorScreen implements Component, Focusable {
|
|
|
82
78
|
private commitModelDraft;
|
|
83
79
|
private refresh;
|
|
84
80
|
private renderFrame;
|
|
81
|
+
private renderRight;
|
|
82
|
+
private renderPaneTitle;
|
|
85
83
|
/** Content-driven left width: fits the longest visible label, clamped to 16–40 and 45% of the terminal. */
|
|
86
84
|
private computeLeftWidth;
|
|
87
85
|
private renderTitle;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../../src/extensions/provider/ui/editor.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../../src/extensions/provider/ui/editor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Two-pane provider editor.
|
|
3
3
|
*
|
|
4
|
-
* Left column
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* fixed window with a (n/N) position indicator instead of resizing, and the
|
|
10
|
-
* selected row stays accent in both panes while the unfocused pane's other
|
|
11
|
-
* rows dim back. Typing on a value row overwrites, Enter tweaks or enters,
|
|
12
|
-
* ←/→ switch panes, Esc cancels/pops/backs out.
|
|
4
|
+
* Left column groups connection, models, and management actions. The right
|
|
5
|
+
* column hosts the selected item's field pane and sub-pane stack. Both columns
|
|
6
|
+
* scroll inside a fixed-height frame; below the wide-layout threshold, only
|
|
7
|
+
* the focused column is shown. Typing on a value row overwrites, Enter tweaks
|
|
8
|
+
* or enters, ←/→ switch panes, and Esc cancels/pops/backs out.
|
|
13
9
|
*/
|
|
14
10
|
import "../keybindings.js";
|
|
15
11
|
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
@@ -28,7 +24,8 @@ const LEFT_WIDTH_MIN = 16;
|
|
|
28
24
|
const LEFT_WIDTH_MAX = 40;
|
|
29
25
|
/** Fixed editor body height: both columns scroll inside it rather than resizing the frame. */
|
|
30
26
|
const BODY_ROWS = 14;
|
|
31
|
-
const
|
|
27
|
+
const DOUBLE_PANE_MIN_WIDTH = 72;
|
|
28
|
+
const MIN_WIDTH = 32;
|
|
32
29
|
function padTo(line, width) {
|
|
33
30
|
const missing = width - visibleWidth(line);
|
|
34
31
|
return missing > 0 ? line + " ".repeat(missing) : line;
|
|
@@ -40,6 +37,8 @@ function leftItemKey(item) {
|
|
|
40
37
|
switch (item.kind) {
|
|
41
38
|
case "model":
|
|
42
39
|
return `model:${item.modelId}`;
|
|
40
|
+
case "section":
|
|
41
|
+
return `section:${item.label}`;
|
|
43
42
|
default:
|
|
44
43
|
return item.kind;
|
|
45
44
|
}
|
|
@@ -60,6 +59,7 @@ export class ProviderEditorScreen {
|
|
|
60
59
|
this.edits = new ProviderEdits(options.store, options.providerId, options.refresher, () => this.refresh());
|
|
61
60
|
this.host = this.createHost();
|
|
62
61
|
this.rebuildLeftItems();
|
|
62
|
+
this.leftIndex = this.selectableIndex(0);
|
|
63
63
|
this.resetRightStack();
|
|
64
64
|
}
|
|
65
65
|
// ------------------------------------------------------------------
|
|
@@ -134,7 +134,10 @@ export class ProviderEditorScreen {
|
|
|
134
134
|
const selectedKey = leftItemKey(selected);
|
|
135
135
|
this.rebuildLeftItems();
|
|
136
136
|
const nextIndex = this.leftItems.findIndex((item) => leftItemKey(item) === selectedKey);
|
|
137
|
-
this.leftIndex =
|
|
137
|
+
this.leftIndex =
|
|
138
|
+
nextIndex >= 0
|
|
139
|
+
? nextIndex
|
|
140
|
+
: this.selectableIndex(Math.min(this.leftIndex, Math.max(0, this.leftItems.length - 1)));
|
|
138
141
|
// If the selected model vanished externally, rebuild the right column.
|
|
139
142
|
if (nextIndex < 0 && selected?.kind === "model") {
|
|
140
143
|
this.resetRightStack();
|
|
@@ -145,23 +148,28 @@ export class ProviderEditorScreen {
|
|
|
145
148
|
// Left column
|
|
146
149
|
// ------------------------------------------------------------------
|
|
147
150
|
rebuildLeftItems() {
|
|
148
|
-
const items = [
|
|
151
|
+
const items = [
|
|
152
|
+
{ kind: "section", label: "SETUP" },
|
|
153
|
+
{ kind: "apiAuth" },
|
|
154
|
+
{ kind: "fetch" },
|
|
155
|
+
{ kind: "section", label: "MODELS" },
|
|
156
|
+
];
|
|
149
157
|
for (const model of this.options.store.getModels(this.options.providerId)) {
|
|
150
158
|
items.push({ kind: "model", modelId: model.id });
|
|
151
159
|
}
|
|
152
160
|
if (this.draft)
|
|
153
161
|
items.push({ kind: "draft" });
|
|
154
|
-
items.push({ kind: "
|
|
162
|
+
items.push({ kind: "section", label: "MANAGE" }, { kind: "addModel" }, { kind: "deleteProvider" });
|
|
155
163
|
this.leftItems = items;
|
|
156
164
|
}
|
|
157
165
|
selectableIndex(start) {
|
|
158
|
-
const index = this.leftItems.findIndex((item, i) => i >= start && item.kind !== "
|
|
159
|
-
return index >= 0 ? index :
|
|
166
|
+
const index = this.leftItems.findIndex((item, i) => i >= start && item.kind !== "section");
|
|
167
|
+
return index >= 0 ? index : this.leftItems.findIndex((item) => item.kind !== "section");
|
|
160
168
|
}
|
|
161
169
|
moveLeft(delta) {
|
|
162
170
|
const selectable = this.leftItems
|
|
163
171
|
.map((item, index) => ({ item, index }))
|
|
164
|
-
.filter((entry) => entry.item.kind !== "
|
|
172
|
+
.filter((entry) => entry.item.kind !== "section");
|
|
165
173
|
if (selectable.length === 0)
|
|
166
174
|
return;
|
|
167
175
|
const position = selectable.findIndex((entry) => entry.index === this.leftIndex);
|
|
@@ -236,7 +244,7 @@ export class ProviderEditorScreen {
|
|
|
236
244
|
const builtin = isBuiltinProviderId(this.options.providerId);
|
|
237
245
|
this.host.confirm(`Delete provider "${this.options.providerId}" from models.json?` +
|
|
238
246
|
(builtin ? " The built-in catalog remains and its models may reappear." : "") +
|
|
239
|
-
" Stored credentials in auth.json are kept.", "Delete
|
|
247
|
+
" Stored credentials in auth.json are kept.", "Delete provider", () => {
|
|
240
248
|
this.edits.removeProvider();
|
|
241
249
|
this.done("deleted");
|
|
242
250
|
});
|
|
@@ -252,8 +260,8 @@ export class ProviderEditorScreen {
|
|
|
252
260
|
const rows = [];
|
|
253
261
|
const renderRow = (index) => {
|
|
254
262
|
const item = this.leftItems[index];
|
|
255
|
-
if (item.kind === "
|
|
256
|
-
return theme.fg("
|
|
263
|
+
if (item.kind === "section") {
|
|
264
|
+
return theme.fg("dim", item.label);
|
|
257
265
|
}
|
|
258
266
|
return this.renderLeftItem(item, index === this.leftIndex, focused, width);
|
|
259
267
|
};
|
|
@@ -267,7 +275,7 @@ export class ProviderEditorScreen {
|
|
|
267
275
|
const start = Math.max(0, Math.min(this.leftIndex - Math.floor(slots / 2), this.leftItems.length - slots));
|
|
268
276
|
for (let index = start; index < start + slots; index++)
|
|
269
277
|
rows.push(renderRow(index));
|
|
270
|
-
const selectable = this.leftItems.filter((item) => item.kind !== "
|
|
278
|
+
const selectable = this.leftItems.filter((item) => item.kind !== "section");
|
|
271
279
|
const position = selectable.indexOf(this.leftItems[this.leftIndex]);
|
|
272
280
|
rows.push(theme.fg("dim", ` (${position + 1}/${selectable.length})`));
|
|
273
281
|
}
|
|
@@ -298,10 +306,10 @@ export class ProviderEditorScreen {
|
|
|
298
306
|
break;
|
|
299
307
|
}
|
|
300
308
|
case "addModel":
|
|
301
|
-
text = "+ Add
|
|
309
|
+
text = "+ Add model";
|
|
302
310
|
break;
|
|
303
311
|
case "deleteProvider":
|
|
304
|
-
text = "Delete
|
|
312
|
+
text = "Delete provider";
|
|
305
313
|
break;
|
|
306
314
|
default:
|
|
307
315
|
text = "";
|
|
@@ -430,11 +438,11 @@ export class ProviderEditorScreen {
|
|
|
430
438
|
confirm: (message, actionLabel, action) => this.pushPane(new ConfirmPane(this.host, message, actionLabel, action)),
|
|
431
439
|
onModelRemoved: () => {
|
|
432
440
|
this.rebuildLeftItems();
|
|
433
|
-
// Land on the next row where the model was; walk back over
|
|
441
|
+
// Land on the next row where the model was; walk back over section labels.
|
|
434
442
|
this.leftIndex = Math.min(this.leftIndex, this.leftItems.length - 1);
|
|
435
|
-
while (this.leftIndex > 0 && this.leftItems[this.leftIndex]?.kind === "
|
|
443
|
+
while (this.leftIndex > 0 && this.leftItems[this.leftIndex]?.kind === "section")
|
|
436
444
|
this.leftIndex--;
|
|
437
|
-
if (this.leftItems[this.leftIndex]?.kind === "
|
|
445
|
+
if (this.leftItems[this.leftIndex]?.kind === "section")
|
|
438
446
|
this.leftIndex = this.selectableIndex(0);
|
|
439
447
|
this.focusPane = "left";
|
|
440
448
|
this.resetRightStack();
|
|
@@ -488,30 +496,66 @@ export class ProviderEditorScreen {
|
|
|
488
496
|
border,
|
|
489
497
|
];
|
|
490
498
|
}
|
|
491
|
-
const leftWidth = this.computeLeftWidth(width);
|
|
492
|
-
const rightWidth = width - leftWidth - 3;
|
|
493
|
-
const separator = theme.fg("border", " │ ");
|
|
494
499
|
const title = this.renderTitle(width);
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
const
|
|
504
|
-
const
|
|
505
|
-
body
|
|
500
|
+
let body;
|
|
501
|
+
if (width < DOUBLE_PANE_MIN_WIDTH) {
|
|
502
|
+
body = this.focusPane === "left" ? this.renderLeft(width) : this.renderRight(width);
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
const leftWidth = this.computeLeftWidth(width);
|
|
506
|
+
const rightWidth = width - leftWidth - 3;
|
|
507
|
+
const separator = theme.fg("border", " │ ");
|
|
508
|
+
const leftLines = this.renderLeft(leftWidth);
|
|
509
|
+
const rightLines = this.renderRight(rightWidth);
|
|
510
|
+
body = [];
|
|
511
|
+
for (let row = 0; row < BODY_ROWS; row++) {
|
|
512
|
+
const left = padTo(truncateToWidth(leftLines[row] ?? "", leftWidth), leftWidth);
|
|
513
|
+
const right = truncateToWidth(rightLines[row] ?? "", rightWidth);
|
|
514
|
+
body.push(left + separator + right);
|
|
515
|
+
}
|
|
506
516
|
}
|
|
507
517
|
const footerLines = this.renderFooter(width);
|
|
508
518
|
return [border, title, border, ...body, border, ...footerLines, border];
|
|
509
519
|
}
|
|
520
|
+
renderRight(width) {
|
|
521
|
+
const pane = this.topPane();
|
|
522
|
+
const paneLines = pane ? windowLines(this.theme, pane.render(width), BODY_ROWS - 1, pane.scrollWindow?.()) : [];
|
|
523
|
+
return [this.renderPaneTitle(width), ...paneLines];
|
|
524
|
+
}
|
|
525
|
+
renderPaneTitle(width) {
|
|
526
|
+
const item = this.leftItems[this.leftIndex];
|
|
527
|
+
let base = "";
|
|
528
|
+
switch (item?.kind) {
|
|
529
|
+
case "apiAuth":
|
|
530
|
+
base = "Connection";
|
|
531
|
+
break;
|
|
532
|
+
case "fetch":
|
|
533
|
+
base = "Discover models";
|
|
534
|
+
break;
|
|
535
|
+
case "model": {
|
|
536
|
+
const model = this.options.store.getModel(this.options.providerId, item.modelId);
|
|
537
|
+
base = model ? modelDisplayName(model) : item.modelId;
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
case "draft":
|
|
541
|
+
base = "New model";
|
|
542
|
+
break;
|
|
543
|
+
case "addModel":
|
|
544
|
+
base = "Add model";
|
|
545
|
+
break;
|
|
546
|
+
case "deleteProvider":
|
|
547
|
+
base = "Delete provider";
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
const crumb = this.stack.length > 1 ? this.topPane()?.crumb : undefined;
|
|
551
|
+
const title = crumb ? [base, crumb].filter(Boolean).join(" / ") : base;
|
|
552
|
+
return truncateToWidth(this.theme.fg("text", this.theme.bold(title)), width);
|
|
553
|
+
}
|
|
510
554
|
/** Content-driven left width: fits the longest visible label, clamped to 16–40 and 45% of the terminal. */
|
|
511
555
|
computeLeftWidth(width) {
|
|
512
556
|
let longest = 12;
|
|
513
557
|
for (const item of this.leftItems) {
|
|
514
|
-
if (item.kind === "
|
|
558
|
+
if (item.kind === "section")
|
|
515
559
|
continue;
|
|
516
560
|
const { text, note } = this.leftItemContent(item);
|
|
517
561
|
const label = text + (note ? ` ${note}` : "");
|
|
@@ -522,17 +566,28 @@ export class ProviderEditorScreen {
|
|
|
522
566
|
}
|
|
523
567
|
renderTitle(width) {
|
|
524
568
|
const theme = this.theme;
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
569
|
+
const title = theme.fg("accent", theme.bold(` Providers / ${this.options.providerId}`));
|
|
570
|
+
const status = this.options.store.pendingCount === 0 ? "" : this.options.store.saveFailed ? "Save failed" : "Saving…";
|
|
571
|
+
if (!status)
|
|
572
|
+
return truncateToWidth(title, width);
|
|
573
|
+
const styledStatus = theme.fg(this.options.store.saveFailed ? "error" : "dim", status);
|
|
574
|
+
const statusWidth = visibleWidth(status);
|
|
575
|
+
if (statusWidth + 1 >= width)
|
|
576
|
+
return truncateToWidth(styledStatus, width);
|
|
577
|
+
const left = truncateToWidth(title, width - statusWidth - 1);
|
|
578
|
+
return padTo(left, width - statusWidth) + styledStatus;
|
|
529
579
|
}
|
|
530
580
|
renderFooter(width) {
|
|
531
581
|
if (this.focusPane === "right") {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
.
|
|
535
|
-
|
|
582
|
+
const paneHints = this.topPane()?.hints() ?? "";
|
|
583
|
+
if (width < DOUBLE_PANE_MIN_WIDTH) {
|
|
584
|
+
// Esc already returns to navigation here; only add the arrow hint when it fits whole.
|
|
585
|
+
const arrowHint = keyHint("app.provider.switchPaneLeft", "nav");
|
|
586
|
+
const combined = [paneHints, arrowHint].filter(Boolean).join(" ");
|
|
587
|
+
return [truncateToWidth(visibleWidth(combined) <= width ? combined : paneHints, width)];
|
|
588
|
+
}
|
|
589
|
+
// The editor owns the way-back hint so every wide-layout pane footer is consistent.
|
|
590
|
+
const hints = [paneHints, keyHint("app.provider.switchPaneLeft", "focus left")].filter(Boolean).join(" ");
|
|
536
591
|
return [truncateToWidth(hints, width)];
|
|
537
592
|
}
|
|
538
593
|
const hints = [
|