triagent 0.1.0-alpha1
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 +51 -0
- package/bunfig.toml +1 -0
- package/package.json +57 -0
- package/src/cli/config.ts +60 -0
- package/src/config.ts +97 -0
- package/src/index.ts +310 -0
- package/src/mastra/agents/debugger.ts +176 -0
- package/src/mastra/index.ts +36 -0
- package/src/mastra/tools/filesystem.ts +129 -0
- package/src/mastra/tools/git.ts +83 -0
- package/src/mastra/tools/kubectl.ts +107 -0
- package/src/sandbox/bashlet.ts +151 -0
- package/src/server/webhook.ts +186 -0
- package/src/tui/app.tsx +281 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Triagent
|
|
2
|
+
|
|
3
|
+
AI-powered Kubernetes debugging agent with terminal UI.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun install triagent
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Run interactive TUI
|
|
15
|
+
triagent
|
|
16
|
+
|
|
17
|
+
# Run webhook server only
|
|
18
|
+
triagent --webhook-only
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
Set the following environment variables:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
ANTHROPIC_API_KEY=your-api-key
|
|
27
|
+
# or
|
|
28
|
+
OPENAI_API_KEY=your-api-key
|
|
29
|
+
# or
|
|
30
|
+
GOOGLE_GENERATIVE_AI_API_KEY=your-api-key
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Development
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Install dependencies
|
|
37
|
+
bun install
|
|
38
|
+
|
|
39
|
+
# Run in development mode
|
|
40
|
+
bun run dev
|
|
41
|
+
|
|
42
|
+
# Build
|
|
43
|
+
bun run build
|
|
44
|
+
|
|
45
|
+
# Type check
|
|
46
|
+
bun run typecheck
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT
|
package/bunfig.toml
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
preload = ["@opentui/solid/preload"]
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "triagent",
|
|
3
|
+
"version": "0.1.0-alpha1",
|
|
4
|
+
"description": "AI-powered Kubernetes debugging agent with terminal UI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"triagent": "./src/index.ts"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"bunfig.toml"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"bun": ">=1.2.0"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"kubernetes",
|
|
19
|
+
"k8s",
|
|
20
|
+
"debugging",
|
|
21
|
+
"ai",
|
|
22
|
+
"agent",
|
|
23
|
+
"cli",
|
|
24
|
+
"tui",
|
|
25
|
+
"devops",
|
|
26
|
+
"sre"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/OWNER/triagent.git"
|
|
31
|
+
},
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"scripts": {
|
|
34
|
+
"dev": "bun run src/index.ts",
|
|
35
|
+
"webhook": "bun run src/index.ts --webhook-only",
|
|
36
|
+
"build": "bun run scripts/build.ts",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"prepublishOnly": "bun run typecheck"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@ai-sdk/anthropic": "^1.2.0",
|
|
42
|
+
"@ai-sdk/google": "^1.2.0",
|
|
43
|
+
"@ai-sdk/openai": "^1.3.0",
|
|
44
|
+
"@bashlet/sdk": "^0.1.0-alpha1",
|
|
45
|
+
"@mastra/core": "^1.0.0-beta.21",
|
|
46
|
+
"@opentui/core": "^0.1.72",
|
|
47
|
+
"@opentui/solid": "^0.1.72",
|
|
48
|
+
"hono": "^4.6.0",
|
|
49
|
+
"solid-js": "^1.9.10",
|
|
50
|
+
"zod": "^3.24.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"typescript": "^5.7.0",
|
|
54
|
+
"@types/node": "^22.0.0",
|
|
55
|
+
"bun-types": "^1.2.0"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import type { AIProvider } from "../config.js";
|
|
5
|
+
|
|
6
|
+
export interface StoredConfig {
|
|
7
|
+
aiProvider?: AIProvider;
|
|
8
|
+
aiModel?: string;
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
webhookPort?: number;
|
|
11
|
+
codebasePath?: string;
|
|
12
|
+
kubeConfigPath?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const CONFIG_DIR = join(homedir(), ".config", "triagent");
|
|
16
|
+
const CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
17
|
+
|
|
18
|
+
export async function getConfigPath(): Promise<string> {
|
|
19
|
+
return CONFIG_FILE;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function loadStoredConfig(): Promise<StoredConfig> {
|
|
23
|
+
try {
|
|
24
|
+
const content = await readFile(CONFIG_FILE, "utf-8");
|
|
25
|
+
return JSON.parse(content);
|
|
26
|
+
} catch {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function saveStoredConfig(config: StoredConfig): Promise<void> {
|
|
32
|
+
await mkdir(CONFIG_DIR, { recursive: true });
|
|
33
|
+
await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function setConfigValue(key: keyof StoredConfig, value: string | number): Promise<void> {
|
|
37
|
+
const config = await loadStoredConfig();
|
|
38
|
+
(config as Record<string, string | number>)[key] = value;
|
|
39
|
+
await saveStoredConfig(config);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function getConfigValue(key: keyof StoredConfig): Promise<string | number | undefined> {
|
|
43
|
+
const config = await loadStoredConfig();
|
|
44
|
+
return config[key];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function deleteConfigValue(key: keyof StoredConfig): Promise<void> {
|
|
48
|
+
const config = await loadStoredConfig();
|
|
49
|
+
delete config[key];
|
|
50
|
+
await saveStoredConfig(config);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function listConfig(): Promise<StoredConfig> {
|
|
54
|
+
return loadStoredConfig();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function maskApiKey(key: string): string {
|
|
58
|
+
if (key.length <= 8) return "****";
|
|
59
|
+
return key.slice(0, 4) + "****" + key.slice(-4);
|
|
60
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { loadStoredConfig, type StoredConfig } from "./cli/config.js";
|
|
5
|
+
|
|
6
|
+
const AIProviderSchema = z.enum(["openai", "anthropic", "google"]);
|
|
7
|
+
export type AIProvider = z.infer<typeof AIProviderSchema>;
|
|
8
|
+
|
|
9
|
+
const ConfigSchema = z.object({
|
|
10
|
+
aiProvider: AIProviderSchema,
|
|
11
|
+
aiModel: z.string().min(1),
|
|
12
|
+
apiKey: z.string().min(1),
|
|
13
|
+
webhookPort: z.number().int().positive().default(3000),
|
|
14
|
+
codebasePath: z.string().min(1).default("./"),
|
|
15
|
+
kubeConfigPath: z.string().min(1).default("~/.kube"),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export type Config = z.infer<typeof ConfigSchema>;
|
|
19
|
+
|
|
20
|
+
function expandPath(path: string): string {
|
|
21
|
+
if (path.startsWith("~")) {
|
|
22
|
+
return resolve(homedir(), path.slice(2));
|
|
23
|
+
}
|
|
24
|
+
return resolve(path);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getApiKey(provider: AIProvider, stored: StoredConfig): string {
|
|
28
|
+
// Check stored config first, then env vars
|
|
29
|
+
if (stored.apiKey) return stored.apiKey;
|
|
30
|
+
|
|
31
|
+
switch (provider) {
|
|
32
|
+
case "openai":
|
|
33
|
+
return process.env.OPENAI_API_KEY || "";
|
|
34
|
+
case "anthropic":
|
|
35
|
+
return process.env.ANTHROPIC_API_KEY || "";
|
|
36
|
+
case "google":
|
|
37
|
+
return process.env.GOOGLE_GENERATIVE_AI_API_KEY || "";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function loadConfig(): Promise<Config> {
|
|
42
|
+
const stored = await loadStoredConfig();
|
|
43
|
+
|
|
44
|
+
const provider = (process.env.AI_PROVIDER || stored.aiProvider || "anthropic") as AIProvider;
|
|
45
|
+
|
|
46
|
+
const rawConfig = {
|
|
47
|
+
aiProvider: provider,
|
|
48
|
+
aiModel: process.env.AI_MODEL || stored.aiModel || getDefaultModel(provider),
|
|
49
|
+
apiKey: getApiKey(provider, stored),
|
|
50
|
+
webhookPort: parseInt(process.env.WEBHOOK_PORT || String(stored.webhookPort || 3000), 10),
|
|
51
|
+
codebasePath: expandPath(process.env.CODEBASE_PATH || stored.codebasePath || "./"),
|
|
52
|
+
kubeConfigPath: expandPath(process.env.KUBE_CONFIG_PATH || stored.kubeConfigPath || "~/.kube"),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const result = ConfigSchema.safeParse(rawConfig);
|
|
56
|
+
if (!result.success) {
|
|
57
|
+
const errors = result.error.errors
|
|
58
|
+
.map((e) => ` - ${e.path.join(".")}: ${e.message}`)
|
|
59
|
+
.join("\n");
|
|
60
|
+
throw new Error(`Invalid configuration:\n${errors}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return result.data;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getDefaultModel(provider: AIProvider): string {
|
|
67
|
+
switch (provider) {
|
|
68
|
+
case "openai":
|
|
69
|
+
return "gpt-4o";
|
|
70
|
+
case "anthropic":
|
|
71
|
+
return "claude-3-5-sonnet-20241022";
|
|
72
|
+
case "google":
|
|
73
|
+
return "gemini-1.5-pro";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function getModelConfig(config: Config) {
|
|
78
|
+
const { aiProvider, aiModel } = config;
|
|
79
|
+
|
|
80
|
+
switch (aiProvider) {
|
|
81
|
+
case "openai":
|
|
82
|
+
return {
|
|
83
|
+
provider: "openai" as const,
|
|
84
|
+
model: aiModel,
|
|
85
|
+
};
|
|
86
|
+
case "anthropic":
|
|
87
|
+
return {
|
|
88
|
+
provider: "anthropic" as const,
|
|
89
|
+
model: aiModel,
|
|
90
|
+
};
|
|
91
|
+
case "google":
|
|
92
|
+
return {
|
|
93
|
+
provider: "google" as const,
|
|
94
|
+
model: aiModel,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { loadConfig } from "./config.js";
|
|
3
|
+
import { initSandboxFromConfig } from "./sandbox/bashlet.js";
|
|
4
|
+
import { createMastraInstance, buildIncidentPrompt, getDebuggerAgent } from "./mastra/index.js";
|
|
5
|
+
import { runTUI } from "./tui/app.jsx";
|
|
6
|
+
import { startWebhookServer } from "./server/webhook.js";
|
|
7
|
+
import {
|
|
8
|
+
loadStoredConfig,
|
|
9
|
+
saveStoredConfig,
|
|
10
|
+
getConfigPath,
|
|
11
|
+
maskApiKey,
|
|
12
|
+
type StoredConfig,
|
|
13
|
+
} from "./cli/config.js";
|
|
14
|
+
import type { AIProvider } from "./config.js";
|
|
15
|
+
|
|
16
|
+
interface CliArgs {
|
|
17
|
+
command: "run" | "config";
|
|
18
|
+
configAction?: "set" | "get" | "list" | "path";
|
|
19
|
+
configKey?: string;
|
|
20
|
+
configValue?: string;
|
|
21
|
+
webhookOnly: boolean;
|
|
22
|
+
incident: string | null;
|
|
23
|
+
help: boolean;
|
|
24
|
+
host: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseArgs(): CliArgs {
|
|
28
|
+
const args = process.argv.slice(2);
|
|
29
|
+
const result: CliArgs = {
|
|
30
|
+
command: "run",
|
|
31
|
+
webhookOnly: false,
|
|
32
|
+
incident: null,
|
|
33
|
+
help: false,
|
|
34
|
+
host: false,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Check for config subcommand
|
|
38
|
+
if (args[0] === "config") {
|
|
39
|
+
result.command = "config";
|
|
40
|
+
result.configAction = args[1] as "set" | "get" | "list" | "path";
|
|
41
|
+
result.configKey = args[2];
|
|
42
|
+
result.configValue = args[3];
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < args.length; i++) {
|
|
47
|
+
const arg = args[i];
|
|
48
|
+
|
|
49
|
+
if (arg === "--webhook-only" || arg === "-w") {
|
|
50
|
+
result.webhookOnly = true;
|
|
51
|
+
} else if (arg === "--incident" || arg === "-i") {
|
|
52
|
+
result.incident = args[++i] || null;
|
|
53
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
54
|
+
result.help = true;
|
|
55
|
+
} else if (arg === "--host") {
|
|
56
|
+
result.host = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function printHelp(): void {
|
|
64
|
+
console.log(`
|
|
65
|
+
🚨 TRIAGENT - AI Kubernetes Debugging Agent
|
|
66
|
+
|
|
67
|
+
USAGE:
|
|
68
|
+
triagent [OPTIONS]
|
|
69
|
+
triagent config <action> [key] [value]
|
|
70
|
+
|
|
71
|
+
OPTIONS:
|
|
72
|
+
-h, --help Show this help message
|
|
73
|
+
-w, --webhook-only Run only the webhook server (no TUI)
|
|
74
|
+
-i, --incident Direct incident input (runs once and exits)
|
|
75
|
+
--host Run commands on host machine (no sandbox)
|
|
76
|
+
|
|
77
|
+
CONFIG COMMANDS:
|
|
78
|
+
triagent config set <key> <value> Set a configuration value
|
|
79
|
+
triagent config get <key> Get a configuration value
|
|
80
|
+
triagent config list List all configuration values
|
|
81
|
+
triagent config path Show config file path
|
|
82
|
+
|
|
83
|
+
CONFIG KEYS:
|
|
84
|
+
aiProvider - AI provider (openai, anthropic, google)
|
|
85
|
+
aiModel - Model ID (e.g., gpt-4o, claude-sonnet-4-20250514)
|
|
86
|
+
apiKey - API key for the provider
|
|
87
|
+
webhookPort - Webhook server port (default: 3000)
|
|
88
|
+
codebasePath - Path to codebase (default: ./)
|
|
89
|
+
kubeConfigPath - Kubernetes config path (default: ~/.kube)
|
|
90
|
+
|
|
91
|
+
MODES:
|
|
92
|
+
Interactive (default):
|
|
93
|
+
Run with no arguments to start the interactive TUI.
|
|
94
|
+
Enter incident descriptions and see real-time debugging output.
|
|
95
|
+
|
|
96
|
+
Webhook Server:
|
|
97
|
+
Use --webhook-only to start an HTTP server that accepts
|
|
98
|
+
incident webhooks from alerting systems.
|
|
99
|
+
|
|
100
|
+
Endpoints:
|
|
101
|
+
POST /webhook/incident - Submit an incident
|
|
102
|
+
GET /investigations/:id - Get investigation results
|
|
103
|
+
GET /health - Health check
|
|
104
|
+
|
|
105
|
+
Direct Input:
|
|
106
|
+
Use --incident "description" for one-shot debugging.
|
|
107
|
+
Example: triagent --incident "checkout pods crashing"
|
|
108
|
+
|
|
109
|
+
ENVIRONMENT VARIABLES:
|
|
110
|
+
AI_PROVIDER - AI provider (openai, anthropic, google)
|
|
111
|
+
AI_MODEL - Model ID (e.g., gpt-4o, claude-3-5-sonnet)
|
|
112
|
+
OPENAI_API_KEY - OpenAI API key
|
|
113
|
+
ANTHROPIC_API_KEY - Anthropic API key
|
|
114
|
+
GOOGLE_GENERATIVE_AI_API_KEY - Google AI API key
|
|
115
|
+
WEBHOOK_PORT - Webhook server port (default: 3000)
|
|
116
|
+
CODEBASE_PATH - Path to codebase (default: ./)
|
|
117
|
+
KUBE_CONFIG_PATH - Kubernetes config path (default: ~/.kube)
|
|
118
|
+
|
|
119
|
+
EXAMPLES:
|
|
120
|
+
# Interactive TUI mode
|
|
121
|
+
triagent
|
|
122
|
+
|
|
123
|
+
# Webhook server mode
|
|
124
|
+
triagent --webhook-only
|
|
125
|
+
|
|
126
|
+
# Direct incident investigation
|
|
127
|
+
triagent -i "API gateway returning 503 errors"
|
|
128
|
+
|
|
129
|
+
# Submit via curl (webhook mode)
|
|
130
|
+
curl -X POST http://localhost:3000/webhook/incident \\
|
|
131
|
+
-H "Content-Type: application/json" \\
|
|
132
|
+
-d '{"title": "API Error", "description": "checkout not working", "severity": "critical"}'
|
|
133
|
+
`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function runDirectIncident(description: string): Promise<void> {
|
|
137
|
+
console.log("🚨 TRIAGENT - Direct Investigation Mode\n");
|
|
138
|
+
console.log(`Incident: ${description}\n`);
|
|
139
|
+
console.log("Starting investigation...\n");
|
|
140
|
+
console.log("─".repeat(60) + "\n");
|
|
141
|
+
|
|
142
|
+
const agent = getDebuggerAgent();
|
|
143
|
+
const prompt = buildIncidentPrompt({
|
|
144
|
+
title: "Direct Investigation",
|
|
145
|
+
description,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const stream = await agent.stream(prompt, {
|
|
150
|
+
maxSteps: 20,
|
|
151
|
+
onStepFinish: ({ toolCalls }) => {
|
|
152
|
+
if (toolCalls && toolCalls.length > 0) {
|
|
153
|
+
const toolCall = toolCalls[0];
|
|
154
|
+
const toolName = "toolName" in toolCall ? toolCall.toolName : "tool";
|
|
155
|
+
console.log(`\n[Tool: ${toolName}]\n`);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
for await (const chunk of stream.textStream) {
|
|
161
|
+
process.stdout.write(chunk);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log("\n\n" + "─".repeat(60));
|
|
165
|
+
console.log("✅ Investigation complete");
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error("\n❌ Investigation failed:", error);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function handleConfigCommand(args: CliArgs): Promise<void> {
|
|
173
|
+
const validKeys: (keyof StoredConfig)[] = [
|
|
174
|
+
"aiProvider",
|
|
175
|
+
"aiModel",
|
|
176
|
+
"apiKey",
|
|
177
|
+
"webhookPort",
|
|
178
|
+
"codebasePath",
|
|
179
|
+
"kubeConfigPath",
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
switch (args.configAction) {
|
|
183
|
+
case "set": {
|
|
184
|
+
if (!args.configKey || args.configValue === undefined) {
|
|
185
|
+
console.error("Usage: triagent config set <key> <value>");
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
if (!validKeys.includes(args.configKey as keyof StoredConfig)) {
|
|
189
|
+
console.error(`Invalid key: ${args.configKey}`);
|
|
190
|
+
console.error(`Valid keys: ${validKeys.join(", ")}`);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
const config = await loadStoredConfig();
|
|
194
|
+
let value: string | number = args.configValue;
|
|
195
|
+
if (args.configKey === "webhookPort") {
|
|
196
|
+
value = parseInt(args.configValue, 10);
|
|
197
|
+
}
|
|
198
|
+
(config as Record<string, string | number>)[args.configKey] = value;
|
|
199
|
+
await saveStoredConfig(config);
|
|
200
|
+
console.log(`✅ Set ${args.configKey}`);
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case "get": {
|
|
204
|
+
if (!args.configKey) {
|
|
205
|
+
console.error("Usage: triagent config get <key>");
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
const config = await loadStoredConfig();
|
|
209
|
+
const value = config[args.configKey as keyof StoredConfig];
|
|
210
|
+
if (value === undefined) {
|
|
211
|
+
console.log(`${args.configKey}: (not set)`);
|
|
212
|
+
} else if (args.configKey === "apiKey") {
|
|
213
|
+
console.log(`${args.configKey}: ${maskApiKey(String(value))}`);
|
|
214
|
+
} else {
|
|
215
|
+
console.log(`${args.configKey}: ${value}`);
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case "list": {
|
|
220
|
+
const config = await loadStoredConfig();
|
|
221
|
+
console.log("Current configuration:\n");
|
|
222
|
+
for (const key of validKeys) {
|
|
223
|
+
const value = config[key];
|
|
224
|
+
if (value === undefined) {
|
|
225
|
+
console.log(` ${key}: (not set)`);
|
|
226
|
+
} else if (key === "apiKey") {
|
|
227
|
+
console.log(` ${key}: ${maskApiKey(String(value))}`);
|
|
228
|
+
} else {
|
|
229
|
+
console.log(` ${key}: ${value}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
case "path": {
|
|
235
|
+
const path = await getConfigPath();
|
|
236
|
+
console.log(path);
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
default:
|
|
240
|
+
console.error("Usage: triagent config <set|get|list|path> [key] [value]");
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function main(): Promise<void> {
|
|
246
|
+
const args = parseArgs();
|
|
247
|
+
|
|
248
|
+
if (args.help) {
|
|
249
|
+
printHelp();
|
|
250
|
+
process.exit(0);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Handle config command
|
|
254
|
+
if (args.command === "config") {
|
|
255
|
+
await handleConfigCommand(args);
|
|
256
|
+
process.exit(0);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Load configuration
|
|
260
|
+
let config;
|
|
261
|
+
try {
|
|
262
|
+
config = await loadConfig();
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error("❌ Configuration error:", error);
|
|
265
|
+
console.error("\nRun 'triagent config set apiKey <your-key>' to configure.");
|
|
266
|
+
console.error("Or set environment variables (see --help for details).");
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Initialize sandbox and Mastra
|
|
271
|
+
try {
|
|
272
|
+
initSandboxFromConfig(config, args.host);
|
|
273
|
+
createMastraInstance(config);
|
|
274
|
+
if (args.host) {
|
|
275
|
+
console.log("⚠️ Running in host mode (no sandbox)\n");
|
|
276
|
+
}
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.error("❌ Initialization error:", error);
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Run in appropriate mode
|
|
283
|
+
if (args.webhookOnly) {
|
|
284
|
+
// Webhook server mode
|
|
285
|
+
await startWebhookServer(config.webhookPort);
|
|
286
|
+
} else if (args.incident) {
|
|
287
|
+
// Direct incident mode
|
|
288
|
+
await runDirectIncident(args.incident);
|
|
289
|
+
} else {
|
|
290
|
+
// Interactive TUI mode
|
|
291
|
+
console.log("Starting Triagent TUI...\n");
|
|
292
|
+
const tui = await runTUI();
|
|
293
|
+
|
|
294
|
+
// Handle graceful shutdown
|
|
295
|
+
process.on("SIGINT", () => {
|
|
296
|
+
tui.shutdown();
|
|
297
|
+
process.exit(0);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
process.on("SIGTERM", () => {
|
|
301
|
+
tui.shutdown();
|
|
302
|
+
process.exit(0);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
main().catch((error) => {
|
|
308
|
+
console.error("Fatal error:", error);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
});
|