@hexclave/shared-backend 1.0.36 → 1.0.37

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.
@@ -0,0 +1,327 @@
1
+ import { CONFIG_AGENT_FILE_TOOLS, ClaudeAgentFailureError, ClaudeAgentTimeoutError, buildCompleteConfigAgentPrompt, buildPartialConfigAgentPrompt, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from "./config-agent.js";
2
+ import path from "path";
3
+ import { evalConfigFileContent } from "@hexclave/shared/dist/config-eval";
4
+ import { normalize, override } from "@hexclave/shared/dist/config/format";
5
+ import { existsSync, readFileSync, rmSync, writeFileSync } from "fs";
6
+ import { ensureConfigFileExists, readConfigFile } from "./config-file.js";
7
+
8
+ //#region src/config-updater.ts
9
+ const LOG_PREFIX = "[Hexclave config updater]";
10
+ const DEFAULT_AGENT_TIMEOUT_MS = 12e4;
11
+ const CONFIG_UPDATE_LOG_PATH_LIMIT = 40;
12
+ const AGENT_OUTPUT_LOG_MAX_LENGTH = 2e4;
13
+ function formatConfigUpdaterErrorForLog(error) {
14
+ if (error instanceof Error) return {
15
+ errorName: error.name,
16
+ errorMessage: error.message,
17
+ errorStack: error.stack
18
+ };
19
+ return { errorMessage: String(error) };
20
+ }
21
+ function configUpdatePathDetailsForLog(changes) {
22
+ const paths = changes.map(({ path: configPath }) => configPath).sort();
23
+ return {
24
+ configUpdatePathCount: paths.length,
25
+ configUpdatePaths: paths.slice(0, CONFIG_UPDATE_LOG_PATH_LIMIT),
26
+ configUpdatePathsTruncated: paths.length > CONFIG_UPDATE_LOG_PATH_LIMIT
27
+ };
28
+ }
29
+ function appendBoundedAgentOutput(current, chunk) {
30
+ const next = `${current}${chunk}`;
31
+ if (next.length <= AGENT_OUTPUT_LOG_MAX_LENGTH) return next;
32
+ return next.slice(next.length - AGENT_OUTPUT_LOG_MAX_LENGTH);
33
+ }
34
+ function stringifyAgentMessageForLog(message) {
35
+ try {
36
+ return `${JSON.stringify(message)}\n`;
37
+ } catch {
38
+ return `${String(message)}\n`;
39
+ }
40
+ }
41
+ function agentOutputDetailsForLog(agentStdout, agentStderr) {
42
+ return {
43
+ agentStdout,
44
+ agentStdoutTruncated: agentStdout.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,
45
+ agentStderr,
46
+ agentStderrTruncated: agentStderr.length >= AGENT_OUTPUT_LOG_MAX_LENGTH
47
+ };
48
+ }
49
+ async function updateConfigObject(configFilePath, configUpdate) {
50
+ const startedAtMs = performance.now();
51
+ ensureConfigFileExists(configFilePath);
52
+ const changes = flattenConfigUpdate(configUpdate);
53
+ if (changes.length === 0) {
54
+ console.log(`${LOG_PREFIX} Skipping config update because it contains no changes`, { configFilePath });
55
+ return;
56
+ }
57
+ const updateLogDetails = {
58
+ configFilePath,
59
+ ...configUpdatePathDetailsForLog(changes)
60
+ };
61
+ console.log(`${LOG_PREFIX} Starting config file update`, updateLogDetails);
62
+ const content = readFileSync(configFilePath, "utf-8");
63
+ console.log(`${LOG_PREFIX} Applying config update with agent-assisted rewrite`, {
64
+ ...updateLogDetails,
65
+ configDirectory: path.dirname(configFilePath)
66
+ });
67
+ const baselineConfig = await tryReadConfigForValidation(configFilePath);
68
+ const { snapshots, seen } = snapshotConfigFiles(configFilePath, content);
69
+ try {
70
+ await runConfigUpdateAgent({
71
+ prompt: buildConfigUpdatePrompt(path.basename(configFilePath), configUpdate, baselineConfig),
72
+ cwd: path.dirname(configFilePath),
73
+ onFileWillChange: (filePath) => captureSnapshotIfAbsent(snapshots, filePath, seen)
74
+ });
75
+ await validateAgentUpdate(configFilePath, baselineConfig, configUpdate);
76
+ } catch (error) {
77
+ console.warn(`${LOG_PREFIX} Config update failed; restoring files from snapshots`, {
78
+ ...updateLogDetails,
79
+ snapshotCount: snapshots.length,
80
+ elapsedMs: Math.round(performance.now() - startedAtMs),
81
+ ...formatConfigUpdaterErrorForLog(error)
82
+ });
83
+ try {
84
+ restoreConfigFiles(snapshots);
85
+ console.warn(`${LOG_PREFIX} Restored files after failed config update`, {
86
+ ...updateLogDetails,
87
+ snapshotCount: snapshots.length
88
+ });
89
+ } catch (restoreError) {
90
+ console.error(`${LOG_PREFIX} Failed to fully roll back config files after a failed update of ${configFilePath}; some files may be left in a partially-restored state`, {
91
+ configFilePath,
92
+ ...formatConfigUpdaterErrorForLog(restoreError)
93
+ });
94
+ }
95
+ throw error;
96
+ }
97
+ console.log(`${LOG_PREFIX} Finished config update with agent-assisted rewrite`, {
98
+ ...updateLogDetails,
99
+ elapsedMs: Math.round(performance.now() - startedAtMs),
100
+ snapshotCount: snapshots.length
101
+ });
102
+ }
103
+ async function runConfigUpdateAgent(options) {
104
+ const timeoutMs = parseAgentTimeoutMs();
105
+ const deniedOutOfBoundsWrites = /* @__PURE__ */ new Set();
106
+ const startedAtMs = performance.now();
107
+ let agentStdout = "";
108
+ let agentStderr = "";
109
+ console.log(`${LOG_PREFIX} Starting config update agent`, {
110
+ cwd: options.cwd,
111
+ timeoutMs
112
+ });
113
+ try {
114
+ await runHeadlessClaudeAgent({
115
+ prompt: options.prompt,
116
+ cwd: options.cwd,
117
+ allowedTools: [...CONFIG_AGENT_FILE_TOOLS],
118
+ strictIsolation: true,
119
+ timeoutMs,
120
+ stderr: (data) => {
121
+ agentStderr = appendBoundedAgentOutput(agentStderr, data);
122
+ console.warn(`${LOG_PREFIX} [agent] ${data}`);
123
+ },
124
+ onMessage: (message) => {
125
+ agentStdout = appendBoundedAgentOutput(agentStdout, stringifyAgentMessageForLog(message));
126
+ },
127
+ onPreToolUse: (input) => {
128
+ const target = getToolWriteTargetPath(input.tool_name, input.tool_input, options.cwd);
129
+ if (target == null) return { continue: true };
130
+ if (!isPathInsideDir(options.cwd, target)) {
131
+ deniedOutOfBoundsWrites.add(target);
132
+ return { hookSpecificOutput: {
133
+ hookEventName: "PreToolUse",
134
+ permissionDecision: "deny",
135
+ permissionDecisionReason: `Refusing to modify ${target}: config updates may only change files inside the config directory.`
136
+ } };
137
+ }
138
+ options.onFileWillChange?.(target);
139
+ return { continue: true };
140
+ }
141
+ });
142
+ } catch (error) {
143
+ if (error instanceof ClaudeAgentTimeoutError) {
144
+ console.warn(`${LOG_PREFIX} Config update agent timed out`, {
145
+ cwd: options.cwd,
146
+ timeoutMs,
147
+ elapsedMs: Math.round(performance.now() - startedAtMs),
148
+ ...formatConfigUpdaterErrorForLog(error),
149
+ ...agentOutputDetailsForLog(agentStdout, agentStderr)
150
+ });
151
+ throw new Error(`Config update agent timed out after ${timeoutMs}ms. It was unable to apply the config changes to the file.`);
152
+ }
153
+ if (error instanceof ClaudeAgentFailureError) {
154
+ console.warn(`${LOG_PREFIX} Config update agent failed`, {
155
+ cwd: options.cwd,
156
+ timeoutMs,
157
+ elapsedMs: Math.round(performance.now() - startedAtMs),
158
+ ...formatConfigUpdaterErrorForLog(error),
159
+ ...agentOutputDetailsForLog(agentStdout, agentStderr)
160
+ });
161
+ throw new Error(`${error.message} It was unable to apply the config changes to the file.`);
162
+ }
163
+ console.warn(`${LOG_PREFIX} Config update agent failed unexpectedly`, {
164
+ cwd: options.cwd,
165
+ timeoutMs,
166
+ elapsedMs: Math.round(performance.now() - startedAtMs),
167
+ ...formatConfigUpdaterErrorForLog(error),
168
+ ...agentOutputDetailsForLog(agentStdout, agentStderr)
169
+ });
170
+ throw error;
171
+ }
172
+ console.log(`${LOG_PREFIX} Finished config update agent`, {
173
+ cwd: options.cwd,
174
+ timeoutMs,
175
+ elapsedMs: Math.round(performance.now() - startedAtMs),
176
+ deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size
177
+ });
178
+ if (deniedOutOfBoundsWrites.size > 0) {
179
+ console.warn(`${LOG_PREFIX} Config update agent attempted out-of-bounds writes`, {
180
+ cwd: options.cwd,
181
+ deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,
182
+ deniedOutOfBoundsWrites: [...deniedOutOfBoundsWrites]
183
+ });
184
+ throw new Error(`Config update agent tried to modify ${deniedOutOfBoundsWrites.size} file(s) outside the config directory, which is not allowed: ${[...deniedOutOfBoundsWrites].join(", ")}. The config was not updated.`);
185
+ }
186
+ }
187
+ function parseAgentTimeoutMs() {
188
+ const raw = process.env.STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS;
189
+ if (raw == null || raw.trim() === "") return DEFAULT_AGENT_TIMEOUT_MS;
190
+ const parsed = Number(raw);
191
+ if (!Number.isFinite(parsed) || parsed <= 0) throw new Error(`Invalid STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS: ${JSON.stringify(raw)}. Expected a positive number of milliseconds.`);
192
+ return parsed;
193
+ }
194
+ function captureSnapshotIfAbsent(snapshots, filePath, seen) {
195
+ const resolved = path.resolve(filePath);
196
+ if (seen.has(resolved)) return;
197
+ seen.add(resolved);
198
+ snapshots.push({
199
+ path: resolved,
200
+ content: existsSync(resolved) ? readFileSync(resolved, "utf-8") : null
201
+ });
202
+ }
203
+ function snapshotConfigFiles(configFilePath, configContent) {
204
+ const dir = path.dirname(configFilePath);
205
+ const resolvedConfig = path.resolve(configFilePath);
206
+ const snapshots = [{
207
+ path: resolvedConfig,
208
+ content: configContent
209
+ }];
210
+ const seen = new Set([resolvedConfig]);
211
+ for (const specifier of getRelativeImportSpecifiers(configContent)) {
212
+ const resolved = path.resolve(dir, specifier);
213
+ if (!isPathInsideDir(dir, resolved)) continue;
214
+ captureSnapshotIfAbsent(snapshots, resolved, seen);
215
+ }
216
+ return {
217
+ snapshots,
218
+ seen
219
+ };
220
+ }
221
+ function restoreConfigFiles(snapshots) {
222
+ const failures = [];
223
+ for (const { path: filePath, content } of snapshots) try {
224
+ if (content === null) {
225
+ if (existsSync(filePath)) rmSync(filePath);
226
+ } else writeFileSync(filePath, content, "utf-8");
227
+ } catch (error) {
228
+ failures.push(`${filePath}: ${error instanceof Error ? error.message : String(error)}`);
229
+ }
230
+ if (failures.length > 0) throw new Error(`Failed to restore ${failures.length} file(s) during rollback: ${failures.join("; ")}`);
231
+ }
232
+ async function tryReadConfigForValidation(configFilePath) {
233
+ try {
234
+ return (await readConfigFile(configFilePath)).config;
235
+ } catch (error) {
236
+ console.warn(`${LOG_PREFIX} Could not evaluate config for validation baseline; will fall back to a structural check`, {
237
+ configFilePath,
238
+ error: error instanceof Error ? error.message : String(error)
239
+ });
240
+ return null;
241
+ }
242
+ }
243
+ async function validateAgentUpdate(configFilePath, baselineConfig, configUpdate) {
244
+ if (baselineConfig != null) {
245
+ const target = canonicalizeConfig(override(baselineConfig, configUpdate));
246
+ if (!configsEqual(canonicalizeConfig((await readConfigFile(configFilePath)).config), target)) throw new Error(`Config update validation failed for ${configFilePath}: the updated file does not evaluate to the expected configuration.`);
247
+ return;
248
+ }
249
+ if (!configFileExportsConfig(readFileSync(configFilePath, "utf-8"), configFilePath)) throw new Error(`Config update validation failed for ${configFilePath}: the updated file no longer exports a valid \`config\`.`);
250
+ }
251
+ function configFileExportsConfig(content, configFilePath) {
252
+ try {
253
+ evalConfigFileContent(content, configFilePath);
254
+ return true;
255
+ } catch {
256
+ return /\bexport\s+const\s+config\b/.test(content);
257
+ }
258
+ }
259
+ function getRelativeImportSpecifiers(content) {
260
+ const specifiers = [];
261
+ const importPattern = /\bimport\b(?:[^'"]*?\bfrom\s*)?["'](\.{1,2}\/[^"']+)["']/g;
262
+ let match;
263
+ while ((match = importPattern.exec(content)) !== null) specifiers.push(match[1]);
264
+ return specifiers;
265
+ }
266
+ function flattenConfigUpdate(update) {
267
+ const changes = [];
268
+ const walk = (prefix, obj) => {
269
+ for (const [key, value] of Object.entries(obj)) {
270
+ const fullPath = prefix === "" ? key : `${prefix}.${key}`;
271
+ if (value === void 0) continue;
272
+ if (value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length > 0) walk(fullPath, value);
273
+ else changes.push({
274
+ path: fullPath,
275
+ value
276
+ });
277
+ }
278
+ };
279
+ walk("", update);
280
+ return changes;
281
+ }
282
+ function buildConfigUpdatePrompt(configFileName, configUpdate, baselineConfig) {
283
+ const changes = flattenConfigUpdate(configUpdate);
284
+ const commandPolicy = "Do not run shell commands and do not create files other than what is required to apply the config changes.";
285
+ if (baselineConfig != null) return buildCompleteConfigAgentPrompt({
286
+ scope: {
287
+ mode: "known-file",
288
+ configFileName
289
+ },
290
+ completeConfig: canonicalizeConfig(override(baselineConfig, configUpdate)),
291
+ commandPolicy
292
+ });
293
+ return buildPartialConfigAgentPrompt({
294
+ configFileName,
295
+ changes,
296
+ commandPolicy
297
+ });
298
+ }
299
+ function canonicalizeConfig(config) {
300
+ const droppedKeys = [];
301
+ const normalized = normalize(config, {
302
+ onDotIntoNonObject: "ignore",
303
+ onDotIntoNull: "empty-object",
304
+ droppedKeys
305
+ });
306
+ if (droppedKeys.length > 0) throw new Error(`Config update has conflicting keys that would be dropped during normalization: ${droppedKeys.map((key) => JSON.stringify(key)).join(", ")}`);
307
+ return normalized;
308
+ }
309
+ function configsEqual(a, b) {
310
+ if (a === b) return true;
311
+ if (a === null || b === null) return a === b;
312
+ if (Array.isArray(a) || Array.isArray(b)) {
313
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
314
+ return a.every((value, index) => configsEqual(value, b[index]));
315
+ }
316
+ if (typeof a === "object" && typeof b === "object") {
317
+ const aEntries = Object.entries(a);
318
+ const bMap = new Map(Object.entries(b));
319
+ if (aEntries.length !== bMap.size) return false;
320
+ return aEntries.every(([key, value]) => bMap.has(key) && configsEqual(value, bMap.get(key)));
321
+ }
322
+ return false;
323
+ }
324
+
325
+ //#endregion
326
+ export { updateConfigObject };
327
+ //# sourceMappingURL=config-updater.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-updater.js","names":[],"sources":["../../src/config-updater.ts"],"sourcesContent":["import { evalConfigFileContent } from \"@hexclave/shared/dist/config-eval\";\nimport type { Config, ConfigValue, NormalizedConfig } from \"@hexclave/shared/dist/config/format\";\nimport { normalize, override } from \"@hexclave/shared/dist/config/format\";\nimport { existsSync, readFileSync, rmSync, writeFileSync } from \"fs\";\nimport path from \"path\";\nimport { buildCompleteConfigAgentPrompt, buildPartialConfigAgentPrompt, ClaudeAgentFailureError, ClaudeAgentTimeoutError, CONFIG_AGENT_FILE_TOOLS, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from \"./config-agent\";\nimport { ensureConfigFileExists, readConfigFile } from \"./config-file\";\n\nconst LOG_PREFIX = \"[Hexclave config updater]\";\nconst DEFAULT_AGENT_TIMEOUT_MS = 120_000;\nconst CONFIG_UPDATE_LOG_PATH_LIMIT = 40;\nconst AGENT_OUTPUT_LOG_MAX_LENGTH = 20_000;\n\ntype ConfigFileSnapshot = { path: string, content: string | null };\ntype ConfigChange = { path: string, value: ConfigValue };\n\nfunction formatConfigUpdaterErrorForLog(error: unknown): Record<string, unknown> {\n if (error instanceof Error) {\n return {\n errorName: error.name,\n errorMessage: error.message,\n errorStack: error.stack,\n };\n }\n return {\n errorMessage: String(error),\n };\n}\n\nfunction configUpdatePathDetailsForLog(changes: ConfigChange[]): Record<string, unknown> {\n const paths = changes.map(({ path: configPath }) => configPath).sort();\n return {\n configUpdatePathCount: paths.length,\n configUpdatePaths: paths.slice(0, CONFIG_UPDATE_LOG_PATH_LIMIT),\n configUpdatePathsTruncated: paths.length > CONFIG_UPDATE_LOG_PATH_LIMIT,\n };\n}\n\nfunction appendBoundedAgentOutput(current: string, chunk: string): string {\n const next = `${current}${chunk}`;\n if (next.length <= AGENT_OUTPUT_LOG_MAX_LENGTH) {\n return next;\n }\n return next.slice(next.length - AGENT_OUTPUT_LOG_MAX_LENGTH);\n}\n\nfunction stringifyAgentMessageForLog(message: unknown): string {\n try {\n return `${JSON.stringify(message)}\\n`;\n } catch {\n return `${String(message)}\\n`;\n }\n}\n\nfunction agentOutputDetailsForLog(agentStdout: string, agentStderr: string): Record<string, unknown> {\n return {\n agentStdout,\n agentStdoutTruncated: agentStdout.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,\n agentStderr,\n agentStderrTruncated: agentStderr.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,\n };\n}\n\nexport async function updateConfigObject(configFilePath: string, configUpdate: Config): Promise<void> {\n const startedAtMs = performance.now();\n ensureConfigFileExists(configFilePath);\n\n const changes = flattenConfigUpdate(configUpdate);\n if (changes.length === 0) {\n console.log(`${LOG_PREFIX} Skipping config update because it contains no changes`, {\n configFilePath,\n });\n return;\n }\n const updateLogDetails = {\n configFilePath,\n ...configUpdatePathDetailsForLog(changes),\n };\n console.log(`${LOG_PREFIX} Starting config file update`, updateLogDetails);\n\n const content = readFileSync(configFilePath, \"utf-8\");\n\n // One write path, always: hand the change to the AI agent so it edits the file\n // in place and preserves its authoring (helper wrappers, imports, comments,\n // layout). There is deliberately no deterministic \"fast path\" — re-rendering a\n // config would flatten and destroy hand-authored files. Reads use jiti\n // (see readConfigFile); writes go through the agent.\n console.log(`${LOG_PREFIX} Applying config update with agent-assisted rewrite`, {\n ...updateLogDetails,\n configDirectory: path.dirname(configFilePath),\n });\n const baselineConfig = await tryReadConfigForValidation(configFilePath);\n const { snapshots, seen } = snapshotConfigFiles(configFilePath, content);\n try {\n await runConfigUpdateAgent({\n prompt: buildConfigUpdatePrompt(path.basename(configFilePath), configUpdate, baselineConfig),\n cwd: path.dirname(configFilePath),\n onFileWillChange: (filePath) => captureSnapshotIfAbsent(snapshots, filePath, seen),\n });\n await validateAgentUpdate(configFilePath, baselineConfig, configUpdate);\n } catch (error) {\n console.warn(`${LOG_PREFIX} Config update failed; restoring files from snapshots`, {\n ...updateLogDetails,\n snapshotCount: snapshots.length,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n });\n try {\n restoreConfigFiles(snapshots);\n console.warn(`${LOG_PREFIX} Restored files after failed config update`, {\n ...updateLogDetails,\n snapshotCount: snapshots.length,\n });\n } catch (restoreError) {\n console.error(`${LOG_PREFIX} Failed to fully roll back config files after a failed update of ${configFilePath}; some files may be left in a partially-restored state`, {\n configFilePath,\n ...formatConfigUpdaterErrorForLog(restoreError),\n });\n }\n throw error;\n }\n console.log(`${LOG_PREFIX} Finished config update with agent-assisted rewrite`, {\n ...updateLogDetails,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n snapshotCount: snapshots.length,\n });\n}\n\nasync function runConfigUpdateAgent(options: {\n prompt: string,\n cwd: string,\n onFileWillChange?: (filePath: string) => void,\n}): Promise<void> {\n const timeoutMs = parseAgentTimeoutMs();\n const deniedOutOfBoundsWrites = new Set<string>();\n const startedAtMs = performance.now();\n let agentStdout = \"\";\n let agentStderr = \"\";\n console.log(`${LOG_PREFIX} Starting config update agent`, {\n cwd: options.cwd,\n timeoutMs,\n });\n try {\n await runHeadlessClaudeAgent({\n prompt: options.prompt,\n cwd: options.cwd,\n allowedTools: [...CONFIG_AGENT_FILE_TOOLS],\n strictIsolation: true,\n timeoutMs,\n stderr: (data) => {\n agentStderr = appendBoundedAgentOutput(agentStderr, data);\n console.warn(`${LOG_PREFIX} [agent] ${data}`);\n },\n onMessage: (message) => {\n agentStdout = appendBoundedAgentOutput(agentStdout, stringifyAgentMessageForLog(message));\n },\n onPreToolUse: (input) => {\n const target = getToolWriteTargetPath(input.tool_name, input.tool_input, options.cwd);\n if (target == null) return { continue: true };\n if (!isPathInsideDir(options.cwd, target)) {\n deniedOutOfBoundsWrites.add(target);\n return {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"deny\",\n permissionDecisionReason: `Refusing to modify ${target}: config updates may only change files inside the config directory.`,\n },\n };\n }\n options.onFileWillChange?.(target);\n return { continue: true };\n },\n });\n } catch (error) {\n if (error instanceof ClaudeAgentTimeoutError) {\n console.warn(`${LOG_PREFIX} Config update agent timed out`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw new Error(`Config update agent timed out after ${timeoutMs}ms. It was unable to apply the config changes to the file.`);\n }\n if (error instanceof ClaudeAgentFailureError) {\n console.warn(`${LOG_PREFIX} Config update agent failed`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw new Error(`${error.message} It was unable to apply the config changes to the file.`);\n }\n console.warn(`${LOG_PREFIX} Config update agent failed unexpectedly`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw error;\n }\n console.log(`${LOG_PREFIX} Finished config update agent`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,\n });\n if (deniedOutOfBoundsWrites.size > 0) {\n console.warn(`${LOG_PREFIX} Config update agent attempted out-of-bounds writes`, {\n cwd: options.cwd,\n deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,\n deniedOutOfBoundsWrites: [...deniedOutOfBoundsWrites],\n });\n throw new Error(`Config update agent tried to modify ${deniedOutOfBoundsWrites.size} file(s) outside the config directory, which is not allowed: ${[...deniedOutOfBoundsWrites].join(\", \")}. The config was not updated.`);\n }\n}\n\nfunction parseAgentTimeoutMs(): number {\n const raw = process.env.STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS;\n if (raw == null || raw.trim() === \"\") return DEFAULT_AGENT_TIMEOUT_MS;\n const parsed = Number(raw);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new Error(`Invalid STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS: ${JSON.stringify(raw)}. Expected a positive number of milliseconds.`);\n }\n return parsed;\n}\n\nfunction captureSnapshotIfAbsent(snapshots: ConfigFileSnapshot[], filePath: string, seen: Set<string>): void {\n const resolved = path.resolve(filePath);\n if (seen.has(resolved)) return;\n seen.add(resolved);\n snapshots.push({ path: resolved, content: existsSync(resolved) ? readFileSync(resolved, \"utf-8\") : null });\n}\n\nfunction snapshotConfigFiles(configFilePath: string, configContent: string): { snapshots: ConfigFileSnapshot[]; seen: Set<string> } {\n const dir = path.dirname(configFilePath);\n const resolvedConfig = path.resolve(configFilePath);\n const snapshots: ConfigFileSnapshot[] = [{ path: resolvedConfig, content: configContent }];\n const seen = new Set<string>([resolvedConfig]);\n for (const specifier of getRelativeImportSpecifiers(configContent)) {\n const resolved = path.resolve(dir, specifier);\n if (!isPathInsideDir(dir, resolved)) continue;\n captureSnapshotIfAbsent(snapshots, resolved, seen);\n }\n return { snapshots, seen };\n}\n\nfunction restoreConfigFiles(snapshots: ConfigFileSnapshot[]): void {\n const failures: string[] = [];\n for (const { path: filePath, content } of snapshots) {\n try {\n if (content === null) {\n if (existsSync(filePath)) rmSync(filePath);\n } else {\n writeFileSync(filePath, content, \"utf-8\");\n }\n } catch (error) {\n failures.push(`${filePath}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n if (failures.length > 0) {\n throw new Error(`Failed to restore ${failures.length} file(s) during rollback: ${failures.join(\"; \")}`);\n }\n}\n\nasync function tryReadConfigForValidation(configFilePath: string): Promise<Config | null> {\n try {\n return (await readConfigFile(configFilePath)).config;\n } catch (error) {\n console.warn(`${LOG_PREFIX} Could not evaluate config for validation baseline; will fall back to a structural check`, {\n configFilePath,\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n}\n\nasync function validateAgentUpdate(configFilePath: string, baselineConfig: Config | null, configUpdate: Config): Promise<void> {\n if (baselineConfig != null) {\n const target = canonicalizeConfig(override(baselineConfig, configUpdate));\n const result = canonicalizeConfig((await readConfigFile(configFilePath)).config);\n if (!configsEqual(result, target)) {\n throw new Error(`Config update validation failed for ${configFilePath}: the updated file does not evaluate to the expected configuration.`);\n }\n return;\n }\n\n // Structural-only fallback when jiti can't evaluate the config (e.g. import-with\n // assets): we can't verify values, only that the file still exports `config`.\n const content = readFileSync(configFilePath, \"utf-8\");\n if (!configFileExportsConfig(content, configFilePath)) {\n throw new Error(`Config update validation failed for ${configFilePath}: the updated file no longer exports a valid \\`config\\`.`);\n }\n}\n\nfunction configFileExportsConfig(content: string, configFilePath: string): boolean {\n try {\n evalConfigFileContent(content, configFilePath);\n return true;\n } catch {\n // jiti may fail to resolve imports valid in the user's project but absent\n // here (relative assets, workspace packages). For the structural check we\n // only need a runtime `config` binding to still exist; the agent always\n // authors `export const config`.\n return /\\bexport\\s+const\\s+config\\b/.test(content);\n }\n}\n\nfunction getRelativeImportSpecifiers(content: string): string[] {\n const specifiers: string[] = [];\n const importPattern = /\\bimport\\b(?:[^'\"]*?\\bfrom\\s*)?[\"'](\\.{1,2}\\/[^\"']+)[\"']/g;\n let match: RegExpExecArray | null;\n while ((match = importPattern.exec(content)) !== null) {\n specifiers.push(match[1]);\n }\n return specifiers;\n}\n\nfunction flattenConfigUpdate(update: Config): ConfigChange[] {\n const changes: ConfigChange[] = [];\n const walk = (prefix: string, obj: Config): void => {\n for (const [key, value] of Object.entries(obj)) {\n const fullPath = prefix === \"\" ? key : `${prefix}.${key}`;\n if (value === undefined) continue;\n if (value !== null && typeof value === \"object\" && !Array.isArray(value) && Object.keys(value).length > 0) {\n walk(fullPath, value);\n } else {\n changes.push({ path: fullPath, value });\n }\n }\n };\n walk(\"\", update);\n return changes;\n}\n\nfunction buildConfigUpdatePrompt(configFileName: string, configUpdate: Config, baselineConfig: Config | null): string {\n const changes = flattenConfigUpdate(configUpdate);\n const commandPolicy = \"Do not run shell commands and do not create files other than what is required to apply the config changes.\";\n if (baselineConfig != null) {\n return buildCompleteConfigAgentPrompt({\n scope: { mode: \"known-file\", configFileName },\n completeConfig: canonicalizeConfig(override(baselineConfig, configUpdate)),\n commandPolicy,\n });\n }\n return buildPartialConfigAgentPrompt({\n configFileName,\n changes,\n commandPolicy,\n });\n}\n\nfunction canonicalizeConfig(config: Config): NormalizedConfig {\n const droppedKeys: string[] = [];\n const normalized = normalize(config, {\n onDotIntoNonObject: \"ignore\",\n onDotIntoNull: \"empty-object\",\n droppedKeys,\n });\n if (droppedKeys.length > 0) {\n throw new Error(`Config update has conflicting keys that would be dropped during normalization: ${droppedKeys.map((key) => JSON.stringify(key)).join(\", \")}`);\n }\n return normalized;\n}\n\nfunction configsEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return a === b;\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;\n return a.every((value, index) => configsEqual(value, b[index]));\n }\n if (typeof a === \"object\" && typeof b === \"object\") {\n const aEntries = Object.entries(a);\n const bMap = new Map(Object.entries(b));\n if (aEntries.length !== bMap.size) return false;\n return aEntries.every(([key, value]) => bMap.has(key) && configsEqual(value, bMap.get(key)));\n }\n return false;\n}\n"],"mappings":"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"}
@@ -1,16 +1,2 @@
1
- import { Config } from "@hexclave/shared/dist/config/format";
2
-
3
- //#region src/index.d.ts
4
- declare function sha256String(value: string): string;
5
- declare function resolveConfigFilePath(inputPath: string): string;
6
- declare function ensureConfigFileExists(configFilePath: string): void;
7
- declare function readConfigObject(configFilePath: string): Promise<Config>;
8
- declare function readConfigFile(configFilePath: string): Promise<{
9
- config: Config;
10
- showOnboarding: boolean;
11
- }>;
12
- declare function updateConfigObject(configFilePath: string, configUpdate: Config): Promise<void>;
13
- declare function replaceConfigObject(configFilePath: string, config: Config): Promise<void>;
14
- //#endregion
15
- export { ensureConfigFileExists, readConfigFile, readConfigObject, replaceConfigObject, resolveConfigFilePath, sha256String, updateConfigObject };
16
- //# sourceMappingURL=index.d.ts.map
1
+ export * from "./config-file";
2
+ export * from "./config-updater";