@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/audits.js +33 -1
- package/dist/claude/spawn-claude-code.d.ts +12 -0
- package/dist/claude/types.d.ts +2 -0
- package/dist/dust.js +312 -77
- package/dist/env-config.d.ts +2 -0
- package/dist/filesystem-emulator.js +1 -1
- package/dist/logging/index.d.ts +10 -1
- package/dist/logging/match.d.ts +21 -1
- package/dist/logging.js +18 -3
- package/dist/proxy/claude-api-proxy.d.ts +34 -1
- package/dist/proxy/git-credential-proxy.d.ts +2 -0
- package/dist/proxy/helper-token.d.ts +73 -0
- package/package.json +1 -1
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.
|
|
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
|
-
|
|
228
|
+
const useJsonFormat = config.logFormat === "json";
|
|
229
|
+
return (message, context) => {
|
|
216
230
|
init();
|
|
217
|
-
const line = formatLine(name,
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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:
|
|
4532
|
-
line:
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
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({
|
|
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
|
|
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:
|
|
8097
|
-
close:
|
|
8098
|
-
send:
|
|
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 =
|
|
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:
|
|
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:
|
|
8356
|
-
chmod:
|
|
8357
|
-
rename:
|
|
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:
|
|
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
|
|
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 =
|
|
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:
|
|
10059
|
-
stderr:
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
10938
|
-
|
|
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:
|
|
11538
|
-
chmod:
|
|
11539
|
-
rename:
|
|
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
|
|
11587
|
-
onError: (
|
|
11588
|
-
console.error(message);
|
|
11589
|
-
}
|
|
11825
|
+
fetch,
|
|
11826
|
+
onError: console.error.bind(console)
|
|
11590
11827
|
});
|
|
11591
|
-
var emitEvent = writeEvent ? createEventEmitter(
|
|
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(),
|