@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,80 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { NextResponse } from "next/server";
4
- import { getWorkspaceRecipesDir } from "@/lib/paths";
5
- import {
6
- splitFrontmatter,
7
- normalizeRole,
8
- parseRecipeFrontmatter,
9
- buildNextMarkdown,
10
- handleRemove,
11
- handleAdd,
12
- handleAddLike,
13
- } from "./helpers";
14
-
15
- export async function POST(req: Request) {
16
- const body = (await req.json()) as {
17
- recipeId?: string;
18
- op?: "add" | "remove" | "addLike";
19
- role?: string;
20
- baseRole?: string;
21
- teamId?: string;
22
- name?: string;
23
- };
24
-
25
- const recipeId = String(body.recipeId ?? "").trim();
26
- const op = body.op;
27
- if (!recipeId) return NextResponse.json({ ok: false, error: "recipeId is required" }, { status: 400 });
28
- if (op !== "add" && op !== "remove" && op !== "addLike") {
29
- return NextResponse.json({ ok: false, error: "op must be add|remove|addLike" }, { status: 400 });
30
- }
31
-
32
- const name = typeof body.name === "string" ? body.name.trim() : "";
33
-
34
- const dir = await getWorkspaceRecipesDir();
35
- const filePath = path.join(dir, `${recipeId}.md`);
36
-
37
- const md = await fs.readFile(filePath, "utf8");
38
- const { yamlText, rest } = splitFrontmatter(md);
39
- const { fm, agents, templates } = parseRecipeFrontmatter(yamlText);
40
-
41
- const kind = String(fm.kind ?? "");
42
- if (kind && kind !== "team") {
43
- return NextResponse.json({ ok: false, error: `recipe kind must be team (got ${kind})` }, { status: 400 });
44
- }
45
-
46
- let result: { nextAgents: Array<Record<string, unknown>>; nextTemplates: Record<string, unknown>; addedRole: string | null } | NextResponse;
47
-
48
- if (op === "remove") {
49
- const role = normalizeRole(String(body.role ?? ""));
50
- result = handleRemove(agents, templates, role);
51
- } else if (op === "add") {
52
- const role = normalizeRole(String(body.role ?? ""));
53
- result = handleAdd(agents, templates, role, name);
54
- } else {
55
- const baseRole = normalizeRole(String(body.baseRole ?? ""));
56
- const teamId = String(body.teamId ?? "").trim();
57
- result = await handleAddLike(agents, templates, baseRole, name, teamId);
58
- }
59
-
60
- if (result instanceof NextResponse) return result;
61
-
62
- const { nextAgents, nextTemplates, addedRole } = result;
63
- nextAgents.sort((a, b) => String(a.role ?? "").localeCompare(String(b.role ?? "")));
64
-
65
- const nextMd = buildNextMarkdown(fm, nextAgents, nextTemplates, rest);
66
- await fs.writeFile(filePath, nextMd, "utf8");
67
-
68
- const teamId = typeof body.teamId === "string" ? body.teamId.trim() : "";
69
- const addedAgentId = teamId && addedRole ? `${teamId}-${addedRole}` : null;
70
-
71
- return NextResponse.json({
72
- ok: true,
73
- recipeId,
74
- filePath,
75
- agents: nextAgents,
76
- content: nextMd,
77
- addedRole,
78
- addedAgentId,
79
- });
80
- }
@@ -1,186 +0,0 @@
1
- import { describe, expect, it, vi, beforeEach } from "vitest";
2
- import {
3
- validateAgentId,
4
- validateTeamId,
5
- withCronOverride,
6
- persistTeamProvenance,
7
- persistAgentProvenance,
8
- } from "../helpers";
9
-
10
- vi.mock("@/lib/openclaw", () => ({
11
- runOpenClaw: vi.fn(),
12
- }));
13
- vi.mock("@/lib/paths", () => ({
14
- readOpenClawConfig: vi.fn(),
15
- getWorkspaceRecipesDir: vi.fn(),
16
- }));
17
- vi.mock("node:fs/promises", () => ({
18
- default: {
19
- stat: vi.fn(),
20
- mkdir: vi.fn(),
21
- writeFile: vi.fn(),
22
- readFile: vi.fn(),
23
- },
24
- }));
25
-
26
- import { runOpenClaw } from "@/lib/openclaw";
27
- import { readOpenClawConfig, getWorkspaceRecipesDir } from "@/lib/paths";
28
- import fs from "node:fs/promises";
29
-
30
- describe("scaffold helpers", () => {
31
- beforeEach(() => {
32
- vi.mocked(runOpenClaw).mockReset();
33
- vi.mocked(readOpenClawConfig).mockReset();
34
- vi.mocked(getWorkspaceRecipesDir).mockReset();
35
- vi.mocked(fs.stat).mockReset();
36
- vi.mocked(fs.mkdir).mockReset();
37
- vi.mocked(fs.writeFile).mockReset();
38
- vi.mocked(fs.readFile).mockReset();
39
- });
40
-
41
- describe("validateAgentId", () => {
42
- it("returns null when agentId empty", async () => {
43
- const result = await validateAgentId("", new Set(["recipe1"]));
44
- expect(result).toBeNull();
45
- });
46
-
47
- it("returns 409 when agentId matches recipe id", async () => {
48
- const result = await validateAgentId("my-recipe", new Set(["my-recipe"]));
49
- expect(result).not.toBeNull();
50
- expect((result as Response).status).toBe(409);
51
- const json = await (result as Response).json();
52
- expect(json.error).toContain("cannot match an existing recipe id");
53
- });
54
-
55
- it("returns null when agent does not exist", async () => {
56
- vi.mocked(runOpenClaw).mockResolvedValue({
57
- ok: true,
58
- stdout: JSON.stringify([{ id: "other-agent" }]),
59
- stderr: "",
60
- exitCode: 0,
61
- });
62
- const result = await validateAgentId("new-agent", new Set());
63
- expect(result).toBeNull();
64
- });
65
-
66
- it("returns 409 when agent already exists", async () => {
67
- vi.mocked(runOpenClaw).mockResolvedValue({
68
- ok: true,
69
- stdout: JSON.stringify([{ id: "existing-agent" }]),
70
- stderr: "",
71
- exitCode: 0,
72
- });
73
- const result = await validateAgentId("existing-agent", new Set());
74
- expect(result).not.toBeNull();
75
- expect((result as Response).status).toBe(409);
76
- const json = await (result as Response).json();
77
- expect(json.error).toContain("Agent already exists");
78
- });
79
- });
80
-
81
- describe("validateTeamId", () => {
82
- it("returns null when teamId empty", async () => {
83
- const result = await validateTeamId("", new Set());
84
- expect(result).toBeNull();
85
- });
86
-
87
- it("returns 409 when teamId matches recipe id", async () => {
88
- const result = await validateTeamId("my-recipe", new Set(["my-recipe"]));
89
- expect(result).not.toBeNull();
90
- expect((result as Response).status).toBe(409);
91
- const json = await (result as Response).json();
92
- expect(json.error).toContain("cannot match an existing recipe id");
93
- });
94
-
95
- it("returns 409 when team workspace exists", async () => {
96
- vi.mocked(readOpenClawConfig).mockResolvedValue({
97
- agents: { defaults: { workspace: "/home/.openclaw/workspace" } },
98
- } as never);
99
- vi.mocked(fs.stat).mockResolvedValue({} as never);
100
- const result = await validateTeamId("team1", new Set());
101
- expect(result).not.toBeNull();
102
- expect((result as Response).status).toBe(409);
103
- const json = await (result as Response).json();
104
- expect(json.error).toContain("Team workspace already exists");
105
- });
106
-
107
- it("returns null when team workspace does not exist and no agents", async () => {
108
- vi.mocked(readOpenClawConfig).mockResolvedValue({
109
- agents: { defaults: { workspace: "/home/.openclaw/workspace" } },
110
- } as never);
111
- vi.mocked(fs.stat).mockRejectedValue(new Error("not found"));
112
- vi.mocked(runOpenClaw).mockResolvedValue({
113
- ok: true,
114
- stdout: JSON.stringify([]),
115
- stderr: "",
116
- exitCode: 0,
117
- });
118
- const result = await validateTeamId("team1", new Set());
119
- expect(result).toBeNull();
120
- });
121
- });
122
-
123
- describe("withCronOverride", () => {
124
- it("runs fn without override when override undefined", async () => {
125
- const fn = vi.fn().mockResolvedValue(42);
126
- const result = await withCronOverride(undefined, fn);
127
- expect(result).toBe(42);
128
- expect(fn).toHaveBeenCalledTimes(1);
129
- expect(runOpenClaw).not.toHaveBeenCalled();
130
- });
131
-
132
- it("runs fn and restores config when override yes", async () => {
133
- vi.mocked(runOpenClaw)
134
- .mockResolvedValueOnce({ ok: true, stdout: "off", stderr: "", exitCode: 0 })
135
- .mockResolvedValueOnce({ ok: true, stdout: "", stderr: "", exitCode: 0 })
136
- .mockResolvedValueOnce({ ok: true, stdout: "", stderr: "", exitCode: 0 });
137
- const fn = vi.fn().mockResolvedValue("done");
138
- const result = await withCronOverride("yes", fn);
139
- expect(result).toBe("done");
140
- expect(runOpenClaw).toHaveBeenCalledTimes(3); // get, set on, set restore
141
- });
142
- });
143
-
144
- describe("persistTeamProvenance", () => {
145
- it("writes team.json when workspace configured", async () => {
146
- vi.mocked(readOpenClawConfig).mockResolvedValue({
147
- agents: { defaults: { workspace: "/home/.openclaw/workspace" } },
148
- } as never);
149
- vi.mocked(runOpenClaw).mockResolvedValue({
150
- ok: true,
151
- stdout: JSON.stringify([{ id: "r1", name: "Recipe 1" }]),
152
- stderr: "",
153
- exitCode: 0,
154
- });
155
- vi.mocked(getWorkspaceRecipesDir).mockResolvedValue("/workspace/recipes");
156
- vi.mocked(fs.readFile).mockResolvedValue("---\nkind: team\n---\nbody");
157
- await persistTeamProvenance("team1", "recipe1", "abc123");
158
- expect(fs.mkdir).toHaveBeenCalled();
159
- expect(fs.writeFile).toHaveBeenCalled();
160
- const writeCall = vi.mocked(fs.writeFile).mock.calls[0];
161
- expect(writeCall[0]).toContain("team.json");
162
- expect(writeCall[1]).toContain("team1");
163
- expect(writeCall[1]).toContain("recipe1");
164
- });
165
- });
166
-
167
- describe("persistAgentProvenance", () => {
168
- it("writes agent.json when workspace configured", async () => {
169
- vi.mocked(readOpenClawConfig).mockResolvedValue({
170
- agents: { defaults: { workspace: "/home/.openclaw/workspace" } },
171
- } as never);
172
- vi.mocked(runOpenClaw).mockResolvedValue({
173
- ok: true,
174
- stdout: JSON.stringify([{ id: "r1", name: "Recipe 1" }]),
175
- stderr: "",
176
- exitCode: 0,
177
- });
178
- await persistAgentProvenance("agent1", "recipe1", null);
179
- expect(fs.mkdir).toHaveBeenCalled();
180
- expect(fs.writeFile).toHaveBeenCalled();
181
- const writeCall = vi.mocked(fs.writeFile).mock.calls[0];
182
- expect(writeCall[0]).toContain("agent.json");
183
- expect(writeCall[1]).toContain("agent1");
184
- });
185
- });
186
- });
@@ -1,214 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import YAML from "yaml";
4
- import { runOpenClaw } from "@/lib/openclaw";
5
- import { readOpenClawConfig, getWorkspaceRecipesDir } from "@/lib/paths";
6
- import { NextResponse } from "next/server";
7
-
8
- const TEAM_META_FILE = "team.json";
9
- const AGENT_META_FILE = "agent.json";
10
-
11
- function teamDirFromTeamId(baseWorkspace: string, teamId: string) {
12
- return path.resolve(baseWorkspace, "..", `workspace-${teamId}`);
13
- }
14
-
15
- export async function validateAgentId(
16
- agentId: string,
17
- recipeIds: Set<string>
18
- ): Promise<NextResponse | null> {
19
- if (!agentId) return null;
20
- if (recipeIds.has(agentId)) {
21
- return NextResponse.json(
22
- { ok: false, error: `Agent id cannot match an existing recipe id: ${agentId}. Choose a new agent id.` },
23
- { status: 409 }
24
- );
25
- }
26
- const agentsRes = await runOpenClaw(["agents", "list", "--json"]);
27
- if (!agentsRes.ok) return null;
28
- try {
29
- const agents = JSON.parse(agentsRes.stdout) as Array<{ id?: unknown }>;
30
- const exists = agents.some((a) => String(a.id ?? "").trim() === agentId);
31
- if (exists) {
32
- return NextResponse.json(
33
- { ok: false, error: `Agent already exists: ${agentId}. Choose a new id or enable overwrite.` },
34
- { status: 409 }
35
- );
36
- }
37
- } catch {
38
- // ignore parse errors
39
- }
40
- return null;
41
- }
42
-
43
- export async function validateTeamId(
44
- teamId: string,
45
- recipeIds: Set<string>
46
- ): Promise<NextResponse | null> {
47
- if (!teamId) return null;
48
- if (recipeIds.has(teamId)) {
49
- return NextResponse.json(
50
- { ok: false, error: `Team id cannot match an existing recipe id: ${teamId}. Choose a new team id.` },
51
- { status: 409 }
52
- );
53
- }
54
- try {
55
- const cfg = await readOpenClawConfig();
56
- const baseWorkspace = String(cfg.agents?.defaults?.workspace ?? "").trim();
57
- if (baseWorkspace) {
58
- const teamDir = teamDirFromTeamId(baseWorkspace, teamId);
59
- const hasWorkspace = await fs.stat(teamDir).then(() => true).catch(() => false);
60
- if (hasWorkspace) {
61
- return NextResponse.json(
62
- { ok: false, error: `Team workspace already exists: ${teamId}. Choose a new id or enable overwrite.` },
63
- { status: 409 }
64
- );
65
- }
66
- }
67
- } catch {
68
- // ignore
69
- }
70
- const agentsRes = await runOpenClaw(["agents", "list", "--json"]);
71
- if (!agentsRes.ok) return null;
72
- try {
73
- const agents = JSON.parse(agentsRes.stdout) as Array<{ id?: unknown }>;
74
- const hasAgents = agents.some((a) => String(a.id ?? "").startsWith(`${teamId}-`));
75
- if (hasAgents) {
76
- return NextResponse.json(
77
- { ok: false, error: `Team agents already exist for team: ${teamId}. Choose a new id or enable overwrite.` },
78
- { status: 409 }
79
- );
80
- }
81
- } catch {
82
- // ignore
83
- }
84
- return null;
85
- }
86
-
87
- export async function withCronOverride<T>(
88
- override: "yes" | "no" | undefined,
89
- fn: () => Promise<T>
90
- ): Promise<T> {
91
- let prevCronInstallation: string | null = null;
92
- if (override === "yes" || override === "no") {
93
- const cfgPath = "plugins.entries.recipes.config.cronInstallation";
94
- const prev = await runOpenClaw(["config", "get", cfgPath]);
95
- prevCronInstallation = prev.stdout.trim() || null;
96
- const next = override === "yes" ? "on" : "off";
97
- await runOpenClaw(["config", "set", cfgPath, next]);
98
- }
99
- try {
100
- return await fn();
101
- } finally {
102
- if (prevCronInstallation !== null) {
103
- try {
104
- await runOpenClaw(["config", "set", "plugins.entries.recipes.config.cronInstallation", prevCronInstallation]);
105
- } catch {
106
- // best-effort restore
107
- }
108
- }
109
- }
110
- }
111
-
112
- async function getRecipeName(recipeId: string): Promise<string | undefined> {
113
- try {
114
- const list = await runOpenClaw(["recipes", "list"]);
115
- if (!list.ok) return undefined;
116
- const items = JSON.parse(list.stdout) as Array<{ id?: string; name?: string }>;
117
- const hit = items.find((r) => String(r.id ?? "").trim() === recipeId);
118
- return String(hit?.name ?? "").trim() || undefined;
119
- } catch {
120
- return undefined;
121
- }
122
- }
123
-
124
- async function persistProvenance(opts: {
125
- id: string;
126
- idKey: "teamId" | "agentId";
127
- recipeId: string;
128
- recipeHash: string | null;
129
- dirResolver: (baseWorkspace: string) => string;
130
- metaFileName: string;
131
- }): Promise<void> {
132
- try {
133
- const cfg = await readOpenClawConfig();
134
- const baseWorkspace = String(cfg.agents?.defaults?.workspace ?? "").trim();
135
- if (!baseWorkspace) return;
136
-
137
- const dir = opts.dirResolver(baseWorkspace);
138
- const recipeName = await getRecipeName(opts.recipeId);
139
- const now = new Date().toISOString();
140
- const meta = {
141
- [opts.idKey]: opts.id,
142
- recipeId: opts.recipeId,
143
- ...(recipeName ? { recipeName } : {}),
144
- ...(opts.recipeHash ? { recipeHash: opts.recipeHash } : {}),
145
- scaffoldedAt: now,
146
- attachedAt: now,
147
- };
148
-
149
- await fs.mkdir(dir, { recursive: true });
150
- await fs.writeFile(path.join(dir, opts.metaFileName), JSON.stringify(meta, null, 2) + "\n", "utf8");
151
- } catch {
152
- // best-effort only
153
- }
154
- }
155
-
156
- export async function persistTeamProvenance(
157
- teamId: string,
158
- recipeId: string,
159
- recipeHash: string | null
160
- ): Promise<void> {
161
- await persistProvenance({
162
- id: teamId,
163
- idKey: "teamId",
164
- recipeId,
165
- recipeHash,
166
- dirResolver: (base) => teamDirFromTeamId(base, teamId),
167
- metaFileName: TEAM_META_FILE,
168
- });
169
-
170
- // Best-effort: ensure team.teamId matches in generated recipe
171
- try {
172
- const cfg = await readOpenClawConfig();
173
- const baseWorkspace = String(cfg.agents?.defaults?.workspace ?? "").trim();
174
- if (!baseWorkspace) return;
175
- const recipesDir = await getWorkspaceRecipesDir();
176
- const recipePath = path.join(recipesDir, `${teamId}.md`);
177
- const md = await fs.readFile(recipePath, "utf8");
178
- if (md.startsWith("---\n")) {
179
- const end = md.indexOf("\n---\n", 4);
180
- if (end !== -1) {
181
- const yamlText = md.slice(4, end + 1);
182
- const rest = md.slice(end + 5);
183
- const fm = (YAML.parse(yamlText) ?? {}) as Record<string, unknown>;
184
- const nextFm: Record<string, unknown> = {
185
- ...fm,
186
- team: {
187
- ...(typeof fm.team === "object" && fm.team ? (fm.team as Record<string, unknown>) : {}),
188
- teamId,
189
- },
190
- };
191
- const nextYaml = YAML.stringify(nextFm).trimEnd();
192
- const nextMd = `---\n${nextYaml}\n---\n${rest}`;
193
- if (nextMd !== md) await fs.writeFile(recipePath, nextMd, "utf8");
194
- }
195
- }
196
- } catch {
197
- // ignore
198
- }
199
- }
200
-
201
- export async function persistAgentProvenance(
202
- agentId: string,
203
- recipeId: string,
204
- recipeHash: string | null
205
- ): Promise<void> {
206
- await persistProvenance({
207
- id: agentId,
208
- idKey: "agentId",
209
- recipeId,
210
- recipeHash,
211
- dirResolver: (base) => path.resolve(base, "agents", agentId),
212
- metaFileName: AGENT_META_FILE,
213
- });
214
- }
@@ -1,95 +0,0 @@
1
- import crypto from "node:crypto";
2
- import { NextResponse } from "next/server";
3
- import { runOpenClaw } from "@/lib/openclaw";
4
- import { buildScaffoldArgs } from "@/lib/scaffold";
5
- import {
6
- validateAgentId,
7
- validateTeamId,
8
- withCronOverride,
9
- persistTeamProvenance,
10
- persistAgentProvenance,
11
- } from "./helpers";
12
-
13
- type ReqBody = Parameters<typeof buildScaffoldArgs>[0] & {
14
- cronInstallChoice?: "yes" | "no";
15
- allowExisting?: boolean;
16
- };
17
-
18
- const asString = (v: unknown) => {
19
- if (typeof v === "string") return v;
20
- if (v instanceof Uint8Array) return new TextDecoder().decode(v);
21
- if (v && typeof (v as { toString?: unknown }).toString === "function") return String(v);
22
- return "";
23
- };
24
-
25
- function sha256(text: string) {
26
- return crypto.createHash("sha256").update(text, "utf8").digest("hex");
27
- }
28
-
29
- async function getRecipeIds(): Promise<Set<string>> {
30
- const recipesRes = await runOpenClaw(["recipes", "list"]);
31
- if (!recipesRes.ok) return new Set();
32
- try {
33
- const recipes = JSON.parse(recipesRes.stdout) as Array<{ id?: unknown }>;
34
- return new Set(recipes.map((r) => String(r.id ?? "").trim()).filter(Boolean));
35
- } catch {
36
- return new Set();
37
- }
38
- }
39
-
40
- export async function POST(req: Request) {
41
- const body = (await req.json()) as ReqBody;
42
-
43
- let recipeHash: string | null = null;
44
- try {
45
- const shown = await runOpenClaw(["recipes", "show", body.recipeId]);
46
- if (shown.ok) recipeHash = sha256(shown.stdout);
47
- } catch {
48
- // ignore
49
- }
50
-
51
- const args = buildScaffoldArgs(body, { allowExisting: body.allowExisting });
52
-
53
- try {
54
- if (!body.overwrite && !body.allowExisting) {
55
- const recipeIds = await getRecipeIds();
56
- if (body.kind === "agent") {
57
- const agentId = String(body.agentId ?? "").trim();
58
- const err = await validateAgentId(agentId, recipeIds);
59
- if (err) return err;
60
- } else {
61
- const teamId = String(body.teamId ?? "").trim();
62
- const err = await validateTeamId(teamId, recipeIds);
63
- if (err) return err;
64
- }
65
- }
66
-
67
- return await withCronOverride(body.cronInstallChoice, async () => {
68
- const { stdout, stderr } = await runOpenClaw(args);
69
-
70
- if (body.kind === "team") {
71
- const teamId = String(body.teamId ?? "").trim();
72
- if (teamId) await persistTeamProvenance(teamId, body.recipeId, recipeHash);
73
- }
74
-
75
- if (body.kind === "agent") {
76
- const agentId = String(body.agentId ?? "").trim();
77
- if (agentId) await persistAgentProvenance(agentId, body.recipeId, recipeHash);
78
- }
79
-
80
- return NextResponse.json({ ok: true, args, stdout, stderr });
81
- });
82
- } catch (e: unknown) {
83
- const err = e as { message?: string; stdout?: unknown; stderr?: unknown };
84
- return NextResponse.json(
85
- {
86
- ok: false,
87
- args,
88
- error: err?.message ?? String(e),
89
- stdout: asString(err?.stdout),
90
- stderr: asString(err?.stderr),
91
- },
92
- { status: 500 }
93
- );
94
- }
95
- }
@@ -1,58 +0,0 @@
1
- import { NextResponse } from "next/server";
2
- import { errorMessage } from "@/lib/errors";
3
- import { gatewayConfigGet, gatewayConfigPatch } from "@/lib/gateway";
4
-
5
- const CFG_PATH = "plugins.entries.recipes.config.cronInstallation";
6
-
7
- function getPath(obj: unknown, p: string): unknown {
8
- return p.split(".").reduce<unknown>((acc, k) => {
9
- if (!acc || typeof acc !== "object") return undefined;
10
- return (acc as Record<string, unknown>)[k];
11
- }, obj);
12
- }
13
-
14
- export async function GET() {
15
- try {
16
- const { raw } = await gatewayConfigGet();
17
- const cfg = JSON.parse(raw);
18
- const value = String(getPath(cfg, CFG_PATH) ?? "").trim();
19
- return NextResponse.json({ ok: true, path: CFG_PATH, value });
20
- } catch (e: unknown) {
21
- return NextResponse.json(
22
- { ok: false, error: errorMessage(e) },
23
- { status: 500 },
24
- );
25
- }
26
- }
27
-
28
- export async function PUT(req: Request) {
29
- try {
30
- const body = (await req.json()) as { value?: string };
31
- const value = String(body.value ?? "").trim();
32
- if (!value || !["off", "prompt", "on"].includes(value)) {
33
- return NextResponse.json({ ok: false, error: "value must be one of: off|prompt|on" }, { status: 400 });
34
- }
35
-
36
- await gatewayConfigPatch(
37
- {
38
- plugins: {
39
- entries: {
40
- recipes: {
41
- config: {
42
- cronInstallation: value,
43
- },
44
- },
45
- },
46
- },
47
- },
48
- `ClawKitchen: set ${CFG_PATH}=${value}`
49
- );
50
-
51
- return NextResponse.json({ ok: true, path: CFG_PATH, value, note: "Gateway will restart to apply config." });
52
- } catch (e: unknown) {
53
- return NextResponse.json(
54
- { ok: false, error: errorMessage(e) },
55
- { status: 500 },
56
- );
57
- }
58
- }
@@ -1,23 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import os from "node:os";
4
- import { NextResponse } from "next/server";
5
- import { errorMessage } from "@/lib/errors";
6
-
7
- export async function GET() {
8
- const home = os.homedir();
9
- const globalSkillsDir = path.join(home, ".openclaw", "skills");
10
-
11
- try {
12
- const entries = await fs.readdir(globalSkillsDir, { withFileTypes: true });
13
- const skills = entries
14
- .filter((e) => e.isDirectory())
15
- .map((e) => e.name)
16
- .sort((a, b) => a.localeCompare(b));
17
-
18
- return NextResponse.json({ ok: true, skillsDir: globalSkillsDir, skills });
19
- } catch (e: unknown) {
20
- // If missing, treat as empty.
21
- return NextResponse.json({ ok: true, skillsDir: globalSkillsDir, skills: [], note: errorMessage(e) });
22
- }
23
- }