dsh-memory-eternal 0.6.1 → 0.6.2-rc.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/index.js CHANGED
@@ -277,7 +277,18 @@ export function apply(ctx, config) {
277
277
 
278
278
  const webServer = ctx.get('webServer')
279
279
  if (webServer !== undefined) {
280
- const handleApi = createApi({ vaultDir, vaultRoots, getSettings: settings.get })
280
+ const handleApi = createApi({
281
+ vaultDir, vaultRoots, getSettings: settings.get,
282
+ getDshInfo: () => ({
283
+ name: 'dsh',
284
+ label: 'DSH (当前宿主)',
285
+ installed: true,
286
+ memoryRecallTool: !!ctx.get('tools'),
287
+ autoCapture: (settings.get() ?? {}).autoCapture !== false,
288
+ autoRecall: (settings.get() ?? {}).autoRecall !== false,
289
+ vaultDir: vaultDir(),
290
+ }),
291
+ })
281
292
  webServer.register({
282
293
  kind: 'prefix',
283
294
  path: API_PREFIX,
@@ -287,6 +298,54 @@ export function apply(ctx, config) {
287
298
  if (pathname === API_PREFIX + '/web-info') {
288
299
  return json(res, 200, { ok: true, ...webInfo })
289
300
  }
301
+ if (pathname === API_PREFIX + '/config') {
302
+ const method = req.method || 'GET'
303
+ if (method === 'GET') {
304
+ const cfg = settings.get() ?? {}
305
+ // 只暴露可安全展示/回填的字段
306
+ const safe = {
307
+ autoCapture: cfg.autoCapture, autoRecall: cfg.autoRecall, recallLimit: cfg.recallLimit, recallSummaryLen: cfg.recallSummaryLen, recallIncludeBody: cfg.recallIncludeBody,
308
+ captureMinChars: cfg.captureMinChars, captureCooldownMs: cfg.captureCooldownMs, dedupThreshold: cfg.dedupThreshold, maxCardsPerDay: cfg.maxCardsPerDay,
309
+ autoWeb: cfg.autoWeb, autoWebMode: cfg.autoWebMode, webPort: cfg.webPort, webCheckIntervalMs: cfg.webCheckIntervalMs, webMaxRestart: cfg.webMaxRestart, watchdogAutoSpawn: cfg.watchdogAutoSpawn, autoMcpSetup: cfg.autoMcpSetup,
310
+ }
311
+ const descriptor = (ctx.get('settings') ?? {}).describe?.({ redactSecrets: true }) ?? []
312
+ const me = descriptor.find((d) => d.ns === 'memory-eternal')
313
+ const dshInfo = {
314
+ name: 'dsh',
315
+ label: 'DSH (当前宿主)',
316
+ installed: true,
317
+ memoryRecallTool: !!ctx.get('tools'),
318
+ autoCapture: cfg.autoCapture !== false,
319
+ autoRecall: cfg.autoRecall !== false,
320
+ vaultDir: vaultDir(),
321
+ }
322
+ return json(res, 200, { ok: true, config: safe, revision: me?.revision ?? 0, schema: me?.schema ?? null, dsh: dshInfo })
323
+ }
324
+ if (method === 'POST') {
325
+ let raw = ''
326
+ for await (const chunk of req) raw += chunk
327
+ let body = {}
328
+ try { body = JSON.parse(raw || '{}') } catch { return json(res, 400, { ok: false, error: 'JSON 解析失败' }) }
329
+ const patch = body.patch ?? {}
330
+ const expectedRevision = Number.isInteger(body.expectedRevision) ? body.expectedRevision : undefined
331
+ // 仅允许写入 Config 中声明过的键(白名单,防注入)。schemastery 用 .dict 存 object schema 字段表。
332
+ const allowed = new Set(Object.keys(Config.dict || {}))
333
+ const clean = {}
334
+ for (const k of Object.keys(patch)) { if (allowed.has(k)) clean[k] = patch[k] }
335
+ if (Object.keys(clean).length === 0) return json(res, 400, { ok: false, error: '无可写入字段' })
336
+ if (typeof settings.update === 'function') {
337
+ try {
338
+ await settings.update(clean)
339
+ return json(res, 200, { ok: true, applied: Object.keys(clean), note: '已保存。autoWebMode/watchdogAutoSpawn 等需重启 DSH 生效' })
340
+ } catch (e) {
341
+ if (e && e.code === 'SETTINGS_CONFLICT') return json(res, 409, { ok: false, error: '配置已被外部修改,请刷新后重试(revision conflict)' })
342
+ return json(res, 500, { ok: false, error: String(e?.message || e) })
343
+ }
344
+ }
345
+ return json(res, 501, { ok: false, error: '当前环境不支持写配置' })
346
+ }
347
+ return json(res, 405, { ok: false, error: 'method not allowed' })
348
+ }
290
349
  await handleApi(req, res)
291
350
  } catch (error) {
292
351
  json(res, 500, { ok: false, error: String(error?.message || error) })
package/lib/api.js CHANGED
@@ -20,6 +20,8 @@ export function createApi(deps = {}) {
20
20
  const vaultDir = deps.vaultDir ?? (() => '')
21
21
  const vaultRoots = deps.vaultRoots ?? (() => [{ name: '', root: vaultDir() }])
22
22
  const getSettings = deps.getSettings ?? (() => ({}))
23
+ // DSH 宿主信息(独立 web server 无 DSH 环境时为 null)。head DSH 状态行用。
24
+ const getDshInfo = deps.getDshInfo ?? (() => null)
23
25
 
24
26
  return async function handleApi(req, res) {
25
27
  const vaultRoot = vaultDir()
@@ -154,6 +156,11 @@ export function createApi(deps = {}) {
154
156
  case '/setup-status': {
155
157
  // 只读查询各 agent MCP 配置状态,不写任何文件
156
158
  const status = await getSetupStatus()
159
+ // prepend DSH 宿主行(独立 web server 无 DSH 环境时只有外部 agent)
160
+ const dsh = getDshInfo ? getDshInfo() : null
161
+ if (dsh) {
162
+ status.agents = [{ name: 'dsh', label: dsh.label || 'DSH (当前宿主)', installed: true, mcpConfigured: true, isDsh: true, recallTool: dsh.memoryRecallTool, autoCapture: dsh.autoCapture, autoRecall: dsh.autoRecall, vaultDir: dsh.vaultDir }, ...(status.agents || [])]
163
+ }
157
164
  json(res, 200, status)
158
165
  return
159
166
  }
@@ -170,10 +177,24 @@ export function createApi(deps = {}) {
170
177
  case '/budget': {
171
178
  try {
172
179
  const cfg = getSettings() ?? {}
173
- json(res, 200, { ok: true, budgetChars: cfg.sessionBudgetChars ?? 80000, recallLimit: cfg.recallLimit ?? 5, embedding: cfg.recallEmbedding || '' })
180
+ const env = typeof process !== 'undefined' ? process.env : {}
181
+ json(res, 200, {
182
+ ok: true,
183
+ budgetChars: cfg.sessionBudgetChars ?? 80000,
184
+ recallLimit: cfg.recallLimit ?? 5,
185
+ embedding: cfg.recallEmbedding || '',
186
+ // 服务自管理配置(DSH host 从 settings 读;独立 web server 从 env 兜底)
187
+ autoWeb: cfg.autoWeb ?? env.MEMORY_AUTO_WEB !== '0',
188
+ autoWebMode: cfg.autoWebMode || 'init',
189
+ webPort: Number(cfg.webPort) || Number(env.MEMORY_WEB_PORT) || 7999,
190
+ webCheckIntervalMs: Number(cfg.webCheckIntervalMs) || 5000,
191
+ webMaxRestart: Number(cfg.webMaxRestart) || 10,
192
+ watchdogAutoSpawn: cfg.watchdogAutoSpawn ?? env.MEMORY_WATCHDOG === '1',
193
+ autoMcpSetup: cfg.autoMcpSetup ?? false,
194
+ })
174
195
  } catch (e) {
175
196
  // 配置读取异常时降级返回默认值,避免面板整块红屏
176
- json(res, 200, { ok: true, budgetChars: 80000, recallLimit: 5, embedding: '' })
197
+ json(res, 200, { ok: true, budgetChars: 80000, recallLimit: 5, embedding: '', autoWeb: true, autoWebMode: 'init', webPort: 7999, webCheckIntervalMs: 5000, webMaxRestart: 10, watchdogAutoSpawn: true, autoMcpSetup: false })
177
198
  }
178
199
  return
179
200
  }