@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.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");
@@ -1605,7 +1606,13 @@ function getVersion() {
1605
1606
  function resolveVersion() {
1606
1607
  const scriptPath = process.argv[1] ?? "";
1607
1608
  const here = scriptPath ? (0, import_node_path3.dirname)(scriptPath) : process.cwd();
1608
- for (const dir of [here, (0, import_node_path3.join)(here, "..")]) {
1609
+ let realHere = here;
1610
+ try {
1611
+ realHere = (0, import_node_path3.dirname)((0, import_node_fs2.realpathSync)(scriptPath));
1612
+ } catch {
1613
+ }
1614
+ const searchDirs = realHere === here ? [here, (0, import_node_path3.join)(here, "..")] : [here, (0, import_node_path3.join)(here, ".."), realHere, (0, import_node_path3.join)(realHere, "..")];
1615
+ for (const dir of searchDirs) {
1609
1616
  try {
1610
1617
  const v = (0, import_node_fs2.readFileSync)((0, import_node_path3.join)(dir, "version.txt"), "utf8").trim();
1611
1618
  if (v) return v;
@@ -1872,6 +1879,10 @@ var createSessionRoutes = (deps) => {
1872
1879
  await deps.handleAdopt(c.req.param("id"), c.env.outgoing);
1873
1880
  return alreadyHandled6();
1874
1881
  });
1882
+ app.post("/:id/stop", async (c) => {
1883
+ await deps.handleStopSession(c.req.param("id"), c.env.outgoing);
1884
+ return alreadyHandled6();
1885
+ });
1875
1886
  app.get("/:id", (c) => {
1876
1887
  deps.handleGetSession(c.req.param("id"), c.env.outgoing);
1877
1888
  return alreadyHandled6();
@@ -3955,7 +3966,9 @@ var StreamerServer = class {
3955
3966
  log = getLogger("server");
3956
3967
  agentConfig;
3957
3968
  agentClient = null;
3969
+ sessionStatusBus = new import_events.EventEmitter();
3958
3970
  constructor(config) {
3971
+ this.sessionStatusBus.setMaxListeners(0);
3959
3972
  this.apiKey = config.apiKey;
3960
3973
  this.localNoAuth = config.localNoAuth ?? false;
3961
3974
  this.verbose = config.verbose ?? false;
@@ -4072,6 +4085,7 @@ var StreamerServer = class {
4072
4085
  if (resp) {
4073
4086
  this.wsHub.broadcast({ type: "session_update", session: resp });
4074
4087
  }
4088
+ this.sessionStatusBus.emit(`status:${session.id}`, session.status);
4075
4089
  }
4076
4090
  });
4077
4091
  this.agentConfig = readAgentConfig();
@@ -4117,6 +4131,7 @@ var StreamerServer = class {
4117
4131
  handleGetOutput: (id, res) => this.handleGetOutput(id, res),
4118
4132
  handleSendInput: (id, req, res) => this.handleSendInput(id, req, res),
4119
4133
  handleCancel: (id, res) => this.handleCancel(id, res),
4134
+ handleStopSession: (id, res) => this.handleStopSession(id, res),
4120
4135
  handleSetSessionName: (id, req, res) => this.handleSetSessionName(id, req, res),
4121
4136
  handleUploadFile: (id, req, res) => this.handleUploadFile(id, req, res),
4122
4137
  handleAdopt: (id, res) => this.handleAdopt(id, res),
@@ -5295,6 +5310,54 @@ var StreamerServer = class {
5295
5310
  json(res, 400, { error: message });
5296
5311
  }
5297
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
+ }
5298
5361
  async handleAdopt(sessionId, res) {
5299
5362
  const discovered = await discoverClaudeProcesses();
5300
5363
  this.sessionStore.setDiscovered(discovered);