@yottameta/yotta-memory 0.5.2 → 0.5.4

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 CHANGED
@@ -40,7 +40,7 @@
40
40
  | **检索与生命周期** | 索引 + TF 打分(中文分词友好);质量分 / 盖棺分 / 自动归档,记忆库不会无限膨胀 |
41
41
  | **生态分发** | GitHub + npm 双源同步发布;npx skills / npm / install.sh 三种方式,覆盖 17+ 类智能体目录 |
42
42
  | **便携记忆盘(随盘走)** | 记忆库即引擎:装在硬盘 / 主机上,插上即恢复全部记忆;引擎主机只需装 CLI 当存放点,无需装任何 AI 智能体 |
43
- | **局域网共享与自启** | 每智能体独立 token(Bearer + X-Agent-Id)鉴权、可吊销;`lan enable` 注册开机自启(默认登录自启,可选开机即启);MCP 工具集与 CLI 一致(8 个工具),管理动作不远程暴露 |
43
+ | **局域网共享与自启** | 每智能体独立 token(Bearer + X-Agent-Id)鉴权、可吊销;`lan enable` 注册开机自启(Windows:优先计划任务;非管理员自动降级用户级 Startup 静默自启);MCP 工具集与 CLI 一致(8 个工具),管理动作不远程暴露 |
44
44
  | **本地 / 局域网双模式** | 本地 `serve --stdio` 零进程、按需拉起(无常驻);局域网 streamable HTTP 常驻——两种模式可并存、按需选用 |
45
45
 
46
46
  ## 功能详解
@@ -106,7 +106,7 @@
106
106
 
107
107
  - **记忆库即引擎**:`serve` 把记忆目录挂成 MCP 服务,目录随盘走;引擎主机只需装 CLI 当存放点,无需装任何 AI 智能体。
108
108
  - **双模式可并存**:本地 `serve --stdio` 零进程(客户端按需拉起);局域网 streamable HTTP(默认 `0.0.0.0:8787`)+ 每智能体 token 鉴权。
109
- - **开机自启**:`lan enable` 注册 Windows 计划任务(默认登录自启,`--onstart` 开机即启需管理员);`lan disable` 移除,`lan status` 查询。
109
+ - **开机自启**:`lan enable` 注册 Windows 开机自启——优先计划任务(默认登录自启,`--onstart` 开机即启需管理员);非管理员(计划任务被拒)自动降级为**用户级 Startup 静默自启**(免管理员);`lan disable` 移除,`lan status` 查询。
110
110
  - **安全边界**:管理动作(init / config / token / lan / serve)不进 MCP,token 不远程暴露;远程智能体只能读写记忆,不能改配置、不能管 token。
111
111
  - 完整操作步骤见上文「局域网多机共享」章节与 [USER_GUIDE.md](USER_GUIDE.md)。
112
112
 
@@ -185,7 +185,7 @@ npm i -g @yottameta/yotta-memory
185
185
  | `yotta-memory iam <id> [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一)|
186
186
  | `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 为智能体生成 / 列出 / 吊销访问 token(登记于记忆库 `.server/tokens.json`;同 ID 已被其它来源占用需 `--force` 覆盖,防不同智能体合流)|
187
187
  | `yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio]` | 启动 MCP 记忆引擎(streamable HTTP 局域网 / --stdio 本地零进程模式;Bearer token + X-Agent-Id 鉴权)|
188
- | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows 计划任务,默认 ONLOGON 登录自启;--onstart 开机即启需管理员)|
188
+ | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务,默认 ONLOGON 登录自启、--onstart 开机即启需管理员;非管理员自动降级用户级 Startup 静默自启)|
189
189
 
190
190
  类型:`FACT`(事实,公共共享)/ `PREF`(偏好)/ `BOUND`(边界)/ `COMMIT`(承诺)。
191
191
 
@@ -226,10 +226,10 @@ yotta-memory recall --type FACT --limit 10
226
226
  3. 启动服务(默认监听 0.0.0.0:8787,Bearer token + X-Agent-Id 鉴权)——临时运行或注册开机自启二选一:
227
227
  ```bash
228
228
  yotta-memory serve # 临时前台运行
229
- yotta-memory lan enable # 注册开机自启(Windows 计划任务,登录自启)
229
+ yotta-memory lan enable # 注册开机自启(Windows:计划任务;非管理员自动降级用户级 Startup 静默自启)
230
230
  yotta-memory lan status # 查看自启状态
231
231
  ```
232
- > `lan enable --onstart` 改为开机即启(需管理员);`lan disable` 移除自启。
232
+ > `lan enable --onstart` 改为开机即启(需管理员);非管理员时 `lan enable` 自动改用用户级 Startup 静默自启(无需管理员);`lan disable` 移除自启。
233
233
 
234
234
  > 首次监听 0.0.0.0 时,Windows / 系统防火墙可能询问是否放行,需允许放行,否则局域网内其它主机无法访问;`--no-auth` 会关闭鉴权,仅限可信内网使用。
235
235
 
package/SKILL.md CHANGED
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: yotta-memory
3
3
  description: "元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/回滚:让任何 AI 智能体活过会话,开工 recall 恢复上下文、重要信息 remember 落盘、收工归档。类型体系 FACT(公共共享)/ PREF / BOUND / COMMIT(私密隔离)。触发:记住、别忘了、记一笔、记忆、remember、recall、跨会话、上次说到、续测、交接、归档、记忆盘、共享记忆、局域网记忆"
4
- version: 0.5.2
4
+ version: 0.5.4
5
5
  license: MIT
6
6
  ---
7
7
 
8
8
  # yotta-memory(元忆)— 有权限边界的文件式智能体记忆
9
9
 
10
10
  > 一句话:元忆 —— 有权限边界的文件式智能体记忆(不注入、可 diff、能回滚;FACT 共享、PREF / BOUND / COMMIT 私密隔离)。
11
- > 版本:0.5.2 | 最后更新:2026-08-25
11
+ > 版本:0.5.4 | 最后更新:2026-08-25
12
12
 
13
13
  ## 这是什么
14
14
 
@@ -79,7 +79,7 @@ license: MIT
79
79
  | `yotta-memory iam <id> [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一)|
80
80
  | `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 每智能体访问 token:生成 / 列出 / 吊销(登记 `<记忆库>/.server/tokens.json`;同 ID 已被其它来源占用需 `--force` 覆盖,防不同智能体合流)|
81
81
  | `yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio]` | 启动 MCP 记忆引擎(streamable HTTP 局域网 / --stdio 本地零进程模式;Bearer token + X-Agent-Id 鉴权)|
82
- | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows 计划任务,默认 ONLOGON 登录自启;--onstart 开机即启需管理员)|
82
+ | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务,默认 ONLOGON 登录自启、--onstart 开机即启需管理员;非管理员自动降级用户级 Startup 静默自启)|
83
83
 
84
84
  ## 存储格式(摘要)
85
85
 
@@ -137,7 +137,7 @@ yotta-memory --version # 或 command -v yotta-memory / where yotta-memory
137
137
  > 记忆盘场景:硬盘插上 → AI 检查盘上目录是否有数据 → 有则接入 + config 记住 → 插盘即恢复,机器记住位置。
138
138
 
139
139
  **步骤 0.5 启动记忆引擎(仅引擎主机,供远程接入)**
140
- - 本机若作引擎:🔒 **征得同意后**启动服务——临时运行 `yotta-memory serve`(默认 `0.0.0.0:8787`,Bearer token + X-Agent-Id 鉴权;`--no-auth` 仅限可信内网),或注册开机自启 `yotta-memory lan enable`(Windows 计划任务,默认登录自启;`--onstart` 开机即启需管理员)。
140
+ - 本机若作引擎:🔒 **征得同意后**启动服务——临时运行 `yotta-memory serve`(默认 `0.0.0.0:8787`,Bearer token + X-Agent-Id 鉴权;`--no-auth` 仅限可信内网),或注册开机自启 `yotta-memory lan enable`(Windows:优先计划任务,默认登录自启;非管理员自动降级用户级 Startup 静默自启,免管理员)。
141
141
  - 本地零进程模式:本机 AI 也可用 `serve --stdio` 由 MCP 客户端按需拉起 CLI(无常驻进程,照灵魂盘)。
142
142
  - 远程客户端接入前,先确认引擎主机 serve 已运行(`lan status` 可查)。
143
143
 
package/USER_GUIDE.md CHANGED
@@ -111,7 +111,7 @@ yotta-memory init --dir /srv/yotta-memory # 新库:初始化(
111
111
  **第 3 步:注册开机自启(可选,推荐)**
112
112
 
113
113
  ```bash
114
- # Windows:内置命令(计划任务)
114
+ # Windows:内置命令(优先计划任务;非管理员自动降级用户级 Startup 静默自启)
115
115
  yotta-memory lan enable # 登录后自动启动(默认)
116
116
  yotta-memory lan enable --onstart # 开机即启(需管理员)
117
117
  yotta-memory lan status # 查看状态
@@ -242,7 +242,7 @@ yotta-memory remember FACT 主题 内容 # 智能体落盘
242
242
  | `yotta-memory iam <id> [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一)|
243
243
  | `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 访问 token(同 ID 已被其它来源占用需 `--force` 覆盖)|
244
244
  | `yotta-memory serve [--port 8787] [--stdio] [--no-auth]` | 启动记忆引擎(--no-auth 关闭鉴权,仅限可信内网)|
245
- | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理 |
245
+ | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务;非管理员自动降级用户级 Startup 静默自启)|
246
246
 
247
247
  类型:`FACT`(事实,共享)/ `PREF`(偏好)/ `BOUND`(边界)/ `COMMIT`(承诺),后三类按智能体物理分目录隔离(`private/<owner>/<type>/`)。
248
248
 
@@ -259,7 +259,7 @@ yotta-memory remember FACT 主题 内容 # 智能体落盘
259
259
  - 返回 401:服务在运行,是鉴权问题(token / 请求头不对)。
260
260
  - 连接被拒 / 超时:服务没启动,或防火墙拦截。
261
261
 
262
- **`lan enable` 失败提示 Access denied:** 需要管理员权限,请用管理员终端重新执行;`--onstart` 模式必须管理员。
262
+ **`lan enable` 计划任务被拒(Access denied 等):** 无需担心——会自动降级为**用户级 Startup 静默自启**(免管理员,重新登录后生效,启动脚本位于用户 Startup 目录);如确需计划任务,请用管理员终端重新执行;`--onstart` 模式必须管理员。
263
263
 
264
264
  **启动引擎报端口被占用(EADDRINUSE):** 用 `--port <其它端口>` 换端口启动,两端保持一致;或先查占用:Linux `ss -tlnp | grep 8787`,Windows `netstat -ano | findstr 8787`。
265
265
 
@@ -3,6 +3,8 @@
3
3
  // v0.4.0 新增:lan(Windows 计划任务开机自启 serve)/ init --dir(显式指定位置)/ serve --stdio(本地零进程模式)/ MCP 工具补 reindex/export/import
4
4
  // v0.5.0 新增:隐私硬隔离——私密物理分目录 private/<agent_id>/{prefs,bounds,commits}/ + 关闭 --agent 越权读 + 私密写跨智能体需 --unsafe + 禁 shell 直读写(文档红线)
5
5
  // v0.4.2 新增:iam/whoami 智能体身份自注册(agents.json 唯一性)+ 自我档案 PREF + 私密记忆须声明 owner
6
+ // v0.5.3 新增:CLI 选项可前置(--agent 等允许放在子命令前,不再误报"未知命令")/ lan enable 成功补"服务不会立刻启动"提示(v0.5.2 lan 引号修复无回归)
7
+ // v0.5.4 新增:lan enable 在非管理员(schtasks Access denied)时自动降级为用户级 Startup 静默自启(VBS sh.Run 窗口0 + autostart.cmd,node 路径 process.execPath 自动探测);lan disable/status 同时管理计划任务与 Startup 双机制
6
8
  'use strict';
7
9
 
8
10
  const fs = require('fs');
@@ -12,7 +14,7 @@ const crypto = require('crypto');
12
14
  const http = require('http');
13
15
  const child_process = require('child_process');
14
16
 
15
- const VERSION = '0.5.2';
17
+ const VERSION = '0.5.4';
16
18
  const TYPES = ['FACT', 'PREF', 'BOUND', 'COMMIT'];
17
19
  const TYPE_DIRS = { FACT: 'facts', PREF: 'prefs', BOUND: 'bounds', COMMIT: 'commits' };
18
20
  const PUBLIC_DIR = 'facts';
@@ -959,8 +961,11 @@ function cmdServeStdio() {
959
961
  process.stdin.on('end', function () { process.exit(0); });
960
962
  }
961
963
 
962
- // ---- lan 命令(Windows 计划任务管理开机自启 serve)----
964
+ // ---- lan 命令(Windows 开机自启管理 serve)----
965
+ // 双机制:① schtasks 计划任务(默认,需相应权限);② 非管理员(Access denied)自动降级为
966
+ // 用户级 Startup 静默自启(VBS sh.Run <autostart.cmd>, 0, False + autostart.cmd;node 路径 process.execPath 自动探测,0.5.4)
963
967
  const LAN_TASK_NAME = 'YottaMemoryServe';
968
+ const LAN_GEN_MARKER = 'Generated by yotta-memory lan enable';
964
969
  function lanTaskRunCmd(opts) {
965
970
  const host = opts.host || '0.0.0.0';
966
971
  const port = opts.port || 8787;
@@ -968,58 +973,158 @@ function lanTaskRunCmd(opts) {
968
973
  const q = (p) => /[\s"]/.test(p) ? '\\"' + p.replace(/"/g, '\\"') + '\\"' : p;
969
974
  return q(process.execPath) + ' ' + q(__filename) + ' serve --host ' + host + ' --port ' + port;
970
975
  }
976
+ function lanStartupDir() {
977
+ // 测试/高级用途可用 YOTTA_LAN_STARTUP_DIR 覆盖(默认用户级 Startup 目录,免管理员)
978
+ if (process.env.YOTTA_LAN_STARTUP_DIR) return process.env.YOTTA_LAN_STARTUP_DIR;
979
+ const base = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
980
+ return path.join(base, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
981
+ }
982
+ function lanAutostartDir() {
983
+ // 产品自有目录 ~/.yottamemory/autostart/(引擎数据目录内,非系统配置);测试可用 YOTTA_LAN_AUTOSTART_DIR 覆盖
984
+ if (process.env.YOTTA_LAN_AUTOSTART_DIR) return process.env.YOTTA_LAN_AUTOSTART_DIR;
985
+ return path.join(os.homedir(), '.yottamemory', 'autostart');
986
+ }
987
+ function lanVbsPath() { return path.join(lanStartupDir(), 'yotta-memory-serve.vbs'); }
988
+ function lanAutostartCmdPath() { return path.join(lanAutostartDir(), 'yotta-memory-autostart.cmd'); }
989
+ function lanLogPath(opts) {
990
+ if (process.env.YOTTA_LAN_LOG_FILE) return process.env.YOTTA_LAN_LOG_FILE;
991
+ return path.join(os.homedir(), '.yottamemory', 'serve-' + (opts.port || 8787) + '.log');
992
+ }
993
+ function lanAutostartCmdContent(opts) {
994
+ const host = opts.host || '0.0.0.0';
995
+ const port = opts.port || 8787;
996
+ const q = (p) => '"' + String(p).replace(/"/g, '""') + '"';
997
+ // 生成文件用纯 ASCII(避免 cmd/VBS 按系统代码页读取中文注释乱码)
998
+ return '@echo off\r\n'
999
+ + 'chcp 65001 >nul\r\n'
1000
+ + 'rem ' + LAN_GEN_MARKER + '; remove with: yotta-memory lan disable\r\n'
1001
+ + q(process.execPath) + ' ' + q(__filename) + ' serve --host ' + host + ' --port ' + port + ' >> ' + q(lanLogPath(opts)) + ' 2>&1\r\n';
1002
+ }
1003
+ function lanVbsContent() {
1004
+ return "' " + LAN_GEN_MARKER + '; remove with: yotta-memory lan disable\r\n'
1005
+ + 'Set sh = CreateObject("WScript.Shell")\r\n'
1006
+ + 'sh.Run "' + String(lanAutostartCmdPath()).replace(/"/g, '""') + '", 0, False\r\n';
1007
+ }
1008
+ function lanInstallStartup(opts) {
1009
+ fs.mkdirSync(lanAutostartDir(), { recursive: true });
1010
+ fs.writeFileSync(lanAutostartCmdPath(), lanAutostartCmdContent(opts), 'utf8');
1011
+ fs.mkdirSync(lanStartupDir(), { recursive: true });
1012
+ // VBS 用 UTF-16LE+BOM:wscript 按 Unicode 读取,中文路径不乱码(纯 ANSI 读取会按系统代码页误读)
1013
+ fs.writeFileSync(lanVbsPath(), '\ufeff' + lanVbsContent(), 'utf16le');
1014
+ }
1015
+ function lanRemoveStartupFiles() {
1016
+ // 只删除带产品标记的生成文件,绝不误删用户自己的 Startup 文件
1017
+ const removed = [];
1018
+ for (const f of [lanVbsPath(), lanAutostartCmdPath()]) {
1019
+ try {
1020
+ if (lanFileHasMarker(f)) { fs.unlinkSync(f); removed.push(f); }
1021
+ } catch (e) { /* 单个文件失败不阻断其它清理 */ }
1022
+ }
1023
+ return removed;
1024
+ }
1025
+ function lanFileHasMarker(f) {
1026
+ try {
1027
+ if (!fs.existsSync(f)) return false;
1028
+ const buf = fs.readFileSync(f);
1029
+ const m8 = Buffer.from(LAN_GEN_MARKER, 'utf8');
1030
+ const m16 = Buffer.from(LAN_GEN_MARKER, 'utf16le');
1031
+ return buf.indexOf(m8) !== -1 || buf.indexOf(m16) !== -1;
1032
+ } catch (e) { return false; }
1033
+ }
1034
+ function isSchtasksAccessDenied(e) {
1035
+ const msg = String((e && e.stderr) || (e && e.message) || e);
1036
+ return /access\s+is\s+denied|access\s+denied|拒绝访问/i.test(msg);
1037
+ }
971
1038
  function cmdLanEnable(opts) {
972
1039
  if (process.platform !== 'win32') {
973
- console.error('lan 命令当前仅支持 Windows(计划任务);本机平台: ' + process.platform);
1040
+ console.error('lan 命令当前仅支持 Windows(计划任务 / Startup 自启);本机平台: ' + process.platform);
974
1041
  process.exit(2);
975
1042
  }
976
1043
  const trigger = opts.onstart ? 'onstart' : 'onlogon';
977
1044
  const tr = lanTaskRunCmd(opts);
978
1045
  try {
979
1046
  child_process.execFileSync('schtasks', ['/create', '/tn', LAN_TASK_NAME, '/tr', tr, '/sc', trigger, '/f'], { stdio: 'inherit' });
1047
+ console.log('已注册开机自启: 计划任务 ' + LAN_TASK_NAME + '(触发器 ' + trigger + ')');
1048
+ console.log('运行命令: ' + tr);
1049
+ console.log('备注: 服务不会立刻启动,需重启/重新登录后自动启动;如需现在运行请执行 yotta-memory serve');
1050
+ return;
980
1051
  } catch (e) {
981
- console.error('注册计划任务失败(ONSTART 需要管理员权限;ONLOGON 一般不需要;如仍失败请用管理员终端执行): ' + String((e && e.stderr) || (e && e.message) || e).split(/\r?\n/)[0]);
982
- process.exit(1);
1052
+ const firstLine = String((e && e.stderr) || (e && e.message) || e).split(/\r?\n/)[0];
1053
+ if (isSchtasksAccessDenied(e)) console.error('计划任务注册被拒绝(当前用户非管理员,Access denied): ' + firstLine);
1054
+ else console.error('计划任务注册失败(当前环境不可用计划任务): ' + firstLine);
1055
+ console.log('自动改用用户级 Startup 静默自启(免管理员)...');
1056
+ try {
1057
+ lanInstallStartup(opts);
1058
+ } catch (e2) {
1059
+ console.error('写 Startup 自启失败: ' + String((e2 && e2.message) || e2).split(/\r?\n/)[0]);
1060
+ console.error('请用管理员终端执行 yotta-memory lan enable,或将以下命令手动加入启动项: ' + tr);
1061
+ process.exit(1);
1062
+ }
1063
+ console.log('已启用用户级 Startup 静默自启(无需管理员)');
1064
+ console.log('启动脚本: ' + lanVbsPath());
1065
+ console.log('启动命令: ' + lanAutostartCmdPath());
1066
+ console.log('日志: ' + lanLogPath(opts));
1067
+ console.log('备注: 服务不会立刻启动,需重新登录后自动启动;如需现在运行请执行 yotta-memory serve');
1068
+ console.log('提示: 如需改回计划任务,请用管理员终端重新执行 yotta-memory lan enable');
1069
+ return;
983
1070
  }
984
- console.log('已注册开机自启: 计划任务 ' + LAN_TASK_NAME + '(触发器 ' + trigger + ')');
985
- console.log('运行命令: ' + tr);
986
1071
  }
987
1072
  function cmdLanDisable() {
988
1073
  if (process.platform !== 'win32') {
989
- console.error('lan 命令当前仅支持 Windows(计划任务);本机平台: ' + process.platform);
1074
+ console.error('lan 命令当前仅支持 Windows(计划任务 / Startup 自启);本机平台: ' + process.platform);
990
1075
  process.exit(2);
991
1076
  }
1077
+ let sawError = false;
1078
+ let removedAny = false;
992
1079
  try {
993
- child_process.execFileSync('schtasks', ['/delete', '/tn', LAN_TASK_NAME, '/f'], { stdio: ['ignore', 'pipe', 'ignore'] });
1080
+ child_process.execFileSync('schtasks', ['/delete', '/tn', LAN_TASK_NAME, '/f'], { stdio: ['ignore', 'pipe', 'pipe'] });
1081
+ console.log('已移除开机自启计划任务 ' + LAN_TASK_NAME);
1082
+ removedAny = true;
994
1083
  } catch (e) {
995
1084
  const msg = String((e && e.stderr) || (e && e.message) || e);
996
- if (msg.indexOf('cannot find the path') !== -1 || msg.indexOf('没有找到') !== -1) {
997
- console.log('未启用: 计划任务 ' + LAN_TASK_NAME + ' 不存在,无需移除');
998
- return;
1085
+ if (!/cannot find the (path|file)|没有找到|找不到|不存在/i.test(msg)) {
1086
+ console.error('移除计划任务失败: ' + msg.split(/\r?\n/)[0]);
1087
+ sawError = true;
999
1088
  }
1000
- console.error('移除计划任务失败: ' + msg.split(/\r?\n/)[0]);
1001
- process.exit(1);
1002
1089
  }
1003
- console.log('已移除开机自启计划任务 ' + LAN_TASK_NAME);
1090
+ for (const f of lanRemoveStartupFiles()) {
1091
+ console.log('已移除 Startup 自启文件: ' + f);
1092
+ removedAny = true;
1093
+ }
1094
+ if (!removedAny && !sawError) console.log('未启用: 未发现任何自启配置(计划任务与 Startup 文件均不存在)');
1095
+ if (sawError) process.exit(1);
1004
1096
  }
1005
1097
  function cmdLanStatus() {
1006
1098
  if (process.platform !== 'win32') {
1007
- console.log('lan 命令当前仅支持 Windows(计划任务);本机平台: ' + process.platform);
1099
+ console.log('lan 命令当前仅支持 Windows(计划任务 / Startup 自启);本机平台: ' + process.platform);
1008
1100
  return;
1009
1101
  }
1102
+ let taskFound = false;
1010
1103
  try {
1011
1104
  const out = child_process.execFileSync('schtasks', ['/query', '/tn', LAN_TASK_NAME, '/fo', 'LIST', '/v'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
1012
- console.log('已启用: 计划任务 ' + LAN_TASK_NAME + ' 存在');
1105
+ taskFound = true;
1106
+ console.log('计划任务 ' + LAN_TASK_NAME + ': 已启用');
1013
1107
  const re = /^(状态|Status)\s*:\s*(.+)$/;
1014
1108
  for (const line of String(out).split(/\r?\n/)) {
1015
1109
  const m = re.exec(line.trim());
1016
- if (m) console.log(m[1] + ': ' + m[2]);
1110
+ if (m) console.log(' ' + m[1] + ': ' + m[2]);
1017
1111
  }
1018
1112
  } catch (e) {
1019
- console.log('未启用: 计划任务 ' + LAN_TASK_NAME + ' 不存在(可用 yotta-memory lan enable 注册)');
1113
+ console.log('计划任务 ' + LAN_TASK_NAME + ': 未启用');
1020
1114
  }
1115
+ const vbsOk = lanFileHasMarker(lanVbsPath());
1116
+ const cmdOk = lanFileHasMarker(lanAutostartCmdPath());
1117
+ if (vbsOk && cmdOk) {
1118
+ console.log('Startup 静默自启(免管理员): 已启用');
1119
+ console.log(' 启动脚本: ' + lanVbsPath());
1120
+ console.log(' 启动命令: ' + lanAutostartCmdPath());
1121
+ } else if (vbsOk || cmdOk) {
1122
+ console.log('Startup 静默自启: 部分残留(建议执行 yotta-memory lan disable 后重新 lan enable)');
1123
+ } else {
1124
+ console.log('Startup 静默自启(免管理员): 未启用');
1125
+ }
1126
+ if (!taskFound && !vbsOk && !cmdOk) console.log('未启用任何开机自启(可用 yotta-memory lan enable 注册)');
1021
1127
  }
1022
-
1023
1128
  // ---- usage / main ----
1024
1129
  function usage() {
1025
1130
  console.log([
@@ -1042,7 +1147,7 @@ function usage() {
1042
1147
  ' yotta-memory token list 列出已登记智能体',
1043
1148
  ' yotta-memory token revoke --agent <id> 吊销某智能体 token',
1044
1149
  ' yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio] 启动 MCP 记忆引擎(streamable HTTP;--stdio 本地零进程模式)',
1045
- ' yotta-memory lan enable [--onstart] | disable | status 开机自启管理(Windows 计划任务,默认 ONLOGON)',
1150
+ ' yotta-memory lan enable [--onstart] | disable | status 开机自启管理(Windows 计划任务,默认 ONLOGON;非管理员自动降级用户级 Startup 静默自启)',
1046
1151
  ' yotta-memory --version 版本',
1047
1152
  '',
1048
1153
  '类型: FACT(公共共享) / PREF(偏好) / BOUND(边界) / COMMIT(承诺)',
@@ -1055,14 +1160,13 @@ function usage() {
1055
1160
  function main() {
1056
1161
  const args = process.argv.slice(2);
1057
1162
  if (!args.length) { usage(); return; }
1058
- const first = args[0];
1059
- if (first === '--version' || first === '-v') { console.log(VERSION); return; }
1060
- if (first === '--help' || first === '-h') { usage(); return; }
1061
1163
  const opts = {};
1062
1164
  const positional = [];
1063
1165
  const valueOpts = new Set(['--type', '--limit', '--days', '--out', '--owner', '--agent', '--threshold', '--scope', '--host', '--port', '--dir']);
1064
- for (let i = 1; i < args.length; i++) {
1166
+ for (let i = 0; i < args.length; i++) {
1065
1167
  const a = args[i];
1168
+ if (a === '--version' || a === '-v') { console.log(VERSION); return; }
1169
+ if (a === '--help' || a === '-h') { usage(); return; }
1066
1170
  if (a === '--project') opts.project = true;
1067
1171
  else if (a === '--all') opts.all = true;
1068
1172
  else if (a === '--unsafe') opts.unsafe = true;
@@ -1090,26 +1194,28 @@ function main() {
1090
1194
  positional.push(a);
1091
1195
  }
1092
1196
  }
1197
+ const first = positional[0];
1198
+ const rest = positional.slice(1);
1093
1199
  switch (first) {
1094
1200
  case 'init': cmdInit(opts); break;
1095
1201
  case 'whoami': cmdWhoami(); break;
1096
- case 'iam': cmdIam(positional[0], opts); break;
1202
+ case 'iam': cmdIam(rest[0], opts); break;
1097
1203
  case 'config': {
1098
- const sub = positional[0];
1099
- if (sub === 'set') cmdConfigSet(positional[1], positional[2]);
1204
+ const sub = rest[0];
1205
+ if (sub === 'set') cmdConfigSet(rest[1], rest[2]);
1100
1206
  else if (sub === 'get') cmdConfigGet();
1101
1207
  else { console.error('config 子命令: set memory_home <目录> / get'); process.exit(2); }
1102
1208
  break;
1103
1209
  }
1104
- case 'remember': cmdRemember(positional[0], positional[1], positional[2], opts); break;
1105
- case 'recall': cmdRecall(positional[0] || null, opts); break;
1106
- case 'forget': cmdForget(positional[0]); break;
1210
+ case 'remember': cmdRemember(rest[0], rest[1], rest[2], opts); break;
1211
+ case 'recall': cmdRecall(rest[0] || null, opts); break;
1212
+ case 'forget': cmdForget(rest[0]); break;
1107
1213
  case 'archive': cmdArchive(opts); break;
1108
1214
  case 'reindex': cmdReindex(); break;
1109
1215
  case 'export': cmdExport(opts.out); break;
1110
- case 'import': cmdImport(positional[0]); break;
1216
+ case 'import': cmdImport(rest[0]); break;
1111
1217
  case 'token': {
1112
- const sub = positional[0];
1218
+ const sub = rest[0];
1113
1219
  if (sub === 'new') cmdTokenNew(opts.agent, opts);
1114
1220
  else if (sub === 'list') cmdTokenList();
1115
1221
  else if (sub === 'revoke') cmdTokenRevoke(opts.agent);
@@ -1118,7 +1224,7 @@ function main() {
1118
1224
  }
1119
1225
  case 'serve': cmdServe(opts); break;
1120
1226
  case 'lan': {
1121
- const sub = positional[0];
1227
+ const sub = rest[0];
1122
1228
  if (sub === 'enable') cmdLanEnable(opts);
1123
1229
  else if (sub === 'disable') cmdLanDisable();
1124
1230
  else if (sub === 'status') cmdLanStatus();
@@ -1126,6 +1232,7 @@ function main() {
1126
1232
  break;
1127
1233
  }
1128
1234
  default:
1235
+ if (!first) { usage(); return; }
1129
1236
  console.error('未知命令: ' + first);
1130
1237
  usage();
1131
1238
  process.exit(2);
@@ -1151,4 +1258,16 @@ module.exports = {
1151
1258
  collectEntryFiles: collectEntryFiles,
1152
1259
  migrateLayout: migrateLayout,
1153
1260
  typeSubdir: typeSubdir,
1261
+ lanTaskRunCmd: lanTaskRunCmd,
1262
+ lanStartupDir: lanStartupDir,
1263
+ lanAutostartDir: lanAutostartDir,
1264
+ lanVbsPath: lanVbsPath,
1265
+ lanAutostartCmdPath: lanAutostartCmdPath,
1266
+ lanLogPath: lanLogPath,
1267
+ lanAutostartCmdContent: lanAutostartCmdContent,
1268
+ lanVbsContent: lanVbsContent,
1269
+ lanInstallStartup: lanInstallStartup,
1270
+ lanRemoveStartupFiles: lanRemoveStartupFiles,
1271
+ lanFileHasMarker: lanFileHasMarker,
1272
+ isSchtasksAccessDenied: isSchtasksAccessDenied,
1154
1273
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yottameta/yotta-memory",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/回滚;FACT/PREF/BOUND/COMMIT 四分类(公共共享/私密隔离),用户级+项目级双级存储。",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -1,4 +1,4 @@
1
- # yotta-memory 协议规范 v0.5.2
1
+ # yotta-memory 协议规范 v0.5.4
2
2
 
3
3
  > 本文件定义 yotta-memory 记忆标准:存储位置、目录结构、文件格式、类型体系与 CLI 命令参考。
4
4
  > 目标:任何支持 Agent Skills 开放标准的智能体,装完即可读写同一份记忆。