dsh-reference-session-collector 0.2.4 → 0.2.5

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/dsh.plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "dsh-reference-session-collector",
3
3
  "name": "dsh-reference-session-collector",
4
- "version": "0.2.4",
4
+ "version": "0.2.5",
5
5
  "description": {
6
6
  "en": "Session reference projector (DRS): read-only scan of ~/.dsh/sessions logs, decoded into a private SQLite (segments/replaces/FTS5) with surface flags, recall-pair accounting and a Chinese-capable search face, served via /api/dsh-reference-session-collector. Never writes session files.",
7
7
  "zh": "会话原文投影同步器(DRS):只读扫描 ~/.dsh/sessions 会话日志,解码后投影进自有 SQLite(segments/replaces/FTS5),经 /api/dsh-reference-session-collector 提供 surface 标记、召回对账与中文可用的内容检索面。绝不写会话文件。"
package/lib/index.d.ts CHANGED
@@ -695,6 +695,37 @@ interface DrsHandlerDeps {
695
695
  * 未知 op / 缺参 → 400(不留空壳)。
696
696
  */
697
697
  declare function handleDrsRequest(deps: DrsHandlerDeps, req: IncomingMessage, res: ServerResponse): Promise<void>;
698
+ /**
699
+ * 跨插件只读门面的返回形状:与 HTTP 面 `handleDrsRequest` 同字段
700
+ * (消费方已按那套映射写)。
701
+ */
702
+ interface DrsReadFacade {
703
+ /** 会话汇总分页(按 last_time_ms 降序)。 */
704
+ listSessions: (offset?: number, limit?: number) => {
705
+ total: number;
706
+ rows: SessionRow[];
707
+ };
708
+ /** 单会话明细;opts.tail 缺省即全量(消费方的重建事实源臂依赖这点)。 */
709
+ sessionDetail: (sessionId: string, opts?: SessionDetailOptions) => {
710
+ session: SessionRow | undefined;
711
+ rows: unknown[];
712
+ };
713
+ /** 全文检索(脱敏臂由 opts.redact 控制)。 */
714
+ search: (query: string, opts?: {
715
+ session?: string;
716
+ limit?: number;
717
+ redact?: boolean;
718
+ }) => {
719
+ query: string;
720
+ match: string;
721
+ hits: Record<string, unknown>[];
722
+ };
723
+ }
724
+ /**
725
+ * 取跨插件只读门面。库尚未就绪(apply 延迟初始化未完成)或已 dispose 时返回
726
+ * **undefined**,不抛——让消费方自己决定降级口径(报不可用或回退 HTTP)。
727
+ */
728
+ declare function drsReadFacade(): DrsReadFacade | undefined;
698
729
  /**
699
730
  * 插件主体:apply() 立即返回,重活(开库/建 sync/注册路由)推迟到 setImmediate。
700
731
  * 宿主插件装载串行同步——apply 不返回则整列插件都装不上,前端一直 Loading plugins…。
@@ -702,4 +733,4 @@ declare function handleDrsRequest(deps: DrsHandlerDeps, req: IncomingMessage, re
702
733
  */
703
734
  declare function apply(ctx: Context): void;
704
735
  //#endregion
705
- export { BOOT_SYNC_DELAY_MS, DEFAULT_SYNC_INTERVAL_MS, DISCOVERY_LIMIT, DRS_API_PATH, type DecodedEvent, DrsHandlerDeps, type ExactCounter, type GroupedSearchHit, type GroupedSearchRequest, INJECTED_BLOCK_TAGS, LEDGER_LRU_CAP, LedgerCache, PROJECTION_SCHEMA_VERSION, type ProjectionOptions, type ProjectionPort, type ProjectionReplace, type ProjectionSegment, type ProjectionStore, ProjectionSync, type QueryContext, SYNC_INTERVAL_ENV, type SessionLogRead, type SessionProjection, type SessionRow, type SessionSummary, type SyncResult, type SyncStorePort, apply, buildSearchSnippet, classifySessionHeader, createQueryContext, createStorePort, decodeSessionLogBytes, decodeSessionLogFile, discoverSessionLogs, extractSearchEventText, extractTitleFromEvents, generationLogFilename, handleDrsRequest, inject, listSessions, openProjectionStore, projectSessionEvents, readArchivedSessionIds, readSessionGenerations, recallReport, reloadSummaries, resolveDshHome, resolveExactCounter, resolveIndexDir, resolveSyncIntervalMs, sanitizeFtsQuery, search, searchGrouped, segmentForIndex, segmentQueryTerm, sessionDetail, startIntervalSync, status, stripInjectedBlocks, surfaceLedger };
736
+ export { BOOT_SYNC_DELAY_MS, DEFAULT_SYNC_INTERVAL_MS, DISCOVERY_LIMIT, DRS_API_PATH, type DecodedEvent, DrsHandlerDeps, DrsReadFacade, type ExactCounter, type GroupedSearchHit, type GroupedSearchRequest, INJECTED_BLOCK_TAGS, LEDGER_LRU_CAP, LedgerCache, PROJECTION_SCHEMA_VERSION, type ProjectionOptions, type ProjectionPort, type ProjectionReplace, type ProjectionSegment, type ProjectionStore, ProjectionSync, type QueryContext, SYNC_INTERVAL_ENV, type SessionLogRead, type SessionProjection, type SessionRow, type SessionSummary, type SyncResult, type SyncStorePort, apply, buildSearchSnippet, classifySessionHeader, createQueryContext, createStorePort, decodeSessionLogBytes, decodeSessionLogFile, discoverSessionLogs, drsReadFacade, extractSearchEventText, extractTitleFromEvents, generationLogFilename, handleDrsRequest, inject, listSessions, openProjectionStore, projectSessionEvents, readArchivedSessionIds, readSessionGenerations, recallReport, reloadSummaries, resolveDshHome, resolveExactCounter, resolveIndexDir, resolveSyncIntervalMs, sanitizeFtsQuery, search, searchGrouped, segmentForIndex, segmentQueryTerm, sessionDetail, startIntervalSync, status, stripInjectedBlocks, surfaceLedger };
package/lib/index.js CHANGED
@@ -2437,6 +2437,29 @@ function safeReadService(ctx, name) {
2437
2437
  }
2438
2438
  }
2439
2439
  /**
2440
+ * 活 QueryContext(在 apply 的 setImmediate 臂赋值,dispose 时清空)。
2441
+ *
2442
+ * 为何要模块级一份:消费方从自己包作用域 `import` 本包时,在 **hoisted 布局**
2443
+ * (desktop profile 就是 `nodeLinker: hoisted`)下解析到同一物理 lib/index.js,
2444
+ * Node 的 ESM 缓存会返回**同一个模块实例**,因此读到的是这里已赋值的活库。
2445
+ * 若某个部署是 pnpm 隔离布局且两包各有链接副本,两侧会是不同实例、本变量在消费侧
2446
+ * 为 undefined——门面会诚实返回 undefined,消费方回退自开库,不会读到错数据。
2447
+ */
2448
+ let liveQuery;
2449
+ /**
2450
+ * 取跨插件只读门面。库尚未就绪(apply 延迟初始化未完成)或已 dispose 时返回
2451
+ * **undefined**,不抛——让消费方自己决定降级口径(报不可用或回退 HTTP)。
2452
+ */
2453
+ function drsReadFacade() {
2454
+ const ctx = liveQuery;
2455
+ if (ctx === void 0) return void 0;
2456
+ return {
2457
+ listSessions: (offset, limit) => listSessions(ctx, offset, limit),
2458
+ sessionDetail: (sessionId, opts) => sessionDetail(ctx, sessionId, opts),
2459
+ search: (query, opts) => search(ctx, query, opts)
2460
+ };
2461
+ }
2462
+ /**
2440
2463
  * 插件主体:apply() 立即返回,重活(开库/建 sync/注册路由)推迟到 setImmediate。
2441
2464
  * 宿主插件装载串行同步——apply 不返回则整列插件都装不上,前端一直 Loading plugins…。
2442
2465
  * @param ctx - host 插件上下文(webServer 声明注入;webStartup/webRuntime 回退臂)。
@@ -2493,12 +2516,15 @@ function apply(ctx) {
2493
2516
  if (bootTimer !== void 0) clearTimeout(bootTimer);
2494
2517
  disposeInterval();
2495
2518
  store?.close();
2519
+ liveQuery = void 0;
2520
+ query = void 0;
2496
2521
  };
2497
2522
  }, "dsh-reference-session-collector: projection lifecycle");
2498
2523
  setImmediate(() => {
2499
2524
  try {
2500
2525
  store = openProjectionStore(resolveIndexDir());
2501
2526
  query = createQueryContext(store);
2527
+ liveQuery = query;
2502
2528
  sync = new ProjectionSync({
2503
2529
  store: createStorePort(store),
2504
2530
  project: (events, options) => projectSessionEvents(events, {
@@ -2542,4 +2568,4 @@ function apply(ctx) {
2542
2568
  });
2543
2569
  }
2544
2570
  //#endregion
2545
- export { BOOT_SYNC_DELAY_MS, DEFAULT_SYNC_INTERVAL_MS, DISCOVERY_LIMIT, DRS_API_PATH, INJECTED_BLOCK_TAGS, LEDGER_LRU_CAP, LedgerCache, PROJECTION_SCHEMA_VERSION, ProjectionSync, SYNC_INTERVAL_ENV, apply, buildSearchSnippet, classifySessionHeader, createQueryContext, createStorePort, decodeSessionLogBytes, decodeSessionLogFile, discoverSessionLogs, extractSearchEventText, extractTitleFromEvents, generationLogFilename, handleDrsRequest, inject, listSessions, openProjectionStore, projectSessionEvents, readArchivedSessionIds, readSessionGenerations, recallReport, reloadSummaries, resolveDshHome, resolveExactCounter, resolveIndexDir, resolveSyncIntervalMs, sanitizeFtsQuery, search, searchGrouped, segmentForIndex, segmentQueryTerm, sessionDetail, startIntervalSync, status, stripInjectedBlocks, surfaceLedger };
2571
+ export { BOOT_SYNC_DELAY_MS, DEFAULT_SYNC_INTERVAL_MS, DISCOVERY_LIMIT, DRS_API_PATH, INJECTED_BLOCK_TAGS, LEDGER_LRU_CAP, LedgerCache, PROJECTION_SCHEMA_VERSION, ProjectionSync, SYNC_INTERVAL_ENV, apply, buildSearchSnippet, classifySessionHeader, createQueryContext, createStorePort, decodeSessionLogBytes, decodeSessionLogFile, discoverSessionLogs, drsReadFacade, extractSearchEventText, extractTitleFromEvents, generationLogFilename, handleDrsRequest, inject, listSessions, openProjectionStore, projectSessionEvents, readArchivedSessionIds, readSessionGenerations, recallReport, reloadSummaries, resolveDshHome, resolveExactCounter, resolveIndexDir, resolveSyncIntervalMs, sanitizeFtsQuery, search, searchGrouped, segmentForIndex, segmentQueryTerm, sessionDetail, startIntervalSync, status, stripInjectedBlocks, surfaceLedger };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-reference-session-collector",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org",
6
6
  "tag": "beta",
package/src/index.ts CHANGED
@@ -11,6 +11,12 @@
11
11
  * - 启动延迟 2.5s 首轮 syncAll + interval 轮(缺省 30s,env 可关);
12
12
  * dispose 关库清定时器。
13
13
  *
14
+ * - 跨插件读面:`drsReadFacade()` 返回绑本插件**活 QueryContext** 的只读门面(供
15
+ * 消费方进程内调用,绕开宿主对插件服务端请求的 HTTP 鉴权)。消费方只需认这一个
16
+ * 稳定符号,不必知道 QueryContext/store 的存在,也不必自己开库(自开库会多占一份
17
+ * 常驻 summaries 与一个读事务,且要自己拿五个内部导出名)。库未就绪/已 dispose
18
+ * 时返回 undefined,由消费方降级。
19
+ *
14
20
  * 只读纪律:本文件不直接触碰会话文件——全部经 ProjectionSync 端口。
15
21
  */
16
22
  import type { Context } from '@deepseek-ai/cordis'
@@ -25,8 +31,9 @@ import {
25
31
  status,
26
32
  surfaceLedger,
27
33
  type QueryContext,
34
+ type SessionDetailOptions,
28
35
  } from './surface.ts'
29
- import { openProjectionStore, type ProjectionStore } from './schema.ts'
36
+ import { openProjectionStore, type ProjectionStore, type SessionRow } from './schema.ts'
30
37
  import { projectSessionEvents } from './projection.ts'
31
38
  import type { SessionProjection } from './projection.ts'
32
39
 
@@ -352,6 +359,44 @@ function safeReadService(ctx: Context, name: string): unknown {
352
359
  }
353
360
  }
354
361
 
362
+ /**
363
+ * 跨插件只读门面的返回形状:与 HTTP 面 `handleDrsRequest` 同字段
364
+ * (消费方已按那套映射写)。
365
+ */
366
+ export interface DrsReadFacade {
367
+ /** 会话汇总分页(按 last_time_ms 降序)。 */
368
+ listSessions: (offset?: number, limit?: number) => { total: number; rows: SessionRow[] }
369
+ /** 单会话明细;opts.tail 缺省即全量(消费方的重建事实源臂依赖这点)。 */
370
+ sessionDetail: (sessionId: string, opts?: SessionDetailOptions) => { session: SessionRow | undefined; rows: unknown[] }
371
+ /** 全文检索(脱敏臂由 opts.redact 控制)。 */
372
+ search: (query: string, opts?: { session?: string; limit?: number; redact?: boolean }) => { query: string; match: string; hits: Record<string, unknown>[] }
373
+ }
374
+
375
+ /**
376
+ * 活 QueryContext(在 apply 的 setImmediate 臂赋值,dispose 时清空)。
377
+ *
378
+ * 为何要模块级一份:消费方从自己包作用域 `import` 本包时,在 **hoisted 布局**
379
+ * (desktop profile 就是 `nodeLinker: hoisted`)下解析到同一物理 lib/index.js,
380
+ * Node 的 ESM 缓存会返回**同一个模块实例**,因此读到的是这里已赋值的活库。
381
+ * 若某个部署是 pnpm 隔离布局且两包各有链接副本,两侧会是不同实例、本变量在消费侧
382
+ * 为 undefined——门面会诚实返回 undefined,消费方回退自开库,不会读到错数据。
383
+ */
384
+ let liveQuery: QueryContext | undefined
385
+
386
+ /**
387
+ * 取跨插件只读门面。库尚未就绪(apply 延迟初始化未完成)或已 dispose 时返回
388
+ * **undefined**,不抛——让消费方自己决定降级口径(报不可用或回退 HTTP)。
389
+ */
390
+ export function drsReadFacade(): DrsReadFacade | undefined {
391
+ const ctx = liveQuery
392
+ if (ctx === undefined) return undefined
393
+ return {
394
+ listSessions: (offset, limit) => listSessions(ctx, offset, limit),
395
+ sessionDetail: (sessionId, opts) => sessionDetail(ctx, sessionId, opts),
396
+ search: (query, opts) => search(ctx, query, opts),
397
+ }
398
+ }
399
+
355
400
  /**
356
401
  * 插件主体:apply() 立即返回,重活(开库/建 sync/注册路由)推迟到 setImmediate。
357
402
  * 宿主插件装载串行同步——apply 不返回则整列插件都装不上,前端一直 Loading plugins…。
@@ -419,6 +464,9 @@ export function apply(ctx: Context): void {
419
464
  if (bootTimer !== undefined) clearTimeout(bootTimer)
420
465
  disposeInterval()
421
466
  store?.close()
467
+ // 库已关 → 门面必须同步失效,否则消费方拿到的是指向已关 db 的 QueryContext。
468
+ liveQuery = undefined
469
+ query = undefined
422
470
  }
423
471
  }, 'dsh-reference-session-collector: projection lifecycle')
424
472
 
@@ -428,6 +476,8 @@ export function apply(ctx: Context): void {
428
476
  const storeDir = resolveIndexDir()
429
477
  store = openProjectionStore(storeDir)
430
478
  query = createQueryContext(store)
479
+ // 对跨插件消费方暴露活查询面(见 drsReadFacade 注)。
480
+ liveQuery = query
431
481
 
432
482
  sync = new ProjectionSync({
433
483
  store: createStorePort(store),