@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/CHANGELOG.md +214 -189
- package/README.md +189 -172
- package/dist/index.js +60 -23
- package/dist/index.js.map +1 -1
- package/package.json +58 -58
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.
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
clank
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
|
76
|
-
|
|
77
|
-
| **
|
|
78
|
-
| **
|
|
79
|
-
|
|
80
|
-
##
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
clank
|
|
105
|
-
clank
|
|
106
|
-
clank
|
|
107
|
-
clank
|
|
108
|
-
|
|
109
|
-
#
|
|
110
|
-
clank
|
|
111
|
-
clank
|
|
112
|
-
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
clank
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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 fallback — tools 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
|
-
|
|
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
|
|
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 {
|
|
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 =
|
|
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
|
|
4514
|
+
const { exec } = await import("child_process");
|
|
4489
4515
|
const openCmd = platform5() === "win32" ? `start "" "${url}"` : platform5() === "darwin" ? `open "${url}"` : `xdg-open "${url}"`;
|
|
4490
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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",
|
|
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.
|
|
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.
|
|
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
|
|
8073
|
+
const { exec } = await import("child_process");
|
|
8037
8074
|
const cmd = platform5() === "win32" ? `start ${url}` : platform5() === "darwin" ? `open ${url}` : `xdg-open ${url}`;
|
|
8038
|
-
|
|
8075
|
+
exec(cmd);
|
|
8039
8076
|
}
|
|
8040
8077
|
});
|
|
8041
8078
|
var pipeline = program.command("pipeline").description("Manage agent pipelines");
|