kubeagent 0.1.2 → 0.1.4

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/dist/auth.js CHANGED
@@ -152,9 +152,16 @@ export async function createApiKey(auth, name) {
152
152
  return data;
153
153
  }
154
154
  export async function showAccount(auth) {
155
- const res = await fetch(`${auth.serverUrl}/v1/balance`, {
156
- headers: { Authorization: `ApiKey ${auth.apiKey ?? auth.token}` },
157
- });
155
+ const url = `${auth.serverUrl}/v1/balance`;
156
+ let res;
157
+ try {
158
+ res = await fetch(url, {
159
+ headers: { Authorization: `ApiKey ${auth.apiKey ?? auth.token}` },
160
+ });
161
+ }
162
+ catch (err) {
163
+ throw new Error(`Could not reach server at ${url} — is it running?\n (${err.message})`);
164
+ }
158
165
  if (!res.ok) {
159
166
  throw new Error(`Failed to fetch account info (${res.status})`);
160
167
  }
package/dist/cli.js CHANGED
@@ -108,12 +108,20 @@ program
108
108
  if (serverSlack.connected && serverSlack.webhookUrl) {
109
109
  const alreadyConfigured = config.notifications.channels.some((ch) => ch.type === "slack" && ch.webhook_url === serverSlack.webhookUrl);
110
110
  if (!alreadyConfigured) {
111
- config.notifications.channels.unshift({
111
+ const dashboardChannel = {
112
112
  type: "slack",
113
113
  webhook_url: serverSlack.webhookUrl,
114
114
  label: serverSlack.teamName ?? serverSlack.channelName ?? "KubeAgent",
115
115
  severity: "warning",
116
- });
116
+ };
117
+ config.notifications.channels.unshift(dashboardChannel);
118
+ // Persist to local config so it survives restarts and shows in `notify list`
119
+ const savedConfig = loadConfig();
120
+ const alreadySaved = savedConfig.notifications.channels.some((ch) => ch.type === "slack" && ch.webhook_url === serverSlack.webhookUrl);
121
+ if (!alreadySaved) {
122
+ savedConfig.notifications.channels.unshift(dashboardChannel);
123
+ saveConfig(savedConfig);
124
+ }
117
125
  console.log(chalk.dim(` Slack: ${serverSlack.teamName ?? ""}${serverSlack.channelName ? ` ${serverSlack.channelName}` : ""} (from dashboard)\n`));
118
126
  }
119
127
  }
@@ -1,7 +1,7 @@
1
1
  import readline from "node:readline";
2
2
  import chalk from "chalk";
3
3
  import { sendSlack } from "./slack.js";
4
- import { testTelegramCredentials } from "./telegram.js";
4
+ import { testTelegramCredentials, autoDetectTelegramChatId } from "./telegram.js";
5
5
  async function ask(question, hint) {
6
6
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
7
7
  const hintStr = hint ? chalk.dim(` (${hint})`) : "";
@@ -43,16 +43,28 @@ export async function setupSlack() {
43
43
  }
44
44
  export async function setupTelegram() {
45
45
  console.log(chalk.bold("\n Telegram Setup"));
46
- console.log(chalk.dim(" 1. Create a bot via @BotFather and copy the token"));
47
- console.log(chalk.dim(" 2. Start a chat with your bot (or add it to a group)"));
48
- console.log(chalk.dim(" 3. Get your chat ID: message the bot, then visit:"));
49
- console.log(chalk.dim(" https://api.telegram.org/bot<TOKEN>/getUpdates\n"));
46
+ console.log(chalk.dim(" 1. Create a bot via @BotFather and copy its token"));
47
+ console.log(chalk.dim(" 2. Start a chat with your bot, or add it to a group/channel\n"));
50
48
  const bot_token = await ask("Bot token");
51
49
  if (!bot_token)
52
50
  return null;
53
- const chat_id = await ask("Chat ID");
54
- if (!chat_id)
55
- return null;
51
+ // Auto-detect chat ID
52
+ console.log(chalk.dim("\n Now send any message to your bot (or the group/channel where it was added)."));
53
+ process.stdout.write(chalk.cyan(" Waiting for a message (30s)..."));
54
+ const detected = await autoDetectTelegramChatId(bot_token);
55
+ let chat_id;
56
+ if (detected) {
57
+ console.log(chalk.green(` detected: ${detected}`));
58
+ chat_id = detected;
59
+ }
60
+ else {
61
+ console.log(chalk.yellow(" timed out."));
62
+ console.log(chalk.dim(" Find it manually: send a message to your bot, then visit:"));
63
+ console.log(chalk.dim(` https://api.telegram.org/bot${bot_token}/getUpdates\n`));
64
+ chat_id = await ask("Chat ID");
65
+ if (!chat_id)
66
+ return null;
67
+ }
56
68
  const label = await ask("Label (optional)", "e.g. ops-alerts");
57
69
  const severity = await pickSeverity();
58
70
  // Test credentials
@@ -2,6 +2,7 @@ import type { Issue } from "../monitor/types.js";
2
2
  import type { TelegramChannel } from "../config.js";
3
3
  export declare function sendTelegramQuestion(channel: TelegramChannel, question: string, choices: string[] | undefined, clusterContext?: string): Promise<void>;
4
4
  export declare function sendTelegram(issues: Issue[], channel: TelegramChannel, clusterContext?: string): Promise<void>;
5
+ export declare function autoDetectTelegramChatId(botToken: string): Promise<string | null>;
5
6
  export declare function testTelegramCredentials(botToken: string, chatId: string): Promise<{
6
7
  ok: boolean;
7
8
  error?: string;
@@ -43,6 +43,35 @@ export async function sendTelegramQuestion(channel, question, choices, clusterCo
43
43
  export async function sendTelegram(issues, channel, clusterContext) {
44
44
  await postToTelegram(channel, formatTelegramMessage(issues, clusterContext));
45
45
  }
46
+ export async function autoDetectTelegramChatId(botToken) {
47
+ const url = `${TELEGRAM_API}/bot${botToken}/getUpdates`;
48
+ const deadline = Date.now() + 30_000;
49
+ let offset = 0;
50
+ // Clear any pending updates first so we only see new messages
51
+ try {
52
+ const clearRes = await fetch(`${url}?offset=-1`);
53
+ const clearData = await clearRes.json();
54
+ if (clearData.ok && clearData.result.length > 0) {
55
+ offset = clearData.result[clearData.result.length - 1].update_id + 1;
56
+ }
57
+ }
58
+ catch { /* ignore */ }
59
+ while (Date.now() < deadline) {
60
+ try {
61
+ const res = await fetch(`${url}?offset=${offset}&timeout=5&limit=1`);
62
+ const data = await res.json();
63
+ if (data.ok && data.result.length > 0) {
64
+ const update = data.result[0];
65
+ offset = update.update_id + 1;
66
+ const chat = update.message?.chat ?? update.channel_post?.chat ?? update.my_chat_member?.chat;
67
+ if (chat)
68
+ return String(chat.id);
69
+ }
70
+ }
71
+ catch { /* ignore, keep polling */ }
72
+ }
73
+ return null;
74
+ }
46
75
  export async function testTelegramCredentials(botToken, chatId) {
47
76
  try {
48
77
  const res = await fetch(`${TELEGRAM_API}/bot${botToken}/sendMessage`, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kubeagent",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "AI-powered Kubernetes management CLI",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -1,3 +0,0 @@
1
- import type { Issue } from "../monitor/types.js";
2
- import type { WebhookConfig } from "../config.js";
3
- export declare function sendWebhook(issues: Issue[], config: WebhookConfig): Promise<void>;
@@ -1,49 +0,0 @@
1
- function formatMessage(issues) {
2
- const header = `**KubeAgent Alert** — ${issues.length} issue(s) detected\n`;
3
- const body = issues
4
- .map((i) => {
5
- const icon = i.severity === "critical" ? "\u{1F534}" : "\u{1F7E1}";
6
- return `${icon} **${i.kind}**: ${i.message}`;
7
- })
8
- .join("\n");
9
- return header + body;
10
- }
11
- export async function sendWebhook(issues, config) {
12
- const message = formatMessage(issues);
13
- let payload;
14
- switch (config.type) {
15
- case "discord":
16
- payload = { content: message };
17
- break;
18
- case "slack":
19
- case "mattermost":
20
- default:
21
- payload = { text: message };
22
- break;
23
- }
24
- // Validate URL to prevent SSRF
25
- try {
26
- const url = new URL(config.url);
27
- if (!["https:", "http:"].includes(url.protocol)) {
28
- console.error(`Webhook error: unsupported protocol ${url.protocol}`);
29
- return;
30
- }
31
- }
32
- catch {
33
- console.error(`Webhook error: invalid URL ${config.url}`);
34
- return;
35
- }
36
- try {
37
- const response = await fetch(config.url, {
38
- method: "POST",
39
- headers: { "Content-Type": "application/json" },
40
- body: JSON.stringify(payload),
41
- });
42
- if (!response.ok) {
43
- console.error(`Webhook failed: ${response.status} ${response.statusText}`);
44
- }
45
- }
46
- catch (err) {
47
- console.error(`Webhook error: ${err.message}`);
48
- }
49
- }