Files
openclaw/src/commands/commitments.ts
Peter Steinberger 77d9ac30bb refactor: reuse shared coercion helpers (#86419)
* refactor: share talk event metric extraction

* refactor: reuse shared coercion helpers

* refactor: reuse shared primitive guards

* refactor: reuse shared record guard

* refactor: reuse shared primitive helpers

* refactor: reuse shared string guards

* refactor: reuse shared non-empty string guard

* refactor: share plugin primitive coercion helpers

* refactor: reuse plugin coercion helpers

* refactor: reuse plugin coercion helpers in more plugins

* refactor: reuse channel coercion helpers

* refactor: reuse monitor coercion helpers

* refactor: reuse provider coercion helpers

* refactor: reuse core coercion helpers

* refactor: reuse runtime coercion helpers

* refactor: reuse helper coercion in codex paths

* refactor: reuse helper coercion in runtime paths

* refactor: reuse codex app-server coercion helpers

* refactor: reuse codex record helpers

* refactor: reuse migration and qa record helpers

* refactor: reuse feishu and core helper guards

* refactor: reuse browser and policy coercion helpers

* refactor: reuse memory wiki record helper

* refactor: share boolean coercion helpers

* refactor: reuse finite number coercion

* refactor: reuse trimmed string list helpers

* refactor: reuse string list normalization

* refactor: reuse remaining string list helpers

* refactor: reuse string entry normalizer

* refactor: share sorted string helpers

* refactor: share string list normalization

* test: preserve command registry browser imports

* refactor: reuse trimmed list helpers

* refactor: reuse string dedupe helpers

* refactor: reuse local dedupe helpers

* refactor: reuse more string dedupe helpers

* refactor: reuse command string dedupe helpers

* refactor: dedupe memory path lists with helper

* refactor: expose string dedupe helpers to plugins

* refactor: reuse core string dedupe helpers

* refactor: reuse shared unique value helpers

* refactor: reuse unique helpers in agent utilities

* refactor: reuse unique helpers in config plumbing

* refactor: reuse unique helpers in extensions

* refactor: reuse unique helpers in core utilities

* refactor: reuse unique helpers in qa plugins

* refactor: reuse unique helpers in memory plugins

* refactor: reuse unique helpers in channel plugins

* refactor: reuse unique helpers in core tails

* refactor: reuse unique helper in comfy workflow

* refactor: reuse unique helpers in test utilities

* refactor: expose unique value helper to plugins

* refactor: reuse unique helpers for numeric lists

* refactor: replace index dedupe filters

* refactor: reuse string entry normalization

* refactor: reuse string normalization in plugin helpers

* refactor: reuse string normalization in extension helpers

* refactor: reuse string normalization in channel parsers

* refactor: reuse string normalization in memory search

* refactor: reuse string normalization in provider parsers

* refactor: reuse string normalization in qa helpers

* refactor: reuse string normalization in infra parsers

* refactor: reuse string normalization in messaging parsers

* refactor: reuse string normalization in core parsers

* refactor: reuse string normalization in extension parsers

* refactor: reuse string normalization in remaining parsers

* refactor: reuse string normalization in final parser spots

* refactor: reuse string normalization in qa media helpers

* refactor: reuse normalization in provider and media lists

* refactor: reuse normalization for remaining set filters

* refactor: reuse normalization in policy allowlists

* refactor: reuse normalization in session and owner lists

* refactor: centralize primitive string lists

* refactor: reuse lowercase entry helpers

* refactor: reuse sorted string helpers

* refactor: reuse unique trimmed helpers

* refactor: reuse string normalization helpers

* refactor: reuse catalog string helpers

* refactor: reuse remaining string helpers

* refactor: simplify remaining list normalization

* refactor: reuse codex auth order normalization

* chore: refresh plugin sdk api baseline

* fix: make shared string sorting deterministic

* chore: refresh plugin sdk api baseline

* fix: align host env security ordering
2026-05-25 21:20:41 +01:00

162 lines
4.6 KiB
TypeScript

import { formatCliCommand } from "../cli/command-format.js";
import {
listCommitments,
markCommitmentsStatus,
resolveCommitmentStorePath,
} from "../commitments/store.js";
import type { CommitmentRecord, CommitmentStatus } from "../commitments/types.js";
import { getRuntimeConfig } from "../config/config.js";
import { info } from "../globals.js";
import { type RuntimeEnv, writeRuntimeJson } from "../runtime.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import { normalizeStringEntries } from "../shared/string-normalization.js";
import { sanitizeTerminalText } from "../terminal/safe-text.js";
import { isRich, theme } from "../terminal/theme.js";
const STATUS_VALUES = new Set<CommitmentStatus>([
"pending",
"sent",
"dismissed",
"snoozed",
"expired",
]);
function truncate(value: string, maxChars: number): string {
return value.length <= maxChars ? value : `${value.slice(0, maxChars - 1)}...`;
}
function safe(value: string): string {
return sanitizeTerminalText(value);
}
function parseStatus(raw: string | undefined, runtime: RuntimeEnv): CommitmentStatus | undefined {
const status = normalizeOptionalString(raw);
if (!status) {
return undefined;
}
if (STATUS_VALUES.has(status as CommitmentStatus)) {
return status as CommitmentStatus;
}
runtime.error(
`Unknown commitment status: ${status}. Use one of: ${Array.from(STATUS_VALUES).join(", ")}.`,
);
runtime.exit(1);
return undefined;
}
function isActiveCommitment(commitment: CommitmentRecord): boolean {
return commitment.status === "pending" || commitment.status === "snoozed";
}
function formatDue(ms: number): string {
return new Date(ms).toISOString();
}
function formatRows(commitments: CommitmentRecord[], rich: boolean): string[] {
const header = [
"ID".padEnd(16),
"Status".padEnd(10),
"Kind".padEnd(16),
"Due".padEnd(24),
"Scope".padEnd(28),
"Suggested text",
].join(" ");
const lines = [rich ? theme.heading(header) : header];
for (const commitment of commitments) {
const scope = truncate(
[
safe(commitment.agentId),
safe(commitment.channel),
safe(commitment.to ?? commitment.sessionKey),
]
.filter(Boolean)
.join("/"),
28,
);
lines.push(
[
truncate(safe(commitment.id), 16).padEnd(16),
safe(commitment.status).padEnd(10),
safe(commitment.kind).padEnd(16),
formatDue(commitment.dueWindow.earliestMs).padEnd(24),
scope.padEnd(28),
truncate(safe(commitment.suggestedText), 90),
].join(" "),
);
}
return lines;
}
export async function commitmentsListCommand(
opts: { json?: boolean; status?: string; all?: boolean; agent?: string },
runtime: RuntimeEnv,
): Promise<void> {
const cfg = getRuntimeConfig();
const status = opts.all ? undefined : parseStatus(opts.status ?? "pending", runtime);
if (!opts.all && opts.status && !status) {
return;
}
const commitments = (
await listCommitments({
cfg,
status,
agentId: normalizeOptionalString(opts.agent),
})
).filter((commitment) => opts.all || status || isActiveCommitment(commitment));
if (opts.json) {
writeRuntimeJson(runtime, {
count: commitments.length,
status: status ?? (opts.all ? null : "pending"),
agentId: normalizeOptionalString(opts.agent) ?? null,
store: resolveCommitmentStorePath(),
commitments,
});
return;
}
runtime.log(info(`Commitments: ${commitments.length}`));
runtime.log(info(`Store: ${resolveCommitmentStorePath()}`));
if (status) {
runtime.log(info(`Status filter: ${status}`));
}
if (opts.agent) {
runtime.log(info(`Agent filter: ${opts.agent}`));
}
if (commitments.length === 0) {
runtime.log(
`No commitments found. Run ${formatCliCommand("openclaw commitments --all")} to include dismissed and expired commitments.`,
);
return;
}
for (const line of formatRows(commitments, isRich())) {
runtime.log(line);
}
}
export async function commitmentsDismissCommand(
opts: { ids: string[]; json?: boolean },
runtime: RuntimeEnv,
): Promise<void> {
const ids = normalizeStringEntries(opts.ids);
if (ids.length === 0) {
runtime.error(
`At least one commitment id is required. Run ${formatCliCommand("openclaw commitments list")} to choose one.`,
);
runtime.exit(1);
return;
}
const cfg = getRuntimeConfig();
await markCommitmentsStatus({
cfg,
ids,
status: "dismissed",
nowMs: Date.now(),
});
if (opts.json) {
writeRuntimeJson(runtime, { dismissed: ids });
return;
}
runtime.log(info(`Dismissed commitments: ${ids.join(", ")}`));
}