mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-26 00:21:59 +00:00
fix: add operator.read and operator.write to default CLI scopes
The 2026.2.19-2 release tightened scope enforcement on the gateway handshake, but the default CLI operator scopes only included admin, approvals, and pairing. Cron announce delivery and sub-agent result delivery use methods gated behind operator.write (e.g. "send", "poll"), causing a scope-upgrade rejection: `gateway closed (1008): pairing required`. Add operator.read and operator.write to the default scope set across all runtime bundles (Node.js, browser Control UI, macOS CLI, OpenClawKit). Fixes #21787 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ struct ConnectOptions {
|
||||
var clientMode: String = "ui"
|
||||
var displayName: String?
|
||||
var role: String = "operator"
|
||||
var scopes: [String] = ["operator.admin", "operator.approvals", "operator.pairing"]
|
||||
var scopes: [String] = ["operator.admin", "operator.read", "operator.write", "operator.approvals", "operator.pairing"]
|
||||
var help: Bool = false
|
||||
|
||||
static func parse(_ args: [String]) -> ConnectOptions {
|
||||
|
||||
@@ -251,7 +251,7 @@ actor GatewayWizardClient {
|
||||
let clientMode = "ui"
|
||||
let role = "operator"
|
||||
// Explicit scopes; gateway no longer defaults empty scopes to admin.
|
||||
let scopes: [String] = ["operator.admin", "operator.approvals", "operator.pairing"]
|
||||
let scopes: [String] = ["operator.admin", "operator.read", "operator.write", "operator.approvals", "operator.pairing"]
|
||||
let client: [String: ProtoAnyCodable] = [
|
||||
"id": ProtoAnyCodable(clientId),
|
||||
"displayName": ProtoAnyCodable(Host.current().localizedName ?? "OpenClaw macOS Wizard CLI"),
|
||||
|
||||
@@ -318,7 +318,7 @@ public actor GatewayChannelActor {
|
||||
let primaryLocale = Locale.preferredLanguages.first ?? Locale.current.identifier
|
||||
let options = self.connectOptions ?? GatewayConnectOptions(
|
||||
role: "operator",
|
||||
scopes: ["operator.admin", "operator.approvals", "operator.pairing"],
|
||||
scopes: ["operator.admin", "operator.read", "operator.write", "operator.approvals", "operator.pairing"],
|
||||
caps: [],
|
||||
commands: [],
|
||||
permissions: [:],
|
||||
|
||||
@@ -206,7 +206,13 @@ describe("callGateway url resolution", () => {
|
||||
{
|
||||
label: "keeps legacy admin scopes for explicit CLI callers",
|
||||
call: () => callGatewayCli({ method: "health" }),
|
||||
expectedScopes: ["operator.admin", "operator.approvals", "operator.pairing"],
|
||||
expectedScopes: [
|
||||
"operator.admin",
|
||||
"operator.read",
|
||||
"operator.write",
|
||||
"operator.approvals",
|
||||
"operator.pairing",
|
||||
],
|
||||
},
|
||||
])("scope selection: $label", async ({ call, expectedScopes }) => {
|
||||
setLocalLoopbackGatewayConfig();
|
||||
|
||||
@@ -13,6 +13,8 @@ export type OperatorScope =
|
||||
|
||||
export const CLI_DEFAULT_OPERATOR_SCOPES: OperatorScope[] = [
|
||||
ADMIN_SCOPE,
|
||||
READ_SCOPE,
|
||||
WRITE_SCOPE,
|
||||
APPROVALS_SCOPE,
|
||||
PAIRING_SCOPE,
|
||||
];
|
||||
|
||||
@@ -873,7 +873,13 @@ describe("gateway server auth/connect", () => {
|
||||
const { randomUUID } = await import("node:crypto");
|
||||
const os = await import("node:os");
|
||||
const path = await import("node:path");
|
||||
const scopes = ["operator.admin", "operator.approvals", "operator.pairing"];
|
||||
const scopes = [
|
||||
"operator.admin",
|
||||
"operator.read",
|
||||
"operator.write",
|
||||
"operator.approvals",
|
||||
"operator.pairing",
|
||||
];
|
||||
const { device } = await createSignedDevice({
|
||||
token: "secret",
|
||||
scopes,
|
||||
|
||||
@@ -145,7 +145,13 @@ export class GatewayBrowserClient {
|
||||
// Gateways may reject this unless gateway.controlUi.allowInsecureAuth is enabled.
|
||||
const isSecureContext = typeof crypto !== "undefined" && !!crypto.subtle;
|
||||
|
||||
const scopes = ["operator.admin", "operator.approvals", "operator.pairing"];
|
||||
const scopes = [
|
||||
"operator.admin",
|
||||
"operator.read",
|
||||
"operator.write",
|
||||
"operator.approvals",
|
||||
"operator.pairing",
|
||||
];
|
||||
const role = "operator";
|
||||
let deviceIdentity: Awaited<ReturnType<typeof loadOrCreateDeviceIdentity>> | null = null;
|
||||
let canFallbackToShared = false;
|
||||
|
||||
Reference in New Issue
Block a user