dsh-webdav-sync 0.0.0-stage → 1.0.1

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/lib/wire.d.ts ADDED
@@ -0,0 +1,346 @@
1
+ /**
2
+ * dsh-webdav-sync wire contract: the zero-runtime-dependency module both the
3
+ * Host half and the browser half import. Nothing here touches node:fs, node:crypto
4
+ * or React, so the browser bundle can pull it in without widening its surface.
5
+ *
6
+ * The shapes below are the plugin's own protocol: the sealed backup that travels
7
+ * to the WebDAV server, the local sync state that never leaves this computer, and
8
+ * the HTTP envelopes the settings card talks to.
9
+ */
10
+ /** Every route this plugin owns lives under this prefix. */
11
+ export declare const ROUTE_PREFIX = "/plugin/webdav-sync";
12
+ /** GET: configuration (without secrets), state, notice, and what installs pend. */
13
+ export declare const ROUTE_STATUS = "/plugin/webdav-sync/status";
14
+ /** POST: save the sync configuration. */
15
+ export declare const ROUTE_CONFIG = "/plugin/webdav-sync/config";
16
+ /** POST: reach the server with the stored configuration and report what happened. */
17
+ export declare const ROUTE_CHECK = "/plugin/webdav-sync/check";
18
+ /** POST: run one merge. `{ force }` runs it even when nothing looks changed. */
19
+ export declare const ROUTE_SYNC = "/plugin/webdav-sync/sync";
20
+ /** POST: make this computer's setup the server's. */
21
+ export declare const ROUTE_RESTORE = "/plugin/webdav-sync/restore";
22
+ /** POST: write over a file on the server that is not a backup of ours. */
23
+ export declare const ROUTE_UPLOAD = "/plugin/webdav-sync/upload";
24
+ /** POST: clear the sync notice. */
25
+ export declare const ROUTE_DISMISS = "/plugin/webdav-sync/dismiss";
26
+ /** POST: forget the configuration and stop syncing. */
27
+ export declare const ROUTE_OFF = "/plugin/webdav-sync/off";
28
+ /** POST: install the profile's declared plugins with the bundled pnpm. */
29
+ export declare const ROUTE_INSTALL = "/plugin/webdav-sync/install";
30
+ /** GET: what one sync would do, without writing anything anywhere. */
31
+ export declare const ROUTE_PREVIEW = "/plugin/webdav-sync/preview";
32
+ /** POST: put back the copies a restore replaced. */
33
+ export declare const ROUTE_UNDO = "/plugin/webdav-sync/undo";
34
+ /** Error envelope shared by every route. */
35
+ export interface RouteError {
36
+ error: string;
37
+ }
38
+ /**
39
+ * Folder made inside the WebDAV address, and the one file kept in it. One sealed
40
+ * file rather than a mirror of the profile: the merge is three-way per part, so
41
+ * the server only ever holds whole versions of the whole setup.
42
+ */
43
+ export declare const REMOTE_FOLDER = "dsh-webdav";
44
+ /** The single sealed file this plugin owns on the server. */
45
+ export declare const REMOTE_FILE = "dsh-profile.dshsync";
46
+ /** Extension the sealed envelope is written with. */
47
+ export declare const BACKUP_EXT = ".dshsync";
48
+ /**
49
+ * The parts of a DSH setup that travel, in merge order. Each is hashed on its
50
+ * own, so a change to one never drags the others along behind it.
51
+ */
52
+ export declare const PARTS: readonly ["models", "defaults", "plugins", "patch"];
53
+ export type Part = (typeof PARTS)[number];
54
+ /** Human copy for each part, used by the card and by notices. */
55
+ export declare const PART_LABELS: Record<Part, {
56
+ zh: string;
57
+ en: string;
58
+ }>;
59
+ /** One model as the provider catalog declares it. Fields are open: the row is carried verbatim. */
60
+ export interface ModelSpec {
61
+ id: string;
62
+ name?: string;
63
+ contextWindow?: number;
64
+ maxTokens?: number;
65
+ input?: string[];
66
+ reasoning?: string;
67
+ reasoningEfforts?: Record<string, string | null>;
68
+ defaultEffort?: string | null;
69
+ compat?: Record<string, unknown>;
70
+ [key: string]: unknown;
71
+ }
72
+ /** One provider entry under `llm-pi-ai.config.providers`. */
73
+ export interface ProviderSpec {
74
+ displayName?: string;
75
+ apiKeyEnv?: string;
76
+ api?: string;
77
+ baseURL?: string;
78
+ models?: ModelSpec[];
79
+ [key: string]: unknown;
80
+ }
81
+ /** The `models` part: the model catalog exactly as the profile patch holds it. */
82
+ export interface ModelsPart {
83
+ /** The whole `llm-pi-ai` entry config, so fields beyond `providers` travel too. */
84
+ config: Record<string, unknown>;
85
+ }
86
+ /** The `defaults` part: which provider/model new sessions start on. */
87
+ export interface DefaultsPart {
88
+ /** The whole `agent-default-model` entry config. */
89
+ config: Record<string, unknown>;
90
+ }
91
+ /** One other top-level row of the profile patch, carried by the `patch` part. */
92
+ export interface PatchEntry {
93
+ /** `id` of an id-targeted row, or `insert` for an insert block. */
94
+ id: string;
95
+ /** For an insert block: the rows it inserts, verbatim. */
96
+ insert?: unknown[];
97
+ /** For an id-targeted row: its `name`. */
98
+ name?: string;
99
+ /** For an id-targeted row: its `config`. */
100
+ config?: unknown;
101
+ /** Any other keys of the row, carried verbatim. */
102
+ [key: string]: unknown;
103
+ }
104
+ /** The `patch` part: every profile-patch row that is not a model row. */
105
+ export interface PatchPart {
106
+ entries: PatchEntry[];
107
+ }
108
+ /** The `plugins` part: what the profile's package.json declares as installed. */
109
+ export interface PluginsPart {
110
+ /** `dependencies` of the profile package.json, verbatim. */
111
+ dependencies: Record<string, string>;
112
+ /** `dsh.profile.bundles`, verbatim and in order. */
113
+ bundles: string[];
114
+ }
115
+ /** Everything one sealed backup carries. Field names are the wire format. */
116
+ export interface Bundle {
117
+ format: 'dsh-webdav-sync-backup';
118
+ version: 1;
119
+ /** When the writing computer sealed it, ISO-8601 UTC. The merge's tie-breaker. */
120
+ created: string;
121
+ /** Which app sealed it, for a file several tools might share a folder with. */
122
+ app: string;
123
+ models?: ModelsPart;
124
+ defaults?: DefaultsPart;
125
+ plugins?: PluginsPart;
126
+ patch?: PatchPart;
127
+ }
128
+ /** The format word in the envelope; anything else on the server is not ours. */
129
+ export declare const BUNDLE_FORMAT = "dsh-webdav-sync-backup";
130
+ /** The bundle structure version this build writes and reads. */
131
+ export declare const BUNDLE_VERSION = 1;
132
+ /** Value written to `Bundle.app`. */
133
+ export declare const BUNDLE_APP = "dsh-webdav-sync";
134
+ /** The server's own name for one version of the file. */
135
+ export interface ServerVersion {
136
+ /** ETag, when the server gives one. */
137
+ etag?: string;
138
+ /** Last-Modified, used only when there is no ETag that can be matched again. */
139
+ modified?: string;
140
+ }
141
+ /** Which parts the last sync moved, and where the replaced copies were kept. */
142
+ export interface Notice {
143
+ at: string;
144
+ /** Parts this computer's copy replaced with the server's. */
145
+ here: Part[];
146
+ /** Parts the server's copy replaced with this computer's. */
147
+ there: Part[];
148
+ /** Folder holding the copies that were replaced. */
149
+ saved?: string;
150
+ /** True when a Restore, not a sync, produced this notice. */
151
+ restored?: boolean;
152
+ }
153
+ /** One side of the last completed sync, per part. */
154
+ export interface PartHashes {
155
+ models?: string;
156
+ defaults?: string;
157
+ plugins?: string;
158
+ patch?: string;
159
+ }
160
+ /**
161
+ * The variable half of the state: everything a completed sync records.
162
+ *
163
+ * Each optional field also admits an explicit `undefined`, because a sync clears
164
+ * them by assignment — and with `exactOptionalPropertyTypes` a field that may be
165
+ * absent is not the same as one that may be set to nothing.
166
+ */
167
+ export interface SyncRecord {
168
+ /** Server version read at the last successful sync. */
169
+ server?: ServerVersion | undefined;
170
+ /** SHA-256 of the sealed bytes last seen on the server. */
171
+ sum?: string | undefined;
172
+ /** Part hashes as this computer left them after the last sync. */
173
+ local?: PartHashes | undefined;
174
+ /** Part hashes as the server left them after the last sync. */
175
+ remote?: PartHashes | undefined;
176
+ /** Set when a write happened without the server checking the version. */
177
+ blind?: {
178
+ sum?: string | undefined;
179
+ local?: PartHashes | undefined;
180
+ remote?: PartHashes | undefined;
181
+ } | undefined;
182
+ /** Sealed copy kept before a Restore, so Undo can put it back. */
183
+ undo?: string | undefined;
184
+ }
185
+ /** What is on disk under the plugin's state directory. */
186
+ export interface StoredState extends SyncRecord {
187
+ /** Identifies the server+account+passphrase this state describes. */
188
+ key?: string | undefined;
189
+ /** Last sync attempt, ISO-8601. */
190
+ last?: string | undefined;
191
+ /** Last failure's message, empty when the last attempt succeeded. */
192
+ error?: string | undefined;
193
+ /**
194
+ * The configuration `error` was recorded against. A failure that changes the
195
+ * state key — a wrong passphrase is one — would otherwise leave no trace,
196
+ * because the stored key is the baseline's and can never match it.
197
+ */
198
+ errorKey?: string | undefined;
199
+ notice?: Notice | undefined;
200
+ }
201
+ /** How often the gateway syncs on its own. `-1` is "only when asked". */
202
+ export declare const AUTO_MANUAL = -1;
203
+ /** The intervals the card offers, in minutes. */
204
+ export declare const AUTO_CHOICES: readonly number[];
205
+ /** The stored sync configuration. Secrets live here and never in the profile patch. */
206
+ export interface SyncConfig {
207
+ url: string;
208
+ user: string;
209
+ password: string;
210
+ passphrase: string;
211
+ /** How often the gateway syncs by itself, in minutes, or {@link AUTO_MANUAL}. */
212
+ autoEvery: number;
213
+ /** Whether the model catalog travels. */
214
+ models: boolean;
215
+ /** Whether the default-model row travels. */
216
+ defaults: boolean;
217
+ /** Whether the installed-plugin list travels. */
218
+ plugins: boolean;
219
+ /** Whether the remaining profile-patch rows travel. */
220
+ patch: boolean;
221
+ /** Whether the plugin's own address, user and password are kept for next time. */
222
+ rememberPassword: boolean;
223
+ }
224
+ /** The configuration as the card sees it: never a secret, only whether one is set. */
225
+ export interface ConfigView {
226
+ url: string;
227
+ user: string;
228
+ passwordSet: boolean;
229
+ passphraseSet: boolean;
230
+ autoEvery: number;
231
+ models: boolean;
232
+ defaults: boolean;
233
+ plugins: boolean;
234
+ patch: boolean;
235
+ rememberPassword: boolean;
236
+ }
237
+ /** Where the plugin's files live, as the card shows them. */
238
+ export interface PathsView {
239
+ profileDir: string;
240
+ stateDir: string;
241
+ patchFile: string;
242
+ packageFile: string;
243
+ }
244
+ /** One part's standing, as the card shows it. */
245
+ export interface PartView {
246
+ part: Part;
247
+ /** What this computer holds now, `null` when the part is empty here. */
248
+ local: string | null;
249
+ /** What the server held at the last sync, `null` when it held none. */
250
+ remote: string | null;
251
+ /** Whether the part changed here since the last sync. */
252
+ localChanged: boolean;
253
+ /** Whether the part changed on the server since the last sync. */
254
+ remoteChanged: boolean;
255
+ /** The part travels with the current configuration. */
256
+ synced: boolean;
257
+ }
258
+ /** GET {@link ROUTE_STATUS}. */
259
+ export interface StatusResult {
260
+ /** False until a usable configuration is stored. */
261
+ configured: boolean;
262
+ config: ConfigView;
263
+ paths: PathsView;
264
+ /** ISO-8601 of the last attempt, absent when none was made. */
265
+ last?: string;
266
+ /** Last failure's message, empty when the last attempt succeeded. */
267
+ error: string;
268
+ notice?: Notice;
269
+ /** What changed since the last sync, per part. */
270
+ parts: PartView[];
271
+ /** The profile declares plugins the installed tree does not have yet. */
272
+ pendingInstall: boolean;
273
+ /** Which plugins those are, present exactly when {@link pendingInstall} is true. */
274
+ pendingNames?: string[];
275
+ /** Whether a Restore can be undone. */
276
+ undoable: boolean;
277
+ }
278
+ /** POST {@link ROUTE_CONFIG} body. A blank secret leaves the stored one alone. */
279
+ export interface ConfigureBody {
280
+ url?: string;
281
+ user?: string;
282
+ password?: string;
283
+ passphrase?: string;
284
+ autoEvery?: number;
285
+ models?: boolean;
286
+ defaults?: boolean;
287
+ plugins?: boolean;
288
+ patch?: boolean;
289
+ rememberPassword?: boolean;
290
+ }
291
+ /** POST {@link ROUTE_SYNC} body. */
292
+ export interface SyncBody {
293
+ /** Sync even when nothing looks changed on either side. */
294
+ force?: boolean;
295
+ }
296
+ /** What one sync, restore, upload or preview did. */
297
+ export interface SyncResult {
298
+ /** Parts this computer's copy replaced with the server's. */
299
+ here: Part[];
300
+ /** Parts the server's copy replaced with this computer's. */
301
+ there: Part[];
302
+ /** Folder the replaced copies were kept in, when any were. */
303
+ saved?: string;
304
+ /** True when this call wrote a new file to the server. */
305
+ pushed: boolean;
306
+ /** The profile now declares plugins that are not installed; run the install action. */
307
+ pendingInstall: boolean;
308
+ /** Which plugins those are, present exactly when {@link pendingInstall} is true. */
309
+ pendingNames?: string[];
310
+ /** The profile has to be reloaded for the applied configuration to take effect. */
311
+ reloadNeeded: boolean;
312
+ }
313
+ /** POST {@link ROUTE_CHECK} result. */
314
+ export interface CheckResult {
315
+ /** The server answered and the account may read and write the folder. */
316
+ ok: boolean;
317
+ /** What the server answered, for the card to show either way. */
318
+ message: string;
319
+ /** Whether a file of ours is already on the server. */
320
+ hasBackup: boolean;
321
+ /** Size of that file in bytes, when it is ours. */
322
+ size?: number;
323
+ /** When it was written, ISO-8601, when the server says. */
324
+ created?: string;
325
+ }
326
+ /** POST {@link ROUTE_INSTALL} result. */
327
+ export interface InstallResult {
328
+ ok: boolean;
329
+ /** The install command's own output tail, for the card to show. */
330
+ output: string;
331
+ }
332
+ /** Envelope for any route whose body carries only a message. */
333
+ export interface MessageResult {
334
+ message: string;
335
+ }
336
+ /** Which of a part's two sides a preview call would take. */
337
+ export interface PreviewChange {
338
+ part: Part;
339
+ /** `local` takes this computer's copy (it is newer or the server has none). */
340
+ take: 'local' | 'remote' | 'newer-local' | 'newer-remote';
341
+ }
342
+ /** GET {@link ROUTE_PREVIEW}. */
343
+ export interface PreviewResult {
344
+ /** What the merge would do, part by part. Empty when nothing changed anywhere. */
345
+ changes: PreviewChange[];
346
+ }
package/lib/wire.js ADDED
@@ -0,0 +1,71 @@
1
+ /**
2
+ * dsh-webdav-sync wire contract: the zero-runtime-dependency module both the
3
+ * Host half and the browser half import. Nothing here touches node:fs, node:crypto
4
+ * or React, so the browser bundle can pull it in without widening its surface.
5
+ *
6
+ * The shapes below are the plugin's own protocol: the sealed backup that travels
7
+ * to the WebDAV server, the local sync state that never leaves this computer, and
8
+ * the HTTP envelopes the settings card talks to.
9
+ */
10
+ /** Every route this plugin owns lives under this prefix. */
11
+ export const ROUTE_PREFIX = '/plugin/webdav-sync';
12
+ /** GET: configuration (without secrets), state, notice, and what installs pend. */
13
+ export const ROUTE_STATUS = `${ROUTE_PREFIX}/status`;
14
+ /** POST: save the sync configuration. */
15
+ export const ROUTE_CONFIG = `${ROUTE_PREFIX}/config`;
16
+ /** POST: reach the server with the stored configuration and report what happened. */
17
+ export const ROUTE_CHECK = `${ROUTE_PREFIX}/check`;
18
+ /** POST: run one merge. `{ force }` runs it even when nothing looks changed. */
19
+ export const ROUTE_SYNC = `${ROUTE_PREFIX}/sync`;
20
+ /** POST: make this computer's setup the server's. */
21
+ export const ROUTE_RESTORE = `${ROUTE_PREFIX}/restore`;
22
+ /** POST: write over a file on the server that is not a backup of ours. */
23
+ export const ROUTE_UPLOAD = `${ROUTE_PREFIX}/upload`;
24
+ /** POST: clear the sync notice. */
25
+ export const ROUTE_DISMISS = `${ROUTE_PREFIX}/dismiss`;
26
+ /** POST: forget the configuration and stop syncing. */
27
+ export const ROUTE_OFF = `${ROUTE_PREFIX}/off`;
28
+ /** POST: install the profile's declared plugins with the bundled pnpm. */
29
+ export const ROUTE_INSTALL = `${ROUTE_PREFIX}/install`;
30
+ /** GET: what one sync would do, without writing anything anywhere. */
31
+ export const ROUTE_PREVIEW = `${ROUTE_PREFIX}/preview`;
32
+ /** POST: put back the copies a restore replaced. */
33
+ export const ROUTE_UNDO = `${ROUTE_PREFIX}/undo`;
34
+ // ---------------------------------------------------------------------------
35
+ // Remote folder layout
36
+ // ---------------------------------------------------------------------------
37
+ /**
38
+ * Folder made inside the WebDAV address, and the one file kept in it. One sealed
39
+ * file rather than a mirror of the profile: the merge is three-way per part, so
40
+ * the server only ever holds whole versions of the whole setup.
41
+ */
42
+ export const REMOTE_FOLDER = 'dsh-webdav';
43
+ /** The single sealed file this plugin owns on the server. */
44
+ export const REMOTE_FILE = 'dsh-profile.dshsync';
45
+ /** Extension the sealed envelope is written with. */
46
+ export const BACKUP_EXT = '.dshsync';
47
+ // ---------------------------------------------------------------------------
48
+ // The sync parts
49
+ // ---------------------------------------------------------------------------
50
+ /**
51
+ * The parts of a DSH setup that travel, in merge order. Each is hashed on its
52
+ * own, so a change to one never drags the others along behind it.
53
+ */
54
+ export const PARTS = ['models', 'defaults', 'plugins', 'patch'];
55
+ /** Human copy for each part, used by the card and by notices. */
56
+ export const PART_LABELS = {
57
+ models: { zh: '模型供应商与模型', en: 'Model providers and models' },
58
+ defaults: { zh: '默认模型', en: 'Default model' },
59
+ plugins: { zh: '已安装插件', en: 'Installed plugins' },
60
+ patch: { zh: '其余 profile 配置', en: 'Other profile configuration' },
61
+ };
62
+ /** The format word in the envelope; anything else on the server is not ours. */
63
+ export const BUNDLE_FORMAT = 'dsh-webdav-sync-backup';
64
+ /** The bundle structure version this build writes and reads. */
65
+ export const BUNDLE_VERSION = 1;
66
+ /** Value written to `Bundle.app`. */
67
+ export const BUNDLE_APP = 'dsh-webdav-sync';
68
+ /** How often the gateway syncs on its own. `-1` is "only when asked". */
69
+ export const AUTO_MANUAL = -1;
70
+ /** The intervals the card offers, in minutes. */
71
+ export const AUTO_CHOICES = [AUTO_MANUAL, 3, 15, 30, 60];
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "WebDAV sync",
4
+ "description": "Sync the model configuration and installed plugins over WebDAV."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "WebDAV 同步 / WebDAV sync",
4
+ "description": "把模型配置与已安装插件同步到 WebDAV。 / Sync the model configuration and installed plugins over WebDAV."
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,110 @@
1
1
  {
2
2
  "name": "dsh-webdav-sync",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.1",
4
+ "description": "用 WebDAV 在多台电脑之间同步 DSH 的配置:模型供应商与模型、默认模型、已安装插件。整个备份在离开本机前用口令加密(AES-256-GCM + PBKDF2),以分片三方合并的方式在服务端保留一个加密文件。",
5
+ "keywords": [
6
+ "dsh",
7
+ "deepseek",
8
+ "cordis",
9
+ "plugin",
10
+ "webdav",
11
+ "sync",
12
+ "backup",
13
+ "nutstore",
14
+ "nextcloud"
15
+ ],
16
+ "homepage": "https://github.com/GaoHu1997/dsh-webdav#readme",
17
+ "bugs": {
18
+ "url": "https://github.com/GaoHu1997/dsh-webdav/issues"
19
+ },
20
+ "license": "MIT",
21
+ "author": "GaoHu1997 (奋斗的小高) <gaohu9712@163.com>",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/GaoHu1997/dsh-webdav.git"
25
+ },
26
+ "type": "module",
27
+ "main": "lib/index.js",
28
+ "types": "lib/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./lib/index.d.ts",
32
+ "default": "./lib/index.js"
33
+ },
34
+ "./wire": {
35
+ "types": "./lib/wire.d.ts",
36
+ "default": "./lib/wire.js"
37
+ },
38
+ "./client": {
39
+ "default": "./lib/client.js"
40
+ },
41
+ "./locale/*.json": "./locale/*.json",
42
+ "./package.json": "./package.json"
43
+ },
44
+ "files": [
45
+ "lib",
46
+ "!lib/**/*.map",
47
+ "cordis.patch.yml",
48
+ "README.md",
49
+ "locale/*.json",
50
+ "icon.svg"
51
+ ],
52
+ "icon": "./icon.svg",
53
+ "publishConfig": {
54
+ "registry": "https://registry.npmjs.org/",
55
+ "access": "public"
56
+ },
57
+ "dsh": {
58
+ "bundle": {
59
+ "patch": "./cordis.patch.yml"
60
+ },
61
+ "client": {
62
+ "platform": "web",
63
+ "inject": [
64
+ "@deepseek-ai/dsh-client-ui-settings"
65
+ ]
66
+ }
67
+ },
68
+ "dependencies": {
69
+ "yaml": "^2.9.1"
70
+ },
71
+ "peerDependencies": {
72
+ "@deepseek-ai/cordis": "^4.0.3",
73
+ "@deepseek-ai/dsh-host-webserver": ">=0.1.7-rc.1 <0.3.0-0 || >=0.2.0-0 <0.3.0-0",
74
+ "@deepseek-ai/dsh-settings": ">=0.1.7-rc.1 <0.3.0-0 || >=0.2.0-0 <0.3.0-0",
75
+ "@deepseek-ai/schemastery": "^3.18.3"
76
+ },
77
+ "devDependencies": {
78
+ "@deepseek-ai/cordis": "^4.0.3",
79
+ "@deepseek-ai/dsh-client-locale": "0.2.0-rc.1",
80
+ "@deepseek-ai/dsh-client-store": "0.2.0-rc.1",
81
+ "@deepseek-ai/dsh-client-ui-plugin-manager": "0.2.0-rc.1",
82
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.1",
83
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.1",
84
+ "@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.1",
85
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.1",
86
+ "@deepseek-ai/dsh-host-webserver": "0.2.0-rc.1",
87
+ "@deepseek-ai/dsh-settings": "0.2.0-rc.1",
88
+ "@deepseek-ai/schemastery": "^3.18.3",
89
+ "@types/node": "^24.13.3",
90
+ "@types/react": "~18.3.1",
91
+ "@types/react-dom": "~18.3.1",
92
+ "react": "^18.2.0",
93
+ "react-dom": "^18.2.0",
94
+ "tsdown": "^0.14.0",
95
+ "typescript": "^5.9.3",
96
+ "vitest": "^3.2.4",
97
+ "yaml": "^2.9.1"
98
+ },
99
+ "engines": {
100
+ "node": ">=22.19"
101
+ },
102
+ "scripts": {
103
+ "build": "tsc -p tsconfig.json",
104
+ "build:client": "tsdown",
105
+ "build:all": "pnpm run build && pnpm run build:client",
106
+ "watch:client": "tsdown --watch",
107
+ "test": "vitest run",
108
+ "typecheck": "tsc -p tsconfig.client.json --noEmit"
109
+ }
6
110
  }