Repository URL to install this package:
|
Version:
0.4.48 ▾
|
/**
* JSON-RPC client that communicates with the Python TUI server over a pipe (fd 3/4).
*
* The Python server reads from our write pipe and writes to our read pipe.
* Protocol: newline-delimited JSON-RPC 2.0.
*/
import { createReadStream, createWriteStream } from "fs";
import { createInterface } from "readline";
let nextId = 1;
const pending = new Map();
let eventHandler = null;
// fd 3 = read from server, fd 4 = write to server
let writeStream = null;
let readStream = null;
let rl = null;
let initialized = false;
export function initRpc(readFd, writeFd) {
if (initialized)
return;
initialized = true;
writeStream = createWriteStream("", { fd: writeFd });
readStream = createReadStream("", { fd: readFd, encoding: "utf-8" });
rl = createInterface({ input: readStream });
rl.on("line", (line) => {
if (!line.trim())
return;
try {
const msg = JSON.parse(line);
// Event notification (no id)
if (!("id" in msg) && typeof msg.method === "string" && msg.method.startsWith("event:")) {
const eventName = msg.method.slice(6);
eventHandler?.(eventName, msg.params);
return;
}
// Response to a request
const id = String(msg.id);
const entry = pending.get(id);
if (!entry)
return;
pending.delete(id);
if (msg.error) {
entry.reject(new Error(msg.error.message || "RPC error"));
}
else {
entry.resolve(msg.result);
}
}
catch {
// ignore malformed lines
}
});
rl.on("close", () => {
for (const entry of pending.values()) {
entry.reject(new Error("RPC connection closed"));
}
pending.clear();
});
}
export function onEvent(handler) {
eventHandler = handler;
}
export function destroyRpc() {
rl?.close();
readStream?.destroy();
writeStream?.end();
rl = null;
readStream = null;
writeStream = null;
}
export function call(method, params = {}) {
if (!writeStream) {
return Promise.reject(new Error("RPC not initialized"));
}
const id = String(nextId++);
const payload = JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n";
return new Promise((resolve, reject) => {
pending.set(id, {
resolve: resolve,
reject,
});
writeStream.write(payload, "utf-8");
});
}
export const api = {
listProjects: () => call("projects.list"),
addProject: (label, workspace_dir) => call("projects.add", { label, workspace_dir }),
removeProject: (project_id) => call("projects.remove", { project_id }),
showProject: (project_id) => call("projects.show", { project_id }),
getPipeline: (project_id) => call("pipeline.get", { project_id }),
listTickets: (project_id) => call("tickets.list", { project_id }),
addTicket: (project_id, title, priority) => call("tickets.add", { project_id, title, priority }),
moveTicket: (ticket_id, column_id, project_id) => call("tickets.move", { ticket_id, column_id, project_id }),
deleteTicket: (ticket_id, project_id) => call("tickets.delete", { ticket_id, project_id }),
updateTicket: (ticket_id, patch, project_id) => call("tickets.update", { ticket_id, patch, project_id }),
listTasks: (project_id) => call("tasks.list", { project_id }),
taskStatus: (ticket_id, project_id) => call("tasks.status", { ticket_id, project_id }),
sandboxStart: (ticket_id, project_id) => call("sandbox.start", { ticket_id, project_id }),
sandboxStop: (ticket_id, project_id) => call("sandbox.stop", { ticket_id, project_id }),
sandboxWsUrl: (ticket_id, project_id) => call("sandbox.ws_url", { ticket_id, project_id }),
supervisorStart: (ticket_id, prompt, project_id) => call("supervisor.start", { ticket_id, prompt, project_id }),
supervisorStop: (ticket_id, project_id) => call("supervisor.stop", { ticket_id, project_id }),
snapshot: () => call("snapshot"),
ticketHistory: (ticket_id, project_id, limit) => call("ticket.history", { ticket_id, project_id, limit }),
ticketArtifacts: (ticket_id, subdir) => call("ticket.artifacts", { ticket_id, subdir }),
ticketArtifactRead: (ticket_id, path) => call("ticket.artifact_read", { ticket_id, path }),
listModels: () => call("models.list"),
addModel: (params) => call("models.add", params),
removeModel: (name) => call("models.remove", { name }),
setDefaultModel: (name) => call("models.set_default", { name }),
listProviders: () => call("providers.list"),
addProvider: (params) => call("providers.add", params),
updateProvider: (name, updates) => call("providers.update", { name, ...updates }),
removeProvider: (name) => call("providers.remove", { name }),
getProvider: (name) => call("providers.get", { name }),
getNetworkConfig: () => call("network.get"),
saveNetworkConfig: (config) => call("network.save", { config }),
getEnv: () => call("env.get"),
saveEnv: (entries) => call("env.save", { entries }),
listContainers: () => call("containers.list"),
startContainer: (name) => call("containers.start", { name }),
stopContainer: (name) => call("containers.stop", { name }),
rebuildContainer: (name) => call("containers.rebuild", { name }),
gitRepoInfo: (project_id) => call("git.repo_info", { project_id }),
gitDiff: (ticket_id, project_id) => call("git.diff", { ticket_id, project_id }),
gitDiffFile: (ticket_id, path, project_id) => call("git.diff_file", { ticket_id, path, project_id }),
ticketArtifactOpen: (ticket_id, path) => call("ticket.artifact_open", { ticket_id, path }),
setAutoDispatch: (project_id, enabled) => call("projects.set_auto_dispatch", { project_id, enabled }),
getAutoDispatch: (project_id) => call("projects.get_auto_dispatch", { project_id }),
mcpList: () => call("mcp.list"),
mcpSave: (config) => call("mcp.save", { config }),
testModel: (name) => call("models.test", { name }),
listSessions: (limit, offset) => call("sessions.list", { limit: limit ?? 20, offset: offset ?? 0 }),
onboardingStatus: () => call("onboarding.status"),
tmuxSsh: (ticket_id, project_id) => call("tmux.ssh", { ticket_id, project_id }),
tmuxOmni: (ticket_id, project_id) => call("tmux.omni", { ticket_id, project_id }),
tmuxChat: (ticket_id, project_id) => call("tmux.chat", { ticket_id, project_id }),
tmuxOmniLocal: () => call("tmux.omni_local"),
tmuxWork: () => call("tmux.work"),
// Pane management
tmuxSetup: () => call("tmux.setup"),
tmuxListPanes: () => call("tmux.list_panes"),
tmuxClosePaneFor: (ticket_id, pane_type) => call("tmux.close_pane", { ticket_id, pane_type }),
tmuxCloseTicketPanes: (ticket_id) => call("tmux.close_ticket_panes", { ticket_id }),
tmuxFocusMain: () => call("tmux.focus_main"),
tmuxPanesForTicket: (ticket_id) => call("tmux.panes_for_ticket", { ticket_id }),
};
//# sourceMappingURL=rpc.js.map