Dashboard › opencode › Distillation
2358f847-fd41-4f29-906e-b35f662d727b["lore_tm_v1_lsXBj3CVHLEzeNRZ9zbiSwiqTl2d1UhXG2_cJERvqBU","lore_tm_v1_Ee8tZh7iQZrXNZq--fd1n2zkAmaL-b6vq4mdRpq9Wz4"]
/home/byk/.local/share/opencode-v2-pilot/config/opencode/plugins/pty.ts is a 684-line TypeScript OpenCode plugin with ID local-pty; it imports Plugin, Context, ToolContext, Effect Schema, Node child-process spawn, and filesystem APIs open and realpath.plugins/pty.ts defines limits: MAX_BUFFER_SIZE = 1_000_000, MAX_SESSIONS = 25, MAX_SESSIONS_PER_OWNER = 5, DEFAULT_READ_LIMIT = 500, MAX_READ_LIMIT = 2_000, MAX_READ_OFFSET = 10_000, MAX_LINE_LENGTH = 2_000, MAX_PATTERN_LENGTH = 500, MAX_WRITE_SIZE = 65_536, MAX_TIMEOUT_SECONDS = 2_147_483, KILL_GRACE_MS = 1_000, KILL_FORCE_MS = 1_000, NOTIFICATION_TIMEOUT_MS = 5_000, MAX_COMMAND_LENGTH = 4_096, MAX_ARGUMENTS = 128, MAX_ARGUMENT_LENGTH = 16_384, MAX_ARGUMENT_BYTES = 65_536, MAX_PATH_LENGTH = 4_096, MAX_TITLE_LENGTH = 500, MAX_DESCRIPTION_LENGTH = 2_000, and MAX_REGEX_WORKERS = 4.plugins/pty.ts uses FIXED_ENV with HOME: process.env.HOME ?? "/home/byk", LANG: process.env.LANG ?? "C.UTF-8", PATH: "/usr/bin:/bin", SHELL: "/bin/sh", and TERM: "xterm-256color".SpawnInput accepts a nonempty command up to MAX_COMMAND_LENGTH, up to MAX_ARGUMENTS arguments each capped at MAX_ARGUMENT_LENGTH, optional workdir up to MAX_PATH_LENGTH, optional title up to MAX_TITLE_LENGTH, a required nonempty description up to MAX_DESCRIPTION_LENGTH, optional notifyOnExit, and optional integer timeoutSeconds from 1 through MAX_TIMEOUT_SECONDS.WriteInput accepts PTY id and data capped at MAX_WRITE_SIZE; documented escape decoding supports \n, \r, \t, \xNN, \uNNNN, and \\.ReadInput accepts PTY id, optional zero-based offset from 0 through MAX_READ_OFFSET, optional limit from 1 through MAX_READ_LIMIT with default DEFAULT_READ_LIMIT, optional extended-regular-expression pattern up to MAX_PATTERN_LENGTH, and optional ignoreCase.KillInput accepts PTY id and optional cleanup; cleanup removes the session and buffered output after stopping it and defaults to false.plugins/pty.ts defines Status as "running" | "killing" | "killed" | "exited" and tracks each Sessionβs identity, title, description, command, arguments, working directory, status, notification and timeout settings, timeout outcome, exit code/signal, PID, creation time, parent session ID, child process, timer, close/termination promises, and output buffer.Map<string, Session>, deleted parent sessions in Set<string>, global and per-owner spawn reservations, pending spawns, pending notifications, shutdown state, and the number of active regex workers.notifyOnExit is true, the plugin is not closing, and the parent session has not been deleted; ctx.session.synthetic uses delivery "steer", description PTY exited: ${truncate(session.description, 100)}, and JSON type "pty.exit" containing ID, truncated description, exit code/signal, timeout settings, output-line count, and the last nonblank line truncated to 200 characters.withTimeout(..., NOTIFICATION_TIMEOUT_MS, ...); failures are logged as [pty] failed to notify ${session.id}, and pending notifications are tracked until settled.plugins/pty.ts registers 5 tools in order: 1. pty_spawn starts an interactive background PTY and has shell permission; 2. pty_write writes input/control characters and has shell permission; 3. pty_read reads buffered output by line range or regex and has no shell-permission option; 4. pty_list lists owned local PTY sessions and statuses; 5. pty_kill stops a session and optionally removes its output and has shell permission. All use codemode: false.pty_spawn validates argv, rejects spawning while closing or after the parent session is deleted, enforces 5 sessions per parent Session and 25 sessions per Location, and counts in-progress reservations toward both limits.MAX_SESSIONS_PER_OWNER, pty_spawn removes one owned inactive session whose status is "exited" or "killed"; if none exists, it throws PTY session limit reached for this Session (5).sessions.size + reserved >= MAX_SESSIONS and throws PTY Location session limit reached (25).pty_spawn calls authorize(ctx, input.command, input.args, input.workdir ?? ctx.location.directory, context), creates an ID of the form pty_ plus the first 8 hyphen-free characters of crypto.randomUUID(), and spawns /usr/bin/script with ["-qefc", shellCommand(input.command, input.args), "/dev/null"]./usr/bin/script with cwd: /proc/self/fd/${binding.handle.fd}, detached: true, and env: FIXED_ENV; it closes the directory handle after the child emits "spawn" or "error".Session object exists, then initializes the session buffer from their concatenated output and retains only the final MAX_BUFFER_SIZE characters; subsequent stdout, stderr, and process error messages are appended with the same tail cap.${input.command} ${input.args.join(" ")} trimmed, defaults notifyOnExit to false, records workdir: binding.directory, and sets a timeout timer to mark timedOut = true, set status "killing", and call terminate(session) after timeoutSeconds * 1_000."killing" session to "killed" and all others to "exited", sets exitCode to the process code or to 1 for signal exit and 0 otherwise, records exitSignal, and calls notify(session).pty_spawn closes the directory handle or terminates the newly created process and throws the corresponding closing/deleted-session error.pty_write requires the PTY to belong to the calling parent session and be "running", decodes supported escapes, checks the decoded UTF-8 byte length against 65,536, requests shell permission using assertShell, rechecks ownership/status after approval, writes to stdin, and returns JSON type "pty.write" with the ID and byte count.pty_read defaults to offset 0 and limit 500; it allows at most 4 concurrent patterned reads, obtains limit + 1 entries to determine pagination, truncates each returned line to 2,000 characters, and returns JSON type "pty.read" with ID, status, pattern, offset, limit, line-number/text records, total line count, hasMore, and optional nextOffset.pty_list exposes only sessions whose parentSessionID matches the calling context.sessionID; it returns [] when none exist and otherwise pretty-printed session metadata.pty_kill checks parent-session ownership, obtains shell permission, rechecks ownership after approval, and either calls remove(current) when cleanup is true or terminates a running/killing process while preserving its buffered record when cleanup is false.session.deleted, marks the parent session ID deleted, waits for its tracked spawn operations, logs unexpected spawn-cleanup failures, then removes every owned session; plugin teardown sets closing = true, aborts event subscription, waits for all pending spawns, removes all sessions, and waits for pending notifications.requireSession() prevents cross-parent access by throwing PTY session not found: ${id} when the ID is absent or its parentSessionID differs from the callerβs session ID.decodeEscapes() recognizes only \xNN, \uNNNN, \n, \r, \t, and \\; unmatched backslash sequences remain unchanged because replacement is regex-based.shellCommand() single-quotes the command and every argument, escapes embedded single quotes as '\'', and joins the quoted parts with spaces.terminate() is idempotent via session.terminate; it sends SIGTERM to process group -session.pid, waits KILL_GRACE_MS = 1_000, sends SIGKILL if still open, waits another KILL_FORCE_MS = 1_000, and throws if the PTY still has not closed. signal() ignores ESRCH.authorize() in the pilot plugin resolves both the requested workdir and ctx.location.project.directory with realpath; if the resolved directory is outside the project, it requests external_directory permission for that resolved directory before requesting shell permission for the quoted command.authorize() calls realpath(workdir) again and rejects a changed result, opens the directory with constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW, verifies handle.stat().isDirectory(), and checks realpath(/proc/self/fd/${handle.fd}) still equals the originally authorized directory before returning { directory, handle }.validateCommand() enforces a nonempty command, at most 128 arguments, command UTF-8 size at most 4,096 bytes, each argument at most 16,384 UTF-8 bytes, aggregate argument bytes at most 65,536, and fully shell-encoded argv size at most 65,536 bytes.assertShell() requests action: "shell" with no saved approval; for writes, its resource combines the original quoted command with <stdin> and the quoted decoded input.matchingLines() delegates regex matching to /usr/bin/grep with arguments ["-a", "-m", String(limit), "-nE", ...(ignoreCase ? ["-i"] : []), "--", pattern]; it limits execution to 250 ms, caps captured stdout at MAX_BUFFER_SIZE * 2, caps captured stderr at MAX_LINE_LENGTH, treats grep exit 1 as no matches, and parses line-number:text output.socketPath = "/run/opencode-pty/supervisor.sock" in /home/byk/.local/share/opencode-v2-pilot/supervisor/internal/supervisor/server.go and supervisorService = "opencode-pty-supervisor.service" in /home/byk/.local/share/opencode-v2-pilot/supervisor/internal/supervisor/systemd.go.