From 539af750d461a344b57ea3d80707fe781e95d873 Mon Sep 17 00:00:00 2001 From: cheats1314 <3030240693@qq.com> Date: Thu, 23 Apr 2026 22:22:18 +0800 Subject: [PATCH] fix(setup): detect registered groups from v2 central db Align the environment check with the v2 setup flow so existing wired agent groups are detected from data/v2.db instead of the retired v1 store. This prevents setup from reporting no registered groups on valid v2 installs and adds regression coverage for both v2 and pre-migration state. Co-Authored-By: Claude Opus 4.7 --- setup/environment.test.ts | 97 +++++++++++++++++++++------------------ setup/environment.ts | 47 ++++++++++--------- 2 files changed, 78 insertions(+), 66 deletions(-) diff --git a/setup/environment.test.ts b/setup/environment.test.ts index deda62f..7765693 100644 --- a/setup/environment.test.ts +++ b/setup/environment.test.ts @@ -1,5 +1,7 @@ -import { describe, it, expect, beforeEach } from 'vitest'; +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import fs from 'fs'; +import os from 'os'; +import path from 'path'; import Database from 'better-sqlite3'; @@ -17,58 +19,63 @@ describe('environment detection', () => { }); }); -describe('registered groups DB query', () => { - let db: Database.Database; +describe('detectRegisteredGroups', () => { + let tempDir: string; beforeEach(() => { - db = new Database(':memory:'); - db.exec(`CREATE TABLE IF NOT EXISTS registered_groups ( - jid TEXT PRIMARY KEY, - name TEXT NOT NULL, - folder TEXT NOT NULL UNIQUE, - trigger_pattern TEXT NOT NULL, - added_at TEXT NOT NULL, - container_config TEXT, - requires_trigger INTEGER DEFAULT 1 - )`); + tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nanoclaw-env-test-')); + fs.mkdirSync(path.join(tempDir, 'data'), { recursive: true }); }); - it('returns 0 for empty table', () => { - const row = db - .prepare('SELECT COUNT(*) as count FROM registered_groups') - .get() as { count: number }; - expect(row.count).toBe(0); + afterEach(() => { + fs.rmSync(tempDir, { recursive: true, force: true }); }); - it('returns correct count after inserts', () => { - db.prepare( - `INSERT INTO registered_groups (jid, name, folder, trigger_pattern, added_at, requires_trigger) - VALUES (?, ?, ?, ?, ?, ?)`, - ).run( - '123@g.us', - 'Group 1', - 'group-1', - '@Andy', - '2024-01-01T00:00:00.000Z', - 1, - ); + it('returns false when no registration state exists', async () => { + const { detectRegisteredGroups } = await import('./environment.js'); + expect(detectRegisteredGroups(tempDir)).toBe(false); + }); - db.prepare( - `INSERT INTO registered_groups (jid, name, folder, trigger_pattern, added_at, requires_trigger) - VALUES (?, ?, ?, ?, ?, ?)`, - ).run( - '456@g.us', - 'Group 2', - 'group-2', - '@Andy', - '2024-01-01T00:00:00.000Z', - 1, - ); + it('detects pre-migration registered_groups.json', async () => { + const { detectRegisteredGroups } = await import('./environment.js'); + fs.writeFileSync(path.join(tempDir, 'data', 'registered_groups.json'), '[]'); + expect(detectRegisteredGroups(tempDir)).toBe(true); + }); - const row = db - .prepare('SELECT COUNT(*) as count FROM registered_groups') - .get() as { count: number }; - expect(row.count).toBe(2); + it('returns false for an empty v2 central DB', async () => { + const { detectRegisteredGroups } = await import('./environment.js'); + const db = new Database(path.join(tempDir, 'data', 'v2.db')); + db.exec(` + CREATE TABLE agent_groups (id TEXT PRIMARY KEY); + CREATE TABLE messaging_group_agents ( + id TEXT PRIMARY KEY, + messaging_group_id TEXT NOT NULL, + agent_group_id TEXT NOT NULL + ); + `); + db.close(); + + expect(detectRegisteredGroups(tempDir)).toBe(false); + }); + + it('detects wired agent groups in the v2 central DB', async () => { + const { detectRegisteredGroups } = await import('./environment.js'); + const db = new Database(path.join(tempDir, 'data', 'v2.db')); + db.exec(` + CREATE TABLE agent_groups (id TEXT PRIMARY KEY); + CREATE TABLE messaging_group_agents ( + id TEXT PRIMARY KEY, + messaging_group_id TEXT NOT NULL, + agent_group_id TEXT NOT NULL + ); + `); + db.prepare('INSERT INTO agent_groups (id) VALUES (?)').run('ag-1'); + db.prepare( + 'INSERT INTO messaging_group_agents (id, messaging_group_id, agent_group_id) VALUES (?, ?, ?)', + ).run('mga-1', 'mg-1', 'ag-1'); + db.close(); + + expect(detectRegisteredGroups(tempDir)).toBe(true); }); }); diff --git a/setup/environment.ts b/setup/environment.ts index 4a83665..6986396 100644 --- a/setup/environment.ts +++ b/setup/environment.ts @@ -7,11 +7,35 @@ import path from 'path'; import Database from 'better-sqlite3'; -import { STORE_DIR } from '../src/config.js'; import { log } from '../src/log.js'; import { commandExists, getPlatform, isHeadless, isWSL } from './platform.js'; import { emitStatus } from './status.js'; +export function detectRegisteredGroups(projectRoot: string): boolean { + if (fs.existsSync(path.join(projectRoot, 'data', 'registered_groups.json'))) { + return true; + } + + const dbPath = path.join(projectRoot, 'data', 'v2.db'); + if (!fs.existsSync(dbPath)) return false; + + let db: Database.Database | null = null; + try { + db = new Database(dbPath, { readonly: true }); + const row = db + .prepare( + `SELECT COUNT(DISTINCT ag.id) as count FROM agent_groups ag + JOIN messaging_group_agents mga ON mga.agent_group_id = ag.id`, + ) + .get() as { count: number }; + return row.count > 0; + } catch { + return false; + } finally { + db?.close(); + } +} + export async function run(_args: string[]): Promise { const projectRoot = process.cwd(); @@ -39,26 +63,7 @@ export async function run(_args: string[]): Promise { const authDir = path.join(projectRoot, 'store', 'auth'); const hasAuth = fs.existsSync(authDir) && fs.readdirSync(authDir).length > 0; - let hasRegisteredGroups = false; - // Check JSON file first (pre-migration) - if (fs.existsSync(path.join(projectRoot, 'data', 'registered_groups.json'))) { - hasRegisteredGroups = true; - } else { - // Check SQLite directly using better-sqlite3 (no sqlite3 CLI needed) - const dbPath = path.join(STORE_DIR, 'messages.db'); - if (fs.existsSync(dbPath)) { - try { - const db = new Database(dbPath, { readonly: true }); - const row = db - .prepare('SELECT COUNT(*) as count FROM registered_groups') - .get() as { count: number }; - if (row.count > 0) hasRegisteredGroups = true; - db.close(); - } catch { - // Table might not exist yet - } - } - } + const hasRegisteredGroups = detectRegisteredGroups(projectRoot); // Check for existing OpenClaw installation const homedir = (await import('os')).homedir();