@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,100 +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 { errorMessage } from "@/lib/errors";
|
|
8
|
-
import { getKitchenApi } from "@/lib/kitchen-api";
|
|
9
|
-
import { normalizeId, resolveAgentWorkspace } from "@/lib/swarms";
|
|
10
|
-
|
|
11
|
-
export async function POST(req: Request) {
|
|
12
|
-
try {
|
|
13
|
-
const body = (await req.json()) as {
|
|
14
|
-
orchestratorAgentId?: string;
|
|
15
|
-
agentId?: string; // compat
|
|
16
|
-
taskId?: string;
|
|
17
|
-
spec?: string;
|
|
18
|
-
specFile?: string;
|
|
19
|
-
baseRef?: string;
|
|
20
|
-
branch?: string;
|
|
21
|
-
tmuxSession?: string;
|
|
22
|
-
agent?: "codex" | "claude";
|
|
23
|
-
model?: string;
|
|
24
|
-
reasoning?: "low" | "medium" | "high";
|
|
25
|
-
timeoutMs?: number;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const orchestratorAgentId = normalizeId("orchestratorAgentId", String(body.orchestratorAgentId ?? body.agentId ?? ""));
|
|
29
|
-
const taskId = normalizeId("taskId", String(body.taskId ?? ""));
|
|
30
|
-
|
|
31
|
-
const spec = typeof body.spec === "string" ? body.spec : "";
|
|
32
|
-
const specFile = typeof body.specFile === "string" ? body.specFile : "";
|
|
33
|
-
if (!!spec === !!specFile) {
|
|
34
|
-
throw new Error("Provide exactly one of spec or specFile");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const orchestratorWs = await resolveAgentWorkspace(orchestratorAgentId);
|
|
38
|
-
const cliPath = path.join(orchestratorWs, ".clawdbot", "task.sh");
|
|
39
|
-
|
|
40
|
-
// Prefer spec-file to avoid shell quoting surprises.
|
|
41
|
-
let effectiveSpecFile = specFile;
|
|
42
|
-
let tmpToCleanup: string | null = null;
|
|
43
|
-
|
|
44
|
-
if (spec) {
|
|
45
|
-
const tmp = path.join(os.tmpdir(), `clawkitchen-swarm-${taskId}-${Date.now()}.md`);
|
|
46
|
-
await fs.writeFile(tmp, spec, "utf8");
|
|
47
|
-
effectiveSpecFile = tmp;
|
|
48
|
-
tmpToCleanup = tmp;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Basic existence checks for actionable errors.
|
|
52
|
-
await fs.access(cliPath);
|
|
53
|
-
|
|
54
|
-
const timeoutMs = Number.isFinite(body.timeoutMs) ? Number(body.timeoutMs) : 120000;
|
|
55
|
-
|
|
56
|
-
const args: string[] = [
|
|
57
|
-
"bash",
|
|
58
|
-
cliPath,
|
|
59
|
-
"start",
|
|
60
|
-
"--task-id",
|
|
61
|
-
taskId,
|
|
62
|
-
"--spec-file",
|
|
63
|
-
effectiveSpecFile,
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
if (typeof body.baseRef === "string" && body.baseRef.trim()) {
|
|
67
|
-
args.push("--base-ref", body.baseRef.trim());
|
|
68
|
-
}
|
|
69
|
-
if (typeof body.branch === "string" && body.branch.trim()) {
|
|
70
|
-
args.push("--branch", body.branch.trim());
|
|
71
|
-
}
|
|
72
|
-
if (typeof body.tmuxSession === "string" && body.tmuxSession.trim()) {
|
|
73
|
-
args.push("--tmux-session", body.tmuxSession.trim());
|
|
74
|
-
}
|
|
75
|
-
if (typeof body.agent === "string" && body.agent.trim()) {
|
|
76
|
-
args.push("--agent", body.agent.trim());
|
|
77
|
-
}
|
|
78
|
-
if (typeof body.model === "string" && body.model.trim()) {
|
|
79
|
-
args.push("--model", body.model.trim());
|
|
80
|
-
}
|
|
81
|
-
if (typeof body.reasoning === "string" && body.reasoning.trim()) {
|
|
82
|
-
args.push("--reasoning", body.reasoning.trim());
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const api = getKitchenApi();
|
|
86
|
-
const res = await api.runtime.system.runCommandWithTimeout(args, { timeoutMs });
|
|
87
|
-
|
|
88
|
-
// best-effort tmp cleanup (fire-and-forget)
|
|
89
|
-
if (tmpToCleanup) {
|
|
90
|
-
fs.unlink(tmpToCleanup).catch(() => {});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return NextResponse.json({ ok: true, orchestratorWorkspace: orchestratorWs, stdout: res.stdout, stderr: res.stderr });
|
|
94
|
-
} catch (err: unknown) {
|
|
95
|
-
const msg = errorMessage(err);
|
|
96
|
-
const isClientError = /required|match \//i.test(msg) || msg.includes("Provide exactly one");
|
|
97
|
-
const status = isClientError ? 400 : 500;
|
|
98
|
-
return NextResponse.json({ ok: false, error: msg }, { status });
|
|
99
|
-
}
|
|
100
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
|
|
4
|
-
import { NextResponse } from "next/server";
|
|
5
|
-
|
|
6
|
-
import { errorMessage } from "@/lib/errors";
|
|
7
|
-
import { getKitchenApi } from "@/lib/kitchen-api";
|
|
8
|
-
import { normalizeId, resolveAgentWorkspace } from "@/lib/swarms";
|
|
9
|
-
|
|
10
|
-
export async function GET(req: Request) {
|
|
11
|
-
try {
|
|
12
|
-
const url = new URL(req.url);
|
|
13
|
-
const orchestratorAgentId = normalizeId(
|
|
14
|
-
"orchestratorAgentId",
|
|
15
|
-
url.searchParams.get("orchestratorAgentId") || url.searchParams.get("agentId") || "",
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
const orchestratorWs = await resolveAgentWorkspace(orchestratorAgentId);
|
|
19
|
-
const cliPath = path.join(orchestratorWs, ".clawdbot", "task.sh");
|
|
20
|
-
await fs.access(cliPath);
|
|
21
|
-
|
|
22
|
-
const api = getKitchenApi();
|
|
23
|
-
const res = await api.runtime.system.runCommandWithTimeout(["bash", cliPath, "status"], { timeoutMs: 30000 });
|
|
24
|
-
|
|
25
|
-
return NextResponse.json({ ok: true, orchestratorWorkspace: orchestratorWs, stdout: res.stdout, stderr: res.stderr });
|
|
26
|
-
} catch (err: unknown) {
|
|
27
|
-
const msg = errorMessage(err);
|
|
28
|
-
const status = /required|match \//i.test(msg) ? 400 : 500;
|
|
29
|
-
return NextResponse.json({ ok: false, error: msg }, { status });
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { NextResponse } from "next/server";
|
|
4
|
-
import { getTicketByIdOrNumber, listTickets, stageDir, type TicketStage } from "@/lib/tickets";
|
|
5
|
-
import { getWorkspaceDir, teamDirFromBaseWorkspace } from "@/lib/paths";
|
|
6
|
-
|
|
7
|
-
export const dynamic = "force-dynamic";
|
|
8
|
-
|
|
9
|
-
type TicketFlowConfig = {
|
|
10
|
-
laneByOwner?: Record<string, TicketStage>;
|
|
11
|
-
defaultLane?: TicketStage;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
async function loadTicketFlowConfig(teamDir: string): Promise<TicketFlowConfig | null> {
|
|
15
|
-
const p = path.join(teamDir, "shared-context", "ticket-flow.json");
|
|
16
|
-
try {
|
|
17
|
-
const raw = await fs.readFile(p, "utf8");
|
|
18
|
-
const parsed = JSON.parse(raw) as TicketFlowConfig;
|
|
19
|
-
// Best-effort validation.
|
|
20
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
21
|
-
return parsed;
|
|
22
|
-
} catch {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function desiredStageForAssignee(opts: {
|
|
28
|
-
teamDir: string;
|
|
29
|
-
assignee: string | null;
|
|
30
|
-
currentStage: TicketStage;
|
|
31
|
-
}): Promise<TicketStage> {
|
|
32
|
-
const config = await loadTicketFlowConfig(opts.teamDir);
|
|
33
|
-
if (!config) return opts.currentStage;
|
|
34
|
-
|
|
35
|
-
const key = (opts.assignee ?? "").trim();
|
|
36
|
-
if (key && config.laneByOwner && typeof config.laneByOwner[key] === "string") {
|
|
37
|
-
return config.laneByOwner[key] as TicketStage;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (config.defaultLane) return config.defaultLane;
|
|
41
|
-
return opts.currentStage;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function upsertField(md: string, field: string, value: string) {
|
|
45
|
-
const re = new RegExp(`^${field}:\\s*.*$`, "mi");
|
|
46
|
-
if (re.test(md)) return md.replace(re, `${field}: ${value}`);
|
|
47
|
-
|
|
48
|
-
// Insert after the first header line, or at top if no header.
|
|
49
|
-
const lines = md.split("\n");
|
|
50
|
-
const headerIdx = lines.findIndex((l) => l.startsWith("# "));
|
|
51
|
-
const insertAt = headerIdx >= 0 ? headerIdx + 1 : 0;
|
|
52
|
-
lines.splice(insertAt, 0, `${field}: ${value}`);
|
|
53
|
-
return lines.join("\n");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function ensureDir(p: string) {
|
|
57
|
-
await fs.mkdir(p, { recursive: true });
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export async function POST(
|
|
61
|
-
req: Request,
|
|
62
|
-
{ params }: { params: Promise<{ teamId: string }> },
|
|
63
|
-
) {
|
|
64
|
-
const { teamId } = await params;
|
|
65
|
-
|
|
66
|
-
const body = (await req.json().catch(() => null)) as null | {
|
|
67
|
-
ticket: string;
|
|
68
|
-
assignee: string;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
if (!body?.ticket || !body?.assignee) {
|
|
72
|
-
return NextResponse.json({ error: "Missing ticket or assignee" }, { status: 400 });
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const baseWorkspace = await getWorkspaceDir();
|
|
76
|
-
const teamDir = teamDirFromBaseWorkspace(baseWorkspace, teamId);
|
|
77
|
-
|
|
78
|
-
const ticket = await getTicketByIdOrNumber(body.ticket, teamDir);
|
|
79
|
-
if (!ticket) {
|
|
80
|
-
return NextResponse.json({ error: "Ticket not found" }, { status: 404 });
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const assignee = body.assignee.trim();
|
|
84
|
-
const nextStage = await desiredStageForAssignee({ teamDir, assignee, currentStage: ticket.stage });
|
|
85
|
-
|
|
86
|
-
const currentMd = await fs.readFile(ticket.file, "utf8");
|
|
87
|
-
let updatedMd = upsertField(currentMd, "Owner", assignee);
|
|
88
|
-
updatedMd = upsertField(updatedMd, "Status", nextStage);
|
|
89
|
-
|
|
90
|
-
const filename = path.basename(ticket.file);
|
|
91
|
-
const nextPath = path.join(stageDir(nextStage, teamDir), filename);
|
|
92
|
-
|
|
93
|
-
await ensureDir(stageDir(nextStage, teamDir));
|
|
94
|
-
|
|
95
|
-
if (ticket.file !== nextPath) {
|
|
96
|
-
await fs.rename(ticket.file, nextPath);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
await fs.writeFile(nextPath, updatedMd, "utf8");
|
|
100
|
-
|
|
101
|
-
// Assignment stubs are deprecated; do not create/update work/assignments/*.md.
|
|
102
|
-
|
|
103
|
-
const refreshed = (await listTickets(teamDir)).find((t) => t.number === ticket.number);
|
|
104
|
-
return NextResponse.json({ ok: true, ticket: refreshed ?? null });
|
|
105
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { NextResponse } from "next/server";
|
|
4
|
-
import { getWorkspaceDir, teamDirFromBaseWorkspace } from "@/lib/paths";
|
|
5
|
-
|
|
6
|
-
export const dynamic = "force-dynamic";
|
|
7
|
-
|
|
8
|
-
export async function GET(
|
|
9
|
-
_req: Request,
|
|
10
|
-
{ params }: { params: Promise<{ teamId: string }> },
|
|
11
|
-
) {
|
|
12
|
-
const { teamId } = await params;
|
|
13
|
-
|
|
14
|
-
const baseWorkspace = await getWorkspaceDir();
|
|
15
|
-
const teamDir = teamDirFromBaseWorkspace(baseWorkspace, teamId);
|
|
16
|
-
const rolesDir = path.join(teamDir, "roles");
|
|
17
|
-
|
|
18
|
-
let entries: string[] = [];
|
|
19
|
-
try {
|
|
20
|
-
entries = await fs.readdir(rolesDir);
|
|
21
|
-
} catch {
|
|
22
|
-
entries = [];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const assignees = entries.filter((e) => !e.startsWith(".")).sort();
|
|
26
|
-
return NextResponse.json({ assignees });
|
|
27
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
|
|
4
|
-
import { NextResponse } from "next/server";
|
|
5
|
-
|
|
6
|
-
import { errorMessage } from "@/lib/errors";
|
|
7
|
-
import { resolveTicket, teamWorkspace } from "@/lib/tickets";
|
|
8
|
-
|
|
9
|
-
function pad4(n: number) {
|
|
10
|
-
return String(n).padStart(4, "0");
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export async function POST(req: Request, ctx: { params: Promise<{ teamId: string }> }) {
|
|
14
|
-
try {
|
|
15
|
-
const { teamId } = await ctx.params;
|
|
16
|
-
const body = await req.json();
|
|
17
|
-
const ticket = String(body.ticket ?? "").trim();
|
|
18
|
-
|
|
19
|
-
if (!ticket) {
|
|
20
|
-
return NextResponse.json({ ok: false, error: "Missing ticket" }, { status: 400 });
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const hit = await resolveTicket(teamId, ticket);
|
|
24
|
-
if (!hit) {
|
|
25
|
-
return NextResponse.json({ ok: false, error: `Ticket not found: ${ticket}` }, { status: 404 });
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Remove ticket file.
|
|
29
|
-
await fs.unlink(hit.file);
|
|
30
|
-
|
|
31
|
-
// Archive any assignment stubs (safer than deleting outright).
|
|
32
|
-
const assignmentsDir = path.join(teamWorkspace(teamId), "work/assignments");
|
|
33
|
-
const deletedDir = path.join(assignmentsDir, "deleted");
|
|
34
|
-
await fs.mkdir(deletedDir, { recursive: true });
|
|
35
|
-
|
|
36
|
-
const moved: string[] = [];
|
|
37
|
-
try {
|
|
38
|
-
const files = await fs.readdir(assignmentsDir);
|
|
39
|
-
const prefix = `${pad4(hit.number)}-assigned-`;
|
|
40
|
-
for (const f of files) {
|
|
41
|
-
if (!f.startsWith(prefix) || !f.endsWith(".md")) continue;
|
|
42
|
-
const from = path.join(assignmentsDir, f);
|
|
43
|
-
const to = path.join(deletedDir, f);
|
|
44
|
-
await fs.rename(from, to);
|
|
45
|
-
moved.push(f);
|
|
46
|
-
}
|
|
47
|
-
} catch {
|
|
48
|
-
// ignore
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return NextResponse.json({ ok: true, archivedAssignments: moved });
|
|
52
|
-
} catch (e: unknown) {
|
|
53
|
-
return NextResponse.json({ ok: false, error: errorMessage(e) }, { status: 500 });
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
|
|
3
|
-
import { NextResponse } from "next/server";
|
|
4
|
-
|
|
5
|
-
import { runOpenClaw } from "@/lib/openclaw";
|
|
6
|
-
import { getTicketMarkdown } from "@/lib/tickets";
|
|
7
|
-
|
|
8
|
-
function todayUtc() {
|
|
9
|
-
return new Date().toISOString().slice(0, 10);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async function appendDoneAuditComment(teamId: string, ticketIdOrNumber: string) {
|
|
13
|
-
const hit = await getTicketMarkdown(teamId, ticketIdOrNumber);
|
|
14
|
-
if (!hit) return;
|
|
15
|
-
|
|
16
|
-
const line = `- ${todayUtc()} (ClawKitchen UI): Marked done from ClawKitchen UI.`;
|
|
17
|
-
if (hit.markdown.includes(line)) return;
|
|
18
|
-
|
|
19
|
-
const hasComments = /\n## Comments\n/i.test(hit.markdown) || /^## Comments\n/im.test(hit.markdown);
|
|
20
|
-
|
|
21
|
-
let next = hit.markdown;
|
|
22
|
-
if (!hasComments) {
|
|
23
|
-
next = next.replace(/\s*$/, "\n\n## Comments\n");
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Append at end; this keeps the operation simple and avoids brittle section parsing.
|
|
27
|
-
next = next.replace(/\s*$/, "\n" + line + "\n");
|
|
28
|
-
|
|
29
|
-
await fs.writeFile(hit.file, next, "utf8");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function POST(req: Request, ctx: { params: Promise<{ teamId: string }> }) {
|
|
33
|
-
try {
|
|
34
|
-
const { teamId } = await ctx.params;
|
|
35
|
-
const body = await req.json();
|
|
36
|
-
const ticket = String(body.ticket ?? "").trim();
|
|
37
|
-
const to = String(body.to ?? "").trim();
|
|
38
|
-
|
|
39
|
-
if (!ticket) {
|
|
40
|
-
return NextResponse.json({ ok: false, error: "Missing ticket" }, { status: 400 });
|
|
41
|
-
}
|
|
42
|
-
if (!to || !["backlog", "in-progress", "testing", "done"].includes(to)) {
|
|
43
|
-
return NextResponse.json({ ok: false, error: "Invalid destination stage" }, { status: 400 });
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const args = [
|
|
47
|
-
"recipes",
|
|
48
|
-
"move-ticket",
|
|
49
|
-
"--team-id",
|
|
50
|
-
teamId,
|
|
51
|
-
"--ticket",
|
|
52
|
-
ticket,
|
|
53
|
-
"--to",
|
|
54
|
-
to,
|
|
55
|
-
"--yes",
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
const res = await runOpenClaw(args);
|
|
59
|
-
if (!res.ok) throw new Error(res.stderr || `openclaw exit ${res.exitCode}`);
|
|
60
|
-
|
|
61
|
-
if (to === "done") {
|
|
62
|
-
await appendDoneAuditComment(teamId, ticket);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return NextResponse.json({ ok: true });
|
|
66
|
-
} catch (err: unknown) {
|
|
67
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
68
|
-
return NextResponse.json({ ok: false, error: message }, { status: 500 });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
|
|
4
|
-
import { NextResponse } from "next/server";
|
|
5
|
-
|
|
6
|
-
import { errorMessage } from "@/lib/errors";
|
|
7
|
-
import { resolveTicket, teamWorkspace } from "@/lib/tickets";
|
|
8
|
-
|
|
9
|
-
function todayUtc() {
|
|
10
|
-
return new Date().toISOString().slice(0, 10);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async function appendGoalsAuditComment(file: string) {
|
|
14
|
-
const md = await fs.readFile(file, "utf8");
|
|
15
|
-
const line = `- ${todayUtc()} (ClawKitchen UI): Moved to Goals from ClawKitchen UI.`;
|
|
16
|
-
if (md.includes(line)) return;
|
|
17
|
-
|
|
18
|
-
const hasComments = /\n## Comments\n/i.test(md) || /^## Comments\n/im.test(md);
|
|
19
|
-
|
|
20
|
-
let next = md;
|
|
21
|
-
if (!hasComments) {
|
|
22
|
-
next = next.replace(/\s*$/, "\n\n## Comments\n");
|
|
23
|
-
}
|
|
24
|
-
next = next.replace(/\s*$/, "\n" + line + "\n");
|
|
25
|
-
|
|
26
|
-
await fs.writeFile(file, next, "utf8");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function POST(req: Request, ctx: { params: Promise<{ teamId: string }> }) {
|
|
30
|
-
try {
|
|
31
|
-
const { teamId } = await ctx.params;
|
|
32
|
-
const body = await req.json();
|
|
33
|
-
const ticket = String(body.ticket ?? "").trim();
|
|
34
|
-
|
|
35
|
-
if (!ticket) {
|
|
36
|
-
return NextResponse.json({ ok: false, error: "Missing ticket" }, { status: 400 });
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const hit = await resolveTicket(teamId, ticket);
|
|
40
|
-
if (!hit) {
|
|
41
|
-
return NextResponse.json({ ok: false, error: `Ticket not found: ${ticket}` }, { status: 404 });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const goalsDir = path.join(teamWorkspace(teamId), "work/goals");
|
|
45
|
-
await fs.mkdir(goalsDir, { recursive: true });
|
|
46
|
-
|
|
47
|
-
const dest = path.join(goalsDir, path.basename(hit.file));
|
|
48
|
-
await fs.rename(hit.file, dest);
|
|
49
|
-
|
|
50
|
-
await appendGoalsAuditComment(dest);
|
|
51
|
-
|
|
52
|
-
return NextResponse.json({ ok: true, to: dest });
|
|
53
|
-
} catch (e: unknown) {
|
|
54
|
-
return NextResponse.json({ ok: false, error: errorMessage(e) }, { status: 500 });
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,46 +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 { getTeamContextFromBody, withTeamContextFromQuery } from "@/lib/api-route-helpers";
|
|
7
|
-
|
|
8
|
-
export async function GET(req: Request) {
|
|
9
|
-
return withTeamContextFromQuery(req, async ({ teamId, teamDir }) => {
|
|
10
|
-
const { searchParams } = new URL(req.url);
|
|
11
|
-
const name = String(searchParams.get("name") ?? "").trim();
|
|
12
|
-
if (!name) return NextResponse.json({ ok: false, error: "name is required" }, { status: 400 });
|
|
13
|
-
|
|
14
|
-
const safe = assertSafeRelativeFileName(name);
|
|
15
|
-
const filePath = path.join(teamDir, safe);
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
const content = await fs.readFile(filePath, "utf8");
|
|
19
|
-
return NextResponse.json({ ok: true, teamId, name: safe, filePath, content });
|
|
20
|
-
} catch (e: unknown) {
|
|
21
|
-
return NextResponse.json(
|
|
22
|
-
{ ok: false, error: errorMessage(e) },
|
|
23
|
-
{ status: 404 }
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function PUT(req: Request) {
|
|
30
|
-
const body = (await req.json()) as { teamId?: string; name?: string; content?: string };
|
|
31
|
-
const ctx = await getTeamContextFromBody(body);
|
|
32
|
-
if (ctx instanceof NextResponse) return ctx;
|
|
33
|
-
const { teamId, teamDir } = ctx;
|
|
34
|
-
|
|
35
|
-
const name = String(body.name ?? "").trim();
|
|
36
|
-
const content = typeof body.content === "string" ? body.content : null;
|
|
37
|
-
if (!name) return NextResponse.json({ ok: false, error: "name is required" }, { status: 400 });
|
|
38
|
-
if (content === null) return NextResponse.json({ ok: false, error: "content is required" }, { status: 400 });
|
|
39
|
-
|
|
40
|
-
const safe = assertSafeRelativeFileName(name);
|
|
41
|
-
const filePath = path.join(teamDir, safe);
|
|
42
|
-
|
|
43
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
44
|
-
await fs.writeFile(filePath, content, "utf8");
|
|
45
|
-
return NextResponse.json({ ok: true, teamId, name: safe, filePath });
|
|
46
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { NextResponse } from "next/server";
|
|
4
|
-
import { listWorkspaceFiles, withTeamContextFromQuery } from "@/lib/api-route-helpers";
|
|
5
|
-
|
|
6
|
-
export async function GET(req: Request) {
|
|
7
|
-
return withTeamContextFromQuery(req, async ({ teamId, teamDir }) => {
|
|
8
|
-
// QA checklist should only be required when:
|
|
9
|
-
// - the team has a test role, and/or
|
|
10
|
-
// - the recipe opts in via frontmatter (qaChecklist: true)
|
|
11
|
-
const hasTestRole = await (async () => {
|
|
12
|
-
try {
|
|
13
|
-
const st = await fs.stat(path.join(teamDir, "roles", "test"));
|
|
14
|
-
return st.isDirectory();
|
|
15
|
-
} catch {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
})();
|
|
19
|
-
|
|
20
|
-
const recipeOptsIn = await (async () => {
|
|
21
|
-
try {
|
|
22
|
-
const teamJsonPath = path.join(teamDir, "team.json");
|
|
23
|
-
const raw = await fs.readFile(teamJsonPath, "utf8");
|
|
24
|
-
const parsed = JSON.parse(raw) as { recipeId?: unknown };
|
|
25
|
-
const recipeId = String(parsed.recipeId ?? "").trim();
|
|
26
|
-
if (!recipeId) return false;
|
|
27
|
-
|
|
28
|
-
// Load recipe frontmatter via OpenClaw CLI and check for qaChecklist: true
|
|
29
|
-
const { runOpenClaw } = await import("@/lib/openclaw");
|
|
30
|
-
const shown = await runOpenClaw(["recipes", "show", recipeId]);
|
|
31
|
-
if (!shown.ok) return false;
|
|
32
|
-
const md = String(shown.stdout ?? "");
|
|
33
|
-
if (!md.startsWith("---\n")) return false;
|
|
34
|
-
const end = md.indexOf("\n---\n", 4);
|
|
35
|
-
if (end === -1) return false;
|
|
36
|
-
const yamlText = md.slice(4, end);
|
|
37
|
-
const { default: YAML } = await import("yaml");
|
|
38
|
-
const fm = (YAML.parse(yamlText) ?? {}) as { qaChecklist?: unknown };
|
|
39
|
-
return Boolean(fm.qaChecklist);
|
|
40
|
-
} catch {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
})();
|
|
44
|
-
|
|
45
|
-
const qaChecklistRequired = hasTestRole || recipeOptsIn;
|
|
46
|
-
|
|
47
|
-
const candidates: Array<{ name: string; required: boolean; rationale: string }> = [
|
|
48
|
-
{ name: "TEAM.md", required: true, rationale: "Team workspace overview" },
|
|
49
|
-
{ name: "TICKETS.md", required: true, rationale: "Ticket workflow + format" },
|
|
50
|
-
{ name: "notes/QA_CHECKLIST.md", required: qaChecklistRequired, rationale: "QA verification checklist" },
|
|
51
|
-
|
|
52
|
-
{ name: "SOUL.md", required: false, rationale: "Optional team persona (some teams use role SOUL.md only)" },
|
|
53
|
-
{ name: "USER.md", required: false, rationale: "Optional user profile" },
|
|
54
|
-
{ name: "AGENTS.md", required: false, rationale: "Optional team notes (often role-scoped)" },
|
|
55
|
-
{ name: "TOOLS.md", required: false, rationale: "Optional team-local tooling notes" },
|
|
56
|
-
{ name: "MEMORY.md", required: false, rationale: "Optional curated memory" },
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
const files = await listWorkspaceFiles(teamDir, candidates);
|
|
60
|
-
|
|
61
|
-
return NextResponse.json({ ok: true, teamId, teamDir, files });
|
|
62
|
-
});
|
|
63
|
-
}
|