feat(core): add location-based permission service (#30287)
This commit is contained in:
@@ -9,6 +9,7 @@ import { Config } from "../../src/config/config"
|
||||
import { RuntimeFlags } from "../../src/effect/runtime-flags"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { Permission } from "../../src/permission"
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { Plugin } from "../../src/plugin"
|
||||
import { Provider } from "../../src/provider/provider"
|
||||
import { Skill } from "../../src/skill"
|
||||
@@ -28,7 +29,7 @@ const it = testEffect(agentLayer())
|
||||
const scout = testEffect(agentLayer({ experimentalScout: true }))
|
||||
|
||||
// Helper to evaluate permission for a tool with wildcard pattern
|
||||
function evalPerm(agent: Agent.Info | undefined, permission: string): Permission.Action | undefined {
|
||||
function evalPerm(agent: Agent.Info | undefined, permission: string): PermissionLegacy.Action | undefined {
|
||||
if (!agent) return undefined
|
||||
return Permission.evaluate(permission, "*", agent.permission).action
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
/**
|
||||
* Reproducer for opencode issue #26514:
|
||||
*
|
||||
@@ -60,7 +61,7 @@ it.instance("[#26514] subagent spawned from plan mode inherits read-only restric
|
||||
// session's `permission` field is empty (Plan Mode lives on the agent
|
||||
// ruleset, not the session). So we pass [] through as the parent
|
||||
// session permission, exactly like the actual code path.
|
||||
const parentSessionPermission: Permission.Ruleset = []
|
||||
const parentSessionPermission: PermissionLegacy.Ruleset = []
|
||||
|
||||
const subagentSessionPermission = deriveSubagentSessionPermission({
|
||||
parentSessionPermission,
|
||||
@@ -88,7 +89,7 @@ it.instance("[#26514] explore subagent launched from plan mode also stays read-o
|
||||
expect(planAgent).toBeDefined()
|
||||
expect(explore).toBeDefined()
|
||||
|
||||
const parentSessionPermission: Permission.Ruleset = []
|
||||
const parentSessionPermission: PermissionLegacy.Ruleset = []
|
||||
const subagentSessionPermission = deriveSubagentSessionPermission({
|
||||
parentSessionPermission,
|
||||
parentAgent: planAgent,
|
||||
@@ -113,7 +114,7 @@ it.instance(
|
||||
expect(planAgent).toBeDefined()
|
||||
expect(my).toBeDefined()
|
||||
|
||||
const parentSessionPermission: Permission.Ruleset = []
|
||||
const parentSessionPermission: PermissionLegacy.Ruleset = []
|
||||
const subagentSessionPermission = deriveSubagentSessionPermission({
|
||||
parentSessionPermission,
|
||||
parentAgent: planAgent,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { describe, test, expect } from "bun:test"
|
||||
import { Effect } from "effect"
|
||||
import { Permission } from "../src/permission"
|
||||
@@ -9,7 +10,7 @@ const it = testEffect(Config.defaultLayer)
|
||||
const load = Config.use.get()
|
||||
|
||||
describe("Permission.evaluate for permission.task", () => {
|
||||
const createRuleset = (rules: Record<string, "allow" | "deny" | "ask">): Permission.Ruleset =>
|
||||
const createRuleset = (rules: Record<string, "allow" | "deny" | "ask">): PermissionLegacy.Ruleset =>
|
||||
Object.entries(rules).map(([pattern, action]) => ({
|
||||
permission: "task",
|
||||
pattern,
|
||||
@@ -75,7 +76,7 @@ describe("Permission.disabled for task tool", () => {
|
||||
// Note: The `disabled` function checks if a TOOL should be completely removed from the tool list.
|
||||
// It only disables a tool when there's a rule with `pattern: "*"` and `action: "deny"`.
|
||||
// It does NOT evaluate complex subagent patterns - those are handled at runtime by `evaluate`.
|
||||
const createRuleset = (rules: Record<string, "allow" | "deny" | "ask">): Permission.Ruleset =>
|
||||
const createRuleset = (rules: Record<string, "allow" | "deny" | "ask">): PermissionLegacy.Ruleset =>
|
||||
Object.entries(rules).map(([pattern, action]) => ({
|
||||
permission: "task",
|
||||
pattern,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { test, expect } from "bun:test"
|
||||
import os from "os"
|
||||
import { Cause, Deferred, Effect, Exit, Fiber, Layer } from "effect"
|
||||
@@ -5,7 +6,6 @@ import { EventV2Bridge } from "../../src/event-v2-bridge"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { Permission } from "../../src/permission"
|
||||
import { PermissionID } from "../../src/permission/schema"
|
||||
import { InstanceBootstrap } from "../../src/project/bootstrap-service"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { TestInstance, tmpdirScoped } from "../fixture/fixture"
|
||||
@@ -261,8 +261,8 @@ test("merge - preserves rule order", () => {
|
||||
})
|
||||
|
||||
test("merge - config permission overrides default ask", () => {
|
||||
const defaults: Permission.Ruleset = [{ permission: "*", pattern: "*", action: "ask" }]
|
||||
const config: Permission.Ruleset = [{ permission: "bash", pattern: "*", action: "allow" }]
|
||||
const defaults: PermissionLegacy.Ruleset = [{ permission: "*", pattern: "*", action: "ask" }]
|
||||
const config: PermissionLegacy.Ruleset = [{ permission: "bash", pattern: "*", action: "allow" }]
|
||||
const merged = Permission.merge(defaults, config)
|
||||
|
||||
expect(Permission.evaluate("bash", "ls", merged).action).toBe("allow")
|
||||
@@ -270,8 +270,8 @@ test("merge - config permission overrides default ask", () => {
|
||||
})
|
||||
|
||||
test("merge - config ask overrides default allow", () => {
|
||||
const defaults: Permission.Ruleset = [{ permission: "bash", pattern: "*", action: "allow" }]
|
||||
const config: Permission.Ruleset = [{ permission: "bash", pattern: "*", action: "ask" }]
|
||||
const defaults: PermissionLegacy.Ruleset = [{ permission: "bash", pattern: "*", action: "allow" }]
|
||||
const config: PermissionLegacy.Ruleset = [{ permission: "bash", pattern: "*", action: "ask" }]
|
||||
const merged = Permission.merge(defaults, config)
|
||||
|
||||
expect(Permission.evaluate("bash", "ls", merged).action).toBe("ask")
|
||||
@@ -443,8 +443,8 @@ test("evaluate - later wildcard permission can override earlier specific permiss
|
||||
})
|
||||
|
||||
test("evaluate - merges multiple rulesets", () => {
|
||||
const config: Permission.Ruleset = [{ permission: "bash", pattern: "*", action: "allow" }]
|
||||
const approved: Permission.Ruleset = [{ permission: "bash", pattern: "rm", action: "deny" }]
|
||||
const config: PermissionLegacy.Ruleset = [{ permission: "bash", pattern: "*", action: "allow" }]
|
||||
const approved: PermissionLegacy.Ruleset = [{ permission: "bash", pattern: "rm", action: "deny" }]
|
||||
const result = Permission.evaluate("bash", "rm", config, approved)
|
||||
expect(result.action).toBe("deny")
|
||||
})
|
||||
@@ -588,7 +588,7 @@ it.instance(
|
||||
ruleset: [{ permission: "bash", pattern: "*", action: "deny" }],
|
||||
}),
|
||||
)
|
||||
expect(err).toBeInstanceOf(Permission.DeniedError)
|
||||
expect(err).toBeInstanceOf(PermissionLegacy.DeniedError)
|
||||
}),
|
||||
{ git: true },
|
||||
)
|
||||
@@ -655,10 +655,10 @@ it.instance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const events = yield* EventV2Bridge.Service
|
||||
const seen = yield* Deferred.make<Permission.Request>()
|
||||
const seen = yield* Deferred.make<PermissionLegacy.Request>()
|
||||
const unsub = yield* events.listen((event) => {
|
||||
if (event.type === Permission.Event.Asked.type)
|
||||
Deferred.doneUnsafe(seen, Effect.succeed(event.data as Permission.Request))
|
||||
Deferred.doneUnsafe(seen, Effect.succeed(event.data as PermissionLegacy.Request))
|
||||
return Effect.void
|
||||
})
|
||||
yield* Effect.addFinalizer(() => unsub)
|
||||
@@ -703,7 +703,7 @@ it.instance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const fiber = yield* ask({
|
||||
id: PermissionID.make("per_test1"),
|
||||
id: PermissionLegacy.ID.make("per_test1"),
|
||||
sessionID: SessionID.make("session_test"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -713,7 +713,7 @@ it.instance(
|
||||
}).pipe(Effect.forkScoped)
|
||||
|
||||
yield* waitForPending(1)
|
||||
yield* reply({ requestID: PermissionID.make("per_test1"), reply: "once" })
|
||||
yield* reply({ requestID: PermissionLegacy.ID.make("per_test1"), reply: "once" })
|
||||
yield* Fiber.join(fiber)
|
||||
}),
|
||||
{ git: true },
|
||||
@@ -724,7 +724,7 @@ it.instance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const fiber = yield* ask({
|
||||
id: PermissionID.make("per_test2"),
|
||||
id: PermissionLegacy.ID.make("per_test2"),
|
||||
sessionID: SessionID.make("session_test"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -734,11 +734,11 @@ it.instance(
|
||||
}).pipe(Effect.forkScoped)
|
||||
|
||||
yield* waitForPending(1)
|
||||
yield* reply({ requestID: PermissionID.make("per_test2"), reply: "reject" })
|
||||
yield* reply({ requestID: PermissionLegacy.ID.make("per_test2"), reply: "reject" })
|
||||
|
||||
const exit = yield* Fiber.await(fiber)
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(PermissionLegacy.RejectedError)
|
||||
}),
|
||||
{ git: true },
|
||||
)
|
||||
@@ -748,7 +748,7 @@ it.instance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const fiber = yield* ask({
|
||||
id: PermissionID.make("per_test2b"),
|
||||
id: PermissionLegacy.ID.make("per_test2b"),
|
||||
sessionID: SessionID.make("session_test"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -759,7 +759,7 @@ it.instance(
|
||||
|
||||
yield* waitForPending(1)
|
||||
yield* reply({
|
||||
requestID: PermissionID.make("per_test2b"),
|
||||
requestID: PermissionLegacy.ID.make("per_test2b"),
|
||||
reply: "reject",
|
||||
message: "Use a safer command",
|
||||
})
|
||||
@@ -768,7 +768,7 @@ it.instance(
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
if (Exit.isFailure(exit)) {
|
||||
const err = Cause.squash(exit.cause)
|
||||
expect(err).toBeInstanceOf(Permission.CorrectedError)
|
||||
expect(err).toBeInstanceOf(PermissionLegacy.CorrectedError)
|
||||
expect(String(err)).toContain("Use a safer command")
|
||||
}
|
||||
}),
|
||||
@@ -780,7 +780,7 @@ it.instance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const fiber = yield* ask({
|
||||
id: PermissionID.make("per_test3"),
|
||||
id: PermissionLegacy.ID.make("per_test3"),
|
||||
sessionID: SessionID.make("session_test"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -790,7 +790,7 @@ it.instance(
|
||||
}).pipe(Effect.forkScoped)
|
||||
|
||||
yield* waitForPending(1)
|
||||
yield* reply({ requestID: PermissionID.make("per_test3"), reply: "always" })
|
||||
yield* reply({ requestID: PermissionLegacy.ID.make("per_test3"), reply: "always" })
|
||||
yield* Fiber.join(fiber)
|
||||
|
||||
const result = yield* ask({
|
||||
@@ -811,7 +811,7 @@ it.instance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const a = yield* ask({
|
||||
id: PermissionID.make("per_test4a"),
|
||||
id: PermissionLegacy.ID.make("per_test4a"),
|
||||
sessionID: SessionID.make("session_same"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -821,7 +821,7 @@ it.instance(
|
||||
}).pipe(Effect.forkScoped)
|
||||
|
||||
const b = yield* ask({
|
||||
id: PermissionID.make("per_test4b"),
|
||||
id: PermissionLegacy.ID.make("per_test4b"),
|
||||
sessionID: SessionID.make("session_same"),
|
||||
permission: "edit",
|
||||
patterns: ["foo.ts"],
|
||||
@@ -831,13 +831,13 @@ it.instance(
|
||||
}).pipe(Effect.forkScoped)
|
||||
|
||||
yield* waitForPending(2)
|
||||
yield* reply({ requestID: PermissionID.make("per_test4a"), reply: "reject" })
|
||||
yield* reply({ requestID: PermissionLegacy.ID.make("per_test4a"), reply: "reject" })
|
||||
|
||||
const [ea, eb] = yield* Effect.all([Fiber.await(a), Fiber.await(b)])
|
||||
expect(Exit.isFailure(ea)).toBe(true)
|
||||
expect(Exit.isFailure(eb)).toBe(true)
|
||||
if (Exit.isFailure(ea)) expect(Cause.squash(ea.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||
if (Exit.isFailure(eb)) expect(Cause.squash(eb.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||
if (Exit.isFailure(ea)) expect(Cause.squash(ea.cause)).toBeInstanceOf(PermissionLegacy.RejectedError)
|
||||
if (Exit.isFailure(eb)) expect(Cause.squash(eb.cause)).toBeInstanceOf(PermissionLegacy.RejectedError)
|
||||
}),
|
||||
{ git: true },
|
||||
)
|
||||
@@ -847,7 +847,7 @@ it.instance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const a = yield* ask({
|
||||
id: PermissionID.make("per_test5a"),
|
||||
id: PermissionLegacy.ID.make("per_test5a"),
|
||||
sessionID: SessionID.make("session_same"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -857,7 +857,7 @@ it.instance(
|
||||
}).pipe(Effect.forkScoped)
|
||||
|
||||
const b = yield* ask({
|
||||
id: PermissionID.make("per_test5b"),
|
||||
id: PermissionLegacy.ID.make("per_test5b"),
|
||||
sessionID: SessionID.make("session_same"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -867,7 +867,7 @@ it.instance(
|
||||
}).pipe(Effect.forkScoped)
|
||||
|
||||
yield* waitForPending(2)
|
||||
yield* reply({ requestID: PermissionID.make("per_test5a"), reply: "always" })
|
||||
yield* reply({ requestID: PermissionLegacy.ID.make("per_test5a"), reply: "always" })
|
||||
|
||||
yield* Fiber.join(a)
|
||||
yield* Fiber.join(b)
|
||||
@@ -881,7 +881,7 @@ it.instance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const a = yield* ask({
|
||||
id: PermissionID.make("per_test6a"),
|
||||
id: PermissionLegacy.ID.make("per_test6a"),
|
||||
sessionID: SessionID.make("session_a"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -891,7 +891,7 @@ it.instance(
|
||||
}).pipe(Effect.forkScoped)
|
||||
|
||||
const b = yield* ask({
|
||||
id: PermissionID.make("per_test6b"),
|
||||
id: PermissionLegacy.ID.make("per_test6b"),
|
||||
sessionID: SessionID.make("session_b"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -901,10 +901,10 @@ it.instance(
|
||||
}).pipe(Effect.forkScoped)
|
||||
|
||||
yield* waitForPending(2)
|
||||
yield* reply({ requestID: PermissionID.make("per_test6a"), reply: "always" })
|
||||
yield* reply({ requestID: PermissionLegacy.ID.make("per_test6a"), reply: "always" })
|
||||
|
||||
yield* Fiber.join(a)
|
||||
expect((yield* list()).map((item) => item.id)).toEqual([PermissionID.make("per_test6b")])
|
||||
expect((yield* list()).map((item) => item.id)).toEqual([PermissionLegacy.ID.make("per_test6b")])
|
||||
|
||||
yield* rejectAll()
|
||||
yield* Fiber.await(b)
|
||||
@@ -917,10 +917,10 @@ it.instance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const events = yield* EventV2Bridge.Service
|
||||
const seen = yield* Deferred.make<{ sessionID: SessionID; requestID: PermissionID; reply: Permission.Reply }>()
|
||||
const seen = yield* Deferred.make<{ sessionID: SessionID; requestID: PermissionLegacy.ID; reply: PermissionLegacy.Reply }>()
|
||||
|
||||
const fiber = yield* ask({
|
||||
id: PermissionID.make("per_test7"),
|
||||
id: PermissionLegacy.ID.make("per_test7"),
|
||||
sessionID: SessionID.make("session_test"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -935,13 +935,13 @@ it.instance(
|
||||
if (event.type === Permission.Event.Replied.type)
|
||||
Deferred.doneUnsafe(
|
||||
seen,
|
||||
Effect.succeed(event.data as { sessionID: SessionID; requestID: PermissionID; reply: Permission.Reply }),
|
||||
Effect.succeed(event.data as { sessionID: SessionID; requestID: PermissionLegacy.ID; reply: PermissionLegacy.Reply }),
|
||||
)
|
||||
return Effect.void
|
||||
})
|
||||
yield* Effect.addFinalizer(() => unsub)
|
||||
|
||||
yield* reply({ requestID: PermissionID.make("per_test7"), reply: "once" })
|
||||
yield* reply({ requestID: PermissionLegacy.ID.make("per_test7"), reply: "once" })
|
||||
yield* Fiber.join(fiber)
|
||||
expect(
|
||||
yield* Deferred.await(seen).pipe(
|
||||
@@ -952,7 +952,7 @@ it.instance(
|
||||
),
|
||||
).toEqual({
|
||||
sessionID: SessionID.make("session_test"),
|
||||
requestID: PermissionID.make("per_test7"),
|
||||
requestID: PermissionLegacy.ID.make("per_test7"),
|
||||
reply: "once",
|
||||
})
|
||||
}),
|
||||
@@ -969,7 +969,7 @@ it.live("permission requests stay isolated by directory", () =>
|
||||
.provide(
|
||||
{ directory: one },
|
||||
ask({
|
||||
id: PermissionID.make("per_dir_a"),
|
||||
id: PermissionLegacy.ID.make("per_dir_a"),
|
||||
sessionID: SessionID.make("session_dir_a"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -984,7 +984,7 @@ it.live("permission requests stay isolated by directory", () =>
|
||||
.provide(
|
||||
{ directory: two },
|
||||
ask({
|
||||
id: PermissionID.make("per_dir_b"),
|
||||
id: PermissionLegacy.ID.make("per_dir_b"),
|
||||
sessionID: SessionID.make("session_dir_b"),
|
||||
permission: "bash",
|
||||
patterns: ["pwd"],
|
||||
@@ -1000,8 +1000,8 @@ it.live("permission requests stay isolated by directory", () =>
|
||||
|
||||
expect(onePending).toHaveLength(1)
|
||||
expect(twoPending).toHaveLength(1)
|
||||
expect(onePending[0].id).toBe(PermissionID.make("per_dir_a"))
|
||||
expect(twoPending[0].id).toBe(PermissionID.make("per_dir_b"))
|
||||
expect(onePending[0].id).toBe(PermissionLegacy.ID.make("per_dir_a"))
|
||||
expect(twoPending[0].id).toBe(PermissionLegacy.ID.make("per_dir_b"))
|
||||
|
||||
yield* store.provide({ directory: one }, reply({ requestID: onePending[0].id, reply: "reject" }))
|
||||
yield* store.provide({ directory: two }, reply({ requestID: twoPending[0].id, reply: "reject" }))
|
||||
@@ -1018,7 +1018,7 @@ it.instance(
|
||||
const test = yield* TestInstance
|
||||
const store = yield* InstanceStore.Service
|
||||
const fiber = yield* ask({
|
||||
id: PermissionID.make("per_dispose"),
|
||||
id: PermissionLegacy.ID.make("per_dispose"),
|
||||
sessionID: SessionID.make("session_dispose"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -1033,7 +1033,7 @@ it.instance(
|
||||
|
||||
const exit = yield* Fiber.await(fiber)
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(PermissionLegacy.RejectedError)
|
||||
}),
|
||||
{ git: true },
|
||||
)
|
||||
@@ -1045,7 +1045,7 @@ it.instance(
|
||||
const test = yield* TestInstance
|
||||
const store = yield* InstanceStore.Service
|
||||
const fiber = yield* ask({
|
||||
id: PermissionID.make("per_reload"),
|
||||
id: PermissionLegacy.ID.make("per_reload"),
|
||||
sessionID: SessionID.make("session_reload"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -1059,7 +1059,7 @@ it.instance(
|
||||
|
||||
const exit = yield* Fiber.await(fiber)
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(PermissionLegacy.RejectedError)
|
||||
}),
|
||||
{ git: true },
|
||||
)
|
||||
@@ -1068,7 +1068,7 @@ it.instance(
|
||||
"reply - fails for unknown requestID",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const exit = yield* reply({ requestID: PermissionID.make("per_unknown"), reply: "once" }).pipe(Effect.exit)
|
||||
const exit = yield* reply({ requestID: PermissionLegacy.ID.make("per_unknown"), reply: "once" }).pipe(Effect.exit)
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
if (Exit.isFailure(exit)) {
|
||||
expect(Cause.squash(exit.cause)).toMatchObject({ _tag: "Permission.NotFoundError", requestID: "per_unknown" })
|
||||
@@ -1095,7 +1095,7 @@ it.instance(
|
||||
],
|
||||
}),
|
||||
)
|
||||
expect(err).toBeInstanceOf(Permission.DeniedError)
|
||||
expect(err).toBeInstanceOf(PermissionLegacy.DeniedError)
|
||||
}),
|
||||
{ git: true },
|
||||
)
|
||||
@@ -1135,7 +1135,7 @@ it.instance(
|
||||
}),
|
||||
)
|
||||
|
||||
expect(err).toBeInstanceOf(Permission.DeniedError)
|
||||
expect(err).toBeInstanceOf(PermissionLegacy.DeniedError)
|
||||
expect(yield* list()).toHaveLength(0)
|
||||
}),
|
||||
{ git: true },
|
||||
@@ -1149,7 +1149,7 @@ it.instance(
|
||||
const store = yield* InstanceStore.Service
|
||||
|
||||
const fiber = yield* ask({
|
||||
id: PermissionID.make("per_reload"),
|
||||
id: PermissionLegacy.ID.make("per_reload"),
|
||||
sessionID: SessionID.make("session_reload"),
|
||||
permission: "bash",
|
||||
patterns: ["ls"],
|
||||
@@ -1164,7 +1164,7 @@ it.instance(
|
||||
|
||||
const exit = yield* Fiber.await(fiber)
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(PermissionLegacy.RejectedError)
|
||||
}),
|
||||
{ git: true },
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ import { tmpdirScoped } from "../fixture/fixture"
|
||||
import { GlobalBus } from "../../src/bus/global"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { ProjectTable } from "@opencode-ai/core/project/sql"
|
||||
import { PermissionTable, SessionTable } from "@opencode-ai/core/session/sql"
|
||||
import { SessionTable } from "@opencode-ai/core/session/sql"
|
||||
import { WorkspaceTable } from "@opencode-ai/core/control-plane/workspace.sql"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { Hash } from "@opencode-ai/core/util/hash"
|
||||
@@ -218,16 +218,6 @@ describe("Project.fromDirectory", () => {
|
||||
})
|
||||
.run()
|
||||
.pipe(Effect.orDie)
|
||||
yield* db
|
||||
.insert(PermissionTable)
|
||||
.values({
|
||||
project_id: rootProject.id,
|
||||
data: [{ permission: "edit", pattern: "*", action: "allow" }],
|
||||
time_created: Date.now(),
|
||||
time_updated: Date.now(),
|
||||
})
|
||||
.run()
|
||||
.pipe(Effect.orDie)
|
||||
yield* db
|
||||
.insert(WorkspaceTable)
|
||||
.values({ id: workspaceID, type: "local", name: "test", project_id: rootProject.id })
|
||||
@@ -245,14 +235,6 @@ describe("Project.fromDirectory", () => {
|
||||
(yield* db.select().from(SessionTable).where(eq(SessionTable.id, sessionID)).get().pipe(Effect.orDie))
|
||||
?.project_id,
|
||||
).toBe(remoteID)
|
||||
expect(
|
||||
yield* db
|
||||
.select()
|
||||
.from(PermissionTable)
|
||||
.where(eq(PermissionTable.project_id, remoteID))
|
||||
.get()
|
||||
.pipe(Effect.orDie),
|
||||
).toBeDefined()
|
||||
expect(
|
||||
(yield* db.select().from(WorkspaceTable).where(eq(WorkspaceTable.id, workspaceID)).get().pipe(Effect.orDie))
|
||||
?.project_id,
|
||||
|
||||
@@ -579,6 +579,32 @@ const scenarios: Scenario[] = [
|
||||
.get("/api/provider/{providerID}", "v2.provider.get")
|
||||
.at((ctx) => ({ path: route("/api/provider/{providerID}", { providerID: "missing" }), headers: ctx.headers() }))
|
||||
.json(404, object, "status"),
|
||||
http.protected.get("/api/permission/request", "v2.permission.request.list").json(200, array),
|
||||
http.protected
|
||||
.get("/api/session/{sessionID}/permission/request", "v2.session.permission.list")
|
||||
.seeded((ctx) => ctx.session({ title: "Permission list owner" }))
|
||||
.at((ctx) => ({
|
||||
path: route("/api/session/{sessionID}/permission/request", { sessionID: ctx.state.id }),
|
||||
headers: ctx.headers(),
|
||||
}))
|
||||
.json(200, array),
|
||||
http.protected
|
||||
.post("/api/session/{sessionID}/permission/request/{requestID}/reply", "v2.session.permission.reply")
|
||||
.seeded((ctx) => ctx.session({ title: "Permission owner" }))
|
||||
.at((ctx) => ({
|
||||
path: route("/api/session/{sessionID}/permission/request/{requestID}/reply", {
|
||||
sessionID: ctx.state.id,
|
||||
requestID: "per_httpapi_missing",
|
||||
}),
|
||||
headers: ctx.headers(),
|
||||
body: { reply: "once" },
|
||||
}))
|
||||
.json(404, object, "status"),
|
||||
http.protected.get("/api/permission/saved", "v2.permission.saved.list").json(200, array),
|
||||
http.protected
|
||||
.delete("/api/permission/saved/{id}", "v2.permission.saved.remove")
|
||||
.at((ctx) => ({ path: route("/api/permission/saved/{id}", { id: "psv_httpapi_missing" }), headers: ctx.headers() }))
|
||||
.status(204, undefined, "status"),
|
||||
http.protected
|
||||
.get("/api/session", "v2.session.list")
|
||||
.at((ctx) => ({ path: "/api/session?roots=true", headers: ctx.headers() }))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { NodeHttpServer, NodeServices } from "@effect/platform-node"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { describe, expect } from "bun:test"
|
||||
@@ -8,7 +9,6 @@ import { WorkspaceV2 } from "@opencode-ai/core/workspace"
|
||||
import { ControlPaths } from "../../src/server/routes/instance/httpapi/groups/control"
|
||||
import { InstancePaths } from "../../src/server/routes/instance/httpapi/groups/instance"
|
||||
import { SessionPaths } from "../../src/server/routes/instance/httpapi/groups/session"
|
||||
import { PermissionID } from "../../src/permission/schema"
|
||||
import { ProjectV2 } from "@opencode-ai/core/project"
|
||||
import { QuestionID } from "../../src/question/schema"
|
||||
import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server"
|
||||
@@ -167,7 +167,7 @@ describe("instance HttpApi", () => {
|
||||
handlerContext,
|
||||
),
|
||||
)
|
||||
const permissionID = PermissionID.ascending()
|
||||
const permissionID = PermissionLegacy.ID.ascending()
|
||||
const questionReplyID = QuestionID.ascending()
|
||||
const questionRejectID = QuestionID.ascending()
|
||||
const [permission, questionReply, questionReject] = yield* Effect.all(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { afterEach, describe, expect } from "bun:test"
|
||||
import { NodeHttpServer, NodeServices } from "@effect/platform-node"
|
||||
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
|
||||
@@ -11,7 +12,6 @@ import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { registerAdapter } from "../../src/control-plane/adapters"
|
||||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
import { Workspace } from "../../src/control-plane/workspace"
|
||||
import { PermissionID } from "../../src/permission/schema"
|
||||
|
||||
import { InstanceBootstrap } from "../../src/project/bootstrap"
|
||||
import { InstanceBootstrap as InstanceBootstrapService } from "../../src/project/bootstrap-service"
|
||||
@@ -913,7 +913,7 @@ describe("session HttpApi", () => {
|
||||
}),
|
||||
).toMatchObject({ id: session.id })
|
||||
|
||||
const permissionID = String(PermissionID.ascending())
|
||||
const permissionID = String(PermissionLegacy.ID.ascending())
|
||||
const permission = yield* request(
|
||||
pathFor(SessionPaths.permissions, {
|
||||
sessionID: session.id,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test"
|
||||
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
|
||||
import path from "path"
|
||||
@@ -332,7 +333,7 @@ describe("session.llm.ai-sdk adapter", () => {
|
||||
})
|
||||
|
||||
test("preserves tool-error cause", async () => {
|
||||
const error = new Permission.RejectedError()
|
||||
const error = new PermissionLegacy.RejectedError()
|
||||
const events = await Effect.runPromise(
|
||||
LLMAISDK.toLLMEvents(LLMAISDK.adapterState(), {
|
||||
type: "tool-error",
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Global } from "@opencode-ai/core/global"
|
||||
import { ProjectTable } from "@opencode-ai/core/project/sql"
|
||||
import { ProjectV2 } from "@opencode-ai/core/project"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { SessionTable, MessageTable, PartTable, TodoTable, PermissionTable } from "@opencode-ai/core/session/sql"
|
||||
import { SessionTable, MessageTable, PartTable, TodoTable } from "@opencode-ai/core/session/sql"
|
||||
import { SessionShareTable } from "@opencode-ai/core/share/sql"
|
||||
import { SessionID, MessageID, PartID } from "../../src/session/schema"
|
||||
|
||||
@@ -574,7 +574,7 @@ describe("JSON to SQLite migration", () => {
|
||||
expect(todos[2].position).toBe(2)
|
||||
})
|
||||
|
||||
test("migrates permissions", async () => {
|
||||
test("does not migrate legacy permissions", async () => {
|
||||
await writeProject(storageDir, {
|
||||
id: "proj_test123abc",
|
||||
worktree: "/",
|
||||
@@ -592,12 +592,7 @@ describe("JSON to SQLite migration", () => {
|
||||
|
||||
const stats = await JsonMigration.run(db)
|
||||
|
||||
expect(stats?.permissions).toBe(1)
|
||||
|
||||
const permissions = db.select().from(PermissionTable).all()
|
||||
expect(permissions.length).toBe(1)
|
||||
expect(permissions[0].project_id).toBe("proj_test123abc")
|
||||
expect(permissions[0].data).toEqual(permissionData)
|
||||
expect(stats?.permissions).toBe(0)
|
||||
})
|
||||
|
||||
test("migrates session shares", async () => {
|
||||
@@ -694,7 +689,7 @@ describe("JSON to SQLite migration", () => {
|
||||
expect(todos[1].position).toBe(2)
|
||||
})
|
||||
|
||||
test("skips orphaned todos, permissions, and shares", async () => {
|
||||
test("skips orphaned todos and shares", async () => {
|
||||
await writeProject(storageDir, {
|
||||
id: "proj_test123abc",
|
||||
worktree: "/",
|
||||
@@ -733,11 +728,10 @@ describe("JSON to SQLite migration", () => {
|
||||
const stats = await JsonMigration.run(db)
|
||||
|
||||
expect(stats.todos).toBe(1)
|
||||
expect(stats.permissions).toBe(1)
|
||||
expect(stats.permissions).toBe(0)
|
||||
expect(stats.shares).toBe(1)
|
||||
|
||||
expect(db.select().from(TodoTable).all().length).toBe(1)
|
||||
expect(db.select().from(PermissionTable).all().length).toBe(1)
|
||||
expect(db.select().from(SessionShareTable).all().length).toBe(1)
|
||||
})
|
||||
|
||||
@@ -848,7 +842,7 @@ describe("JSON to SQLite migration", () => {
|
||||
expect(stats.messages).toBe(1)
|
||||
expect(stats.parts).toBe(1)
|
||||
expect(stats.todos).toBe(1)
|
||||
expect(stats.permissions).toBe(1)
|
||||
expect(stats.permissions).toBe(0)
|
||||
expect(stats.shares).toBe(1)
|
||||
expect(stats.errors.length).toBeGreaterThanOrEqual(6)
|
||||
|
||||
@@ -857,7 +851,6 @@ describe("JSON to SQLite migration", () => {
|
||||
expect(db.select().from(MessageTable).all().length).toBe(1)
|
||||
expect(db.select().from(PartTable).all().length).toBe(1)
|
||||
expect(db.select().from(TodoTable).all().length).toBe(1)
|
||||
expect(db.select().from(PermissionTable).all().length).toBe(1)
|
||||
expect(db.select().from(SessionShareTable).all().length).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { describe, expect } from "bun:test"
|
||||
import path from "path"
|
||||
import { Effect } from "effect"
|
||||
@@ -26,7 +27,7 @@ const glob = (p: string) =>
|
||||
process.platform === "win32" ? Filesystem.normalizePathPattern(p) : p.replaceAll("\\", "/")
|
||||
|
||||
function makeCtx() {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
const ctx: Tool.Context = {
|
||||
...baseCtx,
|
||||
ask: (req) =>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { describe, expect } from "bun:test"
|
||||
import path from "path"
|
||||
import { Cause, Effect, Exit, Layer } from "effect"
|
||||
@@ -52,12 +53,12 @@ const ctx = {
|
||||
}
|
||||
|
||||
const asks = () => {
|
||||
const items: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const items: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
return {
|
||||
items,
|
||||
next: {
|
||||
...ctx,
|
||||
ask: (req: Omit<Permission.Request, "id" | "sessionID" | "tool">) =>
|
||||
ask: (req: Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">) =>
|
||||
Effect.sync(() => {
|
||||
items.push(req)
|
||||
}),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { describe, expect } from "bun:test"
|
||||
import fs from "fs/promises"
|
||||
import os from "os"
|
||||
@@ -186,7 +187,7 @@ describe("tool.grep", () => {
|
||||
[path.join(alias, "*")]: "allow",
|
||||
},
|
||||
})
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
const next: Tool.Context = {
|
||||
...ctx,
|
||||
ask: (req) =>
|
||||
@@ -234,7 +235,7 @@ describe("tool.grep", () => {
|
||||
yield* appfs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie)
|
||||
yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo])
|
||||
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
const next: Tool.Context = {
|
||||
...ctx,
|
||||
ask: (req) =>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { afterEach, describe, expect } from "bun:test"
|
||||
import { Effect, Layer } from "effect"
|
||||
import path from "path"
|
||||
@@ -83,12 +84,12 @@ const put = Effect.fn("LspToolTest.put")(function* (file: string) {
|
||||
})
|
||||
|
||||
const asks = () => {
|
||||
const items: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const items: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
return {
|
||||
items,
|
||||
next: {
|
||||
...ctx,
|
||||
ask: (req: Omit<Permission.Request, "id" | "sessionID" | "tool">) =>
|
||||
ask: (req: Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">) =>
|
||||
Effect.sync(() => {
|
||||
items.push(req)
|
||||
}),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { afterEach, describe, expect } from "bun:test"
|
||||
import { Cause, Effect, Exit, Layer, Stream } from "effect"
|
||||
import path from "path"
|
||||
@@ -140,12 +141,12 @@ const load = Effect.fn("ReadToolTest.load")(function* (p: string) {
|
||||
return yield* fs.readFileString(p)
|
||||
})
|
||||
const asks = () => {
|
||||
const items: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const items: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
return {
|
||||
items,
|
||||
next: {
|
||||
...ctx,
|
||||
ask: (req: Omit<Permission.Request, "id" | "sessionID" | "tool">) =>
|
||||
ask: (req: Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">) =>
|
||||
Effect.sync(() => {
|
||||
items.push(req)
|
||||
}),
|
||||
@@ -328,7 +329,7 @@ describe("tool.read env file permissions", () => {
|
||||
let asked = false
|
||||
const next = {
|
||||
...ctx,
|
||||
ask: (req: Omit<Permission.Request, "id" | "sessionID" | "tool">) =>
|
||||
ask: (req: Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">) =>
|
||||
Effect.sync(() => {
|
||||
for (const pattern of req.patterns) {
|
||||
const rule = Permission.evaluate(req.permission, pattern, info.permission)
|
||||
@@ -336,7 +337,7 @@ describe("tool.read env file permissions", () => {
|
||||
asked = true
|
||||
}
|
||||
if (rule.action === "deny") {
|
||||
throw new Permission.DeniedError({ ruleset: info.permission })
|
||||
throw new PermissionLegacy.DeniedError({ ruleset: info.permission })
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { describe, expect } from "bun:test"
|
||||
import { Cause, Effect, Exit, Layer } from "effect"
|
||||
import type * as Scope from "effect/Scope"
|
||||
@@ -155,9 +156,9 @@ const each = (
|
||||
}
|
||||
}
|
||||
|
||||
const capture = (requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">>, stop?: Error) => ({
|
||||
const capture = (requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">>, stop?: Error) => ({
|
||||
...ctx,
|
||||
ask: (req: Omit<Permission.Request, "id" | "sessionID" | "tool">) =>
|
||||
ask: (req: Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">) =>
|
||||
Effect.sync(() => {
|
||||
requests.push(req)
|
||||
if (stop) throw stop
|
||||
@@ -222,7 +223,7 @@ describe("tool.shell permissions", () => {
|
||||
yield* runIn(
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run(
|
||||
{
|
||||
command: "echo hello",
|
||||
@@ -244,7 +245,7 @@ describe("tool.shell permissions", () => {
|
||||
yield* runIn(
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run(
|
||||
{
|
||||
command: "echo foo && echo bar",
|
||||
@@ -268,7 +269,7 @@ describe("tool.shell permissions", () => {
|
||||
runIn(
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run(
|
||||
{
|
||||
command: "Write-Host foo; if ($?) { Write-Host bar }",
|
||||
@@ -297,7 +298,7 @@ describe("tool.shell permissions", () => {
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{
|
||||
@@ -323,7 +324,7 @@ describe("tool.shell permissions", () => {
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
const file = process.platform === "win32" ? `${process.env.WINDIR!.replaceAll("\\", "/")}/*` : "/etc/*"
|
||||
const want = process.platform === "win32" ? glob(path.join(process.env.WINDIR!, "*")) : "/etc/*"
|
||||
expect(
|
||||
@@ -354,7 +355,7 @@ describe("tool.shell permissions", () => {
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const file = path.join(outerTmp, "outside.txt").replaceAll("\\", "/")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run(
|
||||
{
|
||||
command: `echo $(cat "${file}")`,
|
||||
@@ -383,7 +384,7 @@ describe("tool.shell permissions", () => {
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{
|
||||
@@ -409,7 +410,7 @@ describe("tool.shell permissions", () => {
|
||||
runIn(
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
const file = `${process.env.WINDIR!.replaceAll("\\", "/")}/win.ini`
|
||||
yield* run(
|
||||
{
|
||||
@@ -440,7 +441,7 @@ describe("tool.shell permissions", () => {
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{
|
||||
@@ -468,7 +469,7 @@ describe("tool.shell permissions", () => {
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{
|
||||
@@ -497,7 +498,7 @@ describe("tool.shell permissions", () => {
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{
|
||||
@@ -525,7 +526,7 @@ describe("tool.shell permissions", () => {
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{
|
||||
@@ -560,7 +561,7 @@ describe("tool.shell permissions", () => {
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
const root = path.parse(process.env.WINDIR!).root.replace(/[\\/]+$/, "")
|
||||
expect(
|
||||
yield* fail(
|
||||
@@ -593,7 +594,7 @@ describe("tool.shell permissions", () => {
|
||||
runIn(
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run(
|
||||
{
|
||||
command: "Get-Content $env:WINDIR/win.ini",
|
||||
@@ -620,7 +621,7 @@ describe("tool.shell permissions", () => {
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{
|
||||
@@ -649,7 +650,7 @@ describe("tool.shell permissions", () => {
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{
|
||||
@@ -677,7 +678,7 @@ describe("tool.shell permissions", () => {
|
||||
runIn(
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run(
|
||||
{
|
||||
command: "Set-Location C:/Windows",
|
||||
@@ -705,7 +706,7 @@ describe("tool.shell permissions", () => {
|
||||
runIn(
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run(
|
||||
{
|
||||
command: "Write-Output ('a' * 3)",
|
||||
@@ -731,7 +732,7 @@ describe("tool.shell permissions", () => {
|
||||
runIn(
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run(
|
||||
{
|
||||
command: `TYPE "${path.join(process.env.WINDIR!, "win.ini")}"`,
|
||||
@@ -755,7 +756,7 @@ describe("tool.shell permissions", () => {
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{
|
||||
@@ -779,7 +780,7 @@ describe("tool.shell permissions", () => {
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{
|
||||
@@ -810,7 +811,7 @@ describe("tool.shell permissions", () => {
|
||||
const want = Filesystem.normalizePathPattern(path.join(outerTmp, "*"))
|
||||
|
||||
for (const dir of forms(outerTmp)) {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{
|
||||
@@ -842,7 +843,7 @@ describe("tool.shell permissions", () => {
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
const want = glob(path.join(os.tmpdir(), "*"))
|
||||
expect(
|
||||
yield* fail(
|
||||
@@ -871,7 +872,7 @@ describe("tool.shell permissions", () => {
|
||||
projectRoot,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
const want = glob(path.join(os.tmpdir(), "*"))
|
||||
expect(
|
||||
yield* fail(
|
||||
@@ -903,7 +904,7 @@ describe("tool.shell permissions", () => {
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
const filepath = path.join(outerTmp, "outside.txt")
|
||||
expect(
|
||||
yield* fail(
|
||||
@@ -931,7 +932,7 @@ describe("tool.shell permissions", () => {
|
||||
yield* runIn(
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run(
|
||||
{
|
||||
command: `rm -rf ${path.join(tmp, "nested")}`,
|
||||
@@ -952,7 +953,7 @@ describe("tool.shell permissions", () => {
|
||||
yield* runIn(
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run(
|
||||
{
|
||||
command: "git log --oneline -5",
|
||||
@@ -974,7 +975,7 @@ describe("tool.shell permissions", () => {
|
||||
yield* runIn(
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run(
|
||||
{
|
||||
command: "cd .",
|
||||
@@ -996,7 +997,7 @@ describe("tool.shell permissions", () => {
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const err = new Error("stop after permission")
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
expect(
|
||||
yield* fail(
|
||||
{ command: "echo test > output.txt", description: "Redirect test output" },
|
||||
@@ -1017,7 +1018,7 @@ describe("tool.shell permissions", () => {
|
||||
yield* runIn(
|
||||
tmp,
|
||||
Effect.gen(function* () {
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
yield* run({ command: "ls -la", description: "List" }, capture(requests))
|
||||
const bashReq = requests.find((r) => r.permission === "bash")
|
||||
expect(bashReq).toBeDefined()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { Cause, Effect, Exit, Layer } from "effect"
|
||||
import { afterEach, describe, expect } from "bun:test"
|
||||
@@ -67,7 +68,7 @@ Use this skill.
|
||||
})).find((tool) => tool.id === SkillTool.id)
|
||||
if (!tool) throw new Error("Skill tool not found")
|
||||
|
||||
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
|
||||
const requests: Array<Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">> = []
|
||||
const ctx: Tool.Context = {
|
||||
...baseCtx,
|
||||
ask: (req) =>
|
||||
|
||||
Reference in New Issue
Block a user