mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-17 03:10:38 +09:00
Fix headless OpenCode permission prompts
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
c6364149b1
commit
ed62d58cb2
9 changed files with 652 additions and 430 deletions
|
|
@ -0,0 +1,91 @@
|
|||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { asBoolean } from "@paperclipai/adapter-utils/server-utils";
|
||||
|
||||
type PreparedOpenCodeRuntimeConfig = {
|
||||
env: Record<string, string>;
|
||||
notes: string[];
|
||||
cleanup: () => Promise<void>;
|
||||
};
|
||||
|
||||
function resolveXdgConfigHome(env: Record<string, string>): string {
|
||||
return (
|
||||
(typeof env.XDG_CONFIG_HOME === "string" && env.XDG_CONFIG_HOME.trim()) ||
|
||||
(typeof process.env.XDG_CONFIG_HOME === "string" && process.env.XDG_CONFIG_HOME.trim()) ||
|
||||
path.join(os.homedir(), ".config")
|
||||
);
|
||||
}
|
||||
|
||||
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
async function readJsonObject(filepath: string): Promise<Record<string, unknown>> {
|
||||
try {
|
||||
const raw = await fs.readFile(filepath, "utf8");
|
||||
const parsed = JSON.parse(raw);
|
||||
return isPlainObject(parsed) ? parsed : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export async function prepareOpenCodeRuntimeConfig(input: {
|
||||
env: Record<string, string>;
|
||||
config: Record<string, unknown>;
|
||||
}): Promise<PreparedOpenCodeRuntimeConfig> {
|
||||
const skipPermissions = asBoolean(input.config.dangerouslySkipPermissions, true);
|
||||
if (!skipPermissions) {
|
||||
return {
|
||||
env: input.env,
|
||||
notes: [],
|
||||
cleanup: async () => {},
|
||||
};
|
||||
}
|
||||
|
||||
const sourceConfigDir = path.join(resolveXdgConfigHome(input.env), "opencode");
|
||||
const runtimeConfigHome = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-opencode-config-"));
|
||||
const runtimeConfigDir = path.join(runtimeConfigHome, "opencode");
|
||||
const runtimeConfigPath = path.join(runtimeConfigDir, "opencode.json");
|
||||
|
||||
await fs.mkdir(runtimeConfigDir, { recursive: true });
|
||||
try {
|
||||
await fs.cp(sourceConfigDir, runtimeConfigDir, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
errorOnExist: false,
|
||||
dereference: false,
|
||||
});
|
||||
} catch (err) {
|
||||
if ((err as NodeJS.ErrnoException | null)?.code !== "ENOENT") {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const existingConfig = await readJsonObject(runtimeConfigPath);
|
||||
const existingPermission = isPlainObject(existingConfig.permission)
|
||||
? existingConfig.permission
|
||||
: {};
|
||||
const nextConfig = {
|
||||
...existingConfig,
|
||||
permission: {
|
||||
...existingPermission,
|
||||
external_directory: "allow",
|
||||
},
|
||||
};
|
||||
await fs.writeFile(runtimeConfigPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
|
||||
|
||||
return {
|
||||
env: {
|
||||
...input.env,
|
||||
XDG_CONFIG_HOME: runtimeConfigHome,
|
||||
},
|
||||
notes: [
|
||||
"Injected runtime OpenCode config with permission.external_directory=allow to avoid headless approval prompts.",
|
||||
],
|
||||
cleanup: async () => {
|
||||
await fs.rm(runtimeConfigHome, { recursive: true, force: true });
|
||||
},
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue