mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 15:58:27 +00:00
feat: add /exec session overrides
This commit is contained in:
198
src/auto-reply/reply/exec/directive.ts
Normal file
198
src/auto-reply/reply/exec/directive.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import type { ExecAsk, ExecHost, ExecSecurity } from "../../../infra/exec-approvals.js";
|
||||
|
||||
type ExecDirectiveParse = {
|
||||
cleaned: string;
|
||||
hasDirective: boolean;
|
||||
execHost?: ExecHost;
|
||||
execSecurity?: ExecSecurity;
|
||||
execAsk?: ExecAsk;
|
||||
execNode?: string;
|
||||
rawExecHost?: string;
|
||||
rawExecSecurity?: string;
|
||||
rawExecAsk?: string;
|
||||
rawExecNode?: string;
|
||||
hasExecOptions: boolean;
|
||||
invalidHost: boolean;
|
||||
invalidSecurity: boolean;
|
||||
invalidAsk: boolean;
|
||||
invalidNode: boolean;
|
||||
};
|
||||
|
||||
function normalizeExecHost(value?: string): ExecHost | undefined {
|
||||
const normalized = value?.trim().toLowerCase();
|
||||
if (normalized === "sandbox" || normalized === "gateway" || normalized === "node") return normalized;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function normalizeExecSecurity(value?: string): ExecSecurity | undefined {
|
||||
const normalized = value?.trim().toLowerCase();
|
||||
if (normalized === "deny" || normalized === "allowlist" || normalized === "full") return normalized;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function normalizeExecAsk(value?: string): ExecAsk | undefined {
|
||||
const normalized = value?.trim().toLowerCase();
|
||||
if (normalized === "off" || normalized === "on-miss" || normalized === "always") {
|
||||
return normalized as ExecAsk;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseExecDirectiveArgs(raw: string): Omit<ExecDirectiveParse, "cleaned" | "hasDirective"> & {
|
||||
consumed: number;
|
||||
} {
|
||||
let i = 0;
|
||||
const len = raw.length;
|
||||
while (i < len && /\s/.test(raw[i])) i += 1;
|
||||
if (raw[i] === ":") {
|
||||
i += 1;
|
||||
while (i < len && /\s/.test(raw[i])) i += 1;
|
||||
}
|
||||
let consumed = i;
|
||||
let execHost: ExecHost | undefined;
|
||||
let execSecurity: ExecSecurity | undefined;
|
||||
let execAsk: ExecAsk | undefined;
|
||||
let execNode: string | undefined;
|
||||
let rawExecHost: string | undefined;
|
||||
let rawExecSecurity: string | undefined;
|
||||
let rawExecAsk: string | undefined;
|
||||
let rawExecNode: string | undefined;
|
||||
let hasExecOptions = false;
|
||||
let invalidHost = false;
|
||||
let invalidSecurity = false;
|
||||
let invalidAsk = false;
|
||||
let invalidNode = false;
|
||||
|
||||
const takeToken = (): string | null => {
|
||||
if (i >= len) return null;
|
||||
const start = i;
|
||||
while (i < len && !/\s/.test(raw[i])) i += 1;
|
||||
if (start === i) return null;
|
||||
const token = raw.slice(start, i);
|
||||
while (i < len && /\s/.test(raw[i])) i += 1;
|
||||
return token;
|
||||
};
|
||||
|
||||
const splitToken = (token: string): { key: string; value: string } | null => {
|
||||
const eq = token.indexOf("=");
|
||||
const colon = token.indexOf(":");
|
||||
const idx =
|
||||
eq === -1 ? colon : colon === -1 ? eq : Math.min(eq, colon);
|
||||
if (idx === -1) return null;
|
||||
const key = token.slice(0, idx).trim().toLowerCase();
|
||||
const value = token.slice(idx + 1).trim();
|
||||
if (!key) return null;
|
||||
return { key, value };
|
||||
};
|
||||
|
||||
while (i < len) {
|
||||
const token = takeToken();
|
||||
if (!token) break;
|
||||
const parsed = splitToken(token);
|
||||
if (!parsed) break;
|
||||
const { key, value } = parsed;
|
||||
if (key === "host") {
|
||||
rawExecHost = value;
|
||||
execHost = normalizeExecHost(value);
|
||||
if (!execHost) invalidHost = true;
|
||||
hasExecOptions = true;
|
||||
consumed = i;
|
||||
continue;
|
||||
}
|
||||
if (key === "security") {
|
||||
rawExecSecurity = value;
|
||||
execSecurity = normalizeExecSecurity(value);
|
||||
if (!execSecurity) invalidSecurity = true;
|
||||
hasExecOptions = true;
|
||||
consumed = i;
|
||||
continue;
|
||||
}
|
||||
if (key === "ask") {
|
||||
rawExecAsk = value;
|
||||
execAsk = normalizeExecAsk(value);
|
||||
if (!execAsk) invalidAsk = true;
|
||||
hasExecOptions = true;
|
||||
consumed = i;
|
||||
continue;
|
||||
}
|
||||
if (key === "node") {
|
||||
rawExecNode = value;
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) {
|
||||
invalidNode = true;
|
||||
} else {
|
||||
execNode = trimmed;
|
||||
}
|
||||
hasExecOptions = true;
|
||||
consumed = i;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
consumed,
|
||||
execHost,
|
||||
execSecurity,
|
||||
execAsk,
|
||||
execNode,
|
||||
rawExecHost,
|
||||
rawExecSecurity,
|
||||
rawExecAsk,
|
||||
rawExecNode,
|
||||
hasExecOptions,
|
||||
invalidHost,
|
||||
invalidSecurity,
|
||||
invalidAsk,
|
||||
invalidNode,
|
||||
};
|
||||
}
|
||||
|
||||
export function extractExecDirective(body?: string): ExecDirectiveParse {
|
||||
if (!body) {
|
||||
return {
|
||||
cleaned: "",
|
||||
hasDirective: false,
|
||||
hasExecOptions: false,
|
||||
invalidHost: false,
|
||||
invalidSecurity: false,
|
||||
invalidAsk: false,
|
||||
invalidNode: false,
|
||||
};
|
||||
}
|
||||
const re = /(?:^|\s)\/exec(?=$|\s|:)/i;
|
||||
const match = re.exec(body);
|
||||
if (!match) {
|
||||
return {
|
||||
cleaned: body.trim(),
|
||||
hasDirective: false,
|
||||
hasExecOptions: false,
|
||||
invalidHost: false,
|
||||
invalidSecurity: false,
|
||||
invalidAsk: false,
|
||||
invalidNode: false,
|
||||
};
|
||||
}
|
||||
const start = match.index + match[0].indexOf("/exec");
|
||||
const argsStart = start + "/exec".length;
|
||||
const parsed = parseExecDirectiveArgs(body.slice(argsStart));
|
||||
const cleanedRaw = `${body.slice(0, start)} ${body.slice(argsStart + parsed.consumed)}`;
|
||||
const cleaned = cleanedRaw.replace(/\s+/g, " ").trim();
|
||||
return {
|
||||
cleaned,
|
||||
hasDirective: true,
|
||||
execHost: parsed.execHost,
|
||||
execSecurity: parsed.execSecurity,
|
||||
execAsk: parsed.execAsk,
|
||||
execNode: parsed.execNode,
|
||||
rawExecHost: parsed.rawExecHost,
|
||||
rawExecSecurity: parsed.rawExecSecurity,
|
||||
rawExecAsk: parsed.rawExecAsk,
|
||||
rawExecNode: parsed.rawExecNode,
|
||||
hasExecOptions: parsed.hasExecOptions,
|
||||
invalidHost: parsed.invalidHost,
|
||||
invalidSecurity: parsed.invalidSecurity,
|
||||
invalidAsk: parsed.invalidAsk,
|
||||
invalidNode: parsed.invalidNode,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user