@bike4mind/cli 0.2.29-cli-minimal-ui.18782 → 0.2.29-cli-minimal-ui.18783
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/dist/{chunk-23T2XGSZ.js → chunk-LBTTUQJM.js} +290 -33
- package/dist/commands/mcpCommand.js +1 -1
- package/dist/index.js +172 -412
- package/package.json +6 -6
|
@@ -1,8 +1,264 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/
|
|
4
|
-
import
|
|
3
|
+
// src/utils/Logger.ts
|
|
4
|
+
import fs from "fs/promises";
|
|
5
5
|
import path from "path";
|
|
6
|
+
import os from "os";
|
|
7
|
+
var Logger = class _Logger {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.logFilePath = null;
|
|
10
|
+
this.sessionId = null;
|
|
11
|
+
this.fileLoggingEnabled = true;
|
|
12
|
+
this.consoleVerbose = false;
|
|
13
|
+
}
|
|
14
|
+
static {
|
|
15
|
+
this.instance = null;
|
|
16
|
+
}
|
|
17
|
+
static getInstance() {
|
|
18
|
+
if (!_Logger.instance) {
|
|
19
|
+
_Logger.instance = new _Logger();
|
|
20
|
+
}
|
|
21
|
+
return _Logger.instance;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Initialize the logger with a session ID
|
|
25
|
+
*/
|
|
26
|
+
async initialize(sessionId) {
|
|
27
|
+
this.sessionId = sessionId;
|
|
28
|
+
const debugDir = path.join(os.homedir(), ".bike4mind", "debug");
|
|
29
|
+
await fs.mkdir(debugDir, { recursive: true });
|
|
30
|
+
this.logFilePath = path.join(debugDir, `${sessionId}.txt`);
|
|
31
|
+
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Set whether verbose console logging is enabled
|
|
35
|
+
*/
|
|
36
|
+
setVerbose(enabled) {
|
|
37
|
+
this.consoleVerbose = enabled;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Set whether file logging is enabled
|
|
41
|
+
*/
|
|
42
|
+
setFileLoggingEnabled(enabled) {
|
|
43
|
+
this.fileLoggingEnabled = enabled;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* DEBUG level - verbose-only console, always file
|
|
47
|
+
*/
|
|
48
|
+
debug(message) {
|
|
49
|
+
this.writeToFile("DEBUG", message).catch(() => {
|
|
50
|
+
});
|
|
51
|
+
if (this.consoleVerbose) {
|
|
52
|
+
console.log(message);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* INFO level - always shown to user
|
|
57
|
+
*/
|
|
58
|
+
info(message) {
|
|
59
|
+
this.writeToFile("INFO", message).catch(() => {
|
|
60
|
+
});
|
|
61
|
+
console.log(message);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* WARN level - always shown to user
|
|
65
|
+
*/
|
|
66
|
+
warn(message) {
|
|
67
|
+
this.writeToFile("WARN", message).catch(() => {
|
|
68
|
+
});
|
|
69
|
+
console.warn(message);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* ERROR level - always shown to user
|
|
73
|
+
*/
|
|
74
|
+
error(message, err) {
|
|
75
|
+
this.writeToFile("ERROR", message).catch(() => {
|
|
76
|
+
});
|
|
77
|
+
if (err) {
|
|
78
|
+
this.logErrorDetailsToFile(err).catch(() => {
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
console.error(message);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Write log entry to file
|
|
85
|
+
*/
|
|
86
|
+
async writeToFile(level, message) {
|
|
87
|
+
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
92
|
+
const logEntry = `[${timestamp}] [${level}] ${message}
|
|
93
|
+
`;
|
|
94
|
+
await fs.appendFile(this.logFilePath, logEntry, "utf-8");
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error("File logging failed:", error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Log error details to file
|
|
101
|
+
*/
|
|
102
|
+
async logErrorDetailsToFile(err) {
|
|
103
|
+
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
if (err && typeof err === "object" && "response" in err && err.response) {
|
|
108
|
+
const response = err.response;
|
|
109
|
+
const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
|
|
110
|
+
await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
|
|
111
|
+
await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
|
|
112
|
+
await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
|
|
113
|
+
if (response.data) {
|
|
114
|
+
const errorText = this.extractErrorMessage(response.data);
|
|
115
|
+
if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
|
|
116
|
+
await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
|
|
117
|
+
const parsedError = this.parseHtmlError(errorText);
|
|
118
|
+
if (parsedError) {
|
|
119
|
+
await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
|
|
120
|
+
}
|
|
121
|
+
await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
|
|
122
|
+
} else {
|
|
123
|
+
const preview = this.truncate(errorText, 500);
|
|
124
|
+
await this.writeToFile("ERROR", ` Response: ${preview}`);
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
await this.writeToFile("ERROR", ` Response: (no data)`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
|
|
131
|
+
const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
|
|
132
|
+
await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
|
|
133
|
+
} else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
134
|
+
await this.writeToFile("ERROR", ` Message: ${err.message}`);
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Safely stringify object, handling circular references
|
|
141
|
+
*/
|
|
142
|
+
safeStringify(obj) {
|
|
143
|
+
try {
|
|
144
|
+
return JSON.stringify(obj);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (error instanceof Error && error.message.includes("circular")) {
|
|
147
|
+
try {
|
|
148
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
149
|
+
return JSON.stringify(obj, (key, value) => {
|
|
150
|
+
if (typeof value === "object" && value !== null) {
|
|
151
|
+
if (seen.has(value)) {
|
|
152
|
+
return "[Circular]";
|
|
153
|
+
}
|
|
154
|
+
seen.add(value);
|
|
155
|
+
}
|
|
156
|
+
return value;
|
|
157
|
+
});
|
|
158
|
+
} catch {
|
|
159
|
+
return "[Unable to stringify]";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return "[Stringify error]";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Extract readable text from error response data
|
|
167
|
+
*/
|
|
168
|
+
extractErrorMessage(data) {
|
|
169
|
+
if (Buffer.isBuffer(data)) {
|
|
170
|
+
return data.toString("utf-8");
|
|
171
|
+
}
|
|
172
|
+
if (typeof data === "string") {
|
|
173
|
+
return data;
|
|
174
|
+
}
|
|
175
|
+
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) {
|
|
176
|
+
const chunks = [];
|
|
177
|
+
for (const chunk of data._readableState.buffer) {
|
|
178
|
+
if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
|
|
179
|
+
chunks.push(Buffer.from(chunk.data));
|
|
180
|
+
} else if (Buffer.isBuffer(chunk)) {
|
|
181
|
+
chunks.push(chunk);
|
|
182
|
+
} else if (chunk && typeof chunk === "object" && "data" in chunk) {
|
|
183
|
+
if (Buffer.isBuffer(chunk.data)) {
|
|
184
|
+
chunks.push(chunk.data);
|
|
185
|
+
} else if (Array.isArray(chunk.data)) {
|
|
186
|
+
chunks.push(Buffer.from(chunk.data));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (chunks.length > 0) {
|
|
191
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return this.safeStringify(data);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Parse HTML error page to extract error message
|
|
198
|
+
*/
|
|
199
|
+
parseHtmlError(html) {
|
|
200
|
+
const titleMatch = html.match(/<title>(.*?)<\/title>/i);
|
|
201
|
+
const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
|
|
202
|
+
const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
|
|
203
|
+
if (titleMatch && titleMatch[1] !== "Error") {
|
|
204
|
+
return titleMatch[1].trim();
|
|
205
|
+
}
|
|
206
|
+
if (h1Match) {
|
|
207
|
+
return h1Match[1].trim();
|
|
208
|
+
}
|
|
209
|
+
if (bodyMatch) {
|
|
210
|
+
const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
211
|
+
return text.substring(0, 200);
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Truncate string to max length with ellipsis
|
|
217
|
+
*/
|
|
218
|
+
truncate(str, maxLength) {
|
|
219
|
+
if (str.length <= maxLength) {
|
|
220
|
+
return str;
|
|
221
|
+
}
|
|
222
|
+
return str.substring(0, maxLength) + "... [truncated]";
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Format bytes to human-readable size
|
|
226
|
+
*/
|
|
227
|
+
formatBytes(bytes) {
|
|
228
|
+
if (bytes === 0) return "0 B";
|
|
229
|
+
const k = 1024;
|
|
230
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
231
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
232
|
+
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Clean up old debug logs (older than 30 days)
|
|
236
|
+
*/
|
|
237
|
+
async cleanupOldLogs() {
|
|
238
|
+
if (!this.fileLoggingEnabled) return;
|
|
239
|
+
try {
|
|
240
|
+
const debugDir = path.join(os.homedir(), ".bike4mind", "debug");
|
|
241
|
+
const files = await fs.readdir(debugDir);
|
|
242
|
+
const now = Date.now();
|
|
243
|
+
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
244
|
+
for (const file of files) {
|
|
245
|
+
const filePath = path.join(debugDir, file);
|
|
246
|
+
const stats = await fs.stat(filePath);
|
|
247
|
+
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
248
|
+
await fs.unlink(filePath);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.error("Failed to cleanup old logs:", error);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
var logger = Logger.getInstance();
|
|
257
|
+
logger.setVerbose(process.env.B4M_VERBOSE === "1");
|
|
258
|
+
|
|
259
|
+
// src/storage/ConfigStore.ts
|
|
260
|
+
import { promises as fs2, existsSync } from "fs";
|
|
261
|
+
import path2 from "path";
|
|
6
262
|
import { homedir } from "os";
|
|
7
263
|
import { v4 as uuidv4 } from "uuid";
|
|
8
264
|
import { z } from "zod";
|
|
@@ -147,23 +403,23 @@ var DEFAULT_CONFIG = {
|
|
|
147
403
|
};
|
|
148
404
|
function findProjectConfigDir(startDir = process.cwd()) {
|
|
149
405
|
let currentDir = startDir;
|
|
150
|
-
const { root } =
|
|
406
|
+
const { root } = path2.parse(currentDir);
|
|
151
407
|
while (currentDir !== root) {
|
|
152
|
-
const gitPath =
|
|
408
|
+
const gitPath = path2.join(currentDir, ".git");
|
|
153
409
|
try {
|
|
154
410
|
if (existsSync(gitPath)) {
|
|
155
411
|
return currentDir;
|
|
156
412
|
}
|
|
157
413
|
} catch {
|
|
158
414
|
}
|
|
159
|
-
currentDir =
|
|
415
|
+
currentDir = path2.dirname(currentDir);
|
|
160
416
|
}
|
|
161
417
|
return process.cwd();
|
|
162
418
|
}
|
|
163
419
|
async function loadProjectConfig(projectDir) {
|
|
164
|
-
const configPath =
|
|
420
|
+
const configPath = path2.join(projectDir, ".bike4mind", "config.json");
|
|
165
421
|
try {
|
|
166
|
-
const data = await
|
|
422
|
+
const data = await fs2.readFile(configPath, "utf-8");
|
|
167
423
|
const rawConfig = JSON.parse(data);
|
|
168
424
|
const validated = ProjectConfigSchema.parse(rawConfig);
|
|
169
425
|
const result = {
|
|
@@ -184,9 +440,9 @@ async function loadProjectConfig(projectDir) {
|
|
|
184
440
|
}
|
|
185
441
|
}
|
|
186
442
|
async function loadProjectLocalConfig(projectDir) {
|
|
187
|
-
const configPath =
|
|
443
|
+
const configPath = path2.join(projectDir, ".bike4mind", "local.json");
|
|
188
444
|
try {
|
|
189
|
-
const data = await
|
|
445
|
+
const data = await fs2.readFile(configPath, "utf-8");
|
|
190
446
|
const rawConfig = JSON.parse(data);
|
|
191
447
|
const validated = ProjectLocalConfigSchema.parse(rawConfig);
|
|
192
448
|
const result = {
|
|
@@ -210,9 +466,9 @@ var McpJsonConfigSchema = z.object({
|
|
|
210
466
|
mcpServers: McpServersSchema
|
|
211
467
|
});
|
|
212
468
|
async function loadMcpJsonConfig(projectDir) {
|
|
213
|
-
const mcpConfigPath =
|
|
469
|
+
const mcpConfigPath = path2.join(projectDir, ".mcp.json");
|
|
214
470
|
try {
|
|
215
|
-
const data = await
|
|
471
|
+
const data = await fs2.readFile(mcpConfigPath, "utf-8");
|
|
216
472
|
const rawConfig = JSON.parse(data);
|
|
217
473
|
const validated = McpJsonConfigSchema.parse(rawConfig);
|
|
218
474
|
const servers = normalizeMcpServers(validated.mcpServers);
|
|
@@ -294,15 +550,15 @@ var ConfigStore = class {
|
|
|
294
550
|
constructor(configPath) {
|
|
295
551
|
this.config = null;
|
|
296
552
|
this.projectConfigDir = null;
|
|
297
|
-
this.configPath = configPath ||
|
|
553
|
+
this.configPath = configPath || path2.join(homedir(), ".bike4mind", "config.json");
|
|
298
554
|
}
|
|
299
555
|
/**
|
|
300
556
|
* Initialize config directory
|
|
301
557
|
*/
|
|
302
558
|
async init() {
|
|
303
|
-
const dir =
|
|
559
|
+
const dir = path2.dirname(this.configPath);
|
|
304
560
|
try {
|
|
305
|
-
await
|
|
561
|
+
await fs2.mkdir(dir, { recursive: true });
|
|
306
562
|
} catch (error) {
|
|
307
563
|
console.error("Failed to initialize config directory:", error);
|
|
308
564
|
throw error;
|
|
@@ -320,15 +576,15 @@ var ConfigStore = class {
|
|
|
320
576
|
let globalConfig;
|
|
321
577
|
try {
|
|
322
578
|
try {
|
|
323
|
-
const stats = await
|
|
579
|
+
const stats = await fs2.stat(this.configPath);
|
|
324
580
|
const mode = stats.mode & 511;
|
|
325
581
|
if (mode !== 384) {
|
|
326
582
|
console.warn(`\u26A0\uFE0F Config file has insecure permissions (${mode.toString(8)}). Setting to 0600...`);
|
|
327
|
-
await
|
|
583
|
+
await fs2.chmod(this.configPath, 384);
|
|
328
584
|
}
|
|
329
585
|
} catch (statError) {
|
|
330
586
|
}
|
|
331
|
-
const data = await
|
|
587
|
+
const data = await fs2.readFile(this.configPath, "utf-8");
|
|
332
588
|
const rawConfig = JSON.parse(data);
|
|
333
589
|
if (rawConfig.apiConfig && "environment" in rawConfig.apiConfig) {
|
|
334
590
|
const oldApiConfig = rawConfig.apiConfig;
|
|
@@ -381,10 +637,10 @@ var ConfigStore = class {
|
|
|
381
637
|
projectLocalConfig = await loadProjectLocalConfig(this.projectConfigDir);
|
|
382
638
|
mcpJsonServers = await loadMcpJsonConfig(this.projectConfigDir);
|
|
383
639
|
if (projectConfig) {
|
|
384
|
-
|
|
640
|
+
logger.debug(`\u{1F4C1} Project config loaded from: ${this.projectConfigDir}/.bike4mind/`);
|
|
385
641
|
}
|
|
386
642
|
if (mcpJsonServers && mcpJsonServers.length > 0) {
|
|
387
|
-
|
|
643
|
+
logger.debug(`\u{1F4C1} Project MCP config loaded from: ${this.projectConfigDir}/.mcp.json`);
|
|
388
644
|
}
|
|
389
645
|
}
|
|
390
646
|
} else {
|
|
@@ -438,8 +694,8 @@ var ConfigStore = class {
|
|
|
438
694
|
throw new Error("No configuration to save");
|
|
439
695
|
}
|
|
440
696
|
try {
|
|
441
|
-
await
|
|
442
|
-
await
|
|
697
|
+
await fs2.writeFile(this.configPath, JSON.stringify(this.config, null, 2), "utf-8");
|
|
698
|
+
await fs2.chmod(this.configPath, 384);
|
|
443
699
|
} catch (error) {
|
|
444
700
|
console.error("Failed to save config:", error);
|
|
445
701
|
throw error;
|
|
@@ -599,20 +855,20 @@ var ConfigStore = class {
|
|
|
599
855
|
if (!projectDir) {
|
|
600
856
|
return;
|
|
601
857
|
}
|
|
602
|
-
const configDir =
|
|
603
|
-
await
|
|
858
|
+
const configDir = path2.join(projectDir, ".bike4mind");
|
|
859
|
+
await fs2.mkdir(configDir, { recursive: true });
|
|
604
860
|
await this.ensureGitignore(projectDir);
|
|
605
861
|
}
|
|
606
862
|
/**
|
|
607
863
|
* Ensure .gitignore includes .bike4mind/local.json
|
|
608
864
|
*/
|
|
609
865
|
async ensureGitignore(projectDir) {
|
|
610
|
-
const gitignorePath =
|
|
866
|
+
const gitignorePath = path2.join(projectDir, ".gitignore");
|
|
611
867
|
const entryToAdd = ".bike4mind/local.json";
|
|
612
868
|
try {
|
|
613
869
|
let gitignoreContent = "";
|
|
614
870
|
try {
|
|
615
|
-
gitignoreContent = await
|
|
871
|
+
gitignoreContent = await fs2.readFile(gitignorePath, "utf-8");
|
|
616
872
|
} catch {
|
|
617
873
|
}
|
|
618
874
|
if (gitignoreContent.includes(entryToAdd)) {
|
|
@@ -622,7 +878,7 @@ var ConfigStore = class {
|
|
|
622
878
|
# Bike4Mind local config (developer-specific)
|
|
623
879
|
${entryToAdd}
|
|
624
880
|
`;
|
|
625
|
-
await
|
|
881
|
+
await fs2.writeFile(gitignorePath, newContent, "utf-8");
|
|
626
882
|
console.log(`\u2705 Added ${entryToAdd} to .gitignore`);
|
|
627
883
|
} catch (error) {
|
|
628
884
|
console.warn(`\u26A0\uFE0F Failed to update .gitignore:`, error);
|
|
@@ -633,10 +889,10 @@ ${entryToAdd}
|
|
|
633
889
|
*/
|
|
634
890
|
async saveProjectConfig(config, projectDir) {
|
|
635
891
|
const targetDir = projectDir || this.projectConfigDir || process.cwd();
|
|
636
|
-
const configPath =
|
|
637
|
-
await
|
|
892
|
+
const configPath = path2.join(targetDir, ".bike4mind", "config.json");
|
|
893
|
+
await fs2.mkdir(path2.dirname(configPath), { recursive: true });
|
|
638
894
|
const validated = ProjectConfigSchema.parse(config);
|
|
639
|
-
await
|
|
895
|
+
await fs2.writeFile(configPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
640
896
|
console.log(`\u2705 Saved project config to: ${configPath}`);
|
|
641
897
|
}
|
|
642
898
|
/**
|
|
@@ -644,11 +900,11 @@ ${entryToAdd}
|
|
|
644
900
|
*/
|
|
645
901
|
async saveProjectLocalConfig(config, projectDir) {
|
|
646
902
|
const targetDir = projectDir || this.projectConfigDir || process.cwd();
|
|
647
|
-
const configPath =
|
|
648
|
-
await
|
|
903
|
+
const configPath = path2.join(targetDir, ".bike4mind", "local.json");
|
|
904
|
+
await fs2.mkdir(path2.dirname(configPath), { recursive: true });
|
|
649
905
|
const validated = ProjectLocalConfigSchema.parse(config);
|
|
650
|
-
await
|
|
651
|
-
await
|
|
906
|
+
await fs2.writeFile(configPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
907
|
+
await fs2.chmod(configPath, 384);
|
|
652
908
|
console.log(`\u2705 Saved project-local config to: ${configPath}`);
|
|
653
909
|
}
|
|
654
910
|
/**
|
|
@@ -672,5 +928,6 @@ ${entryToAdd}
|
|
|
672
928
|
};
|
|
673
929
|
|
|
674
930
|
export {
|
|
931
|
+
logger,
|
|
675
932
|
ConfigStore
|
|
676
933
|
};
|