mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
release: v1.1.0
This commit is contained in:
parent
7221d36bab
commit
7f88a6ddd4
27
.env.example
27
.env.example
@ -1,5 +1,9 @@
|
||||
# MateClaw 环境变量配置
|
||||
# 复制此文件为 .env 并填写实际值
|
||||
# 复制此文件为 .env 并填写实际值:cp .env.example .env
|
||||
#
|
||||
# ⚠️ 所有标注「必填」的项若没配置,`docker compose up` 会直接失败退出,避免把默认/示例值带到生产环境。
|
||||
|
||||
# ==================== LLM / 搜索 ====================
|
||||
|
||||
# 阿里云 DashScope API Key(必填)
|
||||
# 申请地址:https://dashscope.aliyun.com/
|
||||
@ -9,9 +13,26 @@ DASHSCOPE_API_KEY=your-dashscope-api-key-here
|
||||
# 申请地址:https://serper.dev/
|
||||
SERPER_API_KEY=
|
||||
|
||||
# 数据库配置(Docker 部署时无需修改)
|
||||
# ==================== 数据库(Docker 模式必填) ====================
|
||||
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_NAME=mateclaw
|
||||
DB_USERNAME=mateclaw
|
||||
DB_PASSWORD=mateclaw123
|
||||
|
||||
# ⚠️ 必填且请改成强密码(至少 16 位,含大小写+数字+符号)。
|
||||
# docker-compose.yml 会通过 ${DB_PASSWORD:?} 强制要求此项。
|
||||
DB_PASSWORD=change-me-strong-user-password
|
||||
|
||||
# ⚠️ MySQL root 账号密码,仅用于容器内初始化。请改成与 DB_PASSWORD 不同的强密码。
|
||||
DB_ROOT_PASSWORD=change-me-strong-root-password
|
||||
|
||||
# ==================== 安全(强烈建议覆盖) ====================
|
||||
|
||||
# JWT 签名密钥。若留空,服务器会用内置默认值并在启动日志里 WARN。
|
||||
# 生产部署必须设置,至少 32 位随机字符串:openssl rand -base64 48
|
||||
JWT_SECRET=
|
||||
|
||||
# CORS 白名单(逗号分隔,如 https://mateclaw.example.com,https://admin.example.com)。
|
||||
# 若留空,服务器会允许所有 origin 并在启动日志里 WARN。生产部署务必设置。
|
||||
MATECLAW_CORS_ALLOWED_ORIGINS=
|
||||
|
||||
159
UPGRADING.md
Normal file
159
UPGRADING.md
Normal file
@ -0,0 +1,159 @@
|
||||
# Upgrading MateClaw
|
||||
|
||||
## 1.0.x → 1.1.0
|
||||
|
||||
**TL;DR** — Most users have nothing to do. Restart with 1.1.0, Flyway's built-in repair heals known checksum drift, Ollama auto-discovery rewrites the bad `:latest` defaults, and everything else self-converges. Docker Compose deployments need a one-time `.env` update.
|
||||
|
||||
See `docs/en/releases/1.1.0.md` for the feature changelog.
|
||||
|
||||
---
|
||||
|
||||
## For everyone
|
||||
|
||||
### ⚠️ What happens automatically (no action)
|
||||
|
||||
- **Flyway migration self-heal** — 1.1.0 rewrote all MySQL migrations V2–V14 to replace unsupported `ADD COLUMN IF NOT EXISTS` syntax (Gitee #IIYHLJ). `FlywayRepairConfig` runs `flyway.repair()` on every boot, so the new checksums auto-accept and migration resumes from wherever your schema is.
|
||||
- **Ollama default model** — if your 1.0.x run auto-picked a model tag Ollama no longer has (commonly `deepseek-r1:latest`), on 1.1.0 restart `OllamaAutoDiscoveryRunner` detects the broken default and re-picks a tag-capable model (e.g. `deepseek-r1:7b`, `qwen3:latest`), preferring one that supports function calling.
|
||||
- **Stale `mate_model_config` rows** — idempotent seed data reconciles on each startup.
|
||||
|
||||
### 📋 Recommended pre-upgrade steps
|
||||
|
||||
1. Back up your database — `mateclaw` schema on MySQL, or `data/mateclaw.mv.db` on H2.
|
||||
2. Back up `data/` directory (skill workspaces, uploaded files, memory files).
|
||||
3. Note your current default model in Settings → Models in case you want to switch back.
|
||||
|
||||
### 🚀 Upgrade
|
||||
|
||||
```bash
|
||||
git pull
|
||||
cd mateclaw-server
|
||||
mvn clean package -DskipTests
|
||||
# then restart your service per your deployment method
|
||||
```
|
||||
|
||||
Or for Desktop app users: just update to 1.1.0 via the in-app updater or re-download.
|
||||
|
||||
---
|
||||
|
||||
## For Docker Compose deployments
|
||||
|
||||
**One-time migration step required** — 1.1.0 refuses to start with default hardcoded passwords.
|
||||
|
||||
### 1. Copy-paste merge the new `.env.example` keys
|
||||
|
||||
```bash
|
||||
cp .env .env.backup
|
||||
# open .env.example — it has new required keys:
|
||||
# DB_PASSWORD= (was default 'mateclaw123', now MUST be overridden)
|
||||
# DB_ROOT_PASSWORD= (new, required for MySQL root)
|
||||
# JWT_SECRET= (new, strongly recommended)
|
||||
# MATECLAW_CORS_ALLOWED_ORIGINS= (new, strongly recommended for prod)
|
||||
```
|
||||
|
||||
### 2. Set strong values in your `.env`
|
||||
|
||||
```env
|
||||
# STRONG passwords — at least 16 chars, mixed case + digits + symbols
|
||||
DB_PASSWORD=<your-strong-db-user-password>
|
||||
DB_ROOT_PASSWORD=<different-strong-root-password>
|
||||
|
||||
# 32+ char random string — generate with: openssl rand -base64 48
|
||||
JWT_SECRET=<your-jwt-secret>
|
||||
|
||||
# Production CORS allowlist — comma-separated, no wildcards
|
||||
MATECLAW_CORS_ALLOWED_ORIGINS=https://mateclaw.example.com
|
||||
```
|
||||
|
||||
If any of `DB_PASSWORD` / `DB_ROOT_PASSWORD` / `DASHSCOPE_API_KEY` is missing, `docker compose up` will fail fast with a clear error — this is intentional.
|
||||
|
||||
### 3. Existing MySQL volume compatibility
|
||||
|
||||
If you already ran 1.0.x with the old default password (`mateclaw123`), **your existing MySQL volume still has the old root password inside**. You have two options:
|
||||
|
||||
**Option A — keep existing password** (fastest, least secure):
|
||||
Set `DB_ROOT_PASSWORD=mateclaw123` and `DB_PASSWORD=mateclaw123` in `.env` to match. Upgrade works. Then rotate after upgrade using `ALTER USER ... IDENTIFIED BY ...` inside the MySQL container.
|
||||
|
||||
**Option B — fresh volume with new password** (cleanest, loses DB if not backed up):
|
||||
```bash
|
||||
docker compose down -v # ⚠️ deletes mysql_data volume; back up first
|
||||
# edit .env with new strong password
|
||||
docker compose up -d
|
||||
```
|
||||
Then re-import your backup if you kept one.
|
||||
|
||||
### 4. Restart
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker compose logs -f mateclaw-server # watch for "Flyway Successfully applied N migrations"
|
||||
```
|
||||
|
||||
Expected log lines during boot:
|
||||
- `Flyway Successfully applied N migrations to schema mateclaw`
|
||||
- `Ollama: auto-activated default model '<actual-tag>'` (if you use Ollama — should NOT say `:latest` any more)
|
||||
- `[Security] Using default JWT secret!` → means you forgot to set `JWT_SECRET` — fix and restart
|
||||
|
||||
---
|
||||
|
||||
## For local dev / H2 deployments
|
||||
|
||||
No action required. `mvn spring-boot:run` picks up the latest migrations on next start, Flyway repair handles checksum drift, H2 file at `data/mateclaw.mv.db` is preserved.
|
||||
|
||||
---
|
||||
|
||||
## Known migration quirks
|
||||
|
||||
### 1. If you manually fiddled with `flyway_schema_history`
|
||||
|
||||
In 1.0.x some users hit Flyway version collisions (V8/V9 and V9/V10) which 1.1.0 fixes by renumbering. If you manually deleted rows from `flyway_schema_history` you may see `Validate failed` on 1.1.0 startup — run:
|
||||
|
||||
```sql
|
||||
-- MySQL
|
||||
DELETE FROM flyway_schema_history WHERE success = 0;
|
||||
```
|
||||
|
||||
Then restart. `FlywayRepairConfig` will rebuild history from current schema state.
|
||||
|
||||
### 2. If your Ollama models are all in the no-tools family
|
||||
|
||||
After upgrade, agents that require tool calling will log a warning on first invocation:
|
||||
|
||||
```
|
||||
Ollama: auto-activated default model '...' but its family does not support tool calling
|
||||
```
|
||||
|
||||
Fix — pull a tool-capable model, or switch default in Settings → Models:
|
||||
|
||||
```bash
|
||||
ollama pull qwen3
|
||||
# or
|
||||
ollama pull llama3.1:8b
|
||||
# or
|
||||
ollama pull mistral-nemo
|
||||
```
|
||||
|
||||
### 3. If you had custom tools using `extract_document_text` / wiki tools
|
||||
|
||||
Wiki chunk schema changed (new `embedding` + `embedding_model` columns on `mate_wiki_chunk`). Your existing wiki pages work unchanged; only semantic search is new and requires an embedding model to be configured in Settings → Models (a default DashScope embedding is seeded).
|
||||
|
||||
---
|
||||
|
||||
## Rolling back to 1.0.x
|
||||
|
||||
Not recommended (some new tables / columns don't exist in 1.0.x), but possible if you backed up the DB before upgrade:
|
||||
|
||||
```bash
|
||||
git checkout v1.0.418
|
||||
# restore DB backup
|
||||
docker compose up -d # or mvn spring-boot:run
|
||||
```
|
||||
|
||||
If you need to keep the new data but downgrade the app, you're in unsupported territory — open a Gitee issue.
|
||||
|
||||
---
|
||||
|
||||
## Getting help
|
||||
|
||||
- **Logs first**: `mateclaw-server/logs/mateclaw.log` + `mateclaw-error.log` have everything. Flyway decisions are at INFO level in main log.
|
||||
- **Doctor tab**: in-app Settings → Doctor runs basic health checks
|
||||
- **Gitee**: https://gitee.com/matevip_admin/mateclaw/issues — include your upgrade path (1.0.?? → 1.1.0), profile (H2 / MySQL), and the last 100 lines of startup log
|
||||
@ -2,15 +2,20 @@ version: '3.8'
|
||||
|
||||
services:
|
||||
# MySQL 数据库
|
||||
#
|
||||
# ⚠️ 密码通过环境变量传入,必须从 .env 文件提供。首次部署前:
|
||||
# 1. cp .env.example .env
|
||||
# 2. 编辑 .env 把 DB_ROOT_PASSWORD / DB_PASSWORD 改成强密码
|
||||
# 未设置会直接在 `docker compose up` 时报错,避免把默认密码带到生产环境。
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: mateclaw-mysql
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: mateclaw123
|
||||
MYSQL_DATABASE: mateclaw
|
||||
MYSQL_USER: mateclaw
|
||||
MYSQL_PASSWORD: mateclaw123
|
||||
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?DB_ROOT_PASSWORD is required in .env}
|
||||
MYSQL_DATABASE: ${DB_NAME:-mateclaw}
|
||||
MYSQL_USER: ${DB_USERNAME:-mateclaw}
|
||||
MYSQL_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required in .env}
|
||||
TZ: Asia/Shanghai
|
||||
ports:
|
||||
- "3306:3306"
|
||||
@ -58,11 +63,13 @@ services:
|
||||
SPRING_PROFILES_ACTIVE: mysql
|
||||
DB_HOST: mysql
|
||||
DB_PORT: 3306
|
||||
DB_NAME: mateclaw
|
||||
DB_USERNAME: mateclaw
|
||||
DB_PASSWORD: mateclaw123
|
||||
DASHSCOPE_API_KEY: ${DASHSCOPE_API_KEY}
|
||||
DB_NAME: ${DB_NAME:-mateclaw}
|
||||
DB_USERNAME: ${DB_USERNAME:-mateclaw}
|
||||
DB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required in .env}
|
||||
DASHSCOPE_API_KEY: ${DASHSCOPE_API_KEY:?DASHSCOPE_API_KEY is required in .env}
|
||||
SERPER_API_KEY: ${SERPER_API_KEY:-}
|
||||
JWT_SECRET: ${JWT_SECRET:-}
|
||||
MATECLAW_CORS_ALLOWED_ORIGINS: ${MATECLAW_CORS_ALLOWED_ORIGINS:-}
|
||||
ports:
|
||||
- "18080:18080"
|
||||
volumes:
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>vip.mate</groupId>
|
||||
<artifactId>mateclaw-server</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<version>1.1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>MateClaw Server</name>
|
||||
|
||||
@ -29,12 +29,16 @@ class ReasoningDispatcherLlmCallCountTest {
|
||||
@DisplayName("LLM 调用计数限制")
|
||||
class LlmCallCountLimit {
|
||||
|
||||
// ReasoningDispatcher.LLM_CALL_MULTIPLIER = 5(默认 maxIterations=10 → 阈值 50)。
|
||||
// 本系列用例固定 MAX_ITERATIONS=10,所以 limit=50、limit-1=49。
|
||||
// 如果后续 MULTIPLIER 再改,这里的常量也要同步。
|
||||
|
||||
@Test
|
||||
@DisplayName("达到上限但有最终回答 → 放行到 finalAnswerNode")
|
||||
void shouldAllowFinalAnswerEvenWhenLlmCallLimitReached() throws Exception {
|
||||
OverAllState state = new OverAllState(Map.of(
|
||||
CURRENT_ITERATION, 5, MAX_ITERATIONS, 10,
|
||||
LLM_CALL_COUNT, 30, NEEDS_TOOL_CALL, false
|
||||
LLM_CALL_COUNT, 50, NEEDS_TOOL_CALL, false
|
||||
));
|
||||
assertEquals(FINAL_ANSWER_NODE, dispatcher.apply(state));
|
||||
}
|
||||
@ -44,7 +48,7 @@ class ReasoningDispatcherLlmCallCountTest {
|
||||
void shouldBlockToolCallWhenLlmCallLimitReached() throws Exception {
|
||||
OverAllState state = new OverAllState(Map.of(
|
||||
CURRENT_ITERATION, 5, MAX_ITERATIONS, 10,
|
||||
LLM_CALL_COUNT, 30, NEEDS_TOOL_CALL, true
|
||||
LLM_CALL_COUNT, 50, NEEDS_TOOL_CALL, true
|
||||
));
|
||||
assertEquals(LIMIT_EXCEEDED_NODE, dispatcher.apply(state));
|
||||
}
|
||||
@ -54,7 +58,7 @@ class ReasoningDispatcherLlmCallCountTest {
|
||||
void shouldBlockSummarizeWhenLlmCallLimitReached() throws Exception {
|
||||
OverAllState state = new OverAllState(Map.of(
|
||||
CURRENT_ITERATION, 5, MAX_ITERATIONS, 10,
|
||||
LLM_CALL_COUNT, 30,
|
||||
LLM_CALL_COUNT, 50,
|
||||
NEEDS_TOOL_CALL, false, SHOULD_SUMMARIZE, true
|
||||
));
|
||||
assertEquals(LIMIT_EXCEEDED_NODE, dispatcher.apply(state));
|
||||
@ -95,7 +99,7 @@ class ReasoningDispatcherLlmCallCountTest {
|
||||
void shouldNotTriggerAtLimitMinusOne() throws Exception {
|
||||
OverAllState state = new OverAllState(Map.of(
|
||||
CURRENT_ITERATION, 5, MAX_ITERATIONS, 10,
|
||||
LLM_CALL_COUNT, 29, NEEDS_TOOL_CALL, true
|
||||
LLM_CALL_COUNT, 49, NEEDS_TOOL_CALL, true
|
||||
));
|
||||
assertEquals(ACTION_NODE, dispatcher.apply(state));
|
||||
}
|
||||
|
||||
@ -86,7 +86,8 @@ class FinalAnswerNodeTest {
|
||||
void shouldFallbackWhenNoContentAndNotStopped() throws Exception {
|
||||
OverAllState state = new OverAllState(Map.of());
|
||||
Map<String, Object> result = node.apply(state);
|
||||
assertEquals("未能生成回答,请重试。", result.get(FINAL_ANSWER));
|
||||
// Fallback 文案在 Jobs-voice 改写后统一为英文(commit 01c3888)。
|
||||
assertEquals("Failed to generate a response, please retry.", result.get(FINAL_ANSWER));
|
||||
assertEquals("error_fallback", result.get(FINISH_REASON));
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,9 +140,15 @@ class SkillSecurityServiceTest {
|
||||
|
||||
SkillValidationResult result = securityService.scanContent(content, "db_skill");
|
||||
|
||||
assertTrue(result.isBlocked());
|
||||
// scanContent 走 FileRole.DOCUMENTATION 降级规则(database-only skill 只是描述文本,
|
||||
// 不能执行代码):SUDO/CURL_PIPE 等 script 规则会替换为 SUDO_DOC/CURL_PIPE_SH_DOC,
|
||||
// 严重度降为 MEDIUM,**生成 finding 但不阻断**。阻断语义由 scanDirectory 里
|
||||
// scripts/* 真实可执行文件触发。
|
||||
assertTrue(result.getFindings().stream()
|
||||
.anyMatch(f -> f.getRuleId().equals("SUDO_USAGE") || f.getRuleId().equals("DESTRUCTIVE_RM")));
|
||||
.anyMatch(f -> f.getRuleId().equals("SUDO_DOC")
|
||||
|| f.getRuleId().equals("SUDO_USAGE")
|
||||
|| f.getRuleId().equals("DESTRUCTIVE_RM")),
|
||||
"danger-pattern finding should still be detected in database-only skill content");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -203,7 +209,8 @@ class SkillSecurityServiceTest {
|
||||
.orElse(null);
|
||||
|
||||
assertNotNull(sudoFinding);
|
||||
assertEquals("scripts/bad.sh", sudoFinding.getFilePath());
|
||||
// 归一化路径分隔符,让断言在 Windows / Unix 都稳定(Path.toString 在 Windows 用 `\`)
|
||||
assertEquals("scripts/bad.sh", sudoFinding.getFilePath().replace('\\', '/'));
|
||||
assertEquals(3, sudoFinding.getLineNumber());
|
||||
assertNotNull(sudoFinding.getSnippet());
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mateclaw-ui",
|
||||
"version": "1.1.0-SNAPSHOT",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "MateClaw - Personal AI Assistant Web Console",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mateclaw/webchat",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0-SNAPSHOT",
|
||||
"description": "MateClaw WebChat embeddable widget",
|
||||
"type": "module",
|
||||
"main": "dist/mateclaw-webchat.umd.js",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user