@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.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +2 -2
- package/.next/server/app/_global-error.html +2 -2
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/channels.html +2 -2
- package/.next/server/app/channels.rsc +1 -1
- package/.next/server/app/channels.segments/_full.segment.rsc +1 -1
- package/.next/server/app/channels.segments/_head.segment.rsc +1 -1
- package/.next/server/app/channels.segments/_index.segment.rsc +1 -1
- package/.next/server/app/channels.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/channels.segments/channels/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/channels.segments/channels.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.html +1 -1
- package/.next/server/app/cron-jobs.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/_full.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/_index.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/cron-jobs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/cron-jobs.segment.rsc +1 -1
- package/.next/server/app/goals/new.html +2 -2
- package/.next/server/app/goals/new.rsc +1 -1
- package/.next/server/app/goals/new.segments/_full.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/_head.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/_index.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/goals/new/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/goals/new.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/goals.segment.rsc +1 -1
- package/.next/server/app/goals.html +1 -1
- package/.next/server/app/goals.rsc +1 -1
- package/.next/server/app/goals.segments/_full.segment.rsc +1 -1
- package/.next/server/app/goals.segments/_head.segment.rsc +1 -1
- package/.next/server/app/goals.segments/_index.segment.rsc +1 -1
- package/.next/server/app/goals.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/goals.segments/goals/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/goals.segments/goals.segment.rsc +1 -1
- package/.next/server/app/settings.html +1 -1
- package/.next/server/app/settings.rsc +1 -1
- package/.next/server/app/settings.segments/_full.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +2 -2
- package/package.json +1 -2
- package/src/app/HomeClient.tsx +0 -207
- package/src/app/agents/[agentId]/agent-editor-tabs.tsx +0 -298
- package/src/app/agents/[agentId]/agent-editor.tsx +0 -468
- package/src/app/agents/[agentId]/page.tsx +0 -32
- package/src/app/api/__tests__/agents-add-route.test.ts +0 -143
- package/src/app/api/__tests__/agents-file-route.test.ts +0 -117
- package/src/app/api/__tests__/agents-files-route.test.ts +0 -61
- package/src/app/api/__tests__/agents-id-route.test.ts +0 -104
- package/src/app/api/__tests__/agents-identity-route.test.ts +0 -92
- package/src/app/api/__tests__/agents-route.test.ts +0 -54
- package/src/app/api/__tests__/agents-skills-install-route.test.ts +0 -131
- package/src/app/api/__tests__/agents-skills-route.test.ts +0 -64
- package/src/app/api/__tests__/agents-update-route.test.ts +0 -95
- package/src/app/api/__tests__/channels-bindings-route.test.ts +0 -143
- package/src/app/api/__tests__/cron-delete-route.test.ts +0 -93
- package/src/app/api/__tests__/cron-job-route.test.ts +0 -78
- package/src/app/api/__tests__/cron-jobs-route.test.ts +0 -116
- package/src/app/api/__tests__/cron-recipe-installed-route.test.ts +0 -114
- package/src/app/api/__tests__/gateway-restart-route.test.ts +0 -36
- package/src/app/api/__tests__/goals-promote-route.test.ts +0 -200
- package/src/app/api/__tests__/goals-route.test.ts +0 -184
- package/src/app/api/__tests__/ids-check-route.test.ts +0 -188
- package/src/app/api/__tests__/marketplace-recipes-route.test.ts +0 -123
- package/src/app/api/__tests__/recipes-clone-route.test.ts +0 -221
- package/src/app/api/__tests__/recipes-delete-route.test.ts +0 -248
- package/src/app/api/__tests__/recipes-id-route.test.ts +0 -166
- package/src/app/api/__tests__/recipes-route.test.ts +0 -57
- package/src/app/api/__tests__/recipes-team-agents-route.test.ts +0 -135
- package/src/app/api/__tests__/scaffold-route.test.ts +0 -173
- package/src/app/api/__tests__/settings-cron-installation-route.test.ts +0 -82
- package/src/app/api/__tests__/skills-available-route.test.ts +0 -47
- package/src/app/api/__tests__/swarms-start-route.test.ts +0 -79
- package/src/app/api/__tests__/swarms-status-route.test.ts +0 -42
- package/src/app/api/__tests__/teams-file-route.test.ts +0 -129
- package/src/app/api/__tests__/teams-files-route.test.ts +0 -57
- package/src/app/api/__tests__/teams-meta-route.test.ts +0 -113
- package/src/app/api/__tests__/teams-orchestrator-install-route.test.ts +0 -66
- package/src/app/api/__tests__/teams-orchestrator-route.test.ts +0 -59
- package/src/app/api/__tests__/teams-remove-team-route.test.ts +0 -122
- package/src/app/api/__tests__/teams-skills-install-route.test.ts +0 -78
- package/src/app/api/__tests__/teams-skills-route.test.ts +0 -73
- package/src/app/api/__tests__/teams-workflow-runs-route.test.ts +0 -85
- package/src/app/api/__tests__/teams-workflows-route.test.ts +0 -110
- package/src/app/api/__tests__/tickets-move-route.test.ts +0 -60
- package/src/app/api/agents/[id]/route.ts +0 -83
- package/src/app/api/agents/add/route.ts +0 -114
- package/src/app/api/agents/file/route.ts +0 -45
- package/src/app/api/agents/files/route.ts +0 -23
- package/src/app/api/agents/identity/route.ts +0 -41
- package/src/app/api/agents/route.ts +0 -22
- package/src/app/api/agents/skills/install/route.ts +0 -34
- package/src/app/api/agents/skills/route.ts +0 -39
- package/src/app/api/agents/update/route.ts +0 -52
- package/src/app/api/channels/bindings/route.ts +0 -63
- package/src/app/api/cron/__tests__/helpers.test.ts +0 -164
- package/src/app/api/cron/delete/route.ts +0 -23
- package/src/app/api/cron/helpers.ts +0 -172
- package/src/app/api/cron/job/route.ts +0 -22
- package/src/app/api/cron/jobs/route.ts +0 -52
- package/src/app/api/cron/recipe-installed/route.ts +0 -65
- package/src/app/api/gateway/restart/route.ts +0 -20
- package/src/app/api/goals/[id]/promote/route.ts +0 -119
- package/src/app/api/goals/[id]/route.ts +0 -54
- package/src/app/api/goals/route.ts +0 -44
- package/src/app/api/ids/check/route.ts +0 -113
- package/src/app/api/marketplace/recipes/[slug]/route.ts +0 -16
- package/src/app/api/marketplace/recipes/route.ts +0 -22
- package/src/app/api/recipes/[id]/route.ts +0 -62
- package/src/app/api/recipes/clone/route.ts +0 -106
- package/src/app/api/recipes/custom-team/route.ts +0 -193
- package/src/app/api/recipes/delete/helpers.ts +0 -65
- package/src/app/api/recipes/delete/route.ts +0 -73
- package/src/app/api/recipes/route.ts +0 -21
- package/src/app/api/recipes/team-agents/__tests__/helpers.test.ts +0 -156
- package/src/app/api/recipes/team-agents/helpers.ts +0 -151
- package/src/app/api/recipes/team-agents/route.ts +0 -80
- package/src/app/api/scaffold/__tests__/helpers.test.ts +0 -186
- package/src/app/api/scaffold/helpers.ts +0 -214
- package/src/app/api/scaffold/route.ts +0 -95
- package/src/app/api/settings/cron-installation/route.ts +0 -58
- package/src/app/api/skills/available/route.ts +0 -23
- package/src/app/api/swarms/start/route.ts +0 -100
- package/src/app/api/swarms/status/route.ts +0 -31
- package/src/app/api/teams/[teamId]/tickets/assign/route.ts +0 -105
- package/src/app/api/teams/[teamId]/tickets/assignees/route.ts +0 -27
- package/src/app/api/teams/[teamId]/tickets/delete/route.ts +0 -55
- package/src/app/api/teams/[teamId]/tickets/move/route.ts +0 -70
- package/src/app/api/teams/[teamId]/tickets/move-to-goals/route.ts +0 -56
- package/src/app/api/teams/file/route.ts +0 -46
- package/src/app/api/teams/files/route.ts +0 -63
- package/src/app/api/teams/memory/route.ts +0 -250
- package/src/app/api/teams/meta/route.ts +0 -43
- package/src/app/api/teams/orchestrator/install/route.ts +0 -129
- package/src/app/api/teams/orchestrator/route.ts +0 -216
- package/src/app/api/teams/remove-team/route.ts +0 -37
- package/src/app/api/teams/skills/install/route.ts +0 -18
- package/src/app/api/teams/skills/route.ts +0 -25
- package/src/app/api/teams/workflow-runs/route.ts +0 -534
- package/src/app/api/teams/workflow-templates/route.ts +0 -71
- package/src/app/api/teams/workflows/route.ts +0 -55
- package/src/app/api/tickets/assign/route.ts +0 -94
- package/src/app/api/tickets/assignees/route.ts +0 -24
- package/src/app/api/tickets/move/route.ts +0 -69
- package/src/app/channels/channels-client.tsx +0 -271
- package/src/app/channels/page.tsx +0 -5
- package/src/app/cron-jobs/cron-jobs-client.tsx +0 -243
- package/src/app/cron-jobs/page.tsx +0 -34
- package/src/app/favicon.ico +0 -0
- package/src/app/global-error.tsx +0 -50
- package/src/app/globals.css +0 -153
- package/src/app/goals/[id]/goal-editor.tsx +0 -162
- package/src/app/goals/[id]/page.tsx +0 -6
- package/src/app/goals/goals-client.tsx +0 -201
- package/src/app/goals/new/page.tsx +0 -81
- package/src/app/goals/page.tsx +0 -10
- package/src/app/layout.tsx +0 -53
- package/src/app/manifest.ts +0 -15
- package/src/app/not-found.tsx +0 -8
- package/src/app/page.tsx +0 -33
- package/src/app/recipes/CreateAgentModal.tsx +0 -156
- package/src/app/recipes/CreateCustomTeamModal.tsx +0 -375
- package/src/app/recipes/CreateModalShell.tsx +0 -55
- package/src/app/recipes/CreateTeamModal.tsx +0 -91
- package/src/app/recipes/[id]/RecipeEditor/RecipeEditorCreateModal.tsx +0 -72
- package/src/app/recipes/[id]/RecipeEditor/RecipeEditorPanel.tsx +0 -216
- package/src/app/recipes/[id]/RecipeEditor/index.tsx +0 -271
- package/src/app/recipes/[id]/RecipeEditor/recipe-editor-utils.ts +0 -46
- package/src/app/recipes/[id]/RecipeEditor/types.ts +0 -52
- package/src/app/recipes/[id]/page.tsx +0 -37
- package/src/app/recipes/page.tsx +0 -101
- package/src/app/recipes/recipes-client.tsx +0 -620
- package/src/app/settings/page.tsx +0 -26
- package/src/app/settings/settings-client.tsx +0 -91
- package/src/app/teams/[teamId]/CloneTeamModal.tsx +0 -116
- package/src/app/teams/[teamId]/OrchestratorPanel.tsx +0 -255
- package/src/app/teams/[teamId]/OrchestratorSetupModal.tsx +0 -184
- package/src/app/teams/[teamId]/PublishChangesModal.tsx +0 -43
- package/src/app/teams/[teamId]/page.tsx +0 -49
- package/src/app/teams/[teamId]/team-editor/TeamAgentsTab.tsx +0 -145
- package/src/app/teams/[teamId]/team-editor/TeamCronTab.tsx +0 -72
- package/src/app/teams/[teamId]/team-editor/TeamFilesTab.tsx +0 -74
- package/src/app/teams/[teamId]/team-editor/TeamMemoryTab.tsx +0 -349
- package/src/app/teams/[teamId]/team-editor/TeamRecipeTab.tsx +0 -151
- package/src/app/teams/[teamId]/team-editor/TeamSkillsTab.tsx +0 -68
- package/src/app/teams/[teamId]/team-editor/index.tsx +0 -558
- package/src/app/teams/[teamId]/team-editor/team-editor-data.ts +0 -255
- package/src/app/teams/[teamId]/team-editor/team-editor-utils.ts +0 -78
- package/src/app/teams/[teamId]/team-editor/types.ts +0 -34
- package/src/app/teams/[teamId]/tickets/[ticket]/page.tsx +0 -35
- package/src/app/teams/[teamId]/tickets/page.tsx +0 -15
- package/src/app/teams/[teamId]/workflows/[workflowId]/WorkflowCanvas.tsx +0 -111
- package/src/app/teams/[teamId]/workflows/[workflowId]/page.tsx +0 -27
- package/src/app/teams/[teamId]/workflows/[workflowId]/workflows-editor-client.tsx +0 -1608
- package/src/app/teams/[teamId]/workflows/page.tsx +0 -40
- package/src/app/teams/[teamId]/workflows/workflows-client.tsx +0 -494
- package/src/app/tickets/TicketDetailClient.tsx +0 -147
- package/src/app/tickets/TicketsBoardClient.tsx +0 -200
- package/src/app/tickets/[ticket]/TicketAssignControl.tsx +0 -112
- package/src/app/tickets/[ticket]/page.tsx +0 -36
- package/src/app/tickets/page.tsx +0 -10
- package/src/components/AppShell.tsx +0 -286
- package/src/components/ConfirmationModal.tsx +0 -81
- package/src/components/DeleteEntityModal.tsx +0 -41
- package/src/components/ErrorBoundary.tsx +0 -70
- package/src/components/FileListWithOptionalToggle.tsx +0 -86
- package/src/components/GoalFormFields.tsx +0 -163
- package/src/components/ScaffoldOverlay.tsx +0 -78
- package/src/components/ThemeToggle.tsx +0 -53
- package/src/components/ToastProvider.tsx +0 -163
- package/src/components/__tests__/ConfirmationModal.test.tsx +0 -109
- package/src/components/__tests__/ErrorBoundary.test.tsx +0 -39
- package/src/components/__tests__/FileListWithOptionalToggle.test.tsx +0 -109
- package/src/components/__tests__/GoalFormFields.test.tsx +0 -117
- package/src/components/delete-modals.tsx +0 -59
- package/src/components/icons.tsx +0 -48
- package/src/lib/__tests__/agent-workspace.test.ts +0 -44
- package/src/lib/__tests__/agents.test.ts +0 -36
- package/src/lib/__tests__/api-route-helpers.test.ts +0 -188
- package/src/lib/__tests__/cron.test.ts +0 -45
- package/src/lib/__tests__/editor-utils.test.ts +0 -38
- package/src/lib/__tests__/errors.test.ts +0 -15
- package/src/lib/__tests__/exec.test.ts +0 -13
- package/src/lib/__tests__/fetch-json.test.ts +0 -118
- package/src/lib/__tests__/gateway.test.ts +0 -234
- package/src/lib/__tests__/goal-promote.test.ts +0 -39
- package/src/lib/__tests__/goals-client.test.ts +0 -26
- package/src/lib/__tests__/goals.test.ts +0 -275
- package/src/lib/__tests__/json.test.ts +0 -15
- package/src/lib/__tests__/kitchen-api.test.ts +0 -32
- package/src/lib/__tests__/marketplace.test.ts +0 -116
- package/src/lib/__tests__/openclaw.test.ts +0 -129
- package/src/lib/__tests__/paths.test.ts +0 -136
- package/src/lib/__tests__/poll.test.ts +0 -26
- package/src/lib/__tests__/recipe-clone.test.ts +0 -85
- package/src/lib/__tests__/recipe-team-agents.test.ts +0 -70
- package/src/lib/__tests__/recipes.test.ts +0 -199
- package/src/lib/__tests__/scaffold-client.test.ts +0 -106
- package/src/lib/__tests__/scaffold.test.ts +0 -64
- package/src/lib/__tests__/slugify.test.ts +0 -23
- package/src/lib/__tests__/tickets.test.ts +0 -158
- package/src/lib/__tests__/type-guards.test.ts +0 -18
- package/src/lib/__tests__/use-slugified-id.test.tsx +0 -120
- package/src/lib/agent-workspace.ts +0 -14
- package/src/lib/agents.ts +0 -17
- package/src/lib/api-route-helpers.ts +0 -157
- package/src/lib/cron.ts +0 -40
- package/src/lib/editor-utils.ts +0 -18
- package/src/lib/errors.ts +0 -7
- package/src/lib/exec.ts +0 -4
- package/src/lib/fetch-json.ts +0 -29
- package/src/lib/gateway.ts +0 -100
- package/src/lib/goal-promote.ts +0 -27
- package/src/lib/goals-client.ts +0 -69
- package/src/lib/goals.ts +0 -171
- package/src/lib/json.ts +0 -10
- package/src/lib/kitchen-api.ts +0 -19
- package/src/lib/marketplace.ts +0 -46
- package/src/lib/openclaw.ts +0 -59
- package/src/lib/paths.ts +0 -69
- package/src/lib/poll.ts +0 -18
- package/src/lib/recipe-clone.ts +0 -42
- package/src/lib/recipe-team-agents.ts +0 -30
- package/src/lib/recipes.ts +0 -95
- package/src/lib/scaffold-client.ts +0 -31
- package/src/lib/scaffold.ts +0 -37
- package/src/lib/slugify.ts +0 -25
- package/src/lib/swarms.ts +0 -25
- package/src/lib/tickets.ts +0 -192
- package/src/lib/type-guards.ts +0 -3
- package/src/lib/use-slugified-id.ts +0 -35
- package/src/lib/workflows/README.md +0 -11
- package/src/lib/workflows/__tests__/storage.test.ts +0 -129
- package/src/lib/workflows/__tests__/validate.test.ts +0 -92
- package/src/lib/workflows/api-handlers.ts +0 -35
- package/src/lib/workflows/readdir.ts +0 -23
- package/src/lib/workflows/runs-storage.ts +0 -59
- package/src/lib/workflows/runs-types.ts +0 -42
- package/src/lib/workflows/storage.ts +0 -70
- package/src/lib/workflows/templates/index.ts +0 -1
- package/src/lib/workflows/templates/marketing-cadence-v1.ts +0 -142
- package/src/lib/workflows/types.ts +0 -48
- package/src/lib/workflows/validate.ts +0 -92
- package/src/proxy.ts +0 -28
- /package/.next/static/{z86RoqzzXXrWnpi229zP6 → Jrrrm9HH5bKkSrQhe1j93}/_buildManifest.js +0 -0
- /package/.next/static/{z86RoqzzXXrWnpi229zP6 → Jrrrm9HH5bKkSrQhe1j93}/_clientMiddlewareManifest.json +0 -0
- /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
|
-
}
|