@swarmclawai/swarmclaw 0.7.4 → 0.7.6

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.
@@ -2,10 +2,17 @@
2
2
 
3
3
  import { useEffect, useState } from 'react'
4
4
  import { BottomSheet } from '@/components/shared/bottom-sheet'
5
+ import { OpenClawDeployPanel } from '@/components/openclaw/openclaw-deploy-panel'
5
6
  import { useAppStore } from '@/stores/use-app-store'
6
7
  import { api } from '@/lib/api-client'
7
8
  import { toast } from 'sonner'
8
- import type { OpenClawDevicePairRequest, OpenClawNode, OpenClawNodePairRequest, OpenClawPairedDevice } from '@/types'
9
+ import type {
10
+ Credential,
11
+ OpenClawDevicePairRequest,
12
+ OpenClawNode,
13
+ OpenClawNodePairRequest,
14
+ OpenClawPairedDevice,
15
+ } from '@/types'
9
16
 
10
17
  interface DiscoveryResult {
11
18
  host: string
@@ -46,6 +53,7 @@ export function GatewaySheet() {
46
53
  const [name, setName] = useState('')
47
54
  const [endpoint, setEndpoint] = useState('http://localhost:18789')
48
55
  const [credentialId, setCredentialId] = useState<string | null>(null)
56
+ const [tokenDraft, setTokenDraft] = useState('')
49
57
  const [notes, setNotes] = useState('')
50
58
  const [tags, setTags] = useState('')
51
59
  const [isDefault, setIsDefault] = useState(false)
@@ -81,6 +89,7 @@ export function GatewaySheet() {
81
89
  setName(editing.name)
82
90
  setEndpoint(editing.endpoint)
83
91
  setCredentialId(editing.credentialId || null)
92
+ setTokenDraft('')
84
93
  setNotes(editing.notes || '')
85
94
  setTags((editing.tags || []).join(', '))
86
95
  setIsDefault(editing.isDefault === true)
@@ -89,6 +98,7 @@ export function GatewaySheet() {
89
98
  setName('')
90
99
  setEndpoint('http://localhost:18789')
91
100
  setCredentialId(null)
101
+ setTokenDraft('')
92
102
  setNotes('')
93
103
  setTags('')
94
104
  setIsDefault(gatewayProfiles.length === 0)
@@ -157,10 +167,19 @@ export function GatewaySheet() {
157
167
  const handleSave = async () => {
158
168
  setSaving(true)
159
169
  try {
170
+ let nextCredentialId = credentialId
171
+ if (tokenDraft.trim()) {
172
+ const created = await api<Credential>('POST', '/credentials', {
173
+ provider: 'openclaw',
174
+ name: `${name.trim() || 'OpenClaw Gateway'} token`,
175
+ apiKey: tokenDraft.trim(),
176
+ })
177
+ nextCredentialId = created.id
178
+ }
160
179
  const payload = {
161
180
  name: name.trim() || 'OpenClaw Gateway',
162
181
  endpoint: endpoint.trim() || 'http://localhost:18789',
163
- credentialId: credentialId || null,
182
+ credentialId: nextCredentialId || null,
164
183
  notes: notes.trim() || null,
165
184
  tags: tags.split(',').map((item) => item.trim()).filter(Boolean),
166
185
  isDefault,
@@ -172,7 +191,7 @@ export function GatewaySheet() {
172
191
  await api('POST', '/gateways', payload)
173
192
  toast.success('Gateway added')
174
193
  }
175
- await loadGatewayProfiles()
194
+ await Promise.all([loadGatewayProfiles(), loadCredentials()])
176
195
  onClose()
177
196
  } catch (err: unknown) {
178
197
  toast.error(err instanceof Error ? err.message : 'Failed to save gateway')
@@ -188,6 +207,7 @@ export function GatewaySheet() {
188
207
  const params = new URLSearchParams()
189
208
  params.set('endpoint', endpoint.trim() || 'http://localhost:18789')
190
209
  if (credentialId) params.set('credentialId', credentialId)
210
+ if (tokenDraft.trim()) params.set('token', tokenDraft.trim())
191
211
  const result = await api<{ ok: boolean; models: string[]; error?: string; hint?: string }>('GET', `/providers/openclaw/health?${params.toString()}`)
192
212
  if (result.ok) {
193
213
  setCheckMessage(`Connected. ${result.models?.length ? `${result.models.length} model${result.models.length === 1 ? '' : 's'} visible.` : 'Gateway responded normally.'}`)
@@ -278,6 +298,23 @@ export function GatewaySheet() {
278
298
 
279
299
  const inputClass = 'w-full px-4 py-3.5 rounded-[14px] border border-white/[0.08] bg-surface text-text text-[15px] outline-none transition-all duration-200 placeholder:text-text-3/50 focus-glow'
280
300
 
301
+ const applyDeployPatch = (patch: { endpoint?: string; token?: string; name?: string; notes?: string }) => {
302
+ if (patch.endpoint) {
303
+ setEndpoint(patch.endpoint)
304
+ setCheckMessage('')
305
+ }
306
+ if (patch.token) {
307
+ setTokenDraft(patch.token)
308
+ setCredentialId(null)
309
+ }
310
+ if (patch.name && !name.trim()) {
311
+ setName(patch.name)
312
+ }
313
+ if (patch.notes && !notes.trim()) {
314
+ setNotes(patch.notes)
315
+ }
316
+ }
317
+
281
318
  return (
282
319
  <BottomSheet open={open} onClose={onClose} wide>
283
320
  <div className="mb-10">
@@ -309,6 +346,17 @@ export function GatewaySheet() {
309
346
  <p className="text-[11px] text-text-3/60 mt-2">Remote HTTPS URLs and local loopback endpoints are both supported.</p>
310
347
  </div>
311
348
 
349
+ <div className="mb-6">
350
+ <OpenClawDeployPanel
351
+ endpoint={endpoint}
352
+ token={tokenDraft}
353
+ suggestedName={name || null}
354
+ title="Deploy OpenClaw From SwarmClaw"
355
+ description="Use official OpenClaw sources only. Start it on this host, or generate a pre-configured remote bundle for VPS and hosted deployments."
356
+ onApply={applyDeployPatch}
357
+ />
358
+ </div>
359
+
312
360
  {discoveries.length > 0 && (
313
361
  <div className="mb-6">
314
362
  <div className="text-[12px] text-text-3/70 mb-2">Detected healthy gateways</div>
@@ -342,6 +390,18 @@ export function GatewaySheet() {
342
390
  <option key={item.id} value={item.id}>{item.name}</option>
343
391
  ))}
344
392
  </select>
393
+ <input
394
+ value={tokenDraft}
395
+ onChange={(e) => {
396
+ setTokenDraft(e.target.value)
397
+ if (e.target.value) setCredentialId(null)
398
+ }}
399
+ placeholder="Or paste/generate a new gateway token"
400
+ className={`${inputClass} mt-3 font-mono text-[13px]`}
401
+ />
402
+ <p className="mt-2 text-[11px] text-text-3/60">
403
+ A pasted token is stored as a new encrypted OpenClaw credential when you save this gateway.
404
+ </p>
345
405
  </div>
346
406
 
347
407
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">