@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,23 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { slugifyId } from "../slugify";
|
|
3
|
-
|
|
4
|
-
describe("slugifyId", () => {
|
|
5
|
-
it("lowercases and replaces spaces with hyphens", () => {
|
|
6
|
-
expect(slugifyId("My Goal Title")).toBe("my-goal-title");
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it("trims leading and trailing hyphens", () => {
|
|
10
|
-
expect(slugifyId(" --foo-- ")).toBe("foo");
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("respects maxLength parameter", () => {
|
|
14
|
-
const long = "a".repeat(100);
|
|
15
|
-
expect(slugifyId(long, 64).length).toBe(64);
|
|
16
|
-
expect(slugifyId(long, 80).length).toBe(80);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("returns empty for empty input", () => {
|
|
20
|
-
expect(slugifyId("")).toBe("");
|
|
21
|
-
expect(slugifyId(" ")).toBe("");
|
|
22
|
-
});
|
|
23
|
-
});
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi, beforeEach } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
parseTitle,
|
|
4
|
-
stageDir,
|
|
5
|
-
parseNumberFromFilename,
|
|
6
|
-
listTickets,
|
|
7
|
-
getTicketMarkdown,
|
|
8
|
-
} from "../tickets";
|
|
9
|
-
import fs from "node:fs/promises";
|
|
10
|
-
|
|
11
|
-
vi.mock("node:fs/promises", () => ({
|
|
12
|
-
default: {
|
|
13
|
-
readdir: vi.fn(),
|
|
14
|
-
readFile: vi.fn(),
|
|
15
|
-
stat: vi.fn(),
|
|
16
|
-
},
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
describe("tickets", () => {
|
|
20
|
-
describe("stageDir", () => {
|
|
21
|
-
it("maps each stage to correct path", () => {
|
|
22
|
-
expect(stageDir("backlog")).toContain("work/backlog");
|
|
23
|
-
expect(stageDir("in-progress")).toContain("work/in-progress");
|
|
24
|
-
expect(stageDir("testing")).toContain("work/testing");
|
|
25
|
-
expect(stageDir("done")).toContain("work/done");
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
describe("parseTitle", () => {
|
|
30
|
-
it("derives title from slug when no explicit title", () => {
|
|
31
|
-
expect(parseTitle("# 0033-fix-login-bug")).toBe("Fix Login Bug");
|
|
32
|
-
expect(parseTitle("# 0042-api-rate-limits")).toBe("API Rate Limits");
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("keeps explicit title when present", () => {
|
|
36
|
-
expect(parseTitle("# 0033-fix-login Fix the login bug")).toBe("Fix the login bug");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("handles acronyms in slug", () => {
|
|
40
|
-
expect(parseTitle("# 0100-api-cli-integration")).toContain("API");
|
|
41
|
-
expect(parseTitle("# 0100-api-cli-integration")).toContain("CLI");
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("returns untitled for empty or missing header", () => {
|
|
45
|
-
expect(parseTitle("")).toBe("(untitled)");
|
|
46
|
-
expect(parseTitle("no header here")).toBe("(untitled)");
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe("parseNumberFromFilename", () => {
|
|
51
|
-
it("extracts 4-digit prefix", () => {
|
|
52
|
-
expect(parseNumberFromFilename("0033-fix-login.md")).toBe(33);
|
|
53
|
-
expect(parseNumberFromFilename("0042-something.md")).toBe(42);
|
|
54
|
-
expect(parseNumberFromFilename("0001-first.md")).toBe(1);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("returns null for invalid filenames", () => {
|
|
58
|
-
expect(parseNumberFromFilename("abc-def.md")).toBeNull();
|
|
59
|
-
expect(parseNumberFromFilename("33-something.md")).toBeNull();
|
|
60
|
-
expect(parseNumberFromFilename("00333-too-long.md")).toBeNull();
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
describe("listTickets", () => {
|
|
65
|
-
beforeEach(() => {
|
|
66
|
-
vi.mocked(fs.readdir).mockReset();
|
|
67
|
-
vi.mocked(fs.readFile).mockReset();
|
|
68
|
-
vi.mocked(fs.stat).mockReset();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("returns tickets from all stages", async () => {
|
|
72
|
-
vi.mocked(fs.readdir)
|
|
73
|
-
.mockResolvedValueOnce(["0033-fix-login.md"])
|
|
74
|
-
.mockResolvedValueOnce([])
|
|
75
|
-
.mockResolvedValueOnce([])
|
|
76
|
-
.mockResolvedValueOnce([]);
|
|
77
|
-
|
|
78
|
-
vi.mocked(fs.readFile).mockResolvedValue("# 0033-fix-login Fix Login\n\nOwner: alice");
|
|
79
|
-
vi.mocked(fs.stat).mockResolvedValue({
|
|
80
|
-
mtime: new Date("2026-01-15T10:00:00Z"),
|
|
81
|
-
mtimeMs: new Date("2026-01-15T10:00:00Z").getTime(),
|
|
82
|
-
} as ReturnType<typeof fs.stat> extends Promise<infer T> ? T : never);
|
|
83
|
-
|
|
84
|
-
const result = await listTickets();
|
|
85
|
-
expect(result).toHaveLength(1);
|
|
86
|
-
expect(result[0].number).toBe(33);
|
|
87
|
-
expect(result[0].id).toBe("0033-fix-login");
|
|
88
|
-
expect(result[0].title).toBe("Fix Login");
|
|
89
|
-
expect(result[0].owner).toBe("alice");
|
|
90
|
-
expect(result[0].stage).toBe("backlog");
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("skips stages that throw", async () => {
|
|
94
|
-
vi.mocked(fs.readdir)
|
|
95
|
-
.mockRejectedValueOnce(new Error("no dir"))
|
|
96
|
-
.mockResolvedValueOnce([])
|
|
97
|
-
.mockResolvedValueOnce([])
|
|
98
|
-
.mockResolvedValueOnce([]);
|
|
99
|
-
|
|
100
|
-
const result = await listTickets();
|
|
101
|
-
expect(result).toEqual([]);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("skips non-md files", async () => {
|
|
105
|
-
vi.mocked(fs.readdir)
|
|
106
|
-
.mockResolvedValueOnce(["0033-a.md", "readme.txt"])
|
|
107
|
-
.mockResolvedValueOnce([])
|
|
108
|
-
.mockResolvedValueOnce([])
|
|
109
|
-
.mockResolvedValueOnce([]);
|
|
110
|
-
|
|
111
|
-
vi.mocked(fs.readFile).mockResolvedValue("# 0033-a\n");
|
|
112
|
-
vi.mocked(fs.stat).mockResolvedValue({
|
|
113
|
-
mtime: new Date(),
|
|
114
|
-
mtimeMs: Date.now(),
|
|
115
|
-
} as ReturnType<typeof fs.stat> extends Promise<infer T> ? T : never);
|
|
116
|
-
|
|
117
|
-
const result = await listTickets();
|
|
118
|
-
expect(result).toHaveLength(1);
|
|
119
|
-
expect(result[0].id).toBe("0033-a");
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
describe("getTicketMarkdown", () => {
|
|
124
|
-
beforeEach(() => {
|
|
125
|
-
vi.mocked(fs.readdir)
|
|
126
|
-
.mockResolvedValueOnce(["0033-fix.md"])
|
|
127
|
-
.mockResolvedValueOnce([])
|
|
128
|
-
.mockResolvedValueOnce([])
|
|
129
|
-
.mockResolvedValueOnce([]);
|
|
130
|
-
vi.mocked(fs.readFile)
|
|
131
|
-
.mockResolvedValueOnce("# 0033-fix Fix it\n")
|
|
132
|
-
.mockResolvedValueOnce("# 0033-fix Fix it\n");
|
|
133
|
-
vi.mocked(fs.stat).mockResolvedValue({
|
|
134
|
-
mtime: new Date(),
|
|
135
|
-
mtimeMs: Date.now(),
|
|
136
|
-
} as ReturnType<typeof fs.stat> extends Promise<infer T> ? T : never);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("finds by id and returns markdown", async () => {
|
|
140
|
-
const result = await getTicketMarkdown("development-team", "0033-fix");
|
|
141
|
-
expect(result).not.toBeNull();
|
|
142
|
-
expect(result!.id).toBe("0033-fix");
|
|
143
|
-
expect(result!.markdown).toContain("Fix it");
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it("finds by number", async () => {
|
|
147
|
-
const result = await getTicketMarkdown("development-team", "33");
|
|
148
|
-
expect(result).not.toBeNull();
|
|
149
|
-
expect(result!.id).toBe("0033-fix");
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it("returns null when not found", async () => {
|
|
153
|
-
vi.mocked(fs.readdir).mockReset().mockResolvedValue([]);
|
|
154
|
-
const result = await getTicketMarkdown("development-team", "9999");
|
|
155
|
-
expect(result).toBeNull();
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { isRecord } from "../type-guards";
|
|
3
|
-
|
|
4
|
-
describe("type-guards", () => {
|
|
5
|
-
describe("isRecord", () => {
|
|
6
|
-
it("returns true for plain objects", () => {
|
|
7
|
-
expect(isRecord({})).toBe(true);
|
|
8
|
-
expect(isRecord({ a: 1 })).toBe(true);
|
|
9
|
-
});
|
|
10
|
-
it("returns false for arrays and primitives", () => {
|
|
11
|
-
expect(isRecord([])).toBe(false);
|
|
12
|
-
expect(isRecord(null)).toBe(false);
|
|
13
|
-
expect(isRecord(undefined)).toBe(false);
|
|
14
|
-
expect(isRecord("x")).toBe(false);
|
|
15
|
-
expect(isRecord(1)).toBe(false);
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
});
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { renderHook, act } from "@testing-library/react";
|
|
3
|
-
import { useSlugifiedId } from "../use-slugified-id";
|
|
4
|
-
|
|
5
|
-
describe("useSlugifiedId", () => {
|
|
6
|
-
const mockSetName = vi.fn();
|
|
7
|
-
const mockSetId = vi.fn();
|
|
8
|
-
const mockSetIdTouched = vi.fn();
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
mockSetName.mockClear();
|
|
12
|
-
mockSetId.mockClear();
|
|
13
|
-
mockSetIdTouched.mockClear();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
afterEach(() => {
|
|
17
|
-
vi.clearAllMocks();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("returns derivedId from slugified name", () => {
|
|
21
|
-
const { result } = renderHook(() =>
|
|
22
|
-
useSlugifiedId({
|
|
23
|
-
open: true,
|
|
24
|
-
name: "My Agent",
|
|
25
|
-
setName: mockSetName,
|
|
26
|
-
id: "",
|
|
27
|
-
setId: mockSetId,
|
|
28
|
-
idTouched: false,
|
|
29
|
-
setIdTouched: mockSetIdTouched,
|
|
30
|
-
})
|
|
31
|
-
);
|
|
32
|
-
expect(result.current.derivedId).toBe("my-agent");
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("returns effectiveId as derivedId when idTouched is false", () => {
|
|
36
|
-
const { result } = renderHook(() =>
|
|
37
|
-
useSlugifiedId({
|
|
38
|
-
open: true,
|
|
39
|
-
name: "Test Name",
|
|
40
|
-
setName: mockSetName,
|
|
41
|
-
id: "custom-id",
|
|
42
|
-
setId: mockSetId,
|
|
43
|
-
idTouched: false,
|
|
44
|
-
setIdTouched: mockSetIdTouched,
|
|
45
|
-
})
|
|
46
|
-
);
|
|
47
|
-
expect(result.current.effectiveId).toBe("test-name");
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("returns effectiveId as id when idTouched is true", () => {
|
|
51
|
-
const { result } = renderHook(() =>
|
|
52
|
-
useSlugifiedId({
|
|
53
|
-
open: true,
|
|
54
|
-
name: "Test Name",
|
|
55
|
-
setName: mockSetName,
|
|
56
|
-
id: "custom-id",
|
|
57
|
-
setId: mockSetId,
|
|
58
|
-
idTouched: true,
|
|
59
|
-
setIdTouched: mockSetIdTouched,
|
|
60
|
-
})
|
|
61
|
-
);
|
|
62
|
-
expect(result.current.effectiveId).toBe("custom-id");
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("syncs derivedId to id when open and !idTouched", () => {
|
|
66
|
-
renderHook(() =>
|
|
67
|
-
useSlugifiedId({
|
|
68
|
-
open: true,
|
|
69
|
-
name: "Hello World",
|
|
70
|
-
setName: mockSetName,
|
|
71
|
-
id: "",
|
|
72
|
-
setId: mockSetId,
|
|
73
|
-
idTouched: false,
|
|
74
|
-
setIdTouched: mockSetIdTouched,
|
|
75
|
-
})
|
|
76
|
-
);
|
|
77
|
-
expect(mockSetId).toHaveBeenCalledWith("hello-world");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("resets form when modal closes (open becomes false)", () => {
|
|
81
|
-
const { rerender } = renderHook(
|
|
82
|
-
({ open }) =>
|
|
83
|
-
useSlugifiedId({
|
|
84
|
-
open,
|
|
85
|
-
name: "Test",
|
|
86
|
-
setName: mockSetName,
|
|
87
|
-
id: "test-id",
|
|
88
|
-
setId: mockSetId,
|
|
89
|
-
idTouched: true,
|
|
90
|
-
setIdTouched: mockSetIdTouched,
|
|
91
|
-
}),
|
|
92
|
-
{ initialProps: { open: true } }
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
act(() => {
|
|
96
|
-
rerender({ open: false });
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
expect(mockSetIdTouched).toHaveBeenCalledWith(false);
|
|
100
|
-
expect(mockSetName).toHaveBeenCalledWith("");
|
|
101
|
-
expect(mockSetId).toHaveBeenCalledWith("");
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("uses custom slugify when provided", () => {
|
|
105
|
-
const customSlugify = (s: string) => s.toUpperCase().replace(/\s/g, "_");
|
|
106
|
-
const { result } = renderHook(() =>
|
|
107
|
-
useSlugifiedId({
|
|
108
|
-
open: true,
|
|
109
|
-
name: "foo bar",
|
|
110
|
-
setName: mockSetName,
|
|
111
|
-
id: "",
|
|
112
|
-
setId: mockSetId,
|
|
113
|
-
idTouched: false,
|
|
114
|
-
setIdTouched: mockSetIdTouched,
|
|
115
|
-
slugify: customSlugify,
|
|
116
|
-
})
|
|
117
|
-
);
|
|
118
|
-
expect(result.current.derivedId).toBe("FOO_BAR");
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
|
|
3
|
-
export function parseTeamRoleWorkspace(ws: string):
|
|
4
|
-
| { kind: "teamRole"; teamDir: string; teamId: string; roleDir: string; role: string }
|
|
5
|
-
| { kind: "other" } {
|
|
6
|
-
const normalized = ws.replace(/\\/g, "/");
|
|
7
|
-
const m = normalized.match(/^(.*\/workspace-([^\/]+))\/roles\/([^\/]+)\/?$/);
|
|
8
|
-
if (!m) return { kind: "other" };
|
|
9
|
-
const teamDir = m[1];
|
|
10
|
-
const teamId = m[2];
|
|
11
|
-
const role = m[3];
|
|
12
|
-
const roleDir = path.join(teamDir, "roles", role);
|
|
13
|
-
return { kind: "teamRole", teamDir, teamId, roleDir, role };
|
|
14
|
-
}
|
package/src/lib/agents.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { runOpenClaw } from "./openclaw";
|
|
2
|
-
|
|
3
|
-
export type AgentListItem = {
|
|
4
|
-
id: string;
|
|
5
|
-
identityName?: string;
|
|
6
|
-
workspace?: string;
|
|
7
|
-
model?: string;
|
|
8
|
-
isDefault?: boolean;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export async function resolveAgentWorkspace(agentId: string): Promise<string> {
|
|
12
|
-
const { stdout } = await runOpenClaw(["agents", "list", "--json"]);
|
|
13
|
-
const list = JSON.parse(stdout) as AgentListItem[];
|
|
14
|
-
const agent = list.find((a) => a.id === agentId);
|
|
15
|
-
if (!agent?.workspace) throw new Error(`Agent workspace not found for ${agentId}`);
|
|
16
|
-
return agent.workspace;
|
|
17
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { NextResponse } from "next/server";
|
|
4
|
-
|
|
5
|
-
/** Parses JSON body; returns 400 on parse error. */
|
|
6
|
-
export async function parseJsonBody(req: Request): Promise<{ body: Record<string, unknown> } | NextResponse> {
|
|
7
|
-
let body: unknown;
|
|
8
|
-
try {
|
|
9
|
-
body = await req.json();
|
|
10
|
-
} catch {
|
|
11
|
-
return NextResponse.json({ ok: false, error: "Invalid JSON" }, { status: 400 });
|
|
12
|
-
}
|
|
13
|
-
const o = body && typeof body === "object" ? (body as Record<string, unknown>) : {};
|
|
14
|
-
return { body: o };
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Returns NextResponse.json({ ok: true, ...rest }) from a storage result with ok field. */
|
|
18
|
-
export function jsonOkRest<T extends { ok: boolean }>(r: T): NextResponse {
|
|
19
|
-
const rest = Object.fromEntries(Object.entries(r as Record<string, unknown>).filter(([k]) => k !== "ok"));
|
|
20
|
-
return NextResponse.json({ ok: true, ...rest });
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
import { resolveAgentWorkspace } from "@/lib/agents";
|
|
24
|
-
import { errorMessage } from "@/lib/errors";
|
|
25
|
-
|
|
26
|
-
/** Runs async storage fn and returns jsonOkRest on success, or 500 on error. */
|
|
27
|
-
export async function withStorageError<T extends { ok: boolean }>(
|
|
28
|
-
fn: () => Promise<T>
|
|
29
|
-
): Promise<NextResponse> {
|
|
30
|
-
try {
|
|
31
|
-
return jsonOkRest(await fn());
|
|
32
|
-
} catch (err: unknown) {
|
|
33
|
-
return NextResponse.json({ ok: false, error: errorMessage(err) }, { status: 500 });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
import { readOpenClawConfig, teamDirFromBaseWorkspace } from "@/lib/paths";
|
|
37
|
-
|
|
38
|
-
export type TeamContext = { teamId: string; teamDir: string };
|
|
39
|
-
|
|
40
|
-
/** Error response for install-skill openclaw failures. Use when runOpenClaw returns !ok. */
|
|
41
|
-
export function installSkillErrorResponse(
|
|
42
|
-
args: string[],
|
|
43
|
-
res: { stdout?: string; stderr?: string; exitCode?: number },
|
|
44
|
-
extra?: Record<string, unknown>
|
|
45
|
-
): NextResponse {
|
|
46
|
-
const stdout = res.stdout?.trim();
|
|
47
|
-
const stderr = res.stderr?.trim();
|
|
48
|
-
return NextResponse.json(
|
|
49
|
-
{
|
|
50
|
-
ok: false,
|
|
51
|
-
error: stderr || stdout || `openclaw ${args.join(" ")} failed (exit=${res.exitCode})`,
|
|
52
|
-
stdout: res.stdout,
|
|
53
|
-
stderr: res.stderr,
|
|
54
|
-
...extra,
|
|
55
|
-
},
|
|
56
|
-
{ status: 500 }
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function resolveTeamContext(teamId: string): Promise<TeamContext | NextResponse> {
|
|
61
|
-
if (!teamId) return NextResponse.json({ ok: false, error: "teamId is required" }, { status: 400 });
|
|
62
|
-
|
|
63
|
-
const cfg = await readOpenClawConfig();
|
|
64
|
-
const baseWorkspace = String(cfg.agents?.defaults?.workspace ?? "").trim();
|
|
65
|
-
if (!baseWorkspace) {
|
|
66
|
-
return NextResponse.json({ ok: false, error: "agents.defaults.workspace not set" }, { status: 500 });
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const teamDir = teamDirFromBaseWorkspace(baseWorkspace, teamId);
|
|
70
|
-
return { teamId, teamDir };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** Resolves teamId and teamDir from URL search params. Returns error response if invalid. */
|
|
74
|
-
export async function getTeamContextFromQuery(req: Request): Promise<TeamContext | NextResponse> {
|
|
75
|
-
const { searchParams } = new URL(req.url);
|
|
76
|
-
const teamId = String(searchParams.get("teamId") ?? "").trim();
|
|
77
|
-
return resolveTeamContext(teamId);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** Runs handler with team context from query; returns error response if context invalid. */
|
|
81
|
-
export async function withTeamContextFromQuery(
|
|
82
|
-
req: Request,
|
|
83
|
-
handler: (ctx: TeamContext) => Promise<NextResponse>
|
|
84
|
-
): Promise<NextResponse> {
|
|
85
|
-
const ctx = await getTeamContextFromQuery(req);
|
|
86
|
-
if (ctx instanceof NextResponse) return ctx;
|
|
87
|
-
return handler(ctx);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Resolves teamId and teamDir from parsed body. Caller must parse req.json() first. */
|
|
91
|
-
export async function getTeamContextFromBody(body: { teamId?: string }): Promise<TeamContext | NextResponse> {
|
|
92
|
-
const teamId = String(body.teamId ?? "").trim();
|
|
93
|
-
return resolveTeamContext(teamId);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export type AgentContext = { agentId: string; ws: string };
|
|
97
|
-
|
|
98
|
-
/** Resolves agentId and workspace from URL search params. Returns error response if invalid. */
|
|
99
|
-
export async function getAgentContextFromQuery(req: Request): Promise<AgentContext | NextResponse> {
|
|
100
|
-
const { searchParams } = new URL(req.url);
|
|
101
|
-
const agentId = String(searchParams.get("agentId") ?? "").trim();
|
|
102
|
-
return resolveAgentContext(agentId);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** Resolves agentId and workspace from parsed body. Caller must parse req.json() first. */
|
|
106
|
-
export async function getAgentContextFromBody(body: { agentId?: string }): Promise<AgentContext | NextResponse> {
|
|
107
|
-
const agentId = String(body.agentId ?? "").trim();
|
|
108
|
-
return resolveAgentContext(agentId);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function resolveAgentContext(agentId: string): Promise<AgentContext | NextResponse> {
|
|
112
|
-
if (!agentId) return NextResponse.json({ ok: false, error: "agentId is required" }, { status: 400 });
|
|
113
|
-
try {
|
|
114
|
-
const ws = await resolveAgentWorkspace(agentId);
|
|
115
|
-
return { agentId, ws };
|
|
116
|
-
} catch (e: unknown) {
|
|
117
|
-
return NextResponse.json({ ok: false, error: errorMessage(e) }, { status: 404 });
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export type FileCandidate = { name: string; required: boolean; rationale: string };
|
|
122
|
-
|
|
123
|
-
/** Lists workspace files with presence/size info. */
|
|
124
|
-
export async function listWorkspaceFiles(
|
|
125
|
-
baseDir: string,
|
|
126
|
-
candidates: FileCandidate[]
|
|
127
|
-
): Promise<
|
|
128
|
-
Array<{
|
|
129
|
-
name: string;
|
|
130
|
-
required: boolean;
|
|
131
|
-
rationale: string;
|
|
132
|
-
path: string;
|
|
133
|
-
missing: boolean;
|
|
134
|
-
size?: number;
|
|
135
|
-
updatedAtMs?: number;
|
|
136
|
-
}>
|
|
137
|
-
> {
|
|
138
|
-
return Promise.all(
|
|
139
|
-
candidates.map(async (c) => {
|
|
140
|
-
const p = path.join(baseDir, c.name);
|
|
141
|
-
try {
|
|
142
|
-
const st = await fs.stat(p);
|
|
143
|
-
return {
|
|
144
|
-
name: c.name,
|
|
145
|
-
required: c.required,
|
|
146
|
-
rationale: c.rationale,
|
|
147
|
-
path: p,
|
|
148
|
-
missing: false,
|
|
149
|
-
size: st.size,
|
|
150
|
-
updatedAtMs: st.mtimeMs,
|
|
151
|
-
};
|
|
152
|
-
} catch {
|
|
153
|
-
return { name: c.name, required: c.required, rationale: c.rationale, path: p, missing: true };
|
|
154
|
-
}
|
|
155
|
-
})
|
|
156
|
-
);
|
|
157
|
-
}
|
package/src/lib/cron.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared shape for cron job objects returned by the gateway cron tool.
|
|
3
|
-
* Different callers may receive slightly different structures; these helpers normalize access.
|
|
4
|
-
*/
|
|
5
|
-
export type CronJobShape = {
|
|
6
|
-
id?: unknown;
|
|
7
|
-
jobId?: unknown;
|
|
8
|
-
name?: unknown;
|
|
9
|
-
enabled?: unknown;
|
|
10
|
-
state?: { enabled?: unknown };
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/** Typed cron job for UI display. API returns jobs that match this structure. */
|
|
14
|
-
export type CronJob = {
|
|
15
|
-
id: string;
|
|
16
|
-
name?: string;
|
|
17
|
-
enabled?: boolean;
|
|
18
|
-
schedule?: { kind?: string; expr?: string; everyMs?: number };
|
|
19
|
-
state?: { nextRunAtMs?: number };
|
|
20
|
-
agentId?: string;
|
|
21
|
-
sessionTarget?: string;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export function cronJobId(j: CronJobShape): string {
|
|
25
|
-
return String(j.id ?? j.jobId ?? "").trim();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function cronJobLabel(j: CronJobShape): string {
|
|
29
|
-
return String(j.name ?? j.id ?? j.jobId ?? "(unnamed)");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function fmtCronSchedule(s?: CronJob["schedule"]): string {
|
|
33
|
-
if (!s) return "";
|
|
34
|
-
if (s.kind === "cron" && s.expr) return s.expr;
|
|
35
|
-
if (s.kind === "every" && s.everyMs) {
|
|
36
|
-
const mins = Math.round(s.everyMs / 60000);
|
|
37
|
-
return mins >= 60 ? `every ${Math.round(mins / 60)}h` : `every ${mins}m`;
|
|
38
|
-
}
|
|
39
|
-
return s.kind ?? "";
|
|
40
|
-
}
|
package/src/lib/editor-utils.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export type FileListEntry = {
|
|
2
|
-
name: string;
|
|
3
|
-
missing: boolean;
|
|
4
|
-
required: boolean;
|
|
5
|
-
rationale?: string;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export function normalizeFileListEntries(files: unknown[]): FileListEntry[] {
|
|
9
|
-
return files.map((f) => {
|
|
10
|
-
const entry = f as { name?: unknown; missing?: unknown; required?: unknown; rationale?: unknown };
|
|
11
|
-
return {
|
|
12
|
-
name: String(entry.name ?? ""),
|
|
13
|
-
missing: Boolean(entry.missing),
|
|
14
|
-
required: Boolean(entry.required),
|
|
15
|
-
rationale: typeof entry.rationale === "string" ? entry.rationale : undefined,
|
|
16
|
-
};
|
|
17
|
-
});
|
|
18
|
-
}
|
package/src/lib/errors.ts
DELETED
package/src/lib/exec.ts
DELETED
package/src/lib/fetch-json.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/** Fetches multiple URLs in parallel with cache: "no-store". */
|
|
2
|
-
export async function fetchAll(urls: string[]): Promise<Response[]> {
|
|
3
|
-
return Promise.all(urls.map((url) => fetch(url, { cache: "no-store" })));
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Fetches JSON and throws on !res.ok.
|
|
8
|
-
* Use errorMessage(e) in catch blocks.
|
|
9
|
-
*/
|
|
10
|
-
export async function fetchJson<T = unknown>(url: string, opts?: RequestInit): Promise<T> {
|
|
11
|
-
const res = await fetch(url, opts);
|
|
12
|
-
const json = (await res.json()) as { error?: string };
|
|
13
|
-
if (!res.ok) throw new Error(json.error ?? "Request failed");
|
|
14
|
-
return json as T;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Fetches JSON and returns result with status. Use when you need to handle specific
|
|
19
|
-
* HTTP status codes (e.g. 409) differently from generic errors.
|
|
20
|
-
*/
|
|
21
|
-
export async function fetchJsonWithStatus<T = unknown>(
|
|
22
|
-
url: string,
|
|
23
|
-
opts?: RequestInit
|
|
24
|
-
): Promise<{ ok: true; data: T } | { ok: false; status: number; error: string }> {
|
|
25
|
-
const res = await fetch(url, opts);
|
|
26
|
-
const json = (await res.json()) as { error?: string };
|
|
27
|
-
if (res.ok) return { ok: true, data: json as T };
|
|
28
|
-
return { ok: false, status: res.status, error: json.error ?? "Request failed" };
|
|
29
|
-
}
|