Skip to content

Commit 2939ac6

Browse files
committed
docs: document status channel helpers
1 parent ae948fa commit 2939ac6

4 files changed

Lines changed: 30 additions & 2 deletions

File tree

src/commands/status-all/channel-issues.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Groups channel-scoped status issues for status report table rendering.
2+
// Kept tiny so both text and report builders share identical issue ordering.
3+
4+
/** Groups issue-like rows by channel id while preserving the original issue order per channel. */
15
export function groupChannelIssuesByChannel<T extends { channel: string }>(
26
issues: readonly T[],
37
): Map<string, T[]> {

src/commands/status-all/channels-table.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Builds the user-facing `openclaw status --all` channel summary table rows.
2+
// Gateway issues are folded in here so every text/report surface shows the same warning state.
3+
14
import { groupChannelIssuesByChannel } from "./channel-issues.js";
25

36
type ChannelTableRowInput = {
@@ -20,6 +23,7 @@ export const statusChannelsTableColumns = [
2023
{ key: "Detail", header: "Detail", flex: true, minWidth: 24 },
2124
] as const;
2225

26+
/** Formats channel rows and overlays live gateway issues onto their display state. */
2327
export function buildStatusChannelsTableRows(params: {
2428
rows: readonly ChannelTableRowInput[];
2529
channelIssues: readonly ChannelIssueLike[];
@@ -33,6 +37,7 @@ export function buildStatusChannelsTableRows(params: {
3337
const formatIssueMessage = params.formatIssueMessage ?? ((message: string) => message);
3438
return params.rows.map((row) => {
3539
const issues = channelIssuesByChannel.get(row.id) ?? [];
40+
// A disabled channel stays disabled even if the gateway still reports stale issues for it.
3641
const effectiveState = row.state === "off" ? "off" : issues.length > 0 ? "warn" : row.state;
3742
const issueSuffix =
3843
issues.length > 0

src/commands/status-all/channels-token-summary.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Summarizes channel token/account credential fields for `openclaw status --all`.
2+
// The display path is intentionally secret-safe unless the caller explicitly requests disclosure.
3+
14
import { asRecord } from "@openclaw/normalization-core/record-coerce";
25
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
36
import { hasConfiguredUnavailableCredentialStatus } from "../../channels/account-snapshot-fields.js";
@@ -10,6 +13,7 @@ export type ChannelAccountTokenSummaryRow = {
1013
snapshot: ChannelAccountSnapshot;
1114
};
1215

16+
/** Collapses credential sources into a stable count label such as `env×2+file`. */
1317
function summarizeSources(sources: Array<string | undefined>): {
1418
label: string;
1519
parts: string[];
@@ -32,6 +36,7 @@ function formatTokenHint(token: string, opts: { showSecrets: boolean }): string
3236
return "empty";
3337
}
3438
if (!opts.showSecrets) {
39+
// Show a stable fingerprint and length so operators can compare tokens without leaking them.
3540
return `sha256:${sha256HexPrefix(t, 8)} · len ${t.length}`;
3641
}
3742
const head = t.slice(0, 4);
@@ -42,6 +47,7 @@ function formatTokenHint(token: string, opts: { showSecrets: boolean }): string
4247
return `${head}${tail} · len ${t.length}`;
4348
}
4449

50+
/** Returns the credential status sentence for enabled channel accounts, if the plugin exposes token fields. */
4551
export function summarizeTokenConfig(params: {
4652
accounts: ChannelAccountTokenSummaryRow[];
4753
showSecrets: boolean;
@@ -52,6 +58,7 @@ export function summarizeTokenConfig(params: {
5258
}
5359

5460
const accountRecs = enabled.map((a) => asRecord(a.account));
61+
// Token field names are plugin-owned; infer the credential mode from the fields the plugin exposes.
5562
const hasBotTokenField = accountRecs.some((r) => "botToken" in r);
5663
const hasAppTokenField = accountRecs.some((r) => "appToken" in r);
5764
const hasSigningSecretField = accountRecs.some(
@@ -82,6 +89,7 @@ export function summarizeTokenConfig(params: {
8289
hasSigningSecretField &&
8390
enabled.every((a) => accountIsHttpMode(asRecord(a.account)))
8491
) {
92+
// Slack/Mattermost-style HTTP mode needs both bot token and signing secret to receive events.
8593
const unavailable = enabled.filter((a) => hasConfiguredUnavailableCredentialStatus(a.account));
8694
const ready = enabled.filter((a) => {
8795
const rec = asRecord(a.account);
@@ -137,6 +145,7 @@ export function summarizeTokenConfig(params: {
137145
}
138146

139147
if (hasBotTokenField && hasAppTokenField) {
148+
// Socket-mode style plugins require both halves; a single present token is still unusable.
140149
const unavailable = enabled.filter((a) => hasConfiguredUnavailableCredentialStatus(a.account));
141150
const ready = enabled.filter((a) => {
142151
const rec = asRecord(a.account);

src/commands/status-all/channels.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Builds channel status rows and account details for `openclaw status --all`.
2+
// This layer stays plugin-generic: channel-specific auth rules live in plugin config/status hooks.
3+
14
import fs from "node:fs";
25
import { asRecord } from "@openclaw/normalization-core/record-coerce";
36
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
@@ -62,6 +65,7 @@ function existsSyncMaybe(p: string | undefined): boolean | null {
6265
}
6366
}
6467

68+
/** Resolves one configured/default account into the normalized row shape used by status rendering. */
6569
async function resolveChannelAccountRow(
6670
params: ResolvedChannelAccountRowParams,
6771
): Promise<ChannelAccountRow> {
@@ -148,6 +152,7 @@ const buildAccountNotes = (params: {
148152
const allowFrom =
149153
plugin.config.resolveAllowFrom?.({ cfg, accountId: snapshot.accountId }) ?? snapshot.allowFrom;
150154
if (allowFrom?.length) {
155+
// Cap allow-list output so large channel policies do not dominate the status table.
151156
const formatted = formatChannelAllowFrom({
152157
plugin,
153158
cfg,
@@ -168,6 +173,7 @@ function resolveLinkFields(summary: unknown): {
168173
authAgeMs: number | null;
169174
selfE164: string | null;
170175
} {
176+
// Plugin summaries are optional extension data; normalize only the fields the core table understands.
171177
const rec = asRecord(summary);
172178
const statusState = typeof rec.statusState === "string" ? rec.statusState : null;
173179
const linked = typeof rec.linked === "boolean" ? rec.linked : null;
@@ -190,6 +196,7 @@ function collectMissingPaths(accounts: ChannelAccountRow[]): string[] {
190196
"dbPath",
191197
"authDir",
192198
]) {
199+
// Account config and snapshots can each expose file-backed credential paths.
193200
const raw =
194201
(accountRec[key] as string | undefined) ?? (snapshotRec[key] as string | undefined);
195202
const ok = existsSyncMaybe(raw);
@@ -214,8 +221,7 @@ function formatLoadFailureDetail(message: string): string {
214221
return `plugin load failed: ${reason}; run openclaw doctor --fix`;
215222
}
216223

217-
// `status --all` channels table.
218-
// Keep this generic: channel-specific rules belong in the channel plugin.
224+
/** Builds the `status --all` channel summary and per-account detail tables. */
219225
export async function buildChannelsTable(
220226
cfg: OpenClawConfig,
221227
opts?: {
@@ -249,6 +255,7 @@ export async function buildChannelsTable(
249255
includeSetupFallbackPlugins,
250256
});
251257
for (const plugin of readOnlyPlugins.plugins) {
258+
// Use the plugin's default account even when no accounts are configured so setup guidance is concrete.
252259
const accountIds = plugin.config.listAccountIds(cfg);
253260
const defaultAccountId = resolveChannelDefaultAccountId({
254261
plugin,
@@ -288,6 +295,7 @@ export async function buildChannelsTable(
288295
hasRuntimeCredentialAvailable({ liveAccounts, accountId: entry.accountId }))
289296
? {
290297
...entry,
298+
// Fast-mode scans may not resolve local secrets; runtime evidence can still prove availability.
291299
account: markConfiguredUnavailableCredentialStatusesAvailable(entry.account),
292300
}
293301
: entry,
@@ -318,6 +326,7 @@ export async function buildChannelsTable(
318326
const label = plugin.meta.label ?? plugin.id;
319327

320328
const state = (() => {
329+
// Precedence matches operator actionability: disabled, local file breakage, plugin issues, auth, link.
321330
if (!anyEnabled) {
322331
return "off";
323332
}
@@ -512,6 +521,7 @@ export async function buildChannelsTable(
512521
});
513522
if (!hint || hint.channelId !== channelId) {
514523
if (!includeSetupFallbackPlugins && explicitConfiguredChannelIds.has(channelId)) {
524+
// Fast mode intentionally skips setup fallback plugins, but configured ids still deserve visibility.
515525
rows.push({
516526
id: channelId,
517527
label: sanitizeForLog(channelId).trim() || "configured-channel",

0 commit comments

Comments
 (0)