mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
ec8f6e5e03
* feat(browser): add copilot security contracts * fix(gateway): expose verified client identity to handlers * feat(browser): add secure per-tab copilot panel Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * refactor(browser): separate copilot gateway hint custody * fix(browser): preserve legacy pairing parse shape * fix(browser): harden copilot lifecycle custody Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(browser): enforce copilot lifecycle boundaries Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * style(browser): format copilot sources Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(browser): preserve copilot consent revocation Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * refactor(browser): split copilot custody owners Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * test(browser): normalize websocket array buffers Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * chore(protocol): regenerate Swift gateway models Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * refactor(browser): model copilot runtime entrypoints Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(browser): honor extension build boundaries Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * test(gateway): assert targeted chat delivery Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * test(gateway): cover targeted delivery calls Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(browser): declare copilot build dependencies Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(ci): clear browser copilot gate failures Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * test(ci): cover copilot lint exclusion Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(browser): gate copilot on relay custody Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * test(browser): bound copilot relay frames Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> --------- Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>
137 lines
5.0 KiB
TypeScript
137 lines
5.0 KiB
TypeScript
/**
|
|
* Shared gateway client identity contract.
|
|
*
|
|
* These values cross the WebSocket handshake boundary, so additions must stay
|
|
* aligned with protocol schemas and server policy checks.
|
|
*/
|
|
function normalizeOptionalLowercaseString(raw?: string | null): string | undefined {
|
|
if (typeof raw !== "string") {
|
|
return undefined;
|
|
}
|
|
const normalized = raw.trim().toLowerCase();
|
|
return normalized || undefined;
|
|
}
|
|
|
|
/** Canonical client ids accepted in gateway hello/connect payloads. */
|
|
export const GATEWAY_CLIENT_IDS = {
|
|
WEBCHAT_UI: "webchat-ui",
|
|
CONTROL_UI: "openclaw-control-ui",
|
|
BROWSER_COPILOT: "openclaw-browser-copilot",
|
|
TUI: "openclaw-tui",
|
|
WEBCHAT: "webchat",
|
|
CLI: "cli",
|
|
GATEWAY_CLIENT: "gateway-client",
|
|
MACOS_APP: "openclaw-macos",
|
|
IOS_APP: "openclaw-ios",
|
|
WATCHOS_APP: "openclaw-watchos",
|
|
ANDROID_APP: "openclaw-android",
|
|
NODE_HOST: "node-host",
|
|
WORKER: "openclaw-worker",
|
|
TEST: "test",
|
|
FINGERPRINT: "fingerprint",
|
|
PROBE: "openclaw-probe",
|
|
} as const;
|
|
|
|
/** Stable gateway client ids used on the wire during hello/connect handshakes. */
|
|
export type GatewayClientId = (typeof GATEWAY_CLIENT_IDS)[keyof typeof GATEWAY_CLIENT_IDS];
|
|
|
|
// Back-compat naming (internal): these values are IDs, not display names.
|
|
export const GATEWAY_CLIENT_NAMES = GATEWAY_CLIENT_IDS;
|
|
/** Compatibility alias for internal callers that still use "name" terminology. */
|
|
export type GatewayClientName = GatewayClientId;
|
|
|
|
/** Coarse modes let policy group clients without matching every product id. */
|
|
export const GATEWAY_CLIENT_MODES = {
|
|
WEBCHAT: "webchat",
|
|
CLI: "cli",
|
|
UI: "ui",
|
|
BACKEND: "backend",
|
|
NODE: "node",
|
|
WORKER: "worker",
|
|
PROBE: "probe",
|
|
TEST: "test",
|
|
} as const;
|
|
|
|
/** Coarse client category used for gateway policy and diagnostics. */
|
|
export type GatewayClientMode = (typeof GATEWAY_CLIENT_MODES)[keyof typeof GATEWAY_CLIENT_MODES];
|
|
|
|
/** Client metadata sent during gateway connection setup. */
|
|
export type GatewayClientInfo = {
|
|
/** Stable product/client identifier from `GATEWAY_CLIENT_IDS`. */
|
|
id: GatewayClientId;
|
|
/** Human-readable label for diagnostics; not used for policy decisions. */
|
|
displayName?: string;
|
|
/** Client app or package version reported by the connecting process. */
|
|
version: string;
|
|
/** Runtime platform string, such as `darwin`, `ios`, `android`, or `web`. */
|
|
platform: string;
|
|
/** Optional device family used by native clients for display and routing hints. */
|
|
deviceFamily?: string;
|
|
/** Native hardware/model identifier when available. */
|
|
modelIdentifier?: string;
|
|
/** Coarse category from `GATEWAY_CLIENT_MODES` for policy and diagnostics. */
|
|
mode: GatewayClientMode;
|
|
/** Per-installation or per-process id used to distinguish same-product clients. */
|
|
instanceId?: string;
|
|
};
|
|
|
|
/** Capability flags a client may advertise during the gateway handshake. */
|
|
export const GATEWAY_CLIENT_CAPS = {
|
|
APPROVALS: "approvals",
|
|
EXEC_APPROVALS: "exec-approvals",
|
|
INLINE_WIDGETS: "inline-widgets",
|
|
RUN_TOOL_BINDINGS: "run-tool-bindings",
|
|
SESSION_SCOPED_EVENTS: "session-scoped-events",
|
|
PLUGIN_APPROVALS: "plugin-approvals",
|
|
TASK_SUGGESTIONS: "task-suggestions",
|
|
TERMINAL_OFFSET_SEQ: "terminal-offset-seq",
|
|
TOOL_EVENTS: "tool-events",
|
|
UI_COMMANDS: "ui-commands",
|
|
} as const;
|
|
|
|
/** Optional capability advertised by clients during gateway handshake. */
|
|
export type GatewayClientCap = (typeof GATEWAY_CLIENT_CAPS)[keyof typeof GATEWAY_CLIENT_CAPS];
|
|
|
|
const GATEWAY_CLIENT_ID_SET = new Set<GatewayClientId>(Object.values(GATEWAY_CLIENT_IDS));
|
|
const GATEWAY_CLIENT_MODE_SET = new Set<GatewayClientMode>(Object.values(GATEWAY_CLIENT_MODES));
|
|
|
|
/** Normalizes untrusted client ids and rejects unknown values. */
|
|
export function normalizeGatewayClientId(raw?: string | null): GatewayClientId | undefined {
|
|
// Handshake input is intentionally case-insensitive, but policy decisions use
|
|
// the canonical lowercase ids from the closed registry above.
|
|
const normalized = normalizeOptionalLowercaseString(raw);
|
|
if (!normalized) {
|
|
return undefined;
|
|
}
|
|
return GATEWAY_CLIENT_ID_SET.has(normalized as GatewayClientId)
|
|
? (normalized as GatewayClientId)
|
|
: undefined;
|
|
}
|
|
|
|
/** Normalizes legacy client-name fields through the canonical client-id registry. */
|
|
export function normalizeGatewayClientName(raw?: string | null): GatewayClientName | undefined {
|
|
return normalizeGatewayClientId(raw);
|
|
}
|
|
|
|
/** Normalizes untrusted client modes and rejects unknown values. */
|
|
export function normalizeGatewayClientMode(raw?: string | null): GatewayClientMode | undefined {
|
|
const normalized = normalizeOptionalLowercaseString(raw);
|
|
if (!normalized) {
|
|
return undefined;
|
|
}
|
|
return GATEWAY_CLIENT_MODE_SET.has(normalized as GatewayClientMode)
|
|
? (normalized as GatewayClientMode)
|
|
: undefined;
|
|
}
|
|
|
|
/** Checks a client-advertised capability list without treating missing caps as errors. */
|
|
export function hasGatewayClientCap(
|
|
caps: string[] | null | undefined,
|
|
cap: GatewayClientCap,
|
|
): boolean {
|
|
if (!Array.isArray(caps)) {
|
|
return false;
|
|
}
|
|
return caps.includes(cap);
|
|
}
|