zcode-acp-server 0.46.7 → 0.47.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.
Files changed (55) hide show
  1. package/README.md +1 -1
  2. package/dist/code-fingerprint.json +2 -2
  3. package/dist/remote/endpoint.d.ts.map +1 -1
  4. package/dist/remote/endpoint.js +23 -0
  5. package/dist/remote/endpoint.js.map +1 -1
  6. package/dist/remote/hub-server.d.ts.map +1 -1
  7. package/dist/remote/hub-server.js +115 -2
  8. package/dist/remote/hub-server.js.map +1 -1
  9. package/dist/remote/settings-endpoint.d.ts +47 -0
  10. package/dist/remote/settings-endpoint.d.ts.map +1 -0
  11. package/dist/remote/settings-endpoint.js +1046 -0
  12. package/dist/remote/settings-endpoint.js.map +1 -0
  13. package/dist/settings/agents-config.d.ts +158 -0
  14. package/dist/settings/agents-config.d.ts.map +1 -0
  15. package/dist/settings/agents-config.js +598 -0
  16. package/dist/settings/agents-config.js.map +1 -0
  17. package/dist/settings/app-update.d.ts +193 -0
  18. package/dist/settings/app-update.d.ts.map +1 -0
  19. package/dist/settings/app-update.js +659 -0
  20. package/dist/settings/app-update.js.map +1 -0
  21. package/dist/settings/atomic-write.d.ts +106 -0
  22. package/dist/settings/atomic-write.d.ts.map +1 -0
  23. package/dist/settings/atomic-write.js +228 -0
  24. package/dist/settings/atomic-write.js.map +1 -0
  25. package/dist/settings/cli-config.d.ts +162 -0
  26. package/dist/settings/cli-config.d.ts.map +1 -0
  27. package/dist/settings/cli-config.js +369 -0
  28. package/dist/settings/cli-config.js.map +1 -0
  29. package/dist/settings/coding-plan.d.ts +106 -0
  30. package/dist/settings/coding-plan.d.ts.map +1 -0
  31. package/dist/settings/coding-plan.js +299 -0
  32. package/dist/settings/coding-plan.js.map +1 -0
  33. package/dist/settings/file-lock.d.ts +39 -0
  34. package/dist/settings/file-lock.d.ts.map +1 -0
  35. package/dist/settings/file-lock.js +282 -0
  36. package/dist/settings/file-lock.js.map +1 -0
  37. package/dist/settings/provider-config.d.ts +123 -0
  38. package/dist/settings/provider-config.d.ts.map +1 -0
  39. package/dist/settings/provider-config.js +311 -0
  40. package/dist/settings/provider-config.js.map +1 -0
  41. package/dist/settings/skills.d.ts +72 -0
  42. package/dist/settings/skills.d.ts.map +1 -0
  43. package/dist/settings/skills.js +245 -0
  44. package/dist/settings/skills.js.map +1 -0
  45. package/dist/settings/usage-stats.d.ts +74 -0
  46. package/dist/settings/usage-stats.d.ts.map +1 -0
  47. package/dist/settings/usage-stats.js +235 -0
  48. package/dist/settings/usage-stats.js.map +1 -0
  49. package/dist/utils.d.ts +14 -0
  50. package/dist/utils.d.ts.map +1 -1
  51. package/dist/utils.js +22 -0
  52. package/dist/utils.js.map +1 -1
  53. package/docs/ARCHITECTURE.md +65 -0
  54. package/docs/REMOTE-CLIENTS.md +285 -0
  55. package/package.json +1 -1
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Read/write for the desktop's personal provider config
3
+ * (`~/.zcode/v2/provider_config.json`).
4
+ *
5
+ * This is where 3.12+ stores every user-added provider and model; the backend
6
+ * registry reads it directly and absorbs external writes through a ~1s poll,
7
+ * so a change here takes effect with NO restart and NO RPC
8
+ * (`personal-provider-config-repository.ts`).
9
+ *
10
+ * What this module deliberately does NOT do:
11
+ *
12
+ * - **Create or delete providers.** A provider rule carries credentials, an
13
+ * API protocol type and a model list, and getting it wrong yields a provider
14
+ * that fails at request time. That is a desktop-app (or hand-edit) job.
15
+ * - **Write `account:*` access.** The registry rejects `zhipu-account` access
16
+ * on personal rules outright — those providers come from the bundled table
17
+ * plus the account snapshot the bridge pushes. Writing one produces a file
18
+ * the backend refuses to load.
19
+ *
20
+ * Everything else the app's model page can do is here: enable/disable, rename,
21
+ * add/remove a model on an existing provider, and edit a model's context
22
+ * window and reasoning-level vocabulary.
23
+ *
24
+ * Canonical form matters: the file is written with 2-space indentation and NO
25
+ * trailing newline, matching the app's own writer. A non-canonical file is
26
+ * read fine but rewritten by the app on its next save, which would look like
27
+ * our edit "reverting".
28
+ */
29
+ export interface ProviderModelConfig {
30
+ enabled?: boolean;
31
+ properties?: {
32
+ contextWindow?: number;
33
+ };
34
+ optionSpecs?: {
35
+ reasoningLevel?: {
36
+ values?: string[];
37
+ };
38
+ };
39
+ }
40
+ /** A model rule as stored, with its owning provider. */
41
+ export interface ModelRule {
42
+ providerId: string;
43
+ modelId: string;
44
+ config: ProviderModelConfig;
45
+ }
46
+ export interface ProviderRule {
47
+ providerId: string;
48
+ providerName?: string;
49
+ enabled?: boolean;
50
+ config?: {
51
+ access?: {
52
+ type?: string;
53
+ apiKey?: string;
54
+ };
55
+ api?: {
56
+ type?: string;
57
+ baseUrl?: string;
58
+ };
59
+ personalModelIds?: string[];
60
+ modelOrder?: string[];
61
+ [key: string]: unknown;
62
+ };
63
+ }
64
+ export interface ProviderConfigFile {
65
+ schemaVersion: number;
66
+ config: {
67
+ providerOrder?: string[];
68
+ providerConfigRules: {
69
+ providerRules: ProviderRule[];
70
+ };
71
+ modelConfigRules: {
72
+ providerModelRules: ModelRule[];
73
+ manualProviderModelRules: ModelRule[];
74
+ };
75
+ defaultModelSelection?: Record<string, unknown>;
76
+ };
77
+ }
78
+ /**
79
+ * Read the file, or return an empty canonical document when it is absent.
80
+ *
81
+ * @throws when the file exists but is malformed — the settings layer must
82
+ * refuse the write rather than base it on an empty document (the app's
83
+ * own reader degrades to an empty overlay while KEEPING the bad bytes,
84
+ * so silently overwriting would destroy what is still recoverable).
85
+ */
86
+ export declare function readProviderConfig(): Promise<ProviderConfigFile>;
87
+ /**
88
+ * Serialize in the app's canonical form: 2-space indentation, NO trailing
89
+ * newline (the app's writer uses `JSON.stringify` directly). A trailing
90
+ * newline is not an error to the reader, but the app rewrites the file to its
91
+ * own canonical form on its next save, which makes our edit look reverted.
92
+ */
93
+ export declare function encodeProviderConfig(doc: unknown): string;
94
+ /** Structural self-check run before the bytes land. */
95
+ export declare function validateProviderConfig(doc: Record<string, unknown>): boolean | string;
96
+ /**
97
+ * Enable or disable a provider, and/or rename it.
98
+ *
99
+ * @param patch.enabled `true`/`false` flips the rule's `enabled` flag; omit to
100
+ * leave it alone.
101
+ * @param patch.providerName renames the provider; omit to leave it alone.
102
+ * @throws when the provider has no rule in this file (account-managed
103
+ * providers and bundled ones cannot be toggled here).
104
+ */
105
+ export declare function updateProvider(providerId: string, patch: {
106
+ enabled?: boolean;
107
+ providerName?: string;
108
+ }): Promise<ProviderConfigFile>;
109
+ /**
110
+ * Add a model to an existing provider, or update an existing rule.
111
+ *
112
+ * The model lands in `providerModelRules` (the list the app writes user models
113
+ * to). A rule already present anywhere in either list is updated in place so a
114
+ * repeated call is idempotent rather than duplicating.
115
+ */
116
+ export declare function upsertModel(providerId: string, modelId: string, patch: ProviderModelConfig): Promise<ProviderConfigFile>;
117
+ /** Remove a model rule from both lists. */
118
+ export declare function removeModel(providerId: string, modelId: string): Promise<ProviderConfigFile>;
119
+ /** Read the raw file text (for the settings snapshot). Absent → null. */
120
+ export declare function readProviderConfigRaw(): Promise<string | null>;
121
+ /** The canonical bytes a document would be written as. */
122
+ export declare function canonicalBytes(doc: ProviderConfigFile): string;
123
+ //# sourceMappingURL=provider-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.d.ts","sourceRoot":"","sources":["../../src/settings/provider-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAaH,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,WAAW,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;KAAE,CAAC;CAC1D;AAED,wDAAwD;AACxD,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5C,GAAG,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1C,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE;QACN,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,mBAAmB,EAAE;YAAE,aAAa,EAAE,YAAY,EAAE,CAAA;SAAE,CAAC;QACvD,gBAAgB,EAAE;YAChB,kBAAkB,EAAE,SAAS,EAAE,CAAC;YAChC,wBAAwB,EAAE,SAAS,EAAE,CAAC;SACvC,CAAC;QACF,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjD,CAAC;CACH;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAKtE;AAmDD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAEzD;AAED,uDAAuD;AACvD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,GAAG,MAAM,CAoCrF;AAsCD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAClD,OAAO,CAAC,kBAAkB,CAAC,CAc7B;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CAiC7B;AAED,2CAA2C;AAC3C,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,CAAC,CAiC7B;AAqCD,yEAAyE;AACzE,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMpE;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,GAAG,EAAE,kBAAkB,GAAG,MAAM,CAE9D"}
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Read/write for the desktop's personal provider config
3
+ * (`~/.zcode/v2/provider_config.json`).
4
+ *
5
+ * This is where 3.12+ stores every user-added provider and model; the backend
6
+ * registry reads it directly and absorbs external writes through a ~1s poll,
7
+ * so a change here takes effect with NO restart and NO RPC
8
+ * (`personal-provider-config-repository.ts`).
9
+ *
10
+ * What this module deliberately does NOT do:
11
+ *
12
+ * - **Create or delete providers.** A provider rule carries credentials, an
13
+ * API protocol type and a model list, and getting it wrong yields a provider
14
+ * that fails at request time. That is a desktop-app (or hand-edit) job.
15
+ * - **Write `account:*` access.** The registry rejects `zhipu-account` access
16
+ * on personal rules outright — those providers come from the bundled table
17
+ * plus the account snapshot the bridge pushes. Writing one produces a file
18
+ * the backend refuses to load.
19
+ *
20
+ * Everything else the app's model page can do is here: enable/disable, rename,
21
+ * add/remove a model on an existing provider, and edit a model's context
22
+ * window and reasoning-level vocabulary.
23
+ *
24
+ * Canonical form matters: the file is written with 2-space indentation and NO
25
+ * trailing newline, matching the app's own writer. A non-canonical file is
26
+ * read fine but rewritten by the app on its next save, which would look like
27
+ * our edit "reverting".
28
+ */
29
+ import { readFile } from "node:fs/promises";
30
+ import { readJsonDocument, writeJsonAtomic } from "./atomic-write.js";
31
+ import { zcodePersonalProviderPath } from "../utils.js";
32
+ /** The only schema version the file format accepts. */
33
+ const SCHEMA_VERSION = 1;
34
+ /** Provider ids whose credentials may never appear in this file. */
35
+ const ACCOUNT_PROVIDER_PREFIX = "account:";
36
+ /**
37
+ * Read the file, or return an empty canonical document when it is absent.
38
+ *
39
+ * @throws when the file exists but is malformed — the settings layer must
40
+ * refuse the write rather than base it on an empty document (the app's
41
+ * own reader degrades to an empty overlay while KEEPING the bad bytes,
42
+ * so silently overwriting would destroy what is still recoverable).
43
+ */
44
+ export async function readProviderConfig() {
45
+ const file = zcodePersonalProviderPath();
46
+ const doc = await readJsonDocument(file);
47
+ if (doc === null)
48
+ return emptyConfig();
49
+ return normalize(doc);
50
+ }
51
+ function emptyConfig() {
52
+ return {
53
+ schemaVersion: SCHEMA_VERSION,
54
+ config: {
55
+ providerConfigRules: { providerRules: [] },
56
+ modelConfigRules: { providerModelRules: [], manualProviderModelRules: [] },
57
+ },
58
+ };
59
+ }
60
+ /**
61
+ * Coerce a parsed document into the canonical shape.
62
+ *
63
+ * Unlike `cli/config.json` (an open document whose unknown keys are preserved),
64
+ * this file's TOP level is strict — `{schemaVersion, config}` only, and an extra
65
+ * key makes the backend registry reject the whole document. Unknown top-level
66
+ * keys are therefore DROPPED, while everything inside `config` (including
67
+ * unrecognized sub-keys on a provider rule) is carried through untouched.
68
+ *
69
+ * Missing containers are created so a hand-truncated file still round-trips
70
+ * instead of producing an invalid document.
71
+ */
72
+ function normalize(doc) {
73
+ const config = (doc.config ?? {});
74
+ const providerRules = Array.isArray(config.providerConfigRules?.providerRules)
75
+ ? config.providerConfigRules.providerRules
76
+ : [];
77
+ const modelRules = config.modelConfigRules ?? {};
78
+ const out = {
79
+ schemaVersion: typeof doc.schemaVersion === "number" ? doc.schemaVersion : SCHEMA_VERSION,
80
+ config: {
81
+ providerConfigRules: { providerRules },
82
+ modelConfigRules: {
83
+ providerModelRules: Array.isArray(modelRules.providerModelRules)
84
+ ? modelRules.providerModelRules
85
+ : [],
86
+ manualProviderModelRules: Array.isArray(modelRules.manualProviderModelRules)
87
+ ? modelRules.manualProviderModelRules
88
+ : [],
89
+ },
90
+ },
91
+ };
92
+ if (Array.isArray(config.providerOrder))
93
+ out.config.providerOrder = config.providerOrder;
94
+ if (config.defaultModelSelection !== undefined) {
95
+ out.config.defaultModelSelection = config.defaultModelSelection;
96
+ }
97
+ return out;
98
+ }
99
+ /**
100
+ * Serialize in the app's canonical form: 2-space indentation, NO trailing
101
+ * newline (the app's writer uses `JSON.stringify` directly). A trailing
102
+ * newline is not an error to the reader, but the app rewrites the file to its
103
+ * own canonical form on its next save, which makes our edit look reverted.
104
+ */
105
+ export function encodeProviderConfig(doc) {
106
+ return JSON.stringify(doc, null, 2);
107
+ }
108
+ /** Structural self-check run before the bytes land. */
109
+ export function validateProviderConfig(doc) {
110
+ const file = doc;
111
+ if (file.schemaVersion !== SCHEMA_VERSION) {
112
+ return `schemaVersion must be ${SCHEMA_VERSION}`;
113
+ }
114
+ const config = file.config;
115
+ if (typeof config !== "object" || config === null)
116
+ return "config must be an object";
117
+ const rules = config.providerConfigRules?.providerRules;
118
+ if (!Array.isArray(rules))
119
+ return "config.providerConfigRules.providerRules must be an array";
120
+ const seen = new Set();
121
+ for (const rule of rules) {
122
+ if (!rule || typeof rule.providerId !== "string" || !rule.providerId) {
123
+ return "every provider rule needs a non-empty providerId";
124
+ }
125
+ if (seen.has(rule.providerId))
126
+ return `duplicate providerId ${rule.providerId}`;
127
+ seen.add(rule.providerId);
128
+ // `zhipu-account` access is only valid on the bundled table; a personal
129
+ // rule carrying it makes the registry reject the whole file.
130
+ const accessType = rule.config?.access?.type;
131
+ if (accessType === "zhipu-account") {
132
+ return `provider ${rule.providerId} may not declare zhipu-account access here`;
133
+ }
134
+ }
135
+ const modelRules = config.modelConfigRules;
136
+ if (typeof modelRules !== "object" || modelRules === null) {
137
+ return "config.modelConfigRules must be an object";
138
+ }
139
+ for (const list of [modelRules.providerModelRules, modelRules.manualProviderModelRules]) {
140
+ if (!Array.isArray(list))
141
+ return "model rule lists must be arrays";
142
+ for (const rule of list) {
143
+ if (!rule || typeof rule.providerId !== "string" || typeof rule.modelId !== "string") {
144
+ return "every model rule needs providerId and modelId";
145
+ }
146
+ }
147
+ }
148
+ return true;
149
+ }
150
+ function assertNotAccountProvider(providerId) {
151
+ if (providerId.startsWith(ACCOUNT_PROVIDER_PREFIX)) {
152
+ throw new Error(`provider ${providerId} is account-managed — its models come from the coding plan, not this file`);
153
+ }
154
+ }
155
+ /**
156
+ * The single write path: validate, back up, rename.
157
+ *
158
+ * `mutator` runs INSIDE the file lock, against the freshly re-read document —
159
+ * never against a snapshot taken before the lock was acquired. Reading outside
160
+ * and passing the result in would silently drop whatever a concurrent writer
161
+ * (a running desktop app, or another bridge process) landed in between, which
162
+ * is exactly the invariant ADR-0026 exists to protect.
163
+ */
164
+ async function saveProviderConfig(mutator) {
165
+ const file = zcodePersonalProviderPath();
166
+ const { doc } = await writeJsonAtomic(file, (current) => {
167
+ const next = normalize(current);
168
+ mutator(next);
169
+ return next;
170
+ }, {
171
+ encode: encodeProviderConfig,
172
+ validate: (d) => validateProviderConfig(d),
173
+ });
174
+ return normalize(doc);
175
+ }
176
+ /**
177
+ * Enable or disable a provider, and/or rename it.
178
+ *
179
+ * @param patch.enabled `true`/`false` flips the rule's `enabled` flag; omit to
180
+ * leave it alone.
181
+ * @param patch.providerName renames the provider; omit to leave it alone.
182
+ * @throws when the provider has no rule in this file (account-managed
183
+ * providers and bundled ones cannot be toggled here).
184
+ */
185
+ export async function updateProvider(providerId, patch) {
186
+ assertNotAccountProvider(providerId);
187
+ return saveProviderConfig((current) => {
188
+ const rule = current.config.providerConfigRules.providerRules.find((r) => r.providerId === providerId);
189
+ if (!rule) {
190
+ throw new Error(`provider ${providerId} has no rule in provider_config.json — only providers declared here can be edited`);
191
+ }
192
+ if (patch.enabled !== undefined)
193
+ rule.enabled = patch.enabled;
194
+ if (patch.providerName !== undefined)
195
+ rule.providerName = patch.providerName;
196
+ });
197
+ }
198
+ /**
199
+ * Add a model to an existing provider, or update an existing rule.
200
+ *
201
+ * The model lands in `providerModelRules` (the list the app writes user models
202
+ * to). A rule already present anywhere in either list is updated in place so a
203
+ * repeated call is idempotent rather than duplicating.
204
+ */
205
+ export async function upsertModel(providerId, modelId, patch) {
206
+ assertNotAccountProvider(providerId);
207
+ // Validate the patch before touching the lock: a rejected patch must not
208
+ // consume a lock slot, and `mergeModelConfig` throws on a bad contextWindow.
209
+ mergeModelConfig({}, patch);
210
+ return saveProviderConfig((current) => {
211
+ const rules = current.config.modelConfigRules;
212
+ const existing = rules.providerModelRules.find((r) => r.providerId === providerId && r.modelId === modelId) ??
213
+ rules.manualProviderModelRules.find((r) => r.providerId === providerId && r.modelId === modelId);
214
+ if (existing) {
215
+ existing.config = mergeModelConfig(existing.config, patch);
216
+ return;
217
+ }
218
+ rules.providerModelRules.push({ providerId, modelId, config: mergeModelConfig({}, patch) });
219
+ // Keep the provider's model list in step: a model absent from
220
+ // personalModelIds never reaches the dropdown.
221
+ const provider = current.config.providerConfigRules.providerRules.find((r) => r.providerId === providerId);
222
+ const list = provider?.config?.personalModelIds ?? [];
223
+ if (!list.includes(modelId)) {
224
+ const nextList = [...list, modelId];
225
+ if (provider) {
226
+ provider.config = { ...(provider.config ?? {}), personalModelIds: nextList };
227
+ // modelOrder is what the UI sorts by; append so the new model lands last.
228
+ const order = provider.config.modelOrder ?? [];
229
+ if (!order.includes(modelId))
230
+ provider.config.modelOrder = [...order, modelId];
231
+ }
232
+ }
233
+ });
234
+ }
235
+ /** Remove a model rule from both lists. */
236
+ export async function removeModel(providerId, modelId) {
237
+ let missing = false;
238
+ const result = await saveProviderConfig((current) => {
239
+ const rules = current.config.modelConfigRules;
240
+ const before = rules.providerModelRules.length + rules.manualProviderModelRules.length;
241
+ rules.providerModelRules = rules.providerModelRules.filter((r) => !(r.providerId === providerId && r.modelId === modelId));
242
+ rules.manualProviderModelRules = rules.manualProviderModelRules.filter((r) => !(r.providerId === providerId && r.modelId === modelId));
243
+ if (rules.providerModelRules.length + rules.manualProviderModelRules.length === before) {
244
+ // Signal through a flag rather than throwing: throwing here would abort
245
+ // the locked write and report a failure the client cannot act on.
246
+ missing = true;
247
+ return;
248
+ }
249
+ // Drop it from the provider's own lists too, or the dropdown keeps a model
250
+ // the registry no longer knows.
251
+ const provider = current.config.providerConfigRules.providerRules.find((r) => r.providerId === providerId);
252
+ if (provider?.config) {
253
+ for (const key of ["personalModelIds", "modelOrder"]) {
254
+ const list = provider.config[key];
255
+ if (Array.isArray(list)) {
256
+ provider.config[key] = list.filter((m) => m !== modelId);
257
+ }
258
+ }
259
+ }
260
+ });
261
+ if (missing)
262
+ throw new Error(`model ${modelId} has no rule for provider ${providerId}`);
263
+ return result;
264
+ }
265
+ /**
266
+ * Merge a patch into an existing model config.
267
+ *
268
+ * Only the three leaves the app's own manual-rule whitelist allows are
269
+ * touched; everything else on the rule survives untouched. An explicit
270
+ * `undefined` in the patch is a no-op, and `enabled: true` is recorded as the
271
+ * absence of the flag (enabled is the default state).
272
+ */
273
+ function mergeModelConfig(base, patch) {
274
+ const out = { ...base };
275
+ if (patch.enabled === true)
276
+ delete out.enabled;
277
+ else if (patch.enabled === false)
278
+ out.enabled = false;
279
+ const ctx = patch.properties?.contextWindow;
280
+ if (ctx !== undefined) {
281
+ if (!(Number.isInteger(ctx) && ctx > 0)) {
282
+ throw new Error("contextWindow must be a positive integer");
283
+ }
284
+ out.properties = { ...(out.properties ?? {}), contextWindow: ctx };
285
+ }
286
+ const values = patch.optionSpecs?.reasoningLevel?.values;
287
+ if (values !== undefined) {
288
+ if (!Array.isArray(values) || values.length === 0) {
289
+ throw new Error("reasoningLevel.values must be a non-empty array");
290
+ }
291
+ if (new Set(values).size !== values.length) {
292
+ throw new Error("reasoningLevel.values must not repeat");
293
+ }
294
+ out.optionSpecs = { ...(out.optionSpecs ?? {}), reasoningLevel: { values: [...values] } };
295
+ }
296
+ return out;
297
+ }
298
+ /** Read the raw file text (for the settings snapshot). Absent → null. */
299
+ export async function readProviderConfigRaw() {
300
+ try {
301
+ return await readFile(zcodePersonalProviderPath(), "utf8");
302
+ }
303
+ catch {
304
+ return null;
305
+ }
306
+ }
307
+ /** The canonical bytes a document would be written as. */
308
+ export function canonicalBytes(doc) {
309
+ return encodeProviderConfig(doc);
310
+ }
311
+ //# sourceMappingURL=provider-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.js","sourceRoot":"","sources":["../../src/settings/provider-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Skill management: discovery plus the actions the app's skills page offers.
3
+ *
4
+ * `config/skill-discovery.ts` exists to build the editor's command list, so it
5
+ * filters out disabled skills and drops the SKILL.md path (its `input.hint` is
6
+ * the author's argument hint, not a location). Neither works for management —
7
+ * a disabled skill must stay visible so the user can re-enable it, and every
8
+ * action needs the real path. So discovery is re-done here, over the same roots
9
+ * and with the same frontmatter rules.
10
+ *
11
+ * Escape guards are the point of this module: a skill path is client-supplied
12
+ * text that becomes a filesystem operation. Every path is resolved through
13
+ * `realpath` (collapsing symlinks and `..`) BEFORE the containment check, so
14
+ * `~/.zcode/skills/../../../etc` or a symlink pointing outside the roots can
15
+ * neither delete nor copy anything outside them. The app's own delete path
16
+ * carries the same guard.
17
+ */
18
+ /** A discovered skill plus the state the management surface needs. */
19
+ export interface SkillEntry {
20
+ name: string;
21
+ description: string;
22
+ /** Absolute path of the SKILL.md file. */
23
+ path: string;
24
+ /** Directory containing SKILL.md. */
25
+ dir: string;
26
+ /** Where it was found, for grouping in a UI. */
27
+ scope: "user" | "agents" | "plugin" | "project";
28
+ /** False when cli/config.json carries `{enable: false}` for this path. */
29
+ enabled: boolean;
30
+ }
31
+ interface SkillRoot {
32
+ dir: string;
33
+ scope: SkillEntry["scope"];
34
+ /** Plugin skills belong to the plugin install — never deletable. */
35
+ deletable: boolean;
36
+ }
37
+ /**
38
+ * Discovery roots, in the app's own order: user scope first (its skills win a
39
+ * name clash), then the shared `~/.agents` root, then plugin caches.
40
+ */
41
+ export declare function skillRoots(): SkillRoot[];
42
+ /** The user-scope root new skills are copied into. */
43
+ export declare function userSkillsRoot(): string;
44
+ /**
45
+ * Every skill, enabled or not.
46
+ *
47
+ * Duplicate names are kept rather than de-duplicated: the app lists the same
48
+ * name from two scopes separately (they are different files), and hiding one
49
+ * would make its toggle unreachable.
50
+ */
51
+ export declare function listSkills(): Promise<SkillEntry[]>;
52
+ /** Enable or disable a skill by its SKILL.md path. */
53
+ export declare function setSkillEnabledByPath(skillPath: string, enable: boolean): Promise<void>;
54
+ /**
55
+ * Delete a skill directory.
56
+ *
57
+ * Only skills inside a deletable root can go: a plugin's skill belongs to the
58
+ * plugin install and would reappear on the next update, so the API refuses
59
+ * rather than deleting something that comes back. The containment check runs on
60
+ * RESOLVED paths, so a symlinked skill directory cannot point the delete at
61
+ * something outside the roots.
62
+ */
63
+ export declare function deleteSkill(skillPath: string): Promise<void>;
64
+ /**
65
+ * Copy a skill into the user root.
66
+ *
67
+ * A name collision is refused rather than merged: silently merging two skill
68
+ * directories yields a SKILL.md whose description matches neither original.
69
+ */
70
+ export declare function copySkillToUser(skillPath: string): Promise<string>;
71
+ export {};
72
+ //# sourceMappingURL=skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/settings/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AASH,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAChD,0EAA0E;IAC1E,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,SAAS;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC3B,oEAAoE;IACpE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,SAAS,EAAE,CAOxC;AAED,sDAAsD;AACtD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AA0DD;;;;;;GAMG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAyCxD;AAWD,sDAAsD;AACtD,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7F;AAWD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBlE;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgCxE"}