@blogic-cz/agent-tools 0.11.0 → 0.12.0
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 +60 -60
- package/package.json +8 -8
- package/schemas/agent-tools.schema.json +12 -12
- package/src/config/index.ts +2 -2
- package/src/config/loader.ts +6 -6
- package/src/config/types.ts +7 -7
- package/src/index.ts +1 -1
- package/src/observability-tool/errors.ts +8 -0
- package/src/{grafana-tool → observability-tool}/index.ts +7 -16
- package/src/observability-tool/metrics.ts +129 -0
- package/src/{grafana-tool → observability-tool}/shared.ts +90 -34
- package/src/observability-tool/trace.ts +379 -0
- package/src/observability-tool/types.ts +119 -0
- package/src/grafana-tool/alerts.ts +0 -159
- package/src/grafana-tool/dashboards.ts +0 -151
- package/src/grafana-tool/datasources.ts +0 -72
- package/src/grafana-tool/errors.ts +0 -8
- package/src/grafana-tool/health.ts +0 -57
- package/src/grafana-tool/logs.ts +0 -124
- package/src/grafana-tool/metrics.ts +0 -217
- package/src/grafana-tool/types.ts +0 -29
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @blogic-cz/agent-tools
|
|
2
2
|
|
|
3
|
-
Safe CLI wrappers for AI coding agents. 8 tools for GitHub,
|
|
3
|
+
Safe CLI wrappers for AI coding agents. 8 tools for GitHub, observability, databases, Kubernetes, Azure DevOps, logs, OpenCode sessions, and audit history — with JSON5 config and a credential guard that blocks agents from touching secrets.
|
|
4
4
|
|
|
5
5
|
## Why
|
|
6
6
|
|
|
@@ -32,19 +32,19 @@ bun add -d @blogic-cz/agent-tools
|
|
|
32
32
|
Install per-tool skills for only the tools your project uses:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
npx skills add blogic-cz/agent-tools --path
|
|
36
|
-
npx skills add blogic-cz/agent-tools --path
|
|
37
|
-
npx skills add blogic-cz/agent-tools --path
|
|
38
|
-
npx skills add blogic-cz/agent-tools --path
|
|
39
|
-
npx skills add blogic-cz/agent-tools --path
|
|
40
|
-
npx skills add blogic-cz/agent-tools --path
|
|
41
|
-
npx skills add blogic-cz/agent-tools --path
|
|
35
|
+
npx skills add blogic-cz/agent-tools --path skills/gh-tool
|
|
36
|
+
npx skills add blogic-cz/agent-tools --path skills/observability-tool
|
|
37
|
+
npx skills add blogic-cz/agent-tools --path skills/db-tool
|
|
38
|
+
npx skills add blogic-cz/agent-tools --path skills/k8s-tool
|
|
39
|
+
npx skills add blogic-cz/agent-tools --path skills/az-tool
|
|
40
|
+
npx skills add blogic-cz/agent-tools --path skills/logs-tool
|
|
41
|
+
npx skills add blogic-cz/agent-tools --path skills/session-tool
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
Or install the all-in-one skill that covers every tool:
|
|
45
45
|
|
|
46
46
|
```bash
|
|
47
|
-
npx skills add blogic-cz/agent-tools --path
|
|
47
|
+
npx skills add blogic-cz/agent-tools --path skills/agent-tools
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
**Requirements:** [Bun](https://bun.sh/) >=1.0.0
|
|
@@ -62,13 +62,13 @@ Follow these steps to set up agent-tools in the consumer project.
|
|
|
62
62
|
- **Non-JavaScript project** (no `package.json`): Skip installation — run tools directly with `bunx`:
|
|
63
63
|
```bash
|
|
64
64
|
bunx gh-tool pr status
|
|
65
|
-
bunx
|
|
65
|
+
bunx observability-tool trace get <trace-id>
|
|
66
66
|
bunx k8s-tool pods --env test
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
**Step 2 — Configure**
|
|
70
70
|
|
|
71
|
-
Ask the user which tools they need (gh,
|
|
71
|
+
Ask the user which tools they need (gh, observability, db, k8s, az, logs, session, audit). Create `agent-tools.json5` in the project root with **only the selected tools configured**. See [`examples/agent-tools.json5`](./examples/agent-tools.json5) for the full config reference with all options documented.
|
|
72
72
|
|
|
73
73
|
Minimal starting config:
|
|
74
74
|
|
|
@@ -99,32 +99,32 @@ Install **only the per-tool skills the project needs** (recommended), or the all
|
|
|
99
99
|
|
|
100
100
|
```bash
|
|
101
101
|
# Per-tool skills (recommended) — install only what the project uses
|
|
102
|
-
npx skills add blogic-cz/agent-tools --path
|
|
103
|
-
npx skills add blogic-cz/agent-tools --path
|
|
104
|
-
npx skills add blogic-cz/agent-tools --path
|
|
105
|
-
npx skills add blogic-cz/agent-tools --path
|
|
106
|
-
npx skills add blogic-cz/agent-tools --path
|
|
107
|
-
npx skills add blogic-cz/agent-tools --path
|
|
108
|
-
npx skills add blogic-cz/agent-tools --path
|
|
102
|
+
npx skills add blogic-cz/agent-tools --path skills/gh-tool
|
|
103
|
+
npx skills add blogic-cz/agent-tools --path skills/observability-tool
|
|
104
|
+
npx skills add blogic-cz/agent-tools --path skills/db-tool
|
|
105
|
+
npx skills add blogic-cz/agent-tools --path skills/k8s-tool
|
|
106
|
+
npx skills add blogic-cz/agent-tools --path skills/az-tool
|
|
107
|
+
npx skills add blogic-cz/agent-tools --path skills/logs-tool
|
|
108
|
+
npx skills add blogic-cz/agent-tools --path skills/session-tool
|
|
109
109
|
|
|
110
110
|
# All-in-one skill (alternative) — all tools in a single skill
|
|
111
|
-
npx skills add blogic-cz/agent-tools --path
|
|
111
|
+
npx skills add blogic-cz/agent-tools --path skills/agent-tools
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
**Do not run these commands for the user** — they require interactive selection.
|
|
115
115
|
|
|
116
116
|
Available per-tool skills:
|
|
117
117
|
|
|
118
|
-
| Skill
|
|
119
|
-
|
|
|
120
|
-
| `gh-tool`
|
|
121
|
-
| `
|
|
122
|
-
| `db-tool`
|
|
123
|
-
| `k8s-tool`
|
|
124
|
-
| `az-tool`
|
|
125
|
-
| `logs-tool`
|
|
126
|
-
| `session-tool`
|
|
127
|
-
| `agent-tools`
|
|
118
|
+
| Skill | Install when project uses |
|
|
119
|
+
| -------------------- | ------------------------------------------- |
|
|
120
|
+
| `gh-tool` | GitHub PRs, issues, workflows, CI checks |
|
|
121
|
+
| `observability-tool` | Tempo traces, Loki logs, Prometheus metrics |
|
|
122
|
+
| `db-tool` | SQL queries, schema introspection |
|
|
123
|
+
| `k8s-tool` | Kubernetes pods, logs, deployments |
|
|
124
|
+
| `az-tool` | Azure DevOps pipelines, builds |
|
|
125
|
+
| `logs-tool` | Application log reading (local and remote) |
|
|
126
|
+
| `session-tool` | OpenCode session history browsing |
|
|
127
|
+
| `agent-tools` | All of the above in a single skill |
|
|
128
128
|
|
|
129
129
|
Then update the project's `AGENTS.md` and/or `CLAUDE.md`:
|
|
130
130
|
|
|
@@ -185,7 +185,7 @@ bun run agent-tools/example-tool/index.ts ping
|
|
|
185
185
|
remotePath: "/app/logs",
|
|
186
186
|
},
|
|
187
187
|
},
|
|
188
|
-
|
|
188
|
+
observability: {
|
|
189
189
|
// Profile name (selected via --profile, or used automatically when it's the only one)
|
|
190
190
|
default: {
|
|
191
191
|
// Environment name (selected via --env)
|
|
@@ -205,7 +205,7 @@ bun run agent-tools/example-tool/index.ts ping
|
|
|
205
205
|
|
|
206
206
|
```bash
|
|
207
207
|
bun gh-tool pr status
|
|
208
|
-
bun
|
|
208
|
+
bun observability-tool trace get 0b7bdf0dde1c55458364ba5588a8075e --env local
|
|
209
209
|
bun k8s-tool kubectl --env test --cmd "get pods"
|
|
210
210
|
bun logs-tool list --env local
|
|
211
211
|
bun audit-tool list --limit 20
|
|
@@ -226,16 +226,16 @@ export default { handleToolExecuteBefore };
|
|
|
226
226
|
|
|
227
227
|
## Tools
|
|
228
228
|
|
|
229
|
-
| Binary
|
|
230
|
-
|
|
|
231
|
-
| `gh-tool`
|
|
232
|
-
| `
|
|
233
|
-
| `audit-tool`
|
|
234
|
-
| `db-tool`
|
|
235
|
-
| `k8s-tool`
|
|
236
|
-
| `az-tool`
|
|
237
|
-
| `logs-tool`
|
|
238
|
-
| `session-tool`
|
|
229
|
+
| Binary | Description |
|
|
230
|
+
| -------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
231
|
+
| `gh-tool` | GitHub CLI wrapper — PR management, issues, workflows, composite commands (`review-triage`, `reply-and-resolve`) |
|
|
232
|
+
| `observability-tool` | LGTM wrapper — Tempo traces, Loki log correlation, and Prometheus metrics via Grafana |
|
|
233
|
+
| `audit-tool` | Audit trail browser — inspect recent tool invocations and purge old entries |
|
|
234
|
+
| `db-tool` | Database query tool — SQL execution, schema introspection |
|
|
235
|
+
| `k8s-tool` | Kubernetes tool — kubectl wrapper + structured commands (`pods`, `logs`, `describe`, `exec`, `top`) |
|
|
236
|
+
| `az-tool` | Azure DevOps tool — pipelines, builds, repos |
|
|
237
|
+
| `logs-tool` | Application logs — read local and remote (k8s pod) logs |
|
|
238
|
+
| `session-tool` | OpenCode session browser — list, read, search sessions |
|
|
239
239
|
|
|
240
240
|
All tools support `--help` for full usage documentation. Legacy `agent-tools-*` binary names (e.g. `agent-tools-gh`) still work for backwards compatibility.
|
|
241
241
|
|
|
@@ -247,7 +247,7 @@ Every tool invocation is automatically recorded to a local SQLite database — z
|
|
|
247
247
|
|
|
248
248
|
### How it works
|
|
249
249
|
|
|
250
|
-
Each CLI wrapper (`gh`, `
|
|
250
|
+
Each CLI wrapper (`gh`, `observability`, `k8s`, `db`, `az`, `logs`, `session`, `audit`) writes a row to `~/.agent-tools/audit.sqlite` on every execution. Logging is fire-and-forget — if the database is unavailable or write fails, the tool continues normally. Audit never blocks or slows down your workflow.
|
|
251
251
|
|
|
252
252
|
Entries older than `retentionDays` (default: 90) are automatically purged on each write.
|
|
253
253
|
|
|
@@ -287,16 +287,16 @@ All settings are optional — audit works out of the box with sensible defaults.
|
|
|
287
287
|
|
|
288
288
|
### What gets recorded
|
|
289
289
|
|
|
290
|
-
| Column | Description
|
|
291
|
-
| ----------- |
|
|
292
|
-
| `ts` | ISO 8601 timestamp
|
|
293
|
-
| `tool` | Tool name (`gh`, `
|
|
294
|
-
| `project` | Working directory (`process.cwd()`)
|
|
295
|
-
| `args` | Command-line arguments (JSON array)
|
|
296
|
-
| `duration` | Execution time in milliseconds
|
|
297
|
-
| `success` | `1` (success) or `0` (failure)
|
|
298
|
-
| `error` | Error message if failed, `null` otherwise
|
|
299
|
-
| `exit_code` | Process exit code
|
|
290
|
+
| Column | Description |
|
|
291
|
+
| ----------- | ----------------------------------------------------------------------- |
|
|
292
|
+
| `ts` | ISO 8601 timestamp |
|
|
293
|
+
| `tool` | Tool name (`gh`, `observability`, `k8s`, `db`, `az`, `logs`, `session`) |
|
|
294
|
+
| `project` | Working directory (`process.cwd()`) |
|
|
295
|
+
| `args` | Command-line arguments (JSON array) |
|
|
296
|
+
| `duration` | Execution time in milliseconds |
|
|
297
|
+
| `success` | `1` (success) or `0` (failure) |
|
|
298
|
+
| `error` | Error message if failed, `null` otherwise |
|
|
299
|
+
| `exit_code` | Process exit code |
|
|
300
300
|
|
|
301
301
|
## Configuration
|
|
302
302
|
|
|
@@ -350,14 +350,14 @@ See [`examples/agent-tools.json5`](./examples/agent-tools.json5) for a complete
|
|
|
350
350
|
|
|
351
351
|
Each tool uses its own auth method — no unified token store:
|
|
352
352
|
|
|
353
|
-
| Tool
|
|
354
|
-
|
|
|
355
|
-
| `gh-tool`
|
|
356
|
-
| `
|
|
357
|
-
| `k8s-tool`
|
|
358
|
-
| `az-tool`
|
|
359
|
-
| `db-tool`
|
|
360
|
-
| `logs-tool`
|
|
353
|
+
| Tool | Auth Method |
|
|
354
|
+
| -------------------- | -------------------------------------------------------------------------------------------- |
|
|
355
|
+
| `gh-tool` | `gh` CLI session (`gh auth login`) or `GITHUB_TOKEN` env var |
|
|
356
|
+
| `observability-tool` | Grafana URL from config plus optional token from `tokenEnvVar` |
|
|
357
|
+
| `k8s-tool` | Existing kubectl context (kubeconfig). Cluster ID from config resolves context automatically |
|
|
358
|
+
| `az-tool` | `az` CLI session (`az login`) |
|
|
359
|
+
| `db-tool` | Password from env var defined by `passwordEnvVar` in config (e.g. `AGENT_TOOLS_DB_PASSWORD`) |
|
|
360
|
+
| `logs-tool` | No auth — reads local files or uses k8s-tool for remote access |
|
|
361
361
|
|
|
362
362
|
Secrets are **never** stored in the config file. The `db-tool` config references env var **names** only:
|
|
363
363
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blogic-cz/agent-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "CLI tools for AI coding agent workflows — GitHub, database, Kubernetes, Azure DevOps, logs, sessions, and audit",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"database",
|
|
10
10
|
"devops",
|
|
11
11
|
"github",
|
|
12
|
-
"grafana",
|
|
13
12
|
"kubernetes",
|
|
13
|
+
"observability",
|
|
14
14
|
"tools"
|
|
15
15
|
],
|
|
16
16
|
"homepage": "https://github.com/blogic-cz/agent-tools#readme",
|
|
@@ -24,17 +24,17 @@
|
|
|
24
24
|
"agent-tools-az": "./src/az-tool/index.ts",
|
|
25
25
|
"agent-tools-db": "./src/db-tool/index.ts",
|
|
26
26
|
"agent-tools-gh": "./src/gh-tool/index.ts",
|
|
27
|
-
"agent-tools-grafana": "./src/grafana-tool/index.ts",
|
|
28
27
|
"agent-tools-k8s": "./src/k8s-tool/index.ts",
|
|
29
28
|
"agent-tools-logs": "./src/logs-tool/index.ts",
|
|
29
|
+
"agent-tools-observability": "./src/observability-tool/index.ts",
|
|
30
30
|
"agent-tools-session": "./src/session-tool/index.ts",
|
|
31
31
|
"audit-tool": "./src/audit-tool/index.ts",
|
|
32
32
|
"az-tool": "./src/az-tool/index.ts",
|
|
33
33
|
"db-tool": "./src/db-tool/index.ts",
|
|
34
34
|
"gh-tool": "./src/gh-tool/index.ts",
|
|
35
|
-
"grafana-tool": "./src/grafana-tool/index.ts",
|
|
36
35
|
"k8s-tool": "./src/k8s-tool/index.ts",
|
|
37
36
|
"logs-tool": "./src/logs-tool/index.ts",
|
|
37
|
+
"observability-tool": "./src/observability-tool/index.ts",
|
|
38
38
|
"session-tool": "./src/session-tool/index.ts"
|
|
39
39
|
},
|
|
40
40
|
"files": [
|
|
@@ -75,9 +75,9 @@
|
|
|
75
75
|
"types": "./dist/db-tool/*.d.ts",
|
|
76
76
|
"default": "./src/db-tool/*.ts"
|
|
77
77
|
},
|
|
78
|
-
"#
|
|
79
|
-
"types": "./dist/
|
|
80
|
-
"default": "./src/
|
|
78
|
+
"#observability/*": {
|
|
79
|
+
"types": "./dist/observability-tool/*.d.ts",
|
|
80
|
+
"default": "./src/observability-tool/*.ts"
|
|
81
81
|
},
|
|
82
82
|
"#logs/*": {
|
|
83
83
|
"types": "./dist/logs-tool/*.d.ts",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"check:ci": "bun check.ts ci",
|
|
124
124
|
"format": "oxfmt",
|
|
125
125
|
"format:check": "oxfmt --check",
|
|
126
|
-
"
|
|
126
|
+
"observability-tool": "bun src/observability-tool/index.ts",
|
|
127
127
|
"gh-tool": "bun src/gh-tool/index.ts",
|
|
128
128
|
"lint": "oxlint -c ./.oxlintrc.json --deny-warnings",
|
|
129
129
|
"lint:fix": "oxlint -c ./.oxlintrc.json --fix",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://raw.githubusercontent.com/blogic-cz/agent-tools/main/schemas/agent-tools.schema.json",
|
|
4
4
|
"title": "Agent Tools Configuration",
|
|
5
|
-
"description": "Root configuration for the agent-tools package. Tool-specific sections (azure, kubernetes, database,
|
|
5
|
+
"description": "Root configuration for the agent-tools package. Tool-specific sections (azure, kubernetes, database, observability, logs) are maps of named profiles keyed by profile name. Tools choose a profile via --profile <name> (default key is 'default', single-entry maps can be auto-selected). session, audit, and credentialGuard are global sections.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": true,
|
|
8
8
|
"properties": {
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"$ref": "#/definitions/DatabaseConfig"
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
|
-
"
|
|
38
|
-
"description": "Named
|
|
37
|
+
"observability": {
|
|
38
|
+
"description": "Named observability profiles as Record<string, ObservabilityConfig>.",
|
|
39
39
|
"type": "object",
|
|
40
40
|
"additionalProperties": {
|
|
41
|
-
"description": "
|
|
42
|
-
"$ref": "#/definitions/
|
|
41
|
+
"description": "Observability profile configuration containing one or more environments.",
|
|
42
|
+
"$ref": "#/definitions/ObservabilityConfig"
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"logs": {
|
|
@@ -200,13 +200,13 @@
|
|
|
200
200
|
},
|
|
201
201
|
"required": ["environments"]
|
|
202
202
|
},
|
|
203
|
-
"
|
|
204
|
-
"description": "Single
|
|
203
|
+
"ObservabilityEnvTarget": {
|
|
204
|
+
"description": "Single observability environment target.",
|
|
205
205
|
"type": "object",
|
|
206
206
|
"additionalProperties": false,
|
|
207
207
|
"properties": {
|
|
208
208
|
"url": {
|
|
209
|
-
"description": "Base Grafana URL.",
|
|
209
|
+
"description": "Base Grafana URL used for observability queries.",
|
|
210
210
|
"type": "string"
|
|
211
211
|
},
|
|
212
212
|
"tokenEnvVar": {
|
|
@@ -224,16 +224,16 @@
|
|
|
224
224
|
},
|
|
225
225
|
"required": ["url"]
|
|
226
226
|
},
|
|
227
|
-
"
|
|
228
|
-
"description": "
|
|
227
|
+
"ObservabilityConfig": {
|
|
228
|
+
"description": "Observability profile configuration.",
|
|
229
229
|
"type": "object",
|
|
230
230
|
"additionalProperties": false,
|
|
231
231
|
"properties": {
|
|
232
232
|
"environments": {
|
|
233
|
-
"description": "Named
|
|
233
|
+
"description": "Named observability environments as Record<string, ObservabilityEnvTarget>.",
|
|
234
234
|
"type": "object",
|
|
235
235
|
"additionalProperties": {
|
|
236
|
-
"$ref": "#/definitions/
|
|
236
|
+
"$ref": "#/definitions/ObservabilityEnvTarget"
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
},
|
package/src/config/index.ts
CHANGED
package/src/config/loader.ts
CHANGED
|
@@ -54,15 +54,15 @@ const LogsConfigSchema = Schema.Struct({
|
|
|
54
54
|
remotePath: Schema.String,
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
-
const
|
|
57
|
+
const ObservabilityEnvTargetSchema = Schema.Struct({
|
|
58
58
|
url: Schema.String,
|
|
59
59
|
tokenEnvVar: Schema.optionalKey(Schema.String),
|
|
60
60
|
prometheusUid: Schema.optionalKey(Schema.String),
|
|
61
61
|
lokiUid: Schema.optionalKey(Schema.String),
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
const
|
|
65
|
-
environments: Schema.Record(Schema.String,
|
|
64
|
+
const ObservabilityConfigSchema = Schema.Struct({
|
|
65
|
+
environments: Schema.Record(Schema.String, ObservabilityEnvTargetSchema),
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
const AuditConfigSchema = Schema.Struct({
|
|
@@ -80,7 +80,7 @@ const KNOWN_TOP_LEVEL_KEYS = new Set([
|
|
|
80
80
|
"azure",
|
|
81
81
|
"kubernetes",
|
|
82
82
|
"database",
|
|
83
|
-
"
|
|
83
|
+
"observability",
|
|
84
84
|
"logs",
|
|
85
85
|
"session",
|
|
86
86
|
"audit",
|
|
@@ -94,7 +94,7 @@ const AgentToolsConfigSchema = Schema.Struct({
|
|
|
94
94
|
azure: Schema.optionalKey(Schema.Record(Schema.String, AzureConfigSchema)),
|
|
95
95
|
kubernetes: Schema.optionalKey(Schema.Record(Schema.String, K8sConfigSchema)),
|
|
96
96
|
database: Schema.optionalKey(Schema.Record(Schema.String, DatabaseConfigSchema)),
|
|
97
|
-
|
|
97
|
+
observability: Schema.optionalKey(Schema.Record(Schema.String, ObservabilityConfigSchema)),
|
|
98
98
|
logs: Schema.optionalKey(Schema.Record(Schema.String, LogsConfigSchema)),
|
|
99
99
|
session: Schema.optionalKey(
|
|
100
100
|
Schema.Struct({
|
|
@@ -190,7 +190,7 @@ export const ConfigServiceLayer = Layer.effect(
|
|
|
190
190
|
|
|
191
191
|
type ProfiledSection = keyof Pick<
|
|
192
192
|
AgentToolsConfig,
|
|
193
|
-
"azure" | "kubernetes" | "database" | "
|
|
193
|
+
"azure" | "kubernetes" | "database" | "observability" | "logs"
|
|
194
194
|
>;
|
|
195
195
|
|
|
196
196
|
export function getToolConfig<T>(
|
package/src/config/types.ts
CHANGED
|
@@ -43,17 +43,17 @@ export type LogsConfig = {
|
|
|
43
43
|
remotePath: string;
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
/** Single
|
|
47
|
-
export type
|
|
46
|
+
/** Single observability environment connection details */
|
|
47
|
+
export type ObservabilityEnvTarget = {
|
|
48
48
|
url: string;
|
|
49
49
|
tokenEnvVar?: string;
|
|
50
50
|
prometheusUid?: string;
|
|
51
51
|
lokiUid?: string;
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
/**
|
|
55
|
-
export type
|
|
56
|
-
environments: Record<string,
|
|
54
|
+
/** Observability profile configuration */
|
|
55
|
+
export type ObservabilityConfig = {
|
|
56
|
+
environments: Record<string, ObservabilityEnvTarget>;
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
export type CliToolOverride = {
|
|
@@ -99,8 +99,8 @@ export type AgentToolsConfig = {
|
|
|
99
99
|
database?: Record<string, DatabaseConfig>;
|
|
100
100
|
/** Named logs profiles. e.g. { default: { localDir: "...", remotePath: "..." } } */
|
|
101
101
|
logs?: Record<string, LogsConfig>;
|
|
102
|
-
/** Named
|
|
103
|
-
|
|
102
|
+
/** Named observability profiles. e.g. { default: { environments: { local: {...}, prod: {...} } } } */
|
|
103
|
+
observability?: Record<string, ObservabilityConfig>;
|
|
104
104
|
/** Global session config (not per-profile) */
|
|
105
105
|
session?: {
|
|
106
106
|
storagePath: string;
|
package/src/index.ts
CHANGED
|
@@ -9,32 +9,23 @@ import { ConfigServiceLayer } from "#config";
|
|
|
9
9
|
import { AuditServiceLayer, withAudit } from "#shared/audit";
|
|
10
10
|
import { VERSION } from "#shared";
|
|
11
11
|
|
|
12
|
-
import { alertsCommand } from "./alerts";
|
|
13
|
-
import { dashboardsCommand } from "./dashboards";
|
|
14
|
-
import { datasourcesCommand } from "./datasources";
|
|
15
|
-
import { healthCommand } from "./health";
|
|
16
|
-
import { logsCommand } from "./logs";
|
|
17
12
|
import { metricsCommand } from "./metrics";
|
|
13
|
+
import { traceCommand } from "./trace";
|
|
18
14
|
|
|
19
15
|
const renderCauseToStderr = (cause: Cause.Cause<unknown>) => Console.error(cause.toString());
|
|
20
16
|
|
|
21
|
-
const mainCommand = Command.make("
|
|
22
|
-
Command.withDescription(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
alertsCommand,
|
|
27
|
-
datasourcesCommand,
|
|
28
|
-
metricsCommand,
|
|
29
|
-
logsCommand,
|
|
30
|
-
]),
|
|
17
|
+
const mainCommand = Command.make("observability-tool", {}).pipe(
|
|
18
|
+
Command.withDescription(
|
|
19
|
+
"LGTM observability queries — Tempo traces, Loki logs, Prometheus metrics",
|
|
20
|
+
),
|
|
21
|
+
Command.withSubcommands([traceCommand, metricsCommand]),
|
|
31
22
|
);
|
|
32
23
|
|
|
33
24
|
const cli = Command.run(mainCommand, { version: VERSION });
|
|
34
25
|
|
|
35
26
|
const MainLayer = Layer.mergeAll(BunServices.layer, ConfigServiceLayer, AuditServiceLayer);
|
|
36
27
|
|
|
37
|
-
const program = withAudit("
|
|
28
|
+
const program = withAudit("observability", cli).pipe(
|
|
38
29
|
Effect.provide(MainLayer),
|
|
39
30
|
Effect.tapCause(renderCauseToStderr),
|
|
40
31
|
);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Console, Effect } from "effect";
|
|
2
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
3
|
+
|
|
4
|
+
import { formatOption, formatOutput } from "#shared";
|
|
5
|
+
|
|
6
|
+
import { ObservabilityToolError } from "./errors";
|
|
7
|
+
import {
|
|
8
|
+
envOption,
|
|
9
|
+
formatObservabilityError,
|
|
10
|
+
observabilityDsQuery,
|
|
11
|
+
profileOption,
|
|
12
|
+
resolveConfig,
|
|
13
|
+
} from "./shared";
|
|
14
|
+
|
|
15
|
+
const queryCommand = Command.make(
|
|
16
|
+
"query",
|
|
17
|
+
{
|
|
18
|
+
promql: Argument.string("promql"),
|
|
19
|
+
format: formatOption,
|
|
20
|
+
env: envOption,
|
|
21
|
+
profile: profileOption,
|
|
22
|
+
start: Flag.string("start").pipe(
|
|
23
|
+
Flag.withDescription("Start time (default: now-1h)"),
|
|
24
|
+
Flag.withDefault("now-1h"),
|
|
25
|
+
),
|
|
26
|
+
end: Flag.string("end").pipe(
|
|
27
|
+
Flag.withDescription("End time (default: now)"),
|
|
28
|
+
Flag.withDefault("now"),
|
|
29
|
+
),
|
|
30
|
+
step: Flag.integer("step").pipe(
|
|
31
|
+
Flag.withDescription("Step in seconds (default: 60)"),
|
|
32
|
+
Flag.withDefault(60),
|
|
33
|
+
),
|
|
34
|
+
},
|
|
35
|
+
({ promql, format, env, profile, start, end, step }) => {
|
|
36
|
+
const startedAt = Date.now();
|
|
37
|
+
|
|
38
|
+
return Effect.gen(function* () {
|
|
39
|
+
const config = yield* resolveConfig(env, profile);
|
|
40
|
+
const response = yield* observabilityDsQuery(
|
|
41
|
+
config,
|
|
42
|
+
config.prometheusUid,
|
|
43
|
+
"prometheus",
|
|
44
|
+
promql,
|
|
45
|
+
{
|
|
46
|
+
instant: false,
|
|
47
|
+
from: start,
|
|
48
|
+
to: end,
|
|
49
|
+
step,
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (response.results.A.error) {
|
|
54
|
+
return yield* new ObservabilityToolError({
|
|
55
|
+
cause: new Error(response.results.A.error),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const series = (response.results.A.frames ?? []).flatMap((frame) => {
|
|
60
|
+
const fields = frame.schema.fields;
|
|
61
|
+
const timeFieldIndex = fields.findIndex((field) => field.type === "time");
|
|
62
|
+
|
|
63
|
+
if (timeFieldIndex === -1) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const timestamps = (frame.data.values[timeFieldIndex] ?? []) as Array<number | string>;
|
|
68
|
+
|
|
69
|
+
return fields.flatMap((field, fieldIndex) => {
|
|
70
|
+
if (fieldIndex === timeFieldIndex || field.type !== "number") {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const values = (frame.data.values[fieldIndex] ?? []) as Array<number | string | null>;
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
labels: field.labels ?? {},
|
|
78
|
+
points: timestamps
|
|
79
|
+
.map((timestamp, index) => ({
|
|
80
|
+
timestamp: Number(timestamp),
|
|
81
|
+
value:
|
|
82
|
+
values[index] === null || values[index] === undefined
|
|
83
|
+
? ""
|
|
84
|
+
: String(values[index]),
|
|
85
|
+
}))
|
|
86
|
+
.filter((point) => point.value.length > 0),
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const result = {
|
|
92
|
+
success: true,
|
|
93
|
+
message: `Resolved ${series.length} metric series for PromQL query`,
|
|
94
|
+
data: {
|
|
95
|
+
environment: env,
|
|
96
|
+
grafanaUrl: config.url,
|
|
97
|
+
prometheusDatasourceUid: config.prometheusUid,
|
|
98
|
+
query: promql,
|
|
99
|
+
start,
|
|
100
|
+
end,
|
|
101
|
+
step,
|
|
102
|
+
seriesCount: series.length,
|
|
103
|
+
series,
|
|
104
|
+
},
|
|
105
|
+
executionTimeMs: Date.now() - startedAt,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
yield* Console.log(formatOutput(result, format));
|
|
109
|
+
}).pipe(
|
|
110
|
+
Effect.catch((error) =>
|
|
111
|
+
Effect.gen(function* () {
|
|
112
|
+
const result = {
|
|
113
|
+
success: false,
|
|
114
|
+
message: "Failed to execute PromQL query",
|
|
115
|
+
error: formatObservabilityError(error),
|
|
116
|
+
hint: "Check PromQL syntax and Grafana/Prometheus connectivity",
|
|
117
|
+
executionTimeMs: Date.now() - startedAt,
|
|
118
|
+
};
|
|
119
|
+
yield* Console.log(formatOutput(result, format));
|
|
120
|
+
}),
|
|
121
|
+
),
|
|
122
|
+
);
|
|
123
|
+
},
|
|
124
|
+
).pipe(Command.withDescription("Execute PromQL range query via Grafana"));
|
|
125
|
+
|
|
126
|
+
export const metricsCommand = Command.make("metrics", {}).pipe(
|
|
127
|
+
Command.withDescription("Prometheus metric operations via Grafana"),
|
|
128
|
+
Command.withSubcommands([queryCommand]),
|
|
129
|
+
);
|