skedyul 0.1.8 → 0.1.10
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/dist/cli/commands/diff.d.ts +1 -0
- package/dist/cli/commands/diff.js +295 -0
- package/dist/cli/commands/invoke.d.ts +1 -0
- package/dist/cli/commands/invoke.js +222 -0
- package/dist/cli/commands/serve.d.ts +1 -0
- package/dist/cli/commands/serve.js +123 -0
- package/dist/cli/commands/tools.d.ts +1 -0
- package/dist/cli/commands/tools.js +148 -0
- package/dist/cli/commands/validate.d.ts +1 -0
- package/dist/cli/commands/validate.js +269 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +94 -0
- package/dist/cli/utils.d.ts +25 -0
- package/dist/cli/utils.js +186 -0
- package/dist/config.d.ts +116 -0
- package/dist/config.js +233 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -1
- package/dist/server.js +21 -3
- package/package.json +4 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function diffCommand(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.diffCommand = diffCommand;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const utils_1 = require("../utils");
|
|
40
|
+
const config_1 = require("../../config");
|
|
41
|
+
function printHelp() {
|
|
42
|
+
console.log(`
|
|
43
|
+
skedyul dev diff - Show what would change on deploy
|
|
44
|
+
|
|
45
|
+
Usage:
|
|
46
|
+
skedyul dev diff [options]
|
|
47
|
+
|
|
48
|
+
Options:
|
|
49
|
+
--config, -c Path to config file (default: auto-detect skedyul.config.ts)
|
|
50
|
+
--registry, -r Path to registry file to compare tools
|
|
51
|
+
--json Output as JSON
|
|
52
|
+
--help, -h Show this help message
|
|
53
|
+
|
|
54
|
+
Description:
|
|
55
|
+
Compares your local skedyul.config.ts with the currently deployed configuration.
|
|
56
|
+
Shows changes in environment variables, tools, and workflows that would be
|
|
57
|
+
applied when deploying a new version.
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
# Show diff for config in current directory
|
|
61
|
+
skedyul dev diff
|
|
62
|
+
|
|
63
|
+
# Show diff with specific config
|
|
64
|
+
skedyul dev diff --config ./skedyul.config.ts
|
|
65
|
+
|
|
66
|
+
# Compare against specific registry
|
|
67
|
+
skedyul dev diff --registry ./dist/registry.js
|
|
68
|
+
`);
|
|
69
|
+
}
|
|
70
|
+
function findConfigFile(startDir) {
|
|
71
|
+
for (const fileName of config_1.CONFIG_FILE_NAMES) {
|
|
72
|
+
const filePath = path.join(startDir, fileName);
|
|
73
|
+
if (fs.existsSync(filePath)) {
|
|
74
|
+
return filePath;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function compareEnvSchemas(newEnv, oldEnv) {
|
|
80
|
+
const newKeys = newEnv ? Object.keys(newEnv) : [];
|
|
81
|
+
const oldKeys = oldEnv ? Object.keys(oldEnv) : [];
|
|
82
|
+
const added = newKeys.filter((k) => !oldKeys.includes(k));
|
|
83
|
+
const removed = oldKeys.filter((k) => !newKeys.includes(k));
|
|
84
|
+
const changed = [];
|
|
85
|
+
// Check for changes in existing keys
|
|
86
|
+
for (const key of newKeys) {
|
|
87
|
+
if (oldKeys.includes(key)) {
|
|
88
|
+
const newDef = newEnv?.[key];
|
|
89
|
+
const oldDef = oldEnv?.[key];
|
|
90
|
+
const changes = [];
|
|
91
|
+
if (newDef?.required !== oldDef?.required) {
|
|
92
|
+
changes.push(newDef?.required
|
|
93
|
+
? 'now required'
|
|
94
|
+
: 'no longer required');
|
|
95
|
+
}
|
|
96
|
+
if (newDef?.visibility !== oldDef?.visibility) {
|
|
97
|
+
changes.push(`visibility: ${oldDef?.visibility || 'visible'} → ${newDef?.visibility || 'visible'}`);
|
|
98
|
+
}
|
|
99
|
+
if (newDef?.label !== oldDef?.label) {
|
|
100
|
+
changes.push(`label changed`);
|
|
101
|
+
}
|
|
102
|
+
if (changes.length > 0) {
|
|
103
|
+
changed.push({ key, changes });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { added, removed, changed };
|
|
108
|
+
}
|
|
109
|
+
async function diffCommand(args) {
|
|
110
|
+
const { flags } = (0, utils_1.parseArgs)(args);
|
|
111
|
+
if (flags.help || flags.h) {
|
|
112
|
+
printHelp();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const jsonOutput = Boolean(flags.json);
|
|
116
|
+
// Find config file
|
|
117
|
+
let configPath = (flags.config || flags.c);
|
|
118
|
+
if (!configPath) {
|
|
119
|
+
const foundConfig = findConfigFile(process.cwd());
|
|
120
|
+
if (!foundConfig) {
|
|
121
|
+
if (jsonOutput) {
|
|
122
|
+
console.log(JSON.stringify({ error: 'No config file found' }));
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.error('❌ No config file found');
|
|
126
|
+
console.error(` Create one of: ${config_1.CONFIG_FILE_NAMES.join(', ')}`);
|
|
127
|
+
}
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
configPath = foundConfig;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
configPath = path.resolve(process.cwd(), configPath);
|
|
134
|
+
}
|
|
135
|
+
if (!fs.existsSync(configPath)) {
|
|
136
|
+
if (jsonOutput) {
|
|
137
|
+
console.log(JSON.stringify({ error: `Config file not found: ${configPath}` }));
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
console.error(`❌ Config file not found: ${configPath}`);
|
|
141
|
+
}
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
// Load config
|
|
145
|
+
let config;
|
|
146
|
+
try {
|
|
147
|
+
config = await (0, config_1.loadConfig)(configPath);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (jsonOutput) {
|
|
151
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
console.error(`❌ Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
|
|
155
|
+
}
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
// Validate config
|
|
159
|
+
const validation = (0, config_1.validateConfig)(config);
|
|
160
|
+
if (!validation.valid) {
|
|
161
|
+
if (jsonOutput) {
|
|
162
|
+
console.log(JSON.stringify({ error: 'Invalid config', errors: validation.errors }));
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
console.error('❌ Config validation failed:');
|
|
166
|
+
for (const err of validation.errors) {
|
|
167
|
+
console.error(` • ${err}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
// For now, we compare against "no previous config" (new deploy scenario)
|
|
173
|
+
// In a full implementation, this would fetch the current deployed config from the server
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
175
|
+
const previousConfig = undefined;
|
|
176
|
+
// Compare env schemas
|
|
177
|
+
const globalEnvDiff = compareEnvSchemas(config.env, previousConfig?.env);
|
|
178
|
+
const installEnvDiff = compareEnvSchemas(config.install?.env, previousConfig?.install?.env);
|
|
179
|
+
// Compare tools if registry path provided
|
|
180
|
+
let toolsDiff;
|
|
181
|
+
const registryPath = (flags.registry || flags.r);
|
|
182
|
+
if (registryPath) {
|
|
183
|
+
try {
|
|
184
|
+
const registry = await (0, utils_1.loadRegistry)(path.resolve(process.cwd(), registryPath));
|
|
185
|
+
const toolNames = Object.values(registry).map((t) => t.name);
|
|
186
|
+
// For now, show all tools as "added" since we don't have previous state
|
|
187
|
+
toolsDiff = {
|
|
188
|
+
added: toolNames,
|
|
189
|
+
removed: [],
|
|
190
|
+
total: toolNames.length,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
// Ignore registry loading errors for diff
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const result = {
|
|
198
|
+
configPath,
|
|
199
|
+
globalEnv: globalEnvDiff,
|
|
200
|
+
installEnv: installEnvDiff,
|
|
201
|
+
tools: toolsDiff,
|
|
202
|
+
summary: {
|
|
203
|
+
hasChanges: globalEnvDiff.added.length > 0 ||
|
|
204
|
+
globalEnvDiff.removed.length > 0 ||
|
|
205
|
+
globalEnvDiff.changed.length > 0 ||
|
|
206
|
+
installEnvDiff.added.length > 0 ||
|
|
207
|
+
installEnvDiff.removed.length > 0 ||
|
|
208
|
+
installEnvDiff.changed.length > 0 ||
|
|
209
|
+
(toolsDiff?.added.length ?? 0) > 0 ||
|
|
210
|
+
(toolsDiff?.removed.length ?? 0) > 0,
|
|
211
|
+
globalEnvChanges: globalEnvDiff.added.length +
|
|
212
|
+
globalEnvDiff.removed.length +
|
|
213
|
+
globalEnvDiff.changed.length,
|
|
214
|
+
installEnvChanges: installEnvDiff.added.length +
|
|
215
|
+
installEnvDiff.removed.length +
|
|
216
|
+
installEnvDiff.changed.length,
|
|
217
|
+
toolChanges: (toolsDiff?.added.length ?? 0) + (toolsDiff?.removed.length ?? 0),
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
if (jsonOutput) {
|
|
221
|
+
console.log(JSON.stringify(result, null, 2));
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
// Human-readable output
|
|
225
|
+
console.log('');
|
|
226
|
+
console.log(`📦 ${config.name}${config.version ? ` v${config.version}` : ''}`);
|
|
227
|
+
console.log(` ${configPath}`);
|
|
228
|
+
console.log('');
|
|
229
|
+
// Note about comparison
|
|
230
|
+
if (previousConfig === undefined) {
|
|
231
|
+
console.log('ℹ️ Comparing against empty state (new deployment)');
|
|
232
|
+
console.log('');
|
|
233
|
+
}
|
|
234
|
+
// Global env diff
|
|
235
|
+
if (result.summary.globalEnvChanges > 0) {
|
|
236
|
+
console.log('Global Environment Variables:');
|
|
237
|
+
for (const key of globalEnvDiff.added) {
|
|
238
|
+
const def = config.env?.[key];
|
|
239
|
+
const required = def?.required ? ' (required)' : '';
|
|
240
|
+
console.log(` + ${key}${required}`);
|
|
241
|
+
}
|
|
242
|
+
for (const key of globalEnvDiff.removed) {
|
|
243
|
+
console.log(` - ${key}`);
|
|
244
|
+
}
|
|
245
|
+
for (const item of globalEnvDiff.changed) {
|
|
246
|
+
console.log(` ~ ${item.key}: ${item.changes.join(', ')}`);
|
|
247
|
+
}
|
|
248
|
+
console.log('');
|
|
249
|
+
}
|
|
250
|
+
// Install env diff
|
|
251
|
+
if (result.summary.installEnvChanges > 0) {
|
|
252
|
+
console.log('Install Environment Variables:');
|
|
253
|
+
for (const key of installEnvDiff.added) {
|
|
254
|
+
const def = config.install?.env?.[key];
|
|
255
|
+
const required = def?.required ? ' (required)' : '';
|
|
256
|
+
console.log(` + ${key}${required}`);
|
|
257
|
+
}
|
|
258
|
+
for (const key of installEnvDiff.removed) {
|
|
259
|
+
console.log(` - ${key}`);
|
|
260
|
+
}
|
|
261
|
+
for (const item of installEnvDiff.changed) {
|
|
262
|
+
console.log(` ~ ${item.key}: ${item.changes.join(', ')}`);
|
|
263
|
+
}
|
|
264
|
+
console.log('');
|
|
265
|
+
}
|
|
266
|
+
// Tools diff
|
|
267
|
+
if (toolsDiff && result.summary.toolChanges > 0) {
|
|
268
|
+
console.log('Tools:');
|
|
269
|
+
for (const name of toolsDiff.added) {
|
|
270
|
+
console.log(` + ${name}`);
|
|
271
|
+
}
|
|
272
|
+
for (const name of toolsDiff.removed) {
|
|
273
|
+
console.log(` - ${name}`);
|
|
274
|
+
}
|
|
275
|
+
console.log('');
|
|
276
|
+
}
|
|
277
|
+
// Summary
|
|
278
|
+
if (result.summary.hasChanges) {
|
|
279
|
+
console.log('Summary:');
|
|
280
|
+
if (result.summary.globalEnvChanges > 0) {
|
|
281
|
+
console.log(` • ${result.summary.globalEnvChanges} global env var change(s)`);
|
|
282
|
+
}
|
|
283
|
+
if (result.summary.installEnvChanges > 0) {
|
|
284
|
+
console.log(` • ${result.summary.installEnvChanges} install env var change(s)`);
|
|
285
|
+
}
|
|
286
|
+
if (result.summary.toolChanges > 0) {
|
|
287
|
+
console.log(` • ${result.summary.toolChanges} tool change(s)`);
|
|
288
|
+
}
|
|
289
|
+
console.log('');
|
|
290
|
+
console.log('✅ Ready to deploy');
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
console.log('✅ No changes detected');
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function invokeCommand(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.invokeCommand = invokeCommand;
|
|
37
|
+
const z = __importStar(require("zod"));
|
|
38
|
+
const utils_1 = require("../utils");
|
|
39
|
+
function printHelp() {
|
|
40
|
+
console.log(`
|
|
41
|
+
skedyul dev invoke - Invoke a tool from the registry
|
|
42
|
+
|
|
43
|
+
Usage:
|
|
44
|
+
skedyul dev invoke <tool-name> [options]
|
|
45
|
+
|
|
46
|
+
Arguments:
|
|
47
|
+
<tool-name> Name of the tool to invoke (e.g., 'calendar_slots.list')
|
|
48
|
+
|
|
49
|
+
Options:
|
|
50
|
+
--registry, -r Path to the registry file (default: ./dist/registry.js)
|
|
51
|
+
--args, -a JSON string of arguments to pass to the tool
|
|
52
|
+
--env, -e Set environment variable (can be used multiple times)
|
|
53
|
+
Format: --env KEY=VALUE
|
|
54
|
+
--env-file Load environment variables from a file (e.g., .env.local)
|
|
55
|
+
--estimate Run in estimate mode (billing only, no execution)
|
|
56
|
+
--help, -h Show this help message
|
|
57
|
+
|
|
58
|
+
Examples:
|
|
59
|
+
# Basic invocation
|
|
60
|
+
skedyul dev invoke my_tool --registry ./dist/registry.js --args '{"key": "value"}'
|
|
61
|
+
|
|
62
|
+
# With environment variables
|
|
63
|
+
skedyul dev invoke api_call \\
|
|
64
|
+
--registry ./dist/registry.js \\
|
|
65
|
+
--args '{"endpoint": "/users"}' \\
|
|
66
|
+
--env API_KEY=secret123 \\
|
|
67
|
+
--env BASE_URL=https://api.example.com
|
|
68
|
+
|
|
69
|
+
# Load env from file
|
|
70
|
+
skedyul dev invoke api_call \\
|
|
71
|
+
--registry ./dist/registry.js \\
|
|
72
|
+
--args '{"endpoint": "/users"}' \\
|
|
73
|
+
--env-file .env.local
|
|
74
|
+
|
|
75
|
+
# Estimate mode (billing only)
|
|
76
|
+
skedyul dev invoke expensive_tool \\
|
|
77
|
+
--registry ./dist/registry.js \\
|
|
78
|
+
--args '{"data": "test"}' \\
|
|
79
|
+
--estimate
|
|
80
|
+
`);
|
|
81
|
+
}
|
|
82
|
+
function getZodSchema(schema) {
|
|
83
|
+
if (!schema)
|
|
84
|
+
return undefined;
|
|
85
|
+
if (schema instanceof z.ZodType) {
|
|
86
|
+
return schema;
|
|
87
|
+
}
|
|
88
|
+
if (typeof schema === 'object' && schema !== null && 'zod' in schema) {
|
|
89
|
+
const schemaWithZod = schema;
|
|
90
|
+
if (schemaWithZod.zod instanceof z.ZodType) {
|
|
91
|
+
return schemaWithZod.zod;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
async function invokeCommand(args) {
|
|
97
|
+
const { flags, positional } = (0, utils_1.parseArgs)(args);
|
|
98
|
+
if (flags.help || flags.h) {
|
|
99
|
+
printHelp();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const toolName = positional[0];
|
|
103
|
+
if (!toolName) {
|
|
104
|
+
console.error('Error: Tool name is required');
|
|
105
|
+
console.error("Run 'skedyul dev invoke --help' for usage information.");
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
// Get registry path
|
|
109
|
+
const registryPath = (flags.registry || flags.r || './dist/registry.js');
|
|
110
|
+
// Parse tool arguments
|
|
111
|
+
let toolArgs = {};
|
|
112
|
+
const argsValue = flags.args || flags.a;
|
|
113
|
+
if (argsValue && typeof argsValue === 'string') {
|
|
114
|
+
try {
|
|
115
|
+
toolArgs = JSON.parse(argsValue);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
console.error('Error: Invalid JSON in --args');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Build environment
|
|
123
|
+
const env = { ...process.env };
|
|
124
|
+
// Load from env file if specified
|
|
125
|
+
const envFile = flags['env-file'];
|
|
126
|
+
if (envFile && typeof envFile === 'string') {
|
|
127
|
+
try {
|
|
128
|
+
const fileEnv = (0, utils_1.loadEnvFile)(envFile);
|
|
129
|
+
Object.assign(env, fileEnv);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
console.error(`Error loading env file: ${error instanceof Error ? error.message : String(error)}`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Parse --env flags from raw args
|
|
137
|
+
const cliEnv = (0, utils_1.parseEnvFlags)(args);
|
|
138
|
+
Object.assign(env, cliEnv);
|
|
139
|
+
// Check for estimate mode
|
|
140
|
+
const estimateMode = Boolean(flags.estimate);
|
|
141
|
+
// Load registry
|
|
142
|
+
let registry;
|
|
143
|
+
try {
|
|
144
|
+
registry = await (0, utils_1.loadRegistry)(registryPath);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
// Find tool
|
|
151
|
+
let tool;
|
|
152
|
+
// Try exact match first
|
|
153
|
+
if (registry[toolName]) {
|
|
154
|
+
tool = registry[toolName];
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// Search by tool.name property
|
|
158
|
+
for (const [, entry] of Object.entries(registry)) {
|
|
159
|
+
if (entry.name === toolName) {
|
|
160
|
+
tool = entry;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (!tool) {
|
|
166
|
+
console.error(`Error: Tool "${toolName}" not found in registry`);
|
|
167
|
+
console.error('\nAvailable tools:');
|
|
168
|
+
for (const [key, entry] of Object.entries(registry)) {
|
|
169
|
+
console.error(` - ${entry.name || key}`);
|
|
170
|
+
}
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
// Validate inputs if schema exists
|
|
174
|
+
const inputSchema = getZodSchema(tool.inputs);
|
|
175
|
+
let validatedArgs = toolArgs;
|
|
176
|
+
if (inputSchema) {
|
|
177
|
+
try {
|
|
178
|
+
validatedArgs = inputSchema.parse(toolArgs);
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
if (error instanceof z.ZodError) {
|
|
182
|
+
console.error('Error: Invalid tool arguments');
|
|
183
|
+
for (const issue of error.issues) {
|
|
184
|
+
console.error(` - ${issue.path.join('.')}: ${issue.message}`);
|
|
185
|
+
}
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Create context
|
|
192
|
+
const context = {
|
|
193
|
+
env,
|
|
194
|
+
mode: estimateMode ? 'estimate' : 'execute',
|
|
195
|
+
};
|
|
196
|
+
// Execute tool
|
|
197
|
+
console.error(`Invoking tool: ${tool.name}`);
|
|
198
|
+
if (estimateMode) {
|
|
199
|
+
console.error('Mode: estimate (billing only)');
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
const handler = tool.handler;
|
|
203
|
+
const result = await handler({
|
|
204
|
+
input: validatedArgs,
|
|
205
|
+
context,
|
|
206
|
+
});
|
|
207
|
+
// Output result
|
|
208
|
+
if (estimateMode) {
|
|
209
|
+
console.log((0, utils_1.formatJson)({ billing: result.billing }));
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
console.log((0, utils_1.formatJson)({
|
|
213
|
+
output: result.output,
|
|
214
|
+
billing: result.billing,
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.error('Error executing tool:', error instanceof Error ? error.message : String(error));
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function serveCommand(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serveCommand = serveCommand;
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
const server_1 = require("../../server");
|
|
6
|
+
function printHelp() {
|
|
7
|
+
console.log(`
|
|
8
|
+
skedyul dev serve - Start a local MCP server for testing
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
skedyul dev serve [options]
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--registry, -r Path to the registry file (default: ./dist/registry.js)
|
|
15
|
+
--port, -p Port to listen on (default: 3000)
|
|
16
|
+
--name Server name for MCP metadata (default: 'Local Dev Server')
|
|
17
|
+
--version Server version for MCP metadata (default: '0.0.1')
|
|
18
|
+
--env, -e Set environment variable (can be used multiple times)
|
|
19
|
+
Format: --env KEY=VALUE
|
|
20
|
+
--env-file Load environment variables from a file
|
|
21
|
+
--help, -h Show this help message
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
# Start server on default port
|
|
25
|
+
skedyul dev serve --registry ./dist/registry.js
|
|
26
|
+
|
|
27
|
+
# Start on custom port
|
|
28
|
+
skedyul dev serve --registry ./dist/registry.js --port 3001
|
|
29
|
+
|
|
30
|
+
# With environment variables
|
|
31
|
+
skedyul dev serve \\
|
|
32
|
+
--registry ./dist/registry.js \\
|
|
33
|
+
--env API_KEY=secret123 \\
|
|
34
|
+
--env-file .env.local
|
|
35
|
+
|
|
36
|
+
Endpoints:
|
|
37
|
+
POST /mcp MCP JSON-RPC endpoint (tools/list, tools/call)
|
|
38
|
+
GET /health Health check endpoint
|
|
39
|
+
POST /estimate Estimate billing for a tool call
|
|
40
|
+
POST /core Core API methods (if configured)
|
|
41
|
+
|
|
42
|
+
Testing with curl:
|
|
43
|
+
# List tools
|
|
44
|
+
curl -X POST http://localhost:3000/mcp \\
|
|
45
|
+
-H 'Content-Type: application/json' \\
|
|
46
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
|
|
47
|
+
|
|
48
|
+
# Call a tool
|
|
49
|
+
curl -X POST http://localhost:3000/mcp \\
|
|
50
|
+
-H 'Content-Type: application/json' \\
|
|
51
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"my_tool","arguments":{"key":"value"}}}'
|
|
52
|
+
`);
|
|
53
|
+
}
|
|
54
|
+
async function serveCommand(args) {
|
|
55
|
+
const { flags } = (0, utils_1.parseArgs)(args);
|
|
56
|
+
if (flags.help || flags.h) {
|
|
57
|
+
printHelp();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// Get registry path
|
|
61
|
+
const registryPath = (flags.registry || flags.r || './dist/registry.js');
|
|
62
|
+
// Get port
|
|
63
|
+
let port = 3000;
|
|
64
|
+
const portFlag = flags.port || flags.p;
|
|
65
|
+
if (portFlag && typeof portFlag === 'string') {
|
|
66
|
+
const parsed = parseInt(portFlag, 10);
|
|
67
|
+
if (!isNaN(parsed)) {
|
|
68
|
+
port = parsed;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Get server metadata
|
|
72
|
+
const serverName = (flags.name || 'Local Dev Server');
|
|
73
|
+
const serverVersion = (flags.version || '0.0.1');
|
|
74
|
+
// Build environment
|
|
75
|
+
const envFile = flags['env-file'];
|
|
76
|
+
if (envFile && typeof envFile === 'string') {
|
|
77
|
+
try {
|
|
78
|
+
const fileEnv = (0, utils_1.loadEnvFile)(envFile);
|
|
79
|
+
Object.assign(process.env, fileEnv);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error(`Error loading env file: ${error instanceof Error ? error.message : String(error)}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Parse --env flags from raw args
|
|
87
|
+
const cliEnv = (0, utils_1.parseEnvFlags)(args);
|
|
88
|
+
Object.assign(process.env, cliEnv);
|
|
89
|
+
// Load registry
|
|
90
|
+
let registry;
|
|
91
|
+
try {
|
|
92
|
+
registry = await (0, utils_1.loadRegistry)(registryPath);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
const toolCount = Object.keys(registry).length;
|
|
99
|
+
console.log(`Loaded ${toolCount} tool(s) from registry`);
|
|
100
|
+
// Create server
|
|
101
|
+
const server = (0, server_1.createSkedyulServer)({
|
|
102
|
+
computeLayer: 'dedicated',
|
|
103
|
+
metadata: {
|
|
104
|
+
name: serverName,
|
|
105
|
+
version: serverVersion,
|
|
106
|
+
},
|
|
107
|
+
defaultPort: port,
|
|
108
|
+
}, registry);
|
|
109
|
+
// Start listening
|
|
110
|
+
const dedicatedServer = server;
|
|
111
|
+
try {
|
|
112
|
+
await dedicatedServer.listen(port);
|
|
113
|
+
console.log(`\nEndpoints:`);
|
|
114
|
+
console.log(` POST http://localhost:${port}/mcp - MCP JSON-RPC`);
|
|
115
|
+
console.log(` GET http://localhost:${port}/health - Health check`);
|
|
116
|
+
console.log(` POST http://localhost:${port}/estimate - Billing estimate`);
|
|
117
|
+
console.log(`\nPress Ctrl+C to stop`);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.error('Failed to start server:', error instanceof Error ? error.message : String(error));
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function toolsCommand(args: string[]): Promise<void>;
|