@assistkick/create 1.7.0 → 1.9.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 (206) hide show
  1. package/dist/bin/create.js +0 -0
  2. package/package.json +9 -7
  3. package/templates/assistkick-product-system/.env.example +1 -0
  4. package/templates/assistkick-product-system/local.db +0 -0
  5. package/templates/assistkick-product-system/package.json +4 -2
  6. package/templates/assistkick-product-system/packages/backend/package.json +2 -0
  7. package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +165 -0
  8. package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +358 -0
  9. package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +356 -0
  10. package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +96 -1
  11. package/templates/assistkick-product-system/packages/backend/src/routes/graph.ts +1 -0
  12. package/templates/assistkick-product-system/packages/backend/src/routes/kanban.ts +61 -6
  13. package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +200 -84
  14. package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +6 -3
  15. package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +53 -17
  16. package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +218 -0
  17. package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +119 -0
  18. package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +158 -0
  19. package/templates/assistkick-product-system/packages/backend/src/server.ts +60 -9
  20. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +489 -0
  21. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +416 -0
  22. package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.test.ts +189 -0
  23. package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.ts +182 -0
  24. package/templates/assistkick-product-system/packages/backend/src/services/init.ts +43 -77
  25. package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +16 -0
  26. package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +73 -2
  27. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +4 -4
  28. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +87 -11
  29. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +210 -69
  30. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +210 -215
  31. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +162 -0
  32. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +148 -0
  33. package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +11 -5
  34. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.test.ts +64 -0
  35. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +134 -0
  36. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.test.ts +256 -0
  37. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +258 -0
  38. package/templates/assistkick-product-system/packages/backend/src/services/workflow_group_service.ts +106 -0
  39. package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.test.ts +275 -0
  40. package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.ts +245 -0
  41. package/templates/assistkick-product-system/packages/frontend/package-lock.json +3455 -0
  42. package/templates/assistkick-product-system/packages/frontend/package.json +6 -0
  43. package/templates/assistkick-product-system/packages/frontend/src/App.tsx +8 -0
  44. package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +458 -18
  45. package/templates/assistkick-product-system/packages/frontend/src/api/client_files.test.ts +172 -0
  46. package/templates/assistkick-product-system/packages/frontend/src/api/client_video.test.ts +238 -0
  47. package/templates/assistkick-product-system/packages/frontend/src/components/AgentsView.tsx +307 -0
  48. package/templates/assistkick-product-system/packages/frontend/src/components/CoherenceView.tsx +82 -66
  49. package/templates/assistkick-product-system/packages/frontend/src/components/CompositionPlaceholder.tsx +97 -0
  50. package/templates/assistkick-product-system/packages/frontend/src/components/DesignSystemView.tsx +20 -0
  51. package/templates/assistkick-product-system/packages/frontend/src/components/EditorTabBar.tsx +57 -0
  52. package/templates/assistkick-product-system/packages/frontend/src/components/FileTree.tsx +313 -0
  53. package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeContextMenu.tsx +61 -0
  54. package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeInlineInput.tsx +73 -0
  55. package/templates/assistkick-product-system/packages/frontend/src/components/FilesView.tsx +404 -0
  56. package/templates/assistkick-product-system/packages/frontend/src/components/GitRepoModal.tsx +187 -56
  57. package/templates/assistkick-product-system/packages/frontend/src/components/GraphLegend.tsx +71 -73
  58. package/templates/assistkick-product-system/packages/frontend/src/components/GraphSettings.tsx +8 -8
  59. package/templates/assistkick-product-system/packages/frontend/src/components/GraphView.tsx +1 -1
  60. package/templates/assistkick-product-system/packages/frontend/src/components/InviteUserDialog.tsx +15 -11
  61. package/templates/assistkick-product-system/packages/frontend/src/components/IterationCommentModal.tsx +80 -0
  62. package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +263 -167
  63. package/templates/assistkick-product-system/packages/frontend/src/components/LoginPage.tsx +14 -14
  64. package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +54 -33
  65. package/templates/assistkick-product-system/packages/frontend/src/components/QaIssueSheet.tsx +32 -49
  66. package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +43 -48
  67. package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +121 -52
  68. package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +20 -14
  69. package/templates/assistkick-product-system/packages/frontend/src/components/UsersView.tsx +52 -52
  70. package/templates/assistkick-product-system/packages/frontend/src/components/VideoGallery.tsx +313 -0
  71. package/templates/assistkick-product-system/packages/frontend/src/components/VideographyView.tsx +250 -0
  72. package/templates/assistkick-product-system/packages/frontend/src/components/WorkflowsView.tsx +474 -0
  73. package/templates/assistkick-product-system/packages/frontend/src/components/ds/AccentBorderList.tsx +53 -0
  74. package/templates/assistkick-product-system/packages/frontend/src/components/ds/Button.tsx +87 -0
  75. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonGroup.tsx +29 -0
  76. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonShowcase.tsx +221 -0
  77. package/templates/assistkick-product-system/packages/frontend/src/components/ds/CardGlass.tsx +141 -0
  78. package/templates/assistkick-product-system/packages/frontend/src/components/ds/CompletionRing.tsx +30 -0
  79. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ContentCard.tsx +34 -0
  80. package/templates/assistkick-product-system/packages/frontend/src/components/ds/IconButton.tsx +74 -0
  81. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCard.tsx +103 -87
  82. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCardShowcase.tsx +9 -188
  83. package/templates/assistkick-product-system/packages/frontend/src/components/ds/Kbd.tsx +11 -0
  84. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KindBadge.tsx +21 -0
  85. package/templates/assistkick-product-system/packages/frontend/src/components/ds/NavBarSidekick.tsx +81 -37
  86. package/templates/assistkick-product-system/packages/frontend/src/components/ds/SidePanelShowcase.tsx +370 -0
  87. package/templates/assistkick-product-system/packages/frontend/src/components/ds/SideSheet.tsx +64 -0
  88. package/templates/assistkick-product-system/packages/frontend/src/components/ds/StatusDot.tsx +18 -0
  89. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCardPositionNode.tsx +36 -0
  90. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCycleCountNode.tsx +60 -0
  91. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/EndNode.tsx +42 -0
  92. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GenerateTTSNode.tsx +52 -0
  93. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +189 -0
  94. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +123 -0
  95. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RebuildBundleNode.tsx +20 -0
  96. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RenderVideoNode.tsx +72 -0
  97. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RunAgentNode.tsx +51 -0
  98. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/SetCardMetadataNode.tsx +53 -0
  99. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/StartNode.tsx +18 -0
  100. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/TransitionCardNode.tsx +59 -0
  101. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +341 -0
  102. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +643 -0
  103. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/autoLayout.ts +103 -0
  104. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/edgeColors.ts +35 -0
  105. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +246 -0
  106. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.test.ts +119 -0
  107. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +136 -0
  108. package/templates/assistkick-product-system/packages/frontend/src/constants/graph.ts +13 -11
  109. package/templates/assistkick-product-system/packages/frontend/src/hooks/useAutoSave.ts +75 -0
  110. package/templates/assistkick-product-system/packages/frontend/src/hooks/useToast.tsx +16 -3
  111. package/templates/assistkick-product-system/packages/frontend/src/pages/accept_invitation_page.tsx +30 -27
  112. package/templates/assistkick-product-system/packages/frontend/src/pages/forgot_password_page.tsx +18 -15
  113. package/templates/assistkick-product-system/packages/frontend/src/pages/register_page.tsx +21 -18
  114. package/templates/assistkick-product-system/packages/frontend/src/pages/reset_password_page.tsx +28 -25
  115. package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +6 -0
  116. package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +1 -1
  117. package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +2 -2
  118. package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +13 -0
  119. package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +2 -2
  120. package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +13 -0
  121. package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +6 -0
  122. package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +6 -3
  123. package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +4 -4
  124. package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +275 -3535
  125. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.test.ts +167 -0
  126. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.ts +101 -0
  127. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.test.ts +42 -0
  128. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.ts +17 -0
  129. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.test.ts +145 -0
  130. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.ts +42 -0
  131. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.test.ts +4 -10
  132. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.ts +19 -1
  133. package/templates/assistkick-product-system/packages/frontend/vite.config.ts +5 -0
  134. package/templates/assistkick-product-system/packages/shared/db/local.db +0 -0
  135. package/templates/assistkick-product-system/packages/shared/db/migrations/0004_tidy_matthew_murdock.sql +9 -0
  136. package/templates/assistkick-product-system/packages/shared/db/migrations/0005_mysterious_falcon.sql +692 -0
  137. package/templates/assistkick-product-system/packages/shared/db/migrations/0006_next_venom.sql +9 -0
  138. package/templates/assistkick-product-system/packages/shared/db/migrations/0007_deep_barracuda.sql +39 -0
  139. package/templates/assistkick-product-system/packages/shared/db/migrations/0008_puzzling_hannibal_king.sql +1 -0
  140. package/templates/assistkick-product-system/packages/shared/db/migrations/0009_amused_beast.sql +8 -0
  141. package/templates/assistkick-product-system/packages/shared/db/migrations/0010_spotty_moira_mactaggert.sql +9 -0
  142. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_goofy_snowbird.sql +3 -0
  143. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_supreme_doctor_octopus.sql +3 -0
  144. package/templates/assistkick-product-system/packages/shared/db/migrations/0013_reflective_prowler.sql +15 -0
  145. package/templates/assistkick-product-system/packages/shared/db/migrations/0014_nifty_punisher.sql +15 -0
  146. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0004_snapshot.json +921 -0
  147. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0005_snapshot.json +1042 -0
  148. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0006_snapshot.json +1101 -0
  149. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0007_snapshot.json +1336 -0
  150. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0008_snapshot.json +1275 -0
  151. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0009_snapshot.json +1327 -0
  152. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0010_snapshot.json +1393 -0
  153. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0011_snapshot.json +1436 -0
  154. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0013_snapshot.json +1538 -0
  155. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0014_snapshot.json +1545 -0
  156. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +77 -0
  157. package/templates/assistkick-product-system/packages/shared/db/schema.ts +114 -0
  158. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +32 -7
  159. package/templates/assistkick-product-system/packages/shared/lib/constants.ts +9 -0
  160. package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +12 -4
  161. package/templates/assistkick-product-system/packages/shared/lib/graph.ts +5 -0
  162. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +1999 -0
  163. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +1437 -0
  164. package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +211 -0
  165. package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +43 -0
  166. package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +13 -2
  167. package/templates/assistkick-product-system/packages/shared/tools/get_kanban.ts +1 -1
  168. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.test.ts +226 -0
  169. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.ts +251 -0
  170. package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -2
  171. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.test.ts +10 -0
  172. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.ts +6 -0
  173. package/templates/assistkick-product-system/packages/video/Root.tsx +85 -0
  174. package/templates/assistkick-product-system/packages/video/components/email_scene.tsx +231 -0
  175. package/templates/assistkick-product-system/packages/video/components/outro_scene.tsx +153 -0
  176. package/templates/assistkick-product-system/packages/video/components/part_divider.tsx +90 -0
  177. package/templates/assistkick-product-system/packages/video/components/scene.tsx +226 -0
  178. package/templates/assistkick-product-system/packages/video/components/theme.ts +22 -0
  179. package/templates/assistkick-product-system/packages/video/components/title_scene.tsx +169 -0
  180. package/templates/assistkick-product-system/packages/video/components/video_split_layout.tsx +84 -0
  181. package/templates/assistkick-product-system/packages/video/compositions/.gitkeep +0 -0
  182. package/templates/assistkick-product-system/packages/video/index.ts +4 -0
  183. package/templates/assistkick-product-system/packages/video/package.json +28 -0
  184. package/templates/assistkick-product-system/packages/video/remotion.config.ts +11 -0
  185. package/templates/assistkick-product-system/packages/video/scripts/process_script.test.ts +326 -0
  186. package/templates/assistkick-product-system/packages/video/scripts/process_script.ts +630 -0
  187. package/templates/assistkick-product-system/packages/video/style.css +1 -0
  188. package/templates/assistkick-product-system/packages/video/tsconfig.json +18 -0
  189. package/templates/assistkick-product-system/tests/graph_legend.test.ts +2 -1
  190. package/templates/assistkick-product-system/tests/video_render_service.test.ts +181 -0
  191. package/templates/assistkick-product-system/tests/web_terminal.test.ts +219 -455
  192. package/templates/assistkick-product-system/tests/workflow_integration.test.ts +341 -0
  193. package/templates/skills/assistkick-developer/SKILL.md +3 -0
  194. package/templates/skills/assistkick-developer/references/react_development_guidelines.md +225 -0
  195. package/templates/skills/product-system/graph.json +1890 -0
  196. package/templates/skills/product-system/kanban.json +304 -0
  197. package/templates/skills/product-system/nodes/comp_001.md +56 -0
  198. package/templates/skills/product-system/nodes/comp_002.md +57 -0
  199. package/templates/skills/product-system/nodes/data_001.md +51 -0
  200. package/templates/skills/product-system/nodes/data_002.md +40 -0
  201. package/templates/skills/product-system/nodes/data_004.md +38 -0
  202. package/templates/skills/product-system/nodes/dec_001.md +34 -0
  203. package/templates/skills/product-system/nodes/dec_016.md +32 -0
  204. package/templates/skills/product-system/nodes/feat_008.md +30 -0
  205. package/templates/skills/video-composition-agent/SKILL.md +232 -0
  206. package/templates/skills/video-script-writer/SKILL.md +136 -0
@@ -0,0 +1,172 @@
1
+ import { describe, it, mock, beforeEach, afterEach } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { ApiClient } from './client.ts';
4
+
5
+ describe('ApiClient file CRUD methods', () => {
6
+ let originalFetch: typeof globalThis.fetch;
7
+
8
+ beforeEach(() => {
9
+ originalFetch = globalThis.fetch;
10
+ });
11
+
12
+ afterEach(() => {
13
+ globalThis.fetch = originalFetch;
14
+ });
15
+
16
+ it('createFile sends POST with path and type', async () => {
17
+ let capturedUrl: string | undefined;
18
+ let capturedMethod: string | undefined;
19
+ let capturedBody: string | undefined;
20
+
21
+ globalThis.fetch = mock.fn(async (url: any, init?: any) => {
22
+ capturedUrl = url;
23
+ capturedMethod = init?.method;
24
+ capturedBody = init?.body;
25
+ return {
26
+ ok: true,
27
+ status: 200,
28
+ json: async () => ({ success: true }),
29
+ };
30
+ }) as any;
31
+
32
+ const client = new ApiClient();
33
+ const result = await client.createFile('proj_001', 'src/new_file.ts', 'file');
34
+
35
+ assert.ok(capturedUrl!.includes('/api/projects/proj_001/files'));
36
+ assert.equal(capturedMethod, 'POST');
37
+ const body = JSON.parse(capturedBody!);
38
+ assert.equal(body.path, 'src/new_file.ts');
39
+ assert.equal(body.type, 'file');
40
+ assert.equal(result.success, true);
41
+ });
42
+
43
+ it('createFile sends directory type', async () => {
44
+ let capturedBody: string | undefined;
45
+
46
+ globalThis.fetch = mock.fn(async (_url: any, init?: any) => {
47
+ capturedBody = init?.body;
48
+ return {
49
+ ok: true,
50
+ status: 200,
51
+ json: async () => ({ success: true }),
52
+ };
53
+ }) as any;
54
+
55
+ const client = new ApiClient();
56
+ await client.createFile('proj_001', 'src/utils', 'directory');
57
+
58
+ const body = JSON.parse(capturedBody!);
59
+ assert.equal(body.path, 'src/utils');
60
+ assert.equal(body.type, 'directory');
61
+ });
62
+
63
+ it('createFile throws on error', async () => {
64
+ globalThis.fetch = mock.fn(async () => ({
65
+ ok: false,
66
+ status: 400,
67
+ json: async () => ({ error: 'Path already exists' }),
68
+ })) as any;
69
+
70
+ const client = new ApiClient();
71
+ await assert.rejects(
72
+ () => client.createFile('proj_001', 'existing.ts', 'file'),
73
+ { message: 'Path already exists' },
74
+ );
75
+ });
76
+
77
+ it('deleteFile sends DELETE with path as query param', async () => {
78
+ let capturedUrl: string | undefined;
79
+ let capturedMethod: string | undefined;
80
+
81
+ globalThis.fetch = mock.fn(async (url: any, init?: any) => {
82
+ capturedUrl = url;
83
+ capturedMethod = init?.method;
84
+ return {
85
+ ok: true,
86
+ status: 200,
87
+ json: async () => ({ success: true }),
88
+ };
89
+ }) as any;
90
+
91
+ const client = new ApiClient();
92
+ await client.deleteFile('proj_001', 'src/old.ts');
93
+
94
+ assert.ok(capturedUrl!.includes('/api/projects/proj_001/files?path='));
95
+ assert.ok(capturedUrl!.includes(encodeURIComponent('src/old.ts')));
96
+ assert.equal(capturedMethod, 'DELETE');
97
+ });
98
+
99
+ it('deleteFile throws on error', async () => {
100
+ globalThis.fetch = mock.fn(async () => ({
101
+ ok: false,
102
+ status: 404,
103
+ json: async () => ({ error: 'File not found' }),
104
+ })) as any;
105
+
106
+ const client = new ApiClient();
107
+ await assert.rejects(
108
+ () => client.deleteFile('proj_001', 'nonexistent.ts'),
109
+ { message: 'File not found' },
110
+ );
111
+ });
112
+
113
+ it('renameFile sends PATCH with oldPath and newPath', async () => {
114
+ let capturedUrl: string | undefined;
115
+ let capturedMethod: string | undefined;
116
+ let capturedBody: string | undefined;
117
+
118
+ globalThis.fetch = mock.fn(async (url: any, init?: any) => {
119
+ capturedUrl = url;
120
+ capturedMethod = init?.method;
121
+ capturedBody = init?.body;
122
+ return {
123
+ ok: true,
124
+ status: 200,
125
+ json: async () => ({ success: true }),
126
+ };
127
+ }) as any;
128
+
129
+ const client = new ApiClient();
130
+ await client.renameFile('proj_001', 'src/old.ts', 'src/new.ts');
131
+
132
+ assert.ok(capturedUrl!.includes('/api/projects/proj_001/files'));
133
+ assert.equal(capturedMethod, 'PATCH');
134
+ const body = JSON.parse(capturedBody!);
135
+ assert.equal(body.oldPath, 'src/old.ts');
136
+ assert.equal(body.newPath, 'src/new.ts');
137
+ });
138
+
139
+ it('renameFile throws on error', async () => {
140
+ globalThis.fetch = mock.fn(async () => ({
141
+ ok: false,
142
+ status: 400,
143
+ json: async () => ({ error: 'Invalid path' }),
144
+ })) as any;
145
+
146
+ const client = new ApiClient();
147
+ await assert.rejects(
148
+ () => client.renameFile('proj_001', '../escape', 'new.ts'),
149
+ { message: 'Invalid path' },
150
+ );
151
+ });
152
+
153
+ it('fetchFileTree sends GET to files endpoint', async () => {
154
+ let capturedUrl: string | undefined;
155
+
156
+ const treeData = { tree: [{ name: 'src', path: 'src', type: 'directory', children: [] }] };
157
+ globalThis.fetch = mock.fn(async (url: any) => {
158
+ capturedUrl = url;
159
+ return {
160
+ ok: true,
161
+ status: 200,
162
+ json: async () => treeData,
163
+ };
164
+ }) as any;
165
+
166
+ const client = new ApiClient();
167
+ const result = await client.fetchFileTree('proj_001');
168
+
169
+ assert.ok(capturedUrl!.includes('/api/projects/proj_001/files'));
170
+ assert.deepEqual(result.tree, treeData.tree);
171
+ });
172
+ });
@@ -0,0 +1,238 @@
1
+ import { describe, it, mock, beforeEach, afterEach } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { ApiClient } from './client.ts';
4
+
5
+ describe('ApiClient video methods', () => {
6
+ let originalFetch: typeof globalThis.fetch;
7
+
8
+ beforeEach(() => {
9
+ originalFetch = globalThis.fetch;
10
+ });
11
+
12
+ afterEach(() => {
13
+ globalThis.fetch = originalFetch;
14
+ });
15
+
16
+ describe('fetchVideoCompositions', () => {
17
+ it('returns compositions list from the API', async () => {
18
+ const compositions = [
19
+ { id: 'my-video', durationInFrames: 300, fps: 30, width: 1920, height: 1080 },
20
+ { id: 'intro', durationInFrames: 150, fps: 24, width: 1280, height: 720, folder: 'Intros' },
21
+ ];
22
+
23
+ globalThis.fetch = mock.fn(async () => ({
24
+ ok: true,
25
+ status: 200,
26
+ json: async () => ({ compositions }),
27
+ })) as any;
28
+
29
+ const client = new ApiClient();
30
+ const result = await client.fetchVideoCompositions();
31
+ assert.deepEqual(result.compositions, compositions);
32
+ });
33
+
34
+ it('throws on non-OK response', async () => {
35
+ globalThis.fetch = mock.fn(async () => ({
36
+ ok: false,
37
+ status: 500,
38
+ })) as any;
39
+
40
+ const client = new ApiClient();
41
+ await assert.rejects(
42
+ () => client.fetchVideoCompositions(),
43
+ { message: /Failed to fetch video compositions: 500/ },
44
+ );
45
+ });
46
+ });
47
+
48
+ describe('fetchVideoBundleStatus', () => {
49
+ it('returns bundle status', async () => {
50
+ const status = {
51
+ ready: true,
52
+ building: false,
53
+ bundlePath: '/data/remotion-bundle',
54
+ lastBuiltAt: '2026-03-10T12:00:00Z',
55
+ error: null,
56
+ };
57
+
58
+ globalThis.fetch = mock.fn(async () => ({
59
+ ok: true,
60
+ status: 200,
61
+ json: async () => status,
62
+ })) as any;
63
+
64
+ const client = new ApiClient();
65
+ const result = await client.fetchVideoBundleStatus();
66
+ assert.deepEqual(result, status);
67
+ });
68
+
69
+ it('returns not-ready status when no bundle exists', async () => {
70
+ const status = {
71
+ ready: false,
72
+ building: false,
73
+ bundlePath: null,
74
+ lastBuiltAt: null,
75
+ error: null,
76
+ };
77
+
78
+ globalThis.fetch = mock.fn(async () => ({
79
+ ok: true,
80
+ status: 200,
81
+ json: async () => status,
82
+ })) as any;
83
+
84
+ const client = new ApiClient();
85
+ const result = await client.fetchVideoBundleStatus();
86
+ assert.equal(result.ready, false);
87
+ assert.equal(result.bundlePath, null);
88
+ });
89
+ });
90
+
91
+ describe('rebuildVideoBundle', () => {
92
+ it('triggers rebuild and returns new status', async () => {
93
+ const status = {
94
+ ready: false,
95
+ building: true,
96
+ bundlePath: null,
97
+ lastBuiltAt: null,
98
+ error: null,
99
+ };
100
+
101
+ let capturedMethod: string | undefined;
102
+ globalThis.fetch = mock.fn(async (_url: any, init?: any) => {
103
+ capturedMethod = init?.method;
104
+ return {
105
+ ok: true,
106
+ status: 200,
107
+ json: async () => status,
108
+ };
109
+ }) as any;
110
+
111
+ const client = new ApiClient();
112
+ const result = await client.rebuildVideoBundle();
113
+ assert.equal(capturedMethod, 'POST');
114
+ assert.equal(result.building, true);
115
+ });
116
+
117
+ it('throws on rebuild failure', async () => {
118
+ globalThis.fetch = mock.fn(async () => ({
119
+ ok: false,
120
+ status: 500,
121
+ json: async () => ({ error: 'Remotion bundle failed' }),
122
+ })) as any;
123
+
124
+ const client = new ApiClient();
125
+ await assert.rejects(
126
+ () => client.rebuildVideoBundle(),
127
+ { message: /Remotion bundle failed/ },
128
+ );
129
+ });
130
+ });
131
+
132
+ describe('fetchVideoRenders', () => {
133
+ it('returns renders list for a project', async () => {
134
+ const renders = [
135
+ {
136
+ id: 'r1', status: 'complete', progress: 100, compositionId: 'comp1',
137
+ filePath: '/data/r1.mp4', fileSize: 1024, durationSeconds: 10.5,
138
+ error: null, resolution: '1920x1080',
139
+ createdAt: '2026-03-10T10:00:00Z', completedAt: '2026-03-10T10:05:00Z',
140
+ },
141
+ ];
142
+
143
+ let capturedUrl: string = '';
144
+ globalThis.fetch = mock.fn(async (url: any) => {
145
+ capturedUrl = typeof url === 'string' ? url : url.toString();
146
+ return {
147
+ ok: true,
148
+ status: 200,
149
+ json: async () => ({ renders }),
150
+ };
151
+ }) as any;
152
+
153
+ const client = new ApiClient();
154
+ const result = await client.fetchVideoRenders('proj_123');
155
+ assert.deepEqual(result.renders, renders);
156
+ assert.ok(capturedUrl.includes('projectId=proj_123'));
157
+ });
158
+
159
+ it('throws on non-OK response', async () => {
160
+ globalThis.fetch = mock.fn(async () => ({
161
+ ok: false,
162
+ status: 500,
163
+ })) as any;
164
+
165
+ const client = new ApiClient();
166
+ await assert.rejects(
167
+ () => client.fetchVideoRenders('proj_123'),
168
+ { message: /Failed to fetch video renders: 500/ },
169
+ );
170
+ });
171
+ });
172
+
173
+ describe('fetchVideoRenderStatus', () => {
174
+ it('returns render status by id', async () => {
175
+ const render = {
176
+ id: 'r1', status: 'rendering', progress: 45, compositionId: 'comp1',
177
+ filePath: null, fileSize: null, durationSeconds: null,
178
+ error: null, resolution: '1920x1080',
179
+ createdAt: '2026-03-10T10:00:00Z', completedAt: null,
180
+ };
181
+
182
+ globalThis.fetch = mock.fn(async () => ({
183
+ ok: true,
184
+ status: 200,
185
+ json: async () => render,
186
+ })) as any;
187
+
188
+ const client = new ApiClient();
189
+ const result = await client.fetchVideoRenderStatus('r1');
190
+ assert.equal(result.id, 'r1');
191
+ assert.equal(result.status, 'rendering');
192
+ assert.equal(result.progress, 45);
193
+ });
194
+ });
195
+
196
+ describe('deleteVideoRender', () => {
197
+ it('sends DELETE request', async () => {
198
+ let capturedMethod: string | undefined;
199
+ let capturedUrl: string = '';
200
+ globalThis.fetch = mock.fn(async (url: any, init?: any) => {
201
+ capturedMethod = init?.method;
202
+ capturedUrl = typeof url === 'string' ? url : url.toString();
203
+ return {
204
+ ok: true,
205
+ status: 200,
206
+ json: async () => ({ ok: true }),
207
+ };
208
+ }) as any;
209
+
210
+ const client = new ApiClient();
211
+ await client.deleteVideoRender('r1');
212
+ assert.equal(capturedMethod, 'DELETE');
213
+ assert.ok(capturedUrl.includes('/api/video/render/r1'));
214
+ });
215
+
216
+ it('throws on delete failure', async () => {
217
+ globalThis.fetch = mock.fn(async () => ({
218
+ ok: false,
219
+ status: 404,
220
+ json: async () => ({ error: 'Render not found' }),
221
+ })) as any;
222
+
223
+ const client = new ApiClient();
224
+ await assert.rejects(
225
+ () => client.deleteVideoRender('r1'),
226
+ { message: /Render not found/ },
227
+ );
228
+ });
229
+ });
230
+
231
+ describe('getVideoFileUrl', () => {
232
+ it('returns correct URL for render file', () => {
233
+ const client = new ApiClient();
234
+ const url = client.getVideoFileUrl('r1');
235
+ assert.ok(url.endsWith('/api/video/file/r1'));
236
+ });
237
+ });
238
+ });