Merge branch 'main' into feat/bitable-delete-record

This commit is contained in:
alecgu 2026-03-09 14:53:23 +08:00 committed by GitHub
commit 4838250e61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 45 additions and 31 deletions

View File

@ -12933,14 +12933,14 @@
"filename": "src/telegram/monitor.test.ts",
"hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
"is_verified": false,
"line_number": 450
"line_number": 497
},
{
"type": "Secret Keyword",
"filename": "src/telegram/monitor.test.ts",
"hashed_secret": "5934c4d4a4fa5d66ddb3d3fc0bba84996c17a5b7",
"is_verified": false,
"line_number": 641
"line_number": 688
}
],
"src/telegram/webhook.test.ts": [
@ -13035,5 +13035,5 @@
}
]
},
"generated_at": "2026-03-09T01:11:58Z"
"generated_at": "2026-03-09T06:30:58Z"
}

View File

@ -4,15 +4,6 @@ Docs: https://docs.openclaw.ai
## Unreleased
### Fixes
- Browser/SSRF: block private-network intermediate redirect hops in strict browser navigation flows and fail closed when remote tab-open paths cannot inspect redirect chains. Thanks @zpbrent.
- MS Teams/authz: keep `groupPolicy: "allowlist"` enforcing sender allowlists even when a team/channel route allowlist is configured, so route matches no longer widen group access to every sender in that route. Thanks @zpbrent.
- Security/system.run: bind approved `bun` and `deno run` script operands to on-disk file snapshots so post-approval script rewrites are denied before execution.
- Skills/download installs: pin the validated per-skill tools root before writing downloaded archives, so rebinding the lexical tools path cannot redirect download writes outside the intended tools directory. Thanks @tdjackey.
## 2026.3.8
### Changes
- CLI/backup: add `openclaw backup create` and `openclaw backup verify` for local state archives, including `--only-config`, `--no-include-workspace`, manifest/payload validation, and backup guidance in destructive flows. (#40163) thanks @shichangs.
@ -72,6 +63,10 @@ Docs: https://docs.openclaw.ai
- Telegram/poll restart cleanup: abort the in-flight Telegram API fetch when shutdown or forced polling restarts stop a runner, preventing stale `getUpdates` long polls from colliding with the replacement runner. Landed from contributor PR #23950 by @Gkinthecodeland. Thanks @Gkinthecodeland.
- Cron/restart catch-up staggering: limit immediate missed-job replay on startup and reschedule the deferred remainder from the post-catchup clock so restart bursts do not starve the gateway or silently skip overdue recurring jobs. Landed from contributor PR #18925 by @rexlunae. Thanks @rexlunae.
- Cron/owner-only tools: pass trusted isolated cron runs into the embedded agent with owner context so `cron`/`gateway` tooling remains available after the owner-auth hardening narrowed direct-message ownership inference.
- Browser/SSRF: block private-network intermediate redirect hops in strict browser navigation flows and fail closed when remote tab-open paths cannot inspect redirect chains. Thanks @zpbrent.
- MS Teams/authz: keep `groupPolicy: "allowlist"` enforcing sender allowlists even when a team/channel route allowlist is configured, so route matches no longer widen group access to every sender in that route. Thanks @zpbrent.
- Security/system.run: bind approved `bun` and `deno run` script operands to on-disk file snapshots so post-approval script rewrites are denied before execution.
- Skills/download installs: pin the validated per-skill tools root before writing downloaded archives, so rebinding the lexical tools path cannot redirect download writes outside the intended tools directory. Thanks @tdjackey.
## 2026.3.7

View File

@ -104,11 +104,11 @@ const hostMemoryGiB = Math.floor(os.totalmem() / 1024 ** 3);
const highMemLocalHost = !isCI && hostMemoryGiB >= 96;
const lowMemLocalHost = !isCI && hostMemoryGiB < 64;
const nodeMajor = Number.parseInt(process.versions.node.split(".")[0] ?? "", 10);
// vmForks is a big win for transform/import heavy suites, but Node 24 had
// regressions with Vitest's vm runtime in this repo, and low-memory local hosts
// vmForks is a big win for transform/import heavy suites, but Node 24+
// regressed with Vitest's vm runtime in this repo, and low-memory local hosts
// are more likely to hit per-worker V8 heap ceilings. Keep it opt-out via
// OPENCLAW_TEST_VM_FORKS=0, and let users force-enable with =1.
const supportsVmForks = Number.isFinite(nodeMajor) ? nodeMajor !== 24 : true;
const supportsVmForks = Number.isFinite(nodeMajor) ? nodeMajor < 24 : true;
const useVmForks =
process.env.OPENCLAW_TEST_VM_FORKS === "1" ||
(process.env.OPENCLAW_TEST_VM_FORKS !== "0" && !isWindows && supportsVmForks && !lowMemLocalHost);

View File

@ -17,11 +17,15 @@ vi.mock("../infra/agent-events.js", () => ({
onAgentEvent: vi.fn((_handler: unknown) => noop),
}));
vi.mock("../config/config.js", () => ({
loadConfig: vi.fn(() => ({
agents: { defaults: { subagents: { archiveAfterMinutes: 60 } } },
})),
}));
vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../config/config.js")>();
return {
...actual,
loadConfig: vi.fn(() => ({
agents: { defaults: { subagents: { archiveAfterMinutes: 60 } } },
})),
};
});
vi.mock("./subagent-announce.js", () => ({
runSubagentAnnounceFlow: vi.fn(async () => true),

View File

@ -49,9 +49,13 @@ vi.mock("../infra/agent-events.js", () => ({
onAgentEvent: onAgentEventMock,
}));
vi.mock("../config/config.js", () => ({
loadConfig: loadConfigMock,
}));
vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../config/config.js")>();
return {
...actual,
loadConfig: loadConfigMock,
};
});
vi.mock("./subagent-announce.js", () => ({
runSubagentAnnounceFlow: announceSpy,

View File

@ -1,11 +1,15 @@
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import "./subagent-registry.mocks.shared.js";
vi.mock("../config/config.js", () => ({
loadConfig: vi.fn(() => ({
agents: { defaults: { subagents: { archiveAfterMinutes: 0 } } },
})),
}));
vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../config/config.js")>();
return {
...actual,
loadConfig: vi.fn(() => ({
agents: { defaults: { subagents: { archiveAfterMinutes: 0 } } },
})),
};
});
vi.mock("./subagent-announce.js", () => ({
runSubagentAnnounceFlow: vi.fn(async () => true),

View File

@ -69,9 +69,16 @@ function withFakeRuntimeBin<T>(params: { binName: string; run: () => T }): T {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), `openclaw-${params.binName}-bin-`));
const binDir = path.join(tmp, "bin");
fs.mkdirSync(binDir, { recursive: true });
const runtimePath = path.join(binDir, params.binName);
fs.writeFileSync(runtimePath, "#!/bin/sh\nexit 0\n", { mode: 0o755 });
fs.chmodSync(runtimePath, 0o755);
const runtimePath =
process.platform === "win32"
? path.join(binDir, `${params.binName}.cmd`)
: path.join(binDir, params.binName);
const runtimeBody =
process.platform === "win32" ? "@echo off\r\nexit /b 0\r\n" : "#!/bin/sh\nexit 0\n";
fs.writeFileSync(runtimePath, runtimeBody, { mode: 0o755 });
if (process.platform !== "win32") {
fs.chmodSync(runtimePath, 0o755);
}
const oldPath = process.env.PATH;
process.env.PATH = `${binDir}${path.delimiter}${oldPath ?? ""}`;
try {