OpenClaw JSON Editing Masterclass

ClawSkills 作者 avirweb v1.0.0

Advanced JSON editing for OpenClaw configuration files, tools, and data structures. Handles JSON5 configs, schema validation, merge patching, env var substitution, and type-safe modifications.

源码 ↗

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install clawskills:avirweb~openclaw-json-editing
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Aavirweb~openclaw-json-editing/file -o openclaw-json-editing.md
Git 仓库获取源码
git clone https://github.com/openclaw/skills/commit/befaf805503da488d84444535f0a61ef036b5809
# 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/config.json

# Collect all environment variable references
jq -r '.. | strings | select(cont