@hexclave/shared-backend 1.0.41 → 1.0.42

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":"config-agent.d.ts","names":[],"sources":["../src/config-agent.ts"],"mappings":";KAMY,mBAAA;AAAA,KAEP,qBAAA;EAA0B,QAAA;AAAA;EAC7B,kBAAA;IACE,aAAA;IACA,kBAAA;IACA,wBAAA;EAAA;AAAA;AAAA,KAIQ,0BAAA;EACV,eAAA;EACA,SAAA;EACA,UAAA;AAAA;AAAA,KAGU,qBAAA;EACV,MAAA;EACA,GAAA;EACA,YAAA,EAAc,mBAAA;EACd,SAAA;EACA,eAAA;EACA,MAAA,IAAU,IAAA;EACV,SAAA,IAAa,OAAA;EACb,YAAA,IAAgB,KAAA,EAAO,0BAAA,KAA+B,OAAA,CAAQ,qBAAA,IAAyB,qBAAA;AAAA;AAAA,KAG7E,oBAAA;EACV,UAAA;AAAA;AAAA,cAGW,uBAAA;AAAA,cACA,uBAAA;AAAA,KAYD,sBAAA;EAER,IAAA;EACA,cAAA;AAAA;EAGA,IAAA;AAAA;AAAA,iBA+BY,8BAAA,CAA+B,OAAA;EAC7C,KAAA,EAAO,sBAAA;EACP,cAAA,EAAgB,MAAA;EAChB,aAAA;AAAA;AAAA,iBAYc,6BAAA,CAA8B,OAAA;EAC5C,cAAA;EACA,OAAA,EAAS,KAAA;IAAQ,IAAA;IAAc,KAAA;EAAA;EAC/B,aAAA;AAAA;AAAA,cAeW,uBAAA,SAAgC,KAAA;cAC/B,SAAA;AAAA;AAAA,cAMD,uBAAA,SAAgC,KAAA;cAC/B,OAAA;AAAA;AAAA,iBAUE,kBAAA,CAAA,GAAsB,MAAA;AAAA,iBAOhB,sBAAA,CAAuB,OAAA,EAAS,qBAAA,GAAwB,OAAA,CAAQ,oBAAA;AAAA,iBA0EtE,sBAAA,CAAuB,QAAA,UAAkB,SAAA,WAAoB,GAAA;AAAA,iBAO7D,eAAA,CAAgB,GAAA,UAAa,MAAA"}
1
+ {"version":3,"file":"config-agent.d.ts","names":[],"sources":["../src/config-agent.ts"],"mappings":";KAMY,mBAAA;AAAA,KAEP,qBAAA;EAA0B,QAAA;AAAA;EAC7B,kBAAA;IACE,aAAA;IACA,kBAAA;IACA,wBAAA;EAAA;AAAA;AAAA,KAIQ,0BAAA;EACV,eAAA;EACA,SAAA;EACA,UAAA;AAAA;AAAA,KAGU,qBAAA;EACV,MAAA;EACA,GAAA;EACA,YAAA,EAAc,mBAAA;EACd,SAAA;EACA,eAAA;EACA,MAAA,IAAU,IAAA;EACV,SAAA,IAAa,OAAA;EACb,YAAA,IAAgB,KAAA,EAAO,0BAAA,KAA+B,OAAA,CAAQ,qBAAA,IAAyB,qBAAA;AAAA;AAAA,KAG7E,oBAAA;EACV,UAAU;AAAA;AAAA,cAGC,uBAAA;AAAA,cACA,uBAAA;AAAA,KAYD,sBAAA;EAER,IAAA;EACA,cAAA;AAAA;EAGA,IAAA;AAAA;AAAA,iBA+BY,8BAAA,CAA+B,OAAA;EAC7C,KAAA,EAAO,sBAAA;EACP,cAAA,EAAgB,MAAM;EACtB,aAAA;AAAA;AAAA,iBAYc,6BAAA,CAA8B,OAAA;EAC5C,cAAA;EACA,OAAA,EAAS,KAAK;IAAG,IAAA;IAAc,KAAA;EAAA;EAC/B,aAAA;AAAA;AAAA,cAeW,uBAAA,SAAgC,KAAK;cACpC,SAAA;AAAA;AAAA,cAMD,uBAAA,SAAgC,KAAK;cACpC,OAAA;AAAA;AAAA,iBAUE,kBAAA,IAAsB,MAAM;AAAA,iBAOtB,sBAAA,CAAuB,OAAA,EAAS,qBAAA,GAAwB,OAAA,CAAQ,oBAAA;AAAA,iBA0EtE,sBAAA,CAAuB,QAAA,UAAkB,SAAA,WAAoB,GAAA;AAAA,iBAO7D,eAAA,CAAgB,GAAA,UAAa,MAAc"}
@@ -1,9 +1,8 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_chunk = require('./chunk-BE-pF4vm.js');
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_rolldown_runtime = require("./rolldown-runtime-D6vf50IK.js");
3
3
  let _anthropic_ai_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
4
4
  let path = require("path");
5
- path = require_chunk.__toESM(path);
6
-
5
+ path = require_rolldown_runtime.__toESM(path);
7
6
  //#region src/config-agent.ts
8
7
  const ANTHROPIC_PROXY_BASE_URL = process.env.STACK_CLAUDE_PROXY_URL ?? "https://api.hexclave.com/api/latest/integrations/ai-proxy";
9
8
  const CONFIG_AGENT_FILE_TOOLS = [
@@ -121,7 +120,7 @@ async function runHeadlessClaudeAgent(options) {
121
120
  if (!sawResult) throw new Error("Claude agent ended without reporting a result.");
122
121
  return { resultText };
123
122
  }
124
- const FILE_MUTATING_TOOLS = new Set(["Write", "Edit"]);
123
+ const FILE_MUTATING_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit"]);
125
124
  function hasStringFilePath(input) {
126
125
  return typeof input === "object" && input !== null && "file_path" in input && typeof input.file_path === "string";
127
126
  }
@@ -133,7 +132,6 @@ function isPathInsideDir(dir, target) {
133
132
  const relative = path.default.relative(path.default.resolve(dir), path.default.resolve(target));
134
133
  return relative === "" || !relative.startsWith("..") && !path.default.isAbsolute(relative);
135
134
  }
136
-
137
135
  //#endregion
138
136
  exports.CONFIG_AGENT_FILE_TOOLS = CONFIG_AGENT_FILE_TOOLS;
139
137
  exports.CONFIG_AGENT_REPO_TOOLS = CONFIG_AGENT_REPO_TOOLS;
@@ -145,4 +143,5 @@ exports.getToolWriteTargetPath = getToolWriteTargetPath;
145
143
  exports.isPathInsideDir = isPathInsideDir;
146
144
  exports.runHeadlessClaudeAgent = runHeadlessClaudeAgent;
147
145
  exports.stripClaudeCodeEnv = stripClaudeCodeEnv;
146
+
148
147
  //# sourceMappingURL=config-agent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-agent.js","names":["path"],"sources":["../src/config-agent.ts"],"sourcesContent":["import { query } from \"@anthropic-ai/claude-agent-sdk\";\nimport path from \"path\";\n\nconst DEFAULT_PROXY_URL = \"https://api.hexclave.com/api/latest/integrations/ai-proxy\";\nconst ANTHROPIC_PROXY_BASE_URL: string = process.env.STACK_CLAUDE_PROXY_URL ?? DEFAULT_PROXY_URL;\n\nexport type ClaudeAgentToolName = \"Read\" | \"Write\" | \"Edit\" | \"MultiEdit\" | \"NotebookEdit\" | \"Bash\" | \"Glob\" | \"Grep\";\n\ntype ClaudeAgentHookResult = { continue: true } | {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"deny\",\n permissionDecisionReason: string,\n },\n};\n\nexport type ClaudeAgentPreToolUseInput = {\n hook_event_name: \"PreToolUse\",\n tool_name: string,\n tool_input: unknown,\n};\n\nexport type RunClaudeAgentOptions = {\n prompt: string,\n cwd: string,\n allowedTools: ClaudeAgentToolName[],\n timeoutMs?: number,\n strictIsolation?: boolean,\n stderr?: (data: string) => void,\n onMessage?: (message: unknown) => void,\n onPreToolUse?: (input: ClaudeAgentPreToolUseInput) => Promise<ClaudeAgentHookResult> | ClaudeAgentHookResult,\n};\n\nexport type RunClaudeAgentResult = {\n resultText: string,\n};\n\nexport const CONFIG_AGENT_FILE_TOOLS = [\"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\"] as const;\nexport const CONFIG_AGENT_REPO_TOOLS = [...CONFIG_AGENT_FILE_TOOLS, \"Bash\"] as const;\n\ntype ConfigAgentPromptTarget =\n | {\n mode: \"complete\",\n completeConfig: Record<string, unknown>,\n }\n | {\n mode: \"partial\",\n changes: Array<{ path: string, value: unknown }>,\n };\n\nexport type ConfigAgentPromptScope =\n | {\n mode: \"known-file\",\n configFileName: string,\n }\n | {\n mode: \"repo\",\n };\n\nfunction buildConfigAgentPrompt(options: {\n scope: ConfigAgentPromptScope,\n target: ConfigAgentPromptTarget,\n commandPolicy: string,\n}): string {\n const targetSection = options.target.mode === \"complete\"\n ? `The exported config must end up deep-equal to this JSON value:\\n\\n${JSON.stringify(options.target.completeConfig, null, 2)}`\n : `Apply EXACTLY these config changes. Paths use dot notation, so \\`a.b.c\\` refers to \\`config.a.b.c\\`:\\n\\n${options.target.changes.map(({ path, value }) => `- ${JSON.stringify(path)}: set to ${JSON.stringify(value)}`).join(\"\\n\")}`;\n const scopeSection = options.scope.mode === \"known-file\"\n ? `Config file: ${JSON.stringify(options.scope.configFileName)} in the current working directory.`\n : \"Current working directory: the repository root. Find the Hexclave / Stack Auth config file. It is usually a `*.config.ts` file exporting `config`, often wrapped in `defineHexclaveConfig(...)` or a similar helper.\";\n\n return `You are updating a Hexclave / Stack Auth configuration file.\n\n${scopeSection}\n\n${targetSection}\n\nRules:\n- Keep the file valid: it must still export \\`config\\`.\n- Preserve the existing authoring style where possible: imports, comments, helper wrappers, file header comments, and formatting.\n- If the config currently exports the placeholder string \"show-onboarding\" or is otherwise a stub, replace it with a real typed config object.\n- If a config value is conventionally sourced from an imported external file, you may keep that indirection as long as the evaluated config matches the requested value.\n- Do not touch unrelated files or application code.\n- ${options.commandPolicy}\n- Make the edits, then stop.`;\n}\n\nexport function buildCompleteConfigAgentPrompt(options: {\n scope: ConfigAgentPromptScope,\n completeConfig: Record<string, unknown>,\n commandPolicy: string,\n}): string {\n return buildConfigAgentPrompt({\n scope: options.scope,\n target: {\n mode: \"complete\",\n completeConfig: options.completeConfig,\n },\n commandPolicy: options.commandPolicy,\n });\n}\n\nexport function buildPartialConfigAgentPrompt(options: {\n configFileName: string,\n changes: Array<{ path: string, value: unknown }>,\n commandPolicy: string,\n}): string {\n return buildConfigAgentPrompt({\n scope: {\n mode: \"known-file\",\n configFileName: options.configFileName,\n },\n target: {\n mode: \"partial\",\n changes: options.changes,\n },\n commandPolicy: options.commandPolicy,\n });\n}\n\nexport class ClaudeAgentTimeoutError extends Error {\n constructor(timeoutMs?: number) {\n super(`Claude agent timed out${timeoutMs == null ? \"\" : ` after ${timeoutMs}ms`}.`);\n this.name = \"ClaudeAgentTimeoutError\";\n }\n}\n\nexport class ClaudeAgentFailureError extends Error {\n constructor(subtype: string) {\n super(`Claude agent failed (${subtype}).`);\n this.name = \"ClaudeAgentFailureError\";\n }\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === \"AbortError\";\n}\n\nexport function stripClaudeCodeEnv(): Record<string, string | undefined> {\n const env = { ...process.env };\n // CLAUDECODE must be unset for nested agents; ANTHROPIC_API_KEY must be non-empty (proxy ignores it).\n delete env.CLAUDECODE;\n return env;\n}\n\nexport async function runHeadlessClaudeAgent(options: RunClaudeAgentOptions): Promise<RunClaudeAgentResult> {\n const abortController = new AbortController();\n const timeout = options.timeoutMs == null ? null : setTimeout(() => abortController.abort(), options.timeoutMs);\n let sawResult = false;\n let resultText = \"\";\n const onPreToolUse = options.onPreToolUse;\n try {\n for await (const message of query({\n prompt: options.prompt,\n options: {\n model: \"anthropic/claude-haiku-4.5\",\n ...(options.strictIsolation === true ? {\n settingSources: [],\n strictMcpConfig: true,\n } : {}),\n ...(onPreToolUse == null ? {} : {\n hooks: {\n PreToolUse: [{\n hooks: [async (input) => {\n if (input.hook_event_name !== \"PreToolUse\") {\n return { continue: true };\n }\n return await onPreToolUse(input);\n }],\n }],\n },\n }),\n allowedTools: options.allowedTools,\n permissionMode: \"dontAsk\",\n cwd: options.cwd,\n abortController,\n env: {\n ...stripClaudeCodeEnv(),\n ...(options.strictIsolation === true ? { CLAUDE_CODE_DISABLE_AUTO_MEMORY: \"1\" } : {}),\n ANTHROPIC_BASE_URL: ANTHROPIC_PROXY_BASE_URL,\n ANTHROPIC_API_KEY: \"stack-auth-proxy\",\n },\n stderr: options.stderr,\n },\n })) {\n options.onMessage?.(message);\n if (message.type === \"result\") {\n if (\"result\" in message) {\n sawResult = true;\n resultText = message.result;\n } else {\n throw new ClaudeAgentFailureError(message.subtype);\n }\n }\n }\n } catch (error) {\n if (abortController.signal.aborted && isAbortError(error)) {\n throw new ClaudeAgentTimeoutError(options.timeoutMs ?? undefined);\n }\n throw error;\n } finally {\n if (timeout != null) {\n clearTimeout(timeout);\n }\n }\n if (!sawResult) {\n throw new Error(\"Claude agent ended without reporting a result.\");\n }\n return { resultText };\n}\n\n// Only the tools actually in the agent's allowedTools list; MultiEdit and\n// NotebookEdit are intentionally excluded from the agent's tool set.\nconst FILE_MUTATING_TOOLS = new Set([\"Write\", \"Edit\"]);\n\nfunction hasStringFilePath(input: unknown): input is { file_path: string } {\n return typeof input === \"object\" && input !== null && \"file_path\" in input && typeof input.file_path === \"string\";\n}\n\nexport function getToolWriteTargetPath(toolName: string, toolInput: unknown, cwd: string): string | null {\n if (!FILE_MUTATING_TOOLS.has(toolName) || !hasStringFilePath(toolInput)) {\n return null;\n }\n return path.isAbsolute(toolInput.file_path) ? toolInput.file_path : path.resolve(cwd, toolInput.file_path);\n}\n\nexport function isPathInsideDir(dir: string, target: string): boolean {\n const relative = path.relative(path.resolve(dir), path.resolve(target));\n return relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config-agent.js","names":["path"],"sources":["../src/config-agent.ts"],"sourcesContent":["import { query } from \"@anthropic-ai/claude-agent-sdk\";\nimport path from \"path\";\n\nconst DEFAULT_PROXY_URL = \"https://api.hexclave.com/api/latest/integrations/ai-proxy\";\nconst ANTHROPIC_PROXY_BASE_URL: string = process.env.STACK_CLAUDE_PROXY_URL ?? DEFAULT_PROXY_URL;\n\nexport type ClaudeAgentToolName = \"Read\" | \"Write\" | \"Edit\" | \"MultiEdit\" | \"NotebookEdit\" | \"Bash\" | \"Glob\" | \"Grep\";\n\ntype ClaudeAgentHookResult = { continue: true } | {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"deny\",\n permissionDecisionReason: string,\n },\n};\n\nexport type ClaudeAgentPreToolUseInput = {\n hook_event_name: \"PreToolUse\",\n tool_name: string,\n tool_input: unknown,\n};\n\nexport type RunClaudeAgentOptions = {\n prompt: string,\n cwd: string,\n allowedTools: ClaudeAgentToolName[],\n timeoutMs?: number,\n strictIsolation?: boolean,\n stderr?: (data: string) => void,\n onMessage?: (message: unknown) => void,\n onPreToolUse?: (input: ClaudeAgentPreToolUseInput) => Promise<ClaudeAgentHookResult> | ClaudeAgentHookResult,\n};\n\nexport type RunClaudeAgentResult = {\n resultText: string,\n};\n\nexport const CONFIG_AGENT_FILE_TOOLS = [\"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\"] as const;\nexport const CONFIG_AGENT_REPO_TOOLS = [...CONFIG_AGENT_FILE_TOOLS, \"Bash\"] as const;\n\ntype ConfigAgentPromptTarget =\n | {\n mode: \"complete\",\n completeConfig: Record<string, unknown>,\n }\n | {\n mode: \"partial\",\n changes: Array<{ path: string, value: unknown }>,\n };\n\nexport type ConfigAgentPromptScope =\n | {\n mode: \"known-file\",\n configFileName: string,\n }\n | {\n mode: \"repo\",\n };\n\nfunction buildConfigAgentPrompt(options: {\n scope: ConfigAgentPromptScope,\n target: ConfigAgentPromptTarget,\n commandPolicy: string,\n}): string {\n const targetSection = options.target.mode === \"complete\"\n ? `The exported config must end up deep-equal to this JSON value:\\n\\n${JSON.stringify(options.target.completeConfig, null, 2)}`\n : `Apply EXACTLY these config changes. Paths use dot notation, so \\`a.b.c\\` refers to \\`config.a.b.c\\`:\\n\\n${options.target.changes.map(({ path, value }) => `- ${JSON.stringify(path)}: set to ${JSON.stringify(value)}`).join(\"\\n\")}`;\n const scopeSection = options.scope.mode === \"known-file\"\n ? `Config file: ${JSON.stringify(options.scope.configFileName)} in the current working directory.`\n : \"Current working directory: the repository root. Find the Hexclave / Stack Auth config file. It is usually a `*.config.ts` file exporting `config`, often wrapped in `defineHexclaveConfig(...)` or a similar helper.\";\n\n return `You are updating a Hexclave / Stack Auth configuration file.\n\n${scopeSection}\n\n${targetSection}\n\nRules:\n- Keep the file valid: it must still export \\`config\\`.\n- Preserve the existing authoring style where possible: imports, comments, helper wrappers, file header comments, and formatting.\n- If the config currently exports the placeholder string \"show-onboarding\" or is otherwise a stub, replace it with a real typed config object.\n- If a config value is conventionally sourced from an imported external file, you may keep that indirection as long as the evaluated config matches the requested value.\n- Do not touch unrelated files or application code.\n- ${options.commandPolicy}\n- Make the edits, then stop.`;\n}\n\nexport function buildCompleteConfigAgentPrompt(options: {\n scope: ConfigAgentPromptScope,\n completeConfig: Record<string, unknown>,\n commandPolicy: string,\n}): string {\n return buildConfigAgentPrompt({\n scope: options.scope,\n target: {\n mode: \"complete\",\n completeConfig: options.completeConfig,\n },\n commandPolicy: options.commandPolicy,\n });\n}\n\nexport function buildPartialConfigAgentPrompt(options: {\n configFileName: string,\n changes: Array<{ path: string, value: unknown }>,\n commandPolicy: string,\n}): string {\n return buildConfigAgentPrompt({\n scope: {\n mode: \"known-file\",\n configFileName: options.configFileName,\n },\n target: {\n mode: \"partial\",\n changes: options.changes,\n },\n commandPolicy: options.commandPolicy,\n });\n}\n\nexport class ClaudeAgentTimeoutError extends Error {\n constructor(timeoutMs?: number) {\n super(`Claude agent timed out${timeoutMs == null ? \"\" : ` after ${timeoutMs}ms`}.`);\n this.name = \"ClaudeAgentTimeoutError\";\n }\n}\n\nexport class ClaudeAgentFailureError extends Error {\n constructor(subtype: string) {\n super(`Claude agent failed (${subtype}).`);\n this.name = \"ClaudeAgentFailureError\";\n }\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === \"AbortError\";\n}\n\nexport function stripClaudeCodeEnv(): Record<string, string | undefined> {\n const env = { ...process.env };\n // CLAUDECODE must be unset for nested agents; ANTHROPIC_API_KEY must be non-empty (proxy ignores it).\n delete env.CLAUDECODE;\n return env;\n}\n\nexport async function runHeadlessClaudeAgent(options: RunClaudeAgentOptions): Promise<RunClaudeAgentResult> {\n const abortController = new AbortController();\n const timeout = options.timeoutMs == null ? null : setTimeout(() => abortController.abort(), options.timeoutMs);\n let sawResult = false;\n let resultText = \"\";\n const onPreToolUse = options.onPreToolUse;\n try {\n for await (const message of query({\n prompt: options.prompt,\n options: {\n model: \"anthropic/claude-haiku-4.5\",\n ...(options.strictIsolation === true ? {\n settingSources: [],\n strictMcpConfig: true,\n } : {}),\n ...(onPreToolUse == null ? {} : {\n hooks: {\n PreToolUse: [{\n hooks: [async (input) => {\n if (input.hook_event_name !== \"PreToolUse\") {\n return { continue: true };\n }\n return await onPreToolUse(input);\n }],\n }],\n },\n }),\n allowedTools: options.allowedTools,\n permissionMode: \"dontAsk\",\n cwd: options.cwd,\n abortController,\n env: {\n ...stripClaudeCodeEnv(),\n ...(options.strictIsolation === true ? { CLAUDE_CODE_DISABLE_AUTO_MEMORY: \"1\" } : {}),\n ANTHROPIC_BASE_URL: ANTHROPIC_PROXY_BASE_URL,\n ANTHROPIC_API_KEY: \"stack-auth-proxy\",\n },\n stderr: options.stderr,\n },\n })) {\n options.onMessage?.(message);\n if (message.type === \"result\") {\n if (\"result\" in message) {\n sawResult = true;\n resultText = message.result;\n } else {\n throw new ClaudeAgentFailureError(message.subtype);\n }\n }\n }\n } catch (error) {\n if (abortController.signal.aborted && isAbortError(error)) {\n throw new ClaudeAgentTimeoutError(options.timeoutMs ?? undefined);\n }\n throw error;\n } finally {\n if (timeout != null) {\n clearTimeout(timeout);\n }\n }\n if (!sawResult) {\n throw new Error(\"Claude agent ended without reporting a result.\");\n }\n return { resultText };\n}\n\n// Only the tools actually in the agent's allowedTools list; MultiEdit and\n// NotebookEdit are intentionally excluded from the agent's tool set.\nconst FILE_MUTATING_TOOLS = new Set([\"Write\", \"Edit\"]);\n\nfunction hasStringFilePath(input: unknown): input is { file_path: string } {\n return typeof input === \"object\" && input !== null && \"file_path\" in input && typeof input.file_path === \"string\";\n}\n\nexport function getToolWriteTargetPath(toolName: string, toolInput: unknown, cwd: string): string | null {\n if (!FILE_MUTATING_TOOLS.has(toolName) || !hasStringFilePath(toolInput)) {\n return null;\n }\n return path.isAbsolute(toolInput.file_path) ? toolInput.file_path : path.resolve(cwd, toolInput.file_path);\n}\n\nexport function isPathInsideDir(dir: string, target: string): boolean {\n const relative = path.relative(path.resolve(dir), path.resolve(target));\n return relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative));\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config-file.d.ts","names":[],"sources":["../src/config-file.ts"],"mappings":";;;iBAUgB,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,iBAuClE,mBAAA,CAAoB,cAAA,UAAwB,MAAA,EAAQ,MAAA,GAAS,OAAA"}
1
+ {"version":3,"file":"config-file.d.ts","names":[],"sources":["../src/config-file.ts"],"mappings":";;;iBAUgB,YAAA,CAAa,KAAa;AAAA,iBAI1B,qBAAA,CAAsB,SAAiB;AAAA,iBAkBvC,sBAAA,CAAuB,cAAsB;AAAA,iBAMvC,gBAAA,CAAiB,cAAA,WAAyB,OAAO,CAAC,MAAA;AAAA,iBAIlD,cAAA,CAAe,cAAA,WAAyB,OAAO;EAAG,MAAA,EAAQ,MAAA;EAAQ,cAAA;AAAA;AAAA,iBAuClE,mBAAA,CAAoB,cAAA,UAAwB,MAAA,EAAQ,MAAA,GAAS,OAAO"}
@@ -1,7 +1,7 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_chunk = require('./chunk-BE-pF4vm.js');
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_rolldown_runtime = require("./rolldown-runtime-D6vf50IK.js");
3
3
  let path = require("path");
4
- path = require_chunk.__toESM(path);
4
+ path = require_rolldown_runtime.__toESM(path);
5
5
  let _hexclave_shared_dist_config_authoring = require("@hexclave/shared/dist/config-authoring");
6
6
  let _hexclave_shared_dist_config_eval = require("@hexclave/shared/dist/config-eval");
7
7
  let _hexclave_shared_dist_config_rendering = require("@hexclave/shared/dist/config-rendering");
@@ -9,7 +9,6 @@ let _hexclave_shared_dist_config_format = require("@hexclave/shared/dist/config/
9
9
  let _hexclave_shared_dist_utils_errors = require("@hexclave/shared/dist/utils/errors");
10
10
  let crypto = require("crypto");
11
11
  let fs = require("fs");
12
-
13
12
  //#region src/config-file.ts
14
13
  function sha256String(value) {
15
14
  return (0, crypto.createHash)("sha256").update(value).digest("hex");
@@ -79,7 +78,6 @@ function writeFileAtomic(configFilePath, content) {
79
78
  function renderConfigObjectToFile(configFilePath, config) {
80
79
  writeFileAtomic(configFilePath, renderConfigObjectToString(configFilePath, config));
81
80
  }
82
-
83
81
  //#endregion
84
82
  exports.ensureConfigFileExists = ensureConfigFileExists;
85
83
  exports.readConfigFile = readConfigFile;
@@ -87,4 +85,5 @@ exports.readConfigObject = readConfigObject;
87
85
  exports.replaceConfigObject = replaceConfigObject;
88
86
  exports.resolveConfigFilePath = resolveConfigFilePath;
89
87
  exports.sha256String = sha256String;
88
+
90
89
  //# sourceMappingURL=config-file.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-file.js","names":["ConfigFileEvalError","showOnboardingHexclaveConfigValue"],"sources":["../src/config-file.ts"],"sourcesContent":["import { showOnboardingHexclaveConfigValue } from \"@hexclave/shared/dist/config-authoring\";\nimport { ConfigFileEvalError, detectImportPackageFromDir, evalConfigFileContent } from \"@hexclave/shared/dist/config-eval\";\nimport { renderConfigFileContent } from \"@hexclave/shared/dist/config-rendering\";\nimport type { Config } from \"@hexclave/shared/dist/config/format\";\nimport { isValidConfig } 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 path from \"path\";\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 // ConfigFileEvalError => \"Invalid config\"; any other loader error is captured\n // for diagnostics but not attached as `cause` (the dashboard renders causes\n // recursively and would leak framework internals).\n let parsed: ReturnType<typeof evalConfigFileContent>;\n try {\n parsed = evalConfigFileContent(content, configFilePath);\n } catch (error) {\n if (error instanceof ConfigFileEvalError) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n captureError(\"shared-backend/readConfigFile\", error);\n throw new Error(`Failed to load config file ${configFilePath}.`);\n }\n\n if (parsed === showOnboardingHexclaveConfigValue) {\n return {\n config: {},\n showOnboarding: true,\n };\n }\n if (!isValidConfig(parsed)) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n return {\n config: parsed,\n showOnboarding: false,\n };\n}\n\nexport async function replaceConfigObject(configFilePath: string, config: Config): Promise<void> {\n renderConfigObjectToFile(configFilePath, config);\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"],"mappings":";;;;;;;;;;;;;AAUA,SAAgB,aAAa,OAAuB;AAClD,+BAAkB,SAAS,CAAC,OAAO,MAAM,CAAC,OAAO,MAAM;;AAGzD,SAAgB,sBAAsB,WAA2B;CAC/D,MAAM,WAAW,aAAK,QAAQ,UAAU;AAExC,KAD4B,sBAAsB,KAAK,SAAS,CAE9D,QAAO;CAGT,MAAM,oBAAoB,aAAK,KAAK,UAAU,qBAAqB;CACnE,MAAM,kBAAkB,aAAK,KAAK,UAAU,kBAAkB;AAC9D,wBAAe,kBAAkB,CAC/B,QAAO;AAET,wBAAe,gBAAgB,CAC7B,QAAO;AAET,QAAO;;AAGT,SAAgB,uBAAuB,gBAA8B;AACnE,wBAAe,eAAe,CAAE;AAChC,mBAAU,aAAK,QAAQ,eAAe,EAAE,EAAE,WAAW,MAAM,CAAC;AAC5D,0BAAyB,gBAAgB,EAAE,CAAC;;AAG9C,eAAsB,iBAAiB,gBAAyC;AAC9E,SAAQ,MAAM,eAAe,eAAe,EAAE;;AAGhD,eAAsB,eAAe,gBAA8E;AACjH,wBAAuB,eAAe;CACtC,MAAM,+BAAuB,gBAAgB,QAAQ;AACrD,KAAI,QAAQ,MAAM,KAAK,GACrB,QAAO;EACL,QAAQ,EAAE;EACV,gBAAgB;EACjB;CAMH,IAAI;AACJ,KAAI;AACF,wEAA+B,SAAS,eAAe;UAChD,OAAO;AACd,MAAI,iBAAiBA,sDACnB,OAAM,IAAI,MAAM,qBAAqB,eAAe,GAAG;AAEzD,uDAAa,iCAAiC,MAAM;AACpD,QAAM,IAAI,MAAM,8BAA8B,eAAe,GAAG;;AAGlE,KAAI,WAAWC,yEACb,QAAO;EACL,QAAQ,EAAE;EACV,gBAAgB;EACjB;AAEH,KAAI,wDAAe,OAAO,CACxB,OAAM,IAAI,MAAM,qBAAqB,eAAe,GAAG;AAEzD,QAAO;EACL,QAAQ;EACR,gBAAgB;EACjB;;AAGH,eAAsB,oBAAoB,gBAAwB,QAA+B;AAC/F,0BAAyB,gBAAgB,OAAO;;AAGlD,SAAS,2BAA2B,gBAAwB,QAAwB;AAElF,4EAA+B,0EADkB,aAAK,QAAQ,eAAe,CAAC,CACzB;;AAGvD,SAAS,gBAAgB,gBAAwB,SAAuB;CACtE,MAAM,MAAM,aAAK,QAAQ,eAAe;AACxC,mBAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CACnC,MAAM,WAAW,aAAK,KAAK,KAAK,iBAAiB,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,MAAM;AAC3F,uBAAc,UAAU,SAAS,QAAQ;AACzC,KAAI;AACF,qBAAW,UAAU,eAAe;UAC7B,OAAO;AACd,MAAI;AACF,kBAAO,SAAS;UACV;AACR,QAAM;;;AAIV,SAAS,yBAAyB,gBAAwB,QAAsB;AAC9E,iBAAgB,gBAAgB,2BAA2B,gBAAgB,OAAO,CAAC"}
1
+ {"version":3,"file":"config-file.js","names":["ConfigFileEvalError","showOnboardingHexclaveConfigValue"],"sources":["../src/config-file.ts"],"sourcesContent":["import { showOnboardingHexclaveConfigValue } from \"@hexclave/shared/dist/config-authoring\";\nimport { ConfigFileEvalError, detectImportPackageFromDir, evalConfigFileContent } from \"@hexclave/shared/dist/config-eval\";\nimport { renderConfigFileContent } from \"@hexclave/shared/dist/config-rendering\";\nimport type { Config } from \"@hexclave/shared/dist/config/format\";\nimport { isValidConfig } 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 path from \"path\";\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 // ConfigFileEvalError => \"Invalid config\"; any other loader error is captured\n // for diagnostics but not attached as `cause` (the dashboard renders causes\n // recursively and would leak framework internals).\n let parsed: ReturnType<typeof evalConfigFileContent>;\n try {\n parsed = evalConfigFileContent(content, configFilePath);\n } catch (error) {\n if (error instanceof ConfigFileEvalError) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n captureError(\"shared-backend/readConfigFile\", error);\n throw new Error(`Failed to load config file ${configFilePath}.`);\n }\n\n if (parsed === showOnboardingHexclaveConfigValue) {\n return {\n config: {},\n showOnboarding: true,\n };\n }\n if (!isValidConfig(parsed)) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n return {\n config: parsed,\n showOnboarding: false,\n };\n}\n\nexport async function replaceConfigObject(configFilePath: string, config: Config): Promise<void> {\n renderConfigObjectToFile(configFilePath, config);\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"],"mappings":";;;;;;;;;;;;AAUA,SAAgB,aAAa,OAAuB;CAClD,QAAA,GAAA,OAAA,WAAA,CAAkB,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;AACxD;AAEA,SAAgB,sBAAsB,WAA2B;CAC/D,MAAM,WAAW,KAAA,QAAK,QAAQ,SAAS;CAEvC,IAD4B,sBAAsB,KAAK,QACjC,GACpB,OAAO;CAGT,MAAM,oBAAoB,KAAA,QAAK,KAAK,UAAU,oBAAoB;CAClE,MAAM,kBAAkB,KAAA,QAAK,KAAK,UAAU,iBAAiB;CAC7D,KAAA,GAAA,GAAA,WAAA,CAAe,iBAAiB,GAC9B,OAAO;CAET,KAAA,GAAA,GAAA,WAAA,CAAe,eAAe,GAC5B,OAAO;CAET,OAAO;AACT;AAEA,SAAgB,uBAAuB,gBAA8B;CACnE,KAAA,GAAA,GAAA,WAAA,CAAe,cAAc,GAAG;CAChC,CAAA,GAAA,GAAA,UAAA,CAAU,KAAA,QAAK,QAAQ,cAAc,GAAG,EAAE,WAAW,KAAK,CAAC;CAC3D,yBAAyB,gBAAgB,CAAC,CAAC;AAC7C;AAEA,eAAsB,iBAAiB,gBAAyC;CAC9E,QAAQ,MAAM,eAAe,cAAc,EAAA,CAAG;AAChD;AAEA,eAAsB,eAAe,gBAA8E;CACjH,uBAAuB,cAAc;CACrC,MAAM,WAAA,GAAA,GAAA,aAAA,CAAuB,gBAAgB,OAAO;CACpD,IAAI,QAAQ,KAAK,MAAM,IACrB,OAAO;EACL,QAAQ,CAAC;EACT,gBAAgB;CAClB;CAMF,IAAI;CACJ,IAAI;EACF,UAAA,GAAA,kCAAA,sBAAA,CAA+B,SAAS,cAAc;CACxD,SAAS,OAAO;EACd,IAAI,iBAAiBA,kCAAAA,qBACnB,MAAM,IAAI,MAAM,qBAAqB,eAAe,EAAE;EAExD,CAAA,GAAA,mCAAA,aAAA,CAAa,iCAAiC,KAAK;EACnD,MAAM,IAAI,MAAM,8BAA8B,eAAe,EAAE;CACjE;CAEA,IAAI,WAAWC,uCAAAA,mCACb,OAAO;EACL,QAAQ,CAAC;EACT,gBAAgB;CAClB;CAEF,IAAI,EAAA,GAAA,oCAAA,cAAA,CAAe,MAAM,GACvB,MAAM,IAAI,MAAM,qBAAqB,eAAe,EAAE;CAExD,OAAO;EACL,QAAQ;EACR,gBAAgB;CAClB;AACF;AAEA,eAAsB,oBAAoB,gBAAwB,QAA+B;CAC/F,yBAAyB,gBAAgB,MAAM;AACjD;AAEA,SAAS,2BAA2B,gBAAwB,QAAwB;CAElF,QAAA,GAAA,uCAAA,wBAAA,CAA+B,SAAA,GAAA,kCAAA,2BAAA,CADkB,KAAA,QAAK,QAAQ,cAAc,CACzB,CAAC;AACtD;AAEA,SAAS,gBAAgB,gBAAwB,SAAuB;CACtE,MAAM,MAAM,KAAA,QAAK,QAAQ,cAAc;CACvC,CAAA,GAAA,GAAA,UAAA,CAAU,KAAK,EAAE,WAAW,KAAK,CAAC;CAClC,MAAM,WAAW,KAAA,QAAK,KAAK,KAAK,iBAAiB,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK;CAC1F,CAAA,GAAA,GAAA,cAAA,CAAc,UAAU,SAAS,OAAO;CACxC,IAAI;EACF,CAAA,GAAA,GAAA,WAAA,CAAW,UAAU,cAAc;CACrC,SAAS,OAAO;EACd,IAAI;GACF,CAAA,GAAA,GAAA,OAAA,CAAO,QAAQ;EACjB,QAAQ,CAA4B;EACpC,MAAM;CACR;AACF;AAEA,SAAS,yBAAyB,gBAAwB,QAAsB;CAC9E,gBAAgB,gBAAgB,2BAA2B,gBAAgB,MAAM,CAAC;AACpF"}
@@ -1 +1 @@
1
- {"version":3,"file":"config-updater.d.ts","names":[],"sources":["../src/config-updater.ts"],"mappings":";;;iBA+DsB,kBAAA,CAAmB,cAAA,UAAwB,YAAA,EAAc,MAAA,GAAS,OAAA"}
1
+ {"version":3,"file":"config-updater.d.ts","names":[],"sources":["../src/config-updater.ts"],"mappings":";;;iBA+DsB,kBAAA,CAAmB,cAAA,UAAwB,YAAA,EAAc,MAAA,GAAS,OAAO"}
@@ -1,13 +1,12 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_chunk = require('./chunk-BE-pF4vm.js');
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_rolldown_runtime = require("./rolldown-runtime-D6vf50IK.js");
3
3
  let __config_agent_js = require("./config-agent.js");
4
4
  let path = require("path");
5
- path = require_chunk.__toESM(path);
5
+ path = require_rolldown_runtime.__toESM(path);
6
6
  let _hexclave_shared_dist_config_eval = require("@hexclave/shared/dist/config-eval");
7
7
  let _hexclave_shared_dist_config_format = require("@hexclave/shared/dist/config/format");
8
8
  let fs = require("fs");
9
9
  let __config_file_js = require("./config-file.js");
10
-
11
10
  //#region src/config-updater.ts
12
11
  const LOG_PREFIX = "[Hexclave config updater]";
13
12
  const DEFAULT_AGENT_TIMEOUT_MS = 12e4;
@@ -210,7 +209,7 @@ function snapshotConfigFiles(configFilePath, configContent) {
210
209
  path: resolvedConfig,
211
210
  content: configContent
212
211
  }];
213
- const seen = new Set([resolvedConfig]);
212
+ const seen = /* @__PURE__ */ new Set([resolvedConfig]);
214
213
  for (const specifier of getRelativeImportSpecifiers(configContent)) {
215
214
  const resolved = path.default.resolve(dir, specifier);
216
215
  if (!(0, __config_agent_js.isPathInsideDir)(dir, resolved)) continue;
@@ -324,7 +323,7 @@ function configsEqual(a, b) {
324
323
  }
325
324
  return false;
326
325
  }
327
-
328
326
  //#endregion
329
327
  exports.updateConfigObject = updateConfigObject;
328
+
330
329
  //# sourceMappingURL=config-updater.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-updater.js","names":["CONFIG_AGENT_FILE_TOOLS","ClaudeAgentTimeoutError","ClaudeAgentFailureError"],"sources":["../src/config-updater.ts"],"sourcesContent":["import { evalConfigFileContent } from \"@hexclave/shared/dist/config-eval\";\nimport type { Config, ConfigValue, NormalizedConfig } from \"@hexclave/shared/dist/config/format\";\nimport { normalize, override } from \"@hexclave/shared/dist/config/format\";\nimport { existsSync, readFileSync, rmSync, writeFileSync } from \"fs\";\nimport path from \"path\";\nimport { buildCompleteConfigAgentPrompt, buildPartialConfigAgentPrompt, ClaudeAgentFailureError, ClaudeAgentTimeoutError, CONFIG_AGENT_FILE_TOOLS, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from \"./config-agent\";\nimport { ensureConfigFileExists, readConfigFile } from \"./config-file\";\n\nconst LOG_PREFIX = \"[Hexclave config updater]\";\nconst DEFAULT_AGENT_TIMEOUT_MS = 120_000;\nconst CONFIG_UPDATE_LOG_PATH_LIMIT = 40;\nconst AGENT_OUTPUT_LOG_MAX_LENGTH = 20_000;\n\ntype ConfigFileSnapshot = { path: string, content: string | null };\ntype ConfigChange = { path: string, value: ConfigValue };\n\nfunction formatConfigUpdaterErrorForLog(error: unknown): Record<string, unknown> {\n if (error instanceof Error) {\n return {\n errorName: error.name,\n errorMessage: error.message,\n errorStack: error.stack,\n };\n }\n return {\n errorMessage: String(error),\n };\n}\n\nfunction configUpdatePathDetailsForLog(changes: ConfigChange[]): Record<string, unknown> {\n const paths = changes.map(({ path: configPath }) => configPath).sort();\n return {\n configUpdatePathCount: paths.length,\n configUpdatePaths: paths.slice(0, CONFIG_UPDATE_LOG_PATH_LIMIT),\n configUpdatePathsTruncated: paths.length > CONFIG_UPDATE_LOG_PATH_LIMIT,\n };\n}\n\nfunction appendBoundedAgentOutput(current: string, chunk: string): string {\n const next = `${current}${chunk}`;\n if (next.length <= AGENT_OUTPUT_LOG_MAX_LENGTH) {\n return next;\n }\n return next.slice(next.length - AGENT_OUTPUT_LOG_MAX_LENGTH);\n}\n\nfunction stringifyAgentMessageForLog(message: unknown): string {\n try {\n return `${JSON.stringify(message)}\\n`;\n } catch {\n return `${String(message)}\\n`;\n }\n}\n\nfunction agentOutputDetailsForLog(agentStdout: string, agentStderr: string): Record<string, unknown> {\n return {\n agentStdout,\n agentStdoutTruncated: agentStdout.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,\n agentStderr,\n agentStderrTruncated: agentStderr.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,\n };\n}\n\nexport async function updateConfigObject(configFilePath: string, configUpdate: Config): Promise<void> {\n const startedAtMs = performance.now();\n ensureConfigFileExists(configFilePath);\n\n const changes = flattenConfigUpdate(configUpdate);\n if (changes.length === 0) {\n console.log(`${LOG_PREFIX} Skipping config update because it contains no changes`, {\n configFilePath,\n });\n return;\n }\n const updateLogDetails = {\n configFilePath,\n ...configUpdatePathDetailsForLog(changes),\n };\n console.log(`${LOG_PREFIX} Starting config file update`, updateLogDetails);\n\n const content = readFileSync(configFilePath, \"utf-8\");\n\n // One write path, always: hand the change to the AI agent so it edits the file\n // in place and preserves its authoring (helper wrappers, imports, comments,\n // layout). There is deliberately no deterministic \"fast path\" — re-rendering a\n // config would flatten and destroy hand-authored files. Reads use jiti\n // (see readConfigFile); writes go through the agent.\n console.log(`${LOG_PREFIX} Applying config update with agent-assisted rewrite`, {\n ...updateLogDetails,\n configDirectory: path.dirname(configFilePath),\n });\n const baselineConfig = await tryReadConfigForValidation(configFilePath);\n const { snapshots, seen } = snapshotConfigFiles(configFilePath, content);\n try {\n await runConfigUpdateAgent({\n prompt: buildConfigUpdatePrompt(path.basename(configFilePath), configUpdate, baselineConfig),\n cwd: path.dirname(configFilePath),\n onFileWillChange: (filePath) => captureSnapshotIfAbsent(snapshots, filePath, seen),\n });\n await validateAgentUpdate(configFilePath, baselineConfig, configUpdate);\n } catch (error) {\n console.warn(`${LOG_PREFIX} Config update failed; restoring files from snapshots`, {\n ...updateLogDetails,\n snapshotCount: snapshots.length,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n });\n try {\n restoreConfigFiles(snapshots);\n console.warn(`${LOG_PREFIX} Restored files after failed config update`, {\n ...updateLogDetails,\n snapshotCount: snapshots.length,\n });\n } catch (restoreError) {\n console.error(`${LOG_PREFIX} Failed to fully roll back config files after a failed update of ${configFilePath}; some files may be left in a partially-restored state`, {\n configFilePath,\n ...formatConfigUpdaterErrorForLog(restoreError),\n });\n }\n throw error;\n }\n console.log(`${LOG_PREFIX} Finished config update with agent-assisted rewrite`, {\n ...updateLogDetails,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n snapshotCount: snapshots.length,\n });\n}\n\nasync function runConfigUpdateAgent(options: {\n prompt: string,\n cwd: string,\n onFileWillChange?: (filePath: string) => void,\n}): Promise<void> {\n const timeoutMs = parseAgentTimeoutMs();\n const deniedOutOfBoundsWrites = new Set<string>();\n const startedAtMs = performance.now();\n let agentStdout = \"\";\n let agentStderr = \"\";\n console.log(`${LOG_PREFIX} Starting config update agent`, {\n cwd: options.cwd,\n timeoutMs,\n });\n try {\n await runHeadlessClaudeAgent({\n prompt: options.prompt,\n cwd: options.cwd,\n allowedTools: [...CONFIG_AGENT_FILE_TOOLS],\n strictIsolation: true,\n timeoutMs,\n stderr: (data) => {\n agentStderr = appendBoundedAgentOutput(agentStderr, data);\n console.warn(`${LOG_PREFIX} [agent] ${data}`);\n },\n onMessage: (message) => {\n agentStdout = appendBoundedAgentOutput(agentStdout, stringifyAgentMessageForLog(message));\n },\n onPreToolUse: (input) => {\n const target = getToolWriteTargetPath(input.tool_name, input.tool_input, options.cwd);\n if (target == null) return { continue: true };\n if (!isPathInsideDir(options.cwd, target)) {\n deniedOutOfBoundsWrites.add(target);\n return {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"deny\",\n permissionDecisionReason: `Refusing to modify ${target}: config updates may only change files inside the config directory.`,\n },\n };\n }\n options.onFileWillChange?.(target);\n return { continue: true };\n },\n });\n } catch (error) {\n if (error instanceof ClaudeAgentTimeoutError) {\n console.warn(`${LOG_PREFIX} Config update agent timed out`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw new Error(`Config update agent timed out after ${timeoutMs}ms. It was unable to apply the config changes to the file.`);\n }\n if (error instanceof ClaudeAgentFailureError) {\n console.warn(`${LOG_PREFIX} Config update agent failed`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw new Error(`${error.message} It was unable to apply the config changes to the file.`);\n }\n console.warn(`${LOG_PREFIX} Config update agent failed unexpectedly`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw error;\n }\n console.log(`${LOG_PREFIX} Finished config update agent`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,\n });\n if (deniedOutOfBoundsWrites.size > 0) {\n console.warn(`${LOG_PREFIX} Config update agent attempted out-of-bounds writes`, {\n cwd: options.cwd,\n deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,\n deniedOutOfBoundsWrites: [...deniedOutOfBoundsWrites],\n });\n throw new Error(`Config update agent tried to modify ${deniedOutOfBoundsWrites.size} file(s) outside the config directory, which is not allowed: ${[...deniedOutOfBoundsWrites].join(\", \")}. The config was not updated.`);\n }\n}\n\nfunction parseAgentTimeoutMs(): number {\n const raw = process.env.STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS;\n if (raw == null || raw.trim() === \"\") return DEFAULT_AGENT_TIMEOUT_MS;\n const parsed = Number(raw);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new Error(`Invalid STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS: ${JSON.stringify(raw)}. Expected a positive number of milliseconds.`);\n }\n return parsed;\n}\n\nfunction captureSnapshotIfAbsent(snapshots: ConfigFileSnapshot[], filePath: string, seen: Set<string>): void {\n const resolved = path.resolve(filePath);\n if (seen.has(resolved)) return;\n seen.add(resolved);\n snapshots.push({ path: resolved, content: existsSync(resolved) ? readFileSync(resolved, \"utf-8\") : null });\n}\n\nfunction snapshotConfigFiles(configFilePath: string, configContent: string): { snapshots: ConfigFileSnapshot[]; seen: Set<string> } {\n const dir = path.dirname(configFilePath);\n const resolvedConfig = path.resolve(configFilePath);\n const snapshots: ConfigFileSnapshot[] = [{ path: resolvedConfig, content: configContent }];\n const seen = new Set<string>([resolvedConfig]);\n for (const specifier of getRelativeImportSpecifiers(configContent)) {\n const resolved = path.resolve(dir, specifier);\n if (!isPathInsideDir(dir, resolved)) continue;\n captureSnapshotIfAbsent(snapshots, resolved, seen);\n }\n return { snapshots, seen };\n}\n\nfunction restoreConfigFiles(snapshots: ConfigFileSnapshot[]): void {\n const failures: string[] = [];\n for (const { path: filePath, content } of snapshots) {\n try {\n if (content === null) {\n if (existsSync(filePath)) rmSync(filePath);\n } else {\n writeFileSync(filePath, content, \"utf-8\");\n }\n } catch (error) {\n failures.push(`${filePath}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n if (failures.length > 0) {\n throw new Error(`Failed to restore ${failures.length} file(s) during rollback: ${failures.join(\"; \")}`);\n }\n}\n\nasync function tryReadConfigForValidation(configFilePath: string): Promise<Config | null> {\n try {\n return (await readConfigFile(configFilePath)).config;\n } catch (error) {\n console.warn(`${LOG_PREFIX} Could not evaluate config for validation baseline; will fall back to a structural check`, {\n configFilePath,\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n}\n\nasync function validateAgentUpdate(configFilePath: string, baselineConfig: Config | null, configUpdate: Config): Promise<void> {\n if (baselineConfig != null) {\n const target = canonicalizeConfig(override(baselineConfig, configUpdate));\n const result = canonicalizeConfig((await readConfigFile(configFilePath)).config);\n if (!configsEqual(result, target)) {\n throw new Error(`Config update validation failed for ${configFilePath}: the updated file does not evaluate to the expected configuration.`);\n }\n return;\n }\n\n // Structural-only fallback when jiti can't evaluate the config (e.g. import-with\n // assets): we can't verify values, only that the file still exports `config`.\n const content = readFileSync(configFilePath, \"utf-8\");\n if (!configFileExportsConfig(content, configFilePath)) {\n throw new Error(`Config update validation failed for ${configFilePath}: the updated file no longer exports a valid \\`config\\`.`);\n }\n}\n\nfunction configFileExportsConfig(content: string, configFilePath: string): boolean {\n try {\n evalConfigFileContent(content, configFilePath);\n return true;\n } catch {\n // jiti may fail to resolve imports valid in the user's project but absent\n // here (relative assets, workspace packages). For the structural check we\n // only need a runtime `config` binding to still exist; the agent always\n // authors `export const config`.\n return /\\bexport\\s+const\\s+config\\b/.test(content);\n }\n}\n\nfunction getRelativeImportSpecifiers(content: string): string[] {\n const specifiers: string[] = [];\n const importPattern = /\\bimport\\b(?:[^'\"]*?\\bfrom\\s*)?[\"'](\\.{1,2}\\/[^\"']+)[\"']/g;\n let match: RegExpExecArray | null;\n while ((match = importPattern.exec(content)) !== null) {\n specifiers.push(match[1]);\n }\n return specifiers;\n}\n\nfunction flattenConfigUpdate(update: Config): ConfigChange[] {\n const changes: ConfigChange[] = [];\n const walk = (prefix: string, obj: Config): void => {\n for (const [key, value] of Object.entries(obj)) {\n const fullPath = prefix === \"\" ? key : `${prefix}.${key}`;\n if (value === undefined) continue;\n if (value !== null && typeof value === \"object\" && !Array.isArray(value) && Object.keys(value).length > 0) {\n walk(fullPath, value);\n } else {\n changes.push({ path: fullPath, value });\n }\n }\n };\n walk(\"\", update);\n return changes;\n}\n\nfunction buildConfigUpdatePrompt(configFileName: string, configUpdate: Config, baselineConfig: Config | null): string {\n const changes = flattenConfigUpdate(configUpdate);\n const commandPolicy = \"Do not run shell commands and do not create files other than what is required to apply the config changes.\";\n if (baselineConfig != null) {\n return buildCompleteConfigAgentPrompt({\n scope: { mode: \"known-file\", configFileName },\n completeConfig: canonicalizeConfig(override(baselineConfig, configUpdate)),\n commandPolicy,\n });\n }\n return buildPartialConfigAgentPrompt({\n configFileName,\n changes,\n commandPolicy,\n });\n}\n\nfunction canonicalizeConfig(config: Config): NormalizedConfig {\n const droppedKeys: string[] = [];\n const normalized = normalize(config, {\n onDotIntoNonObject: \"ignore\",\n onDotIntoNull: \"empty-object\",\n droppedKeys,\n });\n if (droppedKeys.length > 0) {\n throw new Error(`Config update has conflicting keys that would be dropped during normalization: ${droppedKeys.map((key) => JSON.stringify(key)).join(\", \")}`);\n }\n return normalized;\n}\n\nfunction configsEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return a === b;\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;\n return a.every((value, index) => configsEqual(value, b[index]));\n }\n if (typeof a === \"object\" && typeof b === \"object\") {\n const aEntries = Object.entries(a);\n const bMap = new Map(Object.entries(b));\n if (aEntries.length !== bMap.size) return false;\n return aEntries.every(([key, value]) => bMap.has(key) && configsEqual(value, bMap.get(key)));\n }\n return false;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config-updater.js","names":["CONFIG_AGENT_FILE_TOOLS","ClaudeAgentTimeoutError","ClaudeAgentFailureError"],"sources":["../src/config-updater.ts"],"sourcesContent":["import { evalConfigFileContent } from \"@hexclave/shared/dist/config-eval\";\nimport type { Config, ConfigValue, NormalizedConfig } from \"@hexclave/shared/dist/config/format\";\nimport { normalize, override } from \"@hexclave/shared/dist/config/format\";\nimport { existsSync, readFileSync, rmSync, writeFileSync } from \"fs\";\nimport path from \"path\";\nimport { buildCompleteConfigAgentPrompt, buildPartialConfigAgentPrompt, ClaudeAgentFailureError, ClaudeAgentTimeoutError, CONFIG_AGENT_FILE_TOOLS, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from \"./config-agent\";\nimport { ensureConfigFileExists, readConfigFile } from \"./config-file\";\n\nconst LOG_PREFIX = \"[Hexclave config updater]\";\nconst DEFAULT_AGENT_TIMEOUT_MS = 120_000;\nconst CONFIG_UPDATE_LOG_PATH_LIMIT = 40;\nconst AGENT_OUTPUT_LOG_MAX_LENGTH = 20_000;\n\ntype ConfigFileSnapshot = { path: string, content: string | null };\ntype ConfigChange = { path: string, value: ConfigValue };\n\nfunction formatConfigUpdaterErrorForLog(error: unknown): Record<string, unknown> {\n if (error instanceof Error) {\n return {\n errorName: error.name,\n errorMessage: error.message,\n errorStack: error.stack,\n };\n }\n return {\n errorMessage: String(error),\n };\n}\n\nfunction configUpdatePathDetailsForLog(changes: ConfigChange[]): Record<string, unknown> {\n const paths = changes.map(({ path: configPath }) => configPath).sort();\n return {\n configUpdatePathCount: paths.length,\n configUpdatePaths: paths.slice(0, CONFIG_UPDATE_LOG_PATH_LIMIT),\n configUpdatePathsTruncated: paths.length > CONFIG_UPDATE_LOG_PATH_LIMIT,\n };\n}\n\nfunction appendBoundedAgentOutput(current: string, chunk: string): string {\n const next = `${current}${chunk}`;\n if (next.length <= AGENT_OUTPUT_LOG_MAX_LENGTH) {\n return next;\n }\n return next.slice(next.length - AGENT_OUTPUT_LOG_MAX_LENGTH);\n}\n\nfunction stringifyAgentMessageForLog(message: unknown): string {\n try {\n return `${JSON.stringify(message)}\\n`;\n } catch {\n return `${String(message)}\\n`;\n }\n}\n\nfunction agentOutputDetailsForLog(agentStdout: string, agentStderr: string): Record<string, unknown> {\n return {\n agentStdout,\n agentStdoutTruncated: agentStdout.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,\n agentStderr,\n agentStderrTruncated: agentStderr.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,\n };\n}\n\nexport async function updateConfigObject(configFilePath: string, configUpdate: Config): Promise<void> {\n const startedAtMs = performance.now();\n ensureConfigFileExists(configFilePath);\n\n const changes = flattenConfigUpdate(configUpdate);\n if (changes.length === 0) {\n console.log(`${LOG_PREFIX} Skipping config update because it contains no changes`, {\n configFilePath,\n });\n return;\n }\n const updateLogDetails = {\n configFilePath,\n ...configUpdatePathDetailsForLog(changes),\n };\n console.log(`${LOG_PREFIX} Starting config file update`, updateLogDetails);\n\n const content = readFileSync(configFilePath, \"utf-8\");\n\n // One write path, always: hand the change to the AI agent so it edits the file\n // in place and preserves its authoring (helper wrappers, imports, comments,\n // layout). There is deliberately no deterministic \"fast path\" — re-rendering a\n // config would flatten and destroy hand-authored files. Reads use jiti\n // (see readConfigFile); writes go through the agent.\n console.log(`${LOG_PREFIX} Applying config update with agent-assisted rewrite`, {\n ...updateLogDetails,\n configDirectory: path.dirname(configFilePath),\n });\n const baselineConfig = await tryReadConfigForValidation(configFilePath);\n const { snapshots, seen } = snapshotConfigFiles(configFilePath, content);\n try {\n await runConfigUpdateAgent({\n prompt: buildConfigUpdatePrompt(path.basename(configFilePath), configUpdate, baselineConfig),\n cwd: path.dirname(configFilePath),\n onFileWillChange: (filePath) => captureSnapshotIfAbsent(snapshots, filePath, seen),\n });\n await validateAgentUpdate(configFilePath, baselineConfig, configUpdate);\n } catch (error) {\n console.warn(`${LOG_PREFIX} Config update failed; restoring files from snapshots`, {\n ...updateLogDetails,\n snapshotCount: snapshots.length,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n });\n try {\n restoreConfigFiles(snapshots);\n console.warn(`${LOG_PREFIX} Restored files after failed config update`, {\n ...updateLogDetails,\n snapshotCount: snapshots.length,\n });\n } catch (restoreError) {\n console.error(`${LOG_PREFIX} Failed to fully roll back config files after a failed update of ${configFilePath}; some files may be left in a partially-restored state`, {\n configFilePath,\n ...formatConfigUpdaterErrorForLog(restoreError),\n });\n }\n throw error;\n }\n console.log(`${LOG_PREFIX} Finished config update with agent-assisted rewrite`, {\n ...updateLogDetails,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n snapshotCount: snapshots.length,\n });\n}\n\nasync function runConfigUpdateAgent(options: {\n prompt: string,\n cwd: string,\n onFileWillChange?: (filePath: string) => void,\n}): Promise<void> {\n const timeoutMs = parseAgentTimeoutMs();\n const deniedOutOfBoundsWrites = new Set<string>();\n const startedAtMs = performance.now();\n let agentStdout = \"\";\n let agentStderr = \"\";\n console.log(`${LOG_PREFIX} Starting config update agent`, {\n cwd: options.cwd,\n timeoutMs,\n });\n try {\n await runHeadlessClaudeAgent({\n prompt: options.prompt,\n cwd: options.cwd,\n allowedTools: [...CONFIG_AGENT_FILE_TOOLS],\n strictIsolation: true,\n timeoutMs,\n stderr: (data) => {\n agentStderr = appendBoundedAgentOutput(agentStderr, data);\n console.warn(`${LOG_PREFIX} [agent] ${data}`);\n },\n onMessage: (message) => {\n agentStdout = appendBoundedAgentOutput(agentStdout, stringifyAgentMessageForLog(message));\n },\n onPreToolUse: (input) => {\n const target = getToolWriteTargetPath(input.tool_name, input.tool_input, options.cwd);\n if (target == null) return { continue: true };\n if (!isPathInsideDir(options.cwd, target)) {\n deniedOutOfBoundsWrites.add(target);\n return {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"deny\",\n permissionDecisionReason: `Refusing to modify ${target}: config updates may only change files inside the config directory.`,\n },\n };\n }\n options.onFileWillChange?.(target);\n return { continue: true };\n },\n });\n } catch (error) {\n if (error instanceof ClaudeAgentTimeoutError) {\n console.warn(`${LOG_PREFIX} Config update agent timed out`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw new Error(`Config update agent timed out after ${timeoutMs}ms. It was unable to apply the config changes to the file.`);\n }\n if (error instanceof ClaudeAgentFailureError) {\n console.warn(`${LOG_PREFIX} Config update agent failed`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw new Error(`${error.message} It was unable to apply the config changes to the file.`);\n }\n console.warn(`${LOG_PREFIX} Config update agent failed unexpectedly`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw error;\n }\n console.log(`${LOG_PREFIX} Finished config update agent`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,\n });\n if (deniedOutOfBoundsWrites.size > 0) {\n console.warn(`${LOG_PREFIX} Config update agent attempted out-of-bounds writes`, {\n cwd: options.cwd,\n deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,\n deniedOutOfBoundsWrites: [...deniedOutOfBoundsWrites],\n });\n throw new Error(`Config update agent tried to modify ${deniedOutOfBoundsWrites.size} file(s) outside the config directory, which is not allowed: ${[...deniedOutOfBoundsWrites].join(\", \")}. The config was not updated.`);\n }\n}\n\nfunction parseAgentTimeoutMs(): number {\n const raw = process.env.STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS;\n if (raw == null || raw.trim() === \"\") return DEFAULT_AGENT_TIMEOUT_MS;\n const parsed = Number(raw);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new Error(`Invalid STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS: ${JSON.stringify(raw)}. Expected a positive number of milliseconds.`);\n }\n return parsed;\n}\n\nfunction captureSnapshotIfAbsent(snapshots: ConfigFileSnapshot[], filePath: string, seen: Set<string>): void {\n const resolved = path.resolve(filePath);\n if (seen.has(resolved)) return;\n seen.add(resolved);\n snapshots.push({ path: resolved, content: existsSync(resolved) ? readFileSync(resolved, \"utf-8\") : null });\n}\n\nfunction snapshotConfigFiles(configFilePath: string, configContent: string): { snapshots: ConfigFileSnapshot[]; seen: Set<string> } {\n const dir = path.dirname(configFilePath);\n const resolvedConfig = path.resolve(configFilePath);\n const snapshots: ConfigFileSnapshot[] = [{ path: resolvedConfig, content: configContent }];\n const seen = new Set<string>([resolvedConfig]);\n for (const specifier of getRelativeImportSpecifiers(configContent)) {\n const resolved = path.resolve(dir, specifier);\n if (!isPathInsideDir(dir, resolved)) continue;\n captureSnapshotIfAbsent(snapshots, resolved, seen);\n }\n return { snapshots, seen };\n}\n\nfunction restoreConfigFiles(snapshots: ConfigFileSnapshot[]): void {\n const failures: string[] = [];\n for (const { path: filePath, content } of snapshots) {\n try {\n if (content === null) {\n if (existsSync(filePath)) rmSync(filePath);\n } else {\n writeFileSync(filePath, content, \"utf-8\");\n }\n } catch (error) {\n failures.push(`${filePath}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n if (failures.length > 0) {\n throw new Error(`Failed to restore ${failures.length} file(s) during rollback: ${failures.join(\"; \")}`);\n }\n}\n\nasync function tryReadConfigForValidation(configFilePath: string): Promise<Config | null> {\n try {\n return (await readConfigFile(configFilePath)).config;\n } catch (error) {\n console.warn(`${LOG_PREFIX} Could not evaluate config for validation baseline; will fall back to a structural check`, {\n configFilePath,\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n}\n\nasync function validateAgentUpdate(configFilePath: string, baselineConfig: Config | null, configUpdate: Config): Promise<void> {\n if (baselineConfig != null) {\n const target = canonicalizeConfig(override(baselineConfig, configUpdate));\n const result = canonicalizeConfig((await readConfigFile(configFilePath)).config);\n if (!configsEqual(result, target)) {\n throw new Error(`Config update validation failed for ${configFilePath}: the updated file does not evaluate to the expected configuration.`);\n }\n return;\n }\n\n // Structural-only fallback when jiti can't evaluate the config (e.g. import-with\n // assets): we can't verify values, only that the file still exports `config`.\n const content = readFileSync(configFilePath, \"utf-8\");\n if (!configFileExportsConfig(content, configFilePath)) {\n throw new Error(`Config update validation failed for ${configFilePath}: the updated file no longer exports a valid \\`config\\`.`);\n }\n}\n\nfunction configFileExportsConfig(content: string, configFilePath: string): boolean {\n try {\n evalConfigFileContent(content, configFilePath);\n return true;\n } catch {\n // jiti may fail to resolve imports valid in the user's project but absent\n // here (relative assets, workspace packages). For the structural check we\n // only need a runtime `config` binding to still exist; the agent always\n // authors `export const config`.\n return /\\bexport\\s+const\\s+config\\b/.test(content);\n }\n}\n\nfunction getRelativeImportSpecifiers(content: string): string[] {\n const specifiers: string[] = [];\n const importPattern = /\\bimport\\b(?:[^'\"]*?\\bfrom\\s*)?[\"'](\\.{1,2}\\/[^\"']+)[\"']/g;\n let match: RegExpExecArray | null;\n while ((match = importPattern.exec(content)) !== null) {\n specifiers.push(match[1]);\n }\n return specifiers;\n}\n\nfunction flattenConfigUpdate(update: Config): ConfigChange[] {\n const changes: ConfigChange[] = [];\n const walk = (prefix: string, obj: Config): void => {\n for (const [key, value] of Object.entries(obj)) {\n const fullPath = prefix === \"\" ? key : `${prefix}.${key}`;\n if (value === undefined) continue;\n if (value !== null && typeof value === \"object\" && !Array.isArray(value) && Object.keys(value).length > 0) {\n walk(fullPath, value);\n } else {\n changes.push({ path: fullPath, value });\n }\n }\n };\n walk(\"\", update);\n return changes;\n}\n\nfunction buildConfigUpdatePrompt(configFileName: string, configUpdate: Config, baselineConfig: Config | null): string {\n const changes = flattenConfigUpdate(configUpdate);\n const commandPolicy = \"Do not run shell commands and do not create files other than what is required to apply the config changes.\";\n if (baselineConfig != null) {\n return buildCompleteConfigAgentPrompt({\n scope: { mode: \"known-file\", configFileName },\n completeConfig: canonicalizeConfig(override(baselineConfig, configUpdate)),\n commandPolicy,\n });\n }\n return buildPartialConfigAgentPrompt({\n configFileName,\n changes,\n commandPolicy,\n });\n}\n\nfunction canonicalizeConfig(config: Config): NormalizedConfig {\n const droppedKeys: string[] = [];\n const normalized = normalize(config, {\n onDotIntoNonObject: \"ignore\",\n onDotIntoNull: \"empty-object\",\n droppedKeys,\n });\n if (droppedKeys.length > 0) {\n throw new Error(`Config update has conflicting keys that would be dropped during normalization: ${droppedKeys.map((key) => JSON.stringify(key)).join(\", \")}`);\n }\n return normalized;\n}\n\nfunction configsEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return a === b;\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;\n return a.every((value, index) => configsEqual(value, b[index]));\n }\n if (typeof a === \"object\" && typeof b === \"object\") {\n const aEntries = Object.entries(a);\n const bMap = new Map(Object.entries(b));\n if (aEntries.length !== bMap.size) return false;\n return aEntries.every(([key, value]) => bMap.has(key) && configsEqual(value, bMap.get(key)));\n }\n return false;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config-agent.d.ts","names":[],"sources":["../../src/config-agent.ts"],"mappings":";KAMY,mBAAA;AAAA,KAEP,qBAAA;EAA0B,QAAA;AAAA;EAC7B,kBAAA;IACE,aAAA;IACA,kBAAA;IACA,wBAAA;EAAA;AAAA;AAAA,KAIQ,0BAAA;EACV,eAAA;EACA,SAAA;EACA,UAAA;AAAA;AAAA,KAGU,qBAAA;EACV,MAAA;EACA,GAAA;EACA,YAAA,EAAc,mBAAA;EACd,SAAA;EACA,eAAA;EACA,MAAA,IAAU,IAAA;EACV,SAAA,IAAa,OAAA;EACb,YAAA,IAAgB,KAAA,EAAO,0BAAA,KAA+B,OAAA,CAAQ,qBAAA,IAAyB,qBAAA;AAAA;AAAA,KAG7E,oBAAA;EACV,UAAA;AAAA;AAAA,cAGW,uBAAA;AAAA,cACA,uBAAA;AAAA,KAYD,sBAAA;EAER,IAAA;EACA,cAAA;AAAA;EAGA,IAAA;AAAA;AAAA,iBA+BY,8BAAA,CAA+B,OAAA;EAC7C,KAAA,EAAO,sBAAA;EACP,cAAA,EAAgB,MAAA;EAChB,aAAA;AAAA;AAAA,iBAYc,6BAAA,CAA8B,OAAA;EAC5C,cAAA;EACA,OAAA,EAAS,KAAA;IAAQ,IAAA;IAAc,KAAA;EAAA;EAC/B,aAAA;AAAA;AAAA,cAeW,uBAAA,SAAgC,KAAA;cAC/B,SAAA;AAAA;AAAA,cAMD,uBAAA,SAAgC,KAAA;cAC/B,OAAA;AAAA;AAAA,iBAUE,kBAAA,CAAA,GAAsB,MAAA;AAAA,iBAOhB,sBAAA,CAAuB,OAAA,EAAS,qBAAA,GAAwB,OAAA,CAAQ,oBAAA;AAAA,iBA0EtE,sBAAA,CAAuB,QAAA,UAAkB,SAAA,WAAoB,GAAA;AAAA,iBAO7D,eAAA,CAAgB,GAAA,UAAa,MAAA"}
1
+ {"version":3,"file":"config-agent.d.ts","names":[],"sources":["../../src/config-agent.ts"],"mappings":";KAMY,mBAAA;AAAA,KAEP,qBAAA;EAA0B,QAAA;AAAA;EAC7B,kBAAA;IACE,aAAA;IACA,kBAAA;IACA,wBAAA;EAAA;AAAA;AAAA,KAIQ,0BAAA;EACV,eAAA;EACA,SAAA;EACA,UAAA;AAAA;AAAA,KAGU,qBAAA;EACV,MAAA;EACA,GAAA;EACA,YAAA,EAAc,mBAAA;EACd,SAAA;EACA,eAAA;EACA,MAAA,IAAU,IAAA;EACV,SAAA,IAAa,OAAA;EACb,YAAA,IAAgB,KAAA,EAAO,0BAAA,KAA+B,OAAA,CAAQ,qBAAA,IAAyB,qBAAA;AAAA;AAAA,KAG7E,oBAAA;EACV,UAAU;AAAA;AAAA,cAGC,uBAAA;AAAA,cACA,uBAAA;AAAA,KAYD,sBAAA;EAER,IAAA;EACA,cAAA;AAAA;EAGA,IAAA;AAAA;AAAA,iBA+BY,8BAAA,CAA+B,OAAA;EAC7C,KAAA,EAAO,sBAAA;EACP,cAAA,EAAgB,MAAM;EACtB,aAAA;AAAA;AAAA,iBAYc,6BAAA,CAA8B,OAAA;EAC5C,cAAA;EACA,OAAA,EAAS,KAAK;IAAG,IAAA;IAAc,KAAA;EAAA;EAC/B,aAAA;AAAA;AAAA,cAeW,uBAAA,SAAgC,KAAK;cACpC,SAAA;AAAA;AAAA,cAMD,uBAAA,SAAgC,KAAK;cACpC,OAAA;AAAA;AAAA,iBAUE,kBAAA,IAAsB,MAAM;AAAA,iBAOtB,sBAAA,CAAuB,OAAA,EAAS,qBAAA,GAAwB,OAAA,CAAQ,oBAAA;AAAA,iBA0EtE,sBAAA,CAAuB,QAAA,UAAkB,SAAA,WAAoB,GAAA;AAAA,iBAO7D,eAAA,CAAgB,GAAA,UAAa,MAAc"}
@@ -1,6 +1,5 @@
1
1
  import { query } from "@anthropic-ai/claude-agent-sdk";
2
2
  import path from "path";
3
-
4
3
  //#region src/config-agent.ts
5
4
  const ANTHROPIC_PROXY_BASE_URL = process.env.STACK_CLAUDE_PROXY_URL ?? "https://api.hexclave.com/api/latest/integrations/ai-proxy";
6
5
  const CONFIG_AGENT_FILE_TOOLS = [
@@ -118,7 +117,7 @@ async function runHeadlessClaudeAgent(options) {
118
117
  if (!sawResult) throw new Error("Claude agent ended without reporting a result.");
119
118
  return { resultText };
120
119
  }
121
- const FILE_MUTATING_TOOLS = new Set(["Write", "Edit"]);
120
+ const FILE_MUTATING_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit"]);
122
121
  function hasStringFilePath(input) {
123
122
  return typeof input === "object" && input !== null && "file_path" in input && typeof input.file_path === "string";
124
123
  }
@@ -130,7 +129,7 @@ function isPathInsideDir(dir, target) {
130
129
  const relative = path.relative(path.resolve(dir), path.resolve(target));
131
130
  return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
132
131
  }
133
-
134
132
  //#endregion
135
133
  export { CONFIG_AGENT_FILE_TOOLS, CONFIG_AGENT_REPO_TOOLS, ClaudeAgentFailureError, ClaudeAgentTimeoutError, buildCompleteConfigAgentPrompt, buildPartialConfigAgentPrompt, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent, stripClaudeCodeEnv };
134
+
136
135
  //# sourceMappingURL=config-agent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-agent.js","names":[],"sources":["../../src/config-agent.ts"],"sourcesContent":["import { query } from \"@anthropic-ai/claude-agent-sdk\";\nimport path from \"path\";\n\nconst DEFAULT_PROXY_URL = \"https://api.hexclave.com/api/latest/integrations/ai-proxy\";\nconst ANTHROPIC_PROXY_BASE_URL: string = process.env.STACK_CLAUDE_PROXY_URL ?? DEFAULT_PROXY_URL;\n\nexport type ClaudeAgentToolName = \"Read\" | \"Write\" | \"Edit\" | \"MultiEdit\" | \"NotebookEdit\" | \"Bash\" | \"Glob\" | \"Grep\";\n\ntype ClaudeAgentHookResult = { continue: true } | {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"deny\",\n permissionDecisionReason: string,\n },\n};\n\nexport type ClaudeAgentPreToolUseInput = {\n hook_event_name: \"PreToolUse\",\n tool_name: string,\n tool_input: unknown,\n};\n\nexport type RunClaudeAgentOptions = {\n prompt: string,\n cwd: string,\n allowedTools: ClaudeAgentToolName[],\n timeoutMs?: number,\n strictIsolation?: boolean,\n stderr?: (data: string) => void,\n onMessage?: (message: unknown) => void,\n onPreToolUse?: (input: ClaudeAgentPreToolUseInput) => Promise<ClaudeAgentHookResult> | ClaudeAgentHookResult,\n};\n\nexport type RunClaudeAgentResult = {\n resultText: string,\n};\n\nexport const CONFIG_AGENT_FILE_TOOLS = [\"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\"] as const;\nexport const CONFIG_AGENT_REPO_TOOLS = [...CONFIG_AGENT_FILE_TOOLS, \"Bash\"] as const;\n\ntype ConfigAgentPromptTarget =\n | {\n mode: \"complete\",\n completeConfig: Record<string, unknown>,\n }\n | {\n mode: \"partial\",\n changes: Array<{ path: string, value: unknown }>,\n };\n\nexport type ConfigAgentPromptScope =\n | {\n mode: \"known-file\",\n configFileName: string,\n }\n | {\n mode: \"repo\",\n };\n\nfunction buildConfigAgentPrompt(options: {\n scope: ConfigAgentPromptScope,\n target: ConfigAgentPromptTarget,\n commandPolicy: string,\n}): string {\n const targetSection = options.target.mode === \"complete\"\n ? `The exported config must end up deep-equal to this JSON value:\\n\\n${JSON.stringify(options.target.completeConfig, null, 2)}`\n : `Apply EXACTLY these config changes. Paths use dot notation, so \\`a.b.c\\` refers to \\`config.a.b.c\\`:\\n\\n${options.target.changes.map(({ path, value }) => `- ${JSON.stringify(path)}: set to ${JSON.stringify(value)}`).join(\"\\n\")}`;\n const scopeSection = options.scope.mode === \"known-file\"\n ? `Config file: ${JSON.stringify(options.scope.configFileName)} in the current working directory.`\n : \"Current working directory: the repository root. Find the Hexclave / Stack Auth config file. It is usually a `*.config.ts` file exporting `config`, often wrapped in `defineHexclaveConfig(...)` or a similar helper.\";\n\n return `You are updating a Hexclave / Stack Auth configuration file.\n\n${scopeSection}\n\n${targetSection}\n\nRules:\n- Keep the file valid: it must still export \\`config\\`.\n- Preserve the existing authoring style where possible: imports, comments, helper wrappers, file header comments, and formatting.\n- If the config currently exports the placeholder string \"show-onboarding\" or is otherwise a stub, replace it with a real typed config object.\n- If a config value is conventionally sourced from an imported external file, you may keep that indirection as long as the evaluated config matches the requested value.\n- Do not touch unrelated files or application code.\n- ${options.commandPolicy}\n- Make the edits, then stop.`;\n}\n\nexport function buildCompleteConfigAgentPrompt(options: {\n scope: ConfigAgentPromptScope,\n completeConfig: Record<string, unknown>,\n commandPolicy: string,\n}): string {\n return buildConfigAgentPrompt({\n scope: options.scope,\n target: {\n mode: \"complete\",\n completeConfig: options.completeConfig,\n },\n commandPolicy: options.commandPolicy,\n });\n}\n\nexport function buildPartialConfigAgentPrompt(options: {\n configFileName: string,\n changes: Array<{ path: string, value: unknown }>,\n commandPolicy: string,\n}): string {\n return buildConfigAgentPrompt({\n scope: {\n mode: \"known-file\",\n configFileName: options.configFileName,\n },\n target: {\n mode: \"partial\",\n changes: options.changes,\n },\n commandPolicy: options.commandPolicy,\n });\n}\n\nexport class ClaudeAgentTimeoutError extends Error {\n constructor(timeoutMs?: number) {\n super(`Claude agent timed out${timeoutMs == null ? \"\" : ` after ${timeoutMs}ms`}.`);\n this.name = \"ClaudeAgentTimeoutError\";\n }\n}\n\nexport class ClaudeAgentFailureError extends Error {\n constructor(subtype: string) {\n super(`Claude agent failed (${subtype}).`);\n this.name = \"ClaudeAgentFailureError\";\n }\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === \"AbortError\";\n}\n\nexport function stripClaudeCodeEnv(): Record<string, string | undefined> {\n const env = { ...process.env };\n // CLAUDECODE must be unset for nested agents; ANTHROPIC_API_KEY must be non-empty (proxy ignores it).\n delete env.CLAUDECODE;\n return env;\n}\n\nexport async function runHeadlessClaudeAgent(options: RunClaudeAgentOptions): Promise<RunClaudeAgentResult> {\n const abortController = new AbortController();\n const timeout = options.timeoutMs == null ? null : setTimeout(() => abortController.abort(), options.timeoutMs);\n let sawResult = false;\n let resultText = \"\";\n const onPreToolUse = options.onPreToolUse;\n try {\n for await (const message of query({\n prompt: options.prompt,\n options: {\n model: \"anthropic/claude-haiku-4.5\",\n ...(options.strictIsolation === true ? {\n settingSources: [],\n strictMcpConfig: true,\n } : {}),\n ...(onPreToolUse == null ? {} : {\n hooks: {\n PreToolUse: [{\n hooks: [async (input) => {\n if (input.hook_event_name !== \"PreToolUse\") {\n return { continue: true };\n }\n return await onPreToolUse(input);\n }],\n }],\n },\n }),\n allowedTools: options.allowedTools,\n permissionMode: \"dontAsk\",\n cwd: options.cwd,\n abortController,\n env: {\n ...stripClaudeCodeEnv(),\n ...(options.strictIsolation === true ? { CLAUDE_CODE_DISABLE_AUTO_MEMORY: \"1\" } : {}),\n ANTHROPIC_BASE_URL: ANTHROPIC_PROXY_BASE_URL,\n ANTHROPIC_API_KEY: \"stack-auth-proxy\",\n },\n stderr: options.stderr,\n },\n })) {\n options.onMessage?.(message);\n if (message.type === \"result\") {\n if (\"result\" in message) {\n sawResult = true;\n resultText = message.result;\n } else {\n throw new ClaudeAgentFailureError(message.subtype);\n }\n }\n }\n } catch (error) {\n if (abortController.signal.aborted && isAbortError(error)) {\n throw new ClaudeAgentTimeoutError(options.timeoutMs ?? undefined);\n }\n throw error;\n } finally {\n if (timeout != null) {\n clearTimeout(timeout);\n }\n }\n if (!sawResult) {\n throw new Error(\"Claude agent ended without reporting a result.\");\n }\n return { resultText };\n}\n\n// Only the tools actually in the agent's allowedTools list; MultiEdit and\n// NotebookEdit are intentionally excluded from the agent's tool set.\nconst FILE_MUTATING_TOOLS = new Set([\"Write\", \"Edit\"]);\n\nfunction hasStringFilePath(input: unknown): input is { file_path: string } {\n return typeof input === \"object\" && input !== null && \"file_path\" in input && typeof input.file_path === \"string\";\n}\n\nexport function getToolWriteTargetPath(toolName: string, toolInput: unknown, cwd: string): string | null {\n if (!FILE_MUTATING_TOOLS.has(toolName) || !hasStringFilePath(toolInput)) {\n return null;\n }\n return path.isAbsolute(toolInput.file_path) ? toolInput.file_path : path.resolve(cwd, toolInput.file_path);\n}\n\nexport function isPathInsideDir(dir: string, target: string): boolean {\n const relative = path.relative(path.resolve(dir), path.resolve(target));\n return relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative));\n}\n"],"mappings":";;;;AAIA,MAAM,2BAAmC,QAAQ,IAAI,0BAD3B;AAkC1B,MAAa,0BAA0B;CAAC;CAAQ;CAAS;CAAQ;CAAQ;CAAO;AAChF,MAAa,0BAA0B,CAAC,GAAG,yBAAyB,OAAO;AAqB3E,SAAS,uBAAuB,SAIrB;CACT,MAAM,gBAAgB,QAAQ,OAAO,SAAS,aAC1C,qEAAqE,KAAK,UAAU,QAAQ,OAAO,gBAAgB,MAAM,EAAE,KAC3H,2GAA2G,QAAQ,OAAO,QAAQ,KAAK,EAAE,MAAM,YAAY,KAAK,KAAK,UAAU,KAAK,CAAC,WAAW,KAAK,UAAU,MAAM,GAAG,CAAC,KAAK,KAAK;AAKvO,QAAO;;EAJc,QAAQ,MAAM,SAAS,eACxC,gBAAgB,KAAK,UAAU,QAAQ,MAAM,eAAe,CAAC,sCAC7D,uNAIS;;EAEb,cAAc;;;;;;;;IAQZ,QAAQ,cAAc;;;AAI1B,SAAgB,+BAA+B,SAIpC;AACT,QAAO,uBAAuB;EAC5B,OAAO,QAAQ;EACf,QAAQ;GACN,MAAM;GACN,gBAAgB,QAAQ;GACzB;EACD,eAAe,QAAQ;EACxB,CAAC;;AAGJ,SAAgB,8BAA8B,SAInC;AACT,QAAO,uBAAuB;EAC5B,OAAO;GACL,MAAM;GACN,gBAAgB,QAAQ;GACzB;EACD,QAAQ;GACN,MAAM;GACN,SAAS,QAAQ;GAClB;EACD,eAAe,QAAQ;EACxB,CAAC;;AAGJ,IAAa,0BAAb,cAA6C,MAAM;CACjD,YAAY,WAAoB;AAC9B,QAAM,yBAAyB,aAAa,OAAO,KAAK,UAAU,UAAU,IAAI,GAAG;AACnF,OAAK,OAAO;;;AAIhB,IAAa,0BAAb,cAA6C,MAAM;CACjD,YAAY,SAAiB;AAC3B,QAAM,wBAAwB,QAAQ,IAAI;AAC1C,OAAK,OAAO;;;AAIhB,SAAS,aAAa,OAAyB;AAC7C,QAAO,iBAAiB,SAAS,MAAM,SAAS;;AAGlD,SAAgB,qBAAyD;CACvE,MAAM,MAAM,EAAE,GAAG,QAAQ,KAAK;AAE9B,QAAO,IAAI;AACX,QAAO;;AAGT,eAAsB,uBAAuB,SAA+D;CAC1G,MAAM,kBAAkB,IAAI,iBAAiB;CAC7C,MAAM,UAAU,QAAQ,aAAa,OAAO,OAAO,iBAAiB,gBAAgB,OAAO,EAAE,QAAQ,UAAU;CAC/G,IAAI,YAAY;CAChB,IAAI,aAAa;CACjB,MAAM,eAAe,QAAQ;AAC7B,KAAI;AACF,aAAW,MAAM,WAAW,MAAM;GAChC,QAAQ,QAAQ;GAChB,SAAS;IACP,OAAO;IACP,GAAI,QAAQ,oBAAoB,OAAO;KACrC,gBAAgB,EAAE;KAClB,iBAAiB;KAClB,GAAG,EAAE;IACN,GAAI,gBAAgB,OAAO,EAAE,GAAG,EAC9B,OAAO,EACL,YAAY,CAAC,EACX,OAAO,CAAC,OAAO,UAAU;AACvB,SAAI,MAAM,oBAAoB,aAC5B,QAAO,EAAE,UAAU,MAAM;AAE3B,YAAO,MAAM,aAAa,MAAM;MAChC,EACH,CAAC,EACH,EACF;IACD,cAAc,QAAQ;IACtB,gBAAgB;IAChB,KAAK,QAAQ;IACb;IACA,KAAK;KACH,GAAG,oBAAoB;KACvB,GAAI,QAAQ,oBAAoB,OAAO,EAAE,iCAAiC,KAAK,GAAG,EAAE;KACpF,oBAAoB;KACpB,mBAAmB;KACpB;IACD,QAAQ,QAAQ;IACjB;GACF,CAAC,EAAE;AACF,WAAQ,YAAY,QAAQ;AAC5B,OAAI,QAAQ,SAAS,SACnB,KAAI,YAAY,SAAS;AACvB,gBAAY;AACZ,iBAAa,QAAQ;SAErB,OAAM,IAAI,wBAAwB,QAAQ,QAAQ;;UAIjD,OAAO;AACd,MAAI,gBAAgB,OAAO,WAAW,aAAa,MAAM,CACvD,OAAM,IAAI,wBAAwB,QAAQ,aAAa,OAAU;AAEnE,QAAM;WACE;AACR,MAAI,WAAW,KACb,cAAa,QAAQ;;AAGzB,KAAI,CAAC,UACH,OAAM,IAAI,MAAM,iDAAiD;AAEnE,QAAO,EAAE,YAAY;;AAKvB,MAAM,sBAAsB,IAAI,IAAI,CAAC,SAAS,OAAO,CAAC;AAEtD,SAAS,kBAAkB,OAAgD;AACzE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,eAAe,SAAS,OAAO,MAAM,cAAc;;AAG3G,SAAgB,uBAAuB,UAAkB,WAAoB,KAA4B;AACvG,KAAI,CAAC,oBAAoB,IAAI,SAAS,IAAI,CAAC,kBAAkB,UAAU,CACrE,QAAO;AAET,QAAO,KAAK,WAAW,UAAU,UAAU,GAAG,UAAU,YAAY,KAAK,QAAQ,KAAK,UAAU,UAAU;;AAG5G,SAAgB,gBAAgB,KAAa,QAAyB;CACpE,MAAM,WAAW,KAAK,SAAS,KAAK,QAAQ,IAAI,EAAE,KAAK,QAAQ,OAAO,CAAC;AACvE,QAAO,aAAa,MAAO,CAAC,SAAS,WAAW,KAAK,IAAI,CAAC,KAAK,WAAW,SAAS"}
1
+ {"version":3,"file":"config-agent.js","names":[],"sources":["../../src/config-agent.ts"],"sourcesContent":["import { query } from \"@anthropic-ai/claude-agent-sdk\";\nimport path from \"path\";\n\nconst DEFAULT_PROXY_URL = \"https://api.hexclave.com/api/latest/integrations/ai-proxy\";\nconst ANTHROPIC_PROXY_BASE_URL: string = process.env.STACK_CLAUDE_PROXY_URL ?? DEFAULT_PROXY_URL;\n\nexport type ClaudeAgentToolName = \"Read\" | \"Write\" | \"Edit\" | \"MultiEdit\" | \"NotebookEdit\" | \"Bash\" | \"Glob\" | \"Grep\";\n\ntype ClaudeAgentHookResult = { continue: true } | {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"deny\",\n permissionDecisionReason: string,\n },\n};\n\nexport type ClaudeAgentPreToolUseInput = {\n hook_event_name: \"PreToolUse\",\n tool_name: string,\n tool_input: unknown,\n};\n\nexport type RunClaudeAgentOptions = {\n prompt: string,\n cwd: string,\n allowedTools: ClaudeAgentToolName[],\n timeoutMs?: number,\n strictIsolation?: boolean,\n stderr?: (data: string) => void,\n onMessage?: (message: unknown) => void,\n onPreToolUse?: (input: ClaudeAgentPreToolUseInput) => Promise<ClaudeAgentHookResult> | ClaudeAgentHookResult,\n};\n\nexport type RunClaudeAgentResult = {\n resultText: string,\n};\n\nexport const CONFIG_AGENT_FILE_TOOLS = [\"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\"] as const;\nexport const CONFIG_AGENT_REPO_TOOLS = [...CONFIG_AGENT_FILE_TOOLS, \"Bash\"] as const;\n\ntype ConfigAgentPromptTarget =\n | {\n mode: \"complete\",\n completeConfig: Record<string, unknown>,\n }\n | {\n mode: \"partial\",\n changes: Array<{ path: string, value: unknown }>,\n };\n\nexport type ConfigAgentPromptScope =\n | {\n mode: \"known-file\",\n configFileName: string,\n }\n | {\n mode: \"repo\",\n };\n\nfunction buildConfigAgentPrompt(options: {\n scope: ConfigAgentPromptScope,\n target: ConfigAgentPromptTarget,\n commandPolicy: string,\n}): string {\n const targetSection = options.target.mode === \"complete\"\n ? `The exported config must end up deep-equal to this JSON value:\\n\\n${JSON.stringify(options.target.completeConfig, null, 2)}`\n : `Apply EXACTLY these config changes. Paths use dot notation, so \\`a.b.c\\` refers to \\`config.a.b.c\\`:\\n\\n${options.target.changes.map(({ path, value }) => `- ${JSON.stringify(path)}: set to ${JSON.stringify(value)}`).join(\"\\n\")}`;\n const scopeSection = options.scope.mode === \"known-file\"\n ? `Config file: ${JSON.stringify(options.scope.configFileName)} in the current working directory.`\n : \"Current working directory: the repository root. Find the Hexclave / Stack Auth config file. It is usually a `*.config.ts` file exporting `config`, often wrapped in `defineHexclaveConfig(...)` or a similar helper.\";\n\n return `You are updating a Hexclave / Stack Auth configuration file.\n\n${scopeSection}\n\n${targetSection}\n\nRules:\n- Keep the file valid: it must still export \\`config\\`.\n- Preserve the existing authoring style where possible: imports, comments, helper wrappers, file header comments, and formatting.\n- If the config currently exports the placeholder string \"show-onboarding\" or is otherwise a stub, replace it with a real typed config object.\n- If a config value is conventionally sourced from an imported external file, you may keep that indirection as long as the evaluated config matches the requested value.\n- Do not touch unrelated files or application code.\n- ${options.commandPolicy}\n- Make the edits, then stop.`;\n}\n\nexport function buildCompleteConfigAgentPrompt(options: {\n scope: ConfigAgentPromptScope,\n completeConfig: Record<string, unknown>,\n commandPolicy: string,\n}): string {\n return buildConfigAgentPrompt({\n scope: options.scope,\n target: {\n mode: \"complete\",\n completeConfig: options.completeConfig,\n },\n commandPolicy: options.commandPolicy,\n });\n}\n\nexport function buildPartialConfigAgentPrompt(options: {\n configFileName: string,\n changes: Array<{ path: string, value: unknown }>,\n commandPolicy: string,\n}): string {\n return buildConfigAgentPrompt({\n scope: {\n mode: \"known-file\",\n configFileName: options.configFileName,\n },\n target: {\n mode: \"partial\",\n changes: options.changes,\n },\n commandPolicy: options.commandPolicy,\n });\n}\n\nexport class ClaudeAgentTimeoutError extends Error {\n constructor(timeoutMs?: number) {\n super(`Claude agent timed out${timeoutMs == null ? \"\" : ` after ${timeoutMs}ms`}.`);\n this.name = \"ClaudeAgentTimeoutError\";\n }\n}\n\nexport class ClaudeAgentFailureError extends Error {\n constructor(subtype: string) {\n super(`Claude agent failed (${subtype}).`);\n this.name = \"ClaudeAgentFailureError\";\n }\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === \"AbortError\";\n}\n\nexport function stripClaudeCodeEnv(): Record<string, string | undefined> {\n const env = { ...process.env };\n // CLAUDECODE must be unset for nested agents; ANTHROPIC_API_KEY must be non-empty (proxy ignores it).\n delete env.CLAUDECODE;\n return env;\n}\n\nexport async function runHeadlessClaudeAgent(options: RunClaudeAgentOptions): Promise<RunClaudeAgentResult> {\n const abortController = new AbortController();\n const timeout = options.timeoutMs == null ? null : setTimeout(() => abortController.abort(), options.timeoutMs);\n let sawResult = false;\n let resultText = \"\";\n const onPreToolUse = options.onPreToolUse;\n try {\n for await (const message of query({\n prompt: options.prompt,\n options: {\n model: \"anthropic/claude-haiku-4.5\",\n ...(options.strictIsolation === true ? {\n settingSources: [],\n strictMcpConfig: true,\n } : {}),\n ...(onPreToolUse == null ? {} : {\n hooks: {\n PreToolUse: [{\n hooks: [async (input) => {\n if (input.hook_event_name !== \"PreToolUse\") {\n return { continue: true };\n }\n return await onPreToolUse(input);\n }],\n }],\n },\n }),\n allowedTools: options.allowedTools,\n permissionMode: \"dontAsk\",\n cwd: options.cwd,\n abortController,\n env: {\n ...stripClaudeCodeEnv(),\n ...(options.strictIsolation === true ? { CLAUDE_CODE_DISABLE_AUTO_MEMORY: \"1\" } : {}),\n ANTHROPIC_BASE_URL: ANTHROPIC_PROXY_BASE_URL,\n ANTHROPIC_API_KEY: \"stack-auth-proxy\",\n },\n stderr: options.stderr,\n },\n })) {\n options.onMessage?.(message);\n if (message.type === \"result\") {\n if (\"result\" in message) {\n sawResult = true;\n resultText = message.result;\n } else {\n throw new ClaudeAgentFailureError(message.subtype);\n }\n }\n }\n } catch (error) {\n if (abortController.signal.aborted && isAbortError(error)) {\n throw new ClaudeAgentTimeoutError(options.timeoutMs ?? undefined);\n }\n throw error;\n } finally {\n if (timeout != null) {\n clearTimeout(timeout);\n }\n }\n if (!sawResult) {\n throw new Error(\"Claude agent ended without reporting a result.\");\n }\n return { resultText };\n}\n\n// Only the tools actually in the agent's allowedTools list; MultiEdit and\n// NotebookEdit are intentionally excluded from the agent's tool set.\nconst FILE_MUTATING_TOOLS = new Set([\"Write\", \"Edit\"]);\n\nfunction hasStringFilePath(input: unknown): input is { file_path: string } {\n return typeof input === \"object\" && input !== null && \"file_path\" in input && typeof input.file_path === \"string\";\n}\n\nexport function getToolWriteTargetPath(toolName: string, toolInput: unknown, cwd: string): string | null {\n if (!FILE_MUTATING_TOOLS.has(toolName) || !hasStringFilePath(toolInput)) {\n return null;\n }\n return path.isAbsolute(toolInput.file_path) ? toolInput.file_path : path.resolve(cwd, toolInput.file_path);\n}\n\nexport function isPathInsideDir(dir: string, target: string): boolean {\n const relative = path.relative(path.resolve(dir), path.resolve(target));\n return relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative));\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config-file.d.ts","names":[],"sources":["../../src/config-file.ts"],"mappings":";;;iBAUgB,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,iBAuClE,mBAAA,CAAoB,cAAA,UAAwB,MAAA,EAAQ,MAAA,GAAS,OAAA"}
1
+ {"version":3,"file":"config-file.d.ts","names":[],"sources":["../../src/config-file.ts"],"mappings":";;;iBAUgB,YAAA,CAAa,KAAa;AAAA,iBAI1B,qBAAA,CAAsB,SAAiB;AAAA,iBAkBvC,sBAAA,CAAuB,cAAsB;AAAA,iBAMvC,gBAAA,CAAiB,cAAA,WAAyB,OAAO,CAAC,MAAA;AAAA,iBAIlD,cAAA,CAAe,cAAA,WAAyB,OAAO;EAAG,MAAA,EAAQ,MAAA;EAAQ,cAAA;AAAA;AAAA,iBAuClE,mBAAA,CAAoB,cAAA,UAAwB,MAAA,EAAQ,MAAA,GAAS,OAAO"}
@@ -6,7 +6,6 @@ import { isValidConfig } from "@hexclave/shared/dist/config/format";
6
6
  import { captureError } from "@hexclave/shared/dist/utils/errors";
7
7
  import { createHash } from "crypto";
8
8
  import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
9
-
10
9
  //#region src/config-file.ts
11
10
  function sha256String(value) {
12
11
  return createHash("sha256").update(value).digest("hex");
@@ -76,7 +75,7 @@ function writeFileAtomic(configFilePath, content) {
76
75
  function renderConfigObjectToFile(configFilePath, config) {
77
76
  writeFileAtomic(configFilePath, renderConfigObjectToString(configFilePath, config));
78
77
  }
79
-
80
78
  //#endregion
81
79
  export { ensureConfigFileExists, readConfigFile, readConfigObject, replaceConfigObject, resolveConfigFilePath, sha256String };
80
+
82
81
  //# sourceMappingURL=config-file.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-file.js","names":[],"sources":["../../src/config-file.ts"],"sourcesContent":["import { showOnboardingHexclaveConfigValue } from \"@hexclave/shared/dist/config-authoring\";\nimport { ConfigFileEvalError, detectImportPackageFromDir, evalConfigFileContent } from \"@hexclave/shared/dist/config-eval\";\nimport { renderConfigFileContent } from \"@hexclave/shared/dist/config-rendering\";\nimport type { Config } from \"@hexclave/shared/dist/config/format\";\nimport { isValidConfig } 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 path from \"path\";\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 // ConfigFileEvalError => \"Invalid config\"; any other loader error is captured\n // for diagnostics but not attached as `cause` (the dashboard renders causes\n // recursively and would leak framework internals).\n let parsed: ReturnType<typeof evalConfigFileContent>;\n try {\n parsed = evalConfigFileContent(content, configFilePath);\n } catch (error) {\n if (error instanceof ConfigFileEvalError) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n captureError(\"shared-backend/readConfigFile\", error);\n throw new Error(`Failed to load config file ${configFilePath}.`);\n }\n\n if (parsed === showOnboardingHexclaveConfigValue) {\n return {\n config: {},\n showOnboarding: true,\n };\n }\n if (!isValidConfig(parsed)) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n return {\n config: parsed,\n showOnboarding: false,\n };\n}\n\nexport async function replaceConfigObject(configFilePath: string, config: Config): Promise<void> {\n renderConfigObjectToFile(configFilePath, config);\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"],"mappings":";;;;;;;;;;AAUA,SAAgB,aAAa,OAAuB;AAClD,QAAO,WAAW,SAAS,CAAC,OAAO,MAAM,CAAC,OAAO,MAAM;;AAGzD,SAAgB,sBAAsB,WAA2B;CAC/D,MAAM,WAAW,KAAK,QAAQ,UAAU;AAExC,KAD4B,sBAAsB,KAAK,SAAS,CAE9D,QAAO;CAGT,MAAM,oBAAoB,KAAK,KAAK,UAAU,qBAAqB;CACnE,MAAM,kBAAkB,KAAK,KAAK,UAAU,kBAAkB;AAC9D,KAAI,WAAW,kBAAkB,CAC/B,QAAO;AAET,KAAI,WAAW,gBAAgB,CAC7B,QAAO;AAET,QAAO;;AAGT,SAAgB,uBAAuB,gBAA8B;AACnE,KAAI,WAAW,eAAe,CAAE;AAChC,WAAU,KAAK,QAAQ,eAAe,EAAE,EAAE,WAAW,MAAM,CAAC;AAC5D,0BAAyB,gBAAgB,EAAE,CAAC;;AAG9C,eAAsB,iBAAiB,gBAAyC;AAC9E,SAAQ,MAAM,eAAe,eAAe,EAAE;;AAGhD,eAAsB,eAAe,gBAA8E;AACjH,wBAAuB,eAAe;CACtC,MAAM,UAAU,aAAa,gBAAgB,QAAQ;AACrD,KAAI,QAAQ,MAAM,KAAK,GACrB,QAAO;EACL,QAAQ,EAAE;EACV,gBAAgB;EACjB;CAMH,IAAI;AACJ,KAAI;AACF,WAAS,sBAAsB,SAAS,eAAe;UAChD,OAAO;AACd,MAAI,iBAAiB,oBACnB,OAAM,IAAI,MAAM,qBAAqB,eAAe,GAAG;AAEzD,eAAa,iCAAiC,MAAM;AACpD,QAAM,IAAI,MAAM,8BAA8B,eAAe,GAAG;;AAGlE,KAAI,WAAW,kCACb,QAAO;EACL,QAAQ,EAAE;EACV,gBAAgB;EACjB;AAEH,KAAI,CAAC,cAAc,OAAO,CACxB,OAAM,IAAI,MAAM,qBAAqB,eAAe,GAAG;AAEzD,QAAO;EACL,QAAQ;EACR,gBAAgB;EACjB;;AAGH,eAAsB,oBAAoB,gBAAwB,QAA+B;AAC/F,0BAAyB,gBAAgB,OAAO;;AAGlD,SAAS,2BAA2B,gBAAwB,QAAwB;AAElF,QAAO,wBAAwB,QADT,2BAA2B,KAAK,QAAQ,eAAe,CAAC,CACzB;;AAGvD,SAAS,gBAAgB,gBAAwB,SAAuB;CACtE,MAAM,MAAM,KAAK,QAAQ,eAAe;AACxC,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CACnC,MAAM,WAAW,KAAK,KAAK,KAAK,iBAAiB,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,MAAM;AAC3F,eAAc,UAAU,SAAS,QAAQ;AACzC,KAAI;AACF,aAAW,UAAU,eAAe;UAC7B,OAAO;AACd,MAAI;AACF,UAAO,SAAS;UACV;AACR,QAAM;;;AAIV,SAAS,yBAAyB,gBAAwB,QAAsB;AAC9E,iBAAgB,gBAAgB,2BAA2B,gBAAgB,OAAO,CAAC"}
1
+ {"version":3,"file":"config-file.js","names":[],"sources":["../../src/config-file.ts"],"sourcesContent":["import { showOnboardingHexclaveConfigValue } from \"@hexclave/shared/dist/config-authoring\";\nimport { ConfigFileEvalError, detectImportPackageFromDir, evalConfigFileContent } from \"@hexclave/shared/dist/config-eval\";\nimport { renderConfigFileContent } from \"@hexclave/shared/dist/config-rendering\";\nimport type { Config } from \"@hexclave/shared/dist/config/format\";\nimport { isValidConfig } 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 path from \"path\";\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 // ConfigFileEvalError => \"Invalid config\"; any other loader error is captured\n // for diagnostics but not attached as `cause` (the dashboard renders causes\n // recursively and would leak framework internals).\n let parsed: ReturnType<typeof evalConfigFileContent>;\n try {\n parsed = evalConfigFileContent(content, configFilePath);\n } catch (error) {\n if (error instanceof ConfigFileEvalError) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n captureError(\"shared-backend/readConfigFile\", error);\n throw new Error(`Failed to load config file ${configFilePath}.`);\n }\n\n if (parsed === showOnboardingHexclaveConfigValue) {\n return {\n config: {},\n showOnboarding: true,\n };\n }\n if (!isValidConfig(parsed)) {\n throw new Error(`Invalid config in ${configFilePath}.`);\n }\n return {\n config: parsed,\n showOnboarding: false,\n };\n}\n\nexport async function replaceConfigObject(configFilePath: string, config: Config): Promise<void> {\n renderConfigObjectToFile(configFilePath, config);\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"],"mappings":";;;;;;;;;AAUA,SAAgB,aAAa,OAAuB;CAClD,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;AACxD;AAEA,SAAgB,sBAAsB,WAA2B;CAC/D,MAAM,WAAW,KAAK,QAAQ,SAAS;CAEvC,IAD4B,sBAAsB,KAAK,QACjC,GACpB,OAAO;CAGT,MAAM,oBAAoB,KAAK,KAAK,UAAU,oBAAoB;CAClE,MAAM,kBAAkB,KAAK,KAAK,UAAU,iBAAiB;CAC7D,IAAI,WAAW,iBAAiB,GAC9B,OAAO;CAET,IAAI,WAAW,eAAe,GAC5B,OAAO;CAET,OAAO;AACT;AAEA,SAAgB,uBAAuB,gBAA8B;CACnE,IAAI,WAAW,cAAc,GAAG;CAChC,UAAU,KAAK,QAAQ,cAAc,GAAG,EAAE,WAAW,KAAK,CAAC;CAC3D,yBAAyB,gBAAgB,CAAC,CAAC;AAC7C;AAEA,eAAsB,iBAAiB,gBAAyC;CAC9E,QAAQ,MAAM,eAAe,cAAc,EAAA,CAAG;AAChD;AAEA,eAAsB,eAAe,gBAA8E;CACjH,uBAAuB,cAAc;CACrC,MAAM,UAAU,aAAa,gBAAgB,OAAO;CACpD,IAAI,QAAQ,KAAK,MAAM,IACrB,OAAO;EACL,QAAQ,CAAC;EACT,gBAAgB;CAClB;CAMF,IAAI;CACJ,IAAI;EACF,SAAS,sBAAsB,SAAS,cAAc;CACxD,SAAS,OAAO;EACd,IAAI,iBAAiB,qBACnB,MAAM,IAAI,MAAM,qBAAqB,eAAe,EAAE;EAExD,aAAa,iCAAiC,KAAK;EACnD,MAAM,IAAI,MAAM,8BAA8B,eAAe,EAAE;CACjE;CAEA,IAAI,WAAW,mCACb,OAAO;EACL,QAAQ,CAAC;EACT,gBAAgB;CAClB;CAEF,IAAI,CAAC,cAAc,MAAM,GACvB,MAAM,IAAI,MAAM,qBAAqB,eAAe,EAAE;CAExD,OAAO;EACL,QAAQ;EACR,gBAAgB;CAClB;AACF;AAEA,eAAsB,oBAAoB,gBAAwB,QAA+B;CAC/F,yBAAyB,gBAAgB,MAAM;AACjD;AAEA,SAAS,2BAA2B,gBAAwB,QAAwB;CAElF,OAAO,wBAAwB,QADT,2BAA2B,KAAK,QAAQ,cAAc,CACzB,CAAC;AACtD;AAEA,SAAS,gBAAgB,gBAAwB,SAAuB;CACtE,MAAM,MAAM,KAAK,QAAQ,cAAc;CACvC,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;CAClC,MAAM,WAAW,KAAK,KAAK,KAAK,iBAAiB,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK;CAC1F,cAAc,UAAU,SAAS,OAAO;CACxC,IAAI;EACF,WAAW,UAAU,cAAc;CACrC,SAAS,OAAO;EACd,IAAI;GACF,OAAO,QAAQ;EACjB,QAAQ,CAA4B;EACpC,MAAM;CACR;AACF;AAEA,SAAS,yBAAyB,gBAAwB,QAAsB;CAC9E,gBAAgB,gBAAgB,2BAA2B,gBAAgB,MAAM,CAAC;AACpF"}
@@ -1 +1 @@
1
- {"version":3,"file":"config-updater.d.ts","names":[],"sources":["../../src/config-updater.ts"],"mappings":";;;iBA+DsB,kBAAA,CAAmB,cAAA,UAAwB,YAAA,EAAc,MAAA,GAAS,OAAA"}
1
+ {"version":3,"file":"config-updater.d.ts","names":[],"sources":["../../src/config-updater.ts"],"mappings":";;;iBA+DsB,kBAAA,CAAmB,cAAA,UAAwB,YAAA,EAAc,MAAA,GAAS,OAAO"}
@@ -4,7 +4,6 @@ import { evalConfigFileContent } from "@hexclave/shared/dist/config-eval";
4
4
  import { normalize, override } from "@hexclave/shared/dist/config/format";
5
5
  import { existsSync, readFileSync, rmSync, writeFileSync } from "fs";
6
6
  import { ensureConfigFileExists, readConfigFile } from "./config-file.js";
7
-
8
7
  //#region src/config-updater.ts
9
8
  const LOG_PREFIX = "[Hexclave config updater]";
10
9
  const DEFAULT_AGENT_TIMEOUT_MS = 12e4;
@@ -207,7 +206,7 @@ function snapshotConfigFiles(configFilePath, configContent) {
207
206
  path: resolvedConfig,
208
207
  content: configContent
209
208
  }];
210
- const seen = new Set([resolvedConfig]);
209
+ const seen = /* @__PURE__ */ new Set([resolvedConfig]);
211
210
  for (const specifier of getRelativeImportSpecifiers(configContent)) {
212
211
  const resolved = path.resolve(dir, specifier);
213
212
  if (!isPathInsideDir(dir, resolved)) continue;
@@ -321,7 +320,7 @@ function configsEqual(a, b) {
321
320
  }
322
321
  return false;
323
322
  }
324
-
325
323
  //#endregion
326
324
  export { updateConfigObject };
325
+
327
326
  //# sourceMappingURL=config-updater.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-updater.js","names":[],"sources":["../../src/config-updater.ts"],"sourcesContent":["import { evalConfigFileContent } from \"@hexclave/shared/dist/config-eval\";\nimport type { Config, ConfigValue, NormalizedConfig } from \"@hexclave/shared/dist/config/format\";\nimport { normalize, override } from \"@hexclave/shared/dist/config/format\";\nimport { existsSync, readFileSync, rmSync, writeFileSync } from \"fs\";\nimport path from \"path\";\nimport { buildCompleteConfigAgentPrompt, buildPartialConfigAgentPrompt, ClaudeAgentFailureError, ClaudeAgentTimeoutError, CONFIG_AGENT_FILE_TOOLS, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from \"./config-agent\";\nimport { ensureConfigFileExists, readConfigFile } from \"./config-file\";\n\nconst LOG_PREFIX = \"[Hexclave config updater]\";\nconst DEFAULT_AGENT_TIMEOUT_MS = 120_000;\nconst CONFIG_UPDATE_LOG_PATH_LIMIT = 40;\nconst AGENT_OUTPUT_LOG_MAX_LENGTH = 20_000;\n\ntype ConfigFileSnapshot = { path: string, content: string | null };\ntype ConfigChange = { path: string, value: ConfigValue };\n\nfunction formatConfigUpdaterErrorForLog(error: unknown): Record<string, unknown> {\n if (error instanceof Error) {\n return {\n errorName: error.name,\n errorMessage: error.message,\n errorStack: error.stack,\n };\n }\n return {\n errorMessage: String(error),\n };\n}\n\nfunction configUpdatePathDetailsForLog(changes: ConfigChange[]): Record<string, unknown> {\n const paths = changes.map(({ path: configPath }) => configPath).sort();\n return {\n configUpdatePathCount: paths.length,\n configUpdatePaths: paths.slice(0, CONFIG_UPDATE_LOG_PATH_LIMIT),\n configUpdatePathsTruncated: paths.length > CONFIG_UPDATE_LOG_PATH_LIMIT,\n };\n}\n\nfunction appendBoundedAgentOutput(current: string, chunk: string): string {\n const next = `${current}${chunk}`;\n if (next.length <= AGENT_OUTPUT_LOG_MAX_LENGTH) {\n return next;\n }\n return next.slice(next.length - AGENT_OUTPUT_LOG_MAX_LENGTH);\n}\n\nfunction stringifyAgentMessageForLog(message: unknown): string {\n try {\n return `${JSON.stringify(message)}\\n`;\n } catch {\n return `${String(message)}\\n`;\n }\n}\n\nfunction agentOutputDetailsForLog(agentStdout: string, agentStderr: string): Record<string, unknown> {\n return {\n agentStdout,\n agentStdoutTruncated: agentStdout.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,\n agentStderr,\n agentStderrTruncated: agentStderr.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,\n };\n}\n\nexport async function updateConfigObject(configFilePath: string, configUpdate: Config): Promise<void> {\n const startedAtMs = performance.now();\n ensureConfigFileExists(configFilePath);\n\n const changes = flattenConfigUpdate(configUpdate);\n if (changes.length === 0) {\n console.log(`${LOG_PREFIX} Skipping config update because it contains no changes`, {\n configFilePath,\n });\n return;\n }\n const updateLogDetails = {\n configFilePath,\n ...configUpdatePathDetailsForLog(changes),\n };\n console.log(`${LOG_PREFIX} Starting config file update`, updateLogDetails);\n\n const content = readFileSync(configFilePath, \"utf-8\");\n\n // One write path, always: hand the change to the AI agent so it edits the file\n // in place and preserves its authoring (helper wrappers, imports, comments,\n // layout). There is deliberately no deterministic \"fast path\" — re-rendering a\n // config would flatten and destroy hand-authored files. Reads use jiti\n // (see readConfigFile); writes go through the agent.\n console.log(`${LOG_PREFIX} Applying config update with agent-assisted rewrite`, {\n ...updateLogDetails,\n configDirectory: path.dirname(configFilePath),\n });\n const baselineConfig = await tryReadConfigForValidation(configFilePath);\n const { snapshots, seen } = snapshotConfigFiles(configFilePath, content);\n try {\n await runConfigUpdateAgent({\n prompt: buildConfigUpdatePrompt(path.basename(configFilePath), configUpdate, baselineConfig),\n cwd: path.dirname(configFilePath),\n onFileWillChange: (filePath) => captureSnapshotIfAbsent(snapshots, filePath, seen),\n });\n await validateAgentUpdate(configFilePath, baselineConfig, configUpdate);\n } catch (error) {\n console.warn(`${LOG_PREFIX} Config update failed; restoring files from snapshots`, {\n ...updateLogDetails,\n snapshotCount: snapshots.length,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n });\n try {\n restoreConfigFiles(snapshots);\n console.warn(`${LOG_PREFIX} Restored files after failed config update`, {\n ...updateLogDetails,\n snapshotCount: snapshots.length,\n });\n } catch (restoreError) {\n console.error(`${LOG_PREFIX} Failed to fully roll back config files after a failed update of ${configFilePath}; some files may be left in a partially-restored state`, {\n configFilePath,\n ...formatConfigUpdaterErrorForLog(restoreError),\n });\n }\n throw error;\n }\n console.log(`${LOG_PREFIX} Finished config update with agent-assisted rewrite`, {\n ...updateLogDetails,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n snapshotCount: snapshots.length,\n });\n}\n\nasync function runConfigUpdateAgent(options: {\n prompt: string,\n cwd: string,\n onFileWillChange?: (filePath: string) => void,\n}): Promise<void> {\n const timeoutMs = parseAgentTimeoutMs();\n const deniedOutOfBoundsWrites = new Set<string>();\n const startedAtMs = performance.now();\n let agentStdout = \"\";\n let agentStderr = \"\";\n console.log(`${LOG_PREFIX} Starting config update agent`, {\n cwd: options.cwd,\n timeoutMs,\n });\n try {\n await runHeadlessClaudeAgent({\n prompt: options.prompt,\n cwd: options.cwd,\n allowedTools: [...CONFIG_AGENT_FILE_TOOLS],\n strictIsolation: true,\n timeoutMs,\n stderr: (data) => {\n agentStderr = appendBoundedAgentOutput(agentStderr, data);\n console.warn(`${LOG_PREFIX} [agent] ${data}`);\n },\n onMessage: (message) => {\n agentStdout = appendBoundedAgentOutput(agentStdout, stringifyAgentMessageForLog(message));\n },\n onPreToolUse: (input) => {\n const target = getToolWriteTargetPath(input.tool_name, input.tool_input, options.cwd);\n if (target == null) return { continue: true };\n if (!isPathInsideDir(options.cwd, target)) {\n deniedOutOfBoundsWrites.add(target);\n return {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"deny\",\n permissionDecisionReason: `Refusing to modify ${target}: config updates may only change files inside the config directory.`,\n },\n };\n }\n options.onFileWillChange?.(target);\n return { continue: true };\n },\n });\n } catch (error) {\n if (error instanceof ClaudeAgentTimeoutError) {\n console.warn(`${LOG_PREFIX} Config update agent timed out`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw new Error(`Config update agent timed out after ${timeoutMs}ms. It was unable to apply the config changes to the file.`);\n }\n if (error instanceof ClaudeAgentFailureError) {\n console.warn(`${LOG_PREFIX} Config update agent failed`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw new Error(`${error.message} It was unable to apply the config changes to the file.`);\n }\n console.warn(`${LOG_PREFIX} Config update agent failed unexpectedly`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw error;\n }\n console.log(`${LOG_PREFIX} Finished config update agent`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,\n });\n if (deniedOutOfBoundsWrites.size > 0) {\n console.warn(`${LOG_PREFIX} Config update agent attempted out-of-bounds writes`, {\n cwd: options.cwd,\n deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,\n deniedOutOfBoundsWrites: [...deniedOutOfBoundsWrites],\n });\n throw new Error(`Config update agent tried to modify ${deniedOutOfBoundsWrites.size} file(s) outside the config directory, which is not allowed: ${[...deniedOutOfBoundsWrites].join(\", \")}. The config was not updated.`);\n }\n}\n\nfunction parseAgentTimeoutMs(): number {\n const raw = process.env.STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS;\n if (raw == null || raw.trim() === \"\") return DEFAULT_AGENT_TIMEOUT_MS;\n const parsed = Number(raw);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new Error(`Invalid STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS: ${JSON.stringify(raw)}. Expected a positive number of milliseconds.`);\n }\n return parsed;\n}\n\nfunction captureSnapshotIfAbsent(snapshots: ConfigFileSnapshot[], filePath: string, seen: Set<string>): void {\n const resolved = path.resolve(filePath);\n if (seen.has(resolved)) return;\n seen.add(resolved);\n snapshots.push({ path: resolved, content: existsSync(resolved) ? readFileSync(resolved, \"utf-8\") : null });\n}\n\nfunction snapshotConfigFiles(configFilePath: string, configContent: string): { snapshots: ConfigFileSnapshot[]; seen: Set<string> } {\n const dir = path.dirname(configFilePath);\n const resolvedConfig = path.resolve(configFilePath);\n const snapshots: ConfigFileSnapshot[] = [{ path: resolvedConfig, content: configContent }];\n const seen = new Set<string>([resolvedConfig]);\n for (const specifier of getRelativeImportSpecifiers(configContent)) {\n const resolved = path.resolve(dir, specifier);\n if (!isPathInsideDir(dir, resolved)) continue;\n captureSnapshotIfAbsent(snapshots, resolved, seen);\n }\n return { snapshots, seen };\n}\n\nfunction restoreConfigFiles(snapshots: ConfigFileSnapshot[]): void {\n const failures: string[] = [];\n for (const { path: filePath, content } of snapshots) {\n try {\n if (content === null) {\n if (existsSync(filePath)) rmSync(filePath);\n } else {\n writeFileSync(filePath, content, \"utf-8\");\n }\n } catch (error) {\n failures.push(`${filePath}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n if (failures.length > 0) {\n throw new Error(`Failed to restore ${failures.length} file(s) during rollback: ${failures.join(\"; \")}`);\n }\n}\n\nasync function tryReadConfigForValidation(configFilePath: string): Promise<Config | null> {\n try {\n return (await readConfigFile(configFilePath)).config;\n } catch (error) {\n console.warn(`${LOG_PREFIX} Could not evaluate config for validation baseline; will fall back to a structural check`, {\n configFilePath,\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n}\n\nasync function validateAgentUpdate(configFilePath: string, baselineConfig: Config | null, configUpdate: Config): Promise<void> {\n if (baselineConfig != null) {\n const target = canonicalizeConfig(override(baselineConfig, configUpdate));\n const result = canonicalizeConfig((await readConfigFile(configFilePath)).config);\n if (!configsEqual(result, target)) {\n throw new Error(`Config update validation failed for ${configFilePath}: the updated file does not evaluate to the expected configuration.`);\n }\n return;\n }\n\n // Structural-only fallback when jiti can't evaluate the config (e.g. import-with\n // assets): we can't verify values, only that the file still exports `config`.\n const content = readFileSync(configFilePath, \"utf-8\");\n if (!configFileExportsConfig(content, configFilePath)) {\n throw new Error(`Config update validation failed for ${configFilePath}: the updated file no longer exports a valid \\`config\\`.`);\n }\n}\n\nfunction configFileExportsConfig(content: string, configFilePath: string): boolean {\n try {\n evalConfigFileContent(content, configFilePath);\n return true;\n } catch {\n // jiti may fail to resolve imports valid in the user's project but absent\n // here (relative assets, workspace packages). For the structural check we\n // only need a runtime `config` binding to still exist; the agent always\n // authors `export const config`.\n return /\\bexport\\s+const\\s+config\\b/.test(content);\n }\n}\n\nfunction getRelativeImportSpecifiers(content: string): string[] {\n const specifiers: string[] = [];\n const importPattern = /\\bimport\\b(?:[^'\"]*?\\bfrom\\s*)?[\"'](\\.{1,2}\\/[^\"']+)[\"']/g;\n let match: RegExpExecArray | null;\n while ((match = importPattern.exec(content)) !== null) {\n specifiers.push(match[1]);\n }\n return specifiers;\n}\n\nfunction flattenConfigUpdate(update: Config): ConfigChange[] {\n const changes: ConfigChange[] = [];\n const walk = (prefix: string, obj: Config): void => {\n for (const [key, value] of Object.entries(obj)) {\n const fullPath = prefix === \"\" ? key : `${prefix}.${key}`;\n if (value === undefined) continue;\n if (value !== null && typeof value === \"object\" && !Array.isArray(value) && Object.keys(value).length > 0) {\n walk(fullPath, value);\n } else {\n changes.push({ path: fullPath, value });\n }\n }\n };\n walk(\"\", update);\n return changes;\n}\n\nfunction buildConfigUpdatePrompt(configFileName: string, configUpdate: Config, baselineConfig: Config | null): string {\n const changes = flattenConfigUpdate(configUpdate);\n const commandPolicy = \"Do not run shell commands and do not create files other than what is required to apply the config changes.\";\n if (baselineConfig != null) {\n return buildCompleteConfigAgentPrompt({\n scope: { mode: \"known-file\", configFileName },\n completeConfig: canonicalizeConfig(override(baselineConfig, configUpdate)),\n commandPolicy,\n });\n }\n return buildPartialConfigAgentPrompt({\n configFileName,\n changes,\n commandPolicy,\n });\n}\n\nfunction canonicalizeConfig(config: Config): NormalizedConfig {\n const droppedKeys: string[] = [];\n const normalized = normalize(config, {\n onDotIntoNonObject: \"ignore\",\n onDotIntoNull: \"empty-object\",\n droppedKeys,\n });\n if (droppedKeys.length > 0) {\n throw new Error(`Config update has conflicting keys that would be dropped during normalization: ${droppedKeys.map((key) => JSON.stringify(key)).join(\", \")}`);\n }\n return normalized;\n}\n\nfunction configsEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return a === b;\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;\n return a.every((value, index) => configsEqual(value, b[index]));\n }\n if (typeof a === \"object\" && typeof b === \"object\") {\n const aEntries = Object.entries(a);\n const bMap = new Map(Object.entries(b));\n if (aEntries.length !== bMap.size) return false;\n return aEntries.every(([key, value]) => bMap.has(key) && configsEqual(value, bMap.get(key)));\n }\n return false;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config-updater.js","names":[],"sources":["../../src/config-updater.ts"],"sourcesContent":["import { evalConfigFileContent } from \"@hexclave/shared/dist/config-eval\";\nimport type { Config, ConfigValue, NormalizedConfig } from \"@hexclave/shared/dist/config/format\";\nimport { normalize, override } from \"@hexclave/shared/dist/config/format\";\nimport { existsSync, readFileSync, rmSync, writeFileSync } from \"fs\";\nimport path from \"path\";\nimport { buildCompleteConfigAgentPrompt, buildPartialConfigAgentPrompt, ClaudeAgentFailureError, ClaudeAgentTimeoutError, CONFIG_AGENT_FILE_TOOLS, getToolWriteTargetPath, isPathInsideDir, runHeadlessClaudeAgent } from \"./config-agent\";\nimport { ensureConfigFileExists, readConfigFile } from \"./config-file\";\n\nconst LOG_PREFIX = \"[Hexclave config updater]\";\nconst DEFAULT_AGENT_TIMEOUT_MS = 120_000;\nconst CONFIG_UPDATE_LOG_PATH_LIMIT = 40;\nconst AGENT_OUTPUT_LOG_MAX_LENGTH = 20_000;\n\ntype ConfigFileSnapshot = { path: string, content: string | null };\ntype ConfigChange = { path: string, value: ConfigValue };\n\nfunction formatConfigUpdaterErrorForLog(error: unknown): Record<string, unknown> {\n if (error instanceof Error) {\n return {\n errorName: error.name,\n errorMessage: error.message,\n errorStack: error.stack,\n };\n }\n return {\n errorMessage: String(error),\n };\n}\n\nfunction configUpdatePathDetailsForLog(changes: ConfigChange[]): Record<string, unknown> {\n const paths = changes.map(({ path: configPath }) => configPath).sort();\n return {\n configUpdatePathCount: paths.length,\n configUpdatePaths: paths.slice(0, CONFIG_UPDATE_LOG_PATH_LIMIT),\n configUpdatePathsTruncated: paths.length > CONFIG_UPDATE_LOG_PATH_LIMIT,\n };\n}\n\nfunction appendBoundedAgentOutput(current: string, chunk: string): string {\n const next = `${current}${chunk}`;\n if (next.length <= AGENT_OUTPUT_LOG_MAX_LENGTH) {\n return next;\n }\n return next.slice(next.length - AGENT_OUTPUT_LOG_MAX_LENGTH);\n}\n\nfunction stringifyAgentMessageForLog(message: unknown): string {\n try {\n return `${JSON.stringify(message)}\\n`;\n } catch {\n return `${String(message)}\\n`;\n }\n}\n\nfunction agentOutputDetailsForLog(agentStdout: string, agentStderr: string): Record<string, unknown> {\n return {\n agentStdout,\n agentStdoutTruncated: agentStdout.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,\n agentStderr,\n agentStderrTruncated: agentStderr.length >= AGENT_OUTPUT_LOG_MAX_LENGTH,\n };\n}\n\nexport async function updateConfigObject(configFilePath: string, configUpdate: Config): Promise<void> {\n const startedAtMs = performance.now();\n ensureConfigFileExists(configFilePath);\n\n const changes = flattenConfigUpdate(configUpdate);\n if (changes.length === 0) {\n console.log(`${LOG_PREFIX} Skipping config update because it contains no changes`, {\n configFilePath,\n });\n return;\n }\n const updateLogDetails = {\n configFilePath,\n ...configUpdatePathDetailsForLog(changes),\n };\n console.log(`${LOG_PREFIX} Starting config file update`, updateLogDetails);\n\n const content = readFileSync(configFilePath, \"utf-8\");\n\n // One write path, always: hand the change to the AI agent so it edits the file\n // in place and preserves its authoring (helper wrappers, imports, comments,\n // layout). There is deliberately no deterministic \"fast path\" — re-rendering a\n // config would flatten and destroy hand-authored files. Reads use jiti\n // (see readConfigFile); writes go through the agent.\n console.log(`${LOG_PREFIX} Applying config update with agent-assisted rewrite`, {\n ...updateLogDetails,\n configDirectory: path.dirname(configFilePath),\n });\n const baselineConfig = await tryReadConfigForValidation(configFilePath);\n const { snapshots, seen } = snapshotConfigFiles(configFilePath, content);\n try {\n await runConfigUpdateAgent({\n prompt: buildConfigUpdatePrompt(path.basename(configFilePath), configUpdate, baselineConfig),\n cwd: path.dirname(configFilePath),\n onFileWillChange: (filePath) => captureSnapshotIfAbsent(snapshots, filePath, seen),\n });\n await validateAgentUpdate(configFilePath, baselineConfig, configUpdate);\n } catch (error) {\n console.warn(`${LOG_PREFIX} Config update failed; restoring files from snapshots`, {\n ...updateLogDetails,\n snapshotCount: snapshots.length,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n });\n try {\n restoreConfigFiles(snapshots);\n console.warn(`${LOG_PREFIX} Restored files after failed config update`, {\n ...updateLogDetails,\n snapshotCount: snapshots.length,\n });\n } catch (restoreError) {\n console.error(`${LOG_PREFIX} Failed to fully roll back config files after a failed update of ${configFilePath}; some files may be left in a partially-restored state`, {\n configFilePath,\n ...formatConfigUpdaterErrorForLog(restoreError),\n });\n }\n throw error;\n }\n console.log(`${LOG_PREFIX} Finished config update with agent-assisted rewrite`, {\n ...updateLogDetails,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n snapshotCount: snapshots.length,\n });\n}\n\nasync function runConfigUpdateAgent(options: {\n prompt: string,\n cwd: string,\n onFileWillChange?: (filePath: string) => void,\n}): Promise<void> {\n const timeoutMs = parseAgentTimeoutMs();\n const deniedOutOfBoundsWrites = new Set<string>();\n const startedAtMs = performance.now();\n let agentStdout = \"\";\n let agentStderr = \"\";\n console.log(`${LOG_PREFIX} Starting config update agent`, {\n cwd: options.cwd,\n timeoutMs,\n });\n try {\n await runHeadlessClaudeAgent({\n prompt: options.prompt,\n cwd: options.cwd,\n allowedTools: [...CONFIG_AGENT_FILE_TOOLS],\n strictIsolation: true,\n timeoutMs,\n stderr: (data) => {\n agentStderr = appendBoundedAgentOutput(agentStderr, data);\n console.warn(`${LOG_PREFIX} [agent] ${data}`);\n },\n onMessage: (message) => {\n agentStdout = appendBoundedAgentOutput(agentStdout, stringifyAgentMessageForLog(message));\n },\n onPreToolUse: (input) => {\n const target = getToolWriteTargetPath(input.tool_name, input.tool_input, options.cwd);\n if (target == null) return { continue: true };\n if (!isPathInsideDir(options.cwd, target)) {\n deniedOutOfBoundsWrites.add(target);\n return {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"deny\",\n permissionDecisionReason: `Refusing to modify ${target}: config updates may only change files inside the config directory.`,\n },\n };\n }\n options.onFileWillChange?.(target);\n return { continue: true };\n },\n });\n } catch (error) {\n if (error instanceof ClaudeAgentTimeoutError) {\n console.warn(`${LOG_PREFIX} Config update agent timed out`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw new Error(`Config update agent timed out after ${timeoutMs}ms. It was unable to apply the config changes to the file.`);\n }\n if (error instanceof ClaudeAgentFailureError) {\n console.warn(`${LOG_PREFIX} Config update agent failed`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw new Error(`${error.message} It was unable to apply the config changes to the file.`);\n }\n console.warn(`${LOG_PREFIX} Config update agent failed unexpectedly`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n ...formatConfigUpdaterErrorForLog(error),\n ...agentOutputDetailsForLog(agentStdout, agentStderr),\n });\n throw error;\n }\n console.log(`${LOG_PREFIX} Finished config update agent`, {\n cwd: options.cwd,\n timeoutMs,\n elapsedMs: Math.round(performance.now() - startedAtMs),\n deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,\n });\n if (deniedOutOfBoundsWrites.size > 0) {\n console.warn(`${LOG_PREFIX} Config update agent attempted out-of-bounds writes`, {\n cwd: options.cwd,\n deniedOutOfBoundsWriteCount: deniedOutOfBoundsWrites.size,\n deniedOutOfBoundsWrites: [...deniedOutOfBoundsWrites],\n });\n throw new Error(`Config update agent tried to modify ${deniedOutOfBoundsWrites.size} file(s) outside the config directory, which is not allowed: ${[...deniedOutOfBoundsWrites].join(\", \")}. The config was not updated.`);\n }\n}\n\nfunction parseAgentTimeoutMs(): number {\n const raw = process.env.STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS;\n if (raw == null || raw.trim() === \"\") return DEFAULT_AGENT_TIMEOUT_MS;\n const parsed = Number(raw);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new Error(`Invalid STACK_CONFIG_UPDATE_AGENT_TIMEOUT_MS: ${JSON.stringify(raw)}. Expected a positive number of milliseconds.`);\n }\n return parsed;\n}\n\nfunction captureSnapshotIfAbsent(snapshots: ConfigFileSnapshot[], filePath: string, seen: Set<string>): void {\n const resolved = path.resolve(filePath);\n if (seen.has(resolved)) return;\n seen.add(resolved);\n snapshots.push({ path: resolved, content: existsSync(resolved) ? readFileSync(resolved, \"utf-8\") : null });\n}\n\nfunction snapshotConfigFiles(configFilePath: string, configContent: string): { snapshots: ConfigFileSnapshot[]; seen: Set<string> } {\n const dir = path.dirname(configFilePath);\n const resolvedConfig = path.resolve(configFilePath);\n const snapshots: ConfigFileSnapshot[] = [{ path: resolvedConfig, content: configContent }];\n const seen = new Set<string>([resolvedConfig]);\n for (const specifier of getRelativeImportSpecifiers(configContent)) {\n const resolved = path.resolve(dir, specifier);\n if (!isPathInsideDir(dir, resolved)) continue;\n captureSnapshotIfAbsent(snapshots, resolved, seen);\n }\n return { snapshots, seen };\n}\n\nfunction restoreConfigFiles(snapshots: ConfigFileSnapshot[]): void {\n const failures: string[] = [];\n for (const { path: filePath, content } of snapshots) {\n try {\n if (content === null) {\n if (existsSync(filePath)) rmSync(filePath);\n } else {\n writeFileSync(filePath, content, \"utf-8\");\n }\n } catch (error) {\n failures.push(`${filePath}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n if (failures.length > 0) {\n throw new Error(`Failed to restore ${failures.length} file(s) during rollback: ${failures.join(\"; \")}`);\n }\n}\n\nasync function tryReadConfigForValidation(configFilePath: string): Promise<Config | null> {\n try {\n return (await readConfigFile(configFilePath)).config;\n } catch (error) {\n console.warn(`${LOG_PREFIX} Could not evaluate config for validation baseline; will fall back to a structural check`, {\n configFilePath,\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n}\n\nasync function validateAgentUpdate(configFilePath: string, baselineConfig: Config | null, configUpdate: Config): Promise<void> {\n if (baselineConfig != null) {\n const target = canonicalizeConfig(override(baselineConfig, configUpdate));\n const result = canonicalizeConfig((await readConfigFile(configFilePath)).config);\n if (!configsEqual(result, target)) {\n throw new Error(`Config update validation failed for ${configFilePath}: the updated file does not evaluate to the expected configuration.`);\n }\n return;\n }\n\n // Structural-only fallback when jiti can't evaluate the config (e.g. import-with\n // assets): we can't verify values, only that the file still exports `config`.\n const content = readFileSync(configFilePath, \"utf-8\");\n if (!configFileExportsConfig(content, configFilePath)) {\n throw new Error(`Config update validation failed for ${configFilePath}: the updated file no longer exports a valid \\`config\\`.`);\n }\n}\n\nfunction configFileExportsConfig(content: string, configFilePath: string): boolean {\n try {\n evalConfigFileContent(content, configFilePath);\n return true;\n } catch {\n // jiti may fail to resolve imports valid in the user's project but absent\n // here (relative assets, workspace packages). For the structural check we\n // only need a runtime `config` binding to still exist; the agent always\n // authors `export const config`.\n return /\\bexport\\s+const\\s+config\\b/.test(content);\n }\n}\n\nfunction getRelativeImportSpecifiers(content: string): string[] {\n const specifiers: string[] = [];\n const importPattern = /\\bimport\\b(?:[^'\"]*?\\bfrom\\s*)?[\"'](\\.{1,2}\\/[^\"']+)[\"']/g;\n let match: RegExpExecArray | null;\n while ((match = importPattern.exec(content)) !== null) {\n specifiers.push(match[1]);\n }\n return specifiers;\n}\n\nfunction flattenConfigUpdate(update: Config): ConfigChange[] {\n const changes: ConfigChange[] = [];\n const walk = (prefix: string, obj: Config): void => {\n for (const [key, value] of Object.entries(obj)) {\n const fullPath = prefix === \"\" ? key : `${prefix}.${key}`;\n if (value === undefined) continue;\n if (value !== null && typeof value === \"object\" && !Array.isArray(value) && Object.keys(value).length > 0) {\n walk(fullPath, value);\n } else {\n changes.push({ path: fullPath, value });\n }\n }\n };\n walk(\"\", update);\n return changes;\n}\n\nfunction buildConfigUpdatePrompt(configFileName: string, configUpdate: Config, baselineConfig: Config | null): string {\n const changes = flattenConfigUpdate(configUpdate);\n const commandPolicy = \"Do not run shell commands and do not create files other than what is required to apply the config changes.\";\n if (baselineConfig != null) {\n return buildCompleteConfigAgentPrompt({\n scope: { mode: \"known-file\", configFileName },\n completeConfig: canonicalizeConfig(override(baselineConfig, configUpdate)),\n commandPolicy,\n });\n }\n return buildPartialConfigAgentPrompt({\n configFileName,\n changes,\n commandPolicy,\n });\n}\n\nfunction canonicalizeConfig(config: Config): NormalizedConfig {\n const droppedKeys: string[] = [];\n const normalized = normalize(config, {\n onDotIntoNonObject: \"ignore\",\n onDotIntoNull: \"empty-object\",\n droppedKeys,\n });\n if (droppedKeys.length > 0) {\n throw new Error(`Config update has conflicting keys that would be dropped during normalization: ${droppedKeys.map((key) => JSON.stringify(key)).join(\", \")}`);\n }\n return normalized;\n}\n\nfunction configsEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return a === b;\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;\n return a.every((value, index) => configsEqual(value, b[index]));\n }\n if (typeof a === \"object\" && typeof b === \"object\") {\n const aEntries = Object.entries(a);\n const bMap = new Map(Object.entries(b));\n if (aEntries.length !== bMap.size) return false;\n return aEntries.every(([key, value]) => bMap.has(key) && configsEqual(value, bMap.get(key)));\n }\n return false;\n}\n"],"mappings":";;;;;;;AAQA,MAAM,aAAa;AACnB,MAAM,2BAA2B;AACjC,MAAM,+BAA+B;AACrC,MAAM,8BAA8B;AAKpC,SAAS,+BAA+B,OAAyC;CAC/E,IAAI,iBAAiB,OACnB,OAAO;EACL,WAAW,MAAM;EACjB,cAAc,MAAM;EACpB,YAAY,MAAM;CACpB;CAEF,OAAO,EACL,cAAc,OAAO,KAAK,EAC5B;AACF;AAEA,SAAS,8BAA8B,SAAkD;CACvF,MAAM,QAAQ,QAAQ,KAAK,EAAE,MAAM,iBAAiB,UAAU,CAAC,CAAC,KAAK;CACrE,OAAO;EACL,uBAAuB,MAAM;EAC7B,mBAAmB,MAAM,MAAM,GAAG,4BAA4B;EAC9D,4BAA4B,MAAM,SAAS;CAC7C;AACF;AAEA,SAAS,yBAAyB,SAAiB,OAAuB;CACxE,MAAM,OAAO,GAAG,UAAU;CAC1B,IAAI,KAAK,UAAU,6BACjB,OAAO;CAET,OAAO,KAAK,MAAM,KAAK,SAAS,2BAA2B;AAC7D;AAEA,SAAS,4BAA4B,SAA0B;CAC7D,IAAI;EACF,OAAO,GAAG,KAAK,UAAU,OAAO,EAAE;CACpC,QAAQ;EACN,OAAO,GAAG,OAAO,OAAO,EAAE;CAC5B;AACF;AAEA,SAAS,yBAAyB,aAAqB,aAA8C;CACnG,OAAO;EACL;EACA,sBAAsB,YAAY,UAAU;EAC5C;EACA,sBAAsB,YAAY,UAAU;CAC9C;AACF;AAEA,eAAsB,mBAAmB,gBAAwB,cAAqC;CACpG,MAAM,cAAc,YAAY,IAAI;CACpC,uBAAuB,cAAc;CAErC,MAAM,UAAU,oBAAoB,YAAY;CAChD,IAAI,QAAQ,WAAW,GAAG;EACxB,QAAQ,IAAI,GAAG,WAAW,yDAAyD,EACjF,eACF,CAAC;EACD;CACF;CACA,MAAM,mBAAmB;EACvB;EACA,GAAG,8BAA8B,OAAO;CAC1C;CACA,QAAQ,IAAI,GAAG,WAAW,+BAA+B,gBAAgB;CAEzE,MAAM,UAAU,aAAa,gBAAgB,OAAO;CAOpD,QAAQ,IAAI,GAAG,WAAW,sDAAsD;EAC9E,GAAG;EACH,iBAAiB,KAAK,QAAQ,cAAc;CAC9C,CAAC;CACD,MAAM,iBAAiB,MAAM,2BAA2B,cAAc;CACtE,MAAM,EAAE,WAAW,SAAS,oBAAoB,gBAAgB,OAAO;CACvE,IAAI;EACF,MAAM,qBAAqB;GACzB,QAAQ,wBAAwB,KAAK,SAAS,cAAc,GAAG,cAAc,cAAc;GAC3F,KAAK,KAAK,QAAQ,cAAc;GAChC,mBAAmB,aAAa,wBAAwB,WAAW,UAAU,IAAI;EACnF,CAAC;EACD,MAAM,oBAAoB,gBAAgB,gBAAgB,YAAY;CACxE,SAAS,OAAO;EACd,QAAQ,KAAK,GAAG,WAAW,wDAAwD;GACjF,GAAG;GACH,eAAe,UAAU;GACzB,WAAW,KAAK,MAAM,YAAY,IAAI,IAAI,WAAW;GACrD,GAAG,+BAA+B,KAAK;EACzC,CAAC;EACD,IAAI;GACF,mBAAmB,SAAS;GAC5B,QAAQ,KAAK,GAAG,WAAW,6CAA6C;IACtE,GAAG;IACH,eAAe,UAAU;GAC3B,CAAC;EACH,SAAS,cAAc;GACrB,QAAQ,MAAM,GAAG,WAAW,mEAAmE,eAAe,yDAAyD;IACrK;IACA,GAAG,+BAA+B,YAAY;GAChD,CAAC;EACH;EACA,MAAM;CACR;CACA,QAAQ,IAAI,GAAG,WAAW,sDAAsD;EAC9E,GAAG;EACH,WAAW,KAAK,MAAM,YAAY,IAAI,IAAI,WAAW;EACrD,eAAe,UAAU;CAC3B,CAAC;AACH;AAEA,eAAe,qBAAqB,SAIlB;CAChB,MAAM,YAAY,oBAAoB;CACtC,MAAM,0CAA0B,IAAI,IAAY;CAChD,MAAM,cAAc,YAAY,IAAI;CACpC,IAAI,cAAc;CAClB,IAAI,cAAc;CAClB,QAAQ,IAAI,GAAG,WAAW,gCAAgC;EACxD,KAAK,QAAQ;EACb;CACF,CAAC;CACD,IAAI;EACF,MAAM,uBAAuB;GAC3B,QAAQ,QAAQ;GAChB,KAAK,QAAQ;GACb,cAAc,CAAC,GAAG,uBAAuB;GACzC,iBAAiB;GACjB;GACA,SAAS,SAAS;IAChB,cAAc,yBAAyB,aAAa,IAAI;IACxD,QAAQ,KAAK,GAAG,WAAW,WAAW,MAAM;GAC9C;GACA,YAAY,YAAY;IACtB,cAAc,yBAAyB,aAAa,4BAA4B,OAAO,CAAC;GAC1F;GACA,eAAe,UAAU;IACvB,MAAM,SAAS,uBAAuB,MAAM,WAAW,MAAM,YAAY,QAAQ,GAAG;IACpF,IAAI,UAAU,MAAM,OAAO,EAAE,UAAU,KAAK;IAC5C,IAAI,CAAC,gBAAgB,QAAQ,KAAK,MAAM,GAAG;KACzC,wBAAwB,IAAI,MAAM;KAClC,OAAO,EACL,oBAAoB;MAClB,eAAe;MACf,oBAAoB;MACpB,0BAA0B,sBAAsB,OAAO;KACzD,EACF;IACF;IACA,QAAQ,mBAAmB,MAAM;IACjC,OAAO,EAAE,UAAU,KAAK;GAC1B;EACF,CAAC;CACH,SAAS,OAAO;EACd,IAAI,iBAAiB,yBAAyB;GAC5C,QAAQ,KAAK,GAAG,WAAW,iCAAiC;IAC1D,KAAK,QAAQ;IACb;IACA,WAAW,KAAK,MAAM,YAAY,IAAI,IAAI,WAAW;IACrD,GAAG,+BAA+B,KAAK;IACvC,GAAG,yBAAyB,aAAa,WAAW;GACtD,CAAC;GACD,MAAM,IAAI,MAAM,uCAAuC,UAAU,2DAA2D;EAC9H;EACA,IAAI,iBAAiB,yBAAyB;GAC5C,QAAQ,KAAK,GAAG,WAAW,8BAA8B;IACvD,KAAK,QAAQ;IACb;IACA,WAAW,KAAK,MAAM,YAAY,IAAI,IAAI,WAAW;IACrD,GAAG,+BAA+B,KAAK;IACvC,GAAG,yBAAyB,aAAa,WAAW;GACtD,CAAC;GACD,MAAM,IAAI,MAAM,GAAG,MAAM,QAAQ,wDAAwD;EAC3F;EACA,QAAQ,KAAK,GAAG,WAAW,2CAA2C;GACpE,KAAK,QAAQ;GACb;GACA,WAAW,KAAK,MAAM,YAAY,IAAI,IAAI,WAAW;GACrD,GAAG,+BAA+B,KAAK;GACvC,GAAG,yBAAyB,aAAa,WAAW;EACtD,CAAC;EACD,MAAM;CACR;CACA,QAAQ,IAAI,GAAG,WAAW,gCAAgC;EACxD,KAAK,QAAQ;EACb;EACA,WAAW,KAAK,MAAM,YAAY,IAAI,IAAI,WAAW;EACrD,6BAA6B,wBAAwB;CACvD,CAAC;CACD,IAAI,wBAAwB,OAAO,GAAG;EACpC,QAAQ,KAAK,GAAG,WAAW,sDAAsD;GAC/E,KAAK,QAAQ;GACb,6BAA6B,wBAAwB;GACrD,yBAAyB,CAAC,GAAG,uBAAuB;EACtD,CAAC;EACD,MAAM,IAAI,MAAM,uCAAuC,wBAAwB,KAAK,+DAA+D,CAAC,GAAG,uBAAuB,CAAC,CAAC,KAAK,IAAI,EAAE,8BAA8B;CAC3N;AACF;AAEA,SAAS,sBAA8B;CACrC,MAAM,MAAM,QAAQ,IAAI;CACxB,IAAI,OAAO,QAAQ,IAAI,KAAK,MAAM,IAAI,OAAO;CAC7C,MAAM,SAAS,OAAO,GAAG;CACzB,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GACxC,MAAM,IAAI,MAAM,iDAAiD,KAAK,UAAU,GAAG,EAAE,8CAA8C;CAErI,OAAO;AACT;AAEA,SAAS,wBAAwB,WAAiC,UAAkB,MAAyB;CAC3G,MAAM,WAAW,KAAK,QAAQ,QAAQ;CACtC,IAAI,KAAK,IAAI,QAAQ,GAAG;CACxB,KAAK,IAAI,QAAQ;CACjB,UAAU,KAAK;EAAE,MAAM;EAAU,SAAS,WAAW,QAAQ,IAAI,aAAa,UAAU,OAAO,IAAI;CAAK,CAAC;AAC3G;AAEA,SAAS,oBAAoB,gBAAwB,eAA+E;CAClI,MAAM,MAAM,KAAK,QAAQ,cAAc;CACvC,MAAM,iBAAiB,KAAK,QAAQ,cAAc;CAClD,MAAM,YAAkC,CAAC;EAAE,MAAM;EAAgB,SAAS;CAAc,CAAC;CACzF,MAAM,uBAAO,IAAI,IAAY,CAAC,cAAc,CAAC;CAC7C,KAAK,MAAM,aAAa,4BAA4B,aAAa,GAAG;EAClE,MAAM,WAAW,KAAK,QAAQ,KAAK,SAAS;EAC5C,IAAI,CAAC,gBAAgB,KAAK,QAAQ,GAAG;EACrC,wBAAwB,WAAW,UAAU,IAAI;CACnD;CACA,OAAO;EAAE;EAAW;CAAK;AAC3B;AAEA,SAAS,mBAAmB,WAAuC;CACjE,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,EAAE,MAAM,UAAU,aAAa,WACxC,IAAI;EACF,IAAI,YAAY;OACV,WAAW,QAAQ,GAAG,OAAO,QAAQ;EAAA,OAEzC,cAAc,UAAU,SAAS,OAAO;CAE5C,SAAS,OAAO;EACd,SAAS,KAAK,GAAG,SAAS,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;CACxF;CAEF,IAAI,SAAS,SAAS,GACpB,MAAM,IAAI,MAAM,qBAAqB,SAAS,OAAO,4BAA4B,SAAS,KAAK,IAAI,GAAG;AAE1G;AAEA,eAAe,2BAA2B,gBAAgD;CACxF,IAAI;EACF,QAAQ,MAAM,eAAe,cAAc,EAAA,CAAG;CAChD,SAAS,OAAO;EACd,QAAQ,KAAK,GAAG,WAAW,2FAA2F;GACpH;GACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAC9D,CAAC;EACD,OAAO;CACT;AACF;AAEA,eAAe,oBAAoB,gBAAwB,gBAA+B,cAAqC;CAC7H,IAAI,kBAAkB,MAAM;EAC1B,MAAM,SAAS,mBAAmB,SAAS,gBAAgB,YAAY,CAAC;EAExE,IAAI,CAAC,aADU,oBAAoB,MAAM,eAAe,cAAc,EAAA,CAAG,MAClD,GAAG,MAAM,GAC9B,MAAM,IAAI,MAAM,uCAAuC,eAAe,oEAAoE;EAE5I;CACF;CAKA,IAAI,CAAC,wBADW,aAAa,gBAAgB,OACV,GAAG,cAAc,GAClD,MAAM,IAAI,MAAM,uCAAuC,eAAe,yDAAyD;AAEnI;AAEA,SAAS,wBAAwB,SAAiB,gBAAiC;CACjF,IAAI;EACF,sBAAsB,SAAS,cAAc;EAC7C,OAAO;CACT,QAAQ;EAKN,OAAO,8BAA8B,KAAK,OAAO;CACnD;AACF;AAEA,SAAS,4BAA4B,SAA2B;CAC9D,MAAM,aAAuB,CAAC;CAC9B,MAAM,gBAAgB;CACtB,IAAI;CACJ,QAAQ,QAAQ,cAAc,KAAK,OAAO,OAAO,MAC/C,WAAW,KAAK,MAAM,EAAE;CAE1B,OAAO;AACT;AAEA,SAAS,oBAAoB,QAAgC;CAC3D,MAAM,UAA0B,CAAC;CACjC,MAAM,QAAQ,QAAgB,QAAsB;EAClD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAAG;GAC9C,MAAM,WAAW,WAAW,KAAK,MAAM,GAAG,OAAO,GAAG;GACpD,IAAI,UAAU,KAAA,GAAW;GACzB,IAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,KAAK,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,GACtG,KAAK,UAAU,KAAK;QAEpB,QAAQ,KAAK;IAAE,MAAM;IAAU;GAAM,CAAC;EAE1C;CACF;CACA,KAAK,IAAI,MAAM;CACf,OAAO;AACT;AAEA,SAAS,wBAAwB,gBAAwB,cAAsB,gBAAuC;CACpH,MAAM,UAAU,oBAAoB,YAAY;CAChD,MAAM,gBAAgB;CACtB,IAAI,kBAAkB,MACpB,OAAO,+BAA+B;EACpC,OAAO;GAAE,MAAM;GAAc;EAAe;EAC5C,gBAAgB,mBAAmB,SAAS,gBAAgB,YAAY,CAAC;EACzE;CACF,CAAC;CAEH,OAAO,8BAA8B;EACnC;EACA;EACA;CACF,CAAC;AACH;AAEA,SAAS,mBAAmB,QAAkC;CAC5D,MAAM,cAAwB,CAAC;CAC/B,MAAM,aAAa,UAAU,QAAQ;EACnC,oBAAoB;EACpB,eAAe;EACf;CACF,CAAC;CACD,IAAI,YAAY,SAAS,GACvB,MAAM,IAAI,MAAM,kFAAkF,YAAY,KAAK,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG;CAE9J,OAAO;AACT;AAEA,SAAS,aAAa,GAAY,GAAqB;CACrD,IAAI,MAAM,GAAG,OAAO;CACpB,IAAI,MAAM,QAAQ,MAAM,MAAM,OAAO,MAAM;CAC3C,IAAI,MAAM,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,GAAG;EACxC,IAAI,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,OAAO;EAC5E,OAAO,EAAE,OAAO,OAAO,UAAU,aAAa,OAAO,EAAE,MAAM,CAAC;CAChE;CACA,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;EAClD,MAAM,WAAW,OAAO,QAAQ,CAAC;EACjC,MAAM,OAAO,IAAI,IAAI,OAAO,QAAQ,CAAC,CAAC;EACtC,IAAI,SAAS,WAAW,KAAK,MAAM,OAAO;EAC1C,OAAO,SAAS,OAAO,CAAC,KAAK,WAAW,KAAK,IAAI,GAAG,KAAK,aAAa,OAAO,KAAK,IAAI,GAAG,CAAC,CAAC;CAC7F;CACA,OAAO;AACT"}
@@ -1,2 +1,2 @@
1
- export * from "./config-file";
2
- export * from "./config-updater";
1
+ export * from "./config-file.js";
2
+ export * from "./config-updater.js";
package/dist/esm/index.js CHANGED
@@ -1,5 +1,3 @@
1
- export * from "./config-file.js"
2
-
3
- export * from "./config-updater.js"
4
-
5
- export { };
1
+ export * from "./config-file.js";
2
+ export * from "./config-updater.js";
3
+ export {};
package/dist/index.js CHANGED
@@ -1,17 +1,18 @@
1
-
2
-
3
1
  var __config_file_js = require("./config-file.js");
4
- Object.keys(__config_file_js).forEach(function (k) {
5
- if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
6
- enumerable: true,
7
- get: function () { return __config_file_js[k]; }
8
- });
2
+ Object.keys(__config_file_js).forEach(function(k) {
3
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
4
+ enumerable: true,
5
+ get: function() {
6
+ return __config_file_js[k];
7
+ }
8
+ });
9
9
  });
10
-
11
10
  var __config_updater_js = require("./config-updater.js");
12
- Object.keys(__config_updater_js).forEach(function (k) {
13
- if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
14
- enumerable: true,
15
- get: function () { return __config_updater_js[k]; }
16
- });
11
+ Object.keys(__config_updater_js).forEach(function(k) {
12
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
13
+ enumerable: true,
14
+ get: function() {
15
+ return __config_updater_js[k];
16
+ }
17
+ });
17
18
  });
@@ -6,16 +6,12 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
8
  var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") {
10
- for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) {
13
- __defProp(to, key, {
14
- get: ((k) => from[k]).bind(null, key),
15
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
- });
17
- }
18
- }
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
19
15
  }
20
16
  return to;
21
17
  };
@@ -23,12 +19,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
19
  value: mod,
24
20
  enumerable: true
25
21
  }) : target, mod));
26
-
27
22
  //#endregion
28
-
29
- Object.defineProperty(exports, '__toESM', {
30
- enumerable: true,
31
- get: function () {
32
- return __toESM;
33
- }
34
- });
23
+ Object.defineProperty(exports, "__toESM", {
24
+ enumerable: true,
25
+ get: function() {
26
+ return __toESM;
27
+ }
28
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexclave/shared-backend",
3
- "version": "1.0.41",
3
+ "version": "1.0.42",
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.41"
32
+ "@hexclave/shared": "1.0.42"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "20.17.6",
@@ -38,7 +38,6 @@
38
38
  "scripts": {
39
39
  "build": "rimraf dist && tsdown",
40
40
  "clean": "rimraf dist && rimraf node_modules",
41
- "dev": "tsdown --watch",
42
41
  "lint": "eslint --ext .tsx,.ts .",
43
42
  "typecheck": "tsc --noEmit",
44
43
  "test": "vitest run"