glimmervoid 0.29.1 → 0.31.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/README.md +8 -10
- package/dist/bin/glimmervoid.js +152 -53
- package/dist/chunks/{adapters-DIavI9-i.js → adapters-ClA_GbtA.js} +1 -1
- package/dist/chunks/{adapters-Dlvu1FVB.js → adapters-gn7ulNO4.js} +49 -20
- package/dist/chunks/agent-api-cli-DAA7Jx8N.js +135 -0
- package/dist/chunks/{agent-api-core-D6LHrpSL.js → agent-api-core-hVcT26JW.js} +10 -1
- package/dist/chunks/{agent-setup-cli-C1CQ8Nkk.js → agent-setup-cli-CiJSDWSo.js} +15 -30
- package/dist/chunks/backoff-DjVpfHk7.js +14 -0
- package/dist/chunks/child-process-safe-BKPZ-5qQ.js +2 -0
- package/dist/chunks/{child-process-safe-Cz22RwlG.js → child-process-safe-Ch-Zgxqs.js} +2 -2
- package/dist/chunks/coerce-NSst8oRP.js +32 -0
- package/dist/chunks/{contracts-JKChPhZ0.js → config-W1Sv-iPK.js} +380 -1682
- package/dist/chunks/{config-store-DtPUFVaO.js → config-store-BCy4FHNG.js} +595 -327
- package/dist/chunks/config-store-DbBp7LJZ.js +2 -0
- package/dist/chunks/contracts-3LkrilQX.js +2078 -0
- package/dist/chunks/{grok-COekzawt.js → grok-CXKeSt2z.js} +69 -36
- package/dist/chunks/hook-command-core-DclEoE9e.js +14 -0
- package/dist/chunks/{hook-relay-core-BHfYFnYV.js → hook-relay-core-DA_bz2KA.js} +10 -5
- package/dist/chunks/hook-tools-UX2fmt81.js +72 -0
- package/dist/chunks/{ingest-editor-core-BuI1gMF_.js → ingest-editor-core-vR6Wv6bP.js} +24 -13
- package/dist/chunks/json-core-dfa_7Fak.js +23 -0
- package/dist/chunks/json-file-oEH9vt-0.js +352 -0
- package/dist/chunks/knowledge-graph-cli-D2tt5hPn.js +1354 -0
- package/dist/chunks/{loopback-post-C7C22eYi.js → loopback-post-DepQfdcM.js} +40 -26
- package/dist/chunks/{main-BVDjy7vH.js → main-DyTCWIu2.js} +28881 -20693
- package/dist/chunks/{node-pty-preflight-CK1knGIR.js → node-pty-preflight-C8QIlrtU.js} +4 -6
- package/dist/chunks/{pair-cli-BQQnhkkc.js → pair-cli-BbCBfd6l.js} +4 -3
- package/dist/chunks/paths-CeJl2Vcg.js +55 -0
- package/dist/chunks/{remote-config-BHoDFCpo.js → remote-config-DKIH_VxM.js} +43 -43
- package/dist/chunks/rtk-hook-core-BmEz2aq9.js +131 -0
- package/dist/chunks/rtk-resolver-BGDzw5XK.js +21 -0
- package/dist/chunks/rtk-resolver-D4Nke1Fr.js +2 -0
- package/dist/chunks/{runtime-paths-CVvq20A4.js → runtime-paths-BPoxENmK.js} +28 -8
- package/dist/chunks/runtime-paths-DU5jBEEO.js +2 -0
- package/dist/chunks/{sandbox-deps-BwvbDBDl.js → sandbox-deps-BPKE-ylr.js} +16 -13
- package/dist/chunks/sandbox-proxy-core-hnQgKR0T.js +27 -0
- package/dist/chunks/{session-CJtOveHl.js → session-fc4pFbon.js} +10 -2
- package/dist/chunks/text-DoirglWV.js +21 -0
- package/dist/chunks/timer-deps-u2DAQLok.js +15 -0
- package/dist/chunks/{visions-cli-DUZnopGY.js → visions-cli-CT5rjiwf.js} +1 -1
- package/dist/chunks/{visions-setup-C_kJobsy.js → visions-setup-BmOkI-QE.js} +20 -357
- package/dist/client/assets/index-C6KD8lgZ.css +1 -0
- package/dist/client/assets/index-CThQEAEy.js +249 -0
- package/dist/client/fonts/CommitMono-OFL.txt +92 -0
- package/dist/client/fonts/commit-mono-400-italic.woff2 +0 -0
- package/dist/client/fonts/commit-mono-400-regular.woff2 +0 -0
- package/dist/client/fonts/commit-mono-700-italic.woff2 +0 -0
- package/dist/client/fonts/commit-mono-700-regular.woff2 +0 -0
- package/dist/client/index.html +7 -24
- package/dist/client/octicons-LICENSE.txt +21 -0
- package/dist/scripts/postinstall-path-check.js +1 -1
- package/dist/server/index.js +3 -3
- package/dist/session/command-hook-relay.js +1 -1
- package/dist/session/hook-relay.js +3 -3
- package/dist/session/{rtk-relay.js → hook-tool-relay.js} +32 -15
- package/dist/session/sandbox-open-launcher.js +79 -0
- package/dist/session/statusline-relay.js +8 -18
- package/dist/session/visions-relay.js +20 -11
- package/package.json +5 -1
- package/dist/chunks/agent-api-cli-ClOzm28U.js +0 -71
- package/dist/chunks/config-store-DtUGwi4-.js +0 -2
- package/dist/chunks/ingest-core-DWI1Mbu4.js +0 -342
- package/dist/chunks/rtk-hook-core-DRYrKwYP.js +0 -26
- package/dist/chunks/rtk-resolver-DDVVNRBT.js +0 -2
- package/dist/chunks/rtk-resolver-oGmO-ew7.js +0 -58
- package/dist/client/assets/index-BTwYgSAH.js +0 -248
- package/dist/client/assets/index-DgA708PE.css +0 -1
|
@@ -1,50 +1,14 @@
|
|
|
1
|
+
import { n as errorLabel, r as errorMessage } from "./text-DoirglWV.js";
|
|
2
|
+
import { i as equalsIgnoringCaseOnWindows, t as canonicalizePath } from "./paths-CeJl2Vcg.js";
|
|
1
3
|
import { n as glimmervoidHomeDir$1, t as decideConfigPath } from "./config-path-core-DFt-TemS.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
+
import { a as positiveIntOr, c as textOr, n as isRecord } from "./coerce-NSst8oRP.js";
|
|
5
|
+
import { d as writeJsonAtomicSync, f as writeTextAtomic, p as writeTextAtomicSync } from "./json-file-oEH9vt-0.js";
|
|
6
|
+
import { St as normalizeLegacyConfigNumbers, f as BranchGcFileSettings, i as Config, l as configIssueMessage, m as DEFAULT_FACTORY_PROTECTED_PATHS, n as BUILTIN_AGENT_IDS, p as DEFAULT_FACTORY_CHECKS, s as RUNTIME_CONFIG_SCALAR_KEYS, t as AGENT_ID_SHAPE_MESSAGE } from "./config-W1Sv-iPK.js";
|
|
7
|
+
import { V as FeatureFlagsResponse, W as TELEMETRY_KILL_SWITCH_FLAG } from "./contracts-3LkrilQX.js";
|
|
4
8
|
import fs from "node:fs";
|
|
5
9
|
import path from "node:path";
|
|
6
10
|
import os from "node:os";
|
|
7
|
-
import { promisify } from "node:util";
|
|
8
11
|
import crypto, { createHash } from "node:crypto";
|
|
9
|
-
//#region shared/paths.ts
|
|
10
|
-
var realpathNative = promisify(fs.realpath.native);
|
|
11
|
-
function canonicalizePath(p) {
|
|
12
|
-
try {
|
|
13
|
-
return fs.realpathSync.native(p);
|
|
14
|
-
} catch {
|
|
15
|
-
return p;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
function caseFoldedPathKey(value) {
|
|
19
|
-
return process.platform === "win32" ? value.toLowerCase() : value;
|
|
20
|
-
}
|
|
21
|
-
function equalsIgnoringCaseOnWindows(a, b) {
|
|
22
|
-
return caseFoldedPathKey(a) === caseFoldedPathKey(b);
|
|
23
|
-
}
|
|
24
|
-
function isSameDirectoryPath(a, b) {
|
|
25
|
-
const resolvedA = path.resolve(String(a || ""));
|
|
26
|
-
const resolvedB = path.resolve(String(b || ""));
|
|
27
|
-
if (equalsIgnoringCaseOnWindows(resolvedA, resolvedB)) return true;
|
|
28
|
-
if (process.platform !== "win32" && process.platform !== "darwin") return false;
|
|
29
|
-
return equalsIgnoringCaseOnWindows(canonicalizePath(resolvedA), canonicalizePath(resolvedB));
|
|
30
|
-
}
|
|
31
|
-
async function comparableDirectoryPath(candidatePath) {
|
|
32
|
-
const resolvedPath = path.resolve(String(candidatePath || ""));
|
|
33
|
-
let canonicalPath;
|
|
34
|
-
try {
|
|
35
|
-
canonicalPath = await realpathNative(resolvedPath);
|
|
36
|
-
} catch {
|
|
37
|
-
canonicalPath = resolvedPath;
|
|
38
|
-
}
|
|
39
|
-
if (process.platform !== "win32") return canonicalPath;
|
|
40
|
-
return canonicalPath.toLowerCase();
|
|
41
|
-
}
|
|
42
|
-
function safePathSegment(value) {
|
|
43
|
-
return String(value).replace(/[<>:"/\\|?*\x00-\x1f]/g, "-").replace(/[. ]+$/, "") || "_";
|
|
44
|
-
}
|
|
45
|
-
function sanitizeWorktreeName(value) {
|
|
46
|
-
return String(value || "").replace(/[^\w.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
47
|
-
}
|
|
48
12
|
var DEFAULT_BRANCH_GC_PREFIXES = ["glimmervoid/session/", "worktree-agent-"];
|
|
49
13
|
var DAY_MS = 864e5;
|
|
50
14
|
var STALE_DELETION_REASON = "stale-orphan";
|
|
@@ -201,23 +165,6 @@ function planWorktreeGc({ worktrees, liveWorktreePaths, integrationTips, prefixe
|
|
|
201
165
|
};
|
|
202
166
|
}
|
|
203
167
|
//#endregion
|
|
204
|
-
//#region server/core/usage-number-core.ts
|
|
205
|
-
function safeNumber(value) {
|
|
206
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return 0;
|
|
207
|
-
return value;
|
|
208
|
-
}
|
|
209
|
-
function numberOrNull(value) {
|
|
210
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
211
|
-
return value;
|
|
212
|
-
}
|
|
213
|
-
function stringOrNull(value) {
|
|
214
|
-
if (typeof value !== "string" || value.length === 0) return null;
|
|
215
|
-
return value;
|
|
216
|
-
}
|
|
217
|
-
function isPlainObject(value) {
|
|
218
|
-
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
219
|
-
}
|
|
220
|
-
//#endregion
|
|
221
168
|
//#region server/core/config-secrets-core.ts
|
|
222
169
|
var ENV_SECRET_BINDINGS = Object.freeze([{
|
|
223
170
|
blockName: "posthog",
|
|
@@ -242,7 +189,7 @@ function readEnvSecrets(env) {
|
|
|
242
189
|
}
|
|
243
190
|
function copyOfBlock(config, blockName) {
|
|
244
191
|
const block = config[blockName];
|
|
245
|
-
if (!
|
|
192
|
+
if (!isRecord(block)) return {};
|
|
246
193
|
return { ...block };
|
|
247
194
|
}
|
|
248
195
|
function withEnvSecrets(config, envSecrets) {
|
|
@@ -258,7 +205,7 @@ function withoutEnvSecrets(config, envSecrets) {
|
|
|
258
205
|
if (envSecrets.length === 0) return config;
|
|
259
206
|
const stripped = { ...config };
|
|
260
207
|
for (const secret of envSecrets) {
|
|
261
|
-
if (!
|
|
208
|
+
if (!isRecord(stripped[secret.blockName])) continue;
|
|
262
209
|
const remaining = copyOfBlock(stripped, secret.blockName);
|
|
263
210
|
delete remaining[secret.secretKey];
|
|
264
211
|
if (Object.keys(remaining).length === 0) {
|
|
@@ -271,7 +218,7 @@ function withoutEnvSecrets(config, envSecrets) {
|
|
|
271
218
|
}
|
|
272
219
|
function withSortedKeys(value) {
|
|
273
220
|
if (Array.isArray(value)) return value.map(withSortedKeys);
|
|
274
|
-
if (!
|
|
221
|
+
if (!isRecord(value)) return value;
|
|
275
222
|
const sorted = {};
|
|
276
223
|
for (const key of Object.keys(value).sort()) sorted[key] = withSortedKeys(value[key]);
|
|
277
224
|
return sorted;
|
|
@@ -285,7 +232,7 @@ var POSTHOG_INGEST_HOST = "https://us.i.posthog.com";
|
|
|
285
232
|
var TELEMETRY_BATCH_URL = `${POSTHOG_INGEST_HOST}/batch/`;
|
|
286
233
|
var TELEMETRY_FLAGS_URL = `${POSTHOG_INGEST_HOST}/flags?v=2`;
|
|
287
234
|
var MAX_STACK_LENGTH = 16384;
|
|
288
|
-
var FIRST_RUN_NOTICE = ["Glimmervoid sends anonymous usage and error data to help improve it: app starts, daily activity, session starts and ends with the agent kind, exit kind and duration, errors as their type, error code and scrubbed stack frames,
|
|
235
|
+
var FIRST_RUN_NOTICE = ["Glimmervoid sends anonymous usage and error data to help improve it: app starts, daily activity, session starts and ends with the agent kind, exit kind and duration, errors as their type, error code and scrubbed stack frames, per session token counts, models and estimated cost, and factory outcomes as the kind of decision, how it came out, its duration and attempt number. No error messages, paths, repository names, prompts, objectives, model output or terminal output.", "Turn it off in Settings > Privacy, or set GLIMMERVOID_TELEMETRY=0 or DO_NOT_TRACK=1."].join("\n");
|
|
289
236
|
var TRUTHY_FLAGS = /* @__PURE__ */ new Set([
|
|
290
237
|
"1",
|
|
291
238
|
"true",
|
|
@@ -594,11 +541,395 @@ function decideIsDevInstall({ installFlavor, isBundled, homeDir, defaultHomeDir
|
|
|
594
541
|
if (installFlavor === "clone" || !isBundled) return true;
|
|
595
542
|
return comparablePath(homeDir) !== comparablePath(defaultHomeDir);
|
|
596
543
|
}
|
|
544
|
+
function buildFactoryDecisionEvent(decision) {
|
|
545
|
+
return {
|
|
546
|
+
kind: decision.kind,
|
|
547
|
+
outcome: decision.outcome,
|
|
548
|
+
failure_class: decision.failureClass,
|
|
549
|
+
duration_ms: decision.durationMs,
|
|
550
|
+
attempt: decision.attempt
|
|
551
|
+
};
|
|
552
|
+
}
|
|
597
553
|
function nodeMajorVersion(nodeVersion) {
|
|
598
554
|
const major = Number.parseInt(nodeVersion.replace(/^v/, ""), 10);
|
|
599
555
|
return Number.isFinite(major) ? major : 0;
|
|
600
556
|
}
|
|
601
557
|
//#endregion
|
|
558
|
+
//#region shared/display-text.ts
|
|
559
|
+
var SECONDS_PER_MINUTE = 60;
|
|
560
|
+
var HOURS_PER_DAY = 24;
|
|
561
|
+
var UNIT_WORDS = {
|
|
562
|
+
compact: {
|
|
563
|
+
second: (count) => `${count}s`,
|
|
564
|
+
minute: (count) => `${count}m`,
|
|
565
|
+
hour: (count) => `${count}h`,
|
|
566
|
+
day: (count) => `${count}d`
|
|
567
|
+
},
|
|
568
|
+
abbreviated: {
|
|
569
|
+
second: (count) => `${count} sec`,
|
|
570
|
+
minute: (count) => `${count} min`,
|
|
571
|
+
hour: (count) => `${count} hr`,
|
|
572
|
+
day: (count) => `${count} ${count === 1 ? "day" : "days"}`
|
|
573
|
+
},
|
|
574
|
+
spelled: {
|
|
575
|
+
second: (count) => `${count} ${count === 1 ? "second" : "seconds"}`,
|
|
576
|
+
minute: (count) => `${count} ${count === 1 ? "minute" : "minutes"}`,
|
|
577
|
+
hour: (count) => `${count} ${count === 1 ? "hour" : "hours"}`,
|
|
578
|
+
day: (count) => `${count} ${count === 1 ? "day" : "days"}`
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
var COMPACT_FLOORED_AGO = {
|
|
582
|
+
rounding: "floor",
|
|
583
|
+
wording: "compact",
|
|
584
|
+
suffix: " ago",
|
|
585
|
+
justNowBelowSeconds: 0
|
|
586
|
+
};
|
|
587
|
+
function pad2(value) {
|
|
588
|
+
return String(value).padStart(2, "0");
|
|
589
|
+
}
|
|
590
|
+
function wholeSeconds(elapsedMs, rounding) {
|
|
591
|
+
return Math.max(0, Math[rounding](elapsedMs / 1e3));
|
|
592
|
+
}
|
|
593
|
+
function largestUnitText(totalSeconds, wording) {
|
|
594
|
+
const words = UNIT_WORDS[wording];
|
|
595
|
+
if (totalSeconds < SECONDS_PER_MINUTE) return words.second(totalSeconds);
|
|
596
|
+
const minutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE);
|
|
597
|
+
if (minutes < 60) return words.minute(minutes);
|
|
598
|
+
const hours = Math.floor(minutes / 60);
|
|
599
|
+
if (hours < HOURS_PER_DAY) return words.hour(hours);
|
|
600
|
+
return words.day(Math.floor(hours / HOURS_PER_DAY));
|
|
601
|
+
}
|
|
602
|
+
function elapsedText(elapsedMs, options) {
|
|
603
|
+
const totalSeconds = wholeSeconds(elapsedMs, options.rounding);
|
|
604
|
+
if (totalSeconds < options.justNowBelowSeconds) return "just now";
|
|
605
|
+
return `${largestUnitText(totalSeconds, options.wording)}${options.suffix}`;
|
|
606
|
+
}
|
|
607
|
+
function localDayKey(moment) {
|
|
608
|
+
const date = new Date(moment);
|
|
609
|
+
return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
|
|
610
|
+
}
|
|
611
|
+
//#endregion
|
|
612
|
+
//#region server/core/ingest-core.ts
|
|
613
|
+
var SOURCE_NAMES = [
|
|
614
|
+
"terminal",
|
|
615
|
+
"agentLogs",
|
|
616
|
+
"git",
|
|
617
|
+
"fs",
|
|
618
|
+
"shellHistory",
|
|
619
|
+
"editor"
|
|
620
|
+
];
|
|
621
|
+
var SOURCE_DEFAULTS = Object.freeze({
|
|
622
|
+
terminal: Object.freeze({
|
|
623
|
+
maxEntries: 200,
|
|
624
|
+
maxBytes: 262144,
|
|
625
|
+
digestQuota: 8,
|
|
626
|
+
flushMs: 500,
|
|
627
|
+
accumulatorBytes: 8192,
|
|
628
|
+
windowBytes: 65536
|
|
629
|
+
}),
|
|
630
|
+
agentLogs: Object.freeze({
|
|
631
|
+
maxEntries: 200,
|
|
632
|
+
maxBytes: 131072,
|
|
633
|
+
digestQuota: 8,
|
|
634
|
+
pollMs: 2e3
|
|
635
|
+
}),
|
|
636
|
+
git: Object.freeze({
|
|
637
|
+
maxEntries: 100,
|
|
638
|
+
maxBytes: 65536,
|
|
639
|
+
digestQuota: 6,
|
|
640
|
+
debounceMs: 1e3,
|
|
641
|
+
pollMs: 6e4
|
|
642
|
+
}),
|
|
643
|
+
fs: Object.freeze({
|
|
644
|
+
maxEntries: 500,
|
|
645
|
+
maxBytes: 131072,
|
|
646
|
+
digestQuota: 8,
|
|
647
|
+
batchMs: 500
|
|
648
|
+
}),
|
|
649
|
+
shellHistory: Object.freeze({
|
|
650
|
+
maxEntries: 100,
|
|
651
|
+
maxBytes: 32768,
|
|
652
|
+
digestQuota: 6,
|
|
653
|
+
pollMs: 2e3
|
|
654
|
+
}),
|
|
655
|
+
editor: Object.freeze({
|
|
656
|
+
maxEntries: 100,
|
|
657
|
+
maxBytes: 32768,
|
|
658
|
+
digestQuota: 6
|
|
659
|
+
})
|
|
660
|
+
});
|
|
661
|
+
var KINDS_BY_SOURCE = Object.freeze({
|
|
662
|
+
terminal: Object.freeze(["output"]),
|
|
663
|
+
agentLogs: Object.freeze(["agent-turn", "agent-tool"]),
|
|
664
|
+
git: Object.freeze([
|
|
665
|
+
"commit",
|
|
666
|
+
"status-change",
|
|
667
|
+
"branch-change"
|
|
668
|
+
]),
|
|
669
|
+
fs: Object.freeze(["file-change"]),
|
|
670
|
+
shellHistory: Object.freeze(["command"]),
|
|
671
|
+
editor: Object.freeze([
|
|
672
|
+
"doc-save",
|
|
673
|
+
"doc-open",
|
|
674
|
+
"doc-close"
|
|
675
|
+
])
|
|
676
|
+
});
|
|
677
|
+
var MAX_DETAIL_CHARS = 1e3;
|
|
678
|
+
var MAX_DETAIL_KEYS = 12;
|
|
679
|
+
var DEFAULT_DIGEST_BUDGET_CHARS = 2e3;
|
|
680
|
+
var SCRUB_PLACEHOLDER = "[scrubbed]";
|
|
681
|
+
var DIGEST_HEADER = "Recent activity on this machine, newest first:";
|
|
682
|
+
var LIST_KEYS = Object.freeze({
|
|
683
|
+
fs: "roots",
|
|
684
|
+
shellHistory: "shells"
|
|
685
|
+
});
|
|
686
|
+
function stringList(raw) {
|
|
687
|
+
if (!Array.isArray(raw)) return [];
|
|
688
|
+
const values = [];
|
|
689
|
+
for (const entry of raw) {
|
|
690
|
+
if (typeof entry !== "string" || !entry.trim() || values.includes(entry.trim())) continue;
|
|
691
|
+
values.push(entry.trim());
|
|
692
|
+
}
|
|
693
|
+
return values;
|
|
694
|
+
}
|
|
695
|
+
function disabledSource(name) {
|
|
696
|
+
const source = {
|
|
697
|
+
enabled: false,
|
|
698
|
+
...SOURCE_DEFAULTS[name]
|
|
699
|
+
};
|
|
700
|
+
const listKey = LIST_KEYS[name];
|
|
701
|
+
if (listKey) source[listKey] = [];
|
|
702
|
+
return source;
|
|
703
|
+
}
|
|
704
|
+
function disabledConfig() {
|
|
705
|
+
const sources = {};
|
|
706
|
+
for (const name of SOURCE_NAMES) sources[name] = disabledSource(name);
|
|
707
|
+
return {
|
|
708
|
+
enabled: false,
|
|
709
|
+
sources
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function resolveSource(name, raw) {
|
|
713
|
+
const defaults = SOURCE_DEFAULTS[name];
|
|
714
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return disabledSource(name);
|
|
715
|
+
const rawSource = raw;
|
|
716
|
+
const resolved = { enabled: rawSource.enabled === true };
|
|
717
|
+
for (const [key, value] of Object.entries(defaults)) resolved[key] = positiveIntOr(rawSource[key], value);
|
|
718
|
+
const listKey = LIST_KEYS[name];
|
|
719
|
+
if (listKey) resolved[listKey] = stringList(rawSource[listKey]);
|
|
720
|
+
return resolved;
|
|
721
|
+
}
|
|
722
|
+
function resolveIngestConfig(raw) {
|
|
723
|
+
if (!isRecord(raw)) return disabledConfig();
|
|
724
|
+
if (raw.enabled !== true) return disabledConfig();
|
|
725
|
+
const rawSourcesValue = raw.sources;
|
|
726
|
+
const rawSources = isRecord(rawSourcesValue) ? rawSourcesValue : {};
|
|
727
|
+
const sources = {};
|
|
728
|
+
for (const name of SOURCE_NAMES) sources[name] = resolveSource(name, rawSources[name]);
|
|
729
|
+
return {
|
|
730
|
+
enabled: true,
|
|
731
|
+
sources
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
function enabledSourceNames(config) {
|
|
735
|
+
if (!config || config.enabled !== true) return [];
|
|
736
|
+
return SOURCE_NAMES.filter((name) => config.sources[name] && config.sources[name].enabled === true);
|
|
737
|
+
}
|
|
738
|
+
var SECRET_WORD = "(?:password|passwd|pwd|token|api[_-]?key|secret|credential|auth[_-]?token|access[_-]?key|secret[_-]?key|private[_-]?key|client[_-]?secret|connection[_-]?string)";
|
|
739
|
+
var QUOTED_OR_BARE = "(?:\"[^\"\\r\\n]*\"|'[^'\\r\\n]*'|[^\\s\\r\\n]+)";
|
|
740
|
+
var GAP = "[^\\S\\r\\n]";
|
|
741
|
+
var FLAG_REACH = "[^\\r\\n;&|]{0,200}?";
|
|
742
|
+
var SCRUB_PATTERNS = [
|
|
743
|
+
new RegExp(`(${SECRET_WORD}${GAP}*(?:=>|[:=])${GAP}*)${QUOTED_OR_BARE}`, "gi"),
|
|
744
|
+
new RegExp(`((?:^|\\s)--?${SECRET_WORD}${GAP}+)${QUOTED_OR_BARE}`, "gi"),
|
|
745
|
+
new RegExp(`((?:^|\\s)-(?:pwd|pw)(?![a-z])${GAP}*=?${GAP}*)${QUOTED_OR_BARE}`, "g"),
|
|
746
|
+
new RegExp(`(-AsPlainText${GAP}+)${QUOTED_OR_BARE}`, "gi"),
|
|
747
|
+
new RegExp(`((?:^|\\s)setx${GAP}+[^\\s\\r\\n]*${SECRET_WORD}[^\\s\\r\\n]*${GAP}+)${QUOTED_OR_BARE}`, "gi"),
|
|
748
|
+
new RegExp(`(\\bBearer${GAP}+)[A-Za-z0-9._~+/=-]{8,}`, "gi"),
|
|
749
|
+
new RegExp(`(\\bauthorization${GAP}*:${GAP}*Basic${GAP}+)[A-Za-z0-9+/=_-]{8,}`, "gi"),
|
|
750
|
+
new RegExp(`(\\bdocker${GAP}+login\\b${FLAG_REACH}${GAP}-p(?:${GAP}+|=))${QUOTED_OR_BARE}`, "gi"),
|
|
751
|
+
new RegExp(`(\\bcurl\\b${FLAG_REACH}${GAP}(?:--user|-u)(?:${GAP}+|=)[^\\s:\\r\\n]+:)${QUOTED_OR_BARE}`, "gi"),
|
|
752
|
+
new RegExp(`(\\b(?:mysql|mysqldump|mysqladmin|mysqlshow|mariadb|mariadb-dump)\\b${FLAG_REACH}${GAP}-p)${QUOTED_OR_BARE}`, "g"),
|
|
753
|
+
/\b([a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:)[^\s@/]+(?=@)/gi
|
|
754
|
+
];
|
|
755
|
+
var SECRET_SHAPE_PATTERNS = [
|
|
756
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
757
|
+
/\bgh[pousr]_[A-Za-z0-9]{16,}\b/g,
|
|
758
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
|
|
759
|
+
/\bxox[abprs]-[A-Za-z0-9-]{10,}\b/g,
|
|
760
|
+
/\beyJ[A-Za-z0-9_=-]{6,}\.[A-Za-z0-9_=-]{6,}\.[A-Za-z0-9_=-]{4,}/g,
|
|
761
|
+
/-----BEGIN(?:[A-Z0-9 ]+)? PRIVATE KEY-----/g
|
|
762
|
+
];
|
|
763
|
+
function scrubText(text) {
|
|
764
|
+
if (typeof text !== "string" || !text) return typeof text === "string" ? text : "";
|
|
765
|
+
let scrubbed = text;
|
|
766
|
+
for (const pattern of SCRUB_PATTERNS) {
|
|
767
|
+
pattern.lastIndex = 0;
|
|
768
|
+
scrubbed = scrubbed.replace(pattern, (_match, prefix) => `${prefix}${SCRUB_PLACEHOLDER}`);
|
|
769
|
+
}
|
|
770
|
+
for (const pattern of SECRET_SHAPE_PATTERNS) {
|
|
771
|
+
pattern.lastIndex = 0;
|
|
772
|
+
scrubbed = scrubbed.replace(pattern, SCRUB_PLACEHOLDER);
|
|
773
|
+
}
|
|
774
|
+
return scrubbed;
|
|
775
|
+
}
|
|
776
|
+
function scrubDetail(detail) {
|
|
777
|
+
if (!detail || typeof detail !== "object" || Array.isArray(detail)) return null;
|
|
778
|
+
const scrubbed = {};
|
|
779
|
+
let keys = 0;
|
|
780
|
+
for (const [key, value] of Object.entries(detail)) {
|
|
781
|
+
if (keys >= MAX_DETAIL_KEYS) break;
|
|
782
|
+
keys += 1;
|
|
783
|
+
if (typeof value === "string") {
|
|
784
|
+
scrubbed[key] = scrubText(value).slice(0, MAX_DETAIL_CHARS);
|
|
785
|
+
continue;
|
|
786
|
+
}
|
|
787
|
+
if (typeof value === "number" && Number.isFinite(value)) scrubbed[key] = value;
|
|
788
|
+
if (typeof value === "boolean") scrubbed[key] = value;
|
|
789
|
+
}
|
|
790
|
+
return scrubbed;
|
|
791
|
+
}
|
|
792
|
+
function normalizeScope(raw) {
|
|
793
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {
|
|
794
|
+
root: null,
|
|
795
|
+
sessionId: null
|
|
796
|
+
};
|
|
797
|
+
const rawScope = raw;
|
|
798
|
+
return {
|
|
799
|
+
root: textOr(rawScope.root, null),
|
|
800
|
+
sessionId: textOr(rawScope.sessionId, null)
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
function normalizeEvent(raw, { seq, now }) {
|
|
804
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
805
|
+
const rawEvent = raw;
|
|
806
|
+
const source = textOr(rawEvent.source, null);
|
|
807
|
+
if (!source || !KINDS_BY_SOURCE[source]) return null;
|
|
808
|
+
const kind = textOr(rawEvent.kind, null);
|
|
809
|
+
if (!kind || !KINDS_BY_SOURCE[source].includes(kind)) return null;
|
|
810
|
+
const summary = scrubText(typeof rawEvent.summary === "string" ? rawEvent.summary : "").replace(/\s+/g, " ").trim().slice(0, 400);
|
|
811
|
+
if (!summary) return null;
|
|
812
|
+
const ts = Number(rawEvent.ts);
|
|
813
|
+
return {
|
|
814
|
+
source,
|
|
815
|
+
kind,
|
|
816
|
+
ts: Number.isFinite(ts) && ts > 0 ? Math.floor(ts) : now,
|
|
817
|
+
seq,
|
|
818
|
+
scope: normalizeScope(rawEvent.scope),
|
|
819
|
+
summary,
|
|
820
|
+
detail: scrubDetail(rawEvent.detail)
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
function eventBytes(event) {
|
|
824
|
+
return Buffer.byteLength(JSON.stringify(event), "utf8");
|
|
825
|
+
}
|
|
826
|
+
function createRing({ maxEntries, maxBytes }) {
|
|
827
|
+
return {
|
|
828
|
+
entries: [],
|
|
829
|
+
totalBytes: 0,
|
|
830
|
+
maxEntries,
|
|
831
|
+
maxBytes
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
function pushToRing(ring, event) {
|
|
835
|
+
const bytes = eventBytes(event);
|
|
836
|
+
ring.entries.push({
|
|
837
|
+
event,
|
|
838
|
+
bytes
|
|
839
|
+
});
|
|
840
|
+
ring.totalBytes += bytes;
|
|
841
|
+
while (ring.entries.length > 1 && (ring.entries.length > ring.maxEntries || ring.totalBytes > ring.maxBytes)) {
|
|
842
|
+
const evicted = ring.entries.shift();
|
|
843
|
+
ring.totalBytes -= evicted ? evicted.bytes : 0;
|
|
844
|
+
}
|
|
845
|
+
return event;
|
|
846
|
+
}
|
|
847
|
+
function createIngestStore(config) {
|
|
848
|
+
const resolved = config?.sources ? config : disabledConfig();
|
|
849
|
+
const rings = /* @__PURE__ */ new Map();
|
|
850
|
+
for (const name of enabledSourceNames(resolved)) rings.set(name, createRing(resolved.sources[name]));
|
|
851
|
+
return {
|
|
852
|
+
config: resolved,
|
|
853
|
+
rings,
|
|
854
|
+
seq: 0
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
function publishEvent(store, raw, now) {
|
|
858
|
+
const source = textOr((raw ?? null)?.source, null);
|
|
859
|
+
if (!source) return null;
|
|
860
|
+
const ring = store.rings.get(source);
|
|
861
|
+
if (!ring) return null;
|
|
862
|
+
const event = normalizeEvent(raw, {
|
|
863
|
+
seq: store.seq + 1,
|
|
864
|
+
now
|
|
865
|
+
});
|
|
866
|
+
if (!event) return null;
|
|
867
|
+
store.seq += 1;
|
|
868
|
+
return pushToRing(ring, event);
|
|
869
|
+
}
|
|
870
|
+
function latestSeq(store) {
|
|
871
|
+
const seq = Number(store?.seq);
|
|
872
|
+
if (!Number.isFinite(seq)) return 0;
|
|
873
|
+
return seq;
|
|
874
|
+
}
|
|
875
|
+
function snapshotEvents(store, { limit = 200 } = {}) {
|
|
876
|
+
const all = [];
|
|
877
|
+
for (const ring of store.rings.values()) for (const entry of ring.entries) all.push(entry.event);
|
|
878
|
+
all.sort((left, right) => right.seq - left.seq);
|
|
879
|
+
return all.slice(0, Math.max(0, Math.floor(limit)));
|
|
880
|
+
}
|
|
881
|
+
function ringStats(store) {
|
|
882
|
+
const stats = [];
|
|
883
|
+
for (const [source, ring] of store.rings) stats.push({
|
|
884
|
+
source,
|
|
885
|
+
events: ring.entries.length,
|
|
886
|
+
bytes: ring.totalBytes
|
|
887
|
+
});
|
|
888
|
+
return stats;
|
|
889
|
+
}
|
|
890
|
+
var SOURCE_LABELS = Object.freeze({
|
|
891
|
+
terminal: "terminal",
|
|
892
|
+
agentLogs: "agent",
|
|
893
|
+
git: "git",
|
|
894
|
+
fs: "files",
|
|
895
|
+
shellHistory: "shell",
|
|
896
|
+
editor: "editor"
|
|
897
|
+
});
|
|
898
|
+
function digestLine(event, now) {
|
|
899
|
+
const label = SOURCE_LABELS[event.source] || event.source;
|
|
900
|
+
const scope = event.scope.root ? "" : " (machine scope)";
|
|
901
|
+
return `- ${label} ${elapsedText(now - event.ts, COMPACT_FLOORED_AGO)}${scope}: ${event.summary}`;
|
|
902
|
+
}
|
|
903
|
+
function matchesScopes(event, scopes) {
|
|
904
|
+
if (!Array.isArray(scopes) || scopes.length === 0) return true;
|
|
905
|
+
if (!event.scope.root) return true;
|
|
906
|
+
return scopes.includes(event.scope.root);
|
|
907
|
+
}
|
|
908
|
+
function buildContextDigest(store, { scopes = null, budgetChars = DEFAULT_DIGEST_BUDGET_CHARS, now = NaN } = {}) {
|
|
909
|
+
const budget = positiveIntOr(budgetChars, DEFAULT_DIGEST_BUDGET_CHARS);
|
|
910
|
+
const candidates = [];
|
|
911
|
+
const takenBySource = /* @__PURE__ */ new Map();
|
|
912
|
+
for (const event of snapshotEvents(store, { limit: Number.MAX_SAFE_INTEGER })) {
|
|
913
|
+
if (!matchesScopes(event, scopes)) continue;
|
|
914
|
+
const quota = store.config.sources[event.source].digestQuota;
|
|
915
|
+
const taken = takenBySource.get(event.source) || 0;
|
|
916
|
+
if (taken >= quota) continue;
|
|
917
|
+
takenBySource.set(event.source, taken + 1);
|
|
918
|
+
candidates.push(event);
|
|
919
|
+
}
|
|
920
|
+
if (candidates.length === 0) return "";
|
|
921
|
+
const lines = [DIGEST_HEADER];
|
|
922
|
+
let used = 46;
|
|
923
|
+
for (const event of candidates) {
|
|
924
|
+
const line = scrubText(digestLine(event, now));
|
|
925
|
+
if (used + line.length + 1 > budget) break;
|
|
926
|
+
lines.push(line);
|
|
927
|
+
used += line.length + 1;
|
|
928
|
+
}
|
|
929
|
+
if (lines.length === 1) return "";
|
|
930
|
+
return lines.join("\n");
|
|
931
|
+
}
|
|
932
|
+
//#endregion
|
|
602
933
|
//#region server/core/settings-block-core.ts
|
|
603
934
|
var NO_BLOCKS = Object.freeze({});
|
|
604
935
|
var INGEST_SOURCES_SPEC = Object.freeze({
|
|
@@ -617,7 +948,7 @@ var INGEST_SPEC = Object.freeze({
|
|
|
617
948
|
});
|
|
618
949
|
function validateSettingsBlock(block, spec) {
|
|
619
950
|
if (block == null) return null;
|
|
620
|
-
if (!
|
|
951
|
+
if (!isRecord(block)) return `${spec.name} must be an object`;
|
|
621
952
|
for (const [key, value] of Object.entries(block)) {
|
|
622
953
|
if (value == null) continue;
|
|
623
954
|
if (spec.booleans.includes(key)) {
|
|
@@ -634,7 +965,7 @@ function validateSettingsBlock(block, spec) {
|
|
|
634
965
|
return null;
|
|
635
966
|
}
|
|
636
967
|
function pickSettingsBlock(stored, spec) {
|
|
637
|
-
if (!
|
|
968
|
+
if (!isRecord(stored)) return null;
|
|
638
969
|
const fields = stored;
|
|
639
970
|
const out = {};
|
|
640
971
|
for (const key of spec.booleans) if (fields[key] != null) out[key] = !!fields[key];
|
|
@@ -645,8 +976,8 @@ function pickSettingsBlock(stored, spec) {
|
|
|
645
976
|
return out;
|
|
646
977
|
}
|
|
647
978
|
function mergeSettingsBlock(stored, incoming, spec) {
|
|
648
|
-
const out =
|
|
649
|
-
if (!
|
|
979
|
+
const out = isRecord(stored) ? { ...stored } : {};
|
|
980
|
+
if (!isRecord(incoming)) return out;
|
|
650
981
|
const fields = incoming;
|
|
651
982
|
for (const key of spec.booleans) if (fields[key] != null) out[key] = !!fields[key];
|
|
652
983
|
for (const [key, nested] of Object.entries(spec.blocks)) {
|
|
@@ -656,230 +987,89 @@ function mergeSettingsBlock(stored, incoming, spec) {
|
|
|
656
987
|
return out;
|
|
657
988
|
}
|
|
658
989
|
//#endregion
|
|
659
|
-
//#region server/
|
|
660
|
-
var
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
990
|
+
//#region server/core/rtk-install-core.ts
|
|
991
|
+
var RTK_VERSION = "0.45.0";
|
|
992
|
+
var RTK_RELEASE_BASE_URL = "https://github.com/rtk-ai/rtk/releases/download/v0.45.0/";
|
|
993
|
+
var RTK_ASSETS = {
|
|
994
|
+
"linux-x64": {
|
|
995
|
+
file: "rtk-x86_64-unknown-linux-musl.tar.gz",
|
|
996
|
+
sha256: "c4c036fbf181fc55ef329786c8c17e0d427972b053b825944d968a6aafef1ba4"
|
|
997
|
+
},
|
|
998
|
+
"linux-arm64": {
|
|
999
|
+
file: "rtk-aarch64-unknown-linux-gnu.tar.gz",
|
|
1000
|
+
sha256: "80a746dd305ef944ff50ef011ae4ce3878dd5ba88dfe35d859d05498191637c3"
|
|
1001
|
+
},
|
|
1002
|
+
"win32-x64": {
|
|
1003
|
+
file: "rtk-x86_64-pc-windows-msvc.zip",
|
|
1004
|
+
sha256: "34cea9009a8099acdaf85147b971d95f65efabfa63fb3aea7d3e2b73e6f517c3"
|
|
1005
|
+
},
|
|
1006
|
+
"darwin-x64": {
|
|
1007
|
+
file: "rtk-x86_64-apple-darwin.tar.gz",
|
|
1008
|
+
sha256: "9ea02f889d5a2779e4fb700df4587824303c5a57cda22e903e30058079fca0ef"
|
|
1009
|
+
},
|
|
1010
|
+
"darwin-arm64": {
|
|
1011
|
+
file: "rtk-aarch64-apple-darwin.tar.gz",
|
|
1012
|
+
sha256: "064151cfc2d50b24d810b06a0af2e41b9c945e83534e4c438c3d3eae607fc3f4"
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
var INSTALL_FAILURE_COOLDOWN_MS = 6e5;
|
|
1016
|
+
function assetForPlatform(platform, arch) {
|
|
1017
|
+
const entry = RTK_ASSETS[`${platform}-${arch}`];
|
|
1018
|
+
if (!entry) return null;
|
|
667
1019
|
return {
|
|
668
|
-
|
|
669
|
-
|
|
1020
|
+
...entry,
|
|
1021
|
+
version: RTK_VERSION,
|
|
1022
|
+
url: `${RTK_RELEASE_BASE_URL}${entry.file}`
|
|
670
1023
|
};
|
|
671
1024
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
if (
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
const code = error?.code;
|
|
694
|
-
return code === "ENOENT" || code === "ENOTDIR";
|
|
695
|
-
}
|
|
696
|
-
function errorText(error) {
|
|
697
|
-
return error instanceof Error ? error.message : String(error);
|
|
698
|
-
}
|
|
699
|
-
async function loadJsonStateFile({ filePath, fsPromises, parse, nowMs }) {
|
|
700
|
-
let text;
|
|
701
|
-
try {
|
|
702
|
-
text = await fsPromises.readFile(filePath, "utf8");
|
|
703
|
-
} catch (error) {
|
|
704
|
-
if (isMissingFileError(error)) return { status: "missing" };
|
|
705
|
-
return {
|
|
706
|
-
status: "unreadable",
|
|
707
|
-
error
|
|
708
|
-
};
|
|
709
|
-
}
|
|
710
|
-
let value;
|
|
711
|
-
try {
|
|
712
|
-
value = parse(JSON.parse(text));
|
|
713
|
-
} catch {
|
|
714
|
-
value = null;
|
|
715
|
-
}
|
|
716
|
-
if (value !== null) return {
|
|
717
|
-
status: "loaded",
|
|
718
|
-
value
|
|
1025
|
+
function decideRtkInstall({ rtkEnabled, resolvedPath, platform, arch, inFlight = false, lastFailureAt = null, nowMs = 0, cooldownMs = INSTALL_FAILURE_COOLDOWN_MS } = {}) {
|
|
1026
|
+
if (!rtkEnabled) return {
|
|
1027
|
+
action: "skip",
|
|
1028
|
+
reason: "rtk-disabled"
|
|
1029
|
+
};
|
|
1030
|
+
if (resolvedPath) return {
|
|
1031
|
+
action: "skip",
|
|
1032
|
+
reason: "already-resolved"
|
|
1033
|
+
};
|
|
1034
|
+
if (inFlight) return {
|
|
1035
|
+
action: "skip",
|
|
1036
|
+
reason: "install-in-flight"
|
|
1037
|
+
};
|
|
1038
|
+
const asset = assetForPlatform(String(platform), String(arch));
|
|
1039
|
+
if (!asset) return {
|
|
1040
|
+
action: "skip",
|
|
1041
|
+
reason: `unsupported-platform:${platform}-${arch}`
|
|
1042
|
+
};
|
|
1043
|
+
if (typeof lastFailureAt === "number" && nowMs - lastFailureAt < cooldownMs) return {
|
|
1044
|
+
action: "skip",
|
|
1045
|
+
reason: "failure-cooldown"
|
|
719
1046
|
};
|
|
720
|
-
const movedTo = `${filePath}.corrupt-${nowMs()}`;
|
|
721
|
-
try {
|
|
722
|
-
await fsPromises.rename(filePath, movedTo);
|
|
723
|
-
} catch (error) {
|
|
724
|
-
return {
|
|
725
|
-
status: "unreadable",
|
|
726
|
-
error
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
1047
|
return {
|
|
730
|
-
|
|
731
|
-
|
|
1048
|
+
action: "install",
|
|
1049
|
+
reason: "missing-binary",
|
|
1050
|
+
asset
|
|
732
1051
|
};
|
|
733
1052
|
}
|
|
734
|
-
function
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
} catch (error) {
|
|
739
|
-
const delayMs = renameRetryPlan(error, attempt);
|
|
740
|
-
if (delayMs === null) throw error;
|
|
741
|
-
sleepSync(delayMs);
|
|
742
|
-
}
|
|
1053
|
+
function verifyDigest(expectedHex, actualHex) {
|
|
1054
|
+
if (typeof expectedHex !== "string" || typeof actualHex !== "string") return false;
|
|
1055
|
+
if (expectedHex.length !== actualHex.length) return false;
|
|
1056
|
+
return crypto.timingSafeEqual(Buffer.from(expectedHex, "utf8"), Buffer.from(actualHex, "utf8"));
|
|
743
1057
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
return;
|
|
748
|
-
} catch (error) {
|
|
749
|
-
const delayMs = renameRetryPlan(error, attempt);
|
|
750
|
-
if (delayMs === null) throw error;
|
|
751
|
-
await new Promise((resolve) => {
|
|
752
|
-
setTimeout(resolve, delayMs);
|
|
753
|
-
});
|
|
754
|
-
}
|
|
1058
|
+
function installTargetPath(glimmervoidHome, platform) {
|
|
1059
|
+
const binaryName = platform === "win32" ? "rtk.exe" : "rtk";
|
|
1060
|
+
return path.join(glimmervoidHome, "bin", binaryName);
|
|
755
1061
|
}
|
|
756
|
-
function
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
fsSync.writeFileSync(tmpPath, content, writeOptions(mode, encoding));
|
|
760
|
-
try {
|
|
761
|
-
renameWithRetrySync(fsSync, tmpPath, filePath);
|
|
762
|
-
} catch (error) {
|
|
763
|
-
fsSync.rmSync(tmpPath, { force: true });
|
|
764
|
-
throw error;
|
|
765
|
-
}
|
|
1062
|
+
function isRtkBinaryName(name, platform) {
|
|
1063
|
+
const wanted = platform === "win32" ? "rtk.exe" : "rtk";
|
|
1064
|
+
return name.toLowerCase() === wanted;
|
|
766
1065
|
}
|
|
767
|
-
function
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
const tmpPath = tmpPathFor(filePath);
|
|
773
|
-
await fsPromises.writeFile(tmpPath, content, writeOptions(mode, encoding));
|
|
774
|
-
try {
|
|
775
|
-
await renameWithRetry(fsPromises, tmpPath, filePath);
|
|
776
|
-
} catch (error) {
|
|
777
|
-
try {
|
|
778
|
-
await fsPromises.rm(tmpPath, { force: true });
|
|
779
|
-
} catch {}
|
|
780
|
-
throw error;
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
async function writeJsonAtomic(filePath, value, options) {
|
|
784
|
-
await writeTextAtomic(filePath, JSON.stringify(value, null, 2), options);
|
|
785
|
-
}
|
|
786
|
-
var appendChains = /* @__PURE__ */ new Map();
|
|
787
|
-
function appendChained(filePath, payload, { fsPromises = fs.promises, mkdir = false, encoding = "utf8", mode } = {}) {
|
|
788
|
-
const next = (appendChains.get(filePath) || Promise.resolve()).then(async () => {
|
|
789
|
-
if (mkdir) await fsPromises.mkdir(path.dirname(filePath), { recursive: true });
|
|
790
|
-
await fsPromises.appendFile(filePath, payload, writeOptions(mode, encoding));
|
|
791
|
-
});
|
|
792
|
-
const settled = next.then(() => {}, () => {});
|
|
793
|
-
appendChains.set(filePath, settled);
|
|
794
|
-
settled.then(() => {
|
|
795
|
-
if (appendChains.get(filePath) === settled) appendChains.delete(filePath);
|
|
796
|
-
});
|
|
797
|
-
return next;
|
|
798
|
-
}
|
|
799
|
-
function appendJsonLine(filePath, value, options) {
|
|
800
|
-
return appendChained(filePath, `${JSON.stringify(value)}\n`, options);
|
|
801
|
-
}
|
|
802
|
-
function appendJsonLines(filePath, values, options) {
|
|
803
|
-
if (values.length === 0) return Promise.resolve();
|
|
804
|
-
return appendChained(filePath, values.map((value) => `${JSON.stringify(value)}\n`).join(""), options);
|
|
805
|
-
}
|
|
806
|
-
function createJsonStateWriter({ filePath, fsPromises = fs.promises, warn = () => {} }) {
|
|
807
|
-
let signature = null;
|
|
808
|
-
let writeChain = Promise.resolve();
|
|
809
|
-
async function commit(payload) {
|
|
810
|
-
try {
|
|
811
|
-
await writeTextAtomic(filePath, payload, {
|
|
812
|
-
fsPromises,
|
|
813
|
-
mkdir: true
|
|
814
|
-
});
|
|
815
|
-
} catch (error) {
|
|
816
|
-
warn(error);
|
|
817
|
-
signature = null;
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
async function write(subject, buildPayload) {
|
|
821
|
-
const next = JSON.stringify(subject);
|
|
822
|
-
if (next === signature) return;
|
|
823
|
-
signature = next;
|
|
824
|
-
writeChain = writeChain.then(() => commit(buildPayload())).catch(() => {});
|
|
825
|
-
await writeChain;
|
|
826
|
-
}
|
|
827
|
-
function reset() {
|
|
828
|
-
signature = null;
|
|
829
|
-
}
|
|
830
|
-
return {
|
|
831
|
-
write,
|
|
832
|
-
reset,
|
|
833
|
-
idle: () => writeChain
|
|
834
|
-
};
|
|
835
|
-
}
|
|
836
|
-
function createJsonStateStore({ name, filePath, fsPromises = fs.promises, parse, adopt, nowMs = Date.now, warn = () => {} }) {
|
|
837
|
-
const writer = filePath ? createJsonStateWriter({
|
|
838
|
-
filePath,
|
|
839
|
-
fsPromises,
|
|
840
|
-
warn: (error) => warn(`${name} write failed`, { error: errorText(error) })
|
|
841
|
-
}) : null;
|
|
842
|
-
let isFileReadable = true;
|
|
843
|
-
let loadPromise = null;
|
|
844
|
-
function load() {
|
|
845
|
-
const statePath = filePath;
|
|
846
|
-
if (!statePath) return Promise.resolve();
|
|
847
|
-
if (loadPromise) return loadPromise;
|
|
848
|
-
loadPromise = (async () => {
|
|
849
|
-
const outcome = await loadJsonStateFile({
|
|
850
|
-
filePath: statePath,
|
|
851
|
-
fsPromises,
|
|
852
|
-
parse,
|
|
853
|
-
nowMs
|
|
854
|
-
});
|
|
855
|
-
if (outcome.status === "unreadable") {
|
|
856
|
-
isFileReadable = false;
|
|
857
|
-
loadPromise = null;
|
|
858
|
-
warn(`${name} unreadable`, {
|
|
859
|
-
path: statePath,
|
|
860
|
-
error: errorText(outcome.error)
|
|
861
|
-
});
|
|
862
|
-
return;
|
|
863
|
-
}
|
|
864
|
-
if (outcome.status === "quarantined") warn(`${name} quarantined`, {
|
|
865
|
-
path: statePath,
|
|
866
|
-
movedTo: outcome.movedTo
|
|
867
|
-
});
|
|
868
|
-
isFileReadable = true;
|
|
869
|
-
adopt(outcome.status === "loaded" ? outcome.value : null);
|
|
870
|
-
writer?.reset();
|
|
871
|
-
})();
|
|
872
|
-
return loadPromise;
|
|
873
|
-
}
|
|
874
|
-
async function write(subject, buildPayload) {
|
|
875
|
-
if (!writer || !isFileReadable) return;
|
|
876
|
-
await writer.write(subject, buildPayload);
|
|
1066
|
+
function findEscapingArchiveMember(listingText) {
|
|
1067
|
+
const members = String(listingText || "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
1068
|
+
for (const member of members) {
|
|
1069
|
+
if (member.split(/[\\/]+/).includes("..")) return member;
|
|
1070
|
+
if (member.startsWith("/") || member.startsWith("\\") || /^[A-Za-z]:/.test(member)) return member;
|
|
877
1071
|
}
|
|
878
|
-
return
|
|
879
|
-
load,
|
|
880
|
-
write,
|
|
881
|
-
idle: () => writer ? writer.idle() : Promise.resolve()
|
|
882
|
-
};
|
|
1072
|
+
return null;
|
|
883
1073
|
}
|
|
884
1074
|
//#endregion
|
|
885
1075
|
//#region server/config-store.ts
|
|
@@ -899,12 +1089,30 @@ var DEFAULT_CONFIG = {
|
|
|
899
1089
|
recordSignals: true,
|
|
900
1090
|
trace: { enabled: true },
|
|
901
1091
|
planReview: { enabled: true },
|
|
1092
|
+
taskTitle: { refiner: {
|
|
1093
|
+
enabled: true,
|
|
1094
|
+
model: "haiku",
|
|
1095
|
+
minIntervalSeconds: 60,
|
|
1096
|
+
timeoutSeconds: 60
|
|
1097
|
+
} },
|
|
902
1098
|
changeMap: { narrator: {
|
|
903
1099
|
enabled: false,
|
|
904
1100
|
engine: "claude",
|
|
905
1101
|
model: "",
|
|
906
1102
|
timeoutSeconds: 90
|
|
907
1103
|
} },
|
|
1104
|
+
factory: {
|
|
1105
|
+
enabled: false,
|
|
1106
|
+
maxRisk: "medium",
|
|
1107
|
+
maxLiveWorkers: 2,
|
|
1108
|
+
checks: DEFAULT_FACTORY_CHECKS,
|
|
1109
|
+
protectedPaths: DEFAULT_FACTORY_PROTECTED_PATHS,
|
|
1110
|
+
reviewerModel: null,
|
|
1111
|
+
watchWindowMinutes: 30,
|
|
1112
|
+
watchProjects: [],
|
|
1113
|
+
dailyBudgetUsd: null,
|
|
1114
|
+
verifierModel: null
|
|
1115
|
+
},
|
|
908
1116
|
agentApi: { enabled: false },
|
|
909
1117
|
telemetry: { enabled: true },
|
|
910
1118
|
checkForUpdates: true,
|
|
@@ -913,6 +1121,7 @@ var DEFAULT_CONFIG = {
|
|
|
913
1121
|
skipPermissionsByDefault: false,
|
|
914
1122
|
antiSlopPrompt: false,
|
|
915
1123
|
rtk: false,
|
|
1124
|
+
saneYolo: true,
|
|
916
1125
|
telegramNotifications: false,
|
|
917
1126
|
integrationBranch: null,
|
|
918
1127
|
worktreeRoot: "",
|
|
@@ -962,14 +1171,6 @@ var DEFAULT_CONFIG = {
|
|
|
962
1171
|
};
|
|
963
1172
|
var DEFAULT_CONFIG_BY_KEY = DEFAULT_CONFIG;
|
|
964
1173
|
var ABORT_CONFIG_SAVE = "abort-config-save";
|
|
965
|
-
function errorCode(error) {
|
|
966
|
-
const source = error ?? {};
|
|
967
|
-
if (typeof source.code === "string") return source.code;
|
|
968
|
-
return typeof source.message === "string" ? source.message : String(error);
|
|
969
|
-
}
|
|
970
|
-
function errorMessage(error) {
|
|
971
|
-
return error instanceof Error ? error.message : String(error);
|
|
972
|
-
}
|
|
973
1174
|
function glimmervoidHomeDir() {
|
|
974
1175
|
return glimmervoidHomeDir$1(os.homedir(), process.env);
|
|
975
1176
|
}
|
|
@@ -995,11 +1196,10 @@ function resolveConfigPath() {
|
|
|
995
1196
|
mode: 448
|
|
996
1197
|
});
|
|
997
1198
|
restrictMode(homeDir, 448);
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1199
|
+
writeJsonAtomicSync(homeConfig, DEFAULT_CONFIG, {
|
|
1200
|
+
mode: 384,
|
|
1201
|
+
enforceMode: true
|
|
1001
1202
|
});
|
|
1002
|
-
restrictMode(homeConfig, 384);
|
|
1003
1203
|
console.log(`Created default config at ${homeConfig}`);
|
|
1004
1204
|
return homeConfig;
|
|
1005
1205
|
}
|
|
@@ -1007,7 +1207,7 @@ function generateProjectId() {
|
|
|
1007
1207
|
return crypto.randomUUID();
|
|
1008
1208
|
}
|
|
1009
1209
|
function validateConfig(candidate) {
|
|
1010
|
-
if (!
|
|
1210
|
+
if (!isRecord(candidate)) return {
|
|
1011
1211
|
ok: false,
|
|
1012
1212
|
errors: ["config must be a plain object"]
|
|
1013
1213
|
};
|
|
@@ -1018,6 +1218,7 @@ function validateConfig(candidate) {
|
|
|
1018
1218
|
if (issue.code === "custom") return issue.message;
|
|
1019
1219
|
const [root, index, field] = issue.path;
|
|
1020
1220
|
if (root === "port") return "port must be an integer from 0 to 65535";
|
|
1221
|
+
if (root === "replayBufferKB") return issue.message;
|
|
1021
1222
|
if (root === "repoRoots" || root === "worktreeShare") return `${root} must be an array of strings`;
|
|
1022
1223
|
if (root === "remote") return "remote must be a plain object";
|
|
1023
1224
|
if (root !== "projects" && typeof DEFAULT_CONFIG_BY_KEY[String(root)] === "number") return `${String(root)} must be a finite number greater than or equal to 0`;
|
|
@@ -1035,8 +1236,8 @@ function validateConfig(candidate) {
|
|
|
1035
1236
|
};
|
|
1036
1237
|
}
|
|
1037
1238
|
function normalizeConfigFile(candidate) {
|
|
1038
|
-
if (!
|
|
1039
|
-
const draft = candidate;
|
|
1239
|
+
if (!isRecord(candidate)) throw new Error("config must be a plain object");
|
|
1240
|
+
const draft = normalizeLegacyConfigNumbers(candidate);
|
|
1040
1241
|
for (const [key, fallback] of Object.entries(DEFAULT_CONFIG_BY_KEY)) {
|
|
1041
1242
|
if (fallback === null || typeof fallback === "object") continue;
|
|
1042
1243
|
if (!Object.hasOwn(draft, key)) continue;
|
|
@@ -1051,13 +1252,12 @@ function normalizeConfigFile(candidate) {
|
|
|
1051
1252
|
}
|
|
1052
1253
|
function writeBackupContent(backupPath, content) {
|
|
1053
1254
|
try {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1255
|
+
writeTextAtomicSync(backupPath, content, {
|
|
1256
|
+
mode: 384,
|
|
1257
|
+
enforceMode: true
|
|
1057
1258
|
});
|
|
1058
|
-
restrictMode(backupPath, 384);
|
|
1059
1259
|
} catch (err) {
|
|
1060
|
-
console.warn(`[config] Failed to write backup ${backupPath}:`,
|
|
1260
|
+
console.warn(`[config] Failed to write backup ${backupPath}:`, errorLabel(err));
|
|
1061
1261
|
}
|
|
1062
1262
|
}
|
|
1063
1263
|
function loadConfigFile(configPath, { exitOnError = true } = {}) {
|
|
@@ -1070,13 +1270,12 @@ function loadConfigFile(configPath, { exitOnError = true } = {}) {
|
|
|
1070
1270
|
} catch (err) {
|
|
1071
1271
|
const invalidBackupPath = `${configPath}.invalid.bak`;
|
|
1072
1272
|
try {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1273
|
+
writeTextAtomicSync(invalidBackupPath, loadedContent, {
|
|
1274
|
+
mode: 384,
|
|
1275
|
+
enforceMode: true
|
|
1076
1276
|
});
|
|
1077
|
-
restrictMode(invalidBackupPath, 384);
|
|
1078
1277
|
} catch (backupErr) {
|
|
1079
|
-
console.warn(`[config] Failed to save invalid config copy ${invalidBackupPath}:`,
|
|
1278
|
+
console.warn(`[config] Failed to save invalid config copy ${invalidBackupPath}:`, errorLabel(backupErr));
|
|
1080
1279
|
}
|
|
1081
1280
|
const message = `[config] Could not load ${configPath}: ${errorMessage(err)}. The broken file was copied to ${invalidBackupPath} when possible. Restore from ${configPath}.boot.bak or ${configPath}.bak, then restart Glimmervoid.`;
|
|
1082
1281
|
if (!exitOnError) return {
|
|
@@ -1089,7 +1288,7 @@ function loadConfigFile(configPath, { exitOnError = true } = {}) {
|
|
|
1089
1288
|
}
|
|
1090
1289
|
}
|
|
1091
1290
|
function topLevelKeyCount(candidate) {
|
|
1092
|
-
if (!
|
|
1291
|
+
if (!isRecord(candidate)) return 0;
|
|
1093
1292
|
return Object.keys(candidate).length;
|
|
1094
1293
|
}
|
|
1095
1294
|
function isSuspectedExternalWipe(candidate, currentConfig) {
|
|
@@ -1115,7 +1314,7 @@ function ensureProjectIds(projects) {
|
|
|
1115
1314
|
return changed;
|
|
1116
1315
|
}
|
|
1117
1316
|
function collectBranchGcIssues(branchGc) {
|
|
1118
|
-
if (!
|
|
1317
|
+
if (!isRecord(branchGc)) return {
|
|
1119
1318
|
block: { ...DEFAULT_CONFIG.branchGc },
|
|
1120
1319
|
issues: []
|
|
1121
1320
|
};
|
|
@@ -1176,7 +1375,7 @@ var TELEGRAM_SETTINGS_KEYS = Object.freeze(["chatId"]);
|
|
|
1176
1375
|
var TELEGRAM_SECRET_KEYS = Object.freeze(["botToken"]);
|
|
1177
1376
|
var SECRET_PRESENCE_SUFFIX = "Configured";
|
|
1178
1377
|
function pickRedactedBlock(stored, allowedKeys, secretKeys) {
|
|
1179
|
-
if (!
|
|
1378
|
+
if (!isRecord(stored)) return null;
|
|
1180
1379
|
const source = stored;
|
|
1181
1380
|
const redacted = {};
|
|
1182
1381
|
for (const key of allowedKeys) if (source[key] !== void 0) redacted[key] = source[key];
|
|
@@ -1207,20 +1406,10 @@ function createConfigStore({ settingsDefaults } = {}) {
|
|
|
1207
1406
|
}
|
|
1208
1407
|
let _lastWrittenContent = null;
|
|
1209
1408
|
let _lastAppliedContent = loadedConfig.loadedContent;
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
} catch (err) {
|
|
1215
|
-
console.warn("[config] Failed to read config.json for save:", errorCode(err));
|
|
1216
|
-
return null;
|
|
1217
|
-
}
|
|
1218
|
-
if ("error" in loaded) {
|
|
1219
|
-
console.warn(loaded.message);
|
|
1220
|
-
return null;
|
|
1221
|
-
}
|
|
1222
|
-
const freshConfig = loaded.config;
|
|
1223
|
-
const freshContent = loaded.loadedContent;
|
|
1409
|
+
let synchronousWriteRevision = 0;
|
|
1410
|
+
let lastSynchronousContent = null;
|
|
1411
|
+
let pendingSaves = Promise.resolve();
|
|
1412
|
+
function prepareSave(freshConfig, mutatorFn) {
|
|
1224
1413
|
const freshValidation = validateConfig(freshConfig);
|
|
1225
1414
|
if (!freshValidation.ok) {
|
|
1226
1415
|
warnInvalidConfig("save config.json", freshValidation);
|
|
@@ -1231,23 +1420,86 @@ function createConfigStore({ settingsDefaults } = {}) {
|
|
|
1231
1420
|
return null;
|
|
1232
1421
|
}
|
|
1233
1422
|
if (mutatorFn(freshConfig) === "abort-config-save") return null;
|
|
1234
|
-
const
|
|
1235
|
-
const mutatedValidation = validateConfig(effectiveConfig);
|
|
1423
|
+
const mutatedValidation = validateConfig(withEnvSecrets(freshConfig, envSecrets));
|
|
1236
1424
|
if (!mutatedValidation.ok) {
|
|
1237
1425
|
warnInvalidConfig("save config.json", mutatedValidation);
|
|
1238
1426
|
return null;
|
|
1239
1427
|
}
|
|
1428
|
+
return mutatedValidation.config;
|
|
1429
|
+
}
|
|
1430
|
+
function save(mutatorFn) {
|
|
1431
|
+
let loaded;
|
|
1432
|
+
try {
|
|
1433
|
+
loaded = loadConfigFile(configPath, { exitOnError: false });
|
|
1434
|
+
} catch (err) {
|
|
1435
|
+
console.warn("[config] Failed to read config.json for save:", errorLabel(err));
|
|
1436
|
+
return null;
|
|
1437
|
+
}
|
|
1438
|
+
if ("error" in loaded) {
|
|
1439
|
+
console.warn(loaded.message);
|
|
1440
|
+
return null;
|
|
1441
|
+
}
|
|
1442
|
+
const freshConfig = loaded.config;
|
|
1443
|
+
const freshContent = loaded.loadedContent;
|
|
1444
|
+
const effectiveConfig = prepareSave(freshConfig, mutatorFn);
|
|
1445
|
+
if (!effectiveConfig) return null;
|
|
1240
1446
|
try {
|
|
1241
1447
|
const nextContent = JSON.stringify(withoutEnvSecrets(effectiveConfig, envSecrets), null, 2);
|
|
1242
1448
|
if (freshContent !== nextContent) writeBackupContent(`${configPath}.bak`, freshContent);
|
|
1243
1449
|
_lastWrittenContent = nextContent;
|
|
1244
1450
|
_lastAppliedContent = null;
|
|
1245
1451
|
writeTextAtomicSync(configPath, nextContent, { mode: 384 });
|
|
1452
|
+
lastSynchronousContent = nextContent;
|
|
1453
|
+
synchronousWriteRevision++;
|
|
1246
1454
|
} catch (err) {
|
|
1247
|
-
console.warn("[config] Failed to write config.json:",
|
|
1455
|
+
console.warn("[config] Failed to write config.json:", errorLabel(err));
|
|
1248
1456
|
return null;
|
|
1249
1457
|
}
|
|
1250
|
-
return
|
|
1458
|
+
return effectiveConfig;
|
|
1459
|
+
}
|
|
1460
|
+
function saveAsync(mutatorFn) {
|
|
1461
|
+
const scheduledSave = pendingSaves.then(async () => {
|
|
1462
|
+
let concurrentContent = null;
|
|
1463
|
+
for (;;) {
|
|
1464
|
+
const revision = synchronousWriteRevision;
|
|
1465
|
+
try {
|
|
1466
|
+
const diskContent = await fs.promises.readFile(configPath, "utf8");
|
|
1467
|
+
if (revision !== synchronousWriteRevision) {
|
|
1468
|
+
concurrentContent = lastSynchronousContent;
|
|
1469
|
+
continue;
|
|
1470
|
+
}
|
|
1471
|
+
const freshContent = concurrentContent ?? diskContent;
|
|
1472
|
+
const effectiveConfig = prepareSave(withEnvSecrets(normalizeConfigFile(JSON.parse(freshContent)), envSecrets), mutatorFn);
|
|
1473
|
+
if (!effectiveConfig) return null;
|
|
1474
|
+
const nextContent = JSON.stringify(withoutEnvSecrets(effectiveConfig, envSecrets), null, 2);
|
|
1475
|
+
if (freshContent !== nextContent) try {
|
|
1476
|
+
await writeTextAtomic(`${configPath}.bak`, freshContent, {
|
|
1477
|
+
mode: 384,
|
|
1478
|
+
enforceMode: process.platform !== "win32"
|
|
1479
|
+
});
|
|
1480
|
+
} catch (error) {
|
|
1481
|
+
console.warn(`[config] Failed to write backup ${configPath}.bak:`, errorLabel(error));
|
|
1482
|
+
}
|
|
1483
|
+
if (revision !== synchronousWriteRevision) {
|
|
1484
|
+
concurrentContent = lastSynchronousContent;
|
|
1485
|
+
continue;
|
|
1486
|
+
}
|
|
1487
|
+
await writeTextAtomic(configPath, nextContent, { mode: 384 });
|
|
1488
|
+
if (revision !== synchronousWriteRevision) {
|
|
1489
|
+
concurrentContent = lastSynchronousContent;
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
_lastWrittenContent = nextContent;
|
|
1493
|
+
_lastAppliedContent = null;
|
|
1494
|
+
return effectiveConfig;
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
console.warn("[config] Failed to save config.json asynchronously:", errorMessage(error));
|
|
1497
|
+
return null;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
pendingSaves = scheduledSave;
|
|
1502
|
+
return scheduledSave;
|
|
1251
1503
|
}
|
|
1252
1504
|
function getSettings() {
|
|
1253
1505
|
return {
|
|
@@ -1269,6 +1521,8 @@ function createConfigStore({ settingsDefaults } = {}) {
|
|
|
1269
1521
|
telemetryForcedOff: isTelemetryForcedOff(process.env),
|
|
1270
1522
|
antiSlopPrompt: config.antiSlopPrompt ?? effectiveDefaults.antiSlopPrompt,
|
|
1271
1523
|
rtk: config.rtk ?? effectiveDefaults.rtk,
|
|
1524
|
+
rtkInstallSupported: assetForPlatform(process.platform, process.arch) !== null,
|
|
1525
|
+
saneYolo: config.saneYolo ?? effectiveDefaults.saneYolo,
|
|
1272
1526
|
checkForUpdates: config.checkForUpdates ?? effectiveDefaults.checkForUpdates,
|
|
1273
1527
|
updateChannel: config.updateChannel ?? effectiveDefaults.updateChannel,
|
|
1274
1528
|
autoResume: config.autoResume ?? effectiveDefaults.autoResume,
|
|
@@ -1278,14 +1532,20 @@ function createConfigStore({ settingsDefaults } = {}) {
|
|
|
1278
1532
|
worktreeRoot: config.worktreeRoot ?? effectiveDefaults.worktreeRoot,
|
|
1279
1533
|
worktreeShare: config.worktreeShare ?? effectiveDefaults.worktreeShare,
|
|
1280
1534
|
repoRoots: config.repoRoots,
|
|
1535
|
+
repositoryRootCaseInsensitive: equalsIgnoringCaseOnWindows("root", "ROOT"),
|
|
1536
|
+
taskTitle: config.taskTitle ? { ...config.taskTitle } : { ...DEFAULT_CONFIG.taskTitle },
|
|
1281
1537
|
changeMap: config.changeMap ? { ...config.changeMap } : null,
|
|
1282
1538
|
branchGc: { ...config.branchGc },
|
|
1283
1539
|
postTurnChecks: config.postTurnChecks ? { ...config.postTurnChecks } : { ...DEFAULT_CONFIG.postTurnChecks },
|
|
1284
1540
|
visions: config.visions ? { ...config.visions } : null,
|
|
1541
|
+
github: config.github ? { ...config.github } : null,
|
|
1285
1542
|
teamReview: config.teamReview ? { ...config.teamReview } : null,
|
|
1286
1543
|
benchmarks: config.benchmarks ? { ...config.benchmarks } : null,
|
|
1544
|
+
factory: config.factory ? { ...config.factory } : null,
|
|
1545
|
+
knowledgeGraph: config.knowledgeGraph ? { ...config.knowledgeGraph } : null,
|
|
1287
1546
|
workflows: config.workflows ?? null,
|
|
1288
1547
|
posthog: pickRedactedBlock(config.posthog, POSTHOG_SETTINGS_KEYS, POSTHOG_SECRET_KEYS),
|
|
1548
|
+
secretSources: Object.fromEntries(readEnvSecrets(process.env).map((secret) => [`${secret.blockName}.${secret.secretKey}`, secret.environmentVariable])),
|
|
1289
1549
|
usage: config.usage ? { ...config.usage } : null,
|
|
1290
1550
|
telegram: pickRedactedBlock(config.telegram, TELEGRAM_SETTINGS_KEYS, TELEGRAM_SECRET_KEYS),
|
|
1291
1551
|
ingest: pickSettingsBlock(config.ingest, INGEST_SPEC),
|
|
@@ -1318,11 +1578,15 @@ function createConfigStore({ settingsDefaults } = {}) {
|
|
|
1318
1578
|
config.repoRoots = newConfig.repoRoots || [];
|
|
1319
1579
|
if (newConfig.postTurnChecks != null) config.postTurnChecks = newConfig.postTurnChecks;
|
|
1320
1580
|
if (newConfig.worktreeShare != null) config.worktreeShare = newConfig.worktreeShare;
|
|
1581
|
+
if (newConfig.taskTitle != null) config.taskTitle = newConfig.taskTitle;
|
|
1321
1582
|
if (newConfig.changeMap != null) config.changeMap = newConfig.changeMap;
|
|
1322
1583
|
if (newConfig.branchGc != null) config.branchGc = resolveBranchGc(newConfig.branchGc);
|
|
1323
1584
|
if (newConfig.visions != null) config.visions = newConfig.visions;
|
|
1585
|
+
if (newConfig.github != null) config.github = newConfig.github;
|
|
1324
1586
|
if (newConfig.teamReview != null) config.teamReview = newConfig.teamReview;
|
|
1325
1587
|
if (newConfig.benchmarks != null) config.benchmarks = newConfig.benchmarks;
|
|
1588
|
+
if (newConfig.factory != null) config.factory = newConfig.factory;
|
|
1589
|
+
if (newConfig.knowledgeGraph != null) config.knowledgeGraph = newConfig.knowledgeGraph;
|
|
1326
1590
|
if (newConfig.posthog != null) config.posthog = newConfig.posthog;
|
|
1327
1591
|
if (newConfig.usage != null) config.usage = newConfig.usage;
|
|
1328
1592
|
if (newConfig.telegram != null) config.telegram = newConfig.telegram;
|
|
@@ -1331,6 +1595,8 @@ function createConfigStore({ settingsDefaults } = {}) {
|
|
|
1331
1595
|
if (newConfig.telemetry != null) config.telemetry = newConfig.telemetry;
|
|
1332
1596
|
if (newConfig.workflows == null) delete config.workflows;
|
|
1333
1597
|
if (newConfig.workflows != null) config.workflows = newConfig.workflows;
|
|
1598
|
+
if (newConfig.coder == null) delete config.coder;
|
|
1599
|
+
if (newConfig.coder != null) config.coder = newConfig.coder;
|
|
1334
1600
|
config.customAgents = newConfig.customAgents ?? [];
|
|
1335
1601
|
config.hooks = Array.isArray(newConfig.hooks) ? newConfig.hooks : [];
|
|
1336
1602
|
if (newConfig.port != null && newConfig.port !== config.port) console.log(`[settings] Port changed to ${newConfig.port} - restart required to take effect`);
|
|
@@ -1389,6 +1655,8 @@ function createConfigStore({ settingsDefaults } = {}) {
|
|
|
1389
1655
|
config,
|
|
1390
1656
|
configPath,
|
|
1391
1657
|
save,
|
|
1658
|
+
saveAsync,
|
|
1659
|
+
idle: () => pendingSaves,
|
|
1392
1660
|
getSettings,
|
|
1393
1661
|
applySettings,
|
|
1394
1662
|
isUnchosenLaunchDefault,
|
|
@@ -1397,4 +1665,4 @@ function createConfigStore({ settingsDefaults } = {}) {
|
|
|
1397
1665
|
};
|
|
1398
1666
|
}
|
|
1399
1667
|
//#endregion
|
|
1400
|
-
export {
|
|
1668
|
+
export { sessionIdFromBranch as $, snapshotEvents as A, classifySessionExit as B, createIngestStore as C, resolveIngestConfig as D, publishEvent as E, adapterBucket as F, nodeMajorVersion as G, decideRemoteTelemetryState as H, buildAiGenerationEvents as I, stableConfigKey as J, parsePosthogReleaseId as K, buildBrowserExceptionProperties as L, FIRST_RUN_NOTICE as M, TELEMETRY_BATCH_URL as N, ringStats as O, TELEMETRY_FLAGS_URL as P, planWorktreeGc as Q, buildExceptionProperties as R, buildContextDigest as S, latestSeq as T, decideTelemetryConsent as U, decideIsDevInstall as V, exceptionFingerprint as W, matchedPrefix as X, DEFAULT_BRANCH_GC_PREFIXES as Y, planBranchGc as Z, INGEST_SPEC as _, ensureProjectIds as a, DEFAULT_DIGEST_BUDGET_CHARS as b, loadConfigFile as c, assetForPlatform as d, usablePrefixes as et, decideRtkInstall as f, verifyDigest as g, isRtkBinaryName as h, createConfigStore as i, localDayKey as j, scrubText as k, resolveConfigPath as l, installTargetPath as m, DEFAULT_CONFIG as n, generateProjectId as o, findEscapingArchiveMember as p, resolveProjectToken as q, SECRET_PRESENCE_SUFFIX as r, glimmervoidHomeDir as s, ABORT_CONFIG_SAVE as t, worktreeIntegrationTips as tt, validateConfig as u, mergeSettingsBlock as v, enabledSourceNames as w, SOURCE_DEFAULTS as x, validateSettingsBlock as y, buildFactoryDecisionEvent as z };
|