@dsh-xhl/dsh-file-explorer 0.1.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.
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Structural types for the cordis services this plugin consumes, plus the
3
+ * Context augmentation. Mirrors the actual runtime shapes; drift is contained
4
+ * to this file. Client-reachable declaration graph stays free of Node types.
5
+ */
6
+ import type { Context } from 'cordis';
7
+ /** The request face route handlers see (structural subset of node's IncomingMessage). */
8
+ export interface FilexHttpRequest {
9
+ url?: string;
10
+ method?: string;
11
+ headers: Record<string, string | string[] | undefined>;
12
+ [Symbol.asyncIterator](): AsyncIterator<string | Uint8Array>;
13
+ }
14
+ /** The response face route handlers write to (structural subset of ServerResponse). */
15
+ export interface FilexHttpResponse {
16
+ statusCode: number;
17
+ writeHead(status: number, headers?: Record<string, string>): void;
18
+ end(body?: string | Uint8Array): void;
19
+ }
20
+ /** One named webserver route (mirror of the host-webserver WebRoute). */
21
+ export interface FilexWebRoute {
22
+ kind: 'exact' | 'prefix';
23
+ path: string;
24
+ handler: (req: FilexHttpRequest, res: FilexHttpResponse) => void | Promise<void>;
25
+ }
26
+ /** The webServer service face this plugin uses. */
27
+ export interface FilexWebServer {
28
+ register(route: FilexWebRoute): () => void;
29
+ }
30
+ /** A published session's header slice (authoritative cwd). */
31
+ export interface FilexSessionHeader {
32
+ cwd?: string;
33
+ }
34
+ /** The host session store face (`ctx.sessions.get(id)` returns the live session). */
35
+ export interface FilexSessionStore {
36
+ get(id: string): {
37
+ header: FilexSessionHeader;
38
+ } | undefined;
39
+ /** Resolve an Agent-scoped context view for one session (client runtime). */
40
+ scope(id: string): unknown | undefined;
41
+ }
42
+ /** Structural face of the framework session-list snapshot (`useSessions` selector input). */
43
+ export interface FilexSessionListState {
44
+ /** The currently selected session id; undefined while none is selected / still loading. */
45
+ current?: string;
46
+ phase?: string;
47
+ byId?: Record<string, {
48
+ cwd?: string;
49
+ blank?: boolean;
50
+ }>;
51
+ }
52
+ /**
53
+ * The framework-injected `useSessions` selector hook — a standard prop of
54
+ * root-scoped slots (shell.overlay, sidebar, …). Subscribes to the session
55
+ * list store and re-renders on change.
56
+ */
57
+ export type FilexUseSessions = (selector: (state: FilexSessionListState) => unknown) => unknown;
58
+ /**
59
+ * The client workspaces service face (the chat's file-open funnel). Every
60
+ * chat-side path open — tool-row links, produced files, prose mentions —
61
+ * funnels through `openPath`, which the explorer intercepts.
62
+ */
63
+ export interface FilexWorkspacesService {
64
+ /** Open a filesystem path through the Host (OS default application). */
65
+ openPath(path: string): Promise<void>;
66
+ }
67
+ /** The composer input face (client conversation service). */
68
+ export interface FilexConversationInput {
69
+ state: {
70
+ getSnapshot(): {
71
+ draft: string;
72
+ };
73
+ };
74
+ setDraft(text: string): void;
75
+ }
76
+ /** The conversation service face (client, lazy via ctx.get('conversation')). */
77
+ export interface FilexConversation {
78
+ input: {
79
+ for(actx: unknown): FilexConversationInput;
80
+ };
81
+ }
82
+ /** The client slots service face (register returns the disposer). */
83
+ export interface FilexSlotsService {
84
+ register(options: {
85
+ name: string;
86
+ id?: string;
87
+ key?: string;
88
+ order?: number;
89
+ label?: string | (() => string);
90
+ inject?: (...args: unknown[]) => Record<string, unknown>;
91
+ }, component: unknown): () => void;
92
+ inject(key: string, callback: () => () => void): () => void;
93
+ }
94
+ declare module 'cordis' {
95
+ interface Context {
96
+ webServer: FilexWebServer;
97
+ sessions: FilexSessionStore;
98
+ slots: FilexSlotsService;
99
+ /** Client-only: the chat's file-open funnel the explorer intercepts. */
100
+ workspaces: FilexWorkspacesService;
101
+ /** Subscribe to session events (cordis event API). */
102
+ on(event: string, listener: (session: unknown, event: unknown) => void): () => void;
103
+ /** DSH-vendored cordis lifecycle helper. */
104
+ effect(fn: () => void | (() => void), label?: string): void;
105
+ }
106
+ }
107
+ export type { Context };
@@ -0,0 +1,8 @@
1
+ import type { Context } from './context-types.ts';
2
+ /** Plugin identity for cordis.yml rows. */
3
+ export declare const name = "dsh-file-explorer";
4
+ /** Services required before mounting: the webserver routes and the session store. */
5
+ export declare const inject: string[];
6
+ /** Canonical containment check (case-aware on Windows, separator-normalized). */
7
+ export declare function isWithin(parent: string, child: string): boolean;
8
+ export declare function apply(ctx: Context): void;
@@ -0,0 +1,5 @@
1
+ /** Assorted runtime invariants and assertions. */
2
+ export declare function assert(value: unknown, message?: string): asserts value;
3
+ export declare function assertNonNullable<T>(value: T | null | undefined, message?: string): T;
4
+ /** Assert a plain record payload (JSON body guards). */
5
+ export declare function isPlainRecord(value: unknown): value is Record<string, unknown>;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Browser-trust fence for the /filex routes: Host-header loopback passes,
3
+ * cross-site browser markers refuse. DNS-rebinding defense, not auth.
4
+ */
5
+ import type { IncomingHttpHeaders } from 'node:http';
6
+ interface ApiTrustRequest {
7
+ headers: IncomingHttpHeaders;
8
+ }
9
+ /** Whether a normalized URL hostname names the local loopback authority. */
10
+ export declare function isLoopbackHostname(hostname: string): boolean;
11
+ /**
12
+ * Decide whether one request may reach the plugin routes. Only loopback
13
+ * Host authorities pass (the DSH web GUI is served from 127.0.0.1/localhost);
14
+ * cross-site browser requests are refused.
15
+ */
16
+ export declare function isTrustedApiRequest(request: ApiTrustRequest): boolean;
17
+ export {};
package/package.json ADDED
@@ -0,0 +1,113 @@
1
+ {
2
+ "name": "@dsh-xhl/dsh-file-explorer",
3
+ "version": "0.1.0",
4
+ "description": "DSH 外部编译型插件:工作区文件预览 / 编辑(CodeMirror 6 编辑器 + 文件树 + 全文搜索 + Markdown/HTML/图片/PDF 预览)。移植自同目录下的动态 Cordis 插件版本。",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/types/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/types/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./client": "./lib/client.js",
14
+ "./package.json": "./package.json"
15
+ },
16
+ "engines": {
17
+ "node": ">=20"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public",
21
+ "registry": "https://registry.npmjs.org"
22
+ },
23
+ "dsh": {
24
+ "bundle": {
25
+ "patch": "./cordis.patch.yml"
26
+ },
27
+ "client": {
28
+ "inject": [
29
+ "@deepseek-ai/dsh-client-runtime",
30
+ "@deepseek-ai/dsh-client-ui-slots",
31
+ "@deepseek-ai/dsh-client-web-react"
32
+ ],
33
+ "platform": "web"
34
+ }
35
+ },
36
+ "files": [
37
+ "lib/index.js",
38
+ "lib/client.js",
39
+ "lib/types/**/*.d.ts",
40
+ "cordis.patch.yml",
41
+ "README.md"
42
+ ],
43
+ "scripts": {
44
+ "build": "node -e \"fs.rmSync('lib',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && tsdown",
45
+ "typecheck": "tsc --noEmit",
46
+ "bundle": "tsdown",
47
+ "prepare": "tsdown"
48
+ },
49
+ "license": "MIT",
50
+ "peerDependencies": {
51
+ "@deepseek-ai/cordis": "^4.0.1",
52
+ "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
53
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6",
54
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
55
+ "@deepseek-ai/dsh-client-web-react": "^0.1.0-rc.6",
56
+ "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6",
57
+ "@deepseek-ai/dsh-session": "^0.1.0-rc.6",
58
+ "cordis": "^4.0.0-rc.7",
59
+ "react": "^18.2.0",
60
+ "react-dom": "^18.2.0"
61
+ },
62
+ "peerDependenciesMeta": {
63
+ "cordis": {
64
+ "optional": true
65
+ }
66
+ },
67
+ "dependencies": {
68
+ "@codemirror/commands": "^6.10.4",
69
+ "@codemirror/lang-cpp": "^6.0.3",
70
+ "@codemirror/lang-css": "^6.3.1",
71
+ "@codemirror/lang-go": "^6.0.1",
72
+ "@codemirror/lang-html": "^6.4.12",
73
+ "@codemirror/lang-java": "^6.0.2",
74
+ "@codemirror/lang-javascript": "^6.2.5",
75
+ "@codemirror/lang-json": "^6.0.2",
76
+ "@codemirror/lang-markdown": "^6.5.2",
77
+ "@codemirror/lang-php": "^6.0.2",
78
+ "@codemirror/lang-python": "^6.2.1",
79
+ "@codemirror/lang-rust": "^6.0.2",
80
+ "@codemirror/lang-sql": "^6.10.0",
81
+ "@codemirror/lang-vue": "^0.1.3",
82
+ "@codemirror/lang-xml": "^6.1.0",
83
+ "@codemirror/lang-yaml": "^6.1.3",
84
+ "@codemirror/language": "^6.12.4",
85
+ "@codemirror/legacy-modes": "^6.5.3",
86
+ "@codemirror/search": "6.7.1",
87
+ "@codemirror/state": "^6.7.1",
88
+ "@codemirror/view": "^6.43.8",
89
+ "@lezer/highlight": "^1.2.3",
90
+ "vscode-icons-js": "^11.6.1"
91
+ },
92
+ "devDependencies": {
93
+ "@deepseek-ai/cordis": "^4.0.1",
94
+ "@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
95
+ "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6",
96
+ "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
97
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
98
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6",
99
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
100
+ "@deepseek-ai/dsh-client-web-react": "^0.1.0-rc.6",
101
+ "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6",
102
+ "@deepseek-ai/dsh-session": "^0.1.0-rc.6",
103
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
104
+ "@types/node": "^22.0.0",
105
+ "@types/react": "~18.3.1",
106
+ "@types/react-dom": "~18.3.1",
107
+ "cordis": "^4.0.0-rc.7",
108
+ "react": "^18.2.0",
109
+ "react-dom": "^18.2.0",
110
+ "tsdown": "^0.22.2",
111
+ "typescript": "^5.6.0"
112
+ }
113
+ }