@johnny-joster/n9e-plugin 1.0.7 → 1.0.9

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/index.ts CHANGED
@@ -1,19 +1,19 @@
1
1
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
2
+ import { createN9eClient } from "./src/n9e-client.js";
3
+ import { createActiveAlertsTool } from "./src/tools/active-alerts.js";
4
+ import { createHistoricalAlertsTool } from "./src/tools/historical-alerts.js";
5
+ import { createAlertRulesTool } from "./src/tools/alert-rules.js";
6
+ import { createAlertMutesTool } from "./src/tools/alert-mutes.js";
2
7
 
3
- export default function(api: OpenClawPluginApi) {
4
- api.logger.info("=== N9e plugin: Entry point called ===");
8
+ export default function register(api: OpenClawPluginApi) {
9
+ api.logger.info("N9e plugin: initializing", { id: api.id });
5
10
 
6
- try {
7
- api.logger.info("=== N9e plugin: Starting initialization ===");
11
+ const client = createN9eClient(api.config);
8
12
 
9
- // 不注册任何工具,只是测试插件能否加载
10
- api.logger.info("=== N9e plugin: Skipping tool registration (test mode) ===");
13
+ api.registerTool(createActiveAlertsTool(client));
14
+ api.registerTool(createHistoricalAlertsTool(client));
15
+ api.registerTool(createAlertRulesTool(client));
16
+ api.registerTool(createAlertMutesTool(client));
11
17
 
12
- api.logger.info("=== N9e plugin: Initialization completed successfully ===");
13
- } catch (error) {
14
- api.logger.error("=== N9e plugin: Failed to load ===", error);
15
- throw error;
16
- }
17
-
18
- api.logger.info("=== N9e plugin: Function exit ===");
18
+ api.logger.info("N9e plugin: registered 4 tools");
19
19
  }
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "id": "n9e-plugin",
3
+ "name": "Nightingale Alert Plugin",
4
+ "version": "1.0.0",
5
+ "description": "夜莺告警平台查询插件,支持活跃告警、历史告警、告警规则、告警屏蔽查询",
3
6
  "configSchema": {
4
7
  "type": "object",
8
+ "additionalProperties": false,
5
9
  "properties": {
6
10
  "apiBaseUrl": {
7
11
  "type": "string",
@@ -18,6 +22,20 @@
18
22
  "default": 30000,
19
23
  "description": "请求超时时间(毫秒)"
20
24
  }
25
+ },
26
+ "required": ["apiBaseUrl", "apiKey"]
27
+ },
28
+ "uiHints": {
29
+ "apiBaseUrl": {
30
+ "label": "API 地址",
31
+ "placeholder": "http://n9e.example.com"
32
+ },
33
+ "apiKey": {
34
+ "label": "API Key",
35
+ "sensitive": true
36
+ },
37
+ "timeout": {
38
+ "label": "请求超时 (ms)"
21
39
  }
22
40
  }
23
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnny-joster/n9e-plugin",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Nightingale alert platform query plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -14,6 +14,9 @@
14
14
  "keywords": ["openclaw", "plugin", "nightingale", "n9e", "alerts"],
15
15
  "author": "Youdao",
16
16
  "license": "MIT",
17
+ "dependencies": {
18
+ "@sinclair/typebox": "^0.32.0"
19
+ },
17
20
  "devDependencies": {
18
21
  "@types/node": "^20.11.0",
19
22
  "typescript": "^5.3.3"
@@ -0,0 +1,105 @@
1
+ import type { PluginConfig } from "openclaw/plugin-sdk";
2
+ import type { AlertCurEvent, AlertHisEvent, AlertRule, AlertMute, ListResponse } from "./types.js";
3
+
4
+ interface N9eConfig {
5
+ apiBaseUrl: string;
6
+ apiKey: string;
7
+ timeout?: number;
8
+ }
9
+
10
+ export interface ActiveAlertsParams {
11
+ severity?: number;
12
+ query?: string;
13
+ hours?: number;
14
+ limit?: number;
15
+ }
16
+
17
+ export interface HistoricalAlertsParams {
18
+ severity?: number;
19
+ query?: string;
20
+ hours?: number;
21
+ is_recovered?: number;
22
+ limit?: number;
23
+ }
24
+
25
+ export interface AlertRulesParams {
26
+ query?: string;
27
+ group_id?: number;
28
+ disabled?: number;
29
+ limit?: number;
30
+ }
31
+
32
+ export interface AlertMutesParams {
33
+ group_id: number;
34
+ query?: string;
35
+ }
36
+
37
+ export function createN9eClient(config: PluginConfig) {
38
+ const { apiBaseUrl, apiKey, timeout = 30000 } = config as unknown as N9eConfig;
39
+
40
+ async function request<T>(path: string, params: Record<string, unknown> = {}): Promise<T> {
41
+ const url = new URL(`${apiBaseUrl}${path}`);
42
+ for (const [key, value] of Object.entries(params)) {
43
+ if (value !== undefined && value !== null) {
44
+ url.searchParams.set(key, String(value));
45
+ }
46
+ }
47
+
48
+ const controller = new AbortController();
49
+ const timer = setTimeout(() => controller.abort(), timeout);
50
+
51
+ try {
52
+ const response = await fetch(url.toString(), {
53
+ headers: { "X-API-Key": apiKey },
54
+ signal: controller.signal,
55
+ });
56
+
57
+ if (!response.ok) {
58
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
59
+ }
60
+
61
+ return response.json() as Promise<T>;
62
+ } finally {
63
+ clearTimeout(timer);
64
+ }
65
+ }
66
+
67
+ return {
68
+ async getActiveAlerts(params: ActiveAlertsParams): Promise<ListResponse<AlertCurEvent>> {
69
+ return request<ListResponse<AlertCurEvent>>("/api/n9e/alert-cur-events/list", {
70
+ severity: params.severity,
71
+ query: params.query,
72
+ hours: params.hours,
73
+ limit: params.limit ?? 20,
74
+ });
75
+ },
76
+
77
+ async getHistoricalAlerts(params: HistoricalAlertsParams): Promise<ListResponse<AlertHisEvent>> {
78
+ return request<ListResponse<AlertHisEvent>>("/api/n9e/alert-his-events/list", {
79
+ severity: params.severity,
80
+ query: params.query,
81
+ hours: params.hours,
82
+ is_recovered: params.is_recovered,
83
+ limit: params.limit ?? 20,
84
+ });
85
+ },
86
+
87
+ async getAlertRules(params: AlertRulesParams): Promise<ListResponse<AlertRule>> {
88
+ return request<ListResponse<AlertRule>>("/api/n9e/alert-rules", {
89
+ query: params.query,
90
+ group_id: params.group_id,
91
+ disabled: params.disabled,
92
+ limit: params.limit ?? 20,
93
+ });
94
+ },
95
+
96
+ async getAlertMutes(params: AlertMutesParams): Promise<AlertMute[]> {
97
+ return request<AlertMute[]>(
98
+ `/api/n9e/busi-group/${params.group_id}/alert-mutes/search-by-query`,
99
+ { query: params.query }
100
+ );
101
+ },
102
+ };
103
+ }
104
+
105
+ export type N9eClient = ReturnType<typeof createN9eClient>;
@@ -1,47 +1,58 @@
1
- // Type declarations for openclaw/plugin-sdk
2
- declare module "openclaw/plugin-sdk" {
3
- export interface ToolHandler {
4
- (input: any, context: ToolContext): Promise<ToolResult>;
5
- }
6
-
7
- export interface ToolContext {
8
- config: {
9
- plugins?: {
10
- entries?: {
11
- [key: string]: {
12
- config?: any;
13
- };
14
- };
15
- };
16
- };
17
- logger: {
18
- info: (...args: any[]) => void;
19
- error: (...args: any[]) => void;
20
- warn: (...args: any[]) => void;
21
- debug: (...args: any[]) => void;
22
- };
23
- }
24
-
25
- export interface ToolResult {
26
- content: Array<{
27
- type: string;
28
- text: string;
29
- }>;
30
- isError?: boolean;
31
- }
32
-
33
- export interface OpenClawPluginApi {
34
- registerTool: (tool: {
35
- name: string;
36
- description: string;
37
- inputSchema: any;
38
- handler: ToolHandler;
39
- }) => void;
40
- logger: {
41
- info: (...args: any[]) => void;
42
- error: (...args: any[]) => void;
43
- warn: (...args: any[]) => void;
44
- debug: (...args: any[]) => void;
45
- };
46
- }
1
+ import type { TSchema } from "@sinclair/typebox";
2
+
3
+ export interface ToolResult {
4
+ type: "text" | "json" | "error" | "image";
5
+ content: string;
6
+ data?: Record<string, unknown>;
7
+ done?: boolean;
8
+ }
9
+
10
+ export interface ToolContext {
11
+ sessionKey: string;
12
+ channel: string;
13
+ senderId: string;
14
+ config: Record<string, unknown>;
15
+ deps: Record<string, unknown>;
16
+ }
17
+
18
+ export interface ToolExecuteParams {
19
+ args: Record<string, unknown>;
20
+ context: ToolContext;
21
+ }
22
+
23
+ export type ToolExecuteFunction = (
24
+ params: ToolExecuteParams
25
+ ) => Promise<ToolResult> | AsyncIterable<ToolResult>;
26
+
27
+ export interface AgentTool {
28
+ name: string;
29
+ description: string;
30
+ parameters: TSchema;
31
+ execute: ToolExecuteFunction;
32
+ }
33
+
34
+ export interface PluginConfig {
35
+ [key: string]: unknown;
36
+ }
37
+
38
+ export interface Logger {
39
+ info: (...args: unknown[]) => void;
40
+ error: (...args: unknown[]) => void;
41
+ warn: (...args: unknown[]) => void;
42
+ debug: (...args: unknown[]) => void;
43
+ }
44
+
45
+ export interface OpenClawPluginApi {
46
+ id: string;
47
+ config: PluginConfig;
48
+ logger: Logger;
49
+ runtime: Record<string, unknown>;
50
+ registerTool(tool: AgentTool): void;
51
+ registerChannel(options: { plugin: unknown }): void;
52
+ registerGatewayMethod(method: string, handler: unknown): void;
53
+ registerHttpRoute(path: string, handler: unknown): void;
54
+ registerCli(registerFn: unknown, metadata: unknown): void;
55
+ registerService(service: unknown): void;
56
+ registerCommand(command: unknown): void;
57
+ registerProvider(provider: unknown): void;
47
58
  }
@@ -0,0 +1,46 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import type { AgentTool } from "openclaw/plugin-sdk";
3
+ import type { N9eClient } from "../n9e-client.js";
4
+
5
+ export function createActiveAlertsTool(client: N9eClient): AgentTool {
6
+ return {
7
+ name: "n9e_query_active_alerts",
8
+ description: `查询夜莺平台当前活跃的告警事件。
9
+ 当用户询问当前有哪些告警、活跃告警状态、正在触发的告警时使用。
10
+ 返回告警列表,包含规则名称、告警级别、触发时间、触发值等信息。`,
11
+ parameters: Type.Object({
12
+ severity: Type.Optional(Type.Number({
13
+ description: "告警级别筛选:1-紧急 2-警告 3-通知,不传则返回全部",
14
+ })),
15
+ query: Type.Optional(Type.String({
16
+ description: "关键词搜索,支持规则名称、标签模糊搜索",
17
+ })),
18
+ hours: Type.Optional(Type.Number({
19
+ description: "查询最近N小时的告警,不传则返回所有活跃告警",
20
+ })),
21
+ limit: Type.Optional(Type.Number({
22
+ description: "返回数量限制,默认20",
23
+ default: 20,
24
+ })),
25
+ }),
26
+ execute: async ({ args }) => {
27
+ try {
28
+ const data = await client.getActiveAlerts({
29
+ severity: args.severity as number | undefined,
30
+ query: args.query as string | undefined,
31
+ hours: args.hours as number | undefined,
32
+ limit: args.limit as number | undefined,
33
+ });
34
+ return {
35
+ type: "json",
36
+ content: JSON.stringify(data, null, 2),
37
+ };
38
+ } catch (error) {
39
+ return {
40
+ type: "error",
41
+ content: `查询活跃告警失败: ${error instanceof Error ? error.message : "未知错误"}`,
42
+ };
43
+ }
44
+ },
45
+ };
46
+ }
@@ -0,0 +1,37 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import type { AgentTool } from "openclaw/plugin-sdk";
3
+ import type { N9eClient } from "../n9e-client.js";
4
+
5
+ export function createAlertMutesTool(client: N9eClient): AgentTool {
6
+ return {
7
+ name: "n9e_query_alert_mutes",
8
+ description: `查询夜莺平台的告警屏蔽规则。
9
+ 当用户询问告警屏蔽、屏蔽规则、哪些告警被静默时使用。
10
+ 返回屏蔽规则列表,包含屏蔽原因、生效时间范围等信息。`,
11
+ parameters: Type.Object({
12
+ group_id: Type.Number({
13
+ description: "业务组ID(必填)",
14
+ }),
15
+ query: Type.Optional(Type.String({
16
+ description: "搜索关键字",
17
+ })),
18
+ }),
19
+ execute: async ({ args }) => {
20
+ try {
21
+ const data = await client.getAlertMutes({
22
+ group_id: args.group_id as number,
23
+ query: args.query as string | undefined,
24
+ });
25
+ return {
26
+ type: "json",
27
+ content: JSON.stringify(data, null, 2),
28
+ };
29
+ } catch (error) {
30
+ return {
31
+ type: "error",
32
+ content: `查询告警屏蔽规则失败: ${error instanceof Error ? error.message : "未知错误"}`,
33
+ };
34
+ }
35
+ },
36
+ };
37
+ }
@@ -0,0 +1,46 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import type { AgentTool } from "openclaw/plugin-sdk";
3
+ import type { N9eClient } from "../n9e-client.js";
4
+
5
+ export function createAlertRulesTool(client: N9eClient): AgentTool {
6
+ return {
7
+ name: "n9e_query_alert_rules",
8
+ description: `查询夜莺平台的告警规则配置。
9
+ 当用户询问告警规则、规则配置、哪些规则在生效时使用。
10
+ 返回规则列表,包含规则名称、PromQL、告警级别、生效时间等配置信息。`,
11
+ parameters: Type.Object({
12
+ query: Type.Optional(Type.String({
13
+ description: "搜索关键字,支持规则名称模糊搜索",
14
+ })),
15
+ group_id: Type.Optional(Type.Number({
16
+ description: "业务组ID,不传则查询全部业务组",
17
+ })),
18
+ disabled: Type.Optional(Type.Number({
19
+ description: "启用状态:0-启用 1-禁用 -1-全部(默认)",
20
+ })),
21
+ limit: Type.Optional(Type.Number({
22
+ description: "返回数量限制,默认20",
23
+ default: 20,
24
+ })),
25
+ }),
26
+ execute: async ({ args }) => {
27
+ try {
28
+ const data = await client.getAlertRules({
29
+ query: args.query as string | undefined,
30
+ group_id: args.group_id as number | undefined,
31
+ disabled: args.disabled as number | undefined,
32
+ limit: args.limit as number | undefined,
33
+ });
34
+ return {
35
+ type: "json",
36
+ content: JSON.stringify(data, null, 2),
37
+ };
38
+ } catch (error) {
39
+ return {
40
+ type: "error",
41
+ content: `查询告警规则失败: ${error instanceof Error ? error.message : "未知错误"}`,
42
+ };
43
+ }
44
+ },
45
+ };
46
+ }
@@ -0,0 +1,50 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import type { AgentTool } from "openclaw/plugin-sdk";
3
+ import type { N9eClient } from "../n9e-client.js";
4
+
5
+ export function createHistoricalAlertsTool(client: N9eClient): AgentTool {
6
+ return {
7
+ name: "n9e_query_historical_alerts",
8
+ description: `查询夜莺平台历史告警事件记录。
9
+ 当用户询问历史告警、过去发生的告警、已恢复的告警时使用。
10
+ 返回历史告警列表,包含规则名称、告警级别、触发时间、恢复时间等信息。`,
11
+ parameters: Type.Object({
12
+ severity: Type.Optional(Type.Number({
13
+ description: "告警级别筛选:1-紧急 2-警告 3-通知,不传则返回全部",
14
+ })),
15
+ query: Type.Optional(Type.String({
16
+ description: "关键词搜索,支持规则名称、标签模糊搜索",
17
+ })),
18
+ hours: Type.Optional(Type.Number({
19
+ description: "查询最近N小时的历史告警",
20
+ })),
21
+ is_recovered: Type.Optional(Type.Number({
22
+ description: "恢复状态:0-未恢复 1-已恢复 -1-全部(默认)",
23
+ })),
24
+ limit: Type.Optional(Type.Number({
25
+ description: "返回数量限制,默认20",
26
+ default: 20,
27
+ })),
28
+ }),
29
+ execute: async ({ args }) => {
30
+ try {
31
+ const data = await client.getHistoricalAlerts({
32
+ severity: args.severity as number | undefined,
33
+ query: args.query as string | undefined,
34
+ hours: args.hours as number | undefined,
35
+ is_recovered: args.is_recovered as number | undefined,
36
+ limit: args.limit as number | undefined,
37
+ });
38
+ return {
39
+ type: "json",
40
+ content: JSON.stringify(data, null, 2),
41
+ };
42
+ } catch (error) {
43
+ return {
44
+ type: "error",
45
+ content: `查询历史告警失败: ${error instanceof Error ? error.message : "未知错误"}`,
46
+ };
47
+ }
48
+ },
49
+ };
50
+ }
package/src/types.ts ADDED
@@ -0,0 +1,92 @@
1
+ // 夜莺平台数据类型定义
2
+
3
+ export interface AlertCurEvent {
4
+ id: number;
5
+ hash: string;
6
+ rule_id: number;
7
+ rule_name: string;
8
+ rule_note: string;
9
+ severity: number;
10
+ cate: string;
11
+ cluster: string;
12
+ datasource_id: number;
13
+ group_id: number;
14
+ group_name: string;
15
+ target_ident: string;
16
+ target_note: string;
17
+ trigger_time: number;
18
+ trigger_value: string;
19
+ first_trigger_time: number;
20
+ is_recovered: boolean;
21
+ tags: string[];
22
+ annotations: Record<string, string>;
23
+ runbook_url: string;
24
+ }
25
+
26
+ export interface AlertHisEvent {
27
+ id: number;
28
+ hash: string;
29
+ rule_id: number;
30
+ rule_name: string;
31
+ rule_note: string;
32
+ severity: number;
33
+ cate: string;
34
+ cluster: string;
35
+ datasource_id: number;
36
+ group_id: number;
37
+ group_name: string;
38
+ target_ident: string;
39
+ target_note: string;
40
+ trigger_time: number;
41
+ trigger_value: string;
42
+ first_trigger_time: number;
43
+ recover_time: number;
44
+ is_recovered: number;
45
+ tags: string[];
46
+ annotations: Record<string, string>;
47
+ runbook_url: string;
48
+ }
49
+
50
+ export interface AlertRule {
51
+ id: number;
52
+ name: string;
53
+ note: string;
54
+ group_id: number;
55
+ prod: string;
56
+ cate: string;
57
+ severity: number;
58
+ disabled: number;
59
+ prom_ql: string;
60
+ prom_eval_interval: number;
61
+ prom_for_duration: number;
62
+ enable_stime: string;
63
+ enable_etime: string;
64
+ runbook_url: string;
65
+ create_at: number;
66
+ create_by: string;
67
+ update_at: number;
68
+ update_by: string;
69
+ }
70
+
71
+ export interface AlertMute {
72
+ id: number;
73
+ group_id: number;
74
+ prod: string;
75
+ cate: string;
76
+ cluster: string;
77
+ note: string;
78
+ cause: string;
79
+ btime: number;
80
+ etime: number;
81
+ disabled: number;
82
+ mute_time_type: number;
83
+ create_at: number;
84
+ create_by: string;
85
+ update_at: number;
86
+ update_by: string;
87
+ }
88
+
89
+ export interface ListResponse<T> {
90
+ list: T[];
91
+ total: number;
92
+ }
package/tsconfig.json CHANGED
@@ -3,6 +3,9 @@
3
3
  "target": "ES2022",
4
4
  "module": "ESNext",
5
5
  "moduleResolution": "bundler",
6
+ "paths": {
7
+ "openclaw/plugin-sdk": ["./src/openclaw-plugin-sdk.d.ts"]
8
+ },
6
9
  "esModuleInterop": true,
7
10
  "strict": true,
8
11
  "skipLibCheck": true,
@@ -1,11 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(npx tsc:*)",
5
- "Bash(npm install)",
6
- "Bash(export KUBECONFIG=/Users/yaobinze/workarea/youdao/k8s/k8s-prod-maintain1)",
7
- "Bash(kubectl get:*)",
8
- "Bash(kubectl logs:*)"
9
- ]
10
- }
11
- }