claw-insights 0.1.0 → 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Claw Insights Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ <div align="center">
2
+ <img src="packages/web/public/logo/icon-dark.svg" width="80" alt="Claw Insights" />
3
+ <h1>Claw Insights</h1>
4
+ <p><strong>Replay, metrics, logs &amp; shareable snapshots for <a href="https://github.com/openclaw/openclaw">OpenClaw</a> agents</strong></p>
5
+ <p>
6
+ <img src="https://img.shields.io/badge/%F0%9F%94%8C_Zero_Intrusion-read--only_sidecar-10b981" alt="Zero Intrusion" />
7
+ <img src="https://img.shields.io/badge/%F0%9F%94%8D_Full_Replay-session_transcripts-6366f1" alt="Full Replay" />
8
+ <img src="https://img.shields.io/badge/%F0%9F%93%B8_Shareable_Snapshots-PNG_%7C_SVG-f59e0b" alt="Shareable Snapshots" />
9
+ </p>
10
+ <p>
11
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
12
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node.js-%E2%89%A522.5-green" alt="Node.js" /></a>
13
+ <img src="https://img.shields.io/badge/macOS-supported-blue" alt="macOS" />
14
+ <img src="https://img.shields.io/badge/Linux-supported-blue" alt="Linux" />
15
+ </p>
16
+ <br />
17
+ <img src="docs/assets/hero-montage.png" width="100%" alt="Dashboard, Session Transcript, and Snapshot API" />
18
+ </div>
19
+
20
+ ---
21
+
22
+ <p align="center">
23
+ <strong>English</strong> ·
24
+ <a href="README.zh-CN.md">中文</a>
25
+ </p>
26
+
27
+ ## Features
28
+
29
+ - **Zero Intrusion** — Pure read-only sidecar; no code changes, no cloud calls, data never leaves your machine
30
+ - **Session Replay** — Full transcript timeline with role separation, tool calls, and per-turn token tracking
31
+ - **Shareable Snapshots** — Generate PNG/SVG status cards via REST API with themes, languages, and detail levels
32
+ - **Metrics Dashboard** — Per-model token breakdown, error rates, and uptime over 30m / 1h / 6h / 12h / 24h
33
+ - **Event Logs** — Structured viewer with density heatmap, filtering, and search
34
+ - **One Command Setup** — Auto-discovers your running gateway, lightweight SQLite storage
35
+ - **Dark / Light · EN / 中文** — Full theming and i18n with runtime toggle
36
+
37
+ ## Quick Start
38
+
39
+ ```bash
40
+ # Install
41
+ npm install -g claw-insights
42
+
43
+ # Start (auto-connects to your running OpenClaw gateway)
44
+ claw-insights start
45
+ ```
46
+
47
+ On launch you'll see an access URL:
48
+
49
+ ```
50
+ ✅ Claw Insights v0.1.0 ready in 1.2s
51
+
52
+ ➜ Open: http://127.0.0.1:41041/?token=abc123...
53
+ Auth: token (auto-generated)
54
+
55
+ PID 12345 · daemon · Port 41041
56
+ ```
57
+
58
+ Open the URL — token is exchanged for a session cookie, and you're in.
59
+
60
+ ```bash
61
+ claw-insights status # Show current access URL
62
+ claw-insights stop # Stop daemon
63
+ claw-insights start --no-auth # Disable authentication
64
+ ```
65
+
66
+ → Full install options, snapshot API, and troubleshooting: [docs/configuration.md](docs/configuration.md)
67
+
68
+ ## 🤖 AI Agent Friendly
69
+
70
+ Ships with structured resources for AI agents — see **[AGENTS.md](AGENTS.md)** for the full index:
71
+
72
+ | Skill | Use case |
73
+ | ----------------------------------------- | ------------------------------------------------- |
74
+ | [install](docs/skills/install/SKILL.md) | Install, configure, and launch |
75
+ | [snapshot](docs/skills/snapshot/SKILL.md) | Capture dashboard as PNG/SVG/JSON via REST or CLI |
76
+
77
+ ## Architecture
78
+
79
+ ```
80
+ claw-insights/
81
+ ├── packages/
82
+ │ ├── server/ Express + GraphQL Yoga + SQLite + Satori renderer
83
+ │ ├── web/ React 19 + Vite + Tailwind + ECharts + urql
84
+ │ └── shared/ Codegen TypeScript types (shared between server & web)
85
+ ├── bin/ CLI entry (start/stop/restart/status/logs/snapshot/run)
86
+ └── codegen.ts GraphQL codegen config (3 targets)
87
+ ```
88
+
89
+ **Data flow:** OpenClaw gateway → log tailing + CLI → SQLite → GraphQL (SSE subscriptions) → React
90
+
91
+ → Full architecture, dev setup, and codegen: [docs/architecture.md](docs/architecture.md)
92
+
93
+ ## Documentation
94
+
95
+ | Document | Description |
96
+ | -------------------------------------- | ------------------------------------- |
97
+ | [Configuration](docs/configuration.md) | All env vars, config file, auth model |
98
+ | [Architecture](docs/architecture.md) | System design, dev setup, testing |
99
+ | [API Reference](docs/api-reference.md) | GraphQL + REST endpoint signatures |
100
+ | [AGENTS.md](AGENTS.md) | AI agent skill index |
101
+
102
+ ## Contributing
103
+
104
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, PR guidelines, and code conventions.
105
+
106
+ ## Security
107
+
108
+ See [SECURITY.md](SECURITY.md) for vulnerability reporting and security model.
109
+
110
+ ## License
111
+
112
+ [MIT](LICENSE)
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "claw-insights",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Real-time monitoring dashboard for OpenClaw gateway",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "claw-insights": "./bin/claw-insights"
8
8
  },
9
- "files": ["bin/", "server/", "web/", "assets/"],
9
+ "files": ["bin/", "server/", "web/", "assets/", "README.md", "LICENSE"],
10
10
  "engines": { "node": ">=22" },
11
11
  "license": "MIT",
12
12
  "author": "Luca Liao",
@@ -12,6 +12,11 @@ import { execFile } from "child_process";
12
12
  import { promisify } from "util";
13
13
  var execFileAsync = promisify(execFile);
14
14
  var log = createChildLogger("cli-adapter");
15
+ var DEFAULT_TIMEOUT_MS = 8e3;
16
+ var STATUS_JSON_TIMEOUT_MS = 15e3;
17
+ function resolveTimeoutMs(argv) {
18
+ return argv[0] === "status" && argv.includes("--json") ? STATUS_JSON_TIMEOUT_MS : DEFAULT_TIMEOUT_MS;
19
+ }
15
20
  var PosixCliAdapter = class {
16
21
  constructor(cliPath, env) {
17
22
  this.cliPath = cliPath;
@@ -21,7 +26,7 @@ var PosixCliAdapter = class {
21
26
  try {
22
27
  log.debug({ argv }, "CLI exec");
23
28
  const { stdout, stderr: _stderr } = await execFileAsync(this.cliPath, argv, {
24
- timeout: 8e3,
29
+ timeout: resolveTimeoutMs(argv),
25
30
  encoding: "utf-8",
26
31
  env: this.env
27
32
  });
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PosixCliAdapter,
3
3
  PosixProcessAdapter
4
- } from "./chunk-TGTG7GUF.js";
4
+ } from "./chunk-MU2QNIHA.js";
5
5
  import {
6
6
  CLI_ENV,
7
7
  config,
package/server/index.js CHANGED
@@ -1594,9 +1594,9 @@ async function loadPlatform() {
1594
1594
  log7.info({ os }, "loading platform adapter");
1595
1595
  switch (os) {
1596
1596
  case "darwin":
1597
- return (await import("./darwin-DLFRTJ37.js")).platform;
1597
+ return (await import("./darwin-IIBM6CQD.js")).platform;
1598
1598
  case "linux":
1599
- return (await import("./linux-VBQVTPRU.js")).platform;
1599
+ return (await import("./linux-ZEULX5H5.js")).platform;
1600
1600
  default:
1601
1601
  throw new Error(`Unsupported platform: ${os}`);
1602
1602
  }
@@ -3513,6 +3513,7 @@ var CACHE_TTL = 1e4;
3513
3513
  var FAIL_CACHE_TTL = 3e3;
3514
3514
  var VERSION_CACHE_TTL = 6e4;
3515
3515
  var VERSION_FAIL_CACHE_TTL = 5e3;
3516
+ var STATUS_STALE_FALLBACK_MS = 3e4;
3516
3517
  function createGatewayClient(platform, options) {
3517
3518
  const logPath = options?.gatewayLogPath ?? config.openclawDir + "/logs/gateway.log";
3518
3519
  async function getStartedAtFromLog(pid) {
@@ -3551,6 +3552,7 @@ function createGatewayClient(platform, options) {
3551
3552
  let versionCache = null;
3552
3553
  let statusInFlight = null;
3553
3554
  let lastStatusJson = "";
3555
+ let lastSuccessfulStatus = null;
3554
3556
  async function getVersion() {
3555
3557
  if (isCacheValid(versionCache)) {
3556
3558
  return versionCache.data;
@@ -3561,58 +3563,83 @@ function createGatewayClient(platform, options) {
3561
3563
  return version;
3562
3564
  }
3563
3565
  async function parseStatusJson(json, version) {
3566
+ if (json.trim() === "") {
3567
+ return { ok: false, reason: "empty" };
3568
+ }
3569
+ let d;
3570
+ try {
3571
+ d = JSON.parse(json);
3572
+ } catch {
3573
+ return { ok: false, reason: "invalid-json" };
3574
+ }
3564
3575
  try {
3565
- const d = JSON.parse(json);
3566
- const gw = d?.gateway ?? {};
3567
- const svc = d?.gatewayService ?? {};
3568
- const channelSummary = d?.channelSummary ?? [];
3569
- const update = d?.update ?? {};
3570
- const pidMatch = svc?.runtimeShort?.match(/pid\s+(\d+)/);
3576
+ const gw = d.gateway ?? {};
3577
+ const svc = d.gatewayService ?? {};
3578
+ const channelSummary = d.channelSummary ?? [];
3579
+ const update = d.update ?? {};
3580
+ const updateRegistry = update.registry ?? {};
3581
+ const securityAudit = d.securityAudit ?? {};
3582
+ const secAudit = securityAudit.summary ?? { critical: 0, warn: 0, info: 0 };
3583
+ const sessions = d.sessions ?? {};
3584
+ const runtimeShort = typeof svc.runtimeShort === "string" ? svc.runtimeShort : "";
3585
+ const pidMatch = runtimeShort.match(/pid\s+(\d+)/);
3571
3586
  let pid = pidMatch ? Number(pidMatch[1]) : null;
3572
- const running = Boolean(gw?.reachable) || svc?.runtimeShort?.includes("running");
3587
+ const running = gw.reachable === true || runtimeShort.includes("running");
3573
3588
  if (!pid && running) {
3574
- pid = await platform.process.findPidByPort(gw?.port ?? 18789);
3589
+ const port = typeof gw.port === "number" ? gw.port : 18789;
3590
+ pid = await platform.process.findPidByPort(port);
3575
3591
  }
3576
- const latest = update?.registry?.latestVersion ?? update?.latestVersion ?? null;
3592
+ const latestFromRegistry = typeof updateRegistry.latestVersion === "string" ? updateRegistry.latestVersion : null;
3593
+ const latestFromUpdate = typeof update.latestVersion === "string" ? update.latestVersion : null;
3594
+ const latest = latestFromRegistry ?? latestFromUpdate;
3577
3595
  const updateAvailable = latest && latest !== version ? latest : null;
3578
- const connectLatencyMs = gw?.connectLatencyMs ?? null;
3579
- const latestVersion = update?.registry?.latestVersion ?? update?.latestVersion ?? null;
3580
- const secAudit = d?.securityAudit?.summary ?? { critical: 0, warn: 0, info: 0 };
3581
- const sessionDefaults = d?.sessions?.defaults ?? null;
3596
+ const connectLatencyMs = typeof gw.connectLatencyMs === "number" ? gw.connectLatencyMs : null;
3597
+ const latestVersion = latestFromRegistry ?? latestFromUpdate;
3598
+ const rawSessionDefaults = sessions.defaults;
3599
+ const sessionDefaults = rawSessionDefaults && typeof rawSessionDefaults.model === "string" && typeof rawSessionDefaults.contextTokens === "number" ? { model: rawSessionDefaults.model, contextTokens: rawSessionDefaults.contextTokens } : null;
3600
+ const critical = typeof secAudit.critical === "number" ? secAudit.critical : 0;
3601
+ const warn = typeof secAudit.warn === "number" ? secAudit.warn : 0;
3602
+ const info = typeof secAudit.info === "number" ? secAudit.info : 0;
3582
3603
  const startedAt = pid ? await getStartedAtFromLog(pid) : null;
3583
3604
  return {
3584
- running: Boolean(running),
3585
- pid,
3586
- version: version ?? "unknown",
3587
- updateAvailable,
3588
- uptime: pid ? await platform.process.getUptime(pid) : "unknown",
3589
- startedAt,
3590
- channels: parseChannels(channelSummary),
3591
- connectLatencyMs,
3592
- latestVersion,
3593
- securitySummary: {
3594
- critical: secAudit.critical ?? 0,
3595
- warn: secAudit.warn ?? 0,
3596
- info: secAudit.info ?? 0
3597
- },
3598
- sessionDefaults
3605
+ ok: true,
3606
+ data: {
3607
+ running,
3608
+ pid,
3609
+ version: version ?? "unknown",
3610
+ updateAvailable,
3611
+ uptime: pid ? await platform.process.getUptime(pid) : "unknown",
3612
+ startedAt,
3613
+ channels: parseChannels(channelSummary),
3614
+ connectLatencyMs,
3615
+ latestVersion,
3616
+ securitySummary: {
3617
+ critical,
3618
+ warn,
3619
+ info
3620
+ },
3621
+ sessionDefaults
3622
+ }
3599
3623
  };
3600
3624
  } catch {
3601
- return {
3602
- running: false,
3603
- pid: null,
3604
- version: "unknown",
3605
- updateAvailable: null,
3606
- uptime: "unknown",
3607
- startedAt: null,
3608
- channels: [],
3609
- connectLatencyMs: null,
3610
- latestVersion: null,
3611
- securitySummary: { critical: 0, warn: 0, info: 0 },
3612
- sessionDefaults: null
3613
- };
3625
+ return { ok: false, reason: "invalid-json" };
3614
3626
  }
3615
3627
  }
3628
+ function unavailableStatus() {
3629
+ return {
3630
+ running: false,
3631
+ pid: null,
3632
+ version: "unknown",
3633
+ updateAvailable: null,
3634
+ uptime: "unknown",
3635
+ startedAt: null,
3636
+ channels: [],
3637
+ connectLatencyMs: null,
3638
+ latestVersion: null,
3639
+ securitySummary: { critical: 0, warn: 0, info: 0 },
3640
+ sessionDefaults: null
3641
+ };
3642
+ }
3616
3643
  async function getGatewayStatus() {
3617
3644
  if (isCacheValid(statusCache)) {
3618
3645
  return statusCache.data;
@@ -3623,9 +3650,24 @@ function createGatewayClient(platform, options) {
3623
3650
  statusInFlight = (async () => {
3624
3651
  try {
3625
3652
  const [raw, version] = await Promise.all([platform.cli.exec(["status", "--json"]), getVersion()]);
3626
- const status = await parseStatusJson(raw, version);
3653
+ const parsed = await parseStatusJson(raw, version);
3654
+ let status;
3655
+ let ttl;
3656
+ if (parsed.ok) {
3657
+ status = parsed.data;
3658
+ lastSuccessfulStatus = { data: status, ts: Date.now() };
3659
+ ttl = status.running ? CACHE_TTL : FAIL_CACHE_TTL;
3660
+ } else {
3661
+ const staleAge = lastSuccessfulStatus ? Date.now() - lastSuccessfulStatus.ts : Number.POSITIVE_INFINITY;
3662
+ if (lastSuccessfulStatus && staleAge <= STATUS_STALE_FALLBACK_MS) {
3663
+ status = lastSuccessfulStatus.data;
3664
+ ttl = FAIL_CACHE_TTL;
3665
+ } else {
3666
+ status = unavailableStatus();
3667
+ ttl = FAIL_CACHE_TTL;
3668
+ }
3669
+ }
3627
3670
  const curJson = JSON.stringify(status);
3628
- const ttl = status.running ? CACHE_TTL : FAIL_CACHE_TTL;
3629
3671
  statusCache = { data: status, ts: Date.now(), ttl };
3630
3672
  if (curJson !== lastStatusJson) {
3631
3673
  lastStatusJson = curJson;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PosixCliAdapter,
3
3
  PosixProcessAdapter
4
- } from "./chunk-TGTG7GUF.js";
4
+ } from "./chunk-MU2QNIHA.js";
5
5
  import {
6
6
  CLI_ENV,
7
7
  config