@joshski/dust 0.1.84 → 0.1.86
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/biome/no-vitest-mocking.grit +25 -0
- package/dist/agent-events.d.ts +7 -6
- package/dist/bucket/repository-loop.d.ts +11 -0
- package/dist/docker/docker-agent.d.ts +36 -0
- package/dist/dust.js +626 -326
- package/dist/logging/index.d.ts +4 -1
- package/dist/logging.js +7 -6
- package/package.json +1 -1
package/dist/dust.js
CHANGED
|
@@ -319,7 +319,7 @@ async function loadSettings(cwd, fileSystem) {
|
|
|
319
319
|
}
|
|
320
320
|
|
|
321
321
|
// lib/version.ts
|
|
322
|
-
var DUST_VERSION = "0.1.
|
|
322
|
+
var DUST_VERSION = "0.1.86";
|
|
323
323
|
|
|
324
324
|
// lib/session.ts
|
|
325
325
|
var DUST_UNATTENDED = "DUST_UNATTENDED";
|
|
@@ -1740,7 +1740,7 @@ async function defaultExchangeCode(code, fetchFn = fetch) {
|
|
|
1740
1740
|
return body.token;
|
|
1741
1741
|
}
|
|
1742
1742
|
async function authenticate(authDeps) {
|
|
1743
|
-
const exchange = authDeps.exchangeCode ?? defaultExchangeCode;
|
|
1743
|
+
const exchange = authDeps.exchangeCode ?? ((code) => defaultExchangeCode(code, authDeps.fetch));
|
|
1744
1744
|
return new Promise((resolve, reject) => {
|
|
1745
1745
|
let timer = null;
|
|
1746
1746
|
let serverHandle = null;
|
|
@@ -1840,6 +1840,264 @@ function openBrowser(url) {
|
|
|
1840
1840
|
nodeSpawn(cmd, [url], { stdio: "ignore", detached: true }).unref();
|
|
1841
1841
|
}
|
|
1842
1842
|
|
|
1843
|
+
// lib/bucket/bucket-state.ts
|
|
1844
|
+
function handleServerMessage(state, message) {
|
|
1845
|
+
const effects = [];
|
|
1846
|
+
effects.push({
|
|
1847
|
+
type: "debugLog",
|
|
1848
|
+
message: `ws message: ${message.type}`
|
|
1849
|
+
});
|
|
1850
|
+
switch (message.type) {
|
|
1851
|
+
case "repository-list": {
|
|
1852
|
+
const repos = message.repositories;
|
|
1853
|
+
effects.push({
|
|
1854
|
+
type: "log",
|
|
1855
|
+
message: `Received repository list (${repos.length} repositories):`,
|
|
1856
|
+
stream: "stdout"
|
|
1857
|
+
});
|
|
1858
|
+
if (repos.length === 0) {
|
|
1859
|
+
effects.push({
|
|
1860
|
+
type: "log",
|
|
1861
|
+
message: " (empty)",
|
|
1862
|
+
stream: "stdout"
|
|
1863
|
+
});
|
|
1864
|
+
} else {
|
|
1865
|
+
for (const r of repos) {
|
|
1866
|
+
effects.push({
|
|
1867
|
+
type: "log",
|
|
1868
|
+
message: ` - name=${r.name}`,
|
|
1869
|
+
stream: "stdout"
|
|
1870
|
+
});
|
|
1871
|
+
effects.push({
|
|
1872
|
+
type: "log",
|
|
1873
|
+
message: ` id=${r.id}`,
|
|
1874
|
+
stream: "stdout"
|
|
1875
|
+
});
|
|
1876
|
+
effects.push({
|
|
1877
|
+
type: "log",
|
|
1878
|
+
message: ` gitUrl=${r.gitUrl}`,
|
|
1879
|
+
stream: "stdout"
|
|
1880
|
+
});
|
|
1881
|
+
effects.push({
|
|
1882
|
+
type: "log",
|
|
1883
|
+
message: ` gitSshUrl=${r.gitSshUrl ?? "(none)"}`,
|
|
1884
|
+
stream: "stdout"
|
|
1885
|
+
});
|
|
1886
|
+
effects.push({
|
|
1887
|
+
type: "log",
|
|
1888
|
+
message: ` url=${r.url}`,
|
|
1889
|
+
stream: "stdout"
|
|
1890
|
+
});
|
|
1891
|
+
effects.push({
|
|
1892
|
+
type: "log",
|
|
1893
|
+
message: ` hasTask=${r.hasTask}`,
|
|
1894
|
+
stream: "stdout"
|
|
1895
|
+
});
|
|
1896
|
+
if (r.agentProvider) {
|
|
1897
|
+
effects.push({
|
|
1898
|
+
type: "log",
|
|
1899
|
+
message: ` agentProvider=${r.agentProvider}`,
|
|
1900
|
+
stream: "stdout"
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
effects.push({
|
|
1906
|
+
type: "syncUI",
|
|
1907
|
+
repositories: repos
|
|
1908
|
+
});
|
|
1909
|
+
effects.push({
|
|
1910
|
+
type: "handleRepositoryList",
|
|
1911
|
+
repositories: repos
|
|
1912
|
+
});
|
|
1913
|
+
break;
|
|
1914
|
+
}
|
|
1915
|
+
case "task-available": {
|
|
1916
|
+
const repoName = message.repository;
|
|
1917
|
+
effects.push({
|
|
1918
|
+
type: "log",
|
|
1919
|
+
message: `Received task-available for ${repoName}`,
|
|
1920
|
+
stream: "stdout"
|
|
1921
|
+
});
|
|
1922
|
+
if (state.repositoryNames.includes(repoName)) {
|
|
1923
|
+
effects.push({
|
|
1924
|
+
type: "signalTaskAvailable",
|
|
1925
|
+
repositoryName: repoName
|
|
1926
|
+
});
|
|
1927
|
+
} else {
|
|
1928
|
+
effects.push({
|
|
1929
|
+
type: "log",
|
|
1930
|
+
message: `No repository state found for ${repoName}`,
|
|
1931
|
+
stream: "stderr"
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
break;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
return { effects };
|
|
1938
|
+
}
|
|
1939
|
+
function handleMessageParseError(rawData) {
|
|
1940
|
+
return {
|
|
1941
|
+
effects: [
|
|
1942
|
+
{
|
|
1943
|
+
type: "log",
|
|
1944
|
+
message: `Failed to parse WebSocket message: ${rawData}`,
|
|
1945
|
+
stream: "stderr"
|
|
1946
|
+
}
|
|
1947
|
+
]
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
function handleInvalidMessageFormat(rawData) {
|
|
1951
|
+
return {
|
|
1952
|
+
effects: [
|
|
1953
|
+
{
|
|
1954
|
+
type: "log",
|
|
1955
|
+
message: `Invalid WebSocket message format: ${rawData}`,
|
|
1956
|
+
stream: "stderr"
|
|
1957
|
+
}
|
|
1958
|
+
]
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
var KEYS = {
|
|
1962
|
+
UP: "\x1B[A",
|
|
1963
|
+
DOWN: "\x1B[B",
|
|
1964
|
+
RIGHT: "\x1B[C",
|
|
1965
|
+
LEFT: "\x1B[D",
|
|
1966
|
+
PAGE_UP: "\x1B[5~",
|
|
1967
|
+
PAGE_DOWN: "\x1B[6~",
|
|
1968
|
+
HOME: "\x1B[H",
|
|
1969
|
+
END: "\x1B[F",
|
|
1970
|
+
CTRL_C: "\x03"
|
|
1971
|
+
};
|
|
1972
|
+
var SGR_MOUSE_RE = new RegExp(String.raw`^\x1b\[<(\d+);\d+;\d+[Mm]$`);
|
|
1973
|
+
function parseSGRMouse(key) {
|
|
1974
|
+
const match = key.match(SGR_MOUSE_RE);
|
|
1975
|
+
if (!match)
|
|
1976
|
+
return null;
|
|
1977
|
+
return Number.parseInt(match[1], 10);
|
|
1978
|
+
}
|
|
1979
|
+
function handleKeypress(state, key) {
|
|
1980
|
+
const effects = [];
|
|
1981
|
+
const mouseButton = parseSGRMouse(key);
|
|
1982
|
+
if (mouseButton !== null) {
|
|
1983
|
+
if (mouseButton === 64) {
|
|
1984
|
+
effects.push({ type: "scroll", direction: "up" });
|
|
1985
|
+
effects.push({ type: "scroll", direction: "up" });
|
|
1986
|
+
effects.push({ type: "scroll", direction: "up" });
|
|
1987
|
+
} else if (mouseButton === 65) {
|
|
1988
|
+
effects.push({ type: "scroll", direction: "down" });
|
|
1989
|
+
effects.push({ type: "scroll", direction: "down" });
|
|
1990
|
+
effects.push({ type: "scroll", direction: "down" });
|
|
1991
|
+
}
|
|
1992
|
+
return { effects };
|
|
1993
|
+
}
|
|
1994
|
+
switch (key) {
|
|
1995
|
+
case "q":
|
|
1996
|
+
case KEYS.CTRL_C:
|
|
1997
|
+
effects.push({ type: "quit" });
|
|
1998
|
+
break;
|
|
1999
|
+
case KEYS.LEFT:
|
|
2000
|
+
effects.push({ type: "selectPrevious" });
|
|
2001
|
+
break;
|
|
2002
|
+
case KEYS.RIGHT:
|
|
2003
|
+
effects.push({ type: "selectNext" });
|
|
2004
|
+
break;
|
|
2005
|
+
case KEYS.UP:
|
|
2006
|
+
effects.push({ type: "scroll", direction: "up" });
|
|
2007
|
+
break;
|
|
2008
|
+
case KEYS.DOWN:
|
|
2009
|
+
effects.push({ type: "scroll", direction: "down" });
|
|
2010
|
+
break;
|
|
2011
|
+
case KEYS.PAGE_UP:
|
|
2012
|
+
effects.push({ type: "scroll", direction: "pageUp" });
|
|
2013
|
+
break;
|
|
2014
|
+
case KEYS.PAGE_DOWN:
|
|
2015
|
+
effects.push({ type: "scroll", direction: "pageDown" });
|
|
2016
|
+
break;
|
|
2017
|
+
case "g":
|
|
2018
|
+
case KEYS.HOME:
|
|
2019
|
+
effects.push({ type: "scroll", direction: "top" });
|
|
2020
|
+
break;
|
|
2021
|
+
case "G":
|
|
2022
|
+
case KEYS.END:
|
|
2023
|
+
effects.push({ type: "scroll", direction: "bottom" });
|
|
2024
|
+
break;
|
|
2025
|
+
case "o": {
|
|
2026
|
+
if (state.selectedIndex === -1) {
|
|
2027
|
+
break;
|
|
2028
|
+
}
|
|
2029
|
+
const repoName = state.repositories[state.selectedIndex];
|
|
2030
|
+
if (!repoName)
|
|
2031
|
+
break;
|
|
2032
|
+
const url = state.repositoryUrls[repoName];
|
|
2033
|
+
if (url) {
|
|
2034
|
+
effects.push({ type: "openBrowser", url });
|
|
2035
|
+
}
|
|
2036
|
+
break;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
return { effects };
|
|
2040
|
+
}
|
|
2041
|
+
var INITIAL_RECONNECT_DELAY_MS = 1000;
|
|
2042
|
+
var MAX_RECONNECT_DELAY_MS = 30000;
|
|
2043
|
+
function handleClose(state, code, reason) {
|
|
2044
|
+
const effects = [];
|
|
2045
|
+
effects.push({
|
|
2046
|
+
type: "log",
|
|
2047
|
+
message: `bucket.disconnected code=${code} reason=${reason || "none"}`,
|
|
2048
|
+
stream: "stdout"
|
|
2049
|
+
});
|
|
2050
|
+
if (code === 4000) {
|
|
2051
|
+
effects.push({
|
|
2052
|
+
type: "log",
|
|
2053
|
+
message: "Another connection replaced this one. Not reconnecting.",
|
|
2054
|
+
stream: "stdout"
|
|
2055
|
+
});
|
|
2056
|
+
return { state, effects };
|
|
2057
|
+
}
|
|
2058
|
+
if (!state.shuttingDown) {
|
|
2059
|
+
effects.push({
|
|
2060
|
+
type: "log",
|
|
2061
|
+
message: `Reconnecting in ${state.reconnectDelay / 1000} seconds...`,
|
|
2062
|
+
stream: "stdout"
|
|
2063
|
+
});
|
|
2064
|
+
effects.push({
|
|
2065
|
+
type: "scheduleReconnect",
|
|
2066
|
+
delayMs: state.reconnectDelay
|
|
2067
|
+
});
|
|
2068
|
+
const nextDelay = Math.min(state.reconnectDelay * 2, MAX_RECONNECT_DELAY_MS);
|
|
2069
|
+
return {
|
|
2070
|
+
state: { ...state, reconnectDelay: nextDelay },
|
|
2071
|
+
effects
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
return { state, effects };
|
|
2075
|
+
}
|
|
2076
|
+
function handleError(state, errorMessage) {
|
|
2077
|
+
return {
|
|
2078
|
+
state,
|
|
2079
|
+
effects: [
|
|
2080
|
+
{
|
|
2081
|
+
type: "log",
|
|
2082
|
+
message: `WebSocket error: ${errorMessage}`,
|
|
2083
|
+
stream: "stderr"
|
|
2084
|
+
}
|
|
2085
|
+
]
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
function handleOpen(state) {
|
|
2089
|
+
return {
|
|
2090
|
+
state: { ...state, reconnectDelay: INITIAL_RECONNECT_DELAY_MS },
|
|
2091
|
+
effects: [
|
|
2092
|
+
{
|
|
2093
|
+
type: "log",
|
|
2094
|
+
message: "bucket.connected",
|
|
2095
|
+
stream: "stdout"
|
|
2096
|
+
}
|
|
2097
|
+
]
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
|
|
1843
2101
|
// lib/bucket/events.ts
|
|
1844
2102
|
var WS_OPEN = 1;
|
|
1845
2103
|
function formatBucketEvent(event) {
|
|
@@ -1965,7 +2223,8 @@ class FileSink {
|
|
|
1965
2223
|
|
|
1966
2224
|
// lib/logging/index.ts
|
|
1967
2225
|
var DUST_LOG_FILE = "DUST_LOG_FILE";
|
|
1968
|
-
function createLoggingService() {
|
|
2226
|
+
function createLoggingService(options) {
|
|
2227
|
+
const writeStdout = options?.stdout ?? process.stdout.write.bind(process.stdout);
|
|
1969
2228
|
let patterns = null;
|
|
1970
2229
|
let initialized = false;
|
|
1971
2230
|
let activeFileSink = null;
|
|
@@ -1995,12 +2254,12 @@ function createLoggingService() {
|
|
|
1995
2254
|
}
|
|
1996
2255
|
activeFileSink = sinkForTesting ?? new FileSink(path);
|
|
1997
2256
|
},
|
|
1998
|
-
createLogger(name,
|
|
2257
|
+
createLogger(name, options2) {
|
|
1999
2258
|
let perLoggerSink;
|
|
2000
|
-
if (
|
|
2259
|
+
if (options2?.file === false) {
|
|
2001
2260
|
perLoggerSink = null;
|
|
2002
|
-
} else if (typeof
|
|
2003
|
-
perLoggerSink = getOrCreateFileSink(
|
|
2261
|
+
} else if (typeof options2?.file === "string") {
|
|
2262
|
+
perLoggerSink = getOrCreateFileSink(options2.file);
|
|
2004
2263
|
}
|
|
2005
2264
|
return (...messages) => {
|
|
2006
2265
|
init();
|
|
@@ -2013,7 +2272,7 @@ function createLoggingService() {
|
|
|
2013
2272
|
activeFileSink.write(line);
|
|
2014
2273
|
}
|
|
2015
2274
|
if (patterns && matchesAny(name, patterns)) {
|
|
2016
|
-
|
|
2275
|
+
writeStdout(line);
|
|
2017
2276
|
}
|
|
2018
2277
|
};
|
|
2019
2278
|
},
|
|
@@ -2548,22 +2807,21 @@ async function removeRepository(path, spawn, context) {
|
|
|
2548
2807
|
// lib/bucket/repository-loop.ts
|
|
2549
2808
|
import { existsSync as fsExistsSync } from "node:fs";
|
|
2550
2809
|
import os2 from "node:os";
|
|
2551
|
-
import path3 from "node:path";
|
|
2552
2810
|
|
|
2553
2811
|
// lib/agent-events.ts
|
|
2554
|
-
function rawEventToAgentEvent(rawEvent) {
|
|
2812
|
+
function rawEventToAgentEvent(rawEvent, provider) {
|
|
2555
2813
|
if (typeof rawEvent.type === "string" && rawEvent.type === "stream_event") {
|
|
2556
2814
|
return { type: "agent-session-activity" };
|
|
2557
2815
|
}
|
|
2558
|
-
return { type: "
|
|
2816
|
+
return { type: "agent-event", provider, rawEvent };
|
|
2559
2817
|
}
|
|
2560
2818
|
var DEFAULT_HEARTBEAT_INTERVAL_MS = 5000;
|
|
2561
|
-
function createHeartbeatThrottler(onAgentEvent, options) {
|
|
2819
|
+
function createHeartbeatThrottler(onAgentEvent, provider, options) {
|
|
2562
2820
|
const intervalMs = options?.intervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
2563
2821
|
const now = options?.now ?? Date.now;
|
|
2564
2822
|
let lastHeartbeatTime;
|
|
2565
2823
|
return (rawEvent) => {
|
|
2566
|
-
const event = rawEventToAgentEvent(rawEvent);
|
|
2824
|
+
const event = rawEventToAgentEvent(rawEvent, provider);
|
|
2567
2825
|
if (event.type === "agent-session-activity") {
|
|
2568
2826
|
const currentTime = now();
|
|
2569
2827
|
if (lastHeartbeatTime !== undefined && currentTime - lastHeartbeatTime < intervalMs) {
|
|
@@ -2588,7 +2846,7 @@ function formatAgentEvent(event) {
|
|
|
2588
2846
|
case "agent-session-ended":
|
|
2589
2847
|
return event.success ? "\uD83E\uDD16 Agent session ended (success)" : `\uD83E\uDD16 Agent session ended (error: ${event.error})`;
|
|
2590
2848
|
case "agent-session-activity":
|
|
2591
|
-
case "
|
|
2849
|
+
case "agent-event":
|
|
2592
2850
|
return null;
|
|
2593
2851
|
}
|
|
2594
2852
|
}
|
|
@@ -2597,7 +2855,6 @@ function formatAgentEvent(event) {
|
|
|
2597
2855
|
import { spawn as nodeSpawn3 } from "node:child_process";
|
|
2598
2856
|
import { existsSync } from "node:fs";
|
|
2599
2857
|
import os from "node:os";
|
|
2600
|
-
import path2 from "node:path";
|
|
2601
2858
|
|
|
2602
2859
|
// lib/docker/docker-agent.ts
|
|
2603
2860
|
import path from "node:path";
|
|
@@ -2655,6 +2912,36 @@ function hasDockerfile(repoPath, dependencies) {
|
|
|
2655
2912
|
const dockerfilePath = path.join(repoPath, ".dust", "Dockerfile");
|
|
2656
2913
|
return dependencies.existsSync(dockerfilePath);
|
|
2657
2914
|
}
|
|
2915
|
+
async function prepareDockerConfig(repoPath, dependencies, onEvent) {
|
|
2916
|
+
log(`checking for .dust/Dockerfile in ${repoPath}`);
|
|
2917
|
+
if (!hasDockerfile(repoPath, dependencies)) {
|
|
2918
|
+
log("no .dust/Dockerfile found, running without Docker");
|
|
2919
|
+
return {};
|
|
2920
|
+
}
|
|
2921
|
+
const imageTag = generateImageTag(repoPath);
|
|
2922
|
+
log(`Dockerfile found, image tag: ${imageTag}`);
|
|
2923
|
+
onEvent({ type: "loop.docker_detected", imageTag });
|
|
2924
|
+
if (!await isDockerAvailable(dependencies)) {
|
|
2925
|
+
const error = "Docker not available. Install Docker or remove .dust/Dockerfile to run without Docker.";
|
|
2926
|
+
return { error };
|
|
2927
|
+
}
|
|
2928
|
+
onEvent({ type: "loop.docker_building", imageTag });
|
|
2929
|
+
const buildResult = await buildDockerImage({ repoPath, imageTag }, dependencies);
|
|
2930
|
+
if (!buildResult.success) {
|
|
2931
|
+
onEvent({ type: "loop.docker_error", error: buildResult.error });
|
|
2932
|
+
return { error: buildResult.error };
|
|
2933
|
+
}
|
|
2934
|
+
onEvent({ type: "loop.docker_built", imageTag });
|
|
2935
|
+
const homeDir = dependencies.homedir();
|
|
2936
|
+
const config = {
|
|
2937
|
+
imageTag,
|
|
2938
|
+
repoPath,
|
|
2939
|
+
homeDir,
|
|
2940
|
+
hasGitconfig: dependencies.existsSync(path.join(homeDir, ".gitconfig"))
|
|
2941
|
+
};
|
|
2942
|
+
log(`Docker config ready: ${JSON.stringify(config)}`);
|
|
2943
|
+
return { config };
|
|
2944
|
+
}
|
|
2658
2945
|
|
|
2659
2946
|
// lib/artifacts/workflow-tasks.ts
|
|
2660
2947
|
var IDEA_TRANSITION_PREFIXES = [
|
|
@@ -3181,36 +3468,17 @@ async function loopClaude(dependencies, loopDependencies = createDefaultDependen
|
|
|
3181
3468
|
homedir: loopDependencies.dockerDeps?.homedir ?? os.homedir,
|
|
3182
3469
|
existsSync: loopDependencies.dockerDeps?.existsSync ?? existsSync
|
|
3183
3470
|
};
|
|
3184
|
-
|
|
3185
|
-
if (
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
return { exitCode: 1 };
|
|
3192
|
-
}
|
|
3193
|
-
onLoopEvent({ type: "loop.docker_building", imageTag });
|
|
3194
|
-
const buildResult = await buildDockerImage({ repoPath: context.cwd, imageTag }, dockerDeps);
|
|
3195
|
-
if (!buildResult.success) {
|
|
3196
|
-
onLoopEvent({ type: "loop.docker_error", error: buildResult.error });
|
|
3197
|
-
context.stderr(buildResult.error);
|
|
3198
|
-
return { exitCode: 1 };
|
|
3199
|
-
}
|
|
3200
|
-
onLoopEvent({ type: "loop.docker_built", imageTag });
|
|
3201
|
-
const homeDir = os.homedir();
|
|
3202
|
-
dockerConfig = {
|
|
3203
|
-
imageTag,
|
|
3204
|
-
repoPath: context.cwd,
|
|
3205
|
-
homeDir,
|
|
3206
|
-
hasGitconfig: existsSync(path2.join(homeDir, ".gitconfig"))
|
|
3207
|
-
};
|
|
3471
|
+
const dockerResult = await prepareDockerConfig(context.cwd, dockerDeps, onLoopEvent);
|
|
3472
|
+
if ("error" in dockerResult) {
|
|
3473
|
+
context.stderr(dockerResult.error);
|
|
3474
|
+
return { exitCode: 1 };
|
|
3475
|
+
}
|
|
3476
|
+
if ("config" in dockerResult) {
|
|
3477
|
+
dockerConfig = dockerResult.config;
|
|
3208
3478
|
if (!process.env.CLAUDE_CODE_OAUTH_TOKEN) {
|
|
3209
3479
|
context.stderr("Docker mode requires CLAUDE_CODE_OAUTH_TOKEN. Run `claude setup-token` and export the token.");
|
|
3210
3480
|
return { exitCode: 1 };
|
|
3211
3481
|
}
|
|
3212
|
-
} else {
|
|
3213
|
-
log2("no .dust/Dockerfile found, running without Docker");
|
|
3214
3482
|
}
|
|
3215
3483
|
log2(`starting loop, maxIterations=${maxIterations}, sessionId=${sessionId}`);
|
|
3216
3484
|
onLoopEvent({ type: "loop.warning" });
|
|
@@ -3227,7 +3495,7 @@ async function loopClaude(dependencies, loopDependencies = createDefaultDependen
|
|
|
3227
3495
|
docker: dockerConfig
|
|
3228
3496
|
};
|
|
3229
3497
|
if (eventsUrl) {
|
|
3230
|
-
iterationOptions.onRawEvent = createHeartbeatThrottler(onAgentEvent);
|
|
3498
|
+
iterationOptions.onRawEvent = createHeartbeatThrottler(onAgentEvent, loopDependencies.agentType ?? "claude");
|
|
3231
3499
|
}
|
|
3232
3500
|
while (completedIterations < maxIterations) {
|
|
3233
3501
|
agentSessionId = crypto.randomUUID();
|
|
@@ -3287,6 +3555,7 @@ async function* spawnCodex(prompt, options = {}, dependencies = defaultDependenc
|
|
|
3287
3555
|
});
|
|
3288
3556
|
proc.on("error", reject);
|
|
3289
3557
|
});
|
|
3558
|
+
closePromise.catch(() => {});
|
|
3290
3559
|
const abortHandler = () => {
|
|
3291
3560
|
if (!proc.killed) {
|
|
3292
3561
|
proc.kill();
|
|
@@ -3439,9 +3708,15 @@ function createBufferStdoutSink(loopState, logBuffer) {
|
|
|
3439
3708
|
}
|
|
3440
3709
|
};
|
|
3441
3710
|
}
|
|
3711
|
+
function createStdoutSinkFactory(loopState, logBuffer) {
|
|
3712
|
+
return () => createBufferStdoutSink(loopState, logBuffer);
|
|
3713
|
+
}
|
|
3442
3714
|
function createBufferRun(run3, bufferSinkDeps) {
|
|
3443
3715
|
return (prompt, options) => run3(prompt, options, bufferSinkDeps);
|
|
3444
3716
|
}
|
|
3717
|
+
function createCodexBufferRun(run3, codexBufferSinkDeps) {
|
|
3718
|
+
return (prompt, options) => run3(prompt, options, codexBufferSinkDeps);
|
|
3719
|
+
}
|
|
3445
3720
|
async function noOpPostEvent() {}
|
|
3446
3721
|
function createLoopEventHandler(logBuffer) {
|
|
3447
3722
|
return function onLoopEvent(event) {
|
|
@@ -3508,30 +3783,6 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
|
|
|
3508
3783
|
sequence: 0,
|
|
3509
3784
|
agentSessionId: undefined
|
|
3510
3785
|
};
|
|
3511
|
-
const isCodex = repoState.repository.agentProvider === "codex";
|
|
3512
|
-
const agentType = isCodex ? "codex" : "claude";
|
|
3513
|
-
const createStdoutSink2 = () => createBufferStdoutSink(loopState, repoState.logBuffer);
|
|
3514
|
-
let bufferRun;
|
|
3515
|
-
if (isCodex) {
|
|
3516
|
-
const codexBufferSinkDeps = {
|
|
3517
|
-
...defaultRunnerDependencies2,
|
|
3518
|
-
createStdoutSink: createStdoutSink2
|
|
3519
|
-
};
|
|
3520
|
-
bufferRun = (prompt, options) => run2(prompt, options, codexBufferSinkDeps);
|
|
3521
|
-
} else {
|
|
3522
|
-
const bufferSinkDeps = {
|
|
3523
|
-
...defaultRunnerDependencies,
|
|
3524
|
-
createStdoutSink: createStdoutSink2
|
|
3525
|
-
};
|
|
3526
|
-
bufferRun = createBufferRun(run3, bufferSinkDeps);
|
|
3527
|
-
}
|
|
3528
|
-
const loopDeps = {
|
|
3529
|
-
spawn,
|
|
3530
|
-
run: bufferRun,
|
|
3531
|
-
sleep,
|
|
3532
|
-
postEvent: noOpPostEvent,
|
|
3533
|
-
agentType
|
|
3534
|
-
};
|
|
3535
3786
|
const onLoopEvent = createLoopEventHandler(repoState.logBuffer);
|
|
3536
3787
|
const onAgentEvent = createAgentEventHandler({
|
|
3537
3788
|
repoState,
|
|
@@ -3547,43 +3798,53 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
|
|
|
3547
3798
|
homedir: repoDeps.dockerDeps?.homedir ?? os2.homedir,
|
|
3548
3799
|
existsSync: repoDeps.dockerDeps?.existsSync ?? fsExistsSync
|
|
3549
3800
|
};
|
|
3550
|
-
|
|
3551
|
-
if (
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
if (!
|
|
3556
|
-
log3("
|
|
3557
|
-
appendLogLine(repoState.logBuffer, createLogLine("Docker
|
|
3558
|
-
|
|
3559
|
-
onLoopEvent({ type: "loop.docker_building", imageTag });
|
|
3560
|
-
const buildResult = await buildDockerImage({ repoPath: repoState.path, imageTag }, dockerDeps);
|
|
3561
|
-
if (!buildResult.success) {
|
|
3562
|
-
onLoopEvent({ type: "loop.docker_error", error: buildResult.error });
|
|
3563
|
-
log3(`Docker build failed: ${buildResult.error}`);
|
|
3564
|
-
} else {
|
|
3565
|
-
onLoopEvent({ type: "loop.docker_built", imageTag });
|
|
3566
|
-
const homeDir = dockerDeps.homedir();
|
|
3567
|
-
dockerConfig = {
|
|
3568
|
-
imageTag,
|
|
3569
|
-
repoPath: repoState.path,
|
|
3570
|
-
homeDir,
|
|
3571
|
-
hasGitconfig: dockerDeps.existsSync(path3.join(homeDir, ".gitconfig"))
|
|
3572
|
-
};
|
|
3573
|
-
log3(`Docker config ready: ${JSON.stringify(dockerConfig)}`);
|
|
3574
|
-
if (!process.env.CLAUDE_CODE_OAUTH_TOKEN) {
|
|
3575
|
-
log3("CLAUDE_CODE_OAUTH_TOKEN is not set, cannot run in Docker mode");
|
|
3576
|
-
appendLogLine(repoState.logBuffer, createLogLine("Docker mode requires CLAUDE_CODE_OAUTH_TOKEN. Run `claude setup-token` and export the token.", "stderr"));
|
|
3577
|
-
return;
|
|
3578
|
-
}
|
|
3579
|
-
}
|
|
3801
|
+
const dockerResult = await prepareDockerConfig(repoState.path, dockerDeps, onLoopEvent);
|
|
3802
|
+
if ("error" in dockerResult) {
|
|
3803
|
+
log3(`Docker error: ${dockerResult.error}`);
|
|
3804
|
+
appendLogLine(repoState.logBuffer, createLogLine(dockerResult.error, "stderr"));
|
|
3805
|
+
} else if ("config" in dockerResult) {
|
|
3806
|
+
if (!process.env.CLAUDE_CODE_OAUTH_TOKEN) {
|
|
3807
|
+
log3("CLAUDE_CODE_OAUTH_TOKEN is not set, cannot run in Docker mode");
|
|
3808
|
+
appendLogLine(repoState.logBuffer, createLogLine("Docker mode requires CLAUDE_CODE_OAUTH_TOKEN. Run `claude setup-token` and export the token.", "stderr"));
|
|
3809
|
+
return;
|
|
3580
3810
|
}
|
|
3581
|
-
|
|
3582
|
-
log3("no .dust/Dockerfile found, running without Docker");
|
|
3811
|
+
dockerConfig = dockerResult.config;
|
|
3583
3812
|
}
|
|
3584
3813
|
log3(`loop started for ${repoName} at ${repoState.path}`);
|
|
3585
3814
|
while (!repoState.stopRequested) {
|
|
3586
3815
|
loopState.agentSessionId = crypto.randomUUID();
|
|
3816
|
+
const isCodex = repoState.repository.agentProvider === "codex";
|
|
3817
|
+
const agentType = isCodex ? "codex" : "claude";
|
|
3818
|
+
log3(`${repoName}: agentProvider=${repoState.repository.agentProvider ?? "(unset)"}, using ${agentType}`);
|
|
3819
|
+
const createStdoutSink2 = createStdoutSinkFactory(loopState, repoState.logBuffer);
|
|
3820
|
+
let bufferRun;
|
|
3821
|
+
if (isCodex) {
|
|
3822
|
+
const codexBufferSinkDeps = {
|
|
3823
|
+
...defaultRunnerDependencies2,
|
|
3824
|
+
spawnCodex: (prompt, options = {}) => {
|
|
3825
|
+
const spawnDeps = {
|
|
3826
|
+
...defaultDependencies2,
|
|
3827
|
+
spawn
|
|
3828
|
+
};
|
|
3829
|
+
return defaultRunnerDependencies2.spawnCodex(prompt, options, spawnDeps);
|
|
3830
|
+
},
|
|
3831
|
+
createStdoutSink: createStdoutSink2
|
|
3832
|
+
};
|
|
3833
|
+
bufferRun = createCodexBufferRun(run2, codexBufferSinkDeps);
|
|
3834
|
+
} else {
|
|
3835
|
+
const bufferSinkDeps = {
|
|
3836
|
+
...defaultRunnerDependencies,
|
|
3837
|
+
createStdoutSink: createStdoutSink2
|
|
3838
|
+
};
|
|
3839
|
+
bufferRun = createBufferRun(run3, bufferSinkDeps);
|
|
3840
|
+
}
|
|
3841
|
+
const loopDeps = {
|
|
3842
|
+
spawn,
|
|
3843
|
+
run: bufferRun,
|
|
3844
|
+
sleep,
|
|
3845
|
+
postEvent: noOpPostEvent,
|
|
3846
|
+
agentType
|
|
3847
|
+
};
|
|
3587
3848
|
const abortController = new AbortController;
|
|
3588
3849
|
const cancelCurrentIteration = createCancelHandler(abortController);
|
|
3589
3850
|
repoState.cancelCurrentIteration = cancelCurrentIteration;
|
|
@@ -3593,7 +3854,7 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
|
|
|
3593
3854
|
hooksInstalled,
|
|
3594
3855
|
signal: abortController.signal,
|
|
3595
3856
|
repositoryId: repoState.repository.id.toString(),
|
|
3596
|
-
onRawEvent: createHeartbeatThrottler(onAgentEvent),
|
|
3857
|
+
onRawEvent: createHeartbeatThrottler(onAgentEvent, agentType),
|
|
3597
3858
|
docker: dockerConfig
|
|
3598
3859
|
});
|
|
3599
3860
|
} catch (error) {
|
|
@@ -3653,7 +3914,8 @@ function parseRepository(data) {
|
|
|
3653
3914
|
gitUrl: repositoryData.gitUrl,
|
|
3654
3915
|
gitSshUrl: typeof repositoryData.gitSshUrl === "string" ? repositoryData.gitSshUrl : undefined,
|
|
3655
3916
|
url: repositoryData.url,
|
|
3656
|
-
id: repositoryData.id
|
|
3917
|
+
id: repositoryData.id,
|
|
3918
|
+
agentProvider: typeof repositoryData.agentProvider === "string" ? repositoryData.agentProvider : undefined
|
|
3657
3919
|
};
|
|
3658
3920
|
}
|
|
3659
3921
|
}
|
|
@@ -3729,8 +3991,14 @@ async function handleRepositoryList(repositories, manager, repoDeps, context) {
|
|
|
3729
3991
|
}
|
|
3730
3992
|
}
|
|
3731
3993
|
for (const [name, repo] of incomingRepos) {
|
|
3732
|
-
|
|
3994
|
+
const existing = manager.repositories.get(name);
|
|
3995
|
+
if (!existing) {
|
|
3733
3996
|
await addRepository(repo, manager, repoDeps, context);
|
|
3997
|
+
} else if (existing.repository.agentProvider !== repo.agentProvider) {
|
|
3998
|
+
const from = existing.repository.agentProvider ?? "(unset)";
|
|
3999
|
+
const to = repo.agentProvider ?? "(unset)";
|
|
4000
|
+
log4(`${name}: agentProvider changed from ${from} to ${to}`);
|
|
4001
|
+
existing.repository.agentProvider = repo.agentProvider;
|
|
3734
4002
|
}
|
|
3735
4003
|
}
|
|
3736
4004
|
for (const name of manager.repositories.keys()) {
|
|
@@ -3843,18 +4111,19 @@ function visibleLength(text) {
|
|
|
3843
4111
|
function truncateLine(text, maxWidth) {
|
|
3844
4112
|
if (maxWidth <= 0)
|
|
3845
4113
|
return "";
|
|
3846
|
-
|
|
4114
|
+
const textLength = visibleLength(text);
|
|
4115
|
+
if (textLength <= maxWidth)
|
|
3847
4116
|
return text;
|
|
3848
4117
|
const ansiRegex = /\x1b\[[0-9;]*m/g;
|
|
4118
|
+
const truncateAt = maxWidth - CHARS.ellipsis.length;
|
|
3849
4119
|
let visibleCount = 0;
|
|
3850
4120
|
let result = "";
|
|
3851
4121
|
let lastIndex = 0;
|
|
3852
4122
|
for (let match = ansiRegex.exec(text);match !== null; match = ansiRegex.exec(text)) {
|
|
3853
4123
|
const textBefore = text.slice(lastIndex, match.index);
|
|
3854
4124
|
for (const char of textBefore) {
|
|
3855
|
-
if (visibleCount >=
|
|
3856
|
-
result
|
|
3857
|
-
return result + ANSI.RESET;
|
|
4125
|
+
if (visibleCount >= truncateAt) {
|
|
4126
|
+
return result + CHARS.ellipsis + ANSI.RESET;
|
|
3858
4127
|
}
|
|
3859
4128
|
result += char;
|
|
3860
4129
|
visibleCount++;
|
|
@@ -3864,14 +4133,13 @@ function truncateLine(text, maxWidth) {
|
|
|
3864
4133
|
}
|
|
3865
4134
|
const remaining = text.slice(lastIndex);
|
|
3866
4135
|
for (const char of remaining) {
|
|
3867
|
-
if (visibleCount >=
|
|
3868
|
-
result
|
|
3869
|
-
return result + ANSI.RESET;
|
|
4136
|
+
if (visibleCount >= truncateAt) {
|
|
4137
|
+
return result + CHARS.ellipsis + ANSI.RESET;
|
|
3870
4138
|
}
|
|
3871
4139
|
result += char;
|
|
3872
4140
|
visibleCount++;
|
|
3873
4141
|
}
|
|
3874
|
-
return result;
|
|
4142
|
+
return result + ANSI.RESET;
|
|
3875
4143
|
}
|
|
3876
4144
|
function createTerminalUIState() {
|
|
3877
4145
|
return {
|
|
@@ -4001,7 +4269,7 @@ function getVisibleLogs(state) {
|
|
|
4001
4269
|
const buffer2 = state.logBuffers.get(repoName2);
|
|
4002
4270
|
if (!buffer2)
|
|
4003
4271
|
continue;
|
|
4004
|
-
const color2 = repoColors.get(repoName2)
|
|
4272
|
+
const color2 = repoColors.get(repoName2);
|
|
4005
4273
|
const lines = getLogLines(buffer2);
|
|
4006
4274
|
for (const line of lines) {
|
|
4007
4275
|
allLogs.push({ ...line, repository: repoName2, color: color2 });
|
|
@@ -4136,90 +4404,37 @@ function enterAlternateScreen() {
|
|
|
4136
4404
|
function exitAlternateScreen() {
|
|
4137
4405
|
return `\x1B[?1006l\x1B[?1000l${ANSI.EXIT_ALT_SCREEN}${ANSI.SHOW_CURSOR}`;
|
|
4138
4406
|
}
|
|
4139
|
-
var
|
|
4140
|
-
UP: "\x1B[A",
|
|
4141
|
-
DOWN: "\x1B[B",
|
|
4142
|
-
RIGHT: "\x1B[C",
|
|
4143
|
-
LEFT: "\x1B[D",
|
|
4144
|
-
PAGE_UP: "\x1B[5~",
|
|
4145
|
-
PAGE_DOWN: "\x1B[6~",
|
|
4146
|
-
HOME: "\x1B[H",
|
|
4147
|
-
END: "\x1B[F",
|
|
4148
|
-
CTRL_C: "\x03"
|
|
4149
|
-
};
|
|
4150
|
-
var SGR_MOUSE_RE = new RegExp(String.raw`^\x1b\[<(\d+);\d+;\d+[Mm]$`);
|
|
4151
|
-
function parseSGRMouse(key) {
|
|
4152
|
-
const match = key.match(SGR_MOUSE_RE);
|
|
4153
|
-
if (!match)
|
|
4154
|
-
return null;
|
|
4155
|
-
return Number.parseInt(match[1], 10);
|
|
4156
|
-
}
|
|
4157
|
-
function handleKeyInput(state, key, options) {
|
|
4158
|
-
const mouseButton = parseSGRMouse(key);
|
|
4159
|
-
if (mouseButton !== null) {
|
|
4160
|
-
if (mouseButton === 64) {
|
|
4161
|
-
scrollUp(state, 3);
|
|
4162
|
-
} else if (mouseButton === 65) {
|
|
4163
|
-
scrollDown(state, 3);
|
|
4164
|
-
}
|
|
4165
|
-
return false;
|
|
4166
|
-
}
|
|
4167
|
-
switch (key) {
|
|
4168
|
-
case "q":
|
|
4169
|
-
case KEYS.CTRL_C:
|
|
4170
|
-
return true;
|
|
4171
|
-
case KEYS.LEFT:
|
|
4172
|
-
selectPrevious(state);
|
|
4173
|
-
state.scrollOffset = 0;
|
|
4174
|
-
state.autoScroll = true;
|
|
4175
|
-
break;
|
|
4176
|
-
case KEYS.RIGHT:
|
|
4177
|
-
selectNext(state);
|
|
4178
|
-
state.scrollOffset = 0;
|
|
4179
|
-
state.autoScroll = true;
|
|
4180
|
-
break;
|
|
4181
|
-
case KEYS.UP:
|
|
4182
|
-
scrollUp(state, 1);
|
|
4183
|
-
break;
|
|
4184
|
-
case KEYS.DOWN:
|
|
4185
|
-
scrollDown(state, 1);
|
|
4186
|
-
break;
|
|
4187
|
-
case KEYS.PAGE_UP:
|
|
4188
|
-
scrollUp(state, getLogAreaHeight(state));
|
|
4189
|
-
break;
|
|
4190
|
-
case KEYS.PAGE_DOWN:
|
|
4191
|
-
scrollDown(state, getLogAreaHeight(state));
|
|
4192
|
-
break;
|
|
4193
|
-
case "g":
|
|
4194
|
-
case KEYS.HOME:
|
|
4195
|
-
scrollToTop(state);
|
|
4196
|
-
break;
|
|
4197
|
-
case "G":
|
|
4198
|
-
case KEYS.END:
|
|
4199
|
-
scrollToBottom(state);
|
|
4200
|
-
break;
|
|
4201
|
-
case "o": {
|
|
4202
|
-
if (state.selectedIndex === -1) {
|
|
4203
|
-
break;
|
|
4204
|
-
}
|
|
4205
|
-
const repoName = state.repositories[state.selectedIndex];
|
|
4206
|
-
if (!repoName)
|
|
4207
|
-
break;
|
|
4208
|
-
const url = state.repositoryUrls.get(repoName);
|
|
4209
|
-
if (url && options?.openBrowser) {
|
|
4210
|
-
options.openBrowser(url);
|
|
4211
|
-
}
|
|
4212
|
-
break;
|
|
4213
|
-
}
|
|
4214
|
-
}
|
|
4215
|
-
return false;
|
|
4216
|
-
}
|
|
4407
|
+
var SGR_MOUSE_RE2 = new RegExp(String.raw`^\x1b\[<(\d+);\d+;\d+[Mm]$`);
|
|
4217
4408
|
|
|
4218
4409
|
// lib/cli/commands/bucket.ts
|
|
4219
4410
|
var log5 = createLogger("dust:cli:commands:bucket");
|
|
4220
4411
|
var DEFAULT_DUSTBUCKET_WS_URL = "wss://dustbucket.com/agent/connect";
|
|
4221
|
-
|
|
4222
|
-
|
|
4412
|
+
function createAuthFileSystem(dependencies) {
|
|
4413
|
+
return {
|
|
4414
|
+
exists: (path2) => {
|
|
4415
|
+
try {
|
|
4416
|
+
dependencies.accessSync(path2);
|
|
4417
|
+
return true;
|
|
4418
|
+
} catch {
|
|
4419
|
+
return false;
|
|
4420
|
+
}
|
|
4421
|
+
},
|
|
4422
|
+
isDirectory: (path2) => {
|
|
4423
|
+
try {
|
|
4424
|
+
return dependencies.statSync(path2).isDirectory();
|
|
4425
|
+
} catch {
|
|
4426
|
+
return false;
|
|
4427
|
+
}
|
|
4428
|
+
},
|
|
4429
|
+
getFileCreationTime: (path2) => dependencies.statSync(path2).birthtimeMs,
|
|
4430
|
+
readFile: (path2) => dependencies.readFile(path2, "utf8"),
|
|
4431
|
+
writeFile: (path2, content) => dependencies.writeFile(path2, content, "utf8"),
|
|
4432
|
+
mkdir: (path2, options) => dependencies.mkdir(path2, options).then(() => {}),
|
|
4433
|
+
readdir: (path2) => dependencies.readdir(path2),
|
|
4434
|
+
chmod: (path2, mode) => dependencies.chmod(path2, mode),
|
|
4435
|
+
rename: (oldPath, newPath) => dependencies.rename(oldPath, newPath)
|
|
4436
|
+
};
|
|
4437
|
+
}
|
|
4223
4438
|
function defaultCreateWebSocket(url, token) {
|
|
4224
4439
|
const ws = new WebSocket(url, {
|
|
4225
4440
|
headers: {
|
|
@@ -4274,32 +4489,6 @@ function defaultGetTerminalSize() {
|
|
|
4274
4489
|
function defaultWriteStdout(data) {
|
|
4275
4490
|
process.stdout.write(data);
|
|
4276
4491
|
}
|
|
4277
|
-
function createAuthFileSystem(dependencies) {
|
|
4278
|
-
return {
|
|
4279
|
-
exists: (path4) => {
|
|
4280
|
-
try {
|
|
4281
|
-
dependencies.accessSync(path4);
|
|
4282
|
-
return true;
|
|
4283
|
-
} catch {
|
|
4284
|
-
return false;
|
|
4285
|
-
}
|
|
4286
|
-
},
|
|
4287
|
-
isDirectory: (path4) => {
|
|
4288
|
-
try {
|
|
4289
|
-
return dependencies.statSync(path4).isDirectory();
|
|
4290
|
-
} catch {
|
|
4291
|
-
return false;
|
|
4292
|
-
}
|
|
4293
|
-
},
|
|
4294
|
-
getFileCreationTime: (path4) => dependencies.statSync(path4).birthtimeMs,
|
|
4295
|
-
readFile: (path4) => dependencies.readFile(path4, "utf8"),
|
|
4296
|
-
writeFile: (path4, content) => dependencies.writeFile(path4, content, "utf8"),
|
|
4297
|
-
mkdir: (path4, options) => dependencies.mkdir(path4, options).then(() => {}),
|
|
4298
|
-
readdir: (path4) => dependencies.readdir(path4),
|
|
4299
|
-
chmod: (path4, mode) => dependencies.chmod(path4, mode),
|
|
4300
|
-
rename: (oldPath, newPath) => dependencies.rename(oldPath, newPath)
|
|
4301
|
-
};
|
|
4302
|
-
}
|
|
4303
4492
|
function createDefaultBucketDependencies() {
|
|
4304
4493
|
const authFileSystem = createAuthFileSystem({
|
|
4305
4494
|
accessSync,
|
|
@@ -4423,6 +4612,20 @@ function syncTUI(state) {
|
|
|
4423
4612
|
}
|
|
4424
4613
|
}
|
|
4425
4614
|
}
|
|
4615
|
+
function handleRepositoryListSuccess(state, repos, repoDeps, context, useTUI) {
|
|
4616
|
+
syncTUI(state);
|
|
4617
|
+
for (const repoData of repos) {
|
|
4618
|
+
if (repoData.hasTask) {
|
|
4619
|
+
const repoState = state.repositories.get(repoData.name);
|
|
4620
|
+
if (repoState) {
|
|
4621
|
+
signalTaskAvailable(repoState, state, repoDeps, context, useTUI);
|
|
4622
|
+
}
|
|
4623
|
+
}
|
|
4624
|
+
}
|
|
4625
|
+
}
|
|
4626
|
+
function handleRepositoryListError(state, context, useTUI, error) {
|
|
4627
|
+
logMessage(state, context, useTUI, `Failed to handle repository list: ${error.message}`, "stderr");
|
|
4628
|
+
}
|
|
4426
4629
|
function logMessage(state, context, useTUI, message, stream = "stdout") {
|
|
4427
4630
|
if (useTUI) {
|
|
4428
4631
|
const systemBuffer = state.logBuffers.get("system");
|
|
@@ -4470,8 +4673,13 @@ function connectWebSocket(token, state, bucketDependencies, context, fileSystem,
|
|
|
4470
4673
|
state.ws = ws;
|
|
4471
4674
|
ws.onopen = () => {
|
|
4472
4675
|
state.emit({ type: "bucket.connected" });
|
|
4473
|
-
|
|
4474
|
-
|
|
4676
|
+
const lifecycleState = {
|
|
4677
|
+
reconnectDelay: state.reconnectDelay,
|
|
4678
|
+
shuttingDown: state.shuttingDown
|
|
4679
|
+
};
|
|
4680
|
+
const result = handleOpen(lifecycleState);
|
|
4681
|
+
state.reconnectDelay = result.state.reconnectDelay;
|
|
4682
|
+
executeLifecycleEffects(result.effects, { state, context, useTUI, bucketDependencies, fileSystem }, token);
|
|
4475
4683
|
};
|
|
4476
4684
|
}
|
|
4477
4685
|
ws.onclose = (event) => {
|
|
@@ -4481,80 +4689,110 @@ function connectWebSocket(token, state, bucketDependencies, context, fileSystem,
|
|
|
4481
4689
|
reason: event.reason || "none"
|
|
4482
4690
|
};
|
|
4483
4691
|
state.emit(disconnectEvent);
|
|
4484
|
-
logMessage(state, context, useTUI, formatBucketEvent(disconnectEvent));
|
|
4485
4692
|
state.ws = null;
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
}
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
connectWebSocket(token, state, bucketDependencies, context, fileSystem, useTUI);
|
|
4494
|
-
}, state.reconnectDelay);
|
|
4495
|
-
state.reconnectDelay = Math.min(state.reconnectDelay * 2, MAX_RECONNECT_DELAY_MS);
|
|
4496
|
-
}
|
|
4693
|
+
const lifecycleState = {
|
|
4694
|
+
reconnectDelay: state.reconnectDelay,
|
|
4695
|
+
shuttingDown: state.shuttingDown
|
|
4696
|
+
};
|
|
4697
|
+
const result = handleClose(lifecycleState, event.code, event.reason || "");
|
|
4698
|
+
state.reconnectDelay = result.state.reconnectDelay;
|
|
4699
|
+
executeLifecycleEffects(result.effects, { state, context, useTUI, bucketDependencies, fileSystem }, token);
|
|
4497
4700
|
};
|
|
4498
4701
|
ws.onerror = (error) => {
|
|
4499
|
-
|
|
4702
|
+
const lifecycleState = {
|
|
4703
|
+
reconnectDelay: state.reconnectDelay,
|
|
4704
|
+
shuttingDown: state.shuttingDown
|
|
4705
|
+
};
|
|
4706
|
+
const result = handleError(lifecycleState, error.message);
|
|
4707
|
+
executeLifecycleEffects(result.effects, { state, context, useTUI, bucketDependencies, fileSystem }, token);
|
|
4500
4708
|
};
|
|
4501
4709
|
ws.onmessage = (event) => {
|
|
4502
4710
|
let rawData;
|
|
4503
4711
|
try {
|
|
4504
4712
|
rawData = JSON.parse(event.data);
|
|
4505
4713
|
} catch {
|
|
4506
|
-
|
|
4714
|
+
const result2 = handleMessageParseError(event.data);
|
|
4715
|
+
executeEffects(result2.effects, {
|
|
4716
|
+
state,
|
|
4717
|
+
context,
|
|
4718
|
+
useTUI,
|
|
4719
|
+
bucketDependencies,
|
|
4720
|
+
fileSystem
|
|
4721
|
+
});
|
|
4507
4722
|
return;
|
|
4508
4723
|
}
|
|
4509
4724
|
const message = parseServerMessage(rawData);
|
|
4510
4725
|
if (!message) {
|
|
4511
|
-
|
|
4726
|
+
const result2 = handleInvalidMessageFormat(event.data);
|
|
4727
|
+
executeEffects(result2.effects, {
|
|
4728
|
+
state,
|
|
4729
|
+
context,
|
|
4730
|
+
useTUI,
|
|
4731
|
+
bucketDependencies,
|
|
4732
|
+
fileSystem
|
|
4733
|
+
});
|
|
4512
4734
|
return;
|
|
4513
4735
|
}
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4736
|
+
const handlerState = {
|
|
4737
|
+
repositoryNames: Array.from(state.repositories.keys())
|
|
4738
|
+
};
|
|
4739
|
+
const result = handleServerMessage(handlerState, message);
|
|
4740
|
+
executeEffects(result.effects, {
|
|
4741
|
+
state,
|
|
4742
|
+
context,
|
|
4743
|
+
useTUI,
|
|
4744
|
+
bucketDependencies,
|
|
4745
|
+
fileSystem
|
|
4746
|
+
});
|
|
4747
|
+
};
|
|
4748
|
+
}
|
|
4749
|
+
function executeEffects(effects, dependencies) {
|
|
4750
|
+
const { state, context, useTUI, bucketDependencies, fileSystem } = dependencies;
|
|
4751
|
+
for (const effect of effects) {
|
|
4752
|
+
switch (effect.type) {
|
|
4753
|
+
case "log":
|
|
4754
|
+
logMessage(state, context, useTUI, effect.message, effect.stream);
|
|
4755
|
+
break;
|
|
4756
|
+
case "debugLog":
|
|
4757
|
+
log5(effect.message);
|
|
4758
|
+
break;
|
|
4759
|
+
case "syncUI":
|
|
4760
|
+
syncUIWithRepoList(state, effect.repositories);
|
|
4761
|
+
break;
|
|
4762
|
+
case "handleRepositoryList": {
|
|
4763
|
+
const repoDeps = toRepositoryDependencies(bucketDependencies, fileSystem);
|
|
4764
|
+
const repoContext = createTUIContext(state, context, useTUI);
|
|
4765
|
+
const repos = effect.repositories;
|
|
4766
|
+
handleRepositoryList(repos, state, repoDeps, repoContext).then(() => handleRepositoryListSuccess(state, repos, repoDeps, context, useTUI)).catch((error) => handleRepositoryListError(state, context, useTUI, error));
|
|
4767
|
+
break;
|
|
4529
4768
|
}
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
for (const repoData of repos) {
|
|
4536
|
-
if (repoData.hasTask) {
|
|
4537
|
-
const repoState = state.repositories.get(repoData.name);
|
|
4538
|
-
if (repoState) {
|
|
4539
|
-
signalTaskAvailable(repoState, state, repoDeps, context, useTUI);
|
|
4540
|
-
}
|
|
4541
|
-
}
|
|
4769
|
+
case "signalTaskAvailable": {
|
|
4770
|
+
const repoDeps = toRepositoryDependencies(bucketDependencies, fileSystem);
|
|
4771
|
+
const repoState = state.repositories.get(effect.repositoryName);
|
|
4772
|
+
if (repoState) {
|
|
4773
|
+
signalTaskAvailable(repoState, state, repoDeps, context, useTUI);
|
|
4542
4774
|
}
|
|
4543
|
-
|
|
4544
|
-
logMessage(state, context, useTUI, `Failed to handle repository list: ${error.message}`, "stderr");
|
|
4545
|
-
});
|
|
4546
|
-
} else if (message.type === "task-available") {
|
|
4547
|
-
const repoName = message.repository;
|
|
4548
|
-
const repoDeps = toRepositoryDependencies(bucketDependencies, fileSystem);
|
|
4549
|
-
logMessage(state, context, useTUI, `Received task-available for ${repoName}`);
|
|
4550
|
-
const repoState = state.repositories.get(repoName);
|
|
4551
|
-
if (repoState) {
|
|
4552
|
-
signalTaskAvailable(repoState, state, repoDeps, context, useTUI);
|
|
4553
|
-
} else {
|
|
4554
|
-
logMessage(state, context, useTUI, `No repository state found for ${repoName}`, "stderr");
|
|
4775
|
+
break;
|
|
4555
4776
|
}
|
|
4777
|
+
case "scheduleReconnect":
|
|
4778
|
+
break;
|
|
4556
4779
|
}
|
|
4557
|
-
}
|
|
4780
|
+
}
|
|
4781
|
+
}
|
|
4782
|
+
function executeLifecycleEffects(effects, dependencies, token) {
|
|
4783
|
+
const { state, context, useTUI, bucketDependencies, fileSystem } = dependencies;
|
|
4784
|
+
for (const effect of effects) {
|
|
4785
|
+
switch (effect.type) {
|
|
4786
|
+
case "log":
|
|
4787
|
+
logMessage(state, context, useTUI, effect.message, effect.stream);
|
|
4788
|
+
break;
|
|
4789
|
+
case "scheduleReconnect":
|
|
4790
|
+
state.reconnectTimer = setTimeout(() => {
|
|
4791
|
+
connectWebSocket(token, state, bucketDependencies, context, fileSystem, useTUI);
|
|
4792
|
+
}, effect.delayMs);
|
|
4793
|
+
break;
|
|
4794
|
+
}
|
|
4795
|
+
}
|
|
4558
4796
|
}
|
|
4559
4797
|
async function shutdown(state, bucketDeps, context) {
|
|
4560
4798
|
if (state.shuttingDown)
|
|
@@ -4608,17 +4846,80 @@ function setupTUI(state, bucketDeps) {
|
|
|
4608
4846
|
}
|
|
4609
4847
|
};
|
|
4610
4848
|
}
|
|
4849
|
+
function createKeypressHandlerState(ui) {
|
|
4850
|
+
const repositoryUrls = {};
|
|
4851
|
+
for (const [name, url] of ui.repositoryUrls) {
|
|
4852
|
+
repositoryUrls[name] = url;
|
|
4853
|
+
}
|
|
4854
|
+
return {
|
|
4855
|
+
selectedIndex: ui.selectedIndex,
|
|
4856
|
+
repositories: ui.repositories,
|
|
4857
|
+
repositoryUrls
|
|
4858
|
+
};
|
|
4859
|
+
}
|
|
4860
|
+
function executeKeypressEffects(ui, effects, onQuit, options) {
|
|
4861
|
+
for (const effect of effects) {
|
|
4862
|
+
switch (effect.type) {
|
|
4863
|
+
case "quit":
|
|
4864
|
+
onQuit();
|
|
4865
|
+
break;
|
|
4866
|
+
case "openBrowser":
|
|
4867
|
+
if (options?.openBrowser) {
|
|
4868
|
+
options.openBrowser(effect.url);
|
|
4869
|
+
}
|
|
4870
|
+
break;
|
|
4871
|
+
case "selectNext":
|
|
4872
|
+
selectNext(ui);
|
|
4873
|
+
ui.scrollOffset = 0;
|
|
4874
|
+
ui.autoScroll = true;
|
|
4875
|
+
break;
|
|
4876
|
+
case "selectPrevious":
|
|
4877
|
+
selectPrevious(ui);
|
|
4878
|
+
ui.scrollOffset = 0;
|
|
4879
|
+
ui.autoScroll = true;
|
|
4880
|
+
break;
|
|
4881
|
+
case "scroll":
|
|
4882
|
+
switch (effect.direction) {
|
|
4883
|
+
case "up":
|
|
4884
|
+
scrollUp(ui, 1);
|
|
4885
|
+
break;
|
|
4886
|
+
case "down":
|
|
4887
|
+
scrollDown(ui, 1);
|
|
4888
|
+
break;
|
|
4889
|
+
case "pageUp":
|
|
4890
|
+
scrollUp(ui, getLogAreaHeight(ui));
|
|
4891
|
+
break;
|
|
4892
|
+
case "pageDown":
|
|
4893
|
+
scrollDown(ui, getLogAreaHeight(ui));
|
|
4894
|
+
break;
|
|
4895
|
+
case "top":
|
|
4896
|
+
scrollToTop(ui);
|
|
4897
|
+
break;
|
|
4898
|
+
case "bottom":
|
|
4899
|
+
scrollToBottom(ui);
|
|
4900
|
+
break;
|
|
4901
|
+
}
|
|
4902
|
+
break;
|
|
4903
|
+
}
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4611
4906
|
function createKeypressHandler(useTUI, state, onQuit, options) {
|
|
4612
4907
|
if (useTUI) {
|
|
4613
4908
|
return (key) => {
|
|
4614
|
-
const
|
|
4615
|
-
|
|
4616
|
-
|
|
4909
|
+
const keypressState = createKeypressHandlerState(state.ui);
|
|
4910
|
+
const { effects } = handleKeypress(keypressState, key);
|
|
4911
|
+
executeKeypressEffects(state.ui, effects, onQuit, options);
|
|
4617
4912
|
};
|
|
4618
4913
|
}
|
|
4619
4914
|
return (key) => {
|
|
4620
|
-
|
|
4621
|
-
|
|
4915
|
+
const keypressState = createKeypressHandlerState(state.ui);
|
|
4916
|
+
const { effects } = handleKeypress(keypressState, key);
|
|
4917
|
+
for (const effect of effects) {
|
|
4918
|
+
if (effect.type === "quit") {
|
|
4919
|
+
onQuit();
|
|
4920
|
+
break;
|
|
4921
|
+
}
|
|
4922
|
+
}
|
|
4622
4923
|
};
|
|
4623
4924
|
}
|
|
4624
4925
|
async function resolveToken(authDeps, context) {
|
|
@@ -4763,16 +5064,16 @@ function createDefaultUploadDependencies() {
|
|
|
4763
5064
|
getHomeDir: () => homedir2(),
|
|
4764
5065
|
fileSystem: authFileSystem
|
|
4765
5066
|
},
|
|
4766
|
-
readFileBytes: async (
|
|
4767
|
-
const buffer = await Bun.file(
|
|
5067
|
+
readFileBytes: async (path2) => {
|
|
5068
|
+
const buffer = await Bun.file(path2).arrayBuffer();
|
|
4768
5069
|
return new Uint8Array(buffer);
|
|
4769
5070
|
},
|
|
4770
|
-
getFileSize: async (
|
|
4771
|
-
const file = Bun.file(
|
|
5071
|
+
getFileSize: async (path2) => {
|
|
5072
|
+
const file = Bun.file(path2);
|
|
4772
5073
|
return file.size;
|
|
4773
5074
|
},
|
|
4774
|
-
fileExists: async (
|
|
4775
|
-
const file = Bun.file(
|
|
5075
|
+
fileExists: async (path2) => {
|
|
5076
|
+
const file = Bun.file(path2);
|
|
4776
5077
|
return file.exists();
|
|
4777
5078
|
},
|
|
4778
5079
|
uploadFile: async (url, token, fileBytes, contentType, fileName) => {
|
|
@@ -4868,7 +5169,8 @@ async function bucketAssetUpload(dependencies, uploadDeps = createDefaultUploadD
|
|
|
4868
5169
|
}
|
|
4869
5170
|
const fileBytes = await uploadDeps.readFileBytes(filePath);
|
|
4870
5171
|
const contentType = getContentType(filePath);
|
|
4871
|
-
const
|
|
5172
|
+
const parts = filePath.split("/");
|
|
5173
|
+
const fileName = parts[parts.length - 1];
|
|
4872
5174
|
const uploadUrl = `${getDustbucketHost()}/api/assets?repositoryId=${encodeURIComponent(repositoryId)}`;
|
|
4873
5175
|
try {
|
|
4874
5176
|
const result = await uploadDeps.uploadFile(uploadUrl, token, fileBytes, contentType, fileName);
|
|
@@ -5566,12 +5868,12 @@ function validateNoCycles(allPrincipleRelationships) {
|
|
|
5566
5868
|
}
|
|
5567
5869
|
for (const rel of allPrincipleRelationships) {
|
|
5568
5870
|
const visited = new Set;
|
|
5569
|
-
const
|
|
5871
|
+
const path2 = [];
|
|
5570
5872
|
let current = rel.filePath;
|
|
5571
5873
|
while (current) {
|
|
5572
5874
|
if (visited.has(current)) {
|
|
5573
|
-
const cycleStart =
|
|
5574
|
-
const cyclePath =
|
|
5875
|
+
const cycleStart = path2.indexOf(current);
|
|
5876
|
+
const cyclePath = path2.slice(cycleStart).concat(current);
|
|
5575
5877
|
violations.push({
|
|
5576
5878
|
file: rel.filePath,
|
|
5577
5879
|
message: `Cycle detected in principle hierarchy: ${cyclePath.join(" -> ")}`
|
|
@@ -5579,7 +5881,7 @@ function validateNoCycles(allPrincipleRelationships) {
|
|
|
5579
5881
|
break;
|
|
5580
5882
|
}
|
|
5581
5883
|
visited.add(current);
|
|
5582
|
-
|
|
5884
|
+
path2.push(current);
|
|
5583
5885
|
const currentRel = relationshipMap.get(current);
|
|
5584
5886
|
if (currentRel && currentRel.parentPrinciples.length > 0) {
|
|
5585
5887
|
current = currentRel.parentPrinciples[0];
|
|
@@ -5801,13 +6103,13 @@ function truncateOutput(output) {
|
|
|
5801
6103
|
].join(`
|
|
5802
6104
|
`);
|
|
5803
6105
|
}
|
|
5804
|
-
async function runSingleCheck(check, cwd, runner, emitEvent) {
|
|
6106
|
+
async function runSingleCheck(check, cwd, runner, emitEvent, clock = Date.now) {
|
|
5805
6107
|
const timeoutMs = check.timeoutMilliseconds ?? DEFAULT_CHECK_TIMEOUT_MS;
|
|
5806
6108
|
log6(`running check ${check.name}: ${check.command}`);
|
|
5807
6109
|
emitEvent?.({ type: "check-started", name: check.name });
|
|
5808
|
-
const startTime =
|
|
6110
|
+
const startTime = clock();
|
|
5809
6111
|
const result = await runner.run(check.command, cwd, timeoutMs);
|
|
5810
|
-
const durationMs =
|
|
6112
|
+
const durationMs = clock() - startTime;
|
|
5811
6113
|
const status = result.timedOut ? "timed out" : result.exitCode === 0 ? "passed" : "failed";
|
|
5812
6114
|
log6(`check ${check.name} ${status} (${durationMs}ms)`);
|
|
5813
6115
|
if (result.exitCode === 0) {
|
|
@@ -5833,18 +6135,18 @@ async function runSingleCheck(check, cwd, runner, emitEvent) {
|
|
|
5833
6135
|
timeoutSeconds: timeoutMs / 1000
|
|
5834
6136
|
};
|
|
5835
6137
|
}
|
|
5836
|
-
async function runConfiguredChecks(checks, cwd, runner, emitEvent) {
|
|
5837
|
-
const promises = checks.map((check) => runSingleCheck(check, cwd, runner, emitEvent));
|
|
6138
|
+
async function runConfiguredChecks(checks, cwd, runner, emitEvent, clock = Date.now) {
|
|
6139
|
+
const promises = checks.map((check) => runSingleCheck(check, cwd, runner, emitEvent, clock));
|
|
5838
6140
|
return Promise.all(promises);
|
|
5839
6141
|
}
|
|
5840
|
-
async function runConfiguredChecksSerially(checks, cwd, runner, emitEvent) {
|
|
6142
|
+
async function runConfiguredChecksSerially(checks, cwd, runner, emitEvent, clock = Date.now) {
|
|
5841
6143
|
const results = [];
|
|
5842
6144
|
for (const check of checks) {
|
|
5843
|
-
results.push(await runSingleCheck(check, cwd, runner, emitEvent));
|
|
6145
|
+
results.push(await runSingleCheck(check, cwd, runner, emitEvent, clock));
|
|
5844
6146
|
}
|
|
5845
6147
|
return results;
|
|
5846
6148
|
}
|
|
5847
|
-
async function runValidationCheck(dependencies, emitEvent) {
|
|
6149
|
+
async function runValidationCheck(dependencies, emitEvent, clock = Date.now) {
|
|
5848
6150
|
const outputLines = [];
|
|
5849
6151
|
const bufferedContext = {
|
|
5850
6152
|
cwd: dependencies.context.cwd,
|
|
@@ -5853,13 +6155,13 @@ async function runValidationCheck(dependencies, emitEvent) {
|
|
|
5853
6155
|
};
|
|
5854
6156
|
log6("running built-in check: dust lint");
|
|
5855
6157
|
emitEvent?.({ type: "check-started", name: "lint" });
|
|
5856
|
-
const startTime =
|
|
6158
|
+
const startTime = clock();
|
|
5857
6159
|
const result = await lintMarkdown({
|
|
5858
6160
|
...dependencies,
|
|
5859
6161
|
context: bufferedContext,
|
|
5860
6162
|
arguments: []
|
|
5861
6163
|
});
|
|
5862
|
-
const durationMs =
|
|
6164
|
+
const durationMs = clock() - startTime;
|
|
5863
6165
|
const lintStatus = result.exitCode === 0 ? "passed" : "failed";
|
|
5864
6166
|
log6(`built-in check dust lint ${lintStatus} (${durationMs}ms)`);
|
|
5865
6167
|
const output = outputLines.join(`
|
|
@@ -5867,14 +6169,12 @@ async function runValidationCheck(dependencies, emitEvent) {
|
|
|
5867
6169
|
if (result.exitCode === 0) {
|
|
5868
6170
|
emitEvent?.({ type: "check-passed", name: "lint", durationMs });
|
|
5869
6171
|
} else {
|
|
5870
|
-
|
|
6172
|
+
emitEvent?.({
|
|
5871
6173
|
type: "check-failed",
|
|
5872
6174
|
name: "lint",
|
|
5873
|
-
durationMs
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
failedEvent.output = output;
|
|
5877
|
-
emitEvent?.(failedEvent);
|
|
6175
|
+
durationMs,
|
|
6176
|
+
output
|
|
6177
|
+
});
|
|
5878
6178
|
}
|
|
5879
6179
|
return {
|
|
5880
6180
|
name: "lint",
|
|
@@ -5923,7 +6223,7 @@ function displayResults(results, context) {
|
|
|
5923
6223
|
context.stdout(`${indicator} ${passed.length}/${results.length} checks passed`);
|
|
5924
6224
|
return failed.length > 0 ? 1 : 0;
|
|
5925
6225
|
}
|
|
5926
|
-
async function check(dependencies, shellRunner = defaultShellRunner) {
|
|
6226
|
+
async function check(dependencies, shellRunner = defaultShellRunner, clock = Date.now) {
|
|
5927
6227
|
const {
|
|
5928
6228
|
arguments: commandArguments,
|
|
5929
6229
|
context,
|
|
@@ -5949,18 +6249,18 @@ async function check(dependencies, shellRunner = defaultShellRunner) {
|
|
|
5949
6249
|
if (serial) {
|
|
5950
6250
|
const results2 = [];
|
|
5951
6251
|
if (hasDustDir) {
|
|
5952
|
-
results2.push(await runValidationCheck(dependencies, context.emitEvent));
|
|
6252
|
+
results2.push(await runValidationCheck(dependencies, context.emitEvent, clock));
|
|
5953
6253
|
}
|
|
5954
|
-
const configuredResults = await runConfiguredChecksSerially(settings.checks, context.cwd, shellRunner, context.emitEvent);
|
|
6254
|
+
const configuredResults = await runConfiguredChecksSerially(settings.checks, context.cwd, shellRunner, context.emitEvent, clock);
|
|
5955
6255
|
results2.push(...configuredResults);
|
|
5956
6256
|
const exitCode2 = displayResults(results2, context);
|
|
5957
6257
|
return { exitCode: exitCode2 };
|
|
5958
6258
|
}
|
|
5959
6259
|
const checkPromises = [];
|
|
5960
6260
|
if (hasDustDir) {
|
|
5961
|
-
checkPromises.push(runValidationCheck(dependencies, context.emitEvent));
|
|
6261
|
+
checkPromises.push(runValidationCheck(dependencies, context.emitEvent, clock));
|
|
5962
6262
|
}
|
|
5963
|
-
checkPromises.push(runConfiguredChecks(settings.checks, context.cwd, shellRunner, context.emitEvent));
|
|
6263
|
+
checkPromises.push(runConfiguredChecks(settings.checks, context.cwd, shellRunner, context.emitEvent, clock));
|
|
5964
6264
|
const promiseResults = await Promise.all(checkPromises);
|
|
5965
6265
|
const results = [];
|
|
5966
6266
|
for (const result of promiseResults) {
|
|
@@ -6299,7 +6599,7 @@ async function list(dependencies) {
|
|
|
6299
6599
|
ideas: collectedItems.map((i) => ({
|
|
6300
6600
|
path: i.path,
|
|
6301
6601
|
title: i.title,
|
|
6302
|
-
status: i.status
|
|
6602
|
+
status: i.status
|
|
6303
6603
|
}))
|
|
6304
6604
|
});
|
|
6305
6605
|
} else if (type === "principles") {
|
|
@@ -6566,8 +6866,8 @@ function parseGitDiffNameStatus(output) {
|
|
|
6566
6866
|
const parts = line.split("\t");
|
|
6567
6867
|
if (parts.length >= 2) {
|
|
6568
6868
|
const statusChar = parts[0].charAt(0);
|
|
6569
|
-
const
|
|
6570
|
-
changes.push({ status: statusChar, path:
|
|
6869
|
+
const path2 = parts.length > 2 ? parts[2] : parts[1];
|
|
6870
|
+
changes.push({ status: statusChar, path: path2 });
|
|
6571
6871
|
}
|
|
6572
6872
|
}
|
|
6573
6873
|
return changes;
|
|
@@ -6628,12 +6928,12 @@ async function getUncommittedFiles(cwd, gitRunner) {
|
|
|
6628
6928
|
`).filter((line) => line.length > 0);
|
|
6629
6929
|
for (const line of lines) {
|
|
6630
6930
|
if (line.length > 3) {
|
|
6631
|
-
const
|
|
6632
|
-
const arrowIndex =
|
|
6931
|
+
const path2 = line.substring(3);
|
|
6932
|
+
const arrowIndex = path2.indexOf(" -> ");
|
|
6633
6933
|
if (arrowIndex !== -1) {
|
|
6634
|
-
files.push(
|
|
6934
|
+
files.push(path2.substring(arrowIndex + 4));
|
|
6635
6935
|
} else {
|
|
6636
|
-
files.push(
|
|
6936
|
+
files.push(path2);
|
|
6637
6937
|
}
|
|
6638
6938
|
}
|
|
6639
6939
|
}
|
|
@@ -6784,24 +7084,24 @@ async function main(options) {
|
|
|
6784
7084
|
function createFileSystem(primitives) {
|
|
6785
7085
|
return {
|
|
6786
7086
|
exists: primitives.existsSync,
|
|
6787
|
-
isDirectory: (
|
|
7087
|
+
isDirectory: (path2) => {
|
|
6788
7088
|
try {
|
|
6789
|
-
return primitives.statSync(
|
|
7089
|
+
return primitives.statSync(path2).isDirectory();
|
|
6790
7090
|
} catch {
|
|
6791
7091
|
return false;
|
|
6792
7092
|
}
|
|
6793
7093
|
},
|
|
6794
|
-
readFile: (
|
|
6795
|
-
writeFile: (
|
|
7094
|
+
readFile: (path2) => primitives.readFile(path2, "utf-8"),
|
|
7095
|
+
writeFile: (path2, content, options) => primitives.writeFile(path2, content, {
|
|
6796
7096
|
encoding: "utf-8",
|
|
6797
7097
|
flag: options?.flag
|
|
6798
7098
|
}),
|
|
6799
|
-
mkdir: async (
|
|
6800
|
-
await primitives.mkdir(
|
|
7099
|
+
mkdir: async (path2, options) => {
|
|
7100
|
+
await primitives.mkdir(path2, options);
|
|
6801
7101
|
},
|
|
6802
|
-
getFileCreationTime: (
|
|
6803
|
-
readdir: (
|
|
6804
|
-
chmod: (
|
|
7102
|
+
getFileCreationTime: (path2) => primitives.statSync(path2).birthtimeMs,
|
|
7103
|
+
readdir: (path2) => primitives.readdir(path2),
|
|
7104
|
+
chmod: (path2, mode) => primitives.chmod(path2, mode),
|
|
6805
7105
|
rename: (oldPath, newPath) => primitives.rename(oldPath, newPath)
|
|
6806
7106
|
};
|
|
6807
7107
|
}
|