Files
openclaw/src/commands/message.ts
Josh Avant da34f81ce2 fix(secrets): scope message SecretRef resolution and harden doctor/status paths (#48728)
* fix(secrets): scope message runtime resolution and harden doctor/status

* docs: align message/doctor/status SecretRef behavior notes

* test(cli): accept scoped targetIds wiring in secret-resolution coverage

* fix(secrets): keep scoped allowedPaths isolation and tighten coverage gate

* fix(secrets): avoid default-account coercion in scoped target selection

* test(doctor): cover inactive telegram secretref inspect path

* docs

Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>

* changelog

Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>

---------

Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>
2026-03-17 00:01:34 -05:00

91 lines
3.0 KiB
TypeScript

import {
CHANNEL_MESSAGE_ACTION_NAMES,
type ChannelMessageActionName,
} from "../channels/plugins/types.js";
import { resolveCommandSecretRefsViaGateway } from "../cli/command-secret-gateway.js";
import { getScopedChannelsCommandSecretTargets } from "../cli/command-secret-targets.js";
import { resolveMessageSecretScope } from "../cli/message-secret-scope.js";
import { createOutboundSendDeps, type CliDeps } from "../cli/outbound-send-deps.js";
import { withProgress } from "../cli/progress.js";
import { loadConfig } from "../config/config.js";
import type { OutboundSendDeps } from "../infra/outbound/deliver.js";
import { runMessageAction } from "../infra/outbound/message-action-runner.js";
import type { RuntimeEnv } from "../runtime.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
import { buildMessageCliJson, formatMessageCliText } from "./message-format.js";
export async function messageCommand(
opts: Record<string, unknown>,
deps: CliDeps,
runtime: RuntimeEnv,
) {
const loadedRaw = loadConfig();
const scope = resolveMessageSecretScope({
channel: opts.channel,
target: opts.target,
targets: opts.targets,
accountId: opts.accountId,
});
const scopedTargets = getScopedChannelsCommandSecretTargets({
config: loadedRaw,
channel: scope.channel,
accountId: scope.accountId,
});
const { resolvedConfig: cfg, diagnostics } = await resolveCommandSecretRefsViaGateway({
config: loadedRaw,
commandName: "message",
targetIds: scopedTargets.targetIds,
...(scopedTargets.allowedPaths ? { allowedPaths: scopedTargets.allowedPaths } : {}),
});
for (const entry of diagnostics) {
runtime.log(`[secrets] ${entry}`);
}
const rawAction = typeof opts.action === "string" ? opts.action.trim() : "";
const actionInput = rawAction || "send";
const actionMatch = (CHANNEL_MESSAGE_ACTION_NAMES as readonly string[]).find(
(name) => name.toLowerCase() === actionInput.toLowerCase(),
);
if (!actionMatch) {
throw new Error(`Unknown message action: ${actionInput}`);
}
const action = actionMatch as ChannelMessageActionName;
const outboundDeps: OutboundSendDeps = createOutboundSendDeps(deps);
const run = async () =>
await runMessageAction({
cfg,
action,
params: opts,
deps: outboundDeps,
gateway: {
clientName: GATEWAY_CLIENT_NAMES.CLI,
mode: GATEWAY_CLIENT_MODES.CLI,
},
});
const json = opts.json === true;
const dryRun = opts.dryRun === true;
const needsSpinner = !json && !dryRun && (action === "send" || action === "poll");
const result = needsSpinner
? await withProgress(
{
label: action === "poll" ? "Sending poll..." : "Sending...",
indeterminate: true,
enabled: true,
},
run,
)
: await run();
if (json) {
runtime.log(JSON.stringify(buildMessageCliJson(result), null, 2));
return;
}
for (const line of formatMessageCliText(result)) {
runtime.log(line);
}
}