@moewolf/moe-icons-cli 0.0.1 → 0.0.2

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 (151) hide show
  1. package/README.md +243 -9
  2. package/bin/check-node.cjs +36 -0
  3. package/bin/moeicons.js +12 -1
  4. package/dist/api/client.d.ts +13 -2
  5. package/dist/api/client.js +145 -21
  6. package/dist/auth/device-login.d.ts +10 -7
  7. package/dist/auth/device-login.js +60 -37
  8. package/dist/auth/login-schemas.d.ts +24 -0
  9. package/dist/auth/login-schemas.js +67 -0
  10. package/dist/catalog/catalog.d.ts +11 -0
  11. package/dist/catalog/catalog.js +43 -0
  12. package/dist/catalog/catalog.json +538 -3395
  13. package/dist/cli.d.ts +11 -2
  14. package/dist/cli.js +773 -147
  15. package/dist/commands/parser.d.ts +13 -2
  16. package/dist/commands/parser.js +138 -50
  17. package/dist/config-package/generated/SOURCE.json +25 -0
  18. package/dist/config-package/generated/moeicons-config.schema.json +191 -0
  19. package/dist/config-package/generated/moeicons.config.jsonc +42 -0
  20. package/dist/config-package/generated/render-config.cjs +145 -0
  21. package/dist/config-package/generated/validate-config.cjs +293 -0
  22. package/dist/config-package/generated-config.d.ts +32 -0
  23. package/dist/config-package/generated-config.js +19 -0
  24. package/dist/core/auth.d.ts +24 -0
  25. package/dist/core/auth.js +127 -8
  26. package/dist/core/bitmap-assets.d.ts +19 -5
  27. package/dist/core/bitmap-assets.js +40 -31
  28. package/dist/core/bitmap-shard-budget.d.ts +43 -0
  29. package/dist/core/bitmap-shard-budget.js +16 -0
  30. package/dist/core/bitmap-shard-cache.d.ts +68 -0
  31. package/dist/core/bitmap-shard-cache.js +115 -0
  32. package/dist/core/bitmap-shard-download.d.ts +130 -0
  33. package/dist/core/bitmap-shard-download.js +359 -0
  34. package/dist/core/bitmap-shard-resolver.d.ts +129 -0
  35. package/dist/core/bitmap-shard-resolver.js +262 -0
  36. package/dist/core/bitmap-shards.d.ts +70 -0
  37. package/dist/core/bitmap-shards.js +249 -0
  38. package/dist/core/bootstrap.d.ts +37 -0
  39. package/dist/core/bootstrap.js +54 -0
  40. package/dist/core/cache.d.ts +34 -0
  41. package/dist/core/cache.js +96 -0
  42. package/dist/core/context.d.ts +26 -0
  43. package/dist/core/doctor.d.ts +48 -0
  44. package/dist/core/doctor.js +190 -0
  45. package/dist/core/free-download.d.ts +64 -6
  46. package/dist/core/free-download.js +402 -30
  47. package/dist/core/generate.d.ts +29 -0
  48. package/dist/core/generate.js +341 -65
  49. package/dist/core/icon-selection.d.ts +8 -0
  50. package/dist/core/icon-selection.js +19 -0
  51. package/dist/core/install.d.ts +14 -0
  52. package/dist/core/install.js +132 -13
  53. package/dist/core/library-update.d.ts +5 -1
  54. package/dist/core/library-update.js +176 -17
  55. package/dist/core/local-test-env.d.ts +24 -0
  56. package/dist/core/local-test-env.js +43 -0
  57. package/dist/core/metadata-sync.d.ts +30 -0
  58. package/dist/core/metadata-sync.js +97 -0
  59. package/dist/core/pro-download.d.ts +63 -0
  60. package/dist/core/pro-download.js +247 -27
  61. package/dist/core/pro-install.d.ts +11 -3
  62. package/dist/core/pro-install.js +181 -19
  63. package/dist/core/pro-resources.d.ts +79 -0
  64. package/dist/core/pro-resources.js +204 -0
  65. package/dist/core/pro-selected-download.d.ts +18 -0
  66. package/dist/core/pro-selected-download.js +269 -0
  67. package/dist/core/release-descriptor.d.ts +42 -1
  68. package/dist/core/release-descriptor.js +117 -9
  69. package/dist/core/resource-variant.d.ts +27 -10
  70. package/dist/core/resource-variant.js +75 -34
  71. package/dist/core/selected-resources.d.ts +59 -0
  72. package/dist/core/selected-resources.js +350 -0
  73. package/dist/core/signed-artifact.d.ts +8 -1
  74. package/dist/core/signed-artifact.js +35 -11
  75. package/dist/core/target-subtree.d.ts +8 -0
  76. package/dist/core/target-subtree.js +54 -3
  77. package/dist/core/version-service.d.ts +11 -0
  78. package/dist/core/version-service.js +129 -24
  79. package/dist/core/wizard.d.ts +20 -17
  80. package/dist/core/wizard.js +66 -28
  81. package/dist/errors/errors.d.ts +2 -1
  82. package/dist/errors/errors.js +1 -0
  83. package/dist/generator/generate.d.ts +1 -1
  84. package/dist/generator/generate.js +420 -213
  85. package/dist/generator/shared/SOURCE.json +7 -0
  86. package/dist/generator/shared/svg-model.cjs +156 -0
  87. package/dist/generator/svg-model.d.ts +16 -0
  88. package/dist/generator/svg-model.js +4 -0
  89. package/dist/generator/theme-resolve.d.ts +2 -0
  90. package/dist/generator/theme-resolve.js +57 -26
  91. package/dist/mcp/server.d.ts +23 -36
  92. package/dist/mcp/server.js +74 -152
  93. package/dist/mcp/services.d.ts +4 -0
  94. package/dist/mcp/services.js +230 -0
  95. package/dist/metadata/archive.d.ts +38 -0
  96. package/dist/metadata/archive.js +94 -0
  97. package/dist/metadata/manifest.d.ts +33 -0
  98. package/dist/metadata/manifest.js +136 -0
  99. package/dist/metadata/version.d.ts +22 -0
  100. package/dist/metadata/version.js +53 -0
  101. package/dist/project/anchors/application.d.ts +13 -0
  102. package/dist/project/anchors/application.js +377 -0
  103. package/dist/project/anchors/apply.d.ts +28 -0
  104. package/dist/project/anchors/apply.js +65 -0
  105. package/dist/project/anchors/diagnose.d.ts +26 -0
  106. package/dist/project/anchors/diagnose.js +90 -0
  107. package/dist/project/anchors/helpers.d.ts +20 -0
  108. package/dist/project/anchors/helpers.js +55 -0
  109. package/dist/project/anchors/moeicons-config.d.ts +21 -0
  110. package/dist/project/anchors/moeicons-config.js +83 -0
  111. package/dist/project/anchors/parse.d.ts +14 -0
  112. package/dist/project/anchors/parse.js +36 -0
  113. package/dist/project/anchors/project-manifest.d.ts +19 -0
  114. package/dist/project/anchors/project-manifest.js +109 -0
  115. package/dist/project/anchors/style.d.ts +12 -0
  116. package/dist/project/anchors/style.js +87 -0
  117. package/dist/project/anchors/types.d.ts +32 -0
  118. package/dist/project/anchors/types.js +9 -0
  119. package/dist/project/config.d.ts +59 -5
  120. package/dist/project/config.js +234 -102
  121. package/dist/project/install-metadata.d.ts +23 -2
  122. package/dist/project/install-metadata.js +58 -7
  123. package/dist/project/install.d.ts +24 -4
  124. package/dist/project/install.js +402 -190
  125. package/dist/project/project-lock.d.ts +2 -2
  126. package/dist/project/project-lock.js +4 -4
  127. package/dist/project/tailwind.d.ts +2 -2
  128. package/dist/project/tailwind.js +3 -4
  129. package/dist/project/tar-gz.d.ts +18 -1
  130. package/dist/project/tar-gz.js +40 -3
  131. package/dist/tui/components.d.ts +62 -0
  132. package/dist/tui/components.js +100 -0
  133. package/dist/tui/primitives.d.ts +1 -0
  134. package/dist/tui/primitives.js +8 -1
  135. package/dist/ui/banner.d.ts +31 -3
  136. package/dist/ui/banner.js +120 -25
  137. package/dist/ui/branded-prompts.d.ts +41 -0
  138. package/dist/ui/branded-prompts.js +80 -0
  139. package/dist/ui/clack.d.ts +6 -3
  140. package/dist/ui/clack.js +56 -5
  141. package/dist/ui/create-ui.d.ts +1 -0
  142. package/dist/ui/create-ui.js +5 -1
  143. package/dist/ui/generated/wordmark-large.d.ts +17 -0
  144. package/dist/ui/generated/wordmark-large.js +28 -0
  145. package/dist/ui/generated/wordmark.d.ts +2 -0
  146. package/dist/ui/generated/wordmark.js +2 -0
  147. package/dist/ui/non-interactive.js +8 -0
  148. package/dist/ui/stream.js +30 -0
  149. package/dist/ui/theme.d.ts +42 -0
  150. package/dist/ui/theme.js +59 -0
  151. package/package.json +14 -6
@@ -1,5 +1,11 @@
1
+ import { type BitmapShard } from "../core/bitmap-shards.js";
1
2
  export interface InstallMetadata {
2
3
  readonly schemaVersion: 1;
4
+ readonly delivery?: {
5
+ readonly mode: "icons";
6
+ readonly indexSha256: string;
7
+ readonly bundleSha256: string;
8
+ };
3
9
  readonly artifactVersion: string;
4
10
  readonly tier: "free" | "pro";
5
11
  readonly target: "react" | "vue" | "vanilla" | "assets";
@@ -8,10 +14,25 @@ export interface InstallMetadata {
8
14
  readonly catalogSha256: string;
9
15
  readonly installedAt: string;
10
16
  readonly managedFiles: Readonly<Record<string, string>>;
17
+ /** Last successfully generated project proxy root; used for safe migrations. */
18
+ readonly generatedOutputDir?: string;
11
19
  /** Verified target subtree hash/size recorded at install time. */
12
20
  readonly targetSha256?: string;
13
21
  readonly targetFileCount?: number;
14
22
  readonly targetByteCount?: number;
23
+ /**
24
+ * DEV-G07: the exact bitmap shard identities selected by the config at
25
+ * install time. generate/repair/offline re-verify these pins before reuse.
26
+ */
27
+ readonly bitmapShards?: readonly BitmapShard[];
28
+ readonly bitmapShardSetSha256?: string;
29
+ /** A-1b: present only for an installed local-test candidate. */
30
+ readonly channel?: "local-test";
31
+ readonly publishable?: boolean;
32
+ }
33
+ export interface InstallMetadataParseOptions {
34
+ /** A-1b: accept a declared `X.Y.Z-test` local-test candidate (local context only). */
35
+ readonly allowLocalTest?: boolean;
15
36
  }
16
37
  export type InstalledResourceState = {
17
38
  readonly kind: "ok";
@@ -24,6 +45,6 @@ export type InstalledResourceState = {
24
45
  readonly message: string;
25
46
  };
26
47
  export declare function sha256Bytes(value: string | Uint8Array): string;
27
- export declare function parseInstallMetadata(raw: string): InstallMetadata | undefined;
48
+ export declare function parseInstallMetadata(raw: string, opts?: InstallMetadataParseOptions): InstallMetadata | undefined;
28
49
  export declare function serializeInstallMetadata(metadata: InstallMetadata): string;
29
- export declare function readInstalledResourceState(projectRoot: string, expectedTier?: "free" | "pro"): InstalledResourceState;
50
+ export declare function readInstalledResourceState(projectRoot: string, expectedTier?: "free" | "pro", opts?: InstallMetadataParseOptions): InstalledResourceState;
@@ -1,8 +1,10 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
+ import { bitmapShardSetSha256, parseBitmapShard } from "../core/bitmap-shards.js";
4
5
  const SHA256 = /^[a-f0-9]{64}$/;
5
6
  const VERSION = /^\d+\.\d+\.\d+(?:-(?:alpha|beta))?$/;
7
+ const LOCAL_TEST_VERSION = /^\d+\.\d+\.\d+-test$/;
6
8
  const UTC_RFC3339 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/;
7
9
  export function sha256Bytes(value) {
8
10
  return createHash("sha256").update(value).digest("hex");
@@ -12,11 +14,13 @@ function safeManagedPath(path) {
12
14
  !path.startsWith("/") &&
13
15
  !/^[A-Za-z]:/.test(path) &&
14
16
  !path.includes("\\") &&
15
- !path.split("/").includes(".."));
17
+ path.split("/").every((part) => part.length > 0 && part !== "." && part !== "..") &&
18
+ ![".git", "node_modules"].includes(path.split("/")[0] ?? ""));
16
19
  }
17
- export function parseInstallMetadata(raw) {
20
+ export function parseInstallMetadata(raw, opts = {}) {
18
21
  try {
19
22
  const value = JSON.parse(raw);
23
+ const allowLocalTest = opts.allowLocalTest === true;
20
24
  const allowed = new Set([
21
25
  "schemaVersion",
22
26
  "artifactVersion",
@@ -27,13 +31,27 @@ export function parseInstallMetadata(raw) {
27
31
  "catalogSha256",
28
32
  "installedAt",
29
33
  "managedFiles",
34
+ "generatedOutputDir",
30
35
  "targetSha256",
31
36
  "targetFileCount",
32
37
  "targetByteCount",
38
+ "bitmapShards",
39
+ "bitmapShardSetSha256",
40
+ "delivery",
41
+ ...(allowLocalTest ? ["channel", "publishable"] : []),
33
42
  ]);
34
43
  if (Object.keys(value).some((key) => !allowed.has(key)) || value.schemaVersion !== 1)
35
44
  return undefined;
36
- if (typeof value.artifactVersion !== "string" || !VERSION.test(value.artifactVersion))
45
+ const localTestMarked = value.channel === "local-test" && value.publishable === false;
46
+ const strictVersion = typeof value.artifactVersion === "string" && VERSION.test(value.artifactVersion);
47
+ // A formal (stable/alpha/beta) version must never carry the local-test marker.
48
+ if (strictVersion && (value.channel !== undefined || value.publishable !== undefined))
49
+ return undefined;
50
+ const versionAllowed = strictVersion ||
51
+ (allowLocalTest &&
52
+ localTestMarked &&
53
+ LOCAL_TEST_VERSION.test(value.artifactVersion));
54
+ if (!versionAllowed)
37
55
  return undefined;
38
56
  if (value.tier !== "free" && value.tier !== "pro")
39
57
  return undefined;
@@ -63,9 +81,42 @@ export function parseInstallMetadata(raw) {
63
81
  Array.isArray(value.managedFiles))
64
82
  return undefined;
65
83
  const managedFiles = value.managedFiles;
84
+ if (value.generatedOutputDir !== undefined &&
85
+ (typeof value.generatedOutputDir !== "string" ||
86
+ !safeManagedPath(value.generatedOutputDir) ||
87
+ value.generatedOutputDir.split("/").some((part) => !part || part === "." || part === "..")))
88
+ return undefined;
66
89
  if (Object.keys(managedFiles).length === 0 ||
67
90
  Object.entries(managedFiles).some(([path, hash]) => !safeManagedPath(path) || typeof hash !== "string" || !SHA256.test(hash)))
68
91
  return undefined;
92
+ if (value.delivery !== undefined) {
93
+ const delivery = value.delivery;
94
+ if (!delivery ||
95
+ typeof delivery !== "object" ||
96
+ Array.isArray(delivery) ||
97
+ Object.keys(delivery).some((k) => !["mode", "indexSha256", "bundleSha256"].includes(k)) ||
98
+ delivery.mode !== "icons" ||
99
+ typeof delivery.indexSha256 !== "string" ||
100
+ !SHA256.test(delivery.indexSha256) ||
101
+ typeof delivery.bundleSha256 !== "string" ||
102
+ !SHA256.test(delivery.bundleSha256) ||
103
+ managedFiles[".moeicons/resource-index.json.gz"] !== delivery.indexSha256)
104
+ return undefined;
105
+ }
106
+ if (value.bitmapShards !== undefined || value.bitmapShardSetSha256 !== undefined) {
107
+ if (!Array.isArray(value.bitmapShards) || value.bitmapShards.length === 0)
108
+ return undefined;
109
+ const shards = value.bitmapShards.map((entry) => parseBitmapShard(entry));
110
+ for (const shard of shards) {
111
+ if (shard.resourceVersion !== value.artifactVersion)
112
+ return undefined;
113
+ }
114
+ const digest = value.bitmapShardSetSha256;
115
+ if (typeof digest !== "string" || !SHA256.test(digest))
116
+ return undefined;
117
+ if (bitmapShardSetSha256(shards) !== digest)
118
+ return undefined;
119
+ }
69
120
  return value;
70
121
  }
71
122
  catch {
@@ -75,18 +126,18 @@ export function parseInstallMetadata(raw) {
75
126
  export function serializeInstallMetadata(metadata) {
76
127
  return `${JSON.stringify(metadata, null, 2)}\n`;
77
128
  }
78
- export function readInstalledResourceState(projectRoot, expectedTier) {
129
+ export function readInstalledResourceState(projectRoot, expectedTier, opts = {}) {
79
130
  const metadataPath = join(projectRoot, ".moeicons", "install-metadata.json");
80
131
  if (!existsSync(metadataPath))
81
132
  return {
82
133
  kind: "missing",
83
- message: "managed install metadata is missing; run repair or reinstall",
134
+ message: "managed install metadata is missing; run 'moeicons install' to repair or reinstall",
84
135
  };
85
- const metadata = parseInstallMetadata(readFileSync(metadataPath, "utf8"));
136
+ const metadata = parseInstallMetadata(readFileSync(metadataPath, "utf8"), opts);
86
137
  if (!metadata)
87
138
  return {
88
139
  kind: "invalid",
89
- message: "managed install metadata is invalid; run repair or reinstall",
140
+ message: "managed install metadata is invalid; run 'moeicons install' to repair or reinstall",
90
141
  };
91
142
  if (expectedTier && metadata.tier !== expectedTier)
92
143
  return {
@@ -13,6 +13,7 @@ export interface InstallPlanItem {
13
13
  readonly bytes?: Uint8Array;
14
14
  }
15
15
  export interface InstallPlan {
16
+ readonly root?: string;
16
17
  readonly items: readonly InstallPlanItem[];
17
18
  }
18
19
  export interface TransactionalFs {
@@ -27,11 +28,24 @@ export interface TransactionalFsWithCopy extends TransactionalFs {
27
28
  readonly readdirSync: typeof readdirSync;
28
29
  readonly copyFileSync: typeof copyFileSync;
29
30
  }
31
+ /** Shared path boundary for install/update and doctor/init writes. */
32
+ export declare function safeManagedPath(projectRoot: string, relative: string): {
33
+ normalized: string;
34
+ target: string;
35
+ };
30
36
  /**
31
37
  * Atomically reconcile an explicit cross-directory managed set. `removePaths`
32
38
  * must come from trusted metadata; this function never scans or uses globs.
33
39
  */
34
- export declare function executeManagedReconcile(projectRoot: string, writes: Readonly<Record<string, string | Uint8Array>>, removePaths: readonly string[], fs_: TransactionalFsWithCopy): void;
40
+ export declare function executeManagedReconcile(projectRoot: string, writes: Readonly<Record<string, string | Uint8Array>>, removePaths: readonly string[], fs_: TransactionalFsWithCopy, options?: {
41
+ readonly expectedText?: Readonly<Record<string, string | undefined>>;
42
+ readonly expectedSha256?: Readonly<Record<string, string | undefined>>;
43
+ }): void;
44
+ /** Recover an interrupted process while preserving any independently edited file.
45
+ * Must run under the same project lock as reconcile. Invalid journals fail closed.
46
+ * This covers process termination, not power loss or a hostile filesystem.
47
+ */
48
+ export declare function recoverManagedReconcile(projectRoot: string, fs_: TransactionalFsWithCopy): number;
35
49
  /** Pure list of install operations with expected checksums. */
36
50
  export declare function createInstallPlan(targetRoot: string, files: Readonly<Record<string, string | Uint8Array>>): InstallPlan;
37
51
  /** Create a deterministic ZIP for delivery (fixed mtime). */
@@ -68,9 +82,9 @@ export declare function executeGeneratedFiles(files: readonly {
68
82
  * the output directory is never left in a partial state.
69
83
  *
70
84
  * The set of CLI-managed paths is determined by the `files` argument. Any file
71
- * that already exists in outputDir but is NOT in that set is considered
72
- * user-owned and is preserved. Older CLI-managed files are identified by the
73
- * generated-file header and are intentionally not restored.
85
+ * that already exists in outputDir is considered user-owned and is preserved.
86
+ * This compatibility writer has no ownership metadata, so it refuses to
87
+ * replace an existing path. Production generation uses managed reconciliation.
74
88
  */
75
89
  export declare function executeGeneratedFilesDir(files: readonly {
76
90
  path: string;
@@ -103,6 +117,12 @@ export type DownloadResult = {
103
117
  * Download an artifact. HTTPS only, optional host allowlist, bounded redirects,
104
118
  * timeout, byte limit. Abort and temporary-file cleanup are handled by the
105
119
  * caller via the injected fetch/signal.
120
+ *
121
+ * Redirect policy: unlike the API descriptor handshake (which uses
122
+ * `redirect: "error"`), payload downloads intentionally follow a bounded number
123
+ * of redirects because code/metadata archives are served from CDNs that 3xx to
124
+ * a signed object host; the destination host is re-checked against the
125
+ * allowlist on every hop.
106
126
  */
107
127
  export declare function downloadArtifact(url: string, limits: DownloadLimits, deps?: {
108
128
  fetchFn?: typeof fetch;