latitude-mcp-server 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/.releaserc.json +34 -0
- package/README.md +687 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.js +43 -0
- package/dist/cli/latitude.cli.d.ts +10 -0
- package/dist/cli/latitude.cli.js +286 -0
- package/dist/controllers/latitude.controller.d.ts +115 -0
- package/dist/controllers/latitude.controller.js +287 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +166 -0
- package/dist/resources/latitude.resource.d.ts +12 -0
- package/dist/resources/latitude.resource.js +145 -0
- package/dist/services/vendor.latitude.service.d.ts +49 -0
- package/dist/services/vendor.latitude.service.js +294 -0
- package/dist/tools/latitude.tool.d.ts +6 -0
- package/dist/tools/latitude.tool.js +517 -0
- package/dist/types/common.types.d.ts +20 -0
- package/dist/types/common.types.js +7 -0
- package/dist/types/latitude.types.d.ts +487 -0
- package/dist/types/latitude.types.js +311 -0
- package/dist/utils/cli.test.util.d.ts +34 -0
- package/dist/utils/cli.test.util.js +143 -0
- package/dist/utils/config.util.d.ts +43 -0
- package/dist/utils/config.util.js +145 -0
- package/dist/utils/config.util.test.d.ts +1 -0
- package/dist/utils/constants.util.d.ts +26 -0
- package/dist/utils/constants.util.js +29 -0
- package/dist/utils/error-handler.util.d.ts +54 -0
- package/dist/utils/error-handler.util.js +202 -0
- package/dist/utils/error-handler.util.test.d.ts +1 -0
- package/dist/utils/error.util.d.ts +73 -0
- package/dist/utils/error.util.js +174 -0
- package/dist/utils/error.util.test.d.ts +1 -0
- package/dist/utils/formatter.util.d.ts +36 -0
- package/dist/utils/formatter.util.js +116 -0
- package/dist/utils/jest.setup.d.ts +5 -0
- package/dist/utils/jest.setup.js +36 -0
- package/dist/utils/jq.util.d.ts +34 -0
- package/dist/utils/jq.util.js +87 -0
- package/dist/utils/logger.util.d.ts +78 -0
- package/dist/utils/logger.util.js +344 -0
- package/dist/utils/toon.util.d.ts +15 -0
- package/dist/utils/toon.util.js +65 -0
- package/dist/utils/transport.util.d.ts +49 -0
- package/dist/utils/transport.util.js +162 -0
- package/eslint.config.mjs +46 -0
- package/openapi.json +12592 -0
- package/package.json +118 -0
- package/scripts/ensure-executable.js +38 -0
- package/scripts/package.json +3 -0
- package/scripts/update-version.js +204 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CreateLogInputSchema = exports.StopConversationInputSchema = exports.GetConversationInputSchema = exports.ChatInputSchema = exports.PushChangesInputSchema = exports.RunPromptInputSchema = exports.PushPromptFromFileInputSchema = exports.PushPromptInputSchema = exports.GetPromptInputSchema = exports.ListPromptsInputSchema = exports.PublishVersionInputSchema = exports.CreateVersionInputSchema = exports.GetVersionInputSchema = exports.ListVersionsInputSchema = exports.CreateProjectInputSchema = exports.ListProjectsInputSchema = exports.LatitudeErrorSchema = exports.StreamEventSchema = exports.StreamEventTypeEnum = exports.CreateLogRequestSchema = exports.ChatRequestSchema = exports.ConversationSchema = exports.MessageSchema = exports.ContentPartSchema = exports.FileContentSchema = exports.ImageContentSchema = exports.TextContentSchema = exports.MessageRoleEnum = exports.RunRequestSchema = exports.RunSourceEnum = exports.PushChangesSchema = exports.DocumentChangeSchema = exports.ChangeStatusEnum = exports.DocumentListSchema = exports.DocumentSchema = exports.ParameterTypeSchema = exports.ProviderEnum = exports.VersionListSchema = exports.VersionSchema = exports.ProjectListSchema = exports.ProjectSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* Latitude API Types and Zod Schemas
|
|
7
|
+
*
|
|
8
|
+
* Based on OpenAPI spec from openapi.json
|
|
9
|
+
* API Version: 1.0.2
|
|
10
|
+
* Base URL: https://gateway.latitude.so
|
|
11
|
+
*/
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Project Types
|
|
14
|
+
// ============================================================================
|
|
15
|
+
exports.ProjectSchema = zod_1.z.object({
|
|
16
|
+
id: zod_1.z.number(),
|
|
17
|
+
name: zod_1.z.string(),
|
|
18
|
+
workspaceId: zod_1.z.number(),
|
|
19
|
+
createdAt: zod_1.z.string(),
|
|
20
|
+
updatedAt: zod_1.z.string(),
|
|
21
|
+
lastEditedAt: zod_1.z.string().optional(),
|
|
22
|
+
deletedAt: zod_1.z.string().nullable().optional(),
|
|
23
|
+
});
|
|
24
|
+
exports.ProjectListSchema = zod_1.z.array(exports.ProjectSchema);
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Version Types
|
|
27
|
+
// ============================================================================
|
|
28
|
+
exports.VersionSchema = zod_1.z.object({
|
|
29
|
+
id: zod_1.z.number(),
|
|
30
|
+
uuid: zod_1.z.string(),
|
|
31
|
+
title: zod_1.z.string(),
|
|
32
|
+
description: zod_1.z.string().nullable(),
|
|
33
|
+
projectId: zod_1.z.number(),
|
|
34
|
+
version: zod_1.z.number().nullable(),
|
|
35
|
+
userId: zod_1.z.string(),
|
|
36
|
+
mergedAt: zod_1.z.string().nullable(),
|
|
37
|
+
createdAt: zod_1.z.string(),
|
|
38
|
+
updatedAt: zod_1.z.string(),
|
|
39
|
+
deletedAt: zod_1.z.string().nullable(),
|
|
40
|
+
status: zod_1.z.string().optional(),
|
|
41
|
+
message: zod_1.z.string().optional(),
|
|
42
|
+
authorName: zod_1.z.string().nullable().optional(),
|
|
43
|
+
authorEmail: zod_1.z.string().nullable().optional(),
|
|
44
|
+
authorId: zod_1.z.number().nullable().optional(),
|
|
45
|
+
parentCommitUuid: zod_1.z.string().nullable().optional(),
|
|
46
|
+
});
|
|
47
|
+
exports.VersionListSchema = zod_1.z.array(exports.VersionSchema);
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Document/Prompt Types
|
|
50
|
+
// ============================================================================
|
|
51
|
+
exports.ProviderEnum = zod_1.z.enum([
|
|
52
|
+
'openai',
|
|
53
|
+
'anthropic',
|
|
54
|
+
'groq',
|
|
55
|
+
'mistral',
|
|
56
|
+
'azure',
|
|
57
|
+
'google',
|
|
58
|
+
'google_vertex',
|
|
59
|
+
'anthropic_vertex',
|
|
60
|
+
'custom',
|
|
61
|
+
'xai',
|
|
62
|
+
'amazon_bedrock',
|
|
63
|
+
'deepseek',
|
|
64
|
+
'perplexity',
|
|
65
|
+
]);
|
|
66
|
+
exports.ParameterTypeSchema = zod_1.z.object({
|
|
67
|
+
type: zod_1.z.enum(['text', 'image', 'file']),
|
|
68
|
+
});
|
|
69
|
+
exports.DocumentSchema = zod_1.z.object({
|
|
70
|
+
versionUuid: zod_1.z.string(),
|
|
71
|
+
uuid: zod_1.z.string(),
|
|
72
|
+
path: zod_1.z.string(),
|
|
73
|
+
content: zod_1.z.string(),
|
|
74
|
+
contentHash: zod_1.z.string().optional(),
|
|
75
|
+
config: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
76
|
+
parameters: zod_1.z.record(zod_1.z.string(), exports.ParameterTypeSchema).optional(),
|
|
77
|
+
provider: exports.ProviderEnum.optional(),
|
|
78
|
+
});
|
|
79
|
+
exports.DocumentListSchema = zod_1.z.array(exports.DocumentSchema);
|
|
80
|
+
// ============================================================================
|
|
81
|
+
// Document Change Types (for Push)
|
|
82
|
+
// ============================================================================
|
|
83
|
+
exports.ChangeStatusEnum = zod_1.z.enum(['added', 'modified', 'deleted', 'unchanged']);
|
|
84
|
+
exports.DocumentChangeSchema = zod_1.z.object({
|
|
85
|
+
path: zod_1.z.string(),
|
|
86
|
+
content: zod_1.z.string(),
|
|
87
|
+
status: exports.ChangeStatusEnum.default('modified'),
|
|
88
|
+
contentHash: zod_1.z.string().optional(),
|
|
89
|
+
});
|
|
90
|
+
exports.PushChangesSchema = zod_1.z.object({
|
|
91
|
+
changes: zod_1.z.array(exports.DocumentChangeSchema),
|
|
92
|
+
});
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Run/Execute Types
|
|
95
|
+
// ============================================================================
|
|
96
|
+
exports.RunSourceEnum = zod_1.z.enum([
|
|
97
|
+
'api',
|
|
98
|
+
'agent_as_tool',
|
|
99
|
+
'copilot',
|
|
100
|
+
'email_trigger',
|
|
101
|
+
'evaluation',
|
|
102
|
+
'experiment',
|
|
103
|
+
'integration_trigger',
|
|
104
|
+
'playground',
|
|
105
|
+
'scheduled_trigger',
|
|
106
|
+
'shared_prompt',
|
|
107
|
+
'user',
|
|
108
|
+
]);
|
|
109
|
+
exports.RunRequestSchema = zod_1.z.object({
|
|
110
|
+
path: zod_1.z.string(),
|
|
111
|
+
stream: zod_1.z.boolean().default(false),
|
|
112
|
+
customIdentifier: zod_1.z.string().optional(),
|
|
113
|
+
parameters: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
114
|
+
tools: zod_1.z.array(zod_1.z.string()).optional(),
|
|
115
|
+
userMessage: zod_1.z.string().optional(),
|
|
116
|
+
background: zod_1.z.boolean().optional(),
|
|
117
|
+
__internal: zod_1.z
|
|
118
|
+
.object({
|
|
119
|
+
source: exports.RunSourceEnum.optional(),
|
|
120
|
+
})
|
|
121
|
+
.optional(),
|
|
122
|
+
});
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// Conversation Types
|
|
125
|
+
// ============================================================================
|
|
126
|
+
exports.MessageRoleEnum = zod_1.z.enum(['system', 'user', 'assistant', 'tool']);
|
|
127
|
+
exports.TextContentSchema = zod_1.z.object({
|
|
128
|
+
type: zod_1.z.literal('text'),
|
|
129
|
+
text: zod_1.z.string(),
|
|
130
|
+
});
|
|
131
|
+
exports.ImageContentSchema = zod_1.z.object({
|
|
132
|
+
type: zod_1.z.literal('image'),
|
|
133
|
+
image: zod_1.z.string(),
|
|
134
|
+
mimeType: zod_1.z.string().optional(),
|
|
135
|
+
});
|
|
136
|
+
exports.FileContentSchema = zod_1.z.object({
|
|
137
|
+
type: zod_1.z.literal('file'),
|
|
138
|
+
file: zod_1.z.string(),
|
|
139
|
+
mimeType: zod_1.z.string(),
|
|
140
|
+
});
|
|
141
|
+
exports.ContentPartSchema = zod_1.z.union([
|
|
142
|
+
exports.TextContentSchema,
|
|
143
|
+
exports.ImageContentSchema,
|
|
144
|
+
exports.FileContentSchema,
|
|
145
|
+
]);
|
|
146
|
+
exports.MessageSchema = zod_1.z.object({
|
|
147
|
+
role: exports.MessageRoleEnum,
|
|
148
|
+
content: zod_1.z.union([zod_1.z.string(), zod_1.z.array(exports.ContentPartSchema)]),
|
|
149
|
+
name: zod_1.z.string().optional(),
|
|
150
|
+
});
|
|
151
|
+
exports.ConversationSchema = zod_1.z.object({
|
|
152
|
+
uuid: zod_1.z.string(),
|
|
153
|
+
messages: zod_1.z.array(exports.MessageSchema).optional(),
|
|
154
|
+
createdAt: zod_1.z.string().optional(),
|
|
155
|
+
updatedAt: zod_1.z.string().optional(),
|
|
156
|
+
});
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Chat Types
|
|
159
|
+
// ============================================================================
|
|
160
|
+
exports.ChatRequestSchema = zod_1.z.object({
|
|
161
|
+
messages: zod_1.z.array(exports.MessageSchema),
|
|
162
|
+
stream: zod_1.z.boolean().default(false),
|
|
163
|
+
});
|
|
164
|
+
// ============================================================================
|
|
165
|
+
// Log Types
|
|
166
|
+
// ============================================================================
|
|
167
|
+
exports.CreateLogRequestSchema = zod_1.z.object({
|
|
168
|
+
path: zod_1.z.string(),
|
|
169
|
+
messages: zod_1.z.array(exports.MessageSchema),
|
|
170
|
+
});
|
|
171
|
+
// ============================================================================
|
|
172
|
+
// Streaming Event Types
|
|
173
|
+
// ============================================================================
|
|
174
|
+
exports.StreamEventTypeEnum = zod_1.z.enum([
|
|
175
|
+
'text-delta',
|
|
176
|
+
'step-complete',
|
|
177
|
+
'provider-event',
|
|
178
|
+
'tool-call-started',
|
|
179
|
+
'tool-call',
|
|
180
|
+
'chain-complete',
|
|
181
|
+
'chain-error',
|
|
182
|
+
]);
|
|
183
|
+
exports.StreamEventSchema = zod_1.z.object({
|
|
184
|
+
event: exports.StreamEventTypeEnum,
|
|
185
|
+
data: zod_1.z.any(),
|
|
186
|
+
});
|
|
187
|
+
// ============================================================================
|
|
188
|
+
// Error Types
|
|
189
|
+
// ============================================================================
|
|
190
|
+
exports.LatitudeErrorSchema = zod_1.z.object({
|
|
191
|
+
name: zod_1.z.string(),
|
|
192
|
+
errorCode: zod_1.z.string(),
|
|
193
|
+
message: zod_1.z.string(),
|
|
194
|
+
details: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
195
|
+
});
|
|
196
|
+
// ============================================================================
|
|
197
|
+
// Tool Input Schemas (for MCP tools)
|
|
198
|
+
// ============================================================================
|
|
199
|
+
// Project tools
|
|
200
|
+
exports.ListProjectsInputSchema = zod_1.z.object({});
|
|
201
|
+
exports.CreateProjectInputSchema = zod_1.z.object({
|
|
202
|
+
name: zod_1.z.string().describe('Project name'),
|
|
203
|
+
});
|
|
204
|
+
// Version tools
|
|
205
|
+
exports.ListVersionsInputSchema = zod_1.z.object({
|
|
206
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
207
|
+
});
|
|
208
|
+
exports.GetVersionInputSchema = zod_1.z.object({
|
|
209
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
210
|
+
versionUuid: zod_1.z.string().describe('Version UUID'),
|
|
211
|
+
});
|
|
212
|
+
exports.CreateVersionInputSchema = zod_1.z.object({
|
|
213
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
214
|
+
name: zod_1.z.string().describe('Version/commit name'),
|
|
215
|
+
});
|
|
216
|
+
exports.PublishVersionInputSchema = zod_1.z.object({
|
|
217
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
218
|
+
versionUuid: zod_1.z.string().describe('Version UUID to publish'),
|
|
219
|
+
title: zod_1.z.string().optional().describe('Publication title'),
|
|
220
|
+
description: zod_1.z.string().optional().describe('Publication description'),
|
|
221
|
+
});
|
|
222
|
+
// Document/Prompt tools
|
|
223
|
+
exports.ListPromptsInputSchema = zod_1.z.object({
|
|
224
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
225
|
+
versionUuid: zod_1.z
|
|
226
|
+
.string()
|
|
227
|
+
.default('live')
|
|
228
|
+
.describe("Version UUID or 'live' for published version"),
|
|
229
|
+
});
|
|
230
|
+
exports.GetPromptInputSchema = zod_1.z.object({
|
|
231
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
232
|
+
versionUuid: zod_1.z.string().default('live').describe("Version UUID or 'live'"),
|
|
233
|
+
path: zod_1.z
|
|
234
|
+
.string()
|
|
235
|
+
.describe("Prompt path (e.g., '/my-prompt' or 'folder/prompt')"),
|
|
236
|
+
});
|
|
237
|
+
exports.PushPromptInputSchema = zod_1.z.object({
|
|
238
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
239
|
+
versionUuid: zod_1.z
|
|
240
|
+
.string()
|
|
241
|
+
.describe("Target version UUID (must be draft, not 'live')"),
|
|
242
|
+
path: zod_1.z.string().describe('Prompt path'),
|
|
243
|
+
content: zod_1.z.string().describe('Prompt content in PromptL format'),
|
|
244
|
+
force: zod_1.z.boolean().default(false).describe('Force overwrite if exists'),
|
|
245
|
+
});
|
|
246
|
+
exports.PushPromptFromFileInputSchema = zod_1.z.object({
|
|
247
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
248
|
+
versionUuid: zod_1.z
|
|
249
|
+
.string()
|
|
250
|
+
.describe("Target version UUID (must be draft, not 'live')"),
|
|
251
|
+
filePath: zod_1.z
|
|
252
|
+
.string()
|
|
253
|
+
.describe('Absolute path to the prompt file (e.g., /path/to/my-prompt.md)'),
|
|
254
|
+
promptPath: zod_1.z
|
|
255
|
+
.string()
|
|
256
|
+
.optional()
|
|
257
|
+
.describe("Optional: Prompt path in Latitude. If omitted, derived from filename (e.g., 'my-prompt.md' → 'my-prompt')"),
|
|
258
|
+
force: zod_1.z.boolean().default(false).describe('Force overwrite if exists'),
|
|
259
|
+
});
|
|
260
|
+
exports.RunPromptInputSchema = zod_1.z.object({
|
|
261
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
262
|
+
versionUuid: zod_1.z.string().default('live').describe("Version UUID or 'live'"),
|
|
263
|
+
path: zod_1.z.string().describe('Prompt path to run'),
|
|
264
|
+
parameters: zod_1.z
|
|
265
|
+
.record(zod_1.z.string(), zod_1.z.unknown())
|
|
266
|
+
.optional()
|
|
267
|
+
.describe('Prompt parameters as key-value pairs'),
|
|
268
|
+
stream: zod_1.z.boolean().default(false).describe('Enable streaming response'),
|
|
269
|
+
tools: zod_1.z.array(zod_1.z.string()).optional().describe('Tool names to enable'),
|
|
270
|
+
userMessage: zod_1.z.string().optional().describe('Additional user message'),
|
|
271
|
+
});
|
|
272
|
+
exports.PushChangesInputSchema = zod_1.z.object({
|
|
273
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
274
|
+
versionUuid: zod_1.z.string().describe('Target version UUID'),
|
|
275
|
+
changes: zod_1.z
|
|
276
|
+
.array(zod_1.z.object({
|
|
277
|
+
path: zod_1.z.string().describe('Document path'),
|
|
278
|
+
content: zod_1.z.string().describe('Document content'),
|
|
279
|
+
status: zod_1.z
|
|
280
|
+
.enum(['added', 'modified', 'deleted'])
|
|
281
|
+
.default('modified')
|
|
282
|
+
.describe('Change status'),
|
|
283
|
+
}))
|
|
284
|
+
.describe('Array of document changes'),
|
|
285
|
+
});
|
|
286
|
+
// Conversation tools
|
|
287
|
+
exports.ChatInputSchema = zod_1.z.object({
|
|
288
|
+
conversationUuid: zod_1.z
|
|
289
|
+
.string()
|
|
290
|
+
.describe('Conversation UUID from previous run'),
|
|
291
|
+
message: zod_1.z.string().describe('User message to send'),
|
|
292
|
+
stream: zod_1.z.boolean().default(false).describe('Enable streaming response'),
|
|
293
|
+
});
|
|
294
|
+
exports.GetConversationInputSchema = zod_1.z.object({
|
|
295
|
+
conversationUuid: zod_1.z.string().describe('Conversation UUID'),
|
|
296
|
+
});
|
|
297
|
+
exports.StopConversationInputSchema = zod_1.z.object({
|
|
298
|
+
conversationUuid: zod_1.z.string().describe('Conversation UUID to stop'),
|
|
299
|
+
});
|
|
300
|
+
// Log tools
|
|
301
|
+
exports.CreateLogInputSchema = zod_1.z.object({
|
|
302
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
303
|
+
versionUuid: zod_1.z.string().describe('Version UUID'),
|
|
304
|
+
path: zod_1.z.string().describe('Prompt path'),
|
|
305
|
+
messages: zod_1.z
|
|
306
|
+
.array(zod_1.z.object({
|
|
307
|
+
role: zod_1.z.enum(['system', 'user', 'assistant']),
|
|
308
|
+
content: zod_1.z.string(),
|
|
309
|
+
}))
|
|
310
|
+
.describe('Conversation messages to log'),
|
|
311
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for testing CLI commands with real execution
|
|
3
|
+
*/
|
|
4
|
+
export declare class CliTestUtil {
|
|
5
|
+
/**
|
|
6
|
+
* Executes a CLI command and returns the result
|
|
7
|
+
*
|
|
8
|
+
* @param args - CLI arguments to pass to the command
|
|
9
|
+
* @param options - Test options
|
|
10
|
+
* @returns Promise with stdout, stderr, and exit code
|
|
11
|
+
*/
|
|
12
|
+
static runCommand(args: string[], options?: {
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
env?: Record<string, string>;
|
|
15
|
+
}): Promise<{
|
|
16
|
+
stdout: string;
|
|
17
|
+
stderr: string;
|
|
18
|
+
exitCode: number;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* Validates that stdout contains expected strings/patterns
|
|
22
|
+
*/
|
|
23
|
+
static validateOutputContains(output: string, expectedPatterns: (string | RegExp)[]): void;
|
|
24
|
+
/**
|
|
25
|
+
* Validates TOON output format
|
|
26
|
+
* TOON uses "key: value" syntax for objects
|
|
27
|
+
*/
|
|
28
|
+
static validateToonOutput(output: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Validates Markdown output format
|
|
31
|
+
* @deprecated Use validateToonOutput for new code - output is now TOON by default
|
|
32
|
+
*/
|
|
33
|
+
static validateMarkdownOutput(output: string): void;
|
|
34
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CliTestUtil = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
/**
|
|
7
|
+
* Utility for testing CLI commands with real execution
|
|
8
|
+
*/
|
|
9
|
+
class CliTestUtil {
|
|
10
|
+
/**
|
|
11
|
+
* Executes a CLI command and returns the result
|
|
12
|
+
*
|
|
13
|
+
* @param args - CLI arguments to pass to the command
|
|
14
|
+
* @param options - Test options
|
|
15
|
+
* @returns Promise with stdout, stderr, and exit code
|
|
16
|
+
*/
|
|
17
|
+
static async runCommand(args, options = {}) {
|
|
18
|
+
// Default timeout of 30 seconds
|
|
19
|
+
const timeoutMs = options.timeoutMs || 30000;
|
|
20
|
+
// CLI execution path - points to the built CLI script
|
|
21
|
+
const cliPath = (0, path_1.join)(process.cwd(), 'dist', 'index.js');
|
|
22
|
+
// Log what command we're about to run
|
|
23
|
+
console.log(`Running CLI command: node ${cliPath} ${args.join(' ')}`);
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
// Set up timeout handler
|
|
26
|
+
const timeoutId = setTimeout(() => {
|
|
27
|
+
child.kill();
|
|
28
|
+
reject(new Error(`CLI command timed out after ${timeoutMs}ms`));
|
|
29
|
+
}, timeoutMs);
|
|
30
|
+
// Capture stdout and stderr
|
|
31
|
+
let stdout = '';
|
|
32
|
+
let stderr = '';
|
|
33
|
+
// Spawn the process with given arguments and enhanced environment
|
|
34
|
+
const child = (0, child_process_1.spawn)('node', [cliPath, ...args], {
|
|
35
|
+
env: {
|
|
36
|
+
...process.env,
|
|
37
|
+
...options.env,
|
|
38
|
+
DEBUG: 'true', // Enable debug logging
|
|
39
|
+
NODE_ENV: 'test', // Ensure tests are detected
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
// Collect stdout data
|
|
43
|
+
child.stdout.on('data', (data) => {
|
|
44
|
+
const chunk = data.toString();
|
|
45
|
+
stdout += chunk;
|
|
46
|
+
console.log(`STDOUT chunk: ${chunk.substring(0, 50)}...`);
|
|
47
|
+
});
|
|
48
|
+
// Collect stderr data
|
|
49
|
+
child.stderr.on('data', (data) => {
|
|
50
|
+
const chunk = data.toString();
|
|
51
|
+
stderr += chunk;
|
|
52
|
+
console.log(`STDERR chunk: ${chunk.substring(0, 50)}...`);
|
|
53
|
+
});
|
|
54
|
+
// Handle process completion
|
|
55
|
+
child.on('close', (exitCode) => {
|
|
56
|
+
clearTimeout(timeoutId);
|
|
57
|
+
console.log(`Command completed with exit code: ${exitCode}`);
|
|
58
|
+
console.log(`Total STDOUT length: ${stdout.length} chars`);
|
|
59
|
+
// Get the non-debug output for debugging purposes
|
|
60
|
+
const nonDebugOutput = stdout
|
|
61
|
+
.split('\n')
|
|
62
|
+
.filter((line) => !line.match(/^\[\d{2}:\d{2}:\d{2}\]/))
|
|
63
|
+
.join('\n');
|
|
64
|
+
console.log(`Non-debug output length: ${nonDebugOutput.length} chars`);
|
|
65
|
+
console.log(`STDOUT excerpt: ${stdout.substring(0, 100)}...`);
|
|
66
|
+
console.log(`Filtered excerpt: ${nonDebugOutput.substring(0, 100)}...`);
|
|
67
|
+
resolve({
|
|
68
|
+
stdout,
|
|
69
|
+
stderr,
|
|
70
|
+
exitCode: exitCode ?? 0,
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
// Handle process errors
|
|
74
|
+
child.on('error', (err) => {
|
|
75
|
+
clearTimeout(timeoutId);
|
|
76
|
+
console.error(`Command error: ${err.message}`);
|
|
77
|
+
reject(err);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Validates that stdout contains expected strings/patterns
|
|
83
|
+
*/
|
|
84
|
+
static validateOutputContains(output, expectedPatterns) {
|
|
85
|
+
// Filter out debug log lines for cleaner validation
|
|
86
|
+
const cleanOutput = output
|
|
87
|
+
.split('\n')
|
|
88
|
+
.filter((line) => !line.match(/^\[\d{2}:\d{2}:\d{2}\]/))
|
|
89
|
+
.join('\n');
|
|
90
|
+
console.log('==== Cleaned output for validation ====');
|
|
91
|
+
console.log(cleanOutput);
|
|
92
|
+
console.log('=======================================');
|
|
93
|
+
for (const pattern of expectedPatterns) {
|
|
94
|
+
if (typeof pattern === 'string') {
|
|
95
|
+
expect(cleanOutput).toContain(pattern);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
expect(cleanOutput).toMatch(pattern);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Validates TOON output format
|
|
104
|
+
* TOON uses "key: value" syntax for objects
|
|
105
|
+
*/
|
|
106
|
+
static validateToonOutput(output) {
|
|
107
|
+
// Filter out debug log lines for cleaner validation
|
|
108
|
+
const cleanOutput = output
|
|
109
|
+
.split('\n')
|
|
110
|
+
.filter((line) => !line.match(/^\[\d{2}:\d{2}:\d{2}\]/))
|
|
111
|
+
.join('\n');
|
|
112
|
+
// TOON format characteristics:
|
|
113
|
+
// - Key-value pairs in "key: value" format
|
|
114
|
+
// - No curly braces for objects
|
|
115
|
+
// - Arrays use [count]{fields}: notation
|
|
116
|
+
const toonPatterns = [
|
|
117
|
+
/^\w+:\s*.+/m, // key: value pattern
|
|
118
|
+
];
|
|
119
|
+
expect(toonPatterns.some((pattern) => pattern.test(cleanOutput))).toBe(true);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Validates Markdown output format
|
|
123
|
+
* @deprecated Use validateToonOutput for new code - output is now TOON by default
|
|
124
|
+
*/
|
|
125
|
+
static validateMarkdownOutput(output) {
|
|
126
|
+
// Filter out debug log lines for cleaner validation
|
|
127
|
+
const cleanOutput = output
|
|
128
|
+
.split('\n')
|
|
129
|
+
.filter((line) => !line.match(/^\[\d{2}:\d{2}:\d{2}\]/))
|
|
130
|
+
.join('\n');
|
|
131
|
+
// Check for Markdown heading
|
|
132
|
+
expect(cleanOutput).toMatch(/^#\s.+/m);
|
|
133
|
+
// Check for markdown formatting elements like bold text, lists, etc.
|
|
134
|
+
const markdownElements = [
|
|
135
|
+
/\*\*.+\*\*/, // Bold text
|
|
136
|
+
/-\s.+/, // List items
|
|
137
|
+
/\|.+\|.+\|/, // Table rows
|
|
138
|
+
/\[.+\]\(.+\)/, // Links
|
|
139
|
+
];
|
|
140
|
+
expect(markdownElements.some((pattern) => pattern.test(cleanOutput))).toBe(true);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.CliTestUtil = CliTestUtil;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader that handles multiple sources with priority:
|
|
3
|
+
* 1. Direct ENV pass (process.env)
|
|
4
|
+
* 2. .env file in project root
|
|
5
|
+
* 3. Global config file at $HOME/.mcp/configs.json
|
|
6
|
+
*/
|
|
7
|
+
declare class ConfigLoader {
|
|
8
|
+
private packageName;
|
|
9
|
+
private configLoaded;
|
|
10
|
+
/**
|
|
11
|
+
* Create a new ConfigLoader instance
|
|
12
|
+
* @param packageName The package name to use for global config lookup
|
|
13
|
+
*/
|
|
14
|
+
constructor(packageName: string);
|
|
15
|
+
/**
|
|
16
|
+
* Load configuration from all sources with proper priority
|
|
17
|
+
*/
|
|
18
|
+
load(): void;
|
|
19
|
+
/**
|
|
20
|
+
* Load configuration from .env file in project root
|
|
21
|
+
*/
|
|
22
|
+
private loadFromEnvFile;
|
|
23
|
+
/**
|
|
24
|
+
* Load configuration from global config file at $HOME/.mcp/configs.json
|
|
25
|
+
*/
|
|
26
|
+
private loadFromGlobalConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Get a configuration value
|
|
29
|
+
* @param key The configuration key
|
|
30
|
+
* @param defaultValue The default value if the key is not found
|
|
31
|
+
* @returns The configuration value or the default value
|
|
32
|
+
*/
|
|
33
|
+
get(key: string, defaultValue?: string): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Get a boolean configuration value
|
|
36
|
+
* @param key The configuration key
|
|
37
|
+
* @param defaultValue The default value if the key is not found
|
|
38
|
+
* @returns The boolean configuration value or the default value
|
|
39
|
+
*/
|
|
40
|
+
getBoolean(key: string, defaultValue?: boolean): boolean;
|
|
41
|
+
}
|
|
42
|
+
export declare const config: ConfigLoader;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.config = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const logger_util_js_1 = require("./logger.util.js");
|
|
10
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
/**
|
|
13
|
+
* Configuration loader that handles multiple sources with priority:
|
|
14
|
+
* 1. Direct ENV pass (process.env)
|
|
15
|
+
* 2. .env file in project root
|
|
16
|
+
* 3. Global config file at $HOME/.mcp/configs.json
|
|
17
|
+
*/
|
|
18
|
+
class ConfigLoader {
|
|
19
|
+
/**
|
|
20
|
+
* Create a new ConfigLoader instance
|
|
21
|
+
* @param packageName The package name to use for global config lookup
|
|
22
|
+
*/
|
|
23
|
+
constructor(packageName) {
|
|
24
|
+
this.configLoaded = false;
|
|
25
|
+
this.packageName = packageName;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Load configuration from all sources with proper priority
|
|
29
|
+
*/
|
|
30
|
+
load() {
|
|
31
|
+
const methodLogger = logger_util_js_1.Logger.forContext('utils/config.util.ts', 'load');
|
|
32
|
+
if (this.configLoaded) {
|
|
33
|
+
methodLogger.debug('Configuration already loaded, skipping');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
methodLogger.debug('Loading configuration...');
|
|
37
|
+
// Priority 3: Load from global config file
|
|
38
|
+
this.loadFromGlobalConfig();
|
|
39
|
+
// Priority 2: Load from .env file
|
|
40
|
+
this.loadFromEnvFile();
|
|
41
|
+
// Priority 1: Direct ENV pass is already in process.env
|
|
42
|
+
// No need to do anything as it already has highest priority
|
|
43
|
+
this.configLoaded = true;
|
|
44
|
+
methodLogger.debug('Configuration loaded successfully');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Load configuration from .env file in project root
|
|
48
|
+
*/
|
|
49
|
+
loadFromEnvFile() {
|
|
50
|
+
const methodLogger = logger_util_js_1.Logger.forContext('utils/config.util.ts', 'loadFromEnvFile');
|
|
51
|
+
try {
|
|
52
|
+
const result = dotenv_1.default.config({
|
|
53
|
+
// Suppress dotenv output in test environment
|
|
54
|
+
debug: process.env.NODE_ENV !== 'test' &&
|
|
55
|
+
process.env.DEBUG === 'true',
|
|
56
|
+
// Suppress promotional messages in test environment (dotenv v17+)
|
|
57
|
+
quiet: process.env.NODE_ENV === 'test',
|
|
58
|
+
});
|
|
59
|
+
if (result.error) {
|
|
60
|
+
methodLogger.debug('No .env file found or error reading it');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
methodLogger.debug('Loaded configuration from .env file');
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
methodLogger.error('Error loading .env file', error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Load configuration from global config file at $HOME/.mcp/configs.json
|
|
71
|
+
*/
|
|
72
|
+
loadFromGlobalConfig() {
|
|
73
|
+
const methodLogger = logger_util_js_1.Logger.forContext('utils/config.util.ts', 'loadFromGlobalConfig');
|
|
74
|
+
try {
|
|
75
|
+
const homedir = os_1.default.homedir();
|
|
76
|
+
const globalConfigPath = path_1.default.join(homedir, '.mcp', 'configs.json');
|
|
77
|
+
if (!fs_1.default.existsSync(globalConfigPath)) {
|
|
78
|
+
methodLogger.debug('Global config file not found');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const configContent = fs_1.default.readFileSync(globalConfigPath, 'utf8');
|
|
82
|
+
const config = JSON.parse(configContent);
|
|
83
|
+
// Determine the potential keys for the current package
|
|
84
|
+
const shortKey = 'latitude'; // Project-specific short key
|
|
85
|
+
const fullPackageName = this.packageName; // e.g., '@anthropic/latitude-mcp-server'
|
|
86
|
+
const unscopedPackageName = fullPackageName.split('/')[1] || fullPackageName; // e.g., 'latitude-mcp-server'
|
|
87
|
+
const potentialKeys = [
|
|
88
|
+
shortKey,
|
|
89
|
+
fullPackageName,
|
|
90
|
+
unscopedPackageName,
|
|
91
|
+
];
|
|
92
|
+
let foundConfigSection = null;
|
|
93
|
+
let usedKey = null;
|
|
94
|
+
for (const key of potentialKeys) {
|
|
95
|
+
if (config[key] &&
|
|
96
|
+
typeof config[key] === 'object' &&
|
|
97
|
+
config[key].environments) {
|
|
98
|
+
foundConfigSection = config[key];
|
|
99
|
+
usedKey = key;
|
|
100
|
+
methodLogger.debug(`Found configuration using key: ${key}`);
|
|
101
|
+
break; // Stop once found
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (!foundConfigSection || !foundConfigSection.environments) {
|
|
105
|
+
methodLogger.debug(`No configuration found for ${this.packageName} using keys: ${potentialKeys.join(', ')}`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const environments = foundConfigSection.environments;
|
|
109
|
+
for (const [key, value] of Object.entries(environments)) {
|
|
110
|
+
// Only set if not already defined in process.env
|
|
111
|
+
if (process.env[key] === undefined) {
|
|
112
|
+
process.env[key] = String(value);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
methodLogger.debug(`Loaded configuration from global config file using key: ${usedKey}`);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
methodLogger.error('Error loading global config file', error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get a configuration value
|
|
123
|
+
* @param key The configuration key
|
|
124
|
+
* @param defaultValue The default value if the key is not found
|
|
125
|
+
* @returns The configuration value or the default value
|
|
126
|
+
*/
|
|
127
|
+
get(key, defaultValue) {
|
|
128
|
+
return process.env[key] || defaultValue;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get a boolean configuration value
|
|
132
|
+
* @param key The configuration key
|
|
133
|
+
* @param defaultValue The default value if the key is not found
|
|
134
|
+
* @returns The boolean configuration value or the default value
|
|
135
|
+
*/
|
|
136
|
+
getBoolean(key, defaultValue = false) {
|
|
137
|
+
const value = this.get(key);
|
|
138
|
+
if (value === undefined) {
|
|
139
|
+
return defaultValue;
|
|
140
|
+
}
|
|
141
|
+
return value.toLowerCase() === 'true';
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Create and export a singleton instance with the package name from package.json
|
|
145
|
+
exports.config = new ConfigLoader('@anthropic/latitude-mcp-server');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|