mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-14 11:30:41 +00:00
fix(discord): pass gateway auth to exec approvals
Pass resolved gateway token/password into the Discord exec approvals GatewayClient startup path so token-auth installs stop failing approvals with gateway token mismatch. Fixes #38179 Adjacent investigation: #35147 by @0riginal-claw Co-authored-by: 0riginal-claw <0rginal_claw@0rginal-claws-Mac-mini.local>
This commit is contained in:
@@ -308,6 +308,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Nodes/system.run dispatch-wrapper boundary: keep shell-wrapper approval classification active at the depth boundary so `env` wrapper stacks cannot reach `/bin/sh -c` execution without the expected approval gate. Thanks @tdjackey for reporting.
|
||||
- Docker/token persistence on reconfigure: reuse the existing `.env` gateway token during `docker-setup.sh` reruns and align compose token env defaults, so Docker installs stop silently rotating tokens and breaking existing dashboard sessions. Landed from contributor PR #33097 by @chengzhichao-xydt. Thanks @chengzhichao-xydt.
|
||||
- Agents/strict OpenAI turn ordering: apply assistant-first transcript bootstrap sanitization to strict OpenAI-compatible providers (for example vLLM/Gemma via `openai-completions`) without adding Google-specific session markers, preventing assistant-first history rejections. (#39252) Thanks @scoootscooob.
|
||||
- Discord/exec approvals gateway auth: pass resolved shared gateway credentials into the Discord exec-approvals gateway client so token-auth installs stop failing approvals with `gateway token mismatch`. Related to #38179. Thanks @0riginal-claw for the adjacent PR #35147 investigation.
|
||||
|
||||
## 2026.3.2
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ beforeEach(() => {
|
||||
const mockRestPost = vi.hoisted(() => vi.fn());
|
||||
const mockRestPatch = vi.hoisted(() => vi.fn());
|
||||
const mockRestDelete = vi.hoisted(() => vi.fn());
|
||||
const gatewayClientStarts = vi.hoisted(() => vi.fn());
|
||||
const gatewayClientStops = vi.hoisted(() => vi.fn());
|
||||
const gatewayClientRequests = vi.hoisted(() => vi.fn(async () => ({ ok: true })));
|
||||
const gatewayClientParams = vi.hoisted(() => [] as Array<Record<string, unknown>>);
|
||||
|
||||
vi.mock("../send.shared.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../send.shared.js")>();
|
||||
@@ -54,11 +58,16 @@ vi.mock("../../gateway/client.js", () => ({
|
||||
private params: Record<string, unknown>;
|
||||
constructor(params: Record<string, unknown>) {
|
||||
this.params = params;
|
||||
gatewayClientParams.push(params);
|
||||
}
|
||||
start() {
|
||||
gatewayClientStarts();
|
||||
}
|
||||
stop() {
|
||||
gatewayClientStops();
|
||||
}
|
||||
start() {}
|
||||
stop() {}
|
||||
async request() {
|
||||
return { ok: true };
|
||||
return gatewayClientRequests();
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -119,6 +128,17 @@ function createRequest(
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
mockRestPost.mockReset();
|
||||
mockRestPatch.mockReset();
|
||||
mockRestDelete.mockReset();
|
||||
gatewayClientStarts.mockReset();
|
||||
gatewayClientStops.mockReset();
|
||||
gatewayClientRequests.mockReset();
|
||||
gatewayClientRequests.mockResolvedValue({ ok: true });
|
||||
gatewayClientParams.length = 0;
|
||||
});
|
||||
|
||||
// ─── buildExecApprovalCustomId ────────────────────────────────────────────────
|
||||
|
||||
describe("buildExecApprovalCustomId", () => {
|
||||
@@ -611,6 +631,61 @@ describe("DiscordExecApprovalHandler target config", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("DiscordExecApprovalHandler gateway auth", () => {
|
||||
it("passes the shared gateway token from config into GatewayClient", async () => {
|
||||
const handler = new DiscordExecApprovalHandler({
|
||||
token: "discord-bot-token",
|
||||
accountId: "default",
|
||||
config: { enabled: true, approvers: ["123"] },
|
||||
cfg: {
|
||||
gateway: {
|
||||
mode: "local",
|
||||
bind: "loopback",
|
||||
auth: { mode: "token", token: "shared-gateway-token" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await handler.start();
|
||||
|
||||
expect(gatewayClientStarts).toHaveBeenCalledTimes(1);
|
||||
expect(gatewayClientParams[0]).toMatchObject({
|
||||
url: "ws://127.0.0.1:18789",
|
||||
token: "shared-gateway-token",
|
||||
password: undefined,
|
||||
scopes: ["operator.approvals"],
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers OPENCLAW_GATEWAY_TOKEN when config token is missing", async () => {
|
||||
vi.stubEnv("OPENCLAW_GATEWAY_TOKEN", "env-gateway-token");
|
||||
const handler = new DiscordExecApprovalHandler({
|
||||
token: "discord-bot-token",
|
||||
accountId: "default",
|
||||
config: { enabled: true, approvers: ["123"] },
|
||||
cfg: {
|
||||
gateway: {
|
||||
mode: "local",
|
||||
bind: "loopback",
|
||||
auth: { mode: "token" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await handler.start();
|
||||
} finally {
|
||||
vi.unstubAllEnvs();
|
||||
}
|
||||
|
||||
expect(gatewayClientStarts).toHaveBeenCalledTimes(1);
|
||||
expect(gatewayClientParams[0]).toMatchObject({
|
||||
token: "env-gateway-token",
|
||||
password: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Timeout cleanup ─────────────────────────────────────────────────────────
|
||||
|
||||
describe("DiscordExecApprovalHandler timeout cleanup", () => {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { loadSessionStore, resolveStorePath } from "../../config/sessions.js";
|
||||
import type { DiscordExecApprovalConfig } from "../../config/types.discord.js";
|
||||
import { buildGatewayConnectionDetails } from "../../gateway/call.js";
|
||||
import { GatewayClient } from "../../gateway/client.js";
|
||||
import { resolveGatewayCredentialsFromConfig } from "../../gateway/credentials.js";
|
||||
import type { EventFrame } from "../../gateway/protocol/index.js";
|
||||
import type {
|
||||
ExecApprovalDecision,
|
||||
@@ -404,9 +405,14 @@ export class DiscordExecApprovalHandler {
|
||||
config: this.opts.cfg,
|
||||
url: this.opts.gatewayUrl,
|
||||
});
|
||||
const gatewayCredentials = resolveGatewayCredentialsFromConfig({
|
||||
cfg: this.opts.cfg,
|
||||
});
|
||||
|
||||
this.gatewayClient = new GatewayClient({
|
||||
url: gatewayUrl,
|
||||
token: gatewayCredentials.token,
|
||||
password: gatewayCredentials.password,
|
||||
clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT,
|
||||
clientDisplayName: "Discord Exec Approvals",
|
||||
mode: GATEWAY_CLIENT_MODES.BACKEND,
|
||||
|
||||
Reference in New Issue
Block a user