diff --git a/docs/zh/experiments/plugin-arch/19-migration-verify/switch-production.py b/docs/zh/experiments/plugin-arch/19-migration-verify/switch-production.py new file mode 100755 index 0000000..3faecc7 --- /dev/null +++ b/docs/zh/experiments/plugin-arch/19-migration-verify/switch-production.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +"""生产切换:经 pluginmgr 正规通道安装 17 个 hmap(Part 6.5)。 + +与手工拷贝方案的区别 —— 这里复用内核自己的安装逻辑: + + validatePackage 校验 manifest + 平台二进制齐全 + StopAndUnload 停旧实例但**保留配置表** + os.Rename 备份 解包失败自动回滚到旧版本 + platformBinary() 按 runtime 挑当前平台那份,重命名为 plugin.bin + chmod 0755 补执行位 + +手工拷贝会重新实现这一套,且必然实现得更差(第一版就漏了 platforms 字段 +与配置保留语义)。 + +用法: + switch-production.py 演练 + switch-production.py --apply 实际安装 +""" + +import json +import os +import sys +import urllib.error +import urllib.request + +PROD_PLUGINS = "/home/newqqagent/plugins" +SDK_EXAMPLE = "/home/program/TrueAgent/third_party/homeagent-sdk/example" +PLUGINMGR = "http://127.0.0.1:9876/plugins" + + +def find_hmap(name): + """找插件的 hmap 包。 + + bundle:true -> _bundle.hmap(含多平台二进制) + bundle:false -> __.hmap(qq 是这种) + """ + dist = os.path.join(SDK_EXAMPLE, name, "dist") + if not os.path.isdir(dist): + return None + cands = [f for f in os.listdir(dist) if f.endswith(".hmap")] + if not cands: + return None + for c in cands: + if c.endswith("_bundle.hmap"): + return os.path.join(dist, c) + return os.path.join(dist, sorted(cands)[0]) + + +def install(path): + """POST 到 pluginmgr。overwrite=true 走原地更新分支,保留配置表。""" + body = json.dumps({"path": path, "overwrite": True}).encode() + req = urllib.request.Request( + PLUGINMGR, data=body, + headers={"Content-Type": "application/json"}, + method="POST") + try: + with urllib.request.urlopen(req, timeout=180) as resp: + return json.loads(resp.read().decode()), None + except urllib.error.HTTPError as e: + return None, "HTTP %d: %s" % (e.code, e.read().decode()[:300]) + except Exception as e: + return None, str(e) + + +def main(): + apply = "--apply" in sys.argv + + targets = sorted( + d for d in os.listdir(PROD_PLUGINS) + if os.path.isfile(os.path.join(PROD_PLUGINS, d, "plugin.so")) + or os.path.isfile(os.path.join(PROD_PLUGINS, d, "plugin.bin")) + ) + print("生产外部插件: %d 个" % len(targets)) + + # 先全部校验,任一缺包就整批中止。 + # 理由:新 homed 不认 .so,「一半装了一半没装」的中间态最难排查。 + plan = [] + missing = [] + for name in targets: + h = find_hmap(name) + if h is None: + missing.append(name) + else: + plan.append((name, h)) + + if missing: + print("\n✗ 中止:以下插件缺 hmap 包:") + for m in missing: + print(" " + m) + print("\n先跑 rebuild-plugins.sh 重编。") + return 1 + + print("✓ 全部 %d 个 hmap 就位\n" % len(plan)) + for name, h in plan: + print(" %-16s %-44s %6d KB" % ( + name, os.path.basename(h), os.path.getsize(h) // 1024)) + + if not apply: + print("\n[演练] 加 --apply 才实际安装") + return 0 + + print("\n经 pluginmgr 安装(overwrite=true,保留配置)...") + ok = 0 + failed = [] + for name, h in plan: + result, err = install(h) + if err: + print(" ✗ %-16s %s" % (name, err)) + failed.append(name) + continue + if "error" in result: + print(" ✗ %-16s %s: %s" % ( + name, result["error"], result.get("details", ""))) + failed.append(name) + continue + print(" ✓ %-16s %-12s v%s -> v%s config_kept=%s" % ( + name, + result.get("action", "?"), + result.get("previous_version", "?"), + result.get("version", "?"), + result.get("config_kept", False))) + ok += 1 + + print("\n成功 %d / 失败 %d" % (ok, len(failed))) + if failed: + print("失败: " + " ".join(failed)) + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/internal/meta/meta.go b/internal/meta/meta.go index cb47670..1d8d464 100644 --- a/internal/meta/meta.go +++ b/internal/meta/meta.go @@ -1,13 +1,16 @@ // Package meta 收集 HomeAgent 内核的全部元数据。 // 版本号通过 `go build -ldflags` 注入,默认值为 dev 版本。 -// 此文件是 ABI 版本号与 dispatch method ID 的唯一数据源。 // SDK 仓的 meta/meta.go 应与此保持同步。 package meta var ( // Version 是 HomeAgent 内核版本号。 // 通过 `-ldflags="-X gitcode.com/JianFeeeee/HomeAgent/internal/meta.Version=vX.Y.Z"` 注入。 - Version = "0.9.1" + // + // 1.0.0:外部插件从 C ABI 动态库迁到子进程 + 共享内存。 + // 这是首个不再加载 `.so`/`.dll` 的版本,与 0.9.x 不兼容(存量插件必须 + // 用新版 plugindev 重编),故跃到主版本号。 + Version = "1.0.0" // Commit 是构建时的 Git commit hash。 Commit = "unknown" @@ -19,7 +22,7 @@ var ( KernelName = "HomeAgent" // SDKCompatibleVersion 是此内核可兼容的最高 SDK 版本(semver)。 - SDKCompatibleVersion = "0.9.1" + SDKCompatibleVersion = "1.0.0" ) // FullVersion 返回完整的版本字符串。 @@ -27,81 +30,15 @@ func FullVersion() string { return KernelName + " v" + Version + " (" + Commit + ")" } -// ---- ABI 版本(C ABI 协议版本,插件与内核通信用) ---- -// ABI 版本直接取内核版本号字符串(semver),与核心 Version 保持一致,不再使用独立数字编码。 -// 协商层(C 结构体 int version 字段)使用 CABINum:由版本字符串派生的整数(major*100 + minor)。 -// 映射:v0.8.x → CABINum=800;v0.9.x → CABINum=900(invoke_stage 写回)。 -// 小版本(patch)演进不影响 ABI,CABINum 不变。version_min 保证旧 ABI 插件仍可加载。 - -var ( - // ABIVersion 是 ABI 标识版本(字符串 semver,与核心 Version 对齐)。 - ABIVersion = Version - // ABIVersionMin 是兼容的最低 ABI 标识版本。 - ABIVersionMin = "0.8.0" -) - -const ( - // CABINum 是 C 层协商用的整数版本(major*100 + minor),随 ABIVersion 派生。 - CABINum = 900 - // CABINumMin 是 C 层兼容的最低整数版本。 - // 旧工具链(v0.8 之前)写入的整数 version=1,无写回能力但与新内核结构兼容, - // 因此最小值保持 1 以兼容全部旧插件(新插件 900 匹配,旧插件 1/2 通过); - // 仅当未来内核 ABI 破坏兼容时才提高该值。 - CABINumMin = 1 -) - -// ---- Dispatch Method IDs ---- -// 核心→插件:这些 ID 通过 CoreAPI.dispatch 传递,标识 SDK 调用。 -// 插件端的 C enum 定义在 plugindev 的 C ABI header 模板中。 -const ( - CoreRegisterTool = 1 - CoreRegisterStage = 2 - CoreRegisterOutputCh = 3 - CoreRegisterPluginAPI = 4 - CoreInjectText = 5 - CoreInjectInterruptText = 6 - CoreInjectTextNoMemory = 7 - CoreSetAutoRestart = 8 - CoreMemoryRecall = 9 - CoreMemoryCommit = 10 - CoreMemoryIntrospect = 11 - CoreMemoryMerge = 12 - CoreMemoryPurge = 13 - CoreDocQuery = 14 - CoreKnowledgeSearch = 15 - CoreSettingsGet = 16 - CoreSettingsSet = 17 - CoreSettingsRegisterDef = 18 - CoreLLMListSources = 19 - CoreLLMSetSource = 20 - CoreSocialGetPerson = 21 - CoreSocialGetNetwork = 22 - CoreSubscribe = 23 - CoreUnsubscribe = 24 - CoreFreeString = 25 - CoreSettingsGetCore = 26 - CoreSettingsSetCore = 27 - CoreSettingsListCore = 28 - CoreSettingsGetPlugin = 29 - CoreSettingsSetPlugin = 30 - CoreSettingsListPlugin = 31 - CoreDocInsert = 32 - CoreDocRemove = 33 - CoreDocStats = 34 - CoreKnowledgeAdd = 35 - CoreKnowledgeList = 36 - CoreLLMCurrentSource = 37 - CoreSocialGetTrait = 38 - CoreSocialGetRelations = 39 - CoreSocialListPersons = 40 - CoreTextMemoryAppend = 41 - CoreSettingsList = 42 - CoreSettingsDefs = 43 - CoreSettingsDump = 44 - CoreSettingsPlugins = 45 - CoreRegisterInputCh = 46 - CoreInjectInputSync = 47 - CorePluginReloadOne = 48 - CorePluginListLoaded = 49 - CorePluginIsDisabled = 50 -) +// ---- 协议版本 ---- +// +// 子进程 RPC 的协议版本是一个独立的小整数,与内核语义版本解耦: +// 语义版本变动频繁(修 bug、加字段),而 wire 协议只在**帧格式或握手语义** +// 变化时才升。当前值见 internal/plugin/proc/protocol.go 的 ProtocolVersion。 +// +// C ABI 时代的 ABIVersion / CABINum / 51 个 Core 整数 ID 已随 +// Part 6.2 删除 internal/plugin/cabi/ 一并退场: +// - 整数 method id 平移为 method 名字符串(proc/protocol.go 的 Method* 常量) +// - 版本协商改为握手帧里的 protocol 字段 +// +// 保留那些常量只会让人以为它们还在生效。