mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 13:32:23 +00:00
* test(e2e): skill execution flow (core RPC + Skills UI) - Add skill-execution-flow.spec: core.ping, skills start/list_tools/call_tool/stop via shared RPC helper, seeded QuickJS echo skill (same tree as Rust json_rpc_e2e). - Add core-rpc helpers: WebView invoke on tauri-driver, Node fetch + port probe on Appium Mac2 (no WKWebView execute). - Extend navigateViaHash with Mac2 sidebar label fallbacks for /skills, /home, etc. - Wire yarn test:e2e:skill-execution and e2e-run-all-flows. Refs: #68 Made-with: Cursor * fix(e2e): address CodeRabbit review for skill execution PR - core-rpc-node: honor OPENHUMAN_CORE_HOST and OPENHUMAN_CORE_PORT before port scan - shared-flows: Mac2 navigateViaHash for /settings/billing; throw on unmapped hash; navigateToBilling fallback without WebView execute on Mac2 - skill-e2e-runtime: javascript manifest + removeSeededEchoSkill teardown - skill-execution-flow: teardown seeded skill; document RPC shapes vs json_rpc_e2e; skip placeholder for future agent chat tool_calls Made-with: Cursor
97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
/**
|
|
* Seeds the minimal QuickJS echo skill used by Rust `json_rpc_skills_runtime_start_tools_call_stop`
|
|
* so the desktop core can run `openhuman.skills_start` → `skills_call_tool` against a real skill tree.
|
|
*/
|
|
import fs from 'node:fs/promises';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
|
|
export const E2E_RUNTIME_SKILL_ID = 'e2e-runtime';
|
|
|
|
const MANIFEST = {
|
|
id: E2E_RUNTIME_SKILL_ID,
|
|
name: 'E2E Runtime Skill',
|
|
version: '1.0.0',
|
|
description: 'Minimal skill for desktop E2E (echo tool)',
|
|
/** Matches `SkillManifest` docs (`manifest.rs`); executed via QuickJS like `quickjs` alias. */
|
|
runtime: 'javascript',
|
|
entry: 'index.js',
|
|
auto_start: false,
|
|
};
|
|
|
|
const INDEX_JS = `globalThis.__skill = {
|
|
name: "E2E Runtime Skill",
|
|
tools: [
|
|
{
|
|
name: "echo",
|
|
description: "Echoes back the input message",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: {
|
|
message: { type: "string", description: "Message to echo" }
|
|
},
|
|
required: ["message"]
|
|
},
|
|
execute(args) {
|
|
return { type: "text", text: "echo: " + (args.message || "empty") };
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
function init() {
|
|
if (globalThis.__ops && globalThis.__ops.log) {
|
|
globalThis.__ops.log("info", "e2e-runtime-skill initialized");
|
|
}
|
|
}
|
|
|
|
init();
|
|
`;
|
|
|
|
/** Resolve directories that should contain `e2e-runtime/manifest.json` (core may use either). */
|
|
export function resolveE2eRuntimeSkillDirs(): string[] {
|
|
const dirs: string[] = [];
|
|
const homeDir = path.join(
|
|
os.homedir(),
|
|
'.openhuman',
|
|
'workspace',
|
|
'skills',
|
|
E2E_RUNTIME_SKILL_ID
|
|
);
|
|
dirs.push(homeDir);
|
|
|
|
const w = process.env.OPENHUMAN_WORKSPACE?.trim();
|
|
if (w) {
|
|
const resolved = path.resolve(w);
|
|
const nested =
|
|
path.basename(resolved) === 'workspace'
|
|
? path.join(resolved, 'skills', E2E_RUNTIME_SKILL_ID)
|
|
: path.join(resolved, 'workspace', 'skills', E2E_RUNTIME_SKILL_ID);
|
|
if (nested !== homeDir) {
|
|
dirs.push(nested);
|
|
}
|
|
}
|
|
|
|
return dirs;
|
|
}
|
|
|
|
export async function seedMinimalEchoSkill(): Promise<void> {
|
|
const manifestBody = JSON.stringify(MANIFEST, null, 2);
|
|
for (const skillRoot of resolveE2eRuntimeSkillDirs()) {
|
|
await fs.mkdir(skillRoot, { recursive: true });
|
|
await fs.writeFile(path.join(skillRoot, 'manifest.json'), manifestBody, 'utf-8');
|
|
await fs.writeFile(path.join(skillRoot, 'index.js'), INDEX_JS, 'utf-8');
|
|
}
|
|
}
|
|
|
|
/** Remove seeded `e2e-runtime` skill dirs so E2E runs stay isolated. */
|
|
export async function removeSeededEchoSkill(): Promise<void> {
|
|
for (const skillRoot of resolveE2eRuntimeSkillDirs()) {
|
|
try {
|
|
await fs.rm(skillRoot, { recursive: true, force: true });
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
}
|