callman-mcp 0.1.0 → 0.2.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/CHANGELOG.md +82 -0
- package/README.md +45 -16
- package/dist/client/api.d.ts +64 -3
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/api.js +67 -2
- package/dist/client/api.js.map +1 -1
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +33 -7
- package/dist/client/errors.js.map +1 -1
- package/dist/client/types.d.ts +91 -2
- package/dist/client/types.d.ts.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +7 -2
- package/dist/context.js.map +1 -1
- package/dist/domain/contracts.d.ts +893 -0
- package/dist/domain/contracts.d.ts.map +1 -0
- package/dist/domain/contracts.js +90 -0
- package/dist/domain/contracts.js.map +1 -0
- package/dist/domain/diagram/build.d.ts +520 -18
- package/dist/domain/diagram/build.d.ts.map +1 -1
- package/dist/domain/diagram/build.js +65 -19
- package/dist/domain/diagram/build.js.map +1 -1
- package/dist/domain/diagram/patch.d.ts +624 -51
- package/dist/domain/diagram/patch.d.ts.map +1 -1
- package/dist/domain/diagram/patch.js +180 -40
- package/dist/domain/diagram/patch.js.map +1 -1
- package/dist/domain/diagram/style.d.ts +315 -0
- package/dist/domain/diagram/style.d.ts.map +1 -0
- package/dist/domain/diagram/style.js +124 -0
- package/dist/domain/diagram/style.js.map +1 -0
- package/dist/domain/diagram/summarize.d.ts +8 -0
- package/dist/domain/diagram/summarize.d.ts.map +1 -1
- package/dist/domain/diagram/summarize.js +28 -13
- package/dist/domain/diagram/summarize.js.map +1 -1
- package/dist/domain/requestIntegrations.d.ts +719 -0
- package/dist/domain/requestIntegrations.d.ts.map +1 -0
- package/dist/domain/requestIntegrations.js +222 -0
- package/dist/domain/requestIntegrations.js.map +1 -0
- package/dist/domain/requestShape.d.ts +1350 -152
- package/dist/domain/requestShape.d.ts.map +1 -1
- package/dist/domain/requestShape.js +336 -121
- package/dist/domain/requestShape.js.map +1 -1
- package/dist/domain/resolve.d.ts +2 -1
- package/dist/domain/resolve.d.ts.map +1 -1
- package/dist/domain/resolve.js +5 -1
- package/dist/domain/resolve.js.map +1 -1
- package/dist/domain/scenario/build.d.ts +82 -26
- package/dist/domain/scenario/build.d.ts.map +1 -1
- package/dist/domain/scenario/build.js +196 -65
- package/dist/domain/scenario/build.js.map +1 -1
- package/dist/domain/scenario/nodeConfig.d.ts +11 -0
- package/dist/domain/scenario/nodeConfig.d.ts.map +1 -0
- package/dist/domain/scenario/nodeConfig.js +251 -0
- package/dist/domain/scenario/nodeConfig.js.map +1 -0
- package/dist/domain/scenario/summarize.d.ts +145 -25
- package/dist/domain/scenario/summarize.d.ts.map +1 -1
- package/dist/domain/scenario/summarize.js +84 -29
- package/dist/domain/scenario/summarize.js.map +1 -1
- package/dist/lib/mask.d.ts +0 -18
- package/dist/lib/mask.d.ts.map +1 -1
- package/dist/lib/mask.js +2 -44
- package/dist/lib/mask.js.map +1 -1
- package/dist/lib/pagination.d.ts +3 -2
- package/dist/lib/pagination.d.ts.map +1 -1
- package/dist/lib/pagination.js +13 -8
- package/dist/lib/pagination.js.map +1 -1
- package/dist/lib/redact.d.ts +12 -0
- package/dist/lib/redact.d.ts.map +1 -0
- package/dist/lib/redact.js +50 -0
- package/dist/lib/redact.js.map +1 -0
- package/dist/lib/strict.d.ts +4 -0
- package/dist/lib/strict.d.ts.map +1 -0
- package/dist/lib/strict.js +14 -0
- package/dist/lib/strict.js.map +1 -0
- package/dist/prompts/index.js +7 -7
- package/dist/prompts/index.js.map +1 -1
- package/dist/resources/docs/contracts.d.ts +2 -0
- package/dist/resources/docs/contracts.d.ts.map +1 -0
- package/dist/resources/docs/contracts.js +74 -0
- package/dist/resources/docs/contracts.js.map +1 -0
- package/dist/resources/docs/diagramSchema.d.ts +1 -1
- package/dist/resources/docs/diagramSchema.d.ts.map +1 -1
- package/dist/resources/docs/diagramSchema.js +95 -28
- package/dist/resources/docs/diagramSchema.js.map +1 -1
- package/dist/resources/docs/overview.d.ts +1 -1
- package/dist/resources/docs/overview.d.ts.map +1 -1
- package/dist/resources/docs/overview.js +29 -7
- package/dist/resources/docs/overview.js.map +1 -1
- package/dist/resources/docs/requestSchema.d.ts +1 -1
- package/dist/resources/docs/requestSchema.d.ts.map +1 -1
- package/dist/resources/docs/requestSchema.js +85 -16
- package/dist/resources/docs/requestSchema.js.map +1 -1
- package/dist/resources/generated/aiPacks.d.ts +1 -1
- package/dist/resources/generated/aiPacks.d.ts.map +1 -1
- package/dist/resources/generated/aiPacks.js +3 -2
- package/dist/resources/generated/aiPacks.js.map +1 -1
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +19 -19
- package/dist/resources/index.js.map +1 -1
- package/dist/run/executeCollection.d.ts +3 -1
- package/dist/run/executeCollection.d.ts.map +1 -1
- package/dist/run/executeCollection.js +16 -4
- package/dist/run/executeCollection.js.map +1 -1
- package/dist/run/summarize.d.ts +17 -6
- package/dist/run/summarize.d.ts.map +1 -1
- package/dist/run/summarize.js +48 -41
- package/dist/run/summarize.js.map +1 -1
- package/dist/run/types.d.ts +1 -0
- package/dist/run/types.d.ts.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/catalog.d.ts +1 -1
- package/dist/tools/catalog.d.ts.map +1 -1
- package/dist/tools/catalog.js +18 -0
- package/dist/tools/catalog.js.map +1 -1
- package/dist/tools/collections.d.ts.map +1 -1
- package/dist/tools/collections.js +53 -41
- package/dist/tools/collections.js.map +1 -1
- package/dist/tools/contracts.d.ts +4 -0
- package/dist/tools/contracts.d.ts.map +1 -0
- package/dist/tools/contracts.js +201 -0
- package/dist/tools/contracts.js.map +1 -0
- package/dist/tools/diagrams.d.ts.map +1 -1
- package/dist/tools/diagrams.js +19 -10
- package/dist/tools/diagrams.js.map +1 -1
- package/dist/tools/environments.d.ts.map +1 -1
- package/dist/tools/environments.js +184 -64
- package/dist/tools/environments.js.map +1 -1
- package/dist/tools/import.d.ts.map +1 -1
- package/dist/tools/import.js +111 -20
- package/dist/tools/import.js.map +1 -1
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +19 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/lookups.d.ts +38 -7
- package/dist/tools/lookups.d.ts.map +1 -1
- package/dist/tools/lookups.js +113 -10
- package/dist/tools/lookups.js.map +1 -1
- package/dist/tools/meta.d.ts.map +1 -1
- package/dist/tools/meta.js +9 -4
- package/dist/tools/meta.js.map +1 -1
- package/dist/tools/requests.d.ts.map +1 -1
- package/dist/tools/requests.js +138 -50
- package/dist/tools/requests.js.map +1 -1
- package/dist/tools/run.d.ts.map +1 -1
- package/dist/tools/run.js +84 -28
- package/dist/tools/run.js.map +1 -1
- package/dist/tools/scenarios.d.ts.map +1 -1
- package/dist/tools/scenarios.js +238 -75
- package/dist/tools/scenarios.js.map +1 -1
- package/dist/tools/shared.d.ts +1 -0
- package/dist/tools/shared.d.ts.map +1 -1
- package/dist/tools/shared.js +9 -1
- package/dist/tools/shared.js.map +1 -1
- package/dist/tools/workspaces.d.ts.map +1 -1
- package/dist/tools/workspaces.js +44 -7
- package/dist/tools/workspaces.js.map +1 -1
- package/dist-cjs/client/api.js +67 -2
- package/dist-cjs/client/errors.js +33 -7
- package/dist-cjs/context.js +7 -2
- package/dist-cjs/domain/contracts.js +93 -0
- package/dist-cjs/domain/diagram/build.js +71 -20
- package/dist-cjs/domain/diagram/patch.js +179 -39
- package/dist-cjs/domain/diagram/style.js +130 -0
- package/dist-cjs/domain/diagram/summarize.js +28 -13
- package/dist-cjs/domain/requestIntegrations.js +228 -0
- package/dist-cjs/domain/requestShape.js +342 -121
- package/dist-cjs/domain/resolve.js +5 -1
- package/dist-cjs/domain/scenario/build.js +197 -65
- package/dist-cjs/domain/scenario/nodeConfig.js +260 -0
- package/dist-cjs/domain/scenario/summarize.js +86 -29
- package/dist-cjs/lib/mask.js +3 -48
- package/dist-cjs/lib/pagination.js +13 -8
- package/dist-cjs/lib/redact.js +53 -0
- package/dist-cjs/lib/strict.js +18 -0
- package/dist-cjs/prompts/index.js +7 -7
- package/dist-cjs/resources/docs/contracts.js +76 -0
- package/dist-cjs/resources/docs/diagramSchema.js +95 -28
- package/dist-cjs/resources/docs/overview.js +29 -7
- package/dist-cjs/resources/docs/requestSchema.js +85 -16
- package/dist-cjs/resources/generated/aiPacks.js +3 -2
- package/dist-cjs/resources/index.js +17 -17
- package/dist-cjs/run/executeCollection.js +16 -4
- package/dist-cjs/run/summarize.js +48 -41
- package/dist-cjs/server.js +1 -1
- package/dist-cjs/tools/catalog.js +18 -0
- package/dist-cjs/tools/collections.js +52 -40
- package/dist-cjs/tools/contracts.js +204 -0
- package/dist-cjs/tools/diagrams.js +19 -10
- package/dist-cjs/tools/environments.js +182 -62
- package/dist-cjs/tools/import.js +107 -16
- package/dist-cjs/tools/index.js +21 -2
- package/dist-cjs/tools/lookups.js +123 -11
- package/dist-cjs/tools/meta.js +9 -4
- package/dist-cjs/tools/requests.js +134 -46
- package/dist-cjs/tools/run.js +82 -26
- package/dist-cjs/tools/scenarios.js +235 -72
- package/dist-cjs/tools/shared.js +11 -2
- package/dist-cjs/tools/workspaces.js +43 -6
- package/package.json +2 -2
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sameName = exports.resolveDiagram = exports.loadDiagrams = exports.resolveScenarioFolder = exports.resolveScenario = exports.loadScenarioCatalog = exports.resolveEnvironment = exports.loadEnvironments = exports.resolveRequest = exports.searchRequests = exports.requestIdsInFolder = exports.resolveFolder = exports.loadTree = exports.resolveCollection = exports.loadCollections = void 0;
|
|
3
|
+
exports.sameName = exports.connectionResolverFor = exports.loadConnections = exports.resolveDiagram = exports.loadDiagrams = exports.resolveScenarioFolder = exports.resolveScenario = exports.loadScenarioCatalog = exports.resolveEnvironment = exports.loadEnvironments = exports.resolveRequest = exports.searchRequests = exports.requestIdsInFolder = exports.resolveFolder = exports.loadTree = exports.resolveCollection = exports.findEnvironment = exports.findRequest = exports.findCollection = exports.findInWorkspaces = exports.workspaceTag = exports.workspacesForListing = exports.isAllWorkspaces = exports.loadCollections = void 0;
|
|
4
|
+
const errors_js_1 = require("../client/errors.js");
|
|
5
|
+
const requestIntegrations_js_1 = require("../domain/requestIntegrations.js");
|
|
4
6
|
const resolve_js_1 = require("../domain/resolve.js");
|
|
5
7
|
const ids_js_1 = require("../lib/ids.js");
|
|
6
8
|
const truncate_js_1 = require("../lib/truncate.js");
|
|
@@ -9,7 +11,106 @@ const truncate_js_1 = require("../lib/truncate.js");
|
|
|
9
11
|
// prefixes it touches so the next read is fresh.
|
|
10
12
|
const loadCollections = (ctx, ws) => ctx.cache.getOrSet(`collections:${ws}`, () => ctx.api.listAllCollections(ws));
|
|
11
13
|
exports.loadCollections = loadCollections;
|
|
12
|
-
|
|
14
|
+
// ── across workspaces ──────────────────────────────────────────────────
|
|
15
|
+
// A personal token acts in EVERY workspace the user belongs to. Reads span
|
|
16
|
+
// all of them; the session default only orders them and wins ties.
|
|
17
|
+
// `workspace: "all"` (or "*") asks a listing for every workspace explicitly.
|
|
18
|
+
const isAllWorkspaces = (ref) => Boolean(ref && ["all", "*", "any"].includes(ref.trim().toLowerCase()));
|
|
19
|
+
exports.isAllWorkspaces = isAllWorkspaces;
|
|
20
|
+
const workspacesForListing = async (ctx, explicit) => {
|
|
21
|
+
if (explicit && explicit.trim() && !(0, exports.isAllWorkspaces)(explicit))
|
|
22
|
+
return [await ctx.resolveWorkspace(explicit)];
|
|
23
|
+
const workspaces = await ctx.listWorkspaces();
|
|
24
|
+
const preferred = ctx.getDefaultWorkspaceId();
|
|
25
|
+
return [...workspaces].sort((a, b) => Number(b.id === preferred) - Number(a.id === preferred));
|
|
26
|
+
};
|
|
27
|
+
exports.workspacesForListing = workspacesForListing;
|
|
28
|
+
const workspaceTag = (ws) => ({ id: ws.id, name: ws.name });
|
|
29
|
+
exports.workspaceTag = workspaceTag;
|
|
30
|
+
// Find something the model named (a collection, request, environment…).
|
|
31
|
+
// `workspace` given → only there. Otherwise the default workspace first — an
|
|
32
|
+
// exact or id match there wins at once; then every other workspace: a unique
|
|
33
|
+
// match anywhere is used, the same name in several workspaces comes back as
|
|
34
|
+
// candidates tagged with their workspace. A workspace where the thing is
|
|
35
|
+
// missing (404 / REF_NOT_FOUND) is simply skipped.
|
|
36
|
+
const findInWorkspaces = async (ctx, explicit, kind, ref, find, options = {}) => {
|
|
37
|
+
if (explicit && explicit.trim() && !(0, exports.isAllWorkspaces)(explicit)) {
|
|
38
|
+
const ws = await ctx.resolveWorkspace(explicit);
|
|
39
|
+
return { ws, value: await find(ws) };
|
|
40
|
+
}
|
|
41
|
+
const order = await (0, exports.workspacesForListing)(ctx, null);
|
|
42
|
+
if (order.length === 0)
|
|
43
|
+
throw new errors_js_1.ToolInputError("WORKSPACE_NONE", "You are not a member of any workspace.");
|
|
44
|
+
const preferred = ctx.getDefaultWorkspaceId();
|
|
45
|
+
const hits = [];
|
|
46
|
+
const ambiguous = [];
|
|
47
|
+
for (const ws of order) {
|
|
48
|
+
let value;
|
|
49
|
+
try {
|
|
50
|
+
value = await find(ws);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof errors_js_1.ToolInputError && error.code === "REF_NOT_FOUND")
|
|
54
|
+
continue;
|
|
55
|
+
if (error instanceof errors_js_1.ToolInputError && error.code === "REF_AMBIGUOUS") {
|
|
56
|
+
ambiguous.push({ ws, error });
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (error instanceof errors_js_1.CallmanApiError && (error.status === 404 || error.status === 403))
|
|
60
|
+
continue;
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
const matched = options.matchOf?.(value);
|
|
64
|
+
if (matched === "id" || (ws.id === preferred && matched !== "partial")) {
|
|
65
|
+
return { ws, value, ...(ws.id !== preferred && order.length > 1 ? { note: `found in workspace "${ws.name}"` } : {}) };
|
|
66
|
+
}
|
|
67
|
+
hits.push({ ws, value, matched });
|
|
68
|
+
}
|
|
69
|
+
const exact = hits.filter((hit) => hit.matched !== "partial");
|
|
70
|
+
const pool = exact.length > 0 ? exact : hits;
|
|
71
|
+
// Ambiguous inside ONE workspace and nothing elsewhere: that workspace's
|
|
72
|
+
// own candidate list, tagged with where it is.
|
|
73
|
+
if (pool.length === 0 && ambiguous.length === 1) {
|
|
74
|
+
const [{ ws, error }] = ambiguous;
|
|
75
|
+
throw new errors_js_1.ToolInputError(error.code, order.length > 1 ? `${error.message} (in workspace "${ws.name}")` : error.message, { ...(error.data ?? {}), workspace: (0, exports.workspaceTag)(ws) });
|
|
76
|
+
}
|
|
77
|
+
if (pool.length === 1 && ambiguous.length === 0) {
|
|
78
|
+
const [hit] = pool;
|
|
79
|
+
return { ws: hit.ws, value: hit.value, ...(hit.ws.id !== preferred && order.length > 1 ? { note: `found in workspace "${hit.ws.name}"` } : {}) };
|
|
80
|
+
}
|
|
81
|
+
if (pool.length > 1 || ambiguous.length > 0) {
|
|
82
|
+
throw new errors_js_1.ToolInputError("REF_AMBIGUOUS", `"${ref}" matches ${kind}s in several places — pass \`workspace\` (name or id), or use the id.`, {
|
|
83
|
+
kind,
|
|
84
|
+
ref,
|
|
85
|
+
candidates: [
|
|
86
|
+
...pool.map((hit) => ({ workspace: (0, exports.workspaceTag)(hit.ws), ...(options.describe?.(hit.value) ?? {}) })),
|
|
87
|
+
...ambiguous.map((entry) => ({ workspace: (0, exports.workspaceTag)(entry.ws), candidates: entry.error.data?.candidates })),
|
|
88
|
+
],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
throw new errors_js_1.ToolInputError("REF_NOT_FOUND", `No ${kind} matches "${ref}" in ${order.length === 1 ? "your workspace" : `any of your ${order.length} workspaces`}.`, {
|
|
92
|
+
kind,
|
|
93
|
+
ref,
|
|
94
|
+
searchedWorkspaces: order.map(exports.workspaceTag),
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
exports.findInWorkspaces = findInWorkspaces;
|
|
98
|
+
const findCollection = (ctx, workspace, ref, options = {}) => (0, exports.findInWorkspaces)(ctx, workspace, "collection", ref, (ws) => (0, exports.resolveCollection)(ctx, ws.id, ref, options), {
|
|
99
|
+
matchOf: (resolved) => resolved.matched,
|
|
100
|
+
describe: (resolved) => ({ id: resolved.item.id, name: resolved.item.name }),
|
|
101
|
+
});
|
|
102
|
+
exports.findCollection = findCollection;
|
|
103
|
+
const findRequest = (ctx, workspace, ref, collectionRef, options = {}) => (0, exports.findInWorkspaces)(ctx, workspace, "request", ref, (ws) => (0, exports.resolveRequest)(ctx, ws.id, ref, collectionRef, options), {
|
|
104
|
+
matchOf: (resolved) => resolved.matched,
|
|
105
|
+
describe: (resolved) => ({ id: resolved.request.id, name: resolved.request.name, method: resolved.request.method, url: resolved.request.url }),
|
|
106
|
+
});
|
|
107
|
+
exports.findRequest = findRequest;
|
|
108
|
+
const findEnvironment = (ctx, workspace, ref, options = {}) => (0, exports.findInWorkspaces)(ctx, workspace, "environment", ref, (ws) => (0, exports.resolveEnvironment)(ctx, ws.id, ref, options), {
|
|
109
|
+
matchOf: (resolved) => resolved.matched,
|
|
110
|
+
describe: (resolved) => ({ id: resolved.item.id, name: resolved.item.name }),
|
|
111
|
+
});
|
|
112
|
+
exports.findEnvironment = findEnvironment;
|
|
113
|
+
const resolveCollection = async (ctx, ws, ref, options = {}) => {
|
|
13
114
|
const collections = await (0, exports.loadCollections)(ctx, ws);
|
|
14
115
|
if ((0, ids_js_1.isObjectId)(ref) && !collections.some((collection) => collection.id === ref)) {
|
|
15
116
|
// Not in the cached list (just created?) — let the server decide.
|
|
@@ -23,6 +124,7 @@ const resolveCollection = async (ctx, ws, ref) => {
|
|
|
23
124
|
}
|
|
24
125
|
return (0, resolve_js_1.resolveRef)("collection", ref, collections, {
|
|
25
126
|
describe: (collection) => ({ requestCount: collection.requestCount ?? null }),
|
|
127
|
+
exact: options.exact,
|
|
26
128
|
});
|
|
27
129
|
};
|
|
28
130
|
exports.resolveCollection = resolveCollection;
|
|
@@ -31,11 +133,11 @@ const loadTree = (ctx, ws, collectionId) => ctx.cache.getOrSet(`tree:${ws}:${col
|
|
|
31
133
|
return { folders: (0, resolve_js_1.flattenFolderTree)(folderTree.tree ?? []), requests };
|
|
32
134
|
});
|
|
33
135
|
exports.loadTree = loadTree;
|
|
34
|
-
const resolveFolder = async (ctx, ws, collectionId, ref) => {
|
|
136
|
+
const resolveFolder = async (ctx, ws, collectionId, ref, options = {}) => {
|
|
35
137
|
if (!ref || !ref.trim())
|
|
36
138
|
return null;
|
|
37
139
|
const tree = await (0, exports.loadTree)(ctx, ws, collectionId);
|
|
38
|
-
return (0, resolve_js_1.resolveFolderRef)(ref, tree.folders);
|
|
140
|
+
return (0, resolve_js_1.resolveFolderRef)(ref, tree.folders, options.exact);
|
|
39
141
|
};
|
|
40
142
|
exports.resolveFolder = resolveFolder;
|
|
41
143
|
// Request ids inside a folder, subfolders included.
|
|
@@ -85,9 +187,9 @@ const searchRequests = async (ctx, ws, query, collectionId, limit = truncate_js_
|
|
|
85
187
|
exports.searchRequests = searchRequests;
|
|
86
188
|
// A request reference: an id (fetched directly) or a name — unique across the
|
|
87
189
|
// given collection, or across the workspace when no collection is given.
|
|
88
|
-
const resolveRequest = async (ctx, ws, ref, collectionRef) => {
|
|
190
|
+
const resolveRequest = async (ctx, ws, ref, collectionRef, options = {}) => {
|
|
89
191
|
if ((0, ids_js_1.isObjectId)(ref)) {
|
|
90
|
-
return { request: await ctx.api.getRequest(ws, ref) };
|
|
192
|
+
return { request: await ctx.api.getRequest(ws, ref), matched: "id" };
|
|
91
193
|
}
|
|
92
194
|
const collectionId = collectionRef ? (await (0, exports.resolveCollection)(ctx, ws, collectionRef)).item.id : null;
|
|
93
195
|
const collections = await (0, exports.loadCollections)(ctx, ws);
|
|
@@ -101,14 +203,16 @@ const resolveRequest = async (ctx, ws, ref, collectionRef) => {
|
|
|
101
203
|
const resolved = (0, resolve_js_1.resolveRef)("request", ref, candidates, {
|
|
102
204
|
describe: (request) => ({ method: request.method, url: request.url, collection: request.collectionName }),
|
|
103
205
|
aliases: (request) => [`${request.method} ${request.url}`, request.url],
|
|
206
|
+
exact: options.exact,
|
|
104
207
|
});
|
|
105
|
-
return { request: await ctx.api.getRequest(ws, resolved.item.id), annotation: resolved.annotation };
|
|
208
|
+
return { request: await ctx.api.getRequest(ws, resolved.item.id), annotation: resolved.annotation, matched: resolved.matched };
|
|
106
209
|
};
|
|
107
210
|
exports.resolveRequest = resolveRequest;
|
|
108
|
-
const loadEnvironments = (ctx, ws) => ctx.cache.getOrSet(`environments:${ws}`,
|
|
211
|
+
const loadEnvironments = (ctx, ws) => ctx.cache.getOrSet(`environments:${ws}`, () => ctx.api.listAllEnvironments(ws));
|
|
109
212
|
exports.loadEnvironments = loadEnvironments;
|
|
110
|
-
const resolveEnvironment = async (ctx, ws, ref) => (0, resolve_js_1.resolveRef)("environment", ref, await (0, exports.loadEnvironments)(ctx, ws), {
|
|
213
|
+
const resolveEnvironment = async (ctx, ws, ref, options = {}) => (0, resolve_js_1.resolveRef)("environment", ref, await (0, exports.loadEnvironments)(ctx, ws), {
|
|
111
214
|
describe: (environment) => ({ variables: environment.variables.length }),
|
|
215
|
+
exact: options.exact,
|
|
112
216
|
});
|
|
113
217
|
exports.resolveEnvironment = resolveEnvironment;
|
|
114
218
|
const loadScenarioCatalog = (ctx, ws) => ctx.cache.getOrSet(`scenarios:${ws ?? "-"}`, async () => {
|
|
@@ -120,7 +224,7 @@ const loadScenarioCatalog = (ctx, ws) => ctx.cache.getOrSet(`scenarios:${ws ?? "
|
|
|
120
224
|
return { scenarios, folders };
|
|
121
225
|
});
|
|
122
226
|
exports.loadScenarioCatalog = loadScenarioCatalog;
|
|
123
|
-
const resolveScenario = async (ctx, ws, ref) => {
|
|
227
|
+
const resolveScenario = async (ctx, ws, ref, options = {}) => {
|
|
124
228
|
if ((0, ids_js_1.isObjectId)(ref)) {
|
|
125
229
|
return { item: await ctx.api.getScenario(ws, ref), matched: "id" };
|
|
126
230
|
}
|
|
@@ -128,6 +232,7 @@ const resolveScenario = async (ctx, ws, ref) => {
|
|
|
128
232
|
const folderName = new Map(folders.map((folder) => [folder.id, folder.name]));
|
|
129
233
|
return (0, resolve_js_1.resolveRef)("scenario", ref, scenarios, {
|
|
130
234
|
describe: (scenario) => ({ folder: scenario.folderId ? (folderName.get(scenario.folderId) ?? null) : null, reviewStatus: scenario.reviewStatus }),
|
|
235
|
+
exact: options.exact,
|
|
131
236
|
});
|
|
132
237
|
};
|
|
133
238
|
exports.resolveScenario = resolveScenario;
|
|
@@ -135,15 +240,22 @@ const resolveScenarioFolder = async (ctx, ws, ref) => (0, resolve_js_1.resolveRe
|
|
|
135
240
|
exports.resolveScenarioFolder = resolveScenarioFolder;
|
|
136
241
|
const loadDiagrams = (ctx) => ctx.cache.getOrSet("diagrams", () => ctx.api.listDiagrams());
|
|
137
242
|
exports.loadDiagrams = loadDiagrams;
|
|
138
|
-
const resolveDiagram = async (ctx, ref) => {
|
|
243
|
+
const resolveDiagram = async (ctx, ref, options = {}) => {
|
|
139
244
|
if ((0, ids_js_1.isObjectId)(ref))
|
|
140
245
|
return { item: await ctx.api.getDiagram(ref), matched: "id" };
|
|
141
246
|
const resolved = (0, resolve_js_1.resolveRef)("diagram", ref, await (0, exports.loadDiagrams)(ctx), {
|
|
142
247
|
describe: (diagram) => ({ canEdit: diagram.canEdit }),
|
|
248
|
+
exact: options.exact,
|
|
143
249
|
});
|
|
144
250
|
// The list carries the document too, but fetch by id for the freshest copy.
|
|
145
251
|
return { ...resolved, item: await ctx.api.getDiagram(resolved.item.id) };
|
|
146
252
|
};
|
|
147
253
|
exports.resolveDiagram = resolveDiagram;
|
|
254
|
+
const loadConnections = (ctx, ws) => ctx.cache.getOrSet(`connections:${ws}`, () => ctx.api.listConnections(ws));
|
|
255
|
+
exports.loadConnections = loadConnections;
|
|
256
|
+
// Connection resolver for request integration steps — loaded only when a
|
|
257
|
+
// request actually names a connection.
|
|
258
|
+
const connectionResolverFor = async (ctx, ws, needed) => needed ? (0, requestIntegrations_js_1.connectionResolver)(await (0, exports.loadConnections)(ctx, ws)) : undefined;
|
|
259
|
+
exports.connectionResolverFor = connectionResolverFor;
|
|
148
260
|
const sameName = (a, b) => (0, ids_js_1.normalizeName)(a) === (0, ids_js_1.normalizeName)(b);
|
|
149
261
|
exports.sameName = sameName;
|
package/dist-cjs/tools/meta.js
CHANGED
|
@@ -46,8 +46,9 @@ const registerMetaTools = (server, ctx) => {
|
|
|
46
46
|
const me = meResult.value;
|
|
47
47
|
const workspaces = workspacesResult.status === "fulfilled" ? workspacesResult.value : [];
|
|
48
48
|
const features = featuresResult.status === "fulfilled" ? featuresResult.value.features : {};
|
|
49
|
-
const
|
|
50
|
-
|
|
49
|
+
const preferred = ctx.getDefaultWorkspaceId();
|
|
50
|
+
const defaultWorkspace = preferred
|
|
51
|
+
? (workspaces.find((workspace) => workspace.id === preferred) ?? null)
|
|
51
52
|
: workspaces.length === 1
|
|
52
53
|
? workspaces[0]
|
|
53
54
|
: null;
|
|
@@ -69,8 +70,12 @@ const registerMetaTools = (server, ctx) => {
|
|
|
69
70
|
isDefault: workspace.id === defaultWorkspace?.id,
|
|
70
71
|
})),
|
|
71
72
|
defaultWorkspaceId: defaultWorkspace?.id ?? null,
|
|
72
|
-
...(workspaces.length > 1
|
|
73
|
-
? {
|
|
73
|
+
...(workspaces.length > 1
|
|
74
|
+
? {
|
|
75
|
+
workspaceScope: `This token works in all ${workspaces.length} of your workspaces. Lists and searches cover every workspace unless you pass \`workspace\`; a name is looked up in the default workspace first, then in all others. ${defaultWorkspace
|
|
76
|
+
? `The default ("${defaultWorkspace.name}") only decides where NEW things go and wins a name that exists in several workspaces`
|
|
77
|
+
: "There is no default, so creating something needs `workspace`"} — change it for this session with use_workspace.`,
|
|
78
|
+
}
|
|
74
79
|
: {}),
|
|
75
80
|
features,
|
|
76
81
|
localRun: ctx.config.allowLocalRun,
|
|
@@ -4,8 +4,10 @@ exports.registerRequestTools = void 0;
|
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const errors_js_1 = require("../client/errors.js");
|
|
6
6
|
const requestShape_js_1 = require("../domain/requestShape.js");
|
|
7
|
+
const strict_js_1 = require("../lib/strict.js");
|
|
7
8
|
const resolve_js_1 = require("../domain/resolve.js");
|
|
8
9
|
const truncate_js_1 = require("../lib/truncate.js");
|
|
10
|
+
const requestIntegrations_js_1 = require("../domain/requestIntegrations.js");
|
|
9
11
|
const lookups_js_1 = require("./lookups.js");
|
|
10
12
|
const shared_js_1 = require("./shared.js");
|
|
11
13
|
const BULK_MAX = 5000;
|
|
@@ -13,7 +15,7 @@ const MOVE_MAX = 200;
|
|
|
13
15
|
const registerRequestTools = (server, ctx) => {
|
|
14
16
|
server.registerTool("search_requests", {
|
|
15
17
|
title: "Search requests",
|
|
16
|
-
description: "Find saved requests by words in their name, method or URL (all words must match)
|
|
18
|
+
description: "Find saved requests by words in their name, method or URL (all words must match) — across ALL your workspaces, one `workspace`, or inside one collection. Returns up to 50 matches (each with its collection and workspace) with ids you can pass to get_request / run_request.",
|
|
17
19
|
inputSchema: {
|
|
18
20
|
workspace: shared_js_1.workspaceArg,
|
|
19
21
|
query: zod_1.z.string().min(1).max(200).describe("e.g. \"POST login\", \"users\", \"GET /orders\""),
|
|
@@ -22,19 +24,28 @@ const registerRequestTools = (server, ctx) => {
|
|
|
22
24
|
},
|
|
23
25
|
annotations: shared_js_1.READ,
|
|
24
26
|
}, (0, shared_js_1.guarded)(async ({ workspace, query, collection, limit }) => {
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
27
|
+
const max = limit ?? truncate_js_1.LIMITS.SEARCH_RESULTS;
|
|
28
|
+
const scoped = collection ? await (0, lookups_js_1.findCollection)(ctx, workspace, collection) : null;
|
|
29
|
+
const workspaces = scoped ? [scoped.ws] : await (0, lookups_js_1.workspacesForListing)(ctx, workspace);
|
|
30
|
+
const matches = [];
|
|
31
|
+
for (const ws of workspaces) {
|
|
32
|
+
if (matches.length >= max)
|
|
33
|
+
break;
|
|
34
|
+
for (const match of await (0, lookups_js_1.searchRequests)(ctx, ws.id, query, scoped?.value.item.id ?? null, max - matches.length))
|
|
35
|
+
matches.push({ ...match, ws });
|
|
36
|
+
}
|
|
37
|
+
const scope = scoped?.value ?? null;
|
|
28
38
|
return (0, shared_js_1.ok)({
|
|
29
39
|
query,
|
|
30
40
|
...(scope ? { collection: { id: scope.item.id, name: scope.item.name } } : {}),
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
...(workspaces.length > 1 ? { searchedWorkspaces: workspaces.length } : { workspace: (0, lookups_js_1.workspaceTag)(workspaces[0]) }),
|
|
42
|
+
matches: matches.map((request) => ({ ...(0, requestShape_js_1.requestSummaryView)(request), collection: request.collectionName, ...(workspaces.length > 1 ? { workspace: (0, lookups_js_1.workspaceTag)(request.ws) } : {}) })),
|
|
43
|
+
...(matches.length >= max ? { truncated: true } : {}),
|
|
33
44
|
});
|
|
34
45
|
}));
|
|
35
46
|
server.registerTool("get_request", {
|
|
36
47
|
title: "Get a request",
|
|
37
|
-
description: "Read one saved request: url, headers, query, body, auth (
|
|
48
|
+
description: "Read one saved request in the SAME shape create_request / update_request accept: url, headers, query, pathVariables, body (a JSON value or { mode, … }), auth ({ type, token | username/password | key/value/location | oauth2 fields }), scripts, isEnabledInRunner, plus which database / Kafka / Redis steps it has. Secrets in auth and sensitive headers are masked (••••ab) unless reveal: true — sending a masked value back unchanged keeps the stored secret. A long body comes back as bodyPreview (body: null) unless full: true.",
|
|
38
49
|
inputSchema: {
|
|
39
50
|
workspace: shared_js_1.workspaceArg,
|
|
40
51
|
request: (0, shared_js_1.refArg)("Request", "(name, \"METHOD url\" or id)"),
|
|
@@ -44,38 +55,52 @@ const registerRequestTools = (server, ctx) => {
|
|
|
44
55
|
},
|
|
45
56
|
annotations: shared_js_1.READ,
|
|
46
57
|
}, (0, shared_js_1.guarded)(async ({ workspace, request, collection, full, reveal }) => {
|
|
47
|
-
const ws = await
|
|
48
|
-
const { request: found, annotation } =
|
|
49
|
-
return (0, shared_js_1.ok)({
|
|
58
|
+
const { ws, value, note } = await (0, lookups_js_1.findRequest)(ctx, workspace, request, collection);
|
|
59
|
+
const { request: found, annotation } = value;
|
|
60
|
+
return (0, shared_js_1.ok)({
|
|
61
|
+
request: (0, requestShape_js_1.compactRequestView)(found, { full, reveal }),
|
|
62
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws),
|
|
63
|
+
...(annotation || note ? { resolved: [{ input: request, matched: found.name, id: found.id, note: [annotation, note].filter(Boolean).join("; ") }] } : {}),
|
|
64
|
+
});
|
|
50
65
|
}));
|
|
51
66
|
server.registerTool("create_request", {
|
|
52
67
|
title: "Create a request",
|
|
53
|
-
description: "Create one saved request in a collection (optionally inside a folder by id, name or path)
|
|
68
|
+
description: "Create one saved request in a collection (optionally inside a folder by id, name or path): method, url, headers, query, path variables, body (a JSON value, a raw string, or { mode: raw|formdata|urlencoded|json }), auth (bearer, basic, apiKey, oauth2), pre-request / post-response / tests scripts, and optional database / Kafka / Redis steps and DB assertions (connections by id or name — list_connections). Use {{variable}} for hosts and secrets and put the values in an environment. Contract checks are added with add_contract_rules / set_data_contract. For many requests use create_requests_bulk (one call, atomic). Reference: callman://docs/request-schema.",
|
|
54
69
|
inputSchema: { workspace: shared_js_1.workspaceArg, collection: (0, shared_js_1.refArg)("Collection"), ...requestShape_js_1.requestInputSchema.omit({ folderKey: true }).shape },
|
|
55
70
|
annotations: shared_js_1.WRITE,
|
|
56
71
|
}, (0, shared_js_1.guarded)(async ({ workspace, collection, ...item }) => {
|
|
57
|
-
const ws = await
|
|
58
|
-
const resolved = await (0, lookups_js_1.resolveCollection)(ctx, ws.id, collection);
|
|
72
|
+
const { ws, value: resolved, note } = await (0, lookups_js_1.findCollection)(ctx, workspace, collection);
|
|
59
73
|
const folder = await (0, lookups_js_1.resolveFolder)(ctx, ws.id, resolved.item.id, item.folder);
|
|
60
|
-
const
|
|
74
|
+
const input = (0, requestShape_js_1.resolveMaskedValues)(item);
|
|
75
|
+
const resolve = await (0, lookups_js_1.connectionResolverFor)(ctx, ws.id, (0, requestIntegrations_js_1.hasIntegrations)(input));
|
|
76
|
+
const { payload, warnings } = (0, requestShape_js_1.toCreateRequestPayload)(input, { collectionId: resolved.item.id, folderId: folder?.item.id ?? null }, resolve);
|
|
77
|
+
let created = await ctx.api.createRequest(ws.id, payload);
|
|
78
|
+
if (input.isEnabledInRunner === false)
|
|
79
|
+
created = await ctx.api.setRequestRunState(ws.id, created.id, false);
|
|
61
80
|
ctx.invalidate(`tree:${ws.id}:${resolved.item.id}`);
|
|
62
81
|
ctx.invalidate(`collections:${ws.id}`);
|
|
63
82
|
return (0, shared_js_1.ok)({
|
|
64
|
-
request: (0, requestShape_js_1.requestSummaryView)(created),
|
|
83
|
+
request: { ...(0, requestShape_js_1.requestSummaryView)(created), isEnabledInRunner: created.isEnabledInRunner !== false },
|
|
65
84
|
collection: { id: resolved.item.id, name: resolved.item.name },
|
|
85
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws),
|
|
66
86
|
...(folder ? { folder: { id: folder.item.id, path: folder.item.path } } : {}),
|
|
67
|
-
|
|
87
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
88
|
+
resolved: [(0, resolve_js_1.resolvedSummary)(collection, resolved), ...(note ? [{ input: collection, note }] : []), ...(folder && item.folder ? [(0, resolve_js_1.resolvedSummary)(item.folder, folder)] : [])],
|
|
68
89
|
});
|
|
69
90
|
}));
|
|
70
91
|
server.registerTool("create_requests_bulk", {
|
|
71
92
|
title: "Create many requests",
|
|
72
|
-
description: "Create many requests at once. Either `collectionName` (creates a NEW collection with an optional
|
|
93
|
+
description: "Create many requests at once. Either `collectionName` (creates a NEW collection with an optional `folders` skeleton — atomic: all or nothing; requests pick a folder with `folderKey`) or `collection` (adds to an EXISTING one, one request at a time; requests pick a folder with `folder`). Mixing the two folder styles is an error. Each request takes the same fields as create_request. Use dryRun: true first for large sets.",
|
|
73
94
|
inputSchema: {
|
|
74
95
|
workspace: shared_js_1.workspaceArg,
|
|
75
96
|
collectionName: zod_1.z.string().min(1).max(truncate_js_1.LIMITS.NAME).optional().describe("Create a new collection with this name"),
|
|
76
97
|
collection: zod_1.z.string().max(200).optional().describe("Add to this existing collection (id or name)"),
|
|
77
98
|
folders: zod_1.z
|
|
78
|
-
.array(
|
|
99
|
+
.array((0, strict_js_1.strictObject)({
|
|
100
|
+
key: zod_1.z.string().min(1).max(120).describe("Your own key, referenced by requests[].folderKey and folders[].parentKey"),
|
|
101
|
+
name: zod_1.z.string().min(1).max(truncate_js_1.LIMITS.NAME),
|
|
102
|
+
parentKey: zod_1.z.string().max(120).optional().describe("key of the parent folder (declared in this list)"),
|
|
103
|
+
}, "folder field"))
|
|
79
104
|
.max(200)
|
|
80
105
|
.optional()
|
|
81
106
|
.describe("New-collection mode only: folder skeleton; requests use folderKey"),
|
|
@@ -87,13 +112,32 @@ const registerRequestTools = (server, ctx) => {
|
|
|
87
112
|
if ((collectionName ? 1 : 0) + (collection ? 1 : 0) !== 1) {
|
|
88
113
|
throw new errors_js_1.ToolInputError("TARGET_REQUIRED", "Pass exactly one of collectionName (new collection) or collection (existing).");
|
|
89
114
|
}
|
|
90
|
-
|
|
115
|
+
// An existing collection decides the workspace; a new one needs a target.
|
|
116
|
+
const target = collection ? await (0, lookups_js_1.findCollection)(ctx, workspace, collection) : null;
|
|
117
|
+
const ws = target ? target.ws : await ctx.resolveWorkspace(workspace);
|
|
91
118
|
const folderKeys = new Set((folders ?? []).map((folder) => folder.key));
|
|
119
|
+
for (const folder of folders ?? []) {
|
|
120
|
+
if (folder.parentKey && !folderKeys.has(folder.parentKey)) {
|
|
121
|
+
throw new errors_js_1.ToolInputError("FOLDER_KEY_UNKNOWN", `Folder "${folder.name}" has parentKey "${folder.parentKey}", which is not a key in folders.`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (collection && folders && folders.length > 0) {
|
|
125
|
+
throw new errors_js_1.ToolInputError("FOLDERS_NEED_NEW_COLLECTION", "`folders` only applies with collectionName (a new collection). To file requests into an existing collection's folders, give each request a `folder` (id, name or path); create missing folders with create_folder first.");
|
|
126
|
+
}
|
|
92
127
|
for (const [index, request] of requests.entries()) {
|
|
128
|
+
if (collectionName && request.folder) {
|
|
129
|
+
throw new errors_js_1.ToolInputError("FOLDER_NEEDS_EXISTING_COLLECTION", `requests[${index}].folder only applies with \`collection\` (an existing collection). In a new collection declare \`folders\` and use folderKey.`);
|
|
130
|
+
}
|
|
131
|
+
if (collection && request.folderKey) {
|
|
132
|
+
throw new errors_js_1.ToolInputError("FOLDER_KEY_NEEDS_NEW_COLLECTION", `requests[${index}].folderKey only applies with collectionName. For an existing collection use \`folder\` (id, name or path).`);
|
|
133
|
+
}
|
|
93
134
|
if (request.folderKey && !folderKeys.has(request.folderKey)) {
|
|
94
135
|
throw new errors_js_1.ToolInputError("FOLDER_KEY_UNKNOWN", `requests[${index}] references folderKey "${request.folderKey}" which is not declared in folders.`);
|
|
95
136
|
}
|
|
96
137
|
}
|
|
138
|
+
const inputs = requests.map((request) => (0, requestShape_js_1.resolveMaskedValues)(request));
|
|
139
|
+
const disabled = inputs.filter((request) => request.isEnabledInRunner === false);
|
|
140
|
+
const resolve = await (0, lookups_js_1.connectionResolverFor)(ctx, ws.id, inputs.some(requestIntegrations_js_1.hasIntegrations));
|
|
97
141
|
if (collectionName) {
|
|
98
142
|
const existing = (await (0, lookups_js_1.loadCollections)(ctx, ws.id)).find((entry) => (0, lookups_js_1.sameName)(entry.name, collectionName));
|
|
99
143
|
if (existing) {
|
|
@@ -104,20 +148,44 @@ const registerRequestTools = (server, ctx) => {
|
|
|
104
148
|
const payload = {
|
|
105
149
|
collectionName: collectionName.trim(),
|
|
106
150
|
...(folders && folders.length > 0 ? { folders } : {}),
|
|
107
|
-
requests:
|
|
151
|
+
requests: inputs.map((request) => (0, requestShape_js_1.toFlatImportRequest)(request, resolve)),
|
|
108
152
|
};
|
|
109
153
|
if (dryRun) {
|
|
110
154
|
return (0, shared_js_1.ok)({ dryRun: true, mode: "new-collection", collectionName, folderCount: folders?.length ?? 0, requestCount: requests.length, sample: requests.slice(0, 5).map((request) => `${request.method} ${request.name}`) });
|
|
111
155
|
}
|
|
112
156
|
const result = await ctx.api.importJson(ws.id, { format: "requests", payload });
|
|
113
157
|
ctx.invalidate(`collections:${ws.id}`);
|
|
158
|
+
// The import route always stores isEnabledInRunner: true — apply the
|
|
159
|
+
// requested "off" switches afterwards, matched by name + folder.
|
|
160
|
+
const runnerOff = [];
|
|
161
|
+
const runnerWarnings = [];
|
|
162
|
+
if (disabled.length > 0) {
|
|
163
|
+
ctx.invalidate(`tree:${ws.id}:${result.collection.id}`);
|
|
164
|
+
const tree = await (0, lookups_js_1.loadTree)(ctx, ws.id, result.collection.id);
|
|
165
|
+
const folderIdByKey = new Map();
|
|
166
|
+
for (const folder of folders ?? [])
|
|
167
|
+
folderIdByKey.set(folder.key, tree.folders.find((entry) => (0, lookups_js_1.sameName)(entry.name, folder.name))?.id ?? null);
|
|
168
|
+
for (const request of disabled) {
|
|
169
|
+
const folderId = request.folderKey ? (folderIdByKey.get(request.folderKey) ?? null) : null;
|
|
170
|
+
const row = tree.requests.find((entry) => (0, lookups_js_1.sameName)(entry.name, request.name) && (entry.folderId ?? null) === folderId);
|
|
171
|
+
if (!row) {
|
|
172
|
+
runnerWarnings.push(`Could not find "${request.name}" after import to turn it off in the runner — use update_request isEnabledInRunner: false.`);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
await ctx.api.setRequestRunState(ws.id, row.id, false);
|
|
176
|
+
runnerOff.push(request.name);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
114
179
|
return (0, shared_js_1.ok)({
|
|
115
180
|
collection: { id: result.collection.id, name: result.collection.name },
|
|
181
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws),
|
|
116
182
|
created: result.importedCounts,
|
|
183
|
+
...(runnerOff.length > 0 ? { disabledInRunner: runnerOff } : {}),
|
|
184
|
+
...(runnerWarnings.length > 0 ? { warnings: runnerWarnings } : {}),
|
|
117
185
|
note: "Created atomically. Open the collection in the desktop app (it refreshes when it regains focus).",
|
|
118
186
|
});
|
|
119
187
|
}
|
|
120
|
-
const resolved =
|
|
188
|
+
const resolved = target.value;
|
|
121
189
|
const tree = await (0, lookups_js_1.loadTree)(ctx, ws.id, resolved.item.id);
|
|
122
190
|
// Resolve every folder reference up front so a typo fails before any write.
|
|
123
191
|
const folderIdByRef = new Map();
|
|
@@ -140,13 +208,18 @@ const registerRequestTools = (server, ctx) => {
|
|
|
140
208
|
}
|
|
141
209
|
const created = [];
|
|
142
210
|
const failed = [];
|
|
143
|
-
|
|
211
|
+
const warnings = [];
|
|
212
|
+
for (const request of inputs) {
|
|
144
213
|
try {
|
|
145
|
-
const
|
|
214
|
+
const built = (0, requestShape_js_1.toCreateRequestPayload)(request, { collectionId: resolved.item.id, folderId: folderIdByRef.get(request.folder ?? "") ?? null }, resolve);
|
|
215
|
+
const row = await ctx.api.createRequest(ws.id, built.payload);
|
|
216
|
+
if (request.isEnabledInRunner === false)
|
|
217
|
+
await ctx.api.setRequestRunState(ws.id, row.id, false);
|
|
146
218
|
created.push({ id: row.id, name: row.name, method: row.method });
|
|
219
|
+
warnings.push(...built.warnings.map((warning) => `${request.name}: ${warning}`));
|
|
147
220
|
}
|
|
148
221
|
catch (error) {
|
|
149
|
-
failed.push({ name: request.name, error:
|
|
222
|
+
failed.push({ name: request.name, error: (0, shared_js_1.failureOf)(error) });
|
|
150
223
|
}
|
|
151
224
|
}
|
|
152
225
|
ctx.invalidate(`tree:${ws.id}:${resolved.item.id}`);
|
|
@@ -156,13 +229,15 @@ const registerRequestTools = (server, ctx) => {
|
|
|
156
229
|
created: created.length,
|
|
157
230
|
failed,
|
|
158
231
|
requests: created,
|
|
232
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
159
233
|
...(duplicates.length > 0 ? { note: `${duplicates.length} request name(s) already existed in the same folder and were created again: ${duplicates.map((request) => request.name).join(", ")}` } : {}),
|
|
160
|
-
|
|
234
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws),
|
|
235
|
+
resolved: [(0, resolve_js_1.resolvedSummary)(collection, resolved), ...(target?.note ? [{ input: collection, note: target.note }] : [])],
|
|
161
236
|
});
|
|
162
237
|
}));
|
|
163
238
|
server.registerTool("update_request", {
|
|
164
239
|
title: "Update a request",
|
|
165
|
-
description: "Change fields of a saved request
|
|
240
|
+
description: "Change fields of a saved request — only the fields you pass change, in the same shape as create_request. `headers` / `query` / `pathVariables` / `dbAssertions` replace the whole list; `url` and `query` are kept in sync (a ?query in the url becomes rows). `database` / `kafka` merge: a phase or field you leave out is kept, null turns it off. A masked secret sent back unchanged keeps the stored value. isEnabledInRunner toggles the runner switch. To move it, use move_requests; contract checks: add_contract_rules / set_data_contract.",
|
|
166
241
|
inputSchema: {
|
|
167
242
|
workspace: shared_js_1.workspaceArg,
|
|
168
243
|
request: (0, shared_js_1.refArg)("Request", "(name, \"METHOD url\" or id)"),
|
|
@@ -170,19 +245,30 @@ const registerRequestTools = (server, ctx) => {
|
|
|
170
245
|
},
|
|
171
246
|
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
172
247
|
}, (0, shared_js_1.guarded)(async ({ workspace, request, collection, ...changes }) => {
|
|
173
|
-
const ws = await
|
|
174
|
-
const { request: found, annotation } =
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
|
|
248
|
+
const { ws, value, note } = await (0, lookups_js_1.findRequest)(ctx, workspace, request, collection);
|
|
249
|
+
const { request: found, annotation } = value;
|
|
250
|
+
const input = (0, requestShape_js_1.resolveMaskedValues)(changes, found);
|
|
251
|
+
const folder = input.folder !== undefined ? await (0, lookups_js_1.resolveFolder)(ctx, ws.id, found.collectionId, input.folder) : undefined;
|
|
252
|
+
const resolve = await (0, lookups_js_1.connectionResolverFor)(ctx, ws.id, (0, requestIntegrations_js_1.hasIntegrations)(input));
|
|
253
|
+
const { payload, warnings } = (0, requestShape_js_1.toUpdateRequestPayload)(input, found, { ...(folder !== undefined ? { folderId: folder?.item.id ?? null } : {}) }, resolve);
|
|
254
|
+
const toggle = input.isEnabledInRunner !== undefined && input.isEnabledInRunner !== (found.isEnabledInRunner !== false);
|
|
255
|
+
if (Object.keys(payload).length === 0 && input.isEnabledInRunner === undefined)
|
|
178
256
|
throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass at least one field to change.");
|
|
179
|
-
|
|
257
|
+
let updated = Object.keys(payload).length > 0 ? await ctx.api.updateRequest(ws.id, found.id, payload) : found;
|
|
258
|
+
if (toggle)
|
|
259
|
+
updated = await ctx.api.setRequestRunState(ws.id, found.id, input.isEnabledInRunner);
|
|
180
260
|
ctx.invalidate(`tree:${ws.id}:${found.collectionId}`);
|
|
181
|
-
return (0, shared_js_1.ok)({
|
|
261
|
+
return (0, shared_js_1.ok)({
|
|
262
|
+
request: (0, requestShape_js_1.compactRequestView)(updated),
|
|
263
|
+
changed: [...Object.keys(payload), ...(toggle ? ["isEnabledInRunner"] : [])],
|
|
264
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
265
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws),
|
|
266
|
+
...(annotation || note ? { resolved: [{ input: request, matched: found.name, id: found.id, note: [annotation, note].filter(Boolean).join("; ") }] } : {}),
|
|
267
|
+
});
|
|
182
268
|
}));
|
|
183
269
|
server.registerTool("move_requests", {
|
|
184
270
|
title: "Move requests",
|
|
185
|
-
description: "Move up to 200 requests (ids or unique names) into a collection and optional folder.",
|
|
271
|
+
description: "Move up to 200 requests (ids or unique names) into a collection and optional folder of the same workspace (the target collection's).",
|
|
186
272
|
inputSchema: {
|
|
187
273
|
workspace: shared_js_1.workspaceArg,
|
|
188
274
|
requests: zod_1.z.array(zod_1.z.string().min(1).max(500)).min(1).max(MOVE_MAX),
|
|
@@ -192,8 +278,8 @@ const registerRequestTools = (server, ctx) => {
|
|
|
192
278
|
},
|
|
193
279
|
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
194
280
|
}, (0, shared_js_1.guarded)(async ({ workspace, requests, targetCollection, targetFolder, sourceCollection }) => {
|
|
195
|
-
|
|
196
|
-
const target = await (0, lookups_js_1.
|
|
281
|
+
// Requests move within one workspace — the target collection's.
|
|
282
|
+
const { ws, value: target } = await (0, lookups_js_1.findCollection)(ctx, workspace, targetCollection);
|
|
197
283
|
const folder = await (0, lookups_js_1.resolveFolder)(ctx, ws.id, target.item.id, targetFolder);
|
|
198
284
|
const moved = [];
|
|
199
285
|
const failed = [];
|
|
@@ -206,7 +292,7 @@ const registerRequestTools = (server, ctx) => {
|
|
|
206
292
|
moved.push({ id: updated.id, name: updated.name });
|
|
207
293
|
}
|
|
208
294
|
catch (error) {
|
|
209
|
-
failed.push({ request: ref, error:
|
|
295
|
+
failed.push({ request: ref, error: (0, shared_js_1.failureOf)(error) });
|
|
210
296
|
}
|
|
211
297
|
}
|
|
212
298
|
for (const collectionId of touchedCollections)
|
|
@@ -216,7 +302,7 @@ const registerRequestTools = (server, ctx) => {
|
|
|
216
302
|
}));
|
|
217
303
|
server.registerTool("delete_requests", {
|
|
218
304
|
title: "Delete requests",
|
|
219
|
-
description: "Delete up to 200 requests (ids or
|
|
305
|
+
description: "Delete up to 200 requests (ids or EXACT names — partial names are not accepted here). Permanent — confirm with the user first.",
|
|
220
306
|
inputSchema: {
|
|
221
307
|
workspace: shared_js_1.workspaceArg,
|
|
222
308
|
requests: zod_1.z.array(zod_1.z.string().min(1).max(500)).min(1).max(MOVE_MAX),
|
|
@@ -226,24 +312,26 @@ const registerRequestTools = (server, ctx) => {
|
|
|
226
312
|
annotations: shared_js_1.DESTRUCTIVE,
|
|
227
313
|
}, (0, shared_js_1.guarded)(async ({ workspace, requests, collection, confirm }) => {
|
|
228
314
|
(0, shared_js_1.requireConfirm)(confirm, "requests");
|
|
229
|
-
const ws = await ctx.resolveWorkspace(workspace);
|
|
230
315
|
const deleted = [];
|
|
231
316
|
const failed = [];
|
|
232
317
|
const touched = new Set();
|
|
233
318
|
for (const ref of requests) {
|
|
234
319
|
try {
|
|
235
|
-
const {
|
|
320
|
+
const { ws, value } = await (0, lookups_js_1.findRequest)(ctx, workspace, ref, collection, { exact: true });
|
|
321
|
+
const { request } = value;
|
|
236
322
|
await ctx.api.deleteRequest(ws.id, request.id);
|
|
237
|
-
touched.add(request.collectionId);
|
|
238
|
-
deleted.push({ id: request.id, name: request.name });
|
|
323
|
+
touched.add(`${ws.id}:${request.collectionId}`);
|
|
324
|
+
deleted.push({ id: request.id, name: request.name, workspace: (0, lookups_js_1.workspaceTag)(ws) });
|
|
239
325
|
}
|
|
240
326
|
catch (error) {
|
|
241
|
-
failed.push({ request: ref, error:
|
|
327
|
+
failed.push({ request: ref, error: (0, shared_js_1.failureOf)(error) });
|
|
242
328
|
}
|
|
243
329
|
}
|
|
244
|
-
for (const
|
|
245
|
-
|
|
246
|
-
|
|
330
|
+
for (const key of touched) {
|
|
331
|
+
const [wsId, collectionId] = key.split(":");
|
|
332
|
+
ctx.invalidate(`tree:${wsId}:${collectionId}`);
|
|
333
|
+
ctx.invalidate(`collections:${wsId}`);
|
|
334
|
+
}
|
|
247
335
|
return (0, shared_js_1.ok)({ deleted, failed });
|
|
248
336
|
}));
|
|
249
337
|
};
|