#!/usr/bin/env node // perception-kit v1 — 铸渊自有双轨感知工具(神笔马良,宿主无关) // 协议正本:TOOL-SANDBOX/perception-kit/TCS-PROTOCOL-PERCEPTION-TRANSLATION-V1-0001.tcs // 用法: // node perceive.mjs --app "HoloLake" # 对运行中的App窗口做双轨感知 // node perceive.mjs --url http://127.0.0.1:5210 # 对dev server做DOM轨感知 import { execFileSync } from "node:child_process"; import { createHash } from "node:crypto"; import fs from "node:fs"; import path from "node:path"; const KIT_DIR = decodeURIComponent(new URL(".", import.meta.url).pathname); const OUT_DIR = path.join(KIT_DIR, "perceptions"); fs.mkdirSync(OUT_DIR, { recursive: true }); const shasum = (v) => createHash("sha256").update(v).digest("hex"); const run = (cmd, args, opts = {}) => { try { return execFileSync(cmd, args, { encoding: "utf8", timeout: 15000, ...opts }); } catch (e) { return { __error: String(e.message).slice(0, 200) }; } }; const arg = (name) => { const i = process.argv.indexOf(name); return i >= 0 ? process.argv[i + 1] : null; }; const now = () => new Date().toISOString(); const pid = now().replace(/[-:TZ.]/g, "").slice(0, 14) + "-" + Math.random().toString(36).slice(2, 6); const receipt = { schema: "guanghu.perception-receipt/v1", perception_id: `ZY-PERC-${pid}`, occurred_at: now(), operator: "ICE-P-ZY001(铸渊)perception-kit v1", target: arg("--app") ? `app:${arg("--app")}` : (arg("--url") ? `url:${arg("--url")}` : "unknown"), semantic_track: {}, translation_track: {}, detector_findings: [], conclusion: "NOT_SEEN_NO_TARGET", }; // ── 语义轨:进程状态 ───────────────────────────── const procList = run("/bin/ps", ["-axo", "pid,comm,etime"]); if (typeof procList === "string") { const name = (arg("--app") || "HoloLake").toLowerCase(); const procs = procList.split("\n").filter((l) => l.toLowerCase().includes(name)); if (process.env.PERCEIVE_DEBUG) { console.error(`[debug] ps bytes=${procList.length} name=${name} matches=${procs.length}`); console.error(`[debug] head=${JSON.stringify(procList.slice(0,80))} tail=${JSON.stringify(procList.slice(-80))}`); } receipt.semantic_track.process_state = { found: procs.length, sample: procs.slice(0, 5), }; } else receipt.semantic_track.process_state = procList; // ── 翻译轨:窗口截图 ───────────────────────────── if (arg("--app")) { // 用 CGWindowList 经 python 找窗口 ID(不弹权限窗:仅查询) const winInfo = run("/usr/bin/python3", ["-c", ` import Quartz, json wins = [] for w in Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListOptionOnScreenOnly | Quartz.kCGWindowListExcludeDesktopElements, Quartz.kCGNullWindowID): name = w.get("kCGWindowName", "") or w.get("kCGWindowOwnerName", "") if "${arg("--app")}".lower() in str(name).lower() or "${arg("--app")}".lower() in str(w.get("kCGWindowOwnerName", "")).lower(): wins.append({"id": w.get("kCGWindowNumber"), "name": name, "owner": w.get("kCGWindowOwnerName"), "bounds": list(w.get("kCGWindowBounds", {}).values())}) print(json.dumps(wins[:5])) `]); let win; try { win = JSON.parse(winInfo); } catch { win = []; } if (Array.isArray(win) && win.length) { const shot = path.join(OUT_DIR, `${receipt.perception_id}.png`); const cap = run("/usr/sbin/screencapture", ["-x", "-l", String(win[0].id), shot]); if (!cap || cap.__error === undefined || fs.existsSync(shot)) { const buf = fs.readFileSync(shot); receipt.translation_track = { capture_method: `screencapture -l ${win[0].id}`, window: win[0], screenshot_path: shot, screenshot_sha256: shasum(buf), screenshot_bytes: buf.length, note: buf.length < 20000 && buf.length > 0 ? "截图异常小,可能无屏幕录制权限(黑屏帧)" : "ok", }; } else receipt.translation_track = { error: cap.__error }; } else receipt.translation_track = { error: "window_not_found", queried: winInfo }; } // ── 语义轨:DOM(url模式)+ 重影检测器 ───────────── if (arg("--url")) { const html = run("/usr/bin/curl", ["-s", "--max-time", "10", arg("--url")]); if (typeof html === "string" && html.length) { receipt.semantic_track.dom_snapshot_digest = { url: arg("--url"), bytes: html.length, sha256: shasum(html), title: (html.match(/