weflow-cli 1.13.0 → 1.14.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.
@@ -49,8 +49,10 @@ interface CliConfig {
49
49
  typesafeApiKey: string
50
50
  // 阿里云百炼(DashScope)API Key —— 语义检索 / RAG 的 embedding 用它
51
51
  dashscopeApiKey: string
52
- // 决策模型走哪条通路:native(默认,api.typesafe.ai)/ openrouter(本机原生被地区封锁时的第二条路)
52
+ // 决策模型走哪条通路:native(默认,api.typesafe.ai)/ openrouter / chat / local(本机决策模型)
53
53
  judgeProvider: string
54
+ // 本地决策模型服务地址 —— judgeProvider=local 时用它(默认 http://127.0.0.1:8090,见 D-127)
55
+ judgeLocalUrl: string
54
56
  // OpenRouter API Key —— judgeProvider=openrouter 时用它(D-120)
55
57
  openrouterApiKey: string
56
58
  // OpenRouter 上用的模型 id(默认 typesafe/jev-router,即 TypeSafe 发布的决策模型)
@@ -97,6 +99,8 @@ interface CliConfig {
97
99
  /** 不在日报里出现的主题,逗号分隔(如 新闻,投资);展示层开关,改它不用重抓 */
98
100
  dailyExcludeTopics: string
99
101
  dailyAiEnabled: string
102
+ /** 日报邮件的收件人(`daily --email` 与定时任务用);空则发给 SMTP 账号自己 */
103
+ dailyMailTo?: string
100
104
  }
101
105
 
102
106
  export interface ListEntry {
@@ -117,7 +121,7 @@ const CONFIG_DIR = join(homedir(), '.weflow-cli')
117
121
  const CONFIG_FILE = join(CONFIG_DIR, 'config.json')
118
122
 
119
123
  export class ConfigService {
120
- private config: CliConfig = { dbPath: '', wxid: '', decryptKey: '', decryptKey3x: '', dataVersion: '', dbPath3x: '', ntDbPath: '', ntKey: '', ntSalt: '', contactDbPath: '', contactKey: '', contactSalt: '', wechatOcToken: '', wechatOcAccountId: '', wechatOcBaseUrl: '', wechatOcSyncBuf: '', wechatOcContextTokens: '', wechatMediaDownload: 'false', whitelist: [], blacklist: [], whitelistEntries: [], blacklistEntries: [], vaultRepo: '', aiEngine: 'deepseek', wereadApiKey: '', deepseekApiKey: '', typesafeApiKey: '', dashscopeApiKey: '', judgeProvider: 'native', openrouterApiKey: '', openrouterJudgeModel: '', snsDbPath: '', snsKey: '', snsSalt: '', favDbPath: '', favKey: '', favPassphrase: '', emoticonSeed: '', assistantPrivacy: 'strict', localModel: '', aiBaseUrl: '', aiModel: '', assistantWhitelist: '', assistantGroupWhitelist: '', assistantGroupRequireMention: 'true', assistantFastRoute: 'off', assistantPanelUser: '', quickReplyContacts: '', skillDirs: '', skillDisabled: '', dailySources: '', dailySourceCategories: '', dailyExcludeTopics: '', dailyAiEnabled: 'true' }
124
+ private config: CliConfig = { dbPath: '', wxid: '', decryptKey: '', decryptKey3x: '', dataVersion: '', dbPath3x: '', ntDbPath: '', ntKey: '', ntSalt: '', contactDbPath: '', contactKey: '', contactSalt: '', wechatOcToken: '', wechatOcAccountId: '', wechatOcBaseUrl: '', wechatOcSyncBuf: '', wechatOcContextTokens: '', wechatMediaDownload: 'false', whitelist: [], blacklist: [], whitelistEntries: [], blacklistEntries: [], vaultRepo: '', aiEngine: 'deepseek', wereadApiKey: '', deepseekApiKey: '', typesafeApiKey: '', dashscopeApiKey: '', judgeProvider: 'native', openrouterApiKey: '', openrouterJudgeModel: '', judgeLocalUrl: '', snsDbPath: '', snsKey: '', snsSalt: '', favDbPath: '', favKey: '', favPassphrase: '', emoticonSeed: '', assistantPrivacy: 'strict', localModel: '', aiBaseUrl: '', aiModel: '', assistantWhitelist: '', assistantGroupWhitelist: '', assistantGroupRequireMention: 'true', assistantFastRoute: 'off', assistantPanelUser: '', quickReplyContacts: '', skillDirs: '', skillDisabled: '', dailySources: '', dailySourceCategories: '', dailyExcludeTopics: '', dailyAiEnabled: 'true' }
121
125
 
122
126
  /** 本进程修改过、待回写的字段 (多进程并发写保护) */
123
127
  private dirty = new Set<keyof CliConfig>()
@@ -169,6 +173,7 @@ export class ConfigService {
169
173
  typesafeApiKey: data.typesafeApiKey || '',
170
174
  dashscopeApiKey: data.dashscopeApiKey || '',
171
175
  judgeProvider: data.judgeProvider || 'native',
176
+ judgeLocalUrl: data.judgeLocalUrl || '',
172
177
  openrouterApiKey: data.openrouterApiKey || '',
173
178
  openrouterJudgeModel: data.openrouterJudgeModel || '',
174
179
  snsDbPath: data.snsDbPath || '',
@@ -378,7 +383,7 @@ export class ConfigService {
378
383
  }
379
384
 
380
385
  clear(): void {
381
- this.config = { dbPath: '', wxid: '', decryptKey: '', decryptKey3x: '', dataVersion: '', dbPath3x: '', ntDbPath: '', ntKey: '', ntSalt: '', contactDbPath: '', contactKey: '', contactSalt: '', wechatOcToken: '', wechatOcAccountId: '', wechatOcBaseUrl: '', wechatOcSyncBuf: '', wechatOcContextTokens: '', wechatMediaDownload: 'false', whitelist: [], blacklist: [], whitelistEntries: [], blacklistEntries: [], vaultRepo: '', aiEngine: 'deepseek', wereadApiKey: '', deepseekApiKey: '', typesafeApiKey: '', dashscopeApiKey: '', judgeProvider: 'native', openrouterApiKey: '', openrouterJudgeModel: '', snsDbPath: '', snsKey: '', snsSalt: '', favDbPath: '', favKey: '', favPassphrase: '', emoticonSeed: '', assistantPrivacy: 'strict', localModel: '', aiBaseUrl: '', aiModel: '', assistantWhitelist: '', assistantGroupWhitelist: '', assistantGroupRequireMention: 'true', assistantFastRoute: 'off', assistantPanelUser: '', quickReplyContacts: '', skillDirs: '', skillDisabled: '', dailySources: '', dailySourceCategories: '', dailyExcludeTopics: '', dailyAiEnabled: 'true' }
386
+ this.config = { dbPath: '', wxid: '', decryptKey: '', decryptKey3x: '', dataVersion: '', dbPath3x: '', ntDbPath: '', ntKey: '', ntSalt: '', contactDbPath: '', contactKey: '', contactSalt: '', wechatOcToken: '', wechatOcAccountId: '', wechatOcBaseUrl: '', wechatOcSyncBuf: '', wechatOcContextTokens: '', wechatMediaDownload: 'false', whitelist: [], blacklist: [], whitelistEntries: [], blacklistEntries: [], vaultRepo: '', aiEngine: 'deepseek', wereadApiKey: '', deepseekApiKey: '', typesafeApiKey: '', dashscopeApiKey: '', judgeProvider: 'native', openrouterApiKey: '', openrouterJudgeModel: '', judgeLocalUrl: '', snsDbPath: '', snsKey: '', snsSalt: '', favDbPath: '', favKey: '', favPassphrase: '', emoticonSeed: '', assistantPrivacy: 'strict', localModel: '', aiBaseUrl: '', aiModel: '', assistantWhitelist: '', assistantGroupWhitelist: '', assistantGroupRequireMention: 'true', assistantFastRoute: 'off', assistantPanelUser: '', quickReplyContacts: '', skillDirs: '', skillDisabled: '', dailySources: '', dailySourceCategories: '', dailyExcludeTopics: '', dailyAiEnabled: 'true' }
382
387
  // clear 意图是全量重置: 所有字段标记为脏, 覆盖磁盘上的全部旧值
383
388
  this.dirty = new Set(Object.keys(this.config) as (keyof CliConfig)[])
384
389
  this.save()
@@ -90,7 +90,7 @@ const VERSION_DIR = /^\d+(\.\d+)+$/
90
90
  // 于是"客户端自报数据目录"这一项**永远是空的**而看不出来)。版本那一支只认 `Weixin.exe`(见 `summarize`)。
91
91
  // `WeixinDll` 是**同一件事的第二观测**:Win32_Process 只有命令行,模块表要问进程本身。
92
92
  // 每条都单独 try —— 一个进程读不到模块不该让整张表为空(那会把"读不到"讲成"客户端没在跑")。
93
- const PS_LIST = "$rows = Get-CimInstance Win32_Process -Filter (\"Name='Weixin.exe' or Name='WeChatAppEx.exe'\") | ForEach-Object { $dll = $null; try { $dll = (Get-Process -Id $_.ProcessId -ErrorAction Stop).Modules | Where-Object { $_.ModuleName -eq 'Weixin.dll' } | Select-Object -First 1 -ExpandProperty FileName } catch {}; [pscustomobject]@{ ProcessId = $_.ProcessId; ExecutablePath = $_.ExecutablePath; CommandLine = $_.CommandLine; WeixinDll = $dll } }; if ($rows) { $rows | ConvertTo-Json -Compress }"
93
+ export const PS_LIST = "$rows = Get-CimInstance Win32_Process -Filter (\"Name='Weixin.exe' or Name='WeChatAppEx.exe'\") | ForEach-Object { $dll = $null; try { $dll = (Get-Process -Id $_.ProcessId -ErrorAction Stop).Modules | Where-Object { $_.ModuleName -eq 'Weixin.dll' } | Select-Object -First 1 -ExpandProperty FileName } catch {}; [pscustomobject]@{ ProcessId = $_.ProcessId; ExecutablePath = $_.ExecutablePath; CommandLine = $_.CommandLine; WeixinDll = $dll } }; if ($rows) { [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes(($rows | ConvertTo-Json -Compress))) }"
94
94
 
95
95
  export function parseProcessRows(raw: string): WechatClientProcess[] {
96
96
  const text = String(raw || '').trim()
@@ -362,7 +362,17 @@ export async function reportedDataDirs(run: PowershellRunner = defaultRunner): P
362
362
  const defaultRunner: PowershellRunner = async (command) => {
363
363
  const { stdout } = await execFileAsync('powershell.exe',
364
364
  ['-NoProfile', '-NonInteractive', '-Command', command], { timeout: 15_000 })
365
- return String(stdout || '')
365
+ const raw = String(stdout || '').trim()
366
+ // PowerShell 5.1 往 stdout 写的是**控制台代码页**(中文 Windows 上是 GBK 936),
367
+ // 而 Node 按 UTF-8 解码 —— 于是 `D:\微信\xwechat_files` 变成 `D:\΢��\xwechat_files`,
368
+ // 接着把"与配置是同一份"判成"否",报告就会给出错误的迁移建议(2026-10-19 实测)。
369
+ // 让 PowerShell 先 base64(UTF-16LE)再输出,就与代码页彻底无关了。
370
+ if (!raw || raw.startsWith('{') || raw.startsWith('[')) return raw // 兼容注入的 runner / 老输出
371
+ try {
372
+ return Buffer.from(raw, 'base64').toString('utf16le').trim()
373
+ } catch {
374
+ return raw
375
+ }
366
376
  }
367
377
 
368
378
  /**