@swarmclawai/swarmclaw 0.7.5 → 0.7.7
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 +41 -10
- package/package.json +2 -2
- package/src/app/api/agents/[id]/route.ts +16 -0
- package/src/app/api/agents/route.ts +2 -0
- package/src/app/api/chats/[id]/route.ts +21 -1
- package/src/app/api/chats/route.ts +12 -1
- package/src/app/api/external-agents/[id]/heartbeat/route.ts +3 -0
- package/src/app/api/external-agents/[id]/route.ts +38 -6
- package/src/app/api/external-agents/route.ts +17 -1
- package/src/app/api/gateways/[id]/health/route.ts +8 -0
- package/src/app/api/gateways/[id]/route.ts +53 -1
- package/src/app/api/gateways/route.ts +53 -0
- package/src/app/api/openclaw/deploy/route.ts +240 -0
- package/src/cli/index.js +53 -0
- package/src/cli/index.test.js +102 -0
- package/src/cli/spec.js +79 -0
- package/src/components/agents/agent-sheet.tsx +97 -19
- package/src/components/auth/setup-wizard.tsx +111 -54
- package/src/components/gateways/gateway-sheet.tsx +202 -10
- package/src/components/openclaw/openclaw-deploy-panel.tsx +1208 -0
- package/src/components/providers/provider-list.tsx +321 -22
- package/src/lib/server/agent-runtime-config.ts +142 -7
- package/src/lib/server/agent-thread-session.ts +9 -1
- package/src/lib/server/chat-execution.ts +8 -2
- package/src/lib/server/heartbeat-service.ts +5 -1
- package/src/lib/server/openclaw-deploy.test.ts +75 -0
- package/src/lib/server/openclaw-deploy.ts +1384 -0
- package/src/lib/server/orchestrator.ts +9 -0
- package/src/lib/server/queue.ts +45 -2
- package/src/lib/setup-defaults.ts +2 -2
- package/src/lib/validation/schemas.ts +9 -0
- package/src/types/index.ts +65 -0
|
@@ -19,9 +19,16 @@ export function createOrchestratorSession(
|
|
|
19
19
|
task: string,
|
|
20
20
|
parentSessionId?: string,
|
|
21
21
|
cwd?: string,
|
|
22
|
+
routePreferences?: {
|
|
23
|
+
preferredGatewayTags?: string[]
|
|
24
|
+
preferredGatewayUseCase?: string | null
|
|
25
|
+
} | null,
|
|
22
26
|
): string {
|
|
23
27
|
const sessions = loadSessions()
|
|
24
28
|
const sessionId = genId()
|
|
29
|
+
const preferredGatewayTags = Array.isArray(routePreferences?.preferredGatewayTags)
|
|
30
|
+
? routePreferences.preferredGatewayTags.filter((tag) => typeof tag === 'string' && tag.trim())
|
|
31
|
+
: []
|
|
25
32
|
sessions[sessionId] = {
|
|
26
33
|
id: sessionId,
|
|
27
34
|
name: `[Orch] ${orchestrator.name}: ${task.slice(0, 40)}`,
|
|
@@ -31,6 +38,8 @@ export function createOrchestratorSession(
|
|
|
31
38
|
model: orchestrator.model,
|
|
32
39
|
credentialId: orchestrator.credentialId || null,
|
|
33
40
|
apiEndpoint: orchestrator.apiEndpoint || null,
|
|
41
|
+
routePreferredGatewayTags: preferredGatewayTags,
|
|
42
|
+
routePreferredGatewayUseCase: routePreferences?.preferredGatewayUseCase || null,
|
|
34
43
|
claudeSessionId: null,
|
|
35
44
|
codexThreadId: null,
|
|
36
45
|
opencodeSessionId: null,
|
package/src/lib/server/queue.ts
CHANGED
|
@@ -80,6 +80,36 @@ function normalizeInt(value: unknown, fallback: number, min: number, max: number
|
|
|
80
80
|
return Math.max(min, Math.min(max, Math.trunc(parsed)))
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
const OPENCLAW_USE_CASE_TAGS = new Set([
|
|
84
|
+
'local-dev',
|
|
85
|
+
'single-vps',
|
|
86
|
+
'private-tailnet',
|
|
87
|
+
'browser-heavy',
|
|
88
|
+
'team-control',
|
|
89
|
+
])
|
|
90
|
+
|
|
91
|
+
function deriveTaskRoutePreferences(task: BoardTask): {
|
|
92
|
+
preferredGatewayTags?: string[]
|
|
93
|
+
preferredGatewayUseCase?: string | null
|
|
94
|
+
} {
|
|
95
|
+
const tags = Array.isArray(task.tags)
|
|
96
|
+
? [...new Set(task.tags.map((tag) => (typeof tag === 'string' ? tag.trim().toLowerCase() : '')).filter(Boolean))]
|
|
97
|
+
: []
|
|
98
|
+
const customUseCase = typeof task.customFields?.openclawUseCase === 'string'
|
|
99
|
+
? task.customFields.openclawUseCase
|
|
100
|
+
: typeof task.customFields?.gatewayUseCase === 'string'
|
|
101
|
+
? task.customFields.gatewayUseCase
|
|
102
|
+
: null
|
|
103
|
+
const preferredGatewayUseCase = customUseCase && OPENCLAW_USE_CASE_TAGS.has(customUseCase)
|
|
104
|
+
? customUseCase
|
|
105
|
+
: (tags.find((tag) => OPENCLAW_USE_CASE_TAGS.has(tag)) || null)
|
|
106
|
+
const preferredGatewayTags = tags.filter((tag) => tag !== preferredGatewayUseCase)
|
|
107
|
+
return {
|
|
108
|
+
preferredGatewayTags,
|
|
109
|
+
preferredGatewayUseCase,
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
83
113
|
function resolveTaskPolicy(task: BoardTask): { maxAttempts: number; backoffSec: number } {
|
|
84
114
|
const settings = loadSettings()
|
|
85
115
|
const defaultMaxAttempts = normalizeInt(settings.defaultTaskMaxAttempts, 3, 1, 20)
|
|
@@ -1137,6 +1167,7 @@ export async function processNext() {
|
|
|
1137
1167
|
|
|
1138
1168
|
// Resolve the agent's persistent thread session to use as parentSessionId
|
|
1139
1169
|
const agentThreadSessionId = agent.threadSessionId || null
|
|
1170
|
+
const taskRoutePreferences = deriveTaskRoutePreferences(task)
|
|
1140
1171
|
|
|
1141
1172
|
if (isScheduleTask && sourceScheduleId) {
|
|
1142
1173
|
const schedules = loadSchedules()
|
|
@@ -1151,7 +1182,13 @@ export async function processNext() {
|
|
|
1151
1182
|
}
|
|
1152
1183
|
}
|
|
1153
1184
|
if (!sessionId) {
|
|
1154
|
-
sessionId = createOrchestratorSession(
|
|
1185
|
+
sessionId = createOrchestratorSession(
|
|
1186
|
+
agent,
|
|
1187
|
+
task.title,
|
|
1188
|
+
agentThreadSessionId || undefined,
|
|
1189
|
+
taskCwd,
|
|
1190
|
+
taskRoutePreferences,
|
|
1191
|
+
)
|
|
1155
1192
|
}
|
|
1156
1193
|
if (linkedSchedule && linkedSchedule.lastSessionId !== sessionId) {
|
|
1157
1194
|
linkedSchedule.lastSessionId = sessionId
|
|
@@ -1160,7 +1197,13 @@ export async function processNext() {
|
|
|
1160
1197
|
saveSchedules(schedules)
|
|
1161
1198
|
}
|
|
1162
1199
|
} else {
|
|
1163
|
-
sessionId = createOrchestratorSession(
|
|
1200
|
+
sessionId = createOrchestratorSession(
|
|
1201
|
+
agent,
|
|
1202
|
+
task.title,
|
|
1203
|
+
agentThreadSessionId || undefined,
|
|
1204
|
+
taskCwd,
|
|
1205
|
+
taskRoutePreferences,
|
|
1206
|
+
)
|
|
1164
1207
|
}
|
|
1165
1208
|
|
|
1166
1209
|
// Notify the agent's thread that a task has started
|
|
@@ -48,7 +48,7 @@ export const SETUP_PROVIDERS: SetupProviderOption[] = [
|
|
|
48
48
|
{
|
|
49
49
|
id: 'openclaw',
|
|
50
50
|
name: 'OpenClaw',
|
|
51
|
-
description: '
|
|
51
|
+
description: 'Deploy or connect official-only local and remote OpenClaw gateways, then map starter agents across your swarm by role, tag, or use case.',
|
|
52
52
|
requiresKey: false,
|
|
53
53
|
supportsEndpoint: true,
|
|
54
54
|
allowMultiple: true,
|
|
@@ -481,7 +481,7 @@ export const STARTER_KITS: StarterKit[] = [
|
|
|
481
481
|
id: 'openclaw_fleet',
|
|
482
482
|
name: 'OpenClaw Fleet',
|
|
483
483
|
description: 'An OpenClaw-first starter setup for local or remote gateways.',
|
|
484
|
-
detail: 'Designed for users who want multiple OpenClaw-backed agents right away,
|
|
484
|
+
detail: 'Designed for users who want multiple OpenClaw-backed agents right away, with official-only local deploy, single-VPS, and private-tailnet defaults built into setup.',
|
|
485
485
|
recommendedFor: ['manual'],
|
|
486
486
|
badge: 'OpenClaw',
|
|
487
487
|
agents: [
|
|
@@ -10,6 +10,8 @@ const AgentRoutingTargetSchema = z.object({
|
|
|
10
10
|
fallbackCredentialIds: z.array(z.string()).optional().default([]),
|
|
11
11
|
apiEndpoint: z.string().nullable().optional().default(null),
|
|
12
12
|
gatewayProfileId: z.string().nullable().optional().default(null),
|
|
13
|
+
preferredGatewayTags: z.array(z.string()).optional().default([]),
|
|
14
|
+
preferredGatewayUseCase: z.string().nullable().optional().default(null),
|
|
13
15
|
priority: z.number().int().optional(),
|
|
14
16
|
})
|
|
15
17
|
|
|
@@ -23,6 +25,8 @@ export const AgentCreateSchema = z.object({
|
|
|
23
25
|
fallbackCredentialIds: z.array(z.string()).optional().default([]),
|
|
24
26
|
apiEndpoint: z.string().nullable().optional().default(null),
|
|
25
27
|
gatewayProfileId: z.string().nullable().optional().default(null),
|
|
28
|
+
preferredGatewayTags: z.array(z.string()).optional().default([]),
|
|
29
|
+
preferredGatewayUseCase: z.string().nullable().optional().default(null),
|
|
26
30
|
routingStrategy: z.enum(['single', 'balanced', 'economy', 'premium', 'reasoning']).nullable().optional().default(null),
|
|
27
31
|
routingTargets: z.array(AgentRoutingTargetSchema).optional().default([]),
|
|
28
32
|
isOrchestrator: z.boolean().optional().default(false),
|
|
@@ -89,6 +93,11 @@ export const ExternalAgentRegisterSchema = z.object({
|
|
|
89
93
|
gatewayProfileId: z.string().nullable().optional().default(null),
|
|
90
94
|
capabilities: z.array(z.string()).optional().default([]),
|
|
91
95
|
labels: z.array(z.string()).optional().default([]),
|
|
96
|
+
lifecycleState: z.enum(['active', 'draining', 'cordoned']).optional().default('active'),
|
|
97
|
+
gatewayTags: z.array(z.string()).optional().default([]),
|
|
98
|
+
gatewayUseCase: z.string().nullable().optional().default(null),
|
|
99
|
+
version: z.string().nullable().optional().default(null),
|
|
100
|
+
lastHealthNote: z.string().nullable().optional().default(null),
|
|
92
101
|
metadata: z.record(z.string(), z.unknown()).nullable().optional().default(null),
|
|
93
102
|
tokenStats: z.object({
|
|
94
103
|
inputTokens: z.number().nonnegative().optional(),
|
package/src/types/index.ts
CHANGED
|
@@ -93,6 +93,8 @@ export interface Session {
|
|
|
93
93
|
fallbackCredentialIds?: string[]
|
|
94
94
|
apiEndpoint?: string | null
|
|
95
95
|
gatewayProfileId?: string | null
|
|
96
|
+
routePreferredGatewayTags?: string[]
|
|
97
|
+
routePreferredGatewayUseCase?: string | null
|
|
96
98
|
claudeSessionId: string | null
|
|
97
99
|
codexThreadId?: string | null
|
|
98
100
|
opencodeSessionId?: string | null
|
|
@@ -498,6 +500,8 @@ export interface Agent {
|
|
|
498
500
|
fallbackCredentialIds?: string[]
|
|
499
501
|
apiEndpoint?: string | null
|
|
500
502
|
gatewayProfileId?: string | null
|
|
503
|
+
preferredGatewayTags?: string[]
|
|
504
|
+
preferredGatewayUseCase?: string | null
|
|
501
505
|
routingStrategy?: AgentRoutingStrategy | null
|
|
502
506
|
routingTargets?: AgentRoutingTarget[]
|
|
503
507
|
isOrchestrator?: boolean
|
|
@@ -1168,6 +1172,58 @@ export interface ProviderConfig {
|
|
|
1168
1172
|
|
|
1169
1173
|
export type GatewayProvider = 'openclaw'
|
|
1170
1174
|
export type GatewayHealthState = 'unknown' | 'healthy' | 'degraded' | 'offline' | 'pending'
|
|
1175
|
+
export type OpenClawDeploymentMethod = 'local' | 'bundle' | 'ssh' | 'imported'
|
|
1176
|
+
export type OpenClawDeploymentProvider =
|
|
1177
|
+
| 'local'
|
|
1178
|
+
| 'hetzner'
|
|
1179
|
+
| 'digitalocean'
|
|
1180
|
+
| 'vultr'
|
|
1181
|
+
| 'linode'
|
|
1182
|
+
| 'lightsail'
|
|
1183
|
+
| 'gcp'
|
|
1184
|
+
| 'azure'
|
|
1185
|
+
| 'oci'
|
|
1186
|
+
| 'generic'
|
|
1187
|
+
| 'render'
|
|
1188
|
+
| 'fly'
|
|
1189
|
+
| 'railway'
|
|
1190
|
+
export type OpenClawRemoteDeployTarget = 'docker' | 'render' | 'fly' | 'railway'
|
|
1191
|
+
export type OpenClawUseCaseTemplate = 'local-dev' | 'single-vps' | 'private-tailnet' | 'browser-heavy' | 'team-control'
|
|
1192
|
+
export type OpenClawExposurePreset = 'private-lan' | 'tailscale' | 'caddy' | 'nginx' | 'ssh-tunnel'
|
|
1193
|
+
|
|
1194
|
+
export interface OpenClawGatewayStats {
|
|
1195
|
+
nodeCount?: number
|
|
1196
|
+
connectedNodeCount?: number
|
|
1197
|
+
pendingNodePairings?: number
|
|
1198
|
+
pairedDeviceCount?: number
|
|
1199
|
+
pendingDevicePairings?: number
|
|
1200
|
+
externalRuntimeCount?: number
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
export interface OpenClawDeploymentConfig {
|
|
1204
|
+
method?: OpenClawDeploymentMethod | null
|
|
1205
|
+
provider?: OpenClawDeploymentProvider | null
|
|
1206
|
+
remoteTarget?: OpenClawRemoteDeployTarget | null
|
|
1207
|
+
useCase?: OpenClawUseCaseTemplate | null
|
|
1208
|
+
exposure?: OpenClawExposurePreset | null
|
|
1209
|
+
managedBy?: 'swarmclaw' | 'manual' | null
|
|
1210
|
+
targetHost?: string | null
|
|
1211
|
+
sshHost?: string | null
|
|
1212
|
+
sshUser?: string | null
|
|
1213
|
+
sshPort?: number | null
|
|
1214
|
+
sshKeyPath?: string | null
|
|
1215
|
+
sshTargetDir?: string | null
|
|
1216
|
+
image?: string | null
|
|
1217
|
+
version?: string | null
|
|
1218
|
+
lastDeployAt?: number | null
|
|
1219
|
+
lastDeployAction?: string | null
|
|
1220
|
+
lastDeployProcessId?: string | null
|
|
1221
|
+
lastDeploySummary?: string | null
|
|
1222
|
+
lastVerifiedAt?: number | null
|
|
1223
|
+
lastVerifiedOk?: boolean | null
|
|
1224
|
+
lastVerifiedMessage?: string | null
|
|
1225
|
+
lastBackupPath?: string | null
|
|
1226
|
+
}
|
|
1171
1227
|
|
|
1172
1228
|
export interface GatewayProfile {
|
|
1173
1229
|
id: string
|
|
@@ -1184,6 +1240,8 @@ export interface GatewayProfile {
|
|
|
1184
1240
|
lastModelCount?: number | null
|
|
1185
1241
|
discoveredHost?: string | null
|
|
1186
1242
|
discoveredPort?: number | null
|
|
1243
|
+
deployment?: OpenClawDeploymentConfig | null
|
|
1244
|
+
stats?: OpenClawGatewayStats | null
|
|
1187
1245
|
isDefault?: boolean
|
|
1188
1246
|
createdAt: number
|
|
1189
1247
|
updatedAt: number
|
|
@@ -1256,6 +1314,8 @@ export interface AgentRoutingTarget {
|
|
|
1256
1314
|
fallbackCredentialIds?: string[]
|
|
1257
1315
|
apiEndpoint?: string | null
|
|
1258
1316
|
gatewayProfileId?: string | null
|
|
1317
|
+
preferredGatewayTags?: string[]
|
|
1318
|
+
preferredGatewayUseCase?: string | null
|
|
1259
1319
|
priority?: number
|
|
1260
1320
|
}
|
|
1261
1321
|
|
|
@@ -1307,6 +1367,11 @@ export interface ExternalAgentRuntime {
|
|
|
1307
1367
|
gatewayProfileId?: string | null
|
|
1308
1368
|
capabilities?: string[]
|
|
1309
1369
|
labels?: string[]
|
|
1370
|
+
lifecycleState?: 'active' | 'draining' | 'cordoned'
|
|
1371
|
+
gatewayTags?: string[]
|
|
1372
|
+
gatewayUseCase?: string | null
|
|
1373
|
+
version?: string | null
|
|
1374
|
+
lastHealthNote?: string | null
|
|
1310
1375
|
metadata?: Record<string, unknown> | null
|
|
1311
1376
|
tokenStats?: {
|
|
1312
1377
|
inputTokens?: number
|