@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/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
var __require = import.meta.require;
|
|
3
|
+
|
|
2
4
|
// src/agent/loop.ts
|
|
3
5
|
import { randomUUID } from "crypto";
|
|
4
6
|
import { mkdir } from "fs/promises";
|
|
@@ -7,10 +9,15 @@ import { mkdir } from "fs/promises";
|
|
|
7
9
|
import { tmpdir } from "os";
|
|
8
10
|
import { join } from "path";
|
|
9
11
|
import { readFile, unlink } from "fs/promises";
|
|
10
|
-
async function captureScreenshot() {
|
|
12
|
+
async function captureScreenshot(displayNumber) {
|
|
11
13
|
const timestamp = Date.now();
|
|
12
14
|
const tmpPath = join(tmpdir(), `computer-screenshot-${timestamp}.png`);
|
|
13
|
-
const
|
|
15
|
+
const args = ["screencapture", "-x", "-C", "-t", "png"];
|
|
16
|
+
if (displayNumber) {
|
|
17
|
+
args.push(`-D${displayNumber}`);
|
|
18
|
+
}
|
|
19
|
+
args.push(tmpPath);
|
|
20
|
+
const proc = Bun.spawn(args, {
|
|
14
21
|
stdout: "pipe",
|
|
15
22
|
stderr: "pipe"
|
|
16
23
|
});
|
|
@@ -233,19 +240,23 @@ function getScrollHelperPath() {
|
|
|
233
240
|
|
|
234
241
|
// src/drivers/mac/index.ts
|
|
235
242
|
class MacDriver {
|
|
243
|
+
displayNumber;
|
|
244
|
+
constructor(opts) {
|
|
245
|
+
this.displayNumber = opts?.displayNumber;
|
|
246
|
+
}
|
|
236
247
|
async getScreenSize() {
|
|
237
248
|
return getScreenSize();
|
|
238
249
|
}
|
|
239
250
|
async screenshot() {
|
|
240
|
-
return captureScreenshot();
|
|
251
|
+
return captureScreenshot(this.displayNumber);
|
|
241
252
|
}
|
|
242
253
|
async execute(action) {
|
|
243
254
|
return executeAction(action);
|
|
244
255
|
}
|
|
245
256
|
async dispose() {}
|
|
246
257
|
}
|
|
247
|
-
function createMacDriver() {
|
|
248
|
-
return new MacDriver;
|
|
258
|
+
function createMacDriver(opts) {
|
|
259
|
+
return new MacDriver(opts);
|
|
249
260
|
}
|
|
250
261
|
|
|
251
262
|
// node_modules/@anthropic-ai/sdk/version.mjs
|
|
@@ -9586,7 +9597,7 @@ var __export = (target, all) => {
|
|
|
9586
9597
|
set: __exportSetter.bind(all, name)
|
|
9587
9598
|
});
|
|
9588
9599
|
};
|
|
9589
|
-
var
|
|
9600
|
+
var __require2 = /* @__PURE__ */ createRequire(import.meta.url);
|
|
9590
9601
|
var require_postgres_array = __commonJS((exports) => {
|
|
9591
9602
|
exports.parse = function(source, transform) {
|
|
9592
9603
|
return new ArrayParser(source, transform).parse();
|
|
@@ -10526,7 +10537,7 @@ var require_defaults = __commonJS((exports, module) => {
|
|
|
10526
10537
|
});
|
|
10527
10538
|
var require_utils = __commonJS((exports, module) => {
|
|
10528
10539
|
var defaults2 = require_defaults();
|
|
10529
|
-
var util =
|
|
10540
|
+
var util = __require2("util");
|
|
10530
10541
|
var { isDate } = util.types || util;
|
|
10531
10542
|
function escapeElement(elementRepresentation) {
|
|
10532
10543
|
const escaped = elementRepresentation.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
@@ -10682,7 +10693,7 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
10682
10693
|
};
|
|
10683
10694
|
});
|
|
10684
10695
|
var require_utils_legacy = __commonJS((exports, module) => {
|
|
10685
|
-
var nodeCrypto =
|
|
10696
|
+
var nodeCrypto = __require2("crypto");
|
|
10686
10697
|
function md5(string) {
|
|
10687
10698
|
return nodeCrypto.createHash("md5").update(string, "utf-8").digest("hex");
|
|
10688
10699
|
}
|
|
@@ -10715,7 +10726,7 @@ var require_utils_legacy = __commonJS((exports, module) => {
|
|
|
10715
10726
|
};
|
|
10716
10727
|
});
|
|
10717
10728
|
var require_utils_webcrypto = __commonJS((exports, module) => {
|
|
10718
|
-
var nodeCrypto =
|
|
10729
|
+
var nodeCrypto = __require2("crypto");
|
|
10719
10730
|
module.exports = {
|
|
10720
10731
|
postgresMd5PasswordHash,
|
|
10721
10732
|
randomBytes,
|
|
@@ -11125,7 +11136,7 @@ var require_pg_connection_string = __commonJS((exports, module) => {
|
|
|
11125
11136
|
if (config.sslcert || config.sslkey || config.sslrootcert || config.sslmode) {
|
|
11126
11137
|
config.ssl = {};
|
|
11127
11138
|
}
|
|
11128
|
-
const fs = config.sslcert || config.sslkey || config.sslrootcert ?
|
|
11139
|
+
const fs = config.sslcert || config.sslkey || config.sslrootcert ? __require2("fs") : null;
|
|
11129
11140
|
if (config.sslcert) {
|
|
11130
11141
|
config.ssl.cert = fs.readFileSync(config.sslcert).toString();
|
|
11131
11142
|
}
|
|
@@ -11248,7 +11259,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
|
|
|
11248
11259
|
parse.parseIntoClientConfig = parseIntoClientConfig;
|
|
11249
11260
|
});
|
|
11250
11261
|
var require_connection_parameters = __commonJS((exports, module) => {
|
|
11251
|
-
var dns =
|
|
11262
|
+
var dns = __require2("dns");
|
|
11252
11263
|
var defaults2 = require_defaults();
|
|
11253
11264
|
var parse = require_pg_connection_string().parse;
|
|
11254
11265
|
var val = function(key, config, envVar) {
|
|
@@ -11471,7 +11482,7 @@ var require_result = __commonJS((exports, module) => {
|
|
|
11471
11482
|
module.exports = Result;
|
|
11472
11483
|
});
|
|
11473
11484
|
var require_query = __commonJS((exports, module) => {
|
|
11474
|
-
var { EventEmitter } =
|
|
11485
|
+
var { EventEmitter } = __require2("events");
|
|
11475
11486
|
var Result = require_result();
|
|
11476
11487
|
var utils = require_utils();
|
|
11477
11488
|
|
|
@@ -12454,11 +12465,11 @@ var require_stream = __commonJS((exports, module) => {
|
|
|
12454
12465
|
};
|
|
12455
12466
|
function getNodejsStreamFuncs() {
|
|
12456
12467
|
function getStream2(ssl) {
|
|
12457
|
-
const net =
|
|
12468
|
+
const net = __require2("net");
|
|
12458
12469
|
return new net.Socket;
|
|
12459
12470
|
}
|
|
12460
12471
|
function getSecureStream2(options) {
|
|
12461
|
-
const tls =
|
|
12472
|
+
const tls = __require2("tls");
|
|
12462
12473
|
return tls.connect(options);
|
|
12463
12474
|
}
|
|
12464
12475
|
return {
|
|
@@ -12500,7 +12511,7 @@ var require_stream = __commonJS((exports, module) => {
|
|
|
12500
12511
|
}
|
|
12501
12512
|
});
|
|
12502
12513
|
var require_connection = __commonJS((exports, module) => {
|
|
12503
|
-
var EventEmitter =
|
|
12514
|
+
var EventEmitter = __require2("events").EventEmitter;
|
|
12504
12515
|
var { parse, serialize } = require_dist();
|
|
12505
12516
|
var { getStream, getSecureStream } = require_stream();
|
|
12506
12517
|
var flushBuffer = serialize.flush();
|
|
@@ -12573,7 +12584,7 @@ var require_connection = __commonJS((exports, module) => {
|
|
|
12573
12584
|
options.key = self.ssl.key;
|
|
12574
12585
|
}
|
|
12575
12586
|
}
|
|
12576
|
-
const net =
|
|
12587
|
+
const net = __require2("net");
|
|
12577
12588
|
if (net.isIP && net.isIP(host) === 0) {
|
|
12578
12589
|
options.servername = host;
|
|
12579
12590
|
}
|
|
@@ -12676,8 +12687,8 @@ var require_connection = __commonJS((exports, module) => {
|
|
|
12676
12687
|
module.exports = Connection;
|
|
12677
12688
|
});
|
|
12678
12689
|
var require_split2 = __commonJS((exports, module) => {
|
|
12679
|
-
var { Transform } =
|
|
12680
|
-
var { StringDecoder } =
|
|
12690
|
+
var { Transform } = __require2("stream");
|
|
12691
|
+
var { StringDecoder } = __require2("string_decoder");
|
|
12681
12692
|
var kLast = Symbol("last");
|
|
12682
12693
|
var kDecoder = Symbol("decoder");
|
|
12683
12694
|
function transform(chunk, enc, cb) {
|
|
@@ -12773,10 +12784,10 @@ var require_split2 = __commonJS((exports, module) => {
|
|
|
12773
12784
|
module.exports = split;
|
|
12774
12785
|
});
|
|
12775
12786
|
var require_helper = __commonJS((exports, module) => {
|
|
12776
|
-
var path =
|
|
12777
|
-
var Stream3 =
|
|
12787
|
+
var path = __require2("path");
|
|
12788
|
+
var Stream3 = __require2("stream").Stream;
|
|
12778
12789
|
var split = require_split2();
|
|
12779
|
-
var util =
|
|
12790
|
+
var util = __require2("util");
|
|
12780
12791
|
var defaultPort = 5432;
|
|
12781
12792
|
var isWin = process.platform === "win32";
|
|
12782
12793
|
var warnStream = process.stderr;
|
|
@@ -12935,8 +12946,8 @@ var require_helper = __commonJS((exports, module) => {
|
|
|
12935
12946
|
};
|
|
12936
12947
|
});
|
|
12937
12948
|
var require_lib = __commonJS((exports, module) => {
|
|
12938
|
-
var path =
|
|
12939
|
-
var fs =
|
|
12949
|
+
var path = __require2("path");
|
|
12950
|
+
var fs = __require2("fs");
|
|
12940
12951
|
var helper = require_helper();
|
|
12941
12952
|
module.exports = function(connInfo, cb) {
|
|
12942
12953
|
var file = helper.getFileName();
|
|
@@ -12951,9 +12962,9 @@ var require_lib = __commonJS((exports, module) => {
|
|
|
12951
12962
|
module.exports.warnTo = helper.warnTo;
|
|
12952
12963
|
});
|
|
12953
12964
|
var require_client = __commonJS((exports, module) => {
|
|
12954
|
-
var EventEmitter =
|
|
12965
|
+
var EventEmitter = __require2("events").EventEmitter;
|
|
12955
12966
|
var utils = require_utils();
|
|
12956
|
-
var nodeUtils =
|
|
12967
|
+
var nodeUtils = __require2("util");
|
|
12957
12968
|
var sasl = require_sasl();
|
|
12958
12969
|
var TypeOverrides = require_type_overrides();
|
|
12959
12970
|
var ConnectionParameters = require_connection_parameters();
|
|
@@ -13530,7 +13541,7 @@ var require_client = __commonJS((exports, module) => {
|
|
|
13530
13541
|
module.exports = Client;
|
|
13531
13542
|
});
|
|
13532
13543
|
var require_pg_pool = __commonJS((exports, module) => {
|
|
13533
|
-
var EventEmitter =
|
|
13544
|
+
var EventEmitter = __require2("events").EventEmitter;
|
|
13534
13545
|
var NOOP = function() {};
|
|
13535
13546
|
var removeWhere = (list, predicate) => {
|
|
13536
13547
|
const i = list.findIndex(predicate);
|
|
@@ -13940,8 +13951,8 @@ var require_pg_pool = __commonJS((exports, module) => {
|
|
|
13940
13951
|
module.exports = Pool;
|
|
13941
13952
|
});
|
|
13942
13953
|
var require_query2 = __commonJS((exports, module) => {
|
|
13943
|
-
var EventEmitter =
|
|
13944
|
-
var util =
|
|
13954
|
+
var EventEmitter = __require2("events").EventEmitter;
|
|
13955
|
+
var util = __require2("util");
|
|
13945
13956
|
var utils = require_utils();
|
|
13946
13957
|
var NativeQuery = module.exports = function(config, values, callback) {
|
|
13947
13958
|
EventEmitter.call(this);
|
|
@@ -14074,7 +14085,7 @@ var require_query2 = __commonJS((exports, module) => {
|
|
|
14074
14085
|
};
|
|
14075
14086
|
});
|
|
14076
14087
|
var require_client2 = __commonJS((exports, module) => {
|
|
14077
|
-
var nodeUtils =
|
|
14088
|
+
var nodeUtils = __require2("util");
|
|
14078
14089
|
var Native;
|
|
14079
14090
|
try {
|
|
14080
14091
|
Native = (() => {
|
|
@@ -14084,8 +14095,8 @@ var require_client2 = __commonJS((exports, module) => {
|
|
|
14084
14095
|
throw e;
|
|
14085
14096
|
}
|
|
14086
14097
|
var TypeOverrides = require_type_overrides();
|
|
14087
|
-
var EventEmitter =
|
|
14088
|
-
var util =
|
|
14098
|
+
var EventEmitter = __require2("events").EventEmitter;
|
|
14099
|
+
var util = __require2("util");
|
|
14089
14100
|
var ConnectionParameters = require_connection_parameters();
|
|
14090
14101
|
var NativeQuery = require_query2();
|
|
14091
14102
|
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.");
|
|
@@ -18579,6 +18590,7 @@ function getDb() {
|
|
|
18579
18590
|
total_tokens_in INTEGER NOT NULL DEFAULT 0,
|
|
18580
18591
|
total_tokens_out INTEGER NOT NULL DEFAULT 0,
|
|
18581
18592
|
total_duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
18593
|
+
tags TEXT,
|
|
18582
18594
|
error TEXT,
|
|
18583
18595
|
created_at TEXT NOT NULL,
|
|
18584
18596
|
completed_at TEXT
|
|
@@ -18634,9 +18646,9 @@ function getDb() {
|
|
|
18634
18646
|
async function createSession(session) {
|
|
18635
18647
|
const d = getDb();
|
|
18636
18648
|
d.prepare(`
|
|
18637
|
-
INSERT INTO sessions (id, task, provider, model, status, steps, total_tokens_in, total_tokens_out, total_duration_ms, error, created_at, completed_at)
|
|
18638
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
18639
|
-
`).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);
|
|
18649
|
+
INSERT INTO sessions (id, task, provider, model, status, steps, total_tokens_in, total_tokens_out, total_duration_ms, tags, error, created_at, completed_at)
|
|
18650
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
18651
|
+
`).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);
|
|
18640
18652
|
}
|
|
18641
18653
|
async function updateSession(session) {
|
|
18642
18654
|
const d = getDb();
|
|
@@ -18664,10 +18676,18 @@ function listSessions(opts) {
|
|
|
18664
18676
|
const d = getDb();
|
|
18665
18677
|
let sql = "SELECT * FROM sessions";
|
|
18666
18678
|
const params = [];
|
|
18679
|
+
const conditions = [];
|
|
18667
18680
|
if (opts?.status) {
|
|
18668
|
-
|
|
18681
|
+
conditions.push("status = ?");
|
|
18669
18682
|
params.push(opts.status);
|
|
18670
18683
|
}
|
|
18684
|
+
if (opts?.tag) {
|
|
18685
|
+
conditions.push("tags LIKE ?");
|
|
18686
|
+
params.push(`%"${opts.tag}"%`);
|
|
18687
|
+
}
|
|
18688
|
+
if (conditions.length > 0) {
|
|
18689
|
+
sql += " WHERE " + conditions.join(" AND ");
|
|
18690
|
+
}
|
|
18671
18691
|
sql += " ORDER BY created_at DESC";
|
|
18672
18692
|
if (opts?.limit) {
|
|
18673
18693
|
sql += " LIMIT ?";
|
|
@@ -18768,12 +18788,104 @@ function rowToSession(row) {
|
|
|
18768
18788
|
total_tokens_in: row.total_tokens_in,
|
|
18769
18789
|
total_tokens_out: row.total_tokens_out,
|
|
18770
18790
|
total_duration_ms: row.total_duration_ms,
|
|
18791
|
+
tags: row.tags ? JSON.parse(row.tags) : undefined,
|
|
18771
18792
|
error: row.error,
|
|
18772
18793
|
created_at: row.created_at,
|
|
18773
18794
|
completed_at: row.completed_at
|
|
18774
18795
|
};
|
|
18775
18796
|
}
|
|
18776
18797
|
|
|
18798
|
+
// src/lib/integrations.ts
|
|
18799
|
+
async function saveToRecordings(session, logs) {
|
|
18800
|
+
try {
|
|
18801
|
+
const { saveRecording } = await import("@hasna/recordings");
|
|
18802
|
+
await saveRecording({
|
|
18803
|
+
title: `Computer Use: ${session.task.slice(0, 100)}`,
|
|
18804
|
+
type: "computer-use",
|
|
18805
|
+
source: "computer",
|
|
18806
|
+
duration_ms: session.total_duration_ms,
|
|
18807
|
+
metadata: {
|
|
18808
|
+
session_id: session.id,
|
|
18809
|
+
provider: session.provider,
|
|
18810
|
+
model: session.model,
|
|
18811
|
+
steps: session.steps,
|
|
18812
|
+
tokens_in: session.total_tokens_in,
|
|
18813
|
+
tokens_out: session.total_tokens_out,
|
|
18814
|
+
status: session.status,
|
|
18815
|
+
tags: session.tags
|
|
18816
|
+
},
|
|
18817
|
+
transcript: logs.map((l) => ({
|
|
18818
|
+
step: l.step,
|
|
18819
|
+
action: l.action.type,
|
|
18820
|
+
reasoning: l.reasoning?.slice(0, 200),
|
|
18821
|
+
success: l.success,
|
|
18822
|
+
timestamp: l.created_at
|
|
18823
|
+
}))
|
|
18824
|
+
});
|
|
18825
|
+
return true;
|
|
18826
|
+
} catch {
|
|
18827
|
+
return false;
|
|
18828
|
+
}
|
|
18829
|
+
}
|
|
18830
|
+
async function registerWithSessions(session) {
|
|
18831
|
+
try {
|
|
18832
|
+
const mod = await import("@hasna/sessions");
|
|
18833
|
+
const registerSession = mod.registerSession ?? mod.createSession ?? mod.saveSession;
|
|
18834
|
+
if (typeof registerSession !== "function")
|
|
18835
|
+
return false;
|
|
18836
|
+
await registerSession({
|
|
18837
|
+
id: session.id,
|
|
18838
|
+
type: "computer-use",
|
|
18839
|
+
source: "computer",
|
|
18840
|
+
status: session.status,
|
|
18841
|
+
metadata: {
|
|
18842
|
+
task: session.task,
|
|
18843
|
+
provider: session.provider,
|
|
18844
|
+
model: session.model,
|
|
18845
|
+
steps: session.steps
|
|
18846
|
+
},
|
|
18847
|
+
started_at: session.created_at,
|
|
18848
|
+
ended_at: session.completed_at
|
|
18849
|
+
});
|
|
18850
|
+
return true;
|
|
18851
|
+
} catch {
|
|
18852
|
+
return false;
|
|
18853
|
+
}
|
|
18854
|
+
}
|
|
18855
|
+
async function pushToLogs(session, logs) {
|
|
18856
|
+
try {
|
|
18857
|
+
const mod = await import("@hasna/logs");
|
|
18858
|
+
const pushBatch = mod.logPushBatch ?? mod.pushBatch;
|
|
18859
|
+
if (typeof pushBatch !== "function")
|
|
18860
|
+
return false;
|
|
18861
|
+
await pushBatch(logs.map((l) => ({
|
|
18862
|
+
level: l.success ? "info" : "error",
|
|
18863
|
+
source: "computer",
|
|
18864
|
+
message: `[${l.action.type}] ${l.reasoning?.slice(0, 100) ?? ""}`,
|
|
18865
|
+
metadata: {
|
|
18866
|
+
session_id: session.id,
|
|
18867
|
+
step: l.step,
|
|
18868
|
+
action_type: l.action.type,
|
|
18869
|
+
success: l.success,
|
|
18870
|
+
error: l.error,
|
|
18871
|
+
duration_ms: l.duration_ms
|
|
18872
|
+
},
|
|
18873
|
+
timestamp: l.created_at
|
|
18874
|
+
})));
|
|
18875
|
+
return true;
|
|
18876
|
+
} catch {
|
|
18877
|
+
return false;
|
|
18878
|
+
}
|
|
18879
|
+
}
|
|
18880
|
+
async function runPostSessionIntegrations(session, logs) {
|
|
18881
|
+
const [recordings, sessions, logsPushed] = await Promise.all([
|
|
18882
|
+
saveToRecordings(session, logs),
|
|
18883
|
+
registerWithSessions(session),
|
|
18884
|
+
pushToLogs(session, logs)
|
|
18885
|
+
]);
|
|
18886
|
+
return { recordings, sessions, logs: logsPushed };
|
|
18887
|
+
}
|
|
18888
|
+
|
|
18777
18889
|
// src/agent/loop.ts
|
|
18778
18890
|
var DEFAULT_MAX_STEPS = 50;
|
|
18779
18891
|
async function runTask(options) {
|
|
@@ -18787,10 +18899,12 @@ async function runTask(options) {
|
|
|
18787
18899
|
systemPrompt,
|
|
18788
18900
|
screenshotMaxWidth,
|
|
18789
18901
|
dryRun = false,
|
|
18902
|
+
tags,
|
|
18903
|
+
displayNumber,
|
|
18790
18904
|
onStep,
|
|
18791
18905
|
onDone
|
|
18792
18906
|
} = options;
|
|
18793
|
-
const driver = createMacDriver();
|
|
18907
|
+
const driver = createMacDriver({ displayNumber });
|
|
18794
18908
|
const provider = createProvider(providerName, { model });
|
|
18795
18909
|
const config = loadConfig();
|
|
18796
18910
|
const safetyConfig = config.safety;
|
|
@@ -18801,6 +18915,7 @@ async function runTask(options) {
|
|
|
18801
18915
|
provider: providerName,
|
|
18802
18916
|
model: model ?? (providerName === "anthropic" ? "claude-sonnet-4-5-20250514" : "computer-use-preview"),
|
|
18803
18917
|
status: "running",
|
|
18918
|
+
tags,
|
|
18804
18919
|
steps: 0,
|
|
18805
18920
|
total_tokens_in: 0,
|
|
18806
18921
|
total_tokens_out: 0,
|
|
@@ -18853,6 +18968,8 @@ async function runTask(options) {
|
|
|
18853
18968
|
});
|
|
18854
18969
|
await updateSession(session);
|
|
18855
18970
|
onStep?.(step, response, { success: true, duration_ms: 0 });
|
|
18971
|
+
const logs = getActionLogs(sessionId);
|
|
18972
|
+
await runPostSessionIntegrations(session, logs).catch(() => {});
|
|
18856
18973
|
onDone?.(session);
|
|
18857
18974
|
await driver.dispose();
|
|
18858
18975
|
return session;
|
|
@@ -18918,6 +19035,8 @@ async function runTask(options) {
|
|
|
18918
19035
|
session.completed_at = new Date().toISOString();
|
|
18919
19036
|
session.error = `Reached max steps (${maxSteps})`;
|
|
18920
19037
|
await updateSession(session);
|
|
19038
|
+
const endLogs = getActionLogs(sessionId);
|
|
19039
|
+
await runPostSessionIntegrations(session, endLogs).catch(() => {});
|
|
18921
19040
|
onDone?.(session);
|
|
18922
19041
|
await driver.dispose();
|
|
18923
19042
|
return session;
|
|
@@ -18927,6 +19046,8 @@ async function runTask(options) {
|
|
|
18927
19046
|
session.total_duration_ms = Date.now() - startTime;
|
|
18928
19047
|
session.completed_at = new Date().toISOString();
|
|
18929
19048
|
await updateSession(session);
|
|
19049
|
+
const errLogs = getActionLogs(sessionId);
|
|
19050
|
+
await runPostSessionIntegrations(session, errLogs).catch(() => {});
|
|
18930
19051
|
onDone?.(session);
|
|
18931
19052
|
await driver.dispose();
|
|
18932
19053
|
return session;
|
|
@@ -18953,6 +19074,152 @@ function remapCoordinates(action, from, to) {
|
|
|
18953
19074
|
break;
|
|
18954
19075
|
}
|
|
18955
19076
|
}
|
|
19077
|
+
// src/drivers/mac/accessibility.ts
|
|
19078
|
+
import { join as join6, dirname as dirname2 } from "path";
|
|
19079
|
+
import { existsSync as existsSync4 } from "fs";
|
|
19080
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
19081
|
+
async function queryAccessibilityTree(opts) {
|
|
19082
|
+
const helperPath = getAccessibilityHelperPath();
|
|
19083
|
+
const args = [helperPath];
|
|
19084
|
+
if (opts?.app) {
|
|
19085
|
+
args.push("--app", opts.app);
|
|
19086
|
+
}
|
|
19087
|
+
if (opts?.focusedOnly) {
|
|
19088
|
+
args.push("--focused");
|
|
19089
|
+
}
|
|
19090
|
+
if (opts?.depth !== undefined) {
|
|
19091
|
+
args.push("--depth", String(opts.depth));
|
|
19092
|
+
}
|
|
19093
|
+
const proc = Bun.spawn(args, {
|
|
19094
|
+
stdout: "pipe",
|
|
19095
|
+
stderr: "pipe"
|
|
19096
|
+
});
|
|
19097
|
+
await proc.exited;
|
|
19098
|
+
if (proc.exitCode !== 0) {
|
|
19099
|
+
const stderr = await new Response(proc.stderr).text();
|
|
19100
|
+
throw new Error(`accessibility query failed: ${stderr}`);
|
|
19101
|
+
}
|
|
19102
|
+
const stdout = await new Response(proc.stdout).text();
|
|
19103
|
+
try {
|
|
19104
|
+
return JSON.parse(stdout);
|
|
19105
|
+
} catch {
|
|
19106
|
+
return [];
|
|
19107
|
+
}
|
|
19108
|
+
}
|
|
19109
|
+
function summarizeAccessibilityTree(elements) {
|
|
19110
|
+
if (elements.length === 0)
|
|
19111
|
+
return "No accessibility information available.";
|
|
19112
|
+
const lines = ["UI Elements:"];
|
|
19113
|
+
for (const el of elements) {
|
|
19114
|
+
const parts = [];
|
|
19115
|
+
if (el.role)
|
|
19116
|
+
parts.push(el.role);
|
|
19117
|
+
if (el.title)
|
|
19118
|
+
parts.push(`"${el.title}"`);
|
|
19119
|
+
if (el.label)
|
|
19120
|
+
parts.push(`(${el.label})`);
|
|
19121
|
+
if (el.value)
|
|
19122
|
+
parts.push(`value="${el.value}"`);
|
|
19123
|
+
const pos = `at (${el.x + Math.round(el.width / 2)}, ${el.y + Math.round(el.height / 2)})`;
|
|
19124
|
+
const size = `${el.width}x${el.height}`;
|
|
19125
|
+
const flags = [];
|
|
19126
|
+
if (!el.enabled)
|
|
19127
|
+
flags.push("disabled");
|
|
19128
|
+
if (el.focused)
|
|
19129
|
+
flags.push("focused");
|
|
19130
|
+
lines.push(` - ${parts.join(" ")} ${pos} [${size}]${flags.length ? ` (${flags.join(", ")})` : ""}`);
|
|
19131
|
+
}
|
|
19132
|
+
return lines.join(`
|
|
19133
|
+
`);
|
|
19134
|
+
}
|
|
19135
|
+
var _helperPath = null;
|
|
19136
|
+
function getAccessibilityHelperPath() {
|
|
19137
|
+
if (_helperPath)
|
|
19138
|
+
return _helperPath;
|
|
19139
|
+
const candidates = [
|
|
19140
|
+
join6(dirname2(fileURLToPath2(import.meta.url)), "..", "..", "..", "helpers", "accessibility"),
|
|
19141
|
+
join6(dirname2(fileURLToPath2(import.meta.url)), "..", "helpers", "accessibility"),
|
|
19142
|
+
join6(process.env.HOME ?? "~", ".hasna", "computer", "helpers", "accessibility")
|
|
19143
|
+
];
|
|
19144
|
+
for (const candidate of candidates) {
|
|
19145
|
+
if (existsSync4(candidate)) {
|
|
19146
|
+
_helperPath = candidate;
|
|
19147
|
+
return candidate;
|
|
19148
|
+
}
|
|
19149
|
+
}
|
|
19150
|
+
throw new Error("Accessibility helper not found. Run `swiftc helpers/accessibility.swift -o helpers/accessibility -framework AppKit` from the project root.");
|
|
19151
|
+
}
|
|
19152
|
+
// src/db/agents.ts
|
|
19153
|
+
function ensureAgentsTable() {
|
|
19154
|
+
const d = getDb();
|
|
19155
|
+
d.exec(`
|
|
19156
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
19157
|
+
id TEXT PRIMARY KEY,
|
|
19158
|
+
name TEXT NOT NULL UNIQUE,
|
|
19159
|
+
description TEXT,
|
|
19160
|
+
capabilities TEXT,
|
|
19161
|
+
focus TEXT,
|
|
19162
|
+
last_heartbeat TEXT NOT NULL,
|
|
19163
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
19164
|
+
);
|
|
19165
|
+
`);
|
|
19166
|
+
}
|
|
19167
|
+
function registerAgent(agent) {
|
|
19168
|
+
ensureAgentsTable();
|
|
19169
|
+
const d = getDb();
|
|
19170
|
+
const now = new Date().toISOString();
|
|
19171
|
+
const id = agent.name;
|
|
19172
|
+
const existing = d.prepare("SELECT * FROM agents WHERE name = ?").get(agent.name);
|
|
19173
|
+
if (existing) {
|
|
19174
|
+
d.prepare(`
|
|
19175
|
+
UPDATE agents SET description = ?, capabilities = ?, last_heartbeat = ?
|
|
19176
|
+
WHERE name = ?
|
|
19177
|
+
`).run(agent.description ?? existing.description, agent.capabilities ? JSON.stringify(agent.capabilities) : existing.capabilities, now, agent.name);
|
|
19178
|
+
} else {
|
|
19179
|
+
d.prepare(`
|
|
19180
|
+
INSERT INTO agents (id, name, description, capabilities, last_heartbeat, created_at)
|
|
19181
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
19182
|
+
`).run(id, agent.name, agent.description ?? null, agent.capabilities ? JSON.stringify(agent.capabilities) : null, now, now);
|
|
19183
|
+
}
|
|
19184
|
+
return getAgent(id);
|
|
19185
|
+
}
|
|
19186
|
+
function heartbeat(agentId) {
|
|
19187
|
+
ensureAgentsTable();
|
|
19188
|
+
const d = getDb();
|
|
19189
|
+
const result = d.prepare("UPDATE agents SET last_heartbeat = ? WHERE id = ?").run(new Date().toISOString(), agentId);
|
|
19190
|
+
return result.changes > 0;
|
|
19191
|
+
}
|
|
19192
|
+
function setFocus(agentId, focus) {
|
|
19193
|
+
ensureAgentsTable();
|
|
19194
|
+
const d = getDb();
|
|
19195
|
+
const result = d.prepare("UPDATE agents SET focus = ? WHERE id = ?").run(focus, agentId);
|
|
19196
|
+
return result.changes > 0;
|
|
19197
|
+
}
|
|
19198
|
+
function getAgent(id) {
|
|
19199
|
+
ensureAgentsTable();
|
|
19200
|
+
const d = getDb();
|
|
19201
|
+
const row = d.prepare("SELECT * FROM agents WHERE id = ?").get(id);
|
|
19202
|
+
if (!row)
|
|
19203
|
+
return null;
|
|
19204
|
+
return rowToAgent(row);
|
|
19205
|
+
}
|
|
19206
|
+
function listAgents() {
|
|
19207
|
+
ensureAgentsTable();
|
|
19208
|
+
const d = getDb();
|
|
19209
|
+
const rows = d.prepare("SELECT * FROM agents ORDER BY last_heartbeat DESC").all();
|
|
19210
|
+
return rows.map(rowToAgent);
|
|
19211
|
+
}
|
|
19212
|
+
function rowToAgent(row) {
|
|
19213
|
+
return {
|
|
19214
|
+
id: row.id,
|
|
19215
|
+
name: row.name,
|
|
19216
|
+
description: row.description,
|
|
19217
|
+
capabilities: row.capabilities ? JSON.parse(row.capabilities) : undefined,
|
|
19218
|
+
focus: row.focus,
|
|
19219
|
+
last_heartbeat: row.last_heartbeat,
|
|
19220
|
+
created_at: row.created_at
|
|
19221
|
+
};
|
|
19222
|
+
}
|
|
18956
19223
|
// src/lib/pricing.ts
|
|
18957
19224
|
var MODEL_PRICING = {
|
|
18958
19225
|
"claude-sonnet-4-5-20250514": { input: 3, output: 15 },
|
|
@@ -18997,22 +19264,88 @@ function findPricing(model) {
|
|
|
18997
19264
|
function listPricing() {
|
|
18998
19265
|
return { ...MODEL_PRICING };
|
|
18999
19266
|
}
|
|
19267
|
+
// src/lib/terminal-image.ts
|
|
19268
|
+
function detectProtocol() {
|
|
19269
|
+
const term = process.env.TERM_PROGRAM ?? "";
|
|
19270
|
+
const termInfo = process.env.TERM ?? "";
|
|
19271
|
+
if (term === "iTerm.app" || term === "WezTerm") {
|
|
19272
|
+
return "iterm2";
|
|
19273
|
+
}
|
|
19274
|
+
if (termInfo.includes("kitty") || process.env.KITTY_PID) {
|
|
19275
|
+
return "kitty";
|
|
19276
|
+
}
|
|
19277
|
+
return "none";
|
|
19278
|
+
}
|
|
19279
|
+
function renderInlineImage(base64, opts) {
|
|
19280
|
+
const protocol = detectProtocol();
|
|
19281
|
+
const width = opts?.width ?? 40;
|
|
19282
|
+
const height = opts?.height ?? 15;
|
|
19283
|
+
switch (protocol) {
|
|
19284
|
+
case "iterm2":
|
|
19285
|
+
return renderIterm2(base64, width, height, opts?.preserveAspectRatio ?? true);
|
|
19286
|
+
case "kitty":
|
|
19287
|
+
return renderKitty(base64, width, height);
|
|
19288
|
+
case "none":
|
|
19289
|
+
return "";
|
|
19290
|
+
}
|
|
19291
|
+
}
|
|
19292
|
+
function supportsInlineImages() {
|
|
19293
|
+
return detectProtocol() !== "none";
|
|
19294
|
+
}
|
|
19295
|
+
function renderIterm2(base64, width, height, preserveAspectRatio) {
|
|
19296
|
+
const params = [
|
|
19297
|
+
`width=${width}`,
|
|
19298
|
+
`height=${height}`,
|
|
19299
|
+
`preserveAspectRatio=${preserveAspectRatio ? 1 : 0}`,
|
|
19300
|
+
"inline=1"
|
|
19301
|
+
].join(";");
|
|
19302
|
+
return `\x1B]1337;File=${params}:${base64}\x07
|
|
19303
|
+
`;
|
|
19304
|
+
}
|
|
19305
|
+
function renderKitty(base64, width, height) {
|
|
19306
|
+
const chunkSize = 4096;
|
|
19307
|
+
const chunks = [];
|
|
19308
|
+
for (let i = 0;i < base64.length; i += chunkSize) {
|
|
19309
|
+
const chunk = base64.slice(i, i + chunkSize);
|
|
19310
|
+
const isLast = i + chunkSize >= base64.length;
|
|
19311
|
+
const more = isLast ? 0 : 1;
|
|
19312
|
+
if (i === 0) {
|
|
19313
|
+
chunks.push(`\x1B_Gf=100,a=T,m=${more},c=${width},r=${height};${chunk}\x1B\\`);
|
|
19314
|
+
} else {
|
|
19315
|
+
chunks.push(`\x1B_Gm=${more};${chunk}\x1B\\`);
|
|
19316
|
+
}
|
|
19317
|
+
}
|
|
19318
|
+
return chunks.join("") + `
|
|
19319
|
+
`;
|
|
19320
|
+
}
|
|
19000
19321
|
export {
|
|
19001
19322
|
updateSession,
|
|
19323
|
+
supportsInlineImages,
|
|
19324
|
+
summarizeAccessibilityTree,
|
|
19002
19325
|
stepCost,
|
|
19326
|
+
setFocus,
|
|
19003
19327
|
setConfigValue,
|
|
19004
19328
|
searchSessions,
|
|
19005
19329
|
searchActionLogs,
|
|
19006
19330
|
screenshotsMatch,
|
|
19007
19331
|
scaleScreenshot,
|
|
19332
|
+
saveToRecordings,
|
|
19008
19333
|
saveScreenshotToFile,
|
|
19009
19334
|
saveConfig,
|
|
19010
19335
|
runTask,
|
|
19336
|
+
runPostSessionIntegrations,
|
|
19011
19337
|
resetRateLimiter,
|
|
19338
|
+
renderInlineImage,
|
|
19339
|
+
registerWithSessions,
|
|
19340
|
+
registerAgent,
|
|
19341
|
+
queryAccessibilityTree,
|
|
19342
|
+
pushToLogs,
|
|
19012
19343
|
logAction,
|
|
19013
19344
|
loadConfig,
|
|
19014
19345
|
listSessions,
|
|
19015
19346
|
listPricing,
|
|
19347
|
+
listAgents,
|
|
19348
|
+
heartbeat,
|
|
19016
19349
|
getStats,
|
|
19017
19350
|
getSession,
|
|
19018
19351
|
getScreenSize,
|
|
@@ -19020,9 +19353,11 @@ export {
|
|
|
19020
19353
|
getDb,
|
|
19021
19354
|
getConfigValue,
|
|
19022
19355
|
getConfigPath,
|
|
19356
|
+
getAgent,
|
|
19023
19357
|
getActionLogs,
|
|
19024
19358
|
formatCost,
|
|
19025
19359
|
executeAction,
|
|
19360
|
+
detectProtocol,
|
|
19026
19361
|
deleteSession,
|
|
19027
19362
|
createSession,
|
|
19028
19363
|
createProvider,
|