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
|
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.registerEnvironmentTools = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const errors_js_1 = require("../client/errors.js");
|
|
6
|
-
const requestShape_js_1 = require("../domain/requestShape.js");
|
|
7
6
|
const resolve_js_1 = require("../domain/resolve.js");
|
|
8
7
|
const mask_js_1 = require("../lib/mask.js");
|
|
8
|
+
const strict_js_1 = require("../lib/strict.js");
|
|
9
9
|
const truncate_js_1 = require("../lib/truncate.js");
|
|
10
10
|
const lookups_js_1 = require("./lookups.js");
|
|
11
11
|
const shared_js_1 = require("./shared.js");
|
|
@@ -14,98 +14,163 @@ const shared_js_1 = require("./shared.js");
|
|
|
14
14
|
// environments:reveal AND the call asks for reveal — the server enforces
|
|
15
15
|
// both; this side only forwards the flag and refuses to write a masked
|
|
16
16
|
// value back.
|
|
17
|
+
// An environment variable is exactly { key, value } on the server — no
|
|
18
|
+
// enabled flag, no description (strict: sending them is an error, not a no-op).
|
|
19
|
+
const VARIABLES_MAX = 500;
|
|
20
|
+
const variableSchema = (0, strict_js_1.strictObject)({
|
|
21
|
+
key: zod_1.z.string().trim().min(1).max(100),
|
|
22
|
+
value: zod_1.z.string().max(5000).default(""),
|
|
23
|
+
}, "variable field");
|
|
24
|
+
const refuseMasked = (variables, action) => {
|
|
25
|
+
const maskedKeys = variables.filter((variable) => (0, mask_js_1.isMasked)(variable.value)).map((variable) => variable.key);
|
|
26
|
+
if (maskedKeys.length > 0) {
|
|
27
|
+
throw new errors_js_1.ToolInputError("MASKED_VALUE", `Masked values cannot be ${action} (keys: ${maskedKeys.join(", ")}). Read them with get_environment reveal: true (needs environments:reveal) or supply the real values.`, { keys: maskedKeys });
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const duplicateKeys = (variables) => {
|
|
31
|
+
const seen = new Set();
|
|
32
|
+
const duplicates = new Set();
|
|
33
|
+
for (const variable of variables)
|
|
34
|
+
(seen.has(variable.key) ? duplicates : seen).add(variable.key);
|
|
35
|
+
return [...duplicates];
|
|
36
|
+
};
|
|
37
|
+
// Global variables are optional (feature flag, scope) — say why they are
|
|
38
|
+
// missing instead of returning an empty list as if there were none.
|
|
39
|
+
const loadGlobals = async (ctx, ws, reveal) => {
|
|
40
|
+
try {
|
|
41
|
+
return { globals: await ctx.api.listGlobalVariables(ws, reveal) };
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return { globals: [], globalVariablesError: (0, shared_js_1.toolFailure)(error).structuredContent.error };
|
|
45
|
+
}
|
|
46
|
+
};
|
|
17
47
|
const registerEnvironmentTools = (server, ctx) => {
|
|
18
48
|
server.registerTool("list_environments", {
|
|
19
49
|
title: "List environments",
|
|
20
|
-
description: "List
|
|
50
|
+
description: "List environments with their variable KEYS (values are not included) — across ALL your workspaces unless `workspace` narrows it, each workspace with its global variables (shared by that workspace's environments).",
|
|
21
51
|
inputSchema: { workspace: shared_js_1.workspaceArg },
|
|
22
52
|
annotations: shared_js_1.READ,
|
|
23
53
|
}, (0, shared_js_1.guarded)(async ({ workspace }) => {
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
(0, lookups_js_1.loadEnvironments)(ctx, ws.id),
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
54
|
+
const workspaces = await (0, lookups_js_1.workspacesForListing)(ctx, workspace);
|
|
55
|
+
const perWorkspace = await Promise.all(workspaces.map(async (ws) => {
|
|
56
|
+
const [environments, globals] = await Promise.all([(0, lookups_js_1.loadEnvironments)(ctx, ws.id), loadGlobals(ctx, ws.id, false)]);
|
|
57
|
+
return {
|
|
58
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws),
|
|
59
|
+
environments: environments.map((environment) => ({
|
|
60
|
+
id: environment.id,
|
|
61
|
+
name: environment.name,
|
|
62
|
+
variableCount: environment.variables.length,
|
|
63
|
+
variableKeys: environment.variables.map((variable) => variable.key).slice(0, 200),
|
|
64
|
+
...(environment.variables.length > 200 ? { variableKeysTruncated: true } : {}),
|
|
65
|
+
})),
|
|
66
|
+
globalVariableKeys: globals.globals.map((variable) => variable.key),
|
|
67
|
+
...(globals.globalVariablesError ? { globalVariablesError: globals.globalVariablesError } : {}),
|
|
68
|
+
};
|
|
69
|
+
}));
|
|
70
|
+
// One workspace keeps the flat shape; several come back per workspace.
|
|
71
|
+
return (0, shared_js_1.ok)(perWorkspace.length === 1 ? perWorkspace[0] : { workspaces: perWorkspace });
|
|
39
72
|
}));
|
|
40
73
|
server.registerTool("get_environment", {
|
|
41
74
|
title: "Get an environment",
|
|
42
|
-
description: "Read one environment's variables. Values are masked (••••ab) unless reveal: true AND the token holds environments:reveal. Global variables are returned alongside.",
|
|
75
|
+
description: "Read one environment's variables ({ key, value }). Values are masked (••••ab) unless reveal: true AND the token holds environments:reveal. Global variables are returned alongside.",
|
|
43
76
|
inputSchema: { workspace: shared_js_1.workspaceArg, environment: (0, shared_js_1.refArg)("Environment"), reveal: zod_1.z.boolean().optional() },
|
|
44
77
|
annotations: shared_js_1.READ,
|
|
45
78
|
}, (0, shared_js_1.guarded)(async ({ workspace, environment, reveal }) => {
|
|
46
|
-
const ws = await
|
|
47
|
-
const
|
|
48
|
-
const [fresh, globals] = await Promise.all([
|
|
49
|
-
ctx.api.getEnvironment(ws.id, resolved.item.id, reveal ?? false),
|
|
50
|
-
ctx.api.listGlobalVariables(ws.id, reveal ?? false).catch(() => []),
|
|
51
|
-
]);
|
|
79
|
+
const { ws, value: resolved, note } = await (0, lookups_js_1.findEnvironment)(ctx, workspace, environment);
|
|
80
|
+
const [fresh, globals] = await Promise.all([ctx.api.getEnvironment(ws.id, resolved.item.id, reveal ?? false), loadGlobals(ctx, ws.id, reveal ?? false)]);
|
|
52
81
|
const masked = fresh.variables.some((variable) => variable.valueMasked);
|
|
53
82
|
return (0, shared_js_1.ok)({
|
|
54
|
-
environment: {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
},
|
|
59
|
-
globalVariables: globals.map((variable) => ({ key: variable.key, value: variable.value })),
|
|
83
|
+
environment: { id: fresh.id, name: fresh.name, variables: fresh.variables.map((variable) => ({ key: variable.key, value: variable.value })) },
|
|
84
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws),
|
|
85
|
+
globalVariables: globals.globals.map((variable) => ({ key: variable.key, value: variable.value })),
|
|
86
|
+
...(globals.globalVariablesError ? { globalVariablesError: globals.globalVariablesError } : {}),
|
|
60
87
|
valuesMasked: masked,
|
|
61
88
|
...(masked && reveal ? { note: "Values stayed masked: the token lacks environments:reveal." } : {}),
|
|
62
|
-
resolved: [(0, resolve_js_1.resolvedSummary)(environment, resolved)],
|
|
89
|
+
resolved: [(0, resolve_js_1.resolvedSummary)(environment, resolved), ...(note ? [{ input: environment, note }] : [])],
|
|
63
90
|
});
|
|
64
91
|
}));
|
|
65
92
|
server.registerTool("create_environment", {
|
|
66
93
|
title: "Create an environment",
|
|
67
|
-
description:
|
|
94
|
+
description: `Create an environment with variables ({ key, value }, up to ${VARIABLES_MAX}) in \`workspace\` (or the session default — use_workspace). Use it for hosts ({{baseUrl}}) and credentials so requests never hard-code them; requests use environments of their own workspace. Names must be unique in the workspace.`,
|
|
68
95
|
inputSchema: {
|
|
69
96
|
workspace: shared_js_1.workspaceArg,
|
|
70
|
-
name: zod_1.z.string().min(1).max(truncate_js_1.LIMITS.NAME),
|
|
71
|
-
variables: zod_1.z.array(
|
|
97
|
+
name: zod_1.z.string().trim().min(1).max(truncate_js_1.LIMITS.NAME),
|
|
98
|
+
variables: zod_1.z.array(variableSchema).max(VARIABLES_MAX).optional(),
|
|
72
99
|
},
|
|
73
100
|
annotations: shared_js_1.WRITE,
|
|
74
101
|
}, (0, shared_js_1.guarded)(async ({ workspace, name, variables }) => {
|
|
75
102
|
const ws = await ctx.resolveWorkspace(workspace);
|
|
76
|
-
const rows =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
103
|
+
const rows = variables ?? [];
|
|
104
|
+
refuseMasked(rows, "stored");
|
|
105
|
+
const duplicates = duplicateKeys(rows);
|
|
106
|
+
if (duplicates.length > 0)
|
|
107
|
+
throw new errors_js_1.ToolInputError("DUPLICATE_KEYS", `Each key once: ${duplicates.join(", ")}.`);
|
|
108
|
+
const existing = (await (0, lookups_js_1.loadEnvironments)(ctx, ws.id)).find((entry) => (0, lookups_js_1.sameName)(entry.name, name));
|
|
109
|
+
if (existing) {
|
|
110
|
+
throw new errors_js_1.ToolInputError("ENVIRONMENT_NAME_EXISTS", `An environment named "${existing.name}" already exists (id ${existing.id}) — use set_environment_variables on it, or pick another name.`, { existing: { id: existing.id, name: existing.name } });
|
|
111
|
+
}
|
|
80
112
|
const created = await ctx.api.createEnvironment(ws.id, { name: name.trim(), variables: rows });
|
|
81
113
|
ctx.invalidate(`environments:${ws.id}`);
|
|
82
|
-
return (0, shared_js_1.ok)({ environment: { id: created.id, name: created.name, variableKeys: created.variables.map((variable) => variable.key) } });
|
|
114
|
+
return (0, shared_js_1.ok)({ environment: { id: created.id, name: created.name, variableKeys: created.variables.map((variable) => variable.key) }, workspace: (0, lookups_js_1.workspaceTag)(ws) });
|
|
115
|
+
}));
|
|
116
|
+
server.registerTool("update_environment", {
|
|
117
|
+
title: "Rename an environment",
|
|
118
|
+
description: "Rename an environment. (Change its variables with set_environment_variables.)",
|
|
119
|
+
inputSchema: { workspace: shared_js_1.workspaceArg, environment: (0, shared_js_1.refArg)("Environment"), name: zod_1.z.string().trim().min(1).max(truncate_js_1.LIMITS.NAME) },
|
|
120
|
+
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
121
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, environment, name }) => {
|
|
122
|
+
const { ws, value: resolved } = await (0, lookups_js_1.findEnvironment)(ctx, workspace, environment);
|
|
123
|
+
const clash = (await (0, lookups_js_1.loadEnvironments)(ctx, ws.id)).find((entry) => entry.id !== resolved.item.id && (0, lookups_js_1.sameName)(entry.name, name));
|
|
124
|
+
if (clash)
|
|
125
|
+
throw new errors_js_1.ToolInputError("ENVIRONMENT_NAME_EXISTS", `Another environment is already named "${clash.name}".`);
|
|
126
|
+
const updated = await ctx.api.updateEnvironment(ws.id, resolved.item.id, { name: name.trim() });
|
|
127
|
+
ctx.invalidate(`environments:${ws.id}`);
|
|
128
|
+
return (0, shared_js_1.ok)({ environment: { id: updated.id, name: updated.name, previousName: resolved.item.name }, workspace: (0, lookups_js_1.workspaceTag)(ws), resolved: [(0, resolve_js_1.resolvedSummary)(environment, resolved)] });
|
|
83
129
|
}));
|
|
84
130
|
server.registerTool("set_environment_variables", {
|
|
85
|
-
title: "Set / unset environment variables",
|
|
86
|
-
description: "
|
|
131
|
+
title: "Set / rename / unset environment variables",
|
|
132
|
+
description: "Change an environment's variables: `set` upserts ({ key, value } — existing keys updated in place, new keys appended), `rename` changes keys keeping their values, `unset` removes keys (needs environments:delete). Other keys are untouched. A masked value (••••…) is refused — read it with reveal or supply a real value. Reports what changed, what failed (with the error code) and unset keys that did not exist.",
|
|
87
133
|
inputSchema: {
|
|
88
134
|
workspace: shared_js_1.workspaceArg,
|
|
89
135
|
environment: (0, shared_js_1.refArg)("Environment"),
|
|
90
|
-
set: zod_1.z.array(
|
|
91
|
-
|
|
136
|
+
set: zod_1.z.array(variableSchema).max(VARIABLES_MAX).optional(),
|
|
137
|
+
rename: zod_1.z.array((0, strict_js_1.strictObject)({ from: zod_1.z.string().trim().min(1).max(100), to: zod_1.z.string().trim().min(1).max(100) }, "rename field")).max(VARIABLES_MAX).optional(),
|
|
138
|
+
unset: zod_1.z.array(zod_1.z.string().trim().min(1).max(100)).max(VARIABLES_MAX).optional(),
|
|
92
139
|
},
|
|
93
140
|
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
94
|
-
}, (0, shared_js_1.guarded)(async ({ workspace, environment, set, unset }) => {
|
|
95
|
-
const rows =
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
141
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, environment, set, rename, unset }) => {
|
|
142
|
+
const rows = set ?? [];
|
|
143
|
+
const renames = rename ?? [];
|
|
144
|
+
const removals = unset ?? [];
|
|
145
|
+
if (rows.length === 0 && renames.length === 0 && removals.length === 0)
|
|
146
|
+
throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass set, rename and/or unset.");
|
|
147
|
+
refuseMasked(rows, "written back");
|
|
148
|
+
const duplicates = duplicateKeys(rows);
|
|
149
|
+
if (duplicates.length > 0)
|
|
150
|
+
throw new errors_js_1.ToolInputError("DUPLICATE_KEYS", `Each key once in set: ${duplicates.join(", ")}.`);
|
|
151
|
+
const { ws, value: resolved } = await (0, lookups_js_1.findEnvironment)(ctx, workspace, environment);
|
|
104
152
|
const existingKeys = new Set(resolved.item.variables.map((variable) => variable.key));
|
|
105
153
|
const updated = [];
|
|
106
154
|
const added = [];
|
|
155
|
+
const renamed = [];
|
|
107
156
|
const removed = [];
|
|
157
|
+
const notFound = [];
|
|
108
158
|
const failed = [];
|
|
159
|
+
for (const { from, to } of renames) {
|
|
160
|
+
if (!existingKeys.has(from)) {
|
|
161
|
+
notFound.push(from);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
await ctx.api.updateEnvironmentVariable(ws.id, resolved.item.id, from, { key: to });
|
|
166
|
+
existingKeys.delete(from);
|
|
167
|
+
existingKeys.add(to);
|
|
168
|
+
renamed.push({ from, to });
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
failed.push({ key: from, error: (0, shared_js_1.failureOf)(error) });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
109
174
|
for (const row of rows) {
|
|
110
175
|
try {
|
|
111
176
|
if (existingKeys.has(row.key)) {
|
|
@@ -119,35 +184,90 @@ const registerEnvironmentTools = (server, ctx) => {
|
|
|
119
184
|
}
|
|
120
185
|
}
|
|
121
186
|
catch (error) {
|
|
122
|
-
failed.push({ key: row.key, error:
|
|
187
|
+
failed.push({ key: row.key, error: (0, shared_js_1.failureOf)(error) });
|
|
123
188
|
}
|
|
124
189
|
}
|
|
125
190
|
for (const key of removals) {
|
|
191
|
+
if (!existingKeys.has(key)) {
|
|
192
|
+
notFound.push(key);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
126
195
|
try {
|
|
127
|
-
if (!existingKeys.has(key))
|
|
128
|
-
continue;
|
|
129
196
|
await ctx.api.deleteEnvironmentVariable(ws.id, resolved.item.id, key);
|
|
130
197
|
removed.push(key);
|
|
131
198
|
}
|
|
132
199
|
catch (error) {
|
|
133
|
-
failed.push({ key, error:
|
|
200
|
+
failed.push({ key, error: (0, shared_js_1.failureOf)(error) });
|
|
134
201
|
}
|
|
135
202
|
}
|
|
136
203
|
ctx.invalidate(`environments:${ws.id}`);
|
|
137
|
-
|
|
204
|
+
const changed = updated.length + added.length + renamed.length + removed.length;
|
|
205
|
+
const body = { environment: { id: resolved.item.id, name: resolved.item.name }, workspace: (0, lookups_js_1.workspaceTag)(ws), updated, added, renamed, removed, notFound, failed, resolved: [(0, resolve_js_1.resolvedSummary)(environment, resolved)] };
|
|
206
|
+
if (failed.length > 0 && changed === 0) {
|
|
207
|
+
return { ...(0, shared_js_1.toolFailure)(new errors_js_1.ToolInputError("NOTHING_CHANGED", `No variable was changed — every write failed (first: ${String(failed[0].error.code ?? failed[0].error.message)}).`, body)) };
|
|
208
|
+
}
|
|
209
|
+
return (0, shared_js_1.ok)({ ...body, ...(failed.length > 0 ? { partial: true } : {}) });
|
|
210
|
+
}));
|
|
211
|
+
server.registerTool("set_global_variables", {
|
|
212
|
+
title: "Set / unset global variables",
|
|
213
|
+
description: "Upsert (`set`: { key, value }) and/or remove (`unset`, needs environments:delete) the GLOBAL variables of `workspace` (or the session default) — shared by that workspace's environments and resolved as {{key}} when no environment defines it, or explicitly as {{global.key}}. A masked value (••••…) is refused.",
|
|
214
|
+
inputSchema: {
|
|
215
|
+
workspace: shared_js_1.workspaceArg,
|
|
216
|
+
set: zod_1.z.array(variableSchema).max(VARIABLES_MAX).optional(),
|
|
217
|
+
unset: zod_1.z.array(zod_1.z.string().trim().min(1).max(100)).max(VARIABLES_MAX).optional(),
|
|
218
|
+
},
|
|
219
|
+
annotations: shared_js_1.IDEMPOTENT_WRITE,
|
|
220
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, set, unset }) => {
|
|
221
|
+
const rows = set ?? [];
|
|
222
|
+
const removals = unset ?? [];
|
|
223
|
+
if (rows.length === 0 && removals.length === 0)
|
|
224
|
+
throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass set and/or unset.");
|
|
225
|
+
refuseMasked(rows, "written back");
|
|
226
|
+
const ws = await ctx.resolveWorkspace(workspace);
|
|
227
|
+
const existing = new Set((await ctx.api.listGlobalVariables(ws.id)).map((variable) => variable.key));
|
|
228
|
+
const written = [];
|
|
229
|
+
const removed = [];
|
|
230
|
+
const notFound = [];
|
|
231
|
+
const failed = [];
|
|
232
|
+
for (const row of rows) {
|
|
233
|
+
try {
|
|
234
|
+
await ctx.api.setGlobalVariable(ws.id, { key: row.key, value: row.value });
|
|
235
|
+
written.push(row.key);
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
failed.push({ key: row.key, error: (0, shared_js_1.failureOf)(error) });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
for (const key of removals) {
|
|
242
|
+
if (!existing.has(key)) {
|
|
243
|
+
notFound.push(key);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
await ctx.api.deleteGlobalVariable(ws.id, key);
|
|
248
|
+
removed.push(key);
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
failed.push({ key, error: (0, shared_js_1.failureOf)(error) });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const body = { workspace: { id: ws.id, name: ws.name }, written, removed, notFound, failed };
|
|
255
|
+
if (failed.length > 0 && written.length + removed.length === 0) {
|
|
256
|
+
return (0, shared_js_1.toolFailure)(new errors_js_1.ToolInputError("NOTHING_CHANGED", `No global variable was changed — every write failed (first: ${String(failed[0].error.code ?? failed[0].error.message)}).`, body));
|
|
257
|
+
}
|
|
258
|
+
return (0, shared_js_1.ok)({ ...body, ...(failed.length > 0 ? { partial: true } : {}) });
|
|
138
259
|
}));
|
|
139
260
|
server.registerTool("delete_environment", {
|
|
140
261
|
title: "Delete an environment",
|
|
141
|
-
description: "Delete an environment and all its variables. Permanent — confirm with the user first.",
|
|
262
|
+
description: "Delete an environment and all its variables (exact name or id). Permanent — confirm with the user first.",
|
|
142
263
|
inputSchema: { workspace: shared_js_1.workspaceArg, environment: (0, shared_js_1.refArg)("Environment"), confirm: shared_js_1.confirmArg },
|
|
143
264
|
annotations: shared_js_1.DESTRUCTIVE,
|
|
144
265
|
}, (0, shared_js_1.guarded)(async ({ workspace, environment, confirm }) => {
|
|
145
266
|
(0, shared_js_1.requireConfirm)(confirm, "an environment");
|
|
146
|
-
const ws = await
|
|
147
|
-
const resolved = await (0, lookups_js_1.resolveEnvironment)(ctx, ws.id, environment);
|
|
267
|
+
const { ws, value: resolved } = await (0, lookups_js_1.findEnvironment)(ctx, workspace, environment, { exact: true });
|
|
148
268
|
await ctx.api.deleteEnvironment(ws.id, resolved.item.id);
|
|
149
269
|
ctx.invalidate(`environments:${ws.id}`);
|
|
150
|
-
return (0, shared_js_1.ok)({ deleted: { id: resolved.item.id, name: resolved.item.name }, resolved: [(0, resolve_js_1.resolvedSummary)(environment, resolved)] });
|
|
270
|
+
return (0, shared_js_1.ok)({ deleted: { id: resolved.item.id, name: resolved.item.name }, workspace: (0, lookups_js_1.workspaceTag)(ws), resolved: [(0, resolve_js_1.resolvedSummary)(environment, resolved)] });
|
|
151
271
|
}));
|
|
152
272
|
};
|
|
153
273
|
exports.registerEnvironmentTools = registerEnvironmentTools;
|
package/dist-cjs/tools/import.js
CHANGED
|
@@ -7,6 +7,7 @@ const errors_js_1 = require("../client/errors.js");
|
|
|
7
7
|
const importDetect_js_1 = require("../domain/importDetect.js");
|
|
8
8
|
const resolve_js_1 = require("../domain/resolve.js");
|
|
9
9
|
const truncate_js_1 = require("../lib/truncate.js");
|
|
10
|
+
const strict_js_1 = require("../lib/strict.js");
|
|
10
11
|
const lookups_js_1 = require("./lookups.js");
|
|
11
12
|
const shared_js_1 = require("./shared.js");
|
|
12
13
|
const CONTENT_MAX = 8 * 1024 * 1024;
|
|
@@ -14,7 +15,7 @@ const IMPORT_FORMATS = ["auto", "postman", "openapi", "insomnia", "bruno", "inte
|
|
|
14
15
|
const registerImportTools = (server, ctx) => {
|
|
15
16
|
server.registerTool("import_content", {
|
|
16
17
|
title: "Import a file's content",
|
|
17
|
-
description: "Import API definitions from file CONTENT you read: Postman collection, OpenAPI/Swagger (JSON or YAML), Insomnia, Bruno, a Callman export, a cURL script, or a .http/.rest file. The format is detected (`format: auto`). Document formats always create a new collection; cURL/.http create a new collection (`collectionName`) or add to an existing one (`targetCollection` + optional `folder`). Use dryRun: true to see what was detected before writing. For endpoints found in source code, read the routes yourself and call create_requests_bulk instead.",
|
|
18
|
+
description: "Import API definitions from file CONTENT you read: Postman collection, OpenAPI/Swagger (JSON or YAML), Insomnia, Bruno, a Callman export, a cURL script, or a .http/.rest file. The format is detected (`format: auto`). Document formats always create a new collection; cURL/.http create a new collection (`collectionName`) or add to an existing one (`targetCollection` + optional `folder`). Variables the file defines (Postman collection variables, OpenAPI servers → baseUrl, .http @vars) are written into `environment` when you name one — without it, requests reference {{variables}} that have no value anywhere. Use dryRun: true to see what was detected before writing. For endpoints found in source code, read the routes yourself and call create_requests_bulk instead.",
|
|
18
19
|
inputSchema: {
|
|
19
20
|
workspace: shared_js_1.workspaceArg,
|
|
20
21
|
content: zod_1.z.string().min(1).max(CONTENT_MAX).describe("The raw file content"),
|
|
@@ -23,13 +24,21 @@ const registerImportTools = (server, ctx) => {
|
|
|
23
24
|
collectionName: zod_1.z.string().max(truncate_js_1.LIMITS.NAME).optional().describe("Name for the new collection (documents: fallback when the file carries no name)"),
|
|
24
25
|
targetCollection: zod_1.z.string().max(200).optional().describe("cURL/.http only: add to this existing collection instead of creating one"),
|
|
25
26
|
folder: zod_1.z.string().max(300).optional().describe("cURL/.http + targetCollection: folder to put the requests in"),
|
|
27
|
+
environment: zod_1.z.string().max(200).optional().describe("Environment (id or name) that receives the variables the file defines"),
|
|
26
28
|
dryRun: shared_js_1.dryRunArg,
|
|
27
29
|
},
|
|
28
30
|
annotations: shared_js_1.WRITE,
|
|
29
|
-
}, (0, shared_js_1.guarded)(async ({ workspace, content, fileName, format, collectionName, targetCollection, folder, dryRun }) => {
|
|
30
|
-
|
|
31
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, content, fileName, format, collectionName, targetCollection, folder, environment, dryRun }) => {
|
|
32
|
+
// Into an existing collection → that collection's workspace; otherwise
|
|
33
|
+
// the one named, the session default or the only one.
|
|
34
|
+
const target = targetCollection ? await (0, lookups_js_1.findCollection)(ctx, workspace, targetCollection) : null;
|
|
35
|
+
const ws = target ? target.ws : await ctx.resolveWorkspace(workspace);
|
|
31
36
|
const detection = (0, importDetect_js_1.detectImport)(fileName, content);
|
|
32
37
|
const resolvedFormat = format === "auto" ? detection.format : format;
|
|
38
|
+
const targetEnvironment = environment ? await (0, lookups_js_1.resolveEnvironment)(ctx, ws.id, environment) : null;
|
|
39
|
+
if (folder && !targetCollection) {
|
|
40
|
+
throw new errors_js_1.ToolInputError("FOLDER_NEEDS_TARGET_COLLECTION", "`folder` only applies together with targetCollection (cURL/.http into an existing collection).");
|
|
41
|
+
}
|
|
33
42
|
if (resolvedFormat === "unknown" || resolvedFormat === "scenario" || resolvedFormat === "environment") {
|
|
34
43
|
throw new errors_js_1.ToolInputError("IMPORT_FORMAT_UNSUPPORTED", resolvedFormat === "scenario"
|
|
35
44
|
? "This is a Callman scenario export — use import_scenario."
|
|
@@ -49,8 +58,28 @@ const registerImportTools = (server, ctx) => {
|
|
|
49
58
|
if (collectionName)
|
|
50
59
|
payload = { ...payload, collectionName: collectionName.trim() };
|
|
51
60
|
const sample = payload.requests.slice(0, 5).map((request) => `${request.method} ${request.name}`);
|
|
61
|
+
// .http `@name = value` lines (callman-core ≥ 1.28.1 returns them).
|
|
62
|
+
const fileVariables = (payload.variables ?? []).filter((variable) => variable.key);
|
|
63
|
+
const applyFileVariables = async () => {
|
|
64
|
+
if (fileVariables.length === 0)
|
|
65
|
+
return {};
|
|
66
|
+
if (!targetEnvironment) {
|
|
67
|
+
return { fileVariables: fileVariables.map((variable) => variable.key), fileVariablesNote: "The file defines these variables; pass `environment` to store them, or set them with set_environment_variables." };
|
|
68
|
+
}
|
|
69
|
+
const existing = new Set(targetEnvironment.item.variables.map((variable) => variable.key));
|
|
70
|
+
const written = [];
|
|
71
|
+
for (const variable of fileVariables) {
|
|
72
|
+
if (existing.has(variable.key))
|
|
73
|
+
await ctx.api.updateEnvironmentVariable(ws.id, targetEnvironment.item.id, variable.key, { value: variable.value });
|
|
74
|
+
else
|
|
75
|
+
await ctx.api.addEnvironmentVariable(ws.id, targetEnvironment.item.id, { key: variable.key, value: variable.value });
|
|
76
|
+
written.push(variable.key);
|
|
77
|
+
}
|
|
78
|
+
ctx.invalidate(`environments:${ws.id}`);
|
|
79
|
+
return { environment: { id: targetEnvironment.item.id, name: targetEnvironment.item.name, variablesWritten: written } };
|
|
80
|
+
};
|
|
52
81
|
if (targetCollection) {
|
|
53
|
-
const resolved =
|
|
82
|
+
const resolved = target.value;
|
|
54
83
|
const targetFolder = await (0, lookups_js_1.resolveFolder)(ctx, ws.id, resolved.item.id, folder);
|
|
55
84
|
if (dryRun) {
|
|
56
85
|
return (0, shared_js_1.ok)({ dryRun: true, detected: detection, format: resolvedFormat, mode: "existing-collection", collection: { id: resolved.item.id, name: resolved.item.name }, requestCount: payload.requests.length, sample });
|
|
@@ -63,12 +92,12 @@ const registerImportTools = (server, ctx) => {
|
|
|
63
92
|
created.push({ id: row.id, name: row.name, method: row.method });
|
|
64
93
|
}
|
|
65
94
|
catch (error) {
|
|
66
|
-
failed.push({ name: request.name, error:
|
|
95
|
+
failed.push({ name: request.name, error: (0, shared_js_1.failureOf)(error) });
|
|
67
96
|
}
|
|
68
97
|
}
|
|
69
98
|
ctx.invalidate(`tree:${ws.id}:${resolved.item.id}`);
|
|
70
99
|
ctx.invalidate(`collections:${ws.id}`);
|
|
71
|
-
return (0, shared_js_1.ok)({ format: resolvedFormat, collection: { id: resolved.item.id, name: resolved.item.name }, created: created.length, failed, requests: created, resolved: [(0, resolve_js_1.resolvedSummary)(targetCollection, resolved)] });
|
|
100
|
+
return (0, shared_js_1.ok)({ format: resolvedFormat, collection: { id: resolved.item.id, name: resolved.item.name }, workspace: (0, lookups_js_1.workspaceTag)(ws), created: created.length, failed, requests: created, ...(await applyFileVariables()), resolved: [(0, resolve_js_1.resolvedSummary)(targetCollection, resolved)] });
|
|
72
101
|
}
|
|
73
102
|
const existing = (await (0, lookups_js_1.loadCollections)(ctx, ws.id)).find((entry) => (0, lookups_js_1.sameName)(entry.name, payload.collectionName));
|
|
74
103
|
if (existing) {
|
|
@@ -81,7 +110,10 @@ const registerImportTools = (server, ctx) => {
|
|
|
81
110
|
}
|
|
82
111
|
const result = await ctx.api.importJson(ws.id, { format: "requests", payload });
|
|
83
112
|
ctx.invalidate(`collections:${ws.id}`);
|
|
84
|
-
return (0, shared_js_1.ok)({ format: resolvedFormat, collection: { id: result.collection.id, name: result.collection.name }, created: result.importedCounts });
|
|
113
|
+
return (0, shared_js_1.ok)({ format: resolvedFormat, collection: { id: result.collection.id, name: result.collection.name }, workspace: (0, lookups_js_1.workspaceTag)(ws), created: result.importedCounts, ...(await applyFileVariables()) });
|
|
114
|
+
}
|
|
115
|
+
if (targetCollection) {
|
|
116
|
+
throw new errors_js_1.ToolInputError("IMPORT_TARGET_UNSUPPORTED", `A ${resolvedFormat} document always creates a new collection — targetCollection only applies to cURL/.http. Import it, then use move_requests to move requests into "${targetCollection}".`);
|
|
85
117
|
}
|
|
86
118
|
if (!importDetect_js_1.SERVER_DOCUMENT_FORMATS.has(resolvedFormat)) {
|
|
87
119
|
throw new errors_js_1.ToolInputError("IMPORT_FORMAT_UNSUPPORTED", `Format "${resolvedFormat}" is not importable here.`);
|
|
@@ -90,22 +122,45 @@ const registerImportTools = (server, ctx) => {
|
|
|
90
122
|
if (dryRun) {
|
|
91
123
|
return (0, shared_js_1.ok)({ dryRun: true, detected: detection, format: resolvedFormat, mode: "new-collection", note: "The server parses this document; request counts are known after the import." });
|
|
92
124
|
}
|
|
93
|
-
const result = await ctx.api.importJson(ws.id, {
|
|
125
|
+
const result = await ctx.api.importJson(ws.id, {
|
|
126
|
+
format: resolvedFormat,
|
|
127
|
+
payload,
|
|
128
|
+
...(collectionName ? { fallbackCollectionName: collectionName.trim() } : {}),
|
|
129
|
+
...(targetEnvironment ? { targetEnvironmentId: targetEnvironment.item.id } : {}),
|
|
130
|
+
});
|
|
94
131
|
ctx.invalidate(`collections:${ws.id}`);
|
|
95
|
-
|
|
132
|
+
if (targetEnvironment)
|
|
133
|
+
ctx.invalidate(`environments:${ws.id}`);
|
|
134
|
+
return (0, shared_js_1.ok)({
|
|
135
|
+
format: resolvedFormat,
|
|
136
|
+
collection: { id: result.collection.id, name: result.collection.name },
|
|
137
|
+
workspace: (0, lookups_js_1.workspaceTag)(ws),
|
|
138
|
+
created: result.importedCounts,
|
|
139
|
+
// Keys only — the import response carries the environment's values.
|
|
140
|
+
...(result.environment ? { environment: { id: result.environment.id, name: result.environment.name, variableKeys: (result.environment.variables ?? []).map((variable) => variable.key) } } : {}),
|
|
141
|
+
...(!targetEnvironment ? { note: "Variables defined by the file were not stored — pass `environment` to write them into an environment." } : {}),
|
|
142
|
+
});
|
|
96
143
|
}));
|
|
97
144
|
server.registerTool("import_scenario", {
|
|
98
145
|
title: "Import a scenario export",
|
|
99
|
-
description: "Import a Callman scenario export (a `callman.scenario` envelope — the JSON of a .callman.scenario file, as text or object). Connections
|
|
146
|
+
description: "Import a Callman scenario export (a `callman.scenario` envelope — the JSON of a .callman.scenario file, as text or object). Connections the export references are matched to this workspace's connections of the same type and name automatically; `connections` overrides that per export connection (a workspace connection id or name, or \"skip\"). Unmatched ones are skipped (the nodes import without a connection) and listed. On-prem, `group` picks the scenario group.",
|
|
100
147
|
inputSchema: {
|
|
101
148
|
workspace: shared_js_1.workspaceArg,
|
|
102
149
|
envelope: zod_1.z.union([zod_1.z.string().max(CONTENT_MAX), zod_1.z.record(zod_1.z.unknown())]).describe("The export envelope (JSON text or object)"),
|
|
103
150
|
name: zod_1.z.string().max(200).optional().describe("Override the imported scenario's name"),
|
|
104
151
|
folder: zod_1.z.string().max(200).optional().describe("Scenario folder id or name"),
|
|
152
|
+
group: zod_1.z.string().max(200).optional().describe('On-prem scenario group (name or id, or "personal")'),
|
|
153
|
+
connections: zod_1.z
|
|
154
|
+
.array((0, strict_js_1.strictObject)({
|
|
155
|
+
from: zod_1.z.string().min(1).max(300).describe("The export's connection (its refId or name)"),
|
|
156
|
+
to: zod_1.z.string().min(1).max(200).describe('Workspace connection id or name, or "skip"'),
|
|
157
|
+
}, "connection mapping field"))
|
|
158
|
+
.max(200)
|
|
159
|
+
.optional(),
|
|
105
160
|
dryRun: shared_js_1.dryRunArg,
|
|
106
161
|
},
|
|
107
162
|
annotations: shared_js_1.WRITE,
|
|
108
|
-
}, (0, shared_js_1.guarded)(async ({ workspace, envelope, name, folder, dryRun }) => {
|
|
163
|
+
}, (0, shared_js_1.guarded)(async ({ workspace, envelope, name, folder, group, connections: mapping, dryRun }) => {
|
|
109
164
|
const ws = await ctx.resolveWorkspace(workspace);
|
|
110
165
|
let parsed = envelope;
|
|
111
166
|
if (typeof envelope === "string") {
|
|
@@ -121,23 +176,59 @@ const registerImportTools = (server, ctx) => {
|
|
|
121
176
|
throw new errors_js_1.ToolInputError("IMPORT_NOT_SCENARIO", "This is not a Callman scenario export (expected kind: \"callman.scenario\").");
|
|
122
177
|
}
|
|
123
178
|
const targetFolder = folder ? await (0, lookups_js_1.resolveScenarioFolder)(ctx, ws.id, folder) : null;
|
|
124
|
-
|
|
125
|
-
|
|
179
|
+
let groupId;
|
|
180
|
+
if (group) {
|
|
181
|
+
if (group.trim().toLowerCase() === "personal")
|
|
182
|
+
groupId = "personal";
|
|
183
|
+
else {
|
|
184
|
+
const { items } = await ctx.api.listMyScenarioGroups(ws.id);
|
|
185
|
+
if (items.length > 0)
|
|
186
|
+
groupId = (0, resolve_js_1.resolveRef)("scenario group", group, items).item.id;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const exported = (record.connections ?? []).filter((connection) => connection.refId);
|
|
190
|
+
for (const entry of mapping ?? []) {
|
|
191
|
+
if (!exported.some((connection) => connection.refId === entry.from || connection.name === entry.from)) {
|
|
192
|
+
throw new errors_js_1.ToolInputError("IMPORT_CONNECTION_UNKNOWN", `The export has no connection "${entry.from}".`, { exportConnections: exported.map((connection) => ({ refId: connection.refId, name: connection.name, type: connection.type })) });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const available = exported.length > 0 ? await (0, lookups_js_1.loadConnections)(ctx, ws.id) : [];
|
|
196
|
+
const decisions = [];
|
|
197
|
+
const connectionMap = {};
|
|
198
|
+
for (const connection of exported) {
|
|
199
|
+
const override = (mapping ?? []).find((entry) => entry.from === connection.refId || entry.from === connection.name);
|
|
200
|
+
let target;
|
|
201
|
+
if (override && override.to.trim().toLowerCase() !== "skip") {
|
|
202
|
+
target = available.find((candidate) => candidate.id === override.to) ?? available.find((candidate) => candidate.name.toLowerCase() === override.to.toLowerCase());
|
|
203
|
+
if (!target)
|
|
204
|
+
throw new errors_js_1.ToolInputError("CONNECTION_NOT_FOUND", `No workspace connection "${override.to}" (list_connections shows them).`);
|
|
205
|
+
if (connection.type && target.type !== connection.type)
|
|
206
|
+
throw new errors_js_1.ToolInputError("CONNECTION_WRONG_TYPE", `"${target.name}" is a ${target.type} connection; the export's "${connection.name}" is ${connection.type}.`);
|
|
207
|
+
}
|
|
208
|
+
else if (!override) {
|
|
209
|
+
const sameName = available.filter((candidate) => candidate.type === connection.type && candidate.name.toLowerCase() === (connection.name ?? "").toLowerCase());
|
|
210
|
+
target = sameName.length === 1 ? sameName[0] : undefined;
|
|
211
|
+
}
|
|
212
|
+
connectionMap[connection.refId] = target ? { action: "map", connectionId: target.id } : { action: "skip" };
|
|
213
|
+
decisions.push({ export: connection.name ?? connection.refId, type: connection.type ?? null, mappedTo: target ? target.name : null });
|
|
214
|
+
}
|
|
126
215
|
if (dryRun) {
|
|
127
216
|
return (0, shared_js_1.ok)({
|
|
128
217
|
dryRun: true,
|
|
129
218
|
scenario: { name: name ?? record.scenario?.name ?? null, nodeCount: Array.isArray(record.scenario?.nodes) ? record.scenario?.nodes.length : null },
|
|
130
|
-
|
|
219
|
+
connections: decisions,
|
|
131
220
|
folder: targetFolder ? { id: targetFolder.item.id, name: targetFolder.item.name } : null,
|
|
132
221
|
});
|
|
133
222
|
}
|
|
134
|
-
const result = await ctx.api.importScenario(ws.id, { envelope: parsed, ...(name ? { name } : {}), folderId: targetFolder?.item.id ?? null, connectionMap });
|
|
223
|
+
const result = await ctx.api.importScenario(ws.id, { envelope: parsed, ...(name ? { name } : {}), folderId: targetFolder?.item.id ?? null, connectionMap, ...(groupId ? { groupId } : {}) });
|
|
135
224
|
ctx.invalidate("scenarios:");
|
|
136
225
|
const scenario = result.scenario;
|
|
226
|
+
const skipped = decisions.filter((decision) => !decision.mappedTo);
|
|
137
227
|
return (0, shared_js_1.ok)({
|
|
138
228
|
scenario: { id: scenario.id, name: scenario.name, nodeCount: scenario.nodes?.length ?? null, reviewStatus: scenario.reviewStatus ?? null },
|
|
229
|
+
connections: decisions,
|
|
139
230
|
warnings: result.warnings ?? [],
|
|
140
|
-
...(
|
|
231
|
+
...(skipped.length > 0 ? { note: `Not connected: ${skipped.map((decision) => decision.export).join(", ")} — their nodes need a connectionId (patch_scenario + list_connections).` } : {}),
|
|
141
232
|
...(targetFolder ? { resolved: [(0, resolve_js_1.resolvedSummary)(folder, targetFolder)] } : {}),
|
|
142
233
|
});
|
|
143
234
|
}));
|
package/dist-cjs/tools/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.registerAllTools = exports.toolAvailability = exports.TOOL_CATALOG = void 0;
|
|
3
|
+
exports.registerAllTools = exports.withStrictArguments = exports.toolAvailability = exports.TOOL_CATALOG = void 0;
|
|
4
|
+
const strict_js_1 = require("../lib/strict.js");
|
|
4
5
|
const collections_js_1 = require("./collections.js");
|
|
6
|
+
const contracts_js_1 = require("./contracts.js");
|
|
5
7
|
const diagrams_js_1 = require("./diagrams.js");
|
|
6
8
|
const environments_js_1 = require("./environments.js");
|
|
7
9
|
const import_js_1 = require("./import.js");
|
|
@@ -13,14 +15,31 @@ const workspaces_js_1 = require("./workspaces.js");
|
|
|
13
15
|
var catalog_js_1 = require("./catalog.js");
|
|
14
16
|
Object.defineProperty(exports, "TOOL_CATALOG", { enumerable: true, get: function () { return catalog_js_1.TOOL_CATALOG; } });
|
|
15
17
|
Object.defineProperty(exports, "toolAvailability", { enumerable: true, get: function () { return catalog_js_1.toolAvailability; } });
|
|
18
|
+
// Every tool's arguments are strict: the SDK would otherwise build a
|
|
19
|
+
// strip-mode object from the raw shape, and a misspelled argument
|
|
20
|
+
// (`workspaceId` for `workspace`, `colour` for `color`) would vanish while the
|
|
21
|
+
// tool ran with its default. Runtime only — handler types still come from the
|
|
22
|
+
// raw shapes at each registerTool call.
|
|
23
|
+
const isRawShape = (schema) => typeof schema === "object" && schema !== null && !("_def" in schema) && !("_zod" in schema);
|
|
24
|
+
const withStrictArguments = (server) => new Proxy(server, {
|
|
25
|
+
get(target, property, receiver) {
|
|
26
|
+
if (property !== "registerTool")
|
|
27
|
+
return Reflect.get(target, property, receiver);
|
|
28
|
+
const register = (name, config, callback) => target.registerTool(name, { ...config, inputSchema: isRawShape(config.inputSchema) ? (0, strict_js_1.strictObject)(config.inputSchema, "argument", name) : config.inputSchema }, callback);
|
|
29
|
+
return register;
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
exports.withStrictArguments = withStrictArguments;
|
|
16
33
|
// Registration order = the order clients list tools in. Run tools are only
|
|
17
34
|
// registered when local execution is allowed (a backend-hosted server passes
|
|
18
35
|
// allowLocalRun: false and they disappear from tools/list).
|
|
19
|
-
const registerAllTools = (
|
|
36
|
+
const registerAllTools = (host, ctx) => {
|
|
37
|
+
const server = (0, exports.withStrictArguments)(host);
|
|
20
38
|
(0, meta_js_1.registerMetaTools)(server, ctx);
|
|
21
39
|
(0, workspaces_js_1.registerWorkspaceTools)(server, ctx);
|
|
22
40
|
(0, collections_js_1.registerCollectionTools)(server, ctx);
|
|
23
41
|
(0, requests_js_1.registerRequestTools)(server, ctx);
|
|
42
|
+
(0, contracts_js_1.registerContractTools)(server, ctx);
|
|
24
43
|
(0, import_js_1.registerImportTools)(server, ctx);
|
|
25
44
|
(0, environments_js_1.registerEnvironmentTools)(server, ctx);
|
|
26
45
|
(0, scenarios_js_1.registerScenarioTools)(server, ctx);
|