@mushi-mushi/cli 0.7.0 → 0.10.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/CONTRIBUTING.md +11 -0
- package/README.md +402 -69
- package/dist/chunk-GHDS4VGP.js +6 -0
- package/dist/index.js +863 -188
- package/dist/init.js +84 -20
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-LBQX6RYS.js +0 -6
package/dist/init.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./chunk-XHD3H54W.js";
|
|
9
9
|
import {
|
|
10
10
|
MUSHI_CLI_VERSION
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-GHDS4VGP.js";
|
|
12
12
|
|
|
13
13
|
// src/init.ts
|
|
14
14
|
import * as p from "@clack/prompts";
|
|
@@ -18,24 +18,79 @@ import { appendFileSync, existsSync as existsSync3, readFileSync as readFileSync
|
|
|
18
18
|
import { join as join3 } from "path";
|
|
19
19
|
|
|
20
20
|
// src/config.ts
|
|
21
|
-
import { chmodSync, readFileSync, statSync,
|
|
22
|
-
import { join } from "path";
|
|
21
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
23
22
|
import { homedir } from "os";
|
|
24
|
-
|
|
23
|
+
import { dirname, join } from "path";
|
|
25
24
|
var SECURE_FILE_MODE = 384;
|
|
25
|
+
var SECURE_DIR_MODE = 448;
|
|
26
|
+
function resolveXdgConfigPath() {
|
|
27
|
+
const xdg = process.env["XDG_CONFIG_HOME"];
|
|
28
|
+
if (xdg && xdg.length > 0) {
|
|
29
|
+
return join(xdg, "mushi", "config.json");
|
|
30
|
+
}
|
|
31
|
+
if (process.platform === "win32") {
|
|
32
|
+
const appData = process.env["APPDATA"];
|
|
33
|
+
if (appData && appData.length > 0) {
|
|
34
|
+
return join(appData, "mushi", "config.json");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return join(homedir(), ".config", "mushi", "config.json");
|
|
38
|
+
}
|
|
39
|
+
var LEGACY_CONFIG_PATH = join(homedir(), ".mushirc");
|
|
40
|
+
var CONFIG_PATH = resolveXdgConfigPath();
|
|
26
41
|
function loadConfig(path = CONFIG_PATH) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
let file = {};
|
|
43
|
+
if (existsSync(path)) {
|
|
44
|
+
tightenPermissions(path);
|
|
45
|
+
try {
|
|
46
|
+
file = JSON.parse(readFileSync(path, "utf-8"));
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
} else if (path === CONFIG_PATH && existsSync(LEGACY_CONFIG_PATH)) {
|
|
50
|
+
file = migrateLegacyConfig() ?? {};
|
|
33
51
|
}
|
|
52
|
+
const fromEnv = {
|
|
53
|
+
...process.env["MUSHI_API_KEY"] ? { apiKey: process.env["MUSHI_API_KEY"] } : {},
|
|
54
|
+
...process.env["MUSHI_PROJECT_ID"] ? { projectId: process.env["MUSHI_PROJECT_ID"] } : {},
|
|
55
|
+
...process.env["MUSHI_API_ENDPOINT"] ? { endpoint: process.env["MUSHI_API_ENDPOINT"] } : {}
|
|
56
|
+
};
|
|
57
|
+
return { ...file, ...fromEnv };
|
|
34
58
|
}
|
|
35
59
|
function saveConfig(config, path = CONFIG_PATH) {
|
|
60
|
+
const dir = dirname(path);
|
|
61
|
+
if (!existsSync(dir)) {
|
|
62
|
+
mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
|
|
63
|
+
} else {
|
|
64
|
+
tightenDirPermissions(dir);
|
|
65
|
+
}
|
|
36
66
|
writeFileSync(path, JSON.stringify(config, null, 2), { mode: SECURE_FILE_MODE });
|
|
37
67
|
tightenPermissions(path);
|
|
38
68
|
}
|
|
69
|
+
function migrateLegacyConfig(legacyPath = LEGACY_CONFIG_PATH, destPath = CONFIG_PATH) {
|
|
70
|
+
if (!existsSync(legacyPath)) return null;
|
|
71
|
+
let parsed;
|
|
72
|
+
try {
|
|
73
|
+
parsed = JSON.parse(readFileSync(legacyPath, "utf-8"));
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const dir = dirname(destPath);
|
|
78
|
+
if (!existsSync(dir)) {
|
|
79
|
+
mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
renameSync(legacyPath, destPath);
|
|
83
|
+
} catch {
|
|
84
|
+
writeFileSync(destPath, JSON.stringify(parsed, null, 2), { mode: SECURE_FILE_MODE });
|
|
85
|
+
try {
|
|
86
|
+
unlinkSync(legacyPath);
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
tightenPermissions(destPath);
|
|
91
|
+
tightenDirPermissions(dir);
|
|
92
|
+
return parsed;
|
|
93
|
+
}
|
|
39
94
|
function tightenPermissions(path) {
|
|
40
95
|
if (process.platform === "win32") return;
|
|
41
96
|
try {
|
|
@@ -44,6 +99,14 @@ function tightenPermissions(path) {
|
|
|
44
99
|
} catch {
|
|
45
100
|
}
|
|
46
101
|
}
|
|
102
|
+
function tightenDirPermissions(path) {
|
|
103
|
+
if (process.platform === "win32") return;
|
|
104
|
+
try {
|
|
105
|
+
const current = statSync(path).mode & 511;
|
|
106
|
+
if (current !== SECURE_DIR_MODE) chmodSync(path, SECURE_DIR_MODE);
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
47
110
|
|
|
48
111
|
// src/endpoint.ts
|
|
49
112
|
var TEST_REPORT_TIMEOUT_MS = 1e4;
|
|
@@ -118,7 +181,7 @@ function parse(version) {
|
|
|
118
181
|
|
|
119
182
|
// src/monorepo.ts
|
|
120
183
|
import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync, statSync as statSync2 } from "fs";
|
|
121
|
-
import { dirname, join as join2, resolve } from "path";
|
|
184
|
+
import { dirname as dirname2, join as join2, resolve } from "path";
|
|
122
185
|
var WORKSPACE_GLOB_CANDIDATES = ["apps/*", "packages/*", "examples/*"];
|
|
123
186
|
var FRAMEWORK_DEPS = {
|
|
124
187
|
next: "Next.js",
|
|
@@ -146,7 +209,7 @@ function findWorkspaceRoot(start) {
|
|
|
146
209
|
let dir = resolve(start);
|
|
147
210
|
for (let i = 0; i < 8; i++) {
|
|
148
211
|
if (isWorkspaceRoot(dir)) return dir;
|
|
149
|
-
const parent =
|
|
212
|
+
const parent = dirname2(dir);
|
|
150
213
|
if (parent === dir) break;
|
|
151
214
|
dir = parent;
|
|
152
215
|
}
|
|
@@ -215,7 +278,7 @@ function getFrameworkFromPkg(pkg) {
|
|
|
215
278
|
|
|
216
279
|
// src/init.ts
|
|
217
280
|
var ENV_FILES = [".env.local", ".env"];
|
|
218
|
-
var PROJECT_ID_PATTERN = /^proj_[A-Za-z0-9_-]{10,}
|
|
281
|
+
var PROJECT_ID_PATTERN = /^(?:proj_[A-Za-z0-9_-]{10,}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
|
|
219
282
|
var API_KEY_PATTERN = /^mushi_[A-Za-z0-9_-]{10,}$/;
|
|
220
283
|
async function runInit(options = {}) {
|
|
221
284
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -260,7 +323,7 @@ function ensureInteractiveOrBailOut(options) {
|
|
|
260
323
|
);
|
|
261
324
|
if (hasAllFlags) return;
|
|
262
325
|
process.stderr.write(
|
|
263
|
-
"mushi-mushi: non-interactive terminal detected.\nPass all of --yes (or --framework), --project-id, and --api-key to run unattended.\nExample: npx mushi-mushi --yes --project-id
|
|
326
|
+
"mushi-mushi: non-interactive terminal detected.\nPass all of --yes (or --framework), --project-id, and --api-key to run unattended.\nExample: npx mushi-mushi --yes --project-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --api-key mushi_xxx\nYour project ID is the UUID shown in the Projects page of the Mushi admin console.\n"
|
|
264
327
|
);
|
|
265
328
|
process.exit(1);
|
|
266
329
|
}
|
|
@@ -294,21 +357,22 @@ async function collectCredentials(options) {
|
|
|
294
357
|
const existing = loadConfig();
|
|
295
358
|
const rawProjectId = options.projectId ?? existing.projectId ?? await promptText({
|
|
296
359
|
message: "Project ID",
|
|
297
|
-
placeholder: "
|
|
298
|
-
hint: "
|
|
299
|
-
validate: (v) => PROJECT_ID_PATTERN.test(v) ? void 0 : "Expected
|
|
360
|
+
placeholder: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
361
|
+
hint: "Where to find it: https://kensaur.us/mushi-mushi/projects \u2192 click your project \u2192 copy the UUID below the project name.",
|
|
362
|
+
validate: (v) => PROJECT_ID_PATTERN.test(v) ? void 0 : "Expected a UUID (xxxxxxxx-xxxx-...) \u2014 copy it from the Mushi admin console Projects page."
|
|
300
363
|
});
|
|
301
364
|
const rawApiKey = options.apiKey ?? existing.apiKey ?? await promptText({
|
|
302
365
|
message: "API key",
|
|
303
366
|
placeholder: "mushi_xxxxxxxxxxxx",
|
|
304
|
-
hint: "Treat
|
|
367
|
+
hint: "Where to find it: https://kensaur.us/mushi-mushi/settings \u2192 API Keys tab. Treat it like a password \u2014 env file only, never commit it.",
|
|
305
368
|
validate: (v) => API_KEY_PATTERN.test(v) ? void 0 : "Expected format: mushi_ followed by 10+ alphanumeric characters"
|
|
306
369
|
});
|
|
307
370
|
const projectId = sanitizeSecret(rawProjectId);
|
|
308
371
|
const apiKey = sanitizeSecret(rawApiKey);
|
|
309
372
|
if (!PROJECT_ID_PATTERN.test(projectId)) {
|
|
310
373
|
throw new Error(
|
|
311
|
-
`Invalid project ID.
|
|
374
|
+
`Invalid project ID. Got: ${redact(projectId)}
|
|
375
|
+
Expected a UUID (e.g. 542b34e0-019e-41fe-b900-7b637717bb86) \u2014 copy it from the Projects page in the Mushi console at https://kensaur.us/mushi-mushi/projects`
|
|
312
376
|
);
|
|
313
377
|
}
|
|
314
378
|
if (!API_KEY_PATTERN.test(apiKey)) {
|
|
@@ -326,6 +390,7 @@ function redact(value) {
|
|
|
326
390
|
return `${value.slice(0, 4)}\u2026${value.slice(-2)}`;
|
|
327
391
|
}
|
|
328
392
|
async function promptText(opts) {
|
|
393
|
+
if (opts.hint) p.log.info(opts.hint);
|
|
329
394
|
const value = await p.text({
|
|
330
395
|
message: opts.message,
|
|
331
396
|
placeholder: opts.placeholder,
|
|
@@ -342,7 +407,6 @@ async function promptText(opts) {
|
|
|
342
407
|
p.cancel("Aborted.");
|
|
343
408
|
process.exit(0);
|
|
344
409
|
}
|
|
345
|
-
if (opts.hint) p.log.info(opts.hint);
|
|
346
410
|
return value;
|
|
347
411
|
}
|
|
348
412
|
async function installPackages(pm, packages, cwd) {
|
package/dist/version.js
CHANGED
package/package.json
CHANGED