@swarmclawai/swarmclaw 1.2.8 → 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 +30 -6
- package/package.json +2 -2
- 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/connectors/route.ts +2 -2
- 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/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-sheet.tsx +2 -40
- package/src/components/agents/inspector-panel.tsx +0 -83
- package/src/components/chat/chat-card.tsx +0 -31
- package/src/components/chat/message-bubble.tsx +1 -108
- package/src/components/connectors/connector-sheet.tsx +25 -1
- 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/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/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 +0 -2
- package/src/lib/server/capability-router.test.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +14 -14
- package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -2
- 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 +2 -22
- package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
- package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
- package/src/lib/server/chat-execution/message-classifier.ts +1 -16
- package/src/lib/server/chat-execution/prompt-builder.test.ts +0 -1
- package/src/lib/server/chat-execution/prompt-builder.ts +0 -30
- 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 +8 -123
- package/src/lib/server/chat-execution/stream-agent-chat.ts +1 -5
- 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/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/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/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/persistence/storage-context.ts +0 -5
- 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 +0 -9
- 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/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 +0 -1
- package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
- package/src/lib/server/runtime/session-run-manager.test.ts +0 -28
- package/src/lib/server/session-tools/crud.ts +0 -14
- package/src/lib/server/session-tools/delegate.ts +0 -4
- package/src/lib/server/session-tools/index.ts +0 -4
- package/src/lib/server/session-tools/team-context.ts +0 -3
- package/src/lib/server/storage-normalization.ts +8 -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 +0 -1
- package/src/lib/server/tool-capability-policy-advanced.test.ts +4 -4
- package/src/lib/server/tool-capability-policy.ts +0 -2
- package/src/lib/server/tool-planning.ts +0 -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/strip-internal-metadata.test.ts +1 -1
- package/src/lib/strip-internal-metadata.ts +1 -1
- package/src/lib/tool-definitions.ts +0 -1
- package/src/lib/validation/schemas.ts +33 -2
- package/src/stores/slices/data-slice.ts +5 -1
- package/src/stores/slices/ui-slice.ts +0 -4
- package/src/types/agent.ts +0 -84
- package/src/types/app-settings.ts +0 -2
- package/src/types/approval.ts +0 -2
- package/src/types/connector.ts +1 -0
- package/src/types/index.ts +1 -1
- package/src/types/message.ts +0 -1
- package/src/types/misc.ts +0 -2
- package/src/types/protocol.ts +0 -2
- package/src/types/run.ts +0 -3
- package/src/types/session.ts +1 -51
- package/src/types/swarmdock.ts +29 -0
- package/src/types/task.ts +7 -3
- package/src/types/working-state.ts +2 -9
- 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/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/types/mission.ts +0 -185
- package/src/views/settings/section-wallets.tsx +0 -35
|
@@ -0,0 +1,147 @@
|
|
|
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-wallet-routes-'))
|
|
12
|
+
try {
|
|
13
|
+
const result = spawnSync(process.execPath, ['--import', 'tsx', '--input-type=module', '--eval', script], {
|
|
14
|
+
cwd: repoRoot,
|
|
15
|
+
env: {
|
|
16
|
+
...process.env,
|
|
17
|
+
CREDENTIAL_SECRET: 'test-credential-secret',
|
|
18
|
+
DATA_DIR: path.join(tempDir, 'data'),
|
|
19
|
+
WORKSPACE_DIR: path.join(tempDir, 'workspace'),
|
|
20
|
+
},
|
|
21
|
+
encoding: 'utf-8',
|
|
22
|
+
})
|
|
23
|
+
assert.equal(result.status, 0, result.stderr || result.stdout || 'subprocess failed')
|
|
24
|
+
const lines = (result.stdout || '')
|
|
25
|
+
.trim()
|
|
26
|
+
.split('\n')
|
|
27
|
+
.map((line) => line.trim())
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
const jsonLine = [...lines].reverse().find((line) => line.startsWith('{'))
|
|
30
|
+
return JSON.parse(jsonLine || '{}')
|
|
31
|
+
} finally {
|
|
32
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test('wallet routes reject unknown agents and invalid addresses while returning safe payloads', () => {
|
|
37
|
+
const output = runWithTempDataDir(`
|
|
38
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
39
|
+
const walletsRouteMod = await import('./src/app/api/wallets/route')
|
|
40
|
+
const walletsGenerateRouteMod = await import('./src/app/api/wallets/generate/route')
|
|
41
|
+
const storage = storageMod.default || storageMod
|
|
42
|
+
const walletsRoute = walletsRouteMod.default || walletsRouteMod
|
|
43
|
+
const walletsGenerateRoute = walletsGenerateRouteMod.default || walletsGenerateRouteMod
|
|
44
|
+
|
|
45
|
+
storage.saveAgents({
|
|
46
|
+
agent_1: {
|
|
47
|
+
id: 'agent_1',
|
|
48
|
+
name: 'Agent One',
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const missingAgentResponse = await walletsRoute.POST(new Request('http://local/api/wallets', {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: { 'content-type': 'application/json' },
|
|
55
|
+
body: JSON.stringify({
|
|
56
|
+
agentId: 'missing-agent',
|
|
57
|
+
walletAddress: '0x000000000000000000000000000000000000dEaD',
|
|
58
|
+
}),
|
|
59
|
+
}))
|
|
60
|
+
const missingAgentPayload = await missingAgentResponse.json()
|
|
61
|
+
|
|
62
|
+
const blankAddressResponse = await walletsRoute.POST(new Request('http://local/api/wallets', {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: { 'content-type': 'application/json' },
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
agentId: 'agent_1',
|
|
67
|
+
walletAddress: ' ',
|
|
68
|
+
}),
|
|
69
|
+
}))
|
|
70
|
+
const blankAddressPayload = await blankAddressResponse.json()
|
|
71
|
+
|
|
72
|
+
const invalidAddressResponse = await walletsRoute.POST(new Request('http://local/api/wallets', {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
headers: { 'content-type': 'application/json' },
|
|
75
|
+
body: JSON.stringify({
|
|
76
|
+
agentId: 'agent_1',
|
|
77
|
+
walletAddress: '0x1234',
|
|
78
|
+
}),
|
|
79
|
+
}))
|
|
80
|
+
const invalidAddressPayload = await invalidAddressResponse.json()
|
|
81
|
+
|
|
82
|
+
const createResponse = await walletsRoute.POST(new Request('http://local/api/wallets', {
|
|
83
|
+
method: 'POST',
|
|
84
|
+
headers: { 'content-type': 'application/json' },
|
|
85
|
+
body: JSON.stringify({
|
|
86
|
+
agentId: 'agent_1',
|
|
87
|
+
walletAddress: '0x000000000000000000000000000000000000dead',
|
|
88
|
+
label: 'Manual Wallet',
|
|
89
|
+
}),
|
|
90
|
+
}))
|
|
91
|
+
const createPayload = await createResponse.json()
|
|
92
|
+
|
|
93
|
+
const generateMissingResponse = await walletsGenerateRoute.POST(new Request('http://local/api/wallets/generate', {
|
|
94
|
+
method: 'POST',
|
|
95
|
+
headers: { 'content-type': 'application/json' },
|
|
96
|
+
body: JSON.stringify({
|
|
97
|
+
agentId: 'missing-agent',
|
|
98
|
+
}),
|
|
99
|
+
}))
|
|
100
|
+
const generateMissingPayload = await generateMissingResponse.json()
|
|
101
|
+
|
|
102
|
+
const generateResponse = await walletsGenerateRoute.POST(new Request('http://local/api/wallets/generate', {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: { 'content-type': 'application/json' },
|
|
105
|
+
body: JSON.stringify({
|
|
106
|
+
agentId: 'agent_1',
|
|
107
|
+
label: 'Generated Wallet',
|
|
108
|
+
}),
|
|
109
|
+
}))
|
|
110
|
+
const generatePayload = await generateResponse.json()
|
|
111
|
+
|
|
112
|
+
const storedWallets = Object.values(storage.loadWallets())
|
|
113
|
+
const generatedStoredWallet = storedWallets.find((wallet) => wallet.label === 'Generated Wallet') || null
|
|
114
|
+
|
|
115
|
+
console.log(JSON.stringify({
|
|
116
|
+
missingAgentStatus: missingAgentResponse.status,
|
|
117
|
+
missingAgentError: missingAgentPayload?.error || null,
|
|
118
|
+
blankAddressStatus: blankAddressResponse.status,
|
|
119
|
+
blankAddressError: blankAddressPayload?.error || null,
|
|
120
|
+
invalidAddressStatus: invalidAddressResponse.status,
|
|
121
|
+
invalidAddressError: invalidAddressPayload?.error || null,
|
|
122
|
+
createStatus: createResponse.status,
|
|
123
|
+
createAddress: createPayload?.walletAddress || null,
|
|
124
|
+
createHasPrivateKey: Boolean(createPayload && Object.prototype.hasOwnProperty.call(createPayload, 'encryptedPrivateKey')),
|
|
125
|
+
generateMissingStatus: generateMissingResponse.status,
|
|
126
|
+
generateMissingError: generateMissingPayload?.error || null,
|
|
127
|
+
generateStatus: generateResponse.status,
|
|
128
|
+
generateHasPrivateKey: Boolean(generatePayload && Object.prototype.hasOwnProperty.call(generatePayload, 'encryptedPrivateKey')),
|
|
129
|
+
storedGeneratedHasPrivateKey: Boolean(generatedStoredWallet?.encryptedPrivateKey),
|
|
130
|
+
}))
|
|
131
|
+
`)
|
|
132
|
+
|
|
133
|
+
assert.equal(output.missingAgentStatus, 404)
|
|
134
|
+
assert.match(String(output.missingAgentError || ''), /Agent not found/i)
|
|
135
|
+
assert.equal(output.blankAddressStatus, 400)
|
|
136
|
+
assert.equal(output.blankAddressError, 'walletAddress is required')
|
|
137
|
+
assert.equal(output.invalidAddressStatus, 400)
|
|
138
|
+
assert.match(String(output.invalidAddressError || ''), /valid Base\/Ethereum address/i)
|
|
139
|
+
assert.equal(output.createStatus, 201)
|
|
140
|
+
assert.equal(output.createAddress, '0x000000000000000000000000000000000000dEaD')
|
|
141
|
+
assert.equal(output.createHasPrivateKey, false)
|
|
142
|
+
assert.equal(output.generateMissingStatus, 404)
|
|
143
|
+
assert.match(String(output.generateMissingError || ''), /Agent not found/i)
|
|
144
|
+
assert.equal(output.generateStatus, 201)
|
|
145
|
+
assert.equal(output.generateHasPrivateKey, false)
|
|
146
|
+
assert.equal(output.storedGeneratedHasPrivateKey, true)
|
|
147
|
+
})
|
|
@@ -1,109 +1,27 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
3
|
-
import {
|
|
4
|
-
import { createAgentWallet, getAgentActiveWalletId, getWalletPortfolioSnapshot, stripWalletPrivateKey } from '@/lib/server/wallet/wallet-service'
|
|
5
|
-
import { buildEmptyWalletPortfolio } from '@/lib/server/wallet/wallet-portfolio'
|
|
6
|
-
import type { AgentWallet, WalletPortfolioSummary } from '@/types'
|
|
7
|
-
import { errorMessage } from '@/lib/shared-utils'
|
|
3
|
+
import { listWalletsSafe, createWallet, WalletServiceError } from '@/lib/server/wallets/wallet-service'
|
|
8
4
|
export const dynamic = 'force-dynamic'
|
|
9
|
-
const WALLET_LIST_PORTFOLIO_TIMEOUT_MS = 1500
|
|
10
5
|
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
portfolio: {
|
|
14
|
-
balanceAtomic: string
|
|
15
|
-
balanceFormatted: string
|
|
16
|
-
balanceSymbol: string
|
|
17
|
-
balanceDisplay: string
|
|
18
|
-
balanceLamports?: number
|
|
19
|
-
balanceSol?: number
|
|
20
|
-
assets: unknown[]
|
|
21
|
-
summary: WalletPortfolioSummary
|
|
22
|
-
},
|
|
23
|
-
isActive: boolean,
|
|
24
|
-
) {
|
|
25
|
-
return {
|
|
26
|
-
...stripWalletPrivateKey(wallet as unknown as Record<string, unknown>),
|
|
27
|
-
balanceAtomic: portfolio.balanceAtomic,
|
|
28
|
-
balanceFormatted: portfolio.balanceFormatted,
|
|
29
|
-
balanceSymbol: portfolio.balanceSymbol,
|
|
30
|
-
balanceDisplay: portfolio.balanceDisplay,
|
|
31
|
-
balanceLamports: portfolio.balanceLamports,
|
|
32
|
-
balanceSol: portfolio.balanceSol,
|
|
33
|
-
assets: portfolio.assets,
|
|
34
|
-
portfolioSummary: portfolio.summary,
|
|
35
|
-
isActive,
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export async function GET(req: Request) {
|
|
40
|
-
const wallets = loadWallets() as Record<string, AgentWallet>
|
|
41
|
-
const agents = loadAgents()
|
|
42
|
-
const { searchParams } = new URL(req.url)
|
|
43
|
-
const agentId = searchParams.get('agentId')?.trim() || ''
|
|
44
|
-
const walletEntries = Object.entries(wallets)
|
|
45
|
-
.filter(([, wallet]) => !agentId || wallet.agentId === agentId)
|
|
46
|
-
const entries = await Promise.all(
|
|
47
|
-
walletEntries.map(async ([id, wallet]) => {
|
|
48
|
-
let portfolio = buildEmptyWalletPortfolio(wallet)
|
|
49
|
-
try {
|
|
50
|
-
portfolio = await getWalletPortfolioSnapshot(wallet, {
|
|
51
|
-
timeoutMs: WALLET_LIST_PORTFOLIO_TIMEOUT_MS,
|
|
52
|
-
allowStale: true,
|
|
53
|
-
})
|
|
54
|
-
} catch {
|
|
55
|
-
// Slow or failed RPC discovery — return empty/stale portfolio for list view
|
|
56
|
-
}
|
|
57
|
-
const activeWalletId = getAgentActiveWalletId(agents[wallet.agentId])
|
|
58
|
-
return [id, withPortfolio(wallet, portfolio, activeWalletId === wallet.id)] as const
|
|
59
|
-
}),
|
|
60
|
-
)
|
|
61
|
-
return NextResponse.json(Object.fromEntries(entries))
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
interface WalletCreateBody {
|
|
65
|
-
agentId: string
|
|
66
|
-
chain?: string | null
|
|
67
|
-
provider?: string | null
|
|
68
|
-
label?: string
|
|
69
|
-
requireApproval?: boolean
|
|
70
|
-
spendingLimitAtomic?: string | number | null
|
|
71
|
-
spendingLimitLamports?: string | number | null
|
|
72
|
-
dailyLimitAtomic?: string | number | null
|
|
73
|
-
dailyLimitLamports?: string | number | null
|
|
6
|
+
export async function GET() {
|
|
7
|
+
return NextResponse.json(listWalletsSafe())
|
|
74
8
|
}
|
|
75
9
|
|
|
76
10
|
export async function POST(req: Request) {
|
|
77
|
-
const { data: body, error } = await safeParseBody
|
|
11
|
+
const { data: body, error } = await safeParseBody(req)
|
|
78
12
|
if (error) return error
|
|
79
|
-
const settings = loadSettings()
|
|
80
13
|
try {
|
|
81
|
-
const wallet =
|
|
82
|
-
agentId: body.agentId,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
label: body.label,
|
|
86
|
-
requireApproval: typeof body.requireApproval === 'boolean'
|
|
87
|
-
? body.requireApproval
|
|
88
|
-
: settings.walletApprovalsEnabled !== false,
|
|
89
|
-
spendingLimitAtomic: body.spendingLimitAtomic ?? body.spendingLimitLamports,
|
|
90
|
-
dailyLimitAtomic: body.dailyLimitAtomic ?? body.dailyLimitLamports,
|
|
14
|
+
const wallet = await createWallet({
|
|
15
|
+
agentId: typeof body.agentId === 'string' ? body.agentId : '',
|
|
16
|
+
walletAddress: typeof body.walletAddress === 'string' ? body.walletAddress : '',
|
|
17
|
+
label: typeof body.label === 'string' ? body.label : undefined,
|
|
91
18
|
})
|
|
92
|
-
return NextResponse.json(
|
|
93
|
-
} catch (err
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return NextResponse.json({ error: message }, { status: 400 })
|
|
97
|
-
}
|
|
98
|
-
if (/^Unsupported wallet chain or provider: /.test(message)) {
|
|
99
|
-
return NextResponse.json({ error: message }, { status: 400 })
|
|
100
|
-
}
|
|
101
|
-
if (message === 'Agent not found') {
|
|
102
|
-
return NextResponse.json({ error: message }, { status: 404 })
|
|
103
|
-
}
|
|
104
|
-
if (/^Agent already has a (solana|ethereum) wallet$/.test(message)) {
|
|
105
|
-
return NextResponse.json({ error: message }, { status: 409 })
|
|
19
|
+
return NextResponse.json(wallet, { status: 201 })
|
|
20
|
+
} catch (err) {
|
|
21
|
+
if (err instanceof WalletServiceError) {
|
|
22
|
+
return NextResponse.json({ error: err.message }, { status: err.status })
|
|
106
23
|
}
|
|
24
|
+
const message = err instanceof Error ? err.message : 'Failed to create wallet'
|
|
107
25
|
return NextResponse.json({ error: message }, { status: 500 })
|
|
108
26
|
}
|
|
109
27
|
}
|
|
@@ -7,7 +7,7 @@ import { StatCard } from '@/components/ui/stat-card'
|
|
|
7
7
|
import { isOrchestratorEligible } from '@/lib/orchestrator-config'
|
|
8
8
|
import { timeAgo } from '@/lib/time-format'
|
|
9
9
|
import { useWs } from '@/hooks/use-ws'
|
|
10
|
-
import type { Agent, ApprovalRequest, EstopState,
|
|
10
|
+
import type { Agent, ApprovalRequest, EstopState, SupervisorIncident } from '@/types'
|
|
11
11
|
|
|
12
12
|
type EstopResponse = EstopState & {
|
|
13
13
|
ok?: boolean
|
|
@@ -108,7 +108,6 @@ function previewIncidentDetails(value: string): string {
|
|
|
108
108
|
export default function AutonomyPage() {
|
|
109
109
|
const [estop, setEstop] = useState<EstopResponse | null>(null)
|
|
110
110
|
const [incidents, setIncidents] = useState<SupervisorIncident[]>([])
|
|
111
|
-
const [missions, setMissions] = useState<Mission[]>([])
|
|
112
111
|
const [agents, setAgents] = useState<Agent[]>([])
|
|
113
112
|
const [loading, setLoading] = useState(true)
|
|
114
113
|
const [refreshing, setRefreshing] = useState(false)
|
|
@@ -122,15 +121,13 @@ export default function AutonomyPage() {
|
|
|
122
121
|
if (mode === 'initial') setLoading(true)
|
|
123
122
|
else setRefreshing(true)
|
|
124
123
|
try {
|
|
125
|
-
const [estopState, incidentList,
|
|
124
|
+
const [estopState, incidentList, agentMap] = await Promise.all([
|
|
126
125
|
api<EstopResponse>('GET', '/autonomy/estop'),
|
|
127
126
|
api<SupervisorIncident[]>('GET', '/autonomy/incidents?limit=60'),
|
|
128
|
-
api<Mission[]>('GET', '/missions?status=non_terminal&limit=20'),
|
|
129
127
|
api<Record<string, Agent>>('GET', '/agents'),
|
|
130
128
|
])
|
|
131
129
|
setEstop(estopState)
|
|
132
130
|
setIncidents(Array.isArray(incidentList) ? incidentList : [])
|
|
133
|
-
setMissions(Array.isArray(missionList) ? missionList : [])
|
|
134
131
|
setAgents(agentMap ? Object.values(agentMap) : [])
|
|
135
132
|
setRefreshedAt(Date.now())
|
|
136
133
|
setError(null)
|
|
@@ -287,14 +284,6 @@ export default function AutonomyPage() {
|
|
|
287
284
|
() => [...incidents].sort((left, right) => right.createdAt - left.createdAt),
|
|
288
285
|
[incidents],
|
|
289
286
|
)
|
|
290
|
-
const activeMissions = useMemo(
|
|
291
|
-
() => [...missions].sort((left, right) => right.updatedAt - left.updatedAt),
|
|
292
|
-
[missions],
|
|
293
|
-
)
|
|
294
|
-
const blockedMissions = activeMissions.filter((mission) =>
|
|
295
|
-
mission.status === 'waiting' || mission.status === 'failed' || mission.status === 'cancelled',
|
|
296
|
-
)
|
|
297
|
-
|
|
298
287
|
const filteredIncidents = useMemo(() => {
|
|
299
288
|
if (incidentFilter === 'high') return sortedIncidents.filter((incident) => incident.severity === 'high')
|
|
300
289
|
if (incidentFilter === 'runtime_failure') return sortedIncidents.filter((incident) => incident.kind === 'runtime_failure')
|
|
@@ -491,50 +480,6 @@ export default function AutonomyPage() {
|
|
|
491
480
|
</div>
|
|
492
481
|
)}
|
|
493
482
|
|
|
494
|
-
<section className="rounded-[20px] border border-white/[0.06] bg-surface p-5">
|
|
495
|
-
<div className="mb-4 flex items-start justify-between gap-4">
|
|
496
|
-
<div>
|
|
497
|
-
<h2 className="font-display text-[18px] font-700 tracking-[-0.02em] text-text">Active Missions</h2>
|
|
498
|
-
<p className="mt-1 text-[12px] leading-[1.7] text-text-3/72">
|
|
499
|
-
Durable goals that are still running or waiting. Waiting missions surface here so operators can see blockers without digging through incidents.
|
|
500
|
-
</p>
|
|
501
|
-
</div>
|
|
502
|
-
<div className="rounded-full border border-white/[0.08] bg-white/[0.03] px-3 py-1 text-[11px] text-text-3/72">
|
|
503
|
-
{activeMissions.length} active
|
|
504
|
-
{blockedMissions.length > 0 ? ` · ${blockedMissions.length} waiting` : ''}
|
|
505
|
-
</div>
|
|
506
|
-
</div>
|
|
507
|
-
|
|
508
|
-
{activeMissions.length === 0 ? (
|
|
509
|
-
<div className="rounded-[16px] border border-white/[0.06] bg-white/[0.02] px-4 py-3 text-[12px] text-text-3/70">
|
|
510
|
-
No durable missions are active right now.
|
|
511
|
-
</div>
|
|
512
|
-
) : (
|
|
513
|
-
<div className="grid gap-3 lg:grid-cols-2">
|
|
514
|
-
{activeMissions.slice(0, 6).map((mission) => {
|
|
515
|
-
const waiting = mission.status === 'waiting' || mission.status === 'failed' || mission.status === 'cancelled'
|
|
516
|
-
return (
|
|
517
|
-
<div
|
|
518
|
-
key={mission.id}
|
|
519
|
-
className={`rounded-[16px] border p-4 ${waiting ? 'border-amber-500/18 bg-amber-500/[0.05]' : 'border-white/[0.06] bg-white/[0.02]'}`}
|
|
520
|
-
>
|
|
521
|
-
<div className="mb-2 flex items-center justify-between gap-3">
|
|
522
|
-
<span className={`rounded-full px-2.5 py-1 text-[10px] font-700 uppercase tracking-[0.08em] ${waiting ? 'bg-amber-500/12 text-amber-300' : 'bg-emerald-500/12 text-emerald-300'}`}>
|
|
523
|
-
{mission.status}
|
|
524
|
-
</span>
|
|
525
|
-
<span className="text-[11px] text-text-3/65">{timeAgo(mission.updatedAt, now)}</span>
|
|
526
|
-
</div>
|
|
527
|
-
<div className="text-[14px] font-600 text-text">{mission.objective}</div>
|
|
528
|
-
<div className="mt-2 text-[12px] leading-[1.7] text-text-3/72">
|
|
529
|
-
{mission.waitState?.reason || mission.currentStep || mission.verifierSummary || mission.plannerSummary || 'Mission active.'}
|
|
530
|
-
</div>
|
|
531
|
-
</div>
|
|
532
|
-
)
|
|
533
|
-
})}
|
|
534
|
-
</div>
|
|
535
|
-
)}
|
|
536
|
-
</section>
|
|
537
|
-
|
|
538
483
|
<section className="rounded-[20px] border border-white/[0.06] bg-surface p-5">
|
|
539
484
|
<div className="mb-4 flex items-start justify-between gap-4">
|
|
540
485
|
<div>
|
|
@@ -4,7 +4,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
|
4
4
|
import { useRouter, useSearchParams } from 'next/navigation'
|
|
5
5
|
import { useAgentsQuery } from '@/features/agents/queries'
|
|
6
6
|
import { useChatroomsQuery } from '@/features/chatrooms/queries'
|
|
7
|
-
import { useMissionsQuery } from '@/features/missions/queries'
|
|
8
7
|
import {
|
|
9
8
|
useCreateProtocolRunMutation,
|
|
10
9
|
useDeleteProtocolTemplateMutation,
|
|
@@ -21,7 +20,6 @@ import { timeAgo } from '@/lib/time-format'
|
|
|
21
20
|
import type {
|
|
22
21
|
BoardTask,
|
|
23
22
|
Chatroom,
|
|
24
|
-
Mission,
|
|
25
23
|
ProtocolRun,
|
|
26
24
|
ProtocolRunEvent,
|
|
27
25
|
ProtocolStepDefinition,
|
|
@@ -33,7 +31,6 @@ type ProtocolRunDetail = {
|
|
|
33
31
|
template: ProtocolTemplate | null
|
|
34
32
|
transcript: Chatroom | null
|
|
35
33
|
parentChatroom: Chatroom | null
|
|
36
|
-
linkedMission: Mission | null
|
|
37
34
|
linkedTask: BoardTask | null
|
|
38
35
|
events: ProtocolRunEvent[]
|
|
39
36
|
}
|
|
@@ -153,7 +150,6 @@ export default function ProtocolsPage() {
|
|
|
153
150
|
facilitatorAgentId: '',
|
|
154
151
|
participantAgentIds: [] as string[],
|
|
155
152
|
parentChatroomId: '',
|
|
156
|
-
missionId: '',
|
|
157
153
|
taskId: '',
|
|
158
154
|
autoStart: true,
|
|
159
155
|
})
|
|
@@ -162,7 +158,6 @@ export default function ProtocolsPage() {
|
|
|
162
158
|
const runsQuery = useProtocolRunsQuery({ limit: 120 })
|
|
163
159
|
const agentsQuery = useAgentsQuery()
|
|
164
160
|
const chatroomsQuery = useChatroomsQuery()
|
|
165
|
-
const missionsQuery = useMissionsQuery({ limit: 80 })
|
|
166
161
|
const tasksQuery = useTasksQuery({ includeArchived: true })
|
|
167
162
|
const detailQuery = useProtocolRunDetailQuery(selectedRunId, { enabled: Boolean(selectedRunId) })
|
|
168
163
|
const createRunMutation = useCreateProtocolRunMutation()
|
|
@@ -174,14 +169,12 @@ export default function ProtocolsPage() {
|
|
|
174
169
|
const detail = detailQuery.data ?? null
|
|
175
170
|
const agents = agentsQuery.data ?? {}
|
|
176
171
|
const chatrooms = chatroomsQuery.data ?? {}
|
|
177
|
-
const missions = missionsQuery.data ?? []
|
|
178
172
|
const tasks = tasksQuery.data ?? {}
|
|
179
173
|
const loading = (
|
|
180
174
|
templatesQuery.isLoading
|
|
181
175
|
|| runsQuery.isLoading
|
|
182
176
|
|| agentsQuery.isLoading
|
|
183
177
|
|| chatroomsQuery.isLoading
|
|
184
|
-
|| missionsQuery.isLoading
|
|
185
178
|
|| tasksQuery.isLoading
|
|
186
179
|
)
|
|
187
180
|
const detailLoading = detailQuery.isLoading || detailQuery.isFetching
|
|
@@ -190,7 +183,6 @@ export default function ProtocolsPage() {
|
|
|
190
183
|
runsQuery.error,
|
|
191
184
|
agentsQuery.error,
|
|
192
185
|
chatroomsQuery.error,
|
|
193
|
-
missionsQuery.error,
|
|
194
186
|
tasksQuery.error,
|
|
195
187
|
detailQuery.error,
|
|
196
188
|
].find(Boolean)
|
|
@@ -237,7 +229,6 @@ export default function ProtocolsPage() {
|
|
|
237
229
|
participantAgentIds: form.participantAgentIds,
|
|
238
230
|
facilitatorAgentId: form.facilitatorAgentId || null,
|
|
239
231
|
parentChatroomId: form.parentChatroomId || null,
|
|
240
|
-
missionId: form.missionId || null,
|
|
241
232
|
taskId: form.taskId || null,
|
|
242
233
|
autoStart: form.autoStart,
|
|
243
234
|
config: {
|
|
@@ -364,7 +355,7 @@ export default function ProtocolsPage() {
|
|
|
364
355
|
</div>
|
|
365
356
|
<h1 className="mt-4 font-display text-[34px] font-700 tracking-[-0.03em] text-text">Bounded Collaboration Runs</h1>
|
|
366
357
|
<p className="mt-3 max-w-[720px] text-[15px] leading-relaxed text-text-3/72">
|
|
367
|
-
Start structured sessions from chats, chatrooms, tasks,
|
|
358
|
+
Start structured sessions from chats, chatrooms, tasks, or schedules. Runs stay temporary and bounded, while templates remain reusable for the next time you need them.
|
|
368
359
|
</p>
|
|
369
360
|
</div>
|
|
370
361
|
<div className="w-full max-w-[520px] rounded-[20px] border border-white/[0.06] bg-surface/70 p-4">
|
|
@@ -372,7 +363,7 @@ export default function ProtocolsPage() {
|
|
|
372
363
|
<div>
|
|
373
364
|
<div className="text-[11px] font-700 uppercase tracking-[0.12em] text-text-3/55">Launch and Templates</div>
|
|
374
365
|
<div className="mt-1 text-[13px] leading-relaxed text-text-3/68">
|
|
375
|
-
Start from here when you need a blank run. Normal use should start from the chat, task,
|
|
366
|
+
Start from here when you need a blank run. Normal use should start from the chat, task, or chatroom you are already in.
|
|
376
367
|
</div>
|
|
377
368
|
</div>
|
|
378
369
|
<div className="flex flex-wrap gap-2">
|
|
@@ -471,16 +462,6 @@ export default function ProtocolsPage() {
|
|
|
471
462
|
<option key={chatroom.id} value={chatroom.id}>{chatroom.name}</option>
|
|
472
463
|
))}
|
|
473
464
|
</select>
|
|
474
|
-
<select
|
|
475
|
-
value={form.missionId}
|
|
476
|
-
onChange={(event) => setForm((current) => ({ ...current, missionId: event.target.value }))}
|
|
477
|
-
className="rounded-[12px] border border-white/[0.06] bg-white/[0.04] px-3 py-2.5 text-[14px] text-text outline-none"
|
|
478
|
-
>
|
|
479
|
-
<option value="">No linked mission</option>
|
|
480
|
-
{missions.map((mission) => (
|
|
481
|
-
<option key={mission.id} value={mission.id}>{mission.objective}</option>
|
|
482
|
-
))}
|
|
483
|
-
</select>
|
|
484
465
|
<select
|
|
485
466
|
value={form.taskId}
|
|
486
467
|
onChange={(event) => setForm((current) => ({ ...current, taskId: event.target.value }))}
|
|
@@ -11,7 +11,6 @@ import { ThemeSection } from '@/views/settings/section-theme'
|
|
|
11
11
|
import { RuntimeLoopSection } from '@/views/settings/section-runtime-loop'
|
|
12
12
|
import { SupervisorReflectionSection } from '@/views/settings/section-supervisor-reflection'
|
|
13
13
|
import { CapabilityPolicySection } from '@/views/settings/section-capability-policy'
|
|
14
|
-
import { WalletsSection } from '@/views/settings/section-wallets'
|
|
15
14
|
import { StorageSection } from '@/views/settings/section-storage'
|
|
16
15
|
import { VoiceSection } from '@/views/settings/section-voice'
|
|
17
16
|
import { WebSearchSection } from '@/views/settings/section-web-search'
|
|
@@ -149,14 +148,6 @@ export default function SettingsRoute() {
|
|
|
149
148
|
keywords: ['storage', 'uploads', 'disk', 'cleanup', 'files'],
|
|
150
149
|
render: () => <StorageSection {...sectionProps} />,
|
|
151
150
|
},
|
|
152
|
-
{
|
|
153
|
-
id: 'wallets',
|
|
154
|
-
tabId: 'general',
|
|
155
|
-
title: 'Wallets',
|
|
156
|
-
description: 'Control global wallet approval behavior and auto-execution defaults.',
|
|
157
|
-
keywords: ['wallet', 'wallets', 'approval', 'approvals', 'crypto', 'send'],
|
|
158
|
-
render: () => <WalletsSection {...sectionProps} />,
|
|
159
|
-
},
|
|
160
151
|
{
|
|
161
152
|
id: 'theme',
|
|
162
153
|
tabId: 'appearance',
|
package/src/app/wallets/page.tsx
CHANGED
|
@@ -1,12 +1,112 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
import { useAppStore } from '@/stores/use-app-store'
|
|
5
|
+
import { WalletList } from '@/components/wallets/wallet-list'
|
|
6
|
+
import { api } from '@/lib/app/api-client'
|
|
7
|
+
import type { SafeWallet } from '@/types'
|
|
5
8
|
|
|
6
9
|
export default function WalletsPage() {
|
|
10
|
+
const agents = useAppStore((s) => s.agents)
|
|
11
|
+
const loadAgents = useAppStore((s) => s.loadAgents)
|
|
12
|
+
const loadWallets = useAppStore((s) => s.loadWallets)
|
|
13
|
+
const [generating, setGenerating] = useState(false)
|
|
14
|
+
const [showPicker, setShowPicker] = useState(false)
|
|
15
|
+
const [error, setError] = useState<string | null>(null)
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
loadAgents()
|
|
19
|
+
loadWallets()
|
|
20
|
+
}, [loadAgents, loadWallets])
|
|
21
|
+
|
|
22
|
+
const agentList = Object.values(agents)
|
|
23
|
+
|
|
24
|
+
const handleGenerate = async (agentId?: string) => {
|
|
25
|
+
const targetId = agentId || agentList[0]?.id
|
|
26
|
+
if (!targetId) {
|
|
27
|
+
setError('Create an agent first')
|
|
28
|
+
setTimeout(() => setError(null), 3000)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
setGenerating(true)
|
|
32
|
+
setShowPicker(false)
|
|
33
|
+
setError(null)
|
|
34
|
+
try {
|
|
35
|
+
await api<SafeWallet>('POST', '/wallets/generate', { agentId: targetId })
|
|
36
|
+
loadWallets()
|
|
37
|
+
} catch (err) {
|
|
38
|
+
setError(err instanceof Error ? err.message : 'Failed to generate wallet')
|
|
39
|
+
setTimeout(() => setError(null), 4000)
|
|
40
|
+
} finally {
|
|
41
|
+
setGenerating(false)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
7
45
|
return (
|
|
8
|
-
<
|
|
9
|
-
<
|
|
10
|
-
|
|
46
|
+
<div className="flex-1 flex flex-col h-full">
|
|
47
|
+
<div className="flex items-center px-6 pt-5 pb-3 shrink-0">
|
|
48
|
+
<h2 className="font-display text-[14px] font-600 text-text-2 tracking-[-0.01em] capitalize flex-1">
|
|
49
|
+
Wallets
|
|
50
|
+
</h2>
|
|
51
|
+
<div className="relative">
|
|
52
|
+
<button
|
|
53
|
+
onClick={() => {
|
|
54
|
+
if (agentList.length <= 1) {
|
|
55
|
+
handleGenerate()
|
|
56
|
+
} else {
|
|
57
|
+
setShowPicker(!showPicker)
|
|
58
|
+
}
|
|
59
|
+
}}
|
|
60
|
+
disabled={generating}
|
|
61
|
+
className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-[8px] text-[11px] font-600 text-accent-bright bg-accent-soft hover:bg-accent-bright/15 transition-all cursor-pointer disabled:opacity-50"
|
|
62
|
+
style={{ fontFamily: 'inherit' }}
|
|
63
|
+
>
|
|
64
|
+
{generating ? (
|
|
65
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" className="animate-spin">
|
|
66
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
67
|
+
</svg>
|
|
68
|
+
) : (
|
|
69
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
|
|
70
|
+
<line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
|
|
71
|
+
</svg>
|
|
72
|
+
)}
|
|
73
|
+
{generating ? 'Generating...' : 'Generate Wallet'}
|
|
74
|
+
</button>
|
|
75
|
+
|
|
76
|
+
{/* Agent picker dropdown */}
|
|
77
|
+
{showPicker && (
|
|
78
|
+
<>
|
|
79
|
+
<div className="fixed inset-0 z-40" onClick={() => setShowPicker(false)} />
|
|
80
|
+
<div className="absolute right-0 top-full mt-1 z-50 w-56 rounded-[12px] bg-surface border border-white/[0.08] shadow-xl overflow-hidden">
|
|
81
|
+
<div className="px-3 py-2 border-b border-white/[0.06]">
|
|
82
|
+
<span className="text-[11px] font-600 text-text-3">Select Agent</span>
|
|
83
|
+
</div>
|
|
84
|
+
<div className="max-h-48 overflow-y-auto py-1">
|
|
85
|
+
{agentList.map((agent) => (
|
|
86
|
+
<button
|
|
87
|
+
key={agent.id}
|
|
88
|
+
onClick={() => handleGenerate(agent.id)}
|
|
89
|
+
className="w-full text-left px-3 py-2 flex items-center gap-2 hover:bg-white/[0.04] transition-colors cursor-pointer"
|
|
90
|
+
style={{ fontFamily: 'inherit' }}
|
|
91
|
+
>
|
|
92
|
+
<span className="text-[14px]">{agent.emoji || '🤖'}</span>
|
|
93
|
+
<span className="text-[12px] font-600 text-text truncate">{agent.name}</span>
|
|
94
|
+
</button>
|
|
95
|
+
))}
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</>
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
{error && (
|
|
104
|
+
<div className="mx-6 mb-2 px-3 py-2 rounded-[10px] bg-red-500/10 border border-red-500/20 text-[12px] text-red-400 font-600">
|
|
105
|
+
{error}
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
108
|
+
|
|
109
|
+
<WalletList />
|
|
110
|
+
</div>
|
|
11
111
|
)
|
|
12
112
|
}
|