@swarmclawai/swarmclaw 1.2.0 → 1.2.2
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 +19 -0
- package/package.json +5 -2
- package/skills/coding-agent/SKILL.md +111 -0
- package/skills/github/SKILL.md +140 -0
- package/skills/nano-banana-pro/SKILL.md +62 -0
- package/skills/nano-banana-pro/scripts/generate_image.py +235 -0
- package/skills/nano-pdf/SKILL.md +53 -0
- package/skills/openai-image-gen/SKILL.md +78 -0
- package/skills/openai-image-gen/scripts/gen.py +328 -0
- package/skills/resourceful-problem-solving/SKILL.md +49 -0
- package/skills/skill-creator/SKILL.md +147 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/quick_validate.py +159 -0
- package/skills/summarize/SKILL.md +77 -0
- package/src/app/api/auth/route.ts +20 -5
- package/src/app/api/chats/[id]/deploy/route.ts +11 -6
- package/src/app/api/chats/[id]/devserver/route.ts +17 -20
- package/src/app/api/chats/[id]/messages/route.ts +15 -11
- package/src/app/api/chats/[id]/route.ts +9 -10
- package/src/app/api/chats/[id]/stop/route.ts +5 -7
- package/src/app/api/chats/messages-route.test.ts +8 -6
- package/src/app/api/chats/route.ts +9 -10
- package/src/app/api/credentials/[id]/route.ts +4 -1
- package/src/app/api/extensions/marketplace/route.ts +5 -2
- package/src/app/api/ip/route.ts +2 -2
- package/src/app/api/memory/maintenance/route.ts +5 -2
- package/src/app/api/preview-server/route.ts +15 -12
- package/src/app/api/projects/[id]/route.ts +7 -46
- package/src/app/api/system/status/route.ts +11 -0
- package/src/app/api/upload/route.ts +4 -1
- package/src/cli/index.js +7 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-files-editor.tsx +44 -32
- package/src/components/agents/personality-builder.tsx +13 -7
- package/src/components/agents/trash-list.tsx +1 -1
- package/src/components/chat/chat-area.tsx +45 -23
- package/src/components/chat/message-bubble.test.ts +35 -0
- package/src/components/chat/message-bubble.tsx +20 -9
- package/src/components/chat/message-list.tsx +62 -42
- package/src/components/chat/swarm-status-card.tsx +10 -3
- package/src/components/input/chat-input.tsx +34 -14
- package/src/components/layout/daemon-indicator.tsx +7 -8
- package/src/components/layout/update-banner.tsx +8 -13
- package/src/components/logs/log-list.tsx +1 -1
- package/src/components/memory/memory-card.tsx +3 -1
- package/src/components/org-chart/org-chart-view.tsx +4 -0
- package/src/components/projects/project-list.tsx +4 -2
- package/src/components/projects/tabs/overview-tab.tsx +3 -2
- package/src/components/secrets/secret-sheet.tsx +1 -1
- package/src/components/secrets/secrets-list.tsx +1 -1
- package/src/components/shared/agent-switch-dialog.tsx +12 -6
- package/src/components/shared/dir-browser.tsx +22 -18
- package/src/components/skills/skill-sheet.tsx +2 -3
- package/src/components/tasks/task-list.tsx +1 -1
- package/src/components/tasks/task-sheet.tsx +1 -1
- package/src/hooks/use-openclaw-gateway.ts +46 -27
- package/src/instrumentation.ts +10 -7
- package/src/lib/chat/assistant-render-id.ts +3 -0
- package/src/lib/chat/chat-streaming-state.test.ts +42 -3
- package/src/lib/chat/chat-streaming-state.ts +20 -8
- package/src/lib/chat/chat.ts +18 -2
- package/src/lib/chat/queued-message-queue.test.ts +23 -1
- package/src/lib/chat/queued-message-queue.ts +11 -2
- package/src/lib/providers/anthropic.ts +6 -3
- package/src/lib/providers/claude-cli.ts +9 -3
- package/src/lib/providers/cli-utils.test.ts +124 -0
- package/src/lib/providers/cli-utils.ts +15 -0
- package/src/lib/providers/codex-cli.ts +9 -3
- package/src/lib/providers/gemini-cli.ts +6 -2
- package/src/lib/providers/index.ts +4 -1
- package/src/lib/providers/ollama.ts +5 -2
- package/src/lib/providers/openai.ts +8 -5
- package/src/lib/providers/opencode-cli.ts +6 -2
- package/src/lib/server/activity/activity-log.ts +21 -0
- package/src/lib/server/agents/agent-availability.test.ts +10 -5
- package/src/lib/server/agents/agent-cascade.ts +79 -59
- package/src/lib/server/agents/agent-registry.ts +23 -4
- package/src/lib/server/agents/agent-repository.ts +90 -0
- package/src/lib/server/agents/delegation-job-repository.ts +53 -0
- package/src/lib/server/agents/delegation-jobs.ts +11 -4
- package/src/lib/server/agents/guardian-checkpoint-repository.ts +35 -0
- package/src/lib/server/agents/guardian.ts +2 -2
- package/src/lib/server/agents/main-agent-loop.ts +14 -6
- package/src/lib/server/agents/main-loop-state-repository.ts +38 -0
- package/src/lib/server/agents/subagent-runtime.ts +9 -6
- package/src/lib/server/agents/subagent-swarm.ts +3 -2
- package/src/lib/server/agents/task-session.ts +3 -4
- package/src/lib/server/approvals/approval-repository.ts +30 -0
- package/src/lib/server/autonomy/supervisor-incident-repository.ts +42 -0
- package/src/lib/server/autonomy/supervisor-reflection.ts +14 -1
- package/src/lib/server/chat-execution/chat-execution-types.ts +38 -0
- package/src/lib/server/chat-execution/chat-execution-utils.ts +1 -1
- package/src/lib/server/chat-execution/chat-execution.ts +84 -1914
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +620 -0
- package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +221 -0
- package/src/lib/server/chat-execution/chat-turn-preflight.ts +133 -0
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +817 -0
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +296 -0
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +5 -5
- package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -3
- package/src/lib/server/chat-execution/continuation-limits.ts +6 -3
- package/src/lib/server/chat-execution/message-classifier.test.ts +329 -0
- package/src/lib/server/chat-execution/message-classifier.ts +5 -2
- package/src/lib/server/chat-execution/post-stream-finalization.ts +5 -2
- package/src/lib/server/chat-execution/prompt-builder.ts +22 -1
- package/src/lib/server/chat-execution/prompt-sections.ts +55 -13
- package/src/lib/server/chat-execution/response-completeness.ts +5 -2
- package/src/lib/server/chat-execution/situational-awareness.ts +12 -7
- package/src/lib/server/chat-execution/stream-agent-chat.ts +58 -25
- package/src/lib/server/chatrooms/chatroom-memory-bridge.ts +6 -3
- package/src/lib/server/chatrooms/chatroom-repository.ts +32 -0
- package/src/lib/server/connectors/bluebubbles.ts +7 -4
- package/src/lib/server/connectors/connector-inbound.ts +16 -13
- package/src/lib/server/connectors/connector-lifecycle.ts +11 -8
- package/src/lib/server/connectors/connector-outbound.ts +6 -3
- package/src/lib/server/connectors/connector-repository.ts +58 -0
- package/src/lib/server/connectors/discord.ts +10 -7
- package/src/lib/server/connectors/email.ts +17 -14
- package/src/lib/server/connectors/googlechat.ts +7 -4
- package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -2
- package/src/lib/server/connectors/matrix.ts +6 -3
- package/src/lib/server/connectors/openclaw.ts +20 -17
- package/src/lib/server/connectors/outbox.ts +4 -1
- package/src/lib/server/connectors/runtime-state.test.ts +117 -0
- package/src/lib/server/connectors/runtime-state.ts +19 -0
- package/src/lib/server/connectors/session-consolidation.ts +5 -2
- package/src/lib/server/connectors/signal.ts +9 -6
- package/src/lib/server/connectors/slack.ts +13 -10
- package/src/lib/server/connectors/teams.ts +8 -5
- package/src/lib/server/connectors/telegram.ts +15 -12
- package/src/lib/server/connectors/whatsapp.ts +32 -29
- package/src/lib/server/credentials/credential-repository.ts +7 -0
- package/src/lib/server/embeddings.ts +4 -1
- package/src/lib/server/gateways/gateway-profile-repository.ts +4 -0
- package/src/lib/server/link-understanding.ts +4 -1
- package/src/lib/server/memory/memory-abstract.test.ts +59 -0
- package/src/lib/server/memory/memory-abstract.ts +59 -0
- package/src/lib/server/memory/memory-db.ts +40 -14
- package/src/lib/server/missions/mission-repository.ts +74 -0
- package/src/lib/server/missions/mission-service/actions.ts +6 -0
- package/src/lib/server/missions/mission-service/bindings.ts +9 -0
- package/src/lib/server/missions/mission-service/context.ts +4 -0
- package/src/lib/server/missions/mission-service/core.ts +2269 -0
- package/src/lib/server/missions/mission-service/queries.ts +12 -0
- package/src/lib/server/missions/mission-service/recovery.ts +5 -0
- package/src/lib/server/missions/mission-service/ticks.ts +9 -0
- package/src/lib/server/missions/mission-service.test.ts +9 -2
- package/src/lib/server/missions/mission-service.ts +6 -2263
- package/src/lib/server/openclaw/gateway.ts +8 -5
- package/src/lib/server/persistence/repository-utils.ts +154 -0
- package/src/lib/server/persistence/storage-context.ts +51 -0
- package/src/lib/server/persistence/transaction.ts +1 -0
- package/src/lib/server/project-utils.ts +13 -0
- package/src/lib/server/projects/project-repository.ts +36 -0
- package/src/lib/server/projects/project-service.ts +79 -0
- package/src/lib/server/protocols/protocol-agent-turn.ts +5 -2
- package/src/lib/server/protocols/protocol-normalization.test.ts +6 -4
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +5 -2
- package/src/lib/server/protocols/protocol-step-helpers.ts +4 -1
- package/src/lib/server/provider-health.ts +18 -0
- package/src/lib/server/query-expansion.ts +4 -1
- package/src/lib/server/runtime/alert-dispatch.ts +8 -7
- package/src/lib/server/runtime/daemon-policy.ts +1 -1
- package/src/lib/server/runtime/daemon-state/core.ts +1570 -0
- package/src/lib/server/runtime/daemon-state/health.ts +6 -0
- package/src/lib/server/runtime/daemon-state/policy.ts +7 -0
- package/src/lib/server/runtime/daemon-state/supervisor.ts +6 -0
- package/src/lib/server/runtime/daemon-state.test.ts +48 -0
- package/src/lib/server/runtime/daemon-state.ts +3 -1331
- package/src/lib/server/runtime/estop-repository.ts +4 -0
- package/src/lib/server/runtime/estop.ts +3 -1
- package/src/lib/server/runtime/heartbeat-service.test.ts +2 -2
- package/src/lib/server/runtime/heartbeat-service.ts +78 -34
- package/src/lib/server/runtime/heartbeat-wake.ts +6 -4
- package/src/lib/server/runtime/idle-window.ts +6 -3
- package/src/lib/server/runtime/network.ts +11 -0
- package/src/lib/server/runtime/orchestrator-events.ts +2 -2
- package/src/lib/server/runtime/perf.ts +4 -1
- package/src/lib/server/runtime/process-manager.ts +7 -4
- package/src/lib/server/runtime/queue/claims.ts +4 -0
- package/src/lib/server/runtime/queue/core.ts +2079 -0
- package/src/lib/server/runtime/queue/execution.ts +7 -0
- package/src/lib/server/runtime/queue/followups.ts +4 -0
- package/src/lib/server/runtime/queue/queries.ts +12 -0
- package/src/lib/server/runtime/queue/recovery.ts +7 -0
- package/src/lib/server/runtime/queue-recovery.test.ts +48 -13
- package/src/lib/server/runtime/queue-repository.ts +17 -0
- package/src/lib/server/runtime/queue.ts +5 -2058
- package/src/lib/server/runtime/run-ledger.ts +6 -5
- package/src/lib/server/runtime/run-repository.ts +73 -0
- package/src/lib/server/runtime/runtime-lock-repository.ts +8 -0
- package/src/lib/server/runtime/runtime-settings.ts +1 -1
- package/src/lib/server/runtime/runtime-state.ts +99 -0
- package/src/lib/server/runtime/scheduler.ts +13 -8
- package/src/lib/server/runtime/session-run-manager/cancellation.ts +157 -0
- package/src/lib/server/runtime/session-run-manager/drain.ts +246 -0
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +287 -0
- package/src/lib/server/runtime/session-run-manager/queries.ts +117 -0
- package/src/lib/server/runtime/session-run-manager/recovery.ts +238 -0
- package/src/lib/server/runtime/session-run-manager/state.ts +441 -0
- package/src/lib/server/runtime/session-run-manager/types.ts +74 -0
- package/src/lib/server/runtime/session-run-manager.ts +72 -1374
- package/src/lib/server/runtime/watch-job-repository.ts +35 -0
- package/src/lib/server/runtime/watch-jobs.ts +3 -1
- package/src/lib/server/sandbox/bridge-auth-registry.ts +6 -0
- package/src/lib/server/sandbox/novnc-auth.ts +10 -0
- package/src/lib/server/schedules/schedule-repository.ts +42 -0
- package/src/lib/server/session-tools/context.ts +14 -0
- package/src/lib/server/session-tools/discovery.ts +9 -6
- package/src/lib/server/session-tools/index.ts +3 -1
- package/src/lib/server/session-tools/platform.ts +1 -1
- package/src/lib/server/session-tools/subagent.ts +23 -2
- package/src/lib/server/session-tools/wallet.ts +4 -1
- package/src/lib/server/sessions/session-repository.ts +85 -0
- package/src/lib/server/settings/settings-repository.ts +25 -0
- package/src/lib/server/skills/clawhub-client.ts +4 -1
- package/src/lib/server/skills/runtime-skill-resolver.ts +8 -2
- package/src/lib/server/skills/skill-discovery.test.ts +2 -2
- package/src/lib/server/skills/skill-discovery.ts +2 -2
- package/src/lib/server/skills/skill-eligibility.ts +6 -0
- package/src/lib/server/skills/skill-repository.ts +14 -0
- package/src/lib/server/solana.ts +6 -0
- package/src/lib/server/storage-auth.ts +5 -5
- package/src/lib/server/storage-normalization.ts +4 -0
- package/src/lib/server/storage.ts +32 -32
- package/src/lib/server/tasks/task-followups.ts +4 -1
- package/src/lib/server/tasks/task-repository.ts +54 -0
- package/src/lib/server/tool-loop-detection.ts +8 -3
- package/src/lib/server/tool-planning.ts +226 -0
- package/src/lib/server/tool-retry.ts +4 -3
- package/src/lib/server/usage/usage-repository.ts +30 -0
- package/src/lib/server/wallet/wallet-portfolio.ts +29 -0
- package/src/lib/server/webhooks/webhook-repository.ts +10 -0
- package/src/lib/server/ws-hub.ts +5 -2
- package/src/lib/strip-internal-metadata.test.ts +78 -37
- package/src/lib/strip-internal-metadata.ts +20 -6
- package/src/stores/use-approval-store.ts +7 -1
- package/src/stores/use-chat-store.test.ts +54 -0
- package/src/stores/use-chat-store.ts +26 -6
- package/src/types/index.ts +6 -0
- /package/{bundled-skills → skills}/google-workspace/SKILL.md +0 -0
|
@@ -3,6 +3,9 @@ import { execSync } from 'child_process'
|
|
|
3
3
|
import { loadSessions } from '@/lib/server/storage'
|
|
4
4
|
import { notFound } from '@/lib/server/collection-helpers'
|
|
5
5
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
6
|
+
import { log } from '@/lib/server/logger'
|
|
7
|
+
|
|
8
|
+
const TAG = 'api-deploy'
|
|
6
9
|
|
|
7
10
|
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
8
11
|
const { id } = await params
|
|
@@ -21,16 +24,18 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
21
24
|
try {
|
|
22
25
|
execSync(`git commit -m "${msg.replace(/"/g, '\\"')}"`, opts)
|
|
23
26
|
committed = true
|
|
24
|
-
} catch (ce:
|
|
25
|
-
|
|
27
|
+
} catch (ce: unknown) {
|
|
28
|
+
const ex = ce as { stdout?: string; stderr?: string }
|
|
29
|
+
if (!(ex.stdout || ex.stderr || '').includes('nothing to commit')) throw ce
|
|
26
30
|
}
|
|
27
31
|
execSync('git push 2>&1', opts)
|
|
28
|
-
|
|
32
|
+
log.info(TAG, `deployed: ${msg}`)
|
|
29
33
|
return NextResponse.json({ ok: true, output: committed ? 'Committed and pushed!' : 'Already committed — pushed to remote!' })
|
|
30
|
-
} catch (e:
|
|
31
|
-
|
|
34
|
+
} catch (e: unknown) {
|
|
35
|
+
const ex = e as { stderr?: string; stdout?: string; message?: string }
|
|
36
|
+
log.error(TAG, `deploy error:`, ex.message)
|
|
32
37
|
return NextResponse.json(
|
|
33
|
-
{ ok: false, error: (
|
|
38
|
+
{ ok: false, error: (ex.stderr || ex.stdout || ex.message || 'Unknown error').toString().slice(0, 300) },
|
|
34
39
|
{ status: 500 },
|
|
35
40
|
)
|
|
36
41
|
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { spawn } from 'child_process'
|
|
3
|
-
import { loadSessions, devServers, localIP } from '@/lib/server/storage'
|
|
4
3
|
import { notFound } from '@/lib/server/collection-helpers'
|
|
5
4
|
import { resolveDevServerLaunchDir } from '@/lib/server/runtime/devserver-launch'
|
|
5
|
+
import { clearDevServer, getDevServer, hasDevServer, registerDevServer, stopDevServer, updateDevServerUrl } from '@/lib/server/runtime/runtime-state'
|
|
6
|
+
import { localIP } from '@/lib/server/runtime/network'
|
|
7
|
+
import { listSessions } from '@/lib/server/sessions/session-repository'
|
|
6
8
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
7
9
|
import { sleep } from '@/lib/shared-utils'
|
|
8
10
|
import net from 'net'
|
|
11
|
+
import { log } from '@/lib/server/logger'
|
|
12
|
+
|
|
13
|
+
const TAG = 'api-devserver'
|
|
9
14
|
|
|
10
15
|
interface DevServerStartResult {
|
|
11
16
|
status?: number
|
|
@@ -53,22 +58,21 @@ async function startDevServer(id: string, session: { cwd: string }): Promise<Dev
|
|
|
53
58
|
if (match) {
|
|
54
59
|
const detectedPort = match[1]
|
|
55
60
|
detectedUrl = `http://${localIP()}:${detectedPort}`
|
|
56
|
-
|
|
57
|
-
if (ds) ds.url = detectedUrl
|
|
61
|
+
updateDevServerUrl(id, detectedUrl)
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
proc.stdout!.on('data', onData)
|
|
63
67
|
proc.stderr!.on('data', onData)
|
|
64
|
-
proc.on('close', () => {
|
|
65
|
-
proc.on('error', () =>
|
|
68
|
+
proc.on('close', () => { clearDevServer(id); log.info(TAG, `dev server stopped for ${id}`) })
|
|
69
|
+
proc.on('error', () => clearDevServer(id))
|
|
66
70
|
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
registerDevServer(id, { proc, url: `http://${localIP()}:${port}` })
|
|
72
|
+
log.info(TAG, `starting dev server in ${launch.launchDir} (session cwd=${session.cwd})`)
|
|
69
73
|
|
|
70
74
|
await sleep(4000)
|
|
71
|
-
const ds =
|
|
75
|
+
const ds = getDevServer(id)
|
|
72
76
|
if (!ds) {
|
|
73
77
|
return {
|
|
74
78
|
status: 502,
|
|
@@ -96,7 +100,7 @@ async function startDevServer(id: string, session: { cwd: string }): Promise<Dev
|
|
|
96
100
|
|
|
97
101
|
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
98
102
|
const { id } = await params
|
|
99
|
-
const sessions =
|
|
103
|
+
const sessions = listSessions()
|
|
100
104
|
const session = sessions[id]
|
|
101
105
|
if (!session) return notFound()
|
|
102
106
|
|
|
@@ -105,8 +109,8 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
105
109
|
const { action } = body
|
|
106
110
|
|
|
107
111
|
if (action === 'start') {
|
|
108
|
-
if (
|
|
109
|
-
const ds =
|
|
112
|
+
if (hasDevServer(id)) {
|
|
113
|
+
const ds = getDevServer(id)!
|
|
110
114
|
return NextResponse.json({ running: true, url: ds.url })
|
|
111
115
|
}
|
|
112
116
|
|
|
@@ -123,18 +127,11 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
123
127
|
return NextResponse.json(result.body, result.status ? { status: result.status } : undefined)
|
|
124
128
|
|
|
125
129
|
} else if (action === 'stop') {
|
|
126
|
-
|
|
127
|
-
const ds = devServers.get(id)!
|
|
128
|
-
try { ds.proc.kill('SIGTERM') } catch {}
|
|
129
|
-
if (typeof ds.proc.pid === 'number') {
|
|
130
|
-
try { process.kill(-ds.proc.pid, 'SIGTERM') } catch {}
|
|
131
|
-
}
|
|
132
|
-
devServers.delete(id)
|
|
133
|
-
}
|
|
130
|
+
stopDevServer(id)
|
|
134
131
|
return NextResponse.json({ running: false })
|
|
135
132
|
|
|
136
133
|
} else if (action === 'status') {
|
|
137
|
-
return NextResponse.json({ running:
|
|
134
|
+
return NextResponse.json({ running: hasDevServer(id), url: getDevServer(id)?.url })
|
|
138
135
|
}
|
|
139
136
|
|
|
140
137
|
return NextResponse.json({ running: false })
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import { loadStoredItem, upsertStoredItem } from '@/lib/server/storage'
|
|
3
2
|
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
3
|
import { materializeStreamingAssistantArtifacts } from '@/lib/chat/chat-streaming-state'
|
|
5
4
|
import { appendSessionNote } from '@/lib/server/session-note'
|
|
6
|
-
import
|
|
5
|
+
import { getSessionRunState } from '@/lib/server/runtime/session-run-manager'
|
|
6
|
+
import { getSession, saveSession } from '@/lib/server/sessions/session-repository'
|
|
7
|
+
import type { Message } from '@/types'
|
|
7
8
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
8
9
|
|
|
9
10
|
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
10
11
|
const { id } = await params
|
|
11
|
-
const session =
|
|
12
|
+
const session = getSession(id)
|
|
12
13
|
if (!session) return notFound()
|
|
13
14
|
session.messages = Array.isArray(session.messages) ? session.messages : []
|
|
14
15
|
|
|
16
|
+
// Use persisted fields plus the run ledger. Process-local execution state is
|
|
17
|
+
// intentionally excluded here so stale registry entries do not block cleanup.
|
|
15
18
|
const sessionClaimsActive = session.active === true
|
|
16
19
|
|| (typeof session.currentRunId === 'string' && session.currentRunId.trim().length > 0)
|
|
20
|
+
|| !!getSessionRunState(id).runningRunId
|
|
17
21
|
if (!sessionClaimsActive && materializeStreamingAssistantArtifacts(session.messages)) {
|
|
18
|
-
|
|
22
|
+
saveSession(id, session)
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
const url = new URL(req.url)
|
|
@@ -57,7 +61,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
57
61
|
if (error) return error
|
|
58
62
|
|
|
59
63
|
if (body.kind === 'context-clear') {
|
|
60
|
-
const session =
|
|
64
|
+
const session = getSession(id)
|
|
61
65
|
if (!session) return notFound()
|
|
62
66
|
|
|
63
67
|
session.messages.push({
|
|
@@ -66,7 +70,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
66
70
|
kind: 'context-clear',
|
|
67
71
|
time: Date.now(),
|
|
68
72
|
})
|
|
69
|
-
|
|
73
|
+
saveSession(id, session)
|
|
70
74
|
return NextResponse.json({ ok: true })
|
|
71
75
|
}
|
|
72
76
|
|
|
@@ -78,7 +82,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
78
82
|
kind: body.messageKind || 'system',
|
|
79
83
|
})
|
|
80
84
|
if (!inserted) {
|
|
81
|
-
const session =
|
|
85
|
+
const session = getSession(id)
|
|
82
86
|
if (!session) return notFound()
|
|
83
87
|
return NextResponse.json({ error: 'Note text is required' }, { status: 400 })
|
|
84
88
|
}
|
|
@@ -92,7 +96,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
92
96
|
const { id } = await params
|
|
93
97
|
const { data: body, error } = await safeParseBody<{ messageIndex: number; bookmarked: boolean }>(req)
|
|
94
98
|
if (error) return error
|
|
95
|
-
const session =
|
|
99
|
+
const session = getSession(id)
|
|
96
100
|
if (!session) return notFound()
|
|
97
101
|
|
|
98
102
|
const { messageIndex, bookmarked } = body
|
|
@@ -101,7 +105,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
session.messages[messageIndex].bookmarked = bookmarked
|
|
104
|
-
|
|
108
|
+
saveSession(id, session)
|
|
105
109
|
return NextResponse.json(session.messages[messageIndex])
|
|
106
110
|
}
|
|
107
111
|
|
|
@@ -109,7 +113,7 @@ export async function DELETE(req: Request, { params }: { params: Promise<{ id: s
|
|
|
109
113
|
const { id } = await params
|
|
110
114
|
const { data: body, error } = await safeParseBody<{ messageIndex: number }>(req)
|
|
111
115
|
if (error) return error
|
|
112
|
-
const session =
|
|
116
|
+
const session = getSession(id)
|
|
113
117
|
if (!session) return notFound()
|
|
114
118
|
|
|
115
119
|
const { messageIndex } = body
|
|
@@ -123,6 +127,6 @@ export async function DELETE(req: Request, { params }: { params: Promise<{ id: s
|
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
session.messages.splice(messageIndex, 1)
|
|
126
|
-
|
|
130
|
+
saveSession(id, session)
|
|
127
131
|
return NextResponse.json({ ok: true })
|
|
128
132
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import {
|
|
2
|
+
import { loadAgents } from '@/lib/server/storage'
|
|
3
3
|
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
4
|
import { normalizeProviderEndpoint } from '@/lib/openclaw/openclaw-endpoint'
|
|
5
5
|
import { resolvePrimaryAgentRoute } from '@/lib/server/agents/agent-runtime-config'
|
|
6
6
|
import { clearMainLoopStateForSession } from '@/lib/server/agents/main-agent-loop'
|
|
7
7
|
import { cleanupSessionProcesses } from '@/lib/server/runtime/process-manager'
|
|
8
|
+
import { deleteSession, getSession, saveSession } from '@/lib/server/sessions/session-repository'
|
|
9
|
+
import { stopActiveSessionProcess } from '@/lib/server/runtime/runtime-state'
|
|
8
10
|
import { getSessionQueueSnapshot, getSessionRunState } from '@/lib/server/runtime/session-run-manager'
|
|
9
11
|
import { normalizeCapabilitySelection } from '@/lib/capability-selection'
|
|
10
12
|
import { enrichSessionWithMissionSummary } from '@/lib/server/missions/mission-service'
|
|
@@ -12,12 +14,12 @@ import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
|
12
14
|
|
|
13
15
|
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
14
16
|
const { id } = await params
|
|
15
|
-
const session =
|
|
17
|
+
const session = getSession(id)
|
|
16
18
|
if (!session) return notFound()
|
|
17
19
|
|
|
18
20
|
const run = getSessionRunState(id)
|
|
19
21
|
const queue = getSessionQueueSnapshot(id)
|
|
20
|
-
session.active =
|
|
22
|
+
session.active = !!run.runningRunId
|
|
21
23
|
session.queuedCount = queue.queueLength
|
|
22
24
|
session.currentRunId = run.runningRunId || null
|
|
23
25
|
|
|
@@ -28,7 +30,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
28
30
|
const { id } = await params
|
|
29
31
|
const { data: updates, error } = await safeParseBody(req)
|
|
30
32
|
if (error) return error
|
|
31
|
-
const session =
|
|
33
|
+
const session = getSession(id) as Record<string, unknown> | null
|
|
32
34
|
if (!session) return notFound()
|
|
33
35
|
|
|
34
36
|
if (updates.resetMainLoopState === true) {
|
|
@@ -142,17 +144,14 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
142
144
|
if (updates.delegateResumeIds !== undefined) session.delegateResumeIds = updates.delegateResumeIds
|
|
143
145
|
if (!Array.isArray(session.messages)) session.messages = []
|
|
144
146
|
|
|
145
|
-
|
|
147
|
+
saveSession(id, session)
|
|
146
148
|
return NextResponse.json(enrichSessionWithMissionSummary(session as never))
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
150
152
|
const { id } = await params
|
|
151
|
-
if (!
|
|
152
|
-
|
|
153
|
-
try { active.get(id)?.kill() } catch {}
|
|
154
|
-
active.delete(id)
|
|
155
|
-
}
|
|
153
|
+
if (!getSession(id)) return notFound()
|
|
154
|
+
stopActiveSessionProcess(id)
|
|
156
155
|
cleanupSessionProcesses(id)
|
|
157
156
|
deleteSession(id)
|
|
158
157
|
return new NextResponse('OK')
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { materializeStreamingAssistantArtifacts } from '@/lib/chat/chat-streaming-state'
|
|
3
|
-
import { active, loadStoredItem, upsertStoredItem } from '@/lib/server/storage'
|
|
4
3
|
import { cancelSessionRuns } from '@/lib/server/runtime/session-run-manager'
|
|
4
|
+
import { stopActiveSessionProcess } from '@/lib/server/runtime/runtime-state'
|
|
5
|
+
import { getSession, saveSession } from '@/lib/server/sessions/session-repository'
|
|
5
6
|
import type { Session } from '@/types'
|
|
6
7
|
|
|
7
8
|
export async function POST(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
8
9
|
const { id } = await params
|
|
9
10
|
const cancel = cancelSessionRuns(id, 'Stopped by user')
|
|
10
|
-
const session =
|
|
11
|
+
const session = getSession(id) as Session | null
|
|
11
12
|
if (session && Array.isArray(session.messages) && materializeStreamingAssistantArtifacts(session.messages)) {
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
if (active.has(id)) {
|
|
15
|
-
try { active.get(id)?.kill() } catch {}
|
|
16
|
-
active.delete(id)
|
|
13
|
+
saveSession(id, session)
|
|
17
14
|
}
|
|
15
|
+
stopActiveSessionProcess(id)
|
|
18
16
|
return NextResponse.json({ ok: true, ...cancel })
|
|
19
17
|
}
|
|
@@ -10,11 +10,13 @@ test('chat messages route materializes stale streaming artifacts even if runtime
|
|
|
10
10
|
returnedText: string | null
|
|
11
11
|
persistedStreaming: boolean | null
|
|
12
12
|
persistedText: string | null
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
}>(`
|
|
14
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
15
|
+
const routeMod = await import('./src/app/api/chats/[id]/messages/route')
|
|
16
|
+
const runtimeStateMod = await import('./src/lib/server/runtime/runtime-state')
|
|
17
|
+
const storage = storageMod.default || storageMod
|
|
18
|
+
const route = routeMod.default || routeMod
|
|
19
|
+
const runtimeState = runtimeStateMod.default || runtimeStateMod
|
|
18
20
|
|
|
19
21
|
storage.upsertStoredItem('sessions', 'session-stale', {
|
|
20
22
|
id: 'session-stale',
|
|
@@ -37,7 +39,7 @@ test('chat messages route materializes stale streaming artifacts even if runtime
|
|
|
37
39
|
],
|
|
38
40
|
})
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
runtimeState.registerActiveSessionProcess('session-stale', { kill() {} })
|
|
41
43
|
|
|
42
44
|
const response = await route.GET(
|
|
43
45
|
new Request('http://local/api/chats/session-stale/messages'),
|
|
@@ -3,9 +3,11 @@ import { genId } from '@/lib/id'
|
|
|
3
3
|
import os from 'os'
|
|
4
4
|
import path from 'path'
|
|
5
5
|
import { perf } from '@/lib/server/runtime/perf'
|
|
6
|
-
import {
|
|
6
|
+
import { loadAgents } from '@/lib/server/storage'
|
|
7
7
|
import { WORKSPACE_DIR } from '@/lib/server/data-dir'
|
|
8
8
|
import { notify } from '@/lib/server/ws-hub'
|
|
9
|
+
import { deleteSession, listSessions, replaceSessions } from '@/lib/server/sessions/session-repository'
|
|
10
|
+
import { stopActiveSessionProcess } from '@/lib/server/runtime/runtime-state'
|
|
9
11
|
import { getSessionQueueSnapshot, getSessionRunState } from '@/lib/server/runtime/session-run-manager'
|
|
10
12
|
import { normalizeProviderEndpoint } from '@/lib/openclaw/openclaw-endpoint'
|
|
11
13
|
import { applyResolvedRoute, resolvePrimaryAgentRoute } from '@/lib/server/agents/agent-runtime-config'
|
|
@@ -25,11 +27,11 @@ export async function GET(req: Request) {
|
|
|
25
27
|
const endPerf = perf.start('api', 'GET /api/chats')
|
|
26
28
|
// Note: pruneThreadConnectorMirrors and materializeStreamingAssistantArtifacts
|
|
27
29
|
// are handled by the daemon periodic health check, not on every list fetch.
|
|
28
|
-
const sessions =
|
|
30
|
+
const sessions = listSessions()
|
|
29
31
|
for (const id of Object.keys(sessions)) {
|
|
30
32
|
const run = getSessionRunState(id)
|
|
31
33
|
const queue = getSessionQueueSnapshot(id)
|
|
32
|
-
sessions[id].active =
|
|
34
|
+
sessions[id].active = !!run.runningRunId
|
|
33
35
|
sessions[id].queuedCount = queue.queueLength
|
|
34
36
|
sessions[id].currentRunId = run.runningRunId || null
|
|
35
37
|
}
|
|
@@ -59,14 +61,11 @@ export async function DELETE(req: Request) {
|
|
|
59
61
|
if (!Array.isArray(ids) || !ids.length) {
|
|
60
62
|
return new NextResponse('Missing ids', { status: 400 })
|
|
61
63
|
}
|
|
62
|
-
const sessions =
|
|
64
|
+
const sessions = listSessions()
|
|
63
65
|
let deleted = 0
|
|
64
66
|
for (const id of ids) {
|
|
65
67
|
if (!sessions[id]) continue
|
|
66
|
-
|
|
67
|
-
try { active.get(id)?.kill() } catch {}
|
|
68
|
-
active.delete(id)
|
|
69
|
-
}
|
|
68
|
+
stopActiveSessionProcess(id)
|
|
70
69
|
deleteSession(id)
|
|
71
70
|
deleted += 1
|
|
72
71
|
}
|
|
@@ -83,7 +82,7 @@ export async function POST(req: Request) {
|
|
|
83
82
|
else if (!cwd) cwd = WORKSPACE_DIR
|
|
84
83
|
|
|
85
84
|
const id = body.id || genId()
|
|
86
|
-
const sessions =
|
|
85
|
+
const sessions = listSessions()
|
|
87
86
|
const agent = body.agentId ? loadAgents()[body.agentId] : null
|
|
88
87
|
if (isAgentDisabled(agent)) {
|
|
89
88
|
return NextResponse.json({ error: buildAgentDisabledMessage(agent, 'start chats') }, { status: 409 })
|
|
@@ -162,7 +161,7 @@ export async function POST(req: Request) {
|
|
|
162
161
|
sessions[id] = (body.provider || body.model || body.credentialId || body.apiEndpoint)
|
|
163
162
|
? nextSession
|
|
164
163
|
: applyResolvedRoute(nextSession, resolvedRoute)
|
|
165
|
-
|
|
164
|
+
replaceSessions(sessions)
|
|
166
165
|
notify('sessions')
|
|
167
166
|
return NextResponse.json(sessions[id])
|
|
168
167
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { loadCredentials, deleteCredential } from '@/lib/server/storage'
|
|
3
3
|
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
|
+
import { log } from '@/lib/server/logger'
|
|
5
|
+
|
|
6
|
+
const TAG = 'api-credentials'
|
|
4
7
|
|
|
5
8
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
9
|
const { id: credId } = await params
|
|
@@ -9,6 +12,6 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
|
|
|
9
12
|
return notFound()
|
|
10
13
|
}
|
|
11
14
|
deleteCredential(credId)
|
|
12
|
-
|
|
15
|
+
log.info(TAG, `deleted ${credId}`)
|
|
13
16
|
return new NextResponse('OK')
|
|
14
17
|
}
|
|
@@ -3,6 +3,9 @@ import { inferExtensionPublisherSourceFromUrl } from '@/lib/extension-sources'
|
|
|
3
3
|
import { searchClawHub } from '@/lib/server/skills/clawhub-client'
|
|
4
4
|
import type { ExtensionCatalogSource } from '@/types'
|
|
5
5
|
import { errorMessage } from '@/lib/shared-utils'
|
|
6
|
+
import { log } from '@/lib/server/logger'
|
|
7
|
+
|
|
8
|
+
const TAG = 'api-extensions-marketplace'
|
|
6
9
|
|
|
7
10
|
export const dynamic = 'force-dynamic'
|
|
8
11
|
|
|
@@ -71,7 +74,7 @@ export async function GET(req: Request) {
|
|
|
71
74
|
})
|
|
72
75
|
}
|
|
73
76
|
} catch (err: unknown) {
|
|
74
|
-
|
|
77
|
+
log.warn(TAG, 'Registry failed:', {
|
|
75
78
|
registryUrl: registry.url,
|
|
76
79
|
error: errorMessage(err),
|
|
77
80
|
})
|
|
@@ -93,7 +96,7 @@ export async function GET(req: Request) {
|
|
|
93
96
|
catalogSource: 'clawhub',
|
|
94
97
|
})))
|
|
95
98
|
} catch (err: unknown) {
|
|
96
|
-
|
|
99
|
+
log.warn(TAG, 'ClawHub failed:', errorMessage(err))
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
allExtensions.sort((a, b) => {
|
package/src/app/api/ip/route.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import { localIP } from '@/lib/server/
|
|
2
|
+
import { localIP } from '@/lib/server/runtime/network'
|
|
3
3
|
export const dynamic = 'force-dynamic'
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
export async function GET(
|
|
6
|
+
export async function GET() {
|
|
7
7
|
return NextResponse.json({ ip: localIP(), port: parseInt(process.env.PORT || '3000') })
|
|
8
8
|
}
|
|
@@ -4,6 +4,9 @@ import { getMemoryDb } from '@/lib/server/memory/memory-db'
|
|
|
4
4
|
import { loadSettings } from '@/lib/server/storage'
|
|
5
5
|
import { syncAllSessionArchiveMemories } from '@/lib/server/memory/session-archive-memory'
|
|
6
6
|
import { DATA_DIR } from '@/lib/server/data-dir'
|
|
7
|
+
import { log } from '@/lib/server/logger'
|
|
8
|
+
|
|
9
|
+
const TAG = 'api-memory-maintenance'
|
|
7
10
|
|
|
8
11
|
function parseBool(value: unknown, fallback: boolean): boolean {
|
|
9
12
|
if (typeof value === 'boolean') return value
|
|
@@ -46,7 +49,7 @@ export async function GET(req: Request) {
|
|
|
46
49
|
archiveExportDir: path.join(DATA_DIR, 'session-archives'),
|
|
47
50
|
})
|
|
48
51
|
} catch (err: unknown) {
|
|
49
|
-
|
|
52
|
+
log.error(TAG, 'GET failed:', err)
|
|
50
53
|
return NextResponse.json({ ok: false, error: String((err as Error)?.message || err) }, { status: 500 })
|
|
51
54
|
}
|
|
52
55
|
}
|
|
@@ -77,7 +80,7 @@ export async function POST(req: Request) {
|
|
|
77
80
|
...result,
|
|
78
81
|
})
|
|
79
82
|
} catch (err: unknown) {
|
|
80
|
-
|
|
83
|
+
log.error(TAG, 'POST failed:', err)
|
|
81
84
|
return NextResponse.json({ ok: false, error: String((err as Error)?.message || err) }, { status: 500 })
|
|
82
85
|
}
|
|
83
86
|
}
|
|
@@ -3,11 +3,14 @@ import { spawn, type ChildProcess } from 'child_process'
|
|
|
3
3
|
import http from 'http'
|
|
4
4
|
import fs from 'fs'
|
|
5
5
|
import path from 'path'
|
|
6
|
-
import { localIP } from '@/lib/server/
|
|
6
|
+
import { localIP } from '@/lib/server/runtime/network'
|
|
7
7
|
import { resolveDevServerLaunchDir } from '@/lib/server/runtime/devserver-launch'
|
|
8
8
|
import { resolvePathWithinBaseDir } from '@/lib/server/path-utils'
|
|
9
9
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
10
10
|
import { hmrSingleton, sleep } from '@/lib/shared-utils'
|
|
11
|
+
import { log } from '@/lib/server/logger'
|
|
12
|
+
|
|
13
|
+
const TAG = 'api-preview-server'
|
|
11
14
|
|
|
12
15
|
// ---------------------------------------------------------------------------
|
|
13
16
|
// MIME types for static server
|
|
@@ -181,7 +184,7 @@ function createStaticServer(dir: string): http.Server {
|
|
|
181
184
|
async function startNpmServer(dir: string, command: string[], port: number, framework?: string): Promise<PreviewServer> {
|
|
182
185
|
// Install deps if node_modules missing
|
|
183
186
|
if (!fs.existsSync(path.join(dir, 'node_modules'))) {
|
|
184
|
-
|
|
187
|
+
log.info(TAG, `Installing dependencies in ${dir}`)
|
|
185
188
|
await new Promise<void>((resolve, reject) => {
|
|
186
189
|
const install = spawn('npm', ['install'], { cwd: dir, stdio: 'pipe' })
|
|
187
190
|
install.on('close', (code) => code === 0 ? resolve() : reject(new Error(`npm install exited ${code}`)))
|
|
@@ -206,14 +209,14 @@ async function startNpmServer(dir: string, command: string[], port: number, fram
|
|
|
206
209
|
env,
|
|
207
210
|
})
|
|
208
211
|
|
|
209
|
-
let
|
|
212
|
+
let processOutput = ''
|
|
210
213
|
let detectedPort = port
|
|
211
214
|
const urlRe = /https?:\/\/(?:localhost|0\.0\.0\.0|127\.0\.0\.1|[\d.]+):(\d+)/
|
|
212
215
|
|
|
213
216
|
const onData = (chunk: Buffer) => {
|
|
214
217
|
const text = chunk.toString()
|
|
215
|
-
|
|
216
|
-
if (
|
|
218
|
+
processOutput += text
|
|
219
|
+
if (processOutput.length > 10000) processOutput = processOutput.slice(-5000)
|
|
217
220
|
const match = text.match(urlRe)
|
|
218
221
|
if (match) {
|
|
219
222
|
detectedPort = parseInt(match[1], 10)
|
|
@@ -236,7 +239,7 @@ async function startNpmServer(dir: string, command: string[], port: number, fram
|
|
|
236
239
|
|
|
237
240
|
proc.on('close', () => {
|
|
238
241
|
servers.delete(dirKey(dir))
|
|
239
|
-
|
|
242
|
+
log.info(TAG, `npm server stopped for ${dir}`)
|
|
240
243
|
})
|
|
241
244
|
proc.on('error', () => servers.delete(dirKey(dir)))
|
|
242
245
|
|
|
@@ -246,10 +249,10 @@ async function startNpmServer(dir: string, command: string[], port: number, fram
|
|
|
246
249
|
await sleep(5000)
|
|
247
250
|
if (proc.exitCode !== null) {
|
|
248
251
|
servers.delete(dirKey(dir))
|
|
249
|
-
throw new Error(`npm dev server exited early with code ${proc.exitCode}\n${
|
|
252
|
+
throw new Error(`npm dev server exited early with code ${proc.exitCode}\n${processOutput.slice(-4000)}`)
|
|
250
253
|
}
|
|
251
254
|
entry.port = detectedPort
|
|
252
|
-
entry.log =
|
|
255
|
+
entry.log = processOutput
|
|
253
256
|
|
|
254
257
|
return entry
|
|
255
258
|
}
|
|
@@ -295,7 +298,7 @@ export async function POST(req: Request) {
|
|
|
295
298
|
const port = await findFreePort()
|
|
296
299
|
|
|
297
300
|
if (project.type === 'npm' && project.devCommand) {
|
|
298
|
-
|
|
301
|
+
log.info(TAG, `Detected ${project.framework} project in ${launch.launchDir}, running: ${project.devCommand.join(' ')}`)
|
|
299
302
|
try {
|
|
300
303
|
const entry = await startNpmServer(launch.launchDir, project.devCommand, port, project.framework)
|
|
301
304
|
return NextResponse.json({
|
|
@@ -305,7 +308,7 @@ export async function POST(req: Request) {
|
|
|
305
308
|
launchDir: launch.launchDir,
|
|
306
309
|
})
|
|
307
310
|
} catch (err: unknown) {
|
|
308
|
-
|
|
311
|
+
log.error(TAG, 'npm server failed, falling back to static:', err)
|
|
309
312
|
// Fall through to static server
|
|
310
313
|
}
|
|
311
314
|
}
|
|
@@ -319,7 +322,7 @@ export async function POST(req: Request) {
|
|
|
319
322
|
|
|
320
323
|
const entry: PreviewServer = { type: 'static', server, port, dir, startedAt: Date.now(), log: '' }
|
|
321
324
|
servers.set(key, entry)
|
|
322
|
-
|
|
325
|
+
log.info(TAG, `Started static server for ${dir} on port ${port}`)
|
|
323
326
|
|
|
324
327
|
return NextResponse.json(buildResponse(entry))
|
|
325
328
|
|
|
@@ -332,7 +335,7 @@ export async function POST(req: Request) {
|
|
|
332
335
|
}
|
|
333
336
|
if (srv.server) srv.server.close()
|
|
334
337
|
servers.delete(key)
|
|
335
|
-
|
|
338
|
+
log.info(TAG, `Stopped server for ${launch.launchDir}`)
|
|
336
339
|
}
|
|
337
340
|
return NextResponse.json({ running: false, dir: launch.launchDir })
|
|
338
341
|
|
|
@@ -1,65 +1,26 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { ensureProjectWorkspace, normalizeProjectPatchInput } from '@/lib/server/project-utils'
|
|
5
|
-
import { notify } from '@/lib/server/ws-hub'
|
|
2
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
|
+
import { deleteProjectAndDetachReferences, getProject, updateProject } from '@/lib/server/projects/project-service'
|
|
6
4
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
7
5
|
|
|
8
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
-
const ops: CollectionOps<any> = { load: loadProjects, save: saveProjects, deleteFn: deleteProject, topic: 'projects' }
|
|
10
|
-
|
|
11
6
|
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
12
7
|
const { id } = await params
|
|
13
|
-
const
|
|
14
|
-
if (!
|
|
15
|
-
return NextResponse.json(
|
|
8
|
+
const project = getProject(id)
|
|
9
|
+
if (!project) return notFound()
|
|
10
|
+
return NextResponse.json(project)
|
|
16
11
|
}
|
|
17
12
|
|
|
18
13
|
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
19
14
|
const { id } = await params
|
|
20
15
|
const { data: body, error } = await safeParseBody<Record<string, unknown>>(req)
|
|
21
16
|
if (error) return error
|
|
22
|
-
const result =
|
|
23
|
-
const patch = normalizeProjectPatchInput(body && typeof body === 'object' ? body as Record<string, unknown> : {})
|
|
24
|
-
Object.assign(project, patch, { updatedAt: Date.now() })
|
|
25
|
-
delete (project as Record<string, unknown>).id
|
|
26
|
-
project.id = id
|
|
27
|
-
return project
|
|
28
|
-
})
|
|
17
|
+
const result = updateProject(id, body && typeof body === 'object' ? body : {})
|
|
29
18
|
if (!result) return notFound()
|
|
30
|
-
ensureProjectWorkspace(id, result.name)
|
|
31
19
|
return NextResponse.json(result)
|
|
32
20
|
}
|
|
33
21
|
|
|
34
22
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
35
23
|
const { id } = await params
|
|
36
|
-
if (!
|
|
37
|
-
|
|
38
|
-
// Clear projectId from referencing entities
|
|
39
|
-
const clearProjectId = (load: () => Record<string, Record<string, unknown>>, save: (d: Record<string, Record<string, unknown>>) => void, topic: string) => {
|
|
40
|
-
const items = load()
|
|
41
|
-
let changed = false
|
|
42
|
-
for (const item of Object.values(items)) {
|
|
43
|
-
if (item.projectId === id) {
|
|
44
|
-
item.projectId = undefined
|
|
45
|
-
changed = true
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (changed) {
|
|
49
|
-
save(items)
|
|
50
|
-
notify(topic)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
clearProjectId(
|
|
55
|
-
loadAgents as unknown as () => Record<string, Record<string, unknown>>,
|
|
56
|
-
saveAgents as unknown as (data: Record<string, Record<string, unknown>>) => void,
|
|
57
|
-
'agents',
|
|
58
|
-
)
|
|
59
|
-
clearProjectId(loadTasks as unknown as () => Record<string, Record<string, unknown>>, saveTasks as unknown as (data: Record<string, Record<string, unknown>>) => void, 'tasks')
|
|
60
|
-
clearProjectId(loadSchedules as unknown as () => Record<string, Record<string, unknown>>, saveSchedules as unknown as (data: Record<string, Record<string, unknown>>) => void, 'schedules')
|
|
61
|
-
clearProjectId(loadSkills as unknown as () => Record<string, Record<string, unknown>>, saveSkills as unknown as (data: Record<string, Record<string, unknown>>) => void, 'skills')
|
|
62
|
-
clearProjectId(loadSecrets as unknown as () => Record<string, Record<string, unknown>>, saveSecrets as unknown as (data: Record<string, Record<string, unknown>>) => void, 'secrets')
|
|
63
|
-
|
|
24
|
+
if (!deleteProjectAndDetachReferences(id)) return notFound()
|
|
64
25
|
return NextResponse.json({ ok: true })
|
|
65
26
|
}
|