callman-mcp 0.1.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 +28 -0
- package/LICENSE +21 -0
- package/README.md +203 -0
- package/dist/client/api.d.ts +109 -0
- package/dist/client/api.d.ts.map +1 -0
- package/dist/client/api.js +180 -0
- package/dist/client/api.js.map +1 -0
- package/dist/client/errors.d.ts +27 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/errors.js +98 -0
- package/dist/client/errors.js.map +1 -0
- package/dist/client/http.d.ts +34 -0
- package/dist/client/http.d.ts.map +1 -0
- package/dist/client/http.js +121 -0
- package/dist/client/http.js.map +1 -0
- package/dist/client/types.d.ts +327 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +5 -0
- package/dist/client/types.js.map +1 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +69 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +35 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +91 -0
- package/dist/context.js.map +1 -0
- package/dist/domain/diagram/build.d.ts +260 -0
- package/dist/domain/diagram/build.d.ts.map +1 -0
- package/dist/domain/diagram/build.js +160 -0
- package/dist/domain/diagram/build.js.map +1 -0
- package/dist/domain/diagram/layout.d.ts +17 -0
- package/dist/domain/diagram/layout.d.ts.map +1 -0
- package/dist/domain/diagram/layout.js +61 -0
- package/dist/domain/diagram/layout.js.map +1 -0
- package/dist/domain/diagram/patch.d.ts +330 -0
- package/dist/domain/diagram/patch.d.ts.map +1 -0
- package/dist/domain/diagram/patch.js +176 -0
- package/dist/domain/diagram/patch.js.map +1 -0
- package/dist/domain/diagram/summarize.d.ts +41 -0
- package/dist/domain/diagram/summarize.d.ts.map +1 -0
- package/dist/domain/diagram/summarize.js +47 -0
- package/dist/domain/diagram/summarize.js.map +1 -0
- package/dist/domain/importDetect.d.ts +6 -0
- package/dist/domain/importDetect.d.ts.map +1 -0
- package/dist/domain/importDetect.js +32 -0
- package/dist/domain/importDetect.js.map +1 -0
- package/dist/domain/requestShape.d.ts +796 -0
- package/dist/domain/requestShape.d.ts.map +1 -0
- package/dist/domain/requestShape.js +250 -0
- package/dist/domain/requestShape.js.map +1 -0
- package/dist/domain/resolve.d.ts +30 -0
- package/dist/domain/resolve.d.ts.map +1 -0
- package/dist/domain/resolve.js +56 -0
- package/dist/domain/resolve.js.map +1 -0
- package/dist/domain/scenario/build.d.ts +270 -0
- package/dist/domain/scenario/build.d.ts.map +1 -0
- package/dist/domain/scenario/build.js +135 -0
- package/dist/domain/scenario/build.js.map +1 -0
- package/dist/domain/scenario/summarize.d.ts +206 -0
- package/dist/domain/scenario/summarize.d.ts.map +1 -0
- package/dist/domain/scenario/summarize.js +138 -0
- package/dist/domain/scenario/summarize.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +142 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cache.d.ts +12 -0
- package/dist/lib/cache.d.ts.map +1 -0
- package/dist/lib/cache.js +40 -0
- package/dist/lib/cache.js.map +1 -0
- package/dist/lib/ids.d.ts +5 -0
- package/dist/lib/ids.d.ts.map +1 -0
- package/dist/lib/ids.js +19 -0
- package/dist/lib/ids.js.map +1 -0
- package/dist/lib/log.d.ts +10 -0
- package/dist/lib/log.d.ts.map +1 -0
- package/dist/lib/log.js +25 -0
- package/dist/lib/log.js.map +1 -0
- package/dist/lib/mask.d.ts +24 -0
- package/dist/lib/mask.d.ts.map +1 -0
- package/dist/lib/mask.js +63 -0
- package/dist/lib/mask.js.map +1 -0
- package/dist/lib/pagination.d.ts +21 -0
- package/dist/lib/pagination.d.ts.map +1 -0
- package/dist/lib/pagination.js +40 -0
- package/dist/lib/pagination.js.map +1 -0
- package/dist/lib/truncate.d.ts +25 -0
- package/dist/lib/truncate.d.ts.map +1 -0
- package/dist/lib/truncate.js +45 -0
- package/dist/lib/truncate.js.map +1 -0
- package/dist/main.d.ts +9 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +12 -0
- package/dist/main.js.map +1 -0
- package/dist/prompts/index.d.ts +4 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +72 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/resources/docs/diagramSchema.d.ts +2 -0
- package/dist/resources/docs/diagramSchema.d.ts.map +1 -0
- package/dist/resources/docs/diagramSchema.js +65 -0
- package/dist/resources/docs/diagramSchema.js.map +1 -0
- package/dist/resources/docs/overview.d.ts +2 -0
- package/dist/resources/docs/overview.d.ts.map +1 -0
- package/dist/resources/docs/overview.js +46 -0
- package/dist/resources/docs/overview.js.map +1 -0
- package/dist/resources/docs/requestSchema.d.ts +2 -0
- package/dist/resources/docs/requestSchema.d.ts.map +1 -0
- package/dist/resources/docs/requestSchema.js +70 -0
- package/dist/resources/docs/requestSchema.js.map +1 -0
- package/dist/resources/generated/aiPacks.d.ts +3 -0
- package/dist/resources/generated/aiPacks.d.ts.map +1 -0
- package/dist/resources/generated/aiPacks.js +11 -0
- package/dist/resources/generated/aiPacks.js.map +1 -0
- package/dist/resources/index.d.ts +4 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +114 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/run/executeCollection.d.ts +19 -0
- package/dist/run/executeCollection.d.ts.map +1 -0
- package/dist/run/executeCollection.js +89 -0
- package/dist/run/executeCollection.js.map +1 -0
- package/dist/run/loadBundle.d.ts +9 -0
- package/dist/run/loadBundle.d.ts.map +1 -0
- package/dist/run/loadBundle.js +242 -0
- package/dist/run/loadBundle.js.map +1 -0
- package/dist/run/resolveTemplate.d.ts +2 -0
- package/dist/run/resolveTemplate.d.ts.map +1 -0
- package/dist/run/resolveTemplate.js +12 -0
- package/dist/run/resolveTemplate.js.map +1 -0
- package/dist/run/summarize.d.ts +73 -0
- package/dist/run/summarize.d.ts.map +1 -0
- package/dist/run/summarize.js +69 -0
- package/dist/run/summarize.js.map +1 -0
- package/dist/run/types.d.ts +46 -0
- package/dist/run/types.d.ts.map +1 -0
- package/dist/run/types.js +2 -0
- package/dist/run/types.js.map +1 -0
- package/dist/server.d.ts +23 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +25 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/catalog.d.ts +18 -0
- package/dist/tools/catalog.d.ts.map +1 -0
- package/dist/tools/catalog.js +66 -0
- package/dist/tools/catalog.js.map +1 -0
- package/dist/tools/collections.d.ts +4 -0
- package/dist/tools/collections.d.ts.map +1 -0
- package/dist/tools/collections.js +201 -0
- package/dist/tools/collections.js.map +1 -0
- package/dist/tools/diagrams.d.ts +4 -0
- package/dist/tools/diagrams.d.ts.map +1 -0
- package/dist/tools/diagrams.js +226 -0
- package/dist/tools/diagrams.js.map +1 -0
- package/dist/tools/environments.d.ts +4 -0
- package/dist/tools/environments.d.ts.map +1 -0
- package/dist/tools/environments.js +150 -0
- package/dist/tools/environments.js.map +1 -0
- package/dist/tools/import.d.ts +6 -0
- package/dist/tools/import.d.ts.map +1 -0
- package/dist/tools/import.js +144 -0
- package/dist/tools/import.js.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +26 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/lookups.d.ts +33 -0
- package/dist/tools/lookups.d.ts.map +1 -0
- package/dist/tools/lookups.js +132 -0
- package/dist/tools/lookups.js.map +1 -0
- package/dist/tools/meta.d.ts +4 -0
- package/dist/tools/meta.d.ts.map +1 -0
- package/dist/tools/meta.js +78 -0
- package/dist/tools/meta.js.map +1 -0
- package/dist/tools/requests.d.ts +4 -0
- package/dist/tools/requests.d.ts.map +1 -0
- package/dist/tools/requests.js +247 -0
- package/dist/tools/requests.js.map +1 -0
- package/dist/tools/run.d.ts +4 -0
- package/dist/tools/run.d.ts.map +1 -0
- package/dist/tools/run.js +167 -0
- package/dist/tools/run.js.map +1 -0
- package/dist/tools/scenarios.d.ts +4 -0
- package/dist/tools/scenarios.d.ts.map +1 -0
- package/dist/tools/scenarios.js +242 -0
- package/dist/tools/scenarios.js.map +1 -0
- package/dist/tools/shared.d.ts +20 -0
- package/dist/tools/shared.d.ts.map +1 -0
- package/dist/tools/shared.js +74 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/workspaces.d.ts +4 -0
- package/dist/tools/workspaces.d.ts.map +1 -0
- package/dist/tools/workspaces.js +80 -0
- package/dist/tools/workspaces.js.map +1 -0
- package/dist-cjs/client/api.js +183 -0
- package/dist-cjs/client/errors.js +104 -0
- package/dist-cjs/client/http.js +124 -0
- package/dist-cjs/client/types.js +5 -0
- package/dist-cjs/config.js +73 -0
- package/dist-cjs/context.js +94 -0
- package/dist-cjs/domain/diagram/build.js +164 -0
- package/dist-cjs/domain/diagram/layout.js +98 -0
- package/dist-cjs/domain/diagram/patch.js +179 -0
- package/dist-cjs/domain/diagram/summarize.js +52 -0
- package/dist-cjs/domain/importDetect.js +40 -0
- package/dist-cjs/domain/requestShape.js +260 -0
- package/dist-cjs/domain/resolve.js +63 -0
- package/dist-cjs/domain/scenario/build.js +140 -0
- package/dist-cjs/domain/scenario/summarize.js +144 -0
- package/dist-cjs/lib/cache.js +43 -0
- package/dist-cjs/lib/ids.js +24 -0
- package/dist-cjs/lib/log.js +28 -0
- package/dist-cjs/lib/mask.js +72 -0
- package/dist-cjs/lib/pagination.js +48 -0
- package/dist-cjs/lib/truncate.js +52 -0
- package/dist-cjs/main.js +31 -0
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/prompts/index.js +75 -0
- package/dist-cjs/resources/docs/diagramSchema.js +67 -0
- package/dist-cjs/resources/docs/overview.js +48 -0
- package/dist-cjs/resources/docs/requestSchema.js +72 -0
- package/dist-cjs/resources/generated/aiPacks.js +13 -0
- package/dist-cjs/resources/index.js +117 -0
- package/dist-cjs/run/executeCollection.js +93 -0
- package/dist-cjs/run/loadBundle.js +246 -0
- package/dist-cjs/run/resolveTemplate.js +15 -0
- package/dist-cjs/run/summarize.js +73 -0
- package/dist-cjs/run/types.js +2 -0
- package/dist-cjs/server.js +28 -0
- package/dist-cjs/tools/catalog.js +69 -0
- package/dist-cjs/tools/collections.js +204 -0
- package/dist-cjs/tools/diagrams.js +229 -0
- package/dist-cjs/tools/environments.js +153 -0
- package/dist-cjs/tools/import.js +147 -0
- package/dist-cjs/tools/index.js +31 -0
- package/dist-cjs/tools/lookups.js +149 -0
- package/dist-cjs/tools/meta.js +81 -0
- package/dist-cjs/tools/requests.js +250 -0
- package/dist-cjs/tools/run.js +203 -0
- package/dist-cjs/tools/scenarios.js +245 -0
- package/dist-cjs/tools/shared.js +81 -0
- package/dist-cjs/tools/workspaces.js +83 -0
- package/package.json +80 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerRequestTools = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const errors_js_1 = require("../client/errors.js");
|
|
6
|
+
const requestShape_js_1 = require("../domain/requestShape.js");
|
|
7
|
+
const resolve_js_1 = require("../domain/resolve.js");
|
|
8
|
+
const truncate_js_1 = require("../lib/truncate.js");
|
|
9
|
+
const lookups_js_1 = require("./lookups.js");
|
|
10
|
+
const shared_js_1 = require("./shared.js");
|
|
11
|
+
const BULK_MAX = 5000;
|
|
12
|
+
const MOVE_MAX = 200;
|
|
13
|
+
const registerRequestTools = (server, ctx) => {
|
|
14
|
+
server.registerTool("search_requests", {
|
|
15
|
+
title: "Search requests",
|
|
16
|
+
description: "Find saved requests by words in their name, method or URL (all words must match), across the workspace or inside one collection. Returns up to 50 matches with ids you can pass to get_request / run_request.",
|
|
17
|
+
inputSchema: {
|
|
18
|
+
workspace: shared_js_1.workspaceArg,
|
|
19
|
+
query: zod_1.z.string().min(1).max(200).describe("e.g. \"POST login\", \"users\", \"GET /orders\""),
|
|
20
|
+
collection: zod_1.z.string().max(200).optional(),
|
|
21
|
+
limit: zod_1.z.number().int().min(1).max(truncate_js_1.LIMITS.SEARCH_RESULTS).optional(),
|
|
22
|
+
},
|
|
23
|
+
annotations: shared_js_1.READ,
|
|
24
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, query, collection, limit }) => {
|
|
25
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
26
|
+
const scope = collection ? await (0, lookups_js_1.resolveCollection)(ctx, ws.id, collection) : null;
|
|
27
|
+
const matches = await (0, lookups_js_1.searchRequests)(ctx, ws.id, query, scope?.item.id ?? null, limit ?? truncate_js_1.LIMITS.SEARCH_RESULTS);
|
|
28
|
+
return (0, shared_js_1.ok)({
|
|
29
|
+
query,
|
|
30
|
+
...(scope ? { collection: { id: scope.item.id, name: scope.item.name } } : {}),
|
|
31
|
+
matches: matches.map((request) => ({ ...(0, requestShape_js_1.requestSummaryView)(request), collection: request.collectionName })),
|
|
32
|
+
...(matches.length >= (limit ?? truncate_js_1.LIMITS.SEARCH_RESULTS) ? { truncated: true } : {}),
|
|
33
|
+
});
|
|
34
|
+
}));
|
|
35
|
+
server.registerTool("get_request", {
|
|
36
|
+
title: "Get a request",
|
|
37
|
+
description: "Read one saved request: url, headers, query, body, auth (secrets masked unless reveal:true and the token holds requests:read + the request's own permissions), scripts. `full: true` returns untruncated body and scripts.",
|
|
38
|
+
inputSchema: {
|
|
39
|
+
workspace: shared_js_1.workspaceArg,
|
|
40
|
+
request: (0, shared_js_1.refArg)("Request", "(name, \"METHOD url\" or id)"),
|
|
41
|
+
collection: zod_1.z.string().max(200).optional().describe("Narrow a name lookup to one collection"),
|
|
42
|
+
full: zod_1.z.boolean().optional(),
|
|
43
|
+
reveal: zod_1.z.boolean().optional().describe("Return auth secrets and sensitive header values unmasked"),
|
|
44
|
+
},
|
|
45
|
+
annotations: shared_js_1.READ,
|
|
46
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, request, collection, full, reveal }) => {
|
|
47
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
48
|
+
const { request: found, annotation } = await (0, lookups_js_1.resolveRequest)(ctx, ws.id, request, collection);
|
|
49
|
+
return (0, shared_js_1.ok)({ request: (0, requestShape_js_1.compactRequestView)(found, { full, reveal }), ...(annotation ? { resolved: [{ input: request, matched: found.name, id: found.id, note: annotation }] } : {}) });
|
|
50
|
+
}));
|
|
51
|
+
server.registerTool("create_request", {
|
|
52
|
+
title: "Create a request",
|
|
53
|
+
description: "Create one saved request in a collection (optionally inside a folder by id, name or path). Body may be a JSON value, a raw string, or { mode: raw|formdata|urlencoded }. Use {{variable}} for hosts and secrets and put the values in an environment. For many requests use create_requests_bulk (one call, atomic).",
|
|
54
|
+
inputSchema: { workspace: shared_js_1.workspaceArg, collection: (0, shared_js_1.refArg)("Collection"), ...requestShape_js_1.requestInputSchema.omit({ folderKey: true }).shape },
|
|
55
|
+
annotations: shared_js_1.WRITE,
|
|
56
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, collection, ...item }) => {
|
|
57
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
58
|
+
const resolved = await (0, lookups_js_1.resolveCollection)(ctx, ws.id, collection);
|
|
59
|
+
const folder = await (0, lookups_js_1.resolveFolder)(ctx, ws.id, resolved.item.id, item.folder);
|
|
60
|
+
const created = await ctx.api.createRequest(ws.id, (0, requestShape_js_1.toCreateRequestPayload)(item, { collectionId: resolved.item.id, folderId: folder?.item.id ?? null }));
|
|
61
|
+
ctx.invalidate(`tree:${ws.id}:${resolved.item.id}`);
|
|
62
|
+
ctx.invalidate(`collections:${ws.id}`);
|
|
63
|
+
return (0, shared_js_1.ok)({
|
|
64
|
+
request: (0, requestShape_js_1.requestSummaryView)(created),
|
|
65
|
+
collection: { id: resolved.item.id, name: resolved.item.name },
|
|
66
|
+
...(folder ? { folder: { id: folder.item.id, path: folder.item.path } } : {}),
|
|
67
|
+
resolved: [(0, resolve_js_1.resolvedSummary)(collection, resolved), ...(folder && item.folder ? [(0, resolve_js_1.resolvedSummary)(item.folder, folder)] : [])],
|
|
68
|
+
});
|
|
69
|
+
}));
|
|
70
|
+
server.registerTool("create_requests_bulk", {
|
|
71
|
+
title: "Create many requests",
|
|
72
|
+
description: "Create many requests at once. Either `collectionName` (creates a NEW collection with an optional folder skeleton — atomic: all or nothing) or `collection` (adds to an EXISTING one; each request may name a `folder`). Requests reference folders by `folderKey` (new collection) or `folder` (existing). Use dryRun: true first for large sets.",
|
|
73
|
+
inputSchema: {
|
|
74
|
+
workspace: shared_js_1.workspaceArg,
|
|
75
|
+
collectionName: zod_1.z.string().min(1).max(truncate_js_1.LIMITS.NAME).optional().describe("Create a new collection with this name"),
|
|
76
|
+
collection: zod_1.z.string().max(200).optional().describe("Add to this existing collection (id or name)"),
|
|
77
|
+
folders: zod_1.z
|
|
78
|
+
.array(zod_1.z.object({ key: zod_1.z.string().min(1).max(120), name: zod_1.z.string().min(1).max(truncate_js_1.LIMITS.NAME), parentKey: zod_1.z.string().max(120).optional() }))
|
|
79
|
+
.max(200)
|
|
80
|
+
.optional()
|
|
81
|
+
.describe("New-collection mode only: folder skeleton; requests use folderKey"),
|
|
82
|
+
requests: zod_1.z.array(requestShape_js_1.requestInputSchema).min(1).max(BULK_MAX),
|
|
83
|
+
dryRun: shared_js_1.dryRunArg,
|
|
84
|
+
},
|
|
85
|
+
annotations: shared_js_1.WRITE,
|
|
86
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, collectionName, collection, folders, requests, dryRun }) => {
|
|
87
|
+
if ((collectionName ? 1 : 0) + (collection ? 1 : 0) !== 1) {
|
|
88
|
+
throw new errors_js_1.ToolInputError("TARGET_REQUIRED", "Pass exactly one of collectionName (new collection) or collection (existing).");
|
|
89
|
+
}
|
|
90
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
91
|
+
const folderKeys = new Set((folders ?? []).map((folder) => folder.key));
|
|
92
|
+
for (const [index, request] of requests.entries()) {
|
|
93
|
+
if (request.folderKey && !folderKeys.has(request.folderKey)) {
|
|
94
|
+
throw new errors_js_1.ToolInputError("FOLDER_KEY_UNKNOWN", `requests[${index}] references folderKey "${request.folderKey}" which is not declared in folders.`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (collectionName) {
|
|
98
|
+
const existing = (await (0, lookups_js_1.loadCollections)(ctx, ws.id)).find((entry) => (0, lookups_js_1.sameName)(entry.name, collectionName));
|
|
99
|
+
if (existing) {
|
|
100
|
+
throw new errors_js_1.ToolInputError("COLLECTION_NAME_EXISTS", `A collection named "${existing.name}" already exists (id ${existing.id}). Use collection: "${existing.id}" to add to it, or pick another name.`, {
|
|
101
|
+
existing: { id: existing.id, name: existing.name },
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const payload = {
|
|
105
|
+
collectionName: collectionName.trim(),
|
|
106
|
+
...(folders && folders.length > 0 ? { folders } : {}),
|
|
107
|
+
requests: requests.map(requestShape_js_1.toFlatImportRequest),
|
|
108
|
+
};
|
|
109
|
+
if (dryRun) {
|
|
110
|
+
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
|
+
}
|
|
112
|
+
const result = await ctx.api.importJson(ws.id, { format: "requests", payload });
|
|
113
|
+
ctx.invalidate(`collections:${ws.id}`);
|
|
114
|
+
return (0, shared_js_1.ok)({
|
|
115
|
+
collection: { id: result.collection.id, name: result.collection.name },
|
|
116
|
+
created: result.importedCounts,
|
|
117
|
+
note: "Created atomically. Open the collection in the desktop app (it refreshes when it regains focus).",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const resolved = await (0, lookups_js_1.resolveCollection)(ctx, ws.id, collection);
|
|
121
|
+
const tree = await (0, lookups_js_1.loadTree)(ctx, ws.id, resolved.item.id);
|
|
122
|
+
// Resolve every folder reference up front so a typo fails before any write.
|
|
123
|
+
const folderIdByRef = new Map();
|
|
124
|
+
for (const request of requests) {
|
|
125
|
+
const ref = request.folder ?? "";
|
|
126
|
+
if (!folderIdByRef.has(ref)) {
|
|
127
|
+
folderIdByRef.set(ref, ref ? (await (0, lookups_js_1.resolveFolder)(ctx, ws.id, resolved.item.id, ref))?.item.id ?? null : null);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const duplicates = requests.filter((request) => tree.requests.some((existing) => (0, lookups_js_1.sameName)(existing.name, request.name) && existing.folderId === folderIdByRef.get(request.folder ?? "")));
|
|
131
|
+
if (dryRun) {
|
|
132
|
+
return (0, shared_js_1.ok)({
|
|
133
|
+
dryRun: true,
|
|
134
|
+
mode: "existing-collection",
|
|
135
|
+
collection: { id: resolved.item.id, name: resolved.item.name },
|
|
136
|
+
requestCount: requests.length,
|
|
137
|
+
duplicateNames: duplicates.map((request) => request.name),
|
|
138
|
+
sample: requests.slice(0, 5).map((request) => `${request.method} ${request.name}`),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const created = [];
|
|
142
|
+
const failed = [];
|
|
143
|
+
for (const request of requests) {
|
|
144
|
+
try {
|
|
145
|
+
const row = await ctx.api.createRequest(ws.id, (0, requestShape_js_1.toCreateRequestPayload)(request, { collectionId: resolved.item.id, folderId: folderIdByRef.get(request.folder ?? "") ?? null }));
|
|
146
|
+
created.push({ id: row.id, name: row.name, method: row.method });
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
failed.push({ name: request.name, error: error instanceof errors_js_1.CallmanApiError ? `${error.code}: ${error.message}` : error instanceof Error ? error.message : String(error) });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
ctx.invalidate(`tree:${ws.id}:${resolved.item.id}`);
|
|
153
|
+
ctx.invalidate(`collections:${ws.id}`);
|
|
154
|
+
return (0, shared_js_1.ok)({
|
|
155
|
+
collection: { id: resolved.item.id, name: resolved.item.name },
|
|
156
|
+
created: created.length,
|
|
157
|
+
failed,
|
|
158
|
+
requests: created,
|
|
159
|
+
...(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
|
+
resolved: [(0, resolve_js_1.resolvedSummary)(collection, resolved)],
|
|
161
|
+
});
|
|
162
|
+
}));
|
|
163
|
+
server.registerTool("update_request", {
|
|
164
|
+
title: "Update a request",
|
|
165
|
+
description: "Change fields of a saved request (only the fields you pass change). To move it, use move_requests. Sending `headers`/`query` replaces the whole list.",
|
|
166
|
+
inputSchema: {
|
|
167
|
+
workspace: shared_js_1.workspaceArg,
|
|
168
|
+
request: (0, shared_js_1.refArg)("Request", "(name, \"METHOD url\" or id)"),
|
|
169
|
+
...requestShape_js_1.updateRequestInputSchema.shape,
|
|
170
|
+
},
|
|
171
|
+
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
172
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, request, collection, ...changes }) => {
|
|
173
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
174
|
+
const { request: found, annotation } = await (0, lookups_js_1.resolveRequest)(ctx, ws.id, request, collection);
|
|
175
|
+
const folder = changes.folder !== undefined ? await (0, lookups_js_1.resolveFolder)(ctx, ws.id, found.collectionId, changes.folder) : undefined;
|
|
176
|
+
const payload = (0, requestShape_js_1.toUpdateRequestPayload)(changes, { ...(folder !== undefined ? { folderId: folder?.item.id ?? null } : {}) });
|
|
177
|
+
if (Object.keys(payload).length === 0)
|
|
178
|
+
throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass at least one field to change.");
|
|
179
|
+
const updated = await ctx.api.updateRequest(ws.id, found.id, payload);
|
|
180
|
+
ctx.invalidate(`tree:${ws.id}:${found.collectionId}`);
|
|
181
|
+
return (0, shared_js_1.ok)({ request: (0, requestShape_js_1.compactRequestView)(updated), changed: Object.keys(payload), ...(annotation ? { resolved: [{ input: request, matched: found.name, id: found.id, note: annotation }] } : {}) });
|
|
182
|
+
}));
|
|
183
|
+
server.registerTool("move_requests", {
|
|
184
|
+
title: "Move requests",
|
|
185
|
+
description: "Move up to 200 requests (ids or unique names) into a collection and optional folder.",
|
|
186
|
+
inputSchema: {
|
|
187
|
+
workspace: shared_js_1.workspaceArg,
|
|
188
|
+
requests: zod_1.z.array(zod_1.z.string().min(1).max(500)).min(1).max(MOVE_MAX),
|
|
189
|
+
targetCollection: (0, shared_js_1.refArg)("Target collection"),
|
|
190
|
+
targetFolder: zod_1.z.string().max(300).optional().describe("Folder id, name or path in the target collection; omit for top level"),
|
|
191
|
+
sourceCollection: zod_1.z.string().max(200).optional().describe("Narrow name lookups to one collection"),
|
|
192
|
+
},
|
|
193
|
+
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
194
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, requests, targetCollection, targetFolder, sourceCollection }) => {
|
|
195
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
196
|
+
const target = await (0, lookups_js_1.resolveCollection)(ctx, ws.id, targetCollection);
|
|
197
|
+
const folder = await (0, lookups_js_1.resolveFolder)(ctx, ws.id, target.item.id, targetFolder);
|
|
198
|
+
const moved = [];
|
|
199
|
+
const failed = [];
|
|
200
|
+
const touchedCollections = new Set([target.item.id]);
|
|
201
|
+
for (const ref of requests) {
|
|
202
|
+
try {
|
|
203
|
+
const { request } = await (0, lookups_js_1.resolveRequest)(ctx, ws.id, ref, sourceCollection);
|
|
204
|
+
touchedCollections.add(request.collectionId);
|
|
205
|
+
const updated = await ctx.api.updateRequest(ws.id, request.id, { collectionId: target.item.id, folderId: folder?.item.id ?? null });
|
|
206
|
+
moved.push({ id: updated.id, name: updated.name });
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
failed.push({ request: ref, error: error instanceof Error ? error.message : String(error) });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
for (const collectionId of touchedCollections)
|
|
213
|
+
ctx.invalidate(`tree:${ws.id}:${collectionId}`);
|
|
214
|
+
ctx.invalidate(`collections:${ws.id}`);
|
|
215
|
+
return (0, shared_js_1.ok)({ target: { collection: { id: target.item.id, name: target.item.name }, folder: folder ? { id: folder.item.id, path: folder.item.path } : null }, moved, failed });
|
|
216
|
+
}));
|
|
217
|
+
server.registerTool("delete_requests", {
|
|
218
|
+
title: "Delete requests",
|
|
219
|
+
description: "Delete up to 200 requests (ids or unique names). Permanent — confirm with the user first.",
|
|
220
|
+
inputSchema: {
|
|
221
|
+
workspace: shared_js_1.workspaceArg,
|
|
222
|
+
requests: zod_1.z.array(zod_1.z.string().min(1).max(500)).min(1).max(MOVE_MAX),
|
|
223
|
+
collection: zod_1.z.string().max(200).optional().describe("Narrow name lookups to one collection"),
|
|
224
|
+
confirm: shared_js_1.confirmArg,
|
|
225
|
+
},
|
|
226
|
+
annotations: shared_js_1.DESTRUCTIVE,
|
|
227
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, requests, collection, confirm }) => {
|
|
228
|
+
(0, shared_js_1.requireConfirm)(confirm, "requests");
|
|
229
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
230
|
+
const deleted = [];
|
|
231
|
+
const failed = [];
|
|
232
|
+
const touched = new Set();
|
|
233
|
+
for (const ref of requests) {
|
|
234
|
+
try {
|
|
235
|
+
const { request } = await (0, lookups_js_1.resolveRequest)(ctx, ws.id, ref, collection);
|
|
236
|
+
await ctx.api.deleteRequest(ws.id, request.id);
|
|
237
|
+
touched.add(request.collectionId);
|
|
238
|
+
deleted.push({ id: request.id, name: request.name });
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
failed.push({ request: ref, error: error instanceof Error ? error.message : String(error) });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
for (const collectionId of touched)
|
|
245
|
+
ctx.invalidate(`tree:${ws.id}:${collectionId}`);
|
|
246
|
+
ctx.invalidate(`collections:${ws.id}`);
|
|
247
|
+
return (0, shared_js_1.ok)({ deleted, failed });
|
|
248
|
+
}));
|
|
249
|
+
};
|
|
250
|
+
exports.registerRequestTools = registerRequestTools;
|
|
@@ -0,0 +1,203 @@
|
|
|
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.registerRunTools = void 0;
|
|
37
|
+
const zod_1 = require("zod");
|
|
38
|
+
const errors_js_1 = require("../client/errors.js");
|
|
39
|
+
const resolve_js_1 = require("../domain/resolve.js");
|
|
40
|
+
const mask_js_1 = require("../lib/mask.js");
|
|
41
|
+
const truncate_js_1 = require("../lib/truncate.js");
|
|
42
|
+
const executeCollection_js_1 = require("../run/executeCollection.js");
|
|
43
|
+
const loadBundle_js_1 = require("../run/loadBundle.js");
|
|
44
|
+
const summarize_js_1 = require("../run/summarize.js");
|
|
45
|
+
const lookups_js_1 = require("./lookups.js");
|
|
46
|
+
const shared_js_1 = require("./shared.js");
|
|
47
|
+
// Local execution — the same path as callme-cli: the pipeline bundle
|
|
48
|
+
// (collection + environment values + connection configs) is fetched with the
|
|
49
|
+
// token, requests run HERE through callman-core, and only the results go back
|
|
50
|
+
// to the model. Variable values are never in the output. One run at a time.
|
|
51
|
+
const maxBodyArg = zod_1.z.number().int().min(256).max(truncate_js_1.LIMITS.RUN_BODY_MAX).optional().describe("Response body preview cap in bytes (default 16 KB)");
|
|
52
|
+
const prepareRun = async (ctx, ws, collectionId, environmentRef) => {
|
|
53
|
+
const environment = environmentRef ? (await (0, lookups_js_1.resolveEnvironment)(ctx, ws, environmentRef)).item : null;
|
|
54
|
+
const bundle = await ctx.api.pipelineBundle(ws, collectionId, environment?.id ?? null);
|
|
55
|
+
const warnings = [];
|
|
56
|
+
// Global variables apply underneath the environment. They come back masked
|
|
57
|
+
// unless the token can reveal them — a masked value would break requests,
|
|
58
|
+
// so they are only merged when the real values are available.
|
|
59
|
+
let globals = {};
|
|
60
|
+
const scopes = await ctx.getTokenScopes();
|
|
61
|
+
if (scopes.includes("environments:reveal")) {
|
|
62
|
+
try {
|
|
63
|
+
globals = Object.fromEntries((await ctx.api.listGlobalVariables(ws, true)).map((variable) => [variable.key, variable.value]));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
warnings.push(`Global variables not applied: ${error instanceof Error ? error.message : String(error)}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
warnings.push("Global variables were not applied (the token lacks environments:reveal). Environment variables are applied.");
|
|
71
|
+
}
|
|
72
|
+
const collection = (0, loadBundle_js_1.validateCollection)(bundle.collection);
|
|
73
|
+
const { map, skipped } = (0, loadBundle_js_1.buildConnectionRuntimeMap)(bundle.connections);
|
|
74
|
+
if (skipped.length > 0)
|
|
75
|
+
warnings.push(`Connections not usable in a local run: ${skipped.join(", ")}`);
|
|
76
|
+
if (collection.runnerSettings?.kafka)
|
|
77
|
+
warnings.push("Kafka steps never run from an AI tool.");
|
|
78
|
+
return {
|
|
79
|
+
collection,
|
|
80
|
+
env: { ...globals, ...(bundle.environment?.variables ?? {}) },
|
|
81
|
+
connections: map,
|
|
82
|
+
environment: bundle.environment ? { id: bundle.environment.id, name: bundle.environment.name } : null,
|
|
83
|
+
warnings,
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
const registerRunTools = (server, ctx) => {
|
|
87
|
+
server.registerTool("run_request", {
|
|
88
|
+
title: "Run a request locally",
|
|
89
|
+
description: "Execute one saved request from this machine with an environment's variables (like the desktop's Send / callme run): returns status, timing, response headers and a bounded body preview, plus test and contract results and the NAMES of variables the scripts changed (never values). dryRun: true shows the resolved request without sending it. Needs requests:run.",
|
|
90
|
+
inputSchema: {
|
|
91
|
+
workspace: shared_js_1.workspaceArg,
|
|
92
|
+
request: (0, shared_js_1.refArg)("Request", "(name, \"METHOD url\" or id)"),
|
|
93
|
+
collection: zod_1.z.string().max(200).optional().describe("Narrow a name lookup to one collection"),
|
|
94
|
+
environment: zod_1.z.string().max(200).optional().describe("Environment name or id; omit to run without one"),
|
|
95
|
+
dryRun: zod_1.z.boolean().optional(),
|
|
96
|
+
maxBodyBytes: maxBodyArg,
|
|
97
|
+
},
|
|
98
|
+
annotations: shared_js_1.RUN,
|
|
99
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, request, collection, environment, dryRun, maxBodyBytes }) => {
|
|
100
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
101
|
+
const { request: found, annotation } = await (0, lookups_js_1.resolveRequest)(ctx, ws.id, request, collection);
|
|
102
|
+
const prepared = await prepareRun(ctx, ws.id, found.collectionId, environment);
|
|
103
|
+
const runnable = prepared.collection.requests.find((entry) => entry.id === found.id);
|
|
104
|
+
if (!runnable)
|
|
105
|
+
throw new errors_js_1.ToolInputError("REQUEST_NOT_IN_BUNDLE", "The request is not part of the collection bundle (was it just deleted?).");
|
|
106
|
+
if (dryRun) {
|
|
107
|
+
const { resolveTemplate } = await Promise.resolve().then(() => __importStar(require("../run/resolveTemplate.js")));
|
|
108
|
+
const env = prepared.env;
|
|
109
|
+
return (0, shared_js_1.ok)({
|
|
110
|
+
dryRun: true,
|
|
111
|
+
request: {
|
|
112
|
+
id: runnable.id,
|
|
113
|
+
name: runnable.name,
|
|
114
|
+
method: runnable.method,
|
|
115
|
+
url: resolveTemplate(runnable.url, env),
|
|
116
|
+
headers: (0, mask_js_1.maskHeaderRecord)(Object.fromEntries(Object.entries(runnable.headers ?? {}).map(([key, value]) => [key, resolveTemplate(value, env)]))),
|
|
117
|
+
query: Object.fromEntries(Object.entries(runnable.query ?? {}).map(([key, value]) => [key, resolveTemplate(value, env)])),
|
|
118
|
+
body: runnable.body ?? null,
|
|
119
|
+
hasPreRequestScript: Boolean(runnable.preRequestScript),
|
|
120
|
+
hasTests: Boolean(runnable.tests),
|
|
121
|
+
},
|
|
122
|
+
environment: prepared.environment,
|
|
123
|
+
availableVariableKeys: Object.keys(env),
|
|
124
|
+
unresolvedVariables: unresolved([runnable.url, ...Object.values(runnable.headers ?? {}), ...Object.values(runnable.query ?? {})], env),
|
|
125
|
+
warnings: prepared.warnings,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const outcome = await ctx.withRunLock(() => (0, executeCollection_js_1.executeCollection)({
|
|
129
|
+
collection: prepared.collection,
|
|
130
|
+
env: prepared.env,
|
|
131
|
+
connections: prepared.connections,
|
|
132
|
+
only: [found.id],
|
|
133
|
+
deadlineMs: ctx.config.runTimeoutMs,
|
|
134
|
+
}));
|
|
135
|
+
const summary = (0, summarize_js_1.summarizeRun)(outcome, maxBodyBytes ?? ctx.config.maxBodyBytes, {
|
|
136
|
+
collection: { id: found.collectionId },
|
|
137
|
+
environment: prepared.environment,
|
|
138
|
+
warnings: prepared.warnings,
|
|
139
|
+
...(annotation ? { resolved: [{ input: request, matched: found.name, id: found.id, note: annotation }] } : {}),
|
|
140
|
+
});
|
|
141
|
+
return (0, shared_js_1.ok)({ ...summary, result: summary.results[0] ?? null });
|
|
142
|
+
}));
|
|
143
|
+
server.registerTool("run_collection", {
|
|
144
|
+
title: "Run a collection locally",
|
|
145
|
+
description: "Run a collection (or one folder of it, or specific requests) sequentially from this machine with an environment, sharing variables between requests like the collection runner. Returns per-request results (bounded bodies), a pass/fail summary and the variable names scripts changed. `bail` stops at the first failure. Needs requests:run.",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
workspace: shared_js_1.workspaceArg,
|
|
148
|
+
collection: (0, shared_js_1.refArg)("Collection"),
|
|
149
|
+
folder: zod_1.z.string().max(300).optional().describe("Run only this folder (id, name or path), subfolders included"),
|
|
150
|
+
only: zod_1.z.array(zod_1.z.string().min(1).max(500)).max(200).optional().describe("Run only these requests (ids or names)"),
|
|
151
|
+
environment: zod_1.z.string().max(200).optional(),
|
|
152
|
+
bail: zod_1.z.boolean().optional(),
|
|
153
|
+
maxBodyBytes: maxBodyArg,
|
|
154
|
+
},
|
|
155
|
+
annotations: shared_js_1.RUN,
|
|
156
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, collection, folder, only, environment, bail, maxBodyBytes }) => {
|
|
157
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
158
|
+
const resolved = await (0, lookups_js_1.resolveCollection)(ctx, ws.id, collection);
|
|
159
|
+
const prepared = await prepareRun(ctx, ws.id, resolved.item.id, environment);
|
|
160
|
+
let selection = null;
|
|
161
|
+
if (folder) {
|
|
162
|
+
const target = await (0, lookups_js_1.resolveFolder)(ctx, ws.id, resolved.item.id, folder);
|
|
163
|
+
if (!target)
|
|
164
|
+
throw new errors_js_1.ToolInputError("REF_REQUIRED", "folder is required.");
|
|
165
|
+
selection = (0, lookups_js_1.requestIdsInFolder)(await (0, lookups_js_1.loadTree)(ctx, ws.id, resolved.item.id), target.item.id);
|
|
166
|
+
}
|
|
167
|
+
if (only && only.length > 0) {
|
|
168
|
+
const ids = [];
|
|
169
|
+
for (const ref of only)
|
|
170
|
+
ids.push((await (0, lookups_js_1.resolveRequest)(ctx, ws.id, ref, resolved.item.id)).request.id);
|
|
171
|
+
selection = selection ? ids.filter((id) => selection?.includes(id)) : ids;
|
|
172
|
+
}
|
|
173
|
+
if (selection && selection.length === 0)
|
|
174
|
+
throw new errors_js_1.ToolInputError("NOTHING_TO_RUN", "The selection contains no requests.");
|
|
175
|
+
const outcome = await ctx.withRunLock(() => (0, executeCollection_js_1.executeCollection)({
|
|
176
|
+
collection: prepared.collection,
|
|
177
|
+
env: prepared.env,
|
|
178
|
+
connections: prepared.connections,
|
|
179
|
+
only: selection,
|
|
180
|
+
bail: bail ?? false,
|
|
181
|
+
deadlineMs: ctx.config.runTimeoutMs,
|
|
182
|
+
}));
|
|
183
|
+
return (0, shared_js_1.ok)((0, summarize_js_1.summarizeRun)(outcome, maxBodyBytes ?? ctx.config.maxBodyBytes, {
|
|
184
|
+
collection: { id: resolved.item.id, name: resolved.item.name },
|
|
185
|
+
environment: prepared.environment,
|
|
186
|
+
warnings: prepared.warnings,
|
|
187
|
+
resolved: [(0, resolve_js_1.resolvedSummary)(collection, resolved)],
|
|
188
|
+
}));
|
|
189
|
+
}));
|
|
190
|
+
};
|
|
191
|
+
exports.registerRunTools = registerRunTools;
|
|
192
|
+
const TEMPLATE = /\{\{\s*([^}]+?)\s*\}\}/g;
|
|
193
|
+
const unresolved = (values, env) => {
|
|
194
|
+
const missing = new Set();
|
|
195
|
+
for (const value of values) {
|
|
196
|
+
for (const match of value.matchAll(TEMPLATE)) {
|
|
197
|
+
const key = match[1].trim();
|
|
198
|
+
if (!(key in env) && !key.startsWith("$") && !key.startsWith("data."))
|
|
199
|
+
missing.add(key);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return [...missing];
|
|
203
|
+
};
|