@minko-ai/minko 0.1.0 → 0.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.
Files changed (189) hide show
  1. package/README.md +577 -2
  2. package/assets/mascot-preview.png +0 -0
  3. package/assets/mascot-preview.txt +46 -0
  4. package/assets/mascot-source.png +0 -0
  5. package/assets/skills/README.md +21 -0
  6. package/assets/skills/brainstorming/LICENSE +21 -0
  7. package/assets/skills/brainstorming/SKILL.md +316 -0
  8. package/assets/skills/buddhist-method/LICENSE +21 -0
  9. package/assets/skills/buddhist-method/SKILL.md +137 -0
  10. package/assets/skills/buddhist-method/references/ariyasacca-debug.md +57 -0
  11. package/assets/skills/buddhist-method/references/extended-principles.md +100 -0
  12. package/assets/skills/code-humanizer/LICENSE +21 -0
  13. package/assets/skills/code-humanizer/SKILL.md +174 -0
  14. package/assets/skills/code-review/LICENSE +21 -0
  15. package/assets/skills/code-review/SKILL.md +232 -0
  16. package/assets/skills/code-review/reference/angular.md +768 -0
  17. package/assets/skills/code-review/reference/architecture-review-guide.md +472 -0
  18. package/assets/skills/code-review/reference/c.md +890 -0
  19. package/assets/skills/code-review/reference/code-quality-universal.md +488 -0
  20. package/assets/skills/code-review/reference/code-review-best-practices.md +136 -0
  21. package/assets/skills/code-review/reference/common-bugs-checklist.md +302 -0
  22. package/assets/skills/code-review/reference/cpp.md +893 -0
  23. package/assets/skills/code-review/reference/cross-cutting/async-concurrency-patterns.md +515 -0
  24. package/assets/skills/code-review/reference/cross-cutting/error-handling-principles.md +492 -0
  25. package/assets/skills/code-review/reference/cross-cutting/n-plus-one-queries.md +309 -0
  26. package/assets/skills/code-review/reference/cross-cutting/sql-injection-prevention.md +308 -0
  27. package/assets/skills/code-review/reference/cross-cutting/xss-prevention.md +264 -0
  28. package/assets/skills/code-review/reference/csharp.md +525 -0
  29. package/assets/skills/code-review/reference/css-less-sass.md +661 -0
  30. package/assets/skills/code-review/reference/dart.md +670 -0
  31. package/assets/skills/code-review/reference/django.md +985 -0
  32. package/assets/skills/code-review/reference/fastapi.md +580 -0
  33. package/assets/skills/code-review/reference/go.md +993 -0
  34. package/assets/skills/code-review/reference/java.md +409 -0
  35. package/assets/skills/code-review/reference/java8.md +586 -0
  36. package/assets/skills/code-review/reference/kotlin.md +1018 -0
  37. package/assets/skills/code-review/reference/nestjs.md +593 -0
  38. package/assets/skills/code-review/reference/performance-review-guide.md +816 -0
  39. package/assets/skills/code-review/reference/php.md +684 -0
  40. package/assets/skills/code-review/reference/python.md +1073 -0
  41. package/assets/skills/code-review/reference/qt.md +757 -0
  42. package/assets/skills/code-review/reference/react.md +871 -0
  43. package/assets/skills/code-review/reference/ruby.md +964 -0
  44. package/assets/skills/code-review/reference/rust.md +846 -0
  45. package/assets/skills/code-review/reference/security-review-guide.md +494 -0
  46. package/assets/skills/code-review/reference/svelte.md +1064 -0
  47. package/assets/skills/code-review/reference/swift.md +936 -0
  48. package/assets/skills/code-review/reference/typescript.md +1016 -0
  49. package/assets/skills/code-review/reference/vue.md +924 -0
  50. package/assets/skills/code-review/reference/zig.md +440 -0
  51. package/assets/skills/cyclomatic-complexity/LICENSE +201 -0
  52. package/assets/skills/cyclomatic-complexity/SKILL.md +69 -0
  53. package/assets/skills/five-whys/LICENSE +21 -0
  54. package/assets/skills/five-whys/SKILL.md +247 -0
  55. package/assets/skills/five-whys/examples/performance-regression.md +161 -0
  56. package/assets/skills/five-whys/examples/production-incident.md +134 -0
  57. package/assets/skills/five-whys/references/software-patterns.md +284 -0
  58. package/assets/skills/five-whys/references/toyota-origins.md +257 -0
  59. package/assets/skills/systematic-debugging/LICENSE +21 -0
  60. package/assets/skills/systematic-debugging/SKILL.md +283 -0
  61. package/assets/skills/test-driven-development/LICENSE +21 -0
  62. package/assets/skills/test-driven-development/SKILL.md +330 -0
  63. package/assets/skills/verification-before-completion/LICENSE +21 -0
  64. package/assets/skills/verification-before-completion/SKILL.md +120 -0
  65. package/assets/skills/vibe-sec/LICENSE +201 -0
  66. package/assets/skills/vibe-sec/SKILL.md +758 -0
  67. package/assets/skills/writing-plans/LICENSE +21 -0
  68. package/assets/skills/writing-plans/SKILL.md +204 -0
  69. package/bin/minko.js +2 -2
  70. package/dist/agent/approvalTokens.js +224 -0
  71. package/dist/agent/branchLock.js +315 -0
  72. package/dist/agent/compact.js +182 -0
  73. package/dist/agent/context.js +183 -0
  74. package/dist/agent/cron.js +326 -0
  75. package/dist/agent/hooks.js +211 -0
  76. package/dist/agent/lanes.js +511 -0
  77. package/dist/agent/loop.js +337 -0
  78. package/dist/agent/memory.js +159 -0
  79. package/dist/agent/permissions.js +71 -0
  80. package/dist/agent/plan.js +118 -0
  81. package/dist/agent/prompt.js +120 -0
  82. package/dist/agent/subagent.js +67 -0
  83. package/dist/agent/tasks.js +449 -0
  84. package/dist/agent/transcript.js +62 -0
  85. package/dist/agent/workerStore.js +172 -0
  86. package/dist/agent/workerTypes.js +1 -0
  87. package/dist/agent/workers.js +503 -0
  88. package/dist/cli/app.js +261 -0
  89. package/dist/cli/argv.js +206 -0
  90. package/dist/cli/commandsCommand.js +12 -0
  91. package/dist/cli/configCommand.js +170 -0
  92. package/dist/cli/doctorCommand.js +112 -0
  93. package/dist/cli/exportCommand.js +85 -0
  94. package/dist/cli/forkCommand.js +24 -0
  95. package/dist/cli/linkCommand.js +272 -0
  96. package/dist/cli/loginCommand.js +69 -0
  97. package/dist/cli/mcpLoginCommand.js +68 -0
  98. package/dist/cli/pluginCommand.js +113 -0
  99. package/dist/cli/powerCommand.js +174 -0
  100. package/dist/cli/promptFooter.js +74 -0
  101. package/dist/cli/relayCommand.js +38 -0
  102. package/dist/cli/sandboxCommand.js +31 -0
  103. package/dist/cli/serveCommand.js +104 -0
  104. package/dist/cli/statusCommand.js +120 -0
  105. package/dist/cli/tui.js +1104 -0
  106. package/dist/commands/registry.js +99 -0
  107. package/dist/config/paths.js +81 -0
  108. package/dist/config/store.js +605 -0
  109. package/dist/index.js +199 -0
  110. package/dist/link/protocol.js +159 -0
  111. package/dist/link/relay.js +184 -0
  112. package/dist/link/server.js +566 -0
  113. package/dist/link/tunnel.js +90 -0
  114. package/dist/lsp/client.js +340 -0
  115. package/dist/lsp/tool.js +177 -0
  116. package/dist/mcp/client.js +469 -0
  117. package/dist/mcp/httpClient.js +191 -0
  118. package/dist/mcp/server.js +206 -0
  119. package/dist/mcp/tools.js +173 -0
  120. package/dist/oauth/device.js +111 -0
  121. package/dist/oauth/store.js +56 -0
  122. package/dist/plugins/loader.js +291 -0
  123. package/dist/plugins/market.js +98 -0
  124. package/dist/providers/anthropic.js +279 -0
  125. package/dist/providers/factory.js +109 -0
  126. package/dist/providers/openai.js +182 -0
  127. package/dist/providers/retry.js +95 -0
  128. package/dist/providers/sse.js +67 -0
  129. package/dist/providers/types.js +10 -0
  130. package/dist/rag/embeddings.js +171 -0
  131. package/dist/rag/index.js +373 -0
  132. package/dist/rag/server.js +167 -0
  133. package/dist/rag/tool.js +148 -0
  134. package/dist/sandbox/index.js +55 -0
  135. package/dist/sdk/server.js +150 -0
  136. package/dist/sdk/session.js +104 -0
  137. package/dist/session/liveness.js +148 -0
  138. package/dist/session/recorder.js +49 -0
  139. package/dist/session/store.js +171 -0
  140. package/dist/skills/index.js +428 -0
  141. package/dist/theme.js +30 -0
  142. package/dist/tools/fs.js +195 -0
  143. package/dist/tools/notebook.js +109 -0
  144. package/dist/tools/registry.js +32 -0
  145. package/dist/tools/search.js +130 -0
  146. package/dist/tools/shell.js +200 -0
  147. package/dist/tools/structured.js +83 -0
  148. package/dist/tools/types.js +1 -0
  149. package/dist/ui/ansi.js +135 -0
  150. package/dist/ui/banner.js +103 -0
  151. package/dist/ui/input.js +49 -0
  152. package/dist/ui/inputModel.js +113 -0
  153. package/dist/ui/keyboard.js +142 -0
  154. package/dist/ui/mascot.js +34 -0
  155. package/dist/ui/mascotData.js +96 -0
  156. package/dist/ui/md.js +183 -0
  157. package/dist/ui/screen.js +49 -0
  158. package/dist/ui/spinner.js +52 -0
  159. package/dist/ui/transcript.js +287 -0
  160. package/dist/util/clipboard.js +53 -0
  161. package/dist/util/errors.js +50 -0
  162. package/dist/util/version.js +12 -0
  163. package/dist/web/fetch.js +168 -0
  164. package/dist/web/search.js +97 -0
  165. package/package.json +45 -36
  166. package/src/agent/loop.ts +0 -159
  167. package/src/agent/prompt.ts +0 -45
  168. package/src/cli/app.ts +0 -396
  169. package/src/cli/argv.ts +0 -139
  170. package/src/cli/configCommand.ts +0 -175
  171. package/src/config/paths.ts +0 -20
  172. package/src/config/store.ts +0 -149
  173. package/src/index.ts +0 -120
  174. package/src/providers/anthropic.ts +0 -215
  175. package/src/providers/openai.ts +0 -158
  176. package/src/providers/sse.ts +0 -57
  177. package/src/providers/types.ts +0 -59
  178. package/src/session/store.ts +0 -125
  179. package/src/theme.ts +0 -41
  180. package/src/tools/fs.ts +0 -143
  181. package/src/tools/registry.ts +0 -19
  182. package/src/tools/shell.ts +0 -120
  183. package/src/tools/types.ts +0 -26
  184. package/src/ui/ansi.ts +0 -110
  185. package/src/ui/banner.ts +0 -39
  186. package/src/ui/input.ts +0 -54
  187. package/src/ui/md.ts +0 -160
  188. package/src/ui/spinner.ts +0 -58
  189. package/src/util/errors.ts +0 -48
@@ -0,0 +1,605 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { configPath } from "./paths.js";
4
+ export const POWER_LABELS = {
5
+ full: '完全开放权限',
6
+ workspace: '工作区权限',
7
+ restricted: '受限制访问(默认)',
8
+ readonly: '禁止访问文件',
9
+ };
10
+ export const PRESETS = [
11
+ {
12
+ id: 'deepseek',
13
+ label: 'DeepSeek',
14
+ kind: 'openai',
15
+ baseUrl: 'https://api.deepseek.com',
16
+ model: 'deepseek-flash',
17
+ hint: 'DeepSeek 开放平台,OpenAI 兼容',
18
+ },
19
+ {
20
+ id: 'anthropic',
21
+ label: 'Claude',
22
+ kind: 'anthropic',
23
+ baseUrl: 'https://api.anthropic.com',
24
+ model: 'claude-sonnet-5-5',
25
+ hint: 'Anthropic 官方 API',
26
+ },
27
+ {
28
+ id: 'openai',
29
+ label: 'OpenAI',
30
+ kind: 'openai',
31
+ baseUrl: 'https://api.openai.com/v1',
32
+ model: 'gpt-4.1',
33
+ hint: 'OpenAI 官方 API',
34
+ },
35
+ {
36
+ id: 'xai',
37
+ label: 'xAI',
38
+ kind: 'openai',
39
+ baseUrl: 'https://api.x.ai/v1',
40
+ model: 'grok-3',
41
+ hint: 'xAI Grok,OpenAI 兼容',
42
+ },
43
+ {
44
+ id: 'dashscope',
45
+ label: 'DashScope / Qwen',
46
+ kind: 'openai',
47
+ baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
48
+ model: 'qwen-plus',
49
+ hint: '阿里云百炼,OpenAI 兼容',
50
+ },
51
+ {
52
+ id: 'kimi',
53
+ label: 'Kimi / Moonshot',
54
+ kind: 'openai',
55
+ baseUrl: 'https://api.moonshot.cn/v1',
56
+ model: 'kimi-k2.5',
57
+ hint: 'Moonshot,OpenAI 兼容',
58
+ },
59
+ {
60
+ id: 'openrouter',
61
+ label: 'OpenRouter',
62
+ kind: 'openai',
63
+ baseUrl: 'https://openrouter.ai/api/v1',
64
+ model: 'openrouter/auto',
65
+ hint: 'OpenRouter 聚合网关',
66
+ },
67
+ {
68
+ id: 'ollama',
69
+ label: 'Ollama(本地)',
70
+ kind: 'openai',
71
+ baseUrl: 'http://127.0.0.1:11434/v1',
72
+ model: 'qwen3:latest',
73
+ hint: '本地 Ollama,不需要 API Key',
74
+ noKey: true,
75
+ },
76
+ ];
77
+ export function emptyConfig() {
78
+ return {
79
+ active: '',
80
+ providers: {},
81
+ approval: 'ask',
82
+ maxTokens: 8192,
83
+ power: 'restricted',
84
+ permissions: emptyPermissionRules(),
85
+ hooks: emptyHookConfig(),
86
+ mcpServers: {},
87
+ rag: {},
88
+ lsp: {},
89
+ plugins: { disabled: [] },
90
+ oauth: {},
91
+ web: {},
92
+ fallbackProviders: [],
93
+ modelAliases: {},
94
+ sandbox: { mode: 'off' },
95
+ };
96
+ }
97
+ export function loadConfig(file = configPath()) {
98
+ if (!fs.existsSync(file)) {
99
+ return emptyConfig();
100
+ }
101
+ const raw = fs.readFileSync(file, 'utf8');
102
+ let parsed;
103
+ try {
104
+ parsed = JSON.parse(raw);
105
+ }
106
+ catch {
107
+ throw new Error('配置文件不是合法的 JSON:' + file);
108
+ }
109
+ return normalizeConfig(parsed);
110
+ }
111
+ export function normalizeConfig(input) {
112
+ const base = emptyConfig();
113
+ if (input === null || typeof input !== 'object') {
114
+ return base;
115
+ }
116
+ const record = input;
117
+ const providers = {};
118
+ const rawProviders = record.providers;
119
+ if (rawProviders && typeof rawProviders === 'object') {
120
+ for (const [name, value] of Object.entries(rawProviders)) {
121
+ const entry = normalizeProvider(value);
122
+ if (entry) {
123
+ providers[name] = entry;
124
+ }
125
+ }
126
+ }
127
+ const active = typeof record.active === 'string' ? record.active : '';
128
+ const approval = record.approval === 'auto' ? 'auto' : 'ask';
129
+ const maxTokens = typeof record.maxTokens === 'number' && record.maxTokens > 0
130
+ ? Math.floor(record.maxTokens)
131
+ : base.maxTokens;
132
+ const power = record.power === 'full' || record.power === 'workspace'
133
+ || record.power === 'readonly'
134
+ ? record.power
135
+ : 'restricted';
136
+ return {
137
+ active,
138
+ providers,
139
+ approval,
140
+ maxTokens,
141
+ power,
142
+ permissions: normalizePermissionRules(record.permissions),
143
+ hooks: normalizeHookConfig(record.hooks),
144
+ mcpServers: normalizeMcpServers(record.mcpServers),
145
+ rag: normalizeRagConfig(record.rag),
146
+ lsp: normalizeLspConfig(record.lsp),
147
+ plugins: normalizePluginsConfig(record.plugins),
148
+ oauth: normalizeOAuthConfig(record.oauth),
149
+ web: normalizeWebConfig(record.web),
150
+ fallbackProviders: normalizeStringArray(record.fallbackProviders, 10),
151
+ modelAliases: normalizeStringMap(record.modelAliases, 100),
152
+ sandbox: normalizeSandboxConfig(record.sandbox),
153
+ };
154
+ }
155
+ function emptyPermissionRules() {
156
+ return { deny: [], ask: [], allow: [] };
157
+ }
158
+ function emptyHookConfig() {
159
+ return { PreToolUse: [], PostToolUse: [], PostToolUseFailure: [] };
160
+ }
161
+ function normalizePermissionRules(value) {
162
+ const out = emptyPermissionRules();
163
+ if (value === null || typeof value !== 'object') {
164
+ return out;
165
+ }
166
+ const record = value;
167
+ for (const action of ['deny', 'ask', 'allow']) {
168
+ const list = record[action];
169
+ if (!Array.isArray(list)) {
170
+ continue;
171
+ }
172
+ out[action] = list
173
+ .filter((item) => typeof item === 'string' && item.trim().length > 0)
174
+ .map((item) => item.trim())
175
+ .slice(0, 200);
176
+ }
177
+ return out;
178
+ }
179
+ export function normalizeHookConfig(value) {
180
+ const out = emptyHookConfig();
181
+ if (value === null || typeof value !== 'object') {
182
+ return out;
183
+ }
184
+ const record = value;
185
+ for (const event of ['PreToolUse', 'PostToolUse', 'PostToolUseFailure']) {
186
+ const list = record[event];
187
+ if (!Array.isArray(list)) {
188
+ continue;
189
+ }
190
+ const entries = [];
191
+ for (const item of list.slice(0, 100)) {
192
+ if (typeof item === 'string') {
193
+ const command = item.trim();
194
+ if (command.length > 0) {
195
+ entries.push(command);
196
+ }
197
+ continue;
198
+ }
199
+ if (item === null || typeof item !== 'object') {
200
+ continue;
201
+ }
202
+ const raw = item;
203
+ if (!Array.isArray(raw.hooks)) {
204
+ continue;
205
+ }
206
+ const commands = [];
207
+ for (const candidate of raw.hooks) {
208
+ if (candidate === null || typeof candidate !== 'object') {
209
+ continue;
210
+ }
211
+ const hook = candidate;
212
+ if (hook.type !== undefined && hook.type !== 'command') {
213
+ continue;
214
+ }
215
+ if (typeof hook.command !== 'string' || hook.command.trim().length === 0) {
216
+ continue;
217
+ }
218
+ const command = { type: 'command', command: hook.command.trim() };
219
+ if (typeof hook.cwd === 'string' && hook.cwd.trim().length > 0) {
220
+ command.cwd = hook.cwd.trim();
221
+ }
222
+ commands.push(command);
223
+ }
224
+ if (commands.length === 0) {
225
+ continue;
226
+ }
227
+ const matcher = typeof raw.matcher === 'string' && raw.matcher.trim().length > 0
228
+ ? raw.matcher.trim()
229
+ : undefined;
230
+ entries.push(matcher ? { matcher, hooks: commands } : { hooks: commands });
231
+ }
232
+ out[event] = entries;
233
+ }
234
+ return out;
235
+ }
236
+ function normalizeMcpServers(value) {
237
+ const out = {};
238
+ if (value === null || typeof value !== 'object') {
239
+ return out;
240
+ }
241
+ const record = value;
242
+ let count = 0;
243
+ for (const [name, raw] of Object.entries(record)) {
244
+ if (count >= 20) {
245
+ break;
246
+ }
247
+ if (raw === null || typeof raw !== 'object') {
248
+ continue;
249
+ }
250
+ const entry = raw;
251
+ const command = typeof entry.command === 'string' ? entry.command.trim() : '';
252
+ const url = typeof entry.url === 'string' ? entry.url.trim() : '';
253
+ if (command.length === 0 && url.length === 0) {
254
+ continue;
255
+ }
256
+ const server = command.length > 0 ? { command } : { url };
257
+ if (Array.isArray(entry.args)) {
258
+ const args = entry.args.filter((item) => typeof item === 'string');
259
+ if (args.length > 0) {
260
+ server.args = args;
261
+ }
262
+ }
263
+ if (entry.env !== null && typeof entry.env === 'object') {
264
+ const env = {};
265
+ for (const [key, item] of Object.entries(entry.env)) {
266
+ if (typeof item === 'string') {
267
+ env[key] = item;
268
+ }
269
+ }
270
+ if (Object.keys(env).length > 0) {
271
+ server.env = env;
272
+ }
273
+ }
274
+ if (typeof entry.cwd === 'string' && entry.cwd.trim().length > 0) {
275
+ server.cwd = entry.cwd.trim();
276
+ }
277
+ if (typeof entry.token === 'string' && entry.token.trim().length > 0) {
278
+ server.token = entry.token.trim();
279
+ }
280
+ if (entry.oauth !== null && typeof entry.oauth === 'object') {
281
+ const oauth = entry.oauth;
282
+ const deviceAuthorizationUrl = typeof oauth.deviceAuthorizationUrl === 'string'
283
+ ? oauth.deviceAuthorizationUrl.trim() : '';
284
+ const tokenUrl = typeof oauth.tokenUrl === 'string' ? oauth.tokenUrl.trim() : '';
285
+ const clientId = typeof oauth.clientId === 'string' ? oauth.clientId.trim() : '';
286
+ if (deviceAuthorizationUrl && tokenUrl && clientId) {
287
+ const parsed = { deviceAuthorizationUrl, tokenUrl, clientId };
288
+ if (Array.isArray(oauth.scopes)) {
289
+ const scopes = oauth.scopes.filter((item) => typeof item === 'string' && item.trim().length > 0).map((item) => item.trim()).slice(0, 20);
290
+ if (scopes.length > 0) {
291
+ parsed.scopes = scopes;
292
+ }
293
+ }
294
+ server.oauth = parsed;
295
+ }
296
+ }
297
+ out[name] = server;
298
+ count += 1;
299
+ }
300
+ return out;
301
+ }
302
+ function normalizeRagConfig(value) {
303
+ if (value === null || typeof value !== 'object') {
304
+ return {};
305
+ }
306
+ const rag = value;
307
+ const out = {};
308
+ const embeddings = normalizeRagEmbeddings(rag.embeddings);
309
+ if (embeddings) {
310
+ out.embeddings = embeddings;
311
+ }
312
+ const remote = normalizeRagRemote(rag.remote);
313
+ if (remote) {
314
+ out.remote = remote;
315
+ }
316
+ return out;
317
+ }
318
+ function normalizeRagEmbeddings(value) {
319
+ if (value === null || typeof value !== 'object') {
320
+ return undefined;
321
+ }
322
+ const entry = value;
323
+ const embeddings = { enabled: entry.enabled !== false };
324
+ const baseUrl = typeof entry.baseUrl === 'string' ? entry.baseUrl.trim() : '';
325
+ if (baseUrl.length > 0) {
326
+ embeddings.baseUrl = baseUrl;
327
+ }
328
+ const model = typeof entry.model === 'string' ? entry.model.trim() : '';
329
+ if (model.length > 0) {
330
+ embeddings.model = model;
331
+ }
332
+ const apiKey = typeof entry.apiKey === 'string' ? entry.apiKey.trim() : '';
333
+ if (apiKey.length > 0) {
334
+ embeddings.apiKey = apiKey;
335
+ }
336
+ const batchSize = clampInt(entry.batchSize, 1, 128);
337
+ if (batchSize !== undefined) {
338
+ embeddings.batchSize = batchSize;
339
+ }
340
+ const maxChunks = clampInt(entry.maxChunks, 100, 10_000);
341
+ if (maxChunks !== undefined) {
342
+ embeddings.maxChunks = maxChunks;
343
+ }
344
+ const dimensions = clampInt(entry.dimensions, 64, 4_096);
345
+ if (dimensions !== undefined) {
346
+ embeddings.dimensions = dimensions;
347
+ }
348
+ return embeddings;
349
+ }
350
+ function normalizeRagRemote(value) {
351
+ if (value === null || typeof value !== 'object') {
352
+ return undefined;
353
+ }
354
+ const entry = value;
355
+ const baseUrl = typeof entry.baseUrl === 'string' ? entry.baseUrl.trim() : '';
356
+ if (baseUrl.length === 0) {
357
+ return undefined;
358
+ }
359
+ const remote = { baseUrl };
360
+ const apiKey = typeof entry.apiKey === 'string' ? entry.apiKey.trim() : '';
361
+ if (apiKey.length > 0) {
362
+ remote.apiKey = apiKey;
363
+ }
364
+ const timeoutMs = clampInt(entry.timeoutMs, 500, 60_000);
365
+ if (timeoutMs !== undefined) {
366
+ remote.timeoutMs = timeoutMs;
367
+ }
368
+ const topK = clampInt(entry.topK, 1, 20);
369
+ if (topK !== undefined) {
370
+ remote.topK = topK;
371
+ }
372
+ return remote;
373
+ }
374
+ function clampInt(value, min, max) {
375
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
376
+ return undefined;
377
+ }
378
+ return Math.min(max, Math.max(min, Math.floor(value)));
379
+ }
380
+ function normalizeLspConfig(value) {
381
+ if (value === null || typeof value !== 'object') {
382
+ return {};
383
+ }
384
+ const raw = value.servers;
385
+ if (raw === null || typeof raw !== 'object') {
386
+ return {};
387
+ }
388
+ const servers = {};
389
+ let count = 0;
390
+ for (const [language, entry] of Object.entries(raw)) {
391
+ if (count >= 20) {
392
+ break;
393
+ }
394
+ if (entry === null || typeof entry !== 'object') {
395
+ continue;
396
+ }
397
+ const record = entry;
398
+ if (typeof record.command !== 'string' || record.command.trim().length === 0) {
399
+ continue;
400
+ }
401
+ const server = { command: record.command.trim() };
402
+ if (Array.isArray(record.args)) {
403
+ const args = record.args.filter((item) => typeof item === 'string');
404
+ if (args.length > 0) {
405
+ server.args = args;
406
+ }
407
+ }
408
+ servers[language] = server;
409
+ count += 1;
410
+ }
411
+ return Object.keys(servers).length > 0 ? { servers } : {};
412
+ }
413
+ function normalizePluginsConfig(value) {
414
+ if (value === null || typeof value !== 'object') {
415
+ return { disabled: [] };
416
+ }
417
+ const record = value;
418
+ const raw = record.disabled;
419
+ const disabled = Array.isArray(raw)
420
+ ? raw
421
+ .filter((item) => typeof item === 'string' && item.trim().length > 0)
422
+ .map((item) => item.trim())
423
+ .slice(0, 100)
424
+ : [];
425
+ const registryUrl = typeof record.registryUrl === 'string' ? record.registryUrl.trim() : '';
426
+ return {
427
+ disabled,
428
+ ...(registryUrl.length > 0 ? { registryUrl } : {}),
429
+ };
430
+ }
431
+ function normalizeOAuthConfig(value) {
432
+ if (value === null || typeof value !== 'object') {
433
+ return {};
434
+ }
435
+ const record = value;
436
+ const oauth = {};
437
+ const deviceAuthorizationUrl = typeof record.deviceAuthorizationUrl === 'string'
438
+ ? record.deviceAuthorizationUrl.trim() : '';
439
+ if (deviceAuthorizationUrl.length > 0) {
440
+ oauth.deviceAuthorizationUrl = deviceAuthorizationUrl;
441
+ }
442
+ const tokenUrl = typeof record.tokenUrl === 'string' ? record.tokenUrl.trim() : '';
443
+ if (tokenUrl.length > 0) {
444
+ oauth.tokenUrl = tokenUrl;
445
+ }
446
+ const clientId = typeof record.clientId === 'string' ? record.clientId.trim() : '';
447
+ if (clientId.length > 0) {
448
+ oauth.clientId = clientId;
449
+ }
450
+ if (Array.isArray(record.scopes)) {
451
+ const scopes = record.scopes
452
+ .filter((item) => typeof item === 'string' && item.trim().length > 0)
453
+ .map((item) => item.trim())
454
+ .slice(0, 20);
455
+ if (scopes.length > 0) {
456
+ oauth.scopes = scopes;
457
+ }
458
+ }
459
+ const provider = typeof record.provider === 'string' ? record.provider.trim() : '';
460
+ if (provider.length > 0) {
461
+ oauth.provider = provider;
462
+ }
463
+ const name = typeof record.name === 'string' ? record.name.trim() : '';
464
+ if (name.length > 0) {
465
+ oauth.name = name;
466
+ }
467
+ return oauth;
468
+ }
469
+ function normalizeWebConfig(value) {
470
+ if (value === null || typeof value !== 'object') {
471
+ return {};
472
+ }
473
+ const record = value;
474
+ const searchUrl = typeof record.searchUrl === 'string' ? record.searchUrl.trim() : '';
475
+ return searchUrl.length > 0 ? { searchUrl } : {};
476
+ }
477
+ function normalizeSandboxConfig(value) {
478
+ if (value === null || typeof value !== 'object') {
479
+ return { mode: 'off' };
480
+ }
481
+ const record = value;
482
+ if (record.mode !== 'docker') {
483
+ return { mode: 'off' };
484
+ }
485
+ const sandbox = { mode: 'docker' };
486
+ const image = typeof record.image === 'string' ? record.image.trim() : '';
487
+ if (image.length > 0) {
488
+ sandbox.image = image;
489
+ }
490
+ if (record.network === true) {
491
+ sandbox.network = true;
492
+ }
493
+ if (record.allowFallback === true) {
494
+ sandbox.allowFallback = true;
495
+ }
496
+ if (Array.isArray(record.extraArgs)) {
497
+ const extraArgs = record.extraArgs
498
+ .filter((item) => typeof item === 'string' && item.trim().length > 0)
499
+ .map((item) => item.trim())
500
+ .slice(0, 20);
501
+ if (extraArgs.length > 0) {
502
+ sandbox.extraArgs = extraArgs;
503
+ }
504
+ }
505
+ return sandbox;
506
+ }
507
+ function normalizeStringArray(value, cap) {
508
+ if (!Array.isArray(value)) {
509
+ return [];
510
+ }
511
+ return value
512
+ .filter((item) => typeof item === 'string' && item.trim().length > 0)
513
+ .map((item) => item.trim())
514
+ .slice(0, cap);
515
+ }
516
+ function normalizeStringMap(value, cap) {
517
+ if (value === null || typeof value !== 'object') {
518
+ return {};
519
+ }
520
+ const out = {};
521
+ let count = 0;
522
+ for (const [key, item] of Object.entries(value)) {
523
+ if (count >= cap) {
524
+ break;
525
+ }
526
+ if (typeof item !== 'string' || item.trim().length === 0) {
527
+ continue;
528
+ }
529
+ out[key.trim()] = item.trim();
530
+ count += 1;
531
+ }
532
+ return out;
533
+ }
534
+ function normalizeProvider(value) {
535
+ if (value === null || typeof value !== 'object') {
536
+ return null;
537
+ }
538
+ const record = value;
539
+ const kind = record.kind === 'anthropic' ? 'anthropic' : 'openai';
540
+ const baseUrl = typeof record.baseUrl === 'string' ? record.baseUrl.trim() : '';
541
+ const model = typeof record.model === 'string' ? record.model.trim() : '';
542
+ const apiKey = typeof record.apiKey === 'string' ? record.apiKey.trim() : '';
543
+ if (baseUrl.length === 0 || model.length === 0) {
544
+ return null;
545
+ }
546
+ const maxTokens = typeof record.maxTokens === 'number' && record.maxTokens > 0
547
+ ? Math.floor(record.maxTokens)
548
+ : undefined;
549
+ const auth = record.auth === 'bearer' ? 'bearer' : undefined;
550
+ return { kind, baseUrl: normalizeBaseUrl(baseUrl), model, apiKey, maxTokens, auth };
551
+ }
552
+ export function normalizeBaseUrl(url) {
553
+ return url.trim().replace(/\/+$/, '');
554
+ }
555
+ export function saveConfig(config, file = configPath()) {
556
+ fs.mkdirSync(path.dirname(file), { recursive: true });
557
+ fs.writeFileSync(file, JSON.stringify(config, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
558
+ try {
559
+ fs.chmodSync(file, 0o600);
560
+ }
561
+ catch {
562
+ // Windows ignores POSIX modes; the file still lives under the user profile.
563
+ }
564
+ }
565
+ export function activeProvider(config) {
566
+ const name = config.active;
567
+ if (!name) {
568
+ return null;
569
+ }
570
+ const provider = config.providers[name];
571
+ if (!provider) {
572
+ return null;
573
+ }
574
+ return { name, provider };
575
+ }
576
+ export function presetById(id) {
577
+ return PRESETS.find((preset) => preset.id === id);
578
+ }
579
+ export function upsertProvider(config, name, provider) {
580
+ return {
581
+ ...config,
582
+ active: name,
583
+ providers: { ...config.providers, [name]: provider },
584
+ };
585
+ }
586
+ /** Local gateways (Ollama, LM Studio, etc.) do not need an API key. */
587
+ export function needsApiKey(baseUrl) {
588
+ return !/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(?::|\/|$)/i.test(baseUrl);
589
+ }
590
+ /** Problems that must be fixed before the first request is sent. */
591
+ export function configProblems(config) {
592
+ const problems = [];
593
+ const active = activeProvider(config);
594
+ if (!active) {
595
+ problems.push('还没有配置模型:运行 minko config 选择服务商并填入 API Key');
596
+ return problems;
597
+ }
598
+ if (active.provider.apiKey.length === 0 && needsApiKey(active.provider.baseUrl)) {
599
+ problems.push('模型 ' + active.name + ' 还没有 API Key:运行 minko config --api-key <key>');
600
+ }
601
+ if (!/^https?:\/\//i.test(active.provider.baseUrl)) {
602
+ problems.push('服务地址必须以 http:// 或 https:// 开头');
603
+ }
604
+ return problems;
605
+ }