@bike4mind/cli 0.2.13 → 0.2.14-feat-cli-mcp-management.17477
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/bin/bike4mind-cli.mjs +72 -1
- package/dist/ImageStore-MMUOUPI2.js +0 -0
- package/dist/artifactExtractor-BRHUIR7I.js +0 -0
- package/dist/chunk-A7POWM75.js +0 -0
- package/dist/{chunk-K44CDTQN.js → chunk-B6HTF2IE.js} +1 -1
- package/dist/chunk-BDQBOLYG.js +0 -0
- package/dist/chunk-BPFEGDC7.js +0 -0
- package/dist/chunk-FFJX3FF3.js +611 -0
- package/dist/{chunk-SZXO7HOF.js → chunk-GFL7CMSL.js} +56 -17
- package/dist/chunk-OCYRD7D6.js +0 -0
- package/dist/{chunk-3MT5HI3G.js → chunk-SKGB6N3S.js} +1 -1
- package/dist/{chunk-PMY7IPOC.js → chunk-TOMWAKIL.js} +1 -1
- package/dist/commands/mcpCommand.js +201 -0
- package/dist/{create-X2GOA457.js → create-UFJ2VDFP.js} +3 -3
- package/dist/formatConverter-5QEJDW24.js +0 -0
- package/dist/index.js +1293 -1787
- package/dist/llmMarkdownGenerator-AHTZFV2T.js +0 -0
- package/dist/markdownGenerator-BLXDOZWQ.js +0 -0
- package/dist/{mementoService-ZAFUWL4U.js → mementoService-5E5ZJYYF.js} +3 -3
- package/dist/notificationDeduplicator-UTHJHMSZ.js +0 -0
- package/dist/src-SIU2KHTA.js +0 -0
- package/dist/{src-STUEUNUB.js → src-ZJYA6KJ4.js} +16 -2
- package/dist/{subtractCredits-KRFWXMZH.js → subtractCredits-W7S7KHAY.js} +3 -3
- package/dist/utils-JCHWDM4Z.js +0 -0
- package/package.json +19 -17
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/storage/ConfigStore.ts
|
|
4
|
+
import { promises as fs, existsSync } from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { v4 as uuidv4 } from "uuid";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
var AuthTokensSchema = z.object({
|
|
10
|
+
accessToken: z.string(),
|
|
11
|
+
refreshToken: z.string(),
|
|
12
|
+
expiresAt: z.string(),
|
|
13
|
+
userId: z.string()
|
|
14
|
+
});
|
|
15
|
+
var ApiConfigSchema = z.object({
|
|
16
|
+
customUrl: z.string().url().optional()
|
|
17
|
+
});
|
|
18
|
+
var CliConfigSchema = z.object({
|
|
19
|
+
version: z.string(),
|
|
20
|
+
userId: z.string(),
|
|
21
|
+
auth: AuthTokensSchema.optional(),
|
|
22
|
+
defaultModel: z.string(),
|
|
23
|
+
apiConfig: ApiConfigSchema.optional(),
|
|
24
|
+
toolApiKeys: z.object({
|
|
25
|
+
openweather: z.string().optional(),
|
|
26
|
+
serper: z.string().optional()
|
|
27
|
+
}).optional(),
|
|
28
|
+
mcpServers: z.array(
|
|
29
|
+
z.object({
|
|
30
|
+
name: z.string(),
|
|
31
|
+
command: z.string().optional(),
|
|
32
|
+
args: z.array(z.string()).optional(),
|
|
33
|
+
env: z.record(z.string()),
|
|
34
|
+
enabled: z.boolean()
|
|
35
|
+
})
|
|
36
|
+
),
|
|
37
|
+
preferences: z.object({
|
|
38
|
+
maxTokens: z.number(),
|
|
39
|
+
temperature: z.number(),
|
|
40
|
+
autoSave: z.boolean(),
|
|
41
|
+
theme: z.enum(["light", "dark"]),
|
|
42
|
+
exportFormat: z.enum(["markdown", "json"]),
|
|
43
|
+
maxIterations: z.number().nullable().default(10)
|
|
44
|
+
}),
|
|
45
|
+
tools: z.object({
|
|
46
|
+
enabled: z.array(z.string()),
|
|
47
|
+
disabled: z.array(z.string()),
|
|
48
|
+
config: z.record(z.any())
|
|
49
|
+
}),
|
|
50
|
+
trustedTools: z.array(z.string()).optional().default([])
|
|
51
|
+
});
|
|
52
|
+
var ProjectConfigSchema = z.object({
|
|
53
|
+
tools: z.object({
|
|
54
|
+
enabled: z.array(z.string()).optional(),
|
|
55
|
+
denied: z.array(z.string()).optional(),
|
|
56
|
+
config: z.record(z.any()).optional()
|
|
57
|
+
}).optional(),
|
|
58
|
+
defaultModel: z.string().optional(),
|
|
59
|
+
mcpServers: z.array(
|
|
60
|
+
z.object({
|
|
61
|
+
name: z.string(),
|
|
62
|
+
command: z.string().optional(),
|
|
63
|
+
args: z.array(z.string()).optional(),
|
|
64
|
+
env: z.record(z.string()),
|
|
65
|
+
enabled: z.boolean()
|
|
66
|
+
})
|
|
67
|
+
).optional(),
|
|
68
|
+
preferences: z.object({
|
|
69
|
+
maxTokens: z.number().optional(),
|
|
70
|
+
temperature: z.number().optional(),
|
|
71
|
+
autoSave: z.boolean().optional(),
|
|
72
|
+
theme: z.enum(["light", "dark"]).optional(),
|
|
73
|
+
exportFormat: z.enum(["markdown", "json"]).optional()
|
|
74
|
+
}).optional()
|
|
75
|
+
});
|
|
76
|
+
var ProjectLocalConfigSchema = z.object({
|
|
77
|
+
trustedTools: z.array(z.string()).optional(),
|
|
78
|
+
toolApiKeys: z.object({
|
|
79
|
+
openweather: z.string().optional(),
|
|
80
|
+
serper: z.string().optional()
|
|
81
|
+
}).optional(),
|
|
82
|
+
preferences: z.object({
|
|
83
|
+
maxTokens: z.number().optional(),
|
|
84
|
+
temperature: z.number().optional(),
|
|
85
|
+
autoSave: z.boolean().optional(),
|
|
86
|
+
theme: z.enum(["light", "dark"]).optional(),
|
|
87
|
+
exportFormat: z.enum(["markdown", "json"]).optional()
|
|
88
|
+
}).optional(),
|
|
89
|
+
mcpServers: z.array(
|
|
90
|
+
z.object({
|
|
91
|
+
name: z.string(),
|
|
92
|
+
command: z.string().optional(),
|
|
93
|
+
args: z.array(z.string()).optional(),
|
|
94
|
+
env: z.record(z.string()),
|
|
95
|
+
enabled: z.boolean()
|
|
96
|
+
})
|
|
97
|
+
).optional()
|
|
98
|
+
});
|
|
99
|
+
var DEFAULT_CONFIG = {
|
|
100
|
+
version: "0.1.0",
|
|
101
|
+
userId: uuidv4(),
|
|
102
|
+
defaultModel: "claude-sonnet-4-5-20250929",
|
|
103
|
+
toolApiKeys: {
|
|
104
|
+
openweather: void 0,
|
|
105
|
+
serper: void 0
|
|
106
|
+
},
|
|
107
|
+
mcpServers: [],
|
|
108
|
+
preferences: {
|
|
109
|
+
maxTokens: 4096,
|
|
110
|
+
temperature: 0.7,
|
|
111
|
+
autoSave: true,
|
|
112
|
+
theme: "dark",
|
|
113
|
+
exportFormat: "markdown",
|
|
114
|
+
maxIterations: 10
|
|
115
|
+
},
|
|
116
|
+
tools: {
|
|
117
|
+
enabled: [],
|
|
118
|
+
disabled: ["blog_publish", "blog_edit", "blog_draft"],
|
|
119
|
+
// Web-only tools
|
|
120
|
+
config: {}
|
|
121
|
+
},
|
|
122
|
+
trustedTools: []
|
|
123
|
+
// No tools trusted by default
|
|
124
|
+
};
|
|
125
|
+
function findProjectConfigDir(startDir = process.cwd()) {
|
|
126
|
+
let currentDir = startDir;
|
|
127
|
+
const { root } = path.parse(currentDir);
|
|
128
|
+
while (currentDir !== root) {
|
|
129
|
+
const gitPath = path.join(currentDir, ".git");
|
|
130
|
+
try {
|
|
131
|
+
if (existsSync(gitPath)) {
|
|
132
|
+
return currentDir;
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
currentDir = path.dirname(currentDir);
|
|
137
|
+
}
|
|
138
|
+
return process.cwd();
|
|
139
|
+
}
|
|
140
|
+
async function loadProjectConfig(projectDir) {
|
|
141
|
+
const configPath = path.join(projectDir, ".bike4mind", "config.json");
|
|
142
|
+
try {
|
|
143
|
+
const data = await fs.readFile(configPath, "utf-8");
|
|
144
|
+
const rawConfig = JSON.parse(data);
|
|
145
|
+
const validated = ProjectConfigSchema.parse(rawConfig);
|
|
146
|
+
return validated;
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (error.code === "ENOENT") {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
if (error instanceof z.ZodError) {
|
|
152
|
+
console.error("Project config validation error:", error.errors);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
console.error("Failed to load project config:", error);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async function loadProjectLocalConfig(projectDir) {
|
|
160
|
+
const configPath = path.join(projectDir, ".bike4mind", "local.json");
|
|
161
|
+
try {
|
|
162
|
+
const data = await fs.readFile(configPath, "utf-8");
|
|
163
|
+
const rawConfig = JSON.parse(data);
|
|
164
|
+
const validated = ProjectLocalConfigSchema.parse(rawConfig);
|
|
165
|
+
return validated;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error.code === "ENOENT") {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
if (error instanceof z.ZodError) {
|
|
171
|
+
console.error("Project local config validation error:", error.errors);
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
console.error("Failed to load project local config:", error);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function mergeMcpServers(...serverArrays) {
|
|
179
|
+
const serverMap = /* @__PURE__ */ new Map();
|
|
180
|
+
for (const servers of serverArrays) {
|
|
181
|
+
if (servers) {
|
|
182
|
+
for (const server of servers) {
|
|
183
|
+
serverMap.set(server.name, server);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return Array.from(serverMap.values());
|
|
188
|
+
}
|
|
189
|
+
function mergeConfigs(global, project, local) {
|
|
190
|
+
const merged = { ...global };
|
|
191
|
+
if (project) {
|
|
192
|
+
if (project.defaultModel) {
|
|
193
|
+
merged.defaultModel = project.defaultModel;
|
|
194
|
+
}
|
|
195
|
+
if (project.preferences) {
|
|
196
|
+
merged.preferences = {
|
|
197
|
+
...merged.preferences,
|
|
198
|
+
...project.preferences
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
if (project.tools) {
|
|
202
|
+
merged.tools = {
|
|
203
|
+
...merged.tools,
|
|
204
|
+
enabled: [...merged.tools.enabled || [], ...project.tools.enabled || []],
|
|
205
|
+
disabled: [...merged.tools.disabled, ...project.tools.denied || []],
|
|
206
|
+
config: {
|
|
207
|
+
...merged.tools.config,
|
|
208
|
+
...project.tools.config
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (project.mcpServers) {
|
|
213
|
+
merged.mcpServers = mergeMcpServers(merged.mcpServers, project.mcpServers);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (local) {
|
|
217
|
+
if (local.trustedTools) {
|
|
218
|
+
const trustedSet = /* @__PURE__ */ new Set([...merged.trustedTools || [], ...local.trustedTools]);
|
|
219
|
+
merged.trustedTools = Array.from(trustedSet);
|
|
220
|
+
}
|
|
221
|
+
if (local.toolApiKeys) {
|
|
222
|
+
merged.toolApiKeys = {
|
|
223
|
+
...merged.toolApiKeys,
|
|
224
|
+
...local.toolApiKeys
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
if (local.preferences) {
|
|
228
|
+
merged.preferences = {
|
|
229
|
+
...merged.preferences,
|
|
230
|
+
...local.preferences
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
if (local.mcpServers) {
|
|
234
|
+
merged.mcpServers = mergeMcpServers(merged.mcpServers, local.mcpServers);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return merged;
|
|
238
|
+
}
|
|
239
|
+
var ConfigStore = class {
|
|
240
|
+
constructor(configPath) {
|
|
241
|
+
this.config = null;
|
|
242
|
+
this.projectConfigDir = null;
|
|
243
|
+
this.configPath = configPath || path.join(homedir(), ".bike4mind", "config.json");
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Initialize config directory
|
|
247
|
+
*/
|
|
248
|
+
async init() {
|
|
249
|
+
const dir = path.dirname(this.configPath);
|
|
250
|
+
try {
|
|
251
|
+
await fs.mkdir(dir, { recursive: true });
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.error("Failed to initialize config directory:", error);
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Load configuration from disk with Zod validation
|
|
259
|
+
* Merges global → project → local configs
|
|
260
|
+
*/
|
|
261
|
+
async load() {
|
|
262
|
+
if (this.config) {
|
|
263
|
+
return this.config;
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
let globalConfig;
|
|
267
|
+
try {
|
|
268
|
+
try {
|
|
269
|
+
const stats = await fs.stat(this.configPath);
|
|
270
|
+
const mode = stats.mode & 511;
|
|
271
|
+
if (mode !== 384) {
|
|
272
|
+
console.warn(`\u26A0\uFE0F Config file has insecure permissions (${mode.toString(8)}). Setting to 0600...`);
|
|
273
|
+
await fs.chmod(this.configPath, 384);
|
|
274
|
+
}
|
|
275
|
+
} catch (statError) {
|
|
276
|
+
}
|
|
277
|
+
const data = await fs.readFile(this.configPath, "utf-8");
|
|
278
|
+
const rawConfig = JSON.parse(data);
|
|
279
|
+
if (rawConfig.apiConfig && "environment" in rawConfig.apiConfig) {
|
|
280
|
+
const oldApiConfig = rawConfig.apiConfig;
|
|
281
|
+
if (oldApiConfig.environment === "custom" && oldApiConfig.customUrl) {
|
|
282
|
+
rawConfig.apiConfig = { customUrl: oldApiConfig.customUrl };
|
|
283
|
+
} else {
|
|
284
|
+
rawConfig.apiConfig = {};
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const validated = CliConfigSchema.parse(rawConfig);
|
|
288
|
+
globalConfig = {
|
|
289
|
+
...DEFAULT_CONFIG,
|
|
290
|
+
...validated,
|
|
291
|
+
auth: validated.auth,
|
|
292
|
+
// Explicitly preserve auth field
|
|
293
|
+
preferences: {
|
|
294
|
+
...DEFAULT_CONFIG.preferences,
|
|
295
|
+
...validated.preferences
|
|
296
|
+
},
|
|
297
|
+
tools: {
|
|
298
|
+
...DEFAULT_CONFIG.tools,
|
|
299
|
+
...validated.tools
|
|
300
|
+
},
|
|
301
|
+
toolApiKeys: {
|
|
302
|
+
...DEFAULT_CONFIG.toolApiKeys,
|
|
303
|
+
...validated.toolApiKeys || {}
|
|
304
|
+
},
|
|
305
|
+
trustedTools: validated.trustedTools || []
|
|
306
|
+
};
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (error.code === "ENOENT") {
|
|
309
|
+
globalConfig = { ...DEFAULT_CONFIG };
|
|
310
|
+
} else if (error instanceof z.ZodError) {
|
|
311
|
+
console.error("Global config validation error:", error.errors);
|
|
312
|
+
console.error("Using default configuration");
|
|
313
|
+
globalConfig = { ...DEFAULT_CONFIG };
|
|
314
|
+
} else {
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
let projectConfig = null;
|
|
319
|
+
let projectLocalConfig = null;
|
|
320
|
+
if (process.env.B4M_NO_PROJECT_CONFIG !== "1") {
|
|
321
|
+
this.projectConfigDir = findProjectConfigDir();
|
|
322
|
+
if (this.projectConfigDir) {
|
|
323
|
+
projectConfig = await loadProjectConfig(this.projectConfigDir);
|
|
324
|
+
projectLocalConfig = await loadProjectLocalConfig(this.projectConfigDir);
|
|
325
|
+
if (projectConfig) {
|
|
326
|
+
console.log(`\u{1F4C1} Project config loaded from: ${this.projectConfigDir}/.bike4mind/`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
} else {
|
|
330
|
+
this.projectConfigDir = null;
|
|
331
|
+
}
|
|
332
|
+
this.config = mergeConfigs(globalConfig, projectConfig, projectLocalConfig);
|
|
333
|
+
return this.config;
|
|
334
|
+
} catch (error) {
|
|
335
|
+
if (error.code === "ENOENT") {
|
|
336
|
+
return this.reset();
|
|
337
|
+
}
|
|
338
|
+
if (error instanceof z.ZodError) {
|
|
339
|
+
console.error("Config validation error:", error.errors);
|
|
340
|
+
console.error("Resetting to default configuration");
|
|
341
|
+
return this.reset();
|
|
342
|
+
}
|
|
343
|
+
console.error("Failed to load config:", error);
|
|
344
|
+
throw error;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Save configuration to disk
|
|
349
|
+
*/
|
|
350
|
+
async save(config) {
|
|
351
|
+
await this.init();
|
|
352
|
+
if (config) {
|
|
353
|
+
const existingConfig = await this.load();
|
|
354
|
+
this.config = {
|
|
355
|
+
...existingConfig,
|
|
356
|
+
...config,
|
|
357
|
+
auth: config.auth !== void 0 ? config.auth : existingConfig.auth,
|
|
358
|
+
preferences: {
|
|
359
|
+
...existingConfig.preferences,
|
|
360
|
+
...config.preferences || {}
|
|
361
|
+
},
|
|
362
|
+
tools: {
|
|
363
|
+
...existingConfig.tools,
|
|
364
|
+
...config.tools || {}
|
|
365
|
+
},
|
|
366
|
+
toolApiKeys: {
|
|
367
|
+
...existingConfig.toolApiKeys,
|
|
368
|
+
...config.toolApiKeys || {}
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
if (!this.config) {
|
|
373
|
+
throw new Error("No configuration to save");
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
await fs.writeFile(this.configPath, JSON.stringify(this.config, null, 2), "utf-8");
|
|
377
|
+
await fs.chmod(this.configPath, 384);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
console.error("Failed to save config:", error);
|
|
380
|
+
throw error;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Reset configuration to defaults
|
|
385
|
+
*/
|
|
386
|
+
async reset() {
|
|
387
|
+
this.config = { ...DEFAULT_CONFIG, userId: uuidv4() };
|
|
388
|
+
await this.save();
|
|
389
|
+
return this.config;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Get current configuration
|
|
393
|
+
*/
|
|
394
|
+
async get() {
|
|
395
|
+
return this.load();
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Update a specific configuration value
|
|
399
|
+
*/
|
|
400
|
+
async update(updates) {
|
|
401
|
+
await this.save(updates);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Add MCP server configuration
|
|
405
|
+
*/
|
|
406
|
+
async addMcpServer(server) {
|
|
407
|
+
const config = await this.load();
|
|
408
|
+
config.mcpServers = config.mcpServers.filter((s) => s.name !== server.name);
|
|
409
|
+
config.mcpServers.push(server);
|
|
410
|
+
await this.save(config);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Remove MCP server configuration
|
|
414
|
+
*/
|
|
415
|
+
async removeMcpServer(name) {
|
|
416
|
+
const config = await this.load();
|
|
417
|
+
config.mcpServers = config.mcpServers.filter((s) => s.name !== name);
|
|
418
|
+
await this.save(config);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Enable/disable MCP server
|
|
422
|
+
*/
|
|
423
|
+
async toggleMcpServer(name, enabled) {
|
|
424
|
+
const config = await this.load();
|
|
425
|
+
const server = config.mcpServers.find((s) => s.name === name);
|
|
426
|
+
if (server) {
|
|
427
|
+
server.enabled = enabled;
|
|
428
|
+
await this.save(config);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Add a tool to trusted tools list
|
|
433
|
+
*/
|
|
434
|
+
async trustTool(toolName) {
|
|
435
|
+
const config = await this.load();
|
|
436
|
+
if (!config.trustedTools) {
|
|
437
|
+
config.trustedTools = [];
|
|
438
|
+
}
|
|
439
|
+
if (!config.trustedTools.includes(toolName)) {
|
|
440
|
+
config.trustedTools.push(toolName);
|
|
441
|
+
await this.save(config);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Remove a tool from trusted tools list
|
|
446
|
+
*/
|
|
447
|
+
async untrustTool(toolName) {
|
|
448
|
+
const config = await this.load();
|
|
449
|
+
if (config.trustedTools) {
|
|
450
|
+
config.trustedTools = config.trustedTools.filter((t) => t !== toolName);
|
|
451
|
+
await this.save(config);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Get list of trusted tools
|
|
456
|
+
*/
|
|
457
|
+
async getTrustedTools() {
|
|
458
|
+
const config = await this.load();
|
|
459
|
+
return config.trustedTools || [];
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Clear all trusted tools
|
|
463
|
+
*/
|
|
464
|
+
async clearTrustedTools() {
|
|
465
|
+
const config = await this.load();
|
|
466
|
+
config.trustedTools = [];
|
|
467
|
+
await this.save(config);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Get authentication tokens
|
|
471
|
+
*/
|
|
472
|
+
async getAuthTokens() {
|
|
473
|
+
const config = await this.load();
|
|
474
|
+
return config.auth || null;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Set authentication tokens
|
|
478
|
+
*/
|
|
479
|
+
async setAuthTokens(tokens) {
|
|
480
|
+
const config = await this.load();
|
|
481
|
+
config.auth = tokens;
|
|
482
|
+
await this.save(config);
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Clear authentication tokens (logout)
|
|
486
|
+
*/
|
|
487
|
+
async clearAuthTokens() {
|
|
488
|
+
const config = await this.load();
|
|
489
|
+
config.auth = void 0;
|
|
490
|
+
await this.save(config);
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Check if user is authenticated
|
|
494
|
+
*/
|
|
495
|
+
async isAuthenticated() {
|
|
496
|
+
const tokens = await this.getAuthTokens();
|
|
497
|
+
if (!tokens) return false;
|
|
498
|
+
const expiresAt = new Date(tokens.expiresAt);
|
|
499
|
+
return expiresAt > /* @__PURE__ */ new Date();
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Get API configuration
|
|
503
|
+
*/
|
|
504
|
+
async getApiConfig() {
|
|
505
|
+
const config = await this.load();
|
|
506
|
+
return config.apiConfig;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Set custom API URL for self-hosted Bike4Mind instance
|
|
510
|
+
* Pass null to reset to Bike4Mind main service
|
|
511
|
+
*/
|
|
512
|
+
async setCustomApiUrl(url) {
|
|
513
|
+
const config = await this.load();
|
|
514
|
+
if (url === null) {
|
|
515
|
+
config.apiConfig = void 0;
|
|
516
|
+
} else {
|
|
517
|
+
config.apiConfig = { customUrl: url };
|
|
518
|
+
}
|
|
519
|
+
await this.save(config);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Get project config directory (if any)
|
|
523
|
+
*/
|
|
524
|
+
getProjectConfigDir() {
|
|
525
|
+
return this.projectConfigDir;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Initialize project config directory
|
|
529
|
+
* Creates .bike4mind/ directory and ensures local.json is gitignored
|
|
530
|
+
* Does NOT auto-create config.json (user creates that manually)
|
|
531
|
+
*/
|
|
532
|
+
async initProjectConfig() {
|
|
533
|
+
const projectDir = this.projectConfigDir || findProjectConfigDir();
|
|
534
|
+
if (!projectDir) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const configDir = path.join(projectDir, ".bike4mind");
|
|
538
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
539
|
+
await this.ensureGitignore(projectDir);
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Ensure .gitignore includes .bike4mind/local.json
|
|
543
|
+
*/
|
|
544
|
+
async ensureGitignore(projectDir) {
|
|
545
|
+
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
546
|
+
const entryToAdd = ".bike4mind/local.json";
|
|
547
|
+
try {
|
|
548
|
+
let gitignoreContent = "";
|
|
549
|
+
try {
|
|
550
|
+
gitignoreContent = await fs.readFile(gitignorePath, "utf-8");
|
|
551
|
+
} catch {
|
|
552
|
+
}
|
|
553
|
+
if (gitignoreContent.includes(entryToAdd)) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const newContent = gitignoreContent.trim() + (gitignoreContent ? "\n" : "") + `
|
|
557
|
+
# Bike4Mind local config (developer-specific)
|
|
558
|
+
${entryToAdd}
|
|
559
|
+
`;
|
|
560
|
+
await fs.writeFile(gitignorePath, newContent, "utf-8");
|
|
561
|
+
console.log(`\u2705 Added ${entryToAdd} to .gitignore`);
|
|
562
|
+
} catch (error) {
|
|
563
|
+
console.warn(`\u26A0\uFE0F Failed to update .gitignore:`, error);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Save project config to .bike4mind/config.json
|
|
568
|
+
*/
|
|
569
|
+
async saveProjectConfig(config, projectDir) {
|
|
570
|
+
const targetDir = projectDir || this.projectConfigDir || process.cwd();
|
|
571
|
+
const configPath = path.join(targetDir, ".bike4mind", "config.json");
|
|
572
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
573
|
+
const validated = ProjectConfigSchema.parse(config);
|
|
574
|
+
await fs.writeFile(configPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
575
|
+
console.log(`\u2705 Saved project config to: ${configPath}`);
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Save project-local config to .bike4mind/local.json
|
|
579
|
+
*/
|
|
580
|
+
async saveProjectLocalConfig(config, projectDir) {
|
|
581
|
+
const targetDir = projectDir || this.projectConfigDir || process.cwd();
|
|
582
|
+
const configPath = path.join(targetDir, ".bike4mind", "local.json");
|
|
583
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
584
|
+
const validated = ProjectLocalConfigSchema.parse(config);
|
|
585
|
+
await fs.writeFile(configPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
586
|
+
await fs.chmod(configPath, 384);
|
|
587
|
+
console.log(`\u2705 Saved project-local config to: ${configPath}`);
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Load raw project config (without merging)
|
|
591
|
+
*/
|
|
592
|
+
async loadRawProjectConfig() {
|
|
593
|
+
if (!this.projectConfigDir) {
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
return loadProjectConfig(this.projectConfigDir);
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Load raw project-local config (without merging)
|
|
600
|
+
*/
|
|
601
|
+
async loadRawProjectLocalConfig() {
|
|
602
|
+
if (!this.projectConfigDir) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
return loadProjectLocalConfig(this.projectConfigDir);
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
export {
|
|
610
|
+
ConfigStore
|
|
611
|
+
};
|