triagent 0.1.0-alpha8 → 0.1.0-beta2
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/README.md +101 -1
- package/package.json +9 -3
- package/src/cli/config.ts +118 -2
- package/src/config.ts +23 -3
- package/src/index.ts +262 -6
- package/src/integrations/elasticsearch/client.ts +210 -0
- package/src/integrations/grafana/client.ts +186 -0
- package/src/integrations/kubernetes/multi-cluster.ts +199 -0
- package/src/integrations/kubernetes/types.ts +24 -0
- package/src/integrations/loki/client.ts +219 -0
- package/src/integrations/prometheus/client.ts +163 -0
- package/src/integrations/slack/client.ts +265 -0
- package/src/integrations/teams/client.ts +199 -0
- package/src/mastra/agents/debugger.ts +164 -109
- package/src/mastra/index.ts +2 -2
- package/src/mastra/tools/approval-store.ts +180 -0
- package/src/mastra/tools/cli.ts +94 -2
- package/src/mastra/tools/cost.ts +389 -0
- package/src/mastra/tools/logs.ts +210 -0
- package/src/mastra/tools/network.ts +253 -0
- package/src/mastra/tools/prometheus.ts +221 -0
- package/src/mastra/tools/remediation.ts +365 -0
- package/src/mastra/tools/runbook.ts +186 -0
- package/src/sandbox/bashlet.ts +76 -10
- package/src/server/routes/history.ts +207 -0
- package/src/server/routes/notifications.ts +236 -0
- package/src/server/webhook.ts +36 -2
- package/src/storage/index.ts +3 -0
- package/src/storage/investigation-history.ts +277 -0
- package/src/storage/runbook-index.ts +330 -0
- package/src/storage/types.ts +72 -0
- package/src/tui/app.tsx +278 -198
- package/src/tui/components/approval-dialog.tsx +147 -0
- package/src/tui/components/approval-modal.tsx +278 -0
- package/src/tui/components/centered-layout.tsx +33 -0
- package/src/tui/components/editor.tsx +87 -0
- package/src/tui/components/header.tsx +53 -0
- package/src/tui/components/index.ts +55 -0
- package/src/tui/components/message-item.tsx +131 -0
- package/src/tui/components/messages-panel.tsx +71 -0
- package/src/tui/components/status-badge.tsx +20 -0
- package/src/tui/components/status-bar.tsx +39 -0
- package/src/tui/components/styled-span.tsx +24 -0
- package/src/tui/components/timeline.tsx +223 -0
- package/src/tui/components/toast.tsx +104 -0
- package/src/tui/theme/index.ts +21 -0
- package/src/tui/theme/tokens.ts +180 -0
package/README.md
CHANGED
|
@@ -26,6 +26,69 @@ triagent
|
|
|
26
26
|
|
|
27
27
|
# Run webhook server only
|
|
28
28
|
triagent --webhook-only
|
|
29
|
+
|
|
30
|
+
# Direct incident investigation
|
|
31
|
+
triagent --incident "API gateway returning 503 errors"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Execution Modes
|
|
35
|
+
|
|
36
|
+
Triagent supports three execution modes for running commands:
|
|
37
|
+
|
|
38
|
+
### Sandbox Mode (Default)
|
|
39
|
+
|
|
40
|
+
Commands run inside a Docker container via [Bashlet](https://github.com/anthropics/bashlet). This provides isolation and safety - the agent cannot accidentally modify your host system.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
triagent
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Codebases are mounted at `/workspace/<name>` and kubeconfig at `/root/.kube`.
|
|
47
|
+
|
|
48
|
+
### Host Mode
|
|
49
|
+
|
|
50
|
+
Commands run directly on your local machine. Use this when you need access to tools not available in the sandbox.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
triagent --host
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Remote Mode
|
|
57
|
+
|
|
58
|
+
Commands run on a remote server via SSH. This is useful for connecting to a Docker container or VM with pre-installed debugging tools.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
triagent --remote user@host
|
|
62
|
+
triagent -r root@debug-container.local
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
On startup, triagent creates a session workspace on the remote:
|
|
66
|
+
```
|
|
67
|
+
🌐 Running in remote mode: root@debug-container.local
|
|
68
|
+
Workspace: /tmp/triagent-a3b4c5d6 (session: a3b4c5d6)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Requirements:**
|
|
72
|
+
- SSH key-based authentication (no password prompts)
|
|
73
|
+
- The remote must have the necessary CLI tools (kubectl, etc.)
|
|
74
|
+
|
|
75
|
+
**Example: Debug container setup**
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Run a container with SSH and debugging tools
|
|
79
|
+
docker run -d --name debug-tools \
|
|
80
|
+
-p 2222:22 \
|
|
81
|
+
-v ~/.kube:/root/.kube:ro \
|
|
82
|
+
your-debug-image:latest
|
|
83
|
+
|
|
84
|
+
# Add to ~/.ssh/config for easy access
|
|
85
|
+
# Host debug-tools
|
|
86
|
+
# HostName localhost
|
|
87
|
+
# Port 2222
|
|
88
|
+
# User root
|
|
89
|
+
|
|
90
|
+
# Connect triagent to it
|
|
91
|
+
triagent --remote debug-tools
|
|
29
92
|
```
|
|
30
93
|
|
|
31
94
|
## Configuration
|
|
@@ -57,9 +120,46 @@ triagent config path
|
|
|
57
120
|
| `apiKey` | API key for the provider | - |
|
|
58
121
|
| `baseUrl` | Custom API base URL (for proxies or local models) | - |
|
|
59
122
|
| `webhookPort` | Webhook server port | `3000` |
|
|
60
|
-
| `codebasePath` | Path to codebase | `./` |
|
|
123
|
+
| `codebasePath` | Path to single codebase (legacy) | `./` |
|
|
61
124
|
| `kubeConfigPath` | Kubernetes config path | `~/.kube` |
|
|
62
125
|
|
|
126
|
+
### Multiple Codebases
|
|
127
|
+
|
|
128
|
+
For applications spanning multiple repositories, configure `codebasePaths` in `~/.config/triagent/config.json`:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"codebasePaths": [
|
|
133
|
+
{ "name": "frontend", "path": "/path/to/frontend-repo" },
|
|
134
|
+
{ "name": "backend", "path": "/path/to/backend-repo" },
|
|
135
|
+
{ "name": "infra", "path": "/path/to/infrastructure" }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Each codebase is mounted at `/workspace/<name>` in the sandbox. The model can access any codebase as needed during investigation.
|
|
141
|
+
|
|
142
|
+
### Custom Instructions (TRIAGENT.md)
|
|
143
|
+
|
|
144
|
+
Create `~/.config/triagent/TRIAGENT.md` to provide custom instructions to the model. These instructions are prepended to the default system prompt.
|
|
145
|
+
|
|
146
|
+
Example `TRIAGENT.md`:
|
|
147
|
+
|
|
148
|
+
```markdown
|
|
149
|
+
## Project Context
|
|
150
|
+
|
|
151
|
+
This is a microservices e-commerce platform with the following services:
|
|
152
|
+
- frontend: Next.js app in /workspace/frontend
|
|
153
|
+
- api: Go backend in /workspace/backend
|
|
154
|
+
- infra: Terraform configs in /workspace/infra
|
|
155
|
+
|
|
156
|
+
## Investigation Priorities
|
|
157
|
+
|
|
158
|
+
1. Always check the api service logs first for 5xx errors
|
|
159
|
+
2. The frontend service talks to api via internal DNS: api.default.svc.cluster.local
|
|
160
|
+
3. Common issues: Redis connection timeouts, PostgreSQL connection pool exhaustion
|
|
161
|
+
```
|
|
162
|
+
|
|
63
163
|
### Environment Variables
|
|
64
164
|
|
|
65
165
|
| Variable | Description |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "triagent",
|
|
3
|
-
"version": "0.1.0-
|
|
3
|
+
"version": "0.1.0-beta2",
|
|
4
4
|
"description": "AI-powered Kubernetes debugging agent with terminal UI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
],
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|
|
30
|
-
"url": "git+https://github.com/
|
|
30
|
+
"url": "git+https://github.com/ServiceWeave/triagent.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/ServiceWeave/triagent#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/ServiceWeave/triagent/issues"
|
|
31
35
|
},
|
|
32
36
|
"license": "MIT",
|
|
33
37
|
"scripts": {
|
|
@@ -41,8 +45,10 @@
|
|
|
41
45
|
"@ai-sdk/anthropic": "^1.2.0",
|
|
42
46
|
"@ai-sdk/google": "^1.2.0",
|
|
43
47
|
"@ai-sdk/openai": "^1.3.0",
|
|
44
|
-
"@bashlet/sdk": "
|
|
48
|
+
"@bashlet/sdk": "1.3.2",
|
|
45
49
|
"@mastra/core": "^1.0.0-beta.21",
|
|
50
|
+
"@opentui-ui/dialog": "^0.1.2",
|
|
51
|
+
"@opentui-ui/toast": "^0.0.5",
|
|
46
52
|
"@opentui/core": "^0.1.72",
|
|
47
53
|
"@opentui/solid": "^0.1.72",
|
|
48
54
|
"hono": "^4.6.0",
|
package/src/cli/config.ts
CHANGED
|
@@ -3,23 +3,139 @@ import { homedir } from "os";
|
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import type { AIProvider } from "../config.js";
|
|
5
5
|
|
|
6
|
+
export interface CodebaseEntry {
|
|
7
|
+
name: string;
|
|
8
|
+
path: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ClusterConfig {
|
|
12
|
+
name: string;
|
|
13
|
+
context: string;
|
|
14
|
+
kubeConfigPath?: string;
|
|
15
|
+
environment?: "development" | "staging" | "production";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PrometheusConfig {
|
|
19
|
+
url: string;
|
|
20
|
+
auth?: { token: string };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface GrafanaConfig {
|
|
24
|
+
url: string;
|
|
25
|
+
apiKey: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ElasticsearchConfig {
|
|
29
|
+
url: string;
|
|
30
|
+
index: string;
|
|
31
|
+
auth?: { apiKey: string };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface LokiConfig {
|
|
35
|
+
url: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface CloudWatchConfig {
|
|
39
|
+
region: string;
|
|
40
|
+
logGroupPrefix?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface SlackConfig {
|
|
44
|
+
webhookUrl: string;
|
|
45
|
+
botToken?: string;
|
|
46
|
+
defaultChannel?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface TeamsConfig {
|
|
50
|
+
webhookUrl: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface RunbookConfig {
|
|
54
|
+
paths: string[];
|
|
55
|
+
gitRepos?: string[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface CostAnalysisConfig {
|
|
59
|
+
provider?: "aws" | "gcp" | "azure";
|
|
60
|
+
hourlyRates?: {
|
|
61
|
+
cpu: number;
|
|
62
|
+
memory: number;
|
|
63
|
+
storage: number;
|
|
64
|
+
};
|
|
65
|
+
businessImpact?: {
|
|
66
|
+
revenuePerMinute?: number;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
6
70
|
export interface StoredConfig {
|
|
7
71
|
aiProvider?: AIProvider;
|
|
8
72
|
aiModel?: string;
|
|
9
73
|
apiKey?: string;
|
|
10
74
|
baseUrl?: string;
|
|
11
75
|
webhookPort?: number;
|
|
12
|
-
codebasePath?: string;
|
|
76
|
+
codebasePath?: string; // Deprecated: use codebasePaths instead
|
|
77
|
+
codebasePaths?: CodebaseEntry[];
|
|
13
78
|
kubeConfigPath?: string;
|
|
79
|
+
|
|
80
|
+
// Phase 1: Foundation
|
|
81
|
+
historyRetentionDays?: number;
|
|
82
|
+
clusters?: ClusterConfig[];
|
|
83
|
+
activeCluster?: string;
|
|
84
|
+
|
|
85
|
+
// Phase 2: Observability
|
|
86
|
+
prometheus?: PrometheusConfig;
|
|
87
|
+
grafana?: GrafanaConfig;
|
|
88
|
+
logProvider?: "elasticsearch" | "loki" | "cloudwatch";
|
|
89
|
+
elasticsearch?: ElasticsearchConfig;
|
|
90
|
+
loki?: LokiConfig;
|
|
91
|
+
cloudwatch?: CloudWatchConfig;
|
|
92
|
+
|
|
93
|
+
// Phase 3: Operations
|
|
94
|
+
runbooks?: RunbookConfig;
|
|
95
|
+
|
|
96
|
+
// Phase 4: Communication & Cost
|
|
97
|
+
notifications?: {
|
|
98
|
+
slack?: SlackConfig;
|
|
99
|
+
teams?: TeamsConfig;
|
|
100
|
+
};
|
|
101
|
+
costAnalysis?: CostAnalysisConfig;
|
|
14
102
|
}
|
|
15
103
|
|
|
16
104
|
const CONFIG_DIR = join(homedir(), ".config", "triagent");
|
|
17
105
|
const CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
106
|
+
const TRIAGENT_MD_FILE = join(CONFIG_DIR, "TRIAGENT.md");
|
|
107
|
+
const RUNBOOK_MD_FILE = join(CONFIG_DIR, "RUNBOOK.md");
|
|
18
108
|
|
|
19
109
|
export async function getConfigPath(): Promise<string> {
|
|
20
110
|
return CONFIG_FILE;
|
|
21
111
|
}
|
|
22
112
|
|
|
113
|
+
export async function getTriagentMdPath(): Promise<string> {
|
|
114
|
+
return TRIAGENT_MD_FILE;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function loadTriagentMd(): Promise<string | null> {
|
|
118
|
+
try {
|
|
119
|
+
const content = await readFile(TRIAGENT_MD_FILE, "utf-8");
|
|
120
|
+
return content.trim();
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function loadRunbookMd(): Promise<string | null> {
|
|
127
|
+
try {
|
|
128
|
+
const content = await readFile(RUNBOOK_MD_FILE, "utf-8");
|
|
129
|
+
return content.trim();
|
|
130
|
+
} catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export async function getRunbookMdPath(): Promise<string> {
|
|
136
|
+
return RUNBOOK_MD_FILE;
|
|
137
|
+
}
|
|
138
|
+
|
|
23
139
|
export async function loadStoredConfig(): Promise<StoredConfig> {
|
|
24
140
|
try {
|
|
25
141
|
const content = await readFile(CONFIG_FILE, "utf-8");
|
|
@@ -40,7 +156,7 @@ export async function setConfigValue(key: keyof StoredConfig, value: string | nu
|
|
|
40
156
|
await saveStoredConfig(config);
|
|
41
157
|
}
|
|
42
158
|
|
|
43
|
-
export async function getConfigValue(key: keyof StoredConfig): Promise<
|
|
159
|
+
export async function getConfigValue(key: keyof StoredConfig): Promise<StoredConfig[keyof StoredConfig]> {
|
|
44
160
|
const config = await loadStoredConfig();
|
|
45
161
|
return config[key];
|
|
46
162
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { resolve } from "path";
|
|
3
3
|
import { homedir } from "os";
|
|
4
|
-
import { loadStoredConfig, type StoredConfig } from "./cli/config.js";
|
|
4
|
+
import { loadStoredConfig, type StoredConfig, type CodebaseEntry } from "./cli/config.js";
|
|
5
5
|
|
|
6
6
|
const AIProviderSchema = z.enum(["openai", "anthropic", "google"]);
|
|
7
7
|
export type AIProvider = z.infer<typeof AIProviderSchema>;
|
|
8
8
|
|
|
9
|
+
const CodebaseEntrySchema = z.object({
|
|
10
|
+
name: z.string().min(1),
|
|
11
|
+
path: z.string().min(1),
|
|
12
|
+
});
|
|
13
|
+
|
|
9
14
|
const ConfigSchema = z.object({
|
|
10
15
|
aiProvider: AIProviderSchema,
|
|
11
16
|
aiModel: z.string().min(1),
|
|
12
17
|
apiKey: z.string().min(1),
|
|
13
18
|
baseUrl: z.string().url().optional(),
|
|
14
19
|
webhookPort: z.number().int().positive().default(3000),
|
|
15
|
-
|
|
20
|
+
codebasePaths: z.array(CodebaseEntrySchema).min(1),
|
|
16
21
|
kubeConfigPath: z.string().min(1).default("~/.kube"),
|
|
17
22
|
});
|
|
18
23
|
|
|
19
24
|
export type Config = z.infer<typeof ConfigSchema>;
|
|
25
|
+
export type { CodebaseEntry };
|
|
20
26
|
|
|
21
27
|
function expandPath(path: string): string {
|
|
22
28
|
if (path.startsWith("~")) {
|
|
@@ -39,6 +45,20 @@ function getApiKey(provider: AIProvider, stored: StoredConfig): string {
|
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
47
|
|
|
48
|
+
function resolveCodebasePaths(stored: StoredConfig): CodebaseEntry[] {
|
|
49
|
+
// Priority: codebasePaths array > legacy codebasePath > default
|
|
50
|
+
if (stored.codebasePaths && stored.codebasePaths.length > 0) {
|
|
51
|
+
return stored.codebasePaths.map((entry) => ({
|
|
52
|
+
name: entry.name,
|
|
53
|
+
path: expandPath(entry.path),
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Backward compatibility: convert single codebasePath to array
|
|
58
|
+
const legacyPath = process.env.CODEBASE_PATH || stored.codebasePath || "./";
|
|
59
|
+
return [{ name: "workspace", path: expandPath(legacyPath) }];
|
|
60
|
+
}
|
|
61
|
+
|
|
42
62
|
export async function loadConfig(): Promise<Config> {
|
|
43
63
|
const stored = await loadStoredConfig();
|
|
44
64
|
|
|
@@ -50,7 +70,7 @@ export async function loadConfig(): Promise<Config> {
|
|
|
50
70
|
apiKey: getApiKey(provider, stored),
|
|
51
71
|
baseUrl: process.env.AI_BASE_URL || stored.baseUrl || undefined,
|
|
52
72
|
webhookPort: parseInt(process.env.WEBHOOK_PORT || String(stored.webhookPort || 3000), 10),
|
|
53
|
-
|
|
73
|
+
codebasePaths: resolveCodebasePaths(stored),
|
|
54
74
|
kubeConfigPath: expandPath(process.env.KUBE_CONFIG_PATH || stored.kubeConfigPath || "~/.kube"),
|
|
55
75
|
};
|
|
56
76
|
|