dsh-session-cloud 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,117 @@
1
+ /**
2
+ * 本地同步状态(docs/02 §7):首选 dsh storageDomain 版本化 KV 域,
3
+ * 不可用时降级为 $DSH_HOME 下插件自建子目录的单文件 JSON。
4
+ */
5
+ import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain';
6
+ import fsp from 'node:fs/promises';
7
+ import path from 'node:path';
8
+ import { z } from 'zod';
9
+ const sessionStateSchema = z.object({
10
+ uploadedBytes: z.number().int().nonnegative(),
11
+ remoteBytes: z.number().int().nonnegative(),
12
+ lastSegmentIndex: z.number().int().min(-1),
13
+ lastTag: z.string(),
14
+ localRevision: z.string(),
15
+ eventCount: z.number().int().nonnegative(),
16
+ title: z.string().optional(),
17
+ updatedAt: z.number(),
18
+ status: z.enum(['ok', 'skipped-plain', 'conflict', 'error']).optional(),
19
+ error: z.string().optional(),
20
+ });
21
+ const globalStateSchema = z.object({
22
+ lastSyncAt: z.number().optional(),
23
+ lastError: z.string().optional(),
24
+ pathMappings: z.array(z.object({ from: z.string(), to: z.string() })),
25
+ });
26
+ export const INITIAL_GLOBAL = { pathMappings: [] };
27
+ /** storageDomain 实现(首选)。 */
28
+ export const cloudSyncDomainSpec = defineDomain({
29
+ name: 'cloud_sync', // UNIT_NAME_RE 只允许小写字母数字下划线(与 settings 的 cloud-sync 不同体系)
30
+ version: 1,
31
+ global: { schema: globalStateSchema, initial: INITIAL_GLOBAL },
32
+ tables: {
33
+ sessions: domainTable(sessionStateSchema),
34
+ },
35
+ });
36
+ export class DomainStateStore {
37
+ domain;
38
+ constructor(domain) {
39
+ this.domain = domain;
40
+ }
41
+ getSession(id) {
42
+ return this.domain.table('sessions').get(id);
43
+ }
44
+ entries() {
45
+ return this.domain.table('sessions').entries();
46
+ }
47
+ async putSession(id, state) {
48
+ await this.domain.table('sessions').put(id, state);
49
+ }
50
+ getGlobal() {
51
+ return this.domain.global.get();
52
+ }
53
+ async setGlobal(patch) {
54
+ await this.domain.global.set({ ...this.domain.global.get(), ...patch });
55
+ }
56
+ async close() {
57
+ await this.domain.close();
58
+ }
59
+ }
60
+ /** JSON 单文件降级实现:tmp + rename 原子写。 */
61
+ export class JsonFileStateStore {
62
+ file;
63
+ sessions = new Map();
64
+ global = INITIAL_GLOBAL;
65
+ writing = Promise.resolve();
66
+ constructor(dir) {
67
+ this.file = path.join(dir, 'state.json');
68
+ }
69
+ /** 打开(或创建)state 文件;损坏时回退为空 state(即全量重传),不阻断启动。 */
70
+ static async open(dir) {
71
+ const store = new JsonFileStateStore(dir);
72
+ try {
73
+ const parsed = JSON.parse(await fsp.readFile(store.file, 'utf8'));
74
+ for (const [id, state] of Object.entries(parsed.sessions ?? {}))
75
+ store.sessions.set(id, state);
76
+ if (parsed.global)
77
+ store.global = { ...INITIAL_GLOBAL, ...parsed.global };
78
+ }
79
+ catch {
80
+ // 缺失或损坏即空 state
81
+ }
82
+ return store;
83
+ }
84
+ persist() {
85
+ const snapshot = JSON.stringify({
86
+ sessions: Object.fromEntries(this.sessions),
87
+ global: this.global,
88
+ });
89
+ this.writing = this.writing.then(async () => {
90
+ await fsp.mkdir(path.dirname(this.file), { recursive: true });
91
+ const tmp = `${this.file}.tmp-${process.pid}`;
92
+ await fsp.writeFile(tmp, snapshot);
93
+ await fsp.rename(tmp, this.file);
94
+ });
95
+ return this.writing;
96
+ }
97
+ getSession(id) {
98
+ return this.sessions.get(id);
99
+ }
100
+ entries() {
101
+ return this.sessions.entries();
102
+ }
103
+ async putSession(id, state) {
104
+ this.sessions.set(id, state);
105
+ await this.persist();
106
+ }
107
+ getGlobal() {
108
+ return this.global;
109
+ }
110
+ async setGlobal(patch) {
111
+ this.global = { ...this.global, ...patch };
112
+ await this.persist();
113
+ }
114
+ async close() {
115
+ await this.writing;
116
+ }
117
+ }
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "dsh-session-cloud",
3
+ "version": "0.1.0",
4
+ "description": "E2E-encrypted cloud mirror plugin for deepseek-harness sessions",
5
+ "type": "module",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "files": [
10
+ "lib",
11
+ "cordis.patch.yml"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./lib/index.d.ts",
16
+ "default": "./lib/index.js"
17
+ },
18
+ "./client": "./lib/client.js",
19
+ "./package.json": "./package.json",
20
+ "./cordis.patch.yml": "./cordis.patch.yml"
21
+ },
22
+ "dsh": {
23
+ "client": {
24
+ "platform": "web",
25
+ "inject": [
26
+ "@deepseek-ai/dsh-client-ui-settings-plugins"
27
+ ]
28
+ },
29
+ "bundle": {
30
+ "patch": "./cordis.patch.yml"
31
+ }
32
+ },
33
+ "scripts": {
34
+ "build": "tsc -p tsconfig.build.json && node scripts/build-client.mjs",
35
+ "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.client.json",
36
+ "prepublishOnly": "pnpm run build",
37
+ "test": "node --import tsx --test src/**/*.test.ts"
38
+ },
39
+ "peerDependencies": {
40
+ "@deepseek-ai/cordis": "*"
41
+ },
42
+ "devDependencies": {
43
+ "@deepseek-ai/dsh-client-locale": "0.1.2-alpha.2",
44
+ "@deepseek-ai/dsh-client-store": "0.1.2-alpha.2",
45
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-alpha.2",
46
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-alpha.2",
47
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.2-alpha.2",
48
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.2-alpha.2",
49
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-alpha.2",
50
+ "@types/react": "^18",
51
+ "@types/react-dom": "^18",
52
+ "esbuild": "^0.28.2",
53
+ "react": "^18.3.1",
54
+ "react-dom": "^18.3.1"
55
+ },
56
+ "license": "MIT",
57
+ "dependencies": {
58
+ "@deepseek-ai/dsh-storage-domain": "0.0.1-rc.1",
59
+ "@deepseek-ai/schemastery": "^3.18.2",
60
+ "zod": "^4.5.4"
61
+ }
62
+ }