@swarmclawai/swarmclaw 1.2.6 → 1.2.9
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 +54 -23
- package/next.config.ts +1 -0
- package/package.json +4 -3
- package/scripts/easy-setup.mjs +1 -1
- package/scripts/postinstall.mjs +1 -1
- package/skills/swarmclaw.md +115 -0
- package/skills/tools/browser.md +131 -0
- package/skills/tools/execute.md +98 -0
- package/skills/tools/files.md +98 -0
- package/skills/tools/memory.md +104 -0
- package/skills/tools/platform.md +144 -0
- package/skills/tools/skills.md +83 -0
- package/src/app/agents/[id]/page.tsx +1 -18
- package/src/app/api/agents/thread-route.test.ts +0 -1
- package/src/app/api/approvals/route.test.ts +6 -22
- package/src/app/api/chats/[id]/messages/route.ts +23 -19
- package/src/app/api/chats/messages-route.test.ts +105 -51
- package/src/app/api/connectors/route.ts +2 -2
- package/src/app/api/mcp-servers/[id]/test/route.ts +3 -2
- package/src/app/api/openclaw/deploy/route.ts +2 -0
- package/src/app/api/portability/export/route.ts +8 -0
- package/src/app/api/portability/import/route.test.ts +80 -0
- package/src/app/api/portability/import/route.ts +28 -0
- package/src/app/api/settings/route.ts +0 -2
- package/src/app/api/setup/doctor/route.ts +4 -4
- package/src/app/api/wallets/[id]/route.ts +15 -157
- package/src/app/api/wallets/generate/route.ts +22 -0
- package/src/app/api/wallets/route.test.ts +147 -0
- package/src/app/api/wallets/route.ts +13 -95
- package/src/app/autonomy/page.tsx +2 -57
- package/src/app/protocols/page.tsx +2 -21
- package/src/app/settings/page.tsx +0 -9
- package/src/app/wallets/page.tsx +105 -5
- package/src/cli/index.js +21 -33
- package/src/cli/spec.js +19 -30
- package/src/components/agents/agent-chat-list.tsx +23 -1
- package/src/components/agents/agent-sheet.tsx +2 -40
- package/src/components/agents/inspector-panel.tsx +165 -131
- package/src/components/chat/chat-area.tsx +38 -9
- package/src/components/chat/chat-card.tsx +0 -31
- package/src/components/chat/message-bubble.tsx +1 -108
- package/src/components/chat/message-list.tsx +33 -19
- package/src/components/connectors/connector-sheet.tsx +25 -1
- package/src/components/gateways/gateway-sheet.tsx +5 -2
- package/src/components/layout/sidebar-rail.tsx +6 -10
- package/src/components/projects/project-detail.tsx +3 -35
- package/src/components/projects/tabs/overview-tab.tsx +3 -59
- package/src/components/projects/tabs/work-tab.tsx +7 -77
- package/src/components/protocols/structured-session-launcher.tsx +1 -22
- package/src/components/shared/connector-platform-icon.tsx +1 -0
- package/src/components/tasks/task-card.tsx +4 -34
- package/src/components/tasks/task-sheet.tsx +6 -36
- package/src/components/wallets/wallet-list.tsx +150 -0
- package/src/lib/agent-execute-defaults.test.ts +24 -0
- package/src/lib/agent-execute-defaults.ts +62 -0
- package/src/lib/app/navigation.test.ts +0 -13
- package/src/lib/app/navigation.ts +2 -7
- package/src/lib/app/view-constants.ts +14 -19
- package/src/lib/chat/queued-message-queue.test.ts +134 -1
- package/src/lib/chat/queued-message-queue.ts +77 -2
- package/src/lib/server/agents/agent-service.ts +5 -0
- package/src/lib/server/agents/agent-thread-session.ts +0 -1
- package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
- package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
- package/src/lib/server/agents/delegation-jobs.ts +0 -25
- package/src/lib/server/agents/main-agent-loop.ts +1 -49
- package/src/lib/server/agents/subagent-runtime.ts +0 -1
- package/src/lib/server/approval-match.ts +0 -85
- package/src/lib/server/approvals.test.ts +6 -6
- package/src/lib/server/approvals.ts +0 -6
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
- package/src/lib/server/builtin-extensions.ts +1 -2
- package/src/lib/server/capability-router.test.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +1 -1
- package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +15 -14
- package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-utils.ts +2 -4
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +81 -64
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -0
- package/src/lib/server/chat-execution/continuation-evaluator.ts +8 -0
- package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
- package/src/lib/server/chat-execution/memory-mutation-tools.ts +1 -1
- package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
- package/src/lib/server/chat-execution/message-classifier.ts +11 -16
- package/src/lib/server/chat-execution/prompt-builder.test.ts +27 -0
- package/src/lib/server/chat-execution/prompt-builder.ts +14 -31
- package/src/lib/server/chat-execution/prompt-mode.test.ts +24 -0
- package/src/lib/server/chat-execution/prompt-mode.ts +5 -1
- package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
- package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
- package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
- package/src/lib/server/chat-execution/stream-agent-chat.test.ts +13 -126
- package/src/lib/server/chat-execution/stream-agent-chat.ts +46 -21
- package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
- package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
- package/src/lib/server/chatrooms/chatroom-routing.test.ts +4 -0
- package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
- package/src/lib/server/chats/chat-session-service.ts +3 -5
- package/src/lib/server/connectors/connector-inbound.ts +0 -1
- package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
- package/src/lib/server/connectors/connector-service.ts +39 -9
- package/src/lib/server/connectors/discord.ts +2 -2
- package/src/lib/server/connectors/matrix.ts +3 -2
- package/src/lib/server/connectors/signal.ts +5 -4
- package/src/lib/server/connectors/slack.ts +10 -9
- package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
- package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
- package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
- package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
- package/src/lib/server/connectors/swarmdock-tasks.ts +119 -0
- package/src/lib/server/connectors/swarmdock.ts +255 -0
- package/src/lib/server/connectors/teams.ts +3 -2
- package/src/lib/server/connectors/telegram.ts +4 -4
- package/src/lib/server/connectors/whatsapp.ts +2 -2
- package/src/lib/server/daemon/controller.ts +7 -0
- package/src/lib/server/execution-brief.test.ts +2 -25
- package/src/lib/server/execution-brief.ts +12 -35
- package/src/lib/server/execution-engine/task-attempt.ts +0 -1
- package/src/lib/server/gateways/gateway-profile-service.ts +19 -1
- package/src/lib/server/messages/message-repository.test.ts +70 -0
- package/src/lib/server/messages/message-repository.ts +11 -6
- package/src/lib/server/openclaw/deploy.ts +32 -2
- package/src/lib/server/persistence/storage-context.ts +0 -5
- package/src/lib/server/plugins-advanced.test.ts +1 -2
- package/src/lib/server/portability/export.ts +109 -0
- package/src/lib/server/portability/import.ts +159 -0
- package/src/lib/server/protocols/protocol-normalization.ts +0 -4
- package/src/lib/server/protocols/protocol-queries.ts +0 -6
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
- package/src/lib/server/protocols/protocol-service.ts +0 -1
- package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
- package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
- package/src/lib/server/protocols/protocol-swarm.ts +0 -2
- package/src/lib/server/protocols/protocol-types.ts +0 -2
- package/src/lib/server/provider-health.ts +1 -10
- package/src/lib/server/runtime/daemon-state/core.ts +0 -9
- package/src/lib/server/runtime/daemon-state.test.ts +0 -35
- package/src/lib/server/runtime/heartbeat-service.ts +3 -23
- package/src/lib/server/runtime/process-manager.ts +13 -9
- package/src/lib/server/runtime/queue/core.ts +11 -33
- package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
- package/src/lib/server/runtime/scheduler.ts +0 -13
- package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
- package/src/lib/server/runtime/session-run-manager/queries.ts +15 -1
- package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
- package/src/lib/server/runtime/session-run-manager.test.ts +58 -28
- package/src/lib/server/sandbox/session-runtime.test.ts +18 -1
- package/src/lib/server/sandbox/session-runtime.ts +40 -28
- package/src/lib/server/session-tools/autonomy-tools.test.ts +7 -9
- package/src/lib/server/session-tools/context.ts +1 -1
- package/src/lib/server/session-tools/credential-env.ts +109 -0
- package/src/lib/server/session-tools/crud.ts +3 -17
- package/src/lib/server/session-tools/delegate.ts +0 -4
- package/src/lib/server/session-tools/edit_file.ts +3 -2
- package/src/lib/server/session-tools/execute.test.ts +58 -0
- package/src/lib/server/session-tools/execute.ts +334 -0
- package/src/lib/server/session-tools/files-tool.ts +635 -0
- package/src/lib/server/session-tools/index.ts +14 -8
- package/src/lib/server/session-tools/memory-tool.ts +242 -0
- package/src/lib/server/session-tools/memory.ts +1 -1
- package/src/lib/server/session-tools/openclaw-nodes.ts +3 -2
- package/src/lib/server/session-tools/openclaw-workspace.ts +3 -2
- package/src/lib/server/session-tools/platform-tool.ts +617 -0
- package/src/lib/server/session-tools/session-info.ts +3 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +3 -4
- package/src/lib/server/session-tools/shell.ts +7 -122
- package/src/lib/server/session-tools/skills-tool.ts +396 -0
- package/src/lib/server/session-tools/team-context.ts +0 -3
- package/src/lib/server/session-tools/web.ts +2 -2
- package/src/lib/server/storage-normalization.ts +10 -0
- package/src/lib/server/storage.ts +18 -45
- package/src/lib/server/tasks/task-checkout.ts +59 -0
- package/src/lib/server/tasks/task-lifecycle.ts +2 -0
- package/src/lib/server/tasks/task-route-service.ts +4 -26
- package/src/lib/server/tasks/task-service.ts +0 -7
- package/src/lib/server/tool-aliases.ts +2 -2
- package/src/lib/server/tool-capability-policy-advanced.test.ts +13 -6
- package/src/lib/server/tool-capability-policy.test.ts +2 -1
- package/src/lib/server/tool-capability-policy.ts +60 -35
- package/src/lib/server/tool-planning.ts +11 -12
- package/src/lib/server/universal-tool-access.ts +0 -1
- package/src/lib/server/wallets/wallet-crypto.ts +33 -0
- package/src/lib/server/wallets/wallet-repository.ts +24 -0
- package/src/lib/server/wallets/wallet-service.ts +119 -0
- package/src/lib/server/working-state/extraction.ts +8 -42
- package/src/lib/server/working-state/normalization.ts +10 -103
- package/src/lib/server/working-state/service.ts +12 -21
- package/src/lib/setup-defaults.ts +5 -0
- package/src/lib/strip-internal-metadata.test.ts +1 -1
- package/src/lib/strip-internal-metadata.ts +1 -1
- package/src/lib/tool-definitions.ts +1 -1
- package/src/lib/validation/schemas.test.ts +16 -0
- package/src/lib/validation/schemas.ts +49 -2
- package/src/stores/slices/data-slice.ts +5 -1
- package/src/stores/slices/ui-slice.ts +0 -4
- package/src/stores/use-chat-store.test.ts +231 -0
- package/src/stores/use-chat-store.ts +62 -13
- package/src/types/agent.ts +264 -0
- package/src/types/app-settings.ts +173 -0
- package/src/types/approval.ts +25 -0
- package/src/types/connector.ts +188 -0
- package/src/types/extension.ts +386 -0
- package/src/types/index.ts +16 -3555
- package/src/types/message.ts +56 -0
- package/src/types/misc.ts +737 -0
- package/src/types/protocol.ts +420 -0
- package/src/types/provider.ts +52 -0
- package/src/types/run.ts +180 -0
- package/src/types/schedule.ts +59 -0
- package/src/types/session.ts +215 -0
- package/src/types/skill.ts +157 -0
- package/src/types/swarmdock.ts +29 -0
- package/src/types/task.ts +144 -0
- package/src/types/working-state.ts +204 -0
- package/src/views/settings/section-heartbeat.tsx +2 -2
- package/src/views/settings/section-runtime-loop.tsx +0 -14
- package/src/app/api/canvas/[sessionId]/route.ts +0 -35
- package/src/app/api/missions/[id]/actions/route.ts +0 -31
- package/src/app/api/missions/[id]/events/route.ts +0 -14
- package/src/app/api/missions/[id]/route.ts +0 -10
- package/src/app/api/missions/route.test.ts +0 -244
- package/src/app/api/missions/route.ts +0 -57
- package/src/app/api/wallets/[id]/approve/route.ts +0 -79
- package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
- package/src/app/api/wallets/[id]/send/route.ts +0 -113
- package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
- package/src/app/missions/[id]/page.tsx +0 -3
- package/src/app/missions/page.tsx +0 -685
- package/src/components/canvas/canvas-panel.tsx +0 -267
- package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
- package/src/components/wallets/wallet-panel.tsx +0 -1010
- package/src/components/wallets/wallet-section.tsx +0 -260
- package/src/features/missions/queries.ts +0 -23
- package/src/lib/canvas-content.test.ts +0 -360
- package/src/lib/canvas-content.ts +0 -198
- package/src/lib/server/canvas-content.test.ts +0 -32
- package/src/lib/server/canvas-content.ts +0 -6
- package/src/lib/server/ethereum.ts +0 -591
- package/src/lib/server/evm-swap.ts +0 -476
- package/src/lib/server/missions/mission-intent.test.ts +0 -63
- package/src/lib/server/missions/mission-intent.ts +0 -569
- package/src/lib/server/missions/mission-repository.ts +0 -74
- package/src/lib/server/missions/mission-service/actions.ts +0 -6
- package/src/lib/server/missions/mission-service/bindings.ts +0 -9
- package/src/lib/server/missions/mission-service/context.ts +0 -4
- package/src/lib/server/missions/mission-service/core.ts +0 -2271
- package/src/lib/server/missions/mission-service/queries.ts +0 -12
- package/src/lib/server/missions/mission-service/recovery.ts +0 -5
- package/src/lib/server/missions/mission-service/ticks.ts +0 -9
- package/src/lib/server/missions/mission-service.test.ts +0 -888
- package/src/lib/server/missions/mission-service.ts +0 -6
- package/src/lib/server/session-tools/canvas.ts +0 -105
- package/src/lib/server/session-tools/sandbox.ts +0 -281
- package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
- package/src/lib/server/session-tools/wallet.ts +0 -1287
- package/src/lib/server/solana.ts +0 -327
- package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
- package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
- package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
- package/src/lib/server/wallet/wallet-service.test.ts +0 -81
- package/src/lib/server/wallet/wallet-service.ts +0 -225
- package/src/lib/wallet/wallet-transactions.test.ts +0 -75
- package/src/lib/wallet/wallet-transactions.ts +0 -43
- package/src/lib/wallet/wallet.test.ts +0 -333
- package/src/lib/wallet/wallet.ts +0 -183
- package/src/views/settings/section-wallets.tsx +0 -35
|
@@ -3,65 +3,119 @@ import test from 'node:test'
|
|
|
3
3
|
|
|
4
4
|
import { runWithTempDataDir } from '@/lib/server/test-utils/run-with-temp-data-dir'
|
|
5
5
|
|
|
6
|
-
test('
|
|
6
|
+
test('messages route serves and mutates repo-backed transcript history', () => {
|
|
7
7
|
const output = runWithTempDataDir<{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
fullCount: number
|
|
9
|
+
paginatedTexts: string[]
|
|
10
|
+
paginatedStartIndex: number
|
|
11
|
+
paginatedTotal: number
|
|
12
|
+
bookmarkPersisted: boolean
|
|
13
|
+
contextClearCountAfterPost: number
|
|
14
|
+
finalKinds: Array<string | null>
|
|
15
|
+
finalBookmarked: boolean
|
|
16
|
+
blobMessageCount: number
|
|
17
|
+
}>(`
|
|
18
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
19
|
+
const repoMod = await import('@/lib/server/messages/message-repository')
|
|
20
|
+
const routeMod = await import('./src/app/api/chats/[id]/messages/route')
|
|
21
|
+
const storage = storageMod.default || storageMod
|
|
22
|
+
const repo = repoMod.default || repoMod
|
|
23
|
+
const route = routeMod.default || routeMod
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
],
|
|
25
|
+
const now = Date.now()
|
|
26
|
+
storage.saveSessions({
|
|
27
|
+
sess_1: {
|
|
28
|
+
id: 'sess_1',
|
|
29
|
+
name: 'Repo-backed session',
|
|
30
|
+
cwd: process.env.WORKSPACE_DIR,
|
|
31
|
+
user: 'tester',
|
|
32
|
+
provider: 'openai',
|
|
33
|
+
model: 'gpt-5',
|
|
34
|
+
claudeSessionId: null,
|
|
35
|
+
codexThreadId: null,
|
|
36
|
+
opencodeSessionId: null,
|
|
37
|
+
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null },
|
|
38
|
+
messages: [],
|
|
39
|
+
createdAt: now,
|
|
40
|
+
lastActiveAt: now,
|
|
41
|
+
},
|
|
40
42
|
})
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
repo.appendMessage('sess_1', { role: 'user', text: 'hello', time: now })
|
|
45
|
+
repo.appendMessage('sess_1', { role: 'user', text: '', kind: 'context-clear', time: now + 1 })
|
|
46
|
+
repo.appendMessage('sess_1', { role: 'assistant', text: 'welcome back', time: now + 2 })
|
|
47
|
+
storage.patchSession('sess_1', (current) => {
|
|
48
|
+
if (!current) return null
|
|
49
|
+
current.messages = [{ role: 'assistant', text: 'stale blob only', time: now - 10 }]
|
|
50
|
+
return current
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const fullResponse = await route.GET(
|
|
54
|
+
new Request('http://local/api/chats/sess_1/messages'),
|
|
55
|
+
{ params: Promise.resolve({ id: 'sess_1' }) },
|
|
56
|
+
)
|
|
57
|
+
const fullMessages = await fullResponse.json()
|
|
43
58
|
|
|
44
|
-
const
|
|
45
|
-
new Request('http://local/api/chats/
|
|
46
|
-
{ params: Promise.resolve({ id: '
|
|
59
|
+
const paginatedResponse = await route.GET(
|
|
60
|
+
new Request('http://local/api/chats/sess_1/messages?limit=2'),
|
|
61
|
+
{ params: Promise.resolve({ id: 'sess_1' }) },
|
|
47
62
|
)
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
63
|
+
const paginated = await paginatedResponse.json()
|
|
64
|
+
|
|
65
|
+
const bookmarkResponse = await route.PUT(
|
|
66
|
+
new Request('http://local/api/chats/sess_1/messages', {
|
|
67
|
+
method: 'PUT',
|
|
68
|
+
headers: { 'content-type': 'application/json' },
|
|
69
|
+
body: JSON.stringify({ messageIndex: 2, bookmarked: true }),
|
|
70
|
+
}),
|
|
71
|
+
{ params: Promise.resolve({ id: 'sess_1' }) },
|
|
72
|
+
)
|
|
73
|
+
const bookmarked = await bookmarkResponse.json()
|
|
74
|
+
|
|
75
|
+
await route.POST(
|
|
76
|
+
new Request('http://local/api/chats/sess_1/messages', {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: { 'content-type': 'application/json' },
|
|
79
|
+
body: JSON.stringify({ kind: 'context-clear' }),
|
|
80
|
+
}),
|
|
81
|
+
{ params: Promise.resolve({ id: 'sess_1' }) },
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
const afterPost = repo.getMessages('sess_1')
|
|
85
|
+
const contextClearCountAfterPost = afterPost.filter((message) => message.kind === 'context-clear').length
|
|
86
|
+
|
|
87
|
+
await route.DELETE(
|
|
88
|
+
new Request('http://local/api/chats/sess_1/messages', {
|
|
89
|
+
method: 'DELETE',
|
|
90
|
+
headers: { 'content-type': 'application/json' },
|
|
91
|
+
body: JSON.stringify({ messageIndex: 1 }),
|
|
92
|
+
}),
|
|
93
|
+
{ params: Promise.resolve({ id: 'sess_1' }) },
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
const finalMessages = repo.getMessages('sess_1')
|
|
97
|
+
const sessions = storage.loadSessions()
|
|
52
98
|
|
|
53
99
|
console.log(JSON.stringify({
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
100
|
+
fullCount: fullMessages.length,
|
|
101
|
+
paginatedTexts: paginated.messages.map((message) => message.text),
|
|
102
|
+
paginatedStartIndex: paginated.startIndex,
|
|
103
|
+
paginatedTotal: paginated.total,
|
|
104
|
+
bookmarkPersisted: bookmarked.bookmarked === true,
|
|
105
|
+
contextClearCountAfterPost,
|
|
106
|
+
finalKinds: finalMessages.map((message) => message.kind || null),
|
|
107
|
+
finalBookmarked: finalMessages[1]?.bookmarked === true,
|
|
108
|
+
blobMessageCount: Array.isArray(sessions.sess_1.messages) ? sessions.sess_1.messages.length : -1,
|
|
59
109
|
}))
|
|
60
|
-
`, { prefix: 'swarmclaw-
|
|
110
|
+
`, { prefix: 'swarmclaw-messages-route-' })
|
|
61
111
|
|
|
62
|
-
assert.equal(output.
|
|
63
|
-
assert.
|
|
64
|
-
assert.equal(output.
|
|
65
|
-
assert.equal(output.
|
|
66
|
-
assert.equal(output.
|
|
112
|
+
assert.equal(output.fullCount, 3)
|
|
113
|
+
assert.deepEqual(output.paginatedTexts, ['', 'welcome back'])
|
|
114
|
+
assert.equal(output.paginatedStartIndex, 1)
|
|
115
|
+
assert.equal(output.paginatedTotal, 3)
|
|
116
|
+
assert.equal(output.bookmarkPersisted, true)
|
|
117
|
+
assert.equal(output.contextClearCountAfterPost, 2)
|
|
118
|
+
assert.deepEqual(output.finalKinds, [null, null, 'context-clear'])
|
|
119
|
+
assert.equal(output.finalBookmarked, true)
|
|
120
|
+
assert.equal(output.blobMessageCount, 1)
|
|
67
121
|
})
|
|
@@ -6,10 +6,10 @@ import { z } from 'zod'
|
|
|
6
6
|
import {
|
|
7
7
|
autoStartConnectorIfNeeded,
|
|
8
8
|
createConnector,
|
|
9
|
+
getConnectorWithRuntime,
|
|
9
10
|
listConnectorsWithRuntime,
|
|
10
11
|
} from '@/lib/server/connectors/connector-service'
|
|
11
12
|
import { ensureDaemonProcessRunning } from '@/lib/server/daemon/controller'
|
|
12
|
-
import { loadConnector } from '@/lib/server/connectors/connector-repository'
|
|
13
13
|
export const dynamic = 'force-dynamic'
|
|
14
14
|
|
|
15
15
|
export async function GET() {
|
|
@@ -29,5 +29,5 @@ export async function POST(req: Request) {
|
|
|
29
29
|
}
|
|
30
30
|
const connector = createConnector(parsed.data as unknown as Record<string, unknown>)
|
|
31
31
|
await autoStartConnectorIfNeeded(connector, parsed.data as unknown as Record<string, unknown>)
|
|
32
|
-
return NextResponse.json(
|
|
32
|
+
return NextResponse.json(await getConnectorWithRuntime(connector.id) || connector)
|
|
33
33
|
}
|
|
@@ -2,6 +2,7 @@ import { NextResponse } from 'next/server'
|
|
|
2
2
|
import { loadMcpServers } from '@/lib/server/storage'
|
|
3
3
|
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
4
|
import { connectMcpServer, mcpToolsToLangChain, disconnectMcpServer } from '@/lib/server/mcp-client'
|
|
5
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
5
6
|
|
|
6
7
|
export async function POST(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
7
8
|
const { id } = await params
|
|
@@ -15,9 +16,9 @@ export async function POST(_req: Request, { params }: { params: Promise<{ id: st
|
|
|
15
16
|
const toolNames = tools.map((t: any) => t.name)
|
|
16
17
|
await disconnectMcpServer(client, transport)
|
|
17
18
|
return NextResponse.json({ ok: true, tools: toolNames })
|
|
18
|
-
} catch (err:
|
|
19
|
+
} catch (err: unknown) {
|
|
19
20
|
return NextResponse.json(
|
|
20
|
-
{ ok: false, error: err
|
|
21
|
+
{ ok: false, error: errorMessage(err) || 'Connection failed' },
|
|
21
22
|
{ status: 500 }
|
|
22
23
|
)
|
|
23
24
|
}
|
|
@@ -129,6 +129,7 @@ export async function POST(req: Request) {
|
|
|
129
129
|
locals: result.locals,
|
|
130
130
|
localPrimaryId: result.locals.find((item) => item.isPrimary)?.id || result.local.id,
|
|
131
131
|
token: result.token,
|
|
132
|
+
gatewayProfileId: result.gatewayProfileId,
|
|
132
133
|
})
|
|
133
134
|
}
|
|
134
135
|
|
|
@@ -156,6 +157,7 @@ export async function POST(req: Request) {
|
|
|
156
157
|
locals: result.locals,
|
|
157
158
|
localPrimaryId: result.locals.find((item) => item.isPrimary)?.id || result.local.id,
|
|
158
159
|
token: result.token,
|
|
160
|
+
gatewayProfileId: result.gatewayProfileId,
|
|
159
161
|
})
|
|
160
162
|
}
|
|
161
163
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { spawnSync } from 'node:child_process'
|
|
6
|
+
import test from 'node:test'
|
|
7
|
+
|
|
8
|
+
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../../../../..')
|
|
9
|
+
|
|
10
|
+
function runWithTempDataDir(script: string) {
|
|
11
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-portability-import-'))
|
|
12
|
+
try {
|
|
13
|
+
const result = spawnSync(process.execPath, ['--import', 'tsx', '--input-type=module', '--eval', script], {
|
|
14
|
+
cwd: repoRoot,
|
|
15
|
+
env: {
|
|
16
|
+
...process.env,
|
|
17
|
+
DATA_DIR: path.join(tempDir, 'data'),
|
|
18
|
+
WORKSPACE_DIR: path.join(tempDir, 'workspace'),
|
|
19
|
+
},
|
|
20
|
+
encoding: 'utf-8',
|
|
21
|
+
})
|
|
22
|
+
assert.equal(result.status, 0, result.stderr || result.stdout || 'subprocess failed')
|
|
23
|
+
const lines = (result.stdout || '')
|
|
24
|
+
.trim()
|
|
25
|
+
.split('\n')
|
|
26
|
+
.map((line) => line.trim())
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
const jsonLine = [...lines].reverse().find((line) => line.startsWith('{'))
|
|
29
|
+
return JSON.parse(jsonLine || '{}')
|
|
30
|
+
} finally {
|
|
31
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test('POST /api/portability/import validates manifest arrays before importing', () => {
|
|
36
|
+
const output = runWithTempDataDir(`
|
|
37
|
+
const routeMod = await import('./src/app/api/portability/import/route')
|
|
38
|
+
const route = routeMod.default || routeMod
|
|
39
|
+
|
|
40
|
+
const invalidResponse = await route.POST(new Request('http://local/api/portability/import', {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: { 'content-type': 'application/json' },
|
|
43
|
+
body: JSON.stringify({ formatVersion: 1, agents: [] }),
|
|
44
|
+
}))
|
|
45
|
+
const invalidPayload = await invalidResponse.json()
|
|
46
|
+
|
|
47
|
+
const validResponse = await route.POST(new Request('http://local/api/portability/import', {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: { 'content-type': 'application/json' },
|
|
50
|
+
body: JSON.stringify({
|
|
51
|
+
formatVersion: 1,
|
|
52
|
+
exportedAt: '2026-03-29T00:00:00.000Z',
|
|
53
|
+
agents: [],
|
|
54
|
+
skills: [],
|
|
55
|
+
schedules: [],
|
|
56
|
+
}),
|
|
57
|
+
}))
|
|
58
|
+
const validPayload = await validResponse.json()
|
|
59
|
+
|
|
60
|
+
console.log(JSON.stringify({
|
|
61
|
+
invalidStatus: invalidResponse.status,
|
|
62
|
+
invalidError: invalidPayload?.error || null,
|
|
63
|
+
invalidPaths: Array.isArray(invalidPayload?.issues)
|
|
64
|
+
? invalidPayload.issues.map((issue) => issue.path).sort()
|
|
65
|
+
: [],
|
|
66
|
+
validStatus: validResponse.status,
|
|
67
|
+
validAgentsCreated: validPayload?.agents?.created ?? null,
|
|
68
|
+
validSkillsCreated: validPayload?.skills?.created ?? null,
|
|
69
|
+
validSchedulesCreated: validPayload?.schedules?.created ?? null,
|
|
70
|
+
}))
|
|
71
|
+
`)
|
|
72
|
+
|
|
73
|
+
assert.equal(output.invalidStatus, 400)
|
|
74
|
+
assert.equal(output.invalidError, 'Validation failed')
|
|
75
|
+
assert.deepEqual(output.invalidPaths, ['schedules', 'skills'])
|
|
76
|
+
assert.equal(output.validStatus, 200)
|
|
77
|
+
assert.equal(output.validAgentsCreated, 0)
|
|
78
|
+
assert.equal(output.validSkillsCreated, 0)
|
|
79
|
+
assert.equal(output.validSchedulesCreated, 0)
|
|
80
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
3
|
+
import { importConfig } from '@/lib/server/portability/import'
|
|
4
|
+
import type { PortableManifest } from '@/lib/server/portability/export'
|
|
5
|
+
import { PortableManifestSchema, formatZodError } from '@/lib/validation/schemas'
|
|
6
|
+
import { z } from 'zod'
|
|
7
|
+
export const dynamic = 'force-dynamic'
|
|
8
|
+
|
|
9
|
+
export async function POST(req: Request) {
|
|
10
|
+
const { data: raw, error } = await safeParseBody(req)
|
|
11
|
+
if (error) return error
|
|
12
|
+
|
|
13
|
+
const parsed = PortableManifestSchema.safeParse(raw)
|
|
14
|
+
if (!parsed.success) {
|
|
15
|
+
return NextResponse.json(formatZodError(parsed.error as z.ZodError), { status: 400 })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const result = importConfig(parsed.data as PortableManifest)
|
|
20
|
+
return NextResponse.json(result)
|
|
21
|
+
} catch (err) {
|
|
22
|
+
const message = err instanceof Error ? err.message : 'Failed to import manifest'
|
|
23
|
+
if (/^Unsupported format version /i.test(message)) {
|
|
24
|
+
return NextResponse.json({ error: message }, { status: 400 })
|
|
25
|
+
}
|
|
26
|
+
return NextResponse.json({ error: message }, { status: 500 })
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -141,11 +141,9 @@ export async function PUT(req: Request) {
|
|
|
141
141
|
settings.taskQualityGateRequireReport = parseBoolSetting(settings.taskQualityGateRequireReport, false)
|
|
142
142
|
settings.taskManagementEnabled = parseBoolSetting(settings.taskManagementEnabled, true)
|
|
143
143
|
settings.projectManagementEnabled = parseBoolSetting(settings.projectManagementEnabled, true)
|
|
144
|
-
settings.walletApprovalsEnabled = parseBoolSetting(settings.walletApprovalsEnabled, true)
|
|
145
144
|
settings.integrityMonitorEnabled = parseBoolSetting(settings.integrityMonitorEnabled, true)
|
|
146
145
|
settings.daemonAutostartEnabled = parseBoolSetting(settings.daemonAutostartEnabled, true)
|
|
147
146
|
settings.autonomyResumeApprovalsEnabled = parseBoolSetting(settings.autonomyResumeApprovalsEnabled, false)
|
|
148
|
-
settings.missionHumanLoopEnabled = parseBoolSetting(settings.missionHumanLoopEnabled, false)
|
|
149
147
|
settings.untrustedContentGuardMode = parseGuardMode(settings.untrustedContentGuardMode)
|
|
150
148
|
settings.sessionResetMode = settings.sessionResetMode === 'daily' ? 'daily' : settings.sessionResetMode === 'idle' ? 'idle' : null
|
|
151
149
|
settings.whatsappApprovedContacts = normalizeWhatsAppApprovedContacts(settings.whatsappApprovedContacts)
|
|
@@ -217,14 +217,14 @@ export async function GET(req: Request) {
|
|
|
217
217
|
pushCheck(
|
|
218
218
|
checks,
|
|
219
219
|
'docker',
|
|
220
|
-
'Docker (sandbox runtime)',
|
|
220
|
+
'Docker (browser sandbox runtime)',
|
|
221
221
|
docker.available ? 'pass' : 'warn',
|
|
222
222
|
docker.available
|
|
223
|
-
? `Docker ${docker.version || ''} is available for
|
|
224
|
-
: 'Docker is not available. SwarmClaw will
|
|
223
|
+
? `Docker ${docker.version || ''} is available for sandbox browser execution.`.trim()
|
|
224
|
+
: 'Docker is not available. SwarmClaw will use the host Playwright runtime unless Docker Desktop is installed.',
|
|
225
225
|
)
|
|
226
226
|
if (!docker.available) {
|
|
227
|
-
actions.push('Install Docker Desktop if you want
|
|
227
|
+
actions.push('Install Docker Desktop if you want Playwright browser sessions to use the sandbox browser runtime.')
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
const gitRootCheck = run('git', ['rev-parse', '--is-inside-work-tree'], 4_000)
|
|
@@ -1,174 +1,32 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
3
|
-
import {
|
|
4
|
-
import { notify } from '@/lib/server/ws-hub'
|
|
5
|
-
import { getWalletLimitAtomic, normalizeAtomicString } from '@/lib/wallet/wallet'
|
|
6
|
-
import type { AgentWallet, WalletAssetBalance, WalletPortfolioSummary } from '@/types'
|
|
7
|
-
import { buildEmptyWalletPortfolio, getCachedWalletPortfolio } from '@/lib/server/wallet/wallet-portfolio'
|
|
8
|
-
import {
|
|
9
|
-
getAgentActiveWalletId,
|
|
10
|
-
getWalletPortfolioSnapshot,
|
|
11
|
-
linkWalletToAgent,
|
|
12
|
-
setAgentActiveWallet,
|
|
13
|
-
stripWalletPrivateKey,
|
|
14
|
-
unlinkWalletFromAgent,
|
|
15
|
-
} from '@/lib/server/wallet/wallet-service'
|
|
3
|
+
import { getWalletSafe, removeWallet, updateWallet } from '@/lib/server/wallets/wallet-service'
|
|
16
4
|
export const dynamic = 'force-dynamic'
|
|
17
|
-
const WALLET_DETAIL_PORTFOLIO_TIMEOUT_MS = 2500
|
|
18
5
|
|
|
19
|
-
function
|
|
20
|
-
wallet: AgentWallet,
|
|
21
|
-
portfolio: {
|
|
22
|
-
balanceAtomic: string
|
|
23
|
-
balanceFormatted: string
|
|
24
|
-
balanceSymbol: string
|
|
25
|
-
balanceDisplay: string
|
|
26
|
-
balanceLamports?: number
|
|
27
|
-
balanceSol?: number
|
|
28
|
-
assets: WalletAssetBalance[]
|
|
29
|
-
summary: WalletPortfolioSummary
|
|
30
|
-
},
|
|
31
|
-
isActive: boolean,
|
|
32
|
-
) {
|
|
33
|
-
return {
|
|
34
|
-
...stripWalletPrivateKey(wallet as unknown as Record<string, unknown>),
|
|
35
|
-
balanceAtomic: portfolio.balanceAtomic,
|
|
36
|
-
balanceFormatted: portfolio.balanceFormatted,
|
|
37
|
-
balanceSymbol: portfolio.balanceSymbol,
|
|
38
|
-
balanceDisplay: portfolio.balanceDisplay,
|
|
39
|
-
balanceLamports: portfolio.balanceLamports,
|
|
40
|
-
balanceSol: portfolio.balanceSol,
|
|
41
|
-
assets: portfolio.assets,
|
|
42
|
-
portfolioSummary: portfolio.summary,
|
|
43
|
-
isActive,
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
|
+
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
48
7
|
const { id } = await params
|
|
49
|
-
const
|
|
50
|
-
const wallet = wallets[id]
|
|
8
|
+
const wallet = getWalletSafe(id)
|
|
51
9
|
if (!wallet) return NextResponse.json({ error: 'Wallet not found' }, { status: 404 })
|
|
52
|
-
|
|
53
|
-
const url = new URL(req.url)
|
|
54
|
-
const cachedOnly = url.searchParams.get('cached') === '1'
|
|
55
|
-
const agents = loadAgents()
|
|
56
|
-
const isActive = getAgentActiveWalletId(agents[wallet.agentId]) === wallet.id
|
|
57
|
-
|
|
58
|
-
if (cachedOnly) {
|
|
59
|
-
const cached = getCachedWalletPortfolio(wallet)
|
|
60
|
-
if (!cached) {
|
|
61
|
-
return NextResponse.json({
|
|
62
|
-
...stripWalletPrivateKey(wallet as unknown as Record<string, unknown>),
|
|
63
|
-
isActive,
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
return NextResponse.json(withPortfolio(wallet, cached, isActive))
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let portfolio = buildEmptyWalletPortfolio(wallet)
|
|
70
|
-
try {
|
|
71
|
-
portfolio = await getWalletPortfolioSnapshot(wallet, {
|
|
72
|
-
timeoutMs: WALLET_DETAIL_PORTFOLIO_TIMEOUT_MS,
|
|
73
|
-
allowStale: true,
|
|
74
|
-
})
|
|
75
|
-
} catch {
|
|
76
|
-
// RPC failure — return 0
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return NextResponse.json(withPortfolio(wallet, portfolio, isActive))
|
|
10
|
+
return NextResponse.json(wallet)
|
|
80
11
|
}
|
|
81
12
|
|
|
82
13
|
export async function PATCH(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
83
14
|
const { id } = await params
|
|
84
|
-
const wallets = loadWallets() as Record<string, AgentWallet>
|
|
85
|
-
const wallet = wallets[id]
|
|
86
|
-
if (!wallet) return NextResponse.json({ error: 'Wallet not found' }, { status: 404 })
|
|
87
|
-
|
|
88
15
|
const { data: body, error } = await safeParseBody(req)
|
|
89
16
|
if (error) return error
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (typeof body.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const allWallets = loadWallets() as Record<string, AgentWallet>
|
|
99
|
-
const conflict = Object.values(allWallets).find((w) => w.agentId === body.agentId && w.id !== id && w.chain === wallet.chain)
|
|
100
|
-
if (conflict) return NextResponse.json({ error: `Target agent already has a ${wallet.chain} wallet` }, { status: 409 })
|
|
101
|
-
|
|
102
|
-
const oldAgent = loadAgent(wallet.agentId)
|
|
103
|
-
if (oldAgent) {
|
|
104
|
-
unlinkWalletFromAgent(oldAgent as any, id)
|
|
105
|
-
oldAgent.updatedAt = Date.now()
|
|
106
|
-
upsertAgent(wallet.agentId, oldAgent)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
linkWalletToAgent(newAgent as any, id, shouldMakeActive || getAgentActiveWalletId(newAgent as any) == null)
|
|
110
|
-
newAgent.updatedAt = Date.now()
|
|
111
|
-
upsertAgent(body.agentId, newAgent)
|
|
112
|
-
notify('agents')
|
|
113
|
-
|
|
114
|
-
wallet.agentId = body.agentId
|
|
115
|
-
} else if (shouldMakeActive) {
|
|
116
|
-
const agent = loadAgent(wallet.agentId)
|
|
117
|
-
if (agent) {
|
|
118
|
-
setAgentActiveWallet(agent as any, id)
|
|
119
|
-
agent.updatedAt = Date.now()
|
|
120
|
-
upsertAgent(wallet.agentId, agent)
|
|
121
|
-
notify('agents')
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (body.label !== undefined) wallet.label = body.label as string | undefined
|
|
126
|
-
if (body.spendingLimitAtomic !== undefined || body.spendingLimitLamports !== undefined) {
|
|
127
|
-
wallet.spendingLimitAtomic = normalizeAtomicString(body.spendingLimitAtomic ?? body.spendingLimitLamports, getWalletLimitAtomic(wallet, 'perTx'))
|
|
128
|
-
}
|
|
129
|
-
if (body.dailyLimitAtomic !== undefined || body.dailyLimitLamports !== undefined) {
|
|
130
|
-
wallet.dailyLimitAtomic = normalizeAtomicString(body.dailyLimitAtomic ?? body.dailyLimitLamports, getWalletLimitAtomic(wallet, 'daily'))
|
|
131
|
-
}
|
|
132
|
-
if (typeof body.requireApproval === 'boolean') wallet.requireApproval = body.requireApproval
|
|
133
|
-
wallet.updatedAt = Date.now()
|
|
134
|
-
|
|
135
|
-
upsertWallet(id, wallet)
|
|
136
|
-
notify('wallets')
|
|
137
|
-
|
|
138
|
-
return NextResponse.json(stripWalletPrivateKey(wallet as unknown as Record<string, unknown>))
|
|
17
|
+
const patch: Record<string, unknown> = {}
|
|
18
|
+
if (typeof body.label === 'string') patch.label = body.label
|
|
19
|
+
if (typeof body.spendingLimitUsdc === 'string' || body.spendingLimitUsdc === null) patch.spendingLimitUsdc = body.spendingLimitUsdc
|
|
20
|
+
if (typeof body.dailyLimitUsdc === 'string' || body.dailyLimitUsdc === null) patch.dailyLimitUsdc = body.dailyLimitUsdc
|
|
21
|
+
if (typeof body.requireApproval === 'boolean') patch.requireApproval = body.requireApproval
|
|
22
|
+
const updated = updateWallet(id, patch)
|
|
23
|
+
if (!updated) return NextResponse.json({ error: 'Wallet not found' }, { status: 404 })
|
|
24
|
+
return NextResponse.json(updated)
|
|
139
25
|
}
|
|
140
26
|
|
|
141
27
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
142
28
|
const { id } = await params
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// Check if balance > 0 and warn
|
|
148
|
-
let portfolio = buildEmptyWalletPortfolio(wallet)
|
|
149
|
-
try {
|
|
150
|
-
portfolio = await getWalletPortfolioSnapshot(wallet, {
|
|
151
|
-
timeoutMs: WALLET_DETAIL_PORTFOLIO_TIMEOUT_MS,
|
|
152
|
-
allowStale: true,
|
|
153
|
-
})
|
|
154
|
-
} catch { /* ignore */ }
|
|
155
|
-
|
|
156
|
-
// Unlink from agent
|
|
157
|
-
const agent = loadAgent(wallet.agentId)
|
|
158
|
-
if (agent) {
|
|
159
|
-
unlinkWalletFromAgent(agent as any, id)
|
|
160
|
-
agent.updatedAt = Date.now()
|
|
161
|
-
upsertAgent(wallet.agentId, agent)
|
|
162
|
-
notify('agents')
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
deleteWalletFromStore(id)
|
|
166
|
-
notify('wallets')
|
|
167
|
-
|
|
168
|
-
return NextResponse.json({
|
|
169
|
-
ok: true,
|
|
170
|
-
warning: portfolio.summary.nonZeroAssets > 0
|
|
171
|
-
? `Wallet still had ${portfolio.summary.nonZeroAssets} asset${portfolio.summary.nonZeroAssets === 1 ? '' : 's'} remaining, including ${portfolio.balanceDisplay}`
|
|
172
|
-
: undefined,
|
|
173
|
-
})
|
|
29
|
+
const deleted = removeWallet(id)
|
|
30
|
+
if (!deleted) return NextResponse.json({ error: 'Wallet not found' }, { status: 404 })
|
|
31
|
+
return NextResponse.json({ ok: true })
|
|
174
32
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
3
|
+
import { generateWallet, WalletServiceError } from '@/lib/server/wallets/wallet-service'
|
|
4
|
+
export const dynamic = 'force-dynamic'
|
|
5
|
+
|
|
6
|
+
export async function POST(req: Request) {
|
|
7
|
+
const { data: body, error } = await safeParseBody(req)
|
|
8
|
+
if (error) return error
|
|
9
|
+
try {
|
|
10
|
+
const wallet = await generateWallet({
|
|
11
|
+
agentId: typeof body.agentId === 'string' ? body.agentId : '',
|
|
12
|
+
label: typeof body.label === 'string' ? body.label : undefined,
|
|
13
|
+
})
|
|
14
|
+
return NextResponse.json(wallet, { status: 201 })
|
|
15
|
+
} catch (err) {
|
|
16
|
+
if (err instanceof WalletServiceError) {
|
|
17
|
+
return NextResponse.json({ error: err.message }, { status: err.status })
|
|
18
|
+
}
|
|
19
|
+
const message = err instanceof Error ? err.message : 'Failed to generate wallet'
|
|
20
|
+
return NextResponse.json({ error: message }, { status: 500 })
|
|
21
|
+
}
|
|
22
|
+
}
|