@toolrelay/cli 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 +461 -0
- package/dist/api-client-7MTO2YNV.js +4 -0
- package/dist/api-client-7MTO2YNV.js.map +1 -0
- package/dist/auth-flow-VQXXGXIV.js +103 -0
- package/dist/auth-flow-VQXXGXIV.js.map +1 -0
- package/dist/chunk-3LR6JESE.js +78 -0
- package/dist/chunk-3LR6JESE.js.map +1 -0
- package/dist/chunk-7AYNBNB4.js +371 -0
- package/dist/chunk-7AYNBNB4.js.map +1 -0
- package/dist/chunk-CTTPIXB3.js +53 -0
- package/dist/chunk-CTTPIXB3.js.map +1 -0
- package/dist/credentials-KWHZKJ5O.js +4 -0
- package/dist/credentials-KWHZKJ5O.js.map +1 -0
- package/dist/index.js +2774 -0
- package/dist/index.js.map +1 -0
- package/dist/publish-RSJ4I6HJ.js +423 -0
- package/dist/publish-RSJ4I6HJ.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { validateUrlNotPrivate } from './chunk-7AYNBNB4.js';
|
|
3
|
+
import { resolveAuth } from './chunk-CTTPIXB3.js';
|
|
4
|
+
import { ToolRelayApiClient } from './chunk-3LR6JESE.js';
|
|
5
|
+
|
|
6
|
+
// src/publish.ts
|
|
7
|
+
var BOLD = "\x1B[1m";
|
|
8
|
+
var DIM = "\x1B[2m";
|
|
9
|
+
var RESET = "\x1B[0m";
|
|
10
|
+
var GREEN = "\x1B[32m";
|
|
11
|
+
var RED = "\x1B[31m";
|
|
12
|
+
var YELLOW = "\x1B[33m";
|
|
13
|
+
function validateForPublish(config) {
|
|
14
|
+
const errors = [];
|
|
15
|
+
const urlCheck = validateUrlNotPrivate(config.app.base_url);
|
|
16
|
+
if (!urlCheck.valid) {
|
|
17
|
+
errors.push({
|
|
18
|
+
field: "app.base_url",
|
|
19
|
+
message: `"${config.app.base_url}" cannot be deployed \u2014 ${urlCheck.error}. Use a publicly reachable URL.`
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const parsed = new URL(config.app.base_url);
|
|
24
|
+
if (parsed.protocol === "http:") {
|
|
25
|
+
errors.push({
|
|
26
|
+
field: "app.base_url",
|
|
27
|
+
message: `"${config.app.base_url}" uses HTTP. Production APIs should use HTTPS.`
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
if (!config.app.name.trim()) {
|
|
33
|
+
errors.push({ field: "app.name", message: "App name is required." });
|
|
34
|
+
}
|
|
35
|
+
if (config.app.auth_type === "oauth2" && config.app.auth_config) {
|
|
36
|
+
const ac = config.app.auth_config;
|
|
37
|
+
for (const requiredField of ["authorize_url", "token_url"]) {
|
|
38
|
+
if (!ac[requiredField]) {
|
|
39
|
+
errors.push({
|
|
40
|
+
field: `app.auth_config.${requiredField}`,
|
|
41
|
+
message: `OAuth2 apps require "${requiredField}" in auth_config.`
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
for (const urlField of ["authorize_url", "token_url"]) {
|
|
46
|
+
const url = ac[urlField];
|
|
47
|
+
if (typeof url === "string") {
|
|
48
|
+
const oauthUrlCheck = validateUrlNotPrivate(url);
|
|
49
|
+
if (!oauthUrlCheck.valid) {
|
|
50
|
+
errors.push({
|
|
51
|
+
field: `app.auth_config.${urlField}`,
|
|
52
|
+
message: `"${url}" cannot be deployed \u2014 ${oauthUrlCheck.error}.`
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (config.app.auth_type === "static_token" && config.app.auth_config) {
|
|
59
|
+
if (!config.app.auth_config["token"]) {
|
|
60
|
+
errors.push({
|
|
61
|
+
field: "app.auth_config.token",
|
|
62
|
+
message: 'static_token auth requires a "token" in auth_config.'
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const tool of config.tools) {
|
|
67
|
+
if (!tool.description?.trim()) {
|
|
68
|
+
errors.push({
|
|
69
|
+
field: `tools[${tool.name}].description`,
|
|
70
|
+
message: `Tool "${tool.name}" has no description. AI agents need descriptions to pick the right tool.`
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const toolNames = /* @__PURE__ */ new Set();
|
|
75
|
+
for (const tool of config.tools) {
|
|
76
|
+
if (toolNames.has(tool.name)) {
|
|
77
|
+
errors.push({
|
|
78
|
+
field: `tools[${tool.name}]`,
|
|
79
|
+
message: `Duplicate tool name "${tool.name}".`
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
toolNames.add(tool.name);
|
|
83
|
+
}
|
|
84
|
+
return errors;
|
|
85
|
+
}
|
|
86
|
+
async function publish(config, options) {
|
|
87
|
+
const result = {
|
|
88
|
+
success: false,
|
|
89
|
+
created: [],
|
|
90
|
+
updated: [],
|
|
91
|
+
pruned: [],
|
|
92
|
+
errors: []
|
|
93
|
+
};
|
|
94
|
+
const validationErrors = validateForPublish(config);
|
|
95
|
+
const hardErrors = validationErrors.filter((e) => !e.message.includes("uses HTTP") && !e.message.includes("no description"));
|
|
96
|
+
const warnings = validationErrors.filter((e) => e.message.includes("uses HTTP") || e.message.includes("no description"));
|
|
97
|
+
if (warnings.length > 0) {
|
|
98
|
+
console.log(`
|
|
99
|
+
${YELLOW}\u26A0 Warnings:${RESET}`);
|
|
100
|
+
for (const w of warnings) {
|
|
101
|
+
console.log(` ${YELLOW}\u2022${RESET} ${w.field}: ${w.message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (hardErrors.length > 0) {
|
|
105
|
+
console.error(`
|
|
106
|
+
${RED}\u2717 Config is not valid for deployment:${RESET}`);
|
|
107
|
+
for (const e of hardErrors) {
|
|
108
|
+
console.error(` ${RED}\u2022${RESET} ${e.field}: ${e.message}`);
|
|
109
|
+
}
|
|
110
|
+
console.error("");
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
const auth = resolveAuth();
|
|
114
|
+
if (!auth) {
|
|
115
|
+
console.error(`
|
|
116
|
+
${RED}\u2717${RESET} Not authenticated.`);
|
|
117
|
+
console.error(` Run ${BOLD}toolrelay login${RESET} or set ${BOLD}TOOLRELAY_DEPLOY_TOKEN${RESET} env var.
|
|
118
|
+
`);
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
const client = new ToolRelayApiClient(auth);
|
|
122
|
+
let userEmail;
|
|
123
|
+
try {
|
|
124
|
+
const user = await client.whoami();
|
|
125
|
+
userEmail = user.email;
|
|
126
|
+
console.log(`
|
|
127
|
+
${GREEN}\u2713${RESET} Authenticated as ${BOLD}${userEmail}${RESET} (via ${auth.source})`);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
const apiErr = err;
|
|
130
|
+
if (apiErr.status === 401) {
|
|
131
|
+
console.error(`
|
|
132
|
+
${RED}\u2717${RESET} Authentication failed \u2014 token is invalid or expired.`);
|
|
133
|
+
console.error(` Run ${BOLD}toolrelay login${RESET} to re-authenticate.
|
|
134
|
+
`);
|
|
135
|
+
} else {
|
|
136
|
+
console.error(`
|
|
137
|
+
${RED}\u2717${RESET} Failed to verify credentials: ${apiErr.message}`);
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
const slug = config.app.slug ?? slugify(config.app.name);
|
|
142
|
+
if (!slug) {
|
|
143
|
+
console.error(`
|
|
144
|
+
${RED}\u2717${RESET} App slug is required. Add "slug" to your app config.
|
|
145
|
+
`);
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
let existingApp = null;
|
|
149
|
+
try {
|
|
150
|
+
const { apps } = await client.listApps();
|
|
151
|
+
const match = apps.find((a) => a.slug === slug);
|
|
152
|
+
if (match) {
|
|
153
|
+
const { app: fullApp } = await client.getApp(match.id);
|
|
154
|
+
existingApp = fullApp;
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
console.error(`
|
|
158
|
+
${RED}\u2717${RESET} Failed to list apps: ${err.message}`);
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
if (options.dryRun) {
|
|
162
|
+
console.log(`
|
|
163
|
+
${YELLOW}[dry-run]${RESET} No changes will be made.
|
|
164
|
+
`);
|
|
165
|
+
}
|
|
166
|
+
let appId;
|
|
167
|
+
try {
|
|
168
|
+
if (existingApp) {
|
|
169
|
+
appId = existingApp.id;
|
|
170
|
+
const changes = diffApp(config, existingApp);
|
|
171
|
+
if (Object.keys(changes).length > 0) {
|
|
172
|
+
if (options.dryRun) {
|
|
173
|
+
console.log(` ${YELLOW}~${RESET} Would update app "${BOLD}${slug}${RESET}"`);
|
|
174
|
+
for (const [key, val] of Object.entries(changes)) {
|
|
175
|
+
console.log(` ${DIM}${key}: ${JSON.stringify(val)}${RESET}`);
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
const { app } = await client.updateApp(appId, changes);
|
|
179
|
+
result.app = app;
|
|
180
|
+
console.log(` ${GREEN}~${RESET} Updated app "${BOLD}${slug}${RESET}"`);
|
|
181
|
+
for (const key of Object.keys(changes)) {
|
|
182
|
+
console.log(` ${DIM}${key} updated${RESET}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
result.app = existingApp;
|
|
187
|
+
console.log(` ${GREEN}\u2713${RESET} App "${BOLD}${slug}${RESET}" is up to date`);
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
const appBody = buildCreateAppBody(config, slug);
|
|
191
|
+
if (options.dryRun) {
|
|
192
|
+
console.log(` ${YELLOW}+${RESET} Would create app "${BOLD}${slug}${RESET}"`);
|
|
193
|
+
appId = "dry-run";
|
|
194
|
+
} else {
|
|
195
|
+
const { app } = await client.createApp(appBody);
|
|
196
|
+
appId = app.id;
|
|
197
|
+
result.app = app;
|
|
198
|
+
result.created.push(`app:${slug}`);
|
|
199
|
+
console.log(` ${GREEN}+${RESET} Created app "${BOLD}${slug}${RESET}" (${app.id})`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} catch (err) {
|
|
203
|
+
const apiErr = err;
|
|
204
|
+
console.error(`
|
|
205
|
+
${RED}\u2717${RESET} Failed to ${existingApp ? "update" : "create"} app: ${apiErr.message}`);
|
|
206
|
+
result.errors.push(apiErr.message);
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
let existingTools = [];
|
|
210
|
+
if (existingApp && !options.dryRun) {
|
|
211
|
+
try {
|
|
212
|
+
const { tools } = await client.listTools(appId);
|
|
213
|
+
existingTools = tools;
|
|
214
|
+
} catch (err) {
|
|
215
|
+
console.error(`
|
|
216
|
+
${RED}\u2717${RESET} Failed to list tools: ${err.message}`);
|
|
217
|
+
result.errors.push(err.message);
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const existingToolsByName = new Map(existingTools.map((t) => [t.name, t]));
|
|
222
|
+
const configToolNames = new Set(config.tools.map((t) => t.name));
|
|
223
|
+
for (const toolConfig of config.tools) {
|
|
224
|
+
const existingTool = existingToolsByName.get(toolConfig.name);
|
|
225
|
+
const toolBody = buildToolBody(toolConfig);
|
|
226
|
+
try {
|
|
227
|
+
if (existingTool) {
|
|
228
|
+
const changes = diffTool(toolConfig, existingTool);
|
|
229
|
+
if (Object.keys(changes).length > 0) {
|
|
230
|
+
if (options.dryRun) {
|
|
231
|
+
console.log(` ${YELLOW}~${RESET} Would update tool "${BOLD}${toolConfig.name}${RESET}"`);
|
|
232
|
+
for (const key of Object.keys(changes)) {
|
|
233
|
+
console.log(` ${DIM}${key} changed${RESET}`);
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
await client.updateTool(appId, existingTool.id, changes);
|
|
237
|
+
result.updated.push(toolConfig.name);
|
|
238
|
+
console.log(` ${GREEN}~${RESET} Updated tool "${BOLD}${toolConfig.name}${RESET}"`);
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
console.log(` ${GREEN}\u2713${RESET} Tool "${BOLD}${toolConfig.name}${RESET}" is up to date`);
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
if (options.dryRun) {
|
|
245
|
+
console.log(` ${YELLOW}+${RESET} Would create tool "${BOLD}${toolConfig.name}${RESET}" (${toolConfig.http_method} ${toolConfig.endpoint_path})`);
|
|
246
|
+
} else {
|
|
247
|
+
await client.createTool(appId, toolBody);
|
|
248
|
+
result.created.push(toolConfig.name);
|
|
249
|
+
console.log(` ${GREEN}+${RESET} Created tool "${BOLD}${toolConfig.name}${RESET}" (${toolConfig.http_method} ${toolConfig.endpoint_path})`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
} catch (err) {
|
|
253
|
+
const apiErr = err;
|
|
254
|
+
console.error(` ${RED}\u2717${RESET} Failed to sync tool "${toolConfig.name}": ${apiErr.message}`);
|
|
255
|
+
result.errors.push(`${toolConfig.name}: ${apiErr.message}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const orphanedTools = existingTools.filter((t) => !configToolNames.has(t.name));
|
|
259
|
+
if (orphanedTools.length > 0) {
|
|
260
|
+
if (options.prune) {
|
|
261
|
+
for (const tool of orphanedTools) {
|
|
262
|
+
try {
|
|
263
|
+
if (options.dryRun) {
|
|
264
|
+
console.log(` ${YELLOW}-${RESET} Would delete tool "${BOLD}${tool.name}${RESET}"`);
|
|
265
|
+
} else {
|
|
266
|
+
await client.deleteTool(appId, tool.id);
|
|
267
|
+
result.pruned.push(tool.name);
|
|
268
|
+
console.log(` ${RED}-${RESET} Deleted tool "${BOLD}${tool.name}${RESET}"`);
|
|
269
|
+
}
|
|
270
|
+
} catch (err) {
|
|
271
|
+
const apiErr = err;
|
|
272
|
+
console.error(` ${RED}\u2717${RESET} Failed to delete tool "${tool.name}": ${apiErr.message}`);
|
|
273
|
+
result.errors.push(`delete ${tool.name}: ${apiErr.message}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
for (const tool of orphanedTools) {
|
|
278
|
+
console.log(` ${YELLOW}\u26A0${RESET} Tool "${BOLD}${tool.name}${RESET}" exists remotely but not in config ${DIM}(use --prune to remove)${RESET}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (!options.dryRun && result.errors.length === 0) {
|
|
283
|
+
try {
|
|
284
|
+
const currentApp = result.app ?? existingApp;
|
|
285
|
+
if (currentApp && !currentApp.is_published) {
|
|
286
|
+
const { app } = await client.publishApp(appId);
|
|
287
|
+
result.app = app;
|
|
288
|
+
console.log(`
|
|
289
|
+
${GREEN}\u2713${RESET} App published`);
|
|
290
|
+
}
|
|
291
|
+
} catch (err) {
|
|
292
|
+
const apiErr = err;
|
|
293
|
+
console.log(`
|
|
294
|
+
${YELLOW}\u26A0${RESET} App synced but not published: ${apiErr.message}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
result.success = result.errors.length === 0;
|
|
298
|
+
if (options.dryRun) {
|
|
299
|
+
console.log(`
|
|
300
|
+
${YELLOW}[dry-run]${RESET} No changes were made.
|
|
301
|
+
`);
|
|
302
|
+
} else if (result.success) {
|
|
303
|
+
const appUrl = `${auth.api_url.replace(/\/\/api\./, "//")}/apps/${slug}`;
|
|
304
|
+
console.log(`
|
|
305
|
+
${GREEN}\u2713${RESET} Published: ${BOLD}${appUrl}${RESET}
|
|
306
|
+
`);
|
|
307
|
+
} else {
|
|
308
|
+
console.error(`
|
|
309
|
+
${RED}\u2717${RESET} Published with ${result.errors.length} error(s).
|
|
310
|
+
`);
|
|
311
|
+
}
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
function slugify(name) {
|
|
315
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
316
|
+
}
|
|
317
|
+
function buildCreateAppBody(config, slug) {
|
|
318
|
+
const body = {
|
|
319
|
+
name: config.app.name,
|
|
320
|
+
slug,
|
|
321
|
+
description: config.app.description ?? "",
|
|
322
|
+
base_url: config.app.base_url,
|
|
323
|
+
auth_type: config.app.auth_type
|
|
324
|
+
};
|
|
325
|
+
if (config.app.auth_config) {
|
|
326
|
+
if (config.app.auth_type === "oauth2") {
|
|
327
|
+
const ac = config.app.auth_config;
|
|
328
|
+
if (ac["authorize_url"]) body["oauth_authorize_url"] = ac["authorize_url"];
|
|
329
|
+
if (ac["token_url"]) body["oauth_token_url"] = ac["token_url"];
|
|
330
|
+
if (ac["client_id"]) body["oauth_client_id"] = ac["client_id"];
|
|
331
|
+
if (ac["client_secret"]) body["oauth_client_secret"] = ac["client_secret"];
|
|
332
|
+
if (ac["scopes"]) body["oauth_scopes"] = ac["scopes"];
|
|
333
|
+
} else {
|
|
334
|
+
body["auth_config"] = config.app.auth_config;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (config.app.global_headers) {
|
|
338
|
+
body["global_headers"] = config.app.global_headers;
|
|
339
|
+
}
|
|
340
|
+
return body;
|
|
341
|
+
}
|
|
342
|
+
function buildToolBody(toolConfig) {
|
|
343
|
+
const body = {
|
|
344
|
+
name: toolConfig.name,
|
|
345
|
+
description: toolConfig.description ?? "",
|
|
346
|
+
http_method: toolConfig.http_method,
|
|
347
|
+
endpoint_path: toolConfig.endpoint_path,
|
|
348
|
+
parameter_mapping: toolConfig.parameter_mapping
|
|
349
|
+
};
|
|
350
|
+
if (toolConfig.permission_level) {
|
|
351
|
+
body["permission_level"] = toolConfig.permission_level;
|
|
352
|
+
}
|
|
353
|
+
if (toolConfig.headers_template) {
|
|
354
|
+
body["headers_template"] = toolConfig.headers_template;
|
|
355
|
+
}
|
|
356
|
+
if (toolConfig.request_schema) {
|
|
357
|
+
body["request_schema"] = toolConfig.request_schema;
|
|
358
|
+
}
|
|
359
|
+
if (toolConfig.response_schema) {
|
|
360
|
+
body["response_schema"] = toolConfig.response_schema;
|
|
361
|
+
}
|
|
362
|
+
if (toolConfig.mcp_annotations) {
|
|
363
|
+
body["mcp_annotations"] = toolConfig.mcp_annotations;
|
|
364
|
+
}
|
|
365
|
+
return body;
|
|
366
|
+
}
|
|
367
|
+
function diffApp(config, existing) {
|
|
368
|
+
const changes = {};
|
|
369
|
+
if (config.app.name !== existing.name) changes["name"] = config.app.name;
|
|
370
|
+
if (config.app.description !== void 0 && config.app.description !== existing.description) {
|
|
371
|
+
changes["description"] = config.app.description;
|
|
372
|
+
}
|
|
373
|
+
if (config.app.base_url !== existing.base_url) changes["base_url"] = config.app.base_url;
|
|
374
|
+
if (config.app.auth_type !== existing.auth_type) changes["auth_type"] = config.app.auth_type;
|
|
375
|
+
if (config.app.auth_config) {
|
|
376
|
+
const existingConfig = existing.auth_config ?? {};
|
|
377
|
+
if (config.app.auth_type === "oauth2") {
|
|
378
|
+
const ac = config.app.auth_config;
|
|
379
|
+
if (ac["authorize_url"] && ac["authorize_url"] !== existingConfig["authorize_url"]) changes["oauth_authorize_url"] = ac["authorize_url"];
|
|
380
|
+
if (ac["token_url"] && ac["token_url"] !== existingConfig["token_url"]) changes["oauth_token_url"] = ac["token_url"];
|
|
381
|
+
if (ac["client_id"] && ac["client_id"] !== existingConfig["client_id"]) changes["oauth_client_id"] = ac["client_id"];
|
|
382
|
+
if (ac["client_secret"] && ac["client_secret"] !== existingConfig["client_secret"]) changes["oauth_client_secret"] = ac["client_secret"];
|
|
383
|
+
if (ac["scopes"] && ac["scopes"] !== existingConfig["scopes"]) changes["oauth_scopes"] = ac["scopes"];
|
|
384
|
+
} else {
|
|
385
|
+
if (JSON.stringify(config.app.auth_config) !== JSON.stringify(existingConfig)) {
|
|
386
|
+
changes["auth_config"] = config.app.auth_config;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return changes;
|
|
391
|
+
}
|
|
392
|
+
function diffTool(config, existing) {
|
|
393
|
+
const changes = {};
|
|
394
|
+
if ((config.description ?? "") !== existing.description) {
|
|
395
|
+
changes["description"] = config.description ?? "";
|
|
396
|
+
}
|
|
397
|
+
if (config.http_method !== existing.http_method) {
|
|
398
|
+
changes["http_method"] = config.http_method;
|
|
399
|
+
}
|
|
400
|
+
if (config.endpoint_path !== existing.endpoint_path) {
|
|
401
|
+
changes["endpoint_path"] = config.endpoint_path;
|
|
402
|
+
}
|
|
403
|
+
if (JSON.stringify(config.parameter_mapping) !== JSON.stringify(existing.parameter_mapping)) {
|
|
404
|
+
changes["parameter_mapping"] = config.parameter_mapping;
|
|
405
|
+
}
|
|
406
|
+
if (config.headers_template && JSON.stringify(config.headers_template) !== JSON.stringify(existing.headers_template)) {
|
|
407
|
+
changes["headers_template"] = config.headers_template;
|
|
408
|
+
}
|
|
409
|
+
if (config.request_schema && JSON.stringify(config.request_schema) !== JSON.stringify(existing.request_schema)) {
|
|
410
|
+
changes["request_schema"] = config.request_schema;
|
|
411
|
+
}
|
|
412
|
+
if (config.response_schema && JSON.stringify(config.response_schema) !== JSON.stringify(existing.response_schema)) {
|
|
413
|
+
changes["response_schema"] = config.response_schema;
|
|
414
|
+
}
|
|
415
|
+
if (config.mcp_annotations && JSON.stringify(config.mcp_annotations) !== JSON.stringify(existing.mcp_annotations)) {
|
|
416
|
+
changes["mcp_annotations"] = config.mcp_annotations;
|
|
417
|
+
}
|
|
418
|
+
return changes;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export { publish, validateForPublish };
|
|
422
|
+
//# sourceMappingURL=publish-RSJ4I6HJ.js.map
|
|
423
|
+
//# sourceMappingURL=publish-RSJ4I6HJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/publish.ts"],"names":[],"mappings":"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","file":"publish-RSJ4I6HJ.js","sourcesContent":["import type { CliConfig, CliToolConfig } from './types.js';\nimport { validateUrlNotPrivate } from '@toolrelay/shared';\nimport { resolveAuth } from './credentials.js';\nimport { ToolRelayApiClient } from './api-client.js';\nimport type { ApiApp, ApiTool, ApiError } from './api-client.js';\n\n// ANSI helpers\nconst BOLD = '\\x1b[1m';\nconst DIM = '\\x1b[2m';\nconst RESET = '\\x1b[0m';\nconst GREEN = '\\x1b[32m';\nconst RED = '\\x1b[31m';\nconst YELLOW = '\\x1b[33m';\n\n// ─── Options ────────────────────────────────────────────────────────────────\n\nexport interface PublishOptions {\n dryRun: boolean;\n prune: boolean;\n}\n\n// ─── Result ─────────────────────────────────────────────────────────────────\n\nexport interface PublishResult {\n success: boolean;\n app?: ApiApp;\n created: string[];\n updated: string[];\n pruned: string[];\n errors: string[];\n}\n\n// ─── Pre-publish validation ─────────────────────────────────────────────────\n\nexport interface PublishValidationError {\n field: string;\n message: string;\n}\n\nexport function validateForPublish(config: CliConfig): PublishValidationError[] {\n const errors: PublishValidationError[] = [];\n\n // base_url must not be localhost / private IP\n const urlCheck = validateUrlNotPrivate(config.app.base_url);\n if (!urlCheck.valid) {\n errors.push({\n field: 'app.base_url',\n message: `\"${config.app.base_url}\" cannot be deployed — ${urlCheck.error}. Use a publicly reachable URL.`,\n });\n }\n\n // base_url must be HTTPS for production\n try {\n const parsed = new URL(config.app.base_url);\n if (parsed.protocol === 'http:') {\n errors.push({\n field: 'app.base_url',\n message: `\"${config.app.base_url}\" uses HTTP. Production APIs should use HTTPS.`,\n });\n }\n } catch {\n // Already caught by urlCheck above\n }\n\n // app.name is required\n if (!config.app.name.trim()) {\n errors.push({ field: 'app.name', message: 'App name is required.' });\n }\n\n // OAuth2 must have required fields for production\n if (config.app.auth_type === 'oauth2' && config.app.auth_config) {\n const ac = config.app.auth_config;\n // Only authorize_url and token_url are required — client_id/client_secret\n // are optional (public client PKCE model), matching the onboarding wizard.\n for (const requiredField of ['authorize_url', 'token_url']) {\n if (!ac[requiredField]) {\n errors.push({\n field: `app.auth_config.${requiredField}`,\n message: `OAuth2 apps require \"${requiredField}\" in auth_config.`,\n });\n }\n }\n // OAuth URLs must also be public\n for (const urlField of ['authorize_url', 'token_url'] as const) {\n const url = ac[urlField];\n if (typeof url === 'string') {\n const oauthUrlCheck = validateUrlNotPrivate(url);\n if (!oauthUrlCheck.valid) {\n errors.push({\n field: `app.auth_config.${urlField}`,\n message: `\"${url}\" cannot be deployed — ${oauthUrlCheck.error}.`,\n });\n }\n }\n }\n }\n\n // static_token must have a token\n if (config.app.auth_type === 'static_token' && config.app.auth_config) {\n if (!config.app.auth_config['token']) {\n errors.push({\n field: 'app.auth_config.token',\n message: 'static_token auth requires a \"token\" in auth_config.',\n });\n }\n }\n\n // Every tool needs a description (important for AI agents)\n for (const tool of config.tools) {\n if (!tool.description?.trim()) {\n errors.push({\n field: `tools[${tool.name}].description`,\n message: `Tool \"${tool.name}\" has no description. AI agents need descriptions to pick the right tool.`,\n });\n }\n }\n\n // Check for duplicate tool names\n const toolNames = new Set<string>();\n for (const tool of config.tools) {\n if (toolNames.has(tool.name)) {\n errors.push({\n field: `tools[${tool.name}]`,\n message: `Duplicate tool name \"${tool.name}\".`,\n });\n }\n toolNames.add(tool.name);\n }\n\n return errors;\n}\n\n// ─── Publish ────────────────────────────────────────────────────────────────\n\nexport async function publish(config: CliConfig, options: PublishOptions): Promise<PublishResult> {\n const result: PublishResult = {\n success: false,\n created: [],\n updated: [],\n pruned: [],\n errors: [],\n };\n\n // 0. Pre-publish validation\n const validationErrors = validateForPublish(config);\n const hardErrors = validationErrors.filter((e) => !e.message.includes('uses HTTP') && !e.message.includes('no description'));\n const warnings = validationErrors.filter((e) => e.message.includes('uses HTTP') || e.message.includes('no description'));\n\n if (warnings.length > 0) {\n console.log(`\\n${YELLOW}⚠ Warnings:${RESET}`);\n for (const w of warnings) {\n console.log(` ${YELLOW}•${RESET} ${w.field}: ${w.message}`);\n }\n }\n\n if (hardErrors.length > 0) {\n console.error(`\\n${RED}✗ Config is not valid for deployment:${RESET}`);\n for (const e of hardErrors) {\n console.error(` ${RED}•${RESET} ${e.field}: ${e.message}`);\n }\n console.error('');\n return result;\n }\n\n // 1. Resolve authentication\n const auth = resolveAuth();\n if (!auth) {\n console.error(`\\n${RED}✗${RESET} Not authenticated.`);\n console.error(` Run ${BOLD}toolrelay login${RESET} or set ${BOLD}TOOLRELAY_DEPLOY_TOKEN${RESET} env var.\\n`);\n return result;\n }\n\n const client = new ToolRelayApiClient(auth);\n\n // 2. Verify credentials\n let userEmail: string;\n try {\n const user = await client.whoami();\n userEmail = user.email;\n console.log(`\\n${GREEN}✓${RESET} Authenticated as ${BOLD}${userEmail}${RESET} (via ${auth.source})`);\n } catch (err) {\n const apiErr = err as ApiError;\n if (apiErr.status === 401) {\n console.error(`\\n${RED}✗${RESET} Authentication failed — token is invalid or expired.`);\n console.error(` Run ${BOLD}toolrelay login${RESET} to re-authenticate.\\n`);\n } else {\n console.error(`\\n${RED}✗${RESET} Failed to verify credentials: ${apiErr.message}`);\n }\n return result;\n }\n\n // 3. Resolve slug — required for matching\n const slug = config.app.slug ?? slugify(config.app.name);\n if (!slug) {\n console.error(`\\n${RED}✗${RESET} App slug is required. Add \"slug\" to your app config.\\n`);\n return result;\n }\n\n // 4. Check if app already exists (find by slug, then fetch full detail for diffing)\n let existingApp: ApiApp | null = null;\n try {\n const { apps } = await client.listApps();\n const match = apps.find((a) => a.slug === slug);\n if (match) {\n // Fetch full detail — listApps omits auth_config (encrypted), but getApp returns it decrypted\n const { app: fullApp } = await client.getApp(match.id);\n existingApp = fullApp;\n }\n } catch (err) {\n console.error(`\\n${RED}✗${RESET} Failed to list apps: ${(err as ApiError).message}`);\n return result;\n }\n\n if (options.dryRun) {\n console.log(`\\n${YELLOW}[dry-run]${RESET} No changes will be made.\\n`);\n }\n\n // 5. Create or update app\n let appId: string;\n try {\n if (existingApp) {\n appId = existingApp.id;\n const changes = diffApp(config, existingApp);\n if (Object.keys(changes).length > 0) {\n if (options.dryRun) {\n console.log(` ${YELLOW}~${RESET} Would update app \"${BOLD}${slug}${RESET}\"`);\n for (const [key, val] of Object.entries(changes)) {\n console.log(` ${DIM}${key}: ${JSON.stringify(val)}${RESET}`);\n }\n } else {\n const { app } = await client.updateApp(appId, changes);\n result.app = app;\n console.log(` ${GREEN}~${RESET} Updated app \"${BOLD}${slug}${RESET}\"`);\n for (const key of Object.keys(changes)) {\n console.log(` ${DIM}${key} updated${RESET}`);\n }\n }\n } else {\n result.app = existingApp;\n console.log(` ${GREEN}✓${RESET} App \"${BOLD}${slug}${RESET}\" is up to date`);\n }\n } else {\n const appBody = buildCreateAppBody(config, slug);\n if (options.dryRun) {\n console.log(` ${YELLOW}+${RESET} Would create app \"${BOLD}${slug}${RESET}\"`);\n appId = 'dry-run';\n } else {\n const { app } = await client.createApp(appBody);\n appId = app.id;\n result.app = app;\n result.created.push(`app:${slug}`);\n console.log(` ${GREEN}+${RESET} Created app \"${BOLD}${slug}${RESET}\" (${app.id})`);\n }\n }\n } catch (err) {\n const apiErr = err as ApiError;\n console.error(`\\n${RED}✗${RESET} Failed to ${existingApp ? 'update' : 'create'} app: ${apiErr.message}`);\n result.errors.push(apiErr.message);\n return result;\n }\n\n // 6. Sync tools\n let existingTools: ApiTool[] = [];\n if (existingApp && !options.dryRun) {\n try {\n const { tools } = await client.listTools(appId!);\n existingTools = tools;\n } catch (err) {\n console.error(`\\n${RED}✗${RESET} Failed to list tools: ${(err as ApiError).message}`);\n result.errors.push((err as ApiError).message);\n return result;\n }\n }\n\n const existingToolsByName = new Map(existingTools.map((t) => [t.name, t]));\n const configToolNames = new Set(config.tools.map((t) => t.name));\n\n // Create or update each tool from config\n for (const toolConfig of config.tools) {\n const existingTool = existingToolsByName.get(toolConfig.name);\n const toolBody = buildToolBody(toolConfig);\n\n try {\n if (existingTool) {\n const changes = diffTool(toolConfig, existingTool);\n if (Object.keys(changes).length > 0) {\n if (options.dryRun) {\n console.log(` ${YELLOW}~${RESET} Would update tool \"${BOLD}${toolConfig.name}${RESET}\"`);\n for (const key of Object.keys(changes)) {\n console.log(` ${DIM}${key} changed${RESET}`);\n }\n } else {\n await client.updateTool(appId!, existingTool.id, changes);\n result.updated.push(toolConfig.name);\n console.log(` ${GREEN}~${RESET} Updated tool \"${BOLD}${toolConfig.name}${RESET}\"`);\n }\n } else {\n console.log(` ${GREEN}✓${RESET} Tool \"${BOLD}${toolConfig.name}${RESET}\" is up to date`);\n }\n } else {\n if (options.dryRun) {\n console.log(` ${YELLOW}+${RESET} Would create tool \"${BOLD}${toolConfig.name}${RESET}\" (${toolConfig.http_method} ${toolConfig.endpoint_path})`);\n } else {\n await client.createTool(appId!, toolBody);\n result.created.push(toolConfig.name);\n console.log(` ${GREEN}+${RESET} Created tool \"${BOLD}${toolConfig.name}${RESET}\" (${toolConfig.http_method} ${toolConfig.endpoint_path})`);\n }\n }\n } catch (err) {\n const apiErr = err as ApiError;\n console.error(` ${RED}✗${RESET} Failed to sync tool \"${toolConfig.name}\": ${apiErr.message}`);\n result.errors.push(`${toolConfig.name}: ${apiErr.message}`);\n }\n }\n\n // Detect orphaned tools (exist remotely but not in config)\n const orphanedTools = existingTools.filter((t) => !configToolNames.has(t.name));\n if (orphanedTools.length > 0) {\n if (options.prune) {\n for (const tool of orphanedTools) {\n try {\n if (options.dryRun) {\n console.log(` ${YELLOW}-${RESET} Would delete tool \"${BOLD}${tool.name}${RESET}\"`);\n } else {\n await client.deleteTool(appId!, tool.id);\n result.pruned.push(tool.name);\n console.log(` ${RED}-${RESET} Deleted tool \"${BOLD}${tool.name}${RESET}\"`);\n }\n } catch (err) {\n const apiErr = err as ApiError;\n console.error(` ${RED}✗${RESET} Failed to delete tool \"${tool.name}\": ${apiErr.message}`);\n result.errors.push(`delete ${tool.name}: ${apiErr.message}`);\n }\n }\n } else {\n for (const tool of orphanedTools) {\n console.log(` ${YELLOW}⚠${RESET} Tool \"${BOLD}${tool.name}${RESET}\" exists remotely but not in config ${DIM}(use --prune to remove)${RESET}`);\n }\n }\n }\n\n // 7. Publish the app if not already published\n if (!options.dryRun && result.errors.length === 0) {\n try {\n const currentApp = result.app ?? existingApp;\n if (currentApp && !currentApp.is_published) {\n const { app } = await client.publishApp(appId!);\n result.app = app;\n console.log(`\\n ${GREEN}✓${RESET} App published`);\n }\n } catch (err) {\n const apiErr = err as ApiError;\n // Non-fatal — app was synced successfully, just not auto-published\n console.log(`\\n ${YELLOW}⚠${RESET} App synced but not published: ${apiErr.message}`);\n }\n }\n\n // 8. Summary\n result.success = result.errors.length === 0;\n\n if (options.dryRun) {\n console.log(`\\n${YELLOW}[dry-run]${RESET} No changes were made.\\n`);\n } else if (result.success) {\n const appUrl = `${auth.api_url.replace(/\\/\\/api\\./, '//')}/apps/${slug}`;\n console.log(`\\n${GREEN}✓${RESET} Published: ${BOLD}${appUrl}${RESET}\\n`);\n } else {\n console.error(`\\n${RED}✗${RESET} Published with ${result.errors.length} error(s).\\n`);\n }\n\n return result;\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction slugify(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n}\n\nfunction buildCreateAppBody(config: CliConfig, slug: string): Record<string, unknown> {\n const body: Record<string, unknown> = {\n name: config.app.name,\n slug,\n description: config.app.description ?? '',\n base_url: config.app.base_url,\n auth_type: config.app.auth_type,\n };\n\n if (config.app.auth_config) {\n // For OAuth2, the API expects flat fields rather than nested auth_config\n if (config.app.auth_type === 'oauth2') {\n const ac = config.app.auth_config;\n if (ac['authorize_url']) body['oauth_authorize_url'] = ac['authorize_url'];\n if (ac['token_url']) body['oauth_token_url'] = ac['token_url'];\n if (ac['client_id']) body['oauth_client_id'] = ac['client_id'];\n if (ac['client_secret']) body['oauth_client_secret'] = ac['client_secret'];\n if (ac['scopes']) body['oauth_scopes'] = ac['scopes'];\n } else {\n body['auth_config'] = config.app.auth_config;\n }\n }\n\n if (config.app.global_headers) {\n body['global_headers'] = config.app.global_headers;\n }\n\n return body;\n}\n\nfunction buildToolBody(toolConfig: CliToolConfig): Record<string, unknown> {\n const body: Record<string, unknown> = {\n name: toolConfig.name,\n description: toolConfig.description ?? '',\n http_method: toolConfig.http_method,\n endpoint_path: toolConfig.endpoint_path,\n parameter_mapping: toolConfig.parameter_mapping,\n };\n\n if (toolConfig.permission_level) {\n body['permission_level'] = toolConfig.permission_level;\n }\n if (toolConfig.headers_template) {\n body['headers_template'] = toolConfig.headers_template;\n }\n if (toolConfig.request_schema) {\n body['request_schema'] = toolConfig.request_schema;\n }\n if (toolConfig.response_schema) {\n body['response_schema'] = toolConfig.response_schema;\n }\n if (toolConfig.mcp_annotations) {\n body['mcp_annotations'] = toolConfig.mcp_annotations;\n }\n\n return body;\n}\n\nfunction diffApp(config: CliConfig, existing: ApiApp): Record<string, unknown> {\n const changes: Record<string, unknown> = {};\n\n if (config.app.name !== existing.name) changes['name'] = config.app.name;\n if (config.app.description !== undefined && config.app.description !== existing.description) {\n changes['description'] = config.app.description;\n }\n if (config.app.base_url !== existing.base_url) changes['base_url'] = config.app.base_url;\n if (config.app.auth_type !== existing.auth_type) changes['auth_type'] = config.app.auth_type;\n\n // Compare auth_config field-by-field (getApp returns decrypted auth_config to the owner)\n if (config.app.auth_config) {\n const existingConfig = existing.auth_config ?? {};\n if (config.app.auth_type === 'oauth2') {\n const ac = config.app.auth_config;\n if (ac['authorize_url'] && ac['authorize_url'] !== existingConfig['authorize_url']) changes['oauth_authorize_url'] = ac['authorize_url'];\n if (ac['token_url'] && ac['token_url'] !== existingConfig['token_url']) changes['oauth_token_url'] = ac['token_url'];\n if (ac['client_id'] && ac['client_id'] !== existingConfig['client_id']) changes['oauth_client_id'] = ac['client_id'];\n if (ac['client_secret'] && ac['client_secret'] !== existingConfig['client_secret']) changes['oauth_client_secret'] = ac['client_secret'];\n if (ac['scopes'] && ac['scopes'] !== existingConfig['scopes']) changes['oauth_scopes'] = ac['scopes'];\n } else {\n if (JSON.stringify(config.app.auth_config) !== JSON.stringify(existingConfig)) {\n changes['auth_config'] = config.app.auth_config;\n }\n }\n }\n\n return changes;\n}\n\nfunction diffTool(config: CliToolConfig, existing: ApiTool): Record<string, unknown> {\n const changes: Record<string, unknown> = {};\n\n if ((config.description ?? '') !== existing.description) {\n changes['description'] = config.description ?? '';\n }\n if (config.http_method !== existing.http_method) {\n changes['http_method'] = config.http_method;\n }\n if (config.endpoint_path !== existing.endpoint_path) {\n changes['endpoint_path'] = config.endpoint_path;\n }\n if (JSON.stringify(config.parameter_mapping) !== JSON.stringify(existing.parameter_mapping)) {\n changes['parameter_mapping'] = config.parameter_mapping;\n }\n if (config.headers_template && JSON.stringify(config.headers_template) !== JSON.stringify(existing.headers_template)) {\n changes['headers_template'] = config.headers_template;\n }\n if (config.request_schema && JSON.stringify(config.request_schema) !== JSON.stringify(existing.request_schema)) {\n changes['request_schema'] = config.request_schema;\n }\n if (config.response_schema && JSON.stringify(config.response_schema) !== JSON.stringify(existing.response_schema)) {\n changes['response_schema'] = config.response_schema;\n }\n if (config.mcp_annotations && JSON.stringify(config.mcp_annotations) !== JSON.stringify(existing.mcp_annotations)) {\n changes['mcp_annotations'] = config.mcp_annotations;\n }\n\n return changes;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toolrelay/cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "CLI for ToolRelay — validate configs, serve local MCP servers, and deploy to production with audit traces",
|
|
6
|
+
"bin": {
|
|
7
|
+
"toolrelay": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"keywords": [
|
|
15
|
+
"toolrelay",
|
|
16
|
+
"mcp",
|
|
17
|
+
"cli",
|
|
18
|
+
"ai-tools",
|
|
19
|
+
"model-context-protocol",
|
|
20
|
+
"api-testing",
|
|
21
|
+
"oauth2"
|
|
22
|
+
],
|
|
23
|
+
"homepage": "https://toolrelay.io",
|
|
24
|
+
"license": "UNLICENSED",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"lint": "eslint src/",
|
|
29
|
+
"test": "NODE_OPTIONS='--experimental-vm-modules' jest"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"commander": "^12.1.0",
|
|
33
|
+
"zod": "^3.24.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@toolrelay/shared": "*",
|
|
37
|
+
"@types/jest": "^29.5.0",
|
|
38
|
+
"@types/node": "^20.0.0",
|
|
39
|
+
"jest": "^29.7.0",
|
|
40
|
+
"ts-jest": "^29.4.6",
|
|
41
|
+
"tsup": "^8.5.1",
|
|
42
|
+
"typescript": "^5.7.0"
|
|
43
|
+
}
|
|
44
|
+
}
|