@achasoft/dsh-advanced-sidebar 0.1.0 → 0.3.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/README.md +279 -128
- package/cordis.patch.yml +31 -3
- package/lib/client.js +2803 -466
- package/lib/client.js.map +1 -1
- package/lib/host.js +2071 -418
- package/lib/index.js +6 -2
- package/lib/preview-content-BVUQ5oOR.js +465 -0
- package/lib/remote.js +330 -25
- package/lib/typert.host.js +330 -25
- package/lib/ui-preview.js +352 -0
- package/package.json +8 -2
- package/types/client/ActionMenu.d.ts +16 -1
- package/types/client/LogDownloadDialog.d.ts +24 -0
- package/types/client/contract.d.ts +57 -1
- package/types/client/index.d.ts +4 -2
- package/types/client/locales.d.ts +100 -0
- package/types/client/log-download.d.ts +179 -0
- package/types/client/panels/PreviewPanel.d.ts +20 -15
- package/types/client/panels/preview-file.d.ts +61 -0
- package/types/client/panels/preview-mode.d.ts +67 -0
- package/types/client/panels/preview-scratchpad.d.ts +53 -0
- package/types/client/panels/preview-url.d.ts +17 -0
- package/types/client/panels/shared.d.ts +15 -2
- package/types/client/preview-driver.d.ts +121 -0
- package/types/client/preview-storage.d.ts +43 -0
- package/types/client/preview-types.d.ts +21 -0
- package/types/client/preview-values.d.ts +43 -0
- package/types/host/deletion.d.ts +32 -23
- package/types/host/git.d.ts +94 -8
- package/types/host/index.d.ts +97 -5
- package/types/host/preview-content.d.ts +179 -0
- package/types/host/preview-serve.d.ts +242 -0
- package/types/host/settings-section.d.ts +49 -0
- package/types/host/types.d.ts +341 -0
- package/types/host/ui-bridge.d.ts +197 -0
- package/types/host/ui-preview-tool.d.ts +60 -0
- package/types/index.d.ts +6 -2
- package/types/ui-preview.d.ts +11 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { d as parseHttpUrl, h as resolveWorkspace, m as resolveInside } from "./preview-content-BVUQ5oOR.js";
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
4
|
+
|
|
5
|
+
//#region tsbuild/host/ui-preview-tool.js
|
|
6
|
+
/** Cordis plugin name for the model-facing tool. */
|
|
7
|
+
const name = "advanced-sidebar-ui-preview";
|
|
8
|
+
/** The tool registry and the sidebar service both have to be present for this tool to mean anything. */
|
|
9
|
+
const inject = ["tools", "advancedSidebar"];
|
|
10
|
+
/** Schemastery configuration for the tool. */
|
|
11
|
+
const Config = z.object({ commandTimeoutMs: z.number().step(1).min(1e3).max(6e5).default(15e3) });
|
|
12
|
+
/** The action names, as one union the schema and the dispatch both read. */
|
|
13
|
+
const ACTIONS = [
|
|
14
|
+
"open",
|
|
15
|
+
"dom",
|
|
16
|
+
"eval",
|
|
17
|
+
"console",
|
|
18
|
+
"click",
|
|
19
|
+
"type",
|
|
20
|
+
"reload",
|
|
21
|
+
"resize",
|
|
22
|
+
"close"
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* Largest JSON body this tool will put in a result block.
|
|
26
|
+
*
|
|
27
|
+
* A DOM reading is already capped inside the frame, but a page whose `eval` returns a megabyte of
|
|
28
|
+
* JSON is one expression away; the model is told the value was cut rather than handed a transcript
|
|
29
|
+
* it cannot afford.
|
|
30
|
+
*/
|
|
31
|
+
const RESULT_MAX_CHARS = 96 * 1024;
|
|
32
|
+
/** Compose one text block.
|
|
33
|
+
*
|
|
34
|
+
* The return type is deliberately inferred rather than annotated with the harness's `ContentBlock`:
|
|
35
|
+
* the build's types come from the local harness checkout while the running install has its own
|
|
36
|
+
* copy, and the two copies' branded attachment ids are not identical. An inferred structural
|
|
37
|
+
* `{ type: 'text'; text: string }` satisfies both, which is exactly what this block is.
|
|
38
|
+
*/
|
|
39
|
+
function text(value) {
|
|
40
|
+
return {
|
|
41
|
+
type: "text",
|
|
42
|
+
text: value
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Render a JSON value for a tool result, cut at {@link RESULT_MAX_CHARS}.
|
|
47
|
+
* @param value - the value to serialize.
|
|
48
|
+
* @returns a fenced JSON block.
|
|
49
|
+
*/
|
|
50
|
+
function jsonBlock(value) {
|
|
51
|
+
let text$1;
|
|
52
|
+
try {
|
|
53
|
+
text$1 = JSON.stringify(value, null, 2);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
text$1 = JSON.stringify({ unserializable: error instanceof Error ? error.message : String(error) });
|
|
56
|
+
}
|
|
57
|
+
return [
|
|
58
|
+
"```json",
|
|
59
|
+
text$1.length > RESULT_MAX_CHARS ? `${text$1.slice(0, RESULT_MAX_CHARS)}\n…cut at ${String(RESULT_MAX_CHARS)} characters` : text$1,
|
|
60
|
+
"```"
|
|
61
|
+
].join("\n");
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Compose the failure sentence for an action that was missing an argument.
|
|
65
|
+
* @param action - the action being run.
|
|
66
|
+
* @param missing - the argument it needs.
|
|
67
|
+
* @returns the model-facing sentence.
|
|
68
|
+
*/
|
|
69
|
+
function requireArg(action, missing) {
|
|
70
|
+
return `ui_preview "${action}" needs a \`${missing}\` argument`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Register the model-facing UI preview tool.
|
|
74
|
+
* @param ctx - registrant context carrying the tool registry and the sidebar service.
|
|
75
|
+
* @param config - the deployment's tool configuration.
|
|
76
|
+
*/
|
|
77
|
+
function apply(ctx, config) {
|
|
78
|
+
const registry = ctx.get("tools");
|
|
79
|
+
if (registry === void 0) return;
|
|
80
|
+
registry.register(defineTool({
|
|
81
|
+
name: "ui_preview",
|
|
82
|
+
description: "See and drive a UI in the Preview panel: open a page or a workspace file in an iframe that is SAME-ORIGIN with the GUI, then read its DOM, run JavaScript in it, read its console, or click and type into it. Use this to verify a frontend change you just made, to find out why an element is not where it should be, or to drive a flow end to end.\nActions: open (a workspace file path or an http(s) URL), dom (rendered DOM + text + box metrics, optionally under a CSS selector), eval (an expression in the frame, returned as JSON), console (buffered log/warn/error and uncaught errors), click, type (set a value and dispatch input + change), reload, resize (frame viewport), close.\nThe Preview panel must already be open in the session, and a command fails with a clear reason rather than waiting when it is not. A page that is not same-origin (a URL the Host refuses to proxy, or one that redirected off this origin) cannot be inspected: the tool says so instead of returning an empty DOM.",
|
|
83
|
+
parameters: {
|
|
84
|
+
action: {
|
|
85
|
+
type: "string",
|
|
86
|
+
required: true,
|
|
87
|
+
enum: ACTIONS,
|
|
88
|
+
description: "What to do. `open` points the panel at a file or URL; `dom`/`eval`/`console` inspect it; `click`/`type` drive it; `reload`/`resize`/`close` manage the surface."
|
|
89
|
+
},
|
|
90
|
+
url: {
|
|
91
|
+
type: "string",
|
|
92
|
+
description: "open: an http(s) URL. Use this or `path`, not both."
|
|
93
|
+
},
|
|
94
|
+
path: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "open: a workspace file, absolute or relative to the session workspace."
|
|
97
|
+
},
|
|
98
|
+
workspace: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "The absolute session workspace directory. Defaults to the session's own cwd."
|
|
101
|
+
},
|
|
102
|
+
waitMs: {
|
|
103
|
+
type: "integer",
|
|
104
|
+
description: "open: how long the panel may take to mount and load before giving up. Defaults to the configured command timeout."
|
|
105
|
+
},
|
|
106
|
+
selector: {
|
|
107
|
+
type: "string",
|
|
108
|
+
description: "dom/click/type: a CSS selector. Omitted for `dom` means the whole document."
|
|
109
|
+
},
|
|
110
|
+
expression: {
|
|
111
|
+
type: "string",
|
|
112
|
+
description: "eval: a JavaScript expression or statement, run in the frame."
|
|
113
|
+
},
|
|
114
|
+
cursor: {
|
|
115
|
+
type: "integer",
|
|
116
|
+
description: "console: a cursor from a previous call; omitted starts at the oldest retained line."
|
|
117
|
+
},
|
|
118
|
+
text: {
|
|
119
|
+
type: "string",
|
|
120
|
+
description: "type: the value to set before dispatching input and change."
|
|
121
|
+
},
|
|
122
|
+
key: {
|
|
123
|
+
type: "string",
|
|
124
|
+
description: "type: an optional key name to dispatch after the value is set, e.g. \"Enter\"."
|
|
125
|
+
},
|
|
126
|
+
width: {
|
|
127
|
+
type: "integer",
|
|
128
|
+
description: "resize: frame viewport width in CSS pixels."
|
|
129
|
+
},
|
|
130
|
+
height: {
|
|
131
|
+
type: "integer",
|
|
132
|
+
description: "resize: frame viewport height in CSS pixels."
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
output: {
|
|
136
|
+
schema: {
|
|
137
|
+
type: "object",
|
|
138
|
+
additionalProperties: false,
|
|
139
|
+
properties: {
|
|
140
|
+
action: {
|
|
141
|
+
type: "string",
|
|
142
|
+
required: true,
|
|
143
|
+
enum: ACTIONS
|
|
144
|
+
},
|
|
145
|
+
ok: {
|
|
146
|
+
type: "boolean",
|
|
147
|
+
required: true
|
|
148
|
+
},
|
|
149
|
+
summary: {
|
|
150
|
+
type: "string",
|
|
151
|
+
required: true,
|
|
152
|
+
description: "One line describing what happened."
|
|
153
|
+
},
|
|
154
|
+
detail: {
|
|
155
|
+
type: "string",
|
|
156
|
+
description: "A JSON body: the DOM reading, the evaluated value, the console entries, or the frame state."
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
render: (_args, value) => [text(value.summary), ...value.detail === void 0 ? [] : [text(value.detail)]]
|
|
161
|
+
},
|
|
162
|
+
isConcurrencySafe: () => false,
|
|
163
|
+
async execute(args, exec) {
|
|
164
|
+
const service = ctx.advancedSidebar;
|
|
165
|
+
const action = args.action;
|
|
166
|
+
const sessionId = exec.agent?.session.id ?? "";
|
|
167
|
+
const workspace = args.workspace ?? exec.agent?.session.header.cwd;
|
|
168
|
+
switch (action) {
|
|
169
|
+
case "open": {
|
|
170
|
+
const waitMs = args.waitMs ?? config.commandTimeoutMs;
|
|
171
|
+
if (args.url !== void 0 && args.url !== "") {
|
|
172
|
+
const parsed = parseHttpUrl(args.url);
|
|
173
|
+
if (!parsed.ok) return {
|
|
174
|
+
action,
|
|
175
|
+
ok: false,
|
|
176
|
+
summary: parsed.message
|
|
177
|
+
};
|
|
178
|
+
const outcome$1 = await service.openUrl(sessionId, parsed.url.href, waitMs);
|
|
179
|
+
return outcome$1.ok ? {
|
|
180
|
+
action,
|
|
181
|
+
ok: true,
|
|
182
|
+
summary: outcome$1.message,
|
|
183
|
+
detail: jsonBlock(outcome$1.detail)
|
|
184
|
+
} : {
|
|
185
|
+
action,
|
|
186
|
+
ok: false,
|
|
187
|
+
summary: outcome$1.message
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (args.path === void 0 || args.path === "") return {
|
|
191
|
+
action,
|
|
192
|
+
ok: false,
|
|
193
|
+
summary: `${requireArg(action, "path")} (or a \`url\` to open)`
|
|
194
|
+
};
|
|
195
|
+
if (workspace === void 0 || workspace === "") return {
|
|
196
|
+
action,
|
|
197
|
+
ok: false,
|
|
198
|
+
summary: "ui_preview needs a workspace: this call has no session working directory, so pass `workspace` with the absolute session directory"
|
|
199
|
+
};
|
|
200
|
+
const root = await resolveWorkspace(ctx, workspace, exec.signal);
|
|
201
|
+
if (!root.ok) return {
|
|
202
|
+
action,
|
|
203
|
+
ok: false,
|
|
204
|
+
summary: root.rejection.message
|
|
205
|
+
};
|
|
206
|
+
const absolute = args.path.startsWith("/") ? args.path : `${root.value.processPath.replace(/\/$/u, "")}/${args.path}`;
|
|
207
|
+
const inside = await resolveInside(ctx, root.value, absolute, exec.signal);
|
|
208
|
+
if (!inside.ok) return {
|
|
209
|
+
action,
|
|
210
|
+
ok: false,
|
|
211
|
+
summary: inside.rejection.message
|
|
212
|
+
};
|
|
213
|
+
const info = await service.describeFile({
|
|
214
|
+
workspacePath: root.value.processPath,
|
|
215
|
+
path: inside.value.processPath
|
|
216
|
+
}, exec.signal);
|
|
217
|
+
if (!info.ok) return {
|
|
218
|
+
action,
|
|
219
|
+
ok: false,
|
|
220
|
+
summary: info.message
|
|
221
|
+
};
|
|
222
|
+
const outcome = await service.openFile({
|
|
223
|
+
sessionId,
|
|
224
|
+
workspacePath: root.value.processPath,
|
|
225
|
+
filePath: inside.value.processPath,
|
|
226
|
+
kind: info.kind,
|
|
227
|
+
waitMs
|
|
228
|
+
});
|
|
229
|
+
return outcome.ok ? {
|
|
230
|
+
action,
|
|
231
|
+
ok: true,
|
|
232
|
+
summary: outcome.message,
|
|
233
|
+
detail: jsonBlock({
|
|
234
|
+
path: inside.value.processPath,
|
|
235
|
+
kind: info.kind,
|
|
236
|
+
contentType: info.contentType,
|
|
237
|
+
bytes: info.bytes,
|
|
238
|
+
sameOrigin: true
|
|
239
|
+
})
|
|
240
|
+
} : {
|
|
241
|
+
action,
|
|
242
|
+
ok: false,
|
|
243
|
+
summary: outcome.message
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
case "dom":
|
|
247
|
+
case "eval":
|
|
248
|
+
case "console":
|
|
249
|
+
case "click":
|
|
250
|
+
case "type":
|
|
251
|
+
case "reload":
|
|
252
|
+
case "resize":
|
|
253
|
+
case "close": {
|
|
254
|
+
if (action === "eval" && (args.expression === void 0 || args.expression === "")) return {
|
|
255
|
+
action,
|
|
256
|
+
ok: false,
|
|
257
|
+
summary: requireArg(action, "expression")
|
|
258
|
+
};
|
|
259
|
+
if ((action === "click" || action === "type") && (args.selector === void 0 || args.selector === "")) return {
|
|
260
|
+
action,
|
|
261
|
+
ok: false,
|
|
262
|
+
summary: requireArg(action, "selector")
|
|
263
|
+
};
|
|
264
|
+
if (action === "type" && args.text === void 0) return {
|
|
265
|
+
action,
|
|
266
|
+
ok: false,
|
|
267
|
+
summary: requireArg(action, "text")
|
|
268
|
+
};
|
|
269
|
+
if (action === "resize" && (args.width === void 0 || args.height === void 0)) return {
|
|
270
|
+
action,
|
|
271
|
+
ok: false,
|
|
272
|
+
summary: "ui_preview \"resize\" needs both a `width` and a `height`"
|
|
273
|
+
};
|
|
274
|
+
const body = {
|
|
275
|
+
kind: action === "type" ? "input" : action,
|
|
276
|
+
...args.selector === void 0 ? {} : { selector: args.selector },
|
|
277
|
+
...args.expression === void 0 ? {} : { expression: args.expression },
|
|
278
|
+
...args.cursor === void 0 ? {} : { cursor: args.cursor },
|
|
279
|
+
...args.text === void 0 ? {} : { text: args.text },
|
|
280
|
+
...args.key === void 0 ? {} : { key: args.key },
|
|
281
|
+
...args.width === void 0 ? {} : { width: args.width },
|
|
282
|
+
...args.height === void 0 ? {} : { height: args.height }
|
|
283
|
+
};
|
|
284
|
+
const outcome = await service.queueCommand(sessionId, body);
|
|
285
|
+
if (!outcome.ok) return {
|
|
286
|
+
action,
|
|
287
|
+
ok: false,
|
|
288
|
+
summary: outcome.message
|
|
289
|
+
};
|
|
290
|
+
return describe(action, outcome.result);
|
|
291
|
+
}
|
|
292
|
+
default: return {
|
|
293
|
+
action,
|
|
294
|
+
ok: false,
|
|
295
|
+
summary: `unknown action ${String(action)}`
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
presentCall: (args) => ({
|
|
300
|
+
card: "generic",
|
|
301
|
+
title: args.action === "open" ? `Preview ${args.url ?? args.path ?? ""}` : `Preview ${args.action}${args.selector === void 0 ? "" : ` ${args.selector}`}`,
|
|
302
|
+
kind: "other",
|
|
303
|
+
rawInput: args
|
|
304
|
+
})
|
|
305
|
+
}));
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Phrase one command result for the model.
|
|
309
|
+
* @param action - the action that produced it.
|
|
310
|
+
* @param result - the panel's answer.
|
|
311
|
+
* @returns the tool's own answer.
|
|
312
|
+
*/
|
|
313
|
+
function describe(action, result) {
|
|
314
|
+
if (result.kind === "dom") {
|
|
315
|
+
const lines = result.nodes.map((node) => `${" ".repeat(node.depth)}<${node.tag}${node.selector === "" ? "" : ` ${node.selector}`}> ${node.display} ${String(Math.round(node.box.width))}×${String(Math.round(node.box.height))}${node.text === "" ? "" : ` — ${JSON.stringify(node.text.slice(0, 120))}`}`);
|
|
316
|
+
return {
|
|
317
|
+
action,
|
|
318
|
+
ok: true,
|
|
319
|
+
summary: `${String(result.nodes.length)} element(s) under ${result.selector === "" ? "the document" : JSON.stringify(result.selector)} in a ${String(result.viewport.width)}×${String(result.viewport.height)} viewport${result.truncated ? " (cut short)" : ""}.`,
|
|
320
|
+
detail: [
|
|
321
|
+
lines.join("\n"),
|
|
322
|
+
"",
|
|
323
|
+
jsonBlock(result)
|
|
324
|
+
].join("\n")
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
if (result.kind === "eval") return {
|
|
328
|
+
action,
|
|
329
|
+
ok: true,
|
|
330
|
+
summary: `The expression returned ${result.note === void 0 ? "" : `${result.note}: `}${result.value.slice(0, 400)}`,
|
|
331
|
+
detail: jsonBlock(result)
|
|
332
|
+
};
|
|
333
|
+
if (result.kind === "console") return {
|
|
334
|
+
action,
|
|
335
|
+
ok: true,
|
|
336
|
+
summary: result.entries.length === 0 ? "No console output since that cursor." : `${String(result.entries.length)} console entr(ies); cursor ${String(result.cursor)}${result.lossy ? " (earlier lines fell out of the retained window)" : ""}.`,
|
|
337
|
+
...result.entries.length === 0 ? {} : { detail: [
|
|
338
|
+
result.entries.map((entry) => `[${entry.level}] ${entry.text}`).join("\n"),
|
|
339
|
+
"",
|
|
340
|
+
jsonBlock(result)
|
|
341
|
+
].join("\n") }
|
|
342
|
+
};
|
|
343
|
+
return {
|
|
344
|
+
action,
|
|
345
|
+
ok: true,
|
|
346
|
+
summary: result.detail,
|
|
347
|
+
detail: jsonBlock(result)
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
//#endregion
|
|
352
|
+
export { Config, apply, inject, name };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@achasoft/dsh-advanced-sidebar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Advanced sidebar operations for the DeepSeek Harness Web Client: git changes, multi-session terminals, a file browser, a dev-server preview, background tasks, Open in, Archive and Delete, in a resizable dock beside the conversation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
"types": "./types/host/index.d.ts",
|
|
22
22
|
"default": "./lib/host.js"
|
|
23
23
|
},
|
|
24
|
+
"./ui-preview": {
|
|
25
|
+
"types": "./types/ui-preview.d.ts",
|
|
26
|
+
"default": "./lib/ui-preview.js"
|
|
27
|
+
},
|
|
24
28
|
"./client": {
|
|
25
29
|
"default": "./lib/client.js"
|
|
26
30
|
},
|
|
@@ -66,8 +70,8 @@
|
|
|
66
70
|
"@deepseek-ai/dsh-llm": "*",
|
|
67
71
|
"@deepseek-ai/dsh-session": "*",
|
|
68
72
|
"@deepseek-ai/dsh-session-persistence": "*",
|
|
69
|
-
"@deepseek-ai/dsh-settings": "*",
|
|
70
73
|
"@deepseek-ai/dsh-subprocess": "*",
|
|
74
|
+
"@deepseek-ai/dsh-tools": "*",
|
|
71
75
|
"@deepseek-ai/dsh-typert-protocol": "*",
|
|
72
76
|
"@deepseek-ai/dsh-workspace": "*",
|
|
73
77
|
"@deepseek-ai/schemastery": "*"
|
|
@@ -93,12 +97,14 @@
|
|
|
93
97
|
"@deepseek-ai/dsh-client-ui-slots": "link:../../deepseek-harness/packages/client/ui-slots",
|
|
94
98
|
"@deepseek-ai/dsh-client-ui-theme": "link:../../deepseek-harness/packages/client/ui-theme",
|
|
95
99
|
"@deepseek-ai/dsh-fs": "link:../../deepseek-harness/packages/fs/fs",
|
|
100
|
+
"@deepseek-ai/dsh-host-webserver": "link:../../deepseek-harness/packages/host/webserver",
|
|
96
101
|
"@deepseek-ai/dsh-jobs": "link:../../deepseek-harness/packages/jobs/jobs",
|
|
97
102
|
"@deepseek-ai/dsh-llm": "link:../../deepseek-harness/packages/llm/llm",
|
|
98
103
|
"@deepseek-ai/dsh-session": "link:../../deepseek-harness/packages/core/session",
|
|
99
104
|
"@deepseek-ai/dsh-session-persistence": "link:../../deepseek-harness/packages/session/session-persistence",
|
|
100
105
|
"@deepseek-ai/dsh-settings": "link:../../deepseek-harness/packages/settings/settings",
|
|
101
106
|
"@deepseek-ai/dsh-subprocess": "link:../../deepseek-harness/packages/subprocess/subprocess",
|
|
107
|
+
"@deepseek-ai/dsh-tools": "link:../../deepseek-harness/packages/core/tools",
|
|
102
108
|
"@deepseek-ai/dsh-typert-protocol": "link:../../deepseek-harness/packages/typert/protocol",
|
|
103
109
|
"@deepseek-ai/dsh-workspace": "link:../../deepseek-harness/packages/workspace/workspace",
|
|
104
110
|
"@deepseek-ai/schemastery": "link:../../deepseek-harness/vendor/schemastery",
|
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
import type { AdvancedSidebarSettings, AdvancedSidebarView } from '../host/types.ts';
|
|
11
11
|
import type { MenuInjected, Translate } from './contract.ts';
|
|
12
12
|
import type { OperationTarget, PanelKind } from './controller.ts';
|
|
13
|
+
/** What the menu needs to know about the harness's session-log export for this session. */
|
|
14
|
+
export interface LogDownloadRow {
|
|
15
|
+
/** Whether this plugin is the download surface; false leaves the entry out entirely. */
|
|
16
|
+
active: boolean;
|
|
17
|
+
/** Whether this session's export is in flight, which disables the entry as the harness's did. */
|
|
18
|
+
busy: boolean;
|
|
19
|
+
}
|
|
13
20
|
/** Everything the menu renders from, plus the callbacks it fires. */
|
|
14
21
|
export interface ActionMenuProps {
|
|
15
22
|
/** The session and directory every entry acts on; absent disables everything but the trigger. */
|
|
@@ -21,11 +28,19 @@ export interface ActionMenuProps {
|
|
|
21
28
|
/** The namespace translator. */
|
|
22
29
|
t: Translate;
|
|
23
30
|
/** Business callbacks, minus the reactive sources the seat binds itself. */
|
|
24
|
-
actions: Pick<MenuInjected, 'openPanel' | 'openIn' | 'openWindow' | 'archive' | 'requestDelete'>;
|
|
31
|
+
actions: Pick<MenuInjected, 'openPanel' | 'openIn' | 'openWindow' | 'archive' | 'requestDelete' | 'downloadLog'>;
|
|
25
32
|
/** Ask the Host for a fresh capability view; called each time the menu opens. */
|
|
26
33
|
refresh: () => void;
|
|
27
34
|
/** Which panel the dock is showing, so the open entry reads as the current one. */
|
|
28
35
|
openPanel: PanelKind | undefined;
|
|
36
|
+
/** The session-log export entry's state. */
|
|
37
|
+
logDownload: LogDownloadRow;
|
|
38
|
+
/**
|
|
39
|
+
* Offer Download session log and nothing else. Set when the settings section switched the menu
|
|
40
|
+
* out of the header: this plugin still shadows the harness's own download button, so it stands in
|
|
41
|
+
* for exactly that button rather than taking the verb away with the menu.
|
|
42
|
+
*/
|
|
43
|
+
logsOnly: boolean;
|
|
29
44
|
}
|
|
30
45
|
/**
|
|
31
46
|
* The trigger plus its menu.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The entry that shadows the harness's session-log download button.
|
|
3
|
+
*
|
|
4
|
+
* It occupies the `session-log-download` cell of the header's utilities row one priority ahead of
|
|
5
|
+
* the harness, which is what removes the second "⋯" (see `log-download.ts` for why shadowing was
|
|
6
|
+
* chosen). What it renders in that cell is no button at all — the verb now lives in this plugin's
|
|
7
|
+
* menu — only the export dialog the shadowed entry used to render beside its button.
|
|
8
|
+
*
|
|
9
|
+
* The dialog is a faithful re-rendering of the harness's `SessionLogDownloadDialog` (0.1.5-rc.2): the
|
|
10
|
+
* same `Modal` and `Button` from `@deepseek-ai/dsh-client-ui-primitives`, the same status-to-copy
|
|
11
|
+
* mapping, bound to the same controller state. It is not this plugin's own kit `Dialog` on purpose:
|
|
12
|
+
* the dialog also opens after a successful `/export` command, where it has always been the
|
|
13
|
+
* harness's, and a person should not see it change shape because a plugin is installed.
|
|
14
|
+
* @module @achasoft/dsh-advanced-sidebar/client/LogDownloadDialog
|
|
15
|
+
*/
|
|
16
|
+
import type { LogDownloadSeatProps } from './contract.ts';
|
|
17
|
+
/**
|
|
18
|
+
* The export dialog for the session this header belongs to.
|
|
19
|
+
* @param props - the session, the bridged export state, the dismiss callback, and the translator.
|
|
20
|
+
* @returns the modal portal while this session's dialog is open; nothing otherwise, which leaves no
|
|
21
|
+
* box in the header row.
|
|
22
|
+
* @see {@link LogDownloadSeatProps}
|
|
23
|
+
*/
|
|
24
|
+
export declare function LogDownloadDialog(props: LogDownloadSeatProps): import("react").JSX.Element | null;
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client';
|
|
11
11
|
import type { InjectFace, PropsLocale, PropsRuntime, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
|
|
12
|
-
import type { AdvancedSidebarSettings, AdvancedSidebarView, DeleteSessionResult, GitCommitMessageResult, GitCommitResult, GitDiffRequest, GitDiffResult, GitPushResult, GitStageResult, GitStatusResult, ListEntriesResult, PreviewListResult, PreviewLogsResult, PreviewStartResult, PreviewStopResult, ReadFileResult, TaskKillResult, TaskOutputResult, TerminalAckResult, TerminalOpenResult, TerminalReadResult } from '../host/types.ts';
|
|
12
|
+
import type { AdvancedSidebarSettings, AdvancedSidebarView, DeleteSessionResult, GitCommitMessageResult, GitCommitResult, GitDiffRequest, GitDiffResult, GitPushResult, GitStageResult, GitStatusResult, ListEntriesResult, PreviewFileInfoResult, PreviewListResult, PreviewLogsResult, PreviewPollRequest, PreviewPollResult, PreviewResultAck, PreviewResultRequest, PreviewReleaseResult, PreviewStartResult, PreviewStopResult, ReadFileResult, TaskKillResult, TaskOutputResult, TerminalAckResult, TerminalOpenResult, TerminalReadResult } from '../host/types.ts';
|
|
13
13
|
import type { OperationTarget, PanelController, PanelKind } from './controller.ts';
|
|
14
|
+
import type { LogDownloadBridge } from './log-download.ts';
|
|
14
15
|
import type { AdvancedSidebarKey } from './locales.ts';
|
|
15
16
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
16
17
|
interface LocaleNamespaceMap {
|
|
@@ -37,6 +38,11 @@ export interface MenuInjected {
|
|
|
37
38
|
sidebar: PanelController;
|
|
38
39
|
/** The bound `advanced-sidebar` settings scope, which decides the menu's entries. */
|
|
39
40
|
settings: SettingsScope<AdvancedSidebarSettings>;
|
|
41
|
+
/**
|
|
42
|
+
* The harness's session-log export, bridged: inactive while its package is absent or its header
|
|
43
|
+
* button is not being shadowed, which is when the menu offers no Download entry.
|
|
44
|
+
*/
|
|
45
|
+
logDownload: LogDownloadBridge;
|
|
40
46
|
};
|
|
41
47
|
/**
|
|
42
48
|
* Read the Host's capability view so an entry can be disabled with a reason.
|
|
@@ -59,6 +65,12 @@ export interface MenuInjected {
|
|
|
59
65
|
openIn: (targetId: string, path: string) => Promise<void>;
|
|
60
66
|
/** Open the Web Client in a second browser window. */
|
|
61
67
|
openWindow: () => void;
|
|
68
|
+
/**
|
|
69
|
+
* Start the harness's session-log export for one session; its progress dialog is the shadowing
|
|
70
|
+
* entry's, not the menu's.
|
|
71
|
+
* @param sessionId - the session whose log, sub-sessions and attachments are exported.
|
|
72
|
+
*/
|
|
73
|
+
downloadLog: (sessionId: string) => void;
|
|
62
74
|
/**
|
|
63
75
|
* Archive one session.
|
|
64
76
|
* @param target - the session to hide.
|
|
@@ -74,6 +86,21 @@ export interface MenuInjected {
|
|
|
74
86
|
}
|
|
75
87
|
/** Full props of the session-header trigger. */
|
|
76
88
|
export type HeaderMenuProps = PropsRuntime<'conversation.session.header.utilities'> & PropsLocale<'advancedSidebar'> & InjectFace<MenuInjected>;
|
|
89
|
+
/** Everything the entry shadowing the harness's download button needs. */
|
|
90
|
+
export interface LogDownloadSeatInjected {
|
|
91
|
+
/** Registrant-private reactive sources the renderer binds to `use<Name>` hooks. */
|
|
92
|
+
hooks: {
|
|
93
|
+
/** The bridged export state; the dialog renders only while it is active. */
|
|
94
|
+
logDownload: LogDownloadBridge;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Close one session's dialog without cancelling its export.
|
|
98
|
+
* @param sessionId - the session whose dialog closes.
|
|
99
|
+
*/
|
|
100
|
+
dismiss: (sessionId: string) => void;
|
|
101
|
+
}
|
|
102
|
+
/** Full props of the entry shadowing the harness's download button. */
|
|
103
|
+
export type LogDownloadSeatProps = PropsRuntime<'conversation.session.header.utilities'> & PropsLocale<'advancedSidebar'> & InjectFace<LogDownloadSeatInjected>;
|
|
77
104
|
/** Everything the dock and its confirmation dialog need. */
|
|
78
105
|
export interface PanelHostInjected {
|
|
79
106
|
/** Registrant-private reactive sources the renderer binds to `use<Name>` hooks. */
|
|
@@ -217,6 +244,35 @@ export interface PanelHostInjected {
|
|
|
217
244
|
* @returns the delta and the state, or a classified failure.
|
|
218
245
|
*/
|
|
219
246
|
previewLogs: (serverId: string, fromOffset: number) => Promise<PreviewLogsResult>;
|
|
247
|
+
/**
|
|
248
|
+
* Describe one workspace file for the Preview panel's Files mode.
|
|
249
|
+
* @param workspacePath - the workspace the path must stay inside.
|
|
250
|
+
* @param path - the file.
|
|
251
|
+
* @param signal - cancellation for the reads.
|
|
252
|
+
* @returns the file's kind, size, same-origin URL, and change token.
|
|
253
|
+
*/
|
|
254
|
+
previewFileInfo: (workspacePath: string, path: string, signal?: AbortSignal) => Promise<PreviewFileInfoResult>;
|
|
255
|
+
/**
|
|
256
|
+
* Register this panel and take whatever the agent queued for it.
|
|
257
|
+
*
|
|
258
|
+
* This is both the poll and the heartbeat: the Host trusts a panel only while it keeps calling,
|
|
259
|
+
* which is what turns a closed tab into a clear tool failure instead of a wait.
|
|
260
|
+
* @param request - which panel, where it is, and whether a preview is rendered.
|
|
261
|
+
* @returns the work to do, or a classified failure.
|
|
262
|
+
*/
|
|
263
|
+
previewPoll: (request: PreviewPollRequest) => Promise<PreviewPollResult>;
|
|
264
|
+
/**
|
|
265
|
+
* Report what one agent command did.
|
|
266
|
+
* @param request - the panel, the command id, and the outcome.
|
|
267
|
+
* @returns settlement.
|
|
268
|
+
*/
|
|
269
|
+
previewResult: (request: PreviewResultRequest) => Promise<PreviewResultAck>;
|
|
270
|
+
/**
|
|
271
|
+
* Say that this panel is gone, so its queued work is dropped.
|
|
272
|
+
* @param clientId - this panel's id.
|
|
273
|
+
* @returns settlement.
|
|
274
|
+
*/
|
|
275
|
+
previewRelease: (clientId: string) => Promise<PreviewReleaseResult>;
|
|
220
276
|
/**
|
|
221
277
|
* Open one path with the Host operating system's default application.
|
|
222
278
|
* @param path - the absolute path.
|
package/types/client/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Advanced sidebar plugin, browser half: three registrations over one Host endpoint and one shared
|
|
3
3
|
* piece of state.
|
|
4
4
|
*
|
|
5
|
-
* - `conversation.session.header.utilities` — the menu on the open session
|
|
5
|
+
* - `conversation.session.header.utilities` — the menu on the open session, and, in the same row,
|
|
6
|
+
* the entry shadowing the harness's own session-log download button, whose verb the menu absorbs.
|
|
6
7
|
* - `shell.overlay` — the resizable dock holding whichever panel is open, plus the Delete
|
|
7
8
|
* confirmation.
|
|
8
9
|
* - `settings.plugin.item` — the card on the plugin-configuration tab, keyed by the namespace.
|
|
@@ -18,7 +19,8 @@
|
|
|
18
19
|
*/
|
|
19
20
|
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
|
|
20
21
|
export type { AdvancedSidebarKey } from './locales.ts';
|
|
21
|
-
export type { HeaderMenuProps, MenuInjected, PanelHostInjected, PanelHostProps, SettingsCardInjected, SettingsCardProps, Translate, } from './contract.ts';
|
|
22
|
+
export type { HeaderMenuProps, LogDownloadSeatInjected, LogDownloadSeatProps, MenuInjected, PanelHostInjected, PanelHostProps, SettingsCardInjected, SettingsCardProps, Translate, } from './contract.ts';
|
|
23
|
+
export type { LogDownloadEntry, LogDownloadState, LogDownloadView } from './log-download.ts';
|
|
22
24
|
export type { OperationTarget, PanelKind, SidebarState, TerminalGroup, TerminalTab, } from './controller.ts';
|
|
23
25
|
/**
|
|
24
26
|
* Required services of the OUTER plugin: locale and the Remote mount point.
|