@mushi-mushi/cli 0.22.0 → 0.23.0
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/README.md +2 -2
- package/dist/{chunk-JAUKBLPK.js → chunk-AIZRI665.js} +102 -2
- package/dist/chunk-BY5SZYRO.js +6 -0
- package/dist/{chunk-R2HPR2FV.js → chunk-GSQO35YD.js} +520 -27
- package/dist/chunk-WLLFV26V.js +423 -0
- package/dist/{connect-2RM43XHO.js → connect-KX4UGY6Z.js} +10 -7
- package/dist/{connect-JIEIWTF3.js → connect-XFVSH4JZ.js} +1 -1
- package/dist/detect.d.ts +1 -1
- package/dist/detect.js +1 -1
- package/dist/index.js +105 -426
- package/dist/init.js +40 -241
- package/dist/version.js +1 -1
- package/package.json +12 -14
- package/dist/chunk-5KN6S63C.js +0 -6
- package/dist/chunk-XEXYGDQV.js +0 -137
package/dist/index.js
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
FRAMEWORKS,
|
|
4
|
+
MUSHI_CLI_VERSION,
|
|
5
|
+
apiCall,
|
|
4
6
|
apiKeyHeaders,
|
|
7
|
+
apiKeyHint,
|
|
5
8
|
buildMcpServerBlock,
|
|
6
9
|
buildMcpServerName,
|
|
10
|
+
cliSetupDeepLink,
|
|
11
|
+
consoleUrl,
|
|
7
12
|
detectFramework,
|
|
8
13
|
detectPackageManager,
|
|
14
|
+
die,
|
|
9
15
|
envVarsToWrite,
|
|
10
16
|
fetchIngestSetup,
|
|
17
|
+
fmtDate,
|
|
11
18
|
installCommand,
|
|
19
|
+
installSignalHandlers,
|
|
12
20
|
loadConfig,
|
|
21
|
+
openInBrowser,
|
|
22
|
+
pad,
|
|
23
|
+
printAndExit,
|
|
24
|
+
probeEndpointHealth,
|
|
25
|
+
projectIdHint,
|
|
13
26
|
readPackageJson,
|
|
27
|
+
reportsUrl,
|
|
28
|
+
requireConfig,
|
|
29
|
+
requireUuid,
|
|
30
|
+
resolveConsoleUrl,
|
|
31
|
+
resolveConsoleUrlSync,
|
|
14
32
|
runConnect,
|
|
15
33
|
sanitizeApiKey,
|
|
16
34
|
sanitizeCliCredentials,
|
|
@@ -18,9 +36,8 @@ import {
|
|
|
18
36
|
sanitizeProjectId,
|
|
19
37
|
saveConfig,
|
|
20
38
|
writeMcpServerEntry
|
|
21
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-GSQO35YD.js";
|
|
22
40
|
import {
|
|
23
|
-
CLOUD_API_ENDPOINT,
|
|
24
41
|
TEST_REPORT_FETCH_TIMEOUT_MS,
|
|
25
42
|
assertEndpoint,
|
|
26
43
|
normalizeEndpoint,
|
|
@@ -30,369 +47,12 @@ import {
|
|
|
30
47
|
// src/index.ts
|
|
31
48
|
import { Command } from "commander";
|
|
32
49
|
|
|
33
|
-
// src/errors.ts
|
|
34
|
-
var EXIT_CODE_MAP = {
|
|
35
|
-
E_AUTH_MISSING: 2,
|
|
36
|
-
E_AUTH_INVALID: 2,
|
|
37
|
-
E_PROJECT_MISSING: 2,
|
|
38
|
-
E_ENDPOINT_INVALID: 2,
|
|
39
|
-
E_INVALID_INPUT: 2,
|
|
40
|
-
E_NOT_INTERACTIVE: 2,
|
|
41
|
-
E_NETWORK: 3,
|
|
42
|
-
E_TIMEOUT: 3,
|
|
43
|
-
E_API_ERROR: 3,
|
|
44
|
-
E_RATE_LIMITED: 3,
|
|
45
|
-
E_FILE_NOT_FOUND: 1,
|
|
46
|
-
E_FILE_PERMISSION: 1,
|
|
47
|
-
E_FRESHNESS_STALE: 1,
|
|
48
|
-
E_INTERRUPTED: 130,
|
|
49
|
-
// 128 + SIGINT (2). POSIX convention.
|
|
50
|
-
E_INTERNAL: 1
|
|
51
|
-
};
|
|
52
|
-
var MushiCliError = class extends Error {
|
|
53
|
-
code;
|
|
54
|
-
hint;
|
|
55
|
-
cause;
|
|
56
|
-
constructor(code, message, hint, cause) {
|
|
57
|
-
super(message);
|
|
58
|
-
this.name = "MushiCliError";
|
|
59
|
-
this.code = code;
|
|
60
|
-
if (hint) this.hint = hint;
|
|
61
|
-
if (cause !== void 0) this.cause = cause;
|
|
62
|
-
}
|
|
63
|
-
/** Exit code POSIX-aware shell scripts should branch on. */
|
|
64
|
-
get exitCode() {
|
|
65
|
-
return EXIT_CODE_MAP[this.code];
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Format for `--json` output. Used by the JSON writer when a CLI
|
|
69
|
-
* subcommand was invoked with `--json` and an error escapes.
|
|
70
|
-
*/
|
|
71
|
-
toJSON() {
|
|
72
|
-
return {
|
|
73
|
-
error: {
|
|
74
|
-
code: this.code,
|
|
75
|
-
message: this.message,
|
|
76
|
-
...this.hint ? { hint: this.hint } : {}
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
function printAndExit(err, asJson = false) {
|
|
82
|
-
const cliErr = err instanceof MushiCliError ? err : new MushiCliError(
|
|
83
|
-
"E_INTERNAL",
|
|
84
|
-
err instanceof Error ? err.message : String(err),
|
|
85
|
-
"this is unexpected \u2014 re-run with MUSHI_DEBUG=1 and report at https://github.com/kensaurus/mushi-mushi/issues",
|
|
86
|
-
err
|
|
87
|
-
);
|
|
88
|
-
if (asJson) {
|
|
89
|
-
process.stderr.write(JSON.stringify(cliErr.toJSON()) + "\n");
|
|
90
|
-
} else {
|
|
91
|
-
process.stderr.write(`error [${cliErr.code}]: ${cliErr.message}
|
|
92
|
-
`);
|
|
93
|
-
if (cliErr.hint) process.stderr.write(` \u2192 fix: ${cliErr.hint}
|
|
94
|
-
`);
|
|
95
|
-
}
|
|
96
|
-
process.exit(cliErr.exitCode);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// src/signals.ts
|
|
100
|
-
var installed = false;
|
|
101
|
-
var activeController = null;
|
|
102
|
-
function installSignalHandlers() {
|
|
103
|
-
if (installed) return;
|
|
104
|
-
installed = true;
|
|
105
|
-
const abortAndExit = (signal) => {
|
|
106
|
-
activeController?.abort(
|
|
107
|
-
new MushiCliError(
|
|
108
|
-
"E_INTERRUPTED",
|
|
109
|
-
`aborted by ${signal}`,
|
|
110
|
-
"partial state on the server is safe to retry \u2014 re-run the same command"
|
|
111
|
-
)
|
|
112
|
-
);
|
|
113
|
-
const code = signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : 1;
|
|
114
|
-
process.nextTick(() => {
|
|
115
|
-
process.exit(code);
|
|
116
|
-
});
|
|
117
|
-
};
|
|
118
|
-
process.on("SIGINT", () => abortAndExit("SIGINT"));
|
|
119
|
-
process.on("SIGTERM", () => abortAndExit("SIGTERM"));
|
|
120
|
-
}
|
|
121
|
-
function getAbortSignal(external) {
|
|
122
|
-
if (external) return external.signal;
|
|
123
|
-
if (!activeController || activeController.signal.aborted) {
|
|
124
|
-
activeController = new AbortController();
|
|
125
|
-
}
|
|
126
|
-
return activeController.signal;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// src/version.ts
|
|
130
|
-
var MUSHI_CLI_VERSION = true ? "0.22.0" : "0.0.0-dev";
|
|
131
|
-
|
|
132
50
|
// src/init.ts
|
|
133
51
|
import * as p from "@clack/prompts";
|
|
134
52
|
import { spawn } from "child_process";
|
|
135
53
|
import { randomUUID } from "crypto";
|
|
136
|
-
import { appendFileSync, existsSync as
|
|
137
|
-
import { join as
|
|
138
|
-
|
|
139
|
-
// src/console-url.ts
|
|
140
|
-
import { existsSync, readFileSync } from "fs";
|
|
141
|
-
import { dirname, join } from "path";
|
|
142
|
-
var HOSTED_CONSOLE_BASE = "https://kensaur.us/mushi-mushi/admin";
|
|
143
|
-
var LOCAL_ADMIN_URL = "http://localhost:6464";
|
|
144
|
-
var LOCAL_PROBE_MS = 800;
|
|
145
|
-
function normalizeConsoleBase(url) {
|
|
146
|
-
const trimmed = url.trim();
|
|
147
|
-
let end = trimmed.length;
|
|
148
|
-
while (end > 0 && trimmed.charCodeAt(end - 1) === 47) end--;
|
|
149
|
-
return trimmed.slice(0, end);
|
|
150
|
-
}
|
|
151
|
-
function consoleUrl(base, route) {
|
|
152
|
-
const normalizedRoute = route.startsWith("/") ? route : `/${route}`;
|
|
153
|
-
return `${normalizeConsoleBase(base)}${normalizedRoute}`;
|
|
154
|
-
}
|
|
155
|
-
function resolveConsoleUrlFromEnv() {
|
|
156
|
-
const raw = process.env["MUSHI_CONSOLE_URL"]?.trim();
|
|
157
|
-
return raw ? normalizeConsoleBase(raw) : void 0;
|
|
158
|
-
}
|
|
159
|
-
function isInsideMushiMonorepo(cwd) {
|
|
160
|
-
let dir = cwd;
|
|
161
|
-
for (let depth = 0; depth < 14; depth += 1) {
|
|
162
|
-
const pkgPath = join(dir, "package.json");
|
|
163
|
-
if (existsSync(pkgPath)) {
|
|
164
|
-
try {
|
|
165
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
166
|
-
if (pkg.name === "mushi-mushi") return true;
|
|
167
|
-
} catch {
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
const parent = dirname(dir);
|
|
171
|
-
if (parent === dir) break;
|
|
172
|
-
dir = parent;
|
|
173
|
-
}
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
|
-
async function probeLocalAdminConsole() {
|
|
177
|
-
try {
|
|
178
|
-
const controller = new AbortController();
|
|
179
|
-
const timer = setTimeout(() => controller.abort(), LOCAL_PROBE_MS);
|
|
180
|
-
const res = await fetch(LOCAL_ADMIN_URL, {
|
|
181
|
-
signal: controller.signal,
|
|
182
|
-
method: "HEAD"
|
|
183
|
-
});
|
|
184
|
-
clearTimeout(timer);
|
|
185
|
-
if (res.ok || res.status === 404 || res.status === 401) {
|
|
186
|
-
return LOCAL_ADMIN_URL;
|
|
187
|
-
}
|
|
188
|
-
} catch {
|
|
189
|
-
}
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
function resolveConsoleUrlSync(cwd = process.cwd()) {
|
|
193
|
-
const fromEnv = resolveConsoleUrlFromEnv();
|
|
194
|
-
if (fromEnv) return fromEnv;
|
|
195
|
-
const config = loadConfig();
|
|
196
|
-
if (config.consoleUrl) return normalizeConsoleBase(config.consoleUrl);
|
|
197
|
-
if (isInsideMushiMonorepo(cwd)) return LOCAL_ADMIN_URL;
|
|
198
|
-
return HOSTED_CONSOLE_BASE;
|
|
199
|
-
}
|
|
200
|
-
async function resolveConsoleUrl(options) {
|
|
201
|
-
const fromEnv = resolveConsoleUrlFromEnv();
|
|
202
|
-
if (fromEnv) return fromEnv;
|
|
203
|
-
const config = loadConfig();
|
|
204
|
-
if (config.consoleUrl) return normalizeConsoleBase(config.consoleUrl);
|
|
205
|
-
const local = await probeLocalAdminConsole();
|
|
206
|
-
if (local) return local;
|
|
207
|
-
const cwd = options?.cwd ?? process.cwd();
|
|
208
|
-
if (isInsideMushiMonorepo(cwd)) return LOCAL_ADMIN_URL;
|
|
209
|
-
return HOSTED_CONSOLE_BASE;
|
|
210
|
-
}
|
|
211
|
-
function projectIdHint(base) {
|
|
212
|
-
return `Where to find it: ${consoleUrl(base, "/projects")} \u2192 select your project \u2192 click the UUID chip to copy (or copy it from the panel right after you create the project).`;
|
|
213
|
-
}
|
|
214
|
-
function apiKeyHint(base) {
|
|
215
|
-
return `Where to find it: ${consoleUrl(base, "/onboarding?tab=verify")} \u2192 Generate API key (SDK ingest needs report:write \u2014 not Settings \u2192 BYOK keys). Treat it like a password \u2014 env file only, never commit.`;
|
|
216
|
-
}
|
|
217
|
-
function cliSetupDeepLink(base) {
|
|
218
|
-
return consoleUrl(base, "/onboarding?tab=steps&setup=cli");
|
|
219
|
-
}
|
|
220
|
-
function reportsUrl(base, reportId) {
|
|
221
|
-
return reportId ? consoleUrl(base, `/reports/${reportId}`) : consoleUrl(base, "/reports");
|
|
222
|
-
}
|
|
223
|
-
async function openInBrowser(url) {
|
|
224
|
-
let parsed;
|
|
225
|
-
try {
|
|
226
|
-
parsed = new URL(url);
|
|
227
|
-
} catch {
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
const safeUrl = parsed.toString();
|
|
234
|
-
const { spawn: spawn2 } = await import("child_process");
|
|
235
|
-
const [command, args] = process.platform === "win32" ? (
|
|
236
|
-
// Open via rundll32's FileProtocolHandler rather than `cmd /c start`.
|
|
237
|
-
// `start` is a cmd builtin, so the URL would be re-parsed by cmd.exe and
|
|
238
|
-
// characters like `&` / `|` / `^` could still break out — exactly the
|
|
239
|
-
// injection surface CodeQL flags. rundll32 never invokes a shell: the URL
|
|
240
|
-
// is handed to the default browser as one opaque argument.
|
|
241
|
-
["rundll32", ["url.dll,FileProtocolHandler", safeUrl]]
|
|
242
|
-
) : process.platform === "darwin" ? ["open", [safeUrl]] : ["xdg-open", [safeUrl]];
|
|
243
|
-
await new Promise((resolve3) => {
|
|
244
|
-
try {
|
|
245
|
-
const child = spawn2(command, [...args], { stdio: "ignore", shell: false });
|
|
246
|
-
child.on("error", () => resolve3());
|
|
247
|
-
child.on("exit", () => resolve3());
|
|
248
|
-
child.unref();
|
|
249
|
-
} catch {
|
|
250
|
-
resolve3();
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// src/cli-shared.ts
|
|
256
|
-
var API_TIMEOUT_MS = 15e3;
|
|
257
|
-
async function probeEndpointHealth(endpoint) {
|
|
258
|
-
const t0 = Date.now();
|
|
259
|
-
const controller = new AbortController();
|
|
260
|
-
const timer = setTimeout(() => controller.abort(), API_TIMEOUT_MS);
|
|
261
|
-
try {
|
|
262
|
-
const res = await fetch(`${endpoint}/health`, { signal: controller.signal });
|
|
263
|
-
clearTimeout(timer);
|
|
264
|
-
const body = res.headers.get("content-type")?.includes("json") ? await res.json().catch(() => ({})) : {};
|
|
265
|
-
return { ok: res.ok, status: res.status, latencyMs: Date.now() - t0, body };
|
|
266
|
-
} catch (err) {
|
|
267
|
-
clearTimeout(timer);
|
|
268
|
-
throw err;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
async function apiCall(path, config, options = {}) {
|
|
272
|
-
const endpoint = config.endpoint;
|
|
273
|
-
if (!endpoint) {
|
|
274
|
-
return {
|
|
275
|
-
ok: false,
|
|
276
|
-
error: {
|
|
277
|
-
code: "NO_ENDPOINT",
|
|
278
|
-
message: "No API endpoint configured.\n Run: mushi login --api-key <key> --endpoint <url> --project-id <id>\n Or: export MUSHI_API_ENDPOINT=https://<project-ref>.supabase.co/functions/v1/api"
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
const controller = new AbortController();
|
|
283
|
-
const timer = setTimeout(() => controller.abort(), API_TIMEOUT_MS);
|
|
284
|
-
const signals = [controller.signal, getAbortSignal()];
|
|
285
|
-
const compositeSignal = AbortSignal.any ? AbortSignal.any(signals) : controller.signal;
|
|
286
|
-
try {
|
|
287
|
-
const res = await fetch(`${endpoint}${path}`, {
|
|
288
|
-
...options,
|
|
289
|
-
signal: compositeSignal,
|
|
290
|
-
headers: {
|
|
291
|
-
"Content-Type": "application/json",
|
|
292
|
-
// `apiKeyAuth` reads X-Mushi-Api-Key; the Authorization header is a
|
|
293
|
-
// fallback accepted by some older middleware. Both are sent.
|
|
294
|
-
"Authorization": `Bearer ${config.apiKey ?? ""}`,
|
|
295
|
-
"X-Mushi-Api-Key": config.apiKey ?? "",
|
|
296
|
-
"X-Mushi-Project": config.projectId ?? "",
|
|
297
|
-
"X-Mushi-Cli-Version": MUSHI_CLI_VERSION,
|
|
298
|
-
...options.headers
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
clearTimeout(timer);
|
|
302
|
-
let body;
|
|
303
|
-
const contentType = res.headers.get("content-type") ?? "";
|
|
304
|
-
if (contentType.includes("application/json")) {
|
|
305
|
-
try {
|
|
306
|
-
body = await res.json();
|
|
307
|
-
} catch {
|
|
308
|
-
body = null;
|
|
309
|
-
}
|
|
310
|
-
} else {
|
|
311
|
-
const text2 = await res.text();
|
|
312
|
-
try {
|
|
313
|
-
body = JSON.parse(text2);
|
|
314
|
-
} catch {
|
|
315
|
-
body = {
|
|
316
|
-
ok: false,
|
|
317
|
-
error: {
|
|
318
|
-
code: `HTTP_${res.status}`,
|
|
319
|
-
message: text2.trim().slice(0, 300) || `HTTP ${res.status}`
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
if (!res.ok && typeof body === "object" && body !== null && !("ok" in body)) {
|
|
325
|
-
const b = body;
|
|
326
|
-
const nested = b["error"];
|
|
327
|
-
const errCode = nested?.["code"] ?? b["code"] ?? `HTTP_${res.status}`;
|
|
328
|
-
const errMsg = nested?.["message"] ?? b["message"] ?? `Request failed (${res.status})`;
|
|
329
|
-
return {
|
|
330
|
-
ok: false,
|
|
331
|
-
httpStatus: res.status,
|
|
332
|
-
error: { code: errCode, message: errMsg }
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
return body;
|
|
336
|
-
} catch (err) {
|
|
337
|
-
clearTimeout(timer);
|
|
338
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
339
|
-
return {
|
|
340
|
-
ok: false,
|
|
341
|
-
error: {
|
|
342
|
-
code: "TIMEOUT",
|
|
343
|
-
message: `Request timed out after ${API_TIMEOUT_MS / 1e3}s. Check your network or endpoint.`
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
return {
|
|
348
|
-
ok: false,
|
|
349
|
-
error: {
|
|
350
|
-
code: "NETWORK_ERROR",
|
|
351
|
-
message: err instanceof Error ? err.message : String(err)
|
|
352
|
-
}
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
function die(result, exitCode = 1) {
|
|
357
|
-
const { code, message } = result.error;
|
|
358
|
-
const status = result.httpStatus ? ` [${result.httpStatus}]` : "";
|
|
359
|
-
process.stderr.write(`error${status}: ${code} \u2014 ${message}
|
|
360
|
-
`);
|
|
361
|
-
if (code === "INSUFFICIENT_SCOPE") {
|
|
362
|
-
process.stderr.write(
|
|
363
|
-
"\n Your current API key does not have the required scope for this command.\n New keys minted by the wizard already include both scopes. Existing keys\n can be upgraded with:\n\n mushi login --upgrade-scope\n\n Or visit your console \u2192 Projects \u2192 API Keys to mint a new key.\n"
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
process.exit(exitCode);
|
|
367
|
-
}
|
|
368
|
-
function requireConfig(opts = {}) {
|
|
369
|
-
const config = loadConfig();
|
|
370
|
-
if (!config.apiKey) {
|
|
371
|
-
printAndExit(new MushiCliError(
|
|
372
|
-
"E_AUTH_MISSING",
|
|
373
|
-
"API key not configured.",
|
|
374
|
-
"Run: mushi login --api-key <key> Or: export MUSHI_API_KEY=<key>"
|
|
375
|
-
));
|
|
376
|
-
}
|
|
377
|
-
if (!config.endpoint) {
|
|
378
|
-
config.endpoint = process.env.MUSHI_API_ENDPOINT?.trim() || CLOUD_API_ENDPOINT;
|
|
379
|
-
}
|
|
380
|
-
if (opts.needsProject && !config.projectId) {
|
|
381
|
-
printAndExit(new MushiCliError(
|
|
382
|
-
"E_PROJECT_MISSING",
|
|
383
|
-
"Project ID not configured.",
|
|
384
|
-
`Run: mushi login --project-id <uuid> Or find your project ID at ${consoleUrl(resolveConsoleUrlSync(), "/projects")}`
|
|
385
|
-
));
|
|
386
|
-
}
|
|
387
|
-
return config;
|
|
388
|
-
}
|
|
389
|
-
function fmtDate(iso) {
|
|
390
|
-
if (!iso) return "\u2014";
|
|
391
|
-
return new Date(iso).toLocaleString(void 0, { dateStyle: "short", timeStyle: "short" });
|
|
392
|
-
}
|
|
393
|
-
function pad(s, width) {
|
|
394
|
-
return s.length >= width ? s : s + " ".repeat(width - s.length);
|
|
395
|
-
}
|
|
54
|
+
import { appendFileSync, existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
55
|
+
import { join as join2 } from "path";
|
|
396
56
|
|
|
397
57
|
// src/device-auth.ts
|
|
398
58
|
var DEVICE_FETCH_TIMEOUT_MS = 15e3;
|
|
@@ -506,7 +166,11 @@ async function listProjects(endpoint, cliToken) {
|
|
|
506
166
|
const res = await deviceFetch(`${base}/v1/cli/projects`, { headers: cliAuthHeaders(cliToken) });
|
|
507
167
|
const json = await res.json().catch(() => null);
|
|
508
168
|
if (res.ok && json?.ok) return json.data?.projects ?? [];
|
|
509
|
-
} catch {
|
|
169
|
+
} catch (err) {
|
|
170
|
+
console.error(
|
|
171
|
+
"[mushi] Failed to list projects:",
|
|
172
|
+
err instanceof Error ? err.message : err
|
|
173
|
+
);
|
|
510
174
|
}
|
|
511
175
|
return [];
|
|
512
176
|
}
|
|
@@ -599,8 +263,8 @@ function parse(version) {
|
|
|
599
263
|
}
|
|
600
264
|
|
|
601
265
|
// src/monorepo.ts
|
|
602
|
-
import { existsSync
|
|
603
|
-
import { dirname
|
|
266
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
267
|
+
import { dirname, join, resolve } from "path";
|
|
604
268
|
var WORKSPACE_GLOB_CANDIDATES = ["apps/*", "packages/*", "examples/*"];
|
|
605
269
|
var FRAMEWORK_DEPS = {
|
|
606
270
|
next: "Next.js",
|
|
@@ -617,9 +281,9 @@ var FRAMEWORK_DEPS = {
|
|
|
617
281
|
function detectWorkspaceHint(cwd) {
|
|
618
282
|
const root = findWorkspaceRoot(cwd);
|
|
619
283
|
if (!root) return null;
|
|
620
|
-
const rootPkg = readPackageJsonSafely(
|
|
284
|
+
const rootPkg = readPackageJsonSafely(join(root, "package.json"));
|
|
621
285
|
if (rootPkg && getFrameworkFromPkg(rootPkg)) return null;
|
|
622
|
-
const source =
|
|
286
|
+
const source = existsSync(join(root, "pnpm-workspace.yaml")) ? "pnpm-workspace" : root === cwd ? "package-json" : "parent";
|
|
623
287
|
const apps = collectAppsFromGlobs(root);
|
|
624
288
|
if (apps.length === 0) return null;
|
|
625
289
|
return { root, apps, source };
|
|
@@ -628,15 +292,15 @@ function findWorkspaceRoot(start) {
|
|
|
628
292
|
let dir = resolve(start);
|
|
629
293
|
for (let i = 0; i < 8; i++) {
|
|
630
294
|
if (isWorkspaceRoot(dir)) return dir;
|
|
631
|
-
const parent =
|
|
295
|
+
const parent = dirname(dir);
|
|
632
296
|
if (parent === dir) break;
|
|
633
297
|
dir = parent;
|
|
634
298
|
}
|
|
635
299
|
return null;
|
|
636
300
|
}
|
|
637
301
|
function isWorkspaceRoot(dir) {
|
|
638
|
-
if (
|
|
639
|
-
const pkg = readPackageJsonSafely(
|
|
302
|
+
if (existsSync(join(dir, "pnpm-workspace.yaml"))) return true;
|
|
303
|
+
const pkg = readPackageJsonSafely(join(dir, "package.json"));
|
|
640
304
|
if (!pkg) return false;
|
|
641
305
|
return Boolean(pkg.workspaces);
|
|
642
306
|
}
|
|
@@ -644,8 +308,8 @@ function collectAppsFromGlobs(root) {
|
|
|
644
308
|
const results = [];
|
|
645
309
|
for (const glob of WORKSPACE_GLOB_CANDIDATES) {
|
|
646
310
|
const prefix = glob.replace("/*", "");
|
|
647
|
-
const parentDir =
|
|
648
|
-
if (!
|
|
311
|
+
const parentDir = join(root, prefix);
|
|
312
|
+
if (!existsSync(parentDir)) continue;
|
|
649
313
|
let entries;
|
|
650
314
|
try {
|
|
651
315
|
entries = readdirSync(parentDir);
|
|
@@ -653,7 +317,7 @@ function collectAppsFromGlobs(root) {
|
|
|
653
317
|
continue;
|
|
654
318
|
}
|
|
655
319
|
for (const entry of entries) {
|
|
656
|
-
const pkgPath =
|
|
320
|
+
const pkgPath = join(parentDir, entry, "package.json");
|
|
657
321
|
if (!isFileSafe(pkgPath)) continue;
|
|
658
322
|
const pkg = readPackageJsonSafely(pkgPath);
|
|
659
323
|
if (!pkg) continue;
|
|
@@ -671,14 +335,14 @@ function collectAppsFromGlobs(root) {
|
|
|
671
335
|
function readPackageJsonSafely(path) {
|
|
672
336
|
if (!isFileSafe(path)) return null;
|
|
673
337
|
try {
|
|
674
|
-
return JSON.parse(
|
|
338
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
675
339
|
} catch {
|
|
676
340
|
return null;
|
|
677
341
|
}
|
|
678
342
|
}
|
|
679
343
|
function isFileSafe(path) {
|
|
680
344
|
try {
|
|
681
|
-
return
|
|
345
|
+
return existsSync(path) && statSync(path).isFile();
|
|
682
346
|
} catch {
|
|
683
347
|
return false;
|
|
684
348
|
}
|
|
@@ -1085,10 +749,10 @@ function runCommand(pm, packages, cwd) {
|
|
|
1085
749
|
});
|
|
1086
750
|
}
|
|
1087
751
|
async function writeEnvFile(cwd, apiKey, projectId, framework, endpoint, overwrite) {
|
|
1088
|
-
const target = ENV_FILES.find((f) =>
|
|
1089
|
-
const targetPath =
|
|
752
|
+
const target = ENV_FILES.find((f) => existsSync2(join2(cwd, f))) ?? ENV_FILES[0];
|
|
753
|
+
const targetPath = join2(cwd, target);
|
|
1090
754
|
const newVars = envVarsToWrite(apiKey, projectId, framework, endpoint);
|
|
1091
|
-
const existing =
|
|
755
|
+
const existing = existsSync2(targetPath) ? readFileSync2(targetPath, "utf-8") : "";
|
|
1092
756
|
if (existing.includes("MUSHI_PROJECT_ID")) {
|
|
1093
757
|
let shouldOverwrite = overwrite;
|
|
1094
758
|
if (!shouldOverwrite) {
|
|
@@ -1147,12 +811,12 @@ function escapeRegexChar(ch) {
|
|
|
1147
811
|
return /[-/\\^$+?.()|[\]{}]/.test(ch) ? `\\${ch}` : ch;
|
|
1148
812
|
}
|
|
1149
813
|
function warnIfMissingFromGitignore(cwd, envFile) {
|
|
1150
|
-
const gitignorePath =
|
|
1151
|
-
if (!
|
|
814
|
+
const gitignorePath = join2(cwd, ".gitignore");
|
|
815
|
+
if (!existsSync2(gitignorePath)) {
|
|
1152
816
|
p.log.warn(`No .gitignore found \u2014 make sure ${envFile} is not committed.`);
|
|
1153
817
|
return;
|
|
1154
818
|
}
|
|
1155
|
-
const content =
|
|
819
|
+
const content = readFileSync2(gitignorePath, "utf-8");
|
|
1156
820
|
if (!isEnvFileCoveredByGitignore(content, envFile)) {
|
|
1157
821
|
p.log.warn(`${envFile} is not in .gitignore \u2014 add it before committing.`);
|
|
1158
822
|
}
|
|
@@ -1194,7 +858,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
|
|
|
1194
858
|
if (p.isCancel(answer) || !answer) return;
|
|
1195
859
|
const endpoint = resolveCloudEndpoint(options.endpoint);
|
|
1196
860
|
try {
|
|
1197
|
-
const { runConnect: runConnect2 } = await import("./connect-
|
|
861
|
+
const { runConnect: runConnect2 } = await import("./connect-XFVSH4JZ.js");
|
|
1198
862
|
await runConnect2({
|
|
1199
863
|
apiKey: credentials.apiKey,
|
|
1200
864
|
projectId: credentials.projectId,
|
|
@@ -1326,7 +990,7 @@ function isSameDirectory(a, b) {
|
|
|
1326
990
|
}
|
|
1327
991
|
|
|
1328
992
|
// src/migrate.ts
|
|
1329
|
-
var DOCS_BASE = "https://docs
|
|
993
|
+
var DOCS_BASE = "https://kensaur.us/mushi-mushi/docs";
|
|
1330
994
|
var COMPETITOR_PACKAGES = {
|
|
1331
995
|
"instabug-to-mushi": [
|
|
1332
996
|
"instabug-reactnative",
|
|
@@ -1488,8 +1152,8 @@ Examples:
|
|
|
1488
1152
|
mushi login --api-key mushi_xxx # non-interactive / CI
|
|
1489
1153
|
mushi login --api-key mushi_xxx --project-id <uuid>
|
|
1490
1154
|
mushi login --upgrade-scope # re-auth to add mcp:read + mcp:write to your key`).action(async (opts) => {
|
|
1491
|
-
const { CLOUD_API_ENDPOINT
|
|
1492
|
-
const endpoint = opts.endpoint ? resolveCloudEndpoint2(opts.endpoint) :
|
|
1155
|
+
const { CLOUD_API_ENDPOINT, resolveCloudEndpoint: resolveCloudEndpoint2 } = await import("./endpoint-DGS73AVM.js");
|
|
1156
|
+
const endpoint = opts.endpoint ? resolveCloudEndpoint2(opts.endpoint) : CLOUD_API_ENDPOINT;
|
|
1493
1157
|
const consoleBase = await resolveConsoleUrl();
|
|
1494
1158
|
let apiKey = opts.apiKey;
|
|
1495
1159
|
let projectId = opts.projectId;
|
|
@@ -2221,14 +1885,14 @@ Typical CI usage:
|
|
|
2221
1885
|
import { createReadStream } from "fs";
|
|
2222
1886
|
import { readFile, readdir } from "fs/promises";
|
|
2223
1887
|
import { createHash } from "crypto";
|
|
2224
|
-
import { join as
|
|
1888
|
+
import { join as join3, relative, basename } from "path";
|
|
2225
1889
|
import * as p2 from "@clack/prompts";
|
|
2226
1890
|
async function findMapFiles(dir) {
|
|
2227
1891
|
const results = [];
|
|
2228
1892
|
async function walk(current) {
|
|
2229
1893
|
const entries = await readdir(current, { withFileTypes: true });
|
|
2230
1894
|
for (const e of entries) {
|
|
2231
|
-
const full =
|
|
1895
|
+
const full = join3(current, e.name);
|
|
2232
1896
|
if (e.isDirectory()) {
|
|
2233
1897
|
await walk(full);
|
|
2234
1898
|
} else if (e.isFile() && (e.name.endsWith(".js.map") || e.name.endsWith(".css.map"))) {
|
|
@@ -2403,7 +2067,7 @@ Examples:
|
|
|
2403
2067
|
mushi index ./src
|
|
2404
2068
|
mushi index ./src --language ts --dry-run`).action(async (path, opts) => {
|
|
2405
2069
|
const config = requireConfig({ needsProject: true });
|
|
2406
|
-
const { readdir: readdir2, readFile:
|
|
2070
|
+
const { readdir: readdir2, readFile: readFile3, stat } = await import("fs/promises");
|
|
2407
2071
|
const nodePath2 = await import("path");
|
|
2408
2072
|
const SKIP = /node_modules|\.git|dist|build|\.next|\.turbo|coverage/;
|
|
2409
2073
|
const EXTS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rs"]);
|
|
@@ -2430,7 +2094,7 @@ Examples:
|
|
|
2430
2094
|
`);
|
|
2431
2095
|
continue;
|
|
2432
2096
|
}
|
|
2433
|
-
const source = await
|
|
2097
|
+
const source = await readFile3(file, "utf8");
|
|
2434
2098
|
const relative2 = nodePath2.relative(root, file).replaceAll("\\", "/");
|
|
2435
2099
|
count++;
|
|
2436
2100
|
bytes += source.length;
|
|
@@ -2478,21 +2142,32 @@ Examples:
|
|
|
2478
2142
|
}
|
|
2479
2143
|
|
|
2480
2144
|
// src/project-bootstrap.ts
|
|
2481
|
-
import { writeFile } from "fs/promises";
|
|
2482
|
-
import { existsSync as existsSync4 } from "fs";
|
|
2145
|
+
import { writeFile, readFile as readFile2 } from "fs/promises";
|
|
2483
2146
|
import nodePath from "path";
|
|
2484
2147
|
async function writeProjectBootstrapFiles(opts) {
|
|
2485
2148
|
const cwd = opts.cwd ?? process.cwd();
|
|
2486
2149
|
const envPath = nodePath.join(cwd, ".env.local");
|
|
2487
|
-
const
|
|
2150
|
+
const mushiBlock = [
|
|
2488
2151
|
"# Mushi MCP \u2014 drop into .env.local (gitignored). The MCP binary picks these up on spawn.",
|
|
2489
2152
|
`MUSHI_API_ENDPOINT=${opts.endpoint}`,
|
|
2490
2153
|
`MUSHI_PROJECT_ID=${opts.projectId}`,
|
|
2491
|
-
`MUSHI_API_KEY=${opts.apiKey}
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2154
|
+
`MUSHI_API_KEY=${opts.apiKey}`
|
|
2155
|
+
].join("\n");
|
|
2156
|
+
let existing = "";
|
|
2157
|
+
let envUpdated = false;
|
|
2158
|
+
try {
|
|
2159
|
+
existing = await readFile2(envPath, "utf8");
|
|
2160
|
+
envUpdated = true;
|
|
2161
|
+
} catch {
|
|
2162
|
+
}
|
|
2163
|
+
const MUSHI_LINE_RE = /^(NEXT_PUBLIC_|NUXT_PUBLIC_|VITE_|EXPO_PUBLIC_)?MUSHI_[A-Z_]+=.*/gm;
|
|
2164
|
+
const MUSHI_COMMENT_RE = /^# Mushi MCP\b.*/gm;
|
|
2165
|
+
const stripped = existing.replace(MUSHI_LINE_RE, "").replace(MUSHI_COMMENT_RE, "").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
2166
|
+
const prefix = stripped.length > 0 ? `${stripped}
|
|
2167
|
+
|
|
2168
|
+
` : "";
|
|
2169
|
+
await writeFile(envPath, `${prefix}${mushiBlock}
|
|
2170
|
+
`, "utf8");
|
|
2496
2171
|
const mcpPath = nodePath.join(cwd, ".cursor", "mcp.json");
|
|
2497
2172
|
const serverName = buildMcpServerName({ legacy: true });
|
|
2498
2173
|
const serverBlock = buildMcpServerBlock({
|
|
@@ -2649,8 +2324,8 @@ Supported IDEs:
|
|
|
2649
2324
|
zed \u2014 writes ~/.config/zed/settings.json mcpServers block
|
|
2650
2325
|
|
|
2651
2326
|
The command reads credentials from ~/.config/mushi/config.json (run \`mushi login\` first).`).action(async (opts) => {
|
|
2652
|
-
const { writeFile: writeFile2, mkdir, readFile:
|
|
2653
|
-
const { existsSync:
|
|
2327
|
+
const { writeFile: writeFile2, mkdir, readFile: readFile3 } = await import("fs/promises");
|
|
2328
|
+
const { existsSync: existsSync3 } = await import("fs");
|
|
2654
2329
|
const nodePath2 = await import("path");
|
|
2655
2330
|
const os = await import("os");
|
|
2656
2331
|
const config = requireConfig({ needsProject: true });
|
|
@@ -2762,9 +2437,9 @@ The command reads credentials from ~/.config/mushi/config.json (run \`mushi logi
|
|
|
2762
2437
|
}
|
|
2763
2438
|
} else if (ideEntry.format === "zed") {
|
|
2764
2439
|
let settings = {};
|
|
2765
|
-
if (
|
|
2440
|
+
if (existsSync3(configPath)) {
|
|
2766
2441
|
try {
|
|
2767
|
-
const raw = await
|
|
2442
|
+
const raw = await readFile3(configPath, "utf8");
|
|
2768
2443
|
settings = JSON.parse(raw);
|
|
2769
2444
|
} catch {
|
|
2770
2445
|
}
|
|
@@ -2884,7 +2559,11 @@ The command reads credentials from ~/.config/mushi/config.json (run \`mushi logi
|
|
|
2884
2559
|
console.log(`\u26A0 Key probe returned HTTP ${probeRes.status} \u2014 check your credentials.`);
|
|
2885
2560
|
}
|
|
2886
2561
|
}
|
|
2887
|
-
} catch {
|
|
2562
|
+
} catch (probeErr) {
|
|
2563
|
+
console.warn(
|
|
2564
|
+
"[mushi setup] API key probe failed \u2014 verify credentials manually.",
|
|
2565
|
+
probeErr instanceof Error ? probeErr.message : probeErr
|
|
2566
|
+
);
|
|
2888
2567
|
}
|
|
2889
2568
|
}
|
|
2890
2569
|
console.log("");
|
|
@@ -2923,6 +2602,7 @@ Examples:
|
|
|
2923
2602
|
|
|
2924
2603
|
# CI: fail the pipeline if the fix errors
|
|
2925
2604
|
mushi fix $REPORT_ID --agent cursor_cloud --wait && echo "Fix PR opened"`).action(async (reportId, opts) => {
|
|
2605
|
+
requireUuid(reportId, "reportId");
|
|
2926
2606
|
const cfg = loadConfig();
|
|
2927
2607
|
if (opts.endpoint) cfg.endpoint = opts.endpoint;
|
|
2928
2608
|
if (opts.apiKey) cfg.apiKey = opts.apiKey;
|
|
@@ -3297,9 +2977,9 @@ async function planUpgrade(cwd) {
|
|
|
3297
2977
|
...pkg?.dependencies ?? {},
|
|
3298
2978
|
...pkg?.devDependencies ?? {}
|
|
3299
2979
|
};
|
|
3300
|
-
const
|
|
2980
|
+
const installed = MUSHI_PACKAGES.filter((name) => deps[name]);
|
|
3301
2981
|
const entries = await Promise.all(
|
|
3302
|
-
|
|
2982
|
+
installed.map(async (name) => {
|
|
3303
2983
|
const current = deps[name] ?? "";
|
|
3304
2984
|
const currentCore = current.replace(/^[\^~>=<]*/, "");
|
|
3305
2985
|
if (!/^\d/.test(currentCore)) {
|
|
@@ -3463,11 +3143,11 @@ async function checkEndpointReachability(endpoint, doFetch = globalThis.fetch) {
|
|
|
3463
3143
|
}
|
|
3464
3144
|
async function checkSdkInstall(cwd) {
|
|
3465
3145
|
try {
|
|
3466
|
-
const { readFile:
|
|
3467
|
-
const { join:
|
|
3146
|
+
const { readFile: readFile3 } = await import("fs/promises");
|
|
3147
|
+
const { join: join4, resolve: resolve3 } = await import("path");
|
|
3468
3148
|
const root = resolve3(cwd);
|
|
3469
|
-
const pkgPath =
|
|
3470
|
-
const pkg = JSON.parse(await
|
|
3149
|
+
const pkgPath = join4(root, "package.json");
|
|
3150
|
+
const pkg = JSON.parse(await readFile3(pkgPath, "utf8"));
|
|
3471
3151
|
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
3472
3152
|
const sdks = [
|
|
3473
3153
|
"@mushi-mushi/react",
|
|
@@ -3475,11 +3155,11 @@ async function checkSdkInstall(cwd) {
|
|
|
3475
3155
|
"@mushi-mushi/core",
|
|
3476
3156
|
"@mushi-mushi/react-native"
|
|
3477
3157
|
];
|
|
3478
|
-
const
|
|
3158
|
+
const installed = sdks.filter((s) => deps[s]);
|
|
3479
3159
|
return {
|
|
3480
3160
|
name: "SDK installed in this repo",
|
|
3481
|
-
ok:
|
|
3482
|
-
detail:
|
|
3161
|
+
ok: installed.length > 0,
|
|
3162
|
+
detail: installed.length > 0 ? installed.map((s) => `${s}@${deps[s]}`).join(", ") : "No @mushi-mushi/* package in package.json \u2014 run `mushi init` to install"
|
|
3483
3163
|
};
|
|
3484
3164
|
} catch {
|
|
3485
3165
|
return null;
|
|
@@ -3662,18 +3342,18 @@ async function checkQaStoriesHealth(config, doFetch = globalThis.fetch) {
|
|
|
3662
3342
|
async function checkHostAppWiring(cwd) {
|
|
3663
3343
|
const checks = [];
|
|
3664
3344
|
try {
|
|
3665
|
-
const { readFile:
|
|
3666
|
-
const { join:
|
|
3345
|
+
const { readFile: readFile3, access } = await import("fs/promises");
|
|
3346
|
+
const { join: join4, resolve: resolve3 } = await import("path");
|
|
3667
3347
|
const root = resolve3(cwd);
|
|
3668
|
-
const pkgPath =
|
|
3669
|
-
const pkg = JSON.parse(await
|
|
3348
|
+
const pkgPath = join4(root, "package.json");
|
|
3349
|
+
const pkg = JSON.parse(await readFile3(pkgPath, "utf8"));
|
|
3670
3350
|
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
3671
3351
|
const isCapHybrid = Boolean(deps["@capacitor/core"] && deps["react"]);
|
|
3672
3352
|
const envCandidates = [".env.local", ".env"];
|
|
3673
3353
|
let envContent = "";
|
|
3674
3354
|
for (const f of envCandidates) {
|
|
3675
3355
|
try {
|
|
3676
|
-
envContent = await
|
|
3356
|
+
envContent = await readFile3(join4(root, f), "utf8");
|
|
3677
3357
|
break;
|
|
3678
3358
|
} catch {
|
|
3679
3359
|
}
|
|
@@ -3687,7 +3367,7 @@ async function checkHostAppWiring(cwd) {
|
|
|
3687
3367
|
});
|
|
3688
3368
|
let mcpPresent = false;
|
|
3689
3369
|
try {
|
|
3690
|
-
await access(
|
|
3370
|
+
await access(join4(root, ".cursor", "mcp.json"));
|
|
3691
3371
|
mcpPresent = true;
|
|
3692
3372
|
} catch {
|
|
3693
3373
|
}
|
|
@@ -3720,19 +3400,19 @@ async function checkHostAppWiring(cwd) {
|
|
|
3720
3400
|
}
|
|
3721
3401
|
async function checkMcpConfig(config, cwd, doFetch = globalThis.fetch) {
|
|
3722
3402
|
const checks = [];
|
|
3723
|
-
const { readFile:
|
|
3724
|
-
const { join:
|
|
3403
|
+
const { readFile: readFile3 } = await import("fs/promises");
|
|
3404
|
+
const { join: join4, resolve: resolve3 } = await import("path");
|
|
3725
3405
|
const { homedir } = await import("os");
|
|
3726
3406
|
const root = resolve3(cwd);
|
|
3727
3407
|
const candidates = [
|
|
3728
|
-
|
|
3729
|
-
|
|
3408
|
+
join4(root, ".cursor", "mcp.json"),
|
|
3409
|
+
join4(homedir(), ".cursor", "mcp.json")
|
|
3730
3410
|
];
|
|
3731
3411
|
let mcpPath = null;
|
|
3732
3412
|
let mcpRaw = null;
|
|
3733
3413
|
for (const candidate of candidates) {
|
|
3734
3414
|
try {
|
|
3735
|
-
const raw = await
|
|
3415
|
+
const raw = await readFile3(candidate, "utf8");
|
|
3736
3416
|
let parsed = {};
|
|
3737
3417
|
try {
|
|
3738
3418
|
parsed = JSON.parse(raw);
|
|
@@ -3878,13 +3558,13 @@ async function checkOnboardingStatus(config, _consoleBase, cwd, doFetch = global
|
|
|
3878
3558
|
};
|
|
3879
3559
|
}
|
|
3880
3560
|
try {
|
|
3881
|
-
const { readFile:
|
|
3882
|
-
const { join:
|
|
3561
|
+
const { readFile: readFile3 } = await import("fs/promises");
|
|
3562
|
+
const { join: join4, resolve: resolve3 } = await import("path");
|
|
3883
3563
|
const root = resolve3(cwd);
|
|
3884
3564
|
let envContent = "";
|
|
3885
3565
|
for (const f of [".env.local", ".env"]) {
|
|
3886
3566
|
try {
|
|
3887
|
-
envContent = await
|
|
3567
|
+
envContent = await readFile3(join4(root, f), "utf8");
|
|
3888
3568
|
break;
|
|
3889
3569
|
} catch {
|
|
3890
3570
|
}
|
|
@@ -5148,6 +4828,5 @@ registerAuditCommands(program);
|
|
|
5148
4828
|
registerSkillsCommands(program);
|
|
5149
4829
|
registerBillingCommands(program);
|
|
5150
4830
|
program.parseAsync().catch((err) => {
|
|
5151
|
-
|
|
5152
|
-
process.exit(1);
|
|
4831
|
+
printAndExit(err);
|
|
5153
4832
|
});
|