@hexclave/shared-backend 1.0.33 → 1.0.35

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":";;;iBA2BgB,YAAA,CAAa,KAAA;AAAA,iBAIb,qBAAA,CAAsB,SAAA;AAAA,iBAkBtB,sBAAA,CAAuB,cAAA;AAAA,iBAMjB,gBAAA,CAAiB,cAAA,WAAyB,OAAA,CAAQ,MAAA;AAAA,iBAIlD,cAAA,CAAe,cAAA,WAAyB,OAAA;EAAU,MAAA,EAAQ,MAAA;EAAQ,cAAA;AAAA;AAAA,iBAkElE,kBAAA,CAAmB,cAAA,UAAwB,YAAA,EAAc,MAAA,GAAS,OAAA;AAAA,iBA2ClE,mBAAA,CAAoB,cAAA,UAAwB,MAAA,EAAQ,MAAA,GAAS,OAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":";;;iBA4EgB,YAAA,CAAa,KAAA;AAAA,iBAIb,qBAAA,CAAsB,SAAA;AAAA,iBAkBtB,sBAAA,CAAuB,cAAA;AAAA,iBAMjB,gBAAA,CAAiB,cAAA,WAAyB,OAAA,CAAQ,MAAA;AAAA,iBAIlD,cAAA,CAAe,cAAA,WAAyB,OAAA;EAAU,MAAA,EAAQ,MAAA;EAAQ,cAAA;AAAA;AAAA,iBAkElE,kBAAA,CAAmB,cAAA,UAAwB,YAAA,EAAc,MAAA,GAAS,OAAA;AAAA,iBA+ElE,mBAAA,CAAoB,cAAA,UAAwB,MAAA,EAAQ,MAAA,GAAS,OAAA"}
package/dist/esm/index.js CHANGED
@@ -12,6 +12,44 @@ import { ClaudeAgentFailureError, ClaudeAgentTimeoutError, getToolWriteTargetPat
12
12
  const jiti = createJiti(import.meta.url, { moduleCache: false });
13
13
  const LOG_PREFIX = "[Stack config updater]";
14
14
  const DEFAULT_AGENT_TIMEOUT_MS = 12e4;
15
+ const CONFIG_UPDATE_LOG_PATH_LIMIT = 40;
16
+ const AGENT_OUTPUT_LOG_MAX_LENGTH = 2e4;
17
+ function formatConfigUpdaterErrorForLog(error) {
18
+ if (error instanceof Error) return {
19
+ errorName: error.name,
20
+ errorMessage: error.message,
21
+ errorStack: error.stack
22
+ };
23
+ return { errorMessage: String(error) };
24
+ }
25
+ function configUpdatePathDetailsForLog(changes) {
26
+ const paths = changes.map(({ path: configPath }) => configPath).sort();
27
+ return {
28
+ configUpdatePathCount: paths.length,
29
+ configUpdatePaths: paths.slice(0, CONFIG_UPDATE_LOG_PATH_LIMIT),
30
+ configUpdatePathsTruncated: paths.length > CONFIG_UPDATE_LOG_PATH_LIMIT
31
+ };
32
+ }
33
+ function appendBoundedAgentOutput(current, chunk) {
34
+ const next = `${current}${chunk}`;
35
+ if (next.length <= AGENT_OUTPUT_LOG_MAX_LENGTH) return next;
36
+ return next.slice(next.length - AGENT_OUTPUT_LOG_MAX_LENGTH);
37
+ }
38
+ function stringifyAgentMessageForLog(message) {
39
+ try {
40
+ return `${JSON.stringify(message)}\n`;
41
+ } catch {
42
+ return `${String(message)}\n`;
43
+ }
44
+ }
45
+ function agentOutputDetailsForLog(agentStdout, agentStderr) {
46
+ return {
47
+ agentStdout,
48
+ agentStdoutTruncated: agentStdout.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,
49
+ agentStderr,
50
+ agentStderrTruncated: agentStderr.length >= AGENT_OUTPUT_LOG_MAX_LENGTH
51
+ };
52
+ }
15
53
  function isConfigModule(value) {
16
54
  return value !== null && typeof value === "object";
17
55
  }
@@ -81,16 +119,35 @@ function renderConfigObjectToFile(configFilePath, config) {
81
119
  writeFileAtomic(configFilePath, renderConfigObjectToString(configFilePath, config));
82
120
  }
83
121
  async function updateConfigObject(configFilePath, configUpdate) {
122
+ const startedAtMs = performance.now();
84
123
  ensureConfigFileExists(configFilePath);
85
- if (flattenConfigUpdate(configUpdate).length === 0) return;
124
+ const changes = flattenConfigUpdate(configUpdate);
125
+ if (changes.length === 0) {
126
+ console.log(`${LOG_PREFIX} Skipping config update because it contains no changes`, { configFilePath });
127
+ return;
128
+ }
129
+ const updateLogDetails = {
130
+ configFilePath,
131
+ ...configUpdatePathDetailsForLog(changes)
132
+ };
133
+ console.log(`${LOG_PREFIX} Starting config file update`, updateLogDetails);
86
134
  const content = readFileSync(configFilePath, "utf-8");
87
135
  const staticConfig = tryParseStaticConfigFileContent(content, configFilePath);
88
136
  if (staticConfig != null && isValidConfig(staticConfig)) {
137
+ console.log(`${LOG_PREFIX} Applying config update with static config rewrite`, updateLogDetails);
89
138
  const merged = override(staticConfig, configUpdate);
90
139
  if (!isValidConfig(merged)) throw new Error(`${LOG_PREFIX} Merged config is invalid after applying update to ${configFilePath}`);
91
140
  renderConfigObjectToFile(configFilePath, merged);
141
+ console.log(`${LOG_PREFIX} Finished config update with static config rewrite`, {
142
+ ...updateLogDetails,
143
+ elapsedMs: Math.round(performance.now() - startedAtMs)
144
+ });
92
145
  return;
93
146
  }
147
+ console.log(`${LOG_PREFIX} Applying config update with agent-assisted rewrite`, {
148
+ ...updateLogDetails,
149
+ configDirectory: path.dirname(configFilePath)
150
+ });
94
151
  const baselineConfig = await tryReadConfigForValidation(configFilePath);
95
152
  const { snapshots, seen } = snapshotConfigFiles(configFilePath, content);
96
153
  try {
@@ -101,16 +158,31 @@ async function updateConfigObject(configFilePath, configUpdate) {
101
158
  });
102
159
  await validateAgentUpdate(configFilePath, baselineConfig, configUpdate, snapshots);
103
160
  } catch (error) {
161
+ console.warn(`${LOG_PREFIX} Config update failed; restoring files from snapshots`, {
162
+ ...updateLogDetails,
163
+ snapshotCount: snapshots.length,
164
+ elapsedMs: Math.round(performance.now() - startedAtMs),
165
+ ...formatConfigUpdaterErrorForLog(error)
166
+ });
104
167
  try {
105
168
  restoreConfigFiles(snapshots);
169
+ console.warn(`${LOG_PREFIX} Restored files after failed config update`, {
170
+ ...updateLogDetails,
171
+ snapshotCount: snapshots.length
172
+ });
106
173
  } catch (restoreError) {
107
174
  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`, {
108
175
  configFilePath,
109
- restoreError: restoreError instanceof Error ? restoreError.message : String(restoreError)
176
+ ...formatConfigUpdaterErrorForLog(restoreError)
110
177
  });
111
178
  }
112
179
  throw error;
113
180
  }
181
+ console.log(`${LOG_PREFIX} Finished config update with agent-assisted rewrite`, {
182
+ ...updateLogDetails,
183
+ elapsedMs: Math.round(performance.now() - startedAtMs),
184
+ snapshotCount: snapshots.length
185
+ });
114
186
  }
115
187
  async function replaceConfigObject(configFilePath, config) {
116
188
  renderConfigObjectToFile(configFilePath, config);
@@ -118,6 +190,13 @@ async function replaceConfigObject(configFilePath, config) {
118
190
  async function runConfigUpdateAgent(options) {
119
191
  const timeoutMs = parseAgentTimeoutMs();
120
192
  const deniedOutOfBoundsWrites = /* @__PURE__ */ new Set();
193
+ const startedAtMs = performance.now();
194
+ let agentStdout = "";
195
+ let agentStderr = "";
196
+ console.log(`${LOG_PREFIX} Starting config update agent`, {
197
+ cwd: options.cwd,
198
+ timeoutMs
199
+ });
121
200
  try {
122
201
  await runHeadlessClaudeAgent({
123
202
  prompt: options.prompt,
@@ -132,8 +211,12 @@ async function runConfigUpdateAgent(options) {
132
211
  strictIsolation: true,
133
212
  timeoutMs,
134
213
  stderr: (data) => {
214
+ agentStderr = appendBoundedAgentOutput(agentStderr, data);
135
215
  console.warn(`${LOG_PREFIX} [agent] ${data}`);
136
216
  },
217
+ onMessage: (message) => {
218
+ agentStdout = appendBoundedAgentOutput(agentStdout, stringifyAgentMessageForLog(message));
219
+ },
137
220
  onPreToolUse: (input) => {
138
221
  const target = getToolWriteTargetPath(input.tool_name, input.tool_input, options.cwd);
139
222
  if (target == null) return { continue: true };
@@ -150,11 +233,49 @@ async function runConfigUpdateAgent(options) {
150
233
  }
151
234
  });
152
235
  } catch (error) {
153
- if (error instanceof ClaudeAgentTimeoutError) throw new Error(`Config update agent timed out after ${timeoutMs}ms. It was unable to apply the config changes to the file.`);
154
- if (error instanceof ClaudeAgentFailureError) throw new Error(`${error.message} It was unable to apply the config changes to the file.`);
236
+ if (error instanceof ClaudeAgentTimeoutError) {
237
+ console.warn(`${LOG_PREFIX} Config update agent timed out`, {
238
+ cwd: options.cwd,
239
+ timeoutMs,
240
+ elapsedMs: Math.round(performance.now() - startedAtMs),
241
+ ...formatConfigUpdaterErrorForLog(error),
242
+ ...agentOutputDetailsForLog(agentStdout, agentStderr)
243
+ });
244
+ throw new Error(`Config update agent timed out after ${timeoutMs}ms. It was unable to apply the config changes to the file.`);
245
+ }
246
+ if (error instanceof ClaudeAgentFailureError) {
247
+ console.warn(`${LOG_PREFIX} Config update agent failed`, {
248
+ cwd: options.cwd,
249
+ timeoutMs,
250
+ elapsedMs: Math.round(performance.now() - startedAtMs),
251
+ ...formatConfigUpdaterErrorForLog(error),
252
+ ...agentOutputDetailsForLog(agentStdout, agentStderr)
253
+ });
254
+ throw new Error(`${error.message} It was unable to apply the config changes to the file.`);
255
+ }
256
+ console.warn(`${LOG_PREFIX} Config update agent failed unexpectedly`, {
257
+ cwd: options.cwd,
258
+ timeoutMs,
259
+ elapsedMs: Math.round(performance.now() - startedAtMs),
260
+ ...formatConfigUpdaterErrorForLog(error),
261
+ ...agentOutputDetailsForLog(agentStdout, agentStderr)
262
+ });
155
263
  throw error;
156
264
  }
157
- if (deniedOutOfBoundsWrites.size > 0) 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.`);
265
+ console.log(`${LOG_PREFIX} Finished config update agent`, {
266
+ cwd: options.cwd,
267
+ timeoutMs,
268
+ elapsedMs: Math.round(performance.now() - startedAtMs),
269
+ deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size
270
+ });
271
+ if (deniedOutOfBoundsWrites.size > 0) {
272
+ console.warn(`${LOG_PREFIX} Config update agent attempted out-of-bounds writes`, {
273
+ cwd: options.cwd,
274
+ deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,
275
+ deniedOutOfBoundsWrites: [...deniedOutOfBoundsWrites]
276
+ });
277
+ 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.`);
278
+ }
158
279
  }
159
280
  function parseAgentTimeoutMs() {
160
281
  const raw = process.env.STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import { showOnboardingHexclaveConfigValue } from \"@hexclave/shared/dist/config-authoring\";\nimport { detectImportPackageFromDir, parseHexclaveConfigFileContent, renderConfigFileContent } from \"@hexclave/shared/dist/config-rendering\";\nimport type { Config, ConfigValue, NormalizedConfig } from \"@hexclave/shared/dist/config/format\";\nimport { isValidConfig, normalize, override } from \"@hexclave/shared/dist/config/format\";\nimport { captureError } from \"@hexclave/shared/dist/utils/errors\";\nimport { createHash } from \"crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"fs\";\nimport { createJiti } from \"jiti\";\nimport path from \"path\";\nimport { ClaudeAgentFailureError, ClaudeAgentTimeoutError, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from \"./config-agent\";\n\nconst jiti = createJiti(import.meta.url, { moduleCache: false });\n\nconst LOG_PREFIX = \"[Stack config updater]\";\nconst DEFAULT_AGENT_TIMEOUT_MS = 120_000;\n\ntype ConfigModule = {\n config?: unknown,\n};\n\ntype ConfigFileSnapshot = { path: string, content: string | null };\ntype ConfigChange = { path: string, value: ConfigValue };\n\nfunction isConfigModule(value: unknown): value is ConfigModule {\n return value !== null && typeof value === \"object\";\n}\n\nexport function sha256String(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function resolveConfigFilePath(inputPath: string): string {\n const resolved = path.resolve(inputPath);\n const looksLikeConfigFile = /\\.(ts|js|mjs|cjs)$/i.test(resolved);\n if (looksLikeConfigFile) {\n return resolved;\n }\n // Prefer hexclave.config.ts, fall back to stack.config.ts, default to the new name.\n const hexclaveCandidate = path.join(resolved, \"hexclave.config.ts\");\n const legacyCandidate = path.join(resolved, \"stack.config.ts\");\n if (existsSync(hexclaveCandidate)) {\n return hexclaveCandidate;\n }\n if (existsSync(legacyCandidate)) {\n return legacyCandidate;\n }\n return hexclaveCandidate;\n}\n\nexport function ensureConfigFileExists(configFilePath: string): void {\n if (existsSync(configFilePath)) return;\n mkdirSync(path.dirname(configFilePath), { recursive: true });\n renderConfigObjectToFile(configFilePath, {});\n}\n\nexport async function readConfigObject(configFilePath: string): Promise<Config> {\n return (await readConfigFile(configFilePath)).config;\n}\n\nexport async function readConfigFile(configFilePath: string): Promise<{ config: Config, showOnboarding: boolean }> {\n ensureConfigFileExists(configFilePath);\n const content = readFileSync(configFilePath, \"utf-8\");\n if (content.trim() === \"\") {\n return {\n config: {},\n showOnboarding: false,\n };\n }\n\n let configModule: unknown;\n try {\n configModule = await jiti.import<unknown>(configFilePath);\n } catch (error) {\n // Capture the raw jiti/framework error for diagnostics, but don't attach it as `cause` on the thrown error:\n // dashboard error formatting renders causes recursively, which would leak framework internals into the\n // user-facing message we're deliberately replacing.\n captureError(\"shared-backend/readConfigFile\", error);\n throw new Error(\n `Failed to load config file ${configFilePath}.`,\n );\n }\n if (!isConfigModule(configModule)) {\n throw new Error(`Invalid config in ${configFilePath}. The file must export a plain \\`config\\` object or \"show-onboarding\".`);\n }\n\n const config = configModule.config;\n if (config === showOnboardingHexclaveConfigValue) {\n return {\n config: {},\n showOnboarding: true,\n };\n }\n if (!isValidConfig(config)) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n return {\n config,\n showOnboarding: false,\n };\n}\n\nfunction renderConfigObjectToString(configFilePath: string, config: Config): string {\n const importPackage = detectImportPackageFromDir(path.dirname(configFilePath));\n return renderConfigFileContent(config, importPackage);\n}\n\nfunction writeFileAtomic(configFilePath: string, content: string): void {\n const dir = path.dirname(configFilePath);\n mkdirSync(dir, { recursive: true });\n const tempPath = path.join(dir, `.stack.config.${Math.random().toString(36).slice(2)}.tmp`);\n writeFileSync(tempPath, content, \"utf-8\");\n try {\n renameSync(tempPath, configFilePath);\n } catch (error) {\n try {\n rmSync(tempPath);\n } catch { /* best-effort cleanup */ }\n throw error;\n }\n}\n\nfunction renderConfigObjectToFile(configFilePath: string, config: Config): void {\n writeFileAtomic(configFilePath, renderConfigObjectToString(configFilePath, config));\n}\n\nexport async function updateConfigObject(configFilePath: string, configUpdate: Config): Promise<void> {\n ensureConfigFileExists(configFilePath);\n\n if (flattenConfigUpdate(configUpdate).length === 0) return;\n\n const content = readFileSync(configFilePath, \"utf-8\");\n\n // Fast path: if the config is a plain static literal (no imports, no helpers),\n // apply the update deterministically without invoking the AI agent.\n const staticConfig = tryParseStaticConfigFileContent(content, configFilePath);\n if (staticConfig != null && isValidConfig(staticConfig)) {\n const merged = override(staticConfig, configUpdate);\n if (!isValidConfig(merged)) {\n throw new Error(`${LOG_PREFIX} Merged config is invalid after applying update to ${configFilePath}`);\n }\n renderConfigObjectToFile(configFilePath, merged);\n return;\n }\n\n // Agent path: config has custom structure (imports, helpers, external files)\n // that must be preserved — delegate to the AI agent.\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, snapshots);\n } catch (error) {\n try {\n restoreConfigFiles(snapshots);\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 restoreError: restoreError instanceof Error ? restoreError.message : String(restoreError),\n });\n }\n throw error;\n }\n}\n\nexport async function replaceConfigObject(configFilePath: string, config: Config): Promise<void> {\n renderConfigObjectToFile(configFilePath, config);\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 try {\n await runHeadlessClaudeAgent({\n prompt: options.prompt,\n cwd: options.cwd,\n allowedTools: [\"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\"],\n strictIsolation: true,\n timeoutMs,\n stderr: (data) => { console.warn(`${LOG_PREFIX} [agent] ${data}`); },\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 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 throw new Error(`${error.message} It was unable to apply the config changes to the file.`);\n }\n throw error;\n }\n if (deniedOutOfBoundsWrites.size > 0) {\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, snapshots: ConfigFileSnapshot[]): 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. missing\n // runtime dependencies in import-with attributes), we can only verify that\n // (a) something changed on disk and (b) the file still exports `config`.\n // This cannot catch silently mis-applied values — an accepted tradeoff vs.\n // blocking updates entirely for configs we can't evaluate.\n // When nothing changed on disk the update is either already applied or the\n // agent couldn't figure out what to do. Treat it as a no-op rather than a\n // hard failure: the structural check below still verifies the file is valid.\n if (flattenConfigUpdate(configUpdate).length > 0 && !snapshotsChangedOnDisk(snapshots)) {\n console.warn(`${LOG_PREFIX} Agent did not modify any file for ${configFilePath}; assuming values are already up to date.`);\n }\n\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 tryParseStaticConfigFileContent(content: string, configFilePath: string): Config | null {\n try {\n const parsed = parseHexclaveConfigFileContent(content, configFilePath);\n return isValidConfig(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction configFileExportsConfig(content: string, configFilePath: string): boolean {\n try {\n parseHexclaveConfigFileContent(content, configFilePath);\n return true;\n } catch {\n // Dynamic configs can be valid even when the static parser cannot evaluate\n // them. For the structural fallback we only need to know that a runtime\n // config binding still exists after the agent edited the file.\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 snapshotsChangedOnDisk(snapshots: ConfigFileSnapshot[]): boolean {\n return snapshots.some(({ path: filePath, content }) => {\n const current = existsSync(filePath) ? readFileSync(filePath, \"utf-8\") : null;\n return current !== content;\n });\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 changeLines = changes.map(({ path: configPath, value }) => {\n return `- ${JSON.stringify(configPath)}: set to ${JSON.stringify(value)}`;\n }).join(\"\\n\");\n const expectedConfig = baselineConfig == null ? null : canonicalizeConfig(override(baselineConfig, configUpdate));\n const expectedConfigSection = expectedConfig == null ? \"\" : `\nAfter the edit, evaluating the exported \\`config\\` must produce this exact JSON value:\n\n${JSON.stringify(expectedConfig, null, 2)}\n`;\n\n return `You are editing a Hexclave / Stack Auth configuration file in place. Apply a set of configuration changes WITHOUT changing how the file is written.\n\nConfig file: ${JSON.stringify(configFileName)} (in the current working directory).\n\nThe file exports a \\`config\\` object (it may be wrapped in a helper such as \\`defineStackConfig(...)\\`). Some config values may be sourced from other files via imports, for example:\n\n import welcomeEmail from \"./welcome-email.tsx\" with { type: \"text\" };\n export const config = { emails: { templates: { welcome: welcomeEmail } } };\n\nApply EXACTLY these changes. Paths use dot notation, so \\`a.b.c\\` refers to \\`config.a.b.c\\`:\n\n${changeLines}\n${expectedConfigSection}\n\nRules:\n- Change ONLY the config paths listed above. Leave every other part of the file byte-for-byte unchanged: imports, comments, formatting, helper wrappers, and any config fields not listed.\n- If a listed path's value is currently provided by an imported external file (like the \\`import ... with { type: \"text\" }\\` example above), DO NOT inline the new value into the config file. Instead, overwrite that external file with the new value and keep the import statement intact.\n- If a listed path's value is a plain inline literal, edit it inline.\n- Keep the file valid: it must still export a \\`config\\` that, once evaluated, reflects the new values exactly.\n- Do not run any shell commands and do not create files other than what is required to apply these changes.`;\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
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import { showOnboardingHexclaveConfigValue } from \"@hexclave/shared/dist/config-authoring\";\nimport { detectImportPackageFromDir, parseHexclaveConfigFileContent, renderConfigFileContent } from \"@hexclave/shared/dist/config-rendering\";\nimport type { Config, ConfigValue, NormalizedConfig } from \"@hexclave/shared/dist/config/format\";\nimport { isValidConfig, normalize, override } from \"@hexclave/shared/dist/config/format\";\nimport { captureError } from \"@hexclave/shared/dist/utils/errors\";\nimport { createHash } from \"crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"fs\";\nimport { createJiti } from \"jiti\";\nimport path from \"path\";\nimport { ClaudeAgentFailureError, ClaudeAgentTimeoutError, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from \"./config-agent\";\n\nconst jiti = createJiti(import.meta.url, { moduleCache: false });\n\nconst LOG_PREFIX = \"[Stack 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 ConfigModule = {\n config?: unknown,\n};\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\nfunction isConfigModule(value: unknown): value is ConfigModule {\n return value !== null && typeof value === \"object\";\n}\n\nexport function sha256String(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function resolveConfigFilePath(inputPath: string): string {\n const resolved = path.resolve(inputPath);\n const looksLikeConfigFile = /\\.(ts|js|mjs|cjs)$/i.test(resolved);\n if (looksLikeConfigFile) {\n return resolved;\n }\n // Prefer hexclave.config.ts, fall back to stack.config.ts, default to the new name.\n const hexclaveCandidate = path.join(resolved, \"hexclave.config.ts\");\n const legacyCandidate = path.join(resolved, \"stack.config.ts\");\n if (existsSync(hexclaveCandidate)) {\n return hexclaveCandidate;\n }\n if (existsSync(legacyCandidate)) {\n return legacyCandidate;\n }\n return hexclaveCandidate;\n}\n\nexport function ensureConfigFileExists(configFilePath: string): void {\n if (existsSync(configFilePath)) return;\n mkdirSync(path.dirname(configFilePath), { recursive: true });\n renderConfigObjectToFile(configFilePath, {});\n}\n\nexport async function readConfigObject(configFilePath: string): Promise<Config> {\n return (await readConfigFile(configFilePath)).config;\n}\n\nexport async function readConfigFile(configFilePath: string): Promise<{ config: Config, showOnboarding: boolean }> {\n ensureConfigFileExists(configFilePath);\n const content = readFileSync(configFilePath, \"utf-8\");\n if (content.trim() === \"\") {\n return {\n config: {},\n showOnboarding: false,\n };\n }\n\n let configModule: unknown;\n try {\n configModule = await jiti.import<unknown>(configFilePath);\n } catch (error) {\n // Capture the raw jiti/framework error for diagnostics, but don't attach it as `cause` on the thrown error:\n // dashboard error formatting renders causes recursively, which would leak framework internals into the\n // user-facing message we're deliberately replacing.\n captureError(\"shared-backend/readConfigFile\", error);\n throw new Error(\n `Failed to load config file ${configFilePath}.`,\n );\n }\n if (!isConfigModule(configModule)) {\n throw new Error(`Invalid config in ${configFilePath}. The file must export a plain \\`config\\` object or \"show-onboarding\".`);\n }\n\n const config = configModule.config;\n if (config === showOnboardingHexclaveConfigValue) {\n return {\n config: {},\n showOnboarding: true,\n };\n }\n if (!isValidConfig(config)) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n return {\n config,\n showOnboarding: false,\n };\n}\n\nfunction renderConfigObjectToString(configFilePath: string, config: Config): string {\n const importPackage = detectImportPackageFromDir(path.dirname(configFilePath));\n return renderConfigFileContent(config, importPackage);\n}\n\nfunction writeFileAtomic(configFilePath: string, content: string): void {\n const dir = path.dirname(configFilePath);\n mkdirSync(dir, { recursive: true });\n const tempPath = path.join(dir, `.stack.config.${Math.random().toString(36).slice(2)}.tmp`);\n writeFileSync(tempPath, content, \"utf-8\");\n try {\n renameSync(tempPath, configFilePath);\n } catch (error) {\n try {\n rmSync(tempPath);\n } catch { /* best-effort cleanup */ }\n throw error;\n }\n}\n\nfunction renderConfigObjectToFile(configFilePath: string, config: Config): void {\n writeFileAtomic(configFilePath, renderConfigObjectToString(configFilePath, config));\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 // Fast path: if the config is a plain static literal (no imports, no helpers),\n // apply the update deterministically without invoking the AI agent.\n const staticConfig = tryParseStaticConfigFileContent(content, configFilePath);\n if (staticConfig != null && isValidConfig(staticConfig)) {\n console.log(`${LOG_PREFIX} Applying config update with static config rewrite`, updateLogDetails);\n const merged = override(staticConfig, configUpdate);\n if (!isValidConfig(merged)) {\n throw new Error(`${LOG_PREFIX} Merged config is invalid after applying update to ${configFilePath}`);\n }\n renderConfigObjectToFile(configFilePath, merged);\n console.log(`${LOG_PREFIX} Finished config update with static config rewrite`, {\n ...updateLogDetails,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n });\n return;\n }\n\n // Agent path: config has custom structure (imports, helpers, external files)\n // that must be preserved — delegate to the AI 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, snapshots);\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\nexport async function replaceConfigObject(configFilePath: string, config: Config): Promise<void> {\n renderConfigObjectToFile(configFilePath, config);\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: [\"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\"],\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, snapshots: ConfigFileSnapshot[]): 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. missing\n // runtime dependencies in import-with attributes), we can only verify that\n // (a) something changed on disk and (b) the file still exports `config`.\n // This cannot catch silently mis-applied values — an accepted tradeoff vs.\n // blocking updates entirely for configs we can't evaluate.\n // When nothing changed on disk the update is either already applied or the\n // agent couldn't figure out what to do. Treat it as a no-op rather than a\n // hard failure: the structural check below still verifies the file is valid.\n if (flattenConfigUpdate(configUpdate).length > 0 && !snapshotsChangedOnDisk(snapshots)) {\n console.warn(`${LOG_PREFIX} Agent did not modify any file for ${configFilePath}; assuming values are already up to date.`);\n }\n\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 tryParseStaticConfigFileContent(content: string, configFilePath: string): Config | null {\n try {\n const parsed = parseHexclaveConfigFileContent(content, configFilePath);\n return isValidConfig(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction configFileExportsConfig(content: string, configFilePath: string): boolean {\n try {\n parseHexclaveConfigFileContent(content, configFilePath);\n return true;\n } catch {\n // Dynamic configs can be valid even when the static parser cannot evaluate\n // them. For the structural fallback we only need to know that a runtime\n // config binding still exists after the agent edited the file.\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 snapshotsChangedOnDisk(snapshots: ConfigFileSnapshot[]): boolean {\n return snapshots.some(({ path: filePath, content }) => {\n const current = existsSync(filePath) ? readFileSync(filePath, \"utf-8\") : null;\n return current !== content;\n });\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 changeLines = changes.map(({ path: configPath, value }) => {\n return `- ${JSON.stringify(configPath)}: set to ${JSON.stringify(value)}`;\n }).join(\"\\n\");\n const expectedConfig = baselineConfig == null ? null : canonicalizeConfig(override(baselineConfig, configUpdate));\n const expectedConfigSection = expectedConfig == null ? \"\" : `\nAfter the edit, evaluating the exported \\`config\\` must produce this exact JSON value:\n\n${JSON.stringify(expectedConfig, null, 2)}\n`;\n\n return `You are editing a Hexclave / Stack Auth configuration file in place. Apply a set of configuration changes WITHOUT changing how the file is written.\n\nConfig file: ${JSON.stringify(configFileName)} (in the current working directory).\n\nThe file exports a \\`config\\` object (it may be wrapped in a helper such as \\`defineStackConfig(...)\\`). Some config values may be sourced from other files via imports, for example:\n\n import welcomeEmail from \"./welcome-email.tsx\" with { type: \"text\" };\n export const config = { emails: { templates: { welcome: welcomeEmail } } };\n\nApply EXACTLY these changes. Paths use dot notation, so \\`a.b.c\\` refers to \\`config.a.b.c\\`:\n\n${changeLines}\n${expectedConfigSection}\n\nRules:\n- Change ONLY the config paths listed above. Leave every other part of the file byte-for-byte unchanged: imports, comments, formatting, helper wrappers, and any config fields not listed.\n- If a listed path's value is currently provided by an imported external file (like the \\`import ... with { type: \"text\" }\\` example above), DO NOT inline the new value into the config file. Instead, overwrite that external file with the new value and keep the import statement intact.\n- If a listed path's value is a plain inline literal, edit it inline.\n- Keep the file valid: it must still export a \\`config\\` that, once evaluated, reflects the new values exactly.\n- Do not run any shell commands and do not create files other than what is required to apply these changes.`;\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 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;iBA2BgB,YAAA,CAAa,KAAA;AAAA,iBAIb,qBAAA,CAAsB,SAAA;AAAA,iBAkBtB,sBAAA,CAAuB,cAAA;AAAA,iBAMjB,gBAAA,CAAiB,cAAA,WAAyB,OAAA,CAAQ,MAAA;AAAA,iBAIlD,cAAA,CAAe,cAAA,WAAyB,OAAA;EAAU,MAAA,EAAQ,MAAA;EAAQ,cAAA;AAAA;AAAA,iBAkElE,kBAAA,CAAmB,cAAA,UAAwB,YAAA,EAAc,MAAA,GAAS,OAAA;AAAA,iBA2ClE,mBAAA,CAAoB,cAAA,UAAwB,MAAA,EAAQ,MAAA,GAAS,OAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;iBA4EgB,YAAA,CAAa,KAAA;AAAA,iBAIb,qBAAA,CAAsB,SAAA;AAAA,iBAkBtB,sBAAA,CAAuB,cAAA;AAAA,iBAMjB,gBAAA,CAAiB,cAAA,WAAyB,OAAA,CAAQ,MAAA;AAAA,iBAIlD,cAAA,CAAe,cAAA,WAAyB,OAAA;EAAU,MAAA,EAAQ,MAAA;EAAQ,cAAA;AAAA;AAAA,iBAkElE,kBAAA,CAAmB,cAAA,UAAwB,YAAA,EAAc,MAAA,GAAS,OAAA;AAAA,iBA+ElE,mBAAA,CAAoB,cAAA,UAAwB,MAAA,EAAQ,MAAA,GAAS,OAAA"}
package/dist/index.js CHANGED
@@ -15,6 +15,44 @@ let __config_agent_js = require("./config-agent.js");
15
15
  const jiti$1 = (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href, { moduleCache: false });
16
16
  const LOG_PREFIX = "[Stack config updater]";
17
17
  const DEFAULT_AGENT_TIMEOUT_MS = 12e4;
18
+ const CONFIG_UPDATE_LOG_PATH_LIMIT = 40;
19
+ const AGENT_OUTPUT_LOG_MAX_LENGTH = 2e4;
20
+ function formatConfigUpdaterErrorForLog(error) {
21
+ if (error instanceof Error) return {
22
+ errorName: error.name,
23
+ errorMessage: error.message,
24
+ errorStack: error.stack
25
+ };
26
+ return { errorMessage: String(error) };
27
+ }
28
+ function configUpdatePathDetailsForLog(changes) {
29
+ const paths = changes.map(({ path: configPath }) => configPath).sort();
30
+ return {
31
+ configUpdatePathCount: paths.length,
32
+ configUpdatePaths: paths.slice(0, CONFIG_UPDATE_LOG_PATH_LIMIT),
33
+ configUpdatePathsTruncated: paths.length > CONFIG_UPDATE_LOG_PATH_LIMIT
34
+ };
35
+ }
36
+ function appendBoundedAgentOutput(current, chunk) {
37
+ const next = `${current}${chunk}`;
38
+ if (next.length <= AGENT_OUTPUT_LOG_MAX_LENGTH) return next;
39
+ return next.slice(next.length - AGENT_OUTPUT_LOG_MAX_LENGTH);
40
+ }
41
+ function stringifyAgentMessageForLog(message) {
42
+ try {
43
+ return `${JSON.stringify(message)}\n`;
44
+ } catch {
45
+ return `${String(message)}\n`;
46
+ }
47
+ }
48
+ function agentOutputDetailsForLog(agentStdout, agentStderr) {
49
+ return {
50
+ agentStdout,
51
+ agentStdoutTruncated: agentStdout.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,
52
+ agentStderr,
53
+ agentStderrTruncated: agentStderr.length >= AGENT_OUTPUT_LOG_MAX_LENGTH
54
+ };
55
+ }
18
56
  function isConfigModule(value) {
19
57
  return value !== null && typeof value === "object";
20
58
  }
@@ -84,16 +122,35 @@ function renderConfigObjectToFile(configFilePath, config) {
84
122
  writeFileAtomic(configFilePath, renderConfigObjectToString(configFilePath, config));
85
123
  }
86
124
  async function updateConfigObject(configFilePath, configUpdate) {
125
+ const startedAtMs = performance.now();
87
126
  ensureConfigFileExists(configFilePath);
88
- if (flattenConfigUpdate(configUpdate).length === 0) return;
127
+ const changes = flattenConfigUpdate(configUpdate);
128
+ if (changes.length === 0) {
129
+ console.log(`${LOG_PREFIX} Skipping config update because it contains no changes`, { configFilePath });
130
+ return;
131
+ }
132
+ const updateLogDetails = {
133
+ configFilePath,
134
+ ...configUpdatePathDetailsForLog(changes)
135
+ };
136
+ console.log(`${LOG_PREFIX} Starting config file update`, updateLogDetails);
89
137
  const content = (0, fs.readFileSync)(configFilePath, "utf-8");
90
138
  const staticConfig = tryParseStaticConfigFileContent(content, configFilePath);
91
139
  if (staticConfig != null && (0, _hexclave_shared_dist_config_format.isValidConfig)(staticConfig)) {
140
+ console.log(`${LOG_PREFIX} Applying config update with static config rewrite`, updateLogDetails);
92
141
  const merged = (0, _hexclave_shared_dist_config_format.override)(staticConfig, configUpdate);
93
142
  if (!(0, _hexclave_shared_dist_config_format.isValidConfig)(merged)) throw new Error(`${LOG_PREFIX} Merged config is invalid after applying update to ${configFilePath}`);
94
143
  renderConfigObjectToFile(configFilePath, merged);
144
+ console.log(`${LOG_PREFIX} Finished config update with static config rewrite`, {
145
+ ...updateLogDetails,
146
+ elapsedMs: Math.round(performance.now() - startedAtMs)
147
+ });
95
148
  return;
96
149
  }
150
+ console.log(`${LOG_PREFIX} Applying config update with agent-assisted rewrite`, {
151
+ ...updateLogDetails,
152
+ configDirectory: path.default.dirname(configFilePath)
153
+ });
97
154
  const baselineConfig = await tryReadConfigForValidation(configFilePath);
98
155
  const { snapshots, seen } = snapshotConfigFiles(configFilePath, content);
99
156
  try {
@@ -104,16 +161,31 @@ async function updateConfigObject(configFilePath, configUpdate) {
104
161
  });
105
162
  await validateAgentUpdate(configFilePath, baselineConfig, configUpdate, snapshots);
106
163
  } catch (error) {
164
+ console.warn(`${LOG_PREFIX} Config update failed; restoring files from snapshots`, {
165
+ ...updateLogDetails,
166
+ snapshotCount: snapshots.length,
167
+ elapsedMs: Math.round(performance.now() - startedAtMs),
168
+ ...formatConfigUpdaterErrorForLog(error)
169
+ });
107
170
  try {
108
171
  restoreConfigFiles(snapshots);
172
+ console.warn(`${LOG_PREFIX} Restored files after failed config update`, {
173
+ ...updateLogDetails,
174
+ snapshotCount: snapshots.length
175
+ });
109
176
  } catch (restoreError) {
110
177
  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`, {
111
178
  configFilePath,
112
- restoreError: restoreError instanceof Error ? restoreError.message : String(restoreError)
179
+ ...formatConfigUpdaterErrorForLog(restoreError)
113
180
  });
114
181
  }
115
182
  throw error;
116
183
  }
184
+ console.log(`${LOG_PREFIX} Finished config update with agent-assisted rewrite`, {
185
+ ...updateLogDetails,
186
+ elapsedMs: Math.round(performance.now() - startedAtMs),
187
+ snapshotCount: snapshots.length
188
+ });
117
189
  }
118
190
  async function replaceConfigObject(configFilePath, config) {
119
191
  renderConfigObjectToFile(configFilePath, config);
@@ -121,6 +193,13 @@ async function replaceConfigObject(configFilePath, config) {
121
193
  async function runConfigUpdateAgent(options) {
122
194
  const timeoutMs = parseAgentTimeoutMs();
123
195
  const deniedOutOfBoundsWrites = /* @__PURE__ */ new Set();
196
+ const startedAtMs = performance.now();
197
+ let agentStdout = "";
198
+ let agentStderr = "";
199
+ console.log(`${LOG_PREFIX} Starting config update agent`, {
200
+ cwd: options.cwd,
201
+ timeoutMs
202
+ });
124
203
  try {
125
204
  await (0, __config_agent_js.runHeadlessClaudeAgent)({
126
205
  prompt: options.prompt,
@@ -135,8 +214,12 @@ async function runConfigUpdateAgent(options) {
135
214
  strictIsolation: true,
136
215
  timeoutMs,
137
216
  stderr: (data) => {
217
+ agentStderr = appendBoundedAgentOutput(agentStderr, data);
138
218
  console.warn(`${LOG_PREFIX} [agent] ${data}`);
139
219
  },
220
+ onMessage: (message) => {
221
+ agentStdout = appendBoundedAgentOutput(agentStdout, stringifyAgentMessageForLog(message));
222
+ },
140
223
  onPreToolUse: (input) => {
141
224
  const target = (0, __config_agent_js.getToolWriteTargetPath)(input.tool_name, input.tool_input, options.cwd);
142
225
  if (target == null) return { continue: true };
@@ -153,11 +236,49 @@ async function runConfigUpdateAgent(options) {
153
236
  }
154
237
  });
155
238
  } catch (error) {
156
- if (error instanceof __config_agent_js.ClaudeAgentTimeoutError) throw new Error(`Config update agent timed out after ${timeoutMs}ms. It was unable to apply the config changes to the file.`);
157
- if (error instanceof __config_agent_js.ClaudeAgentFailureError) throw new Error(`${error.message} It was unable to apply the config changes to the file.`);
239
+ if (error instanceof __config_agent_js.ClaudeAgentTimeoutError) {
240
+ console.warn(`${LOG_PREFIX} Config update agent timed out`, {
241
+ cwd: options.cwd,
242
+ timeoutMs,
243
+ elapsedMs: Math.round(performance.now() - startedAtMs),
244
+ ...formatConfigUpdaterErrorForLog(error),
245
+ ...agentOutputDetailsForLog(agentStdout, agentStderr)
246
+ });
247
+ throw new Error(`Config update agent timed out after ${timeoutMs}ms. It was unable to apply the config changes to the file.`);
248
+ }
249
+ if (error instanceof __config_agent_js.ClaudeAgentFailureError) {
250
+ console.warn(`${LOG_PREFIX} Config update agent failed`, {
251
+ cwd: options.cwd,
252
+ timeoutMs,
253
+ elapsedMs: Math.round(performance.now() - startedAtMs),
254
+ ...formatConfigUpdaterErrorForLog(error),
255
+ ...agentOutputDetailsForLog(agentStdout, agentStderr)
256
+ });
257
+ throw new Error(`${error.message} It was unable to apply the config changes to the file.`);
258
+ }
259
+ console.warn(`${LOG_PREFIX} Config update agent failed unexpectedly`, {
260
+ cwd: options.cwd,
261
+ timeoutMs,
262
+ elapsedMs: Math.round(performance.now() - startedAtMs),
263
+ ...formatConfigUpdaterErrorForLog(error),
264
+ ...agentOutputDetailsForLog(agentStdout, agentStderr)
265
+ });
158
266
  throw error;
159
267
  }
160
- if (deniedOutOfBoundsWrites.size > 0) 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.`);
268
+ console.log(`${LOG_PREFIX} Finished config update agent`, {
269
+ cwd: options.cwd,
270
+ timeoutMs,
271
+ elapsedMs: Math.round(performance.now() - startedAtMs),
272
+ deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size
273
+ });
274
+ if (deniedOutOfBoundsWrites.size > 0) {
275
+ console.warn(`${LOG_PREFIX} Config update agent attempted out-of-bounds writes`, {
276
+ cwd: options.cwd,
277
+ deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,
278
+ deniedOutOfBoundsWrites: [...deniedOutOfBoundsWrites]
279
+ });
280
+ 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.`);
281
+ }
161
282
  }
162
283
  function parseAgentTimeoutMs() {
163
284
  const raw = process.env.STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["jiti","showOnboardingHexclaveConfigValue","ClaudeAgentTimeoutError","ClaudeAgentFailureError"],"sources":["../src/index.ts"],"sourcesContent":["import { showOnboardingHexclaveConfigValue } from \"@hexclave/shared/dist/config-authoring\";\nimport { detectImportPackageFromDir, parseHexclaveConfigFileContent, renderConfigFileContent } from \"@hexclave/shared/dist/config-rendering\";\nimport type { Config, ConfigValue, NormalizedConfig } from \"@hexclave/shared/dist/config/format\";\nimport { isValidConfig, normalize, override } from \"@hexclave/shared/dist/config/format\";\nimport { captureError } from \"@hexclave/shared/dist/utils/errors\";\nimport { createHash } from \"crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"fs\";\nimport { createJiti } from \"jiti\";\nimport path from \"path\";\nimport { ClaudeAgentFailureError, ClaudeAgentTimeoutError, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from \"./config-agent\";\n\nconst jiti = createJiti(import.meta.url, { moduleCache: false });\n\nconst LOG_PREFIX = \"[Stack config updater]\";\nconst DEFAULT_AGENT_TIMEOUT_MS = 120_000;\n\ntype ConfigModule = {\n config?: unknown,\n};\n\ntype ConfigFileSnapshot = { path: string, content: string | null };\ntype ConfigChange = { path: string, value: ConfigValue };\n\nfunction isConfigModule(value: unknown): value is ConfigModule {\n return value !== null && typeof value === \"object\";\n}\n\nexport function sha256String(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function resolveConfigFilePath(inputPath: string): string {\n const resolved = path.resolve(inputPath);\n const looksLikeConfigFile = /\\.(ts|js|mjs|cjs)$/i.test(resolved);\n if (looksLikeConfigFile) {\n return resolved;\n }\n // Prefer hexclave.config.ts, fall back to stack.config.ts, default to the new name.\n const hexclaveCandidate = path.join(resolved, \"hexclave.config.ts\");\n const legacyCandidate = path.join(resolved, \"stack.config.ts\");\n if (existsSync(hexclaveCandidate)) {\n return hexclaveCandidate;\n }\n if (existsSync(legacyCandidate)) {\n return legacyCandidate;\n }\n return hexclaveCandidate;\n}\n\nexport function ensureConfigFileExists(configFilePath: string): void {\n if (existsSync(configFilePath)) return;\n mkdirSync(path.dirname(configFilePath), { recursive: true });\n renderConfigObjectToFile(configFilePath, {});\n}\n\nexport async function readConfigObject(configFilePath: string): Promise<Config> {\n return (await readConfigFile(configFilePath)).config;\n}\n\nexport async function readConfigFile(configFilePath: string): Promise<{ config: Config, showOnboarding: boolean }> {\n ensureConfigFileExists(configFilePath);\n const content = readFileSync(configFilePath, \"utf-8\");\n if (content.trim() === \"\") {\n return {\n config: {},\n showOnboarding: false,\n };\n }\n\n let configModule: unknown;\n try {\n configModule = await jiti.import<unknown>(configFilePath);\n } catch (error) {\n // Capture the raw jiti/framework error for diagnostics, but don't attach it as `cause` on the thrown error:\n // dashboard error formatting renders causes recursively, which would leak framework internals into the\n // user-facing message we're deliberately replacing.\n captureError(\"shared-backend/readConfigFile\", error);\n throw new Error(\n `Failed to load config file ${configFilePath}.`,\n );\n }\n if (!isConfigModule(configModule)) {\n throw new Error(`Invalid config in ${configFilePath}. The file must export a plain \\`config\\` object or \"show-onboarding\".`);\n }\n\n const config = configModule.config;\n if (config === showOnboardingHexclaveConfigValue) {\n return {\n config: {},\n showOnboarding: true,\n };\n }\n if (!isValidConfig(config)) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n return {\n config,\n showOnboarding: false,\n };\n}\n\nfunction renderConfigObjectToString(configFilePath: string, config: Config): string {\n const importPackage = detectImportPackageFromDir(path.dirname(configFilePath));\n return renderConfigFileContent(config, importPackage);\n}\n\nfunction writeFileAtomic(configFilePath: string, content: string): void {\n const dir = path.dirname(configFilePath);\n mkdirSync(dir, { recursive: true });\n const tempPath = path.join(dir, `.stack.config.${Math.random().toString(36).slice(2)}.tmp`);\n writeFileSync(tempPath, content, \"utf-8\");\n try {\n renameSync(tempPath, configFilePath);\n } catch (error) {\n try {\n rmSync(tempPath);\n } catch { /* best-effort cleanup */ }\n throw error;\n }\n}\n\nfunction renderConfigObjectToFile(configFilePath: string, config: Config): void {\n writeFileAtomic(configFilePath, renderConfigObjectToString(configFilePath, config));\n}\n\nexport async function updateConfigObject(configFilePath: string, configUpdate: Config): Promise<void> {\n ensureConfigFileExists(configFilePath);\n\n if (flattenConfigUpdate(configUpdate).length === 0) return;\n\n const content = readFileSync(configFilePath, \"utf-8\");\n\n // Fast path: if the config is a plain static literal (no imports, no helpers),\n // apply the update deterministically without invoking the AI agent.\n const staticConfig = tryParseStaticConfigFileContent(content, configFilePath);\n if (staticConfig != null && isValidConfig(staticConfig)) {\n const merged = override(staticConfig, configUpdate);\n if (!isValidConfig(merged)) {\n throw new Error(`${LOG_PREFIX} Merged config is invalid after applying update to ${configFilePath}`);\n }\n renderConfigObjectToFile(configFilePath, merged);\n return;\n }\n\n // Agent path: config has custom structure (imports, helpers, external files)\n // that must be preserved — delegate to the AI agent.\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, snapshots);\n } catch (error) {\n try {\n restoreConfigFiles(snapshots);\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 restoreError: restoreError instanceof Error ? restoreError.message : String(restoreError),\n });\n }\n throw error;\n }\n}\n\nexport async function replaceConfigObject(configFilePath: string, config: Config): Promise<void> {\n renderConfigObjectToFile(configFilePath, config);\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 try {\n await runHeadlessClaudeAgent({\n prompt: options.prompt,\n cwd: options.cwd,\n allowedTools: [\"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\"],\n strictIsolation: true,\n timeoutMs,\n stderr: (data) => { console.warn(`${LOG_PREFIX} [agent] ${data}`); },\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 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 throw new Error(`${error.message} It was unable to apply the config changes to the file.`);\n }\n throw error;\n }\n if (deniedOutOfBoundsWrites.size > 0) {\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, snapshots: ConfigFileSnapshot[]): 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. missing\n // runtime dependencies in import-with attributes), we can only verify that\n // (a) something changed on disk and (b) the file still exports `config`.\n // This cannot catch silently mis-applied values — an accepted tradeoff vs.\n // blocking updates entirely for configs we can't evaluate.\n // When nothing changed on disk the update is either already applied or the\n // agent couldn't figure out what to do. Treat it as a no-op rather than a\n // hard failure: the structural check below still verifies the file is valid.\n if (flattenConfigUpdate(configUpdate).length > 0 && !snapshotsChangedOnDisk(snapshots)) {\n console.warn(`${LOG_PREFIX} Agent did not modify any file for ${configFilePath}; assuming values are already up to date.`);\n }\n\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 tryParseStaticConfigFileContent(content: string, configFilePath: string): Config | null {\n try {\n const parsed = parseHexclaveConfigFileContent(content, configFilePath);\n return isValidConfig(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction configFileExportsConfig(content: string, configFilePath: string): boolean {\n try {\n parseHexclaveConfigFileContent(content, configFilePath);\n return true;\n } catch {\n // Dynamic configs can be valid even when the static parser cannot evaluate\n // them. For the structural fallback we only need to know that a runtime\n // config binding still exists after the agent edited the file.\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 snapshotsChangedOnDisk(snapshots: ConfigFileSnapshot[]): boolean {\n return snapshots.some(({ path: filePath, content }) => {\n const current = existsSync(filePath) ? readFileSync(filePath, \"utf-8\") : null;\n return current !== content;\n });\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 changeLines = changes.map(({ path: configPath, value }) => {\n return `- ${JSON.stringify(configPath)}: set to ${JSON.stringify(value)}`;\n }).join(\"\\n\");\n const expectedConfig = baselineConfig == null ? null : canonicalizeConfig(override(baselineConfig, configUpdate));\n const expectedConfigSection = expectedConfig == null ? \"\" : `\nAfter the edit, evaluating the exported \\`config\\` must produce this exact JSON value:\n\n${JSON.stringify(expectedConfig, null, 2)}\n`;\n\n return `You are editing a Hexclave / Stack Auth configuration file in place. Apply a set of configuration changes WITHOUT changing how the file is written.\n\nConfig file: ${JSON.stringify(configFileName)} (in the current working directory).\n\nThe file exports a \\`config\\` object (it may be wrapped in a helper such as \\`defineStackConfig(...)\\`). Some config values may be sourced from other files via imports, for example:\n\n import welcomeEmail from \"./welcome-email.tsx\" with { type: \"text\" };\n export const config = { emails: { templates: { welcome: welcomeEmail } } };\n\nApply EXACTLY these changes. Paths use dot notation, so \\`a.b.c\\` refers to \\`config.a.b.c\\`:\n\n${changeLines}\n${expectedConfigSection}\n\nRules:\n- Change ONLY the config paths listed above. Leave every other part of the file byte-for-byte unchanged: imports, comments, formatting, helper wrappers, and any config fields not listed.\n- If a listed path's value is currently provided by an imported external file (like the \\`import ... with { type: \"text\" }\\` example above), DO NOT inline the new value into the config file. Instead, overwrite that external file with the new value and keep the import statement intact.\n- If a listed path's value is a plain inline literal, edit it inline.\n- Keep the file valid: it must still export a \\`config\\` that, once evaluated, reflects the new values exactly.\n- Do not run any shell commands and do not create files other than what is required to apply these changes.`;\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
+ {"version":3,"file":"index.js","names":["jiti","showOnboardingHexclaveConfigValue","ClaudeAgentTimeoutError","ClaudeAgentFailureError"],"sources":["../src/index.ts"],"sourcesContent":["import { showOnboardingHexclaveConfigValue } from \"@hexclave/shared/dist/config-authoring\";\nimport { detectImportPackageFromDir, parseHexclaveConfigFileContent, renderConfigFileContent } from \"@hexclave/shared/dist/config-rendering\";\nimport type { Config, ConfigValue, NormalizedConfig } from \"@hexclave/shared/dist/config/format\";\nimport { isValidConfig, normalize, override } from \"@hexclave/shared/dist/config/format\";\nimport { captureError } from \"@hexclave/shared/dist/utils/errors\";\nimport { createHash } from \"crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"fs\";\nimport { createJiti } from \"jiti\";\nimport path from \"path\";\nimport { ClaudeAgentFailureError, ClaudeAgentTimeoutError, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from \"./config-agent\";\n\nconst jiti = createJiti(import.meta.url, { moduleCache: false });\n\nconst LOG_PREFIX = \"[Stack 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 ConfigModule = {\n config?: unknown,\n};\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\nfunction isConfigModule(value: unknown): value is ConfigModule {\n return value !== null && typeof value === \"object\";\n}\n\nexport function sha256String(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function resolveConfigFilePath(inputPath: string): string {\n const resolved = path.resolve(inputPath);\n const looksLikeConfigFile = /\\.(ts|js|mjs|cjs)$/i.test(resolved);\n if (looksLikeConfigFile) {\n return resolved;\n }\n // Prefer hexclave.config.ts, fall back to stack.config.ts, default to the new name.\n const hexclaveCandidate = path.join(resolved, \"hexclave.config.ts\");\n const legacyCandidate = path.join(resolved, \"stack.config.ts\");\n if (existsSync(hexclaveCandidate)) {\n return hexclaveCandidate;\n }\n if (existsSync(legacyCandidate)) {\n return legacyCandidate;\n }\n return hexclaveCandidate;\n}\n\nexport function ensureConfigFileExists(configFilePath: string): void {\n if (existsSync(configFilePath)) return;\n mkdirSync(path.dirname(configFilePath), { recursive: true });\n renderConfigObjectToFile(configFilePath, {});\n}\n\nexport async function readConfigObject(configFilePath: string): Promise<Config> {\n return (await readConfigFile(configFilePath)).config;\n}\n\nexport async function readConfigFile(configFilePath: string): Promise<{ config: Config, showOnboarding: boolean }> {\n ensureConfigFileExists(configFilePath);\n const content = readFileSync(configFilePath, \"utf-8\");\n if (content.trim() === \"\") {\n return {\n config: {},\n showOnboarding: false,\n };\n }\n\n let configModule: unknown;\n try {\n configModule = await jiti.import<unknown>(configFilePath);\n } catch (error) {\n // Capture the raw jiti/framework error for diagnostics, but don't attach it as `cause` on the thrown error:\n // dashboard error formatting renders causes recursively, which would leak framework internals into the\n // user-facing message we're deliberately replacing.\n captureError(\"shared-backend/readConfigFile\", error);\n throw new Error(\n `Failed to load config file ${configFilePath}.`,\n );\n }\n if (!isConfigModule(configModule)) {\n throw new Error(`Invalid config in ${configFilePath}. The file must export a plain \\`config\\` object or \"show-onboarding\".`);\n }\n\n const config = configModule.config;\n if (config === showOnboardingHexclaveConfigValue) {\n return {\n config: {},\n showOnboarding: true,\n };\n }\n if (!isValidConfig(config)) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n return {\n config,\n showOnboarding: false,\n };\n}\n\nfunction renderConfigObjectToString(configFilePath: string, config: Config): string {\n const importPackage = detectImportPackageFromDir(path.dirname(configFilePath));\n return renderConfigFileContent(config, importPackage);\n}\n\nfunction writeFileAtomic(configFilePath: string, content: string): void {\n const dir = path.dirname(configFilePath);\n mkdirSync(dir, { recursive: true });\n const tempPath = path.join(dir, `.stack.config.${Math.random().toString(36).slice(2)}.tmp`);\n writeFileSync(tempPath, content, \"utf-8\");\n try {\n renameSync(tempPath, configFilePath);\n } catch (error) {\n try {\n rmSync(tempPath);\n } catch { /* best-effort cleanup */ }\n throw error;\n }\n}\n\nfunction renderConfigObjectToFile(configFilePath: string, config: Config): void {\n writeFileAtomic(configFilePath, renderConfigObjectToString(configFilePath, config));\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 // Fast path: if the config is a plain static literal (no imports, no helpers),\n // apply the update deterministically without invoking the AI agent.\n const staticConfig = tryParseStaticConfigFileContent(content, configFilePath);\n if (staticConfig != null && isValidConfig(staticConfig)) {\n console.log(`${LOG_PREFIX} Applying config update with static config rewrite`, updateLogDetails);\n const merged = override(staticConfig, configUpdate);\n if (!isValidConfig(merged)) {\n throw new Error(`${LOG_PREFIX} Merged config is invalid after applying update to ${configFilePath}`);\n }\n renderConfigObjectToFile(configFilePath, merged);\n console.log(`${LOG_PREFIX} Finished config update with static config rewrite`, {\n ...updateLogDetails,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n });\n return;\n }\n\n // Agent path: config has custom structure (imports, helpers, external files)\n // that must be preserved — delegate to the AI 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, snapshots);\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\nexport async function replaceConfigObject(configFilePath: string, config: Config): Promise<void> {\n renderConfigObjectToFile(configFilePath, config);\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: [\"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\"],\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, snapshots: ConfigFileSnapshot[]): 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. missing\n // runtime dependencies in import-with attributes), we can only verify that\n // (a) something changed on disk and (b) the file still exports `config`.\n // This cannot catch silently mis-applied values — an accepted tradeoff vs.\n // blocking updates entirely for configs we can't evaluate.\n // When nothing changed on disk the update is either already applied or the\n // agent couldn't figure out what to do. Treat it as a no-op rather than a\n // hard failure: the structural check below still verifies the file is valid.\n if (flattenConfigUpdate(configUpdate).length > 0 && !snapshotsChangedOnDisk(snapshots)) {\n console.warn(`${LOG_PREFIX} Agent did not modify any file for ${configFilePath}; assuming values are already up to date.`);\n }\n\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 tryParseStaticConfigFileContent(content: string, configFilePath: string): Config | null {\n try {\n const parsed = parseHexclaveConfigFileContent(content, configFilePath);\n return isValidConfig(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction configFileExportsConfig(content: string, configFilePath: string): boolean {\n try {\n parseHexclaveConfigFileContent(content, configFilePath);\n return true;\n } catch {\n // Dynamic configs can be valid even when the static parser cannot evaluate\n // them. For the structural fallback we only need to know that a runtime\n // config binding still exists after the agent edited the file.\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 snapshotsChangedOnDisk(snapshots: ConfigFileSnapshot[]): boolean {\n return snapshots.some(({ path: filePath, content }) => {\n const current = existsSync(filePath) ? readFileSync(filePath, \"utf-8\") : null;\n return current !== content;\n });\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 changeLines = changes.map(({ path: configPath, value }) => {\n return `- ${JSON.stringify(configPath)}: set to ${JSON.stringify(value)}`;\n }).join(\"\\n\");\n const expectedConfig = baselineConfig == null ? null : canonicalizeConfig(override(baselineConfig, configUpdate));\n const expectedConfigSection = expectedConfig == null ? \"\" : `\nAfter the edit, evaluating the exported \\`config\\` must produce this exact JSON value:\n\n${JSON.stringify(expectedConfig, null, 2)}\n`;\n\n return `You are editing a Hexclave / Stack Auth configuration file in place. Apply a set of configuration changes WITHOUT changing how the file is written.\n\nConfig file: ${JSON.stringify(configFileName)} (in the current working directory).\n\nThe file exports a \\`config\\` object (it may be wrapped in a helper such as \\`defineStackConfig(...)\\`). Some config values may be sourced from other files via imports, for example:\n\n import welcomeEmail from \"./welcome-email.tsx\" with { type: \"text\" };\n export const config = { emails: { templates: { welcome: welcomeEmail } } };\n\nApply EXACTLY these changes. Paths use dot notation, so \\`a.b.c\\` refers to \\`config.a.b.c\\`:\n\n${changeLines}\n${expectedConfigSection}\n\nRules:\n- Change ONLY the config paths listed above. Leave every other part of the file byte-for-byte unchanged: imports, comments, formatting, helper wrappers, and any config fields not listed.\n- If a listed path's value is currently provided by an imported external file (like the \\`import ... with { type: \"text\" }\\` example above), DO NOT inline the new value into the config file. Instead, overwrite that external file with the new value and keep the import statement intact.\n- If a listed path's value is a plain inline literal, edit it inline.\n- Keep the file valid: it must still export a \\`config\\` that, once evaluated, reflects the new values exactly.\n- Do not run any shell commands and do not create files other than what is required to apply these changes.`;\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexclave/shared-backend",
3
- "version": "1.0.33",
3
+ "version": "1.0.35",
4
4
  "repository": "https://github.com/hexclave/hexclave",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -29,7 +29,7 @@
29
29
  "dependencies": {
30
30
  "@anthropic-ai/claude-agent-sdk": "^0.2.73",
31
31
  "jiti": "^2.4.2",
32
- "@hexclave/shared": "1.0.33"
32
+ "@hexclave/shared": "1.0.35"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "20.17.6",