@joshski/dust 0.1.82 → 0.1.83
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/bucket/repository.d.ts +3 -0
- package/dist/dust.js +103 -40
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { spawn as nodeSpawn } from 'node:child_process';
|
|
8
8
|
import { run as claudeRun } from '../claude/run';
|
|
9
9
|
import type { CommandDependencies, FileSystem } from '../cli/types';
|
|
10
|
+
import type { DockerDependencies } from '../docker/docker-agent';
|
|
10
11
|
import { type BucketEmitFn, type SendEventFn } from './events';
|
|
11
12
|
import { type LogBuffer } from './log-buffer';
|
|
12
13
|
export { cloneRepository, getRepoPath, removeRepository, } from './repository-git';
|
|
@@ -47,6 +48,8 @@ export interface RepositoryDependencies {
|
|
|
47
48
|
fileSystem: FileSystem;
|
|
48
49
|
sleep: (ms: number) => Promise<void>;
|
|
49
50
|
getReposDir: () => string;
|
|
51
|
+
/** Optional overrides for Docker dependency functions (for testing) */
|
|
52
|
+
dockerDeps?: Partial<DockerDependencies>;
|
|
50
53
|
}
|
|
51
54
|
/**
|
|
52
55
|
* Start (or restart) the per-repository loop and keep loopPromise state accurate.
|
package/dist/dust.js
CHANGED
|
@@ -318,7 +318,7 @@ async function loadSettings(cwd, fileSystem) {
|
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
// lib/version.ts
|
|
321
|
-
var DUST_VERSION = "0.1.
|
|
321
|
+
var DUST_VERSION = "0.1.83";
|
|
322
322
|
|
|
323
323
|
// lib/session.ts
|
|
324
324
|
var DUST_UNATTENDED = "DUST_UNATTENDED";
|
|
@@ -2043,16 +2043,25 @@ function buildDockerRunArguments(docker, claudeArguments, env) {
|
|
|
2043
2043
|
"-w",
|
|
2044
2044
|
"/workspace",
|
|
2045
2045
|
"-v",
|
|
2046
|
-
`${docker.homeDir}/.claude:/
|
|
2046
|
+
`${docker.homeDir}/.claude:/home/user/.claude`,
|
|
2047
2047
|
"-v",
|
|
2048
|
-
`${docker.homeDir}/.
|
|
2048
|
+
`${docker.homeDir}/.claude.json:/home/user/.claude.json`,
|
|
2049
|
+
"-v",
|
|
2050
|
+
`${docker.homeDir}/.ssh:/home/user/.ssh:ro`,
|
|
2051
|
+
"-e",
|
|
2052
|
+
"HOME=/home/user"
|
|
2049
2053
|
];
|
|
2050
2054
|
if (docker.hasGitconfig) {
|
|
2051
|
-
dockerArguments.push("-v", `${docker.homeDir}/.gitconfig:/
|
|
2055
|
+
dockerArguments.push("-v", `${docker.homeDir}/.gitconfig:/home/user/.gitconfig:ro`);
|
|
2052
2056
|
}
|
|
2053
2057
|
for (const [key, value] of Object.entries(env)) {
|
|
2054
2058
|
dockerArguments.push("-e", `${key}=${value}`);
|
|
2055
2059
|
}
|
|
2060
|
+
for (const key of ["CLAUDE_CODE_OAUTH_TOKEN", "OPENAI_API_KEY"]) {
|
|
2061
|
+
if (process.env[key] && !(key in env)) {
|
|
2062
|
+
dockerArguments.push("-e", `${key}=${process.env[key]}`);
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2056
2065
|
dockerArguments.push(docker.imageTag);
|
|
2057
2066
|
dockerArguments.push("claude");
|
|
2058
2067
|
dockerArguments.push(...claudeArguments);
|
|
@@ -2535,6 +2544,11 @@ async function removeRepository(path, spawn, context) {
|
|
|
2535
2544
|
});
|
|
2536
2545
|
}
|
|
2537
2546
|
|
|
2547
|
+
// lib/bucket/repository-loop.ts
|
|
2548
|
+
import { existsSync as fsExistsSync } from "node:fs";
|
|
2549
|
+
import os2 from "node:os";
|
|
2550
|
+
import path3 from "node:path";
|
|
2551
|
+
|
|
2538
2552
|
// lib/agent-events.ts
|
|
2539
2553
|
function rawEventToAgentEvent(rawEvent) {
|
|
2540
2554
|
if (typeof rawEvent.type === "string" && rawEvent.type === "stream_event") {
|
|
@@ -3166,8 +3180,10 @@ async function loopClaude(dependencies, loopDependencies = createDefaultDependen
|
|
|
3166
3180
|
homedir: loopDependencies.dockerDeps?.homedir ?? os.homedir,
|
|
3167
3181
|
existsSync: loopDependencies.dockerDeps?.existsSync ?? existsSync
|
|
3168
3182
|
};
|
|
3183
|
+
log2(`checking for .dust/Dockerfile in ${context.cwd}`);
|
|
3169
3184
|
if (hasDockerfile(context.cwd, dockerDeps)) {
|
|
3170
3185
|
const imageTag = generateImageTag(context.cwd);
|
|
3186
|
+
log2(`Dockerfile found, image tag: ${imageTag}`);
|
|
3171
3187
|
onLoopEvent({ type: "loop.docker_detected", imageTag });
|
|
3172
3188
|
if (!await isDockerAvailable(dockerDeps)) {
|
|
3173
3189
|
context.stderr("Docker not available. Install Docker or remove .dust/Dockerfile to run without Docker.");
|
|
@@ -3188,6 +3204,12 @@ async function loopClaude(dependencies, loopDependencies = createDefaultDependen
|
|
|
3188
3204
|
homeDir,
|
|
3189
3205
|
hasGitconfig: existsSync(path2.join(homeDir, ".gitconfig"))
|
|
3190
3206
|
};
|
|
3207
|
+
if (!process.env.CLAUDE_CODE_OAUTH_TOKEN) {
|
|
3208
|
+
context.stderr("Docker mode requires CLAUDE_CODE_OAUTH_TOKEN. Run `claude setup-token` and export the token.");
|
|
3209
|
+
return { exitCode: 1 };
|
|
3210
|
+
}
|
|
3211
|
+
} else {
|
|
3212
|
+
log2("no .dust/Dockerfile found, running without Docker");
|
|
3191
3213
|
}
|
|
3192
3214
|
log2(`starting loop, maxIterations=${maxIterations}, sessionId=${sessionId}`);
|
|
3193
3215
|
onLoopEvent({ type: "loop.warning" });
|
|
@@ -3518,6 +3540,46 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
|
|
|
3518
3540
|
loopState
|
|
3519
3541
|
});
|
|
3520
3542
|
const hooksInstalled = await manageGitHooks(commandDeps);
|
|
3543
|
+
let dockerConfig;
|
|
3544
|
+
const dockerDeps = {
|
|
3545
|
+
spawn: repoDeps.dockerDeps?.spawn ?? spawn,
|
|
3546
|
+
homedir: repoDeps.dockerDeps?.homedir ?? os2.homedir,
|
|
3547
|
+
existsSync: repoDeps.dockerDeps?.existsSync ?? fsExistsSync
|
|
3548
|
+
};
|
|
3549
|
+
log3(`checking for .dust/Dockerfile in ${repoState.path}`);
|
|
3550
|
+
if (hasDockerfile(repoState.path, dockerDeps)) {
|
|
3551
|
+
const imageTag = generateImageTag(repoState.path);
|
|
3552
|
+
log3(`Dockerfile found, image tag: ${imageTag}`);
|
|
3553
|
+
onLoopEvent({ type: "loop.docker_detected", imageTag });
|
|
3554
|
+
if (!await isDockerAvailable(dockerDeps)) {
|
|
3555
|
+
log3("Docker not available");
|
|
3556
|
+
appendLogLine(repoState.logBuffer, createLogLine("Docker not available. Install Docker or remove .dust/Dockerfile.", "stderr"));
|
|
3557
|
+
} else {
|
|
3558
|
+
onLoopEvent({ type: "loop.docker_building", imageTag });
|
|
3559
|
+
const buildResult = await buildDockerImage({ repoPath: repoState.path, imageTag }, dockerDeps);
|
|
3560
|
+
if (!buildResult.success) {
|
|
3561
|
+
onLoopEvent({ type: "loop.docker_error", error: buildResult.error });
|
|
3562
|
+
log3(`Docker build failed: ${buildResult.error}`);
|
|
3563
|
+
} else {
|
|
3564
|
+
onLoopEvent({ type: "loop.docker_built", imageTag });
|
|
3565
|
+
const homeDir = dockerDeps.homedir();
|
|
3566
|
+
dockerConfig = {
|
|
3567
|
+
imageTag,
|
|
3568
|
+
repoPath: repoState.path,
|
|
3569
|
+
homeDir,
|
|
3570
|
+
hasGitconfig: dockerDeps.existsSync(path3.join(homeDir, ".gitconfig"))
|
|
3571
|
+
};
|
|
3572
|
+
log3(`Docker config ready: ${JSON.stringify(dockerConfig)}`);
|
|
3573
|
+
if (!process.env.CLAUDE_CODE_OAUTH_TOKEN) {
|
|
3574
|
+
log3("CLAUDE_CODE_OAUTH_TOKEN is not set, cannot run in Docker mode");
|
|
3575
|
+
appendLogLine(repoState.logBuffer, createLogLine("Docker mode requires CLAUDE_CODE_OAUTH_TOKEN. Run `claude setup-token` and export the token.", "stderr"));
|
|
3576
|
+
return;
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
} else {
|
|
3581
|
+
log3("no .dust/Dockerfile found, running without Docker");
|
|
3582
|
+
}
|
|
3521
3583
|
log3(`loop started for ${repoName} at ${repoState.path}`);
|
|
3522
3584
|
while (!repoState.stopRequested) {
|
|
3523
3585
|
loopState.agentSessionId = crypto.randomUUID();
|
|
@@ -3530,7 +3592,8 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
|
|
|
3530
3592
|
hooksInstalled,
|
|
3531
3593
|
signal: abortController.signal,
|
|
3532
3594
|
repositoryId: repoState.repository.id.toString(),
|
|
3533
|
-
onRawEvent: createHeartbeatThrottler(onAgentEvent)
|
|
3595
|
+
onRawEvent: createHeartbeatThrottler(onAgentEvent),
|
|
3596
|
+
docker: dockerConfig
|
|
3534
3597
|
});
|
|
3535
3598
|
} catch (error) {
|
|
3536
3599
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -4212,27 +4275,27 @@ function defaultWriteStdout(data) {
|
|
|
4212
4275
|
}
|
|
4213
4276
|
function createAuthFileSystem(dependencies) {
|
|
4214
4277
|
return {
|
|
4215
|
-
exists: (
|
|
4278
|
+
exists: (path4) => {
|
|
4216
4279
|
try {
|
|
4217
|
-
dependencies.accessSync(
|
|
4280
|
+
dependencies.accessSync(path4);
|
|
4218
4281
|
return true;
|
|
4219
4282
|
} catch {
|
|
4220
4283
|
return false;
|
|
4221
4284
|
}
|
|
4222
4285
|
},
|
|
4223
|
-
isDirectory: (
|
|
4286
|
+
isDirectory: (path4) => {
|
|
4224
4287
|
try {
|
|
4225
|
-
return dependencies.statSync(
|
|
4288
|
+
return dependencies.statSync(path4).isDirectory();
|
|
4226
4289
|
} catch {
|
|
4227
4290
|
return false;
|
|
4228
4291
|
}
|
|
4229
4292
|
},
|
|
4230
|
-
getFileCreationTime: (
|
|
4231
|
-
readFile: (
|
|
4232
|
-
writeFile: (
|
|
4233
|
-
mkdir: (
|
|
4234
|
-
readdir: (
|
|
4235
|
-
chmod: (
|
|
4293
|
+
getFileCreationTime: (path4) => dependencies.statSync(path4).birthtimeMs,
|
|
4294
|
+
readFile: (path4) => dependencies.readFile(path4, "utf8"),
|
|
4295
|
+
writeFile: (path4, content) => dependencies.writeFile(path4, content, "utf8"),
|
|
4296
|
+
mkdir: (path4, options) => dependencies.mkdir(path4, options).then(() => {}),
|
|
4297
|
+
readdir: (path4) => dependencies.readdir(path4),
|
|
4298
|
+
chmod: (path4, mode) => dependencies.chmod(path4, mode),
|
|
4236
4299
|
rename: (oldPath, newPath) => dependencies.rename(oldPath, newPath)
|
|
4237
4300
|
};
|
|
4238
4301
|
}
|
|
@@ -4699,16 +4762,16 @@ function createDefaultUploadDependencies() {
|
|
|
4699
4762
|
getHomeDir: () => homedir2(),
|
|
4700
4763
|
fileSystem: authFileSystem
|
|
4701
4764
|
},
|
|
4702
|
-
readFileBytes: async (
|
|
4703
|
-
const buffer = await Bun.file(
|
|
4765
|
+
readFileBytes: async (path4) => {
|
|
4766
|
+
const buffer = await Bun.file(path4).arrayBuffer();
|
|
4704
4767
|
return new Uint8Array(buffer);
|
|
4705
4768
|
},
|
|
4706
|
-
getFileSize: async (
|
|
4707
|
-
const file = Bun.file(
|
|
4769
|
+
getFileSize: async (path4) => {
|
|
4770
|
+
const file = Bun.file(path4);
|
|
4708
4771
|
return file.size;
|
|
4709
4772
|
},
|
|
4710
|
-
fileExists: async (
|
|
4711
|
-
const file = Bun.file(
|
|
4773
|
+
fileExists: async (path4) => {
|
|
4774
|
+
const file = Bun.file(path4);
|
|
4712
4775
|
return file.exists();
|
|
4713
4776
|
},
|
|
4714
4777
|
uploadFile: async (url, token, fileBytes, contentType, fileName) => {
|
|
@@ -5502,12 +5565,12 @@ function validateNoCycles(allPrincipleRelationships) {
|
|
|
5502
5565
|
}
|
|
5503
5566
|
for (const rel of allPrincipleRelationships) {
|
|
5504
5567
|
const visited = new Set;
|
|
5505
|
-
const
|
|
5568
|
+
const path4 = [];
|
|
5506
5569
|
let current = rel.filePath;
|
|
5507
5570
|
while (current) {
|
|
5508
5571
|
if (visited.has(current)) {
|
|
5509
|
-
const cycleStart =
|
|
5510
|
-
const cyclePath =
|
|
5572
|
+
const cycleStart = path4.indexOf(current);
|
|
5573
|
+
const cyclePath = path4.slice(cycleStart).concat(current);
|
|
5511
5574
|
violations.push({
|
|
5512
5575
|
file: rel.filePath,
|
|
5513
5576
|
message: `Cycle detected in principle hierarchy: ${cyclePath.join(" -> ")}`
|
|
@@ -5515,7 +5578,7 @@ function validateNoCycles(allPrincipleRelationships) {
|
|
|
5515
5578
|
break;
|
|
5516
5579
|
}
|
|
5517
5580
|
visited.add(current);
|
|
5518
|
-
|
|
5581
|
+
path4.push(current);
|
|
5519
5582
|
const currentRel = relationshipMap.get(current);
|
|
5520
5583
|
if (currentRel && currentRel.parentPrinciples.length > 0) {
|
|
5521
5584
|
current = currentRel.parentPrinciples[0];
|
|
@@ -6502,8 +6565,8 @@ function parseGitDiffNameStatus(output) {
|
|
|
6502
6565
|
const parts = line.split("\t");
|
|
6503
6566
|
if (parts.length >= 2) {
|
|
6504
6567
|
const statusChar = parts[0].charAt(0);
|
|
6505
|
-
const
|
|
6506
|
-
changes.push({ status: statusChar, path:
|
|
6568
|
+
const path4 = parts.length > 2 ? parts[2] : parts[1];
|
|
6569
|
+
changes.push({ status: statusChar, path: path4 });
|
|
6507
6570
|
}
|
|
6508
6571
|
}
|
|
6509
6572
|
return changes;
|
|
@@ -6564,12 +6627,12 @@ async function getUncommittedFiles(cwd, gitRunner) {
|
|
|
6564
6627
|
`).filter((line) => line.length > 0);
|
|
6565
6628
|
for (const line of lines) {
|
|
6566
6629
|
if (line.length > 3) {
|
|
6567
|
-
const
|
|
6568
|
-
const arrowIndex =
|
|
6630
|
+
const path4 = line.substring(3);
|
|
6631
|
+
const arrowIndex = path4.indexOf(" -> ");
|
|
6569
6632
|
if (arrowIndex !== -1) {
|
|
6570
|
-
files.push(
|
|
6633
|
+
files.push(path4.substring(arrowIndex + 4));
|
|
6571
6634
|
} else {
|
|
6572
|
-
files.push(
|
|
6635
|
+
files.push(path4);
|
|
6573
6636
|
}
|
|
6574
6637
|
}
|
|
6575
6638
|
}
|
|
@@ -6720,24 +6783,24 @@ async function main(options) {
|
|
|
6720
6783
|
function createFileSystem(primitives) {
|
|
6721
6784
|
return {
|
|
6722
6785
|
exists: primitives.existsSync,
|
|
6723
|
-
isDirectory: (
|
|
6786
|
+
isDirectory: (path4) => {
|
|
6724
6787
|
try {
|
|
6725
|
-
return primitives.statSync(
|
|
6788
|
+
return primitives.statSync(path4).isDirectory();
|
|
6726
6789
|
} catch {
|
|
6727
6790
|
return false;
|
|
6728
6791
|
}
|
|
6729
6792
|
},
|
|
6730
|
-
readFile: (
|
|
6731
|
-
writeFile: (
|
|
6793
|
+
readFile: (path4) => primitives.readFile(path4, "utf-8"),
|
|
6794
|
+
writeFile: (path4, content, options) => primitives.writeFile(path4, content, {
|
|
6732
6795
|
encoding: "utf-8",
|
|
6733
6796
|
flag: options?.flag
|
|
6734
6797
|
}),
|
|
6735
|
-
mkdir: async (
|
|
6736
|
-
await primitives.mkdir(
|
|
6798
|
+
mkdir: async (path4, options) => {
|
|
6799
|
+
await primitives.mkdir(path4, options);
|
|
6737
6800
|
},
|
|
6738
|
-
getFileCreationTime: (
|
|
6739
|
-
readdir: (
|
|
6740
|
-
chmod: (
|
|
6801
|
+
getFileCreationTime: (path4) => primitives.statSync(path4).birthtimeMs,
|
|
6802
|
+
readdir: (path4) => primitives.readdir(path4),
|
|
6803
|
+
chmod: (path4, mode) => primitives.chmod(path4, mode),
|
|
6741
6804
|
rename: (oldPath, newPath) => primitives.rename(oldPath, newPath)
|
|
6742
6805
|
};
|
|
6743
6806
|
}
|