dsh-crossterm 0.5.0 → 0.6.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/CHANGELOG.md +15 -0
- package/README.md +9 -11
- package/lib/bus.js +1 -1
- package/lib/cloud/layout.js +45 -2
- package/lib/cloud/relay.js +5 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# dsh-crossterm 变更记录
|
|
2
2
|
|
|
3
|
+
## 0.6.0 — 2026-10-19
|
|
4
|
+
|
|
5
|
+
### 共享收件箱里也放一份说明
|
|
6
|
+
- 新常量 `INBOX_README_FILE = '_说明.md'`、`RULES_VERSION = 2`(`lib/cloud/layout.js`),
|
|
7
|
+
`buildPaths()` 增 `inboxReadmeFile`(`<账号>/shared/<设备>/inbox/_说明.md`);
|
|
8
|
+
新增 `inboxReadmeMarkdown()`,`ensureRules()` 一并写入(只在缺失时写,不覆盖)。
|
|
9
|
+
- 说明内容:这个文件夹是什么、谁写谁取(别的设备写信封、本机中转站每 `relayIntervalMs` 取)、
|
|
10
|
+
信封 JSON 格式(`id/from/to/subject/text/files/createdAt`)、注意事项
|
|
11
|
+
(别手工删改信封、`_` 开头的都是系统文件会被跳过、收件人找不到就留件不消费、附件按 `<mailId>/<文件名>` 存)。
|
|
12
|
+
- `lib/cloud/relay.js` 新增并导出 `isEnvelopeFile(name)`:只把 **`.json` 且不以 `_` 开头、非 `.tmp`/隐藏文件**
|
|
13
|
+
当信封;`poll()` 改用它过滤 —— 否则 `_说明.md` 会被当成信封反复解析失败、记 failed。
|
|
14
|
+
- 探针:`probe-layout-rules.mjs` 扩到 **26 项全 PASS**(含三份文档位置与收件箱说明内容);
|
|
15
|
+
新增 `probe-inbox-readme.mjs` **17 项全 PASS**(说明不被当信封、正常信封仍投递并归档到 `done/`、
|
|
16
|
+
失败留件、第二轮不重复投递);`probe-cloud-home.mjs` 断言改为「收件箱只剩系统说明」后 **42 项全 PASS**。
|
|
17
|
+
|
|
3
18
|
## 0.5.0 — 2026-10-19
|
|
4
19
|
|
|
5
20
|
### 云端规则写进云端本身:账号规则放在大文件夹下,设备规则放在账号文件夹下
|
package/README.md
CHANGED
|
@@ -23,17 +23,15 @@ A 终端看不到 B 终端里有哪个会话、在干什么,也没法把一条
|
|
|
23
23
|
| 跨设备 | 经云端总线,在多台机器的 DSH 之间互发邮件与文件 | `term_cloud` |
|
|
24
24
|
| 跨账号 | 预留位:后续用于跨账号互通,当前不挂工具 | — |
|
|
25
25
|
|
|
26
|
-
**本版更新(0.
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- `
|
|
35
|
-
升级后不用手动清 `layout.json`。
|
|
36
|
-
- 探针 `probe-layout-rules.mjs` 23 项全 PASS。
|
|
26
|
+
**本版更新(0.6.0)**
|
|
27
|
+
|
|
28
|
+
- **共享收件箱里也带一份说明**:`/dsh/<账号>/shared/<设备>/inbox/_说明.md` 由插件自动生成,
|
|
29
|
+
打开云盘就知道这个收件箱是干什么的 —— 谁写谁取、信封 JSON 格式、`_` 开头的系统文件会被跳过、
|
|
30
|
+
别手工删改信封、收件人找不到就留件不消费、附件怎么存。
|
|
31
|
+
- `lib/cloud/relay.js` 新增 `isEnvelopeFile()`:只把 `.json` 且不以 `_` 开头、非 `.tmp`/隐藏文件当信封,
|
|
32
|
+
所以说明文件不会被反复解析失败、也不影响正常投递与 `done/` 归档。
|
|
33
|
+
- `RULES_VERSION` 升到 2,升级后命中旧缓存会自动补写这份新说明。
|
|
34
|
+
- 探针:`probe-layout-rules.mjs` 26 项、`probe-inbox-readme.mjs` 17 项、`probe-cloud-home.mjs` 42 项全 PASS。
|
|
37
35
|
|
|
38
36
|
## 一、安装与卸载
|
|
39
37
|
|
package/lib/bus.js
CHANGED
package/lib/cloud/layout.js
CHANGED
|
@@ -12,10 +12,11 @@ export const DEFAULT_HOME = 'dsh'
|
|
|
12
12
|
export const ACCOUNT_FILE = '_account.json'
|
|
13
13
|
export const DEVICE_FILE = '_device.json'
|
|
14
14
|
export const INBOX_FILE = '_inbox.json'
|
|
15
|
-
/**
|
|
15
|
+
/** 规则文档(自动生成,手改会被覆盖):账号规则放大文件夹下,设备规则放账号文件夹下,收件箱说明放共享收件箱里 */
|
|
16
16
|
export const ACCOUNT_RULES_FILE = '_规则-账号.md'
|
|
17
17
|
export const DEVICE_RULES_FILE = '_规则-设备.md'
|
|
18
|
-
export const
|
|
18
|
+
export const INBOX_README_FILE = '_说明.md'
|
|
19
|
+
export const RULES_VERSION = 2
|
|
19
20
|
const CACHE_MS = 10 * 60 * 1000
|
|
20
21
|
|
|
21
22
|
/** 目录名净化:去掉路径分隔符/控制字符/中文等非 ASCII(云盘接口对特殊字符不友好) */
|
|
@@ -48,6 +49,7 @@ export function buildPaths({ home = DEFAULT_HOME, account = 'default', deviceId
|
|
|
48
49
|
inboxFile: `${deviceRoot}/${INBOX_FILE}`,
|
|
49
50
|
accountRulesFile: `${base}/${ACCOUNT_RULES_FILE}`,
|
|
50
51
|
deviceRulesFile: `${accountDir}/${DEVICE_RULES_FILE}`,
|
|
52
|
+
inboxReadmeFile: `${deviceRoot}/inbox/${INBOX_README_FILE}`,
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
|
|
@@ -190,6 +192,46 @@ export function deviceRulesMarkdown({ account = 'default', device = 'device', ro
|
|
|
190
192
|
`
|
|
191
193
|
}
|
|
192
194
|
|
|
195
|
+
/** 云端共享收件箱说明(放在 shared/<设备>/inbox/ 里) */
|
|
196
|
+
export function inboxReadmeMarkdown({ account = 'default', device = 'device', version = '', at = iso() } = {}) {
|
|
197
|
+
return `# 云端共享收件箱 · 说明
|
|
198
|
+
|
|
199
|
+
本文件夹是设备 \`${device}\` 的**云端收件箱**:同账号下别的设备发给它的消息/文件先落在这里,
|
|
200
|
+
再由这台设备的「云端中转站」定时取走,按**收件人(会话名)**投递到本机会话。
|
|
201
|
+
|
|
202
|
+
## 谁写、谁取
|
|
203
|
+
|
|
204
|
+
- **写**:别的设备发信时写一个 \`<mailId>.json\` 信封(附件在 \`../files/<mailId>/\`)。
|
|
205
|
+
- **取**:本机中转站每 \`relayIntervalMs\`(默认 20 s)扫一次,投递成功后把信封搬去 \`../done/\`,再删这里的原件。
|
|
206
|
+
|
|
207
|
+
## 信封格式
|
|
208
|
+
|
|
209
|
+
\`\`\`json
|
|
210
|
+
{
|
|
211
|
+
"id": "m-<时间戳>-<随机>",
|
|
212
|
+
"from": { "nodeId": "…", "name": "…" },
|
|
213
|
+
"to": "<会话名 或 完整 sessionId 或 8 位短号>",
|
|
214
|
+
"subject": "主题",
|
|
215
|
+
"text": "正文",
|
|
216
|
+
"files": [{ "name": "文件名", "size": 1234 }],
|
|
217
|
+
"createdAt": 1792385549000
|
|
218
|
+
}
|
|
219
|
+
\`\`\`
|
|
220
|
+
|
|
221
|
+
## 注意
|
|
222
|
+
|
|
223
|
+
- **别手工删/改** \`*.json\` 信封:投递与否以本机状态为准,删掉就等于丢信。
|
|
224
|
+
- 以 \`_\` 开头的文件(本说明、\`_inbox.json\` 等)是系统文件,中转站会跳过,不当作信封。
|
|
225
|
+
- 收件人找不到(会话不存在)时信封**留在云端不消费**,不会丢;本机把会话打开/建好后下一轮自动投递。
|
|
226
|
+
- 附件按 \`<mailId>/<文件名>\` 存放;重名以云端最后写入为准。
|
|
227
|
+
- 想从别的设备发信:\`term_cloud action="send" toDevice="<设备>" to="<会话名>" text="…"\`。
|
|
228
|
+
|
|
229
|
+
## 版本
|
|
230
|
+
|
|
231
|
+
规则版本 v${RULES_VERSION} · 设备 \`${device}\` · 账号 \`${account}\` · 由插件${version ? ` ${version}` : ''} 于 ${at} 生成
|
|
232
|
+
`
|
|
233
|
+
}
|
|
234
|
+
|
|
193
235
|
/** 只在缺失时写规则文档;失败只记日志,不影响目录建立 */
|
|
194
236
|
export async function ensureRules(pan, paths, info = {}) {
|
|
195
237
|
const { log = () => {}, account = 'default', device = 'device', role = 'member', version = '' } = info
|
|
@@ -198,6 +240,7 @@ export async function ensureRules(pan, paths, info = {}) {
|
|
|
198
240
|
[paths.accountRulesFile, accountRulesMarkdown({ account, device, version })],
|
|
199
241
|
[paths.deviceRulesFile, deviceRulesMarkdown({ account, device, role, version })],
|
|
200
242
|
]
|
|
243
|
+
if (paths.inboxReadmeFile) docs.push([paths.inboxReadmeFile, inboxReadmeMarkdown({ account, device, version })])
|
|
201
244
|
const written = []
|
|
202
245
|
for (const [remote, text] of docs) {
|
|
203
246
|
try {
|
package/lib/cloud/relay.js
CHANGED
|
@@ -28,6 +28,10 @@ function saveState(file, state) {
|
|
|
28
28
|
|
|
29
29
|
const isTmp = (name) => /\.tmp$/.test(name) || name.startsWith('.')
|
|
30
30
|
|
|
31
|
+
/** 只处理信封 json:跳过 .tmp / 隐藏文件 / 以 _ 开头的系统说明(如 _说明.md、_inbox.json) */
|
|
32
|
+
export const isEnvelopeFile = (name) =>
|
|
33
|
+
/\.json$/i.test(name) && !isTmp(name) && !String(name).startsWith('_')
|
|
34
|
+
|
|
31
35
|
function parseEnvelope(text) {
|
|
32
36
|
const j = JSON.parse(text)
|
|
33
37
|
const to = Array.isArray(j.to) ? j.to : j.to || j.toSession || j.recipient || ''
|
|
@@ -84,7 +88,7 @@ export function createRelay({ pan, getLayout, localDir, stateFile, deliver, log
|
|
|
84
88
|
stats.at = Date.now()
|
|
85
89
|
return { ...stats, error: `列收件箱失败 errno=${list.errno}` }
|
|
86
90
|
}
|
|
87
|
-
const files = (list.list || []).filter((f) => f.isdir === 0 &&
|
|
91
|
+
const files = (list.list || []).filter((f) => f.isdir === 0 && isEnvelopeFile(f.server_filename))
|
|
88
92
|
stats.scanned = files.length
|
|
89
93
|
stats.delivered = 0
|
|
90
94
|
stats.failed = 0
|