mirror of https://github.com/openclaw/openclaw.git
refactor: share exec host approval helpers
This commit is contained in:
parent
3bc9d9177d
commit
6720bf5be0
|
|
@ -1,5 +1,4 @@
|
|||
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
import { buildExecApprovalUnavailableReplyPayload } from "../infra/exec-approval-reply.js";
|
||||
import {
|
||||
addAllowlistEntry,
|
||||
type ExecAsk,
|
||||
|
|
@ -14,20 +13,22 @@ import { detectCommandObfuscation } from "../infra/exec-obfuscation-detect.js";
|
|||
import type { SafeBinProfile } from "../infra/exec-safe-bin-policy.js";
|
||||
import { logInfo } from "../logger.js";
|
||||
import { markBackgrounded, tail } from "./bash-process-registry.js";
|
||||
import { sendExecApprovalFollowup } from "./bash-tools.exec-approval-followup.js";
|
||||
import {
|
||||
buildExecApprovalRequesterContext,
|
||||
buildExecApprovalTurnSourceContext,
|
||||
registerExecApprovalRequestForHostOrThrow,
|
||||
} from "./bash-tools.exec-approval-request.js";
|
||||
import {
|
||||
buildDefaultExecApprovalRequestArgs,
|
||||
buildExecApprovalFollowupTarget,
|
||||
buildExecApprovalPendingToolResult,
|
||||
createExecApprovalDecisionState,
|
||||
createAndRegisterDefaultExecApprovalRequest,
|
||||
resolveBaseExecApprovalDecision,
|
||||
resolveApprovalDecisionOrUndefined,
|
||||
resolveExecHostApprovalContext,
|
||||
sendExecApprovalFollowupResult,
|
||||
} from "./bash-tools.exec-host-shared.js";
|
||||
import {
|
||||
buildApprovalPendingMessage,
|
||||
DEFAULT_NOTIFY_TAIL_CHARS,
|
||||
createApprovalSlug,
|
||||
normalizeNotifyOutput,
|
||||
|
|
@ -140,6 +141,28 @@ export async function processGatewayAllowlist(
|
|||
}
|
||||
|
||||
if (requiresAsk) {
|
||||
const requestArgs = buildDefaultExecApprovalRequestArgs({
|
||||
warnings: params.warnings,
|
||||
approvalRunningNoticeMs: params.approvalRunningNoticeMs,
|
||||
createApprovalSlug,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
});
|
||||
const registerGatewayApproval = async (approvalId: string) =>
|
||||
await registerExecApprovalRequestForHostOrThrow({
|
||||
approvalId,
|
||||
command: params.command,
|
||||
workdir: params.workdir,
|
||||
host: "gateway",
|
||||
security: hostSecurity,
|
||||
ask: hostAsk,
|
||||
...buildExecApprovalRequesterContext({
|
||||
agentId: params.agentId,
|
||||
sessionKey: params.sessionKey,
|
||||
}),
|
||||
resolvedPath: allowlistEval.segments[0]?.resolution?.resolvedPath,
|
||||
...buildExecApprovalTurnSourceContext(params),
|
||||
});
|
||||
const {
|
||||
approvalId,
|
||||
approvalSlug,
|
||||
|
|
@ -150,57 +173,46 @@ export async function processGatewayAllowlist(
|
|||
sentApproverDms,
|
||||
unavailableReason,
|
||||
} = await createAndRegisterDefaultExecApprovalRequest({
|
||||
warnings: params.warnings,
|
||||
approvalRunningNoticeMs: params.approvalRunningNoticeMs,
|
||||
createApprovalSlug,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
register: async (approvalId) =>
|
||||
await registerExecApprovalRequestForHostOrThrow({
|
||||
approvalId,
|
||||
command: params.command,
|
||||
workdir: params.workdir,
|
||||
host: "gateway",
|
||||
security: hostSecurity,
|
||||
ask: hostAsk,
|
||||
...buildExecApprovalRequesterContext({
|
||||
agentId: params.agentId,
|
||||
sessionKey: params.sessionKey,
|
||||
}),
|
||||
resolvedPath: allowlistEval.segments[0]?.resolution?.resolvedPath,
|
||||
...buildExecApprovalTurnSourceContext(params),
|
||||
}),
|
||||
...requestArgs,
|
||||
register: registerGatewayApproval,
|
||||
});
|
||||
const resolvedPath = allowlistEval.segments[0]?.resolution?.resolvedPath;
|
||||
const effectiveTimeout =
|
||||
typeof params.timeoutSec === "number" ? params.timeoutSec : params.defaultTimeoutSec;
|
||||
const followupTarget = buildExecApprovalFollowupTarget({
|
||||
approvalId,
|
||||
sessionKey: params.notifySessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
});
|
||||
|
||||
void (async () => {
|
||||
const decision = await resolveApprovalDecisionOrUndefined({
|
||||
approvalId,
|
||||
preResolvedDecision,
|
||||
onFailure: () =>
|
||||
void sendExecApprovalFollowup({
|
||||
approvalId,
|
||||
sessionKey: params.notifySessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
resultText: `Exec denied (gateway id=${approvalId}, approval-request-failed): ${params.command}`,
|
||||
}),
|
||||
void sendExecApprovalFollowupResult(
|
||||
followupTarget,
|
||||
`Exec denied (gateway id=${approvalId}, approval-request-failed): ${params.command}`,
|
||||
),
|
||||
});
|
||||
if (decision === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const baseDecision = resolveBaseExecApprovalDecision({
|
||||
const {
|
||||
baseDecision,
|
||||
approvedByAsk: initialApprovedByAsk,
|
||||
deniedReason: initialDeniedReason,
|
||||
} = createExecApprovalDecisionState({
|
||||
decision,
|
||||
askFallback,
|
||||
obfuscationDetected: obfuscation.detected,
|
||||
});
|
||||
let approvedByAsk = baseDecision.approvedByAsk;
|
||||
let deniedReason = baseDecision.deniedReason;
|
||||
let approvedByAsk = initialApprovedByAsk;
|
||||
let deniedReason = initialDeniedReason;
|
||||
|
||||
if (baseDecision.timedOut && askFallback === "allowlist") {
|
||||
if (!analysisOk || !allowlistSatisfied) {
|
||||
|
|
@ -232,15 +244,10 @@ export async function processGatewayAllowlist(
|
|||
}
|
||||
|
||||
if (deniedReason) {
|
||||
await sendExecApprovalFollowup({
|
||||
approvalId,
|
||||
sessionKey: params.notifySessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
resultText: `Exec denied (gateway id=${approvalId}, ${deniedReason}): ${params.command}`,
|
||||
}).catch(() => {});
|
||||
await sendExecApprovalFollowupResult(
|
||||
followupTarget,
|
||||
`Exec denied (gateway id=${approvalId}, ${deniedReason}): ${params.command}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -266,15 +273,10 @@ export async function processGatewayAllowlist(
|
|||
timeoutSec: effectiveTimeout,
|
||||
});
|
||||
} catch {
|
||||
await sendExecApprovalFollowup({
|
||||
approvalId,
|
||||
sessionKey: params.notifySessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
resultText: `Exec denied (gateway id=${approvalId}, spawn-failed): ${params.command}`,
|
||||
}).catch(() => {});
|
||||
await sendExecApprovalFollowupResult(
|
||||
followupTarget,
|
||||
`Exec denied (gateway id=${approvalId}, spawn-failed): ${params.command}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -288,63 +290,22 @@ export async function processGatewayAllowlist(
|
|||
const summary = output
|
||||
? `Exec finished (gateway id=${approvalId}, session=${run.session.id}, ${exitLabel})\n${output}`
|
||||
: `Exec finished (gateway id=${approvalId}, session=${run.session.id}, ${exitLabel})`;
|
||||
await sendExecApprovalFollowup({
|
||||
approvalId,
|
||||
sessionKey: params.notifySessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
resultText: summary,
|
||||
}).catch(() => {});
|
||||
await sendExecApprovalFollowupResult(followupTarget, summary);
|
||||
})();
|
||||
|
||||
return {
|
||||
pendingResult: {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text:
|
||||
unavailableReason !== null
|
||||
? (buildExecApprovalUnavailableReplyPayload({
|
||||
warningText,
|
||||
reason: unavailableReason,
|
||||
channelLabel: initiatingSurface.channelLabel,
|
||||
sentApproverDms,
|
||||
}).text ?? "")
|
||||
: buildApprovalPendingMessage({
|
||||
warningText,
|
||||
approvalSlug,
|
||||
approvalId,
|
||||
command: params.command,
|
||||
cwd: params.workdir,
|
||||
host: "gateway",
|
||||
}),
|
||||
},
|
||||
],
|
||||
details:
|
||||
unavailableReason !== null
|
||||
? ({
|
||||
status: "approval-unavailable",
|
||||
reason: unavailableReason,
|
||||
channelLabel: initiatingSurface.channelLabel,
|
||||
sentApproverDms,
|
||||
host: "gateway",
|
||||
command: params.command,
|
||||
cwd: params.workdir,
|
||||
warningText,
|
||||
} satisfies ExecToolDetails)
|
||||
: ({
|
||||
status: "approval-pending",
|
||||
approvalId,
|
||||
approvalSlug,
|
||||
expiresAtMs,
|
||||
host: "gateway",
|
||||
command: params.command,
|
||||
cwd: params.workdir,
|
||||
warningText,
|
||||
} satisfies ExecToolDetails),
|
||||
},
|
||||
pendingResult: buildExecApprovalPendingToolResult({
|
||||
host: "gateway",
|
||||
command: params.command,
|
||||
cwd: params.workdir,
|
||||
warningText,
|
||||
approvalId,
|
||||
approvalSlug,
|
||||
expiresAtMs,
|
||||
initiatingSurface,
|
||||
sentApproverDms,
|
||||
unavailableReason,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import crypto from "node:crypto";
|
||||
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
import { buildExecApprovalUnavailableReplyPayload } from "../infra/exec-approval-reply.js";
|
||||
import {
|
||||
type ExecApprovalsFile,
|
||||
type ExecAsk,
|
||||
|
|
@ -13,20 +12,13 @@ import { detectCommandObfuscation } from "../infra/exec-obfuscation-detect.js";
|
|||
import { buildNodeShellCommand } from "../infra/node-shell.js";
|
||||
import { parsePreparedSystemRunPayload } from "../infra/system-run-approval-context.js";
|
||||
import { logInfo } from "../logger.js";
|
||||
import { sendExecApprovalFollowup } from "./bash-tools.exec-approval-followup.js";
|
||||
import {
|
||||
buildExecApprovalRequesterContext,
|
||||
buildExecApprovalTurnSourceContext,
|
||||
registerExecApprovalRequestForHostOrThrow,
|
||||
} from "./bash-tools.exec-approval-request.js";
|
||||
import * as execHostShared from "./bash-tools.exec-host-shared.js";
|
||||
import {
|
||||
createAndRegisterDefaultExecApprovalRequest,
|
||||
resolveBaseExecApprovalDecision,
|
||||
resolveApprovalDecisionOrUndefined,
|
||||
resolveExecHostApprovalContext,
|
||||
} from "./bash-tools.exec-host-shared.js";
|
||||
import {
|
||||
buildApprovalPendingMessage,
|
||||
DEFAULT_NOTIFY_TAIL_CHARS,
|
||||
createApprovalSlug,
|
||||
normalizeNotifyOutput,
|
||||
|
|
@ -61,7 +53,7 @@ export type ExecuteNodeHostCommandParams = {
|
|||
export async function executeNodeHostCommand(
|
||||
params: ExecuteNodeHostCommandParams,
|
||||
): Promise<AgentToolResult<ExecToolDetails>> {
|
||||
const { hostSecurity, hostAsk, askFallback } = resolveExecHostApprovalContext({
|
||||
const { hostSecurity, hostAsk, askFallback } = execHostShared.resolveExecHostApprovalContext({
|
||||
agentId: params.agentId,
|
||||
security: params.security,
|
||||
ask: params.ask,
|
||||
|
|
@ -216,6 +208,29 @@ export async function executeNodeHostCommand(
|
|||
}) satisfies Record<string, unknown>;
|
||||
|
||||
if (requiresAsk) {
|
||||
const requestArgs = execHostShared.buildDefaultExecApprovalRequestArgs({
|
||||
warnings: params.warnings,
|
||||
approvalRunningNoticeMs: params.approvalRunningNoticeMs,
|
||||
createApprovalSlug,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
});
|
||||
const registerNodeApproval = async (approvalId: string) =>
|
||||
await registerExecApprovalRequestForHostOrThrow({
|
||||
approvalId,
|
||||
systemRunPlan: prepared.plan,
|
||||
env: nodeEnv,
|
||||
workdir: runCwd,
|
||||
host: "node",
|
||||
nodeId,
|
||||
security: hostSecurity,
|
||||
ask: hostAsk,
|
||||
...buildExecApprovalRequesterContext({
|
||||
agentId: runAgentId,
|
||||
sessionKey: runSessionKey,
|
||||
}),
|
||||
...buildExecApprovalTurnSourceContext(params),
|
||||
});
|
||||
const {
|
||||
approvalId,
|
||||
approvalSlug,
|
||||
|
|
@ -225,57 +240,45 @@ export async function executeNodeHostCommand(
|
|||
initiatingSurface,
|
||||
sentApproverDms,
|
||||
unavailableReason,
|
||||
} = await createAndRegisterDefaultExecApprovalRequest({
|
||||
warnings: params.warnings,
|
||||
approvalRunningNoticeMs: params.approvalRunningNoticeMs,
|
||||
createApprovalSlug,
|
||||
} = await execHostShared.createAndRegisterDefaultExecApprovalRequest({
|
||||
...requestArgs,
|
||||
register: registerNodeApproval,
|
||||
});
|
||||
const followupTarget = execHostShared.buildExecApprovalFollowupTarget({
|
||||
approvalId,
|
||||
sessionKey: params.notifySessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
register: async (approvalId) =>
|
||||
await registerExecApprovalRequestForHostOrThrow({
|
||||
approvalId,
|
||||
systemRunPlan: prepared.plan,
|
||||
env: nodeEnv,
|
||||
workdir: runCwd,
|
||||
host: "node",
|
||||
nodeId,
|
||||
security: hostSecurity,
|
||||
ask: hostAsk,
|
||||
...buildExecApprovalRequesterContext({
|
||||
agentId: runAgentId,
|
||||
sessionKey: runSessionKey,
|
||||
}),
|
||||
...buildExecApprovalTurnSourceContext(params),
|
||||
}),
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
});
|
||||
|
||||
void (async () => {
|
||||
const decision = await resolveApprovalDecisionOrUndefined({
|
||||
const decision = await execHostShared.resolveApprovalDecisionOrUndefined({
|
||||
approvalId,
|
||||
preResolvedDecision,
|
||||
onFailure: () =>
|
||||
void sendExecApprovalFollowup({
|
||||
approvalId,
|
||||
sessionKey: params.notifySessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
resultText: `Exec denied (node=${nodeId} id=${approvalId}, approval-request-failed): ${params.command}`,
|
||||
}),
|
||||
void execHostShared.sendExecApprovalFollowupResult(
|
||||
followupTarget,
|
||||
`Exec denied (node=${nodeId} id=${approvalId}, approval-request-failed): ${params.command}`,
|
||||
),
|
||||
});
|
||||
if (decision === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const baseDecision = resolveBaseExecApprovalDecision({
|
||||
const {
|
||||
baseDecision,
|
||||
approvedByAsk: initialApprovedByAsk,
|
||||
deniedReason: initialDeniedReason,
|
||||
} = execHostShared.createExecApprovalDecisionState({
|
||||
decision,
|
||||
askFallback,
|
||||
obfuscationDetected: obfuscation.detected,
|
||||
});
|
||||
let approvedByAsk = baseDecision.approvedByAsk;
|
||||
let approvedByAsk = initialApprovedByAsk;
|
||||
let approvalDecision: "allow-once" | "allow-always" | null = null;
|
||||
let deniedReason = baseDecision.deniedReason;
|
||||
let deniedReason = initialDeniedReason;
|
||||
|
||||
if (baseDecision.timedOut && askFallback === "full" && approvedByAsk) {
|
||||
approvalDecision = "allow-once";
|
||||
|
|
@ -288,15 +291,10 @@ export async function executeNodeHostCommand(
|
|||
}
|
||||
|
||||
if (deniedReason) {
|
||||
await sendExecApprovalFollowup({
|
||||
approvalId,
|
||||
sessionKey: params.notifySessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
resultText: `Exec denied (node=${nodeId} id=${approvalId}, ${deniedReason}): ${params.command}`,
|
||||
}).catch(() => {});
|
||||
await execHostShared.sendExecApprovalFollowupResult(
|
||||
followupTarget,
|
||||
`Exec denied (node=${nodeId} id=${approvalId}, ${deniedReason}): ${params.command}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -330,76 +328,28 @@ export async function executeNodeHostCommand(
|
|||
const summary = output
|
||||
? `Exec finished (node=${nodeId} id=${approvalId}, ${exitLabel})\n${output}`
|
||||
: `Exec finished (node=${nodeId} id=${approvalId}, ${exitLabel})`;
|
||||
await sendExecApprovalFollowup({
|
||||
approvalId,
|
||||
sessionKey: params.notifySessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
resultText: summary,
|
||||
}).catch(() => {});
|
||||
await execHostShared.sendExecApprovalFollowupResult(followupTarget, summary);
|
||||
} catch {
|
||||
await sendExecApprovalFollowup({
|
||||
approvalId,
|
||||
sessionKey: params.notifySessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
resultText: `Exec denied (node=${nodeId} id=${approvalId}, invoke-failed): ${params.command}`,
|
||||
}).catch(() => {});
|
||||
await execHostShared.sendExecApprovalFollowupResult(
|
||||
followupTarget,
|
||||
`Exec denied (node=${nodeId} id=${approvalId}, invoke-failed): ${params.command}`,
|
||||
);
|
||||
}
|
||||
})();
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text:
|
||||
unavailableReason !== null
|
||||
? (buildExecApprovalUnavailableReplyPayload({
|
||||
warningText,
|
||||
reason: unavailableReason,
|
||||
channelLabel: initiatingSurface.channelLabel,
|
||||
sentApproverDms,
|
||||
}).text ?? "")
|
||||
: buildApprovalPendingMessage({
|
||||
warningText,
|
||||
approvalSlug,
|
||||
approvalId,
|
||||
command: prepared.plan.commandText,
|
||||
cwd: runCwd,
|
||||
host: "node",
|
||||
nodeId,
|
||||
}),
|
||||
},
|
||||
],
|
||||
details:
|
||||
unavailableReason !== null
|
||||
? ({
|
||||
status: "approval-unavailable",
|
||||
reason: unavailableReason,
|
||||
channelLabel: initiatingSurface.channelLabel,
|
||||
sentApproverDms,
|
||||
host: "node",
|
||||
command: params.command,
|
||||
cwd: params.workdir,
|
||||
nodeId,
|
||||
warningText,
|
||||
} satisfies ExecToolDetails)
|
||||
: ({
|
||||
status: "approval-pending",
|
||||
approvalId,
|
||||
approvalSlug,
|
||||
expiresAtMs,
|
||||
host: "node",
|
||||
command: params.command,
|
||||
cwd: params.workdir,
|
||||
nodeId,
|
||||
warningText,
|
||||
} satisfies ExecToolDetails),
|
||||
};
|
||||
return execHostShared.buildExecApprovalPendingToolResult({
|
||||
host: "node",
|
||||
command: params.command,
|
||||
cwd: params.workdir,
|
||||
warningText,
|
||||
approvalId,
|
||||
approvalSlug,
|
||||
expiresAtMs,
|
||||
initiatingSurface,
|
||||
sentApproverDms,
|
||||
unavailableReason,
|
||||
nodeId,
|
||||
});
|
||||
}
|
||||
|
||||
const startedAt = Date.now();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import crypto from "node:crypto";
|
||||
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { buildExecApprovalUnavailableReplyPayload } from "../infra/exec-approval-reply.js";
|
||||
import {
|
||||
hasConfiguredExecApprovalDmRoute,
|
||||
type ExecApprovalInitiatingSurfaceState,
|
||||
|
|
@ -12,11 +14,14 @@ import {
|
|||
type ExecAsk,
|
||||
type ExecSecurity,
|
||||
} from "../infra/exec-approvals.js";
|
||||
import { sendExecApprovalFollowup } from "./bash-tools.exec-approval-followup.js";
|
||||
import {
|
||||
type ExecApprovalRegistration,
|
||||
resolveRegisteredExecApprovalDecision,
|
||||
} from "./bash-tools.exec-approval-request.js";
|
||||
import { buildApprovalPendingMessage } from "./bash-tools.exec-runtime.js";
|
||||
import { DEFAULT_APPROVAL_TIMEOUT_MS } from "./bash-tools.exec-runtime.js";
|
||||
import type { ExecToolDetails } from "./bash-tools.exec-types.js";
|
||||
|
||||
type ResolvedExecApprovals = ReturnType<typeof resolveExecApprovals>;
|
||||
|
||||
|
|
@ -53,6 +58,23 @@ export type RegisteredExecApprovalRequestContext = {
|
|||
unavailableReason: ExecApprovalUnavailableReason | null;
|
||||
};
|
||||
|
||||
export type ExecApprovalFollowupTarget = {
|
||||
approvalId: string;
|
||||
sessionKey?: string;
|
||||
turnSourceChannel?: string;
|
||||
turnSourceTo?: string;
|
||||
turnSourceAccountId?: string;
|
||||
turnSourceThreadId?: string | number;
|
||||
};
|
||||
|
||||
export type DefaultExecApprovalRequestArgs = {
|
||||
warnings: string[];
|
||||
approvalRunningNoticeMs: number;
|
||||
createApprovalSlug: (approvalId: string) => string;
|
||||
turnSourceChannel?: string;
|
||||
turnSourceAccountId?: string;
|
||||
};
|
||||
|
||||
export function createExecApprovalPendingState(params: {
|
||||
warnings: string[];
|
||||
timeoutMs: number;
|
||||
|
|
@ -257,3 +279,123 @@ export async function createAndRegisterDefaultExecApprovalRequest(params: {
|
|||
unavailableReason,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildDefaultExecApprovalRequestArgs(
|
||||
params: DefaultExecApprovalRequestArgs,
|
||||
): DefaultExecApprovalRequestArgs {
|
||||
return {
|
||||
warnings: params.warnings,
|
||||
approvalRunningNoticeMs: params.approvalRunningNoticeMs,
|
||||
createApprovalSlug: params.createApprovalSlug,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildExecApprovalFollowupTarget(
|
||||
params: ExecApprovalFollowupTarget,
|
||||
): ExecApprovalFollowupTarget {
|
||||
return {
|
||||
approvalId: params.approvalId,
|
||||
sessionKey: params.sessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
};
|
||||
}
|
||||
|
||||
export function createExecApprovalDecisionState(params: {
|
||||
decision: string | null | undefined;
|
||||
askFallback: ResolvedExecApprovals["agent"]["askFallback"];
|
||||
obfuscationDetected: boolean;
|
||||
}) {
|
||||
const baseDecision = resolveBaseExecApprovalDecision({
|
||||
decision: params.decision ?? null,
|
||||
askFallback: params.askFallback,
|
||||
obfuscationDetected: params.obfuscationDetected,
|
||||
});
|
||||
return {
|
||||
baseDecision,
|
||||
approvedByAsk: baseDecision.approvedByAsk,
|
||||
deniedReason: baseDecision.deniedReason,
|
||||
};
|
||||
}
|
||||
|
||||
export async function sendExecApprovalFollowupResult(
|
||||
target: ExecApprovalFollowupTarget,
|
||||
resultText: string,
|
||||
): Promise<void> {
|
||||
await sendExecApprovalFollowup({
|
||||
approvalId: target.approvalId,
|
||||
sessionKey: target.sessionKey,
|
||||
turnSourceChannel: target.turnSourceChannel,
|
||||
turnSourceTo: target.turnSourceTo,
|
||||
turnSourceAccountId: target.turnSourceAccountId,
|
||||
turnSourceThreadId: target.turnSourceThreadId,
|
||||
resultText,
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
export function buildExecApprovalPendingToolResult(params: {
|
||||
host: "gateway" | "node";
|
||||
command: string;
|
||||
cwd: string;
|
||||
warningText: string;
|
||||
approvalId: string;
|
||||
approvalSlug: string;
|
||||
expiresAtMs: number;
|
||||
initiatingSurface: ExecApprovalInitiatingSurfaceState;
|
||||
sentApproverDms: boolean;
|
||||
unavailableReason: ExecApprovalUnavailableReason | null;
|
||||
nodeId?: string;
|
||||
}): AgentToolResult<ExecToolDetails> {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text:
|
||||
params.unavailableReason !== null
|
||||
? (buildExecApprovalUnavailableReplyPayload({
|
||||
warningText: params.warningText,
|
||||
reason: params.unavailableReason,
|
||||
channelLabel: params.initiatingSurface.channelLabel,
|
||||
sentApproverDms: params.sentApproverDms,
|
||||
}).text ?? "")
|
||||
: buildApprovalPendingMessage({
|
||||
warningText: params.warningText,
|
||||
approvalSlug: params.approvalSlug,
|
||||
approvalId: params.approvalId,
|
||||
command: params.command,
|
||||
cwd: params.cwd,
|
||||
host: params.host,
|
||||
nodeId: params.nodeId,
|
||||
}),
|
||||
},
|
||||
],
|
||||
details:
|
||||
params.unavailableReason !== null
|
||||
? ({
|
||||
status: "approval-unavailable",
|
||||
reason: params.unavailableReason,
|
||||
channelLabel: params.initiatingSurface.channelLabel,
|
||||
sentApproverDms: params.sentApproverDms,
|
||||
host: params.host,
|
||||
command: params.command,
|
||||
cwd: params.cwd,
|
||||
nodeId: params.nodeId,
|
||||
warningText: params.warningText,
|
||||
} satisfies ExecToolDetails)
|
||||
: ({
|
||||
status: "approval-pending",
|
||||
approvalId: params.approvalId,
|
||||
approvalSlug: params.approvalSlug,
|
||||
expiresAtMs: params.expiresAtMs,
|
||||
host: params.host,
|
||||
command: params.command,
|
||||
cwd: params.cwd,
|
||||
nodeId: params.nodeId,
|
||||
warningText: params.warningText,
|
||||
} satisfies ExecToolDetails),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue