dsh-better-sidebar 0.10.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 +139 -0
- package/README_EN.md +139 -0
- package/lib/client-docx.js +7649 -0
- package/lib/client-editor.js +32677 -0
- package/lib/client-pptx.js +63438 -0
- package/lib/client-registry.js +8021 -0
- package/lib/client-terminal.js +8060 -0
- package/lib/client-xlsx.js +359755 -0
- package/lib/client.js +8021 -0
- package/lib/index.js +2604 -0
- package/lib/invariant.js +22 -0
- package/lib/types/agent-pty.d.ts +220 -0
- package/lib/types/browser-probe.d.ts +15 -0
- package/lib/types/bundle-route.d.ts +13 -0
- package/lib/types/client/BrowserView.d.ts +22 -0
- package/lib/types/client/DiffTab.d.ts +6 -0
- package/lib/types/client/DiffView.d.ts +51 -0
- package/lib/types/client/EditorHost.d.ts +10 -0
- package/lib/types/client/ExplorerView.d.ts +9 -0
- package/lib/types/client/GitView.d.ts +8 -0
- package/lib/types/client/OrphanedTab.d.ts +11 -0
- package/lib/types/client/PdfView.d.ts +6 -0
- package/lib/types/client/PptxView.d.ts +6 -0
- package/lib/types/client/SandboxStatusBar.d.ts +10 -0
- package/lib/types/client/SideCardSection.d.ts +27 -0
- package/lib/types/client/Sidebar.d.ts +6 -0
- package/lib/types/client/SubagentView.d.ts +16 -0
- package/lib/types/client/TabBar.d.ts +38 -0
- package/lib/types/client/TerminalView.d.ts +8 -0
- package/lib/types/client/TextEditor.d.ts +10 -0
- package/lib/types/client/api.d.ts +170 -0
- package/lib/types/client/binary-download.d.ts +12 -0
- package/lib/types/client/breakpoints.d.ts +12 -0
- package/lib/types/client/browser.d.ts +50 -0
- package/lib/types/client/builtins/index.d.ts +17 -0
- package/lib/types/client/builtins/tabs.d.ts +6 -0
- package/lib/types/client/builtins/viewers.d.ts +3 -0
- package/lib/types/client/chunk-loader.d.ts +74 -0
- package/lib/types/client/chunks/docx.d.ts +8 -0
- package/lib/types/client/chunks/editor.d.ts +9 -0
- package/lib/types/client/chunks/pptx.d.ts +8 -0
- package/lib/types/client/chunks/terminal.d.ts +9 -0
- package/lib/types/client/chunks/xlsx.d.ts +9 -0
- package/lib/types/client/cm-themes.d.ts +24 -0
- package/lib/types/client/conversation-draft.d.ts +14 -0
- package/lib/types/client/docx-view.d.ts +8 -0
- package/lib/types/client/editor-load.d.ts +66 -0
- package/lib/types/client/icons.d.ts +47 -0
- package/lib/types/client/image-types.d.ts +3 -0
- package/lib/types/client/ime-guard.d.ts +36 -0
- package/lib/types/client/index.d.ts +10 -0
- package/lib/types/client/intercept.d.ts +20 -0
- package/lib/types/client/lang.d.ts +12 -0
- package/lib/types/client/lazy-chunk.d.ts +25 -0
- package/lib/types/client/link-intercept.d.ts +35 -0
- package/lib/types/client/locales.d.ts +237 -0
- package/lib/types/client/office-shared.d.ts +33 -0
- package/lib/types/client/office-types.d.ts +36 -0
- package/lib/types/client/office-view.d.ts +12 -0
- package/lib/types/client/openpath-intercept.d.ts +41 -0
- package/lib/types/client/paths.d.ts +19 -0
- package/lib/types/client/pdf-types.d.ts +2 -0
- package/lib/types/client/prefs.d.ts +30 -0
- package/lib/types/client/produced-files.d.ts +27 -0
- package/lib/types/client/selection-payload.d.ts +27 -0
- package/lib/types/client/service.d.ts +214 -0
- package/lib/types/client/split-pane.d.ts +29 -0
- package/lib/types/client/state.d.ts +322 -0
- package/lib/types/client/subagent-activity.d.ts +35 -0
- package/lib/types/client/subagent-detect.d.ts +47 -0
- package/lib/types/client/subagent-jobs.d.ts +63 -0
- package/lib/types/client/theme.d.ts +28 -0
- package/lib/types/client/xlsx-to-univer.d.ts +33 -0
- package/lib/types/client/xlsx-view.d.ts +10 -0
- package/lib/types/config.d.ts +41 -0
- package/lib/types/context-types.d.ts +431 -0
- package/lib/types/fs-tree.d.ts +41 -0
- package/lib/types/git.d.ts +75 -0
- package/lib/types/html-route.d.ts +48 -0
- package/lib/types/index.d.ts +39 -0
- package/lib/types/invariant.d.ts +15 -0
- package/lib/types/jobs-routes.d.ts +45 -0
- package/lib/types/prefs-shared.d.ts +106 -0
- package/lib/types/pty-manager.d.ts +76 -0
- package/lib/types/tools.d.ts +28 -0
- package/lib/types/trust-fence.d.ts +24 -0
- package/lib/types/wire.d.ts +38 -0
- package/package.json +168 -0
- package/src/agent-pty.ts +519 -0
- package/src/browser-probe.ts +26 -0
- package/src/bundle-route.ts +130 -0
- package/src/client/BrowserView.tsx +253 -0
- package/src/client/DiffTab.tsx +110 -0
- package/src/client/DiffView.tsx +266 -0
- package/src/client/EditorHost.tsx +101 -0
- package/src/client/ExplorerView.tsx +283 -0
- package/src/client/GitView.tsx +540 -0
- package/src/client/OrphanedTab.tsx +27 -0
- package/src/client/PdfView.tsx +110 -0
- package/src/client/PptxView.tsx +131 -0
- package/src/client/SandboxStatusBar.tsx +60 -0
- package/src/client/SideCardSection.module.css +271 -0
- package/src/client/SideCardSection.tsx +426 -0
- package/src/client/Sidebar.tsx +861 -0
- package/src/client/SubagentView.module.css +547 -0
- package/src/client/SubagentView.tsx +866 -0
- package/src/client/TabBar.tsx +188 -0
- package/src/client/TerminalView.tsx +233 -0
- package/src/client/TextEditor.tsx +382 -0
- package/src/client/api.ts +215 -0
- package/src/client/binary-download.tsx +23 -0
- package/src/client/breakpoints.ts +51 -0
- package/src/client/browser.ts +119 -0
- package/src/client/builtins/index.ts +33 -0
- package/src/client/builtins/tabs.tsx +211 -0
- package/src/client/builtins/viewers.tsx +151 -0
- package/src/client/chunk-loader.ts +196 -0
- package/src/client/chunks/docx.tsx +8 -0
- package/src/client/chunks/editor.tsx +9 -0
- package/src/client/chunks/pptx.tsx +8 -0
- package/src/client/chunks/terminal.tsx +9 -0
- package/src/client/chunks/xlsx.tsx +9 -0
- package/src/client/cm-themes.ts +129 -0
- package/src/client/conversation-draft.ts +29 -0
- package/src/client/css-modules.d.ts +5 -0
- package/src/client/docx-view.tsx +113 -0
- package/src/client/editor-load.ts +92 -0
- package/src/client/icons.tsx +137 -0
- package/src/client/image-types.ts +8 -0
- package/src/client/ime-guard.ts +47 -0
- package/src/client/index.tsx +238 -0
- package/src/client/intercept.tsx +94 -0
- package/src/client/lang.ts +95 -0
- package/src/client/layout.css +54 -0
- package/src/client/lazy-chunk.tsx +89 -0
- package/src/client/link-intercept.ts +66 -0
- package/src/client/locales.ts +476 -0
- package/src/client/office-shared.tsx +40 -0
- package/src/client/office-types.ts +48 -0
- package/src/client/office-view.tsx +12 -0
- package/src/client/openpath-intercept.ts +62 -0
- package/src/client/paths.ts +28 -0
- package/src/client/pdf-types.ts +4 -0
- package/src/client/prefs.ts +100 -0
- package/src/client/produced-files.ts +87 -0
- package/src/client/selection-payload.ts +86 -0
- package/src/client/service.ts +434 -0
- package/src/client/sidebar.module.css +1917 -0
- package/src/client/split-pane.tsx +292 -0
- package/src/client/state.ts +1048 -0
- package/src/client/subagent-activity.ts +67 -0
- package/src/client/subagent-detect.ts +125 -0
- package/src/client/subagent-jobs.ts +161 -0
- package/src/client/theme.ts +44 -0
- package/src/client/xlsx-to-univer.ts +260 -0
- package/src/client/xlsx-view.tsx +116 -0
- package/src/config.ts +95 -0
- package/src/context-types.ts +445 -0
- package/src/fs-tree.ts +115 -0
- package/src/git.ts +236 -0
- package/src/html-route.ts +70 -0
- package/src/index.ts +897 -0
- package/src/invariant.ts +32 -0
- package/src/jobs-routes.ts +257 -0
- package/src/prefs-shared.ts +127 -0
- package/src/pty-manager.ts +199 -0
- package/src/tools.ts +477 -0
- package/src/trust-fence.ts +77 -0
- package/src/wire.ts +94 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** A parsed `git status --porcelain=v1 -z` entry. */
|
|
2
|
+
export interface GitStatusEntry {
|
|
3
|
+
path: string;
|
|
4
|
+
/** Two-letter index/worktree status (X Y), e.g. 'M ', ' M', 'A ', '??'. */
|
|
5
|
+
xy: string;
|
|
6
|
+
}
|
|
7
|
+
/** The source-control panel snapshot. */
|
|
8
|
+
export interface GitStatusResult {
|
|
9
|
+
isRepo: boolean;
|
|
10
|
+
branch?: string;
|
|
11
|
+
entries: GitStatusEntry[];
|
|
12
|
+
}
|
|
13
|
+
/** One `git log` row. */
|
|
14
|
+
export interface GitLogEntry {
|
|
15
|
+
/** Short hash (7+ chars, display). */
|
|
16
|
+
hash: string;
|
|
17
|
+
/** Full 40-char hash (advanced operations: revert / cherry-pick). */
|
|
18
|
+
hashFull: string;
|
|
19
|
+
subject: string;
|
|
20
|
+
author: string;
|
|
21
|
+
/** ISO 8601 author date (`%ai`), e.g. `2024-01-01 10:00:00 +0800`. */
|
|
22
|
+
date: string;
|
|
23
|
+
/** Ref decorations (`%D` with --decorate=short), e.g. `HEAD -> main, origin/main`; '' when none. */
|
|
24
|
+
refs: string;
|
|
25
|
+
}
|
|
26
|
+
/** One git failure (stderr text as the message). */
|
|
27
|
+
export declare class GitCommandError extends Error {
|
|
28
|
+
readonly code: string;
|
|
29
|
+
readonly command: string;
|
|
30
|
+
constructor(message: string, code: string | undefined, command: string);
|
|
31
|
+
}
|
|
32
|
+
/** Parse porcelain v1 -z output into entries (rename/copy pairs collapse to one row). */
|
|
33
|
+
export declare function parsePorcelainZ(output: string): GitStatusEntry[];
|
|
34
|
+
/** Parse `git log --pretty=format:%h%x1f%s%x1f%an%x1f%ai%x1f%H%x1f%D` rows. */
|
|
35
|
+
export declare function parseLogLines(output: string): GitLogEntry[];
|
|
36
|
+
/** Whether the directory is inside a git work tree (exit-0 `git rev-parse`). */
|
|
37
|
+
export declare function isGitRepo(cwd: string): Promise<boolean>;
|
|
38
|
+
/** The repository top level containing `cwd` (`git rev-parse --show-toplevel`). */
|
|
39
|
+
export declare function repoRoot(cwd: string): Promise<string>;
|
|
40
|
+
/** The current branch name (`git rev-parse --abbrev-ref HEAD`; 'HEAD' when detached). */
|
|
41
|
+
export declare function currentBranch(cwd: string): Promise<string>;
|
|
42
|
+
/** Working-tree status (untracked included). */
|
|
43
|
+
export declare function status(cwd: string): Promise<GitStatusResult>;
|
|
44
|
+
/** Diff text of the worktree (unstaged) or the index (staged). */
|
|
45
|
+
export declare function diff(cwd: string, path: string | undefined, staged: boolean): Promise<string>;
|
|
46
|
+
/** Stage paths (all when path is undefined). */
|
|
47
|
+
export declare function stage(cwd: string, path: string | undefined): Promise<void>;
|
|
48
|
+
/** Unstage paths (all when path is undefined). */
|
|
49
|
+
export declare function unstage(cwd: string, path: string | undefined): Promise<void>;
|
|
50
|
+
/** Commit the staged changes with a message (global identity untouched). */
|
|
51
|
+
export declare function commit(cwd: string, message: string): Promise<void>;
|
|
52
|
+
/** Branch names (current first). */
|
|
53
|
+
export declare function branches(cwd: string): Promise<{
|
|
54
|
+
current: string;
|
|
55
|
+
names: string[];
|
|
56
|
+
}>;
|
|
57
|
+
/** Switch to an existing branch. */
|
|
58
|
+
export declare function checkout(cwd: string, branch: string): Promise<void>;
|
|
59
|
+
/** Recent commit history (newest first), lazily pageable via skip/count. */
|
|
60
|
+
export declare function log(cwd: string, count?: number, skip?: number): Promise<GitLogEntry[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Content of a file at a revision (`git show <rev>:<path>`), or null when the
|
|
63
|
+
* revision has no such path (a new/untracked file has no HEAD side).
|
|
64
|
+
*/
|
|
65
|
+
export declare function show(cwd: string, rev: string, path: string): Promise<string | null>;
|
|
66
|
+
/** Full patch text of one commit (`git show` with the commit header suppressed).
|
|
67
|
+
* Merge commits show their diff against the first parent (`-m --first-parent`
|
|
68
|
+
* is a no-op for regular commits), so a history click always has content. */
|
|
69
|
+
export declare function commitDiff(cwd: string, hash: string): Promise<string>;
|
|
70
|
+
/** Discard the worktree changes of one path (`git checkout -- <path>`; the index is untouched). */
|
|
71
|
+
export declare function discard(cwd: string, path: string): Promise<void>;
|
|
72
|
+
/** Revert one commit onto the current branch with an auto-generated message. */
|
|
73
|
+
export declare function revert(cwd: string, hash: string): Promise<void>;
|
|
74
|
+
/** Cherry-pick one commit onto the current branch. */
|
|
75
|
+
export declare function cherryPick(cwd: string, hash: string): Promise<void>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure URL vocabulary of the /sidebar/html route (HTML previewer).
|
|
3
|
+
*
|
|
4
|
+
* Why path-encoded parameters instead of a query string: the previewed
|
|
5
|
+
* page resolves its relative assets (./style.css, img/x.png) against the
|
|
6
|
+
* document URL, and the WHATWG URL algorithm DROPS the query of a
|
|
7
|
+
* path-relative reference — `/sidebar/html?a=1&path=/a/b/` + `./style.css`
|
|
8
|
+
* would lose the session scope and the route would reject the asset.
|
|
9
|
+
* Encoding everything into the URL path keeps relative resolution inside
|
|
10
|
+
* the same route with every request self-contained:
|
|
11
|
+
*
|
|
12
|
+
* /sidebar/html/<sessionId>/<absolute-path segments, encodeURIComponent'd>
|
|
13
|
+
* /sidebar/html/S/Users/me/proj/index.html
|
|
14
|
+
* + ./style.css → /sidebar/html/S/Users/me/proj/style.css
|
|
15
|
+
* Windows: C:\Users\me\a.html → /sidebar/html/S/C%3A/Users/me/a.html
|
|
16
|
+
*
|
|
17
|
+
* This module is intentionally dependency-free (no node imports, no wire
|
|
18
|
+
* helpers) so the client bundle can import `encodeHtmlUrl` without tripping
|
|
19
|
+
* the build-time purity gate; the host converts decode failures into
|
|
20
|
+
* SidebarError responses at the route boundary.
|
|
21
|
+
*/
|
|
22
|
+
/** One decoded route reference. */
|
|
23
|
+
export interface HtmlRouteRef {
|
|
24
|
+
sessionId: string;
|
|
25
|
+
/** Absolute file path (leading slash; Windows drives keep their colon). */
|
|
26
|
+
path: string;
|
|
27
|
+
}
|
|
28
|
+
/** Decode outcome: the reference, or a client-error description. */
|
|
29
|
+
export type HtmlDecodeResult = {
|
|
30
|
+
ok: true;
|
|
31
|
+
ref: HtmlRouteRef;
|
|
32
|
+
} | {
|
|
33
|
+
ok: false;
|
|
34
|
+
status: 400 | 404;
|
|
35
|
+
message: string;
|
|
36
|
+
};
|
|
37
|
+
/** The route prefix both encoders/decoders agree on. */
|
|
38
|
+
export declare const HTML_ROUTE_PREFIX = "/sidebar/html/";
|
|
39
|
+
/** Build the route URL for one absolute file path (client + tests). */
|
|
40
|
+
export declare function encodeHtmlUrl(sessionId: string, path: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Decode a route pathname into the session + absolute file path. Rejects
|
|
43
|
+
* a wrong prefix (404), an empty or double-slash path, malformed percent
|
|
44
|
+
* encoding, and a missing sessionId or file path (400). The caller still
|
|
45
|
+
* must bound the decoded path with requireAbsolute + isWithin(cwd) — a
|
|
46
|
+
* decoded `..` segment resolves outside the cwd and is refused there.
|
|
47
|
+
*/
|
|
48
|
+
export declare function decodeHtmlUrl(pathname: string): HtmlDecodeResult;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Context } from './context-types.ts';
|
|
2
|
+
import { Config, type ResolvedSidebarConfig, type SidebarConfig } from './config.ts';
|
|
3
|
+
export { Config };
|
|
4
|
+
export type { SidebarConfig, ResolvedSidebarConfig };
|
|
5
|
+
export type { Context } from './context-types.ts';
|
|
6
|
+
export type { BetterSidebarService, TabDescriptor, TabComponentProps, FileViewerDescriptor, FileViewerProps, FileFetchStrategy, } from './client/service.ts';
|
|
7
|
+
/** Plugin identity for cordis.yml rows. */
|
|
8
|
+
export declare const name = "dsh-better-sidebar";
|
|
9
|
+
/** Services required before mounting: the webserver routes, the session store, the loader's connection row, and the tool registry. */
|
|
10
|
+
export declare const inject: string[];
|
|
11
|
+
/** Content type served by /sidebar/file (binary-safe fallback for unknowns). */
|
|
12
|
+
export declare function mediaTypeForPath(path: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* The live face of the side card settings namespace, bound to the settings
|
|
15
|
+
* service when it is mounted. The DSH settings RPC domain only serves
|
|
16
|
+
* allowlisted namespaces (api-proxy exposedNamespaces), so the client reads
|
|
17
|
+
* and writes THIS namespace through the plugin's own fenced /sidebar routes,
|
|
18
|
+
* which call the seam in-process — no configuration-client gate involved.
|
|
19
|
+
*/
|
|
20
|
+
export interface SidebarSettingsFace {
|
|
21
|
+
/** The current resolved value + revision (undefined while the settings service is absent). */
|
|
22
|
+
get(): {
|
|
23
|
+
value?: unknown;
|
|
24
|
+
revision?: number;
|
|
25
|
+
};
|
|
26
|
+
/** Merge a patch (revision-guarded) and return the fresh resolved view. */
|
|
27
|
+
update(patch: Record<string, unknown>, expectedRevision?: number): Promise<{
|
|
28
|
+
value?: unknown;
|
|
29
|
+
revision?: number;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Plugin body: mount the fenced routes and the pty lifecycle.
|
|
34
|
+
* @param ctx - host plugin context (webServer, sessions, loader).
|
|
35
|
+
* @param config - deployment-provided limits; the Loader validates against
|
|
36
|
+
* {@link Config} and fills defaults, direct callers get them from
|
|
37
|
+
* {@link resolveSidebarConfig}.
|
|
38
|
+
*/
|
|
39
|
+
export declare function apply(ctx: Context, config?: SidebarConfig): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `dsh-better-sidebar`.
|
|
3
|
+
* @module dsh-better-sidebar/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from './context-types.ts';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "dsh-better-sidebar-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background-job routes of the /sidebar JSON API ('jobs.output' /
|
|
3
|
+
* 'jobs.kill'). The job LIST needs no route: it arrives through the
|
|
4
|
+
* harness's `session/jobs` push mirror (`jobsBySession` in the sessions
|
|
5
|
+
* list feed). The routes:
|
|
6
|
+
*
|
|
7
|
+
* - 'jobs.output' — REPLAYS the output the MODEL has read so far for one
|
|
8
|
+
* job. The source is the owner session's own event log: `tool/call` rows
|
|
9
|
+
* of `job_output` name the job via `arguments.job_id`, and the paired
|
|
10
|
+
* `tool/result` rows carry the finalized content the model received.
|
|
11
|
+
* Because the session store's in-memory log can lag the live append feed
|
|
12
|
+
* after a host restart (the store session stays frozen at its
|
|
13
|
+
* rehydration boundary), the plugin ALSO mirrors job_output events from
|
|
14
|
+
* the live `session/event` feed and merges both sources (deduped by seq).
|
|
15
|
+
* This touches NO DSH source: the model's `job_output` cursor is never
|
|
16
|
+
* consumed, and the pane stays empty until the agent reads the job.
|
|
17
|
+
* - 'jobs.kill' — the registry's stock `kill` (a pristine DSH API),
|
|
18
|
+
* fenced by the owning session via the live agent caller. Absent registry
|
|
19
|
+
* → 503, mirroring the settings routes' optional-service downgrade.
|
|
20
|
+
*/
|
|
21
|
+
import type { Context } from './context-types.ts';
|
|
22
|
+
/** The two background-job routes of the sidebar API. */
|
|
23
|
+
export interface SidebarJobsRoutes {
|
|
24
|
+
/** The output the model has read so far for one job (event replay, capped). */
|
|
25
|
+
output(payload: unknown): {
|
|
26
|
+
text: string;
|
|
27
|
+
truncated: boolean;
|
|
28
|
+
read: boolean;
|
|
29
|
+
};
|
|
30
|
+
/** Request cancellation of one job (live jobs flip to stopping). */
|
|
31
|
+
kill(payload: unknown): {
|
|
32
|
+
ok: true;
|
|
33
|
+
outcome: 'requested' | 'already-finished';
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Build the jobs routes bound to the plugin context. `output` merges the
|
|
38
|
+
* owner session's own event log with the live job_output mirror; `kill`
|
|
39
|
+
* reads the jobs/agents services lazily and degrades to a 503 when the
|
|
40
|
+
* deployment lacks the registry.
|
|
41
|
+
* @param ctx - host plugin context.
|
|
42
|
+
* @param outputLimit - response cap for one output replay in bytes; longer
|
|
43
|
+
* texts are sliced and flagged `truncated` (mirrors the fs.read cap).
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildJobsApi(ctx: Context, outputLimit: number): SidebarJobsRoutes;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared "Side card" preference vocabulary (types + constants), consumed by
|
|
3
|
+
* BOTH halves: the host registers the schemastery schema over these values
|
|
4
|
+
* (config.ts) and the client reads/writes them through the settings RPC
|
|
5
|
+
* (client/prefs.ts, client/SideCardSection.tsx). Kept free of schemastery so
|
|
6
|
+
* the browser bundle never pulls the schema runtime in.
|
|
7
|
+
*/
|
|
8
|
+
/** The user-settings namespace holding the side card preferences. */
|
|
9
|
+
export declare const SIDEBAR_PREFS_NS = "dsh-better-sidebar";
|
|
10
|
+
/** User-facing side card preferences (new-conversation defaults). */
|
|
11
|
+
export interface SidebarPrefs {
|
|
12
|
+
/** Whether a brand-new conversation opens the side card by default. */
|
|
13
|
+
openByDefault: boolean;
|
|
14
|
+
/** Default panel width as a percent of the window width (20–60). */
|
|
15
|
+
defaultWidthPercent: number;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the sidebar auto-activates (opens the panel) and expands the
|
|
18
|
+
* Subagent page when the current conversation spawns a new subagent.
|
|
19
|
+
*/
|
|
20
|
+
autoOpenSubagent: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the sidebar auto-activates (opens the panel) and expands the
|
|
23
|
+
* Jobs page when a NEW background job appears for the current
|
|
24
|
+
* conversation (any new job id, not just the first one).
|
|
25
|
+
*/
|
|
26
|
+
autoOpenJobs: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Whether the model-facing agent terminal tools (terminal_create / list /
|
|
29
|
+
* send / read / wait_for / resize / signal / close) are injected into the
|
|
30
|
+
* model's toolset. Off by default: the feature stays dormant until the
|
|
31
|
+
* user explicitly enables it in the side card settings.
|
|
32
|
+
*/
|
|
33
|
+
agentTerminalTools: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Whether expanding the bottom panel for the FIRST time in a session tries
|
|
36
|
+
* to open a fresh terminal tab there (the terminal quota/type still gates
|
|
37
|
+
* the attempt). On by default; the switch lives under the terminal tab's
|
|
38
|
+
* row in the Side card settings.
|
|
39
|
+
*/
|
|
40
|
+
bottomPanelAutoTerminal: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Whether chat-side file opens (tool-row path links, the produced-files
|
|
43
|
+
* row, prose file mentions — every path that funnels through the client
|
|
44
|
+
* runtime's `ctx.workspaces.openPath`) open in the sidebar editor instead
|
|
45
|
+
* of the Host OS's default application. On by default; the editor tab's
|
|
46
|
+
* own enable switch gates it too (both must be on for the takeover).
|
|
47
|
+
*/
|
|
48
|
+
interceptOpenPath: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Whether the HTML previewer drops its sandboxed iframe. Sandbox ON (the
|
|
51
|
+
* default) renders previewed HTML in an opaque-origin iframe that cannot
|
|
52
|
+
* touch the GUI; turning it OFF runs the previewed page with the GUI's
|
|
53
|
+
* own origin — full read/write access to session files and internal
|
|
54
|
+
* APIs. Only for trusted local content; the setting copy warns.
|
|
55
|
+
*/
|
|
56
|
+
htmlViewerNoSandbox: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether a newly opened HTML preview starts UNSANDBOXED (the per-surface
|
|
59
|
+
* temporary unlock pre-applied). Off by default: previews open sandboxed
|
|
60
|
+
* and the status row offers the one-tap unlock; when on, previews open
|
|
61
|
+
* in the red unsandboxed state and the status row offers a one-tap
|
|
62
|
+
* restore for the current file.
|
|
63
|
+
*/
|
|
64
|
+
htmlViewerDefaultUnsafe: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Whether the browser tab drops its sandboxed iframe. Sandbox ON (the
|
|
67
|
+
* default) keeps browsed sites in an opaque origin with no GUI access;
|
|
68
|
+
* turning it OFF runs any visited site with the GUI's own origin — it
|
|
69
|
+
* can read session data and act as the logged-in GUI. Only for trusted
|
|
70
|
+
* sites; the setting copy warns.
|
|
71
|
+
*/
|
|
72
|
+
browserNoSandbox: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Whether clicking an http(s) EXTERNAL link in the GUI (chat messages,
|
|
75
|
+
* tool rows, prose mentions) opens the sidebar browser instead of a new
|
|
76
|
+
* browser tab. On by default; gated on the browser tab's own enable
|
|
77
|
+
* switch (both must be on for the takeover). Ctrl/Cmd+click always
|
|
78
|
+
* bypasses the takeover.
|
|
79
|
+
*/
|
|
80
|
+
browserInterceptLinks: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Per-tab enable switches, keyed by tab descriptor id (`'explorer'`,
|
|
83
|
+
* `'my-plugin:db'`). An ABSENT key means enabled — only an explicit
|
|
84
|
+
* `false` disables a tab type (hidden from the + menu, `openTab` refuses,
|
|
85
|
+
* and derived flows like subagent auto-open / agent-terminal tabs stop).
|
|
86
|
+
* Already-open tabs of a disabled type keep rendering (closing one
|
|
87
|
+
* prevents reopening), matching the "existing conversations keep their
|
|
88
|
+
* own layouts" rule.
|
|
89
|
+
*/
|
|
90
|
+
tabsEnabled: Record<string, boolean>;
|
|
91
|
+
/**
|
|
92
|
+
* Per-viewer enable switches, keyed by file viewer descriptor id
|
|
93
|
+
* (`'image'`, `'my-plugin:csv'`). An ABSENT key means enabled; a disabled
|
|
94
|
+
* viewer is skipped by `matchFileViewer` so files fall through to the
|
|
95
|
+
* next matching viewer (or the download button when none match).
|
|
96
|
+
*/
|
|
97
|
+
viewersEnabled: Record<string, boolean>;
|
|
98
|
+
}
|
|
99
|
+
/** Range contract of {@link SidebarPrefs.defaultWidthPercent}. */
|
|
100
|
+
export declare const WIDTH_PERCENT_MIN = 20;
|
|
101
|
+
export declare const WIDTH_PERCENT_MAX = 60;
|
|
102
|
+
export declare const WIDTH_PERCENT_DEFAULT = 30;
|
|
103
|
+
/** Fallback prefs used whenever the settings document is unreachable or malformed. */
|
|
104
|
+
export declare const SIDEBAR_PREFS_DEFAULTS: SidebarPrefs;
|
|
105
|
+
/** Clamp one width percent into the contract range (shared by schema and client reads). */
|
|
106
|
+
export declare function clampWidthPercent(value: number): number;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as nodePty from 'node-pty';
|
|
2
|
+
/**
|
|
3
|
+
* Restore the executable bit pnpm strips from node-pty's prebuilt
|
|
4
|
+
* spawn-helper (the macOS helper that forks and sets up the pty). Without it
|
|
5
|
+
* every spawn fails with `posix_spawnp failed`. Idempotent; mirrors
|
|
6
|
+
* @deepseek-ai/dsh-terminal-bash's ensure-spawn-helper postinstall, run at
|
|
7
|
+
* plugin activation so link-installed deployments get the fix too.
|
|
8
|
+
*/
|
|
9
|
+
export declare function ensureSpawnHelper(): void;
|
|
10
|
+
/** One live terminal. */
|
|
11
|
+
export interface SidebarPty {
|
|
12
|
+
/** `${sessionId}:${tabId}` registry key. */
|
|
13
|
+
key: string;
|
|
14
|
+
sessionId: string;
|
|
15
|
+
tabId: string;
|
|
16
|
+
/** The working directory the process was SPAWNED with (a reconnect that
|
|
17
|
+
* resolves a different authoritative cwd respawns instead of reusing —
|
|
18
|
+
* the page-load hydrate race can attach the real cwd after the first
|
|
19
|
+
* connect, and a shell in the wrong directory must not linger). */
|
|
20
|
+
cwd: string;
|
|
21
|
+
pty: nodePty.IPty;
|
|
22
|
+
/** Output accumulated since spawn (bounded; head dropped when over the limit). */
|
|
23
|
+
transcript: string;
|
|
24
|
+
/** Whether the top-level process exited (transcript stays replayable). */
|
|
25
|
+
exited: boolean;
|
|
26
|
+
exitCode?: number | null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The terminal registry. `maxPerSession` bounds concurrent processes per
|
|
30
|
+
* conversation (the client caps tabs at the same number).
|
|
31
|
+
*/
|
|
32
|
+
export declare class PtyManager {
|
|
33
|
+
private readonly shell;
|
|
34
|
+
private readonly maxPerSession;
|
|
35
|
+
private readonly sessions;
|
|
36
|
+
private readonly pendingCloses;
|
|
37
|
+
constructor(shell: string, maxPerSession: number);
|
|
38
|
+
/** All live terminal keys of one session. */
|
|
39
|
+
keysOf(sessionId: string): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Open (or reuse) the terminal for a session/tab key. A handle whose
|
|
42
|
+
* process already exited is replaced with a fresh spawn (reconnecting a
|
|
43
|
+
* dead terminal must yield a live shell, not an input sink), and so is a
|
|
44
|
+
* live handle whose spawn cwd differs from the now-authoritative one (the
|
|
45
|
+
* first connect of a page load can arrive before the session hydrates, so
|
|
46
|
+
* it fell back to the process cwd — reconnecting with the real cwd must
|
|
47
|
+
* restart the shell in the right directory). Reopening also cancels any
|
|
48
|
+
* pending scheduled close (a reconnect within the grace window keeps the
|
|
49
|
+
* process alive).
|
|
50
|
+
* @param sessionId - conversation id.
|
|
51
|
+
* @param tabId - client tab id.
|
|
52
|
+
* @param cwd - initial working directory (the session's cwd).
|
|
53
|
+
* @param cols - initial terminal width.
|
|
54
|
+
* @param rows - initial terminal height.
|
|
55
|
+
* @returns the live handle.
|
|
56
|
+
* @throws {SidebarError} pty-error when the per-session cap is reached.
|
|
57
|
+
*/
|
|
58
|
+
open(sessionId: string, tabId: string, cwd: string, cols: number, rows: number): SidebarPty;
|
|
59
|
+
/**
|
|
60
|
+
* Schedule the terminal's destruction after `delayMs`. A tab close sends
|
|
61
|
+
* delay 0 (release the quota immediately); a bare socket drop (refresh,
|
|
62
|
+
* crash) uses the grace period so a quick reconnect keeps the process.
|
|
63
|
+
* `open()` cancels any pending close.
|
|
64
|
+
*/
|
|
65
|
+
scheduleClose(key: string, delayMs: number): void;
|
|
66
|
+
/** Cancel a pending scheduled close (the terminal is being reopened). */
|
|
67
|
+
cancelClose(key: string): void;
|
|
68
|
+
/** Resolve a live handle by key, or undefined. */
|
|
69
|
+
get(key: string): SidebarPty | undefined;
|
|
70
|
+
/** Close a terminal and drop its state (the owning tab was closed). */
|
|
71
|
+
close(key: string): void;
|
|
72
|
+
/** Close every terminal (plugin teardown). */
|
|
73
|
+
disposeAll(): void;
|
|
74
|
+
}
|
|
75
|
+
/** The interactive shell for this platform (empty SHELL falls back). */
|
|
76
|
+
export declare function defaultShell(): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Context } from './context-types.ts';
|
|
2
|
+
import { AgentPtyRegistry } from './agent-pty.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Bound a string to a byte limit, marking truncation. Truncation never
|
|
5
|
+
* splits a multi-byte UTF-8 sequence: when the byte cap lands inside one,
|
|
6
|
+
* the walk-back retreats to the sequence's leading byte so the retained
|
|
7
|
+
* prefix decodes cleanly (a split would decode to U+FFFD).
|
|
8
|
+
* @internal exported for the unit tests, like {@link snapshotOf}.
|
|
9
|
+
*/
|
|
10
|
+
export declare function boundBytes(text: string, maxBytes: number): {
|
|
11
|
+
text: string;
|
|
12
|
+
truncated: boolean;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Register the eight terminal tools against the host tool registry. The
|
|
16
|
+
* `resolveCwd` callback threads the live session cwd (authoritative from the
|
|
17
|
+
* session store, falling back to the process cwd) so a freshly-created
|
|
18
|
+
* terminal lands in the right directory without the model passing it.
|
|
19
|
+
* Every uuid-keyed tool first asserts the terminal belongs to the calling
|
|
20
|
+
* session (`registry.assertOwned`), so one agent can never reach another
|
|
21
|
+
* session's terminals.
|
|
22
|
+
* @param ctx - host plugin context (carries the tools service).
|
|
23
|
+
* @param registry - the agent-owned terminal registry.
|
|
24
|
+
* @param resolveCwd - live cwd resolver for one session id.
|
|
25
|
+
* @returns a disposer that unregisters all eight tools (the caller gates
|
|
26
|
+
* registration on the side-card setting and calls this to turn them off).
|
|
27
|
+
*/
|
|
28
|
+
export declare function registerTools(ctx: Context, registry: AgentPtyRegistry, resolveCwd: (sessionId: string) => string): () => void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-trust fence for the sidebar routes, behaviorally identical to the
|
|
3
|
+
* /api gateway's fence in @deepseek-ai/dsh-client-connection
|
|
4
|
+
* (src/api-request-trust.ts + src/loopback-hostname.ts, BSD-3-Clause,
|
|
5
|
+
* copied here because the package does not export these helpers and the
|
|
6
|
+
* plugin must not depend on its internals). Host-header loopback or a
|
|
7
|
+
* configured trusted authority passes; cross-site browser markers refuse.
|
|
8
|
+
* This is a DNS-rebinding / cross-site defense, not authentication.
|
|
9
|
+
*/
|
|
10
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
11
|
+
/** The request facts the fence reads (structural subset of IncomingMessage). */
|
|
12
|
+
interface ApiTrustRequest {
|
|
13
|
+
headers: IncomingHttpHeaders;
|
|
14
|
+
}
|
|
15
|
+
/** Whether a normalized URL hostname names the local loopback authority. */
|
|
16
|
+
export declare function isLoopbackHostname(hostname: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Decide whether one sidebar request may reach the plugin routes.
|
|
19
|
+
* @param request - node HTTP request facts (headers).
|
|
20
|
+
* @param trustedHosts - non-loopback authorities this deployment serves.
|
|
21
|
+
* @returns true when the Host is ours (loopback or trusted) and browser markers are same-origin.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isTrustedApiRequest(request: ApiTrustRequest, trustedHosts: readonly string[]): boolean;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire helpers for the /sidebar JSON API: bounded body reading, response
|
|
3
|
+
* writing, and the shared error envelope. Every API method returns
|
|
4
|
+
* `{ok: true, value}` on success and `{ok: false, error: {code, message}}`
|
|
5
|
+
* (HTTP 4xx/5xx matching the code) on failure.
|
|
6
|
+
*/
|
|
7
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
8
|
+
/** Machine-readable error codes of the sidebar API. */
|
|
9
|
+
export type SidebarErrorCode = 'bad-request' | 'not-found' | 'forbidden' | 'method-error' | 'fs-error' | 'git-error' | 'pty-error' | 'job-error' | 'settings-rejected' | 'settings-conflict' | 'internal';
|
|
10
|
+
/** One API failure with its wire code and HTTP status. */
|
|
11
|
+
export declare class SidebarError extends Error {
|
|
12
|
+
readonly code: SidebarErrorCode;
|
|
13
|
+
readonly status: number;
|
|
14
|
+
constructor(code: SidebarErrorCode, message: string, status?: number);
|
|
15
|
+
}
|
|
16
|
+
/** Success envelope of one API method. */
|
|
17
|
+
export interface SidebarOk<T> {
|
|
18
|
+
ok: true;
|
|
19
|
+
value: T;
|
|
20
|
+
}
|
|
21
|
+
/** Failure envelope of one API method. */
|
|
22
|
+
export interface SidebarErr {
|
|
23
|
+
ok: false;
|
|
24
|
+
error: {
|
|
25
|
+
code: SidebarErrorCode;
|
|
26
|
+
message: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Read and parse the JSON request body (bounded; malformed → bad-request). */
|
|
30
|
+
export declare function readJsonBody(req: IncomingMessage): Promise<unknown>;
|
|
31
|
+
/** Write a JSON response with the given status. */
|
|
32
|
+
export declare function writeJson(res: ServerResponse, status: number, body: unknown): void;
|
|
33
|
+
/** Write the success envelope. */
|
|
34
|
+
export declare function writeOk(res: ServerResponse, value: unknown): void;
|
|
35
|
+
/** Write the failure envelope for any thrown value (unknown → internal 500). */
|
|
36
|
+
export declare function writeError(res: ServerResponse, error: unknown): void;
|
|
37
|
+
/** Narrow an unknown payload value to a string, else throw bad-request. */
|
|
38
|
+
export declare function requireString(payload: unknown, key: string): string;
|