@yottameta/yotta-memory 0.6.3 → 0.6.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 +4 -4
- package/SKILL.md +3 -3
- package/USER_GUIDE.md +11 -20
- package/bin/yotta-memory.js +277 -8
- package/package.json +1 -1
- package/references/protocol.md +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
| **越用越懂(v0.6.0)** | `profile` 画像聚合(零推断)+ `context` 开工上下文包(身份 / 画像 / 近期记忆 / 边界 / 承诺)+ SKILL「记忆守则」规则层,记忆随使用成长 |
|
|
44
44
|
| **生态分发** | GitHub + npm 双源同步发布;npx skills / npm / install.sh 三种方式,覆盖 17+ 类智能体目录 |
|
|
45
45
|
| **便携记忆盘(随盘走)** | 记忆库即引擎:装在硬盘 / 主机上,插上即恢复全部记忆;引擎主机只需装 CLI 当存放点,无需装任何 AI 智能体 |
|
|
46
|
-
| **局域网共享与自启** | 每智能体独立 token(Bearer + X-Agent-Id)鉴权、可吊销;`lan enable` 注册开机自启(Windows
|
|
46
|
+
| **局域网共享与自启** | 每智能体独立 token(Bearer + X-Agent-Id)鉴权、可吊销;`lan enable` 注册开机自启(Windows:优先计划任务,非管理员自动降级用户级 Startup 静默自启;Linux:systemd 用户单元,不可用时自动降级用户 crontab @reboot);MCP 工具集与 CLI 一致(8 个工具),管理动作不远程暴露 |
|
|
47
47
|
| **本地 / 局域网双模式** | 本地 `serve --stdio` 零进程、按需拉起(无常驻);局域网 streamable HTTP 常驻——两种模式可并存、按需选用 |
|
|
48
48
|
|
|
49
49
|
## 功能详解
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
|
|
116
116
|
- **记忆库即引擎**:`serve` 把记忆目录挂成 MCP 服务,目录随盘走;引擎主机只需装 CLI 当存放点,无需装任何 AI 智能体。
|
|
117
117
|
- **双模式可并存**:本地 `serve --stdio` 零进程(客户端按需拉起);局域网 streamable HTTP(默认 `0.0.0.0:8787`)+ 每智能体 token 鉴权。
|
|
118
|
-
- **开机自启**:`lan enable`
|
|
118
|
+
- **开机自启**:`lan enable` 注册开机自启——Windows 优先计划任务(默认登录自启,`--onstart` 开机即启需管理员),非管理员自动降级为**用户级 Startup 静默自启**(免管理员,启动脚本内联启动命令、被清理也会在开机时自动重建,v0.6.3 起不再弹 80070002);Linux 优先 **systemd 用户单元**(`systemctl --user`,登录自启;`--onstart` 附加 `loginctl enable-linger` 开机即启),systemd 不可用时自动降级**用户 crontab @reboot**(v0.6.4);`lan disable` 移除,`lan status` 查询。
|
|
119
119
|
- **安全边界**:管理动作(init / config / token / lan / serve)不进 MCP,token 不远程暴露;远程智能体只能读写记忆,不能改配置、不能管 token。
|
|
120
120
|
- 完整操作步骤见上文「局域网多机共享」章节与 [USER_GUIDE.md](USER_GUIDE.md)。
|
|
121
121
|
|
|
@@ -196,7 +196,7 @@ npm i -g @yottameta/yotta-memory
|
|
|
196
196
|
| `yotta-memory iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一;可选扩展显示名 / 用户 / 关系)|
|
|
197
197
|
| `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 为智能体生成 / 列出 / 吊销访问 token(登记于记忆库 `.server/tokens.json`;同 ID 已被其它来源占用需 `--force` 覆盖,防不同智能体合流)|
|
|
198
198
|
| `yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio]` | 启动 MCP 记忆引擎(streamable HTTP 局域网 / --stdio 本地零进程模式;Bearer token + X-Agent-Id 鉴权)|
|
|
199
|
-
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务,默认 ONLOGON
|
|
199
|
+
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务,默认 ONLOGON、--onstart 开机即启需管理员,非管理员自动降级用户级 Startup 静默自启;Linux:systemd 用户单元,不可用时自动降级用户 crontab @reboot)|
|
|
200
200
|
|
|
201
201
|
类型:`FACT`(事实,公共共享)/ `PREF`(偏好)/ `BOUND`(边界)/ `COMMIT`(承诺)。
|
|
202
202
|
|
|
@@ -239,7 +239,7 @@ yotta-memory recall --type FACT --limit 10
|
|
|
239
239
|
3. 启动服务(默认监听 0.0.0.0:8787,Bearer token + X-Agent-Id 鉴权)——临时运行或注册开机自启二选一:
|
|
240
240
|
```bash
|
|
241
241
|
yotta-memory serve # 临时前台运行
|
|
242
|
-
yotta-memory lan enable # 注册开机自启(Windows
|
|
242
|
+
yotta-memory lan enable # 注册开机自启(Windows:计划任务/用户级 Startup;Linux:systemd 用户单元/用户 crontab)
|
|
243
243
|
yotta-memory lan status # 查看自启状态
|
|
244
244
|
```
|
|
245
245
|
> `lan enable --onstart` 改为开机即启(需管理员);非管理员时 `lan enable` 自动改用用户级 Startup 静默自启(无需管理员);`lan disable` 移除自启。
|
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、跨会话、上次说到、续测、交接、归档、记忆盘、共享记忆、局域网记忆、画像、开工上下文、记忆守则、profile、context、越用越懂"
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.4
|
|
5
5
|
license: MIT
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# yotta-memory(元忆)— 有权限边界的文件式智能体记忆
|
|
9
9
|
|
|
10
10
|
> 一句话:元忆 —— 有权限边界的文件式智能体记忆(不注入、可 diff、能回滚;FACT 共享、PREF / BOUND / COMMIT 私密隔离)。
|
|
11
|
-
> 版本:0.6.
|
|
11
|
+
> 版本:0.6.4 | 最后更新:2026-08-26
|
|
12
12
|
|
|
13
13
|
## 这是什么
|
|
14
14
|
|
|
@@ -158,7 +158,7 @@ license: MIT
|
|
|
158
158
|
| `yotta-memory iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一;可选扩展显示名 / 用户 / 关系)|
|
|
159
159
|
| `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 每智能体访问 token:生成 / 列出 / 吊销(登记 `<记忆库>/.server/tokens.json`;同 ID 已被其它来源占用需 `--force` 覆盖,防不同智能体合流)|
|
|
160
160
|
| `yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio]` | 启动 MCP 记忆引擎(streamable HTTP 局域网 / --stdio 本地零进程模式;Bearer token + X-Agent-Id 鉴权)|
|
|
161
|
-
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务,默认 ONLOGON
|
|
161
|
+
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务,默认 ONLOGON、--onstart 开机即启需管理员,非管理员自动降级用户级 Startup 静默自启,v0.6.3 起 VBS 自愈不弹 80070002;Linux:systemd 用户单元,不可用时自动降级用户 crontab @reboot)|
|
|
162
162
|
|
|
163
163
|
## 存储格式(摘要)
|
|
164
164
|
|
package/USER_GUIDE.md
CHANGED
|
@@ -131,25 +131,16 @@ yotta-memory lan enable --onstart # 开机即启(需管理员)
|
|
|
131
131
|
yotta-memory lan status # 查看状态
|
|
132
132
|
yotta-memory lan disable # 取消
|
|
133
133
|
|
|
134
|
-
# Linux
|
|
135
|
-
#
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
[Service]
|
|
142
|
-
ExecStart=/usr/local/bin/yotta-memory serve --host 0.0.0.0 --port 8787
|
|
143
|
-
Restart=on-failure
|
|
144
|
-
|
|
145
|
-
[Install]
|
|
146
|
-
WantedBy=multi-user.target
|
|
147
|
-
EOF
|
|
148
|
-
sudo systemctl daemon-reload
|
|
149
|
-
sudo systemctl enable --now yotta-memory
|
|
150
|
-
systemctl status yotta-memory # 查看状态
|
|
134
|
+
# Linux:内置命令(v0.6.4 起)——优先 systemd 用户单元(登录自启);
|
|
135
|
+
# systemd 不可用时自动降级用户 crontab @reboot(开机自启,免管理员)
|
|
136
|
+
yotta-memory lan enable # 登录后自动启动(默认)
|
|
137
|
+
yotta-memory lan enable --onstart # 开机即启(无需登录,需系统支持 loginctl)
|
|
138
|
+
yotta-memory lan status # 查看状态
|
|
139
|
+
yotta-memory lan disable # 取消
|
|
151
140
|
```
|
|
152
141
|
|
|
142
|
+
> 说明:`lan enable` 默认「登录后自启」;如需系统级(整机、开机即启)服务,可手动创建 `/etc/systemd/system/yotta-memory.service`(ExecStart 指向 `which yotta-memory` 实际路径)并 `sudo systemctl enable --now yotta-memory`;桌面环境也可在 `~/.config/autostart/` 放置 `yotta-memory.desktop` 实现图形登录自启。
|
|
143
|
+
|
|
153
144
|
**第 4 步:为局域网其它主机的智能体生成访问 token**
|
|
154
145
|
|
|
155
146
|
```bash
|
|
@@ -258,7 +249,7 @@ yotta-memory remember FACT 主题 内容 # 智能体落盘
|
|
|
258
249
|
| `yotta-memory iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一;可选扩展显示名 / 用户 / 关系)|
|
|
259
250
|
| `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 访问 token(同 ID 已被其它来源占用需 `--force` 覆盖)|
|
|
260
251
|
| `yotta-memory serve [--port 8787] [--stdio] [--no-auth]` | 启动记忆引擎(--no-auth 关闭鉴权,仅限可信内网)|
|
|
261
|
-
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows
|
|
252
|
+
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务/用户级 Startup 静默自启;Linux:systemd 用户单元/用户 crontab @reboot)|
|
|
262
253
|
|
|
263
254
|
类型:`FACT`(事实,共享)/ `PREF`(偏好)/ `BOUND`(边界)/ `COMMIT`(承诺),后三类按智能体物理分目录隔离(`private/<owner>/<type>/`)。
|
|
264
255
|
|
|
@@ -266,7 +257,7 @@ yotta-memory remember FACT 主题 内容 # 智能体落盘
|
|
|
266
257
|
|
|
267
258
|
**远程连不上时,按顺序检查:**
|
|
268
259
|
|
|
269
|
-
1.
|
|
260
|
+
1. 引擎在运行吗?`yotta-memory lan status`(Windows / Linux 通用);Linux 也可 `systemctl --user status yotta-memory-serve.service`(systemd 自启时)或 `ps aux | grep yotta-memory`;也可直接 `yotta-memory --version`。
|
|
270
261
|
2. IP 对吗?引擎主机执行 `hostname -I`(Linux)或 `ipconfig`(Windows)确认。
|
|
271
262
|
3. 端口对吗?默认 8787,两端要一致。
|
|
272
263
|
4. 防火墙放行了吗?Linux:`sudo ufw status`,未放行则 `sudo ufw allow 8787/tcp`;Windows:允许首次监听的入站请求。
|
|
@@ -281,7 +272,7 @@ yotta-memory remember FACT 主题 内容 # 智能体落盘
|
|
|
281
272
|
|
|
282
273
|
**recall 没有结果:** 关键词太细或确实没有这条记忆;先 `recall`(不带关键词)看库里的全部记忆核对。
|
|
283
274
|
|
|
284
|
-
**systemd 自启的引擎看日志:** `journalctl -u yotta-memory -n 50
|
|
275
|
+
**systemd 自启的引擎看日志:** `journalctl --user -u yotta-memory-serve.service -n 50`(系统级手动服务为 `journalctl -u yotta-memory -n 50`)。
|
|
285
276
|
|
|
286
277
|
**记错位置了?** `yotta-memory config get` 查看当前生效位置;`config set memory_home <正确目录>` 改正。
|
|
287
278
|
|
package/bin/yotta-memory.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// v0.6.0 新增:灵魂盘核心——profile(用户画像聚合,零推断)/ context(开工上下文包)/ iam 扩展(--name/--user/--relationship)/ remember --verify(写后回读)与 --no-hint(关闭类型启发式提示)+ SKILL「记忆守则」
|
|
9
9
|
// v0.6.1 新增:灵魂盘机制精髓补齐——context --budget(token 预算,近记忆按剩余预算放行)/ context 内嵌「多智能体接入铁律」段 / remember --source/--weight(来源 + 重要性权重,去重 weight 取 max)/ 近期记忆排序融合 importance(confidence×recency+updated+weight+immutable)
|
|
10
10
|
// v0.6.3 修复:lan 开机自启 VBS 自愈——VBS 内联 autostart.cmd 内容,启动时自动重建 .cmd(根治 80070002:wscript 找不到被引用启动文件)
|
|
11
|
+
// v0.6.4 新增:lan 命令扩展 Linux——systemd 用户单元(systemctl --user enable/start,登录自启;--onstart 附加 loginctl enable-linger 开机即启)/ systemd 不可用时自动降级用户 crontab @reboot;lanPlatform 测试钩子(YOTTA_LAN_PLATFORM)
|
|
11
12
|
// v0.6.2 修复:remember --verify 写后回读改为直查索引 + 权限判定 + 召回匹配性(不再依赖 recall top-N 排序,消除泛化 subject 下偶发误报「回读未命中」)
|
|
12
13
|
'use strict';
|
|
13
14
|
|
|
@@ -18,7 +19,7 @@ const crypto = require('crypto');
|
|
|
18
19
|
const http = require('http');
|
|
19
20
|
const child_process = require('child_process');
|
|
20
21
|
|
|
21
|
-
const VERSION = '0.6.
|
|
22
|
+
const VERSION = '0.6.4';
|
|
22
23
|
const TYPES = ['FACT', 'PREF', 'BOUND', 'COMMIT'];
|
|
23
24
|
const TYPE_DIRS = { FACT: 'facts', PREF: 'prefs', BOUND: 'bounds', COMMIT: 'commits' };
|
|
24
25
|
const PUBLIC_DIR = 'facts';
|
|
@@ -1268,11 +1269,17 @@ function cmdServeStdio() {
|
|
|
1268
1269
|
process.stdin.on('end', function () { process.exit(0); });
|
|
1269
1270
|
}
|
|
1270
1271
|
|
|
1271
|
-
// ---- lan
|
|
1272
|
-
//
|
|
1273
|
-
//
|
|
1272
|
+
// ---- lan command (autostart management for serve) ----
|
|
1273
|
+
// Windows dual mechanism: (1) schtasks scheduled task (default, needs permission); (2) non-admin
|
|
1274
|
+
// (Access denied) auto-fallback to user-level Startup silent autostart (VBS sh.Run <autostart.cmd>,
|
|
1275
|
+
// 0, False + autostart.cmd; node path process.execPath auto-detected, 0.5.4)
|
|
1276
|
+
// Linux (v0.6.4): (1) systemd user unit (systemctl --user enable/start, login autostart; --onstart
|
|
1277
|
+
// additionally runs loginctl enable-linger for boot autostart); (2) auto-fallback to user crontab
|
|
1278
|
+
// @reboot when no systemd user session is available.
|
|
1274
1279
|
const LAN_TASK_NAME = 'YottaMemoryServe';
|
|
1275
1280
|
const LAN_GEN_MARKER = 'Generated by yotta-memory lan enable';
|
|
1281
|
+
const LAN_UNIT_NAME = 'yotta-memory-serve.service';
|
|
1282
|
+
const LAN_CRONTAB_MARKER = '#YTM_LAN:yotta-memory-serve';
|
|
1276
1283
|
function lanTaskRunCmd(opts) {
|
|
1277
1284
|
const host = opts.host || '0.0.0.0';
|
|
1278
1285
|
const port = opts.port || 8787;
|
|
@@ -1280,6 +1287,33 @@ function lanTaskRunCmd(opts) {
|
|
|
1280
1287
|
const q = (p) => /[\s"]/.test(p) ? '\\"' + p.replace(/"/g, '\\"') + '\\"' : p;
|
|
1281
1288
|
return q(process.execPath) + ' ' + q(__filename) + ' serve --host ' + host + ' --port ' + port;
|
|
1282
1289
|
}
|
|
1290
|
+
|
|
1291
|
+
function lanServeArgs(opts) {
|
|
1292
|
+
return ['serve', '--host', String(opts.host || '0.0.0.0'), '--port', String(opts.port || 8787)];
|
|
1293
|
+
}
|
|
1294
|
+
// generic spawn wrapper: YOTTA_LAN_*_BIN may point to a .js stub (testing/advanced use;
|
|
1295
|
+
// on Windows run it with the current node interpreter automatically)
|
|
1296
|
+
function lanSpawn(bin, args, spawnOpts) {
|
|
1297
|
+
if (process.platform === 'win32' && /\.js$/i.test(String(bin))) {
|
|
1298
|
+
return child_process.spawnSync(process.execPath, [bin].concat(args), spawnOpts);
|
|
1299
|
+
}
|
|
1300
|
+
return child_process.spawnSync(bin, args, spawnOpts);
|
|
1301
|
+
}
|
|
1302
|
+
// platform override for testing/advanced use (YOTTA_LAN_PLATFORM=linux/win32/...)
|
|
1303
|
+
function lanPlatform() { return process.env.YOTTA_LAN_PLATFORM || process.platform; }
|
|
1304
|
+
// sh-style single-quote quoting (crontab line is executed via /bin/sh -c)
|
|
1305
|
+
function shQuote(s) {
|
|
1306
|
+
const p = String(s);
|
|
1307
|
+
if (!/[\s'"\\$\`]/.test(p)) return p;
|
|
1308
|
+
return "'" + p.replace(/'/g, "'\\''") + "'";
|
|
1309
|
+
}
|
|
1310
|
+
// systemd ExecStart quoting (systemd's own parse rules: double quotes group, \" escapes a quote,
|
|
1311
|
+
// \\ escapes a backslash)
|
|
1312
|
+
function systemdEscapeArg(s) {
|
|
1313
|
+
const p = String(s);
|
|
1314
|
+
if (!/[\s"\\$;]/.test(p)) return p;
|
|
1315
|
+
return '"' + p.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
|
|
1316
|
+
}
|
|
1283
1317
|
function lanStartupDir() {
|
|
1284
1318
|
// 测试/高级用途可用 YOTTA_LAN_STARTUP_DIR 覆盖(默认用户级 Startup 目录,免管理员)
|
|
1285
1319
|
if (process.env.YOTTA_LAN_STARTUP_DIR) return process.env.YOTTA_LAN_STARTUP_DIR;
|
|
@@ -1370,7 +1404,191 @@ function isSchtasksAccessDenied(e) {
|
|
|
1370
1404
|
const msg = String((e && e.stderr) || (e && e.message) || e);
|
|
1371
1405
|
return /access\s+is\s+denied|access\s+denied|拒绝访问/i.test(msg);
|
|
1372
1406
|
}
|
|
1373
|
-
|
|
1407
|
+
|
|
1408
|
+
// ---- lan Linux (systemd user unit / user crontab @reboot) ----
|
|
1409
|
+
function lanLinuxSystemdUserDir() {
|
|
1410
|
+
// override with YOTTA_LAN_SYSTEMD_USER_DIR for testing/advanced use (default ~/.config/systemd/user)
|
|
1411
|
+
if (process.env.YOTTA_LAN_SYSTEMD_USER_DIR) return process.env.YOTTA_LAN_SYSTEMD_USER_DIR;
|
|
1412
|
+
const cfg = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
|
|
1413
|
+
return path.join(cfg, 'systemd', 'user');
|
|
1414
|
+
}
|
|
1415
|
+
function lanLinuxUnitPath() { return path.join(lanLinuxSystemdUserDir(), LAN_UNIT_NAME); }
|
|
1416
|
+
function lanLinuxSystemctlBin() { return process.env.YOTTA_LAN_SYSTEMCTL_BIN || 'systemctl'; }
|
|
1417
|
+
function lanLinuxLoginctlBin() { return process.env.YOTTA_LAN_LOGINCTL_BIN || 'loginctl'; }
|
|
1418
|
+
function lanLinuxExecStart(opts) {
|
|
1419
|
+
return [systemdEscapeArg(process.execPath), systemdEscapeArg(__filename)].concat(lanServeArgs(opts)).join(' ');
|
|
1420
|
+
}
|
|
1421
|
+
function lanLinuxUnitContent(opts) {
|
|
1422
|
+
// keep comments ASCII (avoid locale/encoding issues); ExecStart uses systemd's own quote rules
|
|
1423
|
+
return [
|
|
1424
|
+
'# ' + LAN_GEN_MARKER + '; remove with: yotta-memory lan disable (English only, keep ASCII)',
|
|
1425
|
+
'',
|
|
1426
|
+
'[Unit]',
|
|
1427
|
+
'Description=yotta-memory memory engine (lan autostart)',
|
|
1428
|
+
'',
|
|
1429
|
+
'[Service]',
|
|
1430
|
+
'Type=simple',
|
|
1431
|
+
'ExecStart=' + lanLinuxExecStart(opts),
|
|
1432
|
+
'Restart=on-failure',
|
|
1433
|
+
'RestartSec=3',
|
|
1434
|
+
'',
|
|
1435
|
+
'[Install]',
|
|
1436
|
+
'WantedBy=default.target',
|
|
1437
|
+
'',
|
|
1438
|
+
].join('\n');
|
|
1439
|
+
}
|
|
1440
|
+
function lanCrontabBin() { return process.env.YOTTA_LAN_CRONTAB_BIN || 'crontab'; }
|
|
1441
|
+
function lanCrontabLine(opts) {
|
|
1442
|
+
const parts = ['@reboot', shQuote(process.execPath), shQuote(__filename)].concat(lanServeArgs(opts));
|
|
1443
|
+
return parts.join(' ') + ' >> ' + shQuote(lanLogPath(opts)) + ' 2>&1 ' + LAN_CRONTAB_MARKER;
|
|
1444
|
+
}
|
|
1445
|
+
function lanCrontabRead() {
|
|
1446
|
+
try {
|
|
1447
|
+
const r = lanSpawn(lanCrontabBin(), ['-l'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
1448
|
+
if (r.status === 0) return String(r.stdout || '').split(/\r?\n/).filter(function (l) { return l !== ''; });
|
|
1449
|
+
} catch (e) { /* crontab unavailable -> treat as empty */ }
|
|
1450
|
+
return [];
|
|
1451
|
+
}
|
|
1452
|
+
function lanCrontabWrite(lines) {
|
|
1453
|
+
const content = lines.join('\n') + (lines.length ? '\n' : '');
|
|
1454
|
+
const r = lanSpawn(lanCrontabBin(), ['-'], { input: content, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
1455
|
+
if (r.error) throw r.error;
|
|
1456
|
+
if (r.status !== 0) throw new Error(String(r.stderr || r.stdout || '').toString().split(/\r?\n/)[0]);
|
|
1457
|
+
}
|
|
1458
|
+
function lanCrontabHasOurLine(lines) {
|
|
1459
|
+
return lines.some(function (l) { return l.indexOf(LAN_CRONTAB_MARKER) !== -1; });
|
|
1460
|
+
}
|
|
1461
|
+
function lanCrontabWithoutOurLine(lines) {
|
|
1462
|
+
return lines.filter(function (l) { return l.indexOf(LAN_CRONTAB_MARKER) === -1; });
|
|
1463
|
+
}
|
|
1464
|
+
function lanLinuxHasSystemd() {
|
|
1465
|
+
try {
|
|
1466
|
+
const r = lanSpawn(lanLinuxSystemctlBin(), ['--user', 'show-environment'], { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
1467
|
+
return r.status === 0;
|
|
1468
|
+
} catch (e) { return false; }
|
|
1469
|
+
}
|
|
1470
|
+
function lanLinuxSystemctl(args) {
|
|
1471
|
+
const r = lanSpawn(lanLinuxSystemctlBin(), ['--user'].concat(args), { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
1472
|
+
if (r.error) throw r.error;
|
|
1473
|
+
return { status: r.status, out: String(r.stdout || ''), err: String(r.stderr || '') };
|
|
1474
|
+
}
|
|
1475
|
+
function lanLinuxInstallSystemd(opts) {
|
|
1476
|
+
const dir = lanLinuxSystemdUserDir();
|
|
1477
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1478
|
+
const unit = lanLinuxUnitPath();
|
|
1479
|
+
fs.writeFileSync(unit, lanLinuxUnitContent(opts), 'utf8');
|
|
1480
|
+
lanLinuxSystemctl(['daemon-reload']);
|
|
1481
|
+
const en = lanLinuxSystemctl(['enable', LAN_UNIT_NAME]);
|
|
1482
|
+
if (en.status !== 0) throw new Error('systemctl --user enable 失败: ' + en.err.split(/\r?\n/)[0]);
|
|
1483
|
+
const st = lanLinuxSystemctl(['start', LAN_UNIT_NAME]);
|
|
1484
|
+
if (st.status !== 0) {
|
|
1485
|
+
console.error('启动服务失败(已注册,稍后可执行 systemctl --user start yotta-memory-serve.service): ' + st.err.split(/\r?\n/)[0]);
|
|
1486
|
+
}
|
|
1487
|
+
if (opts.onstart) {
|
|
1488
|
+
try {
|
|
1489
|
+
const lr = lanSpawn(lanLinuxLoginctlBin(), ['enable-linger'], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
1490
|
+
if (lr.status === 0) console.log('已启用 lingering(开机即启,无需登录)');
|
|
1491
|
+
else console.error('提示: 开机即启需 loginctl enable-linger,当前未生效(' + String(lr.stderr || '').split(/\r?\n/)[0] + ')');
|
|
1492
|
+
} catch (e) {
|
|
1493
|
+
console.error('提示: 开机即启需 loginctl enable-linger,当前无 loginctl(' + String((e && e.message) || e).split(/\r?\n/)[0] + ')');
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
return unit;
|
|
1497
|
+
}
|
|
1498
|
+
function lanLinuxRemoveSystemd() {
|
|
1499
|
+
// only remove our own marker unit file, never a user's own systemd unit
|
|
1500
|
+
const unit = lanLinuxUnitPath();
|
|
1501
|
+
if (!fs.existsSync(unit) || !lanFileHasMarker(unit)) return false;
|
|
1502
|
+
try { lanLinuxSystemctl(['disable', LAN_UNIT_NAME]); } catch (e) {}
|
|
1503
|
+
try { lanLinuxSystemctl(['stop', LAN_UNIT_NAME]); } catch (e) {}
|
|
1504
|
+
try { fs.unlinkSync(unit); } catch (e) {}
|
|
1505
|
+
try { lanLinuxSystemctl(['daemon-reload']); } catch (e) {}
|
|
1506
|
+
return true;
|
|
1507
|
+
}
|
|
1508
|
+
function lanLinuxInstallCrontab(opts) {
|
|
1509
|
+
const lines = lanCrontabWithoutOurLine(lanCrontabRead());
|
|
1510
|
+
lines.push(lanCrontabLine(opts));
|
|
1511
|
+
lanCrontabWrite(lines);
|
|
1512
|
+
return lanCrontabLine(opts);
|
|
1513
|
+
}
|
|
1514
|
+
function lanLinuxRemoveCrontab() {
|
|
1515
|
+
const cur = lanCrontabRead();
|
|
1516
|
+
const lines = lanCrontabWithoutOurLine(cur);
|
|
1517
|
+
if (lines.length === cur.length) return false;
|
|
1518
|
+
lanCrontabWrite(lines);
|
|
1519
|
+
return true;
|
|
1520
|
+
}
|
|
1521
|
+
function cmdLanLinuxEnable(opts) {
|
|
1522
|
+
if (lanLinuxHasSystemd()) {
|
|
1523
|
+
try {
|
|
1524
|
+
const unit = lanLinuxInstallSystemd(opts);
|
|
1525
|
+
console.log('已注册开机自启: systemd 用户单元 ' + LAN_UNIT_NAME);
|
|
1526
|
+
console.log('单元文件: ' + unit);
|
|
1527
|
+
console.log('备注: 登录后自动启动(systemctl --user);如需现在运行请执行 yotta-memory serve');
|
|
1528
|
+
if (!opts.onstart) console.log('提示: 如需开机即启(无需登录),请用 --onstart 重新执行 yotta-memory lan enable');
|
|
1529
|
+
return;
|
|
1530
|
+
} catch (e) {
|
|
1531
|
+
const msg = String((e && e.message) || e).split(/\r?\n/)[0];
|
|
1532
|
+
console.error('systemd 注册失败: ' + msg);
|
|
1533
|
+
console.log('自动改用用户 crontab @reboot 自启...');
|
|
1534
|
+
}
|
|
1535
|
+
} else {
|
|
1536
|
+
console.log('未检测到 systemd 用户会话,改用用户 crontab @reboot 自启...');
|
|
1537
|
+
}
|
|
1538
|
+
try {
|
|
1539
|
+
const line = lanLinuxInstallCrontab(opts);
|
|
1540
|
+
console.log('已启用用户 crontab @reboot 自启');
|
|
1541
|
+
console.log('启动命令: ' + line);
|
|
1542
|
+
console.log('日志: ' + lanLogPath(opts));
|
|
1543
|
+
console.log('备注: 开机时自动启动;如需现在运行请执行 yotta-memory serve');
|
|
1544
|
+
} catch (e2) {
|
|
1545
|
+
console.error('crontab 注册失败: ' + String((e2 && e2.message) || e2).split(/\r?\n/)[0]);
|
|
1546
|
+
console.error('请手动配置开机自启(如 ~/.config/autostart/*.desktop 或系统服务),或直接运行 yotta-memory serve');
|
|
1547
|
+
process.exit(1);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
function cmdLanLinuxDisable() {
|
|
1551
|
+
let removedAny = false;
|
|
1552
|
+
let sawError = false;
|
|
1553
|
+
try {
|
|
1554
|
+
if (lanLinuxRemoveSystemd()) { console.log('已移除 systemd 用户单元 ' + LAN_UNIT_NAME); removedAny = true; }
|
|
1555
|
+
} catch (e) { console.error('移除 systemd 单元失败: ' + String((e && e.message) || e).split(/\r?\n/)[0]); sawError = true; }
|
|
1556
|
+
try {
|
|
1557
|
+
if (lanLinuxRemoveCrontab()) { console.log('已移除 crontab @reboot 自启'); removedAny = true; }
|
|
1558
|
+
} catch (e) { console.error('移除 crontab 自启失败: ' + String((e && e.message) || e).split(/\r?\n/)[0]); sawError = true; }
|
|
1559
|
+
if (!removedAny && !sawError) console.log('未启用: 未发现任何自启配置(systemd 单元与 crontab 均不存在)');
|
|
1560
|
+
if (sawError) process.exit(1);
|
|
1561
|
+
}
|
|
1562
|
+
function cmdLanLinuxStatus() {
|
|
1563
|
+
let any = false;
|
|
1564
|
+
const unit = lanLinuxUnitPath();
|
|
1565
|
+
const unitExists = fs.existsSync(unit) && lanFileHasMarker(unit);
|
|
1566
|
+
if (unitExists) {
|
|
1567
|
+
any = true;
|
|
1568
|
+
console.log('systemd 用户单元 ' + LAN_UNIT_NAME + ': 已注册');
|
|
1569
|
+
try {
|
|
1570
|
+
const en = lanLinuxSystemctl(['is-enabled', LAN_UNIT_NAME]);
|
|
1571
|
+
if (en.status === 0) console.log(' 启用状态: ' + String(en.out).trim());
|
|
1572
|
+
} catch (e) {}
|
|
1573
|
+
try {
|
|
1574
|
+
const ac = lanLinuxSystemctl(['is-active', LAN_UNIT_NAME]);
|
|
1575
|
+
if (ac.status === 0) console.log(' 运行状态: ' + String(ac.out).trim());
|
|
1576
|
+
} catch (e) {}
|
|
1577
|
+
console.log(' 单元文件: ' + unit);
|
|
1578
|
+
} else {
|
|
1579
|
+
console.log('systemd 用户单元 ' + LAN_UNIT_NAME + ': 未注册');
|
|
1580
|
+
}
|
|
1581
|
+
const lines = lanCrontabRead();
|
|
1582
|
+
if (lanCrontabHasOurLine(lines)) {
|
|
1583
|
+
any = true;
|
|
1584
|
+
console.log('crontab @reboot: 已启用');
|
|
1585
|
+
for (const l of lines) if (l.indexOf(LAN_CRONTAB_MARKER) !== -1) console.log(' ' + l.trim());
|
|
1586
|
+
} else {
|
|
1587
|
+
console.log('crontab @reboot: 未启用');
|
|
1588
|
+
}
|
|
1589
|
+
if (!any) console.log('未启用任何开机自启(可用 yotta-memory lan enable 注册)');
|
|
1590
|
+
}
|
|
1591
|
+
function cmdLanWinEnable(opts) {
|
|
1374
1592
|
if (process.platform !== 'win32') {
|
|
1375
1593
|
console.error('lan 命令当前仅支持 Windows(计划任务 / Startup 自启);本机平台: ' + process.platform);
|
|
1376
1594
|
process.exit(2);
|
|
@@ -1404,7 +1622,7 @@ function cmdLanEnable(opts) {
|
|
|
1404
1622
|
return;
|
|
1405
1623
|
}
|
|
1406
1624
|
}
|
|
1407
|
-
function
|
|
1625
|
+
function cmdLanWinDisable() {
|
|
1408
1626
|
if (process.platform !== 'win32') {
|
|
1409
1627
|
console.error('lan 命令当前仅支持 Windows(计划任务 / Startup 自启);本机平台: ' + process.platform);
|
|
1410
1628
|
process.exit(2);
|
|
@@ -1429,7 +1647,7 @@ function cmdLanDisable() {
|
|
|
1429
1647
|
if (!removedAny && !sawError) console.log('未启用: 未发现任何自启配置(计划任务与 Startup 文件均不存在)');
|
|
1430
1648
|
if (sawError) process.exit(1);
|
|
1431
1649
|
}
|
|
1432
|
-
function
|
|
1650
|
+
function cmdLanWinStatus() {
|
|
1433
1651
|
if (process.platform !== 'win32') {
|
|
1434
1652
|
console.log('lan 命令当前仅支持 Windows(计划任务 / Startup 自启);本机平台: ' + process.platform);
|
|
1435
1653
|
return;
|
|
@@ -1460,6 +1678,27 @@ function cmdLanStatus() {
|
|
|
1460
1678
|
}
|
|
1461
1679
|
if (!taskFound && !vbsOk && !cmdOk) console.log('未启用任何开机自启(可用 yotta-memory lan enable 注册)');
|
|
1462
1680
|
}
|
|
1681
|
+
|
|
1682
|
+
function cmdLanEnable(opts) {
|
|
1683
|
+
const p = lanPlatform();
|
|
1684
|
+
if (p === 'win32') return cmdLanWinEnable(opts);
|
|
1685
|
+
if (p === 'linux') return cmdLanLinuxEnable(opts);
|
|
1686
|
+
console.error('lan 命令当前仅支持 Windows / Linux;本机平台: ' + p);
|
|
1687
|
+
process.exit(2);
|
|
1688
|
+
}
|
|
1689
|
+
function cmdLanDisable() {
|
|
1690
|
+
const p = lanPlatform();
|
|
1691
|
+
if (p === 'win32') return cmdLanWinDisable();
|
|
1692
|
+
if (p === 'linux') return cmdLanLinuxDisable();
|
|
1693
|
+
console.error('lan 命令当前仅支持 Windows / Linux;本机平台: ' + p);
|
|
1694
|
+
process.exit(2);
|
|
1695
|
+
}
|
|
1696
|
+
function cmdLanStatus() {
|
|
1697
|
+
const p = lanPlatform();
|
|
1698
|
+
if (p === 'win32') return cmdLanWinStatus();
|
|
1699
|
+
if (p === 'linux') return cmdLanLinuxStatus();
|
|
1700
|
+
console.log('lan 命令当前仅支持 Windows / Linux;本机平台: ' + p);
|
|
1701
|
+
}
|
|
1463
1702
|
// ---- usage / main ----
|
|
1464
1703
|
function usage() {
|
|
1465
1704
|
console.log([
|
|
@@ -1484,7 +1723,7 @@ function usage() {
|
|
|
1484
1723
|
' yotta-memory token list 列出已登记智能体',
|
|
1485
1724
|
' yotta-memory token revoke --agent <id> 吊销某智能体 token',
|
|
1486
1725
|
' yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio] 启动 MCP 记忆引擎(streamable HTTP;--stdio 本地零进程模式)',
|
|
1487
|
-
' yotta-memory lan enable [--onstart] | disable | status 开机自启管理(Windows
|
|
1726
|
+
' yotta-memory lan enable [--onstart] | disable | status 开机自启管理(Windows:计划任务,默认 ONLOGON,非管理员自动降级用户级 Startup 静默自启;Linux:systemd 用户单元,不可用时自动降级用户 crontab @reboot)',
|
|
1488
1727
|
' yotta-memory --version 版本',
|
|
1489
1728
|
'',
|
|
1490
1729
|
'类型: FACT(公共共享) / PREF(偏好) / BOUND(边界) / COMMIT(承诺)',
|
|
@@ -1620,4 +1859,34 @@ module.exports = {
|
|
|
1620
1859
|
lanRemoveStartupFiles: lanRemoveStartupFiles,
|
|
1621
1860
|
lanFileHasMarker: lanFileHasMarker,
|
|
1622
1861
|
isSchtasksAccessDenied: isSchtasksAccessDenied,
|
|
1862
|
+
lanServeArgs: lanServeArgs,
|
|
1863
|
+
lanSpawn: lanSpawn,
|
|
1864
|
+
lanPlatform: lanPlatform,
|
|
1865
|
+
shQuote: shQuote,
|
|
1866
|
+
systemdEscapeArg: systemdEscapeArg,
|
|
1867
|
+
lanLinuxSystemdUserDir: lanLinuxSystemdUserDir,
|
|
1868
|
+
lanLinuxUnitPath: lanLinuxUnitPath,
|
|
1869
|
+
lanLinuxSystemctlBin: lanLinuxSystemctlBin,
|
|
1870
|
+
lanLinuxLoginctlBin: lanLinuxLoginctlBin,
|
|
1871
|
+
lanLinuxExecStart: lanLinuxExecStart,
|
|
1872
|
+
lanLinuxUnitContent: lanLinuxUnitContent,
|
|
1873
|
+
lanCrontabBin: lanCrontabBin,
|
|
1874
|
+
lanCrontabLine: lanCrontabLine,
|
|
1875
|
+
lanCrontabRead: lanCrontabRead,
|
|
1876
|
+
lanCrontabWrite: lanCrontabWrite,
|
|
1877
|
+
lanCrontabHasOurLine: lanCrontabHasOurLine,
|
|
1878
|
+
lanCrontabWithoutOurLine: lanCrontabWithoutOurLine,
|
|
1879
|
+
lanLinuxHasSystemd: lanLinuxHasSystemd,
|
|
1880
|
+
lanLinuxSystemctl: lanLinuxSystemctl,
|
|
1881
|
+
lanLinuxInstallSystemd: lanLinuxInstallSystemd,
|
|
1882
|
+
lanLinuxRemoveSystemd: lanLinuxRemoveSystemd,
|
|
1883
|
+
lanLinuxInstallCrontab: lanLinuxInstallCrontab,
|
|
1884
|
+
lanLinuxRemoveCrontab: lanLinuxRemoveCrontab,
|
|
1885
|
+
cmdLanWinEnable: cmdLanWinEnable,
|
|
1886
|
+
cmdLanWinDisable: cmdLanWinDisable,
|
|
1887
|
+
cmdLanWinStatus: cmdLanWinStatus,
|
|
1888
|
+
cmdLanLinuxEnable: cmdLanLinuxEnable,
|
|
1889
|
+
cmdLanLinuxDisable: cmdLanLinuxDisable,
|
|
1890
|
+
cmdLanLinuxStatus: cmdLanLinuxStatus,
|
|
1891
|
+
|
|
1623
1892
|
};
|
package/package.json
CHANGED
package/references/protocol.md
CHANGED