lembas -l / --local: everything in the project, nothing written elsewhere
Config, data and state are rooted at <project>/.agent/local/lembas (or .lembas/), kept out of git; a local directory the repository carries is refused. The user's logins and connections.yaml are read, never written; the user's config.yaml is not read. The TUI is started with TMPDIR inside it (the compiled binary unpacks its native library there) and OpenTUI's cache goes there too; the agent's commands keep the user's TMPDIR. An instance serves models, chat, speech and the balance only: the link, /remote, the service and the hub are off, deletes are not sent, and the server library, search, fetch, embeddings and personalization are not used. Self-update and uninstall are refused. hardline_disable is ignored, and tools treat the local directory as outside the project. The smoke test runs a whole local session in the compiled binary and checks that HOME, the XDG directories and TMPDIR gained nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
27 files changed
+636
-105
No files matched your search
+18
-8
@@ -10,11 +10,14 @@ export function selfCommand(args: string[]): string[] {
|
||||
return compiled ? [process.execPath, ...args] : [process.execPath, Bun.main, ...args]
|
||||
}
|
||||
|
||||
function execv(argv: string[]): Error {
|
||||
/** execv, or execve with `env` when given (local mode's start: TMPDIR must be set before the
|
||||
* runtime reads it). */
|
||||
function execv(argv: string[], env?: Record<string, string | undefined>): Error {
|
||||
try {
|
||||
const { dlopen, FFIType, ptr } = require("bun:ffi") as typeof import("bun:ffi")
|
||||
const libc = dlopen("libc.so.6", {
|
||||
execv: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.i32 },
|
||||
execve: { args: [FFIType.ptr, FFIType.ptr, FFIType.ptr], returns: FFIType.i32 },
|
||||
fcntl: { args: [FFIType.i32, FFIType.i32, FFIType.i32], returns: FFIType.i32 },
|
||||
})
|
||||
// Everything above stdio closes on exec: an open database, an MCP server's pipe would otherwise
|
||||
@@ -35,10 +38,17 @@ function execv(argv: string[]): Error {
|
||||
libc.symbols.fcntl(fd, F_SETFD, FD_CLOEXEC)
|
||||
} catch {}
|
||||
}
|
||||
const strings = argv.map((a) => Buffer.from(`${a}\0`))
|
||||
const table = new BigUint64Array(strings.length + 1)
|
||||
strings.forEach((s, i) => (table[i] = BigInt(ptr(s))))
|
||||
libc.symbols.execv(ptr(strings[0]!), ptr(table))
|
||||
const table = (items: string[]) => {
|
||||
const strings = items.map((a) => Buffer.from(`${a}\0`))
|
||||
const t = new BigUint64Array(strings.length + 1)
|
||||
strings.forEach((s, i) => (t[i] = BigInt(ptr(s))))
|
||||
return { strings, t }
|
||||
}
|
||||
const args = table(argv)
|
||||
if (env) {
|
||||
const vars = table(Object.entries(env).flatMap(([k, v]) => (v === undefined ? [] : [`${k}=${v}`])))
|
||||
libc.symbols.execve(ptr(args.strings[0]!), ptr(args.t), ptr(vars.t))
|
||||
} else libc.symbols.execv(ptr(args.strings[0]!), ptr(args.t))
|
||||
// execv returns only when it failed.
|
||||
return new Error("execv failed")
|
||||
} catch (e) {
|
||||
@@ -47,9 +57,9 @@ function execv(argv: string[]): Error {
|
||||
}
|
||||
|
||||
/** Never returns: the new program runs, in this process or a child this one waits for. */
|
||||
export async function restart(args: string[]): Promise<never> {
|
||||
export async function restart(args: string[], env?: Record<string, string | undefined>): Promise<never> {
|
||||
const argv = selfCommand(args)
|
||||
execv(argv)
|
||||
const child = Bun.spawn(argv, { stdio: ["inherit", "inherit", "inherit"] })
|
||||
execv(argv, env)
|
||||
const child = Bun.spawn(argv, { stdio: ["inherit", "inherit", "inherit"], ...(env ? { env } : {}) })
|
||||
process.exit(await child.exited)
|
||||
}
|
||||
Reference in new issue
Block a user