@mushi-mushi/cli 0.24.0 → 0.25.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/{chunk-GUVGTFVK.js → chunk-2WHQONHK.js} +1 -1
- package/dist/{chunk-BWASDQI6.js → chunk-3JRQN6S4.js} +1 -1
- package/dist/chunk-SS42XQ57.js +6 -0
- package/dist/{connect-KKN62YK2.js → connect-22IHGT2L.js} +1 -1
- package/dist/{connect-7LTQRBEW.js → connect-XFWRSEMW.js} +2 -2
- package/dist/index.js +212 -205
- package/dist/init.js +20 -22
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-X74T7JKC.js +0 -6
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
normalizeEndpoint,
|
|
5
5
|
requireUuid,
|
|
6
6
|
saveConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2WHQONHK.js";
|
|
8
8
|
import {
|
|
9
9
|
detectFramework,
|
|
10
10
|
envVarsToWrite,
|
|
11
11
|
readPackageJson
|
|
12
12
|
} from "./chunk-AIZRI665.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-SS42XQ57.js";
|
|
14
14
|
|
|
15
15
|
// src/connect.ts
|
|
16
16
|
import { appendFile, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
sanitizeProjectId,
|
|
38
38
|
saveConfig,
|
|
39
39
|
writeMcpServerEntry
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-3JRQN6S4.js";
|
|
41
41
|
import {
|
|
42
42
|
TEST_REPORT_FETCH_TIMEOUT_MS,
|
|
43
43
|
assertEndpoint,
|
|
@@ -245,28 +245,26 @@ async function createProject(endpoint, cliToken, name) {
|
|
|
245
245
|
}
|
|
246
246
|
async function mintProjectKey(endpoint, cliToken, projectId) {
|
|
247
247
|
const base = trimTrailingSlash(endpoint);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
} catch (err) {
|
|
257
|
-
throw new DeviceAuthRequestError(
|
|
258
|
-
"mint_key",
|
|
259
|
-
`Could not reach ${base}/v1/cli/projects/\u2026/keys: ${err instanceof Error ? err.message : String(err)}`
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
const json = await res.json().catch(() => null);
|
|
263
|
-
if (res.ok && json?.ok && json.data?.key) return json.data.key;
|
|
248
|
+
let res;
|
|
249
|
+
try {
|
|
250
|
+
res = await deviceFetch(`${base}/v1/cli/projects/${projectId}/keys`, {
|
|
251
|
+
method: "POST",
|
|
252
|
+
headers: cliAuthHeaders(cliToken),
|
|
253
|
+
body: JSON.stringify({})
|
|
254
|
+
});
|
|
255
|
+
} catch (err) {
|
|
264
256
|
throw new DeviceAuthRequestError(
|
|
265
257
|
"mint_key",
|
|
266
|
-
|
|
267
|
-
res.ok ? void 0 : res.status
|
|
258
|
+
`Could not reach ${base}/v1/cli/projects/\u2026/keys: ${err instanceof Error ? err.message : String(err)}`
|
|
268
259
|
);
|
|
269
|
-
}
|
|
260
|
+
}
|
|
261
|
+
const json = await res.json().catch(() => null);
|
|
262
|
+
if (res.ok && json?.ok && json.data?.key) return json.data.key;
|
|
263
|
+
throw new DeviceAuthRequestError(
|
|
264
|
+
"mint_key",
|
|
265
|
+
json?.error?.message ?? `Could not mint an SDK key (HTTP ${res.status}).`,
|
|
266
|
+
res.status
|
|
267
|
+
);
|
|
270
268
|
}
|
|
271
269
|
|
|
272
270
|
// src/freshness.ts
|
|
@@ -984,7 +982,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
|
|
|
984
982
|
if (p.isCancel(answer) || !answer) return;
|
|
985
983
|
const endpoint = resolveCloudEndpoint(options.endpoint);
|
|
986
984
|
try {
|
|
987
|
-
const { runConnect: runConnect2 } = await import("./connect-
|
|
985
|
+
const { runConnect: runConnect2 } = await import("./connect-22IHGT2L.js");
|
|
988
986
|
await runConnect2({
|
|
989
987
|
apiKey: credentials.apiKey,
|
|
990
988
|
projectId: credentials.projectId,
|
|
@@ -1273,189 +1271,7 @@ Examples:
|
|
|
1273
1271
|
mushi login # browser-guided (recommended)
|
|
1274
1272
|
mushi login --api-key mushi_xxx # non-interactive / CI
|
|
1275
1273
|
mushi login --api-key mushi_xxx --project-id <uuid>
|
|
1276
|
-
mushi login --upgrade-scope # re-auth to add mcp:read + mcp:write to your key`).action(
|
|
1277
|
-
const { CLOUD_API_ENDPOINT, resolveCloudEndpoint: resolveCloudEndpoint2 } = await import("./endpoint-DGS73AVM.js");
|
|
1278
|
-
const endpoint = opts.endpoint ? resolveCloudEndpoint2(opts.endpoint) : CLOUD_API_ENDPOINT;
|
|
1279
|
-
const consoleBase = await resolveConsoleUrl();
|
|
1280
|
-
let apiKey = opts.apiKey;
|
|
1281
|
-
let projectId = opts.projectId;
|
|
1282
|
-
if (apiKey) {
|
|
1283
|
-
const config2 = loadConfig();
|
|
1284
|
-
config2.apiKey = apiKey;
|
|
1285
|
-
config2.endpoint = endpoint;
|
|
1286
|
-
if (projectId) config2.projectId = projectId;
|
|
1287
|
-
config2.consoleUrl = consoleBase;
|
|
1288
|
-
saveConfig(config2);
|
|
1289
|
-
console.log("");
|
|
1290
|
-
console.log(" \u2713 Credentials saved.");
|
|
1291
|
-
const verifyResult = await apiCall(
|
|
1292
|
-
"/v1/sync/whoami",
|
|
1293
|
-
{ apiKey, endpoint, projectId }
|
|
1294
|
-
);
|
|
1295
|
-
if (!verifyResult.ok) {
|
|
1296
|
-
console.warn(` \u26A0 Key saved, but verification failed: ${verifyResult.error?.message ?? "unknown error"}`);
|
|
1297
|
-
console.warn(` Check your key at: ${cliSetupDeepLink(consoleBase)}`);
|
|
1298
|
-
return;
|
|
1299
|
-
}
|
|
1300
|
-
const d = verifyResult.data;
|
|
1301
|
-
if (d.project_name) {
|
|
1302
|
-
if (!projectId && d.project_id) {
|
|
1303
|
-
config2.projectId = d.project_id;
|
|
1304
|
-
saveConfig(config2);
|
|
1305
|
-
}
|
|
1306
|
-
console.log(` \u2713 Project: ${d.project_name} (${d.project_id})`);
|
|
1307
|
-
}
|
|
1308
|
-
console.log(` \u2713 Endpoint: ${endpoint}`);
|
|
1309
|
-
console.log("");
|
|
1310
|
-
console.log(" Run 'mushi whoami' to verify \xB7 'mushi init' to set up the SDK");
|
|
1311
|
-
return;
|
|
1312
|
-
}
|
|
1313
|
-
console.log("");
|
|
1314
|
-
if (opts.upgradeScope) {
|
|
1315
|
-
console.log(" Mushi login \u2014 scope upgrade");
|
|
1316
|
-
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1317
|
-
console.log(" Re-authenticating to mint a new key with report:write + mcp:read + mcp:write scopes.");
|
|
1318
|
-
console.log(" This lets the CLI admin commands (including writes) and MCP server both work with a single key.");
|
|
1319
|
-
console.log("");
|
|
1320
|
-
} else {
|
|
1321
|
-
console.log(" Mushi login");
|
|
1322
|
-
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1323
|
-
}
|
|
1324
|
-
let deviceData;
|
|
1325
|
-
try {
|
|
1326
|
-
deviceData = await startDeviceAuth(endpoint, ensureClientId());
|
|
1327
|
-
} catch (err) {
|
|
1328
|
-
process.stderr.write(`
|
|
1329
|
-
error: Could not start login session: ${err instanceof Error ? err.message : String(err)}
|
|
1330
|
-
`);
|
|
1331
|
-
process.stderr.write(` Fallback: mushi login --api-key <key> --project-id <uuid>
|
|
1332
|
-
`);
|
|
1333
|
-
process.exit(1);
|
|
1334
|
-
}
|
|
1335
|
-
const verifyUrl = deviceData.verification_uri;
|
|
1336
|
-
if (opts.browser !== false) {
|
|
1337
|
-
try {
|
|
1338
|
-
await openInBrowser(verifyUrl);
|
|
1339
|
-
} catch {
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
printAuthBanner(deviceData.user_code, verifyUrl);
|
|
1343
|
-
let cliToken = null;
|
|
1344
|
-
const pollIntervalMs = (deviceData.interval ?? 5) * 1e3;
|
|
1345
|
-
const deadline = Date.now() + (deviceData.expires_in ?? 600) * 1e3;
|
|
1346
|
-
const MAX_CONSECUTIVE_ERRORS = 5;
|
|
1347
|
-
let consecutiveErrors = 0;
|
|
1348
|
-
let firstPoll = true;
|
|
1349
|
-
while (Date.now() < deadline) {
|
|
1350
|
-
if (!firstPoll) {
|
|
1351
|
-
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
1352
|
-
}
|
|
1353
|
-
firstPoll = false;
|
|
1354
|
-
const outcome = await pollDeviceToken(endpoint, deviceData.device_code);
|
|
1355
|
-
if (outcome.status === "approved") {
|
|
1356
|
-
cliToken = outcome.cliToken;
|
|
1357
|
-
break;
|
|
1358
|
-
}
|
|
1359
|
-
if (outcome.status === "pending") {
|
|
1360
|
-
consecutiveErrors = 0;
|
|
1361
|
-
continue;
|
|
1362
|
-
}
|
|
1363
|
-
if (outcome.status === "error") {
|
|
1364
|
-
if (outcome.retryable) {
|
|
1365
|
-
consecutiveErrors += 1;
|
|
1366
|
-
if (consecutiveErrors < MAX_CONSECUTIVE_ERRORS) continue;
|
|
1367
|
-
}
|
|
1368
|
-
printAuthFailed("error", outcome.message);
|
|
1369
|
-
process.exit(1);
|
|
1370
|
-
}
|
|
1371
|
-
if (outcome.status === "denied") {
|
|
1372
|
-
printAuthFailed("denied");
|
|
1373
|
-
process.exit(1);
|
|
1374
|
-
}
|
|
1375
|
-
if (outcome.status === "expired") {
|
|
1376
|
-
printAuthFailed("timeout");
|
|
1377
|
-
process.exit(1);
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
if (!cliToken) {
|
|
1381
|
-
printAuthFailed("timeout");
|
|
1382
|
-
process.exit(1);
|
|
1383
|
-
}
|
|
1384
|
-
printAuthApproved();
|
|
1385
|
-
const projectsList = await listProjects(endpoint, cliToken);
|
|
1386
|
-
let chosenProjectId = projectId;
|
|
1387
|
-
let chosenProjectName;
|
|
1388
|
-
if (!chosenProjectId) {
|
|
1389
|
-
const { createInterface } = await import("readline");
|
|
1390
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1391
|
-
const ask = (q) => new Promise((resolve3) => rl.question(q, (a) => resolve3(a.trim())));
|
|
1392
|
-
if (projectsList.length > 0) {
|
|
1393
|
-
console.log("");
|
|
1394
|
-
console.log(" Your projects:");
|
|
1395
|
-
projectsList.forEach((p3, i) => {
|
|
1396
|
-
console.log(` ${i + 1}. ${p3.name} (${p3.id})`);
|
|
1397
|
-
});
|
|
1398
|
-
console.log(` ${projectsList.length + 1}. Create a new project`);
|
|
1399
|
-
console.log("");
|
|
1400
|
-
const choice = await ask(` Pick a project [1-${projectsList.length + 1}]: `);
|
|
1401
|
-
const num = parseInt(choice, 10);
|
|
1402
|
-
if (num >= 1 && num <= projectsList.length) {
|
|
1403
|
-
const picked = projectsList[num - 1];
|
|
1404
|
-
chosenProjectId = picked.id;
|
|
1405
|
-
chosenProjectName = picked.name;
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
if (!chosenProjectId) {
|
|
1409
|
-
console.log("");
|
|
1410
|
-
const newName = await ask(" Project name: ");
|
|
1411
|
-
rl.close();
|
|
1412
|
-
if (!newName.trim()) {
|
|
1413
|
-
process.stderr.write("\nerror: Project name is required.\n");
|
|
1414
|
-
process.exit(2);
|
|
1415
|
-
}
|
|
1416
|
-
try {
|
|
1417
|
-
const created = await createProject(endpoint, cliToken, newName.trim());
|
|
1418
|
-
chosenProjectId = created.id;
|
|
1419
|
-
chosenProjectName = created.name;
|
|
1420
|
-
apiKey = created.apiKey ?? void 0;
|
|
1421
|
-
console.log(` \u2713 Created project "${chosenProjectName}"`);
|
|
1422
|
-
} catch (err) {
|
|
1423
|
-
process.stderr.write(`
|
|
1424
|
-
error: Could not create project: ${err instanceof Error ? err.message : String(err)}
|
|
1425
|
-
`);
|
|
1426
|
-
process.exit(1);
|
|
1427
|
-
}
|
|
1428
|
-
} else {
|
|
1429
|
-
rl.close();
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
if (!apiKey && chosenProjectId) {
|
|
1433
|
-
try {
|
|
1434
|
-
apiKey = await mintProjectKey(endpoint, cliToken, chosenProjectId) ?? void 0;
|
|
1435
|
-
} catch {
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
const config = loadConfig();
|
|
1439
|
-
config.endpoint = endpoint;
|
|
1440
|
-
config.consoleUrl = consoleBase;
|
|
1441
|
-
if (chosenProjectId) config.projectId = chosenProjectId;
|
|
1442
|
-
if (apiKey) config.apiKey = apiKey;
|
|
1443
|
-
saveConfig(config);
|
|
1444
|
-
console.log("");
|
|
1445
|
-
if (chosenProjectName) console.log(` \u2713 Project: ${chosenProjectName}`);
|
|
1446
|
-
if (apiKey) {
|
|
1447
|
-
console.log(` \u2713 Full-scope CLI key saved (${apiKey.slice(0, 12)}\u2026)`);
|
|
1448
|
-
console.log(` Scopes: report:write \xB7 mcp:read \xB7 mcp:write \u2014 SDK + MCP + admin CLI (incl. writes) all work with this key`);
|
|
1449
|
-
} else {
|
|
1450
|
-
console.log(` \u2139 Get an SDK key at: ${apiKeyHint(consoleBase)}`);
|
|
1451
|
-
}
|
|
1452
|
-
console.log("");
|
|
1453
|
-
if (opts.upgradeScope) {
|
|
1454
|
-
console.log(" Key upgraded! Re-run 'mushi setup' to update your .cursor/mcp.json, then restart Cursor.");
|
|
1455
|
-
} else {
|
|
1456
|
-
console.log(" Run 'mushi init' to set up the SDK in this project.");
|
|
1457
|
-
}
|
|
1458
|
-
});
|
|
1274
|
+
mushi login --upgrade-scope # re-auth to add mcp:read + mcp:write to your key`).action(runLogin);
|
|
1459
1275
|
program2.command("whoami").description("Verify API key and display project info").option("--json", "Machine-readable JSON output").addHelpText("after", `
|
|
1460
1276
|
Verifies that MUSHI_API_KEY is valid and shows which project it belongs to.
|
|
1461
1277
|
Useful after 'mushi login' to confirm credentials are correct.`).action(async (opts) => {
|
|
@@ -1547,6 +1363,189 @@ Examples:
|
|
|
1547
1363
|
}
|
|
1548
1364
|
});
|
|
1549
1365
|
}
|
|
1366
|
+
async function runLogin(opts) {
|
|
1367
|
+
const { CLOUD_API_ENDPOINT, resolveCloudEndpoint: resolveCloudEndpoint2 } = await import("./endpoint-DGS73AVM.js");
|
|
1368
|
+
const endpoint = opts.endpoint ? resolveCloudEndpoint2(opts.endpoint) : CLOUD_API_ENDPOINT;
|
|
1369
|
+
const consoleBase = await resolveConsoleUrl();
|
|
1370
|
+
let apiKey = opts.apiKey;
|
|
1371
|
+
let projectId = opts.projectId;
|
|
1372
|
+
if (apiKey) {
|
|
1373
|
+
const config2 = loadConfig();
|
|
1374
|
+
config2.apiKey = apiKey;
|
|
1375
|
+
config2.endpoint = endpoint;
|
|
1376
|
+
if (projectId) config2.projectId = projectId;
|
|
1377
|
+
config2.consoleUrl = consoleBase;
|
|
1378
|
+
saveConfig(config2);
|
|
1379
|
+
console.log("");
|
|
1380
|
+
console.log(" \u2713 Credentials saved.");
|
|
1381
|
+
const verifyResult = await apiCall(
|
|
1382
|
+
"/v1/sync/whoami",
|
|
1383
|
+
{ apiKey, endpoint, projectId }
|
|
1384
|
+
);
|
|
1385
|
+
if (!verifyResult.ok) {
|
|
1386
|
+
console.warn(` \u26A0 Key saved, but verification failed: ${verifyResult.error?.message ?? "unknown error"}`);
|
|
1387
|
+
console.warn(` Check your key at: ${cliSetupDeepLink(consoleBase)}`);
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
const d = verifyResult.data;
|
|
1391
|
+
if (d.project_name) {
|
|
1392
|
+
if (!projectId && d.project_id) {
|
|
1393
|
+
config2.projectId = d.project_id;
|
|
1394
|
+
saveConfig(config2);
|
|
1395
|
+
}
|
|
1396
|
+
console.log(` \u2713 Project: ${d.project_name} (${d.project_id})`);
|
|
1397
|
+
}
|
|
1398
|
+
console.log(` \u2713 Endpoint: ${endpoint}`);
|
|
1399
|
+
console.log("");
|
|
1400
|
+
console.log(" Run 'mushi whoami' to verify \xB7 'mushi init' to set up the SDK");
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
console.log("");
|
|
1404
|
+
if (opts.upgradeScope) {
|
|
1405
|
+
console.log(" Mushi login \u2014 scope upgrade");
|
|
1406
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1407
|
+
console.log(" Re-authenticating to mint a new key with report:write + mcp:read + mcp:write scopes.");
|
|
1408
|
+
console.log(" This lets the CLI admin commands (including writes) and MCP server both work with a single key.");
|
|
1409
|
+
console.log("");
|
|
1410
|
+
} else {
|
|
1411
|
+
console.log(" Mushi login");
|
|
1412
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1413
|
+
}
|
|
1414
|
+
let deviceData;
|
|
1415
|
+
try {
|
|
1416
|
+
deviceData = await startDeviceAuth(endpoint, ensureClientId());
|
|
1417
|
+
} catch (err) {
|
|
1418
|
+
process.stderr.write(`
|
|
1419
|
+
error: Could not start login session: ${err instanceof Error ? err.message : String(err)}
|
|
1420
|
+
`);
|
|
1421
|
+
process.stderr.write(` Fallback: mushi login --api-key <key> --project-id <uuid>
|
|
1422
|
+
`);
|
|
1423
|
+
process.exit(1);
|
|
1424
|
+
}
|
|
1425
|
+
const verifyUrl = deviceData.verification_uri;
|
|
1426
|
+
if (opts.browser !== false) {
|
|
1427
|
+
try {
|
|
1428
|
+
await openInBrowser(verifyUrl);
|
|
1429
|
+
} catch {
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
printAuthBanner(deviceData.user_code, verifyUrl);
|
|
1433
|
+
let cliToken = null;
|
|
1434
|
+
const pollIntervalMs = (deviceData.interval ?? 5) * 1e3;
|
|
1435
|
+
const deadline = Date.now() + (deviceData.expires_in ?? 600) * 1e3;
|
|
1436
|
+
const MAX_CONSECUTIVE_ERRORS = 5;
|
|
1437
|
+
let consecutiveErrors = 0;
|
|
1438
|
+
let firstPoll = true;
|
|
1439
|
+
while (Date.now() < deadline) {
|
|
1440
|
+
if (!firstPoll) {
|
|
1441
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
1442
|
+
}
|
|
1443
|
+
firstPoll = false;
|
|
1444
|
+
const outcome = await pollDeviceToken(endpoint, deviceData.device_code);
|
|
1445
|
+
if (outcome.status === "approved") {
|
|
1446
|
+
cliToken = outcome.cliToken;
|
|
1447
|
+
break;
|
|
1448
|
+
}
|
|
1449
|
+
if (outcome.status === "pending") {
|
|
1450
|
+
consecutiveErrors = 0;
|
|
1451
|
+
continue;
|
|
1452
|
+
}
|
|
1453
|
+
if (outcome.status === "error") {
|
|
1454
|
+
if (outcome.retryable) {
|
|
1455
|
+
consecutiveErrors += 1;
|
|
1456
|
+
if (consecutiveErrors < MAX_CONSECUTIVE_ERRORS) continue;
|
|
1457
|
+
}
|
|
1458
|
+
printAuthFailed("error", outcome.message);
|
|
1459
|
+
process.exit(1);
|
|
1460
|
+
}
|
|
1461
|
+
if (outcome.status === "denied") {
|
|
1462
|
+
printAuthFailed("denied");
|
|
1463
|
+
process.exit(1);
|
|
1464
|
+
}
|
|
1465
|
+
if (outcome.status === "expired") {
|
|
1466
|
+
printAuthFailed("timeout");
|
|
1467
|
+
process.exit(1);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
if (!cliToken) {
|
|
1471
|
+
printAuthFailed("timeout");
|
|
1472
|
+
process.exit(1);
|
|
1473
|
+
}
|
|
1474
|
+
printAuthApproved();
|
|
1475
|
+
const projectsList = await listProjects(endpoint, cliToken);
|
|
1476
|
+
let chosenProjectId = projectId;
|
|
1477
|
+
let chosenProjectName;
|
|
1478
|
+
if (!chosenProjectId) {
|
|
1479
|
+
const { createInterface } = await import("readline");
|
|
1480
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1481
|
+
const ask = (q) => new Promise((resolve3) => rl.question(q, (a) => resolve3(a.trim())));
|
|
1482
|
+
if (projectsList.length > 0) {
|
|
1483
|
+
console.log("");
|
|
1484
|
+
console.log(" Your projects:");
|
|
1485
|
+
projectsList.forEach((p3, i) => {
|
|
1486
|
+
console.log(` ${i + 1}. ${p3.name} (${p3.id})`);
|
|
1487
|
+
});
|
|
1488
|
+
console.log(` ${projectsList.length + 1}. Create a new project`);
|
|
1489
|
+
console.log("");
|
|
1490
|
+
const choice = await ask(` Pick a project [1-${projectsList.length + 1}]: `);
|
|
1491
|
+
const num = parseInt(choice, 10);
|
|
1492
|
+
if (num >= 1 && num <= projectsList.length) {
|
|
1493
|
+
const picked = projectsList[num - 1];
|
|
1494
|
+
chosenProjectId = picked.id;
|
|
1495
|
+
chosenProjectName = picked.name;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
if (!chosenProjectId) {
|
|
1499
|
+
console.log("");
|
|
1500
|
+
const newName = await ask(" Project name: ");
|
|
1501
|
+
rl.close();
|
|
1502
|
+
if (!newName.trim()) {
|
|
1503
|
+
process.stderr.write("\nerror: Project name is required.\n");
|
|
1504
|
+
process.exit(2);
|
|
1505
|
+
}
|
|
1506
|
+
try {
|
|
1507
|
+
const created = await createProject(endpoint, cliToken, newName.trim());
|
|
1508
|
+
chosenProjectId = created.id;
|
|
1509
|
+
chosenProjectName = created.name;
|
|
1510
|
+
apiKey = created.apiKey ?? void 0;
|
|
1511
|
+
console.log(` \u2713 Created project "${chosenProjectName}"`);
|
|
1512
|
+
} catch (err) {
|
|
1513
|
+
process.stderr.write(`
|
|
1514
|
+
error: Could not create project: ${err instanceof Error ? err.message : String(err)}
|
|
1515
|
+
`);
|
|
1516
|
+
process.exit(1);
|
|
1517
|
+
}
|
|
1518
|
+
} else {
|
|
1519
|
+
rl.close();
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
if (!apiKey && chosenProjectId) {
|
|
1523
|
+
try {
|
|
1524
|
+
apiKey = await mintProjectKey(endpoint, cliToken, chosenProjectId) ?? void 0;
|
|
1525
|
+
} catch {
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
const config = loadConfig();
|
|
1529
|
+
config.endpoint = endpoint;
|
|
1530
|
+
config.consoleUrl = consoleBase;
|
|
1531
|
+
if (chosenProjectId) config.projectId = chosenProjectId;
|
|
1532
|
+
if (apiKey) config.apiKey = apiKey;
|
|
1533
|
+
saveConfig(config);
|
|
1534
|
+
console.log("");
|
|
1535
|
+
if (chosenProjectName) console.log(` \u2713 Project: ${chosenProjectName}`);
|
|
1536
|
+
if (apiKey) {
|
|
1537
|
+
console.log(` \u2713 Full-scope CLI key saved (${apiKey.slice(0, 12)}\u2026)`);
|
|
1538
|
+
console.log(` Scopes: report:write \xB7 mcp:read \xB7 mcp:write \u2014 SDK + MCP + admin CLI (incl. writes) all work with this key`);
|
|
1539
|
+
} else {
|
|
1540
|
+
console.log(` \u2139 Get an SDK key at: ${apiKeyHint(consoleBase)}`);
|
|
1541
|
+
}
|
|
1542
|
+
console.log("");
|
|
1543
|
+
if (opts.upgradeScope) {
|
|
1544
|
+
console.log(" Key upgraded! Re-run 'mushi setup' to update your .cursor/mcp.json, then restart Cursor.");
|
|
1545
|
+
} else {
|
|
1546
|
+
console.log(" Run 'mushi init' to set up the SDK in this project.");
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1550
1549
|
|
|
1551
1550
|
// src/commands/deploy.ts
|
|
1552
1551
|
function registerDeployCommands(program2) {
|
|
@@ -2431,8 +2430,11 @@ error: Could not create project: ${err instanceof Error ? err.message : String(e
|
|
|
2431
2430
|
}
|
|
2432
2431
|
|
|
2433
2432
|
// src/commands/setup.ts
|
|
2433
|
+
function resolveLoginEndpoint(optsEndpoint, existingConfigEndpoint, envEndpoint) {
|
|
2434
|
+
return optsEndpoint ?? existingConfigEndpoint ?? envEndpoint?.trim();
|
|
2435
|
+
}
|
|
2434
2436
|
function registerSetupCommands(program2) {
|
|
2435
|
-
program2.command("setup").description("Wire Cursor MCP into your IDE (reads ~/.config/mushi/config.json \u2014 run `mushi login` first)").option("--ide <ide>", "Target IDE: cursor | claude | continue | zed", "cursor").option("--project-slug <slug>", "Override the project slug in the server name (default: fetched from API or ID prefix)").option("--all-projects", "Write a separate mushi-<name> server entry for every accessible project").option("--with-rules", "Also write the .cursorrules / .claude/rules/mushi.md lesson-library hook").option("--dry-run", "Print what would be written without making changes").option("--verify", "Probe the MCP key after writing to confirm it has mcp:read scope (default: on)").option("--no-verify", "Skip the post-write key probe").addHelpText("after", `
|
|
2437
|
+
program2.command("setup").description("Wire Cursor MCP into your IDE (reads ~/.config/mushi/config.json \u2014 run `mushi login` first)").option("--ide <ide>", "Target IDE: cursor | claude | continue | zed", "cursor").option("--project-slug <slug>", "Override the project slug in the server name (default: fetched from API or ID prefix)").option("--all-projects", "Write a separate mushi-<name> server entry for every accessible project").option("--with-rules", "Also write the .cursorrules / .claude/rules/mushi.md lesson-library hook").option("--dry-run", "Print what would be written without making changes").option("--verify", "Probe the MCP key after writing to confirm it has mcp:read scope (default: on)").option("--no-verify", "Skip the post-write key probe").option("--endpoint <url>", "Override the Mushi API endpoint (self-hosted) \u2014 used for first-run login if not yet configured").addHelpText("after", `
|
|
2436
2438
|
Examples:
|
|
2437
2439
|
mushi setup # wire Cursor (default)
|
|
2438
2440
|
mushi setup --all-projects # one server entry per accessible project
|
|
@@ -2450,6 +2452,11 @@ The command reads credentials from ~/.config/mushi/config.json (run \`mushi logi
|
|
|
2450
2452
|
const { existsSync: existsSync3 } = await import("fs");
|
|
2451
2453
|
const nodePath2 = await import("path");
|
|
2452
2454
|
const os = await import("os");
|
|
2455
|
+
const existingConfig = loadConfig();
|
|
2456
|
+
if (!existingConfig.apiKey) {
|
|
2457
|
+
const endpoint = resolveLoginEndpoint(opts.endpoint, existingConfig.endpoint, process.env.MUSHI_API_ENDPOINT);
|
|
2458
|
+
await runLogin({ endpoint });
|
|
2459
|
+
}
|
|
2453
2460
|
const config = requireConfig({ needsProject: true });
|
|
2454
2461
|
const redactKeyForDisplay = (text2) => {
|
|
2455
2462
|
const key = config.apiKey;
|
package/dist/init.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
resolveConsoleUrl,
|
|
15
15
|
resolveConsoleUrlSync,
|
|
16
16
|
saveConfig
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-2WHQONHK.js";
|
|
18
18
|
import {
|
|
19
19
|
FRAMEWORKS,
|
|
20
20
|
detectFramework,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from "./chunk-AIZRI665.js";
|
|
26
26
|
import {
|
|
27
27
|
MUSHI_CLI_VERSION
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-SS42XQ57.js";
|
|
29
29
|
|
|
30
30
|
// src/init.ts
|
|
31
31
|
import * as p from "@clack/prompts";
|
|
@@ -224,28 +224,26 @@ async function createProject(endpoint, cliToken, name) {
|
|
|
224
224
|
}
|
|
225
225
|
async function mintProjectKey(endpoint, cliToken, projectId) {
|
|
226
226
|
const base = trimTrailingSlash(endpoint);
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
} catch (err) {
|
|
236
|
-
throw new DeviceAuthRequestError(
|
|
237
|
-
"mint_key",
|
|
238
|
-
`Could not reach ${base}/v1/cli/projects/\u2026/keys: ${err instanceof Error ? err.message : String(err)}`
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
const json = await res.json().catch(() => null);
|
|
242
|
-
if (res.ok && json?.ok && json.data?.key) return json.data.key;
|
|
227
|
+
let res;
|
|
228
|
+
try {
|
|
229
|
+
res = await deviceFetch(`${base}/v1/cli/projects/${projectId}/keys`, {
|
|
230
|
+
method: "POST",
|
|
231
|
+
headers: cliAuthHeaders(cliToken),
|
|
232
|
+
body: JSON.stringify({})
|
|
233
|
+
});
|
|
234
|
+
} catch (err) {
|
|
243
235
|
throw new DeviceAuthRequestError(
|
|
244
236
|
"mint_key",
|
|
245
|
-
|
|
246
|
-
res.ok ? void 0 : res.status
|
|
237
|
+
`Could not reach ${base}/v1/cli/projects/\u2026/keys: ${err instanceof Error ? err.message : String(err)}`
|
|
247
238
|
);
|
|
248
|
-
}
|
|
239
|
+
}
|
|
240
|
+
const json = await res.json().catch(() => null);
|
|
241
|
+
if (res.ok && json?.ok && json.data?.key) return json.data.key;
|
|
242
|
+
throw new DeviceAuthRequestError(
|
|
243
|
+
"mint_key",
|
|
244
|
+
json?.error?.message ?? `Could not mint an SDK key (HTTP ${res.status}).`,
|
|
245
|
+
res.status
|
|
246
|
+
);
|
|
249
247
|
}
|
|
250
248
|
|
|
251
249
|
// src/freshness.ts
|
|
@@ -943,7 +941,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
|
|
|
943
941
|
if (p.isCancel(answer) || !answer) return;
|
|
944
942
|
const endpoint = resolveCloudEndpoint(options.endpoint);
|
|
945
943
|
try {
|
|
946
|
-
const { runConnect } = await import("./connect-
|
|
944
|
+
const { runConnect } = await import("./connect-XFWRSEMW.js");
|
|
947
945
|
await runConnect({
|
|
948
946
|
apiKey: credentials.apiKey,
|
|
949
947
|
projectId: credentials.projectId,
|
package/dist/version.js
CHANGED
package/package.json
CHANGED