@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/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-LBQX6RYS.js";
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, writeFileSync, existsSync } from "fs";
22
- import { join } from "path";
21
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
23
22
  import { homedir } from "os";
24
- var CONFIG_PATH = join(homedir(), ".mushirc");
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
- if (!existsSync(path)) return {};
28
- tightenPermissions(path);
29
- try {
30
- return JSON.parse(readFileSync(path, "utf-8"));
31
- } catch {
32
- return {};
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 = dirname(dir);
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 proj_xxx --api-key mushi_xxx\n"
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: "proj_xxxxxxxxxxxx",
298
- hint: "Find this at https://kensaur.us/mushi-mushi/projects",
299
- validate: (v) => PROJECT_ID_PATTERN.test(v) ? void 0 : "Expected format: proj_ followed by 10+ alphanumeric characters"
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 this like a password \u2014 it goes in your env file, not in source.",
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. Expected format: proj_[A-Za-z0-9_-]{10,}. Got: ${redact(projectId)}`
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
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MUSHI_CLI_VERSION
3
- } from "./chunk-LBQX6RYS.js";
3
+ } from "./chunk-GHDS4VGP.js";
4
4
  export {
5
5
  MUSHI_CLI_VERSION
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mushi-mushi/cli",
3
- "version": "0.7.0",
3
+ "version": "0.10.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": {
@@ -1,6 +0,0 @@
1
- // src/version.ts
2
- var MUSHI_CLI_VERSION = true ? "0.7.0" : "0.0.0-dev";
3
-
4
- export {
5
- MUSHI_CLI_VERSION
6
- };