@moewolf/moe-icons-cli 0.0.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.
Files changed (103) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +32 -0
  3. package/bin/moeicons.js +20 -0
  4. package/dist/api/client.d.ts +42 -0
  5. package/dist/api/client.js +105 -0
  6. package/dist/auth/callback.d.ts +37 -0
  7. package/dist/auth/callback.js +72 -0
  8. package/dist/auth/device-login.d.ts +29 -0
  9. package/dist/auth/device-login.js +74 -0
  10. package/dist/auth/login.d.ts +41 -0
  11. package/dist/auth/login.js +85 -0
  12. package/dist/auth/open-browser.d.ts +2 -0
  13. package/dist/auth/open-browser.js +15 -0
  14. package/dist/auth/pkce.d.ts +17 -0
  15. package/dist/auth/pkce.js +20 -0
  16. package/dist/auth/token-store.d.ts +38 -0
  17. package/dist/auth/token-store.js +126 -0
  18. package/dist/catalog/catalog.d.ts +32 -0
  19. package/dist/catalog/catalog.js +80 -0
  20. package/dist/catalog/catalog.json +9371 -0
  21. package/dist/cli.d.ts +23 -0
  22. package/dist/cli.js +507 -0
  23. package/dist/commands/parser.d.ts +41 -0
  24. package/dist/commands/parser.js +146 -0
  25. package/dist/core/auth.d.ts +32 -0
  26. package/dist/core/auth.js +131 -0
  27. package/dist/core/bitmap-assets.d.ts +29 -0
  28. package/dist/core/bitmap-assets.js +77 -0
  29. package/dist/core/cli-update.d.ts +43 -0
  30. package/dist/core/cli-update.js +71 -0
  31. package/dist/core/context.d.ts +22 -0
  32. package/dist/core/context.js +1 -0
  33. package/dist/core/free-download.d.ts +66 -0
  34. package/dist/core/free-download.js +193 -0
  35. package/dist/core/generate.d.ts +33 -0
  36. package/dist/core/generate.js +285 -0
  37. package/dist/core/icon-names.d.ts +14 -0
  38. package/dist/core/icon-names.js +54 -0
  39. package/dist/core/init.d.ts +12 -0
  40. package/dist/core/init.js +23 -0
  41. package/dist/core/install.d.ts +67 -0
  42. package/dist/core/install.js +117 -0
  43. package/dist/core/library-update.d.ts +24 -0
  44. package/dist/core/library-update.js +161 -0
  45. package/dist/core/manage.d.ts +24 -0
  46. package/dist/core/manage.js +38 -0
  47. package/dist/core/pro-download.d.ts +29 -0
  48. package/dist/core/pro-download.js +112 -0
  49. package/dist/core/pro-install.d.ts +24 -0
  50. package/dist/core/pro-install.js +72 -0
  51. package/dist/core/release-descriptor.d.ts +40 -0
  52. package/dist/core/release-descriptor.js +114 -0
  53. package/dist/core/resource-variant.d.ts +35 -0
  54. package/dist/core/resource-variant.js +70 -0
  55. package/dist/core/signed-artifact.d.ts +29 -0
  56. package/dist/core/signed-artifact.js +81 -0
  57. package/dist/core/target-subtree.d.ts +49 -0
  58. package/dist/core/target-subtree.js +132 -0
  59. package/dist/core/update-policy.d.ts +31 -0
  60. package/dist/core/update-policy.js +27 -0
  61. package/dist/core/version-service.d.ts +26 -0
  62. package/dist/core/version-service.js +71 -0
  63. package/dist/core/wizard.d.ts +49 -0
  64. package/dist/core/wizard.js +71 -0
  65. package/dist/errors/errors.d.ts +34 -0
  66. package/dist/errors/errors.js +39 -0
  67. package/dist/errors/index.d.ts +1 -0
  68. package/dist/errors/index.js +1 -0
  69. package/dist/generator/cn.d.ts +5 -0
  70. package/dist/generator/cn.js +14 -0
  71. package/dist/generator/generate.d.ts +34 -0
  72. package/dist/generator/generate.js +562 -0
  73. package/dist/generator/theme-resolve.d.ts +26 -0
  74. package/dist/generator/theme-resolve.js +100 -0
  75. package/dist/mcp/server.d.ts +58 -0
  76. package/dist/mcp/server.js +176 -0
  77. package/dist/project/config.d.ts +74 -0
  78. package/dist/project/config.js +353 -0
  79. package/dist/project/detect.d.ts +26 -0
  80. package/dist/project/detect.js +104 -0
  81. package/dist/project/install-metadata.d.ts +29 -0
  82. package/dist/project/install-metadata.js +108 -0
  83. package/dist/project/install.d.ts +115 -0
  84. package/dist/project/install.js +537 -0
  85. package/dist/project/project-lock.d.ts +11 -0
  86. package/dist/project/project-lock.js +81 -0
  87. package/dist/project/tailwind.d.ts +51 -0
  88. package/dist/project/tailwind.js +188 -0
  89. package/dist/project/tar-gz.d.ts +12 -0
  90. package/dist/project/tar-gz.js +100 -0
  91. package/dist/tui/primitives.d.ts +36 -0
  92. package/dist/tui/primitives.js +88 -0
  93. package/dist/ui/banner.d.ts +11 -0
  94. package/dist/ui/banner.js +105 -0
  95. package/dist/ui/clack.d.ts +8 -0
  96. package/dist/ui/clack.js +53 -0
  97. package/dist/ui/create-ui.d.ts +14 -0
  98. package/dist/ui/create-ui.js +16 -0
  99. package/dist/ui/non-interactive.d.ts +5 -0
  100. package/dist/ui/non-interactive.js +30 -0
  101. package/dist/ui/stream.d.ts +11 -0
  102. package/dist/ui/stream.js +50 -0
  103. package/package.json +70 -0
package/dist/cli.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { MOEICONS_BANNER } from "./ui/banner.js";
2
+ import { type AuthUseCaseDependencies } from "./core/auth.js";
3
+ /**
4
+ * main(argv, runtime): parse args, select command/default wizard, catch typed
5
+ * errors, render once, return exit code. Only the executable wrapper calls
6
+ * process.exitCode.
7
+ */
8
+ export interface CliRuntime {
9
+ readonly cwd: () => string;
10
+ readonly stdout: (text: string) => void;
11
+ readonly stderr: (text: string) => void;
12
+ readonly env: Readonly<Record<string, string | undefined>>;
13
+ readonly isTTY: () => boolean;
14
+ readonly readLine?: (prompt: string) => Promise<string>;
15
+ readonly readKey?: () => Promise<string>;
16
+ readonly auth?: AuthUseCaseDependencies;
17
+ readonly fetchVersions?: () => Promise<readonly string[]>;
18
+ }
19
+ export { MOEICONS_BANNER };
20
+ export declare const BANNER: string;
21
+ /** Banner rendered as plain ASCII (fallback for narrow terminals). */
22
+ export declare function renderBanner(runtime: CliRuntime): void;
23
+ export declare function main(argv: readonly string[], runtime: CliRuntime): Promise<number>;
package/dist/cli.js ADDED
@@ -0,0 +1,507 @@
1
+ import { parseArgs, HELP_TEXT } from "./commands/parser.js";
2
+ import { CliError, isCliError, jsonErrorBody } from "./errors/index.js";
3
+ import { detectProject } from "./project/detect.js";
4
+ import { readMoeiconsConfig } from "./project/config.js";
5
+ import { mkdirSync, writeFileSync, readFileSync, existsSync, renameSync, rmSync, readdirSync, copyFileSync, } from "node:fs";
6
+ import { homedir } from "node:os";
7
+ import { join } from "node:path";
8
+ import { runInitUseCase } from "./core/init.js";
9
+ import { runGenerateUseCase } from "./core/generate.js";
10
+ import { runInstallUseCase } from "./core/install.js";
11
+ import { runWizardUseCase } from "./core/wizard.js";
12
+ import { createCommandUi } from "./ui/create-ui.js";
13
+ import { CLI_VERSION, MOEICONS_BANNER, renderBannerText, renderNoticeBox } from "./ui/banner.js";
14
+ import { runAccountUseCase, runLoginUseCase, runLogoutUseCase, runSessionStatusUseCase, } from "./core/auth.js";
15
+ import { formatLibraryVersionStatus, getLibraryVersionStatus } from "./core/manage.js";
16
+ import { runCliUpdateCheck } from "./core/cli-update.js";
17
+ import { fetchLibraryVersions } from "./core/version-service.js";
18
+ import { runProInstallUseCase } from "./core/pro-install.js";
19
+ import { runLibraryUpdateUseCase } from "./core/library-update.js";
20
+ function commandContext(runtime, flags) {
21
+ const streams = runtime.readLine !== undefined && runtime.readKey !== undefined
22
+ ? {
23
+ isTTY: runtime.isTTY,
24
+ stdout: runtime.stdout,
25
+ readLine: runtime.readLine,
26
+ readKey: runtime.readKey,
27
+ }
28
+ : undefined;
29
+ return {
30
+ ui: createCommandUi({
31
+ json: flags.json,
32
+ yes: flags.yes,
33
+ isTTY: runtime.isTTY(),
34
+ ...(streams ? { streams } : {}),
35
+ }),
36
+ cwd: runtime.cwd(),
37
+ env: runtime.env,
38
+ signal: new AbortController().signal,
39
+ now: () => new Date(),
40
+ };
41
+ }
42
+ export { MOEICONS_BANNER };
43
+ export const BANNER = MOEICONS_BANNER;
44
+ /** Banner rendered as plain ASCII (fallback for narrow terminals). */
45
+ export function renderBanner(runtime) {
46
+ runtime.stdout(renderBannerText());
47
+ }
48
+ function argvRequestsJson(argv) {
49
+ return argv.includes("--json");
50
+ }
51
+ function writeJson(runtime, value) {
52
+ runtime.stdout(JSON.stringify(value));
53
+ }
54
+ function reportFailure(runtime, json, error) {
55
+ if (isCliError(error)) {
56
+ if (json)
57
+ writeJson(runtime, jsonErrorBody(error.code, error.message));
58
+ else
59
+ runtime.stderr(`error: ${error.message}\n`);
60
+ return error.exitCode;
61
+ }
62
+ const message = error instanceof Error ? error.message : String(error);
63
+ if (json)
64
+ writeJson(runtime, jsonErrorBody("UNEXPECTED", message));
65
+ else
66
+ runtime.stderr(`unexpected error: ${message}\n`);
67
+ return 5;
68
+ }
69
+ export async function main(argv, runtime) {
70
+ await Promise.resolve();
71
+ const jsonHint = argvRequestsJson(argv);
72
+ let parsed;
73
+ try {
74
+ parsed = parseArgs(argv);
75
+ }
76
+ catch (error) {
77
+ return reportFailure(runtime, jsonHint, error);
78
+ }
79
+ try {
80
+ return await dispatchSync(parsed.command, runtime, parsed.json, parsed.yes, parsed.noTailwind, parsed.target);
81
+ }
82
+ catch (error) {
83
+ return reportFailure(runtime, parsed.json, error);
84
+ }
85
+ }
86
+ async function dispatchSync(command, runtime, json, yes, noTailwind, target) {
87
+ switch (command.name) {
88
+ case "version":
89
+ if (json)
90
+ writeJson(runtime, { ok: true, version: versionString() });
91
+ else
92
+ runtime.stdout(`${versionString()}\n`);
93
+ return 0;
94
+ case "help":
95
+ runtime.stdout(HELP_TEXT);
96
+ return 0;
97
+ case "wizard":
98
+ return await runWizard(runtime, json, yes);
99
+ case "install":
100
+ return await runInstall(command.group, runtime, json, noTailwind, undefined, undefined, command.target);
101
+ case "login":
102
+ return await runLogin(runtime, json, yes);
103
+ case "logout":
104
+ return await runLogout(runtime, json);
105
+ case "account":
106
+ return await runAccount(runtime, json);
107
+ case "groups":
108
+ throw new CliError("NOT_IMPLEMENTED", "groups is not implemented yet");
109
+ case "generate":
110
+ return await runGenerate(runtime, json, noTailwind, false, target, yes);
111
+ case "init":
112
+ return runInit(runtime, json, target);
113
+ case "mcp":
114
+ void runMcp(runtime);
115
+ return 0;
116
+ }
117
+ }
118
+ async function runLogin(runtime, json, yes) {
119
+ const session = await runLoginUseCase(commandContext(runtime, { json, yes }), {
120
+ ...runtime.auth,
121
+ fileFallbackAllowed: runtime.isTTY(),
122
+ });
123
+ if (json)
124
+ writeJson(runtime, { ok: true, account: session });
125
+ else
126
+ runtime.stdout(`Logged in as ${session.accountId}\n`);
127
+ return 0;
128
+ }
129
+ async function runAccount(runtime, json) {
130
+ const session = await runAccountUseCase(commandContext(runtime, { json, yes: false }), runtime.auth);
131
+ if (json)
132
+ writeJson(runtime, { ok: true, account: session });
133
+ else
134
+ runtime.stdout(`Account: ${session.accountId}\nSession expires: ${new Date(session.expiresAt).toISOString()}\n`);
135
+ return 0;
136
+ }
137
+ async function runLogout(runtime, json) {
138
+ const result = await runLogoutUseCase(commandContext(runtime, { json, yes: false }), runtime.auth);
139
+ if (json)
140
+ writeJson(runtime, { ok: true, ...result });
141
+ else
142
+ runtime.stdout(result.revoked
143
+ ? "Logged out.\n"
144
+ : "Logged out locally; remote revocation was not confirmed.\n");
145
+ return 0;
146
+ }
147
+ /** Create moeicons.config.jsonc if absent (never overwrites an existing config). */
148
+ function runInit(runtime, json, target) {
149
+ const result = runInitUseCase(commandContext(runtime, { json, yes: false }), { mkdirSync, writeFileSync, existsSync, renameSync, rmSync }, target);
150
+ if (!result.ok && result.reason === "exists") {
151
+ if (json)
152
+ writeJson(runtime, { ok: true, alreadyExisted: true });
153
+ else
154
+ runtime.stdout("moeicons.config.jsonc already exists; not overwritten.\n");
155
+ return 0;
156
+ }
157
+ if (!result.ok) {
158
+ throw new CliError("VALIDATION_ERROR", result.reason === "no-project" ? "no project found" : "init failed");
159
+ }
160
+ if (json)
161
+ writeJson(runtime, { ok: true, created: result.created });
162
+ else
163
+ runtime.stdout(`Created ${result.created}\n`);
164
+ return 0;
165
+ }
166
+ /** Start the MCP stdio server; protocol data to stdout, logs to stderr. */
167
+ async function runMcp(runtime) {
168
+ const { runMcpStdio } = await import("./mcp/server.js");
169
+ const readline = await import("node:readline");
170
+ const services = {
171
+ listIconGroups: () => Promise.resolve([
172
+ { id: "free", displayName: "Free icons" },
173
+ { id: "pro", displayName: "Pro icons" },
174
+ ]),
175
+ getAccount: () => Promise.resolve(undefined),
176
+ installIconGroup: (args) => {
177
+ runtime.stderr(`installing ${args.groupId} into ${args.projectPath} (stub)\n`);
178
+ return Promise.resolve({ ok: true, message: `installed ${args.groupId}` });
179
+ },
180
+ };
181
+ const rl = readline.createInterface({ input: process.stdin });
182
+ await runMcpStdio({ services, stdout: runtime.stdout, stderr: runtime.stderr, lines: rl });
183
+ }
184
+ function versionString() {
185
+ return CLI_VERSION;
186
+ }
187
+ function renderCliUpdateNotice(update) {
188
+ const latest = update.latestVersion ?? "unavailable";
189
+ const instruction = update.instruction ?? (update.status === "current" ? "already up to date" : "unavailable");
190
+ const prompt = update.status === "update"
191
+ ? "A newer Moeicons CLI is available. Update with the command below."
192
+ : "Moeicons CLI version status";
193
+ return renderNoticeBox([
194
+ prompt,
195
+ `Current ${update.currentVersion} / Latest ${latest} / Update: ${instruction}`,
196
+ ]);
197
+ }
198
+ /** Guided flow: Free / Pro / Login, with project-root confirmation before write. */
199
+ async function runWizard(runtime, json, yes) {
200
+ if (!json && runtime.isTTY()) {
201
+ renderBanner(runtime);
202
+ runtime.stdout(`CLI ${versionString()}\n`);
203
+ try {
204
+ const update = await runCliUpdateCheck({
205
+ currentVersion: versionString(),
206
+ cwd: runtime.cwd(),
207
+ env: runtime.env,
208
+ fs: { existsSync, readFileSync: (path) => readFileSync(path, "utf8") },
209
+ timeoutMs: 1_000,
210
+ ...(runtime.fetchVersions ? { fetchVersions: runtime.fetchVersions } : {}),
211
+ });
212
+ runtime.stdout(`${renderCliUpdateNotice(update)}\n`);
213
+ }
214
+ catch {
215
+ runtime.stdout(`${renderNoticeBox(["Moeicons CLI version status", `Current ${versionString()} / Latest unavailable / Update: unavailable`])}\n`);
216
+ }
217
+ }
218
+ const context = commandContext(runtime, { json, yes });
219
+ const session = await runSessionStatusUseCase(context, runtime.auth).catch((error) => ({
220
+ kind: "unknown",
221
+ reason: error instanceof Error ? error.message : "session status unavailable",
222
+ }));
223
+ const getStatus = async () => {
224
+ const project = detectProject(runtime.cwd());
225
+ if (!project)
226
+ return "Current: invalid / Latest: unavailable / Status: no project";
227
+ const config = readMoeiconsConfig(project.root);
228
+ if (config.kind !== "ok")
229
+ return `Current: invalid / Latest: unavailable / Status: config ${config.kind}`;
230
+ return formatLibraryVersionStatus(await getLibraryVersionStatus(project.root, config.config.tier));
231
+ };
232
+ const result = await runWizardUseCase(context, {
233
+ json,
234
+ session: session.kind,
235
+ getLibraryStatus: getStatus,
236
+ });
237
+ if (result.ok && result.action === "json-hint") {
238
+ writeJson(runtime, { ok: true, message: result.message });
239
+ return 0;
240
+ }
241
+ if (!result.ok) {
242
+ throw new CliError("CANCELLED", "cancelled");
243
+ }
244
+ if (result.action === "install") {
245
+ const project = detectProject(runtime.cwd());
246
+ if (project)
247
+ runtime.stdout(`Project root: ${project.root}\n`);
248
+ return await runInstall(result.group, runtime, false, false, undefined, undefined, result.target);
249
+ }
250
+ if (result.action === "settings") {
251
+ if (result.flow === "logout")
252
+ return runLogout(runtime, false);
253
+ const update = await runCliUpdateCheck({
254
+ currentVersion: versionString(),
255
+ cwd: runtime.cwd(),
256
+ env: runtime.env,
257
+ fs: { existsSync, readFileSync: (path) => readFileSync(path, "utf8") },
258
+ signal: context.signal,
259
+ timeoutMs: 1_000,
260
+ ...(runtime.fetchVersions ? { fetchVersions: runtime.fetchVersions } : {}),
261
+ }).catch(() => undefined);
262
+ if (!update) {
263
+ runtime.stdout(`${renderNoticeBox(["Moeicons CLI version status", `Current ${versionString()} / Latest unavailable / Update: unavailable`])}\n`);
264
+ return 0;
265
+ }
266
+ if (update.status === "update")
267
+ runtime.stdout(`CLI ${update.currentVersion} → ${update.latestVersion}\nRun: ${update.instruction}\n`);
268
+ else if (update.status === "current")
269
+ runtime.stdout(`CLI ${update.currentVersion} is up to date.\n`);
270
+ else
271
+ runtime.stdout(`Unable to find a CLI release in the current channel.\n`);
272
+ return 0;
273
+ }
274
+ if (result.action === "manage") {
275
+ if (result.flow === "reload")
276
+ return await runGenerate(runtime, false, false, true, undefined, yes);
277
+ const project = detectProject(runtime.cwd());
278
+ if (!project)
279
+ throw new CliError("VALIDATION_ERROR", "no project found");
280
+ const config = readMoeiconsConfig(project.root);
281
+ if (config.kind !== "ok")
282
+ throw new CliError("VALIDATION_ERROR", `config ${config.kind}`);
283
+ const status = await getLibraryVersionStatus(project.root, config.config.tier);
284
+ runtime.stdout(`${formatLibraryVersionStatus(status)}\n`);
285
+ if (status.kind !== "update")
286
+ return 0;
287
+ return runLibraryUpdate(runtime, status.metadata.tier, status.latestVersion, status.latestDescriptorSha256);
288
+ }
289
+ if (result.flow === "login")
290
+ return runLogin(runtime, false, yes);
291
+ return 0;
292
+ }
293
+ async function runLibraryUpdate(runtime, tier, version, descriptorSha256) {
294
+ const context = commandContext(runtime, { json: false, yes: false });
295
+ const progress = context.ui.progress("Downloading icon library update", context.signal);
296
+ try {
297
+ const result = await runLibraryUpdateUseCase(context, {
298
+ fs: {
299
+ mkdirSync,
300
+ writeFileSync,
301
+ readFileSync,
302
+ existsSync,
303
+ renameSync,
304
+ rmSync,
305
+ readdirSync,
306
+ copyFileSync,
307
+ },
308
+ free: {
309
+ fetchFn: globalThis.fetch.bind(globalThis),
310
+ readFileSync: (path) => new Uint8Array(readFileSync(path)),
311
+ writeFileSync: (path, data) => writeFileSync(path, data),
312
+ mkdirSync: (path) => mkdirSync(path, { recursive: true }),
313
+ existsSync,
314
+ ...(runtime.env.MOEICONS_FREE_RELEASE_DIR
315
+ ? { fixtureDir: runtime.env.MOEICONS_FREE_RELEASE_DIR }
316
+ : {}),
317
+ cacheDir: runtime.env.MOEICONS_CACHE_DIR ?? join(homedir(), ".moeicons", "cache"),
318
+ cliVersion: versionString(),
319
+ },
320
+ auth: runtime.auth ?? {},
321
+ fetch: runtime.auth?.fetch ?? globalThis.fetch.bind(globalThis),
322
+ onProgress: ({ downloadedBytes, totalBytes }) => progress.update?.(`Downloaded ${downloadedBytes} bytes${totalBytes ? ` of ${totalBytes}` : ""}`),
323
+ }, { tier, version, descriptorSha256 });
324
+ progress.stop("Icon library update complete");
325
+ runtime.stdout(`Updated ${tier} artifact to ${result.artifactVersion}; reconciled ${result.files.length} generated files.\n`);
326
+ return 0;
327
+ }
328
+ catch (error) {
329
+ progress.stop("Icon library update stopped");
330
+ throw error;
331
+ }
332
+ }
333
+ /** Free install orchestration: download/verify then map to JSON/human output. */
334
+ async function runInstall(group, runtime, json, noTailwind, sourceVersion, expectedDescriptorSha256, target) {
335
+ const context = commandContext(runtime, { json, yes: false });
336
+ const progress = context.ui.progress("Downloading icon library", context.signal);
337
+ if (group === "pro" || group === "ent") {
338
+ const identity = sourceVersion && expectedDescriptorSha256
339
+ ? { version: sourceVersion, descriptorSha256: expectedDescriptorSha256 }
340
+ : await fetchLibraryVersions({
341
+ signal: context.signal,
342
+ ...(runtime.auth?.fetch ? { fetch: runtime.auth.fetch } : {}),
343
+ }).then((versions) => {
344
+ if (!versions.pro)
345
+ throw new CliError("NOT_FOUND", "no pro release is published");
346
+ return {
347
+ version: versions.pro.version,
348
+ descriptorSha256: versions.pro.descriptorSha256,
349
+ };
350
+ });
351
+ try {
352
+ const result = await runProInstallUseCase(context, {
353
+ fs: { mkdirSync, writeFileSync, existsSync, renameSync, rmSync },
354
+ auth: runtime.auth ?? {},
355
+ fetch: runtime.auth?.fetch ?? globalThis.fetch.bind(globalThis),
356
+ onProgress: ({ downloadedBytes, totalBytes }) => {
357
+ const percent = totalBytes
358
+ ? ` (${Math.min(100, Math.floor((downloadedBytes / totalBytes) * 100))}%)`
359
+ : "";
360
+ progress.update?.(`Downloaded ${downloadedBytes} bytes${totalBytes ? ` of ${totalBytes}` : ""}${percent}`);
361
+ },
362
+ }, { ...identity, ...(target ? { target } : {}) });
363
+ progress.stop("Icon library download complete");
364
+ if (json)
365
+ writeJson(runtime, { ok: true, group: "pro", ...result });
366
+ else
367
+ runtime.stdout(`Installed pro artifact ${result.artifactVersion} into ${result.projectRoot}.\n`);
368
+ return 0;
369
+ }
370
+ catch (error) {
371
+ progress.stop("Icon library download stopped");
372
+ throw error;
373
+ }
374
+ }
375
+ const result = await runInstallUseCase(context, {
376
+ fs: { mkdirSync, writeFileSync, existsSync, renameSync, rmSync },
377
+ download: {
378
+ fetchFn: globalThis.fetch.bind(globalThis),
379
+ readFileSync: (path) => new Uint8Array(readFileSync(path)),
380
+ writeFileSync: (path, data) => writeFileSync(path, data),
381
+ mkdirSync: (path) => mkdirSync(path, { recursive: true }),
382
+ existsSync,
383
+ ...(runtime.env.MOEICONS_FREE_RELEASE_DIR
384
+ ? { fixtureDir: runtime.env.MOEICONS_FREE_RELEASE_DIR }
385
+ : {}),
386
+ cacheDir: runtime.env.MOEICONS_CACHE_DIR ?? join(homedir(), ".moeicons", "cache"),
387
+ cliVersion: versionString(),
388
+ onProgress: ({ downloadedBytes, totalBytes }) => {
389
+ const percent = totalBytes
390
+ ? ` (${Math.min(100, Math.floor((downloadedBytes / totalBytes) * 100))}%)`
391
+ : "";
392
+ progress.update?.(`Downloaded ${downloadedBytes} bytes${totalBytes ? ` of ${totalBytes}` : ""}${percent}`);
393
+ },
394
+ },
395
+ }, {
396
+ ...(group === undefined ? {} : { group }),
397
+ ...(target ? { target } : {}),
398
+ ...(sourceVersion ? { sourceVersion } : {}),
399
+ ...(expectedDescriptorSha256 ? { expectedDescriptorSha256 } : {}),
400
+ });
401
+ progress.stop(result.ok ? "Icon library download complete" : "Icon library download stopped");
402
+ if (!result.ok && result.reason === "no-project") {
403
+ throw new CliError("VALIDATION_ERROR", "no package.json found in the current directory or parents; run inside a project");
404
+ }
405
+ if (!result.ok && result.reason === "cancelled") {
406
+ throw new CliError("CANCELLED", result.message);
407
+ }
408
+ if (!result.ok && result.reason === "checksum-mismatch") {
409
+ throw new CliError("VALIDATION_ERROR", result.message);
410
+ }
411
+ if (!result.ok && (result.reason === "network" || result.reason === "offline-no-cache")) {
412
+ throw new CliError("NETWORK_ERROR", result.message);
413
+ }
414
+ if (!result.ok && result.reason === "not-found") {
415
+ throw new CliError("NOT_FOUND", result.message);
416
+ }
417
+ if (!result.ok && result.reason === "validation") {
418
+ throw new CliError("VALIDATION_ERROR", result.message);
419
+ }
420
+ if (!result.ok) {
421
+ throw new CliError("UNEXPECTED", result.message);
422
+ }
423
+ if (json) {
424
+ writeJson(runtime, {
425
+ ok: true,
426
+ projectRoot: result.projectRoot,
427
+ packageManager: result.packageManager,
428
+ group: result.group,
429
+ zipBytes: result.artifactBytes,
430
+ artifactBytes: result.artifactBytes,
431
+ planItems: result.planItems,
432
+ config: result.config,
433
+ artifactVersion: result.artifactVersion,
434
+ descriptorSha256: result.descriptorSha256,
435
+ catalogSha256: result.catalogSha256,
436
+ cacheHit: result.cacheHit,
437
+ });
438
+ }
439
+ else {
440
+ runtime.stdout(`Project root: ${result.projectRoot}\n`);
441
+ runtime.stdout(`Group: ${result.group}\n`);
442
+ runtime.stdout(`Artifact: ${result.artifactVersion}\n`);
443
+ runtime.stdout(`Config state: ${result.config}\n`);
444
+ runtime.stdout(`Installed free artifact metadata to .moeicons (cacheHit=${String(result.cacheHit)}).\n`);
445
+ }
446
+ return 0;
447
+ }
448
+ function generateFailureCode(reason) {
449
+ if (reason === "cancelled")
450
+ return "CANCELLED";
451
+ if (reason === "validation" || reason === "no-project" || reason.startsWith("config state:")) {
452
+ return "VALIDATION_ERROR";
453
+ }
454
+ return "UNEXPECTED";
455
+ }
456
+ /** Generate proxy components from config. */
457
+ async function runGenerate(runtime, json, noTailwind, reconcileInstalled = false, target, yes = false) {
458
+ const result = await runGenerateUseCase(commandContext(runtime, { json, yes }), {
459
+ mkdirSync,
460
+ writeFileSync,
461
+ readFileSync,
462
+ existsSync,
463
+ renameSync,
464
+ rmSync,
465
+ readdirSync,
466
+ copyFileSync,
467
+ }, { noTailwind, reconcileInstalled, ...(target ? { target } : {}) });
468
+ if (!result.ok) {
469
+ if (result.code === "TAILWIND_VERSION_UNSUPPORTED") {
470
+ if (json)
471
+ writeJson(runtime, jsonErrorBody("TAILWIND_VERSION_UNSUPPORTED", result.reason));
472
+ else
473
+ runtime.stderr(`error: ${result.reason}\n`);
474
+ return 1;
475
+ }
476
+ const code = generateFailureCode(result.reason);
477
+ const message = result.reason;
478
+ if (json) {
479
+ writeJson(runtime, {
480
+ ...jsonErrorBody(code, message),
481
+ ...(result.errors ? { errors: result.errors } : {}),
482
+ });
483
+ }
484
+ else {
485
+ runtime.stderr(`error: ${message}\n`);
486
+ if (result.errors) {
487
+ for (const item of result.errors)
488
+ runtime.stderr(`${item}\n`);
489
+ }
490
+ }
491
+ return 1;
492
+ }
493
+ if (json)
494
+ writeJson(runtime, {
495
+ ok: true,
496
+ generated: result.files,
497
+ ...(result.warnings ? { warnings: result.warnings } : {}),
498
+ });
499
+ else {
500
+ runtime.stdout(`Generated ${result.files.length} files.\n`);
501
+ if (result.warnings) {
502
+ for (const warning of result.warnings)
503
+ runtime.stderr(`${warning}\n`);
504
+ }
505
+ }
506
+ return 0;
507
+ }
@@ -0,0 +1,41 @@
1
+ export type Command = {
2
+ readonly name: "install";
3
+ readonly group?: string;
4
+ readonly target?: Target;
5
+ } | {
6
+ readonly name: "login";
7
+ } | {
8
+ readonly name: "logout";
9
+ } | {
10
+ readonly name: "account";
11
+ } | {
12
+ readonly name: "groups";
13
+ } | {
14
+ readonly name: "generate";
15
+ } | {
16
+ readonly name: "init";
17
+ } | {
18
+ readonly name: "mcp";
19
+ } | {
20
+ readonly name: "help";
21
+ } | {
22
+ readonly name: "version";
23
+ } | {
24
+ readonly name: "wizard";
25
+ };
26
+ export interface ParseResult {
27
+ readonly command: Command;
28
+ /** Non-interactive/JSON mode flag. */
29
+ readonly json: boolean;
30
+ readonly yes: boolean;
31
+ /** Skip Tailwind config auto-integration (H4). */
32
+ readonly noTailwind: boolean;
33
+ readonly target?: Target;
34
+ }
35
+ export type Target = "react" | "vue" | "vanilla" | "assets";
36
+ /**
37
+ * Parse argv into a Command via Commander. Never calls `process.exit()`.
38
+ * `moeicons --pro` / `--ent` legacy aliases map to install for the compatibility window.
39
+ */
40
+ export declare function parseArgs(argv: readonly string[]): ParseResult;
41
+ export declare const HELP_TEXT = "moeicons \u2014 Moeicons icon library CLI\n\nUsage:\n moeicons interactive guided flow (free / pro / login)\n moeicons install [group] install an icon group\n moeicons login browser login (PKCE)\n moeicons logout clear local session\n moeicons account show account/tier info\n moeicons groups list available icon groups\n moeicons generate generate React/Vue proxy components\n moeicons init create moeicons.config.json\n moeicons mcp start the MCP stdio server\n moeicons --version show version\n moeicons --help show help\n\nOptions:\n --json machine-readable JSON output\n --yes skip confirmations in noninteractive mode\n --target output target: react, vue, vanilla, or assets\n --no-tailwind skip Tailwind config auto-integration\n";