@contextvm/ctxcn 1.0.1
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 +21 -0
- package/README.md +178 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/src/__mocks__/mock-server.d.ts +2 -0
- package/dist/src/__mocks__/mock-server.d.ts.map +1 -0
- package/dist/src/__mocks__/mock-server.js +24 -0
- package/dist/src/__mocks__/mock-server.js.map +1 -0
- package/dist/src/commands/add.d.ts +2 -0
- package/dist/src/commands/add.d.ts.map +1 -0
- package/dist/src/commands/add.js +106 -0
- package/dist/src/commands/add.js.map +1 -0
- package/dist/src/commands/help.d.ts +2 -0
- package/dist/src/commands/help.d.ts.map +1 -0
- package/dist/src/commands/help.js +15 -0
- package/dist/src/commands/help.js.map +1 -0
- package/dist/src/commands/init.d.ts +2 -0
- package/dist/src/commands/init.d.ts.map +1 -0
- package/dist/src/commands/init.js +61 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/update.d.ts +2 -0
- package/dist/src/commands/update.d.ts.map +1 -0
- package/dist/src/commands/update.js +145 -0
- package/dist/src/commands/update.js.map +1 -0
- package/dist/src/config.d.ts +10 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +31 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/ctxcn/DemoServerClient.d.ts +38 -0
- package/dist/src/ctxcn/DemoServerClient.d.ts.map +1 -0
- package/dist/src/ctxcn/DemoServerClient.js +53 -0
- package/dist/src/ctxcn/DemoServerClient.js.map +1 -0
- package/dist/src/ctxcn/ExampleServersEverythingClient.d.ts +149 -0
- package/dist/src/ctxcn/ExampleServersEverythingClient.d.ts.map +1 -0
- package/dist/src/ctxcn/ExampleServersEverythingClient.js +118 -0
- package/dist/src/ctxcn/ExampleServersEverythingClient.js.map +1 -0
- package/dist/src/ctxcn/TestClient.d.ts +149 -0
- package/dist/src/ctxcn/TestClient.d.ts.map +1 -0
- package/dist/src/ctxcn/TestClient.js +118 -0
- package/dist/src/ctxcn/TestClient.js.map +1 -0
- package/dist/src/utils/cli.d.ts +7 -0
- package/dist/src/utils/cli.d.ts.map +1 -0
- package/dist/src/utils/cli.js +77 -0
- package/dist/src/utils/cli.js.map +1 -0
- package/dist/src/utils/schema.d.ts +11 -0
- package/dist/src/utils/schema.d.ts.map +1 -0
- package/dist/src/utils/schema.js +333 -0
- package/dist/src/utils/schema.js.map +1 -0
- package/dist/src/utils.d.ts +2 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +12 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { compile } from "json-schema-to-typescript";
|
|
2
|
+
import { toPascalCase } from "../utils";
|
|
3
|
+
export function sanitizeSchema(schema) {
|
|
4
|
+
// A valid JSON Schema is a boolean or an object.
|
|
5
|
+
if (typeof schema === "boolean") {
|
|
6
|
+
return schema;
|
|
7
|
+
}
|
|
8
|
+
// If it's not an object (e.g., string, null, number, array), it's invalid. Return empty schema.
|
|
9
|
+
if (typeof schema !== "object" || schema === null || Array.isArray(schema)) {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
// It's an object, so we'll traverse it to remove invalid $refs.
|
|
13
|
+
function traverse(obj) {
|
|
14
|
+
if (typeof obj !== "object" || obj === null) {
|
|
15
|
+
return obj;
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(obj)) {
|
|
18
|
+
return obj.map(traverse);
|
|
19
|
+
}
|
|
20
|
+
const newObj = {};
|
|
21
|
+
for (const key in obj) {
|
|
22
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
23
|
+
if (key === "$ref") {
|
|
24
|
+
const refValue = obj[key];
|
|
25
|
+
// Only keep internal references.
|
|
26
|
+
if (typeof refValue === "string" && refValue.startsWith("#")) {
|
|
27
|
+
newObj[key] = refValue;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
newObj[key] = traverse(obj[key]);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return newObj;
|
|
36
|
+
}
|
|
37
|
+
return traverse(schema);
|
|
38
|
+
}
|
|
39
|
+
export function extractInlineType(typeDefinition) {
|
|
40
|
+
// Extract the actual type definition from a generated type
|
|
41
|
+
// Handle both interface and type definitions
|
|
42
|
+
const interfaceMatch = typeDefinition.match(/export interface \w+ ([\s\S]*?)(?=export|\s*$)/);
|
|
43
|
+
if (interfaceMatch && interfaceMatch[1]) {
|
|
44
|
+
return formatInlineType(interfaceMatch[1]);
|
|
45
|
+
}
|
|
46
|
+
const typeMatch = typeDefinition.match(/export type \w+ = ([\s\S]*?);/);
|
|
47
|
+
if (typeMatch && typeMatch[1]) {
|
|
48
|
+
const shape = typeMatch[1].trim();
|
|
49
|
+
return shape === "unknown" ? "{}" : formatInlineType(shape);
|
|
50
|
+
}
|
|
51
|
+
return "{}";
|
|
52
|
+
}
|
|
53
|
+
function formatInlineType(typeShape) {
|
|
54
|
+
// If it's an object type, clean up the formatting
|
|
55
|
+
if (typeShape.startsWith("{") && typeShape.endsWith("}")) {
|
|
56
|
+
const objectContent = typeShape.slice(1, -1).trim();
|
|
57
|
+
if (objectContent) {
|
|
58
|
+
const properties = objectContent
|
|
59
|
+
.split("\n")
|
|
60
|
+
.map((line) => line.trim())
|
|
61
|
+
.filter((line) => line)
|
|
62
|
+
.map((line) => ` ${line}`)
|
|
63
|
+
.join("\n");
|
|
64
|
+
return `{\n${properties}\n }`;
|
|
65
|
+
}
|
|
66
|
+
return "{}";
|
|
67
|
+
}
|
|
68
|
+
return typeShape;
|
|
69
|
+
}
|
|
70
|
+
export function extractSchemaProperties(schema) {
|
|
71
|
+
if (!schema?.properties || typeof schema !== "object") {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
75
|
+
return Object.entries(schema.properties)
|
|
76
|
+
.filter(([_, propSchema]) => propSchema && typeof propSchema === "object")
|
|
77
|
+
.map(([propName, propSchema]) => {
|
|
78
|
+
const propType = propSchema.type || "unknown";
|
|
79
|
+
const isRequired = required.includes(propName);
|
|
80
|
+
return {
|
|
81
|
+
name: propName,
|
|
82
|
+
type: mapJsonTypeToTypeScript(propType),
|
|
83
|
+
required: isRequired,
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function mapJsonTypeToTypeScript(jsonType) {
|
|
88
|
+
const typeMap = {
|
|
89
|
+
string: "string",
|
|
90
|
+
number: "number",
|
|
91
|
+
integer: "number",
|
|
92
|
+
boolean: "boolean",
|
|
93
|
+
array: "any[]",
|
|
94
|
+
object: "object",
|
|
95
|
+
};
|
|
96
|
+
return typeMap[jsonType] || "any";
|
|
97
|
+
}
|
|
98
|
+
export async function generateTypeFromSchema(schema, typeName) {
|
|
99
|
+
if (typeof schema === "boolean") {
|
|
100
|
+
return `export type ${typeName} = ${schema ? "any" : "never"};`;
|
|
101
|
+
}
|
|
102
|
+
const sanitizedSchema = sanitizeSchema(schema);
|
|
103
|
+
if (typeof sanitizedSchema === "boolean") {
|
|
104
|
+
return `export type ${typeName} = ${sanitizedSchema ? "any" : "never"};`;
|
|
105
|
+
}
|
|
106
|
+
return await compile(sanitizedSchema, typeName, {
|
|
107
|
+
bannerComment: "",
|
|
108
|
+
additionalProperties: false,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
export async function generateClientCode(pubkey, serverDetails, toolListResult, serverName) {
|
|
112
|
+
const classMethods = [];
|
|
113
|
+
const serverTypeMethods = [];
|
|
114
|
+
for (const tool of toolListResult.tools) {
|
|
115
|
+
const { methodDefinitions, serverMethod } = await generateToolMethods(tool, serverName);
|
|
116
|
+
classMethods.push(methodDefinitions.classMethod);
|
|
117
|
+
serverTypeMethods.push(serverMethod);
|
|
118
|
+
}
|
|
119
|
+
const clientName = `${serverName}Client`;
|
|
120
|
+
const serverType = generateServerType(serverName, serverTypeMethods);
|
|
121
|
+
const genericCallMethod = generateGenericCallMethod();
|
|
122
|
+
return assembleClientCode(clientName, pubkey, serverType, genericCallMethod, classMethods, serverName);
|
|
123
|
+
}
|
|
124
|
+
async function generateToolMethods(tool, serverName) {
|
|
125
|
+
const toolName = tool.name;
|
|
126
|
+
const capitalizedToolName = toPascalCase(tool.name);
|
|
127
|
+
const inputTypeName = `${capitalizedToolName}Input`;
|
|
128
|
+
const outputTypeName = `${capitalizedToolName}Output`;
|
|
129
|
+
// Generate types temporarily to extract inline definitions
|
|
130
|
+
const inputType = await generateTypeFromSchema(tool.inputSchema, inputTypeName);
|
|
131
|
+
const outputType = await generateTypeFromSchema(tool.outputSchema, outputTypeName);
|
|
132
|
+
// Extract inline type definitions for better IDE inference
|
|
133
|
+
const inputInlineType = extractInlineType(inputType);
|
|
134
|
+
const outputInlineType = extractInlineType(outputType);
|
|
135
|
+
// Extract properties from schema for individual parameters
|
|
136
|
+
const inputProperties = extractSchemaProperties(tool.inputSchema);
|
|
137
|
+
// Generate JSDoc comment with the actual type shape
|
|
138
|
+
const jsDocComment = generateJSDoc(tool, inputProperties, outputInlineType);
|
|
139
|
+
// Generate method with individual parameters for better developer experience
|
|
140
|
+
const methodDefinitions = inputProperties.length > 0
|
|
141
|
+
? generateMethodWithIndividualParams(toolName, inputProperties, outputInlineType, jsDocComment)
|
|
142
|
+
: generateMethodWithObjectParam(toolName, inputInlineType, outputInlineType, jsDocComment);
|
|
143
|
+
// Add corresponding method signature to server type
|
|
144
|
+
const serverMethod = inputProperties.length > 0
|
|
145
|
+
? ` ${toolName}: (${methodDefinitions.parameters}) => Promise<${outputInlineType}>;`
|
|
146
|
+
: ` ${toolName}: (args: ${inputInlineType}) => Promise<${outputInlineType}>;`;
|
|
147
|
+
return { methodDefinitions, serverMethod };
|
|
148
|
+
}
|
|
149
|
+
function generateJSDoc(tool, inputProperties, outputInlineType) {
|
|
150
|
+
const lines = [];
|
|
151
|
+
// Add main description
|
|
152
|
+
if (tool.description) {
|
|
153
|
+
lines.push(` * ${tool.description}`);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
lines.push(` * ${tool.title || tool.name} tool`);
|
|
157
|
+
}
|
|
158
|
+
// Add parameter descriptions
|
|
159
|
+
if (inputProperties.length > 0) {
|
|
160
|
+
// Extract parameter descriptions from input schema if available
|
|
161
|
+
const inputSchema = tool.inputSchema;
|
|
162
|
+
if (inputSchema && inputSchema.properties) {
|
|
163
|
+
for (const prop of inputProperties) {
|
|
164
|
+
const propSchema = inputSchema.properties[prop.name];
|
|
165
|
+
let description = propSchema?.description || "";
|
|
166
|
+
// If no description is available, create a default one based on the parameter name
|
|
167
|
+
if (!description) {
|
|
168
|
+
description = generateParameterDescription(prop.name, tool.name);
|
|
169
|
+
}
|
|
170
|
+
const optional = prop.required ? "" : "[optional] ";
|
|
171
|
+
lines.push(` * @param {${prop.type}} ${prop.name} ${optional}${description}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Add return type description with the actual type shape
|
|
176
|
+
const outputSchema = tool.outputSchema;
|
|
177
|
+
const returnDescription = outputSchema?.description || `The result of the ${tool.name} operation`;
|
|
178
|
+
// Format the return type for JSDoc - use the actual inline type
|
|
179
|
+
if (outputInlineType.includes("\n")) {
|
|
180
|
+
// For multi-line types with JSDoc comments, we need to clean them up
|
|
181
|
+
// Remove inline JSDoc comments and just keep the type structure
|
|
182
|
+
const cleanedType = outputInlineType
|
|
183
|
+
.split("\n")
|
|
184
|
+
.map((line) => {
|
|
185
|
+
const trimmed = line.trim();
|
|
186
|
+
// Remove JSDoc comment lines
|
|
187
|
+
if (trimmed.startsWith("/**") ||
|
|
188
|
+
trimmed.startsWith("*") ||
|
|
189
|
+
trimmed.endsWith("*/")) {
|
|
190
|
+
return "";
|
|
191
|
+
}
|
|
192
|
+
return trimmed;
|
|
193
|
+
})
|
|
194
|
+
.filter((line) => line && !line.startsWith("*"))
|
|
195
|
+
.join(" ");
|
|
196
|
+
// If the cleaned type is too long or complex, use a generic type
|
|
197
|
+
if (cleanedType.length > 100) {
|
|
198
|
+
lines.push(` * @returns {Promise<object>} ${returnDescription}`);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
lines.push(` * @returns {Promise<${cleanedType}>} ${returnDescription}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
lines.push(` * @returns {Promise<${outputInlineType}>} ${returnDescription}`);
|
|
206
|
+
}
|
|
207
|
+
return ` /**\n${lines.join("\n")}\n */`;
|
|
208
|
+
}
|
|
209
|
+
function generateParameterDescription(paramName, toolName) {
|
|
210
|
+
// Generate a meaningful description based on the parameter name and tool context
|
|
211
|
+
const paramDescriptions = {
|
|
212
|
+
a: "The first number to add",
|
|
213
|
+
b: "The second number to add",
|
|
214
|
+
input: "The input value to be echoed",
|
|
215
|
+
text: "The text to process",
|
|
216
|
+
value: "The value to use",
|
|
217
|
+
data: "The data to process",
|
|
218
|
+
options: "Configuration options",
|
|
219
|
+
config: "Configuration settings",
|
|
220
|
+
url: "The URL to connect to",
|
|
221
|
+
path: "The file or directory path",
|
|
222
|
+
name: "The name identifier",
|
|
223
|
+
id: "The unique identifier",
|
|
224
|
+
};
|
|
225
|
+
// Check if we have a predefined description
|
|
226
|
+
if (paramDescriptions[paramName]) {
|
|
227
|
+
return paramDescriptions[paramName];
|
|
228
|
+
}
|
|
229
|
+
// Generate a generic description based on the parameter name
|
|
230
|
+
const capitalizedParam = paramName.charAt(0).toUpperCase() + paramName.slice(1);
|
|
231
|
+
return `The ${paramName.replace(/([A-Z])/g, " $1").toLowerCase()} parameter`;
|
|
232
|
+
}
|
|
233
|
+
function generateMethodWithIndividualParams(toolName, properties, outputType, jsDocComment) {
|
|
234
|
+
const parameters = properties
|
|
235
|
+
.map((prop) => `${prop.name}${prop.required ? "" : "?"}: ${prop.type}`)
|
|
236
|
+
.join(", ");
|
|
237
|
+
const argsObject = properties.map((prop) => prop.name).join(", ");
|
|
238
|
+
return {
|
|
239
|
+
parameters,
|
|
240
|
+
classMethod: ` ${jsDocComment}
|
|
241
|
+
async ${toolName}(
|
|
242
|
+
${parameters}
|
|
243
|
+
): Promise<${outputType}> {
|
|
244
|
+
return this.call("${toolName}", { ${argsObject} });
|
|
245
|
+
}`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function generateMethodWithObjectParam(toolName, inputType, outputType, jsDocComment) {
|
|
249
|
+
return {
|
|
250
|
+
parameters: `args: ${inputType}`,
|
|
251
|
+
classMethod: ` ${jsDocComment}
|
|
252
|
+
async ${toolName}(
|
|
253
|
+
args: ${inputType}
|
|
254
|
+
): Promise<${outputType}> {
|
|
255
|
+
return this.call("${toolName}", args);
|
|
256
|
+
}`,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function generateServerType(serverName, serverTypeMethods) {
|
|
260
|
+
return `export type ${serverName} = {
|
|
261
|
+
${serverTypeMethods.join("\n")}
|
|
262
|
+
};`;
|
|
263
|
+
}
|
|
264
|
+
function generateGenericCallMethod() {
|
|
265
|
+
return ` private async call<T = unknown>(
|
|
266
|
+
name: string,
|
|
267
|
+
args: Record<string, unknown>
|
|
268
|
+
): Promise<T> {
|
|
269
|
+
const result = await this.client.callTool({
|
|
270
|
+
name,
|
|
271
|
+
arguments: { ...args },
|
|
272
|
+
});
|
|
273
|
+
return result.structuredContent as T;
|
|
274
|
+
}`;
|
|
275
|
+
}
|
|
276
|
+
function assembleClientCode(clientName, pubkey, serverType, genericCallMethod, classMethods, serverName) {
|
|
277
|
+
return `import { Client } from "@modelcontextprotocol/sdk/client";
|
|
278
|
+
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
279
|
+
import {
|
|
280
|
+
NostrClientTransport,
|
|
281
|
+
type NostrTransportOptions,
|
|
282
|
+
PrivateKeySigner,
|
|
283
|
+
ApplesauceRelayPool,
|
|
284
|
+
} from "@contextvm/sdk";
|
|
285
|
+
|
|
286
|
+
${serverType}
|
|
287
|
+
|
|
288
|
+
export class ${clientName} implements ${serverName} {
|
|
289
|
+
static readonly SERVER_PUBKEY = "${pubkey}";
|
|
290
|
+
private client: Client;
|
|
291
|
+
private transport: Transport;
|
|
292
|
+
|
|
293
|
+
constructor(
|
|
294
|
+
options: Partial<NostrTransportOptions> & { privateKey?: string; relays?: string[] } = {}
|
|
295
|
+
) {
|
|
296
|
+
this.client = new Client({
|
|
297
|
+
name: "${clientName}",
|
|
298
|
+
version: "1.0.0",
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const {
|
|
302
|
+
privateKey,
|
|
303
|
+
relays = ["ws://localhost:10547"],
|
|
304
|
+
signer = new PrivateKeySigner(privateKey),
|
|
305
|
+
relayHandler = new ApplesauceRelayPool(relays),
|
|
306
|
+
...rest
|
|
307
|
+
} = options;
|
|
308
|
+
|
|
309
|
+
this.transport = new NostrClientTransport({
|
|
310
|
+
serverPubkey: ${clientName}.SERVER_PUBKEY,
|
|
311
|
+
signer,
|
|
312
|
+
relayHandler,
|
|
313
|
+
isStateless: true,
|
|
314
|
+
...rest,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Auto-connect in constructor
|
|
318
|
+
this.client.connect(this.transport).catch((error) => {
|
|
319
|
+
console.error(\`Failed to connect to server: \${error}\`);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async disconnect(): Promise<void> {
|
|
324
|
+
await this.transport.close();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
${genericCallMethod}
|
|
328
|
+
|
|
329
|
+
${classMethods.join("\n\n")}
|
|
330
|
+
}
|
|
331
|
+
`;
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/utils/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,KAAG,MAWxC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Helper function to convert a string to PascalCase
|
|
2
|
+
export const toPascalCase = (s) => {
|
|
3
|
+
if (!s) {
|
|
4
|
+
return "";
|
|
5
|
+
}
|
|
6
|
+
// First replace forward slashes with hyphens to handle names like 'example-servers/everything'
|
|
7
|
+
const normalized = s.replace(/\//g, "-");
|
|
8
|
+
// Convert to camelCase first, then capitalize the first letter.
|
|
9
|
+
const camelCased = normalized.replace(/([-_ ]\w)/g, (g) => g ? g.charAt(1).toUpperCase() : "");
|
|
10
|
+
return camelCased.charAt(0).toUpperCase() + camelCased.slice(1);
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAS,EAAU,EAAE;IAChD,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,+FAA+F;IAC/F,MAAM,UAAU,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACzC,gEAAgE;IAChE,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CACxD,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CACnC,CAAC;IACF,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@contextvm/ctxcn",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "A command-line utility inspired by shadcn that streamlines the integration of ContextVM (CVM) servers into your TypeScript projects",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"bin": {
|
|
10
|
+
"ctxcn": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "bun run tsc",
|
|
14
|
+
"dev": "bun run index.ts",
|
|
15
|
+
"format": "prettier --write .",
|
|
16
|
+
"prepublishOnly": "bun run build",
|
|
17
|
+
"changeset": "changeset",
|
|
18
|
+
"version-packages": "changeset version",
|
|
19
|
+
"release": "bun run build && changeset publish"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"contextvm",
|
|
23
|
+
"cvm",
|
|
24
|
+
"mcp",
|
|
25
|
+
"cli",
|
|
26
|
+
"typescript",
|
|
27
|
+
"code-generation"
|
|
28
|
+
],
|
|
29
|
+
"author": "",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@changesets/cli": "^2.29.7",
|
|
38
|
+
"@types/bun": "latest",
|
|
39
|
+
"@types/node": "^20.0.0",
|
|
40
|
+
"prettier": "^3.6.2",
|
|
41
|
+
"typescript": "^5.0.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"typescript": "^5"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@contextvm/sdk": "^0.1.29",
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.18.2",
|
|
49
|
+
"json-schema-to-typescript": "^15.0.4"
|
|
50
|
+
}
|
|
51
|
+
}
|