mirror of https://github.com/openclaw/openclaw.git
refactor: share exec approval session target routing
This commit is contained in:
parent
c74e5210f6
commit
8473a29da7
|
|
@ -424,7 +424,7 @@ describe("exec approval forwarder", () => {
|
|||
channel: "whatsapp",
|
||||
to: "+15555550123",
|
||||
accountId: "work",
|
||||
threadId: "1739201675.123",
|
||||
threadId: 1739201675,
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import type { ReplyPayload } from "../auto-reply/types.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { loadSessionStore, resolveStorePath } from "../config/sessions.js";
|
||||
import type {
|
||||
ExecApprovalForwardingConfig,
|
||||
ExecApprovalForwardTarget,
|
||||
|
|
@ -18,13 +17,13 @@ import {
|
|||
} from "../utils/message-channel.js";
|
||||
import { resolveExecApprovalCommandDisplay } from "./exec-approval-command-display.js";
|
||||
import { buildExecApprovalPendingReplyPayload } from "./exec-approval-reply.js";
|
||||
import { resolveExecApprovalSessionTarget } from "./exec-approval-session-target.js";
|
||||
import type {
|
||||
ExecApprovalDecision,
|
||||
ExecApprovalRequest,
|
||||
ExecApprovalResolved,
|
||||
} from "./exec-approvals.js";
|
||||
import { deliverOutboundPayloads } from "./outbound/deliver.js";
|
||||
import { resolveSessionDeliveryTarget } from "./outbound/targets.js";
|
||||
|
||||
const log = createSubsystemLogger("gateway/exec-approvals");
|
||||
export type { ExecApprovalRequest, ExecApprovalResolved };
|
||||
|
|
@ -281,21 +280,9 @@ function defaultResolveSessionTarget(params: {
|
|||
cfg: OpenClawConfig;
|
||||
request: ExecApprovalRequest;
|
||||
}): ExecApprovalForwardTarget | null {
|
||||
const sessionKey = params.request.request.sessionKey?.trim();
|
||||
if (!sessionKey) {
|
||||
return null;
|
||||
}
|
||||
const parsed = parseAgentSessionKey(sessionKey);
|
||||
const agentId = parsed?.agentId ?? params.request.request.agentId ?? "main";
|
||||
const storePath = resolveStorePath(params.cfg.session?.store, { agentId });
|
||||
const store = loadSessionStore(storePath);
|
||||
const entry = store[sessionKey];
|
||||
if (!entry) {
|
||||
return null;
|
||||
}
|
||||
const target = resolveSessionDeliveryTarget({
|
||||
entry,
|
||||
requestedChannel: "last",
|
||||
const target = resolveExecApprovalSessionTarget({
|
||||
cfg: params.cfg,
|
||||
request: params.request,
|
||||
turnSourceChannel: normalizeTurnSourceChannel(params.request.request.turnSourceChannel),
|
||||
turnSourceTo: params.request.request.turnSourceTo?.trim() || undefined,
|
||||
turnSourceAccountId: params.request.request.turnSourceAccountId?.trim() || undefined,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { loadSessionStore, resolveStorePath } from "../config/sessions.js";
|
||||
import { parseAgentSessionKey } from "../routing/session-key.js";
|
||||
import type { ExecApprovalRequest } from "./exec-approvals.js";
|
||||
import { resolveSessionDeliveryTarget } from "./outbound/targets.js";
|
||||
|
||||
export type ExecApprovalSessionTarget = {
|
||||
channel?: string;
|
||||
to: string;
|
||||
accountId?: string;
|
||||
threadId?: number;
|
||||
};
|
||||
|
||||
function normalizeOptionalString(value?: string | null): string | undefined {
|
||||
const normalized = value?.trim();
|
||||
return normalized ? normalized : undefined;
|
||||
}
|
||||
|
||||
function normalizeOptionalThreadId(value?: string | number | null): number | undefined {
|
||||
if (typeof value === "number") {
|
||||
return Number.isFinite(value) ? value : undefined;
|
||||
}
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const normalized = Number.parseInt(value, 10);
|
||||
return Number.isFinite(normalized) ? normalized : undefined;
|
||||
}
|
||||
|
||||
export function resolveExecApprovalSessionTarget(params: {
|
||||
cfg: OpenClawConfig;
|
||||
request: ExecApprovalRequest;
|
||||
turnSourceChannel?: string | null;
|
||||
turnSourceTo?: string | null;
|
||||
turnSourceAccountId?: string | null;
|
||||
turnSourceThreadId?: string | number | null;
|
||||
}): ExecApprovalSessionTarget | null {
|
||||
const sessionKey = normalizeOptionalString(params.request.request.sessionKey);
|
||||
if (!sessionKey) {
|
||||
return null;
|
||||
}
|
||||
const parsed = parseAgentSessionKey(sessionKey);
|
||||
const agentId = parsed?.agentId ?? params.request.request.agentId ?? "main";
|
||||
const storePath = resolveStorePath(params.cfg.session?.store, { agentId });
|
||||
const store = loadSessionStore(storePath);
|
||||
const entry = store[sessionKey];
|
||||
if (!entry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const target = resolveSessionDeliveryTarget({
|
||||
entry,
|
||||
requestedChannel: "last",
|
||||
turnSourceChannel: normalizeOptionalString(params.turnSourceChannel),
|
||||
turnSourceTo: normalizeOptionalString(params.turnSourceTo),
|
||||
turnSourceAccountId: normalizeOptionalString(params.turnSourceAccountId),
|
||||
turnSourceThreadId: normalizeOptionalThreadId(params.turnSourceThreadId),
|
||||
});
|
||||
if (!target.to) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
channel: normalizeOptionalString(target.channel),
|
||||
to: target.to,
|
||||
accountId: normalizeOptionalString(target.accountId),
|
||||
threadId: normalizeOptionalThreadId(target.threadId),
|
||||
};
|
||||
}
|
||||
|
|
@ -2,47 +2,35 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
|||
import { resolveTelegramTransport } from "../telegram/fetch.js";
|
||||
import { fetchRemoteMedia } from "./fetch.js";
|
||||
|
||||
const undiciFetch = vi.hoisted(() => vi.fn());
|
||||
const AgentCtor = vi.hoisted(() =>
|
||||
vi.fn(function MockAgent(
|
||||
this: { options?: Record<string, unknown> },
|
||||
options?: Record<string, unknown>,
|
||||
) {
|
||||
this.options = options;
|
||||
}),
|
||||
);
|
||||
const EnvHttpProxyAgentCtor = vi.hoisted(() =>
|
||||
vi.fn(function MockEnvHttpProxyAgent(
|
||||
this: { options?: Record<string, unknown> },
|
||||
options?: Record<string, unknown>,
|
||||
) {
|
||||
this.options = options;
|
||||
}),
|
||||
);
|
||||
const ProxyAgentCtor = vi.hoisted(() =>
|
||||
vi.fn(function MockProxyAgent(
|
||||
this: { options?: Record<string, unknown> | string },
|
||||
options?: Record<string, unknown> | string,
|
||||
) {
|
||||
this.options = options;
|
||||
}),
|
||||
);
|
||||
const undiciMocks = vi.hoisted(() => {
|
||||
const createDispatcherCtor = <T extends Record<string, unknown> | string>() =>
|
||||
vi.fn(function MockDispatcher(this: { options?: T }, options?: T) {
|
||||
this.options = options;
|
||||
});
|
||||
|
||||
return {
|
||||
fetch: vi.fn(),
|
||||
agentCtor: createDispatcherCtor<Record<string, unknown>>(),
|
||||
envHttpProxyAgentCtor: createDispatcherCtor<Record<string, unknown>>(),
|
||||
proxyAgentCtor: createDispatcherCtor<Record<string, unknown> | string>(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("undici", () => ({
|
||||
Agent: AgentCtor,
|
||||
EnvHttpProxyAgent: EnvHttpProxyAgentCtor,
|
||||
ProxyAgent: ProxyAgentCtor,
|
||||
fetch: undiciFetch,
|
||||
Agent: undiciMocks.agentCtor,
|
||||
EnvHttpProxyAgent: undiciMocks.envHttpProxyAgentCtor,
|
||||
ProxyAgent: undiciMocks.proxyAgentCtor,
|
||||
fetch: undiciMocks.fetch,
|
||||
}));
|
||||
|
||||
describe("fetchRemoteMedia telegram network policy", () => {
|
||||
type LookupFn = NonNullable<Parameters<typeof fetchRemoteMedia>[0]["lookupFn"]>;
|
||||
|
||||
afterEach(() => {
|
||||
undiciFetch.mockReset();
|
||||
AgentCtor.mockClear();
|
||||
EnvHttpProxyAgentCtor.mockClear();
|
||||
ProxyAgentCtor.mockClear();
|
||||
undiciMocks.fetch.mockReset();
|
||||
undiciMocks.agentCtor.mockClear();
|
||||
undiciMocks.envHttpProxyAgentCtor.mockClear();
|
||||
undiciMocks.proxyAgentCtor.mockClear();
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
|
|
@ -50,7 +38,7 @@ describe("fetchRemoteMedia telegram network policy", () => {
|
|||
const lookupFn = vi.fn(async () => [
|
||||
{ address: "149.154.167.220", family: 4 },
|
||||
]) as unknown as LookupFn;
|
||||
undiciFetch.mockResolvedValueOnce(
|
||||
undiciMocks.fetch.mockResolvedValueOnce(
|
||||
new Response(new Uint8Array([0xff, 0xd8, 0xff, 0x00]), {
|
||||
status: 200,
|
||||
headers: { "content-type": "image/jpeg" },
|
||||
|
|
@ -76,7 +64,7 @@ describe("fetchRemoteMedia telegram network policy", () => {
|
|||
},
|
||||
});
|
||||
|
||||
const init = undiciFetch.mock.calls[0]?.[1] as
|
||||
const init = undiciMocks.fetch.mock.calls[0]?.[1] as
|
||||
| (RequestInit & {
|
||||
dispatcher?: {
|
||||
options?: {
|
||||
|
|
@ -100,7 +88,7 @@ describe("fetchRemoteMedia telegram network policy", () => {
|
|||
const lookupFn = vi.fn(async () => [
|
||||
{ address: "149.154.167.220", family: 4 },
|
||||
]) as unknown as LookupFn;
|
||||
undiciFetch.mockResolvedValueOnce(
|
||||
undiciMocks.fetch.mockResolvedValueOnce(
|
||||
new Response(new Uint8Array([0x25, 0x50, 0x44, 0x46]), {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/pdf" },
|
||||
|
|
@ -126,7 +114,7 @@ describe("fetchRemoteMedia telegram network policy", () => {
|
|||
},
|
||||
});
|
||||
|
||||
const init = undiciFetch.mock.calls[0]?.[1] as
|
||||
const init = undiciMocks.fetch.mock.calls[0]?.[1] as
|
||||
| (RequestInit & {
|
||||
dispatcher?: {
|
||||
options?: {
|
||||
|
|
@ -137,6 +125,6 @@ describe("fetchRemoteMedia telegram network policy", () => {
|
|||
| undefined;
|
||||
|
||||
expect(init?.dispatcher?.options?.uri).toBe("http://127.0.0.1:7890");
|
||||
expect(ProxyAgentCtor).toHaveBeenCalled();
|
||||
expect(undiciMocks.proxyAgentCtor).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { loadSessionStore, resolveStorePath } from "../config/sessions.js";
|
||||
import { GatewayClient } from "../gateway/client.js";
|
||||
import { createOperatorApprovalsGatewayClient } from "../gateway/operator-approvals-client.js";
|
||||
import type { EventFrame } from "../gateway/protocol/index.js";
|
||||
|
|
@ -8,8 +7,8 @@ import {
|
|||
buildExecApprovalPendingReplyPayload,
|
||||
type ExecApprovalPendingReplyParams,
|
||||
} from "../infra/exec-approval-reply.js";
|
||||
import { resolveExecApprovalSessionTarget } from "../infra/exec-approval-session-target.js";
|
||||
import type { ExecApprovalRequest, ExecApprovalResolved } from "../infra/exec-approvals.js";
|
||||
import { resolveSessionDeliveryTarget } from "../infra/outbound/targets.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { normalizeAccountId, parseAgentSessionKey } from "../routing/session-key.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
|
|
@ -120,40 +119,14 @@ function resolveRequestSessionTarget(params: {
|
|||
cfg: OpenClawConfig;
|
||||
request: ExecApprovalRequest;
|
||||
}): { to: string; accountId?: string; threadId?: number; channel?: string } | null {
|
||||
const sessionKey = params.request.request.sessionKey?.trim();
|
||||
if (!sessionKey) {
|
||||
return null;
|
||||
}
|
||||
const parsed = parseAgentSessionKey(sessionKey);
|
||||
const agentId = parsed?.agentId ?? params.request.request.agentId ?? "main";
|
||||
const storePath = resolveStorePath(params.cfg.session?.store, { agentId });
|
||||
const store = loadSessionStore(storePath);
|
||||
const entry = store[sessionKey];
|
||||
if (!entry) {
|
||||
return null;
|
||||
}
|
||||
const target = resolveSessionDeliveryTarget({
|
||||
entry,
|
||||
requestedChannel: "last",
|
||||
return resolveExecApprovalSessionTarget({
|
||||
cfg: params.cfg,
|
||||
request: params.request,
|
||||
turnSourceChannel: params.request.request.turnSourceChannel ?? undefined,
|
||||
turnSourceTo: params.request.request.turnSourceTo ?? undefined,
|
||||
turnSourceAccountId: params.request.request.turnSourceAccountId ?? undefined,
|
||||
turnSourceThreadId: params.request.request.turnSourceThreadId ?? undefined,
|
||||
});
|
||||
if (!target.to) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
channel: target.channel ?? undefined,
|
||||
to: target.to,
|
||||
accountId: target.accountId ?? undefined,
|
||||
threadId:
|
||||
typeof target.threadId === "number"
|
||||
? target.threadId
|
||||
: typeof target.threadId === "string"
|
||||
? Number.parseInt(target.threadId, 10)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function resolveTelegramSourceTarget(params: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue