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,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized API client for FastMode API requests
|
|
3
|
+
* Uses stored credentials with automatic refresh, or falls back to env var
|
|
4
|
+
*/
|
|
5
|
+
export interface ApiConfig {
|
|
6
|
+
apiUrl: string;
|
|
7
|
+
authToken: string | null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Get API URL from environment or default
|
|
11
|
+
*/
|
|
12
|
+
export declare function getApiUrl(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Get API configuration - tries stored credentials first, then env var
|
|
15
|
+
*/
|
|
16
|
+
export declare function getApiConfigAsync(): Promise<ApiConfig>;
|
|
17
|
+
/**
|
|
18
|
+
* Synchronous version - only checks env var (for quick checks)
|
|
19
|
+
*/
|
|
20
|
+
export declare function getApiConfig(): ApiConfig;
|
|
21
|
+
/**
|
|
22
|
+
* Check if authentication is configured (either stored credentials or env var)
|
|
23
|
+
*/
|
|
24
|
+
export declare function isAuthConfigured(): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Check if we need to trigger the device flow
|
|
27
|
+
*/
|
|
28
|
+
export declare function needsAuthentication(): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Get a helpful message for authentication
|
|
31
|
+
*/
|
|
32
|
+
export declare function getAuthRequiredMessage(): string;
|
|
33
|
+
/**
|
|
34
|
+
* API request options
|
|
35
|
+
*/
|
|
36
|
+
export interface ApiRequestOptions {
|
|
37
|
+
tenantId?: string;
|
|
38
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
39
|
+
body?: unknown;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* API error response
|
|
43
|
+
*/
|
|
44
|
+
export interface ApiError {
|
|
45
|
+
success: false;
|
|
46
|
+
error: string;
|
|
47
|
+
statusCode: number;
|
|
48
|
+
needsAuth?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Make an authenticated API request
|
|
52
|
+
* Uses stored credentials with auto-refresh, or falls back to env var
|
|
53
|
+
*/
|
|
54
|
+
export declare function apiRequest<T>(endpoint: string, options?: ApiRequestOptions): Promise<{
|
|
55
|
+
data: T;
|
|
56
|
+
} | ApiError>;
|
|
57
|
+
/**
|
|
58
|
+
* Check if a response is an error
|
|
59
|
+
*/
|
|
60
|
+
export declare function isApiError(response: unknown): response is ApiError;
|
|
61
|
+
/**
|
|
62
|
+
* Check if error requires authentication
|
|
63
|
+
*/
|
|
64
|
+
export declare function needsAuthError(error: ApiError): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a project identifier to a tenant ID
|
|
67
|
+
* Accepts either a UUID or a project name
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveProjectId(projectIdentifier: string): Promise<{
|
|
70
|
+
tenantId: string;
|
|
71
|
+
} | {
|
|
72
|
+
error: string;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Make an authenticated external API request
|
|
76
|
+
* Uses the /external/* endpoints with tenant context
|
|
77
|
+
*/
|
|
78
|
+
export declare function externalApiRequest<T>(tenantId: string, endpoint: string, options?: Omit<ApiRequestOptions, 'tenantId'>): Promise<{
|
|
79
|
+
data: T;
|
|
80
|
+
} | ApiError>;
|
|
81
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,SAAS,CAAC,CAgB5D;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,SAAS,CAKxC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAW5D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAS/C;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC3C,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG,QAAQ,CAAC,CAoEjC;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,IAAI,QAAQ,CAOlE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAEvD;AAYD;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAwCnH;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAM,GAChD,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG,QAAQ,CAAC,CAOjC"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Centralized API client for FastMode API requests
|
|
4
|
+
* Uses stored credentials with automatic refresh, or falls back to env var
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.getApiUrl = getApiUrl;
|
|
41
|
+
exports.getApiConfigAsync = getApiConfigAsync;
|
|
42
|
+
exports.getApiConfig = getApiConfig;
|
|
43
|
+
exports.isAuthConfigured = isAuthConfigured;
|
|
44
|
+
exports.needsAuthentication = needsAuthentication;
|
|
45
|
+
exports.getAuthRequiredMessage = getAuthRequiredMessage;
|
|
46
|
+
exports.apiRequest = apiRequest;
|
|
47
|
+
exports.isApiError = isApiError;
|
|
48
|
+
exports.needsAuthError = needsAuthError;
|
|
49
|
+
exports.resolveProjectId = resolveProjectId;
|
|
50
|
+
exports.externalApiRequest = externalApiRequest;
|
|
51
|
+
const credentials_1 = require("./credentials");
|
|
52
|
+
/**
|
|
53
|
+
* Get API URL from environment or default
|
|
54
|
+
*/
|
|
55
|
+
function getApiUrl() {
|
|
56
|
+
return process.env.FASTMODE_API_URL || 'https://api.fastmode.ai';
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get API configuration - tries stored credentials first, then env var
|
|
60
|
+
*/
|
|
61
|
+
async function getApiConfigAsync() {
|
|
62
|
+
// First, try to get valid stored credentials
|
|
63
|
+
const credentials = await (0, credentials_1.getValidCredentials)();
|
|
64
|
+
if (credentials) {
|
|
65
|
+
return {
|
|
66
|
+
apiUrl: getApiUrl(),
|
|
67
|
+
authToken: credentials.accessToken,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Fall back to environment variable (for backward compatibility)
|
|
71
|
+
return {
|
|
72
|
+
apiUrl: getApiUrl(),
|
|
73
|
+
authToken: process.env.FASTMODE_AUTH_TOKEN || null,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Synchronous version - only checks env var (for quick checks)
|
|
78
|
+
*/
|
|
79
|
+
function getApiConfig() {
|
|
80
|
+
return {
|
|
81
|
+
apiUrl: getApiUrl(),
|
|
82
|
+
authToken: process.env.FASTMODE_AUTH_TOKEN || null,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Check if authentication is configured (either stored credentials or env var)
|
|
87
|
+
*/
|
|
88
|
+
function isAuthConfigured() {
|
|
89
|
+
return (0, credentials_1.hasCredentials)() || !!process.env.FASTMODE_AUTH_TOKEN;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check if we need to trigger the device flow
|
|
93
|
+
*/
|
|
94
|
+
async function needsAuthentication() {
|
|
95
|
+
// Wait for any startup auth that might be in progress
|
|
96
|
+
const { waitForAuth } = await Promise.resolve().then(() => __importStar(require('./auth-state')));
|
|
97
|
+
await waitForAuth();
|
|
98
|
+
const credentials = await (0, credentials_1.getValidCredentials)();
|
|
99
|
+
if (credentials)
|
|
100
|
+
return false;
|
|
101
|
+
if (process.env.FASTMODE_AUTH_TOKEN)
|
|
102
|
+
return false;
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get a helpful message for authentication
|
|
107
|
+
*/
|
|
108
|
+
function getAuthRequiredMessage() {
|
|
109
|
+
return `# Authentication Required
|
|
110
|
+
|
|
111
|
+
You need to authenticate to use this tool.
|
|
112
|
+
|
|
113
|
+
**Starting authentication flow...**
|
|
114
|
+
|
|
115
|
+
A browser window will open for you to log in. If it doesn't open automatically, you'll see a URL to visit.
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Make an authenticated API request
|
|
120
|
+
* Uses stored credentials with auto-refresh, or falls back to env var
|
|
121
|
+
*/
|
|
122
|
+
async function apiRequest(endpoint, options = {}) {
|
|
123
|
+
const config = await getApiConfigAsync();
|
|
124
|
+
if (!config.authToken) {
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: 'Not authenticated',
|
|
128
|
+
statusCode: 401,
|
|
129
|
+
needsAuth: true,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const url = `${config.apiUrl}${endpoint.startsWith('/') ? endpoint : '/' + endpoint}`;
|
|
133
|
+
const headers = {
|
|
134
|
+
'Authorization': `Bearer ${config.authToken}`,
|
|
135
|
+
'Content-Type': 'application/json',
|
|
136
|
+
};
|
|
137
|
+
if (options.tenantId) {
|
|
138
|
+
headers['X-Tenant-Id'] = options.tenantId;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
const response = await fetch(url, {
|
|
142
|
+
method: options.method || 'GET',
|
|
143
|
+
headers,
|
|
144
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
145
|
+
});
|
|
146
|
+
const data = await response.json();
|
|
147
|
+
if (!response.ok) {
|
|
148
|
+
// Check if this is an auth error
|
|
149
|
+
if (response.status === 401) {
|
|
150
|
+
return {
|
|
151
|
+
success: false,
|
|
152
|
+
error: 'Authentication expired or invalid',
|
|
153
|
+
statusCode: 401,
|
|
154
|
+
needsAuth: true,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
success: false,
|
|
159
|
+
error: data.error || `API request failed with status ${response.status}`,
|
|
160
|
+
statusCode: response.status,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return data;
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
167
|
+
if (message.includes('fetch') || message.includes('network') || message.includes('ECONNREFUSED')) {
|
|
168
|
+
return {
|
|
169
|
+
success: false,
|
|
170
|
+
error: `Network error: Unable to connect to ${config.apiUrl}`,
|
|
171
|
+
statusCode: 0,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
success: false,
|
|
176
|
+
error: message,
|
|
177
|
+
statusCode: 0,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Check if a response is an error
|
|
183
|
+
*/
|
|
184
|
+
function isApiError(response) {
|
|
185
|
+
return (typeof response === 'object' &&
|
|
186
|
+
response !== null &&
|
|
187
|
+
'success' in response &&
|
|
188
|
+
response.success === false);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Check if error requires authentication
|
|
192
|
+
*/
|
|
193
|
+
function needsAuthError(error) {
|
|
194
|
+
return error.needsAuth === true || error.statusCode === 401;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Resolve a project identifier to a tenant ID
|
|
198
|
+
* Accepts either a UUID or a project name
|
|
199
|
+
*/
|
|
200
|
+
async function resolveProjectId(projectIdentifier) {
|
|
201
|
+
// Check if it looks like a UUID
|
|
202
|
+
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
203
|
+
if (uuidPattern.test(projectIdentifier)) {
|
|
204
|
+
return { tenantId: projectIdentifier };
|
|
205
|
+
}
|
|
206
|
+
// Look up by name using /api/tenants
|
|
207
|
+
const response = await apiRequest('/api/tenants');
|
|
208
|
+
if (isApiError(response)) {
|
|
209
|
+
return { error: `Failed to look up project: ${response.error}` };
|
|
210
|
+
}
|
|
211
|
+
const projects = response.data;
|
|
212
|
+
// Find by exact name match (case-insensitive)
|
|
213
|
+
const match = projects.find(p => p.name.toLowerCase() === projectIdentifier.toLowerCase());
|
|
214
|
+
if (match) {
|
|
215
|
+
return { tenantId: match.id };
|
|
216
|
+
}
|
|
217
|
+
// Try partial match
|
|
218
|
+
const partialMatch = projects.find(p => p.name.toLowerCase().includes(projectIdentifier.toLowerCase()));
|
|
219
|
+
if (partialMatch) {
|
|
220
|
+
return { tenantId: partialMatch.id };
|
|
221
|
+
}
|
|
222
|
+
const availableProjects = projects.map(p => `- ${p.name} (${p.id})`).join('\n');
|
|
223
|
+
return {
|
|
224
|
+
error: `Project "${projectIdentifier}" not found.\n\nAvailable projects:\n${availableProjects || 'None'}`
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Make an authenticated external API request
|
|
229
|
+
* Uses the /external/* endpoints with tenant context
|
|
230
|
+
*/
|
|
231
|
+
async function externalApiRequest(tenantId, endpoint, options = {}) {
|
|
232
|
+
// External API endpoints are at /external/...
|
|
233
|
+
const fullEndpoint = endpoint.startsWith('/external')
|
|
234
|
+
? endpoint
|
|
235
|
+
: `/external${endpoint.startsWith('/') ? endpoint : '/' + endpoint}`;
|
|
236
|
+
return apiRequest(fullEndpoint, { ...options, tenantId });
|
|
237
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared authentication state
|
|
3
|
+
* Separate file to avoid circular dependencies
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Set the in-progress auth promise
|
|
7
|
+
*/
|
|
8
|
+
export declare function setAuthInProgress(promise: Promise<string> | null): void;
|
|
9
|
+
/**
|
|
10
|
+
* Wait for any in-progress authentication to complete
|
|
11
|
+
*/
|
|
12
|
+
export declare function waitForAuth(): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=auth-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-state.d.ts","sourceRoot":"","sources":["../../src/lib/auth-state.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAEvE;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAIjD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared authentication state
|
|
4
|
+
* Separate file to avoid circular dependencies
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.setAuthInProgress = setAuthInProgress;
|
|
8
|
+
exports.waitForAuth = waitForAuth;
|
|
9
|
+
// Track in-progress authentication
|
|
10
|
+
let authInProgress = null;
|
|
11
|
+
/**
|
|
12
|
+
* Set the in-progress auth promise
|
|
13
|
+
*/
|
|
14
|
+
function setAuthInProgress(promise) {
|
|
15
|
+
authInProgress = promise;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Wait for any in-progress authentication to complete
|
|
19
|
+
*/
|
|
20
|
+
async function waitForAuth() {
|
|
21
|
+
if (authInProgress) {
|
|
22
|
+
await authInProgress;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Fetcher
|
|
3
|
+
*
|
|
4
|
+
* Centralizes logic for fetching project state, collections, and content.
|
|
5
|
+
* Used by get_started and other tools that need project context.
|
|
6
|
+
*/
|
|
7
|
+
export interface ProjectSummary {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
subdomain: string;
|
|
11
|
+
customDomain: string | null;
|
|
12
|
+
status: 'pending' | 'building' | 'published' | 'failed';
|
|
13
|
+
hasCollections: boolean;
|
|
14
|
+
collectionCount: number;
|
|
15
|
+
hasContent: boolean;
|
|
16
|
+
totalItems: number;
|
|
17
|
+
hasPendingChanges: boolean;
|
|
18
|
+
lastDeployedAt: string | null;
|
|
19
|
+
githubConnected: boolean;
|
|
20
|
+
githubRepo: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface CollectionField {
|
|
23
|
+
slug: string;
|
|
24
|
+
name: string;
|
|
25
|
+
type: string;
|
|
26
|
+
isRequired: boolean;
|
|
27
|
+
referenceCollection?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface CollectionSummary {
|
|
30
|
+
slug: string;
|
|
31
|
+
name: string;
|
|
32
|
+
nameSingular: string;
|
|
33
|
+
fieldCount: number;
|
|
34
|
+
itemCount: number;
|
|
35
|
+
fields: CollectionField[];
|
|
36
|
+
}
|
|
37
|
+
export interface ProjectDetail {
|
|
38
|
+
id: string;
|
|
39
|
+
name: string;
|
|
40
|
+
subdomain: string;
|
|
41
|
+
customDomain: string | null;
|
|
42
|
+
status: 'pending' | 'building' | 'published' | 'failed';
|
|
43
|
+
collections: CollectionSummary[];
|
|
44
|
+
hasPendingChanges: boolean;
|
|
45
|
+
lastDeployedAt: string | null;
|
|
46
|
+
githubConnected: boolean;
|
|
47
|
+
}
|
|
48
|
+
export interface ProjectContext {
|
|
49
|
+
isAuthenticated: boolean;
|
|
50
|
+
projects: ProjectSummary[];
|
|
51
|
+
selectedProject?: ProjectDetail;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Fetch complete project context
|
|
55
|
+
*
|
|
56
|
+
* @param projectId - Optional specific project to get detailed info for
|
|
57
|
+
*/
|
|
58
|
+
export declare function fetchProjectContext(projectId?: string): Promise<ProjectContext>;
|
|
59
|
+
/**
|
|
60
|
+
* Resolve a project identifier to a tenant ID
|
|
61
|
+
*/
|
|
62
|
+
export declare function resolveProjectId(projectIdentifier: string): Promise<{
|
|
63
|
+
tenantId: string;
|
|
64
|
+
name: string;
|
|
65
|
+
subdomain: string;
|
|
66
|
+
} | null>;
|
|
67
|
+
//# sourceMappingURL=context-fetcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-fetcher.d.ts","sourceRoot":"","sources":["../../src/lib/context-fetcher.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAC;IACxD,cAAc,EAAE,OAAO,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAC;IACxD,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,aAAa,CAAC;CACjC;AAgHD;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA6GrF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAsCvI"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Context Fetcher
|
|
4
|
+
*
|
|
5
|
+
* Centralizes logic for fetching project state, collections, and content.
|
|
6
|
+
* Used by get_started and other tools that need project context.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.fetchProjectContext = fetchProjectContext;
|
|
10
|
+
exports.resolveProjectId = resolveProjectId;
|
|
11
|
+
const api_client_1 = require("./api-client");
|
|
12
|
+
// ============ Helper Functions ============
|
|
13
|
+
/**
|
|
14
|
+
* Fetch all projects for the authenticated user
|
|
15
|
+
*/
|
|
16
|
+
async function fetchProjects() {
|
|
17
|
+
const response = await (0, api_client_1.apiRequest)('/api/tenants');
|
|
18
|
+
if ((0, api_client_1.isApiError)(response)) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
return response.data;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Fetch collections for a specific project
|
|
25
|
+
*/
|
|
26
|
+
async function fetchCollections(tenantId) {
|
|
27
|
+
const response = await (0, api_client_1.apiRequest)('/api/collections', { tenantId });
|
|
28
|
+
if ((0, api_client_1.isApiError)(response)) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
return response.data;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Fetch GitHub connection status for a project
|
|
35
|
+
*/
|
|
36
|
+
async function fetchGitHubStatus(tenantId) {
|
|
37
|
+
const response = await (0, api_client_1.apiRequest)('/api/github/site-connection', { tenantId });
|
|
38
|
+
if ((0, api_client_1.isApiError)(response)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return response.data;
|
|
42
|
+
}
|
|
43
|
+
// Note: fetchLatestDeploy could be used for detailed deploy state in the future
|
|
44
|
+
// async function fetchLatestDeploy(tenantId: string): Promise<DeployLog | null> {
|
|
45
|
+
// const response = await apiRequest<DeployLog[]>('/api/deploy/history?limit=1', { tenantId });
|
|
46
|
+
// if (isApiError(response) || !response.data || response.data.length === 0) {
|
|
47
|
+
// return null;
|
|
48
|
+
// }
|
|
49
|
+
// return response.data[0];
|
|
50
|
+
// }
|
|
51
|
+
/**
|
|
52
|
+
* Fetch item counts for collections
|
|
53
|
+
*/
|
|
54
|
+
async function fetchCollectionItemCounts(tenantId, collectionSlugs) {
|
|
55
|
+
const counts = new Map();
|
|
56
|
+
// Fetch counts in parallel (limit concurrency)
|
|
57
|
+
const promises = collectionSlugs.slice(0, 10).map(async (slug) => {
|
|
58
|
+
const response = await (0, api_client_1.apiRequest)(`/api/collections/${slug}/items?limit=0`, { tenantId });
|
|
59
|
+
if (!(0, api_client_1.isApiError)(response) && response.data) {
|
|
60
|
+
counts.set(slug, response.data.total || 0);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
await Promise.all(promises);
|
|
64
|
+
return counts;
|
|
65
|
+
}
|
|
66
|
+
// ============ Main Functions ============
|
|
67
|
+
/**
|
|
68
|
+
* Fetch complete project context
|
|
69
|
+
*
|
|
70
|
+
* @param projectId - Optional specific project to get detailed info for
|
|
71
|
+
*/
|
|
72
|
+
async function fetchProjectContext(projectId) {
|
|
73
|
+
// Check authentication
|
|
74
|
+
const isAuthenticated = !(await (0, api_client_1.needsAuthentication)());
|
|
75
|
+
if (!isAuthenticated) {
|
|
76
|
+
return {
|
|
77
|
+
isAuthenticated: false,
|
|
78
|
+
projects: [],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// Fetch all projects
|
|
82
|
+
const tenants = await fetchProjects();
|
|
83
|
+
// Build project summaries
|
|
84
|
+
const projects = await Promise.all(tenants.map(async (tenant) => {
|
|
85
|
+
// Fetch collections for this project (for count)
|
|
86
|
+
const collections = await fetchCollections(tenant.id);
|
|
87
|
+
const collectionCount = collections.length;
|
|
88
|
+
// Calculate total items
|
|
89
|
+
let totalItems = 0;
|
|
90
|
+
for (const col of collections) {
|
|
91
|
+
if (col._count?.items) {
|
|
92
|
+
totalItems += col._count.items;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Fetch GitHub status
|
|
96
|
+
const github = await fetchGitHubStatus(tenant.id);
|
|
97
|
+
return {
|
|
98
|
+
id: tenant.id,
|
|
99
|
+
name: tenant.name,
|
|
100
|
+
subdomain: tenant.subdomain,
|
|
101
|
+
customDomain: tenant.customDomain || null,
|
|
102
|
+
status: (tenant.site?.status || 'pending'),
|
|
103
|
+
hasCollections: collectionCount > 0,
|
|
104
|
+
collectionCount,
|
|
105
|
+
hasContent: totalItems > 0,
|
|
106
|
+
totalItems,
|
|
107
|
+
hasPendingChanges: tenant.site?.hasPendingChanges || false,
|
|
108
|
+
lastDeployedAt: tenant.site?.updatedAt || null,
|
|
109
|
+
githubConnected: github?.connected || false,
|
|
110
|
+
githubRepo: github?.repo || null,
|
|
111
|
+
};
|
|
112
|
+
}));
|
|
113
|
+
// If a specific project is requested, get detailed info
|
|
114
|
+
let selectedProject;
|
|
115
|
+
if (projectId) {
|
|
116
|
+
// Find the project
|
|
117
|
+
const tenant = tenants.find(t => t.id === projectId ||
|
|
118
|
+
t.name.toLowerCase() === projectId.toLowerCase() ||
|
|
119
|
+
t.subdomain.toLowerCase() === projectId.toLowerCase());
|
|
120
|
+
if (tenant) {
|
|
121
|
+
// Fetch collections with full field details
|
|
122
|
+
const collections = await fetchCollections(tenant.id);
|
|
123
|
+
// Fetch item counts
|
|
124
|
+
const itemCounts = await fetchCollectionItemCounts(tenant.id, collections.map(c => c.slug));
|
|
125
|
+
// Fetch GitHub status
|
|
126
|
+
const github = await fetchGitHubStatus(tenant.id);
|
|
127
|
+
// Build collection summaries
|
|
128
|
+
const collectionSummaries = collections.map(col => ({
|
|
129
|
+
slug: col.slug,
|
|
130
|
+
name: col.name,
|
|
131
|
+
nameSingular: col.nameSingular,
|
|
132
|
+
fieldCount: col.fields.length,
|
|
133
|
+
itemCount: itemCounts.get(col.slug) || col._count?.items || 0,
|
|
134
|
+
fields: col.fields.map(f => ({
|
|
135
|
+
slug: f.slug,
|
|
136
|
+
name: f.name,
|
|
137
|
+
type: f.type,
|
|
138
|
+
isRequired: f.isRequired || false,
|
|
139
|
+
referenceCollection: f.referenceCollection,
|
|
140
|
+
})),
|
|
141
|
+
}));
|
|
142
|
+
selectedProject = {
|
|
143
|
+
id: tenant.id,
|
|
144
|
+
name: tenant.name,
|
|
145
|
+
subdomain: tenant.subdomain,
|
|
146
|
+
customDomain: tenant.customDomain || null,
|
|
147
|
+
status: (tenant.site?.status || 'pending'),
|
|
148
|
+
collections: collectionSummaries,
|
|
149
|
+
hasPendingChanges: tenant.site?.hasPendingChanges || false,
|
|
150
|
+
lastDeployedAt: tenant.site?.updatedAt || null,
|
|
151
|
+
githubConnected: github?.connected || false,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
isAuthenticated: true,
|
|
157
|
+
projects,
|
|
158
|
+
selectedProject,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Resolve a project identifier to a tenant ID
|
|
163
|
+
*/
|
|
164
|
+
async function resolveProjectId(projectIdentifier) {
|
|
165
|
+
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
166
|
+
const tenants = await fetchProjects();
|
|
167
|
+
// Try exact UUID match
|
|
168
|
+
if (uuidPattern.test(projectIdentifier)) {
|
|
169
|
+
const tenant = tenants.find(t => t.id === projectIdentifier);
|
|
170
|
+
if (tenant) {
|
|
171
|
+
return { tenantId: tenant.id, name: tenant.name, subdomain: tenant.subdomain };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Try exact name match
|
|
175
|
+
const exactMatch = tenants.find(t => t.name.toLowerCase() === projectIdentifier.toLowerCase());
|
|
176
|
+
if (exactMatch) {
|
|
177
|
+
return { tenantId: exactMatch.id, name: exactMatch.name, subdomain: exactMatch.subdomain };
|
|
178
|
+
}
|
|
179
|
+
// Try subdomain match
|
|
180
|
+
const subdomainMatch = tenants.find(t => t.subdomain.toLowerCase() === projectIdentifier.toLowerCase());
|
|
181
|
+
if (subdomainMatch) {
|
|
182
|
+
return { tenantId: subdomainMatch.id, name: subdomainMatch.name, subdomain: subdomainMatch.subdomain };
|
|
183
|
+
}
|
|
184
|
+
// Try partial match
|
|
185
|
+
const partialMatch = tenants.find(t => t.name.toLowerCase().includes(projectIdentifier.toLowerCase()));
|
|
186
|
+
if (partialMatch) {
|
|
187
|
+
return { tenantId: partialMatch.id, name: partialMatch.name, subdomain: partialMatch.subdomain };
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stored credentials for the MCP server
|
|
3
|
+
*/
|
|
4
|
+
export interface StoredCredentials {
|
|
5
|
+
accessToken: string;
|
|
6
|
+
refreshToken: string;
|
|
7
|
+
expiresAt: string;
|
|
8
|
+
email: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get the path to the credentials file
|
|
13
|
+
*/
|
|
14
|
+
export declare function getCredentialsPath(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Load stored credentials from disk
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadCredentials(): StoredCredentials | null;
|
|
19
|
+
/**
|
|
20
|
+
* Save credentials to disk
|
|
21
|
+
*/
|
|
22
|
+
export declare function saveCredentials(credentials: StoredCredentials): void;
|
|
23
|
+
/**
|
|
24
|
+
* Delete stored credentials
|
|
25
|
+
*/
|
|
26
|
+
export declare function deleteCredentials(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Check if credentials exist
|
|
29
|
+
*/
|
|
30
|
+
export declare function hasCredentials(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Check if the access token is expired or about to expire
|
|
33
|
+
*/
|
|
34
|
+
export declare function isTokenExpired(credentials: StoredCredentials, bufferMinutes?: number): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Get the API URL from environment or default
|
|
37
|
+
*/
|
|
38
|
+
export declare function getApiUrl(): string;
|
|
39
|
+
/**
|
|
40
|
+
* Refresh the access token using the refresh token
|
|
41
|
+
*/
|
|
42
|
+
export declare function refreshAccessToken(credentials: StoredCredentials): Promise<StoredCredentials | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Get valid credentials, refreshing if needed
|
|
45
|
+
* Returns null if no credentials or refresh fails
|
|
46
|
+
*/
|
|
47
|
+
export declare function getValidCredentials(): Promise<StoredCredentials | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Get the current auth token (access token) if available and valid
|
|
50
|
+
*/
|
|
51
|
+
export declare function getAuthToken(): Promise<string | null>;
|
|
52
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/lib/credentials.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAG3C;AAYD;;GAEG;AACH,wBAAgB,eAAe,IAAI,iBAAiB,GAAG,IAAI,CAoB1D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAUpE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAUxC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,iBAAiB,EAAE,aAAa,GAAE,MAAU,GAAG,OAAO,CAIjG;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAyC1G;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAoB7E;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAG3D"}
|