@hasna/computer 0.1.1 → 0.1.3
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/dashboard/dist/assets/index-6UXnbsOB.js +40 -0
- package/dashboard/dist/assets/index-CwAxlYtY.css +1 -0
- package/dashboard/dist/index.html +13 -0
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/cli/index.js +442 -15
- package/dist/db/agents.d.ts +26 -0
- package/dist/db/agents.d.ts.map +1 -0
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.d.ts.map +1 -1
- package/dist/drivers/mac/accessibility.d.ts +33 -0
- package/dist/drivers/mac/accessibility.d.ts.map +1 -0
- package/dist/drivers/mac/index.d.ts +7 -1
- package/dist/drivers/mac/index.d.ts.map +1 -1
- package/dist/drivers/mac/screenshot.d.ts +1 -1
- package/dist/drivers/mac/screenshot.d.ts.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +371 -36
- package/dist/lib/integrations.d.ts +30 -0
- package/dist/lib/integrations.d.ts.map +1 -0
- package/dist/lib/terminal-image.d.ts +29 -0
- package/dist/lib/terminal-image.d.ts.map +1 -0
- package/dist/mcp/index.js +347 -36
- package/dist/server/index.js +182 -36
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -1
- package/helpers/accessibility +0 -0
- package/helpers/accessibility.swift +161 -0
- package/package.json +3 -1
package/dist/server/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
+
var __require = import.meta.require;
|
|
4
|
+
|
|
5
|
+
// src/server/index.ts
|
|
6
|
+
import { join as join6, dirname as dirname2 } from "path";
|
|
7
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3
9
|
|
|
4
10
|
// src/agent/loop.ts
|
|
5
11
|
import { randomUUID } from "crypto";
|
|
@@ -9,10 +15,15 @@ import { mkdir } from "fs/promises";
|
|
|
9
15
|
import { tmpdir } from "os";
|
|
10
16
|
import { join } from "path";
|
|
11
17
|
import { readFile, unlink } from "fs/promises";
|
|
12
|
-
async function captureScreenshot() {
|
|
18
|
+
async function captureScreenshot(displayNumber) {
|
|
13
19
|
const timestamp = Date.now();
|
|
14
20
|
const tmpPath = join(tmpdir(), `computer-screenshot-${timestamp}.png`);
|
|
15
|
-
const
|
|
21
|
+
const args = ["screencapture", "-x", "-C", "-t", "png"];
|
|
22
|
+
if (displayNumber) {
|
|
23
|
+
args.push(`-D${displayNumber}`);
|
|
24
|
+
}
|
|
25
|
+
args.push(tmpPath);
|
|
26
|
+
const proc = Bun.spawn(args, {
|
|
16
27
|
stdout: "pipe",
|
|
17
28
|
stderr: "pipe"
|
|
18
29
|
});
|
|
@@ -235,19 +246,23 @@ function getScrollHelperPath() {
|
|
|
235
246
|
|
|
236
247
|
// src/drivers/mac/index.ts
|
|
237
248
|
class MacDriver {
|
|
249
|
+
displayNumber;
|
|
250
|
+
constructor(opts) {
|
|
251
|
+
this.displayNumber = opts?.displayNumber;
|
|
252
|
+
}
|
|
238
253
|
async getScreenSize() {
|
|
239
254
|
return getScreenSize();
|
|
240
255
|
}
|
|
241
256
|
async screenshot() {
|
|
242
|
-
return captureScreenshot();
|
|
257
|
+
return captureScreenshot(this.displayNumber);
|
|
243
258
|
}
|
|
244
259
|
async execute(action) {
|
|
245
260
|
return executeAction(action);
|
|
246
261
|
}
|
|
247
262
|
async dispose() {}
|
|
248
263
|
}
|
|
249
|
-
function createMacDriver() {
|
|
250
|
-
return new MacDriver;
|
|
264
|
+
function createMacDriver(opts) {
|
|
265
|
+
return new MacDriver(opts);
|
|
251
266
|
}
|
|
252
267
|
|
|
253
268
|
// node_modules/@anthropic-ai/sdk/version.mjs
|
|
@@ -9537,7 +9552,7 @@ var __export = (target, all) => {
|
|
|
9537
9552
|
set: __exportSetter.bind(all, name)
|
|
9538
9553
|
});
|
|
9539
9554
|
};
|
|
9540
|
-
var
|
|
9555
|
+
var __require2 = /* @__PURE__ */ createRequire(import.meta.url);
|
|
9541
9556
|
var require_postgres_array = __commonJS((exports) => {
|
|
9542
9557
|
exports.parse = function(source, transform) {
|
|
9543
9558
|
return new ArrayParser(source, transform).parse();
|
|
@@ -10477,7 +10492,7 @@ var require_defaults = __commonJS((exports, module) => {
|
|
|
10477
10492
|
});
|
|
10478
10493
|
var require_utils = __commonJS((exports, module) => {
|
|
10479
10494
|
var defaults2 = require_defaults();
|
|
10480
|
-
var util =
|
|
10495
|
+
var util = __require2("util");
|
|
10481
10496
|
var { isDate } = util.types || util;
|
|
10482
10497
|
function escapeElement(elementRepresentation) {
|
|
10483
10498
|
const escaped = elementRepresentation.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
@@ -10633,7 +10648,7 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
10633
10648
|
};
|
|
10634
10649
|
});
|
|
10635
10650
|
var require_utils_legacy = __commonJS((exports, module) => {
|
|
10636
|
-
var nodeCrypto =
|
|
10651
|
+
var nodeCrypto = __require2("crypto");
|
|
10637
10652
|
function md5(string) {
|
|
10638
10653
|
return nodeCrypto.createHash("md5").update(string, "utf-8").digest("hex");
|
|
10639
10654
|
}
|
|
@@ -10666,7 +10681,7 @@ var require_utils_legacy = __commonJS((exports, module) => {
|
|
|
10666
10681
|
};
|
|
10667
10682
|
});
|
|
10668
10683
|
var require_utils_webcrypto = __commonJS((exports, module) => {
|
|
10669
|
-
var nodeCrypto =
|
|
10684
|
+
var nodeCrypto = __require2("crypto");
|
|
10670
10685
|
module.exports = {
|
|
10671
10686
|
postgresMd5PasswordHash,
|
|
10672
10687
|
randomBytes,
|
|
@@ -11076,7 +11091,7 @@ var require_pg_connection_string = __commonJS((exports, module) => {
|
|
|
11076
11091
|
if (config.sslcert || config.sslkey || config.sslrootcert || config.sslmode) {
|
|
11077
11092
|
config.ssl = {};
|
|
11078
11093
|
}
|
|
11079
|
-
const fs = config.sslcert || config.sslkey || config.sslrootcert ?
|
|
11094
|
+
const fs = config.sslcert || config.sslkey || config.sslrootcert ? __require2("fs") : null;
|
|
11080
11095
|
if (config.sslcert) {
|
|
11081
11096
|
config.ssl.cert = fs.readFileSync(config.sslcert).toString();
|
|
11082
11097
|
}
|
|
@@ -11199,7 +11214,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
|
|
|
11199
11214
|
parse.parseIntoClientConfig = parseIntoClientConfig;
|
|
11200
11215
|
});
|
|
11201
11216
|
var require_connection_parameters = __commonJS((exports, module) => {
|
|
11202
|
-
var dns =
|
|
11217
|
+
var dns = __require2("dns");
|
|
11203
11218
|
var defaults2 = require_defaults();
|
|
11204
11219
|
var parse = require_pg_connection_string().parse;
|
|
11205
11220
|
var val = function(key, config, envVar) {
|
|
@@ -11422,7 +11437,7 @@ var require_result = __commonJS((exports, module) => {
|
|
|
11422
11437
|
module.exports = Result;
|
|
11423
11438
|
});
|
|
11424
11439
|
var require_query = __commonJS((exports, module) => {
|
|
11425
|
-
var { EventEmitter } =
|
|
11440
|
+
var { EventEmitter } = __require2("events");
|
|
11426
11441
|
var Result = require_result();
|
|
11427
11442
|
var utils = require_utils();
|
|
11428
11443
|
|
|
@@ -12405,11 +12420,11 @@ var require_stream = __commonJS((exports, module) => {
|
|
|
12405
12420
|
};
|
|
12406
12421
|
function getNodejsStreamFuncs() {
|
|
12407
12422
|
function getStream2(ssl) {
|
|
12408
|
-
const net =
|
|
12423
|
+
const net = __require2("net");
|
|
12409
12424
|
return new net.Socket;
|
|
12410
12425
|
}
|
|
12411
12426
|
function getSecureStream2(options) {
|
|
12412
|
-
const tls =
|
|
12427
|
+
const tls = __require2("tls");
|
|
12413
12428
|
return tls.connect(options);
|
|
12414
12429
|
}
|
|
12415
12430
|
return {
|
|
@@ -12451,7 +12466,7 @@ var require_stream = __commonJS((exports, module) => {
|
|
|
12451
12466
|
}
|
|
12452
12467
|
});
|
|
12453
12468
|
var require_connection = __commonJS((exports, module) => {
|
|
12454
|
-
var EventEmitter =
|
|
12469
|
+
var EventEmitter = __require2("events").EventEmitter;
|
|
12455
12470
|
var { parse, serialize } = require_dist();
|
|
12456
12471
|
var { getStream, getSecureStream } = require_stream();
|
|
12457
12472
|
var flushBuffer = serialize.flush();
|
|
@@ -12524,7 +12539,7 @@ var require_connection = __commonJS((exports, module) => {
|
|
|
12524
12539
|
options.key = self.ssl.key;
|
|
12525
12540
|
}
|
|
12526
12541
|
}
|
|
12527
|
-
const net =
|
|
12542
|
+
const net = __require2("net");
|
|
12528
12543
|
if (net.isIP && net.isIP(host) === 0) {
|
|
12529
12544
|
options.servername = host;
|
|
12530
12545
|
}
|
|
@@ -12627,8 +12642,8 @@ var require_connection = __commonJS((exports, module) => {
|
|
|
12627
12642
|
module.exports = Connection;
|
|
12628
12643
|
});
|
|
12629
12644
|
var require_split2 = __commonJS((exports, module) => {
|
|
12630
|
-
var { Transform } =
|
|
12631
|
-
var { StringDecoder } =
|
|
12645
|
+
var { Transform } = __require2("stream");
|
|
12646
|
+
var { StringDecoder } = __require2("string_decoder");
|
|
12632
12647
|
var kLast = Symbol("last");
|
|
12633
12648
|
var kDecoder = Symbol("decoder");
|
|
12634
12649
|
function transform(chunk, enc, cb) {
|
|
@@ -12724,10 +12739,10 @@ var require_split2 = __commonJS((exports, module) => {
|
|
|
12724
12739
|
module.exports = split;
|
|
12725
12740
|
});
|
|
12726
12741
|
var require_helper = __commonJS((exports, module) => {
|
|
12727
|
-
var path =
|
|
12728
|
-
var Stream3 =
|
|
12742
|
+
var path = __require2("path");
|
|
12743
|
+
var Stream3 = __require2("stream").Stream;
|
|
12729
12744
|
var split = require_split2();
|
|
12730
|
-
var util =
|
|
12745
|
+
var util = __require2("util");
|
|
12731
12746
|
var defaultPort = 5432;
|
|
12732
12747
|
var isWin = process.platform === "win32";
|
|
12733
12748
|
var warnStream = process.stderr;
|
|
@@ -12886,8 +12901,8 @@ var require_helper = __commonJS((exports, module) => {
|
|
|
12886
12901
|
};
|
|
12887
12902
|
});
|
|
12888
12903
|
var require_lib = __commonJS((exports, module) => {
|
|
12889
|
-
var path =
|
|
12890
|
-
var fs =
|
|
12904
|
+
var path = __require2("path");
|
|
12905
|
+
var fs = __require2("fs");
|
|
12891
12906
|
var helper = require_helper();
|
|
12892
12907
|
module.exports = function(connInfo, cb) {
|
|
12893
12908
|
var file = helper.getFileName();
|
|
@@ -12902,9 +12917,9 @@ var require_lib = __commonJS((exports, module) => {
|
|
|
12902
12917
|
module.exports.warnTo = helper.warnTo;
|
|
12903
12918
|
});
|
|
12904
12919
|
var require_client = __commonJS((exports, module) => {
|
|
12905
|
-
var EventEmitter =
|
|
12920
|
+
var EventEmitter = __require2("events").EventEmitter;
|
|
12906
12921
|
var utils = require_utils();
|
|
12907
|
-
var nodeUtils =
|
|
12922
|
+
var nodeUtils = __require2("util");
|
|
12908
12923
|
var sasl = require_sasl();
|
|
12909
12924
|
var TypeOverrides = require_type_overrides();
|
|
12910
12925
|
var ConnectionParameters = require_connection_parameters();
|
|
@@ -13481,7 +13496,7 @@ var require_client = __commonJS((exports, module) => {
|
|
|
13481
13496
|
module.exports = Client;
|
|
13482
13497
|
});
|
|
13483
13498
|
var require_pg_pool = __commonJS((exports, module) => {
|
|
13484
|
-
var EventEmitter =
|
|
13499
|
+
var EventEmitter = __require2("events").EventEmitter;
|
|
13485
13500
|
var NOOP = function() {};
|
|
13486
13501
|
var removeWhere = (list, predicate) => {
|
|
13487
13502
|
const i = list.findIndex(predicate);
|
|
@@ -13891,8 +13906,8 @@ var require_pg_pool = __commonJS((exports, module) => {
|
|
|
13891
13906
|
module.exports = Pool;
|
|
13892
13907
|
});
|
|
13893
13908
|
var require_query2 = __commonJS((exports, module) => {
|
|
13894
|
-
var EventEmitter =
|
|
13895
|
-
var util =
|
|
13909
|
+
var EventEmitter = __require2("events").EventEmitter;
|
|
13910
|
+
var util = __require2("util");
|
|
13896
13911
|
var utils = require_utils();
|
|
13897
13912
|
var NativeQuery = module.exports = function(config, values, callback) {
|
|
13898
13913
|
EventEmitter.call(this);
|
|
@@ -14025,7 +14040,7 @@ var require_query2 = __commonJS((exports, module) => {
|
|
|
14025
14040
|
};
|
|
14026
14041
|
});
|
|
14027
14042
|
var require_client2 = __commonJS((exports, module) => {
|
|
14028
|
-
var nodeUtils =
|
|
14043
|
+
var nodeUtils = __require2("util");
|
|
14029
14044
|
var Native;
|
|
14030
14045
|
try {
|
|
14031
14046
|
Native = (() => {
|
|
@@ -14035,8 +14050,8 @@ var require_client2 = __commonJS((exports, module) => {
|
|
|
14035
14050
|
throw e;
|
|
14036
14051
|
}
|
|
14037
14052
|
var TypeOverrides = require_type_overrides();
|
|
14038
|
-
var EventEmitter =
|
|
14039
|
-
var util =
|
|
14053
|
+
var EventEmitter = __require2("events").EventEmitter;
|
|
14054
|
+
var util = __require2("util");
|
|
14040
14055
|
var ConnectionParameters = require_connection_parameters();
|
|
14041
14056
|
var NativeQuery = require_query2();
|
|
14042
14057
|
var queryQueueLengthDeprecationNotice = nodeUtils.deprecate(() => {}, "Calling client.query() when the client is already executing a query is deprecated and will be removed in pg@9.0. Use async/await or an external async flow control mechanism instead.");
|
|
@@ -18530,6 +18545,7 @@ function getDb() {
|
|
|
18530
18545
|
total_tokens_in INTEGER NOT NULL DEFAULT 0,
|
|
18531
18546
|
total_tokens_out INTEGER NOT NULL DEFAULT 0,
|
|
18532
18547
|
total_duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
18548
|
+
tags TEXT,
|
|
18533
18549
|
error TEXT,
|
|
18534
18550
|
created_at TEXT NOT NULL,
|
|
18535
18551
|
completed_at TEXT
|
|
@@ -18585,9 +18601,9 @@ function getDb() {
|
|
|
18585
18601
|
async function createSession(session) {
|
|
18586
18602
|
const d = getDb();
|
|
18587
18603
|
d.prepare(`
|
|
18588
|
-
INSERT INTO sessions (id, task, provider, model, status, steps, total_tokens_in, total_tokens_out, total_duration_ms, error, created_at, completed_at)
|
|
18589
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
18590
|
-
`).run(session.id, session.task, session.provider, session.model, session.status, session.steps, session.total_tokens_in, session.total_tokens_out, session.total_duration_ms, session.error ?? null, session.created_at, session.completed_at ?? null);
|
|
18604
|
+
INSERT INTO sessions (id, task, provider, model, status, steps, total_tokens_in, total_tokens_out, total_duration_ms, tags, error, created_at, completed_at)
|
|
18605
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
18606
|
+
`).run(session.id, session.task, session.provider, session.model, session.status, session.steps, session.total_tokens_in, session.total_tokens_out, session.total_duration_ms, session.tags?.length ? JSON.stringify(session.tags) : null, session.error ?? null, session.created_at, session.completed_at ?? null);
|
|
18591
18607
|
}
|
|
18592
18608
|
async function updateSession(session) {
|
|
18593
18609
|
const d = getDb();
|
|
@@ -18615,10 +18631,18 @@ function listSessions(opts) {
|
|
|
18615
18631
|
const d = getDb();
|
|
18616
18632
|
let sql = "SELECT * FROM sessions";
|
|
18617
18633
|
const params = [];
|
|
18634
|
+
const conditions = [];
|
|
18618
18635
|
if (opts?.status) {
|
|
18619
|
-
|
|
18636
|
+
conditions.push("status = ?");
|
|
18620
18637
|
params.push(opts.status);
|
|
18621
18638
|
}
|
|
18639
|
+
if (opts?.tag) {
|
|
18640
|
+
conditions.push("tags LIKE ?");
|
|
18641
|
+
params.push(`%"${opts.tag}"%`);
|
|
18642
|
+
}
|
|
18643
|
+
if (conditions.length > 0) {
|
|
18644
|
+
sql += " WHERE " + conditions.join(" AND ");
|
|
18645
|
+
}
|
|
18622
18646
|
sql += " ORDER BY created_at DESC";
|
|
18623
18647
|
if (opts?.limit) {
|
|
18624
18648
|
sql += " LIMIT ?";
|
|
@@ -18684,12 +18708,104 @@ function rowToSession(row) {
|
|
|
18684
18708
|
total_tokens_in: row.total_tokens_in,
|
|
18685
18709
|
total_tokens_out: row.total_tokens_out,
|
|
18686
18710
|
total_duration_ms: row.total_duration_ms,
|
|
18711
|
+
tags: row.tags ? JSON.parse(row.tags) : undefined,
|
|
18687
18712
|
error: row.error,
|
|
18688
18713
|
created_at: row.created_at,
|
|
18689
18714
|
completed_at: row.completed_at
|
|
18690
18715
|
};
|
|
18691
18716
|
}
|
|
18692
18717
|
|
|
18718
|
+
// src/lib/integrations.ts
|
|
18719
|
+
async function saveToRecordings(session, logs) {
|
|
18720
|
+
try {
|
|
18721
|
+
const { saveRecording } = await import("@hasna/recordings");
|
|
18722
|
+
await saveRecording({
|
|
18723
|
+
title: `Computer Use: ${session.task.slice(0, 100)}`,
|
|
18724
|
+
type: "computer-use",
|
|
18725
|
+
source: "computer",
|
|
18726
|
+
duration_ms: session.total_duration_ms,
|
|
18727
|
+
metadata: {
|
|
18728
|
+
session_id: session.id,
|
|
18729
|
+
provider: session.provider,
|
|
18730
|
+
model: session.model,
|
|
18731
|
+
steps: session.steps,
|
|
18732
|
+
tokens_in: session.total_tokens_in,
|
|
18733
|
+
tokens_out: session.total_tokens_out,
|
|
18734
|
+
status: session.status,
|
|
18735
|
+
tags: session.tags
|
|
18736
|
+
},
|
|
18737
|
+
transcript: logs.map((l) => ({
|
|
18738
|
+
step: l.step,
|
|
18739
|
+
action: l.action.type,
|
|
18740
|
+
reasoning: l.reasoning?.slice(0, 200),
|
|
18741
|
+
success: l.success,
|
|
18742
|
+
timestamp: l.created_at
|
|
18743
|
+
}))
|
|
18744
|
+
});
|
|
18745
|
+
return true;
|
|
18746
|
+
} catch {
|
|
18747
|
+
return false;
|
|
18748
|
+
}
|
|
18749
|
+
}
|
|
18750
|
+
async function registerWithSessions(session) {
|
|
18751
|
+
try {
|
|
18752
|
+
const mod = await import("@hasna/sessions");
|
|
18753
|
+
const registerSession = mod.registerSession ?? mod.createSession ?? mod.saveSession;
|
|
18754
|
+
if (typeof registerSession !== "function")
|
|
18755
|
+
return false;
|
|
18756
|
+
await registerSession({
|
|
18757
|
+
id: session.id,
|
|
18758
|
+
type: "computer-use",
|
|
18759
|
+
source: "computer",
|
|
18760
|
+
status: session.status,
|
|
18761
|
+
metadata: {
|
|
18762
|
+
task: session.task,
|
|
18763
|
+
provider: session.provider,
|
|
18764
|
+
model: session.model,
|
|
18765
|
+
steps: session.steps
|
|
18766
|
+
},
|
|
18767
|
+
started_at: session.created_at,
|
|
18768
|
+
ended_at: session.completed_at
|
|
18769
|
+
});
|
|
18770
|
+
return true;
|
|
18771
|
+
} catch {
|
|
18772
|
+
return false;
|
|
18773
|
+
}
|
|
18774
|
+
}
|
|
18775
|
+
async function pushToLogs(session, logs) {
|
|
18776
|
+
try {
|
|
18777
|
+
const mod = await import("@hasna/logs");
|
|
18778
|
+
const pushBatch = mod.logPushBatch ?? mod.pushBatch;
|
|
18779
|
+
if (typeof pushBatch !== "function")
|
|
18780
|
+
return false;
|
|
18781
|
+
await pushBatch(logs.map((l) => ({
|
|
18782
|
+
level: l.success ? "info" : "error",
|
|
18783
|
+
source: "computer",
|
|
18784
|
+
message: `[${l.action.type}] ${l.reasoning?.slice(0, 100) ?? ""}`,
|
|
18785
|
+
metadata: {
|
|
18786
|
+
session_id: session.id,
|
|
18787
|
+
step: l.step,
|
|
18788
|
+
action_type: l.action.type,
|
|
18789
|
+
success: l.success,
|
|
18790
|
+
error: l.error,
|
|
18791
|
+
duration_ms: l.duration_ms
|
|
18792
|
+
},
|
|
18793
|
+
timestamp: l.created_at
|
|
18794
|
+
})));
|
|
18795
|
+
return true;
|
|
18796
|
+
} catch {
|
|
18797
|
+
return false;
|
|
18798
|
+
}
|
|
18799
|
+
}
|
|
18800
|
+
async function runPostSessionIntegrations(session, logs) {
|
|
18801
|
+
const [recordings, sessions, logsPushed] = await Promise.all([
|
|
18802
|
+
saveToRecordings(session, logs),
|
|
18803
|
+
registerWithSessions(session),
|
|
18804
|
+
pushToLogs(session, logs)
|
|
18805
|
+
]);
|
|
18806
|
+
return { recordings, sessions, logs: logsPushed };
|
|
18807
|
+
}
|
|
18808
|
+
|
|
18693
18809
|
// src/agent/loop.ts
|
|
18694
18810
|
var DEFAULT_MAX_STEPS = 50;
|
|
18695
18811
|
async function runTask(options) {
|
|
@@ -18703,10 +18819,12 @@ async function runTask(options) {
|
|
|
18703
18819
|
systemPrompt,
|
|
18704
18820
|
screenshotMaxWidth,
|
|
18705
18821
|
dryRun = false,
|
|
18822
|
+
tags,
|
|
18823
|
+
displayNumber,
|
|
18706
18824
|
onStep,
|
|
18707
18825
|
onDone
|
|
18708
18826
|
} = options;
|
|
18709
|
-
const driver = createMacDriver();
|
|
18827
|
+
const driver = createMacDriver({ displayNumber });
|
|
18710
18828
|
const provider = createProvider(providerName, { model });
|
|
18711
18829
|
const config = loadConfig();
|
|
18712
18830
|
const safetyConfig = config.safety;
|
|
@@ -18717,6 +18835,7 @@ async function runTask(options) {
|
|
|
18717
18835
|
provider: providerName,
|
|
18718
18836
|
model: model ?? (providerName === "anthropic" ? "claude-sonnet-4-5-20250514" : "computer-use-preview"),
|
|
18719
18837
|
status: "running",
|
|
18838
|
+
tags,
|
|
18720
18839
|
steps: 0,
|
|
18721
18840
|
total_tokens_in: 0,
|
|
18722
18841
|
total_tokens_out: 0,
|
|
@@ -18769,6 +18888,8 @@ async function runTask(options) {
|
|
|
18769
18888
|
});
|
|
18770
18889
|
await updateSession(session);
|
|
18771
18890
|
onStep?.(step, response, { success: true, duration_ms: 0 });
|
|
18891
|
+
const logs = getActionLogs(sessionId);
|
|
18892
|
+
await runPostSessionIntegrations(session, logs).catch(() => {});
|
|
18772
18893
|
onDone?.(session);
|
|
18773
18894
|
await driver.dispose();
|
|
18774
18895
|
return session;
|
|
@@ -18834,6 +18955,8 @@ async function runTask(options) {
|
|
|
18834
18955
|
session.completed_at = new Date().toISOString();
|
|
18835
18956
|
session.error = `Reached max steps (${maxSteps})`;
|
|
18836
18957
|
await updateSession(session);
|
|
18958
|
+
const endLogs = getActionLogs(sessionId);
|
|
18959
|
+
await runPostSessionIntegrations(session, endLogs).catch(() => {});
|
|
18837
18960
|
onDone?.(session);
|
|
18838
18961
|
await driver.dispose();
|
|
18839
18962
|
return session;
|
|
@@ -18843,6 +18966,8 @@ async function runTask(options) {
|
|
|
18843
18966
|
session.total_duration_ms = Date.now() - startTime;
|
|
18844
18967
|
session.completed_at = new Date().toISOString();
|
|
18845
18968
|
await updateSession(session);
|
|
18969
|
+
const errLogs = getActionLogs(sessionId);
|
|
18970
|
+
await runPostSessionIntegrations(session, errLogs).catch(() => {});
|
|
18846
18971
|
onDone?.(session);
|
|
18847
18972
|
await driver.dispose();
|
|
18848
18973
|
return session;
|
|
@@ -18872,6 +18997,12 @@ function remapCoordinates(action, from, to) {
|
|
|
18872
18997
|
|
|
18873
18998
|
// src/server/index.ts
|
|
18874
18999
|
var PORT = parseInt(process.env.COMPUTER_PORT ?? "19450");
|
|
19000
|
+
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
19001
|
+
var DASHBOARD_DIRS = [
|
|
19002
|
+
join6(__dirname2, "..", "..", "dashboard", "dist"),
|
|
19003
|
+
join6(__dirname2, "..", "dashboard", "dist")
|
|
19004
|
+
];
|
|
19005
|
+
var DASHBOARD_DIR = DASHBOARD_DIRS.find((d) => existsSync4(d));
|
|
18875
19006
|
var server = Bun.serve({
|
|
18876
19007
|
port: PORT,
|
|
18877
19008
|
async fetch(req) {
|
|
@@ -18939,6 +19070,21 @@ var server = Bun.serve({
|
|
|
18939
19070
|
if (method === "GET" && (path === "/health" || path === "/")) {
|
|
18940
19071
|
return Response.json({ status: "ok", name: "computer", version: "0.1.0", port: PORT }, { headers: corsHeaders });
|
|
18941
19072
|
}
|
|
19073
|
+
if (DASHBOARD_DIR && method === "GET" && (path.startsWith("/dashboard") || path === "/")) {
|
|
19074
|
+
if (path === "/" || path === "/dashboard" || path === "/dashboard/") {
|
|
19075
|
+
return new Response(Bun.file(join6(DASHBOARD_DIR, "index.html")), {
|
|
19076
|
+
headers: { "Content-Type": "text/html", "Access-Control-Allow-Origin": "*" }
|
|
19077
|
+
});
|
|
19078
|
+
}
|
|
19079
|
+
const filePath = path.replace("/dashboard", "");
|
|
19080
|
+
const fullPath = join6(DASHBOARD_DIR, filePath);
|
|
19081
|
+
if (existsSync4(fullPath)) {
|
|
19082
|
+
return new Response(Bun.file(fullPath), { headers: { "Access-Control-Allow-Origin": "*" } });
|
|
19083
|
+
}
|
|
19084
|
+
return new Response(Bun.file(join6(DASHBOARD_DIR, "index.html")), {
|
|
19085
|
+
headers: { "Content-Type": "text/html", "Access-Control-Allow-Origin": "*" }
|
|
19086
|
+
});
|
|
19087
|
+
}
|
|
18942
19088
|
return Response.json({ error: "Not found" }, { status: 404, headers: corsHeaders });
|
|
18943
19089
|
} catch (err) {
|
|
18944
19090
|
const message = err instanceof Error ? err.message : String(err);
|
package/dist/types/index.d.ts
CHANGED
|
@@ -128,6 +128,7 @@ export interface Session {
|
|
|
128
128
|
total_tokens_in: number;
|
|
129
129
|
total_tokens_out: number;
|
|
130
130
|
total_duration_ms: number;
|
|
131
|
+
tags?: string[];
|
|
131
132
|
error?: string;
|
|
132
133
|
created_at: string;
|
|
133
134
|
completed_at?: string;
|
|
@@ -152,6 +153,10 @@ export interface RunOptions {
|
|
|
152
153
|
screenshotMaxWidth?: number;
|
|
153
154
|
/** Dry-run mode — model plans actions but they are not executed */
|
|
154
155
|
dryRun?: boolean;
|
|
156
|
+
/** Tags for this session */
|
|
157
|
+
tags?: string[];
|
|
158
|
+
/** Display number to capture (1=main, 2=secondary, etc.) */
|
|
159
|
+
displayNumber?: number;
|
|
155
160
|
/** Callback for each step */
|
|
156
161
|
onStep?: (step: number, response: ModelResponse, result: ActionResult) => void;
|
|
157
162
|
/** Callback when done */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
Binary file
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env swift
|
|
2
|
+
// accessibility.swift — Query macOS Accessibility tree (AXUIElement)
|
|
3
|
+
// Usage: accessibility [--app <name>] [--focused] [--depth <n>]
|
|
4
|
+
// Output: JSON array of UI elements with role, title, position, size
|
|
5
|
+
//
|
|
6
|
+
// Requires: Accessibility permissions in System Settings
|
|
7
|
+
|
|
8
|
+
import AppKit
|
|
9
|
+
import Foundation
|
|
10
|
+
|
|
11
|
+
struct UIElement: Codable {
|
|
12
|
+
let role: String?
|
|
13
|
+
let title: String?
|
|
14
|
+
let value: String?
|
|
15
|
+
let label: String?
|
|
16
|
+
let x: Int
|
|
17
|
+
let y: Int
|
|
18
|
+
let width: Int
|
|
19
|
+
let height: Int
|
|
20
|
+
let enabled: Bool
|
|
21
|
+
let focused: Bool
|
|
22
|
+
let children: Int
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
func getAXValue<T>(_ element: AXUIElement, _ attribute: String) -> T? {
|
|
26
|
+
var value: AnyObject?
|
|
27
|
+
let result = AXUIElementCopyAttributeValue(element, attribute as CFString, &value)
|
|
28
|
+
guard result == .success else { return nil }
|
|
29
|
+
return value as? T
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func getPosition(_ element: AXUIElement) -> CGPoint? {
|
|
33
|
+
var value: AnyObject?
|
|
34
|
+
let result = AXUIElementCopyAttributeValue(element, kAXPositionAttribute as String as CFString, &value)
|
|
35
|
+
guard result == .success, let val = value else { return nil }
|
|
36
|
+
var point = CGPoint.zero
|
|
37
|
+
AXValueGetValue(val as! AXValue, .cgPoint, &point)
|
|
38
|
+
return point
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func getSize(_ element: AXUIElement) -> CGSize? {
|
|
42
|
+
var value: AnyObject?
|
|
43
|
+
let result = AXUIElementCopyAttributeValue(element, kAXSizeAttribute as String as CFString, &value)
|
|
44
|
+
guard result == .success, let val = value else { return nil }
|
|
45
|
+
var size = CGSize.zero
|
|
46
|
+
AXValueGetValue(val as! AXValue, .cgSize, &size)
|
|
47
|
+
return size
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func getChildren(_ element: AXUIElement) -> [AXUIElement] {
|
|
51
|
+
var value: AnyObject?
|
|
52
|
+
let result = AXUIElementCopyAttributeValue(element, kAXChildrenAttribute as String as CFString, &value)
|
|
53
|
+
guard result == .success, let children = value as? [AXUIElement] else { return [] }
|
|
54
|
+
return children
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
func elementToStruct(_ element: AXUIElement) -> UIElement {
|
|
58
|
+
let role: String? = getAXValue(element, kAXRoleAttribute as String)
|
|
59
|
+
let title: String? = getAXValue(element, kAXTitleAttribute as String)
|
|
60
|
+
let value: String? = getAXValue(element, kAXValueAttribute as String)
|
|
61
|
+
let label: String? = getAXValue(element, kAXDescriptionAttribute as String)
|
|
62
|
+
let enabled: Bool = getAXValue(element, kAXEnabledAttribute as String) ?? true
|
|
63
|
+
let focused: Bool = getAXValue(element, kAXFocusedAttribute as String) ?? false
|
|
64
|
+
|
|
65
|
+
let pos = getPosition(element) ?? CGPoint.zero
|
|
66
|
+
let sz = getSize(element) ?? CGSize.zero
|
|
67
|
+
let children = getChildren(element)
|
|
68
|
+
|
|
69
|
+
return UIElement(
|
|
70
|
+
role: role,
|
|
71
|
+
title: title,
|
|
72
|
+
value: (value?.count ?? 0) > 200 ? String(value!.prefix(200)) + "..." : value,
|
|
73
|
+
label: label,
|
|
74
|
+
x: Int(pos.x),
|
|
75
|
+
y: Int(pos.y),
|
|
76
|
+
width: Int(sz.width),
|
|
77
|
+
height: Int(sz.height),
|
|
78
|
+
enabled: enabled,
|
|
79
|
+
focused: focused,
|
|
80
|
+
children: children.count
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
func walkTree(_ element: AXUIElement, depth: Int, maxDepth: Int) -> [UIElement] {
|
|
85
|
+
var results: [UIElement] = []
|
|
86
|
+
let el = elementToStruct(element)
|
|
87
|
+
|
|
88
|
+
// Skip tiny/invisible elements
|
|
89
|
+
if el.width > 5 && el.height > 5 {
|
|
90
|
+
results.append(el)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if depth < maxDepth {
|
|
94
|
+
for child in getChildren(element) {
|
|
95
|
+
results.append(contentsOf: walkTree(child, depth: depth + 1, maxDepth: maxDepth))
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return results
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Parse arguments
|
|
103
|
+
var appName: String? = nil
|
|
104
|
+
var focusedOnly = false
|
|
105
|
+
var maxDepth = 3
|
|
106
|
+
|
|
107
|
+
var args = Array(CommandLine.arguments.dropFirst())
|
|
108
|
+
while !args.isEmpty {
|
|
109
|
+
let arg = args.removeFirst()
|
|
110
|
+
switch arg {
|
|
111
|
+
case "--app":
|
|
112
|
+
if !args.isEmpty { appName = args.removeFirst() }
|
|
113
|
+
case "--focused":
|
|
114
|
+
focusedOnly = true
|
|
115
|
+
case "--depth":
|
|
116
|
+
if !args.isEmpty { maxDepth = Int(args.removeFirst()) ?? 3 }
|
|
117
|
+
default:
|
|
118
|
+
break
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Get target application
|
|
123
|
+
var targetApp: NSRunningApplication?
|
|
124
|
+
if let name = appName {
|
|
125
|
+
targetApp = NSWorkspace.shared.runningApplications.first {
|
|
126
|
+
$0.localizedName?.lowercased() == name.lowercased()
|
|
127
|
+
}
|
|
128
|
+
if targetApp == nil {
|
|
129
|
+
fputs("Error: Application '\(name)' not found\n", stderr)
|
|
130
|
+
exit(1)
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
targetApp = NSWorkspace.shared.frontmostApplication
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
guard let app = targetApp, let pid = Optional(app.processIdentifier) else {
|
|
137
|
+
fputs("Error: No application found\n", stderr)
|
|
138
|
+
exit(1)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let appElement = AXUIElementCreateApplication(pid)
|
|
142
|
+
|
|
143
|
+
var elements: [UIElement]
|
|
144
|
+
if focusedOnly {
|
|
145
|
+
var focusedElement: AnyObject?
|
|
146
|
+
AXUIElementCopyAttributeValue(appElement, kAXFocusedUIElementAttribute as CFString, &focusedElement)
|
|
147
|
+
if let focused = focusedElement {
|
|
148
|
+
elements = walkTree(focused as! AXUIElement, depth: 0, maxDepth: maxDepth)
|
|
149
|
+
} else {
|
|
150
|
+
elements = []
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
elements = walkTree(appElement, depth: 0, maxDepth: maxDepth)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Output as JSON
|
|
157
|
+
let encoder = JSONEncoder()
|
|
158
|
+
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
|
|
159
|
+
if let data = try? encoder.encode(elements) {
|
|
160
|
+
print(String(data: data, encoding: .utf8)!)
|
|
161
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/computer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Open-source computer use for AI agents — control your Mac with Anthropic or OpenAI. CLI + MCP server + REST API + Dashboard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
"dist",
|
|
21
21
|
"helpers/scroll",
|
|
22
22
|
"helpers/scroll.swift",
|
|
23
|
+
"helpers/accessibility",
|
|
24
|
+
"helpers/accessibility.swift",
|
|
23
25
|
"src/db/migrations",
|
|
24
26
|
"dashboard/dist",
|
|
25
27
|
"LICENSE",
|