@yottameta/yotta-memory 0.22.4 → 0.22.5
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/README.zh-CN.md +1 -1
- package/SKILL.md +2 -2
- package/USER_GUIDE.md +2 -1
- package/bin/yotta-memory.js +82 -17
- package/package.json +1 -1
- package/skill-manifest.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## v0.22.5 (2026-10-07)
|
|
2
|
+
|
|
3
|
+
- **修复 Electron 宿主(YottaCode shim)下开机自启拉起 GUI 而非服务**:宿主运行时为 Electron 时(`process.versions.electron`;`process.execPath` 指向 Electron 可执行文件),此前生成的任务 / 兜底 / 定时配置不带 `ELECTRON_RUN_AS_NODE=1`,开机拉起的是桌面程序本体、8787 无监听(新机首例实测)。现在自动检测并统一注入:Windows 主任务与每日备份任务(cmd /c + `set "ELECTRON_RUN_AS_NODE=1"`)、Windows Startup 兜底 .cmd(set 前缀)、Linux systemd 单元 / crontab @reboot / 备份定时器(`Environment=` / 行内前缀)、macOS 备份 LaunchAgent(EnvironmentVariables)。
|
|
4
|
+
- `lan status` 新增告警:任务或 Startup 兜底命令指向 Electron 可执行文件且缺少 `ELECTRON_RUN_AS_NODE` 时,提示重新 `lan enable` 修复。
|
|
5
|
+
- 非 Electron 宿主行为不变(纯 Node 下生成的命令与旧版一致)。
|
|
6
|
+
- `test/lan-windows-autostart.test.js` 新增 8 项(Electron / 非 Electron 双态:任务 XML / 兜底 cmd / 备份任务 / Linux 单元与 crontab / macOS plist / status 告警两例);全量 `npm test` 352/352。
|
|
7
|
+
|
|
1
8
|
## v0.22.4 (2026-10-07)
|
|
2
9
|
|
|
3
10
|
- **修复 Windows `lan enable` 开机自启三缺陷(实测复现)**:此前计划任务用 schtasks 默认交互式主体——① 在登录会话运行会弹控制台窗口;② `--onstart` 开机触发时无登录会话,任务起不来且不补跑(实测 LastTaskResult 267011);③ schtasks 默认带电池限制与 72 小时执行时限。本版改为 **S4U 主体 + 任务 XML 注册**(零依赖):后台静默运行不弹窗口,开机 / 登录触发均可用;默认去掉电池限制与 72h 时限(`ExecutionTimeLimit=PT0S` 不限时);`--onstart` 在 Windows 真正生效(BootTrigger)。任务注册成功时自动清理旧 Startup 兜底文件,避免双份自启。
|
package/README.zh-CN.md
CHANGED
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
|
|
175
175
|
- **记忆库即引擎**:`serve` 把记忆目录挂成 MCP 服务,目录随盘走;引擎主机只需装 CLI 当存放点,无需装任何 AI 智能体。
|
|
176
176
|
- **双模式可并存**:本地 `serve --stdio` 零进程(客户端按需拉起);局域网 streamable HTTP(默认 `0.0.0.0:8787`)+ 每智能体 token 鉴权。
|
|
177
|
-
- **开机自启**:`lan enable` 注册开机自启——Windows 优先计划任务(v0.22.4 起 S4U 主体后台静默、不弹窗口;默认登录自启,`--onstart` 开机即启、无登录会话也可启动,计划任务注册需管理员),非管理员自动降级为**用户级 Startup 静默自启**(免管理员,启动脚本内联启动命令、被清理也会在开机时自动重建,v0.6.3 起不再弹 80070002
|
|
177
|
+
- **开机自启**:`lan enable` 注册开机自启——Windows 优先计划任务(v0.22.4 起 S4U 主体后台静默、不弹窗口;默认登录自启,`--onstart` 开机即启、无登录会话也可启动,计划任务注册需管理员),非管理员自动降级为**用户级 Startup 静默自启**(免管理员,启动脚本内联启动命令、被清理也会在开机时自动重建,v0.6.3 起不再弹 80070002);**v0.22.5 起 Electron 宿主(如 YottaCode)自动注入 ELECTRON_RUN_AS_NODE,任务不再拉起 GUI**;Linux 优先 **systemd 用户单元**(`systemctl --user`,登录自启;`--onstart` 附加 `loginctl enable-linger` 开机即启),systemd 不可用时自动降级**用户 crontab @reboot**(v0.6.4);`lan disable` 移除,`lan status` 查询。
|
|
178
178
|
- **安全边界**:管理动作(init / config / token / lan / serve)不进 MCP,token 不远程暴露;远程智能体只能读写记忆,且路径限记忆库内(export/import 的 out/src 必须落在库内)、distill 不支持 `--model`(仅本地 CLI),不能改配置、不能管 token。
|
|
179
179
|
- 完整操作步骤见上文「局域网多机共享」章节与 [USER_GUIDE.md](USER_GUIDE.md)。
|
|
180
180
|
|
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yotta-memory
|
|
3
3
|
description: 元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/可回滚:让任何 AI 智能体活过会话,开工 recall 恢复上下文、重要信息 remember 落盘、收工归档。类型体系 FACT(公共共享)/ PREF / BOUND / COMMIT(私密隔离)。触发:记住、别忘了、记一笔、记忆、remember、recall、跨会话、上次说到、续测、交接、归档、记忆盘、共享记忆、局域网记忆、画像、开工上下文、长期理解摘要、近期走廊、会话闭环、记忆守则、profile、context、越用越懂、语义检索、反馈、维护、蒸馏、feedback、maintain、distill、explain、自我学习、自我进化、自我提升、查看平台分页、recall 候选预过滤、任务相关记忆、--focus、--embedding、压缩遗忘、consolidate、周期摘要、自动合并、分类型衰减、回滚、备份、backup、防误删、doctor、事务快照、基线探针、恢复演练、记忆库安全扫描、scan、命中打点、容量水位、--capacity、--propose、上下文压缩审计、context --audit
|
|
4
|
-
version: 0.22.
|
|
4
|
+
version: 0.22.5
|
|
5
5
|
license: MIT
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -333,7 +333,7 @@ yotta-memory doctor --json
|
|
|
333
333
|
| `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 每智能体访问 token:生成 / 列出 / 吊销(登记 `<记忆库>/.server/tokens.json`;同 ID 已被其它来源占用需 `--force` 覆盖,防不同智能体合流)|
|
|
334
334
|
| `yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio] [--tools core|full]` | 启动 MCP 记忆引擎(streamable HTTP 局域网 / --stdio 本地零进程模式;Bearer token + X-Agent-Id + X-Agent-Key 鉴权;工具分组默认 full)|
|
|
335
335
|
| `yotta-memory runtime list / install <tarball|版本> [--from-current] [--force] / use <版本> [--restart] / rollback [--restart] / status` | 运行时稳定入口(runtime.json + versions + current;安装 / 切换 / 回滚 / 查看漂移;`--restart` 尝试重启受管 server,失败自动切回旧版本);漂移诊断用 `doctor --runtime` |
|
|
336
|
-
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:任务计划程序,S4U 主体后台静默——v0.22.4 起不弹窗口,`--onstart` 无登录会话也可启动;任务 XML 注册,去电池限制与 72h 时限;非管理员自动降级用户级 Startup 静默自启,v0.6.3 起 VBS 自愈不弹 80070002;Linux:systemd 用户单元,不可用时自动降级用户 crontab @reboot)|
|
|
336
|
+
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:任务计划程序,S4U 主体后台静默——v0.22.4 起不弹窗口,`--onstart` 无登录会话也可启动;任务 XML 注册,去电池限制与 72h 时限;非管理员自动降级用户级 Startup 静默自启,v0.6.3 起 VBS 自愈不弹 80070002;v0.22.5 起 Electron 宿主(如 YottaCode)自动注入 ELECTRON_RUN_AS_NODE,不再拉起 GUI;Linux:systemd 用户单元,不可用时自动降级用户 crontab @reboot)|
|
|
337
337
|
| `yotta-memory feedback <文件|主题> --useful|--useless [--reason <原因>] [--undo]` | 显式使用反馈(v0.8.0 自我学习闭环:useful → weight×1.2 / useless → weight×0.8,confidence / feedback_net 同步演化;`--undo` 回滚最近一次;审计写 `.archive/feedback-<日期>.jsonl`)|
|
|
338
338
|
| `yotta-memory maintain [--dry-run] [--apply] [--purge] [--threshold N] [--age N] [--dedup] [--dedup --apply] [--merge A,B] [--rules]` | 记忆自组织(v0.8.0 自我进化 + v0.10.0 自动合并):默认 dry-run 预览;`--apply` 执行归档(immutable / BOUND 豁免;私密归档入 `.archive/private/<owner>/<type>/`),`--purge` 才真删遗忘候选;`--dedup` 查重并给**置信度分档**(≥0.85 高置信 / 0.65–0.85 建议手动 / 其余忽略),`--dedup --apply` 自动合并同归属高置信组(写批次审计可回滚;与归档互斥,不误归档);`--merge A,B` 手动合并两条;`--rules` 只读输出重复踩坑 → 建议升级为规则的清单(阈值 `maintain_rule_min_hits`,默认 3;只给建议不写 BOUND);审计写 `.archive/audit-<日期>.jsonl`)|
|
|
339
339
|
| `yotta-memory consolidate [--min-age N] [--min-idle N] [--max-utility N] [--min-group N] [--period N] [--type T] [--model <cmd>] [--apply] [--undo <batch>] [--batches]` | 周期摘要压缩(v0.10.0 压缩遗忘 + v0.18.0 提案闸门 + v0.18.1 日期 / 标记:把超龄 + 长期闲置 + 低效用的同主题旧记忆归纳成**带溯源**的周期摘要并留在活跃区,原文整体进 `.archive/`;摘要正文按条目 `created` 把「昨天 / 上周 / 本月 / 今年」等绝对化,归档副本追加一行巩固标记;默认 dry-run;immutable / BOUND 豁免,活跃 / 高效用记忆不动;`--apply` 执行并写批次审计,`--undo <batch>` 剥离标记并回滚(幂等),`--batches` 查近期批次;`--model` 仅本地 CLI)|
|
package/USER_GUIDE.md
CHANGED
|
@@ -335,7 +335,8 @@ yotta-memory init --dir /srv/yotta-memory # 新库:初始化(
|
|
|
335
335
|
|
|
336
336
|
```bash
|
|
337
337
|
# Windows:内置命令(优先计划任务——v0.22.4 起 S4U 主体后台静默、不弹窗口;
|
|
338
|
-
# 非管理员自动降级用户级 Startup 静默自启;v0.6.3
|
|
338
|
+
# 非管理员自动降级用户级 Startup 静默自启;v0.6.3 起启动脚本自愈——启动文件被清理也会在开机时自动重建;
|
|
339
|
+
# v0.22.5 起 Electron 宿主(如 YottaCode)自动注入 ELECTRON_RUN_AS_NODE,不再拉起 GUI)
|
|
339
340
|
yotta-memory lan enable # 登录后自动启动(默认)
|
|
340
341
|
yotta-memory lan enable --onstart # 开机即启、无登录会话也可启动(计划任务注册需管理员)
|
|
341
342
|
yotta-memory lan status # 查看状态
|
package/bin/yotta-memory.js
CHANGED
|
@@ -25,7 +25,7 @@ const net = require('net');
|
|
|
25
25
|
const child_process = require('child_process');
|
|
26
26
|
const { AsyncLocalStorage } = require('async_hooks');
|
|
27
27
|
|
|
28
|
-
const VERSION = '0.22.
|
|
28
|
+
const VERSION = '0.22.5';
|
|
29
29
|
const CLI_VALUE_OPTS = new Set(['--type', '--limit', '--days', '--out', '--owner', '--agent', '--agent-id', '--agent-key', '--agent-key-file', '--plugin-data', '--threshold', '--scope', '--host', '--port', '--dir', '--name', '--user', '--relationship', '--source', '--weight', '--budget', '--password', '--new-password', '--recovery-key', '--recovery-key-out', '--reason', '--merge', '--model', '--subject', '--embedding', '--focus', '--embedding-timeout', '--min-age', '--min-idle', '--max-utility', '--min-group', '--period', '--to', '--id', '--time', '--tools', '--mcp-config', '--skill-dir', '--year', '--evalset', '--k', '--seed', '--bootstrap', '--gate', '--against', '--template', '--path', '--from']);
|
|
30
30
|
const CLI_FLAG_OPTS = new Set(['--project', '--all', '--unsafe', '--no-auth', '--stdio', '--onstart', '--from-current', '--restart', '--force', '--attach', '--allow-same-volume', '--verify', '--no-hint', '--encrypt', '--no-encrypt', '--password-stdin', '--json', '--manual', '--skip-schedule', '--useful', '--useless', '--undo', '--dry-run', '--apply', '--purge', '--dedup', '--batches', '--propose', '--audit', '--capacity', '--rules', '--explain', '--semantic', '--runtime', '--ablate', '--timing', '--baseline', '--probe', '--quarantine', '--restore', '--no-usage', '--yes', '--keep-memories', '--keep-identity']);
|
|
31
31
|
|
|
@@ -2919,6 +2919,12 @@ function backupWindowsTaskXml(opts) {
|
|
|
2919
2919
|
const principal = userId
|
|
2920
2920
|
? '<Principal id="Author"><UserId>' + xmlEscape(userId) + '</UserId><LogonType>InteractiveToken</LogonType><RunLevel>LeastPrivilege</RunLevel></Principal>'
|
|
2921
2921
|
: '<Principal id="Author"><LogonType>InteractiveToken</LogonType><RunLevel>LeastPrivilege</RunLevel></Principal>';
|
|
2922
|
+
const cmdPath = opts.cmdPath || process.env.ComSpec || 'cmd.exe';
|
|
2923
|
+
// v0.22.5: Electron 宿主(如 YottaCode shim)下 process.execPath 是 Electron 可执行文件,
|
|
2924
|
+
// 任务需经 cmd /c + ELECTRON_RUN_AS_NODE=1 包装,否则拉起的是桌面程序本体而非 Node。
|
|
2925
|
+
const action = lanElectronHost(opts)
|
|
2926
|
+
? '<Actions Context="Author"><Exec><Command>' + xmlEscape(cmdPath) + '</Command><Arguments>/c "set "ELECTRON_RUN_AS_NODE=1" && "' + xmlEscape(nodePath) + '" "' + xmlEscape(scriptPath) + '" backup ensure-daily"</Arguments></Exec></Actions>'
|
|
2927
|
+
: '<Actions Context="Author"><Exec><Command>' + xmlEscape(nodePath) + '</Command><Arguments>"' + xmlEscape(scriptPath) + '" backup ensure-daily</Arguments></Exec></Actions>';
|
|
2922
2928
|
return [
|
|
2923
2929
|
'<?xml version="1.0" encoding="UTF-16"?>',
|
|
2924
2930
|
'<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">',
|
|
@@ -2940,7 +2946,7 @@ function backupWindowsTaskXml(opts) {
|
|
|
2940
2946
|
'<ExecutionTimeLimit>PT2H</ExecutionTimeLimit>',
|
|
2941
2947
|
'<Priority>7</Priority>',
|
|
2942
2948
|
'</Settings>',
|
|
2943
|
-
|
|
2949
|
+
action,
|
|
2944
2950
|
'</Task>',
|
|
2945
2951
|
].join('\r\n');
|
|
2946
2952
|
}
|
|
@@ -2948,16 +2954,20 @@ function backupSystemdServiceContent(opts) {
|
|
|
2948
2954
|
opts = opts || {};
|
|
2949
2955
|
const nodePath = opts.nodePath || process.execPath;
|
|
2950
2956
|
const scriptPath = opts.scriptPath || runtimeManagedScript();
|
|
2951
|
-
|
|
2957
|
+
const lines = [
|
|
2952
2958
|
'[Unit]',
|
|
2953
2959
|
'Description=Yotta Memory daily backup',
|
|
2954
2960
|
'',
|
|
2955
2961
|
'[Service]',
|
|
2956
2962
|
'Type=oneshot',
|
|
2963
|
+
];
|
|
2964
|
+
if (lanElectronHost(opts)) lines.push('Environment=ELECTRON_RUN_AS_NODE=1');
|
|
2965
|
+
lines.push(
|
|
2957
2966
|
'ExecStart=' + systemdEscapeArg(nodePath) + ' ' + systemdEscapeArg(scriptPath) + ' backup ensure-daily',
|
|
2958
2967
|
'Nice=10',
|
|
2959
2968
|
'',
|
|
2960
|
-
|
|
2969
|
+
);
|
|
2970
|
+
return lines.join('\n');
|
|
2961
2971
|
}
|
|
2962
2972
|
function backupSystemdTimerContent(opts) {
|
|
2963
2973
|
opts = opts || {};
|
|
@@ -2983,7 +2993,8 @@ function backupCronLine(opts) {
|
|
|
2983
2993
|
const scriptPath = opts.scriptPath || runtimeManagedScript();
|
|
2984
2994
|
const logPath = opts.logPath || '';
|
|
2985
2995
|
const redirect = logPath ? ' >> ' + shQuote(logPath) + ' 2>&1' : '';
|
|
2986
|
-
|
|
2996
|
+
const env = lanElectronHost(opts) ? 'ELECTRON_RUN_AS_NODE=1 ' : '';
|
|
2997
|
+
return time.minute + ' ' + time.hour + ' * * * ' + env + shQuote(nodePath) + ' ' + shQuote(scriptPath) + ' backup ensure-daily' + redirect + ' ' + BACKUP_CRON_MARKER;
|
|
2987
2998
|
}
|
|
2988
2999
|
function backupLaunchdPlist(opts) {
|
|
2989
3000
|
opts = opts || {};
|
|
@@ -2995,6 +3006,11 @@ function backupLaunchdPlist(opts) {
|
|
|
2995
3006
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
2996
3007
|
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
2997
3008
|
'<plist version="1.0"><dict>',
|
|
3009
|
+
];
|
|
3010
|
+
if (lanElectronHost(opts)) {
|
|
3011
|
+
lines.push('<key>EnvironmentVariables</key><dict><key>ELECTRON_RUN_AS_NODE</key><string>1</string></dict>');
|
|
3012
|
+
}
|
|
3013
|
+
lines.push(
|
|
2998
3014
|
'<key>Label</key><string>' + BACKUP_LAUNCHD_LABEL + '</string>',
|
|
2999
3015
|
'<key>ProgramArguments</key><array>',
|
|
3000
3016
|
'<string>' + xmlEscape(nodePath) + '</string>',
|
|
@@ -3006,7 +3022,7 @@ function backupLaunchdPlist(opts) {
|
|
|
3006
3022
|
'<key>Hour</key><integer>' + time.hour + '</integer>',
|
|
3007
3023
|
'<key>Minute</key><integer>' + time.minute + '</integer>',
|
|
3008
3024
|
'</dict>',
|
|
3009
|
-
|
|
3025
|
+
);
|
|
3010
3026
|
if (logPath) {
|
|
3011
3027
|
lines.push('<key>StandardOutPath</key><string>' + xmlEscape(logPath) + '</string>');
|
|
3012
3028
|
lines.push('<key>StandardErrorPath</key><string>' + xmlEscape(logPath) + '</string>');
|
|
@@ -3046,7 +3062,7 @@ function backupScheduleEnableCore(opts) {
|
|
|
3046
3062
|
const userId = opts.userId || (process.env.USERDOMAIN && process.env.USERNAME ? process.env.USERDOMAIN + '\\' + process.env.USERNAME : '');
|
|
3047
3063
|
const taskName = opts.taskName || BACKUP_TASK_NAME;
|
|
3048
3064
|
const xmlPath = opts.xmlPath || path.join(os.tmpdir(), taskName + '.xml');
|
|
3049
|
-
fs.writeFileSync(xmlPath, '\ufeff' + backupWindowsTaskXml({ time: time, nodePath: nodePath, scriptPath: scriptPath, userId: userId }), 'utf16le');
|
|
3065
|
+
fs.writeFileSync(xmlPath, '\ufeff' + backupWindowsTaskXml({ time: time, nodePath: nodePath, scriptPath: scriptPath, userId: userId, electronHost: opts.electronHost }), 'utf16le');
|
|
3050
3066
|
schedulerExec(opts, 'schtasks', ['/create', '/tn', taskName, '/xml', xmlPath, '/f']);
|
|
3051
3067
|
return { error: false, scheduler: 'windows-task', taskName: taskName, text: '已注册 Windows 每日备份任务: ' + taskName };
|
|
3052
3068
|
}
|
|
@@ -3055,7 +3071,7 @@ function backupScheduleEnableCore(opts) {
|
|
|
3055
3071
|
const servicePath = path.join(unitDir, BACKUP_SYSTEMD_SERVICE);
|
|
3056
3072
|
const timerPath = path.join(unitDir, BACKUP_SYSTEMD_TIMER);
|
|
3057
3073
|
fs.mkdirSync(unitDir, { recursive: true });
|
|
3058
|
-
fs.writeFileSync(servicePath, backupSystemdServiceContent({ nodePath: nodePath, scriptPath: scriptPath }), 'utf8');
|
|
3074
|
+
fs.writeFileSync(servicePath, backupSystemdServiceContent({ nodePath: nodePath, scriptPath: scriptPath, electronHost: opts.electronHost }), 'utf8');
|
|
3059
3075
|
fs.writeFileSync(timerPath, backupSystemdTimerContent({ time: time }), 'utf8');
|
|
3060
3076
|
const reload = schedulerTry(opts, 'systemctl', ['--user', 'daemon-reload']);
|
|
3061
3077
|
const enable = reload.ok ? schedulerTry(opts, 'systemctl', ['--user', 'enable', '--now', BACKUP_SYSTEMD_TIMER]) : reload;
|
|
@@ -3063,7 +3079,7 @@ function backupScheduleEnableCore(opts) {
|
|
|
3063
3079
|
return { error: false, scheduler: 'linux-systemd', unit: BACKUP_SYSTEMD_TIMER, text: '已注册 systemd 用户定时器: ' + BACKUP_SYSTEMD_TIMER };
|
|
3064
3080
|
}
|
|
3065
3081
|
const logPath = opts.logPath || path.join(os.homedir(), '.yottamemory', 'backup.log');
|
|
3066
|
-
const line = backupCronLine({ time: time, nodePath: nodePath, scriptPath: scriptPath, logPath: logPath });
|
|
3082
|
+
const line = backupCronLine({ time: time, nodePath: nodePath, scriptPath: scriptPath, logPath: logPath, electronHost: opts.electronHost });
|
|
3067
3083
|
const current = schedulerTry(opts, 'crontab', ['-l']);
|
|
3068
3084
|
const kept = current.stdout.split(/\r?\n/).filter(function (item) {
|
|
3069
3085
|
return item && item.indexOf(BACKUP_CRON_MARKER) === -1;
|
|
@@ -3087,6 +3103,7 @@ function backupScheduleEnableCore(opts) {
|
|
|
3087
3103
|
nodePath: nodePath,
|
|
3088
3104
|
scriptPath: scriptPath,
|
|
3089
3105
|
logPath: opts.logPath || path.join(os.homedir(), 'Library', 'Logs', 'yotta-memory-backup.log'),
|
|
3106
|
+
electronHost: opts.electronHost,
|
|
3090
3107
|
}), 'utf8');
|
|
3091
3108
|
const uid = typeof process.getuid === 'function' ? process.getuid() : '';
|
|
3092
3109
|
const bootstrap = schedulerTry(opts, 'launchctl', ['bootstrap', 'gui/' + uid, plistPath]);
|
|
@@ -10394,11 +10411,13 @@ const LAN_GEN_MARKER = 'Generated by yotta-memory lan enable';
|
|
|
10394
10411
|
const LAN_UNIT_NAME = 'yotta-memory-serve.service';
|
|
10395
10412
|
const LAN_CRONTAB_MARKER = '#YTM_LAN:yotta-memory-serve';
|
|
10396
10413
|
function lanTaskRunCmd(opts) {
|
|
10414
|
+
opts = opts || {};
|
|
10397
10415
|
const host = opts.host || '0.0.0.0';
|
|
10398
10416
|
const port = opts.port || 8787;
|
|
10399
10417
|
// schtasks /tr 不接受多余内嵌引号:路径无空格不加引号,含空格/引号用 \" 转义(0.5.2 修复)
|
|
10400
10418
|
const q = (p) => /[\s"]/.test(p) ? '\\"' + p.replace(/"/g, '\\"') + '\\"' : p;
|
|
10401
|
-
|
|
10419
|
+
const prefix = lanElectronHost(opts) ? 'set "ELECTRON_RUN_AS_NODE=1" && ' : '';
|
|
10420
|
+
return prefix + q(process.execPath) + ' ' + q(runtimeManagedScript()) + ' serve --host ' + host + ' --port ' + port;
|
|
10402
10421
|
}
|
|
10403
10422
|
|
|
10404
10423
|
function lanServeArgs(opts) {
|
|
@@ -10414,6 +10433,28 @@ function lanSpawn(bin, args, spawnOpts) {
|
|
|
10414
10433
|
}
|
|
10415
10434
|
// platform override for testing/advanced use (YOTTA_LAN_PLATFORM=linux/win32/...)
|
|
10416
10435
|
function lanPlatform() { return process.env.YOTTA_LAN_PLATFORM || process.platform; }
|
|
10436
|
+
// ---- v0.22.5: Electron 宿主(YottaCode shim)适配 ----
|
|
10437
|
+
// 背景:宿主运行时为 Electron 时(如 YottaCode.exe 以 ELECTRON_RUN_AS_NODE=1 当 Node 运行本脚本),
|
|
10438
|
+
// process.execPath 指向 Electron 可执行文件;自启 / 定时任务环境不带 ELECTRON_RUN_AS_NODE=1 时,
|
|
10439
|
+
// 拉起的是桌面程序本体而非 Node 服务(0.22.4 后新机首例实测)。生成任务 / 兜底 / 定时配置时统一注入。
|
|
10440
|
+
function lanElectronHost(opts) {
|
|
10441
|
+
opts = opts || {};
|
|
10442
|
+
if (typeof opts.electronHost === 'boolean') return opts.electronHost;
|
|
10443
|
+
if (process.env.YOTTA_LAN_ELECTRON_HOST === '1') return true;
|
|
10444
|
+
if (process.env.YOTTA_LAN_ELECTRON_HOST === '0') return false;
|
|
10445
|
+
return !!(process.versions && process.versions.electron);
|
|
10446
|
+
}
|
|
10447
|
+
// 任务 / 兜底命令是否指向 Electron 可执行文件且缺少 ELECTRON_RUN_AS_NODE(旧任务告警用)
|
|
10448
|
+
function lanTaskCommandNeedsElectronEnv(text) {
|
|
10449
|
+
const t = String(text || '');
|
|
10450
|
+
if (!t || /ELECTRON_RUN_AS_NODE/.test(t)) return false;
|
|
10451
|
+
if (/electron|yottacode/i.test(t)) return true;
|
|
10452
|
+
if (lanElectronHost({})) {
|
|
10453
|
+
const exe = String(process.execPath || '');
|
|
10454
|
+
if (exe && t.toLowerCase().indexOf(exe.toLowerCase()) !== -1) return true;
|
|
10455
|
+
}
|
|
10456
|
+
return false;
|
|
10457
|
+
}
|
|
10417
10458
|
// sh-style single-quote quoting (crontab line is executed via /bin/sh -c)
|
|
10418
10459
|
function shQuote(s) {
|
|
10419
10460
|
const p = String(s);
|
|
@@ -10448,13 +10489,16 @@ function lanLogPath(opts) {
|
|
|
10448
10489
|
return path.join(os.homedir(), '.yottamemory', 'serve-' + (opts.port || 8787) + '.log');
|
|
10449
10490
|
}
|
|
10450
10491
|
function lanAutostartCmdContent(opts) {
|
|
10492
|
+
opts = opts || {};
|
|
10451
10493
|
const host = opts.host || '0.0.0.0';
|
|
10452
10494
|
const port = opts.port || 8787;
|
|
10453
10495
|
const q = (p) => '"' + String(p).replace(/"/g, '""') + '"';
|
|
10454
10496
|
// 生成文件用纯 ASCII(避免 cmd/VBS 按系统代码页读取中文注释乱码;VBS 自愈内联同一内容)
|
|
10497
|
+
// v0.22.5: Electron 宿主下需先设 ELECTRON_RUN_AS_NODE=1,否则拉起桌面程序本体而非 Node。
|
|
10455
10498
|
return '@echo off\r\n'
|
|
10456
10499
|
+ 'chcp 65001 >nul\r\n'
|
|
10457
10500
|
+ 'rem ' + LAN_GEN_MARKER + '; remove with: yotta-memory lan disable (English only, keep ASCII)\r\n'
|
|
10501
|
+
+ (lanElectronHost(opts) ? 'set "ELECTRON_RUN_AS_NODE=1"\r\n' : '')
|
|
10458
10502
|
+ q(process.execPath) + ' ' + q(runtimeManagedScript()) + ' serve --host ' + host + ' --port ' + port + ' >> ' + q(lanLogPath(opts)) + ' 2>&1\r\n';
|
|
10459
10503
|
}
|
|
10460
10504
|
function lanVbsContent(opts) {
|
|
@@ -10574,7 +10618,10 @@ function lanWindowsTaskXml(opts) {
|
|
|
10574
10618
|
// 让 node/脚本/日志路径里的空格与中文都能安全传递;S4U 非交互会话下不弹窗口。
|
|
10575
10619
|
// 注意:重定向 `2>&1` 的 & 在 XML 文本里必须写成 &(DOM 解码后仍是 2>&1),
|
|
10576
10620
|
// 否则 schtasks 报 "The task XML is malformed"。
|
|
10577
|
-
|
|
10621
|
+
// v0.22.5: Electron 宿主(YottaCode shim)下先 set ELECTRON_RUN_AS_NODE=1,
|
|
10622
|
+
// 否则任务会拉起桌面程序本体(GUI)而不是 Node 服务。
|
|
10623
|
+
const envPrefix = lanElectronHost(opts) ? 'set "ELECTRON_RUN_AS_NODE=1" && ' : '';
|
|
10624
|
+
const inner = envPrefix + '"' + xmlEscape(nodePath) + '" "' + xmlEscape(scriptPath) + '" serve --host ' + host + ' --port ' + port
|
|
10578
10625
|
+ ' >> "' + xmlEscape(logPath) + '" 2>&1';
|
|
10579
10626
|
const triggerXml = onstart
|
|
10580
10627
|
? '<BootTrigger><Enabled>true</Enabled></BootTrigger>'
|
|
@@ -10616,7 +10663,7 @@ function lanWindowsEnableCore(opts) {
|
|
|
10616
10663
|
const scriptPath = opts.scriptPath || runtimeManagedScript();
|
|
10617
10664
|
const logPath = lanLogPath(opts);
|
|
10618
10665
|
const userId = lanWindowsTaskUser(opts);
|
|
10619
|
-
const xml = lanWindowsTaskXml({ trigger: trigger, host: host, port: port, nodePath: nodePath, scriptPath: scriptPath, logPath: logPath, userId: userId });
|
|
10666
|
+
const xml = lanWindowsTaskXml({ trigger: trigger, host: host, port: port, nodePath: nodePath, scriptPath: scriptPath, logPath: logPath, userId: userId, electronHost: opts.electronHost });
|
|
10620
10667
|
const xmlDir = opts.xmlDir || process.env.YOTTA_LAN_TASK_XML_DIR || os.tmpdir();
|
|
10621
10668
|
const xmlPath = opts.xmlPath || path.join(xmlDir, LAN_TASK_NAME + '-task-' + process.pid + '-' + Date.now() + '.xml');
|
|
10622
10669
|
let failure = null;
|
|
@@ -10668,7 +10715,7 @@ function lanLinuxExecStart(opts) {
|
|
|
10668
10715
|
}
|
|
10669
10716
|
function lanLinuxUnitContent(opts) {
|
|
10670
10717
|
// keep comments ASCII (avoid locale/encoding issues); ExecStart uses systemd's own quote rules
|
|
10671
|
-
|
|
10718
|
+
const lines = [
|
|
10672
10719
|
'# ' + LAN_GEN_MARKER + '; remove with: yotta-memory lan disable (English only, keep ASCII)',
|
|
10673
10720
|
'',
|
|
10674
10721
|
'[Unit]',
|
|
@@ -10676,6 +10723,9 @@ function lanLinuxUnitContent(opts) {
|
|
|
10676
10723
|
'',
|
|
10677
10724
|
'[Service]',
|
|
10678
10725
|
'Type=simple',
|
|
10726
|
+
];
|
|
10727
|
+
if (lanElectronHost(opts)) lines.push('Environment=ELECTRON_RUN_AS_NODE=1');
|
|
10728
|
+
lines.push(
|
|
10679
10729
|
'ExecStart=' + lanLinuxExecStart(opts),
|
|
10680
10730
|
'Restart=on-failure',
|
|
10681
10731
|
'RestartSec=3',
|
|
@@ -10683,12 +10733,15 @@ function lanLinuxUnitContent(opts) {
|
|
|
10683
10733
|
'[Install]',
|
|
10684
10734
|
'WantedBy=default.target',
|
|
10685
10735
|
'',
|
|
10686
|
-
|
|
10736
|
+
);
|
|
10737
|
+
return lines.join('\n');
|
|
10687
10738
|
}
|
|
10688
10739
|
function lanCrontabBin() { return process.env.YOTTA_LAN_CRONTAB_BIN || 'crontab'; }
|
|
10689
10740
|
function lanCrontabLine(opts) {
|
|
10690
|
-
const parts = ['@reboot'
|
|
10691
|
-
|
|
10741
|
+
const parts = ['@reboot'];
|
|
10742
|
+
if (lanElectronHost(opts)) parts.push('ELECTRON_RUN_AS_NODE=1');
|
|
10743
|
+
parts.push(shQuote(process.execPath), shQuote(runtimeManagedScript()));
|
|
10744
|
+
return parts.concat(lanServeArgs(opts)).join(' ') + ' >> ' + shQuote(lanLogPath(opts)) + ' 2>&1 ' + LAN_CRONTAB_MARKER;
|
|
10692
10745
|
}
|
|
10693
10746
|
function lanCrontabRead() {
|
|
10694
10747
|
try {
|
|
@@ -10906,17 +10959,22 @@ function cmdLanWinStatus() {
|
|
|
10906
10959
|
if (q.status === 0) {
|
|
10907
10960
|
taskFound = true;
|
|
10908
10961
|
console.log('计划任务 ' + LAN_TASK_NAME + ': 已启用');
|
|
10909
|
-
const re = /^(状态|Status|登录模式|Logon Mode|上次运行时间|Last Run Time|上次结果|Last Result)\s
|
|
10962
|
+
const re = /^(状态|Status|登录模式|Logon Mode|上次运行时间|Last Run Time|上次结果|Last Result|要运行的任务|Task To Run)\s*[:=]\s*(.+)$/;
|
|
10910
10963
|
let logonMode = '';
|
|
10964
|
+
let taskRun = '';
|
|
10911
10965
|
for (const line of String(q.stdout).split(/\r?\n/)) {
|
|
10912
10966
|
const m = re.exec(line.trim());
|
|
10913
10967
|
if (!m) continue;
|
|
10914
10968
|
console.log(' ' + m[1] + ': ' + m[2]);
|
|
10915
10969
|
if (/^(登录模式|Logon Mode)$/.test(m[1])) logonMode = m[2];
|
|
10970
|
+
if (/^(要运行的任务|Task To Run)$/.test(m[1])) taskRun = m[2];
|
|
10916
10971
|
}
|
|
10917
10972
|
if (/interactive|交互式/i.test(logonMode)) {
|
|
10918
10973
|
console.log(' 提示: 登录模式为交互式(仅登录时运行,且在登录会话会弹窗口)——这是旧版任务;请重新执行 yotta-memory lan enable 修复为后台静默');
|
|
10919
10974
|
}
|
|
10975
|
+
if (lanTaskCommandNeedsElectronEnv(taskRun)) {
|
|
10976
|
+
console.log(' 警告: 任务命令指向 Electron 可执行文件(如 YottaCode.exe)但缺少 ELECTRON_RUN_AS_NODE —— 开机将拉起桌面程序而非服务;请重新执行 yotta-memory lan enable 修复');
|
|
10977
|
+
}
|
|
10920
10978
|
} else {
|
|
10921
10979
|
console.log('计划任务 ' + LAN_TASK_NAME + ': 未启用');
|
|
10922
10980
|
}
|
|
@@ -10926,6 +10984,12 @@ function cmdLanWinStatus() {
|
|
|
10926
10984
|
console.log('Startup 静默自启(免管理员): 已启用');
|
|
10927
10985
|
console.log(' 启动脚本: ' + lanVbsPath());
|
|
10928
10986
|
console.log(' 启动命令: ' + lanAutostartCmdPath());
|
|
10987
|
+
try {
|
|
10988
|
+
const cmdText = fs.readFileSync(lanAutostartCmdPath(), 'utf8');
|
|
10989
|
+
if (lanTaskCommandNeedsElectronEnv(cmdText)) {
|
|
10990
|
+
console.log(' 警告: Startup 兜底脚本缺少 ELECTRON_RUN_AS_NODE —— Electron 宿主下会拉起桌面程序而非服务;请重新执行 yotta-memory lan enable 修复');
|
|
10991
|
+
}
|
|
10992
|
+
} catch (e) { /* 读取失败不影响状态输出 */ }
|
|
10929
10993
|
} else if (vbsOk || cmdOk) {
|
|
10930
10994
|
console.log('Startup 静默自启: 部分残留(建议执行 yotta-memory lan disable 后重新 lan enable)');
|
|
10931
10995
|
} else {
|
|
@@ -12160,6 +12224,7 @@ module.exports = {
|
|
|
12160
12224
|
lanServeArgs: lanServeArgs,
|
|
12161
12225
|
lanSpawn: lanSpawn,
|
|
12162
12226
|
lanPlatform: lanPlatform,
|
|
12227
|
+
lanElectronHost: lanElectronHost,
|
|
12163
12228
|
shQuote: shQuote,
|
|
12164
12229
|
systemdEscapeArg: systemdEscapeArg,
|
|
12165
12230
|
lanLinuxSystemdUserDir: lanLinuxSystemdUserDir,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yottameta/yotta-memory",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.5",
|
|
4
4
|
"description": "Yuanyi (元忆) — boundary-aware, file-based memory for AI agents. File-based, zero-dependency, diff/rollback-able; FACT/PREF/BOUND/COMMIT types (public shared / private isolated), user-level + project-level storage; v0.12 reliability baseline (init guard, trash-based deletion, independent-volume backup/list/doctor/restore, start-of-work doctor, transactional snapshots before destructive writes), v0.10 consolidation (periodic summaries with provenance, near-duplicate auto-merge, per-type decay, batch audit + rollback), v0.9 recall quality + context focus + optional local embedding plugin, plus v0.8 semantic search, feedback loop, self-organization and distillation.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|