@bike4mind/cli 0.2.64-worktree-refactor-extract-search-query-builders.21815 → 0.2.64
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 +6 -6
- package/dist/BubblewrapRuntime-BHbtqvLx.mjs +72 -0
- package/dist/ConfigStore-CllM6jOf.mjs +8614 -0
- package/dist/ImageStore-DaKT_Ew8.mjs +202 -0
- package/dist/ProxyManager-Dl2nFk-A.mjs +259 -0
- package/dist/ProxyManager-kiOD1X8-.mjs +3 -0
- package/dist/SandboxOrchestrator-BEW3rqYi.mjs +159 -0
- package/dist/SandboxOrchestrator-CHZgSR3P.mjs +3 -0
- package/dist/SandboxRuntimeAdapter-C1B4t20N.mjs +57 -0
- package/dist/SandboxRuntimeAdapter-D7UAG13n.mjs +3 -0
- package/dist/SeatbeltRuntime-D4m0VOcD.mjs +116 -0
- package/dist/StderrViolationParser-D0afQ3-1.mjs +70 -0
- package/dist/ViolationLogStore-CZl35HcA.mjs +96 -0
- package/dist/bashExecute-BTkdqlSs-5foM20Lb.mjs +466 -0
- package/dist/commands/doctorCommand.mjs +101 -0
- package/dist/commands/headlessCommand.mjs +319 -0
- package/dist/commands/mcpCommand.mjs +218 -0
- package/dist/commands/updateCommand.mjs +40 -0
- package/dist/createFile-yQfh8uvk-I-yM5DxC.mjs +63 -0
- package/dist/deleteFile-DKHfnyny-G3b1Kj2T.mjs +66 -0
- package/dist/globFiles-D1en6joM-8jekiXdX.mjs +100 -0
- package/dist/grepSearch-aMamoBn_-DCJcY8JS.mjs +173 -0
- package/dist/index.mjs +6722 -0
- package/dist/pathValidation-Cgjh5WQO-DiCZTcq6.mjs +63 -0
- package/dist/store-Dw1nZX2Y.mjs +128 -0
- package/dist/store-nZExNOWX.mjs +3 -0
- package/dist/terminalSetup-rmr1P8KF.mjs +254 -0
- package/dist/tools-C6M5aW8W.mjs +20907 -0
- package/dist/treeSitterEngine-DCSXcm_3.mjs +309 -0
- package/dist/types-DBEjF9YS.mjs +59 -0
- package/dist/types-DK3P88Px.mjs +3 -0
- package/dist/updateChecker-Cu9dkHxV.mjs +120 -0
- package/package.json +10 -10
- package/dist/BubblewrapRuntime-PMIOLWKR.js +0 -71
- package/dist/HydrationEngine-YL2HWJ3V.js +0 -9
- package/dist/ImageStore-MMUOUPI2.js +0 -224
- package/dist/ProxyManager-HEB4TLVX.js +0 -7
- package/dist/SandboxOrchestrator-UIJ5GYBB.js +0 -8
- package/dist/SandboxRuntimeAdapter-FQ56MAB2.js +0 -13
- package/dist/SeatbeltRuntime-EE3TTLEP.js +0 -98
- package/dist/StderrViolationParser-7OYPM2DJ.js +0 -59
- package/dist/ViolationLogStore-RIIUVURH.js +0 -104
- package/dist/artifactExtractor-R7DIP2XO.js +0 -180
- package/dist/bashExecute-GLGLD3JD.js +0 -379
- package/dist/chunk-4BIBE3J7.js +0 -48
- package/dist/chunk-5LZS5CVJ.js +0 -161
- package/dist/chunk-BDQBOLYG.js +0 -120
- package/dist/chunk-BPFEGDC7.js +0 -192
- package/dist/chunk-EPIYC3LA.js +0 -13770
- package/dist/chunk-G4ZGEQFT.js +0 -250
- package/dist/chunk-GQGOWACU.js +0 -770
- package/dist/chunk-J6ZBI6TI.js +0 -1079
- package/dist/chunk-JW3JRHH7.js +0 -12433
- package/dist/chunk-KQAMBXAW.js +0 -163
- package/dist/chunk-KUVV2NAB.js +0 -19125
- package/dist/chunk-LTLJRF6I.js +0 -44
- package/dist/chunk-PFBYGCOW.js +0 -449
- package/dist/chunk-QWB6ZYY4.js +0 -48
- package/dist/chunk-SGPRXN4C.js +0 -245
- package/dist/chunk-UZUHPHZC.js +0 -95
- package/dist/chunk-WBE7SQUB.js +0 -241
- package/dist/chunk-Y4WOJJM3.js +0 -147
- package/dist/commands/doctorCommand.js +0 -87
- package/dist/commands/headlessCommand.js +0 -380
- package/dist/commands/mcpCommand.js +0 -203
- package/dist/commands/updateCommand.js +0 -42
- package/dist/create-C4VEEEYR.js +0 -12
- package/dist/createFile-6PSPLW6R.js +0 -71
- package/dist/deleteFile-AUSRLWIK.js +0 -73
- package/dist/formatConverter-5QEJDW24.js +0 -7
- package/dist/globFiles-TSRN64N2.js +0 -120
- package/dist/grepSearch-634XWZOJ.js +0 -216
- package/dist/index.js +0 -6779
- package/dist/llmMarkdownGenerator-Z6NB26TT.js +0 -371
- package/dist/markdownGenerator-SK2ZQQL4.js +0 -269
- package/dist/mementoService-N4IM6QAC.js +0 -12
- package/dist/notificationDeduplicator-HUC53NEW.js +0 -9
- package/dist/src-F4KZCAA2.js +0 -319
- package/dist/src-ISX322I7.js +0 -1101
- package/dist/store-CAB6BV3P.js +0 -11
- package/dist/subtractCredits-D4KEM6VU.js +0 -12
- package/dist/terminalSetup-C5FHMLC3.js +0 -214
- package/dist/treeSitterEngine-4SGFQDY3.js +0 -330
- package/dist/types-KB5NP6T4.js +0 -7
- package/dist/utils-JCHWDM4Z.js +0 -31
package/dist/chunk-J6ZBI6TI.js
DELETED
|
@@ -1,1079 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
ChatModels
|
|
4
|
-
} from "./chunk-JW3JRHH7.js";
|
|
5
|
-
import {
|
|
6
|
-
DEFAULT_SANDBOX_CONFIG
|
|
7
|
-
} from "./chunk-4BIBE3J7.js";
|
|
8
|
-
|
|
9
|
-
// src/utils/Logger.ts
|
|
10
|
-
import fs from "fs/promises";
|
|
11
|
-
import path from "path";
|
|
12
|
-
import os from "os";
|
|
13
|
-
var Logger = class _Logger {
|
|
14
|
-
constructor() {
|
|
15
|
-
this.logFilePath = null;
|
|
16
|
-
this.sessionId = null;
|
|
17
|
-
this.fileLoggingEnabled = true;
|
|
18
|
-
this.consoleVerbose = false;
|
|
19
|
-
}
|
|
20
|
-
static {
|
|
21
|
-
this.instance = null;
|
|
22
|
-
}
|
|
23
|
-
static getInstance() {
|
|
24
|
-
if (!_Logger.instance) {
|
|
25
|
-
_Logger.instance = new _Logger();
|
|
26
|
-
}
|
|
27
|
-
return _Logger.instance;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Initialize the logger with a session ID
|
|
31
|
-
*/
|
|
32
|
-
async initialize(sessionId) {
|
|
33
|
-
this.sessionId = sessionId;
|
|
34
|
-
const debugDir = path.join(os.homedir(), ".bike4mind", "debug");
|
|
35
|
-
await fs.mkdir(debugDir, { recursive: true });
|
|
36
|
-
this.logFilePath = path.join(debugDir, `${sessionId}.txt`);
|
|
37
|
-
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Set whether verbose console logging is enabled
|
|
41
|
-
*/
|
|
42
|
-
setVerbose(enabled) {
|
|
43
|
-
this.consoleVerbose = enabled;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Set whether file logging is enabled
|
|
47
|
-
*/
|
|
48
|
-
setFileLoggingEnabled(enabled) {
|
|
49
|
-
this.fileLoggingEnabled = enabled;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* DEBUG level - verbose-only console, always file
|
|
53
|
-
*/
|
|
54
|
-
debug(message) {
|
|
55
|
-
this.writeToFile("DEBUG", message).catch(() => {
|
|
56
|
-
});
|
|
57
|
-
if (this.consoleVerbose) {
|
|
58
|
-
console.log(message);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* INFO level - always shown to user
|
|
63
|
-
*/
|
|
64
|
-
info(message) {
|
|
65
|
-
this.writeToFile("INFO", message).catch(() => {
|
|
66
|
-
});
|
|
67
|
-
console.log(message);
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* WARN level - always shown to user
|
|
71
|
-
*/
|
|
72
|
-
warn(message) {
|
|
73
|
-
this.writeToFile("WARN", message).catch(() => {
|
|
74
|
-
});
|
|
75
|
-
console.warn(message);
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* ERROR level - always shown to user
|
|
79
|
-
*/
|
|
80
|
-
error(message, err) {
|
|
81
|
-
this.writeToFile("ERROR", message).catch(() => {
|
|
82
|
-
});
|
|
83
|
-
if (err) {
|
|
84
|
-
this.logErrorDetailsToFile(err).catch(() => {
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
console.error(message);
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Write log entry to file
|
|
91
|
-
*/
|
|
92
|
-
async writeToFile(level, message) {
|
|
93
|
-
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
try {
|
|
97
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
98
|
-
const logEntry = `[${timestamp}] [${level}] ${message}
|
|
99
|
-
`;
|
|
100
|
-
await fs.appendFile(this.logFilePath, logEntry, "utf-8");
|
|
101
|
-
} catch (error) {
|
|
102
|
-
console.error("File logging failed:", error);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Log error details to file
|
|
107
|
-
*/
|
|
108
|
-
async logErrorDetailsToFile(err) {
|
|
109
|
-
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
try {
|
|
113
|
-
if (err && typeof err === "object" && "response" in err && err.response) {
|
|
114
|
-
const response = err.response;
|
|
115
|
-
const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
|
|
116
|
-
await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
|
|
117
|
-
await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
|
|
118
|
-
await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
|
|
119
|
-
if (response.data) {
|
|
120
|
-
const errorText = this.extractErrorMessage(response.data);
|
|
121
|
-
if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
|
|
122
|
-
await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
|
|
123
|
-
const parsedError = this.parseHtmlError(errorText);
|
|
124
|
-
if (parsedError) {
|
|
125
|
-
await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
|
|
126
|
-
}
|
|
127
|
-
await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
|
|
128
|
-
} else {
|
|
129
|
-
const preview = this.truncate(errorText, 500);
|
|
130
|
-
await this.writeToFile("ERROR", ` Response: ${preview}`);
|
|
131
|
-
}
|
|
132
|
-
} else {
|
|
133
|
-
await this.writeToFile("ERROR", ` Response: (no data)`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
|
|
137
|
-
const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
|
|
138
|
-
await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
|
|
139
|
-
} else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
140
|
-
await this.writeToFile("ERROR", ` Message: ${err.message}`);
|
|
141
|
-
}
|
|
142
|
-
} catch (error) {
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Safely stringify object, handling circular references
|
|
147
|
-
*/
|
|
148
|
-
safeStringify(obj) {
|
|
149
|
-
try {
|
|
150
|
-
return JSON.stringify(obj);
|
|
151
|
-
} catch (error) {
|
|
152
|
-
if (error instanceof Error && error.message.includes("circular")) {
|
|
153
|
-
try {
|
|
154
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
155
|
-
return JSON.stringify(obj, (key, value) => {
|
|
156
|
-
if (typeof value === "object" && value !== null) {
|
|
157
|
-
if (seen.has(value)) {
|
|
158
|
-
return "[Circular]";
|
|
159
|
-
}
|
|
160
|
-
seen.add(value);
|
|
161
|
-
}
|
|
162
|
-
return value;
|
|
163
|
-
});
|
|
164
|
-
} catch {
|
|
165
|
-
return "[Unable to stringify]";
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return "[Stringify error]";
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Extract readable text from error response data
|
|
173
|
-
*/
|
|
174
|
-
extractErrorMessage(data) {
|
|
175
|
-
if (Buffer.isBuffer(data)) {
|
|
176
|
-
return data.toString("utf-8");
|
|
177
|
-
}
|
|
178
|
-
if (typeof data === "string") {
|
|
179
|
-
return data;
|
|
180
|
-
}
|
|
181
|
-
if (data && typeof data === "object" && "_readableState" in data && data._readableState && typeof data._readableState === "object" && "buffer" in data._readableState && Array.isArray(data._readableState.buffer) && data._readableState.buffer.length > 0) {
|
|
182
|
-
const chunks = [];
|
|
183
|
-
for (const chunk of data._readableState.buffer) {
|
|
184
|
-
if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
|
|
185
|
-
chunks.push(Buffer.from(chunk.data));
|
|
186
|
-
} else if (Buffer.isBuffer(chunk)) {
|
|
187
|
-
chunks.push(chunk);
|
|
188
|
-
} else if (chunk && typeof chunk === "object" && "data" in chunk) {
|
|
189
|
-
if (Buffer.isBuffer(chunk.data)) {
|
|
190
|
-
chunks.push(chunk.data);
|
|
191
|
-
} else if (Array.isArray(chunk.data)) {
|
|
192
|
-
chunks.push(Buffer.from(chunk.data));
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if (chunks.length > 0) {
|
|
197
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return this.safeStringify(data);
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Parse HTML error page to extract error message
|
|
204
|
-
*/
|
|
205
|
-
parseHtmlError(html) {
|
|
206
|
-
const titleMatch = html.match(/<title>(.*?)<\/title>/i);
|
|
207
|
-
const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
|
|
208
|
-
const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
|
|
209
|
-
if (titleMatch && titleMatch[1] !== "Error") {
|
|
210
|
-
return titleMatch[1].trim();
|
|
211
|
-
}
|
|
212
|
-
if (h1Match) {
|
|
213
|
-
return h1Match[1].trim();
|
|
214
|
-
}
|
|
215
|
-
if (bodyMatch) {
|
|
216
|
-
const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
217
|
-
return text.substring(0, 200);
|
|
218
|
-
}
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Truncate string to max length with ellipsis
|
|
223
|
-
*/
|
|
224
|
-
truncate(str, maxLength) {
|
|
225
|
-
if (str.length <= maxLength) {
|
|
226
|
-
return str;
|
|
227
|
-
}
|
|
228
|
-
return str.substring(0, maxLength) + "... [truncated]";
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Format bytes to human-readable size
|
|
232
|
-
*/
|
|
233
|
-
formatBytes(bytes) {
|
|
234
|
-
if (bytes === 0) return "0 B";
|
|
235
|
-
const k = 1024;
|
|
236
|
-
const sizes = ["B", "KB", "MB", "GB"];
|
|
237
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
238
|
-
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Clean up old debug logs (older than 30 days)
|
|
242
|
-
*/
|
|
243
|
-
async cleanupOldLogs() {
|
|
244
|
-
if (!this.fileLoggingEnabled) return;
|
|
245
|
-
try {
|
|
246
|
-
const debugDir = path.join(os.homedir(), ".bike4mind", "debug");
|
|
247
|
-
const files = await fs.readdir(debugDir);
|
|
248
|
-
const now = Date.now();
|
|
249
|
-
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
250
|
-
for (const file of files) {
|
|
251
|
-
const filePath = path.join(debugDir, file);
|
|
252
|
-
const stats = await fs.stat(filePath);
|
|
253
|
-
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
254
|
-
await fs.unlink(filePath);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
} catch (error) {
|
|
258
|
-
console.error("Failed to cleanup old logs:", error);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
var logger = Logger.getInstance();
|
|
263
|
-
logger.setVerbose(process.env.B4M_VERBOSE === "1");
|
|
264
|
-
|
|
265
|
-
// src/storage/ConfigStore.ts
|
|
266
|
-
import { promises as fs2, existsSync } from "fs";
|
|
267
|
-
import path2 from "path";
|
|
268
|
-
import { homedir } from "os";
|
|
269
|
-
import { v4 as uuidv4 } from "uuid";
|
|
270
|
-
import { z } from "zod";
|
|
271
|
-
var SandboxFilesystemSchema = z.object({
|
|
272
|
-
allowedReadPaths: z.array(z.string()).default(DEFAULT_SANDBOX_CONFIG.filesystem.allowedReadPaths),
|
|
273
|
-
deniedPaths: z.array(z.string()).default(DEFAULT_SANDBOX_CONFIG.filesystem.deniedPaths),
|
|
274
|
-
writeOnlyToWorkingDir: z.boolean().default(true)
|
|
275
|
-
});
|
|
276
|
-
var SandboxNetworkSchema = z.object({
|
|
277
|
-
enabled: z.boolean().default(false),
|
|
278
|
-
allowedDomains: z.array(z.string()).default(DEFAULT_SANDBOX_CONFIG.network.allowedDomains)
|
|
279
|
-
});
|
|
280
|
-
var SandboxPlatformSchema = z.object({
|
|
281
|
-
linux: z.object({
|
|
282
|
-
runtime: z.literal("bubblewrap").default("bubblewrap"),
|
|
283
|
-
seccompProfile: z.string().optional()
|
|
284
|
-
}).default({ runtime: "bubblewrap" }),
|
|
285
|
-
macos: z.object({
|
|
286
|
-
runtime: z.literal("seatbelt").default("seatbelt"),
|
|
287
|
-
profileTemplate: z.string().default("default")
|
|
288
|
-
}).default({ runtime: "seatbelt", profileTemplate: "default" })
|
|
289
|
-
});
|
|
290
|
-
var SandboxConfigSchema = z.object({
|
|
291
|
-
enabled: z.boolean().default(false),
|
|
292
|
-
mode: z.enum(["disabled", "auto-allow", "permissions"]).default("disabled"),
|
|
293
|
-
filesystem: SandboxFilesystemSchema.default(DEFAULT_SANDBOX_CONFIG.filesystem),
|
|
294
|
-
network: SandboxNetworkSchema.default(DEFAULT_SANDBOX_CONFIG.network),
|
|
295
|
-
excludedCommands: z.array(z.string()).default(DEFAULT_SANDBOX_CONFIG.excludedCommands),
|
|
296
|
-
allowUnsandboxedCommands: z.boolean().default(true),
|
|
297
|
-
platform: SandboxPlatformSchema.default(DEFAULT_SANDBOX_CONFIG.platform)
|
|
298
|
-
}).refine(
|
|
299
|
-
(config) => {
|
|
300
|
-
if (config.enabled && config.mode === "disabled") return false;
|
|
301
|
-
if (!config.enabled && config.mode !== "disabled") return false;
|
|
302
|
-
return true;
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
message: 'Sandbox config inconsistency: enabled and mode must agree. If enabled=true, mode must be "auto-allow" or "permissions". If mode="disabled", enabled must be false.'
|
|
306
|
-
}
|
|
307
|
-
);
|
|
308
|
-
var PartialSandboxConfigSchema = z.object({
|
|
309
|
-
enabled: z.boolean().optional(),
|
|
310
|
-
mode: z.enum(["disabled", "auto-allow", "permissions"]).optional(),
|
|
311
|
-
filesystem: z.object({
|
|
312
|
-
allowedReadPaths: z.array(z.string()).optional(),
|
|
313
|
-
deniedPaths: z.array(z.string()).optional(),
|
|
314
|
-
writeOnlyToWorkingDir: z.boolean().optional()
|
|
315
|
-
}).optional(),
|
|
316
|
-
network: z.object({
|
|
317
|
-
enabled: z.boolean().optional(),
|
|
318
|
-
allowedDomains: z.array(z.string()).optional()
|
|
319
|
-
}).optional(),
|
|
320
|
-
excludedCommands: z.array(z.string()).optional(),
|
|
321
|
-
allowUnsandboxedCommands: z.boolean().optional(),
|
|
322
|
-
platform: SandboxPlatformSchema.optional()
|
|
323
|
-
}).optional();
|
|
324
|
-
var AuthTokensSchema = z.object({
|
|
325
|
-
accessToken: z.string(),
|
|
326
|
-
refreshToken: z.string(),
|
|
327
|
-
expiresAt: z.string(),
|
|
328
|
-
userId: z.string()
|
|
329
|
-
});
|
|
330
|
-
var ApiConfigSchema = z.object({
|
|
331
|
-
customUrl: z.url().optional()
|
|
332
|
-
});
|
|
333
|
-
var McpServerSchema = z.object({
|
|
334
|
-
name: z.string(),
|
|
335
|
-
command: z.string().optional(),
|
|
336
|
-
args: z.array(z.string()).optional(),
|
|
337
|
-
env: z.record(z.string(), z.string()).prefault({}),
|
|
338
|
-
enabled: z.boolean().prefault(true)
|
|
339
|
-
});
|
|
340
|
-
var McpServersSchema = z.union([
|
|
341
|
-
// Array format (B4M native)
|
|
342
|
-
z.array(McpServerSchema),
|
|
343
|
-
// Object format (portable - compatible with Claude Code)
|
|
344
|
-
z.record(
|
|
345
|
-
z.string(),
|
|
346
|
-
z.object({
|
|
347
|
-
command: z.string().optional(),
|
|
348
|
-
args: z.array(z.string()).optional(),
|
|
349
|
-
env: z.record(z.string(), z.string()).optional(),
|
|
350
|
-
enabled: z.boolean().optional()
|
|
351
|
-
})
|
|
352
|
-
)
|
|
353
|
-
]);
|
|
354
|
-
function normalizeMcpServers(servers) {
|
|
355
|
-
if (Array.isArray(servers)) {
|
|
356
|
-
return servers.map((server) => ({
|
|
357
|
-
name: server.name,
|
|
358
|
-
command: server.command,
|
|
359
|
-
args: server.args,
|
|
360
|
-
env: server.env || {},
|
|
361
|
-
enabled: server.enabled ?? true
|
|
362
|
-
}));
|
|
363
|
-
} else {
|
|
364
|
-
return Object.entries(servers).map(([name, config]) => ({
|
|
365
|
-
name,
|
|
366
|
-
command: config.command,
|
|
367
|
-
args: config.args,
|
|
368
|
-
env: config.env || {},
|
|
369
|
-
enabled: config.enabled ?? true
|
|
370
|
-
}));
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
var CliConfigSchema = z.object({
|
|
374
|
-
version: z.string(),
|
|
375
|
-
userId: z.string(),
|
|
376
|
-
auth: AuthTokensSchema.optional(),
|
|
377
|
-
defaultModel: z.string(),
|
|
378
|
-
apiConfig: ApiConfigSchema.optional(),
|
|
379
|
-
toolApiKeys: z.object({
|
|
380
|
-
openweather: z.string().optional(),
|
|
381
|
-
serper: z.string().optional()
|
|
382
|
-
}).optional(),
|
|
383
|
-
mcpServers: McpServersSchema,
|
|
384
|
-
preferences: z.object({
|
|
385
|
-
maxTokens: z.number(),
|
|
386
|
-
temperature: z.number(),
|
|
387
|
-
autoSave: z.boolean(),
|
|
388
|
-
autoCompact: z.boolean().optional().prefault(true),
|
|
389
|
-
theme: z.enum(["light", "dark"]),
|
|
390
|
-
exportFormat: z.enum(["markdown", "json"]),
|
|
391
|
-
maxIterations: z.number().nullable().prefault(10),
|
|
392
|
-
enableSkillTool: z.boolean().optional().prefault(true),
|
|
393
|
-
enableDynamicAgentCreation: z.boolean().optional().prefault(false)
|
|
394
|
-
}),
|
|
395
|
-
tools: z.object({
|
|
396
|
-
enabled: z.array(z.string()),
|
|
397
|
-
disabled: z.array(z.string()),
|
|
398
|
-
config: z.record(z.string(), z.any())
|
|
399
|
-
}),
|
|
400
|
-
features: z.object({
|
|
401
|
-
tavern: z.boolean().optional()
|
|
402
|
-
}).optional().prefault({}),
|
|
403
|
-
trustedTools: z.array(z.string()).optional().prefault([]),
|
|
404
|
-
sandbox: SandboxConfigSchema.optional(),
|
|
405
|
-
additionalDirectories: z.array(z.string()).optional().prefault([])
|
|
406
|
-
});
|
|
407
|
-
var ProjectConfigSchema = z.object({
|
|
408
|
-
tools: z.object({
|
|
409
|
-
enabled: z.array(z.string()).optional(),
|
|
410
|
-
denied: z.array(z.string()).optional(),
|
|
411
|
-
config: z.record(z.string(), z.any()).optional()
|
|
412
|
-
}).optional(),
|
|
413
|
-
defaultModel: z.string().optional(),
|
|
414
|
-
mcpServers: McpServersSchema.optional(),
|
|
415
|
-
preferences: z.object({
|
|
416
|
-
maxTokens: z.number().optional(),
|
|
417
|
-
temperature: z.number().optional(),
|
|
418
|
-
autoSave: z.boolean().optional(),
|
|
419
|
-
autoCompact: z.boolean().optional(),
|
|
420
|
-
theme: z.enum(["light", "dark"]).optional(),
|
|
421
|
-
exportFormat: z.enum(["markdown", "json"]).optional(),
|
|
422
|
-
enableSkillTool: z.boolean().optional(),
|
|
423
|
-
enableDynamicAgentCreation: z.boolean().optional()
|
|
424
|
-
}).optional(),
|
|
425
|
-
sandbox: PartialSandboxConfigSchema,
|
|
426
|
-
additionalDirectories: z.array(z.string()).optional()
|
|
427
|
-
});
|
|
428
|
-
var ProjectLocalConfigSchema = z.object({
|
|
429
|
-
trustedTools: z.array(z.string()).optional(),
|
|
430
|
-
toolApiKeys: z.object({
|
|
431
|
-
openweather: z.string().optional(),
|
|
432
|
-
serper: z.string().optional()
|
|
433
|
-
}).optional(),
|
|
434
|
-
preferences: z.object({
|
|
435
|
-
maxTokens: z.number().optional(),
|
|
436
|
-
temperature: z.number().optional(),
|
|
437
|
-
autoSave: z.boolean().optional(),
|
|
438
|
-
autoCompact: z.boolean().optional(),
|
|
439
|
-
theme: z.enum(["light", "dark"]).optional(),
|
|
440
|
-
exportFormat: z.enum(["markdown", "json"]).optional(),
|
|
441
|
-
enableSkillTool: z.boolean().optional(),
|
|
442
|
-
enableDynamicAgentCreation: z.boolean().optional()
|
|
443
|
-
}).optional(),
|
|
444
|
-
mcpServers: McpServersSchema.optional(),
|
|
445
|
-
sandbox: PartialSandboxConfigSchema
|
|
446
|
-
});
|
|
447
|
-
var DEFAULT_CONFIG = {
|
|
448
|
-
version: "0.1.0",
|
|
449
|
-
userId: uuidv4(),
|
|
450
|
-
defaultModel: ChatModels.CLAUDE_4_5_SONNET,
|
|
451
|
-
toolApiKeys: {
|
|
452
|
-
openweather: void 0,
|
|
453
|
-
serper: void 0
|
|
454
|
-
},
|
|
455
|
-
mcpServers: [],
|
|
456
|
-
preferences: {
|
|
457
|
-
maxTokens: 4096,
|
|
458
|
-
temperature: 0.7,
|
|
459
|
-
autoSave: true,
|
|
460
|
-
autoCompact: true,
|
|
461
|
-
theme: "dark",
|
|
462
|
-
exportFormat: "markdown",
|
|
463
|
-
maxIterations: 10,
|
|
464
|
-
enableSkillTool: true,
|
|
465
|
-
enableDynamicAgentCreation: false
|
|
466
|
-
},
|
|
467
|
-
tools: {
|
|
468
|
-
enabled: [],
|
|
469
|
-
disabled: ["blog_publish", "blog_edit", "blog_draft"],
|
|
470
|
-
// Web-only tools
|
|
471
|
-
config: {}
|
|
472
|
-
},
|
|
473
|
-
trustedTools: [],
|
|
474
|
-
// No tools trusted by default
|
|
475
|
-
additionalDirectories: []
|
|
476
|
-
// No additional directories by default
|
|
477
|
-
};
|
|
478
|
-
function findProjectConfigDir(startDir = process.cwd()) {
|
|
479
|
-
let currentDir = startDir;
|
|
480
|
-
const { root } = path2.parse(currentDir);
|
|
481
|
-
while (currentDir !== root) {
|
|
482
|
-
const gitPath = path2.join(currentDir, ".git");
|
|
483
|
-
try {
|
|
484
|
-
if (existsSync(gitPath)) {
|
|
485
|
-
return currentDir;
|
|
486
|
-
}
|
|
487
|
-
} catch {
|
|
488
|
-
}
|
|
489
|
-
currentDir = path2.dirname(currentDir);
|
|
490
|
-
}
|
|
491
|
-
return process.cwd();
|
|
492
|
-
}
|
|
493
|
-
async function loadProjectConfig(projectDir) {
|
|
494
|
-
const configPath = path2.join(projectDir, ".bike4mind", "config.json");
|
|
495
|
-
try {
|
|
496
|
-
const data = await fs2.readFile(configPath, "utf-8");
|
|
497
|
-
const rawConfig = JSON.parse(data);
|
|
498
|
-
const validated = ProjectConfigSchema.parse(rawConfig);
|
|
499
|
-
const result = {
|
|
500
|
-
...validated,
|
|
501
|
-
mcpServers: validated.mcpServers ? normalizeMcpServers(validated.mcpServers) : void 0
|
|
502
|
-
};
|
|
503
|
-
return result;
|
|
504
|
-
} catch (error) {
|
|
505
|
-
if (error.code === "ENOENT") {
|
|
506
|
-
return null;
|
|
507
|
-
}
|
|
508
|
-
if (error instanceof z.ZodError) {
|
|
509
|
-
console.error("Project config validation error:", error.issues);
|
|
510
|
-
return null;
|
|
511
|
-
}
|
|
512
|
-
console.error("Failed to load project config:", error);
|
|
513
|
-
return null;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
async function loadProjectLocalConfig(projectDir) {
|
|
517
|
-
const configPath = path2.join(projectDir, ".bike4mind", "local.json");
|
|
518
|
-
try {
|
|
519
|
-
const data = await fs2.readFile(configPath, "utf-8");
|
|
520
|
-
const rawConfig = JSON.parse(data);
|
|
521
|
-
const validated = ProjectLocalConfigSchema.parse(rawConfig);
|
|
522
|
-
const result = {
|
|
523
|
-
...validated,
|
|
524
|
-
mcpServers: validated.mcpServers ? normalizeMcpServers(validated.mcpServers) : void 0
|
|
525
|
-
};
|
|
526
|
-
return result;
|
|
527
|
-
} catch (error) {
|
|
528
|
-
if (error.code === "ENOENT") {
|
|
529
|
-
return null;
|
|
530
|
-
}
|
|
531
|
-
if (error instanceof z.ZodError) {
|
|
532
|
-
console.error("Project local config validation error:", error.issues);
|
|
533
|
-
return null;
|
|
534
|
-
}
|
|
535
|
-
console.error("Failed to load project local config:", error);
|
|
536
|
-
return null;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
var McpJsonConfigSchema = z.object({
|
|
540
|
-
mcpServers: McpServersSchema
|
|
541
|
-
});
|
|
542
|
-
async function loadMcpJsonConfig(projectDir) {
|
|
543
|
-
const mcpConfigPath = path2.join(projectDir, ".mcp.json");
|
|
544
|
-
try {
|
|
545
|
-
const data = await fs2.readFile(mcpConfigPath, "utf-8");
|
|
546
|
-
const rawConfig = JSON.parse(data);
|
|
547
|
-
const validated = McpJsonConfigSchema.parse(rawConfig);
|
|
548
|
-
const servers = normalizeMcpServers(validated.mcpServers);
|
|
549
|
-
return servers;
|
|
550
|
-
} catch (error) {
|
|
551
|
-
if (error.code === "ENOENT") {
|
|
552
|
-
return null;
|
|
553
|
-
}
|
|
554
|
-
if (error instanceof z.ZodError) {
|
|
555
|
-
console.error(".mcp.json validation error:", error.issues);
|
|
556
|
-
return null;
|
|
557
|
-
}
|
|
558
|
-
console.error("Failed to load .mcp.json:", error);
|
|
559
|
-
return null;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
function mergeMcpServers(...serverArrays) {
|
|
563
|
-
const serverMap = /* @__PURE__ */ new Map();
|
|
564
|
-
for (const servers of serverArrays) {
|
|
565
|
-
if (servers) {
|
|
566
|
-
for (const server of servers) {
|
|
567
|
-
serverMap.set(server.name, server);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
return Array.from(serverMap.values());
|
|
572
|
-
}
|
|
573
|
-
function mergeSandboxConfig(base, override) {
|
|
574
|
-
const resolved = base ?? DEFAULT_SANDBOX_CONFIG;
|
|
575
|
-
if (!override) return resolved;
|
|
576
|
-
return {
|
|
577
|
-
enabled: override.enabled ?? resolved.enabled,
|
|
578
|
-
mode: override.mode ?? resolved.mode,
|
|
579
|
-
filesystem: {
|
|
580
|
-
...resolved.filesystem,
|
|
581
|
-
...override.filesystem ?? {}
|
|
582
|
-
},
|
|
583
|
-
network: {
|
|
584
|
-
...resolved.network,
|
|
585
|
-
...override.network ?? {}
|
|
586
|
-
},
|
|
587
|
-
excludedCommands: override.excludedCommands ?? resolved.excludedCommands,
|
|
588
|
-
allowUnsandboxedCommands: override.allowUnsandboxedCommands ?? resolved.allowUnsandboxedCommands,
|
|
589
|
-
platform: override.platform ?? resolved.platform
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
function mergeConfigs(global, project, local) {
|
|
593
|
-
const merged = { ...global };
|
|
594
|
-
if (project) {
|
|
595
|
-
if (project.defaultModel) {
|
|
596
|
-
merged.defaultModel = project.defaultModel;
|
|
597
|
-
}
|
|
598
|
-
if (project.preferences) {
|
|
599
|
-
merged.preferences = {
|
|
600
|
-
...merged.preferences,
|
|
601
|
-
...project.preferences
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
|
-
if (project.tools) {
|
|
605
|
-
merged.tools = {
|
|
606
|
-
...merged.tools,
|
|
607
|
-
enabled: [...merged.tools.enabled || [], ...project.tools.enabled || []],
|
|
608
|
-
disabled: [...merged.tools.disabled, ...project.tools.denied || []],
|
|
609
|
-
config: {
|
|
610
|
-
...merged.tools.config,
|
|
611
|
-
...project.tools.config
|
|
612
|
-
}
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
if (project.mcpServers) {
|
|
616
|
-
merged.mcpServers = mergeMcpServers(merged.mcpServers, project.mcpServers);
|
|
617
|
-
}
|
|
618
|
-
if (project.sandbox) {
|
|
619
|
-
merged.sandbox = mergeSandboxConfig(merged.sandbox, project.sandbox);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
if (local) {
|
|
623
|
-
if (local.trustedTools) {
|
|
624
|
-
const trustedSet = /* @__PURE__ */ new Set([...merged.trustedTools || [], ...local.trustedTools]);
|
|
625
|
-
merged.trustedTools = Array.from(trustedSet);
|
|
626
|
-
}
|
|
627
|
-
if (local.toolApiKeys) {
|
|
628
|
-
merged.toolApiKeys = {
|
|
629
|
-
...merged.toolApiKeys,
|
|
630
|
-
...local.toolApiKeys
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
if (local.preferences) {
|
|
634
|
-
merged.preferences = {
|
|
635
|
-
...merged.preferences,
|
|
636
|
-
...local.preferences
|
|
637
|
-
};
|
|
638
|
-
}
|
|
639
|
-
if (local.mcpServers) {
|
|
640
|
-
merged.mcpServers = mergeMcpServers(merged.mcpServers, local.mcpServers);
|
|
641
|
-
}
|
|
642
|
-
if (local.sandbox) {
|
|
643
|
-
merged.sandbox = mergeSandboxConfig(merged.sandbox, local.sandbox);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
return merged;
|
|
647
|
-
}
|
|
648
|
-
var ConfigStore = class {
|
|
649
|
-
constructor(configPath) {
|
|
650
|
-
this.config = null;
|
|
651
|
-
this.projectConfigDir = null;
|
|
652
|
-
this.configPath = configPath || path2.join(homedir(), ".bike4mind", "config.json");
|
|
653
|
-
}
|
|
654
|
-
/**
|
|
655
|
-
* Initialize config directory
|
|
656
|
-
*/
|
|
657
|
-
async init() {
|
|
658
|
-
const dir = path2.dirname(this.configPath);
|
|
659
|
-
try {
|
|
660
|
-
await fs2.mkdir(dir, { recursive: true });
|
|
661
|
-
} catch (error) {
|
|
662
|
-
console.error("Failed to initialize config directory:", error);
|
|
663
|
-
throw error;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
/**
|
|
667
|
-
* Load configuration from disk with Zod validation
|
|
668
|
-
* Merges global → project → local configs
|
|
669
|
-
*/
|
|
670
|
-
async load() {
|
|
671
|
-
if (this.config) {
|
|
672
|
-
return this.config;
|
|
673
|
-
}
|
|
674
|
-
try {
|
|
675
|
-
let globalConfig;
|
|
676
|
-
try {
|
|
677
|
-
try {
|
|
678
|
-
const stats = await fs2.stat(this.configPath);
|
|
679
|
-
const mode = stats.mode & 511;
|
|
680
|
-
if (mode !== 384) {
|
|
681
|
-
console.warn(`\u26A0\uFE0F Config file has insecure permissions (${mode.toString(8)}). Setting to 0600...`);
|
|
682
|
-
await fs2.chmod(this.configPath, 384);
|
|
683
|
-
}
|
|
684
|
-
} catch (statError) {
|
|
685
|
-
}
|
|
686
|
-
const data = await fs2.readFile(this.configPath, "utf-8");
|
|
687
|
-
const rawConfig = JSON.parse(data);
|
|
688
|
-
if (rawConfig.apiConfig && "environment" in rawConfig.apiConfig) {
|
|
689
|
-
const oldApiConfig = rawConfig.apiConfig;
|
|
690
|
-
if (oldApiConfig.environment === "custom" && oldApiConfig.customUrl) {
|
|
691
|
-
rawConfig.apiConfig = { customUrl: oldApiConfig.customUrl };
|
|
692
|
-
} else {
|
|
693
|
-
rawConfig.apiConfig = {};
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
const validated = CliConfigSchema.parse(rawConfig);
|
|
697
|
-
const normalizedMcpServers = normalizeMcpServers(validated.mcpServers);
|
|
698
|
-
globalConfig = {
|
|
699
|
-
...DEFAULT_CONFIG,
|
|
700
|
-
...validated,
|
|
701
|
-
auth: validated.auth,
|
|
702
|
-
// Explicitly preserve auth field
|
|
703
|
-
mcpServers: normalizedMcpServers,
|
|
704
|
-
preferences: {
|
|
705
|
-
...DEFAULT_CONFIG.preferences,
|
|
706
|
-
...validated.preferences
|
|
707
|
-
},
|
|
708
|
-
tools: {
|
|
709
|
-
...DEFAULT_CONFIG.tools,
|
|
710
|
-
...validated.tools
|
|
711
|
-
},
|
|
712
|
-
toolApiKeys: {
|
|
713
|
-
...DEFAULT_CONFIG.toolApiKeys,
|
|
714
|
-
...validated.toolApiKeys || {}
|
|
715
|
-
},
|
|
716
|
-
trustedTools: validated.trustedTools || []
|
|
717
|
-
};
|
|
718
|
-
} catch (error) {
|
|
719
|
-
if (error.code === "ENOENT") {
|
|
720
|
-
globalConfig = { ...DEFAULT_CONFIG };
|
|
721
|
-
} else if (error instanceof z.ZodError) {
|
|
722
|
-
console.error("Global config validation error:", error.issues);
|
|
723
|
-
console.error("Using default configuration");
|
|
724
|
-
globalConfig = { ...DEFAULT_CONFIG };
|
|
725
|
-
} else {
|
|
726
|
-
throw error;
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
let projectConfig = null;
|
|
730
|
-
let projectLocalConfig = null;
|
|
731
|
-
let mcpJsonServers = null;
|
|
732
|
-
if (process.env.B4M_NO_PROJECT_CONFIG !== "1") {
|
|
733
|
-
this.projectConfigDir = findProjectConfigDir();
|
|
734
|
-
if (this.projectConfigDir) {
|
|
735
|
-
projectConfig = await loadProjectConfig(this.projectConfigDir);
|
|
736
|
-
projectLocalConfig = await loadProjectLocalConfig(this.projectConfigDir);
|
|
737
|
-
mcpJsonServers = await loadMcpJsonConfig(this.projectConfigDir);
|
|
738
|
-
if (projectConfig) {
|
|
739
|
-
logger.debug(`\u{1F4C1} Project config loaded from: ${this.projectConfigDir}/.bike4mind/`);
|
|
740
|
-
}
|
|
741
|
-
if (mcpJsonServers && mcpJsonServers.length > 0) {
|
|
742
|
-
logger.debug(`\u{1F4C1} Project MCP config loaded from: ${this.projectConfigDir}/.mcp.json`);
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
} else {
|
|
746
|
-
this.projectConfigDir = null;
|
|
747
|
-
}
|
|
748
|
-
const mergedConfig = mergeConfigs(globalConfig, projectConfig, projectLocalConfig);
|
|
749
|
-
if (mcpJsonServers && mcpJsonServers.length > 0) {
|
|
750
|
-
mergedConfig.mcpServers = mergeMcpServers(mcpJsonServers, mergedConfig.mcpServers);
|
|
751
|
-
}
|
|
752
|
-
this.config = mergedConfig;
|
|
753
|
-
return this.config;
|
|
754
|
-
} catch (error) {
|
|
755
|
-
if (error.code === "ENOENT") {
|
|
756
|
-
return this.reset();
|
|
757
|
-
}
|
|
758
|
-
if (error instanceof z.ZodError) {
|
|
759
|
-
console.error("Config validation error:", error.issues);
|
|
760
|
-
console.error("Resetting to default configuration");
|
|
761
|
-
return this.reset();
|
|
762
|
-
}
|
|
763
|
-
console.error("Failed to load config:", error);
|
|
764
|
-
throw error;
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* Save configuration to disk
|
|
769
|
-
*/
|
|
770
|
-
async save(config) {
|
|
771
|
-
await this.init();
|
|
772
|
-
if (config) {
|
|
773
|
-
const existingConfig = await this.load();
|
|
774
|
-
this.config = {
|
|
775
|
-
...existingConfig,
|
|
776
|
-
...config,
|
|
777
|
-
auth: config.auth !== void 0 ? config.auth : existingConfig.auth,
|
|
778
|
-
preferences: {
|
|
779
|
-
...existingConfig.preferences,
|
|
780
|
-
...config.preferences || {}
|
|
781
|
-
},
|
|
782
|
-
tools: {
|
|
783
|
-
...existingConfig.tools,
|
|
784
|
-
...config.tools || {}
|
|
785
|
-
},
|
|
786
|
-
toolApiKeys: {
|
|
787
|
-
...existingConfig.toolApiKeys,
|
|
788
|
-
...config.toolApiKeys || {}
|
|
789
|
-
}
|
|
790
|
-
};
|
|
791
|
-
}
|
|
792
|
-
if (!this.config) {
|
|
793
|
-
throw new Error("No configuration to save");
|
|
794
|
-
}
|
|
795
|
-
try {
|
|
796
|
-
await fs2.writeFile(this.configPath, JSON.stringify(this.config, null, 2), "utf-8");
|
|
797
|
-
await fs2.chmod(this.configPath, 384);
|
|
798
|
-
} catch (error) {
|
|
799
|
-
console.error("Failed to save config:", error);
|
|
800
|
-
throw error;
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
/**
|
|
804
|
-
* Reset configuration to defaults
|
|
805
|
-
*/
|
|
806
|
-
async reset() {
|
|
807
|
-
this.config = { ...DEFAULT_CONFIG, userId: uuidv4() };
|
|
808
|
-
await this.save();
|
|
809
|
-
return this.config;
|
|
810
|
-
}
|
|
811
|
-
/**
|
|
812
|
-
* Get current configuration
|
|
813
|
-
*/
|
|
814
|
-
async get() {
|
|
815
|
-
return this.load();
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* Update a specific configuration value
|
|
819
|
-
*/
|
|
820
|
-
async update(updates) {
|
|
821
|
-
await this.save(updates);
|
|
822
|
-
}
|
|
823
|
-
/**
|
|
824
|
-
* Add MCP server configuration
|
|
825
|
-
*/
|
|
826
|
-
async addMcpServer(server) {
|
|
827
|
-
const config = await this.load();
|
|
828
|
-
config.mcpServers = config.mcpServers.filter((s) => s.name !== server.name);
|
|
829
|
-
config.mcpServers.push(server);
|
|
830
|
-
await this.save(config);
|
|
831
|
-
}
|
|
832
|
-
/**
|
|
833
|
-
* Remove MCP server configuration
|
|
834
|
-
*/
|
|
835
|
-
async removeMcpServer(name) {
|
|
836
|
-
const config = await this.load();
|
|
837
|
-
config.mcpServers = config.mcpServers.filter((s) => s.name !== name);
|
|
838
|
-
await this.save(config);
|
|
839
|
-
}
|
|
840
|
-
/**
|
|
841
|
-
* Enable/disable MCP server
|
|
842
|
-
*/
|
|
843
|
-
async toggleMcpServer(name, enabled) {
|
|
844
|
-
const config = await this.load();
|
|
845
|
-
const server = config.mcpServers.find((s) => s.name === name);
|
|
846
|
-
if (server) {
|
|
847
|
-
server.enabled = enabled;
|
|
848
|
-
await this.save(config);
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
/**
|
|
852
|
-
* Add a tool to trusted tools list
|
|
853
|
-
*/
|
|
854
|
-
async trustTool(toolName) {
|
|
855
|
-
const config = await this.load();
|
|
856
|
-
if (!config.trustedTools) {
|
|
857
|
-
config.trustedTools = [];
|
|
858
|
-
}
|
|
859
|
-
if (!config.trustedTools.includes(toolName)) {
|
|
860
|
-
config.trustedTools.push(toolName);
|
|
861
|
-
await this.save(config);
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
/**
|
|
865
|
-
* Remove a tool from trusted tools list
|
|
866
|
-
*/
|
|
867
|
-
async untrustTool(toolName) {
|
|
868
|
-
const config = await this.load();
|
|
869
|
-
if (config.trustedTools) {
|
|
870
|
-
config.trustedTools = config.trustedTools.filter((t) => t !== toolName);
|
|
871
|
-
await this.save(config);
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
/**
|
|
875
|
-
* Get list of trusted tools
|
|
876
|
-
*/
|
|
877
|
-
async getTrustedTools() {
|
|
878
|
-
const config = await this.load();
|
|
879
|
-
return config.trustedTools || [];
|
|
880
|
-
}
|
|
881
|
-
/**
|
|
882
|
-
* Clear all trusted tools
|
|
883
|
-
*/
|
|
884
|
-
async clearTrustedTools() {
|
|
885
|
-
const config = await this.load();
|
|
886
|
-
config.trustedTools = [];
|
|
887
|
-
await this.save(config);
|
|
888
|
-
}
|
|
889
|
-
/**
|
|
890
|
-
* Get authentication tokens
|
|
891
|
-
*/
|
|
892
|
-
async getAuthTokens() {
|
|
893
|
-
const config = await this.load();
|
|
894
|
-
return config.auth || null;
|
|
895
|
-
}
|
|
896
|
-
/**
|
|
897
|
-
* Set authentication tokens
|
|
898
|
-
*/
|
|
899
|
-
async setAuthTokens(tokens) {
|
|
900
|
-
const config = await this.load();
|
|
901
|
-
config.auth = tokens;
|
|
902
|
-
await this.save(config);
|
|
903
|
-
}
|
|
904
|
-
/**
|
|
905
|
-
* Clear authentication tokens (logout)
|
|
906
|
-
*/
|
|
907
|
-
async clearAuthTokens() {
|
|
908
|
-
const config = await this.load();
|
|
909
|
-
config.auth = void 0;
|
|
910
|
-
await this.save(config);
|
|
911
|
-
}
|
|
912
|
-
/**
|
|
913
|
-
* Check if user is authenticated
|
|
914
|
-
*/
|
|
915
|
-
async isAuthenticated() {
|
|
916
|
-
const tokens = await this.getAuthTokens();
|
|
917
|
-
if (!tokens) return false;
|
|
918
|
-
const expiresAt = new Date(tokens.expiresAt);
|
|
919
|
-
return expiresAt > /* @__PURE__ */ new Date();
|
|
920
|
-
}
|
|
921
|
-
/**
|
|
922
|
-
* Get API configuration
|
|
923
|
-
*/
|
|
924
|
-
async getApiConfig() {
|
|
925
|
-
const config = await this.load();
|
|
926
|
-
return config.apiConfig;
|
|
927
|
-
}
|
|
928
|
-
/**
|
|
929
|
-
* Set custom API URL for self-hosted Bike4Mind instance
|
|
930
|
-
* Pass null to reset to Bike4Mind main service
|
|
931
|
-
*/
|
|
932
|
-
async setCustomApiUrl(url) {
|
|
933
|
-
const config = await this.load();
|
|
934
|
-
if (url === null) {
|
|
935
|
-
config.apiConfig = void 0;
|
|
936
|
-
} else {
|
|
937
|
-
config.apiConfig = { customUrl: url };
|
|
938
|
-
}
|
|
939
|
-
await this.save(config);
|
|
940
|
-
}
|
|
941
|
-
/**
|
|
942
|
-
* Get project config directory (if any)
|
|
943
|
-
*/
|
|
944
|
-
getProjectConfigDir() {
|
|
945
|
-
return this.projectConfigDir;
|
|
946
|
-
}
|
|
947
|
-
/**
|
|
948
|
-
* Initialize project config directory
|
|
949
|
-
* Creates .bike4mind/ directory and ensures local.json is gitignored
|
|
950
|
-
* Does NOT auto-create config.json (user creates that manually)
|
|
951
|
-
*/
|
|
952
|
-
async initProjectConfig() {
|
|
953
|
-
const projectDir = this.projectConfigDir || findProjectConfigDir();
|
|
954
|
-
if (!projectDir) {
|
|
955
|
-
return;
|
|
956
|
-
}
|
|
957
|
-
const configDir = path2.join(projectDir, ".bike4mind");
|
|
958
|
-
await fs2.mkdir(configDir, { recursive: true });
|
|
959
|
-
await this.ensureGitignore(projectDir);
|
|
960
|
-
}
|
|
961
|
-
/**
|
|
962
|
-
* Ensure .gitignore includes .bike4mind/local.json
|
|
963
|
-
*/
|
|
964
|
-
async ensureGitignore(projectDir) {
|
|
965
|
-
const gitignorePath = path2.join(projectDir, ".gitignore");
|
|
966
|
-
const entryToAdd = ".bike4mind/local.json";
|
|
967
|
-
try {
|
|
968
|
-
let gitignoreContent = "";
|
|
969
|
-
try {
|
|
970
|
-
gitignoreContent = await fs2.readFile(gitignorePath, "utf-8");
|
|
971
|
-
} catch {
|
|
972
|
-
}
|
|
973
|
-
if (gitignoreContent.includes(entryToAdd)) {
|
|
974
|
-
return;
|
|
975
|
-
}
|
|
976
|
-
const newContent = gitignoreContent.trim() + (gitignoreContent ? "\n" : "") + `
|
|
977
|
-
# Bike4Mind local config (developer-specific)
|
|
978
|
-
${entryToAdd}
|
|
979
|
-
`;
|
|
980
|
-
await fs2.writeFile(gitignorePath, newContent, "utf-8");
|
|
981
|
-
console.log(`\u2705 Added ${entryToAdd} to .gitignore`);
|
|
982
|
-
} catch (error) {
|
|
983
|
-
console.warn(`\u26A0\uFE0F Failed to update .gitignore:`, error);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
/**
|
|
987
|
-
* Save project config to .bike4mind/config.json
|
|
988
|
-
*/
|
|
989
|
-
async saveProjectConfig(config, projectDir) {
|
|
990
|
-
const targetDir = projectDir || this.projectConfigDir || process.cwd();
|
|
991
|
-
const configPath = path2.join(targetDir, ".bike4mind", "config.json");
|
|
992
|
-
await fs2.mkdir(path2.dirname(configPath), { recursive: true });
|
|
993
|
-
const validated = ProjectConfigSchema.parse(config);
|
|
994
|
-
await fs2.writeFile(configPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
995
|
-
console.log(`\u2705 Saved project config to: ${configPath}`);
|
|
996
|
-
}
|
|
997
|
-
/**
|
|
998
|
-
* Save project-local config to .bike4mind/local.json
|
|
999
|
-
*/
|
|
1000
|
-
async saveProjectLocalConfig(config, projectDir) {
|
|
1001
|
-
const targetDir = projectDir || this.projectConfigDir || process.cwd();
|
|
1002
|
-
const configPath = path2.join(targetDir, ".bike4mind", "local.json");
|
|
1003
|
-
await fs2.mkdir(path2.dirname(configPath), { recursive: true });
|
|
1004
|
-
const validated = ProjectLocalConfigSchema.parse(config);
|
|
1005
|
-
await fs2.writeFile(configPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
1006
|
-
await fs2.chmod(configPath, 384);
|
|
1007
|
-
console.log(`\u2705 Saved project-local config to: ${configPath}`);
|
|
1008
|
-
}
|
|
1009
|
-
/**
|
|
1010
|
-
* Load raw project config (without merging)
|
|
1011
|
-
*/
|
|
1012
|
-
async loadRawProjectConfig() {
|
|
1013
|
-
if (!this.projectConfigDir) {
|
|
1014
|
-
return null;
|
|
1015
|
-
}
|
|
1016
|
-
return loadProjectConfig(this.projectConfigDir);
|
|
1017
|
-
}
|
|
1018
|
-
/**
|
|
1019
|
-
* Load raw project-local config (without merging)
|
|
1020
|
-
*/
|
|
1021
|
-
async loadRawProjectLocalConfig() {
|
|
1022
|
-
if (!this.projectConfigDir) {
|
|
1023
|
-
return null;
|
|
1024
|
-
}
|
|
1025
|
-
return loadProjectLocalConfig(this.projectConfigDir);
|
|
1026
|
-
}
|
|
1027
|
-
/**
|
|
1028
|
-
* Add a directory to the allowed directories list
|
|
1029
|
-
* Persists to global config
|
|
1030
|
-
*/
|
|
1031
|
-
async addDirectory(dirPath) {
|
|
1032
|
-
const config = await this.load();
|
|
1033
|
-
if (!config.additionalDirectories) {
|
|
1034
|
-
config.additionalDirectories = [];
|
|
1035
|
-
}
|
|
1036
|
-
const resolvedPath = path2.resolve(dirPath);
|
|
1037
|
-
if (!config.additionalDirectories.includes(resolvedPath)) {
|
|
1038
|
-
config.additionalDirectories.push(resolvedPath);
|
|
1039
|
-
await this.save(config);
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
/**
|
|
1043
|
-
* Remove a directory from the allowed directories list
|
|
1044
|
-
*/
|
|
1045
|
-
async removeDirectory(dirPath) {
|
|
1046
|
-
const config = await this.load();
|
|
1047
|
-
if (config.additionalDirectories) {
|
|
1048
|
-
const resolvedPath = path2.resolve(dirPath);
|
|
1049
|
-
config.additionalDirectories = config.additionalDirectories.filter((d) => path2.resolve(d) !== resolvedPath);
|
|
1050
|
-
await this.save(config);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
/**
|
|
1054
|
-
* Get all additional directories (merged from global + project configs)
|
|
1055
|
-
* Returns resolved absolute paths
|
|
1056
|
-
*/
|
|
1057
|
-
async getAdditionalDirectories() {
|
|
1058
|
-
const config = await this.load();
|
|
1059
|
-
const dirs = /* @__PURE__ */ new Set();
|
|
1060
|
-
if (config.additionalDirectories) {
|
|
1061
|
-
for (const dir of config.additionalDirectories) {
|
|
1062
|
-
dirs.add(path2.resolve(dir));
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
const projectConfig = await this.loadRawProjectConfig();
|
|
1066
|
-
if (projectConfig?.additionalDirectories) {
|
|
1067
|
-
const projectRoot = this.projectConfigDir || process.cwd();
|
|
1068
|
-
for (const dir of projectConfig.additionalDirectories) {
|
|
1069
|
-
dirs.add(path2.resolve(projectRoot, dir));
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
return Array.from(dirs);
|
|
1073
|
-
}
|
|
1074
|
-
};
|
|
1075
|
-
|
|
1076
|
-
export {
|
|
1077
|
-
logger,
|
|
1078
|
-
ConfigStore
|
|
1079
|
-
};
|