mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 12:38:38 +00:00
fix(telegram): exclude plugin commands from setMyCommands when native=false (openclaw#15164) thanks @Glucksberg
Verified: - pnpm install --frozen-lockfile - pnpm build - pnpm check - pnpm test Co-authored-by: Glucksberg <80581902+Glucksberg@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Security/Gateway: block `system.execApprovals.*` via `node.invoke` (use `exec.approvals.node.*` instead). Thanks @christos-eth.
|
- Security/Gateway: block `system.execApprovals.*` via `node.invoke` (use `exec.approvals.node.*` instead). Thanks @christos-eth.
|
||||||
- Security/Exec: harden PATH handling by disabling project-local `node_modules/.bin` bootstrapping by default, disallowing node-host `PATH` overrides, and spawning ACP servers via the current executable by default. Thanks @akhmittra.
|
- Security/Exec: harden PATH handling by disabling project-local `node_modules/.bin` bootstrapping by default, disallowing node-host `PATH` overrides, and spawning ACP servers via the current executable by default. Thanks @akhmittra.
|
||||||
- CLI: fix lazy core command registration so top-level maintenance commands (`doctor`, `dashboard`, `reset`, `uninstall`) resolve correctly instead of exposing a non-functional `maintenance` placeholder command.
|
- CLI: fix lazy core command registration so top-level maintenance commands (`doctor`, `dashboard`, `reset`, `uninstall`) resolve correctly instead of exposing a non-functional `maintenance` placeholder command.
|
||||||
|
- Telegram: when `channels.telegram.commands.native` is `false`, exclude plugin commands from `setMyCommands` menu registration while keeping plugin slash handlers callable. (#15132) Thanks @Glucksberg.
|
||||||
- Security/Agents: scope CLI process cleanup to owned child PIDs to avoid killing unrelated processes on shared hosts. Thanks @aether-ai-agent.
|
- Security/Agents: scope CLI process cleanup to owned child PIDs to avoid killing unrelated processes on shared hosts. Thanks @aether-ai-agent.
|
||||||
- Security/Agents (macOS): prevent shell injection when writing Claude CLI keychain credentials. (#15924) Thanks @aether-ai-agent.
|
- Security/Agents (macOS): prevent shell injection when writing Claude CLI keychain credentials. (#15924) Thanks @aether-ai-agent.
|
||||||
- Security: fix Chutes manual OAuth login state validation (thanks @aether-ai-agent). (#16058)
|
- Security: fix Chutes manual OAuth login state validation (thanks @aether-ai-agent). (#16058)
|
||||||
|
|||||||
@@ -103,8 +103,6 @@
|
|||||||
"test:install:e2e:openai": "OPENCLAW_E2E_MODELS=openai CLAWDBOT_E2E_MODELS=openai bash scripts/test-install-sh-e2e-docker.sh",
|
"test:install:e2e:openai": "OPENCLAW_E2E_MODELS=openai CLAWDBOT_E2E_MODELS=openai bash scripts/test-install-sh-e2e-docker.sh",
|
||||||
"test:install:smoke": "bash scripts/test-install-sh-docker.sh",
|
"test:install:smoke": "bash scripts/test-install-sh-docker.sh",
|
||||||
"test:live": "OPENCLAW_LIVE_TEST=1 CLAWDBOT_LIVE_TEST=1 vitest run --config vitest.live.config.ts",
|
"test:live": "OPENCLAW_LIVE_TEST=1 CLAWDBOT_LIVE_TEST=1 vitest run --config vitest.live.config.ts",
|
||||||
"test:lowcpu": "OPENCLAW_TEST_PROFILE=low NODE_OPTIONS=--max-old-space-size=8192 node scripts/test-parallel.mjs",
|
|
||||||
"test:macmini": "OPENCLAW_TEST_PROFILE=low NODE_OPTIONS=--max-old-space-size=8192 node scripts/test-parallel.mjs",
|
|
||||||
"test:ui": "pnpm --dir ui test",
|
"test:ui": "pnpm --dir ui test",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"tsgo:test": "tsgo -p tsconfig.test.json",
|
"tsgo:test": "tsgo -p tsconfig.test.json",
|
||||||
|
|||||||
@@ -110,11 +110,6 @@ const silentArgs =
|
|||||||
const rawPassthroughArgs = process.argv.slice(2);
|
const rawPassthroughArgs = process.argv.slice(2);
|
||||||
const passthroughArgs =
|
const passthroughArgs =
|
||||||
rawPassthroughArgs[0] === "--" ? rawPassthroughArgs.slice(1) : rawPassthroughArgs;
|
rawPassthroughArgs[0] === "--" ? rawPassthroughArgs.slice(1) : rawPassthroughArgs;
|
||||||
const rawTestProfile = process.env.OPENCLAW_TEST_PROFILE?.trim().toLowerCase();
|
|
||||||
const testProfile =
|
|
||||||
rawTestProfile === "low" || rawTestProfile === "max" || rawTestProfile === "normal"
|
|
||||||
? rawTestProfile
|
|
||||||
: "normal";
|
|
||||||
const overrideWorkers = Number.parseInt(process.env.OPENCLAW_TEST_WORKERS ?? "", 10);
|
const overrideWorkers = Number.parseInt(process.env.OPENCLAW_TEST_WORKERS ?? "", 10);
|
||||||
const resolvedOverride =
|
const resolvedOverride =
|
||||||
Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null;
|
Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null;
|
||||||
@@ -127,29 +122,10 @@ const keepGatewaySerial =
|
|||||||
const parallelRuns = keepGatewaySerial ? runs.filter((entry) => entry.name !== "gateway") : runs;
|
const parallelRuns = keepGatewaySerial ? runs.filter((entry) => entry.name !== "gateway") : runs;
|
||||||
const serialRuns = keepGatewaySerial ? runs.filter((entry) => entry.name === "gateway") : [];
|
const serialRuns = keepGatewaySerial ? runs.filter((entry) => entry.name === "gateway") : [];
|
||||||
const localWorkers = Math.max(4, Math.min(16, os.cpus().length));
|
const localWorkers = Math.max(4, Math.min(16, os.cpus().length));
|
||||||
const defaultWorkerBudget =
|
const defaultUnitWorkers = localWorkers;
|
||||||
testProfile === "low"
|
// Local perf: extensions tend to be the critical path under parallel vitest runs; give them more headroom.
|
||||||
? {
|
const defaultExtensionsWorkers = Math.max(1, Math.min(6, Math.floor(localWorkers / 2)));
|
||||||
unit: 2,
|
const defaultGatewayWorkers = Math.max(1, Math.min(2, Math.floor(localWorkers / 4)));
|
||||||
unitIsolated: 1,
|
|
||||||
extensions: 1,
|
|
||||||
gateway: 1,
|
|
||||||
}
|
|
||||||
: testProfile === "max"
|
|
||||||
? {
|
|
||||||
unit: localWorkers,
|
|
||||||
unitIsolated: Math.min(4, localWorkers),
|
|
||||||
extensions: Math.max(1, Math.min(6, Math.floor(localWorkers / 2))),
|
|
||||||
gateway: Math.max(1, Math.min(2, Math.floor(localWorkers / 4))),
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
// Local `pnpm test` runs multiple vitest groups concurrently;
|
|
||||||
// keep per-group workers conservative to avoid pegging all cores.
|
|
||||||
unit: Math.max(2, Math.min(8, Math.floor(localWorkers / 2))),
|
|
||||||
unitIsolated: 1,
|
|
||||||
extensions: Math.max(1, Math.min(4, Math.floor(localWorkers / 4))),
|
|
||||||
gateway: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Keep worker counts predictable for local runs; trim macOS CI workers to avoid worker crashes/OOM.
|
// Keep worker counts predictable for local runs; trim macOS CI workers to avoid worker crashes/OOM.
|
||||||
// In CI on linux/windows, prefer Vitest defaults to avoid cross-test interference from lower worker counts.
|
// In CI on linux/windows, prefer Vitest defaults to avoid cross-test interference from lower worker counts.
|
||||||
@@ -164,15 +140,16 @@ const maxWorkersForRun = (name) => {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (name === "unit-isolated") {
|
if (name === "unit-isolated") {
|
||||||
return defaultWorkerBudget.unitIsolated;
|
// Local: allow a bit of parallelism while keeping this run stable.
|
||||||
|
return Math.min(4, localWorkers);
|
||||||
}
|
}
|
||||||
if (name === "extensions") {
|
if (name === "extensions") {
|
||||||
return defaultWorkerBudget.extensions;
|
return defaultExtensionsWorkers;
|
||||||
}
|
}
|
||||||
if (name === "gateway") {
|
if (name === "gateway") {
|
||||||
return defaultWorkerBudget.gateway;
|
return defaultGatewayWorkers;
|
||||||
}
|
}
|
||||||
return defaultWorkerBudget.unit;
|
return defaultUnitWorkers;
|
||||||
};
|
};
|
||||||
|
|
||||||
const WARNING_SUPPRESSION_FLAGS = [
|
const WARNING_SUPPRESSION_FLAGS = [
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ vi.mock("../pairing/pairing-store.js", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
describe("registerTelegramNativeCommands (plugin auth)", () => {
|
describe("registerTelegramNativeCommands (plugin auth)", () => {
|
||||||
it("caps menu registration at 100 while leaving hidden plugin handlers available", () => {
|
it("does not register plugin commands in menu when native=false but keeps handlers available", () => {
|
||||||
const specs = Array.from({ length: 101 }, (_, i) => ({
|
const specs = Array.from({ length: 101 }, (_, i) => ({
|
||||||
name: `cmd_${i}`,
|
name: `cmd_${i}`,
|
||||||
description: `Command ${i}`,
|
description: `Command ${i}`,
|
||||||
@@ -73,14 +73,8 @@ describe("registerTelegramNativeCommands (plugin auth)", () => {
|
|||||||
opts: { token: "token" },
|
opts: { token: "token" },
|
||||||
});
|
});
|
||||||
|
|
||||||
const registered = setMyCommands.mock.calls[0]?.[0] as Array<{
|
expect(setMyCommands).not.toHaveBeenCalled();
|
||||||
command: string;
|
expect(log).not.toHaveBeenCalledWith(expect.stringContaining("registering first 100"));
|
||||||
description: string;
|
|
||||||
}>;
|
|
||||||
expect(registered).toHaveLength(100);
|
|
||||||
expect(registered[0]).toEqual({ command: "cmd_0", description: "Command 0" });
|
|
||||||
expect(registered[99]).toEqual({ command: "cmd_99", description: "Command 99" });
|
|
||||||
expect(log).toHaveBeenCalledWith(expect.stringContaining("registering first 100"));
|
|
||||||
expect(Object.keys(handlers)).toHaveLength(101);
|
expect(Object.keys(handlers)).toHaveLength(101);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ export const registerTelegramNativeCommands = ({
|
|||||||
command: command.name,
|
command: command.name,
|
||||||
description: command.description,
|
description: command.description,
|
||||||
})),
|
})),
|
||||||
...pluginCatalog.commands,
|
...(nativeEnabled ? pluginCatalog.commands : []),
|
||||||
...customCommands,
|
...customCommands,
|
||||||
];
|
];
|
||||||
const { commandsToRegister, totalCommands, maxCommands, overflowCount } =
|
const { commandsToRegister, totalCommands, maxCommands, overflowCount } =
|
||||||
@@ -357,7 +357,7 @@ export const registerTelegramNativeCommands = ({
|
|||||||
// Keep hidden commands callable by registering handlers for the full catalog.
|
// Keep hidden commands callable by registering handlers for the full catalog.
|
||||||
syncTelegramMenuCommands({ bot, runtime, commandsToRegister });
|
syncTelegramMenuCommands({ bot, runtime, commandsToRegister });
|
||||||
|
|
||||||
if (commandsToRegister.length > 0) {
|
if (commandsToRegister.length > 0 || pluginCatalog.commands.length > 0) {
|
||||||
if (typeof (bot as unknown as { command?: unknown }).command !== "function") {
|
if (typeof (bot as unknown as { command?: unknown }).command !== "function") {
|
||||||
logVerbose("telegram: bot.command unavailable; skipping native handlers");
|
logVerbose("telegram: bot.command unavailable; skipping native handlers");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user