@skillful-agents/agent-computer 0.0.3 → 0.0.5
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/bin/ac-core-darwin-arm64 +0 -0
- package/bin/ac-core-darwin-x64 +0 -0
- package/bin/ac-core-win32-arm64.exe +0 -0
- package/bin/ac-core-win32-x64.exe +0 -0
- package/dist/src/platform/resolve.d.ts.map +1 -1
- package/dist/src/platform/resolve.js +33 -6
- package/dist/src/platform/resolve.js.map +1 -1
- package/dist-cjs/bin/ac.js +127 -0
- package/dist-cjs/package.json +1 -0
- package/dist-cjs/src/bridge.js +693 -0
- package/dist-cjs/src/cdp/ax-tree.js +162 -0
- package/dist-cjs/src/cdp/bounds.js +66 -0
- package/dist-cjs/src/cdp/client.js +272 -0
- package/dist-cjs/src/cdp/connection.js +285 -0
- package/dist-cjs/src/cdp/diff.js +55 -0
- package/dist-cjs/src/cdp/discovery.js +91 -0
- package/dist-cjs/src/cdp/index.js +27 -0
- package/dist-cjs/src/cdp/interactions.js +301 -0
- package/dist-cjs/src/cdp/port-manager.js +68 -0
- package/dist-cjs/src/cdp/role-map.js +102 -0
- package/dist-cjs/src/cdp/types.js +2 -0
- package/dist-cjs/src/cli/commands/apps.js +63 -0
- package/dist-cjs/src/cli/commands/batch.js +37 -0
- package/dist-cjs/src/cli/commands/click.js +61 -0
- package/dist-cjs/src/cli/commands/clipboard.js +31 -0
- package/dist-cjs/src/cli/commands/dialog.js +45 -0
- package/dist-cjs/src/cli/commands/drag.js +26 -0
- package/dist-cjs/src/cli/commands/find.js +99 -0
- package/dist-cjs/src/cli/commands/menu.js +36 -0
- package/dist-cjs/src/cli/commands/screenshot.js +27 -0
- package/dist-cjs/src/cli/commands/scroll.js +77 -0
- package/dist-cjs/src/cli/commands/session.js +27 -0
- package/dist-cjs/src/cli/commands/snapshot.js +24 -0
- package/dist-cjs/src/cli/commands/type.js +69 -0
- package/dist-cjs/src/cli/commands/windowmgmt.js +62 -0
- package/dist-cjs/src/cli/commands/windows.js +10 -0
- package/dist-cjs/src/cli/commands.js +215 -0
- package/dist-cjs/src/cli/output.js +253 -0
- package/dist-cjs/src/cli/parser.js +128 -0
- package/dist-cjs/src/config.js +79 -0
- package/dist-cjs/src/daemon.js +183 -0
- package/dist-cjs/src/errors.js +118 -0
- package/dist-cjs/src/index.js +24 -0
- package/dist-cjs/src/platform/index.js +16 -0
- package/dist-cjs/src/platform/resolve.js +83 -0
- package/dist-cjs/src/refs.js +91 -0
- package/dist-cjs/src/sdk.js +288 -0
- package/dist-cjs/src/types.js +11 -0
- package/package.json +4 -2
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DaemonManager = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const net_1 = require("net");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const index_js_1 = require("./platform/index.js");
|
|
8
|
+
const resolve_js_1 = require("./platform/resolve.js");
|
|
9
|
+
class DaemonManager {
|
|
10
|
+
binaryPath;
|
|
11
|
+
constructor(binaryPath) {
|
|
12
|
+
this.binaryPath = binaryPath ?? (0, resolve_js_1.resolveBinary)();
|
|
13
|
+
}
|
|
14
|
+
async start() {
|
|
15
|
+
// Check if already running
|
|
16
|
+
const current = this.readDaemonInfo();
|
|
17
|
+
if (current && this.isProcessAlive(current.pid)) {
|
|
18
|
+
return this.buildStatus(current, true);
|
|
19
|
+
}
|
|
20
|
+
// Clean up stale files
|
|
21
|
+
this.cleanupStale();
|
|
22
|
+
// Spawn daemon
|
|
23
|
+
(0, fs_1.mkdirSync)(index_js_1.AC_DIR, { recursive: true });
|
|
24
|
+
const proc = (0, child_process_1.spawn)(this.binaryPath, ['--daemon'], {
|
|
25
|
+
detached: true,
|
|
26
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
27
|
+
});
|
|
28
|
+
proc.unref();
|
|
29
|
+
// Wait for daemon to be ready
|
|
30
|
+
const start = Date.now();
|
|
31
|
+
if (index_js_1.IS_NAMED_PIPE) {
|
|
32
|
+
// Named pipes: wait for daemon.json to appear (pipe has no file)
|
|
33
|
+
while (Date.now() - start < 5000) {
|
|
34
|
+
if ((0, fs_1.existsSync)(index_js_1.DAEMON_JSON_PATH)) {
|
|
35
|
+
await sleep(50);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
await sleep(50);
|
|
39
|
+
}
|
|
40
|
+
if (!(0, fs_1.existsSync)(index_js_1.DAEMON_JSON_PATH)) {
|
|
41
|
+
throw new Error('Daemon failed to start: daemon.json not created within 5s');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// Unix socket: wait for socket file to appear
|
|
46
|
+
while (Date.now() - start < 5000) {
|
|
47
|
+
if ((0, fs_1.existsSync)(index_js_1.SOCKET_PATH)) {
|
|
48
|
+
await sleep(50);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
await sleep(50);
|
|
52
|
+
}
|
|
53
|
+
if (!(0, fs_1.existsSync)(index_js_1.SOCKET_PATH)) {
|
|
54
|
+
throw new Error('Daemon failed to start: socket not created within 5s');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const info = this.readDaemonInfo();
|
|
58
|
+
if (!info) {
|
|
59
|
+
throw new Error('Daemon started but daemon.json not found');
|
|
60
|
+
}
|
|
61
|
+
return this.buildStatus(info, true);
|
|
62
|
+
}
|
|
63
|
+
async stop() {
|
|
64
|
+
const info = this.readDaemonInfo();
|
|
65
|
+
if (!info)
|
|
66
|
+
return;
|
|
67
|
+
if (this.isProcessAlive(info.pid)) {
|
|
68
|
+
// On Windows, SIGTERM is a hard kill — try graceful RPC shutdown first
|
|
69
|
+
if (index_js_1.IS_NAMED_PIPE) {
|
|
70
|
+
try {
|
|
71
|
+
await this.sendShutdownRPC(info.socket);
|
|
72
|
+
}
|
|
73
|
+
catch { /* fall through to SIGTERM */ }
|
|
74
|
+
}
|
|
75
|
+
// Wait for process to exit (may already be gone from RPC shutdown)
|
|
76
|
+
let start = Date.now();
|
|
77
|
+
while (Date.now() - start < 3000) {
|
|
78
|
+
if (!this.isProcessAlive(info.pid))
|
|
79
|
+
break;
|
|
80
|
+
await sleep(50);
|
|
81
|
+
}
|
|
82
|
+
// Send SIGTERM if still alive
|
|
83
|
+
if (this.isProcessAlive(info.pid)) {
|
|
84
|
+
try {
|
|
85
|
+
process.kill(info.pid, 'SIGTERM');
|
|
86
|
+
}
|
|
87
|
+
catch { /* ok */ }
|
|
88
|
+
start = Date.now();
|
|
89
|
+
while (Date.now() - start < 3000) {
|
|
90
|
+
if (!this.isProcessAlive(info.pid))
|
|
91
|
+
break;
|
|
92
|
+
await sleep(50);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Force kill if still alive
|
|
96
|
+
if (this.isProcessAlive(info.pid)) {
|
|
97
|
+
try {
|
|
98
|
+
process.kill(info.pid, 'SIGKILL');
|
|
99
|
+
}
|
|
100
|
+
catch { /* ok */ }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
this.cleanupStale();
|
|
104
|
+
}
|
|
105
|
+
sendShutdownRPC(socketPath) {
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
const sock = (0, net_1.connect)({ path: socketPath });
|
|
108
|
+
const timeout = setTimeout(() => { sock.destroy(); reject(new Error('shutdown timeout')); }, 2000);
|
|
109
|
+
sock.on('connect', () => {
|
|
110
|
+
const req = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'shutdown', params: {} }) + '\n';
|
|
111
|
+
sock.write(req, () => {
|
|
112
|
+
clearTimeout(timeout);
|
|
113
|
+
// Give daemon a moment to process before closing
|
|
114
|
+
setTimeout(() => { sock.destroy(); resolve(); }, 100);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
sock.on('error', (err) => { clearTimeout(timeout); reject(err); });
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async status() {
|
|
121
|
+
const info = this.readDaemonInfo();
|
|
122
|
+
if (!info) {
|
|
123
|
+
return { running: false };
|
|
124
|
+
}
|
|
125
|
+
const alive = this.isProcessAlive(info.pid);
|
|
126
|
+
if (!alive) {
|
|
127
|
+
this.cleanupStale();
|
|
128
|
+
return { running: false };
|
|
129
|
+
}
|
|
130
|
+
return this.buildStatus(info, true);
|
|
131
|
+
}
|
|
132
|
+
async restart() {
|
|
133
|
+
await this.stop();
|
|
134
|
+
return this.start();
|
|
135
|
+
}
|
|
136
|
+
readDaemonInfo() {
|
|
137
|
+
try {
|
|
138
|
+
if (!(0, fs_1.existsSync)(index_js_1.DAEMON_JSON_PATH))
|
|
139
|
+
return null;
|
|
140
|
+
const raw = (0, fs_1.readFileSync)(index_js_1.DAEMON_JSON_PATH, 'utf-8');
|
|
141
|
+
return JSON.parse(raw);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
isProcessAlive(pid) {
|
|
148
|
+
try {
|
|
149
|
+
process.kill(pid, 0);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
cleanupStale() {
|
|
157
|
+
if (!index_js_1.IS_NAMED_PIPE) {
|
|
158
|
+
try {
|
|
159
|
+
(0, fs_1.unlinkSync)(index_js_1.SOCKET_PATH);
|
|
160
|
+
}
|
|
161
|
+
catch { /* ok */ }
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
(0, fs_1.unlinkSync)(index_js_1.DAEMON_JSON_PATH);
|
|
165
|
+
}
|
|
166
|
+
catch { /* ok */ }
|
|
167
|
+
}
|
|
168
|
+
buildStatus(info, running) {
|
|
169
|
+
const startedAt = new Date(info.started_at);
|
|
170
|
+
const uptime = Date.now() - startedAt.getTime();
|
|
171
|
+
return {
|
|
172
|
+
running,
|
|
173
|
+
pid: info.pid,
|
|
174
|
+
socket: info.socket,
|
|
175
|
+
started_at: info.started_at,
|
|
176
|
+
uptime_ms: Math.max(0, uptime),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
exports.DaemonManager = DaemonManager;
|
|
181
|
+
function sleep(ms) {
|
|
182
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
183
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InvalidParamsError = exports.MethodNotFoundError = exports.InvalidRequestError = exports.OCRFallbackFailedError = exports.InvalidRefError = exports.WindowNotFoundError = exports.AppNotFoundError = exports.TimeoutError = exports.PermissionDeniedError = exports.ElementNotFoundError = exports.ACError = void 0;
|
|
4
|
+
exports.errorFromCode = errorFromCode;
|
|
5
|
+
exports.exitCodeFromErrorCode = exitCodeFromErrorCode;
|
|
6
|
+
class ACError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
exitCode;
|
|
9
|
+
data;
|
|
10
|
+
constructor(message, code, exitCode, data) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.exitCode = exitCode;
|
|
14
|
+
this.data = data;
|
|
15
|
+
this.name = 'ACError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.ACError = ACError;
|
|
19
|
+
class ElementNotFoundError extends ACError {
|
|
20
|
+
constructor(message, data) {
|
|
21
|
+
super(message, -32001, 1, data);
|
|
22
|
+
this.name = 'ELEMENT_NOT_FOUND';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.ElementNotFoundError = ElementNotFoundError;
|
|
26
|
+
class PermissionDeniedError extends ACError {
|
|
27
|
+
constructor(message, data) {
|
|
28
|
+
super(message, -32002, 2, data);
|
|
29
|
+
this.name = 'PERMISSION_DENIED';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.PermissionDeniedError = PermissionDeniedError;
|
|
33
|
+
class TimeoutError extends ACError {
|
|
34
|
+
constructor(message, data) {
|
|
35
|
+
super(message, -32003, 3, data);
|
|
36
|
+
this.name = 'TIMEOUT';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.TimeoutError = TimeoutError;
|
|
40
|
+
class AppNotFoundError extends ACError {
|
|
41
|
+
constructor(message, data) {
|
|
42
|
+
super(message, -32004, 4, data);
|
|
43
|
+
this.name = 'APP_NOT_FOUND';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.AppNotFoundError = AppNotFoundError;
|
|
47
|
+
class WindowNotFoundError extends ACError {
|
|
48
|
+
constructor(message, data) {
|
|
49
|
+
super(message, -32005, 5, data);
|
|
50
|
+
this.name = 'WINDOW_NOT_FOUND';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.WindowNotFoundError = WindowNotFoundError;
|
|
54
|
+
class InvalidRefError extends ACError {
|
|
55
|
+
constructor(message, data) {
|
|
56
|
+
super(message, -32006, 6, data);
|
|
57
|
+
this.name = 'INVALID_REF';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.InvalidRefError = InvalidRefError;
|
|
61
|
+
class OCRFallbackFailedError extends ACError {
|
|
62
|
+
constructor(message, data) {
|
|
63
|
+
super(message, -32007, 7, data);
|
|
64
|
+
this.name = 'OCR_FALLBACK_FAILED';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.OCRFallbackFailedError = OCRFallbackFailedError;
|
|
68
|
+
// JSON-RPC standard errors
|
|
69
|
+
class InvalidRequestError extends ACError {
|
|
70
|
+
constructor(message, data) {
|
|
71
|
+
super(message, -32600, 126, data);
|
|
72
|
+
this.name = 'INVALID_REQUEST';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.InvalidRequestError = InvalidRequestError;
|
|
76
|
+
class MethodNotFoundError extends ACError {
|
|
77
|
+
constructor(message, data) {
|
|
78
|
+
super(message, -32601, 126, data);
|
|
79
|
+
this.name = 'METHOD_NOT_FOUND';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.MethodNotFoundError = MethodNotFoundError;
|
|
83
|
+
class InvalidParamsError extends ACError {
|
|
84
|
+
constructor(message, data) {
|
|
85
|
+
super(message, -32602, 126, data);
|
|
86
|
+
this.name = 'INVALID_PARAMS';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.InvalidParamsError = InvalidParamsError;
|
|
90
|
+
// Error code → error class mapping
|
|
91
|
+
const ERROR_MAP = {
|
|
92
|
+
[-32001]: ElementNotFoundError,
|
|
93
|
+
[-32002]: PermissionDeniedError,
|
|
94
|
+
[-32003]: TimeoutError,
|
|
95
|
+
[-32004]: AppNotFoundError,
|
|
96
|
+
[-32005]: WindowNotFoundError,
|
|
97
|
+
[-32006]: InvalidRefError,
|
|
98
|
+
[-32007]: OCRFallbackFailedError,
|
|
99
|
+
[-32600]: InvalidRequestError,
|
|
100
|
+
[-32601]: MethodNotFoundError,
|
|
101
|
+
[-32602]: InvalidParamsError,
|
|
102
|
+
};
|
|
103
|
+
function errorFromCode(code, message, data) {
|
|
104
|
+
const ErrorClass = ERROR_MAP[code];
|
|
105
|
+
if (ErrorClass) {
|
|
106
|
+
return new ErrorClass(message, data);
|
|
107
|
+
}
|
|
108
|
+
return new ACError(message, code, 126, data);
|
|
109
|
+
}
|
|
110
|
+
function exitCodeFromErrorCode(code) {
|
|
111
|
+
const instance = ERROR_MAP[code];
|
|
112
|
+
if (instance) {
|
|
113
|
+
// Create a temporary instance to get the exit code
|
|
114
|
+
const temp = new instance('');
|
|
115
|
+
return temp.exitCode;
|
|
116
|
+
}
|
|
117
|
+
return 126;
|
|
118
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// @skillful-agents/ac — TypeScript SDK for macOS desktop automation
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.CDPInteractions = exports.CDPAXTree = exports.CDPConnection = exports.CDPClient = exports.AC = exports.Bridge = exports.TimeoutError = exports.PermissionDeniedError = exports.ElementNotFoundError = exports.ACError = exports.REF_PREFIXES = exports.refToRole = exports.isValidRef = exports.parseRef = void 0;
|
|
5
|
+
var refs_js_1 = require("./refs.js");
|
|
6
|
+
Object.defineProperty(exports, "parseRef", { enumerable: true, get: function () { return refs_js_1.parseRef; } });
|
|
7
|
+
Object.defineProperty(exports, "isValidRef", { enumerable: true, get: function () { return refs_js_1.isValidRef; } });
|
|
8
|
+
Object.defineProperty(exports, "refToRole", { enumerable: true, get: function () { return refs_js_1.refToRole; } });
|
|
9
|
+
Object.defineProperty(exports, "REF_PREFIXES", { enumerable: true, get: function () { return refs_js_1.REF_PREFIXES; } });
|
|
10
|
+
var errors_js_1 = require("./errors.js");
|
|
11
|
+
Object.defineProperty(exports, "ACError", { enumerable: true, get: function () { return errors_js_1.ACError; } });
|
|
12
|
+
Object.defineProperty(exports, "ElementNotFoundError", { enumerable: true, get: function () { return errors_js_1.ElementNotFoundError; } });
|
|
13
|
+
Object.defineProperty(exports, "PermissionDeniedError", { enumerable: true, get: function () { return errors_js_1.PermissionDeniedError; } });
|
|
14
|
+
Object.defineProperty(exports, "TimeoutError", { enumerable: true, get: function () { return errors_js_1.TimeoutError; } });
|
|
15
|
+
var bridge_js_1 = require("./bridge.js");
|
|
16
|
+
Object.defineProperty(exports, "Bridge", { enumerable: true, get: function () { return bridge_js_1.Bridge; } });
|
|
17
|
+
var sdk_js_1 = require("./sdk.js");
|
|
18
|
+
Object.defineProperty(exports, "AC", { enumerable: true, get: function () { return sdk_js_1.AC; } });
|
|
19
|
+
// CDP support
|
|
20
|
+
var index_js_1 = require("./cdp/index.js");
|
|
21
|
+
Object.defineProperty(exports, "CDPClient", { enumerable: true, get: function () { return index_js_1.CDPClient; } });
|
|
22
|
+
Object.defineProperty(exports, "CDPConnection", { enumerable: true, get: function () { return index_js_1.CDPConnection; } });
|
|
23
|
+
Object.defineProperty(exports, "CDPAXTree", { enumerable: true, get: function () { return index_js_1.CDPAXTree; } });
|
|
24
|
+
Object.defineProperty(exports, "CDPInteractions", { enumerable: true, get: function () { return index_js_1.CDPInteractions; } });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IS_NAMED_PIPE = exports.SOCKET_PATH = exports.SNAPSHOTS_DIR = exports.DAEMON_JSON_PATH = exports.AC_DIR = void 0;
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
const os_1 = require("os");
|
|
6
|
+
const os = (0, os_1.platform)();
|
|
7
|
+
exports.AC_DIR = (0, path_1.join)((0, os_1.homedir)(), '.ac');
|
|
8
|
+
exports.DAEMON_JSON_PATH = (0, path_1.join)(exports.AC_DIR, 'daemon.json');
|
|
9
|
+
exports.SNAPSHOTS_DIR = (0, path_1.join)(exports.AC_DIR, 'snapshots');
|
|
10
|
+
// macOS: Unix domain socket file on disk
|
|
11
|
+
// Windows: Named pipe (kernel object, no file on disk)
|
|
12
|
+
exports.SOCKET_PATH = os === 'win32'
|
|
13
|
+
? '\\\\.\\pipe\\ac-daemon'
|
|
14
|
+
: (0, path_1.join)(exports.AC_DIR, 'daemon.sock');
|
|
15
|
+
// Named pipes don't exist as files — connection-based detection needed
|
|
16
|
+
exports.IS_NAMED_PIPE = os === 'win32';
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveBinary = resolveBinary;
|
|
4
|
+
const os_1 = require("os");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
/**
|
|
8
|
+
* Get directory of this file. Works in both ESM and CJS:
|
|
9
|
+
* - CJS: __dirname is a global provided by Node
|
|
10
|
+
* - ESM: use import.meta.url (wrapped in eval to avoid CJS parse error)
|
|
11
|
+
*/
|
|
12
|
+
function getModuleDir() {
|
|
13
|
+
if (typeof __dirname !== 'undefined')
|
|
14
|
+
return __dirname;
|
|
15
|
+
// ESM fallback — eval hides import.meta from the CJS parser
|
|
16
|
+
// eslint-disable-next-line no-eval
|
|
17
|
+
const url = eval('import.meta.url');
|
|
18
|
+
const { fileURLToPath } = require('url');
|
|
19
|
+
return (0, path_1.dirname)(fileURLToPath(url));
|
|
20
|
+
}
|
|
21
|
+
const _dirname = getModuleDir();
|
|
22
|
+
/**
|
|
23
|
+
* Find the package root by walking up from __dirname until we find package.json.
|
|
24
|
+
* Works whether running from source (src/platform/) or compiled (dist/src/platform/).
|
|
25
|
+
*/
|
|
26
|
+
function findProjectRoot() {
|
|
27
|
+
let dir = _dirname;
|
|
28
|
+
for (let i = 0; i < 5; i++) {
|
|
29
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(dir, 'package.json')))
|
|
30
|
+
return dir;
|
|
31
|
+
dir = (0, path_1.dirname)(dir);
|
|
32
|
+
}
|
|
33
|
+
return (0, path_1.join)(_dirname, '..', '..');
|
|
34
|
+
}
|
|
35
|
+
function resolveBinary() {
|
|
36
|
+
const os = (0, os_1.platform)();
|
|
37
|
+
const cpu = (0, os_1.arch)();
|
|
38
|
+
if (os !== 'darwin' && os !== 'win32') {
|
|
39
|
+
throw new Error(`Unsupported platform: ${os}. agent-computer supports macOS and Windows.`);
|
|
40
|
+
}
|
|
41
|
+
const ext = os === 'win32' ? '.exe' : '';
|
|
42
|
+
const key = `${os}-${cpu === 'arm64' ? 'arm64' : 'x64'}`;
|
|
43
|
+
const projectRoot = findProjectRoot();
|
|
44
|
+
// Bundled platform-specific binary (npm package)
|
|
45
|
+
const bundledPath = (0, path_1.join)(projectRoot, 'bin', `ac-core-${key}${ext}`);
|
|
46
|
+
if ((0, fs_1.existsSync)(bundledPath)) {
|
|
47
|
+
return bundledPath;
|
|
48
|
+
}
|
|
49
|
+
if (os === 'darwin') {
|
|
50
|
+
// Development: locally-built Swift binary (release)
|
|
51
|
+
const devBinaryPath = (0, path_1.join)(projectRoot, 'native', 'macos', '.build', 'release', 'ac-core');
|
|
52
|
+
if ((0, fs_1.existsSync)(devBinaryPath))
|
|
53
|
+
return devBinaryPath;
|
|
54
|
+
// Development: Swift debug build
|
|
55
|
+
const debugBinaryPath = (0, path_1.join)(projectRoot, 'native', 'macos', '.build', 'debug', 'ac-core');
|
|
56
|
+
if ((0, fs_1.existsSync)(debugBinaryPath))
|
|
57
|
+
return debugBinaryPath;
|
|
58
|
+
}
|
|
59
|
+
if (os === 'win32') {
|
|
60
|
+
const rid = cpu === 'arm64' ? 'win-arm64' : 'win-x64';
|
|
61
|
+
const tfms = ['net9.0-windows', 'net9.0'];
|
|
62
|
+
const base = (0, path_1.join)(projectRoot, 'native', 'windows', 'ACCore', 'bin');
|
|
63
|
+
for (const tfm of tfms) {
|
|
64
|
+
// Development: self-contained release publish
|
|
65
|
+
const publishPath = (0, path_1.join)(base, 'Release', tfm, rid, 'publish', 'ac-core.exe');
|
|
66
|
+
if ((0, fs_1.existsSync)(publishPath))
|
|
67
|
+
return publishPath;
|
|
68
|
+
// Development: release build (framework-dependent)
|
|
69
|
+
const releasePath = (0, path_1.join)(base, 'Release', tfm, 'ac-core.exe');
|
|
70
|
+
if ((0, fs_1.existsSync)(releasePath))
|
|
71
|
+
return releasePath;
|
|
72
|
+
// Development: debug build (framework-dependent)
|
|
73
|
+
const debugPath = (0, path_1.join)(base, 'Debug', tfm, 'ac-core.exe');
|
|
74
|
+
if ((0, fs_1.existsSync)(debugPath))
|
|
75
|
+
return debugPath;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const buildHint = os === 'win32'
|
|
79
|
+
? 'cd native/windows && dotnet build'
|
|
80
|
+
: 'cd native/macos && swift build -c release';
|
|
81
|
+
throw new Error(`Native binary not found for ${key}. ` +
|
|
82
|
+
`Build from source: ${buildHint}`);
|
|
83
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.REF_PREFIXES = void 0;
|
|
4
|
+
exports.roleToPrefix = roleToPrefix;
|
|
5
|
+
exports.parseRef = parseRef;
|
|
6
|
+
exports.isValidRef = isValidRef;
|
|
7
|
+
exports.refToRole = refToRole;
|
|
8
|
+
// Single-letter prefix → role mapping
|
|
9
|
+
const SINGLE_LETTER_PREFIXES = {
|
|
10
|
+
b: 'button',
|
|
11
|
+
t: 'textfield',
|
|
12
|
+
l: 'link',
|
|
13
|
+
m: 'menuitem',
|
|
14
|
+
c: 'checkbox',
|
|
15
|
+
r: 'radio',
|
|
16
|
+
s: 'slider',
|
|
17
|
+
d: 'dropdown',
|
|
18
|
+
i: 'image',
|
|
19
|
+
g: 'group',
|
|
20
|
+
w: 'window',
|
|
21
|
+
x: 'table',
|
|
22
|
+
o: 'row',
|
|
23
|
+
a: 'tab',
|
|
24
|
+
e: 'generic',
|
|
25
|
+
};
|
|
26
|
+
// Two-letter prefix → role mapping (for less common roles)
|
|
27
|
+
const TWO_LETTER_PREFIXES = {
|
|
28
|
+
cb: 'combobox',
|
|
29
|
+
sa: 'scrollarea',
|
|
30
|
+
st: 'stepper',
|
|
31
|
+
sp: 'splitgroup',
|
|
32
|
+
tl: 'timeline',
|
|
33
|
+
pg: 'progress',
|
|
34
|
+
tv: 'treeview',
|
|
35
|
+
wb: 'webarea',
|
|
36
|
+
};
|
|
37
|
+
exports.REF_PREFIXES = {
|
|
38
|
+
...SINGLE_LETTER_PREFIXES,
|
|
39
|
+
...TWO_LETTER_PREFIXES,
|
|
40
|
+
};
|
|
41
|
+
// Reverse mapping: role → prefix
|
|
42
|
+
const ROLE_TO_PREFIX = {};
|
|
43
|
+
for (const [prefix, role] of Object.entries(exports.REF_PREFIXES)) {
|
|
44
|
+
// Prefer shorter prefix if role already mapped
|
|
45
|
+
if (!ROLE_TO_PREFIX[role] || prefix.length < ROLE_TO_PREFIX[role].length) {
|
|
46
|
+
ROLE_TO_PREFIX[role] = prefix;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// textfield and textarea both map to 't' — textarea gets 't' by convention
|
|
50
|
+
ROLE_TO_PREFIX['textarea'] = 't';
|
|
51
|
+
function roleToPrefix(role) {
|
|
52
|
+
return ROLE_TO_PREFIX[role] ?? 'e';
|
|
53
|
+
}
|
|
54
|
+
const ALL_PREFIXES = new Set(Object.keys(exports.REF_PREFIXES));
|
|
55
|
+
// Ref pattern: @<prefix><positive integer>
|
|
56
|
+
// Try two-letter prefix first, then single-letter
|
|
57
|
+
const REF_REGEX = /^@([a-z]{1,2})([1-9]\d*)$/;
|
|
58
|
+
function parseRef(input) {
|
|
59
|
+
const match = REF_REGEX.exec(input);
|
|
60
|
+
if (!match) {
|
|
61
|
+
throw new Error(`Invalid ref format: "${input}". Expected @<prefix><number> (e.g., @b1, @cb3)`);
|
|
62
|
+
}
|
|
63
|
+
const rawPrefix = match[1];
|
|
64
|
+
const id = parseInt(match[2], 10);
|
|
65
|
+
// Try two-letter prefix first (e.g., "@cb1" → prefix "cb", not "c" with "b1")
|
|
66
|
+
if (rawPrefix.length === 2 && ALL_PREFIXES.has(rawPrefix)) {
|
|
67
|
+
return { prefix: rawPrefix, role: exports.REF_PREFIXES[rawPrefix], id };
|
|
68
|
+
}
|
|
69
|
+
// Try single-letter prefix
|
|
70
|
+
if (rawPrefix.length === 1 && ALL_PREFIXES.has(rawPrefix)) {
|
|
71
|
+
return { prefix: rawPrefix, role: exports.REF_PREFIXES[rawPrefix], id };
|
|
72
|
+
}
|
|
73
|
+
// Two-letter prefix but only first letter is valid (e.g., "@bx1" is not valid)
|
|
74
|
+
if (rawPrefix.length === 2) {
|
|
75
|
+
// Check if first letter alone is a valid prefix — if so, reject because the second char is part of the number parsing issue
|
|
76
|
+
throw new Error(`Invalid ref prefix: "${rawPrefix}" in "${input}". Valid prefixes: ${[...ALL_PREFIXES].sort().join(', ')}`);
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`Unknown ref prefix: "${rawPrefix}" in "${input}". Valid prefixes: ${[...ALL_PREFIXES].sort().join(', ')}`);
|
|
79
|
+
}
|
|
80
|
+
function isValidRef(input) {
|
|
81
|
+
try {
|
|
82
|
+
parseRef(input);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function refToRole(prefix) {
|
|
90
|
+
return exports.REF_PREFIXES[prefix];
|
|
91
|
+
}
|