@threadbase-sh/streamer 1.15.4 → 1.16.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.
- package/dist/cli.cjs +6156 -5671
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +78 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +78 -7
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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),
|
|
@@ -4631,22 +4640,36 @@ var StreamerServer = class {
|
|
|
4631
4640
|
async handleConversationsCount(url, res) {
|
|
4632
4641
|
const project = url.searchParams.get("project") ?? void 0;
|
|
4633
4642
|
const bustCache = url.searchParams.get("refresh") === "1";
|
|
4634
|
-
if (
|
|
4635
|
-
this.cache?.invalidate();
|
|
4636
|
-
this.scanner = null;
|
|
4637
|
-
this.scannerReady = null;
|
|
4638
|
-
}
|
|
4639
|
-
if (this.cache && !bustCache) {
|
|
4643
|
+
if (this.cache) {
|
|
4640
4644
|
const { total } = this.cache.listConversations({ project, limit: 0, offset: 0 });
|
|
4641
4645
|
json(res, 200, { total });
|
|
4646
|
+
if (bustCache) this.refreshCountInBackground();
|
|
4642
4647
|
return;
|
|
4643
4648
|
}
|
|
4644
|
-
const scanner = await this.getScanner();
|
|
4649
|
+
const scanner = await this.getScanner(true);
|
|
4645
4650
|
let metas = [...scanner.getMetadataCache().values()];
|
|
4646
4651
|
metas = (0, import_scanner2.applyIncludeFilter)(metas, "conversations");
|
|
4647
4652
|
if (project) metas = (0, import_scanner2.applyProjectFilter)(metas, project);
|
|
4648
4653
|
json(res, 200, { total: metas.length });
|
|
4649
4654
|
}
|
|
4655
|
+
// Fire-and-forget full rescan that reconciles the SQLite cache from disk so a
|
|
4656
|
+
// later count reflects new/removed conversations. Never awaited by the request
|
|
4657
|
+
// path — refresh=1 returns the cached total synchronously and this catches up.
|
|
4658
|
+
refreshCountInBackground() {
|
|
4659
|
+
void (async () => {
|
|
4660
|
+
try {
|
|
4661
|
+
const scanner = await this.getFreshScanner();
|
|
4662
|
+
if (this.cache) {
|
|
4663
|
+
this.cache.upsertFromScannerMeta([...scanner.getMetadataCache().values()]);
|
|
4664
|
+
}
|
|
4665
|
+
} catch (err) {
|
|
4666
|
+
this.log.warn(
|
|
4667
|
+
`Background count refresh failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
4668
|
+
{ event: "count.refresh_failed" }
|
|
4669
|
+
);
|
|
4670
|
+
}
|
|
4671
|
+
})();
|
|
4672
|
+
}
|
|
4650
4673
|
handleSessionsCount(res) {
|
|
4651
4674
|
json(res, 200, { total: this.sessionStore.list(this.ptyAttachedIds()).length });
|
|
4652
4675
|
}
|
|
@@ -5301,6 +5324,54 @@ var StreamerServer = class {
|
|
|
5301
5324
|
json(res, 400, { error: message });
|
|
5302
5325
|
}
|
|
5303
5326
|
}
|
|
5327
|
+
async handleStopSession(sessionId, res) {
|
|
5328
|
+
const STOP_TIMEOUT_MS = 5e3;
|
|
5329
|
+
const session = this.ptyManager.getSession(sessionId);
|
|
5330
|
+
if (!session) {
|
|
5331
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
5332
|
+
res.end(JSON.stringify({ error: "Session not found" }));
|
|
5333
|
+
return;
|
|
5334
|
+
}
|
|
5335
|
+
if (session.status === "idle") {
|
|
5336
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
5337
|
+
res.end(JSON.stringify({ status: "already_idle", sessionId }));
|
|
5338
|
+
return;
|
|
5339
|
+
}
|
|
5340
|
+
res.writeHead(200, {
|
|
5341
|
+
"Content-Type": "application/x-ndjson",
|
|
5342
|
+
"Transfer-Encoding": "chunked",
|
|
5343
|
+
"Cache-Control": "no-cache",
|
|
5344
|
+
"X-Accel-Buffering": "no"
|
|
5345
|
+
});
|
|
5346
|
+
res.write(`${JSON.stringify({ event: "stopping", sessionId })}
|
|
5347
|
+
`);
|
|
5348
|
+
const idlePromise = new Promise((resolve2) => {
|
|
5349
|
+
const handler = (status) => {
|
|
5350
|
+
if (status === "idle") {
|
|
5351
|
+
this.sessionStatusBus.off(`status:${sessionId}`, handler);
|
|
5352
|
+
resolve2("idle");
|
|
5353
|
+
}
|
|
5354
|
+
};
|
|
5355
|
+
this.sessionStatusBus.on(`status:${sessionId}`, handler);
|
|
5356
|
+
});
|
|
5357
|
+
const timeoutPromise = new Promise(
|
|
5358
|
+
(resolve2) => setTimeout(() => resolve2("timeout"), STOP_TIMEOUT_MS)
|
|
5359
|
+
);
|
|
5360
|
+
this.ptyManager.putOnHold(sessionId);
|
|
5361
|
+
this.discoveryCache = null;
|
|
5362
|
+
const outcome = await Promise.race([idlePromise, timeoutPromise]);
|
|
5363
|
+
if (outcome === "idle") {
|
|
5364
|
+
res.write(`${JSON.stringify({ event: "stopped", sessionId })}
|
|
5365
|
+
`);
|
|
5366
|
+
} else {
|
|
5367
|
+
res.write(`${JSON.stringify({ event: "timeout", sessionId })}
|
|
5368
|
+
`);
|
|
5369
|
+
this.log.warn(
|
|
5370
|
+
`[stop] session ${sessionId.slice(0, 8)} did not idle within ${STOP_TIMEOUT_MS}ms`
|
|
5371
|
+
);
|
|
5372
|
+
}
|
|
5373
|
+
res.end();
|
|
5374
|
+
}
|
|
5304
5375
|
async handleAdopt(sessionId, res) {
|
|
5305
5376
|
const discovered = await discoverClaudeProcesses();
|
|
5306
5377
|
this.sessionStore.setDiscovered(discovered);
|