@openspecui/server 2.2.0 → 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +255 -77
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createServer as createServer$1 } from "node:net";
|
|
2
2
|
import { serve } from "@hono/node-server";
|
|
3
|
-
import { CliExecutor, CodeEditorThemeSchema, ConfigManager, DASHBOARD_METRIC_KEYS, DashboardConfigSchema, OpenSpecAdapter, OpenSpecWatcher, OpsxKernel, PtyClientMessageSchema, ReactiveContext, TerminalConfigSchema, TerminalRendererEngineSchema, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, getDetectedProjectTools, getToolInitStates, getWatcherRuntimeStatus, initWatcherPool, isWatcherPoolInitialized, sniffGlobalCli } from "@openspecui/core";
|
|
3
|
+
import { CliExecutor, CodeEditorThemeSchema, ConfigManager, DASHBOARD_METRIC_KEYS, DashboardConfigSchema, GitConfigSchema, OpenSpecAdapter, OpenSpecWatcher, OpsxKernel, PtyClientMessageSchema, ReactiveContext, TerminalConfigSchema, TerminalRendererEngineSchema, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, getDetectedProjectTools, getToolInitStates, getWatcherRuntimeStatus, initWatcherPool, isWatcherPoolInitialized, sniffGlobalCli } from "@openspecui/core";
|
|
4
4
|
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
|
5
5
|
import { applyWSSHandler } from "@trpc/server/adapters/ws";
|
|
6
6
|
import { Hono } from "hono";
|
|
@@ -1359,27 +1359,54 @@ function createCliStreamObservable(startStream) {
|
|
|
1359
1359
|
const gitPanelCaches = {
|
|
1360
1360
|
overview: /* @__PURE__ */ new Map(),
|
|
1361
1361
|
entries: /* @__PURE__ */ new Map(),
|
|
1362
|
+
meta: /* @__PURE__ */ new Map(),
|
|
1362
1363
|
shell: /* @__PURE__ */ new Map(),
|
|
1364
|
+
files: /* @__PURE__ */ new Map(),
|
|
1365
|
+
snapshot: /* @__PURE__ */ new Map(),
|
|
1366
|
+
patch: /* @__PURE__ */ new Map()
|
|
1367
|
+
};
|
|
1368
|
+
const gitPanelPendingCaches = {
|
|
1369
|
+
overview: /* @__PURE__ */ new Map(),
|
|
1370
|
+
entries: /* @__PURE__ */ new Map(),
|
|
1371
|
+
meta: /* @__PURE__ */ new Map(),
|
|
1372
|
+
shell: /* @__PURE__ */ new Map(),
|
|
1373
|
+
files: /* @__PURE__ */ new Map(),
|
|
1374
|
+
snapshot: /* @__PURE__ */ new Map(),
|
|
1363
1375
|
patch: /* @__PURE__ */ new Map()
|
|
1364
1376
|
};
|
|
1365
1377
|
function buildCacheKey(projectDir, key) {
|
|
1366
1378
|
return `${resolve(projectDir)}::${key}`;
|
|
1367
1379
|
}
|
|
1368
|
-
function
|
|
1369
|
-
return
|
|
1380
|
+
function isImmutableCommitDetailCache(scope, key) {
|
|
1381
|
+
return (scope === "meta" || scope === "shell" || scope === "files" || scope === "snapshot" || scope === "patch") && key.startsWith("commit:");
|
|
1382
|
+
}
|
|
1383
|
+
function getCacheVersion(scope, key) {
|
|
1384
|
+
if (isImmutableCommitDetailCache(scope, key)) return "commit-detail:immutable";
|
|
1385
|
+
return `refresh:${getDashboardGitTaskStatus().lastFinishedAt ?? 0}`;
|
|
1370
1386
|
}
|
|
1371
1387
|
async function getCachedGitPanelValue(scope, projectDir, key, load) {
|
|
1372
1388
|
const cache = gitPanelCaches[scope];
|
|
1389
|
+
const pendingCache = gitPanelPendingCaches[scope];
|
|
1373
1390
|
const cacheKey = buildCacheKey(projectDir, key);
|
|
1374
|
-
const version = getCacheVersion();
|
|
1391
|
+
const version = getCacheVersion(scope, key);
|
|
1375
1392
|
const hit = cache.get(cacheKey);
|
|
1376
1393
|
if (hit && hit.version === version) return hit.value;
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1394
|
+
const pending = pendingCache.get(cacheKey);
|
|
1395
|
+
if (pending && pending.version === version) return pending.promise;
|
|
1396
|
+
const promise = load().then((value) => {
|
|
1397
|
+
cache.set(cacheKey, {
|
|
1398
|
+
version,
|
|
1399
|
+
value
|
|
1400
|
+
});
|
|
1401
|
+
return value;
|
|
1402
|
+
}).finally(() => {
|
|
1403
|
+
if (pendingCache.get(cacheKey)?.promise === promise) pendingCache.delete(cacheKey);
|
|
1404
|
+
});
|
|
1405
|
+
pendingCache.set(cacheKey, {
|
|
1379
1406
|
version,
|
|
1380
|
-
|
|
1407
|
+
promise
|
|
1381
1408
|
});
|
|
1382
|
-
return
|
|
1409
|
+
return promise;
|
|
1383
1410
|
}
|
|
1384
1411
|
|
|
1385
1412
|
//#endregion
|
|
@@ -1553,31 +1580,6 @@ function splitPatchLines(text) {
|
|
|
1553
1580
|
if (lines.at(-1) === "") lines.pop();
|
|
1554
1581
|
return lines;
|
|
1555
1582
|
}
|
|
1556
|
-
async function buildTrackedPatchFile(options) {
|
|
1557
|
-
const { worktreePath, file, runGit, selector } = options;
|
|
1558
|
-
const patchResult = await runGit(worktreePath, selector.type === "commit" ? [
|
|
1559
|
-
"show",
|
|
1560
|
-
"--patch",
|
|
1561
|
-
"--find-renames",
|
|
1562
|
-
"--format=",
|
|
1563
|
-
selector.hash,
|
|
1564
|
-
"--",
|
|
1565
|
-
file.path
|
|
1566
|
-
] : [
|
|
1567
|
-
"diff",
|
|
1568
|
-
"--patch",
|
|
1569
|
-
"--find-renames",
|
|
1570
|
-
"HEAD",
|
|
1571
|
-
"--",
|
|
1572
|
-
file.path
|
|
1573
|
-
]);
|
|
1574
|
-
const normalized = normalizePatchState(patchResult.stdout);
|
|
1575
|
-
return {
|
|
1576
|
-
...file,
|
|
1577
|
-
patch: normalized.patch,
|
|
1578
|
-
state: patchResult.ok ? normalized.state : "unavailable"
|
|
1579
|
-
};
|
|
1580
|
-
}
|
|
1581
1583
|
async function buildUntrackedPatchFile(worktreePath, file) {
|
|
1582
1584
|
try {
|
|
1583
1585
|
const buffer = await readFile(resolve(worktreePath, file.path));
|
|
@@ -1701,6 +1703,198 @@ async function loadGitEntryShell(options) {
|
|
|
1701
1703
|
runGit
|
|
1702
1704
|
});
|
|
1703
1705
|
}
|
|
1706
|
+
function buildSelectorCacheKey(selector) {
|
|
1707
|
+
return selector.type === "commit" ? `commit:${selector.hash}` : "uncommitted";
|
|
1708
|
+
}
|
|
1709
|
+
function buildTrackedPatchArgs(selector) {
|
|
1710
|
+
return selector.type === "commit" ? [
|
|
1711
|
+
"show",
|
|
1712
|
+
"--patch",
|
|
1713
|
+
"--find-renames",
|
|
1714
|
+
"--format=",
|
|
1715
|
+
selector.hash
|
|
1716
|
+
] : [
|
|
1717
|
+
"diff",
|
|
1718
|
+
"--patch",
|
|
1719
|
+
"--find-renames",
|
|
1720
|
+
"HEAD"
|
|
1721
|
+
];
|
|
1722
|
+
}
|
|
1723
|
+
function decodeGitPatchPathToken(token) {
|
|
1724
|
+
const trimmed = token.trim();
|
|
1725
|
+
if (!trimmed || trimmed === "/dev/null") return null;
|
|
1726
|
+
let value = trimmed;
|
|
1727
|
+
if (value.startsWith("\"") && value.endsWith("\"") && value.length >= 2) value = value.slice(1, -1).replace(/\\([\\"])/g, "$1").replace(/\\t/g, " ").replace(/\\n/g, "\n");
|
|
1728
|
+
if (value === "/dev/null") return null;
|
|
1729
|
+
if (value.startsWith("a/") || value.startsWith("b/")) return normalizeGitPath(value.slice(2));
|
|
1730
|
+
return normalizeGitPath(value);
|
|
1731
|
+
}
|
|
1732
|
+
function parseDiffGitHeaderPaths(line) {
|
|
1733
|
+
const rest = line.slice(11).trim();
|
|
1734
|
+
const quotedMatch = /^"a\/((?:[^"\\]|\\.)+)" "b\/((?:[^"\\]|\\.)+)"$/.exec(rest);
|
|
1735
|
+
if (quotedMatch) return {
|
|
1736
|
+
oldPath: decodeGitPatchPathToken(`"a/${quotedMatch[1] ?? ""}"`),
|
|
1737
|
+
newPath: decodeGitPatchPathToken(`"b/${quotedMatch[2] ?? ""}"`)
|
|
1738
|
+
};
|
|
1739
|
+
const plainMatch = /^a\/(.+?) b\/(.+)$/.exec(rest);
|
|
1740
|
+
if (plainMatch) return {
|
|
1741
|
+
oldPath: decodeGitPatchPathToken(`a/${plainMatch[1] ?? ""}`),
|
|
1742
|
+
newPath: decodeGitPatchPathToken(`b/${plainMatch[2] ?? ""}`)
|
|
1743
|
+
};
|
|
1744
|
+
return {
|
|
1745
|
+
oldPath: null,
|
|
1746
|
+
newPath: null
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
function splitTrackedPatchBlocks(stdout) {
|
|
1750
|
+
const lines = splitPatchLines(stdout);
|
|
1751
|
+
const blocks = [];
|
|
1752
|
+
let current = [];
|
|
1753
|
+
for (const line of lines) {
|
|
1754
|
+
if (line.startsWith("diff --git ")) {
|
|
1755
|
+
if (current.length > 0) blocks.push(current.join("\n"));
|
|
1756
|
+
current = [line];
|
|
1757
|
+
continue;
|
|
1758
|
+
}
|
|
1759
|
+
if (current.length > 0) current.push(line);
|
|
1760
|
+
}
|
|
1761
|
+
if (current.length > 0) blocks.push(current.join("\n"));
|
|
1762
|
+
return blocks;
|
|
1763
|
+
}
|
|
1764
|
+
function resolveTrackedPatchBlockIdentity(block) {
|
|
1765
|
+
const lines = splitPatchLines(block);
|
|
1766
|
+
const pathCandidates = /* @__PURE__ */ new Set();
|
|
1767
|
+
const fileIdCandidates = /* @__PURE__ */ new Set();
|
|
1768
|
+
let oldPath = null;
|
|
1769
|
+
let newPath = null;
|
|
1770
|
+
let renameFrom = null;
|
|
1771
|
+
let renameTo = null;
|
|
1772
|
+
const headerLine = lines[0];
|
|
1773
|
+
if (headerLine?.startsWith("diff --git ")) {
|
|
1774
|
+
const parsed = parseDiffGitHeaderPaths(headerLine);
|
|
1775
|
+
oldPath = parsed.oldPath;
|
|
1776
|
+
newPath = parsed.newPath;
|
|
1777
|
+
}
|
|
1778
|
+
for (const line of lines) {
|
|
1779
|
+
if (line.startsWith("rename from ") || line.startsWith("copy from ")) {
|
|
1780
|
+
renameFrom = normalizeGitPath(line.slice(line.indexOf(" from ") + 6).trim());
|
|
1781
|
+
continue;
|
|
1782
|
+
}
|
|
1783
|
+
if (line.startsWith("rename to ") || line.startsWith("copy to ")) {
|
|
1784
|
+
renameTo = normalizeGitPath(line.slice(line.indexOf(" to ") + 4).trim());
|
|
1785
|
+
continue;
|
|
1786
|
+
}
|
|
1787
|
+
if (line.startsWith("--- ")) {
|
|
1788
|
+
oldPath = decodeGitPatchPathToken(line.slice(4));
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1791
|
+
if (line.startsWith("+++ ")) newPath = decodeGitPatchPathToken(line.slice(4));
|
|
1792
|
+
}
|
|
1793
|
+
if (renameFrom) oldPath = renameFrom;
|
|
1794
|
+
if (renameTo) newPath = renameTo;
|
|
1795
|
+
if (newPath) {
|
|
1796
|
+
pathCandidates.add(newPath);
|
|
1797
|
+
fileIdCandidates.add(createGitFileId(newPath, null));
|
|
1798
|
+
}
|
|
1799
|
+
if (oldPath) {
|
|
1800
|
+
pathCandidates.add(oldPath);
|
|
1801
|
+
fileIdCandidates.add(createGitFileId(oldPath, null));
|
|
1802
|
+
}
|
|
1803
|
+
if (newPath && oldPath && newPath !== oldPath) fileIdCandidates.add(createGitFileId(newPath, oldPath));
|
|
1804
|
+
return {
|
|
1805
|
+
fileIdCandidates: [...fileIdCandidates],
|
|
1806
|
+
pathCandidates: [...pathCandidates]
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
function buildTrackedPatchLookup(files, stdout) {
|
|
1810
|
+
const trackedFiles = files.filter((file) => file.source === "tracked");
|
|
1811
|
+
const fileIds = new Set(trackedFiles.map((file) => file.fileId));
|
|
1812
|
+
const fileIdsByPath = /* @__PURE__ */ new Map();
|
|
1813
|
+
for (const file of trackedFiles) {
|
|
1814
|
+
const pathCandidates = new Set([file.path]);
|
|
1815
|
+
if (file.previousPath) pathCandidates.add(file.previousPath);
|
|
1816
|
+
for (const path of pathCandidates) {
|
|
1817
|
+
const current = fileIdsByPath.get(path) ?? [];
|
|
1818
|
+
current.push(file.fileId);
|
|
1819
|
+
fileIdsByPath.set(path, current);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
const patchByFileId = /* @__PURE__ */ new Map();
|
|
1823
|
+
for (const block of splitTrackedPatchBlocks(stdout)) {
|
|
1824
|
+
const identity = resolveTrackedPatchBlockIdentity(block);
|
|
1825
|
+
let matchedFileId = identity.fileIdCandidates.find((fileId) => fileIds.has(fileId)) ?? null;
|
|
1826
|
+
if (!matchedFileId) for (const path of identity.pathCandidates) {
|
|
1827
|
+
const candidates = fileIdsByPath.get(path);
|
|
1828
|
+
if (!candidates || candidates.length === 0) continue;
|
|
1829
|
+
matchedFileId = candidates.find((fileId) => !patchByFileId.has(fileId)) ?? candidates[0] ?? null;
|
|
1830
|
+
if (matchedFileId) break;
|
|
1831
|
+
}
|
|
1832
|
+
if (matchedFileId && !patchByFileId.has(matchedFileId)) patchByFileId.set(matchedFileId, block.trimEnd());
|
|
1833
|
+
}
|
|
1834
|
+
return patchByFileId;
|
|
1835
|
+
}
|
|
1836
|
+
function buildTrackedPatchFile(file, rawPatch, available) {
|
|
1837
|
+
const normalized = normalizePatchState(rawPatch ?? "");
|
|
1838
|
+
return {
|
|
1839
|
+
...file,
|
|
1840
|
+
patch: available ? normalized.patch : null,
|
|
1841
|
+
state: available ? normalized.state : "unavailable"
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
function countPatchLines(file) {
|
|
1845
|
+
return file.patch ? splitPatchLines(file.patch).length : 0;
|
|
1846
|
+
}
|
|
1847
|
+
function projectGitEntryFiles(snapshot, eagerPatchLineBudget) {
|
|
1848
|
+
if (eagerPatchLineBudget <= 0) return {
|
|
1849
|
+
files: snapshot.files,
|
|
1850
|
+
eagerFiles: [],
|
|
1851
|
+
eagerPatchLineBudget,
|
|
1852
|
+
eagerPatchLineCount: 0
|
|
1853
|
+
};
|
|
1854
|
+
const eagerFiles = [];
|
|
1855
|
+
let eagerPatchLineCount = 0;
|
|
1856
|
+
for (const file of snapshot.files) {
|
|
1857
|
+
if (eagerPatchLineCount >= eagerPatchLineBudget) break;
|
|
1858
|
+
const patch = snapshot.patchByFileId.get(file.fileId);
|
|
1859
|
+
if (!patch) continue;
|
|
1860
|
+
eagerFiles.push(patch);
|
|
1861
|
+
eagerPatchLineCount += countPatchLines(patch);
|
|
1862
|
+
}
|
|
1863
|
+
return {
|
|
1864
|
+
files: snapshot.files,
|
|
1865
|
+
eagerFiles,
|
|
1866
|
+
eagerPatchLineBudget,
|
|
1867
|
+
eagerPatchLineCount
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
async function buildGitEntrySnapshot(options) {
|
|
1871
|
+
const runGit = options.runGit ?? defaultRunGit;
|
|
1872
|
+
const resolvedProjectDir = resolve(options.projectDir);
|
|
1873
|
+
const shell = await loadGitEntryShell({
|
|
1874
|
+
...options,
|
|
1875
|
+
projectDir: resolvedProjectDir
|
|
1876
|
+
});
|
|
1877
|
+
if (!shell.entry) return {
|
|
1878
|
+
entry: null,
|
|
1879
|
+
files: [],
|
|
1880
|
+
patchByFileId: /* @__PURE__ */ new Map()
|
|
1881
|
+
};
|
|
1882
|
+
const trackedFiles = shell.files.filter((file) => file.source === "tracked");
|
|
1883
|
+
const trackedPatchPromise = trackedFiles.length > 0 ? runGit(resolvedProjectDir, buildTrackedPatchArgs(options.selector)) : Promise.resolve({
|
|
1884
|
+
ok: true,
|
|
1885
|
+
stdout: ""
|
|
1886
|
+
});
|
|
1887
|
+
const untrackedPatchPromise = Promise.all(shell.files.filter((file) => file.source === "untracked").map(async (file) => [file.fileId, await buildUntrackedPatchFile(resolvedProjectDir, file)]));
|
|
1888
|
+
const [trackedPatchResult, untrackedPatches] = await Promise.all([trackedPatchPromise, untrackedPatchPromise]);
|
|
1889
|
+
const trackedPatchLookup = trackedPatchResult.ok ? buildTrackedPatchLookup(shell.files, trackedPatchResult.stdout) : /* @__PURE__ */ new Map();
|
|
1890
|
+
const patchByFileId = new Map(untrackedPatches);
|
|
1891
|
+
for (const file of trackedFiles) patchByFileId.set(file.fileId, buildTrackedPatchFile(file, trackedPatchLookup.get(file.fileId) ?? null, trackedPatchResult.ok));
|
|
1892
|
+
return {
|
|
1893
|
+
entry: shell.entry,
|
|
1894
|
+
files: shell.files,
|
|
1895
|
+
patchByFileId
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1704
1898
|
async function buildGitWorktreeOverview(options) {
|
|
1705
1899
|
const resolvedProjectDir = resolve(options.projectDir);
|
|
1706
1900
|
return getCachedGitPanelValue("overview", resolvedProjectDir, "overview", async () => {
|
|
@@ -1757,52 +1951,32 @@ async function listCurrentWorktreeGitEntries(options) {
|
|
|
1757
1951
|
}
|
|
1758
1952
|
async function getCurrentWorktreeGitEntryShell(options) {
|
|
1759
1953
|
const resolvedProjectDir = resolve(options.projectDir);
|
|
1760
|
-
return getCachedGitPanelValue("shell", resolvedProjectDir, options.selector
|
|
1954
|
+
return getCachedGitPanelValue("shell", resolvedProjectDir, buildSelectorCacheKey(options.selector), () => loadGitEntryShell({
|
|
1761
1955
|
...options,
|
|
1762
1956
|
projectDir: resolvedProjectDir
|
|
1763
1957
|
}));
|
|
1764
1958
|
}
|
|
1765
|
-
async function
|
|
1959
|
+
async function getCurrentWorktreeGitEntryMeta(options) {
|
|
1766
1960
|
const resolvedProjectDir = resolve(options.projectDir);
|
|
1767
|
-
return getCachedGitPanelValue("
|
|
1768
|
-
|
|
1769
|
-
const shell = await getCurrentWorktreeGitEntryShell({
|
|
1961
|
+
return getCachedGitPanelValue("meta", resolvedProjectDir, buildSelectorCacheKey(options.selector), async () => {
|
|
1962
|
+
return (await getCurrentWorktreeGitEntryShell({
|
|
1770
1963
|
...options,
|
|
1771
1964
|
projectDir: resolvedProjectDir
|
|
1772
|
-
});
|
|
1773
|
-
const file = shell.files.find((candidate) => candidate.fileId === options.fileId) ?? null;
|
|
1774
|
-
if (!shell.entry || !file) return {
|
|
1775
|
-
entry: shell.entry,
|
|
1776
|
-
file: null
|
|
1777
|
-
};
|
|
1778
|
-
const patch = file.source === "untracked" ? await buildUntrackedPatchFile(resolvedProjectDir, file) : await buildTrackedPatchFile({
|
|
1779
|
-
worktreePath: resolvedProjectDir,
|
|
1780
|
-
file,
|
|
1781
|
-
runGit,
|
|
1782
|
-
selector: options.selector
|
|
1783
|
-
});
|
|
1784
|
-
return {
|
|
1785
|
-
entry: shell.entry,
|
|
1786
|
-
file: patch
|
|
1787
|
-
};
|
|
1965
|
+
})).entry;
|
|
1788
1966
|
});
|
|
1789
1967
|
}
|
|
1790
|
-
async function
|
|
1791
|
-
const
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
};
|
|
1796
|
-
const patches = await Promise.all(shell.files.map(async (file) => {
|
|
1797
|
-
return (await getCurrentWorktreeGitEntryPatch({
|
|
1798
|
-
...options,
|
|
1799
|
-
fileId: file.fileId
|
|
1800
|
-
})).file;
|
|
1968
|
+
async function getCurrentWorktreeGitEntrySnapshot(options) {
|
|
1969
|
+
const resolvedProjectDir = resolve(options.projectDir);
|
|
1970
|
+
return getCachedGitPanelValue("snapshot", resolvedProjectDir, buildSelectorCacheKey(options.selector), () => buildGitEntrySnapshot({
|
|
1971
|
+
...options,
|
|
1972
|
+
projectDir: resolvedProjectDir
|
|
1801
1973
|
}));
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1974
|
+
}
|
|
1975
|
+
async function getCurrentWorktreeGitEntryFiles(options) {
|
|
1976
|
+
return projectGitEntryFiles(await getCurrentWorktreeGitEntrySnapshot(options), options.eagerPatchLineBudget);
|
|
1977
|
+
}
|
|
1978
|
+
async function getCurrentWorktreeGitEntryPatch(options) {
|
|
1979
|
+
return { file: (await getCurrentWorktreeGitEntrySnapshot(options)).patchByFileId.get(options.fileId) ?? null };
|
|
1806
1980
|
}
|
|
1807
1981
|
|
|
1808
1982
|
//#endregion
|
|
@@ -2317,18 +2491,20 @@ const configRouter = router({
|
|
|
2317
2491
|
codeEditor: z.object({ theme: CodeEditorThemeSchema.optional() }).optional(),
|
|
2318
2492
|
appBaseUrl: z.string().optional(),
|
|
2319
2493
|
terminal: TerminalConfigSchema.omit({ rendererEngine: true }).partial().extend({ rendererEngine: TerminalRendererEngineSchema.optional() }).optional(),
|
|
2320
|
-
dashboard: DashboardConfigSchema.partial().optional()
|
|
2494
|
+
dashboard: DashboardConfigSchema.partial().optional(),
|
|
2495
|
+
git: GitConfigSchema.partial().optional()
|
|
2321
2496
|
})).mutation(async ({ ctx, input }) => {
|
|
2322
2497
|
const hasCliCommand = input.cli !== void 0 && Object.prototype.hasOwnProperty.call(input.cli, "command");
|
|
2323
2498
|
const hasCliArgs = input.cli !== void 0 && Object.prototype.hasOwnProperty.call(input.cli, "args");
|
|
2324
2499
|
if (hasCliCommand && !hasCliArgs) {
|
|
2325
2500
|
await ctx.configManager.setCliCommand(input.cli?.command ?? "");
|
|
2326
|
-
if (input.theme !== void 0 || input.codeEditor !== void 0 || input.appBaseUrl !== void 0 || input.terminal !== void 0 || input.dashboard !== void 0) await ctx.configManager.writeConfig({
|
|
2501
|
+
if (input.theme !== void 0 || input.codeEditor !== void 0 || input.appBaseUrl !== void 0 || input.terminal !== void 0 || input.dashboard !== void 0 || input.git !== void 0) await ctx.configManager.writeConfig({
|
|
2327
2502
|
theme: input.theme,
|
|
2328
2503
|
codeEditor: input.codeEditor,
|
|
2329
2504
|
appBaseUrl: input.appBaseUrl,
|
|
2330
2505
|
terminal: input.terminal,
|
|
2331
|
-
dashboard: input.dashboard
|
|
2506
|
+
dashboard: input.dashboard,
|
|
2507
|
+
git: input.git
|
|
2332
2508
|
});
|
|
2333
2509
|
return { success: true };
|
|
2334
2510
|
}
|
|
@@ -2893,16 +3069,18 @@ const gitRouter = router({
|
|
|
2893
3069
|
limit: input?.limit
|
|
2894
3070
|
});
|
|
2895
3071
|
}),
|
|
2896
|
-
|
|
2897
|
-
return
|
|
3072
|
+
getEntryMeta: publicProcedure.input(z.object({ selector: gitEntrySelectorSchema })).query(async ({ ctx, input }) => {
|
|
3073
|
+
return getCurrentWorktreeGitEntryMeta({
|
|
2898
3074
|
projectDir: ctx.projectDir,
|
|
2899
3075
|
selector: input.selector
|
|
2900
3076
|
});
|
|
2901
3077
|
}),
|
|
2902
|
-
|
|
2903
|
-
|
|
3078
|
+
getEntryFiles: publicProcedure.input(z.object({ selector: gitEntrySelectorSchema })).query(async ({ ctx, input }) => {
|
|
3079
|
+
const config = await ctx.configManager.readConfig();
|
|
3080
|
+
return getCurrentWorktreeGitEntryFiles({
|
|
2904
3081
|
projectDir: ctx.projectDir,
|
|
2905
|
-
selector: input.selector
|
|
3082
|
+
selector: input.selector,
|
|
3083
|
+
eagerPatchLineBudget: config.git.diffEagerLineBudget
|
|
2906
3084
|
});
|
|
2907
3085
|
}),
|
|
2908
3086
|
getEntryPatch: publicProcedure.input(z.object({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openspecui/server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"exports": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"tsdown": "^0.16.6",
|
|
38
38
|
"tsx": "^4.19.2",
|
|
39
39
|
"typescript": "^5.7.2",
|
|
40
|
-
"vitest": "^
|
|
40
|
+
"vitest": "^4.1.0"
|
|
41
41
|
},
|
|
42
42
|
"repository": {
|
|
43
43
|
"type": "git",
|