@tbsten/mir 0.0.2-alpha03 → 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 +300 -22
  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";
@@ -410,6 +466,21 @@ async function publishSnippet(name, opts2 = {}, cwd = process.cwd(), configPath)
410
466
  const definition = parseSnippetYaml(yamlContent);
411
467
  const config = loadMirConfig(configPath ? { configPath } : { cwd });
412
468
  const registryEntry = resolvePublishRegistry(config, opts2.registry);
469
+ if (opts2.interactive !== false) {
470
+ const registryDisplay = registryEntry.url || resolveRegistryPath(registryEntry);
471
+ const shouldProceed = await confirm(
472
+ `
473
+ Snippet: ${name}
474
+ Location: ${dirPath}
475
+ Registry: ${registryDisplay}
476
+
477
+ \u3053\u308C\u3067\u516C\u958B\u3057\u3066\u3082\u3088\u308D\u3057\u3044\u3067\u3059\u304B?`
478
+ );
479
+ if (!shouldProceed) {
480
+ info(t4("publish.cancelled"));
481
+ return;
482
+ }
483
+ }
413
484
  if (registryEntry.url) {
414
485
  if (!registryEntry.publish_token) {
415
486
  throw new Error(t4("error.publish-token-required"));
@@ -465,6 +536,9 @@ Examples:
465
536
  mir publish react-hook --force
466
537
  mir publish my-component --registry custom
467
538
  mir publish react-hook --no-interactive`).action(async (name, opts2) => {
539
+ if (process.argv.includes("--no-interactive")) {
540
+ opts2.interactive = false;
541
+ }
468
542
  let snippetName = name;
469
543
  if (!snippetName) {
470
544
  const snippets = listLocalSnippets(process.cwd());
@@ -629,11 +703,13 @@ async function installSnippet(name, variableArgs, opts2 = {}, cwd = process.cwd(
629
703
  const fetchOptions = opts2.timeout ? { timeoutMs: opts2.timeout * 1e3 } : {};
630
704
  let source;
631
705
  let definition;
706
+ let authorizationStatus;
632
707
  for (const entry of registries) {
633
708
  if (entry.url) {
634
709
  try {
635
- const remote = await fetchRemoteSnippet(entry.url, name, fetchOptions);
710
+ const remote = await fetchRemoteSnippet(resolveRegistryUrl(entry), name, fetchOptions);
636
711
  definition = remote.definition;
712
+ authorizationStatus = remote.authorizationStatus;
637
713
  source = { type: "remote", files: remote.files };
638
714
  break;
639
715
  } catch {
@@ -667,6 +743,24 @@ async function installSnippet(name, variableArgs, opts2 = {}, cwd = process.cwd(
667
743
  if (!quiet && !isJson) {
668
744
  logVariableSummary(name, variableDefs, variables, variableArgs);
669
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
+ }
670
764
  if (definition.hooks?.["before-install"]?.length) {
671
765
  if (safe) {
672
766
  if (!quiet) {
@@ -811,11 +905,19 @@ async function resolveVariables(variableDefs, args, interactive = true) {
811
905
  }
812
906
  const description = def.description ?? def.name ?? key;
813
907
  let answer = "";
814
- while (!answer) {
908
+ const maxRetries = 3;
909
+ for (let retry = 0; retry < maxRetries; retry++) {
815
910
  answer = await prompt(`${description} (${key}): `);
816
- if (!answer) {
817
- warn(t6("error.variable-empty", { key }));
818
- }
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
+ );
819
921
  }
820
922
  variables[key] = coerceValue(answer, def.schema?.type);
821
923
  }
@@ -996,7 +1098,7 @@ Examples:
996
1098
  for (const entry of registries) {
997
1099
  if (entry.url) {
998
1100
  try {
999
- const remoteNames = await listRemoteSnippets(entry.url, fetchOptions);
1101
+ const remoteNames = await listRemoteSnippets(resolveRegistryUrl(entry), fetchOptions);
1000
1102
  for (const s of remoteNames) {
1001
1103
  if (!allSnippets.includes(s)) {
1002
1104
  allSnippets.push(s);
@@ -1114,7 +1216,7 @@ async function listSnippets(opts2 = {}) {
1114
1216
  snippets: []
1115
1217
  };
1116
1218
  try {
1117
- regData.snippets = await listRemoteSnippets2(entry.url, fetchOptions);
1219
+ regData.snippets = await listRemoteSnippets2(resolveRegistryUrl(entry), fetchOptions);
1118
1220
  } catch {
1119
1221
  }
1120
1222
  registryDataList.push(regData);
@@ -1147,7 +1249,7 @@ async function listSnippets(opts2 = {}) {
1147
1249
  step(`${entry.name ?? "remote"} (${entry.url}):`);
1148
1250
  }
1149
1251
  try {
1150
- const remoteSnippets = await listRemoteSnippets2(entry.url, fetchOptions);
1252
+ const remoteSnippets = await listRemoteSnippets2(resolveRegistryUrl(entry), fetchOptions);
1151
1253
  for (const name of remoteSnippets) {
1152
1254
  if (!opts2.quiet) {
1153
1255
  fileItem(name);
@@ -1223,7 +1325,7 @@ async function showSnippetInfo(name, opts2 = {}, configPath) {
1223
1325
  for (const entry of registries2) {
1224
1326
  if (entry.url) {
1225
1327
  try {
1226
- const remoteNames = await listRemoteSnippets3(entry.url, fetchOptions);
1328
+ const remoteNames = await listRemoteSnippets3(resolveRegistryUrl(entry), fetchOptions);
1227
1329
  for (const s of remoteNames) {
1228
1330
  if (!allSnippets.includes(s)) {
1229
1331
  allSnippets.push(s);
@@ -1251,7 +1353,7 @@ async function showSnippetInfo(name, opts2 = {}, configPath) {
1251
1353
  for (const entry of registries) {
1252
1354
  if (entry.url) {
1253
1355
  try {
1254
- const remote = await fetchRemoteSnippet2(entry.url, snippetName, fetchOptions);
1356
+ const remote = await fetchRemoteSnippet2(resolveRegistryUrl(entry), snippetName, fetchOptions);
1255
1357
  definition = remote.definition;
1256
1358
  break;
1257
1359
  } catch {
@@ -1352,13 +1454,11 @@ var SAMPLE_SNIPPET_FILES = {
1352
1454
  };
1353
1455
  var SAMPLE_MIRCONFIG = `# mir config
1354
1456
  #
1355
- # registry:
1356
- # - name: default
1357
- # path: ~/.mir/registry
1358
- #
1359
1457
  # locale: ja
1360
1458
 
1361
- registry:
1459
+ registries:
1460
+ - name: official
1461
+ url: https://mir.tbsten.me
1362
1462
  - name: default
1363
1463
  path: ~/.mir/registry
1364
1464
  `;
@@ -1404,6 +1504,7 @@ async function initProject(cwd = process.cwd(), opts2 = {}) {
1404
1504
  fs9.writeFileSync(configPath, SAMPLE_MIRCONFIG, "utf-8");
1405
1505
  success("\u2713 mirconfig.yaml \u3092\u4F5C\u6210\u3057\u307E\u3057\u305F");
1406
1506
  }
1507
+ addToGitignore(cwd, ".mir/config.local.yaml");
1407
1508
  info("");
1408
1509
  step("\u521D\u671F\u5316\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F!");
1409
1510
  info("\n\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
@@ -1423,6 +1524,23 @@ async function initProject(cwd = process.cwd(), opts2 = {}) {
1423
1524
  throw new MirError3("\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
1424
1525
  }
1425
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
+ }
1426
1544
  function registerInitCommand(program2) {
1427
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", `
1428
1546
  Examples:
@@ -1459,7 +1577,7 @@ async function previewSnippet(name, opts2 = {}, variableArgs = {}, cwd = process
1459
1577
  for (const entry of registries) {
1460
1578
  if (entry.url) {
1461
1579
  try {
1462
- const remote = await fetchRemoteSnippet3(entry.url, name, fetchOptions);
1580
+ const remote = await fetchRemoteSnippet3(resolveRegistryUrl(entry), name, fetchOptions);
1463
1581
  definition = remote.definition;
1464
1582
  source = { type: "remote", files: remote.files };
1465
1583
  break;
@@ -1644,7 +1762,7 @@ async function searchSnippets(query, opts2 = {}) {
1644
1762
  snippets: []
1645
1763
  };
1646
1764
  try {
1647
- const allSnippets = await listRemoteSnippets4(entry.url);
1765
+ const allSnippets = await listRemoteSnippets4(resolveRegistryUrl(entry));
1648
1766
  const lowerQuery = query.toLowerCase();
1649
1767
  result.snippets = allSnippets.filter(
1650
1768
  (name) => name.toLowerCase().includes(lowerQuery)
@@ -1818,6 +1936,125 @@ Examples:
1818
1936
  mir clone my-snippet new-name --force`);
1819
1937
  }
1820
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
+
1821
2058
  // src/lib/env.ts
1822
2059
  function getLocaleFromEnv() {
1823
2060
  const locale2 = process.env.MIR_LOCALE;
@@ -1829,7 +2066,46 @@ function getLocaleFromEnv() {
1829
2066
 
1830
2067
  // src/cli.ts
1831
2068
  var program = new Command();
1832
- 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-alpha02", "-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
+ });
1833
2109
  var opts = program.opts();
1834
2110
  var locale = "en";
1835
2111
  if (opts.locale) {
@@ -1863,6 +2139,8 @@ registerInfoCommand(program);
1863
2139
  registerSearchCommand(program);
1864
2140
  registerCloneCommand(program);
1865
2141
  registerPreviewCommand(program);
2142
+ registerLoginCommand(program);
2143
+ registerLogoutCommand(program);
1866
2144
  program.parseAsync().catch((err) => {
1867
2145
  if (err instanceof SnippetNotFoundError7) {
1868
2146
  error(err.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tbsten/mir",
3
- "version": "0.0.2-alpha03",
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": "*",