@sikuligo/sikuligo 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -0
- package/dist/examples/app.d.ts +1 -0
- package/dist/examples/app.js +27 -0
- package/dist/examples/find.d.ts +1 -0
- package/dist/examples/find.js +45 -0
- package/dist/examples/input.d.ts +1 -0
- package/dist/examples/input.js +31 -0
- package/dist/examples/ocr.d.ts +1 -0
- package/dist/examples/ocr.js +47 -0
- package/dist/examples/user-flow.d.ts +1 -0
- package/dist/examples/user-flow.js +21 -0
- package/dist/src/binary.d.ts +1 -0
- package/dist/src/binary.js +114 -0
- package/dist/src/client.d.ts +45 -0
- package/dist/src/client.js +190 -0
- package/dist/src/doctor.d.ts +2 -0
- package/dist/src/doctor.js +20 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.js +13 -0
- package/dist/src/launcher.d.ts +19 -0
- package/dist/src/launcher.js +154 -0
- package/dist/src/sikuli.d.ts +53 -0
- package/dist/src/sikuli.js +102 -0
- package/generated/.gitkeep +1 -0
- package/generated/sikuli/v1/sikuli_grpc_pb.d.ts +296 -0
- package/generated/sikuli/v1/sikuli_grpc_pb.js +385 -0
- package/generated/sikuli/v1/sikuli_pb.d.ts +858 -0
- package/generated/sikuli/v1/sikuli_pb.js +6797 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# sikuligo (Node.js)
|
|
2
|
+
|
|
3
|
+
This package provides a Node.js SDK that can launch a local `sikuligrpc` process and execute automation with a small API surface.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Node.js 20+
|
|
8
|
+
- npm
|
|
9
|
+
- `sikuligrpc` binary available from one of:
|
|
10
|
+
- this package's `optionalDependencies` (`@sikuligo/bin-*`) after binary packages are published
|
|
11
|
+
- `SIKULIGO_BINARY_PATH`
|
|
12
|
+
- a manually installed platform package (for example `@sikuligo/bin-darwin-arm64`)
|
|
13
|
+
- `sikuligrpc` in `PATH`
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cd clients/node
|
|
19
|
+
npm install
|
|
20
|
+
npm run generate
|
|
21
|
+
npm run build
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Environment
|
|
25
|
+
|
|
26
|
+
- `SIKULIGO_BINARY_PATH` (optional explicit path to `sikuligrpc`)
|
|
27
|
+
- `SIKULI_GRPC_ADDR` (used by `Sikuli.connect`; default `127.0.0.1:50051`)
|
|
28
|
+
- `SIKULI_GRPC_AUTH_TOKEN` (optional; sent as `x-api-key` for spawned/connected sessions)
|
|
29
|
+
- `SIKULI_APP_NAME` (optional; used by `examples/app.ts`)
|
|
30
|
+
|
|
31
|
+
## Quickstart User Flow
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { Sikuli } from "@sikuligo/sikuligo";
|
|
35
|
+
|
|
36
|
+
const bot = await Sikuli.launch();
|
|
37
|
+
await bot.click({ x: 300, y: 220 });
|
|
38
|
+
await bot.typeText("hello");
|
|
39
|
+
await bot.hotkey(["cmd", "enter"]);
|
|
40
|
+
await bot.close();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Run Examples
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
cd clients/node
|
|
47
|
+
npm run example:find
|
|
48
|
+
npm run example:ocr
|
|
49
|
+
npm run example:input
|
|
50
|
+
npm run example:app
|
|
51
|
+
npm run example:user-flow
|
|
52
|
+
npm run doctor
|
|
53
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const client_1 = require("../src/client");
|
|
4
|
+
async function main() {
|
|
5
|
+
const client = new client_1.SikuliGrpcClient();
|
|
6
|
+
const appName = process.env.SIKULI_APP_NAME ?? "Calculator";
|
|
7
|
+
try {
|
|
8
|
+
await client.openApp({
|
|
9
|
+
name: appName,
|
|
10
|
+
args: []
|
|
11
|
+
});
|
|
12
|
+
const running = await client.isAppRunning({ name: appName });
|
|
13
|
+
console.log("isAppRunning", JSON.stringify(running, null, 2));
|
|
14
|
+
const windows = await client.listWindows({ name: appName });
|
|
15
|
+
console.log("listWindows", JSON.stringify(windows, null, 2));
|
|
16
|
+
await client.focusApp({ name: appName });
|
|
17
|
+
await client.closeApp({ name: appName });
|
|
18
|
+
console.log("app control actions sent");
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
client.close();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
main().catch((err) => {
|
|
25
|
+
console.error(err);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const client_1 = require("../src/client");
|
|
4
|
+
function grayImageFromRows(name, rows) {
|
|
5
|
+
const height = rows.length;
|
|
6
|
+
const width = rows[0].length;
|
|
7
|
+
const pix = rows.flat().map((v) => v & 0xff);
|
|
8
|
+
return {
|
|
9
|
+
name,
|
|
10
|
+
width,
|
|
11
|
+
height,
|
|
12
|
+
pix: Buffer.from(pix)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function main() {
|
|
16
|
+
const client = new client_1.SikuliGrpcClient();
|
|
17
|
+
const source = grayImageFromRows("source", [
|
|
18
|
+
[10, 10, 10, 10, 10, 10, 10, 10],
|
|
19
|
+
[10, 0, 255, 10, 10, 10, 10, 10],
|
|
20
|
+
[10, 255, 0, 10, 0, 255, 10, 10],
|
|
21
|
+
[10, 10, 10, 10, 255, 0, 10, 10],
|
|
22
|
+
[10, 10, 10, 10, 10, 10, 10, 10]
|
|
23
|
+
]);
|
|
24
|
+
const needle = grayImageFromRows("needle", [
|
|
25
|
+
[0, 255],
|
|
26
|
+
[255, 0]
|
|
27
|
+
]);
|
|
28
|
+
try {
|
|
29
|
+
const response = await client.find({
|
|
30
|
+
source,
|
|
31
|
+
pattern: {
|
|
32
|
+
image: needle,
|
|
33
|
+
exact: true
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
console.log(JSON.stringify(response, null, 2));
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
client.close();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
main().catch((err) => {
|
|
43
|
+
console.error(err);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const client_1 = require("../src/client");
|
|
4
|
+
async function main() {
|
|
5
|
+
const client = new client_1.SikuliGrpcClient();
|
|
6
|
+
try {
|
|
7
|
+
await client.moveMouse({
|
|
8
|
+
x: 200,
|
|
9
|
+
y: 180,
|
|
10
|
+
opts: { delay_millis: 30 }
|
|
11
|
+
});
|
|
12
|
+
await client.click({
|
|
13
|
+
x: 200,
|
|
14
|
+
y: 180,
|
|
15
|
+
opts: { button: "left", delay_millis: 20 }
|
|
16
|
+
});
|
|
17
|
+
await client.typeText({
|
|
18
|
+
text: "hello from node grpc",
|
|
19
|
+
opts: { delay_millis: 15 }
|
|
20
|
+
});
|
|
21
|
+
await client.hotkey({ keys: ["cmd", "a"] });
|
|
22
|
+
console.log("input actions sent");
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
client.close();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
main().catch((err) => {
|
|
29
|
+
console.error(err);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const client_1 = require("../src/client");
|
|
4
|
+
function grayImageFromRows(name, rows) {
|
|
5
|
+
const height = rows.length;
|
|
6
|
+
const width = rows[0].length;
|
|
7
|
+
const pix = rows.flat().map((v) => v & 0xff);
|
|
8
|
+
return {
|
|
9
|
+
name,
|
|
10
|
+
width,
|
|
11
|
+
height,
|
|
12
|
+
pix: Buffer.from(pix)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function main() {
|
|
16
|
+
const client = new client_1.SikuliGrpcClient();
|
|
17
|
+
const source = grayImageFromRows("ocr-source", [
|
|
18
|
+
[220, 220, 220, 220],
|
|
19
|
+
[220, 20, 20, 220],
|
|
20
|
+
[220, 220, 220, 220]
|
|
21
|
+
]);
|
|
22
|
+
try {
|
|
23
|
+
const readText = await client.readText({
|
|
24
|
+
source,
|
|
25
|
+
params: {
|
|
26
|
+
language: "eng"
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
console.log("readText", JSON.stringify(readText, null, 2));
|
|
30
|
+
const findText = await client.findText({
|
|
31
|
+
source,
|
|
32
|
+
query: "example",
|
|
33
|
+
params: {
|
|
34
|
+
language: "eng",
|
|
35
|
+
case_sensitive: false
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
console.log("findText", JSON.stringify(findText, null, 2));
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
client.close();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
main().catch((err) => {
|
|
45
|
+
console.error(err);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const src_1 = require("../src");
|
|
4
|
+
async function main() {
|
|
5
|
+
const bot = await src_1.Sikuli.launch({
|
|
6
|
+
startupTimeoutMs: 10_000
|
|
7
|
+
});
|
|
8
|
+
try {
|
|
9
|
+
await bot.click({ x: 300, y: 220 });
|
|
10
|
+
await bot.typeText("hello from sikuligo");
|
|
11
|
+
await bot.hotkey(["cmd", "enter"]);
|
|
12
|
+
console.log("automation actions sent");
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
await bot.close();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
main().catch((err) => {
|
|
19
|
+
console.error(err);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolveSikuliGrpcBinary(explicitPath?: string): string;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveSikuliGrpcBinary = resolveSikuliGrpcBinary;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const node_child_process_1 = require("node:child_process");
|
|
10
|
+
const DEFAULT_BINARY_NAME = process.platform === "win32" ? "sikuligrpc.exe" : "sikuligrpc";
|
|
11
|
+
const PLATFORM_BINARY_PACKAGES = {
|
|
12
|
+
"darwin-arm64": ["@sikuligo/bin-darwin-arm64"],
|
|
13
|
+
"darwin-x64": ["@sikuligo/bin-darwin-x64"],
|
|
14
|
+
"linux-x64": ["@sikuligo/bin-linux-x64"],
|
|
15
|
+
"win32-x64": ["@sikuligo/bin-win32-x64"]
|
|
16
|
+
};
|
|
17
|
+
function isExecutable(candidatePath) {
|
|
18
|
+
try {
|
|
19
|
+
if (!candidatePath) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (process.platform === "win32") {
|
|
23
|
+
node_fs_1.default.accessSync(candidatePath, node_fs_1.default.constants.F_OK);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
node_fs_1.default.accessSync(candidatePath, node_fs_1.default.constants.F_OK | node_fs_1.default.constants.X_OK);
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function candidateBinaryPaths(rootDir) {
|
|
35
|
+
const names = [DEFAULT_BINARY_NAME];
|
|
36
|
+
if (process.platform !== "win32") {
|
|
37
|
+
names.push("sikuligrpc");
|
|
38
|
+
}
|
|
39
|
+
return [
|
|
40
|
+
...names.map((name) => node_path_1.default.join(rootDir, name)),
|
|
41
|
+
...names.map((name) => node_path_1.default.join(rootDir, "bin", name)),
|
|
42
|
+
...names.map((name) => node_path_1.default.join(rootDir, "dist", name))
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
function resolveOptionalPackageBinary() {
|
|
46
|
+
const key = `${process.platform}-${process.arch}`;
|
|
47
|
+
const packages = PLATFORM_BINARY_PACKAGES[key] ?? [];
|
|
48
|
+
for (const pkgName of packages) {
|
|
49
|
+
try {
|
|
50
|
+
const packageJsonPath = require.resolve(`${pkgName}/package.json`);
|
|
51
|
+
const packageRoot = node_path_1.default.dirname(packageJsonPath);
|
|
52
|
+
for (const candidate of candidateBinaryPaths(packageRoot)) {
|
|
53
|
+
if (isExecutable(candidate)) {
|
|
54
|
+
return candidate;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Optional package not installed for this platform.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
function resolveFromPath() {
|
|
65
|
+
const cmd = process.platform === "win32" ? "where" : "which";
|
|
66
|
+
const result = (0, node_child_process_1.spawnSync)(cmd, [DEFAULT_BINARY_NAME], { encoding: "utf8" });
|
|
67
|
+
if (result.status !== 0) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const first = result.stdout
|
|
71
|
+
.split(/\r?\n/)
|
|
72
|
+
.map((line) => line.trim())
|
|
73
|
+
.find((line) => line.length > 0);
|
|
74
|
+
if (!first) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
return isExecutable(first) ? first : undefined;
|
|
78
|
+
}
|
|
79
|
+
function resolveLocalRepoFallback() {
|
|
80
|
+
const candidates = [
|
|
81
|
+
node_path_1.default.resolve(process.cwd(), DEFAULT_BINARY_NAME),
|
|
82
|
+
node_path_1.default.resolve(process.cwd(), "bin", DEFAULT_BINARY_NAME),
|
|
83
|
+
node_path_1.default.resolve(__dirname, "..", "..", "..", "sikuligrpc"),
|
|
84
|
+
node_path_1.default.resolve(__dirname, "..", "..", "..", "bin", DEFAULT_BINARY_NAME)
|
|
85
|
+
];
|
|
86
|
+
return candidates.find((candidate) => isExecutable(candidate));
|
|
87
|
+
}
|
|
88
|
+
function errorWithResolutionHelp(detail) {
|
|
89
|
+
return new Error(`${detail}\n` +
|
|
90
|
+
"Set SIKULIGO_BINARY_PATH or install a platform binary package (for example @sikuligo/bin-darwin-arm64), " +
|
|
91
|
+
"or place sikuligrpc in PATH.");
|
|
92
|
+
}
|
|
93
|
+
function resolveSikuliGrpcBinary(explicitPath) {
|
|
94
|
+
const manual = explicitPath || process.env.SIKULIGO_BINARY_PATH || "";
|
|
95
|
+
if (manual) {
|
|
96
|
+
if (!isExecutable(manual)) {
|
|
97
|
+
throw errorWithResolutionHelp(`Configured binary path is not executable: ${manual}`);
|
|
98
|
+
}
|
|
99
|
+
return manual;
|
|
100
|
+
}
|
|
101
|
+
const optionalPackageBinary = resolveOptionalPackageBinary();
|
|
102
|
+
if (optionalPackageBinary) {
|
|
103
|
+
return optionalPackageBinary;
|
|
104
|
+
}
|
|
105
|
+
const pathBinary = resolveFromPath();
|
|
106
|
+
if (pathBinary) {
|
|
107
|
+
return pathBinary;
|
|
108
|
+
}
|
|
109
|
+
const localFallback = resolveLocalRepoFallback();
|
|
110
|
+
if (localFallback) {
|
|
111
|
+
return localFallback;
|
|
112
|
+
}
|
|
113
|
+
throw errorWithResolutionHelp(`Unable to resolve sikuligrpc binary for platform ${process.platform}/${process.arch}`);
|
|
114
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as grpc from "@grpc/grpc-js";
|
|
2
|
+
export type RpcMessage = Record<string, unknown>;
|
|
3
|
+
export interface SikuliClientOptions {
|
|
4
|
+
address?: string;
|
|
5
|
+
authToken?: string;
|
|
6
|
+
traceId?: string;
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
protoPath?: string;
|
|
9
|
+
credentials?: grpc.ChannelCredentials;
|
|
10
|
+
}
|
|
11
|
+
export interface UnaryCallOptions {
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
metadata?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
export declare class SikuliGrpcError extends Error {
|
|
16
|
+
readonly code: number;
|
|
17
|
+
readonly details: string;
|
|
18
|
+
readonly traceId?: string;
|
|
19
|
+
constructor(code: number, details: string, traceId?: string);
|
|
20
|
+
}
|
|
21
|
+
export declare class SikuliGrpcClient {
|
|
22
|
+
private readonly client;
|
|
23
|
+
private readonly authToken;
|
|
24
|
+
private readonly traceId;
|
|
25
|
+
private readonly defaultTimeoutMs;
|
|
26
|
+
constructor(opts?: SikuliClientOptions);
|
|
27
|
+
close(): void;
|
|
28
|
+
waitForReady(timeoutMs?: number): Promise<void>;
|
|
29
|
+
private buildMetadata;
|
|
30
|
+
private grpcError;
|
|
31
|
+
private unary;
|
|
32
|
+
find(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
33
|
+
findAll(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
34
|
+
readText(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
35
|
+
findText(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
36
|
+
moveMouse(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
37
|
+
click(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
38
|
+
typeText(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
39
|
+
hotkey(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
40
|
+
openApp(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
41
|
+
focusApp(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
42
|
+
closeApp(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
43
|
+
isAppRunning(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
44
|
+
listWindows(request: RpcMessage, opts?: UnaryCallOptions): Promise<RpcMessage>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.SikuliGrpcClient = exports.SikuliGrpcError = void 0;
|
|
40
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
41
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
42
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
43
|
+
const protoLoader = __importStar(require("@grpc/proto-loader"));
|
|
44
|
+
const DEFAULT_ADDR = "127.0.0.1:50051";
|
|
45
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
46
|
+
const TRACE_HEADER = "x-trace-id";
|
|
47
|
+
class SikuliGrpcError extends Error {
|
|
48
|
+
constructor(code, details, traceId) {
|
|
49
|
+
const suffix = traceId ? ` trace_id=${traceId}` : "";
|
|
50
|
+
super(`grpc_code=${code} details=${details}${suffix}`);
|
|
51
|
+
this.code = code;
|
|
52
|
+
this.details = details;
|
|
53
|
+
this.traceId = traceId;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.SikuliGrpcError = SikuliGrpcError;
|
|
57
|
+
function resolveDefaultProtoPath() {
|
|
58
|
+
const candidates = [
|
|
59
|
+
node_path_1.default.resolve(__dirname, "../../../proto/sikuli/v1/sikuli.proto"),
|
|
60
|
+
node_path_1.default.resolve(__dirname, "../../../../proto/sikuli/v1/sikuli.proto"),
|
|
61
|
+
node_path_1.default.resolve(process.cwd(), "proto/sikuli/v1/sikuli.proto"),
|
|
62
|
+
node_path_1.default.resolve(process.cwd(), "../../proto/sikuli/v1/sikuli.proto")
|
|
63
|
+
];
|
|
64
|
+
for (const candidate of candidates) {
|
|
65
|
+
if (node_fs_1.default.existsSync(candidate)) {
|
|
66
|
+
return candidate;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return candidates[0];
|
|
70
|
+
}
|
|
71
|
+
function serviceConstructorFromProto(protoPath) {
|
|
72
|
+
const packageDefinition = protoLoader.loadSync(protoPath, {
|
|
73
|
+
keepCase: true,
|
|
74
|
+
longs: String,
|
|
75
|
+
enums: String,
|
|
76
|
+
defaults: true,
|
|
77
|
+
oneofs: true
|
|
78
|
+
});
|
|
79
|
+
const root = grpc.loadPackageDefinition(packageDefinition);
|
|
80
|
+
const serviceCtor = root?.sikuli?.v1?.SikuliService;
|
|
81
|
+
if (!serviceCtor) {
|
|
82
|
+
throw new Error(`SikuliService not found in proto: ${protoPath}`);
|
|
83
|
+
}
|
|
84
|
+
return serviceCtor;
|
|
85
|
+
}
|
|
86
|
+
class SikuliGrpcClient {
|
|
87
|
+
constructor(opts = {}) {
|
|
88
|
+
const address = opts.address ?? process.env.SIKULI_GRPC_ADDR ?? DEFAULT_ADDR;
|
|
89
|
+
this.authToken = opts.authToken ?? process.env.SIKULI_GRPC_AUTH_TOKEN ?? "";
|
|
90
|
+
this.traceId = opts.traceId ?? "";
|
|
91
|
+
this.defaultTimeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
92
|
+
const protoPath = opts.protoPath ?? resolveDefaultProtoPath();
|
|
93
|
+
const ctor = serviceConstructorFromProto(protoPath);
|
|
94
|
+
const credentials = opts.credentials ?? grpc.credentials.createInsecure();
|
|
95
|
+
this.client = new ctor(address, credentials);
|
|
96
|
+
}
|
|
97
|
+
close() {
|
|
98
|
+
this.client.close();
|
|
99
|
+
}
|
|
100
|
+
waitForReady(timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
101
|
+
const deadline = new Date(Date.now() + timeoutMs);
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
this.client.waitForReady(deadline, (err) => {
|
|
104
|
+
if (err) {
|
|
105
|
+
reject(err);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
resolve();
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
buildMetadata(extra = {}) {
|
|
113
|
+
const md = new grpc.Metadata();
|
|
114
|
+
if (this.authToken) {
|
|
115
|
+
md.set("x-api-key", this.authToken);
|
|
116
|
+
}
|
|
117
|
+
if (this.traceId) {
|
|
118
|
+
md.set(TRACE_HEADER, this.traceId);
|
|
119
|
+
}
|
|
120
|
+
for (const [k, v] of Object.entries(extra)) {
|
|
121
|
+
if (v) {
|
|
122
|
+
md.set(k, v);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return md;
|
|
126
|
+
}
|
|
127
|
+
grpcError(err) {
|
|
128
|
+
const traceValues = err.metadata?.get(TRACE_HEADER) ?? [];
|
|
129
|
+
const traceId = traceValues.length > 0 ? String(traceValues[0]) : undefined;
|
|
130
|
+
return new SikuliGrpcError(err.code ?? grpc.status.UNKNOWN, err.details || err.message, traceId);
|
|
131
|
+
}
|
|
132
|
+
unary(methodName, request, opts = {}) {
|
|
133
|
+
const callFn = this.client[methodName];
|
|
134
|
+
if (typeof callFn !== "function") {
|
|
135
|
+
return Promise.reject(new Error(`unknown gRPC method: ${methodName}`));
|
|
136
|
+
}
|
|
137
|
+
const timeoutMs = opts.timeoutMs ?? this.defaultTimeoutMs;
|
|
138
|
+
const deadline = new Date(Date.now() + timeoutMs);
|
|
139
|
+
const metadata = this.buildMetadata(opts.metadata);
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
callFn.call(this.client, request, metadata, { deadline }, (err, response) => {
|
|
142
|
+
if (err) {
|
|
143
|
+
reject(this.grpcError(err));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
resolve(response);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
find(request, opts) {
|
|
151
|
+
return this.unary("Find", request, opts);
|
|
152
|
+
}
|
|
153
|
+
findAll(request, opts) {
|
|
154
|
+
return this.unary("FindAll", request, opts);
|
|
155
|
+
}
|
|
156
|
+
readText(request, opts) {
|
|
157
|
+
return this.unary("ReadText", request, opts);
|
|
158
|
+
}
|
|
159
|
+
findText(request, opts) {
|
|
160
|
+
return this.unary("FindText", request, opts);
|
|
161
|
+
}
|
|
162
|
+
moveMouse(request, opts) {
|
|
163
|
+
return this.unary("MoveMouse", request, opts);
|
|
164
|
+
}
|
|
165
|
+
click(request, opts) {
|
|
166
|
+
return this.unary("Click", request, opts);
|
|
167
|
+
}
|
|
168
|
+
typeText(request, opts) {
|
|
169
|
+
return this.unary("TypeText", request, opts);
|
|
170
|
+
}
|
|
171
|
+
hotkey(request, opts) {
|
|
172
|
+
return this.unary("Hotkey", request, opts);
|
|
173
|
+
}
|
|
174
|
+
openApp(request, opts) {
|
|
175
|
+
return this.unary("OpenApp", request, opts);
|
|
176
|
+
}
|
|
177
|
+
focusApp(request, opts) {
|
|
178
|
+
return this.unary("FocusApp", request, opts);
|
|
179
|
+
}
|
|
180
|
+
closeApp(request, opts) {
|
|
181
|
+
return this.unary("CloseApp", request, opts);
|
|
182
|
+
}
|
|
183
|
+
isAppRunning(request, opts) {
|
|
184
|
+
return this.unary("IsAppRunning", request, opts);
|
|
185
|
+
}
|
|
186
|
+
listWindows(request, opts) {
|
|
187
|
+
return this.unary("ListWindows", request, opts);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
exports.SikuliGrpcClient = SikuliGrpcClient;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const binary_1 = require("./binary");
|
|
5
|
+
function run() {
|
|
6
|
+
try {
|
|
7
|
+
const binary = (0, binary_1.resolveSikuliGrpcBinary)();
|
|
8
|
+
console.log("sikuligo doctor: ok");
|
|
9
|
+
console.log(`binary: ${binary}`);
|
|
10
|
+
console.log(`platform: ${process.platform}/${process.arch}`);
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
15
|
+
console.error("sikuligo doctor: failed");
|
|
16
|
+
console.error(message);
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
process.exitCode = run();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Sikuli } from "./sikuli";
|
|
2
|
+
export { resolveSikuliGrpcBinary } from "./binary";
|
|
3
|
+
export { launchClient, stopSpawnedProcess } from "./launcher";
|
|
4
|
+
export { SikuliGrpcClient, SikuliGrpcError } from "./client";
|
|
5
|
+
export type { LaunchOptions, LaunchResult } from "./launcher";
|
|
6
|
+
export type { InputOptions, MoveMouseRequest, ClickRequest, TypeTextRequest, LaunchResultMeta } from "./sikuli";
|
|
7
|
+
export type { RpcMessage, SikuliClientOptions, UnaryCallOptions } from "./client";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SikuliGrpcError = exports.SikuliGrpcClient = exports.stopSpawnedProcess = exports.launchClient = exports.resolveSikuliGrpcBinary = exports.Sikuli = void 0;
|
|
4
|
+
var sikuli_1 = require("./sikuli");
|
|
5
|
+
Object.defineProperty(exports, "Sikuli", { enumerable: true, get: function () { return sikuli_1.Sikuli; } });
|
|
6
|
+
var binary_1 = require("./binary");
|
|
7
|
+
Object.defineProperty(exports, "resolveSikuliGrpcBinary", { enumerable: true, get: function () { return binary_1.resolveSikuliGrpcBinary; } });
|
|
8
|
+
var launcher_1 = require("./launcher");
|
|
9
|
+
Object.defineProperty(exports, "launchClient", { enumerable: true, get: function () { return launcher_1.launchClient; } });
|
|
10
|
+
Object.defineProperty(exports, "stopSpawnedProcess", { enumerable: true, get: function () { return launcher_1.stopSpawnedProcess; } });
|
|
11
|
+
var client_1 = require("./client");
|
|
12
|
+
Object.defineProperty(exports, "SikuliGrpcClient", { enumerable: true, get: function () { return client_1.SikuliGrpcClient; } });
|
|
13
|
+
Object.defineProperty(exports, "SikuliGrpcError", { enumerable: true, get: function () { return client_1.SikuliGrpcError; } });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ChildProcess } from "node:child_process";
|
|
2
|
+
import { SikuliGrpcClient, SikuliClientOptions } from "./client";
|
|
3
|
+
export interface LaunchOptions extends SikuliClientOptions {
|
|
4
|
+
spawnServer?: boolean;
|
|
5
|
+
startupTimeoutMs?: number;
|
|
6
|
+
binaryPath?: string;
|
|
7
|
+
adminListen?: string;
|
|
8
|
+
serverArgs?: string[];
|
|
9
|
+
stdio?: "ignore" | "pipe" | "inherit";
|
|
10
|
+
}
|
|
11
|
+
export interface LaunchResult {
|
|
12
|
+
address: string;
|
|
13
|
+
authToken: string;
|
|
14
|
+
client: SikuliGrpcClient;
|
|
15
|
+
child?: ChildProcess;
|
|
16
|
+
spawnedServer: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function stopSpawnedProcess(child?: ChildProcess, timeoutMs?: number): Promise<void>;
|
|
19
|
+
export declare function launchClient(opts?: LaunchOptions): Promise<LaunchResult>;
|