mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 18:10:42 +00:00
160 lines
4.3 KiB
TypeScript
160 lines
4.3 KiB
TypeScript
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 } from "../runtime.js";
|
|
import { normalizeOptionalString } from "../shared/string-coerce.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}`);
|
|
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) {
|
|
runtime.log(
|
|
JSON.stringify(
|
|
{
|
|
count: commitments.length,
|
|
status: status ?? (opts.all ? null : "pending"),
|
|
agentId: normalizeOptionalString(opts.agent) ?? null,
|
|
store: resolveCommitmentStorePath(),
|
|
commitments,
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|
|
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.");
|
|
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 = opts.ids.map((id) => id.trim()).filter(Boolean);
|
|
if (ids.length === 0) {
|
|
runtime.error("At least one commitment id is required.");
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
const cfg = getRuntimeConfig();
|
|
await markCommitmentsStatus({
|
|
cfg,
|
|
ids,
|
|
status: "dismissed",
|
|
nowMs: Date.now(),
|
|
});
|
|
if (opts.json) {
|
|
runtime.log(JSON.stringify({ dismissed: ids }, null, 2));
|
|
return;
|
|
}
|
|
runtime.log(info(`Dismissed commitments: ${ids.join(", ")}`));
|
|
}
|