dsh-soul-forging 0.1.4 → 0.1.6
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/bin/archive-log +1 -1
- package/bin/brain-forget +1 -1
- package/bin/compact +2 -2
- package/bin/debug-archive +2 -2
- package/bin/growth-review +1 -1
- package/bin/mem-snapshot +1 -1
- package/bin/persona-sediment +1 -1
- package/bin/soul +5 -5
- package/bin/soul-peer +1 -1
- package/bin/sync +12 -7
- package/package.json +8 -3
- package/scripts/smoke.sh +112 -0
package/bin/archive-log
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"""
|
|
11
11
|
import os, re, sys
|
|
12
12
|
from datetime import datetime, timedelta, timezone
|
|
13
|
-
sys.path.insert(0, os.path.join(os.path.dirname(os.path.
|
|
13
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "brain"))
|
|
14
14
|
from soul_lib import MEM, atomic_write
|
|
15
15
|
|
|
16
16
|
def main():
|
package/bin/brain-forget
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"""
|
|
16
16
|
import json, math, os, sys
|
|
17
17
|
from datetime import datetime, timezone
|
|
18
|
-
sys.path.insert(0, os.path.join(os.path.dirname(os.path.
|
|
18
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "brain"))
|
|
19
19
|
from soul_lib import load_state, save_state, BRAIN
|
|
20
20
|
|
|
21
21
|
BASE = {1: 1.0, 2: 0.75, 3: 0.5}
|
package/bin/compact
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
用法: bin/compact [--apply] [--budget 20000]
|
|
15
15
|
"""
|
|
16
16
|
import json, os, sys
|
|
17
|
-
sys.path.insert(0, os.path.join(os.path.dirname(os.path.
|
|
17
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "brain"))
|
|
18
18
|
from soul_lib import MEM, BRAIN, LIVE, atomic_write
|
|
19
19
|
|
|
20
20
|
BUDGET_DEFAULT = 20000
|
|
@@ -47,7 +47,7 @@ def main():
|
|
|
47
47
|
# ① 时间线瘦身
|
|
48
48
|
print("\n== 时间线 ==")
|
|
49
49
|
import subprocess
|
|
50
|
-
cmd = [sys.executable, os.path.join(os.path.dirname(os.path.
|
|
50
|
+
cmd = [sys.executable, os.path.join(os.path.dirname(os.path.realpath(__file__)), "archive-log")]
|
|
51
51
|
if not apply_:
|
|
52
52
|
cmd.append("--dry")
|
|
53
53
|
print(subprocess.run(cmd, capture_output=True, text=True).stdout.strip() or "(没输出)")
|
package/bin/debug-archive
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
bin/debug-archive --tar # 顺便打成一个包
|
|
10
10
|
"""
|
|
11
11
|
import os, subprocess, sys, tarfile, time
|
|
12
|
-
sys.path.insert(0, os.path.join(os.path.dirname(os.path.
|
|
12
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "brain"))
|
|
13
13
|
from soul_lib import DOCS
|
|
14
14
|
|
|
15
15
|
DBG = os.path.join(DOCS, "debug")
|
|
@@ -26,7 +26,7 @@ def main():
|
|
|
26
26
|
print(f"档案 {len(files)} 份,未登记 {len(missing)} 份")
|
|
27
27
|
for f in missing:
|
|
28
28
|
print(f"\n {f}")
|
|
29
|
-
out = subprocess.run([sys.executable, os.path.join(os.path.dirname(os.path.
|
|
29
|
+
out = subprocess.run([sys.executable, os.path.join(os.path.dirname(os.path.realpath(__file__)), "summarize-debug"),
|
|
30
30
|
os.path.join(DBG, f)], capture_output=True, text=True).stdout.strip()
|
|
31
31
|
print(" 建议加这一行到 INDEX.md:")
|
|
32
32
|
print(" " + (out or "(没提取到,手写吧)"))
|
package/bin/growth-review
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
用法: bin/growth-review [--n 20]
|
|
9
9
|
"""
|
|
10
10
|
import os, sys
|
|
11
|
-
sys.path.insert(0, os.path.join(os.path.dirname(os.path.
|
|
11
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "brain"))
|
|
12
12
|
from soul_lib import MEM
|
|
13
13
|
|
|
14
14
|
def main():
|
package/bin/mem-snapshot
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
bin/mem-snapshot diff <编号> 只比对不改
|
|
12
12
|
"""
|
|
13
13
|
import os, shutil, subprocess, sys, tarfile, time
|
|
14
|
-
sys.path.insert(0, os.path.join(os.path.dirname(os.path.
|
|
14
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "brain"))
|
|
15
15
|
from soul_lib import MEM, BRAIN
|
|
16
16
|
|
|
17
17
|
SNAP = os.path.join(BRAIN, "snapshots")
|
package/bin/persona-sediment
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
用法: bin/persona-sediment [--n 8]
|
|
14
14
|
"""
|
|
15
15
|
import os, re, sys
|
|
16
|
-
sys.path.insert(0, os.path.join(os.path.dirname(os.path.
|
|
16
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "brain"))
|
|
17
17
|
from soul_lib import MEM, atomic_write
|
|
18
18
|
|
|
19
19
|
def read(p):
|
package/bin/soul
CHANGED
|
@@ -20,11 +20,11 @@ import sys
|
|
|
20
20
|
import time
|
|
21
21
|
from datetime import datetime, timezone
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
# 路径统一走公共模块 —— 别在这里另算一套,否则装成包后又会对不上
|
|
24
|
+
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
|
25
|
+
sys.path.insert(0, os.path.join(ROOT, "brain"))
|
|
26
|
+
from soul_lib import MEM, BRAIN, DOCS, LIVE, atomic_write # noqa: E402
|
|
27
|
+
|
|
28
28
|
PORT = os.environ.get("BRAIN_PORT") or "3914"
|
|
29
29
|
|
|
30
30
|
|
package/bin/soul-peer
CHANGED
|
@@ -18,7 +18,7 @@ source 字段就是"这是哪一端写的",检索时能看出来源。
|
|
|
18
18
|
bin/kit-peer push "ssh host 'cat >> ~/.soul-forging/brain/live.jsonl'"
|
|
19
19
|
"""
|
|
20
20
|
import json, os, subprocess, sys
|
|
21
|
-
sys.path.insert(0, os.path.join(os.path.dirname(os.path.
|
|
21
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "brain"))
|
|
22
22
|
from soul_lib import BRAIN, LIVE, read_live, append_live, make_rec
|
|
23
23
|
|
|
24
24
|
def ids():
|
package/bin/sync
CHANGED
|
@@ -8,13 +8,18 @@ LOCK=/tmp/soul-sync.lock
|
|
|
8
8
|
exec 9>"$LOCK"
|
|
9
9
|
if ! flock -n 9; then echo "同步已在运行,跳过"; exit 0; fi
|
|
10
10
|
|
|
11
|
-
R() {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
R() { bash "$BIN_DIR/runlog" "$@"; }; rc=0
|
|
12
|
+
bash "$BIN_DIR/runlog" "live-summary" -- "$PY" "$MEMKIT_ROOT/brain/fix-live-summary.py" || rc=$((rc+1))
|
|
13
|
+
bash "$BIN_DIR/runlog" "archive-log" -- "$PY" "$BIN_DIR/archive-log" || rc=$((rc+2))
|
|
14
|
+
bash "$BIN_DIR/runlog" "index" -- "$PY" "$MEMKIT_ROOT/brain/index.py" || rc=$((rc+4))
|
|
15
|
+
# 服务没起就不算失败 —— 用户不一定要常驻它,检索会自己现算
|
|
16
|
+
if curl -s -m 2 "$BRAIN_URL/health" 2>/dev/null | grep -q '"ok"'; then
|
|
17
|
+
bash "$BIN_DIR/runlog" "reload" -- bash -c "curl -s -m 5 -X POST $BRAIN_URL/reload >/dev/null" || rc=$((rc+8))
|
|
18
|
+
else
|
|
19
|
+
echo "(检索服务没起,跳过重载——不影响使用)"
|
|
20
|
+
fi
|
|
21
|
+
bash "$BIN_DIR/runlog" "check" -- "$PY" "$BIN_DIR/soul" check || rc=$((rc+16))
|
|
22
|
+
[ -n "${SOUL_BACKUP_CMD:-}" ] && { bash "$BIN_DIR/runlog" "backup" -- bash -c "$SOUL_BACKUP_CMD" || rc=$((rc+32)); }
|
|
18
23
|
|
|
19
24
|
if [ $rc -eq 0 ]; then echo "sync ok"; else echo "sync 有失败项 rc=$rc(看 $BRAIN_DIR/alarm.log)"; fi
|
|
20
25
|
exit 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-soul-forging",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Separate Vessels, One Heart · 异体同心 —— 给 AI 的可迁移长期记忆与铸魂流程:markdown 记忆库 + 小脑检索 + 能长出来的性格。纯本地,不要服务器。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
"install.sh",
|
|
42
42
|
"README.md",
|
|
43
43
|
"LICENSE",
|
|
44
|
-
"NOTICE.md"
|
|
44
|
+
"NOTICE.md",
|
|
45
|
+
"scripts/smoke.sh"
|
|
45
46
|
],
|
|
46
47
|
"dsh": {
|
|
47
48
|
"bundle": {
|
|
@@ -76,5 +77,9 @@
|
|
|
76
77
|
"bugs": {
|
|
77
78
|
"url": "https://github.com/Fqingwan/dsh-soul-forging/issues"
|
|
78
79
|
},
|
|
79
|
-
"author": ""
|
|
80
|
+
"author": "",
|
|
81
|
+
"scripts": {
|
|
82
|
+
"test": "bash scripts/smoke.sh",
|
|
83
|
+
"prepublishOnly": "bash scripts/smoke.sh"
|
|
84
|
+
}
|
|
80
85
|
}
|
package/scripts/smoke.sh
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 装机烟测 —— 每次 npm publish 之前必须跑过。
|
|
3
|
+
#
|
|
4
|
+
# 为什么要有这个:源码目录里跑一百遍都是好的,装出来才炸。
|
|
5
|
+
# 已经栽过三次:
|
|
6
|
+
# ① .bin 里是软链接,按 $0 推上级目录指错
|
|
7
|
+
# ② 数据目录写进 /usr/local/lib/node_modules,权限拒绝
|
|
8
|
+
# ③ runlog 没执行位;后来又用 python 去跑 bash 脚本
|
|
9
|
+
#
|
|
10
|
+
# 这个脚本模拟「全新机器 + 全局安装 + 干净 HOME」:
|
|
11
|
+
# npm pack → 装进临时 prefix → 用干净 HOME 跑每一条命令 → 逐项断言
|
|
12
|
+
#
|
|
13
|
+
# 用法: bash scripts/smoke.sh
|
|
14
|
+
set -uo pipefail
|
|
15
|
+
cd "$(dirname "$0")/.."
|
|
16
|
+
ROOT="$(pwd)"
|
|
17
|
+
|
|
18
|
+
PASS=0; FAIL=0
|
|
19
|
+
ok() { PASS=$((PASS+1)); printf ' ✓ %s\n' "$1"; }
|
|
20
|
+
bad() { FAIL=$((FAIL+1)); printf ' ✗ %s\n %s\n' "$1" "${2:-}"; }
|
|
21
|
+
|
|
22
|
+
TMP="$(mktemp -d)"
|
|
23
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
24
|
+
export HOME="$TMP/home"; mkdir -p "$HOME"
|
|
25
|
+
PREFIX="$TMP/prefix"; mkdir -p "$PREFIX"
|
|
26
|
+
|
|
27
|
+
echo "== 1. 打包 =="
|
|
28
|
+
TGZ=$(npm pack --silent 2>/dev/null | tail -1)
|
|
29
|
+
[ -f "$TGZ" ] && ok "npm pack → $TGZ" || { bad "npm pack"; exit 1; }
|
|
30
|
+
|
|
31
|
+
echo "== 2. 全局安装(模拟 /usr/local)=="
|
|
32
|
+
INSTALL_LOG=$TMP/install.log
|
|
33
|
+
if ! npm i -g --prefix "$PREFIX" "$ROOT/$TGZ" >"$INSTALL_LOG" 2>&1; then
|
|
34
|
+
bad "安装失败" "$(grep -E 'npm (ERR|error)' "$INSTALL_LOG" | head -2 | tr '\n' ' ')"
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
BIN="$PREFIX/bin"
|
|
38
|
+
[ -x "$BIN/soul" ] && ok "命令已安装到 $BIN" || { bad "安装后没有 soul 命令" "$(ls "$PREFIX" 2>/dev/null | tr '\n' ' ')"; exit 1; }
|
|
39
|
+
VER=$(node -e "console.log(require('$PREFIX/lib/node_modules/dsh-soul-forging/package.json').version)")
|
|
40
|
+
ok "版本 $VER"
|
|
41
|
+
|
|
42
|
+
check_pkg_clean() {
|
|
43
|
+
node -e "
|
|
44
|
+
const fs=require('fs');const p='$PREFIX/lib/node_modules/dsh-soul-forging';
|
|
45
|
+
const bad=['brain/entries.json','brain/state.json','brain/live.jsonl','brain/snapshots']
|
|
46
|
+
.filter(f=>fs.existsSync(p+'/'+f));
|
|
47
|
+
if(bad.length){console.log(bad.join(','));process.exit(1)}
|
|
48
|
+
" 2>/dev/null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
echo "== 3. 逐条命令 =="
|
|
52
|
+
run() { # 名字 命令...
|
|
53
|
+
local name="$1"; shift
|
|
54
|
+
local out; out=$("$@" 2>&1); local rc=$?
|
|
55
|
+
if [ $rc -eq 0 ] && [ -n "$out" ]; then ok "$name"; else bad "$name" "rc=$rc ${out:0:120}"; fi
|
|
56
|
+
LAST_OUT="$out"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
run "soul-init" "$BIN/soul-init"
|
|
60
|
+
run "soul-boot" "$BIN/soul-boot" --list
|
|
61
|
+
# sync 永远 exit 0(失败也返回 0,只是打印"有失败项"),所以必须看输出
|
|
62
|
+
sync_ok() {
|
|
63
|
+
local out; out=$("$BIN/soul-sync" 2>&1)
|
|
64
|
+
echo "$out" | grep -q "^sync ok$" && ok "soul-sync(干净)" && return 0
|
|
65
|
+
bad "soul-sync 有失败项" "$(echo "$out" | grep -E '失败' | head -1)"
|
|
66
|
+
return 1
|
|
67
|
+
}
|
|
68
|
+
sync_ok
|
|
69
|
+
run "soul (write)" "$BIN/soul" write "烟测写的一条"
|
|
70
|
+
sync_ok
|
|
71
|
+
run "soul-recall" "$BIN/soul-recall" "烟测" 1
|
|
72
|
+
echo "$LAST_OUT" | grep -q "烟测写的一条" && ok "检索命中刚写的内容" || bad "检索没命中"
|
|
73
|
+
|
|
74
|
+
# 服务类:起在随机高位端口,测完杀掉
|
|
75
|
+
PORT=$(( (RANDOM % 2000) + 39000 ))
|
|
76
|
+
BRAIN_PORT=$PORT "$BIN/soul-serve" >/dev/null 2>&1
|
|
77
|
+
sleep 1
|
|
78
|
+
curl -s -m 3 "http://127.0.0.1:$PORT/health" | grep -q '"ok": true' && ok "soul-serve 起得来" || bad "soul-serve"
|
|
79
|
+
BRAIN_PORT=$PORT "$BIN/soul-sync" 2>&1 | grep -q "^sync ok$" && ok "sync(服务在跑时)" || bad "sync(服务在跑时)"
|
|
80
|
+
|
|
81
|
+
run "soul-check" "$BIN/soul" check
|
|
82
|
+
run "soul-forget" "$BIN/soul-forget"
|
|
83
|
+
run "soul-snapshot" "$BIN/soul-snapshot" save "烟测"
|
|
84
|
+
run "soul-peer" "$BIN/soul-peer" export
|
|
85
|
+
run "soul-sediment" "$BIN/soul-sediment"
|
|
86
|
+
run "soul-review" "$BIN/soul-review"
|
|
87
|
+
run "soul-archive" "$BIN/soul-archive"
|
|
88
|
+
run "soul-debug" "$BIN/soul-debug"
|
|
89
|
+
|
|
90
|
+
echo "== 4. 包目录必须干净(跑完所有命令之后查)=="
|
|
91
|
+
DIRTY=$(check_pkg_clean) && ok "包目录没被写脏" || bad "包目录被污染:$DIRTY"
|
|
92
|
+
|
|
93
|
+
echo "== 5. 插件加载 =="
|
|
94
|
+
node -e "
|
|
95
|
+
import('$PREFIX/lib/node_modules/dsh-soul-forging/lib/plugin.js').then(m=>{
|
|
96
|
+
let c; const ctx={systemPrompt:{context(o){c=o;return()=>{}}},skills:{register(){return()=>{}}},logger:{info(){},warn(){}}};
|
|
97
|
+
m.apply(ctx,{});
|
|
98
|
+
const t=c.text({});
|
|
99
|
+
if(typeof m.name!=='string') throw new Error('name 缺失');
|
|
100
|
+
if(!Array.isArray(m.inject)) throw new Error('inject 缺失');
|
|
101
|
+
if(!t || t.length<1000) throw new Error('注入块太短: '+t.length);
|
|
102
|
+
console.log('OK '+t.length);
|
|
103
|
+
}).catch(e=>{console.log('FAIL '+e.message);process.exit(1)})
|
|
104
|
+
" >/tmp/smoke-plugin.out 2>&1
|
|
105
|
+
grep -q "^OK" /tmp/smoke-plugin.out && ok "插件注入 $(cut -d' ' -f2 /tmp/smoke-plugin.out) 字" \
|
|
106
|
+
|| bad "插件加载" "$(cat /tmp/smoke-plugin.out | head -2)"
|
|
107
|
+
|
|
108
|
+
rm -f "$ROOT/$TGZ"
|
|
109
|
+
echo
|
|
110
|
+
echo "===== 通过 $PASS · 失败 $FAIL ====="
|
|
111
|
+
[ $FAIL -eq 0 ] || exit 1
|
|
112
|
+
echo "✓ 可以发"
|