@mushi-mushi/cli 0.20.0 → 0.22.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 +14 -2
- package/README.md +11 -10
- package/SECURITY.md +1 -1
- package/dist/chunk-5KN6S63C.js +6 -0
- package/dist/{chunk-7Q44KWGN.js → chunk-JAUKBLPK.js} +15 -9
- package/dist/{chunk-D5IDNBJO.js → chunk-R2HPR2FV.js} +15 -9
- package/dist/{connect-IC7I76Z2.js → connect-2RM43XHO.js} +1 -1
- package/dist/{connect-W2Q6N7K3.js → connect-JIEIWTF3.js} +1 -1
- package/dist/detect.d.ts +1 -1
- package/dist/detect.js +1 -1
- package/dist/index.js +1266 -792
- package/dist/init.js +107 -30
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-6LM2KW4S.js +0 -6
package/dist/init.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MUSHI_CLI_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5KN6S63C.js";
|
|
4
4
|
import {
|
|
5
5
|
TEST_REPORT_FETCH_TIMEOUT_MS,
|
|
6
6
|
loadConfig,
|
|
@@ -15,13 +15,13 @@ import {
|
|
|
15
15
|
envVarsToWrite,
|
|
16
16
|
installCommand,
|
|
17
17
|
readPackageJson
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-JAUKBLPK.js";
|
|
19
19
|
|
|
20
20
|
// src/init.ts
|
|
21
21
|
import * as p from "@clack/prompts";
|
|
22
22
|
import { spawn } from "child_process";
|
|
23
23
|
import { randomUUID } from "crypto";
|
|
24
|
-
import { appendFileSync, existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
24
|
+
import { appendFileSync, existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
25
25
|
import { join as join3 } from "path";
|
|
26
26
|
|
|
27
27
|
// src/console-url.ts
|
|
@@ -207,13 +207,13 @@ async function apiCall(path, config, options = {}) {
|
|
|
207
207
|
}
|
|
208
208
|
if (!res.ok && typeof body === "object" && body !== null && !("ok" in body)) {
|
|
209
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})`;
|
|
210
213
|
return {
|
|
211
214
|
ok: false,
|
|
212
215
|
httpStatus: res.status,
|
|
213
|
-
error: {
|
|
214
|
-
code: b["code"] ?? `HTTP_${res.status}`,
|
|
215
|
-
message: b["message"] ?? `Request failed (${res.status})`
|
|
216
|
-
}
|
|
216
|
+
error: { code: errCode, message: errMsg }
|
|
217
217
|
};
|
|
218
218
|
}
|
|
219
219
|
return body;
|
|
@@ -306,8 +306,12 @@ async function waitForCliToken(endpoint, session, opts = {}) {
|
|
|
306
306
|
const deadline = now() + (session.expires_in || 600) * 1e3;
|
|
307
307
|
const maxConsecutiveErrors = opts.maxConsecutiveErrors ?? 5;
|
|
308
308
|
let consecutiveErrors = 0;
|
|
309
|
+
let firstPoll = true;
|
|
309
310
|
while (now() < deadline) {
|
|
310
|
-
|
|
311
|
+
if (!firstPoll) {
|
|
312
|
+
await sleep(intervalMs);
|
|
313
|
+
}
|
|
314
|
+
firstPoll = false;
|
|
311
315
|
const outcome = await pollDeviceToken(endpoint, session.device_code);
|
|
312
316
|
switch (outcome.status) {
|
|
313
317
|
case "approved":
|
|
@@ -535,6 +539,33 @@ function getFrameworkFromPkg(pkg) {
|
|
|
535
539
|
return void 0;
|
|
536
540
|
}
|
|
537
541
|
|
|
542
|
+
// src/auth-ui.ts
|
|
543
|
+
var LINE = "\u2500".repeat(58);
|
|
544
|
+
function printAuthBanner(userCode, verificationUri) {
|
|
545
|
+
console.log("");
|
|
546
|
+
console.log(` \u250C${LINE}\u2510`);
|
|
547
|
+
console.log(` \u2502 Browser sign-in \u2014 follow these 3 steps \u2502`);
|
|
548
|
+
console.log(` \u251C${LINE}\u2524`);
|
|
549
|
+
console.log(` \u2502 \u2502`);
|
|
550
|
+
console.log(` \u2502 1. A browser tab will open (or visit the URL below) \u2502`);
|
|
551
|
+
console.log(` \u2502 2. Sign in if prompted, then click "Approve" \u2502`);
|
|
552
|
+
console.log(` \u2502 3. Come back here \u2014 setup continues automatically \u2502`);
|
|
553
|
+
console.log(` \u2502 \u2502`);
|
|
554
|
+
console.log(` \u251C${LINE}\u2524`);
|
|
555
|
+
console.log(` \u2502 Verification code (show in browser \u2014 NOT terminal): \u2502`);
|
|
556
|
+
console.log(` \u2502 \u2502`);
|
|
557
|
+
console.log(` \u2502 ${userCode.padEnd(52)}\u2502`);
|
|
558
|
+
console.log(` \u2502 \u2502`);
|
|
559
|
+
console.log(` \u2502 \u26A0 Do NOT paste or type this code in your terminal \u2502`);
|
|
560
|
+
console.log(` \u2514${LINE}\u2518`);
|
|
561
|
+
console.log("");
|
|
562
|
+
console.log(` If the browser didn't open, visit this URL and approve:`);
|
|
563
|
+
console.log(` ${verificationUri}`);
|
|
564
|
+
console.log("");
|
|
565
|
+
console.log(" \u23F3 Waiting for browser approval\u2026 (Ctrl+C to cancel)");
|
|
566
|
+
console.log("");
|
|
567
|
+
}
|
|
568
|
+
|
|
538
569
|
// src/init.ts
|
|
539
570
|
var ENV_FILES = [".env.local", ".env"];
|
|
540
571
|
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;
|
|
@@ -542,7 +573,7 @@ var API_KEY_PATTERN = /^(mushi_|mush_pk_)[A-Za-z0-9_-]{10,}$/;
|
|
|
542
573
|
async function runInit(options = {}) {
|
|
543
574
|
const cwd = options.cwd ?? process.cwd();
|
|
544
575
|
ensureInteractiveOrBailOut(options);
|
|
545
|
-
p.intro("
|
|
576
|
+
p.intro("Mushi setup");
|
|
546
577
|
await printFreshnessHint();
|
|
547
578
|
warnIfWorkspaceRoot(cwd);
|
|
548
579
|
const pkg = readPackageJson(cwd);
|
|
@@ -570,13 +601,13 @@ async function runInit(options = {}) {
|
|
|
570
601
|
} else {
|
|
571
602
|
p.log.info(`Skipped install. Run \`${installCommand(pm, packagesToInstall)}\` yourself.`);
|
|
572
603
|
}
|
|
573
|
-
writeEnvFile(cwd, credentials.apiKey, credentials.projectId, framework);
|
|
604
|
+
await writeEnvFile(cwd, credentials.apiKey, credentials.projectId, framework, endpoint, Boolean(options.yes));
|
|
574
605
|
persistCliConfig(credentials.apiKey, credentials.projectId, endpoint);
|
|
575
606
|
const enableRewards = await maybeEnableRewards(options);
|
|
576
607
|
printNextSteps(framework, consoleBase, enableRewards);
|
|
577
608
|
await maybeSendTestReport(credentials, { ...options, endpoint, consoleBase });
|
|
578
609
|
await maybeOfferConnect(credentials, options, consoleBase);
|
|
579
|
-
p.outro("Setup complete.
|
|
610
|
+
p.outro("Setup complete.");
|
|
580
611
|
}
|
|
581
612
|
function ensureInteractiveOrBailOut(options) {
|
|
582
613
|
const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
@@ -641,7 +672,7 @@ async function acquireCredentials(options, consoleBase, endpoint) {
|
|
|
641
672
|
}
|
|
642
673
|
}
|
|
643
674
|
if (options.yes) {
|
|
644
|
-
const creds = await runBrowserSignIn(options, endpoint);
|
|
675
|
+
const creds = await runBrowserSignIn(options, endpoint, consoleBase);
|
|
645
676
|
if (creds) return creds;
|
|
646
677
|
p.log.warn("Browser sign-in didn't complete \u2014 switching to manual entry.");
|
|
647
678
|
} else {
|
|
@@ -657,7 +688,7 @@ async function acquireCredentials(options, consoleBase, endpoint) {
|
|
|
657
688
|
{
|
|
658
689
|
value: "manual",
|
|
659
690
|
label: "Paste a Project ID + API key",
|
|
660
|
-
hint: "Self-hosted or expert setup"
|
|
691
|
+
hint: "Self-hosted or expert setup \u2014 run npx mushi-mushi (not mushi setup)"
|
|
661
692
|
}
|
|
662
693
|
]
|
|
663
694
|
});
|
|
@@ -666,14 +697,14 @@ async function acquireCredentials(options, consoleBase, endpoint) {
|
|
|
666
697
|
process.exit(0);
|
|
667
698
|
}
|
|
668
699
|
if (method === "browser") {
|
|
669
|
-
const creds = await runBrowserSignIn(options, endpoint);
|
|
700
|
+
const creds = await runBrowserSignIn(options, endpoint, consoleBase);
|
|
670
701
|
if (creds) return creds;
|
|
671
702
|
p.log.warn("Browser sign-in didn't complete \u2014 switching to manual entry.");
|
|
672
703
|
}
|
|
673
704
|
}
|
|
674
705
|
return collectCredentialsManually(options, consoleBase);
|
|
675
706
|
}
|
|
676
|
-
async function runBrowserSignIn(options, endpoint) {
|
|
707
|
+
async function runBrowserSignIn(options, endpoint, consoleBase) {
|
|
677
708
|
const startSpin = p.spinner();
|
|
678
709
|
startSpin.start("Starting secure browser sign-in\u2026");
|
|
679
710
|
let session;
|
|
@@ -685,13 +716,11 @@ async function runBrowserSignIn(options, endpoint) {
|
|
|
685
716
|
return null;
|
|
686
717
|
}
|
|
687
718
|
startSpin.stop("Browser sign-in ready.");
|
|
688
|
-
p.log.step(`Confirmation code: ${session.user_code}`);
|
|
689
|
-
p.log.info(`Opening ${session.verification_uri}`);
|
|
690
719
|
try {
|
|
691
720
|
await openInBrowser(session.verification_uri);
|
|
692
721
|
} catch {
|
|
693
722
|
}
|
|
694
|
-
|
|
723
|
+
printAuthBanner(session.user_code, session.verification_uri);
|
|
695
724
|
const waitSpin = p.spinner();
|
|
696
725
|
waitSpin.start("Waiting for you to approve in the browser\u2026");
|
|
697
726
|
let cliToken;
|
|
@@ -707,9 +736,14 @@ async function runBrowserSignIn(options, endpoint) {
|
|
|
707
736
|
let apiKey;
|
|
708
737
|
if (!projectId) {
|
|
709
738
|
let projects = [];
|
|
739
|
+
const fetchSpin = p.spinner();
|
|
740
|
+
fetchSpin.start("Loading your projects\u2026");
|
|
710
741
|
try {
|
|
711
742
|
projects = await listProjects(endpoint, cliToken);
|
|
712
|
-
|
|
743
|
+
fetchSpin.stop(projects.length > 0 ? `Found ${projects.length} project(s).` : "No projects yet.");
|
|
744
|
+
} catch (err) {
|
|
745
|
+
fetchSpin.stop("Could not load projects \u2014 you can still create a new one.");
|
|
746
|
+
p.log.warn(err instanceof Error ? err.message : String(err));
|
|
713
747
|
}
|
|
714
748
|
const NEW = "__new__";
|
|
715
749
|
const choice = await p.select({
|
|
@@ -751,7 +785,24 @@ async function runBrowserSignIn(options, endpoint) {
|
|
|
751
785
|
}
|
|
752
786
|
}
|
|
753
787
|
if (projectId && !apiKey) {
|
|
754
|
-
|
|
788
|
+
const keySpin = p.spinner();
|
|
789
|
+
keySpin.start("Minting SDK key\u2026");
|
|
790
|
+
try {
|
|
791
|
+
const minted = await mintProjectKey(endpoint, cliToken, projectId);
|
|
792
|
+
apiKey = minted ?? void 0;
|
|
793
|
+
if (!apiKey) {
|
|
794
|
+
keySpin.stop("Could not mint an API key.");
|
|
795
|
+
p.log.warn(
|
|
796
|
+
`Open the console Verify tab to generate a key manually: ${consoleUrl(consoleBase, "/onboarding?tab=verify")}`
|
|
797
|
+
);
|
|
798
|
+
return null;
|
|
799
|
+
}
|
|
800
|
+
keySpin.stop("SDK key ready.");
|
|
801
|
+
} catch (err) {
|
|
802
|
+
keySpin.stop("Could not mint an API key.");
|
|
803
|
+
p.log.warn(err instanceof Error ? err.message : String(err));
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
755
806
|
}
|
|
756
807
|
if (!projectId || !apiKey) return null;
|
|
757
808
|
return { apiKey, projectId };
|
|
@@ -857,13 +908,37 @@ function runCommand(pm, packages, cwd) {
|
|
|
857
908
|
});
|
|
858
909
|
});
|
|
859
910
|
}
|
|
860
|
-
function writeEnvFile(cwd, apiKey, projectId, framework) {
|
|
911
|
+
async function writeEnvFile(cwd, apiKey, projectId, framework, endpoint, overwrite) {
|
|
861
912
|
const target = ENV_FILES.find((f) => existsSync3(join3(cwd, f))) ?? ENV_FILES[0];
|
|
862
913
|
const targetPath = join3(cwd, target);
|
|
863
|
-
const newVars = envVarsToWrite(apiKey, projectId, framework);
|
|
914
|
+
const newVars = envVarsToWrite(apiKey, projectId, framework, endpoint);
|
|
864
915
|
const existing = existsSync3(targetPath) ? readFileSync3(targetPath, "utf-8") : "";
|
|
865
916
|
if (existing.includes("MUSHI_PROJECT_ID")) {
|
|
866
|
-
|
|
917
|
+
let shouldOverwrite = overwrite;
|
|
918
|
+
if (!shouldOverwrite) {
|
|
919
|
+
const answer = await p.confirm({
|
|
920
|
+
message: `Existing MUSHI_* vars found in ${target}. Update with new credentials?`,
|
|
921
|
+
initialValue: true
|
|
922
|
+
});
|
|
923
|
+
if (p.isCancel(answer)) {
|
|
924
|
+
p.log.info(`Kept existing env vars in ${target}.`);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
shouldOverwrite = Boolean(answer);
|
|
928
|
+
}
|
|
929
|
+
if (!shouldOverwrite) {
|
|
930
|
+
p.log.info(`Kept existing env vars in ${target}. Re-run and confirm to overwrite.`);
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
const MUSHI_LINE_RE = /^(NEXT_PUBLIC_|NUXT_PUBLIC_|VITE_|EXPO_PUBLIC_)?MUSHI_[A-Z_]+=.*/gm;
|
|
934
|
+
const stripped = existing.replace(MUSHI_LINE_RE, "").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
935
|
+
const prefix2 = stripped.length > 0 ? "\n" : "";
|
|
936
|
+
writeFileSync(targetPath, `${stripped}${prefix2}
|
|
937
|
+
# Mushi Mushi
|
|
938
|
+
${newVars}
|
|
939
|
+
`);
|
|
940
|
+
p.log.success(`Updated MUSHI_* env vars in ${target}`);
|
|
941
|
+
warnIfMissingFromGitignore(cwd, target);
|
|
867
942
|
return;
|
|
868
943
|
}
|
|
869
944
|
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
@@ -924,12 +999,14 @@ import { MushiRewardsBadge } from '@mushi-mushi/react';
|
|
|
924
999
|
p.log.info(`Enable rewards in your project settings at ${consoleUrl(consoleBase, "/rewards")}`);
|
|
925
1000
|
p.log.info("Users will earn points for bug reports, screen navigation, and app activity.");
|
|
926
1001
|
}
|
|
927
|
-
p.
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
1002
|
+
p.note(
|
|
1003
|
+
[
|
|
1004
|
+
" [ ] 1. Paste the init snippet above into your app entry file",
|
|
1005
|
+
" [ ] 2. Start your dev server",
|
|
1006
|
+
" [ ] 3. Run: mushi connect --write-env --wire-ide --wait",
|
|
1007
|
+
` [ ] 4. Open the Verify tab to send a test report: ${consoleUrl(consoleBase, "/onboarding?tab=verify")}`
|
|
1008
|
+
].join("\n"),
|
|
1009
|
+
"Next steps:"
|
|
933
1010
|
);
|
|
934
1011
|
}
|
|
935
1012
|
async function maybeOfferConnect(credentials, options, _consoleBase) {
|
|
@@ -941,7 +1018,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
|
|
|
941
1018
|
if (p.isCancel(answer) || !answer) return;
|
|
942
1019
|
const endpoint = resolveCloudEndpoint(options.endpoint);
|
|
943
1020
|
try {
|
|
944
|
-
const { runConnect } = await import("./connect-
|
|
1021
|
+
const { runConnect } = await import("./connect-2RM43XHO.js");
|
|
945
1022
|
await runConnect({
|
|
946
1023
|
apiKey: credentials.apiKey,
|
|
947
1024
|
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.22.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": {
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
"vitest": "^4.1.8",
|
|
19
19
|
"@mushi-mushi/eslint-config": "0.0.0"
|
|
20
20
|
},
|
|
21
|
-
"author": "Kenji Sakuramoto",
|
|
21
|
+
"author": "Kenji Sakuramoto (https://bsky.app/profile/mushimushi.dev)",
|
|
22
22
|
"repository": {
|
|
23
23
|
"type": "git",
|
|
24
24
|
"url": "https://github.com/kensaurus/mushi-mushi.git",
|
|
25
25
|
"directory": "packages/cli"
|
|
26
26
|
},
|
|
27
|
-
"homepage": "https://
|
|
27
|
+
"homepage": "https://kensaur.us/mushi-mushi",
|
|
28
28
|
"bugs": {
|
|
29
29
|
"url": "https://github.com/kensaurus/mushi-mushi/issues"
|
|
30
30
|
},
|