@chanl/cli 2.0.4 → 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.
Files changed (59) hide show
  1. package/dist/__tests__/agents.test.js +574 -0
  2. package/dist/__tests__/agents.test.js.map +1 -0
  3. package/dist/__tests__/auth.test.d.ts +2 -0
  4. package/dist/__tests__/auth.test.js +87 -0
  5. package/dist/__tests__/auth.test.js.map +1 -0
  6. package/dist/__tests__/calls.test.d.ts +2 -0
  7. package/dist/__tests__/calls.test.js +115 -0
  8. package/dist/__tests__/calls.test.js.map +1 -0
  9. package/dist/__tests__/chat.test.d.ts +2 -0
  10. package/dist/__tests__/chat.test.js +357 -0
  11. package/dist/__tests__/chat.test.js.map +1 -0
  12. package/dist/__tests__/cli-base.test.d.ts +2 -0
  13. package/dist/__tests__/cli-base.test.js +87 -0
  14. package/dist/__tests__/cli-base.test.js.map +1 -0
  15. package/dist/__tests__/cli-missing.test.d.ts +2 -0
  16. package/dist/__tests__/cli-missing.test.js +546 -0
  17. package/dist/__tests__/cli-missing.test.js.map +1 -0
  18. package/dist/__tests__/helpers.d.ts +234 -0
  19. package/dist/__tests__/helpers.js +314 -0
  20. package/dist/__tests__/helpers.js.map +1 -0
  21. package/dist/__tests__/mcp.test.d.ts +2 -0
  22. package/dist/__tests__/mcp.test.js +470 -0
  23. package/dist/__tests__/mcp.test.js.map +1 -0
  24. package/dist/__tests__/prompts.test.d.ts +2 -0
  25. package/dist/__tests__/prompts.test.js +227 -0
  26. package/dist/__tests__/prompts.test.js.map +1 -0
  27. package/dist/__tests__/scenarios.test.d.ts +2 -0
  28. package/dist/__tests__/scenarios.test.js +270 -0
  29. package/dist/__tests__/scenarios.test.js.map +1 -0
  30. package/dist/__tests__/tools.test.d.ts +2 -0
  31. package/dist/__tests__/tools.test.js +331 -0
  32. package/dist/__tests__/tools.test.js.map +1 -0
  33. package/dist/__tests__/toolsets.test.d.ts +2 -0
  34. package/dist/__tests__/toolsets.test.js +196 -0
  35. package/dist/__tests__/toolsets.test.js.map +1 -0
  36. package/dist/__tests__/workspaces.test.d.ts +2 -0
  37. package/dist/__tests__/workspaces.test.js +155 -0
  38. package/dist/__tests__/workspaces.test.js.map +1 -0
  39. package/dist/commands/agents.js +96 -6
  40. package/dist/commands/agents.js.map +1 -1
  41. package/dist/commands/chat.js +2 -2
  42. package/dist/commands/chat.js.map +1 -1
  43. package/dist/commands/config.js +24 -2
  44. package/dist/commands/config.js.map +1 -1
  45. package/dist/commands/memory.js +134 -1
  46. package/dist/commands/memory.js.map +1 -1
  47. package/dist/commands/prompts.js +148 -1
  48. package/dist/commands/prompts.js.map +1 -1
  49. package/dist/commands/scorecards.js +3 -3
  50. package/dist/commands/scorecards.js.map +1 -1
  51. package/dist/utils/config-store.d.ts +8 -0
  52. package/dist/utils/config-store.js +18 -1
  53. package/dist/utils/config-store.js.map +1 -1
  54. package/dist/utils/sdk-factory.js +15 -3
  55. package/dist/utils/sdk-factory.js.map +1 -1
  56. package/package.json +4 -4
  57. package/dist/__tests__/cli.test.js +0 -2308
  58. package/dist/__tests__/cli.test.js.map +0 -1
  59. /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,2 @@
1
+
2
+ export { }
@@ -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":[]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }