weflow-cli 1.14.0 → 1.14.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/CHANGELOG.md CHANGED
@@ -17,6 +17,22 @@ purpose** - a hand-written "latest is X" is exactly the claim that goes stale he
17
17
 
18
18
  All notable user-facing changes are recorded here. This project follows [Semantic Versioning](https://semver.org/).
19
19
 
20
+ ## 1.14.1
21
+
22
+ - **The daily run now keeps the retrieval indexes fresh.** On 2026-10-22 the semantic index was
23
+ still at 10-15 and the phrase index at 10-17, so `search` could not find anything newer than a
24
+ week ago - nothing had ever refreshed them. After the day's report is written, the wrapper runs
25
+ `semantic_search.py update` (embeds only what is missing: 4,061 new records on that first run)
26
+ and `semantic_search.py phrase-build` (local FTS, no API cost). Each has its own log and neither
27
+ can fail the day.
28
+ - **The noon pass mails too, and says it is half a day.** It used to generate silently, which read
29
+ as "it never ran" - the only evidence was a log file. The noon email is titled
30
+ 「…(截至中午)」 and states that the complete version arrives the next morning, so the two daily
31
+ emails cannot be mistaken for duplicates.
32
+ - **`phrase-build` no longer fails when `VACUUM` cannot run.** `VACUUM` needs a temp file, and in a
33
+ restricted environment it reports `unable to open database file` *after* the index has been
34
+ written successfully - that turned a good rebuild into exit code 1.
35
+
20
36
  ## 1.14.0
21
37
 
22
38
  - **A daily report you can receive by mail, cut down to the topics you actually read.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weflow-cli",
3
- "version": "1.14.0",
3
+ "version": "1.14.1",
4
4
  "description": "让 AI 读懂你的微信 — 公众号日报·聊天导出·知识库搜索·MCP Server",
5
5
  "type": "module",
6
6
  "bin": {
@@ -423,6 +423,7 @@ def main():
423
423
  ap.add_argument('--date', required=True, help='YYYY-MM-DD')
424
424
  ap.add_argument('--to', default='', help='收件人;缺省用配置 dailyMailTo,再缺省发给自己')
425
425
  ap.add_argument('--topics', default=','.join(DEFAULT_TOPICS), help='逗号分隔的主题')
426
+ ap.add_argument('--label', default='', help='标题后缀,如「截至中午」——区分半日速览与次日完整版')
426
427
  ap.add_argument('--no-digest', action='store_true', help='不生成每主题的 AI 综述(省钱、更快)')
427
428
  ap.add_argument('--dry-run', action='store_true', help='只渲染,不发送')
428
429
  args = ap.parse_args()
@@ -449,7 +450,11 @@ def main():
449
450
  print(f'正文含 GitHub 链接: {with_repo} 篇')
450
451
 
451
452
  text_body, html_body, total = render(args.date, groups, digests, bodies, clusters)
452
- subject = f'公众号日报 {args.date}:{" / ".join(t for t in topics if groups.get(t))} {total} 篇'
453
+ label = (args.label or '').strip()
454
+ suffix = f'({label})' if label else ''
455
+ subject = f'公众号日报 {args.date}{suffix}:{" / ".join(t for t in topics if groups.get(t))} {total} 篇'
456
+ if label:
457
+ text_body = f'【{label}】以下是到此刻为止收到的 {total} 篇。完整版会在次日凌晨按整天重发。\n\n' + text_body
453
458
 
454
459
  user, auth = smtp_settings()
455
460
  to = args.to or str(cfg.get('dailyMailTo') or '') or user
@@ -128,18 +128,43 @@ if ($proc.ExitCode -eq 0 -and $rest.Count -eq 0) {
128
128
  }
129
129
  }
130
130
 
131
- # Mail is a separate step on purpose: the day's product is already on disk, so a mail
132
- # failure must not turn a successful run into a failed one (and must not stop the
133
- # noon marker from being written). It has its own log pair.
134
- #
135
- # **Only the day pass mails.** The noon pass is a half day, so mailing it too means two
136
- # emails a day where the second one repeats the first - the user asked for one email,
137
- # and the complete day is the one worth reading (2026-10-20).
131
+ # Retrieval indexes lag behind unless something refreshes them: on 2026-10-22 the
132
+ # semantic index was still at 10-15 (7 days stale) and the phrase index at 10-17, so
133
+ # `search` simply could not find anything newer. Both run here, after the day is on
134
+ # disk, in their own logs -- a failure is recorded and never fails the day.
135
+ $searchScript = Join-Path $PSScriptRoot 'semantic_search.py'
136
+ if ($mode -eq 'day' -and (Test-Path $searchScript)) {
137
+ $idxOut = Join-Path $logDir ('index-{0}.log' -f $stamp)
138
+ $idxErr = Join-Path $logDir ('index-{0}.err.log' -f $stamp)
139
+ # `update` only embeds what is not in the index yet (4,061 new records on the first
140
+ # run after a week). `phrase-build` is local FTS, no API, so it rebuilds in full.
141
+ $idxProc = Start-Process -FilePath $python `
142
+ -ArgumentList @($searchScript, 'update', '--article-days', '30') `
143
+ -NoNewWindow -Wait -PassThru -RedirectStandardOutput $idxOut -RedirectStandardError $idxErr
144
+ $phraseProc = Start-Process -FilePath $python `
145
+ -ArgumentList @($searchScript, 'phrase-build') `
146
+ -NoNewWindow -Wait -PassThru `
147
+ -RedirectStandardOutput (Join-Path $logDir ('phrase-{0}.log' -f $stamp)) `
148
+ -RedirectStandardError (Join-Path $logDir ('phrase-{0}.err.log' -f $stamp))
149
+ if ($idxProc.ExitCode -ne 0 -or $phraseProc.ExitCode -ne 0) {
150
+ Add-Content -Path (Join-Path $logDir 'daily-task.log') -Value `
151
+ ('===== {0} index update failed (update={1}, phrase={2}), see {3} =====' -f `
152
+ (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $idxProc.ExitCode, $phraseProc.ExitCode, $idxErr) -Encoding UTF8
153
+ }
154
+ }
155
+
156
+ # **Both passes mail now.** The noon pass used to be silent, which read as "it never
157
+ # ran" - the only way to tell it had done anything was to open the log. The two emails
158
+ # are clearly different: the noon one is labelled 截至中午 and says the complete version
159
+ # follows next morning. Mail is still its own step with its own log pair, so a mail
160
+ # failure never fails the day nor blocks the noon marker.
138
161
  $mailScript = Join-Path $PSScriptRoot 'daily_mail.py'
139
162
  $mailOut = Join-Path $logDir ('daily-mail-{0}{1}.log' -f $stamp, $suffix)
140
163
  $mailErr = Join-Path $logDir ('daily-mail-{0}{1}.err.log' -f $stamp, $suffix)
141
- if ($mode -eq 'day' -and (Test-Path $mailScript)) {
142
- $mailProc = Start-Process -FilePath $python -ArgumentList @($mailScript, '--date', $targetDay) `
164
+ if (Test-Path $mailScript) {
165
+ $mailArgs = @($mailScript, '--date', $targetDay)
166
+ if ($mode -eq 'noon') { $mailArgs += @('--label', '截至中午') }
167
+ $mailProc = Start-Process -FilePath $python -ArgumentList $mailArgs `
143
168
  -NoNewWindow -Wait -PassThru -RedirectStandardOutput $mailOut -RedirectStandardError $mailErr
144
169
  if ($mailProc.ExitCode -ne 0) {
145
170
  Add-Content -Path (Join-Path $logDir 'daily-task.log') -Value `
@@ -1235,7 +1235,15 @@ def build_phrase_index(line: str = 'all', db_path=None, meta_path=None):
1235
1235
  conn.executemany('INSERT INTO hits(body, rid, line, label, date, title)'
1236
1236
  ' VALUES (?, ?, ?, ?, ?, ?)', rows)
1237
1237
  conn.commit()
1238
- conn.execute('VACUUM')
1238
+ # `VACUUM` 要写临时文件;在受限环境(沙箱 / %TEMP% 不可写)它会报
1239
+ # `unable to open database file`,而**数据其实已经写进去了** —— 那一步只是把库压实。
1240
+ # 所以它失败不能被当成"索引没建好":2026-10-22 实测 155MB 的库内容完整,
1241
+ # 却因为这一行让整个 phrase-build 的退出码变成非 0。
1242
+ try:
1243
+ conn.execute('VACUUM')
1244
+ except Exception as exc:
1245
+ print(f'[WARN] 短语索引已写入,但 VACUUM 失败({type(exc).__name__}: {exc})'
1246
+ '——只是没压实,查询不受影响', file=sys.stderr)
1239
1247
  finally:
1240
1248
  conn.close()
1241
1249
  return {'records': len(rows), 'byLine': by_line, 'textMissing': text_missing,