weaver-work-cli 0.1.38 → 0.1.40
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.md +7 -3
- package/dist/chunks/{archive-PDV724YK.js → archive-JHTONRST.js} +556 -66
- package/dist/chunks/{archive-oa-4XE4MNXQ.js → archive-oa-FCHLCPHC.js} +2 -2
- package/dist/chunks/{asset-PDL2QE2N.js → asset-RLIHC7DB.js} +2 -2
- package/dist/chunks/asset-idle-KVOFTLCP.js +670 -0
- package/dist/chunks/{attachment-content-MDDGOE5E.js → attachment-content-RZYDB3MB.js} +3 -3
- package/dist/chunks/{blog-UYR3EUXW.js → blog-42OZTJNP.js} +2 -2
- package/dist/chunks/{caizhilian-LG2X5NMF.js → caizhilian-KRK547K5.js} +2 -2
- package/dist/chunks/{calendar-FG4BAZHE.js → calendar-RTWAMKHD.js} +2 -2
- package/dist/chunks/{chunk-J7USS3ED.js → chunk-4CTESSKI.js} +4 -4
- package/dist/chunks/{chunk-OJQUJZQF.js → chunk-4TEDNCON.js} +45 -5
- package/dist/chunks/chunk-57IECMWX.js +487 -0
- package/dist/chunks/{chunk-ELYDWBZJ.js → chunk-7BRU4EFH.js} +13 -7
- package/dist/chunks/{chunk-6LTFIFLM.js → chunk-7E6MUOEK.js} +2 -2
- package/dist/chunks/{chunk-KKRL3REV.js → chunk-7U577Q7U.js} +2 -2
- package/dist/chunks/{chunk-UJJD4QWF.js → chunk-CAQNBBQA.js} +2 -2
- package/dist/chunks/{chunk-CORFRD7Q.js → chunk-CXRQWOZ5.js} +2 -2
- package/dist/chunks/{chunk-GGJFMNJM.js → chunk-FLS4DPGP.js} +110 -16
- package/dist/chunks/{chunk-JSMFPO43.js → chunk-I566ZYNF.js} +1 -1
- package/dist/chunks/{chunk-6FTTSA45.js → chunk-IZSSVYAG.js} +2 -2
- package/dist/chunks/{chunk-XJY7G4DD.js → chunk-JWRZM2H2.js} +3 -3
- package/dist/chunks/{chunk-NI6GIPIM.js → chunk-LFMSDBZN.js} +529 -395
- package/dist/chunks/{chunk-VRYHGG6O.js → chunk-LN447PY2.js} +92 -51
- package/dist/chunks/{chunk-NR6RKXPA.js → chunk-NETCCARD.js} +4 -2
- package/dist/chunks/{chunk-Z32KWY6G.js → chunk-NJKREDA3.js} +1 -1
- package/dist/chunks/{chunk-FUWVHMVV.js → chunk-SPRHHSVC.js} +2 -2
- package/dist/chunks/{chunk-IZUDA353.js → chunk-T6EGVBKU.js} +1 -1
- package/dist/chunks/{chunk-DMV2TOMH.js → chunk-VNBU55SL.js} +428 -145
- package/dist/chunks/{chunk-UZZBA6PV.js → chunk-WFFNM65B.js} +47 -6
- package/dist/chunks/{chunk-EG6ZU444.js → chunk-ZUX3P6BQ.js} +10 -5
- package/dist/chunks/{conflict-BQZEQ4SP.js → conflict-VR55AOOO.js} +2 -2
- package/dist/chunks/{contract-XIRAFTEV.js → contract-HI4FNPYE.js} +2 -2
- package/dist/chunks/{cowork-BLG62BL2.js → cowork-HJLI353D.js} +12 -5
- package/dist/chunks/{ebuilder-form-NUKG5QW4.js → ebuilder-form-HTGI63UU.js} +3 -3
- package/dist/chunks/{ehr-2STW5ILH.js → ehr-GYXIYH3A.js} +2 -2
- package/dist/chunks/env-DW3G4HUO.js +19 -0
- package/dist/chunks/{esb-UUDQUNKY.js → esb-I5INCEFE.js} +4 -4
- package/dist/chunks/factory-TKXGXS6L.js +30 -0
- package/dist/chunks/{fna-6RCY4NU5.js → fna-K2MHTQ24.js} +2 -2
- package/dist/chunks/group-chat-VYBNMJLG.js +18 -0
- package/dist/chunks/{host-FWDOSRH5.js → host-256GVCAH.js} +3 -3
- package/dist/chunks/{host-EJZFROU3.js → host-D5SERMAJ.js} +3 -3
- package/dist/chunks/{host-RHM3NPXV.js → host-JM4YVHFM.js} +3 -3
- package/dist/chunks/{host-7RXEDMAH.js → host-L3VLITMT.js} +3 -3
- package/dist/chunks/{host-UFF33TWJ.js → host-ZUQTJYYK.js} +3 -3
- package/dist/chunks/{hrm-PKJ6ELSH.js → hrm-VLRBKTZB.js} +3 -3
- package/dist/chunks/{im-YJAXS7CO.js → im-QK3VTLXK.js} +8 -8
- package/dist/chunks/{invoice-HWVSXGX6.js → invoice-T5JTA32H.js} +3 -3
- package/dist/chunks/{jiuchuanhui-L7VKILZB.js → jiuchuanhui-P2FNEQVZ.js} +2 -2
- package/dist/chunks/{lazy-dispatch-NMYEIHRH.js → lazy-dispatch-XRRI46M6.js} +5 -5
- package/dist/chunks/{mail-B42RRU7G.js → mail-3Y7MJ6PS.js} +9 -4
- package/dist/chunks/{meeting-CFYVBJSS.js → meeting-M7LTXGJE.js} +11 -10
- package/dist/chunks/{okr-GCLF6V7W.js → okr-OBC6S67L.js} +2 -2
- package/dist/chunks/{plan-ZVL7GBRI.js → plan-5H6I53SK.js} +2 -2
- package/dist/chunks/{project-4YWAAE2K.js → project-L75QZV4S.js} +2 -2
- package/dist/chunks/{registry-EP4VTHZS.js → registry-KMHCPAAD.js} +9 -8
- package/dist/chunks/{registry-OZSMDVPP.js → registry-QGHFNUWS.js} +2 -2
- package/dist/chunks/{registry-FRJ6H7BT.js → registry-RXPN6BBT.js} +4 -4
- package/dist/chunks/{registry-SG2PGVCF.js → registry-UGRBCBHO.js} +3 -3
- package/dist/chunks/{room-recommend-R3JVUUAC.js → room-recommend-A3UDC3CA.js} +4 -4
- package/dist/chunks/{schedule-NIJBTYDO.js → schedule-5GCACCK7.js} +4 -4
- package/dist/chunks/{workflow-BB24DFBY.js → workflow-M5ZJ7DWF.js} +11 -8
- package/dist/core/output.js +16 -6
- package/dist/index.js +33 -28
- package/dist/internal/e10/auth/commands.js +109 -56
- package/dist/internal/e10/auth/messages.js +1 -0
- package/dist/internal/e10/auth/session.js +11 -1
- package/dist/internal/e10/auth/xiaoe.js +29 -4
- package/dist/internal/e10/auth-recovery.js +13 -6
- package/dist/internal/e10/client.js +13 -0
- package/dist/internal/e10/context.js +24 -3
- package/dist/internal/e10/request-runtime.js +12 -1
- package/dist/shortcuts/archive/host.js +2 -0
- package/dist/shortcuts/archive/manifest.js +1 -1
- package/dist/shortcuts/archive/operations/borrow-list.js +13 -1
- package/dist/shortcuts/archive/operations/flow-url.js +1 -1
- package/dist/shortcuts/archive/operations/prelib.js +472 -0
- package/dist/shortcuts/archive/operations/registry.js +2 -0
- package/dist/shortcuts/archive/operations/upload.js +1 -1
- package/dist/shortcuts/asset-idle/errors.js +44 -0
- package/dist/shortcuts/asset-idle/host.js +161 -0
- package/dist/shortcuts/asset-idle/index.js +114 -0
- package/dist/shortcuts/asset-idle/manifest.js +159 -0
- package/dist/shortcuts/asset-idle/operations/idle.js +47 -0
- package/dist/shortcuts/asset-idle/operations/registry.js +33 -0
- package/dist/shortcuts/asset-idle/operations/shared.js +142 -0
- package/dist/shortcuts/asset-idle/operations/types.js +9 -0
- package/dist/shortcuts/asset-idle/operations.js +15 -0
- package/dist/shortcuts/cowork/operations/item-write.js +10 -2
- package/dist/shortcuts/index.js +5 -0
- package/dist/shortcuts/mail/operations/mail-send.js +8 -2
- package/dist/shortcuts/meeting/operations/eb/catalog.js +5 -1
- package/dist/shortcuts/meeting/operations/eb/create-flow.js +381 -104
- package/dist/shortcuts/meeting/operations/eb/crm-search.js +87 -5
- package/dist/shortcuts/meeting/operations/eb/flow-memory.js +49 -0
- package/dist/shortcuts/meeting/operations/eb/group-chat.js +583 -0
- package/dist/shortcuts/meeting/operations/eb/index.js +2 -0
- package/dist/shortcuts/meeting/operations/eb/room-recommend.js +19 -3
- package/dist/shortcuts/meeting/operations/eb/room-summary.js +2 -0
- package/dist/shortcuts/meeting/operations/eb/usage-cache.js +99 -0
- package/dist/shortcuts/meeting/operations/from-group-chat.js +35 -12
- package/dist/shortcuts/workflow/host.js +3 -0
- package/dist/shortcuts/workflow/index.js +1 -1
- package/dist/shortcuts/workflow/operations/form.js +65 -171
- package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupRules.js +129 -27
- package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupStore.js +6 -0
- package/dist/shortcuts/workflow/operations/todoSmartConvert/summaryCache.js +60 -117
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailCache.js +44 -7
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailFetcher.js +37 -29
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +48 -8
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +1 -0
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.detail.js +22 -18
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +17 -9
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +34 -10
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +5 -2
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.smartGroup.js +9 -2
- package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +122 -20
- package/docs/_catalog.md +3 -0
- package/docs/asset-idle.md +45 -0
- package/docs/e10-auth.md +16 -1
- package/docs/todo-smart-convert/subgroup-rules.md +1 -1
- package/package.json +1 -1
- package/scripts/mcp-meeting.mjs +234 -0
- package/scripts/package-skill.mjs +2 -0
- package/skill-template/business-info.json +14 -1
- package/skill-template/domains/asset-idle.md +13 -0
- package/skill-template/domains/wenshuding.md +3 -0
- package/skills/weaver-e10-archive-oa/SKILL.md +1 -0
- package/skills/weaver-e10-archive-oa/references/my-borrow.md +49 -0
- package/skills/weaver-e10-asset-idle/SKILL.md +73 -0
- package/skills/weaver-e10-asset-idle/references/idle-list.md +62 -0
- package/skills/weaver-e10-asset-idle/references/idle-stats-and-charts.md +65 -0
- package/skills/weaver-e10-asset-idle/references/source-manifest.json +193 -0
- package/skills/weaver-e10-asset-idle/references/widget-style.md +205 -0
- package/skills/weaver-e10-caizhilian/SKILL.md +29 -11
- package/skills/weaver-e10-caizhilian/references/auth-and-install.md +12 -0
- package/skills/weaver-e10-caizhilian/references/files.md +1 -1
- package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +1 -1
- package/skills/weaver-e10-caizhilian/references/source-manifest.json +45 -45
- package/skills/weaver-e10-cowork/SKILL.md +2 -0
- package/skills/weaver-e10-cowork/references/cowork-write.md +1 -1
- package/skills/weaver-e10-jucailin/SKILL.md +2 -0
- package/skills/weaver-e10-jucailin/references/recruit-read.md +2 -0
- package/skills/weaver-e10-jucailin/references/recruit-write.md +1 -1
- package/skills/weaver-e10-mail/SKILL.md +3 -0
- package/skills/weaver-e10-mail/references/mail-send.md +2 -0
- package/skills/weaver-e10-meeting/SKILL.md +88 -25
- package/skills/weaver-e10-meeting/references/meeting-minute-file.md +25 -11
- package/skills/weaver-e10-meeting.zip +0 -0
- package/skills/weaver-e10-shared/SKILL.md +5 -5
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +3 -3
- package/skills/weaver-e10-wenshuding/SKILL.md +29 -6
- package/skills/weaver-e10-wenshuding/references/archive-borrow-list.md +14 -0
- package/skills/weaver-e10-wenshuding/references/archive-download.md +2 -0
- package/skills/weaver-e10-wenshuding/references/archive-flow.md +1 -1
- package/skills/weaver-e10-wenshuding/references/archive-prelib-arrange.md +76 -0
- package/skills/weaver-e10-wenshuding/references/archive-prelib-list.md +107 -0
- package/skills/weaver-e10-wenshuding/references/archive-prelib-open.md +59 -0
- package/skills/weaver-e10-wenshuding/references/archive-upload.md +4 -1
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +108 -65
- package/skills/weaver-e10-workflow/SKILL.md +9 -2
- package/skills/weaver-e10-workflow/aigroup/default.json +166 -19
- package/skills/weaver-e10-workflow/references/todo-smart-convert.md +72 -34
- package/skills/weaver-e10-workflow/tools/todo-serve.cjs +113 -25
- package/skills/weaver-e10-workflow/tools/todo-summary.cjs +350 -50
- package/dist/chunks/{chunk-2LV74IWP.js → chunk-E7OK5WD5.js} +5 -5
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* todo-serve.cjs —— 待办汇总载荷的本地 GET
|
|
3
|
+
* todo-serve.cjs —— 待办汇总载荷的本地 GET 数据源(v4:内存优先,2026-10-30)。
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* v4 变化(为什么必须改):
|
|
6
|
+
* 宿主沙箱对「脱离父命令的常驻进程」的文件读授权,只在其启动命令存活期间有效——
|
|
7
|
+
* 命令一结束,服务就读不了任何快照文件(能 stat、不能 read ⇒ GET 404 snapshot_empty),
|
|
8
|
+
* 用户过几分钟再打开链接必 404。所以 v4 改为:**快照正文由 CLI 通过 POST 推进本进程内存,
|
|
9
|
+
* GET 从内存返回,服务端不再读任何磁盘文件**。
|
|
7
10
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
+
* 数据通路:
|
|
12
|
+
* POST /s/<token>/todo.json -> CLI 发布:body 即三键 JSON 原文,存入内存(须带 x-publish-key)
|
|
13
|
+
* GET /s/<token>/todo.json -> 内存优先,miss 再回落读磁盘快照(兼容旧文件)
|
|
14
|
+
* GET /todo.json?token=<t> -> 同上(兼容旧链接形态)
|
|
15
|
+
* GET /health -> 存活信息(inMemory = 内存快照数)
|
|
11
16
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* - 快照各自按目录 mtime 计空闲,超时即删(请求命中过期快照也会顺手删掉)。
|
|
18
|
-
* - 所有快照都过期且全局无请求超过 idle 分钟 -> 进程退出并删 current.json。
|
|
19
|
-
*
|
|
20
|
-
* 为什么必须自己写这个服务(而不是 python -m http.server / http-server):
|
|
21
|
-
* https 页面取 http://127.0.0.1 会先发 OPTIONS 私有网络预检,
|
|
22
|
-
* 必须回 `Access-Control-Allow-Private-Network: true` 且回显 Origin,否则浏览器直接拦
|
|
23
|
-
* (python 的 http.server 对 OPTIONS 直接 501)。
|
|
17
|
+
* 兼容与安全:
|
|
18
|
+
* - x-publish-key:启动时随机生成、写入 current.json 给 CLI;发布必须携带,防止拿到 URL 的人伪造发布;
|
|
19
|
+
* - 内存快照上限 20 份(LRU 淘汰)、单份 ≤ 8MB;
|
|
20
|
+
* - 服务收到正文后仍 best-effort 写一份磁盘副本(只写不读,排错用);
|
|
21
|
+
* - 旧版「CLI 写文件、服务读文件」的路径保留为回落,但不再是主通路。
|
|
24
22
|
*/
|
|
25
23
|
const http = require('node:http');
|
|
24
|
+
const crypto = require('node:crypto');
|
|
26
25
|
const fs = require('node:fs');
|
|
27
26
|
const os = require('node:os');
|
|
28
27
|
const path = require('node:path');
|
|
@@ -41,7 +40,7 @@ function parseArgs(argv) {
|
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
/** 与服务端约定一致的版本号:客户端只复用同版本实例。 */
|
|
44
|
-
const SERVE_VERSION = '
|
|
43
|
+
const SERVE_VERSION = '4';
|
|
45
44
|
|
|
46
45
|
const args = parseArgs(process.argv.slice(2));
|
|
47
46
|
// 默认放系统临时目录:快照只有 30 分钟寿命,本就不该写进家目录;
|
|
@@ -58,6 +57,12 @@ const allowOrigin = args['allow-origin'] && args['allow-origin'] !== true ? Stri
|
|
|
58
57
|
// token 只允许十六进制,避免 `../` 之类的目录穿越
|
|
59
58
|
const TOKEN_RE = /^[a-f0-9]{8,64}$/;
|
|
60
59
|
const IDLE_MS = idleMinutes * 60000;
|
|
60
|
+
/** 发布密钥:启动时随机生成,写进 current.json 给 CLI;发布请求必须带它(防伪造发布)。 */
|
|
61
|
+
const publishKey = crypto.randomBytes(8).toString('hex');
|
|
62
|
+
/** 内存快照:token -> { body, createdAt, bytes }。GET 的主来源,服务端不再读磁盘。 */
|
|
63
|
+
const memorySnapshots = new Map();
|
|
64
|
+
const MAX_MEMORY_SNAPSHOTS = 20;
|
|
65
|
+
const MAX_BODY_BYTES = 8 * 1024 * 1024;
|
|
61
66
|
|
|
62
67
|
fs.mkdirSync(snapshotsDir, { recursive: true });
|
|
63
68
|
|
|
@@ -82,9 +87,14 @@ function dropSnapshot(dir) {
|
|
|
82
87
|
} catch { /* 忽略 */ }
|
|
83
88
|
}
|
|
84
89
|
|
|
85
|
-
/**
|
|
90
|
+
/** 读一份快照:**内存优先**,miss 再回落磁盘文件(兼容旧路径)。过期都返回 404。 */
|
|
86
91
|
function readSnapshot(token) {
|
|
87
92
|
if (!TOKEN_RE.test(String(token || ''))) return { status: 400, error: 'invalid_token' };
|
|
93
|
+
const mem = memorySnapshots.get(token);
|
|
94
|
+
if (mem) {
|
|
95
|
+
mem.createdAt = Date.now(); // 命中即续期
|
|
96
|
+
return { status: 200, body: mem.body };
|
|
97
|
+
}
|
|
88
98
|
const dir = path.join(snapshotsDir, token);
|
|
89
99
|
const file = path.join(dir, 'todo.json');
|
|
90
100
|
let mtime = 0;
|
|
@@ -107,13 +117,17 @@ function readSnapshot(token) {
|
|
|
107
117
|
return { status: 200, body };
|
|
108
118
|
}
|
|
109
119
|
|
|
110
|
-
/**
|
|
120
|
+
/** 清掉过期快照(内存 + 磁盘),返回仍在有效期内的数量。 */
|
|
111
121
|
function sweepSnapshots() {
|
|
122
|
+
const now = Date.now();
|
|
123
|
+
for (const [token, mem] of memorySnapshots) {
|
|
124
|
+
if (now - mem.createdAt > IDLE_MS) memorySnapshots.delete(token);
|
|
125
|
+
}
|
|
112
126
|
let names = [];
|
|
113
127
|
try {
|
|
114
128
|
names = fs.readdirSync(snapshotsDir);
|
|
115
129
|
} catch {
|
|
116
|
-
|
|
130
|
+
names = [];
|
|
117
131
|
}
|
|
118
132
|
let alive = 0;
|
|
119
133
|
for (const name of names) {
|
|
@@ -127,14 +141,14 @@ function sweepSnapshots() {
|
|
|
127
141
|
if (Date.now() - mtime > IDLE_MS) dropSnapshot(dir);
|
|
128
142
|
else alive += 1;
|
|
129
143
|
}
|
|
130
|
-
return alive;
|
|
144
|
+
return alive + memorySnapshots.size;
|
|
131
145
|
}
|
|
132
146
|
|
|
133
147
|
function corsHeaders(req) {
|
|
134
148
|
const origin = allowOrigin || req.headers.origin || '*';
|
|
135
149
|
return {
|
|
136
150
|
'Access-Control-Allow-Origin': origin,
|
|
137
|
-
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
|
151
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
138
152
|
'Access-Control-Allow-Headers': req.headers['access-control-request-headers'] || '*',
|
|
139
153
|
'Access-Control-Allow-Private-Network': 'true',
|
|
140
154
|
'Access-Control-Max-Age': '600',
|
|
@@ -147,6 +161,36 @@ function send(res, status, headers, body) {
|
|
|
147
161
|
res.end(body);
|
|
148
162
|
}
|
|
149
163
|
|
|
164
|
+
/** 读取请求体(有 8MB 上限,超限返回 null)。 */
|
|
165
|
+
function readBody(req, onDone) {
|
|
166
|
+
const chunks = [];
|
|
167
|
+
let size = 0;
|
|
168
|
+
let aborted = false;
|
|
169
|
+
req.on('data', (chunk) => {
|
|
170
|
+
if (aborted) return;
|
|
171
|
+
size += chunk.length;
|
|
172
|
+
if (size > MAX_BODY_BYTES) {
|
|
173
|
+
aborted = true;
|
|
174
|
+
onDone(null);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
chunks.push(chunk);
|
|
178
|
+
});
|
|
179
|
+
req.on('end', () => { if (!aborted) onDone(Buffer.concat(chunks).toString('utf8')); });
|
|
180
|
+
req.on('error', () => { if (!aborted) onDone(null); });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** 内存快照:存入并按 LRU 淘汰最旧的,返回是否成功。 */
|
|
184
|
+
function putMemorySnapshot(token, body) {
|
|
185
|
+
memorySnapshots.set(token, { body, createdAt: Date.now(), bytes: Buffer.byteLength(body) });
|
|
186
|
+
while (memorySnapshots.size > MAX_MEMORY_SNAPSHOTS) {
|
|
187
|
+
const oldest = [...memorySnapshots.entries()].sort((a, b) => a[1].createdAt - b[1].createdAt)[0];
|
|
188
|
+
if (!oldest) break;
|
|
189
|
+
memorySnapshots.delete(oldest[0]);
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
|
|
150
194
|
const server = http.createServer((req, res) => {
|
|
151
195
|
lastHit = Date.now();
|
|
152
196
|
const url = new URL(req.url || '/', 'http://127.0.0.1');
|
|
@@ -157,6 +201,48 @@ const server = http.createServer((req, res) => {
|
|
|
157
201
|
res.end();
|
|
158
202
|
return;
|
|
159
203
|
}
|
|
204
|
+
|
|
205
|
+
// 发布端点:CLI 把三键 JSON 原文 POST 进内存(必须带 x-publish-key)。
|
|
206
|
+
// 这是 v4 的主通路——服务端从此不依赖「读磁盘」就能服务 GET。
|
|
207
|
+
let publishMatch = null;
|
|
208
|
+
if (req.method === 'POST') {
|
|
209
|
+
publishMatch = url.pathname.match(/^\/s\/([^/]+)\/todo\.json$/);
|
|
210
|
+
if (!publishMatch) {
|
|
211
|
+
send(res, 404, headers, JSON.stringify({ ok: false, error: 'not_found' }));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const token = publishMatch[1];
|
|
215
|
+
if (!TOKEN_RE.test(token)) {
|
|
216
|
+
send(res, 400, headers, JSON.stringify({ ok: false, error: 'invalid_token' }));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (String(req.headers['x-publish-key'] || '') !== publishKey) {
|
|
220
|
+
send(res, 403, headers, JSON.stringify({ ok: false, error: 'forbidden' }));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
readBody(req, (body) => {
|
|
224
|
+
if (body === null) {
|
|
225
|
+
send(res, 413, headers, JSON.stringify({ ok: false, error: 'payload_too_large' }));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
try { JSON.parse(body); } catch {
|
|
229
|
+
send(res, 400, headers, JSON.stringify({ ok: false, error: 'invalid_json' }));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
putMemorySnapshot(token, body);
|
|
233
|
+
// best-effort 落一份磁盘副本:只写不读,排错/人工恢复用
|
|
234
|
+
try {
|
|
235
|
+
const dir = path.join(snapshotsDir, token);
|
|
236
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
237
|
+
fs.writeFileSync(path.join(dir, 'todo.json'), body);
|
|
238
|
+
} catch { /* 写不了也不影响内存通路 */ }
|
|
239
|
+
send(res, 200, { ...headers, 'Cache-Control': 'no-store' }, JSON.stringify({
|
|
240
|
+
ok: true, token, bytes: Buffer.byteLength(body), inMemory: memorySnapshots.size,
|
|
241
|
+
}));
|
|
242
|
+
});
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
160
246
|
if (req.method !== 'GET') {
|
|
161
247
|
send(res, 405, headers, JSON.stringify({ ok: false, error: 'method_not_allowed' }));
|
|
162
248
|
return;
|
|
@@ -170,6 +256,7 @@ const server = http.createServer((req, res) => {
|
|
|
170
256
|
pid: process.pid,
|
|
171
257
|
port: address ? address.port : port,
|
|
172
258
|
snapshots: alive,
|
|
259
|
+
inMemory: memorySnapshots.size,
|
|
173
260
|
idleMinutes,
|
|
174
261
|
hits,
|
|
175
262
|
}));
|
|
@@ -216,8 +303,9 @@ server.listen(port, '127.0.0.1', () => {
|
|
|
216
303
|
root,
|
|
217
304
|
startedAt: new Date().toISOString(),
|
|
218
305
|
idleMinutes,
|
|
306
|
+
publishKey,
|
|
219
307
|
}, null, 2));
|
|
220
|
-
console.log('[todo-serve] listening http://127.0.0.1:%d/s/<token>/todo.json|空闲 %d 分钟自动退出|pid=%d', actualPort, idleMinutes, process.pid);
|
|
308
|
+
console.log('[todo-serve] listening http://127.0.0.1:%d/s/<token>/todo.json|内存优先版 v%s|空闲 %d 分钟自动退出|pid=%d', actualPort, SERVE_VERSION, idleMinutes, process.pid);
|
|
221
309
|
});
|
|
222
310
|
|
|
223
311
|
function cleanup(code) {
|