@clappstore/connect 0.7.7 → 0.7.9

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.
Files changed (41) hide show
  1. package/dist/agent-client.d.ts +6 -0
  2. package/dist/agent-client.d.ts.map +1 -1
  3. package/dist/agent-client.js +102 -13
  4. package/dist/agent-client.js.map +1 -1
  5. package/dist/auth.d.ts +18 -0
  6. package/dist/auth.d.ts.map +1 -0
  7. package/dist/auth.js +248 -0
  8. package/dist/auth.js.map +1 -0
  9. package/dist/chat-handler.d.ts +52 -0
  10. package/dist/chat-handler.d.ts.map +1 -0
  11. package/dist/chat-handler.js +453 -0
  12. package/dist/chat-handler.js.map +1 -0
  13. package/dist/defaults.d.ts +1 -1
  14. package/dist/defaults.d.ts.map +1 -1
  15. package/dist/defaults.js +36 -23
  16. package/dist/defaults.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +53 -30
  20. package/dist/index.js.map +1 -1
  21. package/dist/server.d.ts +1 -0
  22. package/dist/server.d.ts.map +1 -1
  23. package/dist/server.js +88 -7
  24. package/dist/server.js.map +1 -1
  25. package/dist/settings-handler.d.ts +76 -0
  26. package/dist/settings-handler.d.ts.map +1 -0
  27. package/dist/settings-handler.js +848 -0
  28. package/dist/settings-handler.js.map +1 -0
  29. package/package.json +4 -8
  30. package/web-app/assets/{index-CEpgiIwf.js → index-CWzlxjUK.js} +86 -56
  31. package/web-app/assets/index-Cic64hbc.css +1 -0
  32. package/web-app/index.html +2 -2
  33. package/clapps/settings/README.md +0 -74
  34. package/clapps/settings/clapp.json +0 -25
  35. package/clapps/settings/components/ProviderEditor.tsx +0 -512
  36. package/clapps/settings/components/ProviderList.tsx +0 -300
  37. package/clapps/settings/components/SessionList.tsx +0 -189
  38. package/clapps/settings/handlers/settings-handler.js +0 -742
  39. package/clapps/settings/views/default.settings.view.md +0 -35
  40. package/clapps/settings/views/settings.app.md +0 -12
  41. package/web-app/assets/index-BsI5PEAv.css +0 -1
@@ -1,512 +0,0 @@
1
- import { useState, useEffect } from "react";
2
- import { useIntent } from "@clapps/renderer";
3
- import { cn } from "@/lib/utils";
4
- import { X, Eye, EyeOff, Loader2, ExternalLink } from "lucide-react";
5
- import { Button } from "@/components/ui/button";
6
-
7
- export type ProviderType = "anthropic" | "openai" | "kimi-coding";
8
- export type AuthMode = "api-key" | "subscription";
9
-
10
- interface ProviderConfig {
11
- type: ProviderType;
12
- name: string;
13
- description: string;
14
- authModes: {
15
- mode: AuthMode;
16
- label: string;
17
- description: string;
18
- fields: FieldConfig[];
19
- helpUrl?: string;
20
- }[];
21
- }
22
-
23
- interface FieldConfig {
24
- name: string;
25
- label: string;
26
- type: "text" | "password" | "textarea";
27
- placeholder?: string;
28
- helpText?: string;
29
- }
30
-
31
- const PROVIDER_CONFIGS: ProviderConfig[] = [
32
- {
33
- type: "anthropic",
34
- name: "Anthropic",
35
- description: "Claude models via API or subscription",
36
- authModes: [
37
- {
38
- mode: "api-key",
39
- label: "API Key",
40
- description: "Use your Anthropic API key (usage-based billing)",
41
- helpUrl: "https://console.anthropic.com/settings/keys",
42
- fields: [
43
- {
44
- name: "apiKey",
45
- label: "API Key",
46
- type: "password",
47
- placeholder: "sk-ant-api03-...",
48
- helpText: "Get your API key from the Anthropic Console",
49
- },
50
- ],
51
- },
52
- {
53
- mode: "subscription",
54
- label: "Claude Subscription",
55
- description: "Use your Claude Pro/Max subscription",
56
- helpUrl: "https://docs.openclaw.ai/providers/anthropic#option-b-claude-setup-token",
57
- fields: [
58
- {
59
- name: "setupToken",
60
- label: "Setup Token",
61
- type: "textarea",
62
- placeholder: "Paste your setup token here...",
63
- helpText: "Run 'claude setup-token' in your terminal to generate this",
64
- },
65
- ],
66
- },
67
- ],
68
- },
69
- {
70
- type: "openai",
71
- name: "OpenAI",
72
- description: "GPT models via API or Codex subscription",
73
- authModes: [
74
- {
75
- mode: "api-key",
76
- label: "API Key",
77
- description: "Use your OpenAI API key (usage-based billing)",
78
- helpUrl: "https://platform.openai.com/api-keys",
79
- fields: [
80
- {
81
- name: "apiKey",
82
- label: "API Key",
83
- type: "password",
84
- placeholder: "sk-...",
85
- helpText: "Get your API key from the OpenAI dashboard",
86
- },
87
- ],
88
- },
89
- {
90
- mode: "subscription",
91
- label: "Codex Subscription",
92
- description: "Use your ChatGPT Plus/Pro subscription via Codex",
93
- helpUrl: "https://docs.openclaw.ai/providers/openai#option-b-openai-code-codex-subscription",
94
- fields: [], // OAuth flow - no fields, just a button
95
- },
96
- ],
97
- },
98
- {
99
- type: "kimi-coding",
100
- name: "Kimi Coding",
101
- description: "Moonshot's Kimi K2 models for coding",
102
- authModes: [
103
- {
104
- mode: "api-key",
105
- label: "API Key",
106
- description: "Use your Kimi Coding API key",
107
- helpUrl: "https://platform.moonshot.cn/console/api-keys",
108
- fields: [
109
- {
110
- name: "apiKey",
111
- label: "API Key",
112
- type: "password",
113
- placeholder: "sk-...",
114
- helpText: "Get your API key from the Moonshot platform",
115
- },
116
- ],
117
- },
118
- ],
119
- },
120
- ];
121
-
122
- interface ProviderEditorProps {
123
- isOpen: boolean;
124
- onClose: () => void;
125
- editingProvider?: {
126
- id: string;
127
- name: string;
128
- type: ProviderType;
129
- mode: string;
130
- authType?: "api-key" | "subscription" | "oauth";
131
- maskedCredential?: string;
132
- } | null;
133
- }
134
-
135
- export function ProviderEditor({ isOpen, onClose, editingProvider }: ProviderEditorProps) {
136
- const { emit } = useIntent();
137
-
138
- const [step, setStep] = useState<"select" | "configure">("select");
139
- const [selectedProvider, setSelectedProvider] = useState<ProviderType | null>(null);
140
- const [selectedAuthMode, setSelectedAuthMode] = useState<AuthMode | null>(null);
141
- const [customName, setCustomName] = useState("");
142
- const [fieldValues, setFieldValues] = useState<Record<string, string>>({});
143
- const [showPassword, setShowPassword] = useState<Record<string, boolean>>({});
144
- const [isSaving, setIsSaving] = useState(false);
145
- const [error, setError] = useState<string | null>(null);
146
-
147
- // Reset state when modal opens/closes
148
- useEffect(() => {
149
- if (isOpen) {
150
- if (editingProvider) {
151
- // Editing existing provider
152
- setSelectedProvider(editingProvider.type);
153
- // Use authType if available, otherwise fall back to mode-based detection
154
- const authMode: AuthMode = editingProvider.authType === "subscription" || editingProvider.authType === "oauth"
155
- ? "subscription"
156
- : editingProvider.mode === "oauth"
157
- ? "subscription"
158
- : "api-key";
159
- setSelectedAuthMode(authMode);
160
- setCustomName(editingProvider.name);
161
- setStep("configure");
162
- } else {
163
- // Adding new provider
164
- setStep("select");
165
- setSelectedProvider(null);
166
- setSelectedAuthMode(null);
167
- setCustomName("");
168
- }
169
- setFieldValues({});
170
- setShowPassword({});
171
- setError(null);
172
- setIsSaving(false);
173
- }
174
- }, [isOpen, editingProvider]);
175
-
176
- const providerConfig = selectedProvider
177
- ? PROVIDER_CONFIGS.find(p => p.type === selectedProvider)
178
- : null;
179
-
180
- const authModeConfig = providerConfig && selectedAuthMode
181
- ? providerConfig.authModes.find(a => a.mode === selectedAuthMode)
182
- : null;
183
-
184
- const handleProviderSelect = (type: ProviderType) => {
185
- setSelectedProvider(type);
186
- const config = PROVIDER_CONFIGS.find(p => p.type === type);
187
- if (config) {
188
- // Auto-select first auth mode
189
- setSelectedAuthMode(config.authModes[0].mode);
190
- // Set default name
191
- setCustomName(config.name);
192
- }
193
- setStep("configure");
194
- };
195
-
196
- const handleFieldChange = (fieldName: string, value: string) => {
197
- setFieldValues(prev => ({ ...prev, [fieldName]: value }));
198
- setError(null);
199
- };
200
-
201
- const togglePasswordVisibility = (fieldName: string) => {
202
- setShowPassword(prev => ({ ...prev, [fieldName]: !prev[fieldName] }));
203
- };
204
-
205
- const handleSave = async () => {
206
- if (!selectedProvider || !selectedAuthMode || !providerConfig) return;
207
-
208
- // Validate required fields
209
- if (authModeConfig?.fields.length) {
210
- for (const field of authModeConfig.fields) {
211
- if (!fieldValues[field.name]?.trim()) {
212
- setError(`${field.label} is required`);
213
- return;
214
- }
215
- }
216
- }
217
-
218
- setIsSaving(true);
219
- setError(null);
220
-
221
- try {
222
- // Determine the intent based on provider and auth mode
223
- const intentName = getIntentName(selectedProvider, selectedAuthMode);
224
- const payload: Record<string, unknown> = {
225
- customName: customName.trim() || providerConfig.name,
226
- profileId: editingProvider?.id,
227
- };
228
-
229
- // Add field values to payload
230
- for (const field of authModeConfig?.fields ?? []) {
231
- payload[field.name] = fieldValues[field.name]?.trim();
232
- }
233
-
234
- emit(intentName, payload);
235
-
236
- // Wait a bit for the handler to process
237
- await new Promise(resolve => setTimeout(resolve, 1000));
238
-
239
- onClose();
240
- } catch (err) {
241
- setError(err instanceof Error ? err.message : "Failed to save provider");
242
- } finally {
243
- setIsSaving(false);
244
- }
245
- };
246
-
247
- const handleOAuthLogin = () => {
248
- if (!selectedProvider) return;
249
-
250
- setIsSaving(true);
251
- emit("settings.startOAuth", {
252
- provider: selectedProvider === "openai" ? "openai-codex" : selectedProvider,
253
- customName: customName.trim() || providerConfig?.name,
254
- });
255
-
256
- // OAuth will redirect, so just show loading
257
- };
258
-
259
- const handleDelete = () => {
260
- if (!editingProvider) return;
261
-
262
- if (confirm(`Delete "${editingProvider.name}"? This cannot be undone.`)) {
263
- emit("settings.deleteProvider", { profileId: editingProvider.id });
264
- onClose();
265
- }
266
- };
267
-
268
- if (!isOpen) return null;
269
-
270
- return (
271
- <div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center">
272
- {/* Backdrop */}
273
- <div
274
- className="absolute inset-0 bg-black/50 backdrop-blur-sm"
275
- onClick={onClose}
276
- />
277
-
278
- {/* Modal */}
279
- <div className="relative w-full sm:max-w-md bg-background rounded-t-2xl sm:rounded-2xl shadow-xl max-h-[90vh] overflow-hidden flex flex-col animate-in slide-in-from-bottom-4 sm:slide-in-from-bottom-0 sm:zoom-in-95">
280
- {/* Header */}
281
- <div className="flex items-center justify-between px-4 py-3 border-b border-border shrink-0">
282
- <h2 className="text-lg font-semibold">
283
- {editingProvider ? "Edit Provider" : "Add Provider"}
284
- </h2>
285
- <button
286
- onClick={onClose}
287
- className="p-1 rounded-full hover:bg-muted transition-colors"
288
- aria-label="Close"
289
- >
290
- <X className="h-5 w-5" />
291
- </button>
292
- </div>
293
-
294
- {/* Content */}
295
- <div className="flex-1 overflow-y-auto p-4">
296
- {step === "select" && (
297
- <div className="space-y-3">
298
- <p className="text-sm text-muted-foreground mb-4">
299
- Choose a provider to add:
300
- </p>
301
- {PROVIDER_CONFIGS.map(provider => (
302
- <button
303
- key={provider.type}
304
- onClick={() => handleProviderSelect(provider.type)}
305
- className="w-full text-left p-4 rounded-lg border border-border hover:border-primary hover:bg-muted/50 transition-colors"
306
- >
307
- <div className="font-medium">{provider.name}</div>
308
- <div className="text-sm text-muted-foreground">{provider.description}</div>
309
- </button>
310
- ))}
311
- </div>
312
- )}
313
-
314
- {step === "configure" && providerConfig && (
315
- <div className="space-y-4">
316
- {/* Provider name input */}
317
- <div className="space-y-2">
318
- <label className="text-sm font-medium">Display Name</label>
319
- <input
320
- type="text"
321
- value={customName}
322
- onChange={(e) => setCustomName(e.target.value)}
323
- placeholder={providerConfig.name}
324
- className="w-full h-10 px-3 rounded-md border border-input bg-background text-sm focus:outline-none focus:ring-2 focus:ring-primary"
325
- />
326
- <p className="text-xs text-muted-foreground">
327
- Give this configuration a custom name (e.g., "Work Account", "Personal")
328
- </p>
329
- </div>
330
-
331
- {/* Auth mode selector (if multiple options) */}
332
- {providerConfig.authModes.length > 1 && (
333
- <div className="space-y-2">
334
- <label className="text-sm font-medium">Authentication Method</label>
335
- <div className="grid grid-cols-2 gap-2">
336
- {providerConfig.authModes.map(authMode => (
337
- <button
338
- key={authMode.mode}
339
- onClick={() => setSelectedAuthMode(authMode.mode)}
340
- className={cn(
341
- "p-3 rounded-lg border text-left transition-colors",
342
- selectedAuthMode === authMode.mode
343
- ? "border-primary bg-primary/10"
344
- : "border-border hover:border-muted-foreground"
345
- )}
346
- >
347
- <div className="text-sm font-medium">{authMode.label}</div>
348
- <div className="text-xs text-muted-foreground mt-0.5">
349
- {authMode.description}
350
- </div>
351
- </button>
352
- ))}
353
- </div>
354
- </div>
355
- )}
356
-
357
- {/* Auth fields */}
358
- {authModeConfig && (
359
- <div className="space-y-4">
360
- {authModeConfig.fields.length > 0 ? (
361
- authModeConfig.fields.map(field => (
362
- <div key={field.name} className="space-y-2">
363
- <label className="text-sm font-medium">{field.label}</label>
364
- {/* Show current credential when editing */}
365
- {editingProvider?.maskedCredential && !fieldValues[field.name] && (
366
- <div className="text-xs text-muted-foreground bg-muted/50 px-2 py-1 rounded mb-1">
367
- Current: {editingProvider.maskedCredential}
368
- </div>
369
- )}
370
- {field.type === "textarea" ? (
371
- <textarea
372
- value={fieldValues[field.name] ?? ""}
373
- onChange={(e) => handleFieldChange(field.name, e.target.value)}
374
- placeholder={editingProvider ? "Enter new value to replace..." : field.placeholder}
375
- rows={4}
376
- className="w-full px-3 py-2 rounded-md border border-input bg-background text-sm font-mono focus:outline-none focus:ring-2 focus:ring-primary resize-none"
377
- />
378
- ) : (
379
- <div className="relative">
380
- <input
381
- type={field.type === "password" && !showPassword[field.name] ? "password" : "text"}
382
- value={fieldValues[field.name] ?? ""}
383
- onChange={(e) => handleFieldChange(field.name, e.target.value)}
384
- placeholder={editingProvider ? "Enter new value to replace..." : field.placeholder}
385
- className="w-full h-10 px-3 pr-10 rounded-md border border-input bg-background text-sm font-mono focus:outline-none focus:ring-2 focus:ring-primary"
386
- />
387
- {field.type === "password" && (
388
- <button
389
- type="button"
390
- onClick={() => togglePasswordVisibility(field.name)}
391
- className="absolute right-2 top-1/2 -translate-y-1/2 p-1 text-muted-foreground hover:text-foreground"
392
- >
393
- {showPassword[field.name] ? (
394
- <EyeOff className="h-4 w-4" />
395
- ) : (
396
- <Eye className="h-4 w-4" />
397
- )}
398
- </button>
399
- )}
400
- </div>
401
- )}
402
- {field.helpText && (
403
- <p className="text-xs text-muted-foreground">{field.helpText}</p>
404
- )}
405
- </div>
406
- ))
407
- ) : (
408
- /* OAuth flow - show connect button */
409
- <div className="space-y-3">
410
- <p className="text-sm text-muted-foreground">
411
- Click the button below to sign in with your {providerConfig.name} account.
412
- </p>
413
- <Button
414
- onClick={handleOAuthLogin}
415
- disabled={isSaving}
416
- className="w-full"
417
- >
418
- {isSaving ? (
419
- <>
420
- <Loader2 className="h-4 w-4 animate-spin mr-2" />
421
- Connecting...
422
- </>
423
- ) : (
424
- `Sign in with ${providerConfig.name}`
425
- )}
426
- </Button>
427
- </div>
428
- )}
429
-
430
- {/* Help link */}
431
- {authModeConfig.helpUrl && (
432
- <a
433
- href={authModeConfig.helpUrl}
434
- target="_blank"
435
- rel="noopener noreferrer"
436
- className="inline-flex items-center gap-1 text-xs text-primary hover:underline"
437
- >
438
- Learn more
439
- <ExternalLink className="h-3 w-3" />
440
- </a>
441
- )}
442
- </div>
443
- )}
444
-
445
- {/* Error message */}
446
- {error && (
447
- <div className="p-3 rounded-md bg-destructive/10 text-destructive text-sm">
448
- {error}
449
- </div>
450
- )}
451
- </div>
452
- )}
453
- </div>
454
-
455
- {/* Footer */}
456
- {step === "configure" && authModeConfig?.fields.length !== 0 && (
457
- <div className="flex items-center gap-2 px-4 py-3 border-t border-border shrink-0">
458
- {editingProvider && (
459
- <Button
460
- variant="destructive"
461
- onClick={handleDelete}
462
- disabled={isSaving}
463
- className="mr-auto"
464
- >
465
- Delete
466
- </Button>
467
- )}
468
- <Button
469
- variant="outline"
470
- onClick={() => editingProvider ? onClose() : setStep("select")}
471
- disabled={isSaving}
472
- >
473
- {editingProvider ? "Cancel" : "Back"}
474
- </Button>
475
- <Button
476
- onClick={handleSave}
477
- disabled={isSaving}
478
- >
479
- {isSaving ? (
480
- <>
481
- <Loader2 className="h-4 w-4 animate-spin mr-2" />
482
- Saving...
483
- </>
484
- ) : (
485
- editingProvider ? "Update" : "Add Provider"
486
- )}
487
- </Button>
488
- </div>
489
- )}
490
- </div>
491
- </div>
492
- );
493
- }
494
-
495
- function getIntentName(provider: ProviderType, authMode: AuthMode): string {
496
- const map: Record<string, Record<AuthMode, string>> = {
497
- anthropic: {
498
- "api-key": "settings.setAnthropicKey",
499
- subscription: "settings.setClaudeToken",
500
- },
501
- openai: {
502
- "api-key": "settings.setOpenAIKey",
503
- subscription: "settings.startOAuth", // Handled separately
504
- },
505
- "kimi-coding": {
506
- "api-key": "settings.setKimiCodingKey",
507
- subscription: "settings.setKimiCodingKey", // No subscription mode
508
- },
509
- };
510
-
511
- return map[provider]?.[authMode] ?? "settings.addProvider";
512
- }