fastmode-mcp 1.0.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/README.md +561 -0
- package/bin/run.js +50 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +802 -0
- package/dist/lib/api-client.d.ts +81 -0
- package/dist/lib/api-client.d.ts.map +1 -0
- package/dist/lib/api-client.js +237 -0
- package/dist/lib/auth-state.d.ts +13 -0
- package/dist/lib/auth-state.d.ts.map +1 -0
- package/dist/lib/auth-state.js +24 -0
- package/dist/lib/context-fetcher.d.ts +67 -0
- package/dist/lib/context-fetcher.d.ts.map +1 -0
- package/dist/lib/context-fetcher.js +190 -0
- package/dist/lib/credentials.d.ts +52 -0
- package/dist/lib/credentials.d.ts.map +1 -0
- package/dist/lib/credentials.js +196 -0
- package/dist/lib/device-flow.d.ts +14 -0
- package/dist/lib/device-flow.d.ts.map +1 -0
- package/dist/lib/device-flow.js +244 -0
- package/dist/tools/cms-items.d.ts +56 -0
- package/dist/tools/cms-items.d.ts.map +1 -0
- package/dist/tools/cms-items.js +376 -0
- package/dist/tools/create-site.d.ts +9 -0
- package/dist/tools/create-site.d.ts.map +1 -0
- package/dist/tools/create-site.js +202 -0
- package/dist/tools/deploy-package.d.ts +9 -0
- package/dist/tools/deploy-package.d.ts.map +1 -0
- package/dist/tools/deploy-package.js +434 -0
- package/dist/tools/generate-samples.d.ts +19 -0
- package/dist/tools/generate-samples.d.ts.map +1 -0
- package/dist/tools/generate-samples.js +272 -0
- package/dist/tools/get-conversion-guide.d.ts +7 -0
- package/dist/tools/get-conversion-guide.d.ts.map +1 -0
- package/dist/tools/get-conversion-guide.js +1323 -0
- package/dist/tools/get-example.d.ts +7 -0
- package/dist/tools/get-example.d.ts.map +1 -0
- package/dist/tools/get-example.js +1568 -0
- package/dist/tools/get-field-types.d.ts +30 -0
- package/dist/tools/get-field-types.d.ts.map +1 -0
- package/dist/tools/get-field-types.js +154 -0
- package/dist/tools/get-schema.d.ts +5 -0
- package/dist/tools/get-schema.d.ts.map +1 -0
- package/dist/tools/get-schema.js +320 -0
- package/dist/tools/get-started.d.ts +21 -0
- package/dist/tools/get-started.d.ts.map +1 -0
- package/dist/tools/get-started.js +624 -0
- package/dist/tools/get-tenant-schema.d.ts +18 -0
- package/dist/tools/get-tenant-schema.d.ts.map +1 -0
- package/dist/tools/get-tenant-schema.js +158 -0
- package/dist/tools/list-projects.d.ts +5 -0
- package/dist/tools/list-projects.d.ts.map +1 -0
- package/dist/tools/list-projects.js +101 -0
- package/dist/tools/sync-schema.d.ts +41 -0
- package/dist/tools/sync-schema.d.ts.map +1 -0
- package/dist/tools/sync-schema.js +483 -0
- package/dist/tools/validate-manifest.d.ts +5 -0
- package/dist/tools/validate-manifest.d.ts.map +1 -0
- package/dist/tools/validate-manifest.js +311 -0
- package/dist/tools/validate-package.d.ts +5 -0
- package/dist/tools/validate-package.d.ts.map +1 -0
- package/dist/tools/validate-package.js +337 -0
- package/dist/tools/validate-template.d.ts +12 -0
- package/dist/tools/validate-template.d.ts.map +1 -0
- package/dist/tools/validate-template.js +790 -0
- package/package.json +54 -0
- package/scripts/postinstall.js +129 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Generate Sample Items Tool
|
|
4
|
+
*
|
|
5
|
+
* Generates sample items for collections with automatic dependency ordering.
|
|
6
|
+
* Handles relation fields by generating parent collections first.
|
|
7
|
+
* Requires authentication.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.generateSampleItems = generateSampleItems;
|
|
11
|
+
const api_client_1 = require("../lib/api-client");
|
|
12
|
+
const device_flow_1 = require("../lib/device-flow");
|
|
13
|
+
// ============ Constants ============
|
|
14
|
+
const AUTH_REQUIRED_MESSAGE = `# Authentication Required
|
|
15
|
+
|
|
16
|
+
This tool requires authentication to generate sample items.
|
|
17
|
+
|
|
18
|
+
**To authenticate:**
|
|
19
|
+
1. Set the FASTMODE_AUTH_TOKEN environment variable, OR
|
|
20
|
+
2. Run this tool again and follow the browser-based login flow
|
|
21
|
+
|
|
22
|
+
Use \`list_projects\` to verify your authentication status.
|
|
23
|
+
`;
|
|
24
|
+
// ============ Helper Functions ============
|
|
25
|
+
/**
|
|
26
|
+
* Resolve project identifier to tenant ID
|
|
27
|
+
*/
|
|
28
|
+
async function resolveProjectId(projectIdentifier) {
|
|
29
|
+
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
30
|
+
if (uuidPattern.test(projectIdentifier)) {
|
|
31
|
+
return { tenantId: projectIdentifier };
|
|
32
|
+
}
|
|
33
|
+
const response = await (0, api_client_1.apiRequest)('/api/tenants');
|
|
34
|
+
if ((0, api_client_1.isApiError)(response)) {
|
|
35
|
+
return { error: `Failed to look up project: ${response.error}` };
|
|
36
|
+
}
|
|
37
|
+
const match = response.data.find(p => p.name.toLowerCase() === projectIdentifier.toLowerCase());
|
|
38
|
+
if (match) {
|
|
39
|
+
return { tenantId: match.id };
|
|
40
|
+
}
|
|
41
|
+
const partialMatch = response.data.find(p => p.name.toLowerCase().includes(projectIdentifier.toLowerCase()));
|
|
42
|
+
if (partialMatch) {
|
|
43
|
+
return { tenantId: partialMatch.id };
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
error: `Project "${projectIdentifier}" not found. Use list_projects to see available projects.`
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Topological sort of collections based on relation dependencies
|
|
51
|
+
* Returns collections ordered so that dependencies come first
|
|
52
|
+
*/
|
|
53
|
+
function sortByDependencies(collections) {
|
|
54
|
+
const slugToCollection = new Map();
|
|
55
|
+
const inDegree = new Map();
|
|
56
|
+
const adjacencyList = new Map();
|
|
57
|
+
// Initialize
|
|
58
|
+
for (const col of collections) {
|
|
59
|
+
slugToCollection.set(col.slug, col);
|
|
60
|
+
inDegree.set(col.slug, 0);
|
|
61
|
+
adjacencyList.set(col.slug, []);
|
|
62
|
+
}
|
|
63
|
+
// Build dependency graph
|
|
64
|
+
// If collection A has a relation to collection B, then B must be populated first
|
|
65
|
+
// So B -> A (B is a dependency of A)
|
|
66
|
+
for (const col of collections) {
|
|
67
|
+
for (const field of col.fields) {
|
|
68
|
+
if (field.type === 'relation' && field.referenceCollection) {
|
|
69
|
+
const refSlug = field.referenceCollection;
|
|
70
|
+
// Only count if the referenced collection is in our list
|
|
71
|
+
if (slugToCollection.has(refSlug)) {
|
|
72
|
+
// refSlug must come before col.slug
|
|
73
|
+
const deps = adjacencyList.get(refSlug) || [];
|
|
74
|
+
deps.push(col.slug);
|
|
75
|
+
adjacencyList.set(refSlug, deps);
|
|
76
|
+
inDegree.set(col.slug, (inDegree.get(col.slug) || 0) + 1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Kahn's algorithm for topological sort
|
|
82
|
+
const queue = [];
|
|
83
|
+
const result = [];
|
|
84
|
+
// Start with nodes that have no dependencies
|
|
85
|
+
for (const [slug, degree] of inDegree) {
|
|
86
|
+
if (degree === 0) {
|
|
87
|
+
queue.push(slug);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
while (queue.length > 0) {
|
|
91
|
+
const current = queue.shift();
|
|
92
|
+
const col = slugToCollection.get(current);
|
|
93
|
+
if (col) {
|
|
94
|
+
result.push(col);
|
|
95
|
+
}
|
|
96
|
+
// Reduce in-degree for dependent collections
|
|
97
|
+
const dependents = adjacencyList.get(current) || [];
|
|
98
|
+
for (const dep of dependents) {
|
|
99
|
+
const newDegree = (inDegree.get(dep) || 0) - 1;
|
|
100
|
+
inDegree.set(dep, newDegree);
|
|
101
|
+
if (newDegree === 0) {
|
|
102
|
+
queue.push(dep);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// If there are still collections not in result (cycle detected), add them anyway
|
|
107
|
+
for (const col of collections) {
|
|
108
|
+
if (!result.find(c => c.slug === col.slug)) {
|
|
109
|
+
result.push(col);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
// ============ Main Function ============
|
|
115
|
+
/**
|
|
116
|
+
* Generate sample items for collections
|
|
117
|
+
*
|
|
118
|
+
* @param input - The input with projectId and optional collectionSlugs
|
|
119
|
+
*/
|
|
120
|
+
async function generateSampleItems(input) {
|
|
121
|
+
// Check authentication
|
|
122
|
+
if (await (0, api_client_1.needsAuthentication)()) {
|
|
123
|
+
const authResult = await (0, device_flow_1.ensureAuthenticated)();
|
|
124
|
+
if (!authResult.authenticated) {
|
|
125
|
+
return AUTH_REQUIRED_MESSAGE;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const { projectId, collectionSlugs } = input;
|
|
129
|
+
// Validate input
|
|
130
|
+
if (!projectId) {
|
|
131
|
+
return `# Error: Missing projectId
|
|
132
|
+
|
|
133
|
+
Please provide a projectId. Use \`list_projects\` to see your available projects.
|
|
134
|
+
`;
|
|
135
|
+
}
|
|
136
|
+
// Resolve project ID
|
|
137
|
+
const resolved = await resolveProjectId(projectId);
|
|
138
|
+
if ('error' in resolved) {
|
|
139
|
+
return `# Project Not Found
|
|
140
|
+
|
|
141
|
+
${resolved.error}
|
|
142
|
+
`;
|
|
143
|
+
}
|
|
144
|
+
const { tenantId } = resolved;
|
|
145
|
+
// Fetch all collections for this project
|
|
146
|
+
const collectionsRes = await (0, api_client_1.apiRequest)('/api/collections', { tenantId });
|
|
147
|
+
if ((0, api_client_1.isApiError)(collectionsRes)) {
|
|
148
|
+
// Check if auth error
|
|
149
|
+
if (collectionsRes.error.includes('401') || collectionsRes.error.includes('auth')) {
|
|
150
|
+
const authResult = await (0, device_flow_1.ensureAuthenticated)();
|
|
151
|
+
if (!authResult.authenticated) {
|
|
152
|
+
return AUTH_REQUIRED_MESSAGE;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return `# Error
|
|
156
|
+
|
|
157
|
+
Failed to fetch collections: ${collectionsRes.error}
|
|
158
|
+
`;
|
|
159
|
+
}
|
|
160
|
+
const allCollections = collectionsRes.data;
|
|
161
|
+
if (allCollections.length === 0) {
|
|
162
|
+
return `# No Collections Found
|
|
163
|
+
|
|
164
|
+
This project has no collections yet. Use \`sync_schema\` to create collections first.
|
|
165
|
+
`;
|
|
166
|
+
}
|
|
167
|
+
// Determine which collections to generate samples for
|
|
168
|
+
let targetCollections;
|
|
169
|
+
if (collectionSlugs && collectionSlugs.length > 0) {
|
|
170
|
+
// Specific collections requested
|
|
171
|
+
targetCollections = [];
|
|
172
|
+
const notFound = [];
|
|
173
|
+
for (const slug of collectionSlugs) {
|
|
174
|
+
const col = allCollections.find(c => c.slug.toLowerCase() === slug.toLowerCase());
|
|
175
|
+
if (col) {
|
|
176
|
+
targetCollections.push(col);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
notFound.push(slug);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (notFound.length > 0) {
|
|
183
|
+
return `# Collections Not Found
|
|
184
|
+
|
|
185
|
+
The following collections were not found: ${notFound.join(', ')}
|
|
186
|
+
|
|
187
|
+
Available collections: ${allCollections.map(c => c.slug).join(', ')}
|
|
188
|
+
`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// All collections - check which ones are empty
|
|
193
|
+
const emptyCollections = [];
|
|
194
|
+
for (const col of allCollections) {
|
|
195
|
+
const itemsRes = await (0, api_client_1.apiRequest)(`/api/collections/${col.id}/items`, { tenantId });
|
|
196
|
+
if (!(0, api_client_1.isApiError)(itemsRes) && itemsRes.data.length === 0) {
|
|
197
|
+
emptyCollections.push(col);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (emptyCollections.length === 0) {
|
|
201
|
+
return `# No Empty Collections
|
|
202
|
+
|
|
203
|
+
All collections already have items. To regenerate samples for specific collections, provide their slugs.
|
|
204
|
+
|
|
205
|
+
Collections with items: ${allCollections.map(c => c.slug).join(', ')}
|
|
206
|
+
`;
|
|
207
|
+
}
|
|
208
|
+
targetCollections = emptyCollections;
|
|
209
|
+
}
|
|
210
|
+
// Sort collections by dependencies (parents first)
|
|
211
|
+
const sortedCollections = sortByDependencies(targetCollections);
|
|
212
|
+
// Generate samples for each collection in order
|
|
213
|
+
const results = [];
|
|
214
|
+
const generated = [];
|
|
215
|
+
const failed = [];
|
|
216
|
+
results.push('## Generation Order\n');
|
|
217
|
+
results.push('Collections are processed in dependency order (parent collections first):\n');
|
|
218
|
+
results.push(sortedCollections.map((c, i) => `${i + 1}. ${c.name} (\`${c.slug}\`)`).join('\n'));
|
|
219
|
+
results.push('\n');
|
|
220
|
+
for (const col of sortedCollections) {
|
|
221
|
+
// Check for relation fields and their dependencies
|
|
222
|
+
const relationFields = col.fields.filter(f => f.type === 'relation' && f.referenceCollection);
|
|
223
|
+
const dependencies = relationFields.map(f => f.referenceCollection).filter(Boolean);
|
|
224
|
+
let dependencyNote = '';
|
|
225
|
+
if (dependencies.length > 0) {
|
|
226
|
+
dependencyNote = ` (depends on: ${dependencies.join(', ')})`;
|
|
227
|
+
}
|
|
228
|
+
// Call the generate-samples endpoint
|
|
229
|
+
const generateRes = await (0, api_client_1.apiRequest)(`/api/collections/${col.id}/generate-samples`, {
|
|
230
|
+
tenantId,
|
|
231
|
+
method: 'POST',
|
|
232
|
+
});
|
|
233
|
+
if ((0, api_client_1.isApiError)(generateRes)) {
|
|
234
|
+
failed.push({ collection: col.slug, error: generateRes.error });
|
|
235
|
+
results.push(`❌ **${col.name}** - Failed: ${generateRes.error}${dependencyNote}`);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
const count = generateRes.data.data?.length || 5;
|
|
239
|
+
generated.push({ collection: col.slug, count });
|
|
240
|
+
results.push(`✅ **${col.name}** - Generated ${count} sample items${dependencyNote}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Build summary
|
|
244
|
+
let output = `# Sample Items Generated
|
|
245
|
+
|
|
246
|
+
**Project ID:** \`${tenantId}\`
|
|
247
|
+
|
|
248
|
+
## Summary
|
|
249
|
+
|
|
250
|
+
| Metric | Count |
|
|
251
|
+
|--------|-------|
|
|
252
|
+
| Collections Processed | ${sortedCollections.length} |
|
|
253
|
+
| Successfully Generated | ${generated.length} |
|
|
254
|
+
| Failed | ${failed.length} |
|
|
255
|
+
|
|
256
|
+
${results.join('\n')}
|
|
257
|
+
`;
|
|
258
|
+
if (failed.length > 0) {
|
|
259
|
+
output += `\n## Errors\n\n`;
|
|
260
|
+
for (const f of failed) {
|
|
261
|
+
output += `- **${f.collection}**: ${f.error}\n`;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (generated.length > 0) {
|
|
265
|
+
output += `\n---\n\n**Next Steps:**
|
|
266
|
+
- View the generated items in the CMS dashboard
|
|
267
|
+
- Edit or delete sample items as needed
|
|
268
|
+
- These are placeholder items - replace with real content when ready
|
|
269
|
+
`;
|
|
270
|
+
}
|
|
271
|
+
return output;
|
|
272
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type Section = 'full' | 'first_steps' | 'analysis' | 'structure' | 'seo' | 'manifest' | 'templates' | 'tokens' | 'forms' | 'assets' | 'checklist' | 'common_mistakes';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the conversion guide, either full or a specific section
|
|
4
|
+
*/
|
|
5
|
+
export declare function getConversionGuide(section: Section): Promise<string>;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=get-conversion-guide.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-conversion-guide.d.ts","sourceRoot":"","sources":["../../src/tools/get-conversion-guide.ts"],"names":[],"mappings":"AAAA,KAAK,OAAO,GAAG,MAAM,GAAG,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,KAAK,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,iBAAiB,CAAC;AAotCtK;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CA+F1E"}
|