@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/queries.js
CHANGED
|
@@ -9,6 +9,13 @@ const FIRST_PARENT_DIFF = "--diff-merges=first-parent";
|
|
|
9
9
|
const VISIBLE_REFS = ["--branches", "--remotes", "--tags", "HEAD"];
|
|
10
10
|
async function runQuery(deps, config, request) {
|
|
11
11
|
const q = request.query;
|
|
12
|
+
if (q.kind === "model-catalog") {
|
|
13
|
+
try {
|
|
14
|
+
return await queryModelCatalog(deps);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return { ok: false, error: { code: "git-error", message: error instanceof Error ? error.message : String(error) } };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
12
19
|
if (q.kind === "dir-list" || q.kind === "file-content") {
|
|
13
20
|
const browse = await resolveBrowseRoot(deps, request.sessionId);
|
|
14
21
|
if (!browse.ok) return { ok: false, error: browse.error };
|
|
@@ -307,7 +314,8 @@ async function queryDirList(deps, root, q, isGitRepo) {
|
|
|
307
314
|
if (rel !== "" && !isSafePath(rel) || hasGitSegment(rel)) return { ok: false, error: { code: "invalid-path", message: rel } };
|
|
308
315
|
const dir = rel === "" ? root : join(root, rel);
|
|
309
316
|
const inside = await isInsideRoot(deps, root, dir, true);
|
|
310
|
-
if (
|
|
317
|
+
if (inside === "missing") return { ok: false, error: { code: "file-not-found", message: rel } };
|
|
318
|
+
if (inside === "outside") return { ok: false, error: { code: "invalid-path", message: rel } };
|
|
311
319
|
let raw;
|
|
312
320
|
try {
|
|
313
321
|
raw = await deps.fs.readdir(dir);
|
|
@@ -358,12 +366,13 @@ async function queryFileContent(deps, config, root, q) {
|
|
|
358
366
|
if (!isSafePath(q.path) || hasGitSegment(q.path)) return { ok: false, error: { code: "invalid-path", message: q.path } };
|
|
359
367
|
const file = join(root, q.path);
|
|
360
368
|
const inside = await isInsideRoot(deps, root, file, true);
|
|
361
|
-
if (
|
|
369
|
+
if (inside === "missing") return { ok: false, error: { code: "file-not-found", message: q.path } };
|
|
370
|
+
if (inside === "outside") return { ok: false, error: { code: "invalid-path", message: q.path } };
|
|
362
371
|
let info;
|
|
363
372
|
try {
|
|
364
373
|
info = await deps.fs.stat(file);
|
|
365
374
|
} catch (error) {
|
|
366
|
-
return { ok: false, error: { code: "git-error", message: error instanceof Error ? error.message : "stat failed" } };
|
|
375
|
+
return { ok: false, error: { code: isMissingPath(error) ? "file-not-found" : "git-error", message: error instanceof Error ? error.message : "stat failed" } };
|
|
367
376
|
}
|
|
368
377
|
const mime = imageMimeFor(q.path);
|
|
369
378
|
const cap = mime !== null ? config.maxBytes : Math.max(config.maxBytes, FILE_COPY_MAX_BYTES);
|
|
@@ -374,7 +383,7 @@ async function queryFileContent(deps, config, root, q) {
|
|
|
374
383
|
try {
|
|
375
384
|
buf = await deps.fs.readFile(file);
|
|
376
385
|
} catch (error) {
|
|
377
|
-
return { ok: false, error: { code: "git-error", message: error instanceof Error ? error.message : "read failed" } };
|
|
386
|
+
return { ok: false, error: { code: isMissingPath(error) ? "file-not-found" : "git-error", message: error instanceof Error ? error.message : "read failed" } };
|
|
378
387
|
}
|
|
379
388
|
if (buf.length > cap) return tooLarge;
|
|
380
389
|
if (mime !== null) {
|
|
@@ -383,19 +392,22 @@ async function queryFileContent(deps, config, root, q) {
|
|
|
383
392
|
if (isBinaryBuffer(buf)) return { ok: true, value: { kind: "file-content", path: q.path, variant: "binary", ...statFields } };
|
|
384
393
|
const content = buf.toString("utf8");
|
|
385
394
|
const lines = content === "" ? 0 : content.split("\n").length - (content.endsWith("\n") ? 1 : 0);
|
|
386
|
-
return { ok: true, value: { kind: "file-content", path: q.path, variant: "text", content, lines, ...statFields } };
|
|
395
|
+
return { ok: true, value: { kind: "file-content", path: q.path, variant: "text", content, lines, absolutePath: file, ...statFields } };
|
|
387
396
|
}
|
|
388
397
|
function hasGitSegment(path) {
|
|
389
398
|
return path.split(/[\\/]/).includes(".git");
|
|
390
399
|
}
|
|
400
|
+
function isMissingPath(error) {
|
|
401
|
+
return error?.code === "ENOENT";
|
|
402
|
+
}
|
|
391
403
|
async function isInsideRoot(deps, root, path, hideGit = false) {
|
|
392
404
|
try {
|
|
393
405
|
const real = await deps.fs.realpath(path);
|
|
394
406
|
const rootReal = await deps.fs.realpath(root);
|
|
395
|
-
if (real !== rootReal && !real.startsWith(rootReal + sep)) return
|
|
396
|
-
return !hideGit || !hasGitSegment(real.slice(rootReal.length + 1));
|
|
397
|
-
} catch {
|
|
398
|
-
return
|
|
407
|
+
if (real !== rootReal && !real.startsWith(rootReal + sep)) return "outside";
|
|
408
|
+
return !hideGit || !hasGitSegment(real.slice(rootReal.length + 1)) ? "inside" : "outside";
|
|
409
|
+
} catch (error) {
|
|
410
|
+
return isMissingPath(error) ? "missing" : "outside";
|
|
399
411
|
}
|
|
400
412
|
}
|
|
401
413
|
function isBinaryBuffer(buf) {
|
|
@@ -501,6 +513,54 @@ async function queryWorktreeStats(deps, config, sessionId) {
|
|
|
501
513
|
if (!snapshot.ok) return { ok: false, error: { code: "git-error", message: "snapshot failed" } };
|
|
502
514
|
return { ok: true, value: { kind: "worktree-stats", stats: snapshot.value.stats } };
|
|
503
515
|
}
|
|
516
|
+
async function queryModelCatalog(deps) {
|
|
517
|
+
const llm = deps.getLlm?.();
|
|
518
|
+
const empty = { kind: "model-catalog", groups: [], failures: [] };
|
|
519
|
+
if (llm === void 0 || typeof llm.listModels !== "function") {
|
|
520
|
+
return { ok: true, value: empty };
|
|
521
|
+
}
|
|
522
|
+
const listModels = llm.listModels.bind(llm);
|
|
523
|
+
const resolveModelInfo = typeof llm.resolveModelInfo === "function" ? llm.resolveModelInfo.bind(llm) : void 0;
|
|
524
|
+
const providers = llm.listProviders?.() ?? [];
|
|
525
|
+
const results = await Promise.all(providers.map(async (provider) => {
|
|
526
|
+
const name = provider.name ?? provider.id;
|
|
527
|
+
let listed;
|
|
528
|
+
try {
|
|
529
|
+
listed = await listModels(provider.id);
|
|
530
|
+
} catch (error) {
|
|
531
|
+
return { id: provider.id, name, message: error instanceof Error ? error.message : String(error) };
|
|
532
|
+
}
|
|
533
|
+
const models = await Promise.all(listed.map(async (model) => {
|
|
534
|
+
const entry = { id: model.id, name: model.name ?? model.id };
|
|
535
|
+
if (resolveModelInfo === void 0) return entry;
|
|
536
|
+
try {
|
|
537
|
+
const info = await resolveModelInfo(provider.id, model.id, deps.signal);
|
|
538
|
+
const reasoning = info.reasoning;
|
|
539
|
+
if (reasoning !== void 0 && reasoning.efforts.length > 0) {
|
|
540
|
+
return {
|
|
541
|
+
...entry,
|
|
542
|
+
reasoning: {
|
|
543
|
+
efforts: reasoning.efforts.map((effort) => ({ id: effort.id, name: effort.name }))
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
} catch {
|
|
548
|
+
}
|
|
549
|
+
return entry;
|
|
550
|
+
}));
|
|
551
|
+
if (models.length === 0) return null;
|
|
552
|
+
return { id: provider.id, name, models };
|
|
553
|
+
}));
|
|
554
|
+
const groups = [];
|
|
555
|
+
const failures = [];
|
|
556
|
+
for (const one of results) {
|
|
557
|
+
if (one === null) continue;
|
|
558
|
+
if ("models" in one) groups.push(one);
|
|
559
|
+
else failures.push(one);
|
|
560
|
+
}
|
|
561
|
+
return { ok: true, value: { kind: "model-catalog", groups, failures } };
|
|
562
|
+
}
|
|
504
563
|
export {
|
|
564
|
+
isInsideRoot,
|
|
505
565
|
runQuery
|
|
506
566
|
};
|
package/lib/host/suggest.js
CHANGED
|
@@ -60,9 +60,23 @@ function systemPrompt(language) {
|
|
|
60
60
|
}
|
|
61
61
|
const UNTRACKED_FILE_CAP = 16;
|
|
62
62
|
const PROMPT_FILE_CAP = 200;
|
|
63
|
-
function
|
|
63
|
+
function routeNeedsSessionId(provider) {
|
|
64
|
+
return provider.toLowerCase().includes("opencode");
|
|
65
|
+
}
|
|
66
|
+
function resolveRoute(config, sessionModel, agentDefaultModel) {
|
|
64
67
|
if (config.suggestProvider !== void 0 && config.suggestModel !== void 0) {
|
|
65
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
provider: config.suggestProvider,
|
|
70
|
+
model: config.suggestModel,
|
|
71
|
+
...config.suggestReasoningEffort !== void 0 ? { reasoningEffort: config.suggestReasoningEffort } : {}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (sessionModel !== void 0) {
|
|
75
|
+
return {
|
|
76
|
+
provider: sessionModel.provider,
|
|
77
|
+
model: sessionModel.model,
|
|
78
|
+
...sessionModel.reasoningEffort !== void 0 ? { reasoningEffort: sessionModel.reasoningEffort } : {}
|
|
79
|
+
};
|
|
66
80
|
}
|
|
67
81
|
return agentDefaultModel?.currentSelection();
|
|
68
82
|
}
|
|
@@ -238,7 +252,7 @@ async function runSuggest(deps, config, request) {
|
|
|
238
252
|
diff = untracked.diff;
|
|
239
253
|
truncated = truncated || untracked.truncated;
|
|
240
254
|
const llm = deps.getLlm?.();
|
|
241
|
-
const route = resolveRoute(config, deps.getAgentDefaultModel?.());
|
|
255
|
+
const route = resolveRoute(config, deps.getSessionModel?.(request.sessionId), deps.getAgentDefaultModel?.());
|
|
242
256
|
if (llm === void 0 || route === void 0) {
|
|
243
257
|
return { ok: false, error: { code: "llm-unavailable", message: "no model service or route is configured" } };
|
|
244
258
|
}
|
|
@@ -252,6 +266,10 @@ async function runSuggest(deps, config, request) {
|
|
|
252
266
|
const options = {
|
|
253
267
|
provider: route.provider,
|
|
254
268
|
model: route.model,
|
|
269
|
+
// Catalog effort ids come from the same adapter surface GenerateOptions
|
|
270
|
+
// accepts; the brand is compile-time only (the runtime factory lives in
|
|
271
|
+
// @deepseek-ai/dsh-llm, which is externalized), so a cast is the boundary.
|
|
272
|
+
...route.reasoningEffort !== void 0 ? { reasoningEffort: route.reasoningEffort } : {},
|
|
255
273
|
system: systemPrompt(language),
|
|
256
274
|
messages: [{
|
|
257
275
|
id: "git-panel-suggest",
|
|
@@ -264,7 +282,8 @@ async function runSuggest(deps, config, request) {
|
|
|
264
282
|
}],
|
|
265
283
|
maxTokens: config.suggestMaxOutputTokens,
|
|
266
284
|
temperature: 0.3,
|
|
267
|
-
signal: deadline.signal
|
|
285
|
+
signal: deadline.signal,
|
|
286
|
+
...routeNeedsSessionId(route.provider) ? { sessionId: request.sessionId } : {}
|
|
268
287
|
};
|
|
269
288
|
let output = "";
|
|
270
289
|
let finish;
|
|
@@ -292,6 +311,7 @@ async function runSuggest(deps, config, request) {
|
|
|
292
311
|
message,
|
|
293
312
|
provider: route.provider,
|
|
294
313
|
model: route.model,
|
|
314
|
+
...route.reasoningEffort !== void 0 ? { reasoningEffort: route.reasoningEffort } : {},
|
|
295
315
|
...truncated ? { truncated: true } : {}
|
|
296
316
|
}
|
|
297
317
|
};
|
package/lib/host/types.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export type DiffViewMode = 'unified' | 'split';
|
|
|
9
9
|
* lane (git log --graph); `parallel` = keep each merge's first parent in its
|
|
10
10
|
* own lane, converging only at the ancestor (VSCode / GUI tools). */
|
|
11
11
|
export type GraphStyle = 'compact' | 'parallel';
|
|
12
|
+
/** Panel font-size step: -1 smaller, 0 default, +1 larger (CSS zoom on .gp-panel). */
|
|
13
|
+
export type FontDelta = -1 | 0 | 1;
|
|
12
14
|
/**
|
|
13
15
|
* Diff context (-U) values shared by both halves. `DEFAULT` matches git's own
|
|
14
16
|
* default and is what the host applies when a request omits `context`.
|
|
@@ -29,9 +31,16 @@ export declare const DIFF_EXPAND_CONTEXT = 2000;
|
|
|
29
31
|
* size the count reports 0 (unknown) and no trailing control is offered.
|
|
30
32
|
*/
|
|
31
33
|
export declare const DIFF_COUNT_READ_MAX: number;
|
|
32
|
-
/**
|
|
34
|
+
/** Hard ceiling for a `fullChanges` snapshot (the client's "expand all"):
|
|
35
|
+
* well above the default `maxChanges` (1000) so a big-but-sane change set
|
|
36
|
+
* expands completely, while an absurd one stays bounded on the wire. */
|
|
37
|
+
export declare const FULL_CHANGES_MAX = 10000;
|
|
38
|
+
/** Wire request: the browser sends only session identity, never a path.
|
|
39
|
+
* `fullChanges` asks the host to skip the `maxChanges` slice (still bounded
|
|
40
|
+
* by `FULL_CHANGES_MAX`) so the client can expand a truncated list once. */
|
|
33
41
|
export interface GitSnapshotRequest {
|
|
34
42
|
readonly sessionId: string;
|
|
43
|
+
readonly fullChanges?: boolean;
|
|
35
44
|
}
|
|
36
45
|
export type GitSnapshotResult = {
|
|
37
46
|
readonly ok: true;
|
|
@@ -116,6 +125,8 @@ export interface GitSnapshot {
|
|
|
116
125
|
readonly defaultDiffView: DiffViewMode;
|
|
117
126
|
/** Commit-graph line style (user preference). */
|
|
118
127
|
readonly graphStyle: GraphStyle;
|
|
128
|
+
/** Panel font-size step (user preference). */
|
|
129
|
+
readonly fontDelta: FontDelta;
|
|
119
130
|
/** Whether the commit box shows the AI-suggest button (user preference). */
|
|
120
131
|
readonly suggestEnabled: boolean;
|
|
121
132
|
/** SHA-256 of the final business snapshot, excluding checkedAt and itself.
|
|
@@ -173,6 +184,8 @@ export type GitAction = {
|
|
|
173
184
|
} | {
|
|
174
185
|
readonly kind: 'discard';
|
|
175
186
|
readonly paths: readonly string[];
|
|
187
|
+
} | {
|
|
188
|
+
readonly kind: 'discard-all';
|
|
176
189
|
} | {
|
|
177
190
|
readonly kind: 'commit';
|
|
178
191
|
readonly message: string;
|
|
@@ -228,7 +241,7 @@ export type GitAction = {
|
|
|
228
241
|
/** `git reset` modes, from least to most destructive. Single source so the
|
|
229
242
|
* client dialog and the command builder cannot drift. */
|
|
230
243
|
export type ResetMode = 'soft' | 'mixed' | 'hard';
|
|
231
|
-
export type GitErrorCode = 'cwd-unavailable' | 'not-a-git-repo' | 'git-unavailable' | 'invalid-path' | 'invalid-name' | 'invalid-index' | 'git-error' | 'timeout' | 'cancelled' | 'empty-message' | 'local-changes-block' | 'not-ff' | 'push-rejected' | 'auth-failed' | 'conflict' | 'revert-conflict' | 'revert-stuck' | 'revert-merge' | 'not-found' | 'branch-in-use' | 'stash-switch-failed' | 'index-busy' | 'nested-repo' | 'nested-repo-partial' | 'empty-diff' | 'llm-unavailable' | 'llm-error' | 'llm-output' | 'suggest-disabled';
|
|
244
|
+
export type GitErrorCode = 'cwd-unavailable' | 'not-a-git-repo' | 'git-unavailable' | 'invalid-path' | 'invalid-name' | 'invalid-index' | 'git-error' | 'timeout' | 'cancelled' | 'empty-message' | 'nothing-to-commit' | 'local-changes-block' | 'not-ff' | 'push-rejected' | 'auth-failed' | 'conflict' | 'revert-conflict' | 'revert-stuck' | 'revert-merge' | 'not-found' | 'file-not-found' | 'branch-in-use' | 'stash-switch-failed' | 'index-busy' | 'nested-repo' | 'nested-repo-partial' | 'status-lossy' | 'no-branch' | 'empty-diff' | 'llm-unavailable' | 'llm-error' | 'llm-output' | 'suggest-disabled';
|
|
232
245
|
export type GitActionResult = {
|
|
233
246
|
readonly ok: true;
|
|
234
247
|
readonly snapshot: GitSnapshot;
|
|
@@ -324,6 +337,8 @@ export type GitQuery = {
|
|
|
324
337
|
readonly kind: 'pull-preview';
|
|
325
338
|
} | {
|
|
326
339
|
readonly kind: 'push-preview';
|
|
340
|
+
} | {
|
|
341
|
+
readonly kind: 'model-catalog';
|
|
327
342
|
};
|
|
328
343
|
/** One entry in a `dir-list` result. */
|
|
329
344
|
export interface DirEntry {
|
|
@@ -358,6 +373,33 @@ export interface StashEntry {
|
|
|
358
373
|
/** Human relative time (e.g. "2 hours ago"). */
|
|
359
374
|
readonly relTime: string;
|
|
360
375
|
}
|
|
376
|
+
/** Selectable reasoning effort for one exact model route (ids go straight
|
|
377
|
+
* into `GenerateOptions.reasoningEffort`). */
|
|
378
|
+
export interface ModelCatalogEffort {
|
|
379
|
+
readonly id: string;
|
|
380
|
+
readonly name: string;
|
|
381
|
+
}
|
|
382
|
+
/** One model inside a provider group of the host-level catalog. */
|
|
383
|
+
export interface ModelCatalogModel {
|
|
384
|
+
readonly id: string;
|
|
385
|
+
readonly name: string;
|
|
386
|
+
/** Adapter-advertised efforts; absent when the model exposes none. */
|
|
387
|
+
readonly reasoning?: {
|
|
388
|
+
readonly efforts: readonly ModelCatalogEffort[];
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
/** One provider and its successfully listed models. */
|
|
392
|
+
export interface ModelCatalogGroup {
|
|
393
|
+
readonly id: string;
|
|
394
|
+
readonly name: string;
|
|
395
|
+
readonly models: readonly ModelCatalogModel[];
|
|
396
|
+
}
|
|
397
|
+
/** One provider / model whose catalog lookup failed in isolation. */
|
|
398
|
+
export interface ModelCatalogFailure {
|
|
399
|
+
readonly id: string;
|
|
400
|
+
readonly name: string;
|
|
401
|
+
readonly message: string;
|
|
402
|
+
}
|
|
361
403
|
/** Working-tree statistics for the changes page header. */
|
|
362
404
|
export interface WorktreeStats {
|
|
363
405
|
/** Distinct changed-file paths (staged ∪ modified ∪ untracked). */
|
|
@@ -492,6 +534,10 @@ export type GitQueryResult = {
|
|
|
492
534
|
readonly files: number;
|
|
493
535
|
readonly insertions: number;
|
|
494
536
|
readonly deletions: number;
|
|
537
|
+
} | {
|
|
538
|
+
readonly kind: 'model-catalog';
|
|
539
|
+
readonly groups: readonly ModelCatalogGroup[];
|
|
540
|
+
readonly failures: readonly ModelCatalogFailure[];
|
|
495
541
|
} | {
|
|
496
542
|
readonly kind: 'dir-list';
|
|
497
543
|
readonly path: string;
|
|
@@ -505,6 +551,11 @@ export type GitQueryResult = {
|
|
|
505
551
|
readonly content?: string;
|
|
506
552
|
/** text variant: total line count. */
|
|
507
553
|
readonly lines?: number;
|
|
554
|
+
/** text variant: absolute path of this file — the rendered-Markdown
|
|
555
|
+
* image resolver needs the document's own location to resolve relative
|
|
556
|
+
* destinations onto the authenticated `api/file` route. Absent on older
|
|
557
|
+
* hosts; the client then keeps the alt-text fallback. */
|
|
558
|
+
readonly absolutePath?: string;
|
|
508
559
|
/** image variant: a `data:<mime>;base64,…` URL. */
|
|
509
560
|
readonly dataUrl?: string;
|
|
510
561
|
/** The file exceeded the byte cap, so no content is returned. */
|
|
@@ -591,6 +642,8 @@ export type GitSuggestResult = {
|
|
|
591
642
|
/** Actual provider/model route used (display only). */
|
|
592
643
|
readonly provider?: string;
|
|
593
644
|
readonly model?: string;
|
|
645
|
+
/** Effort actually sent; absent when the request omitted it. */
|
|
646
|
+
readonly reasoningEffort?: string;
|
|
594
647
|
/** The diff reached the model truncated: over the input cap, or cut
|
|
595
648
|
* by the per-command runner stdout cap before it. */
|
|
596
649
|
readonly truncated?: boolean;
|
|
@@ -609,6 +662,26 @@ export type GitSuggestResult = {
|
|
|
609
662
|
export declare const IMAGE_MIME: Readonly<Record<string, string>>;
|
|
610
663
|
/** MIME for a path's extension; null when it is not a served image type. */
|
|
611
664
|
export declare function imageMimeFor(path: string): string | null;
|
|
665
|
+
/**
|
|
666
|
+
* Hand one browsable file to the operating system so it opens outside the
|
|
667
|
+
* panel. The path is validated and confirmed inside the browse root before the
|
|
668
|
+
* OS sees it; `sessionId` picks that root.
|
|
669
|
+
*/
|
|
670
|
+
export interface GitOpenFileRequest {
|
|
671
|
+
readonly sessionId: string;
|
|
672
|
+
/** Browse-root-relative path of the file to open. */
|
|
673
|
+
readonly path: string;
|
|
674
|
+
}
|
|
675
|
+
export type GitOpenFileResult = {
|
|
676
|
+
readonly ok: true;
|
|
677
|
+
readonly absolutePath: string;
|
|
678
|
+
} | {
|
|
679
|
+
readonly ok: false;
|
|
680
|
+
readonly error: {
|
|
681
|
+
readonly code: GitErrorCode;
|
|
682
|
+
readonly message?: string;
|
|
683
|
+
};
|
|
684
|
+
};
|
|
612
685
|
export interface GitVersionRequest {
|
|
613
686
|
/** True → query the GitHub releases API; false/absent → local view only. */
|
|
614
687
|
readonly check?: boolean;
|
package/lib/host/types.js
CHANGED
|
@@ -2,6 +2,7 @@ const DIFF_CONTEXT_DEFAULT = 3;
|
|
|
2
2
|
const DIFF_CONTEXT_MAX = 1e5;
|
|
3
3
|
const DIFF_EXPAND_CONTEXT = 2e3;
|
|
4
4
|
const DIFF_COUNT_READ_MAX = 8 * 1024 * 1024;
|
|
5
|
+
const FULL_CHANGES_MAX = 1e4;
|
|
5
6
|
function isHistoryHashSearch(search) {
|
|
6
7
|
return /^[0-9a-fA-F]{7,40}$/.test(search.trim());
|
|
7
8
|
}
|
|
@@ -41,6 +42,7 @@ export {
|
|
|
41
42
|
DIFF_COUNT_READ_MAX,
|
|
42
43
|
DIFF_EXPAND_CONTEXT,
|
|
43
44
|
FILE_COPY_MAX_BYTES,
|
|
45
|
+
FULL_CHANGES_MAX,
|
|
44
46
|
IMAGE_MIME,
|
|
45
47
|
SUGGEST_LANGUAGES,
|
|
46
48
|
SUGGEST_LANGUAGE_DEFAULT,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbzbing/dsh-git-panel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "DeepSeek Harness (dsh) plugin: an IDE-style Git panel in the Web GUI — branch/history overview, uncommitted changes with commit & amend, diff stats, and a zsh-style branch marker in the input bar.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
33
|
"lib/client.js",
|
|
34
|
+
"lib/client.mermaid.js",
|
|
34
35
|
"lib/host",
|
|
35
36
|
"locale/*.json",
|
|
36
37
|
"icon.svg",
|
|
@@ -41,11 +42,12 @@
|
|
|
41
42
|
],
|
|
42
43
|
"scripts": {
|
|
43
44
|
"build": "node build.mjs",
|
|
45
|
+
"lint": "eslint .",
|
|
44
46
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
45
|
-
"test:unit": "node build.mjs && node --test
|
|
47
|
+
"test:unit": "node build.mjs && node --test test/unit/*.test.mjs",
|
|
46
48
|
"test:e2e": "node build.mjs && node test/e2e/setup.mjs && node test/e2e/all.mjs",
|
|
47
49
|
"test": "npm run test:unit && npm run test:e2e",
|
|
48
|
-
"check": "npm run
|
|
50
|
+
"check": "npm run lint && npm run typecheck && npm run test"
|
|
49
51
|
},
|
|
50
52
|
"dsh": {
|
|
51
53
|
"bundle": {
|
|
@@ -63,7 +65,10 @@
|
|
|
63
65
|
"compatibility": {
|
|
64
66
|
"dsh": ">=0.1.7-rc.2",
|
|
65
67
|
"dshReleases": {
|
|
66
|
-
"0.
|
|
68
|
+
"0.1.7-rc.2": "compatible",
|
|
69
|
+
"0.2.0-rc.2": "compatible",
|
|
70
|
+
"0.2.1-rc.1": "compatible",
|
|
71
|
+
"0.2.1-rc.2": "compatible"
|
|
67
72
|
}
|
|
68
73
|
}
|
|
69
74
|
},
|
|
@@ -84,14 +89,23 @@
|
|
|
84
89
|
"@deepseek-ai/dsh-typert-protocol": ">=0.1.2-rc.1",
|
|
85
90
|
"@deepseek-ai/schemastery": ">=3.18.4"
|
|
86
91
|
},
|
|
92
|
+
"peerDependenciesMeta": {
|
|
93
|
+
"@deepseek-ai/dsh": {
|
|
94
|
+
"optional": true
|
|
95
|
+
}
|
|
96
|
+
},
|
|
87
97
|
"devDependencies": {
|
|
88
98
|
"@types/node": "22.10.5",
|
|
89
99
|
"@types/react": "18.3.18",
|
|
90
100
|
"@types/react-dom": "18.3.5",
|
|
91
101
|
"esbuild": "0.24.2",
|
|
102
|
+
"eslint": "9.39.5",
|
|
103
|
+
"eslint-plugin-react-hooks": "7.1.1",
|
|
104
|
+
"mermaid": "12.2.0",
|
|
92
105
|
"playwright-core": "1.63.0",
|
|
93
106
|
"react": "18.3.1",
|
|
94
107
|
"react-dom": "18.3.1",
|
|
95
|
-
"typescript": "5.7.3"
|
|
108
|
+
"typescript": "5.7.3",
|
|
109
|
+
"typescript-eslint": "8.71.2"
|
|
96
110
|
}
|
|
97
111
|
}
|