@agent-canvas/cli 0.11.0 → 0.12.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/commands/__tests__/create-folder.test.d.ts +1 -0
- package/dist/commands/__tests__/create-folder.test.js +82 -0
- package/dist/commands/__tests__/delete-folder.test.d.ts +1 -0
- package/dist/commands/__tests__/delete-folder.test.js +79 -0
- package/dist/commands/__tests__/list.test.js +90 -0
- package/dist/commands/__tests__/move-to-folder.test.d.ts +1 -0
- package/dist/commands/__tests__/move-to-folder.test.js +99 -0
- package/dist/commands/create-folder.d.ts +20 -0
- package/dist/commands/create-folder.js +24 -0
- package/dist/commands/delete-folder.d.ts +20 -0
- package/dist/commands/delete-folder.js +24 -0
- package/dist/commands/list.d.ts +3 -1
- package/dist/commands/list.js +38 -12
- package/dist/commands/move-to-folder.d.ts +21 -0
- package/dist/commands/move-to-folder.js +29 -0
- package/dist/index.js +45 -1
- package/dist/lib/protocol.d.ts +31 -0
- package/dist/static/assets/{ar-SA-G6X2FPQ2-Ce08lp2o.js → ar-SA-G6X2FPQ2-DBNnjE12.js} +1 -1
- package/dist/static/assets/{arc-BkHVeepa.js → arc-6RZ6YUY1.js} +1 -1
- package/dist/static/assets/{az-AZ-76LH7QW2-BVPMoIzP.js → az-AZ-76LH7QW2-CEisZhHx.js} +1 -1
- package/dist/static/assets/{bg-BG-XCXSNQG7-CInScHp1.js → bg-BG-XCXSNQG7-DTKl-Qhp.js} +1 -1
- package/dist/static/assets/{blockDiagram-38ab4fdb-Dt3degeL.js → blockDiagram-38ab4fdb-BOQCxa-J.js} +1 -1
- package/dist/static/assets/{bn-BD-2XOGV67Q-CD6tY0zX.js → bn-BD-2XOGV67Q-IHdqEEiU.js} +1 -1
- package/dist/static/assets/{c4Diagram-3d4e48cf-CsBfo8s7.js → c4Diagram-3d4e48cf-BsfS6bbA.js} +1 -1
- package/dist/static/assets/{ca-ES-6MX7JW3Y-BiZWDmu1.js → ca-ES-6MX7JW3Y-BvXW96-a.js} +1 -1
- package/dist/static/assets/channel-CxlHPNx-.js +1 -0
- package/dist/static/assets/{classDiagram-70f12bd4-CqreGe0S.js → classDiagram-70f12bd4-DtEBciUO.js} +1 -1
- package/dist/static/assets/{classDiagram-v2-f2320105-B-fnFJnj.js → classDiagram-v2-f2320105-DhstM95e.js} +1 -1
- package/dist/static/assets/clone-CLXPdWxD.js +1 -0
- package/dist/static/assets/{createText-2e5e7dd3-OugxGj-6.js → createText-2e5e7dd3-CMwfBpkf.js} +1 -1
- package/dist/static/assets/{cs-CZ-2BRQDIVT-C_2Qpntw.js → cs-CZ-2BRQDIVT-CEO7-ad3.js} +1 -1
- package/dist/static/assets/{da-DK-5WZEPLOC-Dv1WhdR5.js → da-DK-5WZEPLOC--SyHMiIr.js} +1 -1
- package/dist/static/assets/{de-DE-XR44H4JA-DQlTqiWP.js → de-DE-XR44H4JA-DFhdbiqr.js} +1 -1
- package/dist/static/assets/{edges-e0da2a9e-dDd1EBCU.js → edges-e0da2a9e-Dt6wNpzA.js} +1 -1
- package/dist/static/assets/{el-GR-BZB4AONW-B7Lao56C.js → el-GR-BZB4AONW-BtqKxae0.js} +1 -1
- package/dist/static/assets/{erDiagram-9861fffd-D4fjZHso.js → erDiagram-9861fffd-D8dTYLmG.js} +1 -1
- package/dist/static/assets/{es-ES-U4NZUMDT-Cn0xmMVA.js → es-ES-U4NZUMDT-CQECcRJK.js} +1 -1
- package/dist/static/assets/{eu-ES-A7QVB2H4-DkHBhAsA.js → eu-ES-A7QVB2H4-DMCSZcU_.js} +1 -1
- package/dist/static/assets/{fa-IR-HGAKTJCU-BM18A3xb.js → fa-IR-HGAKTJCU-CuWGTo8O.js} +1 -1
- package/dist/static/assets/{fi-FI-Z5N7JZ37-2lVAIeWc.js → fi-FI-Z5N7JZ37-B48P0yVO.js} +1 -1
- package/dist/static/assets/{flowDb-956e92f1-BSPjf8yF.js → flowDb-956e92f1-DjuJ2RsZ.js} +1 -1
- package/dist/static/assets/{flowDiagram-66a62f08-BMgRAlPI.js → flowDiagram-66a62f08-CGxdWcr1.js} +1 -1
- package/dist/static/assets/flowDiagram-v2-96b9c2cf-AVP-y8Wc.js +1 -0
- package/dist/static/assets/{flowchart-elk-definition-4a651766-CdvNef4S.js → flowchart-elk-definition-4a651766-CwrQg8bQ.js} +1 -1
- package/dist/static/assets/{fr-FR-RHASNOE6-C4q6bJB9.js → fr-FR-RHASNOE6-kC-2LYF7.js} +1 -1
- package/dist/static/assets/{ganttDiagram-c361ad54-BEfuNuJ-.js → ganttDiagram-c361ad54-BKeezv9l.js} +1 -1
- package/dist/static/assets/{gitGraphDiagram-72cf32ee-CxzHZ4c8.js → gitGraphDiagram-72cf32ee-Dj-thy84.js} +1 -1
- package/dist/static/assets/{gl-ES-HMX3MZ6V-DxNHxjYU.js → gl-ES-HMX3MZ6V-Dqb5t32o.js} +1 -1
- package/dist/static/assets/{graph-CMXJkOFy.js → graph-BVXcMiT7.js} +1 -1
- package/dist/static/assets/{he-IL-6SHJWFNN-Dl8s228h.js → he-IL-6SHJWFNN-sQ-x56Z1.js} +1 -1
- package/dist/static/assets/{hi-IN-IWLTKZ5I-qiaxj6he.js → hi-IN-IWLTKZ5I-D3YHTvwR.js} +1 -1
- package/dist/static/assets/{hu-HU-A5ZG7DT2-Cul9-CZS.js → hu-HU-A5ZG7DT2-C7N2rM7-.js} +1 -1
- package/dist/static/assets/{id-ID-SAP4L64H-he93I50g.js → id-ID-SAP4L64H-CquleGuo.js} +1 -1
- package/dist/static/assets/{index-3862675e-CG4MNtwd.js → index-3862675e-C20q-vC5.js} +1 -1
- package/dist/static/assets/{index-D_54h8Zf.js → index-CPaVz_o7.js} +71 -71
- package/dist/static/assets/{index-D8VjnP-8.js → index-n9PxF2n7.js} +4 -4
- package/dist/static/assets/{infoDiagram-f8f76790-mVkTzQvk.js → infoDiagram-f8f76790-DTzqBsX9.js} +1 -1
- package/dist/static/assets/{it-IT-JPQ66NNP-m_RzGNh7.js → it-IT-JPQ66NNP-DgQ1I_rS.js} +1 -1
- package/dist/static/assets/{ja-JP-DBVTYXUO-jSmANWMZ.js → ja-JP-DBVTYXUO-BDDHsohK.js} +1 -1
- package/dist/static/assets/{journeyDiagram-49397b02-Z_Ho5ZAB.js → journeyDiagram-49397b02-DDWDe9VT.js} +1 -1
- package/dist/static/assets/{kaa-6HZHGXH3-BiV0VYjS.js → kaa-6HZHGXH3-7kU2MZDG.js} +1 -1
- package/dist/static/assets/{kab-KAB-ZGHBKWFO-BT44LKTf.js → kab-KAB-ZGHBKWFO-DxLZR8Xc.js} +1 -1
- package/dist/static/assets/{kk-KZ-P5N5QNE5-DXpgSPnS.js → kk-KZ-P5N5QNE5-CrjN6SZo.js} +1 -1
- package/dist/static/assets/{km-KH-HSX4SM5Z-GreOo74X.js → km-KH-HSX4SM5Z-fXBCY1PI.js} +1 -1
- package/dist/static/assets/{ko-KR-MTYHY66A-B8oviY81.js → ko-KR-MTYHY66A-r0fEGl_8.js} +1 -1
- package/dist/static/assets/{ku-TR-6OUDTVRD-B_KIF7e5.js → ku-TR-6OUDTVRD-QiwK8NVD.js} +1 -1
- package/dist/static/assets/{layout-DXzB1vTB.js → layout-DDTwsdmP.js} +1 -1
- package/dist/static/assets/{line-DdAgkzw5.js → line-vXc1x1Ab.js} +1 -1
- package/dist/static/assets/{linear-BCMJbGyJ.js → linear-BjjGEUdO.js} +1 -1
- package/dist/static/assets/{lt-LT-XHIRWOB4-FrsUgb0b.js → lt-LT-XHIRWOB4-BRXBv4jU.js} +1 -1
- package/dist/static/assets/{lv-LV-5QDEKY6T-BLZxr6R9.js → lv-LV-5QDEKY6T-B_Md2FV-.js} +1 -1
- package/dist/static/assets/{mindmap-definition-fc14e90a-DKyX3emJ.js → mindmap-definition-fc14e90a-C_Rv7XkC.js} +1 -1
- package/dist/static/assets/{mr-IN-CRQNXWMA-Ddfj-qUW.js → mr-IN-CRQNXWMA-BBosvXx_.js} +1 -1
- package/dist/static/assets/{my-MM-5M5IBNSE-CQF313Z3.js → my-MM-5M5IBNSE-bOGsKksC.js} +1 -1
- package/dist/static/assets/{nb-NO-T6EIAALU-Bs9HDcQA.js → nb-NO-T6EIAALU-Sq_cBGBN.js} +1 -1
- package/dist/static/assets/{nl-NL-IS3SIHDZ-5IudGVUP.js → nl-NL-IS3SIHDZ-D2C0R6Ta.js} +1 -1
- package/dist/static/assets/{nn-NO-6E72VCQL-DeZGIXBG.js → nn-NO-6E72VCQL-gS3iXfeO.js} +1 -1
- package/dist/static/assets/{oc-FR-POXYY2M6-DvK0OlJY.js → oc-FR-POXYY2M6-CExAQbrv.js} +1 -1
- package/dist/static/assets/{pa-IN-N4M65BXN-CTt5TGS7.js → pa-IN-N4M65BXN-8dS4bEav.js} +1 -1
- package/dist/static/assets/{pica-Cx-Eb85q.js → pica-DmOZUjq-.js} +1 -1
- package/dist/static/assets/{pieDiagram-8a3498a8-C6pU4z_X.js → pieDiagram-8a3498a8-NdIgX8Qu.js} +1 -1
- package/dist/static/assets/{pl-PL-T2D74RX3-DOeC5J8p.js → pl-PL-T2D74RX3-CyaePQAI.js} +1 -1
- package/dist/static/assets/{pt-BR-5N22H2LF-CaV3hUbn.js → pt-BR-5N22H2LF-DCElnIPA.js} +1 -1
- package/dist/static/assets/{pt-PT-UZXXM6DQ-C7E4Si6G.js → pt-PT-UZXXM6DQ-fiz3tL4V.js} +1 -1
- package/dist/static/assets/{quadrantDiagram-120e2f19-CfcB20VK.js → quadrantDiagram-120e2f19-SzZ20uL9.js} +1 -1
- package/dist/static/assets/{requirementDiagram-deff3bca-ikZaiaAR.js → requirementDiagram-deff3bca-BkESiM5P.js} +1 -1
- package/dist/static/assets/{ro-RO-JPDTUUEW-B0w9YPkY.js → ro-RO-JPDTUUEW-B7Ks_bu3.js} +1 -1
- package/dist/static/assets/{ru-RU-B4JR7IUQ-BhLH0Gn4.js → ru-RU-B4JR7IUQ-BCkgHOqG.js} +1 -1
- package/dist/static/assets/{sankeyDiagram-04a897e0-OB3SO0Dc.js → sankeyDiagram-04a897e0-DO-VaUSa.js} +1 -1
- package/dist/static/assets/{sequenceDiagram-704730f1-D167QCul.js → sequenceDiagram-704730f1-BvITw4ii.js} +1 -1
- package/dist/static/assets/{si-LK-N5RQ5JYF-DmTM35vb.js → si-LK-N5RQ5JYF-DXMX6jRF.js} +1 -1
- package/dist/static/assets/{sk-SK-C5VTKIMK-BqIMGS85.js → sk-SK-C5VTKIMK-mVkvwInm.js} +1 -1
- package/dist/static/assets/{sl-SI-NN7IZMDC-BQSdLR8B.js → sl-SI-NN7IZMDC-yqb-5JGK.js} +1 -1
- package/dist/static/assets/{stateDiagram-587899a1-CuXXxt5C.js → stateDiagram-587899a1-Cb555UUm.js} +1 -1
- package/dist/static/assets/{stateDiagram-v2-d93cdb3a-PBKC0jL5.js → stateDiagram-v2-d93cdb3a-BnSTRfdW.js} +1 -1
- package/dist/static/assets/{styles-6aaf32cf-BwTgMSix.js → styles-6aaf32cf-DmM-xNbN.js} +1 -1
- package/dist/static/assets/{styles-9a916d00-CSnciK5f.js → styles-9a916d00-CQp9r-zI.js} +1 -1
- package/dist/static/assets/{styles-c10674c1-BbjnCqMr.js → styles-c10674c1-DukJEX99.js} +1 -1
- package/dist/static/assets/{subset-shared.chunk-lDC8bbuQ.js → subset-shared.chunk-DxGqiA-P.js} +1 -1
- package/dist/static/assets/{subset-worker.chunk-DPv0zJBp.js → subset-worker.chunk-CgfLr-3s.js} +1 -1
- package/dist/static/assets/{sv-SE-XGPEYMSR-DKHoj3KM.js → sv-SE-XGPEYMSR-D4OA9xsC.js} +1 -1
- package/dist/static/assets/{svgDrawCommon-08f97a94-wMt4MZkg.js → svgDrawCommon-08f97a94-CKUw9D3R.js} +1 -1
- package/dist/static/assets/{ta-IN-2NMHFXQM-DzRF5wNX.js → ta-IN-2NMHFXQM-CGYGb5ad.js} +1 -1
- package/dist/static/assets/{th-TH-HPSO5L25-jF9etk7j.js → th-TH-HPSO5L25-C4-hGx1K.js} +1 -1
- package/dist/static/assets/{timeline-definition-85554ec2-DES-e9Dl.js → timeline-definition-85554ec2-BGkqW2cy.js} +1 -1
- package/dist/static/assets/{tr-TR-DEFEU3FU-BHBwBdDN.js → tr-TR-DEFEU3FU-qfQO0kyM.js} +1 -1
- package/dist/static/assets/{uk-UA-QMV73CPH-CaKBJcwe.js → uk-UA-QMV73CPH-Dim8QSj6.js} +1 -1
- package/dist/static/assets/{vi-VN-M7AON7JQ-B3-vrsch.js → vi-VN-M7AON7JQ-DI9wHHg_.js} +1 -1
- package/dist/static/assets/{xychartDiagram-e933f94c-D5LbRAMn.js → xychartDiagram-e933f94c-C9PQpTDy.js} +1 -1
- package/dist/static/assets/{zh-CN-LNUGB5OW-DHFAxh-M.js → zh-CN-LNUGB5OW-Y-ZGqBc2.js} +1 -1
- package/dist/static/assets/{zh-HK-E62DVLB3-cxLNf1YN.js → zh-HK-E62DVLB3-D56waEAR.js} +1 -1
- package/dist/static/assets/{zh-TW-RAJ6MFWO-DwuDS7A5.js → zh-TW-RAJ6MFWO-B1JfV8AH.js} +1 -1
- package/dist/static/index.html +1 -1
- package/package.json +1 -1
- package/dist/static/assets/channel-LxwPflxn.js +0 -1
- package/dist/static/assets/clone-Du22Wc8Z.js +0 -1
- package/dist/static/assets/flowDiagram-v2-96b9c2cf-BOfGO5-y.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { createFolder } from '../create-folder';
|
|
3
|
+
function createMockClient(sendFn) {
|
|
4
|
+
return {
|
|
5
|
+
send: sendFn ?? vi.fn(() => Promise.resolve({ success: true, category: { id: 'cat-1', name: 'Test', isCollapsed: false, order: 0 } })),
|
|
6
|
+
close: vi.fn(),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function createMockDeps(overrides) {
|
|
10
|
+
return {
|
|
11
|
+
connectToCanvas: vi.fn(() => Promise.resolve(createMockClient())),
|
|
12
|
+
generateId: vi.fn(() => 'test-id'),
|
|
13
|
+
log: vi.fn(),
|
|
14
|
+
error: vi.fn(),
|
|
15
|
+
exit: vi.fn(),
|
|
16
|
+
...overrides,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
describe('createFolder', () => {
|
|
20
|
+
describe('parameter assembly', () => {
|
|
21
|
+
it('should send correct params with folder name', async () => {
|
|
22
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: true, category: { id: 'cat-1', name: 'My Folder', isCollapsed: false, order: 0 } }));
|
|
23
|
+
const mockClient = createMockClient(mockSend);
|
|
24
|
+
const deps = createMockDeps({
|
|
25
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
26
|
+
});
|
|
27
|
+
await createFolder({ name: 'My Folder' }, deps);
|
|
28
|
+
expect(mockSend).toHaveBeenCalledWith(expect.objectContaining({
|
|
29
|
+
type: 'createFolder',
|
|
30
|
+
id: 'test-id',
|
|
31
|
+
params: { name: 'My Folder' },
|
|
32
|
+
}));
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('response handling', () => {
|
|
36
|
+
it('should log success message on success', async () => {
|
|
37
|
+
const mockSend = vi.fn(() => Promise.resolve({
|
|
38
|
+
success: true,
|
|
39
|
+
category: { id: 'cat-1', name: 'My Folder', isCollapsed: false, order: 0 },
|
|
40
|
+
}));
|
|
41
|
+
const mockClient = createMockClient(mockSend);
|
|
42
|
+
const deps = createMockDeps({
|
|
43
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
44
|
+
});
|
|
45
|
+
await createFolder({ name: 'My Folder' }, deps);
|
|
46
|
+
expect(deps.log).toHaveBeenCalledWith('Folder "My Folder" created');
|
|
47
|
+
});
|
|
48
|
+
it('should error and exit on failure response', async () => {
|
|
49
|
+
const mockSend = vi.fn(() => Promise.resolve({
|
|
50
|
+
success: false,
|
|
51
|
+
error: 'Folder already exists',
|
|
52
|
+
}));
|
|
53
|
+
const mockClient = createMockClient(mockSend);
|
|
54
|
+
const deps = createMockDeps({
|
|
55
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
56
|
+
});
|
|
57
|
+
await createFolder({ name: 'Test' }, deps);
|
|
58
|
+
expect(deps.error).toHaveBeenCalledWith('Failed: Folder already exists');
|
|
59
|
+
expect(deps.exit).toHaveBeenCalledWith(1);
|
|
60
|
+
});
|
|
61
|
+
it('should close client after success', async () => {
|
|
62
|
+
const mockClose = vi.fn();
|
|
63
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: true, category: { id: 'cat-1', name: 'Test', isCollapsed: false, order: 0 } }));
|
|
64
|
+
const mockClient = { send: mockSend, close: mockClose };
|
|
65
|
+
const deps = createMockDeps({
|
|
66
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
67
|
+
});
|
|
68
|
+
await createFolder({ name: 'Test' }, deps);
|
|
69
|
+
expect(mockClose).toHaveBeenCalled();
|
|
70
|
+
});
|
|
71
|
+
it('should close client after failure', async () => {
|
|
72
|
+
const mockClose = vi.fn();
|
|
73
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: false, error: 'error' }));
|
|
74
|
+
const mockClient = { send: mockSend, close: mockClose };
|
|
75
|
+
const deps = createMockDeps({
|
|
76
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
77
|
+
});
|
|
78
|
+
await createFolder({ name: 'Test' }, deps);
|
|
79
|
+
expect(mockClose).toHaveBeenCalled();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { deleteFolder } from '../delete-folder';
|
|
3
|
+
function createMockClient(sendFn) {
|
|
4
|
+
return {
|
|
5
|
+
send: sendFn ?? vi.fn(() => Promise.resolve({ success: true })),
|
|
6
|
+
close: vi.fn(),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function createMockDeps(overrides) {
|
|
10
|
+
return {
|
|
11
|
+
connectToCanvas: vi.fn(() => Promise.resolve(createMockClient())),
|
|
12
|
+
generateId: vi.fn(() => 'test-id'),
|
|
13
|
+
log: vi.fn(),
|
|
14
|
+
error: vi.fn(),
|
|
15
|
+
exit: vi.fn(),
|
|
16
|
+
...overrides,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
describe('deleteFolder', () => {
|
|
20
|
+
describe('parameter assembly', () => {
|
|
21
|
+
it('should send correct params with folder name', async () => {
|
|
22
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: true }));
|
|
23
|
+
const mockClient = createMockClient(mockSend);
|
|
24
|
+
const deps = createMockDeps({
|
|
25
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
26
|
+
});
|
|
27
|
+
await deleteFolder({ name: 'My Folder' }, deps);
|
|
28
|
+
expect(mockSend).toHaveBeenCalledWith(expect.objectContaining({
|
|
29
|
+
type: 'deleteFolder',
|
|
30
|
+
id: 'test-id',
|
|
31
|
+
params: { name: 'My Folder' },
|
|
32
|
+
}));
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('response handling', () => {
|
|
36
|
+
it('should log success message on success', async () => {
|
|
37
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: true }));
|
|
38
|
+
const mockClient = createMockClient(mockSend);
|
|
39
|
+
const deps = createMockDeps({
|
|
40
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
41
|
+
});
|
|
42
|
+
await deleteFolder({ name: 'My Folder' }, deps);
|
|
43
|
+
expect(deps.log).toHaveBeenCalledWith('Folder "My Folder" deleted');
|
|
44
|
+
});
|
|
45
|
+
it('should error and exit on failure response', async () => {
|
|
46
|
+
const mockSend = vi.fn(() => Promise.resolve({
|
|
47
|
+
success: false,
|
|
48
|
+
error: 'Folder not found',
|
|
49
|
+
}));
|
|
50
|
+
const mockClient = createMockClient(mockSend);
|
|
51
|
+
const deps = createMockDeps({
|
|
52
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
53
|
+
});
|
|
54
|
+
await deleteFolder({ name: 'Test' }, deps);
|
|
55
|
+
expect(deps.error).toHaveBeenCalledWith('Failed: Folder not found');
|
|
56
|
+
expect(deps.exit).toHaveBeenCalledWith(1);
|
|
57
|
+
});
|
|
58
|
+
it('should close client after success', async () => {
|
|
59
|
+
const mockClose = vi.fn();
|
|
60
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: true }));
|
|
61
|
+
const mockClient = { send: mockSend, close: mockClose };
|
|
62
|
+
const deps = createMockDeps({
|
|
63
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
64
|
+
});
|
|
65
|
+
await deleteFolder({ name: 'Test' }, deps);
|
|
66
|
+
expect(mockClose).toHaveBeenCalled();
|
|
67
|
+
});
|
|
68
|
+
it('should close client after failure', async () => {
|
|
69
|
+
const mockClose = vi.fn();
|
|
70
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: false, error: 'error' }));
|
|
71
|
+
const mockClient = { send: mockSend, close: mockClose };
|
|
72
|
+
const deps = createMockDeps({
|
|
73
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
74
|
+
});
|
|
75
|
+
await deleteFolder({ name: 'Test' }, deps);
|
|
76
|
+
expect(mockClose).toHaveBeenCalled();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -76,6 +76,77 @@ describe('list', () => {
|
|
|
76
76
|
expect(lines[1]).toContain('(updated:');
|
|
77
77
|
});
|
|
78
78
|
});
|
|
79
|
+
describe('folder display', () => {
|
|
80
|
+
it('should display canvases grouped by folder when categories exist', () => {
|
|
81
|
+
const lines = formatCanvasList({
|
|
82
|
+
canvases: [
|
|
83
|
+
{ id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
|
|
84
|
+
{ id: 'c2', name: 'Canvas B', createdAt: 1000, updatedAt: 3000 },
|
|
85
|
+
],
|
|
86
|
+
categories: [
|
|
87
|
+
{ id: 'cat-1', name: 'Work', isCollapsed: false, order: 0 },
|
|
88
|
+
],
|
|
89
|
+
canvasCategoryMap: { 'c1': 'cat-1' },
|
|
90
|
+
});
|
|
91
|
+
expect(lines).toContain(' [F] Work (1)');
|
|
92
|
+
const ungroupedLine = lines.find(l => l.includes('Ungrouped'));
|
|
93
|
+
expect(ungroupedLine).toContain('(1)');
|
|
94
|
+
});
|
|
95
|
+
it('should sort folders by order', () => {
|
|
96
|
+
const lines = formatCanvasList({
|
|
97
|
+
canvases: [
|
|
98
|
+
{ id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
|
|
99
|
+
{ id: 'c2', name: 'Canvas B', createdAt: 1000, updatedAt: 3000 },
|
|
100
|
+
],
|
|
101
|
+
categories: [
|
|
102
|
+
{ id: 'cat-2', name: 'Zebra', isCollapsed: false, order: 1 },
|
|
103
|
+
{ id: 'cat-1', name: 'Alpha', isCollapsed: false, order: 0 },
|
|
104
|
+
],
|
|
105
|
+
canvasCategoryMap: { 'c1': 'cat-1', 'c2': 'cat-2' },
|
|
106
|
+
});
|
|
107
|
+
const alphaIdx = lines.findIndex(l => l.includes('Alpha'));
|
|
108
|
+
const zebraIdx = lines.findIndex(l => l.includes('Zebra'));
|
|
109
|
+
expect(alphaIdx).toBeLessThan(zebraIdx);
|
|
110
|
+
});
|
|
111
|
+
it('should indent canvases inside folders', () => {
|
|
112
|
+
const lines = formatCanvasList({
|
|
113
|
+
canvases: [
|
|
114
|
+
{ id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
|
|
115
|
+
],
|
|
116
|
+
categories: [
|
|
117
|
+
{ id: 'cat-1', name: 'Work', isCollapsed: false, order: 0 },
|
|
118
|
+
],
|
|
119
|
+
canvasCategoryMap: { 'c1': 'cat-1' },
|
|
120
|
+
});
|
|
121
|
+
const canvasLine = lines.find(l => l.includes('Canvas A'));
|
|
122
|
+
expect(canvasLine).toMatch(/^ /);
|
|
123
|
+
});
|
|
124
|
+
it('should not show ungrouped section when all canvases are categorized', () => {
|
|
125
|
+
const lines = formatCanvasList({
|
|
126
|
+
canvases: [
|
|
127
|
+
{ id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
|
|
128
|
+
],
|
|
129
|
+
categories: [
|
|
130
|
+
{ id: 'cat-1', name: 'Work', isCollapsed: false, order: 0 },
|
|
131
|
+
],
|
|
132
|
+
canvasCategoryMap: { 'c1': 'cat-1' },
|
|
133
|
+
});
|
|
134
|
+
const ungroupedLine = lines.find(l => l.includes('Ungrouped'));
|
|
135
|
+
expect(ungroupedLine).toBeUndefined();
|
|
136
|
+
});
|
|
137
|
+
it('should fall back to flat list when no categories', () => {
|
|
138
|
+
const lines = formatCanvasList({
|
|
139
|
+
canvases: [
|
|
140
|
+
{ id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
|
|
141
|
+
{ id: 'c2', name: 'Canvas B', createdAt: 1000, updatedAt: 3000 },
|
|
142
|
+
],
|
|
143
|
+
categories: [],
|
|
144
|
+
canvasCategoryMap: {},
|
|
145
|
+
});
|
|
146
|
+
expect(lines).toHaveLength(3);
|
|
147
|
+
expect(lines.find(l => l.includes('[F]'))).toBeUndefined();
|
|
148
|
+
});
|
|
149
|
+
});
|
|
79
150
|
// ==================== Response Handling Tests ====================
|
|
80
151
|
describe('response handling', () => {
|
|
81
152
|
it('should log each formatted line on success', async () => {
|
|
@@ -130,5 +201,24 @@ describe('list', () => {
|
|
|
130
201
|
await list(deps);
|
|
131
202
|
expect(mockClose).toHaveBeenCalled();
|
|
132
203
|
});
|
|
204
|
+
it('should pass categories and canvasCategoryMap from response', async () => {
|
|
205
|
+
const mockSend = vi.fn(() => Promise.resolve({
|
|
206
|
+
success: true,
|
|
207
|
+
activeCanvasId: 'c1',
|
|
208
|
+
canvases: [
|
|
209
|
+
{ id: 'c1', name: 'Canvas A', createdAt: 1000, updatedAt: 2000 },
|
|
210
|
+
],
|
|
211
|
+
categories: [
|
|
212
|
+
{ id: 'cat-1', name: 'Work', isCollapsed: false, order: 0 },
|
|
213
|
+
],
|
|
214
|
+
canvasCategoryMap: { 'c1': 'cat-1' },
|
|
215
|
+
}));
|
|
216
|
+
const mockClient = createMockClient(mockSend);
|
|
217
|
+
const deps = createMockDeps({
|
|
218
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
219
|
+
});
|
|
220
|
+
await list(deps);
|
|
221
|
+
expect(deps.log).toHaveBeenCalledWith(expect.stringContaining('[F] Work'));
|
|
222
|
+
});
|
|
133
223
|
});
|
|
134
224
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { moveToFolder } from '../move-to-folder';
|
|
3
|
+
function createMockClient(sendFn) {
|
|
4
|
+
return {
|
|
5
|
+
send: sendFn ?? vi.fn(() => Promise.resolve({ success: true })),
|
|
6
|
+
close: vi.fn(),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function createMockDeps(overrides) {
|
|
10
|
+
return {
|
|
11
|
+
connectToCanvas: vi.fn(() => Promise.resolve(createMockClient())),
|
|
12
|
+
generateId: vi.fn(() => 'test-id'),
|
|
13
|
+
log: vi.fn(),
|
|
14
|
+
error: vi.fn(),
|
|
15
|
+
exit: vi.fn(),
|
|
16
|
+
...overrides,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
describe('moveToFolder', () => {
|
|
20
|
+
describe('parameter assembly', () => {
|
|
21
|
+
it('should send correct params when moving to a folder', async () => {
|
|
22
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: true }));
|
|
23
|
+
const mockClient = createMockClient(mockSend);
|
|
24
|
+
const deps = createMockDeps({
|
|
25
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
26
|
+
});
|
|
27
|
+
await moveToFolder({ canvasName: 'My Canvas', folderName: 'My Folder' }, deps);
|
|
28
|
+
expect(mockSend).toHaveBeenCalledWith(expect.objectContaining({
|
|
29
|
+
type: 'moveCanvasToFolder',
|
|
30
|
+
id: 'test-id',
|
|
31
|
+
params: { canvasName: 'My Canvas', folderName: 'My Folder' },
|
|
32
|
+
}));
|
|
33
|
+
});
|
|
34
|
+
it('should send null folderName when moving to ungrouped', async () => {
|
|
35
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: true }));
|
|
36
|
+
const mockClient = createMockClient(mockSend);
|
|
37
|
+
const deps = createMockDeps({
|
|
38
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
39
|
+
});
|
|
40
|
+
await moveToFolder({ canvasName: 'My Canvas', folderName: null }, deps);
|
|
41
|
+
expect(mockSend).toHaveBeenCalledWith(expect.objectContaining({
|
|
42
|
+
params: { canvasName: 'My Canvas', folderName: null },
|
|
43
|
+
}));
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe('response handling', () => {
|
|
47
|
+
it('should log success message when moved to folder', async () => {
|
|
48
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: true }));
|
|
49
|
+
const mockClient = createMockClient(mockSend);
|
|
50
|
+
const deps = createMockDeps({
|
|
51
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
52
|
+
});
|
|
53
|
+
await moveToFolder({ canvasName: 'My Canvas', folderName: 'My Folder' }, deps);
|
|
54
|
+
expect(deps.log).toHaveBeenCalledWith('Canvas "My Canvas" moved to folder "My Folder"');
|
|
55
|
+
});
|
|
56
|
+
it('should log ungrouped message when folderName is null', async () => {
|
|
57
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: true }));
|
|
58
|
+
const mockClient = createMockClient(mockSend);
|
|
59
|
+
const deps = createMockDeps({
|
|
60
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
61
|
+
});
|
|
62
|
+
await moveToFolder({ canvasName: 'My Canvas', folderName: null }, deps);
|
|
63
|
+
expect(deps.log).toHaveBeenCalledWith('Canvas "My Canvas" moved to ungrouped');
|
|
64
|
+
});
|
|
65
|
+
it('should error and exit on failure response', async () => {
|
|
66
|
+
const mockSend = vi.fn(() => Promise.resolve({
|
|
67
|
+
success: false,
|
|
68
|
+
error: 'Canvas not found',
|
|
69
|
+
}));
|
|
70
|
+
const mockClient = createMockClient(mockSend);
|
|
71
|
+
const deps = createMockDeps({
|
|
72
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
73
|
+
});
|
|
74
|
+
await moveToFolder({ canvasName: 'Test', folderName: 'Folder' }, deps);
|
|
75
|
+
expect(deps.error).toHaveBeenCalledWith('Failed: Canvas not found');
|
|
76
|
+
expect(deps.exit).toHaveBeenCalledWith(1);
|
|
77
|
+
});
|
|
78
|
+
it('should close client after success', async () => {
|
|
79
|
+
const mockClose = vi.fn();
|
|
80
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: true }));
|
|
81
|
+
const mockClient = { send: mockSend, close: mockClose };
|
|
82
|
+
const deps = createMockDeps({
|
|
83
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
84
|
+
});
|
|
85
|
+
await moveToFolder({ canvasName: 'Test', folderName: 'Folder' }, deps);
|
|
86
|
+
expect(mockClose).toHaveBeenCalled();
|
|
87
|
+
});
|
|
88
|
+
it('should close client after failure', async () => {
|
|
89
|
+
const mockClose = vi.fn();
|
|
90
|
+
const mockSend = vi.fn(() => Promise.resolve({ success: false, error: 'error' }));
|
|
91
|
+
const mockClient = { send: mockSend, close: mockClose };
|
|
92
|
+
const deps = createMockDeps({
|
|
93
|
+
connectToCanvas: vi.fn(() => Promise.resolve(mockClient)),
|
|
94
|
+
});
|
|
95
|
+
await moveToFolder({ canvasName: 'Test', folderName: 'Folder' }, deps);
|
|
96
|
+
expect(mockClose).toHaveBeenCalled();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface CreateFolderOptions {
|
|
2
|
+
name: string;
|
|
3
|
+
}
|
|
4
|
+
export interface CreateFolderClient {
|
|
5
|
+
send: <T>(request: {
|
|
6
|
+
type: string;
|
|
7
|
+
id: string;
|
|
8
|
+
params?: unknown;
|
|
9
|
+
}) => Promise<T>;
|
|
10
|
+
close: () => void;
|
|
11
|
+
}
|
|
12
|
+
export interface CreateFolderDeps {
|
|
13
|
+
connectToCanvas: () => Promise<CreateFolderClient>;
|
|
14
|
+
generateId: () => string;
|
|
15
|
+
log: (msg: string) => void;
|
|
16
|
+
error: (msg: string) => void;
|
|
17
|
+
exit: (code: number) => never | void;
|
|
18
|
+
}
|
|
19
|
+
export declare const defaultDeps: CreateFolderDeps;
|
|
20
|
+
export declare function createFolder(options: CreateFolderOptions, deps?: CreateFolderDeps): Promise<void>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { connectToCanvas, generateId } from '../lib/ws-client.js';
|
|
2
|
+
export const defaultDeps = {
|
|
3
|
+
connectToCanvas,
|
|
4
|
+
generateId,
|
|
5
|
+
log: console.log,
|
|
6
|
+
error: console.error,
|
|
7
|
+
exit: process.exit,
|
|
8
|
+
};
|
|
9
|
+
export async function createFolder(options, deps = defaultDeps) {
|
|
10
|
+
const client = await deps.connectToCanvas();
|
|
11
|
+
const result = await client.send({
|
|
12
|
+
type: 'createFolder',
|
|
13
|
+
id: deps.generateId(),
|
|
14
|
+
params: { name: options.name },
|
|
15
|
+
});
|
|
16
|
+
if (result.success && result.category) {
|
|
17
|
+
deps.log(`Folder "${result.category.name}" created`);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
deps.error(`Failed: ${result.error}`);
|
|
21
|
+
deps.exit(1);
|
|
22
|
+
}
|
|
23
|
+
client.close();
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface DeleteFolderOptions {
|
|
2
|
+
name: string;
|
|
3
|
+
}
|
|
4
|
+
export interface DeleteFolderClient {
|
|
5
|
+
send: <T>(request: {
|
|
6
|
+
type: string;
|
|
7
|
+
id: string;
|
|
8
|
+
params?: unknown;
|
|
9
|
+
}) => Promise<T>;
|
|
10
|
+
close: () => void;
|
|
11
|
+
}
|
|
12
|
+
export interface DeleteFolderDeps {
|
|
13
|
+
connectToCanvas: () => Promise<DeleteFolderClient>;
|
|
14
|
+
generateId: () => string;
|
|
15
|
+
log: (msg: string) => void;
|
|
16
|
+
error: (msg: string) => void;
|
|
17
|
+
exit: (code: number) => never | void;
|
|
18
|
+
}
|
|
19
|
+
export declare const defaultDeps: DeleteFolderDeps;
|
|
20
|
+
export declare function deleteFolder(options: DeleteFolderOptions, deps?: DeleteFolderDeps): Promise<void>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { connectToCanvas, generateId } from '../lib/ws-client.js';
|
|
2
|
+
export const defaultDeps = {
|
|
3
|
+
connectToCanvas,
|
|
4
|
+
generateId,
|
|
5
|
+
log: console.log,
|
|
6
|
+
error: console.error,
|
|
7
|
+
exit: process.exit,
|
|
8
|
+
};
|
|
9
|
+
export async function deleteFolder(options, deps = defaultDeps) {
|
|
10
|
+
const client = await deps.connectToCanvas();
|
|
11
|
+
const result = await client.send({
|
|
12
|
+
type: 'deleteFolder',
|
|
13
|
+
id: deps.generateId(),
|
|
14
|
+
params: { name: options.name },
|
|
15
|
+
});
|
|
16
|
+
if (result.success) {
|
|
17
|
+
deps.log(`Folder "${options.name}" deleted`);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
deps.error(`Failed: ${result.error}`);
|
|
21
|
+
deps.exit(1);
|
|
22
|
+
}
|
|
23
|
+
client.close();
|
|
24
|
+
}
|
package/dist/commands/list.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CanvasMetadata } from '../lib/protocol.js';
|
|
1
|
+
import type { CanvasMetadata, CanvasCategory } from '../lib/protocol.js';
|
|
2
2
|
export interface ListClient {
|
|
3
3
|
send: <T>(request: {
|
|
4
4
|
type: string;
|
|
@@ -18,6 +18,8 @@ export interface ListResult {
|
|
|
18
18
|
activeCanvasId?: string;
|
|
19
19
|
agentActiveCanvasId?: string;
|
|
20
20
|
canvases: CanvasMetadata[];
|
|
21
|
+
categories?: CanvasCategory[];
|
|
22
|
+
canvasCategoryMap?: Record<string, string>;
|
|
21
23
|
}
|
|
22
24
|
export declare function formatCanvasList(result: ListResult): string[];
|
|
23
25
|
export declare function list(deps?: ListDeps): Promise<void>;
|
package/dist/commands/list.js
CHANGED
|
@@ -6,23 +6,47 @@ export const defaultDeps = {
|
|
|
6
6
|
error: console.error,
|
|
7
7
|
exit: process.exit,
|
|
8
8
|
};
|
|
9
|
+
function getMarker(canvas, activeCanvasId, agentActiveCanvasId) {
|
|
10
|
+
const isUser = canvas.id === activeCanvasId;
|
|
11
|
+
const isAgent = canvas.id === agentActiveCanvasId;
|
|
12
|
+
if (isUser && isAgent)
|
|
13
|
+
return '[UA]';
|
|
14
|
+
if (isUser)
|
|
15
|
+
return '[U] ';
|
|
16
|
+
if (isAgent)
|
|
17
|
+
return '[A] ';
|
|
18
|
+
return ' ';
|
|
19
|
+
}
|
|
20
|
+
function formatCanvasLine(canvas, activeCanvasId, agentActiveCanvasId, indent = '') {
|
|
21
|
+
const marker = getMarker(canvas, activeCanvasId, agentActiveCanvasId);
|
|
22
|
+
const date = new Date(canvas.updatedAt).toLocaleString();
|
|
23
|
+
return `${indent}${marker} ${canvas.name} (updated: ${date})`;
|
|
24
|
+
}
|
|
9
25
|
export function formatCanvasList(result) {
|
|
10
26
|
const lines = ['Canvases: ([U]=User [A]=Agent)'];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
marker = '[UA]';
|
|
27
|
+
const categories = result.categories || [];
|
|
28
|
+
const categoryMap = result.canvasCategoryMap || {};
|
|
29
|
+
if (categories.length === 0) {
|
|
30
|
+
for (const canvas of result.canvases) {
|
|
31
|
+
lines.push(formatCanvasLine(canvas, result.activeCanvasId, result.agentActiveCanvasId));
|
|
17
32
|
}
|
|
18
|
-
|
|
19
|
-
|
|
33
|
+
return lines;
|
|
34
|
+
}
|
|
35
|
+
const categorizedCanvasIds = new Set(Object.keys(categoryMap));
|
|
36
|
+
const sorted = [...categories].sort((a, b) => a.order - b.order);
|
|
37
|
+
for (const cat of sorted) {
|
|
38
|
+
const catCanvases = result.canvases.filter(c => categoryMap[c.id] === cat.id);
|
|
39
|
+
lines.push(` [F] ${cat.name} (${catCanvases.length})`);
|
|
40
|
+
for (const canvas of catCanvases) {
|
|
41
|
+
lines.push(formatCanvasLine(canvas, result.activeCanvasId, result.agentActiveCanvasId, ' '));
|
|
20
42
|
}
|
|
21
|
-
|
|
22
|
-
|
|
43
|
+
}
|
|
44
|
+
const ungrouped = result.canvases.filter(c => !categorizedCanvasIds.has(c.id));
|
|
45
|
+
if (ungrouped.length > 0) {
|
|
46
|
+
lines.push(` Ungrouped (${ungrouped.length})`);
|
|
47
|
+
for (const canvas of ungrouped) {
|
|
48
|
+
lines.push(formatCanvasLine(canvas, result.activeCanvasId, result.agentActiveCanvasId, ' '));
|
|
23
49
|
}
|
|
24
|
-
const date = new Date(canvas.updatedAt).toLocaleString();
|
|
25
|
-
lines.push(`${marker} ${canvas.name} (updated: ${date})`);
|
|
26
50
|
}
|
|
27
51
|
return lines;
|
|
28
52
|
}
|
|
@@ -37,6 +61,8 @@ export async function list(deps = defaultDeps) {
|
|
|
37
61
|
activeCanvasId: result.activeCanvasId,
|
|
38
62
|
agentActiveCanvasId: result.agentActiveCanvasId,
|
|
39
63
|
canvases: result.canvases,
|
|
64
|
+
categories: result.categories,
|
|
65
|
+
canvasCategoryMap: result.canvasCategoryMap,
|
|
40
66
|
});
|
|
41
67
|
for (const line of lines) {
|
|
42
68
|
deps.log(line);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface MoveToFolderOptions {
|
|
2
|
+
canvasName: string;
|
|
3
|
+
folderName: string | null;
|
|
4
|
+
}
|
|
5
|
+
export interface MoveToFolderClient {
|
|
6
|
+
send: <T>(request: {
|
|
7
|
+
type: string;
|
|
8
|
+
id: string;
|
|
9
|
+
params?: unknown;
|
|
10
|
+
}) => Promise<T>;
|
|
11
|
+
close: () => void;
|
|
12
|
+
}
|
|
13
|
+
export interface MoveToFolderDeps {
|
|
14
|
+
connectToCanvas: () => Promise<MoveToFolderClient>;
|
|
15
|
+
generateId: () => string;
|
|
16
|
+
log: (msg: string) => void;
|
|
17
|
+
error: (msg: string) => void;
|
|
18
|
+
exit: (code: number) => never | void;
|
|
19
|
+
}
|
|
20
|
+
export declare const defaultDeps: MoveToFolderDeps;
|
|
21
|
+
export declare function moveToFolder(options: MoveToFolderOptions, deps?: MoveToFolderDeps): Promise<void>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { connectToCanvas, generateId } from '../lib/ws-client.js';
|
|
2
|
+
export const defaultDeps = {
|
|
3
|
+
connectToCanvas,
|
|
4
|
+
generateId,
|
|
5
|
+
log: console.log,
|
|
6
|
+
error: console.error,
|
|
7
|
+
exit: process.exit,
|
|
8
|
+
};
|
|
9
|
+
export async function moveToFolder(options, deps = defaultDeps) {
|
|
10
|
+
const client = await deps.connectToCanvas();
|
|
11
|
+
const result = await client.send({
|
|
12
|
+
type: 'moveCanvasToFolder',
|
|
13
|
+
id: deps.generateId(),
|
|
14
|
+
params: { canvasName: options.canvasName, folderName: options.folderName },
|
|
15
|
+
});
|
|
16
|
+
if (result.success) {
|
|
17
|
+
if (options.folderName) {
|
|
18
|
+
deps.log(`Canvas "${options.canvasName}" moved to folder "${options.folderName}"`);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
deps.log(`Canvas "${options.canvasName}" moved to ungrouped`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
deps.error(`Failed: ${result.error}`);
|
|
26
|
+
deps.exit(1);
|
|
27
|
+
}
|
|
28
|
+
client.close();
|
|
29
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -21,11 +21,14 @@ import { list, defaultDeps as listDeps } from './commands/list.js';
|
|
|
21
21
|
import { newCanvas, defaultDeps as newCanvasDeps } from './commands/new-canvas.js';
|
|
22
22
|
import { useCanvas, defaultDeps as useCanvasDeps } from './commands/use-canvas.js';
|
|
23
23
|
import { renameCanvas, defaultDeps as renameCanvasDeps } from './commands/rename-canvas.js';
|
|
24
|
+
import { createFolder, defaultDeps as createFolderDeps } from './commands/create-folder.js';
|
|
25
|
+
import { deleteFolder, defaultDeps as deleteFolderDeps } from './commands/delete-folder.js';
|
|
26
|
+
import { moveToFolder, defaultDeps as moveToFolderDeps } from './commands/move-to-folder.js';
|
|
24
27
|
const program = new Command();
|
|
25
28
|
program
|
|
26
29
|
.name('agent-canvas')
|
|
27
30
|
.description('CLI for Agent Canvas - Excalidraw interface for AI agents')
|
|
28
|
-
.version('0.
|
|
31
|
+
.version('0.12.0');
|
|
29
32
|
program
|
|
30
33
|
.command('start')
|
|
31
34
|
.description('Start the canvas server and open in browser')
|
|
@@ -388,4 +391,45 @@ program
|
|
|
388
391
|
.action(async (name) => {
|
|
389
392
|
await renameCanvas({ newName: name }, renameCanvasDeps);
|
|
390
393
|
});
|
|
394
|
+
// ============================================================================
|
|
395
|
+
// Create Folder
|
|
396
|
+
// ============================================================================
|
|
397
|
+
program
|
|
398
|
+
.command('create-folder')
|
|
399
|
+
.description('Create a new folder for organizing canvases')
|
|
400
|
+
.requiredOption('-n, --name <name>', 'Folder name')
|
|
401
|
+
.action(async (options) => {
|
|
402
|
+
await createFolder({ name: options.name }, createFolderDeps);
|
|
403
|
+
});
|
|
404
|
+
// ============================================================================
|
|
405
|
+
// Delete Folder
|
|
406
|
+
// ============================================================================
|
|
407
|
+
program
|
|
408
|
+
.command('delete-folder')
|
|
409
|
+
.description('Delete a folder (canvases in it become ungrouped)')
|
|
410
|
+
.argument('<name>', 'Folder name to delete')
|
|
411
|
+
.action(async (name) => {
|
|
412
|
+
await deleteFolder({ name }, deleteFolderDeps);
|
|
413
|
+
});
|
|
414
|
+
// ============================================================================
|
|
415
|
+
// Move to Folder
|
|
416
|
+
// ============================================================================
|
|
417
|
+
program
|
|
418
|
+
.command('move-to-folder')
|
|
419
|
+
.description('Move a canvas to a folder, or to ungrouped')
|
|
420
|
+
.argument('<canvas-name>', 'Canvas name to move')
|
|
421
|
+
.argument('[folder-name]', 'Target folder name (omit with --ungrouped to remove from folder)')
|
|
422
|
+
.option('--ungrouped', 'Move canvas out of its folder to ungrouped')
|
|
423
|
+
.action(async (canvasName, folderName, options) => {
|
|
424
|
+
if (options.ungrouped) {
|
|
425
|
+
await moveToFolder({ canvasName, folderName: null }, moveToFolderDeps);
|
|
426
|
+
}
|
|
427
|
+
else if (folderName) {
|
|
428
|
+
await moveToFolder({ canvasName, folderName }, moveToFolderDeps);
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
console.error('Error: provide a folder name or use --ungrouped');
|
|
432
|
+
process.exit(1);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
391
435
|
program.parse();
|