Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

Agent OS runtime cockpit for OpenClaw. Dashclaw connects to a local OpenClaw Gateway over the official WebSocket protocol and provides real-time observability, persistent timeline history, and guarded admin controls.

![Dashclaw Command Center](docs/images/dashclaw-command-center.png)

## Why Dashclaw?

- **See what is happening now** — connect agents, sessions, tasks, approvals,
failures, and recent events in one operational view.
- **Find work that needs intervention** — surface blocked, failed, stale, and
approval-bound activity without joining raw Gateway data by hand.
- **Act with guardrails** — inspect runtime history and use allowlisted admin
operations with confirmation, audit logging, redaction, and read-only mode.

Dashclaw v0.1.0 is distributed as source code. It has been tested on Linux and
macOS with OpenClaw v2026.6.11. Windows is experimental: use loopback binding
and restrict `.env` and the data directory with Windows ACLs.
Expand Down Expand Up @@ -213,6 +224,14 @@ bugs and feature requests. Read [SUPPORT.md](SUPPORT.md) for support boundaries,
[CONTRIBUTING.md](CONTRIBUTING.md) before submitting changes, and [SECURITY.md](SECURITY.md)
for private vulnerability reporting.

## Feedback

Running Dashclaw with a real OpenClaw workflow? Share your setup experience,
the first confusing screen, missing operational signals, and anything you would
use weekly in the [v0.1 feedback discussion](https://github.com/savva312/dashclaw/discussions/16).
Include your Dashclaw version, OpenClaw version, and operating system, but never
post credentials, private session output, environment files, or database data.

## License

[MIT](LICENSE)
Expand Down
20 changes: 16 additions & 4 deletions apps/server/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { loadEnv } from "./load-env.js";

loadEnv();
const WORKSPACE_ROOT = loadEnv();

import { existsSync } from "node:fs";
import { join } from "node:path";
Expand All @@ -11,19 +11,31 @@ import { AdminService } from "./admin-service.js";
import { GatewayBridge } from "./gateway-bridge.js";
import { RuntimeStore } from "./runtime-store.js";
import { safeOutputString } from "@dashclaw/runtime-model";
import { isLoopbackBind, resolveDashboardAuth } from "./startup-config.js";
import {
isLoopbackBind,
resolveDashboardAuth,
resolveWorkspacePath,
} from "./startup-config.js";

const PORT = Number(process.env.DASHCLAW_PORT ?? "8090");
const BIND = process.env.DASHCLAW_BIND ?? "127.0.0.1";
const GATEWAY_URL = process.env.OPENCLAW_GATEWAY_URL ?? "ws://127.0.0.1:18789";
const GATEWAY_TOKEN = process.env.OPENCLAW_GATEWAY_TOKEN ?? process.env.OPENCLAW_GATEWAY_PASSWORD;
const ALLOW_INSECURE_REMOTE_GATEWAY = process.env.OPENCLAW_GATEWAY_ALLOW_INSECURE === "1";
const DASHBOARD_AUTH = resolveDashboardAuth(process.env.DASHCLAW_TOKEN, BIND);
const DB_PATH = process.env.DASHCLAW_DB ?? join(process.cwd(), "data", "dashclaw.db");
const DB_PATH = resolveWorkspacePath(
WORKSPACE_ROOT,
process.env.DASHCLAW_DB,
"data/dashclaw.db",
);
Comment thread
savva312 marked this conversation as resolved.
Outdated
const USE_FAKE = process.env.DASHCLAW_FAKE_GATEWAY === "1";
const READ_ONLY = process.env.DASHCLAW_READ_ONLY === "1";
const FAKE_GATEWAY_SCENARIO = parseFakeGatewayScenario(process.env.DASHCLAW_FAKE_GATEWAY_SCENARIO);
const WEB_DIST = process.env.DASHCLAW_WEB_DIST ?? join(process.cwd(), "apps/web/dist");
const WEB_DIST = resolveWorkspacePath(
WORKSPACE_ROOT,
process.env.DASHCLAW_WEB_DIST,
"apps/web/dist",
);
const SERVE_WEB = process.env.DASHCLAW_SERVE_WEB !== "0" && existsSync(join(WEB_DIST, "index.html"));

async function main(): Promise<void> {
Expand Down
15 changes: 15 additions & 0 deletions apps/server/src/startup-config.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";
import {
MIN_DASHBOARD_TOKEN_LENGTH,
isLoopbackBind,
resolveDashboardAuth,
resolveWorkspacePath,
} from "./startup-config.js";

describe("dashboard startup authentication", () => {
Expand Down Expand Up @@ -45,4 +47,17 @@ describe("dashboard startup authentication", () => {
"rejects non-loopback bind %s",
(bind) => expect(isLoopbackBind(bind)).toBe(false),
);

it("resolves default and configured runtime paths from the workspace root", () => {
const root = resolve("test-workspace");
expect(resolveWorkspacePath(root, undefined, "apps/web/dist")).toBe(
resolve(root, "apps/web/dist"),
);
expect(resolveWorkspacePath(root, "./runtime/dashclaw.db", "data/dashclaw.db")).toBe(
resolve(root, "runtime/dashclaw.db"),
);

const absolute = resolve(root, "external/runtime.db");
expect(resolveWorkspacePath(root, absolute, "data/dashclaw.db")).toBe(absolute);
});
});
9 changes: 9 additions & 0 deletions apps/server/src/startup-config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { randomBytes } from "node:crypto";
import { isIP } from "node:net";
import { resolve } from "node:path";

export const MIN_DASHBOARD_TOKEN_LENGTH = 32;

Expand Down Expand Up @@ -40,3 +41,11 @@ export function isLoopbackBind(value: string): boolean {
if (ipVersion === 4) return Number(unbracketed.split(".")[0]) === 127;
return false;
}

export function resolveWorkspacePath(
workspaceRoot: string,
configuredPath: string | undefined,
defaultPath: string,
): string {
return resolve(workspaceRoot, configuredPath ?? defaultPath);
}
Binary file added docs/images/dashclaw-command-center.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/dashclaw-social-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.