sage-mem 2.5.1 → 2.7.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/README.en.md +6 -6
- package/README.md +6 -6
- package/lib/client.js +862 -168
- package/lib/index.js +6 -3
- package/lib/settings.js +41 -9
- package/lib/typert.host.js +4 -4
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -430,14 +430,16 @@ export class MemoryGateway extends TypertRemoteService {
|
|
|
430
430
|
const norm = (list) => JSON.stringify([...list].map((s) => String(s).toLowerCase()).sort())
|
|
431
431
|
const restartRequired =
|
|
432
432
|
JSON.stringify(limits) !== JSON.stringify(pending.limits) || norm(activeExtra) !== norm(reservedExtra)
|
|
433
|
+
// 2.7.0:entry 是 canonical;sidebarEntry 只为 2.6.0 的客户端保留(派生值,不落盘)。
|
|
434
|
+
const ui = pendingUi(fileRead.data)
|
|
433
435
|
return {
|
|
434
436
|
limits,
|
|
435
437
|
pendingLimits: pending.limits,
|
|
436
438
|
restartRequired,
|
|
437
439
|
// Q04 的诊断出口:面板据此说明「你保存的值没生效,原因是 X」;宿主侧先如实给出,别让问题只能靠猜。
|
|
438
440
|
limitDiagnostics,
|
|
439
|
-
// 1.8.0
|
|
440
|
-
ui:
|
|
441
|
+
// 1.8.0 / 2.7.0:客户端行为(界面入口三选一)。**不参与** restartRequired —— 改完立刻生效。
|
|
442
|
+
ui: { ...ui, sidebarEntry: ui.entry !== 'settings' },
|
|
441
443
|
// 生效的保护名单(含两个内置名)+ 用户声明的那部分原文(面板编辑的是后者)
|
|
442
444
|
reserved: [...new Set([INDEX_FILE_KEY, SESSION_LOG_KEY, ...reservedExtra.map((s) => s.toLowerCase())])],
|
|
443
445
|
reservedExtra,
|
|
@@ -498,7 +500,8 @@ export class MemoryGateway extends TypertRemoteService {
|
|
|
498
500
|
}
|
|
499
501
|
const current = currentRead.data
|
|
500
502
|
const mergedLimits = { ...(current?.limits ?? {}), ...nextLimits }
|
|
501
|
-
// ui 只覆盖这次给出的键;没给就沿用文件里的有效值(坏值 /
|
|
503
|
+
// ui 只覆盖这次给出的键;没给就沿用文件里的有效值(坏值 / 缺失一律按默认「两个都显示」)。
|
|
504
|
+
// 2.7.0:落盘只写 canonical 的 entry,旧 sidebarEntry 由 pendingUi 迁移进来、不再写出。
|
|
502
505
|
const mergedUi = { ...pendingUi(current), ...(uiPatch ?? {}) }
|
|
503
506
|
await writeFileAtomic(SETTINGS_FILE, JSON.stringify({ limits: mergedLimits, ui: mergedUi, updatedAt: Date.now() }, null, 2))
|
|
504
507
|
}
|
package/lib/settings.js
CHANGED
|
@@ -21,22 +21,34 @@ export const RESERVED_FILE = join(STATE_ROOT, 'reserved.json')
|
|
|
21
21
|
export const clampInt = (v, spec) => (Number.isFinite(v) ? Math.max(spec.lo, Math.min(spec.hi, Math.round(v))) : spec.dflt)
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* 面板 UI 设置(1.8.0
|
|
24
|
+
* 面板 UI 设置(1.8.0;2.7.0 改成三选一)。
|
|
25
25
|
*
|
|
26
26
|
* ⚠️ 与 `limits` **不是一类**:上限在模块加载时就固化进常量,改完必须重启;
|
|
27
|
-
* `ui.
|
|
27
|
+
* `ui.entry` 是**客户端行为** —— 界面读到值就决定注不注册侧栏 / 设置页槽位,
|
|
28
28
|
* 改完立刻生效。所以它不进 `restartRequired` 的计算。
|
|
29
|
+
*
|
|
30
|
+
* 2.7.0:`entry` 取代 1.8.0 的布尔 `sidebarEntry`。三个值从类型上就保证
|
|
31
|
+
* 「至少保留一个入口」,不需要额外的互斥校验;旧字段只读不写。
|
|
29
32
|
*/
|
|
30
|
-
export const
|
|
33
|
+
export const ENTRY_MODES = Object.freeze(['both', 'sidebar', 'settings'])
|
|
34
|
+
export const DEFAULT_UI = Object.freeze({ entry: 'both' })
|
|
31
35
|
|
|
32
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* 读出 `ui` 的**有效值**:新字段优先,旧 `sidebarEntry` 按
|
|
38
|
+
* `true → both` / `false → settings` 迁移;缺失或坏值回落默认(两个都显示)。
|
|
39
|
+
*/
|
|
33
40
|
export function pendingUi(fileObj) {
|
|
34
41
|
const saved = fileObj?.ui && typeof fileObj.ui === 'object' && !Array.isArray(fileObj.ui) ? fileObj.ui : {}
|
|
35
|
-
|
|
42
|
+
if (ENTRY_MODES.includes(saved.entry)) return { entry: saved.entry }
|
|
43
|
+
if (typeof saved.sidebarEntry === 'boolean') return { entry: saved.sidebarEntry ? 'both' : 'settings' }
|
|
44
|
+
return { entry: DEFAULT_UI.entry }
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
/**
|
|
39
|
-
* 校验 `setSettings({ ui })`
|
|
48
|
+
* 校验 `setSettings({ ui })` 的那一段:认 `entry`(2.7.0)与旧 `sidebarEntry`(1.8.0)。
|
|
49
|
+
*
|
|
50
|
+
* 两个键同时给时要求语义一致(`sidebarEntry === (entry !== 'settings')`),否则报冲突 ——
|
|
51
|
+
* 旧客户端写 true 的语义是「侧栏显示、设置页也保留」,和 `entry:'sidebar'` 不是一回事。
|
|
40
52
|
*
|
|
41
53
|
* 返回 `{ ok:true, patch }`(patch 为 null = 调用方没给 ui,或给的是空对象);`{ ok:false, error }`。
|
|
42
54
|
* 与 limits 同一套严格语义:绝不静默丢掉不认识的键。
|
|
@@ -45,10 +57,30 @@ export function parseUiPatch(ui) {
|
|
|
45
57
|
if (ui === undefined) return { ok: true, patch: null }
|
|
46
58
|
if (!ui || typeof ui !== 'object' || Array.isArray(ui)) return { ok: false, error: 'sage-mem: ui must be an object' }
|
|
47
59
|
const patch = {}
|
|
60
|
+
let hasLegacy = false
|
|
61
|
+
let legacy = null
|
|
48
62
|
for (const [key, value] of Object.entries(ui)) {
|
|
49
|
-
if (key
|
|
50
|
-
|
|
51
|
-
|
|
63
|
+
if (key === 'entry') {
|
|
64
|
+
if (typeof value !== 'string' || !ENTRY_MODES.includes(value)) {
|
|
65
|
+
return { ok: false, error: `sage-mem: ui.entry must be one of ${ENTRY_MODES.join(' | ')}, got ${JSON.stringify(value)}` }
|
|
66
|
+
}
|
|
67
|
+
patch.entry = value
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
if (key === 'sidebarEntry') {
|
|
71
|
+
if (typeof value !== 'boolean') return { ok: false, error: `sage-mem: ui.sidebarEntry must be a boolean, got ${typeof value}` }
|
|
72
|
+
hasLegacy = true
|
|
73
|
+
legacy = value
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
return { ok: false, error: `sage-mem: unknown ui setting: ${key}` }
|
|
77
|
+
}
|
|
78
|
+
if (hasLegacy) {
|
|
79
|
+
const derived = legacy ? 'both' : 'settings'
|
|
80
|
+
if (patch.entry !== undefined && patch.entry !== derived) {
|
|
81
|
+
return { ok: false, error: `sage-mem: ui.entry=${patch.entry} conflicts with ui.sidebarEntry=${legacy}` }
|
|
82
|
+
}
|
|
83
|
+
patch.entry = derived
|
|
52
84
|
}
|
|
53
85
|
return { ok: true, patch: Object.keys(patch).length ? patch : null }
|
|
54
86
|
}
|
package/lib/typert.host.js
CHANGED
|
@@ -239,10 +239,9 @@ const settingsLimitsSchema = z.object({
|
|
|
239
239
|
'maxSessionBytes': z.number().readonly(),
|
|
240
240
|
'staleDays': z.number().readonly(),
|
|
241
241
|
}).readonly()
|
|
242
|
-
// 1.8.0:面板 UI 设置。`
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}).readonly()
|
|
242
|
+
// 1.8.0 / 2.7.0:面板 UI 设置。`entry` 是**客户端行为**(改完立刻生效),不进 restartRequired。
|
|
243
|
+
// `sidebarEntry` 是给 2.6.0 客户端的派生值(= entry !== 'settings'),永远随 `entry` 一起返回。
|
|
244
|
+
const settingsUiSchema = z.object({ 'entry': z.enum(['both', 'sidebar', 'settings']).readonly(), 'sidebarEntry': z.boolean().readonly() }).readonly()
|
|
246
245
|
const settingsSchema = z.object({
|
|
247
246
|
'limits': settingsLimitsSchema,
|
|
248
247
|
'pendingLimits': settingsLimitsSchema,
|
|
@@ -260,6 +259,7 @@ const settingsPatchSchema = z.object({
|
|
|
260
259
|
'staleDays': z.number().optional(),
|
|
261
260
|
}).readonly().optional(),
|
|
262
261
|
'ui': z.object({
|
|
262
|
+
'entry': z.enum(['both', 'sidebar', 'settings']).optional(),
|
|
263
263
|
'sidebarEntry': z.boolean().optional(),
|
|
264
264
|
}).readonly().optional(),
|
|
265
265
|
'reserved': z.array(z.string()).optional(),
|
package/package.json
CHANGED