dsh-sun-space 0.2.9 → 0.3.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/index.mjs +12 -9
- package/lib/router-tools.mjs +28 -19
- package/lib/worker-vault.mjs +19 -10
- package/package.json +1 -1
- package/src/client.js +20 -8
package/lib/index.mjs
CHANGED
|
@@ -22,11 +22,12 @@ var __exportAll = (all, no_symbols) => {
|
|
|
22
22
|
* ① 环境变量 SUN_VAULT(启动命令注入,随进程生效)
|
|
23
23
|
* ② ~/.dsh/state/sun-space/config.json 的 vault 字段(用户手写,持久化,不入库)
|
|
24
24
|
* ③ ~/.dsh/state/sun-space/state.json 的 vault 字段(Router 会话创建时插件自动登记 = 用户选定的工作区目录)
|
|
25
|
-
* ④
|
|
25
|
+
* ④ 会话工作目录(调用方从 exec 提取传入——用户在哪个工作区开会话,vault 就解析到哪)
|
|
26
|
+
* ⑤ 默认约定 ~/Workspace/sun-vault
|
|
26
27
|
*
|
|
27
28
|
* ③ 的设计动机:用户的 vault 就是他在 DSH 里选定的那个目录——Router 会话创建时
|
|
28
29
|
* 插件自动把该会话 cwd 登记为 vault 根(registerVaultRootFromSession),无需任何
|
|
29
|
-
* 手工配置。①②
|
|
30
|
+
* 手工配置。①② 供自定义/覆盖;⑤ 仅在全新裸环境兜底。
|
|
30
31
|
*
|
|
31
32
|
* 解析结果写回 state.json,作为浏览器端(孙学空间)的发现通道——浏览器经
|
|
32
33
|
* workspaceFiles/read 以绝对路径读取该文件(工作区外绝对路径读取被宿主允许)。
|
|
@@ -38,13 +39,14 @@ const DEFAULT_VAULT_DIRNAME = "sun-vault";
|
|
|
38
39
|
/** 插件自有状态目录(<DSH_HOME 或 ~/>/.dsh/state/sun-space/),沿用官方 state/<插件名> 约定。
|
|
39
40
|
* 跟随 DSH_HOME:多实例(隔离 HOME 的测试环境)之间状态互不串扰。 */
|
|
40
41
|
const stateDir = () => join(process.env.DSH_HOME ?? homedir(), ".dsh", "state", "sun-space");
|
|
41
|
-
/**
|
|
42
|
-
function computeVaultPath(envValue, configJson, stateJson, home = homedir()) {
|
|
42
|
+
/** 纯函数:五级优先解析(测试与 vaultPath 共用)。 */
|
|
43
|
+
function computeVaultPath(envValue, configJson, stateJson, sessionCwd, home = homedir()) {
|
|
43
44
|
if (envValue && envValue.trim() !== "") return envValue;
|
|
44
45
|
for (const raw of [configJson, stateJson]) try {
|
|
45
46
|
const parsed = JSON.parse(raw ?? "");
|
|
46
47
|
if (typeof parsed?.vault === "string" && parsed.vault.startsWith("/")) return parsed.vault;
|
|
47
48
|
} catch {}
|
|
49
|
+
if (sessionCwd && sessionCwd.startsWith("/")) return sessionCwd;
|
|
48
50
|
return join(home, "Workspace", DEFAULT_VAULT_DIRNAME);
|
|
49
51
|
}
|
|
50
52
|
let cached = {
|
|
@@ -52,8 +54,9 @@ let cached = {
|
|
|
52
54
|
key: "",
|
|
53
55
|
value: ""
|
|
54
56
|
};
|
|
55
|
-
/** 运行期解析:每次调用重读状态目录(60s 微缓存防抖),登记/手改后即时生效。
|
|
56
|
-
|
|
57
|
+
/** 运行期解析:每次调用重读状态目录(60s 微缓存防抖),登记/手改后即时生效。
|
|
58
|
+
* sessionCwd:调用方从 exec 提取的会话工作目录(可选)——用户在哪个工作区开会话,vault 就解析到哪。 */
|
|
59
|
+
function vaultPath(sessionCwd, now = Date.now()) {
|
|
57
60
|
const dir = stateDir();
|
|
58
61
|
let configJson;
|
|
59
62
|
let stateJson;
|
|
@@ -63,9 +66,9 @@ function vaultPath(now = Date.now()) {
|
|
|
63
66
|
try {
|
|
64
67
|
stateJson = readFileSync(join(dir, "state.json"), "utf8");
|
|
65
68
|
} catch {}
|
|
66
|
-
const key = `${configJson ?? ""}|${stateJson ?? ""}`;
|
|
69
|
+
const key = `${sessionCwd ?? ""}|${configJson ?? ""}|${stateJson ?? ""}`;
|
|
67
70
|
if (now - cached.at < 6e4 && cached.key === key) return cached.value;
|
|
68
|
-
const value = computeVaultPath(process.env.SUN_VAULT, configJson, stateJson);
|
|
71
|
+
const value = computeVaultPath(process.env.SUN_VAULT, configJson, stateJson, sessionCwd);
|
|
69
72
|
cached = {
|
|
70
73
|
at: now,
|
|
71
74
|
key,
|
|
@@ -110,7 +113,7 @@ function publishVaultState(now = /* @__PURE__ */ new Date()) {
|
|
|
110
113
|
try {
|
|
111
114
|
state = JSON.parse(readFileSync(p, "utf8"));
|
|
112
115
|
} catch {}
|
|
113
|
-
state.vault = vaultPath(now.getTime());
|
|
116
|
+
state.vault = vaultPath(void 0, now.getTime());
|
|
114
117
|
state.updatedAt = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}T${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
|
|
115
118
|
writeFileSync(p, JSON.stringify(state, null, 2), "utf8");
|
|
116
119
|
} catch {}
|
package/lib/router-tools.mjs
CHANGED
|
@@ -193,11 +193,12 @@ async function dingSelf(userId, content, type = "app") {
|
|
|
193
193
|
* ① 环境变量 SUN_VAULT(启动命令注入,随进程生效)
|
|
194
194
|
* ② ~/.dsh/state/sun-space/config.json 的 vault 字段(用户手写,持久化,不入库)
|
|
195
195
|
* ③ ~/.dsh/state/sun-space/state.json 的 vault 字段(Router 会话创建时插件自动登记 = 用户选定的工作区目录)
|
|
196
|
-
* ④
|
|
196
|
+
* ④ 会话工作目录(调用方从 exec 提取传入——用户在哪个工作区开会话,vault 就解析到哪)
|
|
197
|
+
* ⑤ 默认约定 ~/Workspace/sun-vault
|
|
197
198
|
*
|
|
198
199
|
* ③ 的设计动机:用户的 vault 就是他在 DSH 里选定的那个目录——Router 会话创建时
|
|
199
200
|
* 插件自动把该会话 cwd 登记为 vault 根(registerVaultRootFromSession),无需任何
|
|
200
|
-
* 手工配置。①②
|
|
201
|
+
* 手工配置。①② 供自定义/覆盖;⑤ 仅在全新裸环境兜底。
|
|
201
202
|
*
|
|
202
203
|
* 解析结果写回 state.json,作为浏览器端(孙学空间)的发现通道——浏览器经
|
|
203
204
|
* workspaceFiles/read 以绝对路径读取该文件(工作区外绝对路径读取被宿主允许)。
|
|
@@ -209,13 +210,14 @@ const DEFAULT_VAULT_DIRNAME = "sun-vault";
|
|
|
209
210
|
/** 插件自有状态目录(<DSH_HOME 或 ~/>/.dsh/state/sun-space/),沿用官方 state/<插件名> 约定。
|
|
210
211
|
* 跟随 DSH_HOME:多实例(隔离 HOME 的测试环境)之间状态互不串扰。 */
|
|
211
212
|
const stateDir = () => join(process.env.DSH_HOME ?? homedir(), ".dsh", "state", "sun-space");
|
|
212
|
-
/**
|
|
213
|
-
function computeVaultPath(envValue, configJson, stateJson, home = homedir()) {
|
|
213
|
+
/** 纯函数:五级优先解析(测试与 vaultPath 共用)。 */
|
|
214
|
+
function computeVaultPath(envValue, configJson, stateJson, sessionCwd, home = homedir()) {
|
|
214
215
|
if (envValue && envValue.trim() !== "") return envValue;
|
|
215
216
|
for (const raw of [configJson, stateJson]) try {
|
|
216
217
|
const parsed = JSON.parse(raw ?? "");
|
|
217
218
|
if (typeof parsed?.vault === "string" && parsed.vault.startsWith("/")) return parsed.vault;
|
|
218
219
|
} catch {}
|
|
220
|
+
if (sessionCwd && sessionCwd.startsWith("/")) return sessionCwd;
|
|
219
221
|
return join(home, "Workspace", DEFAULT_VAULT_DIRNAME);
|
|
220
222
|
}
|
|
221
223
|
let cached = {
|
|
@@ -223,8 +225,9 @@ let cached = {
|
|
|
223
225
|
key: "",
|
|
224
226
|
value: ""
|
|
225
227
|
};
|
|
226
|
-
/** 运行期解析:每次调用重读状态目录(60s 微缓存防抖),登记/手改后即时生效。
|
|
227
|
-
|
|
228
|
+
/** 运行期解析:每次调用重读状态目录(60s 微缓存防抖),登记/手改后即时生效。
|
|
229
|
+
* sessionCwd:调用方从 exec 提取的会话工作目录(可选)——用户在哪个工作区开会话,vault 就解析到哪。 */
|
|
230
|
+
function vaultPath(sessionCwd, now = Date.now()) {
|
|
228
231
|
const dir = stateDir();
|
|
229
232
|
let configJson;
|
|
230
233
|
let stateJson;
|
|
@@ -234,9 +237,9 @@ function vaultPath(now = Date.now()) {
|
|
|
234
237
|
try {
|
|
235
238
|
stateJson = readFileSync(join(dir, "state.json"), "utf8");
|
|
236
239
|
} catch {}
|
|
237
|
-
const key = `${configJson ?? ""}|${stateJson ?? ""}`;
|
|
240
|
+
const key = `${sessionCwd ?? ""}|${configJson ?? ""}|${stateJson ?? ""}`;
|
|
238
241
|
if (now - cached.at < 6e4 && cached.key === key) return cached.value;
|
|
239
|
-
const value = computeVaultPath(process.env.SUN_VAULT, configJson, stateJson);
|
|
242
|
+
const value = computeVaultPath(process.env.SUN_VAULT, configJson, stateJson, sessionCwd);
|
|
240
243
|
cached = {
|
|
241
244
|
at: now,
|
|
242
245
|
key,
|
|
@@ -247,6 +250,12 @@ function vaultPath(now = Date.now()) {
|
|
|
247
250
|
vaultPath();
|
|
248
251
|
// src/router-tools.ts
|
|
249
252
|
const inject = ["tools", "subagents"];
|
|
253
|
+
/** 从工具执行上下文提取会话工作目录(用户在哪个工作区开会话,vault 就解析到哪)。防御式:形状未知时返回 undefined。 */
|
|
254
|
+
function sessionCwdOf(exec) {
|
|
255
|
+
const e = exec;
|
|
256
|
+
const c = e?.agent?.session?.cwd ?? e?.agent?.session?.header?.cwd ?? e?.session?.cwd;
|
|
257
|
+
return typeof c === "string" && c.startsWith("/") ? c : void 0;
|
|
258
|
+
}
|
|
250
259
|
const text = (s) => [{
|
|
251
260
|
type: "text",
|
|
252
261
|
text: s
|
|
@@ -322,10 +331,10 @@ function apply(ctx) {
|
|
|
322
331
|
}
|
|
323
332
|
},
|
|
324
333
|
required: ["what"]
|
|
325
|
-
}, async (args) => {
|
|
334
|
+
}, async (args, exec) => {
|
|
326
335
|
try {
|
|
327
336
|
if (args.what === "triggers") {
|
|
328
|
-
const dir = resolveSafe(vaultPath(), "90-Meta/triggers");
|
|
337
|
+
const dir = resolveSafe(vaultPath(sessionCwdOf(exec)), "90-Meta/triggers");
|
|
329
338
|
try {
|
|
330
339
|
const files = (await readdir(dir)).filter((f) => f.endsWith(".json")).sort().reverse().slice(0, 10);
|
|
331
340
|
const triggers = [];
|
|
@@ -357,7 +366,7 @@ function apply(ctx) {
|
|
|
357
366
|
}
|
|
358
367
|
if (args.what === "journal") {
|
|
359
368
|
const d = args.date ?? localDate();
|
|
360
|
-
const p = resolveRead(vaultPath(), `40-Journal/${d}.md`);
|
|
369
|
+
const p = resolveRead(vaultPath(sessionCwdOf(exec)), `40-Journal/${d}.md`);
|
|
361
370
|
const raw = await readFile(p, "utf8");
|
|
362
371
|
if (args.full) return {
|
|
363
372
|
ok: true,
|
|
@@ -372,7 +381,7 @@ function apply(ctx) {
|
|
|
372
381
|
};
|
|
373
382
|
}
|
|
374
383
|
if (args.what === "tasks") {
|
|
375
|
-
const dir = resolveSafe(vaultPath(), "50-Tasks");
|
|
384
|
+
const dir = resolveSafe(vaultPath(sessionCwdOf(exec)), "50-Tasks");
|
|
376
385
|
const files = (await readdir(dir)).filter((f) => f.endsWith(".md"));
|
|
377
386
|
const tasks = [];
|
|
378
387
|
for (const f of files) {
|
|
@@ -395,7 +404,7 @@ function apply(ctx) {
|
|
|
395
404
|
};
|
|
396
405
|
}
|
|
397
406
|
if (args.what === "runbook" || args.what === "memory") {
|
|
398
|
-
const p = resolveSafe(vaultPath(), `90-Meta/${args.what}.md`);
|
|
407
|
+
const p = resolveSafe(vaultPath(sessionCwdOf(exec)), `90-Meta/${args.what}.md`);
|
|
399
408
|
return {
|
|
400
409
|
ok: true,
|
|
401
410
|
data: { text: (await readFile(p, "utf8")).slice(0, 8e3) }
|
|
@@ -406,7 +415,7 @@ function apply(ctx) {
|
|
|
406
415
|
ok: false,
|
|
407
416
|
note: "note 需要 path"
|
|
408
417
|
};
|
|
409
|
-
const raw = await readFile(resolveRead(vaultPath(), args.path), "utf8");
|
|
418
|
+
const raw = await readFile(resolveRead(vaultPath(sessionCwdOf(exec)), args.path), "utf8");
|
|
410
419
|
if (args.full) return {
|
|
411
420
|
ok: true,
|
|
412
421
|
data: { text: raw.slice(0, 8e3) }
|
|
@@ -448,9 +457,9 @@ function apply(ctx) {
|
|
|
448
457
|
}
|
|
449
458
|
},
|
|
450
459
|
required: ["path", "content"]
|
|
451
|
-
}, async (args) => {
|
|
460
|
+
}, async (args, exec) => {
|
|
452
461
|
try {
|
|
453
|
-
const p = resolveSafe(vaultPath(), args.path);
|
|
462
|
+
const p = resolveSafe(vaultPath(sessionCwdOf(exec)), args.path);
|
|
454
463
|
await mkdir(join(p, ".."), { recursive: true });
|
|
455
464
|
if (args.mode === "append") await appendFile(p, args.content, "utf8");
|
|
456
465
|
else await writeFile(p, args.content, "utf8");
|
|
@@ -544,9 +553,9 @@ function apply(ctx) {
|
|
|
544
553
|
}
|
|
545
554
|
},
|
|
546
555
|
required: ["path", "fields"]
|
|
547
|
-
}, async (args) => {
|
|
556
|
+
}, async (args, exec) => {
|
|
548
557
|
try {
|
|
549
|
-
const p = resolveSafe(vaultPath(), args.path);
|
|
558
|
+
const p = resolveSafe(vaultPath(sessionCwdOf(exec)), args.path);
|
|
550
559
|
const updated = updateFrontmatter(await readFile(p, "utf8"), args.fields);
|
|
551
560
|
await writeFile(p, updated, "utf8");
|
|
552
561
|
return {
|
|
@@ -620,7 +629,7 @@ function apply(ctx) {
|
|
|
620
629
|
};
|
|
621
630
|
}
|
|
622
631
|
try {
|
|
623
|
-
const p = resolveSafe(vaultPath(), `50-Tasks/${args.task_id}.md`);
|
|
632
|
+
const p = resolveSafe(vaultPath(sessionCwdOf(exec)), `50-Tasks/${args.task_id}.md`);
|
|
624
633
|
const raw = await readFile(p, "utf8");
|
|
625
634
|
await writeFile(p, updateFrontmatter(raw, { session: String(activation.childId ?? "") }), "utf8");
|
|
626
635
|
} catch {}
|
package/lib/worker-vault.mjs
CHANGED
|
@@ -36,11 +36,12 @@ function resolveSafe(vaultRoot, rel) {
|
|
|
36
36
|
* ① 环境变量 SUN_VAULT(启动命令注入,随进程生效)
|
|
37
37
|
* ② ~/.dsh/state/sun-space/config.json 的 vault 字段(用户手写,持久化,不入库)
|
|
38
38
|
* ③ ~/.dsh/state/sun-space/state.json 的 vault 字段(Router 会话创建时插件自动登记 = 用户选定的工作区目录)
|
|
39
|
-
* ④
|
|
39
|
+
* ④ 会话工作目录(调用方从 exec 提取传入——用户在哪个工作区开会话,vault 就解析到哪)
|
|
40
|
+
* ⑤ 默认约定 ~/Workspace/sun-vault
|
|
40
41
|
*
|
|
41
42
|
* ③ 的设计动机:用户的 vault 就是他在 DSH 里选定的那个目录——Router 会话创建时
|
|
42
43
|
* 插件自动把该会话 cwd 登记为 vault 根(registerVaultRootFromSession),无需任何
|
|
43
|
-
* 手工配置。①②
|
|
44
|
+
* 手工配置。①② 供自定义/覆盖;⑤ 仅在全新裸环境兜底。
|
|
44
45
|
*
|
|
45
46
|
* 解析结果写回 state.json,作为浏览器端(孙学空间)的发现通道——浏览器经
|
|
46
47
|
* workspaceFiles/read 以绝对路径读取该文件(工作区外绝对路径读取被宿主允许)。
|
|
@@ -52,13 +53,14 @@ const DEFAULT_VAULT_DIRNAME = "sun-vault";
|
|
|
52
53
|
/** 插件自有状态目录(<DSH_HOME 或 ~/>/.dsh/state/sun-space/),沿用官方 state/<插件名> 约定。
|
|
53
54
|
* 跟随 DSH_HOME:多实例(隔离 HOME 的测试环境)之间状态互不串扰。 */
|
|
54
55
|
const stateDir = () => join(process.env.DSH_HOME ?? homedir(), ".dsh", "state", "sun-space");
|
|
55
|
-
/**
|
|
56
|
-
function computeVaultPath(envValue, configJson, stateJson, home = homedir()) {
|
|
56
|
+
/** 纯函数:五级优先解析(测试与 vaultPath 共用)。 */
|
|
57
|
+
function computeVaultPath(envValue, configJson, stateJson, sessionCwd, home = homedir()) {
|
|
57
58
|
if (envValue && envValue.trim() !== "") return envValue;
|
|
58
59
|
for (const raw of [configJson, stateJson]) try {
|
|
59
60
|
const parsed = JSON.parse(raw ?? "");
|
|
60
61
|
if (typeof parsed?.vault === "string" && parsed.vault.startsWith("/")) return parsed.vault;
|
|
61
62
|
} catch {}
|
|
63
|
+
if (sessionCwd && sessionCwd.startsWith("/")) return sessionCwd;
|
|
62
64
|
return join(home, "Workspace", DEFAULT_VAULT_DIRNAME);
|
|
63
65
|
}
|
|
64
66
|
let cached = {
|
|
@@ -66,8 +68,9 @@ let cached = {
|
|
|
66
68
|
key: "",
|
|
67
69
|
value: ""
|
|
68
70
|
};
|
|
69
|
-
/** 运行期解析:每次调用重读状态目录(60s 微缓存防抖),登记/手改后即时生效。
|
|
70
|
-
|
|
71
|
+
/** 运行期解析:每次调用重读状态目录(60s 微缓存防抖),登记/手改后即时生效。
|
|
72
|
+
* sessionCwd:调用方从 exec 提取的会话工作目录(可选)——用户在哪个工作区开会话,vault 就解析到哪。 */
|
|
73
|
+
function vaultPath(sessionCwd, now = Date.now()) {
|
|
71
74
|
const dir = stateDir();
|
|
72
75
|
let configJson;
|
|
73
76
|
let stateJson;
|
|
@@ -77,9 +80,9 @@ function vaultPath(now = Date.now()) {
|
|
|
77
80
|
try {
|
|
78
81
|
stateJson = readFileSync(join(dir, "state.json"), "utf8");
|
|
79
82
|
} catch {}
|
|
80
|
-
const key = `${configJson ?? ""}|${stateJson ?? ""}`;
|
|
83
|
+
const key = `${sessionCwd ?? ""}|${configJson ?? ""}|${stateJson ?? ""}`;
|
|
81
84
|
if (now - cached.at < 6e4 && cached.key === key) return cached.value;
|
|
82
|
-
const value = computeVaultPath(process.env.SUN_VAULT, configJson, stateJson);
|
|
85
|
+
const value = computeVaultPath(process.env.SUN_VAULT, configJson, stateJson, sessionCwd);
|
|
83
86
|
cached = {
|
|
84
87
|
at: now,
|
|
85
88
|
key,
|
|
@@ -90,6 +93,12 @@ function vaultPath(now = Date.now()) {
|
|
|
90
93
|
vaultPath();
|
|
91
94
|
// src/worker-vault.ts
|
|
92
95
|
const inject = ["tools"];
|
|
96
|
+
/** 从工具执行上下文提取会话工作目录(同 router-tools)。 */
|
|
97
|
+
function sessionCwdOf(exec) {
|
|
98
|
+
const e = exec;
|
|
99
|
+
const c = e?.agent?.session?.cwd ?? e?.agent?.session?.header?.cwd ?? e?.session?.cwd;
|
|
100
|
+
return typeof c === "string" && c.startsWith("/") ? c : void 0;
|
|
101
|
+
}
|
|
93
102
|
const TASK_ID = /^T-\d{8}-\d{2}$/;
|
|
94
103
|
/** 纯函数:拼装受控产物路径;非法输入抛 VaultPathError。可单测。 */
|
|
95
104
|
function artifactPath(taskId, filename) {
|
|
@@ -135,11 +144,11 @@ function apply(ctx) {
|
|
|
135
144
|
text: JSON.stringify(value)
|
|
136
145
|
}]
|
|
137
146
|
},
|
|
138
|
-
execute: async (args) => {
|
|
147
|
+
execute: async (args, exec) => {
|
|
139
148
|
const { task_id: taskId, filename, content } = args ?? {};
|
|
140
149
|
try {
|
|
141
150
|
const rel = artifactPath(String(taskId), String(filename));
|
|
142
|
-
const abs = resolveSafe(vaultPath(), rel);
|
|
151
|
+
const abs = resolveSafe(vaultPath(sessionCwdOf(exec)), rel);
|
|
143
152
|
await mkdir(join(abs, ".."), { recursive: true });
|
|
144
153
|
await writeFile(abs, String(content ?? ""), "utf8");
|
|
145
154
|
return {
|
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -22,7 +22,7 @@ window.__ModuleLoader__.load({
|
|
|
22
22
|
// 会话解析链:session/list(sun-router 会话)→ 90-Meta/system/state.json 裁决 → updatedAt 最新兜底。
|
|
23
23
|
// vault 解析链:会话 cwd 推 HOME → 约定布局探测 state.json → state.json 的 vault 字段裁决(跨机自愈)。
|
|
24
24
|
// 写回:① 创建会话后经 session/prompt 引导自登记(含 vault 字段);② 晨报自检发现缺失/不一致自动修正。
|
|
25
|
-
const WB_REV = '
|
|
25
|
+
const WB_REV = 'v18' // 根节点 data-wb-rev 版本标记:判别浏览器是否吃到旧 client.js(immutable 缓存坑,HANDOFF 踩坑 1)
|
|
26
26
|
|
|
27
27
|
const STATUS_META = {
|
|
28
28
|
proposed: { label: '待确认派发', color: '#9e9e9e' },
|
|
@@ -115,7 +115,16 @@ window.__ModuleLoader__.load({
|
|
|
115
115
|
} catch { /* 该文件不存在 → 下一个 */ }
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
-
// ②
|
|
118
|
+
// ② 会话 cwd 即 vault(用户"新建目录作为工作区并开会话"流:cwd 下有 90-Meta 即认定)
|
|
119
|
+
try {
|
|
120
|
+
const cwd = String((items.find(s => s.sessionId === scope) ?? {}).cwd ?? '')
|
|
121
|
+
if (cwd && cwd.startsWith('/')) {
|
|
122
|
+
await readVaultFile(scope, `${cwd}/90-Meta/runbook.md`, 2)
|
|
123
|
+
D.vaultFoundNoField = cwd
|
|
124
|
+
return cwd
|
|
125
|
+
}
|
|
126
|
+
} catch { /* 该 cwd 无 90-Meta → 约定兜底 */ }
|
|
127
|
+
// ③ 约定布局探测(默认 ~/Workspace/sun-vault;自定义 vault 路径由①覆盖)
|
|
119
128
|
const candidates = [...new Set(homes.map(h => `${h}/Workspace/${VAULT_DIRNAME}`))]
|
|
120
129
|
for (const cand of candidates) {
|
|
121
130
|
try {
|
|
@@ -289,12 +298,14 @@ window.__ModuleLoader__.load({
|
|
|
289
298
|
requestId: 'sun-skel-' + Date.now(),
|
|
290
299
|
sessionId: routerSession,
|
|
291
300
|
mode: 'queue',
|
|
292
|
-
content: [{ type: 'text', text:
|
|
293
|
-
-
|
|
294
|
-
|
|
295
|
-
- 90-Meta/
|
|
296
|
-
- 90-Meta/
|
|
297
|
-
|
|
301
|
+
content: [{ type: 'text', text: `【知识库骨架补建 + vault 根登记】你的工作目录即知识库根(vault)。按顺序完成:
|
|
302
|
+
1) 把知识库根登记到状态目录 config.json(执行:mkdir -p "\${DSH_HOME:-\$HOME}/.dsh/state/sun-space" && printf '{"vault": "%s"}' "\$(pwd)" > "\${DSH_HOME:-\$HOME}/.dsh/state/sun-space/config.json"——此写在工作区之外,会弹一次审批,等待用户批准后再继续)
|
|
303
|
+
2) 用 sun_vault_write 创建/补全知识库骨架:
|
|
304
|
+
- 90-Meta/schemas.md:frontmatter 契约最小集(通用字段 type/id/status/created/updated;写入纪律同 runbook)
|
|
305
|
+
- 90-Meta/runbook.md:若缺失则写初始版(# Sun 行为手册 + v0.1)
|
|
306
|
+
- 90-Meta/memory.md:若缺失则写初始版(# 关于用户)
|
|
307
|
+
- 90-Meta/system/state.json:{"routerSession": "${routerSession}", "vault": "<你的工作目录绝对路径>", "updatedAt": "<本地时间>"}(已有的话合并更新 routerSession/vault 字段)
|
|
308
|
+
完成后报告清单与知识库路径。` }],
|
|
298
309
|
clientTimeZone: 'Asia/Shanghai',
|
|
299
310
|
})
|
|
300
311
|
onRetry()
|
|
@@ -526,6 +537,7 @@ window.__ModuleLoader__.load({
|
|
|
526
537
|
h('div', { key: 'tabs', style: { display: 'flex', gap: 3, marginBottom: 16, background: 'rgba(128,128,128,0.12)', borderRadius: 8, padding: 3 } }, [
|
|
527
538
|
['overview', '总览'], ['schedules', '定时任务'], ['setup', '设置'],
|
|
528
539
|
].map(([id, label]) => h('button', { key: id, onClick: () => setTab(id), style: { flex: 1, border: 'none', cursor: 'pointer', fontSize: 13, padding: '6px 0', borderRadius: 6, color: 'inherit', background: tab === id ? 'rgba(128,128,128,0.28)' : 'transparent', opacity: tab === id ? 1 : 0.6, transition: 'background 0.15s' } }, label))),
|
|
540
|
+
h('div', { key: 'vp', style: { fontSize: 12, opacity: 0.65, marginBottom: 12 } }, `📚 识别到的知识库:${vault ?? '(未定位——完成下方引导后自动登记)'}`),
|
|
529
541
|
h('div', { key: 'cnt', style: { display: 'flex', gap: 18, marginBottom: 16 } },
|
|
530
542
|
counters.map(([label, n, color]) => h('div', { key: label, style: { display: 'flex', alignItems: 'center', gap: 6 } }, [
|
|
531
543
|
h('span', { key: 'd', style: { width: 8, height: 8, borderRadius: 4, background: color, display: 'inline-block' } }),
|