OpenClaw JSON Editing Masterclass

TotalClaw 作者 avirweb v1.0.0

OpenClaw 配置文件、工具和数据结构的高级 JSON 编辑。处理 JSON5 配置、架构验证、合并修补、环境变量替换和类型安全修改。

源码 ↗

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install totalclaw:avirweb~openclaw-json-editing
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Aavirweb~openclaw-json-editing/file -o openclaw-json-editing.md
Git 仓库获取源码
git clone https://github.com/openclaw/skills/commit/befaf805503da488d84444535f0a61ef036b5809
## 概述(中文)

OpenClaw 配置文件、工具和数据结构的高级 JSON 编辑。处理 JSON5 配置、架构验证、合并修补、环境变量替换和类型安全修改。

## 原文

# OpenClaw JSON Editing

Expert guidance for editing JSON in the OpenClaw ecosystem. OpenClaw uses **JSON5** for configuration (allows comments, trailing commas), has sophisticated config merging, and validates with **Zod schemas**.

## Quick Reference

| Task | Command/Pattern |
|------|-----------------|
| Validate config | `openclaw config validate` |
| Apply config patch | `openclaw config patch <file.json>` |
| Safe JSON parse | Use `safeParseJson()` wrapper |
| Check config location | `openclaw config path` |
| Pretty print | `JSON.stringify(data, null, 2)` |

## OpenClaw JSON5 Config

OpenClaw config files use **JSON5** (not strict JSON):

```json5
{
  // Single-line comments are allowed
  "gateway": {
    "mode": "http",  // Trailing commas are allowed
  },
  /* Multi-line comments
     are also supported */
  "agents": {
    "main": {
      "model": "anthropic/claude-opus-4-6",
    },
  },
}
```

### Key Differences from JSON

- **Comments**: Single-line (`//`) and multi-line (`/* */`)
- **Trailing commas**: Allowed in arrays and objects
- **Unquoted keys**: `{ key: "value" }` is valid
- **Single quotes**: `'string'` is valid

### Config File Locations

| Type | Path |
|------|------|
| User config | `~/.openclaw/config.json` |
| Project config | `./openclaw.config.json` |
| Agent config | `~/.openclaw/agents/<id>/config.json` |
| Session store | `~/.openclaw/sessions/` |
| State dir | `~/.openclaw/` (or `$OPENCLAW_STATE_DIR`) |

## Safe JSON Operations

### Reading Config Files

OpenClaw uses `JSON5.parse()` for configs and safe wrappers:

```typescript
// OpenClaw's safeParseJson pattern
function safeParseJson<T>(raw: string): T | null {
  try {
    return JSON.parse(raw) as T;
  } catch {
    return null;
  }
}

// For OpenClaw configs, use JSON5
import JSON5 from "json5";

function loadConfigFile(path: string): unknown {
  try {
    const raw = fs.readFileSync(path, "utf8");
    return JSON5.parse(raw);  // Allows comments, trailing commas
  } catch {
    return undefined;
  }
}
```

### Writing Config Files

OpenClaw writes with specific formatting and permissions:

```typescript
function saveJsonFile(pathname: string, data: unknown) {
  const dir = path.dirname(pathname);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
  }
  // 2-space indentation, trailing newline
  fs.writeFileSync(pathname, `${JSON.stringify(data, null, 2)}\n`, "utf8");
  fs.chmodSync(pathname, 0o600);  // User read/write only
}
```

### Type Guards

Always validate before assuming structure:

```typescript
// OpenClaw's isPlainObject (strictest)
function isPlainObject(value: unknown): value is Record<string, unknown> {
  return (
    typeof value === "object" &&
    value !== null &&
    !Array.isArray(value) &&
    Object.prototype.toString.call(value) === "[object Object]"
  );
}

// Less strict version
function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
```

## Config Merging & Patching

### Merge Patch (RFC 7386)

OpenClaw uses merge patching for config updates:

```typescript
// Apply a merge patch to base config
function applyMergePatch(base: unknown, patch: unknown): unknown {
  if (!isPlainObject(patch)) {
    return patch;
  }

  const result: Record<string, unknown> = isPlainObject(base) ? { ...base } : {};

  for (const [key, value] of Object.entries(patch)) {
    if (value === null) {
      delete result[key];  // null = delete key
      continue;
    }
    if (isPlainObject(value)) {
      const baseValue = result[key];
      result[key] = applyMergePatch(
        isPlainObject(baseValue) ? baseValue : {},
        value
      );
      continue;
    }
    result[key] = value;
  }

  return result;
}
```

### Usage Examples

```javascript
// Add/update nested field
const patch = {
  agents: {
    main: {
      model: "anthropic/claude-opus-4-6"
    }
  }
};

// Delete a field (set to null)
const deletePatch = {
  agents: {
    main: {
      temperature: null  // Removes temperature
    }
  }
};

// Replace entire section
const replacePatch = {
  channels: {
    telegram: null,  // Delete old
    discord: { token: "new-token" }  // Add new
  }
};
```

## Environment Variable Substitution

OpenClaw configs support `${VAR}` and `${VAR:-default}` syntax:

```json5
{
  "auth": {
    "profiles": {
      "openai": {
        "apiKey": "${OPENAI_API_KEY}"  // Substituted at load time
      },
      "anthropic": {
        "apiKey": "${ANTHROPIC_API_KEY:-fallback-key}"
      }
    }
  }
}
```

### Handling in Code

```typescript
// Check if string contains env var reference
function containsEnvVarReference(value: string): boolean {
  return /\$\{[^}]+\}/.test(value);
}

// Collect all env var paths in an object
function collectEnvRefPaths(
  value: unknown,
  path: string,
  output: Map<string, string>
): void {
  if (typeof value === "string") {
    if (containsEnvVarReference(value)) {
      output.set(path, value);
    }
    return;
  }
  if (Array.isArray(value)) {
    value.forEach((item, index) => {
      collectEnvRefPaths(item, `${path}[${index}]`, output);
    });
    return;
  }
  if (isPlainObject(value)) {
    for (const [key, child] of Object.entries(value)) {
      const childPath = path ? `${path}.${key}` : key;
      collectEnvRefPaths(child, childPath, output);
    }
  }
}
```

## Schema Validation

### Zod Schema Pattern

OpenClaw uses Zod for runtime validation:

```typescript
import { z } from "zod";

// Define schema
const AgentConfigSchema = z.object({
  model: z.string().optional(),
  temperature: z.number().min(0).max(2).optional(),
  maxTokens: z.number().positive().optional(),
  enabled: z.boolean().default(true),
});

// Validate
type AgentConfig = z.infer<typeof AgentConfigSchema>;

function validateConfig(data: unknown): AgentConfig {
  return AgentConfigSchema.parse(data);
}

// Safe validation
function safeValidateConfig(data: unknown): AgentConfig | null {
  const result = AgentConfigSchema.safeParse(data);
  return result.success ? result.data : null;
}
```

### Common OpenClaw Schema Types

```typescript
// Model reference: "provider/model-name"
const ModelRefSchema = z.string().regex(/^[a-z0-9-]+\/[a-z0-9-]+$/i);

// Channel ID
const ChannelIdSchema = z.enum([
  "telegram", "discord", "slack", "whatsapp",
  "signal", "imessage", "irc", "web"
]);

// Duration string: "30s", "5m", "1h"
const DurationSchema = z.string().regex(/^\d+[smhd]$/);
```

## Config Includes

OpenClaw supports config file includes:

```json5
{
  "include": [
    "./base-config.json",
    "~/.openclaw/shared-channels.json"
  ],
  "agents": {
    // Local overrides
  }
}
```

### Processing Order

1. Load included files (recursive, depth-limited)
2. Merge in order (later files override earlier)
3. Apply env var substitution
4. Validate against schema
5. Apply runtime overrides

## jq Patterns for OpenClaw

### Common Operations

```bash
# Pretty print OpenClaw config
jq . ~/.openclaw/config.json

# Get gateway mode
jq '.gateway.mode' ~/.openclaw/config.json

# List all agent IDs
jq '.agents | keys[]' ~/.openclaw/config.json

# Find agent using specific model
jq '.agents | to_entries[] | select(.value.model == "anthropic/claude-opus-4-6") | .key' ~/.openclaw/config.json

# Get all channel types
jq '.channels | keys[]' ~/.openclaw/config.json

# Check if Telegram is configured
jq '.channels.telegram != null' ~/.openclaw/config.json

# Extract all model references
jq '.. | objects | select(has("model")) | .model' ~/.openclaw/config.json

# Merge patch using jq
jq '.agents.main.model = "anthropic/claude-opus-4-6"' ~/.openclaw/config.json > tmp.json \
  && mv tmp.json ~/.openclaw/config.json
```

### Advanced jq

```bash
# Deep search for all API keys (for audit)
jq '.. | objects | .apiKey? // .token? // .password? | select(.)' ~/.openclaw/con