# dsh-grafana-query

[English](README.md) | [繁體中文](README.zh-TW.md) | [简体中文](README.zh-CN.md) | 日本語

`dsh-grafana-query` は無料でオープンソースの、**読み取り専用**な DeepSeek Harness 用 Grafana
プラグインです。エージェントが Grafana のデータソースプロキシ経由で PromQL を実行し、
Grafana unified alerting の現在の状態を読み取れるようにします。Grafana 側は一切変更しません。

npm の `dsh-grafana` とは別物です。あちらは dashboard JSON を Grafana へ書き戻す
**書き込み型のダッシュボードエディタ**です。本プラグインはその逆で、読み取り専用の
メトリクスクエリとアラート状態を担当します。ダッシュボードと panel JSON は明確に対象外です。

## Tools

| ツール | 目的 |
| --- | --- |
| `grafana_health` | インスタンスへ接続できるかを確認し、バージョンを返します。 |
| `grafana_list_datasources` | データソースの uid、type、access モードを一覧します。最初に実行してください。 |
| `grafana_query` | データソースプロキシ経由で instant PromQL クエリを実行します。 |
| `grafana_query_range` | step とポイント数の上限を強制したうえで範囲 PromQL クエリを実行します。 |
| `grafana_alert_state` | unified alerting のルールの現在の状態を読み取ります。 |
| `grafana_list_alert_rules` | プロビジョニング済みのアラートルール定義を一覧します。 |

すべてのツールは読み取り専用です。v0.1 は Grafana 上で作成・編集・削除・silence・ack・
一時停止のいずれも行いません。

## 上限

以下の上限はいずれも Grafana ではなくプラグイン側で強制されます。何かが切り詰められた場合は
`meta.truncated` と切り詰め前の総数で示されます。

| 項目 | 値 |
| --- | --- |
| series ごとのポイント数（`max_points`） | 既定 200、最大 500。Prometheus は両端を返すため、`n` 秒の範囲に step `s` を指定すると `floor(n / s) + 1` 個のポイントになります |
| 範囲の長さ（`grafana_query_range`） | 31 日 |
| 1 回の範囲クエリの合計ポイント数 | 20000。超えた series は丸ごと破棄され、途中で切られることはありません |
| 1 回のクエリの series 数 | `maxSeries`、既定 100 |
| アラートルール件数（`grafana_alert_state`、`grafana_list_alert_rules`） | 一致したルールの先頭 500 件。それ以降はページ送りでも取得できないため、絞り込み条件を使ってください |
| ルールごとのアラートインスタンス | 既定 10、最大 50 |
| 1 ページあたりの件数 | 既定 20、最大 100 |
| 上流のエラーテキスト | 200 文字、HTTP 400 のときのみ |

`grafana_alert_state` は既定で `firing`、`pending`、`unknown` のルールのみを返します——**`inactive`
のルールは既定では表示されません**。必要な場合は `state` で明示してください。

## Requirements

- 互換性のある `@deepseek-ai/dsh-tools` API を備えた DeepSeek Harness
- Node.js 22.19 以上（22.x 系）または Node.js 24 以上
- **Grafana 9.0 以上** — uid 版のデータソースプロキシ（`/api/datasources/proxy/uid/:uid/*`）
  のみ対応し、非推奨の数値 id パスには対応しません

## Configuration

```sh
export GRAFANA_URL='https://grafana.example.com'
export GRAFANA_TOKEN='glsa_your_service_account_token'
```

| 項目 | 環境変数 | 既定値 | 範囲 |
| --- | --- | --- | --- |
| `baseUrl` | `GRAFANA_URL` | 必須 | http(s) URL。認証情報の埋め込み、query、fragment は不可。サブパスは可 |
| `token` | `GRAFANA_TOKEN` | 必須 | 空不可 |
| `locale` | — | `en` | `en`、`zh-TW`、`zh-CN`、`ja` |
| `requestTimeoutMs` | — | `30000` | 1 – 300000 |
| `maxResponseBytes` | — | `5242880` | 1 – 52428800 |
| `maxSeries` | — | `100` | 1 – 1000 |

プラグイン設定は環境変数より優先されます。

## Permissions

Grafana の service account token（推奨）と旧来の API key はどちらも利用できます。
両者とも同じ `Authorization: Bearer` ヘッダーを使います。Grafana Cloud の Access Policy
token（`glc_`）は Cloud のデータエンドポイント用であり、この API では**使えません**。

### Grafana 側での実際の設定方法

下表のスコープ名は Grafana が内部で参照するものであり、**UI でそのままチェックする項目ではありません**。
service account を作成するときに動作する組み合わせは次のとおりです。

1. basic role に **Viewer** を選ぶ——`datasources:read` と `datasources:query` をカバーします。
2. さらに fixed role **Alerting → Full read-only access** を付与する——`alert.rules:read` と
   `alert.provisioning:read` をカバーします。

2026-08-27 に Grafana Cloud でこの組み合わせを実測し、6 つのツールすべてが動作しました。
詳細は[検証ノート](docs/superpowers/specs/2026-08-26-dsh-grafana-verification.md)を参照してください。

**最小権限の token でも使い勝手は落ちません。** Grafana は `GET /api/datasources` に対して 403 を返す
のではなく、その token が到達できるものだけに**絞り込んだ一覧**を返します。単一のデータソースに
**Query** 権限だけを付与した token では、`grafana_list_datasources` はその 1 件だけを返します
（2026-08-27 実測: Viewer token は 26 件、制限付き token は 1 件）。クエリすると 403 になる項目が
一覧に並ぶことはありません。なお、データソース単位の Query 権限はそのデータソースの metadata 読み取りも
含むため、「クエリはできるが読めない」という状態は存在しません。

### スコープ対照

| ツール | 必要な権限 |
| --- | --- |
| `grafana_health` | なし——`/api/health` は認証を必要としないため、このツールでは token の有効性を判定できません。token の検証には `grafana_list_datasources` を使ってください。 |
| `grafana_list_datasources` | `datasources:read` |
| `grafana_query`、`grafana_query_range` | `datasources:query`（事前の型チェックには `datasources:read` も必要） |
| `grafana_alert_state` | `alert.rules:read` |
| `grafana_list_alert_rules` | `alert.provisioning:read` |

## Grafana Cloud

`baseUrl` にはスタック自体を指定し、そのスタックで作成した service account token を使います。

```sh
export GRAFANA_URL='https://your-stack.grafana.net'
export GRAFANA_TOKEN='glsa_your_service_account_token'
```

ここで `glc_` で始まる Access Policy token は使わないでください。Cloud のスタックには
多数の組み込みデータソースがあるため、`grafana_list_datasources` の `type` と
`name_contains` フィルターで一覧を短く保ってください。

## Install

```sh
bun add dsh-grafana-query
```

パッケージには `cordis.patch.yml` が含まれ、`package.json` の `dsh.bundle.patch` で宣言されます。
これにより DeepSeek Harness のレジストリが既定設定のままプラグインを読み込めます。

## Examples

1. `grafana_list_datasources` に `{"type": "prometheus"}` を渡して uid を取得します。
2. `grafana_query` に `{"datasource_uid": "prom-1", "query": "up"}` を渡して現在値を取得します。
3. `grafana_query_range` に `{"datasource_uid": "prom-1", "query": "rate(node_cpu_seconds_total[5m])", "start": "...", "end": "..."}`
   を渡して推移を取得します。`step` を省略すると、各 series が `max_points` を超えないよう
   プラグインが自動で選びます。
4. `grafana_alert_state` を引数なしで実行し、今何が発報しているかを確認します。

## Internationalization

`locale` に `en`、`zh-TW`、`zh-CN`、`ja` のいずれかを設定すると、モデルが見るツールと
パラメータの説明が切り替わります。ツール名は常に英語のままで、エラーメッセージも常に英語です。

## Security and error behavior

- すべてのツールは読み取り専用です。
- エラーに token、`Authorization` ヘッダー、生のレスポンスボディが含まれることはありません。
- 唯一の例外として、Prometheus が HTTP 400 でクエリを拒否した場合のみ、構造化された
  `error` フィールドを返します。エージェントが PromQL を修正できるようにするためで、
  200 文字が上限、かつ事前に秘匿情報のマスキングを行います。それ以外のステータスコードでは
  常に静的なメッセージを返します。
- レスポンスサイズは `maxResponseBytes`、`maxSeries`、および series ごとのポイント数上限の
  3 段階で制限されます。切り詰めが発生した場合は `meta.truncated` と切り詰め前の合計値に必ず記録されます。

## Development

```sh
bun install
bun run lint
bun run typecheck
bun run test
bun run build
```

## License

MIT
