@threadbase-sh/streamer 1.15.4 → 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.cjs CHANGED
@@ -1229,6 +1229,7 @@ function stripAnsi(str) {
1229
1229
  var import_node_ws = require("@hono/node-ws");
1230
1230
  var import_client = require("@temporalio/client");
1231
1231
  var import_scanner2 = require("@threadbase-sh/scanner");
1232
+ var import_events = require("events");
1232
1233
  var import_fs11 = require("fs");
1233
1234
  var import_promises5 = require("fs/promises");
1234
1235
  var import_http = require("http");
@@ -1878,6 +1879,10 @@ var createSessionRoutes = (deps) => {
1878
1879
  await deps.handleAdopt(c.req.param("id"), c.env.outgoing);
1879
1880
  return alreadyHandled6();
1880
1881
  });
1882
+ app.post("/:id/stop", async (c) => {
1883
+ await deps.handleStopSession(c.req.param("id"), c.env.outgoing);
1884
+ return alreadyHandled6();
1885
+ });
1881
1886
  app.get("/:id", (c) => {
1882
1887
  deps.handleGetSession(c.req.param("id"), c.env.outgoing);
1883
1888
  return alreadyHandled6();
@@ -3961,7 +3966,9 @@ var StreamerServer = class {
3961
3966
  log = getLogger("server");
3962
3967
  agentConfig;
3963
3968
  agentClient = null;
3969
+ sessionStatusBus = new import_events.EventEmitter();
3964
3970
  constructor(config) {
3971
+ this.sessionStatusBus.setMaxListeners(0);
3965
3972
  this.apiKey = config.apiKey;
3966
3973
  this.localNoAuth = config.localNoAuth ?? false;
3967
3974
  this.verbose = config.verbose ?? false;
@@ -4078,6 +4085,7 @@ var StreamerServer = class {
4078
4085
  if (resp) {
4079
4086
  this.wsHub.broadcast({ type: "session_update", session: resp });
4080
4087
  }
4088
+ this.sessionStatusBus.emit(`status:${session.id}`, session.status);
4081
4089
  }
4082
4090
  });
4083
4091
  this.agentConfig = readAgentConfig();
@@ -4123,6 +4131,7 @@ var StreamerServer = class {
4123
4131
  handleGetOutput: (id, res) => this.handleGetOutput(id, res),
4124
4132
  handleSendInput: (id, req, res) => this.handleSendInput(id, req, res),
4125
4133
  handleCancel: (id, res) => this.handleCancel(id, res),
4134
+ handleStopSession: (id, res) => this.handleStopSession(id, res),
4126
4135
  handleSetSessionName: (id, req, res) => this.handleSetSessionName(id, req, res),
4127
4136
  handleUploadFile: (id, req, res) => this.handleUploadFile(id, req, res),
4128
4137
  handleAdopt: (id, res) => this.handleAdopt(id, res),
@@ -5301,6 +5310,54 @@ var StreamerServer = class {
5301
5310
  json(res, 400, { error: message });
5302
5311
  }
5303
5312
  }
5313
+ async handleStopSession(sessionId, res) {
5314
+ const STOP_TIMEOUT_MS = 5e3;
5315
+ const session = this.ptyManager.getSession(sessionId);
5316
+ if (!session) {
5317
+ res.writeHead(404, { "Content-Type": "application/json" });
5318
+ res.end(JSON.stringify({ error: "Session not found" }));
5319
+ return;
5320
+ }
5321
+ if (session.status === "idle") {
5322
+ res.writeHead(200, { "Content-Type": "application/json" });
5323
+ res.end(JSON.stringify({ status: "already_idle", sessionId }));
5324
+ return;
5325
+ }
5326
+ res.writeHead(200, {
5327
+ "Content-Type": "application/x-ndjson",
5328
+ "Transfer-Encoding": "chunked",
5329
+ "Cache-Control": "no-cache",
5330
+ "X-Accel-Buffering": "no"
5331
+ });
5332
+ res.write(`${JSON.stringify({ event: "stopping", sessionId })}
5333
+ `);
5334
+ const idlePromise = new Promise((resolve2) => {
5335
+ const handler = (status) => {
5336
+ if (status === "idle") {
5337
+ this.sessionStatusBus.off(`status:${sessionId}`, handler);
5338
+ resolve2("idle");
5339
+ }
5340
+ };
5341
+ this.sessionStatusBus.on(`status:${sessionId}`, handler);
5342
+ });
5343
+ const timeoutPromise = new Promise(
5344
+ (resolve2) => setTimeout(() => resolve2("timeout"), STOP_TIMEOUT_MS)
5345
+ );
5346
+ this.ptyManager.putOnHold(sessionId);
5347
+ this.discoveryCache = null;
5348
+ const outcome = await Promise.race([idlePromise, timeoutPromise]);
5349
+ if (outcome === "idle") {
5350
+ res.write(`${JSON.stringify({ event: "stopped", sessionId })}
5351
+ `);
5352
+ } else {
5353
+ res.write(`${JSON.stringify({ event: "timeout", sessionId })}
5354
+ `);
5355
+ this.log.warn(
5356
+ `[stop] session ${sessionId.slice(0, 8)} did not idle within ${STOP_TIMEOUT_MS}ms`
5357
+ );
5358
+ }
5359
+ res.end();
5360
+ }
5304
5361
  async handleAdopt(sessionId, res) {
5305
5362
  const discovered = await discoverClaudeProcesses();
5306
5363
  this.sessionStore.setDiscovered(discovered);