@swarmclawai/swarmclaw 0.7.8 → 0.8.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 +12 -15
- package/next.config.ts +13 -2
- package/package.json +4 -2
- package/src/app/api/agents/[id]/thread/route.ts +9 -0
- package/src/app/api/agents/route.ts +4 -0
- package/src/app/api/agents/thread-route.test.ts +133 -0
- package/src/app/api/approvals/route.test.ts +148 -0
- package/src/app/api/canvas/[sessionId]/route.ts +3 -1
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -2
- package/src/app/api/chats/[id]/devserver/route.ts +48 -7
- package/src/app/api/chats/[id]/messages/route.ts +42 -18
- package/src/app/api/chats/[id]/route.ts +1 -1
- package/src/app/api/chats/[id]/stop/route.ts +5 -4
- package/src/app/api/chats/route.ts +22 -2
- package/src/app/api/clawhub/install/route.ts +28 -8
- package/src/app/api/connectors/[id]/route.ts +26 -1
- package/src/app/api/external-agents/route.test.ts +165 -0
- package/src/app/api/gateways/[id]/health/route.ts +27 -12
- package/src/app/api/gateways/[id]/route.ts +2 -0
- package/src/app/api/gateways/health-route.test.ts +135 -0
- package/src/app/api/gateways/route.ts +2 -0
- package/src/app/api/mcp-servers/route.test.ts +130 -0
- package/src/app/api/openclaw/deploy/route.ts +38 -5
- package/src/app/api/plugins/install/route.ts +46 -6
- package/src/app/api/plugins/marketplace/route.ts +48 -15
- package/src/app/api/preview-server/route.ts +26 -11
- package/src/app/api/schedules/[id]/run/route.ts +4 -0
- package/src/app/api/schedules/route.test.ts +86 -0
- package/src/app/api/schedules/route.ts +6 -1
- package/src/app/api/setup/check-provider/route.test.ts +19 -0
- package/src/app/api/setup/check-provider/route.ts +40 -10
- package/src/app/api/skills/[id]/route.ts +12 -0
- package/src/app/api/skills/import/route.ts +14 -12
- package/src/app/api/skills/route.ts +13 -1
- package/src/app/api/tasks/[id]/route.ts +10 -1
- package/src/app/api/tasks/import/github/route.test.ts +65 -0
- package/src/app/api/tasks/import/github/route.ts +337 -0
- package/src/app/api/wallets/[id]/approve/route.ts +17 -3
- package/src/app/api/wallets/[id]/route.ts +79 -33
- package/src/app/api/wallets/[id]/send/route.ts +19 -33
- package/src/app/api/wallets/route.ts +78 -61
- package/src/app/api/webhooks/[id]/route.ts +33 -6
- package/src/app/api/webhooks/route.test.ts +272 -0
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-card.tsx +9 -2
- package/src/components/agents/agent-chat-list.tsx +18 -2
- package/src/components/agents/agent-list.tsx +1 -0
- package/src/components/agents/agent-sheet.tsx +73 -24
- package/src/components/agents/inspector-panel.tsx +41 -0
- package/src/components/canvas/canvas-panel.tsx +236 -65
- package/src/components/chat/chat-card.tsx +36 -13
- package/src/components/chat/chat-header.tsx +44 -16
- package/src/components/chat/chat-list.tsx +28 -4
- package/src/components/chat/checkpoint-timeline.tsx +50 -34
- package/src/components/chat/message-bubble.tsx +208 -145
- package/src/components/chat/message-list.tsx +48 -19
- package/src/components/chatrooms/chatroom-message.tsx +2 -2
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -2
- package/src/components/connectors/connector-health.tsx +1 -1
- package/src/components/connectors/connector-list.tsx +7 -2
- package/src/components/connectors/connector-sheet.tsx +337 -148
- package/src/components/gateways/gateway-sheet.tsx +2 -2
- package/src/components/mcp-servers/mcp-server-list.tsx +26 -5
- package/src/components/mcp-servers/mcp-server-sheet.tsx +19 -2
- package/src/components/openclaw/openclaw-deploy-panel.tsx +269 -21
- package/src/components/plugins/plugin-list.tsx +45 -9
- package/src/components/plugins/plugin-sheet.tsx +55 -7
- package/src/components/providers/provider-list.tsx +2 -1
- package/src/components/providers/provider-sheet.tsx +21 -2
- package/src/components/schedules/schedule-card.tsx +25 -1
- package/src/components/schedules/schedule-sheet.tsx +44 -2
- package/src/components/secrets/secret-sheet.tsx +21 -2
- package/src/components/shared/agent-switch-dialog.tsx +12 -1
- package/src/components/shared/bottom-sheet.tsx +13 -3
- package/src/components/shared/command-palette.tsx +8 -1
- package/src/components/shared/confirm-dialog.tsx +19 -4
- package/src/components/shared/connector-platform-icon.test.ts +28 -0
- package/src/components/shared/connector-platform-icon.tsx +39 -6
- package/src/components/shared/settings/plugin-manager.tsx +29 -6
- package/src/components/shared/settings/section-capability-policy.tsx +7 -3
- package/src/components/skills/skill-list.tsx +25 -0
- package/src/components/skills/skill-sheet.tsx +84 -12
- package/src/components/tasks/approvals-panel.tsx +191 -95
- package/src/components/tasks/task-board.tsx +273 -2
- package/src/components/tasks/task-card.tsx +38 -9
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/wallets/wallet-approval-dialog.tsx +4 -2
- package/src/components/wallets/wallet-panel.tsx +435 -90
- package/src/components/wallets/wallet-section.tsx +198 -48
- package/src/components/webhooks/webhook-sheet.tsx +22 -2
- package/src/lib/approval-display.ts +20 -0
- package/src/lib/canvas-content.ts +198 -0
- package/src/lib/chat-artifact-summary.ts +165 -0
- package/src/lib/chat-display.test.ts +91 -0
- package/src/lib/chat-display.ts +58 -0
- package/src/lib/chat-streaming-state.test.ts +47 -1
- package/src/lib/chat-streaming-state.ts +42 -0
- package/src/lib/ollama-model.ts +10 -0
- package/src/lib/openclaw-endpoint.test.ts +8 -0
- package/src/lib/openclaw-endpoint.ts +6 -1
- package/src/lib/plugin-install-cors.ts +46 -0
- package/src/lib/plugin-sources.test.ts +43 -0
- package/src/lib/plugin-sources.ts +77 -0
- package/src/lib/providers/ollama.ts +16 -6
- package/src/lib/providers/openclaw.test.ts +54 -0
- package/src/lib/providers/openclaw.ts +127 -11
- package/src/lib/schedule-dedupe-advanced.test.ts +1335 -0
- package/src/lib/schedule-dedupe.test.ts +66 -1
- package/src/lib/schedule-dedupe.ts +169 -12
- package/src/lib/schedule-origin.test.ts +20 -0
- package/src/lib/schedule-origin.ts +15 -0
- package/src/lib/server/__fixtures__/fake-mcp-stdio-server.mjs +27 -0
- package/src/lib/server/agent-availability.ts +16 -0
- package/src/lib/server/agent-runtime-config.ts +12 -4
- package/src/lib/server/agent-thread-session.test.ts +51 -0
- package/src/lib/server/agent-thread-session.ts +7 -0
- package/src/lib/server/approval-match.ts +205 -0
- package/src/lib/server/approvals-auto-approve.test.ts +538 -1
- package/src/lib/server/approvals.ts +214 -1
- package/src/lib/server/assistant-control.test.ts +29 -0
- package/src/lib/server/assistant-control.ts +23 -0
- package/src/lib/server/build-llm.test.ts +79 -0
- package/src/lib/server/build-llm.ts +14 -4
- package/src/lib/server/canvas-content.test.ts +32 -0
- package/src/lib/server/canvas-content.ts +6 -0
- package/src/lib/server/capability-router.test.ts +11 -0
- package/src/lib/server/capability-router.ts +26 -1
- package/src/lib/server/chat-execution-advanced.test.ts +651 -0
- package/src/lib/server/chat-execution-disabled.test.ts +94 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +157 -0
- package/src/lib/server/chat-execution.ts +353 -72
- package/src/lib/server/clawhub-client.test.ts +14 -8
- package/src/lib/server/connectors/manager.test.ts +1147 -0
- package/src/lib/server/connectors/manager.ts +362 -63
- package/src/lib/server/connectors/pairing.ts +26 -5
- package/src/lib/server/connectors/types.ts +2 -0
- package/src/lib/server/connectors/whatsapp.test.ts +134 -0
- package/src/lib/server/connectors/whatsapp.ts +271 -47
- package/src/lib/server/context-manager.ts +6 -1
- package/src/lib/server/daemon-state.ts +1 -1
- package/src/lib/server/data-dir.test.ts +37 -0
- package/src/lib/server/data-dir.ts +20 -1
- package/src/lib/server/delegation-jobs-advanced.test.ts +513 -0
- package/src/lib/server/devserver-launch.test.ts +60 -0
- package/src/lib/server/devserver-launch.ts +85 -0
- package/src/lib/server/elevenlabs.test.ts +189 -1
- package/src/lib/server/elevenlabs.ts +147 -43
- package/src/lib/server/ethereum.ts +590 -0
- package/src/lib/server/eval/agent-regression-advanced.test.ts +302 -0
- package/src/lib/server/eval/agent-regression.test.ts +18 -1
- package/src/lib/server/eval/agent-regression.ts +383 -11
- package/src/lib/server/evm-swap.ts +475 -0
- package/src/lib/server/execution-log.ts +1 -0
- package/src/lib/server/heartbeat-service-timer.test.ts +173 -0
- package/src/lib/server/heartbeat-service.ts +15 -10
- package/src/lib/server/heartbeat-wake.test.ts +112 -0
- package/src/lib/server/heartbeat-wake.ts +338 -57
- package/src/lib/server/main-agent-loop-advanced.test.ts +538 -0
- package/src/lib/server/mcp-client.test.ts +16 -0
- package/src/lib/server/mcp-client.ts +25 -0
- package/src/lib/server/memory-integration.test.ts +719 -0
- package/src/lib/server/memory-policy.test.ts +43 -0
- package/src/lib/server/memory-policy.ts +132 -0
- package/src/lib/server/memory-tiers.test.ts +60 -0
- package/src/lib/server/memory-tiers.ts +16 -0
- package/src/lib/server/ollama-runtime.ts +58 -0
- package/src/lib/server/openclaw-deploy.test.ts +109 -1
- package/src/lib/server/openclaw-deploy.ts +557 -81
- package/src/lib/server/openclaw-gateway.test.ts +131 -0
- package/src/lib/server/openclaw-gateway.ts +10 -4
- package/src/lib/server/openclaw-health.test.ts +35 -0
- package/src/lib/server/openclaw-health.ts +215 -47
- package/src/lib/server/orchestrator-lg.ts +2 -2
- package/src/lib/server/plugins-advanced.test.ts +351 -0
- package/src/lib/server/plugins.ts +205 -5
- package/src/lib/server/queue-advanced.test.ts +528 -0
- package/src/lib/server/queue-followups.test.ts +262 -0
- package/src/lib/server/queue-reconcile.test.ts +128 -0
- package/src/lib/server/queue.ts +293 -61
- package/src/lib/server/scheduler.ts +29 -1
- package/src/lib/server/session-note.test.ts +36 -0
- package/src/lib/server/session-note.ts +42 -0
- package/src/lib/server/session-run-manager.ts +52 -4
- package/src/lib/server/session-tools/canvas.ts +14 -12
- package/src/lib/server/session-tools/connector.test.ts +138 -0
- package/src/lib/server/session-tools/connector.ts +348 -61
- package/src/lib/server/session-tools/context.ts +12 -3
- package/src/lib/server/session-tools/crud.ts +221 -10
- package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
- package/src/lib/server/session-tools/delegate.ts +64 -8
- package/src/lib/server/session-tools/discovery-approvals.test.ts +142 -0
- package/src/lib/server/session-tools/discovery.ts +80 -12
- package/src/lib/server/session-tools/file-normalize.test.ts +36 -0
- package/src/lib/server/session-tools/file.ts +43 -4
- package/src/lib/server/session-tools/human-loop.ts +35 -5
- package/src/lib/server/session-tools/index.ts +44 -9
- package/src/lib/server/session-tools/manage-connectors.test.ts +139 -0
- package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +564 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +283 -0
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +852 -0
- package/src/lib/server/session-tools/memory.test.ts +93 -0
- package/src/lib/server/session-tools/memory.ts +546 -79
- package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
- package/src/lib/server/session-tools/plugin-creator.ts +57 -1
- package/src/lib/server/session-tools/primitive-tools.test.ts +6 -0
- package/src/lib/server/session-tools/schedule.ts +6 -1
- package/src/lib/server/session-tools/shell-normalize.test.ts +25 -1
- package/src/lib/server/session-tools/shell.ts +22 -3
- package/src/lib/server/session-tools/wallet-tool.test.ts +254 -0
- package/src/lib/server/session-tools/wallet.ts +1374 -139
- package/src/lib/server/session-tools/web-inputs.test.ts +162 -1
- package/src/lib/server/session-tools/web.ts +468 -64
- package/src/lib/server/skill-discovery.ts +128 -0
- package/src/lib/server/skill-eligibility.test.ts +84 -0
- package/src/lib/server/skill-eligibility.ts +95 -0
- package/src/lib/server/skill-prompt-budget.test.ts +102 -0
- package/src/lib/server/skill-prompt-budget.ts +125 -0
- package/src/lib/server/skills-normalize.test.ts +54 -0
- package/src/lib/server/skills-normalize.ts +372 -26
- package/src/lib/server/solana.ts +214 -29
- package/src/lib/server/storage.ts +65 -36
- package/src/lib/server/stream-agent-chat.test.ts +419 -9
- package/src/lib/server/stream-agent-chat.ts +887 -83
- package/src/lib/server/system-events.ts +1 -1
- package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
- package/src/lib/server/tool-loop-detection.test.ts +105 -0
- package/src/lib/server/tool-loop-detection.ts +260 -0
- package/src/lib/server/tool-planning.ts +4 -2
- package/src/lib/server/wallet-execution.test.ts +198 -0
- package/src/lib/server/wallet-portfolio.test.ts +98 -0
- package/src/lib/server/wallet-portfolio.ts +724 -0
- package/src/lib/server/wallet-service.test.ts +57 -0
- package/src/lib/server/wallet-service.ts +213 -0
- package/src/lib/server/watch-jobs-advanced.test.ts +594 -0
- package/src/lib/server/watch-jobs.ts +17 -2
- package/src/lib/server/workspace-context.ts +111 -0
- package/src/lib/skill-save-payload.test.ts +39 -0
- package/src/lib/skill-save-payload.ts +37 -0
- package/src/lib/tasks.ts +28 -0
- package/src/lib/tool-event-summary.test.ts +30 -0
- package/src/lib/tool-event-summary.ts +37 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/wallet-transactions.test.ts +75 -0
- package/src/lib/wallet-transactions.ts +43 -0
- package/src/lib/wallet.test.ts +17 -0
- package/src/lib/wallet.ts +183 -0
- package/src/proxy.test.ts +31 -0
- package/src/proxy.ts +34 -2
- package/src/stores/use-chat-store.ts +15 -1
- package/src/types/index.ts +210 -14
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
import { useEffect, useCallback, useState, useRef, useMemo } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
5
|
import { useWs } from '@/hooks/use-ws'
|
|
6
|
-
import { updateTask, bulkUpdateTasks } from '@/lib/tasks'
|
|
6
|
+
import { updateTask, bulkUpdateTasks, importGitHubIssues, type GitHubIssueImportResult } from '@/lib/tasks'
|
|
7
7
|
import { TaskColumn } from './task-column'
|
|
8
8
|
import { TaskCard } from './task-card'
|
|
9
9
|
import { Skeleton } from '@/components/shared/skeleton'
|
|
10
10
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
11
|
+
import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
12
|
+
import { inputClass } from '@/components/shared/form-styles'
|
|
11
13
|
import type { BoardTask, BoardTaskStatus } from '@/types'
|
|
12
14
|
import { toast } from 'sonner'
|
|
13
15
|
|
|
@@ -160,6 +162,15 @@ export function TaskBoard() {
|
|
|
160
162
|
})
|
|
161
163
|
const [viewMode, setViewMode] = useState<BoardViewMode>('board')
|
|
162
164
|
const [attentionFilter, setAttentionFilter] = useState<AttentionFilter>('all')
|
|
165
|
+
const [githubImportOpen, setGitHubImportOpen] = useState(false)
|
|
166
|
+
const [githubRepo, setGitHubRepo] = useState('')
|
|
167
|
+
const [githubToken, setGitHubToken] = useState('')
|
|
168
|
+
const [githubState, setGitHubState] = useState<'open' | 'closed' | 'all'>('open')
|
|
169
|
+
const [githubLimit, setGitHubLimit] = useState('25')
|
|
170
|
+
const [githubLabels, setGitHubLabels] = useState('')
|
|
171
|
+
const [githubImporting, setGitHubImporting] = useState(false)
|
|
172
|
+
const [githubImportError, setGitHubImportError] = useState<string | null>(null)
|
|
173
|
+
const [githubImportResult, setGitHubImportResult] = useState<GitHubIssueImportResult | null>(null)
|
|
163
174
|
|
|
164
175
|
// Seed activeProjectFilter from URL on mount
|
|
165
176
|
useEffect(() => {
|
|
@@ -185,7 +196,9 @@ export function TaskBoard() {
|
|
|
185
196
|
}, [filterAgentId, filterTag, activeProjectFilter, taskScopeFilter])
|
|
186
197
|
|
|
187
198
|
const [loaded, setLoaded] = useState(Object.keys(tasks).length > 0)
|
|
188
|
-
useEffect(() => {
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
Promise.all([loadTasks(), loadAgents(), loadProjects()]).then(() => setLoaded(true))
|
|
201
|
+
}, [loadAgents, loadProjects, loadTasks])
|
|
189
202
|
useWs('tasks', loadTasks, 5000)
|
|
190
203
|
|
|
191
204
|
// Collect all unique tags across tasks
|
|
@@ -252,6 +265,59 @@ export function TaskBoard() {
|
|
|
252
265
|
|
|
253
266
|
const archivedCount = Object.values(tasks).filter((t) => t.status === 'archived').length
|
|
254
267
|
|
|
268
|
+
const resetGitHubImportState = useCallback(() => {
|
|
269
|
+
setGitHubImportError(null)
|
|
270
|
+
setGitHubImportResult(null)
|
|
271
|
+
}, [])
|
|
272
|
+
|
|
273
|
+
const handleGitHubImport = useCallback(async () => {
|
|
274
|
+
if (!githubRepo.trim()) {
|
|
275
|
+
setGitHubImportError('Repository is required.')
|
|
276
|
+
return
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
setGitHubImporting(true)
|
|
280
|
+
setGitHubImportError(null)
|
|
281
|
+
setGitHubImportResult(null)
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
const rawLimit = Number.parseInt(githubLimit, 10)
|
|
285
|
+
const limit = Number.isFinite(rawLimit) ? Math.max(1, Math.min(rawLimit, 200)) : 25
|
|
286
|
+
const result = await importGitHubIssues({
|
|
287
|
+
repo: githubRepo.trim(),
|
|
288
|
+
token: githubToken.trim() || undefined,
|
|
289
|
+
state: githubState,
|
|
290
|
+
limit,
|
|
291
|
+
labels: githubLabels
|
|
292
|
+
.split(',')
|
|
293
|
+
.map((value) => value.trim())
|
|
294
|
+
.filter(Boolean),
|
|
295
|
+
projectId: activeProjectFilter,
|
|
296
|
+
})
|
|
297
|
+
setGitHubImportResult(result)
|
|
298
|
+
await loadTasks()
|
|
299
|
+
const summary = result.created.length > 0
|
|
300
|
+
? `Imported ${result.created.length} issue(s) from ${result.repo}`
|
|
301
|
+
: `No new issues imported from ${result.repo}`
|
|
302
|
+
const suffix = result.skipped.length > 0 ? `, skipped ${result.skipped.length} existing` : ''
|
|
303
|
+
toast.success(summary + suffix)
|
|
304
|
+
} catch (err) {
|
|
305
|
+
const message = err instanceof Error ? err.message : 'GitHub import failed'
|
|
306
|
+
setGitHubImportError(message)
|
|
307
|
+
toast.error(message)
|
|
308
|
+
} finally {
|
|
309
|
+
setGitHubImporting(false)
|
|
310
|
+
}
|
|
311
|
+
}, [
|
|
312
|
+
activeProjectFilter,
|
|
313
|
+
githubLabels,
|
|
314
|
+
githubLimit,
|
|
315
|
+
githubRepo,
|
|
316
|
+
githubState,
|
|
317
|
+
githubToken,
|
|
318
|
+
loadTasks,
|
|
319
|
+
])
|
|
320
|
+
|
|
255
321
|
// Task counts per project (non-archived)
|
|
256
322
|
const projectTaskCounts: Record<string, number> = {}
|
|
257
323
|
for (const t of scopedTasks) {
|
|
@@ -540,6 +606,16 @@ export function TaskBoard() {
|
|
|
540
606
|
>
|
|
541
607
|
{showArchived ? 'Hide' : 'Show'} Archived{!showArchived && archivedCount > 0 ? ` (${archivedCount})` : ''}
|
|
542
608
|
</button>
|
|
609
|
+
<button
|
|
610
|
+
onClick={() => {
|
|
611
|
+
resetGitHubImportState()
|
|
612
|
+
setGitHubImportOpen(true)
|
|
613
|
+
}}
|
|
614
|
+
className="px-4 py-2 rounded-[10px] text-[13px] font-600 cursor-pointer transition-all border border-white/[0.08] bg-white/[0.04] text-text-2 hover:bg-white/[0.08]"
|
|
615
|
+
style={{ fontFamily: 'inherit' }}
|
|
616
|
+
>
|
|
617
|
+
Import GitHub
|
|
618
|
+
</button>
|
|
543
619
|
<button
|
|
544
620
|
onClick={() => {
|
|
545
621
|
setEditingTaskId(null)
|
|
@@ -743,6 +819,201 @@ export function TaskBoard() {
|
|
|
743
819
|
</div>
|
|
744
820
|
)}
|
|
745
821
|
|
|
822
|
+
<BottomSheet
|
|
823
|
+
open={githubImportOpen}
|
|
824
|
+
onClose={() => {
|
|
825
|
+
setGitHubImportOpen(false)
|
|
826
|
+
setGitHubImportError(null)
|
|
827
|
+
}}
|
|
828
|
+
wide
|
|
829
|
+
title="Import GitHub Issues"
|
|
830
|
+
description="Pull issues from a GitHub repository into the task board as backlog items."
|
|
831
|
+
>
|
|
832
|
+
<div className="mx-auto w-full max-w-2xl">
|
|
833
|
+
<div className="mb-6">
|
|
834
|
+
<h2 className="font-display text-[24px] font-800 tracking-[-0.03em] text-text">Import GitHub Issues</h2>
|
|
835
|
+
<p className="mt-2 text-[14px] text-text-3">
|
|
836
|
+
Pull issues from a GitHub repository into the task board as backlog items.
|
|
837
|
+
</p>
|
|
838
|
+
</div>
|
|
839
|
+
|
|
840
|
+
<div className="grid gap-4 sm:grid-cols-[minmax(0,1fr)_180px]">
|
|
841
|
+
<label className="block">
|
|
842
|
+
<span className="mb-2 block text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/70">Repository</span>
|
|
843
|
+
<input
|
|
844
|
+
value={githubRepo}
|
|
845
|
+
onChange={(e) => setGitHubRepo(e.target.value)}
|
|
846
|
+
placeholder="owner/repo or https://github.com/owner/repo"
|
|
847
|
+
className={inputClass}
|
|
848
|
+
style={{ fontFamily: 'inherit' }}
|
|
849
|
+
/>
|
|
850
|
+
</label>
|
|
851
|
+
<label className="block">
|
|
852
|
+
<span className="mb-2 block text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/70">State</span>
|
|
853
|
+
<select
|
|
854
|
+
value={githubState}
|
|
855
|
+
onChange={(e) => setGitHubState(e.target.value as 'open' | 'closed' | 'all')}
|
|
856
|
+
className={inputClass}
|
|
857
|
+
style={{ fontFamily: 'inherit' }}
|
|
858
|
+
>
|
|
859
|
+
<option value="open">Open issues</option>
|
|
860
|
+
<option value="closed">Closed issues</option>
|
|
861
|
+
<option value="all">All issues</option>
|
|
862
|
+
</select>
|
|
863
|
+
</label>
|
|
864
|
+
</div>
|
|
865
|
+
|
|
866
|
+
<div className="mt-4 grid gap-4 sm:grid-cols-2">
|
|
867
|
+
<label className="block">
|
|
868
|
+
<span className="mb-2 block text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/70">Limit</span>
|
|
869
|
+
<input
|
|
870
|
+
type="number"
|
|
871
|
+
min={1}
|
|
872
|
+
max={200}
|
|
873
|
+
value={githubLimit}
|
|
874
|
+
onChange={(e) => setGitHubLimit(e.target.value)}
|
|
875
|
+
className={inputClass}
|
|
876
|
+
style={{ fontFamily: 'inherit' }}
|
|
877
|
+
/>
|
|
878
|
+
</label>
|
|
879
|
+
<label className="block">
|
|
880
|
+
<span className="mb-2 block text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/70">Labels</span>
|
|
881
|
+
<input
|
|
882
|
+
value={githubLabels}
|
|
883
|
+
onChange={(e) => setGitHubLabels(e.target.value)}
|
|
884
|
+
placeholder="bug, api, high priority"
|
|
885
|
+
className={inputClass}
|
|
886
|
+
style={{ fontFamily: 'inherit' }}
|
|
887
|
+
/>
|
|
888
|
+
</label>
|
|
889
|
+
</div>
|
|
890
|
+
|
|
891
|
+
<label className="mt-4 block">
|
|
892
|
+
<span className="mb-2 block text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/70">GitHub token</span>
|
|
893
|
+
<input
|
|
894
|
+
type="password"
|
|
895
|
+
value={githubToken}
|
|
896
|
+
onChange={(e) => setGitHubToken(e.target.value)}
|
|
897
|
+
placeholder="Optional. Needed for private repos or higher rate limits."
|
|
898
|
+
className={inputClass}
|
|
899
|
+
style={{ fontFamily: 'inherit' }}
|
|
900
|
+
/>
|
|
901
|
+
</label>
|
|
902
|
+
|
|
903
|
+
<div className="mt-3 flex flex-wrap items-center gap-2">
|
|
904
|
+
<span className="inline-flex items-center rounded-full bg-white/[0.05] px-3 py-1 text-[12px] font-600 text-text-2">
|
|
905
|
+
Imported tasks land in backlog
|
|
906
|
+
</span>
|
|
907
|
+
{activeProjectFilter && projects[activeProjectFilter] && (
|
|
908
|
+
<span className="inline-flex items-center gap-1.5 rounded-full bg-accent-soft px-3 py-1 text-[12px] font-600 text-accent-bright">
|
|
909
|
+
<span className="h-2 w-2 rounded-full" style={{ backgroundColor: projects[activeProjectFilter].color || '#6366F1' }} />
|
|
910
|
+
Project: {projects[activeProjectFilter].name}
|
|
911
|
+
</span>
|
|
912
|
+
)}
|
|
913
|
+
</div>
|
|
914
|
+
|
|
915
|
+
{githubImportError && (
|
|
916
|
+
<div className="mt-4 rounded-[14px] border border-red-500/20 bg-red-500/10 px-4 py-3 text-[13px] text-red-200">
|
|
917
|
+
{githubImportError}
|
|
918
|
+
</div>
|
|
919
|
+
)}
|
|
920
|
+
|
|
921
|
+
{githubImportResult && (
|
|
922
|
+
<div className="mt-5 rounded-[18px] border border-white/[0.08] bg-white/[0.03] p-4">
|
|
923
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
924
|
+
<span className="text-[14px] font-700 text-text">{githubImportResult.repo}</span>
|
|
925
|
+
<span className="rounded-full bg-white/[0.05] px-2 py-1 text-[11px] font-600 text-text-3">
|
|
926
|
+
{githubImportResult.fetched} fetched
|
|
927
|
+
</span>
|
|
928
|
+
<span className="rounded-full bg-emerald-500/10 px-2 py-1 text-[11px] font-600 text-emerald-300">
|
|
929
|
+
{githubImportResult.created.length} created
|
|
930
|
+
</span>
|
|
931
|
+
<span className="rounded-full bg-amber-500/10 px-2 py-1 text-[11px] font-600 text-amber-300">
|
|
932
|
+
{githubImportResult.skipped.length} skipped
|
|
933
|
+
</span>
|
|
934
|
+
</div>
|
|
935
|
+
|
|
936
|
+
{githubImportResult.created.length > 0 && (
|
|
937
|
+
<div className="mt-4">
|
|
938
|
+
<p className="text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/60">Created</p>
|
|
939
|
+
<div className="mt-2 flex flex-col gap-2">
|
|
940
|
+
{githubImportResult.created.slice(0, 8).map((item) => (
|
|
941
|
+
item.url ? (
|
|
942
|
+
<a
|
|
943
|
+
key={`created-${item.taskId || item.number}`}
|
|
944
|
+
href={item.url}
|
|
945
|
+
target="_blank"
|
|
946
|
+
rel="noreferrer"
|
|
947
|
+
className="rounded-[12px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[13px] text-text-2 no-underline transition-colors hover:bg-white/[0.05]"
|
|
948
|
+
>
|
|
949
|
+
#{item.number} {item.title}
|
|
950
|
+
</a>
|
|
951
|
+
) : (
|
|
952
|
+
<div
|
|
953
|
+
key={`created-${item.taskId || item.number}`}
|
|
954
|
+
className="rounded-[12px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[13px] text-text-2"
|
|
955
|
+
>
|
|
956
|
+
#{item.number} {item.title}
|
|
957
|
+
</div>
|
|
958
|
+
)
|
|
959
|
+
))}
|
|
960
|
+
</div>
|
|
961
|
+
</div>
|
|
962
|
+
)}
|
|
963
|
+
|
|
964
|
+
{githubImportResult.skipped.length > 0 && (
|
|
965
|
+
<div className="mt-4">
|
|
966
|
+
<p className="text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/60">Skipped existing</p>
|
|
967
|
+
<div className="mt-2 flex flex-col gap-2">
|
|
968
|
+
{githubImportResult.skipped.slice(0, 8).map((item) => (
|
|
969
|
+
item.url ? (
|
|
970
|
+
<a
|
|
971
|
+
key={`skipped-${item.taskId || item.number}`}
|
|
972
|
+
href={item.url}
|
|
973
|
+
target="_blank"
|
|
974
|
+
rel="noreferrer"
|
|
975
|
+
className="rounded-[12px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[13px] text-text-3 no-underline transition-colors hover:bg-white/[0.05] hover:text-text-2"
|
|
976
|
+
>
|
|
977
|
+
#{item.number} {item.title}
|
|
978
|
+
</a>
|
|
979
|
+
) : (
|
|
980
|
+
<div
|
|
981
|
+
key={`skipped-${item.taskId || item.number}`}
|
|
982
|
+
className="rounded-[12px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[13px] text-text-3"
|
|
983
|
+
>
|
|
984
|
+
#{item.number} {item.title}
|
|
985
|
+
</div>
|
|
986
|
+
)
|
|
987
|
+
))}
|
|
988
|
+
</div>
|
|
989
|
+
</div>
|
|
990
|
+
)}
|
|
991
|
+
</div>
|
|
992
|
+
)}
|
|
993
|
+
|
|
994
|
+
<div className="mt-6 flex items-center justify-end gap-3">
|
|
995
|
+
<button
|
|
996
|
+
onClick={() => {
|
|
997
|
+
setGitHubImportOpen(false)
|
|
998
|
+
setGitHubImportError(null)
|
|
999
|
+
}}
|
|
1000
|
+
className="px-4 py-2 rounded-[10px] border border-white/[0.08] bg-transparent text-[13px] font-600 text-text-3 transition-colors hover:bg-white/[0.04] hover:text-text-2"
|
|
1001
|
+
style={{ fontFamily: 'inherit' }}
|
|
1002
|
+
>
|
|
1003
|
+
Close
|
|
1004
|
+
</button>
|
|
1005
|
+
<button
|
|
1006
|
+
onClick={handleGitHubImport}
|
|
1007
|
+
disabled={githubImporting}
|
|
1008
|
+
className="px-5 py-2.5 rounded-[12px] border-none bg-accent-bright text-white text-[14px] font-700 transition-all disabled:cursor-not-allowed disabled:opacity-60"
|
|
1009
|
+
style={{ fontFamily: 'inherit' }}
|
|
1010
|
+
>
|
|
1011
|
+
{githubImporting ? 'Importing...' : 'Import issues'}
|
|
1012
|
+
</button>
|
|
1013
|
+
</div>
|
|
1014
|
+
</div>
|
|
1015
|
+
</BottomSheet>
|
|
1016
|
+
|
|
746
1017
|
{/* Bulk action bar */}
|
|
747
1018
|
{selectionMode && (
|
|
748
1019
|
<div className="absolute bottom-6 left-1/2 -translate-x-1/2 flex items-center gap-2 px-4 py-3 rounded-[16px] bg-surface-2/95 backdrop-blur-xl border border-white/[0.1] shadow-[0_8px_40px_rgba(0,0,0,0.5)] z-50">
|
|
@@ -8,8 +8,8 @@ import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
|
8
8
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
9
9
|
import type { BoardTask } from '@/types'
|
|
10
10
|
|
|
11
|
-
function timeAgo(ts: number) {
|
|
12
|
-
const diff =
|
|
11
|
+
function timeAgo(ts: number, now: number) {
|
|
12
|
+
const diff = now - ts
|
|
13
13
|
if (diff < 60_000) return 'just now'
|
|
14
14
|
if (diff < 3600_000) return `${Math.floor(diff / 60_000)}m ago`
|
|
15
15
|
if (diff < 86400_000) return `${Math.floor(diff / 3600_000)}h ago`
|
|
@@ -35,13 +35,21 @@ export function TaskCard({ task, selectionMode, selected, onToggleSelect, index
|
|
|
35
35
|
const [dragging, setDragging] = useState(false)
|
|
36
36
|
const [confirmArchive, setConfirmArchive] = useState(false)
|
|
37
37
|
const [allowDrag, setAllowDrag] = useState(false)
|
|
38
|
+
const [now, setNow] = useState(() => Date.now())
|
|
38
39
|
|
|
39
40
|
useEffect(() => {
|
|
40
41
|
if (typeof window === 'undefined') return
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
const frame = window.requestAnimationFrame(() => {
|
|
43
|
+
const isCoarsePointer = typeof window.matchMedia === 'function'
|
|
44
|
+
? window.matchMedia('(pointer: coarse)').matches
|
|
45
|
+
: 'ontouchstart' in window
|
|
46
|
+
setAllowDrag(!isCoarsePointer)
|
|
47
|
+
})
|
|
48
|
+
const timer = window.setInterval(() => setNow(Date.now()), 60_000)
|
|
49
|
+
return () => {
|
|
50
|
+
window.cancelAnimationFrame(frame)
|
|
51
|
+
window.clearInterval(timer)
|
|
52
|
+
}
|
|
45
53
|
}, [])
|
|
46
54
|
|
|
47
55
|
const tasks = useAppStore((s) => s.tasks)
|
|
@@ -49,6 +57,7 @@ export function TaskCard({ task, selectionMode, selected, onToggleSelect, index
|
|
|
49
57
|
const project = task.projectId ? projects[task.projectId] : null
|
|
50
58
|
const creatorAgent = task.createdByAgentId ? agents[task.createdByAgentId] : null
|
|
51
59
|
const delegatorAgent = task.delegatedByAgentId ? agents[task.delegatedByAgentId] : null
|
|
60
|
+
const githubSource = task.externalSource?.source === 'github' ? task.externalSource : null
|
|
52
61
|
|
|
53
62
|
const priorityConfig = {
|
|
54
63
|
critical: { label: 'Critical', cls: 'bg-red-500/10 text-red-400' },
|
|
@@ -59,7 +68,7 @@ export function TaskCard({ task, selectionMode, selected, onToggleSelect, index
|
|
|
59
68
|
const prio = task.priority && priorityConfig[task.priority]
|
|
60
69
|
|
|
61
70
|
const isBlocked = Array.isArray(task.blockedBy) && task.blockedBy.length > 0
|
|
62
|
-
const isOverdue = task.dueAt && task.dueAt <
|
|
71
|
+
const isOverdue = task.dueAt && task.dueAt < now && task.status !== 'completed' && task.status !== 'archived'
|
|
63
72
|
const borderColor = isBlocked ? 'border-l-rose-500'
|
|
64
73
|
: task.pendingApproval ? 'border-l-amber-500'
|
|
65
74
|
: task.status === 'running' ? 'border-l-emerald-500'
|
|
@@ -210,7 +219,7 @@ export function TaskCard({ task, selectionMode, selected, onToggleSelect, index
|
|
|
210
219
|
</div>
|
|
211
220
|
)}
|
|
212
221
|
|
|
213
|
-
{(creatorAgent || delegatorAgent || task.sourceType === 'schedule') && (
|
|
222
|
+
{(creatorAgent || delegatorAgent || task.sourceType === 'schedule' || githubSource) && (
|
|
214
223
|
<div className="flex flex-wrap gap-1.5 mb-3">
|
|
215
224
|
{delegatorAgent && (
|
|
216
225
|
<span className="inline-flex items-center gap-1.5 rounded-[7px] bg-amber-500/10 px-2 py-1 text-[10px] font-600 text-amber-300">
|
|
@@ -233,6 +242,26 @@ export function TaskCard({ task, selectionMode, selected, onToggleSelect, index
|
|
|
233
242
|
{task.sourceScheduleName ? `Scheduled via ${task.sourceScheduleName}` : 'Scheduled task'}
|
|
234
243
|
</span>
|
|
235
244
|
)}
|
|
245
|
+
{githubSource && (
|
|
246
|
+
githubSource.url ? (
|
|
247
|
+
<a
|
|
248
|
+
href={githubSource.url}
|
|
249
|
+
target="_blank"
|
|
250
|
+
rel="noreferrer"
|
|
251
|
+
onClick={(e) => e.stopPropagation()}
|
|
252
|
+
className="inline-flex items-center gap-1.5 rounded-[7px] bg-sky-500/10 px-2 py-1 text-[10px] font-600 text-sky-300 hover:bg-sky-500/15"
|
|
253
|
+
>
|
|
254
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor" className="shrink-0">
|
|
255
|
+
<path d="M12 .5C5.65.5.5 5.65.5 12A11.5 11.5 0 0 0 8.36 22.9c.57.1.78-.25.78-.55 0-.27-.01-1.17-.02-2.13-3.2.69-3.88-1.36-3.88-1.36-.52-1.33-1.28-1.68-1.28-1.68-1.05-.72.08-.71.08-.71 1.16.08 1.77 1.19 1.77 1.19 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.55-.29-5.24-1.28-5.24-5.68 0-1.25.45-2.27 1.18-3.07-.12-.29-.51-1.45.11-3.02 0 0 .96-.31 3.15 1.17a10.9 10.9 0 0 1 5.73 0c2.18-1.48 3.14-1.17 3.14-1.17.63 1.57.24 2.73.12 3.02.74.8 1.18 1.82 1.18 3.07 0 4.41-2.7 5.38-5.27 5.66.42.36.78 1.06.78 2.14 0 1.55-.01 2.79-.01 3.17 0 .31.2.66.79.55A11.5 11.5 0 0 0 23.5 12C23.5 5.65 18.35.5 12 .5Z" />
|
|
256
|
+
</svg>
|
|
257
|
+
{githubSource.repo ? `${githubSource.repo}#${githubSource.number}` : `GitHub #${githubSource.number ?? githubSource.id}`}
|
|
258
|
+
</a>
|
|
259
|
+
) : (
|
|
260
|
+
<span className="inline-flex items-center gap-1.5 rounded-[7px] bg-sky-500/10 px-2 py-1 text-[10px] font-600 text-sky-300">
|
|
261
|
+
GitHub {githubSource.repo ? `${githubSource.repo}#${githubSource.number}` : `#${githubSource.number ?? githubSource.id}`}
|
|
262
|
+
</span>
|
|
263
|
+
)
|
|
264
|
+
)}
|
|
236
265
|
</div>
|
|
237
266
|
)}
|
|
238
267
|
|
|
@@ -248,7 +277,7 @@ export function TaskCard({ task, selectionMode, selected, onToggleSelect, index
|
|
|
248
277
|
{project.name}
|
|
249
278
|
</span>
|
|
250
279
|
)}
|
|
251
|
-
<span className="text-[11px] text-text-3">{timeAgo(task.updatedAt)}</span>
|
|
280
|
+
<span className="text-[11px] text-text-3">{timeAgo(task.updatedAt, now)}</span>
|
|
252
281
|
{task.comments && task.comments.length > 0 && (
|
|
253
282
|
<span className="flex items-center gap-1 text-[11px] text-text-3">
|
|
254
283
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="text-text-3/60">
|
|
@@ -39,7 +39,7 @@ function DialogOverlay({
|
|
|
39
39
|
<DialogPrimitive.Overlay
|
|
40
40
|
data-slot="dialog-overlay"
|
|
41
41
|
className={cn(
|
|
42
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-
|
|
42
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-[1400] bg-black/72 backdrop-blur-md",
|
|
43
43
|
className
|
|
44
44
|
)}
|
|
45
45
|
{...props}
|
|
@@ -61,7 +61,7 @@ function DialogContent({
|
|
|
61
61
|
<DialogPrimitive.Content
|
|
62
62
|
data-slot="dialog-content"
|
|
63
63
|
className={cn(
|
|
64
|
-
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-
|
|
64
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-[1400] grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
|
|
65
65
|
className
|
|
66
66
|
)}
|
|
67
67
|
style={{ animation: 'spring-in 0.4s var(--ease-spring)' }}
|
|
@@ -4,6 +4,7 @@ import { useState, useCallback } from 'react'
|
|
|
4
4
|
import { api } from '@/lib/api-client'
|
|
5
5
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
|
6
6
|
import type { WalletTransaction } from '@/types'
|
|
7
|
+
import { formatWalletAmount, getWalletAssetSymbol, getWalletAtomicAmount } from '@/lib/wallet'
|
|
7
8
|
|
|
8
9
|
interface WalletApprovalDialogProps {
|
|
9
10
|
transaction: WalletTransaction
|
|
@@ -33,7 +34,8 @@ export function WalletApprovalDialog({ transaction, walletAddress, onClose, onRe
|
|
|
33
34
|
}
|
|
34
35
|
}, [transaction, onResolved, onClose])
|
|
35
36
|
|
|
36
|
-
const
|
|
37
|
+
const amountFormatted = formatWalletAmount(transaction.chain, getWalletAtomicAmount(transaction), { minFractionDigits: 4, maxFractionDigits: 6 })
|
|
38
|
+
const symbol = getWalletAssetSymbol(transaction.chain)
|
|
37
39
|
|
|
38
40
|
return (
|
|
39
41
|
<Dialog open onOpenChange={(nextOpen) => { if (!nextOpen) onClose() }}>
|
|
@@ -57,7 +59,7 @@ export function WalletApprovalDialog({ transaction, walletAddress, onClose, onRe
|
|
|
57
59
|
<div className="rounded-[14px] border border-white/[0.06] bg-black/20 p-4 space-y-3">
|
|
58
60
|
<div className="flex items-center justify-between">
|
|
59
61
|
<span className="text-[11px] uppercase tracking-wide text-text-3/70">Amount</span>
|
|
60
|
-
<span className="text-[16px] font-600 text-text-1">{
|
|
62
|
+
<span className="text-[16px] font-600 text-text-1">{amountFormatted} {symbol}</span>
|
|
61
63
|
</div>
|
|
62
64
|
<div>
|
|
63
65
|
<span className="mb-1 block text-[11px] uppercase tracking-wide text-text-3/70">From</span>
|