@mushi-mushi/cli 0.22.1 → 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 +75 -407
- package/dist/init.js +40 -241
- package/dist/version.js +1 -1
- package/package.json +12 -14
- package/dist/chunk-XEXYGDQV.js +0 -137
- package/dist/chunk-YK4LF7AM.js +0 -6
package/dist/init.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MUSHI_CLI_VERSION
|
|
3
|
-
} from "./chunk-YK4LF7AM.js";
|
|
4
1
|
import {
|
|
5
2
|
TEST_REPORT_FETCH_TIMEOUT_MS,
|
|
3
|
+
apiCall,
|
|
4
|
+
apiKeyHint,
|
|
5
|
+
cliSetupDeepLink,
|
|
6
|
+
consoleUrl,
|
|
6
7
|
loadConfig,
|
|
7
8
|
normalizeEndpoint,
|
|
9
|
+
openInBrowser,
|
|
10
|
+
projectIdHint,
|
|
11
|
+
reportsUrl,
|
|
8
12
|
resolveCloudEndpoint,
|
|
13
|
+
resolveConsoleUrl,
|
|
14
|
+
resolveConsoleUrlSync,
|
|
9
15
|
saveConfig
|
|
10
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-WLLFV26V.js";
|
|
11
17
|
import {
|
|
12
18
|
FRAMEWORKS,
|
|
13
19
|
detectFramework,
|
|
@@ -15,228 +21,17 @@ import {
|
|
|
15
21
|
envVarsToWrite,
|
|
16
22
|
installCommand,
|
|
17
23
|
readPackageJson
|
|
18
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-AIZRI665.js";
|
|
25
|
+
import {
|
|
26
|
+
MUSHI_CLI_VERSION
|
|
27
|
+
} from "./chunk-BY5SZYRO.js";
|
|
19
28
|
|
|
20
29
|
// src/init.ts
|
|
21
30
|
import * as p from "@clack/prompts";
|
|
22
31
|
import { spawn } from "child_process";
|
|
23
32
|
import { randomUUID } from "crypto";
|
|
24
|
-
import { appendFileSync, existsSync as
|
|
25
|
-
import { join as
|
|
26
|
-
|
|
27
|
-
// src/console-url.ts
|
|
28
|
-
import { existsSync, readFileSync } from "fs";
|
|
29
|
-
import { dirname, join } from "path";
|
|
30
|
-
var HOSTED_CONSOLE_BASE = "https://kensaur.us/mushi-mushi/admin";
|
|
31
|
-
var LOCAL_ADMIN_URL = "http://localhost:6464";
|
|
32
|
-
var LOCAL_PROBE_MS = 800;
|
|
33
|
-
function normalizeConsoleBase(url) {
|
|
34
|
-
const trimmed = url.trim();
|
|
35
|
-
let end = trimmed.length;
|
|
36
|
-
while (end > 0 && trimmed.charCodeAt(end - 1) === 47) end--;
|
|
37
|
-
return trimmed.slice(0, end);
|
|
38
|
-
}
|
|
39
|
-
function consoleUrl(base, route) {
|
|
40
|
-
const normalizedRoute = route.startsWith("/") ? route : `/${route}`;
|
|
41
|
-
return `${normalizeConsoleBase(base)}${normalizedRoute}`;
|
|
42
|
-
}
|
|
43
|
-
function resolveConsoleUrlFromEnv() {
|
|
44
|
-
const raw = process.env["MUSHI_CONSOLE_URL"]?.trim();
|
|
45
|
-
return raw ? normalizeConsoleBase(raw) : void 0;
|
|
46
|
-
}
|
|
47
|
-
function isInsideMushiMonorepo(cwd) {
|
|
48
|
-
let dir = cwd;
|
|
49
|
-
for (let depth = 0; depth < 14; depth += 1) {
|
|
50
|
-
const pkgPath = join(dir, "package.json");
|
|
51
|
-
if (existsSync(pkgPath)) {
|
|
52
|
-
try {
|
|
53
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
54
|
-
if (pkg.name === "mushi-mushi") return true;
|
|
55
|
-
} catch {
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const parent = dirname(dir);
|
|
59
|
-
if (parent === dir) break;
|
|
60
|
-
dir = parent;
|
|
61
|
-
}
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
async function probeLocalAdminConsole() {
|
|
65
|
-
try {
|
|
66
|
-
const controller = new AbortController();
|
|
67
|
-
const timer = setTimeout(() => controller.abort(), LOCAL_PROBE_MS);
|
|
68
|
-
const res = await fetch(LOCAL_ADMIN_URL, {
|
|
69
|
-
signal: controller.signal,
|
|
70
|
-
method: "HEAD"
|
|
71
|
-
});
|
|
72
|
-
clearTimeout(timer);
|
|
73
|
-
if (res.ok || res.status === 404 || res.status === 401) {
|
|
74
|
-
return LOCAL_ADMIN_URL;
|
|
75
|
-
}
|
|
76
|
-
} catch {
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
function resolveConsoleUrlSync(cwd = process.cwd()) {
|
|
81
|
-
const fromEnv = resolveConsoleUrlFromEnv();
|
|
82
|
-
if (fromEnv) return fromEnv;
|
|
83
|
-
const config = loadConfig();
|
|
84
|
-
if (config.consoleUrl) return normalizeConsoleBase(config.consoleUrl);
|
|
85
|
-
if (isInsideMushiMonorepo(cwd)) return LOCAL_ADMIN_URL;
|
|
86
|
-
return HOSTED_CONSOLE_BASE;
|
|
87
|
-
}
|
|
88
|
-
async function resolveConsoleUrl(options) {
|
|
89
|
-
const fromEnv = resolveConsoleUrlFromEnv();
|
|
90
|
-
if (fromEnv) return fromEnv;
|
|
91
|
-
const config = loadConfig();
|
|
92
|
-
if (config.consoleUrl) return normalizeConsoleBase(config.consoleUrl);
|
|
93
|
-
const local = await probeLocalAdminConsole();
|
|
94
|
-
if (local) return local;
|
|
95
|
-
const cwd = options?.cwd ?? process.cwd();
|
|
96
|
-
if (isInsideMushiMonorepo(cwd)) return LOCAL_ADMIN_URL;
|
|
97
|
-
return HOSTED_CONSOLE_BASE;
|
|
98
|
-
}
|
|
99
|
-
function projectIdHint(base) {
|
|
100
|
-
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).`;
|
|
101
|
-
}
|
|
102
|
-
function apiKeyHint(base) {
|
|
103
|
-
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.`;
|
|
104
|
-
}
|
|
105
|
-
function cliSetupDeepLink(base) {
|
|
106
|
-
return consoleUrl(base, "/onboarding?tab=steps&setup=cli");
|
|
107
|
-
}
|
|
108
|
-
function reportsUrl(base, reportId) {
|
|
109
|
-
return reportId ? consoleUrl(base, `/reports/${reportId}`) : consoleUrl(base, "/reports");
|
|
110
|
-
}
|
|
111
|
-
async function openInBrowser(url) {
|
|
112
|
-
let parsed;
|
|
113
|
-
try {
|
|
114
|
-
parsed = new URL(url);
|
|
115
|
-
} catch {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
const safeUrl = parsed.toString();
|
|
122
|
-
const { spawn: spawn2 } = await import("child_process");
|
|
123
|
-
const [command, args] = process.platform === "win32" ? (
|
|
124
|
-
// Open via rundll32's FileProtocolHandler rather than `cmd /c start`.
|
|
125
|
-
// `start` is a cmd builtin, so the URL would be re-parsed by cmd.exe and
|
|
126
|
-
// characters like `&` / `|` / `^` could still break out — exactly the
|
|
127
|
-
// injection surface CodeQL flags. rundll32 never invokes a shell: the URL
|
|
128
|
-
// is handed to the default browser as one opaque argument.
|
|
129
|
-
["rundll32", ["url.dll,FileProtocolHandler", safeUrl]]
|
|
130
|
-
) : process.platform === "darwin" ? ["open", [safeUrl]] : ["xdg-open", [safeUrl]];
|
|
131
|
-
await new Promise((resolve2) => {
|
|
132
|
-
try {
|
|
133
|
-
const child = spawn2(command, [...args], { stdio: "ignore", shell: false });
|
|
134
|
-
child.on("error", () => resolve2());
|
|
135
|
-
child.on("exit", () => resolve2());
|
|
136
|
-
child.unref();
|
|
137
|
-
} catch {
|
|
138
|
-
resolve2();
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// src/signals.ts
|
|
144
|
-
var activeController = null;
|
|
145
|
-
function getAbortSignal(external) {
|
|
146
|
-
if (external) return external.signal;
|
|
147
|
-
if (!activeController || activeController.signal.aborted) {
|
|
148
|
-
activeController = new AbortController();
|
|
149
|
-
}
|
|
150
|
-
return activeController.signal;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// src/cli-shared.ts
|
|
154
|
-
var API_TIMEOUT_MS = 15e3;
|
|
155
|
-
async function apiCall(path, config, options = {}) {
|
|
156
|
-
const endpoint = config.endpoint;
|
|
157
|
-
if (!endpoint) {
|
|
158
|
-
return {
|
|
159
|
-
ok: false,
|
|
160
|
-
error: {
|
|
161
|
-
code: "NO_ENDPOINT",
|
|
162
|
-
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"
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
const controller = new AbortController();
|
|
167
|
-
const timer = setTimeout(() => controller.abort(), API_TIMEOUT_MS);
|
|
168
|
-
const signals = [controller.signal, getAbortSignal()];
|
|
169
|
-
const compositeSignal = AbortSignal.any ? AbortSignal.any(signals) : controller.signal;
|
|
170
|
-
try {
|
|
171
|
-
const res = await fetch(`${endpoint}${path}`, {
|
|
172
|
-
...options,
|
|
173
|
-
signal: compositeSignal,
|
|
174
|
-
headers: {
|
|
175
|
-
"Content-Type": "application/json",
|
|
176
|
-
// `apiKeyAuth` reads X-Mushi-Api-Key; the Authorization header is a
|
|
177
|
-
// fallback accepted by some older middleware. Both are sent.
|
|
178
|
-
"Authorization": `Bearer ${config.apiKey ?? ""}`,
|
|
179
|
-
"X-Mushi-Api-Key": config.apiKey ?? "",
|
|
180
|
-
"X-Mushi-Project": config.projectId ?? "",
|
|
181
|
-
"X-Mushi-Cli-Version": MUSHI_CLI_VERSION,
|
|
182
|
-
...options.headers
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
clearTimeout(timer);
|
|
186
|
-
let body;
|
|
187
|
-
const contentType = res.headers.get("content-type") ?? "";
|
|
188
|
-
if (contentType.includes("application/json")) {
|
|
189
|
-
try {
|
|
190
|
-
body = await res.json();
|
|
191
|
-
} catch {
|
|
192
|
-
body = null;
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
const text2 = await res.text();
|
|
196
|
-
try {
|
|
197
|
-
body = JSON.parse(text2);
|
|
198
|
-
} catch {
|
|
199
|
-
body = {
|
|
200
|
-
ok: false,
|
|
201
|
-
error: {
|
|
202
|
-
code: `HTTP_${res.status}`,
|
|
203
|
-
message: text2.trim().slice(0, 300) || `HTTP ${res.status}`
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
if (!res.ok && typeof body === "object" && body !== null && !("ok" in body)) {
|
|
209
|
-
const b = body;
|
|
210
|
-
const nested = b["error"];
|
|
211
|
-
const errCode = nested?.["code"] ?? b["code"] ?? `HTTP_${res.status}`;
|
|
212
|
-
const errMsg = nested?.["message"] ?? b["message"] ?? `Request failed (${res.status})`;
|
|
213
|
-
return {
|
|
214
|
-
ok: false,
|
|
215
|
-
httpStatus: res.status,
|
|
216
|
-
error: { code: errCode, message: errMsg }
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
return body;
|
|
220
|
-
} catch (err) {
|
|
221
|
-
clearTimeout(timer);
|
|
222
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
223
|
-
return {
|
|
224
|
-
ok: false,
|
|
225
|
-
error: {
|
|
226
|
-
code: "TIMEOUT",
|
|
227
|
-
message: `Request timed out after ${API_TIMEOUT_MS / 1e3}s. Check your network or endpoint.`
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
return {
|
|
232
|
-
ok: false,
|
|
233
|
-
error: {
|
|
234
|
-
code: "NETWORK_ERROR",
|
|
235
|
-
message: err instanceof Error ? err.message : String(err)
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
}
|
|
33
|
+
import { appendFileSync, existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
34
|
+
import { join as join2 } from "path";
|
|
240
35
|
|
|
241
36
|
// src/device-auth.ts
|
|
242
37
|
var DEVICE_FETCH_TIMEOUT_MS = 15e3;
|
|
@@ -350,7 +145,11 @@ async function listProjects(endpoint, cliToken) {
|
|
|
350
145
|
const res = await deviceFetch(`${base}/v1/cli/projects`, { headers: cliAuthHeaders(cliToken) });
|
|
351
146
|
const json = await res.json().catch(() => null);
|
|
352
147
|
if (res.ok && json?.ok) return json.data?.projects ?? [];
|
|
353
|
-
} catch {
|
|
148
|
+
} catch (err) {
|
|
149
|
+
console.error(
|
|
150
|
+
"[mushi] Failed to list projects:",
|
|
151
|
+
err instanceof Error ? err.message : err
|
|
152
|
+
);
|
|
354
153
|
}
|
|
355
154
|
return [];
|
|
356
155
|
}
|
|
@@ -443,8 +242,8 @@ function parse(version) {
|
|
|
443
242
|
}
|
|
444
243
|
|
|
445
244
|
// src/monorepo.ts
|
|
446
|
-
import { existsSync
|
|
447
|
-
import { dirname
|
|
245
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
246
|
+
import { dirname, join, resolve } from "path";
|
|
448
247
|
var WORKSPACE_GLOB_CANDIDATES = ["apps/*", "packages/*", "examples/*"];
|
|
449
248
|
var FRAMEWORK_DEPS = {
|
|
450
249
|
next: "Next.js",
|
|
@@ -461,9 +260,9 @@ var FRAMEWORK_DEPS = {
|
|
|
461
260
|
function detectWorkspaceHint(cwd) {
|
|
462
261
|
const root = findWorkspaceRoot(cwd);
|
|
463
262
|
if (!root) return null;
|
|
464
|
-
const rootPkg = readPackageJsonSafely(
|
|
263
|
+
const rootPkg = readPackageJsonSafely(join(root, "package.json"));
|
|
465
264
|
if (rootPkg && getFrameworkFromPkg(rootPkg)) return null;
|
|
466
|
-
const source =
|
|
265
|
+
const source = existsSync(join(root, "pnpm-workspace.yaml")) ? "pnpm-workspace" : root === cwd ? "package-json" : "parent";
|
|
467
266
|
const apps = collectAppsFromGlobs(root);
|
|
468
267
|
if (apps.length === 0) return null;
|
|
469
268
|
return { root, apps, source };
|
|
@@ -472,15 +271,15 @@ function findWorkspaceRoot(start) {
|
|
|
472
271
|
let dir = resolve(start);
|
|
473
272
|
for (let i = 0; i < 8; i++) {
|
|
474
273
|
if (isWorkspaceRoot(dir)) return dir;
|
|
475
|
-
const parent =
|
|
274
|
+
const parent = dirname(dir);
|
|
476
275
|
if (parent === dir) break;
|
|
477
276
|
dir = parent;
|
|
478
277
|
}
|
|
479
278
|
return null;
|
|
480
279
|
}
|
|
481
280
|
function isWorkspaceRoot(dir) {
|
|
482
|
-
if (
|
|
483
|
-
const pkg = readPackageJsonSafely(
|
|
281
|
+
if (existsSync(join(dir, "pnpm-workspace.yaml"))) return true;
|
|
282
|
+
const pkg = readPackageJsonSafely(join(dir, "package.json"));
|
|
484
283
|
if (!pkg) return false;
|
|
485
284
|
return Boolean(pkg.workspaces);
|
|
486
285
|
}
|
|
@@ -488,8 +287,8 @@ function collectAppsFromGlobs(root) {
|
|
|
488
287
|
const results = [];
|
|
489
288
|
for (const glob of WORKSPACE_GLOB_CANDIDATES) {
|
|
490
289
|
const prefix = glob.replace("/*", "");
|
|
491
|
-
const parentDir =
|
|
492
|
-
if (!
|
|
290
|
+
const parentDir = join(root, prefix);
|
|
291
|
+
if (!existsSync(parentDir)) continue;
|
|
493
292
|
let entries;
|
|
494
293
|
try {
|
|
495
294
|
entries = readdirSync(parentDir);
|
|
@@ -497,7 +296,7 @@ function collectAppsFromGlobs(root) {
|
|
|
497
296
|
continue;
|
|
498
297
|
}
|
|
499
298
|
for (const entry of entries) {
|
|
500
|
-
const pkgPath =
|
|
299
|
+
const pkgPath = join(parentDir, entry, "package.json");
|
|
501
300
|
if (!isFileSafe(pkgPath)) continue;
|
|
502
301
|
const pkg = readPackageJsonSafely(pkgPath);
|
|
503
302
|
if (!pkg) continue;
|
|
@@ -515,14 +314,14 @@ function collectAppsFromGlobs(root) {
|
|
|
515
314
|
function readPackageJsonSafely(path) {
|
|
516
315
|
if (!isFileSafe(path)) return null;
|
|
517
316
|
try {
|
|
518
|
-
return JSON.parse(
|
|
317
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
519
318
|
} catch {
|
|
520
319
|
return null;
|
|
521
320
|
}
|
|
522
321
|
}
|
|
523
322
|
function isFileSafe(path) {
|
|
524
323
|
try {
|
|
525
|
-
return
|
|
324
|
+
return existsSync(path) && statSync(path).isFile();
|
|
526
325
|
} catch {
|
|
527
326
|
return false;
|
|
528
327
|
}
|
|
@@ -909,10 +708,10 @@ function runCommand(pm, packages, cwd) {
|
|
|
909
708
|
});
|
|
910
709
|
}
|
|
911
710
|
async function writeEnvFile(cwd, apiKey, projectId, framework, endpoint, overwrite) {
|
|
912
|
-
const target = ENV_FILES.find((f) =>
|
|
913
|
-
const targetPath =
|
|
711
|
+
const target = ENV_FILES.find((f) => existsSync2(join2(cwd, f))) ?? ENV_FILES[0];
|
|
712
|
+
const targetPath = join2(cwd, target);
|
|
914
713
|
const newVars = envVarsToWrite(apiKey, projectId, framework, endpoint);
|
|
915
|
-
const existing =
|
|
714
|
+
const existing = existsSync2(targetPath) ? readFileSync2(targetPath, "utf-8") : "";
|
|
916
715
|
if (existing.includes("MUSHI_PROJECT_ID")) {
|
|
917
716
|
let shouldOverwrite = overwrite;
|
|
918
717
|
if (!shouldOverwrite) {
|
|
@@ -971,12 +770,12 @@ function escapeRegexChar(ch) {
|
|
|
971
770
|
return /[-/\\^$+?.()|[\]{}]/.test(ch) ? `\\${ch}` : ch;
|
|
972
771
|
}
|
|
973
772
|
function warnIfMissingFromGitignore(cwd, envFile) {
|
|
974
|
-
const gitignorePath =
|
|
975
|
-
if (!
|
|
773
|
+
const gitignorePath = join2(cwd, ".gitignore");
|
|
774
|
+
if (!existsSync2(gitignorePath)) {
|
|
976
775
|
p.log.warn(`No .gitignore found \u2014 make sure ${envFile} is not committed.`);
|
|
977
776
|
return;
|
|
978
777
|
}
|
|
979
|
-
const content =
|
|
778
|
+
const content = readFileSync2(gitignorePath, "utf-8");
|
|
980
779
|
if (!isEnvFileCoveredByGitignore(content, envFile)) {
|
|
981
780
|
p.log.warn(`${envFile} is not in .gitignore \u2014 add it before committing.`);
|
|
982
781
|
}
|
|
@@ -1018,7 +817,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
|
|
|
1018
817
|
if (p.isCancel(answer) || !answer) return;
|
|
1019
818
|
const endpoint = resolveCloudEndpoint(options.endpoint);
|
|
1020
819
|
try {
|
|
1021
|
-
const { runConnect } = await import("./connect-
|
|
820
|
+
const { runConnect } = await import("./connect-KX4UGY6Z.js");
|
|
1022
821
|
await runConnect({
|
|
1023
822
|
apiKey: credentials.apiKey,
|
|
1024
823
|
projectId: credentials.projectId,
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mushi-mushi/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "CLI for Mushi Mushi — `mushi init` wizard installs the right SDK for your framework, plus report triage and pipeline health commands",
|
|
6
6
|
"bin": {
|
|
@@ -47,18 +47,19 @@
|
|
|
47
47
|
"sideEffects": false,
|
|
48
48
|
"keywords": [
|
|
49
49
|
"mushi-mushi",
|
|
50
|
-
"bug-
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
50
|
+
"bug-translation",
|
|
51
|
+
"vibe-coding",
|
|
52
|
+
"ai-debugging",
|
|
53
|
+
"ai-triage",
|
|
54
|
+
"auto-fix",
|
|
55
|
+
"mcp",
|
|
56
|
+
"cursor",
|
|
57
|
+
"claude-code",
|
|
58
|
+
"cli",
|
|
58
59
|
"wizard",
|
|
59
60
|
"setup",
|
|
60
|
-
"cli",
|
|
61
61
|
"init",
|
|
62
|
+
"sdk",
|
|
62
63
|
"next.js",
|
|
63
64
|
"nextjs",
|
|
64
65
|
"react",
|
|
@@ -72,10 +73,7 @@
|
|
|
72
73
|
"capacitor",
|
|
73
74
|
"sentry-companion",
|
|
74
75
|
"sentry-alternative",
|
|
75
|
-
"
|
|
76
|
-
"ai-triage",
|
|
77
|
-
"auto-fix",
|
|
78
|
-
"llm-ops"
|
|
76
|
+
"ai-agent"
|
|
79
77
|
],
|
|
80
78
|
"exports": {
|
|
81
79
|
".": {
|
package/dist/chunk-XEXYGDQV.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
// src/config.ts
|
|
2
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
3
|
-
import { homedir } from "os";
|
|
4
|
-
import { dirname, join } from "path";
|
|
5
|
-
var SECURE_FILE_MODE = 384;
|
|
6
|
-
var SECURE_DIR_MODE = 448;
|
|
7
|
-
function resolveXdgConfigPath() {
|
|
8
|
-
const xdg = process.env["XDG_CONFIG_HOME"];
|
|
9
|
-
if (xdg && xdg.length > 0) {
|
|
10
|
-
return join(xdg, "mushi", "config.json");
|
|
11
|
-
}
|
|
12
|
-
if (process.platform === "win32") {
|
|
13
|
-
const appData = process.env["APPDATA"];
|
|
14
|
-
if (appData && appData.length > 0) {
|
|
15
|
-
return join(appData, "mushi", "config.json");
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return join(homedir(), ".config", "mushi", "config.json");
|
|
19
|
-
}
|
|
20
|
-
var LEGACY_CONFIG_PATH = join(homedir(), ".mushirc");
|
|
21
|
-
var CONFIG_PATH = resolveXdgConfigPath();
|
|
22
|
-
function loadConfig(path = CONFIG_PATH) {
|
|
23
|
-
let file = {};
|
|
24
|
-
if (existsSync(path)) {
|
|
25
|
-
tightenPermissions(path);
|
|
26
|
-
try {
|
|
27
|
-
file = JSON.parse(readFileSync(path, "utf-8"));
|
|
28
|
-
} catch {
|
|
29
|
-
}
|
|
30
|
-
} else if (path === CONFIG_PATH && existsSync(LEGACY_CONFIG_PATH)) {
|
|
31
|
-
file = migrateLegacyConfig() ?? {};
|
|
32
|
-
}
|
|
33
|
-
const endpointFromEnv = process.env["MUSHI_API_ENDPOINT"] ?? process.env["MUSHI_ENDPOINT"] ?? void 0;
|
|
34
|
-
const fromEnv = {
|
|
35
|
-
...process.env["MUSHI_API_KEY"] ? { apiKey: process.env["MUSHI_API_KEY"] } : {},
|
|
36
|
-
...process.env["MUSHI_PROJECT_ID"] ? { projectId: process.env["MUSHI_PROJECT_ID"] } : {},
|
|
37
|
-
...endpointFromEnv ? { endpoint: endpointFromEnv } : {},
|
|
38
|
-
...process.env["MUSHI_CONSOLE_URL"] ? { consoleUrl: process.env["MUSHI_CONSOLE_URL"].trim() } : {}
|
|
39
|
-
};
|
|
40
|
-
return { ...file, ...fromEnv };
|
|
41
|
-
}
|
|
42
|
-
function saveConfig(config, path = CONFIG_PATH) {
|
|
43
|
-
const dir = dirname(path);
|
|
44
|
-
if (!existsSync(dir)) {
|
|
45
|
-
mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
|
|
46
|
-
} else {
|
|
47
|
-
tightenDirPermissions(dir);
|
|
48
|
-
}
|
|
49
|
-
writeFileSync(path, JSON.stringify(config, null, 2), { mode: SECURE_FILE_MODE });
|
|
50
|
-
tightenPermissions(path);
|
|
51
|
-
}
|
|
52
|
-
function migrateLegacyConfig(legacyPath = LEGACY_CONFIG_PATH, destPath = CONFIG_PATH) {
|
|
53
|
-
if (!existsSync(legacyPath)) return null;
|
|
54
|
-
let parsed;
|
|
55
|
-
try {
|
|
56
|
-
parsed = JSON.parse(readFileSync(legacyPath, "utf-8"));
|
|
57
|
-
} catch {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
const dir = dirname(destPath);
|
|
61
|
-
if (!existsSync(dir)) {
|
|
62
|
-
mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
|
|
63
|
-
}
|
|
64
|
-
try {
|
|
65
|
-
renameSync(legacyPath, destPath);
|
|
66
|
-
} catch {
|
|
67
|
-
writeFileSync(destPath, JSON.stringify(parsed, null, 2), { mode: SECURE_FILE_MODE });
|
|
68
|
-
try {
|
|
69
|
-
unlinkSync(legacyPath);
|
|
70
|
-
} catch {
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
tightenPermissions(destPath);
|
|
74
|
-
tightenDirPermissions(dir);
|
|
75
|
-
return parsed;
|
|
76
|
-
}
|
|
77
|
-
function tightenPermissions(path) {
|
|
78
|
-
if (process.platform === "win32") return;
|
|
79
|
-
try {
|
|
80
|
-
const current = statSync(path).mode & 511;
|
|
81
|
-
if (current !== SECURE_FILE_MODE) chmodSync(path, SECURE_FILE_MODE);
|
|
82
|
-
} catch {
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
function tightenDirPermissions(path) {
|
|
86
|
-
if (process.platform === "win32") return;
|
|
87
|
-
try {
|
|
88
|
-
const current = statSync(path).mode & 511;
|
|
89
|
-
if (current !== SECURE_DIR_MODE) chmodSync(path, SECURE_DIR_MODE);
|
|
90
|
-
} catch {
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// src/endpoint.ts
|
|
95
|
-
var TEST_REPORT_TIMEOUT_MS = 1e4;
|
|
96
|
-
var TEST_REPORT_FETCH_TIMEOUT_MS = TEST_REPORT_TIMEOUT_MS;
|
|
97
|
-
var CLOUD_API_ENDPOINT = "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api";
|
|
98
|
-
function resolveCloudEndpoint(explicit) {
|
|
99
|
-
if (explicit?.trim()) return explicit.trim();
|
|
100
|
-
const fromEnv = process.env.MUSHI_API_ENDPOINT?.trim();
|
|
101
|
-
if (fromEnv) return fromEnv;
|
|
102
|
-
return CLOUD_API_ENDPOINT;
|
|
103
|
-
}
|
|
104
|
-
function assertEndpoint(url) {
|
|
105
|
-
let parsed;
|
|
106
|
-
try {
|
|
107
|
-
parsed = new URL(url);
|
|
108
|
-
} catch {
|
|
109
|
-
throw new Error(`Invalid endpoint URL: ${url}`);
|
|
110
|
-
}
|
|
111
|
-
const host = parsed.hostname;
|
|
112
|
-
const isLocal = host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".local");
|
|
113
|
-
if (parsed.protocol !== "https:" && !isLocal) {
|
|
114
|
-
throw new Error(`Endpoint must use https:// (got ${parsed.protocol}//${host}).`);
|
|
115
|
-
}
|
|
116
|
-
return normalizeEndpoint(parsed.origin + parsed.pathname);
|
|
117
|
-
}
|
|
118
|
-
function normalizeEndpoint(url) {
|
|
119
|
-
if (!url) {
|
|
120
|
-
throw new Error(
|
|
121
|
-
"No API endpoint configured. Run `mushi init` or set MUSHI_API_ENDPOINT. Set endpoint to your Supabase edge function URL, e.g. https://xyz.supabase.co/functions/v1/api"
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
let end = url.length;
|
|
125
|
-
while (end > 0 && url.charCodeAt(end - 1) === 47) end--;
|
|
126
|
-
return url.slice(0, end);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export {
|
|
130
|
-
CONFIG_PATH,
|
|
131
|
-
loadConfig,
|
|
132
|
-
saveConfig,
|
|
133
|
-
TEST_REPORT_FETCH_TIMEOUT_MS,
|
|
134
|
-
resolveCloudEndpoint,
|
|
135
|
-
assertEndpoint,
|
|
136
|
-
normalizeEndpoint
|
|
137
|
-
};
|