@swarmclawai/swarmclaw 0.4.0 → 0.5.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/README.md +21 -4
- package/bin/server-cmd.js +28 -19
- package/next.config.ts +13 -0
- package/package.json +3 -1
- package/src/app/api/agents/[id]/route.ts +39 -22
- package/src/app/api/agents/[id]/thread/route.ts +2 -2
- package/src/app/api/agents/route.ts +3 -2
- package/src/app/api/agents/trash/route.ts +44 -0
- package/src/app/api/clawhub/install/route.ts +2 -2
- package/src/app/api/connectors/[id]/route.ts +17 -7
- package/src/app/api/connectors/[id]/webhook/route.ts +103 -0
- package/src/app/api/connectors/route.ts +6 -3
- package/src/app/api/credentials/[id]/route.ts +2 -1
- package/src/app/api/credentials/route.ts +2 -2
- package/src/app/api/documents/route.ts +2 -2
- package/src/app/api/files/serve/route.ts +8 -0
- package/src/app/api/knowledge/[id]/route.ts +5 -4
- package/src/app/api/knowledge/upload/route.ts +2 -2
- package/src/app/api/mcp-servers/[id]/route.ts +11 -14
- package/src/app/api/mcp-servers/[id]/test/route.ts +2 -1
- package/src/app/api/mcp-servers/[id]/tools/route.ts +2 -1
- package/src/app/api/mcp-servers/route.ts +2 -2
- package/src/app/api/memory/[id]/route.ts +9 -8
- package/src/app/api/memory/route.ts +2 -2
- package/src/app/api/memory-images/[filename]/route.ts +2 -1
- package/src/app/api/openclaw/agent-files/route.ts +57 -0
- package/src/app/api/openclaw/approvals/route.ts +46 -0
- package/src/app/api/openclaw/config-sync/route.ts +33 -0
- package/src/app/api/openclaw/cron/route.ts +52 -0
- package/src/app/api/openclaw/directory/route.ts +27 -0
- package/src/app/api/openclaw/discover/route.ts +62 -0
- package/src/app/api/openclaw/dotenv-keys/route.ts +18 -0
- package/src/app/api/openclaw/exec-config/route.ts +41 -0
- package/src/app/api/openclaw/gateway/route.ts +72 -0
- package/src/app/api/openclaw/history/route.ts +109 -0
- package/src/app/api/openclaw/media/route.ts +53 -0
- package/src/app/api/openclaw/models/route.ts +12 -0
- package/src/app/api/openclaw/permissions/route.ts +39 -0
- package/src/app/api/openclaw/sandbox-env/route.ts +69 -0
- package/src/app/api/openclaw/skills/install/route.ts +32 -0
- package/src/app/api/openclaw/skills/remove/route.ts +24 -0
- package/src/app/api/openclaw/skills/route.ts +82 -0
- package/src/app/api/openclaw/sync/route.ts +31 -0
- package/src/app/api/orchestrator/run/route.ts +2 -2
- package/src/app/api/projects/[id]/route.ts +55 -0
- package/src/app/api/projects/route.ts +27 -0
- package/src/app/api/providers/[id]/models/route.ts +2 -1
- package/src/app/api/providers/[id]/route.ts +13 -15
- package/src/app/api/providers/route.ts +2 -2
- package/src/app/api/schedules/[id]/route.ts +16 -18
- package/src/app/api/schedules/[id]/run/route.ts +4 -3
- package/src/app/api/schedules/route.ts +2 -2
- package/src/app/api/secrets/[id]/route.ts +16 -17
- package/src/app/api/secrets/route.ts +2 -2
- package/src/app/api/sessions/[id]/clear/route.ts +2 -1
- package/src/app/api/sessions/[id]/deploy/route.ts +2 -1
- package/src/app/api/sessions/[id]/devserver/route.ts +2 -1
- package/src/app/api/sessions/[id]/edit-resend/route.ts +22 -0
- package/src/app/api/sessions/[id]/fork/route.ts +44 -0
- package/src/app/api/sessions/[id]/messages/route.ts +20 -2
- package/src/app/api/sessions/[id]/retry/route.ts +2 -1
- package/src/app/api/sessions/[id]/route.ts +14 -4
- package/src/app/api/sessions/route.ts +8 -4
- package/src/app/api/skills/[id]/route.ts +23 -21
- package/src/app/api/skills/import/route.ts +2 -2
- package/src/app/api/skills/route.ts +2 -2
- package/src/app/api/tasks/[id]/approve/route.ts +2 -1
- package/src/app/api/tasks/[id]/route.ts +6 -5
- package/src/app/api/tasks/route.ts +2 -2
- package/src/app/api/tts/stream/route.ts +48 -0
- package/src/app/api/upload/route.ts +2 -2
- package/src/app/api/uploads/[filename]/route.ts +4 -1
- package/src/app/api/webhooks/[id]/route.ts +29 -31
- package/src/app/api/webhooks/route.ts +2 -2
- package/src/app/globals.css +14 -0
- package/src/app/layout.tsx +5 -20
- package/src/app/page.tsx +3 -24
- package/src/cli/index.js +60 -0
- package/src/cli/index.ts +1 -1
- package/src/cli/spec.js +42 -0
- package/src/components/agents/agent-avatar.tsx +45 -0
- package/src/components/agents/agent-card.tsx +19 -5
- package/src/components/agents/agent-chat-list.tsx +31 -24
- package/src/components/agents/agent-files-editor.tsx +185 -0
- package/src/components/agents/agent-list.tsx +84 -3
- package/src/components/agents/agent-sheet.tsx +147 -14
- package/src/components/agents/cron-job-form.tsx +137 -0
- package/src/components/agents/exec-config-panel.tsx +147 -0
- package/src/components/agents/inspector-panel.tsx +310 -0
- package/src/components/agents/openclaw-skills-panel.tsx +230 -0
- package/src/components/agents/permission-preset-selector.tsx +79 -0
- package/src/components/agents/personality-builder.tsx +111 -0
- package/src/components/agents/sandbox-env-panel.tsx +72 -0
- package/src/components/agents/skill-install-dialog.tsx +102 -0
- package/src/components/agents/trash-list.tsx +109 -0
- package/src/components/chat/chat-area.tsx +41 -6
- package/src/components/chat/chat-header.tsx +305 -29
- package/src/components/chat/chat-preview-panel.tsx +113 -0
- package/src/components/chat/exec-approval-card.tsx +89 -0
- package/src/components/chat/message-bubble.tsx +218 -36
- package/src/components/chat/message-list.tsx +135 -31
- package/src/components/chat/streaming-bubble.tsx +59 -10
- package/src/components/chat/suggestions-bar.tsx +74 -0
- package/src/components/chat/thinking-indicator.tsx +20 -6
- package/src/components/chat/tool-call-bubble.tsx +98 -19
- package/src/components/chat/tool-request-banner.tsx +20 -2
- package/src/components/chat/trace-block.tsx +103 -0
- package/src/components/chat/voice-overlay.tsx +80 -0
- package/src/components/connectors/connector-list.tsx +6 -2
- package/src/components/connectors/connector-sheet.tsx +31 -7
- package/src/components/layout/app-layout.tsx +47 -25
- package/src/components/projects/project-list.tsx +123 -0
- package/src/components/projects/project-sheet.tsx +135 -0
- package/src/components/schedules/schedule-list.tsx +3 -1
- package/src/components/sessions/new-session-sheet.tsx +6 -6
- package/src/components/sessions/session-card.tsx +1 -1
- package/src/components/sessions/session-list.tsx +7 -7
- package/src/components/settings/gateway-connection-panel.tsx +278 -0
- package/src/components/shared/avatar.tsx +13 -2
- package/src/components/shared/connector-platform-icon.tsx +4 -0
- package/src/components/shared/settings/section-heartbeat.tsx +1 -1
- package/src/components/shared/settings/section-orchestrator.tsx +1 -2
- package/src/components/shared/settings/section-web-search.tsx +56 -0
- package/src/components/shared/settings/settings-page.tsx +74 -0
- package/src/components/skills/skill-list.tsx +2 -1
- package/src/components/tasks/task-board.tsx +1 -1
- package/src/components/tasks/task-list.tsx +5 -2
- package/src/components/tasks/task-sheet.tsx +12 -12
- package/src/hooks/use-continuous-speech.ts +181 -0
- package/src/hooks/use-openclaw-gateway.ts +63 -0
- package/src/hooks/use-view-router.ts +52 -0
- package/src/hooks/use-voice-conversation.ts +80 -0
- package/src/lib/id.ts +6 -0
- package/src/lib/notification-sounds.ts +58 -0
- package/src/lib/personality-parser.ts +97 -0
- package/src/lib/projects.ts +13 -0
- package/src/lib/provider-sets.ts +5 -0
- package/src/lib/providers/anthropic.ts +14 -1
- package/src/lib/providers/index.ts +6 -0
- package/src/lib/providers/ollama.ts +9 -1
- package/src/lib/providers/openai.ts +9 -1
- package/src/lib/providers/openclaw.ts +28 -2
- package/src/lib/runtime-loop.ts +2 -2
- package/src/lib/server/api-routes.test.ts +5 -6
- package/src/lib/server/build-llm.ts +17 -4
- package/src/lib/server/chat-execution.ts +82 -6
- package/src/lib/server/collection-helpers.ts +54 -0
- package/src/lib/server/connectors/bluebubbles.test.ts +217 -0
- package/src/lib/server/connectors/bluebubbles.ts +360 -0
- package/src/lib/server/connectors/connector-routing.test.ts +1 -1
- package/src/lib/server/connectors/googlechat.ts +51 -8
- package/src/lib/server/connectors/manager.ts +424 -13
- package/src/lib/server/connectors/media.ts +2 -2
- package/src/lib/server/connectors/openclaw.ts +65 -0
- package/src/lib/server/connectors/pairing.test.ts +99 -0
- package/src/lib/server/connectors/pairing.ts +256 -0
- package/src/lib/server/connectors/signal.ts +1 -0
- package/src/lib/server/connectors/teams.ts +5 -5
- package/src/lib/server/connectors/types.ts +10 -0
- package/src/lib/server/daemon-state.ts +11 -0
- package/src/lib/server/execution-log.ts +3 -3
- package/src/lib/server/heartbeat-service.ts +1 -1
- package/src/lib/server/knowledge-db.test.ts +2 -33
- package/src/lib/server/main-agent-loop.ts +8 -9
- package/src/lib/server/main-session.ts +21 -0
- package/src/lib/server/memory-db.ts +6 -6
- package/src/lib/server/openclaw-approvals.ts +105 -0
- package/src/lib/server/openclaw-config-sync.ts +107 -0
- package/src/lib/server/openclaw-exec-config.ts +52 -0
- package/src/lib/server/openclaw-gateway.ts +291 -0
- package/src/lib/server/openclaw-history-merge.ts +36 -0
- package/src/lib/server/openclaw-models.ts +56 -0
- package/src/lib/server/openclaw-permission-presets.ts +64 -0
- package/src/lib/server/openclaw-sync.ts +497 -0
- package/src/lib/server/orchestrator-lg.ts +30 -9
- package/src/lib/server/orchestrator.ts +4 -4
- package/src/lib/server/process-manager.ts +2 -2
- package/src/lib/server/queue.ts +24 -11
- package/src/lib/server/scheduler.ts +2 -2
- package/src/lib/server/session-mailbox.ts +2 -2
- package/src/lib/server/session-run-manager.ts +2 -2
- package/src/lib/server/session-tools/connector.ts +53 -6
- package/src/lib/server/session-tools/crud.ts +3 -3
- package/src/lib/server/session-tools/delegate.ts +22 -6
- package/src/lib/server/session-tools/file.ts +192 -19
- package/src/lib/server/session-tools/index.ts +4 -2
- package/src/lib/server/session-tools/memory.ts +2 -2
- package/src/lib/server/session-tools/openclaw-nodes.ts +112 -0
- package/src/lib/server/session-tools/sandbox.ts +33 -0
- package/src/lib/server/session-tools/search-providers.ts +277 -0
- package/src/lib/server/session-tools/session-info.ts +2 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +2 -2
- package/src/lib/server/session-tools/shell.ts +1 -1
- package/src/lib/server/session-tools/web.ts +53 -72
- package/src/lib/server/storage.ts +74 -11
- package/src/lib/server/stream-agent-chat.ts +53 -4
- package/src/lib/server/suggestions.ts +20 -0
- package/src/lib/server/task-result.test.ts +44 -0
- package/src/lib/server/task-result.ts +14 -0
- package/src/lib/server/ws-hub.ts +14 -0
- package/src/lib/tool-definitions.ts +5 -3
- package/src/lib/tts-stream.ts +130 -0
- package/src/lib/view-routes.ts +28 -0
- package/src/proxy.ts +3 -0
- package/src/stores/use-app-store.ts +80 -1
- package/src/stores/use-approval-store.ts +78 -0
- package/src/stores/use-chat-store.ts +162 -6
- package/src/types/index.ts +154 -3
- package/tsconfig.json +13 -4
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { loadSchedules, saveSchedules, deleteSchedule } from '@/lib/server/storage'
|
|
3
3
|
import { resolveScheduleName } from '@/lib/schedule-name'
|
|
4
|
-
import {
|
|
4
|
+
import { mutateItem, deleteItem, notFound, type CollectionOps } from '@/lib/server/collection-helpers'
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
const ops: CollectionOps<any> = { load: loadSchedules, save: saveSchedules, deleteFn: deleteSchedule, topic: 'schedules' }
|
|
5
8
|
|
|
6
9
|
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
7
10
|
const { id } = await params
|
|
8
11
|
const body = await req.json()
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
taskPrompt: schedules[id].taskPrompt,
|
|
12
|
+
const result = mutateItem(ops, id, (schedule) => {
|
|
13
|
+
Object.assign(schedule, body)
|
|
14
|
+
schedule.id = id
|
|
15
|
+
schedule.name = resolveScheduleName({
|
|
16
|
+
name: schedule.name,
|
|
17
|
+
taskPrompt: schedule.taskPrompt,
|
|
18
|
+
})
|
|
19
|
+
return schedule
|
|
18
20
|
})
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return NextResponse.json(schedules[id])
|
|
21
|
+
if (!result) return notFound()
|
|
22
|
+
return NextResponse.json(result)
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
25
26
|
const { id } = await params
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
deleteSchedule(id)
|
|
29
|
-
notify('schedules')
|
|
30
|
-
return NextResponse.json('ok')
|
|
27
|
+
if (!deleteItem(ops, id)) return notFound()
|
|
28
|
+
return NextResponse.json({ ok: true })
|
|
31
29
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import
|
|
2
|
+
import { genId } from '@/lib/id'
|
|
3
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
4
|
import { loadSchedules, saveSchedules, loadAgents, loadTasks, saveTasks } from '@/lib/server/storage'
|
|
4
5
|
import { enqueueTask } from '@/lib/server/queue'
|
|
5
6
|
import { pushMainLoopEventToMainSessions } from '@/lib/server/main-agent-loop'
|
|
@@ -14,7 +15,7 @@ export async function POST(_req: Request, { params }: { params: Promise<{ id: st
|
|
|
14
15
|
const { id } = await params
|
|
15
16
|
const schedules = loadSchedules()
|
|
16
17
|
const schedule = schedules[id]
|
|
17
|
-
if (!schedule) return
|
|
18
|
+
if (!schedule) return notFound()
|
|
18
19
|
|
|
19
20
|
const agents = loadAgents()
|
|
20
21
|
const agent = agents[schedule.agentId]
|
|
@@ -64,7 +65,7 @@ export async function POST(_req: Request, { params }: { params: Promise<{ id: st
|
|
|
64
65
|
existingTask.validation = null
|
|
65
66
|
prev.runNumber = schedule.runNumber
|
|
66
67
|
} else {
|
|
67
|
-
taskId =
|
|
68
|
+
taskId = genId()
|
|
68
69
|
tasks[taskId] = {
|
|
69
70
|
id: taskId,
|
|
70
71
|
title: `[Sched] ${schedule.name} (run #${schedule.runNumber})`,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import
|
|
2
|
+
import { genId } from '@/lib/id'
|
|
3
3
|
import { loadSchedules, saveSchedules } from '@/lib/server/storage'
|
|
4
4
|
import { resolveScheduleName } from '@/lib/schedule-name'
|
|
5
5
|
import { findDuplicateSchedule } from '@/lib/schedule-dedupe'
|
|
@@ -51,7 +51,7 @@ export async function POST(req: Request) {
|
|
|
51
51
|
return NextResponse.json(duplicate)
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
const id =
|
|
54
|
+
const id = genId()
|
|
55
55
|
|
|
56
56
|
let nextRunAt: number | undefined
|
|
57
57
|
if (scheduleType === 'once' && body.runAt) {
|
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { loadSecrets, saveSecrets } from '@/lib/server/storage'
|
|
3
|
+
import { mutateItem, deleteItem, notFound, type CollectionOps } from '@/lib/server/collection-helpers'
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
const ops: CollectionOps<any> = { load: loadSecrets, save: saveSecrets }
|
|
3
7
|
|
|
4
8
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
5
9
|
const { id } = await params
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
delete secrets[id]
|
|
9
|
-
saveSecrets(secrets)
|
|
10
|
-
return NextResponse.json('ok')
|
|
10
|
+
if (!deleteItem(ops, id)) return notFound()
|
|
11
|
+
return NextResponse.json({ ok: true })
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
14
15
|
const { id } = await params
|
|
15
16
|
const body = await req.json()
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const { encryptedValue, ...safe } = secrets[id]
|
|
17
|
+
const result = mutateItem(ops, id, (secret) => {
|
|
18
|
+
if (body.name !== undefined) secret.name = body.name
|
|
19
|
+
if (body.service !== undefined) secret.service = body.service
|
|
20
|
+
if (body.scope !== undefined) secret.scope = body.scope
|
|
21
|
+
if (body.agentIds !== undefined) secret.agentIds = body.agentIds
|
|
22
|
+
secret.updatedAt = Date.now()
|
|
23
|
+
return secret
|
|
24
|
+
})
|
|
25
|
+
if (!result) return notFound()
|
|
26
|
+
const { encryptedValue, ...safe } = result as Record<string, unknown>
|
|
28
27
|
return NextResponse.json(safe)
|
|
29
28
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import
|
|
2
|
+
import { genId } from '@/lib/id'
|
|
3
3
|
import { loadSecrets, saveSecrets, encryptKey } from '@/lib/server/storage'
|
|
4
4
|
export const dynamic = 'force-dynamic'
|
|
5
5
|
|
|
@@ -18,7 +18,7 @@ export async function GET(_req: Request) {
|
|
|
18
18
|
|
|
19
19
|
export async function POST(req: Request) {
|
|
20
20
|
const body = await req.json()
|
|
21
|
-
const id =
|
|
21
|
+
const id = genId()
|
|
22
22
|
const now = Date.now()
|
|
23
23
|
const secrets = loadSecrets()
|
|
24
24
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { loadSessions, saveSessions } from '@/lib/server/storage'
|
|
3
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
4
|
|
|
4
5
|
export async function POST(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
5
6
|
const { id } = await params
|
|
6
7
|
const sessions = loadSessions()
|
|
7
|
-
if (!sessions[id]) return
|
|
8
|
+
if (!sessions[id]) return notFound()
|
|
8
9
|
sessions[id].messages = []
|
|
9
10
|
sessions[id].claudeSessionId = null
|
|
10
11
|
sessions[id].codexThreadId = null
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { execSync } from 'child_process'
|
|
3
3
|
import { loadSessions } from '@/lib/server/storage'
|
|
4
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
5
|
|
|
5
6
|
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
7
|
const { id } = await params
|
|
7
8
|
const sessions = loadSessions()
|
|
8
9
|
const session = sessions[id]
|
|
9
|
-
if (!session) return
|
|
10
|
+
if (!session) return notFound()
|
|
10
11
|
|
|
11
12
|
const body = await req.json()
|
|
12
13
|
const msg = body.message || 'Deploy from SwarmClaw'
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { spawn } from 'child_process'
|
|
3
3
|
import { loadSessions, devServers, localIP } from '@/lib/server/storage'
|
|
4
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
5
|
|
|
5
6
|
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
7
|
const { id } = await params
|
|
7
8
|
const sessions = loadSessions()
|
|
8
9
|
const session = sessions[id]
|
|
9
|
-
if (!session) return
|
|
10
|
+
if (!session) return notFound()
|
|
10
11
|
|
|
11
12
|
const { action } = await req.json()
|
|
12
13
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { loadSessions, saveSessions } from '@/lib/server/storage'
|
|
3
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
|
+
|
|
5
|
+
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
|
+
const { id } = await params
|
|
7
|
+
const body = await req.json() as { messageIndex: number; newText: string }
|
|
8
|
+
const sessions = loadSessions()
|
|
9
|
+
const session = sessions[id]
|
|
10
|
+
if (!session) return notFound()
|
|
11
|
+
|
|
12
|
+
const { messageIndex, newText } = body
|
|
13
|
+
if (typeof messageIndex !== 'number' || messageIndex < 0 || messageIndex >= session.messages.length) {
|
|
14
|
+
return NextResponse.json({ error: 'Invalid message index' }, { status: 400 })
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Truncate messages to messageIndex (discard that msg + everything after)
|
|
18
|
+
session.messages = session.messages.slice(0, messageIndex)
|
|
19
|
+
saveSessions(sessions)
|
|
20
|
+
|
|
21
|
+
return NextResponse.json({ message: newText })
|
|
22
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { randomUUID } from 'node:crypto'
|
|
3
|
+
import { loadSessions, saveSessions } from '@/lib/server/storage'
|
|
4
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
5
|
+
|
|
6
|
+
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
7
|
+
const { id } = await params
|
|
8
|
+
const body = await req.json() as { messageIndex: number }
|
|
9
|
+
const sessions = loadSessions()
|
|
10
|
+
const source = sessions[id]
|
|
11
|
+
if (!source) return notFound()
|
|
12
|
+
|
|
13
|
+
const { messageIndex } = body
|
|
14
|
+
if (typeof messageIndex !== 'number' || messageIndex < 0 || messageIndex >= source.messages.length) {
|
|
15
|
+
return NextResponse.json({ error: 'Invalid message index' }, { status: 400 })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const now = Date.now()
|
|
19
|
+
const newId = randomUUID()
|
|
20
|
+
const forked = {
|
|
21
|
+
id: newId,
|
|
22
|
+
name: `Fork of ${source.name}`,
|
|
23
|
+
cwd: source.cwd,
|
|
24
|
+
user: source.user,
|
|
25
|
+
provider: source.provider,
|
|
26
|
+
model: source.model,
|
|
27
|
+
credentialId: source.credentialId ?? null,
|
|
28
|
+
fallbackCredentialIds: source.fallbackCredentialIds,
|
|
29
|
+
apiEndpoint: source.apiEndpoint ?? null,
|
|
30
|
+
claudeSessionId: null,
|
|
31
|
+
messages: source.messages.slice(0, messageIndex + 1),
|
|
32
|
+
createdAt: now,
|
|
33
|
+
lastActiveAt: now,
|
|
34
|
+
agentId: source.agentId ?? null,
|
|
35
|
+
parentSessionId: id,
|
|
36
|
+
tools: source.tools,
|
|
37
|
+
conversationTone: source.conversationTone,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
sessions[newId] = forked
|
|
41
|
+
saveSessions(sessions)
|
|
42
|
+
|
|
43
|
+
return NextResponse.json(forked)
|
|
44
|
+
}
|
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import { loadSessions } from '@/lib/server/storage'
|
|
2
|
+
import { loadSessions, saveSessions } from '@/lib/server/storage'
|
|
3
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
4
|
|
|
4
5
|
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
5
6
|
const { id } = await params
|
|
6
7
|
const sessions = loadSessions()
|
|
7
|
-
if (!sessions[id]) return
|
|
8
|
+
if (!sessions[id]) return notFound()
|
|
8
9
|
return NextResponse.json(sessions[id].messages)
|
|
9
10
|
}
|
|
11
|
+
|
|
12
|
+
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
13
|
+
const { id } = await params
|
|
14
|
+
const body = await req.json() as { messageIndex: number; bookmarked: boolean }
|
|
15
|
+
const sessions = loadSessions()
|
|
16
|
+
const session = sessions[id]
|
|
17
|
+
if (!session) return notFound()
|
|
18
|
+
|
|
19
|
+
const { messageIndex, bookmarked } = body
|
|
20
|
+
if (typeof messageIndex !== 'number' || messageIndex < 0 || messageIndex >= session.messages.length) {
|
|
21
|
+
return NextResponse.json({ error: 'Invalid message index' }, { status: 400 })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
session.messages[messageIndex].bookmarked = bookmarked
|
|
25
|
+
saveSessions(sessions)
|
|
26
|
+
return NextResponse.json(session.messages[messageIndex])
|
|
27
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { loadSessions, saveSessions } from '@/lib/server/storage'
|
|
3
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
4
|
|
|
4
5
|
export async function POST(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
5
6
|
const { id } = await params
|
|
6
7
|
const sessions = loadSessions()
|
|
7
8
|
const session = sessions[id]
|
|
8
|
-
if (!session) return
|
|
9
|
+
if (!session) return notFound()
|
|
9
10
|
|
|
10
11
|
const msgs = session.messages
|
|
11
12
|
// Pop trailing assistant messages to find the last user message
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { loadSessions, saveSessions, deleteSession, active, loadAgents } from '@/lib/server/storage'
|
|
3
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
4
|
import { enqueueSessionRun } from '@/lib/server/session-run-manager'
|
|
4
5
|
import { normalizeProviderEndpoint } from '@/lib/openclaw-endpoint'
|
|
6
|
+
import { ensureMainSessionFlag, isProtectedMainSession } from '@/lib/server/main-session'
|
|
5
7
|
|
|
6
8
|
function buildSessionAwakeningPrompt(user: string | null | undefined): string {
|
|
7
9
|
const displayName = typeof user === 'string' && user.trim() ? user.trim() : 'there'
|
|
@@ -20,7 +22,8 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
20
22
|
const { id } = await params
|
|
21
23
|
const updates = await req.json()
|
|
22
24
|
const sessions = loadSessions()
|
|
23
|
-
if (!sessions[id]) return
|
|
25
|
+
if (!sessions[id]) return notFound()
|
|
26
|
+
const wasProtectedMain = isProtectedMainSession(sessions[id])
|
|
24
27
|
const hadMessagesBefore = Array.isArray(sessions[id].messages) && sessions[id].messages.length > 0
|
|
25
28
|
|
|
26
29
|
const agentIdUpdateProvided = updates.agentId !== undefined
|
|
@@ -32,7 +35,13 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
32
35
|
|
|
33
36
|
const linkedAgent = nextAgentId ? loadAgents()[nextAgentId] : null
|
|
34
37
|
|
|
35
|
-
if (updates.name !== undefined)
|
|
38
|
+
if (updates.name !== undefined) {
|
|
39
|
+
const nextName = typeof updates.name === 'string' ? updates.name.trim() : String(updates.name || '')
|
|
40
|
+
if (wasProtectedMain && nextName !== '__main__') {
|
|
41
|
+
return new NextResponse('Cannot rename main chat session', { status: 400 })
|
|
42
|
+
}
|
|
43
|
+
sessions[id].name = updates.name
|
|
44
|
+
}
|
|
36
45
|
if (updates.cwd !== undefined) sessions[id].cwd = updates.cwd
|
|
37
46
|
if (updates.provider !== undefined) sessions[id].provider = updates.provider
|
|
38
47
|
else if (agentIdUpdateProvided && linkedAgent?.provider) sessions[id].provider = linkedAgent.provider
|
|
@@ -61,8 +70,9 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
61
70
|
if (updates.heartbeatIntervalSec !== undefined) sessions[id].heartbeatIntervalSec = updates.heartbeatIntervalSec
|
|
62
71
|
if (updates.pinned !== undefined) sessions[id].pinned = !!updates.pinned
|
|
63
72
|
if (!Array.isArray(sessions[id].messages)) sessions[id].messages = []
|
|
73
|
+
ensureMainSessionFlag(sessions[id])
|
|
64
74
|
|
|
65
|
-
const shouldKickoffAwakening = sessions[id]
|
|
75
|
+
const shouldKickoffAwakening = isProtectedMainSession(sessions[id])
|
|
66
76
|
&& agentIdUpdateProvided
|
|
67
77
|
&& !!sessions[id].agentId
|
|
68
78
|
&& !hadMessagesBefore
|
|
@@ -91,7 +101,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
91
101
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
92
102
|
const { id } = await params
|
|
93
103
|
const sessions = loadSessions()
|
|
94
|
-
if (sessions[id]
|
|
104
|
+
if (isProtectedMainSession(sessions[id])) {
|
|
95
105
|
return new NextResponse('Cannot delete main chat session', { status: 403 })
|
|
96
106
|
}
|
|
97
107
|
if (active.has(id)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import
|
|
2
|
+
import { genId } from '@/lib/id'
|
|
3
3
|
import os from 'os'
|
|
4
4
|
import path from 'path'
|
|
5
5
|
import { loadSessions, saveSessions, deleteSession, active, loadAgents } from '@/lib/server/storage'
|
|
@@ -7,6 +7,7 @@ import { WORKSPACE_DIR } from '@/lib/server/data-dir'
|
|
|
7
7
|
import { notify } from '@/lib/server/ws-hub'
|
|
8
8
|
import { getSessionRunState } from '@/lib/server/session-run-manager'
|
|
9
9
|
import { normalizeProviderEndpoint } from '@/lib/openclaw-endpoint'
|
|
10
|
+
import { ensureMainSessionFlag, isProtectedMainSession } from '@/lib/server/main-session'
|
|
10
11
|
export const dynamic = 'force-dynamic'
|
|
11
12
|
|
|
12
13
|
|
|
@@ -27,16 +28,18 @@ export async function DELETE(req: Request) {
|
|
|
27
28
|
return new NextResponse('Missing ids', { status: 400 })
|
|
28
29
|
}
|
|
29
30
|
const sessions = loadSessions()
|
|
31
|
+
let deleted = 0
|
|
30
32
|
for (const id of ids) {
|
|
31
|
-
if (sessions[id]
|
|
33
|
+
if (isProtectedMainSession(sessions[id])) continue
|
|
32
34
|
if (active.has(id)) {
|
|
33
35
|
try { active.get(id).kill() } catch {}
|
|
34
36
|
active.delete(id)
|
|
35
37
|
}
|
|
36
38
|
deleteSession(id)
|
|
39
|
+
deleted += 1
|
|
37
40
|
}
|
|
38
41
|
notify('sessions')
|
|
39
|
-
return NextResponse.json({ deleted: ids.length })
|
|
42
|
+
return NextResponse.json({ deleted, requested: ids.length })
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
export async function POST(req: Request) {
|
|
@@ -46,7 +49,7 @@ export async function POST(req: Request) {
|
|
|
46
49
|
else if (cwd === '~') cwd = os.homedir()
|
|
47
50
|
else if (!cwd) cwd = WORKSPACE_DIR
|
|
48
51
|
|
|
49
|
-
const id = body.id ||
|
|
52
|
+
const id = body.id || genId()
|
|
50
53
|
const sessions = loadSessions()
|
|
51
54
|
const agent = body.agentId ? loadAgents()[body.agentId] : null
|
|
52
55
|
const requestedTools = Array.isArray(body.tools) ? body.tools : null
|
|
@@ -86,6 +89,7 @@ export async function POST(req: Request) {
|
|
|
86
89
|
heartbeatEnabled: body.heartbeatEnabled ?? null,
|
|
87
90
|
heartbeatIntervalSec: body.heartbeatIntervalSec ?? null,
|
|
88
91
|
}
|
|
92
|
+
ensureMainSessionFlag(sessions[id])
|
|
89
93
|
saveSessions(sessions)
|
|
90
94
|
notify('sessions')
|
|
91
95
|
return NextResponse.json(sessions[id])
|
|
@@ -1,40 +1,42 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { loadSkills, saveSkills, deleteSkill } from '@/lib/server/storage'
|
|
3
3
|
import { normalizeSkillPayload } from '@/lib/server/skills-normalize'
|
|
4
|
+
import { mutateItem, deleteItem, notFound, type CollectionOps } from '@/lib/server/collection-helpers'
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
const ops: CollectionOps<any> = { load: loadSkills, save: saveSkills, deleteFn: deleteSkill }
|
|
4
8
|
|
|
5
9
|
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
10
|
const { id } = await params
|
|
7
11
|
const skills = loadSkills()
|
|
8
|
-
if (!skills[id]) return
|
|
12
|
+
if (!skills[id]) return notFound()
|
|
9
13
|
return NextResponse.json(skills[id])
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
13
17
|
const { id } = await params
|
|
14
18
|
const body = await req.json()
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return NextResponse.json(
|
|
19
|
+
const result = mutateItem(ops, id, (skill) => {
|
|
20
|
+
const normalized = normalizeSkillPayload({ ...skill, ...body })
|
|
21
|
+
return {
|
|
22
|
+
...skill,
|
|
23
|
+
...body,
|
|
24
|
+
name: normalized.name,
|
|
25
|
+
filename: normalized.filename,
|
|
26
|
+
description: normalized.description,
|
|
27
|
+
content: normalized.content,
|
|
28
|
+
sourceUrl: normalized.sourceUrl,
|
|
29
|
+
sourceFormat: normalized.sourceFormat,
|
|
30
|
+
id,
|
|
31
|
+
updatedAt: Date.now(),
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
if (!result) return notFound()
|
|
35
|
+
return NextResponse.json(result)
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
35
39
|
const { id } = await params
|
|
36
|
-
|
|
37
|
-
if (!skills[id]) return new NextResponse(null, { status: 404 })
|
|
38
|
-
deleteSkill(id)
|
|
40
|
+
if (!deleteItem(ops, id)) return notFound()
|
|
39
41
|
return NextResponse.json({ deleted: id })
|
|
40
42
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { genId } from '@/lib/id'
|
|
2
2
|
import { NextResponse } from 'next/server'
|
|
3
3
|
import { loadSkills, saveSkills } from '@/lib/server/storage'
|
|
4
4
|
import { normalizeSkillPayload } from '@/lib/server/skills-normalize'
|
|
@@ -47,7 +47,7 @@ export async function POST(req: Request) {
|
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
const skills = loadSkills()
|
|
50
|
-
const id =
|
|
50
|
+
const id = genId()
|
|
51
51
|
skills[id] = {
|
|
52
52
|
id,
|
|
53
53
|
name: normalized.name,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import
|
|
2
|
+
import { genId } from '@/lib/id'
|
|
3
3
|
import { loadSkills, saveSkills } from '@/lib/server/storage'
|
|
4
4
|
import { normalizeSkillPayload } from '@/lib/server/skills-normalize'
|
|
5
5
|
export const dynamic = 'force-dynamic'
|
|
@@ -12,7 +12,7 @@ export async function GET(_req: Request) {
|
|
|
12
12
|
export async function POST(req: Request) {
|
|
13
13
|
const body = await req.json()
|
|
14
14
|
const skills = loadSkills()
|
|
15
|
-
const id =
|
|
15
|
+
const id = genId()
|
|
16
16
|
const normalized = normalizeSkillPayload(body)
|
|
17
17
|
skills[id] = {
|
|
18
18
|
id,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { loadTasks, saveTasks, loadAgents } from '@/lib/server/storage'
|
|
3
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
4
|
import { notify } from '@/lib/server/ws-hub'
|
|
4
5
|
import { getCheckpointSaver } from '@/lib/server/langgraph-checkpoint'
|
|
5
6
|
|
|
@@ -10,7 +11,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
10
11
|
|
|
11
12
|
const tasks = loadTasks()
|
|
12
13
|
const task = tasks[id]
|
|
13
|
-
if (!task) return
|
|
14
|
+
if (!task) return notFound()
|
|
14
15
|
if (!task.pendingApproval) {
|
|
15
16
|
return NextResponse.json({ error: 'No pending approval on this task' }, { status: 400 })
|
|
16
17
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { genId } from '@/lib/id'
|
|
2
2
|
import { NextResponse } from 'next/server'
|
|
3
3
|
import { loadTasks, saveTasks } from '@/lib/server/storage'
|
|
4
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
5
|
import { disableSessionHeartbeat, enqueueTask, validateCompletedTasksQueue } from '@/lib/server/queue'
|
|
5
6
|
import { ensureTaskCompletionReport } from '@/lib/server/task-reports'
|
|
6
7
|
import { formatValidationFailure, validateTaskCompletion } from '@/lib/server/task-validation'
|
|
@@ -13,7 +14,7 @@ export async function GET(_req: Request, { params }: { params: Promise<{ id: str
|
|
|
13
14
|
|
|
14
15
|
const { id } = await params
|
|
15
16
|
const tasks = loadTasks()
|
|
16
|
-
if (!tasks[id]) return
|
|
17
|
+
if (!tasks[id]) return notFound()
|
|
17
18
|
return NextResponse.json(tasks[id])
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -21,7 +22,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
21
22
|
const { id } = await params
|
|
22
23
|
const body = await req.json()
|
|
23
24
|
const tasks = loadTasks()
|
|
24
|
-
if (!tasks[id]) return
|
|
25
|
+
if (!tasks[id]) return notFound()
|
|
25
26
|
|
|
26
27
|
const prevStatus = tasks[id].status
|
|
27
28
|
|
|
@@ -55,7 +56,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
55
56
|
tasks[id].error = formatValidationFailure(validation.reasons).slice(0, 500)
|
|
56
57
|
if (!tasks[id].comments) tasks[id].comments = []
|
|
57
58
|
tasks[id].comments.push({
|
|
58
|
-
id:
|
|
59
|
+
id: genId(),
|
|
59
60
|
author: 'System',
|
|
60
61
|
text: `Completion validation failed.\n\n${validation.reasons.map((r) => `- ${r}`).join('\n')}`,
|
|
61
62
|
createdAt: Date.now(),
|
|
@@ -88,7 +89,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
88
89
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
89
90
|
const { id } = await params
|
|
90
91
|
const tasks = loadTasks()
|
|
91
|
-
if (!tasks[id]) return
|
|
92
|
+
if (!tasks[id]) return notFound()
|
|
92
93
|
|
|
93
94
|
// Soft delete: move to archived status instead of hard delete
|
|
94
95
|
tasks[id].status = 'archived'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import
|
|
2
|
+
import { genId } from '@/lib/id'
|
|
3
3
|
import { loadTasks, saveTasks, loadSettings } from '@/lib/server/storage'
|
|
4
4
|
import { enqueueTask, validateCompletedTasksQueue } from '@/lib/server/queue'
|
|
5
5
|
import { ensureTaskCompletionReport } from '@/lib/server/task-reports'
|
|
@@ -54,7 +54,7 @@ export async function DELETE(req: Request) {
|
|
|
54
54
|
|
|
55
55
|
export async function POST(req: Request) {
|
|
56
56
|
const body = await req.json()
|
|
57
|
-
const id =
|
|
57
|
+
const id = genId()
|
|
58
58
|
const now = Date.now()
|
|
59
59
|
const tasks = loadTasks()
|
|
60
60
|
const settings = loadSettings()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { loadSettings } from '@/lib/server/storage'
|
|
2
|
+
|
|
3
|
+
export async function POST(req: Request) {
|
|
4
|
+
const settings = loadSettings()
|
|
5
|
+
const ELEVENLABS_KEY = settings.elevenLabsApiKey || process.env.ELEVENLABS_API_KEY
|
|
6
|
+
const ELEVENLABS_VOICE = settings.elevenLabsVoiceId || process.env.ELEVENLABS_VOICE || 'JBFqnCBsd6RMkjVDRZzb'
|
|
7
|
+
|
|
8
|
+
if (!ELEVENLABS_KEY) {
|
|
9
|
+
return new Response('No ElevenLabs API key. Set one in Settings > Voice.', { status: 500 })
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { text } = await req.json()
|
|
13
|
+
if (!text?.trim()) {
|
|
14
|
+
return new Response('No text provided', { status: 400 })
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const apiRes = await fetch(
|
|
18
|
+
`https://api.elevenlabs.io/v1/text-to-speech/${ELEVENLABS_VOICE}/stream`,
|
|
19
|
+
{
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: {
|
|
22
|
+
'xi-api-key': ELEVENLABS_KEY,
|
|
23
|
+
'Content-Type': 'application/json',
|
|
24
|
+
'Accept': 'audio/mpeg',
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify({
|
|
27
|
+
text: text.slice(0, 2000),
|
|
28
|
+
model_id: 'eleven_multilingual_v2',
|
|
29
|
+
voice_settings: { stability: 0.5, similarity_boost: 0.75 },
|
|
30
|
+
output_format: 'mp3_22050_32',
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
if (!apiRes.ok) {
|
|
36
|
+
const err = await apiRes.text()
|
|
37
|
+
return new Response(err, { status: apiRes.status })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Pipe the streaming response directly
|
|
41
|
+
return new Response(apiRes.body, {
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'audio/mpeg',
|
|
44
|
+
'Transfer-Encoding': 'chunked',
|
|
45
|
+
'Cache-Control': 'no-cache',
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
}
|