dsh-easygit-plugin 0.2.0
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/LICENSE +21 -0
- package/README.md +131 -0
- package/README.zh-CN.md +131 -0
- package/SECURITY.md +23 -0
- package/assets/preview.png +0 -0
- package/git-guide.cordis.yml +14 -0
- package/lib/client.js +2064 -0
- package/lib/index.js +1705 -0
- package/lib/types/client/index.d.ts +34 -0
- package/lib/types/client/panel-controller.d.ts +38 -0
- package/lib/types/client/view-model.d.ts +99 -0
- package/lib/types/host/actions.d.ts +41 -0
- package/lib/types/host/command-policy.d.ts +38 -0
- package/lib/types/host/git-repository-service.d.ts +53 -0
- package/lib/types/host/index.d.ts +3 -0
- package/lib/types/host/plugin.d.ts +109 -0
- package/lib/types/host/proposal-service.d.ts +60 -0
- package/lib/types/shared/contracts.d.ts +282 -0
- package/package.json +79 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createPanelController } from './panel-controller';
|
|
2
|
+
import { appendCommandLog, beginTrackedRequest, buildFileTree, cancelTrackedRequest, clampWorkbenchRatio, commitFileTone, deriveCommitGraph, filterLocalBranches, isCurrentCommitRequest, isLatestRequest, isTrackedRequestCurrent, mutationCommand, nextCommitSelection, parseReviewRows, repositoryName, type AnyRecord } from './view-model';
|
|
3
|
+
type RefreshState = 'idle' | 'loading' | 'succeeded' | 'failed';
|
|
4
|
+
declare function injectStyles(): () => void;
|
|
5
|
+
declare function refreshButtonLabel(state: RefreshState): string;
|
|
6
|
+
declare function renderRawDiffSurface(diff: string): any;
|
|
7
|
+
declare function renderReviewSurface(diff: string): any;
|
|
8
|
+
declare const plugin: {
|
|
9
|
+
inject: string[];
|
|
10
|
+
apply(ctx: AnyRecord): void;
|
|
11
|
+
__testing: {
|
|
12
|
+
createPanelController: typeof createPanelController;
|
|
13
|
+
buildFileTree: typeof buildFileTree;
|
|
14
|
+
parseReviewRows: typeof parseReviewRows;
|
|
15
|
+
renderRawDiffSurface: typeof renderRawDiffSurface;
|
|
16
|
+
renderReviewSurface: typeof renderReviewSurface;
|
|
17
|
+
injectStyles: typeof injectStyles;
|
|
18
|
+
filterLocalBranches: typeof filterLocalBranches;
|
|
19
|
+
clampWorkbenchRatio: typeof clampWorkbenchRatio;
|
|
20
|
+
deriveCommitGraph: typeof deriveCommitGraph;
|
|
21
|
+
repositoryName: typeof repositoryName;
|
|
22
|
+
mutationCommand: typeof mutationCommand;
|
|
23
|
+
appendCommandLog: typeof appendCommandLog;
|
|
24
|
+
refreshButtonLabel: typeof refreshButtonLabel;
|
|
25
|
+
isCurrentCommitRequest: typeof isCurrentCommitRequest;
|
|
26
|
+
nextCommitSelection: typeof nextCommitSelection;
|
|
27
|
+
commitFileTone: typeof commitFileTone;
|
|
28
|
+
isLatestRequest: typeof isLatestRequest;
|
|
29
|
+
beginTrackedRequest: typeof beginTrackedRequest;
|
|
30
|
+
cancelTrackedRequest: typeof cancelTrackedRequest;
|
|
31
|
+
isTrackedRequestCurrent: typeof isTrackedRequestCurrent;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export = plugin;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type Dispose = () => void;
|
|
2
|
+
export interface PanelSnapshot {
|
|
3
|
+
detailsReady: boolean;
|
|
4
|
+
activeSessionId: string | null;
|
|
5
|
+
open: boolean;
|
|
6
|
+
error: string;
|
|
7
|
+
}
|
|
8
|
+
export interface PanelController {
|
|
9
|
+
attachDetails(): Dispose;
|
|
10
|
+
open(sessionId: unknown): boolean;
|
|
11
|
+
close(sessionId?: unknown): boolean;
|
|
12
|
+
toggle(sessionId: unknown): boolean;
|
|
13
|
+
isOpen(sessionId: unknown): boolean;
|
|
14
|
+
subscribe(listener: (state: PanelSnapshot) => void): Dispose;
|
|
15
|
+
snapshot(): PanelSnapshot;
|
|
16
|
+
}
|
|
17
|
+
interface SlotsLike {
|
|
18
|
+
register(definition: Record<string, unknown>, renderer: (props: Record<string, unknown>) => unknown): unknown;
|
|
19
|
+
}
|
|
20
|
+
interface LayoutLike {
|
|
21
|
+
openDetails(): void;
|
|
22
|
+
closeDetails(): void;
|
|
23
|
+
}
|
|
24
|
+
export interface PanelControllerOptions {
|
|
25
|
+
slots?: SlotsLike | null;
|
|
26
|
+
layout?: LayoutLike | null;
|
|
27
|
+
renderPanel: (props: {
|
|
28
|
+
sessionId: string;
|
|
29
|
+
close: Dispose;
|
|
30
|
+
}) => unknown;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Mount the workbench only while it is open. Harness's built-in DetailsPanel
|
|
34
|
+
* uses priority 0, so the workbench temporarily overrides it at -10 and
|
|
35
|
+
* immediately releases the registration when closed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createPanelController(options: PanelControllerOptions): PanelController;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { BranchSummary, CommitSummary, RepositoryFile } from '../shared/contracts';
|
|
2
|
+
export type AnyRecord = Record<string, any>;
|
|
3
|
+
export type RepositoryMutationAction = 'stage-paths' | 'unstage-paths' | 'stage-all' | 'unstage-all' | 'commit' | 'create-branch' | 'switch-branch' | 'delete-branch';
|
|
4
|
+
export interface CommandLogEntry {
|
|
5
|
+
id: number;
|
|
6
|
+
label: string;
|
|
7
|
+
command: string;
|
|
8
|
+
status: 'running' | 'succeeded' | 'failed';
|
|
9
|
+
}
|
|
10
|
+
export interface RequestSlot {
|
|
11
|
+
controller: AbortController | null;
|
|
12
|
+
sequence: number;
|
|
13
|
+
}
|
|
14
|
+
export interface TrackedRequest {
|
|
15
|
+
sequence: number;
|
|
16
|
+
signal: AbortSignal;
|
|
17
|
+
}
|
|
18
|
+
export interface FileTreeNode {
|
|
19
|
+
name: string;
|
|
20
|
+
path: string;
|
|
21
|
+
folders: FileTreeNode[];
|
|
22
|
+
files: RepositoryFile[];
|
|
23
|
+
}
|
|
24
|
+
export interface ReviewRow {
|
|
25
|
+
kind: 'context' | 'added' | 'deleted' | 'skipped' | 'annotation';
|
|
26
|
+
oldNumber: number | null;
|
|
27
|
+
newNumber: number | null;
|
|
28
|
+
text: string;
|
|
29
|
+
}
|
|
30
|
+
export interface CommitGraphEdge {
|
|
31
|
+
from: number;
|
|
32
|
+
to: number | null;
|
|
33
|
+
active: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface CommitGraphRow {
|
|
36
|
+
commit: CommitSummary;
|
|
37
|
+
lane: number;
|
|
38
|
+
laneCount: number;
|
|
39
|
+
edges: CommitGraphEdge[];
|
|
40
|
+
}
|
|
41
|
+
export interface HostSplitLayout {
|
|
42
|
+
frame: HTMLElement;
|
|
43
|
+
sidebar: HTMLElement;
|
|
44
|
+
center: HTMLElement;
|
|
45
|
+
details: HTMLElement;
|
|
46
|
+
}
|
|
47
|
+
export interface ActiveHostSplit {
|
|
48
|
+
layout: HostSplitLayout;
|
|
49
|
+
splitColumns: string;
|
|
50
|
+
previousGridTemplateColumns: string;
|
|
51
|
+
previousTrack: string;
|
|
52
|
+
previousDetailsWidth: string;
|
|
53
|
+
previousDetailsMinWidth: string;
|
|
54
|
+
previousDetailsMaxWidth: string;
|
|
55
|
+
previousDetailsBorderLeft: string;
|
|
56
|
+
}
|
|
57
|
+
export interface ResizeDrag {
|
|
58
|
+
pointerId: number;
|
|
59
|
+
startX: number;
|
|
60
|
+
startWidth: number;
|
|
61
|
+
currentRatio: number;
|
|
62
|
+
}
|
|
63
|
+
export declare const WORKBENCH_RATIO_KEY = "dsh-easygit-plugin:workbench-ratio";
|
|
64
|
+
export declare const WORKBENCH_TRACK = "--dsh-easygit-plugin-workbench-width";
|
|
65
|
+
export declare const WORKBENCH_DEFAULT_RATIO = 0.36;
|
|
66
|
+
export declare const WORKBENCH_MIN_RATIO = 0.24;
|
|
67
|
+
export declare const WORKBENCH_MAX_RATIO = 0.75;
|
|
68
|
+
export declare function appendCommandLog(current: CommandLogEntry[], entry: CommandLogEntry): CommandLogEntry[];
|
|
69
|
+
export declare function filterLocalBranches(branches: BranchSummary[], query: string): BranchSummary[];
|
|
70
|
+
export declare function isCurrentCommitRequest(selectedHash: string, requestedHash: string, currentSequence: number, requestSequence: number): boolean;
|
|
71
|
+
export declare function nextCommitSelection(currentHash: string, requestedHash: string): string;
|
|
72
|
+
export declare function commitFileTone(status: string): string;
|
|
73
|
+
export declare function isLatestRequest(currentSequence: number, requestSequence: number): boolean;
|
|
74
|
+
export declare function beginTrackedRequest(ref: {
|
|
75
|
+
current: RequestSlot;
|
|
76
|
+
}): TrackedRequest;
|
|
77
|
+
export declare function cancelTrackedRequest(ref: {
|
|
78
|
+
current: RequestSlot;
|
|
79
|
+
}): void;
|
|
80
|
+
export declare function isTrackedRequestCurrent(ref: {
|
|
81
|
+
current: RequestSlot;
|
|
82
|
+
}, request: TrackedRequest): boolean;
|
|
83
|
+
export declare function isAbortError(error: unknown): boolean;
|
|
84
|
+
export declare function deriveCommitGraph(commits: CommitSummary[]): CommitGraphRow[];
|
|
85
|
+
export declare function clampWorkbenchRatio(value: number): number;
|
|
86
|
+
export declare function readWorkbenchRatio(): number;
|
|
87
|
+
export declare function persistWorkbenchRatio(value: number | null): void;
|
|
88
|
+
export declare function workbenchTrackForRatio(ratio: number): string;
|
|
89
|
+
export declare function repositoryName(topLevel: unknown): string;
|
|
90
|
+
export declare function mutationCommand(action: string, payload?: AnyRecord): {
|
|
91
|
+
label: string;
|
|
92
|
+
command: string;
|
|
93
|
+
} | null;
|
|
94
|
+
export declare function viewportWidth(): number;
|
|
95
|
+
export declare function sidebarTrackWidth(layout: HostSplitLayout): number;
|
|
96
|
+
export declare function findWorkbenchHostSplit(anchor: HTMLElement): HostSplitLayout | null;
|
|
97
|
+
export declare function buildFileTree(files: RepositoryFile[]): FileTreeNode;
|
|
98
|
+
export declare function parseReviewRows(diff: string): ReviewRow[];
|
|
99
|
+
export declare function diffLineClass(line: string): string;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { ProposalView } from '../shared/contracts';
|
|
3
|
+
import { deriveChecks } from './command-policy';
|
|
4
|
+
import type { GitRepositoryService, ShellService } from './git-repository-service';
|
|
5
|
+
import type { StoredProposal } from './proposal-service';
|
|
6
|
+
type UnknownRecord = Record<string, unknown>;
|
|
7
|
+
export interface WebServerService {
|
|
8
|
+
register(definition: {
|
|
9
|
+
kind: 'prefix';
|
|
10
|
+
path: string;
|
|
11
|
+
handler(req: IncomingMessage, res: ServerResponse): Promise<void>;
|
|
12
|
+
}): unknown;
|
|
13
|
+
}
|
|
14
|
+
interface ProposalVerification {
|
|
15
|
+
changed: boolean;
|
|
16
|
+
verified: boolean;
|
|
17
|
+
partial: boolean;
|
|
18
|
+
message: string;
|
|
19
|
+
changedState: string;
|
|
20
|
+
}
|
|
21
|
+
interface GitGuideActionDependencies {
|
|
22
|
+
repository: GitRepositoryService;
|
|
23
|
+
proposalStorageReady: Promise<void>;
|
|
24
|
+
shell: ShellService | null;
|
|
25
|
+
repositoryContext(sessionId: string): {
|
|
26
|
+
workdir: string;
|
|
27
|
+
policy: unknown;
|
|
28
|
+
} | null;
|
|
29
|
+
latestPending(sessionId: string): StoredProposal | null;
|
|
30
|
+
findProposal(sessionId: string, proposalId: unknown): StoredProposal | undefined;
|
|
31
|
+
proposalView(proposal: StoredProposal): ProposalView;
|
|
32
|
+
flushProposal(sessionId: string): Promise<void>;
|
|
33
|
+
captureFingerprint(shell: ShellService | null, workdir: string): Promise<string | null>;
|
|
34
|
+
runChecks(shell: ShellService | null, workdir: string, checks: ReturnType<typeof deriveChecks>): Promise<string[]>;
|
|
35
|
+
verifyProposal(shell: ShellService | null, proposal: StoredProposal): Promise<ProposalVerification>;
|
|
36
|
+
executeProposal(shell: ShellService | null, proposal: StoredProposal, policy: unknown, persist: () => Promise<void>): Promise<UnknownRecord>;
|
|
37
|
+
resolveExecutionPolicy(sessionId: string): unknown;
|
|
38
|
+
}
|
|
39
|
+
/** Register the Client-to-Host POST dispatcher with a 1 MiB body limit. */
|
|
40
|
+
export declare function registerGitGuideActions(webServer: WebServerService | null, dependencies: GitGuideActionDependencies): unknown;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type RiskLevel = 'safe' | 'normal' | 'hard';
|
|
2
|
+
export interface ParsedCommand {
|
|
3
|
+
ok: true;
|
|
4
|
+
args: string[];
|
|
5
|
+
subcommand: string;
|
|
6
|
+
normalized: string;
|
|
7
|
+
}
|
|
8
|
+
export interface InvalidCommand {
|
|
9
|
+
ok: false;
|
|
10
|
+
error: string;
|
|
11
|
+
}
|
|
12
|
+
export type ParseResult = ParsedCommand | InvalidCommand;
|
|
13
|
+
export interface RiskAssessment {
|
|
14
|
+
level: RiskLevel;
|
|
15
|
+
reasons: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface ModernizedCommand {
|
|
18
|
+
command: string;
|
|
19
|
+
changed: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface ExpectedCheck {
|
|
22
|
+
type: 'branch' | 'branch-gone' | 'commit-msg' | 'staged' | 'clean' | 'stash-nonempty' | 'stash-empty' | 'no-ahead';
|
|
23
|
+
value: string | string[] | boolean;
|
|
24
|
+
label: string;
|
|
25
|
+
}
|
|
26
|
+
export declare const MAX_COMMAND_LENGTH = 800;
|
|
27
|
+
export declare function quoteShellArg(value: unknown): string;
|
|
28
|
+
export declare function parseCommand(command: unknown): ParseResult;
|
|
29
|
+
export declare function validateCommand(command: unknown): ParseResult & {
|
|
30
|
+
segments?: string[];
|
|
31
|
+
};
|
|
32
|
+
export declare function modernizeCommand(command: unknown): ModernizedCommand;
|
|
33
|
+
export declare function classifyRisk(command: unknown): RiskAssessment;
|
|
34
|
+
export declare function classifyStepsRisk(commands: readonly string[]): RiskAssessment;
|
|
35
|
+
export declare function redactSecrets(value: unknown): string;
|
|
36
|
+
export declare function redactAndLimit(value: unknown, maxChars?: number): string;
|
|
37
|
+
export declare function addPathsOf(command: string): string | null;
|
|
38
|
+
export declare function deriveChecks(commands: readonly string[]): ExpectedCheck[];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ActionResult, CommitDetail, CommitDiffResult, CommitSummary, DiffResult, RepositoryReferences, RepositorySummary, StashSummary } from '../shared/contracts';
|
|
2
|
+
export type { BranchSummary, CommitDetail, CommitDiffResult, CommitFileChange, CommitRefSummary, CommitSummary, DiffResult, ReferenceSummary, RepositoryFile, RepositoryReferences, RepositorySummary, StashSummary, } from '../shared/contracts';
|
|
3
|
+
export interface GitRunResult {
|
|
4
|
+
exitCode: number | null;
|
|
5
|
+
signal?: string | null;
|
|
6
|
+
timedOut?: boolean;
|
|
7
|
+
aborted?: boolean;
|
|
8
|
+
stdout?: {
|
|
9
|
+
text?: string;
|
|
10
|
+
};
|
|
11
|
+
stderr?: {
|
|
12
|
+
text?: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface ShellService {
|
|
16
|
+
resolve(request: Record<string, unknown>): unknown;
|
|
17
|
+
run(specification: unknown): Promise<GitRunResult>;
|
|
18
|
+
}
|
|
19
|
+
interface MutationRequest {
|
|
20
|
+
sessionId: string;
|
|
21
|
+
workdir: string;
|
|
22
|
+
operationId: unknown;
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
sandboxPolicy?: unknown;
|
|
25
|
+
}
|
|
26
|
+
export declare class GitRepositoryService {
|
|
27
|
+
private readonly shell;
|
|
28
|
+
private readonly locks;
|
|
29
|
+
private readonly operations;
|
|
30
|
+
constructor(shell: ShellService | undefined);
|
|
31
|
+
run(workdir: string, command: string, timeoutMs?: number, stdoutMaxBytes?: number, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<GitRunResult>;
|
|
32
|
+
getTopLevel(workdir: string, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<{
|
|
33
|
+
topLevel: string;
|
|
34
|
+
}>>;
|
|
35
|
+
getSummary(workdir: string, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<RepositorySummary>>;
|
|
36
|
+
getDiff(workdir: string, path: unknown, staged: boolean, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<DiffResult>>;
|
|
37
|
+
getBranches(workdir: string, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<RepositoryReferences>>;
|
|
38
|
+
getCommits(workdir: string, limit: unknown, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<CommitSummary[]>>;
|
|
39
|
+
getCommitDetail(workdir: string, hash: unknown, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<CommitDetail>>;
|
|
40
|
+
getCommitDiff(workdir: string, hash: unknown, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<CommitDiffResult>>;
|
|
41
|
+
getStashes(workdir: string, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<StashSummary[]>>;
|
|
42
|
+
stagePaths(request: MutationRequest, paths: unknown): Promise<ActionResult<RepositorySummary>>;
|
|
43
|
+
unstagePaths(request: MutationRequest, paths: unknown): Promise<ActionResult<RepositorySummary>>;
|
|
44
|
+
stageAll(request: MutationRequest): Promise<ActionResult<RepositorySummary>>;
|
|
45
|
+
unstageAll(request: MutationRequest): Promise<ActionResult<RepositorySummary>>;
|
|
46
|
+
commit(request: MutationRequest, message: unknown): Promise<ActionResult<RepositorySummary>>;
|
|
47
|
+
createBranch(request: MutationRequest, name: unknown, base: unknown): Promise<ActionResult<RepositorySummary>>;
|
|
48
|
+
switchBranch(request: MutationRequest, name: unknown): Promise<ActionResult<RepositorySummary>>;
|
|
49
|
+
deleteBranch(request: MutationRequest, name: unknown, force: boolean, confirmRisk: boolean): Promise<ActionResult<RepositorySummary>>;
|
|
50
|
+
private validatePaths;
|
|
51
|
+
private mutate;
|
|
52
|
+
private pruneOperations;
|
|
53
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-easygit-plugin Host plugin package.
|
|
3
|
+
*
|
|
4
|
+
* It is mounted as one profile composition entry and serves every DeepSeek
|
|
5
|
+
* Harness session. It uses only ctx.tools.register for tools and a
|
|
6
|
+
* ctx.webServer POST /git-guide route for Client-to-Host actions.
|
|
7
|
+
*
|
|
8
|
+
* The exposed model tools are git_propose and git_repo_state. Repository changes
|
|
9
|
+
* are only available through the Client action route.
|
|
10
|
+
*/
|
|
11
|
+
import { ProposalService, type ProposalView, type StoredProposal } from './proposal-service';
|
|
12
|
+
import { type ShellService } from './git-repository-service';
|
|
13
|
+
import { addPathsOf, classifyRisk, classifyStepsRisk, deriveChecks, modernizeCommand, parseCommand, quoteShellArg, redactAndLimit, redactSecrets, validateCommand, type ExpectedCheck } from './command-policy';
|
|
14
|
+
type UnknownRecord = Record<string, unknown>;
|
|
15
|
+
interface HostContext {
|
|
16
|
+
get<T = unknown>(name: string): T;
|
|
17
|
+
inject?(services: string[], callback: (context: HostContext) => unknown): unknown;
|
|
18
|
+
effect?(callback: () => () => void | Promise<void>): unknown;
|
|
19
|
+
}
|
|
20
|
+
interface ProposalExecutionResult extends UnknownRecord {
|
|
21
|
+
ok: boolean;
|
|
22
|
+
proposalId: string;
|
|
23
|
+
command: string;
|
|
24
|
+
steps: Array<{
|
|
25
|
+
command: string;
|
|
26
|
+
ok: boolean;
|
|
27
|
+
exitCode: number;
|
|
28
|
+
}>;
|
|
29
|
+
exitCode: number;
|
|
30
|
+
signal: string;
|
|
31
|
+
timedOut: boolean;
|
|
32
|
+
stdout: string;
|
|
33
|
+
stderr: string;
|
|
34
|
+
diagnostics: string;
|
|
35
|
+
recovery?: {
|
|
36
|
+
suggestion: string;
|
|
37
|
+
command: string;
|
|
38
|
+
proposalId: string | null;
|
|
39
|
+
} | null;
|
|
40
|
+
error: string;
|
|
41
|
+
}
|
|
42
|
+
interface ProposalVerification {
|
|
43
|
+
changed: boolean;
|
|
44
|
+
verified: boolean;
|
|
45
|
+
partial: boolean;
|
|
46
|
+
message: string;
|
|
47
|
+
changedState: string;
|
|
48
|
+
}
|
|
49
|
+
interface RecoverySuggestion {
|
|
50
|
+
suggestion: string;
|
|
51
|
+
command: string | null;
|
|
52
|
+
proposalId?: string;
|
|
53
|
+
}
|
|
54
|
+
declare function captureFingerprint(shell: ShellService | null | undefined, workdir: string): Promise<string | null>;
|
|
55
|
+
declare function captureDiagnostics(shell: ShellService | null | undefined, workdir: string): Promise<string>;
|
|
56
|
+
declare function runChecks(shell: ShellService | null | undefined, workdir: string, checks: readonly ExpectedCheck[]): Promise<string[]>;
|
|
57
|
+
declare function verifyProposal(shell: ShellService | null | undefined, proposal: StoredProposal): Promise<ProposalVerification>;
|
|
58
|
+
declare function executeProposalSteps(shell: ShellService, proposal: StoredProposal, signal?: AbortSignal, policy?: unknown): Promise<{
|
|
59
|
+
ok: boolean;
|
|
60
|
+
stepsResult: Array<{
|
|
61
|
+
command: string;
|
|
62
|
+
ok: boolean;
|
|
63
|
+
exitCode: number;
|
|
64
|
+
}>;
|
|
65
|
+
}>;
|
|
66
|
+
declare function executeRegisteredProposal(shell: ShellService | null | undefined, proposal: StoredProposal | null | undefined, signal?: AbortSignal, policy?: unknown, persistStatus?: () => Promise<void>): Promise<ProposalExecutionResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Derive a safe recovery command from a failed step and repository diagnostics.
|
|
69
|
+
* A null command means the condition requires manual remediation.
|
|
70
|
+
*/
|
|
71
|
+
declare function buildRecovery(_proposal: StoredProposal, failedStep: StoredProposal['steps'][number] | undefined, diagnostics: string): RecoverySuggestion | null;
|
|
72
|
+
/** Register a recovery command as a pending proposal in the same session. */
|
|
73
|
+
declare function registerRecoveryProposal(failedProposal: Pick<StoredProposal, 'sessionId' | 'workdir'>, recovery: RecoverySuggestion): StoredProposal | null;
|
|
74
|
+
declare function storeProposal(sessionId: string, proposal: StoredProposal): StoredProposal;
|
|
75
|
+
declare function findProposal(sessionId: string, proposalId: unknown): StoredProposal | undefined;
|
|
76
|
+
declare function proposalView(proposal: StoredProposal): ProposalView;
|
|
77
|
+
declare function latestPending(sessionId: string): StoredProposal | null;
|
|
78
|
+
declare const _default: {
|
|
79
|
+
name: string;
|
|
80
|
+
inject: string[];
|
|
81
|
+
apply(ctx: HostContext): void;
|
|
82
|
+
} & {
|
|
83
|
+
helpers: {
|
|
84
|
+
parseCommand: typeof parseCommand;
|
|
85
|
+
validateCommand: typeof validateCommand;
|
|
86
|
+
modernizeCommand: typeof modernizeCommand;
|
|
87
|
+
classifyRisk: typeof classifyRisk;
|
|
88
|
+
classifyStepsRisk: typeof classifyStepsRisk;
|
|
89
|
+
quoteShellArg: typeof quoteShellArg;
|
|
90
|
+
redactSecrets: typeof redactSecrets;
|
|
91
|
+
redactAndLimit: typeof redactAndLimit;
|
|
92
|
+
addPathsOf: typeof addPathsOf;
|
|
93
|
+
deriveChecks: typeof deriveChecks;
|
|
94
|
+
runChecks: typeof runChecks;
|
|
95
|
+
verifyProposal: typeof verifyProposal;
|
|
96
|
+
captureFingerprint: typeof captureFingerprint;
|
|
97
|
+
captureDiagnostics: typeof captureDiagnostics;
|
|
98
|
+
executeProposalSteps: typeof executeProposalSteps;
|
|
99
|
+
executeRegisteredProposal: typeof executeRegisteredProposal;
|
|
100
|
+
buildRecovery: typeof buildRecovery;
|
|
101
|
+
registerRecoveryProposal: typeof registerRecoveryProposal;
|
|
102
|
+
storeProposal: typeof storeProposal;
|
|
103
|
+
findProposal: typeof findProposal;
|
|
104
|
+
proposalView: typeof proposalView;
|
|
105
|
+
latestPending: typeof latestPending;
|
|
106
|
+
ProposalService: typeof ProposalService;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
export = _default;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ProposalStatus, ProposalView } from '../shared/contracts';
|
|
2
|
+
export type { ProposalView } from '../shared/contracts';
|
|
3
|
+
import type { RiskLevel } from './command-policy';
|
|
4
|
+
export interface ProposalStep {
|
|
5
|
+
command: string;
|
|
6
|
+
result: Record<string, unknown> | null;
|
|
7
|
+
}
|
|
8
|
+
export interface StoredProposal {
|
|
9
|
+
proposalId: string;
|
|
10
|
+
sessionId: string;
|
|
11
|
+
intent: string;
|
|
12
|
+
command: string;
|
|
13
|
+
steps: ProposalStep[];
|
|
14
|
+
explanation: string;
|
|
15
|
+
risk: RiskLevel;
|
|
16
|
+
reasons: string[];
|
|
17
|
+
confirmed: boolean;
|
|
18
|
+
workdir: string;
|
|
19
|
+
createdAt: number;
|
|
20
|
+
status: ProposalStatus;
|
|
21
|
+
closed: boolean;
|
|
22
|
+
copied: boolean;
|
|
23
|
+
fingerprint: string | null;
|
|
24
|
+
verified: boolean;
|
|
25
|
+
result: Record<string, unknown> | null;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface ProposalStorageUnit {
|
|
29
|
+
loadAll(): Promise<{
|
|
30
|
+
tables: Record<string, Record<string, unknown>>;
|
|
31
|
+
global: unknown;
|
|
32
|
+
}>;
|
|
33
|
+
putRecord(table: string, key: string, value: unknown): Promise<void>;
|
|
34
|
+
deleteRecord(table: string, key: string): Promise<void>;
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
export declare class ProposalService {
|
|
38
|
+
private readonly proposalsPerSession;
|
|
39
|
+
private readonly maxSessions;
|
|
40
|
+
private readonly sessionTtlMs;
|
|
41
|
+
private readonly proposalsBySession;
|
|
42
|
+
private storage;
|
|
43
|
+
private storageTail;
|
|
44
|
+
constructor(proposalsPerSession?: number, maxSessions?: number, sessionTtlMs?: number);
|
|
45
|
+
attachStorage(storage: ProposalStorageUnit): Promise<void>;
|
|
46
|
+
closeStorage(storage: ProposalStorageUnit): Promise<void>;
|
|
47
|
+
flush(sessionId: string): Promise<void>;
|
|
48
|
+
newId(): string;
|
|
49
|
+
list(sessionId: string): StoredProposal[] | undefined;
|
|
50
|
+
hasRunning(sessionId: string): boolean;
|
|
51
|
+
closeOpen(sessionId: string): void;
|
|
52
|
+
store(sessionId: string, proposal: StoredProposal): StoredProposal;
|
|
53
|
+
prune(now?: number): void;
|
|
54
|
+
private pruneMap;
|
|
55
|
+
private readStoredEntries;
|
|
56
|
+
find(sessionId: string, proposalId: unknown): StoredProposal | undefined;
|
|
57
|
+
latestPending(sessionId: string): StoredProposal | null;
|
|
58
|
+
view(proposal: StoredProposal): ProposalView;
|
|
59
|
+
}
|
|
60
|
+
export declare const proposalService: ProposalService;
|