tiger-agent 0.2.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/.env.example +22 -0
- package/.env.secrets.example +14 -0
- package/LICENSE +22 -0
- package/README.md +284 -0
- package/bin/tiger.js +96 -0
- package/package.json +58 -0
- package/scripts/audit.sh +54 -0
- package/scripts/backup.sh +42 -0
- package/scripts/cryptoEnv.js +57 -0
- package/scripts/decrypt-env.js +34 -0
- package/scripts/encrypt-env.js +34 -0
- package/scripts/migrate-vector-db.js +44 -0
- package/scripts/onboard.js +319 -0
- package/scripts/scan-secrets.sh +87 -0
- package/scripts/setup.js +302 -0
- package/scripts/sqlite_memory.py +297 -0
- package/scripts/sqlite_vec_setup.py +112 -0
- package/src/agent/contextFiles.js +30 -0
- package/src/agent/db.js +349 -0
- package/src/agent/mainAgent.js +406 -0
- package/src/agent/reflectionAgent.js +193 -0
- package/src/agent/reflectionScheduler.js +21 -0
- package/src/agent/skills.js +169 -0
- package/src/agent/subAgent.js +39 -0
- package/src/agent/toolbox.js +291 -0
- package/src/apiProviders.js +217 -0
- package/src/cli.js +187 -0
- package/src/config.js +141 -0
- package/src/kimiClient.js +88 -0
- package/src/llmClient.js +147 -0
- package/src/telegram/bot.js +182 -0
- package/src/telegram/supervisor.js +84 -0
- package/src/tokenManager.js +223 -0
- package/src/utils.js +30 -0
package/.env.example
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# ============================================
|
|
2
|
+
# Tiger Bot Environment Configuration
|
|
3
|
+
# Copy this file to .env and fill in real values
|
|
4
|
+
# NEVER commit .env to git!
|
|
5
|
+
# ============================================
|
|
6
|
+
|
|
7
|
+
# Google Gemini (for image generation)
|
|
8
|
+
GEMINI_API_KEY=your_gemini_api_key_here
|
|
9
|
+
|
|
10
|
+
# Telegram Bot
|
|
11
|
+
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
|
|
12
|
+
TELEGRAM_CHAT_ID=8172556270
|
|
13
|
+
|
|
14
|
+
# X/Twitter (optional - paid API)
|
|
15
|
+
X_BEARER_TOKEN=your_x_bearer_token_here
|
|
16
|
+
|
|
17
|
+
# Pinecone (optional - if using cloud vector DB)
|
|
18
|
+
PINECONE_API_KEY=your_pinecone_key_here
|
|
19
|
+
PINECONE_ENVIRONMENT=your_pinecone_env
|
|
20
|
+
|
|
21
|
+
# SQLite Configuration
|
|
22
|
+
TIGER_DB_PATH=/root/.tiger/memory/tiger_memory.db
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Secrets only. Keep this file local as .env.secrets (gitignored),
|
|
2
|
+
# or encrypt it to .env.secrets.enc and delete the plaintext.
|
|
3
|
+
|
|
4
|
+
# Moonshot/Open Platform key (used when KIMI_PROVIDER=moonshot)
|
|
5
|
+
MOONSHOT_API_KEY=
|
|
6
|
+
|
|
7
|
+
# Kimi Code API key (used when KIMI_PROVIDER=code)
|
|
8
|
+
KIMI_CODE_API_KEY=
|
|
9
|
+
|
|
10
|
+
# Backward-compatible alias (optional)
|
|
11
|
+
KIMI_API_KEY=
|
|
12
|
+
|
|
13
|
+
# Telegram bot token
|
|
14
|
+
TELEGRAM_BOT_TOKEN=
|
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 AI Research Group, Department of Civil Engineering,
|
|
4
|
+
King Mongkut's University of Technology Thonburi (KMUTT)
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# ๐ฏ Tiger Agent
|
|
2
|
+
|
|
3
|
+
**Made by AI Research Group, Department of Civil Engineering, King Mongkut's University of Technology Thonburi (KMUTT)**
|
|
4
|
+
|
|
5
|
+
Tiger is a **Cognitive AI Agent** with persistent long-term memory, multi-provider LLM support, self-learning, and Telegram bot integration โ designed for 24/7 autonomous operation on Linux.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ๐ฏ Why Tiger?
|
|
10
|
+
|
|
11
|
+
| Feature | Tiger Bot | Generic AI Assistants |
|
|
12
|
+
|---------|-----------|----------------------|
|
|
13
|
+
| **Memory** | Persistent lifetime memory (Vector DB) | Forgets when session ends |
|
|
14
|
+
| **Learning** | Self-training every 12 hours | Static, never improves |
|
|
15
|
+
| **Security** | Audit logs + Encryption + Hardened perms | No audit trail |
|
|
16
|
+
| **Channels** | CLI + Telegram simultaneously | Single channel only |
|
|
17
|
+
| **Execution** | Chains multiple skills autonomously | Single command only |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## ๐ฆ Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g tiger-agent
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
No git clone needed.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## ๐ Quick Start
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
tiger onboard # First-time setup wizard
|
|
35
|
+
tiger start # Start CLI chat
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
CLI exit: `/exit` or `/quit`
|
|
39
|
+
|
|
40
|
+
For Telegram:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
tiger telegram # Foreground
|
|
44
|
+
tiger telegram --background # Background daemon
|
|
45
|
+
tiger stop # Stop daemon
|
|
46
|
+
tiger status # Check daemon status
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## ๐ Requirements
|
|
52
|
+
|
|
53
|
+
- Node.js 18+ (20+ recommended)
|
|
54
|
+
- npm
|
|
55
|
+
- Python 3 (for SQLite memory helper)
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## ๐ฎ Run Modes
|
|
60
|
+
|
|
61
|
+
| Mode | Global install | From source | Use Case |
|
|
62
|
+
|------|---------------|-------------|----------|
|
|
63
|
+
| **CLI** | `tiger start` | `npm run cli` | Interactive terminal |
|
|
64
|
+
| **Telegram** | `tiger telegram` | `npm run telegram` | Bot foreground |
|
|
65
|
+
| **Background** | `tiger telegram --background` | `npm run telegram:bg` | 24/7 daemon |
|
|
66
|
+
| **Stop** | `tiger stop` | `npm run telegram:stop` | Kill daemon |
|
|
67
|
+
| **Status** | `tiger status` | โ | Check daemon |
|
|
68
|
+
|
|
69
|
+
Background logs: `~/.tiger/logs/telegram-supervisor.log`
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## ๐ง Setup Wizard
|
|
74
|
+
|
|
75
|
+
`tiger onboard` (global) or `npm run setup` (from source) configures:
|
|
76
|
+
|
|
77
|
+
- `~/.tiger/.env` โ non-secret settings
|
|
78
|
+
- `~/.tiger/.env.secrets` โ API keys and tokens (mode 600)
|
|
79
|
+
|
|
80
|
+
Options during setup:
|
|
81
|
+
- Persistent vs temporary vector DB
|
|
82
|
+
- Optional `sqlite-vec` acceleration
|
|
83
|
+
- Optional encrypted secrets file
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## ๐ Environment Variables
|
|
88
|
+
|
|
89
|
+
| Variable | Default | Description |
|
|
90
|
+
|----------|---------|-------------|
|
|
91
|
+
| `ACTIVE_PROVIDER` | โ | Active LLM provider (`kimi`, `zai`, `minimax`, `claude`, `moonshot`) |
|
|
92
|
+
| `PROVIDER_ORDER` | โ | Fallback order, comma-separated |
|
|
93
|
+
| `TELEGRAM_BOT_TOKEN` | โ | Telegram bot token |
|
|
94
|
+
| `ALLOW_SHELL` | `false` | Enable shell tool |
|
|
95
|
+
| `ALLOW_SKILL_INSTALL` | `false` | Enable ClawHub skill install |
|
|
96
|
+
| `VECTOR_DB_PATH` | `~/.tiger/db/memory.sqlite` | SQLite vector DB path |
|
|
97
|
+
| `DATA_DIR` | `~/.tiger/data` | Context files directory |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## ๐ Multi-Provider LLM
|
|
102
|
+
|
|
103
|
+
Tiger supports **5 providers** with automatic fallback and daily token limits.
|
|
104
|
+
|
|
105
|
+
### Supported Providers
|
|
106
|
+
|
|
107
|
+
| Provider | ID | Default Model | API Key |
|
|
108
|
+
|----------|----|--------------|---------|
|
|
109
|
+
| Kimi Code | `kimi` | `k2p5` | `KIMI_CODE_API_KEY` |
|
|
110
|
+
| Kimi Moonshot | `moonshot` | `kimi-k1` | `MOONSHOT_API_KEY` |
|
|
111
|
+
| Z.ai (Zhipu) | `zai` | `glm-5` | `ZAI_API_KEY` (format: `id.secret`) |
|
|
112
|
+
| MiniMax | `minimax` | `abab6.5s-chat` | `MINIMAX_API_KEY` |
|
|
113
|
+
| Claude (Anthropic) | `claude` | `claude-sonnet-4-6` | `CLAUDE_API_KEY` |
|
|
114
|
+
|
|
115
|
+
### `.env` Configuration
|
|
116
|
+
|
|
117
|
+
```env
|
|
118
|
+
ACTIVE_PROVIDER=zai
|
|
119
|
+
PROVIDER_ORDER=zai,claude,kimi,minimax,moonshot
|
|
120
|
+
|
|
121
|
+
KIMI_CODE_API_KEY=<key>
|
|
122
|
+
ZAI_API_KEY=<key>
|
|
123
|
+
MINIMAX_API_KEY=<key>
|
|
124
|
+
CLAUDE_API_KEY=<key>
|
|
125
|
+
MOONSHOT_API_KEY=<key>
|
|
126
|
+
|
|
127
|
+
# Daily token limits per provider (0 = unlimited)
|
|
128
|
+
KIMI_TOKEN_LIMIT=100000
|
|
129
|
+
ZAI_TOKEN_LIMIT=100000
|
|
130
|
+
MINIMAX_TOKEN_LIMIT=100000
|
|
131
|
+
CLAUDE_TOKEN_LIMIT=500000
|
|
132
|
+
MOONSHOT_TOKEN_LIMIT=100000
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Auto-Switch Behaviour
|
|
136
|
+
|
|
137
|
+
1. Uses `ACTIVE_PROVIDER` for all requests
|
|
138
|
+
2. On **429** (rate limit) or **403** (quota exceeded) โ switches to next in `PROVIDER_ORDER`
|
|
139
|
+
3. When a provider's daily token limit is reached โ skipped for the rest of the day
|
|
140
|
+
4. Providers with no API key are silently skipped
|
|
141
|
+
5. Token usage tracked in `~/.tiger/db/token_usage.json`, resets at UTC midnight
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## ๐ฌ Telegram Commands
|
|
146
|
+
|
|
147
|
+
| Command | Description |
|
|
148
|
+
|---------|-------------|
|
|
149
|
+
| `/api` | Show all providers with token usage |
|
|
150
|
+
| `/api <id>` | Switch provider (e.g. `/api claude`) |
|
|
151
|
+
| `/tokens` | Show today's token usage per provider |
|
|
152
|
+
| `/help` | Show all commands |
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## ๐ง Memory & Context
|
|
157
|
+
|
|
158
|
+
Context files loaded every turn (from `~/.tiger/data/`):
|
|
159
|
+
|
|
160
|
+
- `soul.md` โ Agent personality
|
|
161
|
+
- `human.md` / `human2.md` โ User profile
|
|
162
|
+
- `ownskill.md` โ Known skills (auto-refreshed every 24h)
|
|
163
|
+
|
|
164
|
+
Auto-refresh cycles (configurable via `.env`):
|
|
165
|
+
|
|
166
|
+
| Cycle | Variable | Default |
|
|
167
|
+
|-------|----------|---------|
|
|
168
|
+
| Skill summary | `OWN_SKILL_UPDATE_HOURS` | 24h |
|
|
169
|
+
| Soul refresh | `SOUL_UPDATE_HOURS` | 24h |
|
|
170
|
+
| Reflection | `REFLECTION_UPDATE_HOURS` | 12h |
|
|
171
|
+
| Memory ingest | `MEMORY_INGEST_EVERY_TURNS` | every N turns |
|
|
172
|
+
|
|
173
|
+
Vector memory DB: `~/.tiger/db/memory.sqlite`
|
|
174
|
+
|
|
175
|
+
Optional `sqlite-vec` acceleration:
|
|
176
|
+
```env
|
|
177
|
+
SQLITE_VEC_EXTENSION=/path/to/sqlite_vec
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Memory commands (from source):
|
|
181
|
+
```bash
|
|
182
|
+
npm run memory:init # Initialize DB
|
|
183
|
+
npm run memory:stats # Show stats
|
|
184
|
+
npm run memory:migrate # Migrate from /tmp/
|
|
185
|
+
npm run memory:vec:check # Check sqlite-vec
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## ๐ ๏ธ Built-in Tools
|
|
191
|
+
|
|
192
|
+
| Category | Tools |
|
|
193
|
+
|----------|-------|
|
|
194
|
+
| **Files** | `list_files`, `read_file`, `write_file` |
|
|
195
|
+
| **Shell** | `run_shell` (requires `ALLOW_SHELL=true`) |
|
|
196
|
+
| **Skills** | `list_skills`, `load_skill`, `clawhub_search`, `clawhub_install` |
|
|
197
|
+
| **Orchestration** | `run_sub_agents` |
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## ๐ Security
|
|
202
|
+
|
|
203
|
+
| Feature | Detail |
|
|
204
|
+
|---------|--------|
|
|
205
|
+
| **Credential Storage** | Externalized to `~/.tiger/.env.secrets` (mode 600) |
|
|
206
|
+
| **Database Security** | `~/.tiger/db/` with hardened permissions |
|
|
207
|
+
| **Audit Logging** | Sanitized skill logs at `~/.tiger/logs/audit.log` |
|
|
208
|
+
| **Auto Backup** | Daily SQLite backups, 30-day retention |
|
|
209
|
+
| **Secret Rotation** | Built-in 90-day rotation reminders |
|
|
210
|
+
|
|
211
|
+
### Encrypted Secrets (optional)
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
export SECRETS_PASSPHRASE='your-long-passphrase'
|
|
215
|
+
node scripts/encrypt-env.js --in .env.secrets --out .env.secrets.enc
|
|
216
|
+
rm .env.secrets
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## ๐ Tiger vs OpenClaw
|
|
222
|
+
|
|
223
|
+
| Feature | **Tiger** ๐ฏ | **OpenClaw** ๐ง |
|
|
224
|
+
|---------|-------------|-----------------|
|
|
225
|
+
| **Identity** | Persistent AI persona | Skill marketplace |
|
|
226
|
+
| **Memory** | Text files + SQLite vector | Skill-based only |
|
|
227
|
+
| **Self-Training** | โ
12h auto-reflection | โ Manual only |
|
|
228
|
+
| **Skill Orchestration** | Multi-skill pipelines | Single execution |
|
|
229
|
+
| **Context Retention** | โ
Cross-session | Session-only |
|
|
230
|
+
| **Security** | โ
Encryption + audit logs | Basic |
|
|
231
|
+
| **Installation** | `npm install -g tiger-agent` | `clawhub install` |
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## ๐ Troubleshooting
|
|
236
|
+
|
|
237
|
+
| Issue | Solution |
|
|
238
|
+
|-------|----------|
|
|
239
|
+
| Bot stuck on one provider | `/api <name>` in Telegram to switch manually |
|
|
240
|
+
| Provider silently skipped | No API key set, or daily limit reached โ check `/tokens` |
|
|
241
|
+
| `401` auth error | Wrong or missing API key |
|
|
242
|
+
| `403` quota error | Daily quota exhausted โ auto-switches; top up billing or raise `*_TOKEN_LIMIT` |
|
|
243
|
+
| `429` rate limit | Auto-switches to next provider in `PROVIDER_ORDER` |
|
|
244
|
+
| Z.ai auth fails | Key must be `id.secret` format (from Zhipu/BigModel console) |
|
|
245
|
+
| Shell tool disabled | Set `ALLOW_SHELL=true` in `.env` |
|
|
246
|
+
| Stuck processes | `pkill -f "node src/cli.js"` then restart |
|
|
247
|
+
| Reset token counters | Delete `~/.tiger/db/token_usage.json` and restart |
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## ๐ Data Directory
|
|
252
|
+
|
|
253
|
+
After global install, all runtime data lives in `~/.tiger/`:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
~/.tiger/
|
|
257
|
+
โโโ .env # Settings
|
|
258
|
+
โโโ .env.secrets # API keys (mode 600)
|
|
259
|
+
โโโ data/ # Context files (soul.md, human.md, ...)
|
|
260
|
+
โโโ db/
|
|
261
|
+
โ โโโ agent.json # Conversation state
|
|
262
|
+
โ โโโ memory.sqlite # Vector memory
|
|
263
|
+
โ โโโ token_usage.json # Daily token counters
|
|
264
|
+
โโโ logs/
|
|
265
|
+
โโโ audit.log
|
|
266
|
+
โโโ telegram-supervisor.log
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## ๐ฅ Authors
|
|
272
|
+
|
|
273
|
+
**AI Research Group**
|
|
274
|
+
Department of Civil Engineering
|
|
275
|
+
King Mongkut's University of Technology Thonburi (KMUTT)
|
|
276
|
+
Bangkok, Thailand
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## ๐ License
|
|
281
|
+
|
|
282
|
+
[](LICENSE)
|
|
283
|
+
|
|
284
|
+
*[่ - Hว - The Tiger: Powerful, agile, and relentless in pursuit of goals]*
|
package/bin/tiger.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
const PKG_ROOT = path.resolve(__dirname, '..');
|
|
9
|
+
const TIGER_HOME = process.env.TIGER_HOME || path.join(os.homedir(), '.tiger');
|
|
10
|
+
|
|
11
|
+
// Ensure runtime dirs exist
|
|
12
|
+
['', 'data', 'db', 'logs'].forEach((d) => fs.mkdirSync(path.join(TIGER_HOME, d), { recursive: true }));
|
|
13
|
+
|
|
14
|
+
// Expose to child modules and the supervisor worker
|
|
15
|
+
process.env.TIGER_HOME = TIGER_HOME;
|
|
16
|
+
|
|
17
|
+
// chdir so all relative env paths (./data, ./db, .env) resolve inside ~/.tiger
|
|
18
|
+
process.chdir(TIGER_HOME);
|
|
19
|
+
|
|
20
|
+
const argv = process.argv.slice(2);
|
|
21
|
+
const cmd = argv[0] || '';
|
|
22
|
+
|
|
23
|
+
// โโโ Help โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
24
|
+
function showHelp() {
|
|
25
|
+
console.log(`
|
|
26
|
+
Tiger Agent ๐ฏ โ AI assistant with persistent memory
|
|
27
|
+
|
|
28
|
+
Usage:
|
|
29
|
+
tiger onboard Interactive setup wizard
|
|
30
|
+
tiger onboard --install-daemon Setup + install system daemon (auto-start on boot)
|
|
31
|
+
tiger start Start CLI chat
|
|
32
|
+
tiger telegram Start Telegram bot (foreground)
|
|
33
|
+
tiger telegram --background Start Telegram bot as background process
|
|
34
|
+
tiger stop Stop background Telegram bot
|
|
35
|
+
tiger status Show daemon / process status
|
|
36
|
+
tiger version Print version
|
|
37
|
+
|
|
38
|
+
Config & data: ${TIGER_HOME}
|
|
39
|
+
`.trim());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// โโโ Route commands โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
43
|
+
switch (cmd) {
|
|
44
|
+
case 'onboard':
|
|
45
|
+
require(path.join(PKG_ROOT, 'scripts', 'onboard.js'));
|
|
46
|
+
break;
|
|
47
|
+
|
|
48
|
+
case 'version':
|
|
49
|
+
case '-v':
|
|
50
|
+
case '--version': {
|
|
51
|
+
const pkg = require(path.join(PKG_ROOT, 'package.json'));
|
|
52
|
+
console.log(pkg.version);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
case 'status': {
|
|
57
|
+
const pidFile = path.join(TIGER_HOME, 'tiger-telegram.pid');
|
|
58
|
+
if (!fs.existsSync(pidFile)) { console.log('Tiger daemon: not running'); break; }
|
|
59
|
+
const pid = Number(fs.readFileSync(pidFile, 'utf8').trim());
|
|
60
|
+
try { process.kill(pid, 0); console.log(`Tiger daemon: running (PID ${pid})`); }
|
|
61
|
+
catch (_) { console.log('Tiger daemon: not running (stale pid file)'); }
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
case 'stop':
|
|
66
|
+
process.argv = [process.argv[0], process.argv[1], '--telegram-stop'];
|
|
67
|
+
require(path.join(PKG_ROOT, 'src', 'cli.js'));
|
|
68
|
+
break;
|
|
69
|
+
|
|
70
|
+
case 'telegram':
|
|
71
|
+
if (argv.includes('--background') || argv.includes('-b')) {
|
|
72
|
+
process.argv = [process.argv[0], process.argv[1], '--telegram', '--background'];
|
|
73
|
+
} else {
|
|
74
|
+
process.argv = [process.argv[0], process.argv[1], '--telegram'];
|
|
75
|
+
}
|
|
76
|
+
require(path.join(PKG_ROOT, 'src', 'cli.js'));
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case 'start':
|
|
80
|
+
case 'cli':
|
|
81
|
+
case '':
|
|
82
|
+
process.argv = [process.argv[0], process.argv[1]];
|
|
83
|
+
require(path.join(PKG_ROOT, 'src', 'cli.js'));
|
|
84
|
+
break;
|
|
85
|
+
|
|
86
|
+
case 'help':
|
|
87
|
+
case '--help':
|
|
88
|
+
case '-h':
|
|
89
|
+
showHelp();
|
|
90
|
+
break;
|
|
91
|
+
|
|
92
|
+
default:
|
|
93
|
+
console.error(`Unknown command: ${cmd}`);
|
|
94
|
+
showHelp();
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tiger-agent",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Cognitive AI agent with persistent memory, multi-provider LLM, and Telegram bot",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "src/cli.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"tiger": "bin/tiger.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin/",
|
|
12
|
+
"src/",
|
|
13
|
+
"scripts/",
|
|
14
|
+
".env.example",
|
|
15
|
+
".env.secrets.example"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"postinstall": "node -e \"if(process.env.npm_config_global==='true'){console.log('\\n๐ฏ Tiger Agent installed!\\n\\nRun to get started:\\n tiger onboard\\n')}\"",
|
|
19
|
+
"setup": "node scripts/setup.js",
|
|
20
|
+
"onboard": "node scripts/onboard.js",
|
|
21
|
+
"start": "node src/cli.js",
|
|
22
|
+
"telegram": "node src/cli.js --telegram",
|
|
23
|
+
"telegram:bg": "node src/cli.js --telegram --background",
|
|
24
|
+
"telegram:stop": "node src/cli.js --telegram-stop",
|
|
25
|
+
"cli": "node src/cli.js",
|
|
26
|
+
"memory:init": "python3 scripts/sqlite_memory.py init --db ./db/memory.sqlite",
|
|
27
|
+
"memory:stats": "python3 scripts/sqlite_memory.py stats --db ./db/memory.sqlite",
|
|
28
|
+
"memory:migrate": "node scripts/migrate-vector-db.js --from /tmp/tiger_memory.db --to ./db/memory.sqlite",
|
|
29
|
+
"memory:vec:check": "python3 scripts/sqlite_vec_setup.py",
|
|
30
|
+
"memory:vec:install": "python3 scripts/sqlite_vec_setup.py --install --write-env"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"ai",
|
|
37
|
+
"agent",
|
|
38
|
+
"llm",
|
|
39
|
+
"telegram",
|
|
40
|
+
"chatbot",
|
|
41
|
+
"memory",
|
|
42
|
+
"claude",
|
|
43
|
+
"zhipu",
|
|
44
|
+
"glm",
|
|
45
|
+
"kimi",
|
|
46
|
+
"minimax"
|
|
47
|
+
],
|
|
48
|
+
"author": "AI Research Group, Dept. of Civil Engineering, KMUTT",
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"clawhub": "^0.5.0",
|
|
55
|
+
"dotenv": "^16.4.5",
|
|
56
|
+
"node-telegram-bot-api": "^0.66.0"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/scripts/audit.sh
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tiger Audit Logger
|
|
3
|
+
# Logs skill usage with sanitization (no secrets)
|
|
4
|
+
# Format: TIMESTAMP | SKILL | ACTION | STATUS | USER_HASH
|
|
5
|
+
|
|
6
|
+
TIGER_HOME="${TIGER_HOME:-$HOME/.tiger}"
|
|
7
|
+
AUDIT_LOG="$TIGER_HOME/logs/audit.log"
|
|
8
|
+
MAX_LOG_SIZE=10485760 # 10MB
|
|
9
|
+
MAX_LOG_FILES=5
|
|
10
|
+
|
|
11
|
+
# Create log directory
|
|
12
|
+
mkdir -p "$(dirname "$AUDIT_LOG")"
|
|
13
|
+
chmod 700 "$(dirname "$AUDIT_LOG")"
|
|
14
|
+
|
|
15
|
+
# Rotate logs if too large
|
|
16
|
+
rotate_logs() {
|
|
17
|
+
if [ -f "$AUDIT_LOG" ] && [ $(stat -f%z "$AUDIT_LOG" 2>/dev/null || stat -c%s "$AUDIT_LOG" 2>/dev/null || echo 0) -gt $MAX_LOG_SIZE ]; then
|
|
18
|
+
for i in $(seq $MAX_LOG_FILES -1 1); do
|
|
19
|
+
[ -f "$AUDIT_LOG.$i" ] && mv "$AUDIT_LOG.$i" "$AUDIT_LOG.$((i+1))"
|
|
20
|
+
done
|
|
21
|
+
[ -f "$AUDIT_LOG" ] && mv "$AUDIT_LOG" "$AUDIT_LOG.1"
|
|
22
|
+
touch "$AUDIT_LOG"
|
|
23
|
+
chmod 600 "$AUDIT_LOG"
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Log function
|
|
28
|
+
log_audit() {
|
|
29
|
+
rotate_logs
|
|
30
|
+
|
|
31
|
+
local skill="$1"
|
|
32
|
+
local action="$2"
|
|
33
|
+
local status="$3"
|
|
34
|
+
local timestamp=$(date -Iseconds)
|
|
35
|
+
local user_hash=$(echo "$USER@$(hostname)" | sha256sum | cut -d' ' -f1 | head -c16)
|
|
36
|
+
|
|
37
|
+
# Sanitize: remove potential secrets from action
|
|
38
|
+
local sanitized_action=$(echo "$action" | sed -E 's/(api[_-]?key|token|password|secret)[=:][^ ]+/\1=***/gi')
|
|
39
|
+
|
|
40
|
+
echo "[$timestamp] | $skill | $sanitized_action | $status | $user_hash" >> "$AUDIT_LOG"
|
|
41
|
+
chmod 600 "$AUDIT_LOG"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Example usage:
|
|
45
|
+
# log_audit "nano-banana-pro-2" "generate_image --prompt '...'" "success"
|
|
46
|
+
|
|
47
|
+
# If called directly, show usage
|
|
48
|
+
if [ "${BASH_SOURCE[0]}" == "${0}" ]; then
|
|
49
|
+
echo "Tiger Audit Logger"
|
|
50
|
+
echo "Usage: source $0 && log_audit <skill> <action> <status>"
|
|
51
|
+
echo ""
|
|
52
|
+
echo "Recent audit entries:"
|
|
53
|
+
[ -f "$AUDIT_LOG" ] && tail -20 "$AUDIT_LOG" || echo "(no entries yet)"
|
|
54
|
+
fi
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tiger Database Backup Script
|
|
3
|
+
# Runs: Daily via cron or manual
|
|
4
|
+
# Backs up: SQLite memory DB, config files
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
TIGER_HOME="${TIGER_HOME:-$HOME/.tiger}"
|
|
9
|
+
BACKUP_DIR="$TIGER_HOME/backup"
|
|
10
|
+
DB_PATH="$TIGER_HOME/memory/tiger_memory.db"
|
|
11
|
+
DATE=$(date +%Y%m%d_%H%M%S)
|
|
12
|
+
RETENTION_DAYS=30
|
|
13
|
+
|
|
14
|
+
echo "๐ฏ Tiger Backup Started: $DATE"
|
|
15
|
+
|
|
16
|
+
# Create backup directory
|
|
17
|
+
mkdir -p "$BACKUP_DIR"
|
|
18
|
+
|
|
19
|
+
# Backup SQLite database (with WAL if exists)
|
|
20
|
+
if [ -f "$DB_PATH" ]; then
|
|
21
|
+
BACKUP_FILE="$BACKUP_DIR/tiger_memory_$DATE.db"
|
|
22
|
+
sqlite3 "$DB_PATH" ".backup '$BACKUP_FILE'"
|
|
23
|
+
gzip "$BACKUP_FILE"
|
|
24
|
+
echo "โ
Database backed up: ${BACKUP_FILE}.gz"
|
|
25
|
+
else
|
|
26
|
+
echo "โ ๏ธ Database not found at $DB_PATH"
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Backup config (excluding secrets)
|
|
30
|
+
if [ -d "$TIGER_HOME/config" ]; then
|
|
31
|
+
CONFIG_BACKUP="$BACKUP_DIR/config_$DATE.tar.gz"
|
|
32
|
+
tar -czf "$CONFIG_BACKUP" -C "$TIGER_HOME" config/ 2>/dev/null || true
|
|
33
|
+
echo "โ
Config backed up: $CONFIG_BACKUP"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Cleanup old backups (keep last 30 days)
|
|
37
|
+
find "$BACKUP_DIR" -name "*.gz" -type f -mtime +$RETENTION_DAYS -delete
|
|
38
|
+
echo "๐งน Cleaned backups older than $RETENTION_DAYS days"
|
|
39
|
+
|
|
40
|
+
# Log backup event
|
|
41
|
+
logger -t tiger-backup "Backup completed: $DATE"
|
|
42
|
+
echo "โ
Backup completed successfully"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
|
|
3
|
+
const DEFAULT_ITERATIONS = 310000;
|
|
4
|
+
|
|
5
|
+
function deriveKey(passphrase, salt, iterations = DEFAULT_ITERATIONS) {
|
|
6
|
+
return crypto.pbkdf2Sync(
|
|
7
|
+
Buffer.from(String(passphrase), 'utf8'),
|
|
8
|
+
salt,
|
|
9
|
+
iterations,
|
|
10
|
+
32,
|
|
11
|
+
'sha256'
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function encryptString(plaintext, passphrase, { iterations = DEFAULT_ITERATIONS } = {}) {
|
|
16
|
+
if (!passphrase) throw new Error('Missing passphrase');
|
|
17
|
+
const salt = crypto.randomBytes(16);
|
|
18
|
+
const iv = crypto.randomBytes(12);
|
|
19
|
+
const key = deriveKey(passphrase, salt, iterations);
|
|
20
|
+
|
|
21
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
22
|
+
const ciphertext = Buffer.concat([cipher.update(Buffer.from(String(plaintext), 'utf8')), cipher.final()]);
|
|
23
|
+
const tag = cipher.getAuthTag();
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
v: 1,
|
|
27
|
+
alg: 'aes-256-gcm',
|
|
28
|
+
kdf: 'pbkdf2-sha256',
|
|
29
|
+
iterations,
|
|
30
|
+
salt_b64: salt.toString('base64'),
|
|
31
|
+
iv_b64: iv.toString('base64'),
|
|
32
|
+
tag_b64: tag.toString('base64'),
|
|
33
|
+
ciphertext_b64: ciphertext.toString('base64')
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function decryptToString(payload, passphrase) {
|
|
38
|
+
if (!passphrase) throw new Error('Missing passphrase');
|
|
39
|
+
if (!payload || payload.v !== 1) throw new Error('Unsupported payload');
|
|
40
|
+
|
|
41
|
+
const salt = Buffer.from(payload.salt_b64, 'base64');
|
|
42
|
+
const iv = Buffer.from(payload.iv_b64, 'base64');
|
|
43
|
+
const tag = Buffer.from(payload.tag_b64, 'base64');
|
|
44
|
+
const ciphertext = Buffer.from(payload.ciphertext_b64, 'base64');
|
|
45
|
+
|
|
46
|
+
const key = deriveKey(passphrase, salt, Number(payload.iterations) || DEFAULT_ITERATIONS);
|
|
47
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
48
|
+
decipher.setAuthTag(tag);
|
|
49
|
+
|
|
50
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
51
|
+
return plaintext.toString('utf8');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
encryptString,
|
|
56
|
+
decryptToString
|
|
57
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { decryptToString } = require('./cryptoEnv');
|
|
6
|
+
|
|
7
|
+
function arg(name, def = '') {
|
|
8
|
+
const idx = process.argv.indexOf(name);
|
|
9
|
+
if (idx === -1) return def;
|
|
10
|
+
return process.argv[idx + 1] || def;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const inPath = arg('--in', '.env.secrets.enc');
|
|
14
|
+
const outPath = arg('--out', '.env.secrets');
|
|
15
|
+
const passphrase = process.env.SECRETS_PASSPHRASE || '';
|
|
16
|
+
|
|
17
|
+
if (!passphrase) {
|
|
18
|
+
console.error('Missing SECRETS_PASSPHRASE env var');
|
|
19
|
+
process.exit(2);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const absIn = path.resolve(process.cwd(), inPath);
|
|
23
|
+
const absOut = path.resolve(process.cwd(), outPath);
|
|
24
|
+
|
|
25
|
+
if (!fs.existsSync(absIn)) {
|
|
26
|
+
console.error(`Encrypted file not found: ${absIn}`);
|
|
27
|
+
process.exit(2);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const payload = JSON.parse(fs.readFileSync(absIn, 'utf8'));
|
|
31
|
+
const plaintext = decryptToString(payload, passphrase);
|
|
32
|
+
fs.writeFileSync(absOut, plaintext, { mode: 0o600 });
|
|
33
|
+
|
|
34
|
+
console.log(`Wrote decrypted secrets to ${outPath}`);
|