@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,250 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { NextResponse } from "next/server";
|
|
4
|
-
import { withTeamContextFromQuery } from "@/lib/api-route-helpers";
|
|
5
|
-
import { errorMessage } from "@/lib/errors";
|
|
6
|
-
|
|
7
|
-
type MemoryItem = {
|
|
8
|
-
ts: string;
|
|
9
|
-
author: string;
|
|
10
|
-
type: string;
|
|
11
|
-
content: string;
|
|
12
|
-
source?: unknown;
|
|
13
|
-
_file?: string;
|
|
14
|
-
_line?: number;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
type MemoryPointer = {
|
|
18
|
-
file: "team.jsonl";
|
|
19
|
-
line: number;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
type PinnedOp =
|
|
23
|
-
| {
|
|
24
|
-
op: "pin";
|
|
25
|
-
ts: string;
|
|
26
|
-
actor: string;
|
|
27
|
-
key: MemoryPointer;
|
|
28
|
-
item: Omit<MemoryItem, "_file" | "_line">;
|
|
29
|
-
}
|
|
30
|
-
| {
|
|
31
|
-
op: "unpin";
|
|
32
|
-
ts: string;
|
|
33
|
-
actor: string;
|
|
34
|
-
key: MemoryPointer;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const TEAM_FILE = "team.jsonl" as const;
|
|
38
|
-
const PINNED_FILE = "pinned.jsonl" as const;
|
|
39
|
-
|
|
40
|
-
function safeParseJsonLine(line: string): unknown {
|
|
41
|
-
const t = line.trim();
|
|
42
|
-
if (!t) return null;
|
|
43
|
-
try {
|
|
44
|
-
return JSON.parse(t);
|
|
45
|
-
} catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function asMemoryItem(x: unknown): Omit<MemoryItem, "_file" | "_line"> | null {
|
|
51
|
-
if (!x || typeof x !== "object" || Array.isArray(x)) return null;
|
|
52
|
-
const o = x as Record<string, unknown>;
|
|
53
|
-
const ts = String(o.ts ?? "").trim();
|
|
54
|
-
const author = String(o.author ?? "").trim();
|
|
55
|
-
const type = String(o.type ?? "").trim();
|
|
56
|
-
const content = String(o.content ?? "").trim();
|
|
57
|
-
if (!ts || !author || !type || !content) return null;
|
|
58
|
-
const source = o.source;
|
|
59
|
-
return { ts, author, type, content, source };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function asPointer(x: unknown): MemoryPointer | null {
|
|
63
|
-
if (!x || typeof x !== "object" || Array.isArray(x)) return null;
|
|
64
|
-
const o = x as Record<string, unknown>;
|
|
65
|
-
const file = String(o.file ?? "").trim();
|
|
66
|
-
const line = Number(o.line);
|
|
67
|
-
if (file !== TEAM_FILE) return null;
|
|
68
|
-
if (!Number.isFinite(line) || line <= 0) return null;
|
|
69
|
-
return { file: TEAM_FILE, line: Math.floor(line) };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function asPinnedOp(x: unknown): PinnedOp | null {
|
|
73
|
-
if (!x || typeof x !== "object" || Array.isArray(x)) return null;
|
|
74
|
-
const o = x as Record<string, unknown>;
|
|
75
|
-
const op = String(o.op ?? "").trim();
|
|
76
|
-
const ts = String(o.ts ?? "").trim();
|
|
77
|
-
const actor = String(o.actor ?? "").trim();
|
|
78
|
-
const key = asPointer(o.key);
|
|
79
|
-
|
|
80
|
-
if (!op || !ts || !actor || !key) return null;
|
|
81
|
-
|
|
82
|
-
if (op === "pin") {
|
|
83
|
-
const item = asMemoryItem(o.item);
|
|
84
|
-
if (!item) return null;
|
|
85
|
-
return { op: "pin", ts, actor, key, item };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (op === "unpin") {
|
|
89
|
-
return { op: "unpin", ts, actor, key };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
async function readJsonlFile(full: string): Promise<string[]> {
|
|
96
|
-
try {
|
|
97
|
-
const text = await fs.readFile(full, "utf8");
|
|
98
|
-
return text.split(/\r?\n/);
|
|
99
|
-
} catch {
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export async function GET(req: Request) {
|
|
105
|
-
return withTeamContextFromQuery(req, async ({ teamId, teamDir }) => {
|
|
106
|
-
try {
|
|
107
|
-
const memoryDir = path.join(teamDir, "shared-context", "memory");
|
|
108
|
-
const teamFull = path.join(memoryDir, TEAM_FILE);
|
|
109
|
-
const pinnedFull = path.join(memoryDir, PINNED_FILE);
|
|
110
|
-
|
|
111
|
-
// Load team items.
|
|
112
|
-
const teamLines = await readJsonlFile(teamFull);
|
|
113
|
-
const teamItems: MemoryItem[] = [];
|
|
114
|
-
for (let i = 0; i < teamLines.length; i++) {
|
|
115
|
-
const raw = safeParseJsonLine(teamLines[i] ?? "");
|
|
116
|
-
const item = asMemoryItem(raw);
|
|
117
|
-
if (!item) continue;
|
|
118
|
-
teamItems.push({ ...item, _file: TEAM_FILE, _line: i + 1 });
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Load pinned operations and compute active pinned set.
|
|
122
|
-
const pinnedLines = await readJsonlFile(pinnedFull);
|
|
123
|
-
const pinnedByKey = new Map<string, { key: MemoryPointer; item: Omit<MemoryItem, "_file" | "_line">; pinnedAt: string; pinnedBy: string }>();
|
|
124
|
-
|
|
125
|
-
for (const line of pinnedLines) {
|
|
126
|
-
const raw = safeParseJsonLine(line);
|
|
127
|
-
const op = asPinnedOp(raw);
|
|
128
|
-
if (!op) continue;
|
|
129
|
-
const k = `${op.key.file}:${op.key.line}`;
|
|
130
|
-
if (op.op === "pin") {
|
|
131
|
-
pinnedByKey.set(k, { key: op.key, item: op.item, pinnedAt: op.ts, pinnedBy: op.actor });
|
|
132
|
-
} else {
|
|
133
|
-
pinnedByKey.delete(k);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const pinnedItems: (MemoryItem & { pinnedAt: string; pinnedBy: string; _key: string })[] = Array.from(pinnedByKey.values()).map(
|
|
138
|
-
({ key, item, pinnedAt, pinnedBy }) => ({
|
|
139
|
-
...item,
|
|
140
|
-
_file: key.file,
|
|
141
|
-
_line: key.line,
|
|
142
|
-
pinnedAt,
|
|
143
|
-
pinnedBy,
|
|
144
|
-
_key: `${key.file}:${key.line}`,
|
|
145
|
-
})
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Sort pinned by pinnedAt desc (then item.ts desc).
|
|
149
|
-
pinnedItems.sort((a, b) => String(b.pinnedAt).localeCompare(String(a.pinnedAt)) || String(b.ts).localeCompare(String(a.ts)));
|
|
150
|
-
|
|
151
|
-
// Recent: exclude pinned keys and show most recent first.
|
|
152
|
-
const pinnedKeys = new Set(pinnedItems.map((x) => x._key));
|
|
153
|
-
const recentItems = teamItems
|
|
154
|
-
.filter((x) => !pinnedKeys.has(`${x._file ?? ""}:${x._line ?? 0}`))
|
|
155
|
-
.sort((a, b) => String(b.ts).localeCompare(String(a.ts)))
|
|
156
|
-
.slice(0, 200);
|
|
157
|
-
|
|
158
|
-
return NextResponse.json({
|
|
159
|
-
ok: true,
|
|
160
|
-
teamId,
|
|
161
|
-
memoryDir,
|
|
162
|
-
files: [TEAM_FILE, PINNED_FILE],
|
|
163
|
-
pinnedItems,
|
|
164
|
-
items: recentItems,
|
|
165
|
-
});
|
|
166
|
-
} catch (e: unknown) {
|
|
167
|
-
return NextResponse.json({ ok: false, error: errorMessage(e) }, { status: 500 });
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export async function POST(req: Request) {
|
|
173
|
-
return withTeamContextFromQuery(req, async ({ teamId, teamDir }) => {
|
|
174
|
-
let body: unknown;
|
|
175
|
-
try {
|
|
176
|
-
body = await req.json();
|
|
177
|
-
} catch {
|
|
178
|
-
return NextResponse.json({ ok: false, error: "Invalid JSON" }, { status: 400 });
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const o = body && typeof body === "object" ? (body as Record<string, unknown>) : {};
|
|
182
|
-
|
|
183
|
-
const op = String(o.op ?? "append").trim() || "append";
|
|
184
|
-
|
|
185
|
-
// Shared validation.
|
|
186
|
-
const actor = String(o.actor ?? "").trim() || `${teamId}-lead`;
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
const memoryDir = path.join(teamDir, "shared-context", "memory");
|
|
190
|
-
await fs.mkdir(memoryDir, { recursive: true });
|
|
191
|
-
|
|
192
|
-
// Append to team.jsonl (existing behavior) — explicitly allowlisted.
|
|
193
|
-
if (op === "append") {
|
|
194
|
-
const ts = String(o.ts ?? "").trim();
|
|
195
|
-
const author = String(o.author ?? "").trim();
|
|
196
|
-
const type = String(o.type ?? "").trim();
|
|
197
|
-
const content = String(o.content ?? "").trim();
|
|
198
|
-
const source = o.source;
|
|
199
|
-
|
|
200
|
-
if (!ts || !author || !type || !content) {
|
|
201
|
-
return NextResponse.json(
|
|
202
|
-
{ ok: false, error: "ts, author, type, and content are required" },
|
|
203
|
-
{ status: 400 }
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Safety: only allow appends to team.jsonl.
|
|
208
|
-
const file = String(o.file ?? TEAM_FILE).trim() || TEAM_FILE;
|
|
209
|
-
if (file !== TEAM_FILE) {
|
|
210
|
-
return NextResponse.json({ ok: false, error: "Invalid file" }, { status: 400 });
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const full = path.join(memoryDir, TEAM_FILE);
|
|
214
|
-
const item: MemoryItem = { ts, author, type, content, ...(source !== undefined ? { source } : {}) };
|
|
215
|
-
await fs.appendFile(full, JSON.stringify(item) + "\n", "utf8");
|
|
216
|
-
return NextResponse.json({ ok: true, teamId, file: TEAM_FILE, item });
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Pin/unpin operations: append-only ops into pinned.jsonl.
|
|
220
|
-
if (op === "pin" || op === "unpin") {
|
|
221
|
-
const ts = String(o.ts ?? new Date().toISOString()).trim();
|
|
222
|
-
const key = asPointer(o.key);
|
|
223
|
-
if (!key) {
|
|
224
|
-
return NextResponse.json({ ok: false, error: "Invalid key" }, { status: 400 });
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (op === "pin") {
|
|
228
|
-
const item = asMemoryItem(o.item);
|
|
229
|
-
if (!item) {
|
|
230
|
-
return NextResponse.json({ ok: false, error: "Invalid item" }, { status: 400 });
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const record: PinnedOp = { op: "pin", ts, actor, key, item };
|
|
234
|
-
const full = path.join(memoryDir, PINNED_FILE);
|
|
235
|
-
await fs.appendFile(full, JSON.stringify(record) + "\n", "utf8");
|
|
236
|
-
return NextResponse.json({ ok: true, teamId, op: "pin", key });
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const record: PinnedOp = { op: "unpin", ts, actor, key };
|
|
240
|
-
const full = path.join(memoryDir, PINNED_FILE);
|
|
241
|
-
await fs.appendFile(full, JSON.stringify(record) + "\n", "utf8");
|
|
242
|
-
return NextResponse.json({ ok: true, teamId, op: "unpin", key });
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return NextResponse.json({ ok: false, error: `Unknown op: ${op}` }, { status: 400 });
|
|
246
|
-
} catch (e: unknown) {
|
|
247
|
-
return NextResponse.json({ ok: false, error: errorMessage(e) }, { status: 500 });
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { NextResponse } from "next/server";
|
|
4
|
-
import { getTeamContextFromBody, withTeamContextFromQuery } from "@/lib/api-route-helpers";
|
|
5
|
-
|
|
6
|
-
export async function GET(req: Request) {
|
|
7
|
-
return withTeamContextFromQuery(req, async ({ teamId, teamDir }) => {
|
|
8
|
-
const metaPath = path.join(teamDir, "team.json");
|
|
9
|
-
|
|
10
|
-
try {
|
|
11
|
-
const raw = await fs.readFile(metaPath, "utf8");
|
|
12
|
-
const meta = JSON.parse(raw) as Record<string, unknown>;
|
|
13
|
-
return NextResponse.json({ ok: true, teamId, teamDir, metaPath, meta });
|
|
14
|
-
} catch {
|
|
15
|
-
return NextResponse.json({ ok: true, teamId, teamDir, metaPath, meta: null, missing: true });
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function POST(req: Request) {
|
|
21
|
-
const body = (await req.json()) as { teamId?: string; recipeId?: string; recipeName?: string };
|
|
22
|
-
const ctx = await getTeamContextFromBody(body);
|
|
23
|
-
if (ctx instanceof NextResponse) return ctx;
|
|
24
|
-
const { teamId, teamDir } = ctx;
|
|
25
|
-
|
|
26
|
-
const recipeId = String(body.recipeId ?? "").trim();
|
|
27
|
-
const recipeName = typeof body.recipeName === "string" ? body.recipeName : "";
|
|
28
|
-
if (!recipeId) return NextResponse.json({ ok: false, error: "recipeId is required" }, { status: 400 });
|
|
29
|
-
|
|
30
|
-
const metaPath = path.join(teamDir, "team.json");
|
|
31
|
-
|
|
32
|
-
const meta = {
|
|
33
|
-
teamId,
|
|
34
|
-
recipeId,
|
|
35
|
-
recipeName,
|
|
36
|
-
attachedAt: new Date().toISOString(),
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
await fs.mkdir(teamDir, { recursive: true });
|
|
40
|
-
await fs.writeFile(metaPath, JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
41
|
-
|
|
42
|
-
return NextResponse.json({ ok: true, teamId, teamDir, metaPath, meta });
|
|
43
|
-
}
|
|
@@ -1,129 +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 { runOpenClaw } from "@/lib/openclaw";
|
|
8
|
-
import { normalizeId, resolveAgentWorkspace } from "@/lib/swarms";
|
|
9
|
-
|
|
10
|
-
function escapeShValue(v: string) {
|
|
11
|
-
// Keep it simple: wrap in double-quotes and escape backslash + double-quote + dollar.
|
|
12
|
-
return `"${v.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"").replace(/\$/g, "\\$")}"`;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function upsertExportLine(content: string, key: string, value: string) {
|
|
16
|
-
const lines = content.split("\n");
|
|
17
|
-
const assignment = `export ${key}=${escapeShValue(value)}`;
|
|
18
|
-
|
|
19
|
-
const re = new RegExp(`^\\s*(export\\s+)?${key}\\s*=`, "i");
|
|
20
|
-
let replaced = false;
|
|
21
|
-
const out = lines.map((line) => {
|
|
22
|
-
if (re.test(line)) {
|
|
23
|
-
replaced = true;
|
|
24
|
-
return assignment;
|
|
25
|
-
}
|
|
26
|
-
return line;
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
if (!replaced) {
|
|
30
|
-
// Add near the top, after any shebang / initial comments.
|
|
31
|
-
let insertAt = 0;
|
|
32
|
-
while (insertAt < out.length) {
|
|
33
|
-
const l = out[insertAt];
|
|
34
|
-
if (insertAt === 0 && l.startsWith("#!")) {
|
|
35
|
-
insertAt++;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (l.trim().startsWith("#") || l.trim() === "") {
|
|
39
|
-
insertAt++;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
out.splice(insertAt, 0, assignment);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return out.join("\n");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export async function POST(req: Request) {
|
|
51
|
-
try {
|
|
52
|
-
const body = (await req.json()) as {
|
|
53
|
-
teamId?: string;
|
|
54
|
-
orchestratorAgentId?: string;
|
|
55
|
-
repoDir?: string;
|
|
56
|
-
worktreeRoot?: string;
|
|
57
|
-
baseRef?: string;
|
|
58
|
-
applyConfig?: boolean;
|
|
59
|
-
makeExecutable?: boolean;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const teamId = normalizeId("teamId", String(body.teamId ?? ""));
|
|
63
|
-
const orchestratorAgentId = normalizeId("orchestratorAgentId", String(body.orchestratorAgentId ?? ""));
|
|
64
|
-
const repoDir = String(body.repoDir ?? "").trim();
|
|
65
|
-
const worktreeRoot = String(body.worktreeRoot ?? "").trim();
|
|
66
|
-
const baseRef = String(body.baseRef ?? "").trim();
|
|
67
|
-
|
|
68
|
-
if (!repoDir) throw new Error("repoDir is required");
|
|
69
|
-
if (!path.isAbsolute(repoDir)) throw new Error("repoDir must be an absolute path");
|
|
70
|
-
if (worktreeRoot && !path.isAbsolute(worktreeRoot)) throw new Error("worktreeRoot must be an absolute path");
|
|
71
|
-
|
|
72
|
-
// 1) Scaffold orchestrator agent.
|
|
73
|
-
const scaffoldArgs: string[] = [
|
|
74
|
-
"recipes",
|
|
75
|
-
"scaffold",
|
|
76
|
-
"swarm-orchestrator",
|
|
77
|
-
"--agent-id",
|
|
78
|
-
orchestratorAgentId,
|
|
79
|
-
"--name",
|
|
80
|
-
`${teamId} Swarm Orchestrator`,
|
|
81
|
-
];
|
|
82
|
-
if (body.applyConfig !== false) scaffoldArgs.push("--apply-config");
|
|
83
|
-
|
|
84
|
-
const scaffoldRes = await runOpenClaw(scaffoldArgs);
|
|
85
|
-
if (!scaffoldRes.ok) {
|
|
86
|
-
throw new Error(scaffoldRes.stderr || scaffoldRes.stdout || "Failed to scaffold orchestrator");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const workspace = await resolveAgentWorkspace(orchestratorAgentId);
|
|
90
|
-
|
|
91
|
-
// 2) Write env config.
|
|
92
|
-
const envPath = path.join(workspace, ".clawdbot", "env.sh");
|
|
93
|
-
let env = "";
|
|
94
|
-
try {
|
|
95
|
-
env = await fs.readFile(envPath, "utf8");
|
|
96
|
-
} catch {
|
|
97
|
-
env = "# env.sh (generated by ClawKitchen)\n";
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
env = upsertExportLine(env, "SWARM_REPO_DIR", repoDir);
|
|
101
|
-
if (worktreeRoot) env = upsertExportLine(env, "SWARM_WORKTREE_ROOT", worktreeRoot);
|
|
102
|
-
if (baseRef) env = upsertExportLine(env, "SWARM_BASE_REF", baseRef);
|
|
103
|
-
|
|
104
|
-
await fs.mkdir(path.dirname(envPath), { recursive: true });
|
|
105
|
-
await fs.writeFile(envPath, env, "utf8");
|
|
106
|
-
|
|
107
|
-
// 3) (Optional) chmod +x .clawdbot/*.sh
|
|
108
|
-
if (body.makeExecutable) {
|
|
109
|
-
try {
|
|
110
|
-
const dir = path.join(workspace, ".clawdbot");
|
|
111
|
-
const entries = await fs.readdir(dir);
|
|
112
|
-
const sh = entries.filter((f) => f.endsWith(".sh"));
|
|
113
|
-
for (const f of sh) {
|
|
114
|
-
const p = path.join(dir, f);
|
|
115
|
-
// add +x for user/group/other (standard script perms)
|
|
116
|
-
await fs.chmod(p, 0o750);
|
|
117
|
-
}
|
|
118
|
-
} catch {
|
|
119
|
-
// best-effort
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return NextResponse.json({ ok: true, orchestratorAgentId, workspace });
|
|
124
|
-
} catch (err: unknown) {
|
|
125
|
-
const msg = errorMessage(err);
|
|
126
|
-
const status = /required|match \//i.test(msg) ? 400 : 500;
|
|
127
|
-
return NextResponse.json({ ok: false, error: msg }, { status });
|
|
128
|
-
}
|
|
129
|
-
}
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { NextResponse } from "next/server";
|
|
4
|
-
|
|
5
|
-
import { errorMessage } from "@/lib/errors";
|
|
6
|
-
import { getKitchenApi } from "@/lib/kitchen-api";
|
|
7
|
-
import { runOpenClaw } from "@/lib/openclaw";
|
|
8
|
-
|
|
9
|
-
type AgentListItem = {
|
|
10
|
-
id: string;
|
|
11
|
-
identityName?: string;
|
|
12
|
-
workspace?: string;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
type TmuxSession = {
|
|
16
|
-
name: string;
|
|
17
|
-
attached: boolean;
|
|
18
|
-
windows?: number;
|
|
19
|
-
created?: string;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
type GitWorktree = {
|
|
23
|
-
path: string;
|
|
24
|
-
branch?: string;
|
|
25
|
-
sha?: string;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
async function listAgents(): Promise<AgentListItem[]> {
|
|
29
|
-
const res = await runOpenClaw(["agents", "list", "--json"]);
|
|
30
|
-
if (!res.ok) throw new Error(res.stderr || "Failed to list agents");
|
|
31
|
-
return JSON.parse(res.stdout) as AgentListItem[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function pickOrchestratorAgentId(teamId: string, agents: AgentListItem[]) {
|
|
35
|
-
const candidates = [
|
|
36
|
-
`${teamId}-swarm-orchestrator`,
|
|
37
|
-
`${teamId}-orchestrator`,
|
|
38
|
-
"swarm-orchestrator",
|
|
39
|
-
"orchestrator",
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
for (const id of candidates) {
|
|
43
|
-
const match = agents.find((a) => a.id === id);
|
|
44
|
-
if (match) return match;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Heuristic fallback: any agent id containing "orchestrator" whose workspace path mentions the teamId.
|
|
48
|
-
const heuristic = agents.find((a) => {
|
|
49
|
-
const id = String(a.id ?? "");
|
|
50
|
-
const ws = String(a.workspace ?? "");
|
|
51
|
-
return id.includes("orchestrator") && (ws.includes(`/workspace-${teamId}`) || ws.includes(teamId));
|
|
52
|
-
});
|
|
53
|
-
return heuristic ?? null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function runCommand(argv: string[], timeoutMs = 8000) {
|
|
57
|
-
const api = getKitchenApi();
|
|
58
|
-
const res = (await api.runtime.system.runCommandWithTimeout(argv, { timeoutMs })) as {
|
|
59
|
-
stdout?: unknown;
|
|
60
|
-
stderr?: unknown;
|
|
61
|
-
};
|
|
62
|
-
return { stdout: String(res.stdout ?? ""), stderr: String(res.stderr ?? "") };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function parseTmuxList(stdout: string): TmuxSession[] {
|
|
66
|
-
const lines = stdout
|
|
67
|
-
.split("\n")
|
|
68
|
-
.map((l) => l.trimEnd())
|
|
69
|
-
.filter(Boolean);
|
|
70
|
-
|
|
71
|
-
// Expected format:
|
|
72
|
-
// name\tattached\twindows\tcreated
|
|
73
|
-
return lines.map((line) => {
|
|
74
|
-
const [name, attached, windows, created] = line.split("\t");
|
|
75
|
-
return {
|
|
76
|
-
name: String(name ?? "").trim(),
|
|
77
|
-
attached: String(attached ?? "0").trim() === "1",
|
|
78
|
-
windows: windows ? Number(windows) : undefined,
|
|
79
|
-
created: created ? String(created) : undefined,
|
|
80
|
-
};
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function parseWorktreePorcelain(stdout: string): GitWorktree[] {
|
|
85
|
-
// `git worktree list --porcelain` is a list of blocks separated by blank lines.
|
|
86
|
-
const blocks = stdout
|
|
87
|
-
.split(/\n\s*\n/g)
|
|
88
|
-
.map((b) => b.trim())
|
|
89
|
-
.filter(Boolean);
|
|
90
|
-
|
|
91
|
-
return blocks
|
|
92
|
-
.map((block) => {
|
|
93
|
-
const wt: GitWorktree = { path: "" };
|
|
94
|
-
for (const rawLine of block.split("\n")) {
|
|
95
|
-
const line = rawLine.trim();
|
|
96
|
-
if (line.startsWith("worktree ")) wt.path = line.slice("worktree ".length).trim();
|
|
97
|
-
else if (line.startsWith("HEAD ")) wt.sha = line.slice("HEAD ".length).trim();
|
|
98
|
-
else if (line.startsWith("branch ")) wt.branch = line.slice("branch ".length).trim();
|
|
99
|
-
}
|
|
100
|
-
return wt.path ? wt : null;
|
|
101
|
-
})
|
|
102
|
-
.filter((x): x is GitWorktree => Boolean(x));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async function firstExistingJsonFile(absCandidates: string[]) {
|
|
106
|
-
for (const p of absCandidates) {
|
|
107
|
-
try {
|
|
108
|
-
const stat = await fs.stat(p);
|
|
109
|
-
if (stat.isFile()) return p;
|
|
110
|
-
} catch {
|
|
111
|
-
// ignore
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export async function GET(req: Request) {
|
|
118
|
-
const { searchParams } = new URL(req.url);
|
|
119
|
-
const teamId = String(searchParams.get("teamId") ?? "").trim();
|
|
120
|
-
if (!teamId) return NextResponse.json({ ok: false, error: "teamId is required" }, { status: 400 });
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
const agents = await listAgents();
|
|
124
|
-
const match = pickOrchestratorAgentId(teamId, agents);
|
|
125
|
-
|
|
126
|
-
if (!match || !match.workspace) {
|
|
127
|
-
return NextResponse.json({
|
|
128
|
-
ok: true,
|
|
129
|
-
teamId,
|
|
130
|
-
present: false,
|
|
131
|
-
reason:
|
|
132
|
-
"No orchestrator agent detected. Expected an agent id like <teamId>-swarm-orchestrator (or swarm-orchestrator).",
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const workspace = match.workspace;
|
|
137
|
-
|
|
138
|
-
// tmux sessions (best-effort)
|
|
139
|
-
let tmuxSessions: TmuxSession[] = [];
|
|
140
|
-
try {
|
|
141
|
-
const { stdout } = await runCommand([
|
|
142
|
-
"tmux",
|
|
143
|
-
"ls",
|
|
144
|
-
"-F",
|
|
145
|
-
"#S\t#{session_attached}\t#{session_windows}\t#{session_created_string}",
|
|
146
|
-
]);
|
|
147
|
-
tmuxSessions = parseTmuxList(stdout);
|
|
148
|
-
} catch {
|
|
149
|
-
tmuxSessions = [];
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// git worktrees (best-effort)
|
|
153
|
-
let worktrees: GitWorktree[] = [];
|
|
154
|
-
try {
|
|
155
|
-
const { stdout } = await runCommand(["git", "-C", workspace, "worktree", "list", "--porcelain"], 12000);
|
|
156
|
-
worktrees = parseWorktreePorcelain(stdout);
|
|
157
|
-
} catch {
|
|
158
|
-
worktrees = [];
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// active tasks (best-effort)
|
|
162
|
-
const activeTasksPath = await firstExistingJsonFile([
|
|
163
|
-
path.join(workspace, "active-tasks.json"),
|
|
164
|
-
path.join(workspace, ".clawdbot", "active-tasks.json"),
|
|
165
|
-
path.join(workspace, "notes", "active-tasks.json"),
|
|
166
|
-
]);
|
|
167
|
-
|
|
168
|
-
let activeTasksSummary: { path: string; taskCount?: number; rawType?: string } | null = null;
|
|
169
|
-
if (activeTasksPath) {
|
|
170
|
-
try {
|
|
171
|
-
const raw = await fs.readFile(activeTasksPath, "utf8");
|
|
172
|
-
const parsed = JSON.parse(raw) as unknown;
|
|
173
|
-
if (Array.isArray(parsed)) {
|
|
174
|
-
activeTasksSummary = { path: activeTasksPath, taskCount: parsed.length, rawType: "array" };
|
|
175
|
-
} else if (parsed && typeof parsed === "object") {
|
|
176
|
-
const obj = parsed as { tasks?: unknown };
|
|
177
|
-
const tasks = Array.isArray(obj.tasks) ? obj.tasks : null;
|
|
178
|
-
activeTasksSummary = {
|
|
179
|
-
path: activeTasksPath,
|
|
180
|
-
taskCount: tasks ? tasks.length : undefined,
|
|
181
|
-
rawType: "object",
|
|
182
|
-
};
|
|
183
|
-
} else {
|
|
184
|
-
activeTasksSummary = { path: activeTasksPath, rawType: typeof parsed };
|
|
185
|
-
}
|
|
186
|
-
} catch {
|
|
187
|
-
activeTasksSummary = { path: activeTasksPath };
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const settingsPaths = [
|
|
192
|
-
path.join(workspace, ".env"),
|
|
193
|
-
path.join(workspace, ".clawdbot", "README.md"),
|
|
194
|
-
path.join(workspace, ".clawdbot", "CONVENTIONS.md"),
|
|
195
|
-
path.join(workspace, ".clawdbot", "PROMPT_TEMPLATE.md"),
|
|
196
|
-
path.join(workspace, ".clawdbot", "TEMPLATE.md"),
|
|
197
|
-
];
|
|
198
|
-
|
|
199
|
-
return NextResponse.json({
|
|
200
|
-
ok: true,
|
|
201
|
-
teamId,
|
|
202
|
-
present: true,
|
|
203
|
-
agent: {
|
|
204
|
-
id: match.id,
|
|
205
|
-
identityName: match.identityName,
|
|
206
|
-
workspace,
|
|
207
|
-
},
|
|
208
|
-
tmuxSessions,
|
|
209
|
-
worktrees,
|
|
210
|
-
activeTasksSummary,
|
|
211
|
-
settingsPaths,
|
|
212
|
-
});
|
|
213
|
-
} catch (e: unknown) {
|
|
214
|
-
return NextResponse.json({ ok: false, error: errorMessage(e) }, { status: 500 });
|
|
215
|
-
}
|
|
216
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { NextResponse } from "next/server";
|
|
2
|
-
import { runOpenClaw } from "@/lib/openclaw";
|
|
3
|
-
import { findRecipeById } from "@/lib/recipes";
|
|
4
|
-
|
|
5
|
-
export async function POST(req: Request) {
|
|
6
|
-
const body = (await req.json()) as { teamId?: string; includeAmbiguous?: boolean };
|
|
7
|
-
const teamId = String(body.teamId ?? "").trim();
|
|
8
|
-
if (!teamId) return NextResponse.json({ ok: false, error: "teamId is required" }, { status: 400 });
|
|
9
|
-
|
|
10
|
-
const match = await findRecipeById(teamId);
|
|
11
|
-
if (match?.kind === "team" && match.source === "builtin") {
|
|
12
|
-
return NextResponse.json(
|
|
13
|
-
{ ok: false, error: `Refusing to delete builtin team: ${teamId}. Clone to a custom team first.` },
|
|
14
|
-
{ status: 403 },
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const args = ["recipes", "remove-team", "--team-id", teamId, "--yes", "--json"];
|
|
19
|
-
if (body.includeAmbiguous) args.push("--include-ambiguous");
|
|
20
|
-
|
|
21
|
-
const res = await runOpenClaw(args);
|
|
22
|
-
if (!res.ok) {
|
|
23
|
-
return NextResponse.json(
|
|
24
|
-
{ ok: false, error: res.stderr.trim() || `openclaw ${args.join(" ")} failed (exit=${res.exitCode})`, stdout: res.stdout, stderr: res.stderr },
|
|
25
|
-
{ status: 500 },
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let parsed: unknown = null;
|
|
30
|
-
try {
|
|
31
|
-
parsed = JSON.parse(res.stdout);
|
|
32
|
-
} catch {
|
|
33
|
-
parsed = { raw: res.stdout };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return NextResponse.json({ ok: true, result: parsed, stderr: res.stderr });
|
|
37
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { NextResponse } from "next/server";
|
|
2
|
-
import { runOpenClaw } from "@/lib/openclaw";
|
|
3
|
-
import { installSkillErrorResponse } from "@/lib/api-route-helpers";
|
|
4
|
-
|
|
5
|
-
export async function POST(req: Request) {
|
|
6
|
-
const body = (await req.json()) as { teamId?: string; skill?: string };
|
|
7
|
-
const teamId = String(body.teamId ?? "").trim();
|
|
8
|
-
const skill = String(body.skill ?? "").trim();
|
|
9
|
-
|
|
10
|
-
if (!teamId) return NextResponse.json({ ok: false, error: "teamId is required" }, { status: 400 });
|
|
11
|
-
if (!skill) return NextResponse.json({ ok: false, error: "skill is required" }, { status: 400 });
|
|
12
|
-
|
|
13
|
-
const args = ["recipes", "install-skill", skill, "--team-id", teamId, "--yes"];
|
|
14
|
-
const res = await runOpenClaw(args);
|
|
15
|
-
if (!res.ok) return installSkillErrorResponse(args, res);
|
|
16
|
-
|
|
17
|
-
return NextResponse.json({ ok: true, teamId, skill, stdout: res.stdout, stderr: res.stderr });
|
|
18
|
-
}
|