mirror of
https://fastgit.cc/github.com/openclaw/openclaw
synced 2026-04-30 14:02:56 +08:00
fix(doctor): preserve discord streaming downgrade compatibility
This commit is contained in:
@@ -9,6 +9,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- WhatsApp/outbound: fall back to the first `mediaUrls` entry when `mediaUrl` is empty so gateway media sends stop silently dropping attachments that already have a resolved media list. (#64394) Thanks @eric-fr4 and @vincentkoc.
|
||||
- Doctor/Discord: stop `openclaw doctor --fix` from rewriting legacy Discord preview-streaming config into the nested modern shape, so downgrades can still recover without hand-editing `channels.discord.streaming`. (#65035) Thanks @vincentkoc.
|
||||
- Gateway/auth: blank the shipped example gateway credential in `.env.example` and fail startup when a copied placeholder token or password is still configured, so operators cannot accidentally launch with a publicly known secret. (#64586) Thanks @navarrotech and @vincentkoc.
|
||||
- Memory/active-memory+dreaming: keep active-memory recall runs on the strongest resolved channel, consume managed dreaming heartbeat events exactly once, stop dreaming from re-ingesting its own narrative transcripts, and add explicit repair/dedupe recovery flows in CLI, doctor, and the Dreams UI.
|
||||
- Gateway/keepalive: stop marking WebSocket tick broadcasts as droppable so slow or backpressured clients do not self-disconnect with `tick timeout` while long-running work is still alive. (#65256) Thanks @100yenadmin and @vincentkoc.
|
||||
@@ -26,7 +27,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/plugins: always send a non-empty `idempotencyKey` for plugin subagent runs, so dreaming narrative jobs stop failing gateway schema validation. (#65354) Thanks @CodeForgeNet and @vincentkoc.
|
||||
- Cron/isolated sessions: persist the right transcript path for each isolated run, including fresh session rollovers, so cron runs stop appending to stale session files. Thanks @samrusani and @vincentkoc.
|
||||
- Dreaming/cron: wake managed dreaming jobs immediately instead of waiting for the next heartbeat, so scheduled dreaming runs start when the cron fires. (#65053) Thanks @l0cka and @vincentkoc.
|
||||
<<<<<<< HEAD
|
||||
- QA/packaging: stop packaged QA helpers from crashing when optional scenario execution config is unavailable, so npm distributions can skip the repo-only scenario pack without breaking completion-cache and startup paths. (#65118) Thanks @EdderTalmor and @vincentkoc.
|
||||
- Media/audio transcription: surface the real provider failure when every audio transcription attempt fails, so status output and the CLI stop collapsing those errors into generic skips. (#65096) Thanks @l0cka and @vincentkoc.
|
||||
|
||||
|
||||
@@ -3,18 +3,7 @@ import type {
|
||||
ChannelDoctorLegacyConfigRule,
|
||||
} from "openclaw/plugin-sdk/channel-contract";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import {
|
||||
asObjectRecord,
|
||||
hasLegacyAccountStreamingAliases,
|
||||
hasLegacyStreamingAliases,
|
||||
normalizeLegacyDmAliases,
|
||||
normalizeLegacyStreamingAliases,
|
||||
} from "openclaw/plugin-sdk/runtime-doctor";
|
||||
import { resolveDiscordPreviewStreamMode } from "./preview-streaming.js";
|
||||
|
||||
function hasLegacyDiscordStreamingAliases(value: unknown): boolean {
|
||||
return hasLegacyStreamingAliases(value, { includePreviewChunk: true });
|
||||
}
|
||||
import { asObjectRecord, normalizeLegacyDmAliases } from "openclaw/plugin-sdk/runtime-doctor";
|
||||
|
||||
const LEGACY_TTS_PROVIDER_KEYS = ["openai", "elevenlabs", "microsoft", "edge"] as const;
|
||||
|
||||
@@ -114,18 +103,6 @@ function migrateLegacyTtsConfig(
|
||||
}
|
||||
|
||||
export const legacyConfigRules: ChannelDoctorLegacyConfigRule[] = [
|
||||
{
|
||||
path: ["channels", "discord"],
|
||||
message:
|
||||
"channels.discord.streamMode, channels.discord.streaming (scalar), chunkMode, blockStreaming, draftChunk, and blockStreamingCoalesce are legacy; use channels.discord.streaming.{mode,chunkMode,preview.chunk,block.enabled,block.coalesce}.",
|
||||
match: hasLegacyDiscordStreamingAliases,
|
||||
},
|
||||
{
|
||||
path: ["channels", "discord", "accounts"],
|
||||
message:
|
||||
"channels.discord.accounts.<id>.streamMode, streaming (scalar), chunkMode, blockStreaming, draftChunk, and blockStreamingCoalesce are legacy; use channels.discord.accounts.<id>.streaming.{mode,chunkMode,preview.chunk,block.enabled,block.coalesce}.",
|
||||
match: (value) => hasLegacyAccountStreamingAliases(value, hasLegacyDiscordStreamingAliases),
|
||||
},
|
||||
{
|
||||
path: ["channels", "discord", "voice", "tts"],
|
||||
message:
|
||||
@@ -164,18 +141,6 @@ export function normalizeCompatibilityConfig({
|
||||
updated = dm.entry;
|
||||
changed = changed || dm.changed;
|
||||
|
||||
const streaming = normalizeLegacyStreamingAliases({
|
||||
entry: updated,
|
||||
pathPrefix: "channels.discord",
|
||||
changes,
|
||||
includePreviewChunk: true,
|
||||
resolvedMode: resolveDiscordPreviewStreamMode(updated),
|
||||
offModeLegacyNotice: (pathPrefix) =>
|
||||
`${pathPrefix}.streaming remains off by default to avoid Discord preview-edit rate limits; set ${pathPrefix}.streaming.mode="partial" to opt in explicitly.`,
|
||||
});
|
||||
updated = streaming.entry;
|
||||
changed = changed || streaming.changed;
|
||||
|
||||
const rawAccounts = asObjectRecord(updated.accounts);
|
||||
if (rawAccounts) {
|
||||
let accountsChanged = false;
|
||||
@@ -194,17 +159,6 @@ export function normalizeCompatibilityConfig({
|
||||
});
|
||||
accountEntry = accountDm.entry;
|
||||
accountChanged = accountDm.changed;
|
||||
const accountStreaming = normalizeLegacyStreamingAliases({
|
||||
entry: accountEntry,
|
||||
pathPrefix: `channels.discord.accounts.${accountId}`,
|
||||
changes,
|
||||
includePreviewChunk: true,
|
||||
resolvedMode: resolveDiscordPreviewStreamMode(accountEntry),
|
||||
offModeLegacyNotice: (pathPrefix) =>
|
||||
`${pathPrefix}.streaming remains off by default to avoid Discord preview-edit rate limits; set ${pathPrefix}.streaming.mode="partial" to opt in explicitly.`,
|
||||
});
|
||||
accountEntry = accountStreaming.entry;
|
||||
accountChanged = accountChanged || accountStreaming.changed;
|
||||
const accountVoice = asObjectRecord(accountEntry.voice);
|
||||
if (
|
||||
accountVoice &&
|
||||
|
||||
@@ -1,46 +1,5 @@
|
||||
import type { ChannelDoctorLegacyConfigRule } from "openclaw/plugin-sdk/channel-contract";
|
||||
|
||||
function asObjectRecord(value: unknown): Record<string, unknown> | null {
|
||||
return value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: null;
|
||||
}
|
||||
|
||||
function hasLegacyDiscordStreamingAliases(value: unknown): boolean {
|
||||
const entry = asObjectRecord(value);
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
entry.streamMode !== undefined ||
|
||||
typeof entry.streaming === "boolean" ||
|
||||
typeof entry.streaming === "string" ||
|
||||
entry.chunkMode !== undefined ||
|
||||
entry.blockStreaming !== undefined ||
|
||||
entry.draftChunk !== undefined ||
|
||||
entry.blockStreamingCoalesce !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
function hasLegacyDiscordAccountStreamingAliases(value: unknown): boolean {
|
||||
const accounts = asObjectRecord(value);
|
||||
if (!accounts) {
|
||||
return false;
|
||||
}
|
||||
return Object.values(accounts).some((account) => hasLegacyDiscordStreamingAliases(account));
|
||||
}
|
||||
|
||||
export const DISCORD_LEGACY_CONFIG_RULES: ChannelDoctorLegacyConfigRule[] = [
|
||||
{
|
||||
path: ["channels", "discord"],
|
||||
message:
|
||||
"channels.discord.streamMode, channels.discord.streaming (scalar), chunkMode, blockStreaming, draftChunk, and blockStreamingCoalesce are legacy; use channels.discord.streaming.{mode,chunkMode,preview.chunk,block.enabled,block.coalesce}.",
|
||||
match: hasLegacyDiscordStreamingAliases,
|
||||
},
|
||||
{
|
||||
path: ["channels", "discord", "accounts"],
|
||||
message:
|
||||
"channels.discord.accounts.<id>.streamMode, streaming (scalar), chunkMode, blockStreaming, draftChunk, and blockStreamingCoalesce are legacy; use channels.discord.accounts.<id>.streaming.{mode,chunkMode,preview.chunk,block.enabled,block.coalesce}.",
|
||||
match: hasLegacyDiscordAccountStreamingAliases,
|
||||
},
|
||||
];
|
||||
// Runtime config loading already normalizes these aliases without rewriting the
|
||||
// source file. Keep doctor non-destructive so downgrade paths remain recoverable.
|
||||
export const DISCORD_LEGACY_CONFIG_RULES: ChannelDoctorLegacyConfigRule[] = [];
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "./doctor.js";
|
||||
|
||||
describe("discord doctor", () => {
|
||||
it("normalizes legacy discord streaming aliases into the nested streaming shape", () => {
|
||||
it("leaves legacy discord streaming aliases untouched during doctor normalization", () => {
|
||||
const normalize = discordDoctor.normalizeCompatibilityConfig;
|
||||
expect(normalize).toBeDefined();
|
||||
if (!normalize) {
|
||||
@@ -38,62 +38,23 @@ describe("discord doctor", () => {
|
||||
} as never,
|
||||
});
|
||||
|
||||
expect(result.config.channels?.discord?.streaming).toEqual({
|
||||
mode: "block",
|
||||
expect(result.config.channels?.discord).toEqual({
|
||||
streamMode: "block",
|
||||
chunkMode: "newline",
|
||||
block: {
|
||||
enabled: true,
|
||||
blockStreaming: true,
|
||||
draftChunk: {
|
||||
minChars: 120,
|
||||
},
|
||||
preview: {
|
||||
chunk: {
|
||||
minChars: 120,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(result.config.channels?.discord?.accounts?.work?.streaming).toEqual({
|
||||
mode: "off",
|
||||
block: {
|
||||
coalesce: {
|
||||
idleMs: 250,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(result.changes).toEqual(
|
||||
expect.arrayContaining([
|
||||
"Moved channels.discord.streamMode → channels.discord.streaming.mode (block).",
|
||||
"Moved channels.discord.chunkMode → channels.discord.streaming.chunkMode.",
|
||||
"Moved channels.discord.blockStreaming → channels.discord.streaming.block.enabled.",
|
||||
"Moved channels.discord.draftChunk → channels.discord.streaming.preview.chunk.",
|
||||
"Moved channels.discord.accounts.work.streaming (boolean) → channels.discord.accounts.work.streaming.mode (off).",
|
||||
"Moved channels.discord.accounts.work.blockStreamingCoalesce → channels.discord.accounts.work.streaming.block.coalesce.",
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not duplicate streaming.mode change messages when streamMode wins over boolean streaming", () => {
|
||||
const normalize = discordDoctor.normalizeCompatibilityConfig;
|
||||
expect(normalize).toBeDefined();
|
||||
if (!normalize) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = normalize({
|
||||
cfg: {
|
||||
channels: {
|
||||
discord: {
|
||||
streamMode: "block",
|
||||
streaming: false,
|
||||
accounts: {
|
||||
work: {
|
||||
streaming: false,
|
||||
blockStreamingCoalesce: {
|
||||
idleMs: 250,
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.config.channels?.discord?.streaming).toEqual({
|
||||
mode: "block",
|
||||
});
|
||||
expect(
|
||||
result.changes.filter((change) => change.includes("channels.discord.streaming.mode")),
|
||||
).toEqual(["Moved channels.discord.streamMode → channels.discord.streaming.mode (block)."]);
|
||||
expect(result.changes).toEqual([]);
|
||||
});
|
||||
|
||||
it("moves account voice.tts.edge into providers.microsoft", () => {
|
||||
|
||||
@@ -166,11 +166,6 @@ vi.mock("./doctor/shared/channel-doctor.js", () => {
|
||||
function collectCompatibilityMutations(cfg: { channels?: Record<string, unknown> }) {
|
||||
const next = structuredClone(cfg);
|
||||
const changes: string[] = [];
|
||||
const discord = asRecord(next.channels?.discord);
|
||||
if (discord && typeof discord.streaming === "boolean") {
|
||||
discord.streaming = { mode: discord.streaming ? "partial" : "off" };
|
||||
changes.push("Normalized channels.discord.streaming legacy scalar.");
|
||||
}
|
||||
const telegram = asRecord(next.channels?.telegram);
|
||||
if (telegram && "groupMentionsOnly" in telegram) {
|
||||
const groups = asRecord(telegram.groups) ?? {};
|
||||
@@ -948,14 +943,6 @@ describe("doctor config flow", () => {
|
||||
message.includes("channels.telegram.streamMode, channels.telegram.streaming"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
noteSpy.mock.calls.some(
|
||||
([message, title]) =>
|
||||
title === "Legacy config keys detected" &&
|
||||
message.includes("channels.discord:") &&
|
||||
message.includes("channels.discord.streamMode, channels.discord.streaming"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
noteSpy.mock.calls.some(
|
||||
([message, title]) =>
|
||||
@@ -977,6 +964,55 @@ describe("doctor config flow", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps discord streaming aliases on disk during repair so downgrades stay recoverable", async () => {
|
||||
await withTempHome(
|
||||
async (home) => {
|
||||
const configDir = path.join(home, ".openclaw");
|
||||
const configPath = path.join(configDir, "openclaw.json");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
channels: {
|
||||
discord: {
|
||||
streaming: false,
|
||||
chunkMode: "newline",
|
||||
blockStreaming: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
await loadAndMaybeMigrateDoctorConfig({
|
||||
options: { nonInteractive: true, repair: true },
|
||||
confirm: async () => false,
|
||||
});
|
||||
|
||||
const persisted = JSON.parse(await fs.readFile(configPath, "utf-8")) as {
|
||||
channels?: {
|
||||
discord?: {
|
||||
streaming?: unknown;
|
||||
chunkMode?: unknown;
|
||||
blockStreaming?: unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
expect(persisted.channels?.discord).toEqual({
|
||||
streaming: false,
|
||||
chunkMode: "newline",
|
||||
blockStreaming: true,
|
||||
});
|
||||
},
|
||||
{ skipSessionCleanup: true },
|
||||
);
|
||||
});
|
||||
|
||||
it("repairs legacy googlechat streamMode by removing it", async () => {
|
||||
const result = await runDoctorConfigWithInput({
|
||||
config: {
|
||||
|
||||
@@ -150,19 +150,16 @@ describe("normalizeCompatibilityConfigValues", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
expect(res.config.channels?.discord?.streaming).toEqual({ mode: "partial" });
|
||||
expect(res.config.channels?.discord?.streaming).toBe(true);
|
||||
expect(getLegacyProperty(res.config.channels?.discord, "streamMode")).toBeUndefined();
|
||||
expect(res.config.channels?.discord?.accounts?.work?.streaming).toEqual({ mode: "off" });
|
||||
expect(res.config.channels?.discord?.accounts?.work?.streaming).toBe(false);
|
||||
expect(
|
||||
getLegacyProperty(res.config.channels?.discord?.accounts?.work, "streamMode"),
|
||||
).toBeUndefined();
|
||||
expect(res.changes).toEqual([
|
||||
"Moved channels.discord.streaming (boolean) → channels.discord.streaming.mode (partial).",
|
||||
"Moved channels.discord.accounts.work.streaming (boolean) → channels.discord.accounts.work.streaming.mode (off).",
|
||||
]);
|
||||
expect(res.changes).toEqual([]);
|
||||
});
|
||||
|
||||
it("migrates Discord legacy streamMode into nested streaming.mode", () => {
|
||||
it("keeps Discord legacy streamMode untouched", () => {
|
||||
const res = normalizeCompatibilityConfigValues(
|
||||
asLegacyConfig({
|
||||
channels: {
|
||||
@@ -174,11 +171,9 @@ describe("normalizeCompatibilityConfigValues", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
expect(res.config.channels?.discord?.streaming).toEqual({ mode: "block" });
|
||||
expect(getLegacyProperty(res.config.channels?.discord, "streamMode")).toBeUndefined();
|
||||
expect(res.changes).toEqual([
|
||||
"Moved channels.discord.streamMode → channels.discord.streaming.mode (block).",
|
||||
]);
|
||||
expect(res.config.channels?.discord?.streaming).toBe(false);
|
||||
expect(getLegacyProperty(res.config.channels?.discord, "streamMode")).toBe("block");
|
||||
expect(res.changes).toEqual([]);
|
||||
});
|
||||
|
||||
it("migrates Telegram streamMode into nested streaming.mode", () => {
|
||||
|
||||
@@ -178,7 +178,7 @@ describe("legacy migrate sandbox scope aliases", () => {
|
||||
});
|
||||
|
||||
describe("legacy migrate channel streaming aliases", () => {
|
||||
it("migrates preview-channel legacy streaming fields into the nested streaming shape", () => {
|
||||
it("migrates Telegram and Slack preview-channel legacy streaming fields without rewriting Discord", () => {
|
||||
const res = migrateLegacyConfigForTest({
|
||||
channels: {
|
||||
telegram: {
|
||||
@@ -226,12 +226,6 @@ describe("legacy migrate channel streaming aliases", () => {
|
||||
expect(res.changes).toContain(
|
||||
"Moved channels.telegram.blockStreamingCoalesce → channels.telegram.streaming.block.coalesce.",
|
||||
);
|
||||
expect(res.changes).toContain(
|
||||
"Moved channels.discord.streaming (boolean) → channels.discord.streaming.mode (off).",
|
||||
);
|
||||
expect(res.changes).toContain(
|
||||
"Moved channels.discord.draftChunk → channels.discord.streaming.preview.chunk.",
|
||||
);
|
||||
expect(res.changes).toContain(
|
||||
"Moved channels.slack.streamMode → channels.slack.streaming.mode (progress).",
|
||||
);
|
||||
@@ -256,17 +250,11 @@ describe("legacy migrate channel streaming aliases", () => {
|
||||
},
|
||||
});
|
||||
expect(res.config?.channels?.discord).toMatchObject({
|
||||
streaming: {
|
||||
mode: "off",
|
||||
chunkMode: "newline",
|
||||
block: {
|
||||
enabled: true,
|
||||
},
|
||||
preview: {
|
||||
chunk: {
|
||||
maxChars: 900,
|
||||
},
|
||||
},
|
||||
streaming: false,
|
||||
chunkMode: "newline",
|
||||
blockStreaming: true,
|
||||
draftChunk: {
|
||||
maxChars: 900,
|
||||
},
|
||||
});
|
||||
expect(res.config?.channels?.slack).toMatchObject({
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import { normalizeOptionalLowercaseString } from "../../../shared/string-coerce.js";
|
||||
|
||||
type StreamingMode = "off" | "partial" | "block" | "progress";
|
||||
type DiscordPreviewStreamMode = "off" | "partial" | "block";
|
||||
type TelegramPreviewStreamMode = "off" | "partial" | "block";
|
||||
type SlackLegacyDraftStreamMode = "replace" | "status_final" | "append";
|
||||
|
||||
@@ -32,14 +31,6 @@ function parseStreamingMode(value: unknown): StreamingMode | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseDiscordPreviewStreamMode(value: unknown): DiscordPreviewStreamMode | null {
|
||||
const parsed = parseStreamingMode(value);
|
||||
if (!parsed) {
|
||||
return null;
|
||||
}
|
||||
return parsed === "progress" ? "partial" : parsed;
|
||||
}
|
||||
|
||||
function parseTelegramPreviewStreamMode(value: unknown): TelegramPreviewStreamMode | null {
|
||||
const parsed = parseStreamingMode(value);
|
||||
if (!parsed) {
|
||||
@@ -87,27 +78,6 @@ function resolveTelegramPreviewStreamMode(
|
||||
return "partial";
|
||||
}
|
||||
|
||||
function resolveDiscordPreviewStreamMode(
|
||||
params: {
|
||||
streamMode?: unknown;
|
||||
streaming?: unknown;
|
||||
} = {},
|
||||
): DiscordPreviewStreamMode {
|
||||
const parsedStreaming = parseDiscordPreviewStreamMode(params.streaming);
|
||||
if (parsedStreaming) {
|
||||
return parsedStreaming;
|
||||
}
|
||||
|
||||
const legacy = parseDiscordPreviewStreamMode(params.streamMode);
|
||||
if (legacy) {
|
||||
return legacy;
|
||||
}
|
||||
if (typeof params.streaming === "boolean") {
|
||||
return params.streaming ? "partial" : "off";
|
||||
}
|
||||
return "off";
|
||||
}
|
||||
|
||||
function resolveSlackStreamingMode(
|
||||
params: {
|
||||
streamMode?: unknown;
|
||||
@@ -220,22 +190,6 @@ function hasLegacyTelegramStreamingKeys(value: unknown): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function hasLegacyDiscordStreamingKeys(value: unknown): boolean {
|
||||
const entry = getRecord(value);
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
entry.streamMode !== undefined ||
|
||||
typeof entry.streaming === "boolean" ||
|
||||
typeof entry.streaming === "string" ||
|
||||
hasOwnKey(entry, "chunkMode") ||
|
||||
hasOwnKey(entry, "blockStreaming") ||
|
||||
hasOwnKey(entry, "draftChunk") ||
|
||||
hasOwnKey(entry, "blockStreamingCoalesce")
|
||||
);
|
||||
}
|
||||
|
||||
function hasLegacySlackStreamingKeys(value: unknown): boolean {
|
||||
const entry = getRecord(value);
|
||||
if (!entry) {
|
||||
@@ -524,18 +478,6 @@ const CHANNEL_STREAMING_RULES: LegacyConfigRule[] = [
|
||||
'channels.telegram.accounts.<id>.streamMode, streaming (scalar), chunkMode, blockStreaming, draftChunk, and blockStreamingCoalesce are legacy; use channels.telegram.accounts.<id>.streaming.{mode,chunkMode,preview.chunk,block.enabled,block.coalesce} instead. Run "openclaw doctor --fix".',
|
||||
match: (value) => hasLegacyKeysInAccounts(value, hasLegacyTelegramStreamingKeys),
|
||||
},
|
||||
{
|
||||
path: ["channels", "discord"],
|
||||
message:
|
||||
'channels.discord.streamMode, channels.discord.streaming (scalar), chunkMode, blockStreaming, draftChunk, and blockStreamingCoalesce are legacy; use channels.discord.streaming.{mode,chunkMode,preview.chunk,block.enabled,block.coalesce} instead. Run "openclaw doctor --fix".',
|
||||
match: (value) => hasLegacyDiscordStreamingKeys(value),
|
||||
},
|
||||
{
|
||||
path: ["channels", "discord", "accounts"],
|
||||
message:
|
||||
'channels.discord.accounts.<id>.streamMode, streaming (scalar), chunkMode, blockStreaming, draftChunk, and blockStreamingCoalesce are legacy; use channels.discord.accounts.<id>.streaming.{mode,chunkMode,preview.chunk,block.enabled,block.coalesce} instead. Run "openclaw doctor --fix".',
|
||||
match: (value) => hasLegacyKeysInAccounts(value, hasLegacyDiscordStreamingKeys),
|
||||
},
|
||||
{
|
||||
path: ["channels", "slack"],
|
||||
message:
|
||||
@@ -659,8 +601,7 @@ export const LEGACY_CONFIG_MIGRATIONS_CHANNELS: LegacyConfigMigrationSpec[] = [
|
||||
}),
|
||||
defineLegacyConfigMigration({
|
||||
id: "channels.streaming-keys->channels.streaming",
|
||||
describe:
|
||||
"Normalize legacy streaming keys to channels.<provider>.streaming (Telegram/Discord/Slack)",
|
||||
describe: "Normalize legacy streaming keys to channels.<provider>.streaming (Telegram/Slack)",
|
||||
legacyRules: CHANNEL_STREAMING_RULES,
|
||||
apply: (raw, changes) => {
|
||||
const channels = getRecord(raw.channels);
|
||||
@@ -683,16 +624,6 @@ export const LEGACY_CONFIG_MIGRATIONS_CHANNELS: LegacyConfigMigrationSpec[] = [
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.provider === "discord") {
|
||||
moveLegacyStreamingShapeForPath({
|
||||
entry: params.entry,
|
||||
pathPrefix: params.pathPrefix,
|
||||
changes,
|
||||
resolveMode: resolveDiscordPreviewStreamMode,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
moveLegacyStreamingShapeForPath({
|
||||
entry: params.entry,
|
||||
pathPrefix: params.pathPrefix,
|
||||
@@ -702,7 +633,7 @@ export const LEGACY_CONFIG_MIGRATIONS_CHANNELS: LegacyConfigMigrationSpec[] = [
|
||||
});
|
||||
};
|
||||
|
||||
const migrateProvider = (provider: "telegram" | "discord" | "slack") => {
|
||||
const migrateProvider = (provider: "telegram" | "slack") => {
|
||||
const providerEntry = getRecord(channels[provider]);
|
||||
if (!providerEntry) {
|
||||
return;
|
||||
@@ -730,7 +661,6 @@ export const LEGACY_CONFIG_MIGRATIONS_CHANNELS: LegacyConfigMigrationSpec[] = [
|
||||
};
|
||||
|
||||
migrateProvider("telegram");
|
||||
migrateProvider("discord");
|
||||
migrateProvider("slack");
|
||||
},
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user