@thurstonsand/pi-agent-switchboard 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +40 -8
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,3 +1,7 @@
1
+ // packages/pi/src/index.ts
2
+ import { execFile } from "node:child_process";
3
+ import { promisify } from "node:util";
4
+
1
5
  // packages/shared/src/index.ts
2
6
  import { execFileSync } from "node:child_process";
3
7
  import { readFileSync } from "node:fs";
@@ -103,13 +107,43 @@ var store = globalThis;
103
107
  function claim() {
104
108
  const env = process.env;
105
109
  if (env.SWB_MANAGED === "1" && env.SWB_DB && env.SWB_TMUX_SESSION) {
106
- store[KEY] = { launch: Object.freeze({ db: env.SWB_DB, tmuxSession: env.SWB_TMUX_SESSION }), attention: new Set };
110
+ store[KEY] = {
111
+ launch: Object.freeze({ db: env.SWB_DB, tmuxSession: env.SWB_TMUX_SESSION, bin: env.SWB_BIN ?? "" }),
112
+ attention: new Set,
113
+ ...env.SWB_BIN ? {} : { failure: "started by an swb older than this recorder; upgrade swb" }
114
+ };
107
115
  }
108
116
  delete env.SWB_MANAGED;
109
117
  delete env.SWB_DB;
110
118
  delete env.SWB_TMUX_SESSION;
119
+ delete env.SWB_BIN;
111
120
  return store[KEY];
112
121
  }
122
+ var run = promisify(execFile);
123
+ function host(bin) {
124
+ const swb = async (...args) => {
125
+ try {
126
+ return (await run(bin, args, { timeout: 15000 })).stdout;
127
+ } catch (error) {
128
+ const { stderr, message } = error;
129
+ throw new Error(stderr?.trim() || message);
130
+ }
131
+ };
132
+ return {
133
+ name: "swb",
134
+ async launch(input) {
135
+ await swb("launch", "--cwd", input.cwd, "--session-id", input.sessionId, "--model", input.model);
136
+ return { success: true };
137
+ },
138
+ async listSessions() {
139
+ const sessions = JSON.parse(await swb("ls", "--json"));
140
+ return sessions.filter((s) => s.open).map((s) => ({ sessionId: s.id, cwd: s.cwd, title: s.title ?? s.id }));
141
+ },
142
+ async wake(sessionId) {
143
+ await swb("wake", sessionId);
144
+ }
145
+ };
146
+ }
113
147
  function liveHost(row) {
114
148
  if (row && row.boot_id === bootId() && pidAlive(row.pid))
115
149
  return row.tmux_session;
@@ -127,6 +161,8 @@ function src_default(pi) {
127
161
  return;
128
162
  const proc = claimed;
129
163
  let ui;
164
+ if (proc.launch.bin)
165
+ pi.events.on("pi-sessions:hosts:v1", (request) => request.register(host(proc.launch.bin)));
130
166
  const showFailure = () => ui?.setStatus("swb", ui.theme.fg("error", `swb: not recording: ${proc.failure}`));
131
167
  const connect = () => {
132
168
  proc.db ??= new Db(proc.launch.db, { create: false });
@@ -192,9 +228,7 @@ function src_default(pi) {
192
228
  db.run("UPDATE sessions SET phase = 'idle', cwd = ?, project_root = ?, branch = ?, transcript = ? WHERE session_id = ?", ctx.cwd, projectRoot(ctx.cwd), branch(ctx.cwd), transcript, id);
193
229
  });
194
230
  });
195
- pi.on("message_start", (event, ctx) => {
196
- if (event.message.role !== "user")
197
- return;
231
+ pi.on("agent_start", (_event, ctx) => {
198
232
  writeFor(ctx, (db, id) => {
199
233
  const transcript = ctx.sessionManager.getSessionFile();
200
234
  if (!transcript)
@@ -202,12 +236,10 @@ function src_default(pi) {
202
236
  const now = Date.now();
203
237
  db.run(`INSERT INTO sessions (session_id, tool, cwd, project_root, transcript, title, branch, phase, created_at, last_prompt_at)
204
238
  VALUES (?, 'pi', ?, ?, ?, ?, ?, 'working', ?, ?)
205
- ON CONFLICT (session_id) DO UPDATE SET phase = 'working', last_prompt_at = excluded.last_prompt_at`, id, ctx.cwd, projectRoot(ctx.cwd), transcript, ctx.sessionManager.getSessionName() ?? null, branch(ctx.cwd), now, now);
239
+ ON CONFLICT (session_id) DO UPDATE SET phase = 'working',
240
+ last_prompt_at = CASE WHEN sessions.phase = 'idle' THEN excluded.last_prompt_at ELSE sessions.last_prompt_at END`, id, ctx.cwd, projectRoot(ctx.cwd), transcript, ctx.sessionManager.getSessionName() ?? null, branch(ctx.cwd), now, now);
206
241
  });
207
242
  });
208
- pi.on("agent_start", (_event, ctx) => {
209
- writeFor(ctx, (db, id) => db.run("UPDATE sessions SET phase = 'working' WHERE session_id = ?", id));
210
- });
211
243
  const attention = (open) => (data) => {
212
244
  const attentionId = data.attentionId;
213
245
  if (typeof attentionId !== "string" || !current)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thurstonsand/pi-agent-switchboard",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Records managed pi sessions for Agent Switchboard (swb)",
6
6
  "license": "MIT",