@agent-native/core 0.98.0 → 0.98.1
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/core/CHANGELOG.md +24 -0
- package/corpus/core/package.json +2 -2
- package/corpus/core/src/action-change-marker.ts +8 -1
- package/corpus/core/src/changelog/parse.ts +8 -3
- package/corpus/core/src/client/AgentPanel.tsx +2 -3
- package/corpus/core/src/client/AssistantChat.tsx +83 -39
- package/corpus/core/src/client/analytics.ts +29 -0
- package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +16 -2
- package/corpus/core/src/client/chat-model-groups.ts +28 -0
- package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +442 -85
- package/corpus/core/src/client/composer/TiptapComposer.tsx +46 -29
- package/corpus/core/src/client/composer/VoiceButton.tsx +2 -1
- package/corpus/core/src/client/composer/index.ts +5 -0
- package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +1206 -67
- package/corpus/core/src/client/index.ts +5 -0
- package/corpus/core/src/client/progress/RunsTray.tsx +13 -1
- package/corpus/core/src/client/settings/SecretsSection.tsx +387 -266
- package/corpus/core/src/client/settings/SettingsPanel.tsx +117 -38
- package/corpus/core/src/client/use-action.ts +12 -0
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +8 -1
- package/corpus/core/src/client/use-agent-engine-configured.ts +17 -3
- package/corpus/core/src/client/use-db-sync.ts +84 -34
- package/corpus/core/src/client/use-pausing-interval.ts +14 -4
- package/corpus/core/src/integrations/scope-store.ts +1 -1
- package/corpus/core/src/integrations/usage-budget-store.ts +1 -1
- package/corpus/core/src/localization/default-messages.ts +44 -1
- package/corpus/core/src/mcp-client/workspace-servers.ts +4 -4
- package/corpus/core/src/secrets/crypto.ts +28 -8
- package/corpus/core/src/server/action-change.ts +5 -1
- package/corpus/core/src/server/action-routes.ts +8 -0
- package/corpus/core/src/server/poll.ts +1 -0
- package/corpus/core/src/server/realtime-voice.ts +299 -13
- package/corpus/core/src/styles/agent-native.css +2 -2
- package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
- package/corpus/core/src/vite/client.ts +9 -1
- package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +10 -5
- package/corpus/templates/analytics/AGENTS.md +9 -13
- package/corpus/templates/analytics/actions/navigate.ts +59 -5
- package/corpus/templates/analytics/actions/view-screen.ts +3 -6
- package/corpus/templates/analytics/app/components/ProviderCorpusJobNotifier.tsx +20 -3
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +2 -1
- package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -6
- package/corpus/templates/analytics/app/global.css +19 -20
- package/corpus/templates/analytics/app/lib/provider-corpus-job-sync.ts +26 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +35 -341
- package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +38 -7
- package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +1 -1
- package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +3 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +333 -272
- package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +72 -17
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +297 -108
- package/corpus/templates/analytics/app/root.tsx +28 -8
- package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +2 -1
- package/corpus/templates/analytics/changelog/2026-06-25-referrer-dashboard-panels-show-external-domains.md +2 -1
- package/corpus/templates/analytics/changelog/2026-07-06-mobile-chat-header-is-more-compact-and-the-menu-button-opens-navigation.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-08-dashboard-history-and-chat-context.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-08-settings-are-cleaner-searchable-and-alerts-have-their-own-tab.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-08-sidebar-sections-stay-collapsed-until-opened.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-11-analytics-no-longer-repeatedly-restarts-background-requests-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-analytics-only-asks-for-replay-storage-when-you-enable-sessi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-charts-now-show-their-loading-skeleton-while-refreshing.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-realtime-voice-now-shows-a-clear-connection-indicator-comple.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-replay-event-counts-stay-hidden-until-recordings-finish-load.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-replay-storage-key-mismatches-show-actionable-setup-guidance.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-replay-timelines-group-continuous-scrolling-and-dev-tools-co.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-filters-now-stay-compact-in-a-settings-menu-at-mediu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replay-console-errors-now-link-to-matching-issues-or.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replays-load-large-recordings-faster-and-surface-fai.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replays-preserve-recorded-styles-and-viewport-layout.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replays-stay-centered-instead-of-clipping-wide-recor.md +6 -0
- package/corpus/templates/analytics/package.json +1 -1
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +1 -303
- package/corpus/templates/analytics/server/handlers/session-replay.ts +57 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +1 -1
- package/corpus/templates/analytics/server/lib/demo-dashboards.ts +16 -3
- package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +12 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +208 -2
- package/corpus/templates/analytics/server/plugins/onboarding.ts +7 -79
- package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/chunks.get.ts +1 -0
- package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/assets/app/global.css +4 -4
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +0 -1
- package/corpus/templates/assets/app/routes/library.tsx +0 -1
- package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/brain/app/global.css +5 -5
- package/corpus/templates/brain/app/root.tsx +0 -3
- package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/calendar/app/root.tsx +1 -0
- package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +74 -8
- package/corpus/templates/clips/app/hooks/use-library.ts +12 -25
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +20 -22
- package/corpus/templates/clips/changelog/2026-07-11-generated-titles-no-longer-cause-continuous-background-refreshes.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-11-s3-storage-setup-stays-collapsed-until-you-choose-it-as-the-.md +6 -0
- package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/content/app/hooks/use-comments.ts +0 -1
- package/corpus/templates/content/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
- package/corpus/templates/design/app/components/layout/Header.tsx +1 -1
- package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +1 -2
- package/corpus/templates/macros/app/root.tsx +0 -3
- package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/mail/app/hooks/use-compose-state.ts +1 -0
- package/corpus/templates/mail/app/root.tsx +1 -12
- package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +5 -2
- package/corpus/templates/plan/app/hooks/use-plans.ts +6 -13
- package/corpus/templates/plan/app/pages/PlansPage.tsx +5 -27
- package/corpus/templates/plan/changelog/2026-07-11-plan-refreshes-only-when-content-changes.md +6 -0
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +4 -1
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +1 -1
- package/corpus/templates/slides/app/components/layout/Header.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +1 -0
- package/corpus/templates/slides/app/hooks/use-slide-comments.ts +7 -31
- package/corpus/templates/slides/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
- package/dist/action-change-marker.d.ts +1 -0
- package/dist/action-change-marker.d.ts.map +1 -1
- package/dist/action-change-marker.js +7 -1
- package/dist/action-change-marker.js.map +1 -1
- package/dist/changelog/parse.d.ts +4 -2
- package/dist/changelog/parse.d.ts.map +1 -1
- package/dist/changelog/parse.js +5 -3
- package/dist/changelog/parse.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +7 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +62 -40
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +26 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.d.ts +2 -0
- package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.js +9 -2
- package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +24 -0
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.d.ts +40 -4
- package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.js +172 -42
- package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +4 -0
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +47 -29
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/VoiceButton.d.ts.map +1 -1
- package/dist/client/composer/VoiceButton.js +2 -2
- package/dist/client/composer/VoiceButton.js.map +1 -1
- package/dist/client/composer/index.d.ts +2 -2
- package/dist/client/composer/index.d.ts.map +1 -1
- package/dist/client/composer/index.js +1 -1
- package/dist/client/composer/index.js.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.d.ts +100 -3
- package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.js +952 -51
- package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/progress/RunsTray.d.ts.map +1 -1
- package/dist/client/progress/RunsTray.js +10 -1
- package/dist/client/progress/RunsTray.js.map +1 -1
- package/dist/client/settings/SecretsSection.d.ts +2 -3
- package/dist/client/settings/SecretsSection.d.ts.map +1 -1
- package/dist/client/settings/SecretsSection.js +46 -22
- package/dist/client/settings/SecretsSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts +9 -0
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +46 -12
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/use-action.d.ts +2 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +10 -0
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +2 -0
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +7 -1
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
- package/dist/client/use-agent-engine-configured.js +17 -3
- package/dist/client/use-agent-engine-configured.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +8 -2
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +57 -32
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/client/use-pausing-interval.d.ts.map +1 -1
- package/dist/client/use-pausing-interval.js +16 -4
- package/dist/client/use-pausing-interval.js.map +1 -1
- package/dist/integrations/scope-store.js +1 -1
- package/dist/integrations/scope-store.js.map +1 -1
- package/dist/integrations/usage-budget-store.js +1 -1
- package/dist/integrations/usage-budget-store.js.map +1 -1
- package/dist/localization/default-messages.d.ts +41 -1
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +41 -1
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp-client/workspace-servers.js +4 -4
- package/dist/mcp-client/workspace-servers.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/secrets/crypto.d.ts +7 -6
- package/dist/secrets/crypto.d.ts.map +1 -1
- package/dist/secrets/crypto.js +30 -11
- package/dist/secrets/crypto.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-change.d.ts +1 -0
- package/dist/server/action-change.d.ts.map +1 -1
- package/dist/server/action-change.js +4 -1
- package/dist/server/action-change.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +5 -0
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +1 -0
- package/dist/server/poll.js.map +1 -1
- package/dist/server/realtime-voice.d.ts +7 -0
- package/dist/server/realtime-voice.d.ts.map +1 -1
- package/dist/server/realtime-voice.js +222 -12
- package/dist/server/realtime-voice.js.map +1 -1
- package/dist/styles/agent-native.css +2 -2
- package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +6 -1
- package/dist/vite/client.js.map +1 -1
- package/package.json +2 -2
- package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* <SecretsSection /> — renders the registered secrets from the framework
|
|
3
|
-
* secrets registry.
|
|
4
|
-
*
|
|
5
|
-
* buttons (api-key kind) or a Connect / Disconnect button (oauth kind).
|
|
3
|
+
* secrets registry. Configured keys stay compact; adding or editing one
|
|
4
|
+
* progressively discloses its controls.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
import {
|
|
9
8
|
IconCheck,
|
|
9
|
+
IconChevronRight,
|
|
10
10
|
IconExternalLink,
|
|
11
11
|
IconLoader2,
|
|
12
12
|
IconPlugConnected,
|
|
@@ -17,6 +17,14 @@ import {
|
|
|
17
17
|
import React, { useEffect, useMemo, useState, useCallback } from "react";
|
|
18
18
|
|
|
19
19
|
import { agentNativePath } from "../api-path.js";
|
|
20
|
+
import {
|
|
21
|
+
DropdownMenu,
|
|
22
|
+
DropdownMenuContent,
|
|
23
|
+
DropdownMenuItem,
|
|
24
|
+
DropdownMenuLabel,
|
|
25
|
+
DropdownMenuSeparator,
|
|
26
|
+
DropdownMenuTrigger,
|
|
27
|
+
} from "../components/ui/dropdown-menu.js";
|
|
20
28
|
import {
|
|
21
29
|
Tooltip,
|
|
22
30
|
TooltipContent,
|
|
@@ -59,6 +67,10 @@ export function SecretsSection({ focusKey }: SecretsSectionProps) {
|
|
|
59
67
|
const [secrets, setSecrets] = useState<SecretStatus[] | null>(null);
|
|
60
68
|
const [error, setError] = useState<string | null>(null);
|
|
61
69
|
const [reloadToken, setReloadToken] = useState(0);
|
|
70
|
+
const [openSecretKey, setOpenSecretKey] = useState<string | null>(
|
|
71
|
+
focusKey ?? null,
|
|
72
|
+
);
|
|
73
|
+
const [customKeyOpen, setCustomKeyOpen] = useState(false);
|
|
62
74
|
|
|
63
75
|
useEffect(() => {
|
|
64
76
|
let cancelled = false;
|
|
@@ -82,6 +94,13 @@ export function SecretsSection({ focusKey }: SecretsSectionProps) {
|
|
|
82
94
|
|
|
83
95
|
const reload = useCallback(() => setReloadToken((t) => t + 1), []);
|
|
84
96
|
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (focusKey) {
|
|
99
|
+
setCustomKeyOpen(false);
|
|
100
|
+
setOpenSecretKey(focusKey);
|
|
101
|
+
}
|
|
102
|
+
}, [focusKey]);
|
|
103
|
+
|
|
85
104
|
if (error) {
|
|
86
105
|
return (
|
|
87
106
|
<p className="text-[10px] text-red-500">
|
|
@@ -99,27 +118,112 @@ export function SecretsSection({ focusKey }: SecretsSectionProps) {
|
|
|
99
118
|
}
|
|
100
119
|
if (secrets.length === 0) {
|
|
101
120
|
return (
|
|
102
|
-
<div className="space-y-
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
121
|
+
<div className="space-y-3">
|
|
122
|
+
<KeysHeader onCustomKey={() => setCustomKeyOpen(true)} />
|
|
123
|
+
<AdHocKeysSection
|
|
124
|
+
showForm={customKeyOpen}
|
|
125
|
+
onShowFormChange={setCustomKeyOpen}
|
|
126
|
+
showEmptyState
|
|
127
|
+
/>
|
|
108
128
|
</div>
|
|
109
129
|
);
|
|
110
130
|
}
|
|
111
131
|
|
|
132
|
+
const visibleSecrets = secrets.filter(
|
|
133
|
+
(secret) => secret.status !== "unset" || secret.key === openSecretKey,
|
|
134
|
+
);
|
|
135
|
+
const availableSecrets = secrets.filter(
|
|
136
|
+
(secret) => secret.status === "unset" && secret.key !== openSecretKey,
|
|
137
|
+
);
|
|
138
|
+
|
|
112
139
|
return (
|
|
113
|
-
<div className="space-y-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
140
|
+
<div className="space-y-3">
|
|
141
|
+
<KeysHeader
|
|
142
|
+
availableSecrets={availableSecrets}
|
|
143
|
+
onSecret={(key) => {
|
|
144
|
+
setCustomKeyOpen(false);
|
|
145
|
+
setOpenSecretKey(key);
|
|
146
|
+
}}
|
|
147
|
+
onCustomKey={() => {
|
|
148
|
+
setOpenSecretKey(null);
|
|
149
|
+
setCustomKeyOpen(true);
|
|
150
|
+
}}
|
|
151
|
+
/>
|
|
152
|
+
{visibleSecrets.length > 0 && (
|
|
153
|
+
<div className="overflow-hidden rounded-md border border-border">
|
|
154
|
+
{visibleSecrets.map((secret) => (
|
|
155
|
+
<SecretCard
|
|
156
|
+
key={secret.key}
|
|
157
|
+
secret={secret}
|
|
158
|
+
onChanged={reload}
|
|
159
|
+
open={openSecretKey === secret.key}
|
|
160
|
+
onOpenChange={(open) => {
|
|
161
|
+
if (open) setCustomKeyOpen(false);
|
|
162
|
+
setOpenSecretKey(open ? secret.key : null);
|
|
163
|
+
}}
|
|
164
|
+
focusInput={openSecretKey === secret.key}
|
|
165
|
+
/>
|
|
166
|
+
))}
|
|
167
|
+
</div>
|
|
168
|
+
)}
|
|
169
|
+
<AdHocKeysSection
|
|
170
|
+
showForm={customKeyOpen}
|
|
171
|
+
onShowFormChange={setCustomKeyOpen}
|
|
172
|
+
showEmptyState={visibleSecrets.length === 0}
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function KeysHeader({
|
|
179
|
+
availableSecrets = [],
|
|
180
|
+
onSecret,
|
|
181
|
+
onCustomKey,
|
|
182
|
+
}: {
|
|
183
|
+
availableSecrets?: SecretStatus[];
|
|
184
|
+
onSecret?: (key: string) => void;
|
|
185
|
+
onCustomKey: () => void;
|
|
186
|
+
}) {
|
|
187
|
+
return (
|
|
188
|
+
<div className="flex items-center justify-between gap-3">
|
|
189
|
+
<p className="text-[11px] font-medium text-foreground">Keys</p>
|
|
190
|
+
<DropdownMenu>
|
|
191
|
+
<DropdownMenuTrigger asChild>
|
|
192
|
+
<button
|
|
193
|
+
type="button"
|
|
194
|
+
className="inline-flex items-center gap-1 rounded-md border border-border px-2 py-1 text-[10px] font-medium text-muted-foreground transition-colors hover:bg-accent/40 hover:text-foreground"
|
|
195
|
+
>
|
|
196
|
+
<IconPlus size={11} />
|
|
197
|
+
New
|
|
198
|
+
</button>
|
|
199
|
+
</DropdownMenuTrigger>
|
|
200
|
+
<DropdownMenuContent align="end" className="w-60">
|
|
201
|
+
{availableSecrets.length > 0 && (
|
|
202
|
+
<>
|
|
203
|
+
<DropdownMenuLabel>Choose a key</DropdownMenuLabel>
|
|
204
|
+
{availableSecrets.map((secret) => (
|
|
205
|
+
<DropdownMenuItem
|
|
206
|
+
key={secret.key}
|
|
207
|
+
onSelect={() => onSecret?.(secret.key)}
|
|
208
|
+
className="flex items-center justify-between gap-3"
|
|
209
|
+
>
|
|
210
|
+
<span className="truncate">{secret.label}</span>
|
|
211
|
+
{secret.required && (
|
|
212
|
+
<span className="text-[9px] font-semibold uppercase tracking-wide text-amber-600 dark:text-amber-400">
|
|
213
|
+
Required
|
|
214
|
+
</span>
|
|
215
|
+
)}
|
|
216
|
+
</DropdownMenuItem>
|
|
217
|
+
))}
|
|
218
|
+
<DropdownMenuSeparator />
|
|
219
|
+
</>
|
|
220
|
+
)}
|
|
221
|
+
<DropdownMenuItem onSelect={onCustomKey}>
|
|
222
|
+
<IconPlus size={14} />
|
|
223
|
+
Custom
|
|
224
|
+
</DropdownMenuItem>
|
|
225
|
+
</DropdownMenuContent>
|
|
226
|
+
</DropdownMenu>
|
|
123
227
|
</div>
|
|
124
228
|
);
|
|
125
229
|
}
|
|
@@ -127,10 +231,18 @@ export function SecretsSection({ focusKey }: SecretsSectionProps) {
|
|
|
127
231
|
interface SecretCardProps {
|
|
128
232
|
secret: SecretStatus;
|
|
129
233
|
onChanged: () => void;
|
|
234
|
+
open: boolean;
|
|
235
|
+
onOpenChange: (open: boolean) => void;
|
|
130
236
|
focusInput?: boolean;
|
|
131
237
|
}
|
|
132
238
|
|
|
133
|
-
function SecretCard({
|
|
239
|
+
function SecretCard({
|
|
240
|
+
secret,
|
|
241
|
+
onChanged,
|
|
242
|
+
open,
|
|
243
|
+
onOpenChange,
|
|
244
|
+
focusInput,
|
|
245
|
+
}: SecretCardProps) {
|
|
134
246
|
const [value, setValue] = useState("");
|
|
135
247
|
const [busy, setBusy] = useState<null | "save" | "delete" | "test">(null);
|
|
136
248
|
const [confirmDelete, setConfirmDelete] = useState(false);
|
|
@@ -141,10 +253,10 @@ function SecretCard({ secret, onChanged, focusInput }: SecretCardProps) {
|
|
|
141
253
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
142
254
|
|
|
143
255
|
useEffect(() => {
|
|
144
|
-
if (focusInput && inputRef.current) {
|
|
256
|
+
if (open && focusInput && inputRef.current) {
|
|
145
257
|
inputRef.current.focus();
|
|
146
258
|
}
|
|
147
|
-
}, [focusInput]);
|
|
259
|
+
}, [focusInput, open]);
|
|
148
260
|
|
|
149
261
|
const setToastAndClear = (kind: "ok" | "err", text: string, ms = 2500) => {
|
|
150
262
|
setToast({ kind, text });
|
|
@@ -256,164 +368,183 @@ function SecretCard({ secret, onChanged, focusInput }: SecretCardProps) {
|
|
|
256
368
|
const isOAuth = secret.kind === "oauth";
|
|
257
369
|
|
|
258
370
|
return (
|
|
259
|
-
<div className="
|
|
260
|
-
<
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
371
|
+
<div className="border-b border-border last:border-b-0">
|
|
372
|
+
<button
|
|
373
|
+
type="button"
|
|
374
|
+
aria-expanded={open}
|
|
375
|
+
onClick={() => onOpenChange(!open)}
|
|
376
|
+
className="flex w-full items-center gap-2 px-2.5 py-2 text-start transition-colors hover:bg-accent/30"
|
|
377
|
+
>
|
|
378
|
+
<IconChevronRight
|
|
379
|
+
size={13}
|
|
380
|
+
className={`shrink-0 text-muted-foreground transition-transform ${open ? "rotate-90" : ""}`}
|
|
381
|
+
/>
|
|
382
|
+
<span className="min-w-0 flex-1 truncate text-[11px] font-medium text-foreground">
|
|
383
|
+
{secret.label}
|
|
384
|
+
</span>
|
|
385
|
+
{secret.status === "set" && secret.last4 && (
|
|
386
|
+
<code className="text-[10px] text-muted-foreground">
|
|
387
|
+
••••{secret.last4}
|
|
388
|
+
</code>
|
|
389
|
+
)}
|
|
390
|
+
<span className="shrink-0">{pill}</span>
|
|
391
|
+
</button>
|
|
392
|
+
|
|
393
|
+
{open && (
|
|
394
|
+
<div className="border-t border-border/60 bg-accent/20 px-3 pb-3 pt-2.5">
|
|
265
395
|
{secret.description && (
|
|
266
|
-
<p className="text-[10px] text-muted-foreground
|
|
396
|
+
<p className="mb-2 text-[10px] leading-relaxed text-muted-foreground">
|
|
267
397
|
{secret.description}
|
|
268
398
|
</p>
|
|
269
399
|
)}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
Docs
|
|
294
|
-
<IconExternalLink size={10} />
|
|
295
|
-
</a>
|
|
296
|
-
)}
|
|
297
|
-
</div>
|
|
298
|
-
) : (
|
|
299
|
-
<div className="mt-2 space-y-1.5">
|
|
300
|
-
{secret.status === "set" && (
|
|
301
|
-
<div className="flex items-center gap-2 text-[10px] text-muted-foreground">
|
|
302
|
-
<span>Stored value ending in</span>
|
|
303
|
-
<code className="rounded bg-background px-1 py-0.5 text-foreground">
|
|
304
|
-
{secret.last4}
|
|
305
|
-
</code>
|
|
400
|
+
{isOAuth ? (
|
|
401
|
+
<div className="mt-2 flex items-center gap-1.5">
|
|
402
|
+
{secret.oauthConnectUrl && (
|
|
403
|
+
<a
|
|
404
|
+
href={secret.oauthConnectUrl}
|
|
405
|
+
className="inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium no-underline"
|
|
406
|
+
style={{ backgroundColor: "#00B5FF", color: "white" }}
|
|
407
|
+
>
|
|
408
|
+
<IconPlugConnected size={10} />
|
|
409
|
+
{secret.status === "set" ? "Reconnect" : "Connect"}
|
|
410
|
+
</a>
|
|
411
|
+
)}
|
|
412
|
+
{secret.docsUrl && (
|
|
413
|
+
<a
|
|
414
|
+
href={secret.docsUrl}
|
|
415
|
+
target="_blank"
|
|
416
|
+
rel="noopener noreferrer"
|
|
417
|
+
className="inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] no-underline text-muted-foreground hover:text-foreground"
|
|
418
|
+
>
|
|
419
|
+
Docs
|
|
420
|
+
<IconExternalLink size={10} />
|
|
421
|
+
</a>
|
|
422
|
+
)}
|
|
306
423
|
</div>
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}}
|
|
317
|
-
placeholder={
|
|
318
|
-
secret.status === "set"
|
|
319
|
-
? "Enter new value to rotate"
|
|
320
|
-
: "Paste key"
|
|
321
|
-
}
|
|
322
|
-
className="flex-1 rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent"
|
|
323
|
-
/>
|
|
324
|
-
<button
|
|
325
|
-
type="button"
|
|
326
|
-
onClick={handleSave}
|
|
327
|
-
disabled={!value.trim() || busy !== null}
|
|
328
|
-
className="inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium disabled:opacity-40"
|
|
329
|
-
style={{ backgroundColor: "#00B5FF", color: "white" }}
|
|
330
|
-
>
|
|
331
|
-
{busy === "save" ? (
|
|
332
|
-
<IconLoader2 size={10} className="animate-spin" />
|
|
333
|
-
) : secret.status === "set" ? (
|
|
334
|
-
<>
|
|
335
|
-
<IconRefresh size={10} />
|
|
336
|
-
Rotate
|
|
337
|
-
</>
|
|
338
|
-
) : (
|
|
339
|
-
"Save"
|
|
424
|
+
) : (
|
|
425
|
+
<div className="mt-2 space-y-1.5">
|
|
426
|
+
{secret.status === "set" && (
|
|
427
|
+
<div className="flex items-center gap-2 text-[10px] text-muted-foreground">
|
|
428
|
+
<span>Stored value ending in</span>
|
|
429
|
+
<code className="rounded bg-background px-1 py-0.5 text-foreground">
|
|
430
|
+
{secret.last4}
|
|
431
|
+
</code>
|
|
432
|
+
</div>
|
|
340
433
|
)}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
434
|
+
<div className="flex gap-1.5">
|
|
435
|
+
<input
|
|
436
|
+
ref={inputRef}
|
|
437
|
+
type="password"
|
|
438
|
+
aria-label={secret.label}
|
|
439
|
+
value={value}
|
|
440
|
+
onChange={(e) => setValue(e.target.value)}
|
|
441
|
+
onKeyDown={(e) => {
|
|
442
|
+
if (e.key === "Enter") handleSave();
|
|
443
|
+
}}
|
|
444
|
+
placeholder={
|
|
445
|
+
secret.status === "set"
|
|
446
|
+
? "Enter new value to rotate"
|
|
447
|
+
: "Paste key"
|
|
448
|
+
}
|
|
449
|
+
className="flex-1 rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent"
|
|
450
|
+
/>
|
|
346
451
|
<button
|
|
347
452
|
type="button"
|
|
348
|
-
onClick={
|
|
349
|
-
disabled={busy !== null}
|
|
350
|
-
className="inline-flex items-center gap-1 rounded
|
|
453
|
+
onClick={handleSave}
|
|
454
|
+
disabled={!value.trim() || busy !== null}
|
|
455
|
+
className="inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium disabled:opacity-40"
|
|
456
|
+
style={{ backgroundColor: "#00B5FF", color: "white" }}
|
|
351
457
|
>
|
|
352
|
-
{busy === "
|
|
458
|
+
{busy === "save" ? (
|
|
353
459
|
<IconLoader2 size={10} className="animate-spin" />
|
|
460
|
+
) : secret.status === "set" ? (
|
|
461
|
+
<>
|
|
462
|
+
<IconRefresh size={10} />
|
|
463
|
+
Rotate
|
|
464
|
+
</>
|
|
354
465
|
) : (
|
|
355
|
-
"
|
|
466
|
+
"Save"
|
|
356
467
|
)}
|
|
357
468
|
</button>
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
<button
|
|
385
|
-
type="button"
|
|
386
|
-
onClick={handleDelete}
|
|
387
|
-
disabled={busy !== null}
|
|
388
|
-
className="inline-flex items-center gap-1 rounded border border-red-500/40 px-1.5 py-0.5 font-medium disabled:opacity-40"
|
|
389
|
-
>
|
|
390
|
-
{busy === "delete" ? (
|
|
391
|
-
<IconLoader2 size={10} className="animate-spin" />
|
|
392
|
-
) : (
|
|
393
|
-
"Confirm"
|
|
469
|
+
</div>
|
|
470
|
+
<div className="flex items-center gap-1.5">
|
|
471
|
+
{secret.status === "set" && (
|
|
472
|
+
<>
|
|
473
|
+
<button
|
|
474
|
+
type="button"
|
|
475
|
+
onClick={handleTest}
|
|
476
|
+
disabled={busy !== null}
|
|
477
|
+
className="inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-40"
|
|
478
|
+
>
|
|
479
|
+
{busy === "test" ? (
|
|
480
|
+
<IconLoader2 size={10} className="animate-spin" />
|
|
481
|
+
) : (
|
|
482
|
+
"Test"
|
|
483
|
+
)}
|
|
484
|
+
</button>
|
|
485
|
+
<button
|
|
486
|
+
type="button"
|
|
487
|
+
onClick={() => setConfirmDelete(true)}
|
|
488
|
+
disabled={busy !== null}
|
|
489
|
+
className="inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] text-muted-foreground hover:text-red-500 disabled:opacity-40"
|
|
490
|
+
>
|
|
491
|
+
<IconTrash size={10} />
|
|
492
|
+
Remove
|
|
493
|
+
</button>
|
|
494
|
+
</>
|
|
394
495
|
)}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
496
|
+
{secret.docsUrl && (
|
|
497
|
+
<a
|
|
498
|
+
href={secret.docsUrl}
|
|
499
|
+
target="_blank"
|
|
500
|
+
rel="noopener noreferrer"
|
|
501
|
+
className="inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-[10px] no-underline text-muted-foreground hover:text-foreground ms-auto"
|
|
502
|
+
>
|
|
503
|
+
Get key
|
|
504
|
+
<IconExternalLink size={10} />
|
|
505
|
+
</a>
|
|
506
|
+
)}
|
|
507
|
+
</div>
|
|
508
|
+
{confirmDelete && (
|
|
509
|
+
<div className="flex items-center gap-1.5 rounded border border-red-500/30 bg-red-500/10 px-2 py-1.5 text-[10px] text-red-500">
|
|
510
|
+
<span className="min-w-0 flex-1">
|
|
511
|
+
Remove this saved value?
|
|
512
|
+
</span>
|
|
513
|
+
<button
|
|
514
|
+
type="button"
|
|
515
|
+
onClick={handleDelete}
|
|
516
|
+
disabled={busy !== null}
|
|
517
|
+
className="inline-flex items-center gap-1 rounded border border-red-500/40 px-1.5 py-0.5 font-medium disabled:opacity-40"
|
|
518
|
+
>
|
|
519
|
+
{busy === "delete" ? (
|
|
520
|
+
<IconLoader2 size={10} className="animate-spin" />
|
|
521
|
+
) : (
|
|
522
|
+
"Confirm"
|
|
523
|
+
)}
|
|
524
|
+
</button>
|
|
525
|
+
<button
|
|
526
|
+
type="button"
|
|
527
|
+
onClick={() => setConfirmDelete(false)}
|
|
528
|
+
disabled={busy !== null}
|
|
529
|
+
className="rounded border border-border px-1.5 py-0.5 text-muted-foreground hover:text-foreground disabled:opacity-40"
|
|
530
|
+
>
|
|
531
|
+
Cancel
|
|
532
|
+
</button>
|
|
533
|
+
</div>
|
|
534
|
+
)}
|
|
404
535
|
</div>
|
|
405
536
|
)}
|
|
406
|
-
</div>
|
|
407
|
-
)}
|
|
408
537
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
538
|
+
{toast && (
|
|
539
|
+
<p
|
|
540
|
+
className={`mt-1.5 text-[10px] ${
|
|
541
|
+
toast.kind === "ok" ? "text-green-500" : "text-red-500"
|
|
542
|
+
}`}
|
|
543
|
+
>
|
|
544
|
+
{toast.text}
|
|
545
|
+
</p>
|
|
546
|
+
)}
|
|
547
|
+
</div>
|
|
417
548
|
)}
|
|
418
549
|
</div>
|
|
419
550
|
);
|
|
@@ -433,11 +564,18 @@ interface AdHocKey {
|
|
|
433
564
|
|
|
434
565
|
const ADHOC_ENDPOINT = agentNativePath("/_agent-native/secrets/adhoc");
|
|
435
566
|
|
|
436
|
-
function AdHocKeysSection(
|
|
567
|
+
function AdHocKeysSection({
|
|
568
|
+
showForm,
|
|
569
|
+
onShowFormChange,
|
|
570
|
+
showEmptyState,
|
|
571
|
+
}: {
|
|
572
|
+
showForm: boolean;
|
|
573
|
+
onShowFormChange: (show: boolean) => void;
|
|
574
|
+
showEmptyState: boolean;
|
|
575
|
+
}) {
|
|
437
576
|
const [keys, setKeys] = useState<AdHocKey[]>([]);
|
|
438
577
|
const [loading, setLoading] = useState(true);
|
|
439
578
|
const [reloadToken, setReloadToken] = useState(0);
|
|
440
|
-
const [showForm, setShowForm] = useState(false);
|
|
441
579
|
const [formName, setFormName] = useState("");
|
|
442
580
|
const [formValue, setFormValue] = useState("");
|
|
443
581
|
const [formDescription, setFormDescription] = useState("");
|
|
@@ -489,13 +627,13 @@ function AdHocKeysSection() {
|
|
|
489
627
|
}, [reloadToken]);
|
|
490
628
|
|
|
491
629
|
const resetForm = useCallback(() => {
|
|
492
|
-
|
|
630
|
+
onShowFormChange(false);
|
|
493
631
|
setFormName("");
|
|
494
632
|
setFormValue("");
|
|
495
633
|
setFormDescription("");
|
|
496
634
|
setFormScope("user");
|
|
497
635
|
setFormError(null);
|
|
498
|
-
}, []);
|
|
636
|
+
}, [onShowFormChange]);
|
|
499
637
|
|
|
500
638
|
const handleAdd = useCallback(async () => {
|
|
501
639
|
const name = formName.trim();
|
|
@@ -524,6 +662,7 @@ function AdHocKeysSection() {
|
|
|
524
662
|
}
|
|
525
663
|
resetForm();
|
|
526
664
|
showToast("ok", "Key saved");
|
|
665
|
+
notifySecretsChanged();
|
|
527
666
|
reload();
|
|
528
667
|
} catch (err: any) {
|
|
529
668
|
setFormError(err?.message ?? "Failed to save");
|
|
@@ -558,6 +697,7 @@ function AdHocKeysSection() {
|
|
|
558
697
|
}
|
|
559
698
|
showToast("ok", "Key deleted");
|
|
560
699
|
setConfirmDeleteName(null);
|
|
700
|
+
notifySecretsChanged();
|
|
561
701
|
reload();
|
|
562
702
|
} finally {
|
|
563
703
|
setDeletingName(null);
|
|
@@ -567,30 +707,7 @@ function AdHocKeysSection() {
|
|
|
567
707
|
);
|
|
568
708
|
|
|
569
709
|
return (
|
|
570
|
-
<div className="
|
|
571
|
-
<div className="flex items-center justify-between">
|
|
572
|
-
<p className="text-[11px] font-medium text-foreground">
|
|
573
|
-
Additional Keys
|
|
574
|
-
</p>
|
|
575
|
-
{!showForm && (
|
|
576
|
-
<button
|
|
577
|
-
type="button"
|
|
578
|
-
onClick={() => setShowForm(true)}
|
|
579
|
-
className="inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] font-medium text-muted-foreground hover:text-foreground hover:bg-accent/40"
|
|
580
|
-
>
|
|
581
|
-
<IconPlus size={10} />
|
|
582
|
-
Add Key
|
|
583
|
-
</button>
|
|
584
|
-
)}
|
|
585
|
-
</div>
|
|
586
|
-
<p className="text-[10px] text-muted-foreground/60 leading-relaxed">
|
|
587
|
-
Keys are referenced in automations as{" "}
|
|
588
|
-
<code className="rounded bg-background px-1 py-0.5 text-[9px]">
|
|
589
|
-
{"${keys.KEY_NAME}"}
|
|
590
|
-
</code>
|
|
591
|
-
. Values are encrypted and never shown to the AI agent.
|
|
592
|
-
</p>
|
|
593
|
-
|
|
710
|
+
<div className="space-y-2">
|
|
594
711
|
{showForm && (
|
|
595
712
|
<div className="rounded-md border border-border px-2.5 py-2 bg-accent/30 space-y-1.5">
|
|
596
713
|
<input
|
|
@@ -601,10 +718,12 @@ function AdHocKeysSection() {
|
|
|
601
718
|
)
|
|
602
719
|
}
|
|
603
720
|
className="w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent"
|
|
604
|
-
|
|
721
|
+
aria-label="Key name"
|
|
722
|
+
placeholder="KEY_NAME"
|
|
605
723
|
/>
|
|
606
724
|
<input
|
|
607
725
|
type="password"
|
|
726
|
+
aria-label="Secret value"
|
|
608
727
|
value={formValue}
|
|
609
728
|
onChange={(e) => setFormValue(e.target.value)}
|
|
610
729
|
className="w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent"
|
|
@@ -612,12 +731,14 @@ function AdHocKeysSection() {
|
|
|
612
731
|
/>
|
|
613
732
|
<input
|
|
614
733
|
value={formDescription}
|
|
734
|
+
aria-label="Description"
|
|
615
735
|
onChange={(e) => setFormDescription(e.target.value)}
|
|
616
736
|
className="w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent"
|
|
617
737
|
placeholder="Description (optional)"
|
|
618
738
|
/>
|
|
619
739
|
<div className="flex items-center gap-2">
|
|
620
740
|
<select
|
|
741
|
+
aria-label="Scope"
|
|
621
742
|
value={formScope}
|
|
622
743
|
onChange={(e) =>
|
|
623
744
|
setFormScope(e.target.value as "user" | "workspace")
|
|
@@ -659,88 +780,88 @@ function AdHocKeysSection() {
|
|
|
659
780
|
<IconLoader2 size={10} className="animate-spin" />
|
|
660
781
|
Loading...
|
|
661
782
|
</div>
|
|
662
|
-
) : keys.length === 0 && !showForm ? (
|
|
663
|
-
<p className="text-[10px] text-muted-foreground">
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
}`}
|
|
684
|
-
>
|
|
685
|
-
{key.scope === "workspace" ? "workspace" : "personal"}
|
|
686
|
-
</span>
|
|
687
|
-
</div>
|
|
688
|
-
{key.description && (
|
|
689
|
-
<p className="text-[10px] text-muted-foreground mt-0.5">
|
|
690
|
-
{key.description}
|
|
691
|
-
</p>
|
|
692
|
-
)}
|
|
693
|
-
<div className="flex items-center gap-2 text-[10px] text-muted-foreground mt-0.5">
|
|
694
|
-
<span>
|
|
695
|
-
Ending in{" "}
|
|
696
|
-
<code className="rounded bg-background px-1 py-0.5 text-foreground">
|
|
697
|
-
{key.last4}
|
|
698
|
-
</code>
|
|
699
|
-
</span>
|
|
700
|
-
</div>
|
|
701
|
-
</div>
|
|
702
|
-
<div className="shrink-0">
|
|
703
|
-
{confirmDeleteName === key.name ? (
|
|
704
|
-
<div className="flex items-center gap-1">
|
|
705
|
-
<button
|
|
706
|
-
type="button"
|
|
707
|
-
onClick={() => handleDelete(key.name)}
|
|
708
|
-
disabled={deletingName === key.name}
|
|
709
|
-
className="rounded px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide bg-red-500/15 text-red-500 hover:bg-red-500/25 disabled:opacity-40"
|
|
710
|
-
>
|
|
711
|
-
{deletingName === key.name ? (
|
|
712
|
-
<IconLoader2 size={10} className="animate-spin" />
|
|
713
|
-
) : (
|
|
714
|
-
"Confirm"
|
|
715
|
-
)}
|
|
716
|
-
</button>
|
|
717
|
-
<button
|
|
718
|
-
type="button"
|
|
719
|
-
onClick={() => setConfirmDeleteName(null)}
|
|
720
|
-
className="rounded px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide bg-accent/60 text-muted-foreground hover:text-foreground"
|
|
783
|
+
) : keys.length === 0 && !showForm && showEmptyState ? (
|
|
784
|
+
<p className="text-[10px] text-muted-foreground">No keys added yet.</p>
|
|
785
|
+
) : keys.length > 0 ? (
|
|
786
|
+
<div className="overflow-hidden rounded-md border border-border">
|
|
787
|
+
{keys.map((key) => (
|
|
788
|
+
<div
|
|
789
|
+
key={`${key.scope}-${key.name}`}
|
|
790
|
+
className="border-b border-border px-2.5 py-2 last:border-b-0"
|
|
791
|
+
>
|
|
792
|
+
<div className="flex items-center justify-between gap-2">
|
|
793
|
+
<div className="min-w-0 flex-1">
|
|
794
|
+
<div className="flex items-center gap-1.5">
|
|
795
|
+
<span className="text-[11px] font-medium text-foreground font-mono truncate">
|
|
796
|
+
{key.name}
|
|
797
|
+
</span>
|
|
798
|
+
<span
|
|
799
|
+
className={`rounded-full px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide ${
|
|
800
|
+
key.scope === "workspace"
|
|
801
|
+
? "bg-blue-500/15 text-blue-500"
|
|
802
|
+
: "bg-accent/60 text-muted-foreground"
|
|
803
|
+
}`}
|
|
721
804
|
>
|
|
722
|
-
|
|
723
|
-
</
|
|
805
|
+
{key.scope === "workspace" ? "workspace" : "personal"}
|
|
806
|
+
</span>
|
|
724
807
|
</div>
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
808
|
+
{key.description && (
|
|
809
|
+
<p className="text-[10px] text-muted-foreground mt-0.5">
|
|
810
|
+
{key.description}
|
|
811
|
+
</p>
|
|
812
|
+
)}
|
|
813
|
+
<div className="flex items-center gap-2 text-[10px] text-muted-foreground mt-0.5">
|
|
814
|
+
<span>
|
|
815
|
+
Ending in{" "}
|
|
816
|
+
<code className="rounded bg-background px-1 py-0.5 text-foreground">
|
|
817
|
+
{key.last4}
|
|
818
|
+
</code>
|
|
819
|
+
</span>
|
|
820
|
+
</div>
|
|
821
|
+
</div>
|
|
822
|
+
<div className="shrink-0">
|
|
823
|
+
{confirmDeleteName === key.name ? (
|
|
824
|
+
<div className="flex items-center gap-1">
|
|
728
825
|
<button
|
|
729
826
|
type="button"
|
|
730
|
-
onClick={() =>
|
|
731
|
-
|
|
827
|
+
onClick={() => handleDelete(key.name)}
|
|
828
|
+
disabled={deletingName === key.name}
|
|
829
|
+
className="rounded px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide bg-red-500/15 text-red-500 hover:bg-red-500/25 disabled:opacity-40"
|
|
732
830
|
>
|
|
733
|
-
|
|
831
|
+
{deletingName === key.name ? (
|
|
832
|
+
<IconLoader2 size={10} className="animate-spin" />
|
|
833
|
+
) : (
|
|
834
|
+
"Confirm"
|
|
835
|
+
)}
|
|
734
836
|
</button>
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
837
|
+
<button
|
|
838
|
+
type="button"
|
|
839
|
+
onClick={() => setConfirmDeleteName(null)}
|
|
840
|
+
className="rounded px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide bg-accent/60 text-muted-foreground hover:text-foreground"
|
|
841
|
+
>
|
|
842
|
+
Cancel
|
|
843
|
+
</button>
|
|
844
|
+
</div>
|
|
845
|
+
) : (
|
|
846
|
+
<Tooltip>
|
|
847
|
+
<TooltipTrigger asChild>
|
|
848
|
+
<button
|
|
849
|
+
type="button"
|
|
850
|
+
onClick={() => setConfirmDeleteName(key.name)}
|
|
851
|
+
className="text-muted-foreground hover:text-red-500"
|
|
852
|
+
>
|
|
853
|
+
<IconTrash size={12} />
|
|
854
|
+
</button>
|
|
855
|
+
</TooltipTrigger>
|
|
856
|
+
<TooltipContent>Delete</TooltipContent>
|
|
857
|
+
</Tooltip>
|
|
858
|
+
)}
|
|
859
|
+
</div>
|
|
739
860
|
</div>
|
|
740
861
|
</div>
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
)}
|
|
862
|
+
))}
|
|
863
|
+
</div>
|
|
864
|
+
) : null}
|
|
744
865
|
|
|
745
866
|
{toast && (
|
|
746
867
|
<p
|