ymmv-cli 0.6.0 → 0.6.1
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/cli.js +71 -42
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -270,8 +270,8 @@ var MAX_PARSE_EXTRAS = 256;
|
|
|
270
270
|
var MAX_PARSE_VALUE = 4096;
|
|
271
271
|
var MAX_PARSE_LABEL = 4096;
|
|
272
272
|
var ProfileParseError = class extends Error {
|
|
273
|
-
constructor(
|
|
274
|
-
super(
|
|
273
|
+
constructor(message2) {
|
|
274
|
+
super(message2);
|
|
275
275
|
this.name = "ProfileParseError";
|
|
276
276
|
}
|
|
277
277
|
};
|
|
@@ -353,6 +353,10 @@ var NO_CODES = { amber: "", faint: "", bold: "", reset: "" };
|
|
|
353
353
|
function palette(color) {
|
|
354
354
|
return color ? CODES : NO_CODES;
|
|
355
355
|
}
|
|
356
|
+
function message(text) {
|
|
357
|
+
return `
|
|
358
|
+
${text.split(/\r?\n/).map((l) => l ? ` ${l}` : l).join("\n")}`;
|
|
359
|
+
}
|
|
356
360
|
var ESC_INTRODUCERS = `${String.fromCharCode(27)}${String.fromCharCode(155)}`;
|
|
357
361
|
var BEL = String.fromCharCode(7);
|
|
358
362
|
var ANSI_RE = new RegExp(
|
|
@@ -449,7 +453,6 @@ function renderProfile(profile, opts) {
|
|
|
449
453
|
if (!preview) {
|
|
450
454
|
lines.push("", ` ${c.faint}updated ${relTime(profile.updated_at, opts.now)}${c.reset}`);
|
|
451
455
|
}
|
|
452
|
-
lines.push("");
|
|
453
456
|
return lines.join("\n");
|
|
454
457
|
}
|
|
455
458
|
var MISSING = "\u2014";
|
|
@@ -499,20 +502,18 @@ function renderDiff(result, opts) {
|
|
|
499
502
|
}
|
|
500
503
|
}
|
|
501
504
|
lines.push(...extrasBlock(result.extras, theirsLabel, mineLabel, c));
|
|
502
|
-
lines.push("", ` ${c.faint}${result.differ} differ ${result.shared} shared${c.reset}
|
|
505
|
+
lines.push("", ` ${c.faint}${result.differ} differ ${result.shared} shared${c.reset}`);
|
|
503
506
|
return lines.join("\n");
|
|
504
507
|
}
|
|
505
508
|
function nudge(color) {
|
|
506
509
|
const c = palette(color);
|
|
507
510
|
return `
|
|
508
|
-
${c.amber}publish yours to diff \u2192${c.reset} run ${c.bold}ymmv${c.reset}
|
|
509
|
-
`;
|
|
511
|
+
${c.amber}publish yours to diff \u2192${c.reset} run ${c.bold}ymmv${c.reset}`;
|
|
510
512
|
}
|
|
511
513
|
function notFound(handle, color, base) {
|
|
512
514
|
return `
|
|
513
515
|
no ymmv profile for "${sanitizeValue(handle)}" yet.
|
|
514
|
-
publish one at ${link(base, color)} with: npx ymmv-cli
|
|
515
|
-
`;
|
|
516
|
+
publish one at ${link(base, color)} with: npx ymmv-cli`;
|
|
516
517
|
}
|
|
517
518
|
|
|
518
519
|
// src/http.ts
|
|
@@ -574,7 +575,7 @@ async function mintYmmvToken(accessToken) {
|
|
|
574
575
|
if (res.status === 429) {
|
|
575
576
|
const retry = res.headers.get("retry-after");
|
|
576
577
|
const msg = body.message ? wireText(body.message) : "Too many login attempts. Slow down and try again shortly";
|
|
577
|
-
throw new Error(retry ? `${msg} (retry in ${retry}s)` : msg);
|
|
578
|
+
throw new Error(retry && /^\d+$/.test(retry) ? `${msg} (retry in ${retry}s)` : msg);
|
|
578
579
|
}
|
|
579
580
|
throw new Error(`login failed: ${res.status} ${wireText(body.error ?? "")}`.trim());
|
|
580
581
|
}
|
|
@@ -754,11 +755,11 @@ async function login(deps = {}) {
|
|
|
754
755
|
const c = palette(color);
|
|
755
756
|
const verifyUri = /^https:\/\/github\.com\//.test(dc.verification_uri) ? link(dc.verification_uri, color) : sanitizeValue(dc.verification_uri);
|
|
756
757
|
console.log(
|
|
757
|
-
|
|
758
|
-
|
|
758
|
+
message(
|
|
759
|
+
`Open ${verifyUri} and enter code: ${c.bold}${sanitizeValue(dc.user_code)}${c.reset}
|
|
760
|
+
${c.faint}waiting for GitHub approval\u2026 (Ctrl+C to cancel)${c.reset}`
|
|
761
|
+
)
|
|
759
762
|
);
|
|
760
|
-
console.log(` ${c.faint}waiting for GitHub approval\u2026 (Ctrl+C to cancel)${c.reset}
|
|
761
|
-
`);
|
|
762
763
|
const accessToken = await pollForToken(dc, deps);
|
|
763
764
|
const { token, handle } = await mintYmmvToken(accessToken);
|
|
764
765
|
try {
|
|
@@ -769,8 +770,9 @@ async function login(deps = {}) {
|
|
|
769
770
|
throw e;
|
|
770
771
|
}
|
|
771
772
|
console.log(
|
|
772
|
-
|
|
773
|
-
` : "
|
|
773
|
+
message(
|
|
774
|
+
handle ? `Logged in as ${handle}.` : "Logged in. No handle bound (your GitHub username is a reserved word)."
|
|
775
|
+
)
|
|
774
776
|
);
|
|
775
777
|
}
|
|
776
778
|
|
|
@@ -783,7 +785,7 @@ async function rateLimitMessage(res) {
|
|
|
783
785
|
if (typeof body?.message === "string" && body.message) msg = wireText(body.message);
|
|
784
786
|
} catch {
|
|
785
787
|
}
|
|
786
|
-
return retry ? `${msg} (retry in ${retry}s)` : msg;
|
|
788
|
+
return retry && /^\d+$/.test(retry) ? `${msg} (retry in ${retry}s)` : msg;
|
|
787
789
|
}
|
|
788
790
|
async function ensureLogin() {
|
|
789
791
|
const existing = await loadToken();
|
|
@@ -1201,15 +1203,25 @@ function makePrompter() {
|
|
|
1201
1203
|
const answer = (await question(promptLine(label, def, color))).trim();
|
|
1202
1204
|
return answer === "" ? clean ?? "" : answer;
|
|
1203
1205
|
},
|
|
1206
|
+
// Prompts are output units (render.ts convention): confirm/choice open with the unit's one
|
|
1207
|
+
// blank line here — never in the caller's question string. Field ask()s stay tight: the
|
|
1208
|
+
// 13-key walk is a single unit opened by its hint line.
|
|
1204
1209
|
async confirm(q, defYes) {
|
|
1205
|
-
const answer = (await question(`
|
|
1210
|
+
const answer = (await question(`
|
|
1211
|
+
${q} ${c.faint}[${defYes ? "Y/n" : "y/N"}]${c.reset} `)).trim().toLowerCase();
|
|
1206
1212
|
if (answer === "") return defYes;
|
|
1207
1213
|
return answer === "y" || answer === "yes";
|
|
1208
1214
|
},
|
|
1209
1215
|
async choice(q, keys, def, hint) {
|
|
1216
|
+
let prefix = "\n";
|
|
1210
1217
|
for (; ; ) {
|
|
1211
|
-
const hit = matchChoice(
|
|
1218
|
+
const hit = matchChoice(
|
|
1219
|
+
await question(`${prefix} ${q} ${c.faint}[${hint}]${c.reset} `),
|
|
1220
|
+
keys,
|
|
1221
|
+
def
|
|
1222
|
+
);
|
|
1212
1223
|
if (hit !== null) return hit;
|
|
1224
|
+
prefix = "";
|
|
1213
1225
|
}
|
|
1214
1226
|
},
|
|
1215
1227
|
close() {
|
|
@@ -1223,7 +1235,9 @@ function makePrompter() {
|
|
|
1223
1235
|
function requireHandle(cred) {
|
|
1224
1236
|
if (cred.handle) return cred.handle;
|
|
1225
1237
|
console.error(
|
|
1226
|
-
|
|
1238
|
+
message(
|
|
1239
|
+
"Your GitHub username is a reserved word, so no handle is bound. Rename on GitHub, then run `ymmv login` again."
|
|
1240
|
+
)
|
|
1227
1241
|
);
|
|
1228
1242
|
process.exitCode = 1;
|
|
1229
1243
|
return null;
|
|
@@ -1245,7 +1259,7 @@ function newProfile(handle, entries, extras) {
|
|
|
1245
1259
|
};
|
|
1246
1260
|
}
|
|
1247
1261
|
function printPublished(res, color) {
|
|
1248
|
-
console.log(`Published ${res.handle} \u2192 ${link(res.url, color)}`);
|
|
1262
|
+
console.log(message(`Published ${res.handle} \u2192 ${link(res.url, color)}`));
|
|
1249
1263
|
}
|
|
1250
1264
|
function pagePointer(handle) {
|
|
1251
1265
|
const color = colorEnabled();
|
|
@@ -1254,8 +1268,7 @@ function pagePointer(handle) {
|
|
|
1254
1268
|
}
|
|
1255
1269
|
async function promptEntries(defaults, prompter) {
|
|
1256
1270
|
const c = palette(colorEnabled());
|
|
1257
|
-
console.log(`
|
|
1258
|
-
${c.faint}Enter to keep, "-" to clear${c.reset}`);
|
|
1271
|
+
console.log(message(`${c.faint}Enter to keep, "-" to clear${c.reset}`));
|
|
1259
1272
|
const chosen = /* @__PURE__ */ new Map();
|
|
1260
1273
|
for (const key of CURATED_KEYS) {
|
|
1261
1274
|
const answer = (await prompter.ask(KEY_LABELS[key], defaults.get(key))).trim();
|
|
@@ -1266,7 +1279,9 @@ async function promptEntries(defaults, prompter) {
|
|
|
1266
1279
|
}
|
|
1267
1280
|
async function publish(io) {
|
|
1268
1281
|
if (!io.interactive && !io.yes) {
|
|
1269
|
-
console.error(
|
|
1282
|
+
console.error(
|
|
1283
|
+
message("Non-interactive publish needs -y (nothing publishes unconfirmed): ymmv -y")
|
|
1284
|
+
);
|
|
1270
1285
|
process.exitCode = 1;
|
|
1271
1286
|
return;
|
|
1272
1287
|
}
|
|
@@ -1291,11 +1306,12 @@ async function publish(io) {
|
|
|
1291
1306
|
console.log(
|
|
1292
1307
|
renderProfile(newProfile(handle, entries, extras), { color, site, mode: "preview" })
|
|
1293
1308
|
);
|
|
1309
|
+
const notes = [];
|
|
1294
1310
|
if (carried.length > 0) {
|
|
1295
1311
|
const s = carried.length === 1 ? "" : "s";
|
|
1296
|
-
|
|
1312
|
+
notes.push(`(+${carried.length} newer field${s} kept as-is; upgrade ymmv-cli to edit them)`);
|
|
1297
1313
|
for (const e of carried) {
|
|
1298
|
-
|
|
1314
|
+
notes.push(` ${sanitizeValue(e.key)} = ${sanitizeValue(e.value)}`);
|
|
1299
1315
|
}
|
|
1300
1316
|
}
|
|
1301
1317
|
const publishedLabels = new Set(
|
|
@@ -1304,9 +1320,10 @@ async function publish(io) {
|
|
|
1304
1320
|
for (const x of extras) {
|
|
1305
1321
|
if (publishedLabels.has(x.label.trim().toLowerCase())) {
|
|
1306
1322
|
const label = sanitizeValue(x.label.trim());
|
|
1307
|
-
|
|
1323
|
+
notes.push(`(extra "${label}" duplicates a curated field; ymmv unset --extra "${label}")`);
|
|
1308
1324
|
}
|
|
1309
1325
|
}
|
|
1326
|
+
if (notes.length > 0) console.log(message(notes.join("\n")));
|
|
1310
1327
|
};
|
|
1311
1328
|
let values = defaults;
|
|
1312
1329
|
const assemble = () => [...entriesFromMap(values), ...carried];
|
|
@@ -1332,14 +1349,15 @@ async function publish(io) {
|
|
|
1332
1349
|
return;
|
|
1333
1350
|
}
|
|
1334
1351
|
if (ans === "n") {
|
|
1335
|
-
console.log("Aborted. Nothing published.");
|
|
1352
|
+
console.log(message("Aborted. Nothing published."));
|
|
1336
1353
|
return;
|
|
1337
1354
|
}
|
|
1338
1355
|
values = await promptEntries(values, io.prompter);
|
|
1339
1356
|
}
|
|
1340
1357
|
} catch (e) {
|
|
1341
1358
|
if (e instanceof PromptAborted) {
|
|
1342
|
-
console.log(
|
|
1359
|
+
console.log(`
|
|
1360
|
+
${message("Aborted. Nothing published.")}`);
|
|
1343
1361
|
process.exitCode = 130;
|
|
1344
1362
|
return;
|
|
1345
1363
|
}
|
|
@@ -1379,7 +1397,7 @@ async function runSet(target) {
|
|
|
1379
1397
|
const { entries, extras } = applySet(existing, target);
|
|
1380
1398
|
const res = await publishProfile(newProfile(handle, entries, extras));
|
|
1381
1399
|
const line = target.kind === "curated" ? `Set ${KEY_LABELS[target.key]} = ${target.value}.` : `Set extra ${target.label} = ${target.value}.`;
|
|
1382
|
-
console.log(`${line}${pagePointer(res.handle)}`);
|
|
1400
|
+
console.log(message(`${line}${pagePointer(res.handle)}`));
|
|
1383
1401
|
}
|
|
1384
1402
|
async function runUnset(target) {
|
|
1385
1403
|
const cred = await ensureLogin();
|
|
@@ -1388,19 +1406,21 @@ async function runUnset(target) {
|
|
|
1388
1406
|
const existing = await fetchProfileJson(handle);
|
|
1389
1407
|
assertHandleUnchanged(existing, handle);
|
|
1390
1408
|
if (!existing) {
|
|
1391
|
-
console.log("No profile yet. Run `ymmv` to publish one.");
|
|
1409
|
+
console.log(message("No profile yet. Run `ymmv` to publish one."));
|
|
1392
1410
|
return;
|
|
1393
1411
|
}
|
|
1394
1412
|
const { entries, extras, removed } = applyUnset(existing, target);
|
|
1395
1413
|
if (!removed) {
|
|
1396
1414
|
console.log(
|
|
1397
|
-
|
|
1415
|
+
message(
|
|
1416
|
+
target.kind === "curated" ? `${KEY_LABELS[target.key]} is not set.` : `No extra "${target.label}".`
|
|
1417
|
+
)
|
|
1398
1418
|
);
|
|
1399
1419
|
return;
|
|
1400
1420
|
}
|
|
1401
1421
|
const res = await publishProfile(newProfile(handle, entries, extras));
|
|
1402
1422
|
const line = target.kind === "curated" ? `Removed ${KEY_LABELS[target.key]} (was "${sanitizeValue(removed.value)}").` : `Removed extra "${sanitizeValue(removed.label)}" (was "${sanitizeValue(removed.value)}").`;
|
|
1403
|
-
console.log(`${line}${pagePointer(res.handle)}`);
|
|
1423
|
+
console.log(message(`${line}${pagePointer(res.handle)}`));
|
|
1404
1424
|
}
|
|
1405
1425
|
async function runDelete(io) {
|
|
1406
1426
|
const cred = await ensureLogin();
|
|
@@ -1408,7 +1428,9 @@ async function runDelete(io) {
|
|
|
1408
1428
|
if (!io.yes) {
|
|
1409
1429
|
if (!io.interactive || !io.prompter) {
|
|
1410
1430
|
console.error(
|
|
1411
|
-
|
|
1431
|
+
message(
|
|
1432
|
+
`Refusing to delete ${target} without confirmation. Re-run with -y to confirm: ymmv delete -y`
|
|
1433
|
+
)
|
|
1412
1434
|
);
|
|
1413
1435
|
process.exitCode = 1;
|
|
1414
1436
|
return;
|
|
@@ -1418,20 +1440,21 @@ async function runDelete(io) {
|
|
|
1418
1440
|
go = await io.prompter.confirm(`Delete ${target}? This is permanent`, false);
|
|
1419
1441
|
} catch (e) {
|
|
1420
1442
|
if (e instanceof PromptAborted) {
|
|
1421
|
-
console.log(
|
|
1443
|
+
console.log(`
|
|
1444
|
+
${message("Cancelled. Nothing deleted.")}`);
|
|
1422
1445
|
process.exitCode = 130;
|
|
1423
1446
|
return;
|
|
1424
1447
|
}
|
|
1425
1448
|
throw e;
|
|
1426
1449
|
}
|
|
1427
1450
|
if (!go) {
|
|
1428
|
-
console.log("Cancelled. Nothing deleted.");
|
|
1451
|
+
console.log(message("Cancelled. Nothing deleted."));
|
|
1429
1452
|
return;
|
|
1430
1453
|
}
|
|
1431
1454
|
}
|
|
1432
1455
|
await deleteProfile();
|
|
1433
1456
|
await deleteToken();
|
|
1434
|
-
console.log(`Deleted ${target}. Run \`ymmv\` to publish again.`);
|
|
1457
|
+
console.log(message(`Deleted ${target}. Run \`ymmv\` to publish again.`));
|
|
1435
1458
|
}
|
|
1436
1459
|
|
|
1437
1460
|
// src/resolve.ts
|
|
@@ -1541,7 +1564,9 @@ async function logout() {
|
|
|
1541
1564
|
if (!stored) {
|
|
1542
1565
|
const otherBase = await peekBase();
|
|
1543
1566
|
console.log(
|
|
1544
|
-
|
|
1567
|
+
message(
|
|
1568
|
+
otherBase && otherBase !== BASE ? `Not logged in to ${BASE} (a token for ${otherBase} exists; set YMMV_API to that to log out of it).` : "Not logged in."
|
|
1569
|
+
)
|
|
1545
1570
|
);
|
|
1546
1571
|
return;
|
|
1547
1572
|
}
|
|
@@ -1550,13 +1575,15 @@ async function logout() {
|
|
|
1550
1575
|
revoked = await revokeYmmvToken(stored.token);
|
|
1551
1576
|
} catch {
|
|
1552
1577
|
console.error(
|
|
1553
|
-
|
|
1578
|
+
message(
|
|
1579
|
+
"Couldn't reach the server to revoke. Your token is still active. Run `ymmv logout` again when connected."
|
|
1580
|
+
)
|
|
1554
1581
|
);
|
|
1555
1582
|
process.exitCode = 1;
|
|
1556
1583
|
return;
|
|
1557
1584
|
}
|
|
1558
1585
|
await deleteToken();
|
|
1559
|
-
console.log(revoked ? "Logged out." : "Logged out (no active session on this server).");
|
|
1586
|
+
console.log(message(revoked ? "Logged out." : "Logged out (no active session on this server)."));
|
|
1560
1587
|
}
|
|
1561
1588
|
function printVersion() {
|
|
1562
1589
|
try {
|
|
@@ -1596,7 +1623,7 @@ async function main(argv) {
|
|
|
1596
1623
|
case "login": {
|
|
1597
1624
|
await login();
|
|
1598
1625
|
const c = palette(colorEnabled());
|
|
1599
|
-
console.log(
|
|
1626
|
+
console.log(message(`${c.faint}next: run ymmv to publish your stack${c.reset}`));
|
|
1600
1627
|
break;
|
|
1601
1628
|
}
|
|
1602
1629
|
case "logout":
|
|
@@ -1609,7 +1636,7 @@ async function main(argv) {
|
|
|
1609
1636
|
printVersion();
|
|
1610
1637
|
break;
|
|
1611
1638
|
case "error":
|
|
1612
|
-
console.error(cmd.message);
|
|
1639
|
+
console.error(message(cmd.message));
|
|
1613
1640
|
process.exitCode = 1;
|
|
1614
1641
|
break;
|
|
1615
1642
|
}
|
|
@@ -1617,6 +1644,8 @@ async function main(argv) {
|
|
|
1617
1644
|
|
|
1618
1645
|
// src/cli.ts
|
|
1619
1646
|
main(process.argv.slice(2)).catch((err) => {
|
|
1620
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
1647
|
+
console.error(message(err instanceof Error ? err.message : String(err)));
|
|
1621
1648
|
process.exitCode = 1;
|
|
1649
|
+
}).finally(() => {
|
|
1650
|
+
(process.exitCode ? process.stderr : process.stdout).write("\n");
|
|
1622
1651
|
});
|