@threadbase-sh/streamer 1.15.3 → 1.16.0

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.
package/dist/index.d.cts CHANGED
@@ -596,6 +596,7 @@ type ApiDeps = {
596
596
  handleGetOutput: (sessionId: string, res: ServerResponse) => void;
597
597
  handleSendInput: (sessionId: string, req: IncomingMessage, res: ServerResponse) => Promise<void>;
598
598
  handleCancel: (sessionId: string, res: ServerResponse) => void;
599
+ handleStopSession: (sessionId: string, res: ServerResponse) => Promise<void>;
599
600
  handleSetSessionName: (sessionId: string, req: IncomingMessage, res: ServerResponse) => Promise<void>;
600
601
  handleUploadFile: (sessionId: string, req: IncomingMessage, res: ServerResponse) => Promise<void>;
601
602
  handleAdopt: (sessionId: string, res: ServerResponse) => Promise<void>;
@@ -725,6 +726,7 @@ declare class StreamerServer {
725
726
  private log;
726
727
  private agentConfig;
727
728
  private agentClient;
729
+ private sessionStatusBus;
728
730
  constructor(config: ServerConfig & {
729
731
  apiKey: string;
730
732
  });
@@ -771,6 +773,7 @@ declare class StreamerServer {
771
773
  private handleUploadFile;
772
774
  private handleGetOutput;
773
775
  private handleCancel;
776
+ private handleStopSession;
774
777
  private handleAdopt;
775
778
  private handleStartSession;
776
779
  private linkSessionToProject;
package/dist/index.d.ts CHANGED
@@ -596,6 +596,7 @@ type ApiDeps = {
596
596
  handleGetOutput: (sessionId: string, res: ServerResponse) => void;
597
597
  handleSendInput: (sessionId: string, req: IncomingMessage, res: ServerResponse) => Promise<void>;
598
598
  handleCancel: (sessionId: string, res: ServerResponse) => void;
599
+ handleStopSession: (sessionId: string, res: ServerResponse) => Promise<void>;
599
600
  handleSetSessionName: (sessionId: string, req: IncomingMessage, res: ServerResponse) => Promise<void>;
600
601
  handleUploadFile: (sessionId: string, req: IncomingMessage, res: ServerResponse) => Promise<void>;
601
602
  handleAdopt: (sessionId: string, res: ServerResponse) => Promise<void>;
@@ -725,6 +726,7 @@ declare class StreamerServer {
725
726
  private log;
726
727
  private agentConfig;
727
728
  private agentClient;
729
+ private sessionStatusBus;
728
730
  constructor(config: ServerConfig & {
729
731
  apiKey: string;
730
732
  });
@@ -771,6 +773,7 @@ declare class StreamerServer {
771
773
  private handleUploadFile;
772
774
  private handleGetOutput;
773
775
  private handleCancel;
776
+ private handleStopSession;
774
777
  private handleAdopt;
775
778
  private handleStartSession;
776
779
  private linkSessionToProject;
package/dist/index.js CHANGED
@@ -1189,6 +1189,7 @@ import {
1189
1189
  ConversationScanner,
1190
1190
  search
1191
1191
  } from "@threadbase-sh/scanner";
1192
+ import { EventEmitter } from "events";
1192
1193
  import {
1193
1194
  createReadStream,
1194
1195
  existsSync as existsSync6,
@@ -1561,7 +1562,7 @@ var createConversationRoutes = (deps) => {
1561
1562
  import { Hono as Hono4 } from "hono";
1562
1563
 
1563
1564
  // src/version.ts
1564
- import { readFileSync as readFileSync3 } from "fs";
1565
+ import { readFileSync as readFileSync3, realpathSync } from "fs";
1565
1566
  import { dirname as dirname4, join as join6 } from "path";
1566
1567
  var cached;
1567
1568
  function getVersion() {
@@ -1572,7 +1573,13 @@ function getVersion() {
1572
1573
  function resolveVersion() {
1573
1574
  const scriptPath = process.argv[1] ?? "";
1574
1575
  const here = scriptPath ? dirname4(scriptPath) : process.cwd();
1575
- for (const dir of [here, join6(here, "..")]) {
1576
+ let realHere = here;
1577
+ try {
1578
+ realHere = dirname4(realpathSync(scriptPath));
1579
+ } catch {
1580
+ }
1581
+ const searchDirs = realHere === here ? [here, join6(here, "..")] : [here, join6(here, ".."), realHere, join6(realHere, "..")];
1582
+ for (const dir of searchDirs) {
1576
1583
  try {
1577
1584
  const v = readFileSync3(join6(dir, "version.txt"), "utf8").trim();
1578
1585
  if (v) return v;
@@ -1839,6 +1846,10 @@ var createSessionRoutes = (deps) => {
1839
1846
  await deps.handleAdopt(c.req.param("id"), c.env.outgoing);
1840
1847
  return alreadyHandled6();
1841
1848
  });
1849
+ app.post("/:id/stop", async (c) => {
1850
+ await deps.handleStopSession(c.req.param("id"), c.env.outgoing);
1851
+ return alreadyHandled6();
1852
+ });
1842
1853
  app.get("/:id", (c) => {
1843
1854
  deps.handleGetSession(c.req.param("id"), c.env.outgoing);
1844
1855
  return alreadyHandled6();
@@ -3921,7 +3932,9 @@ var StreamerServer = class {
3921
3932
  log = getLogger("server");
3922
3933
  agentConfig;
3923
3934
  agentClient = null;
3935
+ sessionStatusBus = new EventEmitter();
3924
3936
  constructor(config) {
3937
+ this.sessionStatusBus.setMaxListeners(0);
3925
3938
  this.apiKey = config.apiKey;
3926
3939
  this.localNoAuth = config.localNoAuth ?? false;
3927
3940
  this.verbose = config.verbose ?? false;
@@ -4038,6 +4051,7 @@ var StreamerServer = class {
4038
4051
  if (resp) {
4039
4052
  this.wsHub.broadcast({ type: "session_update", session: resp });
4040
4053
  }
4054
+ this.sessionStatusBus.emit(`status:${session.id}`, session.status);
4041
4055
  }
4042
4056
  });
4043
4057
  this.agentConfig = readAgentConfig();
@@ -4083,6 +4097,7 @@ var StreamerServer = class {
4083
4097
  handleGetOutput: (id, res) => this.handleGetOutput(id, res),
4084
4098
  handleSendInput: (id, req, res) => this.handleSendInput(id, req, res),
4085
4099
  handleCancel: (id, res) => this.handleCancel(id, res),
4100
+ handleStopSession: (id, res) => this.handleStopSession(id, res),
4086
4101
  handleSetSessionName: (id, req, res) => this.handleSetSessionName(id, req, res),
4087
4102
  handleUploadFile: (id, req, res) => this.handleUploadFile(id, req, res),
4088
4103
  handleAdopt: (id, res) => this.handleAdopt(id, res),
@@ -5261,6 +5276,54 @@ var StreamerServer = class {
5261
5276
  json(res, 400, { error: message });
5262
5277
  }
5263
5278
  }
5279
+ async handleStopSession(sessionId, res) {
5280
+ const STOP_TIMEOUT_MS = 5e3;
5281
+ const session = this.ptyManager.getSession(sessionId);
5282
+ if (!session) {
5283
+ res.writeHead(404, { "Content-Type": "application/json" });
5284
+ res.end(JSON.stringify({ error: "Session not found" }));
5285
+ return;
5286
+ }
5287
+ if (session.status === "idle") {
5288
+ res.writeHead(200, { "Content-Type": "application/json" });
5289
+ res.end(JSON.stringify({ status: "already_idle", sessionId }));
5290
+ return;
5291
+ }
5292
+ res.writeHead(200, {
5293
+ "Content-Type": "application/x-ndjson",
5294
+ "Transfer-Encoding": "chunked",
5295
+ "Cache-Control": "no-cache",
5296
+ "X-Accel-Buffering": "no"
5297
+ });
5298
+ res.write(`${JSON.stringify({ event: "stopping", sessionId })}
5299
+ `);
5300
+ const idlePromise = new Promise((resolve2) => {
5301
+ const handler = (status) => {
5302
+ if (status === "idle") {
5303
+ this.sessionStatusBus.off(`status:${sessionId}`, handler);
5304
+ resolve2("idle");
5305
+ }
5306
+ };
5307
+ this.sessionStatusBus.on(`status:${sessionId}`, handler);
5308
+ });
5309
+ const timeoutPromise = new Promise(
5310
+ (resolve2) => setTimeout(() => resolve2("timeout"), STOP_TIMEOUT_MS)
5311
+ );
5312
+ this.ptyManager.putOnHold(sessionId);
5313
+ this.discoveryCache = null;
5314
+ const outcome = await Promise.race([idlePromise, timeoutPromise]);
5315
+ if (outcome === "idle") {
5316
+ res.write(`${JSON.stringify({ event: "stopped", sessionId })}
5317
+ `);
5318
+ } else {
5319
+ res.write(`${JSON.stringify({ event: "timeout", sessionId })}
5320
+ `);
5321
+ this.log.warn(
5322
+ `[stop] session ${sessionId.slice(0, 8)} did not idle within ${STOP_TIMEOUT_MS}ms`
5323
+ );
5324
+ }
5325
+ res.end();
5326
+ }
5264
5327
  async handleAdopt(sessionId, res) {
5265
5328
  const discovered = await discoverClaudeProcesses();
5266
5329
  this.sessionStore.setDiscovered(discovered);