@joshski/dust 0.1.101 → 0.1.102

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dust.js CHANGED
@@ -7,7 +7,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
7
7
  var require_package = __commonJS((exports, module) => {
8
8
  module.exports = {
9
9
  name: "@joshski/dust",
10
- version: "0.1.101",
10
+ version: "0.1.102",
11
11
  description: "Flow state for AI coding agents",
12
12
  type: "module",
13
13
  bin: {
@@ -131,6 +131,19 @@ function formatLine(name, messages) {
131
131
  return `${new Date().toISOString()} [${name}] ${text}
132
132
  `;
133
133
  }
134
+ function formatJsonLine(entry) {
135
+ return JSON.stringify(entry) + `
136
+ `;
137
+ }
138
+ function createLogEntry(name, message, context) {
139
+ return {
140
+ ts: new Date().toISOString(),
141
+ logger: name,
142
+ level: "info",
143
+ msg: message,
144
+ ...context
145
+ };
146
+ }
134
147
 
135
148
  // lib/logging/sink.ts
136
149
  import { appendFileSync, mkdirSync } from "node:fs";
@@ -212,9 +225,10 @@ function createLoggingService(options) {
212
225
  } else if (typeof loggerOptions?.file === "string") {
213
226
  perLoggerSink = getOrCreateFileSink(loggerOptions.file);
214
227
  }
215
- return (...messages) => {
228
+ const useJsonFormat = config.logFormat === "json";
229
+ return (message, context) => {
216
230
  init();
217
- const line = formatLine(name, messages);
231
+ const line = useJsonFormat ? formatJsonLine(createLogEntry(name, message, context)) : formatLine(name, [message, ...context ? [context] : []]);
218
232
  if (perLoggerSink !== undefined) {
219
233
  if (perLoggerSink !== null) {
220
234
  perLoggerSink.write(line);
@@ -237,7 +251,8 @@ var defaultService = createLoggingService({
237
251
  config: {
238
252
  debug: process.env.DEBUG,
239
253
  logDir: process.env.DUST_LOG_DIR,
240
- logFile: process.env.DUST_LOG_FILE
254
+ logFile: process.env.DUST_LOG_FILE,
255
+ logFormat: process.env.DUST_LOG_FORMAT === "json" ? "json" : process.env.DUST_LOG_FORMAT === "text" ? "text" : undefined
241
256
  }
242
257
  });
243
258
  var enableFileLogs = defaultService.enableFileLogs.bind(defaultService);
@@ -293,12 +308,19 @@ function createCommandEventWriter(env = process.env, dependencies = defaultDepen
293
308
  }
294
309
 
295
310
  // lib/env-config.ts
311
+ function parseLogFormat(value) {
312
+ if (value === "json" || value === "text") {
313
+ return value;
314
+ }
315
+ return;
316
+ }
296
317
  function readEnvConfig(env) {
297
318
  return {
298
319
  logging: {
299
320
  debug: env.DEBUG,
300
321
  logDir: env.DUST_LOG_DIR,
301
- logFile: env.DUST_LOG_FILE
322
+ logFile: env.DUST_LOG_FILE,
323
+ logFormat: parseLogFormat(env.DUST_LOG_FORMAT)
302
324
  },
303
325
  bucket: {
304
326
  host: env.DUST_BUCKET_HOST,
@@ -663,7 +685,7 @@ async function loadSettings(cwd, fileSystem, runtime) {
663
685
  }
664
686
 
665
687
  // lib/version.ts
666
- var DUST_VERSION = "0.1.101";
688
+ var DUST_VERSION = "0.1.102";
667
689
 
668
690
  // lib/session.ts
669
691
  var DUST_UNATTENDED = "DUST_UNATTENDED";
@@ -1087,11 +1109,12 @@ function checksAuditTemplate() {
1087
1109
  - Created ideas for each missing check category
1088
1110
  - For multi-ecosystem projects, created separate ideas per ecosystem
1089
1111
  - Each idea includes suggested command and alternatives
1112
+ - No changes to files outside \`.dust/\`
1090
1113
  `;
1091
1114
  }
1092
1115
 
1093
1116
  // lib/audits/stock-audits.ts
1094
- var ideasHint = "Review existing ideas in `./.dust/ideas/` to understand what has been proposed or considered historically, then create new idea files in `./.dust/ideas/` for any issues you identify, avoiding duplication.";
1117
+ var ideasHint = "Review existing ideas in `./.dust/ideas/` to understand what has been proposed or considered historically, then create new idea files in `./.dust/ideas/` for any issues you identify, avoiding duplication. Do not modify source code - create ideas instead.";
1095
1118
  function dataAccessReview() {
1096
1119
  return dedent`
1097
1120
  # Data Access Review
@@ -1142,6 +1165,7 @@ function dataAccessReview() {
1142
1165
  - Checked for sequential operations that could be batched
1143
1166
  - Verified connection/resource cleanup is handled properly
1144
1167
  - Proposed ideas for any data access improvements identified
1168
+ - No changes to files outside \`.dust/\`
1145
1169
  `;
1146
1170
  }
1147
1171
  function coverageExclusions() {
@@ -1189,6 +1213,7 @@ function coverageExclusions() {
1189
1213
  - Labeled justification quality for visibility
1190
1214
  - Identified exclusions that could be removed through decoupling
1191
1215
  - Proposed ideas for refactoring where feasible
1216
+ - No changes to files outside \`.dust/\`
1192
1217
  `;
1193
1218
  }
1194
1219
  function componentReuse() {
@@ -1219,6 +1244,7 @@ function componentReuse() {
1219
1244
  - Evaluated each case for whether extraction would be beneficial
1220
1245
  - Considered whether similar code serves different purposes that may evolve independently
1221
1246
  - Proposed ideas only for extractions where duplication is truly about the same concept
1247
+ - No changes to files outside \`.dust/\`
1222
1248
  `;
1223
1249
  }
1224
1250
  function agentDeveloperExperience() {
@@ -1250,6 +1276,7 @@ function agentDeveloperExperience() {
1250
1276
  - Measured feedback loop speed (time from change to check result)
1251
1277
  - Confirmed debugging tools and structured logging are in place
1252
1278
  - Proposed ideas for any improvements identified
1279
+ - No changes to files outside \`.dust/\`
1253
1280
  `;
1254
1281
  }
1255
1282
  function agentInstructionQuality() {
@@ -1414,6 +1441,7 @@ function agentInstructionQuality() {
1414
1441
  - Found instructions that could be replaced by linter rules
1415
1442
  - Documented each issue with location, type, impact, and suggestion
1416
1443
  - Created ideas for substantial instruction improvements
1444
+ - No changes to files outside \`.dust/\`
1417
1445
  `;
1418
1446
  }
1419
1447
  function deadCode() {
@@ -1447,6 +1475,7 @@ function deadCode() {
1447
1475
  - Created list of code safe to remove
1448
1476
  - Verified removal won't break dynamic imports or reflection
1449
1477
  - Proposed ideas for any dead code worth removing
1478
+ - No changes to files outside \`.dust/\`
1450
1479
  `;
1451
1480
  }
1452
1481
  function documentationDrift() {
@@ -1560,6 +1589,7 @@ function documentationDrift() {
1560
1589
  - Reviewed inline comments for outdated descriptions
1561
1590
  - Documented each drift finding with location, claim, reality, and suggested fix
1562
1591
  - Created ideas for any substantial documentation updates needed
1592
+ - No changes to files outside \`.dust/\`
1563
1593
  `;
1564
1594
  }
1565
1595
  function factsVerification() {
@@ -1595,6 +1625,7 @@ function factsVerification() {
1595
1625
  - Listed missing facts that would help agents
1596
1626
  - Updated or removed stale facts
1597
1627
  - Proposed ideas for any facts improvements needed
1628
+ - No changes to files outside \`.dust/\`
1598
1629
  `;
1599
1630
  }
1600
1631
  function feedbackLoopSpeed() {
@@ -1721,6 +1752,7 @@ function feedbackLoopSpeed() {
1721
1752
  - Flagged dominant checks (>30% of total time)
1722
1753
  - Documented findings in summary table format
1723
1754
  - Created ideas for any feedback loop speed improvements identified
1755
+ - No changes to files outside \`.dust/\`
1724
1756
  `;
1725
1757
  }
1726
1758
  function ideasFromPrinciples() {
@@ -1754,6 +1786,7 @@ function ideasFromPrinciples() {
1754
1786
  - Analyzed codebase for alignment with each principle
1755
1787
  - Listed gaps between current state and principle intent
1756
1788
  - Proposed new ideas for unmet or underserved principles
1789
+ - No changes to files outside \`.dust/\`
1757
1790
  `;
1758
1791
  }
1759
1792
  function commitReview() {
@@ -1806,6 +1839,7 @@ function commitReview() {
1806
1839
  - Noted areas where changes could be generalized
1807
1840
  - Provided specific suggestions for each opportunity
1808
1841
  - Created ideas for any substantial work identified
1842
+ - No changes to files outside \`.dust/\`
1809
1843
  `;
1810
1844
  }
1811
1845
  function securityReview() {
@@ -1870,6 +1904,7 @@ function securityReview() {
1870
1904
  - Ran lightweight pattern scan for obvious issues (documented as supplementary)
1871
1905
  - Created ideas for any missing security tooling categories
1872
1906
  - Each idea specifies which tool to add and where to configure it
1907
+ - No changes to files outside \`.dust/\`
1873
1908
  `;
1874
1909
  }
1875
1910
  function staleIdeas() {
@@ -1904,6 +1939,7 @@ function staleIdeas() {
1904
1939
  - Reviewed each stale idea for current relevance
1905
1940
  - Promoted actionable ideas to tasks
1906
1941
  - Deleted ideas that are no longer relevant
1942
+ - No changes to files outside \`.dust/\`
1907
1943
  `;
1908
1944
  }
1909
1945
  function errorHandling() {
@@ -1952,6 +1988,7 @@ function errorHandling() {
1952
1988
  - Reviewed error messages for actionability
1953
1989
  - Compared error handling consistency across similar operations
1954
1990
  - Proposed ideas for any error handling improvements identified
1991
+ - No changes to files outside \`.dust/\`
1955
1992
  `;
1956
1993
  }
1957
1994
  function globalState() {
@@ -2002,6 +2039,7 @@ function globalState() {
2002
2039
  - Checked for scattered process.env access
2003
2040
  - Documented impact of each global state instance on testing
2004
2041
  - Proposed ideas for refactoring global state to explicit dependencies
2042
+ - No changes to files outside \`.dust/\`
2005
2043
  `;
2006
2044
  }
2007
2045
  function repositoryContext() {
@@ -2051,6 +2089,7 @@ function repositoryContext() {
2051
2089
  - Design philosophy or guiding approach is captured
2052
2090
  - Document is concise enough to fit comfortably in an agent context window
2053
2091
  - A new agent reading only this document could make sensible high-level suggestions
2092
+ - No changes to files outside \`.dust/\`
2054
2093
  `;
2055
2094
  }
2056
2095
  function slowTests() {
@@ -2103,6 +2142,7 @@ function slowTests() {
2103
2142
  - Reviewed beforeEach/beforeAll for optimization opportunities
2104
2143
  - Checked test parallelization configuration
2105
2144
  - Proposed ideas for optimizing the slowest tests
2145
+ - No changes to files outside \`.dust/\`
2106
2146
  `;
2107
2147
  }
2108
2148
  function primitiveObsession() {
@@ -2171,6 +2211,7 @@ function primitiveObsession() {
2171
2211
  - Preserved Functional Core, Imperative Shell boundaries in recommendations
2172
2212
  - Avoided speculative introduction of entirely new types
2173
2213
  - Proposed ideas for primitive obsession improvements identified
2214
+ - No changes to files outside \`.dust/\`
2174
2215
  `;
2175
2216
  }
2176
2217
  function singleResponsibilityViolations() {
@@ -2229,6 +2270,7 @@ function singleResponsibilityViolations() {
2229
2270
  - Preserved Functional Core, Imperative Shell boundaries in recommendations
2230
2271
  - Kept recommendations high-confidence only with clear concern boundaries
2231
2272
  - Proposed ideas for substantial responsibility-splitting work identified
2273
+ - No changes to files outside \`.dust/\`
2232
2274
  `;
2233
2275
  }
2234
2276
  function ubiquitousLanguage() {
@@ -2293,6 +2335,7 @@ function ubiquitousLanguage() {
2293
2335
  - Reviewed factory/constructor naming for \`build*\`, \`create*\`, \`make*\`, \`new*\` consistency
2294
2336
  - Documented any terminology drift or inconsistencies found
2295
2337
  - Proposed ideas for standardizing inconsistent terminology
2338
+ - No changes to files outside \`.dust/\`
2296
2339
  `;
2297
2340
  }
2298
2341
  function designPatterns() {
@@ -2360,6 +2403,7 @@ function designPatterns() {
2360
2403
  - Documented each finding with location, code smell, recommended pattern, trade-offs, and migration complexity
2361
2404
  - Noted tech-stack considerations where Gang of Four patterns may or may not apply
2362
2405
  - Proposed ideas for any design pattern improvements identified
2406
+ - No changes to files outside \`.dust/\`
2363
2407
  `;
2364
2408
  }
2365
2409
  function testAssertions() {
@@ -2511,6 +2555,7 @@ function testAssertions() {
2511
2555
  - Found tests covering multiple unrelated behaviors
2512
2556
  - Documented each finding with location, pattern, impact, and suggestion
2513
2557
  - Proposed ideas for any assertion quality improvements identified
2558
+ - No changes to files outside \`.dust/\`
2514
2559
  `;
2515
2560
  }
2516
2561
  function algorithms() {
@@ -2564,6 +2609,7 @@ function algorithms() {
2564
2609
  - Checked recursive graph/tree operations for cycle protection
2565
2610
  - Documented each finding with function name, location, complexity, data structures, optimization, and acceptability assessment
2566
2611
  - Proposed ideas for any algorithmic improvements identified
2612
+ - No changes to files outside \`.dust/\`
2567
2613
  `;
2568
2614
  }
2569
2615
  function loggingAndTraceability() {
@@ -2616,6 +2662,7 @@ function loggingAndTraceability() {
2616
2662
  - Assessed whether running the application produces understandable output
2617
2663
  - Tested whether logs help diagnose common failure scenarios
2618
2664
  - Proposed ideas for any logging improvements identified
2665
+ - No changes to files outside \`.dust/\`
2619
2666
  `;
2620
2667
  }
2621
2668
  function testPyramid() {
@@ -2697,6 +2744,7 @@ function testPyramid() {
2697
2744
  - Identified miscategorized tests (e.g., unit tests with I/O)
2698
2745
  - Provided specific recommendations to improve pyramid shape
2699
2746
  - Proposed ideas for any substantial test restructuring needed
2747
+ - No changes to files outside \`.dust/\`
2700
2748
  `;
2701
2749
  }
2702
2750
  function idiomaticStyle() {
@@ -2758,6 +2806,7 @@ function idiomaticStyle() {
2758
2806
  - Evaluated code style consistency with rest of codebase
2759
2807
  - Identified anti-patterns or deprecated approaches
2760
2808
  - Proposed ideas for any idiomaticity improvements identified
2809
+ - No changes to files outside \`.dust/\`
2761
2810
  `;
2762
2811
  }
2763
2812
  function uxAudit() {
@@ -2822,6 +2871,7 @@ function uxAudit() {
2822
2871
  - Documented findings with evidence and recommendations
2823
2872
  - Included verification criteria for each issue
2824
2873
  - Created ideas for any UX improvements needed
2874
+ - No changes to files outside \`.dust/\`
2825
2875
  `;
2826
2876
  }
2827
2877
  function dependencyHealth() {
@@ -2964,6 +3014,7 @@ function dependencyHealth() {
2964
3014
  - Searched for packages with known better-maintained alternatives
2965
3015
  - Documented each concern with package name, version, type, and suggested action
2966
3016
  - Created ideas for any dependency health improvements needed
3017
+ - No changes to files outside \`.dust/\`
2967
3018
  `;
2968
3019
  }
2969
3020
  function ciDevelopmentParity() {
@@ -3121,6 +3172,7 @@ function ciDevelopmentParity() {
3121
3172
  - Identified CI-only checks (in CI but not dust)
3122
3173
  - Created idea files for each gap with suggested fix
3123
3174
  - Each idea links to relevant principles
3175
+ - No changes to files outside \`.dust/\`
3124
3176
  `;
3125
3177
  }
3126
3178
  function commitMessageQuality() {
@@ -3270,6 +3322,7 @@ function commitMessageQuality() {
3270
3322
  - Identified commits missing links to issues/tasks
3271
3323
  - Created idea files for patterns found
3272
3324
  - Each idea includes concrete suggestions for improvement
3325
+ - No changes to files outside \`.dust/\`
3273
3326
  `;
3274
3327
  }
3275
3328
  function suggestAudits() {
@@ -3359,6 +3412,7 @@ function suggestAudits() {
3359
3412
  - Identified relevant audits based on change patterns
3360
3413
  - Created task files for each suggested audit with rationale
3361
3414
  - Each task explains why the audit is valuable given recent changes
3415
+ - No changes to files outside \`.dust/\`
3362
3416
  `;
3363
3417
  return content;
3364
3418
  }
@@ -4072,6 +4126,12 @@ import { dirname as dirname2 } from "node:path";
4072
4126
  import { spawn as nodeSpawn } from "node:child_process";
4073
4127
  import { createInterface as nodeCreateInterface } from "node:readline";
4074
4128
  var debug = createLogger("dust.claude.spawn-claude-code");
4129
+ function generateApiKeyHelperSettings(proxyUrl) {
4130
+ const settings = {
4131
+ apiKeyHelper: `curl -fsS --max-time 2 ${proxyUrl}/token | tr -d '\\n'`
4132
+ };
4133
+ return JSON.stringify(settings, null, 2);
4134
+ }
4075
4135
  var defaultDependencies2 = {
4076
4136
  spawn: nodeSpawn,
4077
4137
  createInterface: nodeCreateInterface
@@ -4097,7 +4157,11 @@ function buildDockerRunArguments(docker, claudeArguments, env) {
4097
4157
  }
4098
4158
  if (docker.claudeApiProxyUrl) {
4099
4159
  dockerArguments.push("-e", `ANTHROPIC_BASE_URL=${docker.claudeApiProxyUrl}`);
4100
- dockerArguments.push("-e", "ANTHROPIC_AUTH_TOKEN=proxy-managed");
4160
+ if (docker.settingsFilePath) {
4161
+ const containerSettingsPath = "/home/user/.dust-settings.json";
4162
+ dockerArguments.push("-v", `${docker.settingsFilePath}:${containerSettingsPath}:ro`);
4163
+ claudeArguments.push("--settings", containerSettingsPath);
4164
+ }
4101
4165
  }
4102
4166
  const gitIdentityDefaults = {
4103
4167
  GIT_AUTHOR_NAME: "Dust Agent",
@@ -4202,7 +4266,7 @@ async function* spawnClaudeCode(prompt, options = {}, dependencies = defaultDepe
4202
4266
  try {
4203
4267
  yield JSON.parse(line);
4204
4268
  } catch {
4205
- debug("Skipping malformed JSON line: %s", line.slice(0, 200));
4269
+ debug(`Skipping malformed JSON line: ${line.slice(0, 200)}`);
4206
4270
  }
4207
4271
  }
4208
4272
  await closePromise;
@@ -4528,8 +4592,8 @@ function processEvent(event, sink, state) {
4528
4592
  }
4529
4593
  function createStdoutSink() {
4530
4594
  return {
4531
- write: (text) => process.stdout.write(text),
4532
- line: (text) => console.log(text)
4595
+ write: process.stdout.write.bind(process.stdout),
4596
+ line: console.log.bind(console)
4533
4597
  };
4534
4598
  }
4535
4599
 
@@ -4657,8 +4721,9 @@ async function removeRepository(path, spawn, context) {
4657
4721
  }
4658
4722
 
4659
4723
  // lib/bucket/repository-loop.ts
4660
- import { existsSync as fsExistsSync } from "node:fs";
4724
+ import { existsSync as fsExistsSync, unlinkSync, writeFileSync } from "node:fs";
4661
4725
  import os2 from "node:os";
4726
+ import { join as join9 } from "node:path";
4662
4727
 
4663
4728
  // lib/agent-events.ts
4664
4729
  function rawEventToAgentEvent(rawEvent, provider) {
@@ -4756,7 +4821,7 @@ function formatLoopEvent(event) {
4756
4821
  }
4757
4822
 
4758
4823
  // lib/loop/iteration.ts
4759
- import { spawn as nodeSpawn2 } from "node:child_process";
4824
+ import { execSync, spawn as nodeSpawn2 } from "node:child_process";
4760
4825
  import os from "node:os";
4761
4826
 
4762
4827
  // lib/cli/process-runner.ts
@@ -5636,7 +5701,39 @@ async function runOneIteration(dependencies, loopDependencies, onLoopEvent, onAg
5636
5701
  const taskContent = await fileSystem.readFile(`${context.cwd}/${task.path}`);
5637
5702
  const instructions = buildImplementationInstructions(settings.dustCommand, hooksInstalled, task.title ?? undefined, task.path, settings.installCommand, true);
5638
5703
  const taskPrompt = buildTaskPrompt(task.path, taskContent, instructions, toolsSection, options.branch);
5639
- return executeTask(task, { run: run2, prompt: taskPrompt, spawnOptions, onRawEvent }, onAgentEvent, context, agentName, agentType, logger);
5704
+ let originalRemoteUrl;
5705
+ if (docker?.gitProxyUrl) {
5706
+ try {
5707
+ originalRemoteUrl = execSync("git remote get-url origin", {
5708
+ cwd: context.cwd,
5709
+ encoding: "utf-8"
5710
+ }).trim();
5711
+ const proxyUrl = originalRemoteUrl.replace(/^https:\/\/github\.com\//, `${docker.gitProxyUrl}/github.com/`);
5712
+ if (proxyUrl !== originalRemoteUrl) {
5713
+ execSync(`git remote set-url origin "${proxyUrl}"`, {
5714
+ cwd: context.cwd
5715
+ });
5716
+ log2(`set remote URL to proxy: ${proxyUrl}`);
5717
+ }
5718
+ } catch {
5719
+ log2("warning: failed to rewrite remote URL for Docker proxy");
5720
+ originalRemoteUrl = undefined;
5721
+ }
5722
+ }
5723
+ try {
5724
+ return await executeTask(task, { run: run2, prompt: taskPrompt, spawnOptions, onRawEvent }, onAgentEvent, context, agentName, agentType, logger);
5725
+ } finally {
5726
+ if (originalRemoteUrl) {
5727
+ try {
5728
+ execSync(`git remote set-url origin "${originalRemoteUrl}"`, {
5729
+ cwd: context.cwd
5730
+ });
5731
+ log2(`restored remote URL: ${originalRemoteUrl}`);
5732
+ } catch {
5733
+ log2("warning: failed to restore original remote URL");
5734
+ }
5735
+ }
5736
+ }
5640
5737
  }
5641
5738
  function buildCheckFixPrompt(dustCommand, checkOutput) {
5642
5739
  return `Note: Do NOT run \`${dustCommand} agent\`.
@@ -5739,7 +5836,7 @@ async function* spawnCodex(prompt, options = {}, dependencies = defaultDependenc
5739
5836
  try {
5740
5837
  yield JSON.parse(line);
5741
5838
  } catch {
5742
- debug2("Skipping malformed JSON line: %s", line.slice(0, 200));
5839
+ debug2(`Skipping malformed JSON line: ${line.slice(0, 200)}`);
5743
5840
  }
5744
5841
  }
5745
5842
  await closePromise;
@@ -5898,12 +5995,53 @@ import { readFileSync } from "node:fs";
5898
5995
  import { createServer as httpCreateServer } from "node:http";
5899
5996
  import { homedir } from "node:os";
5900
5997
  import { join as join8 } from "node:path";
5998
+
5999
+ // lib/proxy/helper-token.ts
6000
+ var HELPER_TOKEN_TTL_MS = 60000;
6001
+ function generateRandomHex(length) {
6002
+ const bytes = new Uint8Array(Math.ceil(length / 2));
6003
+ crypto.getRandomValues(bytes);
6004
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, length);
6005
+ }
6006
+ function generateHelperToken(now = Date.now()) {
6007
+ const randomPart = generateRandomHex(56);
6008
+ const token = `sk-ant-api03-${randomPart}`;
6009
+ return {
6010
+ token,
6011
+ issuedAt: now
6012
+ };
6013
+ }
6014
+ function isHelperTokenValid(token, issued, now = Date.now(), ttlMs = HELPER_TOKEN_TTL_MS) {
6015
+ if (token !== issued.token) {
6016
+ return false;
6017
+ }
6018
+ const elapsed = now - issued.issuedAt;
6019
+ return elapsed >= 0 && elapsed < ttlMs;
6020
+ }
6021
+ function createHelperTokenState() {
6022
+ return {
6023
+ current: null
6024
+ };
6025
+ }
6026
+ function rotateHelperToken(state, now = Date.now()) {
6027
+ return {
6028
+ current: generateHelperToken(now)
6029
+ };
6030
+ }
6031
+ function isCurrentTokenValid(state, now = Date.now(), ttlMs = HELPER_TOKEN_TTL_MS) {
6032
+ if (!state.current) {
6033
+ return false;
6034
+ }
6035
+ return isHelperTokenValid(state.current.token, state.current, now, ttlMs);
6036
+ }
6037
+
6038
+ // lib/proxy/claude-api-proxy.ts
5901
6039
  var log4 = createLogger("dust:proxy:claude-api");
5902
6040
  var ANTHROPIC_API_HOST = "https://api.anthropic.com";
5903
6041
  var OAUTH_BETA_FLAG = "oauth-2025-04-20";
5904
6042
  var defaultDependencies4 = {
5905
6043
  homedir,
5906
- readFileSync: (path2, encoding) => readFileSync(path2, encoding),
6044
+ readFileSync,
5907
6045
  fetch
5908
6046
  };
5909
6047
  function readOAuthToken(dependencies = defaultDependencies4) {
@@ -5984,6 +6122,48 @@ function buildNoTokenResponse() {
5984
6122
  body: "Could not obtain OAuth token"
5985
6123
  };
5986
6124
  }
6125
+ function buildInvalidHelperTokenResponse() {
6126
+ return {
6127
+ statusCode: 401,
6128
+ contentType: "text/plain",
6129
+ body: "Invalid or expired helper token"
6130
+ };
6131
+ }
6132
+ function extractHelperToken(headers) {
6133
+ const authHeader = headers["authorization"];
6134
+ if (typeof authHeader === "string") {
6135
+ const bearerMatch = authHeader.match(/^Bearer\s+(.+)$/i);
6136
+ if (bearerMatch) {
6137
+ return bearerMatch[1];
6138
+ }
6139
+ }
6140
+ const apiKey = headers["x-api-key"];
6141
+ if (typeof apiKey === "string") {
6142
+ return apiKey;
6143
+ }
6144
+ return null;
6145
+ }
6146
+ function validateHelperToken(incomingToken, state, now = Date.now()) {
6147
+ if (!incomingToken || !state.current) {
6148
+ return false;
6149
+ }
6150
+ return isHelperTokenValid(incomingToken, state.current, now);
6151
+ }
6152
+ function getOrRefreshHelperToken(state, now = Date.now()) {
6153
+ if (!isCurrentTokenValid(state, now)) {
6154
+ const newState = rotateHelperToken(state, now);
6155
+ return { state: newState, token: newState.current.token };
6156
+ }
6157
+ return { state, token: state.current.token };
6158
+ }
6159
+ function buildTokenResponse(token) {
6160
+ return {
6161
+ kind: "error",
6162
+ status: 200,
6163
+ contentType: "text/plain",
6164
+ body: token
6165
+ };
6166
+ }
5987
6167
  function buildUpstreamErrorResponse(error) {
5988
6168
  const message = error instanceof Error ? error.message : "Unknown error";
5989
6169
  return {
@@ -5992,8 +6172,21 @@ function buildUpstreamErrorResponse(error) {
5992
6172
  body: `Upstream request failed: ${message}`
5993
6173
  };
5994
6174
  }
5995
- async function handleProxyRequest(request, dependencies) {
6175
+ async function handleProxyRequest(request, dependencies, helperTokenState, now = Date.now()) {
5996
6176
  log4(`${request.method} ${request.pathname}${request.search}`);
6177
+ if (helperTokenState) {
6178
+ const incomingToken = extractHelperToken(request.headers);
6179
+ if (!validateHelperToken(incomingToken, helperTokenState, now)) {
6180
+ log4("invalid or expired helper token");
6181
+ const errorResponse = buildInvalidHelperTokenResponse();
6182
+ return {
6183
+ kind: "error",
6184
+ status: errorResponse.statusCode,
6185
+ contentType: errorResponse.contentType,
6186
+ body: errorResponse.body
6187
+ };
6188
+ }
6189
+ }
5997
6190
  const token = readOAuthToken(dependencies);
5998
6191
  if (!token) {
5999
6192
  const errorResponse = buildNoTokenResponse();
@@ -6070,9 +6263,17 @@ async function readNodeRequestBody(nodeRequest) {
6070
6263
  }
6071
6264
  async function createClaudeApiProxyServer(dependencies = defaultDependencies4) {
6072
6265
  let resolvedPort = 0;
6266
+ let helperTokenState = createHelperTokenState();
6073
6267
  const server = httpCreateServer(async (nodeRequest, nodeResponse) => {
6074
6268
  const method = nodeRequest.method ?? "GET";
6075
6269
  const url = new URL(nodeRequest.url ?? "/", `http://localhost:${resolvedPort}`);
6270
+ if (url.pathname === "/token" && method === "GET") {
6271
+ const result = getOrRefreshHelperToken(helperTokenState);
6272
+ helperTokenState = result.state;
6273
+ const response = buildTokenResponse(result.token);
6274
+ await writeProxyResponse(response, nodeResponse);
6275
+ return;
6276
+ }
6076
6277
  const body = await readNodeRequestBody(nodeRequest);
6077
6278
  const proxyRequest = {
6078
6279
  method,
@@ -6081,7 +6282,7 @@ async function createClaudeApiProxyServer(dependencies = defaultDependencies4) {
6081
6282
  headers: nodeRequest.headers,
6082
6283
  body
6083
6284
  };
6084
- const proxyResponse = await handleProxyRequest(proxyRequest, dependencies);
6285
+ const proxyResponse = await handleProxyRequest(proxyRequest, dependencies, helperTokenState);
6085
6286
  await writeProxyResponse(proxyResponse, nodeResponse);
6086
6287
  });
6087
6288
  await new Promise((resolve) => {
@@ -6121,9 +6322,13 @@ function parseGitPath(urlPath) {
6121
6322
  }
6122
6323
  async function getGitCredentials(host, dependencies) {
6123
6324
  return new Promise((resolve) => {
6124
- const proc = dependencies.spawn("git", ["credential", "fill"], {
6325
+ const spawnOptions = {
6125
6326
  stdio: ["pipe", "pipe", "pipe"]
6126
- });
6327
+ };
6328
+ if (dependencies.userHome) {
6329
+ spawnOptions.env = { ...process.env, HOME: dependencies.userHome };
6330
+ }
6331
+ const proc = dependencies.spawn("git", ["credential", "fill"], spawnOptions);
6127
6332
  const input = `protocol=https
6128
6333
  host=${host}
6129
6334
 
@@ -6240,13 +6445,14 @@ async function createGitCredentialProxyServer(dependencies) {
6240
6445
  if (nodeRequest.headers["content-type"]) {
6241
6446
  headers["Content-Type"] = nodeRequest.headers["content-type"];
6242
6447
  }
6243
- log5(`forwarding to ${upstreamUrl.toString()}`);
6448
+ log5(`forwarding ${method} to ${upstreamUrl.toString()}${body ? ` (${body.length} bytes)` : ""}`);
6244
6449
  try {
6245
6450
  const upstreamResponse = await fetch(upstreamUrl.toString(), {
6246
6451
  method,
6247
6452
  headers,
6248
6453
  body
6249
6454
  });
6455
+ log5(`upstream ${method} ${endpointInfo.endpoint} responded: ${upstreamResponse.status}`);
6250
6456
  const responseHeaders = {};
6251
6457
  upstreamResponse.headers.forEach((value, key) => {
6252
6458
  if (key.toLowerCase() !== "transfer-encoding") {
@@ -6756,15 +6962,24 @@ async function setupDockerConfig(repoState, repoDeps, onLoopEvent) {
6756
6962
  shouldExit: true
6757
6963
  };
6758
6964
  }
6759
- const gitProxy = await createGitCredentialProxyServer({ spawn: spawn2 });
6965
+ const gitProxy = await createGitCredentialProxyServer({
6966
+ spawn: spawn2,
6967
+ userHome: process.env.DUST_USER_HOME || undefined
6968
+ });
6760
6969
  log7(`git credential proxy started on port ${gitProxy.port}`);
6761
6970
  const apiProxy = await createClaudeApiProxyServer();
6762
6971
  log7(`claude api proxy started on port ${apiProxy.port}`);
6972
+ const claudeApiProxyUrl = `http://host.docker.internal:${apiProxy.port}`;
6973
+ const settingsFilePath = join9(os2.tmpdir(), `dust-claude-settings-${repoState.repository.name.replace(/\//g, "-")}.json`);
6974
+ const settingsContent = generateApiKeyHelperSettings(claudeApiProxyUrl);
6975
+ writeFileSync(settingsFilePath, settingsContent, "utf-8");
6976
+ log7(`created settings file at ${settingsFilePath}`);
6763
6977
  return {
6764
6978
  config: {
6765
6979
  ...dockerResult.config,
6766
6980
  gitProxyUrl: `http://host.docker.internal:${gitProxy.port}`,
6767
- claudeApiProxyUrl: `http://host.docker.internal:${apiProxy.port}`
6981
+ claudeApiProxyUrl,
6982
+ settingsFilePath
6768
6983
  },
6769
6984
  stopGitProxy: gitProxy.stop,
6770
6985
  stopApiProxy: apiProxy.stop,
@@ -6803,7 +7018,7 @@ function handleNoTasksResult(repoState, repoName) {
6803
7018
  appendLogLine(repoState.logBuffer, createLogLine("Waiting for tasks...", "stdout"));
6804
7019
  return false;
6805
7020
  }
6806
- function cleanupDockerProxies(repoName, stopGitProxy, stopApiProxy) {
7021
+ function cleanupDockerProxies(repoName, stopGitProxy, stopApiProxy, dockerConfig) {
6807
7022
  if (stopGitProxy) {
6808
7023
  stopGitProxy();
6809
7024
  log7(`git credential proxy stopped for ${repoName}`);
@@ -6812,6 +7027,12 @@ function cleanupDockerProxies(repoName, stopGitProxy, stopApiProxy) {
6812
7027
  stopApiProxy();
6813
7028
  log7(`claude api proxy stopped for ${repoName}`);
6814
7029
  }
7030
+ if (dockerConfig?.settingsFilePath) {
7031
+ try {
7032
+ unlinkSync(dockerConfig.settingsFilePath);
7033
+ log7(`cleaned up settings file ${dockerConfig.settingsFilePath}`);
7034
+ } catch {}
7035
+ }
6815
7036
  }
6816
7037
  async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
6817
7038
  const { spawn: spawn2, run: run3, fileSystem, sleep, runtime } = repoDeps;
@@ -6880,7 +7101,7 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
6880
7101
  await waitForTasks(repoState, sleep);
6881
7102
  }
6882
7103
  }
6883
- cleanupDockerProxies(repoName, dockerSetup.stopGitProxy, dockerSetup.stopApiProxy);
7104
+ cleanupDockerProxies(repoName, dockerSetup.stopGitProxy, dockerSetup.stopApiProxy, dockerSetup.config);
6884
7105
  log7(`loop stopped for ${repoName}`);
6885
7106
  appendLogLine(repoState.logBuffer, createLogLine(`Stopped loop for ${repoName}`, "stdout"));
6886
7107
  }
@@ -8093,9 +8314,9 @@ function adaptWebSocket(ws) {
8093
8314
  emitter.emit("message", { data: String(event.data) });
8094
8315
  });
8095
8316
  return {
8096
- addEventListener: (type, handler) => emitter.on(type, handler),
8097
- close: () => ws.close(),
8098
- send: (data) => ws.send(data),
8317
+ addEventListener: emitter.on.bind(emitter),
8318
+ close: ws.close.bind(ws),
8319
+ send: ws.send.bind(ws),
8099
8320
  get readyState() {
8100
8321
  return currentReadyState;
8101
8322
  }
@@ -8128,7 +8349,7 @@ function defaultSetupKeypress(onKey) {
8128
8349
  };
8129
8350
  }
8130
8351
  function defaultSetupSignals(onSignal) {
8131
- const handler = () => onSignal();
8352
+ const handler = onSignal;
8132
8353
  process.on("SIGINT", handler);
8133
8354
  process.on("SIGTERM", handler);
8134
8355
  return () => {
@@ -8224,7 +8445,7 @@ function createDefaultBucketDependencies() {
8224
8445
  isTTY: process.stdout.isTTY ?? false,
8225
8446
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
8226
8447
  getReposDir: () => getReposDir(envConfig.session, homedir2()),
8227
- createInterval: (callback, ms) => setInterval(callback, ms),
8448
+ createInterval: setInterval,
8228
8449
  clearInterval: (id) => clearInterval(id),
8229
8450
  auth: {
8230
8451
  createServer: createLocalServer,
@@ -8352,9 +8573,9 @@ function createAuthFileSystem(dependencies) {
8352
8573
  readFile: (path2) => dependencies.readFile(path2, "utf8"),
8353
8574
  writeFile: (path2, content) => dependencies.writeFile(path2, content, "utf8"),
8354
8575
  mkdir: (path2, options) => dependencies.mkdir(path2, options).then(() => {}),
8355
- readdir: (path2) => dependencies.readdir(path2),
8356
- chmod: (path2, mode) => dependencies.chmod(path2, mode),
8357
- rename: (oldPath, newPath) => dependencies.rename(oldPath, newPath)
8576
+ readdir: dependencies.readdir.bind(dependencies),
8577
+ chmod: dependencies.chmod.bind(dependencies),
8578
+ rename: dependencies.rename.bind(dependencies)
8358
8579
  };
8359
8580
  }
8360
8581
  function createInitialState() {
@@ -8507,7 +8728,6 @@ function connectWebSocket(token, state, bucketDependencies, context, fileSystem,
8507
8728
  if (state.shuttingDown)
8508
8729
  return;
8509
8730
  const wsUrl = getWebSocketUrl(bucketDependencies.bucket);
8510
- let waitingForConnectionReady = false;
8511
8731
  let connectionReady = false;
8512
8732
  const processIncomingServerMessage = (event) => {
8513
8733
  let rawData;
@@ -8546,10 +8766,8 @@ function connectWebSocket(token, state, bucketDependencies, context, fileSystem,
8546
8766
  }
8547
8767
  if (message.type === "connection-ready") {
8548
8768
  connectionReady = true;
8549
- waitingForConnectionReady = false;
8550
8769
  } else if (message.type === "connection-rejected") {
8551
8770
  connectionReady = false;
8552
- waitingForConnectionReady = false;
8553
8771
  } else if (!connectionReady) {
8554
8772
  return;
8555
8773
  }
@@ -8568,7 +8786,6 @@ function connectWebSocket(token, state, bucketDependencies, context, fileSystem,
8568
8786
  });
8569
8787
  };
8570
8788
  const sendConnectionInit = async () => {
8571
- waitingForConnectionReady = true;
8572
8789
  let message;
8573
8790
  try {
8574
8791
  message = await bucketDependencies.buildConnectionInit();
@@ -8588,7 +8805,6 @@ function connectWebSocket(token, state, bucketDependencies, context, fileSystem,
8588
8805
  const messageText = error instanceof Error ? error.message : String(error);
8589
8806
  context.stderr(`Failed to send connection init: ${messageText}. Continuing without handshake.`);
8590
8807
  connectionReady = true;
8591
- waitingForConnectionReady = false;
8592
8808
  }
8593
8809
  };
8594
8810
  let ws;
@@ -8662,7 +8878,7 @@ function createMessageEffectDeps(dependencies) {
8662
8878
  } = dependencies;
8663
8879
  return {
8664
8880
  logMessage: (message, stream) => logMessage(state, context, useTUI, message, stream),
8665
- debugLog: (message) => log10(message),
8881
+ debugLog: log10,
8666
8882
  syncUIWithRepoList: (repositories) => syncUIWithRepoList(state, repositories),
8667
8883
  handleRepositoryList: (repositories) => {
8668
8884
  const repoDeps = toRepositoryDependencies(bucketDependencies, fileSystem, state, forwardToolExecution);
@@ -9120,7 +9336,7 @@ Run \`dust bucket tool ${toolName}\` to see available operations.`);
9120
9336
  }
9121
9337
 
9122
9338
  // lib/cli/commands/lint-markdown.ts
9123
- import { isAbsolute, join as join9, relative, sep } from "node:path";
9339
+ import { isAbsolute, join as join10, relative, sep } from "node:path";
9124
9340
 
9125
9341
  // lib/artifacts/principles.ts
9126
9342
  function extractLinksFromSection(content, sectionHeading) {
@@ -9940,7 +10156,7 @@ async function lintMarkdown(dependencies) {
9940
10156
  const violations = [];
9941
10157
  context.stdout("Validating directory structure...");
9942
10158
  violations.push(...await validateDirectoryStructure(dustPath, fileSystem));
9943
- const settingsPath = join9(dustPath, "config", "settings.json");
10159
+ const settingsPath = join10(dustPath, "config", "settings.json");
9944
10160
  const settingsResult = await validateSettingsFile(fileSystem, settingsPath);
9945
10161
  if (settingsResult.didValidate) {
9946
10162
  context.stdout("Validating settings.json...");
@@ -10055,8 +10271,8 @@ async function runValidationCheck(dependencies, emitEvent, clock = Date.now) {
10055
10271
  const outputLines = [];
10056
10272
  const bufferedContext = {
10057
10273
  cwd: dependencies.context.cwd,
10058
- stdout: (msg) => outputLines.push(msg),
10059
- stderr: (msg) => outputLines.push(msg)
10274
+ stdout: outputLines.push.bind(outputLines),
10275
+ stderr: outputLines.push.bind(outputLines)
10060
10276
  };
10061
10277
  log11("running built-in check: dust lint");
10062
10278
  emitEvent?.({ type: "check-started", name: "lint .dust directory" });
@@ -10370,7 +10586,7 @@ async function init(dependencies) {
10370
10586
  import { basename as basename3, resolve as resolve4 } from "node:path";
10371
10587
 
10372
10588
  // lib/core-principles.ts
10373
- import { join as join10, dirname as dirname5 } from "node:path";
10589
+ import { join as join11, dirname as dirname5 } from "node:path";
10374
10590
  import { fileURLToPath } from "node:url";
10375
10591
  import { existsSync, readdirSync, statSync as statSync2 } from "node:fs";
10376
10592
  import { readFile as readFile3 } from "node:fs/promises";
@@ -10449,7 +10665,7 @@ function locatePackagePrinciplesDir() {
10449
10665
  const thisFile = fileURLToPath(import.meta.url);
10450
10666
  const thisDir = dirname5(thisFile);
10451
10667
  const packageRoot = dirname5(thisDir);
10452
- const principlesDir = join10(packageRoot, ".dust", "principles");
10668
+ const principlesDir = join11(packageRoot, ".dust", "principles");
10453
10669
  if (!existsSync(principlesDir)) {
10454
10670
  throw new Error(`Core principles directory not found at ${principlesDir}. ` + "Ensure the @joshski/dust package is properly installed.");
10455
10671
  }
@@ -10458,7 +10674,7 @@ function locatePackagePrinciplesDir() {
10458
10674
  async function readAllCorePrinciples() {
10459
10675
  const principlesDir = locatePackagePrinciplesDir();
10460
10676
  const packageRoot = dirname5(dirname5(principlesDir));
10461
- const dustPath = join10(packageRoot, ".dust");
10677
+ const dustPath = join11(packageRoot, ".dust");
10462
10678
  const fileSystem = createReadableFileSystem();
10463
10679
  const files = readdirSync(principlesDir);
10464
10680
  const mdFiles = files.filter((f) => f.endsWith(".md"));
@@ -10854,8 +11070,9 @@ async function list(dependencies) {
10854
11070
  }
10855
11071
 
10856
11072
  // lib/loop/loop.ts
10857
- import { existsSync as existsSync2 } from "node:fs";
11073
+ import { existsSync as existsSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "node:fs";
10858
11074
  import os3 from "node:os";
11075
+ import { join as join12 } from "node:path";
10859
11076
 
10860
11077
  // lib/loop/parse-args.ts
10861
11078
  var DEFAULT_MAX_ITERATIONS = 10;
@@ -10886,6 +11103,36 @@ async function sleepWithProgress(sleep, totalMs, writeInline, writeLine) {
10886
11103
 
10887
11104
  // lib/loop/loop.ts
10888
11105
  var log12 = createLogger("dust:loop");
11106
+ async function setupDockerProxies(dockerResult, loopDependencies, sessionId) {
11107
+ const gitProxy = await createGitCredentialProxyServer({
11108
+ spawn: loopDependencies.spawn,
11109
+ userHome: process.env.DUST_USER_HOME || undefined
11110
+ });
11111
+ const apiProxy = await createClaudeApiProxyServer();
11112
+ const claudeApiProxyUrl = `http://host.docker.internal:${apiProxy.port}`;
11113
+ const settingsFilePath = join12(os3.tmpdir(), `dust-claude-settings-${sessionId}.json`);
11114
+ const settingsContent = generateApiKeyHelperSettings(claudeApiProxyUrl);
11115
+ writeFileSync2(settingsFilePath, settingsContent, "utf-8");
11116
+ log12(`created settings file at ${settingsFilePath}`);
11117
+ return {
11118
+ dockerConfig: {
11119
+ ...dockerResult.config,
11120
+ gitProxyUrl: `http://host.docker.internal:${gitProxy.port}`,
11121
+ claudeApiProxyUrl,
11122
+ settingsFilePath
11123
+ },
11124
+ stopGitProxy: gitProxy.stop,
11125
+ stopApiProxy: apiProxy.stop,
11126
+ settingsFilePath
11127
+ };
11128
+ }
11129
+ function resolveDockerDependencies(loopDependencies) {
11130
+ return {
11131
+ spawn: loopDependencies.dockerDeps?.spawn ?? loopDependencies.spawn,
11132
+ homedir: loopDependencies.dockerDeps?.homedir ?? os3.homedir,
11133
+ existsSync: loopDependencies.dockerDeps?.existsSync ?? existsSync2
11134
+ };
11135
+ }
10889
11136
  async function runLoop(dependencies, loopDependencies) {
10890
11137
  enableFileLogs("loop");
10891
11138
  const { context, settings } = dependencies;
@@ -10917,34 +11164,20 @@ async function runLoop(dependencies, loopDependencies) {
10917
11164
  };
10918
11165
  const hooksInstalled = await manageGitHooks(dependencies);
10919
11166
  let dockerConfig;
10920
- const dockerDeps = {
10921
- spawn: loopDependencies.dockerDeps?.spawn ?? loopDependencies.spawn,
10922
- homedir: loopDependencies.dockerDeps?.homedir ?? os3.homedir,
10923
- existsSync: loopDependencies.dockerDeps?.existsSync ?? existsSync2
10924
- };
11167
+ const dockerDeps = resolveDockerDependencies(loopDependencies);
10925
11168
  const dockerResult = await prepareDockerConfig(context.cwd, dockerDeps, onLoopEvent);
10926
11169
  if ("error" in dockerResult) {
10927
11170
  context.stderr(dockerResult.error);
10928
11171
  return { exitCode: 1 };
10929
11172
  }
10930
- let stopGitProxy;
10931
- let stopApiProxy;
11173
+ let dockerProxies;
10932
11174
  if ("config" in dockerResult) {
10933
11175
  if (!process.env.CLAUDE_CODE_OAUTH_TOKEN) {
10934
11176
  context.stderr("Docker mode requires CLAUDE_CODE_OAUTH_TOKEN. Run `claude setup-token` and export the token.");
10935
11177
  return { exitCode: 1 };
10936
11178
  }
10937
- const gitProxy = await createGitCredentialProxyServer({
10938
- spawn: loopDependencies.spawn
10939
- });
10940
- stopGitProxy = gitProxy.stop;
10941
- const apiProxy = await createClaudeApiProxyServer();
10942
- stopApiProxy = apiProxy.stop;
10943
- dockerConfig = {
10944
- ...dockerResult.config,
10945
- gitProxyUrl: `http://host.docker.internal:${gitProxy.port}`,
10946
- claudeApiProxyUrl: `http://host.docker.internal:${apiProxy.port}`
10947
- };
11179
+ dockerProxies = await setupDockerProxies(dockerResult, loopDependencies, sessionId);
11180
+ dockerConfig = dockerProxies.dockerConfig;
10948
11181
  }
10949
11182
  log12(`starting loop, maxIterations=${maxIterations}, sessionId=${sessionId}`);
10950
11183
  onLoopEvent({ type: "loop.warning" });
@@ -10980,8 +11213,14 @@ async function runLoop(dependencies, loopDependencies) {
10980
11213
  });
10981
11214
  }
10982
11215
  }
10983
- stopGitProxy?.();
10984
- stopApiProxy?.();
11216
+ dockerProxies?.stopGitProxy();
11217
+ dockerProxies?.stopApiProxy();
11218
+ if (dockerProxies?.settingsFilePath) {
11219
+ try {
11220
+ unlinkSync2(dockerProxies.settingsFilePath);
11221
+ log12(`cleaned up settings file ${dockerProxies.settingsFilePath}`);
11222
+ } catch {}
11223
+ }
10985
11224
  log12(`loop ended after ${completedIterations} iterations`);
10986
11225
  onLoopEvent({ type: "loop.ended", maxIterations });
10987
11226
  return { exitCode: 0 };
@@ -11534,9 +11773,9 @@ function createFileSystem(primitives) {
11534
11773
  await primitives.mkdir(path2, options);
11535
11774
  },
11536
11775
  getFileCreationTime: (path2) => primitives.statSync(path2).birthtimeMs,
11537
- readdir: (path2) => primitives.readdir(path2),
11538
- chmod: (path2, mode) => primitives.chmod(path2, mode),
11539
- rename: (oldPath, newPath) => primitives.rename(oldPath, newPath)
11776
+ readdir: primitives.readdir.bind(primitives),
11777
+ chmod: primitives.chmod.bind(primitives),
11778
+ rename: primitives.rename.bind(primitives)
11540
11779
  };
11541
11780
  }
11542
11781
  function createGlobScanner(readdirFn) {
@@ -11583,14 +11822,10 @@ async function wireEntry(fsPrimitives, processPrimitives, consolePrimitives, run
11583
11822
  // lib/cli/run.ts
11584
11823
  var envConfig = readEnvConfig(process.env);
11585
11824
  var writeEvent = createCommandEventWriter(process.env, {
11586
- fetch: (input, init2) => fetch(input, init2),
11587
- onError: (message) => {
11588
- console.error(message);
11589
- }
11825
+ fetch,
11826
+ onError: console.error.bind(console)
11590
11827
  });
11591
- var emitEvent = writeEvent ? createEventEmitter((message) => {
11592
- writeEvent(message);
11593
- }) : undefined;
11828
+ var emitEvent = writeEvent ? createEventEmitter(writeEvent) : undefined;
11594
11829
  await wireEntry(defaultFileSystemPrimitives, {
11595
11830
  argv: process.argv,
11596
11831
  cwd: () => process.cwd(),