File size: 4,568 Bytes
5cb63c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import fs from "node:fs";
import path from "node:path";
import { afterEach, expect, it } from "vitest";
import { useAutoCleanupTempDirTracker } from "../../../test/helpers/temp-dir.js";
import { createCompiledSdkHost } from "../../plugins/compiled-sdk-host.test-support.js";
import { computerUseSdkEntrypoint } from "../../plugins/loader-sdk-bridge-artifacts.test-support.js";
import {
  createColdPluginConfig,
  createColdPluginFixture,
} from "../../plugins/test-helpers/cold-plugin-fixtures.js";
import { startComputerHostProcess } from "./computer-process.js";

const tempDirs = useAutoCleanupTempDirTracker(afterEach);

it("runs a synthetic provider through the private worker and finalizes its execution before exiting", async () => {
  const home = tempDirs.make("openclaw-computer-worker-");
  const pluginRoot = path.join(home, "plugin");
  fs.mkdirSync(pluginRoot);
  const pluginId = "fixture-computer";
  const fixture = createColdPluginFixture({
    rootDir: pluginRoot,
    pluginId,
    manifest: { providers: [], channels: [], channelConfigs: {}, providerAuthChoices: [] },
  });
  const lifecycleFile = path.join(home, "provider-lifecycle.txt");
  fs.writeFileSync(
    fixture.runtimeSource,
    `
const { appendFileSync } = require("node:fs");
const { registerComputerUseProvider } = require("openclaw/plugin-sdk/computer-use");
const record = (event) => appendFileSync(${JSON.stringify(lifecycleFile)}, event + "\\n");
process.once("exit", (code) => record("exit:" + code));
module.exports = {
  id: ${JSON.stringify(pluginId)},
  register(api) {
    registerComputerUseProvider(api, {
      id: ${JSON.stringify(pluginId)},
      label: "Synthetic computer",
      isAvailable: () => true,
      capabilities: () => ({
        contractVersion: 2,
        provider: { id: ${JSON.stringify(pluginId)}, label: "Synthetic computer", generation: "fixture-generation" },
        actions: ["screenshot", "left_click"],
        targets: ["screen"], deliveryModes: ["foreground"], observations: ["image"],
        features: { recording: false, agentCursor: false, multiDisplay: false },
      }),
      async openExecution({ executionId, sessionKey }) {
        record("open:" + executionId);
        return {
          async snapshot() { return JSON.stringify({ format: "png", base64: "c3ludGhldGljLXBpeGVscw==" }); },
          async act(paramsJSON) {
            const params = JSON.parse(paramsJSON);
            return JSON.stringify({ ok: true, action: params.action, details: { executionId, sessionKey } });
          },
          async close(reason) { record("close:" + reason); },
        };
      },
    });
  },
};
`,
  );
  const config = createColdPluginConfig(pluginRoot, pluginId);
  config.plugins!.allow = [pluginId];
  const configPath = path.join(home, "openclaw.json");
  fs.writeFileSync(configPath, JSON.stringify(config));
  const sdkHost = createCompiledSdkHost(computerUseSdkEntrypoint, (prefix) =>
    tempDirs.make(prefix),
  );
  const child = startComputerHostProcess({
    env: {
      PATH: path.dirname(process.execPath),
      HOME: home,
      USERPROFILE: home,
      OPENCLAW_HOME: home,
      OPENCLAW_STATE_DIR: path.join(home, "state"),
      OPENCLAW_CONFIG_PATH: configPath,
      OPENCLAW_DISABLE_BUNDLED_PLUGINS: "1",
      ...(sdkHost ? { OPENCLAW_DEV_SOURCE_ROOT: sdkHost } : {}),
      NODE_ENV: "test",
    },
    pluginIds: [pluginId],
    assertCurrent: () => {},
  });
  const executionId = "123e4567-e89b-42d3-a456-426614174000";
  try {
    expect((await child.ready).provider).toEqual({
      id: pluginId,
      label: "Synthetic computer",
      generation: "fixture-generation",
    });
    await expect(
      child.invoke({
        command: "screen.snapshot",
        params: { executionId },
        sessionKey: "fixture-session",
        assertCurrent: () => {},
      }),
    ).resolves.toEqual({ format: "png", base64: "c3ludGhldGljLXBpeGVscw==" });
    await expect(
      child.invoke({
        command: "computer.act",
        params: { executionId, action: "left_click", x: 1, y: 2 },
        sessionKey: "fixture-session",
        assertCurrent: () => {},
      }),
    ).resolves.toEqual({
      ok: true,
      action: "left_click",
      details: { executionId, sessionKey: "fixture-session" },
    });
  } finally {
    await child.close({ executionId, reason: "completion" });
  }
  expect(child.isCurrent()).toBe(false);
  expect(fs.readFileSync(lifecycleFile, "utf8").trim().split("\n")).toEqual([
    `open:${executionId}`,
    "close:completion",
    "exit:0",
  ]);
}, 90_000);