mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 01:31:08 +00:00
fix(ci): align protocol and cron gates
This commit is contained in:
@@ -1893,6 +1893,7 @@ public struct ConfigApplyParams: Codable, Sendable {
|
||||
public let raw: String
|
||||
public let basehash: String?
|
||||
public let sessionkey: String?
|
||||
public let deliverycontext: [String: AnyCodable]?
|
||||
public let note: String?
|
||||
public let restartdelayms: Int?
|
||||
|
||||
@@ -1900,12 +1901,14 @@ public struct ConfigApplyParams: Codable, Sendable {
|
||||
raw: String,
|
||||
basehash: String?,
|
||||
sessionkey: String?,
|
||||
deliverycontext: [String: AnyCodable]?,
|
||||
note: String?,
|
||||
restartdelayms: Int?)
|
||||
{
|
||||
self.raw = raw
|
||||
self.basehash = basehash
|
||||
self.sessionkey = sessionkey
|
||||
self.deliverycontext = deliverycontext
|
||||
self.note = note
|
||||
self.restartdelayms = restartdelayms
|
||||
}
|
||||
@@ -1914,6 +1917,7 @@ public struct ConfigApplyParams: Codable, Sendable {
|
||||
case raw
|
||||
case basehash = "baseHash"
|
||||
case sessionkey = "sessionKey"
|
||||
case deliverycontext = "deliveryContext"
|
||||
case note
|
||||
case restartdelayms = "restartDelayMs"
|
||||
}
|
||||
@@ -1923,6 +1927,7 @@ public struct ConfigPatchParams: Codable, Sendable {
|
||||
public let raw: String
|
||||
public let basehash: String?
|
||||
public let sessionkey: String?
|
||||
public let deliverycontext: [String: AnyCodable]?
|
||||
public let note: String?
|
||||
public let restartdelayms: Int?
|
||||
|
||||
@@ -1930,12 +1935,14 @@ public struct ConfigPatchParams: Codable, Sendable {
|
||||
raw: String,
|
||||
basehash: String?,
|
||||
sessionkey: String?,
|
||||
deliverycontext: [String: AnyCodable]?,
|
||||
note: String?,
|
||||
restartdelayms: Int?)
|
||||
{
|
||||
self.raw = raw
|
||||
self.basehash = basehash
|
||||
self.sessionkey = sessionkey
|
||||
self.deliverycontext = deliverycontext
|
||||
self.note = note
|
||||
self.restartdelayms = restartdelayms
|
||||
}
|
||||
@@ -1944,6 +1951,7 @@ public struct ConfigPatchParams: Codable, Sendable {
|
||||
case raw
|
||||
case basehash = "baseHash"
|
||||
case sessionkey = "sessionKey"
|
||||
case deliverycontext = "deliveryContext"
|
||||
case note
|
||||
case restartdelayms = "restartDelayMs"
|
||||
}
|
||||
@@ -4313,17 +4321,20 @@ public struct ChatEvent: Codable, Sendable {
|
||||
|
||||
public struct UpdateRunParams: Codable, Sendable {
|
||||
public let sessionkey: String?
|
||||
public let deliverycontext: [String: AnyCodable]?
|
||||
public let note: String?
|
||||
public let restartdelayms: Int?
|
||||
public let timeoutms: Int?
|
||||
|
||||
public init(
|
||||
sessionkey: String?,
|
||||
deliverycontext: [String: AnyCodable]?,
|
||||
note: String?,
|
||||
restartdelayms: Int?,
|
||||
timeoutms: Int?)
|
||||
{
|
||||
self.sessionkey = sessionkey
|
||||
self.deliverycontext = deliverycontext
|
||||
self.note = note
|
||||
self.restartdelayms = restartdelayms
|
||||
self.timeoutms = timeoutms
|
||||
@@ -4331,6 +4342,7 @@ public struct UpdateRunParams: Codable, Sendable {
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case sessionkey = "sessionKey"
|
||||
case deliverycontext = "deliveryContext"
|
||||
case note
|
||||
case restartdelayms = "restartDelayMs"
|
||||
case timeoutms = "timeoutMs"
|
||||
|
||||
@@ -1893,6 +1893,7 @@ public struct ConfigApplyParams: Codable, Sendable {
|
||||
public let raw: String
|
||||
public let basehash: String?
|
||||
public let sessionkey: String?
|
||||
public let deliverycontext: [String: AnyCodable]?
|
||||
public let note: String?
|
||||
public let restartdelayms: Int?
|
||||
|
||||
@@ -1900,12 +1901,14 @@ public struct ConfigApplyParams: Codable, Sendable {
|
||||
raw: String,
|
||||
basehash: String?,
|
||||
sessionkey: String?,
|
||||
deliverycontext: [String: AnyCodable]?,
|
||||
note: String?,
|
||||
restartdelayms: Int?)
|
||||
{
|
||||
self.raw = raw
|
||||
self.basehash = basehash
|
||||
self.sessionkey = sessionkey
|
||||
self.deliverycontext = deliverycontext
|
||||
self.note = note
|
||||
self.restartdelayms = restartdelayms
|
||||
}
|
||||
@@ -1914,6 +1917,7 @@ public struct ConfigApplyParams: Codable, Sendable {
|
||||
case raw
|
||||
case basehash = "baseHash"
|
||||
case sessionkey = "sessionKey"
|
||||
case deliverycontext = "deliveryContext"
|
||||
case note
|
||||
case restartdelayms = "restartDelayMs"
|
||||
}
|
||||
@@ -1923,6 +1927,7 @@ public struct ConfigPatchParams: Codable, Sendable {
|
||||
public let raw: String
|
||||
public let basehash: String?
|
||||
public let sessionkey: String?
|
||||
public let deliverycontext: [String: AnyCodable]?
|
||||
public let note: String?
|
||||
public let restartdelayms: Int?
|
||||
|
||||
@@ -1930,12 +1935,14 @@ public struct ConfigPatchParams: Codable, Sendable {
|
||||
raw: String,
|
||||
basehash: String?,
|
||||
sessionkey: String?,
|
||||
deliverycontext: [String: AnyCodable]?,
|
||||
note: String?,
|
||||
restartdelayms: Int?)
|
||||
{
|
||||
self.raw = raw
|
||||
self.basehash = basehash
|
||||
self.sessionkey = sessionkey
|
||||
self.deliverycontext = deliverycontext
|
||||
self.note = note
|
||||
self.restartdelayms = restartdelayms
|
||||
}
|
||||
@@ -1944,6 +1951,7 @@ public struct ConfigPatchParams: Codable, Sendable {
|
||||
case raw
|
||||
case basehash = "baseHash"
|
||||
case sessionkey = "sessionKey"
|
||||
case deliverycontext = "deliveryContext"
|
||||
case note
|
||||
case restartdelayms = "restartDelayMs"
|
||||
}
|
||||
@@ -4313,17 +4321,20 @@ public struct ChatEvent: Codable, Sendable {
|
||||
|
||||
public struct UpdateRunParams: Codable, Sendable {
|
||||
public let sessionkey: String?
|
||||
public let deliverycontext: [String: AnyCodable]?
|
||||
public let note: String?
|
||||
public let restartdelayms: Int?
|
||||
public let timeoutms: Int?
|
||||
|
||||
public init(
|
||||
sessionkey: String?,
|
||||
deliverycontext: [String: AnyCodable]?,
|
||||
note: String?,
|
||||
restartdelayms: Int?,
|
||||
timeoutms: Int?)
|
||||
{
|
||||
self.sessionkey = sessionkey
|
||||
self.deliverycontext = deliverycontext
|
||||
self.note = note
|
||||
self.restartdelayms = restartdelayms
|
||||
self.timeoutms = timeoutms
|
||||
@@ -4331,6 +4342,7 @@ public struct UpdateRunParams: Codable, Sendable {
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case sessionkey = "sessionKey"
|
||||
case deliverycontext = "deliveryContext"
|
||||
case note
|
||||
case restartdelayms = "restartDelayMs"
|
||||
case timeoutms = "timeoutMs"
|
||||
|
||||
@@ -161,4 +161,32 @@ describe("cron service store seam coverage", () => {
|
||||
const after = await fs.readFile(storePath, "utf8");
|
||||
expect(after).toBe(before);
|
||||
});
|
||||
|
||||
it("loads persisted jobs with unsafe custom session ids so run paths can fail closed", async () => {
|
||||
const { storePath } = await makeStorePath();
|
||||
|
||||
await writeSingleJobStore(storePath, {
|
||||
id: "unsafe-session-target-job",
|
||||
name: "unsafe session target job",
|
||||
enabled: true,
|
||||
createdAtMs: STORE_TEST_NOW - 60_000,
|
||||
updatedAtMs: STORE_TEST_NOW - 60_000,
|
||||
schedule: { kind: "every", everyMs: 60_000 },
|
||||
sessionTarget: "session:../../outside",
|
||||
wakeMode: "now",
|
||||
payload: { kind: "agentTurn", message: "ping" },
|
||||
state: {},
|
||||
});
|
||||
|
||||
const state = createStoreTestState(storePath);
|
||||
|
||||
await ensureLoaded(state, { skipRecompute: true });
|
||||
|
||||
const job = findJobOrThrow(state, "unsafe-session-target-job");
|
||||
expect(job.sessionTarget).toBe("session:../../outside");
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ storePath, jobId: "unsafe-session-target-job" }),
|
||||
expect.stringContaining("invalid persisted sessionTarget"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import { normalizeCronJobIdentityFields } from "../normalize-job-identity.js";
|
||||
import { normalizeCronJobInput } from "../normalize.js";
|
||||
import { isInvalidCronSessionTargetIdError } from "../session-target.js";
|
||||
import { loadCronStore, saveCronStore } from "../store.js";
|
||||
import type { CronJob } from "../types.js";
|
||||
import { recomputeNextRuns } from "./jobs.js";
|
||||
@@ -38,7 +39,19 @@ export async function ensureLoaded(
|
||||
for (const [index, job] of jobs.entries()) {
|
||||
const raw = job as unknown as Record<string, unknown>;
|
||||
const { legacyJobIdIssue } = normalizeCronJobIdentityFields(raw);
|
||||
const normalized = normalizeCronJobInput(raw);
|
||||
let normalized: Record<string, unknown> | null;
|
||||
try {
|
||||
normalized = normalizeCronJobInput(raw);
|
||||
} catch (error) {
|
||||
if (!isInvalidCronSessionTargetIdError(error)) {
|
||||
throw error;
|
||||
}
|
||||
normalized = null;
|
||||
state.deps.log.warn(
|
||||
{ storePath: state.deps.storePath, jobId: typeof raw.id === "string" ? raw.id : undefined },
|
||||
"cron: job has invalid persisted sessionTarget; run openclaw doctor --fix to repair",
|
||||
);
|
||||
}
|
||||
const hydrated =
|
||||
normalized && typeof normalized === "object" ? (normalized as unknown as CronJob) : job;
|
||||
jobs[index] = hydrated;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
const INVALID_CRON_SESSION_TARGET_ID_ERROR = "invalid cron sessionTarget session id";
|
||||
export const INVALID_CRON_SESSION_TARGET_ID_ERROR = "invalid cron sessionTarget session id";
|
||||
|
||||
export function isInvalidCronSessionTargetIdError(error: unknown): boolean {
|
||||
return error instanceof Error && error.message === INVALID_CRON_SESSION_TARGET_ID_ERROR;
|
||||
}
|
||||
|
||||
export function assertSafeCronSessionTargetId(sessionId: string): string {
|
||||
const trimmed = sessionId.trim();
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
readCronRunLogEntriesPageAll,
|
||||
resolveCronRunLogPath,
|
||||
} from "../../cron/run-log.js";
|
||||
import { isInvalidCronSessionTargetIdError } from "../../cron/session-target.js";
|
||||
import type { CronJobCreate, CronJobPatch } from "../../cron/types.js";
|
||||
import { validateScheduleTimestamp } from "../../cron/validate-timestamp.js";
|
||||
import { formatErrorMessage } from "../../infra/errors.js";
|
||||
@@ -250,9 +251,12 @@ export const cronHandlers: GatewayRequestHandlers = {
|
||||
try {
|
||||
result = await context.cron.enqueueRun(jobId, p.mode ?? "force");
|
||||
} catch (error) {
|
||||
const message = formatErrorMessage(error);
|
||||
if (message === "invalid cron sessionTarget session id") {
|
||||
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, message));
|
||||
if (isInvalidCronSessionTargetIdError(error)) {
|
||||
respond(
|
||||
false,
|
||||
undefined,
|
||||
errorShape(ErrorCodes.INVALID_REQUEST, formatErrorMessage(error)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
|
||||
@@ -24,11 +24,11 @@ describe("projects vitest config", () => {
|
||||
expect(createCommandsVitestConfig().test.pool).toBe("threads");
|
||||
expect(createPluginSdkLightVitestConfig().test.pool).toBe("threads");
|
||||
expect(createUnitFastVitestConfig().test.pool).toBe("threads");
|
||||
expect(createContractsVitestConfig().test.pool).toBe("threads");
|
||||
});
|
||||
|
||||
it("keeps the contracts lane on the non-isolated runner by default", () => {
|
||||
it("keeps the contracts lane on the non-isolated fork runner by default", () => {
|
||||
const config = createContractsVitestConfig();
|
||||
expect(config.test.pool).toBe("forks");
|
||||
expect(config.test.isolate).toBe(false);
|
||||
expect(normalizeConfigPath(config.test.runner)).toBe("test/non-isolated-runner.ts");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user