@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,40 +0,0 @@
|
|
|
1
|
-
import Link from "next/link";
|
|
2
|
-
import { unstable_noStore as noStore } from "next/cache";
|
|
3
|
-
|
|
4
|
-
import { getTeamDisplayName } from "@/lib/recipes";
|
|
5
|
-
import WorkflowsClient from "./workflows-client";
|
|
6
|
-
|
|
7
|
-
export const dynamic = "force-dynamic";
|
|
8
|
-
export const revalidate = 0;
|
|
9
|
-
|
|
10
|
-
export default async function WorkflowsPage({
|
|
11
|
-
params,
|
|
12
|
-
}: {
|
|
13
|
-
params: Promise<{ teamId: string }>;
|
|
14
|
-
}) {
|
|
15
|
-
noStore();
|
|
16
|
-
|
|
17
|
-
const { teamId } = await params;
|
|
18
|
-
const name = await getTeamDisplayName(teamId);
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div className="space-y-4 p-6">
|
|
22
|
-
<div className="flex flex-wrap items-start justify-between gap-4">
|
|
23
|
-
<div>
|
|
24
|
-
<Link
|
|
25
|
-
href={`/teams/${encodeURIComponent(teamId)}`}
|
|
26
|
-
className="text-sm font-medium text-[color:var(--ck-text-secondary)] hover:underline"
|
|
27
|
-
>
|
|
28
|
-
← {name || teamId}
|
|
29
|
-
</Link>
|
|
30
|
-
<h1 className="mt-2 text-2xl font-semibold tracking-tight">Workflows</h1>
|
|
31
|
-
<p className="mt-1 text-sm text-[color:var(--ck-text-secondary)]">
|
|
32
|
-
Create and edit workflow definitions for this team.
|
|
33
|
-
</p>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<WorkflowsClient teamId={teamId} />
|
|
38
|
-
</div>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
@@ -1,494 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
-
import Link from "next/link";
|
|
5
|
-
import { useRouter } from "next/navigation";
|
|
6
|
-
import { fetchJson } from "@/lib/fetch-json";
|
|
7
|
-
import { errorMessage } from "@/lib/errors";
|
|
8
|
-
|
|
9
|
-
type RunDetail = {
|
|
10
|
-
id: string;
|
|
11
|
-
status?: string;
|
|
12
|
-
startedAt?: string;
|
|
13
|
-
finishedAt?: string;
|
|
14
|
-
meta?: unknown;
|
|
15
|
-
memoryUsed?: unknown;
|
|
16
|
-
approval?: unknown;
|
|
17
|
-
raw?: Record<string, unknown>;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
function isRecord(v: unknown): v is Record<string, unknown> {
|
|
21
|
-
return Boolean(v) && typeof v === "object" && !Array.isArray(v);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default function WorkflowsClient({ teamId }: { teamId: string }) {
|
|
25
|
-
const router = useRouter();
|
|
26
|
-
const [workflows, setWorkflows] = useState<Array<{ id: string; name?: string }>>([]);
|
|
27
|
-
const [loading, setLoading] = useState(true);
|
|
28
|
-
const [refreshing, setRefreshing] = useState(false);
|
|
29
|
-
const [error, setError] = useState<string>("");
|
|
30
|
-
|
|
31
|
-
const [expandedWorkflowId, setExpandedWorkflowId] = useState<string>("");
|
|
32
|
-
const [runsByWorkflow, setRunsByWorkflow] = useState<Record<string, string[]>>({});
|
|
33
|
-
const [runsLoading, setRunsLoading] = useState<Record<string, boolean>>({});
|
|
34
|
-
const [selectedRunId, setSelectedRunId] = useState<string>("");
|
|
35
|
-
const [selectedWorkflowId, setSelectedWorkflowId] = useState<string>("");
|
|
36
|
-
const [selectedRun, setSelectedRun] = useState<RunDetail | null>(null);
|
|
37
|
-
const [runError, setRunError] = useState<string>("");
|
|
38
|
-
const [approvalNote, setApprovalNote] = useState<string>("");
|
|
39
|
-
const [approvalBusy, setApprovalBusy] = useState<boolean>(false);
|
|
40
|
-
|
|
41
|
-
const load = useCallback(
|
|
42
|
-
async (opts?: { quiet?: boolean }) => {
|
|
43
|
-
const quiet = Boolean(opts?.quiet);
|
|
44
|
-
setError("");
|
|
45
|
-
if (!quiet) setLoading(true);
|
|
46
|
-
try {
|
|
47
|
-
const json = await fetchJson<{ ok?: boolean; files?: string[] }>(
|
|
48
|
-
`/api/teams/workflows?teamId=${encodeURIComponent(teamId)}`,
|
|
49
|
-
{ cache: "no-store" }
|
|
50
|
-
);
|
|
51
|
-
if (!json.ok) throw new Error("Failed to load workflows");
|
|
52
|
-
const files = Array.isArray(json.files) ? json.files : [];
|
|
53
|
-
const ids = files
|
|
54
|
-
.map((f) => (f.endsWith(".workflow.json") ? f.slice(0, -".workflow.json".length) : null))
|
|
55
|
-
.filter((id): id is string => Boolean(id));
|
|
56
|
-
setWorkflows(ids.map((id) => ({ id, name: id })));
|
|
57
|
-
} catch (e: unknown) {
|
|
58
|
-
setError(errorMessage(e));
|
|
59
|
-
} finally {
|
|
60
|
-
if (!quiet) setLoading(false);
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
[teamId]
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
void load();
|
|
68
|
-
}, [load]);
|
|
69
|
-
|
|
70
|
-
async function onRefresh() {
|
|
71
|
-
setRefreshing(true);
|
|
72
|
-
try {
|
|
73
|
-
await load({ quiet: true });
|
|
74
|
-
} finally {
|
|
75
|
-
setRefreshing(false);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async function onDelete(id: string) {
|
|
80
|
-
if (!confirm(`Delete workflow “${id}”? This removes the .workflow.json file from the team workspace.`)) return;
|
|
81
|
-
setError("");
|
|
82
|
-
try {
|
|
83
|
-
const json = await fetchJson<{ ok?: boolean; error?: string }>(
|
|
84
|
-
`/api/teams/workflows?teamId=${encodeURIComponent(teamId)}&id=${encodeURIComponent(id)}`,
|
|
85
|
-
{ method: "DELETE" }
|
|
86
|
-
);
|
|
87
|
-
if (!json.ok) throw new Error(json.error || "Failed to delete workflow");
|
|
88
|
-
await load({ quiet: true });
|
|
89
|
-
if (expandedWorkflowId === id) {
|
|
90
|
-
setExpandedWorkflowId("");
|
|
91
|
-
setSelectedRunId("");
|
|
92
|
-
setSelectedRun(null);
|
|
93
|
-
}
|
|
94
|
-
} catch (e: unknown) {
|
|
95
|
-
setError(errorMessage(e));
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async function loadRunsForWorkflow(workflowId: string) {
|
|
100
|
-
setRunsLoading((s) => ({ ...s, [workflowId]: true }));
|
|
101
|
-
setRunError("");
|
|
102
|
-
try {
|
|
103
|
-
const json = await fetchJson<{ ok?: boolean; files?: string[]; error?: string }>(
|
|
104
|
-
`/api/teams/workflow-runs?teamId=${encodeURIComponent(teamId)}&workflowId=${encodeURIComponent(workflowId)}`,
|
|
105
|
-
{ cache: "no-store" }
|
|
106
|
-
);
|
|
107
|
-
if (!json.ok) throw new Error(json.error || "Failed to load runs");
|
|
108
|
-
const files = Array.isArray(json.files) ? json.files : [];
|
|
109
|
-
const runIds = files
|
|
110
|
-
.map((f) => (typeof f === "string" && f.endsWith(".run.json") ? f.slice(0, -".run.json".length) : null))
|
|
111
|
-
.filter((x): x is string => Boolean(x));
|
|
112
|
-
setRunsByWorkflow((s) => ({ ...s, [workflowId]: runIds }));
|
|
113
|
-
} catch (e: unknown) {
|
|
114
|
-
setRunError(errorMessage(e));
|
|
115
|
-
setRunsByWorkflow((s) => ({ ...s, [workflowId]: [] }));
|
|
116
|
-
} finally {
|
|
117
|
-
setRunsLoading((s) => ({ ...s, [workflowId]: false }));
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function loadRunDetail(workflowId: string, runId: string) {
|
|
122
|
-
setSelectedWorkflowId(workflowId);
|
|
123
|
-
setSelectedRunId(runId);
|
|
124
|
-
setSelectedRun(null);
|
|
125
|
-
setRunError("");
|
|
126
|
-
setApprovalNote("");
|
|
127
|
-
try {
|
|
128
|
-
const json = await fetchJson<{ ok?: boolean; run?: unknown; error?: string }>(
|
|
129
|
-
`/api/teams/workflow-runs?teamId=${encodeURIComponent(teamId)}&workflowId=${encodeURIComponent(workflowId)}&runId=${encodeURIComponent(runId)}`,
|
|
130
|
-
{ cache: "no-store" }
|
|
131
|
-
);
|
|
132
|
-
if (!json.ok) throw new Error(json.error || "Failed to load run");
|
|
133
|
-
const run = isRecord(json.run) ? json.run : null;
|
|
134
|
-
if (!run) throw new Error("Invalid run format");
|
|
135
|
-
setSelectedRun({
|
|
136
|
-
id: String(run.id ?? runId),
|
|
137
|
-
status: typeof run.status === "string" ? run.status : undefined,
|
|
138
|
-
startedAt: typeof run.startedAt === "string" ? run.startedAt : undefined,
|
|
139
|
-
finishedAt: typeof (run as Record<string, unknown>).finishedAt === "string" ? String((run as Record<string, unknown>).finishedAt) : undefined,
|
|
140
|
-
meta: (run as Record<string, unknown>).meta,
|
|
141
|
-
memoryUsed: (run as Record<string, unknown>).memoryUsed,
|
|
142
|
-
approval: (run as Record<string, unknown>).approval,
|
|
143
|
-
raw: run,
|
|
144
|
-
});
|
|
145
|
-
} catch (e: unknown) {
|
|
146
|
-
setRunError(errorMessage(e));
|
|
147
|
-
setSelectedRun(null);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async function onAddTemplate(templateId: string) {
|
|
152
|
-
if (!confirm(`Add workflow from template “${templateId}”?`)) return;
|
|
153
|
-
setError("");
|
|
154
|
-
try {
|
|
155
|
-
const json = await fetchJson<{ ok?: boolean; workflowId?: string; error?: string }>("/api/teams/workflow-templates", {
|
|
156
|
-
method: "POST",
|
|
157
|
-
headers: { "content-type": "application/json" },
|
|
158
|
-
body: JSON.stringify({ teamId, templateId }),
|
|
159
|
-
});
|
|
160
|
-
if (!json.ok) throw new Error(json.error || "Failed to apply template");
|
|
161
|
-
const workflowId = String(json.workflowId ?? "").trim();
|
|
162
|
-
if (!workflowId) throw new Error("Template applied but workflowId missing");
|
|
163
|
-
await load({ quiet: true });
|
|
164
|
-
router.push(`/teams/${encodeURIComponent(teamId)}/workflows/${encodeURIComponent(workflowId)}`);
|
|
165
|
-
} catch (e: unknown) {
|
|
166
|
-
setError(errorMessage(e));
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
const memoryUsedItems = useMemo(() => {
|
|
170
|
-
const run = selectedRun;
|
|
171
|
-
if (!run) return [] as Array<{ ts: string; author: string; type: string; content: string; source?: unknown }>;
|
|
172
|
-
|
|
173
|
-
const raw = run.memoryUsed ?? (isRecord(run.meta) ? (run.meta as Record<string, unknown>).memoryUsed : undefined);
|
|
174
|
-
if (!Array.isArray(raw)) return [];
|
|
175
|
-
|
|
176
|
-
return raw
|
|
177
|
-
.map((x) => (isRecord(x) ? x : null))
|
|
178
|
-
.filter(Boolean)
|
|
179
|
-
.map((o) => ({
|
|
180
|
-
ts: String((o as Record<string, unknown>).ts ?? "").trim(),
|
|
181
|
-
author: String((o as Record<string, unknown>).author ?? "").trim(),
|
|
182
|
-
type: String((o as Record<string, unknown>).type ?? "").trim(),
|
|
183
|
-
content: String((o as Record<string, unknown>).content ?? "").trim(),
|
|
184
|
-
source: (o as Record<string, unknown>).source,
|
|
185
|
-
}))
|
|
186
|
-
.filter((it) => it.ts && it.author && it.type && it.content);
|
|
187
|
-
}, [selectedRun]);
|
|
188
|
-
|
|
189
|
-
if (loading) {
|
|
190
|
-
return <div className="ck-glass p-4">Loading workflows…</div>;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return (
|
|
194
|
-
<div className="ck-glass p-6">
|
|
195
|
-
<div>
|
|
196
|
-
<h2 className="text-lg font-semibold">Workflows (file-first)</h2>
|
|
197
|
-
<p className="mt-1 text-sm text-[color:var(--ck-text-secondary)]">
|
|
198
|
-
Stored in <code>shared-context/workflows/<id>.workflow.json</code> inside the team workspace.
|
|
199
|
-
</p>
|
|
200
|
-
|
|
201
|
-
<div className="mt-3 flex flex-wrap items-center justify-start gap-2">
|
|
202
|
-
<button
|
|
203
|
-
type="button"
|
|
204
|
-
onClick={() => {
|
|
205
|
-
const id = `new-${Date.now()}`;
|
|
206
|
-
router.push(`/teams/${encodeURIComponent(teamId)}/workflows/${encodeURIComponent(id)}?draft=1`);
|
|
207
|
-
}}
|
|
208
|
-
className="rounded-[var(--ck-radius-sm)] bg-[var(--ck-accent-red)] px-3 py-2 text-sm font-medium text-white shadow-[var(--ck-shadow-1)]"
|
|
209
|
-
>
|
|
210
|
-
Add workflow
|
|
211
|
-
</button>
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
<button
|
|
215
|
-
type="button"
|
|
216
|
-
onClick={() => void onAddTemplate("marketing-cadence-v1")}
|
|
217
|
-
className="rounded-[var(--ck-radius-sm)] border border-white/10 bg-white/5 px-3 py-2 text-sm font-medium text-[color:var(--ck-text-primary)] hover:bg-white/10"
|
|
218
|
-
>
|
|
219
|
-
Add Marketing Cadence template
|
|
220
|
-
</button>
|
|
221
|
-
|
|
222
|
-
<button
|
|
223
|
-
type="button"
|
|
224
|
-
onClick={onRefresh}
|
|
225
|
-
disabled={refreshing}
|
|
226
|
-
className="rounded-[var(--ck-radius-sm)] border border-white/10 bg-white/5 px-3 py-2 text-sm font-medium text-[color:var(--ck-text-primary)] hover:bg-white/10 disabled:opacity-60"
|
|
227
|
-
>
|
|
228
|
-
{refreshing ? "Refreshing…" : "Refresh"}
|
|
229
|
-
</button>
|
|
230
|
-
</div>
|
|
231
|
-
</div>
|
|
232
|
-
|
|
233
|
-
{error ? (
|
|
234
|
-
<div className="mt-4 rounded-[var(--ck-radius-sm)] border border-red-400/30 bg-red-500/10 p-4 text-sm text-red-100">
|
|
235
|
-
{error}
|
|
236
|
-
</div>
|
|
237
|
-
) : null}
|
|
238
|
-
|
|
239
|
-
{workflows.length === 0 ? (
|
|
240
|
-
<p className="mt-4 text-sm text-[color:var(--ck-text-secondary)]">No workflows yet.</p>
|
|
241
|
-
) : (
|
|
242
|
-
<ul className="mt-4 divide-y divide-white/10 overflow-hidden rounded-[var(--ck-radius-sm)] border border-white/10">
|
|
243
|
-
{workflows.map((w) => {
|
|
244
|
-
const expanded = expandedWorkflowId === w.id;
|
|
245
|
-
const runs = runsByWorkflow[w.id] ?? [];
|
|
246
|
-
const isLoadingRuns = Boolean(runsLoading[w.id]);
|
|
247
|
-
|
|
248
|
-
return (
|
|
249
|
-
<li key={w.id} className="bg-white/5">
|
|
250
|
-
<div className="flex items-center justify-between gap-3 px-4 py-3">
|
|
251
|
-
<button
|
|
252
|
-
type="button"
|
|
253
|
-
onClick={async () => {
|
|
254
|
-
const next = expanded ? "" : w.id;
|
|
255
|
-
setExpandedWorkflowId(next);
|
|
256
|
-
setSelectedRunId("");
|
|
257
|
-
setSelectedRun(null);
|
|
258
|
-
setRunError("");
|
|
259
|
-
if (next) await loadRunsForWorkflow(next);
|
|
260
|
-
}}
|
|
261
|
-
className="min-w-0 flex-1 text-left"
|
|
262
|
-
>
|
|
263
|
-
<div className="truncate text-sm font-medium text-[color:var(--ck-text-primary)]">{w.name || w.id}</div>
|
|
264
|
-
<div className="truncate text-xs text-[color:var(--ck-text-tertiary)]">{w.id}</div>
|
|
265
|
-
<div className="mt-1 text-[10px] text-[color:var(--ck-text-tertiary)]">
|
|
266
|
-
Click to {expanded ? "collapse" : "expand"} run details
|
|
267
|
-
</div>
|
|
268
|
-
</button>
|
|
269
|
-
|
|
270
|
-
<div className="flex shrink-0 items-center gap-2">
|
|
271
|
-
<Link
|
|
272
|
-
href={`/teams/${encodeURIComponent(teamId)}/workflows/${encodeURIComponent(w.id)}`}
|
|
273
|
-
className="rounded-[var(--ck-radius-sm)] border border-white/10 bg-white/5 px-3 py-1.5 text-sm font-medium text-[color:var(--ck-text-primary)] hover:bg-white/10"
|
|
274
|
-
>
|
|
275
|
-
Edit
|
|
276
|
-
</Link>
|
|
277
|
-
<button
|
|
278
|
-
type="button"
|
|
279
|
-
onClick={() => void onDelete(w.id)}
|
|
280
|
-
className="rounded-[var(--ck-radius-sm)] border border-[color:rgba(255,59,48,0.45)] bg-[color:rgba(255,59,48,0.08)] px-3 py-1.5 text-sm font-medium text-[color:var(--ck-accent-red)] transition-colors hover:bg-[color:rgba(255,59,48,0.12)]"
|
|
281
|
-
>
|
|
282
|
-
Delete
|
|
283
|
-
</button>
|
|
284
|
-
</div>
|
|
285
|
-
</div>
|
|
286
|
-
|
|
287
|
-
{expanded ? (
|
|
288
|
-
<div className="border-t border-white/10 bg-black/20 px-4 py-3">
|
|
289
|
-
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
290
|
-
<div className="text-xs font-medium text-[color:var(--ck-text-secondary)]">Runs</div>
|
|
291
|
-
<button
|
|
292
|
-
type="button"
|
|
293
|
-
disabled={isLoadingRuns}
|
|
294
|
-
onClick={() => void loadRunsForWorkflow(w.id)}
|
|
295
|
-
className="rounded-[var(--ck-radius-sm)] border border-white/10 bg-white/5 px-2 py-1 text-[11px] font-medium text-[color:var(--ck-text-primary)] hover:bg-white/10 disabled:opacity-60"
|
|
296
|
-
>
|
|
297
|
-
{isLoadingRuns ? "Loading…" : "Refresh runs"}
|
|
298
|
-
</button>
|
|
299
|
-
</div>
|
|
300
|
-
|
|
301
|
-
{runError ? (
|
|
302
|
-
<div className="mt-2 rounded-[var(--ck-radius-sm)] border border-red-400/30 bg-red-500/10 p-2 text-xs text-red-100">{runError}</div>
|
|
303
|
-
) : null}
|
|
304
|
-
|
|
305
|
-
<div className="mt-2 grid grid-cols-1 gap-3 lg:grid-cols-2">
|
|
306
|
-
<div className="rounded-[var(--ck-radius-sm)] border border-white/10 bg-black/20 p-2">
|
|
307
|
-
{runs.length ? (
|
|
308
|
-
<div className="space-y-1">
|
|
309
|
-
{runs.slice(0, 8).map((runId) => {
|
|
310
|
-
const selected = selectedRunId === runId;
|
|
311
|
-
return (
|
|
312
|
-
<button
|
|
313
|
-
key={runId}
|
|
314
|
-
type="button"
|
|
315
|
-
onClick={() => void loadRunDetail(w.id, runId)}
|
|
316
|
-
className={
|
|
317
|
-
selected
|
|
318
|
-
? "w-full rounded-[var(--ck-radius-sm)] bg-white/10 px-2 py-1 text-left text-[11px] text-[color:var(--ck-text-primary)]"
|
|
319
|
-
: "w-full rounded-[var(--ck-radius-sm)] px-2 py-1 text-left text-[11px] text-[color:var(--ck-text-secondary)] hover:bg-white/5"
|
|
320
|
-
}
|
|
321
|
-
>
|
|
322
|
-
<span className="font-mono">{runId}</span>
|
|
323
|
-
</button>
|
|
324
|
-
);
|
|
325
|
-
})}
|
|
326
|
-
</div>
|
|
327
|
-
) : (
|
|
328
|
-
<div className="text-xs text-[color:var(--ck-text-tertiary)]">No runs yet.</div>
|
|
329
|
-
)}
|
|
330
|
-
</div>
|
|
331
|
-
|
|
332
|
-
<div className="rounded-[var(--ck-radius-sm)] border border-white/10 bg-black/20 p-3">
|
|
333
|
-
{selectedRun ? (
|
|
334
|
-
<div className="space-y-3">
|
|
335
|
-
<div>
|
|
336
|
-
<div className="text-xs font-medium text-[color:var(--ck-text-secondary)]">Run detail</div>
|
|
337
|
-
<div className="mt-1 text-[11px] text-[color:var(--ck-text-tertiary)]">
|
|
338
|
-
<span className="font-mono">{selectedRun.id}</span>
|
|
339
|
-
{selectedRun.status ? <span> • {selectedRun.status}</span> : null}
|
|
340
|
-
</div>
|
|
341
|
-
</div>
|
|
342
|
-
|
|
343
|
-
<div className="border-t border-white/10 pt-3">
|
|
344
|
-
<div className="text-xs font-medium text-[color:var(--ck-text-secondary)]">Approval</div>
|
|
345
|
-
{(() => {
|
|
346
|
-
const approval = selectedRun.approval;
|
|
347
|
-
if (!isRecord(approval)) {
|
|
348
|
-
return <div className="mt-2 text-xs text-[color:var(--ck-text-tertiary)]">(No approval info recorded.)</div>;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
const state = String(approval.state ?? "").trim();
|
|
352
|
-
const requestedAt = typeof approval.requestedAt === "string" ? approval.requestedAt : "";
|
|
353
|
-
const decidedAt = typeof approval.decidedAt === "string" ? approval.decidedAt : "";
|
|
354
|
-
const decidedBy = typeof approval.decidedBy === "string" ? approval.decidedBy : "";
|
|
355
|
-
const note = typeof approval.note === "string" ? approval.note : "";
|
|
356
|
-
const outbound = isRecord(approval.outbound) ? approval.outbound : null;
|
|
357
|
-
|
|
358
|
-
const canAct = state === "pending" || selectedRun.status === "waiting_for_approval";
|
|
359
|
-
|
|
360
|
-
return (
|
|
361
|
-
<div className="mt-2 space-y-2">
|
|
362
|
-
<div className="rounded-[var(--ck-radius-sm)] border border-white/10 bg-black/25 p-2 text-[11px] text-[color:var(--ck-text-secondary)]">
|
|
363
|
-
<div>
|
|
364
|
-
<span className="font-mono">state</span>: <span className="font-mono text-[color:var(--ck-text-primary)]">{state || "(unknown)"}</span>
|
|
365
|
-
</div>
|
|
366
|
-
{requestedAt ? (
|
|
367
|
-
<div>
|
|
368
|
-
<span className="font-mono">requestedAt</span>: <span className="font-mono">{requestedAt}</span>
|
|
369
|
-
</div>
|
|
370
|
-
) : null}
|
|
371
|
-
{decidedAt ? (
|
|
372
|
-
<div>
|
|
373
|
-
<span className="font-mono">decidedAt</span>: <span className="font-mono">{decidedAt}</span>
|
|
374
|
-
</div>
|
|
375
|
-
) : null}
|
|
376
|
-
{decidedBy ? (
|
|
377
|
-
<div>
|
|
378
|
-
<span className="font-mono">decidedBy</span>: <span className="font-mono">{decidedBy}</span>
|
|
379
|
-
</div>
|
|
380
|
-
) : null}
|
|
381
|
-
{note ? (
|
|
382
|
-
<div className="mt-1 whitespace-pre-wrap text-xs text-[color:var(--ck-text-primary)]">{note}</div>
|
|
383
|
-
) : null}
|
|
384
|
-
{outbound ? (
|
|
385
|
-
<div className="mt-2 rounded-[var(--ck-radius-sm)] border border-white/10 bg-black/30 p-2 text-[10px]">
|
|
386
|
-
<div className="text-[color:var(--ck-text-tertiary)]">Outbound</div>
|
|
387
|
-
<pre className="mt-1 overflow-auto whitespace-pre-wrap">{JSON.stringify(outbound, null, 2)}</pre>
|
|
388
|
-
</div>
|
|
389
|
-
) : null}
|
|
390
|
-
</div>
|
|
391
|
-
|
|
392
|
-
{canAct ? (
|
|
393
|
-
<div className="rounded-[var(--ck-radius-sm)] border border-white/10 bg-black/20 p-2">
|
|
394
|
-
<div className="text-[10px] uppercase tracking-wide text-[color:var(--ck-text-tertiary)]">approval note (optional)</div>
|
|
395
|
-
<textarea
|
|
396
|
-
value={approvalNote}
|
|
397
|
-
onChange={(e) => setApprovalNote(e.target.value)}
|
|
398
|
-
rows={3}
|
|
399
|
-
className="mt-1 w-full rounded-[var(--ck-radius-sm)] border border-white/10 bg-black/25 px-2 py-1 text-xs text-[color:var(--ck-text-primary)]"
|
|
400
|
-
placeholder="e.g. Ship it / please tweak hook"
|
|
401
|
-
/>
|
|
402
|
-
<div className="mt-2 flex flex-wrap gap-2">
|
|
403
|
-
{([
|
|
404
|
-
{ action: "approve", label: "Approve" },
|
|
405
|
-
{ action: "request_changes", label: "Request changes" },
|
|
406
|
-
{ action: "cancel", label: "Cancel" },
|
|
407
|
-
] as const).map((btn) => (
|
|
408
|
-
<button
|
|
409
|
-
key={btn.action}
|
|
410
|
-
type="button"
|
|
411
|
-
disabled={approvalBusy}
|
|
412
|
-
onClick={async () => {
|
|
413
|
-
if (!selectedWorkflowId || !selectedRunId) return;
|
|
414
|
-
if (!confirm(`${btn.label} run ${selectedRunId}?`)) return;
|
|
415
|
-
setApprovalBusy(true);
|
|
416
|
-
setRunError("");
|
|
417
|
-
try {
|
|
418
|
-
const res = await fetch("/api/teams/workflow-runs", {
|
|
419
|
-
method: "POST",
|
|
420
|
-
headers: { "content-type": "application/json" },
|
|
421
|
-
body: JSON.stringify({
|
|
422
|
-
teamId,
|
|
423
|
-
workflowId: selectedWorkflowId,
|
|
424
|
-
runId: selectedRunId,
|
|
425
|
-
action: btn.action,
|
|
426
|
-
note: approvalNote || undefined,
|
|
427
|
-
decidedBy: "ClawKitchen UI",
|
|
428
|
-
}),
|
|
429
|
-
});
|
|
430
|
-
const json = await res.json();
|
|
431
|
-
if (!res.ok || !json.ok) throw new Error(json.error || "Failed to apply approval action");
|
|
432
|
-
await loadRunDetail(selectedWorkflowId, selectedRunId);
|
|
433
|
-
} catch (e: unknown) {
|
|
434
|
-
setRunError(errorMessage(e));
|
|
435
|
-
} finally {
|
|
436
|
-
setApprovalBusy(false);
|
|
437
|
-
}
|
|
438
|
-
}}
|
|
439
|
-
className="rounded-[var(--ck-radius-sm)] border border-white/10 bg-white/5 px-2 py-1 text-[11px] font-medium text-[color:var(--ck-text-primary)] hover:bg-white/10 disabled:opacity-60"
|
|
440
|
-
>
|
|
441
|
-
{btn.label}
|
|
442
|
-
</button>
|
|
443
|
-
))}
|
|
444
|
-
</div>
|
|
445
|
-
</div>
|
|
446
|
-
) : null}
|
|
447
|
-
</div>
|
|
448
|
-
);
|
|
449
|
-
})()}
|
|
450
|
-
</div>
|
|
451
|
-
|
|
452
|
-
<div className="border-t border-white/10 pt-3">
|
|
453
|
-
<div className="text-xs font-medium text-[color:var(--ck-text-secondary)]">Memory used in this run</div>
|
|
454
|
-
{memoryUsedItems.length ? (
|
|
455
|
-
<div className="mt-2 space-y-2">
|
|
456
|
-
{memoryUsedItems.slice(0, 20).map((m, idx) => (
|
|
457
|
-
<div key={`${m.ts}-${idx}`} className="rounded-[var(--ck-radius-sm)] border border-white/10 bg-black/25 p-2">
|
|
458
|
-
<div className="text-[10px] text-[color:var(--ck-text-tertiary)]">
|
|
459
|
-
<span className="font-mono">{m.ts}</span> • <span className="font-mono">{m.type}</span> • <span className="font-mono">{m.author}</span>
|
|
460
|
-
</div>
|
|
461
|
-
<div className="mt-1 whitespace-pre-wrap text-xs text-[color:var(--ck-text-primary)]">{m.content}</div>
|
|
462
|
-
{m.source !== undefined ? (
|
|
463
|
-
<pre className="mt-2 overflow-auto rounded-[var(--ck-radius-sm)] border border-white/10 bg-black/30 p-2 text-[10px] text-[color:var(--ck-text-secondary)]">
|
|
464
|
-
{JSON.stringify(m.source, null, 2)}
|
|
465
|
-
</pre>
|
|
466
|
-
) : null}
|
|
467
|
-
</div>
|
|
468
|
-
))}
|
|
469
|
-
</div>
|
|
470
|
-
) : (
|
|
471
|
-
<div className="mt-2 text-xs text-[color:var(--ck-text-tertiary)]">
|
|
472
|
-
(None recorded yet.)
|
|
473
|
-
<div className="mt-1">Next step: have the workflow runner write an explicit <span className="font-mono">memoryUsed[]</span> list into the run file.</div>
|
|
474
|
-
</div>
|
|
475
|
-
)}
|
|
476
|
-
</div>
|
|
477
|
-
</div>
|
|
478
|
-
) : selectedRunId ? (
|
|
479
|
-
<div className="text-xs text-[color:var(--ck-text-secondary)]">Loading run…</div>
|
|
480
|
-
) : (
|
|
481
|
-
<div className="text-xs text-[color:var(--ck-text-tertiary)]">Select a run to see details.</div>
|
|
482
|
-
)}
|
|
483
|
-
</div>
|
|
484
|
-
</div>
|
|
485
|
-
</div>
|
|
486
|
-
) : null}
|
|
487
|
-
</li>
|
|
488
|
-
);
|
|
489
|
-
})}
|
|
490
|
-
</ul>
|
|
491
|
-
)}
|
|
492
|
-
</div>
|
|
493
|
-
);
|
|
494
|
-
}
|