aman-intelligence 0.1.0

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 (197) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +116 -0
  3. package/dist/bin/aman.d.ts +2 -0
  4. package/dist/bin/aman.js +165 -0
  5. package/dist/cli/global-install.d.ts +7 -0
  6. package/dist/cli/global-install.js +36 -0
  7. package/dist/cli/help-text.d.ts +1 -0
  8. package/dist/cli/help-text.js +62 -0
  9. package/dist/cli/version.d.ts +1 -0
  10. package/dist/cli/version.js +6 -0
  11. package/dist/commands/backup.d.ts +11 -0
  12. package/dist/commands/backup.js +262 -0
  13. package/dist/commands/browse.d.ts +11 -0
  14. package/dist/commands/browse.js +641 -0
  15. package/dist/commands/cache.d.ts +1 -0
  16. package/dist/commands/cache.js +38 -0
  17. package/dist/commands/config.d.ts +4 -0
  18. package/dist/commands/config.js +146 -0
  19. package/dist/commands/dashboard.d.ts +1 -0
  20. package/dist/commands/dashboard.js +1004 -0
  21. package/dist/commands/doctor.d.ts +4 -0
  22. package/dist/commands/doctor.js +54 -0
  23. package/dist/commands/export.d.ts +1 -0
  24. package/dist/commands/export.js +137 -0
  25. package/dist/commands/help.d.ts +1 -0
  26. package/dist/commands/help.js +47 -0
  27. package/dist/commands/import-wizard.d.ts +7 -0
  28. package/dist/commands/import-wizard.js +374 -0
  29. package/dist/commands/import.d.ts +9 -0
  30. package/dist/commands/import.js +351 -0
  31. package/dist/commands/info.d.ts +1 -0
  32. package/dist/commands/info.js +174 -0
  33. package/dist/commands/init.d.ts +20 -0
  34. package/dist/commands/init.js +146 -0
  35. package/dist/commands/install.d.ts +10 -0
  36. package/dist/commands/install.js +342 -0
  37. package/dist/commands/pack.d.ts +23 -0
  38. package/dist/commands/pack.js +331 -0
  39. package/dist/commands/registry.d.ts +6 -0
  40. package/dist/commands/registry.js +218 -0
  41. package/dist/commands/remove.d.ts +1 -0
  42. package/dist/commands/remove.js +76 -0
  43. package/dist/commands/search.d.ts +7 -0
  44. package/dist/commands/search.js +295 -0
  45. package/dist/commands/stack.d.ts +18 -0
  46. package/dist/commands/stack.js +327 -0
  47. package/dist/commands/sync.d.ts +9 -0
  48. package/dist/commands/sync.js +428 -0
  49. package/dist/commands/update.d.ts +1 -0
  50. package/dist/commands/update.js +97 -0
  51. package/dist/config/features.d.ts +2 -0
  52. package/dist/config/features.js +2 -0
  53. package/dist/config/index.d.ts +13 -0
  54. package/dist/config/index.js +80 -0
  55. package/dist/config/paths.d.ts +23 -0
  56. package/dist/config/paths.js +45 -0
  57. package/dist/import/adapters.d.ts +14 -0
  58. package/dist/import/adapters.js +580 -0
  59. package/dist/import/discovery.service.d.ts +8 -0
  60. package/dist/import/discovery.service.js +26 -0
  61. package/dist/import/import.service.d.ts +7 -0
  62. package/dist/import/import.service.js +259 -0
  63. package/dist/import/types.d.ts +71 -0
  64. package/dist/import/types.js +1 -0
  65. package/dist/import/utils.d.ts +36 -0
  66. package/dist/import/utils.js +428 -0
  67. package/dist/marketplace/cache.d.ts +18 -0
  68. package/dist/marketplace/cache.js +141 -0
  69. package/dist/marketplace/github-search.d.ts +17 -0
  70. package/dist/marketplace/github-search.js +268 -0
  71. package/dist/marketplace/install-from-candidate.d.ts +6 -0
  72. package/dist/marketplace/install-from-candidate.js +14 -0
  73. package/dist/marketplace/install.d.ts +15 -0
  74. package/dist/marketplace/install.js +54 -0
  75. package/dist/marketplace/metadata-validator.d.ts +8 -0
  76. package/dist/marketplace/metadata-validator.js +79 -0
  77. package/dist/marketplace/types.d.ts +34 -0
  78. package/dist/marketplace/types.js +1 -0
  79. package/dist/providers/local.provider.d.ts +9 -0
  80. package/dist/providers/local.provider.js +51 -0
  81. package/dist/providers/provider.interface.d.ts +7 -0
  82. package/dist/providers/provider.interface.js +1 -0
  83. package/dist/providers/registry.provider.d.ts +2 -0
  84. package/dist/providers/registry.provider.js +42 -0
  85. package/dist/providers/skills-sh.provider.d.ts +11 -0
  86. package/dist/providers/skills-sh.provider.js +56 -0
  87. package/dist/registry/adapter.interface.d.ts +16 -0
  88. package/dist/registry/adapter.interface.js +1 -0
  89. package/dist/registry/errors.d.ts +5 -0
  90. package/dist/registry/errors.js +8 -0
  91. package/dist/registry/filesystem-registry.adapter.d.ts +25 -0
  92. package/dist/registry/filesystem-registry.adapter.js +288 -0
  93. package/dist/registry/github-registry.adapter.d.ts +11 -0
  94. package/dist/registry/github-registry.adapter.js +32 -0
  95. package/dist/registry/index.d.ts +8 -0
  96. package/dist/registry/index.js +8 -0
  97. package/dist/registry/local-registry.adapter.d.ts +6 -0
  98. package/dist/registry/local-registry.adapter.js +9 -0
  99. package/dist/registry/registry.service.d.ts +44 -0
  100. package/dist/registry/registry.service.js +163 -0
  101. package/dist/registry/slug-utils.d.ts +12 -0
  102. package/dist/registry/slug-utils.js +51 -0
  103. package/dist/registry/types.d.ts +160 -0
  104. package/dist/registry/types.js +1 -0
  105. package/dist/services/asset.service.d.ts +12 -0
  106. package/dist/services/asset.service.js +142 -0
  107. package/dist/services/backup.service.d.ts +8 -0
  108. package/dist/services/backup.service.js +169 -0
  109. package/dist/services/classification.service.d.ts +31 -0
  110. package/dist/services/classification.service.js +271 -0
  111. package/dist/services/config.service.d.ts +9 -0
  112. package/dist/services/config.service.js +20 -0
  113. package/dist/services/doctor.service.d.ts +5 -0
  114. package/dist/services/doctor.service.js +186 -0
  115. package/dist/services/environment.service.d.ts +42 -0
  116. package/dist/services/environment.service.js +227 -0
  117. package/dist/services/github.service.d.ts +7 -0
  118. package/dist/services/github.service.js +42 -0
  119. package/dist/services/lock.service.d.ts +12 -0
  120. package/dist/services/lock.service.js +71 -0
  121. package/dist/services/marketplace.service.d.ts +40 -0
  122. package/dist/services/marketplace.service.js +225 -0
  123. package/dist/services/pack.service.d.ts +9 -0
  124. package/dist/services/pack.service.js +193 -0
  125. package/dist/services/stack.service.d.ts +9 -0
  126. package/dist/services/stack.service.js +94 -0
  127. package/dist/storage/asset-layout.d.ts +46 -0
  128. package/dist/storage/asset-layout.js +277 -0
  129. package/dist/storage/filesystem.d.ts +12 -0
  130. package/dist/storage/filesystem.js +113 -0
  131. package/dist/storage/scan-by-type.d.ts +2 -0
  132. package/dist/storage/scan-by-type.js +8 -0
  133. package/dist/storage/scanner.d.ts +11 -0
  134. package/dist/storage/scanner.js +188 -0
  135. package/dist/types/asset-metadata.d.ts +84 -0
  136. package/dist/types/asset-metadata.js +104 -0
  137. package/dist/types/index.d.ts +212 -0
  138. package/dist/types/index.js +1 -0
  139. package/dist/ui/animations/ErrorIndicator.d.ts +5 -0
  140. package/dist/ui/animations/ErrorIndicator.js +6 -0
  141. package/dist/ui/animations/GithubIndicator.d.ts +6 -0
  142. package/dist/ui/animations/GithubIndicator.js +9 -0
  143. package/dist/ui/animations/ProgressBar.d.ts +5 -0
  144. package/dist/ui/animations/ProgressBar.js +15 -0
  145. package/dist/ui/animations/Spinner.d.ts +5 -0
  146. package/dist/ui/animations/Spinner.js +21 -0
  147. package/dist/ui/animations/SuccessIndicator.d.ts +5 -0
  148. package/dist/ui/animations/SuccessIndicator.js +6 -0
  149. package/dist/ui/animations/SyncActivity.d.ts +5 -0
  150. package/dist/ui/animations/SyncActivity.js +21 -0
  151. package/dist/ui/animations/TransitionScreen.d.ts +7 -0
  152. package/dist/ui/animations/TransitionScreen.js +25 -0
  153. package/dist/ui/animations/useAnimationMode.d.ts +1 -0
  154. package/dist/ui/animations/useAnimationMode.js +16 -0
  155. package/dist/ui/assetDisplay.d.ts +19 -0
  156. package/dist/ui/assetDisplay.js +59 -0
  157. package/dist/ui/components/Confirm.d.ts +8 -0
  158. package/dist/ui/components/Confirm.js +14 -0
  159. package/dist/ui/components/CustomSelect.d.ts +19 -0
  160. package/dist/ui/components/CustomSelect.js +13 -0
  161. package/dist/ui/components/Header.d.ts +6 -0
  162. package/dist/ui/components/Header.js +9 -0
  163. package/dist/ui/components/HealthReport.d.ts +7 -0
  164. package/dist/ui/components/HealthReport.js +13 -0
  165. package/dist/ui/components/MarketplaceInstallConfirm.d.ts +19 -0
  166. package/dist/ui/components/MarketplaceInstallConfirm.js +23 -0
  167. package/dist/ui/components/Narrator.d.ts +9 -0
  168. package/dist/ui/components/Narrator.js +26 -0
  169. package/dist/ui/components/ScopePrompt.d.ts +8 -0
  170. package/dist/ui/components/ScopePrompt.js +23 -0
  171. package/dist/ui/components/TooSmallScreen.d.ts +8 -0
  172. package/dist/ui/components/TooSmallScreen.js +6 -0
  173. package/dist/ui/date.d.ts +2 -0
  174. package/dist/ui/date.js +33 -0
  175. package/dist/ui/layout.d.ts +23 -0
  176. package/dist/ui/layout.js +44 -0
  177. package/dist/ui/list-item.d.ts +12 -0
  178. package/dist/ui/list-item.js +1 -0
  179. package/dist/ui/marketplaceDisplay.d.ts +10 -0
  180. package/dist/ui/marketplaceDisplay.js +36 -0
  181. package/dist/ui/theme.d.ts +42 -0
  182. package/dist/ui/theme.js +47 -0
  183. package/dist/utils/asset-list-fields.d.ts +11 -0
  184. package/dist/utils/asset-list-fields.js +28 -0
  185. package/dist/utils/error-message.d.ts +2 -0
  186. package/dist/utils/error-message.js +6 -0
  187. package/dist/utils/integrity.d.ts +9 -0
  188. package/dist/utils/integrity.js +23 -0
  189. package/dist/utils/lock-migrate.d.ts +25 -0
  190. package/dist/utils/lock-migrate.js +93 -0
  191. package/dist/utils/mcp-local.d.ts +15 -0
  192. package/dist/utils/mcp-local.js +129 -0
  193. package/dist/utils/slug.d.ts +6 -0
  194. package/dist/utils/slug.js +13 -0
  195. package/dist/utils/stack-normalize.d.ts +3 -0
  196. package/dist/utils/stack-normalize.js +43 -0
  197. package/package.json +77 -0
@@ -0,0 +1,16 @@
1
+ import { RegistryDeprecateRequest, RegistryDeprecateResult, RegistryListRequest, RegistryListResult, RegistryPublishRequest, RegistryPublishResult, RegistryResolveByIdRequest, RegistryResolveRequest, RegistryResolveResult, RegistrySearchRequest, RegistrySearchResult, RegistryVerifyRequest, RegistryVerifyResult } from './types.js';
2
+ /**
3
+ * Canonical registry adapter contract (Phase 3 — locked).
4
+ * Local filesystem, GitHub mirror, and future HTTP server MUST implement this interface.
5
+ */
6
+ export interface RegistryAdapter {
7
+ readonly name: string;
8
+ available(): Promise<boolean>;
9
+ resolve(request: RegistryResolveRequest): Promise<RegistryResolveResult>;
10
+ resolveById(request: RegistryResolveByIdRequest): Promise<RegistryResolveResult>;
11
+ publish(request: RegistryPublishRequest): Promise<RegistryPublishResult>;
12
+ deprecate(request: RegistryDeprecateRequest): Promise<RegistryDeprecateResult>;
13
+ list(request: RegistryListRequest): Promise<RegistryListResult>;
14
+ search(request: RegistrySearchRequest): Promise<RegistrySearchResult>;
15
+ verify(request: RegistryVerifyRequest): Promise<RegistryVerifyResult>;
16
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ export type RegistryErrorCode = 'INVALID_SLUG' | 'SCOPE_REQUIRED' | 'NOT_FOUND' | 'VERSION_EXISTS' | 'VERSION_NOT_FOUND' | 'CHECKSUM_MISMATCH' | 'IMMUTABLE_VIOLATION' | 'UNAUTHORIZED' | 'CIRCULAR_DEPENDENCY' | 'ADAPTER_UNAVAILABLE';
2
+ export declare class RegistryError extends Error {
3
+ readonly code: RegistryErrorCode;
4
+ constructor(code: RegistryErrorCode, message: string);
5
+ }
@@ -0,0 +1,8 @@
1
+ export class RegistryError extends Error {
2
+ code;
3
+ constructor(code, message) {
4
+ super(message);
5
+ this.name = 'RegistryError';
6
+ this.code = code;
7
+ }
8
+ }
@@ -0,0 +1,25 @@
1
+ import { RegistryAdapter } from './adapter.interface.js';
2
+ import { RegistryDeprecateRequest, RegistryDeprecateResult, RegistryListRequest, RegistryListResult, RegistryPublishRequest, RegistryPublishResult, RegistryResolveByIdRequest, RegistryResolveRequest, RegistryResolveResult, RegistrySearchRequest, RegistrySearchResult, RegistrySlugIndexEntry, RegistryVerifyRequest, RegistryVerifyResult } from './types.js';
3
+ /**
4
+ * Shared filesystem layout for local and GitHub-backed registry mirrors.
5
+ * Layout: `{root}/slug-index/{namespace}/{name}.json`, `{root}/assets/{id}/versions/{version}/`.
6
+ */
7
+ export declare class FilesystemRegistryAdapter implements RegistryAdapter {
8
+ protected readonly registryRoot: string;
9
+ readonly name: string;
10
+ constructor(registryRoot: string, name?: string);
11
+ available(): Promise<boolean>;
12
+ resolve(request: RegistryResolveRequest): Promise<RegistryResolveResult>;
13
+ resolveById(request: RegistryResolveByIdRequest): Promise<RegistryResolveResult>;
14
+ publish(request: RegistryPublishRequest): Promise<RegistryPublishResult>;
15
+ deprecate(request: RegistryDeprecateRequest): Promise<RegistryDeprecateResult>;
16
+ list(request: RegistryListRequest): Promise<RegistryListResult>;
17
+ search(request: RegistrySearchRequest): Promise<RegistrySearchResult>;
18
+ verify(request: RegistryVerifyRequest): Promise<RegistryVerifyResult>;
19
+ protected resolveSlugToId(slug: string): Promise<{
20
+ id: string;
21
+ entry: RegistrySlugIndexEntry;
22
+ }>;
23
+ private findIdByRedirect;
24
+ private resolveRecord;
25
+ }
@@ -0,0 +1,288 @@
1
+ import path from 'path';
2
+ import { RegistryError } from './errors.js';
3
+ import { assetIndexPath, assetRootPath, normalizeRegistrySlug, namespaceFromSlug, slugIndexFilePath, versionContentPath, versionRecordPath, } from './slug-utils.js';
4
+ import { copyDir, ensureDir, exists, listDirs, readJson, writeJson } from '../storage/filesystem.js';
5
+ import { computeAssetChecksum } from '../utils/integrity.js';
6
+ import { isCanonicalAssetDir } from '../storage/asset-layout.js';
7
+ function compareVersions(a, b) {
8
+ const ap = a.split('.').map((p) => parseInt(p, 10) || 0);
9
+ const bp = b.split('.').map((p) => parseInt(p, 10) || 0);
10
+ const len = Math.max(ap.length, bp.length);
11
+ for (let i = 0; i < len; i++) {
12
+ const diff = (ap[i] ?? 0) - (bp[i] ?? 0);
13
+ if (diff !== 0)
14
+ return diff;
15
+ }
16
+ return 0;
17
+ }
18
+ /**
19
+ * Shared filesystem layout for local and GitHub-backed registry mirrors.
20
+ * Layout: `{root}/slug-index/{namespace}/{name}.json`, `{root}/assets/{id}/versions/{version}/`.
21
+ */
22
+ export class FilesystemRegistryAdapter {
23
+ registryRoot;
24
+ name;
25
+ constructor(registryRoot, name = 'local') {
26
+ this.registryRoot = registryRoot;
27
+ this.name = name;
28
+ }
29
+ async available() {
30
+ await ensureDir(this.registryRoot);
31
+ return true;
32
+ }
33
+ async resolve(request) {
34
+ const slug = normalizeRegistrySlug(request.scope, request.slug);
35
+ const { id } = await this.resolveSlugToId(slug);
36
+ return this.resolveRecord(id, slug, request.version);
37
+ }
38
+ async resolveById(request) {
39
+ const assetIndex = await readJson(assetIndexPath(this.registryRoot, request.id));
40
+ if (!assetIndex) {
41
+ throw new RegistryError('NOT_FOUND', `Asset id not found: ${request.id}`);
42
+ }
43
+ return this.resolveRecord(request.id, assetIndex.slug, request.version);
44
+ }
45
+ async publish(request) {
46
+ const slug = normalizeRegistrySlug(request.scope, request.slug);
47
+ const version = request.version.trim();
48
+ const contentDir = request.contentDirectory;
49
+ if (!exists(contentDir) || !isCanonicalAssetDir(request.type, contentDir)) {
50
+ throw new RegistryError('CHECKSUM_MISMATCH', `Content directory is not a canonical ${request.type} layout: ${contentDir}`);
51
+ }
52
+ const checksum = await computeAssetChecksum(request.type, contentDir);
53
+ if (request.metadata.integrity?.checksum && request.metadata.integrity.checksum !== checksum) {
54
+ throw new RegistryError('CHECKSUM_MISMATCH', `Submitted checksum does not match content (expected ${request.metadata.integrity.checksum}, got ${checksum})`);
55
+ }
56
+ const id = request.metadata.id;
57
+ const versionDir = versionContentPath(this.registryRoot, id, version);
58
+ if (exists(versionRecordPath(this.registryRoot, id, version))) {
59
+ throw new RegistryError('VERSION_EXISTS', `Version ${version} already published for ${slug} (${id}). Published versions are immutable.`);
60
+ }
61
+ const publishedAt = new Date().toISOString();
62
+ const namespace = namespaceFromSlug(slug);
63
+ const record = {
64
+ schemaVersion: 1,
65
+ recordType: 'asset',
66
+ id,
67
+ slug,
68
+ type: request.type,
69
+ name: request.metadata.name,
70
+ description: request.metadata.description,
71
+ version,
72
+ author: request.metadata.author,
73
+ tags: request.metadata.tags ?? [],
74
+ visibility: request.metadata.visibility ?? 'public',
75
+ publishedAt,
76
+ integrity: {
77
+ algorithm: 'sha256',
78
+ checksum,
79
+ signature: request.metadata.integrity?.signature ?? null,
80
+ },
81
+ dependencies: request.metadata.dependencies ?? [],
82
+ trust: {
83
+ verified: request.metadata.trust?.verified ?? false,
84
+ downloads: request.metadata.trust?.downloads ?? 0,
85
+ rating: request.metadata.trust?.rating ?? null,
86
+ publisher: request.metadata.trust?.publisher ?? `@${namespace}`,
87
+ deprecated: false,
88
+ },
89
+ deprecated: null,
90
+ };
91
+ await ensureDir(versionDir);
92
+ await copyDir(contentDir, versionDir);
93
+ await writeJson(versionRecordPath(this.registryRoot, id, version), record);
94
+ const assetIndex = {
95
+ schemaVersion: 1,
96
+ id,
97
+ slug,
98
+ aliases: [],
99
+ type: request.type,
100
+ visibility: record.visibility,
101
+ };
102
+ await writeJson(assetIndexPath(this.registryRoot, id), assetIndex);
103
+ const slugIndexPath = slugIndexFilePath(this.registryRoot, slug);
104
+ const existingSlug = await readJson(slugIndexPath);
105
+ const latestVersion = existingSlug && compareVersions(existingSlug.latestVersion, version) > 0
106
+ ? existingSlug.latestVersion
107
+ : version;
108
+ const slugEntry = {
109
+ schemaVersion: 1,
110
+ slug,
111
+ id,
112
+ latestVersion,
113
+ visibility: record.visibility,
114
+ namespace,
115
+ redirectFrom: existingSlug?.redirectFrom ?? [],
116
+ };
117
+ await writeJson(slugIndexPath, slugEntry);
118
+ return { record };
119
+ }
120
+ async deprecate(request) {
121
+ const slug = normalizeRegistrySlug(request.scope, request.slug);
122
+ const { id } = await this.resolveSlugToId(slug);
123
+ const recordPath = versionRecordPath(this.registryRoot, id, request.version);
124
+ const record = await readJson(recordPath);
125
+ if (!record) {
126
+ throw new RegistryError('VERSION_NOT_FOUND', `Version ${request.version} not found for ${slug}`);
127
+ }
128
+ const at = new Date().toISOString();
129
+ record.deprecated = {
130
+ at,
131
+ reason: request.reason,
132
+ successor: request.successor,
133
+ };
134
+ record.trust = { ...record.trust, deprecated: true };
135
+ await writeJson(recordPath, record);
136
+ return { record };
137
+ }
138
+ async list(request) {
139
+ const slug = normalizeRegistrySlug(request.scope, request.slug);
140
+ const { id } = await this.resolveSlugToId(slug);
141
+ const versionsDir = path.join(assetRootPath(this.registryRoot, id), 'versions');
142
+ if (!exists(versionsDir)) {
143
+ return { slug, id, versions: [] };
144
+ }
145
+ const versionNames = await listDirs(versionsDir);
146
+ const versions = [];
147
+ for (const version of versionNames) {
148
+ const record = await readJson(versionRecordPath(this.registryRoot, id, version));
149
+ if (record)
150
+ versions.push(record);
151
+ }
152
+ versions.sort((a, b) => compareVersions(b.version, a.version));
153
+ return { slug, id, versions };
154
+ }
155
+ async search(request) {
156
+ const slugIndexRoot = path.join(this.registryRoot, 'slug-index');
157
+ if (!exists(slugIndexRoot)) {
158
+ return { hits: [], total: 0 };
159
+ }
160
+ const query = request.query.trim().toLowerCase();
161
+ const hits = [];
162
+ const namespaces = await listDirs(slugIndexRoot);
163
+ for (const ns of namespaces) {
164
+ if (request.filters?.namespace && request.filters.namespace !== ns)
165
+ continue;
166
+ const nsDir = path.join(slugIndexRoot, ns);
167
+ const files = await listSlugIndexFiles(nsDir);
168
+ for (const file of files) {
169
+ const entry = await readJson(file);
170
+ if (!entry)
171
+ continue;
172
+ if (request.filters?.visibility && entry.visibility !== request.filters.visibility)
173
+ continue;
174
+ const record = await readJson(versionRecordPath(this.registryRoot, entry.id, entry.latestVersion));
175
+ if (!record)
176
+ continue;
177
+ if (request.filters?.type && record.type !== request.filters.type)
178
+ continue;
179
+ if (!request.filters?.includeDeprecated && record.deprecated)
180
+ continue;
181
+ const haystack = [
182
+ entry.slug,
183
+ record.name,
184
+ record.description,
185
+ ...(record.tags ?? []),
186
+ ]
187
+ .join(' ')
188
+ .toLowerCase();
189
+ if (query && !haystack.includes(query))
190
+ continue;
191
+ hits.push({
192
+ slug: entry.slug,
193
+ latestVersion: entry.latestVersion,
194
+ record,
195
+ });
196
+ }
197
+ }
198
+ hits.sort((a, b) => a.slug.localeCompare(b.slug));
199
+ return { hits, total: hits.length };
200
+ }
201
+ async verify(request) {
202
+ const resolved = await this.resolve({
203
+ scope: request.scope,
204
+ slug: request.slug,
205
+ version: request.version,
206
+ context: request.context,
207
+ });
208
+ if (resolved.content.kind !== 'filesystem') {
209
+ throw new RegistryError('ADAPTER_UNAVAILABLE', 'Verify requires filesystem content location');
210
+ }
211
+ const actual = await computeAssetChecksum(resolved.record.type, resolved.content.path);
212
+ const expected = request.checksum;
213
+ return {
214
+ valid: actual === expected,
215
+ expected,
216
+ actual,
217
+ };
218
+ }
219
+ async resolveSlugToId(slug) {
220
+ const direct = await readJson(slugIndexFilePath(this.registryRoot, slug));
221
+ if (direct) {
222
+ return { id: direct.id, entry: direct };
223
+ }
224
+ const redirectId = await this.findIdByRedirect(slug);
225
+ if (redirectId) {
226
+ const assetIndex = await readJson(assetIndexPath(this.registryRoot, redirectId));
227
+ if (assetIndex) {
228
+ const canonical = await readJson(slugIndexFilePath(this.registryRoot, assetIndex.slug));
229
+ if (canonical) {
230
+ return {
231
+ id: redirectId,
232
+ entry: {
233
+ ...canonical,
234
+ slug,
235
+ redirectFrom: canonical.redirectFrom,
236
+ },
237
+ };
238
+ }
239
+ }
240
+ }
241
+ throw new RegistryError('NOT_FOUND', `Slug not found in registry: ${slug}`);
242
+ }
243
+ async findIdByRedirect(slug) {
244
+ const slugIndexRoot = path.join(this.registryRoot, 'slug-index');
245
+ if (!exists(slugIndexRoot))
246
+ return null;
247
+ const namespaces = await listDirs(slugIndexRoot);
248
+ for (const ns of namespaces) {
249
+ const files = await listSlugIndexFiles(path.join(slugIndexRoot, ns));
250
+ for (const file of files) {
251
+ const entry = await readJson(file);
252
+ if (entry?.redirectFrom?.includes(slug)) {
253
+ return entry.id;
254
+ }
255
+ }
256
+ }
257
+ return null;
258
+ }
259
+ async resolveRecord(id, slug, version) {
260
+ const recordPath = versionRecordPath(this.registryRoot, id, version);
261
+ const record = await readJson(recordPath);
262
+ if (!record) {
263
+ throw new RegistryError('VERSION_NOT_FOUND', `Version ${version} not found for ${slug}`);
264
+ }
265
+ const contentPath = versionContentPath(this.registryRoot, id, version);
266
+ if (!exists(contentPath)) {
267
+ throw new RegistryError('NOT_FOUND', `Content missing for ${slug}@${version}`);
268
+ }
269
+ record.trust = {
270
+ ...record.trust,
271
+ downloads: (record.trust.downloads ?? 0) + 1,
272
+ };
273
+ await writeJson(recordPath, record);
274
+ return {
275
+ record,
276
+ content: { kind: 'filesystem', path: contentPath },
277
+ };
278
+ }
279
+ }
280
+ async function listSlugIndexFiles(dir) {
281
+ const { promises: fs } = await import('fs');
282
+ if (!exists(dir))
283
+ return [];
284
+ const entries = await fs.readdir(dir, { withFileTypes: true });
285
+ return entries
286
+ .filter((e) => e.isFile() && e.name.endsWith('.json'))
287
+ .map((e) => path.join(dir, e.name));
288
+ }
@@ -0,0 +1,11 @@
1
+ import { FilesystemRegistryAdapter } from './filesystem-registry.adapter.js';
2
+ /**
3
+ * GitHub mirror using the same on-disk layout under the cloned environment repo.
4
+ * Path: `~/.aman/repositories/{repo}/registry/`
5
+ */
6
+ export declare class GitHubRegistryAdapter extends FilesystemRegistryAdapter {
7
+ constructor();
8
+ static resolveRoot(): string;
9
+ available(): Promise<boolean>;
10
+ }
11
+ export declare const githubRegistryAdapter: GitHubRegistryAdapter;
@@ -0,0 +1,32 @@
1
+ import path from 'path';
2
+ import { GLOBAL_DIR } from '../config/paths.js';
3
+ import { exists } from '../storage/filesystem.js';
4
+ import { environmentService } from '../services/environment.service.js';
5
+ import { FilesystemRegistryAdapter } from './filesystem-registry.adapter.js';
6
+ function repoSlugToDirName(repository) {
7
+ return repository.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'aman-environment';
8
+ }
9
+ /**
10
+ * GitHub mirror using the same on-disk layout under the cloned environment repo.
11
+ * Path: `~/.aman/repositories/{repo}/registry/`
12
+ */
13
+ export class GitHubRegistryAdapter extends FilesystemRegistryAdapter {
14
+ constructor() {
15
+ super(GitHubRegistryAdapter.resolveRoot(), 'github');
16
+ }
17
+ static resolveRoot() {
18
+ const storage = environmentService.getStorage();
19
+ if (storage.type !== 'github' || !storage.repository) {
20
+ return path.join(GLOBAL_DIR, 'repositories', '_unconfigured', 'registry');
21
+ }
22
+ const repoDir = path.join(GLOBAL_DIR, 'repositories', repoSlugToDirName(storage.repository));
23
+ return path.join(repoDir, 'registry');
24
+ }
25
+ async available() {
26
+ const storage = environmentService.getStorage();
27
+ if (storage.type !== 'github' || !storage.repository)
28
+ return false;
29
+ return exists(this.registryRoot);
30
+ }
31
+ }
32
+ export const githubRegistryAdapter = new GitHubRegistryAdapter();
@@ -0,0 +1,8 @@
1
+ export * from './types.js';
2
+ export * from './errors.js';
3
+ export * from './adapter.interface.js';
4
+ export * from './slug-utils.js';
5
+ export * from './filesystem-registry.adapter.js';
6
+ export * from './local-registry.adapter.js';
7
+ export * from './github-registry.adapter.js';
8
+ export * from './registry.service.js';
@@ -0,0 +1,8 @@
1
+ export * from './types.js';
2
+ export * from './errors.js';
3
+ export * from './adapter.interface.js';
4
+ export * from './slug-utils.js';
5
+ export * from './filesystem-registry.adapter.js';
6
+ export * from './local-registry.adapter.js';
7
+ export * from './github-registry.adapter.js';
8
+ export * from './registry.service.js';
@@ -0,0 +1,6 @@
1
+ import { FilesystemRegistryAdapter } from './filesystem-registry.adapter.js';
2
+ /** V1 canonical registry at `~/.aman/registry/`. */
3
+ export declare class LocalRegistryAdapter extends FilesystemRegistryAdapter {
4
+ constructor(registryRoot?: string);
5
+ }
6
+ export declare const localRegistryAdapter: LocalRegistryAdapter;
@@ -0,0 +1,9 @@
1
+ import { GLOBAL_REGISTRY_DIR } from '../config/paths.js';
2
+ import { FilesystemRegistryAdapter } from './filesystem-registry.adapter.js';
3
+ /** V1 canonical registry at `~/.aman/registry/`. */
4
+ export class LocalRegistryAdapter extends FilesystemRegistryAdapter {
5
+ constructor(registryRoot = GLOBAL_REGISTRY_DIR) {
6
+ super(registryRoot, 'local');
7
+ }
8
+ }
9
+ export const localRegistryAdapter = new LocalRegistryAdapter();
@@ -0,0 +1,44 @@
1
+ import { RegistryAdapter } from './adapter.interface.js';
2
+ import { RegistryDeprecateRequest, RegistryDeprecateResult, RegistryListRequest, RegistryListResult, RegistryPublishRequest, RegistryPublishResult, RegistryResolveRequest, RegistrySearchRequest, RegistrySearchResult, RegistryVersionRecord } from './types.js';
3
+ import { Scope } from '../types/index.js';
4
+ export type RegistryBackend = 'local' | 'github';
5
+ export interface RegistryInstallResult {
6
+ slug: string;
7
+ version: string;
8
+ localName: string;
9
+ warnings: string[];
10
+ }
11
+ interface DependencyNode {
12
+ slug: string;
13
+ version: string;
14
+ }
15
+ export declare class RegistryService {
16
+ private backend;
17
+ constructor();
18
+ setBackend(backend: RegistryBackend): void;
19
+ getBackend(): RegistryBackend;
20
+ getAdapter(): RegistryAdapter;
21
+ /** Search merges local registry hits (V1 primary). */
22
+ search(request: RegistrySearchRequest): Promise<RegistrySearchResult>;
23
+ resolve(request: RegistryResolveRequest): Promise<import("./types.js").RegistryResolveResult>;
24
+ publish(request: RegistryPublishRequest): Promise<RegistryPublishResult>;
25
+ deprecate(request: RegistryDeprecateRequest): Promise<RegistryDeprecateResult>;
26
+ list(request: RegistryListRequest): Promise<RegistryListResult>;
27
+ verifyChecksum(slug: string, version: string, checksum: string): Promise<import("./types.js").RegistryVerifyResult>;
28
+ /**
29
+ * Resolves the full dependency tree (exact versions, asset-only, no cycles).
30
+ */
31
+ resolveDependencyTree(slug: string, version: string, warnings?: string[]): Promise<DependencyNode[]>;
32
+ collectInstallWarnings(record: RegistryVersionRecord): string[];
33
+ installFromRegistry(slug: string, version: string, scope: Scope, options?: {
34
+ localName?: string;
35
+ }): Promise<RegistryInstallResult>;
36
+ parseInstallReference(name: string): {
37
+ slug: string;
38
+ version: string;
39
+ } | null;
40
+ newPublishId(): string;
41
+ }
42
+ export declare const registryService: RegistryService;
43
+ export declare function isRegistryInstallReference(name: string): boolean;
44
+ export {};
@@ -0,0 +1,163 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { RegistryError } from './errors.js';
3
+ import { localRegistryAdapter } from './local-registry.adapter.js';
4
+ import { githubRegistryAdapter } from './github-registry.adapter.js';
5
+ import { parseRegistryReference } from './slug-utils.js';
6
+ import { assetService } from '../services/asset.service.js';
7
+ import { localNameFromSlug } from '../utils/slug.js';
8
+ function readRegistryBackendFromEnv() {
9
+ const value = process.env.AMAN_REGISTRY_BACKEND?.trim().toLowerCase();
10
+ return value === 'github' ? 'github' : 'local';
11
+ }
12
+ export class RegistryService {
13
+ backend;
14
+ constructor() {
15
+ this.backend = readRegistryBackendFromEnv();
16
+ }
17
+ setBackend(backend) {
18
+ this.backend = backend;
19
+ }
20
+ getBackend() {
21
+ return this.backend;
22
+ }
23
+ getAdapter() {
24
+ return this.backend === 'github' ? githubRegistryAdapter : localRegistryAdapter;
25
+ }
26
+ /** Search merges local registry hits (V1 primary). */
27
+ async search(request) {
28
+ const adapter = localRegistryAdapter;
29
+ if (!(await adapter.available())) {
30
+ return { hits: [], total: 0 };
31
+ }
32
+ return adapter.search(request);
33
+ }
34
+ async resolve(request) {
35
+ return this.getAdapter().resolve(request);
36
+ }
37
+ async publish(request) {
38
+ return this.getAdapter().publish(request);
39
+ }
40
+ async deprecate(request) {
41
+ return this.getAdapter().deprecate(request);
42
+ }
43
+ async list(request) {
44
+ return this.getAdapter().list(request);
45
+ }
46
+ async verifyChecksum(slug, version, checksum) {
47
+ const verify = await this.getAdapter().verify({ slug, version, checksum });
48
+ if (!verify.valid) {
49
+ throw new RegistryError('CHECKSUM_MISMATCH', `Checksum mismatch for ${slug}@${version}: expected ${verify.expected}, got ${verify.actual}`);
50
+ }
51
+ return verify;
52
+ }
53
+ /**
54
+ * Resolves the full dependency tree (exact versions, asset-only, no cycles).
55
+ */
56
+ async resolveDependencyTree(slug, version, warnings = []) {
57
+ const ordered = [];
58
+ const visiting = new Set();
59
+ const visited = new Set();
60
+ const walk = async (nodeSlug, nodeVersion) => {
61
+ const key = `${nodeSlug}@${nodeVersion}`;
62
+ if (visited.has(key))
63
+ return;
64
+ if (visiting.has(key)) {
65
+ throw new RegistryError('CIRCULAR_DEPENDENCY', `Circular dependency detected at ${key}`);
66
+ }
67
+ visiting.add(key);
68
+ const resolved = await this.getAdapter().resolve({ slug: nodeSlug, version: nodeVersion });
69
+ if (resolved.record.deprecated) {
70
+ const d = resolved.record.deprecated;
71
+ warnings.push(`Dependency ${nodeSlug}@${nodeVersion} is deprecated: ${d.reason}${d.successor ? ` (successor: ${d.successor})` : ''}`);
72
+ }
73
+ for (const dep of resolved.record.dependencies) {
74
+ try {
75
+ await this.getAdapter().resolve({ slug: dep.slug, version: dep.version });
76
+ }
77
+ catch (err) {
78
+ if (err instanceof RegistryError && err.code === 'NOT_FOUND') {
79
+ warnings.push(`Missing dependency ${dep.slug}@${dep.version} required by ${nodeSlug}`);
80
+ continue;
81
+ }
82
+ throw err;
83
+ }
84
+ await walk(dep.slug, dep.version);
85
+ }
86
+ visiting.delete(key);
87
+ visited.add(key);
88
+ ordered.push({ slug: nodeSlug, version: nodeVersion });
89
+ };
90
+ await walk(slug, version);
91
+ return ordered;
92
+ }
93
+ collectInstallWarnings(record) {
94
+ const warnings = [];
95
+ if (record.deprecated) {
96
+ const d = record.deprecated;
97
+ warnings.push(`WARNING: ${record.slug}@${record.version} is deprecated — ${d.reason}${d.successor ? `. Successor: ${d.successor}` : ''}`);
98
+ }
99
+ if (!record.trust.verified) {
100
+ warnings.push(`Note: ${record.slug}@${record.version} is not verified by a human reviewer.`);
101
+ }
102
+ return warnings;
103
+ }
104
+ async installFromRegistry(slug, version, scope, options) {
105
+ const warnings = [];
106
+ const tree = await this.resolveDependencyTree(slug, version, warnings);
107
+ for (const node of tree) {
108
+ const resolved = await this.getAdapter().resolve({ slug: node.slug, version: node.version });
109
+ await this.verifyChecksum(node.slug, node.version, resolved.record.integrity.checksum);
110
+ warnings.push(...this.collectInstallWarnings(resolved.record));
111
+ if (resolved.content.kind !== 'filesystem') {
112
+ throw new RegistryError('ADAPTER_UNAVAILABLE', 'Install requires filesystem content');
113
+ }
114
+ const localName = options?.localName ?? localNameFromSlug(node.slug);
115
+ const deprecated = resolved.record.deprecated
116
+ ? {
117
+ at: resolved.record.deprecated.at,
118
+ reason: resolved.record.deprecated.reason,
119
+ replacement: resolved.record.deprecated.successor,
120
+ }
121
+ : null;
122
+ await assetService.install(localName, resolved.record.type, scope, resolved.content.path, `registry:${node.slug}@${node.version}`, {
123
+ id: resolved.record.id,
124
+ slug: resolved.record.slug,
125
+ version: resolved.record.version,
126
+ description: resolved.record.description,
127
+ tags: resolved.record.tags,
128
+ author: resolved.record.author,
129
+ visibility: resolved.record.visibility,
130
+ dependencies: resolved.record.dependencies.map((d) => ({
131
+ type: 'asset',
132
+ slug: d.slug,
133
+ versionRange: d.version,
134
+ optional: false,
135
+ })),
136
+ trust: {
137
+ verified: resolved.record.trust.verified,
138
+ downloads: resolved.record.trust.downloads,
139
+ rating: resolved.record.trust.rating,
140
+ publisher: resolved.record.trust.publisher,
141
+ },
142
+ deprecated,
143
+ });
144
+ }
145
+ const root = tree[tree.length - 1] ?? { slug, version };
146
+ return {
147
+ slug: root.slug,
148
+ version: root.version,
149
+ localName: options?.localName ?? localNameFromSlug(root.slug),
150
+ warnings,
151
+ };
152
+ }
153
+ parseInstallReference(name) {
154
+ return parseRegistryReference(name);
155
+ }
156
+ newPublishId() {
157
+ return randomUUID();
158
+ }
159
+ }
160
+ export const registryService = new RegistryService();
161
+ export function isRegistryInstallReference(name) {
162
+ return parseRegistryReference(name) !== null;
163
+ }
@@ -0,0 +1,12 @@
1
+ import { RegistryNamespace } from './types.js';
2
+ export declare function parseRegistryReference(input: string): {
3
+ slug: string;
4
+ version: string;
5
+ } | null;
6
+ export declare function namespaceFromSlug(slug: string): RegistryNamespace;
7
+ export declare function normalizeRegistrySlug(scope: RegistryNamespace | undefined, slug: string): string;
8
+ export declare function slugIndexFilePath(registryRoot: string, slug: string): string;
9
+ export declare function assetRootPath(registryRoot: string, id: string): string;
10
+ export declare function versionRecordPath(registryRoot: string, id: string, version: string): string;
11
+ export declare function versionContentPath(registryRoot: string, id: string, version: string): string;
12
+ export declare function assetIndexPath(registryRoot: string, id: string): string;