@swarmclawai/swarmclaw 1.3.3 → 1.3.4

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 CHANGED
@@ -204,6 +204,10 @@ Read the full setup guide in [`SWARMDOCK.md`](./SWARMDOCK.md), browse the public
204
204
 
205
205
  ## Release Notes
206
206
 
207
+ ### v1.3.4 Highlights
208
+
209
+ - **Bug fix — custom provider loading under Turbopack (#32)**: converted all CommonJS `require()` calls across the codebase to ES module imports, fixing "Unknown provider: custom-\<id\>" errors and other potential Turbopack compatibility issues. Affected modules: providers, provider health, subagent swarm, prompt builder, chat finalization, CLI utils, and OpenClaw connectors. Thanks to @psywolf85 for the initial fix.
210
+
207
211
  ### v1.3.3 Highlights
208
212
 
209
213
  - **Bug fix — stale connector status after auto-restart (#31)**: connectors that auto-restart via the daemon health monitor now show "Starting" instead of a stale "Stopped" or "Error" status in the UI until the daemon reports runtime state. Added `starting` to the `ConnectorStatus` type and updated both the connector list and detail views.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmclawai/swarmclaw",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "Self-hosted AI runtime for OpenClaw, delegation, autonomy, runtime skills, crypto wallets, and chat platform connectors.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -8,6 +8,7 @@
8
8
 
9
9
  import fs from 'fs'
10
10
  import os from 'os'
11
+ import { findBinaryOnPath } from '../server/session-tools/context'
11
12
  import path from 'path'
12
13
  import { spawnSync, type ChildProcess } from 'child_process'
13
14
  import { log } from '../server/logger'
@@ -63,10 +64,8 @@ function getNvmBinaryPaths(name: string): string[] {
63
64
  * then falls back to known paths + nvm paths.
64
65
  */
65
66
  export function resolveCliBinary(name: string, extraPaths?: string[]): string | null {
66
- // Lazy import to avoid circular dependency at module load
67
- // eslint-disable-next-line @typescript-eslint/no-require-imports
68
- const { findBinaryOnPath } = require('../server/session-tools/context')
69
- const fromPath = findBinaryOnPath(name) as string | null
67
+ const fromPath = findBinaryOnPath(name)
68
+
70
69
  if (fromPath) return fromPath
71
70
 
72
71
  const paths = [
@@ -11,6 +11,8 @@ import { errorMessage, sleep, jitteredBackoff } from '@/lib/shared-utils'
11
11
  import { classifyProviderError } from './error-classification'
12
12
  import { log } from '@/lib/server/logger'
13
13
  import type { ProviderInfo, ProviderConfig as CustomProviderConfig, ProviderType, ProviderId } from '../../types'
14
+ import { loadProviderConfigs, loadModelOverrides, loadStoredItem, loadCredentials, decryptKey } from '@/lib/server/storage'
15
+ import { getExtensionManager } from '@/lib/server/extensions'
14
16
 
15
17
  const TAG = 'providers'
16
18
 
@@ -289,8 +291,6 @@ export const PROVIDERS: Record<string, BuiltinProviderConfig> = {
289
291
  /** Merge built-in providers with custom providers from storage */
290
292
  function getCustomProviders(): Record<string, CustomProviderConfig> {
291
293
  try {
292
- // eslint-disable-next-line @typescript-eslint/no-require-imports
293
- const { loadProviderConfigs } = require('../server/storage') as typeof import('@/lib/server/storage')
294
294
  const configs = loadProviderConfigs() as Record<string, CustomProviderConfig>
295
295
  return Object.fromEntries(
296
296
  Object.entries(configs).filter(([, config]) => config?.type === 'custom'),
@@ -303,8 +303,6 @@ function getCustomProviders(): Record<string, CustomProviderConfig> {
303
303
 
304
304
  function getModelOverrides(): Record<string, string[]> {
305
305
  try {
306
- // eslint-disable-next-line @typescript-eslint/no-require-imports
307
- const { loadModelOverrides } = require('../server/storage') as typeof import('@/lib/server/storage')
308
306
  return loadModelOverrides()
309
307
  } catch {
310
308
  return {}
@@ -342,17 +340,15 @@ export function getProviderList(): ProviderInfo[] {
342
340
 
343
341
  let extensionProviders: ProviderInfo[] = []
344
342
  try {
345
- // eslint-disable-next-line @typescript-eslint/no-require-imports
346
- const { getExtensionManager } = require('../server/extensions')
347
- extensionProviders = getExtensionManager().getProviders().map((p: Record<string, unknown>) => ({
348
- id: String(p.id) as ProviderId,
349
- name: String(p.name),
350
- models: p.models as string[],
351
- defaultModels: p.models as string[],
352
- supportsModelDiscovery: Boolean(p.supportsModelDiscovery),
353
- requiresApiKey: Boolean(p.requiresApiKey),
354
- requiresEndpoint: Boolean(p.requiresEndpoint),
355
- defaultEndpoint: p.defaultEndpoint as string | undefined,
343
+ extensionProviders = getExtensionManager().getProviders().map((p) => ({
344
+ id: p.id as ProviderId,
345
+ name: p.name,
346
+ models: p.models,
347
+ defaultModels: p.models,
348
+ supportsModelDiscovery: false,
349
+ requiresApiKey: p.requiresApiKey,
350
+ requiresEndpoint: p.requiresEndpoint,
351
+ defaultEndpoint: p.defaultEndpoint,
356
352
  }))
357
353
  } catch { /* ignore if running somewhere extensions aren't available */ }
358
354
 
@@ -391,8 +387,6 @@ export function getProvider(id: string): BuiltinProviderConfig | null {
391
387
  // Fallback: direct single-item DB lookup for custom-* providers
392
388
  if (id.startsWith('custom-') && !custom) {
393
389
  try {
394
- // eslint-disable-next-line @typescript-eslint/no-require-imports
395
- const { loadStoredItem } = require('../server/storage') as typeof import('@/lib/server/storage')
396
390
  const directConfig = loadStoredItem('provider_configs', id) as CustomProviderConfig | null
397
391
  if (directConfig?.type === 'custom' && directConfig.isEnabled) {
398
392
  log.info(TAG, `Resolved custom provider '${id}' via direct DB lookup (batch load missed it)`)
@@ -405,10 +399,8 @@ export function getProvider(id: string): BuiltinProviderConfig | null {
405
399
 
406
400
  // Check Extension Providers
407
401
  try {
408
- // eslint-disable-next-line @typescript-eslint/no-require-imports
409
- const { getExtensionManager } = require('../server/extensions')
410
402
  const extensionProviders = getExtensionManager().getProviders()
411
- const found = extensionProviders.find((p: Record<string, unknown>) => p.id === id)
403
+ const found = extensionProviders.find((p) => p.id === id)
412
404
  if (found) {
413
405
  return {
414
406
  id: found.id as ProviderId,
@@ -455,8 +447,6 @@ export async function streamChatWithFailover(
455
447
  let apiKey: string | null = opts.apiKey || null
456
448
  if (credId && i > 0) {
457
449
  // Need to decrypt fallback credential
458
- // eslint-disable-next-line @typescript-eslint/no-require-imports
459
- const { loadCredentials, decryptKey } = require('../server/storage') as typeof import('@/lib/server/storage')
460
450
  const creds = loadCredentials()
461
451
  const cred = creds[credId]
462
452
  if (cred?.encryptedKey) {
@@ -8,6 +8,7 @@ import type { Agent } from '@/types'
8
8
  import { deriveOpenClawWsUrl } from '@/lib/openclaw/openclaw-endpoint'
9
9
  import { normalizeOpenClawAgentId } from '@/lib/openclaw/openclaw-agent-id'
10
10
  import { loadAgents } from '../server/storage'
11
+ import { getSharedDeviceToken } from '../server/openclaw/sync'
11
12
  import {
12
13
  resolveOpenClawGatewayAgentIdFromList,
13
14
  type OpenClawGatewayAgentSummary,
@@ -74,8 +75,6 @@ function tryLoadIdentityFile(filePath: string): DeviceIdentity | null {
74
75
  function loadOrCreateDeviceIdentity(): DeviceIdentity {
75
76
  // 0. Check shared device token for cross-synced identity
76
77
  try {
77
- // eslint-disable-next-line @typescript-eslint/no-require-imports
78
- const { getSharedDeviceToken } = require('../server/openclaw/sync')
79
78
  const sharedToken = getSharedDeviceToken()
80
79
  if (sharedToken) {
81
80
  // Shared token exists — the connector has already paired.
@@ -17,6 +17,7 @@ import { logActivity } from '@/lib/server/activity/activity-log'
17
17
  import { createNotification } from '@/lib/server/create-notification'
18
18
  import { notify } from '@/lib/server/ws-hub'
19
19
  import { loadAgents } from '@/lib/server/agents/agent-repository'
20
+ import { upsertStoredItem, loadStoredItem, loadCollection } from '../storage'
20
21
  import {
21
22
  spawnSubagent,
22
23
  type SubagentContext,
@@ -159,8 +160,6 @@ function notifySwarmChanged() {
159
160
 
160
161
  function persistSwarmSnapshot(swarm: SwarmHandle): void {
161
162
  try {
162
- // eslint-disable-next-line @typescript-eslint/no-require-imports
163
- const { upsertStoredItem } = require('../storage')
164
163
  upsertStoredItem('swarm_snapshots', swarm.swarmId, {
165
164
  swarmId: swarm.swarmId,
166
165
  parentSessionId: swarm.parentSessionId,
@@ -549,8 +548,6 @@ export function getSwarmSnapshot(swarmId: string): SwarmSnapshot | null {
549
548
  if (swarm) return buildSwarmSnapshot(swarm)
550
549
  // Fallback to persisted store for swarms from previous process lifetimes
551
550
  try {
552
- // eslint-disable-next-line @typescript-eslint/no-require-imports
553
- const { loadStoredItem } = require('../storage')
554
551
  const persisted = loadStoredItem('swarm_snapshots', swarmId)
555
552
  return persisted ? (persisted as SwarmSnapshot) : null
556
553
  } catch { return null }
@@ -640,9 +637,7 @@ function buildSwarmSnapshot(swarm: SwarmHandle): SwarmSnapshot {
640
637
  */
641
638
  export function restoreSwarmRegistry(): number {
642
639
  try {
643
- // eslint-disable-next-line @typescript-eslint/no-require-imports
644
- const { loadCollection, upsertStoredItem } = require('../storage')
645
- const persisted = loadCollection('swarm_snapshots') as Record<string, SwarmSnapshot>
640
+ const persisted = loadCollection('swarm_snapshots') as unknown as Record<string, SwarmSnapshot>
646
641
  let lost = 0
647
642
  for (const [id, record] of Object.entries(persisted)) {
648
643
  if (swarmRegistry.has(id)) continue
@@ -1,4 +1,5 @@
1
1
  import type { Message, MessageToolEvent, SSEEvent, Session, UsageRecord } from '@/types'
2
+ import { sendConnectorMessage } from '../connectors/manager'
2
3
  import { applyExactOutputContract, classifyExactOutputContract, type ExactOutputContract } from '@/lib/server/chat-execution/exact-output-contract'
3
4
  import { stripMainLoopMetaForPersistence } from '@/lib/server/agents/main-agent-loop'
4
5
  import { shouldSuppressHiddenControlText, stripHiddenControlTokens } from '@/lib/server/agents/assistant-control'
@@ -478,8 +479,6 @@ export async function finalizeChatTurn(params: {
478
479
  && heartbeatConfig.target !== 'none'
479
480
  ) {
480
481
  try {
481
- // eslint-disable-next-line @typescript-eslint/no-require-imports
482
- const { sendConnectorMessage } = require('../connectors/manager')
483
482
  let connectorId: string | undefined
484
483
  let channelId: string | undefined
485
484
  if (heartbeatConfig.target === 'last') {
@@ -527,9 +526,7 @@ export async function finalizeChatTurn(params: {
527
526
  : ''
528
527
  if (!recentInbound && channelId) {
529
528
  try {
530
- // eslint-disable-next-line @typescript-eslint/no-require-imports
531
- const { sendConnectorMessage: sendMsg } = require('../connectors/manager')
532
- sendMsg({ connectorId: connectorId || undefined, channelId, text: nextAssistantMessage.text }).catch((err: unknown) => {
529
+ sendConnectorMessage({ connectorId: connectorId || undefined, channelId, text: nextAssistantMessage.text }).catch((err: unknown) => {
533
530
  log.warn('connector', 'Auto-route connector delivery failed', {
534
531
  connectorId,
535
532
  channelId,
@@ -11,6 +11,8 @@ import {
11
11
  collectCapabilityOperatingGuidance,
12
12
  } from '@/lib/server/native-capabilities'
13
13
  import { getExtensionManager } from '@/lib/server/extensions'
14
+ import { loadAgents } from '../storage'
15
+ import { resolveTeam } from '../agents/team-resolution'
14
16
  import {
15
17
  getEnabledToolPlanningView,
16
18
  getToolsForCapability,
@@ -44,12 +46,8 @@ function buildExtensionCapabilityLines(enabledExtensions: string[], opts?: { del
44
46
  // CLI team hint — if teammates run CLI providers, mention their strengths
45
47
  if (opts.agentId) {
46
48
  try {
47
- // eslint-disable-next-line @typescript-eslint/no-require-imports
48
- const { loadAgents } = require('../storage')
49
- // eslint-disable-next-line @typescript-eslint/no-require-imports
50
- const { resolveTeam } = require('../agents/team-resolution')
51
- const agents = loadAgents() as Record<string, Record<string, unknown>>
52
- const team = resolveTeam(opts.agentId, agents)
49
+ const agents = loadAgents()
50
+ const team = resolveTeam(opts.agentId, agents as Record<string, import('@/types').Agent>)
53
51
  if (team.mode === 'team') {
54
52
  const cliTeammates: string[] = []
55
53
  const allMembers = [...(team.coordinator ? [team.coordinator] : []), ...team.peers, ...team.directReports]
@@ -13,6 +13,7 @@ import {
13
13
  type GatewayResponseFrame,
14
14
  } from '../gateway/protocol'
15
15
  import { log } from '@/lib/server/logger'
16
+ import { setSharedDeviceToken } from '../openclaw/sync'
16
17
 
17
18
  const TAG = 'openclaw'
18
19
 
@@ -830,8 +831,6 @@ const openclaw: PlatformConnector = {
830
831
  // Cross-sync device token for provider identity resolution
831
832
  if (normalized) {
832
833
  try {
833
- // eslint-disable-next-line @typescript-eslint/no-require-imports
834
- const { setSharedDeviceToken } = require('../openclaw/sync')
835
834
  setSharedDeviceToken(normalized)
836
835
  } catch { /* openclaw-sync not available */ }
837
836
  }
@@ -1,5 +1,6 @@
1
1
  import { spawnSync } from 'child_process'
2
2
  import { errorMessage, hmrSingleton, jitteredBackoff } from '@/lib/shared-utils'
3
+ import { upsertStoredItem, loadCollection } from './storage'
3
4
 
4
5
  type DelegateTool = 'delegate_to_claude_code' | 'delegate_to_codex_cli' | 'delegate_to_opencode_cli' | 'delegate_to_gemini_cli'
5
6
 
@@ -70,8 +71,6 @@ export function markProviderFailure(providerId: string, error: string, credentia
70
71
  cooldownUntil: now + cooldownMsForFailures(failures),
71
72
  })
72
73
  try {
73
- // eslint-disable-next-line @typescript-eslint/no-require-imports
74
- const { upsertStoredItem } = require('./storage')
75
74
  upsertStoredItem('provider_health', key, states.get(key)!)
76
75
  } catch {}
77
76
  }
@@ -88,8 +87,6 @@ export function markProviderSuccess(providerId: string, credentialId?: string |
88
87
  cooldownUntil: undefined,
89
88
  })
90
89
  try {
91
- // eslint-disable-next-line @typescript-eslint/no-require-imports
92
- const { upsertStoredItem } = require('./storage')
93
90
  upsertStoredItem('provider_health', key, states.get(key)!)
94
91
  } catch {}
95
92
  }
@@ -187,9 +184,7 @@ export function getProviderHealthSnapshot(): Record<string, ProviderHealthState
187
184
 
188
185
  export function restoreProviderHealthState(): number {
189
186
  try {
190
- // eslint-disable-next-line @typescript-eslint/no-require-imports
191
- const { loadCollection } = require('./storage')
192
- const persisted = loadCollection('provider_health') as Record<string, ProviderHealthState>
187
+ const persisted = loadCollection('provider_health') as unknown as Record<string, ProviderHealthState>
193
188
  let restored = 0
194
189
  for (const [id, record] of Object.entries(persisted)) {
195
190
  if (states.has(id)) continue
@@ -242,7 +242,7 @@ function loadCollectionWithNormalizationState(table: string): {
242
242
  return { result, normalizedCount }
243
243
  }
244
244
 
245
- function loadCollection(table: string): Record<string, StoredObject> {
245
+ export function loadCollection(table: string): Record<string, StoredObject> {
246
246
  const { result, normalizedCount } = loadCollectionWithNormalizationState(table)
247
247
  if (normalizedCount > 0) saveCollection(table, result)
248
248
  return result