@swarmclawai/swarmclaw 1.2.8 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -6
- package/package.json +2 -2
- package/src/app/agents/[id]/page.tsx +1 -18
- package/src/app/api/activity/route.ts +9 -23
- package/src/app/api/agents/route.ts +17 -1
- 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/approvals/route.ts +13 -5
- package/src/app/api/connectors/route.ts +2 -2
- package/src/app/api/credentials/[id]/route.ts +2 -0
- package/src/app/api/credentials/route.ts +4 -1
- package/src/app/api/goals/[id]/route.ts +28 -0
- package/src/app/api/goals/route.ts +33 -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/protocols/templates/[id]/route.ts +2 -1
- package/src/app/api/protocols/templates/route.ts +2 -1
- package/src/app/api/settings/route.ts +13 -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/home/page.tsx +3 -0
- 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 +32 -33
- package/src/cli/spec.js +26 -27
- 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/activity/activity-log.ts +16 -1
- package/src/lib/server/agents/agent-service.ts +24 -11
- 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 +14 -85
- package/src/lib/server/approvals/approval-hooks.ts +81 -0
- package/src/lib/server/approvals.test.ts +6 -6
- package/src/lib/server/approvals.ts +11 -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 +127 -0
- package/src/lib/server/connectors/swarmdock.ts +285 -0
- package/src/lib/server/execution-brief.test.ts +2 -25
- package/src/lib/server/execution-brief.ts +30 -35
- package/src/lib/server/execution-engine/task-attempt.ts +0 -1
- package/src/lib/server/goals/goal-repository.ts +19 -0
- package/src/lib/server/goals/goal-service.ts +143 -0
- 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 +13 -0
- package/src/lib/server/storage.ts +75 -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/usage/cost-rollup.ts +124 -0
- package/src/lib/server/usage/usage-repository.ts +6 -0
- 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 +36 -32
- package/src/lib/validation/server-schemas.ts +35 -0
- package/src/stores/slices/data-slice.ts +5 -1
- package/src/stores/slices/ui-slice.ts +0 -4
- package/src/types/agent.ts +10 -84
- package/src/types/app-settings.ts +6 -2
- package/src/types/approval.ts +3 -2
- package/src/types/connector.ts +1 -0
- package/src/types/goal.ts +30 -0
- package/src/types/index.ts +2 -1
- package/src/types/message.ts +0 -1
- package/src/types/misc.ts +2 -4
- 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 +9 -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
|
@@ -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
|
+
}
|
|
@@ -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>
|
package/src/app/home/page.tsx
CHANGED
|
@@ -42,6 +42,9 @@ const ACTIVITY_ICONS: Record<ActivityEntry['action'], string> = {
|
|
|
42
42
|
incident: 'M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 9v4m0 4h.01',
|
|
43
43
|
running: 'M12 2v4m0 12v4m10-10h-4M6 12H2',
|
|
44
44
|
claimed: 'M9 12l2 2 4-4m6 2a10 10 0 1 1-20 0 10 10 0 0 1 20 0z',
|
|
45
|
+
configured: 'M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z',
|
|
46
|
+
budget_exceeded: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20m0 6v4m0 4h.01',
|
|
47
|
+
budget_warning: 'M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 9v4m0 4h.01',
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
const ACTIVITY_COLORS: Record<string, string> = {
|