@agent-native/core 0.147.0 → 0.148.0
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/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/analytics/DEVELOPING.md +50 -3
- package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +190 -0
- package/corpus/templates/analytics/app/i18n/index.ts +14 -29
- package/corpus/templates/analytics/app/i18n-data.ts +10 -10
- package/corpus/templates/analytics/app/pages/Settings.tsx +5 -21
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -3
- package/corpus/templates/analytics/scripts/first-party-analytics-bigquery-backfill.ts +197 -18
- package/corpus/templates/analytics/server/db/schema.ts +23 -0
- package/corpus/templates/analytics/server/handlers/first-party-analytics.ts +3 -1
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +1 -1
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +113 -13
- package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +140 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-volume.ts +207 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +40 -9
- package/corpus/templates/analytics/server/lib/gcloud.ts +148 -20
- package/corpus/templates/analytics/server/plugins/db.ts +42 -0
- package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/assets/app/i18n/index.ts +14 -29
- package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/brain/app/i18n/index.ts +14 -29
- package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/chat/app/i18n/index.ts +14 -29
- package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +2 -7
- package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +18 -13
- package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +87 -370
- package/corpus/templates/clips/app/components/settings/slack-section.tsx +59 -101
- package/corpus/templates/clips/app/components/settings/video-storage-section.tsx +136 -213
- package/corpus/templates/clips/app/i18n/en-US.ts +11 -8
- package/corpus/templates/clips/desktop/src/app.tsx +1 -1
- package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/content/app/i18n/index.ts +14 -29
- package/corpus/templates/crm/app/i18n/index.ts +14 -29
- package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/design/actions/analyze-brand-assets.ts +15 -15
- package/corpus/templates/design/actions/import-code.ts +2 -43
- package/corpus/templates/design/app/i18n/index.ts +14 -29
- package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/dispatch/app/i18n-data.ts +1 -1
- package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/factory/AGENTS.md +40 -49
- package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +66 -0
- package/corpus/templates/factory/actions/evaluate-triage-item.ts +37 -0
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +20 -0
- package/corpus/templates/factory/actions/get-triage-item.ts +25 -4
- package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +55 -0
- package/corpus/templates/factory/actions/ingest-github-observation.ts +21 -0
- package/corpus/templates/factory/actions/list-factory-audit.ts +118 -0
- package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
- package/corpus/templates/factory/actions/list-triage-items.ts +24 -2
- package/corpus/templates/factory/actions/poll-github-sources.ts +60 -0
- package/corpus/templates/factory/actions/poll-sentry-errors.ts +39 -0
- package/corpus/templates/factory/actions/poll-slack-channel.ts +45 -1
- package/corpus/templates/factory/actions/run-factory-automation.ts +1 -1
- package/corpus/templates/factory/actions/save-factory-automation.ts +2 -2
- package/corpus/templates/factory/actions/start-builder-for-item.ts +51 -0
- package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +685 -0
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +6 -0
- package/corpus/templates/factory/app/i18n/en-US.ts +25 -0
- package/corpus/templates/factory/app/routes/factory.tsx +95 -42
- package/corpus/templates/factory/server/db/schema.ts +37 -0
- package/corpus/templates/factory/server/lib/require-factory-automation.ts +5 -1
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +29 -0
- package/corpus/templates/factory/server/triage/audit.ts +79 -0
- package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/forms/app/i18n/index.ts +14 -29
- package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/macros/app/i18n/index.ts +14 -29
- package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/mail/app/i18n/index.ts +14 -29
- package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +7 -7
- package/corpus/templates/plan/app/i18n/index.ts +14 -29
- package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/slides/actions/analyze-brand-assets.ts +15 -15
- package/corpus/templates/slides/actions/import-code.ts +2 -43
- package/corpus/templates/slides/app/i18n/index.ts +14 -29
- package/corpus/templates/slides/server/routes/api/preview-images.get.ts +4 -1
- package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/tasks/app/components/layout/HeaderActions.tsx +1 -84
- package/corpus/templates/tasks/app/hooks/use-mobile.tsx +1 -21
- package/corpus/templates/tasks/app/i18n/index.ts +14 -29
- package/dist/agent/engine/ai-sdk-engine.js +2 -1
- package/dist/agent/engine/openai-compatible-endpoint.d.ts +2 -0
- package/dist/agent/engine/openai-compatible-endpoint.js +5 -1
- package/dist/agent/engine/provider-endpoint-validation.d.ts +9 -0
- package/dist/agent/engine/provider-endpoint-validation.js +26 -0
- package/dist/agent/engine/provider-env-vars.d.ts +1 -1
- package/dist/agent/engine/provider-env-vars.js +1 -1
- package/dist/agent/engine/registry.d.ts +8 -6
- package/dist/agent/engine/registry.js +37 -24
- package/dist/agent/tool-search.d.ts +1 -0
- package/dist/agent/tool-search.js +17 -1
- package/dist/automations/service.js +4 -10
- package/dist/brand-kit/brand-signals.d.ts +13 -0
- package/dist/brand-kit/brand-signals.js +15 -0
- package/dist/brand-kit/index.d.ts +2 -2
- package/dist/brand-kit/index.js +1 -1
- package/dist/cli/plan-blocks.d.ts +3 -18
- package/dist/cli/plan-blocks.js +2 -95
- package/dist/cli/plan-publish-store.d.ts +2 -61
- package/dist/cli/plan-publish-store.js +2 -127
- package/dist/cli/skills-content/connection.d.ts +1 -1
- package/dist/cli/skills-content/connection.js +1 -52
- package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
- package/dist/cli/skills-content/visual-recap-skill.js +1 -553
- package/dist/client/agent-engine-key.d.ts +14 -4
- package/dist/client/agent-engine-key.js +51 -5
- package/dist/client/agent-provider-catalog.d.ts +20 -0
- package/dist/client/agent-provider-catalog.js +100 -0
- package/dist/client/blocks/index.d.ts +1 -0
- package/dist/client/blocks/index.js +1 -0
- package/dist/client/chat/run-recovery.js +4 -48
- package/dist/client/chat-first.js +4 -3
- package/dist/client/i18n.d.ts +14 -0
- package/dist/client/i18n.js +18 -0
- package/dist/client/settings/ProviderSetupForm.d.ts +21 -0
- package/dist/client/settings/ProviderSetupForm.js +145 -0
- package/dist/client/settings/SettingsPanel.js +52 -49
- package/dist/client/settings/index.d.ts +2 -0
- package/dist/client/settings/index.js +2 -0
- package/dist/coding-tools/run-code.d.ts +5 -0
- package/dist/coding-tools/run-code.js +196 -36
- package/dist/coding-tools/tool-orchestration.d.ts +10 -0
- package/dist/coding-tools/tool-orchestration.js +107 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/jobs/frontmatter.d.ts +9 -0
- package/dist/jobs/frontmatter.js +16 -0
- package/dist/jobs/scheduler.js +8 -8
- package/dist/jobs/tools.d.ts +1 -1
- package/dist/jobs/tools.js +2 -7
- package/dist/localization/default-messages.d.ts +20 -0
- package/dist/localization/default-messages.js +25 -5
- package/dist/observability/routes.d.ts +3 -3
- package/dist/onboarding/app-profile.js +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/agent-engines/test-agent-engine.js +34 -10
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat/run-code-tools.d.ts +1 -1
- package/dist/server/agent-chat/run-code-tools.js +3 -1
- package/dist/server/agent-chat-plugin.js +6 -6
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.js +43 -12
- package/dist/server/agents-bundle.js +2 -1
- package/dist/server/auth.js +21 -5
- package/dist/server/credential-provider.js +1 -0
- package/dist/server/design-token-utils.d.ts +18 -0
- package/dist/server/design-token-utils.js +31 -0
- package/dist/server/onboarding-html.js +22 -2
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
- package/dist/templates/chat/app/i18n/index.ts +14 -29
- package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
- package/dist/templates/default/app/i18n/index.ts +14 -29
- package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
- package/dist/triggers/dispatcher.js +1 -8
- package/docs/content/environment-variables.mdx +39 -38
- package/package.json +4 -4
- package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
- package/src/templates/chat/app/i18n/index.ts +14 -29
- package/src/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
- package/src/templates/default/app/i18n/index.ts +14 -29
- package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/plan/app/components/plan/wireframe/sanitize-html.ts +0 -343
|
@@ -4,7 +4,6 @@ import { openBuilderConnectPopup } from "@agent-native/core/client/settings";
|
|
|
4
4
|
import {
|
|
5
5
|
IconCheck,
|
|
6
6
|
IconCloud,
|
|
7
|
-
IconExternalLink,
|
|
8
7
|
IconLoader2,
|
|
9
8
|
IconServer,
|
|
10
9
|
} from "@tabler/icons-react";
|
|
@@ -173,13 +172,20 @@ export function StorageSetupCard({
|
|
|
173
172
|
onClick={handleConnect}
|
|
174
173
|
disabled={connecting || connected}
|
|
175
174
|
className={
|
|
176
|
-
"flex items-start gap-3 rounded-xl border px-4 py-3.5 text-start " +
|
|
175
|
+
"flex items-start gap-3 rounded-xl border px-4 py-3.5 text-start transition-colors " +
|
|
177
176
|
(connected
|
|
178
177
|
? "border-primary/50 bg-primary/5"
|
|
179
|
-
: "border-
|
|
178
|
+
: "border-primary bg-primary text-primary-foreground hover:bg-primary/90")
|
|
180
179
|
}
|
|
181
180
|
>
|
|
182
|
-
<div
|
|
181
|
+
<div
|
|
182
|
+
className={
|
|
183
|
+
"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg " +
|
|
184
|
+
(connected
|
|
185
|
+
? "bg-foreground text-background"
|
|
186
|
+
: "bg-primary-foreground/15 text-primary-foreground")
|
|
187
|
+
}
|
|
188
|
+
>
|
|
183
189
|
{connected ? (
|
|
184
190
|
<IconCheck className="h-5 w-5" />
|
|
185
191
|
) : connecting ? (
|
|
@@ -197,16 +203,15 @@ export function StorageSetupCard({
|
|
|
197
203
|
? t("storageSetup.waitingForBuilder")
|
|
198
204
|
: t("storageSetup.connectBuilder")}
|
|
199
205
|
</span>
|
|
200
|
-
{!connected && !connecting && (
|
|
201
|
-
<>
|
|
202
|
-
<span className="rounded-sm bg-primary/15 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-primary">
|
|
203
|
-
{t("storageSetup.free")}
|
|
204
|
-
</span>
|
|
205
|
-
<IconExternalLink className="h-3.5 w-3.5 text-muted-foreground" />
|
|
206
|
-
</>
|
|
207
|
-
)}
|
|
208
206
|
</div>
|
|
209
|
-
<span
|
|
207
|
+
<span
|
|
208
|
+
className={
|
|
209
|
+
"mt-0.5 block text-xs " +
|
|
210
|
+
(connected
|
|
211
|
+
? "text-muted-foreground"
|
|
212
|
+
: "text-primary-foreground/80")
|
|
213
|
+
}
|
|
214
|
+
>
|
|
210
215
|
{connected ? connectedDescription : connectDescription}
|
|
211
216
|
</span>
|
|
212
217
|
</div>
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import { agentNativePath } from "@agent-native/core/client/api-path";
|
|
2
1
|
import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
3
2
|
import { useT } from "@agent-native/core/client/i18n";
|
|
3
|
+
import {
|
|
4
|
+
AGENT_PROVIDER_CATALOG,
|
|
5
|
+
AgentProviderSetupForm,
|
|
6
|
+
SettingsGroup,
|
|
7
|
+
SettingsRow,
|
|
8
|
+
type AgentProviderId,
|
|
9
|
+
} from "@agent-native/core/client/settings";
|
|
4
10
|
import {
|
|
5
11
|
BUILDER_CREDITS_UPGRADE_URL,
|
|
6
12
|
type BuilderCreditsStatus,
|
|
7
13
|
} from "@shared/builder-credits";
|
|
8
14
|
import {
|
|
9
15
|
IconBolt,
|
|
10
|
-
IconBrain,
|
|
11
16
|
IconCheck,
|
|
12
|
-
IconChevronDown,
|
|
13
17
|
IconExternalLink,
|
|
14
18
|
IconKey,
|
|
15
19
|
IconLoader2,
|
|
@@ -17,142 +21,16 @@ import {
|
|
|
17
21
|
import { useState } from "react";
|
|
18
22
|
import { toast } from "sonner";
|
|
19
23
|
|
|
20
|
-
import { Badge } from "@/components/ui/badge";
|
|
21
24
|
import { Button } from "@/components/ui/button";
|
|
22
|
-
import {
|
|
23
|
-
Card,
|
|
24
|
-
CardContent,
|
|
25
|
-
CardDescription,
|
|
26
|
-
CardHeader,
|
|
27
|
-
CardTitle,
|
|
28
|
-
} from "@/components/ui/card";
|
|
29
25
|
import {
|
|
30
26
|
Collapsible,
|
|
31
27
|
CollapsibleContent,
|
|
32
28
|
CollapsibleTrigger,
|
|
33
29
|
} from "@/components/ui/collapsible";
|
|
34
|
-
import { Input } from "@/components/ui/input";
|
|
35
|
-
import { Label } from "@/components/ui/label";
|
|
36
30
|
import type { SecretStatus } from "@/hooks/use-secret-status";
|
|
37
|
-
import { cn } from "@/lib/utils";
|
|
38
31
|
|
|
39
32
|
import type { BuilderConnection } from "./types";
|
|
40
33
|
|
|
41
|
-
const BUILDER_CREDITS_FEATURE_LABELS = [
|
|
42
|
-
"builderCredits.featureBackupTranscription",
|
|
43
|
-
"builderCredits.featureCleanup",
|
|
44
|
-
"builderCredits.featureSummaries",
|
|
45
|
-
"builderCredits.featureTitles",
|
|
46
|
-
] as const;
|
|
47
|
-
|
|
48
|
-
const AI_PROVIDER_FIELDS = [
|
|
49
|
-
{
|
|
50
|
-
key: "ANTHROPIC_API_KEY",
|
|
51
|
-
label: "Anthropic",
|
|
52
|
-
placeholder: "sk-ant-...",
|
|
53
|
-
storage: "agent-engine",
|
|
54
|
-
engine: "anthropic",
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
key: "OPENAI_API_KEY",
|
|
58
|
-
label: "OpenAI",
|
|
59
|
-
placeholder: "sk-...",
|
|
60
|
-
storage: "agent-engine",
|
|
61
|
-
engine: "ai-sdk:openai",
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
key: "GEMINI_API_KEY",
|
|
65
|
-
label: "Gemini",
|
|
66
|
-
placeholder: "AI...",
|
|
67
|
-
storage: "secret",
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
key: "GROQ_API_KEY",
|
|
71
|
-
label: "Groq",
|
|
72
|
-
placeholder: "gsk_...",
|
|
73
|
-
storage: "secret",
|
|
74
|
-
engine: "ai-sdk:groq",
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
key: "OPENROUTER_API_KEY",
|
|
78
|
-
label: "OpenRouter",
|
|
79
|
-
placeholder: "sk-or-...",
|
|
80
|
-
storage: "agent-engine",
|
|
81
|
-
engine: "ai-sdk:openrouter",
|
|
82
|
-
},
|
|
83
|
-
] as const;
|
|
84
|
-
|
|
85
|
-
async function saveAgentEngineApiKey(
|
|
86
|
-
key: string,
|
|
87
|
-
value: string,
|
|
88
|
-
): Promise<void> {
|
|
89
|
-
const res = await fetch(
|
|
90
|
-
agentNativePath("/_agent-native/agent-engine/api-key"),
|
|
91
|
-
{
|
|
92
|
-
method: "POST",
|
|
93
|
-
headers: { "Content-Type": "application/json" },
|
|
94
|
-
body: JSON.stringify({ key, value, scope: "user" }),
|
|
95
|
-
},
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
if (!res.ok) {
|
|
99
|
-
// coercion-ok: an error body may not be JSON; the failure is still raised with the status code.
|
|
100
|
-
const body = (await res.json().catch(() => null)) as {
|
|
101
|
-
error?: string;
|
|
102
|
-
} | null;
|
|
103
|
-
throw new Error(body?.error ?? `Save failed (${res.status})`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async function applyAgentEngine(engine: string): Promise<void> {
|
|
108
|
-
const res = await fetch(
|
|
109
|
-
agentNativePath("/_agent-native/actions/manage-agent-engine"),
|
|
110
|
-
{
|
|
111
|
-
method: "POST",
|
|
112
|
-
headers: { "Content-Type": "application/json" },
|
|
113
|
-
body: JSON.stringify({ action: "set", engine }),
|
|
114
|
-
},
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
// coercion-ok: an error body may not be JSON; the failure is still raised with the status code.
|
|
118
|
-
const body = (await res.json().catch(() => null)) as {
|
|
119
|
-
error?: string;
|
|
120
|
-
result?: unknown;
|
|
121
|
-
} | null;
|
|
122
|
-
if (!res.ok) {
|
|
123
|
-
throw new Error(body?.error ?? `Engine switch failed (${res.status})`);
|
|
124
|
-
}
|
|
125
|
-
const result = body?.result ?? body;
|
|
126
|
-
const text =
|
|
127
|
-
typeof result === "string"
|
|
128
|
-
? result.trim()
|
|
129
|
-
: result && typeof result === "object"
|
|
130
|
-
? JSON.stringify(result)
|
|
131
|
-
: "";
|
|
132
|
-
if (/^(Error|Warning):/i.test(text)) {
|
|
133
|
-
throw new Error(text);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async function saveRegisteredSecret(key: string, value: string): Promise<void> {
|
|
138
|
-
const res = await fetch(
|
|
139
|
-
agentNativePath(`/_agent-native/secrets/${encodeURIComponent(key)}`),
|
|
140
|
-
{
|
|
141
|
-
method: "POST",
|
|
142
|
-
headers: { "Content-Type": "application/json" },
|
|
143
|
-
body: JSON.stringify({ value }),
|
|
144
|
-
},
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
if (!res.ok) {
|
|
148
|
-
// coercion-ok: an error body may not be JSON; the failure is still raised with the status code.
|
|
149
|
-
const body = (await res.json().catch(() => null)) as {
|
|
150
|
-
error?: string;
|
|
151
|
-
} | null;
|
|
152
|
-
throw new Error(body?.error ?? `Save failed (${res.status})`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
34
|
export interface AiSetupSectionProps {
|
|
157
35
|
builder: BuilderConnection;
|
|
158
36
|
secrets: SecretStatus;
|
|
@@ -166,276 +44,115 @@ export function AiSetupSection({ builder, secrets }: AiSetupSectionProps) {
|
|
|
166
44
|
{ retry: false },
|
|
167
45
|
);
|
|
168
46
|
const [expanded, setExpanded] = useState(false);
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
47
|
+
const configuredProviders = new Set<AgentProviderId>(
|
|
48
|
+
AGENT_PROVIDER_CATALOG.filter(
|
|
49
|
+
(provider) =>
|
|
50
|
+
(provider.key && secrets.configured[provider.key]) ||
|
|
51
|
+
(provider.endpointKey && secrets.configured[provider.endpointKey]),
|
|
52
|
+
).map((provider) => provider.id),
|
|
53
|
+
);
|
|
54
|
+
const configuredCount = configuredProviders.size;
|
|
175
55
|
const creditsPaused = creditStatus.data?.exhausted === true;
|
|
176
56
|
const upgradeUrl =
|
|
177
57
|
creditStatus.data?.upgradeUrl ?? BUILDER_CREDITS_UPGRADE_URL;
|
|
178
58
|
|
|
179
|
-
async function handleSaveApiKey(key: string) {
|
|
180
|
-
const value = (values[key] ?? "").trim();
|
|
181
|
-
if (!value) {
|
|
182
|
-
toast.error(t("settings.pasteProviderKey"));
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
setSavingKey(key);
|
|
187
|
-
try {
|
|
188
|
-
const field = AI_PROVIDER_FIELDS.find((item) => item.key === key);
|
|
189
|
-
if (field?.storage === "secret") {
|
|
190
|
-
await saveRegisteredSecret(key, value);
|
|
191
|
-
} else {
|
|
192
|
-
await saveAgentEngineApiKey(key, value);
|
|
193
|
-
}
|
|
194
|
-
if (field && "engine" in field) {
|
|
195
|
-
await applyAgentEngine(field.engine);
|
|
196
|
-
}
|
|
197
|
-
setValues((current) => ({ ...current, [key]: "" }));
|
|
198
|
-
window.dispatchEvent(new CustomEvent("agent-engine:configured-changed"));
|
|
199
|
-
await secrets.refresh();
|
|
200
|
-
toast.success(t("settings.apiKeySaved"));
|
|
201
|
-
} catch (err) {
|
|
202
|
-
toast.error(
|
|
203
|
-
err instanceof Error ? err.message : t("settings.apiKeyFailed"),
|
|
204
|
-
);
|
|
205
|
-
} finally {
|
|
206
|
-
setSavingKey(null);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
59
|
function openProviderSetup() {
|
|
211
60
|
setExpanded(true);
|
|
212
61
|
window.requestAnimationFrame(() => {
|
|
213
62
|
document
|
|
214
63
|
.getElementById("ai-provider-keys")
|
|
215
64
|
?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
216
|
-
const firstEmptyField =
|
|
217
|
-
AI_PROVIDER_FIELDS.find((field) => !secrets.configured[field.key]) ??
|
|
218
|
-
AI_PROVIDER_FIELDS[0];
|
|
219
|
-
window.setTimeout(() => {
|
|
220
|
-
document.getElementById(firstEmptyField.key)?.focus();
|
|
221
|
-
}, 150);
|
|
222
65
|
});
|
|
223
66
|
}
|
|
224
67
|
|
|
225
68
|
return (
|
|
226
|
-
<
|
|
227
|
-
|
|
228
|
-
<
|
|
229
|
-
<
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
<CardDescription>{t("settings.apiSetupDescription")}</CardDescription>
|
|
233
|
-
</CardHeader>
|
|
234
|
-
<CardContent className="space-y-4">
|
|
235
|
-
<div
|
|
236
|
-
className={cn(
|
|
237
|
-
"flex flex-col gap-3 rounded-md border px-3 py-3 sm:flex-row sm:items-center sm:justify-between",
|
|
238
|
-
builder.connected
|
|
239
|
-
? "border-border bg-muted/20"
|
|
240
|
-
: "border-border bg-accent/30",
|
|
241
|
-
)}
|
|
242
|
-
>
|
|
243
|
-
<div className="min-w-0">
|
|
244
|
-
<div className="flex items-center gap-2 text-sm font-medium">
|
|
245
|
-
{builder.connected ? (
|
|
246
|
-
<IconCheck className="h-4 w-4 text-primary" />
|
|
247
|
-
) : (
|
|
248
|
-
<IconKey className="h-4 w-4 text-muted-foreground" />
|
|
249
|
-
)}
|
|
250
|
-
{builder.loading
|
|
251
|
-
? t("settings.checkingBuilder")
|
|
252
|
-
: builder.connected
|
|
253
|
-
? t("settings.builderAiAvailable")
|
|
254
|
-
: t("settings.builderEasySetup")}
|
|
255
|
-
</div>
|
|
256
|
-
<p className="mt-0.5 text-xs text-muted-foreground">
|
|
257
|
-
{builder.connected
|
|
258
|
-
? t("settings.apiSetupDescription")
|
|
259
|
-
: t("settings.builderAiDescription")}
|
|
260
|
-
</p>
|
|
69
|
+
<SettingsGroup id="ai-providers" title={t("settings.apiSetup")}>
|
|
70
|
+
{creditsPaused ? (
|
|
71
|
+
<div className="flex items-center justify-between gap-3 px-5 py-3 text-amber-950 dark:text-amber-100">
|
|
72
|
+
<div className="flex items-center gap-2 text-sm font-medium">
|
|
73
|
+
<IconBolt className="h-4 w-4 text-amber-700 dark:text-amber-200" />
|
|
74
|
+
{t("builderCredits.pausedTitle")}
|
|
261
75
|
</div>
|
|
262
|
-
|
|
76
|
+
<div className="flex shrink-0 gap-2">
|
|
77
|
+
<Button asChild size="sm" className="h-8">
|
|
78
|
+
<a href={upgradeUrl} target="_blank" rel="noopener noreferrer">
|
|
79
|
+
<IconExternalLink className="h-4 w-4" />
|
|
80
|
+
{t("builderCredits.upgrade")}
|
|
81
|
+
</a>
|
|
82
|
+
</Button>
|
|
263
83
|
<Button
|
|
264
84
|
type="button"
|
|
265
85
|
variant="outline"
|
|
266
86
|
size="sm"
|
|
267
|
-
className="
|
|
268
|
-
onClick={
|
|
269
|
-
builder.start({
|
|
270
|
-
trackingSource: "clips_settings_ai_setup",
|
|
271
|
-
trackingFlow: "connect_llm",
|
|
272
|
-
})
|
|
273
|
-
}
|
|
274
|
-
disabled={builder.connecting || builder.loading}
|
|
87
|
+
className="h-8 border-amber-300/80 bg-background/70 text-amber-950 hover:bg-amber-100 dark:border-amber-400/40 dark:bg-amber-950/30 dark:text-amber-100 dark:hover:bg-amber-900/40"
|
|
88
|
+
onClick={openProviderSetup}
|
|
275
89
|
>
|
|
276
|
-
{
|
|
277
|
-
<IconLoader2 className="h-4 w-4 animate-spin" />
|
|
278
|
-
) : (
|
|
279
|
-
<IconExternalLink className="h-4 w-4" />
|
|
280
|
-
)}
|
|
281
|
-
{t("settings.connectBuilder")}
|
|
90
|
+
{t("builderCredits.openAiSetup")}
|
|
282
91
|
</Button>
|
|
283
|
-
|
|
92
|
+
</div>
|
|
284
93
|
</div>
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
300
|
-
{BUILDER_CREDITS_FEATURE_LABELS.map((key) => (
|
|
301
|
-
<span
|
|
302
|
-
key={key}
|
|
303
|
-
className="rounded-full border border-amber-300/70 bg-background/70 px-2 py-0.5 text-[11px] font-medium text-amber-900 dark:border-amber-400/30 dark:bg-amber-950/30 dark:text-amber-100"
|
|
304
|
-
>
|
|
305
|
-
{t(key)}
|
|
306
|
-
</span>
|
|
307
|
-
))}
|
|
308
|
-
</div>
|
|
309
|
-
</div>
|
|
310
|
-
<div className="flex shrink-0 flex-wrap gap-2">
|
|
311
|
-
<Button asChild size="sm" className="h-8">
|
|
312
|
-
<a
|
|
313
|
-
href={upgradeUrl}
|
|
314
|
-
target="_blank"
|
|
315
|
-
rel="noopener noreferrer"
|
|
316
|
-
>
|
|
317
|
-
<IconExternalLink className="h-4 w-4" />
|
|
318
|
-
{t("builderCredits.upgrade")}
|
|
319
|
-
</a>
|
|
320
|
-
</Button>
|
|
94
|
+
) : null}
|
|
95
|
+
|
|
96
|
+
<Collapsible open={expanded} onOpenChange={setExpanded}>
|
|
97
|
+
<SettingsRow
|
|
98
|
+
label={t("settings.providerActionTitle")}
|
|
99
|
+
description={t("settings.providerActionDescription")}
|
|
100
|
+
control={
|
|
101
|
+
<div className="flex flex-wrap items-center justify-end gap-2">
|
|
102
|
+
{builder.connected ? (
|
|
103
|
+
<span className="inline-flex items-center gap-1.5 text-sm font-medium text-primary">
|
|
104
|
+
<IconCheck className="h-4 w-4" />
|
|
105
|
+
Builder.io
|
|
106
|
+
</span>
|
|
107
|
+
) : (
|
|
321
108
|
<Button
|
|
322
109
|
type="button"
|
|
323
|
-
variant="
|
|
110
|
+
variant="default"
|
|
324
111
|
size="sm"
|
|
325
|
-
|
|
326
|
-
|
|
112
|
+
onClick={() =>
|
|
113
|
+
builder.start({
|
|
114
|
+
trackingSource: "clips_settings_ai_setup",
|
|
115
|
+
trackingFlow: "connect_llm",
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
disabled={builder.connecting || builder.loading}
|
|
327
119
|
>
|
|
328
|
-
{
|
|
120
|
+
{builder.connecting ? (
|
|
121
|
+
<IconLoader2 className="h-4 w-4 animate-spin" />
|
|
122
|
+
) : null}
|
|
123
|
+
{t("settings.connectBuilder")}
|
|
329
124
|
</Button>
|
|
330
|
-
|
|
125
|
+
)}
|
|
126
|
+
<CollapsibleTrigger asChild>
|
|
127
|
+
<Button type="button" variant="outline" size="sm">
|
|
128
|
+
{configuredCount > 0
|
|
129
|
+
? t("settings.providerManage")
|
|
130
|
+
: t("settings.providerCustomKeys")}
|
|
131
|
+
</Button>
|
|
132
|
+
</CollapsibleTrigger>
|
|
331
133
|
</div>
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
id="ai-provider-keys"
|
|
340
|
-
type="button"
|
|
341
|
-
className="flex w-full items-center justify-between gap-3 px-3 py-3 text-start"
|
|
342
|
-
>
|
|
343
|
-
<div className="min-w-0">
|
|
344
|
-
<div className="flex items-center gap-2 text-sm font-medium">
|
|
345
|
-
<IconKey className="h-4 w-4 text-muted-foreground" />
|
|
346
|
-
{t("settings.providerKeyTitle")}
|
|
347
|
-
{configuredCount > 0 ? (
|
|
348
|
-
<Badge variant="secondary" className="text-[10px]">
|
|
349
|
-
{t("settings.providerKeysSet", {
|
|
350
|
-
count: configuredCount,
|
|
351
|
-
})}
|
|
352
|
-
</Badge>
|
|
353
|
-
) : null}
|
|
354
|
-
</div>
|
|
355
|
-
<p className="mt-0.5 text-xs text-muted-foreground">
|
|
356
|
-
{t("settings.providerKeyDescription")}
|
|
357
|
-
</p>
|
|
358
|
-
</div>
|
|
359
|
-
<IconChevronDown
|
|
360
|
-
className={cn(
|
|
361
|
-
"h-4 w-4 shrink-0 text-muted-foreground transition-transform",
|
|
362
|
-
expanded && "rotate-180",
|
|
363
|
-
)}
|
|
364
|
-
/>
|
|
365
|
-
</button>
|
|
366
|
-
</CollapsibleTrigger>
|
|
367
|
-
<CollapsibleContent>
|
|
368
|
-
<div className="space-y-3 border-t border-border px-3 py-4">
|
|
369
|
-
{secrets.loading ? (
|
|
370
|
-
<div className="text-xs text-muted-foreground">
|
|
371
|
-
{t("settings.checkingProviderKeys")}
|
|
372
|
-
</div>
|
|
373
|
-
) : null}
|
|
374
|
-
<div className="grid gap-3 sm:grid-cols-2">
|
|
375
|
-
{AI_PROVIDER_FIELDS.map((field) => {
|
|
376
|
-
const configured = Boolean(secrets.configured[field.key]);
|
|
377
|
-
const savingThisKey = savingKey === field.key;
|
|
378
|
-
return (
|
|
379
|
-
<div key={field.key} className="space-y-1.5">
|
|
380
|
-
<div className="flex items-center justify-between gap-2">
|
|
381
|
-
<Label htmlFor={field.key}>{field.label}</Label>
|
|
382
|
-
{configured ? (
|
|
383
|
-
<span className="flex items-center gap-1 text-[10px] font-medium text-primary">
|
|
384
|
-
<IconCheck className="h-3 w-3" />
|
|
385
|
-
{t("settings.keySet")}
|
|
386
|
-
</span>
|
|
387
|
-
) : null}
|
|
388
|
-
</div>
|
|
389
|
-
<div className="flex gap-2">
|
|
390
|
-
<Input
|
|
391
|
-
id={field.key}
|
|
392
|
-
type="password"
|
|
393
|
-
value={values[field.key] ?? ""}
|
|
394
|
-
onChange={(event) =>
|
|
395
|
-
setValues((current) => ({
|
|
396
|
-
...current,
|
|
397
|
-
[field.key]: event.target.value,
|
|
398
|
-
}))
|
|
399
|
-
}
|
|
400
|
-
onKeyDown={(event) => {
|
|
401
|
-
if (event.key === "Enter") {
|
|
402
|
-
void handleSaveApiKey(field.key);
|
|
403
|
-
}
|
|
404
|
-
}}
|
|
405
|
-
placeholder={
|
|
406
|
-
configured
|
|
407
|
-
? t("settings.replaceKey")
|
|
408
|
-
: field.placeholder
|
|
409
|
-
}
|
|
410
|
-
autoComplete="off"
|
|
411
|
-
disabled={savingThisKey}
|
|
412
|
-
/>
|
|
413
|
-
<Button
|
|
414
|
-
type="button"
|
|
415
|
-
variant="outline"
|
|
416
|
-
size="sm"
|
|
417
|
-
className="shrink-0"
|
|
418
|
-
onClick={() => handleSaveApiKey(field.key)}
|
|
419
|
-
disabled={
|
|
420
|
-
savingThisKey || !(values[field.key] ?? "").trim()
|
|
421
|
-
}
|
|
422
|
-
>
|
|
423
|
-
{savingThisKey ? (
|
|
424
|
-
<IconLoader2 className="h-4 w-4 animate-spin" />
|
|
425
|
-
) : (
|
|
426
|
-
t("common.save")
|
|
427
|
-
)}
|
|
428
|
-
</Button>
|
|
429
|
-
</div>
|
|
430
|
-
</div>
|
|
431
|
-
);
|
|
432
|
-
})}
|
|
433
|
-
</div>
|
|
134
|
+
}
|
|
135
|
+
/>
|
|
136
|
+
<CollapsibleContent>
|
|
137
|
+
<div className="space-y-3 border-t border-border px-5 py-4">
|
|
138
|
+
{secrets.loading ? (
|
|
139
|
+
<div className="text-xs text-muted-foreground">
|
|
140
|
+
{t("settings.checkingProviderKeys")}
|
|
434
141
|
</div>
|
|
435
|
-
|
|
142
|
+
) : null}
|
|
143
|
+
<AgentProviderSetupForm
|
|
144
|
+
initialProvider="openrouter"
|
|
145
|
+
configuredProviders={configuredProviders}
|
|
146
|
+
layout="page"
|
|
147
|
+
showTitle={false}
|
|
148
|
+
onConnected={() => {
|
|
149
|
+
void secrets.refresh();
|
|
150
|
+
toast.success(t("settings.apiKeySaved"));
|
|
151
|
+
}}
|
|
152
|
+
/>
|
|
436
153
|
</div>
|
|
437
|
-
</
|
|
438
|
-
</
|
|
439
|
-
</
|
|
154
|
+
</CollapsibleContent>
|
|
155
|
+
</Collapsible>
|
|
156
|
+
</SettingsGroup>
|
|
440
157
|
);
|
|
441
158
|
}
|