@tractorscorch/clank 1.4.0 → 1.4.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.
package/README.md CHANGED
@@ -1,172 +1,189 @@
1
- <p align="center">
2
- <img src="https://raw.githubusercontent.com/ItsTrag1c/Clank/main/docs/banner.png" alt="Clank" width="100%" />
3
- </p>
4
-
5
- <h1 align="center">Clank</h1>
6
-
7
- <p align="center">
8
- <b>Local-first AI agent gateway.</b> Open-source alternative to OpenClaw, optimized for local models.
9
- </p>
10
-
11
- <p align="center">
12
- <a href="https://github.com/ItsTrag1c/Clank/releases/latest"><img src="https://img.shields.io/badge/version-1.1.0-blue.svg" alt="Version" /></a>
13
- <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License" /></a>
14
- <a href="https://www.npmjs.com/package/@tractorscorch/clank"><img src="https://img.shields.io/npm/v/@tractorscorch/clank.svg" alt="npm" /></a>
15
- <a href="https://github.com/ItsTrag1c/Clank/stargazers"><img src="https://img.shields.io/github/stars/ItsTrag1c/Clank.svg" alt="Stars" /></a>
16
- </p>
17
-
18
- <p align="center">
19
- <a href="https://clanksuite.dev">Website</a> ·
20
- <a href="https://github.com/ItsTrag1c/Clank/blob/main/docs/INSTALL.md">Install Guide</a> ·
21
- <a href="https://github.com/ItsTrag1c/Clank/blob/main/docs/USER_GUIDE.md">User Guide</a> ·
22
- <a href="https://github.com/ItsTrag1c/Clank/blob/main/CHANGELOG.md">Changelog</a> ·
23
- <a href="https://x.com/ClankSuite">Twitter</a> ·
24
- <a href="https://reddit.com/u/ClankSuite">Reddit</a>
25
- </p>
26
-
27
- ---
28
-
29
- ## What is Clank?
30
-
31
- Clank is a personal AI gateway — **one daemon, many frontends**. It connects your preferred interfaces (CLI, TUI, browser, Telegram, Discord) to AI agents running local or cloud models. All interfaces share sessions, memory, and agent state.
32
-
33
- **Built for people who want the OpenClaw experience without the token costs.**
34
-
35
- ```
36
- ┌─────────────────────────────┐
37
- │ Clank Gateway │
38
- │ (single daemon) │
39
- │ │
40
- │ Agent Pool + Routing │
41
- │ Sessions, Memory, Pipelines │
42
- │ Cron, Tools, Plugins │
43
- └──────────────┬───────────────┘
44
-
45
- WebSocket + HTTP (port 18790)
46
-
47
- ┌──────────┬───────────┼───────────┬──────────┐
48
- │ │ │ │ │
49
- CLI Web UI Telegram Discord TUI
50
- (direct) (browser) (bot) (bot) (terminal)
51
- ```
52
-
53
- ## Quick Start
54
-
55
- ```bash
56
- npm install -g @tractorscorch/clank
57
- clank setup
58
- clank
59
- ```
60
-
61
- That's it. Setup auto-detects your local models, configures the gateway, and gets you chatting in under 2 minutes. See the [full install guide](docs/INSTALL.md) for details.
62
-
63
- ## Features
64
-
65
- | Feature | Description |
66
- |---------|-------------|
67
- | **Local-first** | Auto-detects Ollama, LM Studio, llama.cpp, vLLM. Cloud providers optional. |
68
- | **Multi-agent** | Named agents with separate models, workspaces, tools, and routing. |
69
- | **Multi-channel** | CLI, TUI, Web UI, Telegram, Discord — all equal, all share sessions. |
70
- | **Self-configuring** | After setup, configure everything through conversation. |
71
- | **18 tools** | File ops, bash, git, web search (Brave), plus 8 self-config tools. |
72
- | **Web Control UI** | 8-panel dashboard: Chat, Agents, Sessions, Config, Pipelines, Cron, Logs, Channels. |
73
- | **Pipeline orchestration** | Chain agents together for multi-step workflows. |
74
- | **Plugin system** | Extend with custom tools, channels, and providers. 25+ hook types. |
75
- | **Cron scheduler** | Recurring and one-shot scheduled agent tasks. |
76
- | **Voice** | Cloud (ElevenLabs) or fully local (whisper.cpp + piper). |
77
- | **Memory** | TF-IDF with decay scoring. Agent learns and remembers across sessions. |
78
- | **Security** | AES-256-GCM encryption, SSRF protection, path containment, config redaction. |
79
-
80
- ## Commands
81
-
82
- ```bash
83
- # Start — gateway + TUI (Telegram/Discord stay alive in background)
84
- clank
85
-
86
- # Chat interfaces
87
- clank chat # Direct mode (no gateway needed)
88
- clank chat --web # Auto-start gateway + open Web UI
89
- clank tui # Rich TUI connected to gateway
90
- clank dashboard # Open Web UI in browser
91
-
92
- # Gateway
93
- clank gateway start # Start in background
94
- clank gateway stop # Stop
95
- clank gateway status # Show status, clients, sessions
96
- clank gateway restart # Restart
97
-
98
- # Setup & diagnostics
99
- clank setup # Onboarding wizard
100
- clank fix # Diagnostics & auto-repair
101
-
102
- # Model & agent management
103
- clank models list # Detect + list models
104
- clank models add # Add a provider (Anthropic, OpenAI, Google, Brave)
105
- clank models test # Test connectivity
106
- clank agents list # List agents
107
- clank agents add # Create an agent
108
-
109
- # Scheduled tasks
110
- clank cron list # List jobs
111
- clank cron add # Schedule a task
112
-
113
- # System
114
- clank daemon install # Auto-start at login (Windows/macOS/Linux)
115
- clank channels # Channel status
116
- clank uninstall # Remove everything
117
- ```
118
-
119
- ## Providers
120
-
121
- | Provider | Type | How |
122
- |----------|------|-----|
123
- | **Ollama** | Local | Auto-detected at `localhost:11434` |
124
- | **LM Studio** | Local | Auto-detected at `localhost:1234` |
125
- | **llama.cpp** | Local | Auto-detected at `localhost:8080` |
126
- | **vLLM** | Local | Auto-detected at `localhost:8000` |
127
- | **Anthropic** | Cloud | API key via `clank setup` or config |
128
- | **OpenAI** | Cloud | API key via `clank setup` or config |
129
- | **Google Gemini** | Cloud | API key via `clank setup` or config |
130
-
131
- Models without native tool calling automatically use prompt-based fallback tools are injected into the system prompt and parsed from text output.
132
-
133
- ## Security
134
-
135
- Clank is designed to be safe by default:
136
-
137
- - **Workspace containment** — file tools blocked outside workspace
138
- - **Bash protection** 25-pattern blocklist for destructive commands
139
- - **API key redaction** — keys never sent to LLM context
140
- - **SSRF protection** web_fetch blocks localhost, cloud metadata, internal hosts
141
- - **Gateway auth** token-based, auto-generated, localhost-only by default
142
- - **Encryption** AES-256-GCM for API keys at rest
143
-
144
- See [SECURITY.md](SECURITY.md) for the full security model.
145
-
146
- ## Documentation
147
-
148
- - **[Install Guide](docs/INSTALL.md)** Detailed installation and setup instructions
149
- - **[User Guide](docs/USER_GUIDE.md)** — How to use Clank day-to-day
150
- - **[Changelog](CHANGELOG.md)** — Version history
151
- - **[Privacy Policy](PRIVACY_POLICY.md)** — Data handling
152
- - **[Security Policy](SECURITY.md)** Security model and vulnerability reporting
153
-
154
- ## Links
155
-
156
- | | |
157
- |--|--|
158
- | **Website** | [clanksuite.dev](https://clanksuite.dev) |
159
- | **GitHub** | [ItsTrag1c/Clank](https://github.com/ItsTrag1c/Clank) |
160
- | **npm** | [npmjs.com/package/@tractorscorch/clank](https://www.npmjs.com/package/@tractorscorch/clank) |
161
- | **Twitter/X** | [@ClankSuite](https://x.com/ClankSuite) |
162
- | **Reddit** | [u/ClankSuite](https://reddit.com/u/ClankSuite) |
163
- | **Legacy** | [Clank-Legacy](https://github.com/ItsTrag1c/Clank-Legacy) (archived CLI v2.7.0 + Desktop v2.6.1) |
164
-
165
- ## Requirements
166
-
167
- - Node.js 20+
168
- - A local model server (Ollama recommended) or cloud API key
169
-
170
- ## License
171
-
172
- MIT — see [LICENSE](LICENSE)
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/ItsTrag1c/Clank/main/docs/banner.png" alt="Clank" width="100%" />
3
+ </p>
4
+
5
+ <h1 align="center">Clank</h1>
6
+
7
+ <p align="center">
8
+ <b>Local-first AI agent gateway.</b> Open-source alternative to OpenClaw, optimized for local models.
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://github.com/ItsTrag1c/Clank/releases/latest"><img src="https://img.shields.io/badge/version-1.4.3-blue.svg" alt="Version" /></a>
13
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License" /></a>
14
+ <a href="https://www.npmjs.com/package/@tractorscorch/clank"><img src="https://img.shields.io/npm/v/@tractorscorch/clank.svg" alt="npm" /></a>
15
+ <a href="https://github.com/ItsTrag1c/Clank/stargazers"><img src="https://img.shields.io/github/stars/ItsTrag1c/Clank.svg" alt="Stars" /></a>
16
+ </p>
17
+
18
+ <p align="center">
19
+ <a href="https://clanksuite.dev">Website</a> ·
20
+ <a href="https://github.com/ItsTrag1c/Clank/blob/main/docs/INSTALL.md">Install Guide</a> ·
21
+ <a href="https://github.com/ItsTrag1c/Clank/blob/main/docs/USER_GUIDE.md">User Guide</a> ·
22
+ <a href="https://github.com/ItsTrag1c/Clank/blob/main/CHANGELOG.md">Changelog</a> ·
23
+ <a href="https://x.com/ClankSuite">Twitter</a> ·
24
+ <a href="https://reddit.com/u/ClankSuite">Reddit</a>
25
+ </p>
26
+
27
+ ---
28
+
29
+ ## What is Clank?
30
+
31
+ Clank is a personal AI gateway — **one daemon, many frontends**. It connects your preferred interfaces (CLI, TUI, browser, Telegram, Discord) to AI agents running local or cloud models. All interfaces share sessions, memory, and agent state.
32
+
33
+ **Built for people who want the OpenClaw experience without the token costs.**
34
+
35
+ ```
36
+ ┌─────────────────────────────┐
37
+ │ Clank Gateway │
38
+ │ (single daemon) │
39
+ │ │
40
+ │ Agent Pool + Routing │
41
+ │ Sessions, Memory, Pipelines │
42
+ │ Cron, Tools, Plugins │
43
+ └──────────────┬───────────────┘
44
+
45
+ WebSocket + HTTP (port 18790)
46
+
47
+ ┌──────────┬───────────┼───────────┬──────────┐
48
+ │ │ │ │ │
49
+ CLI Web UI Telegram Discord TUI
50
+ (direct) (browser) (bot) (bot) (terminal)
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ ### npm (all platforms)
56
+
57
+ ```bash
58
+ npm install -g @tractorscorch/clank
59
+ clank setup
60
+ clank
61
+ ```
62
+
63
+ ### macOS (standalone binary)
64
+
65
+ ```bash
66
+ curl -fsSL https://raw.githubusercontent.com/ItsTrag1c/Clank/main/install.sh | bash
67
+ clank setup
68
+ clank
69
+ ```
70
+
71
+ That's it. Setup auto-detects your local models, configures the gateway, and gets you chatting in under 2 minutes. See the [full install guide](docs/INSTALL.md) for details.
72
+
73
+ ### Downloads
74
+
75
+ | Platform | Download |
76
+ |----------|----------|
77
+ | **npm** (all platforms) | `npm install -g @tractorscorch/clank` |
78
+ | **macOS** (Apple Silicon) | [Clank_1.4.3_macos](https://github.com/ItsTrag1c/Clank/releases/latest/download/Clank_1.4.3_macos) |
79
+
80
+ ## Features
81
+
82
+ | Feature | Description |
83
+ |---------|-------------|
84
+ | **Local-first** | Auto-detects Ollama, LM Studio, llama.cpp, vLLM. Cloud providers optional. |
85
+ | **Multi-agent** | Named agents with separate models, workspaces, tools, and routing. |
86
+ | **Multi-channel** | CLI, TUI, Web UI, Telegram, Discord — all equal, all share sessions. |
87
+ | **Self-configuring** | After setup, configure everything through conversation. |
88
+ | **18 tools** | File ops, bash, git, web search (Brave), plus 8 self-config tools. |
89
+ | **Web Control UI** | 8-panel dashboard: Chat, Agents, Sessions, Config, Pipelines, Cron, Logs, Channels. |
90
+ | **Pipeline orchestration** | Chain agents together for multi-step workflows. |
91
+ | **Plugin system** | Extend with custom tools, channels, and providers. 25+ hook types. |
92
+ | **Cron scheduler** | Recurring and one-shot scheduled agent tasks. |
93
+ | **Voice** | Cloud (ElevenLabs) or fully local (whisper.cpp + piper). |
94
+ | **Memory** | TF-IDF with decay scoring. Agent learns and remembers across sessions. |
95
+ | **Security** | AES-256-GCM encryption, SSRF protection, path containment, config redaction. |
96
+
97
+ ## Commands
98
+
99
+ ```bash
100
+ # Start gateway + TUI (Telegram/Discord stay alive in background)
101
+ clank
102
+
103
+ # Chat interfaces
104
+ clank chat # Direct mode (no gateway needed)
105
+ clank chat --web # Auto-start gateway + open Web UI
106
+ clank tui # Rich TUI connected to gateway
107
+ clank dashboard # Open Web UI in browser
108
+
109
+ # Gateway
110
+ clank gateway start # Start in background
111
+ clank gateway stop # Stop
112
+ clank gateway status # Show status, clients, sessions
113
+ clank gateway restart # Restart
114
+
115
+ # Setup & diagnostics
116
+ clank setup # Onboarding wizard
117
+ clank fix # Diagnostics & auto-repair
118
+
119
+ # Model & agent management
120
+ clank models list # Detect + list models
121
+ clank models add # Add a provider (Anthropic, OpenAI, Google, Brave)
122
+ clank models test # Test connectivity
123
+ clank agents list # List agents
124
+ clank agents add # Create an agent
125
+
126
+ # Scheduled tasks
127
+ clank cron list # List jobs
128
+ clank cron add # Schedule a task
129
+
130
+ # System
131
+ clank daemon install # Auto-start at login (Windows/macOS/Linux)
132
+ clank channels # Channel status
133
+ clank uninstall # Remove everything
134
+ ```
135
+
136
+ ## Providers
137
+
138
+ | Provider | Type | How |
139
+ |----------|------|-----|
140
+ | **Ollama** | Local | Auto-detected at `localhost:11434` |
141
+ | **LM Studio** | Local | Auto-detected at `localhost:1234` |
142
+ | **llama.cpp** | Local | Auto-detected at `localhost:8080` |
143
+ | **vLLM** | Local | Auto-detected at `localhost:8000` |
144
+ | **Anthropic** | Cloud | API key via `clank setup` or config |
145
+ | **OpenAI** | Cloud | API key via `clank setup` or config |
146
+ | **Google Gemini** | Cloud | API key via `clank setup` or config |
147
+
148
+ Models without native tool calling automatically use prompt-based fallbacktools are injected into the system prompt and parsed from text output.
149
+
150
+ ## Security
151
+
152
+ Clank is designed to be safe by default:
153
+
154
+ - **Workspace containment** — file tools blocked outside workspace
155
+ - **Bash protection** — 25-pattern blocklist for destructive commands
156
+ - **API key redaction** — keys never sent to LLM context
157
+ - **SSRF protection** — web_fetch blocks localhost, cloud metadata, internal hosts
158
+ - **Gateway auth** token-based, auto-generated, localhost-only by default
159
+ - **Encryption** AES-256-GCM for API keys at rest
160
+
161
+ See [SECURITY.md](SECURITY.md) for the full security model.
162
+
163
+ ## Documentation
164
+
165
+ - **[Install Guide](docs/INSTALL.md)** — Detailed installation and setup instructions
166
+ - **[User Guide](docs/USER_GUIDE.md)** — How to use Clank day-to-day
167
+ - **[Changelog](CHANGELOG.md)** — Version history
168
+ - **[Privacy Policy](PRIVACY_POLICY.md)** Data handling
169
+ - **[Security Policy](SECURITY.md)** — Security model and vulnerability reporting
170
+
171
+ ## Links
172
+
173
+ | | |
174
+ |--|--|
175
+ | **Website** | [clanksuite.dev](https://clanksuite.dev) |
176
+ | **GitHub** | [ItsTrag1c/Clank](https://github.com/ItsTrag1c/Clank) |
177
+ | **npm** | [npmjs.com/package/@tractorscorch/clank](https://www.npmjs.com/package/@tractorscorch/clank) |
178
+ | **Twitter/X** | [@ClankSuite](https://x.com/ClankSuite) |
179
+ | **Reddit** | [u/ClankSuite](https://reddit.com/u/ClankSuite) |
180
+ | **Legacy** | [Clank-Legacy](https://github.com/ItsTrag1c/Clank-Legacy) (archived CLI v2.7.0 + Desktop v2.6.1) |
181
+
182
+ ## Requirements
183
+
184
+ - Node.js 20+
185
+ - A local model server (Ollama recommended) or cloud API key
186
+
187
+ ## License
188
+
189
+ MIT — see [LICENSE](LICENSE)
package/dist/index.js CHANGED
@@ -1276,6 +1276,10 @@ var init_registry = __esm({
1276
1276
  });
1277
1277
 
1278
1278
  // src/tools/path-guard.ts
1279
+ var path_guard_exports = {};
1280
+ __export(path_guard_exports, {
1281
+ guardPath: () => guardPath
1282
+ });
1279
1283
  import { resolve, isAbsolute, normalize, relative } from "path";
1280
1284
  function guardPath(inputPath, projectRoot, opts) {
1281
1285
  const resolved = isAbsolute(inputPath) ? normalize(inputPath) : normalize(resolve(projectRoot, inputPath));
@@ -2255,6 +2259,12 @@ var init_web_fetch = __esm({
2255
2259
  if (host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "0.0.0.0") {
2256
2260
  return { ok: false, error: "localhost URLs are blocked (SSRF protection)" };
2257
2261
  }
2262
+ if (/^10\./.test(host) || /^192\.168\./.test(host) || /^172\.(1[6-9]|2\d|3[01])\./.test(host)) {
2263
+ return { ok: false, error: "Private network IPs are blocked (SSRF protection)" };
2264
+ }
2265
+ if (host.startsWith("[::ffff:")) {
2266
+ return { ok: false, error: "IPv4-mapped IPv6 addresses are blocked" };
2267
+ }
2258
2268
  if (host === "169.254.169.254" || host === "metadata.google.internal") {
2259
2269
  return { ok: false, error: "Cloud metadata endpoints are blocked" };
2260
2270
  }
@@ -2391,9 +2401,21 @@ var init_config_tool = __esm({
2391
2401
  return `Key not found: ${key}`;
2392
2402
  }
2393
2403
  }
2394
- return typeof current === "object" ? JSON.stringify(current, null, 2) : String(current);
2404
+ if (typeof current === "object") {
2405
+ return JSON.stringify(redactConfig(current), null, 2);
2406
+ }
2407
+ const SENSITIVE = /* @__PURE__ */ new Set(["apikey", "api_key", "apiKey", "token", "bottoken", "botToken", "secret", "password", "pin"]);
2408
+ const lastKey = keys[keys.length - 1];
2409
+ if (SENSITIVE.has(lastKey) && typeof current === "string") {
2410
+ return "[REDACTED]";
2411
+ }
2412
+ return String(current);
2395
2413
  }
2396
2414
  if (action === "set") {
2415
+ const BLOCKED_KEYS = ["__proto__", "constructor", "prototype"];
2416
+ if (keys.some((k) => BLOCKED_KEYS.includes(k))) {
2417
+ return "Error: blocked \u2014 unsafe key";
2418
+ }
2397
2419
  let parsed = args.value;
2398
2420
  try {
2399
2421
  parsed = JSON.parse(args.value);
@@ -4247,14 +4269,17 @@ var init_voice_tool = __esm({
4247
4269
  },
4248
4270
  safetyLevel: "low",
4249
4271
  readOnly: true,
4250
- validate(args) {
4272
+ validate(args, ctx) {
4251
4273
  if (!args.file_path || typeof args.file_path !== "string") return { ok: false, error: "file_path is required" };
4252
4274
  return { ok: true };
4253
4275
  },
4254
- async execute(args) {
4276
+ async execute(args, ctx) {
4255
4277
  const { readFile: readFile13 } = await import("fs/promises");
4256
4278
  const { existsSync: existsSync12 } = await import("fs");
4257
- const filePath = args.file_path;
4279
+ const { guardPath: guardPath2 } = await Promise.resolve().then(() => (init_path_guard(), path_guard_exports));
4280
+ const guard = guardPath2(args.file_path, ctx.projectRoot, { allowExternal: ctx.allowExternal });
4281
+ if (!guard.ok) return guard.error;
4282
+ const filePath = guard.path;
4258
4283
  if (!existsSync12(filePath)) return `Error: File not found: ${filePath}`;
4259
4284
  const config = await loadConfig();
4260
4285
  const engine = new STTEngine(config);
@@ -4455,14 +4480,15 @@ async function runChat(opts) {
4455
4480
  }
4456
4481
  if (!gatewayRunning) {
4457
4482
  console.log(dim("Starting gateway..."));
4458
- const { fork: fork2 } = await import("child_process");
4483
+ const { spawn } = await import("child_process");
4459
4484
  const { fileURLToPath: fileURLToPath6 } = await import("url");
4460
4485
  const { dirname: dirname6, join: join20 } = await import("path");
4461
4486
  const __filename4 = fileURLToPath6(import.meta.url);
4462
4487
  const entryPoint = join20(dirname6(__filename4), "index.js");
4463
- const child = fork2(entryPoint, ["gateway", "start", "--foreground"], {
4488
+ const child = spawn(process.execPath, [entryPoint, "gateway", "start", "--foreground"], {
4464
4489
  detached: true,
4465
- stdio: "ignore"
4490
+ stdio: "ignore",
4491
+ windowsHide: true
4466
4492
  });
4467
4493
  child.unref();
4468
4494
  for (let i = 0; i < 20; i++) {
@@ -4485,9 +4511,9 @@ async function runChat(opts) {
4485
4511
  const url = `http://127.0.0.1:${port}/#token=${token}`;
4486
4512
  console.log(dim(`Opening ${url}`));
4487
4513
  const { platform: platform5 } = await import("os");
4488
- const { exec: exec2 } = await import("child_process");
4514
+ const { exec } = await import("child_process");
4489
4515
  const openCmd = platform5() === "win32" ? `start "" "${url}"` : platform5() === "darwin" ? `open "${url}"` : `xdg-open "${url}"`;
4490
- exec2(openCmd);
4516
+ exec(openCmd);
4491
4517
  console.log(green("Web UI opened in browser."));
4492
4518
  return;
4493
4519
  }
@@ -5201,6 +5227,7 @@ var init_telegram = __esm({
5201
5227
  try {
5202
5228
  await ctx.api.sendChatAction(chatId, "typing");
5203
5229
  let streamMsgId = null;
5230
+ let sendingInitial = false;
5204
5231
  let accumulated = "";
5205
5232
  let lastEditTime = 0;
5206
5233
  const EDIT_INTERVAL = 800;
@@ -5215,7 +5242,8 @@ var init_telegram = __esm({
5215
5242
  onToken: (content) => {
5216
5243
  accumulated += content;
5217
5244
  const now = Date.now();
5218
- if (!streamMsgId && accumulated.length > 20) {
5245
+ if (!streamMsgId && !sendingInitial && accumulated.length > 20) {
5246
+ sendingInitial = true;
5219
5247
  bot.api.sendMessage(chatId, accumulated + " \u258D").then((sent) => {
5220
5248
  streamMsgId = sent.message_id;
5221
5249
  lastEditTime = now;
@@ -5948,6 +5976,13 @@ var init_server = __esm({
5948
5976
  * Used by channel adapters for real-time streaming (e.g., Telegram message editing).
5949
5977
  */
5950
5978
  async handleInboundMessageStreaming(context, text, callbacks) {
5979
+ const rlKey = deriveSessionKey(context);
5980
+ if (this.isRateLimited(rlKey)) {
5981
+ throw new Error("Rate limited \u2014 too many messages. Wait a moment.");
5982
+ }
5983
+ return this._handleInboundMessageStreamingInner(context, text, callbacks);
5984
+ }
5985
+ async _handleInboundMessageStreamingInner(context, text, callbacks) {
5951
5986
  const agentId = resolveRoute(
5952
5987
  context,
5953
5988
  [],
@@ -6021,7 +6056,7 @@ var init_server = __esm({
6021
6056
  res.writeHead(200, { "Content-Type": "application/json" });
6022
6057
  res.end(JSON.stringify({
6023
6058
  status: "ok",
6024
- version: "1.4.0",
6059
+ version: "1.4.3",
6025
6060
  uptime: process.uptime(),
6026
6061
  clients: this.clients.size,
6027
6062
  agents: this.engines.size
@@ -6129,7 +6164,7 @@ var init_server = __esm({
6129
6164
  const hello = {
6130
6165
  type: "hello",
6131
6166
  protocol: PROTOCOL_VERSION,
6132
- version: "1.4.0",
6167
+ version: "1.4.3",
6133
6168
  agents: this.config.agents.list.map((a) => ({
6134
6169
  id: a.id,
6135
6170
  name: a.name || a.id,
@@ -6476,7 +6511,6 @@ __export(gateway_cmd_exports, {
6476
6511
  gatewayStop: () => gatewayStop,
6477
6512
  isGatewayRunning: () => isGatewayRunning
6478
6513
  });
6479
- import { fork } from "child_process";
6480
6514
  import { writeFile as writeFile8, readFile as readFile12, unlink as unlink3 } from "fs/promises";
6481
6515
  import { existsSync as existsSync9 } from "fs";
6482
6516
  import { join as join15, dirname as dirname3 } from "path";
@@ -6538,15 +6572,18 @@ async function gatewayStartBackground() {
6538
6572
  }
6539
6573
  console.log(dim2(" Starting gateway in background..."));
6540
6574
  const entryPoint = join15(dirname3(__filename2), "index.js");
6541
- const logFile = join15(getConfigDir(), "logs", "gateway.log");
6542
6575
  const { mkdir: mkdir7 } = await import("fs/promises");
6576
+ const { spawn } = await import("child_process");
6577
+ const { openSync } = await import("fs");
6543
6578
  await mkdir7(join15(getConfigDir(), "logs"), { recursive: true });
6544
- const child = fork(entryPoint, ["gateway", "start", "--foreground"], {
6579
+ const logFile = join15(getConfigDir(), "logs", "gateway.log");
6580
+ const logFd = openSync(logFile, "a");
6581
+ const child = spawn(process.execPath, [entryPoint, "gateway", "start", "--foreground"], {
6545
6582
  detached: true,
6546
- stdio: ["ignore", "ignore", "ignore", "ipc"]
6583
+ stdio: ["ignore", logFd, logFd],
6584
+ windowsHide: true
6547
6585
  });
6548
6586
  child.unref();
6549
- child.disconnect();
6550
6587
  for (let i = 0; i < 20; i++) {
6551
6588
  await new Promise((r) => setTimeout(r, 500));
6552
6589
  if (await isGatewayRunning(port)) {
@@ -7511,7 +7548,7 @@ async function runTui(opts) {
7511
7548
  ws.on("open", () => {
7512
7549
  ws.send(JSON.stringify({
7513
7550
  type: "connect",
7514
- params: { auth: { token }, mode: "tui", version: "1.4.0" }
7551
+ params: { auth: { token }, mode: "tui", version: "1.4.3" }
7515
7552
  }));
7516
7553
  });
7517
7554
  ws.on("message", (data) => {
@@ -7813,14 +7850,14 @@ async function runUpdate() {
7813
7850
  }
7814
7851
  console.log(dim9(" Pulling latest version..."));
7815
7852
  try {
7816
- const output = execSync2("npm install -g @tractorscorch/clank@latest", {
7853
+ const output = execSync2("npm install -g @tractorscorch/clank@latest --force", {
7817
7854
  encoding: "utf-8",
7818
7855
  timeout: 12e4
7819
7856
  });
7820
7857
  console.log(dim9(` ${output.trim()}`));
7821
7858
  } catch (err) {
7822
7859
  console.error(red6(` Update failed: ${err instanceof Error ? err.message : err}`));
7823
- console.error(dim9(" Try manually: npm install -g @tractorscorch/clank@latest"));
7860
+ console.error(dim9(" Try manually: npm install -g @tractorscorch/clank@latest --force"));
7824
7861
  return;
7825
7862
  }
7826
7863
  try {
@@ -7940,7 +7977,7 @@ import { fileURLToPath as fileURLToPath5 } from "url";
7940
7977
  import { dirname as dirname5, join as join19 } from "path";
7941
7978
  var __filename3 = fileURLToPath5(import.meta.url);
7942
7979
  var __dirname3 = dirname5(__filename3);
7943
- var version = "1.4.0";
7980
+ var version = "1.4.3";
7944
7981
  try {
7945
7982
  const pkg = JSON.parse(readFileSync(join19(__dirname3, "..", "package.json"), "utf-8"));
7946
7983
  version = pkg.version;
@@ -8033,9 +8070,9 @@ program.command("dashboard").description("Open the Web UI in your browser").opti
8033
8070
  `);
8034
8071
  if (opts.open !== false) {
8035
8072
  const { platform: platform5 } = await import("os");
8036
- const { exec: exec2 } = await import("child_process");
8073
+ const { exec } = await import("child_process");
8037
8074
  const cmd = platform5() === "win32" ? `start ${url}` : platform5() === "darwin" ? `open ${url}` : `xdg-open ${url}`;
8038
- exec2(cmd);
8075
+ exec(cmd);
8039
8076
  }
8040
8077
  });
8041
8078
  var pipeline = program.command("pipeline").description("Manage agent pipelines");