joonecli 0.1.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.
- package/AGENTS.md +56 -0
- package/Handover.md +115 -0
- package/LICENSE +201 -0
- package/PROGRESS.md +160 -0
- package/README.md +114 -0
- package/dist/__tests__/bootstrap.test.d.ts +1 -0
- package/dist/__tests__/bootstrap.test.js +76 -0
- package/dist/__tests__/bootstrap.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +1 -0
- package/dist/__tests__/config.test.js +84 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/m55.test.d.ts +1 -0
- package/dist/__tests__/m55.test.js +160 -0
- package/dist/__tests__/m55.test.js.map +1 -0
- package/dist/__tests__/middleware.test.d.ts +1 -0
- package/dist/__tests__/middleware.test.js +169 -0
- package/dist/__tests__/middleware.test.js.map +1 -0
- package/dist/__tests__/modelFactory.test.d.ts +1 -0
- package/dist/__tests__/modelFactory.test.js +50 -0
- package/dist/__tests__/modelFactory.test.js.map +1 -0
- package/dist/__tests__/optimizations.test.d.ts +1 -0
- package/dist/__tests__/optimizations.test.js +136 -0
- package/dist/__tests__/optimizations.test.js.map +1 -0
- package/dist/__tests__/promptBuilder.test.d.ts +1 -0
- package/dist/__tests__/promptBuilder.test.js +108 -0
- package/dist/__tests__/promptBuilder.test.js.map +1 -0
- package/dist/__tests__/sandbox.test.d.ts +1 -0
- package/dist/__tests__/sandbox.test.js +78 -0
- package/dist/__tests__/sandbox.test.js.map +1 -0
- package/dist/__tests__/security.test.d.ts +1 -0
- package/dist/__tests__/security.test.js +86 -0
- package/dist/__tests__/security.test.js.map +1 -0
- package/dist/__tests__/streaming.test.d.ts +1 -0
- package/dist/__tests__/streaming.test.js +71 -0
- package/dist/__tests__/streaming.test.js.map +1 -0
- package/dist/__tests__/toolRouter.test.d.ts +1 -0
- package/dist/__tests__/toolRouter.test.js +37 -0
- package/dist/__tests__/toolRouter.test.js.map +1 -0
- package/dist/__tests__/tools.test.d.ts +1 -0
- package/dist/__tests__/tools.test.js +112 -0
- package/dist/__tests__/tools.test.js.map +1 -0
- package/dist/__tests__/tracing.test.d.ts +1 -0
- package/dist/__tests__/tracing.test.js +147 -0
- package/dist/__tests__/tracing.test.js.map +1 -0
- package/dist/cli/config.d.ts +49 -0
- package/dist/cli/config.js +86 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +625 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/modelFactory.d.ts +9 -0
- package/dist/cli/modelFactory.js +154 -0
- package/dist/cli/modelFactory.js.map +1 -0
- package/dist/cli/providers.d.ts +18 -0
- package/dist/cli/providers.js +94 -0
- package/dist/cli/providers.js.map +1 -0
- package/dist/core/agentLoop.d.ts +43 -0
- package/dist/core/agentLoop.js +245 -0
- package/dist/core/agentLoop.js.map +1 -0
- package/dist/core/errors.d.ts +62 -0
- package/dist/core/errors.js +139 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/promptBuilder.d.ts +49 -0
- package/dist/core/promptBuilder.js +84 -0
- package/dist/core/promptBuilder.js.map +1 -0
- package/dist/core/reasoningRouter.d.ts +62 -0
- package/dist/core/reasoningRouter.js +102 -0
- package/dist/core/reasoningRouter.js.map +1 -0
- package/dist/core/retry.d.ts +25 -0
- package/dist/core/retry.js +49 -0
- package/dist/core/retry.js.map +1 -0
- package/dist/core/sessionResumer.d.ts +17 -0
- package/dist/core/sessionResumer.js +78 -0
- package/dist/core/sessionResumer.js.map +1 -0
- package/dist/core/sessionStore.d.ts +45 -0
- package/dist/core/sessionStore.js +167 -0
- package/dist/core/sessionStore.js.map +1 -0
- package/dist/core/tokenCounter.d.ts +17 -0
- package/dist/core/tokenCounter.js +54 -0
- package/dist/core/tokenCounter.js.map +1 -0
- package/dist/evals/dataset.d.ts +4 -0
- package/dist/evals/dataset.js +61 -0
- package/dist/evals/dataset.js.map +1 -0
- package/dist/evals/evaluator.d.ts +21 -0
- package/dist/evals/evaluator.js +68 -0
- package/dist/evals/evaluator.js.map +1 -0
- package/dist/hitl/bridge.d.ts +65 -0
- package/dist/hitl/bridge.js +120 -0
- package/dist/hitl/bridge.js.map +1 -0
- package/dist/middleware/commandSanitizer.d.ts +18 -0
- package/dist/middleware/commandSanitizer.js +50 -0
- package/dist/middleware/commandSanitizer.js.map +1 -0
- package/dist/middleware/loopDetection.d.ts +28 -0
- package/dist/middleware/loopDetection.js +49 -0
- package/dist/middleware/loopDetection.js.map +1 -0
- package/dist/middleware/permission.d.ts +17 -0
- package/dist/middleware/permission.js +59 -0
- package/dist/middleware/permission.js.map +1 -0
- package/dist/middleware/pipeline.d.ts +31 -0
- package/dist/middleware/pipeline.js +62 -0
- package/dist/middleware/pipeline.js.map +1 -0
- package/dist/middleware/preCompletion.d.ts +29 -0
- package/dist/middleware/preCompletion.js +82 -0
- package/dist/middleware/preCompletion.js.map +1 -0
- package/dist/middleware/types.d.ts +40 -0
- package/dist/middleware/types.js +8 -0
- package/dist/middleware/types.js.map +1 -0
- package/dist/sandbox/bootstrap.d.ts +38 -0
- package/dist/sandbox/bootstrap.js +107 -0
- package/dist/sandbox/bootstrap.js.map +1 -0
- package/dist/sandbox/manager.d.ts +72 -0
- package/dist/sandbox/manager.js +180 -0
- package/dist/sandbox/manager.js.map +1 -0
- package/dist/sandbox/sync.d.ts +55 -0
- package/dist/sandbox/sync.js +135 -0
- package/dist/sandbox/sync.js.map +1 -0
- package/dist/skills/loader.d.ts +55 -0
- package/dist/skills/loader.js +132 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/tools.d.ts +5 -0
- package/dist/skills/tools.js +78 -0
- package/dist/skills/tools.js.map +1 -0
- package/dist/skills/types.d.ts +13 -0
- package/dist/skills/types.js +2 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/test_cache.d.ts +1 -0
- package/dist/test_cache.js +55 -0
- package/dist/test_cache.js.map +1 -0
- package/dist/test_google.js +93 -0
- package/dist/tools/askUser.d.ts +10 -0
- package/dist/tools/askUser.js +42 -0
- package/dist/tools/askUser.js.map +1 -0
- package/dist/tools/browser.d.ts +19 -0
- package/dist/tools/browser.js +111 -0
- package/dist/tools/browser.js.map +1 -0
- package/dist/tools/index.d.ts +27 -0
- package/dist/tools/index.js +184 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +31 -0
- package/dist/tools/registry.js +168 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/router.d.ts +34 -0
- package/dist/tools/router.js +73 -0
- package/dist/tools/router.js.map +1 -0
- package/dist/tools/security.d.ts +28 -0
- package/dist/tools/security.js +183 -0
- package/dist/tools/security.js.map +1 -0
- package/dist/tools/webSearch.d.ts +6 -0
- package/dist/tools/webSearch.js +120 -0
- package/dist/tools/webSearch.js.map +1 -0
- package/dist/tracing/analyzer.d.ts +58 -0
- package/dist/tracing/analyzer.js +190 -0
- package/dist/tracing/analyzer.js.map +1 -0
- package/dist/tracing/langsmith.d.ts +38 -0
- package/dist/tracing/langsmith.js +50 -0
- package/dist/tracing/langsmith.js.map +1 -0
- package/dist/tracing/sessionTracer.d.ts +73 -0
- package/dist/tracing/sessionTracer.js +157 -0
- package/dist/tracing/sessionTracer.js.map +1 -0
- package/dist/tracing/types.d.ts +46 -0
- package/dist/tracing/types.js +5 -0
- package/dist/tracing/types.js.map +1 -0
- package/dist/ui/App.d.ts +24 -0
- package/dist/ui/App.js +172 -0
- package/dist/ui/App.js.map +1 -0
- package/dist/ui/components/HITLPrompt.d.ts +15 -0
- package/dist/ui/components/HITLPrompt.js +35 -0
- package/dist/ui/components/HITLPrompt.js.map +1 -0
- package/dist/ui/components/Header.d.ts +8 -0
- package/dist/ui/components/Header.js +6 -0
- package/dist/ui/components/Header.js.map +1 -0
- package/dist/ui/components/MessageBubble.d.ts +13 -0
- package/dist/ui/components/MessageBubble.js +17 -0
- package/dist/ui/components/MessageBubble.js.map +1 -0
- package/dist/ui/components/StatusBar.d.ts +21 -0
- package/dist/ui/components/StatusBar.js +34 -0
- package/dist/ui/components/StatusBar.js.map +1 -0
- package/dist/ui/components/StreamingText.d.ts +13 -0
- package/dist/ui/components/StreamingText.js +24 -0
- package/dist/ui/components/StreamingText.js.map +1 -0
- package/dist/ui/components/ToolCallPanel.d.ts +15 -0
- package/dist/ui/components/ToolCallPanel.js +18 -0
- package/dist/ui/components/ToolCallPanel.js.map +1 -0
- package/docs/01_insights_and_patterns.md +27 -0
- package/docs/02_edge_cases_and_mitigations.md +143 -0
- package/docs/03_initial_implementation_plan.md +66 -0
- package/docs/04_tech_stack_proposal.md +20 -0
- package/docs/05_prd.md +87 -0
- package/docs/06_user_stories.md +72 -0
- package/docs/07_system_architecture.md +138 -0
- package/docs/08_roadmap.md +200 -0
- package/e2b/Dockerfile +26 -0
- package/package.json +57 -0
- package/src/__tests__/bootstrap.test.ts +111 -0
- package/src/__tests__/config.test.ts +97 -0
- package/src/__tests__/m55.test.ts +238 -0
- package/src/__tests__/middleware.test.ts +219 -0
- package/src/__tests__/modelFactory.test.ts +63 -0
- package/src/__tests__/optimizations.test.ts +201 -0
- package/src/__tests__/promptBuilder.test.ts +141 -0
- package/src/__tests__/sandbox.test.ts +102 -0
- package/src/__tests__/security.test.ts +122 -0
- package/src/__tests__/streaming.test.ts +82 -0
- package/src/__tests__/toolRouter.test.ts +52 -0
- package/src/__tests__/tools.test.ts +146 -0
- package/src/__tests__/tracing.test.ts +196 -0
- package/src/agents/agentRegistry.ts +69 -0
- package/src/agents/agentSpec.ts +67 -0
- package/src/agents/builtinAgents.ts +142 -0
- package/src/cli/config.ts +124 -0
- package/src/cli/index.ts +730 -0
- package/src/cli/modelFactory.ts +174 -0
- package/src/cli/providers.ts +107 -0
- package/src/commands/builtinCommands.ts +293 -0
- package/src/commands/commandRegistry.ts +194 -0
- package/src/core/agentLoop.d.ts.map +1 -0
- package/src/core/agentLoop.ts +312 -0
- package/src/core/autoSave.ts +95 -0
- package/src/core/compactor.ts +252 -0
- package/src/core/contextGuard.ts +129 -0
- package/src/core/errors.ts +202 -0
- package/src/core/promptBuilder.d.ts.map +1 -0
- package/src/core/promptBuilder.ts +139 -0
- package/src/core/reasoningRouter.ts +121 -0
- package/src/core/retry.ts +75 -0
- package/src/core/sessionResumer.ts +90 -0
- package/src/core/sessionStore.ts +215 -0
- package/src/core/subAgent.ts +339 -0
- package/src/core/tokenCounter.ts +64 -0
- package/src/evals/dataset.ts +67 -0
- package/src/evals/evaluator.ts +81 -0
- package/src/hitl/bridge.ts +160 -0
- package/src/middleware/commandSanitizer.ts +60 -0
- package/src/middleware/loopDetection.ts +63 -0
- package/src/middleware/permission.ts +72 -0
- package/src/middleware/pipeline.ts +75 -0
- package/src/middleware/preCompletion.ts +94 -0
- package/src/middleware/types.ts +45 -0
- package/src/sandbox/bootstrap.ts +121 -0
- package/src/sandbox/manager.ts +239 -0
- package/src/sandbox/sync.ts +157 -0
- package/src/skills/loader.ts +143 -0
- package/src/skills/tools.ts +99 -0
- package/src/skills/types.ts +13 -0
- package/src/test_cache.ts +72 -0
- package/src/test_google.js +40 -0
- package/src/test_google.ts +40 -0
- package/src/tools/askUser.ts +47 -0
- package/src/tools/browser.ts +137 -0
- package/src/tools/index.d.ts.map +1 -0
- package/src/tools/index.ts +237 -0
- package/src/tools/registry.ts +198 -0
- package/src/tools/router.ts +78 -0
- package/src/tools/security.ts +220 -0
- package/src/tools/spawnAgent.ts +158 -0
- package/src/tools/webSearch.ts +142 -0
- package/src/tracing/analyzer.ts +265 -0
- package/src/tracing/langsmith.ts +63 -0
- package/src/tracing/sessionTracer.ts +202 -0
- package/src/tracing/types.ts +49 -0
- package/src/types/valyu.d.ts +37 -0
- package/src/ui/App.tsx +404 -0
- package/src/ui/components/HITLPrompt.tsx +119 -0
- package/src/ui/components/Header.tsx +51 -0
- package/src/ui/components/MessageBubble.tsx +46 -0
- package/src/ui/components/StatusBar.tsx +138 -0
- package/src/ui/components/StreamingText.tsx +48 -0
- package/src/ui/components/ToolCallPanel.tsx +80 -0
- package/tests/commands/commands.test.ts +356 -0
- package/tests/core/compactor.test.ts +217 -0
- package/tests/core/retryAndErrors.test.ts +164 -0
- package/tests/core/sessionResumer.test.ts +95 -0
- package/tests/core/sessionStore.test.ts +84 -0
- package/tests/core/stability.test.ts +165 -0
- package/tests/core/subAgent.test.ts +238 -0
- package/tests/hitl/hitlBridge.test.ts +115 -0
- package/tsconfig.json +16 -0
- package/vitest.config.ts +10 -0
- package/vitest.out +48 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { ToolRouter, ToolTarget } from "../tools/router.js";
|
|
3
|
+
describe("Tool Router", () => {
|
|
4
|
+
let router;
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
router = new ToolRouter();
|
|
7
|
+
});
|
|
8
|
+
// ─── Test #20: Routes write_file to host ───
|
|
9
|
+
it("routes write_file to the host", () => {
|
|
10
|
+
expect(router.getTarget("write_file")).toBe(ToolTarget.HOST);
|
|
11
|
+
});
|
|
12
|
+
// ─── Test #21: Routes read_file to host ───
|
|
13
|
+
it("routes read_file to the host", () => {
|
|
14
|
+
expect(router.getTarget("read_file")).toBe(ToolTarget.HOST);
|
|
15
|
+
});
|
|
16
|
+
// ─── Test #22: Routes bash to sandbox ───
|
|
17
|
+
it("routes bash to the sandbox", () => {
|
|
18
|
+
expect(router.getTarget("bash")).toBe(ToolTarget.SANDBOX);
|
|
19
|
+
});
|
|
20
|
+
// ─── Test #23: Routes run_tests to sandbox ───
|
|
21
|
+
it("routes run_tests to the sandbox", () => {
|
|
22
|
+
expect(router.getTarget("run_tests")).toBe(ToolTarget.SANDBOX);
|
|
23
|
+
});
|
|
24
|
+
// ─── Test #24: Routes install_deps to sandbox ───
|
|
25
|
+
it("routes install_deps to the sandbox", () => {
|
|
26
|
+
expect(router.getTarget("install_deps")).toBe(ToolTarget.SANDBOX);
|
|
27
|
+
});
|
|
28
|
+
// ─── Test #25: Routes search_tools to host ───
|
|
29
|
+
it("routes search_tools to the host", () => {
|
|
30
|
+
expect(router.getTarget("search_tools")).toBe(ToolTarget.HOST);
|
|
31
|
+
});
|
|
32
|
+
// ─── Test #26: Unknown tools default to sandbox (safe) ───
|
|
33
|
+
it("defaults unknown tools to sandbox for safety", () => {
|
|
34
|
+
expect(router.getTarget("unknown_tool")).toBe(ToolTarget.SANDBOX);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=toolRouter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolRouter.test.js","sourceRoot":"","sources":["../../src/__tests__/toolRouter.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAM,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE5D,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,MAAkB,CAAC;IAEvB,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,8CAA8C;IAE9C,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,6CAA6C;IAE7C,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,2CAA2C;IAE3C,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,gDAAgD;IAEhD,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,mDAAmD;IAEnD,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,gDAAgD;IAEhD,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,4DAA4D;IAE5D,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import * as os from "node:os";
|
|
5
|
+
import { ReadFileTool, WriteFileTool } from "../tools/index.js";
|
|
6
|
+
describe("ReadFileTool", () => {
|
|
7
|
+
let tmpDir;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
tmpDir = fs.mkdtempSync(path.join(process.cwd(), ".joone-tools-test-"));
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
13
|
+
});
|
|
14
|
+
// ─── Test #27: Reads a normal file ───
|
|
15
|
+
it("reads a small file and returns its content", async () => {
|
|
16
|
+
const filePath = path.join(tmpDir, "hello.txt");
|
|
17
|
+
fs.writeFileSync(filePath, "Hello, world!", "utf-8");
|
|
18
|
+
const result = await ReadFileTool.execute({ path: filePath });
|
|
19
|
+
expect(result.content).toBe("Hello, world!");
|
|
20
|
+
});
|
|
21
|
+
// ─── Test #28: Returns error for non-existent file ───
|
|
22
|
+
it("returns an error message for a non-existent file", async () => {
|
|
23
|
+
const result = await ReadFileTool.execute({
|
|
24
|
+
path: path.join(tmpDir, "nope.txt"),
|
|
25
|
+
});
|
|
26
|
+
expect(result.content).toMatch(/not found/i);
|
|
27
|
+
});
|
|
28
|
+
// ─── Test #29: File size guardrail rejects files over 512KB ───
|
|
29
|
+
it("rejects files larger than 512KB with a descriptive error", async () => {
|
|
30
|
+
const filePath = path.join(tmpDir, "big.txt");
|
|
31
|
+
// Create a 600KB file
|
|
32
|
+
const bigContent = "x".repeat(600 * 1024);
|
|
33
|
+
fs.writeFileSync(filePath, bigContent, "utf-8");
|
|
34
|
+
const result = await ReadFileTool.execute({ path: filePath });
|
|
35
|
+
expect(result.content).toMatch(/too large/i);
|
|
36
|
+
expect(result.content).toMatch(/512/);
|
|
37
|
+
});
|
|
38
|
+
// ─── Test #30: Line range slicing works ───
|
|
39
|
+
it("returns only the requested line range", async () => {
|
|
40
|
+
const filePath = path.join(tmpDir, "lines.txt");
|
|
41
|
+
const lines = Array.from({ length: 20 }, (_, i) => `Line ${i + 1}`);
|
|
42
|
+
fs.writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
43
|
+
const result = await ReadFileTool.execute({
|
|
44
|
+
path: filePath,
|
|
45
|
+
startLine: 5,
|
|
46
|
+
endLine: 7,
|
|
47
|
+
});
|
|
48
|
+
expect(result.content).toContain("5: Line 5");
|
|
49
|
+
expect(result.content).toContain("6: Line 6");
|
|
50
|
+
expect(result.content).toContain("7: Line 7");
|
|
51
|
+
expect(result.content).not.toContain("4: Line 4");
|
|
52
|
+
expect(result.content).not.toContain("8: Line 8");
|
|
53
|
+
});
|
|
54
|
+
// ─── Test #31: Line count guardrail truncates long files ───
|
|
55
|
+
it("truncates files with more than 2000 lines", async () => {
|
|
56
|
+
const filePath = path.join(tmpDir, "long.txt");
|
|
57
|
+
// Create a file with 2500 short lines (under 512KB)
|
|
58
|
+
const lines = Array.from({ length: 2500 }, (_, i) => `L${i + 1}`);
|
|
59
|
+
fs.writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
60
|
+
const result = await ReadFileTool.execute({ path: filePath });
|
|
61
|
+
expect(result.content).toMatch(/truncated at 2000 lines/i);
|
|
62
|
+
expect(result.content).toContain("1: L1");
|
|
63
|
+
expect(result.content).toContain("2000: L2000");
|
|
64
|
+
expect(result.content).not.toContain("2001: L2001");
|
|
65
|
+
});
|
|
66
|
+
// ─── Test #X: Security Guardrail Blocks Outside Files ───
|
|
67
|
+
it("blocks reading files outside the project workspace", async () => {
|
|
68
|
+
// Create a file in the OS tmp directory (guaranteed outside project workspace)
|
|
69
|
+
const outsideDir = fs.mkdtempSync(path.join(os.tmpdir(), "joone-outside-"));
|
|
70
|
+
const filePath = path.join(outsideDir, "secret.txt");
|
|
71
|
+
fs.writeFileSync(filePath, "secret token", "utf-8");
|
|
72
|
+
try {
|
|
73
|
+
const result = await ReadFileTool.execute({ path: filePath });
|
|
74
|
+
expect(result.isError).toBe(true);
|
|
75
|
+
expect(result.content).toMatch(/Security Error: Access Denied/i);
|
|
76
|
+
expect(result.content).toMatch(/outside the current project workspace/i);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
fs.rmSync(outsideDir, { recursive: true, force: true });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe("WriteFileTool", () => {
|
|
84
|
+
let tmpDir;
|
|
85
|
+
beforeEach(() => {
|
|
86
|
+
tmpDir = fs.mkdtempSync(path.join(process.cwd(), ".joone-write-test-"));
|
|
87
|
+
});
|
|
88
|
+
afterEach(() => {
|
|
89
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
90
|
+
});
|
|
91
|
+
// ─── Test #32: Writes a file to disk ───
|
|
92
|
+
it("writes content to a file and confirms", async () => {
|
|
93
|
+
const filePath = path.join(tmpDir, "output.ts");
|
|
94
|
+
const result = await WriteFileTool.execute({
|
|
95
|
+
path: filePath,
|
|
96
|
+
content: "const x = 42;",
|
|
97
|
+
});
|
|
98
|
+
expect(result.content).toMatch(/file written/i);
|
|
99
|
+
expect(fs.readFileSync(filePath, "utf-8")).toBe("const x = 42;");
|
|
100
|
+
});
|
|
101
|
+
// ─── Test #33: Creates parent directories if needed ───
|
|
102
|
+
it("creates parent directories if they do not exist", async () => {
|
|
103
|
+
const filePath = path.join(tmpDir, "nested", "deep", "file.ts");
|
|
104
|
+
const result = await WriteFileTool.execute({
|
|
105
|
+
path: filePath,
|
|
106
|
+
content: "export {}",
|
|
107
|
+
});
|
|
108
|
+
expect(result.content).toMatch(/file written/i);
|
|
109
|
+
expect(fs.existsSync(filePath)).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=tools.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.test.js","sourceRoot":"","sources":["../../src/__tests__/tools.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import { SessionTracer } from "../tracing/sessionTracer.js";
|
|
3
|
+
import { enableLangSmith, disableLangSmith, isLangSmithEnabled, } from "../tracing/langsmith.js";
|
|
4
|
+
import { TraceAnalyzer } from "../tracing/analyzer.js";
|
|
5
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
6
|
+
// 6a: SessionTracer
|
|
7
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
8
|
+
describe("SessionTracer", () => {
|
|
9
|
+
// ─── Test #83: Records LLM calls and computes totals ───
|
|
10
|
+
it("records LLM calls and computes token totals", () => {
|
|
11
|
+
const tracer = new SessionTracer("test-session-1");
|
|
12
|
+
tracer.recordLLMCall({ promptTokens: 500, completionTokens: 100, cached: false, duration: 800 });
|
|
13
|
+
tracer.recordLLMCall({ promptTokens: 400, completionTokens: 150, cached: true, duration: 600 });
|
|
14
|
+
const summary = tracer.getSummary();
|
|
15
|
+
expect(summary.promptTokens).toBe(900);
|
|
16
|
+
expect(summary.completionTokens).toBe(250);
|
|
17
|
+
expect(summary.totalTokens).toBe(1150);
|
|
18
|
+
expect(summary.turnCount).toBe(2);
|
|
19
|
+
});
|
|
20
|
+
// ─── Test #84: Records tool calls and counts them ───
|
|
21
|
+
it("records tool calls and counts them", () => {
|
|
22
|
+
const tracer = new SessionTracer("test-session-2");
|
|
23
|
+
tracer.recordToolCall({ name: "bash", args: { command: "ls" }, duration: 50, success: true });
|
|
24
|
+
tracer.recordToolCall({ name: "write_file", args: { path: "a.ts" }, duration: 30, success: true });
|
|
25
|
+
tracer.recordToolCall({ name: "bash", args: { command: "npm test" }, duration: 200, success: false });
|
|
26
|
+
const summary = tracer.getSummary();
|
|
27
|
+
expect(summary.toolCallCount).toBe(3);
|
|
28
|
+
});
|
|
29
|
+
// ─── Test #85: Computes cache hit rate correctly ───
|
|
30
|
+
it("computes cache hit rate correctly", () => {
|
|
31
|
+
const tracer = new SessionTracer("test-session-3");
|
|
32
|
+
// 3 calls: 2 cached, 1 not
|
|
33
|
+
tracer.recordLLMCall({ promptTokens: 100, completionTokens: 50, cached: true, duration: 100 });
|
|
34
|
+
tracer.recordLLMCall({ promptTokens: 100, completionTokens: 50, cached: true, duration: 100 });
|
|
35
|
+
tracer.recordLLMCall({ promptTokens: 100, completionTokens: 50, cached: false, duration: 100 });
|
|
36
|
+
const summary = tracer.getSummary();
|
|
37
|
+
// 200 cached out of 300 total prompt tokens = 66.7%
|
|
38
|
+
expect(summary.cacheHitRate).toBeCloseTo(0.667, 2);
|
|
39
|
+
});
|
|
40
|
+
// ─── Test #86: export() returns valid SessionTrace ───
|
|
41
|
+
it("export() returns a valid SessionTrace", () => {
|
|
42
|
+
const tracer = new SessionTracer("export-test");
|
|
43
|
+
tracer.recordLLMCall({ promptTokens: 100, completionTokens: 50, cached: true, duration: 200 });
|
|
44
|
+
tracer.recordError({ message: "Timeout", tool: "bash" });
|
|
45
|
+
const trace = tracer.export();
|
|
46
|
+
expect(trace.sessionId).toBe("export-test");
|
|
47
|
+
expect(trace.startedAt).toBeGreaterThan(0);
|
|
48
|
+
expect(trace.endedAt).toBeGreaterThanOrEqual(trace.startedAt);
|
|
49
|
+
expect(trace.events).toHaveLength(2);
|
|
50
|
+
expect(trace.summary.turnCount).toBe(1);
|
|
51
|
+
expect(trace.summary.errorCount).toBe(1);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
55
|
+
// 6b: LangSmith Integration
|
|
56
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
57
|
+
describe("LangSmith Integration", () => {
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
disableLangSmith();
|
|
60
|
+
});
|
|
61
|
+
// ─── Test #87: enableLangSmith sets correct env vars ───
|
|
62
|
+
it("sets the correct environment variables", () => {
|
|
63
|
+
enableLangSmith({ apiKey: "test-key-123", project: "my-project" });
|
|
64
|
+
expect(process.env.LANGCHAIN_TRACING_V2).toBe("true");
|
|
65
|
+
expect(process.env.LANGCHAIN_API_KEY).toBe("test-key-123");
|
|
66
|
+
expect(process.env.LANGCHAIN_PROJECT).toBe("my-project");
|
|
67
|
+
expect(isLangSmithEnabled()).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
// ─── Test #88: disableLangSmith clears env vars ───
|
|
70
|
+
it("disableLangSmith clears the environment variables", () => {
|
|
71
|
+
enableLangSmith({ apiKey: "test-key" });
|
|
72
|
+
disableLangSmith();
|
|
73
|
+
expect(process.env.LANGCHAIN_TRACING_V2).toBeUndefined();
|
|
74
|
+
expect(isLangSmithEnabled()).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
78
|
+
// 6c: TraceAnalyzer
|
|
79
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
80
|
+
describe("TraceAnalyzer", () => {
|
|
81
|
+
const createTrace = (overrides) => ({
|
|
82
|
+
sessionId: "test",
|
|
83
|
+
startedAt: Date.now() - 10000,
|
|
84
|
+
endedAt: Date.now(),
|
|
85
|
+
events: [],
|
|
86
|
+
summary: {
|
|
87
|
+
totalTokens: 1000,
|
|
88
|
+
promptTokens: 700,
|
|
89
|
+
completionTokens: 300,
|
|
90
|
+
totalCost: 0.006,
|
|
91
|
+
cacheHitRate: 0.8,
|
|
92
|
+
toolCallCount: 5,
|
|
93
|
+
errorCount: 0,
|
|
94
|
+
totalDuration: 10000,
|
|
95
|
+
turnCount: 5,
|
|
96
|
+
},
|
|
97
|
+
...overrides,
|
|
98
|
+
});
|
|
99
|
+
// ─── Test #89: Detects loop patterns ───
|
|
100
|
+
it("detects doom-loop patterns in tool calls", () => {
|
|
101
|
+
const trace = createTrace({
|
|
102
|
+
events: [
|
|
103
|
+
{ type: "tool_call", timestamp: 1, data: { name: "bash", args: { command: "ls" } } },
|
|
104
|
+
{ type: "tool_call", timestamp: 2, data: { name: "bash", args: { command: "ls" } } },
|
|
105
|
+
{ type: "tool_call", timestamp: 3, data: { name: "bash", args: { command: "ls" } } },
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
const analyzer = new TraceAnalyzer(trace);
|
|
109
|
+
const report = analyzer.analyze();
|
|
110
|
+
const loopIssues = report.issues.filter((i) => i.category === "loop");
|
|
111
|
+
expect(loopIssues.length).toBeGreaterThan(0);
|
|
112
|
+
expect(loopIssues[0].severity).toBe("critical");
|
|
113
|
+
});
|
|
114
|
+
// ─── Test #90: Detects cost hotspots ───
|
|
115
|
+
it("flags turns consuming >20% of total tokens", () => {
|
|
116
|
+
const trace = createTrace({
|
|
117
|
+
summary: {
|
|
118
|
+
...createTrace().summary,
|
|
119
|
+
totalTokens: 1000,
|
|
120
|
+
},
|
|
121
|
+
events: [
|
|
122
|
+
{ type: "llm_call", timestamp: 1, data: { promptTokens: 300, completionTokens: 100, cached: false } },
|
|
123
|
+
{ type: "llm_call", timestamp: 2, data: { promptTokens: 100, completionTokens: 50, cached: true } },
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
const analyzer = new TraceAnalyzer(trace);
|
|
127
|
+
const report = analyzer.analyze();
|
|
128
|
+
const costIssues = report.issues.filter((i) => i.category === "cost");
|
|
129
|
+
expect(costIssues.length).toBeGreaterThan(0);
|
|
130
|
+
});
|
|
131
|
+
// ─── Test #91: Warns on low cache hit rate ───
|
|
132
|
+
it("warns when cache hit rate is below 70%", () => {
|
|
133
|
+
const trace = createTrace({
|
|
134
|
+
summary: {
|
|
135
|
+
...createTrace().summary,
|
|
136
|
+
cacheHitRate: 0.5,
|
|
137
|
+
turnCount: 5,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
const analyzer = new TraceAnalyzer(trace);
|
|
141
|
+
const report = analyzer.analyze();
|
|
142
|
+
const cacheIssues = report.issues.filter((i) => i.category === "cache");
|
|
143
|
+
expect(cacheIssues.length).toBe(1);
|
|
144
|
+
expect(cacheIssues[0].message).toContain("50.0%");
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
//# sourceMappingURL=tracing.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.test.js","sourceRoot":"","sources":["../../src/__tests__/tracing.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape of the Joone configuration file (~/.joone/config.json).
|
|
3
|
+
*/
|
|
4
|
+
export interface JooneConfig {
|
|
5
|
+
provider: string;
|
|
6
|
+
model: string;
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
maxTokens: number;
|
|
9
|
+
temperature: number;
|
|
10
|
+
streaming: boolean;
|
|
11
|
+
/** E2B sandbox template. If set, uses a pre-baked template (prod). If unset, uses default + lazy install (dev). */
|
|
12
|
+
sandboxTemplate?: string;
|
|
13
|
+
/** E2B API key for sandbox provisioning. */
|
|
14
|
+
e2bApiKey?: string;
|
|
15
|
+
/** OpenSandbox API key for sandbox fallback provisioning. */
|
|
16
|
+
openSandboxApiKey?: string;
|
|
17
|
+
/** OpenSandbox API Domain for fallback. */
|
|
18
|
+
openSandboxDomain?: string;
|
|
19
|
+
/** Gemini API key for SecurityScanTool (Gemini CLI inside sandbox). */
|
|
20
|
+
geminiApiKey?: string;
|
|
21
|
+
/** Valyu API key for web search. */
|
|
22
|
+
valyuApiKey?: string;
|
|
23
|
+
/** LangSmith API key for tracing (optional). */
|
|
24
|
+
langsmithApiKey?: string;
|
|
25
|
+
/** LangSmith project name (optional, default: "joone"). */
|
|
26
|
+
langsmithProject?: string;
|
|
27
|
+
/** Tool permission mode: 'auto' (no prompts), 'ask_dangerous' (prompt for destructive tools), 'ask_all' (prompt for everything). */
|
|
28
|
+
permissionMode?: "auto" | "ask_dangerous" | "ask_all";
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Sensible defaults — Anthropic Claude as the default provider.
|
|
32
|
+
*/
|
|
33
|
+
export declare const DEFAULT_CONFIG: JooneConfig;
|
|
34
|
+
/**
|
|
35
|
+
* Loads the Joone config from the specified path.
|
|
36
|
+
* Returns DEFAULT_CONFIG if the file does not exist.
|
|
37
|
+
* Falls back to environment variables for API key if not set in config.
|
|
38
|
+
*/
|
|
39
|
+
export declare function loadConfig(configPath: string): JooneConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Saves the Joone config to the specified path.
|
|
42
|
+
* Creates the parent directory if it doesn't exist.
|
|
43
|
+
* Sets restrictive file permissions (owner-only read/write) for security.
|
|
44
|
+
*/
|
|
45
|
+
export declare function saveConfig(configPath: string, config: JooneConfig): void;
|
|
46
|
+
/**
|
|
47
|
+
* Returns the expected environment variable name for a provider's API key.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getProviderEnvVar(provider: string): string | undefined;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Sensible defaults — Anthropic Claude as the default provider.
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_CONFIG = {
|
|
7
|
+
provider: "anthropic",
|
|
8
|
+
model: "claude-sonnet-4-20250514",
|
|
9
|
+
maxTokens: 4096,
|
|
10
|
+
temperature: 0,
|
|
11
|
+
streaming: true,
|
|
12
|
+
permissionMode: "auto",
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Maps provider names to their expected environment variable for the API key.
|
|
16
|
+
*/
|
|
17
|
+
const PROVIDER_ENV_VARS = {
|
|
18
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
19
|
+
openai: "OPENAI_API_KEY",
|
|
20
|
+
google: "GOOGLE_API_KEY",
|
|
21
|
+
mistral: "MISTRAL_API_KEY",
|
|
22
|
+
groq: "GROQ_API_KEY",
|
|
23
|
+
deepseek: "DEEPSEEK_API_KEY",
|
|
24
|
+
fireworks: "FIREWORKS_API_KEY",
|
|
25
|
+
together: "TOGETHER_API_KEY",
|
|
26
|
+
// Ollama (local) doesn't need an API key
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Loads the Joone config from the specified path.
|
|
30
|
+
* Returns DEFAULT_CONFIG if the file does not exist.
|
|
31
|
+
* Falls back to environment variables for API key if not set in config.
|
|
32
|
+
*/
|
|
33
|
+
export function loadConfig(configPath) {
|
|
34
|
+
let config;
|
|
35
|
+
if (!fs.existsSync(configPath)) {
|
|
36
|
+
config = { ...DEFAULT_CONFIG };
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
try {
|
|
40
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
41
|
+
const parsed = JSON.parse(raw);
|
|
42
|
+
config = { ...DEFAULT_CONFIG, ...parsed };
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
console.warn(`Warning: Failed to parse config at ${configPath}. Using defaults.`);
|
|
46
|
+
config = { ...DEFAULT_CONFIG };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Env var fallback: if apiKey is missing, check the provider's env var
|
|
50
|
+
if (!config.apiKey) {
|
|
51
|
+
const envVar = PROVIDER_ENV_VARS[config.provider];
|
|
52
|
+
if (envVar && process.env[envVar]) {
|
|
53
|
+
config.apiKey = process.env[envVar];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return config;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Saves the Joone config to the specified path.
|
|
60
|
+
* Creates the parent directory if it doesn't exist.
|
|
61
|
+
* Sets restrictive file permissions (owner-only read/write) for security.
|
|
62
|
+
*/
|
|
63
|
+
export function saveConfig(configPath, config) {
|
|
64
|
+
const dir = path.dirname(configPath);
|
|
65
|
+
if (!fs.existsSync(dir)) {
|
|
66
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
67
|
+
}
|
|
68
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), {
|
|
69
|
+
encoding: "utf-8",
|
|
70
|
+
mode: 0o600, // Owner read/write only (Linux/macOS)
|
|
71
|
+
});
|
|
72
|
+
// On Unix systems, enforce permissions even if file already existed
|
|
73
|
+
try {
|
|
74
|
+
fs.chmodSync(configPath, 0o600);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// chmod may fail on Windows — ignore silently
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns the expected environment variable name for a provider's API key.
|
|
82
|
+
*/
|
|
83
|
+
export function getProviderEnvVar(provider) {
|
|
84
|
+
return PROVIDER_ENV_VARS[provider];
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAgClC;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAgB;IACzC,QAAQ,EAAE,WAAW;IACrB,KAAK,EAAE,0BAA0B;IACjC,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,CAAC;IACd,SAAS,EAAE,IAAI;IACf,cAAc,EAAE,MAAM;CACvB,CAAC;AAEF;;GAEG;AACH,MAAM,iBAAiB,GAA2B;IAChD,SAAS,EAAE,mBAAmB;IAC9B,MAAM,EAAE,gBAAgB;IACxB,MAAM,EAAE,gBAAgB;IACxB,OAAO,EAAE,iBAAiB;IAC1B,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,kBAAkB;IAC5B,SAAS,EAAE,mBAAmB;IAC9B,QAAQ,EAAE,kBAAkB;IAC5B,yCAAyC;CAC1C,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,IAAI,MAAmB,CAAC;IAExB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAC;YACvD,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,sCAAsC,UAAU,mBAAmB,CAAC,CAAC;YAClF,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC;QACjC,CAAC;IACH,CAAC;IACD,uEAAuE;IACvE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClD,IAAI,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,UAAkB,EAAE,MAAmB;IAChE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QAC5D,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE,KAAK,EAAE,sCAAsC;KACpD,CAAC,CAAC;IAEH,oEAAoE;IACpE,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;IAChD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC"}
|