- Added `nuqs` package for enhanced query handling. - Introduced new routes for tools, including catalog, connections, policies, and editor. - Updated navigation links to direct to specific tool routes instead of generic paths. - Refactored tool-related components to accommodate new routing and state management. - Adjusted type definitions to reflect changes in tool policy management, replacing role-based terminology with policy-centric terms.
Unified · Split
| @@ -106,6 +106,7 @@ · 105 unmodified lines | |||
| 106 | "lucide-react": "^0.563.0", | 106 | "lucide-react": "^0.563.0", |
| 107 | "monaco-editor": "^0.55.1", | 107 | "monaco-editor": "^0.55.1", |
| 108 | "next-themes": "^0.4.6", | 108 | "next-themes": "^0.4.6", |
| 109 | "nuqs": "^2.8.8", | ||
| 109 | "radix-ui": "^1.4.3", | 110 | "radix-ui": "^1.4.3", |
| 110 | "react": "19.2.3", | 111 | "react": "19.2.3", |
| 111 | "react-dom": "19.2.3", | 112 | "react-dom": "19.2.3", |
| @@ -2049,6 +2050,8 @@ · 1937 unmodified lines | |||
| 2049 | 2050 | ||
| 2050 | "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], | 2051 | "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], |
| 2051 | 2052 | ||
| 2053 | "nuqs": ["nuqs@2.8.8", "", { "dependencies": { "@standard-schema/spec": "1.0.0" }, "peerDependencies": { "@remix-run/react": ">=2", "@tanstack/react-router": "^1", "next": ">=14.2.0", "react": ">=18.2.0 || ^19.0.0-0", "react-router": "^5 || ^6 || ^7", "react-router-dom": "^5 || ^6 || ^7" }, "optionalPeers": ["@remix-run/react", "@tanstack/react-router", "next", "react-router", "react-router-dom"] }, "sha512-LF5sw9nWpHyPWzMMu9oho3r9C5DvkpmBIg4LQN78sexIzGaeRx8DWr0uy3YiFx5i2QGZN1Qqcb+OAtEVRa2bnA=="], | ||
| 2054 | |||
| 2052 | "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], | 2055 | "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], |
| 2053 | 2056 | ||
| 2054 | "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], | 2057 | "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], |
| @@ -2687,6 +2690,8 @@ · 632 unmodified lines | |||
| 2687 | 2690 | ||
| 2688 | "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], | 2691 | "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], |
| 2689 | 2692 | ||
| 2693 | "nuqs/@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], | ||
| 2694 | |||
| 2690 | "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], | 2695 | "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], |
| 2691 | 2696 | ||
| 2692 | "parse-json/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], | 2697 | "parse-json/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], |
| @@ -17,11 +17,11 @@ · 16 unmodified lines | |||
| 17 | "@executor/ui": "workspace:*", | 17 | "@executor/ui": "workspace:*", |
| 18 | "@monaco-editor/react": "^4.7.0", | 18 | "@monaco-editor/react": "^4.7.0", |
| 19 | "@streamdown/code": "^1.0.1", | 19 | "@streamdown/code": "^1.0.1", |
| 20 | "@tanstack/react-query": "^5.80.7", | ||
| 20 | "@tanstack/react-router": "^1.132.0", | 21 | "@tanstack/react-router": "^1.132.0", |
| 21 | "@tanstack/react-start": "^1.132.0", | 22 | "@tanstack/react-start": "^1.132.0", |
| 22 | "@tanstack/router-plugin": "^1.132.0", | ||
| 23 | "@tanstack/react-query": "^5.80.7", | ||
| 24 | "@tanstack/react-virtual": "^3.13.18", | 23 | "@tanstack/react-virtual": "^3.13.18", |
| 24 | "@tanstack/router-plugin": "^1.132.0", | ||
| 25 | "@workos/authkit-tanstack-react-start": "^0.5.0", | 25 | "@workos/authkit-tanstack-react-start": "^0.5.0", |
| 26 | "better-result": "^2.7.0", | 26 | "better-result": "^2.7.0", |
| 27 | "class-variance-authority": "^0.7.1", | 27 | "class-variance-authority": "^0.7.1", |
| @@ -32,6 +32,7 @@ · 4 unmodified lines | |||
| 32 | "lucide-react": "^0.563.0", | 32 | "lucide-react": "^0.563.0", |
| 33 | "monaco-editor": "^0.55.1", | 33 | "monaco-editor": "^0.55.1", |
| 34 | "next-themes": "^0.4.6", | 34 | "next-themes": "^0.4.6", |
| 35 | "nuqs": "^2.8.8", | ||
| 35 | "radix-ui": "^1.4.3", | 36 | "radix-ui": "^1.4.3", |
| 36 | "react": "19.2.3", | 37 | "react": "19.2.3", |
| 37 | "react-dom": "19.2.3", | 38 | "react-dom": "19.2.3", |
| @@ -97,7 +97,7 @@ function ApprovalCard({ · 96 unmodified lines | |||
| 97 | variant="ghost" | 97 | variant="ghost" |
| 98 | size="sm" | 98 | size="sm" |
| 99 | className="h-7 text-[11px]" | 99 | className="h-7 text-[11px]" |
| 100 | onClick={() => navigate("/tools?tab=editor")} | 100 | onClick={() => navigate("/tools/editor")} |
| 101 | > | 101 | > |
| 102 | Open editor | 102 | Open editor |
| 103 | </Button> | 103 | </Button> |
| @@ -60,7 +60,7 @@ export function MenubarMvpView() { · 59 unmodified lines | |||
| 60 | </p> | 60 | </p> |
| 61 | </div> | 61 | </div> |
| 62 | <div className="flex items-center gap-2"> | 62 | <div className="flex items-center gap-2"> |
| 63 | <Button size="sm" variant="outline" className="h-8 text-xs" onClick={() => navigate("/tools?tab=editor")}> | 63 | <Button size="sm" variant="outline" className="h-8 text-xs" onClick={() => navigate("/tools/editor")}> |
| 64 | Open Editor | 64 | Open Editor |
| 65 | </Button> | 65 | </Button> |
| 66 | <Button size="sm" variant="outline" className="h-8 text-xs" onClick={() => navigate("/approvals")}> | 66 | <Button size="sm" variant="outline" className="h-8 text-xs" onClick={() => navigate("/approvals")}> |
| @@ -112,7 +112,7 @@ export function MenubarMvpView() { · 45 unmodified lines | |||
| 112 | <Sparkles className="h-4 w-4 text-primary" /> | 112 | <Sparkles className="h-4 w-4 text-primary" /> |
| 113 | Recent AI tasks | 113 | Recent AI tasks |
| 114 | </p> | 114 | </p> |
| 115 | <Button size="sm" variant="outline" className="h-8 text-xs" onClick={() => navigate("/tools?tab=editor")}>Open Editor</Button> | 115 | <Button size="sm" variant="outline" className="h-8 text-xs" onClick={() => navigate("/tools/editor")}>Open Editor</Button> |
| 116 | </div> | 116 | </div> |
| 117 | 117 | ||
| 118 | {tasksLoading ? ( | 118 | {tasksLoading ? ( |
| @@ -130,7 +130,7 @@ export function MenubarMvpView() { · 11 unmodified lines | |||
| 130 | key={task.id} | 130 | key={task.id} |
| 131 | type="button" | 131 | type="button" |
| 132 | className="flex w-full items-center justify-between rounded border border-border bg-background px-2.5 py-2 text-left hover:bg-accent" | 132 | className="flex w-full items-center justify-between rounded border border-border bg-background px-2.5 py-2 text-left hover:bg-accent" |
| 133 | onClick={() => navigate("/tools?tab=editor")} | 133 | onClick={() => navigate("/tools/editor")} |
| 134 | > | 134 | > |
| 135 | <span className="min-w-0"> | 135 | <span className="min-w-0"> |
| 136 | <span className="block truncate text-[11px] font-mono text-foreground">{task.id}</span> | 136 | <span className="block truncate text-[11px] font-mono text-foreground">{task.id}</span> |
| @@ -51,9 +51,17 @@ interface ToolExplorerProps { · 50 unmodified lines | |||
| 51 | loadingSources?: string[]; | 51 | loadingSources?: string[]; |
| 52 | onLoadToolDetails?: (toolPaths: string[]) => Promise<Record<string, Pick<ToolDescriptor, "path" | "description" | "display" | "typing">>>; | 52 | onLoadToolDetails?: (toolPaths: string[]) => Promise<Record<string, Pick<ToolDescriptor, "path" | "description" | "display" | "typing">>>; |
| 53 | warnings?: string[]; | 53 | warnings?: string[]; |
| 54 | initialSource?: string | null; | 54 | activeSource: string | null; |
| 55 | activeSource?: string | null; | 55 | searchValue: string; |
| 56 | onActiveSourceChange?: (source: string | null) => void; | 56 | filterApprovalValue: FilterApproval; |
| 57 | focusedToolPathValue: string | null; | ||
| 58 | selectedToolPathsValue: string[]; | ||
| 59 | focusedSourceNameValue: string | null; | ||
| 60 | onSearchValueChange: (value: string) => void; | ||
| 61 | onFilterApprovalValueChange: (filter: FilterApproval) => void; | ||
| 62 | onFocusedToolPathChange: (toolPath: string | null) => void; | ||
| 63 | onSelectedToolPathsChange: (toolPaths: string[]) => void; | ||
| 64 | onFocusedSourceNameChange: (sourceName: string | null) => void; | ||
| 57 | /** @deprecated No longer used — source sidebar is merged into the tool list panel. */ | 65 | /** @deprecated No longer used — source sidebar is merged into the tool list panel. */ |
| 58 | showSourceSidebar?: boolean; | 66 | showSourceSidebar?: boolean; |
| 59 | onSourceAdded?: (source: ToolSourceRecord) => void; | 67 | onSourceAdded?: (source: ToolSourceRecord) => void; |
| @@ -79,9 +87,17 @@ export function ToolExplorer({ · 19 unmodified lines | |||
| 79 | loadingSources = [], | 87 | loadingSources = [], |
| 80 | onLoadToolDetails, | 88 | onLoadToolDetails, |
| 81 | warnings = [], | 89 | warnings = [], |
| 82 | initialSource = null, | ||
| 83 | activeSource, | 90 | activeSource, |
| 84 | onActiveSourceChange, | 91 | searchValue, |
| 92 | filterApprovalValue, | ||
| 93 | focusedToolPathValue, | ||
| 94 | selectedToolPathsValue, | ||
| 95 | focusedSourceNameValue, | ||
| 96 | onSearchValueChange, | ||
| 97 | onFilterApprovalValueChange, | ||
| 98 | onFocusedToolPathChange, | ||
| 99 | onSelectedToolPathsChange, | ||
| 100 | onFocusedSourceNameChange, | ||
| 85 | onSourceAdded, | 101 | onSourceAdded, |
| 86 | sourceDialogMeta, | 102 | sourceDialogMeta, |
| 87 | sourceAuthProfiles, | 103 | sourceAuthProfiles, |
| @@ -106,21 +122,45 @@ export function ToolExplorer({ · 18 unmodified lines | |||
| 106 | return description.length > 0 || hasInputHint || hasOutputHint || hasSchemas; | 122 | return description.length > 0 || hasInputHint || hasOutputHint || hasSchemas; |
| 107 | }, []); | 123 | }, []); |
| 108 | 124 | ||
| 109 | const [searchInput, setSearchInput] = useState(""); | ||
| 110 | const search = useDeferredValue(searchInput); | ||
| 111 | const [internalActiveSource] = useState<string | null>(initialSource); | ||
| 112 | const [selectedKeys, setSelectedKeys] = useState<Set<string>>(new Set()); | ||
| 113 | const [filterApproval, setFilterApproval] = useState<FilterApproval>("all"); | ||
| 114 | const [toolDetailsByPath, setToolDetailsByPath] = useState<Record<string, Pick<ToolDescriptor, "path" | "description" | "display" | "typing">>>({}); | 125 | const [toolDetailsByPath, setToolDetailsByPath] = useState<Record<string, Pick<ToolDescriptor, "path" | "description" | "display" | "typing">>>({}); |
| 115 | const [loadingDetailPaths, setLoadingDetailPaths] = useState<Set<string>>(new Set()); | 126 | const [loadingDetailPaths, setLoadingDetailPaths] = useState<Set<string>>(new Set()); |
| 116 | const [focusedToolPath, setFocusedToolPath] = useState<string | null>(null); | ||
| 117 | const [focusedSourceName, setFocusedSourceName] = useState<string | null>(null); | ||
| 118 | // "new" = add source form, ToolSourceRecord = edit/view existing source, null = no source panel | 127 | // "new" = add source form, ToolSourceRecord = edit/view existing source, null = no source panel |
| 119 | const [formSource, setFormSource] = useState<"new" | ToolSourceRecord | null>(null); | 128 | const [formSource, setFormSource] = useState<"new" | ToolSourceRecord | null>(null); |
| 120 | const toolListRef = useRef<HTMLDivElement>(null); | 129 | const toolListRef = useRef<HTMLDivElement>(null); |
| 121 | const toolListScrollContainerId = "tool-explorer-toollist-scroll"; | 130 | const toolListScrollContainerId = "tool-explorer-toollist-scroll"; |
| 122 | const resolvedActiveSource = | 131 | const resolvedActiveSource = activeSource; |
| 123 | activeSource === undefined ? internalActiveSource : activeSource; | 132 | const searchInput = searchValue; |
| 133 | const filterApproval = filterApprovalValue; | ||
| 134 | const focusedToolPath = focusedToolPathValue; | ||
| 135 | const selectedKeys = useMemo(() => new Set(selectedToolPathsValue), [selectedToolPathsValue]); | ||
| 136 | const focusedSourceName = focusedSourceNameValue; | ||
| 137 | const search = useDeferredValue(searchInput); | ||
| 138 | |||
| 139 | const setSearchInput = useCallback((value: string) => { | ||
| 140 | onSearchValueChange(value); | ||
| 141 | }, [onSearchValueChange]); | ||
| 142 | |||
| 143 | const setFilterApproval = useCallback((nextFilter: FilterApproval) => { | ||
| 144 | onFilterApprovalValueChange(nextFilter); | ||
| 145 | }, [onFilterApprovalValueChange]); | ||
| 146 | |||
| 147 | const setFocusedToolPath = useCallback((toolPath: string | null) => { | ||
| 148 | onFocusedToolPathChange(toolPath); | ||
| 149 | }, [onFocusedToolPathChange]); | ||
| 150 | |||
| 151 | const setSelectedKeys = useCallback((next: Set<string>) => { | ||
| 152 | onSelectedToolPathsChange(Array.from(next)); | ||
| 153 | }, [onSelectedToolPathsChange]); | ||
| 154 | |||
| 155 | const setFocusedSourceName = useCallback((sourceName: string | null) => { | ||
| 156 | onFocusedSourceNameChange(sourceName); | ||
| 157 | }, [onFocusedSourceNameChange]); | ||
| 158 | |||
| 159 | const setSourceFocusState = useCallback((sourceName: string | null, sourceRecord: ToolSourceRecord | null) => { | ||
| 160 | setFocusedSourceName(sourceName); | ||
| 161 | setFormSource(sourceRecord); | ||
| 162 | setFocusedToolPath(null); | ||
| 163 | }, [setFocusedSourceName, setFocusedToolPath]); | ||
| 124 | 164 | ||
| 125 | const hydratedTools = useMemo(() => { | 165 | const hydratedTools = useMemo(() => { |
| 126 | if (Object.keys(toolDetailsByPath).length === 0) { | 166 | if (Object.keys(toolDetailsByPath).length === 0) { |
| @@ -212,21 +252,30 @@ export function ToolExplorer({ · 85 unmodified lines | |||
| 212 | return map; | 252 | return map; |
| 213 | }, [visibleSources]); | 253 | }, [visibleSources]); |
| 214 | 254 | ||
| 255 | useEffect(() => { | ||
| 256 | const resolvedSource = focusedSourceNameValue | ||
| 257 | ? sourceByName.get(focusedSourceNameValue) ?? null | ||
| 258 | : null; | ||
| 259 | |||
| 260 | setFormSource(resolvedSource); | ||
| 261 | if (focusedSourceNameValue) { | ||
| 262 | setFocusedToolPath(null); | ||
| 263 | } | ||
| 264 | }, [focusedSourceNameValue, sourceByName, setFocusedToolPath]); | ||
| 265 | |||
| 215 | const toggleSelectTool = useCallback( | 266 | const toggleSelectTool = useCallback( |
| 216 | (path: string, e: React.MouseEvent) => { | 267 | (path: string, e: React.MouseEvent) => { |
| 217 | e.stopPropagation(); | 268 | e.stopPropagation(); |
| 218 | e.preventDefault(); | 269 | e.preventDefault(); |
| 219 | setSelectedKeys((prev) => { | 270 | const next = new Set(selectedKeys); |
| 220 | const next = new Set(prev); | 271 | if (next.has(path)) { |
| 221 | if (next.has(path)) { | 272 | next.delete(path); |
| 222 | next.delete(path); | 273 | } else { |
| 223 | } else { | 274 | next.add(path); |
| 224 | next.add(path); | 275 | } |
| 225 | } | 276 | setSelectedKeys(next); |
| 226 | return next; | ||
| 227 | }); | ||
| 228 | }, | 277 | }, |
| 229 | [], | 278 | [selectedKeys, setSelectedKeys], |
| 230 | ); | 279 | ); |
| 231 | 280 | ||
| 232 | const clearSelection = useCallback(() => { | 281 | const clearSelection = useCallback(() => { |
| @@ -280,14 +329,11 @@ export function ToolExplorer({ · 47 unmodified lines | |||
| 280 | setFocusedSourceName(null); | 329 | setFocusedSourceName(null); |
| 281 | setFormSource(null); | 330 | setFormSource(null); |
| 282 | void maybeLoadToolDetails(tool, true); | 331 | void maybeLoadToolDetails(tool, true); |
| 283 | }, [maybeLoadToolDetails]); | 332 | }, [maybeLoadToolDetails, setFocusedSourceName]); |
| 284 | 333 | ||
| 285 | const handleSourceClick = useCallback((sourceName: string) => { | 334 | const handleSourceClick = useCallback((sourceName: string) => { |
| 286 | const source = sourceByName.get(sourceName); | 335 | const source = sourceByName.get(sourceName); |
| 287 | setFocusedSourceName(sourceName); | 336 | setSourceFocusState(sourceName, source ?? null); |
| 288 | setFocusedToolPath(null); | ||
| 289 | // Open the source form panel directly — no separate "view" mode | ||
| 290 | setFormSource(source ?? null); | ||
| 291 | // Ensure the source group is expanded | 337 | // Ensure the source group is expanded |
| 292 | setExpandedKeys((prev) => { | 338 | setExpandedKeys((prev) => { |
| 293 | const key = `source:${sourceName}`; | 339 | const key = `source:${sourceName}`; |
| @@ -299,10 +345,9 @@ export function ToolExplorer({ · 5 unmodified lines | |||
| 299 | }, [sourceByName]); | 345 | }, [sourceByName]); |
| 300 | 346 | ||
| 301 | const handleAddSource = useCallback(() => { | 347 | const handleAddSource = useCallback(() => { |
| 348 | setSourceFocusState(null, null); | ||
| 302 | setFormSource("new"); | 349 | setFormSource("new"); |
| 303 | setFocusedToolPath(null); | 350 | }, [setFocusedSourceName, setSourceFocusState]); |
| 304 | setFocusedSourceName(null); | ||
| 305 | }, []); | ||
| 306 | 351 | ||
| 307 | const handleSourceFormClose = useCallback(() => { | 352 | const handleSourceFormClose = useCallback(() => { |
| 308 | setFormSource(null); | 353 | setFormSource(null); |
| @@ -311,10 +356,8 @@ export function ToolExplorer({ · 2 unmodified lines | |||
| 311 | const handleSourceFormAdded = useCallback((source: ToolSourceRecord) => { | 356 | const handleSourceFormAdded = useCallback((source: ToolSourceRecord) => { |
| 312 | onSourceAdded?.(source); | 357 | onSourceAdded?.(source); |
| 313 | // After adding, show the new source in the form | 358 | // After adding, show the new source in the form |
| 314 | setFormSource(null); | 359 | setSourceFocusState(source.name, source); |
| 315 | setFocusedSourceName(source.name); | 360 | }, [onSourceAdded, setSourceFocusState]); |
| 316 | setFocusedToolPath(null); | ||
| 317 | }, [onSourceAdded]); | ||
| 318 | 361 | ||
| 319 | const focusedTool = useMemo(() => { | 362 | const focusedTool = useMemo(() => { |
| 320 | if (!focusedToolPath) return null; | 363 | if (!focusedToolPath) return null; |
| @@ -328,7 +371,7 @@ export function ToolExplorer({ · 7 unmodified lines | |||
| 328 | 371 | ||
| 329 | // Auto-focus first tool when tools arrive | 372 | // Auto-focus first tool when tools arrive |
| 330 | useEffect(() => { | 373 | useEffect(() => { |
| 331 | if (!focusedToolPath && searchedTools.length > 0) { | 374 | if (!focusedToolPath && !focusedSourceName && searchedTools.length > 0) { |
| 332 | const first = searchedTools[0]; | 375 | const first = searchedTools[0]; |
| 333 | setFocusedToolPath(first.path); | 376 | setFocusedToolPath(first.path); |
| 334 | void maybeLoadToolDetails(first, true); | 377 | void maybeLoadToolDetails(first, true); |
| @@ -336,6 +379,16 @@ export function ToolExplorer({ · 1 unmodified line | |||
| 336 | // eslint-disable-next-line react-hooks/exhaustive-deps | 379 | // eslint-disable-next-line react-hooks/exhaustive-deps |
| 337 | }, [searchedTools.length > 0]); | 380 | }, [searchedTools.length > 0]); |
| 338 | 381 | ||
| 382 | useEffect(() => { | ||
| 383 | if (!focusedToolPath || searchedTools.length === 0) { | ||
| 384 | return; | ||
| 385 | } | ||
| 386 | |||
| 387 | if (!searchedTools.some((tool) => tool.path === focusedToolPath)) { | ||
| 388 | setFocusedToolPath(null); | ||
| 389 | } | ||
| 390 | }, [focusedToolPath, searchedTools]); | ||
| 391 | |||
| 339 | const loadingRows = useMemo(() => { | 392 | const loadingRows = useMemo(() => { |
| 340 | if (search.length > 0) return []; | 393 | if (search.length > 0) return []; |
| 341 | const visibleLoadingSources = loadingSourceSet.size === 0 | 394 | const visibleLoadingSources = loadingSourceSet.size === 0 |
| @@ -53,9 +53,9 @@ import type { · 52 unmodified lines | |||
| 53 | ArgumentCondition, | 53 | ArgumentCondition, |
| 54 | ArgumentConditionOperator, | 54 | ArgumentConditionOperator, |
| 55 | ToolDescriptor, | 55 | ToolDescriptor, |
| 56 | ToolRoleBindingRecord, | 56 | ToolPolicyAssignmentRecord, |
| 57 | ToolRoleRuleRecord, | 57 | ToolPolicyRuleRecord, |
| 58 | ToolRoleRecord, | 58 | ToolPolicySetRecord, |
| 59 | } from "@/lib/types"; | 59 | } from "@/lib/types"; |
| 60 | import type { Id } from "@executor/database/convex/_generated/dataModel"; | 60 | import type { Id } from "@executor/database/convex/_generated/dataModel"; |
| 61 | import { workspaceQueryArgs } from "@/lib/workspace/query-args"; | 61 | import { workspaceQueryArgs } from "@/lib/workspace/query-args"; |
| @@ -94,20 +94,20 @@ interface RoleFormState { · 32 unmodified lines | |||
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | interface RoleRuleFormState { | 96 | interface RoleRuleFormState { |
| 97 | selectorType: ToolRoleRuleRecord["selectorType"]; | 97 | selectorType: ToolPolicyRuleRecord["selectorType"]; |
| 98 | sourceKey: string; | 98 | sourceKey: string; |
| 99 | resourcePattern: string; | 99 | resourcePattern: string; |
| 100 | matchType: ToolRoleRuleRecord["matchType"]; | 100 | matchType: ToolPolicyRuleRecord["matchType"]; |
| 101 | effect: ToolRoleRuleRecord["effect"]; | 101 | effect: ToolPolicyRuleRecord["effect"]; |
| 102 | approvalMode: ToolRoleRuleRecord["approvalMode"]; | 102 | approvalMode: ToolPolicyRuleRecord["approvalMode"]; |
| 103 | priority: string; | 103 | priority: string; |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | interface RoleBindingFormState { | 106 | interface RoleBindingFormState { |
| 107 | scopeType: ToolRoleBindingRecord["scopeType"]; | 107 | scopeType: ToolPolicyAssignmentRecord["scopeType"]; |
| 108 | targetAccountId: string; | 108 | targetAccountId: string; |
| 109 | clientId: string; | 109 | clientId: string; |
| 110 | status: ToolRoleBindingRecord["status"]; | 110 | status: ToolPolicyAssignmentRecord["status"]; |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | interface OrganizationMemberListItem { | 113 | interface OrganizationMemberListItem { |
| @@ -343,7 +343,7 @@ function isDirectToolPolicy(policy: ToolPolicyRecord): boolean { · 229 unmodified lines | |||
| 343 | ); | 343 | ); |
| 344 | } | 344 | } |
| 345 | 345 | ||
| 346 | function roleRulePattern(rule: ToolRoleRuleRecord): string { | 346 | function roleRulePattern(rule: ToolPolicyRuleRecord): string { |
| 347 | if (rule.selectorType === "all") return "*"; | 347 | if (rule.selectorType === "all") return "*"; |
| 348 | if (rule.selectorType === "source") return rule.sourceKey ?? ""; | 348 | if (rule.selectorType === "source") return rule.sourceKey ?? ""; |
| 349 | if (rule.selectorType === "namespace") return rule.namespacePattern ?? ""; | 349 | if (rule.selectorType === "namespace") return rule.namespacePattern ?? ""; |
| @@ -837,7 +837,7 @@ function PolicyCard({ · 487 unmodified lines | |||
| 837 | </button> | 837 | </button> |
| 838 | </TooltipTrigger> | 838 | </TooltipTrigger> |
| 839 | <TooltipContent side="left" className="text-xs"> | 839 | <TooltipContent side="left" className="text-xs"> |
| 840 | {canDelete ? "Delete policy" : "Role-managed policy (delete from roles)"} | 840 | {canDelete ? "Delete policy" : "Policy-set-managed policy (delete from policy sets)"} |
| 841 | </TooltipContent> | 841 | </TooltipContent> |
| 842 | </Tooltip> | 842 | </Tooltip> |
| 843 | </TooltipProvider> | 843 | </TooltipProvider> |
| @@ -869,12 +869,12 @@ export function PoliciesPanel({ · 25 unmodified lines | |||
| 869 | 869 | ||
| 870 | const listArgs = workspaceQueryArgs(context); | 870 | const listArgs = workspaceQueryArgs(context); |
| 871 | const policiesQuery = useQuery(convexApi.workspace.listToolPolicies, listArgs); | 871 | const policiesQuery = useQuery(convexApi.workspace.listToolPolicies, listArgs); |
| 872 | const upsertToolRole = useMutation(convexApi.workspace.upsertToolRole); | 872 | const upsertToolPolicySet = useMutation(convexApi.workspace.upsertToolPolicySet); |
| 873 | const upsertToolRoleRule = useMutation(convexApi.workspace.upsertToolRoleRule); | 873 | const upsertToolPolicyRule = useMutation(convexApi.workspace.upsertToolPolicyRule); |
| 874 | const upsertToolRoleBinding = useMutation(convexApi.workspace.upsertToolRoleBinding); | 874 | const upsertToolPolicyAssignment = useMutation(convexApi.workspace.upsertToolPolicyAssignment); |
| 875 | const deleteToolRole = useMutation(convexApi.workspace.deleteToolRole); | 875 | const deleteToolPolicySet = useMutation(convexApi.workspace.deleteToolPolicySet); |
| 876 | const deleteToolRoleRule = useMutation(convexApi.workspace.deleteToolRoleRule); | 876 | const deleteToolPolicyRule = useMutation(convexApi.workspace.deleteToolPolicyRule); |
| 877 | const deleteToolRoleBinding = useMutation(convexApi.workspace.deleteToolRoleBinding); | 877 | const deleteToolPolicyAssignment = useMutation(convexApi.workspace.deleteToolPolicyAssignment); |
| 878 | 878 | ||
| 879 | const loading = Boolean(context) && policiesQuery === undefined; | 879 | const loading = Boolean(context) && policiesQuery === undefined; |
| 880 | const policies = useMemo(() => (policiesQuery ?? []) as ToolPolicyRecord[], [policiesQuery]); | 880 | const policies = useMemo(() => (policiesQuery ?? []) as ToolPolicyRecord[], [policiesQuery]); |
| @@ -902,10 +902,10 @@ export function PoliciesPanel({ · 21 unmodified lines | |||
| 902 | const canManageRoles = selfMembership?.role === "owner" || selfMembership?.role === "admin"; | 902 | const canManageRoles = selfMembership?.role === "owner" || selfMembership?.role === "admin"; |
| 903 | 903 | ||
| 904 | const rolesQuery = useQuery( | 904 | const rolesQuery = useQuery( |
| 905 | convexApi.workspace.listToolRoles, | 905 | convexApi.workspace.listToolPolicySets, |
| 906 | context && canManageRoles ? listArgs : "skip", | 906 | context && canManageRoles ? listArgs : "skip", |
| 907 | ); | 907 | ); |
| 908 | const roleItems = useMemo(() => ((rolesQuery ?? []) as ToolRoleRecord[]), [rolesQuery]); | 908 | const roleItems = useMemo(() => ((rolesQuery ?? []) as ToolPolicySetRecord[]), [rolesQuery]); |
| 909 | const activeRoleId = useMemo(() => { | 909 | const activeRoleId = useMemo(() => { |
| 910 | if (!canManageRoles) { | 910 | if (!canManageRoles) { |
| 911 | return null; | 911 | return null; |
| @@ -919,13 +919,13 @@ export function PoliciesPanel({ · 7 unmodified lines | |||
| 919 | }, [canManageRoles, roleItems, selectedRoleId]); | 919 | }, [canManageRoles, roleItems, selectedRoleId]); |
| 920 | 920 | ||
| 921 | const bindingsQuery = useQuery( | 921 | const bindingsQuery = useQuery( |
| 922 | convexApi.workspace.listToolRoleBindings, | 922 | convexApi.workspace.listToolPolicyAssignments, |
| 923 | context && canManageRoles ? listArgs : "skip", | 923 | context && canManageRoles ? listArgs : "skip", |
| 924 | ); | 924 | ); |
| 925 | const bindingItems = useMemo(() => ((bindingsQuery ?? []) as ToolRoleBindingRecord[]), [bindingsQuery]); | 925 | const bindingItems = useMemo(() => ((bindingsQuery ?? []) as ToolPolicyAssignmentRecord[]), [bindingsQuery]); |
| 926 | 926 | ||
| 927 | const roleRulesQuery = useQuery( | 927 | const roleRulesQuery = useQuery( |
| 928 | convexApi.workspace.listToolRoleRules, | 928 | convexApi.workspace.listToolPolicyRules, |
| 929 | context && canManageRoles && activeRoleId | 929 | context && canManageRoles && activeRoleId |
| 930 | ? { | 930 | ? { |
| 931 | workspaceId: context.workspaceId, | 931 | workspaceId: context.workspaceId, |
| @@ -935,7 +935,7 @@ export function PoliciesPanel({ · 3 unmodified lines | |||
| 935 | : "skip", | 935 | : "skip", |
| 936 | ); | 936 | ); |
| 937 | const selectedRoleRules = useMemo( | 937 | const selectedRoleRules = useMemo( |
| 938 | () => ((roleRulesQuery ?? []) as ToolRoleRuleRecord[]), | 938 | () => ((roleRulesQuery ?? []) as ToolPolicyRuleRecord[]), |
| 939 | [roleRulesQuery], | 939 | [roleRulesQuery], |
| 940 | ); | 940 | ); |
| 941 | 941 | ||
| @@ -1024,7 +1024,7 @@ export function PoliciesPanel({ · 82 unmodified lines | |||
| 1024 | : "organization" as const; | 1024 | : "organization" as const; |
| 1025 | const targetAccountId = form.scope === "personal" ? context.accountId : undefined; | 1025 | const targetAccountId = form.scope === "personal" ? context.accountId : undefined; |
| 1026 | 1026 | ||
| 1027 | await upsertToolRole({ | 1027 | await upsertToolPolicySet({ |
| 1028 | workspaceId: context.workspaceId, | 1028 | workspaceId: context.workspaceId, |
| 1029 | sessionId: context.sessionId, | 1029 | sessionId: context.sessionId, |
| 1030 | id: roleId, | 1030 | id: roleId, |
| @@ -1033,7 +1033,7 @@ export function PoliciesPanel({ · 2 unmodified lines | |||
| 1033 | }); | 1033 | }); |
| 1034 | 1034 | ||
| 1035 | try { | 1035 | try { |
| 1036 | await upsertToolRoleRule({ | 1036 | await upsertToolPolicyRule({ |
| 1037 | workspaceId: context.workspaceId, | 1037 | workspaceId: context.workspaceId, |
| 1038 | sessionId: context.sessionId, | 1038 | sessionId: context.sessionId, |
| 1039 | id: ruleId, | 1039 | id: ruleId, |
| @@ -1048,7 +1048,7 @@ export function PoliciesPanel({ · 8 unmodified lines | |||
| 1048 | priority, | 1048 | priority, |
| 1049 | }); | 1049 | }); |
| 1050 | 1050 | ||
| 1051 | await upsertToolRoleBinding({ | 1051 | await upsertToolPolicyAssignment({ |
| 1052 | workspaceId: context.workspaceId, | 1052 | workspaceId: context.workspaceId, |
| 1053 | sessionId: context.sessionId, | 1053 | sessionId: context.sessionId, |
| 1054 | id: bindingId, | 1054 | id: bindingId, |
| @@ -1060,7 +1060,7 @@ export function PoliciesPanel({ · 5 unmodified lines | |||
| 1060 | }); | 1060 | }); |
| 1061 | } catch (error) { | 1061 | } catch (error) { |
| 1062 | try { | 1062 | try { |
| 1063 | await deleteToolRole({ | 1063 | await deleteToolPolicySet({ |
| 1064 | workspaceId: context.workspaceId, | 1064 | workspaceId: context.workspaceId, |
| 1065 | sessionId: context.sessionId, | 1065 | sessionId: context.sessionId, |
| 1066 | roleId, | 1066 | roleId, |
| @@ -1084,7 +1084,7 @@ export function PoliciesPanel({ · 17 unmodified lines | |||
| 1084 | const handleDelete = useCallback(async (policy: ToolPolicyRecord) => { | 1084 | const handleDelete = useCallback(async (policy: ToolPolicyRecord) => { |
| 1085 | if (!context) return; | 1085 | if (!context) return; |
| 1086 | if (!isDirectToolPolicy(policy)) { | 1086 | if (!isDirectToolPolicy(policy)) { |
| 1087 | toast.error("This policy is managed by role bindings. Use role management to remove it."); | 1087 | toast.error("This policy is managed by a policy set assignment. Use the Policy Set Manager to remove it."); |
| 1088 | return; | 1088 | return; |
| 1089 | } | 1089 | } |
| 1090 | 1090 | ||
| @@ -1095,7 +1095,7 @@ export function PoliciesPanel({ · 4 unmodified lines | |||
| 1095 | 1095 | ||
| 1096 | setDeletingId(policy.id); | 1096 | setDeletingId(policy.id); |
| 1097 | try { | 1097 | try { |
| 1098 | await deleteToolRole({ | 1098 | await deleteToolPolicySet({ |
| 1099 | workspaceId: context.workspaceId, | 1099 | workspaceId: context.workspaceId, |
| 1100 | sessionId: context.sessionId, | 1100 | sessionId: context.sessionId, |
| 1101 | roleId: policy.roleId, | 1101 | roleId: policy.roleId, |
| @@ -1106,7 +1106,7 @@ export function PoliciesPanel({ · 4 unmodified lines | |||
| 1106 | } finally { | 1106 | } finally { |
| 1107 | setDeletingId(null); | 1107 | setDeletingId(null); |
| 1108 | } | 1108 | } |
| 1109 | }, [context, deleteToolRole]); | 1109 | }, [context, deleteToolPolicySet]); |
| 1110 | 1110 | ||
| 1111 | const handleCreateRole = useCallback(async () => { | 1111 | const handleCreateRole = useCallback(async () => { |
| 1112 | if (!context) return; | 1112 | if (!context) return; |
| @@ -1118,7 +1118,7 @@ export function PoliciesPanel({ · 5 unmodified lines | |||
| 1118 | 1118 | ||
| 1119 | setBusyRoleAction("create-role"); | 1119 | setBusyRoleAction("create-role"); |
| 1120 | try { | 1120 | try { |
| 1121 | const role = await upsertToolRole({ | 1121 | const role = await upsertToolPolicySet({ |
| 1122 | workspaceId: context.workspaceId, | 1122 | workspaceId: context.workspaceId, |
| 1123 | sessionId: context.sessionId, | 1123 | sessionId: context.sessionId, |
| 1124 | name, | 1124 | name, |
| @@ -1132,7 +1132,7 @@ export function PoliciesPanel({ · 7 unmodified lines | |||
| 1132 | } finally { | 1132 | } finally { |
| 1133 | setBusyRoleAction(null); | 1133 | setBusyRoleAction(null); |
| 1134 | } | 1134 | } |
| 1135 | }, [context, roleForm, upsertToolRole]); | 1135 | }, [context, roleForm, upsertToolPolicySet]); |
| 1136 | 1136 | ||
| 1137 | const handleCreateRoleRule = useCallback(async () => { | 1137 | const handleCreateRoleRule = useCallback(async () => { |
| 1138 | if (!context || !activeRoleId) return; | 1138 | if (!context || !activeRoleId) return; |
| @@ -1157,7 +1157,7 @@ export function PoliciesPanel({ · 18 unmodified lines | |||
| 1157 | 1157 | ||
| 1158 | setBusyRoleAction("create-rule"); | 1158 | setBusyRoleAction("create-rule"); |
| 1159 | try { | 1159 | try { |
| 1160 | await upsertToolRoleRule({ | 1160 | await upsertToolPolicyRule({ |
| 1161 | workspaceId: context.workspaceId, | 1161 | workspaceId: context.workspaceId, |
| 1162 | sessionId: context.sessionId, | 1162 | sessionId: context.sessionId, |
| 1163 | roleId: activeRoleId, | 1163 | roleId: activeRoleId, |
| @@ -1176,7 +1176,7 @@ export function PoliciesPanel({ · 12 unmodified lines | |||
| 1176 | } finally { | 1176 | } finally { |
| 1177 | setBusyRoleAction(null); | 1177 | setBusyRoleAction(null); |
| 1178 | } | 1178 | } |
| 1179 | }, [activeRoleId, context, roleRuleForm, upsertToolRoleRule]); | 1179 | }, [activeRoleId, context, roleRuleForm, upsertToolPolicyRule]); |
| 1180 | 1180 | ||
| 1181 | const handleCreateRoleBinding = useCallback(async () => { | 1181 | const handleCreateRoleBinding = useCallback(async () => { |
| 1182 | if (!context || !activeRoleId) return; | 1182 | if (!context || !activeRoleId) return; |
| @@ -1190,7 +1190,7 @@ export function PoliciesPanel({ · 7 unmodified lines | |||
| 1190 | 1190 | ||
| 1191 | setBusyRoleAction("create-binding"); | 1191 | setBusyRoleAction("create-binding"); |
| 1192 | try { | 1192 | try { |
| 1193 | await upsertToolRoleBinding({ | 1193 | await upsertToolPolicyAssignment({ |
| 1194 | workspaceId: context.workspaceId, | 1194 | workspaceId: context.workspaceId, |
| 1195 | sessionId: context.sessionId, | 1195 | sessionId: context.sessionId, |
| 1196 | roleId: activeRoleId, | 1196 | roleId: activeRoleId, |
| @@ -1208,14 +1208,14 @@ export function PoliciesPanel({ · 11 unmodified lines | |||
| 1208 | } finally { | 1208 | } finally { |
| 1209 | setBusyRoleAction(null); | 1209 | setBusyRoleAction(null); |
| 1210 | } | 1210 | } |
| 1211 | }, [activeRoleId, context, roleBindingForm, upsertToolRoleBinding]); | 1211 | }, [activeRoleId, context, roleBindingForm, upsertToolPolicyAssignment]); |
| 1212 | 1212 | ||
| 1213 | const handleDeleteRole = useCallback(async (roleId: string) => { | 1213 | const handleDeleteRole = useCallback(async (roleId: string) => { |
| 1214 | if (!context) return; | 1214 | if (!context) return; |
| 1215 | 1215 | ||
| 1216 | setBusyRoleAction(`delete-role:${roleId}`); | 1216 | setBusyRoleAction(`delete-role:${roleId}`); |
| 1217 | try { | 1217 | try { |
| 1218 | await deleteToolRole({ | 1218 | await deleteToolPolicySet({ |
| 1219 | workspaceId: context.workspaceId, | 1219 | workspaceId: context.workspaceId, |
| 1220 | sessionId: context.sessionId, | 1220 | sessionId: context.sessionId, |
| 1221 | roleId, | 1221 | roleId, |
| @@ -1229,14 +1229,14 @@ export function PoliciesPanel({ · 7 unmodified lines | |||
| 1229 | } finally { | 1229 | } finally { |
| 1230 | setBusyRoleAction(null); | 1230 | setBusyRoleAction(null); |
| 1231 | } | 1231 | } |
| 1232 | }, [activeRoleId, context, deleteToolRole]); | 1232 | }, [activeRoleId, context, deleteToolPolicySet]); |
| 1233 | 1233 | ||
| 1234 | const handleDeleteRule = useCallback(async (ruleId: string) => { | 1234 | const handleDeleteRule = useCallback(async (ruleId: string) => { |
| 1235 | if (!context || !activeRoleId) return; | 1235 | if (!context || !activeRoleId) return; |
| 1236 | 1236 | ||
| 1237 | setBusyRoleAction(`delete-rule:${ruleId}`); | 1237 | setBusyRoleAction(`delete-rule:${ruleId}`); |
| 1238 | try { | 1238 | try { |
| 1239 | await deleteToolRoleRule({ | 1239 | await deleteToolPolicyRule({ |
| 1240 | workspaceId: context.workspaceId, | 1240 | workspaceId: context.workspaceId, |
| 1241 | sessionId: context.sessionId, | 1241 | sessionId: context.sessionId, |
| 1242 | roleId: activeRoleId, | 1242 | roleId: activeRoleId, |
| @@ -1248,14 +1248,14 @@ export function PoliciesPanel({ · 5 unmodified lines | |||
| 1248 | } finally { | 1248 | } finally { |
| 1249 | setBusyRoleAction(null); | 1249 | setBusyRoleAction(null); |
| 1250 | } | 1250 | } |
| 1251 | }, [activeRoleId, context, deleteToolRoleRule]); | 1251 | }, [activeRoleId, context, deleteToolPolicyRule]); |
| 1252 | 1252 | ||
| 1253 | const handleDeleteBinding = useCallback(async (bindingId: string) => { | 1253 | const handleDeleteBinding = useCallback(async (bindingId: string) => { |
| 1254 | if (!context) return; | 1254 | if (!context) return; |
| 1255 | 1255 | ||
| 1256 | setBusyRoleAction(`delete-binding:${bindingId}`); | 1256 | setBusyRoleAction(`delete-binding:${bindingId}`); |
| 1257 | try { | 1257 | try { |
| 1258 | await deleteToolRoleBinding({ | 1258 | await deleteToolPolicyAssignment({ |
| 1259 | workspaceId: context.workspaceId, | 1259 | workspaceId: context.workspaceId, |
| 1260 | sessionId: context.sessionId, | 1260 | sessionId: context.sessionId, |
| 1261 | bindingId, | 1261 | bindingId, |
| @@ -1266,7 +1266,7 @@ export function PoliciesPanel({ · 4 unmodified lines | |||
| 1266 | } finally { | 1266 | } finally { |
| 1267 | setBusyRoleAction(null); | 1267 | setBusyRoleAction(null); |
| 1268 | } | 1268 | } |
| 1269 | }, [context, deleteToolRoleBinding]); | 1269 | }, [context, deleteToolPolicyAssignment]); |
| 1270 | 1270 | ||
| 1271 | // ── Group policies by decision for display ── | 1271 | // ── Group policies by decision for display ── |
| 1272 | 1272 | ||
| @@ -1305,7 +1305,7 @@ export function PoliciesPanel({ · 32 unmodified lines | |||
| 1305 | disabled={!context} | 1305 | disabled={!context} |
| 1306 | > | 1306 | > |
| 1307 | <Plus className="h-3.5 w-3.5" /> | 1307 | <Plus className="h-3.5 w-3.5" /> |
| 1308 | New Policy | 1308 | Quick Policy |
| 1309 | </Button> | 1309 | </Button> |
| 1310 | </div> | 1310 | </div> |
| 1311 | 1311 | ||
| @@ -1375,28 +1375,31 @@ export function PoliciesPanel({ · 63 unmodified lines | |||
| 1375 | 1375 | ||
| 1376 | <Separator className="bg-border/40" /> | 1376 | <Separator className="bg-border/40" /> |
| 1377 | 1377 | ||
| 1378 | <div className="space-y-3"> | 1378 | <details className="group rounded-lg border border-border/60 bg-card/40"> |
| 1379 | <div className="flex items-center justify-between"> | 1379 | <summary className="flex cursor-pointer list-none items-center justify-between gap-3 px-3 py-2.5"> |
| 1380 | <div> | 1380 | <div> |
| 1381 | <h4 className="text-sm font-medium">Role Manager</h4> | 1381 | <h4 className="text-sm font-medium">Policy Set Manager</h4> |
| 1382 | <p className="text-[11px] text-muted-foreground mt-1"> | 1382 | <p className="text-[11px] text-muted-foreground mt-0.5"> |
| 1383 | Manage reusable tool roles, rule selectors, and scoped bindings. | 1383 | Advanced: reusable policy sets with rules and assignments. |
| 1384 | </p> | 1384 | </p> |
| 1385 | </div> | 1385 | </div> |
| 1386 | </div> | 1386 | <span className="text-[10px] text-muted-foreground group-open:hidden">Expand</span> |
| 1387 | 1387 | <span className="text-[10px] text-muted-foreground hidden group-open:inline">Collapse</span> | |
| 1388 | {!canManageRoles ? ( | 1388 | </summary> |
| 1389 | <div className="rounded-lg border border-dashed border-border/50 p-4 text-xs text-muted-foreground"> | 1389 | |
| 1390 | Organization owner/admin role required to manage tool roles. | 1390 | <div className="px-3 pb-3"> |
| 1391 | </div> | 1391 | {!canManageRoles ? ( |
| 1392 | ) : ( | 1392 | <div className="rounded-lg border border-dashed border-border/50 p-4 text-xs text-muted-foreground"> |
| 1393 | <div className="grid grid-cols-1 gap-4 lg:grid-cols-[280px_1fr]"> | 1393 | Organization owner/admin role required to manage tool policy sets. |
| 1394 | </div> | ||
| 1395 | ) : ( | ||
| 1396 | <div className="grid grid-cols-1 gap-4 lg:grid-cols-[280px_1fr]"> | ||
| 1394 | <div className="space-y-3 rounded-lg border border-border/60 bg-card p-3"> | 1397 | <div className="space-y-3 rounded-lg border border-border/60 bg-card p-3"> |
| 1395 | <Label className="text-xs text-muted-foreground">Create role</Label> | 1398 | <Label className="text-xs text-muted-foreground">Create policy set</Label> |
| 1396 | <Input | 1399 | <Input |
| 1397 | value={roleForm.name} | 1400 | value={roleForm.name} |
| 1398 | onChange={(event) => setRoleForm((state) => ({ ...state, name: event.target.value }))} | 1401 | onChange={(event) => setRoleForm((state) => ({ ...state, name: event.target.value }))} |
| 1399 | placeholder="Role name" | 1402 | placeholder="Policy set name" |
| 1400 | className="h-8 text-xs" | 1403 | className="h-8 text-xs" |
| 1401 | /> | 1404 | /> |
| 1402 | <Input | 1405 | <Input |
| @@ -1411,14 +1414,14 @@ export function PoliciesPanel({ · 8 unmodified lines | |||
| 1411 | onClick={handleCreateRole} | 1414 | onClick={handleCreateRole} |
| 1412 | disabled={busyRoleAction === "create-role"} | 1415 | disabled={busyRoleAction === "create-role"} |
| 1413 | > | 1416 | > |
| 1414 | {busyRoleAction === "create-role" ? "Creating..." : "Create role"} | 1417 | {busyRoleAction === "create-role" ? "Creating..." : "Create policy set"} |
| 1415 | </Button> | 1418 | </Button> |
| 1416 | 1419 | ||
| 1417 | <Separator className="bg-border/40" /> | 1420 | <Separator className="bg-border/40" /> |
| 1418 | 1421 | ||
| 1419 | <div className="space-y-1.5 max-h-[320px] overflow-y-auto"> | 1422 | <div className="space-y-1.5 max-h-[320px] overflow-y-auto"> |
| 1420 | {roleItems.length === 0 ? ( | 1423 | {roleItems.length === 0 ? ( |
| 1421 | <p className="text-[11px] text-muted-foreground">No roles yet.</p> | 1424 | <p className="text-[11px] text-muted-foreground">No policy sets yet.</p> |
| 1422 | ) : ( | 1425 | ) : ( |
| 1423 | roleItems.map((role) => { | 1426 | roleItems.map((role) => { |
| 1424 | const bindingCount = bindingItems.filter((binding) => binding.roleId === role.id).length; | 1427 | const bindingCount = bindingItems.filter((binding) => binding.roleId === role.id).length; |
| @@ -1429,7 +1432,7 @@ export function PoliciesPanel({ · 4 unmodified lines | |||
| 1429 | onClick={() => setSelectedRoleId(role.id)} | 1432 | onClick={() => setSelectedRoleId(role.id)} |
| 1430 | className={cn( | 1433 | className={cn( |
| 1431 | "w-full rounded-md border px-2 py-1.5 text-left", | 1434 | "w-full rounded-md border px-2 py-1.5 text-left", |
| 1432 | selectedRoleId === role.id | 1435 | activeRoleId === role.id |
| 1433 | ? "border-primary/40 bg-primary/5" | 1436 | ? "border-primary/40 bg-primary/5" |
| 1434 | : "border-border/40 hover:bg-muted/30", | 1437 | : "border-border/40 hover:bg-muted/30", |
| 1435 | )} | 1438 | )} |
| @@ -1437,7 +1440,7 @@ export function PoliciesPanel({ · 1 unmodified line | |||
| 1437 | <div className="flex items-center justify-between gap-2"> | 1440 | <div className="flex items-center justify-between gap-2"> |
| 1438 | <span className="truncate text-xs font-medium">{role.name}</span> | 1441 | <span className="truncate text-xs font-medium">{role.name}</span> |
| 1439 | <Badge variant="outline" className="text-[9px] px-1.5 py-0"> | 1442 | <Badge variant="outline" className="text-[9px] px-1.5 py-0"> |
| 1440 | {bindingCount} bind | 1443 | {bindingCount} assign |
| 1441 | </Badge> | 1444 | </Badge> |
| 1442 | </div> | 1445 | </div> |
| 1443 | {role.description ? ( | 1446 | {role.description ? ( |
| @@ -1452,7 +1455,7 @@ export function PoliciesPanel({ · 8 unmodified lines | |||
| 1452 | 1455 | ||
| 1453 | <div className="space-y-4 rounded-lg border border-border/60 bg-card p-4"> | 1456 | <div className="space-y-4 rounded-lg border border-border/60 bg-card p-4"> |
| 1454 | {!selectedRole ? ( | 1457 | {!selectedRole ? ( |
| 1455 | <p className="text-xs text-muted-foreground">Select a role to manage its rules and bindings.</p> | 1458 | <p className="text-xs text-muted-foreground">Select a policy set to manage its rules and assignments.</p> |
| 1456 | ) : ( | 1459 | ) : ( |
| 1457 | <> | 1460 | <> |
| 1458 | <div className="flex items-start justify-between gap-3"> | 1461 | <div className="flex items-start justify-between gap-3"> |
| @@ -1470,7 +1473,7 @@ export function PoliciesPanel({ · 11 unmodified lines | |||
| 1470 | onClick={() => handleDeleteRole(selectedRole.id)} | 1473 | onClick={() => handleDeleteRole(selectedRole.id)} |
| 1471 | disabled={busyRoleAction === `delete-role:${selectedRole.id}`} | 1474 | disabled={busyRoleAction === `delete-role:${selectedRole.id}`} |
| 1472 | > | 1475 | > |
| 1473 | {busyRoleAction === `delete-role:${selectedRole.id}` ? "Deleting..." : "Delete role"} | 1476 | {busyRoleAction === `delete-role:${selectedRole.id}` ? "Deleting..." : "Delete set"} |
| 1474 | </Button> | 1477 | </Button> |
| 1475 | </div> | 1478 | </div> |
| 1476 | 1479 | ||
| @@ -1520,7 +1523,7 @@ export function PoliciesPanel({ · 43 unmodified lines | |||
| 1520 | value={roleRuleForm.selectorType} | 1523 | value={roleRuleForm.selectorType} |
| 1521 | onValueChange={(value) => setRoleRuleForm((state) => ({ | 1524 | onValueChange={(value) => setRoleRuleForm((state) => ({ |
| 1522 | ...state, | 1525 | ...state, |
| 1523 | selectorType: value as ToolRoleRuleRecord["selectorType"], | 1526 | selectorType: value as ToolPolicyRuleRecord["selectorType"], |
| 1524 | }))} | 1527 | }))} |
| 1525 | > | 1528 | > |
| 1526 | <SelectTrigger className="h-8 text-xs"> | 1529 | <SelectTrigger className="h-8 text-xs"> |
| @@ -1564,7 +1567,7 @@ export function PoliciesPanel({ · 37 unmodified lines | |||
| 1564 | value={roleRuleForm.effect} | 1567 | value={roleRuleForm.effect} |
| 1565 | onValueChange={(value) => setRoleRuleForm((state) => ({ | 1568 | onValueChange={(value) => setRoleRuleForm((state) => ({ |
| 1566 | ...state, | 1569 | ...state, |
| 1567 | effect: value as ToolRoleRuleRecord["effect"], | 1570 | effect: value as ToolPolicyRuleRecord["effect"], |
| 1568 | }))} | 1571 | }))} |
| 1569 | > | 1572 | > |
| 1570 | <SelectTrigger className="h-8 text-xs"> | 1573 | <SelectTrigger className="h-8 text-xs"> |
| @@ -1580,7 +1583,7 @@ export function PoliciesPanel({ · 9 unmodified lines | |||
| 1580 | value={roleRuleForm.approvalMode} | 1583 | value={roleRuleForm.approvalMode} |
| 1581 | onValueChange={(value) => setRoleRuleForm((state) => ({ | 1584 | onValueChange={(value) => setRoleRuleForm((state) => ({ |
| 1582 | ...state, | 1585 | ...state, |
| 1583 | approvalMode: value as ToolRoleRuleRecord["approvalMode"], | 1586 | approvalMode: value as ToolPolicyRuleRecord["approvalMode"], |
| 1584 | }))} | 1587 | }))} |
| 1585 | > | 1588 | > |
| 1586 | <SelectTrigger className="h-8 text-xs"> | 1589 | <SelectTrigger className="h-8 text-xs"> |
| @@ -1615,7 +1618,7 @@ export function PoliciesPanel({ · 28 unmodified lines | |||
| 1615 | 1618 | ||
| 1616 | <div className="space-y-2"> | 1619 | <div className="space-y-2"> |
| 1617 | <div className="flex items-center justify-between"> | 1620 | <div className="flex items-center justify-between"> |
| 1618 | <Label className="text-xs text-muted-foreground">Bindings</Label> | 1621 | <Label className="text-xs text-muted-foreground">Assignments</Label> |
| 1619 | <span className="text-[10px] text-muted-foreground">{selectedRoleBindings.length}</span> | 1622 | <span className="text-[10px] text-muted-foreground">{selectedRoleBindings.length}</span> |
| 1620 | </div> | 1623 | </div> |
| 1621 | <div className="space-y-1.5 max-h-[200px] overflow-y-auto"> | 1624 | <div className="space-y-1.5 max-h-[200px] overflow-y-auto"> |
| @@ -1657,7 +1660,7 @@ export function PoliciesPanel({ · 35 unmodified lines | |||
| 1657 | value={roleBindingForm.scopeType} | 1660 | value={roleBindingForm.scopeType} |
| 1658 | onValueChange={(value) => setRoleBindingForm((state) => ({ | 1661 | onValueChange={(value) => setRoleBindingForm((state) => ({ |
| 1659 | ...state, | 1662 | ...state, |
| 1660 | scopeType: value as ToolRoleBindingRecord["scopeType"], | 1663 | scopeType: value as ToolPolicyAssignmentRecord["scopeType"], |
| 1661 | }))} | 1664 | }))} |
| 1662 | > | 1665 | > |
| 1663 | <SelectTrigger className="h-8 text-xs"> | 1666 | <SelectTrigger className="h-8 text-xs"> |
| @@ -1699,7 +1702,7 @@ export function PoliciesPanel({ · 35 unmodified lines | |||
| 1699 | value={roleBindingForm.status} | 1702 | value={roleBindingForm.status} |
| 1700 | onValueChange={(value) => setRoleBindingForm((state) => ({ | 1703 | onValueChange={(value) => setRoleBindingForm((state) => ({ |
| 1701 | ...state, | 1704 | ...state, |
| 1702 | status: value as ToolRoleBindingRecord["status"], | 1705 | status: value as ToolPolicyAssignmentRecord["status"], |
| 1703 | }))} | 1706 | }))} |
| 1704 | > | 1707 | > |
| 1705 | <SelectTrigger className="h-8 text-xs"> | 1708 | <SelectTrigger className="h-8 text-xs"> |
| @@ -1717,7 +1720,7 @@ export function PoliciesPanel({ · 11 unmodified lines | |||
| 1717 | onClick={handleCreateRoleBinding} | 1720 | onClick={handleCreateRoleBinding} |
| 1718 | disabled={busyRoleAction === "create-binding"} | 1721 | disabled={busyRoleAction === "create-binding"} |
| 1719 | > | 1722 | > |
| 1720 | {busyRoleAction === "create-binding" ? "Adding binding..." : "Add binding"} | 1723 | {busyRoleAction === "create-binding" ? "Adding assignment..." : "Add assignment"} |
| 1721 | </Button> | 1724 | </Button> |
| 1722 | </div> | 1725 | </div> |
| 1723 | </div> | 1726 | </div> |
| @@ -1725,8 +1728,9 @@ export function PoliciesPanel({ · 1 unmodified line | |||
| 1725 | )} | 1728 | )} |
| 1726 | </div> | 1729 | </div> |
| 1727 | </div> | 1730 | </div> |
| 1728 | )} | 1731 | )} |
| 1729 | </div> | 1732 | </div> |
| 1733 | </details> | ||
| 1730 | 1734 | ||
| 1731 | {/* Create Policy Dialog */} | 1735 | {/* Create Policy Dialog */} |
| 1732 | <Dialog open={dialogOpen} onOpenChange={setDialogOpen}> | 1736 | <Dialog open={dialogOpen} onOpenChange={setDialogOpen}> |
| @@ -1924,26 +1928,28 @@ export function PoliciesPanel({ · 191 unmodified lines | |||
| 1924 | /> | 1928 | /> |
| 1925 | </div> | 1929 | </div> |
| 1926 | 1930 | ||
| 1927 | {/* Advanced filters */} | 1931 | <details className="rounded-md border border-border/40 bg-muted/10 p-3"> |
| 1928 | <div className="space-y-3 rounded-md border border-border/40 bg-muted/10 p-3"> | 1932 | <summary className="cursor-pointer text-xs text-muted-foreground">Optional filters</summary> |
| 1929 | <div className="space-y-1"> | 1933 | <div className="mt-3 space-y-3"> |
| 1930 | <Label className="text-xs text-muted-foreground">Client ID</Label> | 1934 | <div className="space-y-1"> |
| 1931 | <Input | 1935 | <Label className="text-xs text-muted-foreground">Client ID</Label> |
| 1932 | value={form.clientId} | 1936 | <Input |
| 1933 | onChange={(event) => setForm((state) => ({ ...state, clientId: event.target.value }))} | 1937 | value={form.clientId} |
| 1934 | placeholder="optional client identifier" | 1938 | onChange={(event) => setForm((state) => ({ ...state, clientId: event.target.value }))} |
| 1935 | className="h-8 text-xs font-mono bg-background" | 1939 | placeholder="optional client identifier" |
| 1940 | className="h-8 text-xs font-mono bg-background" | ||
| 1941 | /> | ||
| 1942 | <p className="text-[10px] text-muted-foreground/60 leading-tight"> | ||
| 1943 | When set, this policy only applies to tool calls from this client ID. | ||
| 1944 | </p> | ||
| 1945 | </div> | ||
| 1946 | |||
| 1947 | <ArgumentConditionsEditor | ||
| 1948 | conditions={form.argumentConditions} | ||
| 1949 | onChange={(argumentConditions) => setForm((state) => ({ ...state, argumentConditions }))} | ||
| 1936 | /> | 1950 | /> |
| 1937 | <p className="text-[10px] text-muted-foreground/60 leading-tight"> | ||
| 1938 | When set, this policy only applies to tool calls from this client ID. | ||
| 1939 | </p> | ||
| 1940 | </div> | 1951 | </div> |
| 1941 | 1952 | </details> | |
| 1942 | <ArgumentConditionsEditor | ||
| 1943 | conditions={form.argumentConditions} | ||
| 1944 | onChange={(argumentConditions) => setForm((state) => ({ ...state, argumentConditions }))} | ||
| 1945 | /> | ||
| 1946 | </div> | ||
| 1947 | </div> | 1953 | </div> |
| 1948 | 1954 | ||
| 1949 | <DialogFooter> | 1955 | <DialogFooter> |
| @@ -1,6 +1,8 @@ | |||
| 1 | "use client"; | 1 | "use client"; |
| 2 | 2 | ||
| 3 | import { useCallback, useMemo, useRef, useState } from "react"; | 3 | import { useCallback, useEffect, useMemo, useRef, useState } from "react"; |
| 4 | import { parseAsNativeArrayOf, parseAsString, parseAsStringLiteral, useQueryStates } from "nuqs"; | ||
| 5 | import { useLocation, useNavigate } from "@/lib/router"; | ||
| 4 | import { Card, CardContent } from "@/components/ui/card"; | 6 | import { Card, CardContent } from "@/components/ui/card"; |
| 5 | import { Skeleton } from "@/components/ui/skeleton"; | 7 | import { Skeleton } from "@/components/ui/skeleton"; |
| 6 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; | 8 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; |
| @@ -27,6 +29,7 @@ import { · 20 unmodified lines | |||
| 27 | import { sourceLabel } from "@/lib/tool/source-utils"; | 29 | import { sourceLabel } from "@/lib/tool/source-utils"; |
| 28 | import { workspaceQueryArgs } from "@/lib/workspace/query-args"; | 30 | import { workspaceQueryArgs } from "@/lib/workspace/query-args"; |
| 29 | import type { SourceDialogMeta } from "@/components/tools/add/source-dialog"; | 31 | import type { SourceDialogMeta } from "@/components/tools/add/source-dialog"; |
| 32 | import type { FilterApproval } from "@/components/tools/explorer-derived"; | ||
| 30 | 33 | ||
| 31 | // ── Optimistic source helpers ── | 34 | // ── Optimistic source helpers ── |
| 32 | 35 | ||
| @@ -80,34 +83,87 @@ function pruneStaleOps( · 47 unmodified lines | |||
| 80 | }); | 83 | }); |
| 81 | } | 84 | } |
| 82 | 85 | ||
| 83 | type ToolsTab = "catalog" | "credentials" | "policies" | "editor"; | 86 | type ToolsTab = "catalog" | "connections" | "policies" | "editor"; |
| 84 | const INVENTORY_REGENERATION_TOAST_ID = "tool-inventory-regeneration"; | 87 | const INVENTORY_REGENERATION_TOAST_ID = "tool-inventory-regeneration"; |
| 85 | 88 | ||
| 86 | function parseInitialTab(tab?: string | null): ToolsTab { | 89 | function parseToolsTab(pathname: string): ToolsTab { |
| 87 | if (tab === "runner" || tab === "editor") { | 90 | const segments = pathname.split("/").filter(Boolean); |
| 88 | return "editor"; | 91 | const last = segments.at(-1); |
| 92 | |||
| 93 | if (last === "catalog") { | ||
| 94 | return "catalog"; | ||
| 89 | } | 95 | } |
| 90 | if (tab === "catalog" || tab === "credentials") { | 96 | if (last === "connections" || last === "credentials") { |
| 91 | return tab; | 97 | return "connections"; |
| 92 | } | 98 | } |
| 93 | if (tab === "policies") { | 99 | if (last === "policies") { |
| 94 | return "policies"; | 100 | return "policies"; |
| 95 | } | 101 | } |
| 102 | if (last === "editor" || last === "runner") { | ||
| 103 | return "editor"; | ||
| 104 | } | ||
| 105 | |||
| 96 | return "catalog"; | 106 | return "catalog"; |
| 97 | } | 107 | } |
| 98 | 108 | ||
| 109 | function normalizeCatalogApprovalFilter(value: string | null): FilterApproval { | ||
| 110 | if (value === "required" || value === "auto") { | ||
| 111 | return value; | ||
| 112 | } | ||
| 113 | |||
| 114 | return "all"; | ||
| 115 | } | ||
| 116 | |||
| 117 | function toolsPathFromTab(tab: ToolsTab): string { | ||
| 118 | if (tab === "catalog") { | ||
| 119 | return "/tools/catalog"; | ||
| 120 | } | ||
| 121 | if (tab === "connections") { | ||
| 122 | return "/tools/connections"; | ||
| 123 | } | ||
| 124 | if (tab === "policies") { | ||
| 125 | return "/tools/policies"; | ||
| 126 | } | ||
| 127 | return "/tools/editor"; | ||
| 128 | } | ||
| 129 | |||
| 130 | function mapEmptyQueryValueToNull(value: string): string | null { | ||
| 131 | return value.length > 0 ? value : null; | ||
| 132 | } | ||
| 133 | |||
| 99 | // ── Tools View ── | 134 | // ── Tools View ── |
| 100 | 135 | ||
| 101 | export function ToolsView({ | 136 | export function ToolsView() { |
| 102 | initialSource, | 137 | const navigate = useNavigate(); |
| 103 | initialTab, | 138 | const location = useLocation(); |
| 104 | }: { | ||
| 105 | initialSource?: string | null; | ||
| 106 | initialTab?: string | null; | ||
| 107 | }) { | ||
| 108 | const { context, loading: sessionLoading } = useSession(); | 139 | const { context, loading: sessionLoading } = useSession(); |
| 109 | const [selectedSource, setSelectedSource] = useState<string | null>(initialSource ?? null); | 140 | const activeTab = useMemo(() => parseToolsTab(location.pathname), [location.pathname]); |
| 110 | const [activeTab, setActiveTab] = useState<ToolsTab>(parseInitialTab(initialTab)); | 141 | const [catalogQueryState, setCatalogQueryState] = useQueryStates({ |
| 142 | q: parseAsString.withDefault(""), | ||
| 143 | approval: parseAsStringLiteral(["all", "required", "auto"]).withDefault("all"), | ||
| 144 | tool: parseAsString.withDefault(""), | ||
| 145 | selected: parseAsNativeArrayOf(parseAsString), | ||
| 146 | source: parseAsString.withDefault(""), | ||
| 147 | sourcePanel: parseAsString.withDefault(""), | ||
| 148 | }, { | ||
| 149 | history: "replace", | ||
| 150 | }); | ||
| 151 | |||
| 152 | const catalogSearchValue = catalogQueryState.q; | ||
| 153 | const catalogFilterValue = useMemo( | ||
| 154 | () => normalizeCatalogApprovalFilter(catalogQueryState.approval), | ||
| 155 | [catalogQueryState.approval], | ||
| 156 | ); | ||
| 157 | const catalogSourceValue = useMemo(() => mapEmptyQueryValueToNull(catalogQueryState.source), [catalogQueryState.source]); | ||
| 158 | const catalogActiveToolPath = mapEmptyQueryValueToNull(catalogQueryState.tool); | ||
| 159 | const catalogSelectedToolPaths = useMemo( | ||
| 160 | () => catalogQueryState.selected ?? [], | ||
| 161 | [catalogQueryState.selected], | ||
| 162 | ); | ||
| 163 | const catalogFocusedSourceName = useMemo( | ||
| 164 | () => mapEmptyQueryValueToNull(catalogQueryState.sourcePanel), | ||
| 165 | [catalogQueryState.sourcePanel], | ||
| 166 | ); | ||
| 111 | const [connectionDialogOpen, setConnectionDialogOpen] = useState(false); | 167 | const [connectionDialogOpen, setConnectionDialogOpen] = useState(false); |
| 112 | const [connectionDialogEditing, setConnectionDialogEditing] = useState<CredentialRecord | null>(null); | 168 | const [connectionDialogEditing, setConnectionDialogEditing] = useState<CredentialRecord | null>(null); |
| 113 | const [connectionDialogSourceKey, setConnectionDialogSourceKey] = useState<string | null>(null); | 169 | const [connectionDialogSourceKey, setConnectionDialogSourceKey] = useState<string | null>(null); |
| @@ -316,6 +372,7 @@ export function ToolsView({ · 202 unmodified lines | |||
| 316 | 372 | ||
| 317 | const existingSourceNames = useMemo(() => new Set(sourceItems.map((source) => source.name)), [sourceItems]); | 373 | const existingSourceNames = useMemo(() => new Set(sourceItems.map((source) => source.name)), [sourceItems]); |
| 318 | const warningsBySource = useMemo(() => warningsBySourceName(warnings), [warnings]); | 374 | const warningsBySource = useMemo(() => warningsBySourceName(warnings), [warnings]); |
| 375 | |||
| 319 | const sourceDialogMeta = useMemo(() => { | 376 | const sourceDialogMeta = useMemo(() => { |
| 320 | const bySource: Record<string, SourceDialogMeta> = {}; | 377 | const bySource: Record<string, SourceDialogMeta> = {}; |
| 321 | for (const source of sourceItems) { | 378 | for (const source of sourceItems) { |
| @@ -328,10 +385,10 @@ export function ToolsView({ · 6 unmodified lines | |||
| 328 | } | 385 | } |
| 329 | return bySource; | 386 | return bySource; |
| 330 | }, [sourceItems, sourceQuality, refreshingTools, warningsBySource]); | 387 | }, [sourceItems, sourceQuality, refreshingTools, warningsBySource]); |
| 331 | const activeSource = selectedSource | 388 | const activeSource = catalogSourceValue |
| 332 | && (sourceItems.some((source) => source.name === selectedSource) || toolSourceNames.has(selectedSource)) | 389 | && (sourceItems.some((source) => source.name === catalogSourceValue) || toolSourceNames.has(catalogSourceValue)) |
| 333 | ? selectedSource | 390 | ? catalogSourceValue |
| 334 | : null; | 391 | : null; |
| 335 | 392 | ||
| 336 | const handleSourceAdded = useCallback((source: ToolSourceRecord) => { | 393 | const handleSourceAdded = useCallback((source: ToolSourceRecord) => { |
| 337 | setOptimisticOps((ops) => [ | 394 | setOptimisticOps((ops) => [ |
| @@ -340,13 +397,67 @@ export function ToolsView({ · 2 unmodified lines | |||
| 340 | ]); | 397 | ]); |
| 341 | }, []); | 398 | }, []); |
| 342 | 399 | ||
| 400 | const syncSourceToUrl = useCallback((sourceName: string) => { | ||
| 401 | void setCatalogQueryState({ | ||
| 402 | source: sourceName, | ||
| 403 | }, { | ||
| 404 | history: "replace", | ||
| 405 | }); | ||
| 406 | }, [setCatalogQueryState]); | ||
| 407 | |||
| 343 | const handleSourceDeleted = useCallback((sourceName: string) => { | 408 | const handleSourceDeleted = useCallback((sourceName: string) => { |
| 344 | setOptimisticOps((ops) => [ | 409 | setOptimisticOps((ops) => [ |
| 345 | ...ops, | 410 | ...ops, |
| 346 | { kind: "remove", sourceName, removedAt: Date.now() }, | 411 | { kind: "remove", sourceName, removedAt: Date.now() }, |
| 347 | ]); | 412 | ]); |
| 348 | setSelectedSource((current) => (current === sourceName ? null : current)); | 413 | syncSourceToUrl(activeSource === sourceName ? "" : (activeSource ?? "")); |
| 349 | }, []); | 414 | }, [activeSource, syncSourceToUrl]); |
| 415 | |||
| 416 | const setCatalogSearch = useCallback((search: string) => { | ||
| 417 | void setCatalogQueryState({ | ||
| 418 | q: search, | ||
| 419 | }, { | ||
| 420 | history: "replace", | ||
| 421 | }); | ||
| 422 | }, [setCatalogQueryState]); | ||
| 423 | |||
| 424 | const setCatalogApprovalFilter = useCallback((filterApproval: FilterApproval) => { | ||
| 425 | void setCatalogQueryState({ | ||
| 426 | approval: filterApproval, | ||
| 427 | }, { | ||
| 428 | history: "replace", | ||
| 429 | }); | ||
| 430 | }, [setCatalogQueryState]); | ||
| 431 | |||
| 432 | const setCatalogActiveToolPath = useCallback((toolPath: string | null) => { | ||
| 433 | void setCatalogQueryState({ | ||
| 434 | tool: toolPath ?? "", | ||
| 435 | }, { | ||
| 436 | history: "replace", | ||
| 437 | }); | ||
| 438 | }, [setCatalogQueryState]); | ||
| 439 | |||
| 440 | const setCatalogSelectedToolPaths = useCallback((toolPaths: string[]) => { | ||
| 441 | void setCatalogQueryState({ | ||
| 442 | selected: toolPaths, | ||
| 443 | }, { | ||
| 444 | history: "replace", | ||
| 445 | }); | ||
| 446 | }, [setCatalogQueryState]); | ||
| 447 | |||
| 448 | const setCatalogFocusedSourceName = useCallback((sourceName: string | null) => { | ||
| 449 | void setCatalogQueryState({ | ||
| 450 | sourcePanel: sourceName ?? "", | ||
| 451 | }, { | ||
| 452 | history: "replace", | ||
| 453 | }); | ||
| 454 | }, [setCatalogQueryState]); | ||
| 455 | |||
| 456 | const setActiveTab = useCallback((nextTab: ToolsTab) => { | ||
| 457 | const nextPath = toolsPathFromTab(nextTab); | ||
| 458 | const qs = location.searchStr; | ||
| 459 | navigate(qs ? `${nextPath}?${qs}` : nextPath); | ||
| 460 | }, [location.searchStr, navigate]); | ||
| 350 | 461 | ||
| 351 | const handleRegenerateInventory = useCallback(async () => { | 462 | const handleRegenerateInventory = useCallback(async () => { |
| 352 | if (!context || regenerationInFlight) { | 463 | if (!context || regenerationInFlight) { |
| @@ -442,7 +553,7 @@ export function ToolsView({ · 89 unmodified lines | |||
| 442 | {loadingTools ? "..." : totalTools} | 553 | {loadingTools ? "..." : totalTools} |
| 443 | </span> | 554 | </span> |
| 444 | </TabsTrigger> | 555 | </TabsTrigger> |
| 445 | <TabsTrigger value="credentials" className="text-xs data-[state=active]:bg-background"> | 556 | <TabsTrigger value="connections" className="text-xs data-[state=active]:bg-background"> |
| 446 | Connections | 557 | Connections |
| 447 | {credentials && ( | 558 | {credentials && ( |
| 448 | <span className="ml-1.5 text-[10px] font-mono text-muted-foreground"> | 559 | <span className="ml-1.5 text-[10px] font-mono text-muted-foreground"> |
| @@ -478,16 +589,24 @@ export function ToolsView({ · 29 unmodified lines | |||
| 478 | sourceHasMoreTools={sourceHasMoreTools} | 589 | sourceHasMoreTools={sourceHasMoreTools} |
| 479 | sourceLoadingMoreTools={sourceLoadingMoreTools} | 590 | sourceLoadingMoreTools={sourceLoadingMoreTools} |
| 480 | onLoadMoreToolsForSource={loadMoreToolsForSource} | 591 | onLoadMoreToolsForSource={loadMoreToolsForSource} |
| 481 | loading={loadingTools} | 592 | loading={loadingTools} |
| 482 | sourceDialogMeta={sourceDialogMeta} | 593 | sourceDialogMeta={sourceDialogMeta} |
| 483 | sourceAuthProfiles={sourceAuthProfiles} | 594 | sourceAuthProfiles={sourceAuthProfiles} |
| 484 | existingSourceNames={existingSourceNames} | 595 | existingSourceNames={existingSourceNames} |
| 485 | onSourceDeleted={handleSourceDeleted} | 596 | onSourceDeleted={handleSourceDeleted} |
| 486 | onLoadToolDetails={loadToolDetails} | 597 | onLoadToolDetails={loadToolDetails} |
| 487 | warnings={warnings} | 598 | warnings={warnings} |
| 488 | initialSource={initialSource} | 599 | activeSource={activeSource} |
| 489 | activeSource={activeSource} | 600 | searchValue={catalogSearchValue} |
| 490 | onActiveSourceChange={setSelectedSource} | 601 | filterApprovalValue={catalogFilterValue} |
| 602 | focusedToolPathValue={catalogActiveToolPath} | ||
| 603 | selectedToolPathsValue={catalogSelectedToolPaths} | ||
| 604 | focusedSourceNameValue={catalogFocusedSourceName} | ||
| 605 | onSearchValueChange={setCatalogSearch} | ||
| 606 | onFilterApprovalValueChange={setCatalogApprovalFilter} | ||
| 607 | onFocusedToolPathChange={setCatalogActiveToolPath} | ||
| 608 | onSelectedToolPathsChange={setCatalogSelectedToolPaths} | ||
| 609 | onFocusedSourceNameChange={setCatalogFocusedSourceName} | ||
| 491 | onRegenerate={handleRegenerateInventory} | 610 | onRegenerate={handleRegenerateInventory} |
| 492 | inventoryState={inventoryStatus?.state} | 611 | inventoryState={inventoryStatus?.state} |
| 493 | inventoryError={inventoryStatus?.error} | 612 | inventoryError={inventoryStatus?.error} |
| @@ -503,7 +622,7 @@ export function ToolsView({ · 9 unmodified lines | |||
| 503 | </Card> | 622 | </Card> |
| 504 | </TabsContent> | 623 | </TabsContent> |
| 505 | 624 | ||
| 506 | <TabsContent value="credentials" className="mt-4"> | 625 | <TabsContent value="connections" className="mt-4"> |
| 507 | <CredentialsPanel | 626 | <CredentialsPanel |
| 508 | sources={sourceItems} | 627 | sources={sourceItems} |
| 509 | credentials={credentialItems} | 628 | credentials={credentialItems} |
| @@ -1,9 +1,9 @@ | |||
| 1 | import type { Id } from "@executor/database/convex/_generated/dataModel"; | 1 | import type { Id } from "@executor/database/convex/_generated/dataModel"; |
| 2 | import type { | 2 | import type { |
| 3 | ToolPolicyRecord as CoreToolPolicyRecord, | 3 | ToolPolicyRecord as CoreToolPolicyRecord, |
| 4 | ToolRoleBindingRecord as CoreToolRoleBindingRecord, | 4 | ToolPolicyAssignmentRecord as CoreToolPolicyAssignmentRecord, |
| 5 | ToolRoleRecord as CoreToolRoleRecord, | 5 | ToolPolicySetRecord as CoreToolPolicySetRecord, |
| 6 | ToolRoleRuleRecord as CoreToolRoleRuleRecord, | 6 | ToolPolicyRuleRecord as CoreToolPolicyRuleRecord, |
| 7 | } from "@executor/core/types"; | 7 | } from "@executor/core/types"; |
| 8 | 8 | ||
| 9 | // ── Shared types (inlined from @executor/contracts) ────────────────────────── | 9 | // ── Shared types (inlined from @executor/contracts) ────────────────────────── |
| @@ -85,9 +85,9 @@ export interface TaskEventRecord { · 75 unmodified lines | |||
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | export type ToolPolicyRecord = CoreToolPolicyRecord; | 87 | export type ToolPolicyRecord = CoreToolPolicyRecord; |
| 88 | export type ToolRoleRecord = CoreToolRoleRecord; | 88 | export type ToolPolicySetRecord = CoreToolPolicySetRecord; |
| 89 | export type ToolRoleRuleRecord = CoreToolRoleRuleRecord; | 89 | export type ToolPolicyRuleRecord = CoreToolPolicyRuleRecord; |
| 90 | export type ToolRoleBindingRecord = CoreToolRoleBindingRecord; | 90 | export type ToolPolicyAssignmentRecord = CoreToolPolicyAssignmentRecord; |
| 91 | 91 | ||
| 92 | export interface CredentialRecord { | 92 | export interface CredentialRecord { |
| 93 | id: string; | 93 | id: string; |
| @@ -9,7 +9,6 @@ · 8 unmodified lines | |||
| 9 | // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. | 9 | // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. |
| 10 | 10 | ||
| 11 | import { Route as rootRouteImport } from './routes/__root' | 11 | import { Route as rootRouteImport } from './routes/__root' |
| 12 | import { Route as ToolsRouteImport } from './routes/tools' | ||
| 13 | import { Route as TasksRouteImport } from './routes/tasks' | 12 | import { Route as TasksRouteImport } from './routes/tasks' |
| 14 | import { Route as StaticAppShellRouteImport } from './routes/static-app-shell' | 13 | import { Route as StaticAppShellRouteImport } from './routes/static-app-shell' |
| 15 | import { Route as SignUpRouteImport } from './routes/sign-up' | 14 | import { Route as SignUpRouteImport } from './routes/sign-up' |
| @@ -25,6 +24,11 @@ import { Route as CallbackRouteImport } from './routes/callback' · 9 unmodified lines | |||
| 25 | import { Route as BillingRouteImport } from './routes/billing' | 24 | import { Route as BillingRouteImport } from './routes/billing' |
| 26 | import { Route as ApprovalsRouteImport } from './routes/approvals' | 25 | import { Route as ApprovalsRouteImport } from './routes/approvals' |
| 27 | import { Route as IndexRouteImport } from './routes/index' | 26 | import { Route as IndexRouteImport } from './routes/index' |
| 27 | import { Route as ToolsIndexRouteImport } from './routes/tools/index' | ||
| 28 | import { Route as ToolsPoliciesRouteImport } from './routes/tools/policies' | ||
| 29 | import { Route as ToolsEditorRouteImport } from './routes/tools/editor' | ||
| 30 | import { Route as ToolsConnectionsRouteImport } from './routes/tools/connections' | ||
| 31 | import { Route as ToolsCatalogRouteImport } from './routes/tools/catalog' | ||
| 28 | import { Route as McpOauthStartRouteImport } from './routes/mcp/oauth/start' | 32 | import { Route as McpOauthStartRouteImport } from './routes/mcp/oauth/start' |
| 29 | import { Route as McpOauthResultRouteImport } from './routes/mcp/oauth/result' | 33 | import { Route as McpOauthResultRouteImport } from './routes/mcp/oauth/result' |
| 30 | import { Route as McpOauthDetectRouteImport } from './routes/mcp/oauth/detect' | 34 | import { Route as McpOauthDetectRouteImport } from './routes/mcp/oauth/detect' |
| @@ -34,11 +38,6 @@ import { Route as ApiOpenapiInspectRouteImport } from './routes/api/openapi/insp · 3 unmodified lines | |||
| 34 | import { Route as ApiAuthCallbackRouteImport } from './routes/api/auth/callback' | 38 | import { Route as ApiAuthCallbackRouteImport } from './routes/api/auth/callback' |
| 35 | import { Route as ApiAuthAnonymousTokenRouteImport } from './routes/api/auth/anonymous-token' | 39 | import { Route as ApiAuthAnonymousTokenRouteImport } from './routes/api/auth/anonymous-token' |
| 36 | 40 | ||
| 37 | const ToolsRoute = ToolsRouteImport.update({ | ||
| 38 | id: '/tools', | ||
| 39 | path: '/tools', | ||
| 40 | getParentRoute: () => rootRouteImport, | ||
| 41 | } as any) | ||
| 42 | const TasksRoute = TasksRouteImport.update({ | 41 | const TasksRoute = TasksRouteImport.update({ |
| 43 | id: '/tasks', | 42 | id: '/tasks', |
| 44 | path: '/tasks', | 43 | path: '/tasks', |
| @@ -114,6 +113,31 @@ const IndexRoute = IndexRouteImport.update({ · 69 unmodified lines | |||
| 114 | path: '/', | 113 | path: '/', |
| 115 | getParentRoute: () => rootRouteImport, | 114 | getParentRoute: () => rootRouteImport, |
| 116 | } as any) | 115 | } as any) |
| 116 | const ToolsIndexRoute = ToolsIndexRouteImport.update({ | ||
| 117 | id: '/tools/', | ||
| 118 | path: '/tools/', | ||
| 119 | getParentRoute: () => rootRouteImport, | ||
| 120 | } as any) | ||
| 121 | const ToolsPoliciesRoute = ToolsPoliciesRouteImport.update({ | ||
| 122 | id: '/tools/policies', | ||
| 123 | path: '/tools/policies', | ||
| 124 | getParentRoute: () => rootRouteImport, | ||
| 125 | } as any) | ||
| 126 | const ToolsEditorRoute = ToolsEditorRouteImport.update({ | ||
| 127 | id: '/tools/editor', | ||
| 128 | path: '/tools/editor', | ||
| 129 | getParentRoute: () => rootRouteImport, | ||
| 130 | } as any) | ||
| 131 | const ToolsConnectionsRoute = ToolsConnectionsRouteImport.update({ | ||
| 132 | id: '/tools/connections', | ||
| 133 | path: '/tools/connections', | ||
| 134 | getParentRoute: () => rootRouteImport, | ||
| 135 | } as any) | ||
| 136 | const ToolsCatalogRoute = ToolsCatalogRouteImport.update({ | ||
| 137 | id: '/tools/catalog', | ||
| 138 | path: '/tools/catalog', | ||
| 139 | getParentRoute: () => rootRouteImport, | ||
| 140 | } as any) | ||
| 117 | const McpOauthStartRoute = McpOauthStartRouteImport.update({ | 141 | const McpOauthStartRoute = McpOauthStartRouteImport.update({ |
| 118 | id: '/mcp/oauth/start', | 142 | id: '/mcp/oauth/start', |
| 119 | path: '/mcp/oauth/start', | 143 | path: '/mcp/oauth/start', |
| @@ -171,7 +195,11 @@ export interface FileRoutesByFullPath { · 51 unmodified lines | |||
| 171 | '/sign-up': typeof SignUpRoute | 195 | '/sign-up': typeof SignUpRoute |
| 172 | '/static-app-shell': typeof StaticAppShellRoute | 196 | '/static-app-shell': typeof StaticAppShellRoute |
| 173 | '/tasks': typeof TasksRoute | 197 | '/tasks': typeof TasksRoute |
| 174 | '/tools': typeof ToolsRoute | 198 | '/tools/catalog': typeof ToolsCatalogRoute |
| 199 | '/tools/connections': typeof ToolsConnectionsRoute | ||
| 200 | '/tools/editor': typeof ToolsEditorRoute | ||
| 201 | '/tools/policies': typeof ToolsPoliciesRoute | ||
| 202 | '/tools/': typeof ToolsIndexRoute | ||
| 175 | '/api/auth/anonymous-token': typeof ApiAuthAnonymousTokenRoute | 203 | '/api/auth/anonymous-token': typeof ApiAuthAnonymousTokenRoute |
| 176 | '/api/auth/callback': typeof ApiAuthCallbackRoute | 204 | '/api/auth/callback': typeof ApiAuthCallbackRoute |
| 177 | '/api/openapi/inspect': typeof ApiOpenapiInspectRoute | 205 | '/api/openapi/inspect': typeof ApiOpenapiInspectRoute |
| @@ -197,7 +225,11 @@ export interface FileRoutesByTo { · 19 unmodified lines | |||
| 197 | '/sign-up': typeof SignUpRoute | 225 | '/sign-up': typeof SignUpRoute |
| 198 | '/static-app-shell': typeof StaticAppShellRoute | 226 | '/static-app-shell': typeof StaticAppShellRoute |
| 199 | '/tasks': typeof TasksRoute | 227 | '/tasks': typeof TasksRoute |
| 200 | '/tools': typeof ToolsRoute | 228 | '/tools/catalog': typeof ToolsCatalogRoute |
| 229 | '/tools/connections': typeof ToolsConnectionsRoute | ||
| 230 | '/tools/editor': typeof ToolsEditorRoute | ||
| 231 | '/tools/policies': typeof ToolsPoliciesRoute | ||
| 232 | '/tools': typeof ToolsIndexRoute | ||
| 201 | '/api/auth/anonymous-token': typeof ApiAuthAnonymousTokenRoute | 233 | '/api/auth/anonymous-token': typeof ApiAuthAnonymousTokenRoute |
| 202 | '/api/auth/callback': typeof ApiAuthCallbackRoute | 234 | '/api/auth/callback': typeof ApiAuthCallbackRoute |
| 203 | '/api/openapi/inspect': typeof ApiOpenapiInspectRoute | 235 | '/api/openapi/inspect': typeof ApiOpenapiInspectRoute |
| @@ -224,7 +256,11 @@ export interface FileRoutesById { · 20 unmodified lines | |||
| 224 | '/sign-up': typeof SignUpRoute | 256 | '/sign-up': typeof SignUpRoute |
| 225 | '/static-app-shell': typeof StaticAppShellRoute | 257 | '/static-app-shell': typeof StaticAppShellRoute |
| 226 | '/tasks': typeof TasksRoute | 258 | '/tasks': typeof TasksRoute |
| 227 | '/tools': typeof ToolsRoute | 259 | '/tools/catalog': typeof ToolsCatalogRoute |
| 260 | '/tools/connections': typeof ToolsConnectionsRoute | ||
| 261 | '/tools/editor': typeof ToolsEditorRoute | ||
| 262 | '/tools/policies': typeof ToolsPoliciesRoute | ||
| 263 | '/tools/': typeof ToolsIndexRoute | ||
| 228 | '/api/auth/anonymous-token': typeof ApiAuthAnonymousTokenRoute | 264 | '/api/auth/anonymous-token': typeof ApiAuthAnonymousTokenRoute |
| 229 | '/api/auth/callback': typeof ApiAuthCallbackRoute | 265 | '/api/auth/callback': typeof ApiAuthCallbackRoute |
| 230 | '/api/openapi/inspect': typeof ApiOpenapiInspectRoute | 266 | '/api/openapi/inspect': typeof ApiOpenapiInspectRoute |
| @@ -252,7 +288,11 @@ export interface FileRouteTypes { · 21 unmodified lines | |||
| 252 | | '/sign-up' | 288 | | '/sign-up' |
| 253 | | '/static-app-shell' | 289 | | '/static-app-shell' |
| 254 | | '/tasks' | 290 | | '/tasks' |
| 255 | | '/tools' | 291 | | '/tools/catalog' |
| 292 | | '/tools/connections' | ||
| 293 | | '/tools/editor' | ||
| 294 | | '/tools/policies' | ||
| 295 | | '/tools/' | ||
| 256 | | '/api/auth/anonymous-token' | 296 | | '/api/auth/anonymous-token' |
| 257 | | '/api/auth/callback' | 297 | | '/api/auth/callback' |
| 258 | | '/api/openapi/inspect' | 298 | | '/api/openapi/inspect' |
| @@ -278,6 +318,10 @@ export interface FileRouteTypes { · 19 unmodified lines | |||
| 278 | | '/sign-up' | 318 | | '/sign-up' |
| 279 | | '/static-app-shell' | 319 | | '/static-app-shell' |
| 280 | | '/tasks' | 320 | | '/tasks' |
| 321 | | '/tools/catalog' | ||
| 322 | | '/tools/connections' | ||
| 323 | | '/tools/editor' | ||
| 324 | | '/tools/policies' | ||
| 281 | | '/tools' | 325 | | '/tools' |
| 282 | | '/api/auth/anonymous-token' | 326 | | '/api/auth/anonymous-token' |
| 283 | | '/api/auth/callback' | 327 | | '/api/auth/callback' |
| @@ -304,7 +348,11 @@ export interface FileRouteTypes { · 20 unmodified lines | |||
| 304 | | '/sign-up' | 348 | | '/sign-up' |
| 305 | | '/static-app-shell' | 349 | | '/static-app-shell' |
| 306 | | '/tasks' | 350 | | '/tasks' |
| 307 | | '/tools' | 351 | | '/tools/catalog' |
| 352 | | '/tools/connections' | ||
| 353 | | '/tools/editor' | ||
| 354 | | '/tools/policies' | ||
| 355 | | '/tools/' | ||
| 308 | | '/api/auth/anonymous-token' | 356 | | '/api/auth/anonymous-token' |
| 309 | | '/api/auth/callback' | 357 | | '/api/auth/callback' |
| 310 | | '/api/openapi/inspect' | 358 | | '/api/openapi/inspect' |
| @@ -331,7 +379,11 @@ export interface RootRouteChildren { · 20 unmodified lines | |||
| 331 | SignUpRoute: typeof SignUpRoute | 379 | SignUpRoute: typeof SignUpRoute |
| 332 | StaticAppShellRoute: typeof StaticAppShellRoute | 380 | StaticAppShellRoute: typeof StaticAppShellRoute |
| 333 | TasksRoute: typeof TasksRoute | 381 | TasksRoute: typeof TasksRoute |
| 334 | ToolsRoute: typeof ToolsRoute | 382 | ToolsCatalogRoute: typeof ToolsCatalogRoute |
| 383 | ToolsConnectionsRoute: typeof ToolsConnectionsRoute | ||
| 384 | ToolsEditorRoute: typeof ToolsEditorRoute | ||
| 385 | ToolsPoliciesRoute: typeof ToolsPoliciesRoute | ||
| 386 | ToolsIndexRoute: typeof ToolsIndexRoute | ||
| 335 | ApiAuthAnonymousTokenRoute: typeof ApiAuthAnonymousTokenRoute | 387 | ApiAuthAnonymousTokenRoute: typeof ApiAuthAnonymousTokenRoute |
| 336 | ApiAuthCallbackRoute: typeof ApiAuthCallbackRoute | 388 | ApiAuthCallbackRoute: typeof ApiAuthCallbackRoute |
| 337 | ApiOpenapiInspectRoute: typeof ApiOpenapiInspectRoute | 389 | ApiOpenapiInspectRoute: typeof ApiOpenapiInspectRoute |
| @@ -344,13 +396,6 @@ export interface RootRouteChildren { · 6 unmodified lines | |||
| 344 | 396 | ||
| 345 | declare module '@tanstack/react-router' { | 397 | declare module '@tanstack/react-router' { |
| 346 | interface FileRoutesByPath { | 398 | interface FileRoutesByPath { |
| 347 | '/tools': { | ||
| 348 | id: '/tools' | ||
| 349 | path: '/tools' | ||
| 350 | fullPath: '/tools' | ||
| 351 | preLoaderRoute: typeof ToolsRouteImport | ||
| 352 | parentRoute: typeof rootRouteImport | ||
| 353 | } | ||
| 354 | '/tasks': { | 399 | '/tasks': { |
| 355 | id: '/tasks' | 400 | id: '/tasks' |
| 356 | path: '/tasks' | 401 | path: '/tasks' |
| @@ -456,6 +501,41 @@ declare module '@tanstack/react-router' { · 99 unmodified lines | |||
| 456 | preLoaderRoute: typeof IndexRouteImport | 501 | preLoaderRoute: typeof IndexRouteImport |
| 457 | parentRoute: typeof rootRouteImport | 502 | parentRoute: typeof rootRouteImport |
| 458 | } | 503 | } |
| 504 | '/tools/': { | ||
| 505 | id: '/tools/' | ||
| 506 | path: '/tools' | ||
| 507 | fullPath: '/tools/' | ||
| 508 | preLoaderRoute: typeof ToolsIndexRouteImport | ||
| 509 | parentRoute: typeof rootRouteImport | ||
| 510 | } | ||
| 511 | '/tools/policies': { | ||
| 512 | id: '/tools/policies' | ||
| 513 | path: '/tools/policies' | ||
| 514 | fullPath: '/tools/policies' | ||
| 515 | preLoaderRoute: typeof ToolsPoliciesRouteImport | ||
| 516 | parentRoute: typeof rootRouteImport | ||
| 517 | } | ||
| 518 | '/tools/editor': { | ||
| 519 | id: '/tools/editor' | ||
| 520 | path: '/tools/editor' | ||
| 521 | fullPath: '/tools/editor' | ||
| 522 | preLoaderRoute: typeof ToolsEditorRouteImport | ||
| 523 | parentRoute: typeof rootRouteImport | ||
| 524 | } | ||
| 525 | '/tools/connections': { | ||
| 526 | id: '/tools/connections' | ||
| 527 | path: '/tools/connections' | ||
| 528 | fullPath: '/tools/connections' | ||
| 529 | preLoaderRoute: typeof ToolsConnectionsRouteImport | ||
| 530 | parentRoute: typeof rootRouteImport | ||
| 531 | } | ||
| 532 | '/tools/catalog': { | ||
| 533 | id: '/tools/catalog' | ||
| 534 | path: '/tools/catalog' | ||
| 535 | fullPath: '/tools/catalog' | ||
| 536 | preLoaderRoute: typeof ToolsCatalogRouteImport | ||
| 537 | parentRoute: typeof rootRouteImport | ||
| 538 | } | ||
| 459 | '/mcp/oauth/start': { | 539 | '/mcp/oauth/start': { |
| 460 | id: '/mcp/oauth/start' | 540 | id: '/mcp/oauth/start' |
| 461 | path: '/mcp/oauth/start' | 541 | path: '/mcp/oauth/start' |
| @@ -531,7 +611,11 @@ const rootRouteChildren: RootRouteChildren = { · 69 unmodified lines | |||
| 531 | SignUpRoute: SignUpRoute, | 611 | SignUpRoute: SignUpRoute, |
| 532 | StaticAppShellRoute: StaticAppShellRoute, | 612 | StaticAppShellRoute: StaticAppShellRoute, |
| 533 | TasksRoute: TasksRoute, | 613 | TasksRoute: TasksRoute, |
| 534 | ToolsRoute: ToolsRoute, | 614 | ToolsCatalogRoute: ToolsCatalogRoute, |
| 615 | ToolsConnectionsRoute: ToolsConnectionsRoute, | ||
| 616 | ToolsEditorRoute: ToolsEditorRoute, | ||
| 617 | ToolsPoliciesRoute: ToolsPoliciesRoute, | ||
| 618 | ToolsIndexRoute: ToolsIndexRoute, | ||
| 535 | ApiAuthAnonymousTokenRoute: ApiAuthAnonymousTokenRoute, | 619 | ApiAuthAnonymousTokenRoute: ApiAuthAnonymousTokenRoute, |
| 536 | ApiAuthCallbackRoute: ApiAuthCallbackRoute, | 620 | ApiAuthCallbackRoute: ApiAuthCallbackRoute, |
| 537 | ApiOpenapiInspectRoute: ApiOpenapiInspectRoute, | 621 | ApiOpenapiInspectRoute: ApiOpenapiInspectRoute, |
| @@ -5,6 +5,7 @@ import { AppConvexProvider } from "@/lib/convex-provider"; · 4 unmodified lines | |||
| 5 | import { QueryProvider } from "@/lib/query-provider"; | 5 | import { QueryProvider } from "@/lib/query-provider"; |
| 6 | import { SessionProvider } from "@/lib/session-context"; | 6 | import { SessionProvider } from "@/lib/session-context"; |
| 7 | import { AppErrorBoundary } from "@/components/app-error-boundary"; | 7 | import { AppErrorBoundary } from "@/components/app-error-boundary"; |
| 8 | import { NuqsAdapter } from "nuqs/adapters/tanstack-router"; | ||
| 8 | import { runtimeConfigFromEnv } from "@/lib/runtime-config"; | 9 | import { runtimeConfigFromEnv } from "@/lib/runtime-config"; |
| 9 | import appCss from "../app/globals.css?url"; | 10 | import appCss from "../app/globals.css?url"; |
| 10 | 11 | ||
| @@ -55,23 +56,25 @@ function RootDocument({ children }: { children: React.ReactNode }) { · 44 unmodified lines | |||
| 55 | __html: `window.__EXECUTOR_RUNTIME_CONFIG__ = ${runtimeConfig};`, | 56 | __html: `window.__EXECUTOR_RUNTIME_CONFIG__ = ${runtimeConfig};`, |
| 56 | }} | 57 | }} |
| 57 | /> | 58 | /> |
| 58 | <ThemeProvider | 59 | <NuqsAdapter> |
| 59 | attribute="class" | 60 | <ThemeProvider |
| 60 | defaultTheme="dark" | 61 | attribute="class" |
| 61 | enableSystem | 62 | defaultTheme="dark" |
| 62 | enableColorScheme | 63 | enableSystem |
| 63 | > | 64 | enableColorScheme |
| 64 | <AppErrorBoundary> | 65 | > |
| 65 | <QueryProvider> | 66 | <AppErrorBoundary> |
| 66 | <AppConvexProvider> | 67 | <QueryProvider> |
| 67 | <SessionProvider> | 68 | <AppConvexProvider> |
| 68 | {children} | 69 | <SessionProvider> |
| 69 | </SessionProvider> | 70 | {children} |
| 70 | </AppConvexProvider> | 71 | </SessionProvider> |
| 71 | </QueryProvider> | 72 | </AppConvexProvider> |
| 72 | </AppErrorBoundary> | 73 | </QueryProvider> |
| 73 | <Toaster position="bottom-right" /> | 74 | </AppErrorBoundary> |
| 74 | </ThemeProvider> | 75 | <Toaster position="bottom-right" /> |
| 76 | </ThemeProvider> | ||
| 77 | </NuqsAdapter> | ||
| 75 | <Scripts /> | 78 | <Scripts /> |
| 76 | </body> | 79 | </body> |
| 77 | </html> | 80 | </html> |
| @@ -1,22 +0,0 @@ | |||
| 1 | import { createFileRoute } from "@tanstack/react-router"; | ||
| 2 | import { AppShell } from "@/components/app-shell"; | ||
| 3 | import { ToolsView } from "@/components/tools/view"; | ||
| 4 | import { useSearchParams } from "@/lib/router"; | ||
| 5 | |||
| 6 | export const Route = createFileRoute("/tools")({ | ||
| 7 | component: ToolsPage, | ||
| 8 | }); | ||
| 9 | |||
| 10 | function ToolsPage() { | ||
| 11 | const [searchParams] = useSearchParams(); | ||
| 12 | const source = searchParams.get("source"); | ||
| 13 | const tab = searchParams.get("tab"); | ||
| 14 | |||
| 15 | return ( | ||
| 16 | <AppShell> | ||
| 17 | <div className="h-full min-h-0"> | ||
| 18 | <ToolsView key={`${tab ?? "catalog"}:${source ?? "all"}`} initialSource={source} initialTab={tab} /> | ||
| 19 | </div> | ||
| 20 | </AppShell> | ||
| 21 | ); | ||
| 22 | } | ||
| @@ -0,0 +1,15 @@ | |||
| 1 | import { createFileRoute } from "@tanstack/react-router"; | ||
| 2 | import { AppShell } from "@/components/app-shell"; | ||
| 3 | import { ToolsView } from "@/components/tools/view"; | ||
| 4 | |||
| 5 | export const Route = createFileRoute("/tools/catalog")({ | ||
| 6 | component: ToolsCatalogPage, | ||
| 7 | }); | ||
| 8 | |||
| 9 | function ToolsCatalogPage() { | ||
| 10 | return ( | ||
| 11 | <AppShell> | ||
| 12 | <ToolsView /> | ||
| 13 | </AppShell> | ||
| 14 | ); | ||
| 15 | } | ||
| @@ -0,0 +1,15 @@ | |||
| 1 | import { createFileRoute } from "@tanstack/react-router"; | ||
| 2 | import { AppShell } from "@/components/app-shell"; | ||
| 3 | import { ToolsView } from "@/components/tools/view"; | ||
| 4 | |||
| 5 | export const Route = createFileRoute("/tools/connections")({ | ||
| 6 | component: ToolsConnectionsPage, | ||
| 7 | }); | ||
| 8 | |||
| 9 | function ToolsConnectionsPage() { | ||
| 10 | return ( | ||
| 11 | <AppShell> | ||
| 12 | <ToolsView /> | ||
| 13 | </AppShell> | ||
| 14 | ); | ||
| 15 | } | ||
| @@ -0,0 +1,15 @@ | |||
| 1 | import { createFileRoute } from "@tanstack/react-router"; | ||
| 2 | import { AppShell } from "@/components/app-shell"; | ||
| 3 | import { ToolsView } from "@/components/tools/view"; | ||
| 4 | |||
| 5 | export const Route = createFileRoute("/tools/editor")({ | ||
| 6 | component: ToolsEditorPage, | ||
| 7 | }); | ||
| 8 | |||
| 9 | function ToolsEditorPage() { | ||
| 10 | return ( | ||
| 11 | <AppShell> | ||
| 12 | <ToolsView /> | ||
| 13 | </AppShell> | ||
| 14 | ); | ||
| 15 | } | ||
| @@ -0,0 +1,18 @@ | |||
| 1 | import { createFileRoute } from "@tanstack/react-router"; | ||
| 2 | import { AppShell } from "@/components/app-shell"; | ||
| 3 | import { Navigate, useLocation } from "@/lib/router"; | ||
| 4 | |||
| 5 | export const Route = createFileRoute("/tools/")({ | ||
| 6 | component: ToolsLayout, | ||
| 7 | }); | ||
| 8 | |||
| 9 | function ToolsLayout() { | ||
| 10 | const location = useLocation(); | ||
| 11 | const search = location.searchStr ? `?${location.searchStr}` : ""; | ||
| 12 | |||
| 13 | return ( | ||
| 14 | <AppShell> | ||
| 15 | <Navigate to={`/tools/catalog${search}`} replace /> | ||
| 16 | </AppShell> | ||
| 17 | ); | ||
| 18 | } | ||
| @@ -0,0 +1,15 @@ | |||
| 1 | import { createFileRoute } from "@tanstack/react-router"; | ||
| 2 | import { AppShell } from "@/components/app-shell"; | ||
| 3 | import { ToolsView } from "@/components/tools/view"; | ||
| 4 | |||
| 5 | export const Route = createFileRoute("/tools/policies")({ | ||
| 6 | component: ToolsPoliciesPage, | ||
| 7 | }); | ||
| 8 | |||
| 9 | function ToolsPoliciesPage() { | ||
| 10 | return ( | ||
| 11 | <AppShell> | ||
| 12 | <ToolsView /> | ||
| 13 | </AppShell> | ||
| 14 | ); | ||
| 15 | } | ||
| @@ -123,7 +123,7 @@ export interface ToolPolicyRecord { · 122 unmodified lines | |||
| 123 | 123 | ||
| 124 | export type ToolRoleSelectorType = "all" | "source" | "namespace" | "tool_path"; | 124 | export type ToolRoleSelectorType = "all" | "source" | "namespace" | "tool_path"; |
| 125 | 125 | ||
| 126 | export interface ToolRoleRecord { | 126 | export interface ToolPolicySetRecord { |
| 127 | id: string; | 127 | id: string; |
| 128 | organizationId: Id<"organizations">; | 128 | organizationId: Id<"organizations">; |
| 129 | name: string; | 129 | name: string; |
| @@ -133,7 +133,7 @@ export interface ToolRoleRecord { · 3 unmodified lines | |||
| 133 | updatedAt: number; | 133 | updatedAt: number; |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | export interface ToolRoleRuleRecord { | 136 | export interface ToolPolicyRuleRecord { |
| 137 | id: string; | 137 | id: string; |
| 138 | roleId: string; | 138 | roleId: string; |
| 139 | organizationId: Id<"organizations">; | 139 | organizationId: Id<"organizations">; |
| @@ -150,7 +150,7 @@ export interface ToolRoleRuleRecord { · 10 unmodified lines | |||
| 150 | updatedAt: number; | 150 | updatedAt: number; |
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | export interface ToolRoleBindingRecord { | 153 | export interface ToolPolicyAssignmentRecord { |
| 154 | id: string; | 154 | id: string; |
| 155 | roleId: string; | 155 | roleId: string; |
| 156 | organizationId: Id<"organizations">; | 156 | organizationId: Id<"organizations">; |
| @@ -306,19 +306,13 @@ export declare const api: { · 305 unmodified lines | |||
| 306 | { accountId?: string; sessionId?: string }, | 306 | { accountId?: string; sessionId?: string }, |
| 307 | any | 307 | any |
| 308 | >; | 308 | >; |
| 309 | deleteToolRole: FunctionReference< | 309 | deleteToolPolicyAssignment: FunctionReference< |
| 310 | "mutation", | ||
| 311 | "public", | ||
| 312 | { roleId: string; sessionId?: string; workspaceId: Id<"workspaces"> }, | ||
| 313 | any | ||
| 314 | >; | ||
| 315 | deleteToolRoleBinding: FunctionReference< | ||
| 316 | "mutation", | 310 | "mutation", |
| 317 | "public", | 311 | "public", |
| 318 | { bindingId: string; sessionId?: string; workspaceId: Id<"workspaces"> }, | 312 | { bindingId: string; sessionId?: string; workspaceId: Id<"workspaces"> }, |
| 319 | any | 313 | any |
| 320 | >; | 314 | >; |
| 321 | deleteToolRoleRule: FunctionReference< | 315 | deleteToolPolicyRule: FunctionReference< |
| 322 | "mutation", | 316 | "mutation", |
| 323 | "public", | 317 | "public", |
| 324 | { | 318 | { |
| @@ -329,6 +323,12 @@ export declare const api: { · 4 unmodified lines | |||
| 329 | }, | 323 | }, |
| 330 | any | 324 | any |
| 331 | >; | 325 | >; |
| 326 | deleteToolPolicySet: FunctionReference< | ||
| 327 | "mutation", | ||
| 328 | "public", | ||
| 329 | { roleId: string; sessionId?: string; workspaceId: Id<"workspaces"> }, | ||
| 330 | any | ||
| 331 | >; | ||
| 332 | deleteToolSource: FunctionReference< | 332 | deleteToolSource: FunctionReference< |
| 333 | "mutation", | 333 | "mutation", |
| 334 | "public", | 334 | "public", |
| @@ -381,19 +381,19 @@ export declare const api: { · 46 unmodified lines | |||
| 381 | { sessionId?: string; workspaceId: Id<"workspaces"> }, | 381 | { sessionId?: string; workspaceId: Id<"workspaces"> }, |
| 382 | any | 382 | any |
| 383 | >; | 383 | >; |
| 384 | listToolRoleBindings: FunctionReference< | 384 | listToolPolicyAssignments: FunctionReference< |
| 385 | "query", | 385 | "query", |
| 386 | "public", | 386 | "public", |
| 387 | { roleId?: string; sessionId?: string; workspaceId: Id<"workspaces"> }, | 387 | { roleId?: string; sessionId?: string; workspaceId: Id<"workspaces"> }, |
| 388 | any | 388 | any |
| 389 | >; | 389 | >; |
| 390 | listToolRoleRules: FunctionReference< | 390 | listToolPolicyRules: FunctionReference< |
| 391 | "query", | 391 | "query", |
| 392 | "public", | 392 | "public", |
| 393 | { roleId: string; sessionId?: string; workspaceId: Id<"workspaces"> }, | 393 | { roleId: string; sessionId?: string; workspaceId: Id<"workspaces"> }, |
| 394 | any | 394 | any |
| 395 | >; | 395 | >; |
| 396 | listToolRoles: FunctionReference< | 396 | listToolPolicySets: FunctionReference< |
| 397 | "query", | 397 | "query", |
| 398 | "public", | 398 | "public", |
| 399 | { sessionId?: string; workspaceId: Id<"workspaces"> }, | 399 | { sessionId?: string; workspaceId: Id<"workspaces"> }, |
| @@ -439,19 +439,7 @@ export declare const api: { · 39 unmodified lines | |||
| 439 | }, | 439 | }, |
| 440 | any | 440 | any |
| 441 | >; | 441 | >; |
| 442 | upsertToolRole: FunctionReference< | 442 | upsertToolPolicyAssignment: FunctionReference< |
| 443 | "mutation", | ||
| 444 | "public", | ||
| 445 | { | ||
| 446 | description?: string; | ||
| 447 | id?: string; | ||
| 448 | name: string; | ||
| 449 | sessionId?: string; | ||
| 450 | workspaceId: Id<"workspaces">; | ||
| 451 | }, | ||
| 452 | any | ||
| 453 | >; | ||
| 454 | upsertToolRoleBinding: FunctionReference< | ||
| 455 | "mutation", | 443 | "mutation", |
| 456 | "public", | 444 | "public", |
| 457 | { | 445 | { |
| @@ -467,7 +455,7 @@ export declare const api: { · 9 unmodified lines | |||
| 467 | }, | 455 | }, |
| 468 | any | 456 | any |
| 469 | >; | 457 | >; |
| 470 | upsertToolRoleRule: FunctionReference< | 458 | upsertToolPolicyRule: FunctionReference< |
| 471 | "mutation", | 459 | "mutation", |
| 472 | "public", | 460 | "public", |
| 473 | { | 461 | { |
| @@ -490,6 +478,18 @@ export declare const api: { · 16 unmodified lines | |||
| 490 | }, | 478 | }, |
| 491 | any | 479 | any |
| 492 | >; | 480 | >; |
| 481 | upsertToolPolicySet: FunctionReference< | ||
| 482 | "mutation", | ||
| 483 | "public", | ||
| 484 | { | ||
| 485 | description?: string; | ||
| 486 | id?: string; | ||
| 487 | name: string; | ||
| 488 | sessionId?: string; | ||
| 489 | workspaceId: Id<"workspaces">; | ||
| 490 | }, | ||
| 491 | any | ||
| 492 | >; | ||
| 493 | upsertToolSource: FunctionReference< | 493 | upsertToolSource: FunctionReference< |
| 494 | "mutation", | 494 | "mutation", |
| 495 | "public", | 495 | "public", |
| @@ -742,22 +742,22 @@ export declare const internal: { · 246 unmodified lines | |||
| 742 | any | 742 | any |
| 743 | >; | 743 | >; |
| 744 | }; | 744 | }; |
| 745 | deleteToolRole: FunctionReference< | 745 | deleteToolPolicyAssignment: FunctionReference< |
| 746 | "mutation", | 746 | "mutation", |
| 747 | "internal", | 747 | "internal", |
| 748 | { roleId: string; workspaceId: Id<"workspaces"> }, | 748 | { bindingId: string; workspaceId: Id<"workspaces"> }, |
| 749 | any | 749 | any |
| 750 | >; | 750 | >; |
| 751 | deleteToolRoleBinding: FunctionReference< | 751 | deleteToolPolicyRule: FunctionReference< |
| 752 | "mutation", | 752 | "mutation", |
| 753 | "internal", | 753 | "internal", |
| 754 | { bindingId: string; workspaceId: Id<"workspaces"> }, | 754 | { roleId: string; ruleId: string; workspaceId: Id<"workspaces"> }, |
| 755 | any | 755 | any |
| 756 | >; | 756 | >; |
| 757 | deleteToolRoleRule: FunctionReference< | 757 | deleteToolPolicySet: FunctionReference< |
| 758 | "mutation", | 758 | "mutation", |
| 759 | "internal", | 759 | "internal", |
| 760 | { roleId: string; ruleId: string; workspaceId: Id<"workspaces"> }, | 760 | { roleId: string; workspaceId: Id<"workspaces"> }, |
| 761 | any | 761 | any |
| 762 | >; | 762 | >; |
| 763 | deleteToolSource: FunctionReference< | 763 | deleteToolSource: FunctionReference< |
| @@ -855,19 +855,19 @@ export declare const internal: { · 91 unmodified lines | |||
| 855 | { accountId?: Id<"accounts">; workspaceId: Id<"workspaces"> }, | 855 | { accountId?: Id<"accounts">; workspaceId: Id<"workspaces"> }, |
| 856 | any | 856 | any |
| 857 | >; | 857 | >; |
| 858 | listToolRoleBindings: FunctionReference< | 858 | listToolPolicyAssignments: FunctionReference< |
| 859 | "query", | 859 | "query", |
| 860 | "internal", | 860 | "internal", |
| 861 | { roleId?: string; workspaceId: Id<"workspaces"> }, | 861 | { roleId?: string; workspaceId: Id<"workspaces"> }, |
| 862 | any | 862 | any |
| 863 | >; | 863 | >; |
| 864 | listToolRoleRules: FunctionReference< | 864 | listToolPolicyRules: FunctionReference< |
| 865 | "query", | 865 | "query", |
| 866 | "internal", | 866 | "internal", |
| 867 | { roleId: string; workspaceId: Id<"workspaces"> }, | 867 | { roleId: string; workspaceId: Id<"workspaces"> }, |
| 868 | any | 868 | any |
| 869 | >; | 869 | >; |
| 870 | listToolRoles: FunctionReference< | 870 | listToolPolicySets: FunctionReference< |
| 871 | "query", | 871 | "query", |
| 872 | "internal", | 872 | "internal", |
| 873 | { workspaceId: Id<"workspaces"> }, | 873 | { workspaceId: Id<"workspaces"> }, |
| @@ -897,22 +897,22 @@ export declare const internal: { · 23 unmodified lines | |||
| 897 | any | 897 | any |
| 898 | >; | 898 | >; |
| 899 | policies: { | 899 | policies: { |
| 900 | deleteToolRole: FunctionReference< | 900 | deleteToolPolicyAssignment: FunctionReference< |
| 901 | "mutation", | 901 | "mutation", |
| 902 | "internal", | 902 | "internal", |
| 903 | { roleId: string; workspaceId: Id<"workspaces"> }, | 903 | { bindingId: string; workspaceId: Id<"workspaces"> }, |
| 904 | any | 904 | any |
| 905 | >; | 905 | >; |
| 906 | deleteToolRoleBinding: FunctionReference< | 906 | deleteToolPolicyRule: FunctionReference< |
| 907 | "mutation", | 907 | "mutation", |
| 908 | "internal", | 908 | "internal", |
| 909 | { bindingId: string; workspaceId: Id<"workspaces"> }, | 909 | { roleId: string; ruleId: string; workspaceId: Id<"workspaces"> }, |
| 910 | any | 910 | any |
| 911 | >; | 911 | >; |
| 912 | deleteToolRoleRule: FunctionReference< | 912 | deleteToolPolicySet: FunctionReference< |
| 913 | "mutation", | 913 | "mutation", |
| 914 | "internal", | 914 | "internal", |
| 915 | { roleId: string; ruleId: string; workspaceId: Id<"workspaces"> }, | 915 | { roleId: string; workspaceId: Id<"workspaces"> }, |
| 916 | any | 916 | any |
| 917 | >; | 917 | >; |
| 918 | listRuntimeTargets: FunctionReference<"query", "internal", {}, any>; | 918 | listRuntimeTargets: FunctionReference<"query", "internal", {}, any>; |
| @@ -922,37 +922,25 @@ export declare const internal: { · 3 unmodified lines | |||
| 922 | { accountId?: Id<"accounts">; workspaceId: Id<"workspaces"> }, | 922 | { accountId?: Id<"accounts">; workspaceId: Id<"workspaces"> }, |
| 923 | any | 923 | any |
| 924 | >; | 924 | >; |
| 925 | listToolRoleBindings: FunctionReference< | 925 | listToolPolicyAssignments: FunctionReference< |
| 926 | "query", | 926 | "query", |
| 927 | "internal", | 927 | "internal", |
| 928 | { roleId?: string; workspaceId: Id<"workspaces"> }, | 928 | { roleId?: string; workspaceId: Id<"workspaces"> }, |
| 929 | any | 929 | any |
| 930 | >; | 930 | >; |
| 931 | listToolRoleRules: FunctionReference< | 931 | listToolPolicyRules: FunctionReference< |
| 932 | "query", | 932 | "query", |
| 933 | "internal", | 933 | "internal", |
| 934 | { roleId: string; workspaceId: Id<"workspaces"> }, | 934 | { roleId: string; workspaceId: Id<"workspaces"> }, |
| 935 | any | 935 | any |
| 936 | >; | 936 | >; |
| 937 | listToolRoles: FunctionReference< | 937 | listToolPolicySets: FunctionReference< |
| 938 | "query", | 938 | "query", |
| 939 | "internal", | 939 | "internal", |
| 940 | { workspaceId: Id<"workspaces"> }, | 940 | { workspaceId: Id<"workspaces"> }, |
| 941 | any | 941 | any |
| 942 | >; | 942 | >; |
| 943 | upsertToolRole: FunctionReference< | 943 | upsertToolPolicyAssignment: FunctionReference< |
| 944 | "mutation", | ||
| 945 | "internal", | ||
| 946 | { | ||
| 947 | createdByAccountId?: Id<"accounts">; | ||
| 948 | description?: string; | ||
| 949 | id?: string; | ||
| 950 | name: string; | ||
| 951 | workspaceId: Id<"workspaces">; | ||
| 952 | }, | ||
| 953 | any | ||
| 954 | >; | ||
| 955 | upsertToolRoleBinding: FunctionReference< | ||
| 956 | "mutation", | 944 | "mutation", |
| 957 | "internal", | 945 | "internal", |
| 958 | { | 946 | { |
| @@ -967,7 +955,7 @@ export declare const internal: { · 8 unmodified lines | |||
| 967 | }, | 955 | }, |
| 968 | any | 956 | any |
| 969 | >; | 957 | >; |
| 970 | upsertToolRoleRule: FunctionReference< | 958 | upsertToolPolicyRule: FunctionReference< |
| 971 | "mutation", | 959 | "mutation", |
| 972 | "internal", | 960 | "internal", |
| 973 | { | 961 | { |
| @@ -989,6 +977,18 @@ export declare const internal: { · 15 unmodified lines | |||
| 989 | }, | 977 | }, |
| 990 | any | 978 | any |
| 991 | >; | 979 | >; |
| 980 | upsertToolPolicySet: FunctionReference< | ||
| 981 | "mutation", | ||
| 982 | "internal", | ||
| 983 | { | ||
| 984 | createdByAccountId?: Id<"accounts">; | ||
| 985 | description?: string; | ||
| 986 | id?: string; | ||
| 987 | name: string; | ||
| 988 | workspaceId: Id<"workspaces">; | ||
| 989 | }, | ||
| 990 | any | ||
| 991 | >; | ||
| 992 | }; | 992 | }; |
| 993 | resolveApproval: FunctionReference< | 993 | resolveApproval: FunctionReference< |
| 994 | "mutation", | 994 | "mutation", |
| @@ -1186,19 +1186,7 @@ export declare const internal: { · 191 unmodified lines | |||
| 1186 | }, | 1186 | }, |
| 1187 | any | 1187 | any |
| 1188 | >; | 1188 | >; |
| 1189 | upsertToolRole: FunctionReference< | 1189 | upsertToolPolicyAssignment: FunctionReference< |
| 1190 | "mutation", | ||
| 1191 | "internal", | ||
| 1192 | { | ||
| 1193 | createdByAccountId?: Id<"accounts">; | ||
| 1194 | description?: string; | ||
| 1195 | id?: string; | ||
| 1196 | name: string; | ||
| 1197 | workspaceId: Id<"workspaces">; | ||
| 1198 | }, | ||
| 1199 | any | ||
| 1200 | >; | ||
| 1201 | upsertToolRoleBinding: FunctionReference< | ||
| 1202 | "mutation", | 1190 | "mutation", |
| 1203 | "internal", | 1191 | "internal", |
| 1204 | { | 1192 | { |
| @@ -1213,7 +1201,7 @@ export declare const internal: { · 8 unmodified lines | |||
| 1213 | }, | 1201 | }, |
| 1214 | any | 1202 | any |
| 1215 | >; | 1203 | >; |
| 1216 | upsertToolRoleRule: FunctionReference< | 1204 | upsertToolPolicyRule: FunctionReference< |
| 1217 | "mutation", | 1205 | "mutation", |
| 1218 | "internal", | 1206 | "internal", |
| 1219 | { | 1207 | { |
| @@ -1235,6 +1223,18 @@ export declare const internal: { · 15 unmodified lines | |||
| 1235 | }, | 1223 | }, |
| 1236 | any | 1224 | any |
| 1237 | >; | 1225 | >; |
| 1226 | upsertToolPolicySet: FunctionReference< | ||
| 1227 | "mutation", | ||
| 1228 | "internal", | ||
| 1229 | { | ||
| 1230 | createdByAccountId?: Id<"accounts">; | ||
| 1231 | description?: string; | ||
| 1232 | id?: string; | ||
| 1233 | name: string; | ||
| 1234 | workspaceId: Id<"workspaces">; | ||
| 1235 | }, | ||
| 1236 | any | ||
| 1237 | >; | ||
| 1238 | upsertToolSource: FunctionReference< | 1238 | upsertToolSource: FunctionReference< |
| 1239 | "mutation", | 1239 | "mutation", |
| 1240 | "internal", | 1240 | "internal", |
| @@ -217,7 +217,7 @@ describe("authentication", () => { · 216 unmodified lines | |||
| 217 | const owner = await seedUser(t, { subject: "owner-2" }); | 217 | const owner = await seedUser(t, { subject: "owner-2" }); |
| 218 | 218 | ||
| 219 | await expect( | 219 | await expect( |
| 220 | t.mutation(api.workspace.upsertToolRole, { | 220 | t.mutation(api.workspace.upsertToolPolicySet, { |
| 221 | workspaceId: owner.workspaceId, | 221 | workspaceId: owner.workspaceId, |
| 222 | name: "anonymous-role", | 222 | name: "anonymous-role", |
| 223 | }), | 223 | }), |
| @@ -439,7 +439,7 @@ describe("workspace access controls", () => { · 215 unmodified lines | |||
| 439 | const authedMember = t.withIdentity({ subject: "ws-plain-member" }); | 439 | const authedMember = t.withIdentity({ subject: "ws-plain-member" }); |
| 440 | 440 | ||
| 441 | await expect( | 441 | await expect( |
| 442 | authedMember.mutation(api.workspace.upsertToolRole, { | 442 | authedMember.mutation(api.workspace.upsertToolPolicySet, { |
| 443 | workspaceId: owner.workspaceId, | 443 | workspaceId: owner.workspaceId, |
| 444 | name: "member-managed-role", | 444 | name: "member-managed-role", |
| 445 | }), | 445 | }), |
| @@ -464,13 +464,13 @@ describe("workspace access controls", () => { · 18 unmodified lines | |||
| 464 | 464 | ||
| 465 | const authedAdmin = t.withIdentity({ subject: "ws-policy-admin" }); | 465 | const authedAdmin = t.withIdentity({ subject: "ws-policy-admin" }); |
| 466 | 466 | ||
| 467 | const role = await authedAdmin.mutation(api.workspace.upsertToolRole, { | 467 | const role = await authedAdmin.mutation(api.workspace.upsertToolPolicySet, { |
| 468 | workspaceId: owner.workspaceId, | 468 | workspaceId: owner.workspaceId, |
| 469 | name: "policy-admin-role", | 469 | name: "policy-admin-role", |
| 470 | description: "workspace admin policy role", | 470 | description: "workspace admin policy role", |
| 471 | }); | 471 | }); |
| 472 | 472 | ||
| 473 | await authedAdmin.mutation(api.workspace.upsertToolRoleRule, { | 473 | await authedAdmin.mutation(api.workspace.upsertToolPolicyRule, { |
| 474 | workspaceId: owner.workspaceId, | 474 | workspaceId: owner.workspaceId, |
| 475 | roleId: role.id, | 475 | roleId: role.id, |
| 476 | selectorType: "tool_path", | 476 | selectorType: "tool_path", |
| @@ -479,7 +479,7 @@ describe("workspace access controls", () => { · 2 unmodified lines | |||
| 479 | approvalMode: "required", | 479 | approvalMode: "required", |
| 480 | }); | 480 | }); |
| 481 | 481 | ||
| 482 | await authedAdmin.mutation(api.workspace.upsertToolRoleBinding, { | 482 | await authedAdmin.mutation(api.workspace.upsertToolPolicyAssignment, { |
| 483 | workspaceId: owner.workspaceId, | 483 | workspaceId: owner.workspaceId, |
| 484 | roleId: role.id, | 484 | roleId: role.id, |
| 485 | scopeType: "workspace", | 485 | scopeType: "workspace", |
| @@ -503,13 +503,13 @@ describe("workspace access controls", () => { · 17 unmodified lines | |||
| 503 | const outsider = await seedUser(t, { subject: "policy-target-outsider" }); | 503 | const outsider = await seedUser(t, { subject: "policy-target-outsider" }); |
| 504 | const authedOwner = t.withIdentity({ subject: "policy-target-owner" }); | 504 | const authedOwner = t.withIdentity({ subject: "policy-target-owner" }); |
| 505 | 505 | ||
| 506 | const role = await authedOwner.mutation(api.workspace.upsertToolRole, { | 506 | const role = await authedOwner.mutation(api.workspace.upsertToolPolicySet, { |
| 507 | workspaceId: owner.workspaceId, | 507 | workspaceId: owner.workspaceId, |
| 508 | name: "policy-target-role", | 508 | name: "policy-target-role", |
| 509 | }); | 509 | }); |
| 510 | 510 | ||
| 511 | await expect( | 511 | await expect( |
| 512 | authedOwner.mutation(api.workspace.upsertToolRoleBinding, { | 512 | authedOwner.mutation(api.workspace.upsertToolPolicyAssignment, { |
| 513 | workspaceId: owner.workspaceId, | 513 | workspaceId: owner.workspaceId, |
| 514 | roleId: role.id, | 514 | roleId: role.id, |
| 515 | scopeType: "account", | 515 | scopeType: "account", |
| @@ -524,12 +524,12 @@ describe("workspace access controls", () => { · 8 unmodified lines | |||
| 524 | const owner = await seedUser(t, { subject: "ws-owner-policy" }); | 524 | const owner = await seedUser(t, { subject: "ws-owner-policy" }); |
| 525 | const authedOwner = t.withIdentity({ subject: "ws-owner-policy" }); | 525 | const authedOwner = t.withIdentity({ subject: "ws-owner-policy" }); |
| 526 | 526 | ||
| 527 | const role = await authedOwner.mutation(api.workspace.upsertToolRole, { | 527 | const role = await authedOwner.mutation(api.workspace.upsertToolPolicySet, { |
| 528 | workspaceId: owner.workspaceId, | 528 | workspaceId: owner.workspaceId, |
| 529 | name: "owner-policy-role", | 529 | name: "owner-policy-role", |
| 530 | }); | 530 | }); |
| 531 | 531 | ||
| 532 | await authedOwner.mutation(api.workspace.upsertToolRoleRule, { | 532 | await authedOwner.mutation(api.workspace.upsertToolPolicyRule, { |
| 533 | workspaceId: owner.workspaceId, | 533 | workspaceId: owner.workspaceId, |
| 534 | roleId: role.id, | 534 | roleId: role.id, |
| 535 | selectorType: "all", | 535 | selectorType: "all", |
| @@ -537,7 +537,7 @@ describe("workspace access controls", () => { · 1 unmodified line | |||
| 537 | approvalMode: "required", | 537 | approvalMode: "required", |
| 538 | }); | 538 | }); |
| 539 | 539 | ||
| 540 | await authedOwner.mutation(api.workspace.upsertToolRoleBinding, { | 540 | await authedOwner.mutation(api.workspace.upsertToolPolicyAssignment, { |
| 541 | workspaceId: owner.workspaceId, | 541 | workspaceId: owner.workspaceId, |
| 542 | roleId: role.id, | 542 | roleId: role.id, |
| 543 | scopeType: "workspace", | 543 | scopeType: "workspace", |
| @@ -555,12 +555,12 @@ describe("workspace access controls", () => { · 11 unmodified lines | |||
| 555 | const owner = await seedUser(t, { subject: "ws-owner-policy-delete" }); | 555 | const owner = await seedUser(t, { subject: "ws-owner-policy-delete" }); |
| 556 | const authedOwner = t.withIdentity({ subject: "ws-owner-policy-delete" }); | 556 | const authedOwner = t.withIdentity({ subject: "ws-owner-policy-delete" }); |
| 557 | 557 | ||
| 558 | const role = await authedOwner.mutation(api.workspace.upsertToolRole, { | 558 | const role = await authedOwner.mutation(api.workspace.upsertToolPolicySet, { |
| 559 | workspaceId: owner.workspaceId, | 559 | workspaceId: owner.workspaceId, |
| 560 | name: "owner-policy-delete-role", | 560 | name: "owner-policy-delete-role", |
| 561 | }); | 561 | }); |
| 562 | 562 | ||
| 563 | await authedOwner.mutation(api.workspace.upsertToolRoleRule, { | 563 | await authedOwner.mutation(api.workspace.upsertToolPolicyRule, { |
| 564 | workspaceId: owner.workspaceId, | 564 | workspaceId: owner.workspaceId, |
| 565 | roleId: role.id, | 565 | roleId: role.id, |
| 566 | selectorType: "tool_path", | 566 | selectorType: "tool_path", |
| @@ -569,14 +569,14 @@ describe("workspace access controls", () => { · 2 unmodified lines | |||
| 569 | matchType: "exact", | 569 | matchType: "exact", |
| 570 | }); | 570 | }); |
| 571 | 571 | ||
| 572 | await authedOwner.mutation(api.workspace.upsertToolRoleBinding, { | 572 | await authedOwner.mutation(api.workspace.upsertToolPolicyAssignment, { |
| 573 | workspaceId: owner.workspaceId, | 573 | workspaceId: owner.workspaceId, |
| 574 | roleId: role.id, | 574 | roleId: role.id, |
| 575 | scopeType: "workspace", | 575 | scopeType: "workspace", |
| 576 | status: "active", | 576 | status: "active", |
| 577 | }); | 577 | }); |
| 578 | 578 | ||
| 579 | await authedOwner.mutation(api.workspace.deleteToolRole, { | 579 | await authedOwner.mutation(api.workspace.deleteToolPolicySet, { |
| 580 | workspaceId: owner.workspaceId, | 580 | workspaceId: owner.workspaceId, |
| 581 | roleId: role.id, | 581 | roleId: role.id, |
| 582 | }); | 582 | }); |
| @@ -754,7 +754,7 @@ describe("workspace access controls", () => { · 171 unmodified lines | |||
| 754 | const authedMember = t.withIdentity({ subject: "role-tool-member" }); | 754 | const authedMember = t.withIdentity({ subject: "role-tool-member" }); |
| 755 | 755 | ||
| 756 | await expect( | 756 | await expect( |
| 757 | authedMember.mutation(api.workspace.upsertToolRole, { | 757 | authedMember.mutation(api.workspace.upsertToolPolicySet, { |
| 758 | workspaceId: owner.workspaceId, | 758 | workspaceId: owner.workspaceId, |
| 759 | name: "limited", | 759 | name: "limited", |
| 760 | }), | 760 | }), |
| @@ -766,13 +766,13 @@ describe("workspace access controls", () => { · 5 unmodified lines | |||
| 766 | const owner = await seedUser(t, { subject: "role-source-owner" }); | 766 | const owner = await seedUser(t, { subject: "role-source-owner" }); |
| 767 | const authedOwner = t.withIdentity({ subject: "role-source-owner" }); | 767 | const authedOwner = t.withIdentity({ subject: "role-source-owner" }); |
| 768 | 768 | ||
| 769 | const role = await authedOwner.mutation(api.workspace.upsertToolRole, { | 769 | const role = await authedOwner.mutation(api.workspace.upsertToolPolicySet, { |
| 770 | workspaceId: owner.workspaceId, | 770 | workspaceId: owner.workspaceId, |
| 771 | name: "github-access", | 771 | name: "github-access", |
| 772 | description: "Allows github source tools", | 772 | description: "Allows github source tools", |
| 773 | }); | 773 | }); |
| 774 | 774 | ||
| 775 | await authedOwner.mutation(api.workspace.upsertToolRoleRule, { | 775 | await authedOwner.mutation(api.workspace.upsertToolPolicyRule, { |
| 776 | workspaceId: owner.workspaceId, | 776 | workspaceId: owner.workspaceId, |
| 777 | roleId: role.id, | 777 | roleId: role.id, |
| 778 | selectorType: "source", | 778 | selectorType: "source", |
| @@ -781,7 +781,7 @@ describe("workspace access controls", () => { · 2 unmodified lines | |||
| 781 | approvalMode: "auto", | 781 | approvalMode: "auto", |
| 782 | }); | 782 | }); |
| 783 | 783 | ||
| 784 | await authedOwner.mutation(api.workspace.upsertToolRoleBinding, { | 784 | await authedOwner.mutation(api.workspace.upsertToolPolicyAssignment, { |
| 785 | workspaceId: owner.workspaceId, | 785 | workspaceId: owner.workspaceId, |
| 786 | roleId: role.id, | 786 | roleId: role.id, |
| 787 | scopeType: "organization", | 787 | scopeType: "organization", |
| @@ -1120,7 +1120,7 @@ describe("cross-workspace isolation", () => { · 332 unmodified lines | |||
| 1120 | const authedA = t.withIdentity({ subject: "policy-user-a" }); | 1120 | const authedA = t.withIdentity({ subject: "policy-user-a" }); |
| 1121 | 1121 | ||
| 1122 | await expect( | 1122 | await expect( |
| 1123 | authedA.mutation(api.workspace.upsertToolRole, { | 1123 | authedA.mutation(api.workspace.upsertToolPolicySet, { |
| 1124 | workspaceId: userB.workspaceId, | 1124 | workspaceId: userB.workspaceId, |
| 1125 | name: "cross-workspace-role", | 1125 | name: "cross-workspace-role", |
| 1126 | }), | 1126 | }), |
| @@ -1628,18 +1628,18 @@ describe("role hierarchy validation", () => { · 501 unmodified lines | |||
| 1628 | const authed = t.withIdentity({ subject: "admin-hier-admin" }); | 1628 | const authed = t.withIdentity({ subject: "admin-hier-admin" }); |
| 1629 | 1629 | ||
| 1630 | // Admin can manage tool-policy roles/rules/bindings | 1630 | // Admin can manage tool-policy roles/rules/bindings |
| 1631 | const role = await authed.mutation(api.workspace.upsertToolRole, { | 1631 | const role = await authed.mutation(api.workspace.upsertToolPolicySet, { |
| 1632 | workspaceId: org.workspaceId, | 1632 | workspaceId: org.workspaceId, |
| 1633 | name: "admin-hierarchy-role", | 1633 | name: "admin-hierarchy-role", |
| 1634 | }); | 1634 | }); |
| 1635 | await authed.mutation(api.workspace.upsertToolRoleRule, { | 1635 | await authed.mutation(api.workspace.upsertToolPolicyRule, { |
| 1636 | workspaceId: org.workspaceId, | 1636 | workspaceId: org.workspaceId, |
| 1637 | roleId: role.id, | 1637 | roleId: role.id, |
| 1638 | selectorType: "tool_path", | 1638 | selectorType: "tool_path", |
| 1639 | resourcePattern: "admin.*", | 1639 | resourcePattern: "admin.*", |
| 1640 | effect: "deny", | 1640 | effect: "deny", |
| 1641 | }); | 1641 | }); |
| 1642 | await authed.mutation(api.workspace.upsertToolRoleBinding, { | 1642 | await authed.mutation(api.workspace.upsertToolPolicyAssignment, { |
| 1643 | workspaceId: org.workspaceId, | 1643 | workspaceId: org.workspaceId, |
| 1644 | roleId: role.id, | 1644 | roleId: role.id, |
| 1645 | scopeType: "workspace", | 1645 | scopeType: "workspace", |
| @@ -25,15 +25,15 @@ export { · 24 unmodified lines | |||
| 25 | export { bootstrapAnonymousSession } from "./database/anonymous_session"; | 25 | export { bootstrapAnonymousSession } from "./database/anonymous_session"; |
| 26 | export { | 26 | export { |
| 27 | listRuntimeTargets, | 27 | listRuntimeTargets, |
| 28 | upsertToolRole, | 28 | upsertToolPolicySet, |
| 29 | listToolRoles, | 29 | listToolPolicySets, |
| 30 | deleteToolRole, | 30 | deleteToolPolicySet, |
| 31 | upsertToolRoleRule, | 31 | upsertToolPolicyRule, |
| 32 | listToolRoleRules, | 32 | listToolPolicyRules, |
| 33 | deleteToolRoleRule, | 33 | deleteToolPolicyRule, |
| 34 | upsertToolRoleBinding, | 34 | upsertToolPolicyAssignment, |
| 35 | listToolRoleBindings, | 35 | listToolPolicyAssignments, |
| 36 | deleteToolRoleBinding, | 36 | deleteToolPolicyAssignment, |
| 37 | listToolPolicies, | 37 | listToolPolicies, |
| 38 | } from "./database/policies"; | 38 | } from "./database/policies"; |
| 39 | export { | 39 | export { |
| @@ -5,9 +5,9 @@ import type { MutationCtx, QueryCtx } from "../_generated/server"; · 4 unmodified lines | |||
| 5 | import { listRuntimeTargets as listAvailableRuntimeTargets } from "../../../core/src/runtimes/runtime-catalog"; | 5 | import { listRuntimeTargets as listAvailableRuntimeTargets } from "../../../core/src/runtimes/runtime-catalog"; |
| 6 | import type { | 6 | import type { |
| 7 | ToolPolicyRecord, | 7 | ToolPolicyRecord, |
| 8 | ToolRoleBindingRecord, | 8 | ToolPolicyAssignmentRecord, |
| 9 | ToolRoleRecord, | 9 | ToolPolicySetRecord, |
| 10 | ToolRoleRuleRecord, | 10 | ToolPolicyRuleRecord, |
| 11 | ToolRoleSelectorType, | 11 | ToolRoleSelectorType, |
| 12 | } from "../../../core/src/types"; | 12 | } from "../../../core/src/types"; |
| 13 | import { | 13 | import { |
| @@ -83,7 +83,7 @@ function mapToolRole(doc: { · 69 unmodified lines | |||
| 83 | createdByAccountId?: Id<"accounts">; | 83 | createdByAccountId?: Id<"accounts">; |
| 84 | createdAt: number; | 84 | createdAt: number; |
| 85 | updatedAt: number; | 85 | updatedAt: number; |
| 86 | }): ToolRoleRecord { | 86 | }): ToolPolicySetRecord { |
| 87 | return { | 87 | return { |
| 88 | id: doc.roleId, | 88 | id: doc.roleId, |
| 89 | organizationId: doc.organizationId, | 89 | organizationId: doc.organizationId, |
| @@ -110,7 +110,7 @@ function mapToolRoleRule(doc: { · 20 unmodified lines | |||
| 110 | priority: number; | 110 | priority: number; |
| 111 | createdAt: number; | 111 | createdAt: number; |
| 112 | updatedAt: number; | 112 | updatedAt: number; |
| 113 | }): ToolRoleRuleRecord { | 113 | }): ToolPolicyRuleRecord { |
| 114 | return { | 114 | return { |
| 115 | id: doc.ruleId, | 115 | id: doc.ruleId, |
| 116 | roleId: doc.roleId, | 116 | roleId: doc.roleId, |
| @@ -141,7 +141,7 @@ function mapToolRoleBinding(doc: { · 24 unmodified lines | |||
| 141 | expiresAt?: number; | 141 | expiresAt?: number; |
| 142 | createdAt: number; | 142 | createdAt: number; |
| 143 | updatedAt: number; | 143 | updatedAt: number; |
| 144 | }): ToolRoleBindingRecord { | 144 | }): ToolPolicyAssignmentRecord { |
| 145 | return { | 145 | return { |
| 146 | id: doc.bindingId, | 146 | id: doc.bindingId, |
| 147 | roleId: doc.roleId, | 147 | roleId: doc.roleId, |
| @@ -328,7 +328,7 @@ async function listEffectiveBindings( · 180 unmodified lines | |||
| 328 | } | 328 | } |
| 329 | 329 | ||
| 330 | function selectorInputToResource( | 330 | function selectorInputToResource( |
| 331 | rule: Pick<ToolRoleRuleRecord, "selectorType" | "sourceKey" | "namespacePattern" | "toolPathPattern">, | 331 | rule: Pick<ToolPolicyRuleRecord, "selectorType" | "sourceKey" | "namespacePattern" | "toolPathPattern">, |
| 332 | ): { resourceType: PolicyResourceType; resourcePattern: string } { | 332 | ): { resourceType: PolicyResourceType; resourcePattern: string } { |
| 333 | const resourceType = resourceTypeFromSelectorType(rule.selectorType); | 333 | const resourceType = resourceTypeFromSelectorType(rule.selectorType); |
| 334 | const resourcePattern = resourcePatternFromRule(rule); | 334 | const resourcePattern = resourcePatternFromRule(rule); |
| @@ -346,7 +346,7 @@ export const listRuntimeTargets = internalQuery({ · 11 unmodified lines | |||
| 346 | }, | 346 | }, |
| 347 | }); | 347 | }); |
| 348 | 348 | ||
| 349 | export const upsertToolRole = internalMutation({ | 349 | export const upsertToolPolicySet = internalMutation({ |
| 350 | args: { | 350 | args: { |
| 351 | workspaceId: v.id("workspaces"), | 351 | workspaceId: v.id("workspaces"), |
| 352 | id: v.optional(v.string()), | 352 | id: v.optional(v.string()), |
| @@ -410,7 +410,7 @@ export const upsertToolRole = internalMutation({ · 57 unmodified lines | |||
| 410 | }, | 410 | }, |
| 411 | }); | 411 | }); |
| 412 | 412 | ||
| 413 | export const listToolRoles = internalQuery({ | 413 | export const listToolPolicySets = internalQuery({ |
| 414 | args: { | 414 | args: { |
| 415 | workspaceId: v.id("workspaces"), | 415 | workspaceId: v.id("workspaces"), |
| 416 | }, | 416 | }, |
| @@ -426,7 +426,7 @@ export const listToolRoles = internalQuery({ · 9 unmodified lines | |||
| 426 | }, | 426 | }, |
| 427 | }); | 427 | }); |
| 428 | 428 | ||
| 429 | export const deleteToolRole = internalMutation({ | 429 | export const deleteToolPolicySet = internalMutation({ |
| 430 | args: { | 430 | args: { |
| 431 | workspaceId: v.id("workspaces"), | 431 | workspaceId: v.id("workspaces"), |
| 432 | roleId: v.string(), | 432 | roleId: v.string(), |
| @@ -459,7 +459,7 @@ export const deleteToolRole = internalMutation({ · 26 unmodified lines | |||
| 459 | }, | 459 | }, |
| 460 | }); | 460 | }); |
| 461 | 461 | ||
| 462 | export const upsertToolRoleRule = internalMutation({ | 462 | export const upsertToolPolicyRule = internalMutation({ |
| 463 | args: { | 463 | args: { |
| 464 | workspaceId: v.id("workspaces"), | 464 | workspaceId: v.id("workspaces"), |
| 465 | roleId: v.string(), | 465 | roleId: v.string(), |
| @@ -541,7 +541,7 @@ export const upsertToolRoleRule = internalMutation({ · 75 unmodified lines | |||
| 541 | }, | 541 | }, |
| 542 | }); | 542 | }); |
| 543 | 543 | ||
| 544 | export const listToolRoleRules = internalQuery({ | 544 | export const listToolPolicyRules = internalQuery({ |
| 545 | args: { | 545 | args: { |
| 546 | workspaceId: v.id("workspaces"), | 546 | workspaceId: v.id("workspaces"), |
| 547 | roleId: v.string(), | 547 | roleId: v.string(), |
| @@ -572,7 +572,7 @@ export const listToolRoleRules = internalQuery({ · 24 unmodified lines | |||
| 572 | }, | 572 | }, |
| 573 | }); | 573 | }); |
| 574 | 574 | ||
| 575 | export const deleteToolRoleRule = internalMutation({ | 575 | export const deleteToolPolicyRule = internalMutation({ |
| 576 | args: { | 576 | args: { |
| 577 | workspaceId: v.id("workspaces"), | 577 | workspaceId: v.id("workspaces"), |
| 578 | roleId: v.string(), | 578 | roleId: v.string(), |
| @@ -601,7 +601,7 @@ export const deleteToolRoleRule = internalMutation({ · 22 unmodified lines | |||
| 601 | }, | 601 | }, |
| 602 | }); | 602 | }); |
| 603 | 603 | ||
| 604 | export const upsertToolRoleBinding = internalMutation({ | 604 | export const upsertToolPolicyAssignment = internalMutation({ |
| 605 | args: { | 605 | args: { |
| 606 | workspaceId: v.id("workspaces"), | 606 | workspaceId: v.id("workspaces"), |
| 607 | roleId: v.string(), | 607 | roleId: v.string(), |
| @@ -680,7 +680,7 @@ export const upsertToolRoleBinding = internalMutation({ · 72 unmodified lines | |||
| 680 | }, | 680 | }, |
| 681 | }); | 681 | }); |
| 682 | 682 | ||
| 683 | export const listToolRoleBindings = internalQuery({ | 683 | export const listToolPolicyAssignments = internalQuery({ |
| 684 | args: { | 684 | args: { |
| 685 | workspaceId: v.id("workspaces"), | 685 | workspaceId: v.id("workspaces"), |
| 686 | roleId: v.optional(v.string()), | 686 | roleId: v.optional(v.string()), |
| @@ -705,7 +705,7 @@ export const listToolRoleBindings = internalQuery({ · 18 unmodified lines | |||
| 705 | }, | 705 | }, |
| 706 | }); | 706 | }); |
| 707 | 707 | ||
| 708 | export const deleteToolRoleBinding = internalMutation({ | 708 | export const deleteToolPolicyAssignment = internalMutation({ |
| 709 | args: { | 709 | args: { |
| 710 | workspaceId: v.id("workspaces"), | 710 | workspaceId: v.id("workspaces"), |
| 711 | bindingId: v.string(), | 711 | bindingId: v.string(), |
| @@ -136,7 +136,7 @@ export const listToolPolicies = workspaceQuery({ · 135 unmodified lines | |||
| 136 | }, | 136 | }, |
| 137 | }); | 137 | }); |
| 138 | 138 | ||
| 139 | export const upsertToolRole = workspaceMutation({ | 139 | export const upsertToolPolicySet = workspaceMutation({ |
| 140 | method: "POST", | 140 | method: "POST", |
| 141 | requireAdmin: true, | 141 | requireAdmin: true, |
| 142 | args: { | 142 | args: { |
| @@ -145,7 +145,7 @@ export const upsertToolRole = workspaceMutation({ · 2 unmodified lines | |||
| 145 | description: v.optional(v.string()), | 145 | description: v.optional(v.string()), |
| 146 | }, | 146 | }, |
| 147 | handler: async (ctx, args) => { | 147 | handler: async (ctx, args) => { |
| 148 | return await ctx.runMutation(internal.database.upsertToolRole, { | 148 | return await ctx.runMutation(internal.database.upsertToolPolicySet, { |
| 149 | ...args, | 149 | ...args, |
| 150 | workspaceId: ctx.workspaceId, | 150 | workspaceId: ctx.workspaceId, |
| 151 | createdByAccountId: ctx.account._id, | 151 | createdByAccountId: ctx.account._id, |
| @@ -153,32 +153,32 @@ export const upsertToolRole = workspaceMutation({ · 1 unmodified line | |||
| 153 | }, | 153 | }, |
| 154 | }); | 154 | }); |
| 155 | 155 | ||
| 156 | export const listToolRoles = workspaceQuery({ | 156 | export const listToolPolicySets = workspaceQuery({ |
| 157 | method: "GET", | 157 | method: "GET", |
| 158 | requireAdmin: true, | 158 | requireAdmin: true, |
| 159 | args: {}, | 159 | args: {}, |
| 160 | handler: async (ctx) => { | 160 | handler: async (ctx) => { |
| 161 | return await ctx.runQuery(internal.database.listToolRoles, { | 161 | return await ctx.runQuery(internal.database.listToolPolicySets, { |
| 162 | workspaceId: ctx.workspaceId, | 162 | workspaceId: ctx.workspaceId, |
| 163 | }); | 163 | }); |
| 164 | }, | 164 | }, |
| 165 | }); | 165 | }); |
| 166 | 166 | ||
| 167 | export const deleteToolRole = workspaceMutation({ | 167 | export const deleteToolPolicySet = workspaceMutation({ |
| 168 | method: "POST", | 168 | method: "POST", |
| 169 | requireAdmin: true, | 169 | requireAdmin: true, |
| 170 | args: { | 170 | args: { |
| 171 | roleId: v.string(), | 171 | roleId: v.string(), |
| 172 | }, | 172 | }, |
| 173 | handler: async (ctx, args) => { | 173 | handler: async (ctx, args) => { |
| 174 | return await ctx.runMutation(internal.database.deleteToolRole, { | 174 | return await ctx.runMutation(internal.database.deleteToolPolicySet, { |
| 175 | workspaceId: ctx.workspaceId, | 175 | workspaceId: ctx.workspaceId, |
| 176 | roleId: args.roleId, | 176 | roleId: args.roleId, |
| 177 | }); | 177 | }); |
| 178 | }, | 178 | }, |
| 179 | }); | 179 | }); |
| 180 | 180 | ||
| 181 | export const upsertToolRoleRule = workspaceMutation({ | 181 | export const upsertToolPolicyRule = workspaceMutation({ |
| 182 | method: "POST", | 182 | method: "POST", |
| 183 | requireAdmin: true, | 183 | requireAdmin: true, |
| 184 | args: { | 184 | args: { |
| @@ -198,28 +198,28 @@ export const upsertToolRoleRule = workspaceMutation({ · 13 unmodified lines | |||
| 198 | priority: v.optional(v.number()), | 198 | priority: v.optional(v.number()), |
| 199 | }, | 199 | }, |
| 200 | handler: async (ctx, args) => { | 200 | handler: async (ctx, args) => { |
| 201 | return await ctx.runMutation(internal.database.upsertToolRoleRule, { | 201 | return await ctx.runMutation(internal.database.upsertToolPolicyRule, { |
| 202 | ...args, | 202 | ...args, |
| 203 | workspaceId: ctx.workspaceId, | 203 | workspaceId: ctx.workspaceId, |
| 204 | }); | 204 | }); |
| 205 | }, | 205 | }, |
| 206 | }); | 206 | }); |
| 207 | 207 | ||
| 208 | export const listToolRoleRules = workspaceQuery({ | 208 | export const listToolPolicyRules = workspaceQuery({ |
| 209 | method: "GET", | 209 | method: "GET", |
| 210 | requireAdmin: true, | 210 | requireAdmin: true, |
| 211 | args: { | 211 | args: { |
| 212 | roleId: v.string(), | 212 | roleId: v.string(), |
| 213 | }, | 213 | }, |
| 214 | handler: async (ctx, args) => { | 214 | handler: async (ctx, args) => { |
| 215 | return await ctx.runQuery(internal.database.listToolRoleRules, { | 215 | return await ctx.runQuery(internal.database.listToolPolicyRules, { |
| 216 | workspaceId: ctx.workspaceId, | 216 | workspaceId: ctx.workspaceId, |
| 217 | roleId: args.roleId, | 217 | roleId: args.roleId, |
| 218 | }); | 218 | }); |
| 219 | }, | 219 | }, |
| 220 | }); | 220 | }); |
| 221 | 221 | ||
| 222 | export const deleteToolRoleRule = workspaceMutation({ | 222 | export const deleteToolPolicyRule = workspaceMutation({ |
| 223 | method: "POST", | 223 | method: "POST", |
| 224 | requireAdmin: true, | 224 | requireAdmin: true, |
| 225 | args: { | 225 | args: { |
| @@ -227,7 +227,7 @@ export const deleteToolRoleRule = workspaceMutation({ · 1 unmodified line | |||
| 227 | ruleId: v.string(), | 227 | ruleId: v.string(), |
| 228 | }, | 228 | }, |
| 229 | handler: async (ctx, args) => { | 229 | handler: async (ctx, args) => { |
| 230 | return await ctx.runMutation(internal.database.deleteToolRoleRule, { | 230 | return await ctx.runMutation(internal.database.deleteToolPolicyRule, { |
| 231 | workspaceId: ctx.workspaceId, | 231 | workspaceId: ctx.workspaceId, |
| 232 | roleId: args.roleId, | 232 | roleId: args.roleId, |
| 233 | ruleId: args.ruleId, | 233 | ruleId: args.ruleId, |
| @@ -235,7 +235,7 @@ export const deleteToolRoleRule = workspaceMutation({ · 1 unmodified line | |||
| 235 | }, | 235 | }, |
| 236 | }); | 236 | }); |
| 237 | 237 | ||
| 238 | export const upsertToolRoleBinding = workspaceMutation({ | 238 | export const upsertToolPolicyAssignment = workspaceMutation({ |
| 239 | method: "POST", | 239 | method: "POST", |
| 240 | requireAdmin: true, | 240 | requireAdmin: true, |
| 241 | args: { | 241 | args: { |
| @@ -248,35 +248,35 @@ export const upsertToolRoleBinding = workspaceMutation({ · 6 unmodified lines | |||
| 248 | expiresAt: v.optional(v.number()), | 248 | expiresAt: v.optional(v.number()), |
| 249 | }, | 249 | }, |
| 250 | handler: async (ctx, args) => { | 250 | handler: async (ctx, args) => { |
| 251 | return await ctx.runMutation(internal.database.upsertToolRoleBinding, { | 251 | return await ctx.runMutation(internal.database.upsertToolPolicyAssignment, { |
| 252 | ...args, | 252 | ...args, |
| 253 | workspaceId: ctx.workspaceId, | 253 | workspaceId: ctx.workspaceId, |
| 254 | }); | 254 | }); |
| 255 | }, | 255 | }, |
| 256 | }); | 256 | }); |
| 257 | 257 | ||
| 258 | export const listToolRoleBindings = workspaceQuery({ | 258 | export const listToolPolicyAssignments = workspaceQuery({ |
| 259 | method: "GET", | 259 | method: "GET", |
| 260 | requireAdmin: true, | 260 | requireAdmin: true, |
| 261 | args: { | 261 | args: { |
| 262 | roleId: v.optional(v.string()), | 262 | roleId: v.optional(v.string()), |
| 263 | }, | 263 | }, |
| 264 | handler: async (ctx, args) => { | 264 | handler: async (ctx, args) => { |
| 265 | return await ctx.runQuery(internal.database.listToolRoleBindings, { | 265 | return await ctx.runQuery(internal.database.listToolPolicyAssignments, { |
| 266 | workspaceId: ctx.workspaceId, | 266 | workspaceId: ctx.workspaceId, |
| 267 | roleId: args.roleId, | 267 | roleId: args.roleId, |
| 268 | }); | 268 | }); |
| 269 | }, | 269 | }, |
| 270 | }); | 270 | }); |
| 271 | 271 | ||
| 272 | export const deleteToolRoleBinding = workspaceMutation({ | 272 | export const deleteToolPolicyAssignment = workspaceMutation({ |
| 273 | method: "POST", | 273 | method: "POST", |
| 274 | requireAdmin: true, | 274 | requireAdmin: true, |
| 275 | args: { | 275 | args: { |
| 276 | bindingId: v.string(), | 276 | bindingId: v.string(), |
| 277 | }, | 277 | }, |
| 278 | handler: async (ctx, args) => { | 278 | handler: async (ctx, args) => { |
| 279 | return await ctx.runMutation(internal.database.deleteToolRoleBinding, { | 279 | return await ctx.runMutation(internal.database.deleteToolPolicyAssignment, { |
| 280 | workspaceId: ctx.workspaceId, | 280 | workspaceId: ctx.workspaceId, |
| 281 | bindingId: args.bindingId, | 281 | bindingId: args.bindingId, |
| 282 | }); | 282 | }); |