@swarmclawai/swarmclaw 1.7.1 → 1.7.2

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.
@@ -0,0 +1,84 @@
1
+ import {
2
+ CLI_PROVIDER_METADATA_BY_ID,
3
+ isCliProviderId,
4
+ type CliAuthBackend,
5
+ type CliProviderMetadata,
6
+ } from '@/lib/providers/cli-provider-metadata'
7
+ import { buildCliEnv, probeCliAuth, resolveCliBinary, type AuthProbeResult } from '@/lib/providers/cli-utils'
8
+
9
+ export interface CliProviderReadyResult {
10
+ ok: boolean
11
+ message: string
12
+ providerId?: string
13
+ displayName?: string
14
+ binaryName?: string
15
+ binaryPath?: string
16
+ generic?: boolean
17
+ }
18
+
19
+ interface CheckCliProviderReadyOptions {
20
+ cwd?: string
21
+ env?: NodeJS.ProcessEnv
22
+ resolveBinary?: (name: string) => string | null
23
+ probeAuth?: (
24
+ binary: string,
25
+ backend: CliAuthBackend,
26
+ env: NodeJS.ProcessEnv,
27
+ cwd?: string,
28
+ ) => AuthProbeResult
29
+ }
30
+
31
+ function missingBinaryMessage(meta: CliProviderMetadata): string {
32
+ return `${meta.displayName} is not installed. Install \`${meta.binaryName}\` and ensure it is on your PATH.`
33
+ }
34
+
35
+ export function checkCliProviderReady(
36
+ providerId: string,
37
+ options: CheckCliProviderReadyOptions = {},
38
+ ): CliProviderReadyResult {
39
+ if (!isCliProviderId(providerId)) {
40
+ return { ok: false, message: 'Unknown CLI provider.', providerId }
41
+ }
42
+
43
+ const meta = CLI_PROVIDER_METADATA_BY_ID[providerId]
44
+ const resolveBinary = options.resolveBinary || resolveCliBinary
45
+ const binaryPath = resolveBinary(meta.binaryName)
46
+ if (!binaryPath) {
47
+ return {
48
+ ok: false,
49
+ message: missingBinaryMessage(meta),
50
+ providerId,
51
+ displayName: meta.displayName,
52
+ binaryName: meta.binaryName,
53
+ generic: meta.generic,
54
+ }
55
+ }
56
+
57
+ if (meta.authBackend) {
58
+ const env = options.env || buildCliEnv()
59
+ const auth = (options.probeAuth || probeCliAuth)(binaryPath, meta.authBackend, env, options.cwd || process.cwd())
60
+ if (!auth.authenticated) {
61
+ return {
62
+ ok: false,
63
+ message: auth.errorMessage || `${meta.displayName} is not configured.`,
64
+ providerId,
65
+ displayName: meta.displayName,
66
+ binaryName: meta.binaryName,
67
+ binaryPath,
68
+ generic: meta.generic,
69
+ }
70
+ }
71
+ }
72
+
73
+ return {
74
+ ok: true,
75
+ message: meta.authBackend
76
+ ? `${meta.displayName} is installed and ready.`
77
+ : `${meta.displayName} binary is available. If it requires account setup, complete that in \`${meta.binaryName}\` before running agent turns.`,
78
+ providerId,
79
+ displayName: meta.displayName,
80
+ binaryName: meta.binaryName,
81
+ binaryPath,
82
+ generic: meta.generic,
83
+ }
84
+ }
@@ -179,4 +179,10 @@ describe('provider-health', () => {
179
179
  globalThis.fetch = originalFetch
180
180
  }
181
181
  })
182
+
183
+ it('skips extended CLI providers instead of treating them as HTTP providers', async () => {
184
+ const result = await providerHealth.pingProvider('aider-cli', undefined, undefined)
185
+ assert.equal(result.ok, true)
186
+ assert.equal(result.message, 'CLI provider - skipped.')
187
+ })
182
188
  })
@@ -2,6 +2,7 @@ import { spawnSync } from 'child_process'
2
2
  import { errorMessage, hmrSingleton, jitteredBackoff } from '@/lib/shared-utils'
3
3
  import { upsertStoredItem, loadCollection } from './storage'
4
4
  import { log } from './logger'
5
+ import { isCliProviderId } from '@/lib/providers/cli-provider-metadata'
5
6
 
6
7
  const TAG = 'provider-health'
7
8
 
@@ -352,9 +353,8 @@ export async function pingProvider(
352
353
  apiKey: string | undefined,
353
354
  endpoint: string | undefined,
354
355
  ): Promise<{ ok: boolean; message: string }> {
355
- const CLI_PROVIDERS = ['claude-cli', 'codex-cli', 'opencode-cli', 'gemini-cli', 'copilot-cli', 'droid-cli', 'cursor-cli', 'qwen-code-cli', 'goose']
356
356
  const OPTIONAL_OPENAI_COMPATIBLE_KEY_PROVIDERS = new Set(['hermes'])
357
- if (CLI_PROVIDERS.includes(provider)) return { ok: true, message: 'CLI provider skipped.' }
357
+ if (isCliProviderId(provider)) return { ok: true, message: 'CLI provider - skipped.' }
358
358
 
359
359
  try {
360
360
  if (provider === 'anthropic') {
@@ -1,5 +1,6 @@
1
1
  import assert from 'node:assert/strict'
2
2
  import { test } from 'node:test'
3
+ import { CLI_PROVIDER_METADATA } from './providers/cli-provider-metadata'
3
4
  import { DEFAULT_AGENTS, getDefaultModelForProvider } from './setup-defaults'
4
5
 
5
6
  // ---------------------------------------------------------------------------
@@ -48,6 +49,13 @@ test('getDefaultModelForProvider returns expected defaults for cursor, qwen, and
48
49
  assert.equal(getDefaultModelForProvider('goose'), 'default')
49
50
  })
50
51
 
52
+ test('every CLI provider has setup default agent coverage', () => {
53
+ for (const provider of CLI_PROVIDER_METADATA) {
54
+ assert.equal(getDefaultModelForProvider(provider.id), provider.defaultModel)
55
+ assert.ok(DEFAULT_AGENTS[provider.id].description.includes(provider.displayName))
56
+ }
57
+ })
58
+
51
59
  test('getDefaultModelForProvider returns non-empty for ollama', () => {
52
60
  const model = getDefaultModelForProvider('ollama')
53
61
  assert.ok(model, 'ollama model should be truthy')
@@ -3,33 +3,10 @@
3
3
  * Isomorphic — no 'use client', no server imports.
4
4
  */
5
5
 
6
- export type SetupProvider =
7
- | 'claude-cli'
8
- | 'codex-cli'
9
- | 'opencode-cli'
10
- | 'opencode-web'
11
- | 'gemini-cli'
12
- | 'copilot-cli'
13
- | 'droid-cli'
14
- | 'cursor-cli'
15
- | 'qwen-code-cli'
16
- | 'goose'
17
- | 'anthropic'
18
- | 'openai'
19
- | 'openrouter'
20
- | 'google'
21
- | 'deepseek'
22
- | 'groq'
23
- | 'together'
24
- | 'mistral'
25
- | 'xai'
26
- | 'fireworks'
27
- | 'nebius'
28
- | 'deepinfra'
29
- | 'ollama'
30
- | 'openclaw'
31
- | 'hermes'
32
- | 'custom'
6
+ import { CLI_PROVIDER_METADATA, type CliProviderId, type CliProviderMetadata } from './providers/cli-provider-metadata.ts'
7
+ import type { ProviderType } from '../types/provider.ts'
8
+
9
+ export type SetupProvider = ProviderType | 'custom'
33
10
 
34
11
  export interface SetupProviderOption {
35
12
  id: SetupProvider
@@ -48,38 +25,27 @@ export interface SetupProviderOption {
48
25
  icon: string
49
26
  modelLibraryUrl?: string
50
27
  cloudEndpoint?: string
28
+ category?: 'cli' | 'api' | 'gateway' | 'local' | 'custom'
51
29
  }
52
30
 
31
+ const CLI_SETUP_PROVIDERS: SetupProviderOption[] = (CLI_PROVIDER_METADATA as readonly CliProviderMetadata[]).map((provider) => ({
32
+ id: provider.id,
33
+ name: provider.displayName,
34
+ description: provider.description,
35
+ requiresKey: false,
36
+ supportsEndpoint: false,
37
+ optionalKey: provider.optionalApiKey,
38
+ keyUrl: provider.keyUrl,
39
+ keyLabel: provider.keyLabel,
40
+ keyPlaceholder: provider.keyPlaceholder,
41
+ badge: provider.setupBadge,
42
+ icon: provider.icon,
43
+ modelLibraryUrl: provider.modelLibraryUrl,
44
+ category: 'cli',
45
+ }))
46
+
53
47
  export const SETUP_PROVIDERS: SetupProviderOption[] = [
54
- {
55
- id: 'claude-cli',
56
- name: 'Claude Code CLI',
57
- description: 'Anthropic’s coding agent with native tools, strong edits, and first-class CLI workflows.',
58
- requiresKey: false,
59
- supportsEndpoint: false,
60
- badge: 'CLI',
61
- icon: 'C',
62
- modelLibraryUrl: 'https://docs.anthropic.com/en/docs/about-claude/models',
63
- },
64
- {
65
- id: 'codex-cli',
66
- name: 'OpenAI Codex CLI',
67
- description: 'OpenAI’s terminal coding agent with resume support and structured headless output.',
68
- requiresKey: false,
69
- supportsEndpoint: false,
70
- badge: 'CLI',
71
- icon: 'O',
72
- modelLibraryUrl: 'https://platform.openai.com/docs/models',
73
- },
74
- {
75
- id: 'opencode-cli',
76
- name: 'OpenCode CLI',
77
- description: 'A flexible coding CLI that can route across multiple model backends.',
78
- requiresKey: false,
79
- supportsEndpoint: false,
80
- badge: 'CLI',
81
- icon: 'O',
82
- },
48
+ ...CLI_SETUP_PROVIDERS,
83
49
  {
84
50
  id: 'opencode-web',
85
51
  name: 'OpenCode Web',
@@ -92,66 +58,7 @@ export const SETUP_PROVIDERS: SetupProviderOption[] = [
92
58
  keyPlaceholder: 'opencode:••••••• (or just the password)',
93
59
  badge: 'HTTP',
94
60
  icon: 'O',
95
- },
96
- {
97
- id: 'gemini-cli',
98
- name: 'Gemini CLI',
99
- description: 'Google’s terminal coding agent with project-aware headless mode and resume support.',
100
- requiresKey: false,
101
- supportsEndpoint: false,
102
- badge: 'CLI',
103
- icon: 'G',
104
- modelLibraryUrl: 'https://ai.google.dev/gemini-api/docs/models',
105
- },
106
- {
107
- id: 'copilot-cli',
108
- name: 'GitHub Copilot CLI',
109
- description: 'GitHub’s multi-model terminal agent for coding and automation.',
110
- requiresKey: false,
111
- supportsEndpoint: false,
112
- badge: 'CLI',
113
- icon: 'P',
114
- },
115
- {
116
- id: 'droid-cli',
117
- name: 'Factory Droid CLI',
118
- description: 'Factory.ai’s terminal coding agent with headless exec mode, session resume, and autonomy controls.',
119
- requiresKey: false,
120
- supportsEndpoint: false,
121
- optionalKey: true,
122
- keyUrl: 'https://app.factory.ai/settings/api-keys',
123
- keyLabel: 'app.factory.ai',
124
- keyPlaceholder: 'FACTORY_API_KEY (optional if signed in via `droid`)',
125
- badge: 'CLI',
126
- icon: 'F',
127
- },
128
- {
129
- id: 'cursor-cli',
130
- name: 'Cursor Agent CLI',
131
- description: 'Cursor’s terminal agent with resume support, JSON output, and Cursor-native coding workflows.',
132
- requiresKey: false,
133
- supportsEndpoint: false,
134
- badge: 'CLI',
135
- icon: 'U',
136
- },
137
- {
138
- id: 'qwen-code-cli',
139
- name: 'Qwen Code CLI',
140
- description: 'Qwen’s terminal coding agent with structured headless mode and multi-provider model config.',
141
- requiresKey: false,
142
- supportsEndpoint: false,
143
- badge: 'CLI',
144
- icon: 'Q',
145
- },
146
- {
147
- id: 'goose',
148
- name: 'Goose',
149
- description: 'A runtime-managed terminal agent with extensions, session history, and ACP support.',
150
- requiresKey: false,
151
- supportsEndpoint: false,
152
- optionalKey: true,
153
- badge: 'Runtime',
154
- icon: 'G',
61
+ category: 'cli',
155
62
  },
156
63
  {
157
64
  id: 'openai',
@@ -190,6 +97,7 @@ export const SETUP_PROVIDERS: SetupProviderOption[] = [
190
97
  optionalKey: true,
191
98
  badge: 'First-Tier',
192
99
  icon: 'C',
100
+ category: 'gateway',
193
101
  },
194
102
  {
195
103
  id: 'hermes',
@@ -202,6 +110,7 @@ export const SETUP_PROVIDERS: SetupProviderOption[] = [
202
110
  optionalKey: true,
203
111
  badge: 'API Server',
204
112
  icon: 'H',
113
+ category: 'gateway',
205
114
  },
206
115
  {
207
116
  id: 'anthropic',
@@ -327,6 +236,7 @@ export const SETUP_PROVIDERS: SetupProviderOption[] = [
327
236
  icon: 'L',
328
237
  modelLibraryUrl: 'https://ollama.com/library',
329
238
  cloudEndpoint: 'https://api.ollama.com',
239
+ category: 'local',
330
240
  },
331
241
  {
332
242
  id: 'custom',
@@ -337,6 +247,7 @@ export const SETUP_PROVIDERS: SetupProviderOption[] = [
337
247
  allowMultiple: true,
338
248
  optionalKey: true,
339
249
  icon: '+',
250
+ category: 'custom',
340
251
  },
341
252
  ]
342
253
 
@@ -815,28 +726,19 @@ export interface DefaultAgentConfig {
815
726
  tools: string[]
816
727
  }
817
728
 
818
- export const DEFAULT_AGENTS: Record<SetupProvider, DefaultAgentConfig> = {
819
- 'claude-cli': {
820
- name: 'Claude CLI',
821
- description: 'A helpful assistant powered by Claude Code CLI.',
822
- systemPrompt: SWARMCLAW_ASSISTANT_PROMPT,
823
- model: 'claude-sonnet-4-6',
824
- tools: STARTER_AGENT_TOOLS,
825
- },
826
- 'codex-cli': {
827
- name: 'Codex CLI',
828
- description: 'A helpful assistant powered by OpenAI Codex CLI.',
829
- systemPrompt: SWARMCLAW_ASSISTANT_PROMPT,
830
- model: 'gpt-5.3-codex',
831
- tools: STARTER_AGENT_TOOLS,
832
- },
833
- 'opencode-cli': {
834
- name: 'OpenCode',
835
- description: 'A helpful assistant powered by OpenCode CLI.',
729
+ const CLI_DEFAULT_AGENTS = Object.fromEntries(CLI_PROVIDER_METADATA.map((provider) => [
730
+ provider.id,
731
+ {
732
+ name: provider.displayName.endsWith(' CLI') ? provider.displayName.slice(0, -4) : provider.displayName,
733
+ description: `A helpful assistant powered by ${provider.displayName}.`,
836
734
  systemPrompt: SWARMCLAW_ASSISTANT_PROMPT,
837
- model: 'claude-sonnet-4-6',
735
+ model: provider.defaultModel,
838
736
  tools: STARTER_AGENT_TOOLS,
839
737
  },
738
+ ])) as Record<CliProviderId, DefaultAgentConfig>
739
+
740
+ export const DEFAULT_AGENTS = {
741
+ ...CLI_DEFAULT_AGENTS,
840
742
  'opencode-web': {
841
743
  name: 'OpenCode Web',
842
744
  description: 'A helpful assistant powered by a remote OpenCode HTTP server.',
@@ -844,48 +746,6 @@ export const DEFAULT_AGENTS: Record<SetupProvider, DefaultAgentConfig> = {
844
746
  model: 'anthropic/claude-sonnet-4-6',
845
747
  tools: STARTER_AGENT_TOOLS,
846
748
  },
847
- 'gemini-cli': {
848
- name: 'Gemini CLI',
849
- description: 'A helpful assistant powered by Gemini CLI.',
850
- systemPrompt: SWARMCLAW_ASSISTANT_PROMPT,
851
- model: 'gemini-3.1-pro',
852
- tools: STARTER_AGENT_TOOLS,
853
- },
854
- 'copilot-cli': {
855
- name: 'Copilot CLI',
856
- description: 'A helpful assistant powered by GitHub Copilot CLI.',
857
- systemPrompt: SWARMCLAW_ASSISTANT_PROMPT,
858
- model: 'claude-sonnet-4-6',
859
- tools: STARTER_AGENT_TOOLS,
860
- },
861
- 'droid-cli': {
862
- name: 'Factory Droid',
863
- description: 'A helpful assistant powered by Factory Droid CLI.',
864
- systemPrompt: SWARMCLAW_ASSISTANT_PROMPT,
865
- model: 'default',
866
- tools: STARTER_AGENT_TOOLS,
867
- },
868
- 'cursor-cli': {
869
- name: 'Cursor CLI',
870
- description: 'A helpful assistant powered by Cursor Agent CLI.',
871
- systemPrompt: SWARMCLAW_ASSISTANT_PROMPT,
872
- model: 'auto',
873
- tools: STARTER_AGENT_TOOLS,
874
- },
875
- 'qwen-code-cli': {
876
- name: 'Qwen Code',
877
- description: 'A helpful assistant powered by Qwen Code CLI.',
878
- systemPrompt: SWARMCLAW_ASSISTANT_PROMPT,
879
- model: 'default',
880
- tools: STARTER_AGENT_TOOLS,
881
- },
882
- goose: {
883
- name: 'Goose',
884
- description: 'A helpful assistant powered by Goose.',
885
- systemPrompt: SWARMCLAW_ASSISTANT_PROMPT,
886
- model: 'default',
887
- tools: STARTER_AGENT_TOOLS,
888
- },
889
749
  anthropic: {
890
750
  name: 'Claude',
891
751
  description: 'A helpful Claude-powered assistant.',
@@ -998,7 +858,7 @@ export const DEFAULT_AGENTS: Record<SetupProvider, DefaultAgentConfig> = {
998
858
  model: '',
999
859
  tools: STARTER_AGENT_TOOLS,
1000
860
  },
1001
- }
861
+ } satisfies Record<SetupProvider, DefaultAgentConfig>
1002
862
 
1003
863
  export function getDefaultModelForProvider(provider: SetupProvider): string {
1004
864
  return DEFAULT_AGENTS[provider].model
package/tsconfig.json CHANGED
@@ -13,6 +13,7 @@
13
13
  "esModuleInterop": true,
14
14
  "module": "esnext",
15
15
  "moduleResolution": "bundler",
16
+ "allowImportingTsExtensions": true,
16
17
  "resolveJsonModule": true,
17
18
  "isolatedModules": true,
18
19
  "jsx": "react-jsx",