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 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.abspath(__file__)), "..", "brain"))
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.abspath(__file__)), "..", "brain"))
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.abspath(__file__)), "..", "brain"))
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.abspath(__file__)), "archive-log")]
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.abspath(__file__)), "..", "brain"))
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.abspath(__file__)), "summarize-debug"),
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.abspath(__file__)), "..", "brain"))
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.abspath(__file__)), "..", "brain"))
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")
@@ -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.abspath(__file__)), "..", "brain"))
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
- ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
24
- MEM = os.environ.get("MEMORY_DIR") or os.path.join(ROOT, "memory")
25
- BRAIN = os.environ.get("BRAIN_DIR") or os.path.join(ROOT, "brain")
26
- DOCS = os.environ.get("DOCS_DIR") or os.path.join(ROOT, "docs")
27
- LIVE = os.path.join(BRAIN, "live.jsonl")
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.abspath(__file__)), "..", "brain"))
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() { "$PY" "$BIN_DIR/runlog" "$@"; }; rc=0
12
- "$PY" "$BIN_DIR/runlog" "live-summary" -- "$PY" "$MEMKIT_ROOT/brain/fix-live-summary.py" || rc=$((rc+1))
13
- "$PY" "$BIN_DIR/runlog" "archive-log" -- bash "$BIN_DIR/archive-log" || rc=$((rc+2))
14
- "$PY" "$BIN_DIR/runlog" "index" -- "$PY" "$MEMKIT_ROOT/brain/index.py" || rc=$((rc+4))
15
- "$PY" "$BIN_DIR/runlog" "reload" -- bash -c "curl -s -m 5 -X POST $BRAIN_URL/reload >/dev/null" || rc=$((rc+8))
16
- "$PY" "$BIN_DIR/runlog" "check" -- bash "$BIN_DIR/soul" check || rc=$((rc+16))
17
- [ -n "${SOUL_BACKUP_CMD:-}" ] && { "$PY" "$BIN_DIR/runlog" "backup" -- bash -c "$SOUL_BACKUP_CMD" || rc=$((rc+32)); }
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.4",
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
  }
@@ -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 "✓ 可以发"