@runfusion/fusion 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/bin.js +6505 -2184
  2. package/dist/client/assets/AgentDetailView-C1XceMgi.js +18 -0
  3. package/dist/client/assets/AgentDetailView-CeO_1MK7.css +1 -0
  4. package/dist/client/assets/AgentsView-DSGQWObq.css +1 -0
  5. package/dist/client/assets/AgentsView-Deh125ss.js +527 -0
  6. package/dist/client/assets/ChatView-7D_RQDqT.js +1 -0
  7. package/dist/client/assets/DevServerView-C9lzHrcT.js +1 -0
  8. package/dist/client/assets/DirectoryPicker-aVdFaV37.js +1 -0
  9. package/dist/client/assets/DocumentsView-DIpg3NSP.js +1 -0
  10. package/dist/client/assets/{InsightsView-CqDethVs.js → InsightsView-jKjEFAx_.js} +2 -2
  11. package/dist/client/assets/{MemoryView-BLIm9Vr7.js → MemoryView-nXlTqebk.js} +2 -2
  12. package/dist/client/assets/{NodesView-DEXvp3WT.js → NodesView-Di2SvOhg.js} +3 -3
  13. package/dist/client/assets/{PiExtensionsManager-C2YjI9o2.js → PiExtensionsManager-Buopv-jb.js} +2 -2
  14. package/dist/client/assets/PluginManager-B9-NbQ8f.js +1 -0
  15. package/dist/client/assets/PluginManager-C1DbPaar.css +1 -0
  16. package/dist/client/assets/ResearchView-_BHXUv2j.js +1 -0
  17. package/dist/client/assets/{RoadmapsView-DPcfX5MS.js → RoadmapsView-DHWjUoc8.js} +2 -2
  18. package/dist/client/assets/{SettingsModal-BRNAPR1u.js → SettingsModal-C89Ikhfm.js} +1 -1
  19. package/dist/client/assets/SettingsModal-DHitIpsa.css +1 -0
  20. package/dist/client/assets/SettingsModal-DR_yirvK.js +31 -0
  21. package/dist/client/assets/SetupWizardModal-BtDMY9pa.js +1 -0
  22. package/dist/client/assets/SkillsView-hDpTBdFT.js +1 -0
  23. package/dist/client/assets/agentSkills-B-w5wFHh.js +1 -0
  24. package/dist/client/assets/agentSkills-DDHJnrkn.css +1 -0
  25. package/dist/client/assets/folder-open-usZkXdq2.js +6 -0
  26. package/dist/client/assets/index-Bc6ZdGMz.css +1 -0
  27. package/dist/client/assets/index-D__RMku8.js +694 -0
  28. package/dist/client/assets/{star-B314SwLA.js → star-BAT_ObKE.js} +2 -2
  29. package/dist/client/assets/upload-BC2YKNEV.js +6 -0
  30. package/dist/client/assets/{users-Bu_ltePs.js → users-Dkd4rtrN.js} +2 -2
  31. package/dist/client/index.html +2 -2
  32. package/dist/client/version.json +1 -1
  33. package/dist/droid-cli/package.json +1 -1
  34. package/dist/extension.js +4411 -932
  35. package/dist/pi-claude-cli/package.json +1 -1
  36. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  37. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraphView.css +12 -3
  38. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/storage.test.ts +12 -2
  39. package/dist/plugins/fusion-plugin-dependency-graph/src/storage.ts +6 -7
  40. package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +649 -0
  41. package/dist/plugins/fusion-plugin-hermes-runtime/manifest.json +14 -0
  42. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +11 -0
  43. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +369 -0
  44. package/dist/plugins/fusion-plugin-openclaw-runtime/manifest.json +14 -0
  45. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +11 -0
  46. package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +966 -0
  47. package/dist/plugins/fusion-plugin-paperclip-runtime/manifest.json +15 -0
  48. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +11 -0
  49. package/package.json +2 -1
  50. package/skill/fusion/references/engine-tools.md +6 -3
  51. package/dist/client/assets/AgentDetailView-CUtWvXBn.css +0 -1
  52. package/dist/client/assets/AgentDetailView-Dg7Qa1rG.js +0 -18
  53. package/dist/client/assets/ChatView-ODq-kBk6.js +0 -1
  54. package/dist/client/assets/DevServerView-6PS9Lvl7.js +0 -1
  55. package/dist/client/assets/DirectoryPicker-B3dza2Dq.js +0 -1
  56. package/dist/client/assets/DocumentsView-Bu9YYlki.js +0 -1
  57. package/dist/client/assets/PluginManager-DA_T0GHn.css +0 -1
  58. package/dist/client/assets/PluginManager-Dnf-LhYw.js +0 -1
  59. package/dist/client/assets/ResearchView-Z0TZ7WGo.js +0 -1
  60. package/dist/client/assets/SettingsModal-B6RN9VYe.js +0 -31
  61. package/dist/client/assets/SettingsModal-BWe0KrGY.css +0 -1
  62. package/dist/client/assets/SetupWizardModal-BFc3xID2.js +0 -1
  63. package/dist/client/assets/SkillsView-CipGahOR.js +0 -1
  64. package/dist/client/assets/index-Df1bHDY4.css +0 -1
  65. package/dist/client/assets/index-NFptaeUQ.js +0 -1222
@@ -0,0 +1,649 @@
1
+ // ../plugin-sdk/dist/index.js
2
+ function definePlugin(plugin2) {
3
+ return plugin2;
4
+ }
5
+
6
+ // ../../plugins/fusion-plugin-hermes-runtime/dist/cli-spawn.js
7
+ import { spawn, spawnSync } from "node:child_process";
8
+ import os from "node:os";
9
+ import path, { sep as PATH_SEP } from "node:path";
10
+ var resolvedBinaryCache = /* @__PURE__ */ new Map();
11
+ function resolveBinaryForSpawn(binary) {
12
+ if (process.platform !== "win32")
13
+ return binary;
14
+ if (binary.includes(PATH_SEP) || binary.includes("/") || /\.[a-z]{2,4}$/i.test(binary)) {
15
+ return binary;
16
+ }
17
+ const cached = resolvedBinaryCache.get(binary);
18
+ if (cached)
19
+ return cached;
20
+ try {
21
+ const result = spawnSync("where", [binary], { encoding: "utf-8" });
22
+ if (result.status === 0) {
23
+ const first = (result.stdout ?? "").trim().split(/\r?\n/)[0];
24
+ if (first?.length) {
25
+ resolvedBinaryCache.set(binary, first);
26
+ return first;
27
+ }
28
+ }
29
+ } catch {
30
+ }
31
+ return binary;
32
+ }
33
+ var ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]/g;
34
+ var SESSION_ID_RE = /^session_id:\s+([0-9]{8}_[0-9]{6}_[0-9a-f]{6})\s*$/m;
35
+ var CHROME_LINE_RES = [
36
+ /^\s*┊\s/,
37
+ // memory/status sidebar lines
38
+ /^↻ Resumed session /,
39
+ // resume banner
40
+ /^╭─.*╮\s*$/,
41
+ // top box border
42
+ /^╰─.*╯\s*$/,
43
+ // bottom box border
44
+ /^Query:\s*/
45
+ // query echo line
46
+ ];
47
+ var PROFILE_RULE_RE = /^[\s─]+$/;
48
+ var EM_DASH = "\u2014";
49
+ function parseProfileListOutput(raw) {
50
+ const lines = raw.replace(ANSI_RE, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
51
+ const profiles = [];
52
+ for (const line of lines) {
53
+ if (line.trim() === "")
54
+ continue;
55
+ if (/^\s*Profile\b/.test(line))
56
+ continue;
57
+ if (PROFILE_RULE_RE.test(line))
58
+ continue;
59
+ const stripped = line.replace(/^\s+/, "");
60
+ const columns = stripped.split(/\s{2,}/);
61
+ const rawName = columns[0] ?? "";
62
+ const isDefault = rawName.startsWith("\u25C6");
63
+ const name = rawName.replace(/^◆/, "").trim();
64
+ if (!name)
65
+ continue;
66
+ const toUndef = (v) => v === void 0 || v.trim() === "" || v.trim() === EM_DASH ? void 0 : v.trim();
67
+ profiles.push({
68
+ name,
69
+ model: toUndef(columns[1]),
70
+ gateway: toUndef(columns[2]),
71
+ alias: toUndef(columns[3]),
72
+ isDefault
73
+ });
74
+ }
75
+ return profiles;
76
+ }
77
+ function hermesProfileHome(profileName) {
78
+ const base = process.env.HERMES_HOME ?? path.join(os.homedir(), ".hermes");
79
+ if (profileName === "default" || profileName === "")
80
+ return base;
81
+ return path.join(base, "profiles", profileName);
82
+ }
83
+ async function listHermesProfiles(opts) {
84
+ const binary = resolveBinaryForSpawn(opts?.binaryPath ?? "hermes");
85
+ const timeoutMs = opts?.timeoutMs ?? 5e3;
86
+ return new Promise((resolve2, reject) => {
87
+ let settled = false;
88
+ const child = spawn(binary, ["profile", "list"], {
89
+ stdio: ["ignore", "pipe", "pipe"],
90
+ env: { ...process.env }
91
+ });
92
+ const timer = setTimeout(() => {
93
+ if (settled)
94
+ return;
95
+ settled = true;
96
+ try {
97
+ child.kill("SIGKILL");
98
+ } catch {
99
+ }
100
+ reject(new Error(`hermes profile list failed: timed out after ${timeoutMs}ms`));
101
+ }, timeoutMs);
102
+ let stdout = "";
103
+ let stderr = "";
104
+ child.stdout?.on("data", (chunk) => {
105
+ stdout += chunk.toString("utf-8");
106
+ });
107
+ child.stderr?.on("data", (chunk) => {
108
+ stderr += chunk.toString("utf-8");
109
+ });
110
+ child.on("error", (err) => {
111
+ if (settled)
112
+ return;
113
+ settled = true;
114
+ clearTimeout(timer);
115
+ const isNotFound = err.code === "ENOENT";
116
+ reject(new Error(isNotFound ? `hermes profile list failed: binary not found at "${opts?.binaryPath ?? "hermes"}"` : `hermes profile list failed: ${err.message}`));
117
+ });
118
+ child.on("close", (code) => {
119
+ if (settled)
120
+ return;
121
+ settled = true;
122
+ clearTimeout(timer);
123
+ if (code !== 0) {
124
+ const combined = [stdout, stderr].filter(Boolean).join("\n");
125
+ reject(new Error(`hermes profile list failed: process exited with code ${String(code)}.
126
+ ${combined}`));
127
+ return;
128
+ }
129
+ resolve2(parseProfileListOutput(stdout));
130
+ });
131
+ });
132
+ }
133
+ function resolveCliSettings(settings) {
134
+ const str = (v) => typeof v === "string" && v.trim().length > 0 ? v.trim() : void 0;
135
+ const num = (v, envKey, fallback) => {
136
+ if (typeof v === "number" && Number.isFinite(v) && v > 0)
137
+ return v;
138
+ const raw = str(v) ?? str(process.env[envKey]);
139
+ if (raw !== void 0) {
140
+ const parsed = Number(raw);
141
+ if (Number.isFinite(parsed) && parsed > 0)
142
+ return parsed;
143
+ }
144
+ return fallback;
145
+ };
146
+ const bool = (v, envKey, fallback) => {
147
+ if (typeof v === "boolean")
148
+ return v;
149
+ const raw = str(v) ?? str(process.env[envKey]);
150
+ if (raw !== void 0)
151
+ return raw === "1" || raw.toLowerCase() === "true";
152
+ return fallback;
153
+ };
154
+ return {
155
+ binaryPath: str(settings?.binaryPath) ?? str(process.env.HERMES_BIN) ?? "hermes",
156
+ model: str(settings?.model) ?? str(process.env.HERMES_MODEL_ID),
157
+ provider: str(settings?.provider) ?? str(process.env.HERMES_PROVIDER),
158
+ maxTurns: num(settings?.maxTurns, "HERMES_MAX_TURNS", 12),
159
+ yolo: bool(settings?.yolo, "HERMES_YOLO", false),
160
+ cliTimeoutMs: num(settings?.cliTimeoutMs, "HERMES_CLI_TIMEOUT_MS", 3e5),
161
+ profile: str(settings?.profile) ?? str(process.env.HERMES_PROFILE)
162
+ };
163
+ }
164
+ function cleanText(raw) {
165
+ return raw.replace(ANSI_RE, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
166
+ }
167
+ function stripChrome(text) {
168
+ const lines = text.split("\n");
169
+ const filtered = lines.filter((line) => !CHROME_LINE_RES.some((re) => re.test(line)));
170
+ return filtered.join("\n").trim();
171
+ }
172
+ function parseHermesOutput(rawStdout, rawStderr) {
173
+ const cleaned = cleanText(rawStdout);
174
+ const match = SESSION_ID_RE.exec(cleaned);
175
+ if (!match) {
176
+ const combined = [rawStdout, rawStderr].filter(Boolean).join("\n");
177
+ throw new Error(`hermes: missing session_id in output.
178
+ ${combined}`);
179
+ }
180
+ const sessionId = match[1];
181
+ const sessionIdLineStart = cleaned.lastIndexOf("\nsession_id:");
182
+ const bodyRaw = sessionIdLineStart >= 0 ? cleaned.slice(0, sessionIdLineStart) : cleaned;
183
+ const body = stripChrome(bodyRaw);
184
+ return { body, sessionId };
185
+ }
186
+ function buildHermesArgs(prompt, settings, resumeSessionId) {
187
+ const args = ["chat", "-q", prompt, "-Q", "--source", "tool"];
188
+ if (resumeSessionId) {
189
+ args.push("--resume", resumeSessionId);
190
+ }
191
+ if (settings.model) {
192
+ args.push("-m", settings.model);
193
+ }
194
+ if (settings.provider) {
195
+ args.push("--provider", settings.provider);
196
+ }
197
+ args.push("--max-turns", String(settings.maxTurns));
198
+ if (settings.yolo) {
199
+ args.push("--yolo");
200
+ }
201
+ return args;
202
+ }
203
+ async function invokeHermesCli(prompt, settings, resumeSessionId, signal) {
204
+ const args = buildHermesArgs(prompt, settings, resumeSessionId);
205
+ const binary = resolveBinaryForSpawn(settings.binaryPath);
206
+ return new Promise((resolve2, reject) => {
207
+ let settled = false;
208
+ const spawnEnv = { ...process.env, PYTHONUNBUFFERED: "1" };
209
+ if (settings.profile) {
210
+ spawnEnv.HERMES_HOME = hermesProfileHome(settings.profile);
211
+ }
212
+ const child = spawn(binary, args, {
213
+ stdio: ["ignore", "pipe", "pipe"],
214
+ env: spawnEnv
215
+ });
216
+ const hardKillTimer = setTimeout(() => {
217
+ if (settled)
218
+ return;
219
+ settled = true;
220
+ try {
221
+ child.kill("SIGKILL");
222
+ } catch {
223
+ }
224
+ reject(new Error(`hermes: process timed out after ${settings.cliTimeoutMs}ms`));
225
+ }, settings.cliTimeoutMs);
226
+ const onAbort = () => {
227
+ if (settled)
228
+ return;
229
+ settled = true;
230
+ clearTimeout(hardKillTimer);
231
+ try {
232
+ child.kill("SIGTERM");
233
+ } catch {
234
+ }
235
+ reject(new Error("hermes: invocation aborted"));
236
+ };
237
+ if (signal) {
238
+ if (signal.aborted) {
239
+ onAbort();
240
+ return;
241
+ }
242
+ signal.addEventListener("abort", onAbort, { once: true });
243
+ }
244
+ let stdout = "";
245
+ let stderr = "";
246
+ child.stdout?.on("data", (chunk) => {
247
+ stdout += chunk.toString("utf-8");
248
+ });
249
+ child.stderr?.on("data", (chunk) => {
250
+ stderr += chunk.toString("utf-8");
251
+ });
252
+ child.on("error", (err) => {
253
+ if (settled)
254
+ return;
255
+ settled = true;
256
+ clearTimeout(hardKillTimer);
257
+ signal?.removeEventListener("abort", onAbort);
258
+ const isNotFound = err.code === "ENOENT";
259
+ reject(new Error(isNotFound ? `hermes: binary not found at "${settings.binaryPath}". Install hermes or set binaryPath/HERMES_BIN.` : `hermes: spawn error \u2014 ${err.message}`));
260
+ });
261
+ child.on("close", (code) => {
262
+ if (settled)
263
+ return;
264
+ settled = true;
265
+ clearTimeout(hardKillTimer);
266
+ signal?.removeEventListener("abort", onAbort);
267
+ if (code !== 0) {
268
+ const combined = [stdout, stderr].filter(Boolean).join("\n");
269
+ reject(new Error(`hermes: process exited with code ${String(code)}.
270
+ ${combined}`));
271
+ return;
272
+ }
273
+ try {
274
+ resolve2(parseHermesOutput(stdout, stderr));
275
+ } catch (parseErr) {
276
+ reject(parseErr);
277
+ }
278
+ });
279
+ });
280
+ }
281
+
282
+ // ../../plugins/fusion-plugin-hermes-runtime/dist/fusion-skill-install.js
283
+ import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, rmSync, symlinkSync, unlinkSync } from "node:fs";
284
+ import { homedir } from "node:os";
285
+ import { basename, dirname, join, resolve } from "node:path";
286
+ import { fileURLToPath } from "node:url";
287
+ var FUSION_SKILL_NAME = "fusion";
288
+ function resolveHermesHome(profile) {
289
+ const base = process.env.HERMES_HOME ?? join(homedir(), ".hermes");
290
+ if (!profile || profile === "default")
291
+ return base;
292
+ return join(base, "profiles", profile);
293
+ }
294
+ function getFusionSkillSourceCandidates(moduleUrl = import.meta.url) {
295
+ const here = fileURLToPath(moduleUrl);
296
+ const moduleDir = dirname(here);
297
+ return [
298
+ resolve(moduleDir, "..", "..", "..", "..", "packages", "cli", "skill", FUSION_SKILL_NAME),
299
+ resolve(moduleDir, "..", "..", "..", "skill", FUSION_SKILL_NAME),
300
+ resolve(moduleDir, "..", "..", "skill", FUSION_SKILL_NAME),
301
+ resolve(moduleDir, "..", "..", "..", "..", "skill", FUSION_SKILL_NAME)
302
+ ];
303
+ }
304
+ function resolveBundledFusionSkillSource() {
305
+ const candidates = getFusionSkillSourceCandidates();
306
+ for (const candidate of candidates) {
307
+ if (existsSync(join(candidate, "SKILL.md")))
308
+ return candidate;
309
+ }
310
+ return null;
311
+ }
312
+ function installFusionSkillIntoHermesHome(options = {}) {
313
+ const sourceDir = options.sourceDir ?? resolveBundledFusionSkillSource();
314
+ const targetDir = join(resolveHermesHome(options.profile), "skills", FUSION_SKILL_NAME);
315
+ if (!sourceDir) {
316
+ return {
317
+ outcome: "warning",
318
+ sourceDir,
319
+ targetDir,
320
+ reason: "bundled Fusion skill source directory not found"
321
+ };
322
+ }
323
+ try {
324
+ mkdirSync(dirname(targetDir), { recursive: true });
325
+ let replaced = false;
326
+ if (existsSync(targetDir) || isBrokenSymlink(targetDir)) {
327
+ const stat = lstatSync(targetDir);
328
+ if (stat.isSymbolicLink()) {
329
+ const currentTarget = safeReadlink(targetDir);
330
+ if (currentTarget && resolve(dirname(targetDir), currentTarget) === resolve(sourceDir)) {
331
+ return { outcome: "already-installed", sourceDir, targetDir };
332
+ }
333
+ if (!looksLikeFusionSkillTarget(resolve(dirname(targetDir), currentTarget ?? ""))) {
334
+ return {
335
+ outcome: "skipped",
336
+ sourceDir,
337
+ targetDir,
338
+ reason: "existing symlink does not look like a Fusion skill install"
339
+ };
340
+ }
341
+ unlinkSync(targetDir);
342
+ replaced = true;
343
+ } else {
344
+ if (!looksLikePriorFusionInstall(targetDir)) {
345
+ return {
346
+ outcome: "skipped",
347
+ sourceDir,
348
+ targetDir,
349
+ reason: "existing directory does not look like a Fusion skill install"
350
+ };
351
+ }
352
+ rmSync(targetDir, { recursive: true, force: true });
353
+ replaced = true;
354
+ }
355
+ }
356
+ try {
357
+ symlinkSync(sourceDir, targetDir, "dir");
358
+ } catch (error) {
359
+ const symlinkReason = error instanceof Error ? error.message : String(error);
360
+ try {
361
+ cpSync(sourceDir, targetDir, { recursive: true });
362
+ return {
363
+ outcome: replaced ? "replaced" : "installed",
364
+ sourceDir,
365
+ targetDir,
366
+ reason: `symlink failed (${symlinkReason}); copied files instead`
367
+ };
368
+ } catch (copyError) {
369
+ return {
370
+ outcome: "warning",
371
+ sourceDir,
372
+ targetDir,
373
+ reason: copyError instanceof Error ? copyError.message : String(copyError)
374
+ };
375
+ }
376
+ }
377
+ return { outcome: replaced ? "replaced" : "installed", sourceDir, targetDir };
378
+ } catch (error) {
379
+ return {
380
+ outcome: "warning",
381
+ sourceDir,
382
+ targetDir,
383
+ reason: error instanceof Error ? error.message : String(error)
384
+ };
385
+ }
386
+ }
387
+ function safeReadlink(path2) {
388
+ try {
389
+ return readlinkSync(path2);
390
+ } catch {
391
+ return null;
392
+ }
393
+ }
394
+ function isBrokenSymlink(path2) {
395
+ try {
396
+ const stat = lstatSync(path2);
397
+ return stat.isSymbolicLink() && !existsSync(path2);
398
+ } catch {
399
+ return false;
400
+ }
401
+ }
402
+ function looksLikePriorFusionInstall(path2) {
403
+ const skillMd = join(path2, "SKILL.md");
404
+ if (!existsSync(skillMd))
405
+ return false;
406
+ try {
407
+ const body = readFileSync(skillMd, "utf-8");
408
+ return /\bfusion\b/i.test(body) && /\bskill\b/i.test(body);
409
+ } catch {
410
+ return false;
411
+ }
412
+ }
413
+ function looksLikeFusionSkillTarget(path2) {
414
+ if (!path2)
415
+ return false;
416
+ if (basename(path2).toLowerCase() === FUSION_SKILL_NAME)
417
+ return true;
418
+ return existsSync(join(path2, "SKILL.md"));
419
+ }
420
+
421
+ // ../../plugins/fusion-plugin-hermes-runtime/dist/runtime-adapter.js
422
+ function buildRuntimeContextSection(options) {
423
+ const skillNames = Array.isArray(options.skills) ? options.skills.filter((value) => typeof value === "string" && value.trim().length > 0) : [];
424
+ const skillSelection = options.skillSelection;
425
+ const selectionSkillNames = Array.isArray(skillSelection?.requestedSkillNames) ? skillSelection.requestedSkillNames.filter((value) => typeof value === "string" && value.trim().length > 0) : [];
426
+ const mergedSkills = skillNames.length > 0 ? skillNames : selectionSkillNames;
427
+ const lines = [
428
+ "Fusion runtime context:",
429
+ `- Tool mode: ${options.tools ?? "coding"}`
430
+ ];
431
+ if (mergedSkills.length > 0) {
432
+ lines.push(`- Requested skills: ${mergedSkills.join(", ")}`);
433
+ }
434
+ lines.push("- If fn_* tools are available in your runtime, use them directly for coordination/memory/task actions.");
435
+ return lines.join("\n");
436
+ }
437
+ var HermesRuntimeAdapter = class {
438
+ id = "hermes";
439
+ name = "Hermes Runtime";
440
+ settings;
441
+ constructor(settings) {
442
+ this.settings = resolveCliSettings(settings);
443
+ }
444
+ async createSession(options) {
445
+ const session = {
446
+ model: void 0,
447
+ systemPrompt: options.systemPrompt,
448
+ messages: [],
449
+ apiKey: void 0,
450
+ thinkingLevel: void 0,
451
+ sessionId: "",
452
+ lastModelDescription: this.describeFromSettings(),
453
+ callbacks: {
454
+ onText: options.onText,
455
+ onThinking: options.onThinking,
456
+ onToolStart: options.onToolStart,
457
+ onToolEnd: options.onToolEnd
458
+ },
459
+ runtimeContext: options.runtimeContext,
460
+ fusedSystemPrompt: [options.systemPrompt.trim(), buildRuntimeContextSection(options).trim()].filter((part) => part.length > 0).join("\n\n"),
461
+ dispose: () => void 0
462
+ };
463
+ return { session, sessionFile: void 0 };
464
+ }
465
+ async promptWithFallback(session, prompt, _options) {
466
+ const resumeId = session.sessionId || void 0;
467
+ const promptWithContext = resumeId ? prompt : `${session.fusedSystemPrompt}
468
+
469
+ User request:
470
+ ${prompt}`;
471
+ const result = await invokeHermesCli(promptWithContext, this.settings, resumeId);
472
+ session.sessionId = result.sessionId;
473
+ session.lastModelDescription = this.describeFromSettings();
474
+ if (result.body) {
475
+ session.callbacks.onText?.(result.body);
476
+ }
477
+ }
478
+ describeModel(session) {
479
+ return session.lastModelDescription || this.describeFromSettings();
480
+ }
481
+ async dispose(_session) {
482
+ }
483
+ describeFromSettings() {
484
+ const provider = this.settings.provider;
485
+ const model = this.settings.model;
486
+ if (provider && model)
487
+ return `hermes/${provider}/${model}`;
488
+ if (model)
489
+ return `hermes/${model}`;
490
+ if (provider)
491
+ return `hermes/${provider}`;
492
+ return "hermes";
493
+ }
494
+ };
495
+
496
+ // ../../plugins/fusion-plugin-hermes-runtime/dist/probe.js
497
+ import { spawn as spawn2 } from "node:child_process";
498
+ var DEFAULT_PROBE_TIMEOUT_MS = 2e3;
499
+ async function probeHermesBinary(opts) {
500
+ const startedAt = Date.now();
501
+ const binary = typeof opts?.binaryPath === "string" && opts.binaryPath.trim().length > 0 ? opts.binaryPath.trim() : "hermes";
502
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
503
+ const resolvedPath = await tryResolveBinaryPath(binary);
504
+ return new Promise((resolvePromise) => {
505
+ const finish = (result) => {
506
+ resolvePromise({ ...result, probeDurationMs: Date.now() - startedAt });
507
+ };
508
+ let settled = false;
509
+ const child = spawn2(resolvedPath ?? binary, ["--version"], {
510
+ stdio: ["ignore", "pipe", "pipe"]
511
+ });
512
+ const timer = setTimeout(() => {
513
+ if (settled)
514
+ return;
515
+ settled = true;
516
+ try {
517
+ child.kill("SIGKILL");
518
+ } catch {
519
+ }
520
+ finish({
521
+ available: false,
522
+ binaryPath: resolvedPath,
523
+ reason: `Probe timed out after ${timeoutMs}ms`
524
+ });
525
+ }, timeoutMs);
526
+ let stdout = "";
527
+ let stderr = "";
528
+ child.stdout?.on("data", (chunk) => {
529
+ stdout += chunk.toString("utf-8");
530
+ });
531
+ child.stderr?.on("data", (chunk) => {
532
+ stderr += chunk.toString("utf-8");
533
+ });
534
+ child.on("error", (err) => {
535
+ if (settled)
536
+ return;
537
+ settled = true;
538
+ clearTimeout(timer);
539
+ const isNotFound = err.code === "ENOENT";
540
+ finish({
541
+ available: false,
542
+ binaryPath: resolvedPath,
543
+ reason: isNotFound ? `\`${binary}\` not found on PATH` : err.message
544
+ });
545
+ });
546
+ child.on("close", (code) => {
547
+ if (settled)
548
+ return;
549
+ settled = true;
550
+ clearTimeout(timer);
551
+ if (code === 0) {
552
+ finish({
553
+ available: true,
554
+ version: stdout.trim() || void 0,
555
+ binaryPath: resolvedPath
556
+ });
557
+ } else {
558
+ finish({
559
+ available: false,
560
+ binaryPath: resolvedPath,
561
+ reason: stderr.trim() || `hermes --version exited with code ${String(code)}`
562
+ });
563
+ }
564
+ });
565
+ });
566
+ }
567
+ async function tryResolveBinaryPath(binary) {
568
+ return new Promise((resolvePromise) => {
569
+ const which = process.platform === "win32" ? "where" : "which";
570
+ const child = spawn2(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
571
+ let out = "";
572
+ child.stdout?.on("data", (chunk) => {
573
+ out += chunk.toString("utf-8");
574
+ });
575
+ child.on("error", () => resolvePromise(void 0));
576
+ child.on("close", (code) => {
577
+ if (code === 0) {
578
+ const first = out.trim().split(/\r?\n/)[0];
579
+ resolvePromise(first?.length ? first : void 0);
580
+ } else {
581
+ resolvePromise(void 0);
582
+ }
583
+ });
584
+ });
585
+ }
586
+
587
+ // ../../plugins/fusion-plugin-hermes-runtime/dist/index.js
588
+ var HERMES_RUNTIME_ID = "hermes";
589
+ var HERMES_RUNTIME_VERSION = "0.2.0";
590
+ var hermesRuntimeMetadata = {
591
+ runtimeId: HERMES_RUNTIME_ID,
592
+ name: "Hermes Runtime",
593
+ description: "Drives the local `hermes` CLI (NousResearch/hermes-agent)",
594
+ version: HERMES_RUNTIME_VERSION
595
+ };
596
+ var hermesRuntimeFactory = async (ctx) => {
597
+ return new HermesRuntimeAdapter(ctx.settings);
598
+ };
599
+ var plugin = definePlugin({
600
+ manifest: {
601
+ id: "fusion-plugin-hermes-runtime",
602
+ name: "Hermes Runtime Plugin",
603
+ version: HERMES_RUNTIME_VERSION,
604
+ description: "Drives the local `hermes` CLI for Fusion agents \u2014 captures session ids and resumes via --resume.",
605
+ author: "Fusion Team",
606
+ homepage: "https://github.com/NousResearch/hermes-agent",
607
+ runtime: hermesRuntimeMetadata
608
+ },
609
+ state: "installed",
610
+ hooks: {
611
+ onLoad: (ctx) => {
612
+ const settings = resolveCliSettings(ctx.settings);
613
+ const skillInstall = installFusionSkillIntoHermesHome({ profile: settings.profile });
614
+ if (skillInstall.outcome === "warning") {
615
+ ctx.logger.warn(`Hermes Runtime Plugin: Fusion skill auto-install warning: ${skillInstall.reason ?? "unknown"}`);
616
+ } else if (skillInstall.outcome === "skipped") {
617
+ ctx.logger.warn(`Hermes Runtime Plugin: Fusion skill auto-install skipped: ${skillInstall.reason ?? "unknown"}`);
618
+ }
619
+ ctx.logger.info(`Hermes Runtime Plugin loaded \u2014 binary=${settings.binaryPath} model=${settings.model ?? "(default)"} fusionSkill=${skillInstall.outcome}`);
620
+ ctx.emitEvent("hermes-runtime:loaded", {
621
+ runtimeId: HERMES_RUNTIME_ID,
622
+ version: HERMES_RUNTIME_VERSION
623
+ });
624
+ },
625
+ onUnload: () => {
626
+ }
627
+ },
628
+ runtime: {
629
+ metadata: hermesRuntimeMetadata,
630
+ factory: hermesRuntimeFactory
631
+ }
632
+ });
633
+ var index_default = plugin;
634
+ export {
635
+ HERMES_RUNTIME_ID,
636
+ HermesRuntimeAdapter,
637
+ buildHermesArgs,
638
+ index_default as default,
639
+ hermesRuntimeFactory,
640
+ hermesRuntimeMetadata,
641
+ installFusionSkillIntoHermesHome,
642
+ invokeHermesCli,
643
+ listHermesProfiles,
644
+ parseHermesOutput,
645
+ probeHermesBinary,
646
+ resolveBundledFusionSkillSource,
647
+ resolveCliSettings,
648
+ resolveHermesHome
649
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "fusion-plugin-hermes-runtime",
3
+ "name": "Hermes Runtime Plugin",
4
+ "version": "0.1.0",
5
+ "description": "Hermes AI runtime plugin for Fusion - provides AI agent execution runtime capabilities",
6
+ "author": "Fusion Team",
7
+ "homepage": "https://github.com/gsxdsm/fusion",
8
+ "runtime": {
9
+ "runtimeId": "hermes",
10
+ "name": "Hermes Runtime",
11
+ "description": "Hermes raw-model runtime using pi-ai direct streaming",
12
+ "version": "0.1.0"
13
+ }
14
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@fusion-plugin-examples/hermes-runtime",
3
+ "version": "0.2.31",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./bundled.js"
8
+ }
9
+ },
10
+ "private": true
11
+ }