dsh-bash-terminal-ts 0.2.5
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.en.md +195 -0
- package/README.ja.md +194 -0
- package/README.ko.md +192 -0
- package/README.md +192 -0
- package/cordis.patch.yml +5 -0
- package/dist/client.core.js +132 -0
- package/dist/client.js +141 -0
- package/dsh.plugin.json +22 -0
- package/lib/dsh-types.d.ts +260 -0
- package/lib/dsh-types.js +9 -0
- package/lib/dsh.d.ts +33 -0
- package/lib/dsh.js +29 -0
- package/lib/index.d.ts +201 -0
- package/lib/index.js +759 -0
- package/lib/terminal.d.ts +87 -0
- package/lib/terminal.js +273 -0
- package/package.json +122 -0
- package/scripts/build-client.mjs +46 -0
- package/src/client.tsx +161 -0
- package/src/dsh-types.ts +313 -0
- package/src/dsh.ts +68 -0
- package/src/index.ts +952 -0
- package/src/terminal.ts +368 -0
- package/tsconfig.client.json +17 -0
- package/tsconfig.json +23 -0
- package/tsconfig.test.json +11 -0
package/src/client.tsx
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// dsh-bash-terminal-ts client plugin: a "Default terminal" preference row in the
|
|
2
|
+
// Web UI General settings. The user picks powershell / gitbash / msys2 / wsl;
|
|
3
|
+
// the host shell tool obeys that choice (the model cannot change it).
|
|
4
|
+
//
|
|
5
|
+
// The option list below must stay in lockstep with SHELLS in ./index.ts: every
|
|
6
|
+
// id gets an <option> here and a `shell.<id>` entry in both dictionaries. The
|
|
7
|
+
// shipped bundle cannot import the host module (it pulls node: builtins), so
|
|
8
|
+
// test/client.ts asserts the bundle covers every host shell id instead.
|
|
9
|
+
|
|
10
|
+
import { defineStore } from "@deepseek-ai/dsh-client-store";
|
|
11
|
+
|
|
12
|
+
const SETTINGS_NS = "settings.bash-terminal";
|
|
13
|
+
const SETTINGS_NAMESPACE = "bash-terminal";
|
|
14
|
+
|
|
15
|
+
const zh: Record<string, string> = {
|
|
16
|
+
"shell.title": "默认终端",
|
|
17
|
+
"shell.description": "shell 工具执行命令时使用的终端(由你决定,AI 无法更改)",
|
|
18
|
+
"shell.powershell": "PowerShell",
|
|
19
|
+
"shell.gitbash": "Git Bash",
|
|
20
|
+
"shell.msys2": "MSYS2",
|
|
21
|
+
"shell.wsl": "WSL"
|
|
22
|
+
};
|
|
23
|
+
const en: Record<string, string> = {
|
|
24
|
+
"shell.title": "Default terminal",
|
|
25
|
+
"shell.description": "Terminal used by the shell tool (you control this; the AI cannot change it)",
|
|
26
|
+
"shell.powershell": "PowerShell",
|
|
27
|
+
"shell.gitbash": "Git Bash",
|
|
28
|
+
"shell.msys2": "MSYS2",
|
|
29
|
+
"shell.wsl": "WSL"
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const inject = ["slots", "locale", "settingsScope"];
|
|
33
|
+
|
|
34
|
+
/** Row state snapshot served through the settings store. */
|
|
35
|
+
interface RowState {
|
|
36
|
+
shell: string;
|
|
37
|
+
revision: number;
|
|
38
|
+
writable: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface ShellPreferenceRowProps {
|
|
42
|
+
t: (key: string) => string;
|
|
43
|
+
useStore: <S>(selector: (state: RowState) => S) => S;
|
|
44
|
+
setShell: (id: string) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function ShellPreferenceRow({ t, useStore, setShell }: ShellPreferenceRowProps) {
|
|
48
|
+
const shell = useStore((s) => s.shell);
|
|
49
|
+
const writable = useStore((s) => s.writable);
|
|
50
|
+
return (
|
|
51
|
+
<div
|
|
52
|
+
style={{
|
|
53
|
+
display: "flex",
|
|
54
|
+
alignItems: "center",
|
|
55
|
+
justifyContent: "space-between",
|
|
56
|
+
gap: 16,
|
|
57
|
+
padding: "12px 0"
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
<div style={{ minWidth: 0 }}>
|
|
61
|
+
<div style={{ fontSize: 14, fontWeight: 500, lineHeight: "22px", color: "var(--dsw-alias-label-primary, inherit)" }}>
|
|
62
|
+
{t("shell.title")}
|
|
63
|
+
</div>
|
|
64
|
+
<div style={{ fontSize: 12, lineHeight: "18px", opacity: 0.65 }}>{t("shell.description")}</div>
|
|
65
|
+
</div>
|
|
66
|
+
<select
|
|
67
|
+
value={shell}
|
|
68
|
+
disabled={!writable}
|
|
69
|
+
onChange={(e) => setShell(e.target.value)}
|
|
70
|
+
style={{
|
|
71
|
+
fontSize: 14,
|
|
72
|
+
padding: "6px 10px",
|
|
73
|
+
borderRadius: 8,
|
|
74
|
+
border: "1px solid var(--dsw-alias-line-strong, #ccc)",
|
|
75
|
+
background: "var(--dsw-alias-bg-layer-2, #fff)",
|
|
76
|
+
color: "var(--dsw-alias-label-primary, inherit)",
|
|
77
|
+
outline: "none",
|
|
78
|
+
cursor: writable ? "pointer" : "not-allowed",
|
|
79
|
+
maxWidth: 180
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
<option value="powershell">{t("shell.powershell")}</option>
|
|
83
|
+
<option value="gitbash">{t("shell.gitbash")}</option>
|
|
84
|
+
<option value="msys2">{t("shell.msys2")}</option>
|
|
85
|
+
<option value="wsl">{t("shell.wsl")}</option>
|
|
86
|
+
</select>
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface SettingsSnapshot {
|
|
92
|
+
status: string;
|
|
93
|
+
value?: { defaultShell?: string };
|
|
94
|
+
revision: number;
|
|
95
|
+
writable: boolean;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
interface SettingsScopeBinding {
|
|
99
|
+
getSnapshot(): SettingsSnapshot;
|
|
100
|
+
subscribe(fn: () => void): () => void;
|
|
101
|
+
set(field: string, value: string): void;
|
|
102
|
+
unset(): void;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
interface ClientContext {
|
|
106
|
+
effect(fn: () => unknown, name?: string): unknown;
|
|
107
|
+
locale: { register(ns: string, dicts: Record<string, Record<string, string>>): unknown };
|
|
108
|
+
settingsScope: { bind(options: { namespace: string }): SettingsScopeBinding };
|
|
109
|
+
slots: {
|
|
110
|
+
inject(slot: string, fn: () => unknown, name?: string): void;
|
|
111
|
+
register(options: Record<string, unknown>, Component: unknown): Record<string, unknown>;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
interface RowStoreActions {
|
|
116
|
+
sync(shell?: string, revision?: number, writable?: boolean): void;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function apply(ctx: ClientContext): void {
|
|
120
|
+
ctx.effect(() => ctx.locale.register(SETTINGS_NS, { zh, en }), "bash-terminal: settings dictionaries");
|
|
121
|
+
const scope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE });
|
|
122
|
+
const store = defineStore<RowState, { sync: (draft: RowState, shell?: string, revision?: number, writable?: boolean) => void }>({
|
|
123
|
+
init: (): RowState => ({ shell: "powershell", revision: -1, writable: false }),
|
|
124
|
+
actions: {
|
|
125
|
+
sync: (d, shell, revision, writable) => {
|
|
126
|
+
if (revision !== undefined && revision <= d.revision) return;
|
|
127
|
+
if (shell !== undefined) d.shell = shell;
|
|
128
|
+
if (revision !== undefined) d.revision = revision;
|
|
129
|
+
if (writable !== undefined) d.writable = writable;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
// defineStore() yields a { spec, create } factory; slots.register creates
|
|
134
|
+
// the live store, so we bind the actions in the inject callback and push
|
|
135
|
+
// the initial snapshot there too (mirrors the theme row pattern).
|
|
136
|
+
let bound: RowStoreActions | undefined;
|
|
137
|
+
const push = (snap: SettingsSnapshot): void => {
|
|
138
|
+
bound?.sync(snap.value?.defaultShell, snap.revision, snap.writable);
|
|
139
|
+
};
|
|
140
|
+
ctx.slots.inject(
|
|
141
|
+
"settings.general.item",
|
|
142
|
+
() =>
|
|
143
|
+
ctx.slots.register(
|
|
144
|
+
{
|
|
145
|
+
name: "settings.general.item",
|
|
146
|
+
id: "bash-terminal-shell",
|
|
147
|
+
order: 20,
|
|
148
|
+
store,
|
|
149
|
+
locale: SETTINGS_NS,
|
|
150
|
+
inject: (actions: unknown) => {
|
|
151
|
+
bound = actions as RowStoreActions;
|
|
152
|
+
push(scope.getSnapshot());
|
|
153
|
+
return { setShell: (value: string) => void scope.set("defaultShell", value) };
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
ShellPreferenceRow
|
|
157
|
+
),
|
|
158
|
+
"bash-terminal: settings row"
|
|
159
|
+
);
|
|
160
|
+
ctx.effect(() => scope.subscribe(() => push(scope.getSnapshot())), "bash-terminal: settings watch");
|
|
161
|
+
}
|
package/src/dsh-types.ts
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// Hand-written boundary types for the DSH seams this plugin consumes.
|
|
2
|
+
//
|
|
3
|
+
// The @deepseek-ai/* packages ship their own .d.ts, but their full generic
|
|
4
|
+
// surface (cordis Context augmentation, agent/session graphs) is far wider
|
|
5
|
+
// than what a plugin touches. These structural types describe exactly the
|
|
6
|
+
// face this plugin programs against — call them the plugin's side of the
|
|
7
|
+
// contract. Value imports stay on the real packages (see ./dsh.ts); only
|
|
8
|
+
// the shapes live here, so peer version drift cannot ripple into this tree.
|
|
9
|
+
|
|
10
|
+
// ---- shared ------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
/** The terminal backends this plugin exposes. */
|
|
13
|
+
export type ShellId = "powershell" | "gitbash" | "msys2" | "wsl";
|
|
14
|
+
|
|
15
|
+
/** Permissive node of the DSH JSON-schema dialect used in tool output declarations. */
|
|
16
|
+
export interface JsonSchemaNode {
|
|
17
|
+
type?: string;
|
|
18
|
+
required?: boolean;
|
|
19
|
+
const?: string;
|
|
20
|
+
enum?: readonly string[];
|
|
21
|
+
properties?: Record<string, JsonSchemaNode>;
|
|
22
|
+
items?: JsonSchemaNode;
|
|
23
|
+
oneOf?: JsonSchemaNode[];
|
|
24
|
+
additionalProperties?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Model-facing content block (subset). */
|
|
28
|
+
export interface ContentBlock {
|
|
29
|
+
type: string;
|
|
30
|
+
text?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ---- tool surface ------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
/** One entry of a tool's `parameters` declaration. */
|
|
36
|
+
export interface ToolParameterProperty {
|
|
37
|
+
type: string;
|
|
38
|
+
required?: boolean;
|
|
39
|
+
description?: string;
|
|
40
|
+
enum?: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ToolParameters {
|
|
44
|
+
type: "object";
|
|
45
|
+
properties: Record<string, ToolParameterProperty>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Flat parameter map as accepted by defineTool (wrapped into `ToolParameters` by the runtime). */
|
|
49
|
+
export type ToolParameterMap = Record<string, ToolParameterProperty>;
|
|
50
|
+
|
|
51
|
+
/** The exec object every tool body receives. */
|
|
52
|
+
export interface ToolRunContext {
|
|
53
|
+
/** Caller cancellation; observe or forward it in async work. */
|
|
54
|
+
signal: AbortSignal;
|
|
55
|
+
callId?: string;
|
|
56
|
+
agent?: ExecAgent;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The agent slice of exec this plugin reads (session cwd) or passes on (owner). */
|
|
60
|
+
export interface ExecAgent {
|
|
61
|
+
session: { header: { cwd?: string } };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ToolResult {
|
|
65
|
+
content: ContentBlock[];
|
|
66
|
+
isError?: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Tool output view handed to presentResult. */
|
|
70
|
+
export interface CallView {
|
|
71
|
+
card: string;
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** A fully specified tool: schema, canonical output, body, presentations. */
|
|
76
|
+
export interface ToolSpec<TValue> {
|
|
77
|
+
name: string;
|
|
78
|
+
description: string;
|
|
79
|
+
parameters: ToolParameterMap;
|
|
80
|
+
output: {
|
|
81
|
+
schema: JsonSchemaNode;
|
|
82
|
+
render: (args: unknown, value: TValue) => ContentBlock[];
|
|
83
|
+
};
|
|
84
|
+
execute: (args: Record<string, unknown>, exec: ToolRunContext) => Promise<TValue>;
|
|
85
|
+
presentCall?: (args: Record<string, unknown>) => CallView;
|
|
86
|
+
presentResult?: (args: Record<string, unknown>, result: ToolResult) => CallView | undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Canonical output declaration, erased of the tool's value type (registry face). */
|
|
90
|
+
export interface ToolOutputDefinition {
|
|
91
|
+
schema: JsonSchemaNode;
|
|
92
|
+
render: (args: unknown, value: unknown) => ContentBlock[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** What defineTool hands back (opaque to the plugin; registered as-is). */
|
|
96
|
+
export interface ToolDefinition {
|
|
97
|
+
name: string;
|
|
98
|
+
description: string;
|
|
99
|
+
parameters: ToolParameters;
|
|
100
|
+
output: ToolOutputDefinition;
|
|
101
|
+
execute: (args: Record<string, unknown>, exec: ToolRunContext) => Promise<unknown>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ---- ctx services ------------------------------------------------------------
|
|
105
|
+
|
|
106
|
+
export interface LoggerSeam {
|
|
107
|
+
info?: (message: string) => void;
|
|
108
|
+
warn?: (message: string) => void;
|
|
109
|
+
error?: (message: string) => void;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface SystemPromptSection {
|
|
113
|
+
name: string;
|
|
114
|
+
order: number;
|
|
115
|
+
text: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface SystemPromptSeam {
|
|
119
|
+
section(spec: SystemPromptSection): unknown;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface ToolsSeam {
|
|
123
|
+
register(tool: ToolDefinition): unknown;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** A tool entry seen during system-prompt assembly (description re-render). */
|
|
127
|
+
export interface AssembledToolRef {
|
|
128
|
+
name: string;
|
|
129
|
+
description?: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface PromptAssembly {
|
|
133
|
+
tools: AssembledToolRef[];
|
|
134
|
+
[key: string]: unknown;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Type of the system-prompt/assemble waterfall listener. */
|
|
138
|
+
export type AssembleHandler = (
|
|
139
|
+
assembly: PromptAssembly,
|
|
140
|
+
context: unknown,
|
|
141
|
+
next: () => Promise<PromptAssembly>
|
|
142
|
+
) => Promise<PromptAssembly>;
|
|
143
|
+
|
|
144
|
+
export interface ShellEnvSeam {
|
|
145
|
+
collect(exec: ToolRunContext): Record<string, string> | undefined;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface SettingsScope<T> {
|
|
149
|
+
get(): T;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface SettingsSeam {
|
|
153
|
+
register<T>(namespace: string, schema: unknown, options: { base: T }): SettingsScope<T>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ---- sandbox ------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
export interface SandboxPolicy {
|
|
159
|
+
mode: string;
|
|
160
|
+
workspaceRoot?: string;
|
|
161
|
+
sessionId?: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface SandboxPolicySeam {
|
|
165
|
+
resolve(scope: { session?: unknown }): SandboxPolicy;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface SandboxConfineResult {
|
|
169
|
+
argv: string[];
|
|
170
|
+
enforcement: string;
|
|
171
|
+
denialSignatures?: string[];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface SandboxSeam {
|
|
175
|
+
confine(argv: string[], policy: SandboxPolicy): SandboxConfineResult;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Sandbox facts embedded in a canonical result. */
|
|
179
|
+
export interface SandboxFacts {
|
|
180
|
+
mode: string;
|
|
181
|
+
enforcement: string;
|
|
182
|
+
denialSignatures?: string[];
|
|
183
|
+
denied?: boolean;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface EscalationRequest {
|
|
187
|
+
requestedMode: string;
|
|
188
|
+
justification: string;
|
|
189
|
+
effectiveMode: string;
|
|
190
|
+
subject: string;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface EscalationContext {
|
|
194
|
+
approver?: unknown;
|
|
195
|
+
agent?: unknown;
|
|
196
|
+
callId?: string;
|
|
197
|
+
toolName: string;
|
|
198
|
+
signal: AbortSignal;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ---- background jobs ------------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
export interface JobHooks {
|
|
204
|
+
cancel: () => void;
|
|
205
|
+
done: Promise<unknown>;
|
|
206
|
+
readOutput: () => string;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export interface JobSpec {
|
|
210
|
+
kind: string;
|
|
211
|
+
label: string;
|
|
212
|
+
owner?: unknown;
|
|
213
|
+
run: () => JobHooks;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export interface JobsRegistry {
|
|
217
|
+
start(spec: JobSpec): string;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ---- subprocess seam ------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
/** Executable paths per backend; undefined when a backend is not installed. */
|
|
223
|
+
export interface ResolvedPaths {
|
|
224
|
+
pwsh?: string;
|
|
225
|
+
gitbash?: string;
|
|
226
|
+
msys2?: string;
|
|
227
|
+
wsl?: string;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface CollectSpec {
|
|
231
|
+
maxBytes: number;
|
|
232
|
+
spill: { maxBytes: number };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface StdioSpec {
|
|
236
|
+
stdin: { data: string } | "ignore";
|
|
237
|
+
stdout: CollectSpec;
|
|
238
|
+
stderr: CollectSpec;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export interface SubprocessSpawnSpec {
|
|
242
|
+
argv: string[];
|
|
243
|
+
cwd?: string;
|
|
244
|
+
stdio: StdioSpec;
|
|
245
|
+
graceMs: number;
|
|
246
|
+
signal?: AbortSignal;
|
|
247
|
+
env?: Record<string, string | undefined>;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export interface CollectedStream {
|
|
251
|
+
readFrom(offset: number): { text: string; lossy: boolean; nextOffset: number; spillPath?: string };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface SubprocessOutcome {
|
|
255
|
+
exitCode: number | null;
|
|
256
|
+
signal: string | null;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export interface SubprocessHandle {
|
|
260
|
+
collected: { stdout: CollectedStream; stderr: CollectedStream };
|
|
261
|
+
done: Promise<SubprocessOutcome>;
|
|
262
|
+
terminate: () => void | Promise<void>;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export interface TerminalSpawnSpec {
|
|
266
|
+
argv: string[];
|
|
267
|
+
cwd: string;
|
|
268
|
+
env: Record<string, string | undefined>;
|
|
269
|
+
rows: number;
|
|
270
|
+
cols: number;
|
|
271
|
+
graceMs?: number;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export interface TerminalHandle {
|
|
275
|
+
pid: number;
|
|
276
|
+
output: NodeJS.ReadableStream;
|
|
277
|
+
done: Promise<SubprocessOutcome>;
|
|
278
|
+
write(data: string): void | Promise<void>;
|
|
279
|
+
signalForeground(sig: string): void | Promise<unknown>;
|
|
280
|
+
terminate(): Promise<void>;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export interface SubprocessSeam {
|
|
284
|
+
spawn(spec: SubprocessSpawnSpec): SubprocessHandle;
|
|
285
|
+
spawnTerminal?(spec: TerminalSpawnSpec): Promise<TerminalHandle>;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ---- approval ------------------------------------------------------------------
|
|
289
|
+
|
|
290
|
+
export interface ApprovalSeam {
|
|
291
|
+
request(input: unknown): Promise<unknown>;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ---- plugin context (server side) ----------------------------------------------
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* The structural context face dsh-bash-terminal-ts programs against: the union
|
|
298
|
+
* of every `ctx.*` member touched by src/index.ts and src/terminal.ts.
|
|
299
|
+
*/
|
|
300
|
+
export interface BashTerminalContext {
|
|
301
|
+
logger?: LoggerSeam;
|
|
302
|
+
systemPrompt: SystemPromptSeam;
|
|
303
|
+
tools: ToolsSeam;
|
|
304
|
+
on(event: "system-prompt/assemble", handler: AssembleHandler): void;
|
|
305
|
+
shellEnv: ShellEnvSeam;
|
|
306
|
+
settings: SettingsSeam;
|
|
307
|
+
sandboxPolicy: SandboxPolicySeam;
|
|
308
|
+
sandbox: SandboxSeam;
|
|
309
|
+
get(key: string): unknown;
|
|
310
|
+
effect(fn: () => void | (() => void), name?: string): unknown;
|
|
311
|
+
/** Injected via `inject: ["subprocess"]`; tests may defer assignment. */
|
|
312
|
+
subprocess: SubprocessSeam | null;
|
|
313
|
+
}
|
package/src/dsh.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Value bridge to the @deepseek-ai/* peer packages.
|
|
2
|
+
//
|
|
3
|
+
// Peer packages ship their own .d.ts, but this plugin's contract with them is
|
|
4
|
+
// a narrow, stable face (see ./dsh-types.ts). Every import passes through one
|
|
5
|
+
// narrow cast here, so the plugin compiles against any 0.1.x peer whose
|
|
6
|
+
// runtime behavior matches the face — no deep generic coupling, no version
|
|
7
|
+
// drift leaking into the rewrite.
|
|
8
|
+
|
|
9
|
+
import * as toolsNs from "@deepseek-ai/dsh-tools";
|
|
10
|
+
import * as sandboxNs from "@deepseek-ai/dsh-sandbox";
|
|
11
|
+
import { HarnessError } from "@deepseek-ai/dsh-llm";
|
|
12
|
+
import { parseExitStatus } from "@deepseek-ai/dsh-shell";
|
|
13
|
+
import { clampTimeout, deadline, timeoutOf } from "@deepseek-ai/dsh-timeout";
|
|
14
|
+
import zDefault from "@deepseek-ai/schemastery";
|
|
15
|
+
import type {
|
|
16
|
+
EscalationContext,
|
|
17
|
+
EscalationRequest,
|
|
18
|
+
JsonSchemaNode,
|
|
19
|
+
ToolDefinition,
|
|
20
|
+
ToolSpec
|
|
21
|
+
} from "./dsh-types.js";
|
|
22
|
+
|
|
23
|
+
export { HarnessError, parseExitStatus, clampTimeout, deadline, timeoutOf };
|
|
24
|
+
|
|
25
|
+
/** Wrap a fully specified tool spec into a registrable tool definition. */
|
|
26
|
+
export const defineTool = toolsNs.defineTool as unknown as <TValue>(spec: ToolSpec<TValue>) => ToolDefinition;
|
|
27
|
+
|
|
28
|
+
/** Sentinel code stamped on aborted tool calls. */
|
|
29
|
+
export const TOOL_ABORTED = toolsNs.TOOL_ABORTED as string;
|
|
30
|
+
|
|
31
|
+
/** Escalation modes advertised by the official sandbox seam. */
|
|
32
|
+
export const ESCALATION_TARGETS = sandboxNs.ESCALATION_TARGETS as readonly string[];
|
|
33
|
+
|
|
34
|
+
/** Official sandbox escalation (mirrors dsh-tool-bash / dsh-tool-pwsh). */
|
|
35
|
+
export const approveEscalation = sandboxNs.approveEscalation as unknown as (
|
|
36
|
+
request: EscalationRequest,
|
|
37
|
+
context: EscalationContext
|
|
38
|
+
) => Promise<string>;
|
|
39
|
+
|
|
40
|
+
/** Fail-closed validation of the sandbox_permissions / justification pair. */
|
|
41
|
+
export const validateEscalationArgs = sandboxNs.validateEscalationArgs as unknown as (
|
|
42
|
+
permissions: unknown,
|
|
43
|
+
justification: unknown
|
|
44
|
+
) => void;
|
|
45
|
+
|
|
46
|
+
/** Marker appended to denied results so the UI can offer escalation. */
|
|
47
|
+
export const sandboxDenialMarker = sandboxNs.sandboxDenialMarker as unknown as (mode: string) => string;
|
|
48
|
+
|
|
49
|
+
/** Marker appended to denied results pointing at the escalation affordance. */
|
|
50
|
+
export const escalationHintMarker = sandboxNs.escalationHintMarker as unknown as (subject: string) => string;
|
|
51
|
+
|
|
52
|
+
/** Runtime config schema factory (schemastery fork). */
|
|
53
|
+
export interface SchemasterySchema {
|
|
54
|
+
default(value: unknown): SchemasterySchema;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface Schemastery {
|
|
58
|
+
object(fields: Record<string, SchemasterySchema>): SchemasterySchema;
|
|
59
|
+
string(): SchemasterySchema;
|
|
60
|
+
number(): SchemasterySchema;
|
|
61
|
+
const(value: string): SchemasterySchema;
|
|
62
|
+
union(schemas: SchemasterySchema[]): SchemasterySchema;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const z = zDefault as unknown as Schemastery;
|
|
66
|
+
|
|
67
|
+
/** Re-exported for consumers that want to validate output schemas. */
|
|
68
|
+
export type { JsonSchemaNode };
|