@threadbase-sh/streamer 1.43.0 → 1.44.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/cli.cjs +1379 -483
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +853 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -4
- package/dist/index.d.ts +44 -4
- package/dist/index.js +894 -150
- package/dist/index.js.map +1 -1
- package/dist/launchd-entry.cjs +8 -5
- package/dist/launchd-entry.cjs.map +1 -1
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -4637,11 +4637,11 @@ var require_tools = __commonJS({
|
|
|
4637
4637
|
}
|
|
4638
4638
|
}
|
|
4639
4639
|
}
|
|
4640
|
-
function buildFormatters(level, bindings,
|
|
4640
|
+
function buildFormatters(level, bindings, log13) {
|
|
4641
4641
|
return {
|
|
4642
4642
|
level,
|
|
4643
4643
|
bindings,
|
|
4644
|
-
log:
|
|
4644
|
+
log: log13
|
|
4645
4645
|
};
|
|
4646
4646
|
}
|
|
4647
4647
|
function normalizeDestFileDescriptor(destination) {
|
|
@@ -5023,11 +5023,11 @@ var require_proto = __commonJS({
|
|
|
5023
5023
|
}
|
|
5024
5024
|
} else instance[serializersSym] = serializers;
|
|
5025
5025
|
if (options.hasOwnProperty("formatters")) {
|
|
5026
|
-
const { level, bindings: chindings, log:
|
|
5026
|
+
const { level, bindings: chindings, log: log13 } = options.formatters;
|
|
5027
5027
|
instance[formattersSym] = buildFormatters(
|
|
5028
5028
|
level || formatters.level,
|
|
5029
5029
|
chindings || resetChildingsFormatter,
|
|
5030
|
-
|
|
5030
|
+
log13 || formatters.log
|
|
5031
5031
|
);
|
|
5032
5032
|
} else {
|
|
5033
5033
|
instance[formattersSym] = buildFormatters(
|
|
@@ -6097,6 +6097,9 @@ var require_pino = __commonJS({
|
|
|
6097
6097
|
});
|
|
6098
6098
|
|
|
6099
6099
|
// src/logger.ts
|
|
6100
|
+
function defaultDest() {
|
|
6101
|
+
return process.stdout.isTTY ? "console" : "pino";
|
|
6102
|
+
}
|
|
6100
6103
|
function emit(pinoChild, level, msg, fields, dest) {
|
|
6101
6104
|
if (dest === "pino" || dest === "both") {
|
|
6102
6105
|
if (fields && Object.keys(fields).length > 0) pinoChild[level](fields, msg);
|
|
@@ -6109,11 +6112,11 @@ function emit(pinoChild, level, msg, fields, dest) {
|
|
|
6109
6112
|
}
|
|
6110
6113
|
function build(pinoChild) {
|
|
6111
6114
|
return {
|
|
6112
|
-
debug: (m2, f2, d =
|
|
6113
|
-
info: (m2, f2, d =
|
|
6114
|
-
warn: (m2, f2, d =
|
|
6115
|
-
error: (m2, f2, d =
|
|
6116
|
-
log: (lvl, m2, f2, d =
|
|
6115
|
+
debug: (m2, f2, d = defaultDest()) => emit(pinoChild, "debug", m2, f2, d),
|
|
6116
|
+
info: (m2, f2, d = defaultDest()) => emit(pinoChild, "info", m2, f2, d),
|
|
6117
|
+
warn: (m2, f2, d = defaultDest()) => emit(pinoChild, "warn", m2, f2, d),
|
|
6118
|
+
error: (m2, f2, d = defaultDest()) => emit(pinoChild, "error", m2, f2, d),
|
|
6119
|
+
log: (lvl, m2, f2, d = defaultDest()) => emit(pinoChild, lvl, m2, f2, d),
|
|
6117
6120
|
pino: pinoChild
|
|
6118
6121
|
};
|
|
6119
6122
|
}
|
|
@@ -6219,6 +6222,12 @@ var init_feature_flags = __esm({
|
|
|
6219
6222
|
description: "Seed the session list at boot with sessions a previous streamer run left behind, so a restart leaves them one tap from resuming instead of silently gone. On by default, with a kill switch: it changes what GET /api/sessions contains.",
|
|
6220
6223
|
default: true,
|
|
6221
6224
|
env: "THREADBASE_FEATURE_SESSION_REHYDRATION"
|
|
6225
|
+
},
|
|
6226
|
+
{
|
|
6227
|
+
id: "ptyHost",
|
|
6228
|
+
description: "Keep live PTYs in a separate host process so a streamer restart can reconnect without restarting the agents. Off by default until cross-platform behavior is qualified.",
|
|
6229
|
+
default: false,
|
|
6230
|
+
env: "THREADBASE_FEATURE_PTY_HOST"
|
|
6222
6231
|
}
|
|
6223
6232
|
];
|
|
6224
6233
|
}
|
|
@@ -8197,7 +8206,7 @@ var require_merge = __commonJS({
|
|
|
8197
8206
|
var require_addPairToJSMap = __commonJS({
|
|
8198
8207
|
"node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports2) {
|
|
8199
8208
|
"use strict";
|
|
8200
|
-
var
|
|
8209
|
+
var log13 = require_log();
|
|
8201
8210
|
var merge2 = require_merge();
|
|
8202
8211
|
var stringify = require_stringify();
|
|
8203
8212
|
var identity = require_identity();
|
|
@@ -8246,7 +8255,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
8246
8255
|
let jsonStr = JSON.stringify(strKey);
|
|
8247
8256
|
if (jsonStr.length > 40)
|
|
8248
8257
|
jsonStr = jsonStr.substring(0, 36) + '..."';
|
|
8249
|
-
|
|
8258
|
+
log13.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
|
|
8250
8259
|
ctx.mapKeyWarned = true;
|
|
8251
8260
|
}
|
|
8252
8261
|
return strKey;
|
|
@@ -13662,7 +13671,7 @@ var require_public_api = __commonJS({
|
|
|
13662
13671
|
var composer = require_composer();
|
|
13663
13672
|
var Document = require_Document();
|
|
13664
13673
|
var errors = require_errors();
|
|
13665
|
-
var
|
|
13674
|
+
var log13 = require_log();
|
|
13666
13675
|
var identity = require_identity();
|
|
13667
13676
|
var lineCounter = require_line_counter();
|
|
13668
13677
|
var parser = require_parser();
|
|
@@ -13714,7 +13723,7 @@ var require_public_api = __commonJS({
|
|
|
13714
13723
|
const doc = parseDocument(src, options);
|
|
13715
13724
|
if (!doc)
|
|
13716
13725
|
return null;
|
|
13717
|
-
doc.warnings.forEach((warning) =>
|
|
13726
|
+
doc.warnings.forEach((warning) => log13.warn(doc.options.logLevel, warning));
|
|
13718
13727
|
if (doc.errors.length > 0) {
|
|
13719
13728
|
if (doc.options.logLevel !== "silent")
|
|
13720
13729
|
throw doc.errors[0];
|
|
@@ -32964,11 +32973,11 @@ var require_type_helpers = __commonJS({
|
|
|
32964
32973
|
function isRecord(value) {
|
|
32965
32974
|
return typeof value === "object" && value !== null;
|
|
32966
32975
|
}
|
|
32967
|
-
function hasOwnProperty(
|
|
32968
|
-
return prop in
|
|
32976
|
+
function hasOwnProperty(record3, prop) {
|
|
32977
|
+
return prop in record3;
|
|
32969
32978
|
}
|
|
32970
|
-
function hasOwnProperties(
|
|
32971
|
-
return props.every((prop) => prop in
|
|
32979
|
+
function hasOwnProperties(record3, props) {
|
|
32980
|
+
return props.every((prop) => prop in record3);
|
|
32972
32981
|
}
|
|
32973
32982
|
function isError(error51) {
|
|
32974
32983
|
return isRecord(error51) && typeof error51.name === "string" && typeof error51.message === "string" && (error51.stack == null || typeof error51.stack === "string");
|
|
@@ -37230,7 +37239,7 @@ var require_logging = __commonJS({
|
|
|
37230
37239
|
_logVerbosity = verbosity;
|
|
37231
37240
|
};
|
|
37232
37241
|
exports2.setLoggerVerbosity = setLoggerVerbosity;
|
|
37233
|
-
var
|
|
37242
|
+
var log13 = (severity, ...args) => {
|
|
37234
37243
|
let logFunction;
|
|
37235
37244
|
if (severity >= _logVerbosity) {
|
|
37236
37245
|
switch (severity) {
|
|
@@ -37252,7 +37261,7 @@ var require_logging = __commonJS({
|
|
|
37252
37261
|
}
|
|
37253
37262
|
}
|
|
37254
37263
|
};
|
|
37255
|
-
exports2.log =
|
|
37264
|
+
exports2.log = log13;
|
|
37256
37265
|
var tracersString = (_d = (_c = process.env.GRPC_NODE_TRACE) !== null && _c !== void 0 ? _c : process.env.GRPC_TRACE) !== null && _d !== void 0 ? _d : "";
|
|
37257
37266
|
var enabledTracers = /* @__PURE__ */ new Set();
|
|
37258
37267
|
var disabledTracers = /* @__PURE__ */ new Set();
|
|
@@ -38663,9 +38672,9 @@ var require_service_config = __commonJS({
|
|
|
38663
38672
|
throw new Error("No matching service config found");
|
|
38664
38673
|
}
|
|
38665
38674
|
function extractAndSelectServiceConfig(txtRecord, percentage) {
|
|
38666
|
-
for (const
|
|
38667
|
-
if (
|
|
38668
|
-
const recordString =
|
|
38675
|
+
for (const record3 of txtRecord) {
|
|
38676
|
+
if (record3.length > 0 && record3[0].startsWith("grpc_config=")) {
|
|
38677
|
+
const recordString = record3.join("").substring("grpc_config=".length);
|
|
38669
38678
|
const recordJson = JSON.parse(recordString);
|
|
38670
38679
|
return validateAndSelectCanaryConfig(recordJson, percentage);
|
|
38671
38680
|
}
|
|
@@ -100690,11 +100699,11 @@ var require_out = __commonJS({
|
|
|
100690
100699
|
async.read(path2, getSettings(optionsOrSettingsOrCallback), callback);
|
|
100691
100700
|
}
|
|
100692
100701
|
exports2.stat = stat7;
|
|
100693
|
-
function
|
|
100702
|
+
function statSync15(path2, optionsOrSettings) {
|
|
100694
100703
|
const settings = getSettings(optionsOrSettings);
|
|
100695
100704
|
return sync.read(path2, settings);
|
|
100696
100705
|
}
|
|
100697
|
-
exports2.statSync =
|
|
100706
|
+
exports2.statSync = statSync15;
|
|
100698
100707
|
function getSettings(settingsOrOptions = {}) {
|
|
100699
100708
|
if (settingsOrOptions instanceof settings_1.default) {
|
|
100700
100709
|
return settingsOrOptions;
|
|
@@ -102581,6 +102590,14 @@ var require_out4 = __commonJS({
|
|
|
102581
102590
|
});
|
|
102582
102591
|
|
|
102583
102592
|
// src/platform.ts
|
|
102593
|
+
function isWindowsExecutablePath(path2) {
|
|
102594
|
+
const dot = path2.lastIndexOf(".");
|
|
102595
|
+
if (dot < 0) return false;
|
|
102596
|
+
return WINDOWS_EXECUTABLE_EXTENSIONS.has(path2.slice(dot).toLowerCase());
|
|
102597
|
+
}
|
|
102598
|
+
function clearClaudeExeCache() {
|
|
102599
|
+
_claudeExe = void 0;
|
|
102600
|
+
}
|
|
102584
102601
|
function resolveClaudeExe() {
|
|
102585
102602
|
if (_claudeExe !== void 0) return _claudeExe;
|
|
102586
102603
|
if (isWindows2) {
|
|
@@ -102589,7 +102606,7 @@ function resolveClaudeExe() {
|
|
|
102589
102606
|
encoding: "utf-8",
|
|
102590
102607
|
windowsHide: true,
|
|
102591
102608
|
timeout: 3e3
|
|
102592
|
-
}).trim().split("\n")
|
|
102609
|
+
}).trim().split("\n").map((line) => line.trim()).find(isWindowsExecutablePath);
|
|
102593
102610
|
if (found) {
|
|
102594
102611
|
_claudeExe = found;
|
|
102595
102612
|
return _claudeExe;
|
|
@@ -102638,6 +102655,9 @@ function resolveClaudeExe() {
|
|
|
102638
102655
|
_claudeExe = "claude";
|
|
102639
102656
|
return _claudeExe;
|
|
102640
102657
|
}
|
|
102658
|
+
function clearCodexExeCache() {
|
|
102659
|
+
_codexExe = void 0;
|
|
102660
|
+
}
|
|
102641
102661
|
function resolveCodexExe() {
|
|
102642
102662
|
if (_codexExe !== void 0) return _codexExe;
|
|
102643
102663
|
if (isWindows2) {
|
|
@@ -102646,7 +102666,7 @@ function resolveCodexExe() {
|
|
|
102646
102666
|
encoding: "utf-8",
|
|
102647
102667
|
windowsHide: true,
|
|
102648
102668
|
timeout: 3e3
|
|
102649
|
-
}).trim().split("\n")
|
|
102669
|
+
}).trim().split("\n").map((line) => line.trim()).find(isWindowsExecutablePath);
|
|
102650
102670
|
if (found) {
|
|
102651
102671
|
_codexExe = found;
|
|
102652
102672
|
return _codexExe;
|
|
@@ -102695,7 +102715,7 @@ function resolveCodexExe() {
|
|
|
102695
102715
|
_codexExe = "codex";
|
|
102696
102716
|
return _codexExe;
|
|
102697
102717
|
}
|
|
102698
|
-
var import_child_process, import_fs13, import_os6, import_path13, isWindows2, _claudeExe, _codexExe;
|
|
102718
|
+
var import_child_process, import_fs13, import_os6, import_path13, isWindows2, WINDOWS_EXECUTABLE_EXTENSIONS, _claudeExe, _codexExe;
|
|
102699
102719
|
var init_platform = __esm({
|
|
102700
102720
|
"src/platform.ts"() {
|
|
102701
102721
|
"use strict";
|
|
@@ -102704,6 +102724,7 @@ var init_platform = __esm({
|
|
|
102704
102724
|
import_os6 = require("os");
|
|
102705
102725
|
import_path13 = require("path");
|
|
102706
102726
|
isWindows2 = (0, import_os6.platform)() === "win32";
|
|
102727
|
+
WINDOWS_EXECUTABLE_EXTENSIONS = /* @__PURE__ */ new Set([".exe", ".cmd", ".bat"]);
|
|
102707
102728
|
}
|
|
102708
102729
|
});
|
|
102709
102730
|
|
|
@@ -113213,20 +113234,26 @@ var init_codex_pty_runner = __esm({
|
|
|
113213
113234
|
async doStart(sessionId, options) {
|
|
113214
113235
|
const nodePty = await loadPty();
|
|
113215
113236
|
const projectName = options.projectName ?? (0, import_path21.basename)(options.projectPath);
|
|
113216
|
-
|
|
113217
|
-
|
|
113218
|
-
|
|
113219
|
-
|
|
113220
|
-
|
|
113221
|
-
|
|
113222
|
-
|
|
113223
|
-
|
|
113224
|
-
|
|
113225
|
-
|
|
113226
|
-
|
|
113227
|
-
|
|
113228
|
-
|
|
113229
|
-
|
|
113237
|
+
let proc;
|
|
113238
|
+
try {
|
|
113239
|
+
proc = nodePty.spawn(
|
|
113240
|
+
resolveCodexExe(),
|
|
113241
|
+
// `sessionId` stays the runner's map key — only argv carries the
|
|
113242
|
+
// provider-side id, so a resumed Codex session keeps the placeholder id
|
|
113243
|
+
// its client already navigated to.
|
|
113244
|
+
["resume", options.resumeId ?? sessionId, "--cd", options.projectPath, "--no-alt-screen"],
|
|
113245
|
+
{
|
|
113246
|
+
name: "xterm-256color",
|
|
113247
|
+
cols: PTY_COLS,
|
|
113248
|
+
rows: PTY_ROWS,
|
|
113249
|
+
cwd: options.projectPath,
|
|
113250
|
+
env: process.env
|
|
113251
|
+
}
|
|
113252
|
+
);
|
|
113253
|
+
} catch (err) {
|
|
113254
|
+
clearCodexExeCache();
|
|
113255
|
+
throw err;
|
|
113256
|
+
}
|
|
113230
113257
|
const session = {
|
|
113231
113258
|
id: sessionId,
|
|
113232
113259
|
provider: CODEX_CLI_PROVIDER2,
|
|
@@ -113269,13 +113296,19 @@ var init_codex_pty_runner = __esm({
|
|
|
113269
113296
|
if (options.systemPrompt) {
|
|
113270
113297
|
args.push(options.systemPrompt);
|
|
113271
113298
|
}
|
|
113272
|
-
|
|
113273
|
-
|
|
113274
|
-
|
|
113275
|
-
|
|
113276
|
-
|
|
113277
|
-
|
|
113278
|
-
|
|
113299
|
+
let proc;
|
|
113300
|
+
try {
|
|
113301
|
+
proc = nodePty.spawn(resolveCodexExe(), args, {
|
|
113302
|
+
name: "xterm-256color",
|
|
113303
|
+
cols: PTY_COLS,
|
|
113304
|
+
rows: PTY_ROWS,
|
|
113305
|
+
cwd: options.projectPath,
|
|
113306
|
+
env: process.env
|
|
113307
|
+
});
|
|
113308
|
+
} catch (err) {
|
|
113309
|
+
clearCodexExeCache();
|
|
113310
|
+
throw err;
|
|
113311
|
+
}
|
|
113279
113312
|
const session = {
|
|
113280
113313
|
id: sessionId,
|
|
113281
113314
|
provider: CODEX_CLI_PROVIDER2,
|
|
@@ -113730,6 +113763,394 @@ var init_codex_pty_runner = __esm({
|
|
|
113730
113763
|
}
|
|
113731
113764
|
});
|
|
113732
113765
|
|
|
113766
|
+
// src/pty-host/protocol.ts
|
|
113767
|
+
function isHostEvent(message) {
|
|
113768
|
+
return "type" in message && message.type === "event";
|
|
113769
|
+
}
|
|
113770
|
+
function reviveSession(raw2) {
|
|
113771
|
+
const s3 = { ...raw2 };
|
|
113772
|
+
for (const field of SESSION_DATE_FIELDS) {
|
|
113773
|
+
const value = s3[field];
|
|
113774
|
+
if (typeof value === "string") s3[field] = new Date(value);
|
|
113775
|
+
}
|
|
113776
|
+
return s3;
|
|
113777
|
+
}
|
|
113778
|
+
function encodeMessage(message) {
|
|
113779
|
+
return `${JSON.stringify(message)}
|
|
113780
|
+
`;
|
|
113781
|
+
}
|
|
113782
|
+
var PTY_HOST_PROTOCOL_VERSION, SESSION_DATE_FIELDS, LineDecoder;
|
|
113783
|
+
var init_protocol = __esm({
|
|
113784
|
+
"src/pty-host/protocol.ts"() {
|
|
113785
|
+
"use strict";
|
|
113786
|
+
PTY_HOST_PROTOCOL_VERSION = 2;
|
|
113787
|
+
SESSION_DATE_FIELDS = [
|
|
113788
|
+
"startedAt",
|
|
113789
|
+
"completedAt",
|
|
113790
|
+
"statusUpdatedAt",
|
|
113791
|
+
"lastActivityAt",
|
|
113792
|
+
"firstMessageAt",
|
|
113793
|
+
"lastMessageAt"
|
|
113794
|
+
];
|
|
113795
|
+
LineDecoder = class {
|
|
113796
|
+
buffer = "";
|
|
113797
|
+
push(chunk) {
|
|
113798
|
+
this.buffer += chunk;
|
|
113799
|
+
const lines = this.buffer.split("\n");
|
|
113800
|
+
this.buffer = lines.pop() ?? "";
|
|
113801
|
+
return lines.filter((line) => line.length > 0);
|
|
113802
|
+
}
|
|
113803
|
+
};
|
|
113804
|
+
}
|
|
113805
|
+
});
|
|
113806
|
+
|
|
113807
|
+
// src/pty-host/remote-session-runner.ts
|
|
113808
|
+
var PtyHostProtocolMismatchError, HOST_HEARTBEAT_INTERVAL_MS, HOST_HEARTBEAT_REQUEST_TIMEOUT_MS, HOST_SHUTDOWN_REQUEST_TIMEOUT_MS, RemoteSessionRunner;
|
|
113809
|
+
var init_remote_session_runner = __esm({
|
|
113810
|
+
"src/pty-host/remote-session-runner.ts"() {
|
|
113811
|
+
"use strict";
|
|
113812
|
+
init_protocol();
|
|
113813
|
+
PtyHostProtocolMismatchError = class extends Error {
|
|
113814
|
+
constructor(hostVersion, streamerVersion) {
|
|
113815
|
+
super(
|
|
113816
|
+
`pty-host protocol ${hostVersion} is incompatible with streamer protocol ${streamerVersion}`
|
|
113817
|
+
);
|
|
113818
|
+
this.hostVersion = hostVersion;
|
|
113819
|
+
this.streamerVersion = streamerVersion;
|
|
113820
|
+
this.name = "PtyHostProtocolMismatchError";
|
|
113821
|
+
}
|
|
113822
|
+
hostVersion;
|
|
113823
|
+
streamerVersion;
|
|
113824
|
+
};
|
|
113825
|
+
HOST_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
113826
|
+
HOST_HEARTBEAT_REQUEST_TIMEOUT_MS = 5e3;
|
|
113827
|
+
HOST_SHUTDOWN_REQUEST_TIMEOUT_MS = 1e3;
|
|
113828
|
+
RemoteSessionRunner = class _RemoteSessionRunner {
|
|
113829
|
+
transport;
|
|
113830
|
+
options;
|
|
113831
|
+
decoder = new LineDecoder();
|
|
113832
|
+
nextRequestId = 1;
|
|
113833
|
+
pending = /* @__PURE__ */ new Map();
|
|
113834
|
+
/** The mirror. Rebuilt wholesale by `status`, patched by events. */
|
|
113835
|
+
sessions = /* @__PURE__ */ new Map();
|
|
113836
|
+
/** Ring buffers, fed by `output` events so `getOutput` stays synchronous. */
|
|
113837
|
+
output = /* @__PURE__ */ new Map();
|
|
113838
|
+
inputHistory = /* @__PURE__ */ new Map();
|
|
113839
|
+
/** Fixed for a session's lifetime, so only spawn and status carry it. */
|
|
113840
|
+
pids = /* @__PURE__ */ new Map();
|
|
113841
|
+
closed = false;
|
|
113842
|
+
heartbeatTimer = null;
|
|
113843
|
+
heartbeatInFlight = false;
|
|
113844
|
+
/**
|
|
113845
|
+
* The only supported way to build one: a runner whose mirror has not been
|
|
113846
|
+
* seeded yet would answer `hasSession` with a confident, wrong `false` for
|
|
113847
|
+
* every session the host is holding — which reads as "the agent is gone" and
|
|
113848
|
+
* routes the user to start a new one.
|
|
113849
|
+
*/
|
|
113850
|
+
static async connect(transport, options = {}) {
|
|
113851
|
+
const runner = new _RemoteSessionRunner(transport, options);
|
|
113852
|
+
const status = await runner.readStatus();
|
|
113853
|
+
if (status.protocolVersion !== PTY_HOST_PROTOCOL_VERSION) {
|
|
113854
|
+
try {
|
|
113855
|
+
await runner.request({ type: "shutdown-host" }, HOST_SHUTDOWN_REQUEST_TIMEOUT_MS);
|
|
113856
|
+
} catch (err) {
|
|
113857
|
+
options.logger?.warn("[pty-host] incompatible host did not acknowledge shutdown", {
|
|
113858
|
+
event: "pty_host.shutdown_failed",
|
|
113859
|
+
err
|
|
113860
|
+
});
|
|
113861
|
+
} finally {
|
|
113862
|
+
runner.dispose();
|
|
113863
|
+
}
|
|
113864
|
+
throw new PtyHostProtocolMismatchError(status.protocolVersion, PTY_HOST_PROTOCOL_VERSION);
|
|
113865
|
+
}
|
|
113866
|
+
await runner.request({ type: "subscribe" });
|
|
113867
|
+
runner.refreshMirror(status);
|
|
113868
|
+
return runner;
|
|
113869
|
+
}
|
|
113870
|
+
constructor(transport, options) {
|
|
113871
|
+
this.transport = transport;
|
|
113872
|
+
this.options = options;
|
|
113873
|
+
transport.onLine((line) => this.handleLine(line));
|
|
113874
|
+
transport.onClose(() => this.handleClose());
|
|
113875
|
+
}
|
|
113876
|
+
// ─── Transport plumbing ──────────────────────────────────────────
|
|
113877
|
+
handleLine(line) {
|
|
113878
|
+
for (const complete of this.decoder.push(line)) {
|
|
113879
|
+
let message;
|
|
113880
|
+
try {
|
|
113881
|
+
message = JSON.parse(complete);
|
|
113882
|
+
} catch {
|
|
113883
|
+
this.options.logger?.warn("[pty-host] dropped unparseable message", {
|
|
113884
|
+
event: "pty_host.bad_message"
|
|
113885
|
+
});
|
|
113886
|
+
continue;
|
|
113887
|
+
}
|
|
113888
|
+
if (isHostEvent(message)) {
|
|
113889
|
+
this.handleEvent(message);
|
|
113890
|
+
continue;
|
|
113891
|
+
}
|
|
113892
|
+
const waiter = this.pending.get(message.id);
|
|
113893
|
+
if (!waiter) continue;
|
|
113894
|
+
this.pending.delete(message.id);
|
|
113895
|
+
if (waiter.timeout) clearTimeout(waiter.timeout);
|
|
113896
|
+
if (message.ok) waiter.resolve(message.result);
|
|
113897
|
+
else waiter.reject(new Error(message.error));
|
|
113898
|
+
}
|
|
113899
|
+
}
|
|
113900
|
+
/**
|
|
113901
|
+
* Fail every in-flight request when the socket drops.
|
|
113902
|
+
*
|
|
113903
|
+
* Without this each one stays pending forever and the caller — a session
|
|
113904
|
+
* start, an input write — hangs rather than erroring. PR 9 adds reconnection;
|
|
113905
|
+
* until then a dropped host is a hard failure that says so.
|
|
113906
|
+
*/
|
|
113907
|
+
handleClose() {
|
|
113908
|
+
if (this.closed) return;
|
|
113909
|
+
this.closed = true;
|
|
113910
|
+
this.stopHeartbeat();
|
|
113911
|
+
const err = new Error("pty-host connection closed");
|
|
113912
|
+
for (const waiter of this.pending.values()) {
|
|
113913
|
+
if (waiter.timeout) clearTimeout(waiter.timeout);
|
|
113914
|
+
waiter.reject(err);
|
|
113915
|
+
}
|
|
113916
|
+
this.pending.clear();
|
|
113917
|
+
}
|
|
113918
|
+
request(body, timeoutMs) {
|
|
113919
|
+
if (this.closed) return Promise.reject(new Error("pty-host connection closed"));
|
|
113920
|
+
const id = this.nextRequestId++;
|
|
113921
|
+
return new Promise((resolve4, reject) => {
|
|
113922
|
+
const timeout = timeoutMs === void 0 ? null : setTimeout(() => {
|
|
113923
|
+
if (!this.pending.delete(id)) return;
|
|
113924
|
+
reject(new Error(`${body.type} timed out after ${timeoutMs}ms`));
|
|
113925
|
+
}, timeoutMs);
|
|
113926
|
+
timeout?.unref?.();
|
|
113927
|
+
this.pending.set(id, { resolve: resolve4, reject, timeout });
|
|
113928
|
+
this.transport.send(encodeMessage({ ...body, id }));
|
|
113929
|
+
});
|
|
113930
|
+
}
|
|
113931
|
+
/**
|
|
113932
|
+
* Fire-and-forget for the synchronous parts of `SessionRunner`.
|
|
113933
|
+
*
|
|
113934
|
+
* `sendKeys`, `cancel`, `killPid` and `putOnHold` all return void, so there is
|
|
113935
|
+
* no channel to report a failure through even if we waited for one. The
|
|
113936
|
+
* response is still consumed — an unhandled rejection would take the process
|
|
113937
|
+
* down over a keystroke that failed to land.
|
|
113938
|
+
*/
|
|
113939
|
+
fireAndForget(body) {
|
|
113940
|
+
this.request(body).catch((err) => {
|
|
113941
|
+
this.options.logger?.warn("[pty-host] request failed", {
|
|
113942
|
+
event: "pty_host.request_failed",
|
|
113943
|
+
type: body.type,
|
|
113944
|
+
err
|
|
113945
|
+
});
|
|
113946
|
+
});
|
|
113947
|
+
}
|
|
113948
|
+
async readStatus() {
|
|
113949
|
+
return await this.request({ type: "status" });
|
|
113950
|
+
}
|
|
113951
|
+
refreshMirror(status) {
|
|
113952
|
+
this.sessions = /* @__PURE__ */ new Map();
|
|
113953
|
+
this.pids = /* @__PURE__ */ new Map();
|
|
113954
|
+
for (const entry of status.sessions) {
|
|
113955
|
+
const session = reviveSession(entry.session);
|
|
113956
|
+
this.sessions.set(session.id, session);
|
|
113957
|
+
this.pids.set(session.id, entry.pid);
|
|
113958
|
+
}
|
|
113959
|
+
}
|
|
113960
|
+
async heartbeat(state, timeoutMs = HOST_HEARTBEAT_REQUEST_TIMEOUT_MS) {
|
|
113961
|
+
await this.request({ type: "heartbeat", ...state }, timeoutMs);
|
|
113962
|
+
}
|
|
113963
|
+
startHeartbeat(getState, intervalMs = HOST_HEARTBEAT_INTERVAL_MS) {
|
|
113964
|
+
this.stopHeartbeat();
|
|
113965
|
+
const send = () => {
|
|
113966
|
+
if (this.closed || this.heartbeatInFlight) return;
|
|
113967
|
+
this.heartbeatInFlight = true;
|
|
113968
|
+
void Promise.resolve().then(() => this.heartbeat(getState())).catch((err) => {
|
|
113969
|
+
if (this.closed) return;
|
|
113970
|
+
this.options.logger?.warn("[pty-host] heartbeat failed", {
|
|
113971
|
+
event: "pty_host.heartbeat_failed",
|
|
113972
|
+
err
|
|
113973
|
+
});
|
|
113974
|
+
}).finally(() => {
|
|
113975
|
+
this.heartbeatInFlight = false;
|
|
113976
|
+
});
|
|
113977
|
+
};
|
|
113978
|
+
send();
|
|
113979
|
+
this.heartbeatTimer = setInterval(send, intervalMs);
|
|
113980
|
+
this.heartbeatTimer.unref?.();
|
|
113981
|
+
}
|
|
113982
|
+
stopHeartbeat() {
|
|
113983
|
+
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
113984
|
+
this.heartbeatTimer = null;
|
|
113985
|
+
}
|
|
113986
|
+
// ─── Events ──────────────────────────────────────────────────────
|
|
113987
|
+
handleEvent(event) {
|
|
113988
|
+
switch (event.event) {
|
|
113989
|
+
case "output": {
|
|
113990
|
+
this.output.set(event.sessionId, (this.output.get(event.sessionId) ?? "") + event.data);
|
|
113991
|
+
this.options.onOutput?.(event.sessionId, event.data);
|
|
113992
|
+
break;
|
|
113993
|
+
}
|
|
113994
|
+
case "status-change": {
|
|
113995
|
+
const session = reviveSession(event.session);
|
|
113996
|
+
if (session.status === "idle" && session.completedAt != null) {
|
|
113997
|
+
this.sessions.delete(session.id);
|
|
113998
|
+
this.pids.delete(session.id);
|
|
113999
|
+
this.output.delete(session.id);
|
|
114000
|
+
this.inputHistory.delete(session.id);
|
|
114001
|
+
} else {
|
|
114002
|
+
this.sessions.set(session.id, session);
|
|
114003
|
+
}
|
|
114004
|
+
this.options.onStatusChange?.(session);
|
|
114005
|
+
break;
|
|
114006
|
+
}
|
|
114007
|
+
case "ready": {
|
|
114008
|
+
const session = reviveSession(event.session);
|
|
114009
|
+
this.sessions.set(session.id, session);
|
|
114010
|
+
this.options.onReady?.(session);
|
|
114011
|
+
break;
|
|
114012
|
+
}
|
|
114013
|
+
case "permission-change":
|
|
114014
|
+
this.options.onPermissionChange?.(event.sessionId, event.gate);
|
|
114015
|
+
break;
|
|
114016
|
+
case "live-question":
|
|
114017
|
+
this.options.onLiveQuestion?.(event.sessionId, event.questions);
|
|
114018
|
+
break;
|
|
114019
|
+
case "live-question-gone":
|
|
114020
|
+
this.options.onLiveQuestionGone?.(event.sessionId);
|
|
114021
|
+
break;
|
|
114022
|
+
case "user-message": {
|
|
114023
|
+
const history = this.inputHistory.get(event.sessionId) ?? [];
|
|
114024
|
+
history.push({ text: event.text, ts: event.ts });
|
|
114025
|
+
this.inputHistory.set(event.sessionId, history);
|
|
114026
|
+
this.options.onUserMessage?.(event.sessionId, event.text, event.ts);
|
|
114027
|
+
break;
|
|
114028
|
+
}
|
|
114029
|
+
case "exit": {
|
|
114030
|
+
this.sessions.delete(event.sessionId);
|
|
114031
|
+
this.pids.delete(event.sessionId);
|
|
114032
|
+
this.output.delete(event.sessionId);
|
|
114033
|
+
this.inputHistory.delete(event.sessionId);
|
|
114034
|
+
break;
|
|
114035
|
+
}
|
|
114036
|
+
}
|
|
114037
|
+
}
|
|
114038
|
+
// ─── SessionRunner ───────────────────────────────────────────────
|
|
114039
|
+
async start(sessionId, options) {
|
|
114040
|
+
const provider = options.provider ?? "claude-code";
|
|
114041
|
+
return this.adopt(await this.request({ type: "spawn", provider, sessionId, options }));
|
|
114042
|
+
}
|
|
114043
|
+
async startFresh(options) {
|
|
114044
|
+
const provider = options.provider ?? "claude-code";
|
|
114045
|
+
return this.adopt(await this.request({ type: "spawn", provider, sessionId: null, options }));
|
|
114046
|
+
}
|
|
114047
|
+
/**
|
|
114048
|
+
* Take a spawn answer into the mirror.
|
|
114049
|
+
*
|
|
114050
|
+
* The pid lands here and nowhere else in the live path: `recordSessionSpawn`
|
|
114051
|
+
* reads it immediately after start to write the durable registry row, and a
|
|
114052
|
+
* null there costs the next boot its ability to probe whether the agent
|
|
114053
|
+
* outlived us.
|
|
114054
|
+
*/
|
|
114055
|
+
adopt(raw2) {
|
|
114056
|
+
const entry = raw2;
|
|
114057
|
+
const session = reviveSession(entry.session);
|
|
114058
|
+
this.sessions.set(session.id, session);
|
|
114059
|
+
this.pids.set(session.id, entry.pid);
|
|
114060
|
+
return session;
|
|
114061
|
+
}
|
|
114062
|
+
/**
|
|
114063
|
+
* Returns the mirror's promptCount, optimistically incremented.
|
|
114064
|
+
*
|
|
114065
|
+
* The interface is synchronous, so there is no way to return the host's
|
|
114066
|
+
* authoritative count. The increment matches what an in-process runner does
|
|
114067
|
+
* for the same call, and the next `status-change` event overwrites it — so a
|
|
114068
|
+
* mirror that guessed wrong is corrected within one round trip rather than
|
|
114069
|
+
* drifting.
|
|
114070
|
+
*/
|
|
114071
|
+
sendInput(sessionId, input) {
|
|
114072
|
+
const session = this.requireSession(sessionId);
|
|
114073
|
+
this.fireAndForget({ type: "write", sessionId, input });
|
|
114074
|
+
session.promptCount += 1;
|
|
114075
|
+
return session.promptCount;
|
|
114076
|
+
}
|
|
114077
|
+
sendKeys(sessionId, keys) {
|
|
114078
|
+
this.requireSession(sessionId);
|
|
114079
|
+
this.fireAndForget({ type: "keys", sessionId, keys });
|
|
114080
|
+
}
|
|
114081
|
+
cancel(sessionId) {
|
|
114082
|
+
this.fireAndForget({ type: "cancel", sessionId });
|
|
114083
|
+
}
|
|
114084
|
+
killPid(pid) {
|
|
114085
|
+
this.fireAndForget({ type: "kill", pid });
|
|
114086
|
+
}
|
|
114087
|
+
putOnHold(sessionId) {
|
|
114088
|
+
this.fireAndForget({ type: "kill", sessionId, hold: true });
|
|
114089
|
+
this.sessions.delete(sessionId);
|
|
114090
|
+
this.pids.delete(sessionId);
|
|
114091
|
+
}
|
|
114092
|
+
getOutput(sessionId) {
|
|
114093
|
+
this.requireSession(sessionId);
|
|
114094
|
+
return this.output.get(sessionId) ?? "";
|
|
114095
|
+
}
|
|
114096
|
+
async getOutputLines(sessionId, maxLines) {
|
|
114097
|
+
const result = await this.request({ type: "replay", sessionId, maxLines });
|
|
114098
|
+
if (typeof result.output === "string") this.output.set(sessionId, result.output);
|
|
114099
|
+
return result.lines;
|
|
114100
|
+
}
|
|
114101
|
+
/**
|
|
114102
|
+
* Synchronous, so it answers from the mirror rather than the host.
|
|
114103
|
+
*
|
|
114104
|
+
* Seeded lazily: `user-message` events append as they arrive, and a session
|
|
114105
|
+
* this streamer did not start has none until `hydrateInputHistory` fetches
|
|
114106
|
+
* them. Empty is the same answer an in-process runner gives for an unknown
|
|
114107
|
+
* session, so a caller cannot tell "none yet" from "not fetched" — which is
|
|
114108
|
+
* why the fetch is explicit rather than hidden behind this getter.
|
|
114109
|
+
*/
|
|
114110
|
+
getInputHistory(sessionId) {
|
|
114111
|
+
return this.inputHistory.get(sessionId) ?? [];
|
|
114112
|
+
}
|
|
114113
|
+
/** Pull a session's recorded messages from the host into the mirror. */
|
|
114114
|
+
async hydrateInputHistory(sessionId) {
|
|
114115
|
+
const result = await this.request({
|
|
114116
|
+
type: "input-history",
|
|
114117
|
+
sessionId
|
|
114118
|
+
});
|
|
114119
|
+
this.inputHistory.set(sessionId, result.history);
|
|
114120
|
+
return result.history;
|
|
114121
|
+
}
|
|
114122
|
+
getPid(sessionId) {
|
|
114123
|
+
return this.pids.get(sessionId) ?? null;
|
|
114124
|
+
}
|
|
114125
|
+
getSession(sessionId) {
|
|
114126
|
+
return this.sessions.get(sessionId) ?? null;
|
|
114127
|
+
}
|
|
114128
|
+
hasSession(sessionId) {
|
|
114129
|
+
return this.sessions.has(sessionId);
|
|
114130
|
+
}
|
|
114131
|
+
listSessions() {
|
|
114132
|
+
return [...this.sessions.values()];
|
|
114133
|
+
}
|
|
114134
|
+
/**
|
|
114135
|
+
* Drops this streamer's connection and nothing else.
|
|
114136
|
+
*
|
|
114137
|
+
* Emphatically NOT the in-process `dispose()`, which signals every child. The
|
|
114138
|
+
* entire point of the host is that its PTYs outlive the streamer, so tearing
|
|
114139
|
+
* them down here would spend the feature to implement a method name.
|
|
114140
|
+
*/
|
|
114141
|
+
dispose() {
|
|
114142
|
+
this.handleClose();
|
|
114143
|
+
this.transport.close();
|
|
114144
|
+
}
|
|
114145
|
+
requireSession(sessionId) {
|
|
114146
|
+
const session = this.sessions.get(sessionId);
|
|
114147
|
+
if (!session) throw new Error(`Session not found: ${sessionId}`);
|
|
114148
|
+
return session;
|
|
114149
|
+
}
|
|
114150
|
+
};
|
|
114151
|
+
}
|
|
114152
|
+
});
|
|
114153
|
+
|
|
113733
114154
|
// src/services/questions/detectPermissionGate.ts
|
|
113734
114155
|
function permissionContentKey(gate) {
|
|
113735
114156
|
return `${gate.prompt ?? ""}::${gate.detail ?? ""}::${gate.options.map((o) => `${o.index}.${o.label}`).join(",")}::${gate.cursor ?? ""}`;
|
|
@@ -114159,13 +114580,19 @@ var init_pty_manager = __esm({
|
|
|
114159
114580
|
sessionId
|
|
114160
114581
|
];
|
|
114161
114582
|
args.push(...buildFlagArgs(options.claudeFlags, options.claudeExtraArgs));
|
|
114162
|
-
|
|
114163
|
-
|
|
114164
|
-
|
|
114165
|
-
|
|
114166
|
-
|
|
114167
|
-
|
|
114168
|
-
|
|
114583
|
+
let proc;
|
|
114584
|
+
try {
|
|
114585
|
+
proc = nodePty.spawn(resolveClaudeExe(), args, {
|
|
114586
|
+
name: "xterm-256color",
|
|
114587
|
+
cols: 120,
|
|
114588
|
+
rows: 40,
|
|
114589
|
+
cwd: options.projectPath,
|
|
114590
|
+
env: buildSpawnEnv()
|
|
114591
|
+
});
|
|
114592
|
+
} catch (err) {
|
|
114593
|
+
clearClaudeExeCache();
|
|
114594
|
+
throw err;
|
|
114595
|
+
}
|
|
114169
114596
|
const session = {
|
|
114170
114597
|
id: sessionId,
|
|
114171
114598
|
provider: CLAUDE_CODE_PROVIDER2,
|
|
@@ -114220,13 +114647,19 @@ var init_pty_manager = __esm({
|
|
|
114220
114647
|
args.push("--system-prompt", options.systemPrompt);
|
|
114221
114648
|
}
|
|
114222
114649
|
args.push(...buildFlagArgs(options.claudeFlags, options.claudeExtraArgs));
|
|
114223
|
-
|
|
114224
|
-
|
|
114225
|
-
|
|
114226
|
-
|
|
114227
|
-
|
|
114228
|
-
|
|
114229
|
-
|
|
114650
|
+
let proc;
|
|
114651
|
+
try {
|
|
114652
|
+
proc = nodePty.spawn(resolveClaudeExe(), args, {
|
|
114653
|
+
name: "xterm-256color",
|
|
114654
|
+
cols: 120,
|
|
114655
|
+
rows: 40,
|
|
114656
|
+
cwd: options.projectPath,
|
|
114657
|
+
env: buildSpawnEnv()
|
|
114658
|
+
});
|
|
114659
|
+
} catch (err) {
|
|
114660
|
+
clearClaudeExeCache();
|
|
114661
|
+
throw err;
|
|
114662
|
+
}
|
|
114230
114663
|
const session = {
|
|
114231
114664
|
id: sessionId,
|
|
114232
114665
|
provider: CLAUDE_CODE_PROVIDER2,
|
|
@@ -114775,15 +115208,34 @@ var init_live_session_manager = __esm({
|
|
|
114775
115208
|
import_path23 = require("path");
|
|
114776
115209
|
init_codex_pty_runner();
|
|
114777
115210
|
init_providers();
|
|
115211
|
+
init_remote_session_runner();
|
|
114778
115212
|
init_pty_manager();
|
|
114779
115213
|
LiveSessionManager = class {
|
|
114780
115214
|
runners;
|
|
115215
|
+
remoteRunner = null;
|
|
115216
|
+
options;
|
|
114781
115217
|
constructor(options = {}) {
|
|
115218
|
+
this.options = options;
|
|
114782
115219
|
this.runners = /* @__PURE__ */ new Map([
|
|
114783
115220
|
[CLAUDE_CODE_PROVIDER2, new PTYManager(options)],
|
|
114784
115221
|
[CODEX_CLI_PROVIDER2, new CodexPtyRunner(options)]
|
|
114785
115222
|
]);
|
|
114786
115223
|
}
|
|
115224
|
+
async useRemoteRunner(transport) {
|
|
115225
|
+
const remote = await RemoteSessionRunner.connect(transport, this.options);
|
|
115226
|
+
await Promise.all(
|
|
115227
|
+
remote.listSessions().map((session) => remote.hydrateInputHistory(session.id))
|
|
115228
|
+
);
|
|
115229
|
+
for (const runner of this.runners.values()) runner.dispose();
|
|
115230
|
+
this.remoteRunner = remote;
|
|
115231
|
+
return remote.listSessions();
|
|
115232
|
+
}
|
|
115233
|
+
isRemote() {
|
|
115234
|
+
return this.remoteRunner !== null;
|
|
115235
|
+
}
|
|
115236
|
+
startRemoteHeartbeat(getState) {
|
|
115237
|
+
this.remoteRunner?.startHeartbeat(getState);
|
|
115238
|
+
}
|
|
114787
115239
|
async start(sessionId, options) {
|
|
114788
115240
|
const provider = options.provider ?? CLAUDE_CODE_PROVIDER2;
|
|
114789
115241
|
const runner = this.assertSupportedProvider(provider, options.projectPath);
|
|
@@ -114804,7 +115256,7 @@ var init_live_session_manager = __esm({
|
|
|
114804
115256
|
this.runnerFor(sessionId).cancel(sessionId);
|
|
114805
115257
|
}
|
|
114806
115258
|
killPid(pid) {
|
|
114807
|
-
for (const runner of this.
|
|
115259
|
+
for (const runner of this.activeRunners()) {
|
|
114808
115260
|
runner.killPid(pid);
|
|
114809
115261
|
}
|
|
114810
115262
|
}
|
|
@@ -114814,13 +115266,13 @@ var init_live_session_manager = __esm({
|
|
|
114814
115266
|
// every runner rather than throwing; this matches the pre-extraction
|
|
114815
115267
|
// behavior of delegating straight through with no existence check.
|
|
114816
115268
|
putOnHold(sessionId) {
|
|
114817
|
-
for (const runner of this.
|
|
115269
|
+
for (const runner of this.activeRunners()) {
|
|
114818
115270
|
if (runner.hasSession(sessionId) || runner.getSession(sessionId)) {
|
|
114819
115271
|
runner.putOnHold(sessionId);
|
|
114820
115272
|
return;
|
|
114821
115273
|
}
|
|
114822
115274
|
}
|
|
114823
|
-
for (const runner of this.
|
|
115275
|
+
for (const runner of this.activeRunners()) {
|
|
114824
115276
|
runner.putOnHold(sessionId);
|
|
114825
115277
|
}
|
|
114826
115278
|
}
|
|
@@ -114834,7 +115286,7 @@ var init_live_session_manager = __esm({
|
|
|
114834
115286
|
return this.runnerFor(sessionId).getInputHistory(sessionId);
|
|
114835
115287
|
}
|
|
114836
115288
|
getSession(sessionId) {
|
|
114837
|
-
for (const runner of this.
|
|
115289
|
+
for (const runner of this.activeRunners()) {
|
|
114838
115290
|
const session = runner.getSession(sessionId);
|
|
114839
115291
|
if (session) return session;
|
|
114840
115292
|
}
|
|
@@ -114844,23 +115296,23 @@ var init_live_session_manager = __esm({
|
|
|
114844
115296
|
// best-effort basis, so an unknown session must return null rather than
|
|
114845
115297
|
// throw the way the input-routing methods do.
|
|
114846
115298
|
getPid(sessionId) {
|
|
114847
|
-
for (const runner of this.
|
|
115299
|
+
for (const runner of this.activeRunners()) {
|
|
114848
115300
|
const pid = runner.getPid(sessionId);
|
|
114849
115301
|
if (pid != null) return pid;
|
|
114850
115302
|
}
|
|
114851
115303
|
return null;
|
|
114852
115304
|
}
|
|
114853
115305
|
hasSession(sessionId) {
|
|
114854
|
-
for (const runner of this.
|
|
115306
|
+
for (const runner of this.activeRunners()) {
|
|
114855
115307
|
if (runner.hasSession(sessionId)) return true;
|
|
114856
115308
|
}
|
|
114857
115309
|
return false;
|
|
114858
115310
|
}
|
|
114859
115311
|
listSessions() {
|
|
114860
|
-
return
|
|
115312
|
+
return this.activeRunners().flatMap((runner) => runner.listSessions());
|
|
114861
115313
|
}
|
|
114862
115314
|
dispose() {
|
|
114863
|
-
for (const runner of this.
|
|
115315
|
+
for (const runner of this.activeRunners()) {
|
|
114864
115316
|
runner.dispose();
|
|
114865
115317
|
}
|
|
114866
115318
|
}
|
|
@@ -114868,12 +115320,13 @@ var init_live_session_manager = __esm({
|
|
|
114868
115320
|
// this is a linear scan across hasSession()/getSession() rather than a
|
|
114869
115321
|
// separate session→provider index — see task-1-brief.md.
|
|
114870
115322
|
runnerFor(sessionId) {
|
|
114871
|
-
for (const runner of this.
|
|
115323
|
+
for (const runner of this.activeRunners()) {
|
|
114872
115324
|
if (runner.hasSession(sessionId) || runner.getSession(sessionId)) return runner;
|
|
114873
115325
|
}
|
|
114874
115326
|
throw new Error(`Session not found: ${sessionId}`);
|
|
114875
115327
|
}
|
|
114876
115328
|
assertSupportedProvider(provider, projectPath) {
|
|
115329
|
+
if (this.remoteRunner) return this.remoteRunner;
|
|
114877
115330
|
const runner = this.runners.get(provider);
|
|
114878
115331
|
if (runner) return runner;
|
|
114879
115332
|
const err = new Error(
|
|
@@ -114882,7 +115335,184 @@ var init_live_session_manager = __esm({
|
|
|
114882
115335
|
err.statusCode = 501;
|
|
114883
115336
|
throw err;
|
|
114884
115337
|
}
|
|
115338
|
+
activeRunners() {
|
|
115339
|
+
return this.remoteRunner ? [this.remoteRunner] : [...this.runners.values()];
|
|
115340
|
+
}
|
|
115341
|
+
};
|
|
115342
|
+
}
|
|
115343
|
+
});
|
|
115344
|
+
|
|
115345
|
+
// src/pty-host/socket.ts
|
|
115346
|
+
var socket_exports = {};
|
|
115347
|
+
__export(socket_exports, {
|
|
115348
|
+
broadcast: () => broadcast,
|
|
115349
|
+
connectToHost: () => connectToHost,
|
|
115350
|
+
hostSocketPath: () => hostSocketPath,
|
|
115351
|
+
listenForStreamers: () => listenForStreamers,
|
|
115352
|
+
probePtyHostStatus: () => probePtyHostStatus,
|
|
115353
|
+
socketTransport: () => socketTransport
|
|
115354
|
+
});
|
|
115355
|
+
function hostSocketPath(instanceId) {
|
|
115356
|
+
if (process.platform === "win32") {
|
|
115357
|
+
return `\\\\.\\pipe\\threadbase-pty-host-${instanceId}`;
|
|
115358
|
+
}
|
|
115359
|
+
const dir = process.env.THREADBASE_CONFIG_DIR ?? (0, import_node_path10.join)((0, import_node_os5.homedir)(), ".threadbase");
|
|
115360
|
+
return (0, import_node_path10.join)(dir, "run", `pty-host-${instanceId}.sock`);
|
|
115361
|
+
}
|
|
115362
|
+
function socketTransport(socket) {
|
|
115363
|
+
socket.setEncoding("utf8");
|
|
115364
|
+
return {
|
|
115365
|
+
send(line) {
|
|
115366
|
+
socket.write(line);
|
|
115367
|
+
},
|
|
115368
|
+
onLine(handler) {
|
|
115369
|
+
socket.on("data", (chunk) => handler(chunk));
|
|
115370
|
+
},
|
|
115371
|
+
onClose(handler) {
|
|
115372
|
+
let handled = false;
|
|
115373
|
+
const handleClose = () => {
|
|
115374
|
+
if (handled) return;
|
|
115375
|
+
handled = true;
|
|
115376
|
+
handler();
|
|
115377
|
+
};
|
|
115378
|
+
socket.once("close", handleClose);
|
|
115379
|
+
socket.once("error", handleClose);
|
|
115380
|
+
},
|
|
115381
|
+
close() {
|
|
115382
|
+
socket.destroy();
|
|
115383
|
+
}
|
|
115384
|
+
};
|
|
115385
|
+
}
|
|
115386
|
+
function connectToHost(socketPath) {
|
|
115387
|
+
return new Promise((resolve4, reject) => {
|
|
115388
|
+
const socket = (0, import_node_net.createConnection)(socketPath);
|
|
115389
|
+
socket.once("error", reject);
|
|
115390
|
+
socket.once("connect", () => {
|
|
115391
|
+
socket.removeListener("error", reject);
|
|
115392
|
+
resolve4(socketTransport(socket));
|
|
115393
|
+
});
|
|
115394
|
+
});
|
|
115395
|
+
}
|
|
115396
|
+
async function probePtyHostStatus(socketPath, timeoutMs = 1e3) {
|
|
115397
|
+
let transport;
|
|
115398
|
+
let connectTimedOut = false;
|
|
115399
|
+
let connectTimer;
|
|
115400
|
+
const connecting = connectToHost(socketPath);
|
|
115401
|
+
try {
|
|
115402
|
+
transport = await Promise.race([
|
|
115403
|
+
connecting,
|
|
115404
|
+
new Promise((_2, reject) => {
|
|
115405
|
+
connectTimer = setTimeout(() => {
|
|
115406
|
+
connectTimedOut = true;
|
|
115407
|
+
reject(new Error(`status timed out after ${timeoutMs}ms`));
|
|
115408
|
+
}, timeoutMs);
|
|
115409
|
+
connectTimer.unref?.();
|
|
115410
|
+
})
|
|
115411
|
+
]);
|
|
115412
|
+
} catch (err) {
|
|
115413
|
+
if (connectTimer) clearTimeout(connectTimer);
|
|
115414
|
+
if (connectTimedOut)
|
|
115415
|
+
void connecting.then(
|
|
115416
|
+
(late) => late.close(),
|
|
115417
|
+
() => {
|
|
115418
|
+
}
|
|
115419
|
+
);
|
|
115420
|
+
return { reachable: false, error: err instanceof Error ? err.message : String(err) };
|
|
115421
|
+
}
|
|
115422
|
+
if (connectTimer) clearTimeout(connectTimer);
|
|
115423
|
+
return await new Promise((resolve4) => {
|
|
115424
|
+
const decoder = new LineDecoder();
|
|
115425
|
+
let settled = false;
|
|
115426
|
+
const finish = (result) => {
|
|
115427
|
+
if (settled) return;
|
|
115428
|
+
settled = true;
|
|
115429
|
+
clearTimeout(timer);
|
|
115430
|
+
transport.close();
|
|
115431
|
+
resolve4(result);
|
|
114885
115432
|
};
|
|
115433
|
+
const timer = setTimeout(
|
|
115434
|
+
() => finish({ reachable: false, error: `status timed out after ${timeoutMs}ms` }),
|
|
115435
|
+
timeoutMs
|
|
115436
|
+
);
|
|
115437
|
+
timer.unref?.();
|
|
115438
|
+
transport.onClose(() => finish({ reachable: false, error: "pty-host connection closed" }));
|
|
115439
|
+
transport.onLine((chunk) => {
|
|
115440
|
+
for (const line of decoder.push(chunk)) {
|
|
115441
|
+
let message;
|
|
115442
|
+
try {
|
|
115443
|
+
message = JSON.parse(line);
|
|
115444
|
+
} catch {
|
|
115445
|
+
finish({ reachable: false, error: "pty-host returned an invalid status response" });
|
|
115446
|
+
return;
|
|
115447
|
+
}
|
|
115448
|
+
if (isHostEvent(message) || message.id !== 1) continue;
|
|
115449
|
+
if (!message.ok) {
|
|
115450
|
+
finish({ reachable: false, error: message.error });
|
|
115451
|
+
return;
|
|
115452
|
+
}
|
|
115453
|
+
const status = message.result;
|
|
115454
|
+
if (typeof status?.protocolVersion !== "number" || !Array.isArray(status.sessions)) {
|
|
115455
|
+
finish({ reachable: false, error: "pty-host returned an invalid status response" });
|
|
115456
|
+
return;
|
|
115457
|
+
}
|
|
115458
|
+
finish({
|
|
115459
|
+
reachable: true,
|
|
115460
|
+
protocolVersion: status.protocolVersion,
|
|
115461
|
+
sessionCount: status.sessions.length
|
|
115462
|
+
});
|
|
115463
|
+
}
|
|
115464
|
+
});
|
|
115465
|
+
try {
|
|
115466
|
+
transport.send(encodeMessage({ id: 1, type: "status" }));
|
|
115467
|
+
} catch (err) {
|
|
115468
|
+
finish({ reachable: false, error: err instanceof Error ? err.message : String(err) });
|
|
115469
|
+
}
|
|
115470
|
+
});
|
|
115471
|
+
}
|
|
115472
|
+
async function listenForStreamers(socketPath, handlers) {
|
|
115473
|
+
if (process.platform !== "win32") {
|
|
115474
|
+
const { mkdirSync: mkdirSync16, rmSync: rmSync6 } = await import("fs");
|
|
115475
|
+
const { dirname: dirname18 } = await import("path");
|
|
115476
|
+
mkdirSync16(dirname18(socketPath), { recursive: true });
|
|
115477
|
+
try {
|
|
115478
|
+
const probe = await connectToHost(socketPath);
|
|
115479
|
+
probe.close();
|
|
115480
|
+
throw new Error(`a pty-host is already listening on ${socketPath}`);
|
|
115481
|
+
} catch (err) {
|
|
115482
|
+
if (err.message?.startsWith("a pty-host is already listening")) throw err;
|
|
115483
|
+
rmSync6(socketPath, { force: true });
|
|
115484
|
+
}
|
|
115485
|
+
}
|
|
115486
|
+
const server = (0, import_node_net.createServer)((socket) => {
|
|
115487
|
+
const transport = socketTransport(socket);
|
|
115488
|
+
const dispose = handlers.onConnection(transport);
|
|
115489
|
+
transport.onClose(dispose);
|
|
115490
|
+
});
|
|
115491
|
+
return new Promise((resolve4, reject) => {
|
|
115492
|
+
server.once("error", reject);
|
|
115493
|
+
server.listen(socketPath, () => {
|
|
115494
|
+
server.removeListener("error", reject);
|
|
115495
|
+
resolve4(server);
|
|
115496
|
+
});
|
|
115497
|
+
});
|
|
115498
|
+
}
|
|
115499
|
+
function broadcast(transports, message) {
|
|
115500
|
+
const line = encodeMessage(message);
|
|
115501
|
+
for (const t of transports) {
|
|
115502
|
+
try {
|
|
115503
|
+
t.send(line);
|
|
115504
|
+
} catch {
|
|
115505
|
+
}
|
|
115506
|
+
}
|
|
115507
|
+
}
|
|
115508
|
+
var import_node_net, import_node_os5, import_node_path10;
|
|
115509
|
+
var init_socket = __esm({
|
|
115510
|
+
"src/pty-host/socket.ts"() {
|
|
115511
|
+
"use strict";
|
|
115512
|
+
import_node_net = require("net");
|
|
115513
|
+
import_node_os5 = require("os");
|
|
115514
|
+
import_node_path10 = require("path");
|
|
115515
|
+
init_protocol();
|
|
114886
115516
|
}
|
|
114887
115517
|
});
|
|
114888
115518
|
|
|
@@ -125828,7 +126458,7 @@ function uidScope() {
|
|
|
125828
126458
|
function resolveOwnLabel() {
|
|
125829
126459
|
if (process.env.LAUNCHD_LABEL) return process.env.LAUNCHD_LABEL;
|
|
125830
126460
|
try {
|
|
125831
|
-
(0,
|
|
126461
|
+
(0, import_node_child_process5.execFileSync)("launchctl", ["print", `${uidScope()}/homebrew.mxcl.tb-streamer`], {
|
|
125832
126462
|
stdio: "ignore"
|
|
125833
126463
|
});
|
|
125834
126464
|
return "homebrew.mxcl.tb-streamer";
|
|
@@ -125838,7 +126468,7 @@ function resolveOwnLabel() {
|
|
|
125838
126468
|
}
|
|
125839
126469
|
function isLabelLoaded(label) {
|
|
125840
126470
|
try {
|
|
125841
|
-
(0,
|
|
126471
|
+
(0, import_node_child_process5.execFileSync)("launchctl", ["list", label], { stdio: "ignore" });
|
|
125842
126472
|
return true;
|
|
125843
126473
|
} catch {
|
|
125844
126474
|
return false;
|
|
@@ -125858,7 +126488,7 @@ function detectConflictingAgents() {
|
|
|
125858
126488
|
}
|
|
125859
126489
|
}
|
|
125860
126490
|
try {
|
|
125861
|
-
const out = (0,
|
|
126491
|
+
const out = (0, import_node_child_process5.execFileSync)("launchctl", ["list"], {
|
|
125862
126492
|
stdio: ["ignore", "pipe", "ignore"]
|
|
125863
126493
|
}).toString();
|
|
125864
126494
|
for (const line of out.split("\n")) {
|
|
@@ -125891,11 +126521,11 @@ function formatConflictMessage(conflicts) {
|
|
|
125891
126521
|
);
|
|
125892
126522
|
return lines.join("\n");
|
|
125893
126523
|
}
|
|
125894
|
-
var
|
|
126524
|
+
var import_node_child_process5, KNOWN_STREAMER_LABELS, LEGACY_PATTERN;
|
|
125895
126525
|
var init_conflict_check = __esm({
|
|
125896
126526
|
"src/lifecycle/conflict-check.ts"() {
|
|
125897
126527
|
"use strict";
|
|
125898
|
-
|
|
126528
|
+
import_node_child_process5 = require("child_process");
|
|
125899
126529
|
init_constants();
|
|
125900
126530
|
KNOWN_STREAMER_LABELS = [
|
|
125901
126531
|
"com.ronen.threadbase",
|
|
@@ -125923,7 +126553,7 @@ function uidScope2() {
|
|
|
125923
126553
|
function resolveLoadedLabel() {
|
|
125924
126554
|
if (process.env.LAUNCHD_LABEL) return process.env.LAUNCHD_LABEL;
|
|
125925
126555
|
try {
|
|
125926
|
-
(0,
|
|
126556
|
+
(0, import_node_child_process6.execFileSync)("launchctl", ["print", `${uidScope2()}/${BREW_LAUNCHD_LABEL2}`], {
|
|
125927
126557
|
stdio: "ignore"
|
|
125928
126558
|
});
|
|
125929
126559
|
return BREW_LAUNCHD_LABEL2;
|
|
@@ -125936,7 +126566,7 @@ function fullTarget() {
|
|
|
125936
126566
|
}
|
|
125937
126567
|
function isAgentLoaded() {
|
|
125938
126568
|
try {
|
|
125939
|
-
(0,
|
|
126569
|
+
(0, import_node_child_process6.execFileSync)("launchctl", ["list", resolveLoadedLabel()], { stdio: "ignore" });
|
|
125940
126570
|
return true;
|
|
125941
126571
|
} catch {
|
|
125942
126572
|
return false;
|
|
@@ -125944,7 +126574,7 @@ function isAgentLoaded() {
|
|
|
125944
126574
|
}
|
|
125945
126575
|
function bootoutAgent(timeoutMs = 2e3) {
|
|
125946
126576
|
try {
|
|
125947
|
-
(0,
|
|
126577
|
+
(0, import_node_child_process6.execFileSync)("launchctl", ["bootout", fullTarget()], { stdio: "ignore" });
|
|
125948
126578
|
} catch {
|
|
125949
126579
|
}
|
|
125950
126580
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -125955,7 +126585,7 @@ function bootoutAgent(timeoutMs = 2e3) {
|
|
|
125955
126585
|
}
|
|
125956
126586
|
function bootstrapAgent(plistPath, opts) {
|
|
125957
126587
|
try {
|
|
125958
|
-
(0,
|
|
126588
|
+
(0, import_node_child_process6.execFileSync)("launchctl", ["bootstrap", uidScope2(), plistPath], {
|
|
125959
126589
|
stdio: ["ignore", "ignore", "pipe"]
|
|
125960
126590
|
});
|
|
125961
126591
|
} catch (err) {
|
|
@@ -125968,18 +126598,18 @@ function bootstrapAgent(plistPath, opts) {
|
|
|
125968
126598
|
}
|
|
125969
126599
|
}
|
|
125970
126600
|
function kickstartAgent() {
|
|
125971
|
-
(0,
|
|
126601
|
+
(0, import_node_child_process6.execFileSync)("launchctl", ["kickstart", "-k", fullTarget()], { stdio: "ignore" });
|
|
125972
126602
|
}
|
|
125973
126603
|
function darwinPlistPath() {
|
|
125974
126604
|
return `${process.env.HOME}/Library/LaunchAgents/${resolveLoadedLabel()}.plist`;
|
|
125975
126605
|
}
|
|
125976
126606
|
function getLogPaths() {
|
|
125977
|
-
const dir = (0,
|
|
125978
|
-
return { stdout: (0,
|
|
126607
|
+
const dir = (0, import_node_path25.join)(installDir(), "logs");
|
|
126608
|
+
return { stdout: (0, import_node_path25.join)(dir, "stdout.log"), stderr: (0, import_node_path25.join)(dir, "stderr.log") };
|
|
125979
126609
|
}
|
|
125980
126610
|
function getAgentPid() {
|
|
125981
126611
|
try {
|
|
125982
|
-
const out = (0,
|
|
126612
|
+
const out = (0, import_node_child_process6.execFileSync)("launchctl", ["list"], {
|
|
125983
126613
|
stdio: ["ignore", "pipe", "ignore"]
|
|
125984
126614
|
}).toString();
|
|
125985
126615
|
const label = resolveLoadedLabel();
|
|
@@ -125995,12 +126625,12 @@ function getAgentPid() {
|
|
|
125995
126625
|
return null;
|
|
125996
126626
|
}
|
|
125997
126627
|
}
|
|
125998
|
-
var
|
|
126628
|
+
var import_node_child_process6, import_node_path25, BREW_LAUNCHD_LABEL2;
|
|
125999
126629
|
var init_launchd = __esm({
|
|
126000
126630
|
"src/lifecycle/launchd.ts"() {
|
|
126001
126631
|
"use strict";
|
|
126002
|
-
|
|
126003
|
-
|
|
126632
|
+
import_node_child_process6 = require("child_process");
|
|
126633
|
+
import_node_path25 = require("path");
|
|
126004
126634
|
init_constants();
|
|
126005
126635
|
BREW_LAUNCHD_LABEL2 = "homebrew.mxcl.tb-streamer";
|
|
126006
126636
|
}
|
|
@@ -126031,7 +126661,7 @@ function readMarker() {
|
|
|
126031
126661
|
const parsed = JSON.parse(raw2);
|
|
126032
126662
|
return MarkerSchema.parse(parsed);
|
|
126033
126663
|
} catch (err) {
|
|
126034
|
-
|
|
126664
|
+
log8.warn(`marker at ${markerPath()} is malformed; treating as absent`, {
|
|
126035
126665
|
error: err instanceof Error ? err.message : String(err)
|
|
126036
126666
|
});
|
|
126037
126667
|
return null;
|
|
@@ -126039,7 +126669,7 @@ function readMarker() {
|
|
|
126039
126669
|
}
|
|
126040
126670
|
function writeMarker(marker) {
|
|
126041
126671
|
MarkerSchema.parse(marker);
|
|
126042
|
-
(0, import_node_fs22.mkdirSync)((0,
|
|
126672
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path26.dirname)(markerPath()), { recursive: true });
|
|
126043
126673
|
const tmp = `${markerPath()}.tmp`;
|
|
126044
126674
|
(0, import_node_fs22.writeFileSync)(tmp, `${JSON.stringify(marker, null, 2)}
|
|
126045
126675
|
`, { mode: 384 });
|
|
@@ -126048,16 +126678,16 @@ function writeMarker(marker) {
|
|
|
126048
126678
|
function clearMarker() {
|
|
126049
126679
|
if ((0, import_node_fs22.existsSync)(markerPath())) (0, import_node_fs22.rmSync)(markerPath());
|
|
126050
126680
|
}
|
|
126051
|
-
var import_node_fs22,
|
|
126681
|
+
var import_node_fs22, import_node_path26, log8;
|
|
126052
126682
|
var init_marker = __esm({
|
|
126053
126683
|
"src/lifecycle/marker.ts"() {
|
|
126054
126684
|
"use strict";
|
|
126055
126685
|
import_node_fs22 = require("fs");
|
|
126056
|
-
|
|
126686
|
+
import_node_path26 = require("path");
|
|
126057
126687
|
init_logger();
|
|
126058
126688
|
init_constants();
|
|
126059
126689
|
init_marker_schema();
|
|
126060
|
-
|
|
126690
|
+
log8 = getLogger("lifecycle.marker");
|
|
126061
126691
|
}
|
|
126062
126692
|
});
|
|
126063
126693
|
|
|
@@ -126072,7 +126702,7 @@ __export(task_scheduler_exports, {
|
|
|
126072
126702
|
kickstartAgent: () => kickstartAgent2
|
|
126073
126703
|
});
|
|
126074
126704
|
function ps2(command) {
|
|
126075
|
-
return (0,
|
|
126705
|
+
return (0, import_node_child_process7.execFileSync)("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], {
|
|
126076
126706
|
stdio: ["ignore", "pipe", "ignore"]
|
|
126077
126707
|
}).toString();
|
|
126078
126708
|
}
|
|
@@ -126118,11 +126748,11 @@ function getAgentPid2() {
|
|
|
126118
126748
|
return null;
|
|
126119
126749
|
}
|
|
126120
126750
|
}
|
|
126121
|
-
var
|
|
126751
|
+
var import_node_child_process7;
|
|
126122
126752
|
var init_task_scheduler = __esm({
|
|
126123
126753
|
"src/lifecycle/task-scheduler.ts"() {
|
|
126124
126754
|
"use strict";
|
|
126125
|
-
|
|
126755
|
+
import_node_child_process7 = require("child_process");
|
|
126126
126756
|
init_constants();
|
|
126127
126757
|
}
|
|
126128
126758
|
});
|
|
@@ -126189,6 +126819,34 @@ var init_check_sqlite_abi = __esm({
|
|
|
126189
126819
|
}
|
|
126190
126820
|
});
|
|
126191
126821
|
|
|
126822
|
+
// src/lifecycle/log-cap.ts
|
|
126823
|
+
var log_cap_exports = {};
|
|
126824
|
+
__export(log_cap_exports, {
|
|
126825
|
+
LOG_CAP_BYTES: () => LOG_CAP_BYTES,
|
|
126826
|
+
truncateOversizedLogs: () => truncateOversizedLogs
|
|
126827
|
+
});
|
|
126828
|
+
function truncateOversizedLogs(maxBytes = LOG_CAP_BYTES, paths = Object.values(getLogPaths())) {
|
|
126829
|
+
const truncated = [];
|
|
126830
|
+
for (const file2 of paths) {
|
|
126831
|
+
try {
|
|
126832
|
+
if ((0, import_node_fs24.statSync)(file2).size <= maxBytes) continue;
|
|
126833
|
+
(0, import_node_fs24.truncateSync)(file2, 0);
|
|
126834
|
+
truncated.push(file2);
|
|
126835
|
+
} catch {
|
|
126836
|
+
}
|
|
126837
|
+
}
|
|
126838
|
+
return truncated;
|
|
126839
|
+
}
|
|
126840
|
+
var import_node_fs24, LOG_CAP_BYTES;
|
|
126841
|
+
var init_log_cap = __esm({
|
|
126842
|
+
"src/lifecycle/log-cap.ts"() {
|
|
126843
|
+
"use strict";
|
|
126844
|
+
import_node_fs24 = require("fs");
|
|
126845
|
+
init_launchd();
|
|
126846
|
+
LOG_CAP_BYTES = 32 * 1024 * 1024;
|
|
126847
|
+
}
|
|
126848
|
+
});
|
|
126849
|
+
|
|
126192
126850
|
// src/lifecycle/prompt.ts
|
|
126193
126851
|
var prompt_exports = {};
|
|
126194
126852
|
__export(prompt_exports, {
|
|
@@ -126267,11 +126925,11 @@ The supervised prod streamer is already holding port ${prodPort}.
|
|
|
126267
126925
|
// src/lifecycle/prefs.ts
|
|
126268
126926
|
function readPrefs() {
|
|
126269
126927
|
const path2 = prefsPath();
|
|
126270
|
-
if (!(0,
|
|
126928
|
+
if (!(0, import_node_fs25.existsSync)(path2)) return { repos: {} };
|
|
126271
126929
|
try {
|
|
126272
|
-
return PrefsSchema.parse(JSON.parse((0,
|
|
126930
|
+
return PrefsSchema.parse(JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8")));
|
|
126273
126931
|
} catch (err) {
|
|
126274
|
-
|
|
126932
|
+
log10.warn(`prefs at ${path2} are malformed; treating as empty`, {
|
|
126275
126933
|
error: err instanceof Error ? err.message : String(err)
|
|
126276
126934
|
});
|
|
126277
126935
|
return { repos: {} };
|
|
@@ -126279,11 +126937,11 @@ function readPrefs() {
|
|
|
126279
126937
|
}
|
|
126280
126938
|
function savePrefs(p2) {
|
|
126281
126939
|
const path2 = prefsPath();
|
|
126282
|
-
(0,
|
|
126940
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path27.dirname)(path2), { recursive: true });
|
|
126283
126941
|
const tmp = `${path2}.tmp`;
|
|
126284
|
-
(0,
|
|
126942
|
+
(0, import_node_fs25.writeFileSync)(tmp, `${JSON.stringify(p2, null, 2)}
|
|
126285
126943
|
`, { mode: 384 });
|
|
126286
|
-
(0,
|
|
126944
|
+
(0, import_node_fs25.renameSync)(tmp, path2);
|
|
126287
126945
|
}
|
|
126288
126946
|
function writePrefForRepo(repoToplevel, choice) {
|
|
126289
126947
|
const p2 = readPrefs();
|
|
@@ -126304,18 +126962,18 @@ function forgetRepo(repoToplevel) {
|
|
|
126304
126962
|
}
|
|
126305
126963
|
function forgetAll() {
|
|
126306
126964
|
const path2 = prefsPath();
|
|
126307
|
-
if ((0,
|
|
126965
|
+
if ((0, import_node_fs25.existsSync)(path2)) (0, import_node_fs25.rmSync)(path2);
|
|
126308
126966
|
}
|
|
126309
|
-
var
|
|
126967
|
+
var import_node_fs25, import_node_path27, log10, PrefsSchema;
|
|
126310
126968
|
var init_prefs = __esm({
|
|
126311
126969
|
"src/lifecycle/prefs.ts"() {
|
|
126312
126970
|
"use strict";
|
|
126313
|
-
|
|
126314
|
-
|
|
126971
|
+
import_node_fs25 = require("fs");
|
|
126972
|
+
import_node_path27 = require("path");
|
|
126315
126973
|
init_zod();
|
|
126316
126974
|
init_logger();
|
|
126317
126975
|
init_constants();
|
|
126318
|
-
|
|
126976
|
+
log10 = getLogger("lifecycle.prefs");
|
|
126319
126977
|
PrefsSchema = external_exports.object({
|
|
126320
126978
|
repos: external_exports.record(
|
|
126321
126979
|
external_exports.string(),
|
|
@@ -126381,7 +127039,7 @@ function detectProdActive() {
|
|
|
126381
127039
|
}
|
|
126382
127040
|
function isPortInUse(port) {
|
|
126383
127041
|
return new Promise((resolve4) => {
|
|
126384
|
-
const srv = (0,
|
|
127042
|
+
const srv = (0, import_node_net2.createServer)();
|
|
126385
127043
|
srv.once("error", () => resolve4(true));
|
|
126386
127044
|
srv.once("listening", () => srv.close(() => resolve4(false)));
|
|
126387
127045
|
srv.listen(port, "127.0.0.1");
|
|
@@ -126394,7 +127052,7 @@ async function tryBind(start, offset) {
|
|
|
126394
127052
|
if (offset >= 50) return start;
|
|
126395
127053
|
const port = start + offset;
|
|
126396
127054
|
const free = await new Promise((resolve4) => {
|
|
126397
|
-
const srv = (0,
|
|
127055
|
+
const srv = (0, import_node_net2.createServer)();
|
|
126398
127056
|
srv.once("error", () => resolve4(false));
|
|
126399
127057
|
srv.once("listening", () => srv.close(() => resolve4(true)));
|
|
126400
127058
|
srv.listen(port, "127.0.0.1");
|
|
@@ -126418,7 +127076,7 @@ function takeoverProd(opts) {
|
|
|
126418
127076
|
`prod is already suspended by dev pid ${existing.devPid} (since ${existing.suspendedAt}). Stop that dev session first, or run 'tb-streamer prod doctor'.`
|
|
126419
127077
|
);
|
|
126420
127078
|
}
|
|
126421
|
-
|
|
127079
|
+
log11.info(`stale marker found (pid ${existing.devPid} is gone) \u2014 clearing and proceeding`);
|
|
126422
127080
|
}
|
|
126423
127081
|
getSupervisor().bootoutAgent();
|
|
126424
127082
|
writeMarker({
|
|
@@ -126433,7 +127091,7 @@ function takeoverProd(opts) {
|
|
|
126433
127091
|
const m2 = readMarker();
|
|
126434
127092
|
if (m2 && m2.devPid === process.pid) {
|
|
126435
127093
|
writeMarker({ ...m2, userHeld: true });
|
|
126436
|
-
|
|
127094
|
+
log11.info(
|
|
126437
127095
|
`prod is suspended (userHeld). Run 'tb-streamer prod start' to restore the supervised instance.`
|
|
126438
127096
|
);
|
|
126439
127097
|
}
|
|
@@ -126452,23 +127110,23 @@ function takeoverProd(opts) {
|
|
|
126452
127110
|
});
|
|
126453
127111
|
process.on("uncaughtException", (err) => {
|
|
126454
127112
|
flipUserHeld();
|
|
126455
|
-
|
|
127113
|
+
log11.error(`uncaught: ${err.message}`);
|
|
126456
127114
|
process.exit(1);
|
|
126457
127115
|
});
|
|
126458
127116
|
process.on("exit", () => {
|
|
126459
127117
|
flipUserHeld();
|
|
126460
127118
|
});
|
|
126461
127119
|
}
|
|
126462
|
-
var
|
|
127120
|
+
var import_node_net2, log11;
|
|
126463
127121
|
var init_dev_takeover = __esm({
|
|
126464
127122
|
"src/lifecycle/dev-takeover.ts"() {
|
|
126465
127123
|
"use strict";
|
|
126466
|
-
|
|
127124
|
+
import_node_net2 = require("net");
|
|
126467
127125
|
init_logger();
|
|
126468
127126
|
init_marker();
|
|
126469
127127
|
init_platform2();
|
|
126470
127128
|
init_prefs();
|
|
126471
|
-
|
|
127129
|
+
log11 = getLogger("lifecycle.dev-takeover");
|
|
126472
127130
|
}
|
|
126473
127131
|
});
|
|
126474
127132
|
|
|
@@ -126479,7 +127137,7 @@ __export(repo_exports, {
|
|
|
126479
127137
|
});
|
|
126480
127138
|
function getGitToplevel(cwd) {
|
|
126481
127139
|
try {
|
|
126482
|
-
return (0,
|
|
127140
|
+
return (0, import_node_child_process9.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
|
|
126483
127141
|
cwd,
|
|
126484
127142
|
stdio: ["ignore", "pipe", "ignore"]
|
|
126485
127143
|
}).toString().trim();
|
|
@@ -126487,11 +127145,11 @@ function getGitToplevel(cwd) {
|
|
|
126487
127145
|
return null;
|
|
126488
127146
|
}
|
|
126489
127147
|
}
|
|
126490
|
-
var
|
|
127148
|
+
var import_node_child_process9;
|
|
126491
127149
|
var init_repo = __esm({
|
|
126492
127150
|
"src/lifecycle/repo.ts"() {
|
|
126493
127151
|
"use strict";
|
|
126494
|
-
|
|
127152
|
+
import_node_child_process9 = require("child_process");
|
|
126495
127153
|
}
|
|
126496
127154
|
});
|
|
126497
127155
|
|
|
@@ -126541,39 +127199,16 @@ var init_setKey = __esm({
|
|
|
126541
127199
|
}
|
|
126542
127200
|
});
|
|
126543
127201
|
|
|
126544
|
-
// src/pty-host/protocol.ts
|
|
126545
|
-
function isHostEvent(message) {
|
|
126546
|
-
return "type" in message && message.type === "event";
|
|
126547
|
-
}
|
|
126548
|
-
function encodeMessage(message) {
|
|
126549
|
-
return `${JSON.stringify(message)}
|
|
126550
|
-
`;
|
|
126551
|
-
}
|
|
126552
|
-
var PTY_HOST_PROTOCOL_VERSION, LineDecoder;
|
|
126553
|
-
var init_protocol = __esm({
|
|
126554
|
-
"src/pty-host/protocol.ts"() {
|
|
126555
|
-
"use strict";
|
|
126556
|
-
PTY_HOST_PROTOCOL_VERSION = 1;
|
|
126557
|
-
LineDecoder = class {
|
|
126558
|
-
buffer = "";
|
|
126559
|
-
push(chunk) {
|
|
126560
|
-
this.buffer += chunk;
|
|
126561
|
-
const lines = this.buffer.split("\n");
|
|
126562
|
-
this.buffer = lines.pop() ?? "";
|
|
126563
|
-
return lines.filter((line) => line.length > 0);
|
|
126564
|
-
}
|
|
126565
|
-
};
|
|
126566
|
-
}
|
|
126567
|
-
});
|
|
126568
|
-
|
|
126569
127202
|
// src/pty-host/host.ts
|
|
126570
127203
|
var host_exports = {};
|
|
126571
127204
|
__export(host_exports, {
|
|
127205
|
+
HOST_HEARTBEAT_TIMEOUT_MS: () => HOST_HEARTBEAT_TIMEOUT_MS,
|
|
126572
127206
|
HOST_IDLE_AFTER_MS: () => HOST_IDLE_AFTER_MS,
|
|
126573
127207
|
HOST_IDLE_SWEEP_MS: () => HOST_IDLE_SWEEP_MS,
|
|
127208
|
+
HOST_ORPHAN_SWEEP_MS: () => HOST_ORPHAN_SWEEP_MS,
|
|
126574
127209
|
SessionHost: () => SessionHost
|
|
126575
127210
|
});
|
|
126576
|
-
var HOST_IDLE_SWEEP_MS, HOST_IDLE_AFTER_MS, SessionHost;
|
|
127211
|
+
var HOST_IDLE_SWEEP_MS, HOST_IDLE_AFTER_MS, HOST_HEARTBEAT_TIMEOUT_MS, HOST_ORPHAN_SWEEP_MS, SessionHost;
|
|
126577
127212
|
var init_host = __esm({
|
|
126578
127213
|
"src/pty-host/host.ts"() {
|
|
126579
127214
|
"use strict";
|
|
@@ -126582,6 +127217,8 @@ var init_host = __esm({
|
|
|
126582
127217
|
init_protocol();
|
|
126583
127218
|
HOST_IDLE_SWEEP_MS = 5 * 60 * 1e3;
|
|
126584
127219
|
HOST_IDLE_AFTER_MS = 6 * 60 * 60 * 1e3;
|
|
127220
|
+
HOST_HEARTBEAT_TIMEOUT_MS = 3e4;
|
|
127221
|
+
HOST_ORPHAN_SWEEP_MS = 1e4;
|
|
126585
127222
|
SessionHost = class {
|
|
126586
127223
|
runner;
|
|
126587
127224
|
log;
|
|
@@ -126591,9 +127228,19 @@ var init_host = __esm({
|
|
|
126591
127228
|
lastAgentChunkAt = /* @__PURE__ */ new Map();
|
|
126592
127229
|
idleTimer = null;
|
|
126593
127230
|
idleAfterMs;
|
|
127231
|
+
onShutdown;
|
|
127232
|
+
heartbeatTimeoutMs;
|
|
127233
|
+
heartbeatLeases = /* @__PURE__ */ new Map();
|
|
127234
|
+
lastRegistryHeartbeat = null;
|
|
127235
|
+
orphanTimer = null;
|
|
127236
|
+
onOrphaned;
|
|
127237
|
+
orphaned = false;
|
|
126594
127238
|
constructor(options = {}) {
|
|
126595
127239
|
this.log = options.logger ?? getLogger("pty-host");
|
|
126596
127240
|
this.idleAfterMs = options.idleAfterMs ?? HOST_IDLE_AFTER_MS;
|
|
127241
|
+
this.onShutdown = options.onShutdown;
|
|
127242
|
+
this.heartbeatTimeoutMs = options.heartbeatTimeoutMs ?? HOST_HEARTBEAT_TIMEOUT_MS;
|
|
127243
|
+
this.onOrphaned = options.onOrphaned;
|
|
126597
127244
|
this.runner = new LiveSessionManager({
|
|
126598
127245
|
logger: this.log,
|
|
126599
127246
|
onOutput: (sessionId, data) => {
|
|
@@ -126609,6 +127256,11 @@ var init_host = __esm({
|
|
|
126609
127256
|
});
|
|
126610
127257
|
this.idleTimer = setInterval(() => this.reapIdle(), options.idleSweepMs ?? HOST_IDLE_SWEEP_MS);
|
|
126611
127258
|
this.idleTimer.unref?.();
|
|
127259
|
+
this.orphanTimer = setInterval(
|
|
127260
|
+
() => this.reapOrphan(),
|
|
127261
|
+
options.orphanSweepMs ?? HOST_ORPHAN_SWEEP_MS
|
|
127262
|
+
);
|
|
127263
|
+
this.orphanTimer.unref?.();
|
|
126612
127264
|
}
|
|
126613
127265
|
/**
|
|
126614
127266
|
* Attach a connected streamer. Returns the disposer for its disconnect.
|
|
@@ -126621,6 +127273,7 @@ var init_host = __esm({
|
|
|
126621
127273
|
transport.onLine((chunk) => this.handleChunk(transport, chunk));
|
|
126622
127274
|
return () => {
|
|
126623
127275
|
this.subscribers.delete(transport);
|
|
127276
|
+
this.heartbeatLeases.delete(transport);
|
|
126624
127277
|
this.log.info("[pty-host] streamer disconnected; sessions kept", {
|
|
126625
127278
|
event: "pty_host.streamer_disconnected",
|
|
126626
127279
|
subscribers: this.subscribers.size
|
|
@@ -126648,6 +127301,7 @@ var init_host = __esm({
|
|
|
126648
127301
|
try {
|
|
126649
127302
|
const result = await this.handle(transport, request);
|
|
126650
127303
|
transport.send(encodeMessage({ id: request.id, ok: true, result }));
|
|
127304
|
+
if (request.type === "shutdown-host") this.onShutdown?.();
|
|
126651
127305
|
} catch (err) {
|
|
126652
127306
|
transport.send(
|
|
126653
127307
|
encodeMessage({
|
|
@@ -126672,6 +127326,14 @@ var init_host = __esm({
|
|
|
126672
127326
|
protocolVersion: PTY_HOST_PROTOCOL_VERSION,
|
|
126673
127327
|
sessions: this.runner.listSessions().map((session) => this.toHostSession(session))
|
|
126674
127328
|
};
|
|
127329
|
+
case "heartbeat":
|
|
127330
|
+
this.heartbeatLeases.set(transport, Date.now());
|
|
127331
|
+
this.lastRegistryHeartbeat = {
|
|
127332
|
+
registryState: request.registryState,
|
|
127333
|
+
referencedSessionIds: [...request.referencedSessionIds],
|
|
127334
|
+
at: Date.now()
|
|
127335
|
+
};
|
|
127336
|
+
return {};
|
|
126675
127337
|
case "spawn": {
|
|
126676
127338
|
const session = request.sessionId === null ? await this.runner.startFresh(request.options) : await this.runner.start(request.sessionId, {
|
|
126677
127339
|
...request.options,
|
|
@@ -126696,6 +127358,8 @@ var init_host = __esm({
|
|
|
126696
127358
|
}
|
|
126697
127359
|
return {};
|
|
126698
127360
|
}
|
|
127361
|
+
case "shutdown-host":
|
|
127362
|
+
return {};
|
|
126699
127363
|
case "replay": {
|
|
126700
127364
|
const lines = await this.runner.getOutputLines(request.sessionId, request.maxLines);
|
|
126701
127365
|
return { lines, output: this.runner.getOutput(request.sessionId) };
|
|
@@ -126751,6 +127415,23 @@ var init_host = __esm({
|
|
|
126751
127415
|
sessionCount() {
|
|
126752
127416
|
return this.runner.listSessions().length;
|
|
126753
127417
|
}
|
|
127418
|
+
reapOrphan(now = Date.now()) {
|
|
127419
|
+
if (this.orphaned) return false;
|
|
127420
|
+
for (const lastSeen of this.heartbeatLeases.values()) {
|
|
127421
|
+
if (now - lastSeen <= this.heartbeatTimeoutMs) return false;
|
|
127422
|
+
}
|
|
127423
|
+
const registry2 = this.lastRegistryHeartbeat;
|
|
127424
|
+
if (registry2?.registryState !== "known") return false;
|
|
127425
|
+
if (now - registry2.at <= this.heartbeatTimeoutMs) return false;
|
|
127426
|
+
if (registry2.referencedSessionIds.length > 0) return false;
|
|
127427
|
+
if (this.sessionCount() > 0) return false;
|
|
127428
|
+
this.orphaned = true;
|
|
127429
|
+
this.log.info("[pty-host] releasing unreferenced idle host", {
|
|
127430
|
+
event: "pty_host.orphan_reap"
|
|
127431
|
+
});
|
|
127432
|
+
this.onOrphaned?.();
|
|
127433
|
+
return true;
|
|
127434
|
+
}
|
|
126754
127435
|
/**
|
|
126755
127436
|
* Shut the host down, signalling its children.
|
|
126756
127437
|
*
|
|
@@ -126761,104 +127442,16 @@ var init_host = __esm({
|
|
|
126761
127442
|
dispose() {
|
|
126762
127443
|
if (this.idleTimer) clearInterval(this.idleTimer);
|
|
126763
127444
|
this.idleTimer = null;
|
|
127445
|
+
if (this.orphanTimer) clearInterval(this.orphanTimer);
|
|
127446
|
+
this.orphanTimer = null;
|
|
126764
127447
|
this.subscribers.clear();
|
|
127448
|
+
this.heartbeatLeases.clear();
|
|
126765
127449
|
this.runner.dispose();
|
|
126766
127450
|
}
|
|
126767
127451
|
};
|
|
126768
127452
|
}
|
|
126769
127453
|
});
|
|
126770
127454
|
|
|
126771
|
-
// src/pty-host/socket.ts
|
|
126772
|
-
var socket_exports = {};
|
|
126773
|
-
__export(socket_exports, {
|
|
126774
|
-
broadcast: () => broadcast,
|
|
126775
|
-
connectToHost: () => connectToHost,
|
|
126776
|
-
hostSocketPath: () => hostSocketPath,
|
|
126777
|
-
listenForStreamers: () => listenForStreamers,
|
|
126778
|
-
socketTransport: () => socketTransport
|
|
126779
|
-
});
|
|
126780
|
-
function hostSocketPath(instanceId) {
|
|
126781
|
-
if (process.platform === "win32") {
|
|
126782
|
-
return `\\\\.\\pipe\\threadbase-pty-host-${instanceId}`;
|
|
126783
|
-
}
|
|
126784
|
-
const dir = process.env.THREADBASE_CONFIG_DIR ?? (0, import_node_path27.join)((0, import_node_os6.homedir)(), ".threadbase");
|
|
126785
|
-
return (0, import_node_path27.join)(dir, "run", `pty-host-${instanceId}.sock`);
|
|
126786
|
-
}
|
|
126787
|
-
function socketTransport(socket) {
|
|
126788
|
-
socket.setEncoding("utf8");
|
|
126789
|
-
return {
|
|
126790
|
-
send(line) {
|
|
126791
|
-
socket.write(line);
|
|
126792
|
-
},
|
|
126793
|
-
onLine(handler) {
|
|
126794
|
-
socket.on("data", (chunk) => handler(chunk));
|
|
126795
|
-
},
|
|
126796
|
-
onClose(handler) {
|
|
126797
|
-
socket.once("close", handler);
|
|
126798
|
-
socket.once("error", handler);
|
|
126799
|
-
},
|
|
126800
|
-
close() {
|
|
126801
|
-
socket.destroy();
|
|
126802
|
-
}
|
|
126803
|
-
};
|
|
126804
|
-
}
|
|
126805
|
-
function connectToHost(socketPath) {
|
|
126806
|
-
return new Promise((resolve4, reject) => {
|
|
126807
|
-
const socket = (0, import_node_net2.createConnection)(socketPath);
|
|
126808
|
-
socket.once("error", reject);
|
|
126809
|
-
socket.once("connect", () => {
|
|
126810
|
-
socket.removeListener("error", reject);
|
|
126811
|
-
resolve4(socketTransport(socket));
|
|
126812
|
-
});
|
|
126813
|
-
});
|
|
126814
|
-
}
|
|
126815
|
-
async function listenForStreamers(socketPath, handlers) {
|
|
126816
|
-
if (process.platform !== "win32") {
|
|
126817
|
-
const { mkdirSync: mkdirSync16, rmSync: rmSync6 } = await import("fs");
|
|
126818
|
-
const { dirname: dirname18 } = await import("path");
|
|
126819
|
-
mkdirSync16(dirname18(socketPath), { recursive: true });
|
|
126820
|
-
try {
|
|
126821
|
-
const probe = await connectToHost(socketPath);
|
|
126822
|
-
probe.close();
|
|
126823
|
-
throw new Error(`a pty-host is already listening on ${socketPath}`);
|
|
126824
|
-
} catch (err) {
|
|
126825
|
-
if (err.message?.startsWith("a pty-host is already listening")) throw err;
|
|
126826
|
-
rmSync6(socketPath, { force: true });
|
|
126827
|
-
}
|
|
126828
|
-
}
|
|
126829
|
-
const server = (0, import_node_net2.createServer)((socket) => {
|
|
126830
|
-
const transport = socketTransport(socket);
|
|
126831
|
-
const dispose = handlers.onConnection(transport);
|
|
126832
|
-
socket.once("close", dispose);
|
|
126833
|
-
});
|
|
126834
|
-
return new Promise((resolve4, reject) => {
|
|
126835
|
-
server.once("error", reject);
|
|
126836
|
-
server.listen(socketPath, () => {
|
|
126837
|
-
server.removeListener("error", reject);
|
|
126838
|
-
resolve4(server);
|
|
126839
|
-
});
|
|
126840
|
-
});
|
|
126841
|
-
}
|
|
126842
|
-
function broadcast(transports, message) {
|
|
126843
|
-
const line = encodeMessage(message);
|
|
126844
|
-
for (const t of transports) {
|
|
126845
|
-
try {
|
|
126846
|
-
t.send(line);
|
|
126847
|
-
} catch {
|
|
126848
|
-
}
|
|
126849
|
-
}
|
|
126850
|
-
}
|
|
126851
|
-
var import_node_net2, import_node_os6, import_node_path27;
|
|
126852
|
-
var init_socket = __esm({
|
|
126853
|
-
"src/pty-host/socket.ts"() {
|
|
126854
|
-
"use strict";
|
|
126855
|
-
import_node_net2 = require("net");
|
|
126856
|
-
import_node_os6 = require("os");
|
|
126857
|
-
import_node_path27 = require("path");
|
|
126858
|
-
init_protocol();
|
|
126859
|
-
}
|
|
126860
|
-
});
|
|
126861
|
-
|
|
126862
127455
|
// node_modules/dotenv/config.js
|
|
126863
127456
|
(function() {
|
|
126864
127457
|
require_main().config(
|
|
@@ -134628,7 +135221,7 @@ var REF_PREFIX = "ref: refs/heads/";
|
|
|
134628
135221
|
var MAX_DEPTH = 6;
|
|
134629
135222
|
function readGitBranch(projectPath) {
|
|
134630
135223
|
if (!projectPath) return null;
|
|
134631
|
-
const
|
|
135224
|
+
const log13 = getLogger2();
|
|
134632
135225
|
let dir = projectPath;
|
|
134633
135226
|
let depth = 0;
|
|
134634
135227
|
while (depth < MAX_DEPTH) {
|
|
@@ -134637,11 +135230,11 @@ function readGitBranch(projectPath) {
|
|
|
134637
135230
|
const content = (0, import_fs2.readFileSync)(headPath, "utf-8").trim();
|
|
134638
135231
|
if (content.startsWith(REF_PREFIX)) {
|
|
134639
135232
|
const branch = content.slice(REF_PREFIX.length);
|
|
134640
|
-
|
|
135233
|
+
log13.trace({ projectPath, dir, branch }, "git: branch resolved");
|
|
134641
135234
|
return branch;
|
|
134642
135235
|
}
|
|
134643
135236
|
if (content.length >= 7) {
|
|
134644
|
-
|
|
135237
|
+
log13.trace({ projectPath, dir }, "git: detached HEAD");
|
|
134645
135238
|
return "(detached)";
|
|
134646
135239
|
}
|
|
134647
135240
|
return null;
|
|
@@ -134652,7 +135245,7 @@ function readGitBranch(projectPath) {
|
|
|
134652
135245
|
dir = parent;
|
|
134653
135246
|
depth++;
|
|
134654
135247
|
}
|
|
134655
|
-
|
|
135248
|
+
log13.trace({ projectPath }, "git: no .git found within depth");
|
|
134656
135249
|
return null;
|
|
134657
135250
|
}
|
|
134658
135251
|
function generateMatches(meta3, query) {
|
|
@@ -134945,8 +135538,8 @@ function cleanSystemTags(text) {
|
|
|
134945
135538
|
return text.replace(SYSTEM_TAG_RE, "").replace(/[^\S\n]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
|
|
134946
135539
|
}
|
|
134947
135540
|
async function parseMeta(filePath, account, tier) {
|
|
134948
|
-
const
|
|
134949
|
-
|
|
135541
|
+
const log13 = getLogger2();
|
|
135542
|
+
log13.trace({ filePath, account, tier: tier.name }, "parseMeta: start");
|
|
134950
135543
|
const state = initialReducerState();
|
|
134951
135544
|
const fileStream = (0, import_fs3.createReadStream)(filePath);
|
|
134952
135545
|
const rl = (0, import_readline.createInterface)({ input: fileStream, crlfDelay: Infinity });
|
|
@@ -134963,22 +135556,22 @@ async function parseMeta(filePath, account, tier) {
|
|
|
134963
135556
|
reduceLine(state, entry, tier);
|
|
134964
135557
|
}
|
|
134965
135558
|
} catch (err) {
|
|
134966
|
-
|
|
135559
|
+
log13.warn({ filePath, err }, "parseMeta: read failed");
|
|
134967
135560
|
return null;
|
|
134968
135561
|
}
|
|
134969
135562
|
if (state.badJsonLines > 0) {
|
|
134970
|
-
|
|
135563
|
+
log13.warn(
|
|
134971
135564
|
{ filePath, badJsonLines: state.badJsonLines },
|
|
134972
135565
|
"parseMeta: skipped malformed JSON lines"
|
|
134973
135566
|
);
|
|
134974
135567
|
}
|
|
134975
135568
|
const meta3 = finalizeMeta(state, filePath, account, tier);
|
|
134976
|
-
if (!meta3)
|
|
135569
|
+
if (!meta3) log13.trace({ filePath }, "parseMeta: no messages");
|
|
134977
135570
|
return meta3;
|
|
134978
135571
|
}
|
|
134979
135572
|
async function parseConversation(filePath, account) {
|
|
134980
|
-
const
|
|
134981
|
-
|
|
135573
|
+
const log13 = getLogger2();
|
|
135574
|
+
log13.trace({ filePath, account }, "parseConversation: start");
|
|
134982
135575
|
const messages = [];
|
|
134983
135576
|
let badJsonLines = 0;
|
|
134984
135577
|
const textParts = [];
|
|
@@ -135011,17 +135604,17 @@ async function parseConversation(filePath, account) {
|
|
|
135011
135604
|
}
|
|
135012
135605
|
}
|
|
135013
135606
|
} catch (err) {
|
|
135014
|
-
|
|
135607
|
+
log13.warn({ filePath, err }, "parseConversation: read failed");
|
|
135015
135608
|
return null;
|
|
135016
135609
|
}
|
|
135017
135610
|
if (badJsonLines > 0) {
|
|
135018
|
-
|
|
135611
|
+
log13.warn({ filePath, badJsonLines }, "parseConversation: skipped malformed JSON lines");
|
|
135019
135612
|
}
|
|
135020
135613
|
if (messages.length === 0) {
|
|
135021
|
-
|
|
135614
|
+
log13.trace({ filePath }, "parseConversation: no messages");
|
|
135022
135615
|
return null;
|
|
135023
135616
|
}
|
|
135024
|
-
|
|
135617
|
+
log13.debug({ filePath, messageCount: messages.length }, "parseConversation: complete");
|
|
135025
135618
|
applyTeamInfo(messages, state);
|
|
135026
135619
|
return {
|
|
135027
135620
|
id: filePath,
|
|
@@ -135286,15 +135879,15 @@ async function detectDefaultProfile() {
|
|
|
135286
135879
|
};
|
|
135287
135880
|
}
|
|
135288
135881
|
async function loadProfiles(configPath) {
|
|
135289
|
-
const
|
|
135882
|
+
const log13 = getLogger2();
|
|
135290
135883
|
try {
|
|
135291
135884
|
const resolved = resolveConfigDir(configPath);
|
|
135292
135885
|
const data = await (0, import_promises4.readFile)((0, import_path5.join)(resolved, PROFILES_FILE), "utf-8");
|
|
135293
135886
|
const profiles = JSON.parse(data);
|
|
135294
|
-
|
|
135887
|
+
log13.debug({ configPath, count: profiles.length }, "profiles: loaded");
|
|
135295
135888
|
return profiles;
|
|
135296
135889
|
} catch (err) {
|
|
135297
|
-
|
|
135890
|
+
log13.debug({ configPath, err }, "profiles: load failed, using default");
|
|
135298
135891
|
const defaultProfile = await detectDefaultProfile();
|
|
135299
135892
|
return [defaultProfile];
|
|
135300
135893
|
}
|
|
@@ -135305,7 +135898,7 @@ function canonicalPath(p2) {
|
|
|
135305
135898
|
var CodexCliProvider = class {
|
|
135306
135899
|
name = CODEX_CLI_PROVIDER;
|
|
135307
135900
|
async discover(roots) {
|
|
135308
|
-
const
|
|
135901
|
+
const log13 = getLogger2();
|
|
135309
135902
|
const results = [];
|
|
135310
135903
|
for (const root of roots) {
|
|
135311
135904
|
let paths;
|
|
@@ -135317,7 +135910,7 @@ var CodexCliProvider = class {
|
|
|
135317
135910
|
unique: true
|
|
135318
135911
|
});
|
|
135319
135912
|
} catch (err) {
|
|
135320
|
-
|
|
135913
|
+
log13.warn({ root, err }, "codex discovery: glob failed");
|
|
135321
135914
|
continue;
|
|
135322
135915
|
}
|
|
135323
135916
|
for (const filePath of paths) {
|
|
@@ -135325,7 +135918,7 @@ var CodexCliProvider = class {
|
|
|
135325
135918
|
const s3 = await (0, import_promises5.stat)(filePath);
|
|
135326
135919
|
if (s3.size > 0) results.push({ filePath: canonicalPath(filePath), account: "codex" });
|
|
135327
135920
|
} catch (err) {
|
|
135328
|
-
|
|
135921
|
+
log13.warn({ filePath, err }, "codex discovery: stat failed");
|
|
135329
135922
|
}
|
|
135330
135923
|
}
|
|
135331
135924
|
}
|
|
@@ -135469,7 +136062,7 @@ function getShortProjectName3(fullPath) {
|
|
|
135469
136062
|
return fullPath.split("/").filter(Boolean).slice(-3).join("/");
|
|
135470
136063
|
}
|
|
135471
136064
|
async function parseCodexConversation(filePath, account) {
|
|
135472
|
-
const
|
|
136065
|
+
const log13 = getLogger2();
|
|
135473
136066
|
const messages = [];
|
|
135474
136067
|
const textParts = [];
|
|
135475
136068
|
let sessionId = "";
|
|
@@ -135505,7 +136098,7 @@ async function parseCodexConversation(filePath, account) {
|
|
|
135505
136098
|
if (role === "user") lastUserText = text;
|
|
135506
136099
|
}
|
|
135507
136100
|
} catch (err) {
|
|
135508
|
-
|
|
136101
|
+
log13.warn({ filePath, err }, "parseCodexConversation: read failed");
|
|
135509
136102
|
return null;
|
|
135510
136103
|
}
|
|
135511
136104
|
if (messages.length === 0) return null;
|
|
@@ -135527,7 +136120,7 @@ async function parseCodexConversation(filePath, account) {
|
|
|
135527
136120
|
var EXCLUDED_SEGMENTS = ["/memory/", "/tool-results/"];
|
|
135528
136121
|
var STAT_CONCURRENCY = 32;
|
|
135529
136122
|
async function discoverJsonlFiles(dirs, onProgress) {
|
|
135530
|
-
const
|
|
136123
|
+
const log13 = getLogger2();
|
|
135531
136124
|
const results = [];
|
|
135532
136125
|
for (const { projectsDir, account } of dirs) {
|
|
135533
136126
|
let filePaths;
|
|
@@ -135538,7 +136131,7 @@ async function discoverJsonlFiles(dirs, onProgress) {
|
|
|
135538
136131
|
dot: false
|
|
135539
136132
|
});
|
|
135540
136133
|
} catch (err) {
|
|
135541
|
-
|
|
136134
|
+
log13.warn({ projectsDir, account, err }, "discovery: glob failed");
|
|
135542
136135
|
continue;
|
|
135543
136136
|
}
|
|
135544
136137
|
const filtered = filePaths.filter((fp) => !EXCLUDED_SEGMENTS.some((seg) => fp.includes(seg)));
|
|
@@ -135553,7 +136146,7 @@ async function discoverJsonlFiles(dirs, onProgress) {
|
|
|
135553
136146
|
const s3 = await (0, import_promises6.stat)(filePath);
|
|
135554
136147
|
return { filePath, size: s3.size };
|
|
135555
136148
|
} catch (err) {
|
|
135556
|
-
|
|
136149
|
+
log13.warn({ filePath, err }, "discovery: stat failed");
|
|
135557
136150
|
return { filePath, size: -1 };
|
|
135558
136151
|
}
|
|
135559
136152
|
})
|
|
@@ -135569,7 +136162,7 @@ async function discoverJsonlFiles(dirs, onProgress) {
|
|
|
135569
136162
|
}
|
|
135570
136163
|
}
|
|
135571
136164
|
}
|
|
135572
|
-
|
|
136165
|
+
log13.debug(
|
|
135573
136166
|
{
|
|
135574
136167
|
projectsDir,
|
|
135575
136168
|
account,
|
|
@@ -135583,7 +136176,7 @@ async function discoverJsonlFiles(dirs, onProgress) {
|
|
|
135583
136176
|
);
|
|
135584
136177
|
onProgress?.(results.length);
|
|
135585
136178
|
}
|
|
135586
|
-
|
|
136179
|
+
log13.debug({ totalFiles: results.length, dirs: dirs.length }, "discovery: complete");
|
|
135587
136180
|
return results;
|
|
135588
136181
|
}
|
|
135589
136182
|
var ThreadbaseProvider = class {
|
|
@@ -135893,7 +136486,7 @@ function classify(filePath, existing) {
|
|
|
135893
136486
|
return { change: "reindex", stat: stat42 };
|
|
135894
136487
|
}
|
|
135895
136488
|
async function parseMetaWithProvider(provider, filePath, account, tier) {
|
|
135896
|
-
const
|
|
136489
|
+
const log13 = getLogger2();
|
|
135897
136490
|
const acc = provider.createEmptyAccumulator();
|
|
135898
136491
|
const rl = (0, import_readline3.createInterface)({
|
|
135899
136492
|
input: (0, import_fs11.createReadStream)(filePath),
|
|
@@ -135911,11 +136504,11 @@ async function parseMetaWithProvider(provider, filePath, account, tier) {
|
|
|
135911
136504
|
try {
|
|
135912
136505
|
provider.reduceEntry(acc, entry, tier);
|
|
135913
136506
|
} catch (err) {
|
|
135914
|
-
|
|
136507
|
+
log13.warn({ filePath, provider: provider.name, err }, "provider reduce threw; line skipped");
|
|
135915
136508
|
}
|
|
135916
136509
|
}
|
|
135917
136510
|
} catch (err) {
|
|
135918
|
-
|
|
136511
|
+
log13.warn({ filePath, provider: provider.name, err }, "provider parse: read failed");
|
|
135919
136512
|
return null;
|
|
135920
136513
|
}
|
|
135921
136514
|
return provider.finalize(acc, filePath, account, tier);
|
|
@@ -136089,8 +136682,8 @@ CREATE INDEX IF NOT EXISTS idx_scanned_dirs_parent_root ON scanned_dirs(parent_r
|
|
|
136089
136682
|
function runMigrations(db) {
|
|
136090
136683
|
const current = db.pragma("user_version", { simple: true });
|
|
136091
136684
|
if (current >= SCHEMA_VERSION) return;
|
|
136092
|
-
const
|
|
136093
|
-
|
|
136685
|
+
const log13 = getLogger2();
|
|
136686
|
+
log13.info({ from: current, to: SCHEMA_VERSION }, "migrations: applying");
|
|
136094
136687
|
if (current >= 1 && current < 2 && tableExists(db, "conversations")) {
|
|
136095
136688
|
for (const [col, ddl] of [
|
|
136096
136689
|
[
|
|
@@ -136132,7 +136725,7 @@ function openDatabase(dbPath) {
|
|
|
136132
136725
|
}
|
|
136133
136726
|
var FULL_RECONCILE_EVERY_N_SCANS = 20;
|
|
136134
136727
|
async function discoverJsonlFilesGated(dirs, files, scannedDirs, options = {}) {
|
|
136135
|
-
const
|
|
136728
|
+
const log13 = getLogger2();
|
|
136136
136729
|
if (options.fullRescan) {
|
|
136137
136730
|
return discoverJsonlFiles(dirs);
|
|
136138
136731
|
}
|
|
@@ -136174,7 +136767,7 @@ async function discoverJsonlFilesGated(dirs, files, scannedDirs, options = {}) {
|
|
|
136174
136767
|
}
|
|
136175
136768
|
}
|
|
136176
136769
|
}
|
|
136177
|
-
|
|
136770
|
+
log13.debug(
|
|
136178
136771
|
{ totalFiles: results.length, dirs: dirs.length },
|
|
136179
136772
|
"dir-watermark: gated discovery complete"
|
|
136180
136773
|
);
|
|
@@ -136680,7 +137273,7 @@ var PersistentEngine = class {
|
|
|
136680
137273
|
// changed since the last index, and upsert their metadata. Returns the number
|
|
136681
137274
|
// of files seen on disk this pass (the scan "scanned" count).
|
|
136682
137275
|
async indexAll(activeProfiles, options) {
|
|
136683
|
-
const
|
|
137276
|
+
const log13 = getLogger2();
|
|
136684
137277
|
const tier = resolveTier(options.tier ?? "standard", options.tiers);
|
|
136685
137278
|
const enabled = options.providers ?? [CLAUDE_CODE_PROVIDER];
|
|
136686
137279
|
const discovered = [];
|
|
@@ -136744,7 +137337,7 @@ var PersistentEngine = class {
|
|
|
136744
137337
|
for (const path2 of this.files.activePathsByAccounts([...coveredAccounts])) {
|
|
136745
137338
|
if (!seen.has(path2)) this.markDeleted(path2);
|
|
136746
137339
|
}
|
|
136747
|
-
|
|
137340
|
+
log13.info({ scanned, indexed: this.conversations.count() }, "persistent: indexAll complete");
|
|
136748
137341
|
return { scanned };
|
|
136749
137342
|
}
|
|
136750
137343
|
// (Re)index a single file. Classifies the change vs. the persisted cursor:
|
|
@@ -136755,7 +137348,7 @@ var PersistentEngine = class {
|
|
|
136755
137348
|
// alongside the meta so callers (refreshFile) can keep, extend, or evict
|
|
136756
137349
|
// their own per-file caches without re-stat'ing the file (racy) themselves.
|
|
136757
137350
|
async indexFile(rawFilePath, account, tierName, customTiers, resolveGitBranch, force = false, provider) {
|
|
136758
|
-
const
|
|
137351
|
+
const log13 = getLogger2();
|
|
136759
137352
|
const tier = resolveTier(tierName, customTiers);
|
|
136760
137353
|
const filePath = canonicalPath(rawFilePath);
|
|
136761
137354
|
const existing = this.files.getByPath(filePath);
|
|
@@ -136786,7 +137379,7 @@ var PersistentEngine = class {
|
|
|
136786
137379
|
try {
|
|
136787
137380
|
result = await tailReduce(filePath, startOffset, startLine, state, tier);
|
|
136788
137381
|
} catch (err) {
|
|
136789
|
-
|
|
137382
|
+
log13.warn({ filePath, err }, "persistent: tail read failed");
|
|
136790
137383
|
return { meta: null, change };
|
|
136791
137384
|
}
|
|
136792
137385
|
const meta3 = finalizeMeta(state, filePath, account, tier);
|
|
@@ -136829,7 +137422,7 @@ var PersistentEngine = class {
|
|
|
136829
137422
|
)
|
|
136830
137423
|
);
|
|
136831
137424
|
}
|
|
136832
|
-
|
|
137425
|
+
log13.debug(
|
|
136833
137426
|
{ filePath, change, bytesRead: result.newOffset - startOffset, msgs: meta3.messageCount },
|
|
136834
137427
|
"persistent: indexed file"
|
|
136835
137428
|
);
|
|
@@ -136841,7 +137434,7 @@ var PersistentEngine = class {
|
|
|
136841
137434
|
// so the next pass classifies an unchanged file as "unchanged" and skips it;
|
|
136842
137435
|
// any change reparses from 0 again. No reducer_state is persisted.
|
|
136843
137436
|
async indexFileWithProvider(provider, filePath, account, tier, stat42, resolveGitBranch) {
|
|
136844
|
-
const
|
|
137437
|
+
const log13 = getLogger2();
|
|
136845
137438
|
const meta3 = await parseMetaWithProvider(provider, filePath, account, tier);
|
|
136846
137439
|
if (!meta3) {
|
|
136847
137440
|
this.markDeleted(filePath);
|
|
@@ -136869,7 +137462,7 @@ var PersistentEngine = class {
|
|
|
136869
137462
|
});
|
|
136870
137463
|
});
|
|
136871
137464
|
upsert();
|
|
136872
|
-
|
|
137465
|
+
log13.debug(
|
|
136873
137466
|
{ filePath, provider: provider.name, msgs: meta3.messageCount },
|
|
136874
137467
|
"persistent: indexed provider file"
|
|
136875
137468
|
);
|
|
@@ -136983,7 +137576,7 @@ var FileWatcher = class {
|
|
|
136983
137576
|
// Resolves once every underlying chokidar watcher has finished its initial
|
|
136984
137577
|
// scan, so the caller knows subsequent FS changes will be observed.
|
|
136985
137578
|
async start() {
|
|
136986
|
-
const
|
|
137579
|
+
const log13 = getLogger2();
|
|
136987
137580
|
const ready = [];
|
|
136988
137581
|
for (const profile of this.profiles) {
|
|
136989
137582
|
const dir = getProjectsDir(profile);
|
|
@@ -136995,7 +137588,7 @@ var FileWatcher = class {
|
|
|
136995
137588
|
watcher.on("add", (p2) => this.dispatch(p2, profile.id, "add")).on("change", (p2) => this.dispatch(p2, profile.id, "change")).on("unlink", (p2) => this.dispatch(p2, profile.id, "unlink"));
|
|
136996
137589
|
ready.push(new Promise((resolve4) => watcher.once("ready", () => resolve4())));
|
|
136997
137590
|
this.watchers.push(watcher);
|
|
136998
|
-
|
|
137591
|
+
log13.debug({ dir, account: profile.id }, "watcher: watching");
|
|
136999
137592
|
}
|
|
137000
137593
|
await Promise.all(ready);
|
|
137001
137594
|
}
|
|
@@ -137042,14 +137635,14 @@ var IndexQueue = class {
|
|
|
137042
137635
|
async drain() {
|
|
137043
137636
|
if (this.running) return;
|
|
137044
137637
|
this.running = true;
|
|
137045
|
-
const
|
|
137638
|
+
const log13 = getLogger2();
|
|
137046
137639
|
while (this.pending.size > 0) {
|
|
137047
137640
|
const [path2, job] = this.pending.entries().next().value;
|
|
137048
137641
|
this.pending.delete(path2);
|
|
137049
137642
|
try {
|
|
137050
137643
|
await this.process(job);
|
|
137051
137644
|
} catch (err) {
|
|
137052
|
-
|
|
137645
|
+
log13.warn({ filePath: job.filePath, err }, "index-queue: job failed");
|
|
137053
137646
|
}
|
|
137054
137647
|
}
|
|
137055
137648
|
this.running = false;
|
|
@@ -137158,13 +137751,13 @@ var ConversationScanner = class {
|
|
|
137158
137751
|
// active metas and run the identical filter/sort/view/paginate pipeline as
|
|
137159
137752
|
// the in-memory path — guaranteeing an identical ScanResult shape.
|
|
137160
137753
|
async scanPersistent(activeProfiles, options) {
|
|
137161
|
-
const
|
|
137754
|
+
const log13 = getLogger2();
|
|
137162
137755
|
const startedAt = Date.now();
|
|
137163
137756
|
const engine = this.engine();
|
|
137164
137757
|
const { scanned } = await engine.indexAll(activeProfiles, options);
|
|
137165
137758
|
const allMetas = engine.allActive();
|
|
137166
137759
|
const { conversations, total } = this.finalize(allMetas, options);
|
|
137167
|
-
|
|
137760
|
+
log13.info(
|
|
137168
137761
|
{ scanned, kept: allMetas.length, filteredTotal: total, elapsedMs: Date.now() - startedAt },
|
|
137169
137762
|
"scan: complete (persistent)"
|
|
137170
137763
|
);
|
|
@@ -137191,10 +137784,10 @@ var ConversationScanner = class {
|
|
|
137191
137784
|
return { conversations, total };
|
|
137192
137785
|
}
|
|
137193
137786
|
async scanInMemory(activeProfiles, options) {
|
|
137194
|
-
const
|
|
137787
|
+
const log13 = getLogger2();
|
|
137195
137788
|
const startedAt = Date.now();
|
|
137196
137789
|
const tier = this.lastTier;
|
|
137197
|
-
|
|
137790
|
+
log13.info(
|
|
137198
137791
|
{
|
|
137199
137792
|
activeProfiles: activeProfiles.length,
|
|
137200
137793
|
tier: tier.name,
|
|
@@ -137248,7 +137841,7 @@ var ConversationScanner = class {
|
|
|
137248
137841
|
return meta3;
|
|
137249
137842
|
} catch (err) {
|
|
137250
137843
|
parseFailures++;
|
|
137251
|
-
|
|
137844
|
+
log13.warn({ filePath, account, provider: provider.name, err }, "scan: parse threw");
|
|
137252
137845
|
return null;
|
|
137253
137846
|
}
|
|
137254
137847
|
})
|
|
@@ -137268,12 +137861,12 @@ var ConversationScanner = class {
|
|
|
137268
137861
|
options.onBatch?.(batchMetas);
|
|
137269
137862
|
}
|
|
137270
137863
|
scanned += batch.length;
|
|
137271
|
-
|
|
137864
|
+
log13.debug({ scanned, totalFiles, batchKept: batchMetas.length }, "scan: batch complete");
|
|
137272
137865
|
options.onProgress?.(scanned, totalFiles);
|
|
137273
137866
|
}
|
|
137274
137867
|
const { conversations, total } = this.finalize(allMetas, options);
|
|
137275
137868
|
const elapsedMs = Date.now() - startedAt;
|
|
137276
|
-
|
|
137869
|
+
log13.info(
|
|
137277
137870
|
{
|
|
137278
137871
|
totalFiles,
|
|
137279
137872
|
scanned,
|
|
@@ -137287,13 +137880,13 @@ var ConversationScanner = class {
|
|
|
137287
137880
|
return { conversations, total, scanned };
|
|
137288
137881
|
}
|
|
137289
137882
|
async search(query, options = {}) {
|
|
137290
|
-
const
|
|
137291
|
-
|
|
137883
|
+
const log13 = getLogger2();
|
|
137884
|
+
log13.debug({ query, indexSize: this.indexer.getDocumentCount() }, "search: start");
|
|
137292
137885
|
let results;
|
|
137293
137886
|
if (this.persistent) {
|
|
137294
137887
|
const engine = this.engine();
|
|
137295
137888
|
if (engine.conversations.count() === 0) {
|
|
137296
|
-
|
|
137889
|
+
log13.debug("search: persistent index empty, triggering scan");
|
|
137297
137890
|
const profiles = await this.resolveProfiles(options.profiles);
|
|
137298
137891
|
const activeProfiles = profiles.filter((p2) => p2.enabled && p2.scanHistory !== false);
|
|
137299
137892
|
await engine.indexAll(activeProfiles, { ...options, limit: void 0, offset: void 0 });
|
|
@@ -137306,7 +137899,7 @@ var ConversationScanner = class {
|
|
|
137306
137899
|
}));
|
|
137307
137900
|
} else {
|
|
137308
137901
|
if (this.indexer.getDocumentCount() === 0) {
|
|
137309
|
-
|
|
137902
|
+
log13.debug("search: index empty, triggering scan");
|
|
137310
137903
|
await this.scan({ ...options, limit: void 0, offset: void 0 });
|
|
137311
137904
|
}
|
|
137312
137905
|
results = this.indexer.search(query, {
|
|
@@ -137349,23 +137942,23 @@ var ConversationScanner = class {
|
|
|
137349
137942
|
const limit = options.limit ?? 50;
|
|
137350
137943
|
const offset = options.offset ?? 0;
|
|
137351
137944
|
const sliced = results.slice(offset, offset + limit);
|
|
137352
|
-
|
|
137945
|
+
log13.debug({ query, matched: results.length, returned: sliced.length }, "search: complete");
|
|
137353
137946
|
return sliced;
|
|
137354
137947
|
}
|
|
137355
137948
|
async getConversation(id, _options) {
|
|
137356
|
-
const
|
|
137949
|
+
const log13 = getLogger2();
|
|
137357
137950
|
const cid = canonicalPath(id);
|
|
137358
137951
|
const cached4 = this.conversationLRU.get(cid);
|
|
137359
137952
|
if (cached4) {
|
|
137360
|
-
|
|
137953
|
+
log13.debug({ id }, "getConversation: cache hit");
|
|
137361
137954
|
return cached4.conversation;
|
|
137362
137955
|
}
|
|
137363
137956
|
const meta3 = this.persistent ? this.engine().getByIdOrSession(cid) : this.metadataCache.get(cid) ?? this.resolveSessionId(cid);
|
|
137364
137957
|
if (!meta3) {
|
|
137365
|
-
|
|
137958
|
+
log13.debug({ id }, "getConversation: not found in metadata");
|
|
137366
137959
|
return null;
|
|
137367
137960
|
}
|
|
137368
|
-
|
|
137961
|
+
log13.debug({ id, filePath: meta3.filePath }, "getConversation: cache miss, parsing");
|
|
137369
137962
|
try {
|
|
137370
137963
|
if (this.persistent && meta3.provider !== CODEX_CLI_PROVIDER) {
|
|
137371
137964
|
const parsed = await parseConversationResumable(meta3.filePath, meta3.account);
|
|
@@ -137378,7 +137971,7 @@ var ConversationScanner = class {
|
|
|
137378
137971
|
}
|
|
137379
137972
|
return conversation;
|
|
137380
137973
|
} catch (err) {
|
|
137381
|
-
|
|
137974
|
+
log13.warn({ id, filePath: meta3.filePath, err }, "getConversation: parse failed");
|
|
137382
137975
|
return null;
|
|
137383
137976
|
}
|
|
137384
137977
|
}
|
|
@@ -137464,7 +138057,7 @@ var ConversationScanner = class {
|
|
|
137464
138057
|
return refresh;
|
|
137465
138058
|
}
|
|
137466
138059
|
async doRefreshFile(filePath, account) {
|
|
137467
|
-
const
|
|
138060
|
+
const log13 = getLogger2();
|
|
137468
138061
|
if (this.persistent) {
|
|
137469
138062
|
const engine = this.engine();
|
|
137470
138063
|
const previous2 = engine.getByIdOrSession(filePath);
|
|
@@ -137491,7 +138084,7 @@ var ConversationScanner = class {
|
|
|
137491
138084
|
} else if (change === "appended") {
|
|
137492
138085
|
await this.extendCachedConversations(cacheKeys, filePath, meta22.account);
|
|
137493
138086
|
}
|
|
137494
|
-
|
|
138087
|
+
log13.debug({ filePath, change, kept: !!meta22 }, "refreshFile: updated persistent index");
|
|
137495
138088
|
return meta22;
|
|
137496
138089
|
}
|
|
137497
138090
|
const previous = this.metadataCache.get(filePath);
|
|
@@ -137500,7 +138093,7 @@ var ConversationScanner = class {
|
|
|
137500
138093
|
try {
|
|
137501
138094
|
meta3 = await parseMeta(filePath, resolvedAccount, this.lastTier);
|
|
137502
138095
|
} catch (err) {
|
|
137503
|
-
|
|
138096
|
+
log13.warn({ filePath, err }, "refreshFile: parseMeta threw");
|
|
137504
138097
|
meta3 = null;
|
|
137505
138098
|
}
|
|
137506
138099
|
const evict = (m2) => {
|
|
@@ -137516,7 +138109,7 @@ var ConversationScanner = class {
|
|
|
137516
138109
|
this.removeFromSessionIndex(previous);
|
|
137517
138110
|
this.indexer.removeDocument(previous.id);
|
|
137518
138111
|
}
|
|
137519
|
-
|
|
138112
|
+
log13.debug({ filePath }, "refreshFile: dropped (no parseable messages)");
|
|
137520
138113
|
return null;
|
|
137521
138114
|
}
|
|
137522
138115
|
meta3.gitBranch = readGitBranch(meta3.projectPath);
|
|
@@ -137529,7 +138122,7 @@ var ConversationScanner = class {
|
|
|
137529
138122
|
} else {
|
|
137530
138123
|
this.indexer.addDocument(meta3);
|
|
137531
138124
|
}
|
|
137532
|
-
|
|
138125
|
+
log13.debug(
|
|
137533
138126
|
{ filePath, messageCount: meta3.messageCount },
|
|
137534
138127
|
"refreshFile: updated in-memory indexes"
|
|
137535
138128
|
);
|
|
@@ -137938,14 +138531,14 @@ function createConversationWriter(opts) {
|
|
|
137938
138531
|
}
|
|
137939
138532
|
const file2 = (0, import_node_path6.join)(baseDir, `${args.sessionId}.jsonl`);
|
|
137940
138533
|
await (0, import_promises10.mkdir)((0, import_node_path6.dirname)(file2), { recursive: true });
|
|
137941
|
-
const
|
|
138534
|
+
const record3 = {
|
|
137942
138535
|
role: "assistant",
|
|
137943
138536
|
turnId: args.turnId,
|
|
137944
138537
|
content: args.content,
|
|
137945
138538
|
timestamp: Date.now(),
|
|
137946
138539
|
...args.reviewerOverruled ? { reviewerOverruled: true } : {}
|
|
137947
138540
|
};
|
|
137948
|
-
const line = `${JSON.stringify(
|
|
138541
|
+
const line = `${JSON.stringify(record3)}
|
|
137949
138542
|
`;
|
|
137950
138543
|
await (0, import_promises10.appendFile)(file2, line, { encoding: "utf8" });
|
|
137951
138544
|
}
|
|
@@ -142259,21 +142852,53 @@ var createWsRoutes = (deps, upgradeWebSocket) => {
|
|
|
142259
142852
|
};
|
|
142260
142853
|
|
|
142261
142854
|
// src/api/app.ts
|
|
142855
|
+
var ALREADY_HANDLED7 = 597;
|
|
142856
|
+
function summarizeQuery(query) {
|
|
142857
|
+
const keys = Object.keys(query).sort();
|
|
142858
|
+
if (keys.length === 0) return void 0;
|
|
142859
|
+
return keys.map((k2) => `${k2}=${/^-?\d+$/.test(query[k2]) ? query[k2] : "_"}`).join("&");
|
|
142860
|
+
}
|
|
142861
|
+
function countResponseBytes(res) {
|
|
142862
|
+
let bytes = 0;
|
|
142863
|
+
const add = (chunk) => {
|
|
142864
|
+
if (typeof chunk === "string") bytes += Buffer.byteLength(chunk);
|
|
142865
|
+
else if (chunk instanceof Uint8Array) bytes += chunk.byteLength;
|
|
142866
|
+
};
|
|
142867
|
+
const write = res.write;
|
|
142868
|
+
const end = res.end;
|
|
142869
|
+
res.write = function(...args) {
|
|
142870
|
+
add(args[0]);
|
|
142871
|
+
return write.apply(this, args);
|
|
142872
|
+
};
|
|
142873
|
+
res.end = function(...args) {
|
|
142874
|
+
add(args[0]);
|
|
142875
|
+
return end.apply(this, args);
|
|
142876
|
+
};
|
|
142877
|
+
return () => bytes;
|
|
142878
|
+
}
|
|
142262
142879
|
var createHonoApp = (deps, upgradeWebSocket) => {
|
|
142263
142880
|
const app = new Hono2();
|
|
142264
142881
|
const httpLog = getLogger("http");
|
|
142265
142882
|
app.use("*", async (c, next) => {
|
|
142266
142883
|
const start = Date.now();
|
|
142267
142884
|
const ua2 = c.req.header("user-agent") ?? "";
|
|
142885
|
+
const outgoing = c.env?.outgoing;
|
|
142886
|
+
const bytesWritten = outgoing && countResponseBytes(outgoing);
|
|
142268
142887
|
await next();
|
|
142269
142888
|
if (!deps.logMenubarRequests && c.req.header("x-client") === "menubar") return;
|
|
142270
142889
|
const ms2 = Date.now() - start;
|
|
142271
|
-
|
|
142890
|
+
const handled = c.res.status === ALREADY_HANDLED7 && outgoing !== void 0;
|
|
142891
|
+
const status = handled ? outgoing.statusCode : c.res.status;
|
|
142892
|
+
const qs2 = summarizeQuery(c.req.query());
|
|
142893
|
+
const bytes = handled && bytesWritten ? bytesWritten() : void 0;
|
|
142894
|
+
httpLog.info(`[req] ${c.req.method} ${c.req.path} \u2192 ${status} ${ms2}ms`, {
|
|
142272
142895
|
method: c.req.method,
|
|
142273
142896
|
path: c.req.path,
|
|
142274
|
-
status
|
|
142897
|
+
status,
|
|
142275
142898
|
ms: ms2,
|
|
142276
142899
|
ua: ua2,
|
|
142900
|
+
...qs2 ? { qs: qs2 } : {},
|
|
142901
|
+
...bytes === void 0 ? {} : { bytes },
|
|
142277
142902
|
event: "http.request"
|
|
142278
142903
|
});
|
|
142279
142904
|
});
|
|
@@ -142366,6 +142991,75 @@ var import_promises12 = require("fs/promises");
|
|
|
142366
142991
|
var import_path17 = require("path");
|
|
142367
142992
|
var import_promises13 = require("timers/promises");
|
|
142368
142993
|
|
|
142994
|
+
// src/db/query-timing.ts
|
|
142995
|
+
init_logger();
|
|
142996
|
+
var log3 = getLogger("db");
|
|
142997
|
+
var DEFAULT_SLOW_QUERY_MS = 35;
|
|
142998
|
+
var LABEL = /* @__PURE__ */ Symbol("tbQueryLabel");
|
|
142999
|
+
function deriveLabel(sql) {
|
|
143000
|
+
const verb = /^\s*(\w+)/.exec(sql)?.[1]?.toLowerCase() ?? "sql";
|
|
143001
|
+
const table = /(?:from|into|update)\s+([A-Za-z_]\w*)/i.exec(sql)?.[1] ?? "?";
|
|
143002
|
+
return `${verb}:${table}`;
|
|
143003
|
+
}
|
|
143004
|
+
function resolveSlowMs() {
|
|
143005
|
+
const raw2 = process.env.THREADBASE_DB_SLOW_QUERY_MS;
|
|
143006
|
+
if (raw2 === void 0 || raw2 === "") return DEFAULT_SLOW_QUERY_MS;
|
|
143007
|
+
const parsed = Number(raw2);
|
|
143008
|
+
return Number.isFinite(parsed) ? parsed : DEFAULT_SLOW_QUERY_MS;
|
|
143009
|
+
}
|
|
143010
|
+
function record2(label, ms2, rows, slowMs) {
|
|
143011
|
+
if (slowMs > 0 && ms2 >= slowMs) {
|
|
143012
|
+
log3.warn(
|
|
143013
|
+
`[db] slow query ${label} ${ms2.toFixed(1)}ms rows=${rows}`,
|
|
143014
|
+
{ event: "db.slow_query", stmt: label, ms: Math.round(ms2 * 100) / 100, rows },
|
|
143015
|
+
"pino"
|
|
143016
|
+
);
|
|
143017
|
+
return;
|
|
143018
|
+
}
|
|
143019
|
+
if (log3.pino.isLevelEnabled("debug")) {
|
|
143020
|
+
log3.debug(
|
|
143021
|
+
`[db] ${label} ${ms2.toFixed(2)}ms rows=${rows}`,
|
|
143022
|
+
{ event: "db.query", stmt: label, ms: Math.round(ms2 * 100) / 100, rows },
|
|
143023
|
+
"pino"
|
|
143024
|
+
);
|
|
143025
|
+
}
|
|
143026
|
+
}
|
|
143027
|
+
function rowsOf(method, result) {
|
|
143028
|
+
if (method === "all") return Array.isArray(result) ? result.length : 0;
|
|
143029
|
+
if (method === "run") return result?.changes ?? 0;
|
|
143030
|
+
return result === void 0 ? 0 : 1;
|
|
143031
|
+
}
|
|
143032
|
+
function instrumentDatabase(db, options = {}) {
|
|
143033
|
+
const slowMs = options.slowMs ?? resolveSlowMs();
|
|
143034
|
+
const prepare = db.prepare.bind(db);
|
|
143035
|
+
db.prepare = ((sql) => {
|
|
143036
|
+
const stmt = prepare(sql);
|
|
143037
|
+
const box = { label: deriveLabel(sql) };
|
|
143038
|
+
Object.defineProperty(stmt, LABEL, { value: box, configurable: true });
|
|
143039
|
+
for (const method of ["get", "all", "run"]) {
|
|
143040
|
+
const original = stmt[method].bind(stmt);
|
|
143041
|
+
Object.defineProperty(stmt, method, {
|
|
143042
|
+
configurable: true,
|
|
143043
|
+
writable: true,
|
|
143044
|
+
value: (...args) => {
|
|
143045
|
+
const started = performance.now();
|
|
143046
|
+
const result = original(...args);
|
|
143047
|
+
record2(box.label, performance.now() - started, rowsOf(method, result), slowMs);
|
|
143048
|
+
return result;
|
|
143049
|
+
}
|
|
143050
|
+
});
|
|
143051
|
+
}
|
|
143052
|
+
return stmt;
|
|
143053
|
+
});
|
|
143054
|
+
return db;
|
|
143055
|
+
}
|
|
143056
|
+
function labelStatements(statements) {
|
|
143057
|
+
for (const [name, stmt] of Object.entries(statements)) {
|
|
143058
|
+
const box = stmt?.[LABEL];
|
|
143059
|
+
if (box) box.label = name;
|
|
143060
|
+
}
|
|
143061
|
+
}
|
|
143062
|
+
|
|
142369
143063
|
// src/db/sqlite-migrate.ts
|
|
142370
143064
|
var import_fs15 = require("fs");
|
|
142371
143065
|
var import_path15 = require("path");
|
|
@@ -142412,6 +143106,7 @@ function runSqliteMigrations(db, migrationsDir) {
|
|
|
142412
143106
|
}
|
|
142413
143107
|
|
|
142414
143108
|
// src/conversation-cache.ts
|
|
143109
|
+
init_logger();
|
|
142415
143110
|
init_providers();
|
|
142416
143111
|
|
|
142417
143112
|
// src/services/conversations/isAgentConversation.ts
|
|
@@ -142579,6 +143274,7 @@ CREATE TABLE IF NOT EXISTS session_names (
|
|
|
142579
143274
|
updated_at INTEGER NOT NULL
|
|
142580
143275
|
);
|
|
142581
143276
|
`;
|
|
143277
|
+
var cacheLog = getLogger("cache");
|
|
142582
143278
|
var ConversationCache = class _ConversationCache {
|
|
142583
143279
|
db;
|
|
142584
143280
|
tailSize;
|
|
@@ -142808,6 +143504,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
142808
143504
|
"SELECT COUNT(*) as cnt FROM conversation_message_index WHERE conversation_id = ?"
|
|
142809
143505
|
)
|
|
142810
143506
|
};
|
|
143507
|
+
labelStatements(this.stmts);
|
|
142811
143508
|
}
|
|
142812
143509
|
/**
|
|
142813
143510
|
* Expose the underlying handle so projects/cache_metadata repositories can
|
|
@@ -142971,6 +143668,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
142971
143668
|
return walk;
|
|
142972
143669
|
}
|
|
142973
143670
|
async runBackfill(filePath) {
|
|
143671
|
+
const startedAt = performance.now();
|
|
142974
143672
|
const convId = _ConversationCache.conversationIdForFile(filePath);
|
|
142975
143673
|
this.deleteFileIndex(filePath, convId);
|
|
142976
143674
|
this.indexParseState.delete(filePath);
|
|
@@ -143031,6 +143729,18 @@ var ConversationCache = class _ConversationCache {
|
|
|
143031
143729
|
last_message_index: nextIndex - 1
|
|
143032
143730
|
});
|
|
143033
143731
|
this.indexParseState.set(filePath, state);
|
|
143732
|
+
const ms2 = Math.round(performance.now() - startedAt);
|
|
143733
|
+
cacheLog.info(
|
|
143734
|
+
`[cache] offset-index backfilled ${convId} ${ms2}ms`,
|
|
143735
|
+
{
|
|
143736
|
+
event: "offset_index.backfill_ok",
|
|
143737
|
+
conversationId: convId,
|
|
143738
|
+
ms: ms2,
|
|
143739
|
+
rows: nextIndex,
|
|
143740
|
+
bytes: stat7.size
|
|
143741
|
+
},
|
|
143742
|
+
"pino"
|
|
143743
|
+
);
|
|
143034
143744
|
}
|
|
143035
143745
|
/**
|
|
143036
143746
|
* Windowed detail read straight from the offset index — the hot path.
|
|
@@ -143116,7 +143826,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
143116
143826
|
}
|
|
143117
143827
|
static open(dbPath, tailSize = 10, migrationsDir, options) {
|
|
143118
143828
|
(0, import_fs17.mkdirSync)((0, import_path17.dirname)(dbPath), { recursive: true });
|
|
143119
|
-
const db = new import_better_sqlite32.default(dbPath);
|
|
143829
|
+
const db = instrumentDatabase(new import_better_sqlite32.default(dbPath));
|
|
143120
143830
|
db.pragma("journal_mode = WAL");
|
|
143121
143831
|
db.pragma("foreign_keys = ON");
|
|
143122
143832
|
return new _ConversationCache(db, tailSize, migrationsDir, options);
|
|
@@ -144218,7 +144928,7 @@ var RuntimeStore = class _RuntimeStore {
|
|
|
144218
144928
|
}
|
|
144219
144929
|
db;
|
|
144220
144930
|
static open(dbPath, migrationsDir) {
|
|
144221
|
-
const db = new import_better_sqlite33.default(dbPath);
|
|
144931
|
+
const db = instrumentDatabase(new import_better_sqlite33.default(dbPath));
|
|
144222
144932
|
db.pragma("journal_mode = WAL");
|
|
144223
144933
|
runSqliteMigrations(db, migrationsDir ?? resolveMigrationsDir("runtime-migrations"));
|
|
144224
144934
|
return new _RuntimeStore(db);
|
|
@@ -144357,14 +145067,14 @@ var PairTokenStore = class {
|
|
|
144357
145067
|
};
|
|
144358
145068
|
}
|
|
144359
145069
|
consume(token) {
|
|
144360
|
-
const
|
|
144361
|
-
if (!
|
|
144362
|
-
if (Date.now() >
|
|
145070
|
+
const record3 = this.current;
|
|
145071
|
+
if (!record3 || record3.token !== token) return { ok: false, reason: "unknown" };
|
|
145072
|
+
if (Date.now() > record3.expiresAt) {
|
|
144363
145073
|
this.current = null;
|
|
144364
145074
|
return { ok: false, reason: "expired" };
|
|
144365
145075
|
}
|
|
144366
|
-
if (
|
|
144367
|
-
|
|
145076
|
+
if (record3.used) return { ok: false, reason: "used" };
|
|
145077
|
+
record3.used = true;
|
|
144368
145078
|
return { ok: true };
|
|
144369
145079
|
}
|
|
144370
145080
|
peek() {
|
|
@@ -144653,6 +145363,42 @@ async function readGitBranch2(dir) {
|
|
|
144653
145363
|
|
|
144654
145364
|
// src/server.ts
|
|
144655
145365
|
init_providers();
|
|
145366
|
+
init_remote_session_runner();
|
|
145367
|
+
|
|
145368
|
+
// src/pty-host/spawn-host.ts
|
|
145369
|
+
var import_node_child_process3 = require("child_process");
|
|
145370
|
+
init_socket();
|
|
145371
|
+
var HOST_READY_TIMEOUT_MS = 5e3;
|
|
145372
|
+
var HOST_POLL_INTERVAL_MS = 50;
|
|
145373
|
+
async function connectOrSpawnHost(options) {
|
|
145374
|
+
const socketPath = hostSocketPath(options.instanceId);
|
|
145375
|
+
try {
|
|
145376
|
+
return await connectToHost(socketPath);
|
|
145377
|
+
} catch {
|
|
145378
|
+
}
|
|
145379
|
+
spawnDetachedHost(socketPath, options.entryPoint);
|
|
145380
|
+
const deadline = Date.now() + (options.timeoutMs ?? HOST_READY_TIMEOUT_MS);
|
|
145381
|
+
let lastError;
|
|
145382
|
+
while (Date.now() < deadline) {
|
|
145383
|
+
try {
|
|
145384
|
+
return await connectToHost(socketPath);
|
|
145385
|
+
} catch (err) {
|
|
145386
|
+
lastError = err;
|
|
145387
|
+
await new Promise((r) => setTimeout(r, HOST_POLL_INTERVAL_MS));
|
|
145388
|
+
}
|
|
145389
|
+
}
|
|
145390
|
+
throw new Error(
|
|
145391
|
+
`pty-host did not accept a connection on ${socketPath} within ${options.timeoutMs ?? HOST_READY_TIMEOUT_MS}ms` + (lastError instanceof Error ? `: ${lastError.message}` : "")
|
|
145392
|
+
);
|
|
145393
|
+
}
|
|
145394
|
+
function spawnDetachedHost(socketPath, entryPoint) {
|
|
145395
|
+
const child = (0, import_node_child_process3.spawn)(
|
|
145396
|
+
process.execPath,
|
|
145397
|
+
[entryPoint ?? process.argv[1], "pty-host", "--socket", socketPath],
|
|
145398
|
+
{ detached: true, stdio: "ignore" }
|
|
145399
|
+
);
|
|
145400
|
+
child.unref();
|
|
145401
|
+
}
|
|
144656
145402
|
|
|
144657
145403
|
// src/seal.ts
|
|
144658
145404
|
var import_tweetnacl = __toESM(require_nacl_fast(), 1);
|
|
@@ -144752,10 +145498,10 @@ function fingerprintOf(ids) {
|
|
|
144752
145498
|
return `sha256:${(0, import_crypto11.createHash)("sha256").update(sorted.join("\n")).digest("hex")}`;
|
|
144753
145499
|
}
|
|
144754
145500
|
var CacheIntegrityMonitor = class {
|
|
144755
|
-
constructor(cache, wsHub,
|
|
145501
|
+
constructor(cache, wsHub, log13, cacheDir, rescan, runDuringReset) {
|
|
144756
145502
|
this.cache = cache;
|
|
144757
145503
|
this.wsHub = wsHub;
|
|
144758
|
-
this.log =
|
|
145504
|
+
this.log = log13;
|
|
144759
145505
|
this.cacheDir = cacheDir;
|
|
144760
145506
|
this.rescan = rescan;
|
|
144761
145507
|
this.runDuringReset = runDuringReset;
|
|
@@ -145591,7 +146337,7 @@ function deriveProjectChatTitle(input) {
|
|
|
145591
146337
|
var import_node_crypto3 = require("crypto");
|
|
145592
146338
|
var import_node_http2 = require("http2");
|
|
145593
146339
|
init_logger();
|
|
145594
|
-
var
|
|
146340
|
+
var log4 = getLogger("apns");
|
|
145595
146341
|
var APNS_HOST_SANDBOX = "api.sandbox.push.apple.com";
|
|
145596
146342
|
var APNS_MAX_PAYLOAD_BYTES = 4096;
|
|
145597
146343
|
var JWT_TTL_SECONDS = 3e3;
|
|
@@ -145675,7 +146421,7 @@ var ApnsClient = class {
|
|
|
145675
146421
|
}
|
|
145676
146422
|
const session = (0, import_node_http2.connect)(`https://${this.creds.host}`);
|
|
145677
146423
|
session.on("error", (err) => {
|
|
145678
|
-
|
|
146424
|
+
log4.warn("apns.session_error", { event: "apns.session_error", err: String(err) });
|
|
145679
146425
|
});
|
|
145680
146426
|
this.session = session;
|
|
145681
146427
|
return session;
|
|
@@ -145763,7 +146509,7 @@ function truncateLastOutput(raw2) {
|
|
|
145763
146509
|
}
|
|
145764
146510
|
|
|
145765
146511
|
// src/services/push/liveActivityNotifier.ts
|
|
145766
|
-
var
|
|
146512
|
+
var log5 = getLogger("live-activity");
|
|
145767
146513
|
function contentStateForSession(args) {
|
|
145768
146514
|
const status = toLiveActivityStatus(args.session.status);
|
|
145769
146515
|
if (!status) return null;
|
|
@@ -145823,7 +146569,7 @@ var LiveActivityNotifier = class {
|
|
|
145823
146569
|
}
|
|
145824
146570
|
await this.maybeSendName(session);
|
|
145825
146571
|
} catch (err) {
|
|
145826
|
-
|
|
146572
|
+
log5.error("live_activity.notify_failed", {
|
|
145827
146573
|
event: "live_activity.notify_failed",
|
|
145828
146574
|
sessionId: session.id,
|
|
145829
146575
|
status: session.status,
|
|
@@ -145845,7 +146591,7 @@ var LiveActivityNotifier = class {
|
|
|
145845
146591
|
});
|
|
145846
146592
|
this.openActivity.set(session.id, { sessionNameSent: session.sessionName != null });
|
|
145847
146593
|
if (outcome.attempted > 0) {
|
|
145848
|
-
|
|
146594
|
+
log5.info("live_activity.updated", {
|
|
145849
146595
|
event: "live_activity.updated",
|
|
145850
146596
|
sessionId: session.id,
|
|
145851
146597
|
status: contentState.status,
|
|
@@ -145869,7 +146615,7 @@ var LiveActivityNotifier = class {
|
|
|
145869
146615
|
});
|
|
145870
146616
|
open3.sessionNameSent = true;
|
|
145871
146617
|
if (outcome.attempted > 0) {
|
|
145872
|
-
|
|
146618
|
+
log5.info("live_activity.updated", {
|
|
145873
146619
|
event: "live_activity.updated",
|
|
145874
146620
|
sessionId: session.id,
|
|
145875
146621
|
status: contentState.status,
|
|
@@ -145888,7 +146634,7 @@ var LiveActivityNotifier = class {
|
|
|
145888
146634
|
if (!contentState) return;
|
|
145889
146635
|
const outcome = await this.sender.end({ sessionId: session.id, contentState });
|
|
145890
146636
|
if (outcome.attempted > 0) {
|
|
145891
|
-
|
|
146637
|
+
log5.info("live_activity.ended", {
|
|
145892
146638
|
event: "live_activity.ended",
|
|
145893
146639
|
sessionId: session.id,
|
|
145894
146640
|
...outcome
|
|
@@ -145906,7 +146652,7 @@ init_logger();
|
|
|
145906
146652
|
|
|
145907
146653
|
// src/services/push/liveActivitySender.ts
|
|
145908
146654
|
init_logger();
|
|
145909
|
-
var
|
|
146655
|
+
var log6 = getLogger("live-activity");
|
|
145910
146656
|
var ACTIVITY_MAX_LIFETIME_MS = 8 * 60 * 60 * 1e3;
|
|
145911
146657
|
function buildActivityKitPayload(args) {
|
|
145912
146658
|
return {
|
|
@@ -145970,7 +146716,7 @@ var LiveActivitySender = class {
|
|
|
145970
146716
|
);
|
|
145971
146717
|
for (const { row, result, error: error51 } of results) {
|
|
145972
146718
|
if (error51) {
|
|
145973
|
-
|
|
146719
|
+
log6.error("live_activity.send_failed", {
|
|
145974
146720
|
event: "live_activity.send_failed",
|
|
145975
146721
|
sessionId: args.sessionId,
|
|
145976
146722
|
activityId: row.activity_id,
|
|
@@ -145991,7 +146737,7 @@ var LiveActivitySender = class {
|
|
|
145991
146737
|
this.repo.expire(row.token, now);
|
|
145992
146738
|
outcome.retired += 1;
|
|
145993
146739
|
}
|
|
145994
|
-
|
|
146740
|
+
log6.warn("live_activity.send_rejected", {
|
|
145995
146741
|
event: "live_activity.send_rejected",
|
|
145996
146742
|
sessionId: args.sessionId,
|
|
145997
146743
|
activityId: row.activity_id,
|
|
@@ -146036,7 +146782,7 @@ var LiveActivitySender = class {
|
|
|
146036
146782
|
};
|
|
146037
146783
|
|
|
146038
146784
|
// src/services/push/liveActivityRenewal.ts
|
|
146039
|
-
var
|
|
146785
|
+
var log7 = getLogger("live-activity");
|
|
146040
146786
|
var RENEWAL_LEAD_MS = 30 * 60 * 1e3;
|
|
146041
146787
|
var MAX_TIMER_MS = 60 * 60 * 1e3;
|
|
146042
146788
|
function renewalDueAt(row) {
|
|
@@ -146084,7 +146830,7 @@ var LiveActivityRenewalScheduler = class {
|
|
|
146084
146830
|
await this.renew(row, now);
|
|
146085
146831
|
}
|
|
146086
146832
|
} catch (err) {
|
|
146087
|
-
|
|
146833
|
+
log7.error("live_activity.renewal_sweep_failed", {
|
|
146088
146834
|
event: "live_activity.renewal_sweep_failed",
|
|
146089
146835
|
err: String(err)
|
|
146090
146836
|
});
|
|
@@ -146113,7 +146859,7 @@ var LiveActivityRenewalScheduler = class {
|
|
|
146113
146859
|
if (!session || !status) {
|
|
146114
146860
|
this.deps.repo.claimRenewal(row.token, now);
|
|
146115
146861
|
this.deps.repo.expire(row.token, now);
|
|
146116
|
-
|
|
146862
|
+
log7.info("live_activity.renewal_skipped", {
|
|
146117
146863
|
event: "live_activity.renewal_skipped",
|
|
146118
146864
|
sessionId: row.session_id,
|
|
146119
146865
|
activityId: row.activity_id,
|
|
@@ -146148,7 +146894,7 @@ var LiveActivityRenewalScheduler = class {
|
|
|
146148
146894
|
startedAt,
|
|
146149
146895
|
now
|
|
146150
146896
|
});
|
|
146151
|
-
|
|
146897
|
+
log7.info("live_activity.renewed", {
|
|
146152
146898
|
event: "live_activity.renewed",
|
|
146153
146899
|
sessionId: session.id,
|
|
146154
146900
|
activityId: row.activity_id,
|
|
@@ -146158,7 +146904,7 @@ var LiveActivityRenewalScheduler = class {
|
|
|
146158
146904
|
replacementRequested: started
|
|
146159
146905
|
});
|
|
146160
146906
|
} catch (err) {
|
|
146161
|
-
|
|
146907
|
+
log7.error("live_activity.renewal_failed", {
|
|
146162
146908
|
event: "live_activity.renewal_failed",
|
|
146163
146909
|
sessionId: session.id,
|
|
146164
146910
|
activityId: row.activity_id,
|
|
@@ -146683,6 +147429,12 @@ var SessionStore = class {
|
|
|
146683
147429
|
removeManaged(sessionId) {
|
|
146684
147430
|
return this.managed.delete(sessionId);
|
|
146685
147431
|
}
|
|
147432
|
+
/**
|
|
147433
|
+
* The **live** stored record — mutating it mutates the store. Paired with
|
|
147434
|
+
* `get()`, which hands back a throwaway response copy. Prefer
|
|
147435
|
+
* `updateManaged()` for writes; this is for readers that need the internal
|
|
147436
|
+
* shape (Date fields, `rehydrated`, …) rather than the wire shape.
|
|
147437
|
+
*/
|
|
146686
147438
|
getManaged(sessionId) {
|
|
146687
147439
|
return this.managed.get(sessionId) ?? null;
|
|
146688
147440
|
}
|
|
@@ -146692,12 +147444,21 @@ var SessionStore = class {
|
|
|
146692
147444
|
this.discovered.set(proc.pid, proc);
|
|
146693
147445
|
}
|
|
146694
147446
|
}
|
|
147447
|
+
/**
|
|
147448
|
+
* The **live** stored records — mutating an element mutates the store. Paired
|
|
147449
|
+
* with `list()`, which hands back throwaway response copies.
|
|
147450
|
+
*/
|
|
146695
147451
|
listManaged() {
|
|
146696
147452
|
return Array.from(this.managed.values());
|
|
146697
147453
|
}
|
|
146698
147454
|
// Build the session list: live PTY sessions (managed) merged with externally
|
|
146699
147455
|
// discovered Claude processes. Managed sessions keyed by JSONL UUID take
|
|
146700
147456
|
// priority — discovered processes with the same UUID are skipped.
|
|
147457
|
+
//
|
|
147458
|
+
// Returns freshly constructed response objects, NOT references into the
|
|
147459
|
+
// store — hence `Readonly`: writing to one changes nothing, so the compiler
|
|
147460
|
+
// refuses it. Persist state with `updateManaged()`; to decorate a response,
|
|
147461
|
+
// build a new object (`{ ...s, … }`) as `withReconciledLifecycle` does.
|
|
146701
147462
|
list(ptyAttachedIds) {
|
|
146702
147463
|
const results = [];
|
|
146703
147464
|
const seenIds = /* @__PURE__ */ new Set();
|
|
@@ -146713,6 +147474,9 @@ var SessionStore = class {
|
|
|
146713
147474
|
}
|
|
146714
147475
|
return results;
|
|
146715
147476
|
}
|
|
147477
|
+
// A freshly constructed response object, NOT a reference into the store —
|
|
147478
|
+
// hence `Readonly`, for the same reason as `list()` above. Use `getManaged()`
|
|
147479
|
+
// when you want the live record.
|
|
146716
147480
|
get(sessionId, ptyAttachedIds) {
|
|
146717
147481
|
const managed = this.managed.get(sessionId);
|
|
146718
147482
|
if (managed) return managedToResponse(managed, ptyAttachedIds.has(sessionId));
|
|
@@ -146804,8 +147568,8 @@ function managedToResponse(s3, ptyAttached) {
|
|
|
146804
147568
|
// behind it — `resumable`, and `historical` rather than `managed`
|
|
146805
147569
|
// (docs/plans/live-sessions-persistence-plan.md §4, Phase 1).
|
|
146806
147570
|
lifecycle: ptyAttached ? "attached" : s3.rehydrated ? "resumable" : s3.failureReason != null ? "failed" : "completed",
|
|
146807
|
-
lifecycleSource: ptyAttached ? "spawn" : s3.rehydrated ? "reconcile" : "exit",
|
|
146808
|
-
// We
|
|
147571
|
+
lifecycleSource: ptyAttached ? s3.reconciled ? "reconcile" : "spawn" : s3.rehydrated ? "reconcile" : "exit",
|
|
147572
|
+
// We own its PTY, so `status` is the authoritative signal — no inferred
|
|
146809
147573
|
// `activity` is attached for managed sessions.
|
|
146810
147574
|
ownership: s3.rehydrated ? "historical" : "managed",
|
|
146811
147575
|
projectPath: s3.projectPath,
|
|
@@ -148038,7 +148802,7 @@ var StreamerServer = class {
|
|
|
148038
148802
|
if (!this.managedSessionsRepo) return [];
|
|
148039
148803
|
let verdicts = [];
|
|
148040
148804
|
try {
|
|
148041
|
-
const rows = this.managedSessionsRepo.listNonTerminal();
|
|
148805
|
+
const rows = this.managedSessionsRepo.listNonTerminal().filter((row) => !this.ptyManager.hasSession(row.session_id));
|
|
148042
148806
|
if (rows.length === PROBE_SET_MAX) {
|
|
148043
148807
|
this.log.warn(
|
|
148044
148808
|
`[reconcile] probe set hit its cap of ${PROBE_SET_MAX} \u2014 older rows skipped`,
|
|
@@ -148285,6 +149049,26 @@ var StreamerServer = class {
|
|
|
148285
149049
|
if (session.provider !== CODEX_CLI_PROVIDER2) return session.id;
|
|
148286
149050
|
return session.boundConversationId ?? session.projectPath;
|
|
148287
149051
|
}
|
|
149052
|
+
/** Restore registry-only metadata after the host mirror has been adopted. */
|
|
149053
|
+
refreshHostedSessionsFromRegistry() {
|
|
149054
|
+
for (const session of this.ptyManager.listSessions()) {
|
|
149055
|
+
const row = this.managedSessionsRepo?.get(session.id);
|
|
149056
|
+
const merged = {
|
|
149057
|
+
...session,
|
|
149058
|
+
reconciled: true,
|
|
149059
|
+
...row?.project_id != null && { projectId: row.project_id },
|
|
149060
|
+
...row?.session_name != null && { sessionName: row.session_name },
|
|
149061
|
+
...row?.bound_conversation_id != null && {
|
|
149062
|
+
boundConversationId: row.bound_conversation_id
|
|
149063
|
+
},
|
|
149064
|
+
...row?.resumed_from_conversation_id != null && {
|
|
149065
|
+
resumedFromConversationId: row.resumed_from_conversation_id
|
|
149066
|
+
}
|
|
149067
|
+
};
|
|
149068
|
+
this.sessionStore.addManaged(merged);
|
|
149069
|
+
void this.watchConversationFile(session.id, merged.boundConversationId ?? session.id);
|
|
149070
|
+
}
|
|
149071
|
+
}
|
|
148288
149072
|
/**
|
|
148289
149073
|
* Mirror a freshly-spawned session into the durable registry (C1 Phase 2).
|
|
148290
149074
|
*
|
|
@@ -148380,6 +149164,7 @@ var StreamerServer = class {
|
|
|
148380
149164
|
* of waiting on the interval.
|
|
148381
149165
|
*/
|
|
148382
149166
|
reapIdleSessions(now = Date.now()) {
|
|
149167
|
+
if (this.ptyManager.isRemote()) return [];
|
|
148383
149168
|
const reaped = [];
|
|
148384
149169
|
for (const session of this.ptyManager.listSessions()) {
|
|
148385
149170
|
if (session.status === "running") continue;
|
|
@@ -148481,6 +149266,40 @@ var StreamerServer = class {
|
|
|
148481
149266
|
return true;
|
|
148482
149267
|
}
|
|
148483
149268
|
async listen(port, opts) {
|
|
149269
|
+
if (this.featureFlags.ptyHost) {
|
|
149270
|
+
try {
|
|
149271
|
+
let sessions = null;
|
|
149272
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
149273
|
+
const transport = await connectOrSpawnHost({
|
|
149274
|
+
instanceId: process.env.THREADBASE_INSTANCE_ID ?? (0, import_os13.hostname)()
|
|
149275
|
+
});
|
|
149276
|
+
try {
|
|
149277
|
+
sessions = await this.ptyManager.useRemoteRunner(transport);
|
|
149278
|
+
break;
|
|
149279
|
+
} catch (err) {
|
|
149280
|
+
if (!(err instanceof PtyHostProtocolMismatchError) || attempt > 0) throw err;
|
|
149281
|
+
this.log.info(`[pty-host] replaced incompatible protocol ${err.hostVersion}`, {
|
|
149282
|
+
event: "pty_host.protocol_replaced",
|
|
149283
|
+
hostVersion: err.hostVersion,
|
|
149284
|
+
streamerVersion: err.streamerVersion
|
|
149285
|
+
});
|
|
149286
|
+
}
|
|
149287
|
+
}
|
|
149288
|
+
if (!sessions) throw new Error("pty-host replacement did not produce a compatible host");
|
|
149289
|
+
for (const session of sessions) {
|
|
149290
|
+
this.sessionStore.addManaged({ ...session, reconciled: true });
|
|
149291
|
+
}
|
|
149292
|
+
this.log.info(`[pty-host] re-adopted ${sessions.length} live session(s)`, {
|
|
149293
|
+
event: "pty_host.sessions_adopted",
|
|
149294
|
+
sessions: sessions.length
|
|
149295
|
+
});
|
|
149296
|
+
} catch (err) {
|
|
149297
|
+
this.log.error(
|
|
149298
|
+
"[pty-host] could not attach; falling back to in-process PTYs for this run",
|
|
149299
|
+
{ event: "pty_host.attach_failed", err }
|
|
149300
|
+
);
|
|
149301
|
+
}
|
|
149302
|
+
}
|
|
148484
149303
|
const dbConfig = this.disableDb ? null : getDbConfig();
|
|
148485
149304
|
if (dbConfig) {
|
|
148486
149305
|
this.dbPool = await createPool(dbConfig);
|
|
@@ -148495,8 +149314,10 @@ var StreamerServer = class {
|
|
|
148495
149314
|
this.log.info("Database migrations applied", { event: "db.migrations_applied" });
|
|
148496
149315
|
}
|
|
148497
149316
|
await this.bindWithRetry(port);
|
|
148498
|
-
|
|
148499
|
-
|
|
149317
|
+
if (!this.ptyManager.isRemote()) {
|
|
149318
|
+
this.idleReaperTimer = setInterval(() => this.reapIdleSessions(), IDLE_REAP_SWEEP_MS);
|
|
149319
|
+
this.idleReaperTimer.unref?.();
|
|
149320
|
+
}
|
|
148500
149321
|
const warmUp = new Promise((resolveWarm) => {
|
|
148501
149322
|
{
|
|
148502
149323
|
this.log.info(`Streamer server listening on port ${port}`, {
|
|
@@ -148514,6 +149335,15 @@ var StreamerServer = class {
|
|
|
148514
149335
|
{ error: message, abiMismatch, path: this.runtimeDbPath, event: "runtime.open_failed" }
|
|
148515
149336
|
);
|
|
148516
149337
|
}
|
|
149338
|
+
if (this.ptyManager.isRemote()) {
|
|
149339
|
+
this.ptyManager.startRemoteHeartbeat(() => {
|
|
149340
|
+
if (!this.managedSessionsRepo) {
|
|
149341
|
+
return { registryState: "unknown", referencedSessionIds: [] };
|
|
149342
|
+
}
|
|
149343
|
+
const referencedSessionIds = this.ptyManager.listSessions().filter((session) => this.managedSessionsRepo?.get(session.id)?.completed_at == null).map((session) => session.id);
|
|
149344
|
+
return { registryState: "known", referencedSessionIds };
|
|
149345
|
+
});
|
|
149346
|
+
}
|
|
148517
149347
|
try {
|
|
148518
149348
|
this.cache = ConversationCache.open(
|
|
148519
149349
|
(0, import_path29.join)(this.cacheDir, "cache.db"),
|
|
@@ -148590,6 +149420,7 @@ var StreamerServer = class {
|
|
|
148590
149420
|
);
|
|
148591
149421
|
this.scannerPersistenceDisabled = true;
|
|
148592
149422
|
}
|
|
149423
|
+
if (this.ptyManager.isRemote()) this.refreshHostedSessionsFromRegistry();
|
|
148593
149424
|
void this.reconcilePreviousSessions().then(async (v2) => {
|
|
148594
149425
|
const recoverableRows = this.rehydratePreviousSessions(v2);
|
|
148595
149426
|
await this.autoResumePreviousSessions(recoverableRows);
|
|
@@ -148795,7 +149626,8 @@ var StreamerServer = class {
|
|
|
148795
149626
|
}
|
|
148796
149627
|
this.lastAgentChunkAt.clear();
|
|
148797
149628
|
this.terminalSeq.clear();
|
|
148798
|
-
this.
|
|
149629
|
+
if (this.ptyManager.isRemote()) this.ptyManager.dispose();
|
|
149630
|
+
else this.recordShutdownState();
|
|
148799
149631
|
this.markScannerStaleDebounced.cancel();
|
|
148800
149632
|
await Promise.all([...this.inFlightCacheWrites]);
|
|
148801
149633
|
await Promise.all([...this.allScanners].map((s3) => s3.close()));
|
|
@@ -148803,7 +149635,7 @@ var StreamerServer = class {
|
|
|
148803
149635
|
this.scanner = null;
|
|
148804
149636
|
this.cache?.close();
|
|
148805
149637
|
this.runtimeStore?.close();
|
|
148806
|
-
this.ptyManager.dispose();
|
|
149638
|
+
if (!this.ptyManager.isRemote()) this.ptyManager.dispose();
|
|
148807
149639
|
this.fileWatcher.dispose();
|
|
148808
149640
|
this.externalTails.clear();
|
|
148809
149641
|
this.wsHub.dispose();
|
|
@@ -149853,6 +150685,16 @@ var StreamerServer = class {
|
|
|
149853
150685
|
const windowStart = Math.max(0, beforeIndex - scanLimit);
|
|
149854
150686
|
const indexWindow = scanLimit > 0 && !hasAnchor && indexFilePath && this.cache ? this.cache.readMessageWindow(indexFilePath, windowStart, beforeIndex) : null;
|
|
149855
150687
|
if (!indexWindow && indexFilePath && this.cache && !hasAnchor) {
|
|
150688
|
+
this.log.info(
|
|
150689
|
+
`[server] offset-index miss ${id} \u2192 scanner fallback`,
|
|
150690
|
+
{
|
|
150691
|
+
event: "offset_index.miss",
|
|
150692
|
+
conversationId: id,
|
|
150693
|
+
fromIndex: windowStart,
|
|
150694
|
+
toIndex: beforeIndex
|
|
150695
|
+
},
|
|
150696
|
+
"pino"
|
|
150697
|
+
);
|
|
149856
150698
|
this.trackCacheWrite(
|
|
149857
150699
|
this.cache.backfillIndex(indexFilePath).catch((err) => {
|
|
149858
150700
|
this.log.warn("offset-index.backfill_failed", {
|
|
@@ -150127,14 +150969,15 @@ var StreamerServer = class {
|
|
|
150127
150969
|
}
|
|
150128
150970
|
async handleGetSession(sessionId, res) {
|
|
150129
150971
|
if (this.rejectIfWarmingUp(res)) return;
|
|
150130
|
-
const
|
|
150131
|
-
if (
|
|
150132
|
-
|
|
150133
|
-
|
|
150134
|
-
|
|
150135
|
-
|
|
150136
|
-
|
|
150137
|
-
|
|
150972
|
+
const base = this.sessionStore.get(sessionId, this.ptyAttachedIds());
|
|
150973
|
+
if (base) {
|
|
150974
|
+
const reconciled = this.withReconciledLifecycle([base])[0];
|
|
150975
|
+
const session = {
|
|
150976
|
+
...base,
|
|
150977
|
+
...(0, import_fs30.existsSync)(base.projectPath) ? {} : { failureReason: `Project directory not found: ${base.projectPath}` },
|
|
150978
|
+
lifecycle: reconciled.lifecycle,
|
|
150979
|
+
lifecycleSource: reconciled.lifecycleSource
|
|
150980
|
+
};
|
|
150138
150981
|
if (this.ptyManager.hasSession(sessionId)) {
|
|
150139
150982
|
try {
|
|
150140
150983
|
const lines = await this.ptyManager.getOutputLines(sessionId, 10);
|
|
@@ -150297,31 +151140,37 @@ var StreamerServer = class {
|
|
|
150297
151140
|
this.sessionStore.addManaged(session);
|
|
150298
151141
|
this.recordSessionSpawn(session);
|
|
150299
151142
|
void this.watchConversationFile(sessionId, historyId);
|
|
150300
|
-
const response = this.sessionStore.get(session.id, this.ptyAttachedIds());
|
|
150301
151143
|
this.enrichResumedSessionAsync(sessionId, projectPath, conv);
|
|
151144
|
+
const response = this.sessionStore.get(session.id, this.ptyAttachedIds());
|
|
150302
151145
|
return { ok: true, alreadyRunning: false, session, response };
|
|
150303
151146
|
}
|
|
150304
151147
|
enrichResumedSessionAsync(sessionId, projectPath, conv) {
|
|
150305
151148
|
try {
|
|
150306
|
-
|
|
150307
|
-
if (!session) return;
|
|
151149
|
+
if (!this.sessionStore.getManaged(sessionId)) return;
|
|
150308
151150
|
if (conv) {
|
|
150309
|
-
|
|
150310
|
-
|
|
150311
|
-
|
|
150312
|
-
|
|
151151
|
+
this.sessionStore.updateManaged(sessionId, {
|
|
151152
|
+
sessionName: conv.sessionName ?? void 0,
|
|
151153
|
+
messageCount: conv.messageCount ?? 0,
|
|
151154
|
+
account: conv.account ?? void 0,
|
|
151155
|
+
filePath: conv.filePath ?? void 0
|
|
151156
|
+
});
|
|
150313
151157
|
}
|
|
150314
151158
|
if (!this.cache || !this.projectsRepo || !this.conversationsRepo) return;
|
|
150315
151159
|
const cached4 = this.cache.getMetaById(sessionId);
|
|
150316
151160
|
if (cached4) {
|
|
150317
|
-
session.model = cached4.model ?? void 0;
|
|
150318
|
-
session.preview = cached4.preview ?? void 0;
|
|
150319
151161
|
const first = cached4.firstMessage ? JSON.parse(cached4.firstMessage) : null;
|
|
150320
151162
|
const last = cached4.lastMessage ? JSON.parse(cached4.lastMessage) : null;
|
|
150321
|
-
|
|
150322
|
-
|
|
150323
|
-
|
|
150324
|
-
|
|
151163
|
+
this.sessionStore.updateManaged(sessionId, {
|
|
151164
|
+
model: cached4.model ?? void 0,
|
|
151165
|
+
preview: cached4.preview ?? void 0,
|
|
151166
|
+
firstMessageText: first?.text ?? void 0,
|
|
151167
|
+
// parseIsoDateOrNull, not `new Date()`: an unparseable cached
|
|
151168
|
+
// timestamp must land as absent, not as an Invalid Date that
|
|
151169
|
+
// managedToResponse would throw on when it calls .toISOString().
|
|
151170
|
+
firstMessageAt: parseIsoDateOrNull(first?.timestamp) ?? void 0,
|
|
151171
|
+
lastMessageText: last?.text ?? void 0,
|
|
151172
|
+
lastMessageAt: parseIsoDateOrNull(last?.timestamp) ?? void 0
|
|
151173
|
+
});
|
|
150325
151174
|
}
|
|
150326
151175
|
let resolvedProjectId = cached4?.projectId ?? null;
|
|
150327
151176
|
if (!resolvedProjectId) {
|
|
@@ -150333,8 +151182,10 @@ var StreamerServer = class {
|
|
|
150333
151182
|
});
|
|
150334
151183
|
}
|
|
150335
151184
|
if (resolvedProjectId) {
|
|
150336
|
-
|
|
150337
|
-
|
|
151185
|
+
this.sessionStore.updateManaged(sessionId, {
|
|
151186
|
+
projectId: resolvedProjectId,
|
|
151187
|
+
resumedFromConversationId: sessionId
|
|
151188
|
+
});
|
|
150338
151189
|
}
|
|
150339
151190
|
} catch (err) {
|
|
150340
151191
|
console.error(`[enrichResumedSessionAsync] ${sessionId}:`, err);
|
|
@@ -151611,7 +152462,7 @@ function isBrewInstall(scriptPath = process.argv[1] ?? "") {
|
|
|
151611
152462
|
// src/updater/download.ts
|
|
151612
152463
|
var import_node_crypto5 = require("crypto");
|
|
151613
152464
|
var import_node_fs10 = require("fs");
|
|
151614
|
-
var
|
|
152465
|
+
var import_node_path11 = require("path");
|
|
151615
152466
|
var import_promises17 = require("stream/promises");
|
|
151616
152467
|
|
|
151617
152468
|
// src/updater/manifest.ts
|
|
@@ -151671,7 +152522,7 @@ async function downloadAndVerify(opts) {
|
|
|
151671
152522
|
`Manifest references ${artifact.filename} but it is not attached to the release`
|
|
151672
152523
|
);
|
|
151673
152524
|
}
|
|
151674
|
-
(0, import_node_fs10.mkdirSync)((0,
|
|
152525
|
+
(0, import_node_fs10.mkdirSync)((0, import_node_path11.dirname)(targetPath), { recursive: true });
|
|
151675
152526
|
const res = await fetch(asset.browserDownloadUrl);
|
|
151676
152527
|
if (!res.ok || !res.body) {
|
|
151677
152528
|
throw new Error(`Failed to download ${artifact.filename}: ${res.status} ${res.statusText}`);
|
|
@@ -151710,23 +152561,23 @@ async function downloadAndVerify(opts) {
|
|
|
151710
152561
|
}
|
|
151711
152562
|
|
|
151712
152563
|
// src/updater/paths.ts
|
|
151713
|
-
var
|
|
151714
|
-
var
|
|
151715
|
-
var THREADBASE_ROOT = (0,
|
|
151716
|
-
var RELEASES_DIR = (0,
|
|
151717
|
-
var CURRENT_SYMLINK = (0,
|
|
151718
|
-
var DOWNLOAD_DIR = (0,
|
|
152564
|
+
var import_node_os6 = require("os");
|
|
152565
|
+
var import_node_path12 = require("path");
|
|
152566
|
+
var THREADBASE_ROOT = (0, import_node_path12.join)((0, import_node_os6.homedir)(), ".threadbase");
|
|
152567
|
+
var RELEASES_DIR = (0, import_node_path12.join)(THREADBASE_ROOT, "releases");
|
|
152568
|
+
var CURRENT_SYMLINK = (0, import_node_path12.join)(THREADBASE_ROOT, "current");
|
|
152569
|
+
var DOWNLOAD_DIR = (0, import_node_path12.join)(THREADBASE_ROOT, "releases", ".tmp");
|
|
151719
152570
|
function releaseDir(version2) {
|
|
151720
|
-
return (0,
|
|
152571
|
+
return (0, import_node_path12.join)(RELEASES_DIR, version2);
|
|
151721
152572
|
}
|
|
151722
152573
|
function downloadPath(version2, filename) {
|
|
151723
|
-
return (0,
|
|
152574
|
+
return (0, import_node_path12.join)(DOWNLOAD_DIR, `${version2}-${filename}`);
|
|
151724
152575
|
}
|
|
151725
152576
|
|
|
151726
152577
|
// src/updater/restart.ts
|
|
151727
|
-
var
|
|
152578
|
+
var import_node_child_process4 = require("child_process");
|
|
151728
152579
|
var import_node_util3 = require("util");
|
|
151729
|
-
var execFileP = (0, import_node_util3.promisify)(
|
|
152580
|
+
var execFileP = (0, import_node_util3.promisify)(import_node_child_process4.execFile);
|
|
151730
152581
|
var BREW_LAUNCHD_LABEL = "homebrew.mxcl.tb-streamer";
|
|
151731
152582
|
var DEPLOY_LAUNCHD_LABEL = "com.ronen.threadbase";
|
|
151732
152583
|
function resolveDefaultLabel() {
|
|
@@ -151876,11 +152727,11 @@ async function waitForRestartHealth(opts) {
|
|
|
151876
152727
|
|
|
151877
152728
|
// src/updater/stamp-version.ts
|
|
151878
152729
|
var import_node_fs11 = require("fs");
|
|
151879
|
-
var
|
|
152730
|
+
var import_node_path13 = require("path");
|
|
151880
152731
|
function stampVersionTxt(destDir, version2) {
|
|
151881
|
-
const distDir = (0,
|
|
152732
|
+
const distDir = (0, import_node_path13.join)(destDir, "dist");
|
|
151882
152733
|
(0, import_node_fs11.mkdirSync)(distDir, { recursive: true });
|
|
151883
|
-
(0, import_node_fs11.writeFileSync)((0,
|
|
152734
|
+
(0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(distDir, "version.txt"), versionStamp(version2));
|
|
151884
152735
|
}
|
|
151885
152736
|
function versionStamp(version2) {
|
|
151886
152737
|
return `${version2}+update
|
|
@@ -151889,7 +152740,7 @@ function versionStamp(version2) {
|
|
|
151889
152740
|
|
|
151890
152741
|
// src/updater/swap.ts
|
|
151891
152742
|
var import_node_fs12 = require("fs");
|
|
151892
|
-
var
|
|
152743
|
+
var import_node_path14 = require("path");
|
|
151893
152744
|
var import_semver2 = __toESM(require_semver2(), 1);
|
|
151894
152745
|
var KEEP_LAST_N = 2;
|
|
151895
152746
|
function swapCurrent(version2) {
|
|
@@ -151905,7 +152756,7 @@ function swapCurrent(version2) {
|
|
|
151905
152756
|
(0, import_node_fs12.rmSync)(CURRENT_SYMLINK, { recursive: true, force: true });
|
|
151906
152757
|
}
|
|
151907
152758
|
(0, import_node_fs12.renameSync)(tmp, CURRENT_SYMLINK);
|
|
151908
|
-
(0, import_node_fs12.copyFileSync)((0,
|
|
152759
|
+
(0, import_node_fs12.copyFileSync)((0, import_node_path14.join)(CURRENT_SYMLINK, "dist", "cli.cjs"), (0, import_node_path14.join)(THREADBASE_ROOT, "cli.js"));
|
|
151909
152760
|
publishVersionTxt(target, version2);
|
|
151910
152761
|
return;
|
|
151911
152762
|
}
|
|
@@ -151915,17 +152766,17 @@ function swapCurrent(version2) {
|
|
|
151915
152766
|
}
|
|
151916
152767
|
(0, import_node_fs12.symlinkSync)(target, tmpLink);
|
|
151917
152768
|
(0, import_node_fs12.renameSync)(tmpLink, CURRENT_SYMLINK);
|
|
151918
|
-
const cliJs = (0,
|
|
152769
|
+
const cliJs = (0, import_node_path14.join)(THREADBASE_ROOT, "cli.js");
|
|
151919
152770
|
const tmpCliJs = `${cliJs}.new`;
|
|
151920
152771
|
if ((0, import_node_fs12.existsSync)(tmpCliJs) || lstatSafeIsSymlink(tmpCliJs)) (0, import_node_fs12.unlinkSync)(tmpCliJs);
|
|
151921
|
-
(0, import_node_fs12.symlinkSync)((0,
|
|
152772
|
+
(0, import_node_fs12.symlinkSync)((0, import_node_path14.join)(target, "dist", "cli.cjs"), tmpCliJs);
|
|
151922
152773
|
(0, import_node_fs12.renameSync)(tmpCliJs, cliJs);
|
|
151923
152774
|
publishVersionTxt(target, version2);
|
|
151924
152775
|
}
|
|
151925
152776
|
function publishVersionTxt(target, version2) {
|
|
151926
|
-
const dest = (0,
|
|
152777
|
+
const dest = (0, import_node_path14.join)(THREADBASE_ROOT, "version.txt");
|
|
151927
152778
|
try {
|
|
151928
|
-
(0, import_node_fs12.copyFileSync)((0,
|
|
152779
|
+
(0, import_node_fs12.copyFileSync)((0, import_node_path14.join)(target, "dist", "version.txt"), dest);
|
|
151929
152780
|
} catch {
|
|
151930
152781
|
(0, import_node_fs12.writeFileSync)(dest, versionStamp(version2));
|
|
151931
152782
|
}
|
|
@@ -151964,7 +152815,7 @@ var import_fs31 = __toESM(require("fs"), 1);
|
|
|
151964
152815
|
var import_node_events3 = require("events");
|
|
151965
152816
|
var import_node_stream2 = __toESM(require("stream"), 1);
|
|
151966
152817
|
var import_node_string_decoder = require("string_decoder");
|
|
151967
|
-
var
|
|
152818
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
151968
152819
|
var import_node_fs13 = __toESM(require("fs"), 1);
|
|
151969
152820
|
var import_path30 = require("path");
|
|
151970
152821
|
var import_events4 = require("events");
|
|
@@ -151972,27 +152823,27 @@ var import_assert = __toESM(require("assert"), 1);
|
|
|
151972
152823
|
var import_buffer2 = require("buffer");
|
|
151973
152824
|
var Ps = __toESM(require("zlib"), 1);
|
|
151974
152825
|
var import_zlib = __toESM(require("zlib"), 1);
|
|
151975
|
-
var import_node_path15 = require("path");
|
|
151976
152826
|
var import_node_path16 = require("path");
|
|
152827
|
+
var import_node_path17 = require("path");
|
|
151977
152828
|
var import_fs32 = __toESM(require("fs"), 1);
|
|
151978
152829
|
var import_fs33 = __toESM(require("fs"), 1);
|
|
151979
152830
|
var import_path31 = __toESM(require("path"), 1);
|
|
151980
|
-
var
|
|
152831
|
+
var import_node_path18 = require("path");
|
|
151981
152832
|
var import_path32 = __toESM(require("path"), 1);
|
|
151982
152833
|
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
151983
152834
|
var import_node_assert = __toESM(require("assert"), 1);
|
|
151984
152835
|
var import_node_crypto6 = require("crypto");
|
|
151985
152836
|
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
151986
|
-
var
|
|
152837
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
151987
152838
|
var import_fs34 = __toESM(require("fs"), 1);
|
|
151988
152839
|
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
151989
|
-
var
|
|
152840
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
151990
152841
|
var import_node_fs17 = __toESM(require("fs"), 1);
|
|
151991
152842
|
var import_promises18 = __toESM(require("fs/promises"), 1);
|
|
151992
|
-
var
|
|
151993
|
-
var
|
|
152843
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
152844
|
+
var import_node_path22 = require("path");
|
|
151994
152845
|
var import_node_fs18 = __toESM(require("fs"), 1);
|
|
151995
|
-
var
|
|
152846
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
151996
152847
|
var zr = Object.defineProperty;
|
|
151997
152848
|
var Ur = (s3, t) => {
|
|
151998
152849
|
for (var e in t) zr(s3, e, { get: t[e], enumerable: true });
|
|
@@ -152886,12 +153737,12 @@ var F = class {
|
|
|
152886
153737
|
}
|
|
152887
153738
|
};
|
|
152888
153739
|
var un = (s3, t) => {
|
|
152889
|
-
let i = s3, r = "", n, o =
|
|
153740
|
+
let i = s3, r = "", n, o = import_node_path16.posix.parse(s3).root || ".";
|
|
152890
153741
|
if (Buffer.byteLength(i) < 100) n = [i, r, false];
|
|
152891
153742
|
else {
|
|
152892
|
-
r =
|
|
153743
|
+
r = import_node_path16.posix.dirname(i), i = import_node_path16.posix.basename(i);
|
|
152893
153744
|
do
|
|
152894
|
-
Buffer.byteLength(i) <= 100 && Buffer.byteLength(r) <= t ? n = [i, r, false] : Buffer.byteLength(i) > 100 && Buffer.byteLength(r) <= t ? n = [i.slice(0, 99), r, true] : (i =
|
|
153745
|
+
Buffer.byteLength(i) <= 100 && Buffer.byteLength(r) <= t ? n = [i, r, false] : Buffer.byteLength(i) > 100 && Buffer.byteLength(r) <= t ? n = [i.slice(0, 99), r, true] : (i = import_node_path16.posix.join(import_node_path16.posix.basename(r), i), r = import_node_path16.posix.dirname(r));
|
|
152895
153746
|
while (r !== o && n === void 0);
|
|
152896
153747
|
n || (n = [s3.slice(0, 99), "", true]);
|
|
152897
153748
|
}
|
|
@@ -152937,7 +153788,7 @@ var ft = class s {
|
|
|
152937
153788
|
if (t === "") return Buffer.allocUnsafe(0);
|
|
152938
153789
|
let e = Buffer.byteLength(t), i = 512 * Math.ceil(1 + e / 512), r = Buffer.allocUnsafe(i);
|
|
152939
153790
|
for (let n = 0; n < 512; n++) r[n] = 0;
|
|
152940
|
-
new F({ path: ("PaxHeader/" + (0,
|
|
153791
|
+
new F({ path: ("PaxHeader/" + (0, import_node_path17.basename)(this.path ?? "")).slice(0, 99), mode: this.mode || 420, uid: this.uid, gid: this.gid, size: e, mtime: this.mtime, type: this.global ? "GlobalExtendedHeader" : "ExtendedHeader", linkpath: "", uname: this.uname || "", gname: this.gname || "", devmaj: 0, devmin: 0, atime: this.atime, ctime: this.ctime }).encode(r), r.write(t, 512, e, "utf8");
|
|
152941
153792
|
for (let n = e + 512; n < r.length; n++) r[n] = 0;
|
|
152942
153793
|
return r;
|
|
152943
153794
|
}
|
|
@@ -153393,7 +154244,7 @@ var Ct = K(Mn, Bn, (s3) => new rt(s3), (s3) => new rt(s3), (s3, t) => {
|
|
|
153393
154244
|
t?.length && Qi(s3, t), s3.noResume || vn(s3);
|
|
153394
154245
|
});
|
|
153395
154246
|
var Ji = (s3, t, e) => (s3 &= 4095, e && (s3 = (s3 | 384) & -19), t && (s3 & 256 && (s3 |= 64), s3 & 32 && (s3 |= 8), s3 & 4 && (s3 |= 1)), s3);
|
|
153396
|
-
var { isAbsolute: zn, parse: qs } =
|
|
154247
|
+
var { isAbsolute: zn, parse: qs } = import_node_path18.win32;
|
|
153397
154248
|
var ce = (s3) => {
|
|
153398
154249
|
let t = "", e = qs(s3);
|
|
153399
154250
|
for (; zn(s3) || e.root; ) {
|
|
@@ -154102,11 +154953,11 @@ var $n = (s3, t) => {
|
|
|
154102
154953
|
};
|
|
154103
154954
|
var fr = (s3, t) => {
|
|
154104
154955
|
t.forEach((e) => {
|
|
154105
|
-
e.charAt(0) === "@" ? Ct({ file:
|
|
154956
|
+
e.charAt(0) === "@" ? Ct({ file: import_node_path15.default.resolve(s3.cwd, e.slice(1)), sync: true, noResume: true, onReadEntry: (i) => s3.add(i) }) : s3.add(e);
|
|
154106
154957
|
}), s3.end();
|
|
154107
154958
|
};
|
|
154108
154959
|
var dr = async (s3, t) => {
|
|
154109
|
-
for (let e of t) e.charAt(0) === "@" ? await Ct({ file:
|
|
154960
|
+
for (let e of t) e.charAt(0) === "@" ? await Ct({ file: import_node_path15.default.resolve(String(s3.cwd), e.slice(1)), noResume: true, onReadEntry: (i) => {
|
|
154110
154961
|
s3.add(i);
|
|
154111
154962
|
} }) : s3.add(e);
|
|
154112
154963
|
s3.end();
|
|
@@ -154144,13 +154995,13 @@ var Ei = (s3, t, e, i) => {
|
|
|
154144
154995
|
});
|
|
154145
154996
|
};
|
|
154146
154997
|
var io = (s3, t, e, i, r) => {
|
|
154147
|
-
if (t.isDirectory()) Es(
|
|
154998
|
+
if (t.isDirectory()) Es(import_node_path20.default.resolve(s3, t.name), e, i, (n) => {
|
|
154148
154999
|
if (n) return r(n);
|
|
154149
|
-
let o =
|
|
155000
|
+
let o = import_node_path20.default.resolve(s3, t.name);
|
|
154150
155001
|
Ei(o, e, i, r);
|
|
154151
155002
|
});
|
|
154152
155003
|
else {
|
|
154153
|
-
let n =
|
|
155004
|
+
let n = import_node_path20.default.resolve(s3, t.name);
|
|
154154
155005
|
Ei(n, e, i, r);
|
|
154155
155006
|
}
|
|
154156
155007
|
};
|
|
@@ -154171,7 +155022,7 @@ var Es = (s3, t, e, i) => {
|
|
|
154171
155022
|
});
|
|
154172
155023
|
};
|
|
154173
155024
|
var so = (s3, t, e, i) => {
|
|
154174
|
-
t.isDirectory() && ws(
|
|
155025
|
+
t.isDirectory() && ws(import_node_path20.default.resolve(s3, t.name), e, i), ps(import_node_path20.default.resolve(s3, t.name), e, i);
|
|
154175
155026
|
};
|
|
154176
155027
|
var ws = (s3, t, e) => {
|
|
154177
155028
|
let i;
|
|
@@ -154221,12 +155072,12 @@ var gr = (s3, t, e) => {
|
|
|
154221
155072
|
};
|
|
154222
155073
|
if (s3 === d) return no(s3, S2);
|
|
154223
155074
|
if (l) return import_promises18.default.mkdir(s3, { mode: r, recursive: true }).then((E) => S2(null, E ?? void 0), S2);
|
|
154224
|
-
let D = f(
|
|
155075
|
+
let D = f(import_node_path21.default.relative(d, s3)).split("/");
|
|
154225
155076
|
Ss(d, D, r, c, d, void 0, S2);
|
|
154226
155077
|
};
|
|
154227
155078
|
var Ss = (s3, t, e, i, r, n, o) => {
|
|
154228
155079
|
if (t.length === 0) return o(null, n);
|
|
154229
|
-
let h = t.shift(), a = f(
|
|
155080
|
+
let h = t.shift(), a = f(import_node_path21.default.resolve(s3 + "/" + h));
|
|
154230
155081
|
import_node_fs17.default.mkdir(a, e, br(a, t, e, i, r, n, o));
|
|
154231
155082
|
};
|
|
154232
155083
|
var br = (s3, t, e, i, r, n, o) => (h) => {
|
|
@@ -154260,9 +155111,9 @@ var _r = (s3, t) => {
|
|
|
154260
155111
|
};
|
|
154261
155112
|
if (s3 === c) return oo(c), d();
|
|
154262
155113
|
if (a) return d(import_node_fs17.default.mkdirSync(s3, { mode: i, recursive: true }) ?? void 0);
|
|
154263
|
-
let T2 = f(
|
|
155114
|
+
let T2 = f(import_node_path21.default.relative(c, s3)).split("/"), D;
|
|
154264
155115
|
for (let E = T2.shift(), x = c; E && (x += "/" + E); E = T2.shift()) {
|
|
154265
|
-
x = f(
|
|
155116
|
+
x = f(import_node_path21.default.resolve(x));
|
|
154266
155117
|
try {
|
|
154267
155118
|
import_node_fs17.default.mkdirSync(x, i), D = D || x;
|
|
154268
155119
|
} catch {
|
|
@@ -154291,14 +155142,14 @@ var ho = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
|
|
|
154291
155142
|
var ao = ho === "win32";
|
|
154292
155143
|
var lo = (s3) => s3.split("/").slice(0, -1).reduce((e, i) => {
|
|
154293
155144
|
let r = e.at(-1);
|
|
154294
|
-
return r !== void 0 && (i = (0,
|
|
155145
|
+
return r !== void 0 && (i = (0, import_node_path22.join)(r, i)), e.push(i || "/"), e;
|
|
154295
155146
|
}, []);
|
|
154296
155147
|
var yi = class {
|
|
154297
155148
|
#t = /* @__PURE__ */ new Map();
|
|
154298
155149
|
#i = /* @__PURE__ */ new Map();
|
|
154299
155150
|
#s = /* @__PURE__ */ new Set();
|
|
154300
155151
|
reserve(t, e) {
|
|
154301
|
-
t = ao ? ["win32 parallelization disabled"] : t.map((r) => ut((0,
|
|
155152
|
+
t = ao ? ["win32 parallelization disabled"] : t.map((r) => ut((0, import_node_path22.join)(Tr(r))));
|
|
154302
155153
|
let i = new Set(t.map((r) => lo(r)).reduce((r, n) => r.concat(n)));
|
|
154303
155154
|
this.#i.set(e, { dirs: i, paths: t });
|
|
154304
155155
|
for (let r of t) {
|
|
@@ -154438,7 +155289,7 @@ var Xt = class extends rt {
|
|
|
154438
155289
|
if (t.preserveOwner) throw new TypeError("cannot preserve owner in archive and also set owner explicitly");
|
|
154439
155290
|
this.uid = t.uid, this.gid = t.gid, this.setOwner = true;
|
|
154440
155291
|
} else this.uid = void 0, this.gid = void 0, this.setOwner = false;
|
|
154441
|
-
this.preserveOwner = t.preserveOwner === void 0 && typeof t.uid != "number" ? process.getuid?.() === 0 : !!t.preserveOwner, this.processUid = (this.preserveOwner || this.setOwner) && process.getuid ? process.getuid() : void 0, this.processGid = (this.preserveOwner || this.setOwner) && process.getgid ? process.getgid() : void 0, this.maxDepth = typeof t.maxDepth == "number" ? t.maxDepth : mo, this.forceChown = t.forceChown === true, this.win32 = !!t.win32 || Te, this.newer = !!t.newer, this.keep = !!t.keep, this.noMtime = !!t.noMtime, this.preservePaths = !!t.preservePaths, this.unlink = !!t.unlink, this.cwd = f(
|
|
155292
|
+
this.preserveOwner = t.preserveOwner === void 0 && typeof t.uid != "number" ? process.getuid?.() === 0 : !!t.preserveOwner, this.processUid = (this.preserveOwner || this.setOwner) && process.getuid ? process.getuid() : void 0, this.processGid = (this.preserveOwner || this.setOwner) && process.getgid ? process.getgid() : void 0, this.maxDepth = typeof t.maxDepth == "number" ? t.maxDepth : mo, this.forceChown = t.forceChown === true, this.win32 = !!t.win32 || Te, this.newer = !!t.newer, this.keep = !!t.keep, this.noMtime = !!t.noMtime, this.preservePaths = !!t.preservePaths, this.unlink = !!t.unlink, this.cwd = f(import_node_path19.default.resolve(t.cwd || process.cwd())), this.strip = Number(t.strip) || 0, this.processUmask = this.chmod ? typeof t.processUmask == "number" ? t.processUmask : Lr() : 0, this.umask = typeof t.umask == "number" ? t.umask : this.processUmask, this.dmode = t.dmode || 511 & ~this.umask, this.fmode = t.fmode || 438 & ~this.umask, this.on("entry", (e) => this[Dr](e));
|
|
154442
155293
|
}
|
|
154443
155294
|
warn(t, e, i = {}) {
|
|
154444
155295
|
return (t === "TAR_BAD_ARCHIVE" || t === "TAR_ABORT") && (i.recoverable = false), super.warn(t, e, i);
|
|
@@ -154452,7 +155303,7 @@ var Xt = class extends rt {
|
|
|
154452
155303
|
let [n, o] = ce(i), h = o.replaceAll(/\\/g, "/").split("/");
|
|
154453
155304
|
if (h.includes("..") || Te && /^[a-z]:\.\.$/i.test(h[0] ?? "")) {
|
|
154454
155305
|
if (e === "path" || r === "Link") return this.warn("TAR_ENTRY_ERROR", `${e} contains '..'`, { entry: t, [e]: i }), false;
|
|
154455
|
-
let a =
|
|
155306
|
+
let a = import_node_path19.default.posix.dirname(t.path), l = import_node_path19.default.posix.normalize(import_node_path19.default.posix.join(a, h.join("/")));
|
|
154456
155307
|
if (l.startsWith("../") || l === "..") return this.warn("TAR_ENTRY_ERROR", `${e} escapes extraction directory`, { entry: t, [e]: i }), false;
|
|
154457
155308
|
}
|
|
154458
155309
|
return n && (t[e] = String(o), this.warn("TAR_ENTRY_INFO", `stripping ${n} from absolute ${e}`, { entry: t, [e]: i })), true;
|
|
@@ -154470,12 +155321,12 @@ var Xt = class extends rt {
|
|
|
154470
155321
|
}
|
|
154471
155322
|
if (isFinite(this.maxDepth) && i.length > this.maxDepth) return this.warn("TAR_ENTRY_ERROR", "path excessively deep", { entry: t, path: e, depth: i.length, maxDepth: this.maxDepth }), false;
|
|
154472
155323
|
if (!this[Rs](t, "path") || !this[Rs](t, "linkpath")) return false;
|
|
154473
|
-
if (t.absolute =
|
|
155324
|
+
if (t.absolute = import_node_path19.default.isAbsolute(t.path) ? f(import_node_path19.default.resolve(t.path)) : f(import_node_path19.default.resolve(this.cwd, t.path)), !this.preservePaths && typeof t.absolute == "string" && t.absolute.indexOf(this.cwd + "/") !== 0 && t.absolute !== this.cwd) return this.warn("TAR_ENTRY_ERROR", "path escaped extraction target", { entry: t, path: f(t.path), resolvedPath: t.absolute, cwd: this.cwd }), false;
|
|
154474
155325
|
if (t.absolute === this.cwd && t.type !== "Directory" && t.type !== "GNUDumpDir") return false;
|
|
154475
155326
|
if (this.win32) {
|
|
154476
|
-
let { root: r } =
|
|
155327
|
+
let { root: r } = import_node_path19.default.win32.parse(String(t.absolute));
|
|
154477
155328
|
t.absolute = r + ts(String(t.absolute).slice(r.length));
|
|
154478
|
-
let { root: n } =
|
|
155329
|
+
let { root: n } = import_node_path19.default.win32.parse(t.path);
|
|
154479
155330
|
t.path = n + ts(t.path.slice(n.length));
|
|
154480
155331
|
}
|
|
154481
155332
|
return true;
|
|
@@ -154563,13 +155414,13 @@ var Xt = class extends rt {
|
|
|
154563
155414
|
t.unsupported = true, this.warn("TAR_ENTRY_UNSUPPORTED", `unsupported entry type: ${t.type}`, { entry: t }), t.resume();
|
|
154564
155415
|
}
|
|
154565
155416
|
[Ar](t, e) {
|
|
154566
|
-
let i = f(
|
|
155417
|
+
let i = f(import_node_path19.default.relative(this.cwd, import_node_path19.default.resolve(import_node_path19.default.dirname(String(t.absolute)), String(t.linkpath)))).split("/");
|
|
154567
155418
|
this[Re](t, this.cwd, i, () => this[gi](t, String(t.linkpath), "symlink", e), (r) => {
|
|
154568
155419
|
this[O](r, t), e();
|
|
154569
155420
|
});
|
|
154570
155421
|
}
|
|
154571
155422
|
[Ir](t, e) {
|
|
154572
|
-
let i = f(
|
|
155423
|
+
let i = f(import_node_path19.default.resolve(this.cwd, String(t.linkpath))), r = f(String(t.linkpath)).split("/");
|
|
154573
155424
|
this[Re](t, this.cwd, r, () => this[gi](t, i, "link", e), (n) => {
|
|
154574
155425
|
this[O](n, t), e();
|
|
154575
155426
|
});
|
|
@@ -154577,10 +155428,10 @@ var Xt = class extends rt {
|
|
|
154577
155428
|
[Re](t, e, i, r, n) {
|
|
154578
155429
|
let o = i.shift();
|
|
154579
155430
|
if (this.preservePaths || o === void 0) return r();
|
|
154580
|
-
let h =
|
|
155431
|
+
let h = import_node_path19.default.resolve(e, o);
|
|
154581
155432
|
import_node_fs15.default.lstat(h, (a, l) => {
|
|
154582
155433
|
if (a) return r();
|
|
154583
|
-
if (l?.isSymbolicLink()) return n(new St(h,
|
|
155434
|
+
if (l?.isSymbolicLink()) return n(new St(h, import_node_path19.default.resolve(h, i.join("/"))));
|
|
154584
155435
|
this[Re](t, h, i, r, n);
|
|
154585
155436
|
});
|
|
154586
155437
|
}
|
|
@@ -154614,7 +155465,7 @@ var Xt = class extends rt {
|
|
|
154614
155465
|
});
|
|
154615
155466
|
}, n = () => {
|
|
154616
155467
|
if (t.absolute !== this.cwd) {
|
|
154617
|
-
let h = f(
|
|
155468
|
+
let h = f(import_node_path19.default.dirname(String(t.absolute)));
|
|
154618
155469
|
if (h !== this.cwd) return this[yt](h, this.dmode, (a) => {
|
|
154619
155470
|
if (a) {
|
|
154620
155471
|
this[O](a, t), i();
|
|
@@ -154689,7 +155540,7 @@ var xe = class extends Xt {
|
|
|
154689
155540
|
this[Oe] = true;
|
|
154690
155541
|
}
|
|
154691
155542
|
if (t.absolute !== this.cwd) {
|
|
154692
|
-
let n = f(
|
|
155543
|
+
let n = f(import_node_path19.default.dirname(String(t.absolute)));
|
|
154693
155544
|
if (n !== this.cwd) {
|
|
154694
155545
|
let o = this[yt](n, this.dmode);
|
|
154695
155546
|
if (o) return this[O](o, t);
|
|
@@ -154789,10 +155640,10 @@ var xe = class extends Xt {
|
|
|
154789
155640
|
if (this.preservePaths || i.length === 0) return r();
|
|
154790
155641
|
let o = e;
|
|
154791
155642
|
for (let h of i) {
|
|
154792
|
-
o =
|
|
155643
|
+
o = import_node_path19.default.resolve(o, h);
|
|
154793
155644
|
let [a, l] = ye(() => import_node_fs15.default.lstatSync(o));
|
|
154794
155645
|
if (a) return r();
|
|
154795
|
-
if (l.isSymbolicLink()) return n(new St(o,
|
|
155646
|
+
if (l.isSymbolicLink()) return n(new St(o, import_node_path19.default.resolve(e, i.join("/"))));
|
|
154796
155647
|
}
|
|
154797
155648
|
r();
|
|
154798
155649
|
}
|
|
@@ -154896,11 +155747,11 @@ var go = (s3, t) => {
|
|
|
154896
155747
|
};
|
|
154897
155748
|
var bo = (s3, t) => {
|
|
154898
155749
|
t.forEach((e) => {
|
|
154899
|
-
e.charAt(0) === "@" ? Ct({ file:
|
|
155750
|
+
e.charAt(0) === "@" ? Ct({ file: import_node_path23.default.resolve(s3.cwd, e.slice(1)), sync: true, noResume: true, onReadEntry: (i) => s3.add(i) }) : s3.add(e);
|
|
154900
155751
|
}), s3.end();
|
|
154901
155752
|
};
|
|
154902
155753
|
var _o = async (s3, t) => {
|
|
154903
|
-
for (let e of t) e.charAt(0) === "@" ? await Ct({ file:
|
|
155754
|
+
for (let e of t) e.charAt(0) === "@" ? await Ct({ file: import_node_path23.default.resolve(String(s3.cwd), e.slice(1)), noResume: true, onReadEntry: (i) => s3.add(i) }) : s3.add(e);
|
|
154904
155755
|
s3.end();
|
|
154905
155756
|
};
|
|
154906
155757
|
var vt = K(yo, go, () => {
|
|
@@ -154930,8 +155781,8 @@ async function unpackTarball(opts) {
|
|
|
154930
155781
|
|
|
154931
155782
|
// src/updater/update-log.ts
|
|
154932
155783
|
var import_node_fs20 = require("fs");
|
|
154933
|
-
var
|
|
154934
|
-
var LOG_PATH = (0,
|
|
155784
|
+
var import_node_path24 = require("path");
|
|
155785
|
+
var LOG_PATH = (0, import_node_path24.join)(THREADBASE_ROOT, "update.log");
|
|
154935
155786
|
function appendUpdateLog(line) {
|
|
154936
155787
|
try {
|
|
154937
155788
|
(0, import_node_fs20.mkdirSync)(THREADBASE_ROOT, { recursive: true });
|
|
@@ -155046,15 +155897,19 @@ async function runInstall(opts) {
|
|
|
155046
155897
|
}
|
|
155047
155898
|
|
|
155048
155899
|
// cli/prod.ts
|
|
155049
|
-
var
|
|
155900
|
+
var import_node_child_process8 = require("child_process");
|
|
155050
155901
|
var import_node_fs23 = require("fs");
|
|
155902
|
+
init_auth();
|
|
155903
|
+
init_feature_flags();
|
|
155051
155904
|
init_conflict_check();
|
|
155052
155905
|
init_constants();
|
|
155053
155906
|
init_launchd();
|
|
155054
155907
|
init_marker();
|
|
155055
155908
|
init_platform2();
|
|
155056
155909
|
init_logger();
|
|
155057
|
-
|
|
155910
|
+
init_protocol();
|
|
155911
|
+
init_socket();
|
|
155912
|
+
var log9 = getLogger("prod");
|
|
155058
155913
|
function clearSupervisorLogs() {
|
|
155059
155914
|
let paths;
|
|
155060
155915
|
try {
|
|
@@ -155103,9 +155958,10 @@ async function runProdStatus() {
|
|
|
155103
155958
|
marker: readMarker()
|
|
155104
155959
|
};
|
|
155105
155960
|
}
|
|
155106
|
-
async function runProdDoctor(opts) {
|
|
155961
|
+
async function runProdDoctor(opts, deps = {}) {
|
|
155107
155962
|
const findings = [];
|
|
155108
155963
|
const repairs = [];
|
|
155964
|
+
let ptyHost;
|
|
155109
155965
|
const marker = readMarker();
|
|
155110
155966
|
if (marker && !marker.userHeld && !isPidAlive(marker.devPid)) {
|
|
155111
155967
|
findings.push(`stale marker (dev pid ${marker.devPid} dead)`);
|
|
@@ -155129,7 +155985,23 @@ async function runProdDoctor(opts) {
|
|
|
155129
155985
|
);
|
|
155130
155986
|
}
|
|
155131
155987
|
}
|
|
155132
|
-
|
|
155988
|
+
const featureFlags = deps.featureFlags ?? resolveFeatureFlags({ yaml: loadFeatureFlags() });
|
|
155989
|
+
if (featureFlags.ptyHost) {
|
|
155990
|
+
const probe = deps.probePtyHost ?? (() => probePtyHostStatus(hostSocketPath(getInstanceId())));
|
|
155991
|
+
try {
|
|
155992
|
+
ptyHost = await probe();
|
|
155993
|
+
} catch (err) {
|
|
155994
|
+
ptyHost = { reachable: false, error: err instanceof Error ? err.message : String(err) };
|
|
155995
|
+
}
|
|
155996
|
+
if (!ptyHost.reachable) {
|
|
155997
|
+
findings.push(`pty-host is not reachable: ${ptyHost.error}`);
|
|
155998
|
+
} else if (ptyHost.protocolVersion !== PTY_HOST_PROTOCOL_VERSION) {
|
|
155999
|
+
findings.push(
|
|
156000
|
+
`pty-host protocol ${ptyHost.protocolVersion} does not match streamer protocol ${PTY_HOST_PROTOCOL_VERSION}`
|
|
156001
|
+
);
|
|
156002
|
+
}
|
|
156003
|
+
}
|
|
156004
|
+
return { findings, repairs, ...ptyHost ? { ptyHost } : {} };
|
|
155133
156005
|
}
|
|
155134
156006
|
var defaultSpawnTail = ({ files, lines, follow }) => {
|
|
155135
156007
|
const existing = files.filter((f2) => (0, import_node_fs23.existsSync)(f2));
|
|
@@ -155143,7 +156015,7 @@ var defaultSpawnTail = ({ files, lines, follow }) => {
|
|
|
155143
156015
|
if (follow) args.push("-F");
|
|
155144
156016
|
args.push(...existing);
|
|
155145
156017
|
return new Promise((resolve4) => {
|
|
155146
|
-
const child = (0,
|
|
156018
|
+
const child = (0, import_node_child_process8.spawn)("tail", args, { stdio: ["ignore", "inherit", "inherit"] });
|
|
155147
156019
|
child.on("error", (err) => resolve4({ ok: false, message: `tail failed: ${err.message}` }));
|
|
155148
156020
|
child.on("exit", (code) => resolve4({ ok: code === 0 }));
|
|
155149
156021
|
});
|
|
@@ -155211,12 +156083,12 @@ function registerProdCommands(program3) {
|
|
|
155211
156083
|
prod.command("start").description("Restore prod after a user-held suspension").option("--clear-logs", "Clear existing stdout + stderr logs", false).action(async (opts) => {
|
|
155212
156084
|
if (opts.clearLogs === true) clearSupervisorLogs();
|
|
155213
156085
|
const r = await runProdStart();
|
|
155214
|
-
|
|
156086
|
+
log9.info(r.message, void 0, "console");
|
|
155215
156087
|
if (!r.ok) process.exitCode = 1;
|
|
155216
156088
|
});
|
|
155217
156089
|
prod.command("stop").description("Unload the launchd agent (prod will not auto-restart)").action(async () => {
|
|
155218
156090
|
const r = await runProdStop();
|
|
155219
|
-
|
|
156091
|
+
log9.info(r.message, void 0, "console");
|
|
155220
156092
|
});
|
|
155221
156093
|
prod.command("status").description("Report whether prod is supervised, suspended, or down").action(async () => {
|
|
155222
156094
|
const s3 = await runProdStatus();
|
|
@@ -155225,7 +156097,7 @@ function registerProdCommands(program3) {
|
|
|
155225
156097
|
`pid: ${s3.agentPid ?? "(none)"}`,
|
|
155226
156098
|
s3.marker ? `marker: ${s3.marker.userHeld ? "userHeld (intentional stop)" : "dev-suspended"}, devPid=${s3.marker.devPid}, port=${s3.marker.port}, repo=${s3.marker.repoToplevel}` : "marker: none"
|
|
155227
156099
|
];
|
|
155228
|
-
|
|
156100
|
+
log9.info(parts.join("\n "), void 0, "console");
|
|
155229
156101
|
});
|
|
155230
156102
|
prod.command("restart").description("Stop + restart the supervised streamer (re-reads service definition)").option("--clear-logs", "Clear existing stdout + stderr logs", false).action(async (opts) => {
|
|
155231
156103
|
const sup = getSupervisor();
|
|
@@ -155234,17 +156106,24 @@ function registerProdCommands(program3) {
|
|
|
155234
156106
|
sup.bootoutAgent();
|
|
155235
156107
|
sup.bootstrapAgent(specPath, { afterBootout: true });
|
|
155236
156108
|
const what = process.platform === "darwin" ? `agent restarted from ${specPath}` : `task '${TASK_NAME}' restarted`;
|
|
155237
|
-
|
|
156109
|
+
log9.info(what, void 0, "console");
|
|
155238
156110
|
});
|
|
155239
|
-
prod.command("doctor").description("Detect
|
|
156111
|
+
prod.command("doctor").description("Detect stale markers, missing agents, and pty-host health").option("--fix", "Apply repairs (default is dry-run)", false).action(async (opts) => {
|
|
155240
156112
|
const r = await runProdDoctor({ fix: opts.fix === true });
|
|
155241
|
-
|
|
155242
|
-
|
|
156113
|
+
if (r.ptyHost) {
|
|
156114
|
+
log9.info(
|
|
156115
|
+
r.ptyHost.reachable ? `pty-host: reachable, protocol=${r.ptyHost.protocolVersion}, sessions=${r.ptyHost.sessionCount}` : `pty-host: unreachable (${r.ptyHost.error})`,
|
|
156116
|
+
void 0,
|
|
156117
|
+
"console"
|
|
156118
|
+
);
|
|
156119
|
+
}
|
|
156120
|
+
log9.info(`findings: ${r.findings.length === 0 ? "(none)" : ""}`, void 0, "console");
|
|
156121
|
+
for (const f2 of r.findings) log9.info(` - ${f2}`, void 0, "console");
|
|
155243
156122
|
if (r.repairs.length) {
|
|
155244
|
-
|
|
155245
|
-
for (const fix of r.repairs)
|
|
156123
|
+
log9.info(`repairs:`, void 0, "console");
|
|
156124
|
+
for (const fix of r.repairs) log9.info(` - ${fix}`, void 0, "console");
|
|
155246
156125
|
} else if (!opts.fix && r.findings.length > 0) {
|
|
155247
|
-
|
|
156126
|
+
log9.info(`(re-run with --fix to apply repairs)`, void 0, "console");
|
|
155248
156127
|
}
|
|
155249
156128
|
});
|
|
155250
156129
|
prod.command("logs").description("Tail the supervised streamer's stdout + stderr log files").option("-n, --lines <count>", "Seed with the last N lines (default 50)", "50").option("--no-follow", "Print last N lines and exit (do not follow)").option("--errors-only", "Tail only stderr", false).option("--clear", "Truncate stdout + stderr logs in place, then exit", false).action(async (opts) => {
|
|
@@ -155255,14 +156134,14 @@ function registerProdCommands(program3) {
|
|
|
155255
156134
|
errorsOnly: opts.errorsOnly === true,
|
|
155256
156135
|
clear: opts.clear === true
|
|
155257
156136
|
});
|
|
155258
|
-
if (r.message)
|
|
156137
|
+
if (r.message) log9.info(r.message, void 0, "console");
|
|
155259
156138
|
if (!r.ok) process.exitCode = 1;
|
|
155260
156139
|
});
|
|
155261
156140
|
program3.addCommand(prod);
|
|
155262
156141
|
}
|
|
155263
156142
|
|
|
155264
156143
|
// cli/index.ts
|
|
155265
|
-
var
|
|
156144
|
+
var log12 = getLogger("cli");
|
|
155266
156145
|
var program2 = new Command();
|
|
155267
156146
|
program2.name("threadbase-streamer").description("PTY session management, WebSocket streaming, and REST API server for Claude Code").version(getVersion());
|
|
155268
156147
|
program2.command("serve").description("Start the streamer server").option("-p, --port <number>", "Port to listen on", "8766").option("--api-key <key>", "API key for authentication").option("--local-no-auth", "Skip auth for localhost requests", false).option("-v, --verbose", "Verbose output", false).option("--log-menubar-requests", "Log /healthz requests from the menubar app", false).option("--browse-root <path>", "Root directory for file browsing").option(
|
|
@@ -155304,14 +156183,14 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
155304
156183
|
const { checkSqliteAbi: checkSqliteAbi2 } = await Promise.resolve().then(() => (init_check_sqlite_abi(), check_sqlite_abi_exports));
|
|
155305
156184
|
checkSqliteAbi2();
|
|
155306
156185
|
} catch (err) {
|
|
155307
|
-
|
|
156186
|
+
log12.error(err instanceof Error ? err.message : String(err), void 0, "console");
|
|
155308
156187
|
process.exit(1);
|
|
155309
156188
|
}
|
|
155310
156189
|
if (opts.multiAgentFlow) {
|
|
155311
156190
|
process.env.MULTI_AGENT_FLOW = "true";
|
|
155312
156191
|
}
|
|
155313
156192
|
if (opts.defaultPermissionMode !== void 0 && !isPermissionMode(opts.defaultPermissionMode)) {
|
|
155314
|
-
|
|
156193
|
+
log12.error(
|
|
155315
156194
|
`Invalid --default-permission-mode: ${opts.defaultPermissionMode} (expected one of ${PERMISSION_MODES.join(", ")})`,
|
|
155316
156195
|
void 0,
|
|
155317
156196
|
"console"
|
|
@@ -155327,7 +156206,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
155327
156206
|
const value = eq === -1 ? true : entry.slice(eq + 1);
|
|
155328
156207
|
const def = findFlag(id);
|
|
155329
156208
|
if (!def) {
|
|
155330
|
-
|
|
156209
|
+
log12.error(
|
|
155331
156210
|
`Invalid --claude-flag id: ${id} (expected one of ${CLAUDE_FLAGS.map((f2) => f2.id).join(", ")})`,
|
|
155332
156211
|
void 0,
|
|
155333
156212
|
"console"
|
|
@@ -155344,7 +156223,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
155344
156223
|
claudeFlags = validateFlagValues(raw2);
|
|
155345
156224
|
for (const id of Object.keys(raw2)) {
|
|
155346
156225
|
if (!(id in claudeFlags)) {
|
|
155347
|
-
|
|
156226
|
+
log12.error(`Invalid value for --claude-flag ${id}`, void 0, "console");
|
|
155348
156227
|
process.exit(1);
|
|
155349
156228
|
}
|
|
155350
156229
|
}
|
|
@@ -155353,13 +156232,13 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
155353
156232
|
if (Array.isArray(opts.feature) && opts.feature.length > 0) {
|
|
155354
156233
|
const parsed = parseFeatureFlagArgs(opts.feature);
|
|
155355
156234
|
if (parsed.errors.length > 0) {
|
|
155356
|
-
for (const error51 of parsed.errors)
|
|
156235
|
+
for (const error51 of parsed.errors) log12.error(`--feature: ${error51}`, void 0, "console");
|
|
155357
156236
|
process.exit(1);
|
|
155358
156237
|
}
|
|
155359
156238
|
featureFlags = parsed.values;
|
|
155360
156239
|
}
|
|
155361
156240
|
if (opts.defaultEffort !== void 0 && !isEffortLevel(opts.defaultEffort)) {
|
|
155362
|
-
|
|
156241
|
+
log12.error(
|
|
155363
156242
|
`Invalid --default-effort: ${opts.defaultEffort} (expected one of ${EFFORT_LEVELS.join(", ")})`,
|
|
155364
156243
|
void 0,
|
|
155365
156244
|
"console"
|
|
@@ -155370,7 +156249,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
155370
156249
|
if (opts.ptyGracePeriodMs !== void 0) {
|
|
155371
156250
|
const parsed = Number(opts.ptyGracePeriodMs);
|
|
155372
156251
|
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
155373
|
-
|
|
156252
|
+
log12.error(
|
|
155374
156253
|
`Invalid --pty-grace-period-ms: ${opts.ptyGracePeriodMs} (expected a non-negative integer; 0 disables auto-hold)`,
|
|
155375
156254
|
void 0,
|
|
155376
156255
|
"console"
|
|
@@ -155386,11 +156265,17 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
155386
156265
|
const publicUrl = opts.publicUrl ?? loadPublicUrl() ?? null;
|
|
155387
156266
|
const isProdInvocation = opts.prod === true || process.ppid === 1;
|
|
155388
156267
|
if (!isProdInvocation) appendDevSessionMarker();
|
|
156268
|
+
if (isProdInvocation) {
|
|
156269
|
+
const { truncateOversizedLogs: truncateOversizedLogs2 } = await Promise.resolve().then(() => (init_log_cap(), log_cap_exports));
|
|
156270
|
+
for (const f2 of truncateOversizedLogs2()) {
|
|
156271
|
+
log12.info(`truncated oversized log at boot: ${f2}`, { path: f2, event: "log.truncated" });
|
|
156272
|
+
}
|
|
156273
|
+
}
|
|
155389
156274
|
if (process.platform === "darwin") {
|
|
155390
156275
|
const { detectConflictingAgents: detectConflictingAgents2, formatConflictMessage: formatConflictMessage2 } = await Promise.resolve().then(() => (init_conflict_check(), conflict_check_exports));
|
|
155391
156276
|
const conflicts = detectConflictingAgents2();
|
|
155392
156277
|
if (conflicts.length > 0) {
|
|
155393
|
-
|
|
156278
|
+
log12.warn(formatConflictMessage2(conflicts), void 0, "console");
|
|
155394
156279
|
}
|
|
155395
156280
|
}
|
|
155396
156281
|
let resolvedDefaultPermissionMode = opts.defaultPermissionMode;
|
|
@@ -155405,7 +156290,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
155405
156290
|
resolvedAutoResumeOnBoot = await interactiveAutoResumePrompt2();
|
|
155406
156291
|
setAutoResumeOnBoot(resolvedAutoResumeOnBoot);
|
|
155407
156292
|
} else if (resolvedAutoResumeOnBoot === void 0) {
|
|
155408
|
-
|
|
156293
|
+
log12.info(
|
|
155409
156294
|
"auto_resume_on_boot is not set; interrupted sessions will wait for you to resume them.\nSet `auto_resume_on_boot: true` in ~/.threadbase/server.yaml to resume them automatically.",
|
|
155410
156295
|
void 0,
|
|
155411
156296
|
"console"
|
|
@@ -155455,15 +156340,15 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
155455
156340
|
await server.listen(resolvedPort);
|
|
155456
156341
|
{
|
|
155457
156342
|
const v2 = getVersion();
|
|
155458
|
-
|
|
156343
|
+
log12.info(`Threadbase Streamer v${v2}`, { version: v2, port: resolvedPort });
|
|
155459
156344
|
}
|
|
155460
|
-
|
|
156345
|
+
log12.info(`Listening on http://localhost:${resolvedPort}`, {
|
|
155461
156346
|
url: `http://localhost:${resolvedPort}`
|
|
155462
156347
|
});
|
|
155463
|
-
|
|
156348
|
+
log12.info(`WebSocket at ws://localhost:${resolvedPort}/ws`, {
|
|
155464
156349
|
wsUrl: `ws://localhost:${resolvedPort}/ws`
|
|
155465
156350
|
});
|
|
155466
|
-
|
|
156351
|
+
log12.info(`API key: ${apiKey}`, { apiKeyMasked: `${apiKey.slice(0, 6)}\u2026` });
|
|
155467
156352
|
try {
|
|
155468
156353
|
await printServerBanner({
|
|
155469
156354
|
port: resolvedPort,
|
|
@@ -155473,15 +156358,15 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
155473
156358
|
});
|
|
155474
156359
|
} catch (err) {
|
|
155475
156360
|
const message = err instanceof Error ? err.message : String(err);
|
|
155476
|
-
|
|
155477
|
-
|
|
156361
|
+
log12.warn(`(skipped pairing QR: ${message})`, { reason: message });
|
|
156362
|
+
log12.info(
|
|
155478
156363
|
printUrlBanner({ url: resolveServerUrl({ publicUrl, port: resolvedPort }) }),
|
|
155479
156364
|
void 0,
|
|
155480
156365
|
"console"
|
|
155481
156366
|
);
|
|
155482
156367
|
}
|
|
155483
156368
|
const shutdown = async () => {
|
|
155484
|
-
|
|
156369
|
+
log12.info("Shutting down...");
|
|
155485
156370
|
await server.close();
|
|
155486
156371
|
process.exit(0);
|
|
155487
156372
|
};
|
|
@@ -155489,7 +156374,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
155489
156374
|
process.on("SIGINT", shutdown);
|
|
155490
156375
|
process.on("SIGTERM", shutdown);
|
|
155491
156376
|
process.on("uncaughtException", (err) => {
|
|
155492
|
-
|
|
156377
|
+
log12.error(`uncaught: ${err.message}`, {
|
|
155493
156378
|
error: err.message,
|
|
155494
156379
|
stack: err.stack,
|
|
155495
156380
|
event: "process.uncaught"
|
|
@@ -155498,7 +156383,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
155498
156383
|
});
|
|
155499
156384
|
process.on("unhandledRejection", (reason) => {
|
|
155500
156385
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
155501
|
-
|
|
156386
|
+
log12.error(`unhandled rejection: ${msg}`, {
|
|
155502
156387
|
error: msg,
|
|
155503
156388
|
event: "process.unhandled_rejection"
|
|
155504
156389
|
});
|
|
@@ -155523,10 +156408,10 @@ program2.command("cache").description("Manage the local SQLite conversation cach
|
|
|
155523
156408
|
const f2 = dbPath + suffix;
|
|
155524
156409
|
if (existsSync16(f2)) {
|
|
155525
156410
|
rmSync6(f2);
|
|
155526
|
-
|
|
156411
|
+
log12.info(`Deleted ${f2}`, { path: f2 }, "console");
|
|
155527
156412
|
}
|
|
155528
156413
|
}
|
|
155529
|
-
|
|
156414
|
+
log12.info("Cache cleared. Restart the server to rebuild.", void 0, "console");
|
|
155530
156415
|
})
|
|
155531
156416
|
);
|
|
155532
156417
|
program2.command("pair").description("Print a pairing QR code (server must already be running)").option("-p, --port <number>", "Port the server is listening on", "8766").action(async (opts) => {
|
|
@@ -155551,7 +156436,7 @@ program2.command("set-key [key]").description("Set the streamer API key in ~/.th
|
|
|
155551
156436
|
program2.command("update").description("Check for streamer updates from GitHub Releases and install them").option("--check", "Check only; do not install", false).option("--version <version>", "Pin to a specific release tag").option("--allow-major", "Allow a major-version bump", false).option("--force", "Skip the active-session defer check", false).option("--dry-run", "Print what would be installed without writing to disk", false).option("-p, --port <number>", "Port of the running streamer for active-session check", "8766").action(async (opts) => {
|
|
155552
156437
|
const cfg = loadUpdateConfig();
|
|
155553
156438
|
if (!cfg) {
|
|
155554
|
-
|
|
156439
|
+
log12.warn(
|
|
155555
156440
|
`No update config found at ${DEFAULT_CONFIG_PATH}. Create one with at least 'github_repo: owner/name' to enable updates.`,
|
|
155556
156441
|
void 0,
|
|
155557
156442
|
"console"
|
|
@@ -155570,11 +156455,11 @@ program2.command("update").description("Check for streamer updates from GitHub R
|
|
|
155570
156455
|
appendUpdateLog(
|
|
155571
156456
|
`[check] current=${result2.current} latest=${result2.latest ?? "none"} status=${result2.reason}`
|
|
155572
156457
|
);
|
|
155573
|
-
|
|
155574
|
-
|
|
155575
|
-
|
|
155576
|
-
|
|
155577
|
-
|
|
156458
|
+
log12.info(`Current : ${result2.current}`, void 0, "console");
|
|
156459
|
+
log12.info(`Latest : ${result2.latest ?? "(none)"}`, void 0, "console");
|
|
156460
|
+
log12.info(`Channel : ${cfg.channel}`, void 0, "console");
|
|
156461
|
+
log12.info(`Diff : ${result2.diff ?? "(none)"}`, void 0, "console");
|
|
156462
|
+
log12.info(`Status : ${result2.reason}`, void 0, "console");
|
|
155578
156463
|
return;
|
|
155579
156464
|
}
|
|
155580
156465
|
const port = Number.parseInt(opts.port, 10);
|
|
@@ -155590,20 +156475,20 @@ program2.command("update").description("Check for streamer updates from GitHub R
|
|
|
155590
156475
|
});
|
|
155591
156476
|
switch (result.kind) {
|
|
155592
156477
|
case "no-op":
|
|
155593
|
-
|
|
155594
|
-
|
|
155595
|
-
|
|
156478
|
+
log12.info(`Current : ${result.current}`, void 0, "console");
|
|
156479
|
+
log12.info(`Latest : ${result.latest ?? "(none)"}`, void 0, "console");
|
|
156480
|
+
log12.info(`Status : ${result.reason}`, void 0, "console");
|
|
155596
156481
|
break;
|
|
155597
156482
|
case "unsupported-install":
|
|
155598
|
-
|
|
156483
|
+
log12.warn(result.reason, void 0, "console");
|
|
155599
156484
|
process.exitCode = 2;
|
|
155600
156485
|
break;
|
|
155601
156486
|
case "deferred":
|
|
155602
|
-
|
|
156487
|
+
log12.warn(`Deferred: ${result.reason}`, void 0, "console");
|
|
155603
156488
|
process.exitCode = 2;
|
|
155604
156489
|
break;
|
|
155605
156490
|
case "dry-run":
|
|
155606
|
-
|
|
156491
|
+
log12.info(
|
|
155607
156492
|
`Would install ${result.latest} from ${result.tarballUrl}`,
|
|
155608
156493
|
void 0,
|
|
155609
156494
|
"console"
|
|
@@ -155611,28 +156496,28 @@ program2.command("update").description("Check for streamer updates from GitHub R
|
|
|
155611
156496
|
break;
|
|
155612
156497
|
case "installed":
|
|
155613
156498
|
if (result.restart.method.startsWith("failed:")) {
|
|
155614
|
-
|
|
156499
|
+
log12.error(
|
|
155615
156500
|
`Installed ${result.installed} on disk, but the running service was not updated. Restart: ${result.restart.method}.`,
|
|
155616
156501
|
void 0,
|
|
155617
156502
|
"console"
|
|
155618
156503
|
);
|
|
155619
156504
|
process.exitCode = 1;
|
|
155620
156505
|
} else {
|
|
155621
|
-
|
|
156506
|
+
log12.info(
|
|
155622
156507
|
`Installed ${result.installed} (was ${result.previous}). Restart: ${result.restart.method}.`,
|
|
155623
156508
|
void 0,
|
|
155624
156509
|
"console"
|
|
155625
156510
|
);
|
|
155626
156511
|
}
|
|
155627
156512
|
if (result.pruned.length > 0) {
|
|
155628
|
-
|
|
156513
|
+
log12.info(`Pruned old releases: ${result.pruned.join(", ")}`, void 0, "console");
|
|
155629
156514
|
}
|
|
155630
156515
|
break;
|
|
155631
156516
|
}
|
|
155632
156517
|
} catch (err) {
|
|
155633
156518
|
const message = err instanceof Error ? err.message : String(err);
|
|
155634
156519
|
appendUpdateLog(`[error] ${message}`);
|
|
155635
|
-
|
|
156520
|
+
log12.error(`Update failed: ${message}`, { error: message }, "console");
|
|
155636
156521
|
process.exitCode = 1;
|
|
155637
156522
|
}
|
|
155638
156523
|
});
|
|
@@ -155644,21 +156529,32 @@ program2.command("pty-host").description(
|
|
|
155644
156529
|
).action(async (opts) => {
|
|
155645
156530
|
const { SessionHost: SessionHost2 } = await Promise.resolve().then(() => (init_host(), host_exports));
|
|
155646
156531
|
const { listenForStreamers: listenForStreamers2 } = await Promise.resolve().then(() => (init_socket(), socket_exports));
|
|
155647
|
-
const host = new SessionHost2({ logger: getLogger("pty-host") });
|
|
155648
156532
|
let server;
|
|
156533
|
+
let host;
|
|
156534
|
+
host = new SessionHost2({
|
|
156535
|
+
logger: getLogger("pty-host"),
|
|
156536
|
+
onShutdown: () => {
|
|
156537
|
+
host.dispose();
|
|
156538
|
+
server.close(() => process.exit(0));
|
|
156539
|
+
},
|
|
156540
|
+
onOrphaned: () => {
|
|
156541
|
+
host.dispose();
|
|
156542
|
+
server.close(() => process.exit(0));
|
|
156543
|
+
}
|
|
156544
|
+
});
|
|
155649
156545
|
try {
|
|
155650
156546
|
server = await listenForStreamers2(opts.socket, {
|
|
155651
156547
|
onConnection: (transport) => host.accept(transport)
|
|
155652
156548
|
});
|
|
155653
156549
|
} catch (err) {
|
|
155654
|
-
|
|
156550
|
+
log12.error(
|
|
155655
156551
|
`pty-host could not listen on ${opts.socket}: ${err instanceof Error ? err.message : String(err)}`,
|
|
155656
156552
|
void 0,
|
|
155657
156553
|
"console"
|
|
155658
156554
|
);
|
|
155659
156555
|
process.exit(1);
|
|
155660
156556
|
}
|
|
155661
|
-
|
|
156557
|
+
log12.info(`pty-host listening on ${opts.socket}`, {
|
|
155662
156558
|
event: "pty_host.listening",
|
|
155663
156559
|
socket: opts.socket
|
|
155664
156560
|
});
|
|
@@ -155712,7 +156608,7 @@ async function printServerBanner({
|
|
|
155712
156608
|
}) {
|
|
155713
156609
|
const url2 = resolveServerUrl({ publicUrl, port });
|
|
155714
156610
|
if (!includeQr) {
|
|
155715
|
-
|
|
156611
|
+
log12.info(printUrlBanner({ url: url2 }), void 0, "console");
|
|
155716
156612
|
return;
|
|
155717
156613
|
}
|
|
155718
156614
|
const res = await fetch(`http://localhost:${port}/api/pair/start`, {
|
|
@@ -155727,9 +156623,9 @@ async function printServerBanner({
|
|
|
155727
156623
|
const expSeconds = Math.floor(expiresAt / 1e3);
|
|
155728
156624
|
const payload = `threadbase://pair?url=${encodeURIComponent(url2)}&token=${token}&exp=${expSeconds}`;
|
|
155729
156625
|
const qr2 = await generateQr(payload);
|
|
155730
|
-
|
|
155731
|
-
|
|
155732
|
-
|
|
156626
|
+
log12.info(printUrlBanner({ url: url2, qr: qr2, expiresAt }), void 0, "console");
|
|
156627
|
+
log12.info(`Pair URL: ${payload}`, void 0, "console");
|
|
156628
|
+
log12.info(`Expires in ${expiresInSeconds}s`, void 0, "console");
|
|
155733
156629
|
}
|
|
155734
156630
|
/*! Bundled license information:
|
|
155735
156631
|
|