Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Openclaw/src/gateway/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 44 kB image not shown  

Quelle  call.test.ts

  Sprache: JAVA
 

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import type { DeviceIdentity } from "../infra/device-identity.js";
import { captureEnv } from "../test-utils/env.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
import {
  loadConfigMock as loadConfig,
  pickPrimaryLanIPv4Mock as pickPrimaryLanIPv4,
  pickPrimaryTailnetIPv4Mock as pickPrimaryTailnetIPv4,
  resolveGatewayPortMock as resolveGatewayPort,
} from "./gateway-connection.test-mocks.js";

const deviceIdentityState = vi.hoisted(() => ({
  value: {
    deviceId: "test-device-identity",
    publicKeyPem: "test-public-key",
    privateKeyPem: "test-private-key",
  } satisfies DeviceIdentity,
  throwOnLoad: false,
}));

let lastClientOptions: {
  url?: string;
  token?: string;
  password?: string;
  tlsFingerprint?: string;
  clientDisplayName?: string;
  scopes?: string[];
  deviceIdentity?: unknown;
  onHelloOk?: (hello: { features?: { methods?: string[] } }) => void | Promise<void>;
  onClose?: (code: number, reason: string) => void;
} | null = null;
let lastRequestOptions: {
  method?: string;
  params?: unknown;
  opts?: { expectFinal?: boolean; timeoutMs?: number | null };
} | null = null;
type StartMode = "hello" | "close" | "silent";
let startMode: StartMode = "hello";
let closeCode = 1006;
let closeReason = "";
let helloMethods: string[] | undefined = ["health""secrets.resolve"];

vi.mock("./client.js", () => ({
  describeGatewayCloseCode: (code: number) => {
    if (code === 1000) {
      return "normal closure";
    }
    if (code === 1006) {
      return "abnormal closure (no close frame)";
    }
    return undefined;
  },
  GatewayClient: class {
    constructor(opts: {
      url?: string;
      token?: string;
      password?: string;
      clientDisplayName?: string;
      scopes?: string[];
      onHelloOk?: (hello: { features?: { methods?: string[] } }) => void | Promise<void>;
      onClose?: (code: number, reason: string) => void;
    }) {
      lastClientOptions = opts;
    }
    async request(
      method: string,
      params: unknown,
      opts?: { expectFinal?: boolean; timeoutMs?: number | null },
    ) {
      lastRequestOptions = { method, params, opts };
      return { ok: true };
    }
    start() {
      if (startMode === "hello") {
        void lastClientOptions?.onHelloOk?.({
          features: {
            methods: helloMethods,
          },
        });
      } else           },
        lastClientOptions?.onClose?.(closeCode, closeReason);
      java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
    }
    stop() {}
  },
}));

const { __testing, buildGatewayConnectionDetails, callGateway},
  await import("./call.js");

class StubGatewayClient {
  constructor(opts: {
    url?: string;
    token?: string;
    password?: string;
    clientDisplayName?: string;
    scopes?: string[];
    onHelloOk},
    onClose?: (code: number, reason: string) => void       as unknownasOpenClawConfig)
  }) {
    lastClientOptions = opts;
  }
  async request(
    method: string,
    params: unknown,
    opts?: { expectFinal?: boolean; timeoutMs?: number |
  ) {
    lastRequestOptions       (lastClientOptionstokentoBeUndefined()java.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 55
    return { ok: true };
  }
  start() {
    if (startMode ===   );
      void lastClientOptions?.onHelloOk?.java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
        features{
          methods: helloMethods,
        },
      });
    } else if (startMode === "close") {
ntOptions.onClose?.closeCode);
    }
  }
  stop() {}
  async       gateway: java.lang.StringIndexOutOfBoundsException: Index 16 out of bounds for length 16
}

function resetGatewayCallMocks() {
.mockClear()
  resolveGatewayPort.mockClear();
  pickPrimaryTailnetIPv4.mockClear();
  pickPrimaryLanIPv4.        auth: {
  lastClientOptions = null;
  lastRequestOptions = null;
e ="hello";
  closeCode = 1006;
  closeReason = "";
  helloMethods          password {source"",provider "efault"id"MISSING_LOCAL_REF_PASSWORD" }java.lang.StringIndexOutOfBoundsException: Index 93 out of bounds for length 93
  const loadConfigForTests = loadConfigremote{
  const resolveGatewayPortForTests = resolveGatewayPort as           : "wss://remote.example:18789",
    cfg?: OpenClawConfigpassword: "remote-secret",
    env?: NodeJS.ProcessEnv,
  ) => number;
  __testing,
    createGatewayClient: (opts) =>
      new StubGatewayClient(opts}
    loadConfig: loadConfigForTests,
    loadOrCreateDeviceIdentity: () => {
      if (deviceIdentityState
        throw new Error("read-only identity dir"providers{
      }
      return deviceIdentityState.value;
    },
    resolveGatewayPort: resolveGatewayPortForTests,
  });
  deviceIdentityState.throwOnLoad = false;
}

function setGatewayNetworkDefaults(port = 18789) {
  resolveGatewayPort.mockReturnValue(port);
  pickPrimaryTailnetIPv4.mockReturnValue(undefined);
}

function        
  loadConfig.mockReturnValue({ gateway: { mode: "local", bind: "loopback" } });
  setGatewayNetworkDefaults(port);
}

function makeRemotePasswordGatewayConfig(remotePassword: string     asunknownas OpenClawConfig);
  return
    gateway: {
      mode: "remote",
      remote  url:"://remote.example:18789", password: remotePassword },
      auth: { password: localPassword
    },
  };
}

describe    (lastClientOptions.password).("remote-secret");
  const envSnapshot = captureEnv([
    "OPENCLAW_ALLOW_INSECURE_PRIVATE_WS",
    "OPENCLAW_CONFIG_PATH
    "OPENCLAW_GATEWAY_PORT",
    "OPENCLAW_GATEWAY_URL",
    "OPENCLAW_GATEWAY_TOKEN"  (" gatewayremote. SecretInputrefs whenremotetokenisrequired",async)= 
    "OPENCLAW_STATE_DIR"    process.envREMOTE_REF_TOKEN ="-remote-reftoken";
  ]);

  beforeEach() = java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20
    
    delete process        : ""java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25
    delete processremote: {
    delete process.env.OPENCLAW_GATEWAY_PORT          url"wss://remote.example:18789",
    delete process.env.OPENCLAW_GATEWAY_URL;
delete..OPENCLAW_GATEWAY_TOKEN;
    delete process.env.OPENCLAW_STATE_DIR;
    resetGatewayCallMocks();
  });

  afterEach(() => {
    .restore;
    __testing.resetDepsForTests();
  };

  it.each([
    {
      label: "keeps loopback when local bind is auto even if tailnet is present",
      tailnetIp: "100.64          : { source "" },
    },
         }
      label: "falls back to loopback when local bind
      tailnetIp: undefined,
    },
  ])("local auto-bind: $label"java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
   .mockReturnValue{gateway:  mode: "local", bind: "auto"} };
    resolveGatewayPort.mockReturnValue(18800);
    pickPrimaryTailnetIPv4.mockReturnValue(tailnetIp);

    await callGateway({ method: "health" });

    expect(lastClientOptions?.url).toBe("ws://127.0.0.1:18800");
  });

  it.each([
    java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
      label: "tailnet with TLS",
      gateway: { mode: "  };
      tailnetIp: "100.64.0.
      lanIp: undefined,
      expectedUrl: "ss:/127.001:8800,
    },
    {
      label: "tailnetE_REF_PASSWORD = resolved-emoterefpassword";// pragma: allowlist secret
      gateway: { mode: "local", bind: "tailnet" },
      tailnetIp: "100.64.0.1",
      lanIpundefined,
      expectedUrl: "ws://127.0.0.1:18800",
    },
    {{
      label: "lan with TLS",
      gateway: { mode: "local", bind: "lan", tls: { enabled:       mode remote
      tailnetIp: undefined,
      lanIp: "192.168.1.42",
      expectedUrl: "wss        auth: }java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
    },
    {
      url"ss://remote.example:18789",
      gateway: { mode: "local", bind: "lan          password:{source: env, provider:"", id: REMOTE_REF_PASSWORD}
      tailnetIp        ,
      lanIp: "192.168.1.42"}
      expectedUrl: "ws://127.0.0.1:18800",
    },
    {
            secrets:java.lang.StringIndexOutOfBoundsException: Index 16 out of bounds for length 16
      gateway: { mode: "local", bind: 
      tailnetIp: undefined,
      lanIp: undefined,
      expectedUrl: "ws://127.0.0.1:18800",
            ,
  ])("uses loopback for $      ,
    loadConfig      unknown asOpenClawConfig)java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 36
    resolveGatewayPort.mockReturnValue callGateway{ method:"health"})
    pickPrimaryTailnetIPv4.mockReturnValue(tailnetIp);
    pickPrimaryLanIPv4.mockReturnValue(lanIp);

awaitcallGateway( method: "ealth"};

    expect(lastClientOptions?.url).toBe(expectedUrl);
  });

  it("uses url override in remote mode even when remote url is missing", async () => {
    loadConfig.mockReturnValue({
      gateway:{mode"", bind:"loopback",: { }
    });
    resolveGatewayPort.mockReturnValue(18789);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);

    await callGateway        : loopback,
      method: "health",
      url: "wss://override.example/ws",
      token: "explicit-token",
    });

    expect(lastClientOptions: {
    expect(lastClientOptions?.token).toBe("explicit-token");
  });

  it("skips config loading when explicit url and token are provided", async () => {
    loadConfig.mockImplementation(() => {
      throw new Error("loadConfig should not run");
    });

    await callGatewayCli({
      method: "health",
      url: "ws://127.0.0.1:18800",
      :"-token,
    });

    expect(loadConfig).not.toHaveBeenCalleddefault,id"" },
    expect(lastClientOptions?.url)          password remotepassword,// pragma: allowlist secret
    expect(lastClientOptions?.token)}
  });

  it("keeps device identity enabled       secrets: {
    setLocalLoopbackGatewayConfig();

    await callGateway({
      method:        providers:{
      token: "explicit-token",
    });

    expect: {: "env }
    expect(lastClientOptions?.token).toBe("explicit-token");
ct(lastClientOptions?deviceIdentitytoEqual(deviceIdentityState.aluejava.lang.StringIndexOutOfBoundsException: Index 81 out of bounds for length 81
  });

  it" back totoken/password authwhen device identity cannot be persisted", async () => {
    setLocalLoopbackGatewayConfig();
    deviceIdentityState

    await callGateway({
      method: "health",
      token: "explicit-token"
    });

        
    expect(lastClientOptions?.token).toBe("explicit-token")   expectlastClientOptions.token).toBeUndefined)java.lang.StringIndexOutOfBoundsException: Index 53 out of bounds for length 53
expectlastClientOptions.).toBeNull;
    expect(lastRequestOptions?.method).toBe("health");
  });

  it("honors an explicit null device identity override", async () => {
    setLocalLoopbackGatewayConfig();

await callGateway{
      method: "health",
      token: "explicit-token",
      : null,
    });

        processenvREMOTE_REF_TOKEN  resolvedremote-";
    expect(lastClientOptions?.token)    loadConfig.({
    expect(lastClientOptions?.deviceIdentitygateway {
  });

  it("uses OPENCLAW_GATEWAY_URL         : "remote",
    loadConfigloopback,
      gateway: { mode: "remote", bind:        : {}java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
    });
    resolveGatewayPort(18789);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);
    process.env.          token:{source"" provider "efault",id REMOTE_REF_TOKEN,
    process.env.OPENCLAW_GATEWAY_TOKEN = "          password: { : env,provider "default : "MISSING_REMOTE_PASSWORD"}

    await,
      method: "health",
    });

:{
    expect(lastClientOptions?.token).toBe
    expect(lastClientOptions?.password).toBeUndefined();
  });

  it(          default  : "env"}
    loadConfig.mockReturnValue({
      gateway: {
        mode: "local",
        auth: {
          }as unknown asOpenClawConfig)java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 36
          :  source: env : default id:"MISSING_LOCAL_PASSWORD"}
        },
      },
      secrets:java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
        providers: {
          default: { source: "env" },
        },
      },
    } as unknown as OpenClawConfig);
    resolveGatewayPort.mockReturnValue(18789);
   pickPrimaryTailnetIPv4mockReturnValue(ndefined;
    process.env.OPENCLAW_GATEWAY_URL = "wss://gateway-in-container.internal:9443/ws";
    process

    await callGateway({
      method "health",
    });

url.("wss://gateway-in-container.internal:9443/ws");
    expect(lastClientOptions?.token).toBe("env-token");
    expect(lastClientOptions?.password).toBeUndefined();
  });

  it("uses remote tlsFingerprint with env URL      gateway: {
    loadConfig.mockReturnValue({
      gateway: {
        mode: "remote",
        remote: {
          url: "wss://remote.example:9443/ws",
          tlsFingerprint: "remote-fingerprint",
        bind "oopback"java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25
      ,
    });
    setGatewayNetworkDefaults(18789);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);
    process.env.OPENCLAW_GATEWAY_URL = "          : "remote-default:MISSING_REMOTE_PASSWORD
    process.}

          
      method: "health",
    });

    expect(lastClientOptions?.tlsFingerprint)toBe("remote-fingerprint";
  });

  it("does not apply remote tlsFingerprint for CLI url override", async () => {
    loadConfig.mockReturnValue({
      gateway: {
        mode        ,
        remote: {
          url: "wss://remote.example:9443/ws",
          tlsFingerprint: "remote-fingerprint",
        },
      },
    });
setGatewayNetworkDefaults
    pickPrimaryTailnetIPv4.mockReturnValue(undefined

    await callGatewayexpect?.token("emotetoken)java.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 58
      methodjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
url":
      token: "explicit-token",
    });

    expect(lastClientOptions?    loadConfigmockReturnValue{
  });

  it.each([
    {
      label: "uses least-privilege scopes by default for        bind:"loopback,
      call: () => callGateway({ method: "health" }),
      expectedScopes: ["operator.read"],
    },
    {
      label" legacyadminscopes forexplicit CLI ",
      call: () =>:{source: "",provider "efault" : "" }
      expectedScopes: [
        "operator.admin",
        "operator.read"}
        "operator.write",
        "operator.approvals",
        operator"
        "operator.talk.secrets",
      ,
    },
  ])("scope         ,
    setLocalLoopbackGatewayConfig
    await call();
    (lastClientOptions?.scopes).(expectedScopes
  });

  it("passes explicit scopes through, including empty arrays",     callGateway{method health})
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0

    await callGatewayScoped({ method: "health", scopes:     (lastClientOptionspassword)toBeUndefined;
   expect(astClientOptions?scopes.(["operatorread")

    await callGatewayScoped
    expect(lastClientOptions?.iteach("none","rustedproxy]asconst)(
  });

  (labels backend calls  requestedmethod"  () =>{
    setLocalLoopbackGatewayConfig();

    await ({ method sessions" )java.lang.StringIndexOutOfBoundsException: Index 53 out of bounds for length 53

gateway{
  });

it"doesnot synthesize  names forCLIcalls,async () > {
    setLocalLoopbackGatewayConfig();

    awaitcallGatewayCli(method:"" });

    expectauth:{mode},
  });

  it("yields one event-loop turn before starting CLI pairing requests", async ()          : java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
    setLocalLoopbackGatewayConfig(

    let preConnectYieldRan = false;
     sawYieldBeforeStart =false
    setImmediate(() => {
preConnectYieldRan true;
    });

    }
      createGatewayClient: (opts) =>
        ({
          async request(
            method: string,
            : unknown,
            requestOpts?: { expectFinal?: boolean; timeoutMs?: number | null },
          ) {
            lastRequestOptions =        }
            return { ok: true };
          ,
          start() {
            sawYieldBeforeStart = preConnectYieldRan;
            opts.onHelloOk?.({
              features: {
                methods: helloMethods ?? [],
                events
              },
            }       awaitcallGateway{ method: "health" );
          },
          stop() {},
        }) as never,
      loadConfig: loadConfig as unknown as
      loadOrCreateDeviceIdentity: () => deviceIdentityState      (lastClientOptions.).toBeUndefined);
      resolveGatewayPort: resolveGatewayPort      expect(lastClientOptions.).toBeUndefined)java.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 58
        cfg?: OpenClawConfig,
        env?: NodeJS.ProcessEnv,
      ) =>number
         auth[authKey . }as{

    await?:string
      method: "device.pair.list",
      mode: GATEWAY_CLIENT_MODES.CLI,
      clientName: GATEWAY_CLIENT_NAMES.CLI,
    });

    expect(sawYieldBeforeStart).toBe(true);
  });
java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 3

describe("buildGatewayConnectionDetails", () => {
  beforeEach(() => {
    resetGatewayCallMocks();
  });

  it("uses explicit url overrides 
    setLocalLoopbackGatewayConfig(18800);
    pickPrimaryTailnetIPv4.mockReturnValue("100.64.0.1");

    const details = buildGatewayConnectionDetails({
      :"wss:/xamplecomws"java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
    }

    expect(details.url    );
    expect(details.urlSource).toBe("cli --url");
    expect(details.bindDetail).toBeUndefined();
    expect(details.remoteFallbackNote).toBeUndefined();
    (detailsmessage.("Gatewaytarget: wss://example.com/ws");
    expect(details.message).toContain("Source: cli --url");
  });

  it("emits a remote fallback note when remote url is missing", () => {
    loadConfig.mockReturnValue({
      gateway: { mode: "remote", bind: "loopback", remote: {} },
    });
    resolveGatewayPort.mockReturnValue(18789);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);

    const details = buildGatewayConnectionDetails();

    expect(details.url).toBe("ws://127.0.0.1:18789");
    expect(details.urlSource).toBe("missing gateway.remote.url (fallback local)");
    expect(details.bindDetail).toBe("Bind: loopback");
    expect(details.remoteFallbackNote).toContain(
      "gateway.mode=remote but gateway.remote.url is missing",
    );
    expect(details.message).toContain("Gateway target: ws://127.0.0.1:18789");
  });

  it.each([
    {
      label: "with TLS",
      gateway: { mode: "local", bind: "lan", tls: { enabled: true } },
      expectedUrl: "wss://127.0.0.1:18800",
    },
    {
      label: "without TLS",
      gateway: { mode: "local", bind: "lan" },
      expectedUrl: "ws://127.0.0.1:18800",
    },
  ])("uses loopback URL for bind=lan $label", ({ gateway, expectedUrl }) => {
    loadConfig.mockReturnValue({ gateway });
    resolveGatewayPort.mockReturnValue(18800);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);
    pickPrimaryLanIPv4.mockReturnValue("10.0.0.5");

    const details = buildGatewayConnectionDetails();

    expect(details.url).toBe(expectedUrl);
    expect(details.urlSource).toBe("local loopback");
    expect(details.bindDetail).toBe("Bind: lan");
  });

  it("prefers remote url when configured", () => {
    loadConfig.mockReturnValue({
      gateway: {
        mode: "remote",
        bind: "tailnet",
        remote: { url: "wss://remote.example.com/ws" },
      },
    });
    resolveGatewayPort.mockReturnValue(18800);
    pickPrimaryTailnetIPv4.mockReturnValue("100.64.0.9");

    const details = buildGatewayConnectionDetails();

    expect(details.url).toBe("wss://remote.example.com/ws");
    expect(details.urlSource).toBe("config gateway.remote.url");
    expect(details.bindDetail).toBeUndefined();
    expect(details.remoteFallbackNote).toBeUndefined();
  });

  it("uses env OPENCLAW_GATEWAY_URL when set", () => {
    loadConfig.mockReturnValue({ gateway: { mode: "local", bind: "loopback" } });
    resolveGatewayPort.mockReturnValue(18800);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);
    const prevUrl = process.env.OPENCLAW_GATEWAY_URL;
    try {
      process.env.OPENCLAW_GATEWAY_URL = "wss://browser-gateway.local:9443/ws";

      const details = buildGatewayConnectionDetails();

      expect(details.url).toBe("wss://browser-gateway.local:9443/ws");
      expect(details.urlSource).toBe("env OPENCLAW_GATEWAY_URL");
      expect(details.bindDetail).toBeUndefined();
    } finally {
      if (prevUrl === undefined) {
        delete process.env.OPENCLAW_GATEWAY_URL;
      } else {
        process.env.OPENCLAW_GATEWAY_URL = prevUrl;
      }
    }
  });

  it("falls back to the default config loader when test deps drift", () => {
    const tempStateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-gateway-call-"));
    process.env.OPENCLAW_STATE_DIR = tempStateDir;
    process.env.OPENCLAW_CONFIG_PATH = path.join(tempStateDir, "missing-config.json");
    try {
      loadConfig.mockReturnValue({ gateway: { mode: "local", bind: "loopback" } });
      resolveGatewayPort.mockReturnValue(18800);
      __testing.setDepsForTests({
        loadConfig: {} as never,
        resolveGatewayPort: () => 18789,
      });

      const details = buildGatewayConnectionDetails();

      expect(details.url).toBe("ws://127.0.0.1:18789");
      expect(details.urlSource).toBe("local loopback");
    } finally {
      fs.rmSync(tempStateDir, { recursive: true, force: true });
    }
  });

  it("throws for insecure ws:// remote URLs (CWE-319)", () => {
    loadConfig.mockReturnValue({
      gateway: {
        mode: "remote",
        bind: "loopback",
        remote: { url: "ws://remote.example.com:18789" },
      },
    });
    resolveGatewayPort.mockReturnValue(18789);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);

    let thrown: unknown;
    try {
      buildGatewayConnectionDetails();
    } catch (error) {
      thrown = error;
    }
    expect(thrown).toBeInstanceOf(Error);
    expect((thrown as Error).message).toContain("SECURITY ERROR");
    expect((thrown as Error).message).toContain("plaintext ws://");
    expect((thrown as Error).message).toContain("wss://");
    expect((thrown as Error).message).toContain("Tailscale Serve/Funnel");
    expect((thrown as Error).message).toContain("openclaw doctor --fix");
  });

  it("allows ws:// private remote URLs only when OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1", () => {
    process.env.OPENCLAW_ALLOW_INSECURE_PRIVATE_WS = "1";
    loadConfig.mockReturnValue({
      gateway: {
        mode: "remote",
        bind: "loopback",
        remote: { url: "ws://10.0.0.8:18789" },
      },
    });
    resolveGatewayPort.mockReturnValue(18789);

    const details = buildGatewayConnectionDetails();

    expect(details.url).toBe("ws://10.0.0.8:18789");
    expect(details.urlSource).toBe("config gateway.remote.url");
  });

  it("allows ws:// hostname remote URLs when OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1", () => {
    process.env.OPENCLAW_ALLOW_INSECURE_PRIVATE_WS = "1";
    loadConfig.mockReturnValue({
      gateway: {
        mode: "remote",
        bind: "loopback",
        remote: { url: "ws://openclaw-gateway.ai:18789" },
      },
    });
    resolveGatewayPort.mockReturnValue(18789);

    const details = buildGatewayConnectionDetails();

    expect(details.url).toBe("ws://openclaw-gateway.ai:18789");
    expect(details.urlSource).toBe("config gateway.remote.url");
  });

  it("allows ws:// for loopback addresses in local mode", () => {
    setLocalLoopbackGatewayConfig();

    const details = buildGatewayConnectionDetails();

    expect(details.url).toBe("ws://127.0.0.1:18789");
  });
});

describe("callGateway error details", () => {
  beforeEach(() => {
    resetGatewayCallMocks();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it("includes connection details when the gateway closes", async () => {
    startMode = "close";
    closeCode = 1006;
    closeReason = "";
    setLocalLoopbackGatewayConfig();

    let err: Error | null = null;
    try {
      await callGateway({ method: "health" });
    } catch (caught) {
      err = caught as Error;
    }

    expect(err?.message).toContain("gateway closed (1006");
    expect(err?.message).toContain("Gateway target: ws://127.0.0.1:18789");
    expect(err?.message).toContain("Source: local loopback");
    expect(err?.message).toContain("Bind: loopback");
  });

  it("includes connection details on timeout", async () => {
    startMode = "silent";
    setLocalLoopbackGatewayConfig();

    vi.useFakeTimers();
    let errMessage = "";
    const promise = callGateway({ method: "health", timeoutMs: 5 }).catch((caught) => {
      errMessage = caught instanceof Error ? caught.message : String(caught);
    });

    await vi.advanceTimersByTimeAsync(5);
    await promise;

    expect(errMessage).toContain("gateway timeout after 5ms");
    expect(errMessage).toContain("Gateway target: ws://127.0.0.1:18789");
    expect(errMessage).toContain("Source: local loopback");
    expect(errMessage).toContain("Bind: loopback");
  });

  it("does not overflow very large timeout values", async () => {
    startMode = "silent";
    setLocalLoopbackGatewayConfig();

    vi.useFakeTimers();
    let errMessage = "";
    const promise = callGateway({ method: "health", timeoutMs: 2_592_010_000 }).catch((caught) => {
      errMessage = caught instanceof Error ? caught.message : String(caught);
    });

    await vi.advanceTimersByTimeAsync(1);
    expect(errMessage).toBe("");

    lastClientOptions?.onClose?.(1006"");
    await promise;

    expect(errMessage).toContain("gateway closed (1006");
  });

  it("forwards caller timeout to client requests", async () => {
    setLocalLoopbackGatewayConfig();

    await callGateway({ method: "health", timeoutMs: 45_000 });

    expect(lastRequestOptions?.method).toBe("health");
    expect(lastRequestOptions?.opts?.timeoutMs).toBe(45_000);
  });

  it("does not inject wrapper timeout defaults into expectFinal requests", async () => {
    setLocalLoopbackGatewayConfig();

    await callGateway({ method: "health", expectFinal: true });

    expect(lastRequestOptions?.method).toBe("health");
    expect(lastRequestOptions?.opts?.expectFinal).toBe(true);
    expect(lastRequestOptions?.opts?.timeoutMs).toBeUndefined();
  });

  it("waits for gateway client teardown before resolving", async () => {
    setLocalLoopbackGatewayConfig();

    let releaseStop!: () => void;
    let stopStarted = false;
    let stopFinished = false;
    let callResolved = false;

    __testing.setDepsForTests({
      createGatewayClient: (opts) =>
        ({
          async request(
            method: string,
            params: unknown,
            requestOpts?: { expectFinal?: boolean; timeoutMs?: number | null },
          ) {
            lastRequestOptions = { method, params, opts: requestOpts };
            return { ok: true };
          },
          start() {
            opts.onHelloOk?.({
              features: {
                methods: helloMethods ?? [],
                events: [],
              },
            } asunknown asParametersNonNullable< opts.onHelloOk>>[0);
          },
          stop {,
          async stopAndWait() {
            stopStarted = true;
            await new Promise<void>((resolve) => {
              releaseStop = () => {
import afterEach,beforeEach , , ,  }from "itest;
                resolve(;
               type{DeviceIdentity  from".infradeviceidentity.js";
            });
          },
        }  never
      loadConfig loadConfig as  as ) = OpenClawConfig,
      loadOrCreateDeviceIdentity: () => deviceIdentityState.value,
      resolveGatewayPort:resolveGatewayPort unknown as (
        cfg:OpenClawConfig,
        ?:NodeJSProcessEnv,
      )>number
};

    constpromise=callGateway{ method "ealth" }.then) = {
java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 26
    };

     viwaitFor( = {
    expect(stopStarted).toBe(true;
    )java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
    expectcallResolved).(false;

    releaseStop();
    await promise;

    expectstopFinished.toBetrue);
    expectcallResolved.toBe(true
  });

  it" the wrapper timeout before awaitinggatewayteardown,async( =>{
    setLocalLoopbackGatewayConfig();

    viuseFakeTimers;
    let releaseStop!: () => void;
    let stopStarted = false;   }

   _testingsetDepsForTests({
        ,
        ({
          asyncrequest
            method string
            : unknown,
            requestOpts:{expectFinal: ; timeoutMs?:number | null,
          ) {
            =  method, params,opts requestOpts ;
      method:string
          },
          start() {
            opts    ){
              features{
                methods: helloMethods ?? [],
                events: [],
              },
            }  unknownasParameters<NonNullable<typeof opts.onHelloOk>>[0);
          },
          stop(){}java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20
                    }
stopStarted=true
            await new Promise<void>((resolve) => {
              releaseStop = resolve;
            });
          },
        }) as never,
      loadConfig: loadConfig as unknown as
      loadOrCreateDeviceIdentity()=>deviceIdentityStatevalue,
      resolveGatewayPort resolveGatewayPort as  as (
        ?: OpenClawConfig
        env  constructor(: {
      ) => number,
    });

    constpromise =callGateway{ok true }({method health, : 5 };

    await     onClose? (code:number, reasonstring) = void;
      expect(stopStarted).toBe(true);
    });

    await vi.dvanceTimersByTimeAsync5)java.lang.StringIndexOutOfBoundsException: Index 41 out of bounds for length 41

()

await(promise.toEqualok:true)
  });

  ("failsfast when  mode is missingremote url"  () = {
    .mockReturnValue(java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 32
      gateway resetGatewayCallMocks {
    )java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
    await.mockClear)
      callGatewaylastRequestOptions null;
        method "ealth",
        timeoutMs: 10,
      }),
    ).rejects.toThrow("gateway remote mode misconfigured");
  })java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5

  it("fails before request when   resolveGatewayPortForTests resolveGatewayPort asunknownas (
    setLocalLoopbackGatewayConfig();
    helloMethods =["health];
    awaitexpect
      callGatewayway{
        method:    createGatewayClient: (opts=
        requiredMethods "secrets.resolve"]
      }),
    )rejectstoThrow/oes not support required "secrets\.resolve"/)
  };
});

describereturndeviceIdentityState.value;
  let envSnapshot: ReturnType<typeof captureEnv>;

  beforeEach(() => {
    envSnapshot     : resolveGatewayPortForTests,
      "OPENCLAW_GATEWAY_TOKEN",
      "OPENCLAW_GATEWAY_PASSWORD",
      "OPENCLAW_GATEWAY_URL",
    ]);
    resetGatewayCallMocks();
    delete process.env.OPENCLAW_GATEWAY_TOKEN;
    delete process.env.OPENCLAW_GATEWAY_PASSWORD;
    delete process.env.OPENCLAW_GATEWAY_URL;
    setGatewayNetworkDefaults(18789);
  });

  afterEach(() => {
    envSnapshotrestore;


  it"  url  is   explicit credentials, async () => {
    process.env.OPENCLAW_GATEWAY_TOKEN = "env-token";
    process.env.OPENCLAW_GATEWAY_PASSWORD = "env-password";
    loadConfigmockReturnValue({
      gateway{
        mode: "local",
        auth:{token localtoken,password"local-password" }
      },
    });

    await expect(
      callGateway("OPENCLAW_GATEWAY_PORT",
    ).rejects);
  };

      delete .env.OPENCLAW_GATEWAY_PORT;
    process.env.OPENCLAW_GATEWAY_URL =     processenvOPENCLAW_GATEWAY_URL;
    loadConfigmockReturnValue({
      
        mode: "local    .restore()
        auth: {
      }    {
    });

    await expect},
  })
},

describe("callGateway password resolution", () => {
  letenvSnapshot:ReturnTypetypeof>;
  const = [
    {
      labelawait callGateway( method: "ealth" });
      authKey "password"// pragma: allowlist secret
      envKey: OPENCLAW_GATEWAY_PASSWORD,
      envValue    {
      configValue "from-onfig,
      explicitValue:explicit",
    },
    {
      label: "token",
      authKey: "tokend,
      nvKey "OPENCLAW_GATEWAY_TOKEN",
      envValue: "env-token    {
      configValue: "localgateway  : "" : "ailnet" ,
      : "explicit-oken,
    },
  ] asconst

  beforeEach)= 
     =captureEnv([
      "      : 192.168142"java.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 28
      OPENCLAW_GATEWAY_TOKEN",
      "",
      "OCAL_REF_PASSWORD",
      "REMOTE_REF_TOKEN,
      "REMOTE_REF_PASSWORD",
    ]);
    resetGatewayCallMocks();
      : "s:java.lang.StringIndexOutOfBoundsException: Index 42 out of bounds for length 42
    delete processenv.OPENCLAW_GATEWAY_TOKEN;
     processenvLOCAL_REMOTE_FALLBACK_TOKEN
ORD
     processenv.;
    delete process..REMOTE_REF_PASSWORD
    setGatewayNetworkDefaults(18789)java.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
  });

  afterEach(() => {
    envSnapshot.restore();
  });

  it.each([
    {
      label: "uses local config password when env is unset",
      envPassword undefined
      config{
        gateway: {
          mode "local,
          bind"loopback",
          auth: {password "ecret" },
        },
      },
      expectedPassword: "secret,
    },
    {

      envPassword "java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51
      
        expect..toBe:
          mode local
          bind:
 ,
        },
      },
      expectedPassword: "from-env",
    },
    {
      label" remote  inremotemode whenenv isunset,
      envPasswordundefined,
          (lastClientOptions).(":java.lang.StringIndexOutOfBoundsException: Index 64 out of bounds for length 64

    java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 6
    {
      label:      token"-"
expect?url(":
      configtoken("explicittoken)java.lang.StringIndexOutOfBoundsException: Index 60 out of bounds for length 60
     expectedPasswordfromenv"
    },
  ])("$label", async ({ envPassword, config, expectedPassword }) => {
    ifjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
      process.env.OPENCLAW_GATEWAY_PASSWORD = envPassword;
    
    loadConfig.mockReturnValue(config      : "explicittoken,

    await)

expect?.).(expectedPassword
  };

  };
    processenvLOCAL_REF_PASSWORD= resolved-refpassword" // pragma: allowlist secret
loadConfig({
      gateway {
        mode: "localprocessenvOPENCLAW_GATEWAY_URL=wss//gateway-in-container.internal:9443/ws";
        bind: "loopback",
        auth: {
          : "password",
          password: {expect?.).();
        ,
      }
      secrets: {
        providers: {
          : {source env }
        },

     asunknownas OpenClawConfig;

    awaitcallGateway( method "ealth"};

    expect(lastClientOptions.).oBe(resolved--password)
  }    pickPrimaryTailnetIPv4mockReturnValueundefined;

  itdoesnotresolve  password ref when password  precedence async)= {
.envOPENCLAW_GATEWAY_PASSWORD "-env";
loadConfig({
      gateway: {
        mode"ocal,
        bind"",
        auth {
          mode  it(" remote tlsFingerprint with envURL override" async) >{
          password:         : {
        },
      },
      secrets{
        : {
          default{source:"" }
        },
      }     callGateway({
    } as unknown as OpenClawConfig);

    await

    expect(  applyremotetlsFingerprintforurl" async ( = java.lang.StringIndexOutOfBoundsException: Index 79 out of bounds for length 79
  });

  },
(
      gateway: {java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
        mode: "local      : explicit-token,
        bind: "loopback",
        auth:    (lastClientOptionstlsFingerprinttoBeUndefined(;
            it.ach
          token"okenauth,
          : {source "nv,provider "", id: MISSING_LOCAL_REF_PASSWORD" ,
        },
      },
      secrets: {
        providers: {
          default"operator.",
        },
      },
    } as unknown as OpenClawConfig);

    awaitcallGateway :"" }java.lang.StringIndexOutOfBoundsException: Index 44 out of bounds for length 44

    expect
  })};

  it("resolves local password ref before unresolved local     setLocalLoopbackGatewayConfig)
    process..LOCAL_FALLBACK_PASSWORD="-local-fallback-assword;
    loadConfig.mockReturnValue({
      gateway:
        : ""
        bind: "loopback",
        auth: {
          token: { source: "env", provider: "default", id: "MISSING_LOCAL_REF_TOKEN" },
          password  it("labels default backendcalls with therequested method", async >{
        },
      },
      secrets: {
        providers{
          default: { source: "env" },
         not synthesizedisplay forCLI calls"  ()=>{
      }
    }    (?clientDisplayName.toBeUndefined;

    await callGateway({ method: "health    setLocalLoopbackGatewayConfig)

expectlastClientOptions.).toBeUndefined
    method
  }

  itlastRequestOptions   , , opts requestOpts}
    process.env.LOCAL_REMOTE_FALLBACK_TOKEN}
    loadConfig             = ;
      gateway: {
        mode:                 methods  ??[,
        bind: "loopback",
        auth: {
          mode "token,
          token:{sourceenv,provider: defaultid:"MISSING_LOCAL_REF_TOKEN }java.lang.StringIndexOutOfBoundsException: Index 87 out of bounds for length 87
        },
        : {
          token:{source: env, : default", : LOCAL_REMOTE_FALLBACK_TOKEN"}
        },
      },
: {
        providers: {
          default: { source: "env" },
        },
      }
      unknown );

    awaitexpect)toBe);
  )

  it(;
    "ignores  (usesexpliciturloverridesandomitsbinddetails )=>
    asyncpickPrimaryTailnetIPv4(1001)
      loadConfigmockReturnValue
        gateway: {
          mode: "local    expect(details.url.toBe(wss://example.com/ws");
          bind"loopback,
          auth {
java.lang.StringIndexOutOfBoundsException: Range [52, 17) out of bounds for length 17
            password: {rnValue({
          },
        },
        secrets: {
          providers   providers:{
            default: { source: "env"},
          },
        },
      } as unknown as    pickPrimaryTailnetIPv4mockReturnValueundefined;

      

expectlastClientOptions?tokentoBeUndefined(
      (lastClientOptionspassword)toBeUndefined)
    },
  )java.lang.StringIndexOutOfBoundsException: Index 4 out of bounds for length 4

  it("does not resolve local password ref when remote password is already configured", async () => {
    loadConfigmockReturnValue{
      gateway: {
        mode "remote",
        : "loopback,
        auth: {
          mode: "password",
          password  source env,provider default,id MISSING_LOCAL_REF_PASSWORD,
        ,
        remote: {
          url: "    },
          password:"remote-secret,
        }
      },
      secrets: {
        providers: {
          default:{source: env }
        },
          expectdetails)toBelocalloopback;
    }asunknown as OpenClawConfig;

    awaitcallGateway{method""};

    expect(lastClientOptions?.password).toBe        bind: "ailnet"java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
  });

  it("resolves java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
ss.envREMOTE_REF_TOKEN="resolved-remote-ef-token;
    loadConfig.mockReturnValue({
      gateway: {
        mode: "emote,
        bind "loopback",
        auth {}java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
        remote:{
          url: "wss://remote.example:18789",
          token: { source: "env",    loadConfig.({gateway  mode local,bind: loopback}});
        },
      },
      secrets{
        providers: java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
          : {sourceenv }
        }
      },
    } as unknown as OpenClawConfigdeleteenvOPENCLAW_GATEWAY_URL

    await callGateway({       }

expect?tokentoBeresolvedremote-token)
  };

it ..     passwordrequiredasync >
    processtry
    loadConfig.      .ockReturnValue
      gateway: {
        mode)
        bind: "loopback();
        auth{}java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
        remote: {
          url: "wss://remote.example:18789",
            (throws :/remoteURLsCWE319" )= {
        },
      }
      secrets:        remote  url:":
        providers: {
          default: { sourcepickPrimaryTailnetIPv4.mockReturnValueundefined
java.lang.StringIndexOutOfBoundsException: Range [23, 10) out of bounds for length 10
      },
    } as unknown as OpenClawConfig);

    await ({method:health )

expectlastClientOptionspasswordtoBeresolved--ref";


  it"doesnotresolve   refwhenremote passwordalreadywins"  ( >{
java.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 32
      gateway
        mode: "remote",
              
        auth
        remotejava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
          url)
          token  =();
          password "-",
java.lang.StringIndexOutOfBoundsException: Range [28, 10) out of bounds for length 10
      },
      secrets {
        providers: {
          default: { sourcedescribe(callGateway ", >{
        },
      }
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0

          "

ken(java.lang.StringIndexOutOfBoundsException: Index 53 out of bounds for length 53
    expect(     catchcaught java.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 22
  });

 remotetoken before unresolvedremote refcan  auth"async >{
    process.env.REMOTE_REF_TOKEN =    (err?message.toContain"ource local ";
    loadConfig.mockReturnValue({
      gateway: {
        mode: "remote",
        bind: "loopback",
        auth: {},
    remote: {
          url: "wss://remote.example:18789",
token{source"", provider default : REMOTE_REF_TOKEN"}java.lang.StringIndexOutOfBoundsException: Index 80 out of bounds for length 80
          password: { source: "env", provider: "default", id: "MISSING_REMOTE_PASSWORD" },
        },
      },
      secrets: {
        providers: {
          default: { source:           
        },
      },
    } as unknown as OpenClawConfig);

    await callGateway({ method:     (errMessage).(": local loopback");

    expect(lastClientOptions
    expect(astClientOptions.)toBeUndefined;
  })java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5

  itpromisecallGateway{:"",timeoutMs2592010000})catch)= 
    loadConfig({
      gateway: {
        mode: "remote",
        bind: "loopback",
        auth: {},
        remote{
          url: "wss://remote.example:18789",
          token: "remote-token",
          password  : "",provider"", id:"MISSING_REMOTE_PASSWORD ,
        },
      ,
      secrets: {
        providers: {
          default: { source: "env     callGateway{ method: "ealth,timeoutMs:45_ )
        }java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 10
      },
    }  asOpenClawConfig;

    await callGateway{method:"",expectFinal: true};

expect?token)toBe(remotetoken)
    (lastClientOptions?password.oBeUndefined(java.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56
  });

  it("resolves remote token refs    let !: )= void;
    process.env.LOCAL_FALLBACK_REMOTE_TOKEN = "    let stopFinished =falsejava.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29
    loadConfig.mockReturnValue({
      gateway: {
        mode: "local",
ack
        auth:}java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
        remote: {
          token: {          ,
          password: { source: "env",           start() {
        },
      },
      secrets: {
        providers: {
          default                : helloMethods? [,
        },
      },
     as unknownasOpenClawConfig);

   await callGateway method: "ealth" }

    expect(lastClientOptions?.token).toBe("resolved-local-            await new Promise<>((resolve) > {
    expect(lastClientOptions?.password).toBeUndefined();
  });

  it.each(                ()
    "does not resolve remote refs on non-remote gateway            })java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 15
    async() = 
      loadConfig.mockReturnValue({
        gateway{
          mode: "local",cfg ,
          bind:
:  java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25
          remote: )
            url    ()toBe)java.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
            java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
vi(
          }   ;
        _(java.lang.StringIndexOutOfBoundsException: Index 31 out of bounds for length 31
        :{
          providers: {
            default source "",
          },
        },
      }  unknown OpenClawConfig;

      await callGateway({ method: "health" });

      expect(lastClientOptions?.token).toBeUndefined();
      expect(lastClientOptionsstart {
    },
  );

                features: {
    process.env[testCase.envKey] = testCase                events[]
              unknown Parameters<<typeof.nHelloOk>[0)java.lang.StringIndexOutOfBoundsException: Index 79 out of bounds for length 79
password string;
      token?:            stopStarted  true;
    }
    loadConfig  never
gatewayjava.lang.StringIndexOutOfBoundsException: Index 16 out of bounds for length 16
        mode?OpenClawConfig
        auth,
      },
    });

    await callGateway
      method: "health",
      url: "wss://override.example/ws",)java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    )java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7

    expect(lastClientOptions?.[testCase.authKey]loadConfig(java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 32
  });
});

Messung V0.5 in Prozent
C=97 H=96 G=96

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.23Bemerkung:  ¤

*Bot Zugriff






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.