@tbsten/mir 0.0.2-alpha04 → 0.0.2-alpha06

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.
Files changed (2) hide show
  1. package/dist/cli.js +283 -23
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -38,6 +38,9 @@ function globalConfigPath() {
38
38
  function localConfigPath(cwd) {
39
39
  return path.join(cwd, ".mir", "config.yaml");
40
40
  }
41
+ function localPersonalConfigPath(cwd) {
42
+ return path.join(cwd, ".mir", "config.local.yaml");
43
+ }
41
44
  function installFailuresPath() {
42
45
  return expandTilde("~/.mir/install-failures.json");
43
46
  }
@@ -234,8 +237,7 @@ var DEFAULT_CONFIG = {
234
237
  registries: [
235
238
  {
236
239
  name: "official",
237
- url: "https://mir.tbsten.me",
238
- publish_token: process.env.MIR_PUBLISH_TOKEN || ""
240
+ url: "https://mir.tbsten.me"
239
241
  },
240
242
  { name: "default", path: "~/.mir/registry" }
241
243
  ]
@@ -246,8 +248,25 @@ function loadMirConfig(opts2) {
246
248
  }
247
249
  const cwd = opts2?.cwd ?? process.cwd();
248
250
  const globalConf = loadSingleConfig(globalConfigPath());
249
- const localConf = loadSingleConfig(localConfigPath(cwd));
250
- return mergeConfigs(localConf, globalConf);
251
+ const localConf = loadOptionalConfig(localConfigPath(cwd));
252
+ const localPersonalConf = loadOptionalConfig(localPersonalConfigPath(cwd));
253
+ return mergeConfigs(localPersonalConf, mergeConfigs(localConf, globalConf));
254
+ }
255
+ var EMPTY_CONFIG = { registries: [] };
256
+ function loadOptionalConfig(filePath) {
257
+ if (!fs2.existsSync(filePath)) {
258
+ return { ...EMPTY_CONFIG };
259
+ }
260
+ const content = fs2.readFileSync(filePath, "utf-8");
261
+ const parsed = yaml.load(content);
262
+ if (!parsed) {
263
+ return { ...EMPTY_CONFIG };
264
+ }
265
+ return {
266
+ registries: parsed.registries ?? [],
267
+ defaults: parsed.defaults,
268
+ locale: parsed.locale
269
+ };
251
270
  }
252
271
  function loadSingleConfig(filePath) {
253
272
  if (!fs2.existsSync(filePath)) {
@@ -284,6 +303,16 @@ function mergeConfigs(local, global) {
284
303
  locale: local.locale ?? global.locale
285
304
  };
286
305
  }
306
+ function resolveRegistryUrl(entry) {
307
+ if (!entry.url) {
308
+ throw new RegistryRemoteError(entry.name);
309
+ }
310
+ const url = entry.url.replace(/\/+$/, "");
311
+ if (url.endsWith("/registry")) {
312
+ return url;
313
+ }
314
+ return `${url}/registry`;
315
+ }
287
316
  function resolveRegistryPath(entry) {
288
317
  if (!entry.path) {
289
318
  throw new RegistryRemoteError(entry.name);
@@ -328,6 +357,33 @@ function resolveInstallRegistries(config, registryName) {
328
357
  }
329
358
  return config.registries;
330
359
  }
360
+ function saveRegistryToken(registryName, token, targetConfigPath) {
361
+ const configPath = targetConfigPath ?? globalConfigPath();
362
+ const config = loadSingleConfig(configPath);
363
+ const entry = config.registries.find((r) => r.name === registryName);
364
+ if (entry) {
365
+ entry.publish_token = token;
366
+ } else {
367
+ config.registries.push({ name: registryName, publish_token: token });
368
+ }
369
+ writeConfig(configPath, config);
370
+ }
371
+ function removeRegistryToken(registryName, targetConfigPath) {
372
+ const configPath = targetConfigPath ?? globalConfigPath();
373
+ const config = loadSingleConfig(configPath);
374
+ const entry = config.registries.find((r) => r.name === registryName);
375
+ if (entry) {
376
+ delete entry.publish_token;
377
+ }
378
+ writeConfig(configPath, config);
379
+ }
380
+ function writeConfig(configPath, config) {
381
+ const dir = configPath.replace(/\/[^/]+$/, "");
382
+ if (!fs2.existsSync(dir)) {
383
+ fs2.mkdirSync(dir, { recursive: true });
384
+ }
385
+ fs2.writeFileSync(configPath, yaml.dump(config), "utf-8");
386
+ }
331
387
 
332
388
  // src/lib/remote-publish.ts
333
389
  import { MirError } from "@tbsten/mir-core";
@@ -418,7 +474,7 @@ Snippet: ${name}
418
474
  Location: ${dirPath}
419
475
  Registry: ${registryDisplay}
420
476
 
421
- \u3053\u308C\u3067\u516C\u958B\u3057\u3066\u3044\u3044?`
477
+ \u3053\u308C\u3067\u516C\u958B\u3057\u3066\u3082\u3088\u308D\u3057\u3044\u3067\u3059\u304B?`
422
478
  );
423
479
  if (!shouldProceed) {
424
480
  info(t4("publish.cancelled"));
@@ -647,11 +703,13 @@ async function installSnippet(name, variableArgs, opts2 = {}, cwd = process.cwd(
647
703
  const fetchOptions = opts2.timeout ? { timeoutMs: opts2.timeout * 1e3 } : {};
648
704
  let source;
649
705
  let definition;
706
+ let authorizationStatus;
650
707
  for (const entry of registries) {
651
708
  if (entry.url) {
652
709
  try {
653
- const remote = await fetchRemoteSnippet(entry.url, name, fetchOptions);
710
+ const remote = await fetchRemoteSnippet(resolveRegistryUrl(entry), name, fetchOptions);
654
711
  definition = remote.definition;
712
+ authorizationStatus = remote.authorizationStatus;
655
713
  source = { type: "remote", files: remote.files };
656
714
  break;
657
715
  } catch {
@@ -685,6 +743,24 @@ async function installSnippet(name, variableArgs, opts2 = {}, cwd = process.cwd(
685
743
  if (!quiet && !isJson) {
686
744
  logVariableSummary(name, variableDefs, variables, variableArgs);
687
745
  }
746
+ if (authorizationStatus && authorizationStatus !== "approved") {
747
+ const statusLabel = authorizationStatus === "examination" ? "\u5BE9\u67FB\u4E2D" : "\u5374\u4E0B\u6E08\u307F";
748
+ if (!quiet && !isJson) {
749
+ warn(`\u3053\u306E\u30B9\u30CB\u30DA\u30C3\u30C8\u306F\u73FE\u5728\u300C${statusLabel}\u300D\u3067\u3059\u3002\u516C\u5F0F\u306B\u627F\u8A8D\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002`);
750
+ }
751
+ if (interactive) {
752
+ const answer = await prompt(`\u305D\u308C\u3067\u3082\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u307E\u3059\u304B\uFF1F (y/N): `);
753
+ if (!answer || !["y", "yes"].includes(answer.toLowerCase())) {
754
+ info("\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3092\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F\u3002");
755
+ return {
756
+ success: false,
757
+ snippet: name,
758
+ error: "User cancelled due to authorization status",
759
+ code: "AuthorizationCancelled"
760
+ };
761
+ }
762
+ }
763
+ }
688
764
  if (definition.hooks?.["before-install"]?.length) {
689
765
  if (safe) {
690
766
  if (!quiet) {
@@ -829,11 +905,19 @@ async function resolveVariables(variableDefs, args, interactive = true) {
829
905
  }
830
906
  const description = def.description ?? def.name ?? key;
831
907
  let answer = "";
832
- while (!answer) {
908
+ const maxRetries = 3;
909
+ for (let retry = 0; retry < maxRetries; retry++) {
833
910
  answer = await prompt(`${description} (${key}): `);
834
- if (!answer) {
835
- warn(t6("error.variable-empty", { key }));
836
- }
911
+ if (answer) break;
912
+ warn(t6("error.variable-empty", { key }));
913
+ }
914
+ if (!answer) {
915
+ throw new MirError2(
916
+ t6("error.variable-required", {
917
+ key,
918
+ hint: `--${key}=<value>`
919
+ })
920
+ );
837
921
  }
838
922
  variables[key] = coerceValue(answer, def.schema?.type);
839
923
  }
@@ -1014,7 +1098,7 @@ Examples:
1014
1098
  for (const entry of registries) {
1015
1099
  if (entry.url) {
1016
1100
  try {
1017
- const remoteNames = await listRemoteSnippets(entry.url, fetchOptions);
1101
+ const remoteNames = await listRemoteSnippets(resolveRegistryUrl(entry), fetchOptions);
1018
1102
  for (const s of remoteNames) {
1019
1103
  if (!allSnippets.includes(s)) {
1020
1104
  allSnippets.push(s);
@@ -1132,7 +1216,7 @@ async function listSnippets(opts2 = {}) {
1132
1216
  snippets: []
1133
1217
  };
1134
1218
  try {
1135
- regData.snippets = await listRemoteSnippets2(entry.url, fetchOptions);
1219
+ regData.snippets = await listRemoteSnippets2(resolveRegistryUrl(entry), fetchOptions);
1136
1220
  } catch {
1137
1221
  }
1138
1222
  registryDataList.push(regData);
@@ -1165,7 +1249,7 @@ async function listSnippets(opts2 = {}) {
1165
1249
  step(`${entry.name ?? "remote"} (${entry.url}):`);
1166
1250
  }
1167
1251
  try {
1168
- const remoteSnippets = await listRemoteSnippets2(entry.url, fetchOptions);
1252
+ const remoteSnippets = await listRemoteSnippets2(resolveRegistryUrl(entry), fetchOptions);
1169
1253
  for (const name of remoteSnippets) {
1170
1254
  if (!opts2.quiet) {
1171
1255
  fileItem(name);
@@ -1241,7 +1325,7 @@ async function showSnippetInfo(name, opts2 = {}, configPath) {
1241
1325
  for (const entry of registries2) {
1242
1326
  if (entry.url) {
1243
1327
  try {
1244
- const remoteNames = await listRemoteSnippets3(entry.url, fetchOptions);
1328
+ const remoteNames = await listRemoteSnippets3(resolveRegistryUrl(entry), fetchOptions);
1245
1329
  for (const s of remoteNames) {
1246
1330
  if (!allSnippets.includes(s)) {
1247
1331
  allSnippets.push(s);
@@ -1269,7 +1353,7 @@ async function showSnippetInfo(name, opts2 = {}, configPath) {
1269
1353
  for (const entry of registries) {
1270
1354
  if (entry.url) {
1271
1355
  try {
1272
- const remote = await fetchRemoteSnippet2(entry.url, snippetName, fetchOptions);
1356
+ const remote = await fetchRemoteSnippet2(resolveRegistryUrl(entry), snippetName, fetchOptions);
1273
1357
  definition = remote.definition;
1274
1358
  break;
1275
1359
  } catch {
@@ -1370,13 +1454,11 @@ var SAMPLE_SNIPPET_FILES = {
1370
1454
  };
1371
1455
  var SAMPLE_MIRCONFIG = `# mir config
1372
1456
  #
1373
- # registry:
1374
- # - name: default
1375
- # path: ~/.mir/registry
1376
- #
1377
1457
  # locale: ja
1378
1458
 
1379
- registry:
1459
+ registries:
1460
+ - name: official
1461
+ url: https://mir.tbsten.me
1380
1462
  - name: default
1381
1463
  path: ~/.mir/registry
1382
1464
  `;
@@ -1422,6 +1504,7 @@ async function initProject(cwd = process.cwd(), opts2 = {}) {
1422
1504
  fs9.writeFileSync(configPath, SAMPLE_MIRCONFIG, "utf-8");
1423
1505
  success("\u2713 mirconfig.yaml \u3092\u4F5C\u6210\u3057\u307E\u3057\u305F");
1424
1506
  }
1507
+ addToGitignore(cwd, ".mir/config.local.yaml");
1425
1508
  info("");
1426
1509
  step("\u521D\u671F\u5316\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F!");
1427
1510
  info("\n\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
@@ -1441,6 +1524,23 @@ async function initProject(cwd = process.cwd(), opts2 = {}) {
1441
1524
  throw new MirError3("\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
1442
1525
  }
1443
1526
  }
1527
+ function addToGitignore(cwd, entry) {
1528
+ const gitignorePath = path5.join(cwd, ".gitignore");
1529
+ if (fs9.existsSync(gitignorePath)) {
1530
+ const content = fs9.readFileSync(gitignorePath, "utf-8");
1531
+ const lines = content.split("\n").map((l) => l.trim());
1532
+ if (lines.includes(entry)) {
1533
+ return;
1534
+ }
1535
+ const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
1536
+ fs9.appendFileSync(gitignorePath, `${separator}${entry}
1537
+ `, "utf-8");
1538
+ } else {
1539
+ fs9.writeFileSync(gitignorePath, `${entry}
1540
+ `, "utf-8");
1541
+ }
1542
+ success(`\u2713 .gitignore \u306B ${entry} \u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F`);
1543
+ }
1444
1544
  function registerInitCommand(program2) {
1445
1545
  program2.command("init").description(".mir \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u521D\u671F\u5316\u3059\u308B").option("-f, --force", "\u65E2\u5B58\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u4E0A\u66F8\u304D", false).addHelpText("after", `
1446
1546
  Examples:
@@ -1477,7 +1577,7 @@ async function previewSnippet(name, opts2 = {}, variableArgs = {}, cwd = process
1477
1577
  for (const entry of registries) {
1478
1578
  if (entry.url) {
1479
1579
  try {
1480
- const remote = await fetchRemoteSnippet3(entry.url, name, fetchOptions);
1580
+ const remote = await fetchRemoteSnippet3(resolveRegistryUrl(entry), name, fetchOptions);
1481
1581
  definition = remote.definition;
1482
1582
  source = { type: "remote", files: remote.files };
1483
1583
  break;
@@ -1662,7 +1762,7 @@ async function searchSnippets(query, opts2 = {}) {
1662
1762
  snippets: []
1663
1763
  };
1664
1764
  try {
1665
- const allSnippets = await listRemoteSnippets4(entry.url);
1765
+ const allSnippets = await listRemoteSnippets4(resolveRegistryUrl(entry));
1666
1766
  const lowerQuery = query.toLowerCase();
1667
1767
  result.snippets = allSnippets.filter(
1668
1768
  (name) => name.toLowerCase().includes(lowerQuery)
@@ -1836,6 +1936,125 @@ Examples:
1836
1936
  mir clone my-snippet new-name --force`);
1837
1937
  }
1838
1938
 
1939
+ // src/commands/login.ts
1940
+ import http from "http";
1941
+ import fs12 from "fs";
1942
+ function registerLoginCommand(program2) {
1943
+ program2.command("login").description("registry \u306B\u30ED\u30B0\u30A4\u30F3\u3057\u3066 publish token \u3092\u53D6\u5F97").option("--registry <name>", "\u5BFE\u8C61 registry \u540D (\u30C7\u30D5\u30A9\u30EB\u30C8: official)").option("--local", "token \u3092\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30ED\u30FC\u30AB\u30EB\u306E config.local.yaml \u306B\u4FDD\u5B58").action(async (opts2) => {
1944
+ const registryName = opts2.registry || "official";
1945
+ const config = loadMirConfig();
1946
+ const registry = config.registries.find((r) => r.name === registryName);
1947
+ if (!registry?.url) {
1948
+ error(`Registry "${registryName}" \u304C\u898B\u3064\u304B\u3089\u306A\u3044\u304B\u3001URL \u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093`);
1949
+ process.exit(1);
1950
+ }
1951
+ let targetConfigPath;
1952
+ if (opts2.local) {
1953
+ const mirDir = localPersonalConfigPath(process.cwd()).replace(/\/[^/]+$/, "");
1954
+ if (!fs12.existsSync(mirDir)) {
1955
+ error(".mir/ \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u5B58\u5728\u3057\u307E\u305B\u3093\u3002\u5148\u306B `mir init` \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044");
1956
+ process.exit(1);
1957
+ }
1958
+ targetConfigPath = localPersonalConfigPath(process.cwd());
1959
+ }
1960
+ const registryUrl = registry.url;
1961
+ info(`${registryName} (${registryUrl}) \u306B\u30ED\u30B0\u30A4\u30F3\u3057\u307E\u3059...`);
1962
+ try {
1963
+ const { token, username } = await startLoginFlow(registryUrl);
1964
+ saveRegistryToken(registryName, token, targetConfigPath);
1965
+ success(`\u30ED\u30B0\u30A4\u30F3\u6210\u529F: ${username}`);
1966
+ const dest = opts2.local ? ".mir/config.local.yaml" : "\u30B0\u30ED\u30FC\u30D0\u30EB\u8A2D\u5B9A";
1967
+ info(`publish token \u304C ${dest} \u306B\u4FDD\u5B58\u3055\u308C\u307E\u3057\u305F`);
1968
+ } catch (error2) {
1969
+ if (error2 instanceof Error) {
1970
+ error(`\u30ED\u30B0\u30A4\u30F3\u5931\u6557: ${error2.message}`);
1971
+ }
1972
+ process.exit(1);
1973
+ }
1974
+ });
1975
+ }
1976
+ async function startLoginFlow(registryUrl) {
1977
+ return new Promise((resolve, reject) => {
1978
+ const server = http.createServer((req, res) => {
1979
+ const url = new URL(req.url || "/", `http://localhost`);
1980
+ if (url.pathname === "/callback") {
1981
+ const token = url.searchParams.get("token");
1982
+ const username = url.searchParams.get("username");
1983
+ if (token && username) {
1984
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1985
+ res.end(`
1986
+ <html>
1987
+ <body style="font-family: monospace; text-align: center; padding: 40px;">
1988
+ <h1>\u30ED\u30B0\u30A4\u30F3\u6210\u529F!</h1>
1989
+ <p>${username} \u3068\u3057\u3066\u30ED\u30B0\u30A4\u30F3\u3057\u307E\u3057\u305F\u3002</p>
1990
+ <p>\u3053\u306E\u30A6\u30A3\u30F3\u30C9\u30A6\u306F\u9589\u3058\u3066\u69CB\u3044\u307E\u305B\u3093\u3002</p>
1991
+ </body>
1992
+ </html>
1993
+ `);
1994
+ server.close();
1995
+ resolve({ token, username });
1996
+ } else {
1997
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
1998
+ res.end("<html><body><h1>\u30ED\u30B0\u30A4\u30F3\u5931\u6557</h1></body></html>");
1999
+ server.close();
2000
+ reject(new Error("\u30C8\u30FC\u30AF\u30F3\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F"));
2001
+ }
2002
+ return;
2003
+ }
2004
+ res.writeHead(404);
2005
+ res.end();
2006
+ });
2007
+ server.listen(0, "127.0.0.1", () => {
2008
+ const addr = server.address();
2009
+ if (!addr || typeof addr === "string") {
2010
+ reject(new Error("\u30B5\u30FC\u30D0\u30FC\u306E\u8D77\u52D5\u306B\u5931\u6557\u3057\u307E\u3057\u305F"));
2011
+ return;
2012
+ }
2013
+ const port = addr.port;
2014
+ const loginUrl = `${registryUrl}/auth/login?cli=true&callback_port=${port}`;
2015
+ info(`\u30D6\u30E9\u30A6\u30B6\u3092\u958B\u3044\u3066\u3044\u307E\u3059: ${loginUrl}`);
2016
+ openBrowser(loginUrl);
2017
+ const timeout = setTimeout(() => {
2018
+ server.close();
2019
+ reject(new Error("\u30ED\u30B0\u30A4\u30F3\u304C\u30BF\u30A4\u30E0\u30A2\u30A6\u30C8\u3057\u307E\u3057\u305F"));
2020
+ }, 5 * 60 * 1e3);
2021
+ timeout.unref();
2022
+ });
2023
+ server.on("error", (err) => {
2024
+ reject(new Error(`\u30B5\u30FC\u30D0\u30FC\u30A8\u30E9\u30FC: ${err.message}`));
2025
+ });
2026
+ });
2027
+ }
2028
+ async function openBrowser(url) {
2029
+ const { exec } = await import("child_process");
2030
+ const platform = process.platform;
2031
+ const command = platform === "darwin" ? `open "${url}"` : platform === "win32" ? `start "${url}"` : `xdg-open "${url}"`;
2032
+ exec(command, (err) => {
2033
+ if (err) {
2034
+ info(`\u30D6\u30E9\u30A6\u30B6\u3092\u81EA\u52D5\u3067\u958B\u3051\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u4EE5\u4E0B\u306E URL \u3092\u624B\u52D5\u3067\u958B\u3044\u3066\u304F\u3060\u3055\u3044:`);
2035
+ info(url);
2036
+ }
2037
+ });
2038
+ }
2039
+
2040
+ // src/commands/logout.ts
2041
+ function registerLogoutCommand(program2) {
2042
+ program2.command("logout").description("registry \u304B\u3089\u30ED\u30B0\u30A2\u30A6\u30C8\uFF08\u4FDD\u5B58\u3055\u308C\u305F token \u3092\u524A\u9664\uFF09").option("--registry <name>", "\u5BFE\u8C61 registry \u540D (\u30C7\u30D5\u30A9\u30EB\u30C8: official)").option("--local", "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30ED\u30FC\u30AB\u30EB\u306E config.local.yaml \u304B\u3089 token \u3092\u524A\u9664").action(async (opts2) => {
2043
+ const registryName = opts2.registry || "official";
2044
+ const targetConfigPath = opts2.local ? localPersonalConfigPath(process.cwd()) : void 0;
2045
+ try {
2046
+ removeRegistryToken(registryName, targetConfigPath);
2047
+ const dest = opts2.local ? ".mir/config.local.yaml" : "\u30B0\u30ED\u30FC\u30D0\u30EB\u8A2D\u5B9A";
2048
+ success(`${registryName} \u304B\u3089\u30ED\u30B0\u30A2\u30A6\u30C8\u3057\u307E\u3057\u305F (${dest})`);
2049
+ } catch (error2) {
2050
+ if (error2 instanceof Error) {
2051
+ error(`\u30ED\u30B0\u30A2\u30A6\u30C8\u5931\u6557: ${error2.message}`);
2052
+ }
2053
+ process.exit(1);
2054
+ }
2055
+ });
2056
+ }
2057
+
1839
2058
  // src/lib/env.ts
1840
2059
  function getLocaleFromEnv() {
1841
2060
  const locale2 = process.env.MIR_LOCALE;
@@ -1847,7 +2066,46 @@ function getLocaleFromEnv() {
1847
2066
 
1848
2067
  // src/cli.ts
1849
2068
  var program = new Command();
1850
- program.name("mir").description("\u30B9\u30CB\u30DA\u30C3\u30C8\u3092\u914D\u5E03\u30FB\u53D6\u5F97\u3059\u308B CLI \u30C4\u30FC\u30EB").version("0.0.2-alpha04", "-v, -V, --version").showHelpAfterError(true).option("--config <path>", "\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u30D1\u30B9 (\u30C7\u30D5\u30A9\u30EB\u30C8: ~/.mir/config.yaml)").option("--locale <lang>", "UI \u8A00\u8A9E (ja|en)").option("--no-interactive", "\u975E\u5BFE\u8A71\u30E2\u30FC\u30C9");
2069
+ program.name("mir").description("\u30B9\u30CB\u30DA\u30C3\u30C8\u3092\u914D\u5E03\u30FB\u53D6\u5F97\u3059\u308B CLI \u30C4\u30FC\u30EB").version("0.0.2-alpha06", "-v, --version").showHelpAfterError(true).option("--config <path>", "\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u30D1\u30B9 (\u30C7\u30D5\u30A9\u30EB\u30C8: ~/.mir/config.yaml)").option("--locale <lang>", "UI \u8A00\u8A9E (ja|en)").option("--no-interactive", "\u975E\u5BFE\u8A71\u30E2\u30FC\u30C9").configureHelp({
2070
+ formatHelp(cmd, helper) {
2071
+ const bold3 = "\x1B[1m";
2072
+ const cyan3 = "\x1B[36m";
2073
+ const yellow3 = "\x1B[33m";
2074
+ const dim3 = "\x1B[2m";
2075
+ const reset3 = "\x1B[0m";
2076
+ const title = `${bold3}${cyan3}${cmd.name()}${reset3}`;
2077
+ const desc = cmd.description();
2078
+ const lines = [];
2079
+ lines.push(`${title}${desc ? ` - ${desc}` : ""}`);
2080
+ lines.push("");
2081
+ const usage = helper.commandUsage(cmd);
2082
+ lines.push(`${bold3}Usage:${reset3} ${usage}`);
2083
+ lines.push("");
2084
+ const cmds = helper.visibleCommands(cmd);
2085
+ if (cmds.length > 0) {
2086
+ lines.push(`${bold3}Commands:${reset3}`);
2087
+ const padWidth = helper.padWidth(cmd, helper);
2088
+ for (const sub of cmds) {
2089
+ const name = helper.subcommandTerm(sub).padEnd(padWidth);
2090
+ const subDesc = helper.subcommandDescription(sub);
2091
+ lines.push(` ${cyan3}${name}${reset3} ${dim3}${subDesc}${reset3}`);
2092
+ }
2093
+ lines.push("");
2094
+ }
2095
+ const opts2 = helper.visibleOptions(cmd);
2096
+ if (opts2.length > 0) {
2097
+ lines.push(`${bold3}Options:${reset3}`);
2098
+ const padWidth = helper.padWidth(cmd, helper);
2099
+ for (const opt of opts2) {
2100
+ const flags = helper.optionTerm(opt).padEnd(padWidth);
2101
+ const optDesc = helper.optionDescription(opt);
2102
+ lines.push(` ${yellow3}${flags}${reset3} ${dim3}${optDesc}${reset3}`);
2103
+ }
2104
+ lines.push("");
2105
+ }
2106
+ return lines.join("\n");
2107
+ }
2108
+ });
1851
2109
  var opts = program.opts();
1852
2110
  var locale = "en";
1853
2111
  if (opts.locale) {
@@ -1881,6 +2139,8 @@ registerInfoCommand(program);
1881
2139
  registerSearchCommand(program);
1882
2140
  registerCloneCommand(program);
1883
2141
  registerPreviewCommand(program);
2142
+ registerLoginCommand(program);
2143
+ registerLogoutCommand(program);
1884
2144
  program.parseAsync().catch((err) => {
1885
2145
  if (err instanceof SnippetNotFoundError7) {
1886
2146
  error(err.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tbsten/mir",
3
- "version": "0.0.2-alpha04",
3
+ "version": "0.0.2-alpha06",
4
4
  "description": "スニペット(ディレクトリ構造含む)を配布・取得するCLIツール",
5
5
  "type": "module",
6
6
  "bin": {
@@ -43,7 +43,7 @@
43
43
  "fast-check": "^4.5.3",
44
44
  "tsup": "^8",
45
45
  "typescript": "^5",
46
- "vitest": "^3"
46
+ "vitest": "^4"
47
47
  },
48
48
  "dependencies": {
49
49
  "@tbsten/mir-core": "*",