oc-inspector 1.4.0 → 1.5.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kirill Shidenko
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 CHANGED
@@ -1,5 +1,7 @@
1
1
  # oc-inspector
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/oc-inspector)](https://www.npmjs.com/package/oc-inspector) [![license](https://img.shields.io/github/license/kshidenko/openclaw-inspector)](LICENSE) [![AI Agents](https://img.shields.io/badge/AI_Agents-AGENTS.md-blue)](AGENTS.md)
4
+
3
5
  A debugging and monitoring tool for [OpenClaw](https://openclaw.ai) that helps you understand **where your tokens and money are going**.
4
6
 
5
7
  When working with LLM agents you often have no visibility into what's actually happening under the hood — how many tokens each request burns, which models cost the most, what system prompts look like, how tool calls are structured. `oc-inspector` sits between OpenClaw and your LLM providers as a transparent proxy, capturing every request and response in real time so you can see the full picture: token usage, costs, message flow, thinking blocks, tool calls — everything in a clear, human-readable format.
@@ -31,7 +33,7 @@ Use it to:
31
33
  npx oc-inspector
32
34
  ```
33
35
 
34
- Starts the inspector daemon in background on `localhost:18800` with a live web dashboard.
36
+ Starts the inspector daemon in background on `localhost:3000` with a live web dashboard.
35
37
 
36
38
  ---
37
39
 
@@ -110,6 +112,24 @@ oc-inspector stop
110
112
  oc-inspector restart
111
113
  ```
112
114
 
115
+ ### Autostart (run on login/boot)
116
+
117
+ ```bash
118
+ # Install autostart service (launchd on macOS, systemd on Linux)
119
+ oc-inspector install
120
+
121
+ # Install with custom port
122
+ oc-inspector install --port 9000
123
+
124
+ # Remove autostart
125
+ oc-inspector uninstall
126
+
127
+ # Check autostart status
128
+ oc-inspector status
129
+ ```
130
+
131
+ The inspector will start automatically when you log in (macOS) or boot (Linux), and restart if it crashes.
132
+
113
133
  ### CLI Commands
114
134
 
115
135
  | Command | Description |
@@ -127,6 +147,8 @@ oc-inspector restart
127
147
  | `providers` | List detected providers and their upstream URLs |
128
148
  | `config` | Show `.inspector.json` path and contents |
129
149
  | `logs` | Show daemon log output |
150
+ | `install` | Install autostart — run inspector on login/boot |
151
+ | `uninstall` | Remove autostart service |
130
152
 
131
153
  ### Examples
132
154
 
@@ -157,7 +179,7 @@ oc-inspector logs --lines 100
157
179
 
158
180
  | Flag | Description | Default |
159
181
  |------|-------------|---------|
160
- | `--port <number>` | Port for the inspector proxy | `18800` |
182
+ | `--port <number>` | Port for the inspector proxy | `3000` |
161
183
  | `--open` | Auto-open the dashboard in a browser | `false` |
162
184
  | `--config <path>` | Custom path to `openclaw.json` | `~/.openclaw/openclaw.json` |
163
185
  | `--json` | Output as JSON (for `stats`, `status`, `providers`, `history`, `pricing`) | `false` |
@@ -169,10 +191,10 @@ oc-inspector logs --lines 100
169
191
 
170
192
  ## How It Works
171
193
 
172
- 1. **Start** — `oc-inspector` launches a background daemon with an HTTP reverse proxy on `localhost:18800`. The terminal is free immediately.
194
+ 1. **Start** — `oc-inspector` launches a background daemon with an HTTP reverse proxy on `localhost:3000`. The terminal is free immediately.
173
195
  2. **Enable** — Click **Enable** in the web UI (or run `oc-inspector enable`). This:
174
196
  - Backs up `openclaw.json`
175
- - Rewrites each provider's `baseUrl` to route through `http://127.0.0.1:18800/{provider}/...`
197
+ - Rewrites each provider's `baseUrl` to route through `http://127.0.0.1:3000/{provider}/...`
176
198
  - Restarts the OpenClaw gateway
177
199
  3. **Intercept** — All LLM API traffic flows through the inspector:
178
200
  - Requests/responses are logged in real time
package/bin/cli.mjs CHANGED
@@ -21,7 +21,7 @@
21
21
  * logs Tail daemon log file
22
22
  *
23
23
  * Options:
24
- * --port <number> Port for the inspector proxy (default: 18800)
24
+ * --port <number> Port for the inspector proxy (default: 3000)
25
25
  * --open Auto-open the dashboard in a browser
26
26
  * --config <path> Custom path to openclaw.json
27
27
  * --json Output as JSON (for stats/status)
@@ -66,7 +66,7 @@ const LOG_FILE = join(INSPECTOR_DIR, "inspector.log");
66
66
  function parseArgs(argv) {
67
67
  const opts = {
68
68
  command: "start",
69
- port: 18800,
69
+ port: 3000,
70
70
  open: false,
71
71
  config: undefined,
72
72
  json: false,
@@ -80,6 +80,7 @@ function parseArgs(argv) {
80
80
  "enable", "disable", "status",
81
81
  "stats", "providers", "history", "pricing", "config",
82
82
  "logs", "help", "_serve",
83
+ "install", "uninstall",
83
84
  ]);
84
85
  for (let i = 0; i < argv.length; i++) {
85
86
  const arg = argv[i];
@@ -124,9 +125,11 @@ if (opts.help) {
124
125
  pricing Show model pricing table
125
126
  config Show .inspector.json path and status
126
127
  logs Show daemon log output
128
+ install Install autostart (run on login/boot)
129
+ uninstall Remove autostart service
127
130
 
128
131
  \x1b[1mOptions:\x1b[0m
129
- --port <number> Port for the inspector proxy (default: 18800)
132
+ --port <number> Port for the inspector proxy (default: 3000)
130
133
  --open Auto-open the dashboard in a browser
131
134
  --config <path> Custom path to openclaw.json
132
135
  --json Output as JSON (for stats, status, providers, history)
@@ -148,6 +151,8 @@ if (opts.help) {
148
151
  npx oc-inspector history --days 30 # Last 30 days
149
152
  npx oc-inspector pricing # Show pricing table
150
153
  npx oc-inspector logs # Show daemon logs
154
+ npx oc-inspector install # Autostart on login/boot
155
+ npx oc-inspector uninstall # Remove autostart
151
156
  `);
152
157
  process.exit(0);
153
158
  }
@@ -202,6 +207,12 @@ if (opts.command === "logs") {
202
207
  process.exit(0);
203
208
  }
204
209
 
210
+ // install / uninstall: autostart management
211
+ if (opts.command === "install" || opts.command === "uninstall") {
212
+ await runAutostart(opts);
213
+ process.exit(0);
214
+ }
215
+
205
216
  // Remote commands: talk to a running inspector
206
217
  const remoteCommands = new Set(["stats", "providers", "history", "pricing", "config"]);
207
218
  if (remoteCommands.has(opts.command)) {
@@ -523,6 +534,49 @@ async function restoreConfigOnExit(cmdOpts = {}) {
523
534
  }
524
535
  }
525
536
 
537
+ // ═══════════════════════════════════════════════════════════════
538
+ // Autostart (install / uninstall)
539
+ // ═══════════════════════════════════════════════════════════════
540
+
541
+ /**
542
+ * Handle `install` and `uninstall` commands for system autostart.
543
+ *
544
+ * Uses launchd on macOS, systemd on Linux.
545
+ *
546
+ * @param {object} opts - Parsed CLI options.
547
+ */
548
+ async function runAutostart(opts) {
549
+ const { install, uninstall, autostartStatus } = await import("../src/autostart.mjs");
550
+
551
+ console.log("");
552
+
553
+ if (opts.command === "install") {
554
+ const result = install({ port: opts.port, config: opts.config });
555
+ if (result.ok) {
556
+ console.log(` \x1b[32m✓\x1b[0m ${result.message}`);
557
+ console.log(` \x1b[90m Service file: ${result.path}\x1b[0m`);
558
+ console.log("");
559
+ console.log(" Inspector will now start automatically on login.");
560
+ console.log(" Use \x1b[36moc-inspector uninstall\x1b[0m to remove.");
561
+ } else {
562
+ console.log(` \x1b[31m✗\x1b[0m ${result.message}`);
563
+ }
564
+ console.log("");
565
+ return;
566
+ }
567
+
568
+ if (opts.command === "uninstall") {
569
+ const result = uninstall();
570
+ if (result.ok) {
571
+ console.log(` \x1b[32m✓\x1b[0m ${result.message}`);
572
+ } else {
573
+ console.log(` \x1b[31m✗\x1b[0m ${result.message}`);
574
+ }
575
+ console.log("");
576
+ return;
577
+ }
578
+ }
579
+
526
580
  // ═══════════════════════════════════════════════════════════════
527
581
  // Local commands (enable / disable / status)
528
582
  // ═══════════════════════════════════════════════════════════════
@@ -557,6 +611,17 @@ async function runLocalCommand(opts) {
557
611
  console.log(` Providers: ${st.providers.join(", ")}`);
558
612
  }
559
613
 
614
+ // Autostart status
615
+ try {
616
+ const { autostartStatus } = await import("../src/autostart.mjs");
617
+ const as = autostartStatus();
618
+ const asDot = as.installed ? "\x1b[32m●\x1b[0m" : "\x1b[90m●\x1b[0m";
619
+ const asLabel = as.installed
620
+ ? (as.running ? `\x1b[32minstalled & running\x1b[0m (${as.platform})` : `\x1b[33minstalled, not running\x1b[0m (${as.platform})`)
621
+ : `\x1b[90mnot installed\x1b[0m \x1b[90m(use: oc-inspector install)\x1b[0m`;
622
+ console.log(` ${asDot} Autostart: ${asLabel}`);
623
+ } catch { /* autostart module not available */ }
624
+
560
625
  if (daemon.alive) {
561
626
  console.log(`\n \x1b[90mDashboard: http://127.0.0.1:${st.port || opts.port}\x1b[0m`);
562
627
  }
@@ -673,6 +738,8 @@ function printCommandsHelp() {
673
738
  console.log(` ${C}oc-inspector providers${R} Active providers list`);
674
739
  console.log(` ${C}oc-inspector config${R} Inspector config info`);
675
740
  console.log(` ${C}oc-inspector logs${R} Daemon log output`);
741
+ console.log(` ${C}oc-inspector install${R} Autostart on login/boot`);
742
+ console.log(` ${C}oc-inspector uninstall${R} Remove autostart`);
676
743
  console.log(` ${C}oc-inspector help${R} Show this help`);
677
744
  console.log("");
678
745
  console.log(` ${D}Use --json for machine-readable output. See oc-inspector --help for all options.${R}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oc-inspector",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Real-time API traffic inspector for OpenClaw — intercepts LLM provider requests, shows token usage, costs, and message flow in a live web dashboard.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,379 @@
1
+ /**
2
+ * Autostart management for OpenClaw Inspector.
3
+ *
4
+ * Installs/uninstalls a system service so the inspector starts
5
+ * automatically on login (macOS) or boot (Linux).
6
+ *
7
+ * Supported platforms:
8
+ * - macOS: launchd (~/Library/LaunchAgents/)
9
+ * - Linux: systemd user unit (~/.config/systemd/user/)
10
+ *
11
+ * @module autostart
12
+ */
13
+
14
+ import { writeFileSync, readFileSync, unlinkSync, existsSync, mkdirSync } from "node:fs";
15
+ import { join } from "node:path";
16
+ import { homedir, platform } from "node:os";
17
+ import { execSync } from "node:child_process";
18
+ import { fileURLToPath } from "node:url";
19
+
20
+ /** launchd plist label / systemd unit name. */
21
+ const SERVICE_ID = "com.openclaw.inspector";
22
+
23
+ /** systemd unit file name. */
24
+ const SYSTEMD_UNIT = "oc-inspector.service";
25
+
26
+ /**
27
+ * Resolve the absolute path to the CLI entry point (bin/cli.mjs).
28
+ *
29
+ * @returns {string} Absolute path to cli.mjs.
30
+ */
31
+ function cliPath() {
32
+ const thisFile = fileURLToPath(import.meta.url);
33
+ return join(thisFile, "..", "..", "bin", "cli.mjs");
34
+ }
35
+
36
+ /**
37
+ * Resolve the absolute path to the current Node.js binary.
38
+ *
39
+ * @returns {string} Absolute path to `node`.
40
+ */
41
+ function nodePath() {
42
+ return process.execPath;
43
+ }
44
+
45
+ // ════════════════════════════════════════════════════════════════
46
+ // macOS — launchd
47
+ // ════════════════════════════════════════════════════════════════
48
+
49
+ /**
50
+ * Path to the launchd plist file.
51
+ *
52
+ * @returns {string} ~/Library/LaunchAgents/com.openclaw.inspector.plist
53
+ */
54
+ function plistPath() {
55
+ return join(homedir(), "Library", "LaunchAgents", `${SERVICE_ID}.plist`);
56
+ }
57
+
58
+ /**
59
+ * Generate a launchd plist XML string.
60
+ *
61
+ * The plist tells launchd to run `node bin/cli.mjs _serve --port <port>`
62
+ * at login, keep it alive (restart on crash), and log stdout/stderr.
63
+ *
64
+ * @param {object} opts
65
+ * @param {number} opts.port - Inspector proxy port.
66
+ * @param {string} [opts.config] - Custom openclaw.json path.
67
+ * @returns {string} Plist XML content.
68
+ */
69
+ function buildPlist({ port, config }) {
70
+ const args = [nodePath(), cliPath(), "_serve", "--port", String(port)];
71
+ if (config) args.push("--config", config);
72
+
73
+ const logDir = join(homedir(), ".openclaw", ".inspector-runtime");
74
+ const stdout = join(logDir, "launchd-stdout.log");
75
+ const stderr = join(logDir, "launchd-stderr.log");
76
+
77
+ const argsXml = args.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n");
78
+
79
+ return `<?xml version="1.0" encoding="UTF-8"?>
80
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
81
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
82
+ <plist version="1.0">
83
+ <dict>
84
+ <key>Label</key>
85
+ <string>${SERVICE_ID}</string>
86
+
87
+ <key>ProgramArguments</key>
88
+ <array>
89
+ ${argsXml}
90
+ </array>
91
+
92
+ <key>RunAtLoad</key>
93
+ <true/>
94
+
95
+ <key>KeepAlive</key>
96
+ <true/>
97
+
98
+ <key>StandardOutPath</key>
99
+ <string>${escapeXml(stdout)}</string>
100
+
101
+ <key>StandardErrorPath</key>
102
+ <string>${escapeXml(stderr)}</string>
103
+
104
+ <key>EnvironmentVariables</key>
105
+ <dict>
106
+ <key>PATH</key>
107
+ <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
108
+ </dict>
109
+ </dict>
110
+ </plist>
111
+ `;
112
+ }
113
+
114
+ /**
115
+ * Escape special XML characters.
116
+ *
117
+ * @param {string} s - Raw string.
118
+ * @returns {string} XML-safe string.
119
+ */
120
+ function escapeXml(s) {
121
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
122
+ }
123
+
124
+ /**
125
+ * Install the launchd agent on macOS.
126
+ *
127
+ * @param {object} opts
128
+ * @param {number} opts.port - Inspector port.
129
+ * @param {string} [opts.config] - Custom openclaw.json path.
130
+ * @returns {{ ok: boolean, message: string, path: string }}
131
+ */
132
+ function installLaunchd({ port, config }) {
133
+ const path = plistPath();
134
+ const dir = join(homedir(), "Library", "LaunchAgents");
135
+ mkdirSync(dir, { recursive: true });
136
+
137
+ // Ensure log directory exists
138
+ mkdirSync(join(homedir(), ".openclaw", ".inspector-runtime"), { recursive: true });
139
+
140
+ // Unload if already loaded
141
+ try {
142
+ execSync(`launchctl unload "${path}" 2>/dev/null`, { stdio: "pipe" });
143
+ } catch { /* not loaded — fine */ }
144
+
145
+ // Write plist
146
+ const content = buildPlist({ port, config });
147
+ writeFileSync(path, content, "utf-8");
148
+
149
+ // Load the agent
150
+ try {
151
+ execSync(`launchctl load "${path}"`, { stdio: "pipe" });
152
+ } catch (err) {
153
+ return { ok: false, message: `Plist written but launchctl load failed: ${err.message}`, path };
154
+ }
155
+
156
+ return { ok: true, message: "Autostart installed (launchd)", path };
157
+ }
158
+
159
+ /**
160
+ * Uninstall the launchd agent on macOS.
161
+ *
162
+ * @returns {{ ok: boolean, message: string }}
163
+ */
164
+ function uninstallLaunchd() {
165
+ const path = plistPath();
166
+ if (!existsSync(path)) {
167
+ return { ok: true, message: "Autostart was not installed" };
168
+ }
169
+
170
+ // Unload
171
+ try {
172
+ execSync(`launchctl unload "${path}" 2>/dev/null`, { stdio: "pipe" });
173
+ } catch { /* ignore */ }
174
+
175
+ // Remove file
176
+ try {
177
+ unlinkSync(path);
178
+ } catch (err) {
179
+ return { ok: false, message: `Failed to remove plist: ${err.message}` };
180
+ }
181
+
182
+ return { ok: true, message: "Autostart removed (launchd)" };
183
+ }
184
+
185
+ /**
186
+ * Check if the launchd agent is installed.
187
+ *
188
+ * @returns {{ installed: boolean, running: boolean, path: string }}
189
+ */
190
+ function statusLaunchd() {
191
+ const path = plistPath();
192
+ const installed = existsSync(path);
193
+ let running = false;
194
+ if (installed) {
195
+ try {
196
+ const out = execSync(`launchctl list ${SERVICE_ID} 2>/dev/null`, { stdio: "pipe" }).toString();
197
+ running = !out.includes("Could not find");
198
+ } catch {
199
+ running = false;
200
+ }
201
+ }
202
+ return { installed, running, path };
203
+ }
204
+
205
+ // ════════════════════════════════════════════════════════════════
206
+ // Linux — systemd user unit
207
+ // ════════════════════════════════════════════════════════════════
208
+
209
+ /**
210
+ * Path to the systemd user unit file.
211
+ *
212
+ * @returns {string} ~/.config/systemd/user/oc-inspector.service
213
+ */
214
+ function unitPath() {
215
+ return join(homedir(), ".config", "systemd", "user", SYSTEMD_UNIT);
216
+ }
217
+
218
+ /**
219
+ * Generate a systemd user unit file content.
220
+ *
221
+ * @param {object} opts
222
+ * @param {number} opts.port - Inspector proxy port.
223
+ * @param {string} [opts.config] - Custom openclaw.json path.
224
+ * @returns {string} Unit file content.
225
+ */
226
+ function buildUnit({ port, config }) {
227
+ const args = [cliPath(), "_serve", "--port", String(port)];
228
+ if (config) args.push("--config", config);
229
+
230
+ return `[Unit]
231
+ Description=OpenClaw Inspector — LLM API traffic monitor
232
+ After=network.target
233
+
234
+ [Service]
235
+ Type=simple
236
+ ExecStart=${nodePath()} ${args.join(" ")}
237
+ Restart=on-failure
238
+ RestartSec=5
239
+ Environment=NODE_ENV=production
240
+
241
+ [Install]
242
+ WantedBy=default.target
243
+ `;
244
+ }
245
+
246
+ /**
247
+ * Install the systemd user service on Linux.
248
+ *
249
+ * @param {object} opts
250
+ * @param {number} opts.port - Inspector port.
251
+ * @param {string} [opts.config] - Custom openclaw.json path.
252
+ * @returns {{ ok: boolean, message: string, path: string }}
253
+ */
254
+ function installSystemd({ port, config }) {
255
+ const path = unitPath();
256
+ const dir = join(homedir(), ".config", "systemd", "user");
257
+ mkdirSync(dir, { recursive: true });
258
+
259
+ // Write unit
260
+ const content = buildUnit({ port, config });
261
+ writeFileSync(path, content, "utf-8");
262
+
263
+ // Reload and enable
264
+ try {
265
+ execSync("systemctl --user daemon-reload", { stdio: "pipe" });
266
+ execSync(`systemctl --user enable ${SYSTEMD_UNIT}`, { stdio: "pipe" });
267
+ execSync(`systemctl --user restart ${SYSTEMD_UNIT}`, { stdio: "pipe" });
268
+ } catch (err) {
269
+ return { ok: false, message: `Unit written but systemctl failed: ${err.message}`, path };
270
+ }
271
+
272
+ return { ok: true, message: "Autostart installed (systemd)", path };
273
+ }
274
+
275
+ /**
276
+ * Uninstall the systemd user service on Linux.
277
+ *
278
+ * @returns {{ ok: boolean, message: string }}
279
+ */
280
+ function uninstallSystemd() {
281
+ const path = unitPath();
282
+ if (!existsSync(path)) {
283
+ return { ok: true, message: "Autostart was not installed" };
284
+ }
285
+
286
+ try {
287
+ execSync(`systemctl --user stop ${SYSTEMD_UNIT} 2>/dev/null`, { stdio: "pipe" });
288
+ execSync(`systemctl --user disable ${SYSTEMD_UNIT} 2>/dev/null`, { stdio: "pipe" });
289
+ } catch { /* ignore */ }
290
+
291
+ try {
292
+ unlinkSync(path);
293
+ execSync("systemctl --user daemon-reload", { stdio: "pipe" });
294
+ } catch (err) {
295
+ return { ok: false, message: `Failed to remove unit: ${err.message}` };
296
+ }
297
+
298
+ return { ok: true, message: "Autostart removed (systemd)" };
299
+ }
300
+
301
+ /**
302
+ * Check if the systemd user service is installed.
303
+ *
304
+ * @returns {{ installed: boolean, running: boolean, path: string }}
305
+ */
306
+ function statusSystemd() {
307
+ const path = unitPath();
308
+ const installed = existsSync(path);
309
+ let running = false;
310
+ if (installed) {
311
+ try {
312
+ const out = execSync(`systemctl --user is-active ${SYSTEMD_UNIT} 2>/dev/null`, { stdio: "pipe" }).toString().trim();
313
+ running = out === "active";
314
+ } catch {
315
+ running = false;
316
+ }
317
+ }
318
+ return { installed, running, path };
319
+ }
320
+
321
+ // ════════════════════════════════════════════════════════════════
322
+ // Public API — platform-agnostic
323
+ // ════════════════════════════════════════════════════════════════
324
+
325
+ /**
326
+ * Install autostart service for the current platform.
327
+ *
328
+ * @param {object} opts
329
+ * @param {number} opts.port - Inspector proxy port (default: 3000).
330
+ * @param {string} [opts.config] - Custom openclaw.json path.
331
+ * @returns {{ ok: boolean, message: string, path?: string }}
332
+ *
333
+ * @throws {Error} If the platform is not supported.
334
+ *
335
+ * Example:
336
+ * >>> import { install } from './autostart.mjs';
337
+ * >>> const result = install({ port: 3000 });
338
+ * >>> console.log(result.message);
339
+ */
340
+ export function install({ port = 3000, config } = {}) {
341
+ const os = platform();
342
+ if (os === "darwin") return installLaunchd({ port, config });
343
+ if (os === "linux") return installSystemd({ port, config });
344
+ return { ok: false, message: `Unsupported platform: ${os}. Only macOS and Linux are supported.` };
345
+ }
346
+
347
+ /**
348
+ * Uninstall autostart service for the current platform.
349
+ *
350
+ * @returns {{ ok: boolean, message: string }}
351
+ *
352
+ * Example:
353
+ * >>> import { uninstall } from './autostart.mjs';
354
+ * >>> const result = uninstall();
355
+ * >>> console.log(result.message);
356
+ */
357
+ export function uninstall() {
358
+ const os = platform();
359
+ if (os === "darwin") return uninstallLaunchd();
360
+ if (os === "linux") return uninstallSystemd();
361
+ return { ok: false, message: `Unsupported platform: ${os}. Only macOS and Linux are supported.` };
362
+ }
363
+
364
+ /**
365
+ * Check autostart status for the current platform.
366
+ *
367
+ * @returns {{ installed: boolean, running: boolean, path: string, platform: string }}
368
+ *
369
+ * Example:
370
+ * >>> import { autostartStatus } from './autostart.mjs';
371
+ * >>> const s = autostartStatus();
372
+ * >>> console.log(s.installed, s.running);
373
+ */
374
+ export function autostartStatus() {
375
+ const os = platform();
376
+ if (os === "darwin") return { ...statusLaunchd(), platform: "launchd" };
377
+ if (os === "linux") return { ...statusSystemd(), platform: "systemd" };
378
+ return { installed: false, running: false, path: "", platform: os };
379
+ }
package/src/config.mjs CHANGED
@@ -145,7 +145,7 @@ export function restartGateway() {
145
145
  * @returns {{ ok: boolean, message: string, providers: string[] }}
146
146
  *
147
147
  * Example:
148
- * >>> enable({ configPath: "~/.openclaw/openclaw.json", openclawDir: "~/.openclaw", port: 18800 })
148
+ * >>> enable({ configPath: "~/.openclaw/openclaw.json", openclawDir: "~/.openclaw", port: 3000 })
149
149
  * { ok: true, message: "Enabled 3 providers", providers: ["anthropic", "byteplus", "ollama"] }
150
150
  */
151
151
  export function enable({ configPath, openclawDir, port }) {
@@ -260,7 +260,7 @@ export function disable({ configPath, openclawDir }) {
260
260
  // Verify backup is clean (doesn't contain proxy URLs)
261
261
  try {
262
262
  const backupContent = readFileSync(backupPath, "utf-8");
263
- if (!backupContent.includes("127.0.0.1:18800")) {
263
+ if (!backupContent.includes("127.0.0.1:3000")) {
264
264
  copyFileSync(backupPath, configPath);
265
265
  removeState(openclawDir);
266
266
  const restart = restartGateway();
@@ -331,7 +331,7 @@ function cleanProxyUrls(configPath) {
331
331
  let cleaned = false;
332
332
 
333
333
  for (const [name, cfg] of Object.entries(providers)) {
334
- if (cfg.baseUrl && cfg.baseUrl.includes("127.0.0.1:18800")) {
334
+ if (cfg.baseUrl && cfg.baseUrl.includes("127.0.0.1:3000")) {
335
335
  if (BUILTIN_URLS[name]) {
336
336
  // Known provider — restore builtin URL
337
337
  cfg.baseUrl = BUILTIN_URLS[name];
package/src/server.mjs CHANGED
@@ -21,16 +21,16 @@ import { initHistory, getRecent, getDay, listDates } from "./history.mjs";
21
21
  * Start the inspector server.
22
22
  *
23
23
  * @param {object} options
24
- * @param {number} options.port - Port to listen on (default 18800).
24
+ * @param {number} options.port - Port to listen on (default 3000).
25
25
  * @param {string} [options.configPath] - Custom path to openclaw.json.
26
26
  * @param {boolean} [options.open] - Open browser on start.
27
27
  * @returns {Promise<{ server: http.Server, url: string, openclawDir: string }>}
28
28
  *
29
29
  * Example:
30
- * >>> const { url } = await startServer({ port: 18800 });
30
+ * >>> const { url } = await startServer({ port: 3000 });
31
31
  * >>> console.log("Inspector at", url);
32
32
  */
33
- export async function startServer({ port = 18800, configPath, open = false }) {
33
+ export async function startServer({ port = 3000, configPath, open = false }) {
34
34
  // Detect OpenClaw
35
35
  const oc = detect(configPath);
36
36
  if (!oc.exists) {