@chanl/cli 2.0.3 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/agents.test.js +574 -0
- package/dist/__tests__/agents.test.js.map +1 -0
- package/dist/__tests__/auth.test.d.ts +2 -0
- package/dist/__tests__/auth.test.js +87 -0
- package/dist/__tests__/auth.test.js.map +1 -0
- package/dist/__tests__/calls.test.d.ts +2 -0
- package/dist/__tests__/calls.test.js +115 -0
- package/dist/__tests__/calls.test.js.map +1 -0
- package/dist/__tests__/chat.test.d.ts +2 -0
- package/dist/__tests__/chat.test.js +357 -0
- package/dist/__tests__/chat.test.js.map +1 -0
- package/dist/__tests__/cli-base.test.d.ts +2 -0
- package/dist/__tests__/cli-base.test.js +87 -0
- package/dist/__tests__/cli-base.test.js.map +1 -0
- package/dist/__tests__/cli-missing.test.d.ts +2 -0
- package/dist/__tests__/cli-missing.test.js +546 -0
- package/dist/__tests__/cli-missing.test.js.map +1 -0
- package/dist/__tests__/helpers.d.ts +234 -0
- package/dist/__tests__/helpers.js +314 -0
- package/dist/__tests__/helpers.js.map +1 -0
- package/dist/__tests__/mcp.test.d.ts +2 -0
- package/dist/__tests__/mcp.test.js +470 -0
- package/dist/__tests__/mcp.test.js.map +1 -0
- package/dist/__tests__/prompts.test.d.ts +2 -0
- package/dist/__tests__/prompts.test.js +227 -0
- package/dist/__tests__/prompts.test.js.map +1 -0
- package/dist/__tests__/scenarios.test.d.ts +2 -0
- package/dist/__tests__/scenarios.test.js +270 -0
- package/dist/__tests__/scenarios.test.js.map +1 -0
- package/dist/__tests__/tools.test.d.ts +2 -0
- package/dist/__tests__/tools.test.js +331 -0
- package/dist/__tests__/tools.test.js.map +1 -0
- package/dist/__tests__/toolsets.test.d.ts +2 -0
- package/dist/__tests__/toolsets.test.js +196 -0
- package/dist/__tests__/toolsets.test.js.map +1 -0
- package/dist/__tests__/workspaces.test.d.ts +2 -0
- package/dist/__tests__/workspaces.test.js +155 -0
- package/dist/__tests__/workspaces.test.js.map +1 -0
- package/dist/commands/agents.js +103 -13
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/chat.js +2 -2
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/config.js +24 -2
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/mcp.js +1 -1
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/memory.js +137 -4
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/personas.js +3 -3
- package/dist/commands/personas.js.map +1 -1
- package/dist/commands/prompts.js +148 -1
- package/dist/commands/prompts.js.map +1 -1
- package/dist/commands/scenarios.js +2 -2
- package/dist/commands/scenarios.js.map +1 -1
- package/dist/commands/scorecards.js +3 -3
- package/dist/commands/scorecards.js.map +1 -1
- package/dist/commands/tools.js +5 -5
- package/dist/commands/tools.js.map +1 -1
- package/dist/utils/config-store.d.ts +8 -0
- package/dist/utils/config-store.js +18 -1
- package/dist/utils/config-store.js.map +1 -1
- package/dist/utils/sdk-factory.js +15 -3
- package/dist/utils/sdk-factory.js.map +1 -1
- package/package.json +4 -4
- package/dist/__tests__/cli.test.js +0 -2313
- package/dist/__tests__/cli.test.js.map +0 -1
- /package/dist/__tests__/{cli.test.d.ts → agents.test.d.ts} +0 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { configStore } from "../utils/config-store";
|
|
3
|
+
import {
|
|
4
|
+
runCli,
|
|
5
|
+
mockTool,
|
|
6
|
+
mockExecution,
|
|
7
|
+
installFetchMock,
|
|
8
|
+
mockFetchResponse,
|
|
9
|
+
expectFetchCalled,
|
|
10
|
+
setupFetchConfigStore,
|
|
11
|
+
getFetchUrl
|
|
12
|
+
} from "./helpers";
|
|
13
|
+
vi.mock("../utils/config-store", () => ({
|
|
14
|
+
configStore: {
|
|
15
|
+
getApiKey: vi.fn(),
|
|
16
|
+
getBaseUrl: vi.fn(),
|
|
17
|
+
getWorkspaceId: vi.fn(),
|
|
18
|
+
getJwtToken: vi.fn(),
|
|
19
|
+
getAuthMethod: vi.fn(() => null),
|
|
20
|
+
getAppUrl: vi.fn(() => "https://app.chanl.ai"),
|
|
21
|
+
getDeployment: vi.fn(),
|
|
22
|
+
getApiPrefix: vi.fn(),
|
|
23
|
+
setApiKey: vi.fn(),
|
|
24
|
+
setBaseUrl: vi.fn(),
|
|
25
|
+
setWorkspaceId: vi.fn(),
|
|
26
|
+
removeApiKey: vi.fn(),
|
|
27
|
+
clearAuth: vi.fn(),
|
|
28
|
+
delete: vi.fn(),
|
|
29
|
+
isAuthenticated: vi.fn(),
|
|
30
|
+
getPath: vi.fn(() => "/home/user/.chanl/config.json")
|
|
31
|
+
}
|
|
32
|
+
}));
|
|
33
|
+
describe("CLI", () => {
|
|
34
|
+
let mockFetch;
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
vi.clearAllMocks();
|
|
37
|
+
mockFetch = installFetchMock();
|
|
38
|
+
setupFetchConfigStore(configStore);
|
|
39
|
+
});
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
process.exitCode = void 0;
|
|
42
|
+
});
|
|
43
|
+
describe("tools list", () => {
|
|
44
|
+
it("should list tools with table output", async () => {
|
|
45
|
+
mockFetchResponse(mockFetch, {
|
|
46
|
+
success: true,
|
|
47
|
+
data: {
|
|
48
|
+
tools: [mockTool],
|
|
49
|
+
total: 1,
|
|
50
|
+
pagination: {
|
|
51
|
+
page: 1,
|
|
52
|
+
limit: 20,
|
|
53
|
+
totalPages: 1,
|
|
54
|
+
hasNext: false,
|
|
55
|
+
hasPrev: false
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
message: "Success",
|
|
59
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
60
|
+
});
|
|
61
|
+
const { logs, exitCode } = await runCli(["tools", "list"]);
|
|
62
|
+
expect(exitCode).toBe(0);
|
|
63
|
+
expectFetchCalled(mockFetch, {
|
|
64
|
+
method: "GET",
|
|
65
|
+
path: "/api/v1/tools"
|
|
66
|
+
});
|
|
67
|
+
const output = logs.join("\n");
|
|
68
|
+
expect(output).toContain("get_weather");
|
|
69
|
+
expect(output).toContain("http");
|
|
70
|
+
expect(output).toContain("Total: 1 tools");
|
|
71
|
+
});
|
|
72
|
+
it("should list tools with JSON output", async () => {
|
|
73
|
+
mockFetchResponse(mockFetch, {
|
|
74
|
+
success: true,
|
|
75
|
+
data: {
|
|
76
|
+
tools: [mockTool],
|
|
77
|
+
total: 1,
|
|
78
|
+
pagination: {
|
|
79
|
+
page: 1,
|
|
80
|
+
limit: 20,
|
|
81
|
+
totalPages: 1,
|
|
82
|
+
hasNext: false,
|
|
83
|
+
hasPrev: false
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
message: "Success",
|
|
87
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
88
|
+
});
|
|
89
|
+
const { logs, exitCode } = await runCli(["--json", "tools", "list"]);
|
|
90
|
+
expect(exitCode).toBe(0);
|
|
91
|
+
const output = logs.join("\n");
|
|
92
|
+
const parsed = JSON.parse(output);
|
|
93
|
+
expect(parsed.tools).toHaveLength(1);
|
|
94
|
+
expect(parsed.tools[0].name).toBe("get_weather");
|
|
95
|
+
});
|
|
96
|
+
it("should filter by type", async () => {
|
|
97
|
+
mockFetchResponse(mockFetch, {
|
|
98
|
+
success: true,
|
|
99
|
+
data: {
|
|
100
|
+
tools: [mockTool],
|
|
101
|
+
total: 1,
|
|
102
|
+
pagination: { page: 1, limit: 20, totalPages: 1, hasNext: false, hasPrev: false }
|
|
103
|
+
},
|
|
104
|
+
message: "Success",
|
|
105
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
106
|
+
});
|
|
107
|
+
await runCli(["tools", "list", "--type", "http"]);
|
|
108
|
+
const url = getFetchUrl(mockFetch);
|
|
109
|
+
expect(url).toContain("type=http");
|
|
110
|
+
});
|
|
111
|
+
it("should filter by enabled status", async () => {
|
|
112
|
+
mockFetchResponse(mockFetch, {
|
|
113
|
+
success: true,
|
|
114
|
+
data: {
|
|
115
|
+
tools: [mockTool],
|
|
116
|
+
total: 1,
|
|
117
|
+
pagination: { page: 1, limit: 20, totalPages: 1, hasNext: false, hasPrev: false }
|
|
118
|
+
},
|
|
119
|
+
message: "Success",
|
|
120
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
121
|
+
});
|
|
122
|
+
await runCli(["tools", "list", "--enabled"]);
|
|
123
|
+
const url = getFetchUrl(mockFetch);
|
|
124
|
+
expect(url).toContain("isEnabled=true");
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
describe("tools get", () => {
|
|
128
|
+
it("should get tool by ID", async () => {
|
|
129
|
+
mockFetchResponse(mockFetch, {
|
|
130
|
+
success: true,
|
|
131
|
+
data: mockTool,
|
|
132
|
+
message: "Success",
|
|
133
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
134
|
+
});
|
|
135
|
+
const { logs, exitCode } = await runCli(["tools", "get", "tool_weather123"]);
|
|
136
|
+
expect(exitCode).toBe(0);
|
|
137
|
+
expectFetchCalled(mockFetch, {
|
|
138
|
+
method: "GET",
|
|
139
|
+
path: "/api/v1/tools/tool_weather123"
|
|
140
|
+
});
|
|
141
|
+
const output = logs.join("\n");
|
|
142
|
+
expect(output).toContain("get_weather");
|
|
143
|
+
expect(output).toContain("http");
|
|
144
|
+
});
|
|
145
|
+
it("should output JSON when --json flag is used", async () => {
|
|
146
|
+
mockFetchResponse(mockFetch, {
|
|
147
|
+
success: true,
|
|
148
|
+
data: mockTool,
|
|
149
|
+
message: "Success",
|
|
150
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
151
|
+
});
|
|
152
|
+
const { logs, exitCode } = await runCli(["--json", "tools", "get", "tool_weather123"]);
|
|
153
|
+
expect(exitCode).toBe(0);
|
|
154
|
+
const output = logs.join("\n");
|
|
155
|
+
const parsed = JSON.parse(output);
|
|
156
|
+
expect(parsed.name).toBe("get_weather");
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
describe("tools execute", () => {
|
|
160
|
+
it("should execute tool with input", async () => {
|
|
161
|
+
mockFetchResponse(mockFetch, {
|
|
162
|
+
success: true,
|
|
163
|
+
data: {
|
|
164
|
+
success: true,
|
|
165
|
+
data: { temperature: "12\xB0C", condition: "Cloudy" },
|
|
166
|
+
latencyMs: 245,
|
|
167
|
+
traceId: "trace_abc123",
|
|
168
|
+
executionId: "exec_abc123"
|
|
169
|
+
},
|
|
170
|
+
message: "Success",
|
|
171
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
172
|
+
});
|
|
173
|
+
const { logs, exitCode } = await runCli([
|
|
174
|
+
"tools",
|
|
175
|
+
"execute",
|
|
176
|
+
"tool_weather123",
|
|
177
|
+
"--input",
|
|
178
|
+
'{"city":"London"}'
|
|
179
|
+
]);
|
|
180
|
+
expect(exitCode).toBe(0);
|
|
181
|
+
expectFetchCalled(mockFetch, {
|
|
182
|
+
method: "POST",
|
|
183
|
+
path: "/api/v1/tools/tool_weather123/execute",
|
|
184
|
+
body: { arguments: { city: "London" } }
|
|
185
|
+
});
|
|
186
|
+
const output = logs.join("\n");
|
|
187
|
+
expect(output).toContain("Success");
|
|
188
|
+
expect(output).toContain("245ms");
|
|
189
|
+
});
|
|
190
|
+
it("should handle invalid JSON input", async () => {
|
|
191
|
+
const { exitCode, errors } = await runCli([
|
|
192
|
+
"tools",
|
|
193
|
+
"execute",
|
|
194
|
+
"tool_weather123",
|
|
195
|
+
"--input",
|
|
196
|
+
"not-valid-json"
|
|
197
|
+
]);
|
|
198
|
+
expect(exitCode).toBe(1);
|
|
199
|
+
expect(errors.some((e) => e.includes("Invalid JSON"))).toBe(true);
|
|
200
|
+
expect(mockFetch.mock.calls).toHaveLength(0);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
describe("tools delete", () => {
|
|
204
|
+
it("should delete tool with --yes flag", async () => {
|
|
205
|
+
mockFetchResponse(mockFetch, {
|
|
206
|
+
success: true,
|
|
207
|
+
data: { deleted: true },
|
|
208
|
+
message: "Success",
|
|
209
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
210
|
+
});
|
|
211
|
+
const { logs, exitCode } = await runCli(["tools", "delete", "tool_weather123", "--yes"]);
|
|
212
|
+
expect(exitCode).toBe(0);
|
|
213
|
+
expectFetchCalled(mockFetch, {
|
|
214
|
+
method: "DELETE",
|
|
215
|
+
path: "/api/v1/tools/tool_weather123"
|
|
216
|
+
});
|
|
217
|
+
const output = logs.join("\n");
|
|
218
|
+
expect(output).toContain("Deleted");
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe("tools executions", () => {
|
|
222
|
+
it("should list executions for a tool", async () => {
|
|
223
|
+
mockFetchResponse(mockFetch, {
|
|
224
|
+
success: true,
|
|
225
|
+
data: {
|
|
226
|
+
data: [mockExecution],
|
|
227
|
+
total: 1,
|
|
228
|
+
page: 1,
|
|
229
|
+
limit: 10,
|
|
230
|
+
pagination: {
|
|
231
|
+
page: 1,
|
|
232
|
+
limit: 10,
|
|
233
|
+
totalPages: 1,
|
|
234
|
+
hasNext: false,
|
|
235
|
+
hasPrev: false
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
message: "Success",
|
|
239
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
240
|
+
});
|
|
241
|
+
const { logs, exitCode } = await runCli(["tools", "executions", "tool_weather123"]);
|
|
242
|
+
expect(exitCode).toBe(0);
|
|
243
|
+
const url = getFetchUrl(mockFetch);
|
|
244
|
+
expect(url).toContain("/api/v1/tools/tool_weather123/executions");
|
|
245
|
+
const output = logs.join("\n");
|
|
246
|
+
expect(output).toContain("exec_abc123");
|
|
247
|
+
expect(output).toContain("completed");
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
describe("tools enable/disable", () => {
|
|
251
|
+
it("should enable a tool", async () => {
|
|
252
|
+
mockFetchResponse(mockFetch, {
|
|
253
|
+
success: true,
|
|
254
|
+
data: { ...mockTool, isEnabled: true },
|
|
255
|
+
message: "Success",
|
|
256
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
257
|
+
});
|
|
258
|
+
const { logs, exitCode } = await runCli(["tools", "enable", "tool_weather123"]);
|
|
259
|
+
expect(exitCode).toBe(0);
|
|
260
|
+
expectFetchCalled(mockFetch, {
|
|
261
|
+
method: "POST",
|
|
262
|
+
path: "/api/v1/tools/tool_weather123/enable"
|
|
263
|
+
});
|
|
264
|
+
const output = logs.join("\n");
|
|
265
|
+
expect(output).toContain("Enabled");
|
|
266
|
+
expect(output).toContain("get_weather");
|
|
267
|
+
});
|
|
268
|
+
it("should disable a tool", async () => {
|
|
269
|
+
mockFetchResponse(mockFetch, {
|
|
270
|
+
success: true,
|
|
271
|
+
data: { ...mockTool, isEnabled: false },
|
|
272
|
+
message: "Success",
|
|
273
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
274
|
+
});
|
|
275
|
+
const { logs, exitCode } = await runCli(["tools", "disable", "tool_weather123"]);
|
|
276
|
+
expect(exitCode).toBe(0);
|
|
277
|
+
expectFetchCalled(mockFetch, {
|
|
278
|
+
method: "POST",
|
|
279
|
+
path: "/api/v1/tools/tool_weather123/disable"
|
|
280
|
+
});
|
|
281
|
+
const output = logs.join("\n");
|
|
282
|
+
expect(output).toContain("Disabled");
|
|
283
|
+
expect(output).toContain("get_weather");
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
describe("tools categories", () => {
|
|
287
|
+
it("should show tool categories", async () => {
|
|
288
|
+
mockFetchResponse(mockFetch, {
|
|
289
|
+
success: true,
|
|
290
|
+
data: {
|
|
291
|
+
categories: [
|
|
292
|
+
{ tag: "weather", count: 5, enabled: 4 },
|
|
293
|
+
{ tag: "utility", count: 10, enabled: 8 }
|
|
294
|
+
],
|
|
295
|
+
uncategorized: { count: 3, enabled: 2 }
|
|
296
|
+
},
|
|
297
|
+
message: "Success",
|
|
298
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
299
|
+
});
|
|
300
|
+
const { logs, exitCode } = await runCli(["tools", "categories"]);
|
|
301
|
+
expect(exitCode).toBe(0);
|
|
302
|
+
expectFetchCalled(mockFetch, {
|
|
303
|
+
method: "GET",
|
|
304
|
+
path: "/api/v1/tools/categories"
|
|
305
|
+
});
|
|
306
|
+
const output = logs.join("\n");
|
|
307
|
+
expect(output).toContain("weather");
|
|
308
|
+
expect(output).toContain("utility");
|
|
309
|
+
expect(output).toContain("uncategorized");
|
|
310
|
+
});
|
|
311
|
+
it("should output JSON when --json flag is used", async () => {
|
|
312
|
+
mockFetchResponse(mockFetch, {
|
|
313
|
+
success: true,
|
|
314
|
+
data: {
|
|
315
|
+
categories: [{ tag: "weather", count: 5, enabled: 4 }],
|
|
316
|
+
uncategorized: { count: 3, enabled: 2 }
|
|
317
|
+
},
|
|
318
|
+
message: "Success",
|
|
319
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
320
|
+
});
|
|
321
|
+
const { logs, exitCode } = await runCli(["--json", "tools", "categories"]);
|
|
322
|
+
expect(exitCode).toBe(0);
|
|
323
|
+
const output = logs.join("\n");
|
|
324
|
+
const parsed = JSON.parse(output);
|
|
325
|
+
expect(parsed.categories).toHaveLength(1);
|
|
326
|
+
expect(parsed.categories[0].tag).toBe("weather");
|
|
327
|
+
expect(parsed.uncategorized.count).toBe(3);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
//# sourceMappingURL=tools.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/__tests__/tools.test.ts"],"sourcesContent":["/**\n * CLI tools tests — fetch-level mocking\n *\n * Unlike the legacy SDK-mocking approach, these tests stub only\n * `global.fetch`. The real `ChanlSDK` instance runs: it builds the URL,\n * injects auth headers, JSON-encodes the body, normalizes the response\n * envelope, and unwraps single-key shapes. Failures surface as either:\n *\n * - A wrong HTTP path/method/body (CLI handler passed bad args to SDK\n * OR the SDK's URL builder changed)\n * - A malformed response (SDK's unwrap logic changed)\n *\n * Both classes of regression are invisible to SDK-level mocking. That's\n * the whole point of this conversion.\n *\n * Covers `chanl tools list / get / execute / delete / executions /\n * enable / disable / categories` — 14 tests, same names as the previous\n * SDK-mocked version, same CLI output assertions.\n */\n\nimport { describe, it, expect, beforeEach, afterEach, vi, type Mock } from 'vitest';\nimport { configStore } from '../utils/config-store';\nimport {\n runCli,\n mockTool,\n mockExecution,\n installFetchMock,\n mockFetchResponse,\n expectFetchCalled,\n setupFetchConfigStore,\n getFetchUrl,\n type FetchConfigStoreMock,\n} from './helpers';\n\n// DO NOT mock '@chanl/sdk' — we want the real SDK to build URLs + headers\n// from the mocked config store, then hit `global.fetch` which we stub.\n// The only `vi.mock` here is the config store (local state, not network).\nvi.mock('../utils/config-store', () => ({\n configStore: {\n getApiKey: vi.fn(),\n getBaseUrl: vi.fn(),\n getWorkspaceId: vi.fn(),\n getJwtToken: vi.fn(),\n getAuthMethod: vi.fn(() => null),\n getAppUrl: vi.fn(() => 'https://app.chanl.ai'),\n getDeployment: vi.fn(),\n getApiPrefix: vi.fn(),\n setApiKey: vi.fn(),\n setBaseUrl: vi.fn(),\n setWorkspaceId: vi.fn(),\n removeApiKey: vi.fn(),\n clearAuth: vi.fn(),\n delete: vi.fn(),\n isAuthenticated: vi.fn(),\n getPath: vi.fn(() => '/home/user/.chanl/config.json'),\n },\n}));\n\ndescribe('CLI', () => {\n let mockFetch: Mock;\n\n beforeEach(() => {\n vi.clearAllMocks();\n mockFetch = installFetchMock();\n setupFetchConfigStore(configStore as unknown as FetchConfigStoreMock);\n });\n\n afterEach(() => {\n process.exitCode = undefined;\n });\n\n describe('tools list', () => {\n it('should list tools with table output', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: {\n tools: [mockTool],\n total: 1,\n pagination: {\n page: 1,\n limit: 20,\n totalPages: 1,\n hasNext: false,\n hasPrev: false,\n },\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['tools', 'list']);\n\n expect(exitCode).toBe(0);\n // Real SDK hit /api/v1/tools — verify the HTTP layer, not a spy.\n expectFetchCalled(mockFetch, {\n method: 'GET',\n path: '/api/v1/tools',\n });\n\n const output = logs.join('\\n');\n expect(output).toContain('get_weather');\n expect(output).toContain('http');\n expect(output).toContain('Total: 1 tools');\n });\n\n it('should list tools with JSON output', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: {\n tools: [mockTool],\n total: 1,\n pagination: {\n page: 1,\n limit: 20,\n totalPages: 1,\n hasNext: false,\n hasPrev: false,\n },\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['--json', 'tools', 'list']);\n\n expect(exitCode).toBe(0);\n\n const output = logs.join('\\n');\n const parsed = JSON.parse(output);\n expect(parsed.tools).toHaveLength(1);\n expect(parsed.tools[0].name).toBe('get_weather');\n });\n\n it('should filter by type', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: {\n tools: [mockTool],\n total: 1,\n pagination: { page: 1, limit: 20, totalPages: 1, hasNext: false, hasPrev: false },\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n await runCli(['tools', 'list', '--type', 'http']);\n\n // SDK translates filter args to query params. Catches both:\n // (a) CLI passing the wrong arg name to sdk.tools.list\n // (b) SDK changing how it encodes filters\n const url = getFetchUrl(mockFetch);\n expect(url).toContain('type=http');\n });\n\n it('should filter by enabled status', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: {\n tools: [mockTool],\n total: 1,\n pagination: { page: 1, limit: 20, totalPages: 1, hasNext: false, hasPrev: false },\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n await runCli(['tools', 'list', '--enabled']);\n\n const url = getFetchUrl(mockFetch);\n expect(url).toContain('isEnabled=true');\n });\n });\n\n describe('tools get', () => {\n it('should get tool by ID', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: mockTool,\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['tools', 'get', 'tool_weather123']);\n\n expect(exitCode).toBe(0);\n expectFetchCalled(mockFetch, {\n method: 'GET',\n path: '/api/v1/tools/tool_weather123',\n });\n\n const output = logs.join('\\n');\n expect(output).toContain('get_weather');\n expect(output).toContain('http');\n });\n\n it('should output JSON when --json flag is used', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: mockTool,\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['--json', 'tools', 'get', 'tool_weather123']);\n\n expect(exitCode).toBe(0);\n\n const output = logs.join('\\n');\n const parsed = JSON.parse(output);\n expect(parsed.name).toBe('get_weather');\n });\n });\n\n describe('tools execute', () => {\n it('should execute tool with input', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: {\n success: true,\n data: { temperature: '12°C', condition: 'Cloudy' },\n latencyMs: 245,\n traceId: 'trace_abc123',\n executionId: 'exec_abc123',\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli([\n 'tools',\n 'execute',\n 'tool_weather123',\n '--input',\n '{\"city\":\"London\"}',\n ]);\n\n expect(exitCode).toBe(0);\n // Critical contract: CLI passes the raw input object, SDK wraps it\n // in `{ arguments: ... }` before POSTing. Lock that behaviour — if\n // someone refactors the SDK to send the raw object OR changes the\n // field name (`args` vs `arguments`), this test catches it.\n expectFetchCalled(mockFetch, {\n method: 'POST',\n path: '/api/v1/tools/tool_weather123/execute',\n body: { arguments: { city: 'London' } },\n });\n\n const output = logs.join('\\n');\n expect(output).toContain('Success');\n expect(output).toContain('245ms');\n });\n\n it('should handle invalid JSON input', async () => {\n // No fetch response queued — the CLI should reject the bad input\n // locally before it ever hits the SDK → fetch layer.\n const { exitCode, errors } = await runCli([\n 'tools',\n 'execute',\n 'tool_weather123',\n '--input',\n 'not-valid-json',\n ]);\n\n expect(exitCode).toBe(1);\n expect(errors.some((e) => e.includes('Invalid JSON'))).toBe(true);\n // Nothing should have reached fetch.\n expect(mockFetch.mock.calls).toHaveLength(0);\n });\n });\n\n describe('tools delete', () => {\n it('should delete tool with --yes flag', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: { deleted: true },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['tools', 'delete', 'tool_weather123', '--yes']);\n\n expect(exitCode).toBe(0);\n expectFetchCalled(mockFetch, {\n method: 'DELETE',\n path: '/api/v1/tools/tool_weather123',\n });\n\n const output = logs.join('\\n');\n expect(output).toContain('Deleted');\n });\n });\n\n describe('tools executions', () => {\n it('should list executions for a tool', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: {\n data: [mockExecution],\n total: 1,\n page: 1,\n limit: 10,\n pagination: {\n page: 1,\n limit: 10,\n totalPages: 1,\n hasNext: false,\n hasPrev: false,\n },\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['tools', 'executions', 'tool_weather123']);\n\n expect(exitCode).toBe(0);\n // URL path includes the tool id; method is GET.\n const url = getFetchUrl(mockFetch);\n expect(url).toContain('/api/v1/tools/tool_weather123/executions');\n\n const output = logs.join('\\n');\n expect(output).toContain('exec_abc123');\n expect(output).toContain('completed');\n });\n });\n\n describe('tools enable/disable', () => {\n it('should enable a tool', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: { ...mockTool, isEnabled: true },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['tools', 'enable', 'tool_weather123']);\n\n expect(exitCode).toBe(0);\n // Enable/disable are POST to /:id/enable or /:id/disable — not\n // PATCH with a body. Lock that design choice.\n expectFetchCalled(mockFetch, {\n method: 'POST',\n path: '/api/v1/tools/tool_weather123/enable',\n });\n\n const output = logs.join('\\n');\n expect(output).toContain('Enabled');\n expect(output).toContain('get_weather');\n });\n\n it('should disable a tool', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: { ...mockTool, isEnabled: false },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['tools', 'disable', 'tool_weather123']);\n\n expect(exitCode).toBe(0);\n expectFetchCalled(mockFetch, {\n method: 'POST',\n path: '/api/v1/tools/tool_weather123/disable',\n });\n\n const output = logs.join('\\n');\n expect(output).toContain('Disabled');\n expect(output).toContain('get_weather');\n });\n });\n\n describe('tools categories', () => {\n it('should show tool categories', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: {\n categories: [\n { tag: 'weather', count: 5, enabled: 4 },\n { tag: 'utility', count: 10, enabled: 8 },\n ],\n uncategorized: { count: 3, enabled: 2 },\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['tools', 'categories']);\n\n expect(exitCode).toBe(0);\n expectFetchCalled(mockFetch, {\n method: 'GET',\n path: '/api/v1/tools/categories',\n });\n\n const output = logs.join('\\n');\n expect(output).toContain('weather');\n expect(output).toContain('utility');\n expect(output).toContain('uncategorized');\n });\n\n it('should output JSON when --json flag is used', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: {\n categories: [{ tag: 'weather', count: 5, enabled: 4 }],\n uncategorized: { count: 3, enabled: 2 },\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['--json', 'tools', 'categories']);\n\n expect(exitCode).toBe(0);\n\n const output = logs.join('\\n');\n const parsed = JSON.parse(output);\n expect(parsed.categories).toHaveLength(1);\n expect(parsed.categories[0].tag).toBe('weather');\n expect(parsed.uncategorized.count).toBe(3);\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { createProgram } from "../cli";
|
|
3
|
+
import { configStore } from "../utils/config-store";
|
|
4
|
+
import { ChanlSDK } from "@chanl/sdk";
|
|
5
|
+
import {
|
|
6
|
+
runCli,
|
|
7
|
+
makeMockSdk,
|
|
8
|
+
setupAuthenticatedConfigStore,
|
|
9
|
+
mockToolset
|
|
10
|
+
} from "./helpers";
|
|
11
|
+
vi.mock("@chanl/sdk", () => ({
|
|
12
|
+
ChanlSDK: vi.fn()
|
|
13
|
+
}));
|
|
14
|
+
vi.mock("../utils/config-store", () => ({
|
|
15
|
+
configStore: {
|
|
16
|
+
getApiKey: vi.fn(),
|
|
17
|
+
getBaseUrl: vi.fn(() => "https://api.chanl.ai"),
|
|
18
|
+
getWorkspaceId: vi.fn(() => "ws_123"),
|
|
19
|
+
getJwtToken: vi.fn(() => null),
|
|
20
|
+
getAuthMethod: vi.fn(() => null),
|
|
21
|
+
getAppUrl: vi.fn(() => "https://app.chanl.ai"),
|
|
22
|
+
getDeployment: vi.fn(() => "cloud"),
|
|
23
|
+
getApiPrefix: vi.fn(() => "/api/v1"),
|
|
24
|
+
setApiKey: vi.fn(),
|
|
25
|
+
setBaseUrl: vi.fn(),
|
|
26
|
+
setWorkspaceId: vi.fn(),
|
|
27
|
+
removeApiKey: vi.fn(),
|
|
28
|
+
clearAuth: vi.fn(),
|
|
29
|
+
delete: vi.fn(),
|
|
30
|
+
isAuthenticated: vi.fn(),
|
|
31
|
+
getPath: vi.fn(() => "/home/user/.chanl/config.json")
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
describe("CLI", () => {
|
|
35
|
+
let mockSdk;
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
vi.clearAllMocks();
|
|
38
|
+
mockSdk = makeMockSdk();
|
|
39
|
+
ChanlSDK.mockImplementation(() => mockSdk);
|
|
40
|
+
setupAuthenticatedConfigStore(configStore);
|
|
41
|
+
});
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
process.exitCode = void 0;
|
|
44
|
+
});
|
|
45
|
+
describe("toolsets command registration", () => {
|
|
46
|
+
it("should have toolsets command registered", () => {
|
|
47
|
+
const program = createProgram();
|
|
48
|
+
const commandNames = program.commands.map((c) => c.name());
|
|
49
|
+
expect(commandNames).toContain("toolsets");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe("toolsets list", () => {
|
|
53
|
+
it("should list toolsets with table output", async () => {
|
|
54
|
+
mockSdk.toolsets.list.mockResolvedValue({
|
|
55
|
+
success: true,
|
|
56
|
+
data: {
|
|
57
|
+
data: [mockToolset],
|
|
58
|
+
total: 1,
|
|
59
|
+
pagination: {
|
|
60
|
+
page: 1,
|
|
61
|
+
limit: 20,
|
|
62
|
+
totalPages: 1,
|
|
63
|
+
hasNext: false,
|
|
64
|
+
hasPrev: false
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
message: "Success",
|
|
68
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
69
|
+
});
|
|
70
|
+
const { logs, exitCode } = await runCli(["toolsets", "list"]);
|
|
71
|
+
expect(exitCode).toBe(0);
|
|
72
|
+
expect(mockSdk.toolsets.list).toHaveBeenCalledWith(expect.any(Object));
|
|
73
|
+
const output = logs.join("\n");
|
|
74
|
+
expect(output).toContain("customer_support_tools");
|
|
75
|
+
expect(output).toContain("1.0.0");
|
|
76
|
+
expect(output).toContain("Total: 1 toolsets");
|
|
77
|
+
});
|
|
78
|
+
it("should list toolsets with JSON output", async () => {
|
|
79
|
+
mockSdk.toolsets.list.mockResolvedValue({
|
|
80
|
+
success: true,
|
|
81
|
+
data: {
|
|
82
|
+
data: [mockToolset],
|
|
83
|
+
total: 1,
|
|
84
|
+
pagination: {
|
|
85
|
+
page: 1,
|
|
86
|
+
limit: 20,
|
|
87
|
+
totalPages: 1,
|
|
88
|
+
hasNext: false,
|
|
89
|
+
hasPrev: false
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
message: "Success",
|
|
93
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
94
|
+
});
|
|
95
|
+
const { logs, exitCode } = await runCli(["--json", "toolsets", "list"]);
|
|
96
|
+
expect(exitCode).toBe(0);
|
|
97
|
+
const output = logs.join("\n");
|
|
98
|
+
const parsed = JSON.parse(output);
|
|
99
|
+
expect(parsed.toolsets).toHaveLength(1);
|
|
100
|
+
expect(parsed.toolsets[0].name).toBe("customer_support_tools");
|
|
101
|
+
});
|
|
102
|
+
it("should filter by enabled status", async () => {
|
|
103
|
+
mockSdk.toolsets.list.mockResolvedValue({
|
|
104
|
+
success: true,
|
|
105
|
+
data: {
|
|
106
|
+
data: [mockToolset],
|
|
107
|
+
total: 1,
|
|
108
|
+
pagination: { page: 1, limit: 20, totalPages: 1, hasNext: false, hasPrev: false }
|
|
109
|
+
},
|
|
110
|
+
message: "Success",
|
|
111
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
112
|
+
});
|
|
113
|
+
await runCli(["toolsets", "list", "--enabled"]);
|
|
114
|
+
expect(mockSdk.toolsets.list).toHaveBeenCalledWith(
|
|
115
|
+
expect.objectContaining({ enabled: true })
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe("toolsets get", () => {
|
|
120
|
+
it("should get toolset by ID", async () => {
|
|
121
|
+
mockSdk.toolsets.get.mockResolvedValue({
|
|
122
|
+
success: true,
|
|
123
|
+
data: mockToolset,
|
|
124
|
+
message: "Success",
|
|
125
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
126
|
+
});
|
|
127
|
+
const { logs, exitCode } = await runCli(["toolsets", "get", "toolset_abc123"]);
|
|
128
|
+
expect(exitCode).toBe(0);
|
|
129
|
+
expect(mockSdk.toolsets.get).toHaveBeenCalledWith("toolset_abc123");
|
|
130
|
+
const output = logs.join("\n");
|
|
131
|
+
expect(output).toContain("customer_support_tools");
|
|
132
|
+
expect(output).toContain("1.0.0");
|
|
133
|
+
});
|
|
134
|
+
it("should output JSON when --json flag is used", async () => {
|
|
135
|
+
mockSdk.toolsets.get.mockResolvedValue({
|
|
136
|
+
success: true,
|
|
137
|
+
data: mockToolset,
|
|
138
|
+
message: "Success",
|
|
139
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
140
|
+
});
|
|
141
|
+
const { logs, exitCode } = await runCli(["--json", "toolsets", "get", "toolset_abc123"]);
|
|
142
|
+
expect(exitCode).toBe(0);
|
|
143
|
+
const output = logs.join("\n");
|
|
144
|
+
const parsed = JSON.parse(output);
|
|
145
|
+
expect(parsed.name).toBe("customer_support_tools");
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
describe("toolsets enable", () => {
|
|
149
|
+
it("should enable a toolset", async () => {
|
|
150
|
+
mockSdk.toolsets.enable.mockResolvedValue({
|
|
151
|
+
success: true,
|
|
152
|
+
data: { ...mockToolset, enabled: true },
|
|
153
|
+
message: "Success",
|
|
154
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
155
|
+
});
|
|
156
|
+
const { logs, exitCode } = await runCli(["toolsets", "enable", "toolset_abc123"]);
|
|
157
|
+
expect(exitCode).toBe(0);
|
|
158
|
+
expect(mockSdk.toolsets.enable).toHaveBeenCalledWith("toolset_abc123");
|
|
159
|
+
const output = logs.join("\n");
|
|
160
|
+
expect(output).toContain("Enabled");
|
|
161
|
+
expect(output).toContain("customer_support_tools");
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe("toolsets disable", () => {
|
|
165
|
+
it("should disable a toolset", async () => {
|
|
166
|
+
mockSdk.toolsets.disable.mockResolvedValue({
|
|
167
|
+
success: true,
|
|
168
|
+
data: { ...mockToolset, enabled: false },
|
|
169
|
+
message: "Success",
|
|
170
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
171
|
+
});
|
|
172
|
+
const { logs, exitCode } = await runCli(["toolsets", "disable", "toolset_abc123"]);
|
|
173
|
+
expect(exitCode).toBe(0);
|
|
174
|
+
expect(mockSdk.toolsets.disable).toHaveBeenCalledWith("toolset_abc123");
|
|
175
|
+
const output = logs.join("\n");
|
|
176
|
+
expect(output).toContain("Disabled");
|
|
177
|
+
expect(output).toContain("customer_support_tools");
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
describe("toolsets delete", () => {
|
|
181
|
+
it("should delete toolset with --yes flag", async () => {
|
|
182
|
+
mockSdk.toolsets.delete.mockResolvedValue({
|
|
183
|
+
success: true,
|
|
184
|
+
data: { deleted: true },
|
|
185
|
+
message: "Success",
|
|
186
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
187
|
+
});
|
|
188
|
+
const { logs, exitCode } = await runCli(["toolsets", "delete", "toolset_abc123", "--yes"]);
|
|
189
|
+
expect(exitCode).toBe(0);
|
|
190
|
+
expect(mockSdk.toolsets.delete).toHaveBeenCalledWith("toolset_abc123");
|
|
191
|
+
const output = logs.join("\n");
|
|
192
|
+
expect(output).toContain("Deleted");
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
//# sourceMappingURL=toolsets.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/__tests__/toolsets.test.ts"],"sourcesContent":["/**\n * CLI toolsets tests — toolsets list/get/enable/disable/delete + command registration\n */\n\nimport { describe, it, expect, beforeEach, afterEach, vi, Mock } from 'vitest';\nimport { createProgram } from '../cli';\nimport { configStore } from '../utils/config-store';\nimport { ChanlSDK } from '@chanl/sdk';\nimport {\n runCli,\n makeMockSdk,\n setupAuthenticatedConfigStore,\n mockToolset,\n type MockSdk,\n} from './helpers';\n\n// Mock the SDK\nvi.mock('@chanl/sdk', () => ({\n ChanlSDK: vi.fn(),\n}));\n\n// Mock config store\nvi.mock('../utils/config-store', () => ({\n configStore: {\n getApiKey: vi.fn(),\n getBaseUrl: vi.fn(() => 'https://api.chanl.ai'),\n getWorkspaceId: vi.fn(() => 'ws_123'),\n getJwtToken: vi.fn(() => null),\n getAuthMethod: vi.fn(() => null),\n getAppUrl: vi.fn(() => 'https://app.chanl.ai'),\n getDeployment: vi.fn(() => 'cloud'),\n getApiPrefix: vi.fn(() => '/api/v1'),\n setApiKey: vi.fn(),\n setBaseUrl: vi.fn(),\n setWorkspaceId: vi.fn(),\n removeApiKey: vi.fn(),\n clearAuth: vi.fn(),\n delete: vi.fn(),\n isAuthenticated: vi.fn(),\n getPath: vi.fn(() => '/home/user/.chanl/config.json'),\n },\n}));\n\ndescribe('CLI', () => {\n let mockSdk: MockSdk;\n\n beforeEach(() => {\n vi.clearAllMocks();\n mockSdk = makeMockSdk();\n (ChanlSDK as Mock).mockImplementation(() => mockSdk);\n setupAuthenticatedConfigStore(configStore as unknown as {\n getApiKey: Mock;\n getWorkspaceId: Mock;\n isAuthenticated: Mock;\n });\n });\n\n afterEach(() => {\n process.exitCode = undefined;\n });\n\n describe('toolsets command registration', () => {\n it('should have toolsets command registered', () => {\n const program = createProgram();\n const commandNames = program.commands.map((c) => c.name());\n\n expect(commandNames).toContain('toolsets');\n });\n });\n\n describe('toolsets list', () => {\n it('should list toolsets with table output', async () => {\n mockSdk.toolsets.list.mockResolvedValue({\n success: true,\n data: {\n data: [mockToolset],\n total: 1,\n pagination: {\n page: 1,\n limit: 20,\n totalPages: 1,\n hasNext: false,\n hasPrev: false,\n },\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['toolsets', 'list']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.toolsets.list).toHaveBeenCalledWith(expect.any(Object));\n\n const output = logs.join('\\n');\n expect(output).toContain('customer_support_tools');\n expect(output).toContain('1.0.0');\n expect(output).toContain('Total: 1 toolsets');\n });\n\n it('should list toolsets with JSON output', async () => {\n mockSdk.toolsets.list.mockResolvedValue({\n success: true,\n data: {\n data: [mockToolset],\n total: 1,\n pagination: {\n page: 1,\n limit: 20,\n totalPages: 1,\n hasNext: false,\n hasPrev: false,\n },\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['--json', 'toolsets', 'list']);\n\n expect(exitCode).toBe(0);\n\n const output = logs.join('\\n');\n const parsed = JSON.parse(output);\n expect(parsed.toolsets).toHaveLength(1);\n expect(parsed.toolsets[0].name).toBe('customer_support_tools');\n });\n\n it('should filter by enabled status', async () => {\n mockSdk.toolsets.list.mockResolvedValue({\n success: true,\n data: {\n data: [mockToolset],\n total: 1,\n pagination: { page: 1, limit: 20, totalPages: 1, hasNext: false, hasPrev: false },\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n await runCli(['toolsets', 'list', '--enabled']);\n\n expect(mockSdk.toolsets.list).toHaveBeenCalledWith(\n expect.objectContaining({ enabled: true })\n );\n });\n });\n\n describe('toolsets get', () => {\n it('should get toolset by ID', async () => {\n mockSdk.toolsets.get.mockResolvedValue({\n success: true,\n data: mockToolset,\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['toolsets', 'get', 'toolset_abc123']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.toolsets.get).toHaveBeenCalledWith('toolset_abc123');\n\n const output = logs.join('\\n');\n expect(output).toContain('customer_support_tools');\n expect(output).toContain('1.0.0');\n });\n\n it('should output JSON when --json flag is used', async () => {\n mockSdk.toolsets.get.mockResolvedValue({\n success: true,\n data: mockToolset,\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['--json', 'toolsets', 'get', 'toolset_abc123']);\n\n expect(exitCode).toBe(0);\n\n const output = logs.join('\\n');\n const parsed = JSON.parse(output);\n expect(parsed.name).toBe('customer_support_tools');\n });\n });\n\n describe('toolsets enable', () => {\n it('should enable a toolset', async () => {\n mockSdk.toolsets.enable.mockResolvedValue({\n success: true,\n data: { ...mockToolset, enabled: true },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['toolsets', 'enable', 'toolset_abc123']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.toolsets.enable).toHaveBeenCalledWith('toolset_abc123');\n\n const output = logs.join('\\n');\n expect(output).toContain('Enabled');\n expect(output).toContain('customer_support_tools');\n });\n });\n\n describe('toolsets disable', () => {\n it('should disable a toolset', async () => {\n mockSdk.toolsets.disable.mockResolvedValue({\n success: true,\n data: { ...mockToolset, enabled: false },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['toolsets', 'disable', 'toolset_abc123']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.toolsets.disable).toHaveBeenCalledWith('toolset_abc123');\n\n const output = logs.join('\\n');\n expect(output).toContain('Disabled');\n expect(output).toContain('customer_support_tools');\n });\n });\n\n describe('toolsets delete', () => {\n it('should delete toolset with --yes flag', async () => {\n mockSdk.toolsets.delete.mockResolvedValue({\n success: true,\n data: { deleted: true },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['toolsets', 'delete', 'toolset_abc123', '--yes']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.toolsets.delete).toHaveBeenCalledWith('toolset_abc123');\n\n const output = logs.join('\\n');\n expect(output).toContain('Deleted');\n });\n });\n});\n"],"mappings":"AAIA,SAAS,UAAU,IAAI,QAAQ,YAAY,WAAW,UAAgB;AACtE,SAAS,qBAAqB;AAC9B,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAGP,GAAG,KAAK,cAAc,OAAO;AAAA,EAC3B,UAAU,GAAG,GAAG;AAClB,EAAE;AAGF,GAAG,KAAK,yBAAyB,OAAO;AAAA,EACtC,aAAa;AAAA,IACX,WAAW,GAAG,GAAG;AAAA,IACjB,YAAY,GAAG,GAAG,MAAM,sBAAsB;AAAA,IAC9C,gBAAgB,GAAG,GAAG,MAAM,QAAQ;AAAA,IACpC,aAAa,GAAG,GAAG,MAAM,IAAI;AAAA,IAC7B,eAAe,GAAG,GAAG,MAAM,IAAI;AAAA,IAC/B,WAAW,GAAG,GAAG,MAAM,sBAAsB;AAAA,IAC7C,eAAe,GAAG,GAAG,MAAM,OAAO;AAAA,IAClC,cAAc,GAAG,GAAG,MAAM,SAAS;AAAA,IACnC,WAAW,GAAG,GAAG;AAAA,IACjB,YAAY,GAAG,GAAG;AAAA,IAClB,gBAAgB,GAAG,GAAG;AAAA,IACtB,cAAc,GAAG,GAAG;AAAA,IACpB,WAAW,GAAG,GAAG;AAAA,IACjB,QAAQ,GAAG,GAAG;AAAA,IACd,iBAAiB,GAAG,GAAG;AAAA,IACvB,SAAS,GAAG,GAAG,MAAM,+BAA+B;AAAA,EACtD;AACF,EAAE;AAEF,SAAS,OAAO,MAAM;AACpB,MAAI;AAEJ,aAAW,MAAM;AACf,OAAG,cAAc;AACjB,cAAU,YAAY;AACtB,IAAC,SAAkB,mBAAmB,MAAM,OAAO;AACnD,kCAA8B,WAI7B;AAAA,EACH,CAAC;AAED,YAAU,MAAM;AACd,YAAQ,WAAW;AAAA,EACrB,CAAC;AAED,WAAS,iCAAiC,MAAM;AAC9C,OAAG,2CAA2C,MAAM;AAClD,YAAM,UAAU,cAAc;AAC9B,YAAM,eAAe,QAAQ,SAAS,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAEzD,aAAO,YAAY,EAAE,UAAU,UAAU;AAAA,IAC3C,CAAC;AAAA,EACH,CAAC;AAED,WAAS,iBAAiB,MAAM;AAC9B,OAAG,0CAA0C,YAAY;AACvD,cAAQ,SAAS,KAAK,kBAAkB;AAAA,QACtC,SAAS;AAAA,QACT,MAAM;AAAA,UACJ,MAAM,CAAC,WAAW;AAAA,UAClB,OAAO;AAAA,UACP,YAAY;AAAA,YACV,MAAM;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,YACZ,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,QACF;AAAA,QACA,SAAS;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAED,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,OAAO,CAAC,YAAY,MAAM,CAAC;AAE5D,aAAO,QAAQ,EAAE,KAAK,CAAC;AACvB,aAAO,QAAQ,SAAS,IAAI,EAAE,qBAAqB,OAAO,IAAI,MAAM,CAAC;AAErE,YAAM,SAAS,KAAK,KAAK,IAAI;AAC7B,aAAO,MAAM,EAAE,UAAU,wBAAwB;AACjD,aAAO,MAAM,EAAE,UAAU,OAAO;AAChC,aAAO,MAAM,EAAE,UAAU,mBAAmB;AAAA,IAC9C,CAAC;AAED,OAAG,yCAAyC,YAAY;AACtD,cAAQ,SAAS,KAAK,kBAAkB;AAAA,QACtC,SAAS;AAAA,QACT,MAAM;AAAA,UACJ,MAAM,CAAC,WAAW;AAAA,UAClB,OAAO;AAAA,UACP,YAAY;AAAA,YACV,MAAM;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,YACZ,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,QACF;AAAA,QACA,SAAS;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAED,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,OAAO,CAAC,UAAU,YAAY,MAAM,CAAC;AAEtE,aAAO,QAAQ,EAAE,KAAK,CAAC;AAEvB,YAAM,SAAS,KAAK,KAAK,IAAI;AAC7B,YAAM,SAAS,KAAK,MAAM,MAAM;AAChC,aAAO,OAAO,QAAQ,EAAE,aAAa,CAAC;AACtC,aAAO,OAAO,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,wBAAwB;AAAA,IAC/D,CAAC;AAED,OAAG,mCAAmC,YAAY;AAChD,cAAQ,SAAS,KAAK,kBAAkB;AAAA,QACtC,SAAS;AAAA,QACT,MAAM;AAAA,UACJ,MAAM,CAAC,WAAW;AAAA,UAClB,OAAO;AAAA,UACP,YAAY,EAAE,MAAM,GAAG,OAAO,IAAI,YAAY,GAAG,SAAS,OAAO,SAAS,MAAM;AAAA,QAClF;AAAA,QACA,SAAS;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAED,YAAM,OAAO,CAAC,YAAY,QAAQ,WAAW,CAAC;AAE9C,aAAO,QAAQ,SAAS,IAAI,EAAE;AAAA,QAC5B,OAAO,iBAAiB,EAAE,SAAS,KAAK,CAAC;AAAA,MAC3C;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,WAAS,gBAAgB,MAAM;AAC7B,OAAG,4BAA4B,YAAY;AACzC,cAAQ,SAAS,IAAI,kBAAkB;AAAA,QACrC,SAAS;AAAA,QACT,MAAM;AAAA,QACN,SAAS;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAED,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,OAAO,CAAC,YAAY,OAAO,gBAAgB,CAAC;AAE7E,aAAO,QAAQ,EAAE,KAAK,CAAC;AACvB,aAAO,QAAQ,SAAS,GAAG,EAAE,qBAAqB,gBAAgB;AAElE,YAAM,SAAS,KAAK,KAAK,IAAI;AAC7B,aAAO,MAAM,EAAE,UAAU,wBAAwB;AACjD,aAAO,MAAM,EAAE,UAAU,OAAO;AAAA,IAClC,CAAC;AAED,OAAG,+CAA+C,YAAY;AAC5D,cAAQ,SAAS,IAAI,kBAAkB;AAAA,QACrC,SAAS;AAAA,QACT,MAAM;AAAA,QACN,SAAS;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAED,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,OAAO,CAAC,UAAU,YAAY,OAAO,gBAAgB,CAAC;AAEvF,aAAO,QAAQ,EAAE,KAAK,CAAC;AAEvB,YAAM,SAAS,KAAK,KAAK,IAAI;AAC7B,YAAM,SAAS,KAAK,MAAM,MAAM;AAChC,aAAO,OAAO,IAAI,EAAE,KAAK,wBAAwB;AAAA,IACnD,CAAC;AAAA,EACH,CAAC;AAED,WAAS,mBAAmB,MAAM;AAChC,OAAG,2BAA2B,YAAY;AACxC,cAAQ,SAAS,OAAO,kBAAkB;AAAA,QACxC,SAAS;AAAA,QACT,MAAM,EAAE,GAAG,aAAa,SAAS,KAAK;AAAA,QACtC,SAAS;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAED,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,OAAO,CAAC,YAAY,UAAU,gBAAgB,CAAC;AAEhF,aAAO,QAAQ,EAAE,KAAK,CAAC;AACvB,aAAO,QAAQ,SAAS,MAAM,EAAE,qBAAqB,gBAAgB;AAErE,YAAM,SAAS,KAAK,KAAK,IAAI;AAC7B,aAAO,MAAM,EAAE,UAAU,SAAS;AAClC,aAAO,MAAM,EAAE,UAAU,wBAAwB;AAAA,IACnD,CAAC;AAAA,EACH,CAAC;AAED,WAAS,oBAAoB,MAAM;AACjC,OAAG,4BAA4B,YAAY;AACzC,cAAQ,SAAS,QAAQ,kBAAkB;AAAA,QACzC,SAAS;AAAA,QACT,MAAM,EAAE,GAAG,aAAa,SAAS,MAAM;AAAA,QACvC,SAAS;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAED,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,OAAO,CAAC,YAAY,WAAW,gBAAgB,CAAC;AAEjF,aAAO,QAAQ,EAAE,KAAK,CAAC;AACvB,aAAO,QAAQ,SAAS,OAAO,EAAE,qBAAqB,gBAAgB;AAEtE,YAAM,SAAS,KAAK,KAAK,IAAI;AAC7B,aAAO,MAAM,EAAE,UAAU,UAAU;AACnC,aAAO,MAAM,EAAE,UAAU,wBAAwB;AAAA,IACnD,CAAC;AAAA,EACH,CAAC;AAED,WAAS,mBAAmB,MAAM;AAChC,OAAG,yCAAyC,YAAY;AACtD,cAAQ,SAAS,OAAO,kBAAkB;AAAA,QACxC,SAAS;AAAA,QACT,MAAM,EAAE,SAAS,KAAK;AAAA,QACtB,SAAS;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAED,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,OAAO,CAAC,YAAY,UAAU,kBAAkB,OAAO,CAAC;AAEzF,aAAO,QAAQ,EAAE,KAAK,CAAC;AACvB,aAAO,QAAQ,SAAS,MAAM,EAAE,qBAAqB,gBAAgB;AAErE,YAAM,SAAS,KAAK,KAAK,IAAI;AAC7B,aAAO,MAAM,EAAE,UAAU,SAAS;AAAA,IACpC,CAAC;AAAA,EACH,CAAC;AACH,CAAC;","names":[]}
|