diff --git a/config/default.yaml b/config/default.yaml index f907639..b5eb68f 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -10,7 +10,10 @@ telegram: allowed_chat_ids: [] # Add your Telegram chat ID server: - tailscale_only: true + # Tailscale Serve config (optional). Enable `serve: true` to expose the + # gateway to your tailnet via `tailscale serve`. + tailscale: + serve: false localhost: true port: 18800 diff --git a/docs/plans/state.json b/docs/plans/state.json index f6d8ab9..d52c23f 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -30,10 +30,19 @@ "summary": "Docs fix: clarified the Tailscale auth note in docs/api/PROTOCOL.md to reference server.tailscale_identity." }, "todo-config-default-server-schema-mismatch": { - "status": "planned", + "status": "completed", "date": "2026-02-16", - "summary": "TODO: reconcile config/default.yaml with src/config/schema.ts (config uses server.tailscale_only but schema defines server.tailscale.*). Update docs snippets (if any) and add a test/doctor check to prevent future drift." + "updated": "2026-02-16", + "summary": "Reconciled config/default.yaml with src/config/schema.ts by replacing deprecated server.tailscale_only with server.tailscale.*. Added a doctor warning for deprecated keys and a regression test to prevent future drift.", + "files_modified": [ + "config/default.yaml", + "src/cli/doctor.ts", + "src/cli/doctor.test.ts", + "src/config/defaultYaml.test.ts" + ], + "test_status": "pnpm test:run + pnpm typecheck passing" }, + "openclaw-gap-roadmap": { "file": "2026-02-15-openclaw-gap-roadmap.md", "status": "planned", @@ -2123,7 +2132,7 @@ }, "overall_progress": { - "total_test_count": 1689, + "total_test_count": 1692, "all_tests_passing": true, "p0_completion": "3/3 (100%)", "p1_completion": "4/4 (100%)", diff --git a/src/cli/doctor.test.ts b/src/cli/doctor.test.ts index 59ebfc7..98757ad 100644 --- a/src/cli/doctor.test.ts +++ b/src/cli/doctor.test.ts @@ -72,6 +72,28 @@ telegram: expect(configValidates?.status).toBe('fail'); }); + it('warns when deprecated server.tailscale_only key is present', async () => { + mkdirSync(testDir, { recursive: true }); + const configPath = join(testDir, 'config.yaml'); + writeFileSync(configPath, ` +telegram: + bot_token: "test-token" + allowed_chat_ids: [123] +server: + tailscale_only: true +models: + default: + provider: anthropic + model: claude-sonnet +`); + + const ctx: DoctorContext = { configPath, dataDir: testDir }; + const results = await runChecks(ctx); + + const deprecated = results.find(r => r.label.includes('deprecated keys')); + expect(deprecated?.status).toBe('warn'); + }); + it('reports PASS for writable data directory', async () => { mkdirSync(testDir, { recursive: true }); const configPath = join(testDir, 'config.yaml'); diff --git a/src/cli/doctor.ts b/src/cli/doctor.ts index 28d9da3..96fa3b8 100644 --- a/src/cli/doctor.ts +++ b/src/cli/doctor.ts @@ -71,6 +71,30 @@ const checkConfigValidates: Check = async (ctx) => { } }; +const checkDeprecatedConfigKeys: Check = async (ctx) => { + if (!existsSync(ctx.configPath)) { + return { status: 'skip', label: 'Config deprecated keys', detail: '(no config file)' }; + } + + try { + const raw = readFileSync(ctx.configPath, 'utf-8'); + const parsed = parse(raw) as any; + const tailscaleOnly = Boolean(parsed?.server && typeof parsed.server === 'object' && 'tailscale_only' in parsed.server); + + if (tailscaleOnly) { + return { + status: 'warn', + label: 'Config deprecated keys', + detail: 'server.tailscale_only is deprecated/ignored; use server.tailscale.* + server.localhost instead', + }; + } + + return { status: 'pass', label: 'Config deprecated keys' }; + } catch { + return { status: 'skip', label: 'Config deprecated keys', detail: '(could not read/parse config)' }; + } +}; + const checkEnvVars: Check = async (ctx) => { if (!existsSync(ctx.configPath)) { return { status: 'skip', label: 'Env vars resolved', detail: '(no config file)' }; @@ -492,6 +516,7 @@ const allChecks: Check[] = [ checkConfigExists, checkOverlayExists, checkConfigParses, + checkDeprecatedConfigKeys, checkConfigValidates, checkEnvVars, checkDataDir, diff --git a/src/config/defaultYaml.test.ts b/src/config/defaultYaml.test.ts new file mode 100644 index 0000000..5f01bdb --- /dev/null +++ b/src/config/defaultYaml.test.ts @@ -0,0 +1,23 @@ +import { describe, it, expect } from 'vitest'; +import { readFileSync } from 'fs'; +import { parse } from 'yaml'; + +describe('config/default.yaml', () => { + it('does not use deprecated server.tailscale_only key', () => { + const raw = readFileSync('config/default.yaml', 'utf-8'); + const parsed = parse(raw) as any; + + expect(parsed).toBeTruthy(); + expect(parsed.server).toBeTruthy(); + expect(parsed.server.tailscale_only).toBeUndefined(); + }); + + it('documents server.tailscale.* shape', () => { + const raw = readFileSync('config/default.yaml', 'utf-8'); + const parsed = parse(raw) as any; + + expect(parsed.server.tailscale).toBeTruthy(); + expect(typeof parsed.server.tailscale).toBe('object'); + expect(typeof parsed.server.tailscale.serve).toBe('boolean'); + }); +});