@jiggai/kitchen 0.3.1 → 0.3.3

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 (307) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +2 -2
  3. package/.next/server/app/_global-error.html +2 -2
  4. package/.next/server/app/_global-error.rsc +1 -1
  5. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  6. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  10. package/.next/server/app/_not-found.html +1 -1
  11. package/.next/server/app/_not-found.rsc +1 -1
  12. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  13. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  14. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  15. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  16. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  17. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  18. package/.next/server/app/channels.html +2 -2
  19. package/.next/server/app/channels.rsc +1 -1
  20. package/.next/server/app/channels.segments/_full.segment.rsc +1 -1
  21. package/.next/server/app/channels.segments/_head.segment.rsc +1 -1
  22. package/.next/server/app/channels.segments/_index.segment.rsc +1 -1
  23. package/.next/server/app/channels.segments/_tree.segment.rsc +1 -1
  24. package/.next/server/app/channels.segments/channels/__PAGE__.segment.rsc +1 -1
  25. package/.next/server/app/channels.segments/channels.segment.rsc +1 -1
  26. package/.next/server/app/cron-jobs.html +1 -1
  27. package/.next/server/app/cron-jobs.rsc +1 -1
  28. package/.next/server/app/cron-jobs.segments/_full.segment.rsc +1 -1
  29. package/.next/server/app/cron-jobs.segments/_head.segment.rsc +1 -1
  30. package/.next/server/app/cron-jobs.segments/_index.segment.rsc +1 -1
  31. package/.next/server/app/cron-jobs.segments/_tree.segment.rsc +1 -1
  32. package/.next/server/app/cron-jobs.segments/cron-jobs/__PAGE__.segment.rsc +1 -1
  33. package/.next/server/app/cron-jobs.segments/cron-jobs.segment.rsc +1 -1
  34. package/.next/server/app/goals/new.html +2 -2
  35. package/.next/server/app/goals/new.rsc +1 -1
  36. package/.next/server/app/goals/new.segments/_full.segment.rsc +1 -1
  37. package/.next/server/app/goals/new.segments/_head.segment.rsc +1 -1
  38. package/.next/server/app/goals/new.segments/_index.segment.rsc +1 -1
  39. package/.next/server/app/goals/new.segments/_tree.segment.rsc +1 -1
  40. package/.next/server/app/goals/new.segments/goals/new/__PAGE__.segment.rsc +1 -1
  41. package/.next/server/app/goals/new.segments/goals/new.segment.rsc +1 -1
  42. package/.next/server/app/goals/new.segments/goals.segment.rsc +1 -1
  43. package/.next/server/app/goals.html +1 -1
  44. package/.next/server/app/goals.rsc +1 -1
  45. package/.next/server/app/goals.segments/_full.segment.rsc +1 -1
  46. package/.next/server/app/goals.segments/_head.segment.rsc +1 -1
  47. package/.next/server/app/goals.segments/_index.segment.rsc +1 -1
  48. package/.next/server/app/goals.segments/_tree.segment.rsc +1 -1
  49. package/.next/server/app/goals.segments/goals/__PAGE__.segment.rsc +1 -1
  50. package/.next/server/app/goals.segments/goals.segment.rsc +1 -1
  51. package/.next/server/app/settings.html +1 -1
  52. package/.next/server/app/settings.rsc +1 -1
  53. package/.next/server/app/settings.segments/_full.segment.rsc +1 -1
  54. package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  55. package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  56. package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  57. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +1 -1
  58. package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  59. package/.next/server/pages/404.html +1 -1
  60. package/.next/server/pages/500.html +2 -2
  61. package/package.json +1 -2
  62. package/src/app/HomeClient.tsx +0 -207
  63. package/src/app/agents/[agentId]/agent-editor-tabs.tsx +0 -298
  64. package/src/app/agents/[agentId]/agent-editor.tsx +0 -468
  65. package/src/app/agents/[agentId]/page.tsx +0 -32
  66. package/src/app/api/__tests__/agents-add-route.test.ts +0 -143
  67. package/src/app/api/__tests__/agents-file-route.test.ts +0 -117
  68. package/src/app/api/__tests__/agents-files-route.test.ts +0 -61
  69. package/src/app/api/__tests__/agents-id-route.test.ts +0 -104
  70. package/src/app/api/__tests__/agents-identity-route.test.ts +0 -92
  71. package/src/app/api/__tests__/agents-route.test.ts +0 -54
  72. package/src/app/api/__tests__/agents-skills-install-route.test.ts +0 -131
  73. package/src/app/api/__tests__/agents-skills-route.test.ts +0 -64
  74. package/src/app/api/__tests__/agents-update-route.test.ts +0 -95
  75. package/src/app/api/__tests__/channels-bindings-route.test.ts +0 -143
  76. package/src/app/api/__tests__/cron-delete-route.test.ts +0 -93
  77. package/src/app/api/__tests__/cron-job-route.test.ts +0 -78
  78. package/src/app/api/__tests__/cron-jobs-route.test.ts +0 -116
  79. package/src/app/api/__tests__/cron-recipe-installed-route.test.ts +0 -114
  80. package/src/app/api/__tests__/gateway-restart-route.test.ts +0 -36
  81. package/src/app/api/__tests__/goals-promote-route.test.ts +0 -200
  82. package/src/app/api/__tests__/goals-route.test.ts +0 -184
  83. package/src/app/api/__tests__/ids-check-route.test.ts +0 -188
  84. package/src/app/api/__tests__/marketplace-recipes-route.test.ts +0 -123
  85. package/src/app/api/__tests__/recipes-clone-route.test.ts +0 -221
  86. package/src/app/api/__tests__/recipes-delete-route.test.ts +0 -248
  87. package/src/app/api/__tests__/recipes-id-route.test.ts +0 -166
  88. package/src/app/api/__tests__/recipes-route.test.ts +0 -57
  89. package/src/app/api/__tests__/recipes-team-agents-route.test.ts +0 -135
  90. package/src/app/api/__tests__/scaffold-route.test.ts +0 -173
  91. package/src/app/api/__tests__/settings-cron-installation-route.test.ts +0 -82
  92. package/src/app/api/__tests__/skills-available-route.test.ts +0 -47
  93. package/src/app/api/__tests__/swarms-start-route.test.ts +0 -79
  94. package/src/app/api/__tests__/swarms-status-route.test.ts +0 -42
  95. package/src/app/api/__tests__/teams-file-route.test.ts +0 -129
  96. package/src/app/api/__tests__/teams-files-route.test.ts +0 -57
  97. package/src/app/api/__tests__/teams-meta-route.test.ts +0 -113
  98. package/src/app/api/__tests__/teams-orchestrator-install-route.test.ts +0 -66
  99. package/src/app/api/__tests__/teams-orchestrator-route.test.ts +0 -59
  100. package/src/app/api/__tests__/teams-remove-team-route.test.ts +0 -122
  101. package/src/app/api/__tests__/teams-skills-install-route.test.ts +0 -78
  102. package/src/app/api/__tests__/teams-skills-route.test.ts +0 -73
  103. package/src/app/api/__tests__/teams-workflow-runs-route.test.ts +0 -85
  104. package/src/app/api/__tests__/teams-workflows-route.test.ts +0 -110
  105. package/src/app/api/__tests__/tickets-move-route.test.ts +0 -60
  106. package/src/app/api/agents/[id]/route.ts +0 -83
  107. package/src/app/api/agents/add/route.ts +0 -114
  108. package/src/app/api/agents/file/route.ts +0 -45
  109. package/src/app/api/agents/files/route.ts +0 -23
  110. package/src/app/api/agents/identity/route.ts +0 -41
  111. package/src/app/api/agents/route.ts +0 -22
  112. package/src/app/api/agents/skills/install/route.ts +0 -34
  113. package/src/app/api/agents/skills/route.ts +0 -39
  114. package/src/app/api/agents/update/route.ts +0 -52
  115. package/src/app/api/channels/bindings/route.ts +0 -63
  116. package/src/app/api/cron/__tests__/helpers.test.ts +0 -164
  117. package/src/app/api/cron/delete/route.ts +0 -23
  118. package/src/app/api/cron/helpers.ts +0 -172
  119. package/src/app/api/cron/job/route.ts +0 -22
  120. package/src/app/api/cron/jobs/route.ts +0 -52
  121. package/src/app/api/cron/recipe-installed/route.ts +0 -65
  122. package/src/app/api/gateway/restart/route.ts +0 -20
  123. package/src/app/api/goals/[id]/promote/route.ts +0 -119
  124. package/src/app/api/goals/[id]/route.ts +0 -54
  125. package/src/app/api/goals/route.ts +0 -44
  126. package/src/app/api/ids/check/route.ts +0 -113
  127. package/src/app/api/marketplace/recipes/[slug]/route.ts +0 -16
  128. package/src/app/api/marketplace/recipes/route.ts +0 -22
  129. package/src/app/api/recipes/[id]/route.ts +0 -62
  130. package/src/app/api/recipes/clone/route.ts +0 -106
  131. package/src/app/api/recipes/custom-team/route.ts +0 -193
  132. package/src/app/api/recipes/delete/helpers.ts +0 -65
  133. package/src/app/api/recipes/delete/route.ts +0 -73
  134. package/src/app/api/recipes/route.ts +0 -21
  135. package/src/app/api/recipes/team-agents/__tests__/helpers.test.ts +0 -156
  136. package/src/app/api/recipes/team-agents/helpers.ts +0 -151
  137. package/src/app/api/recipes/team-agents/route.ts +0 -80
  138. package/src/app/api/scaffold/__tests__/helpers.test.ts +0 -186
  139. package/src/app/api/scaffold/helpers.ts +0 -214
  140. package/src/app/api/scaffold/route.ts +0 -95
  141. package/src/app/api/settings/cron-installation/route.ts +0 -58
  142. package/src/app/api/skills/available/route.ts +0 -23
  143. package/src/app/api/swarms/start/route.ts +0 -100
  144. package/src/app/api/swarms/status/route.ts +0 -31
  145. package/src/app/api/teams/[teamId]/tickets/assign/route.ts +0 -105
  146. package/src/app/api/teams/[teamId]/tickets/assignees/route.ts +0 -27
  147. package/src/app/api/teams/[teamId]/tickets/delete/route.ts +0 -55
  148. package/src/app/api/teams/[teamId]/tickets/move/route.ts +0 -70
  149. package/src/app/api/teams/[teamId]/tickets/move-to-goals/route.ts +0 -56
  150. package/src/app/api/teams/file/route.ts +0 -46
  151. package/src/app/api/teams/files/route.ts +0 -63
  152. package/src/app/api/teams/memory/route.ts +0 -250
  153. package/src/app/api/teams/meta/route.ts +0 -43
  154. package/src/app/api/teams/orchestrator/install/route.ts +0 -129
  155. package/src/app/api/teams/orchestrator/route.ts +0 -216
  156. package/src/app/api/teams/remove-team/route.ts +0 -37
  157. package/src/app/api/teams/skills/install/route.ts +0 -18
  158. package/src/app/api/teams/skills/route.ts +0 -25
  159. package/src/app/api/teams/workflow-runs/route.ts +0 -534
  160. package/src/app/api/teams/workflow-templates/route.ts +0 -71
  161. package/src/app/api/teams/workflows/route.ts +0 -55
  162. package/src/app/api/tickets/assign/route.ts +0 -94
  163. package/src/app/api/tickets/assignees/route.ts +0 -24
  164. package/src/app/api/tickets/move/route.ts +0 -69
  165. package/src/app/channels/channels-client.tsx +0 -271
  166. package/src/app/channels/page.tsx +0 -5
  167. package/src/app/cron-jobs/cron-jobs-client.tsx +0 -243
  168. package/src/app/cron-jobs/page.tsx +0 -34
  169. package/src/app/favicon.ico +0 -0
  170. package/src/app/global-error.tsx +0 -50
  171. package/src/app/globals.css +0 -153
  172. package/src/app/goals/[id]/goal-editor.tsx +0 -162
  173. package/src/app/goals/[id]/page.tsx +0 -6
  174. package/src/app/goals/goals-client.tsx +0 -201
  175. package/src/app/goals/new/page.tsx +0 -81
  176. package/src/app/goals/page.tsx +0 -10
  177. package/src/app/layout.tsx +0 -53
  178. package/src/app/manifest.ts +0 -15
  179. package/src/app/not-found.tsx +0 -8
  180. package/src/app/page.tsx +0 -33
  181. package/src/app/recipes/CreateAgentModal.tsx +0 -156
  182. package/src/app/recipes/CreateCustomTeamModal.tsx +0 -375
  183. package/src/app/recipes/CreateModalShell.tsx +0 -55
  184. package/src/app/recipes/CreateTeamModal.tsx +0 -91
  185. package/src/app/recipes/[id]/RecipeEditor/RecipeEditorCreateModal.tsx +0 -72
  186. package/src/app/recipes/[id]/RecipeEditor/RecipeEditorPanel.tsx +0 -216
  187. package/src/app/recipes/[id]/RecipeEditor/index.tsx +0 -271
  188. package/src/app/recipes/[id]/RecipeEditor/recipe-editor-utils.ts +0 -46
  189. package/src/app/recipes/[id]/RecipeEditor/types.ts +0 -52
  190. package/src/app/recipes/[id]/page.tsx +0 -37
  191. package/src/app/recipes/page.tsx +0 -101
  192. package/src/app/recipes/recipes-client.tsx +0 -620
  193. package/src/app/settings/page.tsx +0 -26
  194. package/src/app/settings/settings-client.tsx +0 -91
  195. package/src/app/teams/[teamId]/CloneTeamModal.tsx +0 -116
  196. package/src/app/teams/[teamId]/OrchestratorPanel.tsx +0 -255
  197. package/src/app/teams/[teamId]/OrchestratorSetupModal.tsx +0 -184
  198. package/src/app/teams/[teamId]/PublishChangesModal.tsx +0 -43
  199. package/src/app/teams/[teamId]/page.tsx +0 -49
  200. package/src/app/teams/[teamId]/team-editor/TeamAgentsTab.tsx +0 -145
  201. package/src/app/teams/[teamId]/team-editor/TeamCronTab.tsx +0 -72
  202. package/src/app/teams/[teamId]/team-editor/TeamFilesTab.tsx +0 -74
  203. package/src/app/teams/[teamId]/team-editor/TeamMemoryTab.tsx +0 -349
  204. package/src/app/teams/[teamId]/team-editor/TeamRecipeTab.tsx +0 -151
  205. package/src/app/teams/[teamId]/team-editor/TeamSkillsTab.tsx +0 -68
  206. package/src/app/teams/[teamId]/team-editor/index.tsx +0 -558
  207. package/src/app/teams/[teamId]/team-editor/team-editor-data.ts +0 -255
  208. package/src/app/teams/[teamId]/team-editor/team-editor-utils.ts +0 -78
  209. package/src/app/teams/[teamId]/team-editor/types.ts +0 -34
  210. package/src/app/teams/[teamId]/tickets/[ticket]/page.tsx +0 -35
  211. package/src/app/teams/[teamId]/tickets/page.tsx +0 -15
  212. package/src/app/teams/[teamId]/workflows/[workflowId]/WorkflowCanvas.tsx +0 -111
  213. package/src/app/teams/[teamId]/workflows/[workflowId]/page.tsx +0 -27
  214. package/src/app/teams/[teamId]/workflows/[workflowId]/workflows-editor-client.tsx +0 -1608
  215. package/src/app/teams/[teamId]/workflows/page.tsx +0 -40
  216. package/src/app/teams/[teamId]/workflows/workflows-client.tsx +0 -494
  217. package/src/app/tickets/TicketDetailClient.tsx +0 -147
  218. package/src/app/tickets/TicketsBoardClient.tsx +0 -200
  219. package/src/app/tickets/[ticket]/TicketAssignControl.tsx +0 -112
  220. package/src/app/tickets/[ticket]/page.tsx +0 -36
  221. package/src/app/tickets/page.tsx +0 -10
  222. package/src/components/AppShell.tsx +0 -286
  223. package/src/components/ConfirmationModal.tsx +0 -81
  224. package/src/components/DeleteEntityModal.tsx +0 -41
  225. package/src/components/ErrorBoundary.tsx +0 -70
  226. package/src/components/FileListWithOptionalToggle.tsx +0 -86
  227. package/src/components/GoalFormFields.tsx +0 -163
  228. package/src/components/ScaffoldOverlay.tsx +0 -78
  229. package/src/components/ThemeToggle.tsx +0 -53
  230. package/src/components/ToastProvider.tsx +0 -163
  231. package/src/components/__tests__/ConfirmationModal.test.tsx +0 -109
  232. package/src/components/__tests__/ErrorBoundary.test.tsx +0 -39
  233. package/src/components/__tests__/FileListWithOptionalToggle.test.tsx +0 -109
  234. package/src/components/__tests__/GoalFormFields.test.tsx +0 -117
  235. package/src/components/delete-modals.tsx +0 -59
  236. package/src/components/icons.tsx +0 -48
  237. package/src/lib/__tests__/agent-workspace.test.ts +0 -44
  238. package/src/lib/__tests__/agents.test.ts +0 -36
  239. package/src/lib/__tests__/api-route-helpers.test.ts +0 -188
  240. package/src/lib/__tests__/cron.test.ts +0 -45
  241. package/src/lib/__tests__/editor-utils.test.ts +0 -38
  242. package/src/lib/__tests__/errors.test.ts +0 -15
  243. package/src/lib/__tests__/exec.test.ts +0 -13
  244. package/src/lib/__tests__/fetch-json.test.ts +0 -118
  245. package/src/lib/__tests__/gateway.test.ts +0 -234
  246. package/src/lib/__tests__/goal-promote.test.ts +0 -39
  247. package/src/lib/__tests__/goals-client.test.ts +0 -26
  248. package/src/lib/__tests__/goals.test.ts +0 -275
  249. package/src/lib/__tests__/json.test.ts +0 -15
  250. package/src/lib/__tests__/kitchen-api.test.ts +0 -32
  251. package/src/lib/__tests__/marketplace.test.ts +0 -116
  252. package/src/lib/__tests__/openclaw.test.ts +0 -129
  253. package/src/lib/__tests__/paths.test.ts +0 -136
  254. package/src/lib/__tests__/poll.test.ts +0 -26
  255. package/src/lib/__tests__/recipe-clone.test.ts +0 -85
  256. package/src/lib/__tests__/recipe-team-agents.test.ts +0 -70
  257. package/src/lib/__tests__/recipes.test.ts +0 -199
  258. package/src/lib/__tests__/scaffold-client.test.ts +0 -106
  259. package/src/lib/__tests__/scaffold.test.ts +0 -64
  260. package/src/lib/__tests__/slugify.test.ts +0 -23
  261. package/src/lib/__tests__/tickets.test.ts +0 -158
  262. package/src/lib/__tests__/type-guards.test.ts +0 -18
  263. package/src/lib/__tests__/use-slugified-id.test.tsx +0 -120
  264. package/src/lib/agent-workspace.ts +0 -14
  265. package/src/lib/agents.ts +0 -17
  266. package/src/lib/api-route-helpers.ts +0 -157
  267. package/src/lib/cron.ts +0 -40
  268. package/src/lib/editor-utils.ts +0 -18
  269. package/src/lib/errors.ts +0 -7
  270. package/src/lib/exec.ts +0 -4
  271. package/src/lib/fetch-json.ts +0 -29
  272. package/src/lib/gateway.ts +0 -100
  273. package/src/lib/goal-promote.ts +0 -27
  274. package/src/lib/goals-client.ts +0 -69
  275. package/src/lib/goals.ts +0 -171
  276. package/src/lib/json.ts +0 -10
  277. package/src/lib/kitchen-api.ts +0 -19
  278. package/src/lib/marketplace.ts +0 -46
  279. package/src/lib/openclaw.ts +0 -59
  280. package/src/lib/paths.ts +0 -69
  281. package/src/lib/poll.ts +0 -18
  282. package/src/lib/recipe-clone.ts +0 -42
  283. package/src/lib/recipe-team-agents.ts +0 -30
  284. package/src/lib/recipes.ts +0 -95
  285. package/src/lib/scaffold-client.ts +0 -31
  286. package/src/lib/scaffold.ts +0 -37
  287. package/src/lib/slugify.ts +0 -25
  288. package/src/lib/swarms.ts +0 -25
  289. package/src/lib/tickets.ts +0 -192
  290. package/src/lib/type-guards.ts +0 -3
  291. package/src/lib/use-slugified-id.ts +0 -35
  292. package/src/lib/workflows/README.md +0 -11
  293. package/src/lib/workflows/__tests__/storage.test.ts +0 -129
  294. package/src/lib/workflows/__tests__/validate.test.ts +0 -92
  295. package/src/lib/workflows/api-handlers.ts +0 -35
  296. package/src/lib/workflows/readdir.ts +0 -23
  297. package/src/lib/workflows/runs-storage.ts +0 -59
  298. package/src/lib/workflows/runs-types.ts +0 -42
  299. package/src/lib/workflows/storage.ts +0 -70
  300. package/src/lib/workflows/templates/index.ts +0 -1
  301. package/src/lib/workflows/templates/marketing-cadence-v1.ts +0 -142
  302. package/src/lib/workflows/types.ts +0 -48
  303. package/src/lib/workflows/validate.ts +0 -92
  304. package/src/proxy.ts +0 -28
  305. /package/.next/static/{z86RoqzzXXrWnpi229zP6 → Jrrrm9HH5bKkSrQhe1j93}/_buildManifest.js +0 -0
  306. /package/.next/static/{z86RoqzzXXrWnpi229zP6 → Jrrrm9HH5bKkSrQhe1j93}/_clientMiddlewareManifest.json +0 -0
  307. /package/.next/static/{z86RoqzzXXrWnpi229zP6 → Jrrrm9HH5bKkSrQhe1j93}/_ssgManifest.js +0 -0
@@ -1,110 +0,0 @@
1
- import { describe, expect, it, vi, beforeEach } from "vitest";
2
- import { GET, POST, DELETE } from "../teams/workflows/route";
3
-
4
- vi.mock("@/lib/workflows/storage", () => ({
5
- listWorkflows: vi.fn(),
6
- readWorkflow: vi.fn(),
7
- writeWorkflow: vi.fn(),
8
- deleteWorkflow: vi.fn(),
9
- }));
10
-
11
- import { listWorkflows, readWorkflow, writeWorkflow, deleteWorkflow } from "@/lib/workflows/storage";
12
-
13
- describe("api teams workflows route", () => {
14
- beforeEach(() => {
15
- vi.mocked(listWorkflows).mockReset();
16
- vi.mocked(readWorkflow).mockReset();
17
- vi.mocked(writeWorkflow).mockReset();
18
- vi.mocked(deleteWorkflow).mockReset();
19
- });
20
-
21
- it("GET returns 400 when teamId missing", async () => {
22
- const res = await GET(new Request("https://test"));
23
- expect(res.status).toBe(400);
24
- const json = await res.json();
25
- expect(json.error).toBe("teamId is required");
26
- });
27
-
28
- it("GET returns 200 with list when no id", async () => {
29
- vi.mocked(listWorkflows).mockResolvedValue({ ok: true, dir: "/tmp", files: ["a.workflow.json"] });
30
- const res = await GET(new Request("https://test?teamId=team1"));
31
- expect(res.status).toBe(200);
32
- const json = await res.json();
33
- expect(json.ok).toBe(true);
34
- expect(json.files).toEqual(["a.workflow.json"]);
35
- expect(listWorkflows).toHaveBeenCalledWith("team1");
36
- });
37
-
38
- it("GET returns 200 with workflow when id provided", async () => {
39
- const wf = { id: "wf1", name: "Test", nodes: [], edges: [] };
40
- vi.mocked(readWorkflow).mockResolvedValue({ ok: true, path: "/home/test/wf1.workflow.json", workflow: wf });
41
- const res = await GET(new Request("https://test?teamId=team1&id=wf1"));
42
- expect(res.status).toBe(200);
43
- const json = await res.json();
44
- expect(json.ok).toBe(true);
45
- expect(json.workflow).toEqual(wf);
46
- expect(readWorkflow).toHaveBeenCalledWith("team1", "wf1");
47
- });
48
-
49
- it("POST returns 400 when teamId missing", async () => {
50
- const res = await POST(
51
- new Request("https://test", {
52
- method: "POST",
53
- body: JSON.stringify({ workflow: { id: "wf1", name: "Test", nodes: [], edges: [] } }),
54
- })
55
- );
56
- expect(res.status).toBe(400);
57
- const json = await res.json();
58
- expect(json.error).toBe("teamId is required");
59
- });
60
-
61
- it("POST returns 400 when workflow invalid", async () => {
62
- const res = await POST(
63
- new Request("https://test", {
64
- method: "POST",
65
- body: JSON.stringify({ teamId: "team1", workflow: {} }),
66
- })
67
- );
68
- expect(res.status).toBe(400);
69
- const json = await res.json();
70
- expect(json.error).toMatch(/workflow.*required/i);
71
- });
72
-
73
- it("POST returns 200 when workflow valid", async () => {
74
- const wf = { id: "wf1", name: "Test", nodes: [], edges: [] };
75
- vi.mocked(writeWorkflow).mockResolvedValue({ ok: true, path: "/home/test/wf1.workflow.json" });
76
- const res = await POST(
77
- new Request("https://test", {
78
- method: "POST",
79
- body: JSON.stringify({ teamId: "team1", workflow: wf }),
80
- })
81
- );
82
- expect(res.status).toBe(200);
83
- const json = await res.json();
84
- expect(json.ok).toBe(true);
85
- expect(writeWorkflow).toHaveBeenCalledWith("team1", expect.objectContaining({ id: "wf1", name: "Test" }));
86
- });
87
-
88
- it("DELETE returns 400 when teamId missing", async () => {
89
- const res = await DELETE(new Request("https://test"));
90
- expect(res.status).toBe(400);
91
- const json = await res.json();
92
- expect(json.error).toBe("teamId is required");
93
- });
94
-
95
- it("DELETE returns 400 when id missing", async () => {
96
- const res = await DELETE(new Request("https://test?teamId=team1"));
97
- expect(res.status).toBe(400);
98
- const json = await res.json();
99
- expect(json.error).toBe("id is required");
100
- });
101
-
102
- it("DELETE returns 200 when valid", async () => {
103
- vi.mocked(deleteWorkflow).mockResolvedValue({ ok: true, path: "/home/test/wf1.workflow.json", existed: true });
104
- const res = await DELETE(new Request("https://test?teamId=team1&id=wf1"));
105
- expect(res.status).toBe(200);
106
- const json = await res.json();
107
- expect(json.ok).toBe(true);
108
- expect(deleteWorkflow).toHaveBeenCalledWith("team1", "wf1");
109
- });
110
- });
@@ -1,60 +0,0 @@
1
- import { describe, expect, it, vi, beforeEach } from "vitest";
2
- import { POST } from "../tickets/move/route";
3
-
4
- vi.mock("@/lib/openclaw", () => ({ runOpenClaw: vi.fn() }));
5
-
6
- import { runOpenClaw } from "@/lib/openclaw";
7
-
8
- describe("api tickets move route", () => {
9
- beforeEach(() => {
10
- vi.mocked(runOpenClaw).mockReset();
11
- });
12
-
13
- it("returns 400 when ticket missing", async () => {
14
- const res = await POST(new Request("https://test", { method: "POST", body: JSON.stringify({}) }));
15
- expect(res.status).toBe(400);
16
- const json = await res.json();
17
- expect(json.error).toBe("Missing ticket");
18
- });
19
-
20
- it("returns 400 when destination invalid", async () => {
21
- const res = await POST(
22
- new Request("https://test", {
23
- method: "POST",
24
- body: JSON.stringify({ ticket: "T-1", to: "invalid" }),
25
- })
26
- );
27
- expect(res.status).toBe(400);
28
- const json = await res.json();
29
- expect(json.error).toBe("Invalid destination stage");
30
- });
31
-
32
- it("returns 200 when move succeeds", async () => {
33
- vi.mocked(runOpenClaw).mockResolvedValue({ ok: true, exitCode: 0, stdout: "", stderr: "" });
34
- const res = await POST(
35
- new Request("https://test", {
36
- method: "POST",
37
- body: JSON.stringify({ ticket: "T-1", to: "in-progress" }),
38
- })
39
- );
40
- expect(res.status).toBe(200);
41
- const json = await res.json();
42
- expect(json.ok).toBe(true);
43
- expect(runOpenClaw).toHaveBeenCalledWith(
44
- expect.arrayContaining(["recipes", "move-ticket", "--ticket", "T-1", "--to", "in-progress"])
45
- );
46
- });
47
-
48
- it("returns 500 when exec fails", async () => {
49
- vi.mocked(runOpenClaw).mockRejectedValue(new Error("Command failed"));
50
- const res = await POST(
51
- new Request("https://test", {
52
- method: "POST",
53
- body: JSON.stringify({ ticket: "T-1", to: "done" }),
54
- })
55
- );
56
- expect(res.status).toBe(500);
57
- const json = await res.json();
58
- expect(json.error).toBe("Command failed");
59
- });
60
- });
@@ -1,83 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { NextResponse } from "next/server";
4
-
5
- import { runOpenClaw } from "@/lib/openclaw";
6
- import { parseTeamRoleWorkspace } from "@/lib/agent-workspace";
7
-
8
- async function resolveAgentWorkspace(agentId: string): Promise<string | null> {
9
- try {
10
- const { stdout } = await runOpenClaw(["agents", "list", "--json"]);
11
- const list = JSON.parse(stdout) as Array<{ id: string; workspace?: string }>;
12
- const agent = list.find((a) => a.id === agentId);
13
- return agent?.workspace ? String(agent.workspace) : null;
14
- } catch {
15
- return null;
16
- }
17
- }
18
-
19
- async function moveTeamRoleToTrashOrRemove(info: {
20
- teamDir: string;
21
- role: string;
22
- roleDir: string;
23
- }): Promise<void> {
24
- const trashDir = path.join(info.teamDir, ".trash", "roles");
25
- const ts = new Date().toISOString().replace(/[:.]/g, "-");
26
- const dst = path.join(trashDir, `${info.role}-${ts}`);
27
- try {
28
- await fs.mkdir(trashDir, { recursive: true });
29
- await fs.rename(info.roleDir, dst);
30
- } catch {
31
- try {
32
- await fs.rm(info.roleDir, { recursive: true, force: true });
33
- } catch {
34
- // ignore
35
- }
36
- }
37
- }
38
-
39
- export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
40
- try {
41
- const { id } = await params;
42
- const agentId = String(id ?? "").trim();
43
- if (!agentId) return NextResponse.json({ ok: false, error: "agent id is required" }, { status: 400 });
44
-
45
- const workspace = await resolveAgentWorkspace(agentId);
46
-
47
- const result = await runOpenClaw(["agents", "delete", agentId, "--force", "--json"]);
48
- if (!result.ok) {
49
- return NextResponse.json(
50
- {
51
- ok: false,
52
- error: `Failed to delete agent: ${agentId}`,
53
- stderr: result.stderr || undefined,
54
- stdout: result.stdout || undefined,
55
- },
56
- { status: 500 },
57
- );
58
- }
59
-
60
- let parsed: unknown = null;
61
- try {
62
- parsed = JSON.parse(result.stdout);
63
- } catch {
64
- parsed = null;
65
- }
66
-
67
- if (workspace) {
68
- const info = parseTeamRoleWorkspace(workspace);
69
- if (info.kind === "teamRole") await moveTeamRoleToTrashOrRemove(info);
70
- }
71
-
72
- return NextResponse.json({ ok: true, result: parsed ?? result.stdout });
73
- } catch (err: unknown) {
74
- return NextResponse.json(
75
- {
76
- ok: false,
77
- error: "Failed to delete agent",
78
- message: err instanceof Error ? err.message : String(err),
79
- },
80
- { status: 500 },
81
- );
82
- }
83
- }
@@ -1,114 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
-
5
- import { NextResponse } from "next/server";
6
-
7
- import { getKitchenApi } from "@/lib/kitchen-api";
8
- import { teamDirFromBaseWorkspace } from "@/lib/paths";
9
-
10
- function normalizeAgentId(id: string) {
11
- const s = id.trim();
12
- if (!s) throw new Error("agent id is required");
13
- if (!/^[a-z0-9][a-z0-9-]{0,62}$/i.test(s)) {
14
- throw new Error("agent id must match /^[a-z0-9][a-z0-9-]{0,62}$/i");
15
- }
16
- return s;
17
- }
18
-
19
- function buildAgentEntry(
20
- body: { newAgentId?: string; agentId?: string; name?: string; emoji?: string; theme?: string; avatar?: string; model?: string },
21
- newAgentId: string,
22
- newWorkspace: string
23
- ): Record<string, unknown> {
24
- const identity: Record<string, unknown> = {};
25
- if (typeof body.name === "string" && body.name.trim()) identity.name = body.name.trim();
26
- if (typeof body.theme === "string" && body.theme.trim()) identity.theme = body.theme.trim();
27
- if (typeof body.emoji === "string" && body.emoji.trim()) identity.emoji = body.emoji.trim();
28
- if (typeof body.avatar === "string" && body.avatar.trim()) identity.avatar = body.avatar.trim();
29
-
30
- return {
31
- id: newAgentId,
32
- workspace: newWorkspace,
33
- ...(body.model ? { model: body.model } : {}),
34
- identity,
35
- };
36
- }
37
-
38
- export async function POST(req: Request) {
39
- try {
40
- const body = (await req.json()) as {
41
- newAgentId?: string;
42
- agentId?: string;
43
- name?: string;
44
- emoji?: string;
45
- theme?: string;
46
- avatar?: string;
47
- model?: string;
48
- overwrite?: boolean;
49
- };
50
-
51
- const newAgentId = normalizeAgentId(String(body.newAgentId ?? body.agentId ?? ""));
52
- const overwrite = Boolean(body.overwrite);
53
-
54
- const homeDir = os.homedir();
55
- if (!homeDir) {
56
- return NextResponse.json({ ok: false, error: "HOME is not set" }, { status: 500 });
57
- }
58
-
59
- const configPath = path.join(homeDir, ".openclaw", "openclaw.json");
60
- const raw = await fs.readFile(configPath, "utf8");
61
- const cfg = JSON.parse(raw) as {
62
- agents?: { defaults?: { workspace?: string }; list?: Array<Record<string, unknown>> };
63
- };
64
-
65
- const baseWorkspace = String(cfg?.agents?.defaults?.workspace ?? "").trim();
66
- if (!baseWorkspace) {
67
- return NextResponse.json({ ok: false, error: "agents.defaults.workspace not set" }, { status: 500 });
68
- }
69
-
70
- const newWorkspace = teamDirFromBaseWorkspace(baseWorkspace, newAgentId);
71
- const agentsList: Array<Record<string, unknown>> = Array.isArray(cfg?.agents?.list) ? cfg.agents.list : [];
72
- const exists = agentsList.some((a) => String(a?.id ?? "").toLowerCase() === newAgentId.toLowerCase());
73
- if (exists && !overwrite) {
74
- return NextResponse.json({ ok: false, error: `Agent already exists: ${newAgentId}` }, { status: 409 });
75
- }
76
-
77
- const nextEntry = buildAgentEntry(body, newAgentId, newWorkspace);
78
- const nextList = exists
79
- ? agentsList.map((a) => (String(a?.id ?? "").toLowerCase() === newAgentId.toLowerCase() ? nextEntry : a))
80
- : [...agentsList, nextEntry];
81
-
82
- // Ensure workspace directory exists and seed IDENTITY.md for clarity.
83
- await fs.mkdir(newWorkspace, { recursive: true });
84
- const identityMd = `# IDENTITY.md\n\n- **Name:** ${String(body.name ?? "").trim() || newAgentId}\n- **Creature:**\n- **Vibe:**\n- **Emoji:** ${String(body.emoji ?? "").trim()}\n- **Avatar:** ${String(body.avatar ?? "").trim()}\n`;
85
- await fs.writeFile(path.join(newWorkspace, "IDENTITY.md"), identityMd, "utf8");
86
-
87
- // Persist to ~/.openclaw/openclaw.json
88
- const nextCfg = {
89
- ...cfg,
90
- agents: {
91
- ...(cfg.agents ?? {}),
92
- list: nextList,
93
- },
94
- };
95
-
96
- // Write atomically.
97
- const tmpPath = `${configPath}.tmp`;
98
- const bakPath = `${configPath}.bak.${new Date().toISOString().replace(/[:.]/g, "-")}`;
99
- await fs.writeFile(tmpPath, JSON.stringify(nextCfg, null, 2) + "\n", "utf8");
100
- await fs.copyFile(configPath, bakPath).catch(() => {});
101
- await fs.rename(tmpPath, configPath);
102
-
103
- // Restart gateway so the new agent is live.
104
- const api = getKitchenApi();
105
- await api.runtime.system.runCommandWithTimeout(["openclaw", "gateway", "restart"], { timeoutMs: 120000 });
106
-
107
- return NextResponse.json({ ok: true, agentId: newAgentId, workspace: newWorkspace, restarted: true });
108
- } catch (err) {
109
- const msg = err instanceof Error ? err.message : String(err);
110
- // Prefer 400 for validation/input errors; otherwise 500.
111
- const status = /required|match \//i.test(msg) ? 400 : 500;
112
- return NextResponse.json({ ok: false, error: msg }, { status });
113
- }
114
- }
@@ -1,45 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { NextResponse } from "next/server";
4
- import { errorMessage } from "@/lib/errors";
5
- import { assertSafeRelativeFileName } from "@/lib/paths";
6
- import { getAgentContextFromBody, getAgentContextFromQuery } from "@/lib/api-route-helpers";
7
-
8
- export async function GET(req: Request) {
9
- const ctx = await getAgentContextFromQuery(req);
10
- if (ctx instanceof NextResponse) return ctx;
11
- const { agentId, ws } = ctx;
12
-
13
- const { searchParams } = new URL(req.url);
14
- const name = String(searchParams.get("name") ?? "").trim();
15
- if (!name) return NextResponse.json({ ok: false, error: "name is required" }, { status: 400 });
16
-
17
- const safe = assertSafeRelativeFileName(name);
18
- const filePath = path.join(ws, safe);
19
-
20
- try {
21
- const content = await fs.readFile(filePath, "utf8");
22
- return NextResponse.json({ ok: true, agentId, workspace: ws, name: safe, filePath, content });
23
- } catch (e: unknown) {
24
- return NextResponse.json({ ok: false, error: errorMessage(e) }, { status: 404 });
25
- }
26
- }
27
-
28
- export async function PUT(req: Request) {
29
- const body = (await req.json()) as { agentId?: string; name?: string; content?: string };
30
- const name = String(body.name ?? "").trim();
31
- const content = typeof body.content === "string" ? body.content : null;
32
- if (!name) return NextResponse.json({ ok: false, error: "name is required" }, { status: 400 });
33
- if (content === null) return NextResponse.json({ ok: false, error: "content is required" }, { status: 400 });
34
-
35
- const ctx = await getAgentContextFromBody(body);
36
- if (ctx instanceof NextResponse) return ctx;
37
- const { agentId, ws } = ctx;
38
-
39
- const safe = assertSafeRelativeFileName(name);
40
- const filePath = path.join(ws, safe);
41
-
42
- await fs.mkdir(path.dirname(filePath), { recursive: true });
43
- await fs.writeFile(filePath, content, "utf8");
44
- return NextResponse.json({ ok: true, agentId, workspace: ws, name: safe, filePath });
45
- }
@@ -1,23 +0,0 @@
1
- import { NextResponse } from "next/server";
2
- import { getAgentContextFromQuery, listWorkspaceFiles } from "@/lib/api-route-helpers";
3
-
4
- export async function GET(req: Request) {
5
- const ctx = await getAgentContextFromQuery(req);
6
- if (ctx instanceof NextResponse) return ctx;
7
- const { agentId, ws } = ctx;
8
-
9
- const candidates = [
10
- { name: "SOUL.md", required: true, rationale: "Agent persona/instructions" },
11
- { name: "AGENTS.md", required: true, rationale: "Agent operating rules" },
12
- { name: "TOOLS.md", required: true, rationale: "Agent local notes" },
13
- { name: "STATUS.md", required: false, rationale: "Optional current status snapshot" },
14
- { name: "NOTES.md", required: false, rationale: "Optional scratchpad" },
15
- { name: "IDENTITY.md", required: false, rationale: "Optional identity (name/emoji/avatar)" },
16
- { name: "USER.md", required: false, rationale: "Optional user profile" },
17
- { name: "HEARTBEAT.md", required: false, rationale: "Optional periodic checklist" },
18
- { name: "MEMORY.md", required: false, rationale: "Optional curated memory" },
19
- ];
20
-
21
- const files = await listWorkspaceFiles(ws, candidates);
22
- return NextResponse.json({ ok: true, agentId, workspace: ws, files });
23
- }
@@ -1,41 +0,0 @@
1
- import { NextResponse } from "next/server";
2
-
3
- import { runOpenClaw } from "@/lib/openclaw";
4
-
5
- type Body = {
6
- agentId: string;
7
- name?: string;
8
- emoji?: string;
9
- theme?: string;
10
- avatar?: string;
11
- };
12
-
13
- export async function POST(req: Request) {
14
- try {
15
- const body = (await req.json()) as Body;
16
- const agentId = body.agentId?.trim();
17
-
18
- if (!agentId) {
19
- return NextResponse.json({ error: "agentId is required" }, { status: 400 });
20
- }
21
-
22
- const args = ["agents", "set-identity", agentId];
23
-
24
- if (body.name?.trim()) args.push("--name", body.name.trim());
25
- if (body.emoji?.trim()) args.push("--emoji", body.emoji.trim());
26
- if (body.theme?.trim()) args.push("--theme", body.theme.trim());
27
- if (body.avatar?.trim()) args.push("--avatar", body.avatar.trim());
28
-
29
- const { stdout, stderr } = await runOpenClaw(args);
30
-
31
- return NextResponse.json({ ok: true, stdout, stderr });
32
- } catch (err) {
33
- return NextResponse.json(
34
- {
35
- error: "Failed to set agent identity",
36
- message: err instanceof Error ? err.message : String(err),
37
- },
38
- { status: 500 },
39
- );
40
- }
41
- }
@@ -1,22 +0,0 @@
1
- import { NextResponse } from "next/server";
2
- import { type AgentListItem } from "@/lib/agents";
3
- import { errorMessage } from "@/lib/errors";
4
- import { runOpenClaw } from "@/lib/openclaw";
5
-
6
- export async function GET() {
7
- try {
8
- const { stdout, stderr } = await runOpenClaw(["agents", "list", "--json"]);
9
-
10
- const agents = JSON.parse(stdout) as AgentListItem[];
11
-
12
- return NextResponse.json({ agents, stderr: stderr || undefined });
13
- } catch (err) {
14
- return NextResponse.json(
15
- {
16
- error: "Failed to list agents",
17
- message: errorMessage(err),
18
- },
19
- { status: 500 },
20
- );
21
- }
22
- }
@@ -1,34 +0,0 @@
1
- import { NextResponse } from "next/server";
2
- import { runOpenClaw } from "@/lib/openclaw";
3
- import { parseTeamRoleWorkspace } from "@/lib/agent-workspace";
4
- import { installSkillErrorResponse } from "@/lib/api-route-helpers";
5
-
6
- export async function POST(req: Request) {
7
- const body = (await req.json()) as { agentId?: string; skill?: string };
8
- const agentId = String(body.agentId ?? "").trim();
9
- const skill = String(body.skill ?? "").trim();
10
-
11
- if (!agentId) return NextResponse.json({ ok: false, error: "agentId is required" }, { status: 400 });
12
- if (!skill) return NextResponse.json({ ok: false, error: "skill is required" }, { status: 400 });
13
-
14
- // For role-based team agents, install at team scope to avoid creating a separate
15
- // workspace-<agentId> directory that diverges from roles/<role>.
16
- let args = ["recipes", "install-skill", skill, "--agent-id", agentId, "--yes"];
17
- try {
18
- const { stdout } = await runOpenClaw(["agents", "list", "--json"]);
19
- const list = JSON.parse(stdout) as Array<{ id: string; workspace?: string }>;
20
- const agent = list.find((a) => a.id === agentId);
21
- const ws = agent?.workspace ? String(agent.workspace) : "";
22
- const info = parseTeamRoleWorkspace(ws);
23
- if (info.kind === "teamRole") {
24
- args = ["recipes", "install-skill", skill, "--team-id", info.teamId, "--yes"];
25
- }
26
- } catch {
27
- // If anything goes wrong, fall back to agent scope.
28
- }
29
-
30
- const res = await runOpenClaw(args);
31
- if (!res.ok) return installSkillErrorResponse(args, res, { scopeArgs: args });
32
-
33
- return NextResponse.json({ ok: true, agentId, skill, scopeArgs: args, stdout: res.stdout, stderr: res.stderr });
34
- }
@@ -1,39 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { NextResponse } from "next/server";
4
- import { parseTeamRoleWorkspace } from "@/lib/agent-workspace";
5
- import { getAgentContextFromQuery } from "@/lib/api-route-helpers";
6
- import { errorMessage } from "@/lib/errors";
7
-
8
- export async function GET(req: Request) {
9
- const ctx = await getAgentContextFromQuery(req);
10
- if (ctx instanceof NextResponse) return ctx;
11
- const { agentId, ws } = ctx;
12
-
13
- const info = parseTeamRoleWorkspace(ws);
14
- const dirs: string[] = [path.join(ws, "skills")];
15
- if (info.kind === "teamRole") {
16
- dirs.push(path.join(info.teamDir, "skills"));
17
- }
18
-
19
- const skills = new Set<string>();
20
- const notes: string[] = [];
21
-
22
- for (const skillsDir of dirs) {
23
- try {
24
- const entries = await fs.readdir(skillsDir, { withFileTypes: true });
25
- for (const e of entries) if (e.isDirectory()) skills.add(e.name);
26
- } catch (e: unknown) {
27
- notes.push(`${skillsDir}: ${errorMessage(e)}`);
28
- }
29
- }
30
-
31
- return NextResponse.json({
32
- ok: true,
33
- agentId,
34
- workspace: ws,
35
- skillsDirs: dirs,
36
- skills: Array.from(skills).sort((a, b) => a.localeCompare(b)),
37
- note: notes.length ? notes.join("; ") : undefined,
38
- });
39
- }
@@ -1,52 +0,0 @@
1
- import { NextResponse } from "next/server";
2
- import { gatewayConfigGet, gatewayConfigPatch } from "@/lib/gateway";
3
-
4
- function normalizeAgentId(id: string) {
5
- const s = id.trim();
6
- if (!s) throw new Error("agentId is required");
7
- return s;
8
- }
9
-
10
- export async function POST(req: Request) {
11
- const body = (await req.json()) as {
12
- agentId?: string;
13
- patch?: {
14
- workspace?: string;
15
- model?: string;
16
- identity?: { name?: string; theme?: string; emoji?: string; avatar?: string };
17
- };
18
- };
19
-
20
- const agentId = normalizeAgentId(String(body.agentId ?? ""));
21
- const patch = body.patch ?? {};
22
-
23
- const { raw } = await gatewayConfigGet();
24
- const cfg = JSON.parse(raw) as { agents?: { list?: Array<Record<string, unknown>> } };
25
-
26
- const list = Array.isArray(cfg.agents?.list) ? (cfg.agents?.list as Array<Record<string, unknown>>) : [];
27
- const idx = list.findIndex((a) => String(a.id ?? "").toLowerCase() === agentId.toLowerCase());
28
- if (idx === -1) return NextResponse.json({ ok: false, error: `Agent not found in config: ${agentId}` }, { status: 404 });
29
-
30
- const current = list[idx] ?? {};
31
- const currentIdentity = (current.identity ?? {}) as Record<string, unknown>;
32
-
33
- const next: Record<string, unknown> = {
34
- ...current,
35
- ...(typeof patch.workspace === "string" && patch.workspace.trim() ? { workspace: patch.workspace.trim() } : {}),
36
- ...(typeof patch.model === "string" && patch.model.trim() ? { model: patch.model.trim() } : {}),
37
- identity: {
38
- ...currentIdentity,
39
- ...(typeof patch.identity?.name === "string" ? { name: patch.identity.name } : {}),
40
- ...(typeof patch.identity?.theme === "string" ? { theme: patch.identity.theme } : {}),
41
- ...(typeof patch.identity?.emoji === "string" ? { emoji: patch.identity.emoji } : {}),
42
- ...(typeof patch.identity?.avatar === "string" ? { avatar: patch.identity.avatar } : {}),
43
- },
44
- };
45
-
46
- const nextList = list.slice();
47
- nextList[idx] = next;
48
-
49
- await gatewayConfigPatch({ agents: { list: nextList } }, `ClawKitchen: update agent ${agentId}`);
50
-
51
- return NextResponse.json({ ok: true, agentId });
52
- }