the-campfire 0.1.0
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/bin/cli.ts +182 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/MermaidDiagram-By1J3Whj.js +2 -0
- package/dist/assets/_basePickBy-Dq6gnD-p.js +1 -0
- package/dist/assets/_baseUniq-SpgN6cGc.js +1 -0
- package/dist/assets/arc-D9SEA8dX.js +1 -0
- package/dist/assets/architectureDiagram-VXUJARFQ-CoTcwLeR.js +36 -0
- package/dist/assets/blockDiagram-VD42YOAC-Dvc63voV.js +122 -0
- package/dist/assets/c4Diagram-YG6GDRKO-tuDcG4_4.js +10 -0
- package/dist/assets/channel-CxhcHn7S.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-x1hChYu9.js +1 -0
- package/dist/assets/chunk-55IACEB6-BX6HWnWP.js +1 -0
- package/dist/assets/chunk-B4BG7PRW-B4IlF03G.js +165 -0
- package/dist/assets/chunk-DI55MBZ5-TpYb5rLg.js +220 -0
- package/dist/assets/chunk-FMBD7UC4-DZX4LUPx.js +15 -0
- package/dist/assets/chunk-QN33PNHL-CuQs0NDY.js +1 -0
- package/dist/assets/chunk-QZHKN3VN-CygWwVx8.js +1 -0
- package/dist/assets/chunk-TZMSLE5B-CtI-IdKV.js +1 -0
- package/dist/assets/classDiagram-2ON5EDUG-BM0LptJF.js +1 -0
- package/dist/assets/classDiagram-v2-WZHVMYZB-BM0LptJF.js +1 -0
- package/dist/assets/clone-C6kt1rE7.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-PdySs991.js +1 -0
- package/dist/assets/cytoscape.esm-BQaXIfA_.js +331 -0
- package/dist/assets/dagre-6UL2VRFP-J0SxEiEu.js +4 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/assets/diagram-PSM6KHXK-CW0HLXaH.js +24 -0
- package/dist/assets/diagram-QEK2KX5R-DADHF-k4.js +43 -0
- package/dist/assets/diagram-S2PKOQOG-9S0-PC3S.js +24 -0
- package/dist/assets/erDiagram-Q2GNP2WA-BEHAiY_q.js +60 -0
- package/dist/assets/flowDiagram-NV44I4VS-Cu6n9Xnd.js +162 -0
- package/dist/assets/ganttDiagram-JELNMOA3-Cr_BHryu.js +267 -0
- package/dist/assets/gitGraphDiagram-V2S2FVAM-D5pT35v5.js +65 -0
- package/dist/assets/graph-DKRbrxcs.js +1 -0
- package/dist/assets/index-Byjg9zgI.css +32 -0
- package/dist/assets/index-Cwj3m0hT.js +284 -0
- package/dist/assets/infoDiagram-HS3SLOUP-BaJlrgKV.js +2 -0
- package/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dist/assets/journeyDiagram-XKPGCS4Q-CXC4-DJv.js +139 -0
- package/dist/assets/kanban-definition-3W4ZIXB7-BtHBu9cx.js +89 -0
- package/dist/assets/katex-BlHpptmG.js +261 -0
- package/dist/assets/layout-DnxSwLDJ.js +1 -0
- package/dist/assets/linear-CWbbIT8l.js +1 -0
- package/dist/assets/mermaid.core-DXn1cFAK.js +256 -0
- package/dist/assets/mindmap-definition-VGOIOE7T-BR8br1hs.js +68 -0
- package/dist/assets/ordinal-BENe2yWM.js +1 -0
- package/dist/assets/pieDiagram-ADFJNKIX-B7RmXV7I.js +30 -0
- package/dist/assets/quadrantDiagram-AYHSOK5B-DK0FhyV_.js +7 -0
- package/dist/assets/requirementDiagram-UZGBJVZJ-3zAGxSpL.js +64 -0
- package/dist/assets/sankeyDiagram-TZEHDZUN-DZppKtjc.js +10 -0
- package/dist/assets/sequenceDiagram-WL72ISMW-D1x3r3wV.js +145 -0
- package/dist/assets/stateDiagram-FKZM4ZOC-2ByD08Vq.js +1 -0
- package/dist/assets/stateDiagram-v2-4FDKWEC3-Vm1cYjgV.js +1 -0
- package/dist/assets/timeline-definition-IT6M3QCI-l5S35biR.js +61 -0
- package/dist/assets/treemap-GDKQZRPO-DIPadPiF.js +162 -0
- package/dist/assets/xychartDiagram-PRI3JC2R-9GUvwS2b.js +7 -0
- package/dist/favicon.svg +3 -0
- package/dist/fonts/Geist-Variable.woff2 +0 -0
- package/dist/fonts/GeistMono-Variable.woff2 +0 -0
- package/dist/icon-192.png +0 -0
- package/dist/icon-512.png +0 -0
- package/dist/index.html +33 -0
- package/dist/logo-codex.svg +14 -0
- package/dist/logo.svg +3 -0
- package/dist/manifest.json +26 -0
- package/dist/og-image.png +0 -0
- package/dist/sw.js +130 -0
- package/package.json +92 -0
- package/server/ADAPTERS.md +121 -0
- package/server/adapter-registry-types.ts +28 -0
- package/server/adapter-registry.ts +293 -0
- package/server/adapter-types.ts +51 -0
- package/server/agent-executor.ts +249 -0
- package/server/agent-mcp-bridge.ts +148 -0
- package/server/agent-mcp-stdio.ts +171 -0
- package/server/agent-mcp-tools.ts +76 -0
- package/server/agent-store.ts +183 -0
- package/server/agent-types.ts +79 -0
- package/server/aider-adapter.ts +525 -0
- package/server/auth.ts +153 -0
- package/server/auto-namer.ts +132 -0
- package/server/capability-discovery.ts +431 -0
- package/server/claude-container-auth.ts +40 -0
- package/server/claude-stdio-adapter.ts +452 -0
- package/server/clawhub-export.ts +218 -0
- package/server/cli-launcher.ts +1613 -0
- package/server/codex-adapter.ts +1987 -0
- package/server/codex-container-auth.ts +40 -0
- package/server/codex-home.ts +26 -0
- package/server/collective-intelligence.ts +401 -0
- package/server/commands-discovery.ts +160 -0
- package/server/constants.ts +5 -0
- package/server/container-manager.ts +328 -0
- package/server/cron-scheduler.ts +243 -0
- package/server/cron-store.ts +149 -0
- package/server/cron-types.ts +63 -0
- package/server/deliberation-engine.ts +323 -0
- package/server/embedding.ts +89 -0
- package/server/env-manager.ts +146 -0
- package/server/environment-detector.ts +106 -0
- package/server/environment-rules.ts +106 -0
- package/server/gallery-store.ts +216 -0
- package/server/gallery-types.ts +52 -0
- package/server/gallery-votes.ts +101 -0
- package/server/git-utils.ts +384 -0
- package/server/github-pr.ts +379 -0
- package/server/goose-adapter.ts +929 -0
- package/server/image-pull-manager.ts +144 -0
- package/server/index.ts +363 -0
- package/server/linear-project-manager.ts +78 -0
- package/server/moltbook-client.ts +160 -0
- package/server/openclaw-adapter.ts +916 -0
- package/server/openclaw-channel/channel.ts +188 -0
- package/server/openclaw-channel/index.ts +68 -0
- package/server/openclaw-channel/package.json +19 -0
- package/server/openclaw-channel/types.ts +93 -0
- package/server/opencode-adapter.ts +951 -0
- package/server/openhands-adapter.ts +775 -0
- package/server/orchestrator-executor.ts +184 -0
- package/server/orchestrator-store.ts +106 -0
- package/server/orchestrator-types.ts +86 -0
- package/server/path-resolver.ts +168 -0
- package/server/pr-poller.ts +162 -0
- package/server/proactive-keepalive.ts +121 -0
- package/server/prompt-manager.ts +103 -0
- package/server/protocol-monitor.ts +197 -0
- package/server/race-controller.ts +295 -0
- package/server/race-store.ts +82 -0
- package/server/recorder.ts +359 -0
- package/server/recording-hub/compat-validator.ts +122 -0
- package/server/recording-hub/diagnostics.ts +289 -0
- package/server/recording-hub/hub-routes.ts +146 -0
- package/server/recording-hub/hub-store.ts +224 -0
- package/server/replay.ts +78 -0
- package/server/routes/adapter-routes.ts +31 -0
- package/server/routes/agent-mcp-routes.ts +35 -0
- package/server/routes/agent-routes.ts +185 -0
- package/server/routes/auth-routes.ts +101 -0
- package/server/routes/ci-routes.ts +165 -0
- package/server/routes/commands-routes.ts +94 -0
- package/server/routes/cron-routes.ts +100 -0
- package/server/routes/env-routes.ts +50 -0
- package/server/routes/folder-routes.ts +56 -0
- package/server/routes/fs-routes.ts +403 -0
- package/server/routes/gallery-routes.ts +410 -0
- package/server/routes/git-routes.ts +108 -0
- package/server/routes/index.ts +62 -0
- package/server/routes/linear-routes.ts +254 -0
- package/server/routes/monitor-routes.ts +11 -0
- package/server/routes/orchestrator-routes.ts +210 -0
- package/server/routes/prompt-routes.ts +47 -0
- package/server/routes/race-routes.ts +103 -0
- package/server/routes/recording-routes.ts +101 -0
- package/server/routes/route-deps.ts +32 -0
- package/server/routes/session-routes.ts +560 -0
- package/server/routes/settings-routes.ts +108 -0
- package/server/routes/skills-routes.ts +52 -0
- package/server/routes/system-routes.ts +243 -0
- package/server/routes/webhook-routes.ts +135 -0
- package/server/routes.ts +44 -0
- package/server/security-middleware.ts +100 -0
- package/server/semantic-memory.ts +389 -0
- package/server/service.ts +733 -0
- package/server/session-folders.ts +107 -0
- package/server/session-git-info.ts +89 -0
- package/server/session-linear-issues.ts +94 -0
- package/server/session-names.ts +67 -0
- package/server/session-store.ts +169 -0
- package/server/session-types.ts +415 -0
- package/server/settings-manager.ts +120 -0
- package/server/shared-context.ts +401 -0
- package/server/skills-manager.ts +250 -0
- package/server/sub-session-manager.ts +263 -0
- package/server/terminal-manager.ts +185 -0
- package/server/update-checker.ts +114 -0
- package/server/usage-limits.ts +192 -0
- package/server/webhook-manager.ts +228 -0
- package/server/webhook-store.ts +168 -0
- package/server/webhook-types.ts +66 -0
- package/server/worktree-tracker.ts +84 -0
- package/server/ws-bridge.ts +2322 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { Hono } from "hono";
|
|
2
|
+
import type { RouteDeps } from "./route-deps.js";
|
|
3
|
+
import { discoverCommandsAndSkills, readCommandContent } from "../commands-discovery.js";
|
|
4
|
+
|
|
5
|
+
// Cache for CLI-reported slash commands (populated on first request)
|
|
6
|
+
let cachedCliCommands: string[] | null = null;
|
|
7
|
+
let fetchingCliCommands = false;
|
|
8
|
+
|
|
9
|
+
function findCommandsFromSessions(wsBridge: RouteDeps["wsBridge"]): string[] | null {
|
|
10
|
+
const sessions = wsBridge.getAllSessions();
|
|
11
|
+
for (const session of sessions) {
|
|
12
|
+
if (session.slash_commands && session.slash_commands.length > 0) {
|
|
13
|
+
return session.slash_commands;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function fetchCommandsViaTempSession(launcher: RouteDeps["launcher"], wsBridge: RouteDeps["wsBridge"]): Promise<string[]> {
|
|
20
|
+
const tempSession = launcher.launch({
|
|
21
|
+
permissionMode: "default",
|
|
22
|
+
cwd: process.cwd(),
|
|
23
|
+
backendType: "claude",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const startTime = Date.now();
|
|
27
|
+
let commands: string[] = [];
|
|
28
|
+
while (Date.now() - startTime < 30_000) {
|
|
29
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
30
|
+
const allSessions = wsBridge.getAllSessions();
|
|
31
|
+
const tempState = allSessions.find((s) => s.session_id === tempSession.sessionId);
|
|
32
|
+
if (tempState?.slash_commands && tempState.slash_commands.length > 0) {
|
|
33
|
+
commands = tempState.slash_commands;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await launcher.kill(tempSession.sessionId).catch(() => {});
|
|
39
|
+
return commands;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function registerCommandsRoutes(api: Hono, deps: RouteDeps): void {
|
|
43
|
+
const { launcher, wsBridge } = deps;
|
|
44
|
+
|
|
45
|
+
// Discover custom commands and skills for a given working directory
|
|
46
|
+
api.get("/commands", (c) => {
|
|
47
|
+
const cwd = c.req.query("cwd") || undefined;
|
|
48
|
+
return c.json(discoverCommandsAndSkills(cwd));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Read a specific command's full content
|
|
52
|
+
api.get("/commands/read", (c) => {
|
|
53
|
+
const path = c.req.query("path");
|
|
54
|
+
if (!path) return c.json({ error: "path is required" }, 400);
|
|
55
|
+
const content = readCommandContent(path);
|
|
56
|
+
if (content === null) return c.json({ error: "File not found" }, 404);
|
|
57
|
+
return c.json({ path, content });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Get slash commands from the CLI (dynamic, not hardcoded).
|
|
61
|
+
// Tries existing sessions first, then spins up a temporary one.
|
|
62
|
+
api.get("/commands/slash", async (c) => {
|
|
63
|
+
// 1. Try existing sessions
|
|
64
|
+
const fromSession = findCommandsFromSessions(wsBridge);
|
|
65
|
+
if (fromSession) {
|
|
66
|
+
cachedCliCommands = fromSession;
|
|
67
|
+
return c.json({ commands: fromSession, source: "session" });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 2. Return cache
|
|
71
|
+
if (cachedCliCommands) return c.json({ commands: cachedCliCommands, source: "cache" });
|
|
72
|
+
|
|
73
|
+
// 3. Wait if another request is already fetching
|
|
74
|
+
if (fetchingCliCommands) {
|
|
75
|
+
for (let i = 0; i < 60; i++) {
|
|
76
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
77
|
+
if (cachedCliCommands) return c.json({ commands: cachedCliCommands, source: "cache" });
|
|
78
|
+
}
|
|
79
|
+
return c.json({ commands: [], source: "timeout" });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 4. Spin up temp session
|
|
83
|
+
fetchingCliCommands = true;
|
|
84
|
+
try {
|
|
85
|
+
const commands = await fetchCommandsViaTempSession(launcher, wsBridge);
|
|
86
|
+
if (commands.length > 0) cachedCliCommands = commands;
|
|
87
|
+
return c.json({ commands, source: commands.length > 0 ? "fetched" : "empty" });
|
|
88
|
+
} catch {
|
|
89
|
+
return c.json({ commands: [], source: "error" });
|
|
90
|
+
} finally {
|
|
91
|
+
fetchingCliCommands = false;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Hono } from "hono";
|
|
2
|
+
import type { RouteDeps } from "./route-deps.js";
|
|
3
|
+
import * as cronStore from "../cron-store.js";
|
|
4
|
+
|
|
5
|
+
export function registerCronRoutes(api: Hono, deps: RouteDeps): void {
|
|
6
|
+
const { cronScheduler } = deps;
|
|
7
|
+
|
|
8
|
+
api.get("/cron/jobs", (c) => {
|
|
9
|
+
const jobs = cronStore.listJobs();
|
|
10
|
+
const enriched = jobs.map((j) => ({
|
|
11
|
+
...j,
|
|
12
|
+
nextRunAt: cronScheduler?.getNextRunTime(j.id)?.getTime() ?? null,
|
|
13
|
+
}));
|
|
14
|
+
return c.json(enriched);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
api.get("/cron/jobs/:id", (c) => {
|
|
18
|
+
const job = cronStore.getJob(c.req.param("id"));
|
|
19
|
+
if (!job) return c.json({ error: "Job not found" }, 404);
|
|
20
|
+
return c.json({
|
|
21
|
+
...job,
|
|
22
|
+
nextRunAt: cronScheduler?.getNextRunTime(job.id)?.getTime() ?? null,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
api.post("/cron/jobs", async (c) => {
|
|
27
|
+
const body = await c.req.json().catch(() => ({}));
|
|
28
|
+
try {
|
|
29
|
+
const job = cronStore.createJob({
|
|
30
|
+
name: body.name || "",
|
|
31
|
+
prompt: body.prompt || "",
|
|
32
|
+
schedule: body.schedule || "",
|
|
33
|
+
recurring: body.recurring ?? true,
|
|
34
|
+
backendType: body.backendType || "claude",
|
|
35
|
+
model: body.model || "",
|
|
36
|
+
cwd: body.cwd || "",
|
|
37
|
+
envSlug: body.envSlug,
|
|
38
|
+
enabled: body.enabled ?? true,
|
|
39
|
+
permissionMode: body.permissionMode || "bypassPermissions",
|
|
40
|
+
codexInternetAccess: body.codexInternetAccess,
|
|
41
|
+
});
|
|
42
|
+
if (job.enabled) cronScheduler?.scheduleJob(job);
|
|
43
|
+
return c.json(job, 201);
|
|
44
|
+
} catch (e: unknown) {
|
|
45
|
+
return c.json({ error: e instanceof Error ? e.message : String(e) }, 400);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
api.put("/cron/jobs/:id", async (c) => {
|
|
50
|
+
const id = c.req.param("id");
|
|
51
|
+
const body = await c.req.json().catch(() => ({}));
|
|
52
|
+
try {
|
|
53
|
+
const allowed: Record<string, unknown> = {};
|
|
54
|
+
for (const key of ["name", "prompt", "schedule", "recurring", "backendType", "model", "cwd", "envSlug", "enabled", "permissionMode", "codexInternetAccess"] as const) {
|
|
55
|
+
if (key in body) allowed[key] = body[key];
|
|
56
|
+
}
|
|
57
|
+
const job = cronStore.updateJob(id, allowed);
|
|
58
|
+
if (!job) return c.json({ error: "Job not found" }, 404);
|
|
59
|
+
if (job.id !== id) cronScheduler?.stopJob(id);
|
|
60
|
+
cronScheduler?.scheduleJob(job);
|
|
61
|
+
return c.json(job);
|
|
62
|
+
} catch (e: unknown) {
|
|
63
|
+
return c.json({ error: e instanceof Error ? e.message : String(e) }, 400);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
api.delete("/cron/jobs/:id", (c) => {
|
|
68
|
+
const id = c.req.param("id");
|
|
69
|
+
cronScheduler?.stopJob(id);
|
|
70
|
+
const deleted = cronStore.deleteJob(id);
|
|
71
|
+
if (!deleted) return c.json({ error: "Job not found" }, 404);
|
|
72
|
+
return c.json({ ok: true });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
api.post("/cron/jobs/:id/toggle", (c) => {
|
|
76
|
+
const id = c.req.param("id");
|
|
77
|
+
const job = cronStore.getJob(id);
|
|
78
|
+
if (!job) return c.json({ error: "Job not found" }, 404);
|
|
79
|
+
const updated = cronStore.updateJob(id, { enabled: !job.enabled });
|
|
80
|
+
if (updated?.enabled) {
|
|
81
|
+
cronScheduler?.scheduleJob(updated);
|
|
82
|
+
} else {
|
|
83
|
+
cronScheduler?.stopJob(id);
|
|
84
|
+
}
|
|
85
|
+
return c.json(updated);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
api.post("/cron/jobs/:id/run", (c) => {
|
|
89
|
+
const id = c.req.param("id");
|
|
90
|
+
const job = cronStore.getJob(id);
|
|
91
|
+
if (!job) return c.json({ error: "Job not found" }, 404);
|
|
92
|
+
cronScheduler?.executeJobManually(id);
|
|
93
|
+
return c.json({ ok: true, message: "Job triggered" });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
api.get("/cron/jobs/:id/executions", (c) => {
|
|
97
|
+
const id = c.req.param("id");
|
|
98
|
+
return c.json(cronScheduler?.getExecutions(id) ?? []);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Hono } from "hono";
|
|
2
|
+
import type { RouteDeps } from "./route-deps.js";
|
|
3
|
+
import * as envManager from "../env-manager.js";
|
|
4
|
+
|
|
5
|
+
export function registerEnvRoutes(api: Hono, _deps: RouteDeps): void {
|
|
6
|
+
api.get("/envs", (c) => {
|
|
7
|
+
try {
|
|
8
|
+
return c.json(envManager.listEnvs());
|
|
9
|
+
} catch (e: unknown) {
|
|
10
|
+
return c.json({ error: e instanceof Error ? e.message : String(e) }, 500);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
api.get("/envs/:slug", (c) => {
|
|
15
|
+
const env = envManager.getEnv(c.req.param("slug"));
|
|
16
|
+
if (!env) return c.json({ error: "Environment not found" }, 404);
|
|
17
|
+
return c.json(env);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
api.post("/envs", async (c) => {
|
|
21
|
+
const body = await c.req.json().catch(() => ({}));
|
|
22
|
+
try {
|
|
23
|
+
const env = envManager.createEnv(body.name, body.variables || {});
|
|
24
|
+
return c.json(env, 201);
|
|
25
|
+
} catch (e: unknown) {
|
|
26
|
+
return c.json({ error: e instanceof Error ? e.message : String(e) }, 400);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
api.put("/envs/:slug", async (c) => {
|
|
31
|
+
const slug = c.req.param("slug");
|
|
32
|
+
const body = await c.req.json().catch(() => ({}));
|
|
33
|
+
try {
|
|
34
|
+
const env = envManager.updateEnv(slug, {
|
|
35
|
+
name: body.name,
|
|
36
|
+
variables: body.variables,
|
|
37
|
+
});
|
|
38
|
+
if (!env) return c.json({ error: "Environment not found" }, 404);
|
|
39
|
+
return c.json(env);
|
|
40
|
+
} catch (e: unknown) {
|
|
41
|
+
return c.json({ error: e instanceof Error ? e.message : String(e) }, 400);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
api.delete("/envs/:slug", (c) => {
|
|
46
|
+
const deleted = envManager.deleteEnv(c.req.param("slug"));
|
|
47
|
+
if (!deleted) return c.json({ error: "Environment not found" }, 404);
|
|
48
|
+
return c.json({ ok: true });
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Hono } from "hono";
|
|
2
|
+
import type { RouteDeps } from "./route-deps.js";
|
|
3
|
+
import * as sessionFolders from "../session-folders.js";
|
|
4
|
+
|
|
5
|
+
export function registerFolderRoutes(api: Hono, _deps: RouteDeps): void {
|
|
6
|
+
// List all folders
|
|
7
|
+
api.get("/folders", (c) => {
|
|
8
|
+
return c.json(sessionFolders.listFolders());
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// Create a folder
|
|
12
|
+
api.post("/folders", async (c) => {
|
|
13
|
+
const body = await c.req.json().catch(() => ({}));
|
|
14
|
+
if (!body.name || typeof body.name !== "string") {
|
|
15
|
+
return c.json({ error: "name is required" }, 400);
|
|
16
|
+
}
|
|
17
|
+
const folder = sessionFolders.createFolder(body.name.trim(), body.color);
|
|
18
|
+
return c.json(folder);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Update a folder
|
|
22
|
+
api.patch("/folders/:id", async (c) => {
|
|
23
|
+
const id = c.req.param("id");
|
|
24
|
+
const body = await c.req.json().catch(() => ({}));
|
|
25
|
+
const folder = sessionFolders.updateFolder(id, {
|
|
26
|
+
name: body.name,
|
|
27
|
+
color: body.color,
|
|
28
|
+
});
|
|
29
|
+
if (!folder) return c.json({ error: "Folder not found" }, 404);
|
|
30
|
+
return c.json(folder);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Delete a folder
|
|
34
|
+
api.delete("/folders/:id", (c) => {
|
|
35
|
+
const id = c.req.param("id");
|
|
36
|
+
const ok = sessionFolders.deleteFolder(id);
|
|
37
|
+
if (!ok) return c.json({ error: "Folder not found" }, 404);
|
|
38
|
+
return c.json({ ok: true });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Add session to folder
|
|
42
|
+
api.post("/folders/:id/sessions/:sessionId", (c) => {
|
|
43
|
+
const folderId = c.req.param("id");
|
|
44
|
+
const sessionId = c.req.param("sessionId");
|
|
45
|
+
const ok = sessionFolders.addSessionToFolder(folderId, sessionId);
|
|
46
|
+
if (!ok) return c.json({ error: "Folder not found" }, 404);
|
|
47
|
+
return c.json({ ok: true });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Remove session from any folder
|
|
51
|
+
api.delete("/folders/sessions/:sessionId", (c) => {
|
|
52
|
+
const sessionId = c.req.param("sessionId");
|
|
53
|
+
sessionFolders.removeSessionFromFolder(sessionId);
|
|
54
|
+
return c.json({ ok: true });
|
|
55
|
+
});
|
|
56
|
+
}
|