|
|
@@ -1,17 +1,24 @@
|
|
|
-import { type Accessor, createEffect, createMemo, onCleanup } from "solid-js"
|
|
|
+import { createEffect, createMemo, createRoot, getOwner, onCleanup } from "solid-js"
|
|
|
import { createStore, produce } from "solid-js/store"
|
|
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
|
|
import type { PermissionRequest } from "@opencode-ai/sdk/v2/client"
|
|
|
import { Persist, persisted } from "@/utils/persist"
|
|
|
-import { useServerSDK } from "@/context/server-sdk"
|
|
|
-import { useServerSync } from "./server-sync"
|
|
|
-import { useParams } from "@solidjs/router"
|
|
|
+import type { ServerSDK } from "@/context/server-sdk"
|
|
|
+import type { ServerSync } from "./server-sync"
|
|
|
+import { useParams, useSearchParams } from "@solidjs/router"
|
|
|
import { decode64 } from "@/utils/base64"
|
|
|
+import { useGlobal } from "./global"
|
|
|
+import { ServerConnection, useServer } from "./server"
|
|
|
+import { type DraftTab, useTabs } from "./tabs"
|
|
|
+import { useSettings } from "./settings"
|
|
|
+import { requireServerKey } from "@/utils/session-route"
|
|
|
+import type { ServerScope } from "@/utils/server-scope"
|
|
|
import {
|
|
|
acceptKey,
|
|
|
directoryAcceptKey,
|
|
|
isDirectoryAutoAccepting,
|
|
|
autoRespondsPermission,
|
|
|
+ sessionAutoAccept,
|
|
|
} from "./permission-auto-respond"
|
|
|
|
|
|
type PermissionRespondFn = (input: {
|
|
|
@@ -47,234 +54,417 @@ function hasPermissionPromptRules(permission: unknown) {
|
|
|
export const { use: usePermission, provider: PermissionProvider } = createSimpleContext({
|
|
|
name: "Permission",
|
|
|
gate: false,
|
|
|
- init: (props: { directory?: Accessor<string | undefined> }) => {
|
|
|
- const params = useParams()
|
|
|
- const serverSDK = useServerSDK()
|
|
|
- const serverSync = useServerSync()
|
|
|
+ init: () => {
|
|
|
+ const params = useParams<{ serverKey?: string; dir?: string; id?: string }>()
|
|
|
+ const [search] = useSearchParams<{ draftId?: string }>()
|
|
|
+ const global = useGlobal()
|
|
|
+ const server = useServer()
|
|
|
+ const tabs = useTabs()
|
|
|
+ const settings = useSettings()
|
|
|
+ const owner = getOwner()
|
|
|
+ const states = new Map<ServerScope, { key: ServerConnection.Key; dispose: () => void; state: PermissionState }>()
|
|
|
+
|
|
|
+ const activeDraft = createMemo(() => {
|
|
|
+ if (!search.draftId) return
|
|
|
+ return tabs.store.find((tab): tab is DraftTab => tab.type === "draft" && tab.draftID === search.draftId)
|
|
|
+ })
|
|
|
|
|
|
- const permissionsEnabled = createMemo(() => {
|
|
|
- const directory = props.directory?.() ?? decode64(params.dir)
|
|
|
- if (!directory) return false
|
|
|
- const [store] = serverSync().child(directory)
|
|
|
- return hasPermissionPromptRules(store.config.permission)
|
|
|
+ const activeServer = createMemo(() => {
|
|
|
+ if (params.serverKey && settings.general.newLayoutDesigns()) return requireServerKey(params.serverKey)
|
|
|
+ return activeDraft()?.server ?? server.key
|
|
|
})
|
|
|
|
|
|
- const [store, setStore, _, ready] = persisted(
|
|
|
- {
|
|
|
- ...Persist.serverGlobal(serverSDK().scope, "permission", ["permission.v3"]),
|
|
|
- migrate(value) {
|
|
|
- if (!value || typeof value !== "object" || Array.isArray(value)) return value
|
|
|
-
|
|
|
- const data = value as Record<string, unknown>
|
|
|
- if (data.autoAccept) return value
|
|
|
-
|
|
|
- return {
|
|
|
- ...data,
|
|
|
- autoAccept:
|
|
|
- typeof data.autoAcceptEdits === "object" && data.autoAcceptEdits && !Array.isArray(data.autoAcceptEdits)
|
|
|
- ? data.autoAcceptEdits
|
|
|
- : {},
|
|
|
- }
|
|
|
- },
|
|
|
- },
|
|
|
- createStore({
|
|
|
- autoAccept: {} as Record<string, boolean>,
|
|
|
- }),
|
|
|
- )
|
|
|
+ const ensure = (key: ServerConnection.Key) => {
|
|
|
+ const conn = global.servers.list().find((item) => ServerConnection.key(item) === key)
|
|
|
+ if (!conn) throw new Error(`Permission server not found: ${key}`)
|
|
|
+ const ctx = global.ensureServerCtx(conn)
|
|
|
+ const existing = states.get(ctx.sdk.scope)
|
|
|
+ if (existing && global.servers.list().some((item) => ServerConnection.key(item) === existing.key)) {
|
|
|
+ return existing.state
|
|
|
+ }
|
|
|
+ if (existing) {
|
|
|
+ existing.dispose()
|
|
|
+ states.delete(ctx.sdk.scope)
|
|
|
+ }
|
|
|
+ const root = createRoot(
|
|
|
+ (dispose) => ({
|
|
|
+ key,
|
|
|
+ dispose,
|
|
|
+ state: createServerPermissionState({ sdk: ctx.sdk, sync: ctx.sync }),
|
|
|
+ }),
|
|
|
+ owner ?? undefined,
|
|
|
+ )
|
|
|
+ states.set(ctx.sdk.scope, root)
|
|
|
+ return root.state
|
|
|
+ }
|
|
|
|
|
|
- // When config has permission: "allow", auto-enable directory-level auto-accept
|
|
|
createEffect(() => {
|
|
|
- if (!ready()) return
|
|
|
- const directory = props.directory?.() ?? decode64(params.dir)
|
|
|
- if (!directory) return
|
|
|
- const [childStore] = serverSync().child(directory)
|
|
|
- const perm = childStore.config.permission
|
|
|
- if (typeof perm === "string" && perm === "allow") {
|
|
|
- const key = directoryAcceptKey(directory)
|
|
|
- if (store.autoAccept[key] === undefined) {
|
|
|
- setStore(
|
|
|
- produce((draft) => {
|
|
|
- draft.autoAccept[key] = true
|
|
|
- }),
|
|
|
- )
|
|
|
- }
|
|
|
- }
|
|
|
+ global.servers.list().forEach((conn) => ensure(ServerConnection.key(conn)))
|
|
|
})
|
|
|
|
|
|
- const MAX_RESPONDED = 1000
|
|
|
- const RESPONDED_TTL_MS = 60 * 60 * 1000
|
|
|
- const responded = new Map<string, number>()
|
|
|
- const enableVersion = new Map<string, number>()
|
|
|
+ createEffect(() => {
|
|
|
+ const list = global.servers.list()
|
|
|
+ const keys = new Set(list.map(ServerConnection.key))
|
|
|
+ states.forEach((value, scope) => {
|
|
|
+ if (keys.has(value.key)) return
|
|
|
+ value.dispose()
|
|
|
+ states.delete(scope)
|
|
|
+ const replacement = list.find((conn) => server.scope(ServerConnection.key(conn)) === scope)
|
|
|
+ if (replacement) ensure(ServerConnection.key(replacement))
|
|
|
+ })
|
|
|
+ })
|
|
|
|
|
|
- function pruneResponded(now: number) {
|
|
|
- for (const [id, ts] of responded) {
|
|
|
- if (now - ts < RESPONDED_TTL_MS) break
|
|
|
- responded.delete(id)
|
|
|
- }
|
|
|
+ onCleanup(() => states.forEach((value) => value.dispose()))
|
|
|
|
|
|
- for (const id of responded.keys()) {
|
|
|
- if (responded.size <= MAX_RESPONDED) break
|
|
|
- responded.delete(id)
|
|
|
+ let lastSelected: PermissionState | undefined
|
|
|
+ const selected = () => {
|
|
|
+ const key = activeServer()
|
|
|
+ if (global.servers.list().some((conn) => ServerConnection.key(conn) === key)) {
|
|
|
+ lastSelected = ensure(key)
|
|
|
}
|
|
|
+ if (lastSelected) return lastSelected
|
|
|
+ return ensure(server.key)
|
|
|
}
|
|
|
+ const activeDirectory = createMemo(() => {
|
|
|
+ const directory = decode64(params.dir)
|
|
|
+ if (directory) return directory
|
|
|
+ const draft = activeDraft()
|
|
|
+ if (draft) return draft.directory
|
|
|
+ if (!params.id) return
|
|
|
+ if (!global.servers.list().some((conn) => ServerConnection.key(conn) === activeServer())) return
|
|
|
+ return selected().sync.session.lineage.peek(params.id)?.session.directory
|
|
|
+ })
|
|
|
|
|
|
- const respond: PermissionRespondFn = (input) => {
|
|
|
- serverSDK()
|
|
|
- .client.permission.respond(input)
|
|
|
- .catch(() => {
|
|
|
- responded.delete(input.permissionID)
|
|
|
- })
|
|
|
- }
|
|
|
+ createEffect(() => {
|
|
|
+ const directory = activeDirectory()
|
|
|
+ if (!directory) return
|
|
|
+ selected().enableConfiguredDirectory(directory)
|
|
|
+ })
|
|
|
|
|
|
- function respondOnce(permission: PermissionRequest, directory?: string) {
|
|
|
- const now = Date.now()
|
|
|
- const hit = responded.has(permission.id)
|
|
|
- responded.delete(permission.id)
|
|
|
- responded.set(permission.id, now)
|
|
|
- pruneResponded(now)
|
|
|
- if (hit) return
|
|
|
- respond({
|
|
|
- sessionID: permission.sessionID,
|
|
|
- permissionID: permission.id,
|
|
|
- response: "once",
|
|
|
- directory,
|
|
|
- })
|
|
|
- }
|
|
|
+ const permissionsEnabled = createMemo(() => {
|
|
|
+ const directory = activeDirectory()
|
|
|
+ if (!directory) return false
|
|
|
+ return selected().permissionsEnabled(directory)
|
|
|
+ })
|
|
|
|
|
|
- function isAutoAccepting(sessionID: string, directory?: string) {
|
|
|
- const session = directory ? serverSync().child(directory, { bootstrap: false })[0].session : []
|
|
|
- return autoRespondsPermission(store.autoAccept, session, { sessionID }, directory)
|
|
|
+ return {
|
|
|
+ ready: () => selected().ready(),
|
|
|
+ ensureServerState: (key: ServerConnection.Key) => ensure(key).api,
|
|
|
+ currentServerState: () => selected().api,
|
|
|
+ respond(input: Parameters<PermissionRespondFn>[0]) {
|
|
|
+ selected().respond(input)
|
|
|
+ },
|
|
|
+ autoResponds(permission: PermissionRequest, directory?: string) {
|
|
|
+ return selected().autoResponds(permission, directory)
|
|
|
+ },
|
|
|
+ isAutoAccepting(sessionID: string, directory?: string) {
|
|
|
+ return selected().isAutoAccepting(sessionID, directory)
|
|
|
+ },
|
|
|
+ isAutoAcceptingDirectory(directory: string) {
|
|
|
+ return selected().isAutoAcceptingDirectory(directory)
|
|
|
+ },
|
|
|
+ toggleAutoAccept(sessionID: string, directory: string) {
|
|
|
+ selected().toggleAutoAccept(sessionID, directory)
|
|
|
+ },
|
|
|
+ toggleAutoAcceptDirectory(directory: string) {
|
|
|
+ selected().toggleAutoAcceptDirectory(directory)
|
|
|
+ },
|
|
|
+ enableAutoAccept(sessionID: string, directory: string) {
|
|
|
+ selected().enableAutoAccept(sessionID, directory)
|
|
|
+ },
|
|
|
+ disableAutoAccept(sessionID: string, directory?: string) {
|
|
|
+ selected().disableAutoAccept(sessionID, directory)
|
|
|
+ },
|
|
|
+ permissionsEnabled,
|
|
|
+ isPermissionAllowAll(directory: string) {
|
|
|
+ return selected().isPermissionAllowAll(directory)
|
|
|
+ },
|
|
|
}
|
|
|
+ },
|
|
|
+})
|
|
|
|
|
|
- function isAutoAcceptingDirectory(directory: string) {
|
|
|
- return isDirectoryAutoAccepting(store.autoAccept, directory)
|
|
|
- }
|
|
|
+type PermissionState = ReturnType<typeof createServerPermissionState>
|
|
|
+type PermissionEvent = Parameters<Parameters<ServerSDK["event"]["listen"]>[0]>[0]
|
|
|
+
|
|
|
+function createServerPermissionState(input: { sdk: ServerSDK; sync: ServerSync }) {
|
|
|
+ const [store, setStore, _, ready] = persisted(
|
|
|
+ {
|
|
|
+ ...Persist.serverGlobal(input.sdk.scope, "permission", ["permission.v3"]),
|
|
|
+ migrate(value) {
|
|
|
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value
|
|
|
+
|
|
|
+ const data = value as Record<string, unknown>
|
|
|
+ if (data.autoAccept) return value
|
|
|
+
|
|
|
+ return {
|
|
|
+ ...data,
|
|
|
+ autoAccept:
|
|
|
+ typeof data.autoAcceptEdits === "object" && data.autoAcceptEdits && !Array.isArray(data.autoAcceptEdits)
|
|
|
+ ? data.autoAcceptEdits
|
|
|
+ : {},
|
|
|
+ }
|
|
|
+ },
|
|
|
+ },
|
|
|
+ createStore({
|
|
|
+ autoAccept: {} as Record<string, boolean>,
|
|
|
+ }),
|
|
|
+ )
|
|
|
+
|
|
|
+ function enableConfiguredDirectory(directory: string) {
|
|
|
+ if (meta.disposed || !ready()) return
|
|
|
+ const [childStore] = input.sync.child(directory)
|
|
|
+ if (childStore.config.permission !== "allow") return
|
|
|
+ const key = directoryAcceptKey(directory)
|
|
|
+ if (store.autoAccept[key] !== undefined) return
|
|
|
+ setStore(
|
|
|
+ produce((draft) => {
|
|
|
+ draft.autoAccept[key] = true
|
|
|
+ }),
|
|
|
+ )
|
|
|
+ }
|
|
|
|
|
|
- function shouldAutoRespond(permission: PermissionRequest, directory?: string) {
|
|
|
- const session = directory ? serverSync().child(directory, { bootstrap: false })[0].session : []
|
|
|
- return autoRespondsPermission(store.autoAccept, session, permission, directory)
|
|
|
- }
|
|
|
+ const MAX_RESPONDED = 1000
|
|
|
+ const RESPONDED_TTL_MS = 60 * 60 * 1000
|
|
|
+ const responded = new Map<string, number>()
|
|
|
+ const enableVersion = new Map<string, number>()
|
|
|
+ const meta = { disposed: false }
|
|
|
|
|
|
- function bumpEnableVersion(sessionID: string, directory?: string) {
|
|
|
- const key = acceptKey(sessionID, directory)
|
|
|
- const next = (enableVersion.get(key) ?? 0) + 1
|
|
|
- enableVersion.set(key, next)
|
|
|
- return next
|
|
|
+ function pruneResponded(now: number) {
|
|
|
+ for (const [id, ts] of responded) {
|
|
|
+ if (now - ts < RESPONDED_TTL_MS) break
|
|
|
+ responded.delete(id)
|
|
|
}
|
|
|
|
|
|
- const unsubscribe = serverSDK().event.listen((e) => {
|
|
|
- const event = e.details
|
|
|
- if (event?.type !== "permission.asked") return
|
|
|
+ for (const id of responded.keys()) {
|
|
|
+ if (responded.size <= MAX_RESPONDED) break
|
|
|
+ responded.delete(id)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- const perm = event.properties
|
|
|
- if (!shouldAutoRespond(perm, e.name)) return
|
|
|
+ const respond: PermissionRespondFn = (request) => {
|
|
|
+ if (meta.disposed) return
|
|
|
+ input.sdk.client.permission.respond(request).catch(() => {
|
|
|
+ responded.delete(request.permissionID)
|
|
|
+ })
|
|
|
+ }
|
|
|
|
|
|
- respondOnce(perm, e.name)
|
|
|
+ function respondOnce(permission: PermissionRequest, directory?: string) {
|
|
|
+ const now = Date.now()
|
|
|
+ const hit = responded.has(permission.id)
|
|
|
+ responded.delete(permission.id)
|
|
|
+ responded.set(permission.id, now)
|
|
|
+ pruneResponded(now)
|
|
|
+ if (hit) return
|
|
|
+ respond({
|
|
|
+ sessionID: permission.sessionID,
|
|
|
+ permissionID: permission.id,
|
|
|
+ response: "once",
|
|
|
+ directory,
|
|
|
})
|
|
|
- onCleanup(unsubscribe)
|
|
|
+ }
|
|
|
|
|
|
- function enableDirectory(directory: string) {
|
|
|
- const key = directoryAcceptKey(directory)
|
|
|
- setStore(
|
|
|
- produce((draft) => {
|
|
|
- draft.autoAccept[key] = true
|
|
|
- }),
|
|
|
- )
|
|
|
+ function sessions(directory?: string) {
|
|
|
+ const info = Object.values(input.sync.session.data.info).filter((session) => !!session)
|
|
|
+ if (!directory) return info
|
|
|
+ return [...info, ...input.sync.child(directory, { bootstrap: false })[0].session]
|
|
|
+ }
|
|
|
|
|
|
- serverSDK()
|
|
|
- .client.permission.list({ directory })
|
|
|
- .then((x) => {
|
|
|
- if (!isAutoAcceptingDirectory(directory)) return
|
|
|
- for (const perm of x.data ?? []) {
|
|
|
- if (!perm?.id) continue
|
|
|
- if (!shouldAutoRespond(perm, directory)) continue
|
|
|
- respondOnce(perm, directory)
|
|
|
- }
|
|
|
- })
|
|
|
- .catch(() => undefined)
|
|
|
- }
|
|
|
+ function isAutoAccepting(sessionID: string, directory?: string) {
|
|
|
+ return autoRespondsPermission(store.autoAccept, sessions(directory), { sessionID }, directory)
|
|
|
+ }
|
|
|
|
|
|
- function disableDirectory(directory: string) {
|
|
|
- const key = directoryAcceptKey(directory)
|
|
|
- setStore(
|
|
|
- produce((draft) => {
|
|
|
- draft.autoAccept[key] = false
|
|
|
- }),
|
|
|
- )
|
|
|
- }
|
|
|
+ function isAutoAcceptingDirectory(directory: string) {
|
|
|
+ return isDirectoryAutoAccepting(store.autoAccept, directory)
|
|
|
+ }
|
|
|
|
|
|
- function enable(sessionID: string, directory: string) {
|
|
|
- const key = acceptKey(sessionID, directory)
|
|
|
- const version = bumpEnableVersion(sessionID, directory)
|
|
|
- setStore(
|
|
|
- produce((draft) => {
|
|
|
- draft.autoAccept[key] = true
|
|
|
- delete draft.autoAccept[sessionID]
|
|
|
- }),
|
|
|
- )
|
|
|
+ function shouldAutoRespond(permission: PermissionRequest, directory?: string) {
|
|
|
+ return autoRespondsPermission(store.autoAccept, sessions(directory), permission, directory)
|
|
|
+ }
|
|
|
|
|
|
- serverSDK()
|
|
|
- .client.permission.list({ directory })
|
|
|
- .then((x) => {
|
|
|
- if (enableVersion.get(key) !== version) return
|
|
|
- if (!isAutoAccepting(sessionID, directory)) return
|
|
|
- for (const perm of x.data ?? []) {
|
|
|
- if (!perm?.id) continue
|
|
|
- if (!shouldAutoRespond(perm, directory)) continue
|
|
|
- respondOnce(perm, directory)
|
|
|
- }
|
|
|
- })
|
|
|
- .catch(() => undefined)
|
|
|
- }
|
|
|
+ function isPending(permission: PermissionRequest) {
|
|
|
+ const pending = input.sync.session.data.permission[permission.sessionID]
|
|
|
+ return pending === undefined || pending.some((item) => item.id === permission.id)
|
|
|
+ }
|
|
|
|
|
|
- function disable(sessionID: string, directory?: string) {
|
|
|
- bumpEnableVersion(sessionID, directory)
|
|
|
- const key = directory ? acceptKey(sessionID, directory) : sessionID
|
|
|
- setStore(
|
|
|
- produce((draft) => {
|
|
|
- draft.autoAccept[key] = false
|
|
|
- if (!directory) return
|
|
|
- delete draft.autoAccept[sessionID]
|
|
|
- }),
|
|
|
- )
|
|
|
+ async function shouldAutoRespondResolved(permission: PermissionRequest, directory?: string) {
|
|
|
+ const override = sessionAutoAccept(store.autoAccept, sessions(directory), permission, directory)
|
|
|
+ if (override !== undefined) return override
|
|
|
+ if (input.sync.session.lineage.peek(permission.sessionID)) return shouldAutoRespond(permission, directory)
|
|
|
+ const lineage = await input.sync.session.lineage.resolve(permission.sessionID).catch(() => undefined)
|
|
|
+ if (meta.disposed || !lineage) return false
|
|
|
+ return shouldAutoRespond(permission, directory)
|
|
|
+ }
|
|
|
+
|
|
|
+ async function respondPending(
|
|
|
+ permission: PermissionRequest,
|
|
|
+ directory?: string,
|
|
|
+ current: () => boolean = () => true,
|
|
|
+ ) {
|
|
|
+ if (!current() || !isPending(permission)) return
|
|
|
+ if (!(await shouldAutoRespondResolved(permission, directory))) return
|
|
|
+ if (meta.disposed || !current() || !isPending(permission)) return
|
|
|
+ respondOnce(permission, directory)
|
|
|
+ }
|
|
|
+
|
|
|
+ function bumpEnableVersion(sessionID: string, directory?: string) {
|
|
|
+ const key = acceptKey(sessionID, directory)
|
|
|
+ const next = (enableVersion.get(key) ?? 0) + 1
|
|
|
+ enableVersion.set(key, next)
|
|
|
+ return next
|
|
|
+ }
|
|
|
+
|
|
|
+ const handlePermission = (e: PermissionEvent) => {
|
|
|
+ const event = e.details
|
|
|
+ if (event?.type !== "permission.asked") return
|
|
|
+ void respondPending(event.properties, e.name)
|
|
|
+ }
|
|
|
+
|
|
|
+ const unsubscribe = input.sdk.event.listen((event) => {
|
|
|
+ if (ready()) {
|
|
|
+ handlePermission(event)
|
|
|
+ return
|
|
|
}
|
|
|
+ void ready.promise?.then(() => {
|
|
|
+ if (meta.disposed) return
|
|
|
+ handlePermission(event)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ onCleanup(() => {
|
|
|
+ meta.disposed = true
|
|
|
+ unsubscribe()
|
|
|
+ })
|
|
|
+
|
|
|
+ function enableDirectory(directory: string) {
|
|
|
+ if (meta.disposed) return
|
|
|
+ const key = directoryAcceptKey(directory)
|
|
|
+ setStore(
|
|
|
+ produce((draft) => {
|
|
|
+ draft.autoAccept[key] = true
|
|
|
+ }),
|
|
|
+ )
|
|
|
|
|
|
- return {
|
|
|
- ready,
|
|
|
- respond,
|
|
|
- autoResponds(permission: PermissionRequest, directory?: string) {
|
|
|
- return shouldAutoRespond(permission, directory)
|
|
|
- },
|
|
|
- isAutoAccepting,
|
|
|
- isAutoAcceptingDirectory,
|
|
|
- toggleAutoAccept(sessionID: string, directory: string) {
|
|
|
- if (isAutoAccepting(sessionID, directory)) {
|
|
|
- disable(sessionID, directory)
|
|
|
- return
|
|
|
+ input.sdk.client.permission
|
|
|
+ .list({ directory })
|
|
|
+ .then((x) => {
|
|
|
+ if (meta.disposed) return
|
|
|
+ if (!isAutoAcceptingDirectory(directory)) return
|
|
|
+ for (const perm of x.data ?? []) {
|
|
|
+ if (!perm?.id) continue
|
|
|
+ void respondPending(perm, directory, () => isAutoAcceptingDirectory(directory))
|
|
|
}
|
|
|
+ })
|
|
|
+ .catch(() => undefined)
|
|
|
+ }
|
|
|
|
|
|
- enable(sessionID, directory)
|
|
|
- },
|
|
|
- toggleAutoAcceptDirectory(directory: string) {
|
|
|
- if (isAutoAcceptingDirectory(directory)) {
|
|
|
- disableDirectory(directory)
|
|
|
- return
|
|
|
+ function disableDirectory(directory: string) {
|
|
|
+ if (meta.disposed) return
|
|
|
+ const key = directoryAcceptKey(directory)
|
|
|
+ setStore(
|
|
|
+ produce((draft) => {
|
|
|
+ draft.autoAccept[key] = false
|
|
|
+ }),
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ function enable(sessionID: string, directory: string) {
|
|
|
+ if (meta.disposed) return
|
|
|
+ const key = acceptKey(sessionID, directory)
|
|
|
+ const version = bumpEnableVersion(sessionID, directory)
|
|
|
+ setStore(
|
|
|
+ produce((draft) => {
|
|
|
+ draft.autoAccept[key] = true
|
|
|
+ delete draft.autoAccept[sessionID]
|
|
|
+ }),
|
|
|
+ )
|
|
|
+
|
|
|
+ input.sdk.client.permission
|
|
|
+ .list({ directory })
|
|
|
+ .then((x) => {
|
|
|
+ if (meta.disposed) return
|
|
|
+ if (enableVersion.get(key) !== version) return
|
|
|
+ if (!isAutoAccepting(sessionID, directory)) return
|
|
|
+ for (const perm of x.data ?? []) {
|
|
|
+ if (!perm?.id) continue
|
|
|
+ void respondPending(
|
|
|
+ perm,
|
|
|
+ directory,
|
|
|
+ () => enableVersion.get(key) === version && isAutoAccepting(sessionID, directory),
|
|
|
+ )
|
|
|
}
|
|
|
- enableDirectory(directory)
|
|
|
- },
|
|
|
- enableAutoAccept(sessionID: string, directory: string) {
|
|
|
- if (isAutoAccepting(sessionID, directory)) return
|
|
|
- enable(sessionID, directory)
|
|
|
- },
|
|
|
- disableAutoAccept(sessionID: string, directory?: string) {
|
|
|
+ })
|
|
|
+ .catch(() => undefined)
|
|
|
+ }
|
|
|
+
|
|
|
+ function disable(sessionID: string, directory?: string) {
|
|
|
+ if (meta.disposed) return
|
|
|
+ bumpEnableVersion(sessionID, directory)
|
|
|
+ const key = directory ? acceptKey(sessionID, directory) : sessionID
|
|
|
+ setStore(
|
|
|
+ produce((draft) => {
|
|
|
+ draft.autoAccept[key] = false
|
|
|
+ if (!directory) return
|
|
|
+ delete draft.autoAccept[sessionID]
|
|
|
+ }),
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ const api = {
|
|
|
+ ready: () => !meta.disposed && ready(),
|
|
|
+ respond,
|
|
|
+ autoResponds(permission: PermissionRequest, directory?: string) {
|
|
|
+ if (meta.disposed) return false
|
|
|
+ return shouldAutoRespond(permission, directory)
|
|
|
+ },
|
|
|
+ isAutoAccepting(sessionID: string, directory?: string) {
|
|
|
+ if (meta.disposed) return false
|
|
|
+ return isAutoAccepting(sessionID, directory)
|
|
|
+ },
|
|
|
+ isAutoAcceptingDirectory(directory: string) {
|
|
|
+ if (meta.disposed) return false
|
|
|
+ return isAutoAcceptingDirectory(directory)
|
|
|
+ },
|
|
|
+ toggleAutoAccept(sessionID: string, directory: string) {
|
|
|
+ if (meta.disposed) return
|
|
|
+ if (isAutoAccepting(sessionID, directory)) {
|
|
|
disable(sessionID, directory)
|
|
|
- },
|
|
|
- permissionsEnabled,
|
|
|
- isPermissionAllowAll(directory: string) {
|
|
|
- const [childStore] = serverSync().child(directory)
|
|
|
- const perm = childStore.config.permission
|
|
|
- return typeof perm === "string" && perm === "allow"
|
|
|
- },
|
|
|
- }
|
|
|
- },
|
|
|
-})
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ enable(sessionID, directory)
|
|
|
+ },
|
|
|
+ toggleAutoAcceptDirectory(directory: string) {
|
|
|
+ if (meta.disposed) return
|
|
|
+ if (isAutoAcceptingDirectory(directory)) {
|
|
|
+ disableDirectory(directory)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ enableDirectory(directory)
|
|
|
+ },
|
|
|
+ enableAutoAccept(sessionID: string, directory: string) {
|
|
|
+ if (meta.disposed) return
|
|
|
+ if (isAutoAccepting(sessionID, directory)) return
|
|
|
+ enable(sessionID, directory)
|
|
|
+ },
|
|
|
+ disableAutoAccept(sessionID: string, directory?: string) {
|
|
|
+ if (meta.disposed) return
|
|
|
+ disable(sessionID, directory)
|
|
|
+ },
|
|
|
+ isPermissionAllowAll(directory: string) {
|
|
|
+ if (meta.disposed) return false
|
|
|
+ const [childStore] = input.sync.child(directory)
|
|
|
+ return childStore.config.permission === "allow"
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ ...api,
|
|
|
+ api,
|
|
|
+ sync: input.sync,
|
|
|
+ enableConfiguredDirectory,
|
|
|
+ permissionsEnabled(directory: string) {
|
|
|
+ if (meta.disposed) return false
|
|
|
+ const [childStore] = input.sync.child(directory)
|
|
|
+ return hasPermissionPromptRules(childStore.config.permission)
|
|
|
+ },
|
|
|
+ }
|
|
|
+}
|