@nimblebrain/mpak 0.1.0 → 0.2.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.
- package/LICENSE +10 -198
- package/README.md +50 -383
- package/dist/index.d.ts +0 -2
- package/dist/index.js +2113 -4
- package/dist/index.js.map +1 -1
- package/package.json +32 -29
- package/.claude/settings.local.json +0 -19
- package/.env.example +0 -13
- package/.github/workflows/ci.yml +0 -27
- package/CLAUDE.md +0 -283
- package/dist/commands/config.d.ts +0 -31
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js +0 -129
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/packages/pull.d.ts +0 -11
- package/dist/commands/packages/pull.d.ts.map +0 -1
- package/dist/commands/packages/pull.js +0 -72
- package/dist/commands/packages/pull.js.map +0 -1
- package/dist/commands/packages/run.d.ts +0 -47
- package/dist/commands/packages/run.d.ts.map +0 -1
- package/dist/commands/packages/run.js +0 -419
- package/dist/commands/packages/run.js.map +0 -1
- package/dist/commands/packages/search.d.ts +0 -12
- package/dist/commands/packages/search.d.ts.map +0 -1
- package/dist/commands/packages/search.js +0 -63
- package/dist/commands/packages/search.js.map +0 -1
- package/dist/commands/packages/show.d.ts +0 -8
- package/dist/commands/packages/show.d.ts.map +0 -1
- package/dist/commands/packages/show.js +0 -109
- package/dist/commands/packages/show.js.map +0 -1
- package/dist/commands/search.d.ts +0 -12
- package/dist/commands/search.d.ts.map +0 -1
- package/dist/commands/search.js +0 -144
- package/dist/commands/search.js.map +0 -1
- package/dist/commands/skills/index.d.ts +0 -8
- package/dist/commands/skills/index.d.ts.map +0 -1
- package/dist/commands/skills/index.js +0 -8
- package/dist/commands/skills/index.js.map +0 -1
- package/dist/commands/skills/install.d.ts +0 -9
- package/dist/commands/skills/install.d.ts.map +0 -1
- package/dist/commands/skills/install.js +0 -110
- package/dist/commands/skills/install.js.map +0 -1
- package/dist/commands/skills/list.d.ts +0 -8
- package/dist/commands/skills/list.d.ts.map +0 -1
- package/dist/commands/skills/list.js +0 -89
- package/dist/commands/skills/list.js.map +0 -1
- package/dist/commands/skills/pack.d.ts +0 -22
- package/dist/commands/skills/pack.d.ts.map +0 -1
- package/dist/commands/skills/pack.js +0 -116
- package/dist/commands/skills/pack.js.map +0 -1
- package/dist/commands/skills/pull.d.ts +0 -9
- package/dist/commands/skills/pull.d.ts.map +0 -1
- package/dist/commands/skills/pull.js +0 -68
- package/dist/commands/skills/pull.js.map +0 -1
- package/dist/commands/skills/search.d.ts +0 -14
- package/dist/commands/skills/search.d.ts.map +0 -1
- package/dist/commands/skills/search.js +0 -53
- package/dist/commands/skills/search.js.map +0 -1
- package/dist/commands/skills/show.d.ts +0 -8
- package/dist/commands/skills/show.d.ts.map +0 -1
- package/dist/commands/skills/show.js +0 -64
- package/dist/commands/skills/show.js.map +0 -1
- package/dist/commands/skills/validate.d.ts +0 -25
- package/dist/commands/skills/validate.d.ts.map +0 -1
- package/dist/commands/skills/validate.js +0 -191
- package/dist/commands/skills/validate.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/lib/api/registry-client.d.ts +0 -63
- package/dist/lib/api/registry-client.d.ts.map +0 -1
- package/dist/lib/api/registry-client.js +0 -167
- package/dist/lib/api/registry-client.js.map +0 -1
- package/dist/lib/api/skills-client.d.ts +0 -30
- package/dist/lib/api/skills-client.d.ts.map +0 -1
- package/dist/lib/api/skills-client.js +0 -110
- package/dist/lib/api/skills-client.js.map +0 -1
- package/dist/program.d.ts +0 -12
- package/dist/program.d.ts.map +0 -1
- package/dist/program.js +0 -186
- package/dist/program.js.map +0 -1
- package/dist/schemas/generated/api-responses.d.ts +0 -541
- package/dist/schemas/generated/api-responses.d.ts.map +0 -1
- package/dist/schemas/generated/api-responses.js +0 -313
- package/dist/schemas/generated/api-responses.js.map +0 -1
- package/dist/schemas/generated/auth.d.ts +0 -18
- package/dist/schemas/generated/auth.d.ts.map +0 -1
- package/dist/schemas/generated/auth.js +0 -18
- package/dist/schemas/generated/auth.js.map +0 -1
- package/dist/schemas/generated/index.d.ts +0 -5
- package/dist/schemas/generated/index.d.ts.map +0 -1
- package/dist/schemas/generated/index.js +0 -6
- package/dist/schemas/generated/index.js.map +0 -1
- package/dist/schemas/generated/package.d.ts +0 -43
- package/dist/schemas/generated/package.d.ts.map +0 -1
- package/dist/schemas/generated/package.js +0 -20
- package/dist/schemas/generated/package.js.map +0 -1
- package/dist/schemas/generated/skill.d.ts +0 -381
- package/dist/schemas/generated/skill.d.ts.map +0 -1
- package/dist/schemas/generated/skill.js +0 -216
- package/dist/schemas/generated/skill.js.map +0 -1
- package/dist/utils/config-manager.d.ts +0 -66
- package/dist/utils/config-manager.d.ts.map +0 -1
- package/dist/utils/config-manager.js +0 -193
- package/dist/utils/config-manager.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -12
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -27
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/version.d.ts +0 -5
- package/dist/utils/version.d.ts.map +0 -1
- package/dist/utils/version.js +0 -19
- package/dist/utils/version.js.map +0 -1
- package/eslint.config.js +0 -63
- package/src/commands/config.ts +0 -162
- package/src/commands/packages/pull.ts +0 -96
- package/src/commands/packages/run.test.ts +0 -261
- package/src/commands/packages/run.ts +0 -536
- package/src/commands/packages/search.ts +0 -83
- package/src/commands/packages/show.ts +0 -128
- package/src/commands/search.ts +0 -191
- package/src/commands/skills/index.ts +0 -7
- package/src/commands/skills/install.ts +0 -129
- package/src/commands/skills/list.ts +0 -116
- package/src/commands/skills/pack.test.ts +0 -260
- package/src/commands/skills/pack.ts +0 -145
- package/src/commands/skills/pull.ts +0 -88
- package/src/commands/skills/search.ts +0 -73
- package/src/commands/skills/show.ts +0 -72
- package/src/commands/skills/validate.test.ts +0 -466
- package/src/commands/skills/validate.ts +0 -227
- package/src/index.ts +0 -11
- package/src/lib/api/registry-client.ts +0 -223
- package/src/lib/api/schema.d.ts +0 -520
- package/src/lib/api/skills-client.ts +0 -148
- package/src/program.test.ts +0 -22
- package/src/program.ts +0 -226
- package/src/schemas/config.v1.schema.json +0 -37
- package/src/schemas/generated/api-responses.ts +0 -386
- package/src/schemas/generated/auth.ts +0 -21
- package/src/schemas/generated/index.ts +0 -5
- package/src/schemas/generated/package.ts +0 -29
- package/src/schemas/generated/skill.ts +0 -271
- package/src/utils/config-manager.test.ts +0 -330
- package/src/utils/config-manager.ts +0 -272
- package/src/utils/errors.test.ts +0 -25
- package/src/utils/errors.ts +0 -33
- package/src/utils/version.test.ts +0 -16
- package/src/utils/version.ts +0 -18
- package/test/integration/registry-client.test.ts +0 -180
- package/tsconfig.check.json +0 -9
- package/tsconfig.json +0 -25
- package/vitest.config.ts +0 -14
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
// Server type enum
|
|
4
|
-
export const ServerTypeSchema = z.enum(['node', 'python', 'binary']);
|
|
5
|
-
|
|
6
|
-
// Platform enum
|
|
7
|
-
export const PlatformSchema = z.enum(['darwin', 'win32', 'linux']);
|
|
8
|
-
|
|
9
|
-
// Sort options
|
|
10
|
-
export const PackageSortSchema = z.enum(['downloads', 'recent', 'name']);
|
|
11
|
-
|
|
12
|
-
// Package search params schema
|
|
13
|
-
// Query params from HTTP are always strings, but we parse them to proper types
|
|
14
|
-
export const PackageSearchParamsSchema = z.object({
|
|
15
|
-
q: z.string().optional(),
|
|
16
|
-
type: ServerTypeSchema.optional(),
|
|
17
|
-
tool: z.string().optional(),
|
|
18
|
-
prompt: z.string().optional(),
|
|
19
|
-
platform: PlatformSchema.optional(),
|
|
20
|
-
sort: PackageSortSchema.optional(),
|
|
21
|
-
limit: z.union([z.string(), z.number()]).optional(),
|
|
22
|
-
offset: z.union([z.string(), z.number()]).optional(),
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// Export TypeScript types
|
|
26
|
-
export type ServerType = z.infer<typeof ServerTypeSchema>;
|
|
27
|
-
export type Platform = z.infer<typeof PlatformSchema>;
|
|
28
|
-
export type PackageSort = z.infer<typeof PackageSortSchema>;
|
|
29
|
-
export type PackageSearchParams = z.infer<typeof PackageSearchParamsSchema>;
|
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
// =============================================================================
|
|
4
|
-
// Agent Skills Specification - Skill Frontmatter Schema
|
|
5
|
-
// https://agentskills.io/specification
|
|
6
|
-
// =============================================================================
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Skill name validation
|
|
10
|
-
* - 1-64 characters
|
|
11
|
-
* - Lowercase alphanumeric with single hyphens
|
|
12
|
-
* - Cannot start or end with hyphen
|
|
13
|
-
* - Must match directory name (validated separately)
|
|
14
|
-
*/
|
|
15
|
-
export const SkillNameSchema = z
|
|
16
|
-
.string()
|
|
17
|
-
.min(1)
|
|
18
|
-
.max(64)
|
|
19
|
-
.regex(
|
|
20
|
-
/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,
|
|
21
|
-
'Lowercase alphanumeric with single hyphens, cannot start/end with hyphen'
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Skill description
|
|
26
|
-
* - 1-1024 characters
|
|
27
|
-
* - Should describe what the skill does AND when to use it
|
|
28
|
-
*/
|
|
29
|
-
export const SkillDescriptionSchema = z.string().min(1).max(1024);
|
|
30
|
-
|
|
31
|
-
// =============================================================================
|
|
32
|
-
// Discovery Metadata Extension (via metadata: field)
|
|
33
|
-
// =============================================================================
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Category taxonomy for skill discovery
|
|
37
|
-
*/
|
|
38
|
-
export const SkillCategorySchema = z.enum([
|
|
39
|
-
'development', // Code, debugging, architecture
|
|
40
|
-
'writing', // Documentation, content, editing
|
|
41
|
-
'research', // Investigation, analysis, learning
|
|
42
|
-
'consulting', // Strategy, decisions, planning
|
|
43
|
-
'data', // Analysis, visualization, processing
|
|
44
|
-
'design', // UI/UX, visual, creative
|
|
45
|
-
'operations', // DevOps, infrastructure, automation
|
|
46
|
-
'security', // Auditing, compliance, protection
|
|
47
|
-
'other', // Uncategorized
|
|
48
|
-
]);
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Surfaces where the skill works
|
|
52
|
-
*/
|
|
53
|
-
export const SkillSurfaceSchema = z.enum([
|
|
54
|
-
'claude-code', // Claude Code CLI
|
|
55
|
-
'claude-api', // Claude API with code execution
|
|
56
|
-
'claude-ai', // claude.ai web interface
|
|
57
|
-
]);
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Author information for attribution
|
|
61
|
-
*/
|
|
62
|
-
export const SkillAuthorSchema = z.object({
|
|
63
|
-
name: z.string().min(1),
|
|
64
|
-
url: z.string().url().optional(),
|
|
65
|
-
email: z.string().email().optional(),
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Example usage for discovery
|
|
70
|
-
*/
|
|
71
|
-
export const SkillExampleSchema = z.object({
|
|
72
|
-
prompt: z.string().min(1),
|
|
73
|
-
context: z.string().optional(),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Discovery metadata (via metadata: field in frontmatter)
|
|
78
|
-
* All fields optional - skills can start minimal and add discovery metadata later
|
|
79
|
-
*/
|
|
80
|
-
export const SkillDiscoveryMetadataSchema = z
|
|
81
|
-
.object({
|
|
82
|
-
// Discovery
|
|
83
|
-
tags: z.array(z.string().max(32)).max(10).optional(),
|
|
84
|
-
category: SkillCategorySchema.optional(),
|
|
85
|
-
triggers: z.array(z.string().max(128)).max(20).optional(),
|
|
86
|
-
keywords: z.array(z.string().max(32)).max(30).optional(),
|
|
87
|
-
surfaces: z.array(SkillSurfaceSchema).optional(),
|
|
88
|
-
|
|
89
|
-
// Attribution
|
|
90
|
-
author: SkillAuthorSchema.optional(),
|
|
91
|
-
|
|
92
|
-
// Version (for registry tracking)
|
|
93
|
-
version: z.string().optional(),
|
|
94
|
-
|
|
95
|
-
// Examples
|
|
96
|
-
examples: z.array(SkillExampleSchema).max(5).optional(),
|
|
97
|
-
})
|
|
98
|
-
.passthrough(); // Allow additional custom keys
|
|
99
|
-
|
|
100
|
-
// =============================================================================
|
|
101
|
-
// Complete SKILL.md Frontmatter Schema
|
|
102
|
-
// =============================================================================
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Complete SKILL.md frontmatter schema
|
|
106
|
-
* Combines official Agent Skills spec with discovery metadata extension
|
|
107
|
-
*/
|
|
108
|
-
export const SkillFrontmatterSchema = z.object({
|
|
109
|
-
// Required (official spec)
|
|
110
|
-
name: SkillNameSchema,
|
|
111
|
-
description: SkillDescriptionSchema,
|
|
112
|
-
|
|
113
|
-
// Optional (official spec - informational pass-through)
|
|
114
|
-
license: z.string().optional(),
|
|
115
|
-
compatibility: z.string().max(500).optional(),
|
|
116
|
-
'allowed-tools': z.string().optional(), // space-delimited, experimental
|
|
117
|
-
|
|
118
|
-
// Extensible metadata (official spec) - our discovery extension
|
|
119
|
-
metadata: SkillDiscoveryMetadataSchema.optional(),
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// =============================================================================
|
|
123
|
-
// Registry API Schemas
|
|
124
|
-
// =============================================================================
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Scoped skill name for registry (e.g., @nimblebraininc/strategic-thought-partner)
|
|
128
|
-
*/
|
|
129
|
-
export const ScopedSkillNameSchema = z
|
|
130
|
-
.string()
|
|
131
|
-
.regex(
|
|
132
|
-
/^@[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/,
|
|
133
|
-
'Scoped name format: @scope/name'
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Skill artifact info for announce endpoint
|
|
138
|
-
*/
|
|
139
|
-
export const SkillArtifactSchema = z.object({
|
|
140
|
-
filename: z.string().regex(/\.skill$/, 'Must have .skill extension'),
|
|
141
|
-
sha256: z.string().length(64),
|
|
142
|
-
size: z.number().int().positive(),
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Announce request schema for POST /v1/skills/announce
|
|
147
|
-
*/
|
|
148
|
-
export const SkillAnnounceRequestSchema = z.object({
|
|
149
|
-
name: ScopedSkillNameSchema,
|
|
150
|
-
version: z.string(),
|
|
151
|
-
skill: SkillFrontmatterSchema,
|
|
152
|
-
release_tag: z.string(),
|
|
153
|
-
prerelease: z.boolean().optional().default(false),
|
|
154
|
-
artifact: SkillArtifactSchema,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Announce response schema
|
|
159
|
-
*/
|
|
160
|
-
export const SkillAnnounceResponseSchema = z.object({
|
|
161
|
-
skill: z.string(),
|
|
162
|
-
version: z.string(),
|
|
163
|
-
status: z.enum(['created', 'exists']),
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// =============================================================================
|
|
167
|
-
// Search/List API Schemas
|
|
168
|
-
// =============================================================================
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Skill search parameters
|
|
172
|
-
*/
|
|
173
|
-
export const SkillSearchParamsSchema = z.object({
|
|
174
|
-
q: z.string().optional(),
|
|
175
|
-
tags: z.string().optional(), // comma-separated
|
|
176
|
-
category: SkillCategorySchema.optional(),
|
|
177
|
-
surface: SkillSurfaceSchema.optional(),
|
|
178
|
-
sort: z.enum(['downloads', 'recent', 'name']).optional(),
|
|
179
|
-
limit: z.union([z.string(), z.number()]).optional(),
|
|
180
|
-
offset: z.union([z.string(), z.number()]).optional(),
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Skill summary for search results
|
|
185
|
-
*/
|
|
186
|
-
export const SkillSummarySchema = z.object({
|
|
187
|
-
name: z.string(), // scoped name
|
|
188
|
-
description: z.string(),
|
|
189
|
-
latest_version: z.string(),
|
|
190
|
-
tags: z.array(z.string()).optional(),
|
|
191
|
-
category: SkillCategorySchema.optional(),
|
|
192
|
-
surfaces: z.array(SkillSurfaceSchema).optional(),
|
|
193
|
-
downloads: z.number(),
|
|
194
|
-
published_at: z.string(),
|
|
195
|
-
author: SkillAuthorSchema.optional(),
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Skill search response
|
|
200
|
-
*/
|
|
201
|
-
export const SkillSearchResponseSchema = z.object({
|
|
202
|
-
skills: z.array(SkillSummarySchema),
|
|
203
|
-
total: z.number(),
|
|
204
|
-
pagination: z.object({
|
|
205
|
-
limit: z.number(),
|
|
206
|
-
offset: z.number(),
|
|
207
|
-
has_more: z.boolean(),
|
|
208
|
-
}),
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Skill detail response
|
|
213
|
-
*/
|
|
214
|
-
export const SkillDetailSchema = z.object({
|
|
215
|
-
name: z.string(), // scoped name
|
|
216
|
-
description: z.string(),
|
|
217
|
-
latest_version: z.string(),
|
|
218
|
-
license: z.string().optional(),
|
|
219
|
-
compatibility: z.string().optional(),
|
|
220
|
-
allowed_tools: z.array(z.string()).optional(),
|
|
221
|
-
tags: z.array(z.string()).optional(),
|
|
222
|
-
category: SkillCategorySchema.optional(),
|
|
223
|
-
triggers: z.array(z.string()).optional(),
|
|
224
|
-
surfaces: z.array(SkillSurfaceSchema).optional(),
|
|
225
|
-
downloads: z.number(),
|
|
226
|
-
published_at: z.string(),
|
|
227
|
-
author: SkillAuthorSchema.optional(),
|
|
228
|
-
examples: z.array(SkillExampleSchema).optional(),
|
|
229
|
-
versions: z.array(
|
|
230
|
-
z.object({
|
|
231
|
-
version: z.string(),
|
|
232
|
-
published_at: z.string(),
|
|
233
|
-
downloads: z.number(),
|
|
234
|
-
})
|
|
235
|
-
),
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Skill download info response
|
|
240
|
-
*/
|
|
241
|
-
export const SkillDownloadInfoSchema = z.object({
|
|
242
|
-
url: z.string(),
|
|
243
|
-
skill: z.object({
|
|
244
|
-
name: z.string(),
|
|
245
|
-
version: z.string(),
|
|
246
|
-
sha256: z.string(),
|
|
247
|
-
size: z.number(),
|
|
248
|
-
}),
|
|
249
|
-
expires_at: z.string(),
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// =============================================================================
|
|
253
|
-
// TypeScript Types
|
|
254
|
-
// =============================================================================
|
|
255
|
-
|
|
256
|
-
export type SkillName = z.infer<typeof SkillNameSchema>;
|
|
257
|
-
export type SkillCategory = z.infer<typeof SkillCategorySchema>;
|
|
258
|
-
export type SkillSurface = z.infer<typeof SkillSurfaceSchema>;
|
|
259
|
-
export type SkillAuthor = z.infer<typeof SkillAuthorSchema>;
|
|
260
|
-
export type SkillExample = z.infer<typeof SkillExampleSchema>;
|
|
261
|
-
export type SkillDiscoveryMetadata = z.infer<typeof SkillDiscoveryMetadataSchema>;
|
|
262
|
-
export type SkillFrontmatter = z.infer<typeof SkillFrontmatterSchema>;
|
|
263
|
-
export type ScopedSkillName = z.infer<typeof ScopedSkillNameSchema>;
|
|
264
|
-
export type SkillArtifact = z.infer<typeof SkillArtifactSchema>;
|
|
265
|
-
export type SkillAnnounceRequest = z.infer<typeof SkillAnnounceRequestSchema>;
|
|
266
|
-
export type SkillAnnounceResponse = z.infer<typeof SkillAnnounceResponseSchema>;
|
|
267
|
-
export type SkillSearchParams = z.infer<typeof SkillSearchParamsSchema>;
|
|
268
|
-
export type SkillSummary = z.infer<typeof SkillSummarySchema>;
|
|
269
|
-
export type SkillSearchResponse = z.infer<typeof SkillSearchResponseSchema>;
|
|
270
|
-
export type SkillDetail = z.infer<typeof SkillDetailSchema>;
|
|
271
|
-
export type SkillDownloadInfo = z.infer<typeof SkillDownloadInfoSchema>;
|
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { ConfigManager, ConfigCorruptedError } from './config-manager.js';
|
|
3
|
-
import { existsSync, rmSync, writeFileSync, mkdirSync } from 'fs';
|
|
4
|
-
import { join } from 'path';
|
|
5
|
-
import { homedir } from 'os';
|
|
6
|
-
|
|
7
|
-
describe('ConfigManager', () => {
|
|
8
|
-
const testConfigDir = join(homedir(), '.mpak');
|
|
9
|
-
const testConfigFile = join(testConfigDir, 'config.json');
|
|
10
|
-
|
|
11
|
-
beforeEach(() => {
|
|
12
|
-
// Clean up test config before each test
|
|
13
|
-
if (existsSync(testConfigFile)) {
|
|
14
|
-
rmSync(testConfigFile, { force: true });
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
// Clean up test config after each test
|
|
20
|
-
if (existsSync(testConfigFile)) {
|
|
21
|
-
rmSync(testConfigFile, { force: true });
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe('loadConfig', () => {
|
|
26
|
-
it('should create a new config if none exists', () => {
|
|
27
|
-
const manager = new ConfigManager();
|
|
28
|
-
const config = manager.loadConfig();
|
|
29
|
-
|
|
30
|
-
expect(config).toBeDefined();
|
|
31
|
-
expect(config.version).toBe('1.0.0');
|
|
32
|
-
expect(config.lastUpdated).toBeTruthy();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should set registryUrl in config', () => {
|
|
36
|
-
const manager = new ConfigManager();
|
|
37
|
-
manager.setRegistryUrl('http://test.example.com');
|
|
38
|
-
|
|
39
|
-
// Get the config to verify it's set
|
|
40
|
-
expect(manager.getRegistryUrl()).toBe('http://test.example.com');
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe('getRegistryUrl', () => {
|
|
45
|
-
it('should return default registry URL', () => {
|
|
46
|
-
const manager = new ConfigManager();
|
|
47
|
-
const url = manager.getRegistryUrl();
|
|
48
|
-
|
|
49
|
-
expect(url).toBe('https://api.mpak.dev');
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should return configured registry URL', () => {
|
|
53
|
-
const manager = new ConfigManager();
|
|
54
|
-
manager.setRegistryUrl('http://custom.example.com');
|
|
55
|
-
|
|
56
|
-
expect(manager.getRegistryUrl()).toBe('http://custom.example.com');
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('package config', () => {
|
|
61
|
-
it('should set and get package config value', () => {
|
|
62
|
-
const manager = new ConfigManager();
|
|
63
|
-
manager.setPackageConfigValue('@scope/name', 'api_key', 'test-value');
|
|
64
|
-
|
|
65
|
-
expect(manager.getPackageConfigValue('@scope/name', 'api_key')).toBe('test-value');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should return undefined for non-existent package', () => {
|
|
69
|
-
const manager = new ConfigManager();
|
|
70
|
-
|
|
71
|
-
expect(manager.getPackageConfig('@nonexistent/pkg')).toBeUndefined();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('should return undefined for non-existent key', () => {
|
|
75
|
-
const manager = new ConfigManager();
|
|
76
|
-
manager.setPackageConfigValue('@scope/name', 'existing', 'value');
|
|
77
|
-
|
|
78
|
-
expect(manager.getPackageConfigValue('@scope/name', 'nonexistent')).toBeUndefined();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should get all package config', () => {
|
|
82
|
-
const manager = new ConfigManager();
|
|
83
|
-
manager.setPackageConfigValue('@scope/name', 'key1', 'value1');
|
|
84
|
-
manager.setPackageConfigValue('@scope/name', 'key2', 'value2');
|
|
85
|
-
|
|
86
|
-
const config = manager.getPackageConfig('@scope/name');
|
|
87
|
-
expect(config).toEqual({
|
|
88
|
-
key1: 'value1',
|
|
89
|
-
key2: 'value2',
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should clear specific package config value', () => {
|
|
94
|
-
const manager = new ConfigManager();
|
|
95
|
-
manager.setPackageConfigValue('@scope/name', 'key1', 'value1');
|
|
96
|
-
manager.setPackageConfigValue('@scope/name', 'key2', 'value2');
|
|
97
|
-
|
|
98
|
-
const cleared = manager.clearPackageConfigValue('@scope/name', 'key1');
|
|
99
|
-
expect(cleared).toBe(true);
|
|
100
|
-
expect(manager.getPackageConfigValue('@scope/name', 'key1')).toBeUndefined();
|
|
101
|
-
expect(manager.getPackageConfigValue('@scope/name', 'key2')).toBe('value2');
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('should return false when clearing non-existent key', () => {
|
|
105
|
-
const manager = new ConfigManager();
|
|
106
|
-
manager.setPackageConfigValue('@scope/name', 'key1', 'value1');
|
|
107
|
-
|
|
108
|
-
const cleared = manager.clearPackageConfigValue('@scope/name', 'nonexistent');
|
|
109
|
-
expect(cleared).toBe(false);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('should clear all package config', () => {
|
|
113
|
-
const manager = new ConfigManager();
|
|
114
|
-
manager.setPackageConfigValue('@scope/name', 'key1', 'value1');
|
|
115
|
-
manager.setPackageConfigValue('@scope/name', 'key2', 'value2');
|
|
116
|
-
|
|
117
|
-
const cleared = manager.clearPackageConfig('@scope/name');
|
|
118
|
-
expect(cleared).toBe(true);
|
|
119
|
-
expect(manager.getPackageConfig('@scope/name')).toBeUndefined();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should return false when clearing non-existent package', () => {
|
|
123
|
-
const manager = new ConfigManager();
|
|
124
|
-
|
|
125
|
-
const cleared = manager.clearPackageConfig('@nonexistent/pkg');
|
|
126
|
-
expect(cleared).toBe(false);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('should list packages with config', () => {
|
|
130
|
-
const manager = new ConfigManager();
|
|
131
|
-
manager.setPackageConfigValue('@scope/pkg1', 'key', 'value');
|
|
132
|
-
manager.setPackageConfigValue('@scope/pkg2', 'key', 'value');
|
|
133
|
-
|
|
134
|
-
const packages = manager.listPackagesWithConfig();
|
|
135
|
-
expect(packages).toContain('@scope/pkg1');
|
|
136
|
-
expect(packages).toContain('@scope/pkg2');
|
|
137
|
-
expect(packages).toHaveLength(2);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('should clean up empty package entry after clearing last key', () => {
|
|
141
|
-
const manager = new ConfigManager();
|
|
142
|
-
manager.setPackageConfigValue('@scope/name', 'only_key', 'value');
|
|
143
|
-
manager.clearPackageConfigValue('@scope/name', 'only_key');
|
|
144
|
-
|
|
145
|
-
expect(manager.getPackageConfig('@scope/name')).toBeUndefined();
|
|
146
|
-
expect(manager.listPackagesWithConfig()).not.toContain('@scope/name');
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe('config validation', () => {
|
|
151
|
-
beforeEach(() => {
|
|
152
|
-
// Ensure config directory exists for writing test files
|
|
153
|
-
if (!existsSync(testConfigDir)) {
|
|
154
|
-
mkdirSync(testConfigDir, { recursive: true, mode: 0o700 });
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('should throw ConfigCorruptedError for invalid JSON', () => {
|
|
159
|
-
writeFileSync(testConfigFile, 'not valid json {{{', { mode: 0o600 });
|
|
160
|
-
|
|
161
|
-
const manager = new ConfigManager();
|
|
162
|
-
expect(() => manager.loadConfig()).toThrow(ConfigCorruptedError);
|
|
163
|
-
expect(() => manager.loadConfig()).toThrow(/invalid JSON/);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('should throw ConfigCorruptedError when version is missing', () => {
|
|
167
|
-
writeFileSync(
|
|
168
|
-
testConfigFile,
|
|
169
|
-
JSON.stringify({ lastUpdated: '2024-01-01T00:00:00Z' }),
|
|
170
|
-
{ mode: 0o600 }
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
const manager = new ConfigManager();
|
|
174
|
-
expect(() => manager.loadConfig()).toThrow(ConfigCorruptedError);
|
|
175
|
-
expect(() => manager.loadConfig()).toThrow(/version/);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('should throw ConfigCorruptedError when lastUpdated is missing', () => {
|
|
179
|
-
writeFileSync(
|
|
180
|
-
testConfigFile,
|
|
181
|
-
JSON.stringify({ version: '1.0.0' }),
|
|
182
|
-
{ mode: 0o600 }
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
const manager = new ConfigManager();
|
|
186
|
-
expect(() => manager.loadConfig()).toThrow(ConfigCorruptedError);
|
|
187
|
-
expect(() => manager.loadConfig()).toThrow(/lastUpdated/);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should throw ConfigCorruptedError when registryUrl is not a string', () => {
|
|
191
|
-
writeFileSync(
|
|
192
|
-
testConfigFile,
|
|
193
|
-
JSON.stringify({
|
|
194
|
-
version: '1.0.0',
|
|
195
|
-
lastUpdated: '2024-01-01T00:00:00Z',
|
|
196
|
-
registryUrl: 12345,
|
|
197
|
-
}),
|
|
198
|
-
{ mode: 0o600 }
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
const manager = new ConfigManager();
|
|
202
|
-
expect(() => manager.loadConfig()).toThrow(ConfigCorruptedError);
|
|
203
|
-
expect(() => manager.loadConfig()).toThrow(/registryUrl must be a string/);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('should throw ConfigCorruptedError when packages is not an object', () => {
|
|
207
|
-
writeFileSync(
|
|
208
|
-
testConfigFile,
|
|
209
|
-
JSON.stringify({
|
|
210
|
-
version: '1.0.0',
|
|
211
|
-
lastUpdated: '2024-01-01T00:00:00Z',
|
|
212
|
-
packages: 'not an object',
|
|
213
|
-
}),
|
|
214
|
-
{ mode: 0o600 }
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
const manager = new ConfigManager();
|
|
218
|
-
expect(() => manager.loadConfig()).toThrow(ConfigCorruptedError);
|
|
219
|
-
expect(() => manager.loadConfig()).toThrow(/packages must be an object/);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('should throw ConfigCorruptedError when package config is not an object', () => {
|
|
223
|
-
writeFileSync(
|
|
224
|
-
testConfigFile,
|
|
225
|
-
JSON.stringify({
|
|
226
|
-
version: '1.0.0',
|
|
227
|
-
lastUpdated: '2024-01-01T00:00:00Z',
|
|
228
|
-
packages: {
|
|
229
|
-
'@scope/pkg': 'not an object',
|
|
230
|
-
},
|
|
231
|
-
}),
|
|
232
|
-
{ mode: 0o600 }
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
const manager = new ConfigManager();
|
|
236
|
-
expect(() => manager.loadConfig()).toThrow(ConfigCorruptedError);
|
|
237
|
-
expect(() => manager.loadConfig()).toThrow(/packages.@scope\/pkg must be an object/);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it('should throw ConfigCorruptedError when package config value is not a string', () => {
|
|
241
|
-
writeFileSync(
|
|
242
|
-
testConfigFile,
|
|
243
|
-
JSON.stringify({
|
|
244
|
-
version: '1.0.0',
|
|
245
|
-
lastUpdated: '2024-01-01T00:00:00Z',
|
|
246
|
-
packages: {
|
|
247
|
-
'@scope/pkg': {
|
|
248
|
-
api_key: 12345,
|
|
249
|
-
},
|
|
250
|
-
},
|
|
251
|
-
}),
|
|
252
|
-
{ mode: 0o600 }
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
const manager = new ConfigManager();
|
|
256
|
-
expect(() => manager.loadConfig()).toThrow(ConfigCorruptedError);
|
|
257
|
-
expect(() => manager.loadConfig()).toThrow(/packages.@scope\/pkg.api_key must be a string/);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it('should throw ConfigCorruptedError for unknown fields', () => {
|
|
261
|
-
writeFileSync(
|
|
262
|
-
testConfigFile,
|
|
263
|
-
JSON.stringify({
|
|
264
|
-
version: '1.0.0',
|
|
265
|
-
lastUpdated: '2024-01-01T00:00:00Z',
|
|
266
|
-
unknownField: 'should not be here',
|
|
267
|
-
}),
|
|
268
|
-
{ mode: 0o600 }
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
const manager = new ConfigManager();
|
|
272
|
-
expect(() => manager.loadConfig()).toThrow(ConfigCorruptedError);
|
|
273
|
-
expect(() => manager.loadConfig()).toThrow(/unknown field: unknownField/);
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
it('should include config path in error', () => {
|
|
277
|
-
writeFileSync(testConfigFile, 'invalid json', { mode: 0o600 });
|
|
278
|
-
|
|
279
|
-
const manager = new ConfigManager();
|
|
280
|
-
try {
|
|
281
|
-
manager.loadConfig();
|
|
282
|
-
expect.fail('Should have thrown');
|
|
283
|
-
} catch (err) {
|
|
284
|
-
expect(err).toBeInstanceOf(ConfigCorruptedError);
|
|
285
|
-
expect((err as ConfigCorruptedError).configPath).toBe(testConfigFile);
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
it('should load valid minimal config', () => {
|
|
290
|
-
writeFileSync(
|
|
291
|
-
testConfigFile,
|
|
292
|
-
JSON.stringify({
|
|
293
|
-
version: '1.0.0',
|
|
294
|
-
lastUpdated: '2024-01-01T00:00:00Z',
|
|
295
|
-
}),
|
|
296
|
-
{ mode: 0o600 }
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
const manager = new ConfigManager();
|
|
300
|
-
const config = manager.loadConfig();
|
|
301
|
-
expect(config.version).toBe('1.0.0');
|
|
302
|
-
expect(config.lastUpdated).toBe('2024-01-01T00:00:00Z');
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
it('should load valid full config', () => {
|
|
306
|
-
writeFileSync(
|
|
307
|
-
testConfigFile,
|
|
308
|
-
JSON.stringify({
|
|
309
|
-
version: '1.0.0',
|
|
310
|
-
lastUpdated: '2024-01-01T00:00:00Z',
|
|
311
|
-
registryUrl: 'https://custom.registry.com',
|
|
312
|
-
packages: {
|
|
313
|
-
'@scope/pkg': {
|
|
314
|
-
api_key: 'secret',
|
|
315
|
-
other_key: 'value',
|
|
316
|
-
},
|
|
317
|
-
},
|
|
318
|
-
}),
|
|
319
|
-
{ mode: 0o600 }
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
const manager = new ConfigManager();
|
|
323
|
-
const config = manager.loadConfig();
|
|
324
|
-
expect(config.version).toBe('1.0.0');
|
|
325
|
-
expect(config.registryUrl).toBe('https://custom.registry.com');
|
|
326
|
-
expect(config.packages?.['@scope/pkg']?.api_key).toBe('secret');
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
});
|