@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.
Files changed (60) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/bundle/chunks/{chunk-Z2YN4E74.js → chunk-SBVVM3NV.js} +14 -14
  3. package/dist/bundle/chunks/{chunk-ISXCH6TG.js → chunk-VGSZTBVJ.js} +1 -1
  4. package/dist/bundle/chunks/{virtual-modules-QX6D3D7X.js → virtual-modules-ETYFHSUG.js} +1 -1
  5. package/dist/bundle/cli.js +1 -1
  6. package/dist/bundle/index.js +1 -1
  7. package/dist/bundle/rpc-entry.js +1 -1
  8. package/dist/core/extensions/index.d.ts +1 -1
  9. package/dist/core/extensions/index.d.ts.map +1 -1
  10. package/dist/core/extensions/index.js.map +1 -1
  11. package/dist/core/extensions/runner.d.ts.map +1 -1
  12. package/dist/core/extensions/runner.js +1 -0
  13. package/dist/core/extensions/runner.js.map +1 -1
  14. package/dist/core/extensions/types.d.ts +10 -0
  15. package/dist/core/extensions/types.d.ts.map +1 -1
  16. package/dist/core/extensions/types.js.map +1 -1
  17. package/dist/extensions/index.d.ts.map +1 -1
  18. package/dist/extensions/index.js +2 -0
  19. package/dist/extensions/index.js.map +1 -1
  20. package/dist/extensions/provider/store.d.ts +2 -0
  21. package/dist/extensions/provider/store.d.ts.map +1 -1
  22. package/dist/extensions/provider/store.js +4 -0
  23. package/dist/extensions/provider/store.js.map +1 -1
  24. package/dist/extensions/provider/ui/editor.d.ts +7 -9
  25. package/dist/extensions/provider/ui/editor.d.ts.map +1 -1
  26. package/dist/extensions/provider/ui/editor.js +103 -48
  27. package/dist/extensions/provider/ui/editor.js.map +1 -1
  28. package/dist/extensions/provider/ui/provider-list.d.ts.map +1 -1
  29. package/dist/extensions/provider/ui/provider-list.js +13 -19
  30. package/dist/extensions/provider/ui/provider-list.js.map +1 -1
  31. package/dist/extensions/run-summary/format.d.ts +2 -0
  32. package/dist/extensions/run-summary/format.d.ts.map +1 -0
  33. package/dist/extensions/run-summary/format.js +11 -0
  34. package/dist/extensions/run-summary/format.js.map +1 -0
  35. package/dist/extensions/run-summary/index.d.ts +4 -0
  36. package/dist/extensions/run-summary/index.d.ts.map +1 -0
  37. package/dist/extensions/run-summary/index.js +48 -0
  38. package/dist/extensions/run-summary/index.js.map +1 -0
  39. package/dist/extensions/run-summary/record.d.ts +11 -0
  40. package/dist/extensions/run-summary/record.d.ts.map +1 -0
  41. package/dist/extensions/run-summary/record.js +29 -0
  42. package/dist/extensions/run-summary/record.js.map +1 -0
  43. package/dist/index.d.ts +1 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/modes/interactive/interactive-mode.d.ts +11 -1
  47. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  48. package/dist/modes/interactive/interactive-mode.js +60 -5
  49. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  50. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  51. package/dist/modes/rpc/rpc-mode.js +3 -0
  52. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  53. package/docs/bundled/README.md +1 -0
  54. package/docs/bundled/extensions/provider.md +9 -7
  55. package/docs/bundled/extensions/run-summary.md +17 -0
  56. package/docs/docs.json +4 -0
  57. package/docs/extensions.md +4 -0
  58. package/docs/tui.md +13 -0
  59. package/npm-shrinkwrap.json +2 -2
  60. 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: API Auth / Fetch Models, the provider's
5
- * models (name → id → "New Model" fallback, one draft at a time), then
6
- * + Add Model and Delete Provider. Right column hosts the selected item's
7
- * field pane and sub-pane stack; both columns keep their own selection and
8
- * scroll position. The frame height is fixed: each column scrolls inside a
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":"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"}
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: API Auth / Fetch Models, the provider's
5
- * models (name → id → "New Model" fallback, one draft at a time), then
6
- * + Add Model and Delete Provider. Right column hosts the selected item's
7
- * field pane and sub-pane stack; both columns keep their own selection and
8
- * scroll position. The frame height is fixed: each column scrolls inside a
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 MIN_WIDTH = 56;
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 = nextIndex >= 0 ? nextIndex : Math.min(this.leftIndex, this.selectableIndex(0));
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 = [{ kind: "apiAuth" }, { kind: "fetch" }, { kind: "separator" }];
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: "separator" }, { kind: "addModel" }, { kind: "deleteProvider" });
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 !== "separator");
159
- return index >= 0 ? index : 0;
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 !== "separator");
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 Provider", () => {
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 === "separator") {
256
- return theme.fg("borderMuted", ` ${"─".repeat(Math.max(1, width - 4))}`);
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 !== "separator");
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 Model";
309
+ text = "+ Add model";
302
310
  break;
303
311
  case "deleteProvider":
304
- text = "Delete Provider";
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 separators.
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 === "separator")
443
+ while (this.leftIndex > 0 && this.leftItems[this.leftIndex]?.kind === "section")
436
444
  this.leftIndex--;
437
- if (this.leftItems[this.leftIndex]?.kind === "separator")
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
- // The crumb marks drill-down depth only; base rows are already named by the left selection.
496
- const crumb = truncateToWidth(this.theme.fg("muted", this.topPane()?.crumb ?? ""), rightWidth);
497
- const leftLines = this.renderLeft(leftWidth);
498
- const pane = this.topPane();
499
- const paneLines = pane ? windowLines(theme, pane.render(rightWidth), BODY_ROWS - 1, pane.scrollWindow?.()) : [];
500
- const rightLines = [crumb, ...paneLines];
501
- const body = [];
502
- for (let row = 0; row < BODY_ROWS; row++) {
503
- const left = padTo(truncateToWidth(leftLines[row] ?? "", leftWidth), leftWidth);
504
- const right = truncateToWidth(rightLines[row] ?? "", rightWidth);
505
- body.push(left + separator + right);
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 === "separator")
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
- let title = ` /provider · ${this.options.providerId}`;
526
- if (isBuiltinProviderId(this.options.providerId))
527
- title += " · overlays built-in catalog";
528
- return truncateToWidth(theme.fg("accent", theme.bold(title)), width);
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
- // The editor owns the way-back hint so every pane's footer is consistent.
533
- const hints = [this.topPane()?.hints() ?? "", keyHint("app.provider.switchPaneLeft", "focus left")]
534
- .filter(Boolean)
535
- .join(" ");
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 = [