mirror of
https://github.com/farcasclaudiu/openclaw.git
synced 2026-06-28 23:02:02 +03:00
Commands: add commands.allowFrom config
This commit is contained in:
@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Commands: add `commands.allowFrom` config for separate command authorization, allowing operators to restrict slash commands to specific users while keeping chat open to others. (#12430) Thanks @thewilloftheshadow.
|
||||||
- iOS: alpha node app + setup-code onboarding. (#11756) Thanks @mbelinky.
|
- iOS: alpha node app + setup-code onboarding. (#11756) Thanks @mbelinky.
|
||||||
- Channels: comprehensive BlueBubbles and channel cleanup. (#11093) Thanks @tyler6204.
|
- Channels: comprehensive BlueBubbles and channel cleanup. (#11093) Thanks @tyler6204.
|
||||||
- Plugins: device pairing + phone control plugins (Telegram `/pair`, iOS/Android node controls). (#11755) Thanks @mbelinky.
|
- Plugins: device pairing + phone control plugins (Telegram `/pair`, iOS/Android node controls). (#11755) Thanks @mbelinky.
|
||||||
|
|||||||
@@ -990,6 +990,10 @@ Controls how chat commands are enabled across connectors.
|
|||||||
config: false, // allow /config (writes to disk)
|
config: false, // allow /config (writes to disk)
|
||||||
debug: false, // allow /debug (runtime-only overrides)
|
debug: false, // allow /debug (runtime-only overrides)
|
||||||
restart: false, // allow /restart + gateway restart tool
|
restart: false, // allow /restart + gateway restart tool
|
||||||
|
allowFrom: {
|
||||||
|
"*": ["user1"], // optional per-provider command allowlist
|
||||||
|
discord: ["user:123"],
|
||||||
|
},
|
||||||
useAccessGroups: true, // enforce access-group allowlists/policies for commands
|
useAccessGroups: true, // enforce access-group allowlists/policies for commands
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -1008,9 +1012,14 @@ Notes:
|
|||||||
- `channels.<provider>.configWrites` gates config mutations initiated by that channel (default: true). This applies to `/config set|unset` plus provider-specific auto-migrations (Telegram supergroup ID changes, Slack channel ID changes).
|
- `channels.<provider>.configWrites` gates config mutations initiated by that channel (default: true). This applies to `/config set|unset` plus provider-specific auto-migrations (Telegram supergroup ID changes, Slack channel ID changes).
|
||||||
- `commands.debug: true` enables `/debug` (runtime-only overrides).
|
- `commands.debug: true` enables `/debug` (runtime-only overrides).
|
||||||
- `commands.restart: true` enables `/restart` and the gateway tool restart action.
|
- `commands.restart: true` enables `/restart` and the gateway tool restart action.
|
||||||
- `commands.useAccessGroups: false` allows commands to bypass access-group allowlists/policies.
|
- `commands.allowFrom` sets a per-provider allowlist for command execution. When configured, it is the **only**
|
||||||
- Slash commands and directives are only honored for **authorized senders**. Authorization is derived from
|
authorization source for commands and directives (channel allowlists/pairing and `commands.useAccessGroups` are ignored).
|
||||||
channel allowlists/pairing plus `commands.useAccessGroups`.
|
Use `"*"` for a global default; provider-specific keys (for example `discord`) override it.
|
||||||
|
- `commands.useAccessGroups: false` allows commands to bypass access-group allowlists/policies when `commands.allowFrom`
|
||||||
|
is not set.
|
||||||
|
- Slash commands and directives are only honored for **authorized senders**. If `commands.allowFrom` is set,
|
||||||
|
authorization comes solely from that list; otherwise it is derived from channel allowlists/pairing plus
|
||||||
|
`commands.useAccessGroups`.
|
||||||
|
|
||||||
### `web` (WhatsApp web channel runtime)
|
### `web` (WhatsApp web channel runtime)
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ There are two related systems:
|
|||||||
- Directives are stripped from the message before the model sees it.
|
- Directives are stripped from the message before the model sees it.
|
||||||
- In normal chat messages (not directive-only), they are treated as “inline hints” and do **not** persist session settings.
|
- In normal chat messages (not directive-only), they are treated as “inline hints” and do **not** persist session settings.
|
||||||
- In directive-only messages (the message contains only directives), they persist to the session and reply with an acknowledgement.
|
- In directive-only messages (the message contains only directives), they persist to the session and reply with an acknowledgement.
|
||||||
- Directives are only applied for **authorized senders** (channel allowlists/pairing plus `commands.useAccessGroups`).
|
- Directives are only applied for **authorized senders**. If `commands.allowFrom` is set, it is the only
|
||||||
|
allowlist used; otherwise authorization comes from channel allowlists/pairing plus `commands.useAccessGroups`.
|
||||||
Unauthorized senders see directives treated as plain text.
|
Unauthorized senders see directives treated as plain text.
|
||||||
|
|
||||||
There are also a few **inline shortcuts** (allowlisted/authorized senders only): `/help`, `/commands`, `/status`, `/whoami` (`/id`).
|
There are also a few **inline shortcuts** (allowlisted/authorized senders only): `/help`, `/commands`, `/status`, `/whoami` (`/id`).
|
||||||
@@ -37,6 +38,10 @@ They run immediately, are stripped before the model sees the message, and the re
|
|||||||
config: false,
|
config: false,
|
||||||
debug: false,
|
debug: false,
|
||||||
restart: false,
|
restart: false,
|
||||||
|
allowFrom: {
|
||||||
|
"*": ["user1"],
|
||||||
|
discord: ["user:123"],
|
||||||
|
},
|
||||||
useAccessGroups: true,
|
useAccessGroups: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -55,7 +60,10 @@ They run immediately, are stripped before the model sees the message, and the re
|
|||||||
- `commands.bashForegroundMs` (default `2000`) controls how long bash waits before switching to background mode (`0` backgrounds immediately).
|
- `commands.bashForegroundMs` (default `2000`) controls how long bash waits before switching to background mode (`0` backgrounds immediately).
|
||||||
- `commands.config` (default `false`) enables `/config` (reads/writes `openclaw.json`).
|
- `commands.config` (default `false`) enables `/config` (reads/writes `openclaw.json`).
|
||||||
- `commands.debug` (default `false`) enables `/debug` (runtime-only overrides).
|
- `commands.debug` (default `false`) enables `/debug` (runtime-only overrides).
|
||||||
- `commands.useAccessGroups` (default `true`) enforces allowlists/policies for commands.
|
- `commands.allowFrom` (optional) sets a per-provider allowlist for command authorization. When configured, it is the
|
||||||
|
only authorization source for commands and directives (channel allowlists/pairing and `commands.useAccessGroups`
|
||||||
|
are ignored). Use `"*"` for a global default; provider-specific keys override it.
|
||||||
|
- `commands.useAccessGroups` (default `true`) enforces allowlists/policies for commands when `commands.allowFrom` is not set.
|
||||||
|
|
||||||
## Command list
|
## Command list
|
||||||
|
|
||||||
|
|||||||
@@ -126,6 +126,41 @@ function resolveOwnerAllowFromList(params: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the commands.allowFrom list for a given provider.
|
||||||
|
* Returns the provider-specific list if defined, otherwise the "*" global list.
|
||||||
|
* Returns null if commands.allowFrom is not configured at all (fall back to channel allowFrom).
|
||||||
|
*/
|
||||||
|
function resolveCommandsAllowFromList(params: {
|
||||||
|
dock?: ChannelDock;
|
||||||
|
cfg: OpenClawConfig;
|
||||||
|
accountId?: string | null;
|
||||||
|
providerId?: ChannelId;
|
||||||
|
}): string[] | null {
|
||||||
|
const { dock, cfg, accountId, providerId } = params;
|
||||||
|
const commandsAllowFrom = cfg.commands?.allowFrom;
|
||||||
|
if (!commandsAllowFrom || typeof commandsAllowFrom !== "object") {
|
||||||
|
return null; // Not configured, fall back to channel allowFrom
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check provider-specific list first, then fall back to global "*"
|
||||||
|
const providerKey = providerId ?? "";
|
||||||
|
const providerList = commandsAllowFrom[providerKey];
|
||||||
|
const globalList = commandsAllowFrom["*"];
|
||||||
|
|
||||||
|
const rawList = Array.isArray(providerList) ? providerList : globalList;
|
||||||
|
if (!Array.isArray(rawList)) {
|
||||||
|
return null; // No applicable list found
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatAllowFromList({
|
||||||
|
dock,
|
||||||
|
cfg,
|
||||||
|
accountId,
|
||||||
|
allowFrom: rawList,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function resolveSenderCandidates(params: {
|
function resolveSenderCandidates(params: {
|
||||||
dock?: ChannelDock;
|
dock?: ChannelDock;
|
||||||
providerId?: ChannelId;
|
providerId?: ChannelId;
|
||||||
@@ -175,6 +210,15 @@ export function resolveCommandAuthorization(params: {
|
|||||||
const dock = providerId ? getChannelDock(providerId) : undefined;
|
const dock = providerId ? getChannelDock(providerId) : undefined;
|
||||||
const from = (ctx.From ?? "").trim();
|
const from = (ctx.From ?? "").trim();
|
||||||
const to = (ctx.To ?? "").trim();
|
const to = (ctx.To ?? "").trim();
|
||||||
|
|
||||||
|
// Check if commands.allowFrom is configured (separate command authorization)
|
||||||
|
const commandsAllowFromList = resolveCommandsAllowFromList({
|
||||||
|
dock,
|
||||||
|
cfg,
|
||||||
|
accountId: ctx.AccountId,
|
||||||
|
providerId,
|
||||||
|
});
|
||||||
|
|
||||||
const allowFromRaw = dock?.config?.resolveAllowFrom
|
const allowFromRaw = dock?.config?.resolveAllowFrom
|
||||||
? dock.config.resolveAllowFrom({ cfg, accountId: ctx.AccountId })
|
? dock.config.resolveAllowFrom({ cfg, accountId: ctx.AccountId })
|
||||||
: [];
|
: [];
|
||||||
@@ -256,7 +300,21 @@ export function resolveCommandAuthorization(params: {
|
|||||||
: ownerAllowlistConfigured
|
: ownerAllowlistConfigured
|
||||||
? senderIsOwner
|
? senderIsOwner
|
||||||
: allowAll || ownerCandidatesForCommands.length === 0 || Boolean(matchedCommandOwner);
|
: allowAll || ownerCandidatesForCommands.length === 0 || Boolean(matchedCommandOwner);
|
||||||
const isAuthorizedSender = commandAuthorized && isOwnerForCommands;
|
|
||||||
|
// If commands.allowFrom is configured, use it for command authorization
|
||||||
|
// Otherwise, fall back to existing behavior (channel allowFrom + owner checks)
|
||||||
|
let isAuthorizedSender: boolean;
|
||||||
|
if (commandsAllowFromList !== null) {
|
||||||
|
// commands.allowFrom is configured - use it for authorization
|
||||||
|
const commandsAllowAll = commandsAllowFromList.some((entry) => entry.trim() === "*");
|
||||||
|
const matchedCommandsAllowFrom = commandsAllowFromList.length
|
||||||
|
? senderCandidates.find((candidate) => commandsAllowFromList.includes(candidate))
|
||||||
|
: undefined;
|
||||||
|
isAuthorizedSender = commandsAllowAll || Boolean(matchedCommandsAllowFrom);
|
||||||
|
} else {
|
||||||
|
// Fall back to existing behavior
|
||||||
|
isAuthorizedSender = commandAuthorized && isOwnerForCommands;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
providerId,
|
providerId,
|
||||||
|
|||||||
@@ -211,6 +211,182 @@ describe("resolveCommandAuthorization", () => {
|
|||||||
expect(auth.senderIsOwner).toBe(true);
|
expect(auth.senderIsOwner).toBe(true);
|
||||||
expect(auth.ownerList).toEqual(["123"]);
|
expect(auth.ownerList).toEqual(["123"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("commands.allowFrom", () => {
|
||||||
|
it("uses commands.allowFrom global list when configured", () => {
|
||||||
|
const cfg = {
|
||||||
|
commands: {
|
||||||
|
allowFrom: {
|
||||||
|
"*": ["user123"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
channels: { whatsapp: { allowFrom: ["+different"] } },
|
||||||
|
} as OpenClawConfig;
|
||||||
|
|
||||||
|
const authorizedCtx = {
|
||||||
|
Provider: "whatsapp",
|
||||||
|
Surface: "whatsapp",
|
||||||
|
From: "whatsapp:user123",
|
||||||
|
SenderId: "user123",
|
||||||
|
} as MsgContext;
|
||||||
|
|
||||||
|
const authorizedAuth = resolveCommandAuthorization({
|
||||||
|
ctx: authorizedCtx,
|
||||||
|
cfg,
|
||||||
|
commandAuthorized: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(authorizedAuth.isAuthorizedSender).toBe(true);
|
||||||
|
|
||||||
|
const unauthorizedCtx = {
|
||||||
|
Provider: "whatsapp",
|
||||||
|
Surface: "whatsapp",
|
||||||
|
From: "whatsapp:otheruser",
|
||||||
|
SenderId: "otheruser",
|
||||||
|
} as MsgContext;
|
||||||
|
|
||||||
|
const unauthorizedAuth = resolveCommandAuthorization({
|
||||||
|
ctx: unauthorizedCtx,
|
||||||
|
cfg,
|
||||||
|
commandAuthorized: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(unauthorizedAuth.isAuthorizedSender).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ignores commandAuthorized when commands.allowFrom is configured", () => {
|
||||||
|
const cfg = {
|
||||||
|
commands: {
|
||||||
|
allowFrom: {
|
||||||
|
"*": ["user123"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
channels: { whatsapp: { allowFrom: ["+different"] } },
|
||||||
|
} as OpenClawConfig;
|
||||||
|
|
||||||
|
const authorizedCtx = {
|
||||||
|
Provider: "whatsapp",
|
||||||
|
Surface: "whatsapp",
|
||||||
|
From: "whatsapp:user123",
|
||||||
|
SenderId: "user123",
|
||||||
|
} as MsgContext;
|
||||||
|
|
||||||
|
const authorizedAuth = resolveCommandAuthorization({
|
||||||
|
ctx: authorizedCtx,
|
||||||
|
cfg,
|
||||||
|
commandAuthorized: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(authorizedAuth.isAuthorizedSender).toBe(true);
|
||||||
|
|
||||||
|
const unauthorizedCtx = {
|
||||||
|
Provider: "whatsapp",
|
||||||
|
Surface: "whatsapp",
|
||||||
|
From: "whatsapp:otheruser",
|
||||||
|
SenderId: "otheruser",
|
||||||
|
} as MsgContext;
|
||||||
|
|
||||||
|
const unauthorizedAuth = resolveCommandAuthorization({
|
||||||
|
ctx: unauthorizedCtx,
|
||||||
|
cfg,
|
||||||
|
commandAuthorized: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(unauthorizedAuth.isAuthorizedSender).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses commands.allowFrom provider-specific list over global", () => {
|
||||||
|
const cfg = {
|
||||||
|
commands: {
|
||||||
|
allowFrom: {
|
||||||
|
"*": ["globaluser"],
|
||||||
|
whatsapp: ["+15551234567"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
channels: { whatsapp: { allowFrom: ["*"] } },
|
||||||
|
} as OpenClawConfig;
|
||||||
|
|
||||||
|
// User in global list but not in whatsapp-specific list
|
||||||
|
const globalUserCtx = {
|
||||||
|
Provider: "whatsapp",
|
||||||
|
Surface: "whatsapp",
|
||||||
|
From: "whatsapp:globaluser",
|
||||||
|
SenderId: "globaluser",
|
||||||
|
} as MsgContext;
|
||||||
|
|
||||||
|
const globalAuth = resolveCommandAuthorization({
|
||||||
|
ctx: globalUserCtx,
|
||||||
|
cfg,
|
||||||
|
commandAuthorized: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Provider-specific list overrides global, so globaluser is not authorized
|
||||||
|
expect(globalAuth.isAuthorizedSender).toBe(false);
|
||||||
|
|
||||||
|
// User in whatsapp-specific list
|
||||||
|
const whatsappUserCtx = {
|
||||||
|
Provider: "whatsapp",
|
||||||
|
Surface: "whatsapp",
|
||||||
|
From: "whatsapp:+15551234567",
|
||||||
|
SenderE164: "+15551234567",
|
||||||
|
} as MsgContext;
|
||||||
|
|
||||||
|
const whatsappAuth = resolveCommandAuthorization({
|
||||||
|
ctx: whatsappUserCtx,
|
||||||
|
cfg,
|
||||||
|
commandAuthorized: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(whatsappAuth.isAuthorizedSender).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to channel allowFrom when commands.allowFrom not set", () => {
|
||||||
|
const cfg = {
|
||||||
|
channels: { whatsapp: { allowFrom: ["+15551234567"] } },
|
||||||
|
} as OpenClawConfig;
|
||||||
|
|
||||||
|
const authorizedCtx = {
|
||||||
|
Provider: "whatsapp",
|
||||||
|
Surface: "whatsapp",
|
||||||
|
From: "whatsapp:+15551234567",
|
||||||
|
SenderE164: "+15551234567",
|
||||||
|
} as MsgContext;
|
||||||
|
|
||||||
|
const auth = resolveCommandAuthorization({
|
||||||
|
ctx: authorizedCtx,
|
||||||
|
cfg,
|
||||||
|
commandAuthorized: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(auth.isAuthorizedSender).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows all senders when commands.allowFrom includes wildcard", () => {
|
||||||
|
const cfg = {
|
||||||
|
commands: {
|
||||||
|
allowFrom: {
|
||||||
|
"*": ["*"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
channels: { whatsapp: { allowFrom: ["+specific"] } },
|
||||||
|
} as OpenClawConfig;
|
||||||
|
|
||||||
|
const anyUserCtx = {
|
||||||
|
Provider: "whatsapp",
|
||||||
|
Surface: "whatsapp",
|
||||||
|
From: "whatsapp:anyuser",
|
||||||
|
SenderId: "anyuser",
|
||||||
|
} as MsgContext;
|
||||||
|
|
||||||
|
const auth = resolveCommandAuthorization({
|
||||||
|
ctx: anyUserCtx,
|
||||||
|
cfg,
|
||||||
|
commandAuthorized: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(auth.isAuthorizedSender).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("control command parsing", () => {
|
describe("control command parsing", () => {
|
||||||
|
|||||||
@@ -306,6 +306,7 @@ const FIELD_LABELS: Record<string, string> = {
|
|||||||
"commands.restart": "Allow Restart",
|
"commands.restart": "Allow Restart",
|
||||||
"commands.useAccessGroups": "Use Access Groups",
|
"commands.useAccessGroups": "Use Access Groups",
|
||||||
"commands.ownerAllowFrom": "Command Owners",
|
"commands.ownerAllowFrom": "Command Owners",
|
||||||
|
"commands.allowFrom": "Command Access Allowlist",
|
||||||
"ui.seamColor": "Accent Color",
|
"ui.seamColor": "Accent Color",
|
||||||
"ui.assistant.name": "Assistant Name",
|
"ui.assistant.name": "Assistant Name",
|
||||||
"ui.assistant.avatar": "Assistant Avatar",
|
"ui.assistant.avatar": "Assistant Avatar",
|
||||||
@@ -675,6 +676,8 @@ const FIELD_HELP: Record<string, string> = {
|
|||||||
"commands.useAccessGroups": "Enforce access-group allowlists/policies for commands.",
|
"commands.useAccessGroups": "Enforce access-group allowlists/policies for commands.",
|
||||||
"commands.ownerAllowFrom":
|
"commands.ownerAllowFrom":
|
||||||
"Explicit owner allowlist for owner-only tools/commands. Use channel-native IDs (optionally prefixed like \"whatsapp:+15551234567\"). '*' is ignored.",
|
"Explicit owner allowlist for owner-only tools/commands. Use channel-native IDs (optionally prefixed like \"whatsapp:+15551234567\"). '*' is ignored.",
|
||||||
|
"commands.allowFrom":
|
||||||
|
'Per-provider allowlist restricting who can use slash commands. If set, overrides the channel\'s allowFrom for command authorization. Use \'*\' key for global default; provider-specific keys (e.g. \'discord\') override the global. Example: { "*": ["user1"], "discord": ["user:123"] }.',
|
||||||
"session.dmScope":
|
"session.dmScope":
|
||||||
'DM session scoping: "main" keeps continuity; "per-peer", "per-channel-peer", or "per-account-channel-peer" isolates DM history (recommended for shared inboxes/multi-account).',
|
'DM session scoping: "main" keeps continuity; "per-peer", "per-channel-peer", or "per-account-channel-peer" isolates DM history (recommended for shared inboxes/multi-account).',
|
||||||
"session.identityLinks":
|
"session.identityLinks":
|
||||||
|
|||||||
@@ -88,6 +88,13 @@ export type MessagesConfig = {
|
|||||||
|
|
||||||
export type NativeCommandsSetting = boolean | "auto";
|
export type NativeCommandsSetting = boolean | "auto";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-provider allowlist for command authorization.
|
||||||
|
* Keys are channel IDs (e.g., "discord", "whatsapp") or "*" for global default.
|
||||||
|
* Values are arrays of sender IDs allowed to use commands on that channel.
|
||||||
|
*/
|
||||||
|
export type CommandAllowFrom = Record<string, Array<string | number>>;
|
||||||
|
|
||||||
export type CommandsConfig = {
|
export type CommandsConfig = {
|
||||||
/** Enable native command registration when supported (default: "auto"). */
|
/** Enable native command registration when supported (default: "auto"). */
|
||||||
native?: NativeCommandsSetting;
|
native?: NativeCommandsSetting;
|
||||||
@@ -109,6 +116,13 @@ export type CommandsConfig = {
|
|||||||
useAccessGroups?: boolean;
|
useAccessGroups?: boolean;
|
||||||
/** Explicit owner allowlist for owner-only tools/commands (channel-native IDs). */
|
/** Explicit owner allowlist for owner-only tools/commands (channel-native IDs). */
|
||||||
ownerAllowFrom?: Array<string | number>;
|
ownerAllowFrom?: Array<string | number>;
|
||||||
|
/**
|
||||||
|
* Per-provider allowlist restricting who can use slash commands.
|
||||||
|
* If set, overrides the channel's allowFrom for command authorization.
|
||||||
|
* Use "*" key for global default, provider-specific keys override the global.
|
||||||
|
* Example: { "*": ["user1"], discord: ["user:123"] }
|
||||||
|
*/
|
||||||
|
allowFrom?: CommandAllowFrom;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProviderCommandsConfig = {
|
export type ProviderCommandsConfig = {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { parseByteSize } from "../cli/parse-bytes.js";
|
import { parseByteSize } from "../cli/parse-bytes.js";
|
||||||
import { parseDurationMs } from "../cli/parse-duration.js";
|
import { parseDurationMs } from "../cli/parse-duration.js";
|
||||||
|
import { ElevatedAllowFromSchema } from "./zod-schema.agent-runtime.js";
|
||||||
import {
|
import {
|
||||||
GroupChatSchema,
|
GroupChatSchema,
|
||||||
InboundDebounceSchema,
|
InboundDebounceSchema,
|
||||||
@@ -158,6 +159,7 @@ export const CommandsSchema = z
|
|||||||
restart: z.boolean().optional(),
|
restart: z.boolean().optional(),
|
||||||
useAccessGroups: z.boolean().optional(),
|
useAccessGroups: z.boolean().optional(),
|
||||||
ownerAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
ownerAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
||||||
|
allowFrom: ElevatedAllowFromSchema.optional(),
|
||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
.optional()
|
.optional()
|
||||||
|
|||||||
Reference in New Issue
Block a user