@spencer-kit/coder-studio 0.3.2 → 0.3.4
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/CHANGELOG.md +12 -0
- package/dist/esm/bin.mjs +1155 -636
- package/dist/esm/bin.mjs.map +4 -4
- package/dist/esm/server-runner.mjs +1137 -618
- package/dist/esm/server-runner.mjs.map +4 -4
- package/dist/web/assets/index-gL8kTxHV.css +1 -0
- package/dist/web/assets/index-xgtwbfqN.js +111 -0
- package/dist/web/assets/index-xgtwbfqN.js.map +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-BjrMfcUG.js +0 -111
- package/dist/web/assets/index-BjrMfcUG.js.map +0 -1
- package/dist/web/assets/index-mL_Aq31j.css +0 -1
package/dist/esm/bin.mjs
CHANGED
|
@@ -115,8 +115,8 @@ var init_plugin = __esm({
|
|
|
115
115
|
init_web_ui_routing();
|
|
116
116
|
init_login_protection();
|
|
117
117
|
AUTH_COOKIE_NAME = "coder_studio_auth";
|
|
118
|
-
isPublicPath = (
|
|
119
|
-
const pathname = getRequestPathname(
|
|
118
|
+
isPublicPath = (path10) => {
|
|
119
|
+
const pathname = getRequestPathname(path10);
|
|
120
120
|
return pathname === "/" || pathname === "/login" || pathname === "/healthz" || pathname === "/auth/status" || pathname === "/auth/login" || pathname === "/auth/logout" || pathname.startsWith("/@") || isPublicStaticPath(pathname);
|
|
121
121
|
};
|
|
122
122
|
parseCookies = (cookieHeader) => {
|
|
@@ -809,11 +809,11 @@ var init_src = __esm({
|
|
|
809
809
|
|
|
810
810
|
// packages/utils/src/direct-execution.ts
|
|
811
811
|
import { posix, resolve } from "node:path";
|
|
812
|
-
function isWindowsDrivePath(
|
|
813
|
-
return /^[A-Za-z]:\//.test(
|
|
812
|
+
function isWindowsDrivePath(path10) {
|
|
813
|
+
return /^[A-Za-z]:\//.test(path10);
|
|
814
814
|
}
|
|
815
|
-
function normalizeComparablePath(
|
|
816
|
-
let normalized =
|
|
815
|
+
function normalizeComparablePath(path10) {
|
|
816
|
+
let normalized = path10.replace(/\\/g, "/");
|
|
817
817
|
if (/^\/[A-Za-z]:\//.test(normalized)) {
|
|
818
818
|
normalized = normalized.slice(1);
|
|
819
819
|
}
|
|
@@ -837,8 +837,8 @@ function normalizeModuleUrlPath(moduleUrl) {
|
|
|
837
837
|
if (url.protocol !== "file:") {
|
|
838
838
|
return null;
|
|
839
839
|
}
|
|
840
|
-
const
|
|
841
|
-
return normalizeComparablePath(
|
|
840
|
+
const path10 = `${url.host ? `//${url.host}` : ""}${decodeURIComponent(url.pathname)}`;
|
|
841
|
+
return normalizeComparablePath(path10);
|
|
842
842
|
}
|
|
843
843
|
function normalizeArgvPath(argv1) {
|
|
844
844
|
const isAbsoluteWindowsPath = /^[A-Za-z]:[\\/]/.test(argv1) || /^\\\\/.test(argv1);
|
|
@@ -1017,10 +1017,10 @@ var init_image = __esm({
|
|
|
1017
1017
|
// packages/server/src/fs/file-io.ts
|
|
1018
1018
|
import { createHash } from "crypto";
|
|
1019
1019
|
import { readFile as fsReadFile, writeFile as fsWriteFile, mkdir, rm, stat } from "fs/promises";
|
|
1020
|
-
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
1021
|
-
async function statSafe(
|
|
1020
|
+
import { dirname as dirname2, isAbsolute, relative, resolve as resolve2 } from "path";
|
|
1021
|
+
async function statSafe(path10) {
|
|
1022
1022
|
try {
|
|
1023
|
-
return await stat(
|
|
1023
|
+
return await stat(path10);
|
|
1024
1024
|
} catch {
|
|
1025
1025
|
return null;
|
|
1026
1026
|
}
|
|
@@ -1053,7 +1053,8 @@ async function deleteEntry(rootPath, relPath) {
|
|
|
1053
1053
|
function resolveSafe(root, relPath) {
|
|
1054
1054
|
const absRoot = resolve2(root);
|
|
1055
1055
|
const abs = resolve2(absRoot, relPath);
|
|
1056
|
-
|
|
1056
|
+
const rel = relative(absRoot, abs);
|
|
1057
|
+
if (rel === ".." || rel.startsWith(`..${"/"}`) || isAbsolute(rel)) {
|
|
1057
1058
|
throw { code: "path_escape", message: "Path escapes workspace root" };
|
|
1058
1059
|
}
|
|
1059
1060
|
return abs;
|
|
@@ -1115,8 +1116,10 @@ var init_file_io = __esm({
|
|
|
1115
1116
|
// packages/server/src/routes/file-asset.ts
|
|
1116
1117
|
import { createReadStream } from "fs";
|
|
1117
1118
|
import { realpath, stat as stat2 } from "fs/promises";
|
|
1119
|
+
import { isAbsolute as isAbsolute2, relative as relative2 } from "path";
|
|
1118
1120
|
function isPathInsideRoot(rootPath, targetPath) {
|
|
1119
|
-
|
|
1121
|
+
const rel = relative2(rootPath, targetPath);
|
|
1122
|
+
return rel !== ".." && !rel.startsWith(`..${"/"}`) && !isAbsolute2(rel);
|
|
1120
1123
|
}
|
|
1121
1124
|
function registerFileAssetRoutes(app, deps) {
|
|
1122
1125
|
app.get(
|
|
@@ -1241,12 +1244,12 @@ async function ensureSafeUploadDir(rootDir, targetDir) {
|
|
|
1241
1244
|
await mkdir2(resolvedRoot, { recursive: true });
|
|
1242
1245
|
await assertDirectorySegmentSafe(resolvedRoot);
|
|
1243
1246
|
}
|
|
1244
|
-
const
|
|
1245
|
-
if (!
|
|
1247
|
+
const relative5 = path3.relative(resolvedRoot, resolvedTarget);
|
|
1248
|
+
if (!relative5) {
|
|
1246
1249
|
return;
|
|
1247
1250
|
}
|
|
1248
1251
|
let current = resolvedRoot;
|
|
1249
|
-
for (const segment of
|
|
1252
|
+
for (const segment of relative5.split(path3.sep)) {
|
|
1250
1253
|
current = path3.join(current, segment);
|
|
1251
1254
|
try {
|
|
1252
1255
|
await assertDirectorySegmentSafe(current);
|
|
@@ -1795,189 +1798,265 @@ var init_app = __esm({
|
|
|
1795
1798
|
}
|
|
1796
1799
|
});
|
|
1797
1800
|
|
|
1798
|
-
// packages/server/src/
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
const selected = audit.findings.filter((f) => opts.removeIds.includes(f.id));
|
|
1837
|
-
if (selected.length === 0) {
|
|
1838
|
-
return { removed: [], backupPath: null, noop: true };
|
|
1839
|
-
}
|
|
1840
|
-
const original = readFileSync3(configPath, "utf-8");
|
|
1841
|
-
const backupPath = writeBackup(configPath, original, opts.backupDir);
|
|
1842
|
-
const linesToDrop = /* @__PURE__ */ new Set();
|
|
1843
|
-
for (const finding of selected) {
|
|
1844
|
-
for (let ln = finding.startLine; ln <= finding.endLine; ln++) {
|
|
1845
|
-
linesToDrop.add(ln);
|
|
1846
|
-
}
|
|
1847
|
-
}
|
|
1848
|
-
const originalLines = original.split(/\r?\n/);
|
|
1849
|
-
const kept = [];
|
|
1850
|
-
for (let i = 0; i < originalLines.length; i++) {
|
|
1851
|
-
const ln = i + 1;
|
|
1852
|
-
if (linesToDrop.has(ln)) continue;
|
|
1853
|
-
kept.push(originalLines[i]);
|
|
1854
|
-
}
|
|
1855
|
-
const cleaned = collapseBlankRunsNearDeletions(kept);
|
|
1856
|
-
const output2 = cleaned.join("\n");
|
|
1857
|
-
atomicWrite(configPath, output2);
|
|
1858
|
-
return {
|
|
1859
|
-
removed: selected.map((f) => f.id),
|
|
1860
|
-
backupPath,
|
|
1861
|
-
noop: false
|
|
1862
|
-
};
|
|
1863
|
-
}
|
|
1864
|
-
function detectTopLevelNotify(lines) {
|
|
1865
|
-
const headerRegex = /^\s*\[/;
|
|
1866
|
-
const notifyRegex = /^\s*notify\s*=\s*(.*)$/;
|
|
1867
|
-
let inTopLevel = true;
|
|
1868
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1869
|
-
const line = lines[i];
|
|
1870
|
-
const trimmed = line.trim();
|
|
1871
|
-
if (headerRegex.test(trimmed)) {
|
|
1872
|
-
inTopLevel = false;
|
|
1873
|
-
continue;
|
|
1874
|
-
}
|
|
1875
|
-
if (!inTopLevel) continue;
|
|
1876
|
-
const m = trimmed.match(notifyRegex);
|
|
1877
|
-
if (!m) continue;
|
|
1878
|
-
const rhs = (m[1] ?? "").trim();
|
|
1879
|
-
if (rhs.startsWith("[") && rhs.endsWith("]") && countBrackets(rhs) === 0) {
|
|
1880
|
-
return makeNotifyFinding(lines, i, i);
|
|
1881
|
-
}
|
|
1882
|
-
if (rhs.startsWith("[")) {
|
|
1883
|
-
let depth = countBrackets(rhs);
|
|
1884
|
-
for (let j = i + 1; j < lines.length; j++) {
|
|
1885
|
-
depth += countBrackets(lines[j]);
|
|
1886
|
-
if (depth === 0) {
|
|
1887
|
-
return makeNotifyFinding(lines, i, j);
|
|
1801
|
+
// packages/server/src/git/auto-fetch.ts
|
|
1802
|
+
var PERIOD_SETTING_KEY, DEFAULT_PERIOD_SEC, TICK_INTERVAL_MS, OPEN_TIME_COOLDOWN_MS, MAX_CONSECUTIVE_FAILURES, JITTER_RATIO, AutoFetchScheduler;
|
|
1803
|
+
var init_auto_fetch = __esm({
|
|
1804
|
+
"packages/server/src/git/auto-fetch.ts"() {
|
|
1805
|
+
"use strict";
|
|
1806
|
+
PERIOD_SETTING_KEY = "git.autofetchPeriodSec";
|
|
1807
|
+
DEFAULT_PERIOD_SEC = 180;
|
|
1808
|
+
TICK_INTERVAL_MS = 1e3;
|
|
1809
|
+
OPEN_TIME_COOLDOWN_MS = 5 * 60 * 1e3;
|
|
1810
|
+
MAX_CONSECUTIVE_FAILURES = 3;
|
|
1811
|
+
JITTER_RATIO = 0.1;
|
|
1812
|
+
AutoFetchScheduler = class {
|
|
1813
|
+
constructor(deps) {
|
|
1814
|
+
this.deps = deps;
|
|
1815
|
+
this.now = deps.now ?? Date.now;
|
|
1816
|
+
this.random = deps.random ?? (() => 0.5);
|
|
1817
|
+
this.setTimeoutFn = deps.setTimeout ?? globalThis.setTimeout;
|
|
1818
|
+
this.setIntervalFn = deps.setInterval ?? globalThis.setInterval;
|
|
1819
|
+
this.clearIntervalFn = deps.clearInterval ?? globalThis.clearInterval;
|
|
1820
|
+
this.clearTimeoutFn = deps.clearTimeout ?? globalThis.clearTimeout;
|
|
1821
|
+
this.start();
|
|
1822
|
+
}
|
|
1823
|
+
deps;
|
|
1824
|
+
clientWorkspaceMap = /* @__PURE__ */ new Map();
|
|
1825
|
+
workspaceStateMap = /* @__PURE__ */ new Map();
|
|
1826
|
+
now;
|
|
1827
|
+
random;
|
|
1828
|
+
setTimeoutFn;
|
|
1829
|
+
setIntervalFn;
|
|
1830
|
+
clearIntervalFn;
|
|
1831
|
+
clearTimeoutFn;
|
|
1832
|
+
pendingTimeouts = /* @__PURE__ */ new Set();
|
|
1833
|
+
tickTimer = null;
|
|
1834
|
+
stopped = false;
|
|
1835
|
+
registerViewer(clientId, workspaceId) {
|
|
1836
|
+
const previousWorkspaceId = this.clientWorkspaceMap.get(clientId);
|
|
1837
|
+
if (previousWorkspaceId === workspaceId) {
|
|
1838
|
+
return;
|
|
1888
1839
|
}
|
|
1840
|
+
if (previousWorkspaceId) {
|
|
1841
|
+
this.unregisterViewer(clientId);
|
|
1842
|
+
}
|
|
1843
|
+
this.clientWorkspaceMap.set(clientId, workspaceId);
|
|
1844
|
+
const state = this.getOrCreateState(workspaceId);
|
|
1845
|
+
state.viewerCount += 1;
|
|
1846
|
+
if (state.viewerCount === 1) {
|
|
1847
|
+
state.blocked = false;
|
|
1848
|
+
state.consecutiveFailures = 0;
|
|
1849
|
+
}
|
|
1850
|
+
this.ensureNextPeriodicFetch(state, false);
|
|
1851
|
+
}
|
|
1852
|
+
unregisterViewer(clientId) {
|
|
1853
|
+
const workspaceId = this.clientWorkspaceMap.get(clientId);
|
|
1854
|
+
if (!workspaceId) {
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
this.clientWorkspaceMap.delete(clientId);
|
|
1858
|
+
const state = this.workspaceStateMap.get(workspaceId);
|
|
1859
|
+
if (!state) {
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
state.viewerCount = Math.max(0, state.viewerCount - 1);
|
|
1863
|
+
if (state.viewerCount === 0) {
|
|
1864
|
+
state.blocked = false;
|
|
1865
|
+
state.consecutiveFailures = 0;
|
|
1866
|
+
state.nextFetchAt = void 0;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
triggerOpenTimeFetch(workspaceId) {
|
|
1870
|
+
const state = this.getOrCreateState(workspaceId);
|
|
1871
|
+
const lastFetchAt = state.lastFetchAt;
|
|
1872
|
+
if (this.stopped || state.inFlight) {
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
if (lastFetchAt !== void 0 && this.now() - lastFetchAt < OPEN_TIME_COOLDOWN_MS) {
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
const timer = this.setTimeoutFn(() => {
|
|
1879
|
+
this.pendingTimeouts.delete(timer);
|
|
1880
|
+
if (this.stopped) {
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
void this.fetchWorkspace(workspaceId, "open");
|
|
1884
|
+
}, 0);
|
|
1885
|
+
this.pendingTimeouts.add(timer);
|
|
1886
|
+
}
|
|
1887
|
+
recordSuccess(workspaceId) {
|
|
1888
|
+
const state = this.getOrCreateState(workspaceId);
|
|
1889
|
+
state.lastFetchAt = this.now();
|
|
1890
|
+
state.consecutiveFailures = 0;
|
|
1891
|
+
state.blocked = false;
|
|
1892
|
+
state.nextFetchAt = void 0;
|
|
1893
|
+
this.ensureNextPeriodicFetch(state, true);
|
|
1894
|
+
}
|
|
1895
|
+
recordFailure(workspaceId) {
|
|
1896
|
+
const state = this.getOrCreateState(workspaceId);
|
|
1897
|
+
state.consecutiveFailures += 1;
|
|
1898
|
+
state.nextFetchAt = void 0;
|
|
1899
|
+
if (state.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
1900
|
+
state.blocked = true;
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
this.ensureNextPeriodicFetch(state, true);
|
|
1904
|
+
}
|
|
1905
|
+
getLastFetchAt(workspaceId) {
|
|
1906
|
+
return this.workspaceStateMap.get(workspaceId)?.lastFetchAt;
|
|
1907
|
+
}
|
|
1908
|
+
async runExclusive(workspaceId, op) {
|
|
1909
|
+
const release = await this.acquireWorkspaceOperation(workspaceId);
|
|
1910
|
+
try {
|
|
1911
|
+
return await op();
|
|
1912
|
+
} finally {
|
|
1913
|
+
release();
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
start() {
|
|
1917
|
+
if (this.tickTimer) {
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
1920
|
+
this.stopped = false;
|
|
1921
|
+
this.tickTimer = this.setIntervalFn(() => {
|
|
1922
|
+
this.evaluateDueFetches();
|
|
1923
|
+
}, TICK_INTERVAL_MS);
|
|
1924
|
+
}
|
|
1925
|
+
stop() {
|
|
1926
|
+
if (this.tickTimer) {
|
|
1927
|
+
this.clearIntervalFn(this.tickTimer);
|
|
1928
|
+
this.tickTimer = null;
|
|
1929
|
+
}
|
|
1930
|
+
this.stopped = true;
|
|
1931
|
+
for (const timer of this.pendingTimeouts) {
|
|
1932
|
+
this.clearTimeoutFn(timer);
|
|
1933
|
+
}
|
|
1934
|
+
this.pendingTimeouts.clear();
|
|
1935
|
+
}
|
|
1936
|
+
evaluateDueFetches() {
|
|
1937
|
+
const now = this.now();
|
|
1938
|
+
for (const [workspaceId, state] of this.workspaceStateMap) {
|
|
1939
|
+
if (!this.shouldCheckWorkspace(state)) {
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1942
|
+
if (state.nextFetchAt === void 0) {
|
|
1943
|
+
this.ensureNextPeriodicFetch(state, false);
|
|
1944
|
+
}
|
|
1945
|
+
if (state.nextFetchAt === void 0 || state.nextFetchAt > now) {
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
void this.fetchWorkspace(workspaceId, "periodic", state.nextFetchAt);
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
shouldCheckWorkspace(state) {
|
|
1952
|
+
return this.getPeriodMs() > 0 && state.viewerCount > 0 && !state.inFlight && !state.blocked;
|
|
1953
|
+
}
|
|
1954
|
+
ensureNextPeriodicFetch(state, resetSchedule) {
|
|
1955
|
+
const periodMs = this.getPeriodMs();
|
|
1956
|
+
if (periodMs <= 0 || state.viewerCount <= 0 || state.blocked || state.inFlight) {
|
|
1957
|
+
return;
|
|
1958
|
+
}
|
|
1959
|
+
if (!resetSchedule && state.nextFetchAt !== void 0) {
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
if (state.lastFetchAt === void 0) {
|
|
1963
|
+
state.nextFetchAt = this.now();
|
|
1964
|
+
return;
|
|
1965
|
+
}
|
|
1966
|
+
const nextFetchAt = state.lastFetchAt + this.getJitteredPeriodMs(periodMs);
|
|
1967
|
+
state.nextFetchAt = Math.max(this.now(), nextFetchAt);
|
|
1968
|
+
}
|
|
1969
|
+
getPeriodMs() {
|
|
1970
|
+
const configuredPeriodSec = this.deps.settingsRepo.get(PERIOD_SETTING_KEY);
|
|
1971
|
+
const periodSec = configuredPeriodSec ?? DEFAULT_PERIOD_SEC;
|
|
1972
|
+
return Math.max(0, periodSec) * 1e3;
|
|
1973
|
+
}
|
|
1974
|
+
getJitteredPeriodMs(periodMs) {
|
|
1975
|
+
const jitterScale = 1 + (this.random() - 0.5) * 2 * JITTER_RATIO;
|
|
1976
|
+
return Math.round(periodMs * jitterScale);
|
|
1977
|
+
}
|
|
1978
|
+
getOrCreateState(workspaceId) {
|
|
1979
|
+
const existingState = this.workspaceStateMap.get(workspaceId);
|
|
1980
|
+
if (existingState) {
|
|
1981
|
+
return existingState;
|
|
1982
|
+
}
|
|
1983
|
+
const state = {
|
|
1984
|
+
viewerCount: 0,
|
|
1985
|
+
consecutiveFailures: 0,
|
|
1986
|
+
inFlight: false,
|
|
1987
|
+
blocked: false,
|
|
1988
|
+
waiters: []
|
|
1989
|
+
};
|
|
1990
|
+
this.workspaceStateMap.set(workspaceId, state);
|
|
1991
|
+
return state;
|
|
1992
|
+
}
|
|
1993
|
+
async fetchWorkspace(workspaceId, mode, scheduledAt) {
|
|
1994
|
+
if (this.stopped) {
|
|
1995
|
+
return;
|
|
1996
|
+
}
|
|
1997
|
+
const state = this.getOrCreateState(workspaceId);
|
|
1998
|
+
if (state.inFlight || state.blocked) {
|
|
1999
|
+
return;
|
|
2000
|
+
}
|
|
2001
|
+
if (!this.deps.workspaceMgr.get(workspaceId)) {
|
|
2002
|
+
state.nextFetchAt = void 0;
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
await this.runExclusive(workspaceId, async () => {
|
|
2006
|
+
const currentState = this.getOrCreateState(workspaceId);
|
|
2007
|
+
if (this.stopped || currentState.blocked) {
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
if (!this.deps.workspaceMgr.get(workspaceId)) {
|
|
2011
|
+
currentState.nextFetchAt = void 0;
|
|
2012
|
+
return;
|
|
2013
|
+
}
|
|
2014
|
+
if (mode === "open") {
|
|
2015
|
+
const lastFetchAt = currentState.lastFetchAt;
|
|
2016
|
+
if (lastFetchAt !== void 0 && this.now() - lastFetchAt < OPEN_TIME_COOLDOWN_MS) {
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
} else if (scheduledAt !== void 0 && currentState.lastFetchAt !== void 0 && currentState.lastFetchAt >= scheduledAt) {
|
|
2020
|
+
return;
|
|
2021
|
+
}
|
|
2022
|
+
try {
|
|
2023
|
+
await this.deps.runFetch(workspaceId);
|
|
2024
|
+
this.recordSuccess(workspaceId);
|
|
2025
|
+
} catch {
|
|
2026
|
+
this.recordFailure(workspaceId);
|
|
2027
|
+
}
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
acquireWorkspaceOperation(workspaceId) {
|
|
2031
|
+
const state = this.getOrCreateState(workspaceId);
|
|
2032
|
+
return new Promise((resolve4) => {
|
|
2033
|
+
const grant = () => {
|
|
2034
|
+
state.inFlight = true;
|
|
2035
|
+
state.nextFetchAt = void 0;
|
|
2036
|
+
let released = false;
|
|
2037
|
+
resolve4(() => {
|
|
2038
|
+
if (released) {
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
released = true;
|
|
2042
|
+
const next = state.waiters.shift();
|
|
2043
|
+
if (next) {
|
|
2044
|
+
next();
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
state.inFlight = false;
|
|
2048
|
+
this.ensureNextPeriodicFetch(state, true);
|
|
2049
|
+
});
|
|
2050
|
+
};
|
|
2051
|
+
if (state.inFlight) {
|
|
2052
|
+
state.waiters.push(grant);
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
grant();
|
|
2056
|
+
});
|
|
1889
2057
|
}
|
|
1890
|
-
return null;
|
|
1891
|
-
}
|
|
1892
|
-
return makeNotifyFinding(lines, i, i);
|
|
1893
|
-
}
|
|
1894
|
-
return null;
|
|
1895
|
-
}
|
|
1896
|
-
function makeNotifyFinding(lines, startIdx, endIdx) {
|
|
1897
|
-
return {
|
|
1898
|
-
id: "toml_notify",
|
|
1899
|
-
type: "toml_notify",
|
|
1900
|
-
severity: "warn",
|
|
1901
|
-
startLine: startIdx + 1,
|
|
1902
|
-
endLine: endIdx + 1,
|
|
1903
|
-
snippet: lines.slice(startIdx, endIdx + 1).join("\n"),
|
|
1904
|
-
message: "config.toml \u9876\u5C42\u8BBE\u7F6E\u4E86 notify\uFF0C\u4F1A\u4E0E Coder Studio \u7684\u542F\u52A8\u53C2\u6570\u6CE8\u5165\u51B2\u7A81\uFF0C\u53EF\u80FD\u5BFC\u81F4 session \u72B6\u6001\u4E0D\u540C\u6B65\u3002"
|
|
1905
|
-
};
|
|
1906
|
-
}
|
|
1907
|
-
function detectCodexHooksFlag(lines) {
|
|
1908
|
-
const headerRegex = /^\s*\[([^\]]+)\]\s*$/;
|
|
1909
|
-
const codexHooksRegex = /^\s*codex_hooks\s*=\s*true\b/;
|
|
1910
|
-
let currentSection = null;
|
|
1911
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1912
|
-
const line = lines[i];
|
|
1913
|
-
const headerMatch = line.match(headerRegex);
|
|
1914
|
-
if (headerMatch) {
|
|
1915
|
-
currentSection = headerMatch[1].trim();
|
|
1916
|
-
continue;
|
|
1917
|
-
}
|
|
1918
|
-
if (currentSection !== "features") continue;
|
|
1919
|
-
if (!codexHooksRegex.test(line)) continue;
|
|
1920
|
-
return {
|
|
1921
|
-
id: "toml_codex_hooks",
|
|
1922
|
-
type: "toml_codex_hooks",
|
|
1923
|
-
severity: "info",
|
|
1924
|
-
startLine: i + 1,
|
|
1925
|
-
endLine: i + 1,
|
|
1926
|
-
snippet: line,
|
|
1927
|
-
message: "[features] codex_hooks = true \u542F\u7528\u4E86 Codex CLI \u7684\u5B9E\u9A8C\u6027 hook \u5F15\u64CE\uFF0C\u53EF\u80FD\u5F71\u54CD notify \u7684\u884C\u4E3A\u3002\u82E5\u4E0D\u4E3B\u52A8\u4F7F\u7528\u8BE5\u7279\u6027\uFF0C\u5EFA\u8BAE\u5173\u95ED\u3002"
|
|
1928
2058
|
};
|
|
1929
2059
|
}
|
|
1930
|
-
return null;
|
|
1931
|
-
}
|
|
1932
|
-
function countBrackets(s) {
|
|
1933
|
-
let n = 0;
|
|
1934
|
-
for (const ch of s) {
|
|
1935
|
-
if (ch === "[") n++;
|
|
1936
|
-
else if (ch === "]") n--;
|
|
1937
|
-
}
|
|
1938
|
-
return n;
|
|
1939
|
-
}
|
|
1940
|
-
function writeBackup(configPath, original, backupDir) {
|
|
1941
|
-
const dir = backupDir ?? dirname3(configPath);
|
|
1942
|
-
if (!existsSync3(dir)) {
|
|
1943
|
-
mkdirSync2(dir, { recursive: true });
|
|
1944
|
-
}
|
|
1945
|
-
const ts = formatTimestamp(/* @__PURE__ */ new Date());
|
|
1946
|
-
const backupPath = join2(dir, `${basenameNoTomlExt(configPath)}.bak.${ts}.toml`);
|
|
1947
|
-
writeFileSync2(backupPath, original, "utf-8");
|
|
1948
|
-
return backupPath;
|
|
1949
|
-
}
|
|
1950
|
-
function atomicWrite(configPath, contents) {
|
|
1951
|
-
const tempPath = `${configPath}.tmp`;
|
|
1952
|
-
writeFileSync2(tempPath, contents, "utf-8");
|
|
1953
|
-
renameSync(tempPath, configPath);
|
|
1954
|
-
}
|
|
1955
|
-
function basenameNoTomlExt(p) {
|
|
1956
|
-
const base = p.split(/[\\/]/).pop() ?? "config.toml";
|
|
1957
|
-
return base.replace(/\.toml$/i, "");
|
|
1958
|
-
}
|
|
1959
|
-
function formatTimestamp(d) {
|
|
1960
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
1961
|
-
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
1962
|
-
}
|
|
1963
|
-
function collapseBlankRunsNearDeletions(lines) {
|
|
1964
|
-
const out = [];
|
|
1965
|
-
let blankRun = 0;
|
|
1966
|
-
for (const line of lines) {
|
|
1967
|
-
if (line.trim() === "") {
|
|
1968
|
-
blankRun++;
|
|
1969
|
-
if (blankRun <= 2) out.push(line);
|
|
1970
|
-
} else {
|
|
1971
|
-
blankRun = 0;
|
|
1972
|
-
out.push(line);
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
return out;
|
|
1976
|
-
}
|
|
1977
|
-
var init_codex_config_audit = __esm({
|
|
1978
|
-
"packages/server/src/config/codex-config-audit.ts"() {
|
|
1979
|
-
"use strict";
|
|
1980
|
-
}
|
|
1981
2060
|
});
|
|
1982
2061
|
|
|
1983
2062
|
// packages/server/src/provider-runtime/command-runner.ts
|
|
@@ -2050,6 +2129,157 @@ var init_command_check = __esm({
|
|
|
2050
2129
|
}
|
|
2051
2130
|
});
|
|
2052
2131
|
|
|
2132
|
+
// packages/server/src/provider-runtime/e2e-provider-mock.ts
|
|
2133
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2134
|
+
import { dirname as dirname3, join as join2 } from "node:path";
|
|
2135
|
+
function createE2EProviderMockOverrides(env = process.env) {
|
|
2136
|
+
const statePath = env.CODER_STUDIO_E2E_PROVIDER_STATE_PATH;
|
|
2137
|
+
if (!statePath) {
|
|
2138
|
+
return null;
|
|
2139
|
+
}
|
|
2140
|
+
const binDir = env.CODER_STUDIO_E2E_PROVIDER_BIN_DIR;
|
|
2141
|
+
const debugLogPath = env.CODER_STUDIO_E2E_PROVIDER_DEBUG_LOG_PATH;
|
|
2142
|
+
appendDebugLog(debugLogPath, `init statePath=${statePath} binDir=${binDir ?? ""}`);
|
|
2143
|
+
const commandExists = async (command) => {
|
|
2144
|
+
const state = readMockState(statePath);
|
|
2145
|
+
const override = state.commands?.[command];
|
|
2146
|
+
appendDebugLog(
|
|
2147
|
+
debugLogPath,
|
|
2148
|
+
`commandExists ${command} override=${String(override)} state=${JSON.stringify(state.commands ?? {})}`
|
|
2149
|
+
);
|
|
2150
|
+
if (typeof override === "boolean") {
|
|
2151
|
+
return override;
|
|
2152
|
+
}
|
|
2153
|
+
return checkCommandAvailable(command);
|
|
2154
|
+
};
|
|
2155
|
+
const runCommand2 = async (file, args, options) => {
|
|
2156
|
+
const providerId = getInstallProviderId(file, args);
|
|
2157
|
+
appendDebugLog(
|
|
2158
|
+
debugLogPath,
|
|
2159
|
+
`runCommand ${file} ${args.join(" ")} provider=${providerId ?? "none"}`
|
|
2160
|
+
);
|
|
2161
|
+
if (!providerId) {
|
|
2162
|
+
return runCommandAsString(file, args, options);
|
|
2163
|
+
}
|
|
2164
|
+
const state = readMockState(statePath);
|
|
2165
|
+
const behavior = state.installBehavior?.[providerId];
|
|
2166
|
+
appendDebugLog(
|
|
2167
|
+
debugLogPath,
|
|
2168
|
+
`behavior ${providerId} ${JSON.stringify(behavior)} state=${JSON.stringify(state)}`
|
|
2169
|
+
);
|
|
2170
|
+
if (!behavior) {
|
|
2171
|
+
return runCommandAsString(file, args, options);
|
|
2172
|
+
}
|
|
2173
|
+
if (behavior.result === "success") {
|
|
2174
|
+
writeMockState(statePath, (draft) => {
|
|
2175
|
+
draft.commands ??= {};
|
|
2176
|
+
draft.commands[providerId] = true;
|
|
2177
|
+
});
|
|
2178
|
+
if (binDir) {
|
|
2179
|
+
ensureProviderCommand(binDir, providerId);
|
|
2180
|
+
}
|
|
2181
|
+
appendDebugLog(debugLogPath, `install success ${providerId}`);
|
|
2182
|
+
return {
|
|
2183
|
+
stdout: `installed ${providerId}`,
|
|
2184
|
+
stderr: ""
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
const message = behavior.message ?? (behavior.result === "permission_denied" ? "permission denied" : "command not found");
|
|
2188
|
+
throw Object.assign(new Error(message), {
|
|
2189
|
+
exitCode: 1,
|
|
2190
|
+
stdout: "",
|
|
2191
|
+
stderr: message
|
|
2192
|
+
});
|
|
2193
|
+
};
|
|
2194
|
+
return {
|
|
2195
|
+
commandExists,
|
|
2196
|
+
runCommand: runCommand2
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
function getInstallProviderId(file, args) {
|
|
2200
|
+
if (file !== "npm" || args.length !== 3) {
|
|
2201
|
+
return null;
|
|
2202
|
+
}
|
|
2203
|
+
if (args[0] !== "install" || args[1] !== "-g") {
|
|
2204
|
+
return null;
|
|
2205
|
+
}
|
|
2206
|
+
const packageName = args[2];
|
|
2207
|
+
if (packageName === PROVIDER_INSTALL_PACKAGES.claude) {
|
|
2208
|
+
return "claude";
|
|
2209
|
+
}
|
|
2210
|
+
if (packageName === PROVIDER_INSTALL_PACKAGES.codex) {
|
|
2211
|
+
return "codex";
|
|
2212
|
+
}
|
|
2213
|
+
return null;
|
|
2214
|
+
}
|
|
2215
|
+
function readMockState(statePath) {
|
|
2216
|
+
if (!existsSync3(statePath)) {
|
|
2217
|
+
return {};
|
|
2218
|
+
}
|
|
2219
|
+
const raw = readFileSync3(statePath, "utf8");
|
|
2220
|
+
if (!raw.trim()) {
|
|
2221
|
+
return {};
|
|
2222
|
+
}
|
|
2223
|
+
try {
|
|
2224
|
+
return JSON.parse(raw);
|
|
2225
|
+
} catch (error) {
|
|
2226
|
+
throw new Error(
|
|
2227
|
+
`Invalid provider mock state at ${statePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
2228
|
+
);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
function writeMockState(statePath, updater) {
|
|
2232
|
+
const nextState = readMockState(statePath);
|
|
2233
|
+
updater(nextState);
|
|
2234
|
+
mkdirSync2(dirname3(statePath), { recursive: true });
|
|
2235
|
+
writeFileSync2(statePath, JSON.stringify(nextState, null, 2));
|
|
2236
|
+
return nextState;
|
|
2237
|
+
}
|
|
2238
|
+
function ensureProviderCommand(binDir, providerId) {
|
|
2239
|
+
mkdirSync2(binDir, { recursive: true });
|
|
2240
|
+
const scriptPath = join2(binDir, providerId);
|
|
2241
|
+
writeFileSync2(scriptPath, PROVIDER_COMMAND_SCRIPTS[providerId], "utf8");
|
|
2242
|
+
chmodSync(scriptPath, 493);
|
|
2243
|
+
}
|
|
2244
|
+
function appendDebugLog(path10, line) {
|
|
2245
|
+
if (!path10) {
|
|
2246
|
+
return;
|
|
2247
|
+
}
|
|
2248
|
+
mkdirSync2(dirname3(path10), { recursive: true });
|
|
2249
|
+
writeFileSync2(path10, `${line}
|
|
2250
|
+
`, { flag: "a" });
|
|
2251
|
+
}
|
|
2252
|
+
var PROVIDER_INSTALL_PACKAGES, PROVIDER_COMMAND_SCRIPTS;
|
|
2253
|
+
var init_e2e_provider_mock = __esm({
|
|
2254
|
+
"packages/server/src/provider-runtime/e2e-provider-mock.ts"() {
|
|
2255
|
+
"use strict";
|
|
2256
|
+
init_command_check();
|
|
2257
|
+
init_command_runner();
|
|
2258
|
+
PROVIDER_INSTALL_PACKAGES = {
|
|
2259
|
+
claude: "@anthropic-ai/claude-code",
|
|
2260
|
+
codex: "@openai/codex"
|
|
2261
|
+
};
|
|
2262
|
+
PROVIDER_COMMAND_SCRIPTS = {
|
|
2263
|
+
claude: `#!/usr/bin/env bash
|
|
2264
|
+
set -euo pipefail
|
|
2265
|
+
trap 'exit 0' TERM INT
|
|
2266
|
+
printf 'Mock Claude ready\\n'
|
|
2267
|
+
while true; do
|
|
2268
|
+
sleep 1
|
|
2269
|
+
done
|
|
2270
|
+
`,
|
|
2271
|
+
codex: `#!/usr/bin/env bash
|
|
2272
|
+
set -euo pipefail
|
|
2273
|
+
trap 'exit 0' TERM INT
|
|
2274
|
+
printf 'Session ID: abcdef-123456\\n> '
|
|
2275
|
+
while true; do
|
|
2276
|
+
sleep 1
|
|
2277
|
+
done
|
|
2278
|
+
`
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
});
|
|
2282
|
+
|
|
2053
2283
|
// packages/server/src/provider-runtime/install-manager.ts
|
|
2054
2284
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
2055
2285
|
function getErrorDetails(error) {
|
|
@@ -4210,7 +4440,7 @@ function parseStatus(porcelainV2) {
|
|
|
4210
4440
|
continue;
|
|
4211
4441
|
}
|
|
4212
4442
|
if (record.startsWith("? ")) {
|
|
4213
|
-
untracked.push({ path: record.substring(2) });
|
|
4443
|
+
untracked.push({ path: record.substring(2), status: "untracked" });
|
|
4214
4444
|
}
|
|
4215
4445
|
}
|
|
4216
4446
|
return {
|
|
@@ -4231,11 +4461,11 @@ function parseOrdinaryChangedEntry(record, staged, modified, deleted) {
|
|
|
4231
4461
|
if (!xy) {
|
|
4232
4462
|
return;
|
|
4233
4463
|
}
|
|
4234
|
-
const
|
|
4235
|
-
if (!
|
|
4464
|
+
const path10 = parts.slice(8).join(" ");
|
|
4465
|
+
if (!path10) {
|
|
4236
4466
|
return;
|
|
4237
4467
|
}
|
|
4238
|
-
pushChange({ path:
|
|
4468
|
+
pushChange({ path: path10 }, xy, staged, modified, deleted);
|
|
4239
4469
|
}
|
|
4240
4470
|
function parseRenamedEntry(record, oldPathRecord, staged, modified, deleted) {
|
|
4241
4471
|
const parts = record.split(" ");
|
|
@@ -4247,27 +4477,52 @@ function parseRenamedEntry(record, oldPathRecord, staged, modified, deleted) {
|
|
|
4247
4477
|
const pathAndMaybeOldPath = pathTokens.join(" ");
|
|
4248
4478
|
const inlinePathParts = pathAndMaybeOldPath.split(" ");
|
|
4249
4479
|
const fallbackPath = !oldPathRecord && inlinePathParts.length === 1 && pathTokens.length > 1 ? pathTokens.slice(0, -1).join(" ") : void 0;
|
|
4250
|
-
const
|
|
4251
|
-
if (!
|
|
4480
|
+
const path10 = fallbackPath ?? inlinePathParts[0];
|
|
4481
|
+
if (!path10) {
|
|
4252
4482
|
return;
|
|
4253
4483
|
}
|
|
4254
4484
|
const oldPath = (oldPathRecord && !oldPathRecord.startsWith("#") ? oldPathRecord : void 0) ?? inlinePathParts[1] ?? (pathTokens.length > 1 ? pathTokens[pathTokens.length - 1] : void 0);
|
|
4255
|
-
pushChange({ path:
|
|
4485
|
+
pushChange({ path: path10, oldPath }, xy, staged, modified, deleted);
|
|
4256
4486
|
}
|
|
4257
4487
|
function pushChange(change, xy, staged, modified, deleted) {
|
|
4258
4488
|
const indexStatus = xy[0];
|
|
4259
4489
|
const worktreeStatus = xy[1];
|
|
4260
4490
|
if (indexStatus && indexStatus !== "." && indexStatus !== " ") {
|
|
4261
|
-
staged.push(
|
|
4491
|
+
staged.push({
|
|
4492
|
+
...change,
|
|
4493
|
+
status: resolveGitChangeStatus(indexStatus, change.oldPath)
|
|
4494
|
+
});
|
|
4262
4495
|
}
|
|
4263
4496
|
if (!worktreeStatus || worktreeStatus === "." || worktreeStatus === " ") {
|
|
4264
4497
|
return;
|
|
4265
4498
|
}
|
|
4266
4499
|
if (worktreeStatus === "D") {
|
|
4267
|
-
deleted.push({ path: change.path });
|
|
4500
|
+
deleted.push({ path: change.path, status: "deleted" });
|
|
4268
4501
|
return;
|
|
4269
4502
|
}
|
|
4270
|
-
modified.push({
|
|
4503
|
+
modified.push({
|
|
4504
|
+
path: change.path,
|
|
4505
|
+
oldPath: change.oldPath,
|
|
4506
|
+
status: resolveGitChangeStatus(worktreeStatus, change.oldPath)
|
|
4507
|
+
});
|
|
4508
|
+
}
|
|
4509
|
+
function resolveGitChangeStatus(code, oldPath) {
|
|
4510
|
+
switch (code) {
|
|
4511
|
+
case "A":
|
|
4512
|
+
return "added";
|
|
4513
|
+
case "D":
|
|
4514
|
+
return "deleted";
|
|
4515
|
+
case "R":
|
|
4516
|
+
return "renamed";
|
|
4517
|
+
case "C":
|
|
4518
|
+
return oldPath ? "renamed" : "added";
|
|
4519
|
+
case "U":
|
|
4520
|
+
return "modified";
|
|
4521
|
+
case "M":
|
|
4522
|
+
case "T":
|
|
4523
|
+
default:
|
|
4524
|
+
return oldPath ? "renamed" : "modified";
|
|
4525
|
+
}
|
|
4271
4526
|
}
|
|
4272
4527
|
var init_status_parser = __esm({
|
|
4273
4528
|
"packages/server/src/git/status-parser.ts"() {
|
|
@@ -4337,6 +4592,40 @@ async function getGitStatus(cwd) {
|
|
|
4337
4592
|
headSubject: headSubjectOutput.trim()
|
|
4338
4593
|
};
|
|
4339
4594
|
}
|
|
4595
|
+
async function getGitHistory(cwd, limit = 5) {
|
|
4596
|
+
try {
|
|
4597
|
+
const { stdout } = await runGit(cwd, [
|
|
4598
|
+
"log",
|
|
4599
|
+
`--max-count=${Math.max(1, limit)}`,
|
|
4600
|
+
"--format=%H%x1f%h%x1f%s%x1f%an%x1f%at%x1e"
|
|
4601
|
+
]);
|
|
4602
|
+
return stdout.split("").map((record) => record.trim()).filter((record) => record.length > 0).map((record) => {
|
|
4603
|
+
const [sha = "", shortSha = "", subject = "", authorName = "", authoredAt = "0"] = record.split("");
|
|
4604
|
+
return {
|
|
4605
|
+
sha,
|
|
4606
|
+
shortSha,
|
|
4607
|
+
subject,
|
|
4608
|
+
authorName,
|
|
4609
|
+
authoredAt: Number.parseInt(authoredAt, 10) * 1e3
|
|
4610
|
+
};
|
|
4611
|
+
}).filter((entry) => entry.sha && entry.subject);
|
|
4612
|
+
} catch (error) {
|
|
4613
|
+
if (error instanceof GitError && /does not have any commits yet/i.test(error.stderr)) {
|
|
4614
|
+
return [];
|
|
4615
|
+
}
|
|
4616
|
+
throw error;
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
async function getGitCommitDiff(cwd, sha) {
|
|
4620
|
+
const { stdout } = await runGit(cwd, [
|
|
4621
|
+
"show",
|
|
4622
|
+
"--format=medium",
|
|
4623
|
+
"--no-color",
|
|
4624
|
+
"--end-of-options",
|
|
4625
|
+
sha
|
|
4626
|
+
]);
|
|
4627
|
+
return stdout;
|
|
4628
|
+
}
|
|
4340
4629
|
async function getGitStatusSummary(cwd) {
|
|
4341
4630
|
const { stdout } = await runGit(cwd, ["status", "--short"]);
|
|
4342
4631
|
return stdout.trim();
|
|
@@ -4357,12 +4646,12 @@ async function discardChanges(cwd, paths) {
|
|
|
4357
4646
|
if (paths.length === 0) return;
|
|
4358
4647
|
const trackedPaths = [];
|
|
4359
4648
|
const untrackedPaths = [];
|
|
4360
|
-
for (const
|
|
4649
|
+
for (const path10 of paths) {
|
|
4361
4650
|
try {
|
|
4362
|
-
await runGit(cwd, ["ls-files", "--error-unmatch", "--",
|
|
4363
|
-
trackedPaths.push(
|
|
4651
|
+
await runGit(cwd, ["ls-files", "--error-unmatch", "--", path10]);
|
|
4652
|
+
trackedPaths.push(path10);
|
|
4364
4653
|
} catch {
|
|
4365
|
-
untrackedPaths.push(
|
|
4654
|
+
untrackedPaths.push(path10);
|
|
4366
4655
|
}
|
|
4367
4656
|
}
|
|
4368
4657
|
if (trackedPaths.length > 0) {
|
|
@@ -4401,6 +4690,7 @@ async function runGitPush(cwd, options) {
|
|
|
4401
4690
|
if (!remote) {
|
|
4402
4691
|
remote = await getPreferredRemote(cwd) ?? void 0;
|
|
4403
4692
|
}
|
|
4693
|
+
const summaryBranch = branch ?? await getCurrentBranchName(cwd);
|
|
4404
4694
|
if (remote && branch) {
|
|
4405
4695
|
args.push(remote, `HEAD:${branch}`);
|
|
4406
4696
|
} else if (remote) {
|
|
@@ -4418,8 +4708,13 @@ async function runGitPush(cwd, options) {
|
|
|
4418
4708
|
if (options?.auth) {
|
|
4419
4709
|
await persistGitHttpCredentials(cwd, options.auth, remoteMetadata);
|
|
4420
4710
|
}
|
|
4421
|
-
|
|
4422
|
-
|
|
4711
|
+
return {
|
|
4712
|
+
success: true,
|
|
4713
|
+
message: "Push completed successfully",
|
|
4714
|
+
remote,
|
|
4715
|
+
branch: summaryBranch,
|
|
4716
|
+
updated: !isPushUpToDate(stdout, stderr)
|
|
4717
|
+
};
|
|
4423
4718
|
} catch (error) {
|
|
4424
4719
|
throw normalizeGitAuthFailure(error, {
|
|
4425
4720
|
operation: "push",
|
|
@@ -4443,6 +4738,7 @@ async function runGitPull(cwd, options) {
|
|
|
4443
4738
|
if (!remote && branch) {
|
|
4444
4739
|
remote = await getPreferredRemote(cwd) ?? "origin";
|
|
4445
4740
|
}
|
|
4741
|
+
const summaryBranch = branch ?? await getCurrentBranchName(cwd);
|
|
4446
4742
|
if (remote && branch) {
|
|
4447
4743
|
args.push(remote, branch);
|
|
4448
4744
|
}
|
|
@@ -4472,8 +4768,14 @@ async function runGitPull(cwd, options) {
|
|
|
4472
4768
|
}
|
|
4473
4769
|
}
|
|
4474
4770
|
}
|
|
4475
|
-
|
|
4476
|
-
|
|
4771
|
+
return {
|
|
4772
|
+
success: true,
|
|
4773
|
+
message: "Pull completed successfully",
|
|
4774
|
+
remote,
|
|
4775
|
+
branch: summaryBranch,
|
|
4776
|
+
updated: !isPullUpToDate(stdout, stderr),
|
|
4777
|
+
updatedFiles
|
|
4778
|
+
};
|
|
4477
4779
|
} catch (error) {
|
|
4478
4780
|
throw normalizeGitAuthFailure(error, {
|
|
4479
4781
|
operation: "pull",
|
|
@@ -4485,8 +4787,63 @@ async function runGitPull(cwd, options) {
|
|
|
4485
4787
|
await authExecution.cleanup();
|
|
4486
4788
|
}
|
|
4487
4789
|
}
|
|
4790
|
+
async function runGitFetch(cwd, options) {
|
|
4791
|
+
const args = ["fetch"];
|
|
4792
|
+
const remote = options?.remote;
|
|
4793
|
+
const metadataRemote = remote ?? await getPreferredRemote(cwd) ?? void 0;
|
|
4794
|
+
const prune = options?.prune ?? true;
|
|
4795
|
+
if (remote) {
|
|
4796
|
+
args.push(remote);
|
|
4797
|
+
} else {
|
|
4798
|
+
args.push("--all");
|
|
4799
|
+
}
|
|
4800
|
+
if (prune) {
|
|
4801
|
+
args.push("--prune");
|
|
4802
|
+
}
|
|
4803
|
+
const remoteUrl = metadataRemote ? await getRemoteUrl(cwd, metadataRemote) : null;
|
|
4804
|
+
const remoteMetadata = parseRemoteUrlMetadata(remoteUrl ?? void 0);
|
|
4805
|
+
const authExecution = await prepareGitAuthExecution(options?.auth, remoteMetadata);
|
|
4806
|
+
try {
|
|
4807
|
+
const { stdout, stderr } = await runGit(cwd, args, {
|
|
4808
|
+
timeoutMs: options?.timeoutMs ?? GIT_NETWORK_TIMEOUT_MS,
|
|
4809
|
+
env: authExecution.env,
|
|
4810
|
+
config: authExecution.config
|
|
4811
|
+
});
|
|
4812
|
+
if (options?.auth) {
|
|
4813
|
+
await persistGitHttpCredentials(cwd, options.auth, remoteMetadata);
|
|
4814
|
+
}
|
|
4815
|
+
const message = stdout || stderr || "Fetch completed successfully";
|
|
4816
|
+
return {
|
|
4817
|
+
success: true,
|
|
4818
|
+
message,
|
|
4819
|
+
updatedRefs: parseFetchUpdatedRefs(stderr)
|
|
4820
|
+
};
|
|
4821
|
+
} catch (error) {
|
|
4822
|
+
throw normalizeGitAuthFailure(error, {
|
|
4823
|
+
operation: "fetch",
|
|
4824
|
+
remote: metadataRemote,
|
|
4825
|
+
remoteUrl: remoteMetadata.sanitizedUrl ?? remoteUrl ?? void 0,
|
|
4826
|
+
attemptedCredentialAuth: Boolean(options?.auth)
|
|
4827
|
+
});
|
|
4828
|
+
} finally {
|
|
4829
|
+
await authExecution.cleanup();
|
|
4830
|
+
}
|
|
4831
|
+
}
|
|
4832
|
+
function parseFetchUpdatedRefs(stderr) {
|
|
4833
|
+
const refs = [];
|
|
4834
|
+
for (const rawLine of stderr.split("\n")) {
|
|
4835
|
+
const line = rawLine.trimEnd();
|
|
4836
|
+
const arrowIndex = line.indexOf(" -> ");
|
|
4837
|
+
if (arrowIndex < 0) continue;
|
|
4838
|
+
const target = line.slice(arrowIndex + 4).trim();
|
|
4839
|
+
if (!target) continue;
|
|
4840
|
+
refs.push(target);
|
|
4841
|
+
}
|
|
4842
|
+
return refs;
|
|
4843
|
+
}
|
|
4488
4844
|
async function runGitCheckout(cwd, ref, options) {
|
|
4489
4845
|
const args = ["checkout"];
|
|
4846
|
+
const formatCheckoutError = (error, fallbackMessage) => error instanceof GitError ? error.stderr.trim() || error.message || fallbackMessage : fallbackMessage;
|
|
4490
4847
|
let isRemoteRef = false;
|
|
4491
4848
|
try {
|
|
4492
4849
|
const { stdout: remoteList } = await runGit(cwd, ["remote"]);
|
|
@@ -4498,15 +4855,22 @@ async function runGitCheckout(cwd, ref, options) {
|
|
|
4498
4855
|
if (isRemoteRef && !options?.createBranch) {
|
|
4499
4856
|
const remoteSeparatorIndex = ref.indexOf("/");
|
|
4500
4857
|
const branchName = remoteSeparatorIndex >= 0 ? ref.slice(remoteSeparatorIndex + 1) : ref;
|
|
4501
|
-
|
|
4858
|
+
try {
|
|
4859
|
+
await runGit(cwd, ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`]);
|
|
4860
|
+
const { stdout, stderr } = await runGit(cwd, ["checkout", branchName]);
|
|
4861
|
+
const message = stdout || stderr || `Checkout to ${branchName} completed`;
|
|
4862
|
+
return { success: true, message, branch: branchName };
|
|
4863
|
+
} catch {
|
|
4864
|
+
args.push("-b", branchName, ref);
|
|
4865
|
+
}
|
|
4502
4866
|
try {
|
|
4503
4867
|
const { stdout, stderr } = await runGit(cwd, args);
|
|
4504
4868
|
const message = stdout || stderr || `Checkout to ${ref} completed`;
|
|
4505
4869
|
return { success: true, message, branch: branchName };
|
|
4506
|
-
} catch {
|
|
4870
|
+
} catch (error) {
|
|
4507
4871
|
return {
|
|
4508
4872
|
success: false,
|
|
4509
|
-
message: `Failed to checkout remote branch '${ref}'`
|
|
4873
|
+
message: formatCheckoutError(error, `Failed to checkout remote branch '${ref}'`)
|
|
4510
4874
|
};
|
|
4511
4875
|
}
|
|
4512
4876
|
} else {
|
|
@@ -4520,10 +4884,10 @@ async function runGitCheckout(cwd, ref, options) {
|
|
|
4520
4884
|
const branch = branchMatch?.[1] ?? ref;
|
|
4521
4885
|
const message = stdout || stderr || `Checkout to ${ref} completed`;
|
|
4522
4886
|
return { success: true, message, branch };
|
|
4523
|
-
} catch {
|
|
4887
|
+
} catch (error) {
|
|
4524
4888
|
return {
|
|
4525
4889
|
success: false,
|
|
4526
|
-
message: `Failed to checkout '${ref}'`
|
|
4890
|
+
message: formatCheckoutError(error, `Failed to checkout '${ref}'`)
|
|
4527
4891
|
};
|
|
4528
4892
|
}
|
|
4529
4893
|
}
|
|
@@ -4538,23 +4902,42 @@ async function runGitCreateBranch(cwd, branchName, options) {
|
|
|
4538
4902
|
}
|
|
4539
4903
|
async function runGitListBranches(cwd) {
|
|
4540
4904
|
const { stdout: localOutput } = await runGit(cwd, ["branch", "--list"]);
|
|
4905
|
+
const { stdout: localVerboseOutput } = await runGit(cwd, ["branch", "--list", "-vv"]);
|
|
4541
4906
|
const { stdout: remoteOutput } = await runGit(cwd, ["branch", "-r"]);
|
|
4542
4907
|
const branches = [];
|
|
4543
4908
|
let current = "";
|
|
4909
|
+
const linkedWorktreePathsByBranch = /* @__PURE__ */ new Map();
|
|
4910
|
+
const localVerboseLines = localVerboseOutput.split("\n").filter((line) => line.trim());
|
|
4911
|
+
for (const line of localVerboseLines) {
|
|
4912
|
+
const normalizedLine = line.replace(/^[*+ ]\s+/, "");
|
|
4913
|
+
const branchMatch = normalizedLine.match(/^([^\s]+)\s+/);
|
|
4914
|
+
const worktreeMatch = line.match(/\((.+?)\)\s/);
|
|
4915
|
+
if (!branchMatch?.[1] || !worktreeMatch?.[1]) {
|
|
4916
|
+
continue;
|
|
4917
|
+
}
|
|
4918
|
+
const worktreePath = worktreeMatch[1];
|
|
4919
|
+
if (worktreePath.startsWith("/") || worktreePath.startsWith("~")) {
|
|
4920
|
+
linkedWorktreePathsByBranch.set(branchMatch[1], worktreePath);
|
|
4921
|
+
}
|
|
4922
|
+
}
|
|
4544
4923
|
const localLines = localOutput.split("\n").filter((line) => line.trim());
|
|
4545
4924
|
for (const line of localLines) {
|
|
4546
4925
|
const isCurrent = line.startsWith("*");
|
|
4547
|
-
const name = line.replace(
|
|
4926
|
+
const name = line.replace(/^[*+ ]\s+/, "").trim();
|
|
4548
4927
|
if (name.startsWith("(HEAD detached")) {
|
|
4549
4928
|
if (isCurrent) {
|
|
4550
4929
|
current = "";
|
|
4551
4930
|
}
|
|
4552
4931
|
continue;
|
|
4553
4932
|
}
|
|
4933
|
+
if (linkedWorktreePathsByBranch.has(name) && !isCurrent) {
|
|
4934
|
+
continue;
|
|
4935
|
+
}
|
|
4554
4936
|
branches.push({
|
|
4555
4937
|
name,
|
|
4556
4938
|
isRemote: false,
|
|
4557
|
-
isCurrent
|
|
4939
|
+
isCurrent,
|
|
4940
|
+
linkedWorktreePath: linkedWorktreePathsByBranch.get(name)
|
|
4558
4941
|
});
|
|
4559
4942
|
if (isCurrent) {
|
|
4560
4943
|
current = name;
|
|
@@ -4602,6 +4985,23 @@ async function resolveRemoteBranchTarget(cwd, mode) {
|
|
|
4602
4985
|
return null;
|
|
4603
4986
|
}
|
|
4604
4987
|
}
|
|
4988
|
+
async function getCurrentBranchName(cwd) {
|
|
4989
|
+
try {
|
|
4990
|
+
const { stdout } = await runGit(cwd, ["branch", "--show-current"]);
|
|
4991
|
+
const branch = stdout.trim();
|
|
4992
|
+
return branch || void 0;
|
|
4993
|
+
} catch {
|
|
4994
|
+
return void 0;
|
|
4995
|
+
}
|
|
4996
|
+
}
|
|
4997
|
+
function isPushUpToDate(stdout, stderr) {
|
|
4998
|
+
return /Everything up-to-date/i.test(`${stdout}
|
|
4999
|
+
${stderr}`);
|
|
5000
|
+
}
|
|
5001
|
+
function isPullUpToDate(stdout, stderr) {
|
|
5002
|
+
return /Already up[ -]to[ -]date\.?/i.test(`${stdout}
|
|
5003
|
+
${stderr}`);
|
|
5004
|
+
}
|
|
4605
5005
|
async function getPreferredRemote(cwd) {
|
|
4606
5006
|
try {
|
|
4607
5007
|
const { stdout } = await runGit(cwd, ["remote"]);
|
|
@@ -4986,7 +5386,7 @@ var init_context_builder = __esm({
|
|
|
4986
5386
|
});
|
|
4987
5387
|
|
|
4988
5388
|
// packages/server/src/terminal/pty-host.ts
|
|
4989
|
-
import { chmodSync, existsSync as existsSync4, statSync } from "node:fs";
|
|
5389
|
+
import { chmodSync as chmodSync2, existsSync as existsSync4, statSync } from "node:fs";
|
|
4990
5390
|
import { createRequire } from "node:module";
|
|
4991
5391
|
import path6 from "node:path";
|
|
4992
5392
|
function ensureNodePtySpawnHelperExecutable(deps = {}) {
|
|
@@ -4998,7 +5398,7 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
|
|
|
4998
5398
|
const resolve4 = deps.resolve ?? ((id) => require2.resolve(id));
|
|
4999
5399
|
const fileExists = deps.existsSync ?? existsSync4;
|
|
5000
5400
|
const stat7 = deps.statSync ?? statSync;
|
|
5001
|
-
const chmod = deps.chmodSync ??
|
|
5401
|
+
const chmod = deps.chmodSync ?? chmodSync2;
|
|
5002
5402
|
let packageJsonPath;
|
|
5003
5403
|
try {
|
|
5004
5404
|
packageJsonPath = resolve4(NODE_PTY_PKG);
|
|
@@ -7033,14 +7433,14 @@ var init_manager3 = __esm({
|
|
|
7033
7433
|
// packages/server/src/workspace/validator.ts
|
|
7034
7434
|
import { constants } from "fs";
|
|
7035
7435
|
import { access, stat as stat5 } from "fs/promises";
|
|
7036
|
-
async function validatePath(
|
|
7436
|
+
async function validatePath(path10) {
|
|
7037
7437
|
try {
|
|
7038
|
-
const stats = await stat5(
|
|
7438
|
+
const stats = await stat5(path10);
|
|
7039
7439
|
if (!stats.isDirectory()) {
|
|
7040
7440
|
return { valid: false, error: "Path is not a directory" };
|
|
7041
7441
|
}
|
|
7042
|
-
await access(
|
|
7043
|
-
await access(
|
|
7442
|
+
await access(path10, constants.R_OK);
|
|
7443
|
+
await access(path10, constants.W_OK);
|
|
7044
7444
|
return { valid: true };
|
|
7045
7445
|
} catch (error) {
|
|
7046
7446
|
if (error.code === "ENOENT") {
|
|
@@ -7057,8 +7457,8 @@ var init_validator = __esm({
|
|
|
7057
7457
|
"packages/server/src/workspace/validator.ts"() {
|
|
7058
7458
|
"use strict";
|
|
7059
7459
|
WorkspaceValidator = class {
|
|
7060
|
-
async validate(
|
|
7061
|
-
const result = await validatePath(
|
|
7460
|
+
async validate(path10) {
|
|
7461
|
+
const result = await validatePath(path10);
|
|
7062
7462
|
if (!result.valid) {
|
|
7063
7463
|
throw new Error(`Invalid workspace path: ${result.error}`);
|
|
7064
7464
|
}
|
|
@@ -7070,12 +7470,12 @@ var init_validator = __esm({
|
|
|
7070
7470
|
// packages/server/src/fs/gitignore.ts
|
|
7071
7471
|
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
7072
7472
|
import ignore from "ignore";
|
|
7073
|
-
import { join as join4, relative } from "path";
|
|
7074
|
-
function normalizePath(
|
|
7075
|
-
return
|
|
7473
|
+
import { join as join4, relative as relative3 } from "path";
|
|
7474
|
+
function normalizePath(path10) {
|
|
7475
|
+
return path10.replace(/\\/g, "/");
|
|
7076
7476
|
}
|
|
7077
|
-
function relativeToRoot(rootPath,
|
|
7078
|
-
return normalizePath(
|
|
7477
|
+
function relativeToRoot(rootPath, path10) {
|
|
7478
|
+
return normalizePath(relative3(rootPath, path10));
|
|
7079
7479
|
}
|
|
7080
7480
|
function isDefaultTreeIgnored(name) {
|
|
7081
7481
|
return name.startsWith(".") || name === "node_modules" || name === ".git";
|
|
@@ -7083,11 +7483,11 @@ function isDefaultTreeIgnored(name) {
|
|
|
7083
7483
|
function isAlwaysTreeIgnored(name) {
|
|
7084
7484
|
return name === "node_modules" || name === ".git";
|
|
7085
7485
|
}
|
|
7086
|
-
function isIgnoredByGitignore(ig,
|
|
7087
|
-
if (!
|
|
7486
|
+
function isIgnoredByGitignore(ig, path10) {
|
|
7487
|
+
if (!path10 || path10.startsWith("..")) {
|
|
7088
7488
|
return false;
|
|
7089
7489
|
}
|
|
7090
|
-
return ig.ignores(
|
|
7490
|
+
return ig.ignores(path10) || ig.ignores(`${path10}/`);
|
|
7091
7491
|
}
|
|
7092
7492
|
function createGitignoreFilter(rootPath, dirPath) {
|
|
7093
7493
|
const gitignorePath = join4(rootPath, ".gitignore");
|
|
@@ -7107,16 +7507,16 @@ function createGitignoreFilter(rootPath, dirPath) {
|
|
|
7107
7507
|
function createWatcherIgnoreFilter(rootPath) {
|
|
7108
7508
|
const gitignorePath = join4(rootPath, ".gitignore");
|
|
7109
7509
|
if (!existsSync5(gitignorePath)) {
|
|
7110
|
-
return (
|
|
7510
|
+
return (path10) => DEFAULT_WATCHER_IGNORED_PATTERNS.some((p) => p.test(normalizePath(path10)));
|
|
7111
7511
|
}
|
|
7112
7512
|
const gitignoreContent = readFileSync5(gitignorePath, "utf-8");
|
|
7113
7513
|
const ig = ignore().add(gitignoreContent);
|
|
7114
|
-
return (
|
|
7115
|
-
const normalizedPath = normalizePath(
|
|
7514
|
+
return (path10) => {
|
|
7515
|
+
const normalizedPath = normalizePath(path10);
|
|
7116
7516
|
if (DEFAULT_WATCHER_IGNORED_PATTERNS.some((p) => p.test(normalizedPath))) {
|
|
7117
7517
|
return true;
|
|
7118
7518
|
}
|
|
7119
|
-
const relativePath = relativeToRoot(rootPath,
|
|
7519
|
+
const relativePath = relativeToRoot(rootPath, path10);
|
|
7120
7520
|
return isIgnoredByGitignore(ig, relativePath);
|
|
7121
7521
|
};
|
|
7122
7522
|
}
|
|
@@ -7236,6 +7636,11 @@ var init_manager4 = __esm({
|
|
|
7236
7636
|
new WorkspaceWatcher(workspaceId, rootPath, this.deps.broadcaster)
|
|
7237
7637
|
);
|
|
7238
7638
|
}
|
|
7639
|
+
hydrateWatchers() {
|
|
7640
|
+
for (const workspace of this.list()) {
|
|
7641
|
+
this.startWatcher(workspace.id, workspace.path);
|
|
7642
|
+
}
|
|
7643
|
+
}
|
|
7239
7644
|
updateUiState(workspaceId, uiState) {
|
|
7240
7645
|
const workspace = this.get(workspaceId);
|
|
7241
7646
|
if (!workspace) {
|
|
@@ -7270,6 +7675,7 @@ var init_manager4 = __esm({
|
|
|
7270
7675
|
workspaceId: existing.id,
|
|
7271
7676
|
patch: { lastActiveAt: Date.now() }
|
|
7272
7677
|
});
|
|
7678
|
+
this.deps.autoFetch?.triggerOpenTimeFetch(existing.id);
|
|
7273
7679
|
return existing;
|
|
7274
7680
|
}
|
|
7275
7681
|
const workspace = {
|
|
@@ -7307,6 +7713,7 @@ var init_manager4 = __esm({
|
|
|
7307
7713
|
patch: workspace
|
|
7308
7714
|
});
|
|
7309
7715
|
this.startWatcher(workspace.id, workspace.path);
|
|
7716
|
+
this.deps.autoFetch?.triggerOpenTimeFetch(workspace.id);
|
|
7310
7717
|
return workspace;
|
|
7311
7718
|
}
|
|
7312
7719
|
/**
|
|
@@ -7391,12 +7798,12 @@ var init_manager4 = __esm({
|
|
|
7391
7798
|
* @param path - Workspace path
|
|
7392
7799
|
* @returns Workspace or undefined
|
|
7393
7800
|
*/
|
|
7394
|
-
getByPath(
|
|
7801
|
+
getByPath(path10) {
|
|
7395
7802
|
const row = this.deps.db.prepare(
|
|
7396
7803
|
`SELECT id, path, target_runtime, wsl_distro, opened_at, last_active_at, ui_state
|
|
7397
7804
|
FROM workspaces
|
|
7398
7805
|
WHERE path = ?`
|
|
7399
|
-
).get(
|
|
7806
|
+
).get(path10);
|
|
7400
7807
|
if (!row) return void 0;
|
|
7401
7808
|
return {
|
|
7402
7809
|
id: row.id,
|
|
@@ -7417,13 +7824,87 @@ var init_manager4 = __esm({
|
|
|
7417
7824
|
const now = Date.now();
|
|
7418
7825
|
this.deps.db.prepare("UPDATE workspaces SET last_active_at = ? WHERE id = ?").run(now, workspaceId);
|
|
7419
7826
|
}
|
|
7827
|
+
recordFetch(workspaceId) {
|
|
7828
|
+
this.deps.autoFetch?.recordSuccess(workspaceId);
|
|
7829
|
+
}
|
|
7420
7830
|
};
|
|
7421
7831
|
}
|
|
7422
7832
|
});
|
|
7423
7833
|
|
|
7424
|
-
// packages/server/src/ws/
|
|
7425
|
-
|
|
7426
|
-
|
|
7834
|
+
// packages/server/src/ws/dispatch.ts
|
|
7835
|
+
function registerCommand(op, schema, handler) {
|
|
7836
|
+
handlers.set(op, handler);
|
|
7837
|
+
schemas.set(op, schema);
|
|
7838
|
+
}
|
|
7839
|
+
async function dispatch(msg, ctx, clientId) {
|
|
7840
|
+
const handler = handlers.get(msg.op);
|
|
7841
|
+
if (!handler) {
|
|
7842
|
+
return {
|
|
7843
|
+
kind: "result",
|
|
7844
|
+
id: msg.id,
|
|
7845
|
+
ok: false,
|
|
7846
|
+
error: {
|
|
7847
|
+
code: "unknown_op",
|
|
7848
|
+
message: `Unknown operation: ${msg.op}`
|
|
7849
|
+
}
|
|
7850
|
+
};
|
|
7851
|
+
}
|
|
7852
|
+
try {
|
|
7853
|
+
const schema = schemas.get(msg.op);
|
|
7854
|
+
let args = msg.args;
|
|
7855
|
+
if (schema) {
|
|
7856
|
+
args = schema.parse(msg.args);
|
|
7857
|
+
}
|
|
7858
|
+
const data = await handler(args, ctx, clientId);
|
|
7859
|
+
return {
|
|
7860
|
+
kind: "result",
|
|
7861
|
+
id: msg.id,
|
|
7862
|
+
ok: true,
|
|
7863
|
+
data
|
|
7864
|
+
};
|
|
7865
|
+
} catch (error) {
|
|
7866
|
+
const normalizedError = normalizeError(error);
|
|
7867
|
+
return {
|
|
7868
|
+
kind: "result",
|
|
7869
|
+
id: msg.id,
|
|
7870
|
+
ok: false,
|
|
7871
|
+
error: normalizedError
|
|
7872
|
+
};
|
|
7873
|
+
}
|
|
7874
|
+
}
|
|
7875
|
+
function normalizeError(error) {
|
|
7876
|
+
const candidate = error;
|
|
7877
|
+
if (candidate.name === "ZodError") {
|
|
7878
|
+
return {
|
|
7879
|
+
code: "validation_error",
|
|
7880
|
+
message: "Invalid arguments",
|
|
7881
|
+
details: candidate.errors
|
|
7882
|
+
};
|
|
7883
|
+
}
|
|
7884
|
+
if (candidate.code) {
|
|
7885
|
+
return {
|
|
7886
|
+
code: candidate.code,
|
|
7887
|
+
message: candidate.message ?? String(candidate.code),
|
|
7888
|
+
details: candidate.details
|
|
7889
|
+
};
|
|
7890
|
+
}
|
|
7891
|
+
return {
|
|
7892
|
+
code: "internal_error",
|
|
7893
|
+
message: candidate.message || "An internal error occurred"
|
|
7894
|
+
};
|
|
7895
|
+
}
|
|
7896
|
+
var handlers, schemas;
|
|
7897
|
+
var init_dispatch = __esm({
|
|
7898
|
+
"packages/server/src/ws/dispatch.ts"() {
|
|
7899
|
+
"use strict";
|
|
7900
|
+
handlers = /* @__PURE__ */ new Map();
|
|
7901
|
+
schemas = /* @__PURE__ */ new Map();
|
|
7902
|
+
}
|
|
7903
|
+
});
|
|
7904
|
+
|
|
7905
|
+
// packages/server/src/ws/fencing.ts
|
|
7906
|
+
var DEFAULT_OPTIONS, FencingManager;
|
|
7907
|
+
var init_fencing = __esm({
|
|
7427
7908
|
"packages/server/src/ws/fencing.ts"() {
|
|
7428
7909
|
"use strict";
|
|
7429
7910
|
DEFAULT_OPTIONS = {
|
|
@@ -7593,127 +8074,6 @@ var init_fencing = __esm({
|
|
|
7593
8074
|
}
|
|
7594
8075
|
});
|
|
7595
8076
|
|
|
7596
|
-
// packages/server/src/ws/dispatch.ts
|
|
7597
|
-
async function debounce(key, op, windowMs) {
|
|
7598
|
-
let entry = debounceMap.get(key);
|
|
7599
|
-
if (entry) {
|
|
7600
|
-
clearTimeout(entry.timer);
|
|
7601
|
-
entry.op = op;
|
|
7602
|
-
} else {
|
|
7603
|
-
let resolve4;
|
|
7604
|
-
let reject;
|
|
7605
|
-
const promise = new Promise((res, rej) => {
|
|
7606
|
-
resolve4 = res;
|
|
7607
|
-
reject = rej;
|
|
7608
|
-
});
|
|
7609
|
-
entry = {
|
|
7610
|
-
timer: void 0,
|
|
7611
|
-
promise,
|
|
7612
|
-
resolve: resolve4,
|
|
7613
|
-
reject,
|
|
7614
|
-
op
|
|
7615
|
-
};
|
|
7616
|
-
debounceMap.set(key, entry);
|
|
7617
|
-
}
|
|
7618
|
-
entry.timer = setTimeout(async () => {
|
|
7619
|
-
debounceMap.delete(key);
|
|
7620
|
-
try {
|
|
7621
|
-
const result = await entry.op();
|
|
7622
|
-
entry.resolve(result);
|
|
7623
|
-
} catch (err) {
|
|
7624
|
-
entry.reject(err);
|
|
7625
|
-
}
|
|
7626
|
-
}, windowMs);
|
|
7627
|
-
return entry.promise;
|
|
7628
|
-
}
|
|
7629
|
-
function registerCommand(op, schema, handler) {
|
|
7630
|
-
handlers.set(op, handler);
|
|
7631
|
-
schemas.set(op, schema);
|
|
7632
|
-
}
|
|
7633
|
-
async function dispatch(msg, ctx, clientId) {
|
|
7634
|
-
const handler = handlers.get(msg.op);
|
|
7635
|
-
if (!handler) {
|
|
7636
|
-
return {
|
|
7637
|
-
kind: "result",
|
|
7638
|
-
id: msg.id,
|
|
7639
|
-
ok: false,
|
|
7640
|
-
error: {
|
|
7641
|
-
code: "unknown_op",
|
|
7642
|
-
message: `Unknown operation: ${msg.op}`
|
|
7643
|
-
}
|
|
7644
|
-
};
|
|
7645
|
-
}
|
|
7646
|
-
try {
|
|
7647
|
-
const schema = schemas.get(msg.op);
|
|
7648
|
-
let args = msg.args;
|
|
7649
|
-
if (schema) {
|
|
7650
|
-
args = schema.parse(msg.args);
|
|
7651
|
-
}
|
|
7652
|
-
const data = await executeWithDebounce(msg.op, args, ctx, clientId);
|
|
7653
|
-
return {
|
|
7654
|
-
kind: "result",
|
|
7655
|
-
id: msg.id,
|
|
7656
|
-
ok: true,
|
|
7657
|
-
data
|
|
7658
|
-
};
|
|
7659
|
-
} catch (error) {
|
|
7660
|
-
const normalizedError = normalizeError(error);
|
|
7661
|
-
return {
|
|
7662
|
-
kind: "result",
|
|
7663
|
-
id: msg.id,
|
|
7664
|
-
ok: false,
|
|
7665
|
-
error: normalizedError
|
|
7666
|
-
};
|
|
7667
|
-
}
|
|
7668
|
-
}
|
|
7669
|
-
async function executeWithDebounce(op, args, ctx, clientId) {
|
|
7670
|
-
const handler = handlers.get(op);
|
|
7671
|
-
if (op === "git.status") {
|
|
7672
|
-
const workspaceId = getWorkspaceId(args);
|
|
7673
|
-
const key = workspaceId ? `git.status:${workspaceId}` : op;
|
|
7674
|
-
return debounce(key, () => handler(args, ctx, clientId), DEBOUNCE_GIT_STATUS_MS);
|
|
7675
|
-
}
|
|
7676
|
-
return handler(args, ctx, clientId);
|
|
7677
|
-
}
|
|
7678
|
-
function getWorkspaceId(args) {
|
|
7679
|
-
if (typeof args !== "object" || args === null || !("workspaceId" in args)) {
|
|
7680
|
-
return void 0;
|
|
7681
|
-
}
|
|
7682
|
-
const workspaceId = args.workspaceId;
|
|
7683
|
-
return typeof workspaceId === "string" ? workspaceId : void 0;
|
|
7684
|
-
}
|
|
7685
|
-
function normalizeError(error) {
|
|
7686
|
-
const candidate = error;
|
|
7687
|
-
if (candidate.name === "ZodError") {
|
|
7688
|
-
return {
|
|
7689
|
-
code: "validation_error",
|
|
7690
|
-
message: "Invalid arguments",
|
|
7691
|
-
details: candidate.errors
|
|
7692
|
-
};
|
|
7693
|
-
}
|
|
7694
|
-
if (candidate.code) {
|
|
7695
|
-
return {
|
|
7696
|
-
code: candidate.code,
|
|
7697
|
-
message: candidate.message ?? String(candidate.code),
|
|
7698
|
-
details: candidate.details
|
|
7699
|
-
};
|
|
7700
|
-
}
|
|
7701
|
-
return {
|
|
7702
|
-
code: "internal_error",
|
|
7703
|
-
message: candidate.message || "An internal error occurred"
|
|
7704
|
-
};
|
|
7705
|
-
}
|
|
7706
|
-
var handlers, schemas, debounceMap, DEBOUNCE_GIT_STATUS_MS;
|
|
7707
|
-
var init_dispatch = __esm({
|
|
7708
|
-
"packages/server/src/ws/dispatch.ts"() {
|
|
7709
|
-
"use strict";
|
|
7710
|
-
handlers = /* @__PURE__ */ new Map();
|
|
7711
|
-
schemas = /* @__PURE__ */ new Map();
|
|
7712
|
-
debounceMap = /* @__PURE__ */ new Map();
|
|
7713
|
-
DEBOUNCE_GIT_STATUS_MS = 500;
|
|
7714
|
-
}
|
|
7715
|
-
});
|
|
7716
|
-
|
|
7717
8077
|
// packages/server/src/commands/terminal.ts
|
|
7718
8078
|
import { basename } from "node:path";
|
|
7719
8079
|
import { z as z5 } from "zod";
|
|
@@ -8562,6 +8922,7 @@ var init_hub = __esm({
|
|
|
8562
8922
|
handleClose(client) {
|
|
8563
8923
|
this.clients.delete(client.id);
|
|
8564
8924
|
this.discardPendingBinaryWaiters(client.id);
|
|
8925
|
+
this.deps.commandContext?.autoFetch.unregisterViewer(client.id);
|
|
8565
8926
|
}
|
|
8566
8927
|
/**
|
|
8567
8928
|
* Takeover: Force close existing writer and accept new one
|
|
@@ -8754,7 +9115,7 @@ var init_hub = __esm({
|
|
|
8754
9115
|
|
|
8755
9116
|
// packages/server/src/commands/workspace.ts
|
|
8756
9117
|
import { readdir as readdir2 } from "node:fs/promises";
|
|
8757
|
-
import { homedir as
|
|
9118
|
+
import { homedir as homedir2 } from "node:os";
|
|
8758
9119
|
import { join as join5 } from "node:path";
|
|
8759
9120
|
import { z as z6 } from "zod";
|
|
8760
9121
|
var init_workspace = __esm({
|
|
@@ -8770,7 +9131,7 @@ var init_workspace = __esm({
|
|
|
8770
9131
|
path: z6.string().optional()
|
|
8771
9132
|
}),
|
|
8772
9133
|
async (args) => {
|
|
8773
|
-
const basePath = args.path ||
|
|
9134
|
+
const basePath = args.path || homedir2();
|
|
8774
9135
|
const entries = await readdir2(basePath, { withFileTypes: true });
|
|
8775
9136
|
const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
|
|
8776
9137
|
name: entry.name,
|
|
@@ -8843,6 +9204,35 @@ var init_workspace = __esm({
|
|
|
8843
9204
|
}
|
|
8844
9205
|
});
|
|
8845
9206
|
|
|
9207
|
+
// packages/server/src/commands/workspace-activity.ts
|
|
9208
|
+
import { z as z7 } from "zod";
|
|
9209
|
+
var init_workspace_activity = __esm({
|
|
9210
|
+
"packages/server/src/commands/workspace-activity.ts"() {
|
|
9211
|
+
"use strict";
|
|
9212
|
+
init_dispatch();
|
|
9213
|
+
registerCommand(
|
|
9214
|
+
"workspace.activate",
|
|
9215
|
+
z7.object({
|
|
9216
|
+
workspaceId: z7.string()
|
|
9217
|
+
}),
|
|
9218
|
+
async (args, ctx, clientId) => {
|
|
9219
|
+
if (!clientId) {
|
|
9220
|
+
return {};
|
|
9221
|
+
}
|
|
9222
|
+
ctx.autoFetch.registerViewer(clientId, args.workspaceId);
|
|
9223
|
+
return {};
|
|
9224
|
+
}
|
|
9225
|
+
);
|
|
9226
|
+
registerCommand("workspace.deactivate", z7.object({}), async (_args, ctx, clientId) => {
|
|
9227
|
+
if (!clientId) {
|
|
9228
|
+
return {};
|
|
9229
|
+
}
|
|
9230
|
+
ctx.autoFetch.unregisterViewer(clientId);
|
|
9231
|
+
return {};
|
|
9232
|
+
});
|
|
9233
|
+
}
|
|
9234
|
+
});
|
|
9235
|
+
|
|
8846
9236
|
// packages/server/src/provider-runtime/runtime-status.ts
|
|
8847
9237
|
function canAutoInstall(provider, platform, missingCommands, missingPrerequisites, availableCommands) {
|
|
8848
9238
|
const strategies = provider.install.strategies[platform] ?? [];
|
|
@@ -8937,7 +9327,7 @@ var init_runtime_status = __esm({
|
|
|
8937
9327
|
});
|
|
8938
9328
|
|
|
8939
9329
|
// packages/server/src/commands/session.ts
|
|
8940
|
-
import { z as
|
|
9330
|
+
import { z as z8 } from "zod";
|
|
8941
9331
|
function getProviderFromRegistry(providerId, registry) {
|
|
8942
9332
|
return registry.find((provider) => provider.id === providerId);
|
|
8943
9333
|
}
|
|
@@ -8948,8 +9338,8 @@ var init_session = __esm({
|
|
|
8948
9338
|
init_dispatch();
|
|
8949
9339
|
registerCommand(
|
|
8950
9340
|
"session.list",
|
|
8951
|
-
|
|
8952
|
-
workspaceId:
|
|
9341
|
+
z8.object({
|
|
9342
|
+
workspaceId: z8.string()
|
|
8953
9343
|
}),
|
|
8954
9344
|
async (args, ctx) => {
|
|
8955
9345
|
return ctx.sessionMgr.getForWorkspace(args.workspaceId);
|
|
@@ -8957,10 +9347,10 @@ var init_session = __esm({
|
|
|
8957
9347
|
);
|
|
8958
9348
|
registerCommand(
|
|
8959
9349
|
"session.create",
|
|
8960
|
-
|
|
8961
|
-
workspaceId:
|
|
8962
|
-
providerId:
|
|
8963
|
-
draft:
|
|
9350
|
+
z8.object({
|
|
9351
|
+
workspaceId: z8.string(),
|
|
9352
|
+
providerId: z8.string(),
|
|
9353
|
+
draft: z8.string().optional()
|
|
8964
9354
|
}),
|
|
8965
9355
|
async (args, ctx) => {
|
|
8966
9356
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -8994,8 +9384,8 @@ var init_session = __esm({
|
|
|
8994
9384
|
);
|
|
8995
9385
|
registerCommand(
|
|
8996
9386
|
"session.stop",
|
|
8997
|
-
|
|
8998
|
-
sessionId:
|
|
9387
|
+
z8.object({
|
|
9388
|
+
sessionId: z8.string()
|
|
8999
9389
|
}),
|
|
9000
9390
|
async (args, ctx) => {
|
|
9001
9391
|
await ctx.sessionMgr.stop(args.sessionId);
|
|
@@ -9003,8 +9393,8 @@ var init_session = __esm({
|
|
|
9003
9393
|
);
|
|
9004
9394
|
registerCommand(
|
|
9005
9395
|
"session.remove",
|
|
9006
|
-
|
|
9007
|
-
sessionId:
|
|
9396
|
+
z8.object({
|
|
9397
|
+
sessionId: z8.string()
|
|
9008
9398
|
}),
|
|
9009
9399
|
async (args, ctx) => {
|
|
9010
9400
|
const session = ctx.sessionMgr.get(args.sessionId);
|
|
@@ -9022,7 +9412,7 @@ var init_session = __esm({
|
|
|
9022
9412
|
|
|
9023
9413
|
// packages/server/src/fs/tree.ts
|
|
9024
9414
|
import { readdir as readdir3, stat as stat6 } from "fs/promises";
|
|
9025
|
-
import { join as join6, relative as
|
|
9415
|
+
import { join as join6, relative as relative4 } from "path";
|
|
9026
9416
|
async function readTree(rootPath, subdir) {
|
|
9027
9417
|
const targetPath = subdir ? join6(rootPath, subdir) : rootPath;
|
|
9028
9418
|
const filter = createGitignoreFilter(rootPath, targetPath);
|
|
@@ -9033,7 +9423,7 @@ async function readTree(rootPath, subdir) {
|
|
|
9033
9423
|
continue;
|
|
9034
9424
|
}
|
|
9035
9425
|
const fullPath = join6(targetPath, entry.name);
|
|
9036
|
-
const relPath =
|
|
9426
|
+
const relPath = relative4(rootPath, fullPath);
|
|
9037
9427
|
if (entry.isDirectory()) {
|
|
9038
9428
|
nodes.push({
|
|
9039
9429
|
name: entry.name,
|
|
@@ -9077,7 +9467,7 @@ async function searchFiles(rootPath, query, limit = 10) {
|
|
|
9077
9467
|
filteredEntries.sort((a, b) => a.name.localeCompare(b.name));
|
|
9078
9468
|
for (const entry of filteredEntries) {
|
|
9079
9469
|
const fullPath = join6(dirPath, entry.name);
|
|
9080
|
-
const relPath =
|
|
9470
|
+
const relPath = relative4(rootPath, fullPath);
|
|
9081
9471
|
if (entry.isDirectory()) {
|
|
9082
9472
|
await walk(fullPath);
|
|
9083
9473
|
continue;
|
|
@@ -9169,7 +9559,7 @@ var init_tree = __esm({
|
|
|
9169
9559
|
});
|
|
9170
9560
|
|
|
9171
9561
|
// packages/server/src/commands/file.ts
|
|
9172
|
-
import { z as
|
|
9562
|
+
import { z as z9 } from "zod";
|
|
9173
9563
|
var init_file = __esm({
|
|
9174
9564
|
"packages/server/src/commands/file.ts"() {
|
|
9175
9565
|
"use strict";
|
|
@@ -9178,9 +9568,9 @@ var init_file = __esm({
|
|
|
9178
9568
|
init_dispatch();
|
|
9179
9569
|
registerCommand(
|
|
9180
9570
|
"file.readTree",
|
|
9181
|
-
|
|
9182
|
-
workspaceId:
|
|
9183
|
-
subPath:
|
|
9571
|
+
z9.object({
|
|
9572
|
+
workspaceId: z9.string(),
|
|
9573
|
+
subPath: z9.string().optional()
|
|
9184
9574
|
}),
|
|
9185
9575
|
async (args, ctx) => {
|
|
9186
9576
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9192,10 +9582,10 @@ var init_file = __esm({
|
|
|
9192
9582
|
);
|
|
9193
9583
|
registerCommand(
|
|
9194
9584
|
"file.search",
|
|
9195
|
-
|
|
9196
|
-
workspaceId:
|
|
9197
|
-
query:
|
|
9198
|
-
limit:
|
|
9585
|
+
z9.object({
|
|
9586
|
+
workspaceId: z9.string(),
|
|
9587
|
+
query: z9.string(),
|
|
9588
|
+
limit: z9.number().int().positive().max(50).optional()
|
|
9199
9589
|
}),
|
|
9200
9590
|
async (args, ctx) => {
|
|
9201
9591
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9207,9 +9597,9 @@ var init_file = __esm({
|
|
|
9207
9597
|
);
|
|
9208
9598
|
registerCommand(
|
|
9209
9599
|
"file.read",
|
|
9210
|
-
|
|
9211
|
-
workspaceId:
|
|
9212
|
-
path:
|
|
9600
|
+
z9.object({
|
|
9601
|
+
workspaceId: z9.string(),
|
|
9602
|
+
path: z9.string()
|
|
9213
9603
|
}),
|
|
9214
9604
|
async (args, ctx) => {
|
|
9215
9605
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9221,9 +9611,9 @@ var init_file = __esm({
|
|
|
9221
9611
|
);
|
|
9222
9612
|
registerCommand(
|
|
9223
9613
|
"file.create",
|
|
9224
|
-
|
|
9225
|
-
workspaceId:
|
|
9226
|
-
path:
|
|
9614
|
+
z9.object({
|
|
9615
|
+
workspaceId: z9.string(),
|
|
9616
|
+
path: z9.string()
|
|
9227
9617
|
}),
|
|
9228
9618
|
async (args, ctx) => {
|
|
9229
9619
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9241,9 +9631,9 @@ var init_file = __esm({
|
|
|
9241
9631
|
);
|
|
9242
9632
|
registerCommand(
|
|
9243
9633
|
"file.mkdir",
|
|
9244
|
-
|
|
9245
|
-
workspaceId:
|
|
9246
|
-
path:
|
|
9634
|
+
z9.object({
|
|
9635
|
+
workspaceId: z9.string(),
|
|
9636
|
+
path: z9.string()
|
|
9247
9637
|
}),
|
|
9248
9638
|
async (args, ctx) => {
|
|
9249
9639
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9261,9 +9651,9 @@ var init_file = __esm({
|
|
|
9261
9651
|
);
|
|
9262
9652
|
registerCommand(
|
|
9263
9653
|
"file.delete",
|
|
9264
|
-
|
|
9265
|
-
workspaceId:
|
|
9266
|
-
path:
|
|
9654
|
+
z9.object({
|
|
9655
|
+
workspaceId: z9.string(),
|
|
9656
|
+
path: z9.string()
|
|
9267
9657
|
}),
|
|
9268
9658
|
async (args, ctx) => {
|
|
9269
9659
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9281,11 +9671,11 @@ var init_file = __esm({
|
|
|
9281
9671
|
);
|
|
9282
9672
|
registerCommand(
|
|
9283
9673
|
"file.write",
|
|
9284
|
-
|
|
9285
|
-
workspaceId:
|
|
9286
|
-
path:
|
|
9287
|
-
content:
|
|
9288
|
-
baseHash:
|
|
9674
|
+
z9.object({
|
|
9675
|
+
workspaceId: z9.string(),
|
|
9676
|
+
path: z9.string(),
|
|
9677
|
+
content: z9.string(),
|
|
9678
|
+
baseHash: z9.string().optional()
|
|
9289
9679
|
// For conflict detection
|
|
9290
9680
|
}),
|
|
9291
9681
|
async (args, ctx) => {
|
|
@@ -9344,11 +9734,11 @@ async function getUntrackedFileDiff(cwd, filePath) {
|
|
|
9344
9734
|
await rm5(tempDir, { recursive: true, force: true });
|
|
9345
9735
|
}
|
|
9346
9736
|
}
|
|
9347
|
-
async function getFileDiff(cwd,
|
|
9348
|
-
if (!staged && !await isTrackedPath(cwd,
|
|
9349
|
-
return getUntrackedFileDiff(cwd,
|
|
9737
|
+
async function getFileDiff(cwd, path10, staged = false) {
|
|
9738
|
+
if (!staged && !await isTrackedPath(cwd, path10)) {
|
|
9739
|
+
return getUntrackedFileDiff(cwd, path10);
|
|
9350
9740
|
}
|
|
9351
|
-
const args = staged ? ["diff", "--staged", "--",
|
|
9741
|
+
const args = staged ? ["diff", "--staged", "--", path10] : ["diff", "--", path10];
|
|
9352
9742
|
const result = await runGit(cwd, args);
|
|
9353
9743
|
return result.stdout;
|
|
9354
9744
|
}
|
|
@@ -9359,8 +9749,7 @@ var init_diff = __esm({
|
|
|
9359
9749
|
}
|
|
9360
9750
|
});
|
|
9361
9751
|
|
|
9362
|
-
// packages/server/src/commands/git.ts
|
|
9363
|
-
import { z as z9 } from "zod";
|
|
9752
|
+
// packages/server/src/commands/git-events.ts
|
|
9364
9753
|
function emitGitStateChanged(ctx, workspaceId, options) {
|
|
9365
9754
|
ctx.eventBus.emit({
|
|
9366
9755
|
type: "git.state.changed",
|
|
@@ -9370,21 +9759,38 @@ function emitGitStateChanged(ctx, workspaceId, options) {
|
|
|
9370
9759
|
worktreeChanged: options?.worktreeChanged
|
|
9371
9760
|
});
|
|
9372
9761
|
}
|
|
9373
|
-
var
|
|
9762
|
+
var init_git_events = __esm({
|
|
9763
|
+
"packages/server/src/commands/git-events.ts"() {
|
|
9764
|
+
"use strict";
|
|
9765
|
+
}
|
|
9766
|
+
});
|
|
9767
|
+
|
|
9768
|
+
// packages/server/src/commands/git.ts
|
|
9769
|
+
import { z as z10 } from "zod";
|
|
9770
|
+
async function runGitNetworkOperation(ctx, workspaceId, op) {
|
|
9771
|
+
if (!ctx.autoFetch?.runExclusive) {
|
|
9772
|
+
return op();
|
|
9773
|
+
}
|
|
9774
|
+
return ctx.autoFetch.runExclusive(workspaceId, op);
|
|
9775
|
+
}
|
|
9776
|
+
var gitHttpAuthSchema, gitCommitRevisionSchema, GIT_BACKGROUND_FETCH_TIMEOUT_MS;
|
|
9374
9777
|
var init_git2 = __esm({
|
|
9375
9778
|
"packages/server/src/commands/git.ts"() {
|
|
9376
9779
|
"use strict";
|
|
9377
9780
|
init_cli();
|
|
9378
9781
|
init_diff();
|
|
9379
9782
|
init_dispatch();
|
|
9380
|
-
|
|
9381
|
-
|
|
9382
|
-
|
|
9783
|
+
init_git_events();
|
|
9784
|
+
gitHttpAuthSchema = z10.object({
|
|
9785
|
+
username: z10.string(),
|
|
9786
|
+
password: z10.string()
|
|
9383
9787
|
});
|
|
9788
|
+
gitCommitRevisionSchema = z10.string().regex(/^[0-9a-fA-F]{7,64}$/, "Invalid git commit revision");
|
|
9789
|
+
GIT_BACKGROUND_FETCH_TIMEOUT_MS = 30 * 1e3;
|
|
9384
9790
|
registerCommand(
|
|
9385
9791
|
"git.status",
|
|
9386
|
-
|
|
9387
|
-
workspaceId:
|
|
9792
|
+
z10.object({
|
|
9793
|
+
workspaceId: z10.string()
|
|
9388
9794
|
}),
|
|
9389
9795
|
async (args, ctx) => {
|
|
9390
9796
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9396,9 +9802,9 @@ var init_git2 = __esm({
|
|
|
9396
9802
|
);
|
|
9397
9803
|
registerCommand(
|
|
9398
9804
|
"git.stage",
|
|
9399
|
-
|
|
9400
|
-
workspaceId:
|
|
9401
|
-
paths:
|
|
9805
|
+
z10.object({
|
|
9806
|
+
workspaceId: z10.string(),
|
|
9807
|
+
paths: z10.array(z10.string())
|
|
9402
9808
|
}),
|
|
9403
9809
|
async (args, ctx) => {
|
|
9404
9810
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9412,10 +9818,10 @@ var init_git2 = __esm({
|
|
|
9412
9818
|
);
|
|
9413
9819
|
registerCommand(
|
|
9414
9820
|
"git.diff",
|
|
9415
|
-
|
|
9416
|
-
workspaceId:
|
|
9417
|
-
path:
|
|
9418
|
-
staged:
|
|
9821
|
+
z10.object({
|
|
9822
|
+
workspaceId: z10.string(),
|
|
9823
|
+
path: z10.string(),
|
|
9824
|
+
staged: z10.boolean().optional()
|
|
9419
9825
|
}),
|
|
9420
9826
|
async (args, ctx) => {
|
|
9421
9827
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9427,11 +9833,43 @@ var init_git2 = __esm({
|
|
|
9427
9833
|
};
|
|
9428
9834
|
}
|
|
9429
9835
|
);
|
|
9836
|
+
registerCommand(
|
|
9837
|
+
"git.log",
|
|
9838
|
+
z10.object({
|
|
9839
|
+
workspaceId: z10.string(),
|
|
9840
|
+
limit: z10.number().int().min(1).max(50).optional()
|
|
9841
|
+
}),
|
|
9842
|
+
async (args, ctx) => {
|
|
9843
|
+
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
9844
|
+
if (!workspace) {
|
|
9845
|
+
throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
|
|
9846
|
+
}
|
|
9847
|
+
return {
|
|
9848
|
+
entries: await getGitHistory(workspace.path, args.limit ?? 5)
|
|
9849
|
+
};
|
|
9850
|
+
}
|
|
9851
|
+
);
|
|
9852
|
+
registerCommand(
|
|
9853
|
+
"git.show",
|
|
9854
|
+
z10.object({
|
|
9855
|
+
workspaceId: z10.string(),
|
|
9856
|
+
sha: gitCommitRevisionSchema
|
|
9857
|
+
}),
|
|
9858
|
+
async (args, ctx) => {
|
|
9859
|
+
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
9860
|
+
if (!workspace) {
|
|
9861
|
+
throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
|
|
9862
|
+
}
|
|
9863
|
+
return {
|
|
9864
|
+
diff: await getGitCommitDiff(workspace.path, args.sha)
|
|
9865
|
+
};
|
|
9866
|
+
}
|
|
9867
|
+
);
|
|
9430
9868
|
registerCommand(
|
|
9431
9869
|
"git.unstage",
|
|
9432
|
-
|
|
9433
|
-
workspaceId:
|
|
9434
|
-
paths:
|
|
9870
|
+
z10.object({
|
|
9871
|
+
workspaceId: z10.string(),
|
|
9872
|
+
paths: z10.array(z10.string())
|
|
9435
9873
|
}),
|
|
9436
9874
|
async (args, ctx) => {
|
|
9437
9875
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9445,9 +9883,9 @@ var init_git2 = __esm({
|
|
|
9445
9883
|
);
|
|
9446
9884
|
registerCommand(
|
|
9447
9885
|
"git.discard",
|
|
9448
|
-
|
|
9449
|
-
workspaceId:
|
|
9450
|
-
paths:
|
|
9886
|
+
z10.object({
|
|
9887
|
+
workspaceId: z10.string(),
|
|
9888
|
+
paths: z10.array(z10.string())
|
|
9451
9889
|
}),
|
|
9452
9890
|
async (args, ctx) => {
|
|
9453
9891
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9463,9 +9901,9 @@ var init_git2 = __esm({
|
|
|
9463
9901
|
);
|
|
9464
9902
|
registerCommand(
|
|
9465
9903
|
"git.commit",
|
|
9466
|
-
|
|
9467
|
-
workspaceId:
|
|
9468
|
-
message:
|
|
9904
|
+
z10.object({
|
|
9905
|
+
workspaceId: z10.string(),
|
|
9906
|
+
message: z10.string()
|
|
9469
9907
|
}),
|
|
9470
9908
|
async (args, ctx) => {
|
|
9471
9909
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9482,11 +9920,11 @@ var init_git2 = __esm({
|
|
|
9482
9920
|
);
|
|
9483
9921
|
registerCommand(
|
|
9484
9922
|
"git.push",
|
|
9485
|
-
|
|
9486
|
-
workspaceId:
|
|
9487
|
-
remote:
|
|
9488
|
-
branch:
|
|
9489
|
-
force:
|
|
9923
|
+
z10.object({
|
|
9924
|
+
workspaceId: z10.string(),
|
|
9925
|
+
remote: z10.string().optional(),
|
|
9926
|
+
branch: z10.string().optional(),
|
|
9927
|
+
force: z10.boolean().optional(),
|
|
9490
9928
|
auth: gitHttpAuthSchema.optional()
|
|
9491
9929
|
}),
|
|
9492
9930
|
async (args, ctx) => {
|
|
@@ -9494,12 +9932,16 @@ var init_git2 = __esm({
|
|
|
9494
9932
|
if (!workspace) {
|
|
9495
9933
|
throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
|
|
9496
9934
|
}
|
|
9497
|
-
const result = await
|
|
9498
|
-
|
|
9499
|
-
|
|
9500
|
-
|
|
9501
|
-
|
|
9502
|
-
|
|
9935
|
+
const result = await runGitNetworkOperation(
|
|
9936
|
+
ctx,
|
|
9937
|
+
args.workspaceId,
|
|
9938
|
+
() => runGitPush(workspace.path, {
|
|
9939
|
+
remote: args.remote,
|
|
9940
|
+
branch: args.branch,
|
|
9941
|
+
force: args.force,
|
|
9942
|
+
auth: args.auth
|
|
9943
|
+
})
|
|
9944
|
+
);
|
|
9503
9945
|
emitGitStateChanged(ctx, args.workspaceId, {
|
|
9504
9946
|
branchChanged: true,
|
|
9505
9947
|
worktreeChanged: true
|
|
@@ -9509,10 +9951,10 @@ var init_git2 = __esm({
|
|
|
9509
9951
|
);
|
|
9510
9952
|
registerCommand(
|
|
9511
9953
|
"git.pull",
|
|
9512
|
-
|
|
9513
|
-
workspaceId:
|
|
9514
|
-
remote:
|
|
9515
|
-
branch:
|
|
9954
|
+
z10.object({
|
|
9955
|
+
workspaceId: z10.string(),
|
|
9956
|
+
remote: z10.string().optional(),
|
|
9957
|
+
branch: z10.string().optional(),
|
|
9516
9958
|
auth: gitHttpAuthSchema.optional()
|
|
9517
9959
|
}),
|
|
9518
9960
|
async (args, ctx) => {
|
|
@@ -9520,11 +9962,16 @@ var init_git2 = __esm({
|
|
|
9520
9962
|
if (!workspace) {
|
|
9521
9963
|
throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
|
|
9522
9964
|
}
|
|
9523
|
-
const result = await
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
|
|
9965
|
+
const result = await runGitNetworkOperation(
|
|
9966
|
+
ctx,
|
|
9967
|
+
args.workspaceId,
|
|
9968
|
+
() => runGitPull(workspace.path, {
|
|
9969
|
+
remote: args.remote,
|
|
9970
|
+
branch: args.branch,
|
|
9971
|
+
auth: args.auth
|
|
9972
|
+
})
|
|
9973
|
+
);
|
|
9974
|
+
ctx.workspaceMgr.recordFetch(args.workspaceId);
|
|
9528
9975
|
emitGitStateChanged(ctx, args.workspaceId, {
|
|
9529
9976
|
treeChanged: true,
|
|
9530
9977
|
branchChanged: true,
|
|
@@ -9533,12 +9980,46 @@ var init_git2 = __esm({
|
|
|
9533
9980
|
return result;
|
|
9534
9981
|
}
|
|
9535
9982
|
);
|
|
9983
|
+
registerCommand(
|
|
9984
|
+
"git.fetch",
|
|
9985
|
+
z10.object({
|
|
9986
|
+
workspaceId: z10.string(),
|
|
9987
|
+
remote: z10.string().optional(),
|
|
9988
|
+
prune: z10.boolean().optional(),
|
|
9989
|
+
auth: gitHttpAuthSchema.optional(),
|
|
9990
|
+
background: z10.boolean().optional()
|
|
9991
|
+
}),
|
|
9992
|
+
async (args, ctx, clientId) => {
|
|
9993
|
+
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
9994
|
+
if (!workspace) {
|
|
9995
|
+
throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
|
|
9996
|
+
}
|
|
9997
|
+
try {
|
|
9998
|
+
const isInternalBackgroundFetch = args.background === true && !clientId;
|
|
9999
|
+
const runFetch = () => runGitFetch(workspace.path, {
|
|
10000
|
+
remote: args.remote,
|
|
10001
|
+
prune: args.prune,
|
|
10002
|
+
auth: args.auth,
|
|
10003
|
+
timeoutMs: args.background ? GIT_BACKGROUND_FETCH_TIMEOUT_MS : void 0
|
|
10004
|
+
});
|
|
10005
|
+
const result = isInternalBackgroundFetch ? await runFetch() : await runGitNetworkOperation(ctx, args.workspaceId, runFetch);
|
|
10006
|
+
ctx.workspaceMgr.recordFetch(args.workspaceId);
|
|
10007
|
+
emitGitStateChanged(ctx, args.workspaceId, { branchChanged: true });
|
|
10008
|
+
return result;
|
|
10009
|
+
} catch (err) {
|
|
10010
|
+
if (args.background && err instanceof GitAuthError) {
|
|
10011
|
+
return { success: false, message: err.message, updatedRefs: [] };
|
|
10012
|
+
}
|
|
10013
|
+
throw err;
|
|
10014
|
+
}
|
|
10015
|
+
}
|
|
10016
|
+
);
|
|
9536
10017
|
registerCommand(
|
|
9537
10018
|
"git.checkout",
|
|
9538
|
-
|
|
9539
|
-
workspaceId:
|
|
9540
|
-
ref:
|
|
9541
|
-
createBranch:
|
|
10019
|
+
z10.object({
|
|
10020
|
+
workspaceId: z10.string(),
|
|
10021
|
+
ref: z10.string(),
|
|
10022
|
+
createBranch: z10.boolean().optional()
|
|
9542
10023
|
}),
|
|
9543
10024
|
async (args, ctx) => {
|
|
9544
10025
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9560,10 +10041,10 @@ var init_git2 = __esm({
|
|
|
9560
10041
|
);
|
|
9561
10042
|
registerCommand(
|
|
9562
10043
|
"git.branch",
|
|
9563
|
-
|
|
9564
|
-
workspaceId:
|
|
9565
|
-
name:
|
|
9566
|
-
startPoint:
|
|
10044
|
+
z10.object({
|
|
10045
|
+
workspaceId: z10.string(),
|
|
10046
|
+
name: z10.string(),
|
|
10047
|
+
startPoint: z10.string().optional()
|
|
9567
10048
|
}),
|
|
9568
10049
|
async (args, ctx) => {
|
|
9569
10050
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9582,8 +10063,8 @@ var init_git2 = __esm({
|
|
|
9582
10063
|
);
|
|
9583
10064
|
registerCommand(
|
|
9584
10065
|
"git.branches",
|
|
9585
|
-
|
|
9586
|
-
workspaceId:
|
|
10066
|
+
z10.object({
|
|
10067
|
+
workspaceId: z10.string()
|
|
9587
10068
|
}),
|
|
9588
10069
|
async (args, ctx) => {
|
|
9589
10070
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -9597,8 +10078,8 @@ var init_git2 = __esm({
|
|
|
9597
10078
|
});
|
|
9598
10079
|
|
|
9599
10080
|
// packages/server/src/config/config-io.ts
|
|
9600
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6, renameSync
|
|
9601
|
-
import { homedir as
|
|
10081
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6, renameSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
10082
|
+
import { homedir as homedir3 } from "node:os";
|
|
9602
10083
|
import { basename as basename2, dirname as dirname4, join as join7 } from "node:path";
|
|
9603
10084
|
function resolveConfigPath(configType) {
|
|
9604
10085
|
if (configType === "codex") {
|
|
@@ -9606,14 +10087,18 @@ function resolveConfigPath(configType) {
|
|
|
9606
10087
|
if (testHome && testHome.trim()) {
|
|
9607
10088
|
return join7(testHome, "config.toml");
|
|
9608
10089
|
}
|
|
9609
|
-
|
|
10090
|
+
const codexHome = process.env.CODEX_HOME;
|
|
10091
|
+
if (codexHome && codexHome.trim()) {
|
|
10092
|
+
return join7(codexHome, "config.toml");
|
|
10093
|
+
}
|
|
10094
|
+
return join7(homedir3(), ".codex", "config.toml");
|
|
9610
10095
|
}
|
|
9611
10096
|
if (configType === "claude") {
|
|
9612
10097
|
const testHome = process.env.CODER_STUDIO_CLAUDE_HOME;
|
|
9613
10098
|
if (testHome && testHome.trim()) {
|
|
9614
10099
|
return join7(testHome, "settings.json");
|
|
9615
10100
|
}
|
|
9616
|
-
return join7(
|
|
10101
|
+
return join7(homedir3(), ".claude", "settings.json");
|
|
9617
10102
|
}
|
|
9618
10103
|
throw new Error(`Unknown config type: ${configType}`);
|
|
9619
10104
|
}
|
|
@@ -9642,7 +10127,7 @@ function writeConfigFile(configType, content) {
|
|
|
9642
10127
|
}
|
|
9643
10128
|
const tempPath = `${configPath}.tmp`;
|
|
9644
10129
|
writeFileSync3(tempPath, content, "utf-8");
|
|
9645
|
-
|
|
10130
|
+
renameSync(tempPath, configPath);
|
|
9646
10131
|
return { success: true, backupPath };
|
|
9647
10132
|
} catch (error) {
|
|
9648
10133
|
return {
|
|
@@ -9657,24 +10142,23 @@ function createBackup(filePath) {
|
|
|
9657
10142
|
const ext = filePath.split(".").pop() ?? "";
|
|
9658
10143
|
const base = basename2(filePath, `.${ext}`);
|
|
9659
10144
|
const dir = dirname4(filePath);
|
|
9660
|
-
const ts =
|
|
10145
|
+
const ts = formatTimestamp(/* @__PURE__ */ new Date());
|
|
9661
10146
|
const backupPath = join7(dir, `${base}.bak.${ts}.${ext}`);
|
|
9662
10147
|
writeFileSync3(backupPath, original, "utf-8");
|
|
9663
10148
|
return backupPath;
|
|
9664
10149
|
}
|
|
9665
|
-
function
|
|
10150
|
+
function formatTimestamp(d) {
|
|
9666
10151
|
const pad = (n) => String(n).padStart(2, "0");
|
|
9667
10152
|
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
9668
10153
|
}
|
|
9669
10154
|
var init_config_io = __esm({
|
|
9670
10155
|
"packages/server/src/config/config-io.ts"() {
|
|
9671
10156
|
"use strict";
|
|
9672
|
-
init_codex_config_audit();
|
|
9673
10157
|
}
|
|
9674
10158
|
});
|
|
9675
10159
|
|
|
9676
10160
|
// packages/server/src/commands/settings.ts
|
|
9677
|
-
import { z as
|
|
10161
|
+
import { z as z11 } from "zod";
|
|
9678
10162
|
function flattenSettings(obj, prefix = "") {
|
|
9679
10163
|
const result = {};
|
|
9680
10164
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -9687,7 +10171,7 @@ function flattenSettings(obj, prefix = "") {
|
|
|
9687
10171
|
}
|
|
9688
10172
|
return result;
|
|
9689
10173
|
}
|
|
9690
|
-
var
|
|
10174
|
+
var SettingsSchema;
|
|
9691
10175
|
var init_settings2 = __esm({
|
|
9692
10176
|
"packages/server/src/commands/settings.ts"() {
|
|
9693
10177
|
"use strict";
|
|
@@ -9697,34 +10181,27 @@ var init_settings2 = __esm({
|
|
|
9697
10181
|
init_provider_config_repo();
|
|
9698
10182
|
init_settings();
|
|
9699
10183
|
init_dispatch();
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
}
|
|
9706
|
-
};
|
|
9707
|
-
SettingsSchema = z10.object({
|
|
9708
|
-
defaultProviderId: z10.string().optional(),
|
|
9709
|
-
notifications: z10.object({
|
|
9710
|
-
enabled: z10.boolean().optional(),
|
|
9711
|
-
soundEnabled: z10.boolean().optional(),
|
|
10184
|
+
SettingsSchema = z11.object({
|
|
10185
|
+
defaultProviderId: z11.string().optional(),
|
|
10186
|
+
notifications: z11.object({
|
|
10187
|
+
enabled: z11.boolean().optional(),
|
|
10188
|
+
soundEnabled: z11.boolean().optional(),
|
|
9712
10189
|
// Legacy field — accepted for backward compat with older clients but
|
|
9713
10190
|
// no longer surfaced in the UI. The web client now picks the channel
|
|
9714
10191
|
// automatically based on workspace focus + page visibility.
|
|
9715
|
-
onlyWhenBackgrounded:
|
|
10192
|
+
onlyWhenBackgrounded: z11.boolean().optional()
|
|
9716
10193
|
}).optional(),
|
|
9717
|
-
supervisor:
|
|
9718
|
-
evaluationTimeoutSec:
|
|
10194
|
+
supervisor: z11.object({
|
|
10195
|
+
evaluationTimeoutSec: z11.number().int().min(1).max(MAX_SUPERVISOR_EVALUATION_TIMEOUT_SEC).default(DEFAULT_SUPERVISOR_EVALUATION_TIMEOUT_SEC).optional()
|
|
9719
10196
|
}).optional(),
|
|
9720
|
-
appearance:
|
|
9721
|
-
theme:
|
|
9722
|
-
terminalRenderer:
|
|
9723
|
-
locale:
|
|
10197
|
+
appearance: z11.object({
|
|
10198
|
+
theme: z11.enum(["dark"]).optional(),
|
|
10199
|
+
terminalRenderer: z11.enum(["standard", "compatibility"]).optional(),
|
|
10200
|
+
locale: z11.enum(["zh", "en"]).optional()
|
|
9724
10201
|
}).optional(),
|
|
9725
10202
|
providers: ProviderSettingsSchema.optional()
|
|
9726
10203
|
});
|
|
9727
|
-
registerCommand("settings.get",
|
|
10204
|
+
registerCommand("settings.get", z11.object({}), async (_args, ctx) => {
|
|
9728
10205
|
const row = ctx.db.prepare("SELECT key, value FROM user_settings").all();
|
|
9729
10206
|
const settings = {};
|
|
9730
10207
|
for (const { key, value } of row) {
|
|
@@ -9748,11 +10225,6 @@ var init_settings2 = __esm({
|
|
|
9748
10225
|
flattenSettings(sanitizeProviderLaunchConfig(config), `providers.${providerId}`)
|
|
9749
10226
|
);
|
|
9750
10227
|
}
|
|
9751
|
-
try {
|
|
9752
|
-
settings.externalConfigAudit = ctx.codexConfigAudit?.audit() ?? EMPTY_CODEX_AUDIT;
|
|
9753
|
-
} catch {
|
|
9754
|
-
settings.externalConfigAudit = null;
|
|
9755
|
-
}
|
|
9756
10228
|
if (Object.prototype.hasOwnProperty.call(settings, SUPERVISOR_EVALUATION_TIMEOUT_SETTING_KEY)) {
|
|
9757
10229
|
settings[SUPERVISOR_EVALUATION_TIMEOUT_SETTING_KEY] = resolveSupervisorEvaluationTimeoutSec(
|
|
9758
10230
|
settings[SUPERVISOR_EVALUATION_TIMEOUT_SETTING_KEY]
|
|
@@ -9762,7 +10234,7 @@ var init_settings2 = __esm({
|
|
|
9762
10234
|
});
|
|
9763
10235
|
registerCommand(
|
|
9764
10236
|
"settings.update",
|
|
9765
|
-
|
|
10237
|
+
z11.object({
|
|
9766
10238
|
settings: SettingsSchema
|
|
9767
10239
|
}),
|
|
9768
10240
|
async (args, ctx) => {
|
|
@@ -9792,31 +10264,12 @@ var init_settings2 = __esm({
|
|
|
9792
10264
|
};
|
|
9793
10265
|
}
|
|
9794
10266
|
);
|
|
9795
|
-
registerCommand(
|
|
9796
|
-
"settings.cleanupCodexConfig",
|
|
9797
|
-
z10.object({
|
|
9798
|
-
removeIds: z10.array(z10.enum(["toml_notify", "toml_codex_hooks"])).min(1)
|
|
9799
|
-
}),
|
|
9800
|
-
async (args, ctx) => {
|
|
9801
|
-
const result = ctx.codexConfigAudit?.cleanup(args.removeIds) ?? {
|
|
9802
|
-
removed: [],
|
|
9803
|
-
backupPath: null,
|
|
9804
|
-
noop: true
|
|
9805
|
-
};
|
|
9806
|
-
return {
|
|
9807
|
-
removed: result.removed,
|
|
9808
|
-
backupPath: result.backupPath,
|
|
9809
|
-
noop: result.noop,
|
|
9810
|
-
audit: ctx.codexConfigAudit?.audit() ?? EMPTY_CODEX_AUDIT
|
|
9811
|
-
};
|
|
9812
|
-
}
|
|
9813
|
-
);
|
|
9814
10267
|
registerCommand(
|
|
9815
10268
|
"settings.previewCommand",
|
|
9816
|
-
|
|
9817
|
-
providerId:
|
|
10269
|
+
z11.object({
|
|
10270
|
+
providerId: z11.string(),
|
|
9818
10271
|
config: ProviderLaunchConfigInputSchema,
|
|
9819
|
-
workspacePath:
|
|
10272
|
+
workspacePath: z11.string().optional()
|
|
9820
10273
|
}),
|
|
9821
10274
|
async (args, ctx) => {
|
|
9822
10275
|
const provider = ctx.providerRegistry.find((item) => item.id === args.providerId);
|
|
@@ -9837,8 +10290,8 @@ var init_settings2 = __esm({
|
|
|
9837
10290
|
);
|
|
9838
10291
|
registerCommand(
|
|
9839
10292
|
"settings.readConfigFile",
|
|
9840
|
-
|
|
9841
|
-
configType:
|
|
10293
|
+
z11.object({
|
|
10294
|
+
configType: z11.enum(["codex", "claude"])
|
|
9842
10295
|
}),
|
|
9843
10296
|
async (args) => {
|
|
9844
10297
|
const result = readConfigFile(args.configType);
|
|
@@ -9847,9 +10300,9 @@ var init_settings2 = __esm({
|
|
|
9847
10300
|
);
|
|
9848
10301
|
registerCommand(
|
|
9849
10302
|
"settings.writeConfigFile",
|
|
9850
|
-
|
|
9851
|
-
configType:
|
|
9852
|
-
content:
|
|
10303
|
+
z11.object({
|
|
10304
|
+
configType: z11.enum(["codex", "claude"]),
|
|
10305
|
+
content: z11.string()
|
|
9853
10306
|
}),
|
|
9854
10307
|
async (args) => {
|
|
9855
10308
|
const result = writeConfigFile(args.configType, args.content);
|
|
@@ -9860,19 +10313,19 @@ var init_settings2 = __esm({
|
|
|
9860
10313
|
});
|
|
9861
10314
|
|
|
9862
10315
|
// packages/server/src/commands/provider.ts
|
|
9863
|
-
import { z as
|
|
10316
|
+
import { z as z12 } from "zod";
|
|
9864
10317
|
var init_provider = __esm({
|
|
9865
10318
|
"packages/server/src/commands/provider.ts"() {
|
|
9866
10319
|
"use strict";
|
|
9867
10320
|
init_runtime_status();
|
|
9868
10321
|
init_dispatch();
|
|
9869
|
-
registerCommand("provider.runtimeStatus",
|
|
10322
|
+
registerCommand("provider.runtimeStatus", z12.object({}), async (_args, ctx) => {
|
|
9870
10323
|
return buildProviderRuntimeStatus(ctx.providerRegistry, ctx.providerRuntimeDeps);
|
|
9871
10324
|
});
|
|
9872
10325
|
registerCommand(
|
|
9873
10326
|
"provider.install.start",
|
|
9874
|
-
|
|
9875
|
-
providerId:
|
|
10327
|
+
z12.object({
|
|
10328
|
+
providerId: z12.string()
|
|
9876
10329
|
}),
|
|
9877
10330
|
async (args, ctx) => {
|
|
9878
10331
|
if (!ctx.providerInstallMgr) {
|
|
@@ -9886,8 +10339,8 @@ var init_provider = __esm({
|
|
|
9886
10339
|
);
|
|
9887
10340
|
registerCommand(
|
|
9888
10341
|
"provider.install.get",
|
|
9889
|
-
|
|
9890
|
-
jobId:
|
|
10342
|
+
z12.object({
|
|
10343
|
+
jobId: z12.string()
|
|
9891
10344
|
}),
|
|
9892
10345
|
async (args, ctx) => {
|
|
9893
10346
|
if (!ctx.providerInstallMgr) {
|
|
@@ -9910,29 +10363,29 @@ var init_provider = __esm({
|
|
|
9910
10363
|
});
|
|
9911
10364
|
|
|
9912
10365
|
// packages/server/src/commands/supervisor.ts
|
|
9913
|
-
import { z as
|
|
10366
|
+
import { z as z13 } from "zod";
|
|
9914
10367
|
var supervisorObjectiveSchema, createSupervisorSchema, updateSupervisorSchema, sessionIdSchema, supervisorIdSchema;
|
|
9915
10368
|
var init_supervisor2 = __esm({
|
|
9916
10369
|
"packages/server/src/commands/supervisor.ts"() {
|
|
9917
10370
|
"use strict";
|
|
9918
10371
|
init_dispatch();
|
|
9919
|
-
supervisorObjectiveSchema =
|
|
9920
|
-
createSupervisorSchema =
|
|
9921
|
-
sessionId:
|
|
9922
|
-
workspaceId:
|
|
10372
|
+
supervisorObjectiveSchema = z13.string().trim().min(1).max(4e3);
|
|
10373
|
+
createSupervisorSchema = z13.object({
|
|
10374
|
+
sessionId: z13.string(),
|
|
10375
|
+
workspaceId: z13.string(),
|
|
9923
10376
|
objective: supervisorObjectiveSchema,
|
|
9924
|
-
evaluatorProviderId:
|
|
10377
|
+
evaluatorProviderId: z13.string()
|
|
9925
10378
|
}).strict();
|
|
9926
|
-
updateSupervisorSchema =
|
|
9927
|
-
id:
|
|
10379
|
+
updateSupervisorSchema = z13.object({
|
|
10380
|
+
id: z13.string(),
|
|
9928
10381
|
objective: supervisorObjectiveSchema.optional(),
|
|
9929
|
-
evaluatorProviderId:
|
|
10382
|
+
evaluatorProviderId: z13.string().optional()
|
|
9930
10383
|
}).strict().refine(
|
|
9931
10384
|
(input2) => input2.objective !== void 0 || input2.evaluatorProviderId !== void 0,
|
|
9932
10385
|
"objective or evaluatorProviderId is required"
|
|
9933
10386
|
);
|
|
9934
|
-
sessionIdSchema =
|
|
9935
|
-
supervisorIdSchema =
|
|
10387
|
+
sessionIdSchema = z13.object({ sessionId: z13.string() });
|
|
10388
|
+
supervisorIdSchema = z13.object({ id: z13.string() });
|
|
9936
10389
|
registerCommand("supervisor.create", createSupervisorSchema, async (args, ctx) => {
|
|
9937
10390
|
return {
|
|
9938
10391
|
supervisor: await ctx.supervisorMgr.create({
|
|
@@ -9975,6 +10428,10 @@ import path8 from "node:path";
|
|
|
9975
10428
|
function normalizeWorktreePath(worktreePath) {
|
|
9976
10429
|
return path8.resolve(worktreePath);
|
|
9977
10430
|
}
|
|
10431
|
+
async function getGitCommonDirPath(repoPath) {
|
|
10432
|
+
const { stdout } = await runGit(repoPath, ["rev-parse", "--git-common-dir"]);
|
|
10433
|
+
return normalizeWorktreePath(path8.resolve(repoPath, stdout.trim()));
|
|
10434
|
+
}
|
|
9978
10435
|
async function resolveWorktreePath(repoPath, worktreePath) {
|
|
9979
10436
|
const normalizedRequested = normalizeWorktreePath(worktreePath);
|
|
9980
10437
|
const worktrees = await listWorktrees(repoPath);
|
|
@@ -10060,19 +10517,30 @@ async function getWorktreeTree(worktreePath) {
|
|
|
10060
10517
|
for (const line of lines) {
|
|
10061
10518
|
const isDir = line.endsWith("/");
|
|
10062
10519
|
const name = isDir ? line.slice(0, -1) : line;
|
|
10063
|
-
const
|
|
10520
|
+
const path10 = `${worktreePath}/${name}`;
|
|
10064
10521
|
nodes.push({
|
|
10065
10522
|
name,
|
|
10066
|
-
path:
|
|
10523
|
+
path: path10,
|
|
10067
10524
|
kind: isDir ? "dir" : "file"
|
|
10068
10525
|
});
|
|
10069
10526
|
}
|
|
10070
10527
|
return nodes;
|
|
10071
10528
|
}
|
|
10072
|
-
async function createWorktree(repoPath, branch,
|
|
10073
|
-
|
|
10529
|
+
async function createWorktree(repoPath, branch, worktreePath) {
|
|
10530
|
+
let createArgs = ["worktree", "add", worktreePath, branch];
|
|
10531
|
+
try {
|
|
10532
|
+
await runGit(repoPath, ["rev-parse", "--verify", "--quiet", `${branch}^{commit}`]);
|
|
10533
|
+
} catch (error) {
|
|
10534
|
+
if (error instanceof GitError) {
|
|
10535
|
+
createArgs = ["worktree", "add", "-b", branch, worktreePath];
|
|
10536
|
+
} else {
|
|
10537
|
+
throw error;
|
|
10538
|
+
}
|
|
10539
|
+
}
|
|
10540
|
+
await runGit(repoPath, createArgs);
|
|
10074
10541
|
const worktrees = await listWorktrees(repoPath);
|
|
10075
|
-
const
|
|
10542
|
+
const normalizedRequested = normalizeWorktreePath(worktreePath);
|
|
10543
|
+
const created = worktrees.find((wt) => normalizeWorktreePath(wt.path) === normalizedRequested);
|
|
10076
10544
|
if (!created) {
|
|
10077
10545
|
throw new Error("Failed to find created worktree");
|
|
10078
10546
|
}
|
|
@@ -10094,13 +10562,41 @@ var init_worktree = __esm({
|
|
|
10094
10562
|
});
|
|
10095
10563
|
|
|
10096
10564
|
// packages/server/src/commands/worktree.ts
|
|
10097
|
-
import
|
|
10565
|
+
import path9 from "node:path";
|
|
10566
|
+
import { z as z14 } from "zod";
|
|
10567
|
+
async function findRelatedWorkspaceIds(ctx, workspacePath) {
|
|
10568
|
+
const targetCommonDir = await getGitCommonDirPath(workspacePath);
|
|
10569
|
+
const relatedWorkspaceIds = await Promise.all(
|
|
10570
|
+
ctx.workspaceMgr.list().map(async (workspace) => {
|
|
10571
|
+
try {
|
|
10572
|
+
const commonDir = await getGitCommonDirPath(workspace.path);
|
|
10573
|
+
return commonDir === targetCommonDir ? workspace.id : null;
|
|
10574
|
+
} catch {
|
|
10575
|
+
return null;
|
|
10576
|
+
}
|
|
10577
|
+
})
|
|
10578
|
+
);
|
|
10579
|
+
return relatedWorkspaceIds.filter((workspaceId) => Boolean(workspaceId));
|
|
10580
|
+
}
|
|
10581
|
+
function emitWorktreeChangedForWorkspaceIds(ctx, workspaceIds) {
|
|
10582
|
+
for (const workspaceId of workspaceIds) {
|
|
10583
|
+
if (!ctx.workspaceMgr.get(workspaceId)) {
|
|
10584
|
+
continue;
|
|
10585
|
+
}
|
|
10586
|
+
emitGitStateChanged(ctx, workspaceId, { worktreeChanged: true });
|
|
10587
|
+
}
|
|
10588
|
+
}
|
|
10589
|
+
function isWorkspaceOpenForPath(ctx, workspacePath) {
|
|
10590
|
+
const targetPath = path9.resolve(workspacePath);
|
|
10591
|
+
return ctx.workspaceMgr.list().some((openWorkspace) => path9.resolve(openWorkspace.path) === targetPath);
|
|
10592
|
+
}
|
|
10098
10593
|
var init_worktree2 = __esm({
|
|
10099
10594
|
"packages/server/src/commands/worktree.ts"() {
|
|
10100
10595
|
"use strict";
|
|
10101
10596
|
init_worktree();
|
|
10102
10597
|
init_dispatch();
|
|
10103
|
-
|
|
10598
|
+
init_git_events();
|
|
10599
|
+
registerCommand("worktree.list", z14.object({ workspaceId: z14.string() }), async (args, ctx) => {
|
|
10104
10600
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
10105
10601
|
if (!workspace) {
|
|
10106
10602
|
throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
|
|
@@ -10109,7 +10605,7 @@ var init_worktree2 = __esm({
|
|
|
10109
10605
|
});
|
|
10110
10606
|
registerCommand(
|
|
10111
10607
|
"worktree.status",
|
|
10112
|
-
|
|
10608
|
+
z14.object({ workspaceId: z14.string(), worktreePath: z14.string() }),
|
|
10113
10609
|
async (args, ctx) => {
|
|
10114
10610
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
10115
10611
|
if (!workspace) {
|
|
@@ -10121,10 +10617,10 @@ var init_worktree2 = __esm({
|
|
|
10121
10617
|
);
|
|
10122
10618
|
registerCommand(
|
|
10123
10619
|
"worktree.diff",
|
|
10124
|
-
|
|
10125
|
-
workspaceId:
|
|
10126
|
-
worktreePath:
|
|
10127
|
-
staged:
|
|
10620
|
+
z14.object({
|
|
10621
|
+
workspaceId: z14.string(),
|
|
10622
|
+
worktreePath: z14.string(),
|
|
10623
|
+
staged: z14.boolean().optional().default(false)
|
|
10128
10624
|
}),
|
|
10129
10625
|
async (args, ctx) => {
|
|
10130
10626
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -10137,7 +10633,7 @@ var init_worktree2 = __esm({
|
|
|
10137
10633
|
);
|
|
10138
10634
|
registerCommand(
|
|
10139
10635
|
"worktree.tree",
|
|
10140
|
-
|
|
10636
|
+
z14.object({ workspaceId: z14.string(), worktreePath: z14.string() }),
|
|
10141
10637
|
async (args, ctx) => {
|
|
10142
10638
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
10143
10639
|
if (!workspace) {
|
|
@@ -10149,33 +10645,44 @@ var init_worktree2 = __esm({
|
|
|
10149
10645
|
);
|
|
10150
10646
|
registerCommand(
|
|
10151
10647
|
"worktree.create",
|
|
10152
|
-
|
|
10153
|
-
workspaceId:
|
|
10154
|
-
branch:
|
|
10155
|
-
path:
|
|
10648
|
+
z14.object({
|
|
10649
|
+
workspaceId: z14.string(),
|
|
10650
|
+
branch: z14.string(),
|
|
10651
|
+
path: z14.string()
|
|
10156
10652
|
}),
|
|
10157
10653
|
async (args, ctx) => {
|
|
10158
10654
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
10159
10655
|
if (!workspace) {
|
|
10160
10656
|
throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
|
|
10161
10657
|
}
|
|
10162
|
-
|
|
10658
|
+
const relatedWorkspaceIds = await findRelatedWorkspaceIds(ctx, workspace.path);
|
|
10659
|
+
const worktree = await createWorktree(workspace.path, args.branch, args.path);
|
|
10660
|
+
emitWorktreeChangedForWorkspaceIds(ctx, relatedWorkspaceIds);
|
|
10661
|
+
return { worktree };
|
|
10163
10662
|
}
|
|
10164
10663
|
);
|
|
10165
10664
|
registerCommand(
|
|
10166
10665
|
"worktree.remove",
|
|
10167
|
-
|
|
10168
|
-
workspaceId:
|
|
10169
|
-
worktreePath:
|
|
10170
|
-
force:
|
|
10666
|
+
z14.object({
|
|
10667
|
+
workspaceId: z14.string(),
|
|
10668
|
+
worktreePath: z14.string(),
|
|
10669
|
+
force: z14.boolean().optional().default(false)
|
|
10171
10670
|
}),
|
|
10172
10671
|
async (args, ctx) => {
|
|
10173
10672
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
10174
10673
|
if (!workspace) {
|
|
10175
10674
|
throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
|
|
10176
10675
|
}
|
|
10676
|
+
const relatedWorkspaceIds = await findRelatedWorkspaceIds(ctx, workspace.path);
|
|
10177
10677
|
const worktreePath = await resolveWorktreePath(workspace.path, args.worktreePath);
|
|
10678
|
+
if (isWorkspaceOpenForPath(ctx, worktreePath)) {
|
|
10679
|
+
throw {
|
|
10680
|
+
code: "worktree_in_use",
|
|
10681
|
+
message: `Cannot remove an open worktree workspace: ${worktreePath}`
|
|
10682
|
+
};
|
|
10683
|
+
}
|
|
10178
10684
|
await removeWorktree(workspace.path, worktreePath, args.force);
|
|
10685
|
+
emitWorktreeChangedForWorkspaceIds(ctx, relatedWorkspaceIds);
|
|
10179
10686
|
return {};
|
|
10180
10687
|
}
|
|
10181
10688
|
);
|
|
@@ -10183,7 +10690,7 @@ var init_worktree2 = __esm({
|
|
|
10183
10690
|
});
|
|
10184
10691
|
|
|
10185
10692
|
// packages/server/src/commands/fencing.ts
|
|
10186
|
-
import { z as
|
|
10693
|
+
import { z as z15 } from "zod";
|
|
10187
10694
|
function createMockFencingRequest() {
|
|
10188
10695
|
return {
|
|
10189
10696
|
ip: "127.0.0.1",
|
|
@@ -10196,9 +10703,9 @@ var init_fencing2 = __esm({
|
|
|
10196
10703
|
init_dispatch();
|
|
10197
10704
|
registerCommand(
|
|
10198
10705
|
"fencing.request",
|
|
10199
|
-
|
|
10200
|
-
workspaceId:
|
|
10201
|
-
tabId:
|
|
10706
|
+
z15.object({
|
|
10707
|
+
workspaceId: z15.string(),
|
|
10708
|
+
tabId: z15.string()
|
|
10202
10709
|
}),
|
|
10203
10710
|
async (args, ctx, clientId) => {
|
|
10204
10711
|
return ctx.fencingMgr.requestControl(
|
|
@@ -10211,7 +10718,7 @@ var init_fencing2 = __esm({
|
|
|
10211
10718
|
);
|
|
10212
10719
|
registerCommand(
|
|
10213
10720
|
"fencing.heartbeat",
|
|
10214
|
-
|
|
10721
|
+
z15.object({ workspaceId: z15.string() }),
|
|
10215
10722
|
async (args, ctx, clientId) => {
|
|
10216
10723
|
const success = ctx.fencingMgr.heartbeat(args.workspaceId, clientId);
|
|
10217
10724
|
return { success };
|
|
@@ -10219,13 +10726,13 @@ var init_fencing2 = __esm({
|
|
|
10219
10726
|
);
|
|
10220
10727
|
registerCommand(
|
|
10221
10728
|
"fencing.release",
|
|
10222
|
-
|
|
10729
|
+
z15.object({ workspaceId: z15.string() }),
|
|
10223
10730
|
async (args, ctx, clientId) => {
|
|
10224
10731
|
ctx.fencingMgr.release(args.workspaceId, clientId);
|
|
10225
10732
|
return {};
|
|
10226
10733
|
}
|
|
10227
10734
|
);
|
|
10228
|
-
registerCommand("fencing.status",
|
|
10735
|
+
registerCommand("fencing.status", z15.object({ workspaceId: z15.string() }), async (args, ctx) => {
|
|
10229
10736
|
const controller = ctx.fencingMgr.getController(args.workspaceId);
|
|
10230
10737
|
const isUnresponsive = ctx.fencingMgr.isControllerUnresponsive(args.workspaceId);
|
|
10231
10738
|
return {
|
|
@@ -10236,9 +10743,9 @@ var init_fencing2 = __esm({
|
|
|
10236
10743
|
});
|
|
10237
10744
|
registerCommand(
|
|
10238
10745
|
"fencing.takeover",
|
|
10239
|
-
|
|
10240
|
-
workspaceId:
|
|
10241
|
-
tabId:
|
|
10746
|
+
z15.object({
|
|
10747
|
+
workspaceId: z15.string(),
|
|
10748
|
+
tabId: z15.string()
|
|
10242
10749
|
}),
|
|
10243
10750
|
async (args, ctx, clientId) => {
|
|
10244
10751
|
return ctx.fencingMgr.forceTakeover(
|
|
@@ -10257,6 +10764,7 @@ var init_commands = __esm({
|
|
|
10257
10764
|
"packages/server/src/commands/index.ts"() {
|
|
10258
10765
|
"use strict";
|
|
10259
10766
|
init_workspace();
|
|
10767
|
+
init_workspace_activity();
|
|
10260
10768
|
init_session();
|
|
10261
10769
|
init_terminal();
|
|
10262
10770
|
init_file();
|
|
@@ -10270,32 +10778,6 @@ var init_commands = __esm({
|
|
|
10270
10778
|
});
|
|
10271
10779
|
|
|
10272
10780
|
// packages/server/src/server.ts
|
|
10273
|
-
function createCodexConfigAuditApi() {
|
|
10274
|
-
return {
|
|
10275
|
-
audit: () => ({ codex: auditCodexConfigToml() }),
|
|
10276
|
-
cleanup: (removeIds) => {
|
|
10277
|
-
const audit = auditCodexConfigToml();
|
|
10278
|
-
return cleanupCodexConfigToml(audit.configPath, { removeIds });
|
|
10279
|
-
}
|
|
10280
|
-
};
|
|
10281
|
-
}
|
|
10282
|
-
async function logCodexConfigFindings(auditApi, logger) {
|
|
10283
|
-
try {
|
|
10284
|
-
const audit = auditApi.audit();
|
|
10285
|
-
for (const finding of audit.codex.findings) {
|
|
10286
|
-
logger.warn(
|
|
10287
|
-
{
|
|
10288
|
-
configPath: audit.codex.configPath,
|
|
10289
|
-
startLine: finding.startLine,
|
|
10290
|
-
findingMessage: finding.message
|
|
10291
|
-
},
|
|
10292
|
-
"Codex config finding"
|
|
10293
|
-
);
|
|
10294
|
-
}
|
|
10295
|
-
} catch (err) {
|
|
10296
|
-
logger.warn({ err }, "Codex config audit failed (non-fatal)");
|
|
10297
|
-
}
|
|
10298
|
-
}
|
|
10299
10781
|
async function createServer(configOverrides) {
|
|
10300
10782
|
const config = parseServerConfig(configOverrides);
|
|
10301
10783
|
ensureDataDir(config);
|
|
@@ -10303,14 +10785,45 @@ async function createServer(configOverrides) {
|
|
|
10303
10785
|
const eventBus = new EventBus();
|
|
10304
10786
|
const fencingMgr = new FencingManager();
|
|
10305
10787
|
const wsHub = new WsHub({ eventBus, commandContext: null, config, fencingMgr });
|
|
10788
|
+
let workspaceMgr;
|
|
10789
|
+
let commandContext;
|
|
10306
10790
|
const terminalMgr = new TerminalManager({
|
|
10307
10791
|
ptyHost: createPtyHost(),
|
|
10308
10792
|
eventBus,
|
|
10309
10793
|
db: createTerminalDatabase(db)
|
|
10310
10794
|
});
|
|
10795
|
+
const settingsRepo = new SettingsRepo(db);
|
|
10796
|
+
const autoFetch = new AutoFetchScheduler({
|
|
10797
|
+
workspaceMgr: { get: (workspaceId) => workspaceMgr.get(workspaceId) },
|
|
10798
|
+
eventBus,
|
|
10799
|
+
settingsRepo,
|
|
10800
|
+
runFetch: async (workspaceId) => {
|
|
10801
|
+
if (!workspaceMgr.get(workspaceId)) {
|
|
10802
|
+
return;
|
|
10803
|
+
}
|
|
10804
|
+
const result = await dispatch(
|
|
10805
|
+
{
|
|
10806
|
+
kind: "command",
|
|
10807
|
+
id: `auto-fetch:${workspaceId}:${Date.now()}`,
|
|
10808
|
+
op: "git.fetch",
|
|
10809
|
+
args: {
|
|
10810
|
+
workspaceId,
|
|
10811
|
+
background: true
|
|
10812
|
+
}
|
|
10813
|
+
},
|
|
10814
|
+
commandContext
|
|
10815
|
+
);
|
|
10816
|
+
if (!result.ok) {
|
|
10817
|
+
throw new Error(result.error?.message ?? "Background fetch failed");
|
|
10818
|
+
}
|
|
10819
|
+
const data = result.data;
|
|
10820
|
+
if (data.success === false) {
|
|
10821
|
+
throw new Error(data.message ?? "Background fetch failed");
|
|
10822
|
+
}
|
|
10823
|
+
}
|
|
10824
|
+
});
|
|
10311
10825
|
const sessionDb = createSessionDatabase(db);
|
|
10312
10826
|
const providerConfigRepo = new ProviderConfigRepo(db);
|
|
10313
|
-
const settingsRepo = new SettingsRepo(db);
|
|
10314
10827
|
const sessionMgr = new SessionManager({
|
|
10315
10828
|
terminalMgr,
|
|
10316
10829
|
eventBus,
|
|
@@ -10320,10 +10833,11 @@ async function createServer(configOverrides) {
|
|
|
10320
10833
|
providerConfigRepo
|
|
10321
10834
|
});
|
|
10322
10835
|
let supervisorMgr;
|
|
10323
|
-
|
|
10836
|
+
workspaceMgr = new WorkspaceManager({
|
|
10324
10837
|
db,
|
|
10325
10838
|
eventBus,
|
|
10326
10839
|
broadcaster: wsHub,
|
|
10840
|
+
autoFetch,
|
|
10327
10841
|
teardown: async (workspaceId) => {
|
|
10328
10842
|
await supervisorMgr?.deleteForWorkspace(workspaceId);
|
|
10329
10843
|
await sessionMgr.stopForWorkspace(workspaceId);
|
|
@@ -10334,9 +10848,9 @@ async function createServer(configOverrides) {
|
|
|
10334
10848
|
(err) => console.warn("[uploads] cascade cleanup failed", { wsId: workspaceId, err })
|
|
10335
10849
|
)
|
|
10336
10850
|
});
|
|
10851
|
+
workspaceMgr.hydrateWatchers();
|
|
10337
10852
|
const authSessionRepo = new AuthSessionRepo(db);
|
|
10338
10853
|
const authLoginBlockRepo = new AuthLoginBlockRepo(db);
|
|
10339
|
-
const codexConfigAudit = createCodexConfigAuditApi();
|
|
10340
10854
|
const app = await buildFastifyApp({
|
|
10341
10855
|
wsHub,
|
|
10342
10856
|
db,
|
|
@@ -10357,7 +10871,6 @@ async function createServer(configOverrides) {
|
|
|
10357
10871
|
}
|
|
10358
10872
|
});
|
|
10359
10873
|
wsHub.setLogger(app.log);
|
|
10360
|
-
await logCodexConfigFindings(codexConfigAudit, app.log);
|
|
10361
10874
|
const supervisorRepo = new SupervisorRepo(db);
|
|
10362
10875
|
const cycleRepo = new SupervisorCycleRepo(db);
|
|
10363
10876
|
supervisorMgr = new SupervisorManager({
|
|
@@ -10375,12 +10888,15 @@ async function createServer(configOverrides) {
|
|
|
10375
10888
|
});
|
|
10376
10889
|
await sessionMgr.hydrate();
|
|
10377
10890
|
await supervisorMgr.hydrate();
|
|
10378
|
-
const
|
|
10891
|
+
const providerMockOverrides = createE2EProviderMockOverrides();
|
|
10892
|
+
const providerRuntimeDeps = providerMockOverrides ? {
|
|
10893
|
+
commandExists: providerMockOverrides.commandExists
|
|
10894
|
+
} : {};
|
|
10379
10895
|
const providerInstallMgr = new ProviderInstallManager(providerRegistry, {
|
|
10380
10896
|
...providerRuntimeDeps,
|
|
10381
|
-
runCommand: runCommandAsString
|
|
10897
|
+
runCommand: providerMockOverrides?.runCommand ?? runCommandAsString
|
|
10382
10898
|
});
|
|
10383
|
-
|
|
10899
|
+
commandContext = {
|
|
10384
10900
|
workspaceMgr,
|
|
10385
10901
|
sessionMgr,
|
|
10386
10902
|
terminalMgr,
|
|
@@ -10390,9 +10906,9 @@ async function createServer(configOverrides) {
|
|
|
10390
10906
|
providerRegistry,
|
|
10391
10907
|
fencingMgr,
|
|
10392
10908
|
supervisorMgr,
|
|
10909
|
+
autoFetch,
|
|
10393
10910
|
providerRuntimeDeps,
|
|
10394
|
-
providerInstallMgr
|
|
10395
|
-
codexConfigAudit
|
|
10911
|
+
providerInstallMgr
|
|
10396
10912
|
};
|
|
10397
10913
|
wsHub.setCommandContext(commandContext);
|
|
10398
10914
|
await app.listen({
|
|
@@ -10423,6 +10939,7 @@ async function createServer(configOverrides) {
|
|
|
10423
10939
|
stopped = true;
|
|
10424
10940
|
clearTimeout(gcTimer);
|
|
10425
10941
|
await app.close();
|
|
10942
|
+
autoFetch.stop();
|
|
10426
10943
|
supervisorMgr.stop();
|
|
10427
10944
|
terminalMgr.shutdown();
|
|
10428
10945
|
wsHub.destroy();
|
|
@@ -10544,9 +11061,10 @@ var init_server = __esm({
|
|
|
10544
11061
|
init_src2();
|
|
10545
11062
|
init_app();
|
|
10546
11063
|
init_event_bus();
|
|
10547
|
-
init_codex_config_audit();
|
|
10548
11064
|
init_config();
|
|
11065
|
+
init_auto_fetch();
|
|
10549
11066
|
init_command_runner();
|
|
11067
|
+
init_e2e_provider_mock();
|
|
10550
11068
|
init_install_manager();
|
|
10551
11069
|
init_manager();
|
|
10552
11070
|
init_db();
|
|
@@ -10563,6 +11081,7 @@ var init_server = __esm({
|
|
|
10563
11081
|
init_cleanup();
|
|
10564
11082
|
init_constants();
|
|
10565
11083
|
init_manager4();
|
|
11084
|
+
init_dispatch();
|
|
10566
11085
|
init_fencing();
|
|
10567
11086
|
init_hub();
|
|
10568
11087
|
init_commands();
|
|
@@ -10716,8 +11235,8 @@ var init_workspace_repo = __esm({
|
|
|
10716
11235
|
/**
|
|
10717
11236
|
* Finds a workspace by path
|
|
10718
11237
|
*/
|
|
10719
|
-
findByPath(
|
|
10720
|
-
const row = this.db.prepare("SELECT * FROM workspaces WHERE path = ?").get(
|
|
11238
|
+
findByPath(path10) {
|
|
11239
|
+
const row = this.db.prepare("SELECT * FROM workspaces WHERE path = ?").get(path10);
|
|
10721
11240
|
return row ? this.rowToWorkspace(row) : void 0;
|
|
10722
11241
|
}
|
|
10723
11242
|
/**
|
|
@@ -10861,11 +11380,11 @@ await init_src4();
|
|
|
10861
11380
|
|
|
10862
11381
|
// packages/cli/src/config-store.ts
|
|
10863
11382
|
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
10864
|
-
import { homedir as
|
|
11383
|
+
import { homedir as homedir4 } from "os";
|
|
10865
11384
|
import { basename as basename3, join as join8 } from "path";
|
|
10866
11385
|
var DEFAULT_DB_FILE = "coder-studio.db";
|
|
10867
11386
|
function getCliConfigPath() {
|
|
10868
|
-
return join8(
|
|
11387
|
+
return join8(homedir4(), ".coder-studio", "config.json");
|
|
10869
11388
|
}
|
|
10870
11389
|
function normalizeDataDir(input2) {
|
|
10871
11390
|
if (input2.endsWith(".db")) {
|
|
@@ -10877,12 +11396,12 @@ function normalizeDataDir(input2) {
|
|
|
10877
11396
|
return join8(input2, DEFAULT_DB_FILE);
|
|
10878
11397
|
}
|
|
10879
11398
|
function readCliConfig() {
|
|
10880
|
-
const
|
|
10881
|
-
if (!existsSync7(
|
|
11399
|
+
const path10 = getCliConfigPath();
|
|
11400
|
+
if (!existsSync7(path10)) {
|
|
10882
11401
|
return null;
|
|
10883
11402
|
}
|
|
10884
11403
|
try {
|
|
10885
|
-
const parsed = JSON.parse(readFileSync7(
|
|
11404
|
+
const parsed = JSON.parse(readFileSync7(path10, "utf-8"));
|
|
10886
11405
|
if (parsed.host !== void 0 && typeof parsed.host !== "string" || parsed.port !== void 0 && typeof parsed.port !== "number" || parsed.dataDir !== void 0 && typeof parsed.dataDir !== "string" || parsed.password !== void 0 && typeof parsed.password !== "string") {
|
|
10887
11406
|
return null;
|
|
10888
11407
|
}
|
|
@@ -10892,8 +11411,8 @@ function readCliConfig() {
|
|
|
10892
11411
|
}
|
|
10893
11412
|
}
|
|
10894
11413
|
function writeCliConfig(config) {
|
|
10895
|
-
const
|
|
10896
|
-
const dir = join8(
|
|
11414
|
+
const path10 = getCliConfigPath();
|
|
11415
|
+
const dir = join8(homedir4(), ".coder-studio");
|
|
10897
11416
|
const normalizedConfig = {
|
|
10898
11417
|
...config.host !== void 0 ? { host: config.host } : {},
|
|
10899
11418
|
...config.port !== void 0 && config.port > 0 ? { port: config.port } : {},
|
|
@@ -10903,7 +11422,7 @@ function writeCliConfig(config) {
|
|
|
10903
11422
|
if (!existsSync7(dir)) {
|
|
10904
11423
|
mkdirSync4(dir, { recursive: true });
|
|
10905
11424
|
}
|
|
10906
|
-
writeFileSync4(
|
|
11425
|
+
writeFileSync4(path10, JSON.stringify(normalizedConfig, null, 2), "utf-8");
|
|
10907
11426
|
}
|
|
10908
11427
|
|
|
10909
11428
|
// packages/cli/src/auth-control.ts
|
|
@@ -10971,26 +11490,26 @@ import { closeSync, existsSync as existsSync8, openSync, readSync, statSync as s
|
|
|
10971
11490
|
var DEFAULT_MAX_LINES = 40;
|
|
10972
11491
|
var DEFAULT_MAX_CHARS = 4e3;
|
|
10973
11492
|
var DEFAULT_MAX_BYTES = 16 * 1024;
|
|
10974
|
-
var getFileSize = (
|
|
10975
|
-
if (!existsSync8(
|
|
11493
|
+
var getFileSize = (path10) => {
|
|
11494
|
+
if (!existsSync8(path10)) {
|
|
10976
11495
|
return 0;
|
|
10977
11496
|
}
|
|
10978
11497
|
try {
|
|
10979
|
-
return statSync2(
|
|
11498
|
+
return statSync2(path10).size;
|
|
10980
11499
|
} catch {
|
|
10981
11500
|
return 0;
|
|
10982
11501
|
}
|
|
10983
11502
|
};
|
|
10984
|
-
var readLogExcerpt = (
|
|
11503
|
+
var readLogExcerpt = (path10, {
|
|
10985
11504
|
startOffset = 0,
|
|
10986
11505
|
maxBytes = DEFAULT_MAX_BYTES,
|
|
10987
11506
|
maxLines = DEFAULT_MAX_LINES,
|
|
10988
11507
|
maxChars = DEFAULT_MAX_CHARS
|
|
10989
11508
|
} = {}) => {
|
|
10990
|
-
if (!existsSync8(
|
|
11509
|
+
if (!existsSync8(path10)) {
|
|
10991
11510
|
return null;
|
|
10992
11511
|
}
|
|
10993
|
-
const fileSize = getFileSize(
|
|
11512
|
+
const fileSize = getFileSize(path10);
|
|
10994
11513
|
const safeOffset = startOffset > fileSize ? 0 : Math.max(0, startOffset);
|
|
10995
11514
|
if (fileSize === safeOffset) {
|
|
10996
11515
|
return null;
|
|
@@ -10998,7 +11517,7 @@ var readLogExcerpt = (path9, {
|
|
|
10998
11517
|
const bytesToRead = Math.min(fileSize - safeOffset, maxBytes);
|
|
10999
11518
|
const readStart = fileSize - bytesToRead;
|
|
11000
11519
|
const buffer = Buffer.allocUnsafe(bytesToRead);
|
|
11001
|
-
const fd = openSync(
|
|
11520
|
+
const fd = openSync(path10, "r");
|
|
11002
11521
|
let content = "";
|
|
11003
11522
|
let startsMidLine = false;
|
|
11004
11523
|
try {
|
|
@@ -11289,7 +11808,7 @@ function parseArgs(argv) {
|
|
|
11289
11808
|
// packages/cli/src/pm2-control.ts
|
|
11290
11809
|
init_runtime();
|
|
11291
11810
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
11292
|
-
import { homedir as
|
|
11811
|
+
import { homedir as homedir5 } from "os";
|
|
11293
11812
|
import { join as join9 } from "path";
|
|
11294
11813
|
var MANAGED_SERVER_NAME = "coder-studio-server";
|
|
11295
11814
|
var PM2_RESTART_DELAY_MS = 2e3;
|
|
@@ -11481,11 +12000,11 @@ var deleteManagedServerInSession = async (pm2, {
|
|
|
11481
12000
|
return true;
|
|
11482
12001
|
};
|
|
11483
12002
|
var ensureLogDirectory = () => {
|
|
11484
|
-
mkdirSync5(join9(
|
|
12003
|
+
mkdirSync5(join9(homedir5(), ".coder-studio", "logs"), { recursive: true });
|
|
11485
12004
|
};
|
|
11486
12005
|
var getLogPaths = () => ({
|
|
11487
|
-
outFile: join9(
|
|
11488
|
-
errFile: join9(
|
|
12006
|
+
outFile: join9(homedir5(), ".coder-studio", "logs", "server.out.log"),
|
|
12007
|
+
errFile: join9(homedir5(), ".coder-studio", "logs", "server.err.log")
|
|
11489
12008
|
});
|
|
11490
12009
|
var captureStartupLogOffsets = () => {
|
|
11491
12010
|
const { outFile, errFile } = getLogPaths();
|
|
@@ -11794,8 +12313,8 @@ function showLogs(status, {
|
|
|
11794
12313
|
errorsOnly = false
|
|
11795
12314
|
} = {}) {
|
|
11796
12315
|
const paths = errorsOnly ? [status.errFile] : [status.outFile, status.errFile];
|
|
11797
|
-
const contents = paths.filter((
|
|
11798
|
-
const content = readLogExcerpt(
|
|
12316
|
+
const contents = paths.filter((path10, index, paths2) => paths2.indexOf(path10) === index).flatMap((path10) => {
|
|
12317
|
+
const content = readLogExcerpt(path10, { maxLines: tail, maxChars: null });
|
|
11799
12318
|
return content ? [content] : [];
|
|
11800
12319
|
});
|
|
11801
12320
|
console.log(contents.length === 0 ? "No logs available." : contents.join("\n"));
|