qclaw-cli 0.3.1 → 0.3.3

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.
Files changed (113) hide show
  1. package/CONTRIBUTING.md +1 -1
  2. package/README.aiclaw.md +8 -10
  3. package/README.aliclaw.md +8 -10
  4. package/README.amazonclaw.md +20 -0
  5. package/README.amzclaw.md +20 -0
  6. package/README.anthropicclaw.md +20 -0
  7. package/README.appleclaw.md +20 -0
  8. package/README.autoopenclaw.md +8 -10
  9. package/README.awsclaw.md +20 -0
  10. package/README.bdclaw.md +20 -0
  11. package/README.blclaw.md +20 -0
  12. package/README.bytclaw.md +20 -0
  13. package/README.claw-open.md +8 -10
  14. package/README.clawjs.md +1 -1
  15. package/README.coclaw.md +20 -0
  16. package/README.copaw.md +12 -0
  17. package/README.ddclaw.md +20 -0
  18. package/README.duclaw.md +20 -0
  19. package/README.dyclaw.md +20 -0
  20. package/README.easyclaw.md +20 -0
  21. package/README.fastclaw.md +8 -10
  22. package/README.fbclaw.md +20 -0
  23. package/README.googleclaw.md +20 -0
  24. package/README.hwclaw.md +20 -0
  25. package/README.jdclaw.md +20 -0
  26. package/README.kimiclaw.md +20 -0
  27. package/README.ksclaw.md +12 -0
  28. package/README.maxclaw.md +20 -0
  29. package/README.md +8 -10
  30. package/README.megaclaw.md +8 -10
  31. package/README.metaclaw.md +20 -0
  32. package/README.miclaw.md +20 -0
  33. package/README.msclaw.md +20 -0
  34. package/README.mtclaw.md +20 -0
  35. package/README.nflxclaw.md +20 -0
  36. package/README.nvdaclaw.md +20 -0
  37. package/README.open-claw.md +1 -1
  38. package/README.openaiclaw.md +20 -0
  39. package/README.openclaw-cli.md +1 -1
  40. package/README.openclaw-daemon.md +1 -1
  41. package/README.openclaw-gateway.md +1 -1
  42. package/README.openclaw-health.md +1 -1
  43. package/README.openclaw-helper.md +1 -1
  44. package/README.openclaw-install.md +1 -1
  45. package/README.openclaw-manage.md +1 -1
  46. package/README.openclaw-monitor.md +1 -1
  47. package/README.openclaw-run.md +1 -1
  48. package/README.openclaw-service.md +1 -1
  49. package/README.openclaw-setup.md +1 -1
  50. package/README.openclaw-start.md +1 -1
  51. package/README.openclaw-tools.md +1 -1
  52. package/README.openclaw-upgrade.md +1 -1
  53. package/README.openclaw-utils.md +1 -1
  54. package/README.openclaw-watch.md +1 -1
  55. package/README.pddclaw.md +20 -0
  56. package/README.qclaw-cli.md +8 -10
  57. package/README.qclaw.md +1 -1
  58. package/README.smartclaw.md +8 -10
  59. package/README.ttclaw.md +20 -0
  60. package/README.txclaw.md +20 -0
  61. package/README.uberclaw.md +20 -0
  62. package/README.volclaw.md +8 -10
  63. package/README.wxclaw.md +20 -0
  64. package/README.xclaw.md +12 -0
  65. package/README.zh-CN.md +2 -2
  66. package/README.zhclaw.md +20 -0
  67. package/dist/{chunk-LIZ6XXW3.js → chunk-U6JVOA5H.js} +1 -1
  68. package/dist/index.js +3 -3
  69. package/dist/{server-ZYSNFLSO.js → server-GHEIPQJV.js} +1 -1
  70. package/package.json +10 -5
  71. package/README.md.bak +0 -242
  72. package/app-dist/main.js +0 -300
  73. package/app-dist/package.json +0 -3
  74. package/app-dist/server-process.js +0 -95
  75. package/assets/demo.gif +0 -0
  76. package/assets/welcome.png +0 -0
  77. package/demo.tape +0 -28
  78. package/homebrew/README.md +0 -37
  79. package/homebrew/openclaw-cli.rb +0 -22
  80. package/homebrew/openclaw-doctor.rb +0 -26
  81. package/package.aiclaw.json +0 -25
  82. package/package.aliclaw.json +0 -25
  83. package/package.autoopenclaw.json +0 -25
  84. package/package.claw-open.json +0 -25
  85. package/package.clawjs.json +0 -25
  86. package/package.fastclaw.json +0 -25
  87. package/package.json.bak +0 -51
  88. package/package.megaclaw.json +0 -25
  89. package/package.open-claw.json +0 -25
  90. package/package.openclaw-cli.json +0 -51
  91. package/package.openclaw-daemon.json +0 -51
  92. package/package.openclaw-gateway.json +0 -51
  93. package/package.openclaw-health.json +0 -51
  94. package/package.openclaw-helper.json +0 -51
  95. package/package.openclaw-install.json +0 -51
  96. package/package.openclaw-manage.json +0 -51
  97. package/package.openclaw-monitor.json +0 -51
  98. package/package.openclaw-run.json +0 -51
  99. package/package.openclaw-service.json +0 -51
  100. package/package.openclaw-setup.json +0 -51
  101. package/package.openclaw-start.json +0 -51
  102. package/package.openclaw-tools.json +0 -51
  103. package/package.openclaw-upgrade.json +0 -50
  104. package/package.openclaw-utils.json +0 -51
  105. package/package.openclaw-watch.json +0 -51
  106. package/package.qclaw-cli.json +0 -25
  107. package/package.qclaw.json +0 -25
  108. package/package.smartclaw.json +0 -25
  109. package/package.volclaw.json +0 -25
  110. package/scripts/post-app-compile.cjs +0 -3
  111. package/scripts/post-app-compile.js +0 -7
  112. package/scripts/publish.sh +0 -120
  113. package/scripts/publish.sh.bak +0 -63
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "qclaw-cli",
3
- "version": "0.3.1",
4
- "description": "CLI and health-watch daemon for OpenClaw AI assistant gateway",
3
+ "version": "0.3.3",
4
+ "description": "AI assistant gateway watchdog \u2014 alias for openclaw-cli. Visit https://openclaw-cli.app",
5
5
  "keywords": [
6
6
  "openclaw",
7
- "cli",
7
+ "openclaw-cli",
8
8
  "ai",
9
9
  "daemon",
10
+ "gateway",
11
+ "watchdog",
10
12
  "health-check"
11
13
  ],
12
14
  "main": "./dist/index.js",
@@ -16,10 +18,13 @@
16
18
  "license": "MIT",
17
19
  "repository": {
18
20
  "type": "git",
19
- "url": "https://github.com/Sobranier/openclaw-doctor.git"
21
+ "url": "https://github.com/Sobranier/openclaw-cli.git"
20
22
  },
21
- "homepage": "https://github.com/Sobranier/openclaw-doctor",
23
+ "homepage": "https://openclaw-cli.app",
22
24
  "engines": {
23
25
  "node": ">=22"
26
+ },
27
+ "bugs": {
28
+ "url": "https://github.com/Sobranier/openclaw-cli/issues"
24
29
  }
25
30
  }
package/README.md.bak DELETED
@@ -1,242 +0,0 @@
1
- <p align="center">
2
- <img src="https://raw.githubusercontent.com/Sobranier/openclaw-doctor/main/assets/welcome.png" alt="OpenClaw Doctor" width="400" />
3
- </p>
4
-
5
- <h1 align="center">OpenClaw Doctor</h1>
6
-
7
- <p align="center">
8
- Keep your OpenClaw service alive. Automatically.
9
- </p>
10
-
11
- <p align="center">
12
- <a href="./README.zh-CN.md">中文文档</a>
13
- </p>
14
-
15
- <p align="center">
16
- <a href="https://www.npmjs.com/package/openclaw-doctor"><img src="https://img.shields.io/npm/v/openclaw-doctor?label=openclaw-doctor&color=red" alt="npm version" /></a>
17
- &nbsp;
18
- <a href="https://www.npmjs.com/package/openclaw-doctor"><img src="https://img.shields.io/npm/dm/openclaw-doctor?color=red" alt="npm downloads" /></a>
19
- &nbsp;
20
- <a href="https://www.npmjs.com/package/openclaw-cli"><img src="https://img.shields.io/npm/v/openclaw-cli?label=openclaw-cli&color=blue" alt="npm openclaw-cli" /></a>
21
- &nbsp;
22
- <a href="https://www.npmjs.com/package/openclaw-manage"><img src="https://img.shields.io/npm/v/openclaw-manage?label=openclaw-manage&color=green" alt="npm openclaw-manage" /></a>
23
- &nbsp;
24
- <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-brightgreen" alt="License" /></a>
25
- &nbsp;
26
- <img src="https://img.shields.io/node/v/openclaw-doctor" alt="Node version" />
27
- </p>
28
-
29
- ---
30
-
31
- ## Why Doctor?
32
-
33
- OpenClaw runs as a local daemon. When it crashes — network hiccup, system wake, bad update — your AI assistant goes dark. You notice only when you try to use it.
34
-
35
- **Doctor watches the gateway for you.** It detects failures, restarts the service automatically, and notifies you. No config, no babysitting.
36
-
37
- ## Primary Package
38
-
39
- > **`openclaw-cli` is the primary package.** All package names (`openclaw-doctor`, `openclaw-manage`, `qclaw`, `aiclaw`, etc.) point to the same tool — install whichever name feels right, it works identically.
40
- >
41
- > `openclaw-doctor` is the original name and is kept for backward compatibility.
42
-
43
- ## Get Started
44
-
45
- ```bash
46
- npm install -g openclaw-doctor
47
- openclaw-doctor watch -d
48
- ```
49
-
50
- That's it. Doctor is now running in the background. It reads everything from your existing OpenClaw setup — no configuration needed.
51
-
52
- <p align="center">
53
- <img src="https://raw.githubusercontent.com/Sobranier/openclaw-doctor/main/assets/demo.gif" alt="OpenClaw Doctor demo" width="700" />
54
- </p>
55
-
56
- ## Core Commands
57
-
58
- ```bash
59
- openclaw-doctor watch # Start monitoring (foreground)
60
- openclaw-doctor watch -d # Start monitoring (background)
61
- openclaw-doctor unwatch # Stop monitoring
62
-
63
- openclaw-doctor status # Quick health check
64
- ```
65
-
66
- These four cover 90% of daily use.
67
-
68
- ## Gateway Management
69
-
70
- ```bash
71
- openclaw-doctor gateway start # Start the OpenClaw gateway
72
- openclaw-doctor gateway stop # Stop the gateway
73
- openclaw-doctor gateway restart # Restart the gateway
74
- ```
75
-
76
- ## Diagnostics & Logs
77
-
78
- ```bash
79
- openclaw-doctor doctor # Full diagnostics (binary, gateway, channels)
80
- openclaw-doctor logs # View gateway logs
81
- openclaw-doctor logs --error # Error logs only
82
- openclaw-doctor logs --doctor # Doctor's own event logs
83
- openclaw-doctor dashboard # Web management UI → http://localhost:9090
84
- ```
85
-
86
- ## Install
87
-
88
- ```bash
89
- # npm (recommended)
90
- npm install -g openclaw-doctor
91
-
92
- # Run without installing
93
- npx openclaw-doctor status
94
- ```
95
-
96
- > Requires Node.js >= 22 (same as OpenClaw).
97
-
98
- ## How It Works
99
-
100
- Doctor auto-detects your OpenClaw installation — no manual config:
101
-
102
- - Reads `~/.openclaw/openclaw.json` for gateway port, channels, and agents
103
- - Finds the launchd service under `~/Library/LaunchAgents/`
104
- - Checks health via `openclaw health --json` (real gateway RPC, not HTTP ping)
105
- - Restarts via `launchctl kickstart` when consecutive failures exceed the threshold
106
-
107
- ## All Commands
108
-
109
- | Command | Description |
110
- |---------|-------------|
111
- | `watch` | Start health monitoring (foreground) |
112
- | `watch -d` | Start health monitoring (background) |
113
- | `watch -d --dashboard` | Background monitoring + web dashboard |
114
- | `unwatch` | Stop monitoring |
115
- | `gateway start` | Start the OpenClaw gateway |
116
- | `gateway stop` | Stop the gateway |
117
- | `gateway restart` | Restart the gateway |
118
- | `status` | Show gateway and channel health |
119
- | `status --json` | Machine-readable JSON output |
120
- | `doctor` | Run full diagnostics |
121
- | `dashboard` | Start web management UI |
122
- | `logs` | Show gateway logs |
123
- | `logs --error` | Error logs only |
124
- | `logs --doctor` | Doctor event logs |
125
-
126
- ## Configuration
127
-
128
- Auto-created at `~/.openclaw-doctor/config.json` on first run.
129
-
130
- ```json
131
- {
132
- "checkInterval": 30,
133
- "failThreshold": 3,
134
- "dashboardPort": 9090,
135
- "maxRestartsPerHour": 5,
136
- "openclawProfile": "default",
137
- "notify": {
138
- "webhook": {
139
- "enabled": false,
140
- "url": "",
141
- "bodyTemplate": "{\"msgtype\":\"text\",\"text\":{\"content\":\"{{message}}\"}}"
142
- },
143
- "system": {
144
- "enabled": true
145
- }
146
- }
147
- }
148
- ```
149
-
150
- | Field | Default | Description |
151
- |-------|---------|-------------|
152
- | `checkInterval` | `30` | Seconds between health checks |
153
- | `failThreshold` | `3` | Consecutive failures before restart |
154
- | `dashboardPort` | `9090` | Web dashboard port |
155
- | `maxRestartsPerHour` | `5` | Restart throttle (prevents restart loops) |
156
- | `openclawProfile` | `"default"` | OpenClaw profile to monitor |
157
- | `notify.webhook.url` | — | Webhook URL (DingTalk, Feishu, Slack, etc.) |
158
- | `notify.system.enabled` | `true` | macOS native notifications |
159
-
160
- ## Notifications
161
-
162
- Doctor covers the full restart lifecycle:
163
-
164
- | Event | Message |
165
- |-------|---------|
166
- | Started | "Doctor is watching your OpenClaw service" |
167
- | Degraded | "Service unhealthy (attempt 2/3)" |
168
- | Restarting | "Restarting gateway..." |
169
- | Recovered | "Gateway back online" |
170
- | Failed | "Restart failed: [error]" |
171
- | Throttled | "Too many restarts — manual intervention needed" |
172
- | Self-recovered | "Service recovered on its own" |
173
- | Stopped | "Doctor stopped" |
174
-
175
- **Channels:** Webhook (DingTalk, Feishu, Slack, custom) + macOS system notifications.
176
-
177
- ## Architecture
178
-
179
- ```
180
- +-----------------+
181
- | Notification |
182
- | (Webhook/OS) |
183
- +--------^--------+
184
- |
185
- +-------------+ CLI +--------+--------+ RPC +-----------+
186
- | OpenClaw | --------> | | ---------> | OpenClaw |
187
- | / Scripts | | openclaw-doctor | | Gateway |
188
- | / Skills | <-------- | (daemon) | <--------- | :18789 |
189
- +-------------+ stdout +--------+--------+ health +-----------+
190
- |
191
- +--------v--------+
192
- | ~/.openclaw/logs |
193
- | (read & analyze) |
194
- +-----------------+
195
- ```
196
-
197
- Doctor runs as a standalone daemon. If the calling process crashes, Doctor keeps running.
198
-
199
- ```bash
200
- openclaw-doctor status --json # Machine-readable — pipe into scripts or agents
201
- openclaw-doctor watch -d # Idempotent — safe to call repeatedly
202
- ```
203
-
204
- ## Roadmap
205
-
206
- - [x] Health check via `openclaw health --json` + auto-restart with throttling
207
- - [x] Auto-detect OpenClaw config (gateway port, channels, agents, launchd)
208
- - [x] Background daemon mode (`watch -d` / `unwatch`)
209
- - [x] Gateway management (`gateway start/stop/restart`)
210
- - [x] Read and display OpenClaw gateway logs
211
- - [x] Web status dashboard
212
- - [x] `--json` output for status
213
- - [ ] Notification system (Webhook + macOS)
214
- - [ ] `logs --tail` (real-time follow)
215
- - [ ] `config` command (get/set)
216
- - [ ] Multiple service monitoring
217
- - [ ] Linux systemd support
218
-
219
- ## Development
220
-
221
- ```bash
222
- git clone https://github.com/Sobranier/openclaw-doctor.git
223
- cd openclaw-doctor
224
- npm install
225
-
226
- npm run dev -- status # Quick test
227
- npm run dev -- watch # Foreground monitoring
228
- npm run dev -- watch -d # Background daemon
229
- npm run dev -- unwatch # Stop daemon
230
-
231
- npm run build # Build for distribution
232
- ```
233
-
234
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for the release and publish workflow.
235
-
236
- ## License
237
-
238
- [MIT](./LICENSE)
239
-
240
- ---
241
-
242
- Works with [OpenClaw](https://openclaw.ai) · Built for the OpenClaw ecosystem
package/app-dist/main.js DELETED
@@ -1,300 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- const electron_1 = require("electron");
37
- const path = __importStar(require("node:path"));
38
- const http = __importStar(require("node:http"));
39
- // ── Constants ──────────────────────────────────────────────────────────────
40
- const DASHBOARD_PORT = 9090;
41
- const DASHBOARD_URL = `http://localhost:${DASHBOARD_PORT}`;
42
- const HEALTH_INTERVAL_MS = 30000;
43
- // In dev: __dirname = app-dist/, script is app-dist/server-process.js
44
- // In packaged: __dirname = .../app.asar/app-dist/, script is same
45
- const SERVER_SCRIPT = path.join(__dirname, "server-process.js");
46
- // Icon paths (relative to app bundle Resources or dev project root)
47
- const ICON_PATH = path.join(electron_1.app.isPackaged
48
- ? path.join(process.resourcesPath, "assets/icon.iconset/icon_16x16@2x.png")
49
- : path.join(__dirname, "../assets/icon.iconset/icon_16x16@2x.png"));
50
- // ── State ──────────────────────────────────────────────────────────────────
51
- let tray = null;
52
- let mainWindow = null;
53
- let serverProc = null;
54
- let isHealthy = true;
55
- // ── Server lifecycle ───────────────────────────────────────────────────────
56
- /** Check if dashboard server is already running on the port */
57
- function isServerRunning() {
58
- return new Promise((resolve) => {
59
- http.get(`${DASHBOARD_URL}/api/status`, { timeout: 1000 }, (res) => {
60
- resolve(res.statusCode === 200);
61
- }).on("error", () => resolve(false))
62
- .on("timeout", () => resolve(false));
63
- });
64
- }
65
- function isPortInUse(port) {
66
- return new Promise((resolve) => {
67
- http.get(`http://127.0.0.1:${port}/api/status`, { timeout: 1000 }, (res) => {
68
- resolve(res.statusCode === 200);
69
- }).on("error", () => resolve(false));
70
- });
71
- }
72
- function findCLI() {
73
- // Try env PATH first, then common nvm paths
74
- const { execSync } = require("node:child_process");
75
- try {
76
- return execSync("which openclaw-doctor", { encoding: "utf-8" }).trim();
77
- }
78
- catch { }
79
- const home = process.env.HOME ?? "";
80
- const candidates = [
81
- `${home}/.nvm/versions/node/v24.14.0/bin/openclaw-doctor`,
82
- `${home}/.nvm/versions/node/v22.13.0/bin/openclaw-doctor`,
83
- `/usr/local/bin/openclaw-doctor`,
84
- `/opt/homebrew/bin/openclaw-doctor`,
85
- ];
86
- const { existsSync } = require("node:fs");
87
- return candidates.find(existsSync) ?? null;
88
- }
89
- async function startServer() {
90
- if (await isPortInUse(DASHBOARD_PORT)) {
91
- console.log("[app] dashboard already running, reusing");
92
- return;
93
- }
94
- const cli = findCLI();
95
- if (!cli) {
96
- console.error("[app] openclaw-doctor CLI not found — install with: npm install -g openclaw-doctor");
97
- return;
98
- }
99
- // Use system node (not Electron's node) to run the CLI
100
- const { spawnSync: _ss, spawn } = require("node:child_process");
101
- // Find system node
102
- let nodeExec = "/usr/local/bin/node";
103
- try {
104
- const nvmNode = _ss("bash", ["-lc", "which node"], { encoding: "utf-8" });
105
- if (nvmNode.stdout?.trim())
106
- nodeExec = nvmNode.stdout.trim();
107
- }
108
- catch { }
109
- console.log(`[app] Starting: ${nodeExec} ${cli} monitor`);
110
- serverProc = spawn(nodeExec, [cli, "monitor"], {
111
- env: { ...process.env, PATH: `/usr/local/bin:/opt/homebrew/bin:${process.env.PATH}` },
112
- stdio: "pipe",
113
- });
114
- serverProc.stderr?.on("data", (d) => console.error("[monitor]", d.toString().trim()));
115
- serverProc.stdout?.on("data", (d) => console.log("[monitor]", d.toString().trim()));
116
- serverProc.on("exit", (code) => {
117
- console.log(`[app] monitor exited (code ${code}), retry in 3s`);
118
- serverProc = null;
119
- setTimeout(startServer, 3000);
120
- });
121
- }
122
- async function ensureServer() {
123
- // If already running (e.g. openclaw-doctor watch --dashboard), reuse it
124
- const already = await isServerRunning();
125
- if (already) {
126
- console.log("[app] Dashboard server already running, reusing existing instance");
127
- return;
128
- }
129
- startServer();
130
- await waitForServer();
131
- }
132
- function waitForServer(retries = 20) {
133
- return new Promise((resolve, reject) => {
134
- let attempts = 0;
135
- const check = () => {
136
- http
137
- .get(`${DASHBOARD_URL}/api/status`, { timeout: 2000 }, (res) => {
138
- if (res.statusCode === 200)
139
- return resolve();
140
- retry();
141
- })
142
- .on("error", retry);
143
- };
144
- const retry = () => {
145
- if (++attempts >= retries)
146
- return reject(new Error("Dashboard server did not start"));
147
- setTimeout(check, 500);
148
- };
149
- check();
150
- });
151
- }
152
- // ── Window ─────────────────────────────────────────────────────────────────
153
- function createWindow() {
154
- mainWindow = new electron_1.BrowserWindow({
155
- width: 1024,
156
- height: 720,
157
- minWidth: 800,
158
- minHeight: 600,
159
- title: "OpenClaw Doctor",
160
- backgroundColor: "#050810",
161
- webPreferences: { contextIsolation: true },
162
- show: false,
163
- });
164
- mainWindow.loadURL(DASHBOARD_URL);
165
- mainWindow.once("ready-to-show", () => mainWindow?.show());
166
- mainWindow.on("close", (e) => {
167
- e.preventDefault();
168
- mainWindow?.hide();
169
- });
170
- }
171
- function showWindow() {
172
- if (!mainWindow)
173
- createWindow();
174
- else {
175
- mainWindow.show();
176
- mainWindow.focus();
177
- }
178
- }
179
- // ── Tray ───────────────────────────────────────────────────────────────────
180
- function buildTrayMenu() {
181
- return electron_1.Menu.buildFromTemplate([
182
- {
183
- label: "OpenClaw Doctor",
184
- enabled: false,
185
- },
186
- {
187
- label: `Status: ${isHealthy ? "🟢 HEALTHY" : "🔴 UNREACHABLE"}`,
188
- enabled: false,
189
- },
190
- { type: "separator" },
191
- { label: "Open Dashboard", click: () => showWindow() },
192
- { label: "Open in Browser", click: () => electron_1.shell.openExternal(DASHBOARD_URL) },
193
- { type: "separator" },
194
- {
195
- label: "Restart Gateway",
196
- click: async () => {
197
- try {
198
- await fetch(`${DASHBOARD_URL}/api/restart`, { method: "POST" });
199
- }
200
- catch { }
201
- },
202
- },
203
- {
204
- label: "Run Doctor Fix",
205
- click: async () => {
206
- try {
207
- await fetch(`${DASHBOARD_URL}/api/doctor`, { method: "POST" });
208
- }
209
- catch { }
210
- },
211
- },
212
- { type: "separator" },
213
- {
214
- label: "Start at Login",
215
- type: "checkbox",
216
- checked: electron_1.app.getLoginItemSettings().openAtLogin,
217
- click: (item) => {
218
- electron_1.app.setLoginItemSettings({ openAtLogin: item.checked });
219
- },
220
- },
221
- { type: "separator" },
222
- { label: "Quit", click: () => electron_1.app.exit(0) },
223
- ]);
224
- }
225
- function createTray() {
226
- let icon;
227
- try {
228
- icon = electron_1.nativeImage.createFromPath(ICON_PATH);
229
- // Mark as template for macOS menubar (auto dark/light adaptation)
230
- icon.setTemplateImage(true);
231
- }
232
- catch {
233
- icon = electron_1.nativeImage.createEmpty();
234
- }
235
- tray = new electron_1.Tray(icon);
236
- tray.setToolTip("OpenClaw Doctor — starting...");
237
- tray.setContextMenu(buildTrayMenu());
238
- tray.on("double-click", () => showWindow());
239
- }
240
- function updateTray(healthy) {
241
- if (!tray)
242
- return;
243
- isHealthy = healthy;
244
- tray.setToolTip(`OpenClaw Doctor — ${healthy ? "HEALTHY" : "UNREACHABLE"}`);
245
- tray.setContextMenu(buildTrayMenu());
246
- }
247
- // ── Health polling ─────────────────────────────────────────────────────────
248
- async function pollHealth() {
249
- try {
250
- const res = await fetch(`${DASHBOARD_URL}/api/status`);
251
- const data = (await res.json());
252
- const wasHealthy = isHealthy;
253
- updateTray(data.healthy);
254
- if (!data.healthy && wasHealthy) {
255
- new electron_1.Notification({
256
- title: "OpenClaw Doctor",
257
- body: "Gateway is down — attempting auto-restart...",
258
- }).show();
259
- }
260
- else if (data.healthy && !wasHealthy) {
261
- new electron_1.Notification({
262
- title: "OpenClaw Doctor",
263
- body: "Gateway is back online ✓",
264
- }).show();
265
- }
266
- }
267
- catch {
268
- updateTray(false);
269
- }
270
- }
271
- // ── App lifecycle ──────────────────────────────────────────────────────────
272
- electron_1.app.on("ready", async () => {
273
- // macOS: don't show in Dock
274
- if (process.platform === "darwin")
275
- electron_1.app.dock?.hide();
276
- createTray();
277
- try {
278
- await ensureServer();
279
- }
280
- catch {
281
- console.warn("[app] server warmup timed out, opening anyway");
282
- }
283
- createWindow();
284
- await pollHealth();
285
- setInterval(pollHealth, HEALTH_INTERVAL_MS);
286
- });
287
- electron_1.app.on("window-all-closed", () => {
288
- // Keep app alive in tray (do nothing — don't quit)
289
- });
290
- electron_1.app.on("activate", () => {
291
- showWindow();
292
- });
293
- // Prevent second instance
294
- const gotLock = electron_1.app.requestSingleInstanceLock();
295
- if (!gotLock) {
296
- electron_1.app.quit();
297
- }
298
- else {
299
- electron_1.app.on("second-instance", () => showWindow());
300
- }
@@ -1,3 +0,0 @@
1
- {
2
- "type": "commonjs"
3
- }
@@ -1,95 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- /**
37
- * Forked by Electron main process.
38
- * Starts the dashboard HTTP server directly using Node's http module.
39
- * This file is compiled to CommonJS and runs as a standalone child process.
40
- */
41
- const http = __importStar(require("node:http"));
42
- const fs = __importStar(require("node:fs"));
43
- const path = __importStar(require("node:path"));
44
- const cp = __importStar(require("node:child_process"));
45
- const PORT = Number(process.env.DASHBOARD_PORT ?? 9090);
46
- // Find openclaw-doctor CLI (the installed npm package)
47
- function findOpenclawDoctor() {
48
- // 1. Check if there's a global install
49
- try {
50
- const which = cp.execSync("which openclaw-doctor", { encoding: "utf-8" }).trim();
51
- if (which)
52
- return which;
53
- }
54
- catch { }
55
- // 2. Check common paths
56
- const candidates = [
57
- "/usr/local/bin/openclaw-doctor",
58
- "/opt/homebrew/bin/openclaw-doctor",
59
- path.join(process.env.HOME ?? "", ".nvm/versions/node/v24.14.0/bin/openclaw-doctor"),
60
- path.join(process.env.HOME ?? "", ".nvm/versions/node/v22.13.0/bin/openclaw-doctor"),
61
- ];
62
- for (const c of candidates) {
63
- if (fs.existsSync(c))
64
- return c;
65
- }
66
- return null;
67
- }
68
- // Strategy: spawn openclaw-doctor monitor as a subprocess
69
- const cli = findOpenclawDoctor();
70
- if (cli) {
71
- console.log(`[server-process] Starting via CLI: ${cli} monitor`);
72
- const child = cp.spawn(process.execPath, [cli, "monitor"], {
73
- env: { ...process.env },
74
- stdio: "inherit",
75
- });
76
- child.on("exit", (code) => {
77
- console.log(`[server-process] CLI exited (${code})`);
78
- process.exit(code ?? 1);
79
- });
80
- }
81
- else {
82
- // Fallback: minimal inline HTTP server returning status
83
- console.warn("[server-process] openclaw-doctor CLI not found, starting fallback server");
84
- const server = http.createServer((req, res) => {
85
- if (req.url === "/api/status") {
86
- res.writeHead(200, { "Content-Type": "application/json" });
87
- res.end(JSON.stringify({ healthy: false, gateway: false, channels: [], agents: [], durationMs: 0, error: "CLI not found" }));
88
- }
89
- else {
90
- res.writeHead(200, { "Content-Type": "text/html" });
91
- res.end("<h1>OpenClaw Doctor</h1><p>CLI not found. Please install openclaw-doctor.</p>");
92
- }
93
- });
94
- server.listen(PORT, () => console.log(`[server-process] Fallback server on :${PORT}`));
95
- }
package/assets/demo.gif DELETED
Binary file
Binary file