@xbzbing/dsh-git-panel 1.7.0 → 1.8.1
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/README.en.md +35 -22
- package/README.md +35 -22
- package/lib/client.js +169 -43
- package/lib/client.mermaid.js +4338 -0
- package/lib/host/actions.js +44 -2
- package/lib/host/core.d.ts +18 -6
- package/lib/host/core.js +23 -9
- package/lib/host/index.d.ts +14 -2
- package/lib/host/index.js +79 -7
- package/lib/host/llm-face.d.ts +21 -0
- package/lib/host/open-file.d.ts +16 -0
- package/lib/host/open-file.js +29 -0
- package/lib/host/queries.d.ts +8 -0
- package/lib/host/queries.js +69 -9
- package/lib/host/suggest.js +24 -4
- package/lib/host/types.d.ts +75 -2
- package/lib/host/types.js +2 -0
- package/package.json +19 -5
package/lib/host/actions.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { join, sep } from "node:path";
|
|
2
2
|
import { mapWorkspaceFailure, resolveWorkspace, runCommand, snapshotForSession } from "./core.js";
|
|
3
|
+
import { parseStatus } from "./parser.js";
|
|
3
4
|
import { isSafeBranchName, isSafePath, isSafeRemoteName, isSafeRev } from "./validate.js";
|
|
4
5
|
function withPaths(prefixes, paths) {
|
|
5
6
|
if (paths.length === 0) return { error: "invalid-path", message: "no paths given" };
|
|
@@ -20,6 +21,8 @@ function planAction(action, unborn) {
|
|
|
20
21
|
return unborn ? { argv: [["git", "rm", "--cached", "-r", "--", "."]] } : { argv: [["git", "restore", "--staged", "--", "."]] };
|
|
21
22
|
case "discard":
|
|
22
23
|
return withPaths([["git", "restore", "--"]], action.paths);
|
|
24
|
+
case "discard-all":
|
|
25
|
+
return { error: "invalid-path", message: "discard-all must be resolved to paths before planning" };
|
|
23
26
|
case "commit": {
|
|
24
27
|
const message = action.message.trim();
|
|
25
28
|
const amend = action.amend === true;
|
|
@@ -93,6 +96,14 @@ function isIndexBusy(outcome) {
|
|
|
93
96
|
if (!("run" in outcome) || outcome.run.exitCode === 0) return false;
|
|
94
97
|
return /index\.lock|Unable to create.*index|another git process/i.test(outcome.run.stderr + outcome.run.stdout);
|
|
95
98
|
}
|
|
99
|
+
function probeFailure(outcome) {
|
|
100
|
+
if ("failure" in outcome) {
|
|
101
|
+
return { ok: false, error: { code: "git-unavailable", message: outcome.failure instanceof Error ? outcome.failure.message : String(outcome.failure) } };
|
|
102
|
+
}
|
|
103
|
+
if (outcome.run.cancelled) return { ok: false, error: { code: "cancelled" } };
|
|
104
|
+
if (outcome.run.timedOut) return { ok: false, error: { code: "timeout" } };
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
96
107
|
function scrubCredentials(text) {
|
|
97
108
|
return text.replace(/([a-z][a-z0-9+.-]*:\/\/)[^/@\s]*@/gi, "$1");
|
|
98
109
|
}
|
|
@@ -105,8 +116,8 @@ function classifyActionFailure(stdout, stderr, exitCode) {
|
|
|
105
116
|
if (/index\.lock|Unable to create.*index|another git process/i.test(combined)) {
|
|
106
117
|
return { code: "index-busy", message: err };
|
|
107
118
|
}
|
|
108
|
-
if (/nothing to commit|no changes added/i.test(combined)) {
|
|
109
|
-
return { code: "
|
|
119
|
+
if (/nothing to commit|nothing added to commit|no changes added/i.test(combined)) {
|
|
120
|
+
return { code: "nothing-to-commit", message: err };
|
|
110
121
|
}
|
|
111
122
|
if (/No such ref|not a valid reference|is not a stash|no tag|tag .* not found|unknown revision|bad revision|Could not parse object|ambiguous argument|did not match any file\(s\) known to git/i.test(combined)) {
|
|
112
123
|
return { code: "not-found", message: err || "not found" };
|
|
@@ -137,6 +148,16 @@ async function runAction(deps, config, request) {
|
|
|
137
148
|
const root = workspace.root;
|
|
138
149
|
const headProbe = await runCommand(deps.run, ["git", "rev-parse", "--verify", "HEAD"], root, "head-probe", deps.signal);
|
|
139
150
|
const unborn = !("run" in headProbe) || headProbe.run.exitCode !== 0;
|
|
151
|
+
if (request.action.kind === "publish") {
|
|
152
|
+
const headBroke = probeFailure(headProbe);
|
|
153
|
+
if (headBroke !== null) return headBroke;
|
|
154
|
+
if (unborn) return { ok: false, error: { code: "no-branch", message: "no commits to publish" } };
|
|
155
|
+
const branchProbe = await runCommand(deps.run, ["git", "symbolic-ref", "--quiet", "--short", "HEAD"], root, "publish-branch", deps.signal);
|
|
156
|
+
const branchBroke = probeFailure(branchProbe);
|
|
157
|
+
if (branchBroke !== null) return branchBroke;
|
|
158
|
+
const branch = "run" in branchProbe && branchProbe.run.exitCode === 0 ? branchProbe.run.stdout.trim() : "";
|
|
159
|
+
if (branch === "") return { ok: false, error: { code: "no-branch", message: "detached HEAD: no branch to publish" } };
|
|
160
|
+
}
|
|
140
161
|
const act = request.action;
|
|
141
162
|
if (act.kind === "stash-apply" || act.kind === "stash-pop" || act.kind === "stash-drop") {
|
|
142
163
|
if (!Number.isInteger(act.index) || act.index < 0) {
|
|
@@ -148,6 +169,27 @@ async function runAction(deps, config, request) {
|
|
|
148
169
|
return { ok: false, error: { code: "not-found", message: "stash entry changed; refresh and retry" } };
|
|
149
170
|
}
|
|
150
171
|
}
|
|
172
|
+
if (request.action.kind === "discard-all") {
|
|
173
|
+
const status = await runCommand(deps.run, ["git", "status", "--porcelain=v1", "-z", "--untracked-files=all"], root, "discard-all-status", deps.signal);
|
|
174
|
+
if ("failure" in status) return { ok: false, error: { code: "git-unavailable", message: status.failure instanceof Error ? status.failure.message : String(status.failure) } };
|
|
175
|
+
if (status.run.cancelled) return { ok: false, error: { code: "cancelled" } };
|
|
176
|
+
if (status.run.timedOut) return { ok: false, error: { code: "timeout" } };
|
|
177
|
+
if (status.run.exitCode !== 0) {
|
|
178
|
+
const failure = classifyActionFailure(status.run.stdout, status.run.stderr, status.run.exitCode ?? -1);
|
|
179
|
+
return { ok: false, error: failure };
|
|
180
|
+
}
|
|
181
|
+
if (status.run.stdoutLossy) {
|
|
182
|
+
return { ok: false, error: { code: "status-lossy", message: "status output was clipped; refusing to discard an incomplete change set" } };
|
|
183
|
+
}
|
|
184
|
+
const all = parseStatus(status.run.stdout);
|
|
185
|
+
const paths = [...new Set(all.filter((c) => !c.staged).map((c) => c.path))].sort();
|
|
186
|
+
if (paths.length === 0) {
|
|
187
|
+
const snapshot2 = await snapshotForSession(deps, config, request.sessionId);
|
|
188
|
+
if (!snapshot2.ok) return { ok: false, error: { code: "git-error", message: "snapshot after action failed" } };
|
|
189
|
+
return { ok: true, snapshot: snapshot2.value, output: "" };
|
|
190
|
+
}
|
|
191
|
+
request = { ...request, action: { kind: "discard", paths } };
|
|
192
|
+
}
|
|
151
193
|
if (request.action.kind === "discard") {
|
|
152
194
|
const removed = await discardUntracked(deps, config, root, request.sessionId, request.action.paths);
|
|
153
195
|
if (removed !== null) {
|
package/lib/host/core.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { GitRunner } from './git.js';
|
|
2
|
-
import type { AgentDefaultModelFace, LlmFace } from './llm-face.js';
|
|
3
|
-
import type { DiffViewMode, GraphStyle, GitChange, GitErrorCode, GitRemote, GitSnapshotResult, SuggestLanguage } from './types.js';
|
|
2
|
+
import type { AgentDefaultModelFace, LlmFace, SessionModelSelection } from './llm-face.js';
|
|
3
|
+
import type { DiffViewMode, FontDelta, GraphStyle, GitChange, GitErrorCode, GitRemote, GitSnapshotResult, SuggestLanguage } from './types.js';
|
|
4
4
|
export interface GitPanelConfig {
|
|
5
5
|
readonly timeoutMs: number;
|
|
6
6
|
/** Per-command stdout cap; also the per-side image-diff payload cap. */
|
|
@@ -13,6 +13,8 @@ export interface GitPanelConfig {
|
|
|
13
13
|
readonly defaultDiffView: DiffViewMode;
|
|
14
14
|
/** Commit-graph line style (parallel = VSCode-like, compact = git log). */
|
|
15
15
|
readonly graphStyle: GraphStyle;
|
|
16
|
+
/** Panel font-size step: -1 / 0 / +1. */
|
|
17
|
+
readonly fontDelta: FontDelta;
|
|
16
18
|
/** Whether the "AI suggest" button shows in the commit box. */
|
|
17
19
|
readonly suggestEnabled: boolean;
|
|
18
20
|
/** Language the generated message is written in; `auto` follows repo history. */
|
|
@@ -26,6 +28,9 @@ export interface GitPanelConfig {
|
|
|
26
28
|
/** Optional provider/model route override; absent → the agent default. */
|
|
27
29
|
readonly suggestProvider?: string;
|
|
28
30
|
readonly suggestModel?: string;
|
|
31
|
+
/** Adapter-owned effort for the fixed route; absent → omit from the request
|
|
32
|
+
* (the model / adapter default applies). Ignored when the route is not fixed. */
|
|
33
|
+
readonly suggestReasoningEffort?: string;
|
|
29
34
|
}
|
|
30
35
|
export declare const DEFAULT_CONFIG: GitPanelConfig;
|
|
31
36
|
export declare function normalizeConfig(raw: unknown): GitPanelConfig;
|
|
@@ -33,6 +38,8 @@ export declare function normalizeConfig(raw: unknown): GitPanelConfig;
|
|
|
33
38
|
export declare function readDiffView(value: unknown, fallback: DiffViewMode): DiffViewMode;
|
|
34
39
|
/** Read the commit-graph style field, unwrapping a schemastery volatile ref. */
|
|
35
40
|
export declare function readGraphStyle(value: unknown, fallback: GraphStyle): GraphStyle;
|
|
41
|
+
/** Read the panel font-size step, unwrapping a schemastery volatile ref. */
|
|
42
|
+
export declare function readFontDelta(value: unknown): FontDelta;
|
|
36
43
|
/**
|
|
37
44
|
* Read the commit-message language field, unwrapping a schemastery volatile
|
|
38
45
|
* ref. An unknown string (a hand-edited profile, or a value from a newer
|
|
@@ -94,14 +101,17 @@ export interface SnapshotDeps {
|
|
|
94
101
|
*/
|
|
95
102
|
readonly rootNegCache?: Map<string, number>;
|
|
96
103
|
/**
|
|
97
|
-
* Optional per-request resolvers for the host
|
|
98
|
-
*
|
|
99
|
-
*
|
|
104
|
+
* Optional per-request resolvers for the host model services (see
|
|
105
|
+
* llm-face.ts). Resolved on every suggest call — never frozen at
|
|
106
|
+
* construction — so a provider fiber that activates late or restarts is
|
|
100
107
|
* picked up. Absent in deployments without a model backend; the suggest
|
|
101
108
|
* endpoint then reports `llm-unavailable` instead of failing activation.
|
|
102
109
|
*/
|
|
103
110
|
readonly getLlm?: () => LlmFace | undefined;
|
|
104
111
|
readonly getAgentDefaultModel?: () => AgentDefaultModelFace | undefined;
|
|
112
|
+
/** The session's durable model selection (projection next); undefined when
|
|
113
|
+
* the session has never selected a model. */
|
|
114
|
+
readonly getSessionModel?: (sessionId: string) => SessionModelSelection | undefined;
|
|
105
115
|
}
|
|
106
116
|
/**
|
|
107
117
|
* Pick the primary remote from `git remote -v` output and parse its fetch URL.
|
|
@@ -156,6 +166,8 @@ export declare function mapWorkspaceFailure(failure: GitSnapshotResult & {
|
|
|
156
166
|
message?: string;
|
|
157
167
|
};
|
|
158
168
|
/** Produce a full snapshot for a session. */
|
|
159
|
-
export declare function snapshotForSession(deps: SnapshotDeps, config: GitPanelConfig, sessionId: string
|
|
169
|
+
export declare function snapshotForSession(deps: SnapshotDeps, config: GitPanelConfig, sessionId: string, opts?: {
|
|
170
|
+
readonly fullChanges?: boolean;
|
|
171
|
+
}): Promise<GitSnapshotResult>;
|
|
160
172
|
/** Max mtime among changed files (epoch ms), capped for large sets. */
|
|
161
173
|
export declare function maxChangeMtime(deps: SnapshotDeps, root: string, changes: readonly GitChange[], cap?: number): Promise<number | null>;
|
package/lib/host/core.js
CHANGED
|
@@ -3,7 +3,7 @@ import { commitFromFields, parseStatus, sumNumstat } from "./parser.js";
|
|
|
3
3
|
import { parseRemote } from "./remote.js";
|
|
4
4
|
import { snapshotFingerprint } from "./snapshot-fingerprint.js";
|
|
5
5
|
import { isSafePath } from "./validate.js";
|
|
6
|
-
import { SUGGEST_LANGUAGE_DEFAULT, SUGGEST_LANGUAGES, SUGGEST_LIMITS } from "./types.js";
|
|
6
|
+
import { SUGGEST_LANGUAGE_DEFAULT, SUGGEST_LANGUAGES, SUGGEST_LIMITS, FULL_CHANGES_MAX } from "./types.js";
|
|
7
7
|
const DEFAULT_CONFIG = {
|
|
8
8
|
timeoutMs: 8e3,
|
|
9
9
|
maxBytes: 4 * 1024 * 1024,
|
|
@@ -12,6 +12,7 @@ const DEFAULT_CONFIG = {
|
|
|
12
12
|
showInputPill: true,
|
|
13
13
|
defaultDiffView: "unified",
|
|
14
14
|
graphStyle: "parallel",
|
|
15
|
+
fontDelta: 0,
|
|
15
16
|
suggestEnabled: true,
|
|
16
17
|
suggestLanguage: SUGGEST_LANGUAGE_DEFAULT,
|
|
17
18
|
suggestMaxBytes: SUGGEST_LIMITS.maxBytes.default,
|
|
@@ -25,9 +26,14 @@ function normalizeConfig(raw) {
|
|
|
25
26
|
const raw2 = unwrapVolatile(v);
|
|
26
27
|
return typeof raw2 === "number" && Number.isFinite(raw2) ? Math.min(limit.max, Math.max(limit.min, Math.floor(raw2))) : limit.default;
|
|
27
28
|
};
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
29
|
+
const provider = unwrapVolatile(c.suggestProvider);
|
|
30
|
+
const model = unwrapVolatile(c.suggestModel);
|
|
31
|
+
const effort = unwrapVolatile(c.suggestReasoningEffort);
|
|
32
|
+
const route = typeof provider === "string" && provider !== "" && typeof model === "string" && model !== "" ? {
|
|
33
|
+
suggestProvider: provider,
|
|
34
|
+
suggestModel: model,
|
|
35
|
+
...typeof effort === "string" && effort !== "" ? { suggestReasoningEffort: effort } : {}
|
|
36
|
+
} : {};
|
|
31
37
|
return {
|
|
32
38
|
timeoutMs: num(c.timeoutMs, DEFAULT_CONFIG.timeoutMs),
|
|
33
39
|
maxBytes: num(c.maxBytes, DEFAULT_CONFIG.maxBytes),
|
|
@@ -36,6 +42,7 @@ function normalizeConfig(raw) {
|
|
|
36
42
|
showInputPill: readBool(c.showInputPill, DEFAULT_CONFIG.showInputPill),
|
|
37
43
|
defaultDiffView: readDiffView(c.defaultDiffView, DEFAULT_CONFIG.defaultDiffView),
|
|
38
44
|
graphStyle: readGraphStyle(c.graphStyle, DEFAULT_CONFIG.graphStyle),
|
|
45
|
+
fontDelta: readFontDelta(c.fontDelta),
|
|
39
46
|
suggestEnabled: readBool(c.suggestEnabled, DEFAULT_CONFIG.suggestEnabled),
|
|
40
47
|
suggestLanguage: readSuggestLanguage(c.suggestLanguage, DEFAULT_CONFIG.suggestLanguage),
|
|
41
48
|
suggestMaxBytes: clampLimit(c.suggestMaxBytes, SUGGEST_LIMITS.maxBytes),
|
|
@@ -55,6 +62,10 @@ function readGraphStyle(value, fallback) {
|
|
|
55
62
|
const raw = unwrapVolatile(value);
|
|
56
63
|
return raw === "parallel" || raw === "compact" ? raw : fallback;
|
|
57
64
|
}
|
|
65
|
+
function readFontDelta(value) {
|
|
66
|
+
const raw = unwrapVolatile(value);
|
|
67
|
+
return raw === -1 || raw === 1 ? raw : 0;
|
|
68
|
+
}
|
|
58
69
|
function readSuggestLanguage(value, fallback) {
|
|
59
70
|
const raw = unwrapVolatile(value);
|
|
60
71
|
return typeof raw === "string" && SUGGEST_LANGUAGES.includes(raw) ? raw : fallback;
|
|
@@ -170,7 +181,8 @@ function mapWorkspaceFailure(failure) {
|
|
|
170
181
|
const message = "detail" in error ? error.detail : void 0;
|
|
171
182
|
return { code: error.code, ...message !== void 0 ? { message } : {} };
|
|
172
183
|
}
|
|
173
|
-
async function snapshotForSession(deps, config, sessionId) {
|
|
184
|
+
async function snapshotForSession(deps, config, sessionId, opts) {
|
|
185
|
+
const changeLimit = opts?.fullChanges === true ? Math.max(config.maxChanges, FULL_CHANGES_MAX) : config.maxChanges;
|
|
174
186
|
const workspace = await resolveWorkspace(deps, sessionId);
|
|
175
187
|
if (!workspace.ok) {
|
|
176
188
|
const failure = workspace.failure;
|
|
@@ -209,7 +221,7 @@ async function snapshotForSession(deps, config, sessionId) {
|
|
|
209
221
|
let untrackedCollapsed = false;
|
|
210
222
|
const untrackedEntries = allChanges.filter((c) => c.status === "untracked");
|
|
211
223
|
const collapseCandidate = untrackedEntries.some((c) => c.path.includes("/"));
|
|
212
|
-
if (statusLossy || untrackedEntries.length >
|
|
224
|
+
if (statusLossy || untrackedEntries.length > changeLimit && collapseCandidate) {
|
|
213
225
|
const collapsed = await runCommand(deps.run, ["git", "status", "--porcelain=v1", "-z", "--untracked-files=normal"], root, "status-collapsed", deps.signal);
|
|
214
226
|
if ("run" in collapsed && !collapsed.run.timedOut && collapsed.run.exitCode === 0) {
|
|
215
227
|
const list = parseStatus(collapsed.run.stdout);
|
|
@@ -218,9 +230,9 @@ async function snapshotForSession(deps, config, sessionId) {
|
|
|
218
230
|
statusLossy = collapsed.run.stdoutLossy;
|
|
219
231
|
}
|
|
220
232
|
}
|
|
221
|
-
const truncated = allChanges.length >
|
|
222
|
-
const truncatedReason = statusLossy ? "lossy" : allChanges.length >
|
|
223
|
-
const changes = allChanges.length >
|
|
233
|
+
const truncated = allChanges.length > changeLimit || statusLossy;
|
|
234
|
+
const truncatedReason = statusLossy ? "lossy" : allChanges.length > changeLimit ? "cap" : void 0;
|
|
235
|
+
const changes = allChanges.length > changeLimit ? allChanges.slice(0, changeLimit) : allChanges;
|
|
224
236
|
let staged = 0;
|
|
225
237
|
let modified = 0;
|
|
226
238
|
let untracked = 0;
|
|
@@ -295,6 +307,7 @@ async function snapshotForSession(deps, config, sessionId) {
|
|
|
295
307
|
showInputPill: config.showInputPill,
|
|
296
308
|
defaultDiffView: config.defaultDiffView,
|
|
297
309
|
graphStyle: config.graphStyle,
|
|
310
|
+
fontDelta: config.fontDelta,
|
|
298
311
|
suggestEnabled: config.suggestEnabled,
|
|
299
312
|
checkedAt: Date.now()
|
|
300
313
|
};
|
|
@@ -323,6 +336,7 @@ export {
|
|
|
323
336
|
parsePrimaryRemote,
|
|
324
337
|
readBool,
|
|
325
338
|
readDiffView,
|
|
339
|
+
readFontDelta,
|
|
326
340
|
readGraphStyle,
|
|
327
341
|
readSuggestLanguage,
|
|
328
342
|
resolveBrowseRoot,
|
package/lib/host/index.d.ts
CHANGED
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
10
10
|
import type { Context } from '@deepseek-ai/cordis';
|
|
11
11
|
import Schema from '@deepseek-ai/schemastery';
|
|
12
|
-
import type { GitActionRequest, GitActionResult, GitQueryRequest, GitQueryResponse, GitSnapshotRequest, GitSnapshotResult, GitSuggestRequest, GitSuggestResult, GitVersionInfo, GitVersionRequest } from './types.js';
|
|
13
|
-
export type { GitSnapshot, GitSnapshotResult, GitSnapshotRequest, GitFailure, GitCommit, GraphCommit, GitRef, GitChange, GitChangeStatus, GitAction, GitActionRequest, GitActionResult, GitErrorCode, GitQuery, GitQueryRequest, GitQueryResponse, GitQueryResult, GitBranch, GitFileStat, WorktreeStats, StashEntry, GitVersionRequest, GitVersionInfo, DiffViewMode, GraphStyle, DirEntry, ResetMode, GitSuggestRequest, GitSuggestResult, GitRemote, RemoteHostKind, SuggestLanguage, ResolvedLanguage, } from './types.js';
|
|
12
|
+
import type { GitActionRequest, GitActionResult, GitOpenFileRequest, GitOpenFileResult, GitQueryRequest, GitQueryResponse, GitSnapshotRequest, GitSnapshotResult, GitSuggestRequest, GitSuggestResult, GitVersionInfo, GitVersionRequest } from './types.js';
|
|
13
|
+
export type { GitSnapshot, GitSnapshotResult, GitSnapshotRequest, GitFailure, GitCommit, GraphCommit, GitRef, GitChange, GitChangeStatus, GitAction, GitActionRequest, GitActionResult, GitErrorCode, GitQuery, GitQueryRequest, GitQueryResponse, GitQueryResult, GitBranch, GitFileStat, WorktreeStats, StashEntry, GitVersionRequest, GitVersionInfo, DiffViewMode, GraphStyle, DirEntry, ResetMode, GitSuggestRequest, GitSuggestResult, GitRemote, RemoteHostKind, SuggestLanguage, ResolvedLanguage, GitOpenFileRequest, GitOpenFileResult, ModelCatalogGroup, ModelCatalogModel, ModelCatalogEffort, ModelCatalogFailure, } from './types.js';
|
|
14
14
|
export { normalizeConfig, DEFAULT_CONFIG, snapshotForSession, resolveWorkspace, parsePrimaryRemote, readSuggestLanguage } from './core.js';
|
|
15
15
|
export { createGitRunner } from './git.js';
|
|
16
16
|
export { parseStatus, parseGraphLog, parseBranches, parseNameStatus, parseStashList, parseTags, sumNumstat } from './parser.js';
|
|
17
17
|
export { parseRemote, classifyHost } from './remote.js';
|
|
18
18
|
export { isSafePath, planAction, runAction, classifyActionFailure } from './actions.js';
|
|
19
|
+
export { openerArgv, canOpenWithSystem, openWithSystem } from './open-file.js';
|
|
19
20
|
export { runQuery } from './queries.js';
|
|
20
21
|
export { runSuggest, resolveSuggestLanguage, parseStyleHistory, isCjkText, compactLongLines, LONG_LINE_MAX_CHARS } from './suggest.js';
|
|
21
22
|
export { readVersionInfo, checkLatestVersion, compareVersions, parseRepository } from './version.js';
|
|
@@ -31,20 +32,28 @@ export declare class GitPanelService extends TypertRemoteService {
|
|
|
31
32
|
showInputPill: Schema<boolean, boolean, "volatile-defined">;
|
|
32
33
|
defaultDiffView: Schema<"split" | "unified", "split" | "unified", "volatile-defined">;
|
|
33
34
|
graphStyle: Schema<"compact" | "parallel", "compact" | "parallel", "volatile-defined">;
|
|
35
|
+
fontDelta: Schema<0 | 1 | -1, 0 | 1 | -1, "volatile-defined">;
|
|
34
36
|
suggestEnabled: Schema<boolean, boolean, "volatile-defined">;
|
|
35
37
|
suggestLanguage: Schema<"auto" | "zh" | "en", "auto" | "zh" | "en", "volatile-defined">;
|
|
36
38
|
suggestMaxBytes: Schema<number, number, "volatile-defined">;
|
|
37
39
|
suggestMaxOutputTokens: Schema<number, number, "volatile-defined">;
|
|
38
40
|
suggestTimeoutMs: Schema<number, number, "volatile-defined">;
|
|
41
|
+
suggestProvider: Schema<string, string, "volatile">;
|
|
42
|
+
suggestModel: Schema<string, string, "volatile">;
|
|
43
|
+
suggestReasoningEffort: Schema<string, string, "volatile">;
|
|
39
44
|
}>>, Schemastery.ObjectT<NoInfer<{
|
|
40
45
|
showInputPill: Schema<boolean, boolean, "volatile-defined">;
|
|
41
46
|
defaultDiffView: Schema<"split" | "unified", "split" | "unified", "volatile-defined">;
|
|
42
47
|
graphStyle: Schema<"compact" | "parallel", "compact" | "parallel", "volatile-defined">;
|
|
48
|
+
fontDelta: Schema<0 | 1 | -1, 0 | 1 | -1, "volatile-defined">;
|
|
43
49
|
suggestEnabled: Schema<boolean, boolean, "volatile-defined">;
|
|
44
50
|
suggestLanguage: Schema<"auto" | "zh" | "en", "auto" | "zh" | "en", "volatile-defined">;
|
|
45
51
|
suggestMaxBytes: Schema<number, number, "volatile-defined">;
|
|
46
52
|
suggestMaxOutputTokens: Schema<number, number, "volatile-defined">;
|
|
47
53
|
suggestTimeoutMs: Schema<number, number, "volatile-defined">;
|
|
54
|
+
suggestProvider: Schema<string, string, "volatile">;
|
|
55
|
+
suggestModel: Schema<string, string, "volatile">;
|
|
56
|
+
suggestReasoningEffort: Schema<string, string, "volatile">;
|
|
48
57
|
}>>, "plain">;
|
|
49
58
|
private readonly deps;
|
|
50
59
|
private config;
|
|
@@ -57,6 +66,9 @@ export declare class GitPanelService extends TypertRemoteService {
|
|
|
57
66
|
suggest(request: GitSuggestRequest, signal?: AbortSignal): Promise<GitSuggestResult>;
|
|
58
67
|
version(request: GitVersionRequest): Promise<GitVersionInfo>;
|
|
59
68
|
/** Re-read config so a live-edited volatile field (showInputPill) is current. */
|
|
69
|
+
/** Runs one OS-opener argv; null when the host has no subprocess service. */
|
|
70
|
+
private spawnOpener;
|
|
71
|
+
openFile(request: GitOpenFileRequest): Promise<GitOpenFileResult>;
|
|
60
72
|
private liveConfig;
|
|
61
73
|
private withSignal;
|
|
62
74
|
}
|
package/lib/host/index.js
CHANGED
|
@@ -46,12 +46,13 @@ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use
|
|
|
46
46
|
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
47
47
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
48
48
|
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
49
|
-
var _version_dec, _suggest_dec, _query_dec, _run_dec, _snapshot_dec, _a, _init;
|
|
49
|
+
var _openFile_dec, _version_dec, _suggest_dec, _query_dec, _run_dec, _snapshot_dec, _a, _init;
|
|
50
50
|
import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
51
51
|
import Schema from "@deepseek-ai/schemastery";
|
|
52
52
|
import { createGitRunner } from "./git.js";
|
|
53
53
|
import { createFsDeps } from "./fs-deps.js";
|
|
54
|
-
import { normalizeConfig, snapshotForSession, DEFAULT_CONFIG } from "./core.js";
|
|
54
|
+
import { normalizeConfig, resolveBrowseRoot, snapshotForSession, DEFAULT_CONFIG } from "./core.js";
|
|
55
|
+
import { canOpenWithSystem, openWithSystem } from "./open-file.js";
|
|
55
56
|
import { runAction } from "./actions.js";
|
|
56
57
|
import { runQuery } from "./queries.js";
|
|
57
58
|
import { runSuggest } from "./suggest.js";
|
|
@@ -61,16 +62,38 @@ import { createGitRunner as createGitRunner2 } from "./git.js";
|
|
|
61
62
|
import { parseStatus, parseGraphLog, parseBranches, parseNameStatus, parseStashList, parseTags, sumNumstat } from "./parser.js";
|
|
62
63
|
import { parseRemote, classifyHost } from "./remote.js";
|
|
63
64
|
import { isSafePath, planAction, runAction as runAction2, classifyActionFailure } from "./actions.js";
|
|
65
|
+
import { openerArgv, canOpenWithSystem as canOpenWithSystem2, openWithSystem as openWithSystem2 } from "./open-file.js";
|
|
64
66
|
import { runQuery as runQuery2 } from "./queries.js";
|
|
65
67
|
import { runSuggest as runSuggest2, resolveSuggestLanguage, parseStyleHistory, isCjkText, compactLongLines, LONG_LINE_MAX_CHARS } from "./suggest.js";
|
|
66
68
|
import { readVersionInfo as readVersionInfo2, checkLatestVersion as checkLatestVersion2, compareVersions, parseRepository } from "./version.js";
|
|
67
|
-
|
|
69
|
+
async function spawnDetached(subprocess, argv) {
|
|
70
|
+
try {
|
|
71
|
+
const handle = subprocess.spawn({
|
|
72
|
+
argv,
|
|
73
|
+
cwd: process.cwd(),
|
|
74
|
+
stdio: {
|
|
75
|
+
stdin: "ignore",
|
|
76
|
+
stdout: { collect: { maxBytes: 4096 } },
|
|
77
|
+
stderr: { collect: { maxBytes: 4096 } }
|
|
78
|
+
},
|
|
79
|
+
graceMs: 200
|
|
80
|
+
});
|
|
81
|
+
const outcome = await handle.done;
|
|
82
|
+
return outcome.exitCode;
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
class GitPanelService extends (_a = TypertRemoteService, _snapshot_dec = [Remote("snapshot")], _run_dec = [Remote("run")], _query_dec = [Remote("query")], _suggest_dec = [Remote("suggest")], _version_dec = [Remote("version")], _openFile_dec = [Remote("openFile")], _a) {
|
|
68
88
|
constructor(ctx, config) {
|
|
69
89
|
super(ctx, "gitPanel");
|
|
70
90
|
__runInitializers(_init, 5, this);
|
|
71
91
|
__publicField(this, "deps");
|
|
72
92
|
__publicField(this, "config");
|
|
73
93
|
__publicField(this, "rawConfig");
|
|
94
|
+
/** Re-read config so a live-edited volatile field (showInputPill) is current. */
|
|
95
|
+
/** Runs one OS-opener argv; null when the host has no subprocess service. */
|
|
96
|
+
__publicField(this, "spawnOpener", null);
|
|
74
97
|
this.rawConfig = config;
|
|
75
98
|
this.config = normalizeConfig(config);
|
|
76
99
|
this.deps = this.buildDeps(ctx, this.config);
|
|
@@ -84,8 +107,10 @@ class GitPanelService extends (_a = TypertRemoteService, _snapshot_dec = [Remote
|
|
|
84
107
|
const run = subprocess === void 0 ? { run: async () => {
|
|
85
108
|
throw new Error("subprocess service unavailable");
|
|
86
109
|
} } : createGitRunner(subprocess, config.timeoutMs, config.maxBytes);
|
|
110
|
+
this.spawnOpener = subprocess === void 0 ? null : (argv) => spawnDetached(subprocess, argv);
|
|
87
111
|
const sessions = get("sessions");
|
|
88
112
|
const persistence = get("sessionPersistence");
|
|
113
|
+
const projections = get("sessionProjections");
|
|
89
114
|
return {
|
|
90
115
|
run,
|
|
91
116
|
fs,
|
|
@@ -108,11 +133,29 @@ class GitPanelService extends (_a = TypertRemoteService, _snapshot_dec = [Remote
|
|
|
108
133
|
// a model backend, where the suggest endpoint reports `llm-unavailable`
|
|
109
134
|
// rather than failing activation (kept out of `static inject` on purpose).
|
|
110
135
|
getLlm: () => get("llm"),
|
|
111
|
-
getAgentDefaultModel: () => get("agentDefaultModel")
|
|
136
|
+
getAgentDefaultModel: () => get("agentDefaultModel"),
|
|
137
|
+
// Session model selection: projection `next` is pending ?? lastUsed —
|
|
138
|
+
// the route the next prompt in this session would use.
|
|
139
|
+
getSessionModel: (sessionId) => {
|
|
140
|
+
const session = sessions?.get(sessionId);
|
|
141
|
+
if (session === void 0 || projections === void 0) return void 0;
|
|
142
|
+
try {
|
|
143
|
+
const next = projections.snapshot(session, ["modelSelection"]).values?.modelSelection?.next;
|
|
144
|
+
if (next == null || typeof next.provider !== "string" || typeof next.model !== "string") return void 0;
|
|
145
|
+
if (next.provider === "" || next.model === "") return void 0;
|
|
146
|
+
return {
|
|
147
|
+
provider: next.provider,
|
|
148
|
+
model: next.model,
|
|
149
|
+
...typeof next.reasoningEffort === "string" && next.reasoningEffort !== "" ? { reasoningEffort: next.reasoningEffort } : {}
|
|
150
|
+
};
|
|
151
|
+
} catch {
|
|
152
|
+
return void 0;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
112
155
|
};
|
|
113
156
|
}
|
|
114
157
|
async snapshot(request, signal) {
|
|
115
|
-
return snapshotForSession(this.withSignal(signal), this.liveConfig(), request.sessionId);
|
|
158
|
+
return snapshotForSession(this.withSignal(signal), this.liveConfig(), request.sessionId, { fullChanges: request.fullChanges === true });
|
|
116
159
|
}
|
|
117
160
|
async run(request, signal) {
|
|
118
161
|
return runAction(this.withSignal(signal), this.liveConfig(), request);
|
|
@@ -130,7 +173,21 @@ class GitPanelService extends (_a = TypertRemoteService, _snapshot_dec = [Remote
|
|
|
130
173
|
return { current: "0.0.0", updateAvailable: false, checkedRemote: false, error: error instanceof Error ? error.message : "version unavailable" };
|
|
131
174
|
}
|
|
132
175
|
}
|
|
133
|
-
|
|
176
|
+
async openFile(request) {
|
|
177
|
+
if (!canOpenWithSystem(process.platform)) {
|
|
178
|
+
return { ok: false, error: { code: "git-error", message: "this host cannot open files in a desktop application" } };
|
|
179
|
+
}
|
|
180
|
+
if (this.spawnOpener === null) {
|
|
181
|
+
return { ok: false, error: { code: "git-error", message: "subprocess service unavailable" } };
|
|
182
|
+
}
|
|
183
|
+
const browse = await resolveBrowseRoot(this.withSignal(void 0), request.sessionId);
|
|
184
|
+
if (!browse.ok) return { ok: false, error: browse.error };
|
|
185
|
+
try {
|
|
186
|
+
return await openWithSystem(this.deps, browse.root, request.path, this.spawnOpener);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
return { ok: false, error: { code: "git-error", message: error instanceof Error ? error.message : String(error) } };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
134
191
|
liveConfig() {
|
|
135
192
|
this.config = normalizeConfig(this.rawConfig);
|
|
136
193
|
return this.config;
|
|
@@ -146,6 +203,7 @@ __decorateElement(_init, 1, "run", _run_dec, GitPanelService);
|
|
|
146
203
|
__decorateElement(_init, 1, "query", _query_dec, GitPanelService);
|
|
147
204
|
__decorateElement(_init, 1, "suggest", _suggest_dec, GitPanelService);
|
|
148
205
|
__decorateElement(_init, 1, "version", _version_dec, GitPanelService);
|
|
206
|
+
__decorateElement(_init, 1, "openFile", _openFile_dec, GitPanelService);
|
|
149
207
|
__decoratorMetadata(_init, GitPanelService);
|
|
150
208
|
__publicField(GitPanelService, "inject", ["subprocess", "sessions", "sessionPersistence"]);
|
|
151
209
|
/**
|
|
@@ -164,6 +222,11 @@ __publicField(GitPanelService, "Config", Schema.object({
|
|
|
164
222
|
Schema.const("parallel").description("\u5E73\u884C\u7EBF\uFF08VSCode \u98CE\u683C\uFF0C\u5206\u652F\u5404\u5360\u4E00\u5217\uFF09"),
|
|
165
223
|
Schema.const("compact").description("\u7D27\u51D1\uFF08git log \u98CE\u683C\uFF0C\u5171\u4EAB\u7956\u5148\u5408\u5E76\u4E3A\u4E00\u5217\uFF09")
|
|
166
224
|
]).default("parallel").volatile().description("\u63D0\u4EA4\u5386\u53F2\u56FE\u7EBF\u6761\u6837\u5F0F"),
|
|
225
|
+
fontDelta: Schema.union([
|
|
226
|
+
Schema.const(-1).description("\u8F83\u5C0F"),
|
|
227
|
+
Schema.const(0).description("\u9ED8\u8BA4"),
|
|
228
|
+
Schema.const(1).description("\u8F83\u5927")
|
|
229
|
+
]).default(0).volatile().description("\u9762\u677F\u5B57\u53F7"),
|
|
167
230
|
suggestEnabled: Schema.boolean().default(true).volatile().description("\u63D0\u4EA4\u6846\u663E\u793A AI \u751F\u6210\u6309\u94AE"),
|
|
168
231
|
suggestLanguage: Schema.union([
|
|
169
232
|
Schema.const("auto").description("\u81EA\u52A8\u8DDF\u968F\u4ED3\u5E93\u5386\u53F2\u63D0\u4EA4\u7684\u8BED\u8A00"),
|
|
@@ -174,13 +237,20 @@ __publicField(GitPanelService, "Config", Schema.object({
|
|
|
174
237
|
// a literal here could silently drift from what normalizeConfig enforces.
|
|
175
238
|
suggestMaxBytes: Schema.number().default(DEFAULT_CONFIG.suggestMaxBytes).volatile().description("AI \u751F\u6210\u8F93\u5165\u9884\u7B97\uFF08diff \u5B57\u8282\u4E0A\u9650\uFF0C16\u2013512 KiB\uFF09"),
|
|
176
239
|
suggestMaxOutputTokens: Schema.number().default(DEFAULT_CONFIG.suggestMaxOutputTokens).volatile().description("AI \u751F\u6210\u8F93\u51FA\u4E0A\u9650\uFF08tokens\uFF0C256\u201316384\uFF09"),
|
|
177
|
-
suggestTimeoutMs: Schema.number().default(DEFAULT_CONFIG.suggestTimeoutMs).volatile().description("AI \u751F\u6210\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF0C5\u2013120 \u79D2\uFF09")
|
|
240
|
+
suggestTimeoutMs: Schema.number().default(DEFAULT_CONFIG.suggestTimeoutMs).volatile().description("AI \u751F\u6210\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF0C5\u2013120 \u79D2\uFF09"),
|
|
241
|
+
// Fixed model route: both halves of the pair must be non-empty to take
|
|
242
|
+
// effect (normalizeConfig); effort is omitted from the request when blank.
|
|
243
|
+
// No .default(): without one the field stays optional in the object schema.
|
|
244
|
+
suggestProvider: Schema.string().volatile().description("AI \u751F\u6210\u56FA\u5B9A\u6A21\u578B\u7684 provider\uFF08\u7559\u7A7A = \u8DDF\u968F\u4F1A\u8BDD\u9ED8\u8BA4\uFF09"),
|
|
245
|
+
suggestModel: Schema.string().volatile().description("AI \u751F\u6210\u56FA\u5B9A\u6A21\u578B\u7684 model id\uFF08\u987B\u4E0E provider \u6210\u5BF9\uFF09"),
|
|
246
|
+
suggestReasoningEffort: Schema.string().volatile().description("AI \u751F\u6210\u56FA\u5B9A\u6A21\u578B\u7684\u63A8\u7406\u5F3A\u5EA6\uFF08\u7559\u7A7A = \u4E0D\u8BBE\u7F6E\uFF0C\u7528\u6A21\u578B\u9ED8\u8BA4\uFF09")
|
|
178
247
|
}));
|
|
179
248
|
var index_default = GitPanelService;
|
|
180
249
|
export {
|
|
181
250
|
DEFAULT_CONFIG2 as DEFAULT_CONFIG,
|
|
182
251
|
GitPanelService,
|
|
183
252
|
LONG_LINE_MAX_CHARS,
|
|
253
|
+
canOpenWithSystem2 as canOpenWithSystem,
|
|
184
254
|
checkLatestVersion2 as checkLatestVersion,
|
|
185
255
|
classifyActionFailure,
|
|
186
256
|
classifyHost,
|
|
@@ -191,6 +261,8 @@ export {
|
|
|
191
261
|
isCjkText,
|
|
192
262
|
isSafePath,
|
|
193
263
|
normalizeConfig2 as normalizeConfig,
|
|
264
|
+
openWithSystem2 as openWithSystem,
|
|
265
|
+
openerArgv,
|
|
194
266
|
parseBranches,
|
|
195
267
|
parseGraphLog,
|
|
196
268
|
parseNameStatus,
|
package/lib/host/llm-face.d.ts
CHANGED
|
@@ -16,7 +16,22 @@ export interface LlmFace {
|
|
|
16
16
|
stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
|
|
17
17
|
listProviders?(): readonly {
|
|
18
18
|
readonly id: string;
|
|
19
|
+
readonly name?: string;
|
|
19
20
|
}[];
|
|
21
|
+
/** Adapter-advertised models for one provider; optional on the wire. */
|
|
22
|
+
listModels?(provider: string): Promise<readonly {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly name?: string;
|
|
25
|
+
}[]>;
|
|
26
|
+
/** Exact-route metadata (reasoning efforts etc.); optional on the wire. */
|
|
27
|
+
resolveModelInfo?(provider: string, model: string, signal?: AbortSignal): Promise<{
|
|
28
|
+
readonly reasoning?: {
|
|
29
|
+
readonly efforts: readonly {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly name: string;
|
|
32
|
+
}[];
|
|
33
|
+
};
|
|
34
|
+
}>;
|
|
20
35
|
}
|
|
21
36
|
/** Minimal face of the host `agentDefaultModel` service. */
|
|
22
37
|
export interface AgentDefaultModelFace {
|
|
@@ -25,3 +40,9 @@ export interface AgentDefaultModelFace {
|
|
|
25
40
|
readonly model: string;
|
|
26
41
|
} | undefined;
|
|
27
42
|
}
|
|
43
|
+
/** One session's durable model selection (projection `next`: pending, else lastUsed). */
|
|
44
|
+
export interface SessionModelSelection {
|
|
45
|
+
readonly provider: string;
|
|
46
|
+
readonly model: string;
|
|
47
|
+
readonly reasoningEffort?: string;
|
|
48
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SnapshotDeps } from './core.js';
|
|
2
|
+
import type { GitOpenFileResult } from './types.js';
|
|
3
|
+
/** Platform argv that opens one file with its default handler. */
|
|
4
|
+
export declare function openerArgv(platform: string, target: string): readonly string[] | null;
|
|
5
|
+
/** Whether this host can hand a file to the desktop at all. */
|
|
6
|
+
export declare function canOpenWithSystem(platform: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Resolve `path` inside `root` and open it with the OS default application.
|
|
9
|
+
*
|
|
10
|
+
* @param deps Host fs face (realpath containment).
|
|
11
|
+
* @param root Browse root for the session.
|
|
12
|
+
* @param path Browse-root-relative path from the client (untrusted).
|
|
13
|
+
* @param spawn Runs one argv and resolves with its exit code, or null on failure.
|
|
14
|
+
* @returns The absolute path on success, a typed failure otherwise.
|
|
15
|
+
*/
|
|
16
|
+
export declare function openWithSystem(deps: SnapshotDeps, root: string, path: string, spawn: (argv: readonly string[]) => Promise<number | null>, platform?: string): Promise<GitOpenFileResult>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { isSafePath } from "./validate.js";
|
|
3
|
+
import { isInsideRoot } from "./queries.js";
|
|
4
|
+
function openerArgv(platform, target) {
|
|
5
|
+
if (platform === "darwin") return ["open", target];
|
|
6
|
+
if (platform === "win32") return ["rundll32", "url.dll,FileProtocolHandler", target];
|
|
7
|
+
if (platform === "linux") return ["xdg-open", target];
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
function canOpenWithSystem(platform) {
|
|
11
|
+
return openerArgv(platform, "x") !== null;
|
|
12
|
+
}
|
|
13
|
+
async function openWithSystem(deps, root, path, spawn, platform = process.platform) {
|
|
14
|
+
if (!isSafePath(path)) return { ok: false, error: { code: "invalid-path", message: path } };
|
|
15
|
+
const file = join(root, path);
|
|
16
|
+
const inside = await isInsideRoot(deps, root, file, true);
|
|
17
|
+
if (inside === "missing") return { ok: false, error: { code: "file-not-found", message: path } };
|
|
18
|
+
if (inside !== "inside") return { ok: false, error: { code: "invalid-path", message: path } };
|
|
19
|
+
const argv = openerArgv(platform, file);
|
|
20
|
+
if (argv === null) return { ok: false, error: { code: "git-error", message: "no system opener on this platform" } };
|
|
21
|
+
const code = await spawn(argv);
|
|
22
|
+
if (code !== 0) return { ok: false, error: { code: "git-error", message: `the system opener exited ${String(code)}` } };
|
|
23
|
+
return { ok: true, absolutePath: file };
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
canOpenWithSystem,
|
|
27
|
+
openWithSystem,
|
|
28
|
+
openerArgv
|
|
29
|
+
};
|
package/lib/host/queries.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import type { SnapshotDeps, GitPanelConfig } from './core.js';
|
|
2
2
|
import type { GitQueryRequest, GitQueryResponse } from './types.js';
|
|
3
3
|
export declare function runQuery(deps: SnapshotDeps, config: GitPanelConfig, request: GitQueryRequest): Promise<GitQueryResponse>;
|
|
4
|
+
/** Whether a path resolves inside the browse root, outside its .git tree, or
|
|
5
|
+
* is absent (a deleted entry still listed by a stale tree). */
|
|
6
|
+
export type PathState = 'inside' | 'outside' | 'missing';
|
|
7
|
+
/**
|
|
8
|
+
* Where `path` sits relative to `root` after symlinks are followed: the one
|
|
9
|
+
* containment check shared by every endpoint that touches a browsable file.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isInsideRoot(deps: SnapshotDeps, root: string, path: string, hideGit?: boolean): Promise<PathState>;
|