feat(desktop-wake): add WeCom UIA warm-tree and ship n2-wake in 1.2.3
All checks were successful
Publish Python Package to Gitea / publish (push) Successful in 29s

Expose ensure_ui_ready via DuiLib GetAccessible inject path; package x86 native helpers for host/skill consumers.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-02 08:42:38 +08:00
parent 3dc46b0555
commit 71091e38a9
29 changed files with 3088 additions and 3 deletions

View File

@@ -0,0 +1,156 @@
"""CLI:python -m jiangchang_desktop_wake <probe|inject|wake|ensure|profiles>"""
from __future__ import annotations
import argparse
import json
import sys
def _print_json(obj) -> None:
print(json.dumps(obj, ensure_ascii=False, indent=2))
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(
prog="python -m jiangchang_desktop_wake",
description="桌面 UIA Provider 暖树(probe / inject / wake / ensure)",
)
sub = parser.add_subparsers(dest="cmd", required=True)
p_profiles = sub.add_parser("profiles", help="列出内置 profile")
p_profiles.set_defaults(func=_cmd_profiles)
p_probe = sub.add_parser("probe", help="探测冷/暖")
p_probe.add_argument("--profile", default="wecom")
p_probe.add_argument("--hwnd", type=lambda s: int(s, 0), default=None)
p_probe.set_defaults(func=_cmd_probe)
p_inject = sub.add_parser(
"inject",
help="阶段 B:安全注入探针(不挂钩;成功看 inject_ok + ui_responsive)",
)
p_inject.add_argument("--profile", default="wecom")
p_inject.add_argument("--hwnd", type=lambda s: int(s, 0), default=None)
p_inject.set_defaults(func=_cmd_inject)
p_c2 = sub.add_parser(
"c2",
help="阶段 C2:SPI + 注入 + WakeProvider,再 probe(冲暖树)",
)
p_c2.add_argument("--profile", default="wecom")
p_c2.add_argument("--hwnd", type=lambda s: int(s, 0), default=None)
p_c2.set_defaults(func=_cmd_c2)
p_wake = sub.add_parser("wake", help="仅跑唤醒策略(不强制成功)")
p_wake.add_argument("--profile", default="wecom")
p_wake.add_argument("--hwnd", type=lambda s: int(s, 0), default=None)
p_wake.set_defaults(func=_cmd_wake)
p_ensure = sub.add_parser(
"ensure",
help="probe→wake→probe;wecom 主路径为 n2-wake(DuiLib 暖树)",
)
p_ensure.add_argument("--profile", default="wecom")
p_ensure.add_argument("--hwnd", type=lambda s: int(s, 0), default=None)
p_ensure.add_argument(
"--no-raise",
action="store_true",
help="仍冷也 exit 0,只打印 JSON",
)
p_ensure.set_defaults(func=_cmd_ensure)
args = parser.parse_args(argv)
try:
return int(args.func(args))
except Exception as exc:
print(str(exc), file=sys.stderr)
return 1
def _cmd_profiles(_args) -> int:
from .profiles import list_profiles, load_profile
out = []
for pid in list_profiles():
p = load_profile(pid)
out.append(
{
"id": p.id,
"description": p.description,
"window_class": p.window_class,
"warm_min_findall": p.warm_min_findall,
"warm_markers": p.warm_markers,
"wake_strategies": p.wake_strategies,
}
)
_print_json(out)
return 0
def _cmd_probe(args) -> int:
from .probe import probe
r = probe(args.profile, hwnd=args.hwnd)
_print_json(r.to_dict())
return 0 if r.warm else 2
def _cmd_inject(args) -> int:
from .inject import inject_probe
r = inject_probe(args.profile, hwnd=args.hwnd)
_print_json(r)
if r.get("inject_ok") and r.get("ui_responsive"):
return 0
if r.get("inject_ok") and not r.get("ui_responsive"):
return 6
return 5
def _cmd_c2(args) -> int:
from .c2 import wake_c2
r = wake_c2(args.profile, hwnd=args.hwnd)
_print_json(r)
if r.get("warm"):
return 0
if r.get("ui_responsive") is False:
return 6
return 4
def _cmd_wake(args) -> int:
from .probe import probe
from .wake import wake
steps = wake(args.profile, hwnd=args.hwnd)
after = probe(args.profile, hwnd=args.hwnd)
_print_json(
{
"steps": [s.to_dict() for s in steps],
"probe_after": after.to_dict(),
}
)
return 0 if after.warm else 2
def _cmd_ensure(args) -> int:
from .ensure import ensure_ui_ready
from .errors import DesktopWakeError
try:
r = ensure_ui_ready(
args.profile,
hwnd=args.hwnd,
raise_on_cold=not args.no_raise,
)
except DesktopWakeError as exc:
print(str(exc), file=sys.stderr)
return 1
_print_json(r.to_dict())
return 0 if r.warm else 2
if __name__ == "__main__":
raise SystemExit(main())