morpheus-cli 0.1.5 → 0.1.8

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 (38) hide show
  1. package/README.md +273 -213
  2. package/bin/morpheus.js +30 -0
  3. package/dist/channels/telegram.js +7 -2
  4. package/dist/cli/commands/config.js +18 -3
  5. package/dist/cli/commands/init.js +3 -3
  6. package/dist/cli/index.js +8 -0
  7. package/dist/config/mcp-loader.js +42 -0
  8. package/dist/config/schemas.js +22 -0
  9. package/dist/http/__tests__/auth.test.js +53 -0
  10. package/dist/http/api.js +23 -0
  11. package/dist/http/middleware/auth.js +23 -0
  12. package/dist/http/server.js +2 -1
  13. package/dist/runtime/__tests__/agent_persistence.test.js +39 -33
  14. package/dist/runtime/__tests__/manual_start_verify.js +1 -0
  15. package/dist/runtime/agent.js +93 -8
  16. package/dist/runtime/audio-agent.js +11 -1
  17. package/dist/runtime/display.js +12 -0
  18. package/dist/runtime/memory/sqlite.js +186 -9
  19. package/dist/runtime/providers/factory.js +28 -2
  20. package/dist/runtime/scaffold.js +5 -0
  21. package/dist/runtime/tools/__tests__/tools.test.js +127 -0
  22. package/dist/runtime/tools/analytics-tools.js +103 -0
  23. package/dist/runtime/tools/config-tools.js +70 -0
  24. package/dist/runtime/tools/diagnostic-tools.js +124 -0
  25. package/dist/runtime/tools/factory.js +62 -18
  26. package/dist/runtime/tools/index.js +4 -0
  27. package/dist/types/auth.js +4 -0
  28. package/dist/types/config.js +1 -0
  29. package/dist/types/mcp.js +11 -0
  30. package/dist/types/stats.js +1 -0
  31. package/dist/types/tools.js +2 -0
  32. package/dist/types/usage.js +1 -0
  33. package/dist/ui/assets/index-CMGsLiNG.css +1 -0
  34. package/dist/ui/assets/index-DpbRiL07.js +50 -0
  35. package/dist/ui/index.html +2 -2
  36. package/package.json +5 -1
  37. package/dist/ui/assets/index-D1kvj0eG.css +0 -1
  38. package/dist/ui/assets/index-DTh8waF7.js +0 -50
@@ -0,0 +1,124 @@
1
+ import { tool } from "langchain";
2
+ import * as z from "zod";
3
+ import { ConfigManager } from "../../config/manager.js";
4
+ import { promises as fsPromises } from "fs";
5
+ import path from "path";
6
+ import { homedir } from "os";
7
+ // Tool for performing system diagnostics
8
+ export const DiagnosticTool = tool(async () => {
9
+ try {
10
+ const timestamp = new Date().toISOString();
11
+ const components = {};
12
+ // Check configuration
13
+ try {
14
+ const configManager = ConfigManager.getInstance();
15
+ await configManager.load();
16
+ const config = configManager.get();
17
+ // Basic validation - check if required fields exist
18
+ const requiredFields = ['llm', 'logging', 'ui'];
19
+ const missingFields = requiredFields.filter(field => !(field in config));
20
+ if (missingFields.length === 0) {
21
+ components.config = {
22
+ status: "healthy",
23
+ message: "Configuration is valid and complete",
24
+ details: {
25
+ llmProvider: config.llm?.provider,
26
+ uiEnabled: config.ui?.enabled,
27
+ uiPort: config.ui?.port
28
+ }
29
+ };
30
+ }
31
+ else {
32
+ components.config = {
33
+ status: "warning",
34
+ message: `Missing required configuration fields: ${missingFields.join(', ')}`,
35
+ details: { missingFields }
36
+ };
37
+ }
38
+ }
39
+ catch (error) {
40
+ components.config = {
41
+ status: "error",
42
+ message: `Configuration error: ${error.message}`,
43
+ details: {}
44
+ };
45
+ }
46
+ // Check storage/database
47
+ try {
48
+ // For now, we'll check if the data directory exists
49
+ const dbPath = path.join(homedir(), ".morpheus", "memory", "short-memory.db");
50
+ await fsPromises.access(dbPath);
51
+ components.storage = {
52
+ status: "healthy",
53
+ message: "Database file is accessible",
54
+ details: { path: dbPath }
55
+ };
56
+ }
57
+ catch (error) {
58
+ components.storage = {
59
+ status: "error",
60
+ message: `Storage error: ${error.message}`,
61
+ details: {}
62
+ };
63
+ }
64
+ // Check network connectivity (basic check)
65
+ try {
66
+ // For now, we'll just check if we can reach the LLM provider configuration
67
+ const configManager = ConfigManager.getInstance();
68
+ await configManager.load();
69
+ const config = configManager.get();
70
+ if (config.llm && config.llm.provider) {
71
+ components.network = {
72
+ status: "healthy",
73
+ message: `LLM provider configured: ${config.llm.provider}`,
74
+ details: { provider: config.llm.provider }
75
+ };
76
+ }
77
+ else {
78
+ components.network = {
79
+ status: "warning",
80
+ message: "No LLM provider configured",
81
+ details: {}
82
+ };
83
+ }
84
+ }
85
+ catch (error) {
86
+ components.network = {
87
+ status: "error",
88
+ message: `Network check error: ${error.message}`,
89
+ details: {}
90
+ };
91
+ }
92
+ // Check if the agent is running
93
+ try {
94
+ // This is a basic check - in a real implementation, we might check if the agent process is running
95
+ components.agent = {
96
+ status: "healthy",
97
+ message: "Agent is running",
98
+ details: { uptime: "N/A - runtime information not available in this context" }
99
+ };
100
+ }
101
+ catch (error) {
102
+ components.agent = {
103
+ status: "error",
104
+ message: `Agent check error: ${error.message}`,
105
+ details: {}
106
+ };
107
+ }
108
+ return JSON.stringify({
109
+ timestamp,
110
+ components
111
+ });
112
+ }
113
+ catch (error) {
114
+ console.error("Error in DiagnosticTool:", error);
115
+ return JSON.stringify({
116
+ timestamp: new Date().toISOString(),
117
+ error: "Failed to run diagnostics"
118
+ });
119
+ }
120
+ }, {
121
+ name: "diagnostic_check",
122
+ description: "Performs system health diagnostics and returns a comprehensive report on system components.",
123
+ schema: z.object({}),
124
+ });
@@ -1,33 +1,77 @@
1
1
  import { MultiServerMCPClient } from "@langchain/mcp-adapters";
2
2
  import { DisplayManager } from "../display.js";
3
+ import { loadMCPConfig } from "../../config/mcp-loader.js";
4
+ const display = DisplayManager.getInstance();
5
+ // Fields not supported by Google Gemini API
6
+ const UNSUPPORTED_SCHEMA_FIELDS = ['examples', 'additionalInfo', 'default', '$schema'];
7
+ /**
8
+ * Recursively removes unsupported fields from JSON schema objects.
9
+ * This is needed because some MCP servers (like Coolify) return schemas
10
+ * with fields that Gemini doesn't accept.
11
+ */
12
+ function sanitizeSchema(obj) {
13
+ if (obj === null || typeof obj !== 'object') {
14
+ return obj;
15
+ }
16
+ if (Array.isArray(obj)) {
17
+ return obj.map(sanitizeSchema);
18
+ }
19
+ const sanitized = {};
20
+ for (const [key, value] of Object.entries(obj)) {
21
+ if (!UNSUPPORTED_SCHEMA_FIELDS.includes(key)) {
22
+ sanitized[key] = sanitizeSchema(value);
23
+ }
24
+ }
25
+ return sanitized;
26
+ }
27
+ /**
28
+ * Wraps a tool to sanitize its schema for Gemini compatibility.
29
+ * Creates a proxy that intercepts schema access and sanitizes the output.
30
+ */
31
+ function wrapToolWithSanitizedSchema(tool) {
32
+ display.log('Tool loaded: - ' + tool.name, { source: 'ToolsFactory' });
33
+ // The MCP tools have a schema property that returns JSON Schema
34
+ // We need to intercept and sanitize it
35
+ const originalSchema = tool.schema;
36
+ if (originalSchema && typeof originalSchema === 'object') {
37
+ // Sanitize the schema object directly
38
+ const sanitized = sanitizeSchema(originalSchema);
39
+ tool.schema = sanitized;
40
+ }
41
+ return tool;
42
+ }
3
43
  export class ToolsFactory {
4
44
  static async create() {
5
45
  const display = DisplayManager.getInstance();
46
+ const mcpServers = await loadMCPConfig();
47
+ const serverCount = Object.keys(mcpServers).length;
48
+ if (serverCount === 0) {
49
+ display.log('No MCP servers configured in mcps.json', { level: 'info', source: 'ToolsFactory' });
50
+ return [];
51
+ }
6
52
  const client = new MultiServerMCPClient({
7
- mcpServers: {
8
- coingecko: {
9
- transport: "stdio",
10
- command: "npx",
11
- args: ["-y", "mcp_coingecko_price_ts"],
12
- },
13
- },
53
+ mcpServers: mcpServers,
54
+ onConnectionError: "ignore",
14
55
  // log the MCP client's internal events
15
- beforeToolCall: ({ serverName, name, args }) => {
16
- display.log(`MCP Tool Call - Server: ${serverName}, Tool: ${name}, Args: ${JSON.stringify(args)}`);
17
- return;
18
- },
19
- // log the results of tool calls
20
- afterToolCall: (res) => {
21
- display.log(`MCP Tool Result - ${JSON.stringify(res)}`);
22
- return;
23
- }
56
+ // beforeToolCall: ({ serverName, name, args }) => {
57
+ // display.log(`MCP Tool Call - Server: ${serverName}, Tool: ${name}, Args: ${JSON.stringify(args)}`, { source: 'MCPServer' });
58
+ // return;
59
+ // },
60
+ // // log the results of tool calls
61
+ // afterToolCall: (res) => {
62
+ // display.log(`MCP Tool Result - ${JSON.stringify(res)}`, { source: 'MCPServer' });
63
+ // return;
64
+ // }
24
65
  });
25
66
  try {
26
67
  const tools = await client.getTools();
27
- return tools;
68
+ // Sanitize tool schemas to remove fields not supported by Gemini
69
+ const sanitizedTools = tools.map(tool => wrapToolWithSanitizedSchema(tool));
70
+ display.log(`Loaded ${sanitizedTools.length} MCP tools (schemas sanitized for Gemini compatibility)`, { level: 'info', source: 'ToolsFactory' });
71
+ return sanitizedTools;
28
72
  }
29
73
  catch (error) {
30
- display.log(`Failed to initialize MCP tools: ${error}`, { level: 'warning' });
74
+ display.log(`Failed to initialize MCP tools: ${error}`, { level: 'warning', source: 'ToolsFactory' });
31
75
  return []; // Return empty tools on failure to allow agent to start
32
76
  }
33
77
  }
@@ -0,0 +1,4 @@
1
+ // Export all tools from the tools module
2
+ export * from './config-tools.js';
3
+ export * from './diagnostic-tools.js';
4
+ export * from './analytics-tools.js';
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Shared constants and interfaces for HTTP Authentication
3
+ */
4
+ export const AUTH_HEADER = 'x-architect-pass';
@@ -9,6 +9,7 @@ export const DEFAULT_CONFIG = {
9
9
  retention: '14d',
10
10
  },
11
11
  audio: {
12
+ provider: 'google',
12
13
  enabled: true,
13
14
  maxDurationSeconds: 300,
14
15
  supportedMimeTypes: ['audio/ogg', 'audio/mp3', 'audio/mpeg', 'audio/wav'],
@@ -0,0 +1,11 @@
1
+ export const DEFAULT_MCP_TEMPLATE = {
2
+ "_comment": "MCP Server Configuration for Morpheus",
3
+ "_docs": "Add your MCP servers below. Each key is a unique server name.",
4
+ "example": {
5
+ "_comment": "EXAMPLE - Remove or replace this entry with your own MCPs",
6
+ "transport": "stdio",
7
+ "command": "npx",
8
+ "args": ["-y", "your-mcp-package-name"],
9
+ "env": {}
10
+ }
11
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ // Tool type definitions for Morpheus internal tools
2
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:Courier New,Courier,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}body{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));font-family:Courier New,Courier,monospace;--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}body:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(0 143 17 / var(--tw-text-opacity, 1))}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}:is(.dark *)::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}::-webkit-scrollbar-thumb{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}:is(.dark *)::-webkit-scrollbar-thumb{--tw-bg-opacity: 1;background-color:rgb(0 59 0 / var(--tw-bg-opacity, 1))}::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}:is(.dark *)::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(0 143 17 / var(--tw-bg-opacity, 1))}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.static{position:static}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.top-0{top:0}.z-10{z-index:10}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mt-1{margin-top:.25rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.min-h-screen{min-height:100vh}.w-11{width:2.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-full{width:100%}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.translate-x-1{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-6{--tw-translate-x: 1.5rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(229 231 235 / var(--tw-divide-opacity, 1))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.border{border-width:1px}.border-x{border-left-width:1px;border-right-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-green-500{--tw-border-opacity: 1;border-color:rgb(34 197 94 / var(--tw-border-opacity, 1))}.border-green-800{--tw-border-opacity: 1;border-color:rgb(22 101 52 / var(--tw-border-opacity, 1))}.border-matrix-highlight{--tw-border-opacity: 1;border-color:rgb(0 255 65 / var(--tw-border-opacity, 1))}.border-matrix-primary{--tw-border-opacity: 1;border-color:rgb(0 59 0 / var(--tw-border-opacity, 1))}.border-matrix-primary\/50{border-color:#003b0080}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.border-red-500\/50{border-color:#ef444480}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-green-100{--tw-bg-opacity: 1;background-color:rgb(220 252 231 / var(--tw-bg-opacity, 1))}.bg-green-900{--tw-bg-opacity: 1;background-color:rgb(20 83 45 / var(--tw-bg-opacity, 1))}.bg-green-900\/20{background-color:#14532d33}.bg-matrix-base{--tw-bg-opacity: 1;background-color:rgb(13 2 8 / var(--tw-bg-opacity, 1))}.bg-matrix-base\/50{background-color:#0d020880}.bg-matrix-highlight{--tw-bg-opacity: 1;background-color:rgb(0 255 65 / var(--tw-bg-opacity, 1))}.bg-matrix-highlight\/10{background-color:#00ff411a}.bg-matrix-primary{--tw-bg-opacity: 1;background-color:rgb(0 59 0 / var(--tw-bg-opacity, 1))}.bg-matrix-primary\/20{background-color:#003b0033}.bg-matrix-primary\/50{background-color:#003b0080}.bg-red-900\/10{background-color:#7f1d1d1a}.bg-red-900\/20{background-color:#7f1d1d33}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity, 1))}.bg-zinc-950{--tw-bg-opacity: 1;background-color:rgb(12 12 12 / var(--tw-bg-opacity, 1))}.bg-zinc-950\/50{background-color:#0c0c0c80}.bg-\[linear-gradient\(rgba\(18\,16\,16\,0\)_50\%\,rgba\(0\,0\,0\,0\.1\)_50\%\)\,linear-gradient\(90deg\,rgba\(0\,255\,0\,0\.03\)\,rgba\(0\,255\,0\,0\.01\)\)\]{background-image:linear-gradient(#12101000 50%,#0000001a 50%),linear-gradient(90deg,#00ff0008,#00ff0003)}.bg-\[length\:100\%_2px\,3px_100\%\]{background-size:100% 2px,3px 100%}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-px{padding-bottom:1px}.pl-4{padding-left:1rem}.pr-4{padding-right:1rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:Courier New,Courier,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tracking-tighter{letter-spacing:-.05em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-green-800{--tw-text-opacity: 1;color:rgb(22 101 52 / var(--tw-text-opacity, 1))}.text-matrix-highlight{--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.text-matrix-highlight\/50{color:#00ff4180}.text-matrix-highlight\/80{color:#00ff41cc}.text-matrix-primary{--tw-text-opacity: 1;color:rgb(0 59 0 / var(--tw-text-opacity, 1))}.text-matrix-secondary{--tw-text-opacity: 1;color:rgb(0 143 17 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.placeholder-green-900::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(20 83 45 / var(--tw-placeholder-opacity, 1))}.placeholder-green-900::placeholder{--tw-placeholder-opacity: 1;color:rgb(20 83 45 / var(--tw-placeholder-opacity, 1))}.placeholder-matrix-secondary\/50::-moz-placeholder{color:#008f1180}.placeholder-matrix-secondary\/50::placeholder{color:#008f1180}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.shadow-\[0_0_15px_rgba\(34\,197\,94\,0\.3\)\]{--tw-shadow: 0 0 15px rgba(34,197,94,.3);--tw-shadow-colored: 0 0 15px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.hover\:border-matrix-highlight:hover{--tw-border-opacity: 1;border-color:rgb(0 255 65 / var(--tw-border-opacity, 1))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-green-700:hover{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.hover\:bg-matrix-highlight\/90:hover{background-color:#00ff41e6}.hover\:bg-matrix-secondary:hover{--tw-bg-opacity: 1;background-color:rgb(0 143 17 / var(--tw-bg-opacity, 1))}.hover\:bg-red-50:hover{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-900:hover{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity, 1))}.hover\:text-matrix-highlight:hover{--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.hover\:text-red-600:hover{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.focus\:border-green-500:focus{--tw-border-opacity: 1;border-color:rgb(34 197 94 / var(--tw-border-opacity, 1))}.focus\:border-matrix-highlight:focus{--tw-border-opacity: 1;border-color:rgb(0 255 65 / var(--tw-border-opacity, 1))}.focus\:border-red-500:focus{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-matrix-highlight:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(0 255 65 / var(--tw-ring-opacity, 1))}.focus\:ring-red-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity, 1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus\:ring-offset-black:focus{--tw-ring-offset-color: #000}.group:hover .group-hover\:text-matrix-highlight{--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.dark\:divide-matrix-primary\/30:is(.dark *)>:not([hidden])~:not([hidden]){border-color:#003b004d}.dark\:border-matrix-primary:is(.dark *){--tw-border-opacity: 1;border-color:rgb(0 59 0 / var(--tw-border-opacity, 1))}.dark\:bg-black:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.dark\:bg-black\/40:is(.dark *){background-color:#0006}.dark\:bg-matrix-primary:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 59 0 / var(--tw-bg-opacity, 1))}.dark\:bg-zinc-900\/50:is(.dark *){background-color:#18181b80}.dark\:bg-zinc-950:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(12 12 12 / var(--tw-bg-opacity, 1))}.dark\:text-gray-300:is(.dark *){--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.dark\:text-matrix-highlight:is(.dark *){--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.dark\:text-matrix-secondary:is(.dark *){--tw-text-opacity: 1;color:rgb(0 143 17 / var(--tw-text-opacity, 1))}.dark\:opacity-20:is(.dark *){opacity:.2}.dark\:hover\:bg-matrix-primary\/10:hover:is(.dark *){background-color:#003b001a}.dark\:hover\:bg-matrix-primary\/50:hover:is(.dark *){background-color:#003b0080}.dark\:hover\:bg-red-900\/20:hover:is(.dark *){background-color:#7f1d1d33}@media(min-width:768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}