fix(config): align default.yaml with server schema

This commit is contained in:
William Valentin
2026-02-15 18:11:29 -08:00
parent 83b8bea5eb
commit 81385745e6
5 changed files with 86 additions and 4 deletions
+4 -1
View File
@@ -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
+12 -3
View File
@@ -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%)",
+22
View File
@@ -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');
+25
View File
@@ -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,
+23
View File
@@ -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');
});
});