Files
Masato HoshinoandAyaan Zaidi c1618cd937 fix(directory): reject explicitly blank channel selectors (#153731)
Closes #153730

## What Problem This Solves

Fixes: directory commands infer a configured channel and can return its contacts when `--channel` is explicitly empty or whitespace-only, such as an unset shell variable.

## User Impact

Only omitting `--channel` requests automatic selection. Explicit blank values now fail with `--channel must not be blank` before command bootstrap and directory setup or lookup. Scripts that previously passed blank values must omit the flag instead. Valid channel names, aliases, padded names, account defaults, and documented inference remain unchanged.

## Why This Change Was Made

Directory option parsing now uses the same strict selector rule as message and channel-auth commands. Those existing consumers share one validator without changing their validation positions or precedence. General channel normalization and durable plugin setup behavior are unchanged.

This rejects invalid input before command bootstrap, not before every process side effect: the CLI can still read best-effort proxy configuration before parsing options.

## Evidence

- Supported `pnpm openclaw directory` before/after proof on isolated current-main builds: an explicit blank channel returned the configured IRC contacts before the fix and a CLI error afterward. The six changed files match the prepared PR.
- Fourteen real CLI cases per phase passed, covering all four directory leaves, omitted/explicit/padded selectors, whitespace, `--channel=`, text and JSON output, invalid configuration, blank accounts, and both selector orders.
- Native startup traces show candidate blank selectors reject before the command's config-ready/plugin-registry stages; omitted and valid controls still enter them. Config bytes and canonical plugin installation records/artifacts remained unchanged in the explicitly enabled synthetic fixture. No external network request occurred.
- 168 focused directory, message, and channel-auth tests passed. Eight blank-channel cases fail on the original directory registration; existing blank-account controls pass. Shared table coverage replaces duplicated mock assertions and repeated precedence cases.
- Scoped type-aware lint, formatting, whitespace, both source line-limit ratchets, and executable-runtime builds passed. Required hosted CI covers full static/matrix integration.

The IRC directory is config-backed; no live IRC transport, Gateway, or zero-config-read claim is made. Runtime proof uses current-main builds with matching changed owners, not a full replay of the PR's older base.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-20 21:40:05 +05:30

5.8 KiB

summary, read_when, title
summary read_when title
CLI reference for `openclaw directory` (self, peers, groups)
You want to look up contacts/groups/self ids for a channel
You are developing a channel directory adapter
Directory

openclaw directory

Directory lookups for channels that support them: contacts/peers, groups, and "me" (self).

Results are meant to be pasted into other commands, especially openclaw message send --target ....

Common flags

  • --channel <name>: channel id or alias. Required when several channels are configured, and auto-selected when only one is configured.
  • --account <id>: account id (default: channel default)
  • --json: output JSON
  • --limit <n>: positive integer cap for peers/groups/members listings

Omit --channel to auto-select the only configured channel. Explicitly empty and whitespace-only values fail with --channel must not be blank while options are parsed, before command bootstrap and directory setup or lookup. Scripts that pass an unset shell variable must omit the flag to request automatic selection.

Omit --account to select the channel default. Explicitly empty and whitespace-only account values fail with --account must not be blank before account setup or lookup.

--limit requires a positive integer. Omit --limit to use the selected channel plugin's default. Explicitly empty and whitespace-only values are rejected.

Default output renders IDs and names in a table. Empty list results name the channel and account that were queried. JSON list output uses an empty array ([]). Failures exit nonzero and use the canonical { "ok": false, "error": { "type": "cli_error", "message": "..." } } envelope in JSON mode.

Notes

  • For many channels, results are config-backed (allowlists / configured groups) rather than a live provider directory.
  • Before a live lookup, OpenClaw resolves configured SecretRefs only for the selected channel and account. Resolved credentials remain runtime-only. Plugin installation and auto-enable writes preserve the authored references without persisting runtime defaults.
  • WhatsApp group listing is live. Gateway lookups reuse its owned connection. A standalone command opens the linked session only when no other process owns that account. Otherwise it reports that live groups are unavailable.
  • An already-installed channel plugin can lack directory support. In that case the command reports the unsupported operation. It does not try to reinstall or upgrade the plugin to add support.

Using results with message send

openclaw directory peers list --channel slack --query "U0"
openclaw message send --channel slack --target user:U012ABCDEF --message "hello"

ID formats by channel

Channel Target id format
WhatsApp +15551234567 (DM), 1234567890-1234567890@g.us (group), 120363123456789@newsletter (Channel/Newsletter, outbound only)
Signal Configured aliases resolve to E.164/UUID DM targets or group:<id> group targets
Telegram @username or numeric chat id; groups use numeric ids
Slack user:U… and channel:C…
Discord user:<id> and channel:<id>
Matrix (plugin) user:@user:server, room:!roomId:server, or #alias:server
Microsoft Teams (plugin) user:<id> and conversation:<id>
Zalo (plugin) User id (Bot API)
Zalo Personal / zalouser (plugin) Thread id (DM/group), from zca (me, friend list, group list)

Self ("me")

openclaw directory self --channel zalouser

A channel may legitimately return no self identity. This is a successful empty result (exit code 0), not a failed lookup. Channels without a self resolver report that the channel does not expose a self identity, without suggesting account troubleshooting:

{
  "status": "unavailable",
  "channel": "telegram",
  "accountId": "default",
  "reason": "self-identity-unsupported"
}

When a channel implements self lookup but returns no identity, the text output names the channel and account and suggests checking its configuration and authentication. JSON callers can distinguish that case by its reason:

{
  "status": "unavailable",
  "channel": "msteams",
  "accountId": "default",
  "reason": "plugin-returned-no-self-identity"
}

Peers (contacts/users)

openclaw directory peers list --channel zalouser
openclaw directory peers list --channel zalouser --query "name"
openclaw directory peers list --channel zalouser --limit 50

Groups

openclaw directory groups list --channel zalouser
openclaw directory groups list --channel zalouser --query "work"
openclaw directory groups members --channel zalouser --group-id <id>

groups members requires a non-blank --group-id. Empty or whitespace-only IDs fail before plugin setup or lookup.