前人工作区变更归档(元审计遗留处置·冰朔授权'交给你来处理'·不否定过去原则):hololake-phase-b-staging删除组/WORK区/submodule指针/0826壳源/neural-chain/skills-mirror/codex恢复帧/.zcode本地配置等140项原样冻结入库——不删除不修改不评判,作为历史一环保留;WX-SECRETS回执经查仅含公开AppID无AppSecret,安全

This commit is contained in:
冰朔 2026-09-01 22:18:31 +08:00
commit a40f29c12e
2442 changed files with 596545 additions and 6601 deletions

View file

@ -0,0 +1,404 @@
//! Agent 执行层 v1(个人频道 · 底层)。
//! 语义正本:TCS-PROTOCOL-KB-AGENT-RUNTIME-0001(操作注册/循环规则/权限/回执格式住 TCS,
//! 本模块只做机械执行,不载语义——donor 逻辑来源:guada sub-agent 流式回执与 skills 结构、
//! grok dream 的"整理报告"思想;无 LLM 自主循环,自主性属人格体大脑)。
//! 架构(冰朔 2026-08-27 定):底层=本执行层;上层=知识库;与知识库平行=人类交互入口
//! (AgentConsole,后续接模型 API——设置见 agent_settings)。
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::fs;
use std::path::PathBuf;
use tauri::AppHandle;
use crate::knowledge_base::{
self, CreateKnowledgeDocumentInput, ReadKnowledgeDocumentInput, SaveKnowledgeDocumentInput,
};
pub const OPERATION_REGISTRY: &[(&str, &str, &str)] = &[
// (命令, 操作号, 说明) —— 与 TCS-PROTOCOL-KB-AGENT-RUNTIME-0001 operations 一致
("/help", "KBAG-OP-001", "列出全部命令"),
("/list", "KBAG-OP-002", "知识库文档清单"),
("/search <词>", "KBAG-OP-003", "全文检索"),
("/read <路径>", "KBAG-OP-004", "读取文档"),
("/write <标题> | <正文>", "KBAG-OP-005", "新建/写入文档(git 托管)"),
("/organize", "KBAG-OP-006", "整理:统计+空文档检测+生成人类可读报告入库"),
("/report", "KBAG-OP-007", "把最近一次任务回执翻译成人类可读文档入库"),
("/import <绝对路径>", "KBAG-OP-008", "导入外部文件(支持 md/txt/csv/json,实测格式)"),
("/skills", "KBAG-OP-009", "列出内置技能"),
("/run <技能名>", "KBAG-OP-010", "运行内置技能(当前:kb.organize.daily)"),
];
const BUILT_IN_SKILLS: &[(&str, &str)] = &[
("kb.organize.daily", "整理知识库并生成当日人类可读报告(= /organize + /report)"),
("kb.health.check", "知识库健康检查:文档数/总量/git 状态/空文档"),
];
fn step(op: &str, decision: &str, detail: String) -> Value {
json!({"operation": op, "decision": decision, "detail": detail})
}
fn now_ms() -> u128 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis())
.unwrap_or(0)
}
/// 执行一条人类指令,返回结构化回执(每步留痕,无模型、确定性)。
pub async fn kb_agent_execute(app: AppHandle, command: String) -> Result<Value, String> {
let started = std::time::Instant::now();
let trimmed = command.trim().to_string();
let mut parts = trimmed.splitn(3, ' ');
let cmd = parts.next().unwrap_or("").to_string();
let arg1 = parts.next().unwrap_or("").to_string();
let rest = trimmed.splitn(2, ' ').nth(1).unwrap_or("").to_string();
let mut steps: Vec<Value> = Vec::new();
let mut final_outcome = "PASS";
match cmd.as_str() {
"/help" => {
let text = OPERATION_REGISTRY
.iter()
.map(|(c, id, d)| format!("{c}({id})—— {d}"))
.collect::<Vec<_>>()
.join("\n");
steps.push(step("KBAG-OP-001", "DONE", text));
}
"/list" => {
let snap = knowledge_base::get_knowledge_snapshot(app.clone()).await?;
let lines: Vec<String> = snap
.documents
.iter()
.map(|d| format!("- {}({},{} B)", d.title, d.path, d.size_bytes))
.collect();
steps.push(step(
"KBAG-OP-002",
"DONE",
format!("共 {} 份文档\n{}", snap.documents.len(), lines.join("\n")),
));
}
"/search" => {
if arg1.is_empty() {
final_outcome = "FAIL";
steps.push(step("KBAG-OP-003", "DENY", "缺检索词".into()));
} else {
let hits = knowledge_base::search_knowledge(
app.clone(),
knowledge_base::SearchKnowledgeInput { query: arg1.clone() },
)
.await?;
let lines: Vec<String> = hits
.iter()
.take(20)
.map(|h| format!("- {}({})", h.path, h.title))
.collect();
steps.push(step(
"KBAG-OP-003",
"DONE",
format!("命中 {} 处:\n{}", hits.len(), lines.join("\n")),
));
}
}
"/read" => {
if arg1.is_empty() {
final_outcome = "FAIL";
steps.push(step("KBAG-OP-004", "DENY", "缺路径".into()));
} else {
let doc = knowledge_base::read_knowledge_document(
app.clone(),
ReadKnowledgeDocumentInput { source: "native".into(), path: arg1.clone() },
)
.await?;
steps.push(step("KBAG-OP-004", "DONE", format!("# {}\n\n{}", doc.title, doc.body)));
}
}
"/write" => {
let (title, body) = match rest.split_once('|') {
Some((t, b)) => (t.trim().to_string(), b.trim().to_string()),
None => (rest.trim().to_string(), String::new()),
};
if title.is_empty() {
final_outcome = "FAIL";
steps.push(step("KBAG-OP-005", "DENY", "缺标题".into()));
} else {
let created = knowledge_base::create_knowledge_document(
app.clone(),
CreateKnowledgeDocumentInput { title: title.clone() },
)
.await?;
let before = knowledge_base::read_knowledge_document(
app.clone(),
ReadKnowledgeDocumentInput { source: "native".into(), path: created.removed.clone() },
)
.await?;
let full = format!(
"# {title}\n\n{body}\n\n---\n由知识库 Agent 执行层写入 · KBAG-OP-005 · {}\n",
now_ms()
);
let saved = knowledge_base::save_knowledge_document(
app.clone(),
SaveKnowledgeDocumentInput {
path: created.removed.clone(),
body: full,
expected_content_sha256: before.content_sha256.clone(),
},
)
.await?;
steps.push(step(
"KBAG-OP-005",
"DONE",
format!("已写入 {} · git_commit={}", saved.document.path, &saved.git_commit[..12.min(saved.git_commit.len())]),
));
}
}
"/organize" => {
let snap = knowledge_base::get_knowledge_snapshot(app.clone()).await?;
let total: u64 = snap.documents.iter().map(|d| d.size_bytes).sum();
let mut empty = Vec::new();
for d in &snap.documents {
if let Ok(doc) = knowledge_base::read_knowledge_document(
app.clone(),
ReadKnowledgeDocumentInput { source: "native".into(), path: d.path.clone() },
)
.await
{
if doc.body.trim().len() < 40 {
empty.push(doc.title.clone());
}
}
}
let report = format!(
"# 知识库整理报告\n\n生成:光湖历毫秒 {}\n\n- 文档总数:{}\n- 总字节数:{}\n- 疑似空文档(<40字符):{}\n\n## 建议\n对空文档执行 /write 补充内容,或保持待办状态。\n",
now_ms(),
snap.documents.len(),
total,
if empty.is_empty() { "无".to_string() } else { empty.join("、") }
);
let report_path = write_report_doc(app.clone(), report).await?;
steps.push(step(
"KBAG-OP-006",
"DONE",
format!("统计完成:{} 份 / {} B;空文档 {};人类可读报告已入库 {}", snap.documents.len(), total, empty.len(), report_path),
));
}
"/report" => {
let body = format!(
"# Agent 任务回执(人类可读翻译)\n\n时间:{}\n上一条指令:`{}`\n\n> 本报告由知识库翻译器从结构化回执自动翻译生成,存放于 git 托管的知识库内。\n",
now_ms(),
trimmed.chars().take(120).collect::<String>()
);
let path = write_report_doc(app.clone(), body).await?;
steps.push(step("KBAG-OP-007", "DONE", format!("翻译完成,已入库 {path}")));
}
"/import" => {
if arg1.is_empty() {
final_outcome = "FAIL";
steps.push(step("KBAG-OP-008", "DENY", "缺文件路径".into()));
} else {
let src = PathBuf::from(&arg1);
let ext = src.extension().and_then(|e| e.to_str()).unwrap_or("").to_lowercase();
let raw = fs::read_to_string(&src)
.map_err(|e| format!("HOLOLAKE_AGENT_IMPORT_READ_FAILED: {e}"))?;
let (title, body) = translate_import(&src.file_name().and_then(|n| n.to_str()).unwrap_or("import"), &ext, &raw);
let created = knowledge_base::create_knowledge_document(
app.clone(),
CreateKnowledgeDocumentInput { title: title.clone() },
)
.await?;
let before = knowledge_base::read_knowledge_document(
app.clone(),
ReadKnowledgeDocumentInput { source: "native".into(), path: created.removed.clone() },
)
.await?;
let saved = knowledge_base::save_knowledge_document(
app.clone(),
SaveKnowledgeDocumentInput {
path: created.removed.clone(),
body: format!("# {title}\n\n{body}\n\n---\n导入源:{arg1} · 格式:{ext} · KBAG-OP-008\n"),
expected_content_sha256: before.content_sha256.clone(),
},
)
.await?;
steps.push(step(
"KBAG-OP-008",
"DONE",
format!("已导入 {ext} 文件({} 字符)→ {} · git_commit={}", raw.len(), saved.document.path, &saved.git_commit[..12.min(saved.git_commit.len())]),
));
}
}
"/skills" => {
let text = BUILT_IN_SKILLS
.iter()
.map(|(id, d)| format!("- {id}:{d}"))
.collect::<Vec<_>>()
.join("\n");
steps.push(step("KBAG-OP-009", "DONE", text));
}
"/run" => {
match arg1.as_str() {
"kb.organize.daily" | "kb.health.check" => {
let snap = knowledge_base::get_knowledge_snapshot(app.clone()).await?;
let total: u64 = snap.documents.iter().map(|d| d.size_bytes).sum();
let report = format!(
"# 技能运行报告 · {arg1}\n\n时间:{}\n\n- 文档总数:{}\n- 总字节数:{}\n- git 托管:native root {}\n\n由内置技能生成(TCS 操作注册 KBAG-OP-010)。\n",
now_ms(), snap.documents.len(), total, snap.native_root
);
let path = write_report_doc(app.clone(), report).await?;
steps.push(step("KBAG-OP-010", "DONE", format!("技能 {arg1} 运行完成,报告入库 {path}")));
}
_ => {
final_outcome = "FAIL";
steps.push(step("KBAG-OP-010", "DENY", format!("未知技能:{arg1}(/skills 查看)")));
}
}
}
_ => {
final_outcome = "FAIL";
steps.push(step("KBAG-OP-000", "DENY", format!("未知命令:{cmd}(/help 查看全部命令)")));
}
}
Ok(json!({
"schema": "hololake.kb-agent-execute-receipt/v1",
"outcome": final_outcome,
"layer": "AGENT_EXECUTION_MODEL_FREE",
"command": trimmed,
"model_api_called": false,
"steps": steps,
"elapsed_ms": started.elapsed().as_millis() as u64,
}))
}
/// 把任意结构化内容翻译成人类可读文档并写入知识库(git 托管)——知识库翻译器。
async fn write_report_doc(app: AppHandle, body: String) -> Result<String, String> {
let stamp = now_ms();
let created = knowledge_base::create_knowledge_document(
app.clone(),
CreateKnowledgeDocumentInput { title: format!("Agent报告-{stamp}") },
)
.await?;
let before = knowledge_base::read_knowledge_document(
app.clone(),
ReadKnowledgeDocumentInput { source: "native".into(), path: created.removed.clone() },
)
.await?;
let saved = knowledge_base::save_knowledge_document(
app,
SaveKnowledgeDocumentInput { path: created.removed.clone(), body, expected_content_sha256: before.content_sha256 },
)
.await?;
Ok(saved.document.path)
}
/// 外部文件 → 人类可读 markdown(导入翻译)。支持 md/txt/csv/json,四种格式均实测。
fn translate_import(name: &str, ext: &str, raw: &str) -> (String, String) {
let title = name.to_string();
match ext.as_ref() {
"md" | "markdown" => (title, raw.to_string()),
"txt" => (title, format!("```text\n{raw}\n```")),
"csv" => {
let mut lines = raw.lines();
let header: Vec<&str> = lines.next().unwrap_or("").split(',').map(|h| h.trim()).collect();
let mut table = String::from("| ");
table.push_str(&header.join(" | "));
table.push_str(" |\n| ");
table.push_str(&vec!["---"; header.len()].join(" | "));
table.push_str(" |\n");
for line in lines {
let cells: Vec<&str> = line.split(',').map(|c| c.trim()).collect();
table.push_str("| ");
table.push_str(&cells.join(" | "));
table.push_str(" |\n");
}
(title, table)
}
"json" => {
let pretty = serde_json::from_str::<Value>(raw)
.map(|v| serde_json::to_string_pretty(&v).unwrap_or(raw.to_string()))
.unwrap_or_else(|_| raw.to_string());
(title, format!("```json\n{pretty}\n```"))
}
_other => (title, format!("(未知格式 {_other},按纯文本导入)\n```text\n{raw}\n```")),
}
}
// ---------- Agent 设置(用户自填 API,真实持久化+真实连接测试) ----------
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", default)]
pub struct AgentSettings {
pub provider_name: String,
pub base_url: String,
pub api_key: String,
pub model: String,
}
impl Default for AgentSettings {
fn default() -> Self {
Self { provider_name: String::new(), base_url: String::new(), api_key: String::new(), model: String::new() }
}
}
fn settings_path(app: &AppHandle) -> Result<PathBuf, String> {
use tauri::Manager;
let dir = app
.path()
.app_data_dir()
.map_err(|e| format!("HOLOLAKE_APP_DATA_UNAVAILABLE: {e}"))?;
Ok(dir.join("agent-settings-v1.json"))
}
pub fn agent_settings_get(app: AppHandle) -> Result<AgentSettings, String> {
let path = settings_path(&app)?;
if !path.exists() {
return Ok(AgentSettings::default());
}
serde_json::from_str(&fs::read_to_string(path).unwrap_or_default())
.map_err(|e| format!("HOLOLAKE_AGENT_SETTINGS_INVALID: {e}"))
}
pub fn agent_settings_set(app: AppHandle, settings: AgentSettings) -> Result<AgentSettings, String> {
let path = settings_path(&app)?;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).map_err(|e| format!("HOLOLAKE_AGENT_SETTINGS_WRITE_FAILED: {e}"))?;
}
// API key 以 0600 权限落盘,与账号隔离存储同纪律。
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let _ = fs::set_permissions(&path, fs::Permissions::from_mode(0o600));
}
let body = serde_json::to_string_pretty(&settings)
.map_err(|e| format!("HOLOLAKE_AGENT_SETTINGS_WRITE_FAILED: {e}"))?;
fs::write(&path, body).map_err(|e| format!("HOLOLAKE_AGENT_SETTINGS_WRITE_FAILED: {e}"))?;
Ok(settings)
}
/// 真实连接测试:向用户填写的 baseURL 发一次 OpenAI 兼容探测(GET /models,带鉴权头)。
/// 结果如实返回 HTTP 状态,不伪造成功。
pub async fn agent_api_test(settings: AgentSettings) -> Result<Value, String> {
if settings.base_url.trim().is_empty() {
return Ok(json!({"outcome": "FAIL", "detail": "baseURL 为空"}));
}
let base = settings.base_url.trim().trim_end_matches('/');
let url = format!("{base}/models");
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()
.map_err(|e| format!("HOLOLAKE_AGENT_API_TEST_FAILED: {e}"))?;
let mut req = client.get(&url);
if !settings.api_key.trim().is_empty() {
req = req.bearer_auth(settings.api_key.trim());
}
match req.send().await {
Ok(resp) => {
let status = resp.status().as_u16();
Ok(json!({
"outcome": if (200..300).contains(&status) { "PASS" } else { "FAIL" },
"detail": format!("GET {url} → HTTP {status}"),
"http_status": status
}))
}
Err(e) => Ok(json!({"outcome": "FAIL", "detail": format!("GET {url} → {e}"), "http_status": 0})),
}
}

View file

@ -551,3 +551,112 @@ mod tests {
);
}
}
// —— 机器指纹 + 编号登录(冰朔双通道设计的第二条路 · 2026-08-26)——
// 语义:编号边界已验证的编号 + 本机稳定指纹(IOPlatformSerialNumber,不受
// VPN/局域网/热点影响)= 本机受信设备登录;不验服务器、不存密码。
// 首次登录 TOFU 落绑定文件;再次登录核对绑定未变。会话 host 固定为
// local-machine-identity,账号存储 scope 由 domain+host+username 哈希隔离,
// 与仓库账号登录的数据目录互不共享。
#[cfg(target_os = "macos")]
fn machine_fingerprint() -> Result<String, String> {
use ring::digest::{digest, SHA256};
let output = std::process::Command::new("/usr/sbin/ioreg")
.args(["-rd1", "-c", "IOPlatformExpertDevice"])
.output()
.map_err(|error| format!("HOLOLAKE_MACHINE_IDENTITY_UNAVAILABLE: {error}"))?;
let text = String::from_utf8_lossy(&output.stdout);
let value = ["IOPlatformSerialNumber", "IOPlatformUUID"]
.iter()
.find_map(|key| {
text.lines().find_map(|line| {
let (k, rest) = line.split_once('=')?;
if k.trim().trim_matches(chr(34).to_string().as_str()) != *key {
return None;
}
let v = rest.trim().trim_matches('"');
if v.is_empty() { None } else { Some(v.to_string()) }
})
})
.ok_or_else(|| "HOLOLAKE_MACHINE_IDENTITY_UNAVAILABLE".to_string())?;
let hashed = digest(&SHA256, value.as_bytes());
Ok(hashed.as_ref().iter().map(|byte| format!("{byte:02x}")).collect())
}
#[cfg(not(target_os = "macos"))]
fn machine_fingerprint() -> Result<String, String> {
Err("HOLOLAKE_MACHINE_IDENTITY_UNSUPPORTED_PLATFORM".into())
}
pub async fn perform_machine_identity_login(
app: AppHandle,
state: State<'_, ZeroPointState>,
) -> Result<LoginReceipt, String> {
let Some((human_number, domain)) = zero_point::verified_user_route(&state)? else {
return Err("HOLOLAKE_DOMAIN_ROUTE_REQUIRED".into());
};
let fingerprint = machine_fingerprint()?;
let data_dir = app
.path()
.app_data_dir()
.map_err(|error| format!("HOLOLAKE_APP_DATA_UNAVAILABLE: {error}"))?;
let binding_dir = data_dir.join("machine-identity-v1");
fs::create_dir_all(&binding_dir)
.map_err(|error| format!("HOLOLAKE_MACHINE_IDENTITY_STORE_FAILED: {error}"))?;
use ring::digest::{digest, SHA256};
let material = format!("{human_number}\0{fingerprint}");
let hashed = digest(&SHA256, material.as_bytes());
let binding_name: String = hashed.as_ref().iter().map(|byte| format!("{byte:02x}")).collect();
let binding_path = binding_dir.join(format!("{binding_name}.json"));
let now_ms = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_millis() as u64)
.unwrap_or(0);
if binding_path.exists() {
let bound: serde_json::Value = serde_json::from_str(
&fs::read_to_string(&binding_path)
.map_err(|error| format!("HOLOLAKE_MACHINE_IDENTITY_READ_FAILED: {error}"))?,
)
.map_err(|error| format!("HOLOLAKE_MACHINE_IDENTITY_READ_FAILED: {error}"))?;
if bound.get("fingerprint_sha256").and_then(|value| value.as_str()) != Some(fingerprint.as_str()) {
return Err("HOLOLAKE_MACHINE_IDENTITY_MISMATCH".into());
}
} else {
let record = serde_json::json!({
"schema": "hololake.machine-identity-binding/v1",
"human_number": human_number,
"fingerprint_sha256": fingerprint,
"bound_at_unix_ms": now_ms,
});
let body = serde_json::to_string_pretty(&record)
.map_err(|error| format!("HOLOLAKE_MACHINE_IDENTITY_STORE_FAILED: {error}"))?;
fs::write(&binding_path, body)
.map_err(|error| format!("HOLOLAKE_MACHINE_IDENTITY_STORE_FAILED: {error}"))?;
}
let username = format!("{human_number}@machine");
let host = "local-machine-identity".to_string();
let session = LoginSession {
username: username.clone(),
host: host.clone(),
domain: domain.clone(),
signed_in_at_unix_ms: now_ms,
};
let path = session_path(&app)?;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.map_err(|error| format!("HOLOLAKE_LOGIN_SESSION_WRITE_FAILED: {error}"))?;
}
fs::write(
&path,
serde_json::to_string_pretty(&session)
.map_err(|error| format!("HOLOLAKE_LOGIN_SESSION_WRITE_FAILED: {error}"))?,
)
.map_err(|error| format!("HOLOLAKE_LOGIN_SESSION_WRITE_FAILED: {error}"))?;
Ok(LoginReceipt {
username,
email: String::new(),
host,
domain,
})
}

View file

@ -18,6 +18,7 @@ mod gls_protocol_runtime;
mod home_status;
mod human_authorization;
mod knowledge_base;
mod agent_runtime;
mod kb_agent;
mod living_cognitive_substrate;
mod local_development_bridge;
@ -89,6 +90,58 @@ pub fn run() {
.setup(|app| {
// GLS 是 HoloLake 产品内核,不是开发机旁路服务。合同、依赖闭包、
// 自举编译器或回执账本任一不可用时,产品启动失败关闭。
if std::env::args().any(|a| a == "--kb-agent-self-test") {
let handle = app.handle().clone();
tauri::async_runtime::block_on(async move {
let mut results: Vec<serde_json::Value> = Vec::new();
// 自建零点态(setup 后段才 manage,这里自管自用后即退出)
let zp_state = crate::zero_point::ZeroPointState::default();
if let Err(error) = crate::zero_point::boot_zero_point(&handle, &zp_state) {
results.push(serde_json::json!({"step":"zp_boot","outcome":"FAIL","detail":error.to_string()}));
}
handle.manage(zp_state);
// 1) 机器指纹登录(真路径)
let zp = handle.state::<crate::zero_point::ZeroPointState>();
match crate::code_repo_login::perform_machine_identity_login(handle.clone(), zp).await {
Ok(r) => results.push(serde_json::json!({"step":"machine_login","outcome":"PASS","detail":format!("{}@{}", r.username, r.host)})),
Err(e) => { results.push(serde_json::json!({"step":"machine_login","outcome":"FAIL","detail":e})); }
}
// 2) 全命令循环
let home = std::env::var("HOME").unwrap_or_default();
let cmds: Vec<String> = vec![
"/help".into(), "/write 自测文档 | 这是 Agent 执行层自测写入的内容,验证真实可用。".into(),
"/search 自测".into(), "/list".into(),
format!("/import {home}/kb-import-samples/sample.md"),
format!("/import {home}/kb-import-samples/sample.txt"),
format!("/import {home}/kb-import-samples/sample.csv"),
format!("/import {home}/kb-import-samples/sample.json"),
"/organize".into(), "/report".into(), "/skills".into(), "/run kb.organize.daily".into(),
];
for c in &cmds {
match crate::agent_runtime::kb_agent_execute(handle.clone(), c.clone()).await {
Ok(r) => results.push(serde_json::json!({"step":"execute","cmd":c,"outcome":r["outcome"],"detail":r["steps"]})),
Err(e) => results.push(serde_json::json!({"step":"execute","cmd":c,"outcome":"FAIL","detail":e})),
}
}
// 3) 设置保存+真实连接测试(对自家域名,真实 HTTP,404 也是真结果)
let s = crate::agent_runtime::AgentSettings {
provider_name: "self-test".into(),
base_url: "https://guanghubingshuo.com".into(),
api_key: String::new(),
model: "none".into(),
};
match crate::agent_runtime::agent_settings_set(handle.clone(), s.clone()) {
Ok(_) => results.push(serde_json::json!({"step":"settings_set","outcome":"PASS"})),
Err(e) => results.push(serde_json::json!({"step":"settings_set","outcome":"FAIL","detail":e})),
}
match crate::agent_runtime::agent_api_test(s).await {
Ok(r) => results.push(serde_json::json!({"step":"api_test","outcome":r["outcome"],"detail":r["detail"]})),
Err(e) => results.push(serde_json::json!({"step":"api_test","outcome":"FAIL","detail":e})),
}
println!("{}", serde_json::to_string_pretty(&serde_json::json!({"schema":"hololake.kb-agent-self-test/v1","results":results})).unwrap_or_default());
std::process::exit(0);
});
}
if std::env::args().any(|a| a == "--kb-agent-acceptance") {
let handle = app.handle().clone();
tauri::async_runtime::block_on(async move {

View file

@ -40,6 +40,30 @@ pub(crate) async fn dispatch(
"kb_agent::kb_agent_acceptance" => {
json(crate::kb_agent::kb_agent_acceptance(app).await?)
}
"agent_runtime::kb_agent_execute" => json(
crate::agent_runtime::kb_agent_execute(
app.clone(),
string_field(&payload, "command")?,
)
.await?,
),
"agent_runtime::agent_settings_get" => {
json(crate::agent_runtime::agent_settings_get(app.clone())?)
}
"agent_runtime::agent_settings_set" => {
json(crate::agent_runtime::agent_settings_set(
app.clone(),
serde_json::from_value(payload.get("settings").cloned().unwrap_or(Value::Null))
.map_err(|error| format!("HOLOLAKE_AGENT_SETTINGS_INVALID: {error}"))?,
)?)
}
"agent_runtime::agent_api_test" => json(
crate::agent_runtime::agent_api_test(
serde_json::from_value(payload.get("settings").cloned().unwrap_or(Value::Null))
.map_err(|error| format!("HOLOLAKE_AGENT_SETTINGS_INVALID: {error}"))?,
)
.await?,
),
"home_status::get_hololake_home_status" => {
json(crate::home_status::get_hololake_home_status(
app.clone(),
@ -544,6 +568,13 @@ pub(crate) async fn dispatch(
)
.await?,
),
"code_repo_login::perform_machine_identity_login" => json(
crate::code_repo_login::perform_machine_identity_login(
app.clone(),
app.state::<crate::zero_point::ZeroPointState>(),
)
.await?,
),
"code_repo_login::change_first_login_password" => json(
crate::code_repo_login::change_first_login_password(
app.state::<crate::zero_point::ZeroPointState>(),