@wenbin_wb/dsh-bridge 2.8.3 → 2.8.4
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/CHANGELOG.md +7 -0
- package/lib/platform/conversation-bridge.js +265 -47
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [v2.8.4] - 2026-08-26
|
|
8
|
+
|
|
9
|
+
### 🗂️ 会话列表 Web 端 1:1 深度对齐与归档过滤修复
|
|
10
|
+
- **🗂️ 严格按工作区账本对齐会话列表**:全面采用 DSH 官方工作区账本排序规则,自动过滤子代理派生会话、空白草稿与已归档会话,彻底解决磁盘历史孤立会话冗余列出的问题,保持 IM 端与 Web 端侧边栏 1:1 结构一致与编号精准对应。
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
7
14
|
## [v2.8.3] - 2026-08-26
|
|
8
15
|
|
|
9
16
|
### 📱 微信与全平台 IM 文件收发链路全面修复与深度兼容
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
|
|
20
20
|
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
|
21
21
|
import { randomUUID } from 'node:crypto'
|
|
22
|
-
import { statSync } from 'node:fs'
|
|
22
|
+
import { statSync, existsSync, readFileSync } from 'node:fs'
|
|
23
23
|
import { stat } from 'node:fs/promises'
|
|
24
|
-
import { resolve, normalize, basename, isAbsolute } from 'node:path'
|
|
24
|
+
import { join, resolve, normalize, basename, isAbsolute } from 'node:path'
|
|
25
|
+
import { homedir } from 'node:os'
|
|
25
26
|
import { isSafeWorkspacePath } from '../security/path-validator.js'
|
|
26
27
|
|
|
27
28
|
// 纯文本标记(用户偏好不用 emoji)
|
|
@@ -942,45 +943,261 @@ function foldTitle(events) {
|
|
|
942
943
|
return null
|
|
943
944
|
}
|
|
944
945
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
//
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
946
|
+
/** 获取所有已归档会话 ID 集合(支持 ctx.workspaceRegistry 内存服务 + workspace.json 文件存储双重兜底) */
|
|
947
|
+
function getArchivedSessionIds(ctx) {
|
|
948
|
+
const archived = new Set()
|
|
949
|
+
// 1. 尝试从 ctx.workspaceRegistry 内存服务读取
|
|
950
|
+
try {
|
|
951
|
+
const list = ctx?.workspaceRegistry?.archivedSessionIds
|
|
952
|
+
if (Array.isArray(list)) {
|
|
953
|
+
for (const id of list) {
|
|
954
|
+
if (id) archived.add(String(id))
|
|
955
|
+
}
|
|
956
|
+
return archived
|
|
957
|
+
}
|
|
958
|
+
} catch { /* ignore */ }
|
|
959
|
+
|
|
960
|
+
// 2. 尝试从 DSH workspace 存储文件($DSH_HOME/storages/workspace.json)读取兜底
|
|
961
|
+
if (!ctx?._mock) {
|
|
955
962
|
try {
|
|
956
|
-
const
|
|
957
|
-
|
|
963
|
+
const home = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
964
|
+
const wsFile = join(home, 'storages', 'workspace.json')
|
|
965
|
+
if (existsSync(wsFile)) {
|
|
966
|
+
const data = JSON.parse(readFileSync(wsFile, 'utf8'))
|
|
967
|
+
const fileArchived = data?.global?.archivedSessionIds
|
|
968
|
+
if (Array.isArray(fileArchived)) {
|
|
969
|
+
for (const id of fileArchived) {
|
|
970
|
+
if (id) archived.add(String(id))
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
958
974
|
} catch { /* ignore */ }
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
return archived
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
/** 读取 DSH 官方持久化会话缓存元数据(标题、是否空白、创建时间等) */
|
|
981
|
+
function getSessionProjCache(ctx) {
|
|
982
|
+
if (ctx?._mock) return {}
|
|
963
983
|
try {
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
984
|
+
const home = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
985
|
+
const cacheFile = join(home, 'storages', 'session_projcache.json')
|
|
986
|
+
if (existsSync(cacheFile)) {
|
|
987
|
+
const data = JSON.parse(readFileSync(cacheFile, 'utf8'))
|
|
988
|
+
return data?.tables?.sessions || {}
|
|
989
|
+
}
|
|
990
|
+
} catch { /* ignore */ }
|
|
991
|
+
return {}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/** 读取 DSH 官方注册的工作区列表及各自绑定的 sessionIds 列表 */
|
|
995
|
+
async function getRegisteredWorkspaces(ctx) {
|
|
996
|
+
const workspaces = []
|
|
997
|
+
|
|
998
|
+
// 优先从内存服务获取
|
|
999
|
+
if (ctx?.workspaceRegistry) {
|
|
1000
|
+
try {
|
|
1001
|
+
const list = await ctx.workspaceRegistry.list?.()
|
|
1002
|
+
if (Array.isArray(list)) {
|
|
1003
|
+
for (const w of list) {
|
|
1004
|
+
if (w && w.path) {
|
|
1005
|
+
workspaces.push({
|
|
1006
|
+
id: w.id || w.path,
|
|
1007
|
+
path: w.path,
|
|
1008
|
+
title: w.title || basename(w.path),
|
|
1009
|
+
sessionIds: Array.isArray(w.sessionIds) ? [...w.sessionIds] : [],
|
|
1010
|
+
})
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
return workspaces
|
|
1014
|
+
}
|
|
1015
|
+
} catch { /* ignore */ }
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// 兜底从 workspace.json 存储文件读取
|
|
1019
|
+
if (!ctx?._mock) {
|
|
1020
|
+
try {
|
|
1021
|
+
const home = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
1022
|
+
const wsFile = join(home, 'storages', 'workspace.json')
|
|
1023
|
+
if (existsSync(wsFile)) {
|
|
1024
|
+
const data = JSON.parse(readFileSync(wsFile, 'utf8'))
|
|
1025
|
+
const wsIds = data?.global?.workspaceIds || Object.keys(data?.tables?.workspaces || {})
|
|
1026
|
+
const table = data?.tables?.workspaces || {}
|
|
1027
|
+
for (const wId of wsIds) {
|
|
1028
|
+
const ws = table[wId]
|
|
1029
|
+
if (ws && ws.path) {
|
|
1030
|
+
workspaces.push({
|
|
1031
|
+
id: wId,
|
|
1032
|
+
path: ws.path,
|
|
1033
|
+
title: ws.title || basename(ws.path),
|
|
1034
|
+
sessionIds: Array.isArray(ws.sessionIds) ? [...ws.sessionIds] : [],
|
|
1035
|
+
})
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
} catch { /* ignore */ }
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
return workspaces
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
function isSubagentSession(cacheRow, liveSession) {
|
|
1046
|
+
if (liveSession?.origin === 'subagent' || liveSession?.header?.origin === 'subagent') return true
|
|
1047
|
+
const subVal = cacheRow?.rows?.subagent?.val
|
|
1048
|
+
if (subVal && typeof subVal === 'object' && Object.keys(subVal).length > 0) return true
|
|
1049
|
+
return false
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// 列出会话:严格对齐 DSH Web 端侧边栏会话树逻辑。
|
|
1053
|
+
// 1. 过滤已归档会话 (archivedSessionIds)
|
|
1054
|
+
// 2. 过滤未发起提问的空白会话 (blank: true)
|
|
1055
|
+
// 3. 过滤子代理内部会话 (subagent origin)
|
|
1056
|
+
// 4. 严格按工作区账本 (workspace.sessionIds) 组织
|
|
1057
|
+
async function listSessions(node) {
|
|
1058
|
+
const archived = getArchivedSessionIds(node.ctx)
|
|
1059
|
+
const projCache = getSessionProjCache(node.ctx)
|
|
1060
|
+
const workspaces = await getRegisteredWorkspaces(node.ctx)
|
|
1061
|
+
|
|
1062
|
+
// 内存活跃会话(按 id 索引)
|
|
1063
|
+
const liveList = [...(node.ctx.sessions?.list?.() ?? [])].filter(
|
|
1064
|
+
(s) => s && s.id && !archived.has(s.id) && !s.archived && !s.header?.archived
|
|
1065
|
+
)
|
|
1066
|
+
const liveById = new Map(liveList.map((s) => [s.id, s]))
|
|
1067
|
+
|
|
1068
|
+
const accounted = new Set()
|
|
1069
|
+
const result = []
|
|
1070
|
+
|
|
1071
|
+
// 1. 如果存在已注册的工作区,严格按工作区及其 sessionIds 账本组织(与 Web 端完全一致)
|
|
1072
|
+
if (workspaces.length > 0) {
|
|
1073
|
+
for (const ws of workspaces) {
|
|
1074
|
+
const normWsPath = ws.path ? normalize(ws.path).toLowerCase() : ''
|
|
1075
|
+
|
|
1076
|
+
// 优先将当前工作区下新创建但在内存里的 live 会话追加到头部
|
|
1077
|
+
for (const s of liveList) {
|
|
1078
|
+
const sCwd = s.header?.cwd || s.cwd
|
|
1079
|
+
if (sCwd && normalize(sCwd).toLowerCase() === normWsPath && !accounted.has(s.id)) {
|
|
1080
|
+
if (isSubagentSession(projCache[s.id], s)) continue
|
|
1081
|
+
accounted.add(s.id)
|
|
1082
|
+
let title = s.title || (s.events ? foldTitle(s.events) : '')
|
|
1083
|
+
if (!title) {
|
|
1084
|
+
const cache = projCache[s.id]
|
|
1085
|
+
title = cache?.rows?.title?.val || cache?.rows?.goal?.val
|
|
1086
|
+
}
|
|
1087
|
+
result.push({
|
|
1088
|
+
id: s.id,
|
|
1089
|
+
createdAt: s.header?.createdAt || Date.now(),
|
|
1090
|
+
cwd: ws.path,
|
|
1091
|
+
workspaceTitle: ws.title,
|
|
1092
|
+
title: title || '新会话',
|
|
1093
|
+
events: s.events,
|
|
1094
|
+
seq: s.seq ?? 0,
|
|
1095
|
+
})
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// 按工作区账本存储的 sessionIds 顺序追加已记录会话
|
|
1100
|
+
for (const sId of ws.sessionIds) {
|
|
1101
|
+
if (archived.has(sId) || accounted.has(sId)) continue
|
|
1102
|
+
accounted.add(sId)
|
|
1103
|
+
|
|
1104
|
+
const cache = projCache[sId]
|
|
1105
|
+
const live = liveById.get(sId)
|
|
1106
|
+
// 过滤空白草稿会话(非当前活动会话)
|
|
1107
|
+
if (cache?.rows?.sessionListMetadata?.val?.blank === true && sId !== node.activeSessionId) {
|
|
1108
|
+
continue
|
|
1109
|
+
}
|
|
1110
|
+
// 过滤子代理内部会话
|
|
1111
|
+
if (isSubagentSession(cache, live)) {
|
|
1112
|
+
continue
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
let title = cache?.rows?.title?.val || cache?.rows?.goal?.val
|
|
1116
|
+
let createdAt = cache?.identity?.createdAt || 0
|
|
1117
|
+
let cwd = ws.path
|
|
1118
|
+
|
|
1119
|
+
// 如果内存有该会话,提取最新数据
|
|
1120
|
+
if (live) {
|
|
1121
|
+
title = live.title || (live.events ? foldTitle(live.events) : '') || title
|
|
1122
|
+
createdAt = live.header?.createdAt || createdAt
|
|
1123
|
+
} else if (!title && node.ctx.sessionPersistence?.load) {
|
|
973
1124
|
try {
|
|
974
|
-
const insp = await node.ctx.sessionPersistence.load(
|
|
1125
|
+
const insp = await node.ctx.sessionPersistence.load(sId)
|
|
975
1126
|
title = foldTitle(insp.events ?? []) ?? undefined
|
|
976
|
-
} catch {
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1127
|
+
} catch {}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
result.push({
|
|
1131
|
+
id: sId,
|
|
1132
|
+
createdAt,
|
|
1133
|
+
cwd,
|
|
1134
|
+
workspaceTitle: ws.title,
|
|
1135
|
+
title: title || '新会话',
|
|
1136
|
+
events: live?.events,
|
|
1137
|
+
seq: live?.seq ?? 0,
|
|
1138
|
+
})
|
|
980
1139
|
}
|
|
981
1140
|
}
|
|
982
|
-
|
|
983
|
-
|
|
1141
|
+
|
|
1142
|
+
// 处理当前内存中处于活动状态但未绑定任何工作区的 live 会话
|
|
1143
|
+
for (const s of liveList) {
|
|
1144
|
+
if (accounted.has(s.id)) continue
|
|
1145
|
+
if (isSubagentSession(projCache[s.id], s)) continue
|
|
1146
|
+
accounted.add(s.id)
|
|
1147
|
+
const title = s.title || (s.events ? foldTitle(s.events) : '') || '未分组会话'
|
|
1148
|
+
result.push({
|
|
1149
|
+
id: s.id,
|
|
1150
|
+
createdAt: s.header?.createdAt || Date.now(),
|
|
1151
|
+
cwd: s.header?.cwd || '(未指定)',
|
|
1152
|
+
workspaceTitle: '未指定工作区',
|
|
1153
|
+
title,
|
|
1154
|
+
events: s.events,
|
|
1155
|
+
seq: s.seq ?? 0,
|
|
1156
|
+
})
|
|
1157
|
+
}
|
|
1158
|
+
} else {
|
|
1159
|
+
// 2. 如果系统未注册任何工作区(如单目录/无工作区模式),降级读取内存及持久化会话
|
|
1160
|
+
for (const s of liveList) {
|
|
1161
|
+
accounted.add(s.id)
|
|
1162
|
+
const title = s.title || (s.events ? foldTitle(s.events) : '') || '活跃会话'
|
|
1163
|
+
result.push({
|
|
1164
|
+
id: s.id,
|
|
1165
|
+
createdAt: s.header?.createdAt || Date.now(),
|
|
1166
|
+
cwd: s.header?.cwd || '(未指定)',
|
|
1167
|
+
workspaceTitle: '未指定工作区',
|
|
1168
|
+
title,
|
|
1169
|
+
events: s.events,
|
|
1170
|
+
seq: s.seq ?? 0,
|
|
1171
|
+
})
|
|
1172
|
+
}
|
|
1173
|
+
if (node.ctx.sessionPersistence?.list) {
|
|
1174
|
+
try {
|
|
1175
|
+
const headers = await node.ctx.sessionPersistence.list()
|
|
1176
|
+
if (Array.isArray(headers)) {
|
|
1177
|
+
const coldHeaders = headers.filter((h) => h && h.id && !accounted.has(h.id) && !archived.has(h.id) && !h.archived)
|
|
1178
|
+
for (const h of coldHeaders) {
|
|
1179
|
+
accounted.add(h.id)
|
|
1180
|
+
let title
|
|
1181
|
+
try {
|
|
1182
|
+
const insp = await node.ctx.sessionPersistence.load(h.id)
|
|
1183
|
+
title = foldTitle(insp.events ?? []) ?? undefined
|
|
1184
|
+
} catch {}
|
|
1185
|
+
result.push({
|
|
1186
|
+
id: h.id,
|
|
1187
|
+
createdAt: h.createdAt ?? 0,
|
|
1188
|
+
events: undefined,
|
|
1189
|
+
seq: 0,
|
|
1190
|
+
cwd: h.cwd || '(未指定)',
|
|
1191
|
+
workspaceTitle: '未指定工作区',
|
|
1192
|
+
title: title || '新会话',
|
|
1193
|
+
})
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
} catch {}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
return result
|
|
984
1201
|
}
|
|
985
1202
|
|
|
986
1203
|
// 列出可用工作区:使用 DSH 官方 workspaceRegistry。返回 [{title, path}]。
|
|
@@ -1232,37 +1449,38 @@ async function renderSessions(node) {
|
|
|
1232
1449
|
if (all.length === 0) {
|
|
1233
1450
|
return `## 📋 会话列表\n\n> 暂无历史会话。发送 \`/new <提示词>\` 开始新会话。`
|
|
1234
1451
|
}
|
|
1235
|
-
//
|
|
1452
|
+
// 按工作区分组(保持 listSessions 中的工作区账本顺序)
|
|
1236
1453
|
const groups = new Map()
|
|
1237
1454
|
for (const s of all) {
|
|
1238
1455
|
const key = s.cwd || '(未指定)'
|
|
1239
|
-
if (!groups.has(key))
|
|
1240
|
-
|
|
1456
|
+
if (!groups.has(key)) {
|
|
1457
|
+
groups.set(key, { title: s.workspaceTitle || getWorkspaceBasename(key), sessions: [] })
|
|
1458
|
+
}
|
|
1459
|
+
groups.get(key).sessions.push(s)
|
|
1241
1460
|
}
|
|
1242
|
-
const sortedGroups = [...groups.entries()].sort((a, b) => String(a[0]).localeCompare(String(b[0])))
|
|
1243
1461
|
const parts = [
|
|
1244
1462
|
`## 📋 会话列表 (共 ${all.length} 个)`,
|
|
1245
1463
|
`> 切换会话:发送 \`/use 编号\` 或 \`/resume 编号\``,
|
|
1246
1464
|
'',
|
|
1247
1465
|
]
|
|
1248
1466
|
let idx = 0
|
|
1249
|
-
for (const [cwd,
|
|
1250
|
-
const groupName = cwd === '(未指定)' ? '📁 未指定工作区' : `📁 **${getWorkspaceBasename(cwd)}**`
|
|
1467
|
+
for (const [cwd, group] of groups) {
|
|
1468
|
+
const groupName = cwd === '(未指定)' ? '📁 未指定工作区' : `📁 **${group.title || getWorkspaceBasename(cwd)}**`
|
|
1251
1469
|
parts.push(groupName)
|
|
1252
1470
|
parts.push('')
|
|
1253
1471
|
parts.push('| 序号 | 会话标题 / 摘要 | 时间 | 状态 |')
|
|
1254
1472
|
parts.push('| :--- | :--- | :--- | :--- |')
|
|
1255
|
-
for (const session of sessions.slice(0, 20)) {
|
|
1473
|
+
for (const session of group.sessions.slice(0, 20)) {
|
|
1256
1474
|
idx += 1
|
|
1257
1475
|
const isActive = session.id === node.activeSessionId
|
|
1258
1476
|
const statusTag = isActive ? '`[当前]`' : '-'
|
|
1259
1477
|
const rawTitle = session.title || (session.events ? sessionLabel(session) : '')
|
|
1260
|
-
const safeTitle = (rawTitle || '新会话
|
|
1478
|
+
const safeTitle = (rawTitle || '新会话').replace(/\|/g, '|').replace(/\r?\n/g, ' ')
|
|
1261
1479
|
const when = session.createdAt ? fmtTime(session.createdAt) : '-'
|
|
1262
1480
|
parts.push(`| **#${idx}** | ${safeTitle} | ${when} | ${statusTag} |`)
|
|
1263
1481
|
}
|
|
1264
|
-
if (sessions.length > 20) {
|
|
1265
|
-
parts.push(`*…该工作区共 ${sessions.length} 个会话,仅显示前 20 个*`)
|
|
1482
|
+
if (group.sessions.length > 20) {
|
|
1483
|
+
parts.push(`*…该工作区共 ${group.sessions.length} 个会话,仅显示前 20 个*`)
|
|
1266
1484
|
}
|
|
1267
1485
|
parts.push('')
|
|
1268
1486
|
}
|
|
@@ -1270,7 +1488,7 @@ async function renderSessions(node) {
|
|
|
1270
1488
|
return parts.join('\n').trim()
|
|
1271
1489
|
}
|
|
1272
1490
|
|
|
1273
|
-
// 与 renderSessions
|
|
1491
|
+
// 与 renderSessions 完全一致的显示顺序:保持 listSessions 中的分组和顺序。
|
|
1274
1492
|
// /use N 用这个数组索引,保证显示的编号 N 与切换的会话一一对应。
|
|
1275
1493
|
function sessionsInDisplayOrder(all) {
|
|
1276
1494
|
const groups = new Map()
|
|
@@ -1279,8 +1497,7 @@ function sessionsInDisplayOrder(all) {
|
|
|
1279
1497
|
if (!groups.has(key)) groups.set(key, [])
|
|
1280
1498
|
groups.get(key).push(s)
|
|
1281
1499
|
}
|
|
1282
|
-
|
|
1283
|
-
return sortedGroups.flatMap(([, sessions]) => sessions)
|
|
1500
|
+
return [...groups.values()].flatMap((sessions) => sessions)
|
|
1284
1501
|
}
|
|
1285
1502
|
|
|
1286
1503
|
// 时间戳 → 简洁可读时间 (MM-DD HH:mm 或 YYYY-MM-DD HH:mm)
|
|
@@ -1343,6 +1560,7 @@ export const conversationBridgeHelpers = {
|
|
|
1343
1560
|
extractFilePathsFromText,
|
|
1344
1561
|
sessionsInDisplayOrder,
|
|
1345
1562
|
listSessions,
|
|
1563
|
+
renderSessions,
|
|
1346
1564
|
listWorkspaces,
|
|
1347
1565
|
BRIDGE_MARK,
|
|
1348
1566
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wenbin_wb/dsh-bridge",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.4",
|
|
4
4
|
"description": "手机扫码即可在移动端/公网继续用 DeepSeek Harness,人不在电脑前也能接着干。一键局域网二维码、Cloudflare 公网隧道、自建隧道与微信 / QQ / 飞书 / Telegram Bot(多工作区/会话持久化/媒体/卡片审批/流式输出),无需自己搭公网服务器。",
|
|
5
|
-
"releaseNotes": "【v2.8.
|
|
5
|
+
"releaseNotes": "【v2.8.4 会话列表 Web 端 1:1 深度对齐与归档过滤修复】\n• 🗂️ 严格按工作区账本对齐会话列表:修复会话列表展示冗余,严格以 DSH 官方工作区账本为准,自动过滤子代理派生会话、空白草稿与已归档会话,与 Web 端侧边栏 1:1 保持一致",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|
|
8
8
|
"exports": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"build:lark": "node scripts/bundle-lark.mjs",
|
|
28
28
|
"build:banner": "node scripts/generate-banner.mjs",
|
|
29
29
|
"prepack": "npm run build:lark && npm run build:client",
|
|
30
|
+
"release:github": "node scripts/create-github-release.mjs",
|
|
30
31
|
"test": "node --test test/*.test.mjs"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|