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

Quelle  bundled-runtime-deps.test.ts

  Sprache: JAVA
 

Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import { spawnSync } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
  __testing as bundledRuntimeDepsTesting,
  createBundledRuntimeDependencyAliasMap,
  createBundledRuntimeDepsInstallArgs,
  createBundledRuntimeDepsInstallEnv,
  ensureBundledPluginRuntimeDeps,
  installBundledRuntimeDeps,
  isWritableDirectory,
  resolveBundledRuntimeDependencyInstallRoot,
  resolveBundledRuntimeDepsNpmRunner,
  type BundledRuntimeDepsInstallParams,
} from "./bundled-runtime-deps.js";

vi.mock("node:child_process", () => ({
  spawnSync: vi.fn(),
}));

const spawnSyncMock = vi.mocked(spawnSync);
const tempDirs: string[] = [];

function makeTempDir(): string {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-runtime-deps-test-"));
  tempDirs.push(dir);
  return dir;
}

afterEach(() => {
  vi.restoreAllMocks();
  spawnSyncMock.mockReset();
  for (const dir of tempDirs.splice(0)) {
    fs.rmSync(dir, { recursive: true, force: true });
  }
});

describe("resolveBundledRuntimeDepsNpmRunner", () => {
  it("uses npm_execpath through node on Windows when available", () => {
    const runner = resolveBundledRuntimeDepsNpmRunner({
      env: { npm_execpath: "C:\\node\\node_modules\\npm\\bin\\npm-cli.js" },
      execPath: "C:\\Program Files\\nodejs\\node.exe",
      existsSync: (candidate) => candidate === "C:\\node\\node_modules\\npm\\bin\\npm-cli.js",
      npmArgs: ["install", "acpx@0.5.3"],
      platform: "win32",
    });

    expect(runner).toEqual({
      command: "C:\\Program Files\\nodejs\\node.exe",
      args: ["C:\\node\\node_modules\\npm\\bin\\npm-cli.js", "install", "acpx@0.5.3"],
    });
  });

  it("uses package-manager-neutral install args with npm config env", () => {
    expect(createBundledRuntimeDepsInstallArgs(["acpx@0.5.3"])).toEqual([
      "install",
      "--ignore-scripts",
      "acpx@0.5.3",
    ]);
    expect(
      createBundledRuntimeDepsInstallEnv(
        {
          PATH: "/usr/bin:/bin",
          NPM_CONFIG_CACHE: "/Users/alice/.npm-uppercase",
          npm_config_cache: "/Users/alice/.npm",
          npm_config_global: "true",
          npm_config_prefix: "/opt/homebrew",
        },
        { cacheDir: "/opt/openclaw/runtime-cache" },
      ),
    ).toEqual({
      PATH: "/usr/bin:/bin",
      npm_config_cache: "/opt/openclaw/runtime-cache",
      npm_config_legacy_peer_deps: "true",
      npm_config_package_lock: "false",
      npm_config_save: "false",
    });
  });

  it("uses the Node-adjacent npm CLI on Windows", () => {
    const execPath = "C:\\Program Files\\nodejs\\node.exe";
    const npmCliPath = path.win32.resolve(
      path.win32.dirname(execPath),
      "node_modules/npm/bin/npm-cli.js",
    );

    const runner = resolveBundledRuntimeDepsNpmRunner({
      env: {},
      execPath,
      existsSync: (candidate) => candidate === npmCliPath,
      npmArgs: ["install", "acpx@0.5.3"],
      platform: "win32",
    });

    expect(runner).toEqual({
      command: execPath,
      args: [npmCliPath, "install", "acpx@0.5.3"],
    });
  });

  it("ignores pnpm npm_execpath and falls back to npm", () => {
    const execPath = "/opt/node/bin/node";
    const npmCliPath = "/opt/node/lib/node_modules/npm/bin/npm-cli.js";
    const runner = resolveBundledRuntimeDepsNpmRunner({
      env: {
        npm_execpath: "/home/runner/setup-pnpm/node_modules/.bin/pnpm.cjs",
      },
      execPath,
      existsSync: (candidate) => candidate === npmCliPath,
      npmArgs: ["install", "acpx@0.5.3"],
      platform: "linux",
    });

    expect(runner).toEqual({
      command: execPath,
      args: [npmCliPath, "install", "acpx@0.5.3"],
    });
  });

  it("refuses Windows shell fallback when no safe npm executable is available", () => {
    expect(() =>
      resolveBundledRuntimeDepsNpmRunner({
        env: {},
        execPath: "C:\\Program Files\\nodejs\\node.exe",
        existsSync: () => false,
        npmArgs: ["install"],
        platform: "win32",
      }),
    ).toThrow("Unable to resolve a safe npm executable on Windows");
  });

  it("prefixes PATH with the active Node directory on POSIX", () => {
    const runner = resolveBundledRuntimeDepsNpmRunner({
      env: {
        PATH: "/usr/bin:/bin",
      },
      execPath: "/opt/node/bin/node",
      existsSync: () => false,
      npmArgs: ["install", "acpx@0.5.3"],
      platform: "linux",
    });

    expect(runner).toEqual({
      command: "npm",
      args: ["install", "acpx@0.5.3"],
      env: {
        PATH: `/opt/node/bin${path.delimiter}/usr/bin:/bin`,
      },
    });
  });
});

describe("installBundledRuntimeDeps", () => {
  it("uses a real write probe for runtime dependency roots", () => {
    const accessSpy = vi.spyOn(fs, "accessSync").mockImplementation(() => undefined);
    const mkdirSpy = vi.spyOn(fs, "mkdtempSync").mockImplementation(() => {
      const error = new Error("read-only file system") as NodeJS.ErrnoException;
      error.code = "EROFS";
      throw error;
    });

    expect(isWritableDirectory("/usr/lib/node_modules/openclaw")).toBe(false);
    expect(accessSpy).not.toHaveBeenCalled();
    expect(mkdirSpy).toHaveBeenCalledWith(
      path.join("/usr/lib/node_modules/openclaw", ".openclaw-write-probe-"),
    );
  });

  it("uses the npm cmd shim on Windows", () => {
    vi.spyOn(process, "platform", "get").mockReturnValue("win32");
    vi.spyOn(fs, "existsSync").mockImplementation(
      (candidate) => candidate === "C:\\node\\node_modules\\npm\\bin\\npm-cli.js",
    );
    spawnSyncMock.mockReturnValue({
      pid: 123,
      output: [],
      stdout: "",
      stderr: "",
      signal: null,
      status: 0,
    });

    installBundledRuntimeDeps({
      installRoot: "C:\\openclaw",
      missingSpecs: ["acpx@0.5.3"],
      env: {
        npm_config_prefix: "C:\\prefix",
        PATH: "C:\\node",
        npm_execpath: "C:\\node\\node_modules\\npm\\bin\\npm-cli.js",
      },
    });

    expect(spawnSyncMock).toHaveBeenCalledWith(
      expect.any(String),
      ["C:\\node\\node_modules\\npm\\bin\\npm-cli.js", "install", "--ignore-scripts", "acpx@0.5.3"],
      expect.objectContaining({
        cwd: "C:\\openclaw",
        env: expect.objectContaining({
          npm_config_legacy_peer_deps: "true",
          npm_config_package_lock: "false",
          npm_config_save: "false",
        }),
      }),
    );
    expect(spawnSyncMock).toHaveBeenCalledWith(
      expect.any(String),
      expect.any(Array),
      expect.objectContaining({
        env: expect.not.objectContaining({
          npm_config_prefix: expect.any(String),
        }),
      }),
    );
  });

  it("uses an isolated execution root and copies node_modules back when requested", () => {
    const installRoot = makeTempDir();
    const installExecutionRoot = makeTempDir();
    spawnSyncMock.mockImplementation((_command, _args, options) => {
      const cwd = String(options?.cwd ?? "");
      fs.mkdirSync(path.join(cwd, "node_modules", "tokenjuice"), { recursive: true });
      fs.writeFileSync(
        path.join(cwd, "node_modules", "tokenjuice", "package.json"),
        JSON.stringify({ name: "tokenjuice", version: "0.6.1" }),
      );
      return {
        pid: 123,
        output: [],
        stdout: "",
        stderr: "",
        signal: null,
        status: 0,
      };
    });

    installBundledRuntimeDeps({
      installRoot,
      installExecutionRoot,
      missingSpecs: ["tokenjuice@0.6.1"],
      env: {},
    });

    expect(
      JSON.parse(fs.readFileSync(path.join(installExecutionRoot, "package.json"), "utf8")),
    ).toEqual({
      name: "openclaw-runtime-deps-install",
      private: true,
    });
    expect(
      JSON.parse(
        fs.readFileSync(
          path.join(installRoot, "node_modules", "tokenjuice", "package.json"),
          "utf8",
        ),
      ),
    ).toEqual({
      name: "tokenjuice",
      version: "0.6.1",
    });
    expect(spawnSyncMock).toHaveBeenCalledWith(
      expect.any(String),
      expect.any(Array),
      expect.objectContaining({
        cwd: installExecutionRoot,
      }),
    );
  });

  it("uses an OpenClaw-owned npm cache for runtime dependency installs", () => {
    const installRoot = makeTempDir();
    spawnSyncMock.mockReturnValue({
      pid: 123,
      output: [],
      stdout: "",
      stderr: "",
      signal: null,
      status: 0,
    });

    installBundledRuntimeDeps({
      installRoot,
      missingSpecs: ["tokenjuice@0.6.1"],
      env: {
        HOME: "/Users/alice",
        NPM_CONFIG_CACHE: "/Users/alice/.npm-uppercase",
        npm_config_cache: "/Users/alice/.npm",
      },
    });

    expect(spawnSyncMock).toHaveBeenCalledWith(
      expect.any(String),
      expect.any(Array),
      expect.objectContaining({
        cwd: installRoot,
        env: expect.objectContaining({
          HOME: "/Users/alice",
          npm_config_cache: path.join(installRoot, ".openclaw-npm-cache"),
        }),
      }),
    );
    expect(spawnSyncMock).toHaveBeenCalledWith(
      expect.any(String),
      expect.any(Array),
      expect.objectContaining({
        env: expect.not.objectContaining({
          NPM_CONFIG_CACHE: expect.any(String),
        }),
      }),
    );
  });

  it("cleans an owned isolated execution root after copying node_modules back", () => {
    const installRoot = makeTempDir();
    const installExecutionRoot = path.join(installRoot, ".openclaw-install-stage");
    spawnSyncMock.mockImplementation((_command, _args, options) => {
      const cwd = String(options?.cwd ?? "");
      fs.mkdirSync(path.join(cwd, "node_modules", "tokenjuice"), { recursive: true });
      fs.writeFileSync(
        path.join(cwd, "node_modules", "tokenjuice", "package.json"),
        JSON.stringify({ name: "tokenjuice", version: "0.6.1" }),
      );
      return {
        pid: 123,
        output: [],
        stdout: "",
        stderr: "",
        signal: null,
        status: 0,
      };
    });

    installBundledRuntimeDeps({
      installRoot,
      installExecutionRoot,
      missingSpecs: ["tokenjuice@0.6.1"],
      env: {},
    });

    expect(fs.existsSync(installExecutionRoot)).toBe(false);
    expect(
      JSON.parse(
        fs.readFileSync(
          path.join(installRoot, "node_modules", "tokenjuice", "package.json"),
          "utf8",
        ),
      ),
    ).toEqual({
      name: "tokenjuice",
      version: "0.6.1",
    });
  });

  it("does not fail an isolated runtime deps install when temp cleanup races", () => {
    const installRoot = makeTempDir();
    const installExecutionRoot = makeTempDir();
    const realRmSync = fs.rmSync.bind(fs);
    let blockedCleanup = false;
    vi.spyOn(fs, "rmSync").mockImplementation((target, options) => {
      if (
        !blockedCleanup &&
        path.basename(String(target)).startsWith(".openclaw-runtime-deps-copy-")
      ) {
        blockedCleanup = true;
        const error = new Error("Directory not empty") as NodeJS.ErrnoException;
        error.code = "ENOTEMPTY";
        throw error;
      }
      return realRmSync(target, options);
    });
    spawnSyncMock.mockImplementation((_command, _args, options) => {
      const cwd = String(options?.cwd ?? "");
      fs.mkdirSync(path.join(cwd, "node_modules", "tokenjuice"), { recursive: true });
      fs.writeFileSync(
        path.join(cwd, "node_modules", "tokenjuice", "package.json"),
        JSON.stringify({ name: "tokenjuice", version: "0.6.1" }),
      );
      return {
        pid: 123,
        output: [],
        stdout: "",
        stderr: "",
        signal: null,
        status: 0,
      };
    });

    expect(() =>
      installBundledRuntimeDeps({
        installRoot,
        installExecutionRoot,
        missingSpecs: ["tokenjuice@0.6.1"],
        env: {},
      }),
    ).not.toThrow();

    expect(blockedCleanup).toBe(true);
    expect(
      JSON.parse(
        fs.readFileSync(
          path.join(installRoot, "node_modules", "tokenjuice", "package.json"),
          "utf8",
        ),
      ),
    ).toEqual({
      name: "tokenjuice",
      version: "0.6.1",
    });
  });

  it("rejects invalid install specs before spawning npm", () => {
    expect(() =>
      createBundledRuntimeDepsInstallArgs(["tokenjuice@https://evil.example/t.tgz"]),
    ).toThrow("Unsupported bundled runtime dependency spec for tokenjuice");
  });

  it("includes spawn errors in install failures", () => {
    spawnSyncMock.mockReturnValue({
      pid: 0,
      output: [],
      stdout: "",
      stderr: "",
      signal: null,
      status: null,
      error: new Error("spawn npm ENOENT"),
    });

    expect(() =>
      installBundledRuntimeDeps({
        installRoot: "/tmp/openclaw",
        missingSpecs: ["browser-runtime@1.0.0"],
        env: {},
      }),
    ).toThrow("spawn npm ENOENT");
  });
});

describe("ensureBundledPluginRuntimeDeps", () => {
  it("installs plugin-local runtime deps when one is missing", () => {
    const packageRoot = makeTempDir();
    const extensionsRoot = path.join(packageRoot, "dist", "extensions");
    const pluginRoot = path.join(extensionsRoot, "bedrock");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          "already-present": "1.0.0",
          missing: "2.0.0",
        },
      }),
    );
    fs.mkdirSync(path.join(pluginRoot, "node_modules", "already-present"), {
      recursive: true,
    });
    fs.writeFileSync(
      path.join(pluginRoot, "node_modules", "already-present", "package.json"),
      JSON.stringify({ name: "already-present", version: "1.0.0" }),
    );

    const calls: Array<{
      installRoot: string;
      installExecutionRoot?: string;
      missingSpecs: string[];
      installSpecs?: string[];
    }> = [];

    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "bedrock",
      pluginRoot,
      retainSpecs: ["previous@3.0.0"],
    });

    expect(result).toEqual({
      installedSpecs: ["already-present@1.0.0", "missing@2.0.0"],
      retainSpecs: ["already-present@1.0.0", "missing@2.0.0", "previous@3.0.0"],
    });
    const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env: {} });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["already-present@1.0.0", "missing@2.0.0"],
        installSpecs: ["already-present@1.0.0", "missing@2.0.0", "previous@3.0.0"],
      },
    ]);
    expect(installRoot).not.toBe(pluginRoot);
  });

  it("skips workspace-only runtime deps before npm install", () => {
    const packageRoot = makeTempDir();
    const extensionsRoot = path.join(packageRoot, "dist", "extensions");
    const pluginRoot = path.join(extensionsRoot, "qa-channel");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          "@openclaw/plugin-sdk": "workspace:*",
          "external-runtime": "^1.2.3",
          openclaw: "workspace:*",
        },
      }),
    );

    const calls: BundledRuntimeDepsInstallParams[] = [];
    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "qa-channel",
      pluginRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["external-runtime@^1.2.3"],
      retainSpecs: ["external-runtime@^1.2.3"],
    });
    const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env: {} });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["external-runtime@^1.2.3"],
        installSpecs: ["external-runtime@^1.2.3"],
      },
    ]);
    expect(installRoot).not.toBe(pluginRoot);
  });

  it("uses external staging when a packaged plugin declares workspace:* deps", () => {
    // Regression guard for packaged/Docker bundled plugins whose `package.json`
    // still lists `"@openclaw/plugin-sdk": "workspace:*"` (and similar) alongside
    // concrete runtime deps. Without a distinct execution root, `npm install`
    // would resolve the plugin's own cwd manifest and fail with
    // EUNSUPPORTEDPROTOCOL on the `workspace:` protocol.
    const packageRoot = makeTempDir();
    const extensionsRoot = path.join(packageRoot, "dist", "extensions");
    const pluginRoot = path.join(extensionsRoot, "anthropic");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          "@openclaw/plugin-sdk": "workspace:*",
          "@anthropic-ai/sdk": "^0.50.0",
        },
      }),
    );

    const calls: BundledRuntimeDepsInstallParams[] = [];
    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "anthropic",
      pluginRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["@anthropic-ai/sdk@^0.50.0"],
      retainSpecs: ["@anthropic-ai/sdk@^0.50.0"],
    });
    const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env: {} });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["@anthropic-ai/sdk@^0.50.0"],
        installSpecs: ["@anthropic-ai/sdk@^0.50.0"],
      },
    ]);
    expect(installRoot).not.toBe(pluginRoot);
  });

  it("installs runtime deps into an external stage dir and exposes loader aliases", () => {
    const packageRoot = makeTempDir();
    const stageDir = makeTempDir();
    fs.writeFileSync(
      path.join(packageRoot, "package.json"),
      JSON.stringify({ name: "openclaw", version: "2026.4.22" }),
    );
    const pluginRoot = path.join(packageRoot, "dist", "extensions", "slack");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          "@slack/web-api": "7.15.1",
        },
      }),
    );

    const env = { OPENCLAW_PLUGIN_STAGE_DIR: stageDir };
    const calls: BundledRuntimeDepsInstallParams[] = [];
    const result = ensureBundledPluginRuntimeDeps({
      env,
      installDeps: (params) => {
        calls.push(params);
        fs.mkdirSync(path.join(params.installRoot, "node_modules", "@slack", "web-api"), {
          recursive: true,
        });
        fs.writeFileSync(
          path.join(params.installRoot, "node_modules", "@slack", "web-api", "package.json"),
          JSON.stringify({ name: "@slack/web-api", version: "7.15.1" }),
        );
      },
      pluginId: "slack",
      pluginRoot,
    });

    const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env });
    expect(result).toEqual({
      installedSpecs: ["@slack/web-api@7.15.1"],
      retainSpecs: ["@slack/web-api@7.15.1"],
    });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["@slack/web-api@7.15.1"],
        installSpecs: ["@slack/web-api@7.15.1"],
      },
    ]);
    expect(installRoot).toContain(stageDir);
    expect(installRoot).not.toBe(pluginRoot);
    expect(createBundledRuntimeDependencyAliasMap({ pluginRoot, installRoot })).toEqual({
      "@slack/web-api": path.join(installRoot, "node_modules", "@slack", "web-api"),
    });

    const second = ensureBundledPluginRuntimeDeps({
      env,
      installDeps: () => {
        throw new Error("external staged deps should not reinstall");
      },
      pluginId: "slack",
      pluginRoot,
    });
    expect(second).toEqual({ installedSpecs: [], retainSpecs: [] });
  });

  it("retains external staged deps across separate loader passes", () => {
    const packageRoot = makeTempDir();
    const stageDir = makeTempDir();
    fs.writeFileSync(
      path.join(packageRoot, "package.json"),
      JSON.stringify({ name: "openclaw", version: "2026.4.22" }),
    );
    const alphaRoot = path.join(packageRoot, "dist", "extensions", "alpha");
    const betaRoot = path.join(packageRoot, "dist", "extensions", "beta");
    fs.mkdirSync(alphaRoot, { recursive: true });
    fs.mkdirSync(betaRoot, { recursive: true });
    fs.writeFileSync(
      path.join(alphaRoot, "package.json"),
      JSON.stringify({ dependencies: { "alpha-runtime": "1.0.0" } }),
    );
    fs.writeFileSync(
      path.join(betaRoot, "package.json"),
      JSON.stringify({ dependencies: { "beta-runtime": "2.0.0" } }),
    );

    const env = { OPENCLAW_PLUGIN_STAGE_DIR: stageDir };
    const calls: BundledRuntimeDepsInstallParams[] = [];
    const installDeps = (params: BundledRuntimeDepsInstallParams) => {
      calls.push(params);
      for (const spec of params.installSpecs ?? params.missingSpecs) {
        const name = spec.slice(0, spec.lastIndexOf("@"));
        fs.mkdirSync(path.join(params.installRoot, "node_modules", name), { recursive: true });
        fs.writeFileSync(
          path.join(params.installRoot, "node_modules", name, "package.json"),
          JSON.stringify({ name, version: spec.slice(spec.lastIndexOf("@") + 1) }),
        );
      }
    };

    ensureBundledPluginRuntimeDeps({
      env,
      installDeps,
      pluginId: "alpha",
      pluginRoot: alphaRoot,
    });
    ensureBundledPluginRuntimeDeps({
      env,
      installDeps,
      pluginId: "beta",
      pluginRoot: betaRoot,
    });

    const installRoot = resolveBundledRuntimeDependencyInstallRoot(alphaRoot, { env });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["alpha-runtime@1.0.0"],
        installSpecs: ["alpha-runtime@1.0.0"],
      },
      {
        installRoot,
        missingSpecs: ["beta-runtime@2.0.0"],
        installSpecs: ["alpha-runtime@1.0.0", "beta-runtime@2.0.0"],
      },
    ]);
  });

  it("retains existing staged deps without a retained manifest before shared installs", () => {
    const packageRoot = makeTempDir();
    const stageDir = makeTempDir();
    fs.writeFileSync(
      path.join(packageRoot, "package.json"),
      JSON.stringify({ name: "openclaw", version: "2026.4.22" }),
    );
    const alphaRoot = path.join(packageRoot, "dist", "extensions", "alpha");
    const betaRoot = path.join(packageRoot, "dist", "extensions", "beta");
    fs.mkdirSync(alphaRoot, { recursive: true });
    fs.mkdirSync(betaRoot, { recursive: true });
    fs.writeFileSync(
      path.join(alphaRoot, "package.json"),
      JSON.stringify({ dependencies: { "alpha-runtime": "1.0.0" } }),
    );
    fs.writeFileSync(
      path.join(betaRoot, "package.json"),
      JSON.stringify({ dependencies: { "beta-runtime": "2.0.0" } }),
    );

    const env = { OPENCLAW_PLUGIN_STAGE_DIR: stageDir };
    const installRoot = resolveBundledRuntimeDependencyInstallRoot(alphaRoot, { env });
    fs.mkdirSync(path.join(installRoot, "node_modules", "alpha-runtime"), { recursive: true });
    fs.writeFileSync(
      path.join(installRoot, "node_modules", "alpha-runtime", "package.json"),
      JSON.stringify({ name: "alpha-runtime", version: "1.0.0" }),
    );
    expect(fs.existsSync(path.join(installRoot, ".openclaw-runtime-deps.json"))).toBe(false);

    const calls: BundledRuntimeDepsInstallParams[] = [];
    const result = ensureBundledPluginRuntimeDeps({
      env,
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "beta",
      pluginRoot: betaRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["beta-runtime@2.0.0"],
      retainSpecs: ["alpha-runtime@1.0.0", "beta-runtime@2.0.0"],
    });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["beta-runtime@2.0.0"],
        installSpecs: ["alpha-runtime@1.0.0", "beta-runtime@2.0.0"],
      },
    ]);
  });

  it("does not expire active runtime-deps install locks by age alone", () => {
    expect(
      bundledRuntimeDepsTesting.shouldRemoveRuntimeDepsLock(
        { pid: 123, createdAtMs: 0 },
        Number.MAX_SAFE_INTEGER,
        () => true,
      ),
    ).toBe(false);
  });

  it("removes stale runtime-deps install locks before repairing deps", () => {
    const packageRoot = makeTempDir();
    const pluginRoot = path.join(packageRoot, "dist", "extensions", "openai");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          "@mariozechner/pi-ai": "0.70.2",
        },
      }),
    );
    const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env: {} });
    const lockDir = path.join(installRoot, ".openclaw-runtime-deps.lock");
    fs.mkdirSync(lockDir, { recursive: true });
    fs.writeFileSync(path.join(lockDir, "owner.json"), JSON.stringify({ pid: 0, createdAtMs: 0 }));

    const calls: BundledRuntimeDepsInstallParams[] = [];
    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: (params) => {
        calls.push(params);
        fs.mkdirSync(path.join(params.installRoot, "node_modules", "@mariozechner", "pi-ai"), {
          recursive: true,
        });
        fs.writeFileSync(
          path.join(params.installRoot, "node_modules", "@mariozechner", "pi-ai", "package.json"),
          JSON.stringify({ name: "@mariozechner/pi-ai", version: "0.70.2" }),
        );
      },
      pluginId: "openai",
      pluginRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["@mariozechner/pi-ai@0.70.2"],
      retainSpecs: ["@mariozechner/pi-ai@0.70.2"],
    });
    expect(calls).toHaveLength(1);
    expect(fs.existsSync(lockDir)).toBe(false);
  });

  it("does not install when runtime deps are only workspace links", () => {
    const packageRoot = makeTempDir();
    const extensionsRoot = path.join(packageRoot, "dist", "extensions");
    const pluginRoot = path.join(extensionsRoot, "qa-channel");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          "@openclaw/plugin-sdk": "workspace:*",
          openclaw: "workspace:*",
        },
      }),
    );

    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: () => {
        throw new Error("workspace-only runtime deps should not install");
      },
      pluginId: "qa-channel",
      pluginRoot,
    });

    expect(result).toEqual({ installedSpecs: [], retainSpecs: [] });
  });

  it("installs missing runtime deps for source-checkout bundled plugins", () => {
    const packageRoot = makeTempDir();
    const stageDir = makeTempDir();
    fs.mkdirSync(path.join(packageRoot, ".git"), { recursive: true });
    fs.mkdirSync(path.join(packageRoot, "src"), { recursive: true });
    const pluginRoot = path.join(packageRoot, "extensions", "tokenjuice");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          tokenjuice: "0.6.1",
        },
      }),
    );

    const calls: BundledRuntimeDepsInstallParams[] = [];
    const result = ensureBundledPluginRuntimeDeps({
      env: { OPENCLAW_PLUGIN_STAGE_DIR: stageDir },
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "tokenjuice",
      pluginRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["tokenjuice@0.6.1"],
      retainSpecs: ["tokenjuice@0.6.1"],
    });
    const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, {
      env: { OPENCLAW_PLUGIN_STAGE_DIR: stageDir },
    });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["tokenjuice@0.6.1"],
        installSpecs: ["tokenjuice@0.6.1"],
      },
    ]);
    expect(installRoot).toContain(stageDir);
    expect(installRoot).not.toBe(pluginRoot);
    expect(
      JSON.parse(fs.readFileSync(path.join(installRoot, ".openclaw-runtime-deps.json"), "utf8")),
    ).toEqual({ specs: ["tokenjuice@0.6.1"] });
  });

  it("keeps source-checkout bundled runtime deps in the plugin root without manifest churn", () => {
    const packageRoot = makeTempDir();
    fs.mkdirSync(path.join(packageRoot, ".git"), { recursive: true });
    fs.mkdirSync(path.join(packageRoot, "src"), { recursive: true });
    const pluginRoot = path.join(packageRoot, "extensions", "tokenjuice");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, ".openclaw-runtime-deps.json"),
      JSON.stringify({ specs: ["stale@9.9.9"] }),
    );
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          tokenjuice: "0.6.1",
        },
      }),
    );

    const calls: BundledRuntimeDepsInstallParams[] = [];
    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "tokenjuice",
      pluginRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["tokenjuice@0.6.1"],
      retainSpecs: ["tokenjuice@0.6.1"],
    });
    expect(calls).toEqual([
      {
        installRoot: pluginRoot,
        installExecutionRoot: expect.stringContaining(
          path.join(".local", "bundled-plugin-runtime-deps"),
        ),
        missingSpecs: ["tokenjuice@0.6.1"],
        installSpecs: ["tokenjuice@0.6.1"],
      },
    ]);
    expect(resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env: {} })).toBe(pluginRoot);
    expect(fs.existsSync(path.join(pluginRoot, ".openclaw-runtime-deps.json"))).toBe(false);
  });

  it("removes stale source-checkout manifests even when runtime deps are present", () => {
    const packageRoot = makeTempDir();
    fs.mkdirSync(path.join(packageRoot, ".git"), { recursive: true });
    fs.mkdirSync(path.join(packageRoot, "src"), { recursive: true });
    const pluginRoot = path.join(packageRoot, "extensions", "tokenjuice");
    fs.mkdirSync(path.join(pluginRoot, "node_modules", "tokenjuice"), { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          tokenjuice: "0.6.1",
        },
      }),
    );
    fs.writeFileSync(
      path.join(pluginRoot, "node_modules", "tokenjuice", "package.json"),
      JSON.stringify({ name: "tokenjuice", version: "0.6.1" }),
    );
    fs.writeFileSync(
      path.join(pluginRoot, ".openclaw-runtime-deps.json"),
      JSON.stringify({ specs: ["stale@9.9.9"] }),
    );

    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: () => {
        throw new Error("present source-checkout runtime deps should not reinstall");
      },
      pluginId: "tokenjuice",
      pluginRoot,
    });

    expect(result).toEqual({ installedSpecs: [], retainSpecs: [] });
    expect(fs.existsSync(path.join(pluginRoot, ".openclaw-runtime-deps.json"))).toBe(false);
  });

  it("treats Docker build source trees without .git as source checkouts", () => {
    const packageRoot = makeTempDir();
    fs.mkdirSync(path.join(packageRoot, "src"), { recursive: true });
    fs.writeFileSync(path.join(packageRoot, "pnpm-workspace.yaml"), "packages:\n  - .\n");
    const pluginRoot = path.join(packageRoot, "extensions", "acpx");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          acpx: "0.5.3",
        },
        devDependencies: {
          "@openclaw/plugin-sdk": "workspace:*",
        },
      }),
    );

    const calls: BundledRuntimeDepsInstallParams[] = [];
    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "acpx",
      pluginRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["acpx@0.5.3"],
      retainSpecs: ["acpx@0.5.3"],
    });
    expect(calls).toEqual([
      {
        installRoot: pluginRoot,
        installExecutionRoot: expect.stringContaining(
          path.join(".local", "bundled-plugin-runtime-deps"),
        ),
        missingSpecs: ["acpx@0.5.3"],
        installSpecs: ["acpx@0.5.3"],
      },
    ]);
  });

  it("does not trust package-root runtime deps for source-checkout bundled plugins", () => {
    const packageRoot = makeTempDir();
    const stageDir = makeTempDir();
    fs.mkdirSync(path.join(packageRoot, ".git"), { recursive: true });
    fs.mkdirSync(path.join(packageRoot, "src"), { recursive: true });
    const pluginRoot = path.join(packageRoot, "extensions", "tokenjuice");
    fs.mkdirSync(path.join(packageRoot, "node_modules", "tokenjuice"), {
      recursive: true,
    });
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          tokenjuice: "0.6.1",
        },
      }),
    );
    fs.writeFileSync(
      path.join(packageRoot, "node_modules", "tokenjuice", "package.json"),
      JSON.stringify({ name: "tokenjuice", version: "0.6.1" }),
    );
    const calls: BundledRuntimeDepsInstallParams[] = [];

    const result = ensureBundledPluginRuntimeDeps({
      env: { OPENCLAW_PLUGIN_STAGE_DIR: stageDir },
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "tokenjuice",
      pluginRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["tokenjuice@0.6.1"],
      retainSpecs: ["tokenjuice@0.6.1"],
    });
    expect(calls).toEqual([
      {
        installRoot: resolveBundledRuntimeDependencyInstallRoot(pluginRoot, {
          env: { OPENCLAW_PLUGIN_STAGE_DIR: stageDir },
        }),
        missingSpecs: ["tokenjuice@0.6.1"],
        installSpecs: ["tokenjuice@0.6.1"],
      },
    ]);
  });

  it("does not reuse mismatched package-root runtime deps for source-checkout bundled plugins", () => {
    const packageRoot = makeTempDir();
    const stageDir = makeTempDir();
    fs.mkdirSync(path.join(packageRoot, ".git"), { recursive: true });
    fs.mkdirSync(path.join(packageRoot, "src"), { recursive: true });
    const pluginRoot = path.join(packageRoot, "extensions", "tokenjuice");
    fs.mkdirSync(path.join(packageRoot, "node_modules", "tokenjuice"), {
      recursive: true,
    });
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          tokenjuice: "0.6.1",
        },
      }),
    );
    fs.writeFileSync(
      path.join(packageRoot, "node_modules", "tokenjuice", "package.json"),
      JSON.stringify({ name: "tokenjuice", version: "0.6.0" }),
    );

    const calls: BundledRuntimeDepsInstallParams[] = [];
    const result = ensureBundledPluginRuntimeDeps({
      env: { OPENCLAW_PLUGIN_STAGE_DIR: stageDir },
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "tokenjuice",
      pluginRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["tokenjuice@0.6.1"],
      retainSpecs: ["tokenjuice@0.6.1"],
    });
    const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, {
      env: { OPENCLAW_PLUGIN_STAGE_DIR: stageDir },
    });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["tokenjuice@0.6.1"],
        installSpecs: ["tokenjuice@0.6.1"],
      },
    ]);
    expect(installRoot).toContain(stageDir);
    expect(installRoot).not.toBe(pluginRoot);
  });

  it("repairs external staged deps even when packaged plugin-local deps are present", () => {
    const packageRoot = makeTempDir();
    const extensionsRoot = path.join(packageRoot, "dist", "extensions");
    const pluginRoot = path.join(extensionsRoot, "discord");
    fs.mkdirSync(path.join(pluginRoot, "node_modules", "@buape", "carbon"), {
      recursive: true,
    });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          "@buape/carbon": "0.16.0",
        },
      }),
    );
    fs.writeFileSync(
      path.join(pluginRoot, "node_modules", "@buape", "carbon", "package.json"),
      JSON.stringify({ name: "@buape/carbon", version: "0.16.0" }),
    );

    const calls: BundledRuntimeDepsInstallParams[] = [];
    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: (params) => {
        calls.push(params);
        fs.mkdirSync(path.join(params.installRoot, "node_modules", "@buape", "carbon"), {
          recursive: true,
        });
        fs.writeFileSync(
          path.join(params.installRoot, "node_modules", "@buape", "carbon", "package.json"),
          JSON.stringify({ name: "@buape/carbon", version: "0.16.0" }),
        );
      },
      pluginId: "discord",
      pluginRoot,
    });

    const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env: {} });
    expect(result).toEqual({
      installedSpecs: ["@buape/carbon@0.16.0"],
      retainSpecs: ["@buape/carbon@0.16.0"],
    });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["@buape/carbon@0.16.0"],
        installSpecs: ["@buape/carbon@0.16.0"],
      },
    ]);
    expect(installRoot).not.toBe(pluginRoot);
  });

  it("does not trust runtime deps that only resolve from the package root", () => {
    const packageRoot = makeTempDir();
    const pluginRoot = path.join(packageRoot, "dist", "extensions", "openai");
    fs.mkdirSync(path.join(packageRoot, "node_modules", "@mariozechner", "pi-ai"), {
      recursive: true,
    });
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          "@mariozechner/pi-ai": "0.68.1",
        },
      }),
    );
    fs.writeFileSync(
      path.join(packageRoot, "node_modules", "@mariozechner", "pi-ai", "package.json"),
      JSON.stringify({ name: "@mariozechner/pi-ai", version: "0.68.1" }),
    );
    const calls: BundledRuntimeDepsInstallParams[] = [];

    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "openai",
      pluginRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["@mariozechner/pi-ai@0.68.1"],
      retainSpecs: ["@mariozechner/pi-ai@0.68.1"],
    });
    const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env: {} });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["@mariozechner/pi-ai@0.68.1"],
        installSpecs: ["@mariozechner/pi-ai@0.68.1"],
      },
    ]);
    expect(installRoot).not.toBe(pluginRoot);
  });

  it("installs deps that are only present in the package root", () => {
    const packageRoot = makeTempDir();
    const pluginRoot = path.join(packageRoot, "dist", "extensions", "codex");
    fs.mkdirSync(path.join(packageRoot, "node_modules", "ws"), { recursive: true });
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          ws: "^8.20.0",
          zod: "^4.3.6",
        },
      }),
    );
    fs.writeFileSync(
      path.join(packageRoot, "node_modules", "ws", "package.json"),
      JSON.stringify({ name: "ws", version: "8.20.0" }),
    );
    const calls: BundledRuntimeDepsInstallParams[] = [];

    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "codex",
      pluginRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["ws@^8.20.0", "zod@^4.3.6"],
      retainSpecs: ["ws@^8.20.0", "zod@^4.3.6"],
    });
    const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env: {} });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["ws@^8.20.0", "zod@^4.3.6"],
        installSpecs: ["ws@^8.20.0", "zod@^4.3.6"],
      },
    ]);
    expect(installRoot).not.toBe(pluginRoot);
  });

  it("does not treat sibling extension runtime deps as satisfying a plugin", () => {
    const packageRoot = makeTempDir();
    const extensionsRoot = path.join(packageRoot, "dist", "extensions");
    const pluginRoot = path.join(extensionsRoot, "codex");
    fs.mkdirSync(path.join(extensionsRoot, "discord", "node_modules", "zod"), {
      recursive: true,
    });
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          zod: "^4.3.6",
        },
      }),
    );
    fs.writeFileSync(
      path.join(extensionsRoot, "discord", "node_modules", "zod", "package.json"),
      JSON.stringify({ name: "zod", version: "4.3.6" }),
    );
    const calls: BundledRuntimeDepsInstallParams[] = [];

    const result = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: (params) => {
        calls.push(params);
      },
      pluginId: "codex",
      pluginRoot,
    });

    expect(result).toEqual({
      installedSpecs: ["zod@^4.3.6"],
      retainSpecs: ["zod@^4.3.6"],
    });
    const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env: {} });
    expect(calls).toEqual([
      {
        installRoot,
        missingSpecs: ["zod@^4.3.6"],
        installSpecs: ["zod@^4.3.6"],
      },
    ]);
    expect(installRoot).not.toBe(pluginRoot);
  });

  it("rejects unsupported remote runtime dependency specs", () => {
    const packageRoot = makeTempDir();
    const pluginRoot = path.join(packageRoot, "dist", "extensions", "tokenjuice");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          tokenjuice: "https://evil.example/tokenjuice.tgz",
        },
      }),
    );

    expect(() =>
      ensureBundledPluginRuntimeDeps({
        env: {},
        installDeps: () => {
          throw new Error("should not attempt install");
        },
        pluginId: "tokenjuice",
        pluginRoot,
      }),
    ).toThrow("Unsupported bundled runtime dependency spec for tokenjuice");
  });

  it("rejects invalid runtime dependency names before resolving sentinels", () => {
    const packageRoot = makeTempDir();
    const pluginRoot = path.join(packageRoot, "dist", "extensions", "tokenjuice");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          "../escape": "0.6.1",
        },
      }),
    );

    expect(() =>
      ensureBundledPluginRuntimeDeps({
        env: {},
        installDeps: () => {
          throw new Error("should not attempt install");
        },
        pluginId: "tokenjuice",
        pluginRoot,
      }),
    ).toThrow("Invalid bundled runtime dependency name");
  });

  it("rehydrates source-checkout dist deps from cache after rebuilds", () => {
    const packageRoot = makeTempDir();
    fs.mkdirSync(path.join(packageRoot, ".git"), { recursive: true });
    fs.mkdirSync(path.join(packageRoot, "src"), { recursive: true });
    fs.mkdirSync(path.join(packageRoot, "extensions"), { recursive: true });
    const pluginRoot = path.join(packageRoot, "dist", "extensions", "codex");
    fs.mkdirSync(pluginRoot, { recursive: true });
    fs.writeFileSync(
      path.join(pluginRoot, "package.json"),
      JSON.stringify({
        dependencies: {
          zod: "^4.3.6",
        },
      }),
    );
    const installCalls: BundledRuntimeDepsInstallParams[] = [];

    const first = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: (params) => {
        installCalls.push(params);
        fs.mkdirSync(path.join(params.installRoot, "node_modules", "zod"), { recursive: true });
        fs.writeFileSync(
          path.join(params.installRoot, "node_modules", "zod", "package.json"),
          JSON.stringify({ name: "zod", version: "4.3.6" }),
        );
      },
      pluginId: "codex",
      pluginRoot,
    });

    fs.rmSync(path.join(pluginRoot, "node_modules"), { recursive: true, force: true });

    const second = ensureBundledPluginRuntimeDeps({
      env: {},
      installDeps: () => {
        throw new Error("cached runtime deps should not reinstall");
      },
      pluginId: "codex",
      pluginRoot,
    });

    expect(first).toEqual({
      installedSpecs: ["zod@^4.3.6"],
      retainSpecs: ["zod@^4.3.6"],
    });
    expect(second).toEqual({ installedSpecs: [], retainSpecs: [] });
    expect(installCalls).toHaveLength(1);
    expect(fs.existsSync(path.join(pluginRoot, "node_modules", "zod", "package.json"))).toBe(true);
  });
});

¤ Dauer der Verarbeitung: 0.29 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






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.