@swarmclawai/swarmclaw 0.7.7 → 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 -14
- 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 +23 -2
- package/src/app/api/clawhub/install/route.ts +28 -8
- package/src/app/api/connectors/[id]/route.ts +46 -3
- package/src/app/api/connectors/route.ts +12 -8
- 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/projects/[id]/route.ts +6 -2
- package/src/app/api/projects/route.ts +4 -3
- 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/secrets/[id]/route.ts +1 -0
- package/src/app/api/secrets/route.ts +2 -1
- package/src/app/api/settings/route.ts +2 -0
- 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 +257 -38
- package/src/components/agents/inspector-panel.tsx +41 -0
- package/src/components/canvas/canvas-panel.tsx +236 -65
- package/src/components/chat/chat-area.tsx +36 -19
- package/src/components/chat/chat-card.tsx +36 -13
- package/src/components/chat/chat-header.tsx +48 -16
- package/src/components/chat/chat-list.tsx +28 -4
- package/src/components/chat/checkpoint-timeline.tsx +50 -34
- package/src/components/chat/delegation-banner.test.ts +14 -1
- package/src/components/chat/delegation-banner.tsx +1 -1
- 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/layout/app-layout.tsx +40 -23
- 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/projects/project-detail.tsx +217 -0
- package/src/components/projects/project-sheet.tsx +176 -4
- 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 +45 -3
- package/src/components/shared/settings/section-voice.tsx +11 -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 +289 -34
- package/src/components/tasks/task-board.tsx +410 -25
- package/src/components/tasks/task-card.tsx +66 -8
- package/src/components/tasks/task-sheet.tsx +16 -4
- 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 +33 -0
- package/src/lib/server/capability-router.ts +80 -19
- 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 +378 -73
- package/src/lib/server/clawhub-client.test.ts +14 -8
- package/src/lib/server/connectors/manager-reconnect.test.ts +47 -0
- package/src/lib/server/connectors/manager.test.ts +1147 -0
- package/src/lib/server/connectors/manager.ts +461 -137
- 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 +84 -47
- 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 +247 -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 +20 -11
- 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/main-agent-loop.test.ts +260 -0
- package/src/lib/server/main-agent-loop.ts +559 -14
- 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 +3 -2
- package/src/lib/server/orchestrator.ts +2 -0
- package/src/lib/server/plugins-advanced.test.ts +351 -0
- package/src/lib/server/plugins.ts +211 -6
- package/src/lib/server/project-context.ts +162 -0
- package/src/lib/server/project-utils.ts +150 -0
- package/src/lib/server/queue-advanced.test.ts +528 -0
- package/src/lib/server/queue-followups.test.ts +409 -2
- package/src/lib/server/queue-reconcile.test.ts +128 -0
- package/src/lib/server/queue.ts +527 -68
- 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 +83 -4
- package/src/lib/server/session-tools/canvas.ts +14 -12
- package/src/lib/server/session-tools/connector-inputs.test.ts +37 -0
- package/src/lib/server/session-tools/connector.test.ts +138 -0
- package/src/lib/server/session-tools/connector.ts +366 -54
- package/src/lib/server/session-tools/context.ts +17 -3
- package/src/lib/server/session-tools/crud.ts +484 -84
- package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
- package/src/lib/server/session-tools/delegate-resume.test.ts +50 -0
- package/src/lib/server/session-tools/delegate.ts +102 -10
- 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/manage-tasks.test.ts +114 -0
- package/src/lib/server/session-tools/memory.test.ts +93 -0
- package/src/lib/server/session-tools/memory.ts +554 -75
- package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
- package/src/lib/server/session-tools/platform-access.test.ts +58 -0
- package/src/lib/server/session-tools/platform.ts +60 -19
- 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 +178 -0
- package/src/lib/server/session-tools/web.ts +621 -70
- 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 +437 -2
- package/src/lib/server/stream-agent-chat.ts +957 -79
- package/src/lib/server/system-events.ts +1 -1
- package/src/lib/server/tool-aliases.ts +2 -0
- package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
- package/src/lib/server/tool-capability-policy.test.ts +24 -0
- package/src/lib/server/tool-capability-policy.ts +29 -1
- 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.test.ts +44 -0
- package/src/lib/server/tool-planning.ts +271 -0
- 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-definitions.ts +2 -1
- 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 +249 -14
package/src/lib/server/solana.ts
CHANGED
|
@@ -1,12 +1,95 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Connection,
|
|
3
|
+
Keypair,
|
|
4
|
+
LAMPORTS_PER_SOL,
|
|
5
|
+
PublicKey,
|
|
6
|
+
SystemProgram,
|
|
7
|
+
Transaction,
|
|
8
|
+
VersionedTransaction,
|
|
9
|
+
sendAndConfirmTransaction,
|
|
10
|
+
} from '@solana/web3.js'
|
|
2
11
|
import bs58 from 'bs58'
|
|
3
|
-
import
|
|
12
|
+
import nacl from 'tweetnacl'
|
|
13
|
+
|
|
14
|
+
import { decryptKey, encryptKey } from './storage'
|
|
15
|
+
|
|
16
|
+
export type SolanaCluster = 'mainnet-beta' | 'devnet' | 'testnet'
|
|
17
|
+
|
|
18
|
+
export interface SolanaExecutionOptions {
|
|
19
|
+
cluster?: SolanaCluster | string | null
|
|
20
|
+
rpcUrl?: string | null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SolanaMessageInput {
|
|
24
|
+
message?: string | null
|
|
25
|
+
messageHex?: string | null
|
|
26
|
+
messageBase64?: string | null
|
|
27
|
+
}
|
|
4
28
|
|
|
5
29
|
const DEFAULT_RPC_URL = process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com'
|
|
6
30
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
31
|
+
function getClusterRpcUrl(cluster: SolanaCluster): string {
|
|
32
|
+
if (cluster === 'devnet') return process.env.SOLANA_DEVNET_RPC_URL || 'https://api.devnet.solana.com'
|
|
33
|
+
if (cluster === 'testnet') return process.env.SOLANA_TESTNET_RPC_URL || 'https://api.testnet.solana.com'
|
|
34
|
+
return process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalizeHexMessage(value: string): Uint8Array {
|
|
38
|
+
const trimmed = value.trim()
|
|
39
|
+
if (!/^0x[0-9a-fA-F]*$/.test(trimmed)) {
|
|
40
|
+
throw new Error('messageHex must be a 0x-prefixed hex string')
|
|
41
|
+
}
|
|
42
|
+
return Uint8Array.from(Buffer.from(trimmed.slice(2), 'hex'))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function normalizeMessageBytes(input: SolanaMessageInput): Uint8Array {
|
|
46
|
+
if (typeof input.messageHex === 'string' && input.messageHex.trim()) return normalizeHexMessage(input.messageHex)
|
|
47
|
+
if (typeof input.messageBase64 === 'string' && input.messageBase64.trim()) {
|
|
48
|
+
return Uint8Array.from(Buffer.from(input.messageBase64.trim(), 'base64'))
|
|
49
|
+
}
|
|
50
|
+
if (typeof input.message === 'string') return new TextEncoder().encode(input.message)
|
|
51
|
+
throw new Error('message, messageHex, or messageBase64 is required')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function deserializeTransactionBase64(value: string): Transaction | VersionedTransaction {
|
|
55
|
+
const bytes = Buffer.from(value, 'base64')
|
|
56
|
+
try {
|
|
57
|
+
return VersionedTransaction.deserialize(bytes)
|
|
58
|
+
} catch {
|
|
59
|
+
return Transaction.from(bytes)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function serializeTransactionBase64(transaction: Transaction | VersionedTransaction): string {
|
|
64
|
+
return Buffer.from(transaction.serialize()).toString('base64')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function collectTransactionSignatures(transaction: Transaction | VersionedTransaction): string[] {
|
|
68
|
+
if (transaction instanceof VersionedTransaction) {
|
|
69
|
+
return transaction.signatures
|
|
70
|
+
.map((signature) => bs58.encode(signature))
|
|
71
|
+
.filter(Boolean)
|
|
72
|
+
}
|
|
73
|
+
return transaction.signatures
|
|
74
|
+
.map((entry) => (entry.signature ? bs58.encode(entry.signature) : ''))
|
|
75
|
+
.filter(Boolean)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function signTransactionWithWallet(
|
|
79
|
+
encryptedPrivateKey: string,
|
|
80
|
+
transaction: Transaction | VersionedTransaction,
|
|
81
|
+
): { transaction: Transaction | VersionedTransaction; publicKey: string } {
|
|
82
|
+
const keypair = getKeypairFromEncrypted(encryptedPrivateKey)
|
|
83
|
+
if (transaction instanceof VersionedTransaction) {
|
|
84
|
+
transaction.sign([keypair])
|
|
85
|
+
} else {
|
|
86
|
+
transaction.sign(keypair)
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
transaction,
|
|
90
|
+
publicKey: keypair.publicKey.toBase58(),
|
|
91
|
+
}
|
|
92
|
+
}
|
|
10
93
|
|
|
11
94
|
export function generateSolanaKeypair(): { publicKey: string; encryptedPrivateKey: string } {
|
|
12
95
|
const keypair = Keypair.generate()
|
|
@@ -23,17 +106,36 @@ export function getKeypairFromEncrypted(encryptedPrivateKey: string): Keypair {
|
|
|
23
106
|
return Keypair.fromSecretKey(secretKey)
|
|
24
107
|
}
|
|
25
108
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
109
|
+
export function normalizeSolanaCluster(value: unknown, fallback: SolanaCluster = 'mainnet-beta'): SolanaCluster {
|
|
110
|
+
const normalized = String(value ?? '').trim().toLowerCase()
|
|
111
|
+
if (!normalized) return fallback
|
|
112
|
+
if (normalized === 'mainnet' || normalized === 'mainnet-beta' || normalized === 'solana') return 'mainnet-beta'
|
|
113
|
+
if (normalized === 'devnet') return 'devnet'
|
|
114
|
+
if (normalized === 'testnet') return 'testnet'
|
|
115
|
+
throw new Error(`Unsupported Solana cluster: ${String(value)}`)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function getSolanaClusterLabel(value?: unknown): string {
|
|
119
|
+
const cluster = normalizeSolanaCluster(value)
|
|
120
|
+
if (cluster === 'devnet') return 'Solana Devnet'
|
|
121
|
+
if (cluster === 'testnet') return 'Solana Testnet'
|
|
122
|
+
return 'Solana Mainnet'
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function getSolanaExplorerUrl(cluster: SolanaCluster | string | null | undefined, kind: 'address' | 'transaction', value: string): string {
|
|
126
|
+
const normalized = normalizeSolanaCluster(cluster)
|
|
127
|
+
const prefix = kind === 'address' ? 'address' : 'tx'
|
|
128
|
+
const clusterSuffix = normalized === 'mainnet-beta' ? '' : `?cluster=${normalized}`
|
|
129
|
+
return `https://explorer.solana.com/${prefix}/${value}${clusterSuffix}`
|
|
130
|
+
}
|
|
29
131
|
|
|
30
132
|
export function getConnection(rpcUrl?: string): Connection {
|
|
31
133
|
return new Connection(rpcUrl || DEFAULT_RPC_URL, 'confirmed')
|
|
32
134
|
}
|
|
33
135
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
136
|
+
export function getConnectionForCluster(cluster?: SolanaCluster | string | null, rpcUrl?: string | null): Connection {
|
|
137
|
+
return new Connection(rpcUrl || getClusterRpcUrl(normalizeSolanaCluster(cluster)), 'confirmed')
|
|
138
|
+
}
|
|
37
139
|
|
|
38
140
|
export async function getBalance(publicKey: string, rpcUrl?: string): Promise<number> {
|
|
39
141
|
const connection = getConnection(rpcUrl)
|
|
@@ -41,10 +143,6 @@ export async function getBalance(publicKey: string, rpcUrl?: string): Promise<nu
|
|
|
41
143
|
return connection.getBalance(pk)
|
|
42
144
|
}
|
|
43
145
|
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
// Send SOL
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
|
|
48
146
|
export async function sendSol(
|
|
49
147
|
encryptedPrivateKey: string,
|
|
50
148
|
toAddress: string,
|
|
@@ -65,21 +163,116 @@ export async function sendSol(
|
|
|
65
163
|
|
|
66
164
|
const signature = await sendAndConfirmTransaction(connection, transaction, [fromKeypair])
|
|
67
165
|
|
|
68
|
-
|
|
69
|
-
let fee = 5000 // default fee estimate
|
|
166
|
+
let fee = 5000
|
|
70
167
|
try {
|
|
71
168
|
const txInfo = await connection.getTransaction(signature, { commitment: 'confirmed' })
|
|
72
169
|
if (txInfo?.meta?.fee) fee = txInfo.meta.fee
|
|
73
170
|
} catch {
|
|
74
|
-
//
|
|
171
|
+
// keep default
|
|
75
172
|
}
|
|
76
173
|
|
|
77
174
|
return { signature, fee }
|
|
78
175
|
}
|
|
79
176
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
177
|
+
export async function signSolanaMessage(
|
|
178
|
+
encryptedPrivateKey: string,
|
|
179
|
+
input: SolanaMessageInput,
|
|
180
|
+
): Promise<{ signature: string; publicKey: string }> {
|
|
181
|
+
const keypair = getKeypairFromEncrypted(encryptedPrivateKey)
|
|
182
|
+
const signature = nacl.sign.detached(normalizeMessageBytes(input), keypair.secretKey)
|
|
183
|
+
return {
|
|
184
|
+
signature: bs58.encode(signature),
|
|
185
|
+
publicKey: keypair.publicKey.toBase58(),
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export async function signSolanaTransaction(
|
|
190
|
+
encryptedPrivateKey: string,
|
|
191
|
+
transactionBase64: string,
|
|
192
|
+
): Promise<{
|
|
193
|
+
signedTransactionBase64: string
|
|
194
|
+
signatures: string[]
|
|
195
|
+
publicKey: string
|
|
196
|
+
versioned: boolean
|
|
197
|
+
}> {
|
|
198
|
+
const unsignedTx = deserializeTransactionBase64(transactionBase64)
|
|
199
|
+
const { transaction, publicKey } = signTransactionWithWallet(encryptedPrivateKey, unsignedTx)
|
|
200
|
+
return {
|
|
201
|
+
signedTransactionBase64: serializeTransactionBase64(transaction),
|
|
202
|
+
signatures: collectTransactionSignatures(transaction),
|
|
203
|
+
publicKey,
|
|
204
|
+
versioned: transaction instanceof VersionedTransaction,
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export async function simulateSolanaTransaction(
|
|
209
|
+
encryptedPrivateKey: string,
|
|
210
|
+
transactionBase64: string,
|
|
211
|
+
options?: SolanaExecutionOptions,
|
|
212
|
+
): Promise<{
|
|
213
|
+
signatures: string[]
|
|
214
|
+
publicKey: string
|
|
215
|
+
logs: string[]
|
|
216
|
+
unitsConsumed?: number
|
|
217
|
+
err?: unknown
|
|
218
|
+
versioned: boolean
|
|
219
|
+
}> {
|
|
220
|
+
const unsignedTx = deserializeTransactionBase64(transactionBase64)
|
|
221
|
+
const { transaction, publicKey } = signTransactionWithWallet(encryptedPrivateKey, unsignedTx)
|
|
222
|
+
const connection = getConnectionForCluster(options?.cluster, options?.rpcUrl)
|
|
223
|
+
const simulation = transaction instanceof VersionedTransaction
|
|
224
|
+
? await connection.simulateTransaction(transaction)
|
|
225
|
+
: await connection.simulateTransaction(transaction)
|
|
226
|
+
return {
|
|
227
|
+
signatures: collectTransactionSignatures(transaction),
|
|
228
|
+
publicKey,
|
|
229
|
+
logs: simulation.value.logs || [],
|
|
230
|
+
unitsConsumed: simulation.value.unitsConsumed ?? undefined,
|
|
231
|
+
err: simulation.value.err ?? undefined,
|
|
232
|
+
versioned: transaction instanceof VersionedTransaction,
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export async function sendSolanaTransaction(
|
|
237
|
+
encryptedPrivateKey: string,
|
|
238
|
+
input: {
|
|
239
|
+
transactionBase64?: string | null
|
|
240
|
+
signedTransactionBase64?: string | null
|
|
241
|
+
waitForConfirmation?: boolean
|
|
242
|
+
},
|
|
243
|
+
options?: SolanaExecutionOptions,
|
|
244
|
+
): Promise<{
|
|
245
|
+
signature: string
|
|
246
|
+
publicKey: string
|
|
247
|
+
explorerUrl: string
|
|
248
|
+
versioned: boolean
|
|
249
|
+
}> {
|
|
250
|
+
const connection = getConnectionForCluster(options?.cluster, options?.rpcUrl)
|
|
251
|
+
const keypair = getKeypairFromEncrypted(encryptedPrivateKey)
|
|
252
|
+
const waitForConfirmation = input.waitForConfirmation !== false
|
|
253
|
+
|
|
254
|
+
let transaction: Transaction | VersionedTransaction
|
|
255
|
+
if (typeof input.signedTransactionBase64 === 'string' && input.signedTransactionBase64.trim()) {
|
|
256
|
+
transaction = deserializeTransactionBase64(input.signedTransactionBase64.trim())
|
|
257
|
+
} else if (typeof input.transactionBase64 === 'string' && input.transactionBase64.trim()) {
|
|
258
|
+
transaction = signTransactionWithWallet(encryptedPrivateKey, deserializeTransactionBase64(input.transactionBase64.trim())).transaction
|
|
259
|
+
} else {
|
|
260
|
+
throw new Error('transactionBase64 or signedTransactionBase64 is required')
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const raw = transaction.serialize()
|
|
264
|
+
const signature = await connection.sendRawTransaction(raw)
|
|
265
|
+
if (waitForConfirmation) {
|
|
266
|
+
await connection.confirmTransaction(signature, 'confirmed')
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
signature,
|
|
271
|
+
publicKey: keypair.publicKey.toBase58(),
|
|
272
|
+
explorerUrl: getSolanaExplorerUrl(options?.cluster, 'transaction', signature),
|
|
273
|
+
versioned: transaction instanceof VersionedTransaction,
|
|
274
|
+
}
|
|
275
|
+
}
|
|
83
276
|
|
|
84
277
|
export async function getRecentTransactions(
|
|
85
278
|
publicKey: string,
|
|
@@ -96,10 +289,6 @@ export async function getRecentTransactions(
|
|
|
96
289
|
}))
|
|
97
290
|
}
|
|
98
291
|
|
|
99
|
-
// ---------------------------------------------------------------------------
|
|
100
|
-
// Validate address
|
|
101
|
-
// ---------------------------------------------------------------------------
|
|
102
|
-
|
|
103
292
|
export function isValidSolanaAddress(address: string): boolean {
|
|
104
293
|
try {
|
|
105
294
|
new PublicKey(address)
|
|
@@ -109,10 +298,6 @@ export function isValidSolanaAddress(address: string): boolean {
|
|
|
109
298
|
}
|
|
110
299
|
}
|
|
111
300
|
|
|
112
|
-
// ---------------------------------------------------------------------------
|
|
113
|
-
// Helpers
|
|
114
|
-
// ---------------------------------------------------------------------------
|
|
115
|
-
|
|
116
301
|
export function lamportsToSol(lamports: number): number {
|
|
117
302
|
return lamports / LAMPORTS_PER_SOL
|
|
118
303
|
}
|
|
@@ -2,9 +2,10 @@ import fs from 'fs'
|
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import crypto from 'crypto'
|
|
4
4
|
import os from 'os'
|
|
5
|
+
import type { ChildProcess } from 'node:child_process'
|
|
5
6
|
import Database from 'better-sqlite3'
|
|
6
7
|
|
|
7
|
-
import { DATA_DIR, WORKSPACE_DIR } from './data-dir'
|
|
8
|
+
import { DATA_DIR, IS_BUILD_BOOTSTRAP, WORKSPACE_DIR } from './data-dir'
|
|
8
9
|
import { normalizeHeartbeatSettingFields } from '@/lib/heartbeat-defaults'
|
|
9
10
|
import { normalizeRuntimeSettingFields } from '@/lib/runtime-loop'
|
|
10
11
|
import type { ExternalAgentRuntime, GatewayProfile, Message } from '@/types'
|
|
@@ -110,7 +111,6 @@ for (const dir of [DATA_DIR, UPLOAD_DIR, WORKSPACE_DIR]) {
|
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
// --- SQLite Database ---
|
|
113
|
-
const IS_BUILD_BOOTSTRAP = process.env.SWARMCLAW_BUILD_MODE === '1'
|
|
114
114
|
const DB_PATH = IS_BUILD_BOOTSTRAP ? ':memory:' : path.join(DATA_DIR, 'swarmclaw.db')
|
|
115
115
|
const db = new Database(DB_PATH)
|
|
116
116
|
if (!IS_BUILD_BOOTSTRAP) {
|
|
@@ -120,6 +120,12 @@ if (!IS_BUILD_BOOTSTRAP) {
|
|
|
120
120
|
db.pragma('foreign_keys = ON')
|
|
121
121
|
|
|
122
122
|
const collectionCacheKey = '__swarmclaw_storage_collection_cache__' as const
|
|
123
|
+
type StoredObject = Record<string, unknown>
|
|
124
|
+
type ActiveProcess = ChildProcess | {
|
|
125
|
+
runId?: string | null
|
|
126
|
+
source?: string
|
|
127
|
+
kill: (signal?: NodeJS.Signals | number) => boolean | void
|
|
128
|
+
}
|
|
123
129
|
type StorageGlobals = typeof globalThis & {
|
|
124
130
|
[collectionCacheKey]?: Map<string, LRUMap<string, string>>
|
|
125
131
|
}
|
|
@@ -194,11 +200,11 @@ function getCollectionRawCache(table: string): LRUMap<string, string> {
|
|
|
194
200
|
return loaded
|
|
195
201
|
}
|
|
196
202
|
|
|
197
|
-
function normalizeStoredRecord(table: string, value:
|
|
203
|
+
function normalizeStoredRecord(table: string, value: unknown): unknown {
|
|
198
204
|
if (table !== 'sessions') return value
|
|
199
205
|
if (!value || typeof value !== 'object' || Array.isArray(value)) return value
|
|
200
206
|
|
|
201
|
-
const session = value as
|
|
207
|
+
const session = value as StoredObject
|
|
202
208
|
if (session.sessionType !== 'human') session.sessionType = 'human'
|
|
203
209
|
const isLegacyShortcut = (
|
|
204
210
|
(typeof session.id === 'string' && session.id.startsWith('agent-thread-'))
|
|
@@ -282,7 +288,7 @@ function deleteCollectionItem(table: string, id: string) {
|
|
|
282
288
|
* loading/saving the entire collection. Prevents race conditions when
|
|
283
289
|
* concurrent processes are modifying different items.
|
|
284
290
|
*/
|
|
285
|
-
function upsertCollectionItem(table: string, id: string, value:
|
|
291
|
+
function upsertCollectionItem(table: string, id: string, value: unknown) {
|
|
286
292
|
const serialized = JSON.stringify(normalizeStoredRecord(table, value))
|
|
287
293
|
db.prepare(`INSERT OR REPLACE INTO ${table} (id, data) VALUES (?, ?)`).run(id, serialized)
|
|
288
294
|
// Update the in-memory cache
|
|
@@ -292,7 +298,7 @@ function upsertCollectionItem(table: string, id: string, value: any) {
|
|
|
292
298
|
}
|
|
293
299
|
}
|
|
294
300
|
|
|
295
|
-
function loadCollectionItem(table: string, id: string):
|
|
301
|
+
function loadCollectionItem(table: string, id: string): unknown | null {
|
|
296
302
|
const row = db.prepare(`SELECT data FROM ${table} WHERE id = ?`).get(id) as { data: string } | undefined
|
|
297
303
|
if (!row) return null
|
|
298
304
|
try {
|
|
@@ -302,7 +308,7 @@ function loadCollectionItem(table: string, id: string): any | null {
|
|
|
302
308
|
}
|
|
303
309
|
}
|
|
304
310
|
|
|
305
|
-
function upsertCollectionItems(table: string, entries: Array<[string,
|
|
311
|
+
function upsertCollectionItems(table: string, entries: Array<[string, unknown]>): void {
|
|
306
312
|
if (!entries.length) return
|
|
307
313
|
const prepared = entries
|
|
308
314
|
.map(([id, value]) => [id, JSON.stringify(normalizeStoredRecord(table, value))] as const)
|
|
@@ -325,24 +331,24 @@ function upsertCollectionItems(table: string, entries: Array<[string, any]>): vo
|
|
|
325
331
|
}
|
|
326
332
|
}
|
|
327
333
|
|
|
328
|
-
export function loadStoredItem(table: StorageCollection, id: string):
|
|
334
|
+
export function loadStoredItem(table: StorageCollection, id: string): unknown | null {
|
|
329
335
|
return loadCollectionItem(table, id)
|
|
330
336
|
}
|
|
331
337
|
|
|
332
|
-
export function upsertStoredItem(table: StorageCollection, id: string, value:
|
|
338
|
+
export function upsertStoredItem(table: StorageCollection, id: string, value: unknown): void {
|
|
333
339
|
upsertCollectionItem(table, id, value)
|
|
334
340
|
}
|
|
335
341
|
|
|
336
|
-
export function upsertStoredItems(table: StorageCollection, entries: Array<[string,
|
|
342
|
+
export function upsertStoredItems(table: StorageCollection, entries: Array<[string, unknown]>): void {
|
|
337
343
|
upsertCollectionItems(table, entries)
|
|
338
344
|
}
|
|
339
345
|
|
|
340
|
-
function loadSingleton(table: string, fallback:
|
|
346
|
+
function loadSingleton<T>(table: string, fallback: T): T {
|
|
341
347
|
const row = db.prepare(`SELECT data FROM ${table} WHERE id = 1`).get() as { data: string } | undefined
|
|
342
|
-
return row ? JSON.parse(row.data) : fallback
|
|
348
|
+
return row ? JSON.parse(row.data) as T : fallback
|
|
343
349
|
}
|
|
344
350
|
|
|
345
|
-
function saveSingleton(table: string, data:
|
|
351
|
+
function saveSingleton(table: string, data: unknown) {
|
|
346
352
|
db.prepare(`INSERT OR REPLACE INTO ${table} (id, data) VALUES (1, ?)`).run(JSON.stringify(data))
|
|
347
353
|
}
|
|
348
354
|
|
|
@@ -594,37 +600,43 @@ export function markSetupComplete(): void {
|
|
|
594
600
|
export function loadSessions(): Record<string, any> {
|
|
595
601
|
const sessions = loadCollection('sessions')
|
|
596
602
|
const agents = loadCollection('agents')
|
|
597
|
-
|
|
603
|
+
const changedEntries: Array<[string, any]> = []
|
|
598
604
|
|
|
599
605
|
for (const [id, session] of Object.entries(sessions)) {
|
|
600
606
|
if (!session || typeof session !== 'object') continue
|
|
607
|
+
let touched = false
|
|
601
608
|
|
|
602
609
|
if (typeof session.id !== 'string' || !session.id.trim()) {
|
|
603
610
|
session.id = id
|
|
604
|
-
|
|
611
|
+
touched = true
|
|
605
612
|
}
|
|
606
613
|
|
|
607
|
-
|
|
608
614
|
const agentId = typeof session.agentId === 'string' ? session.agentId.trim() : ''
|
|
609
615
|
if (agentId && !Object.prototype.hasOwnProperty.call(agents, agentId)) {
|
|
610
616
|
session.agentId = null
|
|
611
|
-
|
|
617
|
+
touched = true
|
|
612
618
|
}
|
|
613
619
|
|
|
614
620
|
// Migrate tools → plugins
|
|
615
621
|
if (Array.isArray(session.tools) && !Array.isArray(session.plugins)) {
|
|
616
622
|
session.plugins = session.tools
|
|
617
623
|
delete session.tools
|
|
618
|
-
|
|
624
|
+
touched = true
|
|
619
625
|
}
|
|
626
|
+
|
|
627
|
+
if (touched) changedEntries.push([id, session])
|
|
620
628
|
}
|
|
621
629
|
|
|
622
|
-
|
|
630
|
+
// Upsert only changed entries — never full-replace, which deletes concurrent sessions
|
|
631
|
+
if (changedEntries.length > 0) upsertCollectionItems('sessions', changedEntries)
|
|
623
632
|
return sessions
|
|
624
633
|
}
|
|
625
634
|
|
|
626
635
|
export function saveSessions(s: Record<string, any>) {
|
|
627
|
-
|
|
636
|
+
// Upsert-only — never delete sessions that aren't in the map.
|
|
637
|
+
// Explicit deletion goes through deleteSession(id).
|
|
638
|
+
const entries = Object.entries(s)
|
|
639
|
+
if (entries.length > 0) upsertCollectionItems('sessions', entries)
|
|
628
640
|
}
|
|
629
641
|
|
|
630
642
|
export function disableAllSessionHeartbeats(): number {
|
|
@@ -636,9 +648,9 @@ export function disableAllSessionHeartbeats(): number {
|
|
|
636
648
|
|
|
637
649
|
const tx = db.transaction(() => {
|
|
638
650
|
for (const row of rows) {
|
|
639
|
-
let parsed:
|
|
651
|
+
let parsed: StoredObject | null = null
|
|
640
652
|
try {
|
|
641
|
-
parsed = JSON.parse(row.data)
|
|
653
|
+
parsed = JSON.parse(row.data) as StoredObject
|
|
642
654
|
} catch {
|
|
643
655
|
continue
|
|
644
656
|
}
|
|
@@ -754,7 +766,7 @@ export function loadTasks(): Record<string, any> {
|
|
|
754
766
|
export function saveTasks(t: Record<string, any>) {
|
|
755
767
|
saveCollection('tasks', t)
|
|
756
768
|
}
|
|
757
|
-
export function upsertTask(id: string, task:
|
|
769
|
+
export function upsertTask(id: string, task: unknown) {
|
|
758
770
|
upsertCollectionItem('tasks', id, task)
|
|
759
771
|
}
|
|
760
772
|
export function deleteTask(id: string) { deleteCollectionItem('tasks', id) }
|
|
@@ -809,6 +821,9 @@ function isProvidedSecretValue(value: unknown): value is string {
|
|
|
809
821
|
|
|
810
822
|
function buildPersistedSettings(input: Record<string, any>, existing?: PersistedSettingsRecord): PersistedSettingsRecord {
|
|
811
823
|
const next = cloneRecord(input) as PersistedSettingsRecord
|
|
824
|
+
if (typeof next.approvalsEnabled !== 'boolean' && typeof existing?.approvalsEnabled !== 'boolean') {
|
|
825
|
+
next.approvalsEnabled = false
|
|
826
|
+
}
|
|
812
827
|
Object.assign(next, normalizeRuntimeSettingFields(next))
|
|
813
828
|
Object.assign(next, normalizeHeartbeatSettingFields(next))
|
|
814
829
|
const encrypted = {
|
|
@@ -900,15 +915,13 @@ export async function getSecret(key: string): Promise<{
|
|
|
900
915
|
if (!needle) return null
|
|
901
916
|
|
|
902
917
|
const secrets = loadSecrets()
|
|
903
|
-
|
|
904
|
-
const matches = Object.values(secrets).find((secret: any) => {
|
|
918
|
+
const matches = Object.values(secrets).find((secret): secret is StoredObject => {
|
|
905
919
|
if (!secret || typeof secret !== 'object') return false
|
|
906
920
|
const id = typeof secret.id === 'string' ? secret.id.toLowerCase() : ''
|
|
907
921
|
const name = typeof secret.name === 'string' ? secret.name.toLowerCase() : ''
|
|
908
922
|
const service = typeof secret.service === 'string' ? secret.service.toLowerCase() : ''
|
|
909
923
|
return id === needle || name === needle || service === needle
|
|
910
|
-
|
|
911
|
-
}) as any | undefined
|
|
924
|
+
})
|
|
912
925
|
|
|
913
926
|
if (!matches) return null
|
|
914
927
|
|
|
@@ -919,15 +932,23 @@ export async function getSecret(key: string): Promise<{
|
|
|
919
932
|
: (typeof matches.value === 'string' ? matches.value : '')
|
|
920
933
|
if (!decryptedValue) return null
|
|
921
934
|
|
|
935
|
+
const id = typeof matches.id === 'string' ? matches.id : ''
|
|
936
|
+
const name = typeof matches.name === 'string' ? matches.name : ''
|
|
937
|
+
const service = typeof matches.service === 'string' ? matches.service : ''
|
|
938
|
+
const scope = typeof matches.scope === 'string' ? matches.scope : ''
|
|
939
|
+
const createdAt = typeof matches.createdAt === 'number' ? matches.createdAt : 0
|
|
940
|
+
const updatedAt = typeof matches.updatedAt === 'number' ? matches.updatedAt : 0
|
|
941
|
+
if (!id || !name || !service || !scope) return null
|
|
942
|
+
|
|
922
943
|
return {
|
|
923
|
-
id
|
|
924
|
-
name
|
|
925
|
-
service
|
|
944
|
+
id,
|
|
945
|
+
name,
|
|
946
|
+
service,
|
|
926
947
|
value: decryptedValue,
|
|
927
|
-
scope
|
|
948
|
+
scope,
|
|
928
949
|
agentIds: Array.isArray(matches.agentIds) ? matches.agentIds : [],
|
|
929
|
-
createdAt
|
|
930
|
-
updatedAt
|
|
950
|
+
createdAt,
|
|
951
|
+
updatedAt,
|
|
931
952
|
}
|
|
932
953
|
} catch {
|
|
933
954
|
return null
|
|
@@ -1016,7 +1037,7 @@ export function saveUsage(u: Record<string, any[]>) {
|
|
|
1016
1037
|
transaction()
|
|
1017
1038
|
}
|
|
1018
1039
|
|
|
1019
|
-
export function appendUsage(sessionId: string, record:
|
|
1040
|
+
export function appendUsage(sessionId: string, record: unknown) {
|
|
1020
1041
|
const ins = db.prepare('INSERT INTO usage (session_id, data) VALUES (?, ?)')
|
|
1021
1042
|
ins.run(sessionId, JSON.stringify(record))
|
|
1022
1043
|
}
|
|
@@ -1058,8 +1079,8 @@ export function saveWebhooks(w: Record<string, any>) {
|
|
|
1058
1079
|
}
|
|
1059
1080
|
|
|
1060
1081
|
// --- Active processes ---
|
|
1061
|
-
export const active = new Map<string,
|
|
1062
|
-
export const devServers = new Map<string, { proc:
|
|
1082
|
+
export const active = new Map<string, ActiveProcess>()
|
|
1083
|
+
export const devServers = new Map<string, { proc: ChildProcess; url: string }>()
|
|
1063
1084
|
|
|
1064
1085
|
// --- Utilities ---
|
|
1065
1086
|
export function localIP(): string {
|
|
@@ -1097,6 +1118,10 @@ export function loadWebhookLogs(): Record<string, unknown> {
|
|
|
1097
1118
|
return loadCollection('webhook_logs')
|
|
1098
1119
|
}
|
|
1099
1120
|
|
|
1121
|
+
export function saveWebhookLogs(entries: Record<string, unknown>) {
|
|
1122
|
+
saveCollection('webhook_logs', entries)
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1100
1125
|
export function appendWebhookLog(id: string, entry: unknown) {
|
|
1101
1126
|
upsertCollectionItem('webhook_logs', id, entry)
|
|
1102
1127
|
}
|
|
@@ -1125,6 +1150,10 @@ export function loadWebhookRetryQueue(): Record<string, unknown> {
|
|
|
1125
1150
|
return loadCollection('webhook_retry_queue')
|
|
1126
1151
|
}
|
|
1127
1152
|
|
|
1153
|
+
export function saveWebhookRetryQueue(entries: Record<string, unknown>) {
|
|
1154
|
+
saveCollection('webhook_retry_queue', entries)
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1128
1157
|
export function upsertWebhookRetry(id: string, entry: unknown) {
|
|
1129
1158
|
upsertCollectionItem('webhook_retry_queue', id, entry)
|
|
1130
1159
|
}
|