mirror of https://github.com/openclaw/openclaw.git
fix(config): coerce numeric Discord IDs to strings instead of rejecting (#45125)
Merged via squash.
Prepared head SHA: 099ba514a1
Co-authored-by: moliendocode <29582793+moliendocode@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
This commit is contained in:
parent
4f407d2658
commit
d076153fc9
|
|
@ -9,6 +9,7 @@ Docs: https://docs.openclaw.ai
|
|||
### Fixes
|
||||
|
||||
- Matrix/multi-account: keep room-level `account` scoping, inherited room overrides, and implicit account selection consistent across top-level default auth, named accounts, and cached-credential env setups. (#58449) thanks @Daanvdplas and @gumadeiras.
|
||||
- Config/Discord: coerce safe integer numeric Discord IDs to strings during config validation, keep unsafe or precision-losing numeric snowflakes rejected, and align `openclaw doctor` repair guidance with the same fail-closed behavior. (#45125) Thanks @moliendocode.
|
||||
|
||||
## 2026.4.1
|
||||
|
||||
|
|
|
|||
|
|
@ -10199,10 +10199,7 @@
|
|||
{
|
||||
"path": "channels.discord.accounts.*.allowFrom.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -10452,10 +10449,7 @@
|
|||
{
|
||||
"path": "channels.discord.accounts.*.dm.allowFrom.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -10485,10 +10479,7 @@
|
|||
{
|
||||
"path": "channels.discord.accounts.*.dm.groupChannels.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -10710,10 +10701,7 @@
|
|||
{
|
||||
"path": "channels.discord.accounts.*.execApprovals.approvers.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -10937,10 +10925,7 @@
|
|||
{
|
||||
"path": "channels.discord.accounts.*.guilds.*.channels.*.roles.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -11140,10 +11125,7 @@
|
|||
{
|
||||
"path": "channels.discord.accounts.*.guilds.*.channels.*.users.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -11199,10 +11181,7 @@
|
|||
{
|
||||
"path": "channels.discord.accounts.*.guilds.*.roles.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -11382,10 +11361,7 @@
|
|||
{
|
||||
"path": "channels.discord.accounts.*.guilds.*.users.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -12625,10 +12601,7 @@
|
|||
{
|
||||
"path": "channels.discord.allowFrom.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -12936,10 +12909,7 @@
|
|||
{
|
||||
"path": "channels.discord.dm.allowFrom.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -12969,10 +12939,7 @@
|
|||
{
|
||||
"path": "channels.discord.dm.groupChannels.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -13240,10 +13207,7 @@
|
|||
{
|
||||
"path": "channels.discord.execApprovals.approvers.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -13467,10 +13431,7 @@
|
|||
{
|
||||
"path": "channels.discord.guilds.*.channels.*.roles.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -13670,10 +13631,7 @@
|
|||
{
|
||||
"path": "channels.discord.guilds.*.channels.*.users.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -13729,10 +13687,7 @@
|
|||
{
|
||||
"path": "channels.discord.guilds.*.roles.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
@ -13912,10 +13867,7 @@
|
|||
{
|
||||
"path": "channels.discord.guilds.*.users.*",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"number",
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
|
|
|
|||
|
|
@ -895,7 +895,7 @@
|
|||
{"recordType":"path","path":"channels.discord.accounts.*.agentComponents.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.allowBots","kind":"channel","type":["boolean","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.autoPresence","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.autoPresence.degradedText","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.autoPresence.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
|
|
@ -919,10 +919,10 @@
|
|||
{"recordType":"path","path":"channels.discord.accounts.*.defaultTo","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.dm","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.dm.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.dm.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.dm.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.dm.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.dm.groupChannels","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.dm.groupChannels.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.dm.groupChannels.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.dm.groupEnabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.dm.policy","kind":"channel","type":"string","required":false,"enumValues":["pairing","allowlist","open","disabled"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.dmHistoryLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
|
|
@ -943,7 +943,7 @@
|
|||
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.agentFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.agentFilter.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.approvers","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.approvers.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.approvers.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.cleanupAfterResolve","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.sessionFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
|
|
@ -963,7 +963,7 @@
|
|||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.includeThreadStarter","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.roles","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.roles.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.roles.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.skills","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.skills.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.systemPrompt","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
|
|
@ -983,12 +983,12 @@
|
|||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.toolsBySender.*.deny","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.toolsBySender.*.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.users","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.users.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.users.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.ignoreOtherMentions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.reactionNotifications","kind":"channel","type":"string","required":false,"enumValues":["off","own","all","allowlist"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.roles","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.roles.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.roles.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.slug","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.tools","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.tools.allow","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
|
|
@ -1006,7 +1006,7 @@
|
|||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.toolsBySender.*.deny","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.toolsBySender.*.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.users","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.users.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.users.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.healthMonitor","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.healthMonitor.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.heartbeat","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
|
|
@ -1120,7 +1120,7 @@
|
|||
{"recordType":"path","path":"channels.discord.agentComponents.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.allowBots","kind":"channel","type":["boolean","string"],"required":false,"deprecated":false,"sensitive":false,"tags":["access","channels","network"],"label":"Discord Allow Bot Messages","help":"Allow bot-authored messages to trigger Discord replies (default: false). Set \"mentions\" to only accept bot messages that mention the bot.","hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.autoPresence","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.autoPresence.degradedText","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Discord Auto Presence Degraded Text","help":"Optional custom status text while runtime/model availability is degraded or unknown (idle).","hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.autoPresence.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Discord Auto Presence Enabled","help":"Enable automatic Discord bot presence updates based on runtime/model availability signals. When enabled: healthy=>online, degraded/unknown=>idle, exhausted/unavailable=>dnd.","hasChildren":false}
|
||||
|
|
@ -1145,10 +1145,10 @@
|
|||
{"recordType":"path","path":"channels.discord.defaultTo","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.dm","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.dm.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.dm.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.dm.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.dm.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.dm.groupChannels","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.dm.groupChannels.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.dm.groupChannels.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.dm.groupEnabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.dm.policy","kind":"channel","type":"string","required":false,"enumValues":["pairing","allowlist","open","disabled"],"deprecated":false,"sensitive":false,"tags":["access","channels","network"],"label":"Discord DM Policy","help":"Direct message access control (\"pairing\" recommended). \"open\" requires channels.discord.allowFrom=[\"*\"] (legacy: channels.discord.dm.allowFrom).","hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.dmHistoryLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
|
|
@ -1169,7 +1169,7 @@
|
|||
{"recordType":"path","path":"channels.discord.execApprovals.agentFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.execApprovals.agentFilter.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.execApprovals.approvers","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.execApprovals.approvers.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.execApprovals.approvers.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.execApprovals.cleanupAfterResolve","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.execApprovals.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.execApprovals.sessionFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
|
|
@ -1189,7 +1189,7 @@
|
|||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.includeThreadStarter","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.roles","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.roles.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.roles.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.skills","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.skills.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.systemPrompt","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
|
|
@ -1209,12 +1209,12 @@
|
|||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.toolsBySender.*.deny","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.toolsBySender.*.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.users","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.users.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.users.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.ignoreOtherMentions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.reactionNotifications","kind":"channel","type":"string","required":false,"enumValues":["off","own","all","allowlist"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.roles","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.roles.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.roles.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.slug","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.tools","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.tools.allow","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
|
|
@ -1232,7 +1232,7 @@
|
|||
{"recordType":"path","path":"channels.discord.guilds.*.toolsBySender.*.deny","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.toolsBySender.*.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.users","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.users.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.guilds.*.users.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.healthMonitor","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.healthMonitor.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.heartbeat","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,26 @@ describe("doctor discord provider repairs", () => {
|
|||
"channels.discord.guilds.main.channels.general.users[0]",
|
||||
"channels.discord.guilds.main.channels.general.roles[0]",
|
||||
]);
|
||||
expect(hits.every((hit) => hit.safe)).toBe(true);
|
||||
});
|
||||
|
||||
it("marks unsafe numeric ids as not safe", () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
discord: {
|
||||
allowFrom: [106232522769186816, -1, 123.45, 42],
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
|
||||
const hits = scanDiscordNumericIdEntries(cfg);
|
||||
|
||||
expect(hits).toEqual([
|
||||
{ path: "channels.discord.allowFrom[0]", entry: 106232522769186816, safe: false },
|
||||
{ path: "channels.discord.allowFrom[1]", entry: -1, safe: false },
|
||||
{ path: "channels.discord.allowFrom[2]", entry: 123.45, safe: false },
|
||||
{ path: "channels.discord.allowFrom[3]", entry: 42, safe: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it("repairs numeric discord ids into strings", () => {
|
||||
|
|
@ -50,7 +70,33 @@ describe("doctor discord provider repairs", () => {
|
|||
allowFrom: [123],
|
||||
accounts: {
|
||||
work: {
|
||||
allowFrom: [234],
|
||||
dm: { allowFrom: [345], groupChannels: [456] },
|
||||
execApprovals: { approvers: [456] },
|
||||
guilds: {
|
||||
ops: {
|
||||
users: [567],
|
||||
roles: [678],
|
||||
channels: {
|
||||
alerts: {
|
||||
users: [789],
|
||||
roles: [890],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
guilds: {
|
||||
main: {
|
||||
users: [111],
|
||||
roles: [222],
|
||||
channels: {
|
||||
general: {
|
||||
users: [333],
|
||||
roles: [444],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -59,27 +105,154 @@ describe("doctor discord provider repairs", () => {
|
|||
|
||||
const result = maybeRepairDiscordNumericIds(cfg);
|
||||
|
||||
expect(result.changes).toEqual([
|
||||
expect.stringContaining("channels.discord.allowFrom: converted 1 numeric entry to strings"),
|
||||
expect.stringContaining(
|
||||
"channels.discord.accounts.work.execApprovals.approvers: converted 1 numeric entry to strings",
|
||||
),
|
||||
]);
|
||||
expect(result.changes).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.stringContaining("channels.discord.allowFrom: converted 1 numeric entry to strings"),
|
||||
expect.stringContaining(
|
||||
"channels.discord.accounts.work.allowFrom: converted 1 numeric entry to strings",
|
||||
),
|
||||
expect.stringContaining(
|
||||
"channels.discord.accounts.work.dm.allowFrom: converted 1 numeric entry to strings",
|
||||
),
|
||||
expect.stringContaining(
|
||||
"channels.discord.accounts.work.dm.groupChannels: converted 1 numeric entry to strings",
|
||||
),
|
||||
expect.stringContaining(
|
||||
"channels.discord.accounts.work.execApprovals.approvers: converted 1 numeric entry to strings",
|
||||
),
|
||||
expect.stringContaining(
|
||||
"channels.discord.accounts.work.guilds.ops.users: converted 1 numeric entry to strings",
|
||||
),
|
||||
expect.stringContaining(
|
||||
"channels.discord.accounts.work.guilds.ops.roles: converted 1 numeric entry to strings",
|
||||
),
|
||||
expect.stringContaining(
|
||||
"channels.discord.accounts.work.guilds.ops.channels.alerts.users: converted 1 numeric entry to strings",
|
||||
),
|
||||
expect.stringContaining(
|
||||
"channels.discord.accounts.work.guilds.ops.channels.alerts.roles: converted 1 numeric entry to strings",
|
||||
),
|
||||
expect.stringContaining(
|
||||
"channels.discord.guilds.main.users: converted 1 numeric entry to strings",
|
||||
),
|
||||
expect.stringContaining(
|
||||
"channels.discord.guilds.main.roles: converted 1 numeric entry to strings",
|
||||
),
|
||||
expect.stringContaining(
|
||||
"channels.discord.guilds.main.channels.general.users: converted 1 numeric entry to strings",
|
||||
),
|
||||
expect.stringContaining(
|
||||
"channels.discord.guilds.main.channels.general.roles: converted 1 numeric entry to strings",
|
||||
),
|
||||
]),
|
||||
);
|
||||
expect(result.config.channels?.discord?.allowFrom).toEqual(["123"]);
|
||||
expect(result.config.channels?.discord?.guilds?.main?.users).toEqual(["111"]);
|
||||
expect(result.config.channels?.discord?.guilds?.main?.roles).toEqual(["222"]);
|
||||
expect(result.config.channels?.discord?.guilds?.main?.channels?.general?.users).toEqual([
|
||||
"333",
|
||||
]);
|
||||
expect(result.config.channels?.discord?.guilds?.main?.channels?.general?.roles).toEqual([
|
||||
"444",
|
||||
]);
|
||||
expect(result.config.channels?.discord?.accounts?.work?.allowFrom).toEqual(["234"]);
|
||||
expect(result.config.channels?.discord?.accounts?.work?.dm?.allowFrom).toEqual(["345"]);
|
||||
expect(result.config.channels?.discord?.accounts?.work?.dm?.groupChannels).toEqual(["456"]);
|
||||
expect(result.config.channels?.discord?.accounts?.work?.execApprovals?.approvers).toEqual([
|
||||
"456",
|
||||
]);
|
||||
expect(result.config.channels?.discord?.accounts?.work?.guilds?.ops?.users).toEqual(["567"]);
|
||||
expect(result.config.channels?.discord?.accounts?.work?.guilds?.ops?.roles).toEqual(["678"]);
|
||||
expect(
|
||||
result.config.channels?.discord?.accounts?.work?.guilds?.ops?.channels?.alerts?.users,
|
||||
).toEqual(["789"]);
|
||||
expect(
|
||||
result.config.channels?.discord?.accounts?.work?.guilds?.ops?.channels?.alerts?.roles,
|
||||
).toEqual(["890"]);
|
||||
});
|
||||
|
||||
it("formats numeric id warnings", () => {
|
||||
it("skips entire list when it contains unsafe numeric ids", () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
discord: {
|
||||
allowFrom: [42, 106232522769186816, -1, 123.45],
|
||||
dm: { allowFrom: [99] },
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
|
||||
const result = maybeRepairDiscordNumericIds(cfg);
|
||||
|
||||
expect(result.changes).toEqual([
|
||||
expect.stringContaining(
|
||||
"channels.discord.dm.allowFrom: converted 1 numeric entry to strings",
|
||||
),
|
||||
]);
|
||||
expect(result.config.channels?.discord?.allowFrom).toEqual([
|
||||
42, 106232522769186816, -1, 123.45,
|
||||
]);
|
||||
expect(result.config.channels?.discord?.dm?.allowFrom).toEqual(["99"]);
|
||||
});
|
||||
|
||||
it("returns repair warnings when unsafe numeric ids block doctor fix", () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
discord: {
|
||||
allowFrom: [106232522769186816],
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
|
||||
const result = maybeRepairDiscordNumericIds(cfg, {
|
||||
doctorFixCommand: "openclaw doctor --fix",
|
||||
});
|
||||
|
||||
expect(result.changes).toEqual([]);
|
||||
expect(result.warnings).toEqual([
|
||||
expect.stringContaining("could not be auto-repaired"),
|
||||
expect.stringContaining('rerun "openclaw doctor --fix"'),
|
||||
]);
|
||||
});
|
||||
|
||||
it("formats numeric id warnings for safe entries", () => {
|
||||
const warnings = collectDiscordNumericIdWarnings({
|
||||
hits: [{ path: "channels.discord.allowFrom[0]", entry: 123 }],
|
||||
hits: [{ path: "channels.discord.allowFrom[0]", entry: 123, safe: true }],
|
||||
doctorFixCommand: "openclaw doctor --fix",
|
||||
});
|
||||
|
||||
expect(warnings).toEqual([
|
||||
expect.stringContaining("Discord allowlists contain 1 numeric entries"),
|
||||
expect.stringContaining("Discord allowlists contain 1 numeric entry"),
|
||||
expect.stringContaining('run "openclaw doctor --fix"'),
|
||||
]);
|
||||
});
|
||||
|
||||
it("formats numeric id warnings for unsafe entries", () => {
|
||||
const warnings = collectDiscordNumericIdWarnings({
|
||||
hits: [{ path: "channels.discord.allowFrom[0]", entry: 106232522769186816, safe: false }],
|
||||
doctorFixCommand: "openclaw doctor --fix",
|
||||
});
|
||||
|
||||
expect(warnings).toEqual([
|
||||
expect.stringContaining("cannot be auto-repaired"),
|
||||
expect.stringContaining("manually quote the original values"),
|
||||
]);
|
||||
});
|
||||
|
||||
it("formats warnings for mixed safe and unsafe entries", () => {
|
||||
const warnings = collectDiscordNumericIdWarnings({
|
||||
hits: [
|
||||
{ path: "channels.discord.allowFrom[0]", entry: 123, safe: true },
|
||||
{ path: "channels.discord.dm.allowFrom[0]", entry: 456, safe: true },
|
||||
{ path: "channels.discord.dm.allowFrom[1]", entry: 106232522769186816, safe: false },
|
||||
],
|
||||
doctorFixCommand: "openclaw doctor --fix",
|
||||
});
|
||||
|
||||
expect(warnings).toHaveLength(4);
|
||||
expect(warnings[0]).toContain("1 numeric entry");
|
||||
expect(warnings[1]).toContain('run "openclaw doctor --fix"');
|
||||
expect(warnings[2]).toContain("2 numeric entries in lists that cannot be auto-repaired");
|
||||
expect(warnings[2]).toContain("channels.discord.dm.allowFrom[0]");
|
||||
expect(warnings[3]).toContain('rerun "openclaw doctor --fix"');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { sanitizeForLog } from "../../../terminal/ansi.js";
|
|||
import { asObjectRecord } from "../shared/object.js";
|
||||
import type { DoctorAccountRecord } from "../types.js";
|
||||
|
||||
type DiscordNumericIdHit = { path: string; entry: number };
|
||||
type DiscordNumericIdHit = { path: string; entry: number; safe: boolean };
|
||||
|
||||
type DiscordIdListRef = {
|
||||
pathLabel: string;
|
||||
|
|
@ -102,7 +102,11 @@ export function scanDiscordNumericIdEntries(cfg: OpenClawConfig): DiscordNumeric
|
|||
if (typeof entry !== "number") {
|
||||
continue;
|
||||
}
|
||||
hits.push({ path: `${pathLabel}[${index}]`, entry });
|
||||
hits.push({
|
||||
path: `${pathLabel}[${index}]`,
|
||||
entry,
|
||||
safe: Number.isSafeInteger(entry) && entry >= 0,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -122,17 +126,88 @@ export function collectDiscordNumericIdWarnings(params: {
|
|||
if (params.hits.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const samplePath = sanitizeForLog(params.hits[0]?.path ?? "channels.discord.allowFrom");
|
||||
const sampleEntry = sanitizeForLog(String(params.hits[0]?.entry ?? ""));
|
||||
const lines: string[] = [];
|
||||
const hitsByListPath = new Map<string, DiscordNumericIdHit[]>();
|
||||
for (const hit of params.hits) {
|
||||
const listPath = hit.path.replace(/\[\d+\]$/, "");
|
||||
const existing = hitsByListPath.get(listPath);
|
||||
if (existing) {
|
||||
existing.push(hit);
|
||||
continue;
|
||||
}
|
||||
hitsByListPath.set(listPath, [hit]);
|
||||
}
|
||||
|
||||
const repairableHits: DiscordNumericIdHit[] = [];
|
||||
const blockedHits: DiscordNumericIdHit[] = [];
|
||||
for (const hits of hitsByListPath.values()) {
|
||||
if (hits.some((hit) => !hit.safe)) {
|
||||
blockedHits.push(...hits);
|
||||
continue;
|
||||
}
|
||||
repairableHits.push(...hits);
|
||||
}
|
||||
|
||||
if (repairableHits.length > 0) {
|
||||
const sample = repairableHits[0];
|
||||
const samplePath = sanitizeForLog(sample.path);
|
||||
const sampleEntry = sanitizeForLog(String(sample.entry));
|
||||
lines.push(
|
||||
`- Discord allowlists contain ${repairableHits.length} numeric ${repairableHits.length === 1 ? "entry" : "entries"} (e.g. ${samplePath}=${sampleEntry}).`,
|
||||
`- Discord IDs must be strings; run "${params.doctorFixCommand}" to convert numeric IDs to quoted strings.`,
|
||||
);
|
||||
}
|
||||
if (blockedHits.length > 0) {
|
||||
const sample = blockedHits[0];
|
||||
const samplePath = sanitizeForLog(sample.path);
|
||||
lines.push(
|
||||
`- Discord allowlists contain ${blockedHits.length} numeric ${blockedHits.length === 1 ? "entry" : "entries"} in lists that cannot be auto-repaired (e.g. ${samplePath}).`,
|
||||
`- These lists include invalid or precision-losing numeric IDs; manually quote the original values in your config file, then rerun "${params.doctorFixCommand}".`,
|
||||
);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
function collectBlockedDiscordNumericIdRepairWarnings(params: {
|
||||
hits: DiscordNumericIdHit[];
|
||||
doctorFixCommand: string;
|
||||
}): string[] {
|
||||
const hitsByListPath = new Map<string, DiscordNumericIdHit[]>();
|
||||
for (const hit of params.hits) {
|
||||
const listPath = hit.path.replace(/\[\d+\]$/, "");
|
||||
const existing = hitsByListPath.get(listPath);
|
||||
if (existing) {
|
||||
existing.push(hit);
|
||||
continue;
|
||||
}
|
||||
hitsByListPath.set(listPath, [hit]);
|
||||
}
|
||||
|
||||
const blockedHits: DiscordNumericIdHit[] = [];
|
||||
for (const hits of hitsByListPath.values()) {
|
||||
if (hits.some((hit) => !hit.safe)) {
|
||||
blockedHits.push(...hits);
|
||||
}
|
||||
}
|
||||
if (blockedHits.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const sample = blockedHits[0];
|
||||
const samplePath = sanitizeForLog(sample.path);
|
||||
return [
|
||||
`- Discord allowlists contain ${params.hits.length} numeric entries (e.g. ${samplePath}=${sampleEntry}).`,
|
||||
`- Discord IDs must be strings; run "${params.doctorFixCommand}" to convert numeric IDs to quoted strings.`,
|
||||
`- Discord allowlists contain ${blockedHits.length} numeric ${blockedHits.length === 1 ? "entry" : "entries"} in lists that could not be auto-repaired (e.g. ${samplePath}).`,
|
||||
`- These lists include invalid or precision-losing numeric IDs; manually quote the original values in your config file, then rerun "${params.doctorFixCommand}".`,
|
||||
];
|
||||
}
|
||||
|
||||
export function maybeRepairDiscordNumericIds(cfg: OpenClawConfig): {
|
||||
export function maybeRepairDiscordNumericIds(
|
||||
cfg: OpenClawConfig,
|
||||
params?: { doctorFixCommand?: string },
|
||||
): {
|
||||
config: OpenClawConfig;
|
||||
changes: string[];
|
||||
warnings?: string[];
|
||||
} {
|
||||
const hits = scanDiscordNumericIdEntries(cfg);
|
||||
if (hits.length === 0) {
|
||||
|
|
@ -147,6 +222,12 @@ export function maybeRepairDiscordNumericIds(cfg: OpenClawConfig): {
|
|||
if (!Array.isArray(raw)) {
|
||||
return;
|
||||
}
|
||||
const hasUnsafe = raw.some(
|
||||
(entry) => typeof entry === "number" && (!Number.isSafeInteger(entry) || entry < 0),
|
||||
);
|
||||
if (hasUnsafe) {
|
||||
return;
|
||||
}
|
||||
let converted = 0;
|
||||
const updated = raw.map((entry) => {
|
||||
if (typeof entry === "number") {
|
||||
|
|
@ -170,8 +251,16 @@ export function maybeRepairDiscordNumericIds(cfg: OpenClawConfig): {
|
|||
}
|
||||
}
|
||||
|
||||
if (changes.length === 0) {
|
||||
const warnings =
|
||||
params?.doctorFixCommand === undefined
|
||||
? []
|
||||
: collectBlockedDiscordNumericIdRepairWarnings({
|
||||
hits,
|
||||
doctorFixCommand: params.doctorFixCommand,
|
||||
});
|
||||
|
||||
if (changes.length === 0 && warnings.length === 0) {
|
||||
return { config: cfg, changes: [] };
|
||||
}
|
||||
return { config: next, changes };
|
||||
return { config: next, changes, warnings };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,4 +71,35 @@ describe("doctor repair sequencing", () => {
|
|||
expect(result.warningNotes[0]).not.toContain("\u001B");
|
||||
expect(result.warningNotes[0]).not.toContain("\r");
|
||||
});
|
||||
|
||||
it("emits Discord warnings when unsafe numeric ids block repair", async () => {
|
||||
const result = await runDoctorRepairSequence({
|
||||
state: {
|
||||
cfg: {
|
||||
channels: {
|
||||
discord: {
|
||||
allowFrom: [106232522769186816],
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig,
|
||||
candidate: {
|
||||
channels: {
|
||||
discord: {
|
||||
allowFrom: [106232522769186816],
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig,
|
||||
pendingChanges: false,
|
||||
fixHints: [],
|
||||
},
|
||||
doctorFixCommand: "openclaw doctor --fix",
|
||||
});
|
||||
|
||||
expect(result.changeNotes).toEqual([]);
|
||||
expect(result.warningNotes).toHaveLength(1);
|
||||
expect(result.warningNotes[0]).toContain("could not be auto-repaired");
|
||||
expect(result.warningNotes[0]).toContain('rerun "openclaw doctor --fix"');
|
||||
expect(result.state.pendingChanges).toBe(false);
|
||||
expect(result.state.candidate.channels?.discord?.allowFrom).toEqual([106232522769186816]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -48,7 +48,11 @@ export async function runDoctorRepairSequence(params: {
|
|||
};
|
||||
|
||||
applyMutation(await maybeRepairTelegramAllowFromUsernames(state.candidate));
|
||||
applyMutation(maybeRepairDiscordNumericIds(state.candidate));
|
||||
applyMutation(
|
||||
maybeRepairDiscordNumericIds(state.candidate, {
|
||||
doctorFixCommand: params.doctorFixCommand,
|
||||
}),
|
||||
);
|
||||
applyMutation(maybeRepairOpenPolicyAllowFrom(state.candidate));
|
||||
applyMutation(maybeRepairBundledPluginLoadPaths(state.candidate, process.env));
|
||||
applyMutation(maybeRepairStalePluginConfig(state.candidate, process.env));
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ describe("config discord", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("rejects numeric discord allowlist entries", () => {
|
||||
it("coerces safe-integer numeric discord allowlist entries to strings", () => {
|
||||
const res = validateConfigObject({
|
||||
channels: {
|
||||
discord: {
|
||||
|
|
@ -79,11 +79,42 @@ describe("config discord", () => {
|
|||
},
|
||||
});
|
||||
|
||||
expect(res.ok).toBe(false);
|
||||
if (!res.ok) {
|
||||
expect(
|
||||
res.issues.some((issue) => issue.message.includes("Discord IDs must be strings")),
|
||||
).toBe(true);
|
||||
expect(res.ok).toBe(true);
|
||||
if (res.ok) {
|
||||
expect(res.config.channels?.discord?.allowFrom).toEqual(["123"]);
|
||||
expect(res.config.channels?.discord?.dm?.allowFrom).toEqual(["456"]);
|
||||
expect(res.config.channels?.discord?.dm?.groupChannels).toEqual(["789"]);
|
||||
expect(res.config.channels?.discord?.guilds?.["123"]?.users).toEqual(["111"]);
|
||||
expect(res.config.channels?.discord?.guilds?.["123"]?.roles).toEqual(["222"]);
|
||||
expect(res.config.channels?.discord?.guilds?.["123"]?.channels?.general?.users).toEqual([
|
||||
"333",
|
||||
]);
|
||||
expect(res.config.channels?.discord?.guilds?.["123"]?.channels?.general?.roles).toEqual([
|
||||
"444",
|
||||
]);
|
||||
expect(res.config.channels?.discord?.execApprovals?.approvers).toEqual(["555"]);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects numeric discord IDs that are not valid non-negative safe integers", () => {
|
||||
const cases = [106232522769186816, -1, 123.45];
|
||||
for (const id of cases) {
|
||||
const res = validateConfigObject({
|
||||
channels: {
|
||||
discord: {
|
||||
allowFrom: [id],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok).toBe(false);
|
||||
if (!res.ok) {
|
||||
expect(
|
||||
res.issues.some((issue) =>
|
||||
issue.message.includes("not a valid non-negative safe integer"),
|
||||
),
|
||||
).toBe(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -46,9 +46,22 @@ const ToolPolicyBySenderSchema = z.record(z.string(), ToolPolicySchema).optional
|
|||
|
||||
const DiscordIdSchema = z
|
||||
.union([z.string(), z.number()])
|
||||
.refine((value) => typeof value === "string", {
|
||||
message: "Discord IDs must be strings (wrap numeric IDs in quotes).",
|
||||
});
|
||||
.transform((value, ctx) => {
|
||||
if (typeof value === "number") {
|
||||
if (!Number.isSafeInteger(value) || value < 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message:
|
||||
`Discord ID "${String(value)}" is not a valid non-negative safe integer. ` +
|
||||
`Wrap it in quotes in your config file.`,
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
return value;
|
||||
})
|
||||
.pipe(z.string());
|
||||
const DiscordIdListSchema = z.array(DiscordIdSchema);
|
||||
|
||||
const TelegramInlineButtonsScopeSchema = z.enum(["off", "dm", "group", "all", "allowlist"]);
|
||||
|
|
|
|||
Loading…
Reference in New Issue