@rightkit/agent-ui-settings 0.0.0-stage → 0.1.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.
Files changed (100) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE-APACHE +201 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +44 -2
  5. package/dist/appName.d.ts +2 -0
  6. package/dist/appName.js +8 -0
  7. package/dist/components/PluginContributionHost.d.ts +17 -0
  8. package/dist/components/PluginContributionHost.js +23 -0
  9. package/dist/components/ReadinessCard.d.ts +4 -0
  10. package/dist/components/ReadinessCard.js +19 -0
  11. package/dist/components/remote/RemoteDevicesPanel.d.ts +7 -0
  12. package/dist/components/remote/RemoteDevicesPanel.js +153 -0
  13. package/dist/components/remote/RemoteHostsPanel.d.ts +8 -0
  14. package/dist/components/remote/RemoteHostsPanel.js +139 -0
  15. package/dist/components/settings/analytics/AnalyticsConsentCard.d.ts +14 -0
  16. package/dist/components/settings/analytics/AnalyticsConsentCard.js +22 -0
  17. package/dist/components/settings/cliUpdate/CliUpdatePanel.d.ts +9 -0
  18. package/dist/components/settings/cliUpdate/CliUpdatePanel.js +18 -0
  19. package/dist/components/settings/helpers.d.ts +7 -0
  20. package/dist/components/settings/helpers.js +40 -0
  21. package/dist/components/settings/keyboard/KeyboardShortcutsSettings.d.ts +12 -0
  22. package/dist/components/settings/keyboard/KeyboardShortcutsSettings.js +75 -0
  23. package/dist/components/settings/keyboard/ShortcutRecordingDialog.d.ts +27 -0
  24. package/dist/components/settings/keyboard/ShortcutRecordingDialog.js +154 -0
  25. package/dist/components/settings/marketplace/McpDiscoverPanel.d.ts +34 -0
  26. package/dist/components/settings/marketplace/McpDiscoverPanel.js +66 -0
  27. package/dist/components/settings/marketplace/SkillMarketplace.d.ts +22 -0
  28. package/dist/components/settings/marketplace/SkillMarketplace.js +51 -0
  29. package/dist/components/settings/tabs/ApiKeyAccounts.d.ts +9 -0
  30. package/dist/components/settings/tabs/ApiKeyAccounts.js +67 -0
  31. package/dist/components/settings/tabs/AppearanceTab.d.ts +1 -0
  32. package/dist/components/settings/tabs/AppearanceTab.js +29 -0
  33. package/dist/components/settings/tabs/AppleSyncPanel.d.ts +14 -0
  34. package/dist/components/settings/tabs/AppleSyncPanel.js +65 -0
  35. package/dist/components/settings/tabs/ChildProviderPolicy.d.ts +9 -0
  36. package/dist/components/settings/tabs/ChildProviderPolicy.js +84 -0
  37. package/dist/components/settings/tabs/ConductorsTab.d.ts +6 -0
  38. package/dist/components/settings/tabs/ConductorsTab.js +135 -0
  39. package/dist/components/settings/tabs/DiagnosticsTab.d.ts +13 -0
  40. package/dist/components/settings/tabs/DiagnosticsTab.js +39 -0
  41. package/dist/components/settings/tabs/ExecutionBackendsTab.d.ts +29 -0
  42. package/dist/components/settings/tabs/ExecutionBackendsTab.js +13 -0
  43. package/dist/components/settings/tabs/LicenseTab.d.ts +20 -0
  44. package/dist/components/settings/tabs/LicenseTab.js +66 -0
  45. package/dist/components/settings/tabs/LinkComputerPanel.d.ts +6 -0
  46. package/dist/components/settings/tabs/LinkComputerPanel.js +39 -0
  47. package/dist/components/settings/tabs/LspTab.d.ts +11 -0
  48. package/dist/components/settings/tabs/LspTab.js +34 -0
  49. package/dist/components/settings/tabs/MemoryTab.d.ts +7 -0
  50. package/dist/components/settings/tabs/MemoryTab.js +68 -0
  51. package/dist/components/settings/tabs/ProvidersTab.d.ts +10 -0
  52. package/dist/components/settings/tabs/ProvidersTab.js +214 -0
  53. package/dist/components/settings/tabs/RepairTab.d.ts +6 -0
  54. package/dist/components/settings/tabs/RepairTab.js +44 -0
  55. package/dist/components/settings/tabs/RootModelPicker.d.ts +10 -0
  56. package/dist/components/settings/tabs/RootModelPicker.js +39 -0
  57. package/dist/components/settings/tabs/RuntimeMiddlewareTab.d.ts +8 -0
  58. package/dist/components/settings/tabs/RuntimeMiddlewareTab.js +12 -0
  59. package/dist/components/settings/tabs/WorkspaceTab.d.ts +6 -0
  60. package/dist/components/settings/tabs/WorkspaceTab.js +9 -0
  61. package/dist/components/settings/tabs/execution/CoreSandboxTierPanel.d.ts +9 -0
  62. package/dist/components/settings/tabs/execution/CoreSandboxTierPanel.js +42 -0
  63. package/dist/components/settings/tabs/execution/ExecutionBackendCard.d.ts +20 -0
  64. package/dist/components/settings/tabs/execution/ExecutionBackendCard.js +48 -0
  65. package/dist/components/settings/tabs/execution/ExecutionBackendLimitsForm.d.ts +10 -0
  66. package/dist/components/settings/tabs/execution/ExecutionBackendLimitsForm.js +62 -0
  67. package/dist/components/settings/tabs/execution/ExecutionBackendList.d.ts +19 -0
  68. package/dist/components/settings/tabs/execution/ExecutionBackendList.js +8 -0
  69. package/dist/components/settings/tabs/execution/ExecutionNetworkProfilesPanel.d.ts +16 -0
  70. package/dist/components/settings/tabs/execution/ExecutionNetworkProfilesPanel.js +53 -0
  71. package/dist/components/settings/tabs/execution/RemoteStatusCard.d.ts +2 -0
  72. package/dist/components/settings/tabs/execution/RemoteStatusCard.js +5 -0
  73. package/dist/components/settings/tabs/execution/TextSettingInput.d.ts +15 -0
  74. package/dist/components/settings/tabs/execution/TextSettingInput.js +10 -0
  75. package/dist/components/settings/tabs/execution/types.d.ts +27 -0
  76. package/dist/components/settings/tabs/execution/types.js +1 -0
  77. package/dist/components/settings/tabs/models/ModelEmptyStates.d.ts +12 -0
  78. package/dist/components/settings/tabs/models/ModelEmptyStates.js +63 -0
  79. package/dist/components/settings/tabs/permissions/PersistentPermissionRuleList.d.ts +7 -0
  80. package/dist/components/settings/tabs/permissions/PersistentPermissionRuleList.js +15 -0
  81. package/dist/components/settings/tabs/plugins/InstalledPluginsList.d.ts +6 -0
  82. package/dist/components/settings/tabs/plugins/InstalledPluginsList.js +38 -0
  83. package/dist/components/settings/tabs/remote/RemoteChannelsPanel.d.ts +4 -0
  84. package/dist/components/settings/tabs/remote/RemoteChannelsPanel.js +14 -0
  85. package/dist/components/settings/tabs/remote/RemoteStatusCard.d.ts +8 -0
  86. package/dist/components/settings/tabs/remote/RemoteStatusCard.js +6 -0
  87. package/dist/components/settings/tabs/remote/TelegramStatusPanel.d.ts +6 -0
  88. package/dist/components/settings/tabs/remote/TelegramStatusPanel.js +41 -0
  89. package/dist/components/settings/tabs/shared.d.ts +18 -0
  90. package/dist/components/settings/tabs/shared.js +36 -0
  91. package/dist/components/settings/tabs/skills/SkillShared.d.ts +20 -0
  92. package/dist/components/settings/tabs/skills/SkillShared.js +28 -0
  93. package/dist/components/settings/tabs/skills/SkillTrainingRunList.d.ts +6 -0
  94. package/dist/components/settings/tabs/skills/SkillTrainingRunList.js +11 -0
  95. package/dist/components/settings/tools/ManagedToolHealthPanel.d.ts +16 -0
  96. package/dist/components/settings/tools/ManagedToolHealthPanel.js +24 -0
  97. package/dist/env.d.ts +59 -0
  98. package/dist/index.d.ts +31 -0
  99. package/dist/index.js +33 -0
  100. package/package.json +171 -3
@@ -0,0 +1,66 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { EmptyState } from "@rightkit/react-shell/react";
3
+ import { useMemo, useState } from "react";
4
+ import Icon from "@rightkit/agent-ui-core/components/Icon";
5
+ function transport(server) {
6
+ return server.remotes.length > 0 ? "http" : "stdio";
7
+ }
8
+ function transportLabel(server) {
9
+ if (server.remotes.length > 0)
10
+ return "HTTP";
11
+ return server.packages[0]?.registryType || "stdio";
12
+ }
13
+ function codexKey(name) {
14
+ return name.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
15
+ }
16
+ function isInstalled(server, installedServers, agent) {
17
+ const rows = installedServers.filter((item) => (item.agent ?? "claude") === agent);
18
+ if (server.remotes.length > 0) {
19
+ return rows.some((item) => item.url === server.remotes[0]?.url);
20
+ }
21
+ const identifier = server.packages[0]?.identifier;
22
+ return identifier
23
+ ? rows.some((item) => item.args?.some((arg) => arg === identifier || arg.includes(identifier)))
24
+ : false;
25
+ }
26
+ function RegistryStatus({ health, onRefresh, refreshing }) {
27
+ const tone = health === null || health === undefined ? "bg-text-3/40" : health.available ? "bg-accent-2" : "bg-danger";
28
+ return (_jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [_jsxs("span", { className: "flex items-center gap-1.5 text-[10px] text-text-3", title: health?.reason ?? "", children: [_jsx("span", { className: `inline-block h-2 w-2 rounded-full ${tone}`, "aria-hidden": "true" }), "registry"] }), onRefresh ? (_jsx("button", { type: "button", className: "rounded p-1 text-text-3 hover:bg-panel hover:text-text disabled:opacity-40", title: "Refresh registry", "aria-label": "Refresh registry", disabled: refreshing, onClick: onRefresh, children: _jsx(Icon, { name: "refresh", size: 13 }) })) : null] }));
29
+ }
30
+ export default function McpDiscoverPanel({ projectCwd = "", query, results, popularResults = [], selectedServer = null, installedServers = [], health = null, searching = false, refreshing = false, operationLoading = null, installAgent = "claude", installScope = "user", onQueryChange, onAgentChange, onScopeChange, onRefresh, onSelect, onClearSelection, onInstall, onImportClaudeDesktop, onImportJson, }) {
31
+ const [environment, setEnvironment] = useState({});
32
+ const [headers, setHeaders] = useState({});
33
+ const displayResults = query.trim().length >= 2 ? results : popularResults;
34
+ const selectedInstalled = selectedServer
35
+ ? isInstalled(selectedServer, installedServers, installAgent)
36
+ : false;
37
+ const canUseProjectScope = Boolean(projectCwd) && installAgent !== "codex";
38
+ const selectedPackage = selectedServer?.packages[0];
39
+ const selectedRemote = selectedServer?.remotes[0];
40
+ const environmentVariables = useMemo(() => selectedPackage?.environmentVariables ?? [], [selectedPackage]);
41
+ const headerDefinitions = useMemo(() => selectedRemote?.headers ?? [], [selectedRemote]);
42
+ function selectServer(server) {
43
+ setEnvironment(Object.fromEntries((server.packages[0]?.environmentVariables ?? []).map((item) => [item.name, ""])));
44
+ setHeaders(Object.fromEntries((server.remotes[0]?.headers ?? []).map((item) => [item.name, item.value ?? ""])));
45
+ onSelect(server);
46
+ }
47
+ function install(server) {
48
+ onInstall(server, {
49
+ agent: installAgent,
50
+ scope: installScope,
51
+ cwd: projectCwd || undefined,
52
+ environment,
53
+ headers,
54
+ });
55
+ }
56
+ return (_jsxs("section", { className: "flex min-h-0 flex-col gap-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [_jsx(RegistryStatus, { health: health, onRefresh: onRefresh, refreshing: refreshing }), _jsxs("label", { className: "flex min-w-[220px] flex-1 items-center gap-2 rounded-lg border border-line bg-panel px-3 text-text-3", children: [_jsx(Icon, { name: "search", size: 13 }), _jsx("input", { type: "search", "aria-label": "Search MCP registry", className: "h-9 min-w-0 flex-1 border-0 bg-transparent text-[13px] text-text outline-none placeholder:text-text-3", placeholder: "Search MCP registry", value: query, onChange: (event) => onQueryChange(event.target.value) })] }), _jsx("div", { className: "inline-flex rounded-lg border border-line bg-panel p-1", children: ["claude", "codex"].map((agent) => (_jsx("button", { type: "button", className: `rounded-md px-3 py-1.5 text-[12px] ${installAgent === agent ? "bg-panel-2 text-text" : "text-text-3 hover:text-text"}`, "aria-pressed": installAgent === agent, onClick: () => onAgentChange?.(agent), children: agent === "claude" ? "Claude" : "Codex" }, agent))) }), _jsx("div", { className: "inline-flex rounded-lg border border-line bg-panel p-1", children: ["user", "project", "local"].map((scope) => (_jsx("button", { type: "button", className: `rounded-md px-3 py-1.5 text-[12px] capitalize ${installScope === scope ? "bg-panel-2 text-text" : "text-text-3 hover:text-text"}`, disabled: (scope !== "user" && !canUseProjectScope) || (scope === "local" && installAgent === "codex"), "aria-pressed": installScope === scope, onClick: () => onScopeChange?.(scope), children: scope }, scope))) })] }), _jsx("div", { className: "flex flex-wrap gap-2", children: ["filesystem", "database", "github", "api", "web", "docker"].map((filter) => (_jsx("button", { type: "button", className: `rounded-full border px-3 py-1 text-[11px] ${query === filter ? "border-accent/50 bg-accent/10 text-text" : "border-line text-text-3 hover:border-line-2 hover:text-text"}`, onClick: () => onQueryChange(filter), children: filter }, filter))) }), onImportClaudeDesktop || onImportJson ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2 rounded-lg border border-line bg-panel px-3 py-2", children: [_jsx("span", { className: "mr-auto text-[12px] text-text-2", children: "Import existing MCP configuration" }), onImportClaudeDesktop ? (_jsxs("button", { type: "button", className: "inline-flex h-8 items-center gap-1.5 rounded-lg border border-line px-3 text-[12px] text-text-2 hover:bg-panel-2", onClick: onImportClaudeDesktop, children: [_jsx(Icon, { name: "download", size: 13 }), " Claude Desktop"] })) : null, onImportJson ? (_jsxs("label", { className: "inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-lg border border-line px-3 text-[12px] text-text-2 hover:bg-panel-2", children: [_jsx(Icon, { name: "document", size: 13 }), " JSON", _jsx("input", { type: "file", accept: ".json,application/json", className: "sr-only", onChange: (event) => {
57
+ const file = event.currentTarget.files?.[0];
58
+ if (file)
59
+ void onImportJson(file);
60
+ event.currentTarget.value = "";
61
+ } })] })) : null] })) : null, searching ? (_jsxs("div", { className: "flex items-center justify-center gap-2 py-10 text-[12px] text-text-3", role: "status", children: [_jsx(Icon, { name: "refresh", size: 14 }), " Searching registry\u2026"] })) : displayResults.length === 0 ? (_jsx(EmptyState, { icon: _jsx(Icon, { name: "server", size: 18 }), title: query.trim().length >= 2 ? "No MCP servers found." : "No popular MCP servers available.", action: onRefresh && query.trim().length < 2 ? _jsx("button", { type: "button", className: "rounded-lg border border-line px-3 py-1.5 text-[12px] text-text-2 hover:bg-panel", onClick: onRefresh, children: "Retry" }) : undefined, children: query.trim().length >= 2 ? "Try a broader search term." : "Refresh the registry or search by capability." })) : (_jsxs("div", { className: "flex min-h-[300px] min-w-0 gap-3", children: [_jsxs("div", { className: "w-[280px] shrink-0 space-y-2 overflow-y-auto pr-1", children: [_jsx("div", { className: "px-1 text-[11px] text-text-3", children: query.trim().length >= 2 ? `${results.length} results` : "Popular servers" }), displayResults.map((server) => {
62
+ const installed = isInstalled(server, installedServers, installAgent);
63
+ const pending = operationLoading === server.name;
64
+ return (_jsx("article", { className: `rounded-lg border px-3 py-2 ${selectedServer?.name === server.name ? "border-accent/50 bg-accent/5" : "border-line bg-panel hover:bg-panel-2"}`, children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsxs("button", { type: "button", className: "min-w-0 flex-1 text-left", onClick: () => selectServer(server), children: [_jsx("strong", { className: "block truncate text-[13px] font-semibold text-text", children: server.title || server.name }), _jsxs("span", { className: "mt-1 block text-[11px] text-text-3", children: [server.version ? `v${server.version} · ` : "", transportLabel(server)] })] }), _jsx("button", { type: "button", className: `shrink-0 rounded-md px-2 py-1 text-[11px] font-semibold ${installed ? "bg-accent-2/10 text-accent-2" : "bg-accent text-on-accent"}`, disabled: installed || pending, onClick: () => install(server), children: pending ? "…" : installed ? "Installed" : "Install" })] }) }, `${server.name}:${server.version ?? ""}`));
65
+ })] }), _jsx("div", { className: "min-w-0 flex-1 overflow-y-auto", children: selectedServer ? (_jsxs("div", { className: "space-y-4 rounded-lg border border-line bg-panel p-4", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h3", { className: "truncate text-[14px] font-semibold text-text", children: selectedServer.title || selectedServer.name }), _jsxs("div", { className: "mt-1 flex flex-wrap gap-2 text-[11px] text-text-3", children: [selectedServer.version ? _jsxs("span", { children: ["v", selectedServer.version] }) : null, _jsx("span", { children: transportLabel(selectedServer) }), selectedServer.repository?.url ? _jsx("a", { className: "underline hover:text-text", href: selectedServer.repository.url, target: "_blank", rel: "noreferrer", children: "Repository" }) : null] }), _jsx("p", { className: "mt-2 text-[12px] leading-5 text-text-2", children: selectedServer.description || "No description provided." })] }), _jsx("button", { type: "button", "aria-label": "Close MCP server preview", className: "text-text-3 hover:text-text", onClick: () => onClearSelection?.(), children: _jsx(Icon, { name: "close", size: 14 }) })] }), selectedPackage ? _jsxs("div", { className: "border-t border-line pt-3", children: [_jsx("h4", { className: "mb-2 text-[11px] uppercase text-text-3", children: "Package" }), _jsxs("div", { className: "rounded-lg bg-bg px-3 py-2 text-[12px] text-text", children: [_jsx("code", { children: selectedPackage.identifier }), _jsx("span", { className: "ml-2 text-text-3", children: selectedPackage.registryType })] })] }) : null, selectedRemote ? _jsxs("div", { className: "border-t border-line pt-3", children: [_jsx("h4", { className: "mb-2 text-[11px] uppercase text-text-3", children: "Remote endpoint" }), _jsxs("div", { className: "rounded-lg bg-bg px-3 py-2 text-[12px] text-text", children: [_jsx("code", { className: "break-all", children: selectedRemote.url }), _jsx("span", { className: "ml-2 text-text-3", children: selectedRemote.type })] })] }) : null, environmentVariables.length > 0 ? _jsxs("div", { className: "space-y-2 border-t border-line pt-3", children: [_jsx("h4", { className: "text-[11px] uppercase text-text-3", children: "Environment variables" }), environmentVariables.map((item) => _jsxs("label", { className: "flex items-center gap-2 text-[11px] text-text-2", children: [_jsxs("span", { className: "w-36 shrink-0 truncate font-mono", title: item.description, children: [item.name, item.isRequired ? " *" : ""] }), _jsx("input", { "aria-label": `${item.name} environment variable`, type: item.isSecret ? "password" : "text", className: "h-8 min-w-0 flex-1 rounded-lg border border-line bg-bg px-2 text-[12px] text-text outline-none", value: environment[item.name] ?? "", onChange: (event) => setEnvironment((current) => ({ ...current, [item.name]: event.target.value })), placeholder: item.description })] }, item.name))] }) : null, headerDefinitions.length > 0 ? _jsxs("div", { className: "space-y-2 border-t border-line pt-3", children: [_jsx("h4", { className: "text-[11px] uppercase text-text-3", children: "HTTP headers" }), headerDefinitions.map((item) => _jsxs("label", { className: "flex items-center gap-2 text-[11px] text-text-2", children: [_jsxs("span", { className: "w-36 shrink-0 truncate font-mono", title: item.description, children: [item.name, item.isRequired ? " *" : ""] }), _jsx("input", { "aria-label": `${item.name} HTTP header`, type: item.isSecret ? "password" : "text", className: "h-8 min-w-0 flex-1 rounded-lg border border-line bg-bg px-2 text-[12px] text-text outline-none", value: headers[item.name] ?? "", onChange: (event) => setHeaders((current) => ({ ...current, [item.name]: event.target.value })), placeholder: item.description })] }, item.name))] }) : null, _jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-line pt-3", children: [_jsxs("span", { className: "text-[11px] text-text-3", children: [transport(selectedServer) === "http" ? "HTTP" : `${installAgent === "codex" ? codexKey(selectedServer.name) : selectedServer.name} · ${installScope}`, " "] }), _jsxs("button", { type: "button", className: `inline-flex h-9 items-center gap-2 rounded-lg px-4 text-[12px] font-semibold ${selectedInstalled ? "border border-line bg-transparent text-text-3" : "bg-accent text-on-accent hover:opacity-90"}`, disabled: selectedInstalled || operationLoading === selectedServer.name, onClick: () => install(selectedServer), children: [_jsx(Icon, { name: "download", size: 13 }), " ", operationLoading === selectedServer.name ? "Installing…" : selectedInstalled ? "Installed" : "Install server"] })] })] })) : _jsx(EmptyState, { icon: _jsx(Icon, { name: "server", size: 18 }), title: "Select an MCP server.", children: "Choose a registry result to inspect its transport and installation settings." }) })] }))] }));
66
+ }
@@ -0,0 +1,22 @@
1
+ import { type CommunitySkillDetail, type CommunitySkillResult, type RegistryHealthProjection } from "@rightkit/agent-ui-core/lib/daemon/marketplaces";
2
+ export type SkillMarketplaceScope = "user" | "project";
3
+ export interface SkillMarketplaceProps {
4
+ projectCwd?: string;
5
+ results: CommunitySkillResult[];
6
+ popularResults?: CommunitySkillResult[];
7
+ selectedSkill?: CommunitySkillDetail | null;
8
+ installedSlugs?: string[] | Set<string>;
9
+ health?: RegistryHealthProjection | null;
10
+ searching?: boolean;
11
+ refreshing?: boolean;
12
+ detailLoading?: boolean;
13
+ detailError?: string | null;
14
+ operationLoading?: string | null;
15
+ onSearch: (query: string) => void;
16
+ onRefresh?: () => void;
17
+ onSelect: (skill: CommunitySkillResult) => void;
18
+ onClearSelection?: () => void;
19
+ onInstall: (skill: CommunitySkillResult, scope: SkillMarketplaceScope, cwd?: string) => void | Promise<void>;
20
+ onScopeChange?: (scope: SkillMarketplaceScope) => void;
21
+ }
22
+ export default function SkillMarketplace({ projectCwd, results, popularResults, selectedSkill, installedSlugs, health, searching, refreshing, detailLoading, detailError, operationLoading, onSearch, onRefresh, onSelect, onClearSelection, onInstall, onScopeChange, }: SkillMarketplaceProps): import("react").JSX.Element;
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { EmptyState } from "@rightkit/react-shell/react";
3
+ import { useEffect, useMemo, useState } from "react";
4
+ import Icon from "@rightkit/agent-ui-core/components/Icon";
5
+ import { toMarketplaceSkillSlug, } from "@rightkit/agent-ui-core/lib/daemon/marketplaces";
6
+ function installCount(value) {
7
+ if (value < 1_000)
8
+ return String(value);
9
+ if (value < 1_000_000)
10
+ return `${(value / 1_000).toFixed(value >= 10_000 ? 0 : 1)}k`;
11
+ return `${(value / 1_000_000).toFixed(value >= 10_000_000 ? 0 : 1)}m`;
12
+ }
13
+ function hasInstalledSkill(installedSlugs, skillId) {
14
+ const slug = toMarketplaceSkillSlug(skillId);
15
+ if (!slug)
16
+ return false;
17
+ return installedSlugs instanceof Set ? installedSlugs.has(slug) : installedSlugs.includes(slug);
18
+ }
19
+ function RegistryStatus({ health, refreshing, onRefresh }) {
20
+ const tone = health === null || health === undefined ? "bg-text-3/40" : health.available ? "bg-accent-2" : "bg-danger";
21
+ return (_jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [_jsxs("span", { className: "flex items-center gap-1.5 text-[10px] text-text-3", title: health?.reason ?? "", children: [_jsx("span", { className: `inline-block h-2 w-2 rounded-full ${tone}`, "aria-hidden": "true" }), "skills marketplace"] }), onRefresh ? _jsx("button", { type: "button", className: "rounded p-1 text-text-3 hover:bg-panel hover:text-text disabled:opacity-40", "aria-label": "Refresh skills marketplace", title: "Refresh skills marketplace", disabled: refreshing, onClick: onRefresh, children: _jsx(Icon, { name: "refresh", size: 13 }) }) : null] }));
22
+ }
23
+ export default function SkillMarketplace({ projectCwd = "", results, popularResults = [], selectedSkill = null, installedSlugs = [], health = null, searching = false, refreshing = false, detailLoading = false, detailError = null, operationLoading = null, onSearch, onRefresh, onSelect, onClearSelection, onInstall, onScopeChange, }) {
24
+ const [query, setQuery] = useState("");
25
+ const [scope, setScope] = useState("user");
26
+ const displayResults = query.trim().length >= 2 ? results : popularResults;
27
+ const selectedResult = selectedSkill
28
+ ? displayResults.find((skill) => skill.id === selectedSkill.id) ?? results.find((skill) => skill.id === selectedSkill.id)
29
+ : null;
30
+ const selectedIsInstalled = selectedResult ? hasInstalledSkill(installedSlugs, selectedResult.skillId) : false;
31
+ useEffect(() => {
32
+ const normalized = query.trim();
33
+ const timer = window.setTimeout(() => onSearch(normalized.length >= 2 ? normalized : ""), normalized.length >= 2 ? 300 : 0);
34
+ return () => window.clearTimeout(timer);
35
+ }, [onSearch, query]);
36
+ const quickFilters = useMemo(() => ["react", "python", "security", "testing", "devops", "best practices"], []);
37
+ function changeScope(next) {
38
+ setScope(next);
39
+ onScopeChange?.(next);
40
+ }
41
+ function install(skill) {
42
+ if (toMarketplaceSkillSlug(skill.skillId))
43
+ onInstall(skill, scope, projectCwd || undefined);
44
+ }
45
+ return (_jsxs("section", { className: "flex min-h-0 flex-col gap-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [_jsx(RegistryStatus, { health: health, refreshing: refreshing, onRefresh: onRefresh }), _jsxs("label", { className: "flex min-w-[220px] flex-1 items-center gap-2 rounded-lg border border-line bg-panel px-3 text-text-3", children: [_jsx(Icon, { name: "search", size: 13 }), _jsx("input", { type: "search", "aria-label": "Search community skills", className: "h-9 min-w-0 flex-1 border-0 bg-transparent text-[13px] text-text outline-none placeholder:text-text-3", placeholder: "Search community skills", value: query, onChange: (event) => setQuery(event.target.value) })] }), _jsx("div", { className: "inline-flex rounded-lg border border-line bg-panel p-1", children: ["user", "project"].map((nextScope) => (_jsx("button", { type: "button", className: `rounded-md px-3 py-1.5 text-[12px] capitalize ${scope === nextScope ? "bg-panel-2 text-text" : "text-text-3 hover:text-text"}`, disabled: nextScope === "project" && !projectCwd, "aria-pressed": scope === nextScope, onClick: () => changeScope(nextScope), children: nextScope }, nextScope))) })] }), _jsx("div", { className: "flex flex-wrap gap-2", children: quickFilters.map((filter) => _jsx("button", { type: "button", className: `rounded-full border px-3 py-1 text-[11px] ${query === filter ? "border-accent/50 bg-accent/10 text-text" : "border-line text-text-3 hover:border-line-2 hover:text-text"}`, onClick: () => setQuery(filter), children: filter }, filter)) }), searching ? (_jsxs("div", { className: "flex items-center justify-center gap-2 py-10 text-[12px] text-text-3", role: "status", children: [_jsx(Icon, { name: "refresh", size: 14 }), " Searching skills marketplace\u2026"] })) : displayResults.length === 0 ? (_jsx(EmptyState, { icon: _jsx(Icon, { name: "skill", size: 18 }), title: query.trim().length >= 2 ? "No community skills found." : "No popular skills available.", action: onRefresh && query.trim().length < 2 ? _jsx("button", { type: "button", className: "rounded-lg border border-line px-3 py-1.5 text-[12px] text-text-2 hover:bg-panel", onClick: onRefresh, children: "Retry" }) : undefined, children: query.trim().length >= 2 ? "Try a broader search term." : "Refresh the marketplace or search by capability." })) : (_jsxs("div", { className: "flex min-h-[300px] min-w-0 gap-3", children: [_jsxs("div", { className: "w-[280px] shrink-0 space-y-2 overflow-y-auto pr-1", children: [_jsx("div", { className: "px-1 text-[11px] text-text-3", children: query.trim().length >= 2 ? `${results.length} results` : "Popular skills" }), displayResults.map((skill) => {
46
+ const installed = hasInstalledSkill(installedSlugs, skill.skillId);
47
+ const valid = toMarketplaceSkillSlug(skill.skillId) !== null;
48
+ const pending = operationLoading === skill.id;
49
+ return (_jsx("article", { className: `rounded-lg border px-3 py-2 ${selectedSkill?.id === skill.id ? "border-accent/50 bg-accent/5" : "border-line bg-panel hover:bg-panel-2"}`, children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsxs("button", { type: "button", className: "min-w-0 flex-1 text-left", onClick: () => onSelect(skill), children: [_jsx("strong", { className: "block truncate text-[13px] font-semibold text-text", children: skill.name }), _jsxs("span", { className: "mt-1 block truncate text-[11px] text-text-3", children: [skill.installs > 0 ? `${installCount(skill.installs)} installs · ` : "", skill.source] })] }), _jsx("button", { type: "button", className: `shrink-0 rounded-md px-2 py-1 text-[11px] font-semibold ${installed ? "bg-accent-2/10 text-accent-2" : "bg-accent text-on-accent"}`, disabled: installed || !valid || pending, title: !valid ? "The marketplace id cannot be converted to a safe local slug." : undefined, onClick: () => install(skill), children: pending ? "…" : installed ? "Installed" : valid ? "Install" : "Unavailable" })] }) }, skill.id));
50
+ })] }), _jsx("div", { className: "min-w-0 flex-1 overflow-y-auto", children: detailLoading ? (_jsxs("div", { className: "flex h-full items-center justify-center gap-2 rounded-lg border border-line bg-panel text-[12px] text-text-3", role: "status", children: [_jsx(Icon, { name: "refresh", size: 14 }), " Loading preview\u2026"] })) : detailError ? (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-2 rounded-lg border border-danger/40 bg-danger/5 p-6 text-center", children: [_jsx(Icon, { name: "alert", size: 20 }), _jsx("strong", { className: "text-[13px] text-text", children: "Skill preview unavailable." }), _jsx("span", { className: "max-w-sm text-[12px] text-text-3", children: detailError })] })) : selectedSkill ? (_jsxs("div", { className: "space-y-4 rounded-lg border border-line bg-panel p-4", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h3", { className: "truncate text-[14px] font-semibold text-text", children: selectedSkill.name }), _jsxs("div", { className: "mt-1 flex flex-wrap gap-2 text-[11px] text-text-3", children: [_jsx("span", { children: selectedSkill.source }), selectedSkill.installs > 0 ? _jsxs("span", { children: [installCount(selectedSkill.installs), " installs"] }) : null, selectedSkill.githubUrl ? _jsx("a", { className: "underline hover:text-text", href: selectedSkill.githubUrl, target: "_blank", rel: "noreferrer", children: "GitHub" }) : null, selectedSkill.skillsShUrl ? _jsx("a", { className: "underline hover:text-text", href: selectedSkill.skillsShUrl, target: "_blank", rel: "noreferrer", children: "skills.sh" }) : null] }), _jsx("p", { className: "mt-2 text-[12px] leading-5 text-text-2", children: selectedSkill.description || "No description provided." })] }), _jsx("button", { type: "button", "aria-label": "Close skill preview", className: "text-text-3 hover:text-text", onClick: () => onClearSelection?.(), children: _jsx(Icon, { name: "close", size: 14 }) })] }), _jsxs("div", { className: "border-t border-line pt-3", children: [_jsx("h4", { className: "mb-2 text-[11px] uppercase text-text-3", children: "SKILL.md preview" }), selectedSkill.content ? _jsx("pre", { className: "max-h-[420px] overflow-auto whitespace-pre-wrap break-words rounded-lg bg-bg p-3 font-mono text-[12px] leading-5 text-text-2", children: selectedSkill.content }) : _jsx("p", { className: "text-[12px] italic text-text-3", children: "No content preview supplied." })] }), _jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-line pt-3", children: [_jsxs("span", { className: "text-[11px] text-text-3", children: ["Install scope: ", scope] }), _jsxs("button", { type: "button", className: `inline-flex h-9 items-center gap-2 rounded-lg px-4 text-[12px] font-semibold ${selectedIsInstalled ? "border border-line bg-transparent text-text-3" : "bg-accent text-on-accent hover:opacity-90"}`, disabled: selectedIsInstalled || !selectedResult || toMarketplaceSkillSlug(selectedResult.skillId) === null || operationLoading === selectedResult?.id, onClick: () => selectedResult && install(selectedResult), children: [_jsx(Icon, { name: "download", size: 13 }), operationLoading === selectedResult?.id ? "Installing…" : selectedIsInstalled ? "Installed" : "Install skill"] })] })] })) : _jsx(EmptyState, { icon: _jsx(Icon, { name: "skill", size: 18 }), title: "Select a community skill.", children: "Choose a result to inspect its source, content, and install scope." }) })] }))] }));
51
+ }
@@ -0,0 +1,9 @@
1
+ import type { DaemonConfig } from "@rightkit/agent-ui-core/lib/types";
2
+ /**
3
+ * API-key accounts (issue #34). The key goes to the daemon once, which stores it
4
+ * in the OS credential store. It is never read back, so the field is write-only
5
+ * and clears as soon as the request is sent.
6
+ */
7
+ export default function ApiKeyAccounts({ config }: {
8
+ config?: DaemonConfig | null;
9
+ }): import("react").JSX.Element;
@@ -0,0 +1,67 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Select } from "@rightkit/react-shell/react";
3
+ import { useCallback, useEffect, useState } from "react";
4
+ import { addApiKeyAccount, fetchAccounts, removeApiKeyAccount, } from "@rightkit/agent-ui-core/lib/accounts/accounts";
5
+ const PROVIDERS = [
6
+ { id: "claude", label: "Claude (Anthropic API key)" },
7
+ { id: "openai", label: "OpenAI" },
8
+ ];
9
+ /**
10
+ * API-key accounts (issue #34). The key goes to the daemon once, which stores it
11
+ * in the OS credential store. It is never read back, so the field is write-only
12
+ * and clears as soon as the request is sent.
13
+ */
14
+ export default function ApiKeyAccounts({ config }) {
15
+ const [accounts, setAccounts] = useState([]);
16
+ const [label, setLabel] = useState("");
17
+ const [provider, setProvider] = useState(PROVIDERS[0].id);
18
+ const [apiKey, setApiKey] = useState("");
19
+ const [pending, setPending] = useState(false);
20
+ const [error, setError] = useState(null);
21
+ const refresh = useCallback(async () => {
22
+ if (!config)
23
+ return;
24
+ try {
25
+ setAccounts((await fetchAccounts(config)).filter((a) => a.kind === "api_key"));
26
+ }
27
+ catch {
28
+ setAccounts([]);
29
+ }
30
+ }, [config]);
31
+ useEffect(() => {
32
+ void refresh();
33
+ }, [refresh]);
34
+ const submit = async (event) => {
35
+ event.preventDefault();
36
+ if (!config || pending || !label.trim() || !apiKey.trim())
37
+ return;
38
+ const key = apiKey;
39
+ setApiKey("");
40
+ setPending(true);
41
+ setError(null);
42
+ try {
43
+ await addApiKeyAccount(config, { label: label.trim(), provider, apiKey: key });
44
+ setLabel("");
45
+ await refresh();
46
+ }
47
+ catch (err) {
48
+ setError(err instanceof Error ? err.message : "Could not add the API key");
49
+ }
50
+ finally {
51
+ setPending(false);
52
+ }
53
+ };
54
+ const remove = async (account) => {
55
+ if (!config)
56
+ return;
57
+ setError(null);
58
+ try {
59
+ await removeApiKeyAccount(config, account.id);
60
+ await refresh();
61
+ }
62
+ catch (err) {
63
+ setError(err instanceof Error ? err.message : "Could not remove the API key");
64
+ }
65
+ };
66
+ return (_jsxs("section", { className: "rounded-xl border border-line bg-panel p-4", "aria-label": "API key accounts", children: [_jsx("h4", { className: "text-[13px] font-semibold text-text", children: "API key accounts" }), _jsx("p", { className: "mb-3 text-[12px] text-text-3", children: "Keys are stored in the system credential store and are never shown again." }), _jsx("div", { className: "mb-3 flex flex-col gap-2", children: accounts.map((account) => (_jsxs("article", { className: "flex items-center gap-3 rounded-lg border border-line/70 px-3 py-2", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("strong", { className: "block truncate text-[12px] text-text", children: account.label }), _jsxs("span", { className: "block truncate text-[11px] text-text-3", children: [account.provider, " \u00B7 ", account.ready ? "key stored" : "key missing"] })] }), _jsx("button", { type: "button", "aria-label": `Remove ${account.label}`, className: "rounded border border-danger/40 px-2 py-1 text-[11px] text-danger", onClick: () => void remove(account), children: "Remove" })] }, account.id))) }), _jsxs("form", { className: "flex flex-wrap items-end gap-2", onSubmit: (e) => void submit(e), children: [_jsxs("label", { className: "flex flex-col gap-1 text-[11px] text-text-3", children: ["Label", _jsx("input", { type: "text", value: label, onChange: (e) => setLabel(e.target.value), autoComplete: "off", className: "rounded border border-line bg-transparent px-2 py-1 text-[12px] text-text" })] }), _jsxs("label", { className: "flex flex-col gap-1 text-[11px] text-text-3", children: ["Provider", _jsx(Select, { value: provider, options: [...PROVIDERS.map((p) => ({ value: p.id, label: p.label }))], onChange: (value) => setProvider(value), label: "Provider", className: "rounded border border-line bg-transparent px-2 py-1 text-[12px] text-text" })] }), _jsxs("label", { className: "flex min-w-[200px] flex-1 flex-col gap-1 text-[11px] text-text-3", children: ["API key", _jsx("input", { type: "password", value: apiKey, onChange: (e) => setApiKey(e.target.value), autoComplete: "off", spellCheck: false, className: "rounded border border-line bg-transparent px-2 py-1 text-[12px] text-text" })] }), _jsx("button", { type: "submit", disabled: pending || !config || !label.trim() || !apiKey.trim(), className: "rounded-lg border border-line px-3 py-1.5 text-[12px] text-text hover:bg-panel-2 disabled:opacity-50", children: "Add API key" })] }), error ? (_jsx("p", { role: "alert", className: "mt-2 text-[11px] text-danger", children: error })) : null] }));
67
+ }
@@ -0,0 +1 @@
1
+ export default function AppearanceTab(): import("react").JSX.Element;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Select, useTheme } from "@rightkit/react-shell/react";
3
+ import { PALETTES, useThemeStore } from "@rightkit/agent-ui-core/state/themeStore";
4
+ import AnalyticsConsentCard from "../analytics/AnalyticsConsentCard.js";
5
+ // Dark base names per Right Suite brand.md ("Dark bases (shared): slate
6
+ // (View/Code) · charcoal (Mail) · coffee (Heard/Voice) · espresso (Scrape)").
7
+ // Light bases are all pale off-white/cream ramps across the suite.
8
+ const DARK_BASE_LABEL = {
9
+ coderight: "graphite",
10
+ viewright: "slate",
11
+ heardright: "coffee",
12
+ scraperight: "espresso",
13
+ mailright: "charcoal",
14
+ };
15
+ const PALETTE_SWATCHES = [
16
+ { label: "Background", className: "bg-bg" },
17
+ { label: "Panel", className: "bg-panel" },
18
+ { label: "Border", className: "bg-line" },
19
+ { label: "Text", className: "bg-text" },
20
+ { label: "Accent", className: "bg-accent" },
21
+ ];
22
+ export default function AppearanceTab() {
23
+ const { resolved } = useTheme();
24
+ const paletteId = useThemeStore((state) => state.palette);
25
+ const setPalette = useThemeStore((state) => state.setPalette);
26
+ const baseLabel = resolved === "dark" ? DARK_BASE_LABEL[paletteId] : "off-white";
27
+ const activePalette = PALETTES.find((p) => p.id === paletteId);
28
+ return (_jsxs("section", { className: "flex max-w-[720px] flex-col gap-4 p-6", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h3", { className: "text-[16px] font-bold text-text", children: "Appearance" }), _jsx("p", { className: "text-[13px] text-text-2", children: "Keep the work surface calm while preserving the Right Suite identities." })] }), _jsxs("div", { className: "grid gap-3 rounded-lg border border-line bg-panel p-4", children: [_jsxs("label", { className: "flex flex-col gap-1 text-[13px] text-text-2", children: ["Palette", _jsx(Select, { value: paletteId, options: [...PALETTES.map((palette) => ({ value: palette.id, label: palette.label }))], onChange: (value) => setPalette(value), label: "Palette", className: "h-9 rounded-lg border border-line bg-bg px-3 text-[13px] text-text outline-none" })] }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-[13px] font-semibold text-text", children: "Base" }), _jsx("div", { className: "text-[12px] text-text-3", children: resolved === "dark" ? `Dark ${baseLabel}` : `Light ${baseLabel}` })] })] }), _jsxs("div", { className: "rounded-lg border border-line bg-panel p-4", children: [_jsx("div", { className: "flex items-center justify-between gap-3", children: _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-[13px] font-semibold text-text", children: "Palette preview" }), _jsxs("div", { className: "text-[12px] text-text-3", children: [activePalette?.label ?? "Default", " \u00B7 ", resolved === "dark" ? "dark" : "light", " ", "tokens"] })] }) }), _jsx("div", { className: "mt-3 grid grid-cols-5 gap-3", children: PALETTE_SWATCHES.map((swatch) => (_jsxs("div", { className: "flex flex-col items-center gap-1.5 min-w-0", children: [_jsx("span", { className: `h-10 w-10 rounded-lg border border-line ${swatch.className}` }), _jsx("span", { className: "text-[11px] text-text-3 truncate", children: swatch.label })] }, swatch.label))) })] }), _jsx(AnalyticsConsentCard, {}), _jsxs("div", { className: "rounded-lg border border-line bg-panel p-4", children: [_jsx("div", { className: "text-[13px] font-semibold text-text", children: "Typography" }), _jsxs("div", { className: "mt-2 flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [_jsx("span", { className: "font-display text-[15px] text-text", children: "Tanker" }), _jsx("span", { className: "text-[12px] text-text-3", children: "Display \u00B7 panel titles" })] }), _jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [_jsx("span", { className: "text-[15px] text-text", children: "General Sans" }), _jsx("span", { className: "text-[12px] text-text-3", children: "Body \u00B7 UI" })] }), _jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [_jsx("span", { className: "font-mono text-[15px] text-text", children: "JetBrains Mono" }), _jsx("span", { className: "text-[12px] text-text-3", children: "Code \u00B7 paths \u00B7 ids" })] })] })] })] }));
29
+ }
@@ -0,0 +1,14 @@
1
+ import { type ProBridgeStatus } from "@rightkit/agent-ui-core/state/proBridge";
2
+ type Tone = "ok" | "warn" | "muted";
3
+ /** One line for users. The keychain and Pro-resolution channels stay separate
4
+ * in the status payload; the line reports the worst of them so one failing
5
+ * channel never hides behind "On". */
6
+ export declare function summarizeAppleSync(status: ProBridgeStatus): {
7
+ text: string;
8
+ tone: Tone;
9
+ };
10
+ /** Consent toggle and status for Apple device Pro continuity. Renders nothing
11
+ * where iCloud Keychain does not exist (the native command reports
12
+ * `supported: false` off macOS). */
13
+ export default function AppleSyncPanel(): import("react").JSX.Element | null;
14
+ export {};
@@ -0,0 +1,65 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from "react";
3
+ import { getProBridgeStatus, reasonOf, setProBridgeSyncEnabled, } from "@rightkit/agent-ui-core/state/proBridge";
4
+ /** One line for users. The keychain and Pro-resolution channels stay separate
5
+ * in the status payload; the line reports the worst of them so one failing
6
+ * channel never hides behind "On". */
7
+ export function summarizeAppleSync(status) {
8
+ const enabled = status.keychainSyncEnabled || status.proResolutionEnabled;
9
+ if (!enabled)
10
+ return { text: "Off", tone: "muted" };
11
+ if (status.keychainSyncEnabled && !status.keychainReady) {
12
+ return { text: status.keychainDetail || "Waiting on iCloud Keychain", tone: "warn" };
13
+ }
14
+ if (status.proResolutionStatus === "blocked_no_keychain") {
15
+ return { text: "Waiting on iCloud Keychain", tone: "warn" };
16
+ }
17
+ if (status.keychainSyncEnabled && status.proResolutionEnabled)
18
+ return { text: "On", tone: "ok" };
19
+ return { text: "Partly on", tone: "warn" };
20
+ }
21
+ const TONE_CLASS = {
22
+ ok: "text-accent-2",
23
+ warn: "text-warning",
24
+ muted: "text-text-3",
25
+ };
26
+ /** Consent toggle and status for Apple device Pro continuity. Renders nothing
27
+ * where iCloud Keychain does not exist (the native command reports
28
+ * `supported: false` off macOS). */
29
+ export default function AppleSyncPanel() {
30
+ const [status, setStatus] = useState(null);
31
+ const [busy, setBusy] = useState(false);
32
+ const [error, setError] = useState(null);
33
+ const refresh = useCallback(async () => {
34
+ try {
35
+ setStatus(await getProBridgeStatus());
36
+ }
37
+ catch {
38
+ setStatus(null);
39
+ }
40
+ }, []);
41
+ useEffect(() => {
42
+ void refresh();
43
+ }, [refresh]);
44
+ if (!status || !status.supported)
45
+ return null;
46
+ const summary = summarizeAppleSync(status);
47
+ const checked = status.keychainSyncEnabled && status.proResolutionEnabled;
48
+ async function onToggle(next) {
49
+ if (busy)
50
+ return;
51
+ setBusy(true);
52
+ setError(null);
53
+ try {
54
+ await setProBridgeSyncEnabled(next);
55
+ }
56
+ catch (err) {
57
+ setError(reasonOf(err, "Could not change Apple device sync"));
58
+ }
59
+ finally {
60
+ await refresh();
61
+ setBusy(false);
62
+ }
63
+ }
64
+ return (_jsxs("div", { className: "rounded-lg border border-line bg-panel p-4", "data-testid": "apple-sync-panel", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 flex-wrap", children: [_jsxs("div", { className: "max-w-xl", children: [_jsx("h3", { className: "text-[14px] font-semibold text-text", children: "Apple devices" }), _jsx("p", { className: "text-[12px] text-text-3", children: "Use Pro on your other Apple devices through your private iCloud Keychain. Only an opaque sign-in token is shared, never your license key. Turning this off stops this device only and never deletes iCloud data." })] }), _jsxs("label", { className: "inline-flex items-center gap-2 text-[13px] text-text-2", children: [_jsx("span", { className: TONE_CLASS[summary.tone], children: summary.text }), _jsx("input", { type: "checkbox", "aria-label": "Sync Pro across Apple devices", checked: checked, disabled: busy, onChange: (event) => void onToggle(event.target.checked) })] })] }), status.consumptionSuppressed ? (_jsx("p", { className: "mt-3 text-[12px] text-text-3", children: "Pro sign-in is paused on this device because the license was removed here. Turning sync back on clears this." })) : null, error ? _jsx("div", { className: "mt-3 text-[12px] text-danger", children: error }) : null] }));
65
+ }
@@ -0,0 +1,9 @@
1
+ import type { CliDelegationPolicy } from "@rightkit/agent-ui-core/lib/types/cliProviders";
2
+ export interface ChildProviderPolicyProps {
3
+ policy: CliDelegationPolicy;
4
+ onChange: (policy: CliDelegationPolicy) => void;
5
+ /** Optional hint to render above the controls when the policy is
6
+ * inconsistent (e.g. default child is no longer allowed). */
7
+ warning?: string | null;
8
+ }
9
+ export default function ChildProviderPolicy({ policy, onChange, warning, }: ChildProviderPolicyProps): import("react").JSX.Element;
@@ -0,0 +1,84 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Per-session child provider policy editor.
4
+ *
5
+ * The `CliDelegationPolicy` shape is the model the resolver in
6
+ * `engine/crates/driver_host/src/cli_runtime/policy.rs` consumes to
7
+ * decide whether a root may spawn a child, whether a child may spawn
8
+ * a child, and whether a model-emitted `providerHint` is honored.
9
+ * This component is the desktop surface — it must mirror the Rust
10
+ * data model exactly so round-tripping through the daemon keeps the
11
+ * cap of allowed children, the depth limit, and the default-child
12
+ * provider in lock-step.
13
+ *
14
+ * The policy is per-session rather than global so the user can opt
15
+ * each session into a different mix of providers. Kiro is intentionally
16
+ * absent from the toggle list.
17
+ */
18
+ import { useMemo } from "react";
19
+ import { Checkbox, Select, Slider } from "@rightkit/react-shell/react";
20
+ import Icon from "@rightkit/agent-ui-core/components/Icon";
21
+ import { CLI_PROVIDER_IDS, getProviderDisplay, isLaunchableProvider, } from "@rightkit/agent-ui-core/lib/cliProviders";
22
+ import { hostAppName } from "../../../appName.js";
23
+ const MAX_DEPTH_MIN = 1;
24
+ const MAX_DEPTH_MAX = 4;
25
+ const DEFAULT_MAX_DEPTH = 2;
26
+ export default function ChildProviderPolicy({ policy, onChange, warning, }) {
27
+ const allowed = useMemo(() => new Set(policy.allowedChildProviders), [policy.allowedChildProviders]);
28
+ const toggleAllowed = (id) => {
29
+ if (!isLaunchableProvider(id))
30
+ return;
31
+ const next = new Set(allowed);
32
+ if (next.has(id))
33
+ next.delete(id);
34
+ else
35
+ next.add(id);
36
+ let defaultChild = policy.defaultChildProvider;
37
+ if (defaultChild && !next.has(defaultChild))
38
+ defaultChild = null;
39
+ onChange({
40
+ ...policy,
41
+ allowedChildProviders: Array.from(next),
42
+ defaultChildProvider: defaultChild,
43
+ });
44
+ };
45
+ const setDefaultChild = (raw) => {
46
+ if (raw === "") {
47
+ onChange({ ...policy, defaultChildProvider: null });
48
+ return;
49
+ }
50
+ if (isLaunchableProvider(raw) && allowed.has(raw)) {
51
+ onChange({ ...policy, defaultChildProvider: raw });
52
+ }
53
+ };
54
+ const setMaxDepth = (parsed) => {
55
+ const clamped = Math.max(MAX_DEPTH_MIN, Math.min(MAX_DEPTH_MAX, Math.round(parsed)));
56
+ onChange({ ...policy, maxDepth: clamped });
57
+ };
58
+ const setPermitHints = (allowed) => {
59
+ onChange({ ...policy, permitModelProviderHints: allowed });
60
+ };
61
+ const reset = () => {
62
+ onChange({
63
+ allowedChildProviders: [],
64
+ defaultChildProvider: null,
65
+ permitModelProviderHints: true,
66
+ maxDepth: DEFAULT_MAX_DEPTH,
67
+ maxParallelChildren: 4,
68
+ maxParallelPerProvider: {},
69
+ });
70
+ };
71
+ return (_jsxs("section", { className: "flex flex-col gap-4", children: [_jsxs("header", { className: "flex flex-col gap-1", children: [_jsx("h3", { className: "text-[16px] font-bold text-text", children: "Child provider policy" }), _jsxs("p", { className: "text-[12px] text-text-3", children: ["Per-session rules that decide which authenticated CLIs a root or child agent may delegate to. ", hostAppName(), " stays the authority for tools, approvals, and effects."] })] }), warning ? (_jsx("p", { className: "rounded-md border border-warning/40 bg-warning/10 p-2 text-[12px] text-warning", children: warning })) : null, _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-[12px] font-semibold text-text-2", children: "Max delegation depth" }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Slider, { label: "Max delegation depth", min: MAX_DEPTH_MIN, max: MAX_DEPTH_MAX, step: 1, value: policy.maxDepth, onChange: setMaxDepth, format: (depth) => `depth ${depth}`, className: "w-full max-w-sm" }), _jsx("span", { className: "font-mono text-[12px] tabular-nums text-text-2", "data-testid": "child-policy-max-depth-value", children: policy.maxDepth }), _jsxs("span", { className: "text-[11px] text-text-3", children: ["(", policy.maxDepth === 1
72
+ ? "root only"
73
+ : `root + child${policy.maxDepth > 2 ? " + deeper" : ""}`, ")"] })] }), _jsx("p", { className: "text-[11px] text-text-3", children: "Depth 1 forbids any child from spawning another child. The default of 2 permits one further level of delegation." })] }), _jsxs("fieldset", { className: "flex flex-col gap-2", children: [_jsx("legend", { className: "text-[12px] font-semibold text-text-2", children: "Allowed child providers" }), _jsx("p", { className: "text-[11px] text-text-3", children: "Toggle the providers this session may delegate to. Kiro is not available." }), _jsx("ul", { className: "grid gap-1.5 sm:grid-cols-2", children: CLI_PROVIDER_IDS.map((id) => {
74
+ const display = getProviderDisplay(id);
75
+ const checked = allowed.has(id);
76
+ return (_jsx("li", { children: _jsx(Checkbox, { checked: checked, onChange: () => toggleAllowed(id), className: "rounded-md border border-line bg-panel px-2.5 py-1.5 hover:border-accent", label: _jsxs("span", { className: "flex-1 truncate", children: [_jsx("strong", { className: "text-text", children: display.displayName }), _jsx("span", { className: "ml-2 text-[11px] text-text-3", children: display.shortLabel })] }) }) }, id));
77
+ }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-[12px] font-semibold text-text-2", children: "Default child provider" }), _jsx(Select, { label: "Default child provider", value: policy.defaultChildProvider ?? "", onChange: setDefaultChild, className: "w-full max-w-sm", options: [
78
+ { value: "", label: `No default — fall back to ${hostAppName()}-native` },
79
+ ...CLI_PROVIDER_IDS.filter((id) => allowed.has(id)).map((id) => ({
80
+ value: id,
81
+ label: getProviderDisplay(id).displayName,
82
+ })),
83
+ ] }), _jsx("p", { className: "text-[11px] text-text-3", children: "When the model does not name a child, the root session hands the work to this provider." })] }), _jsx("div", { className: "flex flex-col gap-2", children: _jsx(Checkbox, { checked: policy.permitModelProviderHints, onChange: setPermitHints, label: _jsxs("span", { children: ["Permit model-emitted provider hints", _jsxs("span", { className: "ml-2 text-[11px] text-text-3", children: ["Honors ", _jsx("code", { children: "providerHint" }), " calls only when the hinted provider is in", _jsx("code", { children: " allowed_child_providers" }), "."] })] }) }) }), _jsx("div", { className: "flex items-center justify-end gap-2", children: _jsxs("button", { type: "button", onClick: reset, className: "cr-control-pill inline-flex items-center gap-1.5 rounded-md border border-line bg-panel-2 px-3 py-1.5 text-[12px] font-semibold text-text-2 hover:border-danger/60 hover:text-danger", children: [_jsx(Icon, { name: "refresh", size: 11 }), "Reset to defaults"] }) })] }));
84
+ }
@@ -0,0 +1,6 @@
1
+ import type { DaemonConfig, SessionRow } from "@rightkit/agent-ui-core/lib/types";
2
+ export interface ConductorsTabProps {
3
+ config: DaemonConfig | null;
4
+ selectedSession: SessionRow | null;
5
+ }
6
+ export default function ConductorsTab({ config, selectedSession }: ConductorsTabProps): import("react").JSX.Element;