@threadbase-sh/streamer 1.52.3 → 1.52.4
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 +80 -23
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +42 -17
- 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 +42 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -114,7 +114,53 @@ interface FeatureFlagDefinition {
|
|
|
114
114
|
/** Full env var name. */
|
|
115
115
|
env: string;
|
|
116
116
|
}
|
|
117
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Values arriving from a config source. PARTIAL on purpose: server.yaml, the
|
|
119
|
+
* CLI and ServerConfig each speak about the flags they mention and stay silent
|
|
120
|
+
* on the rest, which is what lets the precedence chain fall through.
|
|
121
|
+
*/
|
|
122
|
+
type FeatureFlagValues = Partial<Record<FeatureFlagId, boolean>>;
|
|
123
|
+
/** Every flag, resolved. Total — see resolveFeatureFlags(). */
|
|
124
|
+
type ResolvedFeatureFlags = Record<FeatureFlagId, boolean>;
|
|
125
|
+
/**
|
|
126
|
+
* Which rung of the precedence chain decided a flag.
|
|
127
|
+
*
|
|
128
|
+
* Kept because the resolved boolean alone cannot answer "why is this on?", and
|
|
129
|
+
* that is the first question asked of a flag that is on when nobody expected it.
|
|
130
|
+
* `override` is the legacy ServerConfig field (see resolveFeatureFlags).
|
|
131
|
+
*/
|
|
132
|
+
type FeatureFlagSource = "override" | "env" | "cli" | "yaml" | "default";
|
|
133
|
+
declare const FEATURE_FLAGS: readonly [{
|
|
134
|
+
readonly id: "codexSystemPrompt";
|
|
135
|
+
readonly description: string;
|
|
136
|
+
readonly default: false;
|
|
137
|
+
readonly env: "THREADBASE_FEATURE_CODEX_SYSTEM_PROMPT";
|
|
138
|
+
}, {
|
|
139
|
+
readonly id: "sessionRehydration";
|
|
140
|
+
readonly description: string;
|
|
141
|
+
readonly default: true;
|
|
142
|
+
readonly env: "THREADBASE_FEATURE_SESSION_REHYDRATION";
|
|
143
|
+
}, {
|
|
144
|
+
readonly id: "liveActivityPush";
|
|
145
|
+
readonly description: string;
|
|
146
|
+
readonly default: false;
|
|
147
|
+
readonly env: "THREADBASE_FEATURE_LIVE_ACTIVITY_PUSH";
|
|
148
|
+
}, {
|
|
149
|
+
readonly id: "ptyHost";
|
|
150
|
+
readonly description: string;
|
|
151
|
+
readonly default: false;
|
|
152
|
+
readonly env: "THREADBASE_FEATURE_PTY_HOST";
|
|
153
|
+
}];
|
|
154
|
+
/**
|
|
155
|
+
* The registry's ids as a union, derived rather than declared.
|
|
156
|
+
*
|
|
157
|
+
* This is what makes `flags.ptyHsot` a compile error instead of `undefined`.
|
|
158
|
+
* Under the old `Record<string, boolean>` an index signature accepted any key,
|
|
159
|
+
* so a typo in a consumer read as falsy and silently disabled the feature it
|
|
160
|
+
* was meant to gate — the exact failure the "total map" contract exists to
|
|
161
|
+
* prevent, reachable through the one door that contract left open.
|
|
162
|
+
*/
|
|
163
|
+
type FeatureFlagId = (typeof FEATURE_FLAGS)[number]["id"];
|
|
118
164
|
|
|
119
165
|
declare const CLAUDE_CODE_PROVIDER: "claude-code";
|
|
120
166
|
declare const CODEX_CLI_PROVIDER: "codex-cli";
|
|
@@ -1695,7 +1741,8 @@ type ApiDeps = {
|
|
|
1695
1741
|
};
|
|
1696
1742
|
featureFlagsConfig: () => {
|
|
1697
1743
|
registry: readonly FeatureFlagDefinition[];
|
|
1698
|
-
values:
|
|
1744
|
+
values: ResolvedFeatureFlags;
|
|
1745
|
+
sources: Record<FeatureFlagId, FeatureFlagSource>;
|
|
1699
1746
|
};
|
|
1700
1747
|
publicUrl: string | null;
|
|
1701
1748
|
browseRoot: string | null;
|
|
@@ -2028,6 +2075,7 @@ declare class StreamerServer {
|
|
|
2028
2075
|
private ptyGracePeriodMs;
|
|
2029
2076
|
private defaultSystemPrompt;
|
|
2030
2077
|
private featureFlags;
|
|
2078
|
+
private featureFlagSources;
|
|
2031
2079
|
private codexSystemPromptEnabled;
|
|
2032
2080
|
private defaultPermissionMode;
|
|
2033
2081
|
private defaultModel;
|
package/dist/index.d.ts
CHANGED
|
@@ -114,7 +114,53 @@ interface FeatureFlagDefinition {
|
|
|
114
114
|
/** Full env var name. */
|
|
115
115
|
env: string;
|
|
116
116
|
}
|
|
117
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Values arriving from a config source. PARTIAL on purpose: server.yaml, the
|
|
119
|
+
* CLI and ServerConfig each speak about the flags they mention and stay silent
|
|
120
|
+
* on the rest, which is what lets the precedence chain fall through.
|
|
121
|
+
*/
|
|
122
|
+
type FeatureFlagValues = Partial<Record<FeatureFlagId, boolean>>;
|
|
123
|
+
/** Every flag, resolved. Total — see resolveFeatureFlags(). */
|
|
124
|
+
type ResolvedFeatureFlags = Record<FeatureFlagId, boolean>;
|
|
125
|
+
/**
|
|
126
|
+
* Which rung of the precedence chain decided a flag.
|
|
127
|
+
*
|
|
128
|
+
* Kept because the resolved boolean alone cannot answer "why is this on?", and
|
|
129
|
+
* that is the first question asked of a flag that is on when nobody expected it.
|
|
130
|
+
* `override` is the legacy ServerConfig field (see resolveFeatureFlags).
|
|
131
|
+
*/
|
|
132
|
+
type FeatureFlagSource = "override" | "env" | "cli" | "yaml" | "default";
|
|
133
|
+
declare const FEATURE_FLAGS: readonly [{
|
|
134
|
+
readonly id: "codexSystemPrompt";
|
|
135
|
+
readonly description: string;
|
|
136
|
+
readonly default: false;
|
|
137
|
+
readonly env: "THREADBASE_FEATURE_CODEX_SYSTEM_PROMPT";
|
|
138
|
+
}, {
|
|
139
|
+
readonly id: "sessionRehydration";
|
|
140
|
+
readonly description: string;
|
|
141
|
+
readonly default: true;
|
|
142
|
+
readonly env: "THREADBASE_FEATURE_SESSION_REHYDRATION";
|
|
143
|
+
}, {
|
|
144
|
+
readonly id: "liveActivityPush";
|
|
145
|
+
readonly description: string;
|
|
146
|
+
readonly default: false;
|
|
147
|
+
readonly env: "THREADBASE_FEATURE_LIVE_ACTIVITY_PUSH";
|
|
148
|
+
}, {
|
|
149
|
+
readonly id: "ptyHost";
|
|
150
|
+
readonly description: string;
|
|
151
|
+
readonly default: false;
|
|
152
|
+
readonly env: "THREADBASE_FEATURE_PTY_HOST";
|
|
153
|
+
}];
|
|
154
|
+
/**
|
|
155
|
+
* The registry's ids as a union, derived rather than declared.
|
|
156
|
+
*
|
|
157
|
+
* This is what makes `flags.ptyHsot` a compile error instead of `undefined`.
|
|
158
|
+
* Under the old `Record<string, boolean>` an index signature accepted any key,
|
|
159
|
+
* so a typo in a consumer read as falsy and silently disabled the feature it
|
|
160
|
+
* was meant to gate — the exact failure the "total map" contract exists to
|
|
161
|
+
* prevent, reachable through the one door that contract left open.
|
|
162
|
+
*/
|
|
163
|
+
type FeatureFlagId = (typeof FEATURE_FLAGS)[number]["id"];
|
|
118
164
|
|
|
119
165
|
declare const CLAUDE_CODE_PROVIDER: "claude-code";
|
|
120
166
|
declare const CODEX_CLI_PROVIDER: "codex-cli";
|
|
@@ -1695,7 +1741,8 @@ type ApiDeps = {
|
|
|
1695
1741
|
};
|
|
1696
1742
|
featureFlagsConfig: () => {
|
|
1697
1743
|
registry: readonly FeatureFlagDefinition[];
|
|
1698
|
-
values:
|
|
1744
|
+
values: ResolvedFeatureFlags;
|
|
1745
|
+
sources: Record<FeatureFlagId, FeatureFlagSource>;
|
|
1699
1746
|
};
|
|
1700
1747
|
publicUrl: string | null;
|
|
1701
1748
|
browseRoot: string | null;
|
|
@@ -2028,6 +2075,7 @@ declare class StreamerServer {
|
|
|
2028
2075
|
private ptyGracePeriodMs;
|
|
2029
2076
|
private defaultSystemPrompt;
|
|
2030
2077
|
private featureFlags;
|
|
2078
|
+
private featureFlagSources;
|
|
2031
2079
|
private codexSystemPromptEnabled;
|
|
2032
2080
|
private defaultPermissionMode;
|
|
2033
2081
|
private defaultModel;
|
package/dist/index.js
CHANGED
|
@@ -485,11 +485,12 @@ function validateFeatureFlagValues(raw) {
|
|
|
485
485
|
const out = {};
|
|
486
486
|
const dropped = [];
|
|
487
487
|
for (const [id, value] of Object.entries(raw)) {
|
|
488
|
-
|
|
488
|
+
const def = findFeatureFlag(id);
|
|
489
|
+
if (!def || typeof value !== "boolean") {
|
|
489
490
|
dropped.push(id);
|
|
490
491
|
continue;
|
|
491
492
|
}
|
|
492
|
-
out[id] = value;
|
|
493
|
+
out[def.id] = value;
|
|
493
494
|
}
|
|
494
495
|
if (dropped.length > 0) {
|
|
495
496
|
getLogger("feature-flags").warn(
|
|
@@ -504,15 +505,29 @@ function validateFeatureFlagValues(raw) {
|
|
|
504
505
|
}
|
|
505
506
|
function resolveFeatureFlags(opts) {
|
|
506
507
|
const env = opts?.env ?? process.env;
|
|
507
|
-
const
|
|
508
|
+
const values = {};
|
|
509
|
+
const sources = {};
|
|
508
510
|
for (const def of FEATURE_FLAGS) {
|
|
509
|
-
|
|
511
|
+
const rungs = [
|
|
512
|
+
["override", opts?.override?.[def.id]],
|
|
513
|
+
["env", parseBooleanEnv(env[def.env])],
|
|
514
|
+
["cli", opts?.cli?.[def.id]],
|
|
515
|
+
["yaml", opts?.yaml?.[def.id]]
|
|
516
|
+
];
|
|
517
|
+
const won = rungs.find(([, v]) => v !== void 0);
|
|
518
|
+
values[def.id] = won ? won[1] : def.default;
|
|
519
|
+
sources[def.id] = won ? won[0] : "default";
|
|
510
520
|
}
|
|
511
|
-
return
|
|
521
|
+
return { values, sources };
|
|
512
522
|
}
|
|
513
523
|
function nonDefaultFeatureFlags(values) {
|
|
514
524
|
return FEATURE_FLAGS.filter((f) => values[f.id] !== f.default).map((f) => f.id);
|
|
515
525
|
}
|
|
526
|
+
function describeFeatureFlags(resolution) {
|
|
527
|
+
return FEATURE_FLAGS.map(
|
|
528
|
+
(f) => `${f.id}=${resolution.values[f.id]}(${resolution.sources[f.id]})`
|
|
529
|
+
).join(" ");
|
|
530
|
+
}
|
|
516
531
|
|
|
517
532
|
// src/auth.ts
|
|
518
533
|
function configDir() {
|
|
@@ -14453,6 +14468,10 @@ var StreamerServer = class {
|
|
|
14453
14468
|
// Resolved once at boot; see src/feature-flags.ts. Total map — every registry
|
|
14454
14469
|
// id is present, so indexing it never yields undefined.
|
|
14455
14470
|
featureFlags;
|
|
14471
|
+
// Which rung of the precedence chain decided each flag. Reported at boot and
|
|
14472
|
+
// over GET /api/config/feature-flags — the resolved boolean alone cannot say
|
|
14473
|
+
// whether a value came from the environment, the CLI, server.yaml or nowhere.
|
|
14474
|
+
featureFlagSources;
|
|
14456
14475
|
// Derived from featureFlags.codexSystemPrompt. Kept as its own field so the
|
|
14457
14476
|
// read site in startFresh() is unchanged.
|
|
14458
14477
|
codexSystemPromptEnabled;
|
|
@@ -14566,10 +14585,13 @@ var StreamerServer = class {
|
|
|
14566
14585
|
this.codexRoots = config.codexRoots ?? [join23(homedir12(), ".codex", "sessions")];
|
|
14567
14586
|
this.ptyGracePeriodMs = config.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
|
|
14568
14587
|
this.defaultSystemPrompt = config.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
14569
|
-
|
|
14570
|
-
|
|
14571
|
-
|
|
14572
|
-
|
|
14588
|
+
const flagResolution = resolveFeatureFlags({
|
|
14589
|
+
override: config.codexSystemPromptEnabled === void 0 ? void 0 : { codexSystemPrompt: config.codexSystemPromptEnabled },
|
|
14590
|
+
cli: config.featureFlags,
|
|
14591
|
+
yaml: loadFeatureFlags()
|
|
14592
|
+
});
|
|
14593
|
+
this.featureFlags = flagResolution.values;
|
|
14594
|
+
this.featureFlagSources = flagResolution.sources;
|
|
14573
14595
|
this.codexSystemPromptEnabled = this.featureFlags.codexSystemPrompt;
|
|
14574
14596
|
this.defaultPermissionMode = config.defaultPermissionMode ?? loadDefaultPermissionMode() ?? "acceptEdits";
|
|
14575
14597
|
this.defaultModel = config.defaultModel ?? "sonnet";
|
|
@@ -14614,13 +14636,12 @@ var StreamerServer = class {
|
|
|
14614
14636
|
});
|
|
14615
14637
|
this.includeAgents = parseIncludeAgentsEnv(process.env.THREADBASE_INCLUDE_AGENTS);
|
|
14616
14638
|
this.agentEntrypoints = parseAgentEntrypointsEnv(process.env.THREADBASE_AGENT_ENTRYPOINTS);
|
|
14617
|
-
|
|
14618
|
-
|
|
14619
|
-
|
|
14620
|
-
|
|
14621
|
-
|
|
14622
|
-
|
|
14623
|
-
}
|
|
14639
|
+
this.log.info(`Feature flags: ${describeFeatureFlags(flagResolution)}`, {
|
|
14640
|
+
event: "config.feature_flags",
|
|
14641
|
+
values: this.featureFlags,
|
|
14642
|
+
sources: this.featureFlagSources,
|
|
14643
|
+
nonDefault: nonDefaultFeatureFlags(this.featureFlags)
|
|
14644
|
+
});
|
|
14624
14645
|
const rawRoot = process.env.THREADBASE_BROWSE_ROOT ?? loadBrowseRoot() ?? config.browseRoot;
|
|
14625
14646
|
if (rawRoot) {
|
|
14626
14647
|
realpath2(rawRoot).then((resolved) => {
|
|
@@ -15643,7 +15664,11 @@ var StreamerServer = class {
|
|
|
15643
15664
|
* the absence of that field is the signal that this endpoint is read-only.
|
|
15644
15665
|
*/
|
|
15645
15666
|
getFeatureFlagsConfig() {
|
|
15646
|
-
return {
|
|
15667
|
+
return {
|
|
15668
|
+
registry: FEATURE_FLAGS,
|
|
15669
|
+
values: this.featureFlags,
|
|
15670
|
+
sources: this.featureFlagSources
|
|
15671
|
+
};
|
|
15647
15672
|
}
|
|
15648
15673
|
getClaudeFlagsConfig() {
|
|
15649
15674
|
return {
|