import { Select } from "@opencode-ai/ui/select" import { showToast } from "@opencode-ai/ui/toast" import { Component, For, createMemo, type JSX } from "solid-js" import { useGlobalSync } from "@/context/global-sync" type PermissionAction = "allow" | "ask" | "deny" type PermissionObject = Record type PermissionValue = PermissionAction | PermissionObject | string[] | undefined type PermissionMap = Record type PermissionItem = { id: string title: string description: string } const ACTIONS: Array<{ value: PermissionAction; label: string }> = [ { value: "allow", label: "Allow" }, { value: "ask", label: "Ask" }, { value: "deny", label: "Deny" }, ] const ITEMS: PermissionItem[] = [ { id: "read", title: "Read", description: "Reading a file (matches the file path)" }, { id: "edit", title: "Edit", description: "Modify files, including edits, writes, patches, and multi-edits" }, { id: "glob", title: "Glob", description: "Match files using glob patterns" }, { id: "grep", title: "Grep", description: "Search file contents using regular expressions" }, { id: "list", title: "List", description: "List files within a directory" }, { id: "bash", title: "Bash", description: "Run shell commands" }, { id: "task", title: "Task", description: "Launch sub-agents" }, { id: "skill", title: "Skill", description: "Load a skill by name" }, { id: "lsp", title: "LSP", description: "Run language server queries" }, { id: "todoread", title: "Todo Read", description: "Read the todo list" }, { id: "todowrite", title: "Todo Write", description: "Update the todo list" }, { id: "webfetch", title: "Web Fetch", description: "Fetch content from a URL" }, { id: "websearch", title: "Web Search", description: "Search the web" }, { id: "codesearch", title: "Code Search", description: "Search code on the web" }, { id: "external_directory", title: "External Directory", description: "Access files outside the project directory" }, { id: "doom_loop", title: "Doom Loop", description: "Detect repeated tool calls with identical input" }, ] const VALID_ACTIONS = new Set(["allow", "ask", "deny"]) function toMap(value: unknown): PermissionMap { if (value && typeof value === "object" && !Array.isArray(value)) return value as PermissionMap const action = getAction(value) if (action) return { "*": action } return {} } function getAction(value: unknown): PermissionAction | undefined { if (typeof value === "string" && VALID_ACTIONS.has(value as PermissionAction)) return value as PermissionAction return } function getRuleDefault(value: unknown): PermissionAction | undefined { const action = getAction(value) if (action) return action if (!value || typeof value !== "object" || Array.isArray(value)) return return getAction((value as Record)["*"]) } export const SettingsPermissions: Component = () => { const globalSync = useGlobalSync() const permission = createMemo(() => { return toMap(globalSync.data.config.permission) }) const actionFor = (id: string): PermissionAction => { const value = permission()[id] const direct = getRuleDefault(value) if (direct) return direct const wildcard = getRuleDefault(permission()["*"]) if (wildcard) return wildcard return "allow" } const setPermission = async (id: string, action: PermissionAction) => { const before = globalSync.data.config.permission const map = toMap(before) const existing = map[id] const nextValue = existing && typeof existing === "object" && !Array.isArray(existing) ? { ...existing, "*": action } : action globalSync.set("config", "permission", { ...map, [id]: nextValue }) globalSync.updateConfig({ permission: { [id]: nextValue } }).catch((err: unknown) => { globalSync.set("config", "permission", before) const message = err instanceof Error ? err.message : String(err) showToast({ title: "Failed to update permissions", description: message }) }) } return (

Permissions

Control what tools the server can use by default.

Appearance

{(item) => (