Files
homeagent-sdk/tools/hmapdev/cmd_debug.go
HomeAgent Agent efb396d7b3 feat(lua): Lua SDK 全量对齐 1.3.0 + hmapdev 单一 mock 源/版本标记/语法预检
内核侧 Lua 桥此前停在 v0.8.0 时代能力面,而 1.1/1.2/1.3 新增的
媒体、注入标志位、中断优先级、事件订阅、动态通道注销只在 Go 侧存在,
文档却宣称『能力完全对齐』——属于静默漂移。

本仓(事实源):
- 新增 sdk/lua/sdk.lua:Lua mock 的单一事实源,补齐全部新 API
  (*_opts / inject_input_sync / inject_*_media / set_tool_blocks /
  unregister_output_channel / events / plugin_mgr / insert_with_media /
  sentence_text+media_digests / attachments / context_policy)。
- scripts/sync-lua-sdk.sh:把事实源同步到 hmapdev assets、luademo、
  以及被 vendored 时的内核副本;三份 sdk.lua 不再各自漂移。
- hmapdev init --lua:优先从激活 SDK 拷权威 mock,内嵌模板降级为
  assets/sdk.lua 回退,不再内联手写副本。
- hmapdev build(Lua):plugin.json 写入 SDK 版本(能力可追溯),
  打包前用 luac -p / lua loadfile 做语法预检,失败以非零码退出。
- hmapdev debug --lua:优先用激活 SDK 的权威 mock(HMAPDEV_SDK_LUA)。
- luademo 升级为全能力示例(新增 luademo_probe_v2)。
2026-09-13 19:56:12 +08:00

194 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"gitcode.com/JianFeeeee/homeagent-sdk/tools/hmapdev/yaegi"
)
// tmplLuaDebug is the temporary Lua debug script template
const tmplLuaDebug = `-- HomeAgent Lua Plugin Debug
-- Generated by hmapdev debug --lua
-- sdk.lua 优先用激活 SDK 的权威 mockHMAPDEV_SDK_LUA否则回退项目内副本
-- 避免拿一份过期的 sdk.lua 调试出“本地能跑、内核报 nil”的假象。
local sdk_path = os.getenv("HMAPDEV_SDK_LUA")
if sdk_path and sdk_path ~= "" then
sdk = dofile(sdk_path)
else
sdk = require("sdk")
end
local ok, plugin = pcall(dofile, "main.lua")
if not ok then
print("[debug] ERROR loading main.lua: " .. tostring(plugin))
os.exit(1)
end
if type(plugin) == "table" then
print("[debug] Plugin: " .. tostring(plugin.name or "unnamed"))
if plugin.start then
print("[debug] Calling plugin.start(sdk) ...")
local ok, err = pcall(plugin.start, sdk)
if ok then
print("[debug] plugin.start() OK")
else
print("[debug] plugin:start() ERROR: " .. tostring(err))
end
end
end
print("")
print("=== Interactive REPL ===")
print("sdk, plugin globals are available")
local function repl()
while true do
io.write("> ")
io.flush()
local line = io.read()
if line == nil or line == "exit" or line == "quit" then break end
if line == "help" then
print(" help - this help")
print(" exit/quit - exit debug")
print(" sdk - SDK API table")
print(" plugin - loaded plugin table")
else
local fn, err = (loadstring or load)(line)
if fn then
local ok, result = pcall(fn)
if ok and result ~= nil then print(tostring(result)) end
if not ok then print("Error: " .. tostring(result)) end
else
print("Error: " .. tostring(err))
end
end
end
end
repl()
`
type DebugConfig struct {
Dir string
Replaces []string
}
func cmdDebug(args []string) {
cfg := DebugConfig{Dir: "."}
for i := 0; i < len(args); i++ {
switch args[i] {
case "--replace", "-R":
if i+1 < len(args) {
cfg.Replaces = append(cfg.Replaces, args[i+1]); i++
}
default:
if !strings.HasPrefix(args[i], "-") {
cfg.Dir = args[i]
}
}
}
dir := cfg.Dir
luaPath := filepath.Join(dir, "main.lua")
sdkPath := filepath.Join(dir, "sdk.lua")
hasGo := false
entries, _ := os.ReadDir(dir)
for _, e := range entries {
if !e.IsDir() && strings.HasSuffix(e.Name(), ".go") && !strings.HasSuffix(e.Name(), "_test.go") {
hasGo = true
break
}
}
// Merge plg.json replaces with CLI --replace overrides
var replaces []string
if plg, err := readPlgJSON(filepath.Join(dir, "plg.json")); err == nil {
replaces = plg.ReplacesToSlice()
}
replaces = append(replaces, cfg.Replaces...)
if _, err := os.Stat(luaPath); err == nil {
debugLua(dir, sdkPath, luaPath)
} else if hasGo {
debugGo(dir, replaces)
} else {
fmt.Println("error: no main.lua or .go files found in", dir)
os.Exit(1)
}
}
func debugLua(dir, sdkPath, luaPath string) {
// check lua interpreter
luaBin, err := exec.LookPath("lua")
if err != nil {
fmt.Println("error: lua interpreter not found in PATH")
fmt.Println(" install Lua 5.1+ or use hmapdev build to compile your plugin")
os.Exit(1)
}
fmt.Printf("[debug] Lua interpreter: %s\n", luaBin)
fmt.Printf("[debug] Plugin dir: %s\n", dir)
// check if sdk.lua exists
if _, err := os.Stat(sdkPath); os.IsNotExist(err) {
fmt.Println("warning: sdk.lua not found, debug SDK mock will not be available")
}
// 优先用激活 SDK 里的权威 mock避免调试用的是项目里可能过期的副本。
env := os.Environ()
if root := tryActiveSDKRoot(); root != "" {
canonical := filepath.Join(root, "sdk", "lua", "sdk.lua")
if _, err := os.Stat(canonical); err == nil {
env = append(env, "HMAPDEV_SDK_LUA="+canonical)
fmt.Printf("[debug] SDK mock: %s\n", canonical)
}
}
// write temporary debug script
debugScript := filepath.Join(dir, "_debug.lua")
if err := os.WriteFile(debugScript, []byte(tmplLuaDebug), 0644); err != nil {
fmt.Printf("error: write debug script: %v\n", err)
os.Exit(1)
}
defer os.Remove(debugScript)
cmd := exec.Command(luaBin, filepath.Base(debugScript))
cmd.Dir = dir
cmd.Env = env
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
fmt.Println("[debug] Starting Lua debug session...")
fmt.Println()
if err := cmd.Run(); err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
os.Exit(exitErr.ExitCode())
}
fmt.Printf("[debug] error: %v\n", err)
os.Exit(1)
}
}
func debugGo(dir string, replaces []string) {
debug, err := yaegi.NewYaegiDebugger(dir, replaces)
if err != nil {
fmt.Printf("error: %v\n", err)
os.Exit(1)
}
fmt.Printf("[debug] Plugin dir: %s\n", dir)
if err := debug.LoadPlugin(); err != nil {
fmt.Printf("[debug] load plugin: %v\n", err)
os.Exit(1)
}
if err := debug.StartREPL(); err != nil {
fmt.Printf("[debug] repl error: %v\n", err)
os.Exit(1)
}
}