@aiwayds/dsh-tui-pi 2.0.2 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,8 +35,6 @@ https://github.com/user-attachments/assets/6a7e00bb-1fd0-4bc5-9070-457f1e9fa54d
35
35
 
36
36
  ```sh
37
37
  dsh plugin --profile tui add @aiwayds/dsh-tui-pi
38
- dsh plugin --profile tui add @aiwayds/dsh-subagent-registry # optional
39
- dsh plugin --profile tui add @aiwayds/dsh-dcp # optional
40
38
  dsh --profile tui # launch (or: dsh-tui-pi)
41
39
  ```
42
40
 
@@ -53,9 +51,24 @@ node scripts/dev-upgrade.mjs 1.0.5 --dry-run # preview the plan first
53
51
 
54
52
  ## Companion plugins
55
53
 
56
- - [@aiwayds/dsh-ask-router](https://www.npmjs.com/package/@aiwayds/dsh-ask-router)ships as a default dependency; fans every `ask_user_question` out to all answering surfaces (TUI panel, Feishu card) and the first answer wins. List it in the profile's `bundles` before the UI bundles to activate.
57
- - [@aiwayds/dsh-feishu](https://github.com/fan56/dsh-feishu) — optional; drives the same dsh session from Feishu/Lark on your phone ([demo](docs/features/feishu-demo.md)).
58
- - [@aiwayds/dsh-mcp-adapter](https://github.com/fan56/dsh-mcp-adapter) — optional; folds MCP tool schemas out of every prompt and adds the `/mcp` command ([demo](docs/features/mcp-adapter.md)).
54
+ **Default dependencies**the eight plugins below ship with this package (installed into the profile's `node_modules`); activation still follows the profile's `bundles` list list each one there to activate it.
55
+
56
+ - [@aiwayds/dsh-ask-router](https://www.npmjs.com/package/@aiwayds/dsh-ask-router) — fans every `ask_user_question` out to all answering surfaces (TUI panel, Feishu card); the first answer wins — list it in `bundles` before the UI bundles to activate.
57
+ - [@aiwayds/dsh-dcp](https://github.com/fan56/dsh-dcp) — the deterministic zero-LLM compaction backend.
58
+ - [@aiwayds/dsh-llm-proxy](https://github.com/fan56/dsh-llm-proxy) — SYSTEM proxy + per-host LLM outbound routing.
59
+ - [@aiwayds/dsh-llm-stats](https://github.com/fan56/dsh-llm-stats) — the `/llm-stats` usage ledger.
60
+ - [@aiwayds/dsh-mcp-adapter](https://github.com/fan56/dsh-mcp-adapter) — folds MCP tool schemas out of every prompt and adds the `/mcp` command ([demo](docs/features/mcp-adapter.md)).
61
+ - [@aiwayds/dsh-model-sync](https://github.com/fan56/dsh-model-sync) — syncs provider routes with the pi.dev model catalog.
62
+ - [@aiwayds/dsh-subagent-registry](https://github.com/fan56/dsh-subagent-registry) — registers `~/.dsh/agents/*.md` as `use_agent` subagents.
63
+ - [@aiwayds/dsh-web-search-anysearch](https://github.com/fan56/dsh-web-search-anysearch) — the AnySearch web search provider.
64
+
65
+ **Recommended install** — [@aiwayds/dsh-llmwiki-memory](https://github.com/fan56/dsh-llmwiki-memory), OKF topic memory for dsh (zero-LLM hot-path injection + a local git-tracked bundle):
66
+
67
+ ```sh
68
+ dsh plugin --profile tui add @aiwayds/dsh-llmwiki-memory
69
+ ```
70
+
71
+ **Optional** — [@aiwayds/dsh-feishu](https://github.com/fan56/dsh-feishu) — drives the same dsh session from Feishu/Lark on your phone ([demo](docs/features/feishu-demo.md)).
59
72
 
60
73
  ---
61
74
 
@@ -102,7 +115,7 @@ Other knobs: `dsh-tui.panelHeight` (think/tool panel height), `dsh-tui.iconSet`
102
115
  ```sh
103
116
  pnpm check # tsc --noEmit
104
117
  pnpm build # emit lib/
105
- pnpm test # unit tests, node --test against lib/ (pretest builds; 1020 tests across 55 files)
118
+ pnpm test # unit tests, node --test against lib/ (pretest builds; 1,100+ tests across 60+ files — see HANDOFF.md for the current baseline)
106
119
  ```
107
120
 
108
121
  `pi-tui` runs pristine from npm — no patches, no fork. See [AGENTS.md](AGENTS.md) for the iron rules and quality gates.
package/README.zh-CN.md CHANGED
@@ -35,8 +35,6 @@ https://github.com/user-attachments/assets/6a7e00bb-1fd0-4bc5-9070-457f1e9fa54d
35
35
 
36
36
  ```sh
37
37
  dsh plugin --profile tui add @aiwayds/dsh-tui-pi
38
- dsh plugin --profile tui add @aiwayds/dsh-subagent-registry # optional
39
- dsh plugin --profile tui add @aiwayds/dsh-dcp # optional
40
38
  dsh --profile tui # 启动(或:dsh-tui-pi)
41
39
  ```
42
40
 
@@ -51,9 +49,24 @@ node scripts/dev-upgrade.mjs 1.0.5 --dry-run # 先预览执行计划
51
49
 
52
50
  ## Companion plugins 伴生插件
53
51
 
54
- - [@aiwayds/dsh-ask-router](https://www.npmjs.com/package/@aiwayds/dsh-ask-router) —— 作为默认依赖附带;把每个 `ask_user_question` 扇出到所有应答面(TUI 面板、飞书卡片),第一个答案获胜。把它加进 profile 的 `bundles`,放在任何 UI bundle 之前即可激活。
55
- - [@aiwayds/dsh-feishu](https://github.com/fan56/dsh-feishu) —— 可选;用手机上的飞书/Lark 驱动同一个 dsh session([演示](docs/features/feishu-demo.md))。
56
- - [@aiwayds/dsh-mcp-adapter](https://github.com/fan56/dsh-mcp-adapter) —— 可选;把 MCP 工具 schema 折出每个请求,并提供 `/mcp` 命令([演示](docs/features/mcp-adapter.md))。
52
+ **默认依赖** —— 以下 8 个插件随本包自带(安装进 profile 的 `node_modules`);激活仍以 profile 的 `bundles` 列表为准——把要用的逐个列进去即可。
53
+
54
+ - [@aiwayds/dsh-ask-router](https://www.npmjs.com/package/@aiwayds/dsh-ask-router) —— 把每个 `ask_user_question` 扇出到所有应答面(TUI 面板、飞书卡片),第一个答案获胜;激活时在 `bundles` 里放在 UI bundle 之前。
55
+ - [@aiwayds/dsh-dcp](https://github.com/fan56/dsh-dcp) —— 确定性零 LLM 压缩后端。
56
+ - [@aiwayds/dsh-llm-proxy](https://github.com/fan56/dsh-llm-proxy) —— SYSTEM 代理 + 按 host 的 LLM 出站分流。
57
+ - [@aiwayds/dsh-llm-stats](https://github.com/fan56/dsh-llm-stats) —— `/llm-stats` 用量台账。
58
+ - [@aiwayds/dsh-mcp-adapter](https://github.com/fan56/dsh-mcp-adapter) —— 把 MCP 工具 schema 折出每个请求,并提供 `/mcp` 命令([演示](docs/features/mcp-adapter.md))。
59
+ - [@aiwayds/dsh-model-sync](https://github.com/fan56/dsh-model-sync) —— pi.dev 模型目录同步进 provider 路由。
60
+ - [@aiwayds/dsh-subagent-registry](https://github.com/fan56/dsh-subagent-registry) —— 把 `~/.dsh/agents/*.md` 注册成 `use_agent` 子代理。
61
+ - [@aiwayds/dsh-web-search-anysearch](https://github.com/fan56/dsh-web-search-anysearch) —— AnySearch web 搜索 provider。
62
+
63
+ **推荐安装** —— [@aiwayds/dsh-llmwiki-memory](https://github.com/fan56/dsh-llmwiki-memory),dsh 的 OKF topic 记忆插件(零 LLM 热路径注入 + 本地 git 可追溯 bundle):
64
+
65
+ ```sh
66
+ dsh plugin --profile tui add @aiwayds/dsh-llmwiki-memory
67
+ ```
68
+
69
+ **可选** —— [@aiwayds/dsh-feishu](https://github.com/fan56/dsh-feishu) —— 用手机上的飞书/Lark 驱动同一个 dsh session([演示](docs/features/feishu-demo.md))。
57
70
 
58
71
  ---
59
72
 
@@ -100,7 +113,7 @@ dsh-tui:
100
113
  ```sh
101
114
  pnpm check # tsc --noEmit
102
115
  pnpm build # 输出 lib/
103
- pnpm test # 单元测试,node --test 对 lib/ 执行(pretest 构建;55 个文件共 1020 个测试)
116
+ pnpm test # 单元测试,node --test 对 lib/ 执行(pretest 构建;1100+ 测试、60+ 文件——当前基数见 HANDOFF.md)
104
117
  ```
105
118
 
106
119
  `pi-tui` 从 npm 原样运行——无补丁、无 fork。铁律与质量门禁见 [AGENTS.md](AGENTS.md)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwayds/dsh-tui-pi",
3
- "version": "2.0.2",
3
+ "version": "2.1.1",
4
4
  "description": "pi-style terminal UI for DeepSeek Harness (dsh) — pi-tui look & feel, dsh slash commands, GitHub light/dark themes, powerline footer",
5
5
  "repository": {
6
6
  "type": "git",
@@ -58,10 +58,14 @@
58
58
  "smoke": "node scripts/smoke-boot.mjs"
59
59
  },
60
60
  "dependencies": {
61
- "@aiwayds/dsh-ask-router": "^0.2.0",
62
- "@aiwayds/dsh-dcp": "^0.5.1",
63
- "@aiwayds/dsh-llm-stats": "^0.3.0",
64
- "@aiwayds/dsh-subagent-registry": "^0.4.0",
61
+ "@aiwayds/dsh-ask-router": "^0.3.0",
62
+ "@aiwayds/dsh-dcp": "^0.6.0",
63
+ "@aiwayds/dsh-llm-proxy": "^0.3.0",
64
+ "@aiwayds/dsh-llm-stats": "^0.4.0",
65
+ "@aiwayds/dsh-mcp-adapter": "^0.3.0",
66
+ "@aiwayds/dsh-model-sync": "^0.2.1",
67
+ "@aiwayds/dsh-subagent-registry": "^0.5.0",
68
+ "@aiwayds/dsh-web-search-anysearch": "^0.2.0",
65
69
  "@earendil-works/pi-tui": "0.84.4"
66
70
  },
67
71
  "peerDependencies": {
@@ -1,122 +0,0 @@
1
- /**
2
- * /model-sync (singular) — discover models for CUSTOM provider routes and
3
- * merge them back into the llm-pi-ai settings section.
4
- *
5
- * A custom route is a hand-declared `llm-pi-ai.providers.<id>` profile that
6
- * carries a `baseURL` and is NOT one of the built-in catalog routes
7
- * (provider-catalog.ts): discovery against a catalog route would short-circuit
8
- * back to the installed pi-ai catalog. This command interrogates
9
- * each route's `GET ${baseURL}/models` through the official seam
10
- * (`LlmRuntime.discoverModels`, registered for the `llm-pi-ai` namespace by
11
- * dsh-llm-pi-ai with catalog short-circuit + stored-credential resolution),
12
- * then merges the answer into the profile's `models` array.
13
- *
14
- * The merge is additive-only: existing entries are preserved verbatim (local
15
- * overrides such as reasoningEfforts survive), discovered ids already present
16
- * are skipped, new ids are appended with sanitized metadata. The write goes
17
- * through `settings.mutate` at the revision read at execution time, with one
18
- * retry after SettingsConflictError (the persistDefaultModel pattern in
19
- * session.ts). Pure logic lives here as exported functions; src/index.ts only
20
- * wires the command.
21
- *
22
- * @module dsh-tui-pi/model-sync
23
- */
24
- import type { SettingsProvider } from '@deepseek-ai/dsh-settings';
25
- import type { LlmRuntime } from '@deepseek-ai/dsh-llm';
26
- /**
27
- * The services /model-sync needs, narrowed to the exact surface it touches so
28
- * tests can supply fakes without a live tree.
29
- */
30
- export interface ModelSyncDeps {
31
- /** The settings provider (describe for revisions, mutate for the write). */
32
- settings: Pick<SettingsProvider, 'describe' | 'mutate'>;
33
- /** The LLM runtime whose discoverModels serves the `llm-pi-ai` namespace. */
34
- llm: Pick<LlmRuntime, 'discoverModels'>;
35
- }
36
- /** One models entry as stored in a profile — passed through untouched. */
37
- type StoredModel = Record<string, unknown>;
38
- /**
39
- * A custom route selected for sync: the dict key plus the endpoint facts
40
- * discovery needs. `api` is optional — discovery defaults the protocol.
41
- */
42
- export interface CustomProviderRoute {
43
- id: string;
44
- baseURL: string;
45
- api?: string;
46
- }
47
- /**
48
- * A sanitized discovered model — the writable shape of one endpoint row:
49
- * always an id; name/capacities carried only when present and valid.
50
- */
51
- export interface SanitizedModel {
52
- id: string;
53
- name?: string;
54
- contextWindow?: number;
55
- maxTokens?: number;
56
- }
57
- /** Outcome of merging one route's discovery into its stored models. */
58
- export interface ModelSyncMergeResult {
59
- /**
60
- * The merged models array to store: existing entries first in their stored
61
- * order, newly discovered ids appended at the end in discovery order.
62
- */
63
- models: StoredModel[];
64
- /** Discovered rows appended as new entries. */
65
- added: number;
66
- /** Existing entries preserved verbatim. */
67
- kept: number;
68
- /**
69
- * Discovered rows dropped entirely: unusable id, or an id already covered
70
- * by an existing entry (local state wins) or by an earlier row of this batch.
71
- */
72
- skipped: number;
73
- }
74
- /**
75
- * Select the custom (hand-declared) routes worth syncing from a providers
76
- * dict: profiles carrying a non-empty `baseURL` whose key is not a built-in
77
- * catalog route — a catalog key would short-circuit discovery back to the
78
- * installed catalog. Sorted by route key so the
79
- * report order is stable regardless of document order.
80
- */
81
- export declare function selectCustomProviders(providers: Record<string, unknown> | undefined): CustomProviderRoute[];
82
- /**
83
- * Clean one discovered row into a writable models entry. The id is mandatory
84
- * (blank/absent → undefined); name must be a non-blank string; capacities
85
- * must be integers ≥ 1 — the llm-pi-ai schema rejects anything else at write
86
- * time (`contextWindow >= 1`, integer step), and a field that fails stays
87
- * behind rather than sinking the whole row (verified against the shipped
88
- * schema: absent name/contextWindow/maxTokens validate fine).
89
- */
90
- export declare function sanitizeDiscoveredModel(raw: unknown): SanitizedModel | undefined;
91
- /**
92
- * Merge one route's discovery into its stored models — additive-only:
93
- *
94
- * - existing entries are the base and are preserved VERBATIM (local overrides
95
- * like reasoningEfforts/input/compat survive; nothing is overwritten or
96
- * deleted);
97
- * - discovered rows join by id: an id already present (or repeated within the
98
- * batch) counts as skipped, local state wins;
99
- * - new ids are appended with `sanitizeDiscoveredModel`'s cleaned fields;
100
- * - the stored order is never touched: existing entries keep their positions
101
- * (id-less rows included) and new ids join at the end in discovery order.
102
- */
103
- export declare function mergeModels(existing: ReadonlyArray<object>, discovered: ReadonlyArray<unknown>): ModelSyncMergeResult;
104
- /**
105
- * Run one /model-sync round: select the target routes (the rawInput-named one,
106
- * or every custom route), discover each endpoint's models, merge, and write
107
- * back. One route's failure never aborts the rest — its line reports the
108
- * reason instead. Agentless by design: nothing here touches a session.
109
- *
110
- * @returns the command result — per-provider `<id>: added N · kept M ·
111
- * skipped K` lines on success, `<id>: <reason>` for failed routes; `kind:
112
- * 'error'` only for preconditions (missing namespace, unknown/non-custom
113
- * named route) or when EVERY route failed.
114
- */
115
- export declare function runModelSync(deps: ModelSyncDeps, options?: {
116
- rawInput?: string;
117
- signal?: AbortSignal;
118
- }): Promise<{
119
- kind: 'success' | 'error';
120
- text: string;
121
- }>;
122
- export {};
package/lib/model-sync.js DELETED
@@ -1,256 +0,0 @@
1
- /**
2
- * /model-sync (singular) — discover models for CUSTOM provider routes and
3
- * merge them back into the llm-pi-ai settings section.
4
- *
5
- * A custom route is a hand-declared `llm-pi-ai.providers.<id>` profile that
6
- * carries a `baseURL` and is NOT one of the built-in catalog routes
7
- * (provider-catalog.ts): discovery against a catalog route would short-circuit
8
- * back to the installed pi-ai catalog. This command interrogates
9
- * each route's `GET ${baseURL}/models` through the official seam
10
- * (`LlmRuntime.discoverModels`, registered for the `llm-pi-ai` namespace by
11
- * dsh-llm-pi-ai with catalog short-circuit + stored-credential resolution),
12
- * then merges the answer into the profile's `models` array.
13
- *
14
- * The merge is additive-only: existing entries are preserved verbatim (local
15
- * overrides such as reasoningEfforts survive), discovered ids already present
16
- * are skipped, new ids are appended with sanitized metadata. The write goes
17
- * through `settings.mutate` at the revision read at execution time, with one
18
- * retry after SettingsConflictError (the persistDefaultModel pattern in
19
- * session.ts). Pure logic lives here as exported functions; src/index.ts only
20
- * wires the command.
21
- *
22
- * @module dsh-tui-pi/model-sync
23
- */
24
- import { settingsNamespace, SettingsConflictError } from '@deepseek-ai/dsh-settings';
25
- import { catalogEntry } from "./provider-catalog.js";
26
- /** The settings namespace dsh-llm-pi-ai owns its provider routes under. */
27
- const NS_LLM_PI_AI = settingsNamespace('llm-pi-ai');
28
- /**
29
- * Read the providers dict out of an llm-pi-ai descriptor value. Tolerant of
30
- * every malformed shape — a section that is not an object yields no routes.
31
- */
32
- function readProviders(value) {
33
- if (typeof value !== 'object' || value === null)
34
- return {};
35
- const providers = value.providers;
36
- if (typeof providers !== 'object' || providers === null)
37
- return {};
38
- return providers;
39
- }
40
- function readProfile(raw) {
41
- const source = typeof raw === 'object' && raw !== null ? raw : {};
42
- const baseURL = typeof source.baseURL === 'string' && source.baseURL !== '' ? source.baseURL : undefined;
43
- const api = typeof source.api === 'string' && source.api !== '' ? source.api : undefined;
44
- const rawModels = Array.isArray(source.models) ? source.models : [];
45
- // Only well-formed entries are merged against; a malformed stored row is
46
- // left alone rather than deleted (additive-only merge).
47
- const models = rawModels.filter((entry) => typeof entry === 'object' && entry !== null);
48
- return { ...(baseURL !== undefined ? { baseURL } : {}), ...(api !== undefined ? { api } : {}), models };
49
- }
50
- /** The stored entry's id, when it is a non-empty string. */
51
- function storedModelId(entry) {
52
- const id = entry.id;
53
- return typeof id === 'string' && id !== '' ? id : undefined;
54
- }
55
- /**
56
- * Select the custom (hand-declared) routes worth syncing from a providers
57
- * dict: profiles carrying a non-empty `baseURL` whose key is not a built-in
58
- * catalog route — a catalog key would short-circuit discovery back to the
59
- * installed catalog. Sorted by route key so the
60
- * report order is stable regardless of document order.
61
- */
62
- export function selectCustomProviders(providers) {
63
- const selected = [];
64
- for (const [id, raw] of Object.entries(providers ?? {})) {
65
- if (catalogEntry(id) !== undefined)
66
- continue;
67
- const profile = readProfile(raw);
68
- if (profile.baseURL === undefined)
69
- continue;
70
- selected.push({
71
- id,
72
- baseURL: profile.baseURL,
73
- ...(profile.api !== undefined ? { api: profile.api } : {}),
74
- });
75
- }
76
- selected.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
77
- return selected;
78
- }
79
- /**
80
- * Clean one discovered row into a writable models entry. The id is mandatory
81
- * (blank/absent → undefined); name must be a non-blank string; capacities
82
- * must be integers ≥ 1 — the llm-pi-ai schema rejects anything else at write
83
- * time (`contextWindow >= 1`, integer step), and a field that fails stays
84
- * behind rather than sinking the whole row (verified against the shipped
85
- * schema: absent name/contextWindow/maxTokens validate fine).
86
- */
87
- export function sanitizeDiscoveredModel(raw) {
88
- if (typeof raw !== 'object' || raw === null)
89
- return undefined;
90
- const row = raw;
91
- if (typeof row.id !== 'string')
92
- return undefined;
93
- const id = row.id.trim();
94
- if (id === '')
95
- return undefined;
96
- const name = typeof row.name === 'string' && row.name.trim() !== '' ? row.name.trim() : undefined;
97
- const capacity = (value) => typeof value === 'number' && Number.isInteger(value) && value >= 1 ? value : undefined;
98
- const contextWindow = capacity(row.contextWindow);
99
- const maxTokens = capacity(row.maxTokens);
100
- return {
101
- id,
102
- ...(name !== undefined ? { name } : {}),
103
- ...(contextWindow !== undefined ? { contextWindow } : {}),
104
- ...(maxTokens !== undefined ? { maxTokens } : {}),
105
- };
106
- }
107
- /**
108
- * Merge one route's discovery into its stored models — additive-only:
109
- *
110
- * - existing entries are the base and are preserved VERBATIM (local overrides
111
- * like reasoningEfforts/input/compat survive; nothing is overwritten or
112
- * deleted);
113
- * - discovered rows join by id: an id already present (or repeated within the
114
- * batch) counts as skipped, local state wins;
115
- * - new ids are appended with `sanitizeDiscoveredModel`'s cleaned fields;
116
- * - the stored order is never touched: existing entries keep their positions
117
- * (id-less rows included) and new ids join at the end in discovery order.
118
- */
119
- export function mergeModels(existing, discovered) {
120
- const models = existing.map(entry => entry);
121
- const ids = new Set();
122
- for (const entry of models) {
123
- const id = storedModelId(entry);
124
- if (id !== undefined)
125
- ids.add(id);
126
- }
127
- let added = 0;
128
- let skipped = 0;
129
- for (const raw of discovered) {
130
- const clean = sanitizeDiscoveredModel(raw);
131
- if (clean === undefined || ids.has(clean.id)) {
132
- skipped += 1;
133
- continue;
134
- }
135
- ids.add(clean.id);
136
- // A fresh literal (not a cast): interfaces carry no implicit index
137
- // signature, so the entry must be rebuilt as a plain record.
138
- models.push({ ...clean });
139
- added += 1;
140
- }
141
- // No reordering: existing entries stay exactly where the user stored them
142
- // and new ids are appended at the end in discovery order — a no-op sync
143
- // therefore leaves the stored array byte-identical.
144
- return { models, added, kept: existing.length, skipped };
145
- }
146
- /**
147
- * Write one route's merged models with optimistic concurrency: mutate at the
148
- * revision read NOW (not at command start), one retry after
149
- * SettingsConflictError with a freshly read revision — the same bottom pattern
150
- * as persistDefaultModel in session.ts. A second conflict surfaces.
151
- */
152
- async function writeModels(settings, providerId, models) {
153
- const ops = [
154
- { op: 'set', path: ['providers', providerId, 'models'], value: models },
155
- ];
156
- const ns = NS_LLM_PI_AI;
157
- for (let attempt = 0;; attempt++) {
158
- const descriptor = currentDescriptor(settings);
159
- try {
160
- await settings.mutate(ns, ops, descriptor?.revision);
161
- return;
162
- }
163
- catch (error) {
164
- if (attempt === 0 && error instanceof SettingsConflictError)
165
- continue;
166
- throw error;
167
- }
168
- }
169
- }
170
- /** The live llm-pi-ai descriptor, re-read on demand (revision freshness). */
171
- function currentDescriptor(settings) {
172
- return settings.describe().find(d => d.ns === NS_LLM_PI_AI);
173
- }
174
- function errorText(error) {
175
- return error instanceof Error ? error.message : String(error);
176
- }
177
- /**
178
- * Run one /model-sync round: select the target routes (the rawInput-named one,
179
- * or every custom route), discover each endpoint's models, merge, and write
180
- * back. One route's failure never aborts the rest — its line reports the
181
- * reason instead. Agentless by design: nothing here touches a session.
182
- *
183
- * @returns the command result — per-provider `<id>: added N · kept M ·
184
- * skipped K` lines on success, `<id>: <reason>` for failed routes; `kind:
185
- * 'error'` only for preconditions (missing namespace, unknown/non-custom
186
- * named route) or when EVERY route failed.
187
- */
188
- export async function runModelSync(deps, options = {}) {
189
- const descriptor = currentDescriptor(deps.settings);
190
- if (descriptor === undefined) {
191
- return { kind: 'error', text: 'The llm-pi-ai settings namespace is not registered — nothing to sync.' };
192
- }
193
- const wanted = options.rawInput?.trim() ?? '';
194
- let targets;
195
- if (wanted !== '') {
196
- const providers = readProviders(descriptor.value);
197
- if (!(wanted in providers)) {
198
- return { kind: 'error', text: `Provider "${wanted}" is not configured under llm-pi-ai providers.` };
199
- }
200
- targets = selectCustomProviders({ [wanted]: providers[wanted] });
201
- if (targets.length === 0) {
202
- // Two distinct shapes land here: a built-in catalog key (excluded by
203
- // selection even when the profile carries a baseURL) and a genuinely
204
- // non-custom route without a baseURL. Name each accurately.
205
- if (catalogEntry(wanted) !== undefined) {
206
- return {
207
- kind: 'error',
208
- text: `Provider "${wanted}" is a built-in catalog route — /model-sync syncs hand-declared (baseURL) routes only.`,
209
- };
210
- }
211
- return {
212
- kind: 'error',
213
- text: `Provider "${wanted}" has no baseURL — /model-sync syncs hand-declared routes only.`,
214
- };
215
- }
216
- }
217
- else {
218
- targets = selectCustomProviders(readProviders(descriptor.value));
219
- if (targets.length === 0) {
220
- return { kind: 'success', text: 'No hand-declared (baseURL) providers configured — nothing to sync.' };
221
- }
222
- }
223
- const lines = [];
224
- let failures = 0;
225
- for (const target of targets) {
226
- try {
227
- // Re-read the live section per route: an api added/removed between the
228
- // selection and this write still reaches discovery (and a vanished
229
- // namespace degrades into that route's failure line, not a crash).
230
- const liveValue = currentDescriptor(deps.settings)?.value;
231
- const profile = readProfile(readProviders(liveValue)[target.id]);
232
- const discovered = await deps.llm.discoverModels(NS_LLM_PI_AI, {
233
- provider: target.id,
234
- baseURL: target.baseURL,
235
- ...(profile.api !== undefined ? { api: profile.api } : {}),
236
- ...(options.signal !== undefined ? { signal: options.signal } : {}),
237
- });
238
- const merged = mergeModels(profile.models, discovered);
239
- // A no-op sync must produce zero settings mutations: write only when
240
- // discovery actually contributed new ids.
241
- if (merged.added > 0) {
242
- await writeModels(deps.settings, target.id, merged.models);
243
- }
244
- lines.push(`${target.id}: added ${merged.added} · kept ${merged.kept} · skipped ${merged.skipped}`);
245
- }
246
- catch (error) {
247
- failures += 1;
248
- lines.push(`${target.id}: ${errorText(error)}`);
249
- }
250
- }
251
- if (failures === targets.length && failures > 0) {
252
- return { kind: 'error', text: lines.join('\n') };
253
- }
254
- return { kind: 'success', text: lines.join('\n') };
255
- }
256
- //# sourceMappingURL=model-sync.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"model-sync.js","sourceRoot":"","sources":["../src/model-sync.ts"],"names":[],"mappings":"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"}