@threadbase-sh/streamer 1.52.3 → 1.52.5
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 +119 -29
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +81 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -2
- package/dist/index.d.ts +50 -2
- package/dist/index.js +81 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -6160,11 +6160,12 @@ function validateFeatureFlagValues(raw2) {
|
|
|
6160
6160
|
const out = {};
|
|
6161
6161
|
const dropped = [];
|
|
6162
6162
|
for (const [id, value] of Object.entries(raw2)) {
|
|
6163
|
-
|
|
6163
|
+
const def = findFeatureFlag(id);
|
|
6164
|
+
if (!def || typeof value !== "boolean") {
|
|
6164
6165
|
dropped.push(id);
|
|
6165
6166
|
continue;
|
|
6166
6167
|
}
|
|
6167
|
-
out[id] = value;
|
|
6168
|
+
out[def.id] = value;
|
|
6168
6169
|
}
|
|
6169
6170
|
if (dropped.length > 0) {
|
|
6170
6171
|
getLogger("feature-flags").warn(
|
|
@@ -6184,7 +6185,8 @@ function parseFeatureFlagArgs(entries) {
|
|
|
6184
6185
|
const eq = entry.indexOf("=");
|
|
6185
6186
|
const id = (eq === -1 ? entry : entry.slice(0, eq)).trim();
|
|
6186
6187
|
const rawValue = eq === -1 ? "true" : entry.slice(eq + 1).trim().toLowerCase();
|
|
6187
|
-
|
|
6188
|
+
const def = findFeatureFlag(id);
|
|
6189
|
+
if (!def) {
|
|
6188
6190
|
errors.push(
|
|
6189
6191
|
`Unknown feature flag "${id}". Known flags: ${FEATURE_FLAGS.map((f2) => f2.id).join(", ")}`
|
|
6190
6192
|
);
|
|
@@ -6194,21 +6196,35 @@ function parseFeatureFlagArgs(entries) {
|
|
|
6194
6196
|
errors.push(`Invalid value "${rawValue}" for feature flag "${id}" \u2014 expected true/false`);
|
|
6195
6197
|
continue;
|
|
6196
6198
|
}
|
|
6197
|
-
values[id] = rawValue === "true";
|
|
6199
|
+
values[def.id] = rawValue === "true";
|
|
6198
6200
|
}
|
|
6199
6201
|
return { values, errors };
|
|
6200
6202
|
}
|
|
6201
6203
|
function resolveFeatureFlags(opts) {
|
|
6202
6204
|
const env = opts?.env ?? process.env;
|
|
6203
|
-
const
|
|
6205
|
+
const values = {};
|
|
6206
|
+
const sources = {};
|
|
6204
6207
|
for (const def of FEATURE_FLAGS) {
|
|
6205
|
-
|
|
6208
|
+
const rungs = [
|
|
6209
|
+
["override", opts?.override?.[def.id]],
|
|
6210
|
+
["env", parseBooleanEnv(env[def.env])],
|
|
6211
|
+
["cli", opts?.cli?.[def.id]],
|
|
6212
|
+
["yaml", opts?.yaml?.[def.id]]
|
|
6213
|
+
];
|
|
6214
|
+
const won = rungs.find(([, v2]) => v2 !== void 0);
|
|
6215
|
+
values[def.id] = won ? won[1] : def.default;
|
|
6216
|
+
sources[def.id] = won ? won[0] : "default";
|
|
6206
6217
|
}
|
|
6207
|
-
return
|
|
6218
|
+
return { values, sources };
|
|
6208
6219
|
}
|
|
6209
6220
|
function nonDefaultFeatureFlags(values) {
|
|
6210
6221
|
return FEATURE_FLAGS.filter((f2) => values[f2.id] !== f2.default).map((f2) => f2.id);
|
|
6211
6222
|
}
|
|
6223
|
+
function describeFeatureFlags(resolution) {
|
|
6224
|
+
return FEATURE_FLAGS.map(
|
|
6225
|
+
(f2) => `${f2.id}=${resolution.values[f2.id]}(${resolution.sources[f2.id]})`
|
|
6226
|
+
).join(" ");
|
|
6227
|
+
}
|
|
6212
6228
|
var FEATURE_FLAGS;
|
|
6213
6229
|
var init_feature_flags = __esm({
|
|
6214
6230
|
"src/feature-flags.ts"() {
|
|
@@ -151031,6 +151047,7 @@ function autoResumeSkipReason(row, opts) {
|
|
|
151031
151047
|
if (opts.now - row.status_updated_at > AUTO_RESUME_WINDOW_MS) return "too_old";
|
|
151032
151048
|
if (!opts.projectExists(row.project_path)) return "project_missing";
|
|
151033
151049
|
if (resumeIdForRow(row) == null) return "resume_identity_missing";
|
|
151050
|
+
if (!opts.historyExists(row)) return "history_missing";
|
|
151034
151051
|
return null;
|
|
151035
151052
|
}
|
|
151036
151053
|
function planAutoResume(rows, opts) {
|
|
@@ -151292,7 +151309,34 @@ var SessionRegistryBoot = class {
|
|
|
151292
151309
|
/** Resume only the recent sessions the user explicitly allowed us to start at boot. */
|
|
151293
151310
|
async autoResumePreviousSessions(rows) {
|
|
151294
151311
|
if (!this.autoResumeOnBoot) return;
|
|
151295
|
-
const
|
|
151312
|
+
const now = Date.now();
|
|
151313
|
+
const baseOptions = { now, projectExists: import_fs36.existsSync, historyExists: () => true };
|
|
151314
|
+
const historyExists = /* @__PURE__ */ new Map();
|
|
151315
|
+
const preflights = /* @__PURE__ */ new Set();
|
|
151316
|
+
for (const row of rows) {
|
|
151317
|
+
if (autoResumeSkipReason(row, baseOptions) != null) {
|
|
151318
|
+
historyExists.set(row.session_id, false);
|
|
151319
|
+
continue;
|
|
151320
|
+
}
|
|
151321
|
+
while (preflights.size >= AUTO_RESUME_CONCURRENCY) {
|
|
151322
|
+
await Promise.race(preflights);
|
|
151323
|
+
}
|
|
151324
|
+
const preflight = (async () => {
|
|
151325
|
+
try {
|
|
151326
|
+
const target = await this.deps.resolveConversationTarget(row.session_id);
|
|
151327
|
+
historyExists.set(row.session_id, target.ok || target.reason !== "history_file_missing");
|
|
151328
|
+
} catch {
|
|
151329
|
+
historyExists.set(row.session_id, true);
|
|
151330
|
+
}
|
|
151331
|
+
})();
|
|
151332
|
+
preflights.add(preflight);
|
|
151333
|
+
void preflight.then(() => preflights.delete(preflight));
|
|
151334
|
+
}
|
|
151335
|
+
await Promise.all(preflights);
|
|
151336
|
+
const plan = planAutoResume(rows, {
|
|
151337
|
+
...baseOptions,
|
|
151338
|
+
historyExists: (row) => historyExists.get(row.session_id) ?? false
|
|
151339
|
+
});
|
|
151296
151340
|
const skippedBy = {};
|
|
151297
151341
|
for (const { row, reason } of plan.skipped) {
|
|
151298
151342
|
skippedBy[reason] = (skippedBy[reason] ?? 0) + 1;
|
|
@@ -152290,6 +152334,10 @@ var StreamerServer = class {
|
|
|
152290
152334
|
// Resolved once at boot; see src/feature-flags.ts. Total map — every registry
|
|
152291
152335
|
// id is present, so indexing it never yields undefined.
|
|
152292
152336
|
featureFlags;
|
|
152337
|
+
// Which rung of the precedence chain decided each flag. Reported at boot and
|
|
152338
|
+
// over GET /api/config/feature-flags — the resolved boolean alone cannot say
|
|
152339
|
+
// whether a value came from the environment, the CLI, server.yaml or nowhere.
|
|
152340
|
+
featureFlagSources;
|
|
152293
152341
|
// Derived from featureFlags.codexSystemPrompt. Kept as its own field so the
|
|
152294
152342
|
// read site in startFresh() is unchanged.
|
|
152295
152343
|
codexSystemPromptEnabled;
|
|
@@ -152403,10 +152451,13 @@ var StreamerServer = class {
|
|
|
152403
152451
|
this.codexRoots = config2.codexRoots ?? [(0, import_path33.join)((0, import_os15.homedir)(), ".codex", "sessions")];
|
|
152404
152452
|
this.ptyGracePeriodMs = config2.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
|
|
152405
152453
|
this.defaultSystemPrompt = config2.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
152406
|
-
|
|
152407
|
-
|
|
152408
|
-
|
|
152409
|
-
|
|
152454
|
+
const flagResolution = resolveFeatureFlags({
|
|
152455
|
+
override: config2.codexSystemPromptEnabled === void 0 ? void 0 : { codexSystemPrompt: config2.codexSystemPromptEnabled },
|
|
152456
|
+
cli: config2.featureFlags,
|
|
152457
|
+
yaml: loadFeatureFlags()
|
|
152458
|
+
});
|
|
152459
|
+
this.featureFlags = flagResolution.values;
|
|
152460
|
+
this.featureFlagSources = flagResolution.sources;
|
|
152410
152461
|
this.codexSystemPromptEnabled = this.featureFlags.codexSystemPrompt;
|
|
152411
152462
|
this.defaultPermissionMode = config2.defaultPermissionMode ?? loadDefaultPermissionMode() ?? "acceptEdits";
|
|
152412
152463
|
this.defaultModel = config2.defaultModel ?? "sonnet";
|
|
@@ -152447,17 +152498,17 @@ var StreamerServer = class {
|
|
|
152447
152498
|
selfPtyEndedAt: this.selfPtyEndedAt,
|
|
152448
152499
|
resumeSession: (opts) => this.sessionHandlers.resumeSession(opts),
|
|
152449
152500
|
watchConversationFile: (sessionId, historyId) => this.sessionWatchers.watchConversationFile(sessionId, historyId),
|
|
152450
|
-
broadcastSessionList: () => this.wsHub.broadcast(this.sessionListPayload())
|
|
152501
|
+
broadcastSessionList: () => this.wsHub.broadcast(this.sessionListPayload()),
|
|
152502
|
+
resolveConversationTarget: (sessionId) => this.resolveConversationTarget(sessionId)
|
|
152451
152503
|
});
|
|
152452
152504
|
this.includeAgents = parseIncludeAgentsEnv(process.env.THREADBASE_INCLUDE_AGENTS);
|
|
152453
152505
|
this.agentEntrypoints = parseAgentEntrypointsEnv(process.env.THREADBASE_AGENT_ENTRYPOINTS);
|
|
152454
|
-
|
|
152455
|
-
|
|
152456
|
-
|
|
152457
|
-
|
|
152458
|
-
|
|
152459
|
-
|
|
152460
|
-
}
|
|
152506
|
+
this.log.info(`Feature flags: ${describeFeatureFlags(flagResolution)}`, {
|
|
152507
|
+
event: "config.feature_flags",
|
|
152508
|
+
values: this.featureFlags,
|
|
152509
|
+
sources: this.featureFlagSources,
|
|
152510
|
+
nonDefault: nonDefaultFeatureFlags(this.featureFlags)
|
|
152511
|
+
});
|
|
152461
152512
|
const rawRoot = process.env.THREADBASE_BROWSE_ROOT ?? loadBrowseRoot() ?? config2.browseRoot;
|
|
152462
152513
|
if (rawRoot) {
|
|
152463
152514
|
(0, import_promises16.realpath)(rawRoot).then((resolved) => {
|
|
@@ -153480,7 +153531,11 @@ var StreamerServer = class {
|
|
|
153480
153531
|
* the absence of that field is the signal that this endpoint is read-only.
|
|
153481
153532
|
*/
|
|
153482
153533
|
getFeatureFlagsConfig() {
|
|
153483
|
-
return {
|
|
153534
|
+
return {
|
|
153535
|
+
registry: FEATURE_FLAGS,
|
|
153536
|
+
values: this.featureFlags,
|
|
153537
|
+
sources: this.featureFlagSources
|
|
153538
|
+
};
|
|
153484
153539
|
}
|
|
153485
153540
|
getClaudeFlagsConfig() {
|
|
153486
153541
|
return {
|
|
@@ -153653,13 +153708,17 @@ var StreamerServer = class {
|
|
|
153653
153708
|
conv = await this.conversationHandlers.findConversationByUuid(boundId);
|
|
153654
153709
|
}
|
|
153655
153710
|
}
|
|
153711
|
+
const cachedConvMeta = this.cache?.getMetaById(historyId);
|
|
153712
|
+
const cachedPath = cachedConvMeta?.filePath ? toNativeFilePath(cachedConvMeta.filePath) : null;
|
|
153713
|
+
const cachedCodexPath = (registryProvider === CODEX_CLI_PROVIDER || cachedConvMeta?.provider === CODEX_CLI_PROVIDER) && cachedPath != null && (0, import_fs38.existsSync)(cachedPath) ? cachedPath : null;
|
|
153656
153714
|
const jsonlCwd = jsonlPath ? await this.conversationHandlers.readCwdFromJsonl(jsonlPath) : null;
|
|
153657
|
-
const projectPath = jsonlCwd ?? conv?.projectPath;
|
|
153715
|
+
const projectPath = jsonlCwd ?? conv?.projectPath ?? (cachedCodexPath ? cachedConvMeta?.projectPath : null);
|
|
153658
153716
|
if (!projectPath) {
|
|
153659
|
-
if (!conv && !jsonlPath)
|
|
153717
|
+
if (!conv && !jsonlPath && !cachedCodexPath) {
|
|
153718
|
+
return { ok: false, reason: "history_file_missing" };
|
|
153719
|
+
}
|
|
153660
153720
|
return { ok: false, reason: "no_project_path" };
|
|
153661
153721
|
}
|
|
153662
|
-
const cachedConvMeta = this.cache?.getMetaById(historyId);
|
|
153663
153722
|
const provider = coerceProviderForRunner(
|
|
153664
153723
|
conv?.provider ?? cachedConvMeta?.provider ?? registryProvider
|
|
153665
153724
|
);
|
|
@@ -153673,7 +153732,7 @@ var StreamerServer = class {
|
|
|
153673
153732
|
// conversation. Kept separate from `jsonlPath` deliberately: feeding it to
|
|
153674
153733
|
// conversationBusy() would newly arm the mtime heuristic for Codex, which
|
|
153675
153734
|
// is exactly the over-broad signal the report ruled out.
|
|
153676
|
-
historyPath: jsonlPath ?? conv?.filePath ?? null,
|
|
153735
|
+
historyPath: jsonlPath ?? conv?.filePath ?? cachedCodexPath ?? null,
|
|
153677
153736
|
conv,
|
|
153678
153737
|
projectPath,
|
|
153679
153738
|
provider
|
|
@@ -157684,7 +157743,7 @@ async function runProdDoctor(opts, deps = {}) {
|
|
|
157684
157743
|
);
|
|
157685
157744
|
}
|
|
157686
157745
|
}
|
|
157687
|
-
const featureFlags = deps.featureFlags ?? resolveFeatureFlags({ yaml: loadFeatureFlags() });
|
|
157746
|
+
const featureFlags = deps.featureFlags ?? resolveFeatureFlags({ yaml: loadFeatureFlags() }).values;
|
|
157688
157747
|
if (featureFlags.ptyHost) {
|
|
157689
157748
|
const probe = deps.probePtyHost ?? (() => probePtyHostStatus(hostSocketPath(getInstanceId())));
|
|
157690
157749
|
try {
|
|
@@ -157705,6 +157764,33 @@ async function runProdDoctor(opts, deps = {}) {
|
|
|
157705
157764
|
function toPowerShellLiteral(value) {
|
|
157706
157765
|
return `'${value.replace(/'/g, "''")}'`;
|
|
157707
157766
|
}
|
|
157767
|
+
function windowsFollowScript(paths, lines) {
|
|
157768
|
+
const list = `@(${paths.map(toPowerShellLiteral).join(", ")})`;
|
|
157769
|
+
return [
|
|
157770
|
+
"$ErrorActionPreference = 'Stop'",
|
|
157771
|
+
`$paths = ${list}`,
|
|
157772
|
+
`foreach ($f in $paths) { Get-Content -LiteralPath $f -Tail ${lines} }`,
|
|
157773
|
+
"$offsets = @{}",
|
|
157774
|
+
"foreach ($f in $paths) { $offsets[$f] = (Get-Item -LiteralPath $f).Length }",
|
|
157775
|
+
"while ($true) {",
|
|
157776
|
+
" foreach ($f in $paths) {",
|
|
157777
|
+
" try {",
|
|
157778
|
+
" $len = (Get-Item -LiteralPath $f).Length",
|
|
157779
|
+
" if ($len -lt $offsets[$f]) { $offsets[$f] = 0 }",
|
|
157780
|
+
" if ($len -gt $offsets[$f]) {",
|
|
157781
|
+
" $stream = [IO.File]::Open($f, 'Open', 'Read', 'ReadWrite')",
|
|
157782
|
+
" [void]$stream.Seek($offsets[$f], 'Begin')",
|
|
157783
|
+
" $reader = New-Object IO.StreamReader($stream)",
|
|
157784
|
+
" $reader.ReadToEnd()",
|
|
157785
|
+
" $reader.Dispose(); $stream.Dispose()",
|
|
157786
|
+
" $offsets[$f] = $len",
|
|
157787
|
+
" }",
|
|
157788
|
+
" } catch { }",
|
|
157789
|
+
" }",
|
|
157790
|
+
" Start-Sleep -Milliseconds 400",
|
|
157791
|
+
"}"
|
|
157792
|
+
].join("; ");
|
|
157793
|
+
}
|
|
157708
157794
|
var defaultSpawnTail = ({ files, lines, follow }) => {
|
|
157709
157795
|
const existing = files.filter((f2) => (0, import_node_fs23.existsSync)(f2));
|
|
157710
157796
|
if (existing.length === 0) {
|
|
@@ -157719,13 +157805,17 @@ var defaultSpawnTail = ({ files, lines, follow }) => {
|
|
|
157719
157805
|
"-NoProfile",
|
|
157720
157806
|
"-NonInteractive",
|
|
157721
157807
|
"-Command",
|
|
157722
|
-
|
|
157808
|
+
follow ? windowsFollowScript(existing, lines) : `$ErrorActionPreference = 'Stop'; Get-Content -LiteralPath @(${existing.map(toPowerShellLiteral).join(", ")}) -Tail ${lines}`
|
|
157723
157809
|
] : ["-n", String(lines), ...follow ? ["-F"] : [], ...existing];
|
|
157810
|
+
const program3 = isWindows3 ? "powershell.exe" : "tail";
|
|
157724
157811
|
return new Promise((resolve4) => {
|
|
157725
|
-
const child = (0, import_node_child_process8.spawn)(
|
|
157812
|
+
const child = (0, import_node_child_process8.spawn)(program3, args, {
|
|
157726
157813
|
stdio: ["ignore", "inherit", "inherit"]
|
|
157727
157814
|
});
|
|
157728
|
-
child.on(
|
|
157815
|
+
child.on(
|
|
157816
|
+
"error",
|
|
157817
|
+
(err) => resolve4({ ok: false, message: `${program3} failed: ${err.message}` })
|
|
157818
|
+
);
|
|
157729
157819
|
child.on("exit", (code) => resolve4({ ok: code === 0 }));
|
|
157730
157820
|
});
|
|
157731
157821
|
};
|