fix(plugins): re-export probe symbols + declare plugin deps in dashboard

- Hermes / OpenClaw plugin index.ts now re-export `probeHermesBinary` /
  `probeOpenClawBinary` and their status types so the dashboard's
  `runtime-provider-probes.ts` façade can import them via the public
  package entry instead of deep paths.
- Dashboard `package.json` adds `@fusion-plugin-examples/hermes-runtime`,
  `…/openclaw-runtime`, `…/paperclip-runtime` as workspace deps so
  pnpm symlinks them into `packages/dashboard/node_modules/`. Without
  these, the new probe imports failed with "Cannot find module" during
  `pnpm typecheck`.

This clears 6 of the 9 outstanding typecheck errors. The remaining 3 are
in the in-flight Hermes plugin rewrite (runtime-adapter still imports
from a deleted `./pi-module.js`; the new `index.ts` calls a factory
with the wrong arg type) and should be resolved by the same change set
that landed the rewrite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Fusion
2026-04-27 20:22:18 -07:00
committed by gsxdsm
parent 1a1bec1fb4
commit d3e1c28cef
82 changed files with 8481 additions and 2833 deletions

View File

@@ -1,123 +1,111 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
const { mockResolveGatewayConfig, mockCreateGatewaySession, mockPromptGateway, mockDescribeGatewayModel, mockProbeGateway, } = vi.hoisted(() => ({
mockResolveGatewayConfig: vi.fn().mockReturnValue({
gatewayUrl: "http://127.0.0.1:18789",
gatewayToken: undefined,
const { mockResolveCliConfig, mockProbeBinary } = vi.hoisted(() => ({
mockResolveCliConfig: vi.fn().mockReturnValue({
binaryPath: "openclaw",
agentId: "main",
model: undefined,
thinking: "off",
cliTimeoutSec: 0,
cliTimeoutMs: 300_000,
useGateway: false,
}),
mockProbeBinary: vi.fn().mockResolvedValue({
available: true,
binaryPath: "/opt/homebrew/bin/openclaw",
version: "OpenClaw 2026.4.26",
probeDurationMs: 12,
}),
mockCreateGatewaySession: vi.fn(),
mockPromptGateway: vi.fn(),
mockDescribeGatewayModel: vi.fn().mockReturnValue("openclaw/main"),
mockProbeGateway: vi.fn().mockResolvedValue(true),
}));
vi.mock("../pi-module.js", () => ({
resolveGatewayConfig: mockResolveGatewayConfig,
createGatewaySession: mockCreateGatewaySession,
promptGateway: mockPromptGateway,
describeGatewayModel: mockDescribeGatewayModel,
probeGateway: mockProbeGateway,
}));
import plugin, { openclawRuntimeMetadata, openclawRuntimeFactory, OPENCLAW_RUNTIME_ID } from "../index.js";
vi.mock("../pi-module.js", async () => {
const actual = await vi.importActual("../pi-module.js");
return {
...actual,
resolveCliConfig: mockResolveCliConfig,
};
});
vi.mock("../probe.js", async () => {
const actual = await vi.importActual("../probe.js");
return {
...actual,
probeOpenClawBinary: mockProbeBinary,
};
});
import plugin, { openclawRuntimeMetadata, openclawRuntimeFactory, OPENCLAW_RUNTIME_ID, } from "../index.js";
import { OpenClawRuntimeAdapter } from "../runtime-adapter.js";
function createMockContext(overrides = {}) {
function createMockContext(settings = {}) {
const logger = {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
};
return {
pluginId: "fusion-plugin-openclaw-runtime",
settings: {},
logger: {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
settings,
logger,
emitEvent: vi.fn(),
taskStore: {
getTask: vi.fn(),
},
...overrides,
taskStore: { getTask: vi.fn() },
};
}
describe("openclaw-runtime plugin", () => {
beforeEach(() => {
vi.clearAllMocks();
mockProbeGateway.mockResolvedValue(true);
mockResolveCliConfig.mockReturnValue({
binaryPath: "openclaw",
agentId: "main",
model: undefined,
thinking: "off",
cliTimeoutSec: 0,
cliTimeoutMs: 300_000,
useGateway: false,
});
mockProbeBinary.mockResolvedValue({
available: true,
binaryPath: "/opt/homebrew/bin/openclaw",
version: "OpenClaw 2026.4.26",
probeDurationMs: 12,
});
});
afterEach(() => {
vi.restoreAllMocks();
});
describe("plugin manifest identity", () => {
it("should have correct manifest fields", () => {
expect(plugin.manifest.id).toBe("fusion-plugin-openclaw-runtime");
expect(plugin.manifest.name).toBe("OpenClaw Runtime Plugin");
expect(plugin.manifest.version).toBe("0.1.0");
expect(plugin.manifest.description).toContain("OpenClaw");
expect(plugin.manifest.author).toBe("Fusion Team");
expect(plugin.state).toBe("installed");
});
it("manifest identity is stable", () => {
expect(plugin.manifest.id).toBe("fusion-plugin-openclaw-runtime");
expect(plugin.manifest.name).toBe("OpenClaw Runtime Plugin");
expect(plugin.state).toBe("installed");
expect(plugin.runtime?.metadata.runtimeId).toBe(OPENCLAW_RUNTIME_ID);
expect(plugin.manifest.runtime).toEqual(openclawRuntimeMetadata);
});
describe("runtime registration", () => {
it("should register openclaw runtime metadata", () => {
expect(plugin.runtime).toBeDefined();
expect(plugin.runtime?.metadata.runtimeId).toBe(OPENCLAW_RUNTIME_ID);
expect(plugin.runtime?.metadata.name).toBe("OpenClaw Runtime");
expect(plugin.runtime?.metadata.description).toContain("OpenClaw-backed AI session");
expect(plugin.runtime?.metadata.version).toBe("0.1.0");
});
it("should have consistent runtime metadata between export and manifest", () => {
expect(plugin.manifest.runtime).toEqual(openclawRuntimeMetadata);
expect(plugin.runtime?.metadata).toEqual(openclawRuntimeMetadata);
});
it("onLoad probes binary and logs binary path + version", async () => {
const ctx = createMockContext({});
await plugin.hooks.onLoad(ctx);
expect(mockProbeBinary).toHaveBeenCalledWith({ binaryPath: "openclaw" });
expect(ctx.logger.info).toHaveBeenCalledWith(expect.stringContaining("openclaw"));
expect(ctx.emitEvent).toHaveBeenCalledWith("openclaw-runtime:loaded", expect.objectContaining({
runtimeId: OPENCLAW_RUNTIME_ID,
binaryAvailable: true,
}));
});
describe("hooks", () => {
it("onLoad should probe gateway, log startup message, and emit loaded event", async () => {
const ctx = createMockContext();
mockResolveGatewayConfig.mockReturnValue({
gatewayUrl: "http://localhost:18789",
gatewayToken: "secret-token",
agentId: "main",
});
await plugin.hooks.onLoad?.(ctx);
expect(mockProbeGateway).toHaveBeenCalledWith("http://localhost:18789");
expect(ctx.logger.info).toHaveBeenCalledWith("OpenClaw Runtime Plugin loaded (gateway: http://localhost:18789, reachable: yes)");
expect(ctx.logger.info.mock.calls.join(" ")).not.toContain("secret-token");
expect(ctx.emitEvent).toHaveBeenCalledWith("openclaw-runtime:loaded", {
runtimeId: OPENCLAW_RUNTIME_ID,
version: "0.1.0",
gatewayUrl: "http://localhost:18789",
gatewayReachable: true,
});
});
it("onUnload should not throw", () => {
expect(() => plugin.hooks.onUnload?.()).not.toThrow();
it("onLoad logs warning when binary missing", async () => {
mockProbeBinary.mockResolvedValueOnce({
available: false,
probeDurationMs: 5,
reason: "`openclaw` not found on PATH",
});
const ctx = createMockContext({});
await plugin.hooks.onLoad(ctx);
expect(ctx.logger.info).toHaveBeenCalledWith(expect.stringContaining("not detected"));
});
describe("runtime factory behavior", () => {
it("should export runtime constants", () => {
expect(OPENCLAW_RUNTIME_ID).toBe("openclaw");
expect(openclawRuntimeMetadata.runtimeId).toBe("openclaw");
expect(typeof openclawRuntimeFactory).toBe("function");
});
it("runtime factory should return executable runtime adapter", async () => {
const runtime = (await openclawRuntimeFactory(createMockContext({
settings: {
gatewayUrl: "http://settings-gateway:18789",
gatewayToken: "plugin-token",
agentId: "ops",
},
})));
expect(mockResolveGatewayConfig).toHaveBeenCalledWith({
gatewayUrl: "http://settings-gateway:18789",
gatewayToken: "plugin-token",
agentId: "ops",
});
expect(runtime).toBeInstanceOf(OpenClawRuntimeAdapter);
expect(runtime.id).toBe("openclaw");
expect(runtime.name).toBe("OpenClaw Runtime");
expect(runtime).not.toHaveProperty("status");
expect(runtime).not.toHaveProperty("execute");
});
it("factory creation should not throw", async () => {
await expect(openclawRuntimeFactory(createMockContext())).resolves.toBeInstanceOf(OpenClawRuntimeAdapter);
});
it("factory returns an OpenClawRuntimeAdapter instance", async () => {
const runtime = (await openclawRuntimeFactory(createMockContext({ binaryPath: "/usr/bin/openclaw", agentId: "ops" })));
expect(runtime).toBeInstanceOf(OpenClawRuntimeAdapter);
expect(runtime.id).toBe("openclaw");
});
it("factory creation does not throw with empty settings", async () => {
await expect(openclawRuntimeFactory(createMockContext())).resolves.toBeInstanceOf(OpenClawRuntimeAdapter);
});
it("onUnload does not throw", () => {
expect(() => plugin.hooks.onUnload?.()).not.toThrow();
});
});
//# sourceMappingURL=index.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
/**
* OpenClaw Runtime Plugin
*
* Provides an executable OpenClaw runtime adapter for Fusion's plugin runtime
* discovery and session execution pipeline.
* Drives the local `openclaw` CLI as a subprocess (via
* `openclaw --no-color agent --local --json`). No daemon required.
*/
import type { FusionPlugin, PluginRuntimeFactory, PluginRuntimeManifestMetadata } from "@fusion/plugin-sdk";
declare const OPENCLAW_RUNTIME_ID = "openclaw";
@@ -11,4 +11,9 @@ declare const openclawRuntimeFactory: PluginRuntimeFactory;
declare const plugin: FusionPlugin;
export default plugin;
export { openclawRuntimeMetadata, openclawRuntimeFactory, OPENCLAW_RUNTIME_ID };
export { OpenClawRuntimeAdapter } from "./runtime-adapter.js";
export { resolveCliConfig, buildOpenClawArgs, createCliSession, promptCli, describeCliModel, extractStderrError, } from "./pi-module.js";
export type { CliConfig, GatewaySession, OpenClawAgentJson } from "./types.js";
export { probeOpenClawBinary } from "./probe.js";
export type { OpenClawBinaryStatus } from "./probe.js";
//# sourceMappingURL=index.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EACV,YAAY,EAEZ,oBAAoB,EACpB,6BAA6B,EAC9B,MAAM,oBAAoB,CAAC;AAE5B,QAAA,MAAM,mBAAmB,aAAa,CAAC;AAGvC,QAAA,MAAM,uBAAuB,EAAE,6BAK9B,CAAC;AAEF,QAAA,MAAM,sBAAsB,EAAE,oBAG7B,CAAC;AAEF,QAAA,MAAM,MAAM,EAAE,YAkCZ,CAAC;AAEH,eAAe,MAAM,CAAC;AAEtB,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,CAAC"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EACV,YAAY,EAEZ,oBAAoB,EACpB,6BAA6B,EAC9B,MAAM,oBAAoB,CAAC;AAE5B,QAAA,MAAM,mBAAmB,aAAa,CAAC;AAGvC,QAAA,MAAM,uBAAuB,EAAE,6BAK9B,CAAC;AAEF,QAAA,MAAM,sBAAsB,EAAE,oBAE7B,CAAC;AAEF,QAAA,MAAM,MAAM,EAAE,YAqCZ,CAAC;AAEH,eAAe,MAAM,CAAC;AAItB,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,EACT,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,YAAY,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC"}

View File

@@ -1,49 +1,51 @@
/**
* OpenClaw Runtime Plugin
*
* Provides an executable OpenClaw runtime adapter for Fusion's plugin runtime
* discovery and session execution pipeline.
* Drives the local `openclaw` CLI as a subprocess (via
* `openclaw --no-color agent --local --json`). No daemon required.
*/
import { definePlugin } from "@fusion/plugin-sdk";
import { OpenClawRuntimeAdapter } from "./runtime-adapter.js";
import { probeGateway, resolveGatewayConfig } from "./pi-module.js";
import { resolveCliConfig } from "./pi-module.js";
import { probeOpenClawBinary } from "./probe.js";
const OPENCLAW_RUNTIME_ID = "openclaw";
const OPENCLAW_RUNTIME_VERSION = "0.1.0";
const OPENCLAW_RUNTIME_VERSION = "0.2.0";
const openclawRuntimeMetadata = {
runtimeId: OPENCLAW_RUNTIME_ID,
name: "OpenClaw Runtime",
description: "OpenClaw-backed AI session using the local OpenClaw gateway",
description: "Drives the local `openclaw` CLI (openclaw/openclaw)",
version: OPENCLAW_RUNTIME_VERSION,
};
const openclawRuntimeFactory = async (ctx) => {
const config = resolveGatewayConfig(ctx?.settings);
return new OpenClawRuntimeAdapter(config);
return new OpenClawRuntimeAdapter(ctx?.settings);
};
const plugin = definePlugin({
manifest: {
id: "fusion-plugin-openclaw-runtime",
name: "OpenClaw Runtime Plugin",
version: "0.1.0",
description: "Provides OpenClaw runtime for Fusion AI agents",
version: OPENCLAW_RUNTIME_VERSION,
description: "Drives the local `openclaw` CLI for Fusion agents — embedded `--local` mode by default; gateway optional.",
author: "Fusion Team",
homepage: "https://github.com/gsxdsm/fusion",
homepage: "https://docs.openclaw.ai/",
runtime: openclawRuntimeMetadata,
},
state: "installed",
hooks: {
onLoad: async (ctx) => {
const config = resolveGatewayConfig(ctx.settings);
const gatewayReachable = await probeGateway(config.gatewayUrl);
ctx.logger.info(`OpenClaw Runtime Plugin loaded (gateway: ${config.gatewayUrl}, reachable: ${gatewayReachable ? "yes" : "no"})`);
const config = resolveCliConfig(ctx.settings);
const probe = await probeOpenClawBinary({ binaryPath: config.binaryPath });
ctx.logger.info(probe.available
? `OpenClaw Runtime Plugin loaded — binary=${config.binaryPath}${probe.version ? ` (${probe.version})` : ""}`
: `OpenClaw Runtime Plugin loaded but binary not detected: ${probe.reason ?? "unknown"}`);
ctx.emitEvent("openclaw-runtime:loaded", {
runtimeId: OPENCLAW_RUNTIME_ID,
version: OPENCLAW_RUNTIME_VERSION,
gatewayUrl: config.gatewayUrl,
gatewayReachable,
binaryAvailable: probe.available,
binaryPath: probe.binaryPath ?? config.binaryPath,
});
},
onUnload: () => {
// No context available during unload
// No persistent state to clean up — each prompt spawns a fresh subprocess.
},
},
runtime: {
@@ -52,5 +54,10 @@ const plugin = definePlugin({
},
});
export default plugin;
// ── Public exports ────────────────────────────────────────────────────────────
export { openclawRuntimeMetadata, openclawRuntimeFactory, OPENCLAW_RUNTIME_ID };
export { OpenClawRuntimeAdapter } from "./runtime-adapter.js";
export { resolveCliConfig, buildOpenClawArgs, createCliSession, promptCli, describeCliModel, extractStderrError, } from "./pi-module.js";
// Probe re-export for the dashboard's runtime-provider-probes façade.
export { probeOpenClawBinary } from "./probe.js";
//# sourceMappingURL=index.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAQpE,MAAM,mBAAmB,GAAG,UAAU,CAAC;AACvC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAEzC,MAAM,uBAAuB,GAAkC;IAC7D,SAAS,EAAE,mBAAmB;IAC9B,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,6DAA6D;IAC1E,OAAO,EAAE,wBAAwB;CAClC,CAAC;AAEF,MAAM,sBAAsB,GAAyB,KAAK,EAAE,GAAmB,EAAE,EAAE;IACjF,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACnD,OAAO,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC,CAAC;AAEF,MAAM,MAAM,GAAiB,YAAY,CAAC;IACxC,QAAQ,EAAE;QACR,EAAE,EAAE,gCAAgC;QACpC,IAAI,EAAE,yBAAyB;QAC/B,OAAO,EAAE,OAAO;QAChB,WAAW,EAAE,gDAAgD;QAC7D,MAAM,EAAE,aAAa;QACrB,QAAQ,EAAE,kCAAkC;QAC5C,OAAO,EAAE,uBAAuB;KACjC;IACD,KAAK,EAAE,WAAW;IAClB,KAAK,EAAE;QACL,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACpB,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAClD,MAAM,gBAAgB,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAE/D,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,4CAA4C,MAAM,CAAC,UAAU,gBAAgB,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAChH,CAAC;YACF,GAAG,CAAC,SAAS,CAAC,yBAAyB,EAAE;gBACvC,SAAS,EAAE,mBAAmB;gBAC9B,OAAO,EAAE,wBAAwB;gBACjC,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,gBAAgB;aACjB,CAAC,CAAC;QACL,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE;YACb,qCAAqC;QACvC,CAAC;KACF;IACD,OAAO,EAAE;QACP,QAAQ,EAAE,uBAAuB;QACjC,OAAO,EAAE,sBAAsB;KAChC;CACF,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC;AAEtB,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,CAAC"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAQjD,MAAM,mBAAmB,GAAG,UAAU,CAAC;AACvC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAEzC,MAAM,uBAAuB,GAAkC;IAC7D,SAAS,EAAE,mBAAmB;IAC9B,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,qDAAqD;IAClE,OAAO,EAAE,wBAAwB;CAClC,CAAC;AAEF,MAAM,sBAAsB,GAAyB,KAAK,EAAE,GAAmB,EAAE,EAAE;IACjF,OAAO,IAAI,sBAAsB,CAAC,GAAG,EAAE,QAA+C,CAAC,CAAC;AAC1F,CAAC,CAAC;AAEF,MAAM,MAAM,GAAiB,YAAY,CAAC;IACxC,QAAQ,EAAE;QACR,EAAE,EAAE,gCAAgC;QACpC,IAAI,EAAE,yBAAyB;QAC/B,OAAO,EAAE,wBAAwB;QACjC,WAAW,EACT,2GAA2G;QAC7G,MAAM,EAAE,aAAa;QACrB,QAAQ,EAAE,2BAA2B;QACrC,OAAO,EAAE,uBAAuB;KACjC;IACD,KAAK,EAAE,WAAW;IAClB,KAAK,EAAE;QACL,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACpB,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC9C,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;YAE3E,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,KAAK,CAAC,SAAS;gBACb,CAAC,CAAC,2CAA2C,MAAM,CAAC,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC7G,CAAC,CAAC,2DAA2D,KAAK,CAAC,MAAM,IAAI,SAAS,EAAE,CAC3F,CAAC;YACF,GAAG,CAAC,SAAS,CAAC,yBAAyB,EAAE;gBACvC,SAAS,EAAE,mBAAmB;gBAC9B,OAAO,EAAE,wBAAwB;gBACjC,eAAe,EAAE,KAAK,CAAC,SAAS;gBAChC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU;aAClD,CAAC,CAAC;QACL,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE;YACb,2EAA2E;QAC7E,CAAC;KACF;IACD,OAAO,EAAE;QACP,QAAQ,EAAE,uBAAuB;QACjC,OAAO,EAAE,sBAAsB;KAChC;CACF,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC;AAEtB,iFAAiF;AAEjF,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,EACT,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AAGxB,sEAAsE;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC"}