@threadbase-sh/streamer 1.47.3 → 1.48.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 +361 -160
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +214 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +214 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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, log15) {
|
|
4641
4641
|
return {
|
|
4642
4642
|
level,
|
|
4643
4643
|
bindings,
|
|
4644
|
-
log:
|
|
4644
|
+
log: log15
|
|
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: log15 } = options.formatters;
|
|
5027
5027
|
instance[formattersSym] = buildFormatters(
|
|
5028
5028
|
level || formatters.level,
|
|
5029
5029
|
chindings || resetChildingsFormatter,
|
|
5030
|
-
|
|
5030
|
+
log15 || formatters.log
|
|
5031
5031
|
);
|
|
5032
5032
|
} else {
|
|
5033
5033
|
instance[formattersSym] = buildFormatters(
|
|
@@ -8206,7 +8206,7 @@ var require_merge = __commonJS({
|
|
|
8206
8206
|
var require_addPairToJSMap = __commonJS({
|
|
8207
8207
|
"node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports2) {
|
|
8208
8208
|
"use strict";
|
|
8209
|
-
var
|
|
8209
|
+
var log15 = require_log();
|
|
8210
8210
|
var merge2 = require_merge();
|
|
8211
8211
|
var stringify = require_stringify();
|
|
8212
8212
|
var identity = require_identity();
|
|
@@ -8255,7 +8255,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
8255
8255
|
let jsonStr = JSON.stringify(strKey);
|
|
8256
8256
|
if (jsonStr.length > 40)
|
|
8257
8257
|
jsonStr = jsonStr.substring(0, 36) + '..."';
|
|
8258
|
-
|
|
8258
|
+
log15.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.`);
|
|
8259
8259
|
ctx.mapKeyWarned = true;
|
|
8260
8260
|
}
|
|
8261
8261
|
return strKey;
|
|
@@ -13671,7 +13671,7 @@ var require_public_api = __commonJS({
|
|
|
13671
13671
|
var composer = require_composer();
|
|
13672
13672
|
var Document = require_Document();
|
|
13673
13673
|
var errors = require_errors();
|
|
13674
|
-
var
|
|
13674
|
+
var log15 = require_log();
|
|
13675
13675
|
var identity = require_identity();
|
|
13676
13676
|
var lineCounter = require_line_counter();
|
|
13677
13677
|
var parser = require_parser();
|
|
@@ -13723,7 +13723,7 @@ var require_public_api = __commonJS({
|
|
|
13723
13723
|
const doc = parseDocument(src, options);
|
|
13724
13724
|
if (!doc)
|
|
13725
13725
|
return null;
|
|
13726
|
-
doc.warnings.forEach((warning) =>
|
|
13726
|
+
doc.warnings.forEach((warning) => log15.warn(doc.options.logLevel, warning));
|
|
13727
13727
|
if (doc.errors.length > 0) {
|
|
13728
13728
|
if (doc.options.logLevel !== "silent")
|
|
13729
13729
|
throw doc.errors[0];
|
|
@@ -37243,7 +37243,7 @@ var require_logging = __commonJS({
|
|
|
37243
37243
|
_logVerbosity = verbosity;
|
|
37244
37244
|
};
|
|
37245
37245
|
exports2.setLoggerVerbosity = setLoggerVerbosity;
|
|
37246
|
-
var
|
|
37246
|
+
var log15 = (severity, ...args) => {
|
|
37247
37247
|
let logFunction;
|
|
37248
37248
|
if (severity >= _logVerbosity) {
|
|
37249
37249
|
switch (severity) {
|
|
@@ -37265,7 +37265,7 @@ var require_logging = __commonJS({
|
|
|
37265
37265
|
}
|
|
37266
37266
|
}
|
|
37267
37267
|
};
|
|
37268
|
-
exports2.log =
|
|
37268
|
+
exports2.log = log15;
|
|
37269
37269
|
var tracersString = (_d = (_c = process.env.GRPC_NODE_TRACE) !== null && _c !== void 0 ? _c : process.env.GRPC_TRACE) !== null && _d !== void 0 ? _d : "";
|
|
37270
37270
|
var enabledTracers = /* @__PURE__ */ new Set();
|
|
37271
37271
|
var disabledTracers = /* @__PURE__ */ new Set();
|
|
@@ -127026,7 +127026,7 @@ function readMarker() {
|
|
|
127026
127026
|
const parsed = JSON.parse(raw2);
|
|
127027
127027
|
return MarkerSchema.parse(parsed);
|
|
127028
127028
|
} catch (err) {
|
|
127029
|
-
|
|
127029
|
+
log10.warn(`marker at ${markerPath()} is malformed; treating as absent`, {
|
|
127030
127030
|
error: err instanceof Error ? err.message : String(err)
|
|
127031
127031
|
});
|
|
127032
127032
|
return null;
|
|
@@ -127043,7 +127043,7 @@ function writeMarker(marker) {
|
|
|
127043
127043
|
function clearMarker() {
|
|
127044
127044
|
if ((0, import_node_fs22.existsSync)(markerPath())) (0, import_node_fs22.rmSync)(markerPath());
|
|
127045
127045
|
}
|
|
127046
|
-
var import_node_fs22, import_node_path25,
|
|
127046
|
+
var import_node_fs22, import_node_path25, log10;
|
|
127047
127047
|
var init_marker = __esm({
|
|
127048
127048
|
"src/lifecycle/marker.ts"() {
|
|
127049
127049
|
"use strict";
|
|
@@ -127052,7 +127052,7 @@ var init_marker = __esm({
|
|
|
127052
127052
|
init_logger();
|
|
127053
127053
|
init_constants();
|
|
127054
127054
|
init_marker_schema();
|
|
127055
|
-
|
|
127055
|
+
log10 = getLogger("lifecycle.marker");
|
|
127056
127056
|
}
|
|
127057
127057
|
});
|
|
127058
127058
|
|
|
@@ -127292,7 +127292,7 @@ function readPrefs() {
|
|
|
127292
127292
|
try {
|
|
127293
127293
|
return PrefsSchema.parse(JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8")));
|
|
127294
127294
|
} catch (err) {
|
|
127295
|
-
|
|
127295
|
+
log12.warn(`prefs at ${path2} are malformed; treating as empty`, {
|
|
127296
127296
|
error: err instanceof Error ? err.message : String(err)
|
|
127297
127297
|
});
|
|
127298
127298
|
return { repos: {} };
|
|
@@ -127327,7 +127327,7 @@ function forgetAll() {
|
|
|
127327
127327
|
const path2 = prefsPath();
|
|
127328
127328
|
if ((0, import_node_fs25.existsSync)(path2)) (0, import_node_fs25.rmSync)(path2);
|
|
127329
127329
|
}
|
|
127330
|
-
var import_node_fs25, import_node_path26,
|
|
127330
|
+
var import_node_fs25, import_node_path26, log12, PrefsSchema;
|
|
127331
127331
|
var init_prefs = __esm({
|
|
127332
127332
|
"src/lifecycle/prefs.ts"() {
|
|
127333
127333
|
"use strict";
|
|
@@ -127336,7 +127336,7 @@ var init_prefs = __esm({
|
|
|
127336
127336
|
init_zod();
|
|
127337
127337
|
init_logger();
|
|
127338
127338
|
init_constants();
|
|
127339
|
-
|
|
127339
|
+
log12 = getLogger("lifecycle.prefs");
|
|
127340
127340
|
PrefsSchema = external_exports.object({
|
|
127341
127341
|
repos: external_exports.record(
|
|
127342
127342
|
external_exports.string(),
|
|
@@ -127439,7 +127439,7 @@ function takeoverProd(opts) {
|
|
|
127439
127439
|
`prod is already suspended by dev pid ${existing.devPid} (since ${existing.suspendedAt}). Stop that dev session first, or run 'tb-streamer prod doctor'.`
|
|
127440
127440
|
);
|
|
127441
127441
|
}
|
|
127442
|
-
|
|
127442
|
+
log13.info(`stale marker found (pid ${existing.devPid} is gone) \u2014 clearing and proceeding`);
|
|
127443
127443
|
}
|
|
127444
127444
|
getSupervisor().bootoutAgent();
|
|
127445
127445
|
writeMarker({
|
|
@@ -127454,7 +127454,7 @@ function takeoverProd(opts) {
|
|
|
127454
127454
|
const m2 = readMarker();
|
|
127455
127455
|
if (m2 && m2.devPid === process.pid) {
|
|
127456
127456
|
writeMarker({ ...m2, userHeld: true });
|
|
127457
|
-
|
|
127457
|
+
log13.info(
|
|
127458
127458
|
`prod is suspended (userHeld). Run 'tb-streamer prod start' to restore the supervised instance.`
|
|
127459
127459
|
);
|
|
127460
127460
|
}
|
|
@@ -127473,14 +127473,14 @@ function takeoverProd(opts) {
|
|
|
127473
127473
|
});
|
|
127474
127474
|
process.on("uncaughtException", (err) => {
|
|
127475
127475
|
flipUserHeld();
|
|
127476
|
-
|
|
127476
|
+
log13.error(`uncaught: ${err.message}`);
|
|
127477
127477
|
process.exit(1);
|
|
127478
127478
|
});
|
|
127479
127479
|
process.on("exit", () => {
|
|
127480
127480
|
flipUserHeld();
|
|
127481
127481
|
});
|
|
127482
127482
|
}
|
|
127483
|
-
var import_node_net2,
|
|
127483
|
+
var import_node_net2, log13;
|
|
127484
127484
|
var init_dev_takeover = __esm({
|
|
127485
127485
|
"src/lifecycle/dev-takeover.ts"() {
|
|
127486
127486
|
"use strict";
|
|
@@ -127489,7 +127489,7 @@ var init_dev_takeover = __esm({
|
|
|
127489
127489
|
init_marker();
|
|
127490
127490
|
init_platform2();
|
|
127491
127491
|
init_prefs();
|
|
127492
|
-
|
|
127492
|
+
log13 = getLogger("lifecycle.dev-takeover");
|
|
127493
127493
|
}
|
|
127494
127494
|
});
|
|
127495
127495
|
|
|
@@ -135584,7 +135584,7 @@ var REF_PREFIX = "ref: refs/heads/";
|
|
|
135584
135584
|
var MAX_DEPTH = 6;
|
|
135585
135585
|
function readGitBranch(projectPath) {
|
|
135586
135586
|
if (!projectPath) return null;
|
|
135587
|
-
const
|
|
135587
|
+
const log15 = getLogger2();
|
|
135588
135588
|
let dir = projectPath;
|
|
135589
135589
|
let depth = 0;
|
|
135590
135590
|
while (depth < MAX_DEPTH) {
|
|
@@ -135593,11 +135593,11 @@ function readGitBranch(projectPath) {
|
|
|
135593
135593
|
const content = (0, import_fs2.readFileSync)(headPath, "utf-8").trim();
|
|
135594
135594
|
if (content.startsWith(REF_PREFIX)) {
|
|
135595
135595
|
const branch = content.slice(REF_PREFIX.length);
|
|
135596
|
-
|
|
135596
|
+
log15.trace({ projectPath, dir, branch }, "git: branch resolved");
|
|
135597
135597
|
return branch;
|
|
135598
135598
|
}
|
|
135599
135599
|
if (content.length >= 7) {
|
|
135600
|
-
|
|
135600
|
+
log15.trace({ projectPath, dir }, "git: detached HEAD");
|
|
135601
135601
|
return "(detached)";
|
|
135602
135602
|
}
|
|
135603
135603
|
return null;
|
|
@@ -135608,7 +135608,7 @@ function readGitBranch(projectPath) {
|
|
|
135608
135608
|
dir = parent;
|
|
135609
135609
|
depth++;
|
|
135610
135610
|
}
|
|
135611
|
-
|
|
135611
|
+
log15.trace({ projectPath }, "git: no .git found within depth");
|
|
135612
135612
|
return null;
|
|
135613
135613
|
}
|
|
135614
135614
|
function generateMatches(meta3, query) {
|
|
@@ -135901,8 +135901,8 @@ function cleanSystemTags(text) {
|
|
|
135901
135901
|
return text.replace(SYSTEM_TAG_RE, "").replace(/[^\S\n]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
|
|
135902
135902
|
}
|
|
135903
135903
|
async function parseMeta(filePath, account, tier) {
|
|
135904
|
-
const
|
|
135905
|
-
|
|
135904
|
+
const log15 = getLogger2();
|
|
135905
|
+
log15.trace({ filePath, account, tier: tier.name }, "parseMeta: start");
|
|
135906
135906
|
const state = initialReducerState();
|
|
135907
135907
|
const fileStream = (0, import_fs3.createReadStream)(filePath);
|
|
135908
135908
|
const rl = (0, import_readline.createInterface)({ input: fileStream, crlfDelay: Infinity });
|
|
@@ -135919,22 +135919,22 @@ async function parseMeta(filePath, account, tier) {
|
|
|
135919
135919
|
reduceLine(state, entry, tier);
|
|
135920
135920
|
}
|
|
135921
135921
|
} catch (err) {
|
|
135922
|
-
|
|
135922
|
+
log15.warn({ filePath, err }, "parseMeta: read failed");
|
|
135923
135923
|
return null;
|
|
135924
135924
|
}
|
|
135925
135925
|
if (state.badJsonLines > 0) {
|
|
135926
|
-
|
|
135926
|
+
log15.warn(
|
|
135927
135927
|
{ filePath, badJsonLines: state.badJsonLines },
|
|
135928
135928
|
"parseMeta: skipped malformed JSON lines"
|
|
135929
135929
|
);
|
|
135930
135930
|
}
|
|
135931
135931
|
const meta3 = finalizeMeta(state, filePath, account, tier);
|
|
135932
|
-
if (!meta3)
|
|
135932
|
+
if (!meta3) log15.trace({ filePath }, "parseMeta: no messages");
|
|
135933
135933
|
return meta3;
|
|
135934
135934
|
}
|
|
135935
135935
|
async function parseConversation(filePath, account) {
|
|
135936
|
-
const
|
|
135937
|
-
|
|
135936
|
+
const log15 = getLogger2();
|
|
135937
|
+
log15.trace({ filePath, account }, "parseConversation: start");
|
|
135938
135938
|
const messages = [];
|
|
135939
135939
|
let badJsonLines = 0;
|
|
135940
135940
|
const textParts = [];
|
|
@@ -135967,17 +135967,17 @@ async function parseConversation(filePath, account) {
|
|
|
135967
135967
|
}
|
|
135968
135968
|
}
|
|
135969
135969
|
} catch (err) {
|
|
135970
|
-
|
|
135970
|
+
log15.warn({ filePath, err }, "parseConversation: read failed");
|
|
135971
135971
|
return null;
|
|
135972
135972
|
}
|
|
135973
135973
|
if (badJsonLines > 0) {
|
|
135974
|
-
|
|
135974
|
+
log15.warn({ filePath, badJsonLines }, "parseConversation: skipped malformed JSON lines");
|
|
135975
135975
|
}
|
|
135976
135976
|
if (messages.length === 0) {
|
|
135977
|
-
|
|
135977
|
+
log15.trace({ filePath }, "parseConversation: no messages");
|
|
135978
135978
|
return null;
|
|
135979
135979
|
}
|
|
135980
|
-
|
|
135980
|
+
log15.debug({ filePath, messageCount: messages.length }, "parseConversation: complete");
|
|
135981
135981
|
applyTeamInfo(messages, state);
|
|
135982
135982
|
return {
|
|
135983
135983
|
id: filePath,
|
|
@@ -136242,15 +136242,15 @@ async function detectDefaultProfile() {
|
|
|
136242
136242
|
};
|
|
136243
136243
|
}
|
|
136244
136244
|
async function loadProfiles(configPath) {
|
|
136245
|
-
const
|
|
136245
|
+
const log15 = getLogger2();
|
|
136246
136246
|
try {
|
|
136247
136247
|
const resolved = resolveConfigDir(configPath);
|
|
136248
136248
|
const data = await (0, import_promises4.readFile)((0, import_path5.join)(resolved, PROFILES_FILE), "utf-8");
|
|
136249
136249
|
const profiles = JSON.parse(data);
|
|
136250
|
-
|
|
136250
|
+
log15.debug({ configPath, count: profiles.length }, "profiles: loaded");
|
|
136251
136251
|
return profiles;
|
|
136252
136252
|
} catch (err) {
|
|
136253
|
-
|
|
136253
|
+
log15.debug({ configPath, err }, "profiles: load failed, using default");
|
|
136254
136254
|
const defaultProfile = await detectDefaultProfile();
|
|
136255
136255
|
return [defaultProfile];
|
|
136256
136256
|
}
|
|
@@ -136261,7 +136261,7 @@ function canonicalPath(p2) {
|
|
|
136261
136261
|
var CodexCliProvider = class {
|
|
136262
136262
|
name = CODEX_CLI_PROVIDER;
|
|
136263
136263
|
async discover(roots) {
|
|
136264
|
-
const
|
|
136264
|
+
const log15 = getLogger2();
|
|
136265
136265
|
const results = [];
|
|
136266
136266
|
for (const root of roots) {
|
|
136267
136267
|
let paths;
|
|
@@ -136273,7 +136273,7 @@ var CodexCliProvider = class {
|
|
|
136273
136273
|
unique: true
|
|
136274
136274
|
});
|
|
136275
136275
|
} catch (err) {
|
|
136276
|
-
|
|
136276
|
+
log15.warn({ root, err }, "codex discovery: glob failed");
|
|
136277
136277
|
continue;
|
|
136278
136278
|
}
|
|
136279
136279
|
for (const filePath of paths) {
|
|
@@ -136281,7 +136281,7 @@ var CodexCliProvider = class {
|
|
|
136281
136281
|
const s3 = await (0, import_promises5.stat)(filePath);
|
|
136282
136282
|
if (s3.size > 0) results.push({ filePath: canonicalPath(filePath), account: "codex" });
|
|
136283
136283
|
} catch (err) {
|
|
136284
|
-
|
|
136284
|
+
log15.warn({ filePath, err }, "codex discovery: stat failed");
|
|
136285
136285
|
}
|
|
136286
136286
|
}
|
|
136287
136287
|
}
|
|
@@ -136425,7 +136425,7 @@ function getShortProjectName3(fullPath) {
|
|
|
136425
136425
|
return fullPath.split("/").filter(Boolean).slice(-3).join("/");
|
|
136426
136426
|
}
|
|
136427
136427
|
async function parseCodexConversation(filePath, account) {
|
|
136428
|
-
const
|
|
136428
|
+
const log15 = getLogger2();
|
|
136429
136429
|
const messages = [];
|
|
136430
136430
|
const textParts = [];
|
|
136431
136431
|
let sessionId = "";
|
|
@@ -136461,7 +136461,7 @@ async function parseCodexConversation(filePath, account) {
|
|
|
136461
136461
|
if (role === "user") lastUserText = text;
|
|
136462
136462
|
}
|
|
136463
136463
|
} catch (err) {
|
|
136464
|
-
|
|
136464
|
+
log15.warn({ filePath, err }, "parseCodexConversation: read failed");
|
|
136465
136465
|
return null;
|
|
136466
136466
|
}
|
|
136467
136467
|
if (messages.length === 0) return null;
|
|
@@ -136483,7 +136483,7 @@ async function parseCodexConversation(filePath, account) {
|
|
|
136483
136483
|
var EXCLUDED_SEGMENTS = ["/memory/", "/tool-results/"];
|
|
136484
136484
|
var STAT_CONCURRENCY = 32;
|
|
136485
136485
|
async function discoverJsonlFiles(dirs, onProgress) {
|
|
136486
|
-
const
|
|
136486
|
+
const log15 = getLogger2();
|
|
136487
136487
|
const results = [];
|
|
136488
136488
|
for (const { projectsDir, account } of dirs) {
|
|
136489
136489
|
let filePaths;
|
|
@@ -136494,7 +136494,7 @@ async function discoverJsonlFiles(dirs, onProgress) {
|
|
|
136494
136494
|
dot: false
|
|
136495
136495
|
});
|
|
136496
136496
|
} catch (err) {
|
|
136497
|
-
|
|
136497
|
+
log15.warn({ projectsDir, account, err }, "discovery: glob failed");
|
|
136498
136498
|
continue;
|
|
136499
136499
|
}
|
|
136500
136500
|
const filtered = filePaths.filter((fp) => !EXCLUDED_SEGMENTS.some((seg) => fp.includes(seg)));
|
|
@@ -136509,7 +136509,7 @@ async function discoverJsonlFiles(dirs, onProgress) {
|
|
|
136509
136509
|
const s3 = await (0, import_promises6.stat)(filePath);
|
|
136510
136510
|
return { filePath, size: s3.size };
|
|
136511
136511
|
} catch (err) {
|
|
136512
|
-
|
|
136512
|
+
log15.warn({ filePath, err }, "discovery: stat failed");
|
|
136513
136513
|
return { filePath, size: -1 };
|
|
136514
136514
|
}
|
|
136515
136515
|
})
|
|
@@ -136525,7 +136525,7 @@ async function discoverJsonlFiles(dirs, onProgress) {
|
|
|
136525
136525
|
}
|
|
136526
136526
|
}
|
|
136527
136527
|
}
|
|
136528
|
-
|
|
136528
|
+
log15.debug(
|
|
136529
136529
|
{
|
|
136530
136530
|
projectsDir,
|
|
136531
136531
|
account,
|
|
@@ -136539,7 +136539,7 @@ async function discoverJsonlFiles(dirs, onProgress) {
|
|
|
136539
136539
|
);
|
|
136540
136540
|
onProgress?.(results.length);
|
|
136541
136541
|
}
|
|
136542
|
-
|
|
136542
|
+
log15.debug({ totalFiles: results.length, dirs: dirs.length }, "discovery: complete");
|
|
136543
136543
|
return results;
|
|
136544
136544
|
}
|
|
136545
136545
|
var ThreadbaseProvider = class {
|
|
@@ -136849,7 +136849,7 @@ function classify(filePath, existing) {
|
|
|
136849
136849
|
return { change: "reindex", stat: stat42 };
|
|
136850
136850
|
}
|
|
136851
136851
|
async function parseMetaWithProvider(provider, filePath, account, tier) {
|
|
136852
|
-
const
|
|
136852
|
+
const log15 = getLogger2();
|
|
136853
136853
|
const acc = provider.createEmptyAccumulator();
|
|
136854
136854
|
const rl = (0, import_readline3.createInterface)({
|
|
136855
136855
|
input: (0, import_fs11.createReadStream)(filePath),
|
|
@@ -136867,11 +136867,11 @@ async function parseMetaWithProvider(provider, filePath, account, tier) {
|
|
|
136867
136867
|
try {
|
|
136868
136868
|
provider.reduceEntry(acc, entry, tier);
|
|
136869
136869
|
} catch (err) {
|
|
136870
|
-
|
|
136870
|
+
log15.warn({ filePath, provider: provider.name, err }, "provider reduce threw; line skipped");
|
|
136871
136871
|
}
|
|
136872
136872
|
}
|
|
136873
136873
|
} catch (err) {
|
|
136874
|
-
|
|
136874
|
+
log15.warn({ filePath, provider: provider.name, err }, "provider parse: read failed");
|
|
136875
136875
|
return null;
|
|
136876
136876
|
}
|
|
136877
136877
|
return provider.finalize(acc, filePath, account, tier);
|
|
@@ -137045,8 +137045,8 @@ CREATE INDEX IF NOT EXISTS idx_scanned_dirs_parent_root ON scanned_dirs(parent_r
|
|
|
137045
137045
|
function runMigrations(db) {
|
|
137046
137046
|
const current = db.pragma("user_version", { simple: true });
|
|
137047
137047
|
if (current >= SCHEMA_VERSION) return;
|
|
137048
|
-
const
|
|
137049
|
-
|
|
137048
|
+
const log15 = getLogger2();
|
|
137049
|
+
log15.info({ from: current, to: SCHEMA_VERSION }, "migrations: applying");
|
|
137050
137050
|
if (current >= 1 && current < 2 && tableExists(db, "conversations")) {
|
|
137051
137051
|
for (const [col, ddl] of [
|
|
137052
137052
|
[
|
|
@@ -137088,7 +137088,7 @@ function openDatabase(dbPath) {
|
|
|
137088
137088
|
}
|
|
137089
137089
|
var FULL_RECONCILE_EVERY_N_SCANS = 20;
|
|
137090
137090
|
async function discoverJsonlFilesGated(dirs, files, scannedDirs, options = {}) {
|
|
137091
|
-
const
|
|
137091
|
+
const log15 = getLogger2();
|
|
137092
137092
|
if (options.fullRescan) {
|
|
137093
137093
|
return discoverJsonlFiles(dirs);
|
|
137094
137094
|
}
|
|
@@ -137130,7 +137130,7 @@ async function discoverJsonlFilesGated(dirs, files, scannedDirs, options = {}) {
|
|
|
137130
137130
|
}
|
|
137131
137131
|
}
|
|
137132
137132
|
}
|
|
137133
|
-
|
|
137133
|
+
log15.debug(
|
|
137134
137134
|
{ totalFiles: results.length, dirs: dirs.length },
|
|
137135
137135
|
"dir-watermark: gated discovery complete"
|
|
137136
137136
|
);
|
|
@@ -137636,7 +137636,7 @@ var PersistentEngine = class {
|
|
|
137636
137636
|
// changed since the last index, and upsert their metadata. Returns the number
|
|
137637
137637
|
// of files seen on disk this pass (the scan "scanned" count).
|
|
137638
137638
|
async indexAll(activeProfiles, options) {
|
|
137639
|
-
const
|
|
137639
|
+
const log15 = getLogger2();
|
|
137640
137640
|
const tier = resolveTier(options.tier ?? "standard", options.tiers);
|
|
137641
137641
|
const enabled = options.providers ?? [CLAUDE_CODE_PROVIDER];
|
|
137642
137642
|
const discovered = [];
|
|
@@ -137700,7 +137700,7 @@ var PersistentEngine = class {
|
|
|
137700
137700
|
for (const path2 of this.files.activePathsByAccounts([...coveredAccounts])) {
|
|
137701
137701
|
if (!seen.has(path2)) this.markDeleted(path2);
|
|
137702
137702
|
}
|
|
137703
|
-
|
|
137703
|
+
log15.info({ scanned, indexed: this.conversations.count() }, "persistent: indexAll complete");
|
|
137704
137704
|
return { scanned };
|
|
137705
137705
|
}
|
|
137706
137706
|
// (Re)index a single file. Classifies the change vs. the persisted cursor:
|
|
@@ -137711,7 +137711,7 @@ var PersistentEngine = class {
|
|
|
137711
137711
|
// alongside the meta so callers (refreshFile) can keep, extend, or evict
|
|
137712
137712
|
// their own per-file caches without re-stat'ing the file (racy) themselves.
|
|
137713
137713
|
async indexFile(rawFilePath, account, tierName, customTiers, resolveGitBranch, force = false, provider) {
|
|
137714
|
-
const
|
|
137714
|
+
const log15 = getLogger2();
|
|
137715
137715
|
const tier = resolveTier(tierName, customTiers);
|
|
137716
137716
|
const filePath = canonicalPath(rawFilePath);
|
|
137717
137717
|
const existing = this.files.getByPath(filePath);
|
|
@@ -137742,7 +137742,7 @@ var PersistentEngine = class {
|
|
|
137742
137742
|
try {
|
|
137743
137743
|
result = await tailReduce(filePath, startOffset, startLine, state, tier);
|
|
137744
137744
|
} catch (err) {
|
|
137745
|
-
|
|
137745
|
+
log15.warn({ filePath, err }, "persistent: tail read failed");
|
|
137746
137746
|
return { meta: null, change };
|
|
137747
137747
|
}
|
|
137748
137748
|
const meta3 = finalizeMeta(state, filePath, account, tier);
|
|
@@ -137785,7 +137785,7 @@ var PersistentEngine = class {
|
|
|
137785
137785
|
)
|
|
137786
137786
|
);
|
|
137787
137787
|
}
|
|
137788
|
-
|
|
137788
|
+
log15.debug(
|
|
137789
137789
|
{ filePath, change, bytesRead: result.newOffset - startOffset, msgs: meta3.messageCount },
|
|
137790
137790
|
"persistent: indexed file"
|
|
137791
137791
|
);
|
|
@@ -137797,7 +137797,7 @@ var PersistentEngine = class {
|
|
|
137797
137797
|
// so the next pass classifies an unchanged file as "unchanged" and skips it;
|
|
137798
137798
|
// any change reparses from 0 again. No reducer_state is persisted.
|
|
137799
137799
|
async indexFileWithProvider(provider, filePath, account, tier, stat42, resolveGitBranch) {
|
|
137800
|
-
const
|
|
137800
|
+
const log15 = getLogger2();
|
|
137801
137801
|
const meta3 = await parseMetaWithProvider(provider, filePath, account, tier);
|
|
137802
137802
|
if (!meta3) {
|
|
137803
137803
|
this.markDeleted(filePath);
|
|
@@ -137825,7 +137825,7 @@ var PersistentEngine = class {
|
|
|
137825
137825
|
});
|
|
137826
137826
|
});
|
|
137827
137827
|
upsert();
|
|
137828
|
-
|
|
137828
|
+
log15.debug(
|
|
137829
137829
|
{ filePath, provider: provider.name, msgs: meta3.messageCount },
|
|
137830
137830
|
"persistent: indexed provider file"
|
|
137831
137831
|
);
|
|
@@ -137939,7 +137939,7 @@ var FileWatcher = class {
|
|
|
137939
137939
|
// Resolves once every underlying chokidar watcher has finished its initial
|
|
137940
137940
|
// scan, so the caller knows subsequent FS changes will be observed.
|
|
137941
137941
|
async start() {
|
|
137942
|
-
const
|
|
137942
|
+
const log15 = getLogger2();
|
|
137943
137943
|
const ready = [];
|
|
137944
137944
|
for (const profile of this.profiles) {
|
|
137945
137945
|
const dir = getProjectsDir(profile);
|
|
@@ -137951,7 +137951,7 @@ var FileWatcher = class {
|
|
|
137951
137951
|
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"));
|
|
137952
137952
|
ready.push(new Promise((resolve4) => watcher.once("ready", () => resolve4())));
|
|
137953
137953
|
this.watchers.push(watcher);
|
|
137954
|
-
|
|
137954
|
+
log15.debug({ dir, account: profile.id }, "watcher: watching");
|
|
137955
137955
|
}
|
|
137956
137956
|
await Promise.all(ready);
|
|
137957
137957
|
}
|
|
@@ -137998,14 +137998,14 @@ var IndexQueue = class {
|
|
|
137998
137998
|
async drain() {
|
|
137999
137999
|
if (this.running) return;
|
|
138000
138000
|
this.running = true;
|
|
138001
|
-
const
|
|
138001
|
+
const log15 = getLogger2();
|
|
138002
138002
|
while (this.pending.size > 0) {
|
|
138003
138003
|
const [path2, job] = this.pending.entries().next().value;
|
|
138004
138004
|
this.pending.delete(path2);
|
|
138005
138005
|
try {
|
|
138006
138006
|
await this.process(job);
|
|
138007
138007
|
} catch (err) {
|
|
138008
|
-
|
|
138008
|
+
log15.warn({ filePath: job.filePath, err }, "index-queue: job failed");
|
|
138009
138009
|
}
|
|
138010
138010
|
}
|
|
138011
138011
|
this.running = false;
|
|
@@ -138114,13 +138114,13 @@ var ConversationScanner = class {
|
|
|
138114
138114
|
// active metas and run the identical filter/sort/view/paginate pipeline as
|
|
138115
138115
|
// the in-memory path — guaranteeing an identical ScanResult shape.
|
|
138116
138116
|
async scanPersistent(activeProfiles, options) {
|
|
138117
|
-
const
|
|
138117
|
+
const log15 = getLogger2();
|
|
138118
138118
|
const startedAt = Date.now();
|
|
138119
138119
|
const engine = this.engine();
|
|
138120
138120
|
const { scanned } = await engine.indexAll(activeProfiles, options);
|
|
138121
138121
|
const allMetas = engine.allActive();
|
|
138122
138122
|
const { conversations, total } = this.finalize(allMetas, options);
|
|
138123
|
-
|
|
138123
|
+
log15.info(
|
|
138124
138124
|
{ scanned, kept: allMetas.length, filteredTotal: total, elapsedMs: Date.now() - startedAt },
|
|
138125
138125
|
"scan: complete (persistent)"
|
|
138126
138126
|
);
|
|
@@ -138147,10 +138147,10 @@ var ConversationScanner = class {
|
|
|
138147
138147
|
return { conversations, total };
|
|
138148
138148
|
}
|
|
138149
138149
|
async scanInMemory(activeProfiles, options) {
|
|
138150
|
-
const
|
|
138150
|
+
const log15 = getLogger2();
|
|
138151
138151
|
const startedAt = Date.now();
|
|
138152
138152
|
const tier = this.lastTier;
|
|
138153
|
-
|
|
138153
|
+
log15.info(
|
|
138154
138154
|
{
|
|
138155
138155
|
activeProfiles: activeProfiles.length,
|
|
138156
138156
|
tier: tier.name,
|
|
@@ -138204,7 +138204,7 @@ var ConversationScanner = class {
|
|
|
138204
138204
|
return meta3;
|
|
138205
138205
|
} catch (err) {
|
|
138206
138206
|
parseFailures++;
|
|
138207
|
-
|
|
138207
|
+
log15.warn({ filePath, account, provider: provider.name, err }, "scan: parse threw");
|
|
138208
138208
|
return null;
|
|
138209
138209
|
}
|
|
138210
138210
|
})
|
|
@@ -138224,12 +138224,12 @@ var ConversationScanner = class {
|
|
|
138224
138224
|
options.onBatch?.(batchMetas);
|
|
138225
138225
|
}
|
|
138226
138226
|
scanned += batch.length;
|
|
138227
|
-
|
|
138227
|
+
log15.debug({ scanned, totalFiles, batchKept: batchMetas.length }, "scan: batch complete");
|
|
138228
138228
|
options.onProgress?.(scanned, totalFiles);
|
|
138229
138229
|
}
|
|
138230
138230
|
const { conversations, total } = this.finalize(allMetas, options);
|
|
138231
138231
|
const elapsedMs = Date.now() - startedAt;
|
|
138232
|
-
|
|
138232
|
+
log15.info(
|
|
138233
138233
|
{
|
|
138234
138234
|
totalFiles,
|
|
138235
138235
|
scanned,
|
|
@@ -138243,13 +138243,13 @@ var ConversationScanner = class {
|
|
|
138243
138243
|
return { conversations, total, scanned };
|
|
138244
138244
|
}
|
|
138245
138245
|
async search(query, options = {}) {
|
|
138246
|
-
const
|
|
138247
|
-
|
|
138246
|
+
const log15 = getLogger2();
|
|
138247
|
+
log15.debug({ query, indexSize: this.indexer.getDocumentCount() }, "search: start");
|
|
138248
138248
|
let results;
|
|
138249
138249
|
if (this.persistent) {
|
|
138250
138250
|
const engine = this.engine();
|
|
138251
138251
|
if (engine.conversations.count() === 0) {
|
|
138252
|
-
|
|
138252
|
+
log15.debug("search: persistent index empty, triggering scan");
|
|
138253
138253
|
const profiles = await this.resolveProfiles(options.profiles);
|
|
138254
138254
|
const activeProfiles = profiles.filter((p2) => p2.enabled && p2.scanHistory !== false);
|
|
138255
138255
|
await engine.indexAll(activeProfiles, { ...options, limit: void 0, offset: void 0 });
|
|
@@ -138262,7 +138262,7 @@ var ConversationScanner = class {
|
|
|
138262
138262
|
}));
|
|
138263
138263
|
} else {
|
|
138264
138264
|
if (this.indexer.getDocumentCount() === 0) {
|
|
138265
|
-
|
|
138265
|
+
log15.debug("search: index empty, triggering scan");
|
|
138266
138266
|
await this.scan({ ...options, limit: void 0, offset: void 0 });
|
|
138267
138267
|
}
|
|
138268
138268
|
results = this.indexer.search(query, {
|
|
@@ -138305,23 +138305,23 @@ var ConversationScanner = class {
|
|
|
138305
138305
|
const limit = options.limit ?? 50;
|
|
138306
138306
|
const offset = options.offset ?? 0;
|
|
138307
138307
|
const sliced = results.slice(offset, offset + limit);
|
|
138308
|
-
|
|
138308
|
+
log15.debug({ query, matched: results.length, returned: sliced.length }, "search: complete");
|
|
138309
138309
|
return sliced;
|
|
138310
138310
|
}
|
|
138311
138311
|
async getConversation(id, _options) {
|
|
138312
|
-
const
|
|
138312
|
+
const log15 = getLogger2();
|
|
138313
138313
|
const cid = canonicalPath(id);
|
|
138314
138314
|
const cached4 = this.conversationLRU.get(cid);
|
|
138315
138315
|
if (cached4) {
|
|
138316
|
-
|
|
138316
|
+
log15.debug({ id }, "getConversation: cache hit");
|
|
138317
138317
|
return cached4.conversation;
|
|
138318
138318
|
}
|
|
138319
138319
|
const meta3 = this.persistent ? this.engine().getByIdOrSession(cid) : this.metadataCache.get(cid) ?? this.resolveSessionId(cid);
|
|
138320
138320
|
if (!meta3) {
|
|
138321
|
-
|
|
138321
|
+
log15.debug({ id }, "getConversation: not found in metadata");
|
|
138322
138322
|
return null;
|
|
138323
138323
|
}
|
|
138324
|
-
|
|
138324
|
+
log15.debug({ id, filePath: meta3.filePath }, "getConversation: cache miss, parsing");
|
|
138325
138325
|
try {
|
|
138326
138326
|
if (this.persistent && meta3.provider !== CODEX_CLI_PROVIDER) {
|
|
138327
138327
|
const parsed = await parseConversationResumable(meta3.filePath, meta3.account);
|
|
@@ -138334,7 +138334,7 @@ var ConversationScanner = class {
|
|
|
138334
138334
|
}
|
|
138335
138335
|
return conversation;
|
|
138336
138336
|
} catch (err) {
|
|
138337
|
-
|
|
138337
|
+
log15.warn({ id, filePath: meta3.filePath, err }, "getConversation: parse failed");
|
|
138338
138338
|
return null;
|
|
138339
138339
|
}
|
|
138340
138340
|
}
|
|
@@ -138420,7 +138420,7 @@ var ConversationScanner = class {
|
|
|
138420
138420
|
return refresh;
|
|
138421
138421
|
}
|
|
138422
138422
|
async doRefreshFile(filePath, account) {
|
|
138423
|
-
const
|
|
138423
|
+
const log15 = getLogger2();
|
|
138424
138424
|
if (this.persistent) {
|
|
138425
138425
|
const engine = this.engine();
|
|
138426
138426
|
const previous2 = engine.getByIdOrSession(filePath);
|
|
@@ -138447,7 +138447,7 @@ var ConversationScanner = class {
|
|
|
138447
138447
|
} else if (change === "appended") {
|
|
138448
138448
|
await this.extendCachedConversations(cacheKeys, filePath, meta22.account);
|
|
138449
138449
|
}
|
|
138450
|
-
|
|
138450
|
+
log15.debug({ filePath, change, kept: !!meta22 }, "refreshFile: updated persistent index");
|
|
138451
138451
|
return meta22;
|
|
138452
138452
|
}
|
|
138453
138453
|
const previous = this.metadataCache.get(filePath);
|
|
@@ -138456,7 +138456,7 @@ var ConversationScanner = class {
|
|
|
138456
138456
|
try {
|
|
138457
138457
|
meta3 = await parseMeta(filePath, resolvedAccount, this.lastTier);
|
|
138458
138458
|
} catch (err) {
|
|
138459
|
-
|
|
138459
|
+
log15.warn({ filePath, err }, "refreshFile: parseMeta threw");
|
|
138460
138460
|
meta3 = null;
|
|
138461
138461
|
}
|
|
138462
138462
|
const evict = (m2) => {
|
|
@@ -138472,7 +138472,7 @@ var ConversationScanner = class {
|
|
|
138472
138472
|
this.removeFromSessionIndex(previous);
|
|
138473
138473
|
this.indexer.removeDocument(previous.id);
|
|
138474
138474
|
}
|
|
138475
|
-
|
|
138475
|
+
log15.debug({ filePath }, "refreshFile: dropped (no parseable messages)");
|
|
138476
138476
|
return null;
|
|
138477
138477
|
}
|
|
138478
138478
|
meta3.gitBranch = readGitBranch(meta3.projectPath);
|
|
@@ -138485,7 +138485,7 @@ var ConversationScanner = class {
|
|
|
138485
138485
|
} else {
|
|
138486
138486
|
this.indexer.addDocument(meta3);
|
|
138487
138487
|
}
|
|
138488
|
-
|
|
138488
|
+
log15.debug(
|
|
138489
138489
|
{ filePath, messageCount: meta3.messageCount },
|
|
138490
138490
|
"refreshFile: updated in-memory indexes"
|
|
138491
138491
|
);
|
|
@@ -145950,10 +145950,10 @@ function fingerprintOf(ids) {
|
|
|
145950
145950
|
return `sha256:${(0, import_crypto11.createHash)("sha256").update(sorted.join("\n")).digest("hex")}`;
|
|
145951
145951
|
}
|
|
145952
145952
|
var CacheIntegrityMonitor = class {
|
|
145953
|
-
constructor(cache, wsHub,
|
|
145953
|
+
constructor(cache, wsHub, log15, cacheDir, rescan, runDuringReset) {
|
|
145954
145954
|
this.cache = cache;
|
|
145955
145955
|
this.wsHub = wsHub;
|
|
145956
|
-
this.log =
|
|
145956
|
+
this.log = log15;
|
|
145957
145957
|
this.cacheDir = cacheDir;
|
|
145958
145958
|
this.rescan = rescan;
|
|
145959
145959
|
this.runDuringReset = runDuringReset;
|
|
@@ -146972,6 +146972,102 @@ var ApnsClient = class {
|
|
|
146972
146972
|
}
|
|
146973
146973
|
};
|
|
146974
146974
|
|
|
146975
|
+
// src/services/push/expoPushSender.ts
|
|
146976
|
+
init_logger();
|
|
146977
|
+
var log5 = getLogger("expo-push");
|
|
146978
|
+
var EXPO_PUSH_ENDPOINT = "https://exp.host/--/api/v2/push/send";
|
|
146979
|
+
var EXPO_PUSH_BATCH_SIZE = 100;
|
|
146980
|
+
var DEAD_TOKEN_ERROR = "DeviceNotRegistered";
|
|
146981
|
+
var ExpoPushSender = class {
|
|
146982
|
+
/**
|
|
146983
|
+
* @param accessToken Expo access token, when the project has enhanced
|
|
146984
|
+
* security enabled. Optional on purpose: a self-hoster does not own the Expo
|
|
146985
|
+
* project and cannot obtain one, so requiring it would break the deployment
|
|
146986
|
+
* this transport exists to serve.
|
|
146987
|
+
*/
|
|
146988
|
+
constructor(repo, accessToken) {
|
|
146989
|
+
this.repo = repo;
|
|
146990
|
+
this.accessToken = accessToken;
|
|
146991
|
+
}
|
|
146992
|
+
repo;
|
|
146993
|
+
accessToken;
|
|
146994
|
+
/**
|
|
146995
|
+
* Send one message to every deliverable Expo token.
|
|
146996
|
+
*
|
|
146997
|
+
* Sends are independent: Expo returns a ticket per token in one response, so
|
|
146998
|
+
* a dead device is recorded against its own row and never silences the other
|
|
146999
|
+
* devices in the batch.
|
|
147000
|
+
*/
|
|
147001
|
+
async send(message, now = Date.now()) {
|
|
147002
|
+
const rows = this.repo.listDeliverable();
|
|
147003
|
+
const outcome = { attempted: rows.length, succeeded: 0, retired: 0 };
|
|
147004
|
+
if (rows.length === 0) return outcome;
|
|
147005
|
+
for (let i = 0; i < rows.length; i += EXPO_PUSH_BATCH_SIZE) {
|
|
147006
|
+
const chunk = rows.slice(i, i + EXPO_PUSH_BATCH_SIZE);
|
|
147007
|
+
await this.sendChunk(chunk, message, now, outcome);
|
|
147008
|
+
}
|
|
147009
|
+
return outcome;
|
|
147010
|
+
}
|
|
147011
|
+
async sendChunk(rows, message, now, outcome) {
|
|
147012
|
+
let payload;
|
|
147013
|
+
try {
|
|
147014
|
+
const res = await fetch(EXPO_PUSH_ENDPOINT, {
|
|
147015
|
+
method: "POST",
|
|
147016
|
+
headers: {
|
|
147017
|
+
"content-type": "application/json",
|
|
147018
|
+
accept: "application/json",
|
|
147019
|
+
...this.accessToken && { authorization: `Bearer ${this.accessToken}` }
|
|
147020
|
+
},
|
|
147021
|
+
body: JSON.stringify(rows.map((row) => ({ to: row.token, ...message })))
|
|
147022
|
+
});
|
|
147023
|
+
if (!res.ok) {
|
|
147024
|
+
const code = `HTTP_${res.status}`;
|
|
147025
|
+
for (const row of rows) this.repo.recordFailure(row.token, code, now);
|
|
147026
|
+
log5.warn("expo_push.request_rejected", {
|
|
147027
|
+
event: "expo_push.request_rejected",
|
|
147028
|
+
status: res.status,
|
|
147029
|
+
tokens: rows.length
|
|
147030
|
+
});
|
|
147031
|
+
return;
|
|
147032
|
+
}
|
|
147033
|
+
payload = await res.json();
|
|
147034
|
+
} catch (err) {
|
|
147035
|
+
for (const row of rows) this.repo.recordFailure(row.token, "SendError", now);
|
|
147036
|
+
log5.error("expo_push.send_failed", {
|
|
147037
|
+
event: "expo_push.send_failed",
|
|
147038
|
+
tokens: rows.length,
|
|
147039
|
+
err: String(err)
|
|
147040
|
+
});
|
|
147041
|
+
return;
|
|
147042
|
+
}
|
|
147043
|
+
const tickets = payload?.data;
|
|
147044
|
+
rows.forEach((row, index) => {
|
|
147045
|
+
const ticket = Array.isArray(tickets) ? tickets[index] : void 0;
|
|
147046
|
+
if (!ticket) {
|
|
147047
|
+
this.repo.recordFailure(row.token, "NoTicket", now);
|
|
147048
|
+
return;
|
|
147049
|
+
}
|
|
147050
|
+
if (ticket.status === "ok") {
|
|
147051
|
+
this.repo.recordSuccess(row.token, now);
|
|
147052
|
+
outcome.succeeded += 1;
|
|
147053
|
+
return;
|
|
147054
|
+
}
|
|
147055
|
+
const code = ticket.details?.error ?? "PushError";
|
|
147056
|
+
this.repo.recordFailure(row.token, code, now);
|
|
147057
|
+
if (code === DEAD_TOKEN_ERROR) {
|
|
147058
|
+
this.repo.revoke(row.token, now);
|
|
147059
|
+
outcome.retired += 1;
|
|
147060
|
+
}
|
|
147061
|
+
log5.warn("expo_push.send_rejected", {
|
|
147062
|
+
event: "expo_push.send_rejected",
|
|
147063
|
+
code,
|
|
147064
|
+
message: ticket.message,
|
|
147065
|
+
retired: code === DEAD_TOKEN_ERROR
|
|
147066
|
+
});
|
|
147067
|
+
});
|
|
147068
|
+
}
|
|
147069
|
+
};
|
|
147070
|
+
|
|
146975
147071
|
// src/services/push/liveActivityNotifier.ts
|
|
146976
147072
|
init_logger();
|
|
146977
147073
|
|
|
@@ -146986,7 +147082,7 @@ function truncateLastOutput(raw2) {
|
|
|
146986
147082
|
}
|
|
146987
147083
|
|
|
146988
147084
|
// src/services/push/liveActivityNotifier.ts
|
|
146989
|
-
var
|
|
147085
|
+
var log6 = getLogger("live-activity");
|
|
146990
147086
|
function contentStateForSession(args) {
|
|
146991
147087
|
const status = toLiveActivityStatus(args.session.status);
|
|
146992
147088
|
if (!status) return null;
|
|
@@ -147046,7 +147142,7 @@ var LiveActivityNotifier = class {
|
|
|
147046
147142
|
}
|
|
147047
147143
|
await this.maybeSendName(session);
|
|
147048
147144
|
} catch (err) {
|
|
147049
|
-
|
|
147145
|
+
log6.error("live_activity.notify_failed", {
|
|
147050
147146
|
event: "live_activity.notify_failed",
|
|
147051
147147
|
sessionId: session.id,
|
|
147052
147148
|
status: session.status,
|
|
@@ -147068,7 +147164,7 @@ var LiveActivityNotifier = class {
|
|
|
147068
147164
|
});
|
|
147069
147165
|
this.openActivity.set(session.id, { sessionNameSent: session.sessionName != null });
|
|
147070
147166
|
if (outcome.attempted > 0) {
|
|
147071
|
-
|
|
147167
|
+
log6.info("live_activity.updated", {
|
|
147072
147168
|
event: "live_activity.updated",
|
|
147073
147169
|
sessionId: session.id,
|
|
147074
147170
|
status: contentState.status,
|
|
@@ -147092,7 +147188,7 @@ var LiveActivityNotifier = class {
|
|
|
147092
147188
|
});
|
|
147093
147189
|
open3.sessionNameSent = true;
|
|
147094
147190
|
if (outcome.attempted > 0) {
|
|
147095
|
-
|
|
147191
|
+
log6.info("live_activity.updated", {
|
|
147096
147192
|
event: "live_activity.updated",
|
|
147097
147193
|
sessionId: session.id,
|
|
147098
147194
|
status: contentState.status,
|
|
@@ -147111,7 +147207,7 @@ var LiveActivityNotifier = class {
|
|
|
147111
147207
|
if (!contentState) return;
|
|
147112
147208
|
const outcome = await this.sender.end({ sessionId: session.id, contentState });
|
|
147113
147209
|
if (outcome.attempted > 0) {
|
|
147114
|
-
|
|
147210
|
+
log6.info("live_activity.ended", {
|
|
147115
147211
|
event: "live_activity.ended",
|
|
147116
147212
|
sessionId: session.id,
|
|
147117
147213
|
...outcome
|
|
@@ -147129,7 +147225,7 @@ init_logger();
|
|
|
147129
147225
|
|
|
147130
147226
|
// src/services/push/liveActivitySender.ts
|
|
147131
147227
|
init_logger();
|
|
147132
|
-
var
|
|
147228
|
+
var log7 = getLogger("live-activity");
|
|
147133
147229
|
var ACTIVITY_MAX_LIFETIME_MS = 8 * 60 * 60 * 1e3;
|
|
147134
147230
|
function buildActivityKitPayload(args) {
|
|
147135
147231
|
return {
|
|
@@ -147193,7 +147289,7 @@ var LiveActivitySender = class {
|
|
|
147193
147289
|
);
|
|
147194
147290
|
for (const { row, result, error: error51 } of results) {
|
|
147195
147291
|
if (error51) {
|
|
147196
|
-
|
|
147292
|
+
log7.error("live_activity.send_failed", {
|
|
147197
147293
|
event: "live_activity.send_failed",
|
|
147198
147294
|
sessionId: args.sessionId,
|
|
147199
147295
|
activityId: row.activity_id,
|
|
@@ -147214,7 +147310,7 @@ var LiveActivitySender = class {
|
|
|
147214
147310
|
this.repo.expire(row.token, now);
|
|
147215
147311
|
outcome.retired += 1;
|
|
147216
147312
|
}
|
|
147217
|
-
|
|
147313
|
+
log7.warn("live_activity.send_rejected", {
|
|
147218
147314
|
event: "live_activity.send_rejected",
|
|
147219
147315
|
sessionId: args.sessionId,
|
|
147220
147316
|
activityId: row.activity_id,
|
|
@@ -147259,7 +147355,7 @@ var LiveActivitySender = class {
|
|
|
147259
147355
|
};
|
|
147260
147356
|
|
|
147261
147357
|
// src/services/push/liveActivityRenewal.ts
|
|
147262
|
-
var
|
|
147358
|
+
var log8 = getLogger("live-activity");
|
|
147263
147359
|
var RENEWAL_LEAD_MS = 30 * 60 * 1e3;
|
|
147264
147360
|
var MAX_TIMER_MS = 60 * 60 * 1e3;
|
|
147265
147361
|
function renewalDueAt(row) {
|
|
@@ -147307,7 +147403,7 @@ var LiveActivityRenewalScheduler = class {
|
|
|
147307
147403
|
await this.renew(row, now);
|
|
147308
147404
|
}
|
|
147309
147405
|
} catch (err) {
|
|
147310
|
-
|
|
147406
|
+
log8.error("live_activity.renewal_sweep_failed", {
|
|
147311
147407
|
event: "live_activity.renewal_sweep_failed",
|
|
147312
147408
|
err: String(err)
|
|
147313
147409
|
});
|
|
@@ -147336,7 +147432,7 @@ var LiveActivityRenewalScheduler = class {
|
|
|
147336
147432
|
if (!session || !status) {
|
|
147337
147433
|
this.deps.repo.claimRenewal(row.token, now);
|
|
147338
147434
|
this.deps.repo.expire(row.token, now);
|
|
147339
|
-
|
|
147435
|
+
log8.info("live_activity.renewal_skipped", {
|
|
147340
147436
|
event: "live_activity.renewal_skipped",
|
|
147341
147437
|
sessionId: row.session_id,
|
|
147342
147438
|
activityId: row.activity_id,
|
|
@@ -147371,7 +147467,7 @@ var LiveActivityRenewalScheduler = class {
|
|
|
147371
147467
|
startedAt,
|
|
147372
147468
|
now
|
|
147373
147469
|
});
|
|
147374
|
-
|
|
147470
|
+
log8.info("live_activity.renewed", {
|
|
147375
147471
|
event: "live_activity.renewed",
|
|
147376
147472
|
sessionId: session.id,
|
|
147377
147473
|
activityId: row.activity_id,
|
|
@@ -147381,7 +147477,7 @@ var LiveActivityRenewalScheduler = class {
|
|
|
147381
147477
|
replacementRequested: started
|
|
147382
147478
|
});
|
|
147383
147479
|
} catch (err) {
|
|
147384
|
-
|
|
147480
|
+
log8.error("live_activity.renewal_failed", {
|
|
147385
147481
|
event: "live_activity.renewal_failed",
|
|
147386
147482
|
sessionId: session.id,
|
|
147387
147483
|
activityId: row.activity_id,
|
|
@@ -147426,6 +147522,78 @@ var LiveActivityRenewalScheduler = class {
|
|
|
147426
147522
|
}
|
|
147427
147523
|
};
|
|
147428
147524
|
|
|
147525
|
+
// src/services/push/waitingInputNotifier.ts
|
|
147526
|
+
init_logger();
|
|
147527
|
+
var log9 = getLogger("expo-push");
|
|
147528
|
+
function waitingInputMessage(session, serverId) {
|
|
147529
|
+
return {
|
|
147530
|
+
title: session.projectName || "Threadbase",
|
|
147531
|
+
body: "Waiting for your input",
|
|
147532
|
+
data: { sessionId: session.id, serverId }
|
|
147533
|
+
};
|
|
147534
|
+
}
|
|
147535
|
+
var WaitingInputNotifier = class {
|
|
147536
|
+
/**
|
|
147537
|
+
* @param isWatched Whether a client is currently subscribed to this session
|
|
147538
|
+
* over WebSocket. Mobile subscribes while the session screen is open and the
|
|
147539
|
+
* socket dies when the app is backgrounded, so this is the available signal
|
|
147540
|
+
* for "the user is already looking" — and a push to someone already reading
|
|
147541
|
+
* the output is pure noise.
|
|
147542
|
+
*/
|
|
147543
|
+
constructor(sender, serverId, isWatched) {
|
|
147544
|
+
this.sender = sender;
|
|
147545
|
+
this.serverId = serverId;
|
|
147546
|
+
this.isWatched = isWatched;
|
|
147547
|
+
}
|
|
147548
|
+
sender;
|
|
147549
|
+
serverId;
|
|
147550
|
+
isWatched;
|
|
147551
|
+
/** Sessions with a turn the user started that has not yet been answered. */
|
|
147552
|
+
openTurn = /* @__PURE__ */ new Set();
|
|
147553
|
+
/**
|
|
147554
|
+
* React to a session status change.
|
|
147555
|
+
*
|
|
147556
|
+
* Fire-and-forget by design: a push must never delay or fail a session
|
|
147557
|
+
* transition, so this returns a promise the caller may ignore and every error
|
|
147558
|
+
* is logged rather than propagated.
|
|
147559
|
+
*/
|
|
147560
|
+
async onStatusChange(session, previousStatus) {
|
|
147561
|
+
try {
|
|
147562
|
+
if (session.status === "running") {
|
|
147563
|
+
if (previousStatus === "waiting_input") this.openTurn.add(session.id);
|
|
147564
|
+
return;
|
|
147565
|
+
}
|
|
147566
|
+
if (session.status !== "waiting_input") {
|
|
147567
|
+
this.openTurn.delete(session.id);
|
|
147568
|
+
return;
|
|
147569
|
+
}
|
|
147570
|
+
if (!this.openTurn.delete(session.id)) return;
|
|
147571
|
+
if (this.isWatched(session.id)) {
|
|
147572
|
+
log9.debug("expo_push.suppressed_watched", {
|
|
147573
|
+
event: "expo_push.suppressed_watched",
|
|
147574
|
+
sessionId: session.id
|
|
147575
|
+
});
|
|
147576
|
+
return;
|
|
147577
|
+
}
|
|
147578
|
+
const outcome = await this.sender.send(waitingInputMessage(session, this.serverId));
|
|
147579
|
+
if (outcome.attempted > 0) {
|
|
147580
|
+
log9.info("expo_push.waiting_input", {
|
|
147581
|
+
event: "expo_push.waiting_input",
|
|
147582
|
+
sessionId: session.id,
|
|
147583
|
+
...outcome
|
|
147584
|
+
});
|
|
147585
|
+
}
|
|
147586
|
+
} catch (err) {
|
|
147587
|
+
log9.error("expo_push.notify_failed", {
|
|
147588
|
+
event: "expo_push.notify_failed",
|
|
147589
|
+
sessionId: session.id,
|
|
147590
|
+
status: session.status,
|
|
147591
|
+
err: String(err)
|
|
147592
|
+
});
|
|
147593
|
+
}
|
|
147594
|
+
}
|
|
147595
|
+
};
|
|
147596
|
+
|
|
147429
147597
|
// src/server.ts
|
|
147430
147598
|
init_detectPermissionGate();
|
|
147431
147599
|
init_detectQuestionFromScreen();
|
|
@@ -148773,6 +148941,10 @@ var StreamerServer = class {
|
|
|
148773
148941
|
apnsClient = null;
|
|
148774
148942
|
liveActivityNotifier = null;
|
|
148775
148943
|
liveActivityRenewal = null;
|
|
148944
|
+
// "Your turn" notifications over Expo's relay (#528). Needs no credential of
|
|
148945
|
+
// its own, so unlike the Live Activity path it is on wherever the cache DB
|
|
148946
|
+
// opened — with no registered device it simply sends nothing.
|
|
148947
|
+
waitingInputNotifier = null;
|
|
148776
148948
|
discoveryCache = null;
|
|
148777
148949
|
// Single-flight for process discovery. Mobile polls GET /api/sessions and
|
|
148778
148950
|
// retries on timeout; without this, every concurrent request starts its own
|
|
@@ -149072,6 +149244,7 @@ var StreamerServer = class {
|
|
|
149072
149244
|
this.wsHub.broadcast({ type: "session_update", session: resp });
|
|
149073
149245
|
}
|
|
149074
149246
|
void this.liveActivityNotifier?.onStatusChange(session, previousStatus);
|
|
149247
|
+
void this.waitingInputNotifier?.onStatusChange(session, previousStatus);
|
|
149075
149248
|
this.sessionStatusBus.emit(`status:${session.id}`, session.status, session);
|
|
149076
149249
|
}
|
|
149077
149250
|
});
|
|
@@ -149384,6 +149557,33 @@ var StreamerServer = class {
|
|
|
149384
149557
|
topic: `${creds.bundleId}.push-type.liveactivity`
|
|
149385
149558
|
});
|
|
149386
149559
|
}
|
|
149560
|
+
/**
|
|
149561
|
+
* Bring up "your turn" notifications over Expo's relay (#528).
|
|
149562
|
+
*
|
|
149563
|
+
* Unconditional, unlike Live Activity push: Expo holds the app's APNs and FCM
|
|
149564
|
+
* credentials, so a self-hosted streamer needs no credential of its own. The
|
|
149565
|
+
* access token is optional and only relevant if the Expo project has enhanced
|
|
149566
|
+
* security enabled — requiring one would lock out every self-hoster, since
|
|
149567
|
+
* they do not own the project. It is never logged.
|
|
149568
|
+
*/
|
|
149569
|
+
initWaitingInputPush(pushRepo) {
|
|
149570
|
+
const sender = new ExpoPushSender(pushRepo, process.env.THREADBASE_EXPO_ACCESS_TOKEN);
|
|
149571
|
+
const serverId = process.env.THREADBASE_INSTANCE_ID ?? (0, import_os13.hostname)();
|
|
149572
|
+
this.waitingInputNotifier = new WaitingInputNotifier(
|
|
149573
|
+
sender,
|
|
149574
|
+
serverId,
|
|
149575
|
+
(id) => this.hasSessionSubscriber(id)
|
|
149576
|
+
);
|
|
149577
|
+
}
|
|
149578
|
+
/** Whether any live socket is subscribed to this session — "someone is looking". */
|
|
149579
|
+
hasSessionSubscriber(sessionId) {
|
|
149580
|
+
const subs = this.sessionSubscribers.get(sessionId);
|
|
149581
|
+
if (!subs) return false;
|
|
149582
|
+
for (const ws2 of subs) {
|
|
149583
|
+
if (ws2.readyState === ws2.OPEN) return true;
|
|
149584
|
+
}
|
|
149585
|
+
return false;
|
|
149586
|
+
}
|
|
149387
149587
|
/**
|
|
149388
149588
|
* Classify sessions left behind by previous streamer runs (C1 Phase 3a).
|
|
149389
149589
|
*
|
|
@@ -149986,6 +150186,7 @@ var StreamerServer = class {
|
|
|
149986
150186
|
this.pushRepo = new PushRepository(db);
|
|
149987
150187
|
this.devicesRepo = new DevicesRepository(db);
|
|
149988
150188
|
this.initLiveActivityPush(this.pushRepo);
|
|
150189
|
+
this.initWaitingInputPush(this.pushRepo);
|
|
149989
150190
|
this.cacheMonitor = new CacheIntegrityMonitor(
|
|
149990
150191
|
this.cache,
|
|
149991
150192
|
this.wsHub,
|
|
@@ -156833,7 +157034,7 @@ init_platform2();
|
|
|
156833
157034
|
init_logger();
|
|
156834
157035
|
init_protocol();
|
|
156835
157036
|
init_socket();
|
|
156836
|
-
var
|
|
157037
|
+
var log11 = getLogger("prod");
|
|
156837
157038
|
function clearSupervisorLogs() {
|
|
156838
157039
|
let paths;
|
|
156839
157040
|
try {
|
|
@@ -157017,12 +157218,12 @@ function registerProdCommands(program3) {
|
|
|
157017
157218
|
prod.command("start").description("Restore prod after a user-held suspension").option("--clear-logs", "Clear existing stdout + stderr logs", false).action(async (opts) => {
|
|
157018
157219
|
if (opts.clearLogs === true) clearSupervisorLogs();
|
|
157019
157220
|
const r = await runProdStart();
|
|
157020
|
-
|
|
157221
|
+
log11.info(r.message, void 0, "console");
|
|
157021
157222
|
if (!r.ok) process.exitCode = 1;
|
|
157022
157223
|
});
|
|
157023
157224
|
prod.command("stop").description("Unload the launchd agent (prod will not auto-restart)").action(async () => {
|
|
157024
157225
|
const r = await runProdStop();
|
|
157025
|
-
|
|
157226
|
+
log11.info(r.message, void 0, "console");
|
|
157026
157227
|
});
|
|
157027
157228
|
prod.command("status").description("Report whether prod is supervised, suspended, or down").action(async () => {
|
|
157028
157229
|
const s3 = await runProdStatus();
|
|
@@ -157031,7 +157232,7 @@ function registerProdCommands(program3) {
|
|
|
157031
157232
|
`pid: ${s3.agentPid ?? "(none)"}`,
|
|
157032
157233
|
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"
|
|
157033
157234
|
];
|
|
157034
|
-
|
|
157235
|
+
log11.info(parts.join("\n "), void 0, "console");
|
|
157035
157236
|
});
|
|
157036
157237
|
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) => {
|
|
157037
157238
|
const sup = getSupervisor();
|
|
@@ -157040,24 +157241,24 @@ function registerProdCommands(program3) {
|
|
|
157040
157241
|
sup.bootoutAgent();
|
|
157041
157242
|
sup.bootstrapAgent(specPath, { afterBootout: true });
|
|
157042
157243
|
const what = process.platform === "darwin" ? `agent restarted from ${specPath}` : `task '${TASK_NAME}' restarted`;
|
|
157043
|
-
|
|
157244
|
+
log11.info(what, void 0, "console");
|
|
157044
157245
|
});
|
|
157045
157246
|
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) => {
|
|
157046
157247
|
const r = await runProdDoctor({ fix: opts.fix === true });
|
|
157047
157248
|
if (r.ptyHost) {
|
|
157048
|
-
|
|
157249
|
+
log11.info(
|
|
157049
157250
|
r.ptyHost.reachable ? `pty-host: reachable, protocol=${r.ptyHost.protocolVersion}, sessions=${r.ptyHost.sessionCount}` : `pty-host: unreachable (${r.ptyHost.error})`,
|
|
157050
157251
|
void 0,
|
|
157051
157252
|
"console"
|
|
157052
157253
|
);
|
|
157053
157254
|
}
|
|
157054
|
-
|
|
157055
|
-
for (const f2 of r.findings)
|
|
157255
|
+
log11.info(`findings: ${r.findings.length === 0 ? "(none)" : ""}`, void 0, "console");
|
|
157256
|
+
for (const f2 of r.findings) log11.info(` - ${f2}`, void 0, "console");
|
|
157056
157257
|
if (r.repairs.length) {
|
|
157057
|
-
|
|
157058
|
-
for (const fix of r.repairs)
|
|
157258
|
+
log11.info(`repairs:`, void 0, "console");
|
|
157259
|
+
for (const fix of r.repairs) log11.info(` - ${fix}`, void 0, "console");
|
|
157059
157260
|
} else if (!opts.fix && r.findings.length > 0) {
|
|
157060
|
-
|
|
157261
|
+
log11.info(`(re-run with --fix to apply repairs)`, void 0, "console");
|
|
157061
157262
|
}
|
|
157062
157263
|
});
|
|
157063
157264
|
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) => {
|
|
@@ -157068,14 +157269,14 @@ function registerProdCommands(program3) {
|
|
|
157068
157269
|
errorsOnly: opts.errorsOnly === true,
|
|
157069
157270
|
clear: opts.clear === true
|
|
157070
157271
|
});
|
|
157071
|
-
if (r.message)
|
|
157272
|
+
if (r.message) log11.info(r.message, void 0, "console");
|
|
157072
157273
|
if (!r.ok) process.exitCode = 1;
|
|
157073
157274
|
});
|
|
157074
157275
|
program3.addCommand(prod);
|
|
157075
157276
|
}
|
|
157076
157277
|
|
|
157077
157278
|
// cli/index.ts
|
|
157078
|
-
var
|
|
157279
|
+
var log14 = getLogger("cli");
|
|
157079
157280
|
var program2 = new Command();
|
|
157080
157281
|
program2.name("threadbase-streamer").description("PTY session management, WebSocket streaming, and REST API server for Claude Code").version(getVersion());
|
|
157081
157282
|
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(
|
|
@@ -157117,14 +157318,14 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
157117
157318
|
const { checkSqliteAbi: checkSqliteAbi2 } = await Promise.resolve().then(() => (init_check_sqlite_abi(), check_sqlite_abi_exports));
|
|
157118
157319
|
checkSqliteAbi2();
|
|
157119
157320
|
} catch (err) {
|
|
157120
|
-
|
|
157321
|
+
log14.error(err instanceof Error ? err.message : String(err), void 0, "console");
|
|
157121
157322
|
process.exit(1);
|
|
157122
157323
|
}
|
|
157123
157324
|
if (opts.multiAgentFlow) {
|
|
157124
157325
|
process.env.MULTI_AGENT_FLOW = "true";
|
|
157125
157326
|
}
|
|
157126
157327
|
if (opts.defaultPermissionMode !== void 0 && !isPermissionMode(opts.defaultPermissionMode)) {
|
|
157127
|
-
|
|
157328
|
+
log14.error(
|
|
157128
157329
|
`Invalid --default-permission-mode: ${opts.defaultPermissionMode} (expected one of ${PERMISSION_MODES.join(", ")})`,
|
|
157129
157330
|
void 0,
|
|
157130
157331
|
"console"
|
|
@@ -157140,7 +157341,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
157140
157341
|
const value = eq === -1 ? true : entry.slice(eq + 1);
|
|
157141
157342
|
const def = findFlag(id);
|
|
157142
157343
|
if (!def) {
|
|
157143
|
-
|
|
157344
|
+
log14.error(
|
|
157144
157345
|
`Invalid --claude-flag id: ${id} (expected one of ${CLAUDE_FLAGS.map((f2) => f2.id).join(", ")})`,
|
|
157145
157346
|
void 0,
|
|
157146
157347
|
"console"
|
|
@@ -157157,7 +157358,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
157157
157358
|
claudeFlags = validateFlagValues(raw2);
|
|
157158
157359
|
for (const id of Object.keys(raw2)) {
|
|
157159
157360
|
if (!(id in claudeFlags)) {
|
|
157160
|
-
|
|
157361
|
+
log14.error(`Invalid value for --claude-flag ${id}`, void 0, "console");
|
|
157161
157362
|
process.exit(1);
|
|
157162
157363
|
}
|
|
157163
157364
|
}
|
|
@@ -157166,13 +157367,13 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
157166
157367
|
if (Array.isArray(opts.feature) && opts.feature.length > 0) {
|
|
157167
157368
|
const parsed = parseFeatureFlagArgs(opts.feature);
|
|
157168
157369
|
if (parsed.errors.length > 0) {
|
|
157169
|
-
for (const error51 of parsed.errors)
|
|
157370
|
+
for (const error51 of parsed.errors) log14.error(`--feature: ${error51}`, void 0, "console");
|
|
157170
157371
|
process.exit(1);
|
|
157171
157372
|
}
|
|
157172
157373
|
featureFlags = parsed.values;
|
|
157173
157374
|
}
|
|
157174
157375
|
if (opts.defaultEffort !== void 0 && !isEffortLevel(opts.defaultEffort)) {
|
|
157175
|
-
|
|
157376
|
+
log14.error(
|
|
157176
157377
|
`Invalid --default-effort: ${opts.defaultEffort} (expected one of ${EFFORT_LEVELS.join(", ")})`,
|
|
157177
157378
|
void 0,
|
|
157178
157379
|
"console"
|
|
@@ -157183,7 +157384,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
157183
157384
|
if (opts.ptyGracePeriodMs !== void 0) {
|
|
157184
157385
|
const parsed = Number(opts.ptyGracePeriodMs);
|
|
157185
157386
|
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
157186
|
-
|
|
157387
|
+
log14.error(
|
|
157187
157388
|
`Invalid --pty-grace-period-ms: ${opts.ptyGracePeriodMs} (expected a non-negative integer; 0 disables auto-hold)`,
|
|
157188
157389
|
void 0,
|
|
157189
157390
|
"console"
|
|
@@ -157202,14 +157403,14 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
157202
157403
|
if (isProdInvocation) {
|
|
157203
157404
|
const { truncateOversizedLogs: truncateOversizedLogs2 } = await Promise.resolve().then(() => (init_log_cap(), log_cap_exports));
|
|
157204
157405
|
for (const f2 of truncateOversizedLogs2()) {
|
|
157205
|
-
|
|
157406
|
+
log14.info(`truncated oversized log at boot: ${f2}`, { path: f2, event: "log.truncated" });
|
|
157206
157407
|
}
|
|
157207
157408
|
}
|
|
157208
157409
|
if (process.platform === "darwin") {
|
|
157209
157410
|
const { detectConflictingAgents: detectConflictingAgents2, formatConflictMessage: formatConflictMessage2 } = await Promise.resolve().then(() => (init_conflict_check(), conflict_check_exports));
|
|
157210
157411
|
const conflicts = detectConflictingAgents2();
|
|
157211
157412
|
if (conflicts.length > 0) {
|
|
157212
|
-
|
|
157413
|
+
log14.warn(formatConflictMessage2(conflicts), void 0, "console");
|
|
157213
157414
|
}
|
|
157214
157415
|
}
|
|
157215
157416
|
let resolvedDefaultPermissionMode = opts.defaultPermissionMode;
|
|
@@ -157224,7 +157425,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
157224
157425
|
resolvedAutoResumeOnBoot = await interactiveAutoResumePrompt2();
|
|
157225
157426
|
setAutoResumeOnBoot(resolvedAutoResumeOnBoot);
|
|
157226
157427
|
} else if (resolvedAutoResumeOnBoot === void 0) {
|
|
157227
|
-
|
|
157428
|
+
log14.info(
|
|
157228
157429
|
"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.",
|
|
157229
157430
|
void 0,
|
|
157230
157431
|
"console"
|
|
@@ -157274,15 +157475,15 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
157274
157475
|
await server.listen(resolvedPort);
|
|
157275
157476
|
{
|
|
157276
157477
|
const v2 = getVersion();
|
|
157277
|
-
|
|
157478
|
+
log14.info(`Threadbase Streamer v${v2}`, { version: v2, port: resolvedPort });
|
|
157278
157479
|
}
|
|
157279
|
-
|
|
157480
|
+
log14.info(`Listening on http://localhost:${resolvedPort}`, {
|
|
157280
157481
|
url: `http://localhost:${resolvedPort}`
|
|
157281
157482
|
});
|
|
157282
|
-
|
|
157483
|
+
log14.info(`WebSocket at ws://localhost:${resolvedPort}/ws`, {
|
|
157283
157484
|
wsUrl: `ws://localhost:${resolvedPort}/ws`
|
|
157284
157485
|
});
|
|
157285
|
-
|
|
157486
|
+
log14.info(`API key: ${apiKey}`, { apiKeyMasked: `${apiKey.slice(0, 6)}\u2026` });
|
|
157286
157487
|
try {
|
|
157287
157488
|
await printServerBanner({
|
|
157288
157489
|
port: resolvedPort,
|
|
@@ -157292,15 +157493,15 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
157292
157493
|
});
|
|
157293
157494
|
} catch (err) {
|
|
157294
157495
|
const message = err instanceof Error ? err.message : String(err);
|
|
157295
|
-
|
|
157296
|
-
|
|
157496
|
+
log14.warn(`(skipped pairing QR: ${message})`, { reason: message });
|
|
157497
|
+
log14.info(
|
|
157297
157498
|
printUrlBanner({ url: resolveServerUrl({ publicUrl, port: resolvedPort }) }),
|
|
157298
157499
|
void 0,
|
|
157299
157500
|
"console"
|
|
157300
157501
|
);
|
|
157301
157502
|
}
|
|
157302
157503
|
const shutdown = async () => {
|
|
157303
|
-
|
|
157504
|
+
log14.info("Shutting down...");
|
|
157304
157505
|
await server.close();
|
|
157305
157506
|
process.exit(0);
|
|
157306
157507
|
};
|
|
@@ -157308,7 +157509,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
157308
157509
|
process.on("SIGINT", shutdown);
|
|
157309
157510
|
process.on("SIGTERM", shutdown);
|
|
157310
157511
|
process.on("uncaughtException", (err) => {
|
|
157311
|
-
|
|
157512
|
+
log14.error(`uncaught: ${err.message}`, {
|
|
157312
157513
|
error: err.message,
|
|
157313
157514
|
stack: err.stack,
|
|
157314
157515
|
event: "process.uncaught"
|
|
@@ -157317,7 +157518,7 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
157317
157518
|
});
|
|
157318
157519
|
process.on("unhandledRejection", (reason) => {
|
|
157319
157520
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
157320
|
-
|
|
157521
|
+
log14.error(`unhandled rejection: ${msg}`, {
|
|
157321
157522
|
error: msg,
|
|
157322
157523
|
event: "process.unhandled_rejection"
|
|
157323
157524
|
});
|
|
@@ -157342,10 +157543,10 @@ program2.command("cache").description("Manage the local SQLite conversation cach
|
|
|
157342
157543
|
const f2 = dbPath + suffix;
|
|
157343
157544
|
if (existsSync16(f2)) {
|
|
157344
157545
|
rmSync6(f2);
|
|
157345
|
-
|
|
157546
|
+
log14.info(`Deleted ${f2}`, { path: f2 }, "console");
|
|
157346
157547
|
}
|
|
157347
157548
|
}
|
|
157348
|
-
|
|
157549
|
+
log14.info("Cache cleared. Restart the server to rebuild.", void 0, "console");
|
|
157349
157550
|
})
|
|
157350
157551
|
);
|
|
157351
157552
|
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) => {
|
|
@@ -157370,7 +157571,7 @@ program2.command("set-key [key]").description("Set the streamer API key in ~/.th
|
|
|
157370
157571
|
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) => {
|
|
157371
157572
|
const cfg = loadUpdateConfig();
|
|
157372
157573
|
if (!cfg) {
|
|
157373
|
-
|
|
157574
|
+
log14.warn(
|
|
157374
157575
|
`No update config found at ${DEFAULT_CONFIG_PATH}. Create one with at least 'github_repo: owner/name' to enable updates.`,
|
|
157375
157576
|
void 0,
|
|
157376
157577
|
"console"
|
|
@@ -157389,11 +157590,11 @@ program2.command("update").description("Check for streamer updates from GitHub R
|
|
|
157389
157590
|
appendUpdateLog(
|
|
157390
157591
|
`[check] current=${result2.current} latest=${result2.latest ?? "none"} status=${result2.reason}`
|
|
157391
157592
|
);
|
|
157392
|
-
|
|
157393
|
-
|
|
157394
|
-
|
|
157395
|
-
|
|
157396
|
-
|
|
157593
|
+
log14.info(`Current : ${result2.current}`, void 0, "console");
|
|
157594
|
+
log14.info(`Latest : ${result2.latest ?? "(none)"}`, void 0, "console");
|
|
157595
|
+
log14.info(`Channel : ${cfg.channel}`, void 0, "console");
|
|
157596
|
+
log14.info(`Diff : ${result2.diff ?? "(none)"}`, void 0, "console");
|
|
157597
|
+
log14.info(`Status : ${result2.reason}`, void 0, "console");
|
|
157397
157598
|
return;
|
|
157398
157599
|
}
|
|
157399
157600
|
const port = Number.parseInt(opts.port, 10);
|
|
@@ -157409,20 +157610,20 @@ program2.command("update").description("Check for streamer updates from GitHub R
|
|
|
157409
157610
|
});
|
|
157410
157611
|
switch (result.kind) {
|
|
157411
157612
|
case "no-op":
|
|
157412
|
-
|
|
157413
|
-
|
|
157414
|
-
|
|
157613
|
+
log14.info(`Current : ${result.current}`, void 0, "console");
|
|
157614
|
+
log14.info(`Latest : ${result.latest ?? "(none)"}`, void 0, "console");
|
|
157615
|
+
log14.info(`Status : ${result.reason}`, void 0, "console");
|
|
157415
157616
|
break;
|
|
157416
157617
|
case "unsupported-install":
|
|
157417
|
-
|
|
157618
|
+
log14.warn(result.reason, void 0, "console");
|
|
157418
157619
|
process.exitCode = 2;
|
|
157419
157620
|
break;
|
|
157420
157621
|
case "deferred":
|
|
157421
|
-
|
|
157622
|
+
log14.warn(`Deferred: ${result.reason}`, void 0, "console");
|
|
157422
157623
|
process.exitCode = 2;
|
|
157423
157624
|
break;
|
|
157424
157625
|
case "dry-run":
|
|
157425
|
-
|
|
157626
|
+
log14.info(
|
|
157426
157627
|
`Would install ${result.latest} from ${result.tarballUrl}`,
|
|
157427
157628
|
void 0,
|
|
157428
157629
|
"console"
|
|
@@ -157430,28 +157631,28 @@ program2.command("update").description("Check for streamer updates from GitHub R
|
|
|
157430
157631
|
break;
|
|
157431
157632
|
case "installed":
|
|
157432
157633
|
if (result.restart.method.startsWith("failed:")) {
|
|
157433
|
-
|
|
157634
|
+
log14.error(
|
|
157434
157635
|
`Installed ${result.installed} on disk, but the running service was not updated. Restart: ${result.restart.method}.`,
|
|
157435
157636
|
void 0,
|
|
157436
157637
|
"console"
|
|
157437
157638
|
);
|
|
157438
157639
|
process.exitCode = 1;
|
|
157439
157640
|
} else {
|
|
157440
|
-
|
|
157641
|
+
log14.info(
|
|
157441
157642
|
`Installed ${result.installed} (was ${result.previous}). Restart: ${result.restart.method}.`,
|
|
157442
157643
|
void 0,
|
|
157443
157644
|
"console"
|
|
157444
157645
|
);
|
|
157445
157646
|
}
|
|
157446
157647
|
if (result.pruned.length > 0) {
|
|
157447
|
-
|
|
157648
|
+
log14.info(`Pruned old releases: ${result.pruned.join(", ")}`, void 0, "console");
|
|
157448
157649
|
}
|
|
157449
157650
|
break;
|
|
157450
157651
|
}
|
|
157451
157652
|
} catch (err) {
|
|
157452
157653
|
const message = err instanceof Error ? err.message : String(err);
|
|
157453
157654
|
appendUpdateLog(`[error] ${message}`);
|
|
157454
|
-
|
|
157655
|
+
log14.error(`Update failed: ${message}`, { error: message }, "console");
|
|
157455
157656
|
process.exitCode = 1;
|
|
157456
157657
|
}
|
|
157457
157658
|
});
|
|
@@ -157481,14 +157682,14 @@ program2.command("pty-host").description(
|
|
|
157481
157682
|
onConnection: (transport) => host.accept(transport)
|
|
157482
157683
|
});
|
|
157483
157684
|
} catch (err) {
|
|
157484
|
-
|
|
157685
|
+
log14.error(
|
|
157485
157686
|
`pty-host could not listen on ${opts.socket}: ${err instanceof Error ? err.message : String(err)}`,
|
|
157486
157687
|
void 0,
|
|
157487
157688
|
"console"
|
|
157488
157689
|
);
|
|
157489
157690
|
process.exit(1);
|
|
157490
157691
|
}
|
|
157491
|
-
|
|
157692
|
+
log14.info(`pty-host listening on ${opts.socket}`, {
|
|
157492
157693
|
event: "pty_host.listening",
|
|
157493
157694
|
socket: opts.socket
|
|
157494
157695
|
});
|
|
@@ -157542,7 +157743,7 @@ async function printServerBanner({
|
|
|
157542
157743
|
}) {
|
|
157543
157744
|
const url2 = resolveServerUrl({ publicUrl, port });
|
|
157544
157745
|
if (!includeQr) {
|
|
157545
|
-
|
|
157746
|
+
log14.info(printUrlBanner({ url: url2 }), void 0, "console");
|
|
157546
157747
|
return;
|
|
157547
157748
|
}
|
|
157548
157749
|
const res = await fetch(`http://localhost:${port}/api/pair/start`, {
|
|
@@ -157557,9 +157758,9 @@ async function printServerBanner({
|
|
|
157557
157758
|
const expSeconds = Math.floor(expiresAt / 1e3);
|
|
157558
157759
|
const payload = `threadbase://pair?url=${encodeURIComponent(url2)}&token=${token}&exp=${expSeconds}`;
|
|
157559
157760
|
const qr2 = await generateQr(payload);
|
|
157560
|
-
|
|
157561
|
-
|
|
157562
|
-
|
|
157761
|
+
log14.info(printUrlBanner({ url: url2, qr: qr2, expiresAt }), void 0, "console");
|
|
157762
|
+
log14.info(`Pair URL: ${payload}`, void 0, "console");
|
|
157763
|
+
log14.info(`Expires in ${expiresInSeconds}s`, void 0, "console");
|
|
157563
157764
|
}
|
|
157564
157765
|
/*! Bundled license information:
|
|
157565
157766
|
|